|
|
1.1 root 1: /*---------------------------------------------------------------------------
2:
3: match.c
4:
5: The match() routine recursively compares a string to a "pattern" (regular
6: expression), returning TRUE if a match is found or FALSE if not. This
7: version is specifically for use with unzip.c: as did the previous match()
8: from SEA, it leaves the case (upper, lower, or mixed) of the string alone,
9: but converts any uppercase characters in the pattern to lowercase if indi-
10: cated by the global var pInfo->lcflag (which is to say, string is assumed
11: to have been converted to lowercase already, if such was necessary).
12:
13: ---------------------------------------------------------------------------*/
14:
15:
16: #ifdef ZIPINFO
17: # undef ZIPINFO /* make certain there is only one version of match.o */
18: #endif /* ZIPINFO */
19: #include "unzip.h"
20:
21: static int matche __((register char *p, register char *t));
22: static int matche_after_star __((register char *p, register char *t));
23:
24: /* #include "filmatch.h": */
25: #ifndef BOOLEAN
26: # define BOOLEAN short int /* v1.2 made it short */
27: #endif
28:
29: /* match defines */
30: #define MATCH_PATTERN 6 /* bad pattern */
31: #define MATCH_LITERAL 5 /* match failure on literal match */
32: #define MATCH_RANGE 4 /* match failure on [..] construct */
33: #define MATCH_ABORT 3 /* premature end of text string */
34: #define MATCH_END 2 /* premature end of pattern string */
35: #define MATCH_VALID 1 /* valid match */
36:
37: /* pattern defines */
38: #define PATTERN_VALID 0 /* valid pattern */
39: #define PATTERN_ESC -1 /* literal escape at end of pattern */
40: #define PATTERN_RANGE -2 /* malformed range in [..] construct */
41: #define PATTERN_CLOSE -3 /* no end bracket in [..] construct */
42: #define PATTERN_EMPTY -4 /* [..] contstruct is empty */
43:
44: /*----------------------------------------------------------------------------
45: *
46: * Match the pattern PATTERN against the string TEXT;
47: *
48: * match() returns TRUE if pattern matches, FALSE otherwise.
49: * matche() returns MATCH_VALID if pattern matches, or an errorcode
50: * as follows otherwise:
51: *
52: * MATCH_PATTERN - bad pattern
53: * MATCH_RANGE - match failure on [..] construct
54: * MATCH_ABORT - premature end of text string
55: * MATCH_END - premature end of pattern string
56: * MATCH_VALID - valid match
57: *
58: *
59: * A match means the entire string TEXT is used up in matching.
60: *
61: * In the pattern string:
62: * `*' matches any sequence of characters (zero or more)
63: * `?' matches any character
64: * [SET] matches any character in the specified set,
65: * [!SET] or [^SET] matches any character not in the specified set.
66: *
67: * A set is composed of characters or ranges; a range looks like
68: * character hyphen character (as in 0-9 or A-Z). [0-9a-zA-Z_] is the
69: * minimal set of characters allowed in the [..] pattern construct.
70: * Other characters are allowed (ie. 8 bit characters) if your system
71: * will support them.
72: *
73: * To suppress the special syntactic significance of any of `[]*?!^-\',
74: * in a [..] construct and match the character exactly, precede it
75: * with a `\'.
76: *
77: ----------------------------------------------------------------------------*/
78:
79: /*----------------------------------------------------------------------------
80: *
81: * Match the pattern PATTERN against the string TEXT;
82: *
83: * returns MATCH_VALID if pattern matches, or an errorcode as follows
84: * otherwise:
85: *
86: * MATCH_PATTERN - bad pattern
87: * MATCH_RANGE - match failure on [..] construct
88: * MATCH_ABORT - premature end of text string
89: * MATCH_END - premature end of pattern string
90: * MATCH_VALID - valid match
91: *
92: *
93: * A match means the entire string TEXT is used up in matching.
94: *
95: * In the pattern string:
96: * `*' matches any sequence of characters (zero or more)
97: * `?' matches any character
98: * [SET] matches any character in the specified set,
99: * [!SET] or [^SET] matches any character not in the specified set.
100: * \ is allowed within a set to escape a character like ']' or '-'
101: *
102: * A set is composed of characters or ranges; a range looks like
103: * character hyphen character (as in 0-9 or A-Z). [0-9a-zA-Z_] is the
104: * minimal set of characters allowed in the [..] pattern construct.
105: * Other characters are allowed (ie. 8 bit characters) if your system
106: * will support them.
107: *
108: * To suppress the special syntactic significance of any of `[]*?!^-\',
109: * within a [..] construct and match the character exactly, precede it
110: * with a `\'.
111: *
112: ----------------------------------------------------------------------------*/
113:
114: static int matche(p, t)
115: register char *p;
116: register char *t;
117: {
118: register char range_start, range_end; /* start and end in range */
119:
120: BOOLEAN invert; /* is this [..] or [!..] */
121: BOOLEAN member_match; /* have I matched the [..] construct? */
122: BOOLEAN loop; /* should I terminate? */
123:
124: for (; *p; p++, t++) {
125:
126: /* if this is the end of the text then this is the end of the match */
127: if (!*t)
128: return ((*p == '*') && (*++p == '\0'))? MATCH_VALID : MATCH_ABORT;
129:
130: /* determine and react to pattern type */
131: switch (*p) {
132:
133: /* single any character match */
134: case '?':
135: break;
136:
137: /* multiple any character match */
138: case '*':
139: return matche_after_star (p, t);
140:
141: /* [..] construct, single member/exclusion character match */
142: case '[': {
143:
144: /* move to beginning of range */
145: p++;
146:
147: /* check if this is a member match or exclusion match */
148: invert = FALSE;
149: if ((*p == '!') || (*p == '^')) {
150: invert = TRUE;
151: p++;
152: }
153:
154: /* if closing bracket here or at range start then we have a
155: malformed pattern */
156: if (*p == ']')
157: return MATCH_PATTERN;
158:
159: member_match = FALSE;
160: loop = TRUE;
161:
162: while (loop) {
163:
164: /* if end of construct then loop is done */
165: if (*p == ']') {
166: loop = FALSE;
167: continue;
168: }
169:
170: /* matching a '!', '^', '-', '\' or a ']' */
171: if (*p == '\\')
172: range_start = range_end = *++p;
173: else
174: range_start = range_end = *p;
175:
176: /* if end of pattern then bad pattern (Missing ']') */
177: if (!*p)
178: return MATCH_PATTERN;
179:
180: /* check for range bar */
181: if (*++p == '-') {
182:
183: /* get the range end */
184: range_end = *++p;
185:
186: /* if end of pattern or construct then bad pattern */
187: if ((range_end == '\0') || (range_end == ']'))
188: return MATCH_PATTERN;
189:
190: /* special character range end */
191: if (range_end == '\\') {
192: range_end = *++p;
193:
194: /* if end of text then we have a bad pattern */
195: if (!range_end)
196: return MATCH_PATTERN;
197: }
198:
199: /* move just beyond this range */
200: p++;
201: }
202:
203: /* if the text character is in range then match found.
204: * make sure the range letters have the proper
205: * relationship to one another before comparison
206: */
207: if (range_start < range_end) {
208: if ((*t >= range_start) && (*t <= range_end)) {
209: member_match = TRUE;
210: loop = FALSE;
211: }
212: } else {
213: if ((*t >= range_end) && (*t <= range_start)) {
214: member_match = TRUE;
215: loop = FALSE;
216: }
217: }
218: }
219:
220: /* if there was a match in an exclusion set then no match */
221: /* if there was no match in a member set then no match */
222: if ((invert && member_match) ||
223: !(invert || member_match))
224: return MATCH_RANGE;
225:
226: /* if this is not an exclusion then skip the rest of the [...]
227: construct that already matched. */
228: if (member_match) {
229: while (*p != ']') {
230:
231: /* bad pattern (Missing ']') */
232: if (!*p)
233: return MATCH_PATTERN;
234:
235: /* skip exact match */
236: if (*p == '\\') {
237: p++;
238:
239: /* if end of text then we have a bad pattern */
240: if (!*p)
241: return MATCH_PATTERN;
242: }
243:
244: /* move to next pattern char */
245: p++;
246: }
247: }
248:
249: break;
250: } /* switch '[' */
251:
252: /* must match this character exactly */
253: default:
254: #ifdef OLDSTUFF
255: if (*p != *t)
256: #else /* !OLDSTUFF */
257: /* do it like arcmatch() (old unzip) did it (v1.2) */
258: if (*t != (char) ((pInfo->lcflag && isupper((int)(*p)))?
259: tolower((int)(*p)) : *p))
260: #endif /* ?OLDSTUFF */
261: return MATCH_LITERAL;
262:
263: } /* switch */
264: } /* for */
265:
266: /* if end of text not reached then the pattern fails */
267: if (*t)
268: return MATCH_END;
269: else
270: return MATCH_VALID;
271: }
272:
273:
274: /*----------------------------------------------------------------------------
275: *
276: * recursively call matche() with final segment of PATTERN and of TEXT.
277: *
278: ----------------------------------------------------------------------------*/
279:
280: static int matche_after_star (p,t)
281: register char *p;
282: register char *t;
283: {
284: register int match = 0;
285: register int nextp;
286:
287: /* pass over existing ? and * in pattern */
288: while ((*p == '?') || (*p == '*')) {
289:
290: /* take one char for each ? and +; if end of text then no match */
291: if ((*p == '?') && (!*t++))
292: return MATCH_ABORT;
293:
294: /* move to next char in pattern */
295: p++;
296: }
297:
298: /* if end of pattern we have matched regardless of text left */
299: if (!*p)
300: return MATCH_VALID;
301:
302: /* get the next character to match which must be a literal or '[' */
303: nextp = *p;
304:
305: /* Continue until we run out of text or definite result seen */
306: do {
307: /* a precondition for matching is that the next character
308: * in the pattern match the next character in the text or that
309: * the next pattern char is the beginning of a range. Increment
310: * text pointer as we go here.
311: */
312: if ((nextp == *t) || (nextp == '['))
313: match = matche(p, t);
314:
315: /* if the end of text is reached then no match */
316: if (!*t++)
317: match = MATCH_ABORT;
318:
319: } while ((match != MATCH_VALID) &&
320: (match != MATCH_ABORT) &&
321: (match != MATCH_PATTERN));
322:
323: /* return result */
324: return match;
325: }
326:
327:
328: /*----------------------------------------------------------------------------
329: *
330: * match() is a shell to matche() to return only BOOLEAN values.
331: *
332: ----------------------------------------------------------------------------*/
333:
334: int match(string,pattern)
335: char *string;
336: char *pattern;
337: {
338: int error_type;
339: error_type = matche(pattern,string);
340: return (error_type == MATCH_VALID ) ? TRUE : FALSE;
341: }
342:
343:
344: #ifdef TEST_MATCH
345:
346: /*----------------------------------------------------------------------------
347: *
348: * Return TRUE if PATTERN has any special wildcard characters
349: *
350: ----------------------------------------------------------------------------*/
351:
352: BOOLEAN is_pattern (char *pattern);
353:
354: /*----------------------------------------------------------------------------
355: *
356: * Return TRUE if PATTERN has is a well formed regular expression according
357: * to the above syntax
358: *
359: * error_type is a return code based on the type of pattern error. Zero is
360: * returned in error_type if the pattern is a valid one. error_type return
361: * values are as follows:
362: *
363: * PATTERN_VALID - pattern is well formed
364: * PATTERN_RANGE - [..] construct has a no end range in a '-' pair (ie [a-])
365: * PATTERN_CLOSE - [..] construct has no end bracket (ie [abc-g )
366: * PATTERN_EMPTY - [..] construct is empty (ie [])
367: *
368: ----------------------------------------------------------------------------*/
369:
370: BOOLEAN is_valid_pattern (char *pattern, int *error_type);
371: int fast_match_after_star (register char *pattern, register char *text);
372:
373: /*----------------------------------------------------------------------------
374: *
375: * Return TRUE if PATTERN has any special wildcard characters
376: *
377: ----------------------------------------------------------------------------*/
378:
379: BOOLEAN is_pattern (char *p)
380: {
381: while (*p)
382: switch (*p++) {
383: case '?':
384: case '*':
385: case '[':
386: return TRUE;
387: }
388: return FALSE;
389: }
390:
391:
392: /*----------------------------------------------------------------------------
393: *
394: * Return TRUE if PATTERN has is a well formed regular expression according
395: * to the above syntax
396: *
397: * error_type is a return code based on the type of pattern error. Zero is
398: * returned in error_type if the pattern is a valid one. error_type return
399: * values are as follows:
400: *
401: * PATTERN_VALID - pattern is well formed
402: * PATTERN_RANGE - [..] construct has a no end range in a '-' pair (ie [a-])
403: * PATTERN_CLOSE - [..] construct has no end bracket (ie [abc-g )
404: * PATTERN_EMPTY - [..] construct is empty (ie [])
405: *
406: ----------------------------------------------------------------------------*/
407:
408: BOOLEAN is_valid_pattern (char *p, int *error_type)
409: {
410: /* init error_type */
411: *error_type = PATTERN_VALID;
412:
413: /* loop through pattern to EOS */
414: while (*p) {
415:
416: /* determine pattern type */
417: switch (*p) {
418:
419: /* the [..] construct must be well formed */
420: case '[':
421: p++;
422:
423: /* if the next character is ']' then bad pattern */
424: if (*p == ']') {
425: *error_type = PATTERN_EMPTY;
426: return FALSE;
427: }
428:
429: /* if end of pattern here then bad pattern */
430: if (!*p) {
431: *error_type = PATTERN_CLOSE;
432: return FALSE;
433: }
434:
435: /* loop to end of [..] construct */
436: while (*p != ']') {
437:
438: /* check for literal escape */
439: if (*p == '\\') {
440: p++;
441:
442: /* if end of pattern here then bad pattern */
443: if (!*p++) {
444: *error_type = PATTERN_ESC;
445: return FALSE;
446: }
447: } else
448: p++;
449:
450: /* if end of pattern here then bad pattern */
451: if (!*p) {
452: *error_type = PATTERN_CLOSE;
453: return FALSE;
454: }
455:
456: /* if this a range */
457: if (*p == '-') {
458:
459: /* we must have an end of range */
460: if (!*++p || (*p == ']')) {
461: *error_type = PATTERN_RANGE;
462: return FALSE;
463: } else {
464:
465: /* check for literal escape */
466: if (*p == '\\')
467: p++;
468:
469: /* if end of pattern here then bad pattern */
470: if (!*p++) {
471: *error_type = PATTERN_ESC;
472: return FALSE;
473: }
474: }
475: }
476: }
477: break;
478:
479: /* all other characters are valid pattern elements */
480: case '*':
481: case '?':
482: default:
483: p++; /* "normal" character */
484: break;
485: } /* switch */
486: }
487:
488: return TRUE;
489: }
490:
491:
492: /*
493: * This test main expects as first arg the pattern and as second arg
494: * the match string. Output is yay or nay on match. If nay on
495: * match then the error code is parsed and written.
496: */
497:
498: #include <stdio.h>
499:
500: int main(int argc, char *argv[])
501: {
502: int error;
503: int is_valid_error;
504:
505: if (argc != 3)
506: printf("Usage: MATCH Pattern Text\n");
507: else {
508: printf("Pattern: %s\n", argv[1]);
509: printf("Text : %s\n", argv[2]);
510:
511: if (!is_pattern(argv[1]))
512: printf(" First Argument Is Not A Pattern\n");
513: else {
514: match(argv[1],argv[2]) ? printf("TRUE") : printf("FALSE");
515: error = matche(argv[1],argv[2]);
516: is_valid_pattern(argv[1],&is_valid_error);
517:
518: switch (error) {
519: case MATCH_VALID:
520: printf(" Match Successful");
521: if (is_valid_error != PATTERN_VALID)
522: printf(" -- is_valid_pattern() is complaining\n");
523: else
524: printf("\n");
525: break;
526: case MATCH_RANGE:
527: printf(" Match Failed on [..]\n");
528: break;
529: case MATCH_ABORT:
530: printf(" Match Failed on Early Text Termination\n");
531: break;
532: case MATCH_END:
533: printf(" Match Failed on Early Pattern Termination\n");
534: break;
535: case MATCH_PATTERN:
536: switch (is_valid_error) {
537: case PATTERN_VALID:
538: printf(" Internal Disagreement On Pattern\n");
539: break;
540: case PATTERN_RANGE:
541: printf(" No End of Range in [..] Construct\n");
542: break;
543: case PATTERN_CLOSE:
544: printf(" [..] Construct is Open\n");
545: break;
546: case PATTERN_EMPTY:
547: printf(" [..] Construct is Empty\n");
548: break;
549: default:
550: printf(" Internal Error in is_valid_pattern()\n");
551: }
552: break;
553: default:
554: printf(" Internal Error in matche()\n");
555: break;
556: } /* switch */
557: }
558:
559: }
560: return(0);
561: }
562:
563: #endif /* TEST_MATCH */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.