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
40extern int errno;
41#endif
42
43/* Match STRING against the filename pattern PATTERN, returning zero if
44   it matches, nonzero if not.  */
45int
46fnmatch (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}
231