Fix: tests: `-Wstringop-overflow` warning
[lttng-tools.git] / src / common / string-utils / string-utils.c
1 /*
2 * Copyright (C) 2017 - Philippe Proulx <pproulx@efficios.com>
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License, version 2 only, as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11 * more details.
12 *
13 * You should have received a copy of the GNU General Public License along with
14 * this program; if not, write to the Free Software Foundation, Inc., 51
15 * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
16 */
17
18 #define _LGPL_SOURCE
19 #include <stdlib.h>
20 #include <string.h>
21 #include <stdbool.h>
22 #include <assert.h>
23
24 #include "string-utils.h"
25 #include "../macros.h"
26
27 enum star_glob_pattern_type_flags {
28 STAR_GLOB_PATTERN_TYPE_FLAG_NONE = 0,
29 STAR_GLOB_PATTERN_TYPE_FLAG_PATTERN = 1,
30 STAR_GLOB_PATTERN_TYPE_FLAG_END_ONLY = 2,
31 };
32
33 /*
34 * Normalizes the star-only globbing pattern `pattern`, that is, crushes
35 * consecutive `*` characters into a single `*`, avoiding `\*`.
36 */
37 LTTNG_HIDDEN
38 void strutils_normalize_star_glob_pattern(char *pattern)
39 {
40 const char *p;
41 char *np;
42 bool got_star = false;
43
44 assert(pattern);
45
46 for (p = pattern, np = pattern; *p != '\0'; p++) {
47 switch (*p) {
48 case '*':
49 if (got_star) {
50 /* Avoid consecutive stars. */
51 continue;
52 }
53
54 got_star = true;
55 break;
56 case '\\':
57 /* Copy backslash character. */
58 *np = *p;
59 np++;
60 p++;
61
62 if (*p == '\0') {
63 goto end;
64 }
65
66 /* Fall through default case. */
67 default:
68 got_star = false;
69 break;
70 }
71
72 /* Copy single character. */
73 *np = *p;
74 np++;
75 }
76
77 end:
78 *np = '\0';
79 }
80
81 static
82 enum star_glob_pattern_type_flags strutils_test_glob_pattern(const char *pattern)
83 {
84 enum star_glob_pattern_type_flags ret =
85 STAR_GLOB_PATTERN_TYPE_FLAG_NONE;
86 const char *p;
87
88 assert(pattern);
89
90 for (p = pattern; *p != '\0'; p++) {
91 switch (*p) {
92 case '*':
93 ret = STAR_GLOB_PATTERN_TYPE_FLAG_PATTERN;
94
95 if (p[1] == '\0') {
96 ret |= STAR_GLOB_PATTERN_TYPE_FLAG_END_ONLY;
97 }
98
99 goto end;
100 case '\\':
101 p++;
102
103 if (*p == '\0') {
104 goto end;
105 }
106 break;
107 default:
108 break;
109 }
110 }
111
112 end:
113 return ret;
114 }
115
116 /*
117 * Returns true if `pattern` is a star-only globbing pattern, that is,
118 * it contains at least one non-escaped `*`.
119 */
120 LTTNG_HIDDEN
121 bool strutils_is_star_glob_pattern(const char *pattern)
122 {
123 return strutils_test_glob_pattern(pattern) &
124 STAR_GLOB_PATTERN_TYPE_FLAG_PATTERN;
125 }
126
127 /*
128 * Returns true if `pattern` is a globbing pattern with a globbing,
129 * non-escaped star only at its very end.
130 */
131 LTTNG_HIDDEN
132 bool strutils_is_star_at_the_end_only_glob_pattern(const char *pattern)
133 {
134 return strutils_test_glob_pattern(pattern) &
135 STAR_GLOB_PATTERN_TYPE_FLAG_END_ONLY;
136 }
137
138 /*
139 * Unescapes the input string `input`, that is, in a `\x` sequence,
140 * removes `\`. If `only_char` is not 0, only this character is
141 * escaped.
142 */
143 LTTNG_HIDDEN
144 char *strutils_unescape_string(const char *input, char only_char)
145 {
146 char *output;
147 char *o;
148 const char *i;
149
150 assert(input);
151 output = zmalloc(strlen(input) + 1);
152 if (!output) {
153 goto end;
154 }
155
156 for (i = input, o = output; *i != '\0'; i++) {
157 switch (*i) {
158 case '\\':
159 if (only_char && i[1] != only_char) {
160 break;
161 }
162
163 i++;
164
165 if (*i == '\0') {
166 /* Copy last `\`. */
167 *o = '\\';
168 o++;
169 goto end;
170 }
171 default:
172 break;
173 }
174
175 /* Copy single character. */
176 *o = *i;
177 o++;
178 }
179
180 end:
181 return output;
182 }
183
184 /*
185 * Frees a null-terminated array of strings, including each contained
186 * string.
187 */
188 LTTNG_HIDDEN
189 void strutils_free_null_terminated_array_of_strings(char **array)
190 {
191 char **item;
192
193 if (!array) {
194 return;
195 }
196
197 for (item = array; *item; item++) {
198 free(*item);
199 }
200
201 free(array);
202 }
203
204 /*
205 * Splits the input string `input` using the given delimiter `delim`.
206 *
207 * The return value is an allocated null-terminated array of the
208 * resulting substrings (also allocated). You can free this array and
209 * its content with strutils_free_null_terminated_array_of_strings(). You
210 * can get the number of substrings in it with
211 * strutils_array_of_strings_len().
212 *
213 * Empty substrings are part of the result. For example:
214 *
215 * Input: ,hello,,there,
216 * Result:
217 * ``
218 * `hello`
219 * ``
220 * `there`
221 * ``
222 *
223 * If `escape_delim` is true, then `\,`, where `,` is the delimiter,
224 * escapes the delimiter and is copied as `,` only in the resulting
225 * substring. For example:
226 *
227 * Input: hello\,world,zoom,\,hi
228 * Result:
229 * `hello,world`
230 * `zoom`
231 * `,hi`
232 *
233 * Other characters are not escaped (this is the caller's job if
234 * needed). However they are considering during the parsing, that is,
235 * `\x`, where `x` is any character, is copied as is to the resulting
236 * substring, e.g.:
237 *
238 * Input: hello\,wo\rld\\,zoom\,
239 * Result:
240 * `hello,wo\rld\\`
241 * `zoom,`
242 *
243 * If `escape_delim` is false, nothing at all is escaped, and `delim`,
244 * when found in `input`, is always a delimiter, e.g.:
245 *
246 * Input: hello\,world,zoom,\,hi
247 * Result:
248 * `hello\`
249 * `world`
250 * `zoom`
251 * `\`
252 * `hi`
253 *
254 * Returns NULL if there's an error.
255 */
256 LTTNG_HIDDEN
257 char **strutils_split(const char *input, char delim, bool escape_delim)
258 {
259 size_t at;
260 size_t number_of_substrings = 1;
261 size_t longest_substring_len = 0;
262 const char *s;
263 const char *last;
264 char **substrings = NULL;
265
266 assert(input);
267 assert(!(escape_delim && delim == '\\'));
268 assert(delim != '\0');
269
270 /* First pass: count the number of substrings. */
271 for (s = input, last = input - 1; *s != '\0'; s++) {
272 if (escape_delim && *s == '\\') {
273 /* Ignore following (escaped) character. */
274 s++;
275
276 if (*s == '\0') {
277 break;
278 }
279
280 continue;
281 }
282
283 if (*s == delim) {
284 size_t last_len = s - last - 1;
285 last = s;
286 number_of_substrings++;
287
288 if (last_len > longest_substring_len) {
289 longest_substring_len = last_len;
290 }
291 }
292 }
293
294 if ((s - last - 1) > longest_substring_len) {
295 longest_substring_len = s - last - 1;
296 }
297
298 substrings = calloc(number_of_substrings + 1, sizeof(*substrings));
299 if (!substrings) {
300 goto error;
301 }
302
303 /* Second pass: actually split and copy substrings. */
304 for (at = 0, s = input; at < number_of_substrings; at++) {
305 const char *ss;
306 char *d;
307
308 substrings[at] = zmalloc(longest_substring_len + 1);
309 if (!substrings[at]) {
310 goto error;
311 }
312
313 /*
314 * Copy characters to substring until we find the next
315 * delimiter or the end of the input string.
316 */
317 for (ss = s, d = substrings[at]; *ss != '\0'; ss++) {
318 if (escape_delim && *ss == '\\') {
319 if (ss[1] == delim) {
320 /*
321 * '\' followed by delimiter and
322 * we need to escape this ('\'
323 * won't be part of the
324 * resulting substring).
325 */
326 ss++;
327 *d = *ss;
328 d++;
329 continue;
330 } else {
331 /*
332 * Copy '\' and the following
333 * character.
334 */
335 *d = *ss;
336 d++;
337 ss++;
338
339 if (*ss == '\0') {
340 break;
341 }
342 }
343 } else if (*ss == delim) {
344 /* We're done with this substring. */
345 break;
346 }
347
348 *d = *ss;
349 d++;
350 }
351
352 /* Next substring starts after the last delimiter. */
353 s = ss + 1;
354 }
355
356 goto end;
357
358 error:
359 strutils_free_null_terminated_array_of_strings(substrings);
360 substrings = NULL;
361 end:
362 return substrings;
363 }
364
365 LTTNG_HIDDEN
366 size_t strutils_array_of_strings_len(char * const *array)
367 {
368 char * const *item;
369 size_t count = 0;
370
371 assert(array);
372
373 for (item = array; *item; item++) {
374 count++;
375 }
376
377 return count;
378 }
This page took 0.071667 seconds and 4 git commands to generate.