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