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

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-08-24-100314-3660-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 <[email protected]>
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
829bool_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
852const char *
853ws_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
866char *
867ws_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
964static char *
965format_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 */
1302char *
1303format_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 */
1327char *
1328format_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 */
1339char *
1340format_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 */
1356char *
1357format_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
1376char *
1377format_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
1404char*
1405ws_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
1423static 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
1458void
1459ASCII_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
1471uint8_t
1472ASCII_to_EBCDIC1(uint8_t c)
1473{
1474 return ASCII_translate_EBCDIC[c];
1475}
1476#endif
1477
1478static 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
1513void
1514EBCDIC_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
1526uint8_t
1527EBCDIC_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 * <[email protected]>.
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
1555bool_Bool
1556hex_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 */