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