1 1.26 christos /* $NetBSD: fnmatch.c,v 1.26 2014/10/12 22:32:33 christos Exp $ */ 2 1.11 cgd 3 1.1 cgd /* 4 1.11 cgd * Copyright (c) 1989, 1993, 1994 5 1.6 cgd * The Regents of the University of California. All rights reserved. 6 1.1 cgd * 7 1.1 cgd * This code is derived from software contributed to Berkeley by 8 1.1 cgd * Guido van Rossum. 9 1.1 cgd * 10 1.1 cgd * Redistribution and use in source and binary forms, with or without 11 1.1 cgd * modification, are permitted provided that the following conditions 12 1.1 cgd * are met: 13 1.1 cgd * 1. Redistributions of source code must retain the above copyright 14 1.1 cgd * notice, this list of conditions and the following disclaimer. 15 1.1 cgd * 2. Redistributions in binary form must reproduce the above copyright 16 1.1 cgd * notice, this list of conditions and the following disclaimer in the 17 1.1 cgd * documentation and/or other materials provided with the distribution. 18 1.20 agc * 3. Neither the name of the University nor the names of its contributors 19 1.1 cgd * may be used to endorse or promote products derived from this software 20 1.1 cgd * without specific prior written permission. 21 1.1 cgd * 22 1.1 cgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 1.1 cgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 1.1 cgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 1.1 cgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 1.1 cgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 1.1 cgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 1.1 cgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 1.1 cgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 1.1 cgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 1.1 cgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 1.1 cgd * SUCH DAMAGE. 33 1.1 cgd */ 34 1.1 cgd 35 1.12 christos #include <sys/cdefs.h> 36 1.1 cgd #if defined(LIBC_SCCS) && !defined(lint) 37 1.11 cgd #if 0 38 1.11 cgd static char sccsid[] = "@(#)fnmatch.c 8.2 (Berkeley) 4/16/94"; 39 1.11 cgd #else 40 1.26 christos __RCSID("$NetBSD: fnmatch.c,v 1.26 2014/10/12 22:32:33 christos Exp $"); 41 1.11 cgd #endif 42 1.1 cgd #endif /* LIBC_SCCS and not lint */ 43 1.1 cgd 44 1.1 cgd /* 45 1.7 jtc * Function fnmatch() as specified in POSIX 1003.2-1992, section B.6. 46 1.1 cgd * Compares a filename or pathname to a pattern. 47 1.1 cgd */ 48 1.1 cgd 49 1.13 jtc #include "namespace.h" 50 1.15 lukem 51 1.15 lukem #include <assert.h> 52 1.18 thorpej #include <ctype.h> 53 1.3 jtc #include <fnmatch.h> 54 1.1 cgd #include <string.h> 55 1.13 jtc 56 1.13 jtc #ifdef __weak_alias 57 1.17 mycroft __weak_alias(fnmatch,_fnmatch) 58 1.13 jtc #endif 59 1.1 cgd 60 1.1 cgd #define EOS '\0' 61 1.1 cgd 62 1.21 perry static inline int 63 1.18 thorpej foldcase(int ch, int flags) 64 1.18 thorpej { 65 1.18 thorpej 66 1.18 thorpej if ((flags & FNM_CASEFOLD) != 0 && isupper(ch)) 67 1.22 christos return tolower(ch); 68 1.22 christos return ch; 69 1.18 thorpej } 70 1.18 thorpej 71 1.18 thorpej #define FOLDCASE(ch, flags) foldcase((unsigned char)(ch), (flags)) 72 1.18 thorpej 73 1.22 christos static const char * 74 1.22 christos rangematch(const char *pattern, int test, int flags) 75 1.22 christos { 76 1.26 christos int negate, ok, need; 77 1.22 christos char c, c2; 78 1.22 christos 79 1.22 christos _DIAGASSERT(pattern != NULL); 80 1.22 christos 81 1.22 christos /* 82 1.22 christos * A bracket expression starting with an unquoted circumflex 83 1.22 christos * character produces unspecified results (IEEE 1003.2-1992, 84 1.22 christos * 3.13.2). This implementation treats it like '!', for 85 1.22 christos * consistency with the regular expression syntax. 86 1.22 christos * J.T. Conklin (conklin (at) ngai.kaleida.com) 87 1.22 christos */ 88 1.22 christos if ((negate = (*pattern == '!' || *pattern == '^')) != 0) 89 1.22 christos ++pattern; 90 1.22 christos 91 1.26 christos need = 1; 92 1.26 christos for (ok = 0; (c = FOLDCASE(*pattern++, flags)) != ']' || need;) { 93 1.26 christos need = 0; 94 1.26 christos if (c == '/') 95 1.26 christos return (void *)-1; 96 1.22 christos if (c == '\\' && !(flags & FNM_NOESCAPE)) 97 1.22 christos c = FOLDCASE(*pattern++, flags); 98 1.22 christos if (c == EOS) 99 1.22 christos return NULL; 100 1.22 christos if (*pattern == '-' 101 1.22 christos && (c2 = FOLDCASE(*(pattern + 1), flags)) != EOS && 102 1.22 christos c2 != ']') { 103 1.22 christos pattern += 2; 104 1.22 christos if (c2 == '\\' && !(flags & FNM_NOESCAPE)) 105 1.22 christos c2 = FOLDCASE(*pattern++, flags); 106 1.22 christos if (c2 == EOS) 107 1.22 christos return NULL; 108 1.22 christos if (c <= test && test <= c2) 109 1.22 christos ok = 1; 110 1.22 christos } else if (c == test) 111 1.22 christos ok = 1; 112 1.22 christos } 113 1.22 christos return ok == negate ? NULL : pattern; 114 1.22 christos } 115 1.22 christos 116 1.22 christos 117 1.22 christos static int 118 1.22 christos fnmatchx(const char *pattern, const char *string, int flags, size_t recursion) 119 1.1 cgd { 120 1.26 christos const char *stringstart, *r; 121 1.11 cgd char c, test; 122 1.1 cgd 123 1.15 lukem _DIAGASSERT(pattern != NULL); 124 1.15 lukem _DIAGASSERT(string != NULL); 125 1.15 lukem 126 1.22 christos if (recursion-- == 0) 127 1.22 christos return FNM_NORES; 128 1.22 christos 129 1.24 christos for (stringstart = string;;) { 130 1.18 thorpej switch (c = FOLDCASE(*pattern++, flags)) { 131 1.1 cgd case EOS: 132 1.19 provos if ((flags & FNM_LEADING_DIR) && *string == '/') 133 1.22 christos return 0; 134 1.22 christos return *string == EOS ? 0 : FNM_NOMATCH; 135 1.1 cgd case '?': 136 1.8 jtc if (*string == EOS) 137 1.22 christos return FNM_NOMATCH; 138 1.8 jtc if (*string == '/' && (flags & FNM_PATHNAME)) 139 1.22 christos return FNM_NOMATCH; 140 1.8 jtc if (*string == '.' && (flags & FNM_PERIOD) && 141 1.11 cgd (string == stringstart || 142 1.11 cgd ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) 143 1.22 christos return FNM_NOMATCH; 144 1.8 jtc ++string; 145 1.1 cgd break; 146 1.1 cgd case '*': 147 1.18 thorpej c = FOLDCASE(*pattern, flags); 148 1.3 jtc /* Collapse multiple stars. */ 149 1.1 cgd while (c == '*') 150 1.18 thorpej c = FOLDCASE(*++pattern, flags); 151 1.1 cgd 152 1.8 jtc if (*string == '.' && (flags & FNM_PERIOD) && 153 1.11 cgd (string == stringstart || 154 1.11 cgd ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) 155 1.22 christos return FNM_NOMATCH; 156 1.8 jtc 157 1.3 jtc /* Optimize for pattern with * at end or before /. */ 158 1.14 christos if (c == EOS) { 159 1.1 cgd if (flags & FNM_PATHNAME) 160 1.22 christos return (flags & FNM_LEADING_DIR) || 161 1.19 provos strchr(string, '/') == NULL ? 162 1.22 christos 0 : FNM_NOMATCH; 163 1.1 cgd else 164 1.22 christos return 0; 165 1.14 christos } else if (c == '/' && flags & FNM_PATHNAME) { 166 1.10 jtc if ((string = strchr(string, '/')) == NULL) 167 1.22 christos return FNM_NOMATCH; 168 1.1 cgd break; 169 1.1 cgd } 170 1.1 cgd 171 1.3 jtc /* General case, use recursion. */ 172 1.18 thorpej while ((test = FOLDCASE(*string, flags)) != EOS) { 173 1.22 christos int e; 174 1.22 christos switch ((e = fnmatchx(pattern, string, 175 1.22 christos flags & ~FNM_PERIOD, recursion))) { 176 1.22 christos case FNM_NOMATCH: 177 1.22 christos break; 178 1.22 christos default: 179 1.22 christos return e; 180 1.22 christos } 181 1.1 cgd if (test == '/' && flags & FNM_PATHNAME) 182 1.1 cgd break; 183 1.1 cgd ++string; 184 1.1 cgd } 185 1.22 christos return FNM_NOMATCH; 186 1.1 cgd case '[': 187 1.8 jtc if (*string == EOS) 188 1.22 christos return FNM_NOMATCH; 189 1.8 jtc if (*string == '/' && flags & FNM_PATHNAME) 190 1.22 christos return FNM_NOMATCH; 191 1.26 christos if ((r = rangematch(pattern, 192 1.22 christos FOLDCASE(*string, flags), flags)) == NULL) 193 1.22 christos return FNM_NOMATCH; 194 1.26 christos if (r == (void *)-1) { 195 1.26 christos if (*string != '[') 196 1.26 christos return FNM_NOMATCH; 197 1.26 christos } else 198 1.26 christos pattern = r; 199 1.8 jtc ++string; 200 1.1 cgd break; 201 1.1 cgd case '\\': 202 1.3 jtc if (!(flags & FNM_NOESCAPE)) { 203 1.18 thorpej if ((c = FOLDCASE(*pattern++, flags)) == EOS) { 204 1.25 christos c = '\0'; 205 1.1 cgd --pattern; 206 1.1 cgd } 207 1.1 cgd } 208 1.1 cgd /* FALLTHROUGH */ 209 1.1 cgd default: 210 1.18 thorpej if (c != FOLDCASE(*string++, flags)) 211 1.22 christos return FNM_NOMATCH; 212 1.1 cgd break; 213 1.1 cgd } 214 1.24 christos } 215 1.3 jtc /* NOTREACHED */ 216 1.3 jtc } 217 1.3 jtc 218 1.22 christos int 219 1.22 christos fnmatch(const char *pattern, const char *string, int flags) 220 1.3 jtc { 221 1.23 christos return fnmatchx(pattern, string, flags, 64); 222 1.1 cgd } 223