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