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