(FOLD): Do not assume that characters are unsigned.
[gnulib.git] / lib / fnmatch.c
1 /* Copyright 1991, 1992, 1993, 1996, 1997, 2000 Free Software Foundation, Inc.
2
3    This program is free software; you can redistribute it and/or modify
4    it under the terms of the GNU General Public License as published by
5    the Free Software Foundation; either version 2, or (at your option)
6    any later version.
7
8    This program is distributed in the hope that it will be useful,
9    but WITHOUT ANY WARRANTY; without even the implied warranty of
10    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11    GNU General Public License for more details.
12
13   You should have received a copy of the GNU General Public License
14   along with this program; if not, write to the Free Software Foundation,
15   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
16
17 #if HAVE_CONFIG_H
18 # include <config.h>
19 #endif
20
21 /* Enable GNU extensions in fnmatch.h.  */
22 #ifndef _GNU_SOURCE
23 # define _GNU_SOURCE    1
24 #endif
25
26 #include <errno.h>
27 #include <fnmatch.h>
28 #include <ctype.h>
29
30
31 /* Comment out all this code if we are using the GNU C Library, and are not
32    actually compiling the library itself.  This code is part of the GNU C
33    Library, but also included in many other GNU distributions.  Compiling
34    and linking in this code is a waste when using the GNU C library
35    (especially if it is a shared library).  Rather than having every GNU
36    program understand `configure --with-gnu-libc' and omit the object files,
37    it is simpler to just do this in the source for each such file.  */
38
39 #if defined _LIBC || !defined __GNU_LIBRARY__
40
41
42 # if defined STDC_HEADERS || !defined isascii
43 #  define IN_CTYPE_DOMAIN(c) 1
44 # else
45 #  define IN_CTYPE_DOMAIN(c) isascii(c)
46 # endif
47
48 # define ISUPPER(c) (IN_CTYPE_DOMAIN (c) && isupper (c))
49
50
51 # ifndef errno
52 extern int errno;
53 # endif
54
55 /* Match STRING against the filename pattern PATTERN, returning zero if
56    it matches, nonzero if not.  */
57 int
58 fnmatch (const char *pattern, const char *string, int flags)
59 {
60   register const char *p = pattern, *n = string;
61   register char c;
62
63 /* Note that this evaluates C many times.  */
64 # define FOLD(c) ((flags & FNM_CASEFOLD) && ISUPPER ((unsigned char) (c)) \
65                   ? tolower ((unsigned char) (c)) \
66                   : (c))
67
68   while ((c = *p++) != '\0')
69     {
70       c = FOLD (c);
71
72       switch (c)
73         {
74         case '?':
75           if (*n == '\0')
76             return FNM_NOMATCH;
77           else if ((flags & FNM_FILE_NAME) && *n == '/')
78             return FNM_NOMATCH;
79           else if ((flags & FNM_PERIOD) && *n == '.' &&
80                    (n == string || ((flags & FNM_FILE_NAME) && n[-1] == '/')))
81             return FNM_NOMATCH;
82           break;
83
84         case '\\':
85           if (!(flags & FNM_NOESCAPE))
86             {
87               c = *p++;
88               if (c == '\0')
89                 /* Trailing \ loses.  */
90                 return FNM_NOMATCH;
91               c = FOLD (c);
92             }
93           if (FOLD (*n) != c)
94             return FNM_NOMATCH;
95           break;
96
97         case '*':
98           if ((flags & FNM_PERIOD) && *n == '.' &&
99               (n == string || ((flags & FNM_FILE_NAME) && n[-1] == '/')))
100             return FNM_NOMATCH;
101
102           for (c = *p++; c == '?' || c == '*'; c = *p++)
103             {
104               if (c == '?')
105                 {
106                   /* A ? needs to match one character.  */
107                   if (*n == '\0' || (*n == '/' && (flags & FNM_FILE_NAME)))
108                     /* There isn't another character; no match.  */
109                     return FNM_NOMATCH;
110                   else
111                     /* One character of the string is consumed in matching
112                        this ? wildcard, so *??? won't match if there are
113                        less than three characters.  */
114                     ++n;
115                 }
116             }
117
118           if (c == '\0')
119             {
120               if ((flags & (FNM_FILE_NAME | FNM_LEADING_DIR)) == FNM_FILE_NAME)
121                 for (; *n != '\0'; n++)
122                   if (*n == '/')
123                     return FNM_NOMATCH;
124               return 0;
125             }
126
127           {
128             char c1 = (!(flags & FNM_NOESCAPE) && c == '\\') ? *p : c;
129             c1 = FOLD (c1);
130             for (--p; *n != '\0'; ++n)
131               if ((c == '[' || FOLD (*n) == c1) &&
132                   fnmatch (p, n, flags & ~FNM_PERIOD) == 0)
133                 return 0;
134               else if (*n == '/' && (flags & FNM_FILE_NAME))
135                 break;
136             return FNM_NOMATCH;
137           }
138
139         case '[':
140           {
141             /* Nonzero if the sense of the character class is inverted.  */
142             register int not;
143
144             if (*n == '\0')
145               return FNM_NOMATCH;
146
147             if ((flags & FNM_PERIOD) && *n == '.' &&
148                 (n == string || ((flags & FNM_FILE_NAME) && n[-1] == '/')))
149               return FNM_NOMATCH;
150
151             not = (*p == '!' || *p == '^');
152             if (not)
153               ++p;
154
155             c = *p++;
156             for (;;)
157               {
158                 register char cstart = c, cend = c;
159
160                 if (!(flags & FNM_NOESCAPE) && c == '\\')
161                   {
162                     if (*p == '\0')
163                       return FNM_NOMATCH;
164                     cstart = cend = *p++;
165                   }
166
167                 cstart = cend = FOLD (cstart);
168
169                 if (c == '\0')
170                   /* [ (unterminated) loses.  */
171                   return FNM_NOMATCH;
172
173                 c = *p++;
174                 c = FOLD (c);
175
176                 if ((flags & FNM_FILE_NAME) && c == '/')
177                   /* [/] can never match.  */
178                   return FNM_NOMATCH;
179
180                 if (c == '-' && *p != ']')
181                   {
182                     cend = *p++;
183                     if (!(flags & FNM_NOESCAPE) && cend == '\\')
184                       cend = *p++;
185                     if (cend == '\0')
186                       return FNM_NOMATCH;
187                     cend = FOLD (cend);
188
189                     c = *p++;
190                   }
191
192                 if (FOLD (*n) >= cstart && FOLD (*n) <= cend)
193                   goto matched;
194
195                 if (c == ']')
196                   break;
197               }
198             if (!not)
199               return FNM_NOMATCH;
200             break;
201
202           matched:;
203             /* Skip the rest of the [...] that already matched.  */
204             while (c != ']')
205               {
206                 if (c == '\0')
207                   /* [... (unterminated) loses.  */
208                   return FNM_NOMATCH;
209
210                 c = *p++;
211                 if (!(flags & FNM_NOESCAPE) && c == '\\')
212                   {
213                     if (*p == '\0')
214                       return FNM_NOMATCH;
215                     /* XXX 1003.2d11 is unclear if this is right.  */
216                     ++p;
217                   }
218               }
219             if (not)
220               return FNM_NOMATCH;
221           }
222           break;
223
224         default:
225           if (c != FOLD (*n))
226             return FNM_NOMATCH;
227         }
228
229       ++n;
230     }
231
232   if (*n == '\0')
233     return 0;
234
235   if ((flags & FNM_LEADING_DIR) && *n == '/')
236     /* The FNM_LEADING_DIR flag says that "foo*" matches "foobar/frobozz".  */
237     return 0;
238
239   return FNM_NOMATCH;
240
241 # undef FOLD
242 }
243
244 #endif  /* _LIBC or not __GNU_LIBRARY__.  */