xref: /netbsd-src/external/gpl2/diffutils/dist/lib/fnmatch.c (revision 76c7fc5f6b13ed0b1508e6b313e88e59977ed78e)
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
42 extern int errno;
43 #endif
44 
45 /* Match STRING against the filename pattern PATTERN, returning zero if
46    it matches, nonzero if not.  */
47 int
48 fnmatch (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