1
+ #include "glob.h"
2
+ #include <stddef.h>
3
+
4
+ // This has been borrowed from the dual MIT/GPL licensed glob module from the Linux kernel under the MIT license.
5
+ // See https://github.com/torvalds/linux/blob/d1bd5fa07667fcc3e38996ec42aef98761f23039/lib/glob.c
6
+
7
+ /**
8
+ * glob_match - Shell-style pattern matching, like !fnmatch(pat, str, 0)
9
+ * @pat: Shell-style pattern to match, e.g. "*.[ch]".
10
+ * @str: String to match. The pattern must match the entire string.
11
+ *
12
+ * Perform shell-style glob matching, returning true (1) if the match
13
+ * succeeds, or false (0) if it fails. Equivalent to !fnmatch(@pat, @str, 0).
14
+ *
15
+ * Pattern metacharacters are ?, *, [ and \.
16
+ * (And, inside character classes, !, - and ].)
17
+ *
18
+ * This is small and simple implementation intended for device blacklists
19
+ * where a string is matched against a number of patterns. Thus, it
20
+ * does not preprocess the patterns. It is non-recursive, and run-time
21
+ * is at most quadratic: strlen(@str)*strlen(@pat).
22
+ *
23
+ * An example of the worst case is glob_match("*aaaaa", "aaaaaaaaaa");
24
+ * it takes 6 passes over the pattern before matching the string.
25
+ *
26
+ * Like !fnmatch(@pat, @str, 0) and unlike the shell, this does NOT
27
+ * treat / or leading . specially; it isn't actually used for pathnames.
28
+ *
29
+ * Note that according to glob(7) (and unlike bash), character classes
30
+ * are complemented by a leading !; this does not support the regex-style
31
+ * [^a-z] syntax.
32
+ *
33
+ * An opening bracket without a matching close is matched literally.
34
+ */
35
+ #ifdef __cplusplus
36
+ extern "C" {
37
+ #endif
38
+ bool glob_match (char const * pat , char const * str ) {
39
+ /*
40
+ * Backtrack to previous * on mismatch and retry starting one
41
+ * character later in the string. Because * matches all characters
42
+ * (no exception for /), it can be easily proved that there's
43
+ * never a need to backtrack multiple levels.
44
+ */
45
+ char const * back_pat = NULL , * back_str = NULL ;
46
+
47
+ /*
48
+ * Loop over each token (character or class) in pat, matching
49
+ * it against the remaining unmatched tail of str. Return false
50
+ * on mismatch, or true after matching the trailing nul bytes.
51
+ */
52
+ for (;;) {
53
+ unsigned char c = * str ++ ;
54
+ unsigned char d = * pat ++ ;
55
+
56
+ switch (d ) {
57
+ case '?' : /* Wildcard: anything but nul */
58
+ if (c == '\0' )
59
+ return false;
60
+ break ;
61
+ case '*' : /* Any-length wildcard */
62
+ if (* pat == '\0' ) /* Optimize trailing * case */
63
+ return true;
64
+ back_pat = pat ;
65
+ back_str = -- str ; /* Allow zero-length match */
66
+ break ;
67
+ case '[' : { /* Character class */
68
+ bool match = false, inverted = (* pat == '!' );
69
+ char const * class = pat + inverted ;
70
+ unsigned char a = * class ++ ;
71
+
72
+ /*
73
+ * Iterate over each span in the character class.
74
+ * A span is either a single character a, or a
75
+ * range a-b. The first span may begin with ']'.
76
+ */
77
+ do {
78
+ unsigned char b = a ;
79
+
80
+ if (a == '\0' ) /* Malformed */
81
+ goto literal ;
82
+
83
+ if (class [0 ] == '-' && class [1 ] != ']' ) {
84
+ b = class [1 ];
85
+
86
+ if (b == '\0' )
87
+ goto literal ;
88
+
89
+ class += 2 ;
90
+ /* Any special action if a > b? */
91
+ }
92
+ match |= (a <= c && c <= b );
93
+ } while ((a = * class ++ ) != ']' );
94
+
95
+ if (match == inverted )
96
+ goto backtrack ;
97
+ pat = class ;
98
+ } break ;
99
+ case '\\' :
100
+ d = * pat ++ ;
101
+ //fallthrough;
102
+ default : /* Literal character */
103
+ literal :
104
+ if (c == d ) {
105
+ if (d == '\0' )
106
+ return true;
107
+ break ;
108
+ }
109
+ backtrack :
110
+ if (c == '\0' || !back_pat )
111
+ return false; /* No point continuing */
112
+ /* Try again from last *, one character later in str. */
113
+ pat = back_pat ;
114
+ str = ++ back_str ;
115
+ break ;
116
+ }
117
+ }
118
+ }
119
+
120
+ #ifdef __cplusplus
121
+ }
122
+ ;
123
+ #endif
0 commit comments