2 * Copyright (C) 2017 Philippe Proulx <pproulx@efficios.com>
4 * SPDX-License-Identifier: GPL-2.0-only
14 #include "string-utils.h"
15 #include "../macros.h"
17 enum 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,
24 * Normalizes the star-only globbing pattern `pattern`, that is, crushes
25 * consecutive `*` characters into a single `*`, avoiding `\*`.
28 void strutils_normalize_star_glob_pattern(char *pattern
)
32 bool got_star
= false;
36 for (p
= pattern
, np
= pattern
; *p
!= '\0'; p
++) {
40 /* Avoid consecutive stars. */
47 /* Copy backslash character. */
56 /* Fall through default case. */
62 /* Copy single character. */
72 enum star_glob_pattern_type_flags
strutils_test_glob_pattern(const char *pattern
)
74 enum star_glob_pattern_type_flags ret
=
75 STAR_GLOB_PATTERN_TYPE_FLAG_NONE
;
80 for (p
= pattern
; *p
!= '\0'; p
++) {
83 ret
= STAR_GLOB_PATTERN_TYPE_FLAG_PATTERN
;
86 ret
|= STAR_GLOB_PATTERN_TYPE_FLAG_END_ONLY
;
107 * Returns true if `pattern` is a star-only globbing pattern, that is,
108 * it contains at least one non-escaped `*`.
111 bool strutils_is_star_glob_pattern(const char *pattern
)
113 return strutils_test_glob_pattern(pattern
) &
114 STAR_GLOB_PATTERN_TYPE_FLAG_PATTERN
;
118 * Returns true if `pattern` is a globbing pattern with a globbing,
119 * non-escaped star only at its very end.
122 bool strutils_is_star_at_the_end_only_glob_pattern(const char *pattern
)
124 return strutils_test_glob_pattern(pattern
) &
125 STAR_GLOB_PATTERN_TYPE_FLAG_END_ONLY
;
129 * Unescapes the input string `input`, that is, in a `\x` sequence,
130 * removes `\`. If `only_char` is not 0, only this character is
134 char *strutils_unescape_string(const char *input
, char only_char
)
141 output
= zmalloc(strlen(input
) + 1);
146 for (i
= input
, o
= output
; *i
!= '\0'; i
++) {
149 if (only_char
&& i
[1] != only_char
) {
165 /* Copy single character. */
175 * Frees a null-terminated array of strings, including each contained
179 void strutils_free_null_terminated_array_of_strings(char **array
)
187 for (item
= array
; *item
; item
++) {
195 * Splits the input string `input` using the given delimiter `delim`.
197 * The return value is an allocated null-terminated array of the
198 * resulting substrings (also allocated). You can free this array and
199 * its content with strutils_free_null_terminated_array_of_strings(). You
200 * can get the number of substrings in it with
201 * strutils_array_of_strings_len().
203 * Empty substrings are part of the result. For example:
205 * Input: ,hello,,there,
213 * If `escape_delim` is true, then `\,`, where `,` is the delimiter,
214 * escapes the delimiter and is copied as `,` only in the resulting
215 * substring. For example:
217 * Input: hello\,world,zoom,\,hi
223 * Other characters are not escaped (this is the caller's job if
224 * needed). However they are considering during the parsing, that is,
225 * `\x`, where `x` is any character, is copied as is to the resulting
228 * Input: hello\,wo\rld\\,zoom\,
233 * If `escape_delim` is false, nothing at all is escaped, and `delim`,
234 * when found in `input`, is always a delimiter, e.g.:
236 * Input: hello\,world,zoom,\,hi
244 * Returns NULL if there's an error.
247 char **strutils_split(const char *input
, char delim
, bool escape_delim
)
250 size_t number_of_substrings
= 1;
251 size_t longest_substring_len
= 0;
254 char **substrings
= NULL
;
257 assert(!(escape_delim
&& delim
== '\\'));
258 assert(delim
!= '\0');
260 /* First pass: count the number of substrings. */
261 for (s
= input
, last
= input
- 1; *s
!= '\0'; s
++) {
262 if (escape_delim
&& *s
== '\\') {
263 /* Ignore following (escaped) character. */
274 size_t last_len
= s
- last
- 1;
276 number_of_substrings
++;
278 if (last_len
> longest_substring_len
) {
279 longest_substring_len
= last_len
;
284 if ((s
- last
- 1) > longest_substring_len
) {
285 longest_substring_len
= s
- last
- 1;
288 substrings
= calloc(number_of_substrings
+ 1, sizeof(*substrings
));
293 /* Second pass: actually split and copy substrings. */
294 for (at
= 0, s
= input
; at
< number_of_substrings
; at
++) {
298 substrings
[at
] = zmalloc(longest_substring_len
+ 1);
299 if (!substrings
[at
]) {
304 * Copy characters to substring until we find the next
305 * delimiter or the end of the input string.
307 for (ss
= s
, d
= substrings
[at
]; *ss
!= '\0'; ss
++) {
308 if (escape_delim
&& *ss
== '\\') {
309 if (ss
[1] == delim
) {
311 * '\' followed by delimiter and
312 * we need to escape this ('\'
313 * won't be part of the
314 * resulting substring).
322 * Copy '\' and the following
333 } else if (*ss
== delim
) {
334 /* We're done with this substring. */
342 /* Next substring starts after the last delimiter. */
349 strutils_free_null_terminated_array_of_strings(substrings
);
356 size_t strutils_array_of_strings_len(char * const *array
)
363 for (item
= array
; *item
; item
++) {