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

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-07-100348-3595-1 -x c /builds/wireshark/wireshark/wsutil/str_util.c
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
24struct 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
31static 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 */
37static const struct prefix_parameters *
38prefix_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
47char *
48wmem_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
78char *
79wmem_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
118char *
119wmem_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
160char **
161wmem_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 */
218char*
219wmem_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
235int
236ws_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. */
260char *
261ascii_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. */
273char *
274ascii_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. */
286bool_Bool
287isprint_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. */
304bool_Bool
305isprint_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. */
329bool_Bool
330isdigit_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
346const char *
347ws_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. */
366const uint8_t *
367ws_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. */
396char *
397ws_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
426static const char *thousands_grouping_fmt;
427static const char *thousands_grouping_fmt_flt;
428
429DIAG_OFF(format)clang diagnostic push clang diagnostic ignored "-Wformat"
430static 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}
444DIAG_ON(format)clang diagnostic pop
445
446static const char* decimal_point = NULL((void*)0);
447
448static 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 */
484char *
485format_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 */
629char *
630format_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
696char
697printable_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 */
706static inline bool_Bool
707escape_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
736static inline bool_Bool
737escape_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
747static char *
748escape_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
801char *
802ws_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
807char *
808ws_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
813char *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
821char *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
829const char *
830ws_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
843char *
844ws_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
941static char *
942format_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 */
1279char *
1280format_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 */
1304char *
1305format_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 */
1316char *
1317format_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 */
1333char *
1334format_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
1353char *
1354format_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
1381char*
1382ws_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
1400static 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
1435void
1436ASCII_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
1448uint8_t
1449ASCII_to_EBCDIC1(uint8_t c)
1450{
1451 return ASCII_translate_EBCDIC[c];
1452}
1453#endif
1454
1455static 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
1490void
1491EBCDIC_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
1503uint8_t
1504EBCDIC_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
1532bool_Bool
1533hex_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 */