### Bug Summary

File: | builds/wireshark/wireshark/wsutil/str_util.c
Warning: | line 1244, column 9 Value stored to 'printable_bytes' is never read

### Annotated Source Code

Press '?' to see keyboard shortcuts

Show analyzer invocation
clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name str_util.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -fno-delete-null-pointer-checks -mframe-pointer=all -relaxed-aliasing -fmath-errno -ffp-contract=on -fno-rounding-math -ffloat16-excess-precision=standard -fbfloat16-excess-precision=standard -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/builds/wireshark/wireshark/build -fcoverage-compilation-dir=/builds/wireshark/wireshark/build -resource-dir /usr/lib/llvm-22/lib/clang/22 -isystem /usr/include/glib-2.0 -isystem /usr/lib/x86_64-linux-gnu/glib-2.0/include -D BUILD_WSUTIL -D CARES_NO_DEPRECATED -D G_DISABLE_DEPRECATED -D G_DISABLE_SINGLE_INCLUDES -D WS_BUILD_DLL -D WS_DEBUG -D WS_DEBUG_UTF_8 -D wsutil_EXPORTS -I /builds/wireshark/wireshark/build -I /builds/wireshark/wireshark -I /builds/wireshark/wireshark/include -I /builds/wireshark/wireshark/build/wsutil -D _GLIBCXX_ASSERTIONS -internal-isystem /usr/lib/llvm-22/lib/clang/22/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -fmacro-prefix-map=/builds/wireshark/wireshark/= -fmacro-prefix-map=/builds/wireshark/wireshark/build/= -fmacro-prefix-map=../= -Wno-format-nonliteral -std=gnu17 -ferror-limit 19 -fvisibility=hidden -fwrapv -fwrapv-pointer -fstrict-flex-arrays=3 -stack-protector 2 -fstack-clash-protection -fcf-protection=full -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fexceptions -fcolor-diagnostics -analyzer-output=html -faddrsig -fdwarf2-cfi-asm -o /builds/wireshark/wireshark/sbout/2026-07-21-100400-3661-1 -x c /builds/wireshark/wireshark/wsutil/str_util.c

Keyboard shortcuts:

- Use 'j/k' keys for keyboard navigation
- Use 'Shift+S' to show/hide relevant lines
- Use '?' to toggle this window

