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
38typedef int boolean;
39#define FALSE 0
40#define TRUE  1
41
42#include "wu_fnmatch.h"
43
44#define	EOS '\0'
45
46static 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
93int 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