71d32046214d5388fba64c02e1ba39b7d8c7a9cc
[kopensolaris-gnu/glibc.git] / posix / fnmatch.c
1 /* Copyright (C) 1991, 1992, 1993 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 #ifdef HAVE_CONFIG_H
19 #include "config.h"
20 #endif
21
22 #include <errno.h>
23 #include <fnmatch.h>
24 #include <ctype.h>
25
26 #if !defined(__GNU_LIBRARY__) && !defined(STDC_HEADERS)
27 extern int errno;
28 #endif
29
30 /* Match STRING against the filename pattern PATTERN, returning zero if
31    it matches, nonzero if not.  */
32 int
33 fnmatch (pattern, string, flags)
34      const char *pattern;
35      const char *string;
36      int flags;
37 {
38   register const char *p = pattern, *n = string;
39   register char c;
40
41 /* Note that this evalutes C many times.  */
42 #define FOLD(c) ((flags & FNM_CASEFOLD) && isupper (c) ? tolower (c) : (c))
43
44   while ((c = *p++) != '\0')
45     {
46       c = FOLD (c);
47
48       switch (c)
49         {
50         case '?':
51           if (*n == '\0')
52             return FNM_NOMATCH;
53           else if ((flags & FNM_FILE_NAME) && *n == '/')
54             return FNM_NOMATCH;
55           else if ((flags & FNM_PERIOD) && *n == '.' &&
56                    (n == string || ((flags & FNM_FILE_NAME) && n[-1] == '/')))
57             return FNM_NOMATCH;
58           break;
59
60         case '\\':
61           if (!(flags & FNM_NOESCAPE))
62             {
63               c = *p++;
64               c = FOLD (c);
65             }
66           if (FOLD (*n) != c)
67             return FNM_NOMATCH;
68           break;
69
70         case '*':
71           if ((flags & FNM_PERIOD) && *n == '.' &&
72               (n == string || ((flags & FNM_FILE_NAME) && n[-1] == '/')))
73             return FNM_NOMATCH;
74
75           for (c = *p++; c == '?' || c == '*'; c = *p++, ++n)
76             if (((flags & FNM_FILE_NAME) && *n == '/') ||
77                 (c == '?' && *n == '\0'))
78               return FNM_NOMATCH;
79
80           if (c == '\0')
81             return 0;
82
83           {
84             char c1 = (!(flags & FNM_NOESCAPE) && c == '\\') ? *p : c;
85             c1 = FOLD (c1);
86             for (--p; *n != '\0'; ++n)
87               if ((c == '[' || FOLD (*n) == c1) &&
88                   fnmatch (p, n, flags & ~FNM_PERIOD) == 0)
89                 return 0;
90             return FNM_NOMATCH;
91           }
92
93         case '[':
94           {
95             /* Nonzero if the sense of the character class is inverted.  */
96             register int not;
97
98             if (*n == '\0')
99               return FNM_NOMATCH;
100
101             if ((flags & FNM_PERIOD) && *n == '.' &&
102                 (n == string || ((flags & FNM_FILE_NAME) && n[-1] == '/')))
103               return FNM_NOMATCH;
104
105             not = (*p == '!' || *p == '^');
106             if (not)
107               ++p;
108
109             c = *p++;
110             for (;;)
111               {
112                 register char cstart = c, cend = c;
113
114                 if (!(flags & FNM_NOESCAPE) && c == '\\')
115                   cstart = cend = *p++;
116
117                 cstart = cend = FOLD (cstart);
118
119                 if (c == '\0')
120                   /* [ (unterminated) loses.  */
121                   return FNM_NOMATCH;
122
123                 c = *p++;
124                 c = FOLD (c);
125
126                 if ((flags & FNM_FILE_NAME) && c == '/')
127                   /* [/] can never match.  */
128                   return FNM_NOMATCH;
129
130                 if (c == '-' && *p != ']')
131                   {
132                     cend = *p++;
133                     if (!(flags & FNM_NOESCAPE) && cend == '\\')
134                       cend = *p++;
135                     if (cend == '\0')
136                       return FNM_NOMATCH;
137                     cend = FOLD (cend);
138
139                     c = *p++;
140                   }
141
142                 if (FOLD (*n) >= cstart && FOLD (*n) <= cend)
143                   goto matched;
144
145                 if (c == ']')
146                   break;
147               }
148             if (!not)
149               return FNM_NOMATCH;
150             break;
151
152           matched:;
153             /* Skip the rest of the [...] that already matched.  */
154             while (c != ']')
155               {
156                 if (c == '\0')
157                   /* [... (unterminated) loses.  */
158                   return FNM_NOMATCH;
159
160                 c = *p++;
161                 if (!(flags & FNM_NOESCAPE) && c == '\\')
162                   /* XXX 1003.2d11 is unclear if this is right.  */
163                   ++p;
164               }
165             if (not)
166               return FNM_NOMATCH;
167           }
168           break;
169
170         default:
171           if (c != FOLD (*n))
172             return FNM_NOMATCH;
173         }
174
175       ++n;
176     }
177
178   if (*n == '\0')
179     return 0;
180
181   if ((flags & FNM_LEADING_DIR) && *n == '/')
182     /* The FNM_LEADING_DIR flag says that "foo*" matches "foobar/frobozz".  */
183     return 0;
184
185   return FNM_NOMATCH;
186 }