Close
Show only relevant lines Show control flow arrows

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 | prefix_parameters_for_flags(uint16_t flags) {
39 | static const char * const si_prefixes[] = {" a", " f", " p", " n", " μ", " m", " ", " k", " M", " G", " T", " P", " E"};
40 | static const struct prefix_parameters si_parameters = {si_prefixes, G_N_ELEMENTS(si_prefixes)(sizeof (si_prefixes) / sizeof ((si_prefixes)[0])), 1000, 6};
41 | static const char * const iec_prefixes[] = {" ", " Ki", " Mi", " Gi", " Ti", " Pi", " Ei"};
42 | static const struct prefix_parameters iec_parameters = {iec_prefixes, G_N_ELEMENTS(iec_prefixes)(sizeof (iec_prefixes) / sizeof ((iec_prefixes)[0])), 1024, 0};
43 |
44 | return (flags & FORMAT_SIZE_PREFIX_IEC(1 << 1)) != 0 ? &iec_parameters : &si_parameters;
45 | }
46 |
47 | char *
48 | wmem_strconcat(wmem_allocator_t *allocator, const char *first, ...)
49 | {
50 | size_t len;
51 | va_list args;
52 | char *s;
53 | char *concat;
54 | char *ptr;
55 |
56 | if (!first)
57 | return NULL((void*)0);
58 |
59 | len = 1 + strlen(first);
60 | va_start(args, first)__builtin_va_start(args, first);
61 | while ((s = va_arg(args, char*)__builtin_va_arg(args, char*))) {
62 | len += strlen(s);
63 | }
64 | va_end(args)__builtin_va_end(args);
65 |
66 | ptr = concat = (char *)wmem_alloc(allocator, len);
67 |
68 | ptr = g_stpcpy(ptr, first);
69 | va_start(args, first)__builtin_va_start(args, first);
70 | while ((s = va_arg(args, char*)__builtin_va_arg(args, char*))) {
71 | ptr = g_stpcpy(ptr, s);
72 | }
73 | va_end(args)__builtin_va_end(args);
74 |
75 | return concat;
76 | }
77 |
78 | char *
79 | wmem_strjoin(wmem_allocator_t *allocator,
80 | const char *separator, const char *first, ...)
81 | {
82 | size_t len;
83 | va_list args;
84 | size_t separator_len;
85 | char *s;
86 | char *concat;
87 | char *ptr;
88 |
89 | if (!first)
90 | return NULL((void*)0);
91 |
92 | if (separator == NULL((void*)0)) {
93 | separator = "";
94 | }
95 |
96 | separator_len = strlen (separator);
97 |
98 | len = 1 + strlen(first); /* + 1 for null byte */
99 | va_start(args, first)__builtin_va_start(args, first);
100 | while ((s = va_arg(args, char*)__builtin_va_arg(args, char*))) {
101 | len += (separator_len + strlen(s));
102 | }
103 | va_end(args)__builtin_va_end(args);
104 |
105 | ptr = concat = (char *)wmem_alloc(allocator, len);
106 | ptr = g_stpcpy(ptr, first);
107 | va_start(args, first)__builtin_va_start(args, first);
108 | while ((s = va_arg(args, char*)__builtin_va_arg(args, char*))) {
109 | ptr = g_stpcpy(ptr, separator);
110 | ptr = g_stpcpy(ptr, s);
111 | }
112 | va_end(args)__builtin_va_end(args);
113 |
114 | return concat;
115 |
116 | }
117 |
118 | char *
119 | wmem_strjoinv(wmem_allocator_t *allocator,
120 | const char *separator, char **str_array)
121 | {
122 | char *string = NULL((void*)0);
123 |
124 | ws_return_val_if(!str_array, NULL)do { if (1 && (!str_array)) { ws_log_full("InvalidArg" , LOG_LEVEL_WARNING, "wsutil/str_util.c", 124, __func__, "invalid argument: %s" , "!str_array"); return (((void*)0)); } } while (0);
125 |
126 | if (separator == NULL((void*)0)) {
127 | separator = "";
128 | }
129 |
130 | if (str_array[0]) {
131 | int i;
132 | char *ptr;
133 | size_t len, separator_len;
134 |
135 | separator_len = strlen(separator);
136 |
137 | /* Get first part of length. Plus one for null byte. */
138 | len = 1 + strlen(str_array[0]);
139 | /* Get the full length, including the separators. */
140 | for (i = 1; str_array[i] != NULL((void*)0); i++) {
141 | len += separator_len;
142 | len += strlen(str_array[i]);
143 | }
144 |
145 | /* Allocate and build the string. */
146 | string = (char *)wmem_alloc(allocator, len);
147 | ptr = g_stpcpy(string, str_array[0]);
148 | for (i = 1; str_array[i] != NULL((void*)0); i++) {
149 | ptr = g_stpcpy(ptr, separator);
150 | ptr = g_stpcpy(ptr, str_array[i]);
151 | }
152 | } else {
153 | string = wmem_strdup(allocator, "");
154 | }
155 |
156 | return string;
157 |
158 | }
159 |
160 | char **
161 | wmem_strsplit(wmem_allocator_t *allocator, const char *src,
162 | const char *delimiter, int max_tokens)
163 | {
164 | char *splitted;
165 | char *s;
166 | unsigned tokens;
167 | unsigned sep_len;
168 | unsigned i;
169 | char **vec;
170 |
171 | if (!src || !delimiter || !delimiter[0])
172 | return NULL((void*)0);
173 |
174 | /* An empty string results in an empty vector. */
175 | if (!src[0]) {
176 | vec = wmem_new0(allocator, char *)((char **)wmem_alloc0((allocator), sizeof(char *)));
177 | return vec;
178 | }
179 |
180 | splitted = wmem_strdup(allocator, src);
181 | sep_len = (unsigned)strlen(delimiter);
182 |
183 | if (max_tokens < 1)
184 | max_tokens = INT_MAX2147483647;
185 |
186 | /* Calculate the number of fields. */
187 | s = splitted;
188 | tokens = 1;
189 | while (tokens < (unsigned)max_tokens && (s = strstr(s, delimiter)_Generic (0 ? (s) : (void *) 1, const void *: (const char *) ( strstr (s, delimiter)), default: strstr (s, delimiter)))) {
190 | s += sep_len;
191 | tokens++;
192 | }
193 |
194 | vec = wmem_alloc_array(allocator, char *, tokens + 1)((char **)wmem_alloc((allocator), (((((tokens + 1)) <= 0) || ((size_t)sizeof(char *) > (9223372036854775807L / (size_t )((tokens + 1))))) ? 0 : (sizeof(char *) * ((tokens + 1))))));
195 |
196 | /* Populate the array of string tokens. */
197 | s = splitted;
198 | vec[0] = s;
199 | tokens = 1;
200 | while (tokens < (unsigned)max_tokens && (s = strstr(s, delimiter)_Generic (0 ? (s) : (void *) 1, const void *: (const char *) ( strstr (s, delimiter)), default: strstr (s, delimiter)))) {
201 | for (i = 0; i < sep_len; i++)
202 | s[i] = '\0';
203 | s += sep_len;
204 | vec[tokens] = s;
205 | tokens++;
206 |
207 | }
208 |
209 | vec[tokens] = NULL((void*)0);
210 |
211 | return vec;
212 | }
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 | {
221 | char *result, *s;
222 | size_t abs_len;
223 |
224 | g_return_val_if_fail (str != NULL, NULL)do { if ((str != ((void*)0))) { } else { g_return_if_fail_warning (((gchar*) 0), ((const char*) (__func__)), "str != NULL"); return (((void*)0)); } } while (0);
225 |
226 | abs_len = (len < 0) ? strlen(str) : (size_t)len;
227 |
228 | result = wmem_strndup(allocator, str, abs_len);
229 | for (s = result; *s; s++)
230 | *s = g_ascii_tolower (*s);
231 |
232 | return result;
233 | }
234 |
235 | int
236 | ws_xton(char ch)
237 | {
238 | switch (ch) {
239 | case '0': return 0;
240 | case '1': return 1;
241 | case '2': return 2;
242 | case '3': return 3;
243 | case '4': return 4;
244 | case '5': return 5;
245 | case '6': return 6;
246 | case '7': return 7;
247 | case '8': return 8;
248 | case '9': return 9;
249 | case 'a': case 'A': return 10;
250 | case 'b': case 'B': return 11;
251 | case 'c': case 'C': return 12;
252 | case 'd': case 'D': return 13;
253 | case 'e': case 'E': return 14;
254 | case 'f': case 'F': return 15;
255 | default: return -1;
256 | }
257 | }
258 |
259 | /* Convert all ASCII letters to lower case, in place. */
260 | char *
261 | ascii_strdown_inplace(char *str)
262 | {
263 | char *s;
264 |
265 | for (s = str; *s; s++)
266 | /* What 'g_ascii_tolower (char c)' does, this should be slightly more efficient */
267 | *s = g_ascii_isupper (*s)((g_ascii_table[(guchar) (*s)] & G_ASCII_UPPER) != 0) ? *s - 'A' + 'a' : *s;
268 |
269 | return (str);
270 | }
271 |
272 | /* Convert all ASCII letters to upper case, in place. */
273 | char *
274 | ascii_strup_inplace(char *str)
275 | {
276 | char *s;
277 |
278 | for (s = str; *s; s++)
279 | /* What 'g_ascii_toupper (char c)' does, this should be slightly more efficient */
280 | *s = g_ascii_islower (*s)((g_ascii_table[(guchar) (*s)] & G_ASCII_LOWER) != 0) ? *s - 'a' + 'A' : *s;
281 |
282 | return (str);
283 | }
284 |
285 | /* Check if an entire string is printable. */
286 | bool_Bool
287 | isprint_string(const char *str)
288 | {
289 | unsigned pos;
290 |
291 | /* Loop until we reach the end of the string (a null) */
292 | for(pos = 0; str[pos] != '\0'; pos++){
293 | if(!g_ascii_isprint(str[pos])((g_ascii_table[(guchar) (str[pos])] & G_ASCII_PRINT) != 0 )){
294 | /* The string contains a non-printable character */
295 | return false0;
296 | }
297 | }
298 |
299 | /* The string contains only printable characters */
300 | return true1;
301 | }
302 |
303 | /* Check if an entire UTF-8 string is printable. */
304 | bool_Bool
305 | isprint_utf8_string(const char *str, const unsigned length)
306 | {
307 | const char *strend = str + length;
308 |
309 | if (!g_utf8_validate(str, length, NULL((void*)0))) {
310 | return false0;
311 | }
312 |
313 | 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 | if (!g_unichar_isprint(g_utf8_get_char(str))) {
320 | return false0;
321 | }
322 | str = g_utf8_next_char(str)((str) + g_utf8_skip[*(const guchar *)(str)]);
323 | }
324 |
325 | return true1;
326 | }
327 |
328 | /* Check if an entire string is digits. */
329 | bool_Bool
330 | isdigit_string(const char *str)
331 | {
332 | unsigned pos;
333 |
334 | /* Loop until we reach the end of the string (a null) */
335 | for(pos = 0; str[pos] != '\0'; pos++){
336 | if(!g_ascii_isdigit(str[pos])((g_ascii_table[(guchar) (str[pos])] & G_ASCII_DIGIT) != 0 )){
337 | /* The string contains a non-digit character */
338 | return false0;
339 | }
340 | }
341 |
342 | /* The string contains only digits */
343 | return true1;
344 | }
345 |
346 | const char *
347 | ws_ascii_strcasestr(const char *haystack, const char *needle)
348 | {
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 | size_t hlen = strlen(haystack);
354 | size_t nlen = strlen(needle);
355 |
356 | while (hlen-- >= nlen) {
357 | if (!g_ascii_strncasecmp(haystack, needle, nlen))
358 | return haystack;
359 | haystack++;
360 | }
361 | return NULL((void*)0);
362 | }
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 | {
369 | #ifdef HAVE_MEMRCHR1
370 | 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((void*)0);
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((void*)0);
391 | #endif /* HAVE_MEMRCHR */
392 | }
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 | {
399 | #ifdef HAVE_STRCHRNUL1
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 | return (char*)strchrnul(str, ch);
416 | #endif
417 | #else
418 | /* Minimal generic implementation. */
419 | while (*str != '\0' && *str != (char)ch) {
420 | str++;
421 | }
422 | return (char *)str;
423 | #endif
424 | }
425 |
426 | static const char *thousands_grouping_fmt;
427 | static const char *thousands_grouping_fmt_flt;
428 |
429 | DIAG_OFF(format)clang diagnostic push clang diagnostic ignored "-Wformat"
430 | static void test_printf_thousands_grouping(void) {
431 | /* test whether wmem_strbuf works with "'" flag character */
432 | wmem_strbuf_t *buf = wmem_strbuf_new(NULL((void*)0), NULL((void*)0));
433 | wmem_strbuf_append_printf(buf, "%'d", 22);
434 | if (g_strcmp0(wmem_strbuf_get_str(buf), "22") == 0) {
435 | thousands_grouping_fmt = "%'"PRId64"l" "d";
436 | thousands_grouping_fmt_flt = "%'.*f";
437 | } else {
438 | /* Don't use */
439 | thousands_grouping_fmt = "%"PRId64"l" "d";
440 | thousands_grouping_fmt_flt = "%.*f";
441 | }
442 | wmem_strbuf_destroy(buf);
443 | }
444 | DIAG_ON(format)clang diagnostic pop
445 |
446 | static const char* decimal_point = NULL((void*)0);
447 |
448 | static void truncate_numeric_strbuf(wmem_strbuf_t *strbuf, int n) {
449 |
450 | const char *s = wmem_strbuf_get_str(strbuf);
451 | const char *p;
452 | int count;
453 |
454 | if (decimal_point == NULL((void*)0)) {
455 | decimal_point = localeconv()->decimal_point;
456 | }
457 |
458 | p = strchr(s, decimal_point[0])_Generic (0 ? (s) : (void *) 1, const void *: (const char *) ( strchr (s, decimal_point[0])), default: strchr (s, decimal_point [0]));
459 | if (p != NULL((void*)0)) {
460 | count = n;
461 | while (count >= 0) {
462 | count--;
463 | if (*p == '\0')
464 | break;
465 | p++;
466 | }
467 |
468 | p--;
469 | while (*p == '0') {
470 | p--;
471 | }
472 |
473 | if (*p != decimal_point[0]) {
474 | p++;
475 | }
476 | wmem_strbuf_truncate(strbuf, (size_t)(p - s));
477 | }
478 | }
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 | {
489 | wmem_strbuf_t *human_str = wmem_strbuf_new(allocator, NULL((void*)0));
490 | bool_Bool is_small = false0;
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 | bool_Bool scientific = false0;
497 | double abs_size = fabs(size);
498 | const struct prefix_parameters * const pp = prefix_parameters_for_flags(flags);
499 | int prefix_index = pp->prefix_offset;
500 | char *ret_val;
501 |
502 | if (thousands_grouping_fmt == NULL((void*)0))
503 | test_printf_thousands_grouping();
504 |
505 | if (isfinite(size)__builtin_isfinite (size) && size != 0.0) {
506 |
507 | 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 | if (abs_size < 1.0) {
519 | while (abs_size < comp) {
520 | abs_size *= pp->power;
521 | if (prefix_index == 0) {
522 | scientific = true1;
523 | break;
524 | }
525 | prefix_index--;
526 | }
527 | } else {
528 | while (abs_size >= comp * pp->power) {
529 | abs_size /= pp->power;
530 | if (prefix_index == pp->prefix_count - 1) {
531 | scientific = true1;
532 | break;
533 | }
534 | prefix_index++;
535 | }
536 | }
537 | }
538 |
539 | if (scientific) {
540 | wmem_strbuf_append_printf(human_str, "%.*g", precision + 1, size);
541 | prefix_index = pp->prefix_offset;
542 | } else {
543 | if (prefix_index == pp->prefix_offset) {
544 | is_small = true1;
545 | }
546 | 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 | wmem_strbuf_append_printf(human_str, thousands_grouping_fmt_flt, precision, size);
552 | 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 | }
564 |
565 | wmem_strbuf_append(human_str, pp->prefix[prefix_index]);
566 |
567 | switch (unit) {
568 | case FORMAT_SIZE_UNIT_NONE:
569 | break;
570 | case FORMAT_SIZE_UNIT_BYTES:
571 | wmem_strbuf_append(human_str, is_small ? "bytes" : "B");
572 | break;
573 | case FORMAT_SIZE_UNIT_BITS:
574 | wmem_strbuf_append(human_str, is_small ? "bits" : "b");
575 | break;
576 | case FORMAT_SIZE_UNIT_BITS_S:
577 | wmem_strbuf_append(human_str, is_small ? "bits/s" : "bps");
578 | break;
579 | case FORMAT_SIZE_UNIT_BYTES_S:
580 | wmem_strbuf_append(human_str, is_small ? "bytes/s" : "Bps");
581 | break;
582 | case FORMAT_SIZE_UNIT_PACKETS:
583 | wmem_strbuf_append(human_str, is_small ? "packets" : "pkts");
584 | break;
585 | case FORMAT_SIZE_UNIT_PACKETS_S:
586 | wmem_strbuf_append(human_str, is_small ? "packets/s" : "pkts/s");
587 | break;
588 | case FORMAT_SIZE_UNIT_EVENTS:
589 | wmem_strbuf_append(human_str, is_small ? "events" : "evts");
590 | break;
591 | case FORMAT_SIZE_UNIT_EVENTS_S:
592 | wmem_strbuf_append(human_str, is_small ? "events/s" : "evts/s");
593 | break;
594 | case FORMAT_SIZE_UNIT_FIELDS:
595 | wmem_strbuf_append(human_str, is_small ? "fields" : "flds");
596 | break;
597 | case FORMAT_SIZE_UNIT_SECONDS:
598 | wmem_strbuf_append(human_str, is_small ? "seconds" : "s");
599 | break;
600 | case FORMAT_SIZE_UNIT_ERLANGS:
601 | wmem_strbuf_append(human_str, is_small ? "erlangs" : "E");
602 | break;
603 | default:
604 | ws_assert_not_reached()ws_log_fatal_full("", LOG_LEVEL_ERROR, "wsutil/str_util.c", 604 , __func__, "assertion \"not reached\" failed");
605 | }
606 |
607 | 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 | return g_strchomp(ret_val);
622 | }
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 | {
633 | wmem_strbuf_t *human_str = wmem_strbuf_new(allocator, NULL((void*)0));
634 | bool_Bool is_small = false0;
635 | const struct prefix_parameters * const pp = prefix_parameters_for_flags(flags);
636 | char *ret_val;
637 |
638 | if (thousands_grouping_fmt == NULL((void*)0))
639 | test_printf_thousands_grouping();
640 |
641 | int prefix_index = pp->prefix_offset;
642 | int64_t scale = 1;
643 | while (prefix_index + 1 < pp->prefix_count && scale < INT64_MAX(9223372036854775807L) / (10 * pp->power) && size >= scale * pp->power * 10) {
644 | prefix_index++;
645 | scale *= pp->power;
646 | }
647 |
648 | wmem_strbuf_append_printf(human_str, thousands_grouping_fmt, size / scale);
649 | wmem_strbuf_append(human_str, pp->prefix[prefix_index]);
650 | is_small = prefix_index == pp->prefix_offset;
651 |
652 | switch (unit) {
653 | case FORMAT_SIZE_UNIT_NONE:
654 | break;
655 | case FORMAT_SIZE_UNIT_BYTES:
656 | wmem_strbuf_append(human_str, is_small ? "bytes" : "B");
657 | break;
658 | case FORMAT_SIZE_UNIT_BITS:
659 | wmem_strbuf_append(human_str, is_small ? "bits" : "b");
660 | break;
661 | case FORMAT_SIZE_UNIT_BITS_S:
662 | wmem_strbuf_append(human_str, is_small ? "bits/s" : "bps");
663 | break;
664 | case FORMAT_SIZE_UNIT_BYTES_S:
665 | wmem_strbuf_append(human_str, is_small ? "bytes/s" : "Bps");
666 | break;
667 | case FORMAT_SIZE_UNIT_PACKETS:
668 | wmem_strbuf_append(human_str, is_small ? "packets" : "pkts");
669 | break;
670 | case FORMAT_SIZE_UNIT_PACKETS_S:
671 | wmem_strbuf_append(human_str, is_small ? "packets/s" : "pkts/s");
672 | break;
673 | case FORMAT_SIZE_UNIT_EVENTS:
674 | wmem_strbuf_append(human_str, is_small ? "events" : "evts");
675 | break;
676 | case FORMAT_SIZE_UNIT_EVENTS_S:
677 | wmem_strbuf_append(human_str, is_small ? "events/s" : "evts/s");
678 | break;
679 | case FORMAT_SIZE_UNIT_FIELDS:
680 | wmem_strbuf_append(human_str, is_small ? "fields" : "flds");
681 | break;
682 | case FORMAT_SIZE_UNIT_SECONDS:
683 | wmem_strbuf_append(human_str, is_small ? "seconds" : "s");
684 | break;
685 | case FORMAT_SIZE_UNIT_ERLANGS:
686 | wmem_strbuf_append(human_str, is_small ? "erlangs" : "E");
687 | break;
688 | default:
689 | ws_assert_not_reached()ws_log_fatal_full("", LOG_LEVEL_ERROR, "wsutil/str_util.c", 689 , __func__, "assertion \"not reached\" failed");
690 | }
691 |
692 | ret_val = wmem_strbuf_finalize(human_str);
693 | return g_strchomp(ret_val);
694 | }
695 |
696 | char
697 | printable_char_or_period(char c)
698 | {
699 | return g_ascii_isprint(c)((g_ascii_table[(guchar) (c)] & G_ASCII_PRINT) != 0) ? c : '.';
700 | }
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_Bool
707 | escape_char(char c, char *p)
708 | {
709 | int r = -1;
710 | ws_assert(p)do { if ((1) && !(p)) ws_log_fatal_full("", LOG_LEVEL_ERROR , "wsutil/str_util.c", 710, __func__, "assertion failed: %s", "p"); } while (0);
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 | switch (c) {
718 | case '\a': r = 'a'; break;
719 | case '\b': r = 'b'; break;
720 | case '\f': r = 'f'; break;
721 | case '\n': r = 'n'; break;
722 | case '\r': r = 'r'; break;
723 | case '\t': r = 't'; break;
724 | case '\v': r = 'v'; break;
725 | case '\\': r = '\\'; break;
726 | case '\0': r = '0'; break;
727 | }
728 |
729 | if (r != -1) {
730 | *p = r;
731 | return true1;
732 | }
733 | return false0;
734 | }
735 |
736 | static inline bool_Bool
737 | escape_null(char c, char *p)
738 | {
739 | ws_assert(p)do { if ((1) && !(p)) ws_log_fatal_full("", LOG_LEVEL_ERROR , "wsutil/str_util.c", 739, __func__, "assertion failed: %s", "p"); } while (0);
740 | if (c == '\0') {
741 | *p = '0';
742 | return true1;
743 | }
744 | return false0;
745 | }
746 |
747 | static char *
748 | escape_string_len(wmem_allocator_t *alloc, const char *string, ssize_t len,
749 | bool_Bool (*escape_func)(char c, char *p), bool_Bool add_quotes,
750 | char quote_char, bool_Bool double_quote)
751 | {
752 | char c, r;
753 | wmem_strbuf_t *buf;
754 | size_t abs_len, alloc_size, i;
755 |
756 | abs_len = (len < 0) ? strlen(string) : (size_t)len;
757 |
758 | alloc_size = abs_len;
759 | if (add_quotes)
760 | alloc_size += 2;
761 |
762 | buf = wmem_strbuf_new_sized(alloc, alloc_size);
763 |
764 | if (add_quotes && quote_char != '\0')
765 | wmem_strbuf_append_c(buf, quote_char);
766 |
767 | for (i = 0; i < abs_len; i++) {
768 | c = string[i];
769 | if ((escape_func(c, &r))) {
770 | wmem_strbuf_append_c(buf, '\\');
771 | wmem_strbuf_append_c(buf, r);
772 | }
773 | else if (c == quote_char && quote_char != '\0') {
774 | /* If quoting, we must escape the quote_char somehow. */
775 | if (double_quote) {
776 | wmem_strbuf_append_c(buf, c);
777 | wmem_strbuf_append_c(buf, c);
778 | } else {
779 | wmem_strbuf_append_c(buf, '\\');
780 | wmem_strbuf_append_c(buf, c);
781 | }
782 | }
783 | 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 | wmem_strbuf_append_c(buf, '\\');
787 | wmem_strbuf_append_c(buf, '\\');
788 | }
789 | else {
790 | /* Other UTF-8 bytes are passed through. */
791 | wmem_strbuf_append_c(buf, c);
792 | }
793 | }
794 |
795 | if (add_quotes && quote_char != '\0')
796 | wmem_strbuf_append_c(buf, quote_char);
797 |
798 | return wmem_strbuf_finalize(buf);
799 | }
800 |
801 | char *
802 | ws_escape_string_len(wmem_allocator_t *alloc, const char *string, ssize_t len, bool_Bool add_quotes)
803 | {
804 | return escape_string_len(alloc, string, len, escape_char, add_quotes, '"', false0);
805 | }
806 |
807 | char *
808 | ws_escape_string(wmem_allocator_t *alloc, const char *string, bool_Bool add_quotes)
809 | {
810 | return escape_string_len(alloc, string, -1, escape_char, add_quotes, '"', false0);
811 | }
812 |
813 | char *ws_escape_null(wmem_allocator_t *alloc, const char *string, size_t len, bool_Bool add_quotes)
814 | {
815 | /* XXX: The existing behavior (maintained) here is not to escape
816 | * backslashes even though NUL is escaped.
817 | */
818 | return escape_string_len(alloc, string, len, escape_null, add_quotes, add_quotes ? '"' : '\0', false0);
819 | }
820 |
821 | char *ws_escape_csv(wmem_allocator_t *alloc, const char *string, bool_Bool add_quotes, char quote_char, bool_Bool double_quote, bool_Bool escape_whitespace)
822 | {
823 | if (escape_whitespace)
824 | return escape_string_len(alloc, string, -1, escape_char, add_quotes, quote_char, double_quote);
825 | else
826 | return escape_string_len(alloc, string, -1, escape_null, add_quotes, quote_char, double_quote);
827 | }
828 |
829 | const char *
830 | ws_strerrorname_r(int errnum, char *buf, size_t buf_size)
831 | {
832 | #ifdef HAVE_STRERRORNAME_NP1
833 | const char *errstr = strerrorname_np(errnum);
834 | if (errstr != NULL((void*)0)) {
835 | (void)g_strlcpy(buf, errstr, buf_size);
836 | return buf;
837 | }
838 | #endif
839 | snprintf(buf, buf_size, "Errno(%d)", errnum);
840 | return buf;
841 | }
842 |
843 | char *
844 | ws_strdup_underline(wmem_allocator_t *allocator, long offset, size_t len)
845 | {
846 | if (offset < 0)
847 | return NULL((void*)0);
848 |
849 | wmem_strbuf_t *buf = wmem_strbuf_new_sized(allocator, offset + len);
850 |
851 | for (int i = 0; i < offset; i++) {
852 | wmem_strbuf_append_c(buf, ' ');
853 | }
854 | wmem_strbuf_append_c(buf, '^');
855 |
856 | for (size_t l = len; l > 1; l--) {
857 | wmem_strbuf_append_c(buf, '~');
858 | }
859 |
860 | return wmem_strbuf_finalize(buf);
861 | }
862 |
863 | #define INITIAL_FMTBUF_SIZE128 128
864 |
865 | /*
866 | * Declare, and initialize, the variables used for an output buffer.
867 | */
868 | #define FMTBUF_VARSchar *fmtbuf = (char*)wmem_alloc(allocator, 128); unsigned fmtbuf_len = 128; unsigned column = 0 \
869 | char *fmtbuf = (char*)wmem_alloc(allocator, INITIAL_FMTBUF_SIZE128); \
870 | unsigned fmtbuf_len = INITIAL_FMTBUF_SIZE128; \
871 | 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)if (column+(nbytes+1) >= fmtbuf_len) { if (__builtin_mul_overflow ((fmtbuf_len), (2), (&fmtbuf_len))) { do { if (1) { ws_log_full ("", LOG_LEVEL_DEBUG, "wsutil/str_util.c", 877, __func__, "overflow!" ); } } while (0); fmtbuf[column] = '\0'; return fmtbuf; } if ( column+(nbytes+1) >= fmtbuf_len) { if (__builtin_add_overflow ((fmtbuf_len), ((column + nbytes + 2) - fmtbuf_len), (&fmtbuf_len ))) { do { if (1) { ws_log_full("", LOG_LEVEL_DEBUG, "wsutil/str_util.c" , 877, __func__, "overflow!"); } } while (0); fmtbuf[column] = '\0'; return fmtbuf; } } fmtbuf = (char *)wmem_realloc(allocator , fmtbuf, fmtbuf_len); } \
878 | /* \
879 | * Is there enough room for those bytes and also enough room for \
880 | * a terminating '\0'? \
881 | */ \
882 | if (column+(nbytes+1) >= fmtbuf_len) { \
883 | /* \
884 | * Double the buffer's size if it's not big enough. \
885 | * The size of the buffer starts at 128, so doubling its size \
886 | * adds at least another 128 bytes, which is more than enough \
887 | * for one more character plus a terminating '\0'. \
888 | */ \
889 | if (ckd_mul(&fmtbuf_len, fmtbuf_len, 2)__builtin_mul_overflow((fmtbuf_len), (2), (&fmtbuf_len))) { \
890 | ws_debug("overflow!")do { if (1) { ws_log_full("", LOG_LEVEL_DEBUG, "wsutil/str_util.c" , 890, __func__, "overflow!"); } } while (0); \
891 | FMTBUF_ENDSTRfmtbuf[column] = '\0'; \
892 | return fmtbuf; \
893 | } \
894 | if (column+(nbytes+1) >= fmtbuf_len) { \
895 | if (ckd_add(&fmtbuf_len, fmtbuf_len, (column + nbytes + 2) - fmtbuf_len)__builtin_add_overflow((fmtbuf_len), ((column + nbytes + 2) - fmtbuf_len), (&fmtbuf_len))) { \
896 | ws_debug("overflow!")do { if (1) { ws_log_full("", LOG_LEVEL_DEBUG, "wsutil/str_util.c" , 896, __func__, "overflow!"); } } while (0); \
897 | FMTBUF_ENDSTRfmtbuf[column] = '\0'; \
898 | return fmtbuf; \
899 | } \
900 | } \
901 | fmtbuf = (char *)wmem_realloc(allocator, fmtbuf, fmtbuf_len); \
902 | }
903 |
904 | /*
905 | * Put a byte into the buffer; space must have been ensured for it.
906 | */
907 | #define FMTBUF_PUTCHAR(b)fmtbuf[column] = (b); column++ \
908 | fmtbuf[column] = (b); \
909 | 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)fmtbuf[column] = ((((b)>>6)&03) + '0'); column++; fmtbuf [column] = ((((b)>>3)&07) + '0'); column++; fmtbuf[ column] = ((((b)>>0)&07) + '0'); column++ \
916 | FMTBUF_PUTCHAR((((b)>>6)&03) + '0')fmtbuf[column] = ((((b)>>6)&03) + '0'); column++; \
917 | FMTBUF_PUTCHAR((((b)>>3)&07) + '0')fmtbuf[column] = ((((b)>>3)&07) + '0'); column++; \
918 | FMTBUF_PUTCHAR((((b)>>0)&07) + '0')fmtbuf[column] = ((((b)>>0)&07) + '0'); column++
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)fmtbuf[column] = ('\\'); column++; fmtbuf[column] = ('x'); column ++; fmtbuf[column] = (hex[((b) >> 4) & 0xF]); column ++; fmtbuf[column] = (hex[((b) >> 0) & 0xF]); column ++ \
925 | FMTBUF_PUTCHAR('\\')fmtbuf[column] = ('\\'); column++; \
926 | FMTBUF_PUTCHAR('x')fmtbuf[column] = ('x'); column++; \
927 | FMTBUF_PUTCHAR(hex[((b) >> 4) & 0xF])fmtbuf[column] = (hex[((b) >> 4) & 0xF]); column++; \
928 | FMTBUF_PUTCHAR(hex[((b) >> 0) & 0xF])fmtbuf[column] = (hex[((b) >> 0) & 0xF]); column++
929 |
930 | #define FMTBUF_PUTBYTES(bytes, len)if (column+(len+1) >= fmtbuf_len) { if (__builtin_mul_overflow ((fmtbuf_len), (2), (&fmtbuf_len))) { do { if (1) { ws_log_full ("", LOG_LEVEL_DEBUG, "wsutil/str_util.c", 930, __func__, "overflow!" ); } } while (0); fmtbuf[column] = '\0'; return fmtbuf; } if ( column+(len+1) >= fmtbuf_len) { if (__builtin_add_overflow ((fmtbuf_len), ((column + len + 2) - fmtbuf_len), (&fmtbuf_len ))) { do { if (1) { ws_log_full("", LOG_LEVEL_DEBUG, "wsutil/str_util.c" , 930, __func__, "overflow!"); } } while (0); fmtbuf[column] = '\0'; return fmtbuf; } } fmtbuf = (char *)wmem_realloc(allocator , fmtbuf, fmtbuf_len); } memcpy(&fmtbuf[column], bytes, len ); column += (unsigned)len; \
931 | FMTBUF_EXPAND(len)if (column+(len+1) >= fmtbuf_len) { if (__builtin_mul_overflow ((fmtbuf_len), (2), (&fmtbuf_len))) { do { if (1) { ws_log_full ("", LOG_LEVEL_DEBUG, "wsutil/str_util.c", 931, __func__, "overflow!" ); } } while (0); fmtbuf[column] = '\0'; return fmtbuf; } if ( column+(len+1) >= fmtbuf_len) { if (__builtin_add_overflow ((fmtbuf_len), ((column + len + 2) - fmtbuf_len), (&fmtbuf_len ))) { do { if (1) { ws_log_full("", LOG_LEVEL_DEBUG, "wsutil/str_util.c" , 931, __func__, "overflow!"); } } while (0); fmtbuf[column] = '\0'; return fmtbuf; } } fmtbuf = (char *)wmem_realloc(allocator , fmtbuf, fmtbuf_len); } \
932 | memcpy(&fmtbuf[column], bytes, len); \
933 | 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_ENDSTRfmtbuf[column] = '\0' \
939 | 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_Bool replace_space)
945 | {
946 | FMTBUF_VARSchar *fmtbuf = (char*)wmem_alloc(allocator, 128); unsigned fmtbuf_len = 128; unsigned column = 0;
947 | const unsigned char *prev = string;
948 | const unsigned char *stringend = string + len;
949 | unsigned char c;
950 | size_t printable_bytes = 0;
951 |
952 | while (string < stringend) {
953 | /*
954 | * Get the first byte of this character.
955 | */
956 | c = *string++;
957 | 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 | printable_bytes++;
964 | } else {
965 | if (printable_bytes) {
966 | FMTBUF_PUTBYTES(prev, printable_bytes)if (column+(printable_bytes+1) >= fmtbuf_len) { if (__builtin_mul_overflow ((fmtbuf_len), (2), (&fmtbuf_len))) { do { if (1) { ws_log_full ("", LOG_LEVEL_DEBUG, "wsutil/str_util.c", 966, __func__, "overflow!" ); } } while (0); fmtbuf[column] = '\0'; return fmtbuf; } if ( column+(printable_bytes+1) >= fmtbuf_len) { if (__builtin_add_overflow ((fmtbuf_len), ((column + printable_bytes + 2) - fmtbuf_len), (&fmtbuf_len))) { do { if (1) { ws_log_full("", LOG_LEVEL_DEBUG , "wsutil/str_util.c", 966, __func__, "overflow!"); } } while (0); fmtbuf[column] = '\0'; return fmtbuf; } } fmtbuf = (char *)wmem_realloc(allocator, fmtbuf, fmtbuf_len); } memcpy(& fmtbuf[column], prev, printable_bytes); column += (unsigned)printable_bytes ;;
967 | printable_bytes = 0;
968 | }
969 | if (replace_space && g_ascii_isspace(c)((g_ascii_table[(guchar) (c)] & G_ASCII_SPACE) != 0)) {
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 | FMTBUF_EXPAND(1)if (column+(1 +1) >= fmtbuf_len) { if (__builtin_mul_overflow ((fmtbuf_len), (2), (&fmtbuf_len))) { do { if (1) { ws_log_full ("", LOG_LEVEL_DEBUG, "wsutil/str_util.c", 978, __func__, "overflow!" ); } } while (0); fmtbuf[column] = '\0'; return fmtbuf; } if ( column+(1 +1) >= fmtbuf_len) { if (__builtin_add_overflow( (fmtbuf_len), ((column + 1 + 2) - fmtbuf_len), (&fmtbuf_len ))) { do { if (1) { ws_log_full("", LOG_LEVEL_DEBUG, "wsutil/str_util.c" , 978, __func__, "overflow!"); } } while (0); fmtbuf[column] = '\0'; return fmtbuf; } } fmtbuf = (char *)wmem_realloc(allocator , fmtbuf, fmtbuf_len); };
979 | FMTBUF_PUTCHAR(' ')fmtbuf[column] = (' '); column++;
980 | } 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 | FMTBUF_EXPAND(2)if (column+(2 +1) >= fmtbuf_len) { if (__builtin_mul_overflow ((fmtbuf_len), (2), (&fmtbuf_len))) { do { if (1) { ws_log_full ("", LOG_LEVEL_DEBUG, "wsutil/str_util.c", 989, __func__, "overflow!" ); } } while (0); fmtbuf[column] = '\0'; return fmtbuf; } if ( column+(2 +1) >= fmtbuf_len) { if (__builtin_add_overflow( (fmtbuf_len), ((column + 2 + 2) - fmtbuf_len), (&fmtbuf_len ))) { do { if (1) { ws_log_full("", LOG_LEVEL_DEBUG, "wsutil/str_util.c" , 989, __func__, "overflow!"); } } while (0); fmtbuf[column] = '\0'; return fmtbuf; } } fmtbuf = (char *)wmem_realloc(allocator , fmtbuf, fmtbuf_len); };
990 | FMTBUF_PUTCHAR('\\')fmtbuf[column] = ('\\'); column++;
991 | switch (c) {
992 |
993 | case '\a':
994 | FMTBUF_PUTCHAR('a')fmtbuf[column] = ('a'); column++;
995 | break;
996 |
997 | case '\b':
998 | FMTBUF_PUTCHAR('b')fmtbuf[column] = ('b'); column++; /* BS */
999 | break;
1000 |
1001 | case '\f':
1002 | FMTBUF_PUTCHAR('f')fmtbuf[column] = ('f'); column++; /* FF */
1003 | break;
1004 |
1005 | case '\n':
1006 | FMTBUF_PUTCHAR('n')fmtbuf[column] = ('n'); column++; /* NL */
1007 | break;
1008 |
1009 | case '\r':
1010 | FMTBUF_PUTCHAR('r')fmtbuf[column] = ('r'); column++; /* CR */
1011 | break;
1012 |
1013 | case '\t':
1014 | FMTBUF_PUTCHAR('t')fmtbuf[column] = ('t'); column++; /* tab */
1015 | break;
1016 |
1017 | case '\v':
1018 | FMTBUF_PUTCHAR('v')fmtbuf[column] = ('v'); column++;
1019 | break;
1020 |
1021 | 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 | FMTBUF_EXPAND(3)if (column+(3 +1) >= fmtbuf_len) { if (__builtin_mul_overflow ((fmtbuf_len), (2), (&fmtbuf_len))) { do { if (1) { ws_log_full ("", LOG_LEVEL_DEBUG, "wsutil/str_util.c", 1028, __func__, "overflow!" ); } } while (0); fmtbuf[column] = '\0'; return fmtbuf; } if ( column+(3 +1) >= fmtbuf_len) { if (__builtin_add_overflow( (fmtbuf_len), ((column + 3 + 2) - fmtbuf_len), (&fmtbuf_len ))) { do { if (1) { ws_log_full("", LOG_LEVEL_DEBUG, "wsutil/str_util.c" , 1028, __func__, "overflow!"); } } while (0); fmtbuf[column] = '\0'; return fmtbuf; } } fmtbuf = (char *)wmem_realloc(allocator , fmtbuf, fmtbuf_len); };
1029 | FMTBUF_PUTBYTE_OCTAL(c)fmtbuf[column] = ((((c)>>6)&03) + '0'); column++; fmtbuf [column] = ((((c)>>3)&07) + '0'); column++; fmtbuf[ column] = ((((c)>>0)&07) + '0'); column++;
1030 | break;
1031 | }
1032 | } else {
1033 | /*
1034 | * We've fetched the first byte of a multi-byte UTF-8
1035 | * sequence into c.
1036 | */
1037 | int utf8_len;
1038 | unsigned char mask;
1039 | gunichar uc;
1040 | unsigned char first;
1041 |
1042 | if ((c & 0xe0) == 0xc0) {
1043 | /* Starts a 2-byte UTF-8 sequence; 1 byte left */
1044 | utf8_len = 1;
1045 | mask = 0x1f;
1046 | } else if ((c & 0xf0) == 0xe0) {
1047 | /* Starts a 3-byte UTF-8 sequence; 2 bytes left */
1048 | utf8_len = 2;
1049 | mask = 0x0f;
1050 | } else if ((c & 0xf8) == 0xf0) {
1051 | /* Starts a 4-byte UTF-8 sequence; 3 bytes left */
1052 | utf8_len = 3;
1053 | mask = 0x07;
1054 | } else if ((c & 0xfc) == 0xf8) {
1055 | /* Starts an old-style 5-byte UTF-8 sequence; 4 bytes left */
1056 | utf8_len = 4;
1057 | mask = 0x03;
1058 | } else if ((c & 0xfe) == 0xfc) {
1059 | /* Starts an old-style 6-byte UTF-8 sequence; 5 bytes left */
1060 | utf8_len = 5;
1061 | mask = 0x01;
1062 | } else {
1063 | /* 0xfe or 0xff or a continuation byte - not valid */
1064 | utf8_len = -1;
1065 | }
1066 | if (utf8_len > 0) {
1067 | /* Try to construct the Unicode character */
1068 | uc = c & mask;
1069 | for (int i = 0; i < utf8_len; i++) {
1070 | 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 | uc = UNICODE_REPLACEMENT_CHARACTER0x00FFFD;
1078 | break;
1079 | }
1080 | c = *string;
1081 | 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 | uc = UNICODE_REPLACEMENT_CHARACTER0x00FFFD;
1088 | break;
1089 | }
1090 | string++;
1091 | uc = (uc << 6) | (c & 0x3f);
1092 | }
1093 |
1094 | /*
1095 | * If this isn't a valid Unicode character, put in
1096 | * a REPLACEMENT CHARACTER.
1097 | */
1098 | if (!g_unichar_validate(uc))
1099 | uc = UNICODE_REPLACEMENT_CHARACTER0x00FFFD;
1100 | } else {
1101 | /* 0xfe or 0xff; put it a REPLACEMENT CHARACTER */
1102 | uc = UNICODE_REPLACEMENT_CHARACTER0x00FFFD;
1103 | }
1104 |
1105 | /*
1106 | * OK, is it a printable Unicode character?
1107 | */
1108 | 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 | if (uc < 0x80) {
1116 | first = 0;
1117 | utf8_len = 1;
1118 | } else if (uc < 0x800) {
1119 | first = 0xc0;
1120 | utf8_len = 2;
1121 | } else if (uc < 0x10000) {
1122 | first = 0xe0;
1123 | utf8_len = 3;
1124 | } else if (uc < 0x200000) {
1125 | first = 0xf0;
1126 | utf8_len = 4;
1127 | } else if (uc < 0x4000000) {
1128 | /*
1129 | * This should never happen, as Unicode doesn't
1130 | * go that high.
1131 | */
1132 | first = 0xf8;
1133 | utf8_len = 5;
1134 | } else {
1135 | /*
1136 | * This should never happen, as Unicode doesn't
1137 | * go that high.
1138 | */
1139 | first = 0xfc;
1140 | utf8_len = 6;
1141 | }
1142 | FMTBUF_EXPAND(utf8_len)if (column+(utf8_len+1) >= fmtbuf_len) { if (__builtin_mul_overflow ((fmtbuf_len), (2), (&fmtbuf_len))) { do { if (1) { ws_log_full ("", LOG_LEVEL_DEBUG, "wsutil/str_util.c", 1142, __func__, "overflow!" ); } } while (0); fmtbuf[column] = '\0'; return fmtbuf; } if ( column+(utf8_len+1) >= fmtbuf_len) { if (__builtin_add_overflow ((fmtbuf_len), ((column + utf8_len + 2) - fmtbuf_len), (& fmtbuf_len))) { do { if (1) { ws_log_full("", LOG_LEVEL_DEBUG , "wsutil/str_util.c", 1142, __func__, "overflow!"); } } while (0); fmtbuf[column] = '\0'; return fmtbuf; } } fmtbuf = (char *)wmem_realloc(allocator, fmtbuf, fmtbuf_len); };
1143 | for (int i = utf8_len - 1; i > 0; i--) {
1144 | fmtbuf[column + i] = (uc & 0x3f) | 0x80;
1145 | uc >>= 6;
1146 | }
1147 | fmtbuf[column] = uc | first;
1148 | column += utf8_len;
1149 | } 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 | FMTBUF_EXPAND(1)if (column+(1 +1) >= fmtbuf_len) { if (__builtin_mul_overflow ((fmtbuf_len), (2), (&fmtbuf_len))) { do { if (1) { ws_log_full ("", LOG_LEVEL_DEBUG, "wsutil/str_util.c", 1157, __func__, "overflow!" ); } } while (0); fmtbuf[column] = '\0'; return fmtbuf; } if ( column+(1 +1) >= fmtbuf_len) { if (__builtin_add_overflow( (fmtbuf_len), ((column + 1 + 2) - fmtbuf_len), (&fmtbuf_len ))) { do { if (1) { ws_log_full("", LOG_LEVEL_DEBUG, "wsutil/str_util.c" , 1157, __func__, "overflow!"); } } while (0); fmtbuf[column] = '\0'; return fmtbuf; } } fmtbuf = (char *)wmem_realloc(allocator , fmtbuf, fmtbuf_len); };
1158 | FMTBUF_PUTCHAR(' ')fmtbuf[column] = (' '); column++;
1159 | } 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 | FMTBUF_EXPAND(2)if (column+(2 +1) >= fmtbuf_len) { if (__builtin_mul_overflow ((fmtbuf_len), (2), (&fmtbuf_len))) { do { if (1) { ws_log_full ("", LOG_LEVEL_DEBUG, "wsutil/str_util.c", 1168, __func__, "overflow!" ); } } while (0); fmtbuf[column] = '\0'; return fmtbuf; } if ( column+(2 +1) >= fmtbuf_len) { if (__builtin_add_overflow( (fmtbuf_len), ((column + 2 + 2) - fmtbuf_len), (&fmtbuf_len ))) { do { if (1) { ws_log_full("", LOG_LEVEL_DEBUG, "wsutil/str_util.c" , 1168, __func__, "overflow!"); } } while (0); fmtbuf[column] = '\0'; return fmtbuf; } } fmtbuf = (char *)wmem_realloc(allocator , fmtbuf, fmtbuf_len); };
1169 | FMTBUF_PUTCHAR('\\')fmtbuf[column] = ('\\'); column++;
1170 | switch (c) {
1171 |
1172 | case '\a':
1173 | FMTBUF_PUTCHAR('a')fmtbuf[column] = ('a'); column++;
1174 | break;
1175 |
1176 | case '\b':
1177 | FMTBUF_PUTCHAR('b')fmtbuf[column] = ('b'); column++; /* BS */
1178 | break;
1179 |
1180 | case '\f':
1181 | FMTBUF_PUTCHAR('f')fmtbuf[column] = ('f'); column++; /* FF */
1182 | break;
1183 |
1184 | case '\n':
1185 | FMTBUF_PUTCHAR('n')fmtbuf[column] = ('n'); column++; /* NL */
1186 | break;
1187 |
1188 | case '\r':
1189 | FMTBUF_PUTCHAR('r')fmtbuf[column] = ('r'); column++; /* CR */
1190 | break;
1191 |
1192 | case '\t':
1193 | FMTBUF_PUTCHAR('t')fmtbuf[column] = ('t'); column++; /* tab */
1194 | break;
1195 |
1196 | case '\v':
1197 | FMTBUF_PUTCHAR('v')fmtbuf[column] = ('v'); column++;
1198 | break;
1199 |
1200 | 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 | FMTBUF_EXPAND(3)if (column+(3 +1) >= fmtbuf_len) { if (__builtin_mul_overflow ((fmtbuf_len), (2), (&fmtbuf_len))) { do { if (1) { ws_log_full ("", LOG_LEVEL_DEBUG, "wsutil/str_util.c", 1207, __func__, "overflow!" ); } } while (0); fmtbuf[column] = '\0'; return fmtbuf; } if ( column+(3 +1) >= fmtbuf_len) { if (__builtin_add_overflow( (fmtbuf_len), ((column + 3 + 2) - fmtbuf_len), (&fmtbuf_len ))) { do { if (1) { ws_log_full("", LOG_LEVEL_DEBUG, "wsutil/str_util.c" , 1207, __func__, "overflow!"); } } while (0); fmtbuf[column] = '\0'; return fmtbuf; } } fmtbuf = (char *)wmem_realloc(allocator , fmtbuf, fmtbuf_len); };
1208 | FMTBUF_PUTBYTE_OCTAL(c)fmtbuf[column] = ((((c)>>6)&03) + '0'); column++; fmtbuf [column] = ((((c)>>3)&07) + '0'); column++; fmtbuf[ column] = ((((c)>>0)&07) + '0'); column++;
1209 | break;
1210 | }
1211 | } else {
1212 | /*
1213 | * Unicode, but not printable, and not ASCII;
1214 | * put it out as \uxxxx or \Uxxxxxxxx.
1215 | */
1216 | if (uc <= 0xFFFF) {
1217 | FMTBUF_EXPAND(6)if (column+(6 +1) >= fmtbuf_len) { if (__builtin_mul_overflow ((fmtbuf_len), (2), (&fmtbuf_len))) { do { if (1) { ws_log_full ("", LOG_LEVEL_DEBUG, "wsutil/str_util.c", 1217, __func__, "overflow!" ); } } while (0); fmtbuf[column] = '\0'; return fmtbuf; } if ( column+(6 +1) >= fmtbuf_len) { if (__builtin_add_overflow( (fmtbuf_len), ((column + 6 + 2) - fmtbuf_len), (&fmtbuf_len ))) { do { if (1) { ws_log_full("", LOG_LEVEL_DEBUG, "wsutil/str_util.c" , 1217, __func__, "overflow!"); } } while (0); fmtbuf[column] = '\0'; return fmtbuf; } } fmtbuf = (char *)wmem_realloc(allocator , fmtbuf, fmtbuf_len); };
1218 | FMTBUF_PUTCHAR('\\')fmtbuf[column] = ('\\'); column++;
1219 | FMTBUF_PUTCHAR('u')fmtbuf[column] = ('u'); column++;
1220 | FMTBUF_PUTCHAR(hex[(uc >> 12) & 0xF])fmtbuf[column] = (hex[(uc >> 12) & 0xF]); column++;
1221 | FMTBUF_PUTCHAR(hex[(uc >> 8) & 0xF])fmtbuf[column] = (hex[(uc >> 8) & 0xF]); column++;
1222 | FMTBUF_PUTCHAR(hex[(uc >> 4) & 0xF])fmtbuf[column] = (hex[(uc >> 4) & 0xF]); column++;
1223 | FMTBUF_PUTCHAR(hex[(uc >> 0) & 0xF])fmtbuf[column] = (hex[(uc >> 0) & 0xF]); column++;
1224 | } else {
1225 | FMTBUF_EXPAND(10)if (column+(10 +1) >= fmtbuf_len) { if (__builtin_mul_overflow ((fmtbuf_len), (2), (&fmtbuf_len))) { do { if (1) { ws_log_full ("", LOG_LEVEL_DEBUG, "wsutil/str_util.c", 1225, __func__, "overflow!" ); } } while (0); fmtbuf[column] = '\0'; return fmtbuf; } if ( column+(10 +1) >= fmtbuf_len) { if (__builtin_add_overflow ((fmtbuf_len), ((column + 10 + 2) - fmtbuf_len), (&fmtbuf_len ))) { do { if (1) { ws_log_full("", LOG_LEVEL_DEBUG, "wsutil/str_util.c" , 1225, __func__, "overflow!"); } } while (0); fmtbuf[column] = '\0'; return fmtbuf; } } fmtbuf = (char *)wmem_realloc(allocator , fmtbuf, fmtbuf_len); };
1226 | FMTBUF_PUTCHAR('\\')fmtbuf[column] = ('\\'); column++;
1227 | FMTBUF_PUTCHAR('U')fmtbuf[column] = ('U'); column++;
1228 | FMTBUF_PUTCHAR(hex[(uc >> 28) & 0xF])fmtbuf[column] = (hex[(uc >> 28) & 0xF]); column++;
1229 | FMTBUF_PUTCHAR(hex[(uc >> 24) & 0xF])fmtbuf[column] = (hex[(uc >> 24) & 0xF]); column++;
1230 | FMTBUF_PUTCHAR(hex[(uc >> 20) & 0xF])fmtbuf[column] = (hex[(uc >> 20) & 0xF]); column++;
1231 | FMTBUF_PUTCHAR(hex[(uc >> 16) & 0xF])fmtbuf[column] = (hex[(uc >> 16) & 0xF]); column++;
1232 | FMTBUF_PUTCHAR(hex[(uc >> 12) & 0xF])fmtbuf[column] = (hex[(uc >> 12) & 0xF]); column++;
1233 | FMTBUF_PUTCHAR(hex[(uc >> 8) & 0xF])fmtbuf[column] = (hex[(uc >> 8) & 0xF]); column++;
1234 | FMTBUF_PUTCHAR(hex[(uc >> 4) & 0xF])fmtbuf[column] = (hex[(uc >> 4) & 0xF]); column++;
1235 | FMTBUF_PUTCHAR(hex[(uc >> 0) & 0xF])fmtbuf[column] = (hex[(uc >> 0) & 0xF]); column++;
1236 | }
1237 | }
1238 | }
1239 | prev = string;
1240 | }
1241 | }
1242 | if (printable_bytes) {
1243 | FMTBUF_PUTBYTES(prev, printable_bytes)if (column+(printable_bytes+1) >= fmtbuf_len) { if (__builtin_mul_overflow ((fmtbuf_len), (2), (&fmtbuf_len))) { do { if (1) { ws_log_full ("", LOG_LEVEL_DEBUG, "wsutil/str_util.c", 1243, __func__, "overflow!" ); } } while (0); fmtbuf[column] = '\0'; return fmtbuf; } if ( column+(printable_bytes+1) >= fmtbuf_len) { if (__builtin_add_overflow ((fmtbuf_len), ((column + printable_bytes + 2) - fmtbuf_len), (&fmtbuf_len))) { do { if (1) { ws_log_full("", LOG_LEVEL_DEBUG , "wsutil/str_util.c", 1243, __func__, "overflow!"); } } while (0); fmtbuf[column] = '\0'; return fmtbuf; } } fmtbuf = (char *)wmem_realloc(allocator, fmtbuf, fmtbuf_len); } memcpy(& fmtbuf[column], prev, printable_bytes); column += (unsigned)printable_bytes ;;
1244 | printable_bytes = 0;
| Value stored to 'printable_bytes' is never read
1245 | }
1246 |
1247 | FMTBUF_ENDSTRfmtbuf[column] = '\0';
1248 |
1249 | return fmtbuf;
1250 | }
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 | {
1283 | return format_text_internal(allocator, (const uint8_t*)string, len, false0);
1284 | }
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 | {
1307 | return format_text_internal(allocator, (const uint8_t*)string, strlen(string), false0);
1308 | }
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 | {
1319 | return format_text_internal(allocator, (const uint8_t*)string, len, true1);
1320 | }
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 | {
1336 | wmem_strbuf_t *buf;
1337 |
1338 | buf = wmem_strbuf_new_sized(allocator, len + 1);
1339 | for (const char *p = string; p < string + len; p++) {
1340 | if (g_ascii_isprint(*p)((g_ascii_table[(guchar) (*p)] & G_ASCII_PRINT) != 0)) {
1341 | wmem_strbuf_append_c(buf, *p);
1342 | }
1343 | else if (g_ascii_isspace(*p)((g_ascii_table[(guchar) (*p)] & G_ASCII_SPACE) != 0)) {
1344 | wmem_strbuf_append_c(buf, ' ');
1345 | }
1346 | else {
1347 | wmem_strbuf_append_c(buf, chr);
1348 | }
1349 | }
1350 | return wmem_strbuf_finalize(buf);
1351 | }
1352 |
1353 | char *
1354 | format_char(wmem_allocator_t *allocator, char c)
1355 | {
1356 | char *buf;
1357 | char r;
1358 |
1359 | if (g_ascii_isprint(c)((g_ascii_table[(guchar) (c)] & G_ASCII_PRINT) != 0)) {
1360 | buf = wmem_alloc_array(allocator, char, 2)((char*)wmem_alloc((allocator), (((((2)) <= 0) || ((size_t )sizeof(char) > (9223372036854775807L / (size_t)((2))))) ? 0 : (sizeof(char) * ((2))))));
1361 | buf[0] = c;
1362 | buf[1] = '\0';
1363 | return buf;
1364 | }
1365 | if (escape_char(c, &r)) {
1366 | buf = wmem_alloc_array(allocator, char, 3)((char*)wmem_alloc((allocator), (((((3)) <= 0) || ((size_t )sizeof(char) > (9223372036854775807L / (size_t)((3))))) ? 0 : (sizeof(char) * ((3))))));
1367 | buf[0] = '\\';
1368 | buf[1] = r;
1369 | buf[2] = '\0';
1370 | return buf;
1371 | }
1372 | buf = wmem_alloc_array(allocator, char, 5)((char*)wmem_alloc((allocator), (((((5)) <= 0) || ((size_t )sizeof(char) > (9223372036854775807L / (size_t)((5))))) ? 0 : (sizeof(char) * ((5))))));
1373 | buf[0] = '\\';
1374 | buf[1] = 'x';
1375 | buf[2] = hex[((uint8_t)c >> 4) & 0xF];
1376 | buf[3] = hex[((uint8_t)c >> 0) & 0xF];
1377 | buf[4] = '\0';
1378 | return buf;
1379 | }
1380 |
1381 | char*
1382 | ws_utf8_truncate(char *string, size_t len)
1383 | {
1384 | char* last_char;
1385 |
1386 | /* Ensure that it is null terminated */
1387 | string[len] = '\0';
1388 | last_char = g_utf8_find_prev_char(string, string + len);
1389 | if (last_char != NULL((void*)0) && g_utf8_get_char_validated(last_char, -1) == (gunichar)-2) {
1390 | /* The last UTF-8 character was truncated into a partial sequence. */
1391 | *last_char = '\0';
1392 | }
1393 | return string;
1394 | }
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 | {
1493 | unsigned i;
1494 | uint8_t *bufptr;
1495 |
1496 | bufptr = buf;
1497 |
1498 | for (i = 0; i < bytes; i++, bufptr++) {
1499 | *bufptr = EBCDIC_translate_ASCII[*bufptr];
1500 | }
1501 | }
1502 |
1503 | uint8_t
1504 | EBCDIC_to_ASCII1(uint8_t c)
1505 | {
1506 | return EBCDIC_translate_ASCII[c];
1507 | }
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_LEN8 8 /* max length of hex offset of bytes */
1517 | #define BYTES_PER_LINE16 16 /* max byte values printed on a line */
1518 | #define HEX_DUMP_LEN(16*3) (BYTES_PER_LINE16*3)
1519 | /* max number of characters hex dump takes -
1520 | 2 digits plus trailing blank */
1521 | #define DATA_DUMP_LEN((16*3) + 2 + 2 + 16) (HEX_DUMP_LEN(16*3) + 2 + 2 + BYTES_PER_LINE16)
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(8 + 2 + ((16*3) + 2 + 2 + 16)) (MAX_OFFSET_LEN8 + 2 + DATA_DUMP_LEN((16*3) + 2 + 2 + 16))
1528 | /* number of characters per line;
1529 | offset, 2 blanks separating offset
1530 | from data dump, data dump */
1531 |
1532 | bool_Bool
1533 | hex_dump_buffer(bool_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 | {
1538 | register unsigned int ad, i, j, k, l;
1539 | unsigned char c;
1540 | char line[MAX_LINE_LEN(8 + 2 + ((16*3) + 2 + 2 + 16)) + 1];
1541 | unsigned int use_digits;
1542 |
1543 | static const char binhex[16] = {
1544 | '0', '1', '2', '3', '4', '5', '6', '7',
1545 | '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 | if (((length - 1) & 0xF0000000) != 0)
1553 | use_digits = 8; /* need all 8 digits */
1554 | else if (((length - 1) & 0x0F000000) != 0)
1555 | use_digits = 7; /* need 7 digits */
1556 | else if (((length - 1) & 0x00F00000) != 0)
1557 | use_digits = 6; /* need 6 digits */
1558 | else if (((length - 1) & 0x000F0000) != 0)
1559 | use_digits = 5; /* need 5 digits */
1560 | else
1561 | use_digits = 4; /* we'll supply 4 digits */
1562 |
1563 | ad = 0;
1564 | i = 0;
1565 | j = 0;
1566 | k = 0;
1567 | while (i < length) {
1568 | if ((i & 15) == 0) {
1569 | /*
1570 | * Start of a new line.
1571 | */
1572 | j = 0;
1573 | l = use_digits;
1574 | do {
1575 | l--;
1576 | c = (ad >> (l*4)) & 0xF;
1577 | line[j++] = binhex[c];
1578 | } while (l != 0);
1579 | line[j++] = ' ';
1580 | line[j++] = ' ';
1581 | memset(line+j, ' ', DATA_DUMP_LEN((16*3) + 2 + 2 + 16));
1582 |
1583 | /*
1584 | * Offset in line of ASCII dump.
1585 | */
1586 | k = j + HEX_DUMP_LEN(16*3) + 2;
1587 | if (ascii_option == HEXDUMP_ASCII_DELIMIT(0x0001U))
1588 | line[k++] = '|';
1589 | }
1590 | c = *cp++;
1591 | line[j++] = binhex[c>>4];
1592 | line[j++] = binhex[c&0xf];
1593 | j++;
1594 | if (ascii_option != HEXDUMP_ASCII_EXCLUDE(0x0002U) ) {
1595 | if (encoding == HEXDUMP_ENC_EBCDIC) {
1596 | c = EBCDIC_to_ASCII1(c);
1597 | }
1598 | line[k++] = ((c >= ' ') && (c < 0x7f)) ? c : '.';
1599 | }
1600 | i++;
1601 | 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 | if (ascii_option == HEXDUMP_ASCII_DELIMIT(0x0001U))
1609 | line[k++] = '|';
1610 | line[k] = '\0';
1611 | if (!print_line(fp, line))
1612 | return false0;
1613 | ad += 16;
1614 | }
1615 | }
1616 | return true1;
1617 | }
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 | */