2 * Routines for utilities to convert various other types to strings.
4 * Wireshark - Network traffic analyzer
5 * By Gerald Combs <gerald@wireshark.org>
6 * Copyright 1998 Gerald Combs
8 * SPDX-License-Identifier: GPL-2.0-or-later
18 #include <epan/wmem_scopes.h>
22 #include <wsutil/pint.h>
23 #include <wsutil/utf8_entities.h>
26 * If a user _does_ pass in a too-small buffer, this is probably
27 * going to be too long to fit. However, even a partial string
28 * starting with "[Buf" should provide enough of a clue to be
31 #define BUF_TOO_SMALL_ERR "[Buffer too small]"
33 static const char mon_names
[12][4] = {
49 get_zonename(struct tm
*tmp
)
53 * The strings in _tzname[] are encoded using the code page
54 * for the current C-language locale.
56 * On Windows, all Wireshark programs set that code page
57 * to the UTF-8 code page by calling
59 * setlocale(LC_ALL, ".UTF-8");
61 * so the strings in _tzname[] are UTF-8 strings, and we can
64 * (Note: the above does *not* mean we've set any code pages
65 * *other* than the one used by the Visual Studio C runtime
66 * to UTF-8, so don't assume, for example, that the "ANSI"
67 * versions of Windows APIs will take UTF-8 strings, or that
68 * non-UTF-16 output to the console will be treated as UTF-8.
69 * Setting those other code pages can cause problems, especially
70 * on pre-Windows 10 or older Windows 10 releases.)
72 return _tzname
[tmp
->tm_isdst
];
77 * If we have tm_zone in struct tm, use that.
78 * Otherwise, if we have tzname[], use it, otherwise just
81 # if defined(HAVE_STRUCT_TM_TM_ZONE)
83 # else /* HAVE_STRUCT_TM_TM_ZONE */
84 if ((tmp
->tm_isdst
!= 0) && (tmp
->tm_isdst
!= 1)) {
87 # if defined(HAVE_TZNAME)
88 return tzname
[tmp
->tm_isdst
];
90 return tmp
->tm_isdst
? "?DT" : "?ST";
91 # endif /* HAVE_TZNAME */
92 # endif /* HAVE_STRUCT_TM_TM_ZONE */
97 get_fmt_broken_down_time(field_display_e fmt
, const time_t *secs
)
100 case ABSOLUTE_TIME_UTC
:
101 case ABSOLUTE_TIME_DOY_UTC
:
102 case ABSOLUTE_TIME_NTP_UTC
:
104 case ABSOLUTE_TIME_LOCAL
:
105 return localtime(secs
);
109 ws_assert_not_reached();
113 snprint_abs_time_secs(wmem_allocator_t
*scope
,
114 field_display_e fmt
, struct tm
*tmp
,
115 const char *nsecs_str
, const char *tzone_sep
,
116 const char *tzone_str
, bool add_quotes
)
121 case ABSOLUTE_TIME_DOY_UTC
:
122 buf
= wmem_strdup_printf(scope
,
123 "%s%04d/%03d:%02d:%02d:%02d%s%s%s%s",
124 add_quotes
? "\"" : "",
133 add_quotes
? "\"" : "");
135 case ABSOLUTE_TIME_NTP_UTC
: /* FALLTHROUGH */
136 case ABSOLUTE_TIME_UTC
: /* FALLTHROUGH */
137 case ABSOLUTE_TIME_LOCAL
:
138 buf
= wmem_strdup_printf(scope
,
139 "%s%s %2d, %d %02d:%02d:%02d%s%s%s%s",
140 add_quotes
? "\"" : "",
141 mon_names
[tmp
->tm_mon
],
150 add_quotes
? "\"" : "");
153 ws_assert_not_reached();
159 snprint_abs_time_iso8601(wmem_allocator_t
*scope
,
160 field_display_e fmt
, struct tm
*tmp
,
161 const char *nsecs_str
, int flags
)
164 bool add_quotes
= flags
& ABS_TIME_TO_STR_ADD_DQUOTES
;
165 bool add_zone
= flags
& ABS_TIME_TO_STR_SHOW_ZONE
;
166 if (fmt
!= ABSOLUTE_TIME_LOCAL
&& flags
& ABS_TIME_TO_STR_SHOW_UTC_ONLY
) {
171 case ABSOLUTE_TIME_DOY_UTC
:
172 /* ISO 8601 4.1.3 Ordinal date */
173 buf
= wmem_strdup_printf(scope
,
174 "%s%04d-%03dT%02d:%02d:%02d%s%s%s",
175 add_quotes
? "\"" : "",
183 add_quotes
? "\"" : "");
185 case ABSOLUTE_TIME_NTP_UTC
: /* FALLTHROUGH */
186 case ABSOLUTE_TIME_UTC
: /* FALLTHROUGH */
187 buf
= wmem_strdup_printf(scope
,
188 "%s%d-%02d-%02dT%02d:%02d:%02d%s%s%s",
189 add_quotes
? "\"" : "",
198 add_quotes
? "\"" : "");
200 case ABSOLUTE_TIME_LOCAL
:
202 char zone_buf
[8] = "";
205 * C11 requires that strftime supports %z; unfortunately
206 * it doesn't put the ':' in the timezone as strict ISO 8601
207 * would require (no mixing "basic" and "extended" formats.)
208 * XXX - Should we add in the ":"?
210 * We could also use _get_timezone on Windows, or on Linux and
211 * *BSD tm_gmtoff (if HAVE_STRUCT_TM_TM_GMTOFF).
213 strftime(zone_buf
, 8, "%z", tmp
);
215 buf
= wmem_strdup_printf(scope
,
216 "%s%d-%02d-%02dT%02d:%02d:%02d%s%s%s",
217 add_quotes
? "\"" : "",
226 add_quotes
? "\"" : "");
230 ws_assert_not_reached();
236 abs_time_to_str_ex(wmem_allocator_t
*scope
, const nstime_t
*abs_time
, field_display_e fmt
,
241 const char *tzone_sep
, *tzone_str
;
243 if (fmt
== BASE_NONE
)
244 fmt
= ABSOLUTE_TIME_LOCAL
;
246 ws_assert(FIELD_DISPLAY_IS_ABSOLUTE_TIME(fmt
));
248 if (fmt
== ABSOLUTE_TIME_UNIX
) {
249 return abs_time_to_unix_str(scope
, abs_time
);
252 if (fmt
== ABSOLUTE_TIME_NTP_UTC
&& abs_time
->secs
== 0 &&
253 (abs_time
->nsecs
== 0 || abs_time
->nsecs
== INT_MAX
)) {
254 return wmem_strdup(scope
, "NULL");
257 tmp
= get_fmt_broken_down_time(fmt
, &abs_time
->secs
);
259 return wmem_strdup(scope
, "Not representable");
263 if (abs_time
->nsecs
!= INT_MAX
) {
264 snprintf(buf_nsecs
, sizeof(buf_nsecs
), ".%09d", abs_time
->nsecs
);
267 if (flags
& ABS_TIME_TO_STR_ISO8601
) {
268 return snprint_abs_time_iso8601(scope
, fmt
, tmp
, buf_nsecs
, flags
);
273 if (flags
& ABS_TIME_TO_STR_SHOW_ZONE
|| flags
& ABS_TIME_TO_STR_SHOW_UTC_ONLY
) {
276 case ABSOLUTE_TIME_UTC
:
277 case ABSOLUTE_TIME_DOY_UTC
:
278 case ABSOLUTE_TIME_NTP_UTC
:
283 case ABSOLUTE_TIME_LOCAL
:
284 if (flags
& ABS_TIME_TO_STR_SHOW_ZONE
) {
286 tzone_str
= get_zonename(tmp
);
290 ws_assert_not_reached();
294 return snprint_abs_time_secs(scope
, fmt
, tmp
, buf_nsecs
, tzone_sep
, tzone_str
, flags
& ABS_TIME_TO_STR_ADD_DQUOTES
);
298 abs_time_secs_to_str_ex(wmem_allocator_t
*scope
, const time_t abs_time_secs
, field_display_e fmt
,
303 nstime_set_unset(&abs_time
);
304 abs_time
.secs
= abs_time_secs
;
305 return abs_time_to_str_ex(scope
, &abs_time
, fmt
, flags
);
308 #define PLURALIZE(n) (((n) > 1) ? "s" : "")
309 #define COMMA(do_it) ((do_it) ? ", " : "")
312 * Maximum length of a string showing days/hours/minutes/seconds.
313 * (Does not include the terminating '\0'.)
314 * Includes space for a '-' sign for any negative components.
315 * -123456789012345 days, 12 hours, 12 minutes, 12.123 seconds
317 #define TIME_SECS_LEN (16+1+4+2+2+5+2+2+7+2+2+7+4)
320 * Convert an unsigned value in seconds and fractions of a second to a string,
321 * giving time in days, hours, minutes, and seconds, and put the result
323 * "is_nsecs" says that "frac" is nanoseconds if true and milliseconds
327 unsigned_time_secs_to_str_buf(uint64_t time_val
, const uint32_t frac
,
328 const bool is_nsecs
, wmem_strbuf_t
*buf
)
330 uint64_t hours
, mins
, secs
;
331 bool do_comma
= false;
333 secs
= time_val
% 60;
335 mins
= time_val
% 60;
337 hours
= time_val
% 24;
341 wmem_strbuf_append_printf(buf
, "%" PRIu64
" day%s", time_val
, PLURALIZE(time_val
));
345 wmem_strbuf_append_printf(buf
, "%s%" PRIu64
" hour%s", COMMA(do_comma
), hours
, PLURALIZE(hours
));
349 wmem_strbuf_append_printf(buf
, "%s%" PRIu64
" minute%s", COMMA(do_comma
), mins
, PLURALIZE(mins
));
355 wmem_strbuf_append_printf(buf
, "%s%" PRIu64
".%09u seconds", COMMA(do_comma
), secs
, frac
);
357 wmem_strbuf_append_printf(buf
, "%s%" PRIu64
".%03u seconds", COMMA(do_comma
), secs
, frac
);
359 wmem_strbuf_append_printf(buf
, "%s%" PRIu64
" second%s", COMMA(do_comma
), secs
, PLURALIZE(secs
));
360 } else if (frac
!= 0) {
363 wmem_strbuf_append_printf(buf
, "%s%u nanosecond%s", COMMA(do_comma
), frac
, PLURALIZE(frac
));
364 } else if (frac
< 1000000) {
365 wmem_strbuf_append_printf(buf
, "%s%u.%03u microseconds", COMMA(do_comma
), frac
/ 1000, frac
% 1000);
367 wmem_strbuf_append_printf(buf
, "%s%u.%06u milliseconds", COMMA(do_comma
), frac
/ 1000000, frac
% 1000000);
370 wmem_strbuf_append_printf(buf
, "%s%u millisecond%s", COMMA(do_comma
), frac
, PLURALIZE(frac
));
376 unsigned_time_secs_to_str(wmem_allocator_t
*scope
, const uint32_t time_val
)
381 return wmem_strdup(scope
, "0 seconds");
384 buf
= wmem_strbuf_new_sized(scope
, TIME_SECS_LEN
+1);
386 unsigned_time_secs_to_str_buf(time_val
, 0, false, buf
);
388 return wmem_strbuf_finalize(buf
);
392 * Convert a signed value in seconds and fractions of a second to a string,
393 * giving time in days, hours, minutes, and seconds, and put the result
395 * "is_nsecs" says that "frac" is nanoseconds if true and milliseconds
399 signed_time_secs_to_str_buf(int64_t time_val
, const uint32_t frac
,
400 const bool is_nsecs
, wmem_strbuf_t
*buf
)
403 wmem_strbuf_append_printf(buf
, "-");
404 if(time_val
== INT64_MIN
) {
406 * You can't fit time_val's absolute value into
407 * a 64-bit signed integer. Just directly
408 * pass UINT64_MAX, which is its absolute
409 * value, directly to unsigned_time_secs_to_str_buf().
411 * (XXX - does ISO C guarantee that -(-2^n),
412 * when calculated and cast to an n-bit unsigned
413 * integer type, will have the value 2^n?)
415 unsigned_time_secs_to_str_buf(UINT64_MAX
, frac
,
419 * We now know -secs will fit into a uint32_t;
420 * negate it and pass that to
421 * unsigned_time_secs_to_str_buf().
423 unsigned_time_secs_to_str_buf(-time_val
, frac
, is_nsecs
, buf
);
426 unsigned_time_secs_to_str_buf(time_val
, frac
, is_nsecs
, buf
);
430 signed_time_secs_to_str(wmem_allocator_t
*scope
, const int32_t time_val
)
435 return wmem_strdup(scope
, "0 seconds");
438 buf
= wmem_strbuf_new_sized(scope
, TIME_SECS_LEN
+1);
440 signed_time_secs_to_str_buf(time_val
, 0, false, buf
);
442 return wmem_strbuf_finalize(buf
);
446 * Convert a signed value in milliseconds to a string, giving time in days,
447 * hours, minutes, and seconds, and put the result into a buffer.
450 signed_time_msecs_to_str(wmem_allocator_t
*scope
, int32_t time_val
)
456 return wmem_strdup(scope
, "0 seconds");
459 buf
= wmem_strbuf_new_sized(scope
, TIME_SECS_LEN
+1+3+1);
462 /* oops we got passed a negative time */
464 msecs
= time_val
% 1000;
468 msecs
= time_val
% 1000;
472 signed_time_secs_to_str_buf(time_val
, msecs
, false, buf
);
474 return wmem_strbuf_finalize(buf
);
478 * Display a relative time as days/hours/minutes/seconds.
481 rel_time_to_str(wmem_allocator_t
*scope
, const nstime_t
*rel_time
)
487 /* If the nanoseconds part of the time stamp is negative,
488 print its absolute value and, if the seconds part isn't
489 (the seconds part should be zero in that case), stick
490 a "-" in front of the entire time stamp. */
491 time_val
= (int64_t) rel_time
->secs
;
492 nsec
= rel_time
->nsecs
;
493 if (time_val
== 0 && nsec
== 0) {
494 return wmem_strdup(scope
, "0.000000000 seconds");
497 buf
= wmem_strbuf_new_sized(scope
, 1+TIME_SECS_LEN
+1+6+1);
501 wmem_strbuf_append_c(buf
, '-');
504 * We assume here that "rel_time->secs" is negative
505 * or zero; if it's not, the time stamp is bogus,
506 * with a positive seconds and negative microseconds.
508 time_val
= (int64_t) -rel_time
->secs
;
511 signed_time_secs_to_str_buf(time_val
, nsec
, true, buf
);
513 return wmem_strbuf_finalize(buf
);
517 * Number of characters required by a 64-bit signed number.
519 #define CHARS_64_BIT_SIGNED 20 /* sign plus 19 digits */
522 * Number of characters required by a fractional part, in nanoseconds */
523 #define CHARS_NANOSECONDS 10 /* .000000001 */
525 /* Includes terminating '\0' */
526 #define NSTIME_SECS_LEN (CHARS_64_BIT_SIGNED+CHARS_NANOSECONDS+1)
529 * Display a relative time as seconds.
532 rel_time_to_secs_str(wmem_allocator_t
*scope
, const nstime_t
*rel_time
)
536 buf
= (char *)wmem_alloc(scope
, NSTIME_SECS_LEN
);
538 display_signed_time(buf
, NSTIME_SECS_LEN
, rel_time
, WS_TSPREC_NSEC
);
543 abs_time_to_unix_str(wmem_allocator_t
*scope
, const nstime_t
*rel_time
)
547 buf
= (char *)wmem_alloc(scope
, NSTIME_SECS_LEN
);
549 display_epoch_time(buf
, NSTIME_SECS_LEN
, rel_time
, WS_TSPREC_NSEC
);
554 * Generates a string representing the bits in a bitfield at "bit_offset" from an 8 bit boundary
555 * with the length in bits of no_of_bits based on value.
560 decode_bits_in_field(wmem_allocator_t
*scope
, const unsigned bit_offset
, const int no_of_bits
, const uint64_t value
, const unsigned encoding
)
566 int max_bits
= MIN(64, no_of_bits
);
569 mask
= UINT64_C(1) << (max_bits
-1);
571 if (encoding
& ENC_LITTLE_ENDIAN
) {
572 /* Bits within octet are numbered from LSB (0) to MSB (7).
573 * The value in string is from most significant bit to lowest.
574 * Calculate how many dots have to be printed at the beginning of string.
576 no_leading_dots
= (8 - ((bit_offset
+ no_of_bits
) % 8)) % 8;
578 no_leading_dots
= bit_offset
% 8;
581 /* Prepare the string, 256 pos for the bits and zero termination, + 64 for the spaces */
582 str
= (char *)wmem_alloc0(scope
, 256+64);
583 for (bit
= 0; bit
< no_leading_dots
; bit
++) {
584 if (bit
&& !(bit
% 4)) {
592 /* read the bits for the int */
593 for (i
= 0; i
< max_bits
; i
++) {
594 if (bit
&& !(bit
% 4)) {
598 if (bit
&& !(bit
% 8)) {
603 if ((value
& mask
) != 0) {
613 for (; bit
% 8; bit
++) {
614 if (bit
&& !(bit
% 4)) {
625 guid_to_str(wmem_allocator_t
*scope
, const e_guid_t
*guid
)
629 buf
= (char *)wmem_alloc(scope
, GUID_STR_LEN
);
630 return guid_to_str_buf(guid
, buf
, GUID_STR_LEN
);
634 guid_to_str_buf(const e_guid_t
*guid
, char *buf
, int buf_len
)
638 if (buf_len
< GUID_STR_LEN
) {
639 (void) g_strlcpy(buf
, BUF_TOO_SMALL_ERR
, buf_len
); /* Let the unexpected value alert user */
644 tempptr
= dword_to_hex(tempptr
, guid
->data1
); /* 8 bytes */
645 *tempptr
++ = '-'; /* 1 byte */
646 tempptr
= word_to_hex(tempptr
, guid
->data2
); /* 4 bytes */
647 *tempptr
++ = '-'; /* 1 byte */
648 tempptr
= word_to_hex(tempptr
, guid
->data3
); /* 4 bytes */
649 *tempptr
++ = '-'; /* 1 byte */
650 tempptr
= bytes_to_hexstr(tempptr
, &guid
->data4
[0], 2); /* 4 bytes */
651 *tempptr
++ = '-'; /* 1 byte */
652 tempptr
= bytes_to_hexstr(tempptr
, &guid
->data4
[2], 6); /* 12 bytes */
659 port_type_to_str (port_type type
)
662 case PT_NONE
: return "NONE";
663 case PT_SCTP
: return "SCTP";
664 case PT_TCP
: return "TCP";
665 case PT_UDP
: return "UDP";
666 case PT_DCCP
: return "DCCP";
667 case PT_IPX
: return "IPX";
668 case PT_DDP
: return "DDP";
669 case PT_IDP
: return "IDP";
670 case PT_USB
: return "USB";
671 case PT_I2C
: return "I2C";
672 case PT_IBQP
: return "IBQP";
673 case PT_BLUETOOTH
: return "BLUETOOTH";
674 case PT_IWARP_MPA
: return "IWARP_MPA";
675 default: return "[Unknown]";
680 * Editor modelines - https://www.wireshark.org/tools/modelines.html
685 * indent-tabs-mode: nil
688 * vi: set shiftwidth=4 tabstop=8 expandtab:
689 * :indentSize=4:tabSize=8:noTabs=true: