10SN/A/*- 20SN/A * SPDX-License-Identifier: BSD-3-Clause 30SN/A * 40SN/A * Copyright (c) 1989, 1993, 1994 50SN/A * The Regents of the University of California. All rights reserved. 62362SN/A * 70SN/A * This code is derived from software contributed to Berkeley by 82362SN/A * Guido van Rossum. 90SN/A * 100SN/A * Redistribution and use in source and binary forms, with or without 110SN/A * modification, are permitted provided that the following conditions 120SN/A * are met: 130SN/A * 1. Redistributions of source code must retain the above copyright 140SN/A * notice, this list of conditions and the following disclaimer. 150SN/A * 2. Redistributions in binary form must reproduce the above copyright 160SN/A * notice, this list of conditions and the following disclaimer in the 170SN/A * documentation and/or other materials provided with the distribution. 180SN/A * 3. Neither the name of the University nor the names of its contributors 190SN/A * may be used to endorse or promote products derived from this software 202362SN/A * without specific prior written permission. 212362SN/A * 222362SN/A * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 230SN/A * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 240SN/A * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 250SN/A * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 260SN/A * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 270SN/A * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 280SN/A * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 290SN/A * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 302693SN/A * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 312693SN/A * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 322693SN/A * SUCH DAMAGE. 3315259Sprr */ 340SN/A 350SN/A#include <sys/cdefs.h> 360SN/A/* 370SN/A * Function fnmatch() as specified in POSIX 1003.2-1992, section B.6. 380SN/A * Compares a filename or pathname to a pattern. 390SN/A */ 400SN/A 410SN/A#include <sys/param.h> 420SN/A#include <sys/ctype.h> 430SN/A#include <sys/libkern.h> 440SN/A 450SN/A#define EOS '\0' 460SN/A 470SN/A#define RANGE_MATCH 1 480SN/A#define RANGE_NOMATCH 0 490SN/A#define RANGE_ERROR (-1) 500SN/A 510SN/Astatic int rangematch(const char *, char, int, char **); 522693SN/A 532693SN/Aint 542693SN/Afnmatch(const char *pattern, const char *string, int flags) 552693SN/A{ 562693SN/A const char *stringstart; 572693SN/A char *newp; 582693SN/A char c, test; 592693SN/A 606031SN/A for (stringstart = string;;) 616031SN/A switch (c = *pattern++) { 620SN/A case EOS: 630SN/A if ((flags & FNM_LEADING_DIR) && *string == '/') 640SN/A return (0); 650SN/A return (*string == EOS ? 0 : FNM_NOMATCH); 660SN/A case '?': 672693SN/A if (*string == EOS) 680SN/A return (FNM_NOMATCH); 690SN/A if (*string == '/' && (flags & FNM_PATHNAME)) 702693SN/A return (FNM_NOMATCH); 712693SN/A if (*string == '.' && (flags & FNM_PERIOD) && 722693SN/A (string == stringstart || 732693SN/A ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) 742693SN/A return (FNM_NOMATCH); 752693SN/A ++string; 766031SN/A break; 772693SN/A case '*': 782693SN/A c = *pattern; 792693SN/A /* Collapse multiple stars. */ 802693SN/A while (c == '*') 812693SN/A c = *++pattern; 822693SN/A 832693SN/A if (*string == '.' && (flags & FNM_PERIOD) && 842693SN/A (string == stringstart || 852693SN/A ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) 862693SN/A return (FNM_NOMATCH); 872693SN/A 882693SN/A /* Optimize for pattern with * at end or before /. */ 892693SN/A if (c == EOS) 902693SN/A if (flags & FNM_PATHNAME) 916031SN/A return ((flags & FNM_LEADING_DIR) || 922693SN/A strchr(string, '/') == NULL ? 932693SN/A 0 : FNM_NOMATCH); 942693SN/A else 952693SN/A return (0); 962693SN/A else if (c == '/' && flags & FNM_PATHNAME) { 972693SN/A if ((string = strchr(string, '/')) == NULL) 986031SN/A return (FNM_NOMATCH); 992693SN/A break; 1002693SN/A } 1012693SN/A 1026031SN/A /* General case, use recursion. */ 1032693SN/A while ((test = *string) != EOS) { 1042693SN/A if (!fnmatch(pattern, string, flags & ~FNM_PERIOD)) 1052693SN/A return (0); 1062693SN/A if (test == '/' && flags & FNM_PATHNAME) 1072693SN/A break; 1082693SN/A ++string; 1092693SN/A } 1102693SN/A return (FNM_NOMATCH); 1112693SN/A case '[': 1120SN/A if (*string == EOS) 11315259Sprr return (FNM_NOMATCH); 1140SN/A if (*string == '/' && (flags & FNM_PATHNAME)) 1150SN/A return (FNM_NOMATCH); 1160SN/A if (*string == '.' && (flags & FNM_PERIOD) && 1170SN/A (string == stringstart || 1180SN/A ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) 1192693SN/A return (FNM_NOMATCH); 1202693SN/A 1216031SN/A switch (rangematch(pattern, *string, flags, &newp)) { 1222693SN/A case RANGE_ERROR: 1232693SN/A goto norm; 1242693SN/A case RANGE_MATCH: 1252693SN/A pattern = newp; 1262693SN/A break; 1272693SN/A case RANGE_NOMATCH: 1282693SN/A return (FNM_NOMATCH); 1292693SN/A } 1302693SN/A ++string; 1312693SN/A break; 1322693SN/A case '\\': 1332693SN/A if (!(flags & FNM_NOESCAPE)) { 1346031SN/A if ((c = *pattern++) == EOS) { 1352693SN/A c = '\\'; 1362693SN/A --pattern; 1372693SN/A } 1382693SN/A } 1392693SN/A /* FALLTHROUGH */ 1402693SN/A default: 1412693SN/A norm: 1422693SN/A if (c == *string) 1432693SN/A ; 1442693SN/A else if ((flags & FNM_CASEFOLD) && 1452693SN/A (tolower((unsigned char)c) == 1462693SN/A tolower((unsigned char)*string))) 1472693SN/A ; 1482693SN/A else 1492693SN/A return (FNM_NOMATCH); 1502693SN/A string++; 1512693SN/A break; 1522693SN/A } 1532693SN/A /* NOTREACHED */ 1542693SN/A} 1552693SN/A 1562693SN/Astatic int 1572693SN/Arangematch(const char *pattern, char test, int flags, char **newp) 1582693SN/A{ 1592693SN/A int negate, ok; 1602693SN/A char c, c2; 1612693SN/A 1622693SN/A /* 1636031SN/A * A bracket expression starting with an unquoted circumflex 1646031SN/A * character produces unspecified results (IEEE 1003.2-1992, 1656031SN/A * 3.13.2). This implementation treats it like '!', for 1666031SN/A * consistency with the regular expression syntax. 1672693SN/A * J.T. Conklin (conklin@ngai.kaleida.com) 1682693SN/A */ 1692693SN/A if ( (negate = (*pattern == '!' || *pattern == '^')) ) 1702693SN/A ++pattern; 1712693SN/A 1722693SN/A if (flags & FNM_CASEFOLD) 1732693SN/A test = tolower((unsigned char)test); 1742693SN/A 1752693SN/A /* 1766031SN/A * A right bracket shall lose its special meaning and represent 1776031SN/A * itself in a bracket expression if it occurs first in the list. 1786031SN/A * -- POSIX.2 2.8.3.2 1796031SN/A */ 1802693SN/A ok = 0; 1812693SN/A c = *pattern++; 1822693SN/A do { 1836031SN/A if (c == '\\' && !(flags & FNM_NOESCAPE)) 1842693SN/A c = *pattern++; 1852693SN/A if (c == EOS) 1862693SN/A return (RANGE_ERROR); 1872693SN/A 1882693SN/A if (c == '/' && (flags & FNM_PATHNAME)) 1892693SN/A return (RANGE_NOMATCH); 1902693SN/A 1912693SN/A if (flags & FNM_CASEFOLD) 1922693SN/A c = tolower((unsigned char)c); 1932693SN/A 1942693SN/A if (*pattern == '-' 1952693SN/A && (c2 = *(pattern+1)) != EOS && c2 != ']') { 1962693SN/A pattern += 2; 1972693SN/A if (c2 == '\\' && !(flags & FNM_NOESCAPE)) 1982693SN/A c2 = *pattern++; 1992693SN/A if (c2 == EOS) 2002693SN/A return (RANGE_ERROR); 2012693SN/A 2022693SN/A if (flags & FNM_CASEFOLD) 2032693SN/A c2 = tolower((unsigned char)c2); 2042693SN/A 2052693SN/A if (c <= test && test <= c2) 2062693SN/A ok = 1; 2072693SN/A } else if (c == test) 2082693SN/A ok = 1; 2092693SN/A } while ((c = *pattern++) != ']'); 2102693SN/A 2112693SN/A *newp = (char *)(uintptr_t)pattern; 2126031SN/A return (ok == negate ? RANGE_NOMATCH : RANGE_MATCH); 2136031SN/A} 2146031SN/A