Initial revision
[gnulib.git] / lib / fnmatch.c
1 /* Copyright (C) 1991, 1992 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
21 #if !defined(__GNU_LIBRARY__) && !defined(STDC_HEADERS)
22 extern int errno;
23 #endif
24
25 #if !__STDC__
26 #define const
27 #endif
28
29 /* Match STRING against the filename pattern PATTERN, returning zero if
30    it matches, nonzero if not.  */
31 int
32 fnmatch (pattern, string, flags)
33      const char *pattern;
34      const char *string;
35      int flags;
36 {
37   register const char *p = pattern, *n = string;
38   register char c;
39
40   if ((flags & ~__FNM_FLAGS) != 0)
41     {
42       errno = EINVAL;
43       return -1;
44     }
45
46   while ((c = *p++) != '\0')
47     {
48       switch (c)
49         {
50         case '?':
51           if (*n == '\0')
52             return FNM_NOMATCH;
53           else if ((flags & FNM_PATHNAME) && *n == '/')
54             return FNM_NOMATCH;
55           else if ((flags & FNM_PERIOD) && *n == '.' &&
56                    (n == string || ((flags & FNM_PATHNAME) && n[-1] == '/')))
57             return FNM_NOMATCH;
58           break;
59
60         case '\\':
61           if (!(flags & FNM_NOESCAPE))
62             c = *p++;
63           if (*n != c)
64             return FNM_NOMATCH;
65           break;
66
67         case '*':
68           if ((flags & FNM_PERIOD) && *n == '.' &&
69               (n == string || ((flags & FNM_PATHNAME) && n[-1] == '/')))
70             return FNM_NOMATCH;
71
72           for (c = *p++; c == '?' || c == '*'; c = *p++, ++n)
73             if (((flags & FNM_PATHNAME) && *n == '/') ||
74                 (c == '?' && *n == '\0'))
75               return FNM_NOMATCH;
76
77           if (c == '\0')
78             return 0;
79
80           {
81             char c1 = (!(flags & FNM_NOESCAPE) && c == '\\') ? *p : c;
82             for (--p; *n != '\0'; ++n)
83               if ((c == '[' || *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_PATHNAME) && 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                 if (c == '\0')
114                   /* [ (unterminated) loses.  */
115                   return FNM_NOMATCH;
116
117                 c = *p++;
118
119                 if ((flags & FNM_PATHNAME) && c == '/')
120                   /* [/] can never match.  */
121                   return FNM_NOMATCH;
122
123                 if (c == '-' && *p != ']')
124                   {
125                     cend = *p++;
126                     if (!(flags & FNM_NOESCAPE) && cend == '\\')
127                       cend = *p++;
128                     if (cend == '\0')
129                       return FNM_NOMATCH;
130                     c = *p++;
131                   }
132
133                 if (*n >= cstart && *n <= cend)
134                   goto matched;
135
136                 if (c == ']')
137                   break;
138               }
139             if (!not)
140               return FNM_NOMATCH;
141             break;
142
143           matched:;
144             /* Skip the rest of the [...] that already matched.  */
145             while (c != ']')
146               {
147                 if (c == '\0')
148                   /* [... (unterminated) loses.  */
149                   return FNM_NOMATCH;
150
151                 c = *p++;
152                 if (!(flags & FNM_NOESCAPE) && c == '\\')
153                   /* 1003.2d11 is unclear if this is right.  %%% */
154                   ++p;
155               }
156             if (not)
157               return FNM_NOMATCH;
158           }
159           break;
160
161         default:
162           if (c != *n)
163             return FNM_NOMATCH;
164         }
165
166       ++n;
167     }
168
169   if (*n == '\0' || ((flags & FNM_TARPATH) && *n == '/'))
170     return 0;
171
172   return FNM_NOMATCH;
173 }