2 * Copyright (C) 2017 Philippe Proulx <pproulx@efficios.com>
4 * SPDX-License-Identifier: LGPL-2.1-only
9 #include "../macros.hpp"
10 #include "string-utils.hpp"
19 #include <type_traits>
21 enum star_glob_pattern_type_flags
{
22 STAR_GLOB_PATTERN_TYPE_FLAG_NONE
= 0,
23 STAR_GLOB_PATTERN_TYPE_FLAG_PATTERN
= 1,
24 STAR_GLOB_PATTERN_TYPE_FLAG_END_ONLY
= 2,
27 static star_glob_pattern_type_flags
& operator|=(star_glob_pattern_type_flags
& l
,
28 star_glob_pattern_type_flags r
)
30 using T
= std::underlying_type
<star_glob_pattern_type_flags
>::type
;
31 l
= static_cast<star_glob_pattern_type_flags
>(static_cast<T
>(l
) | static_cast<T
>(r
));
36 * Normalizes the star-only globbing pattern `pattern`, that is, crushes
37 * consecutive `*` characters into a single `*`, avoiding `\*`.
39 void strutils_normalize_star_glob_pattern(char *pattern
)
43 bool got_star
= false;
45 LTTNG_ASSERT(pattern
);
47 for (p
= pattern
, np
= pattern
; *p
!= '\0'; p
++) {
51 /* Avoid consecutive stars. */
58 /* Copy backslash character. */
73 /* Copy single character. */
82 static enum star_glob_pattern_type_flags
strutils_test_glob_pattern(const char *pattern
)
84 enum star_glob_pattern_type_flags ret
= STAR_GLOB_PATTERN_TYPE_FLAG_NONE
;
87 LTTNG_ASSERT(pattern
);
89 for (p
= pattern
; *p
!= '\0'; p
++) {
92 ret
= STAR_GLOB_PATTERN_TYPE_FLAG_PATTERN
;
95 ret
|= STAR_GLOB_PATTERN_TYPE_FLAG_END_ONLY
;
116 * Returns true if `pattern` is a star-only globbing pattern, that is,
117 * it contains at least one non-escaped `*`.
119 bool strutils_is_star_glob_pattern(const char *pattern
)
121 return strutils_test_glob_pattern(pattern
) & STAR_GLOB_PATTERN_TYPE_FLAG_PATTERN
;
125 * Returns true if `pattern` is a globbing pattern with a globbing,
126 * non-escaped star only at its very end.
128 bool strutils_is_star_at_the_end_only_glob_pattern(const char *pattern
)
130 return strutils_test_glob_pattern(pattern
) & STAR_GLOB_PATTERN_TYPE_FLAG_END_ONLY
;
134 * Unescapes the input string `input`, that is, in a `\x` sequence,
135 * removes `\`. If `only_char` is not 0, only this character is
138 char *strutils_unescape_string(const char *input
, char only_char
)
145 output
= calloc
<char>(strlen(input
) + 1);
150 for (i
= input
, o
= output
; *i
!= '\0'; i
++) {
153 if (only_char
&& i
[1] != only_char
) {
169 /* Copy single character. */
179 * Frees a null-terminated array of strings, including each contained
182 void strutils_free_null_terminated_array_of_strings(char **array
)
190 for (item
= array
; *item
; item
++) {
198 * Splits the input string `input` using the given delimiter `delim`.
200 * The return value is a dynamic pointer array that is assumed to be empty. The
201 * array must be discarded by the caller by invoking
202 * lttng_dynamic_pointer_array_reset().
204 * Empty substrings are part of the result. For example:
206 * Input: ,hello,,there,
214 * If `escape_delim` is true, then `\,`, where `,` is the delimiter,
215 * escapes the delimiter and is copied as `,` only in the resulting
216 * substring. For example:
218 * Input: hello\,world,zoom,\,hi
224 * Other characters are not escaped (this is the caller's job if
225 * needed). However they are considering during the parsing, that is,
226 * `\x`, where `x` is any character, is copied as is to the resulting
229 * Input: hello\,wo\rld\\,zoom\,
234 * If `escape_delim` is false, nothing at all is escaped, and `delim`,
235 * when found in `input`, is always a delimiter, e.g.:
237 * Input: hello\,world,zoom,\,hi
245 * Returns -1 if there's an error.
247 int strutils_split(const char *input
,
250 struct lttng_dynamic_pointer_array
*out_strings
)
254 size_t number_of_substrings
= 1;
255 size_t longest_substring_len
= 0;
260 LTTNG_ASSERT(!(escape_delim
&& delim
== '\\'));
261 LTTNG_ASSERT(delim
!= '\0');
262 lttng_dynamic_pointer_array_init(out_strings
, free
);
264 /* First pass: count the number of substrings. */
265 for (s
= input
, last
= input
- 1; *s
!= '\0'; s
++) {
266 if (escape_delim
&& *s
== '\\') {
267 /* Ignore following (escaped) character. */
278 size_t last_len
= s
- last
- 1;
280 number_of_substrings
++;
282 if (last_len
> longest_substring_len
) {
283 longest_substring_len
= last_len
;
288 if ((s
- last
- 1) > longest_substring_len
) {
289 longest_substring_len
= s
- last
- 1;
292 /* Second pass: actually split and copy substrings. */
293 for (at
= 0, s
= input
; at
< number_of_substrings
; at
++) {
296 char *substring
= calloc
<char>(longest_substring_len
+ 1);
302 ret
= lttng_dynamic_pointer_array_add_pointer(out_strings
, substring
);
309 * Copy characters to substring until we find the next
310 * delimiter or the end of the input string.
312 for (ss
= s
, d
= substring
; *ss
!= '\0'; ss
++) {
313 if (escape_delim
&& *ss
== '\\') {
314 if (ss
[1] == delim
) {
316 * '\' followed by delimiter and
317 * we need to escape this ('\'
318 * won't be part of the
319 * resulting substring).
327 * Copy '\' and the following
338 } else if (*ss
== delim
) {
339 /* We're done with this substring. */
347 /* Next substring starts after the last delimiter. */
360 size_t strutils_array_of_strings_len(char *const *array
)
367 for (item
= array
; *item
; item
++) {
374 int strutils_append_str(char **s
, const char *append
)
378 size_t oldlen
= (old
== nullptr) ? 0 : strlen(old
);
379 size_t appendlen
= strlen(append
);
381 new_str
= zmalloc
<char>(oldlen
+ appendlen
+ 1);
386 strcpy(new_str
, old
);
388 strcat(new_str
, append
);
394 int strutils_appendf(char **s
, const char *fmt
, ...)
397 size_t oldlen
= (*s
) ? strlen(*s
) : 0;
402 /* Compute length of formatted string we append. */
404 ret
= vsnprintf(nullptr, 0, fmt
, args
);
411 /* Allocate space for old string + new string + \0. */
413 new_str
= zmalloc
<char>(oldlen
+ addlen
);
419 /* Copy old string, if there was one. */
424 /* Format new string in-place. */
426 ret
= vsnprintf(&new_str
[oldlen
], addlen
, fmt
, args
);