xref: /onnv-gate/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/wu_fnmatch.c (revision 0:68f95e015346)
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