Annotation of embedaddon/rsync/lib/wildmatch.c, revision 1.1.1.2
1.1 misho 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: **
8: ** Modified by Wayne Davison to special-case '/' matching, to make '**'
9: ** work differently than '*', and to fix the character-class code.
10: */
11:
12: #include "rsync.h"
13:
14: /* What character marks an inverted character class? */
15: #define NEGATE_CLASS '!'
16: #define NEGATE_CLASS2 '^'
17:
18: #define FALSE 0
19: #define TRUE 1
20: #define ABORT_ALL -1
21: #define ABORT_TO_STARSTAR -2
22:
23: #define CC_EQ(class, len, litmatch) ((len) == sizeof (litmatch)-1 \
24: && *(class) == *(litmatch) \
25: && strncmp((char*)class, litmatch, len) == 0)
26:
27: #if defined STDC_HEADERS || !defined isascii
28: # define ISASCII(c) 1
29: #else
30: # define ISASCII(c) isascii(c)
31: #endif
32:
33: #ifdef isblank
34: # define ISBLANK(c) (ISASCII(c) && isblank(c))
35: #else
36: # define ISBLANK(c) ((c) == ' ' || (c) == '\t')
37: #endif
38:
39: #ifdef isgraph
40: # define ISGRAPH(c) (ISASCII(c) && isgraph(c))
41: #else
42: # define ISGRAPH(c) (ISASCII(c) && isprint(c) && !isspace(c))
43: #endif
44:
45: #define ISPRINT(c) (ISASCII(c) && isprint(c))
46: #define ISDIGIT(c) (ISASCII(c) && isdigit(c))
47: #define ISALNUM(c) (ISASCII(c) && isalnum(c))
48: #define ISALPHA(c) (ISASCII(c) && isalpha(c))
49: #define ISCNTRL(c) (ISASCII(c) && iscntrl(c))
50: #define ISLOWER(c) (ISASCII(c) && islower(c))
51: #define ISPUNCT(c) (ISASCII(c) && ispunct(c))
52: #define ISSPACE(c) (ISASCII(c) && isspace(c))
53: #define ISUPPER(c) (ISASCII(c) && isupper(c))
54: #define ISXDIGIT(c) (ISASCII(c) && isxdigit(c))
55:
1.1.1.2 ! misho 56: extern int ignore_case;
! 57:
1.1 misho 58: #ifdef WILD_TEST_ITERATIONS
59: int wildmatch_iteration_count;
60: #endif
61:
62: static int force_lower_case = 0;
63:
64: /* Match pattern "p" against the a virtually-joined string consisting
65: * of "text" and any strings in array "a". */
66: static int dowild(const uchar *p, const uchar *text, const uchar*const *a)
67: {
68: uchar p_ch;
69:
70: #ifdef WILD_TEST_ITERATIONS
71: wildmatch_iteration_count++;
72: #endif
73:
74: for ( ; (p_ch = *p) != '\0'; text++, p++) {
75: int matched, special;
76: uchar t_ch, prev_ch;
1.1.1.2 ! misho 77: if (ignore_case && ISUPPER(p_ch))
! 78: p_ch = tolower(p_ch);
1.1 misho 79: while ((t_ch = *text) == '\0') {
80: if (*a == NULL) {
81: if (p_ch != '*')
82: return ABORT_ALL;
83: break;
84: }
85: text = *a++;
86: }
87: if (force_lower_case && ISUPPER(t_ch))
88: t_ch = tolower(t_ch);
89: switch (p_ch) {
90: case '\\':
91: /* Literal match with following character. Note that the test
92: * in "default" handles the p[1] == '\0' failure case. */
93: p_ch = *++p;
94: /* FALLTHROUGH */
95: default:
96: if (t_ch != p_ch)
97: return FALSE;
98: continue;
99: case '?':
100: /* Match anything but '/'. */
101: if (t_ch == '/')
102: return FALSE;
103: continue;
104: case '*':
105: if (*++p == '*') {
106: while (*++p == '*') {}
107: special = TRUE;
108: } else
109: special = FALSE;
110: if (*p == '\0') {
111: /* Trailing "**" matches everything. Trailing "*" matches
112: * only if there are no more slash characters. */
113: if (!special) {
114: do {
115: if (strchr((char*)text, '/') != NULL)
116: return FALSE;
117: } while ((text = *a++) != NULL);
118: }
119: return TRUE;
120: }
121: while (1) {
122: if (t_ch == '\0') {
123: if ((text = *a++) == NULL)
124: break;
125: t_ch = *text;
126: continue;
127: }
128: if ((matched = dowild(p, text, a)) != FALSE) {
129: if (!special || matched != ABORT_TO_STARSTAR)
130: return matched;
131: } else if (!special && t_ch == '/')
132: return ABORT_TO_STARSTAR;
133: t_ch = *++text;
134: }
135: return ABORT_ALL;
136: case '[':
137: p_ch = *++p;
138: #ifdef NEGATE_CLASS2
139: if (p_ch == NEGATE_CLASS2)
140: p_ch = NEGATE_CLASS;
141: #endif
142: /* Assign literal TRUE/FALSE because of "matched" comparison. */
143: special = p_ch == NEGATE_CLASS? TRUE : FALSE;
144: if (special) {
145: /* Inverted character class. */
146: p_ch = *++p;
147: }
148: prev_ch = 0;
149: matched = FALSE;
150: do {
151: if (!p_ch)
152: return ABORT_ALL;
153: if (p_ch == '\\') {
154: p_ch = *++p;
155: if (!p_ch)
156: return ABORT_ALL;
157: if (t_ch == p_ch)
158: matched = TRUE;
159: } else if (p_ch == '-' && prev_ch && p[1] && p[1] != ']') {
160: p_ch = *++p;
161: if (p_ch == '\\') {
162: p_ch = *++p;
163: if (!p_ch)
164: return ABORT_ALL;
165: }
166: if (t_ch <= p_ch && t_ch >= prev_ch)
167: matched = TRUE;
168: p_ch = 0; /* This makes "prev_ch" get set to 0. */
169: } else if (p_ch == '[' && p[1] == ':') {
170: const uchar *s;
171: int i;
172: for (s = p += 2; (p_ch = *p) && p_ch != ']'; p++) {} /*SHARED ITERATOR*/
173: if (!p_ch)
174: return ABORT_ALL;
175: i = p - s - 1;
176: if (i < 0 || p[-1] != ':') {
177: /* Didn't find ":]", so treat like a normal set. */
178: p = s - 2;
179: p_ch = '[';
180: if (t_ch == p_ch)
181: matched = TRUE;
182: continue;
183: }
184: if (CC_EQ(s,i, "alnum")) {
185: if (ISALNUM(t_ch))
186: matched = TRUE;
187: } else if (CC_EQ(s,i, "alpha")) {
188: if (ISALPHA(t_ch))
189: matched = TRUE;
190: } else if (CC_EQ(s,i, "blank")) {
191: if (ISBLANK(t_ch))
192: matched = TRUE;
193: } else if (CC_EQ(s,i, "cntrl")) {
194: if (ISCNTRL(t_ch))
195: matched = TRUE;
196: } else if (CC_EQ(s,i, "digit")) {
197: if (ISDIGIT(t_ch))
198: matched = TRUE;
199: } else if (CC_EQ(s,i, "graph")) {
200: if (ISGRAPH(t_ch))
201: matched = TRUE;
202: } else if (CC_EQ(s,i, "lower")) {
203: if (ISLOWER(t_ch))
204: matched = TRUE;
205: } else if (CC_EQ(s,i, "print")) {
206: if (ISPRINT(t_ch))
207: matched = TRUE;
208: } else if (CC_EQ(s,i, "punct")) {
209: if (ISPUNCT(t_ch))
210: matched = TRUE;
211: } else if (CC_EQ(s,i, "space")) {
212: if (ISSPACE(t_ch))
213: matched = TRUE;
214: } else if (CC_EQ(s,i, "upper")) {
215: if (ISUPPER(t_ch))
216: matched = TRUE;
217: } else if (CC_EQ(s,i, "xdigit")) {
218: if (ISXDIGIT(t_ch))
219: matched = TRUE;
220: } else /* malformed [:class:] string */
221: return ABORT_ALL;
222: p_ch = 0; /* This makes "prev_ch" get set to 0. */
223: } else if (t_ch == p_ch)
224: matched = TRUE;
225: } while (prev_ch = p_ch, (p_ch = *++p) != ']');
226: if (matched == special || t_ch == '/')
227: return FALSE;
228: continue;
229: }
230: }
231:
232: do {
233: if (*text)
234: return FALSE;
235: } while ((text = *a++) != NULL);
236:
237: return TRUE;
238: }
239:
240: /* Match literal string "s" against the a virtually-joined string consisting
241: * of "text" and any strings in array "a". */
242: static int doliteral(const uchar *s, const uchar *text, const uchar*const *a)
243: {
1.1.1.2 ! misho 244: uchar s_ch, t_ch;
1.1 misho 245: for ( ; *s != '\0'; text++, s++) {
246: while (*text == '\0') {
247: if ((text = *a++) == NULL)
248: return FALSE;
249: }
1.1.1.2 ! misho 250: s_ch = *s;
! 251: t_ch = *text;
! 252: if (ignore_case) {
! 253: if (ISUPPER(s_ch))
! 254: s_ch = tolower(s_ch);
! 255: if (ISUPPER(t_ch))
! 256: t_ch = tolower(t_ch);
! 257: }
! 258: if (t_ch != s_ch)
1.1 misho 259: return FALSE;
260: }
261:
262: do {
263: if (*text)
264: return FALSE;
265: } while ((text = *a++) != NULL);
266:
267: return TRUE;
268: }
269:
270: /* Return the last "count" path elements from the concatenated string.
271: * We return a string pointer to the start of the string, and update the
272: * array pointer-pointer to point to any remaining string elements. */
273: static const uchar *trailing_N_elements(const uchar*const **a_ptr, int count)
274: {
275: const uchar*const *a = *a_ptr;
276: const uchar*const *first_a = a;
277:
278: while (*a)
279: a++;
280:
281: while (a != first_a) {
282: const uchar *s = *--a;
283: s += strlen((char*)s);
284: while (--s >= *a) {
285: if (*s == '/' && !--count) {
286: *a_ptr = a+1;
287: return s+1;
288: }
289: }
290: }
291:
292: if (count == 1) {
293: *a_ptr = a+1;
294: return *a;
295: }
296:
297: return NULL;
298: }
299:
300: /* Match the "pattern" against the "text" string. */
301: int wildmatch(const char *pattern, const char *text)
302: {
303: static const uchar *nomore[1]; /* A NULL pointer. */
1.1.1.2 ! misho 304: int ret;
1.1 misho 305: #ifdef WILD_TEST_ITERATIONS
306: wildmatch_iteration_count = 0;
307: #endif
1.1.1.2 ! misho 308: force_lower_case = ignore_case;
! 309: ret = dowild((const uchar*)pattern, (const uchar*)text, nomore) == TRUE;
! 310: force_lower_case = 0;
! 311: return ret;
1.1 misho 312: }
313:
314: /* Match the "pattern" against the forced-to-lower-case "text" string. */
315: int iwildmatch(const char *pattern, const char *text)
316: {
317: static const uchar *nomore[1]; /* A NULL pointer. */
318: int ret;
319: #ifdef WILD_TEST_ITERATIONS
320: wildmatch_iteration_count = 0;
321: #endif
322: force_lower_case = 1;
323: ret = dowild((const uchar*)pattern, (const uchar*)text, nomore) == TRUE;
324: force_lower_case = 0;
325: return ret;
326: }
327:
328: /* Match pattern "p" against the a virtually-joined string consisting
329: * of all the pointers in array "texts" (which has a NULL pointer at the
330: * end). The int "where" can be 0 (normal matching), > 0 (match only
331: * the trailing N slash-separated filename components of "texts"), or < 0
332: * (match the "pattern" at the start or after any slash in "texts"). */
333: int wildmatch_array(const char *pattern, const char*const *texts, int where)
334: {
335: const uchar *p = (const uchar*)pattern;
336: const uchar*const *a = (const uchar*const*)texts;
337: const uchar *text;
338: int matched;
339:
340: #ifdef WILD_TEST_ITERATIONS
341: wildmatch_iteration_count = 0;
342: #endif
343:
344: if (where > 0)
345: text = trailing_N_elements(&a, where);
346: else
347: text = *a++;
348: if (!text)
349: return FALSE;
350:
1.1.1.2 ! misho 351: force_lower_case = ignore_case;
! 352:
1.1 misho 353: if ((matched = dowild(p, text, a)) != TRUE && where < 0
354: && matched != ABORT_ALL) {
355: while (1) {
356: if (*text == '\0') {
357: if ((text = (uchar*)*a++) == NULL)
1.1.1.2 ! misho 358: break;
1.1 misho 359: continue;
360: }
361: if (*text++ == '/' && (matched = dowild(p, text, a)) != FALSE
362: && matched != ABORT_TO_STARSTAR)
363: break;
364: }
365: }
1.1.1.2 ! misho 366:
! 367: force_lower_case = 0;
! 368:
1.1 misho 369: return matched == TRUE;
370: }
371:
372: /* Match literal string "s" against the a virtually-joined string consisting
373: * of all the pointers in array "texts" (which has a NULL pointer at the
374: * end). The int "where" can be 0 (normal matching), or > 0 (match
375: * only the trailing N slash-separated filename components of "texts"). */
376: int litmatch_array(const char *string, const char*const *texts, int where)
377: {
378: const uchar *s = (const uchar*)string;
379: const uchar*const *a = (const uchar* const*)texts;
380: const uchar *text;
381:
382: if (where > 0)
383: text = trailing_N_elements(&a, where);
384: else
385: text = *a++;
386: if (!text)
387: return FALSE;
388:
389: return doliteral(s, text, a) == TRUE;
390: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>