more decompress
[wireshark-sm.git] / epan / to_str.c
blob1aff7522fcbaaf9d5d2fa0670a4dfa008f03e9f2
1 /* to_str.c
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
9 */
11 #include "config.h"
13 #include <stdio.h>
14 #include <string.h>
15 #include <time.h>
16 #include <glib.h>
18 #include <epan/wmem_scopes.h>
19 #include "proto.h"
20 #include "to_str.h"
21 #include "strutil.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
29 * useful.
31 #define BUF_TOO_SMALL_ERR "[Buffer too small]"
33 static const char mon_names[12][4] = {
34 "Jan",
35 "Feb",
36 "Mar",
37 "Apr",
38 "May",
39 "Jun",
40 "Jul",
41 "Aug",
42 "Sep",
43 "Oct",
44 "Nov",
45 "Dec"
48 static const char *
49 get_zonename(struct tm *tmp)
51 #if defined(_WIN32)
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
62 * just return them.
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];
73 #else
75 * UN*X.
77 * If we have tm_zone in struct tm, use that.
78 * Otherwise, if we have tzname[], use it, otherwise just
79 * say "we don't know.
81 # if defined(HAVE_STRUCT_TM_TM_ZONE)
82 return tmp->tm_zone;
83 # else /* HAVE_STRUCT_TM_TM_ZONE */
84 if ((tmp->tm_isdst != 0) && (tmp->tm_isdst != 1)) {
85 return "???";
87 # if defined(HAVE_TZNAME)
88 return tzname[tmp->tm_isdst];
89 # else
90 return tmp->tm_isdst ? "?DT" : "?ST";
91 # endif /* HAVE_TZNAME */
92 # endif /* HAVE_STRUCT_TM_TM_ZONE */
93 #endif /* _WIN32 */
96 static struct tm *
97 get_fmt_broken_down_time(field_display_e fmt, const time_t *secs)
99 switch (fmt) {
100 case ABSOLUTE_TIME_UTC:
101 case ABSOLUTE_TIME_DOY_UTC:
102 case ABSOLUTE_TIME_NTP_UTC:
103 return gmtime(secs);
104 case ABSOLUTE_TIME_LOCAL:
105 return localtime(secs);
106 default:
107 break;
109 ws_assert_not_reached();
112 static char *
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)
118 char *buf;
120 switch (fmt) {
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 ? "\"" : "",
125 tmp->tm_year + 1900,
126 tmp->tm_yday + 1,
127 tmp->tm_hour,
128 tmp->tm_min,
129 tmp->tm_sec,
130 nsecs_str,
131 tzone_sep,
132 tzone_str,
133 add_quotes ? "\"" : "");
134 break;
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],
142 tmp->tm_mday,
143 tmp->tm_year + 1900,
144 tmp->tm_hour,
145 tmp->tm_min,
146 tmp->tm_sec,
147 nsecs_str,
148 tzone_sep,
149 tzone_str,
150 add_quotes ? "\"" : "");
151 break;
152 default:
153 ws_assert_not_reached();
155 return buf;
158 static char *
159 snprint_abs_time_iso8601(wmem_allocator_t *scope,
160 field_display_e fmt, struct tm *tmp,
161 const char *nsecs_str, int flags)
163 char *buf;
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) {
167 add_zone = true;
170 switch (fmt) {
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 ? "\"" : "",
176 tmp->tm_year + 1900,
177 tmp->tm_yday + 1,
178 tmp->tm_hour,
179 tmp->tm_min,
180 tmp->tm_sec,
181 nsecs_str,
182 add_zone ? "Z" : "",
183 add_quotes ? "\"" : "");
184 break;
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 ? "\"" : "",
190 tmp->tm_year + 1900,
191 tmp->tm_mon + 1,
192 tmp->tm_mday,
193 tmp->tm_hour,
194 tmp->tm_min,
195 tmp->tm_sec,
196 nsecs_str,
197 add_zone ? "Z" : "",
198 add_quotes ? "\"" : "");
199 break;
200 case ABSOLUTE_TIME_LOCAL:
202 char zone_buf[8] = "";
203 if (add_zone) {
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 ? "\"" : "",
218 tmp->tm_year + 1900,
219 tmp->tm_mon + 1,
220 tmp->tm_mday,
221 tmp->tm_hour,
222 tmp->tm_min,
223 tmp->tm_sec,
224 nsecs_str,
225 zone_buf,
226 add_quotes ? "\"" : "");
227 break;
229 default:
230 ws_assert_not_reached();
232 return buf;
235 char *
236 abs_time_to_str_ex(wmem_allocator_t *scope, const nstime_t *abs_time, field_display_e fmt,
237 int flags)
239 struct tm *tmp;
240 char buf_nsecs[32];
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);
258 if (tmp == NULL) {
259 return wmem_strdup(scope, "Not representable");
262 *buf_nsecs = '\0';
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);
271 tzone_sep = "";
272 tzone_str = "";
273 if (flags & ABS_TIME_TO_STR_SHOW_ZONE || flags & ABS_TIME_TO_STR_SHOW_UTC_ONLY) {
274 switch (fmt) {
276 case ABSOLUTE_TIME_UTC:
277 case ABSOLUTE_TIME_DOY_UTC:
278 case ABSOLUTE_TIME_NTP_UTC:
279 tzone_sep = " ";
280 tzone_str = "UTC";
281 break;
283 case ABSOLUTE_TIME_LOCAL:
284 if (flags & ABS_TIME_TO_STR_SHOW_ZONE) {
285 tzone_sep = " ";
286 tzone_str = get_zonename(tmp);
288 break;
289 default:
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);
297 char *
298 abs_time_secs_to_str_ex(wmem_allocator_t *scope, const time_t abs_time_secs, field_display_e fmt,
299 int flags)
301 nstime_t abs_time;
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
322 * into a buffer.
323 * "is_nsecs" says that "frac" is nanoseconds if true and milliseconds
324 * if false.
326 static void
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;
334 time_val /= 60;
335 mins = time_val % 60;
336 time_val /= 60;
337 hours = time_val % 24;
338 time_val /= 24;
340 if (time_val != 0) {
341 wmem_strbuf_append_printf(buf, "%" PRIu64 " day%s", time_val, PLURALIZE(time_val));
342 do_comma = true;
344 if (hours != 0) {
345 wmem_strbuf_append_printf(buf, "%s%" PRIu64 " hour%s", COMMA(do_comma), hours, PLURALIZE(hours));
346 do_comma = true;
348 if (mins != 0) {
349 wmem_strbuf_append_printf(buf, "%s%" PRIu64 " minute%s", COMMA(do_comma), mins, PLURALIZE(mins));
350 do_comma = true;
352 if (secs != 0) {
353 if (frac != 0) {
354 if (is_nsecs)
355 wmem_strbuf_append_printf(buf, "%s%" PRIu64 ".%09u seconds", COMMA(do_comma), secs, frac);
356 else
357 wmem_strbuf_append_printf(buf, "%s%" PRIu64 ".%03u seconds", COMMA(do_comma), secs, frac);
358 } else
359 wmem_strbuf_append_printf(buf, "%s%" PRIu64 " second%s", COMMA(do_comma), secs, PLURALIZE(secs));
360 } else if (frac != 0) {
361 if (is_nsecs) {
362 if (frac < 1000) {
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);
366 } else {
367 wmem_strbuf_append_printf(buf, "%s%u.%06u milliseconds", COMMA(do_comma), frac / 1000000, frac % 1000000);
369 } else {
370 wmem_strbuf_append_printf(buf, "%s%u millisecond%s", COMMA(do_comma), frac, PLURALIZE(frac));
375 char *
376 unsigned_time_secs_to_str(wmem_allocator_t *scope, const uint32_t time_val)
378 wmem_strbuf_t *buf;
380 if (time_val == 0) {
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
394 * into a buffer.
395 * "is_nsecs" says that "frac" is nanoseconds if true and milliseconds
396 * if false.
398 static void
399 signed_time_secs_to_str_buf(int64_t time_val, const uint32_t frac,
400 const bool is_nsecs, wmem_strbuf_t *buf)
402 if(time_val < 0){
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,
416 is_nsecs, buf);
417 } else {
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);
425 } else
426 unsigned_time_secs_to_str_buf(time_val, frac, is_nsecs, buf);
429 char *
430 signed_time_secs_to_str(wmem_allocator_t *scope, const int32_t time_val)
432 wmem_strbuf_t *buf;
434 if (time_val == 0) {
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.
449 char *
450 signed_time_msecs_to_str(wmem_allocator_t *scope, int32_t time_val)
452 wmem_strbuf_t *buf;
453 int msecs;
455 if (time_val == 0) {
456 return wmem_strdup(scope, "0 seconds");
459 buf = wmem_strbuf_new_sized(scope, TIME_SECS_LEN+1+3+1);
461 if (time_val<0) {
462 /* oops we got passed a negative time */
463 time_val= -time_val;
464 msecs = time_val % 1000;
465 time_val /= 1000;
466 time_val= -time_val;
467 } else {
468 msecs = time_val % 1000;
469 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.
480 char *
481 rel_time_to_str(wmem_allocator_t *scope, const nstime_t *rel_time)
483 wmem_strbuf_t *buf;
484 int64_t time_val;
485 int32_t nsec;
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);
499 if (nsec < 0) {
500 nsec = -nsec;
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.
531 char *
532 rel_time_to_secs_str(wmem_allocator_t *scope, const nstime_t *rel_time)
534 char *buf;
536 buf = (char *)wmem_alloc(scope, NSTIME_SECS_LEN);
538 display_signed_time(buf, NSTIME_SECS_LEN, rel_time, WS_TSPREC_NSEC);
539 return buf;
542 char *
543 abs_time_to_unix_str(wmem_allocator_t *scope, const nstime_t *rel_time)
545 char *buf;
547 buf = (char *)wmem_alloc(scope, NSTIME_SECS_LEN);
549 display_epoch_time(buf, NSTIME_SECS_LEN, rel_time, WS_TSPREC_NSEC);
550 return buf;
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.
556 * Ex: ..xx x...
559 char *
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)
562 uint64_t mask;
563 char *str;
564 int bit, str_p = 0;
565 int i;
566 int max_bits = MIN(64, no_of_bits);
567 int no_leading_dots;
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;
577 } else {
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)) {
585 str[str_p] = ' ';
586 str_p++;
588 str[str_p] = '.';
589 str_p++;
592 /* read the bits for the int */
593 for (i = 0; i < max_bits; i++) {
594 if (bit && !(bit % 4)) {
595 str[str_p] = ' ';
596 str_p++;
598 if (bit && !(bit % 8)) {
599 str[str_p] = ' ';
600 str_p++;
602 bit++;
603 if ((value & mask) != 0) {
604 str[str_p] = '1';
605 str_p++;
606 } else {
607 str[str_p] = '0';
608 str_p++;
610 mask = mask>>1;
613 for (; bit % 8; bit++) {
614 if (bit && !(bit % 4)) {
615 str[str_p] = ' ';
616 str_p++;
618 str[str_p] = '.';
619 str_p++;
621 return str;
624 char *
625 guid_to_str(wmem_allocator_t *scope, const e_guid_t *guid)
627 char *buf;
629 buf = (char *)wmem_alloc(scope, GUID_STR_LEN);
630 return guid_to_str_buf(guid, buf, GUID_STR_LEN);
633 char *
634 guid_to_str_buf(const e_guid_t *guid, char *buf, int buf_len)
636 char *tempptr = buf;
638 if (buf_len < GUID_STR_LEN) {
639 (void) g_strlcpy(buf, BUF_TOO_SMALL_ERR, buf_len); /* Let the unexpected value alert user */
640 return buf;
643 /* 37 bytes */
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 */
654 *tempptr = '\0';
655 return buf;
658 const char *
659 port_type_to_str (port_type type)
661 switch (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
682 * Local variables:
683 * c-basic-offset: 4
684 * tab-width: 8
685 * indent-tabs-mode: nil
686 * End:
688 * vi: set shiftwidth=4 tabstop=8 expandtab:
689 * :indentSize=4:tabSize=8:noTabs=true: