wmem: allow wmem_destroy_list to ignore a NULL list.
[metze/wireshark/wip.git] / 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 #include <config.h>
12
13 #include "str_util.h"
14
15 int
16 ws_xton(char ch)
17 {
18         switch (ch) {
19                 case '0': return 0;
20                 case '1': return 1;
21                 case '2': return 2;
22                 case '3': return 3;
23                 case '4': return 4;
24                 case '5': return 5;
25                 case '6': return 6;
26                 case '7': return 7;
27                 case '8': return 8;
28                 case '9': return 9;
29                 case 'a':  case 'A': return 10;
30                 case 'b':  case 'B': return 11;
31                 case 'c':  case 'C': return 12;
32                 case 'd':  case 'D': return 13;
33                 case 'e':  case 'E': return 14;
34                 case 'f':  case 'F': return 15;
35                 default: return -1;
36         }
37 }
38
39 /* Convert all ASCII letters to lower case, in place. */
40 gchar *
41 ascii_strdown_inplace(gchar *str)
42 {
43         gchar *s;
44
45         for (s = str; *s; s++)
46                 /* What 'g_ascii_tolower (gchar c)' does, this should be slightly more efficient */
47                 *s = g_ascii_isupper (*s) ? *s - 'A' + 'a' : *s;
48
49         return (str);
50 }
51
52 /* Convert all ASCII letters to upper case, in place. */
53 gchar *
54 ascii_strup_inplace(gchar *str)
55 {
56         gchar *s;
57
58         for (s = str; *s; s++)
59                 /* What 'g_ascii_toupper (gchar c)' does, this should be slightly more efficient */
60                 *s = g_ascii_islower (*s) ? *s - 'a' + 'A' : *s;
61
62         return (str);
63 }
64
65 /* Check if an entire string is printable. */
66 gboolean
67 isprint_string(const gchar *str)
68 {
69         guint pos;
70
71         /* Loop until we reach the end of the string (a null) */
72         for(pos = 0; str[pos] != '\0'; pos++){
73                 if(!g_ascii_isprint(str[pos])){
74                         /* The string contains a non-printable character */
75                         return FALSE;
76                 }
77         }
78
79         /* The string contains only printable characters */
80         return TRUE;
81 }
82
83 /* Check if an entire UTF-8 string is printable. */
84 gboolean
85 isprint_utf8_string(const gchar *str, guint length)
86 {
87         const char *c;
88
89         if (!g_utf8_validate (str, length, NULL)) {
90                 return FALSE;
91         }
92
93         for (c = str; *c; c = g_utf8_next_char(c)) {
94                 if (!g_unichar_isprint(g_utf8_get_char(c))) {
95                         return FALSE;
96                 }
97         }
98
99         return TRUE;
100 }
101
102 /* Check if an entire string is digits. */
103 gboolean
104 isdigit_string(const guchar *str)
105 {
106         guint pos;
107
108         /* Loop until we reach the end of the string (a null) */
109         for(pos = 0; str[pos] != '\0'; pos++){
110                 if(!g_ascii_isdigit(str[pos])){
111                         /* The string contains a non-digit character */
112                         return FALSE;
113                 }
114         }
115
116         /* The string contains only digits */
117         return TRUE;
118 }
119
120 #define FORMAT_SIZE_UNIT_MASK 0x00ff
121 #define FORMAT_SIZE_PFX_MASK 0xff00
122
123 static const char *thousands_grouping_fmt = NULL;
124
125 DIAG_OFF(format)
126 static void test_printf_thousands_grouping(void) {
127         /* test whether g_printf works with "'" flag character */
128         gchar *str = g_strdup_printf("%'d", 22);
129         if (g_strcmp0(str, "22") == 0) {
130                 thousands_grouping_fmt = "%'"G_GINT64_MODIFIER"d";
131         } else {
132                 /* Don't use */
133                 thousands_grouping_fmt = "%"G_GINT64_MODIFIER"d";
134         }
135         g_free(str);
136 }
137 DIAG_ON(format)
138
139 /* Given a size, return its value in a human-readable format */
140 /* This doesn't handle fractional values. We might want to make size a double. */
141 gchar *
142 format_size(gint64 size, format_size_flags_e flags)
143 {
144         GString *human_str = g_string_new("");
145         int power = 1000;
146         int pfx_off = 0;
147         gboolean is_small = FALSE;
148         static const gchar *prefix[] = {" T", " G", " M", " k", " Ti", " Gi", " Mi", " Ki"};
149         gchar *ret_val;
150
151         if (thousands_grouping_fmt == NULL)
152                 test_printf_thousands_grouping();
153
154         if ((flags & FORMAT_SIZE_PFX_MASK) == format_size_prefix_iec) {
155                 pfx_off = 4;
156                 power = 1024;
157         }
158
159         if (size / power / power / power / power >= 10) {
160                 g_string_printf(human_str, thousands_grouping_fmt, size / power / power / power / power);
161                 g_string_append(human_str, prefix[pfx_off]);
162         } else if (size / power / power / power >= 10) {
163                 g_string_printf(human_str, thousands_grouping_fmt, size / power / power / power);
164                 g_string_append(human_str, prefix[pfx_off+1]);
165         } else if (size / power / power >= 10) {
166                 g_string_printf(human_str, thousands_grouping_fmt, size / power / power);
167                 g_string_append(human_str, prefix[pfx_off+2]);
168         } else if (size / power >= 10) {
169                 g_string_printf(human_str, thousands_grouping_fmt, size / power);
170                 g_string_append(human_str, prefix[pfx_off+3]);
171         } else {
172                 g_string_printf(human_str, thousands_grouping_fmt, size);
173                 is_small = TRUE;
174         }
175
176
177         switch (flags & FORMAT_SIZE_UNIT_MASK) {
178                 case format_size_unit_none:
179                         break;
180                 case format_size_unit_bytes:
181                         g_string_append(human_str, is_small ? " bytes" : "B");
182                         break;
183                 case format_size_unit_bits:
184                         g_string_append(human_str, is_small ? " bits" : "b");
185                         break;
186                 case format_size_unit_bits_s:
187                         g_string_append(human_str, is_small ? " bits/s" : "bps");
188                         break;
189                 case format_size_unit_bytes_s:
190                         g_string_append(human_str, is_small ? " bytes/s" : "Bps");
191                         break;
192                 case format_size_unit_packets:
193                         g_string_append(human_str, is_small ? " packets" : "packets");
194                         break;
195                 case format_size_unit_packets_s:
196                         g_string_append(human_str, is_small ? " packets/s" : "packets/s");
197                         break;
198                 default:
199                         g_assert_not_reached();
200         }
201
202         ret_val = g_string_free(human_str, FALSE);
203         return g_strchomp(ret_val);
204 }
205
206 gchar
207 printable_char_or_period(gchar c)
208 {
209         return g_ascii_isprint(c) ? c : '.';
210 }
211
212 /*
213  * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
214  *
215  * Local variables:
216  * c-basic-offset: 8
217  * tab-width: 8
218  * indent-tabs-mode: t
219  * End:
220  *
221  * vi: set shiftwidth=8 tabstop=8 noexpandtab:
222  * :indentSize=8:tabSize=8:noTabs=false:
223  */