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