callstack context: use delimiter when stack is incomplete
[lttng-modules.git] / lttng-string-utils.c
CommitLineData
231b5333
PP
1/*
2 * Copyright (C) 2017 Philippe Proulx <pproulx@efficios.com>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; only
7 * version 2.1 of the License.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18
19#include <linux/types.h>
20
21#include <lttng-string-utils.h>
22
23enum star_glob_pattern_type_flags {
24 STAR_GLOB_PATTERN_TYPE_FLAG_NONE = 0,
25 STAR_GLOB_PATTERN_TYPE_FLAG_PATTERN = (1U << 0),
26 STAR_GLOB_PATTERN_TYPE_FLAG_END_ONLY = (1U << 1),
27};
28
29static
30enum star_glob_pattern_type_flags strutils_test_glob_pattern(const char *pattern)
31{
32 enum star_glob_pattern_type_flags ret =
33 STAR_GLOB_PATTERN_TYPE_FLAG_NONE;
34 const char *p;
35
36 for (p = pattern; *p != '\0'; p++) {
37 switch (*p) {
38 case '*':
39 ret = STAR_GLOB_PATTERN_TYPE_FLAG_PATTERN;
40
41 if (p[1] == '\0') {
42 ret |= STAR_GLOB_PATTERN_TYPE_FLAG_END_ONLY;
43 }
44 goto end;
45 case '\\':
46 p++;
47
48 if (*p == '\0') {
49 goto end;
50 }
51 break;
52 default:
53 break;
54 }
55 }
56
57end:
58 return ret;
59}
60
61/*
62 * Returns true if `pattern` is a star-only globbing pattern, that is,
63 * it contains at least one non-escaped `*`.
64 */
65bool strutils_is_star_glob_pattern(const char *pattern)
66{
67 return strutils_test_glob_pattern(pattern) &
68 STAR_GLOB_PATTERN_TYPE_FLAG_PATTERN;
69}
70
71/*
72 * Returns true if `pattern` is a globbing pattern with a globbing,
73 * non-escaped star only at its very end.
74 */
75bool strutils_is_star_at_the_end_only_glob_pattern(const char *pattern)
76{
77 return strutils_test_glob_pattern(pattern) &
78 STAR_GLOB_PATTERN_TYPE_FLAG_END_ONLY;
79}
80
81struct string_with_len {
82 const char *str;
83 size_t len;
84};
85
86static
87char string_get_char_at_cb(size_t at, void *data)
88{
89 struct string_with_len *string_with_len = data;
90
91 if (at >= string_with_len->len) {
92 return '\0';
93 }
94
95 return string_with_len->str[at];
96}
97
98/*
99 * Globbing matching function with the star feature only (`?` and
100 * character sets are not supported). This matches `candidate` (plain
101 * string) against `pattern`. A literal star can be escaped with `\` in
102 * `pattern`.
103 *
104 * `pattern_len` or `candidate_len` can be greater than the actual
105 * string length of `pattern` or `candidate` if the string is
106 * null-terminated.
107 */
108bool strutils_star_glob_match(const char *pattern, size_t pattern_len,
109 const char *candidate, size_t candidate_len) {
110 struct string_with_len pattern_with_len = {
111 pattern, pattern_len
112 };
113 struct string_with_len candidate_with_len = {
114 candidate, candidate_len
115 };
116
117 return strutils_star_glob_match_char_cb(string_get_char_at_cb,
118 &pattern_with_len, string_get_char_at_cb,
119 &candidate_with_len);
120}
121
122bool strutils_star_glob_match_char_cb(
123 strutils_get_char_at_cb pattern_get_char_at_cb,
124 void *pattern_get_char_at_cb_data,
125 strutils_get_char_at_cb candidate_get_char_at_cb,
126 void *candidate_get_char_at_cb_data)
127{
128 size_t retry_p_at = 0, retry_c_at = 0, c_at, p_at;
129 char c, p, prev_p;
130 bool got_a_star = false;
131
132retry:
133 c_at = retry_c_at;
134 c = candidate_get_char_at_cb(c_at, candidate_get_char_at_cb_data);
135 p_at = retry_p_at;
136 p = pattern_get_char_at_cb(p_at, pattern_get_char_at_cb_data);
137
138 /*
139 * The concept here is to retry a match in the specific case
140 * where we already got a star. The retry position for the
141 * pattern is just after the most recent star, and the retry
142 * position for the candidate is the character following the
143 * last try's first character.
144 *
145 * Example:
146 *
147 * candidate: hi ev every onyx one
e755fa6d 148 * ^
231b5333 149 * pattern: hi*every*one
e755fa6d 150 * ^
231b5333
PP
151 *
152 * candidate: hi ev every onyx one
e755fa6d 153 * ^
231b5333 154 * pattern: hi*every*one
e755fa6d 155 * ^
231b5333
PP
156 *
157 * candidate: hi ev every onyx one
e755fa6d 158 * ^
231b5333 159 * pattern: hi*every*one
e755fa6d 160 * ^
231b5333
PP
161 *
162 * candidate: hi ev every onyx one
e755fa6d 163 * ^
231b5333 164 * pattern: hi*every*one
e755fa6d 165 * ^ MISMATCH
231b5333
PP
166 *
167 * candidate: hi ev every onyx one
e755fa6d 168 * ^
231b5333 169 * pattern: hi*every*one
e755fa6d 170 * ^
231b5333
PP
171 *
172 * candidate: hi ev every onyx one
e755fa6d 173 * ^^
231b5333 174 * pattern: hi*every*one
e755fa6d 175 * ^^
231b5333
PP
176 *
177 * candidate: hi ev every onyx one
e755fa6d 178 * ^ ^
231b5333 179 * pattern: hi*every*one
e755fa6d 180 * ^ ^ MISMATCH
231b5333
PP
181 *
182 * candidate: hi ev every onyx one
e755fa6d 183 * ^
231b5333 184 * pattern: hi*every*one
e755fa6d 185 * ^ MISMATCH
231b5333
PP
186 *
187 * candidate: hi ev every onyx one
e755fa6d 188 * ^
231b5333 189 * pattern: hi*every*one
e755fa6d 190 * ^ MISMATCH
231b5333
PP
191 *
192 * candidate: hi ev every onyx one
e755fa6d 193 * ^
231b5333 194 * pattern: hi*every*one
e755fa6d 195 * ^
231b5333
PP
196 *
197 * candidate: hi ev every onyx one
e755fa6d 198 * ^^
231b5333 199 * pattern: hi*every*one
e755fa6d 200 * ^^
231b5333
PP
201 *
202 * candidate: hi ev every onyx one
e755fa6d 203 * ^ ^
231b5333 204 * pattern: hi*every*one
e755fa6d 205 * ^ ^
231b5333
PP
206 *
207 * candidate: hi ev every onyx one
e755fa6d 208 * ^ ^
231b5333 209 * pattern: hi*every*one
e755fa6d 210 * ^ ^
231b5333
PP
211 *
212 * candidate: hi ev every onyx one
e755fa6d 213 * ^ ^
231b5333 214 * pattern: hi*every*one
e755fa6d 215 * ^ ^
231b5333
PP
216 *
217 * candidate: hi ev every onyx one
e755fa6d 218 * ^
231b5333 219 * pattern: hi*every*one
e755fa6d 220 * ^
231b5333
PP
221 *
222 * candidate: hi ev every onyx one
e755fa6d 223 * ^
231b5333 224 * pattern: hi*every*one
e755fa6d 225 * ^ MISMATCH
231b5333
PP
226 *
227 * candidate: hi ev every onyx one
e755fa6d 228 * ^
231b5333 229 * pattern: hi*every*one
e755fa6d 230 * ^
231b5333
PP
231 *
232 * candidate: hi ev every onyx one
e755fa6d 233 * ^^
231b5333 234 * pattern: hi*every*one
e755fa6d 235 * ^^
231b5333
PP
236 *
237 * candidate: hi ev every onyx one
e755fa6d 238 * ^ ^
231b5333 239 * pattern: hi*every*one
e755fa6d 240 * ^ ^ MISMATCH
231b5333
PP
241 *
242 * candidate: hi ev every onyx one
e755fa6d 243 * ^
231b5333 244 * pattern: hi*every*one
e755fa6d 245 * ^ MISMATCH
231b5333
PP
246 *
247 * candidate: hi ev every onyx one
e755fa6d 248 * ^
231b5333 249 * pattern: hi*every*one
e755fa6d 250 * ^ MISMATCH
231b5333
PP
251 *
252 * candidate: hi ev every onyx one
e755fa6d 253 * ^
231b5333 254 * pattern: hi*every*one
e755fa6d 255 * ^ MISMATCH
231b5333
PP
256 *
257 * candidate: hi ev every onyx one
e755fa6d 258 * ^
231b5333 259 * pattern: hi*every*one
e755fa6d 260 * ^ MISMATCH
231b5333
PP
261 *
262 * candidate: hi ev every onyx one
e755fa6d 263 * ^
231b5333 264 * pattern: hi*every*one
e755fa6d 265 * ^
231b5333
PP
266 *
267 * candidate: hi ev every onyx one
e755fa6d 268 * ^^
231b5333 269 * pattern: hi*every*one
e755fa6d 270 * ^^
231b5333
PP
271 *
272 * candidate: hi ev every onyx one
e755fa6d 273 * ^ ^
231b5333 274 * pattern: hi*every*one
e755fa6d 275 * ^ ^
231b5333
PP
276 *
277 * candidate: hi ev every onyx one
e755fa6d 278 * ^ ^
231b5333 279 * pattern: hi*every*one
e755fa6d 280 * ^ ^ SUCCESS
231b5333
PP
281 */
282 while (c != '\0') {
283 if (p == '\0') {
284 goto end_of_pattern;
285 }
286
287 switch (p) {
288 case '*':
289 {
290 char retry_p;
291 got_a_star = true;
292
293 /*
294 * Our first try starts at the current candidate
295 * character and after the star in the pattern.
296 */
297 retry_c_at = c_at;
298 retry_p_at = p_at + 1;
299 retry_p = pattern_get_char_at_cb(retry_p_at,
300 pattern_get_char_at_cb_data);
301
302 if (retry_p == '\0') {
303 /*
304 * Star at the end of the pattern at
305 * this point: automatic match.
306 */
307 return true;
308 }
309
310 goto retry;
311 }
312 case '\\':
313 /* Go to escaped character. */
314 p_at++;
315 p = pattern_get_char_at_cb(p_at,
316 pattern_get_char_at_cb_data);
317
318 /*
319 * Fall through the default case which will
320 * compare the escaped character now.
321 */
322 default:
323 if (p == '\0' || c != p) {
324end_of_pattern:
325 /* Character mismatch OR end of pattern. */
326 if (!got_a_star) {
327 /*
328 * We didn't get any star yet,
329 * so this first mismatch
330 * automatically makes the whole
331 * test fail.
332 */
333 return false;
334 }
335
336 /*
337 * Next try: next candidate character,
338 * original pattern character (following
339 * the most recent star).
340 */
341 retry_c_at++;
342 goto retry;
343 }
344 break;
345 }
346
347 /* Next pattern and candidate characters. */
348 c_at++;
349 c = candidate_get_char_at_cb(c_at,
350 candidate_get_char_at_cb_data);
351 p_at++;
352 p = pattern_get_char_at_cb(p_at, pattern_get_char_at_cb_data);
353 }
354
355 /*
356 * We checked every candidate character and we're still in a
357 * success state: the only pattern character allowed to remain
358 * is a star.
359 */
360 if (p == '\0') {
361 return true;
362 }
363
364 prev_p = p;
365 p_at++;
366 p = pattern_get_char_at_cb(p_at, pattern_get_char_at_cb_data);
367 return prev_p == '*' && p == '\0';
368}
This page took 0.037532 seconds and 4 git commands to generate.