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