Commit | Line | Data |
---|---|---|
9d25673b C |
1 | /* match.c: pattern matching routines */ |
2 | ||
3 | #include "rc.h" | |
4 | ||
5 | static int rangematch(char *, char); | |
6 | ||
7 | enum { RANGE_FAIL = -1, RANGE_ERROR = -2 }; | |
8 | ||
9 | /* match() matches a single pattern against a single string. */ | |
10 | ||
11 | extern bool match(char *p, char *m, char *s) { | |
12 | int i, j; | |
13 | if (m == NULL) | |
14 | return streq(p, s); | |
15 | i = 0; | |
16 | while (1) { | |
17 | if (p[i] == '\0') | |
18 | return *s == '\0'; | |
19 | else if (m[i]) { | |
20 | switch (p[i++]) { | |
21 | case '?': | |
22 | if (*s++ == '\0') | |
23 | return FALSE; | |
24 | break; | |
25 | case '*': | |
26 | while (p[i] == '*' && m[i] == 1) /* collapse multiple stars */ | |
27 | i++; | |
28 | if (p[i] == '\0') /* star at end of pattern? */ | |
29 | return TRUE; | |
30 | while (*s != '\0') | |
31 | if (match(p + i, m + i, s++)) | |
32 | return TRUE; | |
33 | return FALSE; | |
34 | case '[': | |
35 | if (*s == '\0') | |
36 | return FALSE; | |
37 | switch (j = rangematch(p + i, *s)) { | |
38 | default: | |
39 | i += j; | |
40 | break; | |
41 | case RANGE_FAIL: | |
42 | return FALSE; | |
43 | case RANGE_ERROR: | |
44 | if (*s != '[') | |
45 | return FALSE; | |
46 | } | |
47 | s++; | |
48 | break; | |
49 | default: | |
50 | panic("bad metacharacter in match"); | |
51 | /* NOTREACHED */ | |
52 | return FALSE; /* hush up gcc -Wall */ | |
53 | } | |
54 | } else if (p[i++] != *s++) | |
55 | return FALSE; | |
56 | } | |
57 | } | |
58 | ||
59 | /* | |
60 | From the ed(1) man pages (on ranges): | |
61 | ||
62 | The `-' is treated as an ordinary character if it occurs first | |
63 | (or first after an initial ^) or last in the string. | |
64 | ||
65 | The right square bracket does not terminate the enclosed string | |
66 | if it is the first character (after an initial `^', if any), in | |
67 | the bracketed string. | |
68 | ||
69 | rangematch() matches a single character against a class, and returns | |
70 | an integer offset to the end of the range on success, or -1 on | |
71 | failure. | |
72 | */ | |
73 | ||
74 | static int rangematch(char *p, char c) { | |
75 | char *orig = p; | |
76 | bool neg = (*p == '~'); | |
77 | bool matched = FALSE; | |
78 | if (neg) | |
79 | p++; | |
80 | if (*p == ']') { | |
81 | p++; | |
82 | matched = (c == ']'); | |
83 | } | |
84 | for (; *p != ']'; p++) { | |
85 | if (*p == '\0') | |
86 | return RANGE_ERROR; /* bad syntax */ | |
87 | if (p[1] == '-' && p[2] != ']') { /* check for [..-..] but ignore [..-] */ | |
88 | if (c >= *p) | |
89 | matched |= (c <= p[2]); | |
90 | p += 2; | |
91 | } else { | |
92 | matched |= (*p == c); | |
93 | } | |
94 | } | |
95 | if (matched ^ neg) | |
96 | return p - orig + 1; /* skip the right-bracket */ | |
97 | else | |
98 | return RANGE_FAIL; | |
99 | } |