1/*	$NetBSD: fnmatch.c,v 1.1.1.1 2016/01/13 03:15:30 christos Exp $	*/
2
3/* Copyright 1991, 1992, 1993, 1996, 1997, 2000 Free Software Foundation, Inc.
4
5   This program is free software; you can redistribute it and/or modify
6   it under the terms of the GNU General Public License as published by
7   the Free Software Foundation; either version 2, or (at your option)
8   any later version.
9
10   This program is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   GNU General Public License for more details.
14
15  You should have received a copy of the GNU General Public License
16  along with this program; if not, write to the Free Software Foundation,
17  Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
18
19#if HAVE_CONFIG_H
20# include <config.h>
21#endif
22
23/* Enable GNU extensions in fnmatch.h.  */
24#ifndef _GNU_SOURCE
25# define _GNU_SOURCE	1
26#endif
27
28#include <errno.h>
29#include <fnmatch.h>
30#include <ctype.h>
31
32#if defined STDC_HEADERS || !defined isascii
33# define IN_CTYPE_DOMAIN(c) 1
34#else
35# define IN_CTYPE_DOMAIN(c) isascii (c)
36#endif
37
38#define ISUPPER(c) (IN_CTYPE_DOMAIN (c) && isupper (c))
39
40
41#ifndef errno
42extern int errno;
43#endif
44
45/* Match STRING against the filename pattern PATTERN, returning zero if
46   it matches, nonzero if not.  */
47int
48fnmatch (const char *pattern, const char *string, int flags)
49{
50  register const char *p = pattern, *n = string;
51  register char c;
52
53/* Note that this evaluates C many times.  */
54#define FOLD(c) ((flags & FNM_CASEFOLD) && ISUPPER ((unsigned char) (c)) \
55                 ? tolower ((unsigned char) (c)) \
56                 : (c))
57
58  while ((c = *p++) != '\0')
59    {
60      c = FOLD (c);
61
62      switch (c)
63	{
64	case '?':
65	  if (*n == '\0')
66	    return FNM_NOMATCH;
67	  else if ((flags & FNM_FILE_NAME) && *n == '/')
68	    return FNM_NOMATCH;
69	  else if ((flags & FNM_PERIOD) && *n == '.' &&
70		   (n == string || ((flags & FNM_FILE_NAME) && n[-1] == '/')))
71	    return FNM_NOMATCH;
72	  break;
73
74	case '\\':
75	  if (!(flags & FNM_NOESCAPE))
76	    {
77	      c = *p++;
78	      if (c == '\0')
79		/* Trailing \ loses.  */
80		return FNM_NOMATCH;
81	      c = FOLD (c);
82	    }
83	  if (FOLD (*n) != c)
84	    return FNM_NOMATCH;
85	  break;
86
87	case '*':
88	  if ((flags & FNM_PERIOD) && *n == '.' &&
89	      (n == string || ((flags & FNM_FILE_NAME) && n[-1] == '/')))
90	    return FNM_NOMATCH;
91
92	  for (c = *p++; c == '?' || c == '*'; c = *p++)
93	    {
94	      if (c == '?')
95		{
96		  /* A ? needs to match one character.  */
97		  if (*n == '\0' || (*n == '/' && (flags & FNM_FILE_NAME)))
98		    /* There isn't another character; no match.  */
99		    return FNM_NOMATCH;
100		  else
101		    /* One character of the string is consumed in matching
102		       this ? wildcard, so *??? won't match if there are
103		       less than three characters.  */
104		    ++n;
105		}
106	    }
107
108	  if (c == '\0')
109	    {
110	      if ((flags & (FNM_FILE_NAME | FNM_LEADING_DIR)) == FNM_FILE_NAME)
111		for (; *n != '\0'; n++)
112		  if (*n == '/')
113		    return FNM_NOMATCH;
114	      return 0;
115	    }
116
117	  {
118	    char c1 = (!(flags & FNM_NOESCAPE) && c == '\\') ? *p : c;
119	    c1 = FOLD (c1);
120	    for (--p; *n != '\0'; ++n)
121	      if ((c == '[' || FOLD (*n) == c1) &&
122		  fnmatch (p, n, flags & ~FNM_PERIOD) == 0)
123		return 0;
124	      else if (*n == '/' && (flags & FNM_FILE_NAME))
125		break;
126	    return FNM_NOMATCH;
127	  }
128
129	case '[':
130	  {
131	    /* Nonzero if the sense of the character class is inverted.  */
132	    register int not;
133
134	    if (*n == '\0')
135	      return FNM_NOMATCH;
136
137	    if ((flags & FNM_PERIOD) && *n == '.' &&
138		(n == string || ((flags & FNM_FILE_NAME) && n[-1] == '/')))
139	      return FNM_NOMATCH;
140
141	    not = (*p == '!' || *p == '^');
142	    if (not)
143	      ++p;
144
145	    c = *p++;
146	    for (;;)
147	      {
148		register char cstart = c, cend = c;
149
150		if (!(flags & FNM_NOESCAPE) && c == '\\')
151		  {
152		    if (*p == '\0')
153		      return FNM_NOMATCH;
154		    cstart = cend = *p++;
155		  }
156
157		cstart = cend = FOLD (cstart);
158
159		if (c == '\0')
160		  /* [ (unterminated) loses.  */
161		  return FNM_NOMATCH;
162
163		c = *p++;
164		c = FOLD (c);
165
166		if ((flags & FNM_FILE_NAME) && c == '/')
167		  /* [/] can never match.  */
168		  return FNM_NOMATCH;
169
170		if (c == '-' && *p != ']')
171		  {
172		    cend = *p++;
173		    if (!(flags & FNM_NOESCAPE) && cend == '\\')
174		      cend = *p++;
175		    if (cend == '\0')
176		      return FNM_NOMATCH;
177		    cend = FOLD (cend);
178
179		    c = *p++;
180		  }
181
182		if (FOLD (*n) >= cstart && FOLD (*n) <= cend)
183		  goto matched;
184
185		if (c == ']')
186		  break;
187	      }
188	    if (!not)
189	      return FNM_NOMATCH;
190	    break;
191
192	  matched:;
193	    /* Skip the rest of the [...] that already matched.  */
194	    while (c != ']')
195	      {
196		if (c == '\0')
197		  /* [... (unterminated) loses.  */
198		  return FNM_NOMATCH;
199
200		c = *p++;
201		if (!(flags & FNM_NOESCAPE) && c == '\\')
202		  {
203		    if (*p == '\0')
204		      return FNM_NOMATCH;
205		    /* XXX 1003.2d11 is unclear if this is right.  */
206		    ++p;
207		  }
208	      }
209	    if (not)
210	      return FNM_NOMATCH;
211	  }
212	  break;
213
214	default:
215	  if (c != FOLD (*n))
216	    return FNM_NOMATCH;
217	}
218
219      ++n;
220    }
221
222  if (*n == '\0')
223    return 0;
224
225  if ((flags & FNM_LEADING_DIR) && *n == '/')
226    /* The FNM_LEADING_DIR flag says that "foo*" matches "foobar/frobozz".  */
227    return 0;
228
229  return FNM_NOMATCH;
230
231#undef FOLD
232}
233