Update from libit.
[gnulib.git] / lib / fnmatch.c
1 /* Copyright (C) 1991, 1992, 1993, 1996, 1997 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 ISASCII(c) 1
44 # else
45 #  define ISASCII(c) isascii(c)
46 # endif
47
48 # define ISUPPER(c) (ISASCII (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 (pattern, string, flags)
59      const char *pattern;
60      const char *string;
61      int flags;
62 {
63   register const char *p = pattern, *n = string;
64   register char c;
65
66 /* Note that this evaluates C many times.  */
67 # define FOLD(c) ((flags & FNM_CASEFOLD) && ISUPPER (c) ? tolower (c) : (c))
68
69   while ((c = *p++) != '\0')
70     {
71       c = FOLD (c);
72
73       switch (c)
74         {
75         case '?':
76           if (*n == '\0')
77             return FNM_NOMATCH;
78           else if ((flags & FNM_FILE_NAME) && *n == '/')
79             return FNM_NOMATCH;
80           else if ((flags & FNM_PERIOD) && *n == '.' &&
81                    (n == string || ((flags & FNM_FILE_NAME) && n[-1] == '/')))
82             return FNM_NOMATCH;
83           break;
84
85         case '\\':
86           if (!(flags & FNM_NOESCAPE))
87             {
88               c = *p++;
89               if (c == '\0')
90                 /* Trailing \ loses.  */
91                 return FNM_NOMATCH;
92               c = FOLD (c);
93             }
94           if (FOLD (*n) != c)
95             return FNM_NOMATCH;
96           break;
97
98         case '*':
99           if ((flags & FNM_PERIOD) && *n == '.' &&
100               (n == string || ((flags & FNM_FILE_NAME) && n[-1] == '/')))
101             return FNM_NOMATCH;
102
103           for (c = *p++; c == '?' || c == '*'; c = *p++)
104             {
105               if ((flags & FNM_FILE_NAME) && *n == '/')
106                 /* A slash does not match a wildcard under FNM_FILE_NAME.  */
107                 return FNM_NOMATCH;
108               else if (c == '?')
109                 {
110                   /* A ? needs to match one character.  */
111                   if (*n == '\0')
112                     /* There isn't another character; no match.  */
113                     return FNM_NOMATCH;
114                   else
115                     /* One character of the string is consumed in matching
116                        this ? wildcard, so *??? won't match if there are
117                        less than three characters.  */
118                     ++n;
119                 }
120             }
121
122           if (c == '\0')
123             return 0;
124
125           {
126             char c1 = (!(flags & FNM_NOESCAPE) && c == '\\') ? *p : c;
127             c1 = FOLD (c1);
128             for (--p; *n != '\0'; ++n)
129               if ((c == '[' || FOLD (*n) == c1) &&
130                   fnmatch (p, n, flags & ~FNM_PERIOD) == 0)
131                 return 0;
132             return FNM_NOMATCH;
133           }
134
135         case '[':
136           {
137             /* Nonzero if the sense of the character class is inverted.  */
138             register int not;
139
140             if (*n == '\0')
141               return FNM_NOMATCH;
142
143             if ((flags & FNM_PERIOD) && *n == '.' &&
144                 (n == string || ((flags & FNM_FILE_NAME) && n[-1] == '/')))
145               return FNM_NOMATCH;
146
147             not = (*p == '!' || *p == '^');
148             if (not)
149               ++p;
150
151             c = *p++;
152             for (;;)
153               {
154                 register char cstart = c, cend = c;
155
156                 if (!(flags & FNM_NOESCAPE) && c == '\\')
157                   {
158                     if (*p == '\0')
159                       return FNM_NOMATCH;
160                     cstart = cend = *p++;
161                   }
162
163                 cstart = cend = FOLD (cstart);
164
165                 if (c == '\0')
166                   /* [ (unterminated) loses.  */
167                   return FNM_NOMATCH;
168
169                 c = *p++;
170                 c = FOLD (c);
171
172                 if ((flags & FNM_FILE_NAME) && c == '/')
173                   /* [/] can never match.  */
174                   return FNM_NOMATCH;
175
176                 if (c == '-' && *p != ']')
177                   {
178                     cend = *p++;
179                     if (!(flags & FNM_NOESCAPE) && cend == '\\')
180                       cend = *p++;
181                     if (cend == '\0')
182                       return FNM_NOMATCH;
183                     cend = FOLD (cend);
184
185                     c = *p++;
186                   }
187
188                 if (FOLD (*n) >= cstart && FOLD (*n) <= cend)
189                   goto matched;
190
191                 if (c == ']')
192                   break;
193               }
194             if (!not)
195               return FNM_NOMATCH;
196             break;
197
198           matched:;
199             /* Skip the rest of the [...] that already matched.  */
200             while (c != ']')
201               {
202                 if (c == '\0')
203                   /* [... (unterminated) loses.  */
204                   return FNM_NOMATCH;
205
206                 c = *p++;
207                 if (!(flags & FNM_NOESCAPE) && c == '\\')
208                   {
209                     if (*p == '\0')
210                       return FNM_NOMATCH;
211                     /* XXX 1003.2d11 is unclear if this is right.  */
212                     ++p;
213                   }
214               }
215             if (not)
216               return FNM_NOMATCH;
217           }
218           break;
219
220         default:
221           if (c != FOLD (*n))
222             return FNM_NOMATCH;
223         }
224
225       ++n;
226     }
227
228   if (*n == '\0')
229     return 0;
230
231   if ((flags & FNM_LEADING_DIR) && *n == '/')
232     /* The FNM_LEADING_DIR flag says that "foo*" matches "foobar/frobozz".  */
233     return 0;
234
235   return FNM_NOMATCH;
236
237 # undef FOLD
238 }
239
240 #endif  /* _LIBC or not __GNU_LIBRARY__.  */