1139815Simp/*- 21573Srgrimes * Copyright (c) 1989, 1993, 1994 31573Srgrimes * The Regents of the University of California. All rights reserved. 41573Srgrimes * 51573Srgrimes * This code is derived from software contributed to Berkeley by 61573Srgrimes * Guido van Rossum. 71573Srgrimes * 81573Srgrimes * Redistribution and use in source and binary forms, with or without 91573Srgrimes * modification, are permitted provided that the following conditions 101573Srgrimes * are met: 111573Srgrimes * 1. Redistributions of source code must retain the above copyright 121573Srgrimes * notice, this list of conditions and the following disclaimer. 131573Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 141573Srgrimes * notice, this list of conditions and the following disclaimer in the 151573Srgrimes * documentation and/or other materials provided with the distribution. 161573Srgrimes * 4. Neither the name of the University nor the names of its contributors 171573Srgrimes * may be used to endorse or promote products derived from this software 181573Srgrimes * without specific prior written permission. 191573Srgrimes * 201573Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 211573Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 221573Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 231573Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 241573Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 251573Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 261573Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 271573Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 281573Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 291573Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 301573Srgrimes * SUCH DAMAGE. 311573Srgrimes */ 321573Srgrimes 33116189Sobrien#include <sys/cdefs.h> 34116189Sobrien__FBSDID("$FreeBSD$"); 35116189Sobrien 361573Srgrimes/* 371573Srgrimes * Function fnmatch() as specified in POSIX 1003.2-1992, section B.6. 381573Srgrimes * Compares a filename or pathname to a pattern. 391573Srgrimes */ 401573Srgrimes 41104652Sdd#include <sys/param.h> 42104652Sdd#include <sys/ctype.h> 43104652Sdd#include <sys/libkern.h> 441573Srgrimes 451573Srgrimes#define EOS '\0' 461573Srgrimes 4726484Sache#define RANGE_MATCH 1 4826484Sache#define RANGE_NOMATCH 0 4926484Sache#define RANGE_ERROR (-1) 501573Srgrimes 5190045Sobrienstatic int rangematch(const char *, char, int, char **); 5226484Sache 531573Srgrimesint 54154660Srwatsonfnmatch(const char *pattern, const char *string, int flags) 551573Srgrimes{ 561573Srgrimes const char *stringstart; 5726484Sache char *newp; 581573Srgrimes char c, test; 591573Srgrimes 601573Srgrimes for (stringstart = string;;) 611573Srgrimes switch (c = *pattern++) { 621573Srgrimes case EOS: 6319132Sache if ((flags & FNM_LEADING_DIR) && *string == '/') 6419132Sache return (0); 651573Srgrimes return (*string == EOS ? 0 : FNM_NOMATCH); 661573Srgrimes case '?': 671573Srgrimes if (*string == EOS) 681573Srgrimes return (FNM_NOMATCH); 691573Srgrimes if (*string == '/' && (flags & FNM_PATHNAME)) 701573Srgrimes return (FNM_NOMATCH); 711573Srgrimes if (*string == '.' && (flags & FNM_PERIOD) && 721573Srgrimes (string == stringstart || 731573Srgrimes ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) 741573Srgrimes return (FNM_NOMATCH); 751573Srgrimes ++string; 761573Srgrimes break; 771573Srgrimes case '*': 781573Srgrimes c = *pattern; 791573Srgrimes /* Collapse multiple stars. */ 801573Srgrimes while (c == '*') 811573Srgrimes c = *++pattern; 821573Srgrimes 831573Srgrimes if (*string == '.' && (flags & FNM_PERIOD) && 841573Srgrimes (string == stringstart || 851573Srgrimes ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) 861573Srgrimes return (FNM_NOMATCH); 871573Srgrimes 881573Srgrimes /* Optimize for pattern with * at end or before /. */ 891573Srgrimes if (c == EOS) 901573Srgrimes if (flags & FNM_PATHNAME) 9125269Sjdp return ((flags & FNM_LEADING_DIR) || 92229272Sed strchr(string, '/') == NULL ? 931573Srgrimes 0 : FNM_NOMATCH); 941573Srgrimes else 951573Srgrimes return (0); 961573Srgrimes else if (c == '/' && flags & FNM_PATHNAME) { 97229272Sed if ((string = strchr(string, '/')) == NULL) 981573Srgrimes return (FNM_NOMATCH); 991573Srgrimes break; 1001573Srgrimes } 1011573Srgrimes 1021573Srgrimes /* General case, use recursion. */ 1031573Srgrimes while ((test = *string) != EOS) { 1041573Srgrimes if (!fnmatch(pattern, string, flags & ~FNM_PERIOD)) 1051573Srgrimes return (0); 1061573Srgrimes if (test == '/' && flags & FNM_PATHNAME) 1071573Srgrimes break; 1081573Srgrimes ++string; 1091573Srgrimes } 1101573Srgrimes return (FNM_NOMATCH); 1111573Srgrimes case '[': 1121573Srgrimes if (*string == EOS) 1131573Srgrimes return (FNM_NOMATCH); 11426486Sache if (*string == '/' && (flags & FNM_PATHNAME)) 1151573Srgrimes return (FNM_NOMATCH); 11626486Sache if (*string == '.' && (flags & FNM_PERIOD) && 11726486Sache (string == stringstart || 11826486Sache ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) 11926486Sache return (FNM_NOMATCH); 12026486Sache 12126484Sache switch (rangematch(pattern, *string, flags, &newp)) { 12226484Sache case RANGE_ERROR: 12326484Sache goto norm; 12426484Sache case RANGE_MATCH: 12526484Sache pattern = newp; 12626484Sache break; 12726484Sache case RANGE_NOMATCH: 1281573Srgrimes return (FNM_NOMATCH); 12926484Sache } 1301573Srgrimes ++string; 1311573Srgrimes break; 1321573Srgrimes case '\\': 1331573Srgrimes if (!(flags & FNM_NOESCAPE)) { 1341573Srgrimes if ((c = *pattern++) == EOS) { 1351573Srgrimes c = '\\'; 1361573Srgrimes --pattern; 1371573Srgrimes } 1381573Srgrimes } 1391573Srgrimes /* FALLTHROUGH */ 1401573Srgrimes default: 14126484Sache norm: 14219059Swosch if (c == *string) 14319059Swosch ; 14419132Sache else if ((flags & FNM_CASEFOLD) && 14519132Sache (tolower((unsigned char)c) == 14619132Sache tolower((unsigned char)*string))) 14719059Swosch ; 14819059Swosch else 1491573Srgrimes return (FNM_NOMATCH); 15019059Swosch string++; 1511573Srgrimes break; 1521573Srgrimes } 1531573Srgrimes /* NOTREACHED */ 1541573Srgrimes} 1551573Srgrimes 15626484Sachestatic int 157154660Srwatsonrangematch(const char *pattern, char test, int flags, char **newp) 1581573Srgrimes{ 15926492Sache int negate, ok; 1601573Srgrimes char c, c2; 1611573Srgrimes 1621573Srgrimes /* 1631573Srgrimes * A bracket expression starting with an unquoted circumflex 1641573Srgrimes * character produces unspecified results (IEEE 1003.2-1992, 1651573Srgrimes * 3.13.2). This implementation treats it like '!', for 1661573Srgrimes * consistency with the regular expression syntax. 1671573Srgrimes * J.T. Conklin (conklin@ngai.kaleida.com) 1681573Srgrimes */ 16926486Sache if ( (negate = (*pattern == '!' || *pattern == '^')) ) 1701573Srgrimes ++pattern; 1718870Srgrimes 17219132Sache if (flags & FNM_CASEFOLD) 17319132Sache test = tolower((unsigned char)test); 17419059Swosch 17526484Sache /* 17626484Sache * A right bracket shall lose its special meaning and represent 17726484Sache * itself in a bracket expression if it occurs first in the list. 17826484Sache * -- POSIX.2 2.8.3.2 17926484Sache */ 18026492Sache ok = 0; 18126492Sache c = *pattern++; 18226492Sache do { 1831573Srgrimes if (c == '\\' && !(flags & FNM_NOESCAPE)) 1841573Srgrimes c = *pattern++; 1851573Srgrimes if (c == EOS) 18626484Sache return (RANGE_ERROR); 18719059Swosch 18826486Sache if (c == '/' && (flags & FNM_PATHNAME)) 18926486Sache return (RANGE_NOMATCH); 19026486Sache 19119132Sache if (flags & FNM_CASEFOLD) 19219132Sache c = tolower((unsigned char)c); 19319059Swosch 1948870Srgrimes if (*pattern == '-' 1951573Srgrimes && (c2 = *(pattern+1)) != EOS && c2 != ']') { 1961573Srgrimes pattern += 2; 1971573Srgrimes if (c2 == '\\' && !(flags & FNM_NOESCAPE)) 1981573Srgrimes c2 = *pattern++; 1991573Srgrimes if (c2 == EOS) 20026484Sache return (RANGE_ERROR); 20119059Swosch 20219132Sache if (flags & FNM_CASEFOLD) 20319132Sache c2 = tolower((unsigned char)c2); 20419059Swosch 205104652Sdd if (c <= test && test <= c2) 2061573Srgrimes ok = 1; 2071573Srgrimes } else if (c == test) 2081573Srgrimes ok = 1; 20926492Sache } while ((c = *pattern++) != ']'); 21026492Sache 211104652Sdd *newp = (char *)(uintptr_t)pattern; 21226484Sache return (ok == negate ? RANGE_NOMATCH : RANGE_MATCH); 2131573Srgrimes} 214