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