xref: /original-bsd/lib/libc/gen/fnmatch.c (revision 9b5efc43)
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.4 (Berkeley) 02/23/91";
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 const char *pattern;
55 	register const char *string;
56 	int flags;
57 {
58 	register char c;
59 	char test, *rangematch();
60 
61 	for (;;)
62 		switch (c = *pattern++) {
63 		case EOS:
64 			return(*string == EOS);
65 		case '?':
66 			if ((test = *string++) == EOS ||
67 			    test == '/' && flags & FNM_PATHNAME)
68 				return(0);
69 			break;
70 		case '*':
71 			c = *pattern;
72 			/* collapse multiple stars */
73 			while (c == '*')
74 				c = *++pattern;
75 
76 			/* optimize for pattern with * at end or before / */
77 			if (c == EOS)
78 				if (flags & FNM_PATHNAME)
79 					return(!index(string, '/'));
80 				else
81 					return(1);
82 			else if (c == '/' && flags & FNM_PATHNAME) {
83 				if ((string = index(string, '/')) == NULL)
84 					return(0);
85 				break;
86 			}
87 
88 			/* general case, use recursion */
89 			while ((test = *string) != EOS) {
90 				if (fnmatch(pattern, string, flags))
91 					return(1);
92 				if (test == '/' && flags & FNM_PATHNAME)
93 					break;
94 				++string;
95 			}
96 			return(0);
97 		case '[':
98 			if ((test = *string++) == EOS ||
99 			    test == '/' && flags & FNM_PATHNAME)
100 				return(0);
101 			if ((pattern = rangematch(pattern, test)) == NULL)
102 				return(0);
103 			break;
104 		case '\\':
105 			if (flags & FNM_QUOTE) {
106 				if ((c = *pattern++) == EOS) {
107 					c = '\\';
108 					--pattern;
109 				}
110 				if (c != *string++)
111 					return(0);
112 				break;
113 			}
114 			/* FALLTHROUGH */
115 		default:
116 			if (c != *string++)
117 				return(0);
118 			break;
119 		}
120 }
121