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