]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - usr.bin/csup/fnmatch.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / usr.bin / csup / fnmatch.c
1 /*
2  * Copyright (c) 1989, 1993, 1994
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Guido van Rossum.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 4. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  * $FreeBSD$
33  *
34  * From FreeBSD fnmatch.c 1.11
35  * $Id: fnmatch.c,v 1.3 1997/08/19 02:34:30 jdp Exp $
36  */
37
38 #if defined(LIBC_SCCS) && !defined(lint)
39 static 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
55 static const char *rangematch(const char *, char, int);
56
57 int
58 fnmatch(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
151 static const char *
152 rangematch(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 }