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