Imported from ../bash-1.14.7.tar.gz.
[platform/upstream/bash.git] / lib / glob / fnmatch.c
1 /* Copyright (C) 1991 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3
4 The GNU C 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 The GNU C 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 the GNU C Library; see the file COPYING.LIB.  If
16 not, write to the Free Software Foundation, Inc., 675 Mass Ave,
17 Cambridge, MA 02139, USA.  */
18
19 #include <errno.h>
20 #include "fnmatch.h"
21
22 #if !defined (__GNU_LIBRARY__) && !defined (STDC_HEADERS)
23 #  if !defined (errno)
24 extern int errno;
25 #  endif /* !errno */
26 #endif
27
28 /* Match STRING against the filename pattern PATTERN, returning zero if
29    it matches, FNM_NOMATCH if not.  */
30 int
31 fnmatch (pattern, string, flags)
32      char *pattern;
33      char *string;
34      int flags;
35 {
36   register char *p = pattern, *n = string;
37   register char c;
38
39   if ((flags & ~__FNM_FLAGS) != 0)
40     {
41       errno = EINVAL;
42       return (-1);
43     }
44
45   while ((c = *p++) != '\0')
46     {
47       switch (c)
48         {
49         case '?':
50           if (*n == '\0')
51             return (FNM_NOMATCH);
52           else if ((flags & FNM_PATHNAME) && *n == '/')
53             return (FNM_NOMATCH);
54           else if ((flags & FNM_PERIOD) && *n == '.' &&
55                    (n == string || ((flags & FNM_PATHNAME) && n[-1] == '/')))
56             return (FNM_NOMATCH);
57           break;
58
59         case '\\':
60           if (!(flags & FNM_NOESCAPE))
61             c = *p++;
62           if (*n != c)
63             return (FNM_NOMATCH);
64           break;
65
66         case '*':
67           if ((flags & FNM_PERIOD) && *n == '.' &&
68               (n == string || ((flags & FNM_PATHNAME) && n[-1] == '/')))
69             return (FNM_NOMATCH);
70
71           for (c = *p++; c == '?' || c == '*'; c = *p++, ++n)
72             if (((flags & FNM_PATHNAME) && *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             for (--p; *n != '\0'; ++n)
82               if ((c == '[' || *n == c1) &&
83                   fnmatch (p, n, flags & ~FNM_PERIOD) == 0)
84                 return (0);
85             return (FNM_NOMATCH);
86           }
87
88         case '[':
89           {
90             /* Nonzero if the sense of the character class is inverted.  */
91             register int not;
92
93             if (*n == '\0')
94               return (FNM_NOMATCH);
95
96             if ((flags & FNM_PERIOD) && *n == '.' &&
97                 (n == string || ((flags & FNM_PATHNAME) && n[-1] == '/')))
98               return (FNM_NOMATCH);
99
100             /* Make sure there is a closing `]'.  If there isn't, the `['
101                is just a character to be matched. */
102             {
103               register char *np;
104
105               for (np = p; np && *np && *np != ']'; np++);
106
107               if (np && !*np)
108                 {
109                   if (*n != '[')
110                     return (FNM_NOMATCH);
111                   goto next_char;
112                 }
113             }
114               
115             not = (*p == '!' || *p == '^');
116             if (not)
117               ++p;
118
119             c = *p++;
120             for (;;)
121               {
122                 register char cstart = c, cend = c;
123
124                 if (!(flags & FNM_NOESCAPE) && c == '\\')
125                   cstart = cend = *p++;
126
127                 if (c == '\0')
128                   /* [ (unterminated) loses.  */
129                   return (FNM_NOMATCH);
130
131                 c = *p++;
132
133                 if ((flags & FNM_PATHNAME) && c == '/')
134                   /* [/] can never match.  */
135                   return (FNM_NOMATCH);
136
137                 if (c == '-' && *p != ']')
138                   {
139                     cend = *p++;
140                     if (!(flags & FNM_NOESCAPE) && cend == '\\')
141                       cend = *p++;
142                     if (cend == '\0')
143                       return (FNM_NOMATCH);
144                     c = *p++;
145                   }
146
147                 if (*n >= cstart && *n <= cend)
148                   goto matched;
149
150                 if (c == ']')
151                   break;
152               }
153             if (!not)
154               return (FNM_NOMATCH);
155
156           next_char:
157             break;
158
159           matched:
160             /* Skip the rest of the [...] that already matched.  */
161             while (c != ']')
162               {
163                 if (c == '\0')
164                   /* [... (unterminated) loses.  */
165                   return (FNM_NOMATCH);
166
167                 c = *p++;
168                 if (!(flags & FNM_NOESCAPE) && c == '\\')
169                   /* 1003.2d11 is unclear if this is right.  %%% */
170                   ++p;
171               }
172             if (not)
173               return (FNM_NOMATCH);
174           }
175           break;
176
177         default:
178           if (c != *n)
179             return (FNM_NOMATCH);
180         }
181
182       ++n;
183     }
184
185   if (*n == '\0')
186     return (0);
187
188   return (FNM_NOMATCH);
189 }