150477Speter/*
21598Srgrimes * Copyright (c) 1989, 1993, 1994
358284Speter *	The Regents of the University of California.  All rights reserved.
4134007Sdes *
5129269Scognet * This code is derived from software contributed to Berkeley by
6116865Speter * Guido van Rossum.
7129269Scognet *
8134007Sdes * Redistribution and use in source and binary forms, with or without
91598Srgrimes * modification, are permitted provided that the following conditions
10143145Sgrog * are met:
11143145Sgrog * 1. Redistributions of source code must retain the above copyright
12143145Sgrog *    notice, this list of conditions and the following disclaimer.
13143145Sgrog * 2. Redistributions in binary form must reproduce the above copyright
14143145Sgrog *    notice, this list of conditions and the following disclaimer in the
15143145Sgrog *    documentation and/or other materials provided with the distribution.
16143145Sgrog * 4. Neither the name of the University nor the names of its contributors
17143145Sgrog *    may be used to endorse or promote products derived from this software
18124610Sru *    without specific prior written permission.
19143145Sgrog *
2061744Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2161744Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2261744Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2361744Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2427356Sjkh * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2527356Sjkh * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26143145Sgrog * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27143145Sgrog * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28143145Sgrog * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29143145Sgrog * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30143145Sgrog * SUCH DAMAGE.
31143145Sgrog *
32143145Sgrog * $FreeBSD$
33143145Sgrog *
34143145Sgrog * From FreeBSD fnmatch.c 1.11
35143145Sgrog * $Id: fnmatch.c,v 1.3 1997/08/19 02:34:30 jdp Exp $
361598Srgrimes */
37
38#if defined(LIBC_SCCS) && !defined(lint)
39static char sccsid[] = "@(#)fnmatch.c	8.2 (Berkeley) 4/16/94";
40#endif /* LIBC_SCCS and not lint */
41
42/*
43 * Function fnmatch() as specified in POSIX 1003.2-1992, section B.6.
44 * Compares a filename or pathname to a pattern.
45 */
46
47#include <ctype.h>
48#include <string.h>
49#include <stdio.h>
50
51#include "fnmatch.h"
52
53#define	EOS	'\0'
54
55static const char *rangematch(const char *, char, int);
56
57int
58fnmatch(const char *pattern, const char *string, int flags)
59{
60	const char *stringstart;
61	char c, test;
62
63	for (stringstart = string;;)
64		switch (c = *pattern++) {
65		case EOS:
66			if ((flags & FNM_LEADING_DIR) && *string == '/')
67				return (0);
68			return (*string == EOS ? 0 : FNM_NOMATCH);
69		case '?':
70			if (*string == EOS)
71				return (FNM_NOMATCH);
72			if (*string == '/' && (flags & FNM_PATHNAME))
73				return (FNM_NOMATCH);
74			if (*string == '.' && (flags & FNM_PERIOD) &&
75			    (string == stringstart ||
76			    ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
77				return (FNM_NOMATCH);
78			++string;
79			break;
80		case '*':
81			c = *pattern;
82			/* Collapse multiple stars. */
83			while (c == '*')
84				c = *++pattern;
85
86			if (*string == '.' && (flags & FNM_PERIOD) &&
87			    (string == stringstart ||
88			    ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
89				return (FNM_NOMATCH);
90
91			/* Optimize for pattern with * at end or before /. */
92			if (c == EOS)
93				if (flags & FNM_PATHNAME)
94					return ((flags & FNM_LEADING_DIR) ||
95					    strchr(string, '/') == NULL ?
96					    0 : FNM_NOMATCH);
97				else
98					return (0);
99			else if (c == '/' && flags & FNM_PATHNAME) {
100				if ((string = strchr(string, '/')) == NULL)
101					return (FNM_NOMATCH);
102				break;
103			}
104
105			/* General case, use recursion. */
106			while ((test = *string) != EOS) {
107				if (!fnmatch(pattern, string, flags & ~FNM_PERIOD))
108					return (0);
109				if (test == '/' && flags & FNM_PATHNAME)
110					break;
111				++string;
112			}
113			return (FNM_NOMATCH);
114		case '[':
115			if (*string == EOS)
116				return (FNM_NOMATCH);
117			if (*string == '/' && flags & FNM_PATHNAME)
118				return (FNM_NOMATCH);
119			if ((pattern =
120			    rangematch(pattern, *string, flags)) == NULL)
121				return (FNM_NOMATCH);
122			++string;
123			break;
124		case '\\':
125			if (!(flags & FNM_NOESCAPE)) {
126				if ((c = *pattern++) == EOS) {
127					c = '\\';
128					--pattern;
129				}
130			}
131			/* FALLTHROUGH */
132		default:
133			if (c == *string)
134				;
135			else if ((flags & FNM_CASEFOLD) &&
136				 (tolower((unsigned char)c) ==
137				  tolower((unsigned char)*string)))
138				;
139			else if ((flags & FNM_PREFIX_DIRS) && *string == EOS &&
140			     ((c == '/' && string != stringstart) ||
141			     (string == stringstart+1 && *stringstart == '/')))
142				return (0);
143			else
144				return (FNM_NOMATCH);
145			string++;
146			break;
147		}
148	/* NOTREACHED */
149}
150
151static const char *
152rangematch(const char *pattern, char test, int flags)
153{
154	int negate, ok;
155	char c, c2;
156
157	/*
158	 * A bracket expression starting with an unquoted circumflex
159	 * character produces unspecified results (IEEE 1003.2-1992,
160	 * 3.13.2).  This implementation treats it like '!', for
161	 * consistency with the regular expression syntax.
162	 * J.T. Conklin (conklin@ngai.kaleida.com)
163	 */
164	if ( (negate = (*pattern == '!' || *pattern == '^')) )
165		++pattern;
166
167	if (flags & FNM_CASEFOLD)
168		test = tolower((unsigned char)test);
169
170	for (ok = 0; (c = *pattern++) != ']';) {
171		if (c == '\\' && !(flags & FNM_NOESCAPE))
172			c = *pattern++;
173		if (c == EOS)
174			return (NULL);
175
176		if (flags & FNM_CASEFOLD)
177			c = tolower((unsigned char)c);
178
179		if (*pattern == '-'
180		    && (c2 = *(pattern+1)) != EOS && c2 != ']') {
181			pattern += 2;
182			if (c2 == '\\' && !(flags & FNM_NOESCAPE))
183				c2 = *pattern++;
184			if (c2 == EOS)
185				return (NULL);
186
187			if (flags & FNM_CASEFOLD)
188				c2 = tolower((unsigned char)c2);
189
190			if ((unsigned char)c <= (unsigned char)test &&
191			    (unsigned char)test <= (unsigned char)c2)
192				ok = 1;
193		} else if (c == test)
194			ok = 1;
195	}
196	return (ok == negate ? NULL : pattern);
197}
198