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