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