### Bug Summary

File: | builds/wireshark/wireshark/wsutil/str_util.c
Warning: | line 1267, 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-09-06-100407-3660-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 | bool_Bool ws_csv_value_is_formula(const char *string)
830 | {
831 | if (string == NULL((void*)0))
832 | return false0;
833 |
834 | /* Some spreadsheet applications strip leading spaces on import and then
835 | * evaluate what follows, so look past them. */
836 | while (*string == ' ')
837 | string++;
838 |
839 | switch (*string) {
840 | case '=': /* Formula */
841 | case '+': /* Formula */
842 | case '-': /* Formula */
843 | case '@': /* Lotus-style function, still honored for compatibility */
844 | case '\t': /* Stripped on import, so it can precede any of the above */
845 | case '\r':
846 | return true1;
847 | default:
848 | return false0;
849 | }
850 | }
851 |
852 | const char *
853 | ws_strerrorname_r(int errnum, char *buf, size_t buf_size)
854 | {
855 | #ifdef HAVE_STRERRORNAME_NP1
856 | const char *errstr = strerrorname_np(errnum);
857 | if (errstr != NULL((void*)0)) {
858 | (void)g_strlcpy(buf, errstr, buf_size);
859 | return buf;
860 | }
861 | #endif
862 | snprintf(buf, buf_size, "Errno(%d)", errnum);
863 | return buf;
864 | }
865 |
866 | char *
867 | ws_strdup_underline(wmem_allocator_t *allocator, long offset, size_t len)
868 | {
869 | if (offset < 0)
870 | return NULL((void*)0);
871 |
872 | wmem_strbuf_t *buf = wmem_strbuf_new_sized(allocator, offset + len);
873 |
874 | for (int i = 0; i < offset; i++) {
875 | wmem_strbuf_append_c(buf, ' ');
876 | }
877 | wmem_strbuf_append_c(buf, '^');
878 |
879 | for (size_t l = len; l > 1; l--) {
880 | wmem_strbuf_append_c(buf, '~');
881 | }
882 |
883 | return wmem_strbuf_finalize(buf);
884 | }
885 |
886 | #define INITIAL_FMTBUF_SIZE128 128
887 |
888 | /*
889 | * Declare, and initialize, the variables used for an output buffer.
890 | */
891 | #define FMTBUF_VARSchar *fmtbuf = (char*)wmem_alloc(allocator, 128); unsigned fmtbuf_len = 128; unsigned column = 0 \
892 | char *fmtbuf = (char*)wmem_alloc(allocator, INITIAL_FMTBUF_SIZE128); \
893 | unsigned fmtbuf_len = INITIAL_FMTBUF_SIZE128; \
894 | unsigned column = 0
895 |
896 | /*
897 | * Expand the buffer to be large enough to add nbytes bytes, plus a
898 | * terminating '\0'.
899 | */
900 | #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", 900, __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" , 900, __func__, "overflow."); } } while (0); fmtbuf[column] = '\0'; return fmtbuf; } } fmtbuf = (char *)wmem_realloc(allocator , fmtbuf, fmtbuf_len); } \
901 | /* \
902 | * Is there enough room for those bytes and also enough room for \
903 | * a terminating '\0'? \
904 | */ \
905 | if (column+(nbytes+1) >= fmtbuf_len) { \
906 | /* \
907 | * Double the buffer's size if it's not big enough. \
908 | * The size of the buffer starts at 128, so doubling its size \
909 | * adds at least another 128 bytes, which is more than enough \
910 | * for one more character plus a terminating '\0'. \
911 | */ \
912 | if (ckd_mul(&fmtbuf_len, fmtbuf_len, 2)__builtin_mul_overflow((fmtbuf_len), (2), (&fmtbuf_len))) { \
913 | ws_debug("overflow.")do { if (1) { ws_log_full("", LOG_LEVEL_DEBUG, "wsutil/str_util.c" , 913, __func__, "overflow."); } } while (0); \
914 | FMTBUF_ENDSTRfmtbuf[column] = '\0'; \
915 | return fmtbuf; \
916 | } \
917 | if (column+(nbytes+1) >= fmtbuf_len) { \
918 | if (ckd_add(&fmtbuf_len, fmtbuf_len, (column + nbytes + 2) - fmtbuf_len)__builtin_add_overflow((fmtbuf_len), ((column + nbytes + 2) - fmtbuf_len), (&fmtbuf_len))) { \
919 | ws_debug("overflow.")do { if (1) { ws_log_full("", LOG_LEVEL_DEBUG, "wsutil/str_util.c" , 919, __func__, "overflow."); } } while (0); \
920 | FMTBUF_ENDSTRfmtbuf[column] = '\0'; \
921 | return fmtbuf; \
922 | } \
923 | } \
924 | fmtbuf = (char *)wmem_realloc(allocator, fmtbuf, fmtbuf_len); \
925 | }
926 |
927 | /*
928 | * Put a byte into the buffer; space must have been ensured for it.
929 | */
930 | #define FMTBUF_PUTCHAR(b)fmtbuf[column] = (b); column++ \
931 | fmtbuf[column] = (b); \
932 | column++
933 |
934 | /*
935 | * Add the one-byte argument, as an octal escape sequence, to the end
936 | * of the buffer.
937 | */
938 | #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++ \
939 | FMTBUF_PUTCHAR((((b)>>6)&03) + '0')fmtbuf[column] = ((((b)>>6)&03) + '0'); column++; \
940 | FMTBUF_PUTCHAR((((b)>>3)&07) + '0')fmtbuf[column] = ((((b)>>3)&07) + '0'); column++; \
941 | FMTBUF_PUTCHAR((((b)>>0)&07) + '0')fmtbuf[column] = ((((b)>>0)&07) + '0'); column++
942 |
943 | /*
944 | * Add the one-byte argument, as a hex escape sequence, to the end
945 | * of the buffer.
946 | */
947 | #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 ++ \
948 | FMTBUF_PUTCHAR('\\')fmtbuf[column] = ('\\'); column++; \
949 | FMTBUF_PUTCHAR('x')fmtbuf[column] = ('x'); column++; \
950 | FMTBUF_PUTCHAR(hex[((b) >> 4) & 0xF])fmtbuf[column] = (hex[((b) >> 4) & 0xF]); column++; \
951 | FMTBUF_PUTCHAR(hex[((b) >> 0) & 0xF])fmtbuf[column] = (hex[((b) >> 0) & 0xF]); column++
952 |
953 | #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", 953, __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" , 953, __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; \
954 | 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", 954, __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" , 954, __func__, "overflow."); } } while (0); fmtbuf[column] = '\0'; return fmtbuf; } } fmtbuf = (char *)wmem_realloc(allocator , fmtbuf, fmtbuf_len); } \
955 | memcpy(&fmtbuf[column], bytes, len); \
956 | column += (unsigned)len; // FMTBUF_EXPAND checks for overflow
957 |
958 | /*
959 | * Put the trailing '\0' at the end of the buffer.
960 | */
961 | #define FMTBUF_ENDSTRfmtbuf[column] = '\0' \
962 | fmtbuf[column] = '\0'
963 |
964 | static char *
965 | format_text_internal(wmem_allocator_t *allocator,
966 | const unsigned char *string, size_t len,
967 | bool_Bool replace_space)
968 | {
969 | FMTBUF_VARSchar *fmtbuf = (char*)wmem_alloc(allocator, 128); unsigned fmtbuf_len = 128; unsigned column = 0;
970 | const unsigned char *prev = string;
971 | const unsigned char *stringend = string + len;
972 | unsigned char c;
973 | size_t printable_bytes = 0;
974 |
975 | while (string < stringend) {
976 | /*
977 | * Get the first byte of this character.
978 | */
979 | c = *string++;
980 | if ((0x20 <= c) && (c < 0x7F)) {
981 | /*
982 | * Printable ASCII, so not part of a multi-byte UTF-8 sequence.
983 | * Make sure there's enough room for one more byte, and add
984 | * the character.
985 | */
986 | printable_bytes++;
987 | } else {
988 | if (printable_bytes) {
989 | 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", 989, __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", 989, __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 ;;
990 | printable_bytes = 0;
991 | }
992 | if (replace_space && g_ascii_isspace(c)((g_ascii_table[(guchar) (c)] & G_ASCII_SPACE) != 0)) {
993 | /*
994 | * ASCII, so not part of a multi-byte UTF-8 sequence, but
995 | * not printable, but is a space character; show it as a
996 | * blank.
997 | *
998 | * Make sure there's enough room for one more byte, and add
999 | * the blank.
1000 | */
1001 | 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", 1001, __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" , 1001, __func__, "overflow."); } } while (0); fmtbuf[column] = '\0'; return fmtbuf; } } fmtbuf = (char *)wmem_realloc(allocator , fmtbuf, fmtbuf_len); };
1002 | FMTBUF_PUTCHAR(' ')fmtbuf[column] = (' '); column++;
1003 | } else if (c < 128) {
1004 | /*
1005 | * ASCII, so not part of a multi-byte UTF-8 sequence, but not
1006 | * printable.
1007 | *
1008 | * That requires a minimum of 2 bytes, one for the backslash
1009 | * and one for a letter, so make sure we have enough room
1010 | * for that, plus a trailing '\0'.
1011 | */
1012 | 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", 1012, __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" , 1012, __func__, "overflow."); } } while (0); fmtbuf[column] = '\0'; return fmtbuf; } } fmtbuf = (char *)wmem_realloc(allocator , fmtbuf, fmtbuf_len); };
1013 | FMTBUF_PUTCHAR('\\')fmtbuf[column] = ('\\'); column++;
1014 | switch (c) {
1015 |
1016 | case '\a':
1017 | FMTBUF_PUTCHAR('a')fmtbuf[column] = ('a'); column++;
1018 | break;
1019 |
1020 | case '\b':
1021 | FMTBUF_PUTCHAR('b')fmtbuf[column] = ('b'); column++; /* BS */
1022 | break;
1023 |
1024 | case '\f':
1025 | FMTBUF_PUTCHAR('f')fmtbuf[column] = ('f'); column++; /* FF */
1026 | break;
1027 |
1028 | case '\n':
1029 | FMTBUF_PUTCHAR('n')fmtbuf[column] = ('n'); column++; /* NL */
1030 | break;
1031 |
1032 | case '\r':
1033 | FMTBUF_PUTCHAR('r')fmtbuf[column] = ('r'); column++; /* CR */
1034 | break;
1035 |
1036 | case '\t':
1037 | FMTBUF_PUTCHAR('t')fmtbuf[column] = ('t'); column++; /* tab */
1038 | break;
1039 |
1040 | case '\v':
1041 | FMTBUF_PUTCHAR('v')fmtbuf[column] = ('v'); column++;
1042 | break;
1043 |
1044 | default:
1045 | /*
1046 | * We've already put the backslash, but this
1047 | * will put 3 more characters for the octal
1048 | * number; make sure we have enough room for
1049 | * that, plus the trailing '\0'.
1050 | */
1051 | 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", 1051, __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" , 1051, __func__, "overflow."); } } while (0); fmtbuf[column] = '\0'; return fmtbuf; } } fmtbuf = (char *)wmem_realloc(allocator , fmtbuf, fmtbuf_len); };
1052 | 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++;
1053 | break;
1054 | }
1055 | } else {
1056 | /*
1057 | * We've fetched the first byte of a multi-byte UTF-8
1058 | * sequence into c.
1059 | */
1060 | int utf8_len;
1061 | unsigned char mask;
1062 | gunichar uc;
1063 | unsigned char first;
1064 |
1065 | if ((c & 0xe0) == 0xc0) {
1066 | /* Starts a 2-byte UTF-8 sequence; 1 byte left */
1067 | utf8_len = 1;
1068 | mask = 0x1f;
1069 | } else if ((c & 0xf0) == 0xe0) {
1070 | /* Starts a 3-byte UTF-8 sequence; 2 bytes left */
1071 | utf8_len = 2;
1072 | mask = 0x0f;
1073 | } else if ((c & 0xf8) == 0xf0) {
1074 | /* Starts a 4-byte UTF-8 sequence; 3 bytes left */
1075 | utf8_len = 3;
1076 | mask = 0x07;
1077 | } else if ((c & 0xfc) == 0xf8) {
1078 | /* Starts an old-style 5-byte UTF-8 sequence; 4 bytes left */
1079 | utf8_len = 4;
1080 | mask = 0x03;
1081 | } else if ((c & 0xfe) == 0xfc) {
1082 | /* Starts an old-style 6-byte UTF-8 sequence; 5 bytes left */
1083 | utf8_len = 5;
1084 | mask = 0x01;
1085 | } else {
1086 | /* 0xfe or 0xff or a continuation byte - not valid */
1087 | utf8_len = -1;
1088 | }
1089 | if (utf8_len > 0) {
1090 | /* Try to construct the Unicode character */
1091 | uc = c & mask;
1092 | for (int i = 0; i < utf8_len; i++) {
1093 | if (string >= stringend) {
1094 | /*
1095 | * Ran out of octets, so the character is
1096 | * incomplete. Put in a REPLACEMENT CHARACTER
1097 | * instead, and then continue the loop, which
1098 | * will terminate.
1099 | */
1100 | uc = UNICODE_REPLACEMENT_CHARACTER0x00FFFD;
1101 | break;
1102 | }
1103 | c = *string;
1104 | if ((c & 0xc0) != 0x80) {
1105 | /*
1106 | * Not valid UTF-8 continuation character; put in
1107 | * a replacement character, and then re-process
1108 | * this octet as the beginning of a new character.
1109 | */
1110 | uc = UNICODE_REPLACEMENT_CHARACTER0x00FFFD;
1111 | break;
1112 | }
1113 | string++;
1114 | uc = (uc << 6) | (c & 0x3f);
1115 | }
1116 |
1117 | /*
1118 | * If this isn't a valid Unicode character, put in
1119 | * a REPLACEMENT CHARACTER.
1120 | */
1121 | if (!g_unichar_validate(uc))
1122 | uc = UNICODE_REPLACEMENT_CHARACTER0x00FFFD;
1123 | } else {
1124 | /* 0xfe or 0xff; put it a REPLACEMENT CHARACTER */
1125 | uc = UNICODE_REPLACEMENT_CHARACTER0x00FFFD;
1126 | }
1127 |
1128 | /*
1129 | * OK, is it a printable Unicode character?
1130 | */
1131 | if (g_unichar_isprint(uc)) {
1132 | /*
1133 | * Yes - put it into the string as UTF-8.
1134 | * This means that if it was an overlong
1135 | * encoding, this will put out the right
1136 | * sized encoding.
1137 | */
1138 | if (uc < 0x80) {
1139 | first = 0;
1140 | utf8_len = 1;
1141 | } else if (uc < 0x800) {
1142 | first = 0xc0;
1143 | utf8_len = 2;
1144 | } else if (uc < 0x10000) {
1145 | first = 0xe0;
1146 | utf8_len = 3;
1147 | } else if (uc < 0x200000) {
1148 | first = 0xf0;
1149 | utf8_len = 4;
1150 | } else if (uc < 0x4000000) {
1151 | /*
1152 | * This should never happen, as Unicode doesn't
1153 | * go that high.
1154 | */
1155 | first = 0xf8;
1156 | utf8_len = 5;
1157 | } else {
1158 | /*
1159 | * This should never happen, as Unicode doesn't
1160 | * go that high.
1161 | */
1162 | first = 0xfc;
1163 | utf8_len = 6;
1164 | }
1165 | 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", 1165, __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", 1165, __func__, "overflow."); } } while (0); fmtbuf[column] = '\0'; return fmtbuf; } } fmtbuf = (char *)wmem_realloc(allocator, fmtbuf, fmtbuf_len); };
1166 | for (int i = utf8_len - 1; i > 0; i--) {
1167 | fmtbuf[column + i] = (uc & 0x3f) | 0x80;
1168 | uc >>= 6;
1169 | }
1170 | fmtbuf[column] = uc | first;
1171 | column += utf8_len;
1172 | } else if (replace_space && g_unichar_isspace(uc)) {
1173 | /*
1174 | * Not printable, but is a space character; show it
1175 | * as a blank.
1176 | *
1177 | * Make sure there's enough room for one more byte,
1178 | * and add the blank.
1179 | */
1180 | 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", 1180, __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" , 1180, __func__, "overflow."); } } while (0); fmtbuf[column] = '\0'; return fmtbuf; } } fmtbuf = (char *)wmem_realloc(allocator , fmtbuf, fmtbuf_len); };
1181 | FMTBUF_PUTCHAR(' ')fmtbuf[column] = (' '); column++;
1182 | } else if (c < 128) {
1183 | /*
1184 | * ASCII, but not printable.
1185 | * Yes, this could happen with an overlong encoding.
1186 | *
1187 | * That requires a minimum of 2 bytes, one for the
1188 | * backslash and one for a letter, so make sure we
1189 | * have enough room for that, plus a trailing '\0'.
1190 | */
1191 | 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", 1191, __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" , 1191, __func__, "overflow."); } } while (0); fmtbuf[column] = '\0'; return fmtbuf; } } fmtbuf = (char *)wmem_realloc(allocator , fmtbuf, fmtbuf_len); };
1192 | FMTBUF_PUTCHAR('\\')fmtbuf[column] = ('\\'); column++;
1193 | switch (c) {
1194 |
1195 | case '\a':
1196 | FMTBUF_PUTCHAR('a')fmtbuf[column] = ('a'); column++;
1197 | break;
1198 |
1199 | case '\b':
1200 | FMTBUF_PUTCHAR('b')fmtbuf[column] = ('b'); column++; /* BS */
1201 | break;
1202 |
1203 | case '\f':
1204 | FMTBUF_PUTCHAR('f')fmtbuf[column] = ('f'); column++; /* FF */
1205 | break;
1206 |
1207 | case '\n':
1208 | FMTBUF_PUTCHAR('n')fmtbuf[column] = ('n'); column++; /* NL */
1209 | break;
1210 |
1211 | case '\r':
1212 | FMTBUF_PUTCHAR('r')fmtbuf[column] = ('r'); column++; /* CR */
1213 | break;
1214 |
1215 | case '\t':
1216 | FMTBUF_PUTCHAR('t')fmtbuf[column] = ('t'); column++; /* tab */
1217 | break;
1218 |
1219 | case '\v':
1220 | FMTBUF_PUTCHAR('v')fmtbuf[column] = ('v'); column++;
1221 | break;
1222 |
1223 | default:
1224 | /*
1225 | * We've already put the backslash, but this
1226 | * will put 3 more characters for the octal
1227 | * number; make sure we have enough room for
1228 | * that, plus the trailing '\0'.
1229 | */
1230 | 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", 1230, __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" , 1230, __func__, "overflow."); } } while (0); fmtbuf[column] = '\0'; return fmtbuf; } } fmtbuf = (char *)wmem_realloc(allocator , fmtbuf, fmtbuf_len); };
1231 | 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++;
1232 | break;
1233 | }
1234 | } else {
1235 | /*
1236 | * Unicode, but not printable, and not ASCII;
1237 | * put it out as \uxxxx or \Uxxxxxxxx.
1238 | */
1239 | if (uc <= 0xFFFF) {
1240 | 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", 1240, __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" , 1240, __func__, "overflow."); } } while (0); fmtbuf[column] = '\0'; return fmtbuf; } } fmtbuf = (char *)wmem_realloc(allocator , fmtbuf, fmtbuf_len); };
1241 | FMTBUF_PUTCHAR('\\')fmtbuf[column] = ('\\'); column++;
1242 | FMTBUF_PUTCHAR('u')fmtbuf[column] = ('u'); column++;
1243 | FMTBUF_PUTCHAR(hex[(uc >> 12) & 0xF])fmtbuf[column] = (hex[(uc >> 12) & 0xF]); column++;
1244 | FMTBUF_PUTCHAR(hex[(uc >> 8) & 0xF])fmtbuf[column] = (hex[(uc >> 8) & 0xF]); column++;
1245 | FMTBUF_PUTCHAR(hex[(uc >> 4) & 0xF])fmtbuf[column] = (hex[(uc >> 4) & 0xF]); column++;
1246 | FMTBUF_PUTCHAR(hex[(uc >> 0) & 0xF])fmtbuf[column] = (hex[(uc >> 0) & 0xF]); column++;
1247 | } else {
1248 | 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", 1248, __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" , 1248, __func__, "overflow."); } } while (0); fmtbuf[column] = '\0'; return fmtbuf; } } fmtbuf = (char *)wmem_realloc(allocator , fmtbuf, fmtbuf_len); };
1249 | FMTBUF_PUTCHAR('\\')fmtbuf[column] = ('\\'); column++;
1250 | FMTBUF_PUTCHAR('U')fmtbuf[column] = ('U'); column++;
1251 | FMTBUF_PUTCHAR(hex[(uc >> 28) & 0xF])fmtbuf[column] = (hex[(uc >> 28) & 0xF]); column++;
1252 | FMTBUF_PUTCHAR(hex[(uc >> 24) & 0xF])fmtbuf[column] = (hex[(uc >> 24) & 0xF]); column++;
1253 | FMTBUF_PUTCHAR(hex[(uc >> 20) & 0xF])fmtbuf[column] = (hex[(uc >> 20) & 0xF]); column++;
1254 | FMTBUF_PUTCHAR(hex[(uc >> 16) & 0xF])fmtbuf[column] = (hex[(uc >> 16) & 0xF]); column++;
1255 | FMTBUF_PUTCHAR(hex[(uc >> 12) & 0xF])fmtbuf[column] = (hex[(uc >> 12) & 0xF]); column++;
1256 | FMTBUF_PUTCHAR(hex[(uc >> 8) & 0xF])fmtbuf[column] = (hex[(uc >> 8) & 0xF]); column++;
1257 | FMTBUF_PUTCHAR(hex[(uc >> 4) & 0xF])fmtbuf[column] = (hex[(uc >> 4) & 0xF]); column++;
1258 | FMTBUF_PUTCHAR(hex[(uc >> 0) & 0xF])fmtbuf[column] = (hex[(uc >> 0) & 0xF]); column++;
1259 | }
1260 | }
1261 | }
1262 | prev = string;
1263 | }
1264 | }
1265 | if (printable_bytes) {
1266 | 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", 1266, __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", 1266, __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 ;;
1267 | printable_bytes = 0;
| Value stored to 'printable_bytes' is never read
1268 | }
1269 |
1270 | FMTBUF_ENDSTRfmtbuf[column] = '\0';
1271 |
1272 | return fmtbuf;
1273 | }
1274 |
1275 | /*
1276 | * Given a wmem scope, a not-necessarily-null-terminated string,
1277 | * expected to be in UTF-8 but possibly containing invalid sequences
1278 | * (as it may have come from packet data), and the length of the string,
1279 | * generate a valid UTF-8 string from it, allocated in the specified
1280 | * wmem scope, that:
1281 | *
1282 | * shows printable Unicode characters as themselves;
1283 | *
1284 | * shows non-printable ASCII characters as C-style escapes (octal
1285 | * if not one of the standard ones such as LF -> '\n');
1286 | *
1287 | * shows non-printable Unicode-but-not-ASCII characters as
1288 | * their universal character names;
1289 | *
1290 | * shows illegal UTF-8 sequences as a sequence of bytes represented
1291 | * as C-style hex escapes (XXX: Does not actually do this. Some illegal
1292 | * sequences, such as overlong encodings, the sequences reserved for
1293 | * UTF-16 surrogate halves (paired or unpaired), and values outside
1294 | * Unicode (i.e., the old sequences for code points above U+10FFFF)
1295 | * will be decoded in a permissive way. Other illegal sequences,
1296 | * such 0xFE and 0xFF and the presence of a continuation byte where
1297 | * not expected (or vice versa its absence), are replaced with
1298 | * REPLACEMENT CHARACTER.)
1299 | *
1300 | * and return a pointer to it.
1301 | */
1302 | char *
1303 | format_text(wmem_allocator_t *allocator,
1304 | const char *string, size_t len)
1305 | {
1306 | return format_text_internal(allocator, (const uint8_t*)string, len, false0);
1307 | }
1308 |
1309 | /** Given a wmem scope and a null-terminated string, expected to be in
1310 | * UTF-8 but possibly containing invalid sequences (as it may have come
1311 | * from packet data), and the length of the string, generate a valid
1312 | * UTF-8 string from it, allocated in the specified wmem scope, that:
1313 | *
1314 | * shows printable Unicode characters as themselves;
1315 | *
1316 | * shows non-printable ASCII characters as C-style escapes (octal
1317 | * if not one of the standard ones such as LF -> '\n');
1318 | *
1319 | * shows non-printable Unicode-but-not-ASCII characters as
1320 | * their universal character names;
1321 | *
1322 | * shows illegal UTF-8 sequences as a sequence of bytes represented
1323 | * as C-style hex escapes;
1324 | *
1325 | * and return a pointer to it.
1326 | */
1327 | char *
1328 | format_text_string(wmem_allocator_t* allocator, const char *string)
1329 | {
1330 | return format_text_internal(allocator, (const uint8_t*)string, strlen(string), false0);
1331 | }
1332 |
1333 | /*
1334 | * Given a string, generate a string from it that shows non-printable
1335 | * characters as C-style escapes except a whitespace character
1336 | * (space, tab, carriage return, new line, vertical tab, or formfeed)
1337 | * which will be replaced by a space, and return a pointer to it.
1338 | */
1339 | char *
1340 | format_text_wsp(wmem_allocator_t* allocator, const char *string, size_t len)
1341 | {
1342 | return format_text_internal(allocator, (const uint8_t*)string, len, true1);
1343 | }
1344 |
1345 | /*
1346 | * Given a string, generate a string from it that shows non-printable
1347 | * characters as the chr parameter passed, except a whitespace character
1348 | * (space, tab, carriage return, new line, vertical tab, or formfeed)
1349 | * which will be replaced by a space, and return a pointer to it.
1350 | *
1351 | * This does *not* treat the input string as UTF-8.
1352 | *
1353 | * This is useful for displaying binary data that frequently but not always
1354 | * contains text; otherwise the number of C escape codes makes it unreadable.
1355 | */
1356 | char *
1357 | format_text_chr(wmem_allocator_t *allocator, const char *string, size_t len, char chr)
1358 | {
1359 | wmem_strbuf_t *buf;
1360 |
1361 | buf = wmem_strbuf_new_sized(allocator, len + 1);
1362 | for (const char *p = string; p < string + len; p++) {
1363 | if (g_ascii_isprint(*p)((g_ascii_table[(guchar) (*p)] & G_ASCII_PRINT) != 0)) {
1364 | wmem_strbuf_append_c(buf, *p);
1365 | }
1366 | else if (g_ascii_isspace(*p)((g_ascii_table[(guchar) (*p)] & G_ASCII_SPACE) != 0)) {
1367 | wmem_strbuf_append_c(buf, ' ');
1368 | }
1369 | else {
1370 | wmem_strbuf_append_c(buf, chr);
1371 | }
1372 | }
1373 | return wmem_strbuf_finalize(buf);
1374 | }
1375 |
1376 | char *
1377 | format_char(wmem_allocator_t *allocator, char c)
1378 | {
1379 | char *buf;
1380 | char r;
1381 |
1382 | if (g_ascii_isprint(c)((g_ascii_table[(guchar) (c)] & G_ASCII_PRINT) != 0)) {
1383 | 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))))));
1384 | buf[0] = c;
1385 | buf[1] = '\0';
1386 | return buf;
1387 | }
1388 | if (escape_char(c, &r)) {
1389 | 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))))));
1390 | buf[0] = '\\';
1391 | buf[1] = r;
1392 | buf[2] = '\0';
1393 | return buf;
1394 | }
1395 | 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))))));
1396 | buf[0] = '\\';
1397 | buf[1] = 'x';
1398 | buf[2] = hex[((uint8_t)c >> 4) & 0xF];
1399 | buf[3] = hex[((uint8_t)c >> 0) & 0xF];
1400 | buf[4] = '\0';
1401 | return buf;
1402 | }
1403 |
1404 | char*
1405 | ws_utf8_truncate(char *string, size_t len)
1406 | {
1407 | char* last_char;
1408 |
1409 | /* Ensure that it is null terminated */
1410 | string[len] = '\0';
1411 | last_char = g_utf8_find_prev_char(string, string + len);
1412 | if (last_char != NULL((void*)0) && g_utf8_get_char_validated(last_char, -1) == (gunichar)-2) {
1413 | /* The last UTF-8 character was truncated into a partial sequence. */
1414 | *last_char = '\0';
1415 | }
1416 | return string;
1417 | }
1418 |
1419 | /* ASCII/EBCDIC conversion tables from
1420 | * https://web.archive.org/web/20060813174742/http://www.room42.com/store/computer_center/code_tables.shtml
1421 | */
1422 | #if 0
1423 | static const uint8_t ASCII_translate_EBCDIC [ 256 ] = {
1424 | 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
1425 | 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
1426 | 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
1427 | 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
1428 | 0x40, 0x5A, 0x7F, 0x7B, 0x5B, 0x6C, 0x50, 0x7D, 0x4D,
1429 | 0x5D, 0x5C, 0x4E, 0x6B, 0x60, 0x4B, 0x61,
1430 | 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8,
1431 | 0xF9, 0x7A, 0x5E, 0x4C, 0x7E, 0x6E, 0x6F,
1432 | 0x7C, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8,
1433 | 0xC9, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6,
1434 | 0xD7, 0xD8, 0xD9, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7,
1435 | 0xE8, 0xE9, 0xAD, 0xE0, 0xBD, 0x5F, 0x6D,
1436 | 0x7D, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88,
1437 | 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96,
1438 | 0x97, 0x98, 0x99, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7,
1439 | 0xA8, 0xA9, 0xC0, 0x6A, 0xD0, 0xA1, 0x4B,
1440 | 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B,
1441 | 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B,
1442 | 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B,
1443 | 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B,
1444 | 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B,
1445 | 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B,
1446 | 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B,
1447 | 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B,
1448 | 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B,
1449 | 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B,
1450 | 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B,
1451 | 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B,
1452 | 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B,
1453 | 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B,
1454 | 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B,
1455 | 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B
1456 | };
1457 |
1458 | void
1459 | ASCII_to_EBCDIC(uint8_t *buf, unsigned bytes)
1460 | {
1461 | unsigned i;
1462 | uint8_t *bufptr;
1463 |
1464 | bufptr = buf;
1465 |
1466 | for (i = 0; i < bytes; i++, bufptr++) {
1467 | *bufptr = ASCII_translate_EBCDIC[*bufptr];
1468 | }
1469 | }
1470 |
1471 | uint8_t
1472 | ASCII_to_EBCDIC1(uint8_t c)
1473 | {
1474 | return ASCII_translate_EBCDIC[c];
1475 | }
1476 | #endif
1477 |
1478 | static const uint8_t EBCDIC_translate_ASCII [ 256 ] = {
1479 | 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
1480 | 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
1481 | 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
1482 | 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
1483 | 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
1484 | 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
1485 | 0x2E, 0x2E, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
1486 | 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x2E, 0x3F,
1487 | 0x20, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
1488 | 0x2E, 0x2E, 0x2E, 0x2E, 0x3C, 0x28, 0x2B, 0x7C,
1489 | 0x26, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
1490 | 0x2E, 0x2E, 0x21, 0x24, 0x2A, 0x29, 0x3B, 0x5E,
1491 | 0x2D, 0x2F, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
1492 | 0x2E, 0x2E, 0x7C, 0x2C, 0x25, 0x5F, 0x3E, 0x3F,
1493 | 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
1494 | 0x2E, 0x2E, 0x3A, 0x23, 0x40, 0x27, 0x3D, 0x22,
1495 | 0x2E, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
1496 | 0x68, 0x69, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
1497 | 0x2E, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70,
1498 | 0x71, 0x72, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
1499 | 0x2E, 0x7E, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
1500 | 0x79, 0x7A, 0x2E, 0x2E, 0x2E, 0x5B, 0x2E, 0x2E,
1501 | 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
1502 | 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x5D, 0x2E, 0x2E,
1503 | 0x7B, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
1504 | 0x48, 0x49, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
1505 | 0x7D, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50,
1506 | 0x51, 0x52, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
1507 | 0x5C, 0x2E, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
1508 | 0x59, 0x5A, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
1509 | 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
1510 | 0x38, 0x39, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E
1511 | };
1512 |
1513 | void
1514 | EBCDIC_to_ASCII(uint8_t *buf, unsigned bytes)
1515 | {
1516 | unsigned i;
1517 | uint8_t *bufptr;
1518 |
1519 | bufptr = buf;
1520 |
1521 | for (i = 0; i < bytes; i++, bufptr++) {
1522 | *bufptr = EBCDIC_translate_ASCII[*bufptr];
1523 | }
1524 | }
1525 |
1526 | uint8_t
1527 | EBCDIC_to_ASCII1(uint8_t c)
1528 | {
1529 | return EBCDIC_translate_ASCII[c];
1530 | }
1531 |
1532 | /*
1533 | * This routine is based on a routine created by Dan Lasley
1534 | * <DLASLEY@PROMUS.com>.
1535 | *
1536 | * It was modified for Wireshark by Gilbert Ramirez and others.
1537 | */
1538 |
1539 | #define MAX_OFFSET_LEN8 8 /* max length of hex offset of bytes */
1540 | #define BYTES_PER_LINE16 16 /* max byte values printed on a line */
1541 | #define HEX_DUMP_LEN(16*3) (BYTES_PER_LINE16*3)
1542 | /* max number of characters hex dump takes -
1543 | 2 digits plus trailing blank */
1544 | #define DATA_DUMP_LEN((16*3) + 2 + 2 + 16) (HEX_DUMP_LEN(16*3) + 2 + 2 + BYTES_PER_LINE16)
1545 | /* number of characters those bytes take;
1546 | 3 characters per byte of hex dump,
1547 | 2 blanks separating hex from ASCII,
1548 | 2 optional ASCII dump delimiters,
1549 | 1 character per byte of ASCII dump */
1550 | #define MAX_LINE_LEN(8 + 2 + ((16*3) + 2 + 2 + 16)) (MAX_OFFSET_LEN8 + 2 + DATA_DUMP_LEN((16*3) + 2 + 2 + 16))
1551 | /* number of characters per line;
1552 | offset, 2 blanks separating offset
1553 | from data dump, data dump */
1554 |
1555 | bool_Bool
1556 | hex_dump_buffer(bool_Bool (*print_line)(void *, const char *), void *fp,
1557 | const unsigned char *cp, unsigned length,
1558 | hex_dump_enc encoding,
1559 | unsigned ascii_option)
1560 | {
1561 | register unsigned int ad, i, j, k, l;
1562 | unsigned char c;
1563 | char line[MAX_LINE_LEN(8 + 2 + ((16*3) + 2 + 2 + 16)) + 1];
1564 | unsigned int use_digits;
1565 |
1566 | static const char binhex[16] = {
1567 | '0', '1', '2', '3', '4', '5', '6', '7',
1568 | '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
1569 |
1570 | /*
1571 | * How many of the leading digits of the offset will we supply?
1572 | * We always supply at least 4 digits, but if the maximum offset
1573 | * won't fit in 4 digits, we use as many digits as will be needed.
1574 | */
1575 | if (((length - 1) & 0xF0000000) != 0)
1576 | use_digits = 8; /* need all 8 digits */
1577 | else if (((length - 1) & 0x0F000000) != 0)
1578 | use_digits = 7; /* need 7 digits */
1579 | else if (((length - 1) & 0x00F00000) != 0)
1580 | use_digits = 6; /* need 6 digits */
1581 | else if (((length - 1) & 0x000F0000) != 0)
1582 | use_digits = 5; /* need 5 digits */
1583 | else
1584 | use_digits = 4; /* we'll supply 4 digits */
1585 |
1586 | ad = 0;
1587 | i = 0;
1588 | j = 0;
1589 | k = 0;
1590 | while (i < length) {
1591 | if ((i & 15) == 0) {
1592 | /*
1593 | * Start of a new line.
1594 | */
1595 | j = 0;
1596 | l = use_digits;
1597 | do {
1598 | l--;
1599 | c = (ad >> (l*4)) & 0xF;
1600 | line[j++] = binhex[c];
1601 | } while (l != 0);
1602 | line[j++] = ' ';
1603 | line[j++] = ' ';
1604 | memset(line+j, ' ', DATA_DUMP_LEN((16*3) + 2 + 2 + 16));
1605 |
1606 | /*
1607 | * Offset in line of ASCII dump.
1608 | */
1609 | k = j + HEX_DUMP_LEN(16*3) + 2;
1610 | if (ascii_option == HEXDUMP_ASCII_DELIMIT(0x0001U))
1611 | line[k++] = '|';
1612 | }
1613 | c = *cp++;
1614 | line[j++] = binhex[c>>4];
1615 | line[j++] = binhex[c&0xf];
1616 | j++;
1617 | if (ascii_option != HEXDUMP_ASCII_EXCLUDE(0x0002U) ) {
1618 | if (encoding == HEXDUMP_ENC_EBCDIC) {
1619 | c = EBCDIC_to_ASCII1(c);
1620 | }
1621 | line[k++] = ((c >= ' ') && (c < 0x7f)) ? c : '.';
1622 | }
1623 | i++;
1624 | if (((i & 15) == 0) || (i == length)) {
1625 | /*
1626 | * We'll be starting a new line, or
1627 | * we're finished printing this buffer;
1628 | * dump out the line we've constructed,
1629 | * and advance the offset.
1630 | */
1631 | if (ascii_option == HEXDUMP_ASCII_DELIMIT(0x0001U))
1632 | line[k++] = '|';
1633 | line[k] = '\0';
1634 | if (!print_line(fp, line))
1635 | return false0;
1636 | ad += 16;
1637 | }
1638 | }
1639 | return true1;
1640 | }
1641 |
1642 | /*
1643 | * Editor modelines - https://www.wireshark.org/tools/modelines.html
1644 | *
1645 | * Local variables:
1646 | * c-basic-offset: 4
1647 | * tab-width: 8
1648 | * indent-tabs-mode: nil
1649 | * End:
1650 | *
1651 | * vi: set shiftwidth=4 tabstop=8 expandtab:
1652 | * :indentSize=4:tabSize=8:noTabs=true:
1653 | */