2 * SPDX-License-Identifier: MIT
4 * Copyright (C) 2017 Philippe Proulx <pproulx@efficios.com>
13 #include "common/strutils.h"
15 enum star_glob_pattern_type_flags
{
16 STAR_GLOB_PATTERN_TYPE_FLAG_NONE
= 0,
17 STAR_GLOB_PATTERN_TYPE_FLAG_PATTERN
= 1,
18 STAR_GLOB_PATTERN_TYPE_FLAG_END_ONLY
= 2,
22 enum star_glob_pattern_type_flags
strutils_test_glob_pattern(const char *pattern
)
24 enum star_glob_pattern_type_flags ret
=
25 STAR_GLOB_PATTERN_TYPE_FLAG_NONE
;
30 for (p
= pattern
; *p
!= '\0'; p
++) {
33 ret
= STAR_GLOB_PATTERN_TYPE_FLAG_PATTERN
;
36 ret
|= STAR_GLOB_PATTERN_TYPE_FLAG_END_ONLY
;
57 * Returns true if `pattern` is a star-only globbing pattern, that is,
58 * it contains at least one non-escaped `*`.
60 bool strutils_is_star_glob_pattern(const char *pattern
)
62 return strutils_test_glob_pattern(pattern
) &
63 STAR_GLOB_PATTERN_TYPE_FLAG_PATTERN
;
67 * Returns true if `pattern` is a globbing pattern with a globbing,
68 * non-escaped star only at its very end.
70 bool strutils_is_star_at_the_end_only_glob_pattern(const char *pattern
)
72 return strutils_test_glob_pattern(pattern
) &
73 STAR_GLOB_PATTERN_TYPE_FLAG_END_ONLY
;
77 bool at_end_of_pattern(const char *p
, const char *pattern
, size_t pattern_len
)
79 return (p
- pattern
) == pattern_len
|| *p
== '\0';
83 * Globbing matching function with the star feature only (`?` and
84 * character sets are not supported). This matches `candidate` (plain
85 * string) against `pattern`. A literal star can be escaped with `\` in
88 * `pattern_len` or `candidate_len` can be greater than the actual
89 * string length of `pattern` or `candidate` if the string is
92 bool strutils_star_glob_match(const char *pattern
, size_t pattern_len
,
93 const char *candidate
, size_t candidate_len
) {
94 const char *retry_c
= candidate
, *retry_p
= pattern
, *c
, *p
;
95 bool got_a_star
= false;
102 * The concept here is to retry a match in the specific case
103 * where we already got a star. The retry position for the
104 * pattern is just after the most recent star, and the retry
105 * position for the candidate is the character following the
106 * last try's first character.
110 * candidate: hi ev every onyx one
112 * pattern: hi*every*one
115 * candidate: hi ev every onyx one
117 * pattern: hi*every*one
120 * candidate: hi ev every onyx one
122 * pattern: hi*every*one
125 * candidate: hi ev every onyx one
127 * pattern: hi*every*one
130 * candidate: hi ev every onyx one
132 * pattern: hi*every*one
135 * candidate: hi ev every onyx one
137 * pattern: hi*every*one
140 * candidate: hi ev every onyx one
142 * pattern: hi*every*one
145 * candidate: hi ev every onyx one
147 * pattern: hi*every*one
150 * candidate: hi ev every onyx one
152 * pattern: hi*every*one
155 * candidate: hi ev every onyx one
157 * pattern: hi*every*one
160 * candidate: hi ev every onyx one
162 * pattern: hi*every*one
165 * candidate: hi ev every onyx one
167 * pattern: hi*every*one
170 * candidate: hi ev every onyx one
172 * pattern: hi*every*one
175 * candidate: hi ev every onyx one
177 * pattern: hi*every*one
180 * candidate: hi ev every onyx one
182 * pattern: hi*every*one
185 * candidate: hi ev every onyx one
187 * pattern: hi*every*one
190 * candidate: hi ev every onyx one
192 * pattern: hi*every*one
195 * candidate: hi ev every onyx one
197 * pattern: hi*every*one
200 * candidate: hi ev every onyx one
202 * pattern: hi*every*one
205 * candidate: hi ev every onyx one
207 * pattern: hi*every*one
210 * candidate: hi ev every onyx one
212 * pattern: hi*every*one
215 * candidate: hi ev every onyx one
217 * pattern: hi*every*one
220 * candidate: hi ev every onyx one
222 * pattern: hi*every*one
225 * candidate: hi ev every onyx one
227 * pattern: hi*every*one
230 * candidate: hi ev every onyx one
232 * pattern: hi*every*one
235 * candidate: hi ev every onyx one
237 * pattern: hi*every*one
240 * candidate: hi ev every onyx one
242 * pattern: hi*every*one
245 while ((c
- candidate
) < candidate_len
&& *c
!= '\0') {
248 if (at_end_of_pattern(p
, pattern
, pattern_len
)) {
257 * Our first try starts at the current candidate
258 * character and after the star in the pattern.
263 if (at_end_of_pattern(retry_p
, pattern
, pattern_len
)) {
265 * Star at the end of the pattern at
266 * this point: automatic match.
273 /* Go to escaped character. */
274 p
++; /* Fallthrough */
277 * Fall through the default case which will
278 * compare the escaped character now.
281 if (at_end_of_pattern(p
, pattern
, pattern_len
) ||
284 /* Character mismatch OR end of pattern. */
287 * We didn't get any star yet,
288 * so this first mismatch
289 * automatically makes the whole
296 * Next try: next candidate character,
297 * original pattern character (following
298 * the most recent star).
306 /* Next pattern and candidate characters. */
312 * We checked every candidate character and we're still in a
313 * success state: the only pattern character allowed to remain
316 if (at_end_of_pattern(p
, pattern
, pattern_len
)) {
321 return p
[-1] == '*' && at_end_of_pattern(p
, pattern
, pattern_len
);