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