xref: /original-bsd/lib/libc/gen/fnmatch.c (revision 608e088d)
1 /*
2  * Copyright (c) 1989 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Guido van Rossum.
7  *
8  * %sccs.include.redist.c%
9  */
10 
11 #if defined(LIBC_SCCS) && !defined(lint)
12 static char sccsid[] = "@(#)fnmatch.c	5.3 (Berkeley) 06/23/90";
13 #endif /* LIBC_SCCS and not lint */
14 
15 /*
16  * Function fnmatch() as proposed in Posix 1003.2 B.6 (rev. 9).
17  * Compares a filename or pathname to a pattern.
18  */
19 
20 #include <unistd.h>
21 #include <string.h>
22 
23 #define	EOS	'\0'
24 
25 static char *
26 rangematch(pattern, test)
27 	register char *pattern, test;
28 {
29 	register char c, c2;
30 	int negate, ok;
31 
32 	if (negate = (*pattern == '!'))
33 		++pattern;
34 
35 	/*
36 	 * TO DO: quoting
37 	 */
38 
39 	for (ok = 0; (c = *pattern++) != ']';) {
40 		if (c == EOS)
41 			return(NULL);		/* illegal pattern */
42 		if (*pattern == '-' && (c2 = pattern[1]) != EOS && c2 != ']') {
43 			if (c <= test && test <= c2)
44 				ok = 1;
45 			pattern += 2;
46 		}
47 		else if (c == test)
48 			ok = 1;
49 	}
50 	return(ok == negate ? NULL : pattern);
51 }
52 
53 fnmatch(pattern, string, flags)
54 	register char *pattern, *string;
55 	int flags;
56 {
57 	register char c;
58 	char test, *rangematch();
59 
60 	for (;;)
61 		switch (c = *pattern++) {
62 		case EOS:
63 			return(*string == EOS);
64 		case '?':
65 			if ((test = *string++) == EOS ||
66 			    test == '/' && flags & FNM_PATHNAME)
67 				return(0);
68 			break;
69 		case '*':
70 			c = *pattern;
71 			/* collapse multiple stars */
72 			while (c == '*')
73 				c = *++pattern;
74 
75 			/* optimize for pattern with * at end or before / */
76 			if (c == EOS)
77 				if (flags & FNM_PATHNAME)
78 					return(!index(string, '/'));
79 				else
80 					return(1);
81 			else if (c == '/' && flags & FNM_PATHNAME) {
82 				if ((string = index(string, '/')) == NULL)
83 					return(0);
84 				break;
85 			}
86 
87 			/* general case, use recursion */
88 			while ((test = *string) != EOS) {
89 				if (fnmatch(pattern, string, flags))
90 					return(1);
91 				if (test == '/' && flags & FNM_PATHNAME)
92 					break;
93 				++string;
94 			}
95 			return(0);
96 		case '[':
97 			if ((test = *string++) == EOS ||
98 			    test == '/' && flags & FNM_PATHNAME)
99 				return(0);
100 			if ((pattern = rangematch(pattern, test)) == NULL)
101 				return(0);
102 			break;
103 		case '\\':
104 			if (flags & FNM_QUOTE) {
105 				if ((c = *pattern++) == EOS) {
106 					c = '\\';
107 					--pattern;
108 				}
109 				if (c != *string++)
110 					return(0);
111 				break;
112 			}
113 			/* FALLTHROUGH */
114 		default:
115 			if (c != *string++)
116 				return(0);
117 			break;
118 		}
119 }
120