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
fnmatch(pattern,string,flags)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 *
rangematch(pattern,test,flags)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