1132720Skan/*- 297403Sobrien * Copyright (c) 1989, 1993, 1994 3169691Skan * The Regents of the University of California. All rights reserved. 4169691Skan * 597403Sobrien * This code is derived from software contributed to Berkeley by 697403Sobrien * Guido van Rossum. 797403Sobrien * 897403Sobrien * Redistribution and use in source and binary forms, with or without 997403Sobrien * modification, are permitted provided that the following conditions 1097403Sobrien * are met: 1197403Sobrien * 1. Redistributions of source code must retain the above copyright 1297403Sobrien * notice, this list of conditions and the following disclaimer. 1397403Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1497403Sobrien * notice, this list of conditions and the following disclaimer in the 1597403Sobrien * documentation and/or other materials provided with the distribution. 1697403Sobrien * 4. Neither the name of the University nor the names of its contributors 1797403Sobrien * may be used to endorse or promote products derived from this software 1897403Sobrien * without specific prior written permission. 19169691Skan * 2097403Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2197403Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2297403Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2397403Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2497403Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2597403Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2697403Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2797403Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2897403Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2997403Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3097403Sobrien * SUCH DAMAGE. 31169691Skan */ 32169691Skan 33169691Skan#include <sys/cdefs.h> 34169691Skan__FBSDID("$FreeBSD$"); 35169691Skan 36132720Skan/* 37132720Skan * Function fnmatch() as specified in POSIX 1003.2-1992, section B.6. 3897403Sobrien * Compares a filename or pathname to a pattern. 3997403Sobrien */ 4097403Sobrien 41169691Skan#include <sys/param.h> 42169691Skan#include <sys/ctype.h> 4397403Sobrien#include <sys/libkern.h> 4497403Sobrien 4597403Sobrien#define EOS '\0' 46132720Skan 4797403Sobrien#define RANGE_MATCH 1 4897403Sobrien#define RANGE_NOMATCH 0 4997403Sobrien#define RANGE_ERROR (-1) 5097403Sobrien 51132720Skanstatic int rangematch(const char *, char, int, char **); 52132720Skan 5397403Sobrienint 54132720Skanfnmatch(const char *pattern, const char *string, int flags) 5597403Sobrien{ 56132720Skan const char *stringstart; 5797403Sobrien char *newp; 5897403Sobrien char c, test; 5997403Sobrien 6097403Sobrien for (stringstart = string;;) 6197403Sobrien switch (c = *pattern++) { 6297403Sobrien case EOS: 6397403Sobrien if ((flags & FNM_LEADING_DIR) && *string == '/') 6497403Sobrien return (0); 6597403Sobrien return (*string == EOS ? 0 : FNM_NOMATCH); 6697403Sobrien case '?': 6797403Sobrien if (*string == EOS) 6897403Sobrien return (FNM_NOMATCH); 69132720Skan if (*string == '/' && (flags & FNM_PATHNAME)) 70132720Skan return (FNM_NOMATCH); 71132720Skan if (*string == '.' && (flags & FNM_PERIOD) && 7297403Sobrien (string == stringstart || 73132720Skan ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) 74132720Skan return (FNM_NOMATCH); 7597403Sobrien ++string; 76132720Skan break; 77132720Skan case '*': 78132720Skan c = *pattern; 7997403Sobrien /* Collapse multiple stars. */ 80132720Skan while (c == '*') 81132720Skan c = *++pattern; 82132720Skan 83132720Skan if (*string == '.' && (flags & FNM_PERIOD) && 84132720Skan (string == stringstart || 85132720Skan ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) 86132720Skan return (FNM_NOMATCH); 87132720Skan 88132720Skan /* Optimize for pattern with * at end or before /. */ 89132720Skan if (c == EOS) 90132720Skan if (flags & FNM_PATHNAME) 91117397Skan return ((flags & FNM_LEADING_DIR) || 92132720Skan strchr(string, '/') == NULL ? 93132720Skan 0 : FNM_NOMATCH); 94132720Skan else 95132720Skan return (0); 96132720Skan else if (c == '/' && flags & FNM_PATHNAME) { 97132720Skan if ((string = strchr(string, '/')) == NULL) 98117397Skan return (FNM_NOMATCH); 99132720Skan break; 100132720Skan } 101132720Skan 102132720Skan /* General case, use recursion. */ 103132720Skan while ((test = *string) != EOS) { 104132720Skan if (!fnmatch(pattern, string, flags & ~FNM_PERIOD)) 105132720Skan return (0); 106117397Skan if (test == '/' && flags & FNM_PATHNAME) 107132720Skan break; 108117397Skan ++string; 109132720Skan } 110132720Skan return (FNM_NOMATCH); 111132720Skan case '[': 11297403Sobrien if (*string == EOS) 11397403Sobrien return (FNM_NOMATCH); 11497403Sobrien if (*string == '/' && (flags & FNM_PATHNAME)) 11597403Sobrien return (FNM_NOMATCH); 11697403Sobrien if (*string == '.' && (flags & FNM_PERIOD) && 11797403Sobrien (string == stringstart || 118132720Skan ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) 11997403Sobrien return (FNM_NOMATCH); 12097403Sobrien 12197403Sobrien switch (rangematch(pattern, *string, flags, &newp)) { 12297403Sobrien case RANGE_ERROR: 123132720Skan goto norm; 12497403Sobrien case RANGE_MATCH: 12597403Sobrien pattern = newp; 12697403Sobrien break; 12797403Sobrien case RANGE_NOMATCH: 12897403Sobrien return (FNM_NOMATCH); 12997403Sobrien } 13097403Sobrien ++string; 13197403Sobrien break; 132117397Skan case '\\': 13397403Sobrien if (!(flags & FNM_NOESCAPE)) { 13497403Sobrien if ((c = *pattern++) == EOS) { 13597403Sobrien c = '\\'; 13697403Sobrien --pattern; 13797403Sobrien } 13897403Sobrien } 13997403Sobrien /* FALLTHROUGH */ 14097403Sobrien default: 14197403Sobrien norm: 14297403Sobrien if (c == *string) 14397403Sobrien ; 144132720Skan else if ((flags & FNM_CASEFOLD) && 145132720Skan (tolower((unsigned char)c) == 146117397Skan tolower((unsigned char)*string))) 14797403Sobrien ; 14897403Sobrien else 14997403Sobrien return (FNM_NOMATCH); 15097403Sobrien string++; 15197403Sobrien break; 15297403Sobrien } 15397403Sobrien /* NOTREACHED */ 15497403Sobrien} 15597403Sobrien 15697403Sobrienstatic int 15797403Sobrienrangematch(const char *pattern, char test, int flags, char **newp) 15897403Sobrien{ 15997403Sobrien int negate, ok; 160102782Skan char c, c2; 16197403Sobrien 16297403Sobrien /* 163132720Skan * A bracket expression starting with an unquoted circumflex 16497403Sobrien * character produces unspecified results (IEEE 1003.2-1992, 16597403Sobrien * 3.13.2). This implementation treats it like '!', for 16697403Sobrien * consistency with the regular expression syntax. 16797403Sobrien * J.T. Conklin (conklin@ngai.kaleida.com) 16897403Sobrien */ 16997403Sobrien if ( (negate = (*pattern == '!' || *pattern == '^')) ) 17097403Sobrien ++pattern; 171117397Skan 17297403Sobrien if (flags & FNM_CASEFOLD) 173117397Skan test = tolower((unsigned char)test); 174132720Skan 17597403Sobrien /* 176132720Skan * A right bracket shall lose its special meaning and represent 177132720Skan * itself in a bracket expression if it occurs first in the list. 178132720Skan * -- POSIX.2 2.8.3.2 179132720Skan */ 18097403Sobrien ok = 0; 181132720Skan c = *pattern++; 182132720Skan do { 183132720Skan if (c == '\\' && !(flags & FNM_NOESCAPE)) 184132720Skan c = *pattern++; 18597403Sobrien if (c == EOS) 186132720Skan return (RANGE_ERROR); 18797403Sobrien 18897403Sobrien if (c == '/' && (flags & FNM_PATHNAME)) 18997403Sobrien return (RANGE_NOMATCH); 190132720Skan 19197403Sobrien if (flags & FNM_CASEFOLD) 192132720Skan c = tolower((unsigned char)c); 19397403Sobrien 194107606Sobrien if (*pattern == '-' 195132720Skan && (c2 = *(pattern+1)) != EOS && c2 != ']') { 19697403Sobrien pattern += 2; 197107606Sobrien if (c2 == '\\' && !(flags & FNM_NOESCAPE)) 198117397Skan c2 = *pattern++; 19997403Sobrien if (c2 == EOS) 200169691Skan return (RANGE_ERROR); 201169691Skan 202132720Skan if (flags & FNM_CASEFOLD) 203 c2 = tolower((unsigned char)c2); 204 205 if (c <= test && test <= c2) 206 ok = 1; 207 } else if (c == test) 208 ok = 1; 209 } while ((c = *pattern++) != ']'); 210 211 *newp = (char *)(uintptr_t)pattern; 212 return (ok == negate ? RANGE_NOMATCH : RANGE_MATCH); 213} 214