Fix: lttng: add-trigger: invalid access past end of exclusions buffer
[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>
12#include <assert.h>
13
14#include "string-utils.h"
15#include "../macros.h"
16
17enum 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 */
27LTTNG_HIDDEN
28void 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
67end:
68 *np = '\0';
69}
70
71static
72enum 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
102end:
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 */
f181fa5a 110LTTNG_HIDDEN
9c55c241
PP
111bool 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 */
f181fa5a 121LTTNG_HIDDEN
9c55c241
PP
122bool 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 */
133LTTNG_HIDDEN
134char *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
170end:
171 return output;
172}
173
174/*
175 * Frees a null-terminated array of strings, including each contained
176 * string.
177 */
178LTTNG_HIDDEN
179void 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 *
e358ddd5
JG
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().
9c55c241
PP
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 *
e358ddd5 242 * Returns -1 if there's an error.
9c55c241
PP
243 */
244LTTNG_HIDDEN
e358ddd5
JG
245int strutils_split(const char *input,
246 char delim,
247 bool escape_delim,
248 struct lttng_dynamic_pointer_array *out_strings)
9c55c241 249{
e358ddd5 250 int ret;
9c55c241
PP
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;
9c55c241
PP
256
257 assert(input);
258 assert(!(escape_delim && delim == '\\'));
259 assert(delim != '\0');
e358ddd5 260 lttng_dynamic_pointer_array_init(out_strings, free);
9c55c241
PP
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
9c55c241
PP
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;
e358ddd5
JG
294 char *substring = zmalloc(longest_substring_len + 1);
295
296 if (!substring) {
297 goto error;
298 }
9c55c241 299
e358ddd5
JG
300 ret = lttng_dynamic_pointer_array_add_pointer(
301 out_strings, substring);
302 if (ret) {
303 free(substring);
9c55c241
PP
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 */
e358ddd5 311 for (ss = s, d = substring; *ss != '\0'; ss++) {
9c55c241
PP
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
e358ddd5 350 ret = 0;
9c55c241
PP
351 goto end;
352
353error:
e358ddd5 354 ret = -1;
9c55c241 355end:
e358ddd5 356 return ret;
9c55c241
PP
357}
358
359LTTNG_HIDDEN
360size_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.051309 seconds and 4 git commands to generate.