2 * Copyright (C) 2017 - Philippe Proulx <pproulx@efficios.com>
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License, version 2 only, as
6 * published by the Free Software Foundation.
8 * This program is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * You should have received a copy of the GNU General Public License along with
14 * this program; if not, write to the Free Software Foundation, Inc., 51
15 * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 #include "string-utils.h"
25 #include "../macros.h"
27 enum star_glob_pattern_type_flags
{
28 STAR_GLOB_PATTERN_TYPE_FLAG_NONE
= 0,
29 STAR_GLOB_PATTERN_TYPE_FLAG_PATTERN
= 1,
30 STAR_GLOB_PATTERN_TYPE_FLAG_END_ONLY
= 2,
34 * Normalizes the star-only globbing pattern `pattern`, that is, crushes
35 * consecutive `*` characters into a single `*`, avoiding `\*`.
38 void strutils_normalize_star_glob_pattern(char *pattern
)
42 bool got_star
= false;
46 for (p
= pattern
, np
= pattern
; *p
!= '\0'; p
++) {
50 /* Avoid consecutive stars. */
57 /* Copy backslash character. */
66 /* Fall through default case. */
72 /* Copy single character. */
82 enum star_glob_pattern_type_flags
strutils_test_glob_pattern(const char *pattern
)
84 enum star_glob_pattern_type_flags ret
=
85 STAR_GLOB_PATTERN_TYPE_FLAG_NONE
;
90 for (p
= pattern
; *p
!= '\0'; p
++) {
93 ret
= STAR_GLOB_PATTERN_TYPE_FLAG_PATTERN
;
96 ret
|= STAR_GLOB_PATTERN_TYPE_FLAG_END_ONLY
;
117 * Returns true if `pattern` is a star-only globbing pattern, that is,
118 * it contains at least one non-escaped `*`.
121 bool strutils_is_star_glob_pattern(const char *pattern
)
123 return strutils_test_glob_pattern(pattern
) &
124 STAR_GLOB_PATTERN_TYPE_FLAG_PATTERN
;
128 * Returns true if `pattern` is a globbing pattern with a globbing,
129 * non-escaped star only at its very end.
132 bool strutils_is_star_at_the_end_only_glob_pattern(const char *pattern
)
134 return strutils_test_glob_pattern(pattern
) &
135 STAR_GLOB_PATTERN_TYPE_FLAG_END_ONLY
;
139 * Unescapes the input string `input`, that is, in a `\x` sequence,
140 * removes `\`. If `only_char` is not 0, only this character is
144 char *strutils_unescape_string(const char *input
, char only_char
)
151 output
= zmalloc(strlen(input
) + 1);
156 for (i
= input
, o
= output
; *i
!= '\0'; i
++) {
159 if (only_char
&& i
[1] != only_char
) {
175 /* Copy single character. */
185 * Frees a null-terminated array of strings, including each contained
189 void strutils_free_null_terminated_array_of_strings(char **array
)
197 for (item
= array
; *item
; item
++) {
205 * Splits the input string `input` using the given delimiter `delim`.
207 * The return value is an allocated null-terminated array of the
208 * resulting substrings (also allocated). You can free this array and
209 * its content with strutils_free_null_terminated_array_of_strings(). You
210 * can get the number of substrings in it with
211 * strutils_array_of_strings_len().
213 * Empty substrings are part of the result. For example:
215 * Input: ,hello,,there,
223 * If `escape_delim` is true, then `\,`, where `,` is the delimiter,
224 * escapes the delimiter and is copied as `,` only in the resulting
225 * substring. For example:
227 * Input: hello\,world,zoom,\,hi
233 * Other characters are not escaped (this is the caller's job if
234 * needed). However they are considering during the parsing, that is,
235 * `\x`, where `x` is any character, is copied as is to the resulting
238 * Input: hello\,wo\rld\\,zoom\,
243 * If `escape_delim` is false, nothing at all is escaped, and `delim`,
244 * when found in `input`, is always a delimiter, e.g.:
246 * Input: hello\,world,zoom,\,hi
254 * Returns NULL if there's an error.
257 char **strutils_split(const char *input
, char delim
, bool escape_delim
)
260 size_t number_of_substrings
= 1;
261 size_t longest_substring_len
= 0;
264 char **substrings
= NULL
;
267 assert(!(escape_delim
&& delim
== '\\'));
268 assert(delim
!= '\0');
270 /* First pass: count the number of substrings. */
271 for (s
= input
, last
= input
- 1; *s
!= '\0'; s
++) {
272 if (escape_delim
&& *s
== '\\') {
273 /* Ignore following (escaped) character. */
284 size_t last_len
= s
- last
- 1;
286 number_of_substrings
++;
288 if (last_len
> longest_substring_len
) {
289 longest_substring_len
= last_len
;
294 if ((s
- last
- 1) > longest_substring_len
) {
295 longest_substring_len
= s
- last
- 1;
298 substrings
= calloc(number_of_substrings
+ 1, sizeof(*substrings
));
303 /* Second pass: actually split and copy substrings. */
304 for (at
= 0, s
= input
; at
< number_of_substrings
; at
++) {
308 substrings
[at
] = zmalloc(longest_substring_len
+ 1);
309 if (!substrings
[at
]) {
314 * Copy characters to substring until we find the next
315 * delimiter or the end of the input string.
317 for (ss
= s
, d
= substrings
[at
]; *ss
!= '\0'; ss
++) {
318 if (escape_delim
&& *ss
== '\\') {
319 if (ss
[1] == delim
) {
321 * '\' followed by delimiter and
322 * we need to escape this ('\'
323 * won't be part of the
324 * resulting substring).
332 * Copy '\' and the following
343 } else if (*ss
== delim
) {
344 /* We're done with this substring. */
352 /* Next substring starts after the last delimiter. */
359 strutils_free_null_terminated_array_of_strings(substrings
);
366 size_t strutils_array_of_strings_len(char * const *array
)
373 for (item
= array
; *item
; item
++) {