1 /* $OpenBSD: fnmatch.c,v 1.14 2008/10/01 23:04:13 millert Exp $ */ 2 3 /* 4 * Copyright (c) 1989, 1993, 1994 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * Guido van Rossum. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. Neither the name of the University nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 /* 36 * Function fnmatch() as specified in POSIX 1003.2-1992, section B.6. 37 * Compares a filename or pathname to a pattern. 38 */ 39 40 #include <ctype.h> 41 #include <stdio.h> 42 #include <string.h> 43 #include <fnmatch.h> 44 45 #include "charclass.h" 46 47 #define EOS '\0' 48 49 #define RANGE_MATCH 1 50 #define RANGE_NOMATCH 0 51 #define RANGE_ERROR (-1) 52 53 static int rangematch(const char *, char, int, char **); 54 static int classmatch(const char *, char, int, const char **); 55 56 int 57 fnmatch(const char *pattern, const char *string, int flags) 58 { 59 const char *stringstart; 60 char *newp; 61 char c, test; 62 63 for (stringstart = string;;) 64 switch (c = *pattern++) { 65 case EOS: 66 if ((flags & FNM_LEADING_DIR) && *string == '/') 67 return (0); 68 return (*string == EOS ? 0 : FNM_NOMATCH); 69 case '?': 70 if (*string == EOS) 71 return (FNM_NOMATCH); 72 if (*string == '/' && (flags & FNM_PATHNAME)) 73 return (FNM_NOMATCH); 74 if (*string == '.' && (flags & FNM_PERIOD) && 75 (string == stringstart || 76 ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) 77 return (FNM_NOMATCH); 78 ++string; 79 break; 80 case '*': 81 c = *pattern; 82 /* Collapse multiple stars. */ 83 while (c == '*') 84 c = *++pattern; 85 86 if (*string == '.' && (flags & FNM_PERIOD) && 87 (string == stringstart || 88 ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) 89 return (FNM_NOMATCH); 90 91 /* Optimize for pattern with * at end or before /. */ 92 if (c == EOS) { 93 if (flags & FNM_PATHNAME) 94 return ((flags & FNM_LEADING_DIR) || 95 strchr(string, '/') == NULL ? 96 0 : FNM_NOMATCH); 97 else 98 return (0); 99 } else if (c == '/' && (flags & FNM_PATHNAME)) { 100 if ((string = strchr(string, '/')) == NULL) 101 return (FNM_NOMATCH); 102 break; 103 } 104 105 /* General case, use recursion. */ 106 while ((test = *string) != EOS) { 107 if (!fnmatch(pattern, string, flags & ~FNM_PERIOD)) 108 return (0); 109 if (test == '/' && (flags & FNM_PATHNAME)) 110 break; 111 ++string; 112 } 113 return (FNM_NOMATCH); 114 case '[': 115 if (*string == EOS) 116 return (FNM_NOMATCH); 117 if (*string == '/' && (flags & FNM_PATHNAME)) 118 return (FNM_NOMATCH); 119 if (*string == '.' && (flags & FNM_PERIOD) && 120 (string == stringstart || 121 ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) 122 return (FNM_NOMATCH); 123 124 switch (rangematch(pattern, *string, flags, &newp)) { 125 case RANGE_ERROR: 126 /* not a good range, treat as normal text */ 127 goto normal; 128 case RANGE_MATCH: 129 pattern = newp; 130 break; 131 case RANGE_NOMATCH: 132 return (FNM_NOMATCH); 133 } 134 ++string; 135 break; 136 case '\\': 137 if (!(flags & FNM_NOESCAPE)) { 138 if ((c = *pattern++) == EOS) { 139 c = '\\'; 140 --pattern; 141 } 142 } 143 /* FALLTHROUGH */ 144 default: 145 normal: 146 if (c != *string && !((flags & FNM_CASEFOLD) && 147 (tolower((unsigned char)c) == 148 tolower((unsigned char)*string)))) 149 return (FNM_NOMATCH); 150 ++string; 151 break; 152 } 153 /* NOTREACHED */ 154 } 155 156 static int 157 rangematch(const char *pattern, char test, int flags, char **newp) 158 { 159 int negate, ok, rv; 160 char c, c2; 161 162 /* 163 * A bracket expression starting with an unquoted circumflex 164 * character produces unspecified results (IEEE 1003.2-1992, 165 * 3.13.2). This implementation treats it like '!', for 166 * consistency with the regular expression syntax. 167 * J.T. Conklin (conklin@ngai.kaleida.com) 168 */ 169 if ((negate = (*pattern == '!' || *pattern == '^'))) 170 ++pattern; 171 172 if (flags & FNM_CASEFOLD) 173 test = (char)tolower((unsigned char)test); 174 175 /* 176 * A right bracket shall lose its special meaning and represent 177 * itself in a bracket expression if it occurs first in the list. 178 * -- POSIX.2 2.8.3.2 179 */ 180 ok = 0; 181 c = *pattern++; 182 do { 183 if (c == '[' && *pattern == ':') { 184 do { 185 rv = classmatch(pattern + 1, test, 186 (flags & FNM_CASEFOLD), &pattern); 187 if (rv == RANGE_MATCH) 188 ok = 1; 189 c = *pattern++; 190 } while (rv != RANGE_ERROR && c == '[' && *pattern == ':'); 191 if (c == ']') 192 break; 193 } 194 if (c == '\\' && !(flags & FNM_NOESCAPE)) 195 c = *pattern++; 196 if (c == EOS) 197 return (RANGE_ERROR); 198 if (c == '/' && (flags & FNM_PATHNAME)) 199 return (RANGE_NOMATCH); 200 if ((flags & FNM_CASEFOLD)) 201 c = (char)tolower((unsigned char)c); 202 if (*pattern == '-' 203 && (c2 = *(pattern+1)) != EOS && c2 != ']') { 204 pattern += 2; 205 if (c2 == '\\' && !(flags & FNM_NOESCAPE)) 206 c2 = *pattern++; 207 if (c2 == EOS) 208 return (RANGE_ERROR); 209 if (flags & FNM_CASEFOLD) 210 c2 = (char)tolower((unsigned char)c2); 211 if (c <= test && test <= c2) 212 ok = 1; 213 } else if (c == test) 214 ok = 1; 215 } while ((c = *pattern++) != ']'); 216 217 *newp = (char *)pattern; 218 return (ok == negate ? RANGE_NOMATCH : RANGE_MATCH); 219 } 220 221 static int 222 classmatch(const char *pattern, char test, int foldcase, const char **ep) 223 { 224 struct cclass *cc; 225 const char *colon; 226 size_t len; 227 int rval = RANGE_NOMATCH; 228 229 if ((colon = strchr(pattern, ':')) == NULL || colon[1] != ']') { 230 *ep = pattern - 2; 231 return(RANGE_ERROR); 232 } 233 *ep = colon + 2; 234 len = (size_t)(colon - pattern); 235 236 if (foldcase && strncmp(pattern, "upper:]", 7) == 0) 237 pattern = "lower:]"; 238 for (cc = cclasses; cc->name != NULL; cc++) { 239 if (!strncmp(pattern, cc->name, len) && cc->name[len] == '\0') { 240 if (cc->isctype((unsigned char)test)) 241 rval = RANGE_MATCH; 242 break; 243 } 244 } 245 if (cc->name == NULL) { 246 /* invalid character class, return EOS */ 247 *ep = colon + strlen(colon); 248 rval = RANGE_ERROR; 249 } 250 return(rval); 251 } 252