(fnmatch): Always pass unsigned char values to FOLD macro.
[kopensolaris-gnu/glibc.git] / posix / fnmatch.c
1 /* Copyright (C) 1991, 92, 93, 96, 97, 98 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3
4    This library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Library General Public License as
6    published by the Free Software Foundation; either version 2 of the
7    License, or (at your option) any later version.
8
9    This library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Library General Public License for more details.
13
14    You should have received a copy of the GNU Library General Public
15    License along with this library; see the file COPYING.LIB.  If not,
16    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17    Boston, MA 02111-1307, USA.  */
18
19 #if HAVE_CONFIG_H
20 # include <config.h>
21 #endif
22
23 /* Enable GNU extensions in fnmatch.h.  */
24 #ifndef _GNU_SOURCE
25 # define _GNU_SOURCE    1
26 #endif
27
28 #include <errno.h>
29 #include <fnmatch.h>
30 #include <ctype.h>
31
32 #if HAVE_STRING_H
33 # include <string.h>
34 #else
35 # include <strings.h>
36 #endif
37
38 #if defined STDC_HEADERS || defined _LIBC
39 # include <stdlib.h>
40 #endif
41
42 /* For platform which support the ISO C amendement 1 functionality we
43    support user defined character classes.  */
44 #if defined _LIBC || (defined HAVE_WCTYPE_H && defined HAVE_WCHAR_H)
45 /* Solaris 2.5 has a bug: <wchar.h> must be included before <wctype.h>.  */
46 # include <wchar.h>
47 # include <wctype.h>
48 #endif
49
50 /* Comment out all this code if we are using the GNU C Library, and are not
51    actually compiling the library itself.  This code is part of the GNU C
52    Library, but also included in many other GNU distributions.  Compiling
53    and linking in this code is a waste when using the GNU C library
54    (especially if it is a shared library).  Rather than having every GNU
55    program understand `configure --with-gnu-libc' and omit the object files,
56    it is simpler to just do this in the source for each such file.  */
57
58 #if defined _LIBC || !defined __GNU_LIBRARY__
59
60
61 # if defined STDC_HEADERS || !defined isascii
62 #  define ISASCII(c) 1
63 # else
64 #  define ISASCII(c) isascii(c)
65 # endif
66
67 #ifdef isblank
68 # define ISBLANK(c) (ISASCII (c) && isblank (c))
69 #else
70 # define ISBLANK(c) ((c) == ' ' || (c) == '\t')
71 #endif
72 #ifdef isgraph
73 # define ISGRAPH(c) (ISASCII (c) && isgraph (c))
74 #else
75 # define ISGRAPH(c) (ISASCII (c) && isprint (c) && !isspace (c))
76 #endif
77
78 #define ISPRINT(c) (ISASCII (c) && isprint (c))
79 #define ISDIGIT(c) (ISASCII (c) && isdigit (c))
80 #define ISALNUM(c) (ISASCII (c) && isalnum (c))
81 #define ISALPHA(c) (ISASCII (c) && isalpha (c))
82 #define ISCNTRL(c) (ISASCII (c) && iscntrl (c))
83 #define ISLOWER(c) (ISASCII (c) && islower (c))
84 #define ISPUNCT(c) (ISASCII (c) && ispunct (c))
85 #define ISSPACE(c) (ISASCII (c) && isspace (c))
86 #define ISUPPER(c) (ISASCII (c) && isupper (c))
87 #define ISXDIGIT(c) (ISASCII (c) && isxdigit (c))
88
89 # define STREQ(s1, s2) ((strcmp (s1, s2) == 0))
90
91 # if defined _LIBC || (defined HAVE_WCTYPE_H && defined HAVE_WCHAR_H)
92 /* The GNU C library provides support for user-defined character classes
93    and the functions from ISO C amendement 1.  */
94 #  ifdef CHARCLASS_NAME_MAX
95 #   define CHAR_CLASS_MAX_LENGTH CHARCLASS_NAME_MAX
96 #  else
97 /* This shouldn't happen but some implementation might still have this
98    problem.  Use a reasonable default value.  */
99 #   define CHAR_CLASS_MAX_LENGTH 256
100 #  endif
101
102 #  ifdef _LIBC
103 #   define IS_CHAR_CLASS(string) __wctype (string)
104 #  else
105 #   define IS_CHAR_CLASS(string) wctype (string)
106 #  endif
107 # else
108 #  define CHAR_CLASS_MAX_LENGTH  6 /* Namely, `xdigit'.  */
109
110 #  define IS_CHAR_CLASS(string)                                               \
111    (STREQ (string, "alpha") || STREQ (string, "upper")                        \
112     || STREQ (string, "lower") || STREQ (string, "digit")                     \
113     || STREQ (string, "alnum") || STREQ (string, "xdigit")                    \
114     || STREQ (string, "space") || STREQ (string, "print")                     \
115     || STREQ (string, "punct") || STREQ (string, "graph")                     \
116     || STREQ (string, "cntrl") || STREQ (string, "blank"))
117 # endif
118
119 /* Avoid depending on library functions or files
120    whose names are inconsistent.  */
121
122 # if !defined _LIBC && !defined getenv
123 extern char *getenv ();
124 # endif
125
126 # ifndef errno
127 extern int errno;
128 # endif
129
130 /* Match STRING against the filename pattern PATTERN, returning zero if
131    it matches, nonzero if not.  */
132 int
133 fnmatch (pattern, string, flags)
134      const char *pattern;
135      const char *string;
136      int flags;
137 {
138   register const char *p = pattern, *n = string;
139   register unsigned char c;
140
141 /* Note that this evaluates C many times.  */
142 # ifdef _LIBC
143 #  define FOLD(c) ((flags & FNM_CASEFOLD) ? tolower (c) : (c))
144 # else
145 #  define FOLD(c) ((flags & FNM_CASEFOLD) && ISUPPER (c) ? tolower (c) : (c))
146 # endif
147
148   while ((c = *p++) != '\0')
149     {
150       c = FOLD (c);
151
152       switch (c)
153         {
154         case '?':
155           if (*n == '\0')
156             return FNM_NOMATCH;
157           else if ((flags & FNM_FILE_NAME) && *n == '/')
158             return FNM_NOMATCH;
159           else if ((flags & FNM_PERIOD) && *n == '.' &&
160                    (n == string || ((flags & FNM_FILE_NAME) && n[-1] == '/')))
161             return FNM_NOMATCH;
162           break;
163
164         case '\\':
165           if (!(flags & FNM_NOESCAPE))
166             {
167               c = *p++;
168               if (c == '\0')
169                 /* Trailing \ loses.  */
170                 return FNM_NOMATCH;
171               c = FOLD (c);
172             }
173           if (FOLD ((unsigned char) *n) != c)
174             return FNM_NOMATCH;
175           break;
176
177         case '*':
178           if ((flags & FNM_PERIOD) && *n == '.' &&
179               (n == string || ((flags & FNM_FILE_NAME) && n[-1] == '/')))
180             return FNM_NOMATCH;
181
182           for (c = *p++; c == '?' || c == '*'; c = *p++)
183             {
184               if ((flags & FNM_FILE_NAME) && *n == '/')
185                 /* A slash does not match a wildcard under FNM_FILE_NAME.  */
186                 return FNM_NOMATCH;
187               else if (c == '?')
188                 {
189                   /* A ? needs to match one character.  */
190                   if (*n == '\0')
191                     /* There isn't another character; no match.  */
192                     return FNM_NOMATCH;
193                   else
194                     /* One character of the string is consumed in matching
195                        this ? wildcard, so *??? won't match if there are
196                        less than three characters.  */
197                     ++n;
198                 }
199             }
200
201           if (c == '\0')
202             return 0;
203
204           {
205             unsigned char c1 = (!(flags & FNM_NOESCAPE) && c == '\\') ? *p : c;
206             c1 = FOLD (c1);
207             for (--p; *n != '\0'; ++n)
208               if ((c == '[' || FOLD ((unsigned char) *n) == c1) &&
209                   fnmatch (p, n, flags & ~FNM_PERIOD) == 0)
210                 return 0;
211             return FNM_NOMATCH;
212           }
213
214         case '[':
215           {
216             /* Nonzero if the sense of the character class is inverted.  */
217             static int posixly_correct;
218             register int not;
219             char cold;
220
221             if (posixly_correct == 0)
222               posixly_correct = getenv ("POSIXLY_CORRECT") != NULL ? 1 : -1;
223
224             if (*n == '\0')
225               return FNM_NOMATCH;
226
227             if (*n == '.' && (flags & FNM_PERIOD) &&
228                 (n == string || ((flags & FNM_FILE_NAME) && n[-1] == '/')))
229               return FNM_NOMATCH;
230
231             if (*n == '/' && (flags & FNM_FILE_NAME))
232               /* `/' cannot be matched.  */
233               return FNM_NOMATCH;
234
235             not = (*p == '!' || (posixly_correct < 0 && *p == '^'));
236             if (not)
237               ++p;
238
239             c = *p++;
240             for (;;)
241               {
242                 unsigned int fn = FOLD (*n);
243
244                 if (!(flags & FNM_NOESCAPE) && c == '\\')
245                   {
246                     if (*p == '\0')
247                       return FNM_NOMATCH;
248                     c = FOLD ((unsigned char) *p);
249                     ++p;
250
251                     if (c == fn)
252                       goto matched;
253                   }
254                 else if (c == '[' && *p == ':')
255                   {
256                     /* Leave room for the null.  */
257                     char str[CHAR_CLASS_MAX_LENGTH + 1];
258                     size_t c1 = 0;
259 # if defined _LIBC || (defined HAVE_WCTYPE_H && defined HAVE_WCHAR_H)
260                     wctype_t wt;
261 # endif
262
263                     for (;;)
264                       {
265                         if (c1 == CHAR_CLASS_MAX_LENGTH)
266                           /* The name is too long and therefore the pattern
267                              is ill-formed.  */
268                           return FNM_NOMATCH;
269
270                         c = *++p;
271                         if (c == ':' && p[1] == ']')
272                           {
273                             p += 2;
274                             break;
275                           }
276                         str[c1++] = 'c';
277                       }
278                     str[c1] = '\0';
279
280 # if defined _LIBC || (defined HAVE_WCTYPE_H && defined HAVE_WCHAR_H)
281                     wt = IS_CHAR_CLASS (str);
282                     if (wt == 0)
283                       /* Invalid character class name.  */
284                       return FNM_NOMATCH;
285
286                     if (__iswctype (__btowc (*n), wt))
287                       goto matched;
288 # else
289                     if ((STREQ (str, "alnum") && ISALNUM (*n))
290                         || (STREQ (str, "alpha") && ISALPHA (*n))
291                         || (STREQ (str, "blank") && ISBLANK (*n))
292                         || (STREQ (str, "cntrl") && ISCNTRL (*n))
293                         || (STREQ (str, "digit") && ISDIGIT (*n))
294                         || (STREQ (str, "graph") && ISGRAPH (*n))
295                         || (STREQ (str, "lower") && ISLOWER (*n))
296                         || (STREQ (str, "print") && ISPRINT (*n))
297                         || (STREQ (str, "punct") && ISPUNCT (*n))
298                         || (STREQ (str, "space") && ISSPACE (*n))
299                         || (STREQ (str, "upper") && ISUPPER (*n))
300                         || (STREQ (str, "xdigit") && ISXDIGIT (*n)))
301                       goto matched;
302 # endif
303                   }
304                 else if (c == '\0')
305                   /* [ (unterminated) loses.  */
306                   return FNM_NOMATCH;
307                 else if (FOLD (c) == fn)
308                   goto matched;
309
310                 cold = c;
311                 c = *p++;
312
313                 if (c == '-' && *p != ']')
314                   {
315                     /* It is a range.  */
316                     unsigned char cend = *p++;
317                     if (!(flags & FNM_NOESCAPE) && cend == '\\')
318                       cend = *p++;
319                     if (cend == '\0')
320                       return FNM_NOMATCH;
321
322                     if (cold <= fn && fn <= FOLD (cend))
323                       goto matched;
324
325                     c = *p++;
326                   }
327                 if (c == ']')
328                   break;
329               }
330
331             if (!not)
332               return FNM_NOMATCH;
333             break;
334
335           matched:
336             /* Skip the rest of the [...] that already matched.  */
337             while (c != ']')
338               {
339                 if (c == '\0')
340                   /* [... (unterminated) loses.  */
341                   return FNM_NOMATCH;
342
343                 c = *p++;
344                 if (!(flags & FNM_NOESCAPE) && c == '\\')
345                   {
346                     if (*p == '\0')
347                       return FNM_NOMATCH;
348                     /* XXX 1003.2d11 is unclear if this is right.  */
349                     ++p;
350                   }
351                 else if (c == '[' && *p == ':')
352                   {
353                     do
354                       if (*++p == '\0')
355                         return FNM_NOMATCH;
356                     while (*p != ':' || p[1] == ']');
357                     p += 2;
358                     c = *p;
359                   }
360               }
361             if (not)
362               return FNM_NOMATCH;
363           }
364           break;
365
366         default:
367           if (c != FOLD ((unsigned char) *n))
368             return FNM_NOMATCH;
369         }
370
371       ++n;
372     }
373
374   if (*n == '\0')
375     return 0;
376
377   if ((flags & FNM_LEADING_DIR) && *n == '/')
378     /* The FNM_LEADING_DIR flag says that "foo*" matches "foobar/frobozz".  */
379     return 0;
380
381   return FNM_NOMATCH;
382
383 # undef FOLD
384 }
385
386 #endif  /* _LIBC or not __GNU_LIBRARY__.  */