1*0Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI"
2*0Sstevel@tonic-gate
3*0Sstevel@tonic-gate /****************************************************************************
4*0Sstevel@tonic-gate Copyright (c) 1999,2000 WU-FTPD Development Group.
5*0Sstevel@tonic-gate All rights reserved.
6*0Sstevel@tonic-gate
7*0Sstevel@tonic-gate Portions Copyright (c) 1980, 1985, 1988, 1989, 1990, 1991, 1993, 1994
8*0Sstevel@tonic-gate The Regents of the University of California.
9*0Sstevel@tonic-gate Portions Copyright (c) 1993, 1994 Washington University in Saint Louis.
10*0Sstevel@tonic-gate Portions Copyright (c) 1996, 1998 Berkeley Software Design, Inc.
11*0Sstevel@tonic-gate Portions Copyright (c) 1989 Massachusetts Institute of Technology.
12*0Sstevel@tonic-gate Portions Copyright (c) 1998 Sendmail, Inc.
13*0Sstevel@tonic-gate Portions Copyright (c) 1983, 1995, 1996, 1997 Eric P. Allman.
14*0Sstevel@tonic-gate Portions Copyright (c) 1997 by Stan Barber.
15*0Sstevel@tonic-gate Portions Copyright (c) 1997 by Kent Landfield.
16*0Sstevel@tonic-gate Portions Copyright (c) 1991, 1992, 1993, 1994, 1995, 1996, 1997
17*0Sstevel@tonic-gate Free Software Foundation, Inc.
18*0Sstevel@tonic-gate
19*0Sstevel@tonic-gate Use and distribution of this software and its source code are governed
20*0Sstevel@tonic-gate by the terms and conditions of the WU-FTPD Software License ("LICENSE").
21*0Sstevel@tonic-gate
22*0Sstevel@tonic-gate If you did not receive a copy of the license, it may be obtained online
23*0Sstevel@tonic-gate at http://www.wu-ftpd.org/license.html.
24*0Sstevel@tonic-gate
25*0Sstevel@tonic-gate $Id: wu_fnmatch.c,v 1.7 2000/10/25 20:18:13 wuftpd Exp $
26*0Sstevel@tonic-gate
27*0Sstevel@tonic-gate ****************************************************************************/
28*0Sstevel@tonic-gate /*
29*0Sstevel@tonic-gate * Function fnmatch() as specified in POSIX 1003.2-1992, section B.6.
30*0Sstevel@tonic-gate * Compares a filename or pathname to a pattern.
31*0Sstevel@tonic-gate */
32*0Sstevel@tonic-gate
33*0Sstevel@tonic-gate #include <ctype.h>
34*0Sstevel@tonic-gate #include <stddef.h>
35*0Sstevel@tonic-gate #include <stdio.h>
36*0Sstevel@tonic-gate #include <string.h>
37*0Sstevel@tonic-gate
38*0Sstevel@tonic-gate typedef int boolean;
39*0Sstevel@tonic-gate #define FALSE 0
40*0Sstevel@tonic-gate #define TRUE 1
41*0Sstevel@tonic-gate
42*0Sstevel@tonic-gate #include "wu_fnmatch.h"
43*0Sstevel@tonic-gate
44*0Sstevel@tonic-gate #define EOS '\0'
45*0Sstevel@tonic-gate
rangematch(const char * pattern,const char * string,int flags)46*0Sstevel@tonic-gate static const char *rangematch(const char *pattern, const char *string, int flags)
47*0Sstevel@tonic-gate {
48*0Sstevel@tonic-gate /*
49*0Sstevel@tonic-gate * A bracket expression starting with an unquoted circumflex character
50*0Sstevel@tonic-gate * produces unspecified results (IEEE 1003.2-1992, 3.13.2). This
51*0Sstevel@tonic-gate * implementation treats it like '!', for consistency with the regular
52*0Sstevel@tonic-gate * expression syntax. J.T. Conklin (conklin@ngai.kaleida.com)
53*0Sstevel@tonic-gate */
54*0Sstevel@tonic-gate char test = *string;
55*0Sstevel@tonic-gate boolean negate = ((*pattern == '!') || (*pattern == '^'));
56*0Sstevel@tonic-gate boolean ok = FALSE;
57*0Sstevel@tonic-gate if (negate)
58*0Sstevel@tonic-gate ++pattern;
59*0Sstevel@tonic-gate if (flags & FNM_CASEFOLD)
60*0Sstevel@tonic-gate test = tolower((unsigned char) test);
61*0Sstevel@tonic-gate while (*pattern != ']') {
62*0Sstevel@tonic-gate char c = *pattern++;
63*0Sstevel@tonic-gate if ((c == '\\') && !(flags & FNM_NOESCAPE))
64*0Sstevel@tonic-gate c = *pattern++;
65*0Sstevel@tonic-gate if (c == EOS)
66*0Sstevel@tonic-gate return (NULL);
67*0Sstevel@tonic-gate if (flags & FNM_CASEFOLD)
68*0Sstevel@tonic-gate c = tolower((unsigned char) c);
69*0Sstevel@tonic-gate if (*pattern == '-') {
70*0Sstevel@tonic-gate char c2 = pattern[1];
71*0Sstevel@tonic-gate if ((c2 != EOS)
72*0Sstevel@tonic-gate && (c2 != ']')) {
73*0Sstevel@tonic-gate pattern += 2;
74*0Sstevel@tonic-gate if ((c2 == '\\') && !(flags & FNM_NOESCAPE))
75*0Sstevel@tonic-gate c2 = *pattern++;
76*0Sstevel@tonic-gate if (c2 == EOS)
77*0Sstevel@tonic-gate return (NULL);
78*0Sstevel@tonic-gate if (flags & FNM_CASEFOLD)
79*0Sstevel@tonic-gate c2 = tolower((unsigned char) c2);
80*0Sstevel@tonic-gate /* this is a hack */
81*0Sstevel@tonic-gate if ((c <= test) && (test <= c2))
82*0Sstevel@tonic-gate ok = TRUE;
83*0Sstevel@tonic-gate }
84*0Sstevel@tonic-gate else if (c == test)
85*0Sstevel@tonic-gate ok = TRUE;
86*0Sstevel@tonic-gate }
87*0Sstevel@tonic-gate else if (c == test)
88*0Sstevel@tonic-gate ok = TRUE;
89*0Sstevel@tonic-gate }
90*0Sstevel@tonic-gate return ((ok == negate) ? NULL : pattern+1);
91*0Sstevel@tonic-gate }
92*0Sstevel@tonic-gate
wu_fnmatch(const char * pattern,const char * string,int flags)93*0Sstevel@tonic-gate int wu_fnmatch(const char *pattern, const char *string, int flags)
94*0Sstevel@tonic-gate {
95*0Sstevel@tonic-gate const char *stringstart = string;
96*0Sstevel@tonic-gate if ((pattern == NULL) || (string == NULL))
97*0Sstevel@tonic-gate return FNM_NOMATCH;
98*0Sstevel@tonic-gate while (TRUE) {
99*0Sstevel@tonic-gate char test;
100*0Sstevel@tonic-gate char c = *pattern++;
101*0Sstevel@tonic-gate switch (c) {
102*0Sstevel@tonic-gate case EOS:
103*0Sstevel@tonic-gate #ifdef FNM_LEADING_DIR
104*0Sstevel@tonic-gate if ((flags & FNM_LEADING_DIR)
105*0Sstevel@tonic-gate && (*string == '/'))
106*0Sstevel@tonic-gate return (0);
107*0Sstevel@tonic-gate /*
108*0Sstevel@tonic-gate * WU-FTPD extension/correction.
109*0Sstevel@tonic-gate *
110*0Sstevel@tonic-gate * If the pattern ended with a '/', and we're doing
111*0Sstevel@tonic-gate * FNM_PATHNAME matching, consider it a match if the
112*0Sstevel@tonic-gate * previous string character was a '/' and the current
113*0Sstevel@tonic-gate * is not a '/'.
114*0Sstevel@tonic-gate */
115*0Sstevel@tonic-gate if ((flags & FNM_LEADING_DIR)
116*0Sstevel@tonic-gate && (string != stringstart)
117*0Sstevel@tonic-gate && (flags & FNM_PATHNAME)
118*0Sstevel@tonic-gate && (*(string - 1) == '/'))
119*0Sstevel@tonic-gate return (0);
120*0Sstevel@tonic-gate #endif
121*0Sstevel@tonic-gate return ((*string == EOS) ? 0 : FNM_NOMATCH);
122*0Sstevel@tonic-gate case '?':
123*0Sstevel@tonic-gate if (*string == EOS)
124*0Sstevel@tonic-gate return (FNM_NOMATCH);
125*0Sstevel@tonic-gate if ((*string == '/')
126*0Sstevel@tonic-gate && (flags & FNM_PATHNAME))
127*0Sstevel@tonic-gate return (FNM_NOMATCH);
128*0Sstevel@tonic-gate if ((*string == '.')
129*0Sstevel@tonic-gate && (flags & FNM_PERIOD)
130*0Sstevel@tonic-gate && ((string == stringstart)
131*0Sstevel@tonic-gate || ((flags & FNM_PATHNAME)
132*0Sstevel@tonic-gate && (*(string - 1) == '/'))))
133*0Sstevel@tonic-gate return (FNM_NOMATCH);
134*0Sstevel@tonic-gate ++string;
135*0Sstevel@tonic-gate break;
136*0Sstevel@tonic-gate case '*':
137*0Sstevel@tonic-gate c = *pattern;
138*0Sstevel@tonic-gate while (c == '*')
139*0Sstevel@tonic-gate c = *++pattern;
140*0Sstevel@tonic-gate if ((*string == '.')
141*0Sstevel@tonic-gate && (flags & FNM_PERIOD)
142*0Sstevel@tonic-gate && ((string == stringstart)
143*0Sstevel@tonic-gate || ((flags & FNM_PATHNAME)
144*0Sstevel@tonic-gate && (*(string - 1) == '/'))))
145*0Sstevel@tonic-gate return (FNM_NOMATCH);
146*0Sstevel@tonic-gate /* Optimize for pattern with * at end or before /. */
147*0Sstevel@tonic-gate if (c == EOS)
148*0Sstevel@tonic-gate if (flags & FNM_PATHNAME) {
149*0Sstevel@tonic-gate #ifdef FNM_LEADING_DIR
150*0Sstevel@tonic-gate if (flags & FNM_LEADING_DIR)
151*0Sstevel@tonic-gate return (0);
152*0Sstevel@tonic-gate #endif
153*0Sstevel@tonic-gate return ((strchr(string, '/') == NULL) ? 0 : FNM_NOMATCH);
154*0Sstevel@tonic-gate }
155*0Sstevel@tonic-gate else
156*0Sstevel@tonic-gate return (0);
157*0Sstevel@tonic-gate else if ((c == '/')
158*0Sstevel@tonic-gate && (flags & FNM_PATHNAME)) {
159*0Sstevel@tonic-gate string = strchr(string, '/');
160*0Sstevel@tonic-gate if (string == NULL)
161*0Sstevel@tonic-gate return (FNM_NOMATCH);
162*0Sstevel@tonic-gate break;
163*0Sstevel@tonic-gate }
164*0Sstevel@tonic-gate /* General case, use recursion. */
165*0Sstevel@tonic-gate for (test = *string; test != EOS; test = *++string) {
166*0Sstevel@tonic-gate if (!wu_fnmatch(pattern, string, (flags & ~FNM_PERIOD)))
167*0Sstevel@tonic-gate return (0);
168*0Sstevel@tonic-gate if ((test == '/')
169*0Sstevel@tonic-gate && (flags & FNM_PATHNAME))
170*0Sstevel@tonic-gate break;
171*0Sstevel@tonic-gate }
172*0Sstevel@tonic-gate return (FNM_NOMATCH);
173*0Sstevel@tonic-gate case '[':
174*0Sstevel@tonic-gate if (*string == EOS)
175*0Sstevel@tonic-gate return (FNM_NOMATCH);
176*0Sstevel@tonic-gate if ((*string == '/')
177*0Sstevel@tonic-gate && (flags & FNM_PATHNAME))
178*0Sstevel@tonic-gate return (FNM_NOMATCH);
179*0Sstevel@tonic-gate pattern = rangematch(pattern, string, flags);
180*0Sstevel@tonic-gate if (pattern == NULL)
181*0Sstevel@tonic-gate return (FNM_NOMATCH);
182*0Sstevel@tonic-gate ++string;
183*0Sstevel@tonic-gate break;
184*0Sstevel@tonic-gate case '\\':
185*0Sstevel@tonic-gate if (!(flags & FNM_NOESCAPE)) {
186*0Sstevel@tonic-gate c = *pattern++;
187*0Sstevel@tonic-gate if (c == EOS) {
188*0Sstevel@tonic-gate c = '\\';
189*0Sstevel@tonic-gate --pattern;
190*0Sstevel@tonic-gate }
191*0Sstevel@tonic-gate }
192*0Sstevel@tonic-gate /* FALLTHROUGH */
193*0Sstevel@tonic-gate default:
194*0Sstevel@tonic-gate if (c == *string);
195*0Sstevel@tonic-gate #ifdef FNM_CASEFOLD
196*0Sstevel@tonic-gate else if ((flags & FNM_CASEFOLD)
197*0Sstevel@tonic-gate && (tolower((unsigned char) c) == tolower((unsigned char) *string)));
198*0Sstevel@tonic-gate #endif
199*0Sstevel@tonic-gate else
200*0Sstevel@tonic-gate return (FNM_NOMATCH);
201*0Sstevel@tonic-gate string++;
202*0Sstevel@tonic-gate break;
203*0Sstevel@tonic-gate }
204*0Sstevel@tonic-gate }
205*0Sstevel@tonic-gate /* NOTREACHED */
206*0Sstevel@tonic-gate }
207