xref: /netbsd-src/lib/libc/gen/fnmatch.c (revision 02807d50d63515f55489363d04e6035cd867cd23)
1*02807d50Schristos /*	$NetBSD: fnmatch.c,v 1.26 2014/10/12 22:32:33 christos Exp $	*/
272c46b1cScgd 
361f28255Scgd /*
472c46b1cScgd  * Copyright (c) 1989, 1993, 1994
5affc13c6Scgd  *	The Regents of the University of California.  All rights reserved.
661f28255Scgd  *
761f28255Scgd  * This code is derived from software contributed to Berkeley by
861f28255Scgd  * Guido van Rossum.
961f28255Scgd  *
1061f28255Scgd  * Redistribution and use in source and binary forms, with or without
1161f28255Scgd  * modification, are permitted provided that the following conditions
1261f28255Scgd  * are met:
1361f28255Scgd  * 1. Redistributions of source code must retain the above copyright
1461f28255Scgd  *    notice, this list of conditions and the following disclaimer.
1561f28255Scgd  * 2. Redistributions in binary form must reproduce the above copyright
1661f28255Scgd  *    notice, this list of conditions and the following disclaimer in the
1761f28255Scgd  *    documentation and/or other materials provided with the distribution.
18eb7c1594Sagc  * 3. Neither the name of the University nor the names of its contributors
1961f28255Scgd  *    may be used to endorse or promote products derived from this software
2061f28255Scgd  *    without specific prior written permission.
2161f28255Scgd  *
2261f28255Scgd  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2361f28255Scgd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2461f28255Scgd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2561f28255Scgd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2661f28255Scgd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2761f28255Scgd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2861f28255Scgd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2961f28255Scgd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3061f28255Scgd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3161f28255Scgd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3261f28255Scgd  * SUCH DAMAGE.
3361f28255Scgd  */
3461f28255Scgd 
351e9dc86bSchristos #include <sys/cdefs.h>
3661f28255Scgd #if defined(LIBC_SCCS) && !defined(lint)
3772c46b1cScgd #if 0
3872c46b1cScgd static char sccsid[] = "@(#)fnmatch.c	8.2 (Berkeley) 4/16/94";
3972c46b1cScgd #else
40*02807d50Schristos __RCSID("$NetBSD: fnmatch.c,v 1.26 2014/10/12 22:32:33 christos Exp $");
4172c46b1cScgd #endif
4261f28255Scgd #endif /* LIBC_SCCS and not lint */
4361f28255Scgd 
4461f28255Scgd /*
4514925245Sjtc  * Function fnmatch() as specified in POSIX 1003.2-1992, section B.6.
4661f28255Scgd  * Compares a filename or pathname to a pattern.
4761f28255Scgd  */
4861f28255Scgd 
4943fa6fe3Sjtc #include "namespace.h"
50b48252f3Slukem 
51b48252f3Slukem #include <assert.h>
52445d18edSthorpej #include <ctype.h>
53dacb3d32Sjtc #include <fnmatch.h>
5461f28255Scgd #include <string.h>
5561f28255Scgd 
5643fa6fe3Sjtc #ifdef __weak_alias
__weak_alias(fnmatch,_fnmatch)5760549036Smycroft __weak_alias(fnmatch,_fnmatch)
5843fa6fe3Sjtc #endif
5943fa6fe3Sjtc 
6061f28255Scgd #define	EOS	'\0'
6161f28255Scgd 
624e11af46Sperry static inline int
63445d18edSthorpej foldcase(int ch, int flags)
64445d18edSthorpej {
65445d18edSthorpej 
66445d18edSthorpej 	if ((flags & FNM_CASEFOLD) != 0 && isupper(ch))
670f6e9a96Schristos 		return tolower(ch);
680f6e9a96Schristos 	return ch;
69445d18edSthorpej }
70445d18edSthorpej 
71445d18edSthorpej #define	FOLDCASE(ch, flags)	foldcase((unsigned char)(ch), (flags))
72445d18edSthorpej 
73dacb3d32Sjtc static const char *
rangematch(const char * pattern,int test,int flags)740f6e9a96Schristos rangematch(const char *pattern, int test, int flags)
7561f28255Scgd {
76*02807d50Schristos 	int negate, ok, need;
7772c46b1cScgd 	char c, c2;
7861f28255Scgd 
79b48252f3Slukem 	_DIAGASSERT(pattern != NULL);
80b48252f3Slukem 
8172c46b1cScgd 	/*
8272c46b1cScgd 	 * A bracket expression starting with an unquoted circumflex
8314925245Sjtc 	 * character produces unspecified results (IEEE 1003.2-1992,
8472c46b1cScgd 	 * 3.13.2).  This implementation treats it like '!', for
8572c46b1cScgd 	 * consistency with the regular expression syntax.
8672c46b1cScgd 	 * J.T. Conklin (conklin@ngai.kaleida.com)
8761f28255Scgd 	 */
881e9dc86bSchristos 	if ((negate = (*pattern == '!' || *pattern == '^')) != 0)
8972c46b1cScgd 		++pattern;
9014925245Sjtc 
91*02807d50Schristos 	need = 1;
92*02807d50Schristos 	for (ok = 0; (c = FOLDCASE(*pattern++, flags)) != ']' || need;) {
93*02807d50Schristos 		need = 0;
94*02807d50Schristos 		if (c == '/')
95*02807d50Schristos 			return (void *)-1;
9672c46b1cScgd 		if (c == '\\' && !(flags & FNM_NOESCAPE))
97445d18edSthorpej 			c = FOLDCASE(*pattern++, flags);
9872c46b1cScgd 		if (c == EOS)
990f6e9a96Schristos 			return NULL;
10014925245Sjtc 		if (*pattern == '-'
101445d18edSthorpej 		    && (c2 = FOLDCASE(*(pattern + 1), flags)) != EOS &&
102445d18edSthorpej 		        c2 != ']') {
10361f28255Scgd 			pattern += 2;
10472c46b1cScgd 			if (c2 == '\\' && !(flags & FNM_NOESCAPE))
105445d18edSthorpej 				c2 = FOLDCASE(*pattern++, flags);
10672c46b1cScgd 			if (c2 == EOS)
1070f6e9a96Schristos 				return NULL;
10872c46b1cScgd 			if (c <= test && test <= c2)
10972c46b1cScgd 				ok = 1;
11072c46b1cScgd 		} else if (c == test)
11161f28255Scgd 			ok = 1;
11261f28255Scgd 	}
1130f6e9a96Schristos 	return ok == negate ? NULL : pattern;
1140f6e9a96Schristos }
1150f6e9a96Schristos 
1160f6e9a96Schristos 
1170f6e9a96Schristos static int
fnmatchx(const char * pattern,const char * string,int flags,size_t recursion)1180f6e9a96Schristos fnmatchx(const char *pattern, const char *string, int flags, size_t recursion)
1190f6e9a96Schristos {
120*02807d50Schristos 	const char *stringstart, *r;
1210f6e9a96Schristos 	char c, test;
1220f6e9a96Schristos 
1230f6e9a96Schristos 	_DIAGASSERT(pattern != NULL);
1240f6e9a96Schristos 	_DIAGASSERT(string != NULL);
1250f6e9a96Schristos 
1260f6e9a96Schristos 	if (recursion-- == 0)
1270f6e9a96Schristos 		return FNM_NORES;
1280f6e9a96Schristos 
1292d5180b4Schristos 	for (stringstart = string;;) {
1300f6e9a96Schristos 		switch (c = FOLDCASE(*pattern++, flags)) {
1310f6e9a96Schristos 		case EOS:
1320f6e9a96Schristos 			if ((flags & FNM_LEADING_DIR) && *string == '/')
1330f6e9a96Schristos 				return 0;
1340f6e9a96Schristos 			return *string == EOS ? 0 : FNM_NOMATCH;
1350f6e9a96Schristos 		case '?':
1360f6e9a96Schristos 			if (*string == EOS)
1370f6e9a96Schristos 				return FNM_NOMATCH;
1380f6e9a96Schristos 			if (*string == '/' && (flags & FNM_PATHNAME))
1390f6e9a96Schristos 				return FNM_NOMATCH;
1400f6e9a96Schristos 			if (*string == '.' && (flags & FNM_PERIOD) &&
1410f6e9a96Schristos 			    (string == stringstart ||
1420f6e9a96Schristos 			    ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
1430f6e9a96Schristos 				return FNM_NOMATCH;
1440f6e9a96Schristos 			++string;
1450f6e9a96Schristos 			break;
1460f6e9a96Schristos 		case '*':
1470f6e9a96Schristos 			c = FOLDCASE(*pattern, flags);
1480f6e9a96Schristos 			/* Collapse multiple stars. */
1490f6e9a96Schristos 			while (c == '*')
1500f6e9a96Schristos 				c = FOLDCASE(*++pattern, flags);
1510f6e9a96Schristos 
1520f6e9a96Schristos 			if (*string == '.' && (flags & FNM_PERIOD) &&
1530f6e9a96Schristos 			    (string == stringstart ||
1540f6e9a96Schristos 			    ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
1550f6e9a96Schristos 				return FNM_NOMATCH;
1560f6e9a96Schristos 
1570f6e9a96Schristos 			/* Optimize for pattern with * at end or before /. */
1580f6e9a96Schristos 			if (c == EOS) {
1590f6e9a96Schristos 				if (flags & FNM_PATHNAME)
1600f6e9a96Schristos 					return (flags & FNM_LEADING_DIR) ||
1610f6e9a96Schristos 					    strchr(string, '/') == NULL ?
1620f6e9a96Schristos 					    0 : FNM_NOMATCH;
1630f6e9a96Schristos 				else
1640f6e9a96Schristos 					return 0;
1650f6e9a96Schristos 			} else if (c == '/' && flags & FNM_PATHNAME) {
1660f6e9a96Schristos 				if ((string = strchr(string, '/')) == NULL)
1670f6e9a96Schristos 					return FNM_NOMATCH;
1680f6e9a96Schristos 				break;
1690f6e9a96Schristos 			}
1700f6e9a96Schristos 
1710f6e9a96Schristos 			/* General case, use recursion. */
1720f6e9a96Schristos 			while ((test = FOLDCASE(*string, flags)) != EOS) {
1730f6e9a96Schristos 				int e;
1740f6e9a96Schristos 				switch ((e = fnmatchx(pattern, string,
1750f6e9a96Schristos 				    flags & ~FNM_PERIOD, recursion))) {
1760f6e9a96Schristos 				case FNM_NOMATCH:
1770f6e9a96Schristos 					break;
1780f6e9a96Schristos 				default:
1790f6e9a96Schristos 					return e;
1800f6e9a96Schristos 				}
1810f6e9a96Schristos 				if (test == '/' && flags & FNM_PATHNAME)
1820f6e9a96Schristos 					break;
1830f6e9a96Schristos 				++string;
1840f6e9a96Schristos 			}
1850f6e9a96Schristos 			return FNM_NOMATCH;
1860f6e9a96Schristos 		case '[':
1870f6e9a96Schristos 			if (*string == EOS)
1880f6e9a96Schristos 				return FNM_NOMATCH;
1890f6e9a96Schristos 			if (*string == '/' && flags & FNM_PATHNAME)
1900f6e9a96Schristos 				return FNM_NOMATCH;
191*02807d50Schristos 			if ((r = rangematch(pattern,
1920f6e9a96Schristos 			    FOLDCASE(*string, flags), flags)) == NULL)
1930f6e9a96Schristos 				return FNM_NOMATCH;
194*02807d50Schristos 			if (r == (void *)-1) {
195*02807d50Schristos 				if (*string != '[')
196*02807d50Schristos 					return FNM_NOMATCH;
197*02807d50Schristos 			} else
198*02807d50Schristos 				pattern = r;
1990f6e9a96Schristos 			++string;
2000f6e9a96Schristos 			break;
2010f6e9a96Schristos 		case '\\':
2020f6e9a96Schristos 			if (!(flags & FNM_NOESCAPE)) {
2030f6e9a96Schristos 				if ((c = FOLDCASE(*pattern++, flags)) == EOS) {
204a105f91cSchristos 					c = '\0';
2050f6e9a96Schristos 					--pattern;
2060f6e9a96Schristos 				}
2070f6e9a96Schristos 			}
2080f6e9a96Schristos 			/* FALLTHROUGH */
2090f6e9a96Schristos 		default:
2100f6e9a96Schristos 			if (c != FOLDCASE(*string++, flags))
2110f6e9a96Schristos 				return FNM_NOMATCH;
2120f6e9a96Schristos 			break;
2130f6e9a96Schristos 		}
2142d5180b4Schristos 	}
2150f6e9a96Schristos 	/* NOTREACHED */
2160f6e9a96Schristos }
2170f6e9a96Schristos 
2180f6e9a96Schristos int
fnmatch(const char * pattern,const char * string,int flags)2190f6e9a96Schristos fnmatch(const char *pattern, const char *string, int flags)
2200f6e9a96Schristos {
221984dfe5fSchristos 	return fnmatchx(pattern, string, flags, 64);
22261f28255Scgd }
223