xref: /original-bsd/lib/libc/gen/fnmatch.c (revision 7e5c8007)
1 /*
2  * Copyright (c) 1989, 1993, 1994
3  *	The Regents of the University of California.  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	8.2 (Berkeley) 04/16/94";
13 #endif /* LIBC_SCCS and not lint */
14 
15 /*
16  * Function fnmatch() as specified in POSIX 1003.2-1992, section B.6.
17  * Compares a filename or pathname to a pattern.
18  */
19 
20 #include <fnmatch.h>
21 #include <string.h>
22 
23 #define	EOS	'\0'
24 
25 static const char *rangematch __P((const char *, int, int));
26 
27 int
28 fnmatch(pattern, string, flags)
29 	const char *pattern, *string;
30 	int flags;
31 {
32 	const char *stringstart;
33 	char c, test;
34 
35 	for (stringstart = string;;)
36 		switch (c = *pattern++) {
37 		case EOS:
38 			return (*string == EOS ? 0 : FNM_NOMATCH);
39 		case '?':
40 			if (*string == EOS)
41 				return (FNM_NOMATCH);
42 			if (*string == '/' && (flags & FNM_PATHNAME))
43 				return (FNM_NOMATCH);
44 			if (*string == '.' && (flags & FNM_PERIOD) &&
45 			    (string == stringstart ||
46 			    ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
47 				return (FNM_NOMATCH);
48 			++string;
49 			break;
50 		case '*':
51 			c = *pattern;
52 			/* Collapse multiple stars. */
53 			while (c == '*')
54 				c = *++pattern;
55 
56 			if (*string == '.' && (flags & FNM_PERIOD) &&
57 			    (string == stringstart ||
58 			    ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
59 				return (FNM_NOMATCH);
60 
61 			/* Optimize for pattern with * at end or before /. */
62 			if (c == EOS)
63 				if (flags & FNM_PATHNAME)
64 					return (strchr(string, '/') == NULL ?
65 					    0 : FNM_NOMATCH);
66 				else
67 					return (0);
68 			else if (c == '/' && flags & FNM_PATHNAME) {
69 				if ((string = strchr(string, '/')) == NULL)
70 					return (FNM_NOMATCH);
71 				break;
72 			}
73 
74 			/* General case, use recursion. */
75 			while ((test = *string) != EOS) {
76 				if (!fnmatch(pattern, string, flags & ~FNM_PERIOD))
77 					return (0);
78 				if (test == '/' && flags & FNM_PATHNAME)
79 					break;
80 				++string;
81 			}
82 			return (FNM_NOMATCH);
83 		case '[':
84 			if (*string == EOS)
85 				return (FNM_NOMATCH);
86 			if (*string == '/' && flags & FNM_PATHNAME)
87 				return (FNM_NOMATCH);
88 			if ((pattern =
89 			    rangematch(pattern, *string, flags)) == NULL)
90 				return (FNM_NOMATCH);
91 			++string;
92 			break;
93 		case '\\':
94 			if (!(flags & FNM_NOESCAPE)) {
95 				if ((c = *pattern++) == EOS) {
96 					c = '\\';
97 					--pattern;
98 				}
99 			}
100 			/* FALLTHROUGH */
101 		default:
102 			if (c != *string++)
103 				return (FNM_NOMATCH);
104 			break;
105 		}
106 	/* NOTREACHED */
107 }
108 
109 static const char *
110 rangematch(pattern, test, flags)
111 	const char *pattern;
112 	int test, flags;
113 {
114 	int negate, ok;
115 	char c, c2;
116 
117 	/*
118 	 * A bracket expression starting with an unquoted circumflex
119 	 * character produces unspecified results (IEEE 1003.2-1992,
120 	 * 3.13.2).  This implementation treats it like '!', for
121 	 * consistency with the regular expression syntax.
122 	 * J.T. Conklin (conklin@ngai.kaleida.com)
123 	 */
124 	if (negate = (*pattern == '!' || *pattern == '^'))
125 		++pattern;
126 
127 	for (ok = 0; (c = *pattern++) != ']';) {
128 		if (c == '\\' && !(flags & FNM_NOESCAPE))
129 			c = *pattern++;
130 		if (c == EOS)
131 			return (NULL);
132 		if (*pattern == '-'
133 		    && (c2 = *(pattern+1)) != EOS && c2 != ']') {
134 			pattern += 2;
135 			if (c2 == '\\' && !(flags & FNM_NOESCAPE))
136 				c2 = *pattern++;
137 			if (c2 == EOS)
138 				return (NULL);
139 			if (c <= test && test <= c2)
140 				ok = 1;
141 		} else if (c == test)
142 			ok = 1;
143 	}
144 	return (ok == negate ? NULL : pattern);
145 }
146