Move to kernel style SPDX license identifiers
[lttng-ust.git] / liblttng-ust / string-utils.c
CommitLineData
e85a0294 1/*
c0c0989a 2 * SPDX-License-Identifier: MIT
e85a0294 3 *
c0c0989a 4 * Copyright (C) 2017 Philippe Proulx <pproulx@efficios.com>
e85a0294
PP
5 */
6
7#define _LGPL_SOURCE
8#include <stdlib.h>
9#include <string.h>
10#include <stdbool.h>
11#include <assert.h>
12
13#include "string-utils.h"
14
15enum 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,
19};
20
21static
22enum star_glob_pattern_type_flags strutils_test_glob_pattern(const char *pattern)
23{
24 enum star_glob_pattern_type_flags ret =
25 STAR_GLOB_PATTERN_TYPE_FLAG_NONE;
26 const char *p;
27
28 assert(pattern);
29
30 for (p = pattern; *p != '\0'; p++) {
31 switch (*p) {
32 case '*':
33 ret = STAR_GLOB_PATTERN_TYPE_FLAG_PATTERN;
34
35 if (p[1] == '\0') {
36 ret |= STAR_GLOB_PATTERN_TYPE_FLAG_END_ONLY;
37 }
38
39 goto end;
40 case '\\':
41 p++;
42
43 if (*p == '\0') {
44 goto end;
45 }
46 break;
47 default:
48 break;
49 }
50 }
51
52end:
53 return ret;
54}
55
56/*
57 * Returns true if `pattern` is a star-only globbing pattern, that is,
58 * it contains at least one non-escaped `*`.
59 */
60bool strutils_is_star_glob_pattern(const char *pattern)
61{
62 return strutils_test_glob_pattern(pattern) &
63 STAR_GLOB_PATTERN_TYPE_FLAG_PATTERN;
64}
65
66/*
67 * Returns true if `pattern` is a globbing pattern with a globbing,
68 * non-escaped star only at its very end.
69 */
70bool strutils_is_star_at_the_end_only_glob_pattern(const char *pattern)
71{
72 return strutils_test_glob_pattern(pattern) &
73 STAR_GLOB_PATTERN_TYPE_FLAG_END_ONLY;
74}
75
76static inline
77bool at_end_of_pattern(const char *p, const char *pattern, size_t pattern_len)
78{
79 return (p - pattern) == pattern_len || *p == '\0';
80}
81
82/*
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
86 * `pattern`.
87 *
88 * `pattern_len` or `candidate_len` can be greater than the actual
89 * string length of `pattern` or `candidate` if the string is
90 * null-terminated.
91 */
92bool 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;
96
97retry:
98 c = retry_c;
99 p = retry_p;
100
101 /*
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.
107 *
108 * Example:
109 *
110 * candidate: hi ev every onyx one
d0cba1f1 111 * ^
e85a0294 112 * pattern: hi*every*one
d0cba1f1 113 * ^
e85a0294
PP
114 *
115 * candidate: hi ev every onyx one
d0cba1f1 116 * ^
e85a0294 117 * pattern: hi*every*one
d0cba1f1 118 * ^
e85a0294
PP
119 *
120 * candidate: hi ev every onyx one
d0cba1f1 121 * ^
e85a0294 122 * pattern: hi*every*one
d0cba1f1 123 * ^
e85a0294
PP
124 *
125 * candidate: hi ev every onyx one
d0cba1f1 126 * ^
e85a0294 127 * pattern: hi*every*one
d0cba1f1 128 * ^ MISMATCH
e85a0294
PP
129 *
130 * candidate: hi ev every onyx one
d0cba1f1 131 * ^
e85a0294 132 * pattern: hi*every*one
d0cba1f1 133 * ^
e85a0294
PP
134 *
135 * candidate: hi ev every onyx one
d0cba1f1 136 * ^^
e85a0294 137 * pattern: hi*every*one
d0cba1f1 138 * ^^
e85a0294
PP
139 *
140 * candidate: hi ev every onyx one
d0cba1f1 141 * ^ ^
e85a0294 142 * pattern: hi*every*one
d0cba1f1 143 * ^ ^ MISMATCH
e85a0294
PP
144 *
145 * candidate: hi ev every onyx one
d0cba1f1 146 * ^
e85a0294 147 * pattern: hi*every*one
d0cba1f1 148 * ^ MISMATCH
e85a0294
PP
149 *
150 * candidate: hi ev every onyx one
d0cba1f1 151 * ^
e85a0294 152 * pattern: hi*every*one
d0cba1f1 153 * ^ MISMATCH
e85a0294
PP
154 *
155 * candidate: hi ev every onyx one
d0cba1f1 156 * ^
e85a0294 157 * pattern: hi*every*one
d0cba1f1 158 * ^
e85a0294
PP
159 *
160 * candidate: hi ev every onyx one
d0cba1f1 161 * ^^
e85a0294 162 * pattern: hi*every*one
d0cba1f1 163 * ^^
e85a0294
PP
164 *
165 * candidate: hi ev every onyx one
d0cba1f1 166 * ^ ^
e85a0294 167 * pattern: hi*every*one
d0cba1f1 168 * ^ ^
e85a0294
PP
169 *
170 * candidate: hi ev every onyx one
d0cba1f1 171 * ^ ^
e85a0294 172 * pattern: hi*every*one
d0cba1f1 173 * ^ ^
e85a0294
PP
174 *
175 * candidate: hi ev every onyx one
d0cba1f1 176 * ^ ^
e85a0294 177 * pattern: hi*every*one
d0cba1f1 178 * ^ ^
e85a0294
PP
179 *
180 * candidate: hi ev every onyx one
d0cba1f1 181 * ^
e85a0294 182 * pattern: hi*every*one
d0cba1f1 183 * ^
e85a0294
PP
184 *
185 * candidate: hi ev every onyx one
d0cba1f1 186 * ^
e85a0294 187 * pattern: hi*every*one
d0cba1f1 188 * ^ MISMATCH
e85a0294
PP
189 *
190 * candidate: hi ev every onyx one
d0cba1f1 191 * ^
e85a0294 192 * pattern: hi*every*one
d0cba1f1 193 * ^
e85a0294
PP
194 *
195 * candidate: hi ev every onyx one
d0cba1f1 196 * ^^
e85a0294 197 * pattern: hi*every*one
d0cba1f1 198 * ^^
e85a0294
PP
199 *
200 * candidate: hi ev every onyx one
d0cba1f1 201 * ^ ^
e85a0294 202 * pattern: hi*every*one
d0cba1f1 203 * ^ ^ MISMATCH
e85a0294
PP
204 *
205 * candidate: hi ev every onyx one
d0cba1f1 206 * ^
e85a0294 207 * pattern: hi*every*one
d0cba1f1 208 * ^ MISMATCH
e85a0294
PP
209 *
210 * candidate: hi ev every onyx one
d0cba1f1 211 * ^
e85a0294 212 * pattern: hi*every*one
d0cba1f1 213 * ^ MISMATCH
e85a0294
PP
214 *
215 * candidate: hi ev every onyx one
d0cba1f1 216 * ^
e85a0294 217 * pattern: hi*every*one
d0cba1f1 218 * ^ MISMATCH
e85a0294
PP
219 *
220 * candidate: hi ev every onyx one
d0cba1f1 221 * ^
e85a0294 222 * pattern: hi*every*one
d0cba1f1 223 * ^ MISMATCH
e85a0294
PP
224 *
225 * candidate: hi ev every onyx one
d0cba1f1 226 * ^
e85a0294 227 * pattern: hi*every*one
d0cba1f1 228 * ^
e85a0294
PP
229 *
230 * candidate: hi ev every onyx one
d0cba1f1 231 * ^^
e85a0294 232 * pattern: hi*every*one
d0cba1f1 233 * ^^
e85a0294
PP
234 *
235 * candidate: hi ev every onyx one
d0cba1f1 236 * ^ ^
e85a0294 237 * pattern: hi*every*one
d0cba1f1 238 * ^ ^
e85a0294
PP
239 *
240 * candidate: hi ev every onyx one
d0cba1f1 241 * ^ ^
e85a0294 242 * pattern: hi*every*one
d0cba1f1 243 * ^ ^ SUCCESS
e85a0294
PP
244 */
245 while ((c - candidate) < candidate_len && *c != '\0') {
246 assert(*c);
247
248 if (at_end_of_pattern(p, pattern, pattern_len)) {
249 goto end_of_pattern;
250 }
251
252 switch (*p) {
253 case '*':
254 got_a_star = true;
255
256 /*
257 * Our first try starts at the current candidate
258 * character and after the star in the pattern.
259 */
260 retry_c = c;
261 retry_p = p + 1;
262
263 if (at_end_of_pattern(retry_p, pattern, pattern_len)) {
264 /*
265 * Star at the end of the pattern at
266 * this point: automatic match.
267 */
268 return true;
269 }
270
271 goto retry;
272 case '\\':
273 /* Go to escaped character. */
274 p++;
275
276 /*
277 * Fall through the default case which will
278 * compare the escaped character now.
279 */
280 default:
281 if (at_end_of_pattern(p, pattern, pattern_len) ||
282 *c != *p) {
283end_of_pattern:
284 /* Character mismatch OR end of pattern. */
285 if (!got_a_star) {
286 /*
287 * We didn't get any star yet,
288 * so this first mismatch
289 * automatically makes the whole
290 * test fail.
291 */
292 return false;
293 }
294
295 /*
296 * Next try: next candidate character,
297 * original pattern character (following
298 * the most recent star).
299 */
300 retry_c++;
301 goto retry;
302 }
303 break;
304 }
305
306 /* Next pattern and candidate characters. */
307 c++;
308 p++;
309 }
310
311 /*
312 * We checked every candidate character and we're still in a
313 * success state: the only pattern character allowed to remain
314 * is a star.
315 */
316 if (at_end_of_pattern(p, pattern, pattern_len)) {
317 return true;
318 }
319
320 p++;
321 return p[-1] == '*' && at_end_of_pattern(p, pattern, pattern_len);
322}
This page took 0.038675 seconds and 4 git commands to generate.