Commit | Line | Data |
---|---|---|
446ee5b1 WD |
1 | /* |
2 | ** Do shell-style pattern matching for ?, \, [], and * characters. | |
3 | ** It is 8bit clean. | |
4 | ** | |
5 | ** Written by Rich $alz, mirror!rs, Wed Nov 26 19:03:17 EST 1986. | |
6 | ** Rich $alz is now <rsalz@bbn.com>. | |
7 | ** | |
7a1f46b6 WD |
8 | ** Modified by Wayne Davison to special-case '/' matching, to make '**' |
9 | ** work differently than '*', and to fix the character-class code. | |
446ee5b1 WD |
10 | */ |
11 | ||
12 | #include "rsync.h" | |
13 | ||
14 | /* What character marks an inverted character class? */ | |
e11c4251 WD |
15 | #define NEGATE_CLASS '!' |
16 | #define NEGATE_CLASS2 '^' | |
446ee5b1 | 17 | |
710faea9 WD |
18 | #define FALSE 0 |
19 | #define TRUE 1 | |
20b2e9ce WD |
20 | #define ABORT_ALL -1 |
21 | #define ABORT_TO_STARSTAR -2 | |
22 | ||
e11c4251 WD |
23 | #define CC_EQ(class, len, litmatch) ((len) == sizeof (litmatch)-1 && strncmp(class, litmatch, len) == 0) |
24 | ||
d5c973cc WD |
25 | #ifdef WILD_TEST_ITERATIONS |
26 | int wildmatch_iteration_count; | |
20b2e9ce | 27 | #endif |
446ee5b1 | 28 | |
e11c4251 | 29 | static int domatch(const unsigned char *p, const unsigned char *text) |
446ee5b1 WD |
30 | { |
31 | int matched, special; | |
e11c4251 | 32 | unsigned char ch, prev; |
446ee5b1 | 33 | |
d5c973cc WD |
34 | #ifdef WILD_TEST_ITERATIONS |
35 | wildmatch_iteration_count++; | |
20b2e9ce WD |
36 | #endif |
37 | ||
446ee5b1 WD |
38 | for ( ; (ch = *p) != '\0'; text++, p++) { |
39 | if (*text == '\0' && ch != '*') | |
710faea9 | 40 | return FALSE; |
446ee5b1 WD |
41 | switch (ch) { |
42 | case '\\': | |
43 | /* Literal match with following character. Note that the test | |
44 | * in "default" handles the p[1] == '\0' failure case. */ | |
45 | ch = *++p; | |
46 | /* FALLTHROUGH */ | |
47 | default: | |
48 | if (*text != ch) | |
710faea9 | 49 | return FALSE; |
446ee5b1 WD |
50 | continue; |
51 | case '?': | |
52 | /* Match anything but '/'. */ | |
53 | if (*text == '/') | |
710faea9 | 54 | return FALSE; |
446ee5b1 WD |
55 | continue; |
56 | case '*': | |
57 | if (*++p == '*') { | |
58 | while (*++p == '*') {} | |
710faea9 | 59 | special = TRUE; |
446ee5b1 WD |
60 | } |
61 | else | |
710faea9 | 62 | special = FALSE; |
446ee5b1 | 63 | if (*p == '\0') { |
710faea9 WD |
64 | /* Trailing "**" matches everything. Trailing "*" matches |
65 | * only if there are no more slash characters. */ | |
c9a59880 | 66 | return special? TRUE : strchr(text, '/') == NULL; |
446ee5b1 WD |
67 | } |
68 | for ( ; *text; text++) { | |
20b2e9ce WD |
69 | if ((matched = domatch(p, text)) != FALSE) { |
70 | if (!special || matched != ABORT_TO_STARSTAR) | |
71 | return matched; | |
72 | } | |
73 | else if (!special && *text == '/') | |
74 | return ABORT_TO_STARSTAR; | |
446ee5b1 | 75 | } |
20b2e9ce | 76 | return ABORT_ALL; |
446ee5b1 | 77 | case '[': |
c9a59880 | 78 | ch = *++p; |
e11c4251 WD |
79 | #ifdef NEGATE_CLASS2 |
80 | if (ch == NEGATE_CLASS2) | |
81 | ch = NEGATE_CLASS; | |
82 | #endif | |
c9a59880 WD |
83 | /* Assign literal TRUE/FALSE because of "matched" comparison. */ |
84 | special = ch == NEGATE_CLASS? TRUE : FALSE; | |
446ee5b1 WD |
85 | if (special) { |
86 | /* Inverted character class. */ | |
c9a59880 | 87 | ch = *++p; |
446ee5b1 WD |
88 | } |
89 | prev = 0; | |
710faea9 | 90 | matched = FALSE; |
c9a59880 | 91 | do { |
446ee5b1 | 92 | if (!ch) |
710faea9 | 93 | return FALSE; |
e11c4251 WD |
94 | if (ch == '\\') { |
95 | ch = *++p; | |
96 | if (!ch) | |
97 | return FALSE; | |
98 | if (*text == ch) | |
99 | matched = TRUE; | |
100 | } | |
101 | else if (ch == '-' && prev && p[1] && p[1] != ']') { | |
102 | ch = *++p; | |
103 | if (ch == '\\') { | |
104 | ch = *++p; | |
105 | if (!ch) | |
106 | return FALSE; | |
107 | } | |
108 | if (*text <= ch && *text >= prev) | |
109 | matched = TRUE; | |
110 | ch = 0; /* This makes "prev" get set to 0. */ | |
111 | } | |
112 | else if (ch == '[' && p[1] == ':') { | |
113 | unsigned const char *s = p += 2; | |
114 | int i; | |
115 | while ((ch = *p) && (ch != ':' || p[1] != ']')) p++; | |
116 | if (!ch) | |
117 | return FALSE; | |
118 | i = p - s; | |
119 | ch = *text; | |
120 | if ((CC_EQ(s,i, "alnum") && isalnum(ch)) | |
121 | || (CC_EQ(s,i, "alpha") && isalpha(ch)) | |
122 | || (CC_EQ(s,i, "blank") && isblank(ch)) | |
123 | || (CC_EQ(s,i, "cntrl") && iscntrl(ch)) | |
124 | || (CC_EQ(s,i, "digit") && isdigit(ch)) | |
125 | || (CC_EQ(s,i, "graph") && isgraph(ch)) | |
126 | || (CC_EQ(s,i, "lower") && islower(ch)) | |
127 | || (CC_EQ(s,i, "print") && isprint(ch)) | |
128 | || (CC_EQ(s,i, "punct") && ispunct(ch)) | |
129 | || (CC_EQ(s,i, "space") && isspace(ch)) | |
130 | || (CC_EQ(s,i, "upper") && isupper(ch)) | |
131 | || (CC_EQ(s,i,"xdigit") && isxdigit(ch))) | |
710faea9 | 132 | matched = TRUE; |
e11c4251 | 133 | p++; |
7a1f46b6 | 134 | ch = 0; /* This makes "prev" get set to 0. */ |
446ee5b1 WD |
135 | } |
136 | else if (*text == ch) | |
710faea9 | 137 | matched = TRUE; |
c9a59880 | 138 | } while (prev = ch, (ch = *++p) != ']'); |
446ee5b1 | 139 | if (matched == special) |
710faea9 | 140 | return FALSE; |
446ee5b1 WD |
141 | continue; |
142 | } | |
143 | } | |
144 | ||
145 | return *text == '\0'; | |
146 | } | |
710faea9 | 147 | |
e11c4251 WD |
148 | /* Find the pattern (p) in the text string (t). */ |
149 | int wildmatch(const char *p, const char *t) | |
710faea9 | 150 | { |
d5c973cc WD |
151 | #ifdef WILD_TEST_ITERATIONS |
152 | wildmatch_iteration_count = 0; | |
20b2e9ce | 153 | #endif |
e11c4251 | 154 | return domatch((const unsigned char*)p, (const unsigned char*)t) == TRUE; |
710faea9 | 155 | } |