Coverage Report

Created: 2026-08-14 06:45

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/wsutil/str_util.c
Line
Count
Source
1
/* str_util.c
2
 * String utility routines
3
 *
4
 * Wireshark - Network traffic analyzer
5
 * By Gerald Combs <gerald@wireshark.org>
6
 * Copyright 1998 Gerald Combs
7
 *
8
 * SPDX-License-Identifier: GPL-2.0-or-later
9
 */
10
11
#define _GNU_SOURCE
12
#include "config.h"
13
#include "str_util.h"
14
15
#include <string.h>
16
#include <locale.h>
17
#include <math.h>
18
19
#include <ws_codepoints.h>
20
21
#include <wsutil/to_str.h>
22
23
24
struct prefix_parameters {
25
    const char * const *prefix; /**< array of prefixes to represent unit multiplication factors. */
26
    int prefix_count;           /**< number of elements in the prefix array. */
27
    int power;                  /**< multiplication factor between prefixes. */
28
    int prefix_offset;          /**< index of element within the prefix array for "no prefix". */
29
};
30
31
static const char hex[16] = { '0', '1', '2', '3', '4', '5', '6', '7',
32
                              '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
33
34
/* Given a "flags" value passed into a formatting function, determine which
35
 * formatting parameters should apply.
36
 */
37
static const struct prefix_parameters *
38
0
prefix_parameters_for_flags(uint16_t flags) {
39
0
    static const char * const si_prefixes[] = {" a", " f", " p", " n", " μ", " m", " ", " k", " M", " G", " T", " P", " E"};
40
0
    static const struct prefix_parameters si_parameters = {si_prefixes, G_N_ELEMENTS(si_prefixes), 1000, 6};
41
0
    static const char * const iec_prefixes[] = {" ", " Ki", " Mi", " Gi", " Ti", " Pi", " Ei"};
42
0
    static const struct prefix_parameters iec_parameters = {iec_prefixes, G_N_ELEMENTS(iec_prefixes), 1024, 0};
43
44
0
    return (flags & FORMAT_SIZE_PREFIX_IEC) != 0 ? &iec_parameters : &si_parameters;
45
0
}
46
47
char *
48
wmem_strconcat(wmem_allocator_t *allocator, const char *first, ...)
49
10.8k
{
50
10.8k
    size_t  len;
51
10.8k
    va_list args;
52
10.8k
    char    *s;
53
10.8k
    char    *concat;
54
10.8k
    char    *ptr;
55
56
10.8k
    if (!first)
57
0
        return NULL;
58
59
10.8k
    len = 1 + strlen(first);
60
10.8k
    va_start(args, first);
61
21.9k
    while ((s = va_arg(args, char*))) {
62
11.1k
        len += strlen(s);
63
11.1k
    }
64
10.8k
    va_end(args);
65
66
10.8k
    ptr = concat = (char *)wmem_alloc(allocator, len);
67
68
10.8k
    ptr = g_stpcpy(ptr, first);
69
10.8k
    va_start(args, first);
70
21.9k
    while ((s = va_arg(args, char*))) {
71
11.1k
        ptr = g_stpcpy(ptr, s);
72
11.1k
    }
73
10.8k
    va_end(args);
74
75
10.8k
    return concat;
76
10.8k
}
77
78
char *
79
wmem_strjoin(wmem_allocator_t *allocator,
80
             const char *separator, const char *first, ...)
81
274
{
82
274
    size_t  len;
83
274
    va_list args;
84
274
    size_t separator_len;
85
274
    char    *s;
86
274
    char    *concat;
87
274
    char    *ptr;
88
89
274
    if (!first)
90
0
        return NULL;
91
92
274
    if (separator == NULL) {
93
0
        separator = "";
94
0
    }
95
96
274
    separator_len = strlen (separator);
97
98
274
    len = 1 + strlen(first); /* + 1 for null byte */
99
274
    va_start(args, first);
100
624
    while ((s = va_arg(args, char*))) {
101
350
        len += (separator_len + strlen(s));
102
350
    }
103
274
    va_end(args);
104
105
274
    ptr = concat = (char *)wmem_alloc(allocator, len);
106
274
    ptr = g_stpcpy(ptr, first);
107
274
    va_start(args, first);
108
624
    while ((s = va_arg(args, char*))) {
109
350
        ptr = g_stpcpy(ptr, separator);
110
350
        ptr = g_stpcpy(ptr, s);
111
350
    }
112
274
    va_end(args);
113
114
274
    return concat;
115
116
274
}
117
118
char *
119
wmem_strjoinv(wmem_allocator_t *allocator,
120
              const char *separator, char **str_array)
121
1
{
122
1
    char *string = NULL;
123
124
1
    ws_return_val_if(!str_array, NULL);
125
126
1
    if (separator == NULL) {
127
0
        separator = "";
128
0
    }
129
130
1
    if (str_array[0]) {
131
1
        int i;
132
1
        char *ptr;
133
1
        size_t len, separator_len;
134
135
1
        separator_len = strlen(separator);
136
137
        /* Get first part of length. Plus one for null byte. */
138
1
        len = 1 + strlen(str_array[0]);
139
        /* Get the full length, including the separators. */
140
1
        for (i = 1; str_array[i] != NULL; i++) {
141
0
            len += separator_len;
142
0
            len += strlen(str_array[i]);
143
0
        }
144
145
        /* Allocate and build the string. */
146
1
        string = (char *)wmem_alloc(allocator, len);
147
1
        ptr = g_stpcpy(string, str_array[0]);
148
1
        for (i = 1; str_array[i] != NULL; i++) {
149
0
            ptr = g_stpcpy(ptr, separator);
150
0
            ptr = g_stpcpy(ptr, str_array[i]);
151
0
        }
152
1
    } else {
153
0
        string = wmem_strdup(allocator, "");
154
0
    }
155
156
1
    return string;
157
158
1
}
159
160
char **
161
wmem_strsplit(wmem_allocator_t *allocator, const char *src,
162
        const char *delimiter, int max_tokens)
163
2.36k
{
164
2.36k
    char *splitted;
165
2.36k
    char *s;
166
2.36k
    unsigned tokens;
167
2.36k
    unsigned sep_len;
168
2.36k
    unsigned i;
169
2.36k
    char **vec;
170
171
2.36k
    if (!src || !delimiter || !delimiter[0])
172
0
        return NULL;
173
174
    /* An empty string results in an empty vector. */
175
2.36k
    if (!src[0]) {
176
142
        vec = wmem_new0(allocator, char *);
177
142
        return vec;
178
142
    }
179
180
2.22k
    splitted = wmem_strdup(allocator, src);
181
2.22k
    sep_len = (unsigned)strlen(delimiter);
182
183
2.22k
    if (max_tokens < 1)
184
698
        max_tokens = INT_MAX;
185
186
    /* Calculate the number of fields. */
187
2.22k
    s = splitted;
188
2.22k
    tokens = 1;
189
16.1k
    while (tokens < (unsigned)max_tokens && (s = strstr(s, delimiter))) {
190
13.9k
        s += sep_len;
191
13.9k
        tokens++;
192
13.9k
    }
193
194
2.22k
    vec = wmem_alloc_array(allocator, char *, tokens + 1);
195
196
    /* Populate the array of string tokens. */
197
2.22k
    s = splitted;
198
2.22k
    vec[0] = s;
199
2.22k
    tokens = 1;
200
16.1k
    while (tokens < (unsigned)max_tokens && (s = strstr(s, delimiter))) {
201
27.9k
        for (i = 0; i < sep_len; i++)
202
13.9k
            s[i] = '\0';
203
13.9k
        s += sep_len;
204
13.9k
        vec[tokens] = s;
205
13.9k
        tokens++;
206
207
13.9k
    }
208
209
2.22k
    vec[tokens] = NULL;
210
211
2.22k
    return vec;
212
2.36k
}
213
214
/*
215
 * wmem_ascii_strdown:
216
 * based on g_ascii_strdown.
217
 */
218
char*
219
wmem_ascii_strdown(wmem_allocator_t *allocator, const char *str, ssize_t len)
220
1.39k
{
221
1.39k
    char *result, *s;
222
1.39k
    size_t abs_len;
223
224
1.39k
    g_return_val_if_fail (str != NULL, NULL);
225
226
1.39k
    abs_len = (len < 0) ? strlen(str) : (size_t)len;
227
228
1.39k
    result = wmem_strndup(allocator, str, abs_len);
229
9.60k
    for (s = result; *s; s++)
230
8.21k
        *s = g_ascii_tolower (*s);
231
232
1.39k
    return result;
233
1.39k
}
234
235
int
236
ws_xton(char ch)
237
274
{
238
274
    switch (ch) {
239
31
        case '0': return 0;
240
6
        case '1': return 1;
241
14
        case '2': return 2;
242
6
        case '3': return 3;
243
6
        case '4': return 4;
244
3
        case '5': return 5;
245
2
        case '6': return 6;
246
5
        case '7': return 7;
247
9
        case '8': return 8;
248
156
        case '9': return 9;
249
8
        case 'a':  case 'A': return 10;
250
0
        case 'b':  case 'B': return 11;
251
4
        case 'c':  case 'C': return 12;
252
2
        case 'd':  case 'D': return 13;
253
0
        case 'e':  case 'E': return 14;
254
18
        case 'f':  case 'F': return 15;
255
4
        default: return -1;
256
274
    }
257
274
}
258
259
/* Convert all ASCII letters to lower case, in place. */
260
char *
261
ascii_strdown_inplace(char *str)
262
10.9k
{
263
10.9k
    char *s;
264
265
70.0k
    for (s = str; *s; s++)
266
        /* What 'g_ascii_tolower (char c)' does, this should be slightly more efficient */
267
59.1k
        *s = g_ascii_isupper (*s) ? *s - 'A' + 'a' : *s;
268
269
10.9k
    return (str);
270
10.9k
}
271
272
/* Convert all ASCII letters to upper case, in place. */
273
char *
274
ascii_strup_inplace(char *str)
275
679
{
276
679
    char *s;
277
278
6.43k
    for (s = str; *s; s++)
279
        /* What 'g_ascii_toupper (char c)' does, this should be slightly more efficient */
280
5.75k
        *s = g_ascii_islower (*s) ? *s - 'a' + 'A' : *s;
281
282
679
    return (str);
283
679
}
284
285
/* Check if an entire string is printable. */
286
bool
287
isprint_string(const char *str)
288
31
{
289
31
    unsigned pos;
290
291
    /* Loop until we reach the end of the string (a null) */
292
76
    for(pos = 0; str[pos] != '\0'; pos++){
293
61
        if(!g_ascii_isprint(str[pos])){
294
            /* The string contains a non-printable character */
295
16
            return false;
296
16
        }
297
61
    }
298
299
    /* The string contains only printable characters */
300
15
    return true;
301
31
}
302
303
/* Check if an entire UTF-8 string is printable. */
304
bool
305
isprint_utf8_string(const char *str, const unsigned length)
306
1.60k
{
307
1.60k
    const char *strend = str + length;
308
309
1.60k
    if (!g_utf8_validate(str, length, NULL)) {
310
1.25k
        return false;
311
1.25k
    }
312
313
1.03k
    while (str < strend) {
314
        /* This returns false for G_UNICODE_CONTROL | G_UNICODE_FORMAT |
315
         * G_UNICODE_UNASSIGNED | G_UNICODE_SURROGATE
316
         * XXX: Could it be ok to have certain format characters, e.g.
317
         * U+00AD SOFT HYPHEN? If so, format_text() should be changed too.
318
         */
319
877
        if (!g_unichar_isprint(g_utf8_get_char(str))) {
320
196
            return false;
321
196
        }
322
681
        str = g_utf8_next_char(str);
323
681
    }
324
325
155
    return true;
326
351
}
327
328
/* Check if an entire string is digits. */
329
bool
330
isdigit_string(const char *str)
331
20
{
332
20
    unsigned pos;
333
334
    /* Loop until we reach the end of the string (a null) */
335
50
    for(pos = 0; str[pos] != '\0'; pos++){
336
44
        if(!g_ascii_isdigit(str[pos])){
337
            /* The string contains a non-digit character */
338
14
            return false;
339
14
        }
340
44
    }
341
342
    /* The string contains only digits */
343
6
    return true;
344
20
}
345
346
const char *
347
ws_ascii_strcasestr(const char *haystack, const char *needle)
348
0
{
349
    /* Do not use strcasestr() here, even if a system has it, as it is
350
     * locale-dependent (and has different results for e.g. Turkic languages.)
351
     * FreeBSD, NetBSD, macOS have a strcasestr_l() that could be used.
352
     */
353
0
    size_t hlen = strlen(haystack);
354
0
    size_t nlen = strlen(needle);
355
356
0
    while (hlen-- >= nlen) {
357
0
        if (!g_ascii_strncasecmp(haystack, needle, nlen))
358
0
            return haystack;
359
0
        haystack++;
360
0
    }
361
0
    return NULL;
362
0
}
363
364
/* Return the last occurrence of ch in the n bytes of haystack.
365
 * If not found or n is 0, return NULL. */
366
const uint8_t *
367
ws_memrchr(const void *_haystack, int ch, size_t n)
368
0
{
369
0
#ifdef HAVE_MEMRCHR
370
0
    return memrchr(_haystack, ch, n);
371
#else
372
    /* A generic implementation. This could be optimized considerably,
373
     * e.g. by fetching a word at a time.
374
     */
375
    if (n == 0) {
376
        return NULL;
377
    }
378
    const uint8_t *haystack = _haystack;
379
    const uint8_t *p;
380
    uint8_t c = (uint8_t)ch;
381
382
    const uint8_t *const end = haystack + n - 1;
383
384
    for (p = end; p >= haystack; --p) {
385
        if (*p == c) {
386
            return p;
387
        }
388
    }
389
390
    return NULL;
391
#endif /* HAVE_MEMRCHR */
392
0
}
393
394
/* Return the first occurrence of ch in the null-terminated string str.
395
 * If not found, return a pointer to the null-terminator. */
396
char *
397
ws_strchrnul(const char *str, int ch)
398
0
{
399
0
#ifdef HAVE_STRCHRNUL
400
 #ifdef __APPLE__
401
    /* strchrnul was introduced in macOS 15.4, runtime check if building
402
     * with a newer SDK than that but an older deployment target. */
403
    if (__builtin_available(macOS 15.4, *)) {
404
        return (char*)strchrnul(str, ch);
405
    } else {
406
        /* Minimal generic implementation. */
407
        while (*str != '\0' && *str != (char)ch) {
408
            str++;
409
        }
410
        return (char *)str;
411
    }
412
 #else
413
    /* Cast in case someone has an implementation that works like one of those
414
     * fancy C23 qualifier-preserving versions. */
415
0
    return (char*)strchrnul(str, ch);
416
0
 #endif
417
#else
418
    /* Minimal generic implementation. */
419
    while (*str != '\0' && *str != (char)ch) {
420
        str++;
421
    }
422
    return (char *)str;
423
#endif
424
0
}
425
426
static const char *thousands_grouping_fmt;
427
static const char *thousands_grouping_fmt_flt;
428
429
DIAG_OFF(format)
430
0
static void test_printf_thousands_grouping(void) {
431
    /* test whether wmem_strbuf works with "'" flag character */
432
0
    wmem_strbuf_t *buf = wmem_strbuf_new(NULL, NULL);
433
0
    wmem_strbuf_append_printf(buf, "%'d", 22);
434
0
    if (g_strcmp0(wmem_strbuf_get_str(buf), "22") == 0) {
435
0
        thousands_grouping_fmt = "%'"PRId64;
436
0
        thousands_grouping_fmt_flt = "%'.*f";
437
0
    } else {
438
        /* Don't use */
439
0
        thousands_grouping_fmt = "%"PRId64;
440
0
        thousands_grouping_fmt_flt = "%.*f";
441
0
    }
442
0
    wmem_strbuf_destroy(buf);
443
0
}
444
DIAG_ON(format)
445
446
static const char* decimal_point = NULL;
447
448
0
static void truncate_numeric_strbuf(wmem_strbuf_t *strbuf, int n) {
449
450
0
    const char *s = wmem_strbuf_get_str(strbuf);
451
0
    const char *p;
452
0
    int count;
453
454
0
    if (decimal_point == NULL) {
455
0
        decimal_point = localeconv()->decimal_point;
456
0
    }
457
458
0
    p = strchr(s, decimal_point[0]);
459
0
    if (p != NULL) {
460
0
        count = n;
461
0
        while (count >= 0) {
462
0
            count--;
463
0
            if (*p == '\0')
464
0
                break;
465
0
            p++;
466
0
        }
467
468
0
        p--;
469
0
        while (*p == '0') {
470
0
            p--;
471
0
        }
472
473
0
        if (*p != decimal_point[0]) {
474
0
            p++;
475
0
        }
476
0
        wmem_strbuf_truncate(strbuf, (size_t)(p - s));
477
0
    }
478
0
}
479
480
/* Given a floating point value, return it in a human-readable format,
481
 * using units with metric prefixes (falling back to scientific notation
482
 * with the base units if outside the range.)
483
 */
484
char *
485
format_units(wmem_allocator_t *allocator, double size,
486
             format_size_units_e unit, uint16_t flags,
487
             int precision)
488
0
{
489
0
    wmem_strbuf_t *human_str = wmem_strbuf_new(allocator, NULL);
490
0
    bool is_small = false;
491
    /* is_small is when to use the longer, spelled out unit.
492
     * We use it for inf, NaN, 0, and unprefixed small values,
493
     * but not for unprefixed values using scientific notation
494
     * the value is outside the supported prefix range.
495
     */
496
0
    bool scientific = false;
497
0
    double abs_size = fabs(size);
498
0
    const struct prefix_parameters * const pp = prefix_parameters_for_flags(flags);
499
0
    int prefix_index = pp->prefix_offset;
500
0
    char *ret_val;
501
502
0
    if (thousands_grouping_fmt == NULL)
503
0
        test_printf_thousands_grouping();
504
505
0
    if (isfinite(size) && size != 0.0) {
506
507
0
        double comp = precision == 0 ? 10.0 : 1.0;
508
509
        /* For precision 0, use the range [10, 10*power) because only
510
         * one significant digit is not as useful. This is what format_size
511
         * does for integers. ("ls -h" uses one digit after the decimal
512
         * point only for the [1, 10) range, g_format_size() always displays
513
         * tenths.) Prefer non-prefixed units for the range [1,10), though.
514
         *
515
         * We have a limited number of units to check, so this (which
516
         * can be unrolled) is presumably faster than log + floor + pow/exp
517
         */
518
0
        if (abs_size < 1.0) {
519
0
            while (abs_size < comp) {
520
0
                abs_size *= pp->power;
521
0
                if (prefix_index == 0) {
522
0
                    scientific = true;
523
0
                    break;
524
0
                }
525
0
                prefix_index--;
526
0
            }
527
0
        } else {
528
0
            while (abs_size >= comp * pp->power) {
529
0
                abs_size /= pp->power;
530
0
                if (prefix_index == pp->prefix_count - 1) {
531
0
                    scientific = true;
532
0
                    break;
533
0
                }
534
0
                prefix_index++;
535
0
            }
536
0
        }
537
0
    }
538
539
0
    if (scientific) {
540
0
        wmem_strbuf_append_printf(human_str, "%.*g", precision + 1, size);
541
0
        prefix_index = pp->prefix_offset;
542
0
    } else {
543
0
        if (prefix_index == pp->prefix_offset) {
544
0
            is_small = true;
545
0
        }
546
0
        size = copysign(abs_size, size);
547
        // Truncate trailing zeros, but do it this way because we know
548
        // we don't want scientific notation, and we don't want %g to
549
        // switch to that if precision is small. (We could always use
550
        // %g when precision is large.)
551
0
        wmem_strbuf_append_printf(human_str, thousands_grouping_fmt_flt, precision, size);
552
0
        truncate_numeric_strbuf(human_str, precision);
553
        // XXX - when rounding to a certain precision, printf might
554
        // round up to "power" from something like 999.99999995, which
555
        // looks a little odd on a graph when transitioning from 1,000 bytes
556
        // (for values just under 1 kB) to 1 kB (for values 1 kB and larger.)
557
        // Due to edge cases in binary fp representation and how printf might
558
        // round things, the right way to handle it is taking the printf output
559
        // and comparing it to "1000" and "1024" and adjusting the exponent
560
        // if so - though we need to compare to the version with the thousands
561
        // separator if we have that (which makes it harder to use strnatcmp
562
        // as is.)
563
0
    }
564
565
0
    wmem_strbuf_append(human_str, pp->prefix[prefix_index]);
566
567
0
    switch (unit) {
568
0
        case FORMAT_SIZE_UNIT_NONE:
569
0
            break;
570
0
        case FORMAT_SIZE_UNIT_BYTES:
571
0
            wmem_strbuf_append(human_str, is_small ? "bytes" : "B");
572
0
            break;
573
0
        case FORMAT_SIZE_UNIT_BITS:
574
0
            wmem_strbuf_append(human_str, is_small ? "bits" : "b");
575
0
            break;
576
0
        case FORMAT_SIZE_UNIT_BITS_S:
577
0
            wmem_strbuf_append(human_str, is_small ? "bits/s" : "bps");
578
0
            break;
579
0
        case FORMAT_SIZE_UNIT_BYTES_S:
580
0
            wmem_strbuf_append(human_str, is_small ? "bytes/s" : "Bps");
581
0
            break;
582
0
        case FORMAT_SIZE_UNIT_PACKETS:
583
0
            wmem_strbuf_append(human_str, is_small ? "packets" : "pkts");
584
0
            break;
585
0
        case FORMAT_SIZE_UNIT_PACKETS_S:
586
0
            wmem_strbuf_append(human_str, is_small ? "packets/s" : "pkts/s");
587
0
            break;
588
0
        case FORMAT_SIZE_UNIT_EVENTS:
589
0
            wmem_strbuf_append(human_str, is_small ? "events" : "evts");
590
0
            break;
591
0
        case FORMAT_SIZE_UNIT_EVENTS_S:
592
0
            wmem_strbuf_append(human_str, is_small ? "events/s" : "evts/s");
593
0
            break;
594
0
        case FORMAT_SIZE_UNIT_FIELDS:
595
0
            wmem_strbuf_append(human_str, is_small ? "fields" : "flds");
596
0
            break;
597
0
        case FORMAT_SIZE_UNIT_SECONDS:
598
0
            wmem_strbuf_append(human_str, is_small ? "seconds" : "s");
599
0
            break;
600
0
        case FORMAT_SIZE_UNIT_ERLANGS:
601
0
            wmem_strbuf_append(human_str, is_small ? "erlangs" : "E");
602
0
            break;
603
0
        default:
604
0
            ws_assert_not_reached();
605
0
    }
606
607
0
    ret_val = wmem_strbuf_finalize(human_str);
608
    /* Convention is a space between the value and the units. If we have
609
     * a prefix, the space is before the prefix. There are two possible
610
     * uses of FORMAT_SIZE_UNIT_NONE:
611
     * 1. Add a unit immediately after the string returned. In this case,
612
     *    we would want the string to end with a space if there's no prefix.
613
     * 2. The unit appears somewhere else, e.g. in a legend, header, or
614
     *    different column. In this case, we don't want the string to end
615
     *    with a space if there's no prefix.
616
     * chomping the string here, as we've traditionally done, optimizes for
617
     * the latter case but makes the former case harder.
618
     * Perhaps the right approach is to distinguish the cases with a new
619
     * enum value.
620
     */
621
0
    return g_strchomp(ret_val);
622
0
}
623
624
/* Given a size, return its value in a human-readable format */
625
/* This doesn't handle fractional values. We might want to just
626
 * call the version with the double and precision 0 (possibly
627
 * slower due to the use of floating point math, but do we care?)
628
 */
629
char *
630
format_size_wmem(wmem_allocator_t *allocator, int64_t size,
631
                 format_size_units_e unit, uint16_t flags)
632
0
{
633
0
    wmem_strbuf_t *human_str = wmem_strbuf_new(allocator, NULL);
634
0
    bool is_small = false;
635
0
    const struct prefix_parameters * const pp = prefix_parameters_for_flags(flags);
636
0
    char *ret_val;
637
638
0
    if (thousands_grouping_fmt == NULL)
639
0
        test_printf_thousands_grouping();
640
641
0
    int prefix_index = pp->prefix_offset;
642
0
    int64_t scale = 1;
643
0
    while (prefix_index + 1 < pp->prefix_count && scale < INT64_MAX / (10 * pp->power) && size >= scale * pp->power * 10) {
644
0
        prefix_index++;
645
0
        scale *= pp->power;
646
0
    }
647
648
0
    wmem_strbuf_append_printf(human_str, thousands_grouping_fmt, size / scale);
649
0
    wmem_strbuf_append(human_str, pp->prefix[prefix_index]);
650
0
    is_small = prefix_index == pp->prefix_offset;
651
652
0
    switch (unit) {
653
0
        case FORMAT_SIZE_UNIT_NONE:
654
0
            break;
655
0
        case FORMAT_SIZE_UNIT_BYTES:
656
0
            wmem_strbuf_append(human_str, is_small ? "bytes" : "B");
657
0
            break;
658
0
        case FORMAT_SIZE_UNIT_BITS:
659
0
            wmem_strbuf_append(human_str, is_small ? "bits" : "b");
660
0
            break;
661
0
        case FORMAT_SIZE_UNIT_BITS_S:
662
0
            wmem_strbuf_append(human_str, is_small ? "bits/s" : "bps");
663
0
            break;
664
0
        case FORMAT_SIZE_UNIT_BYTES_S:
665
0
            wmem_strbuf_append(human_str, is_small ? "bytes/s" : "Bps");
666
0
            break;
667
0
        case FORMAT_SIZE_UNIT_PACKETS:
668
0
            wmem_strbuf_append(human_str, is_small ? "packets" : "pkts");
669
0
            break;
670
0
        case FORMAT_SIZE_UNIT_PACKETS_S:
671
0
            wmem_strbuf_append(human_str, is_small ? "packets/s" : "pkts/s");
672
0
            break;
673
0
        case FORMAT_SIZE_UNIT_EVENTS:
674
0
            wmem_strbuf_append(human_str, is_small ? "events" : "evts");
675
0
            break;
676
0
        case FORMAT_SIZE_UNIT_EVENTS_S:
677
0
            wmem_strbuf_append(human_str, is_small ? "events/s" : "evts/s");
678
0
            break;
679
0
        case FORMAT_SIZE_UNIT_FIELDS:
680
0
            wmem_strbuf_append(human_str, is_small ? "fields" : "flds");
681
0
            break;
682
0
        case FORMAT_SIZE_UNIT_SECONDS:
683
0
            wmem_strbuf_append(human_str, is_small ? "seconds" : "s");
684
0
            break;
685
0
        case FORMAT_SIZE_UNIT_ERLANGS:
686
0
            wmem_strbuf_append(human_str, is_small ? "erlangs" : "E");
687
0
            break;
688
0
        default:
689
0
            ws_assert_not_reached();
690
0
    }
691
692
0
    ret_val = wmem_strbuf_finalize(human_str);
693
0
    return g_strchomp(ret_val);
694
0
}
695
696
char
697
printable_char_or_period(char c)
698
4.78k
{
699
4.78k
    return g_ascii_isprint(c) ? c : '.';
700
4.78k
}
701
702
/*
703
 * This is used by the display filter engine and must be compatible
704
 * with display filter syntax.
705
 */
706
static inline bool
707
escape_char(char c, char *p)
708
44
{
709
44
    int r = -1;
710
44
    ws_assert(p);
711
712
    /*
713
     * backslashes and double-quotes must be escaped (double-quotes
714
     * are escaped by passing '"' as quote_char in escape_string_len)
715
     * whitespace is also escaped.
716
     */
717
44
    switch (c) {
718
0
        case '\a': r = 'a'; break;
719
0
        case '\b': r = 'b'; break;
720
0
        case '\f': r = 'f'; break;
721
1
        case '\n': r = 'n'; break;
722
3
        case '\r': r = 'r'; break;
723
1
        case '\t': r = 't'; break;
724
0
        case '\v': r = 'v'; break;
725
0
        case '\\': r = '\\'; break;
726
23
        case '\0': r = '0'; break;
727
44
    }
728
729
44
    if (r != -1) {
730
28
        *p = r;
731
28
        return true;
732
28
    }
733
16
    return false;
734
44
}
735
736
static inline bool
737
escape_null(char c, char *p)
738
0
{
739
0
    ws_assert(p);
740
0
    if (c == '\0') {
741
0
        *p = '0';
742
0
        return true;
743
0
    }
744
0
    return false;
745
0
}
746
747
static char *
748
escape_string_len(wmem_allocator_t *alloc, const char *string, ssize_t len,
749
                    bool (*escape_func)(char c, char *p), bool add_quotes,
750
                    char quote_char, bool double_quote)
751
0
{
752
0
    char c, r;
753
0
    wmem_strbuf_t *buf;
754
0
    size_t abs_len, alloc_size, i;
755
756
0
    abs_len = (len < 0) ? strlen(string) : (size_t)len;
757
758
0
    alloc_size = abs_len;
759
0
    if (add_quotes)
760
0
        alloc_size += 2;
761
762
0
    buf = wmem_strbuf_new_sized(alloc, alloc_size);
763
764
0
    if (add_quotes && quote_char != '\0')
765
0
        wmem_strbuf_append_c(buf, quote_char);
766
767
0
    for (i = 0; i < abs_len; i++) {
768
0
        c = string[i];
769
0
        if ((escape_func(c, &r))) {
770
0
            wmem_strbuf_append_c(buf, '\\');
771
0
            wmem_strbuf_append_c(buf, r);
772
0
        }
773
0
        else if (c == quote_char && quote_char != '\0') {
774
            /* If quoting, we must escape the quote_char somehow. */
775
0
            if (double_quote) {
776
0
                wmem_strbuf_append_c(buf, c);
777
0
                wmem_strbuf_append_c(buf, c);
778
0
            } else {
779
0
                wmem_strbuf_append_c(buf, '\\');
780
0
                wmem_strbuf_append_c(buf, c);
781
0
            }
782
0
        }
783
0
        else if (c == '\\' && quote_char != '\0' && !double_quote) {
784
            /* If quoting, and escaping the quote_char with a backslash,
785
             * then backslash must be escaped, even if escape_func doesn't. */
786
0
            wmem_strbuf_append_c(buf, '\\');
787
0
            wmem_strbuf_append_c(buf, '\\');
788
0
        }
789
0
        else {
790
            /* Other UTF-8 bytes are passed through. */
791
0
            wmem_strbuf_append_c(buf, c);
792
0
        }
793
0
    }
794
795
0
    if (add_quotes && quote_char != '\0')
796
0
        wmem_strbuf_append_c(buf, quote_char);
797
798
0
    return wmem_strbuf_finalize(buf);
799
0
}
800
801
char *
802
ws_escape_string_len(wmem_allocator_t *alloc, const char *string, ssize_t len, bool add_quotes)
803
0
{
804
0
    return escape_string_len(alloc, string, len, escape_char, add_quotes, '"', false);
805
0
}
806
807
char *
808
ws_escape_string(wmem_allocator_t *alloc, const char *string, bool add_quotes)
809
0
{
810
0
    return escape_string_len(alloc, string, -1, escape_char, add_quotes, '"', false);
811
0
}
812
813
char *ws_escape_null(wmem_allocator_t *alloc, const char *string, size_t len, bool add_quotes)
814
0
{
815
    /* XXX: The existing behavior (maintained) here is not to escape
816
     * backslashes even though NUL is escaped.
817
     */
818
0
    return escape_string_len(alloc, string, len, escape_null, add_quotes, add_quotes ? '"' : '\0', false);
819
0
}
820
821
char *ws_escape_csv(wmem_allocator_t *alloc, const char *string, bool add_quotes, char quote_char, bool double_quote, bool escape_whitespace)
822
0
{
823
0
    if (escape_whitespace)
824
0
        return escape_string_len(alloc, string, -1, escape_char, add_quotes, quote_char, double_quote);
825
0
    else
826
0
        return escape_string_len(alloc, string, -1, escape_null, add_quotes, quote_char, double_quote);
827
0
}
828
829
const char *
830
ws_strerrorname_r(int errnum, char *buf, size_t buf_size)
831
0
{
832
0
#ifdef HAVE_STRERRORNAME_NP
833
0
    const char *errstr = strerrorname_np(errnum);
834
0
    if (errstr != NULL) {
835
0
        (void)g_strlcpy(buf, errstr, buf_size);
836
0
        return buf;
837
0
    }
838
0
#endif
839
0
    snprintf(buf, buf_size, "Errno(%d)", errnum);
840
0
    return buf;
841
0
}
842
843
char *
844
ws_strdup_underline(wmem_allocator_t *allocator, long offset, size_t len)
845
0
{
846
0
    if (offset < 0)
847
0
        return NULL;
848
849
0
    wmem_strbuf_t *buf = wmem_strbuf_new_sized(allocator, offset + len);
850
851
0
    for (int i = 0; i < offset; i++) {
852
0
        wmem_strbuf_append_c(buf, ' ');
853
0
    }
854
0
    wmem_strbuf_append_c(buf, '^');
855
856
0
    for (size_t l = len; l > 1; l--) {
857
0
        wmem_strbuf_append_c(buf, '~');
858
0
    }
859
860
0
    return wmem_strbuf_finalize(buf);
861
0
}
862
863
312k
#define    INITIAL_FMTBUF_SIZE    128
864
865
/*
866
 * Declare, and initialize, the variables used for an output buffer.
867
 */
868
#define FMTBUF_VARS \
869
156k
    char *fmtbuf = (char*)wmem_alloc(allocator, INITIAL_FMTBUF_SIZE); \
870
156k
    unsigned fmtbuf_len = INITIAL_FMTBUF_SIZE; \
871
156k
    unsigned column = 0
872
873
/*
874
 * Expand the buffer to be large enough to add nbytes bytes, plus a
875
 * terminating '\0'.
876
 */
877
#define FMTBUF_EXPAND(nbytes) \
878
    /* \
879
     * Is there enough room for those bytes and also enough room for \
880
     * a terminating '\0'? \
881
     */ \
882
2.56M
    if (column+(nbytes+1) >= fmtbuf_len) { \
883
25.0k
        /* \
884
25.0k
         * Double the buffer's size if it's not big enough. \
885
25.0k
         * The size of the buffer starts at 128, so doubling its size \
886
25.0k
         * adds at least another 128 bytes, which is more than enough \
887
25.0k
         * for one more character plus a terminating '\0'. \
888
25.0k
         */ \
889
25.0k
        if (ckd_mul(&fmtbuf_len, fmtbuf_len, 2)) { \
890
0
            ws_debug("overflow!"); \
891
0
            FMTBUF_ENDSTR; \
892
0
            return fmtbuf; \
893
0
        } \
894
25.0k
        if (column+(nbytes+1) >= fmtbuf_len) { \
895
449
            if (ckd_add(&fmtbuf_len, fmtbuf_len, (column + nbytes + 2) - fmtbuf_len)) { \
896
0
                ws_debug("overflow!"); \
897
0
                FMTBUF_ENDSTR; \
898
0
                return fmtbuf; \
899
0
            } \
900
449
        } \
901
25.0k
        fmtbuf = (char *)wmem_realloc(allocator, fmtbuf, fmtbuf_len); \
902
25.0k
    }
903
904
/*
905
 * Put a byte into the buffer; space must have been ensured for it.
906
 */
907
#define FMTBUF_PUTCHAR(b) \
908
3.29M
    fmtbuf[column] = (b); \
909
3.29M
    column++
910
911
/*
912
 * Add the one-byte argument, as an octal escape sequence, to the end
913
 * of the buffer.
914
 */
915
#define FMTBUF_PUTBYTE_OCTAL(b) \
916
733k
    FMTBUF_PUTCHAR((((b)>>6)&03) + '0'); \
917
733k
    FMTBUF_PUTCHAR((((b)>>3)&07) + '0'); \
918
733k
    FMTBUF_PUTCHAR((((b)>>0)&07) + '0')
919
920
/*
921
 * Add the one-byte argument, as a hex escape sequence, to the end
922
 * of the buffer.
923
 */
924
#define FMTBUF_PUTBYTE_HEX(b) \
925
    FMTBUF_PUTCHAR('\\'); \
926
    FMTBUF_PUTCHAR('x'); \
927
    FMTBUF_PUTCHAR(hex[((b) >> 4) & 0xF]); \
928
    FMTBUF_PUTCHAR(hex[((b) >> 0) & 0xF])
929
930
#define FMTBUF_PUTBYTES(bytes, len) \
931
360k
    FMTBUF_EXPAND(len) \
932
360k
    memcpy(&fmtbuf[column], bytes, len); \
933
360k
    column += (unsigned)len; // FMTBUF_EXPAND checks for overflow
934
935
/*
936
 * Put the trailing '\0' at the end of the buffer.
937
 */
938
#define FMTBUF_ENDSTR \
939
156k
    fmtbuf[column] = '\0'
940
941
static char *
942
format_text_internal(wmem_allocator_t *allocator,
943
                        const unsigned char *string, size_t len,
944
                        bool replace_space)
945
156k
{
946
156k
    FMTBUF_VARS;
947
156k
    const unsigned char *prev = string;
948
156k
    const unsigned char *stringend = string + len;
949
156k
    unsigned char c;
950
156k
    size_t printable_bytes = 0;
951
952
2.80M
    while (string < stringend) {
953
        /*
954
         * Get the first byte of this character.
955
         */
956
2.64M
        c = *string++;
957
2.64M
        if ((0x20 <= c) && (c < 0x7F)) {
958
            /*
959
             * Printable ASCII, so not part of a multi-byte UTF-8 sequence.
960
             * Make sure there's enough room for one more byte, and add
961
             * the character.
962
             */
963
1.17M
            printable_bytes++;
964
1.46M
        } else {
965
1.46M
            if (printable_bytes) {
966
282k
                FMTBUF_PUTBYTES(prev, printable_bytes);
967
282k
                printable_bytes = 0;
968
282k
            }
969
1.46M
            if (replace_space && g_ascii_isspace(c)) {
970
                /*
971
                 * ASCII, so not part of a multi-byte UTF-8 sequence, but
972
                 * not printable, but is a space character; show it as a
973
                 * blank.
974
                 *
975
                 * Make sure there's enough room for one more byte, and add
976
                 * the blank.
977
                 */
978
4.28k
                FMTBUF_EXPAND(1);
979
4.28k
                FMTBUF_PUTCHAR(' ');
980
1.46M
            } else if (c < 128) {
981
                /*
982
                 * ASCII, so not part of a multi-byte UTF-8 sequence, but not
983
                 * printable.
984
                 *
985
                 * That requires a minimum of 2 bytes, one for the backslash
986
                 * and one for a letter, so make sure we have enough room
987
                 * for that, plus a trailing '\0'.
988
                 */
989
806k
                FMTBUF_EXPAND(2);
990
806k
                FMTBUF_PUTCHAR('\\');
991
806k
                switch (c) {
992
993
7.95k
                    case '\a':
994
7.95k
                        FMTBUF_PUTCHAR('a');
995
7.95k
                        break;
996
997
13.1k
                    case '\b':
998
13.1k
                        FMTBUF_PUTCHAR('b'); /* BS */
999
13.1k
                        break;
1000
1001
8.30k
                    case '\f':
1002
8.30k
                        FMTBUF_PUTCHAR('f'); /* FF */
1003
8.30k
                        break;
1004
1005
9.01k
                    case '\n':
1006
9.01k
                        FMTBUF_PUTCHAR('n'); /* NL */
1007
9.01k
                        break;
1008
1009
16.4k
                    case '\r':
1010
16.4k
                        FMTBUF_PUTCHAR('r'); /* CR */
1011
16.4k
                        break;
1012
1013
7.70k
                    case '\t':
1014
7.70k
                        FMTBUF_PUTCHAR('t'); /* tab */
1015
7.70k
                        break;
1016
1017
9.70k
                    case '\v':
1018
9.70k
                        FMTBUF_PUTCHAR('v');
1019
9.70k
                        break;
1020
1021
733k
                    default:
1022
                        /*
1023
                         * We've already put the backslash, but this
1024
                         * will put 3 more characters for the octal
1025
                         * number; make sure we have enough room for
1026
                         * that, plus the trailing '\0'.
1027
                         */
1028
733k
                        FMTBUF_EXPAND(3);
1029
733k
                        FMTBUF_PUTBYTE_OCTAL(c);
1030
733k
                        break;
1031
806k
                }
1032
806k
            } else {
1033
                /*
1034
                 * We've fetched the first byte of a multi-byte UTF-8
1035
                 * sequence into c.
1036
                 */
1037
658k
                int utf8_len;
1038
658k
                unsigned char mask;
1039
658k
                gunichar uc;
1040
658k
                unsigned char first;
1041
1042
658k
                if ((c & 0xe0) == 0xc0) {
1043
                    /* Starts a 2-byte UTF-8 sequence; 1 byte left */
1044
236k
                    utf8_len = 1;
1045
236k
                    mask = 0x1f;
1046
421k
                } else if ((c & 0xf0) == 0xe0) {
1047
                    /* Starts a 3-byte UTF-8 sequence; 2 bytes left */
1048
95.1k
                    utf8_len = 2;
1049
95.1k
                    mask = 0x0f;
1050
326k
                } else if ((c & 0xf8) == 0xf0) {
1051
                    /* Starts a 4-byte UTF-8 sequence; 3 bytes left */
1052
49.8k
                    utf8_len = 3;
1053
49.8k
                    mask = 0x07;
1054
276k
                } else if ((c & 0xfc) == 0xf8) {
1055
                    /* Starts an old-style 5-byte UTF-8 sequence; 4 bytes left */
1056
68.5k
                    utf8_len = 4;
1057
68.5k
                    mask = 0x03;
1058
208k
                } else if ((c & 0xfe) == 0xfc) {
1059
                    /* Starts an old-style 6-byte UTF-8 sequence; 5 bytes left */
1060
13.5k
                    utf8_len = 5;
1061
13.5k
                    mask = 0x01;
1062
194k
                } else {
1063
                    /* 0xfe or 0xff or a continuation byte - not valid */
1064
194k
                    utf8_len = -1;
1065
194k
                }
1066
658k
                if (utf8_len > 0) {
1067
                    /* Try to construct the Unicode character */
1068
463k
                    uc = c & mask;
1069
711k
                    for (int i = 0; i < utf8_len; i++) {
1070
592k
                        if (string >= stringend) {
1071
                            /*
1072
                             * Ran out of octets, so the character is
1073
                             * incomplete.  Put in a REPLACEMENT CHARACTER
1074
                             * instead, and then continue the loop, which
1075
                             * will terminate.
1076
                             */
1077
5.91k
                            uc = UNICODE_REPLACEMENT_CHARACTER;
1078
5.91k
                            break;
1079
5.91k
                        }
1080
586k
                        c = *string;
1081
586k
                        if ((c & 0xc0) != 0x80) {
1082
                            /*
1083
                             * Not valid UTF-8 continuation character; put in
1084
                             * a replacement character, and then re-process
1085
                             * this octet as the beginning of a new character.
1086
                             */
1087
338k
                            uc = UNICODE_REPLACEMENT_CHARACTER;
1088
338k
                            break;
1089
338k
                        }
1090
247k
                        string++;
1091
247k
                        uc = (uc << 6) | (c & 0x3f);
1092
247k
                    }
1093
1094
                    /*
1095
                     * If this isn't a valid Unicode character, put in
1096
                     * a REPLACEMENT CHARACTER.
1097
                     */
1098
463k
                    if (!g_unichar_validate(uc))
1099
1.53k
                        uc = UNICODE_REPLACEMENT_CHARACTER;
1100
463k
                } else {
1101
                    /* 0xfe or 0xff; put it a REPLACEMENT CHARACTER */
1102
194k
                    uc = UNICODE_REPLACEMENT_CHARACTER;
1103
194k
                }
1104
1105
                /*
1106
                 * OK, is it a printable Unicode character?
1107
                 */
1108
658k
                if (g_unichar_isprint(uc)) {
1109
                    /*
1110
                     * Yes - put it into the string as UTF-8.
1111
                     * This means that if it was an overlong
1112
                     * encoding, this will put out the right
1113
                     * sized encoding.
1114
                     */
1115
625k
                    if (uc < 0x80) {
1116
4.28k
                        first = 0;
1117
4.28k
                        utf8_len = 1;
1118
621k
                    } else if (uc < 0x800) {
1119
16.4k
                        first = 0xc0;
1120
16.4k
                        utf8_len = 2;
1121
604k
                    } else if (uc < 0x10000) {
1122
603k
                        first = 0xe0;
1123
603k
                        utf8_len = 3;
1124
603k
                    } else if (uc < 0x200000) {
1125
1.11k
                        first = 0xf0;
1126
1.11k
                        utf8_len = 4;
1127
1.11k
                    } else if (uc < 0x4000000) {
1128
                        /*
1129
                         * This should never happen, as Unicode doesn't
1130
                         * go that high.
1131
                         */
1132
0
                        first = 0xf8;
1133
0
                        utf8_len = 5;
1134
0
                    } else {
1135
                        /*
1136
                         * This should never happen, as Unicode doesn't
1137
                         * go that high.
1138
                         */
1139
0
                        first = 0xfc;
1140
0
                        utf8_len = 6;
1141
0
                    }
1142
625k
                    FMTBUF_EXPAND(utf8_len);
1143
1.85M
                    for (int i = utf8_len - 1; i > 0; i--) {
1144
1.22M
                        fmtbuf[column + i] = (uc & 0x3f) | 0x80;
1145
1.22M
                        uc >>= 6;
1146
1.22M
                    }
1147
625k
                    fmtbuf[column] = uc | first;
1148
625k
                    column += utf8_len;
1149
625k
                } else if (replace_space && g_unichar_isspace(uc)) {
1150
                    /*
1151
                     * Not printable, but is a space character; show it
1152
                     * as a blank.
1153
                     *
1154
                     * Make sure there's enough room for one more byte,
1155
                     * and add the blank.
1156
                     */
1157
15
                    FMTBUF_EXPAND(1);
1158
15
                    FMTBUF_PUTCHAR(' ');
1159
33.1k
                } else if (c < 128) {
1160
                    /*
1161
                     * ASCII, but not printable.
1162
                     * Yes, this could happen with an overlong encoding.
1163
                     *
1164
                     * That requires a minimum of 2 bytes, one for the
1165
                     * backslash and one for a letter, so make sure we
1166
                     * have enough room for that, plus a trailing '\0'.
1167
                     */
1168
0
                    FMTBUF_EXPAND(2);
1169
0
                    FMTBUF_PUTCHAR('\\');
1170
0
                    switch (c) {
1171
1172
0
                        case '\a':
1173
0
                            FMTBUF_PUTCHAR('a');
1174
0
                            break;
1175
1176
0
                        case '\b':
1177
0
                            FMTBUF_PUTCHAR('b'); /* BS */
1178
0
                            break;
1179
1180
0
                        case '\f':
1181
0
                            FMTBUF_PUTCHAR('f'); /* FF */
1182
0
                            break;
1183
1184
0
                        case '\n':
1185
0
                            FMTBUF_PUTCHAR('n'); /* NL */
1186
0
                            break;
1187
1188
0
                        case '\r':
1189
0
                            FMTBUF_PUTCHAR('r'); /* CR */
1190
0
                            break;
1191
1192
0
                        case '\t':
1193
0
                            FMTBUF_PUTCHAR('t'); /* tab */
1194
0
                            break;
1195
1196
0
                        case '\v':
1197
0
                            FMTBUF_PUTCHAR('v');
1198
0
                            break;
1199
1200
0
                        default:
1201
                            /*
1202
                             * We've already put the backslash, but this
1203
                             * will put 3 more characters for the octal
1204
                             * number; make sure we have enough room for
1205
                             * that, plus the trailing '\0'.
1206
                             */
1207
0
                            FMTBUF_EXPAND(3);
1208
0
                            FMTBUF_PUTBYTE_OCTAL(c);
1209
0
                            break;
1210
0
                    }
1211
33.1k
                } else {
1212
                    /*
1213
                     * Unicode, but not printable, and not ASCII;
1214
                     * put it out as \uxxxx or \Uxxxxxxxx.
1215
                     */
1216
33.1k
                    if (uc <= 0xFFFF) {
1217
30.1k
                        FMTBUF_EXPAND(6);
1218
30.1k
                        FMTBUF_PUTCHAR('\\');
1219
30.1k
                        FMTBUF_PUTCHAR('u');
1220
30.1k
                        FMTBUF_PUTCHAR(hex[(uc >> 12) & 0xF]);
1221
30.1k
                        FMTBUF_PUTCHAR(hex[(uc >> 8) & 0xF]);
1222
30.1k
                        FMTBUF_PUTCHAR(hex[(uc >> 4) & 0xF]);
1223
30.1k
                        FMTBUF_PUTCHAR(hex[(uc >> 0) & 0xF]);
1224
30.1k
                    } else {
1225
3.00k
                        FMTBUF_EXPAND(10);
1226
3.00k
                        FMTBUF_PUTCHAR('\\');
1227
3.00k
                        FMTBUF_PUTCHAR('U');
1228
3.00k
                        FMTBUF_PUTCHAR(hex[(uc >> 28) & 0xF]);
1229
3.00k
                        FMTBUF_PUTCHAR(hex[(uc >> 24) & 0xF]);
1230
3.00k
                        FMTBUF_PUTCHAR(hex[(uc >> 20) & 0xF]);
1231
3.00k
                        FMTBUF_PUTCHAR(hex[(uc >> 16) & 0xF]);
1232
3.00k
                        FMTBUF_PUTCHAR(hex[(uc >> 12) & 0xF]);
1233
3.00k
                        FMTBUF_PUTCHAR(hex[(uc >> 8) & 0xF]);
1234
3.00k
                        FMTBUF_PUTCHAR(hex[(uc >> 4) & 0xF]);
1235
3.00k
                        FMTBUF_PUTCHAR(hex[(uc >> 0) & 0xF]);
1236
3.00k
                    }
1237
33.1k
                }
1238
658k
            }
1239
1.46M
            prev = string;
1240
1.46M
        }
1241
2.64M
    }
1242
156k
    if (printable_bytes) {
1243
78.5k
        FMTBUF_PUTBYTES(prev, printable_bytes);
1244
78.5k
        printable_bytes = 0;
1245
78.5k
    }
1246
1247
156k
    FMTBUF_ENDSTR;
1248
1249
156k
    return fmtbuf;
1250
156k
}
1251
1252
/*
1253
 * Given a wmem scope, a not-necessarily-null-terminated string,
1254
 * expected to be in UTF-8 but possibly containing invalid sequences
1255
 * (as it may have come from packet data), and the length of the string,
1256
 * generate a valid UTF-8 string from it, allocated in the specified
1257
 * wmem scope, that:
1258
 *
1259
 *   shows printable Unicode characters as themselves;
1260
 *
1261
 *   shows non-printable ASCII characters as C-style escapes (octal
1262
 *   if not one of the standard ones such as LF -> '\n');
1263
 *
1264
 *   shows non-printable Unicode-but-not-ASCII characters as
1265
 *   their universal character names;
1266
 *
1267
 *   shows illegal UTF-8 sequences as a sequence of bytes represented
1268
 *   as C-style hex escapes (XXX: Does not actually do this. Some illegal
1269
 *   sequences, such as overlong encodings, the sequences reserved for
1270
 *   UTF-16 surrogate halves (paired or unpaired), and values outside
1271
 *   Unicode (i.e., the old sequences for code points above U+10FFFF)
1272
 *   will be decoded in a permissive way. Other illegal sequences,
1273
 *   such 0xFE and 0xFF and the presence of a continuation byte where
1274
 *   not expected (or vice versa its absence), are replaced with
1275
 *   REPLACEMENT CHARACTER.)
1276
 *
1277
 * and return a pointer to it.
1278
 */
1279
char *
1280
format_text(wmem_allocator_t *allocator,
1281
                        const char *string, size_t len)
1282
144k
{
1283
144k
    return format_text_internal(allocator, (const uint8_t*)string, len, false);
1284
144k
}
1285
1286
/** Given a wmem scope and a null-terminated string, expected to be in
1287
 *  UTF-8 but possibly containing invalid sequences (as it may have come
1288
 *  from packet data), and the length of the string, generate a valid
1289
 *  UTF-8 string from it, allocated in the specified wmem scope, that:
1290
 *
1291
 *   shows printable Unicode characters as themselves;
1292
 *
1293
 *   shows non-printable ASCII characters as C-style escapes (octal
1294
 *   if not one of the standard ones such as LF -> '\n');
1295
 *
1296
 *   shows non-printable Unicode-but-not-ASCII characters as
1297
 *   their universal character names;
1298
 *
1299
 *   shows illegal UTF-8 sequences as a sequence of bytes represented
1300
 *   as C-style hex escapes;
1301
 *
1302
 *  and return a pointer to it.
1303
 */
1304
char *
1305
format_text_string(wmem_allocator_t* allocator, const char *string)
1306
9.64k
{
1307
9.64k
    return format_text_internal(allocator, (const uint8_t*)string, strlen(string), false);
1308
9.64k
}
1309
1310
/*
1311
 * Given a string, generate a string from it that shows non-printable
1312
 * characters as C-style escapes except a whitespace character
1313
 * (space, tab, carriage return, new line, vertical tab, or formfeed)
1314
 * which will be replaced by a space, and return a pointer to it.
1315
 */
1316
char *
1317
format_text_wsp(wmem_allocator_t* allocator, const char *string, size_t len)
1318
1.83k
{
1319
1.83k
    return format_text_internal(allocator, (const uint8_t*)string, len, true);
1320
1.83k
}
1321
1322
/*
1323
 * Given a string, generate a string from it that shows non-printable
1324
 * characters as the chr parameter passed, except a whitespace character
1325
 * (space, tab, carriage return, new line, vertical tab, or formfeed)
1326
 * which will be replaced by a space, and return a pointer to it.
1327
 *
1328
 * This does *not* treat the input string as UTF-8.
1329
 *
1330
 * This is useful for displaying binary data that frequently but not always
1331
 * contains text; otherwise the number of C escape codes makes it unreadable.
1332
 */
1333
char *
1334
format_text_chr(wmem_allocator_t *allocator, const char *string, size_t len, char chr)
1335
0
{
1336
0
    wmem_strbuf_t *buf;
1337
1338
0
    buf = wmem_strbuf_new_sized(allocator, len + 1);
1339
0
    for (const char *p = string; p < string + len; p++) {
1340
0
        if (g_ascii_isprint(*p)) {
1341
0
            wmem_strbuf_append_c(buf, *p);
1342
0
        }
1343
0
        else if (g_ascii_isspace(*p)) {
1344
0
            wmem_strbuf_append_c(buf, ' ');
1345
0
        }
1346
0
        else {
1347
0
            wmem_strbuf_append_c(buf, chr);
1348
0
        }
1349
0
    }
1350
0
    return wmem_strbuf_finalize(buf);
1351
0
}
1352
1353
char *
1354
format_char(wmem_allocator_t *allocator, char c)
1355
50
{
1356
50
    char *buf;
1357
50
    char r;
1358
1359
50
    if (g_ascii_isprint(c)) {
1360
6
        buf = wmem_alloc_array(allocator, char, 2);
1361
6
        buf[0] = c;
1362
6
        buf[1] = '\0';
1363
6
        return buf;
1364
6
    }
1365
44
    if (escape_char(c, &r)) {
1366
28
        buf = wmem_alloc_array(allocator, char, 3);
1367
28
        buf[0] = '\\';
1368
28
        buf[1] = r;
1369
28
        buf[2] = '\0';
1370
28
        return buf;
1371
28
    }
1372
16
    buf = wmem_alloc_array(allocator, char, 5);
1373
16
    buf[0] = '\\';
1374
16
    buf[1] = 'x';
1375
16
    buf[2] = hex[((uint8_t)c >> 4) & 0xF];
1376
16
    buf[3] = hex[((uint8_t)c >> 0) & 0xF];
1377
16
    buf[4] = '\0';
1378
16
    return buf;
1379
44
}
1380
1381
char*
1382
ws_utf8_truncate(char *string, size_t len)
1383
354
{
1384
354
    char* last_char;
1385
1386
    /* Ensure that it is null terminated */
1387
354
    string[len] = '\0';
1388
354
    last_char = g_utf8_find_prev_char(string, string + len);
1389
354
    if (last_char != NULL && g_utf8_get_char_validated(last_char, -1) == (gunichar)-2) {
1390
        /* The last UTF-8 character was truncated into a partial sequence. */
1391
173
        *last_char = '\0';
1392
173
    }
1393
354
    return string;
1394
354
}
1395
1396
/* ASCII/EBCDIC conversion tables from
1397
 * https://web.archive.org/web/20060813174742/http://www.room42.com/store/computer_center/code_tables.shtml
1398
 */
1399
#if 0
1400
static const uint8_t ASCII_translate_EBCDIC [ 256 ] = {
1401
    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
1402
    0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
1403
    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
1404
    0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
1405
    0x40, 0x5A, 0x7F, 0x7B, 0x5B, 0x6C, 0x50, 0x7D, 0x4D,
1406
    0x5D, 0x5C, 0x4E, 0x6B, 0x60, 0x4B, 0x61,
1407
    0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8,
1408
    0xF9, 0x7A, 0x5E, 0x4C, 0x7E, 0x6E, 0x6F,
1409
    0x7C, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8,
1410
    0xC9, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6,
1411
    0xD7, 0xD8, 0xD9, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7,
1412
    0xE8, 0xE9, 0xAD, 0xE0, 0xBD, 0x5F, 0x6D,
1413
    0x7D, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88,
1414
    0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96,
1415
    0x97, 0x98, 0x99, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7,
1416
    0xA8, 0xA9, 0xC0, 0x6A, 0xD0, 0xA1, 0x4B,
1417
    0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B,
1418
    0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B,
1419
    0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B,
1420
    0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B,
1421
    0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B,
1422
    0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B,
1423
    0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B,
1424
    0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B,
1425
    0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B,
1426
    0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B,
1427
    0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B,
1428
    0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B,
1429
    0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B,
1430
    0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B,
1431
    0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B,
1432
    0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B
1433
};
1434
1435
void
1436
ASCII_to_EBCDIC(uint8_t *buf, unsigned bytes)
1437
{
1438
    unsigned i;
1439
    uint8_t   *bufptr;
1440
1441
    bufptr = buf;
1442
1443
    for (i = 0; i < bytes; i++, bufptr++) {
1444
        *bufptr = ASCII_translate_EBCDIC[*bufptr];
1445
    }
1446
}
1447
1448
uint8_t
1449
ASCII_to_EBCDIC1(uint8_t c)
1450
{
1451
    return ASCII_translate_EBCDIC[c];
1452
}
1453
#endif
1454
1455
static const uint8_t EBCDIC_translate_ASCII [ 256 ] = {
1456
    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
1457
    0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
1458
    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
1459
    0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
1460
    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
1461
    0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
1462
    0x2E, 0x2E, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
1463
    0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x2E, 0x3F,
1464
    0x20, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
1465
    0x2E, 0x2E, 0x2E, 0x2E, 0x3C, 0x28, 0x2B, 0x7C,
1466
    0x26, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
1467
    0x2E, 0x2E, 0x21, 0x24, 0x2A, 0x29, 0x3B, 0x5E,
1468
    0x2D, 0x2F, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
1469
    0x2E, 0x2E, 0x7C, 0x2C, 0x25, 0x5F, 0x3E, 0x3F,
1470
    0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
1471
    0x2E, 0x2E, 0x3A, 0x23, 0x40, 0x27, 0x3D, 0x22,
1472
    0x2E, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
1473
    0x68, 0x69, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
1474
    0x2E, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70,
1475
    0x71, 0x72, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
1476
    0x2E, 0x7E, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
1477
    0x79, 0x7A, 0x2E, 0x2E, 0x2E, 0x5B, 0x2E, 0x2E,
1478
    0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
1479
    0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x5D, 0x2E, 0x2E,
1480
    0x7B, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
1481
    0x48, 0x49, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
1482
    0x7D, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50,
1483
    0x51, 0x52, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
1484
    0x5C, 0x2E, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
1485
    0x59, 0x5A, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
1486
    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
1487
    0x38, 0x39, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E
1488
};
1489
1490
void
1491
EBCDIC_to_ASCII(uint8_t *buf, unsigned bytes)
1492
0
{
1493
0
    unsigned   i;
1494
0
    uint8_t *bufptr;
1495
1496
0
    bufptr = buf;
1497
1498
0
    for (i = 0; i < bytes; i++, bufptr++) {
1499
0
        *bufptr = EBCDIC_translate_ASCII[*bufptr];
1500
0
    }
1501
0
}
1502
1503
uint8_t
1504
EBCDIC_to_ASCII1(uint8_t c)
1505
0
{
1506
0
    return EBCDIC_translate_ASCII[c];
1507
0
}
1508
1509
/*
1510
 * This routine is based on a routine created by Dan Lasley
1511
 * <DLASLEY@PROMUS.com>.
1512
 *
1513
 * It was modified for Wireshark by Gilbert Ramirez and others.
1514
 */
1515
1516
#define MAX_OFFSET_LEN   8       /* max length of hex offset of bytes */
1517
0
#define BYTES_PER_LINE  16      /* max byte values printed on a line */
1518
0
#define HEX_DUMP_LEN    (BYTES_PER_LINE*3)
1519
                                /* max number of characters hex dump takes -
1520
                                   2 digits plus trailing blank */
1521
0
#define DATA_DUMP_LEN   (HEX_DUMP_LEN + 2 + 2 + BYTES_PER_LINE)
1522
                                /* number of characters those bytes take;
1523
                                   3 characters per byte of hex dump,
1524
                                   2 blanks separating hex from ASCII,
1525
                                   2 optional ASCII dump delimiters,
1526
                                   1 character per byte of ASCII dump */
1527
#define MAX_LINE_LEN    (MAX_OFFSET_LEN + 2 + DATA_DUMP_LEN)
1528
                                /* number of characters per line;
1529
                                   offset, 2 blanks separating offset
1530
                                   from data dump, data dump */
1531
1532
bool
1533
hex_dump_buffer(bool (*print_line)(void *, const char *), void *fp,
1534
                                    const unsigned char *cp, unsigned length,
1535
                                    hex_dump_enc encoding,
1536
                                    unsigned ascii_option)
1537
0
{
1538
0
    register unsigned int ad, i, j, k, l;
1539
0
    unsigned char         c;
1540
0
    char                  line[MAX_LINE_LEN + 1];
1541
0
    unsigned int          use_digits;
1542
1543
0
    static const char binhex[16] = {
1544
0
        '0', '1', '2', '3', '4', '5', '6', '7',
1545
0
        '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
1546
1547
    /*
1548
     * How many of the leading digits of the offset will we supply?
1549
     * We always supply at least 4 digits, but if the maximum offset
1550
     * won't fit in 4 digits, we use as many digits as will be needed.
1551
     */
1552
0
    if (((length - 1) & 0xF0000000) != 0)
1553
0
        use_digits = 8; /* need all 8 digits */
1554
0
    else if (((length - 1) & 0x0F000000) != 0)
1555
0
        use_digits = 7; /* need 7 digits */
1556
0
    else if (((length - 1) & 0x00F00000) != 0)
1557
0
        use_digits = 6; /* need 6 digits */
1558
0
    else if (((length - 1) & 0x000F0000) != 0)
1559
0
        use_digits = 5; /* need 5 digits */
1560
0
    else
1561
0
        use_digits = 4; /* we'll supply 4 digits */
1562
1563
0
    ad = 0;
1564
0
    i = 0;
1565
0
    j = 0;
1566
0
    k = 0;
1567
0
    while (i < length) {
1568
0
        if ((i & 15) == 0) {
1569
            /*
1570
             * Start of a new line.
1571
             */
1572
0
            j = 0;
1573
0
            l = use_digits;
1574
0
            do {
1575
0
                l--;
1576
0
                c = (ad >> (l*4)) & 0xF;
1577
0
                line[j++] = binhex[c];
1578
0
            } while (l != 0);
1579
0
            line[j++] = ' ';
1580
0
            line[j++] = ' ';
1581
0
            memset(line+j, ' ', DATA_DUMP_LEN);
1582
1583
            /*
1584
             * Offset in line of ASCII dump.
1585
             */
1586
0
            k = j + HEX_DUMP_LEN + 2;
1587
0
            if (ascii_option == HEXDUMP_ASCII_DELIMIT)
1588
0
                line[k++] = '|';
1589
0
        }
1590
0
        c = *cp++;
1591
0
        line[j++] = binhex[c>>4];
1592
0
        line[j++] = binhex[c&0xf];
1593
0
        j++;
1594
0
        if (ascii_option != HEXDUMP_ASCII_EXCLUDE ) {
1595
0
            if (encoding == HEXDUMP_ENC_EBCDIC) {
1596
0
                c = EBCDIC_to_ASCII1(c);
1597
0
            }
1598
0
            line[k++] = ((c >= ' ') && (c < 0x7f)) ? c : '.';
1599
0
        }
1600
0
        i++;
1601
0
        if (((i & 15) == 0) || (i == length)) {
1602
            /*
1603
             * We'll be starting a new line, or
1604
             * we're finished printing this buffer;
1605
             * dump out the line we've constructed,
1606
             * and advance the offset.
1607
             */
1608
0
            if (ascii_option == HEXDUMP_ASCII_DELIMIT)
1609
0
                line[k++] = '|';
1610
0
            line[k] = '\0';
1611
0
            if (!print_line(fp, line))
1612
0
                return false;
1613
0
            ad += 16;
1614
0
        }
1615
0
    }
1616
0
    return true;
1617
0
}
1618
1619
/*
1620
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
1621
 *
1622
 * Local variables:
1623
 * c-basic-offset: 4
1624
 * tab-width: 8
1625
 * indent-tabs-mode: nil
1626
 * End:
1627
 *
1628
 * vi: set shiftwidth=4 tabstop=8 expandtab:
1629
 * :indentSize=4:tabSize=8:noTabs=true:
1630
 */