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