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