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 `*`.
120 bool strutils_is_star_glob_pattern(const char *pattern
)
122 return strutils_test_glob_pattern(pattern
) &
123 STAR_GLOB_PATTERN_TYPE_FLAG_PATTERN
;
127 * Returns true if `pattern` is a globbing pattern with a globbing,
128 * non-escaped star only at its very end.
130 bool strutils_is_star_at_the_end_only_glob_pattern(const char *pattern
)
132 return strutils_test_glob_pattern(pattern
) &
133 STAR_GLOB_PATTERN_TYPE_FLAG_END_ONLY
;
137 * Unescapes the input string `input`, that is, in a `\x` sequence,
138 * removes `\`. If `only_char` is not 0, only this character is
142 char *strutils_unescape_string(const char *input
, char only_char
)
149 output
= zmalloc(strlen(input
) + 1);
154 for (i
= input
, o
= output
; *i
!= '\0'; i
++) {
157 if (only_char
&& i
[1] != only_char
) {
173 /* Copy single character. */
183 * Frees a null-terminated array of strings, including each contained
187 void strutils_free_null_terminated_array_of_strings(char **array
)
195 for (item
= array
; *item
; item
++) {
203 * Splits the input string `input` using the given delimiter `delim`.
205 * The return value is an allocated null-terminated array of the
206 * resulting substrings (also allocated). You can free this array and
207 * its content with strutils_free_null_terminated_array_of_strings(). You
208 * can get the number of substrings in it with
209 * strutils_array_of_strings_len().
211 * Empty substrings are part of the result. For example:
213 * Input: ,hello,,there,
221 * If `escape_delim` is true, then `\,`, where `,` is the delimiter,
222 * escapes the delimiter and is copied as `,` only in the resulting
223 * substring. For example:
225 * Input: hello\,world,zoom,\,hi
231 * Other characters are not escaped (this is the caller's job if
232 * needed). However they are considering during the parsing, that is,
233 * `\x`, where `x` is any character, is copied as is to the resulting
236 * Input: hello\,wo\rld\\,zoom\,
241 * If `escape_delim` is false, nothing at all is escaped, and `delim`,
242 * when found in `input`, is always a delimiter, e.g.:
244 * Input: hello\,world,zoom,\,hi
252 * Returns NULL if there's an error.
255 char **strutils_split(const char *input
, char delim
, bool escape_delim
)
258 size_t number_of_substrings
= 1;
259 size_t longest_substring_len
= 0;
262 char **substrings
= NULL
;
265 assert(!(escape_delim
&& delim
== '\\'));
266 assert(delim
!= '\0');
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. */
282 size_t last_len
= s
- last
- 1;
284 number_of_substrings
++;
286 if (last_len
> longest_substring_len
) {
287 longest_substring_len
= last_len
;
292 if ((s
- last
- 1) > longest_substring_len
) {
293 longest_substring_len
= s
- last
- 1;
296 substrings
= calloc(number_of_substrings
+ 1, sizeof(*substrings
));
301 /* Second pass: actually split and copy substrings. */
302 for (at
= 0, s
= input
; at
< number_of_substrings
; at
++) {
306 substrings
[at
] = zmalloc(longest_substring_len
+ 1);
307 if (!substrings
[at
]) {
312 * Copy characters to substring until we find the next
313 * delimiter or the end of the input string.
315 for (ss
= s
, d
= substrings
[at
]; *ss
!= '\0'; ss
++) {
316 if (escape_delim
&& *ss
== '\\') {
317 if (ss
[1] == delim
) {
319 * '\' followed by delimiter and
320 * we need to escape this ('\'
321 * won't be part of the
322 * resulting substring).
330 * Copy '\' and the following
341 } else if (*ss
== delim
) {
342 /* We're done with this substring. */
350 /* Next substring starts after the last delimiter. */
357 strutils_free_null_terminated_array_of_strings(substrings
);
364 size_t strutils_array_of_strings_len(char * const *array
)
371 for (item
= array
; *item
; item
++) {