blob: ad31c9eb492e2c0d9744ad7e82e5f235a1aafd69 [file] [log] [blame]
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001/* $OpenBSD: fnmatch.c,v 1.13 2006/03/31 05:34:14 deraadt Exp $ */
2
3/*
4 * Copyright (c) 1989, 1993, 1994
5 * The Regents of the University of California. All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * Guido van Rossum.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35/*
36 * Function fnmatch() as specified in POSIX 1003.2-1992, section B.6.
37 * Compares a filename or pathname to a pattern.
38 */
39
40#include <ctype.h>
41#include <stdio.h>
42#include <string.h>
43#include <fnmatch.h>
44
45#define EOS '\0'
46
47#define RANGE_MATCH 1
48#define RANGE_NOMATCH 0
49#define RANGE_ERROR (-1)
50
51static int rangematch(const char *, char, int, char **);
52
53int
54fnmatch(const char *pattern, const char *string, int flags)
55{
56 const char *stringstart;
57 char *newp;
58 char c, test;
59
60 for (stringstart = string;;)
61 switch (c = *pattern++) {
62 case EOS:
63 if ((flags & FNM_LEADING_DIR) && *string == '/')
64 return (0);
65 return (*string == EOS ? 0 : FNM_NOMATCH);
66 case '?':
67 if (*string == EOS)
68 return (FNM_NOMATCH);
69 if (*string == '/' && (flags & FNM_PATHNAME))
70 return (FNM_NOMATCH);
71 if (*string == '.' && (flags & FNM_PERIOD) &&
72 (string == stringstart ||
73 ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
74 return (FNM_NOMATCH);
75 ++string;
76 break;
77 case '*':
78 c = *pattern;
79 /* Collapse multiple stars. */
80 while (c == '*')
81 c = *++pattern;
82
83 if (*string == '.' && (flags & FNM_PERIOD) &&
84 (string == stringstart ||
85 ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
86 return (FNM_NOMATCH);
87
88 /* Optimize for pattern with * at end or before /. */
89 if (c == EOS) {
90 if (flags & FNM_PATHNAME)
91 return ((flags & FNM_LEADING_DIR) ||
92 strchr(string, '/') == NULL ?
93 0 : FNM_NOMATCH);
94 else
95 return (0);
96 } else if (c == '/' && (flags & FNM_PATHNAME)) {
97 if ((string = strchr(string, '/')) == NULL)
98 return (FNM_NOMATCH);
99 break;
100 }
101
102 /* General case, use recursion. */
103 while ((test = *string) != EOS) {
104 if (!fnmatch(pattern, string, flags & ~FNM_PERIOD))
105 return (0);
106 if (test == '/' && (flags & FNM_PATHNAME))
107 break;
108 ++string;
109 }
110 return (FNM_NOMATCH);
111 case '[':
112 if (*string == EOS)
113 return (FNM_NOMATCH);
114 if (*string == '/' && (flags & FNM_PATHNAME))
115 return (FNM_NOMATCH);
116 if (*string == '.' && (flags & FNM_PERIOD) &&
117 (string == stringstart ||
118 ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
119 return (FNM_NOMATCH);
120
121 switch (rangematch(pattern, *string, flags, &newp)) {
122 case RANGE_ERROR:
123 /* not a good range, treat as normal text */
124 goto normal;
125 case RANGE_MATCH:
126 pattern = newp;
127 break;
128 case RANGE_NOMATCH:
129 return (FNM_NOMATCH);
130 }
131 ++string;
132 break;
133 case '\\':
134 if (!(flags & FNM_NOESCAPE)) {
135 if ((c = *pattern++) == EOS) {
136 c = '\\';
137 --pattern;
138 }
139 }
140 /* FALLTHROUGH */
141 default:
142 normal:
143 if (c != *string && !((flags & FNM_CASEFOLD) &&
144 (tolower((unsigned char)c) ==
145 tolower((unsigned char)*string))))
146 return (FNM_NOMATCH);
147 ++string;
148 break;
149 }
150 /* NOTREACHED */
151}
152
153static int
154rangematch(const char *pattern, char test, int flags, char **newp)
155{
156 int negate, ok;
157 char c, c2;
158
159 /*
160 * A bracket expression starting with an unquoted circumflex
161 * character produces unspecified results (IEEE 1003.2-1992,
162 * 3.13.2). This implementation treats it like '!', for
163 * consistency with the regular expression syntax.
164 * J.T. Conklin (conklin@ngai.kaleida.com)
165 */
166 if ((negate = (*pattern == '!' || *pattern == '^')))
167 ++pattern;
168
169 if (flags & FNM_CASEFOLD)
170 test = (char)tolower((unsigned char)test);
171
172 /*
173 * A right bracket shall lose its special meaning and represent
174 * itself in a bracket expression if it occurs first in the list.
175 * -- POSIX.2 2.8.3.2
176 */
177 ok = 0;
178 c = *pattern++;
179 do {
180 if (c == '\\' && !(flags & FNM_NOESCAPE))
181 c = *pattern++;
182 if (c == EOS)
183 return (RANGE_ERROR);
184 if (c == '/' && (flags & FNM_PATHNAME))
185 return (RANGE_NOMATCH);
186 if ((flags & FNM_CASEFOLD))
187 c = (char)tolower((unsigned char)c);
188 if (*pattern == '-'
189 && (c2 = *(pattern+1)) != EOS && c2 != ']') {
190 pattern += 2;
191 if (c2 == '\\' && !(flags & FNM_NOESCAPE))
192 c2 = *pattern++;
193 if (c2 == EOS)
194 return (RANGE_ERROR);
195 if (flags & FNM_CASEFOLD)
196 c2 = (char)tolower((unsigned char)c2);
197 if (c <= test && test <= c2)
198 ok = 1;
199 } else if (c == test)
200 ok = 1;
201 } while ((c = *pattern++) != ']');
202
203 *newp = (char *)pattern;
204 return (ok == negate ? RANGE_NOMATCH : RANGE_MATCH);
205}