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