summaryrefslogtreecommitdiffstats
path: root/epan/to_str.c
blob: 2c6028604a33d91b417e4e612caf6077da47ecea (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
/* to_str.c
 * Routines for utilities to convert various other types to strings.
 *
 * Wireshark - Network traffic analyzer
 * By Gerald Combs <gerald@wireshark.org>
 * Copyright 1998 Gerald Combs
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

#include "config.h"

#include <stdio.h>
#include <string.h>
#include <time.h>
#include <glib.h>

#include <epan/wmem_scopes.h>
#include "proto.h"
#include "to_str.h"
#include "strutil.h"
#include <wsutil/pint.h>
#include <wsutil/utf8_entities.h>

/*
 * If a user _does_ pass in a too-small buffer, this is probably
 * going to be too long to fit.  However, even a partial string
 * starting with "[Buf" should provide enough of a clue to be
 * useful.
 */
#define BUF_TOO_SMALL_ERR "[Buffer too small]"

static const char mon_names[12][4] = {
    "Jan",
    "Feb",
    "Mar",
    "Apr",
    "May",
    "Jun",
    "Jul",
    "Aug",
    "Sep",
    "Oct",
    "Nov",
    "Dec"
};

static const gchar *
get_zonename(struct tm *tmp)
{
#if defined(_WIN32)
    /*
     * The strings in _tzname[] are encoded using the code page
     * for the current C-language locale.
     *
     * On Windows, all Wireshark programs set that code page
     * to the UTF-8 code page by calling
     *
     *	  setlocale(LC_ALL, ".UTF-8");
     *
     * so the strings in _tzname[] are UTF-8 strings, and we can
     * just return them.
     *
     * (Note: the above does *not* mean we've set any code pages
     * *other* than the one used by the Visual Studio C runtime
     * to UTF-8, so don't assume, for example, that the "ANSI"
     * versions of Windows APIs will take UTF-8 strings, or that
     * non-UTF-16 output to the console will be treated as UTF-8.
     * Setting those other code pages can cause problems, especially
     * on pre-Windows 10 or older Windows 10 releases.)
     */
    return _tzname[tmp->tm_isdst];
#else
    /*
     * UN*X.
     *
     * If we have tm_zone in struct tm, use that.
     * Otherwise, if we have tzname[], use it, otherwise just
     * say "we don't know.
     */
# if defined(HAVE_STRUCT_TM_TM_ZONE)
    return tmp->tm_zone;
# else /* HAVE_STRUCT_TM_TM_ZONE */
    if ((tmp->tm_isdst != 0) && (tmp->tm_isdst != 1)) {
        return "???";
    }
#  if defined(HAVE_TZNAME)
    return tzname[tmp->tm_isdst];
#  else
    return tmp->tm_isdst ? "?DT" : "?ST";
#  endif /* HAVE_TZNAME */
# endif /* HAVE_STRUCT_TM_TM_ZONE */
#endif /* _WIN32 */
}

static struct tm *
get_fmt_broken_down_time(field_display_e fmt, const time_t *secs)
{
    switch (fmt) {
        case ABSOLUTE_TIME_UTC:
        case ABSOLUTE_TIME_DOY_UTC:
        case ABSOLUTE_TIME_NTP_UTC:
            return gmtime(secs);
        case ABSOLUTE_TIME_LOCAL:
            return localtime(secs);
        default:
            break;
    }
    ws_assert_not_reached();
}

static char *
snprint_abs_time_secs(wmem_allocator_t *scope,
                        field_display_e fmt, struct tm *tmp,
                        const char *nsecs_str, const char *tzone_sep,
                        const char *tzone_str, gboolean add_quotes)
{
    char *buf;

    switch (fmt) {
        case ABSOLUTE_TIME_DOY_UTC:
            buf = wmem_strdup_printf(scope,
                    "%s%04d/%03d:%02d:%02d:%02d%s%s%s%s",
                    add_quotes ? "\"" : "",
                    tmp->tm_year + 1900,
                    tmp->tm_yday + 1,
                    tmp->tm_hour,
                    tmp->tm_min,
                    tmp->tm_sec,
                    nsecs_str,
                    tzone_sep,
                    tzone_str,
                    add_quotes ? "\"" : "");
            break;
        case ABSOLUTE_TIME_NTP_UTC:	/* FALLTHROUGH */
        case ABSOLUTE_TIME_UTC:		/* FALLTHROUGH */
        case ABSOLUTE_TIME_LOCAL:
            buf = wmem_strdup_printf(scope,
                    "%s%s %2d, %d %02d:%02d:%02d%s%s%s%s",
                    add_quotes ? "\"" : "",
                    mon_names[tmp->tm_mon],
                    tmp->tm_mday,
                    tmp->tm_year + 1900,
                    tmp->tm_hour,
                    tmp->tm_min,
                    tmp->tm_sec,
                    nsecs_str,
                    tzone_sep,
                    tzone_str,
                    add_quotes ? "\"" : "");
            break;
        default:
            ws_assert_not_reached();
    }
    return buf;
}

char *
abs_time_to_str_ex(wmem_allocator_t *scope, const nstime_t *abs_time, field_display_e fmt,
                    int flags)
{
    struct tm *tmp;
    char buf_nsecs[32];
    const char *tzone_sep, *tzone_str;

    if (fmt == BASE_NONE)
        fmt = ABSOLUTE_TIME_LOCAL;

    ws_assert(FIELD_DISPLAY_IS_ABSOLUTE_TIME(fmt));

    if (fmt == ABSOLUTE_TIME_UNIX) {
        return abs_time_to_unix_str(scope, abs_time);
    }

    if (fmt == ABSOLUTE_TIME_NTP_UTC && abs_time->secs == 0 &&
                (abs_time->nsecs == 0 || abs_time->nsecs == G_MAXINT)) {
        return wmem_strdup(scope, "NULL");
    }

    tmp = get_fmt_broken_down_time(fmt, &abs_time->secs);
    if (tmp == NULL) {
        return wmem_strdup(scope, "Not representable");
    }

    *buf_nsecs = '\0';
    if (abs_time->nsecs != G_MAXINT) {
        snprintf(buf_nsecs, sizeof(buf_nsecs), ".%09d", abs_time->nsecs);
    }

    tzone_sep = "";
    tzone_str = "";
    if (flags & ABS_TIME_TO_STR_SHOW_ZONE || flags & ABS_TIME_TO_STR_SHOW_UTC_ONLY) {
        switch (fmt) {

        case ABSOLUTE_TIME_UTC:
        case ABSOLUTE_TIME_DOY_UTC:
        case ABSOLUTE_TIME_NTP_UTC:
            tzone_sep = " ";
            tzone_str = "UTC";
            break;

        case ABSOLUTE_TIME_LOCAL:
            if (flags & ABS_TIME_TO_STR_SHOW_ZONE) {
                tzone_sep = " ";
                tzone_str = get_zonename(tmp);
            }
            break;
        default:
            ws_assert_not_reached();
        }
    }

    return snprint_abs_time_secs(scope, fmt, tmp, buf_nsecs, tzone_sep, tzone_str, flags & ABS_TIME_TO_STR_ADD_DQUOTES);
}

char *
abs_time_secs_to_str_ex(wmem_allocator_t *scope, const time_t abs_time_secs, field_display_e fmt,
                        int flags)
{
    nstime_t abs_time;

    nstime_set_unset(&abs_time);
    abs_time.secs = abs_time_secs;
    return abs_time_to_str_ex(scope, &abs_time, fmt, flags);
}

#define	PLURALIZE(n)	(((n) > 1) ? "s" : "")
#define	COMMA(do_it)	((do_it) ? ", " : "")

/*
 * Maximum length of a string showing days/hours/minutes/seconds.
 * (Does not include the terminating '\0'.)
 * Includes space for a '-' sign for any negative components.
 * -12345 days, 12 hours, 12 minutes, 12.123 seconds
 */
#define TIME_SECS_LEN	(10+1+4+2+2+5+2+2+7+2+2+7+4)

/*
 * Convert an unsigned value in seconds and fractions of a second to a string,
 * giving time in days, hours, minutes, and seconds, and put the result
 * into a buffer.
 * "is_nsecs" says that "frac" is nanoseconds if true and milliseconds
 * if false.
 */
static void
unsigned_time_secs_to_str_buf(guint32 time_val, const guint32 frac,
                                const gboolean is_nsecs, wmem_strbuf_t *buf)
{
    int hours, mins, secs;
    gboolean do_comma = FALSE;

    secs = time_val % 60;
    time_val /= 60;
    mins = time_val % 60;
    time_val /= 60;
    hours = time_val % 24;
    time_val /= 24;

    if (time_val != 0) {
        wmem_strbuf_append_printf(buf, "%u day%s", time_val, PLURALIZE(time_val));
        do_comma = TRUE;
    }
    if (hours != 0) {
        wmem_strbuf_append_printf(buf, "%s%u hour%s", COMMA(do_comma), hours, PLURALIZE(hours));
        do_comma = TRUE;
    }
    if (mins != 0) {
        wmem_strbuf_append_printf(buf, "%s%u minute%s", COMMA(do_comma), mins, PLURALIZE(mins));
        do_comma = TRUE;
    }
    if (secs != 0 || frac != 0) {
        if (frac != 0) {
            if (is_nsecs)
                wmem_strbuf_append_printf(buf, "%s%u.%09u seconds", COMMA(do_comma), secs, frac);
            else
                wmem_strbuf_append_printf(buf, "%s%u.%03u seconds", COMMA(do_comma), secs, frac);
        } else
            wmem_strbuf_append_printf(buf, "%s%u second%s", COMMA(do_comma), secs, PLURALIZE(secs));
    }
}

gchar *
unsigned_time_secs_to_str(wmem_allocator_t *scope, const guint32 time_val)
{
    wmem_strbuf_t *buf;

    if (time_val == 0) {
        return wmem_strdup(scope, "0 seconds");
    }

    buf = wmem_strbuf_new_sized(scope, TIME_SECS_LEN+1);

    unsigned_time_secs_to_str_buf(time_val, 0, FALSE, buf);

    return wmem_strbuf_finalize(buf);
}

/*
 * Convert a signed value in seconds and fractions of a second to a string,
 * giving time in days, hours, minutes, and seconds, and put the result
 * into a buffer.
 * "is_nsecs" says that "frac" is nanoseconds if true and milliseconds
 * if false.
 */
static void
signed_time_secs_to_str_buf(gint32 time_val, const guint32 frac,
    const gboolean is_nsecs, wmem_strbuf_t *buf)
{
    if(time_val < 0){
        wmem_strbuf_append_printf(buf, "-");
        if(time_val == G_MININT32) {
            /*
             * You can't fit time_val's absolute value into
             * a 32-bit signed integer.  Just directly
             * pass G_MAXUINT32, which is its absolute
             * value, directly to unsigned_time_secs_to_str_buf().
             *
             * (XXX - does ISO C guarantee that -(-2^n),
             * when calculated and cast to an n-bit unsigned
             * integer type, will have the value 2^n?)
             */
            unsigned_time_secs_to_str_buf(G_MAXUINT32, frac,
                is_nsecs, buf);
        } else {
            /*
             * We now know -secs will fit into a guint32;
             * negate it and pass that to
             * unsigned_time_secs_to_str_buf().
             */
            unsigned_time_secs_to_str_buf(-time_val, frac, is_nsecs, buf);
        }
    } else
        unsigned_time_secs_to_str_buf(time_val, frac, is_nsecs, buf);
}

gchar *
signed_time_secs_to_str(wmem_allocator_t *scope, const gint32 time_val)
{
    wmem_strbuf_t *buf;

    if (time_val == 0) {
        return wmem_strdup(scope, "0 seconds");
    }

    buf = wmem_strbuf_new_sized(scope, TIME_SECS_LEN+1);

    signed_time_secs_to_str_buf(time_val, 0, FALSE, buf);

    return wmem_strbuf_finalize(buf);
}

/*
 * Convert a signed value in milliseconds to a string, giving time in days,
 * hours, minutes, and seconds, and put the result into a buffer.
 */
gchar *
signed_time_msecs_to_str(wmem_allocator_t *scope, gint32 time_val)
{
    wmem_strbuf_t *buf;
    int msecs;

    if (time_val == 0) {
        return wmem_strdup(scope, "0 seconds");
    }

    buf = wmem_strbuf_new_sized(scope, TIME_SECS_LEN+1+3+1);

    if (time_val<0) {
        /* oops we got passed a negative time */
        time_val= -time_val;
        msecs = time_val % 1000;
        time_val /= 1000;
        time_val= -time_val;
    } else {
        msecs = time_val % 1000;
        time_val /= 1000;
    }

    signed_time_secs_to_str_buf(time_val, msecs, FALSE, buf);

    return wmem_strbuf_finalize(buf);
}

/*
 * Display a relative time as days/hours/minutes/seconds.
 */
gchar *
rel_time_to_str(wmem_allocator_t *scope, const nstime_t *rel_time)
{
    wmem_strbuf_t *buf;
    gint32 time_val;
    gint32 nsec;

    /* If the nanoseconds part of the time stamp is negative,
       print its absolute value and, if the seconds part isn't
       (the seconds part should be zero in that case), stick
       a "-" in front of the entire time stamp. */
    time_val = (gint) rel_time->secs;
    nsec = rel_time->nsecs;
    if (time_val == 0 && nsec == 0) {
        return wmem_strdup(scope, "0.000000000 seconds");
    }

    buf = wmem_strbuf_new_sized(scope, 1+TIME_SECS_LEN+1+6+1);

    if (nsec < 0) {
        nsec = -nsec;
        wmem_strbuf_append_c(buf, '-');

        /*
         * We assume here that "rel_time->secs" is negative
         * or zero; if it's not, the time stamp is bogus,
         * with a positive seconds and negative microseconds.
         */
        time_val = (gint) -rel_time->secs;
    }

    signed_time_secs_to_str_buf(time_val, nsec, TRUE, buf);

    return wmem_strbuf_finalize(buf);
}

/*
 * Number of characters required by a 64-bit signed number.
 */
#define CHARS_64_BIT_SIGNED	20	/* sign plus 19 digits */

/*
 * Number of characters required by a fractional part, in nanoseconds */
#define CHARS_NANOSECONDS	10	/* .000000001 */

/* Includes terminating '\0' */
#define NSTIME_SECS_LEN	(CHARS_64_BIT_SIGNED+CHARS_NANOSECONDS+1)

/*
 * Display a relative time as seconds.
 */
gchar *
rel_time_to_secs_str(wmem_allocator_t *scope, const nstime_t *rel_time)
{
    gchar *buf;

    buf = (gchar *)wmem_alloc(scope, NSTIME_SECS_LEN);

    display_signed_time(buf, NSTIME_SECS_LEN, rel_time, WS_TSPREC_NSEC);
    return buf;
}

gchar *
abs_time_to_unix_str(wmem_allocator_t *scope, const nstime_t *rel_time)
{
    gchar *buf;

    buf = (gchar *)wmem_alloc(scope, NSTIME_SECS_LEN);

    display_epoch_time(buf, NSTIME_SECS_LEN, rel_time, WS_TSPREC_NSEC);
    return buf;
}

/*
 * Generates a string representing the bits in a bitfield at "bit_offset" from an 8 bit boundary
 * with the length in bits of no_of_bits based on value.
 * Ex: ..xx x...
 */

char *
decode_bits_in_field(wmem_allocator_t *scope, const guint bit_offset, const gint no_of_bits, const guint64 value, const guint encoding)
{
    guint64 mask;
    char *str;
    int bit, str_p = 0;
    int i;
    int max_bits = MIN(64, no_of_bits);
    int no_leading_dots;

    mask = G_GUINT64_CONSTANT(1) << (max_bits-1);

    if (encoding & ENC_LITTLE_ENDIAN) {
        /* Bits within octet are numbered from LSB (0) to MSB (7).
         * The value in string is from most significant bit to lowest.
         * Calculate how many dots have to be printed at the beginning of string.
         */
        no_leading_dots = (8 - ((bit_offset + no_of_bits) % 8)) % 8;
    } else {
        no_leading_dots = bit_offset % 8;
    }

    /* Prepare the string, 256 pos for the bits and zero termination, + 64 for the spaces */
    str = (char *)wmem_alloc0(scope, 256+64);
    for (bit = 0; bit < no_leading_dots; bit++) {
        if (bit && !(bit % 4)) {
            str[str_p] = ' ';
            str_p++;
        }
        str[str_p] = '.';
        str_p++;
    }

    /* read the bits for the int */
    for (i = 0; i < max_bits; i++) {
        if (bit && !(bit % 4)) {
            str[str_p] = ' ';
            str_p++;
        }
        if (bit && !(bit % 8)) {
            str[str_p] = ' ';
            str_p++;
        }
        bit++;
        if ((value & mask) != 0) {
            str[str_p] = '1';
            str_p++;
        } else {
            str[str_p] = '0';
            str_p++;
        }
        mask = mask>>1;
    }

    for (; bit % 8; bit++) {
        if (bit && !(bit % 4)) {
            str[str_p] = ' ';
            str_p++;
        }
        str[str_p] = '.';
        str_p++;
    }
    return str;
}

gchar *
guid_to_str(wmem_allocator_t *scope, const e_guid_t *guid)
{
    gchar *buf;

    buf = (gchar *)wmem_alloc(scope, GUID_STR_LEN);
    return guid_to_str_buf(guid, buf, GUID_STR_LEN);
}

gchar *
guid_to_str_buf(const e_guid_t *guid, gchar *buf, int buf_len)
{
    char *tempptr = buf;

    if (buf_len < GUID_STR_LEN) {
        (void) g_strlcpy(buf, BUF_TOO_SMALL_ERR, buf_len); /* Let the unexpected value alert user */
        return buf;
    }

    /* 37 bytes */
    tempptr    = dword_to_hex(tempptr, guid->data1);        /*  8 bytes */
    *tempptr++ = '-';                                       /*  1 byte */
    tempptr    = word_to_hex(tempptr, guid->data2);         /*  4 bytes */
    *tempptr++ = '-';                                       /*  1 byte */
    tempptr    = word_to_hex(tempptr, guid->data3);         /*  4 bytes */
    *tempptr++ = '-';                                       /*  1 byte */
    tempptr    = bytes_to_hexstr(tempptr, &guid->data4[0], 2);  /*  4 bytes */
    *tempptr++ = '-';                                       /*  1 byte */
    tempptr    = bytes_to_hexstr(tempptr, &guid->data4[2], 6);  /* 12 bytes */

    *tempptr   = '\0';
    return buf;
}

const gchar *
port_type_to_str (port_type type)
{
    switch (type) {
        case PT_NONE:       return "NONE";
        case PT_SCTP:       return "SCTP";
        case PT_TCP:        return "TCP";
        case PT_UDP:        return "UDP";
        case PT_DCCP:       return "DCCP";
        case PT_IPX:        return "IPX";
        case PT_DDP:        return "DDP";
        case PT_IDP:        return "IDP";
        case PT_USB:        return "USB";
        case PT_I2C:        return "I2C";
        case PT_IBQP:       return "IBQP";
        case PT_BLUETOOTH:  return "BLUETOOTH";
        case PT_IWARP_MPA:  return "IWARP_MPA";
        default:            return "[Unknown]";
    }
}

/*
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
 *
 * Local variables:
 * c-basic-offset: 4
 * tab-width: 8
 * indent-tabs-mode: nil
 * End:
 *
 * vi: set shiftwidth=4 tabstop=8 expandtab:
 * :indentSize=4:tabSize=8:noTabs=true:
 */