1 /* SPDX-License-Identifier: BSD-3-Clause */
2 
3 /*    $NetBSD: fnmatch.c,v 1.26 2014/10/12 22:32:33 christos Exp $    */
4 
5 /*
6  * Copyright (c) 1989, 1993, 1994
7  *    The Regents of the University of California.  All rights reserved.
8  *
9  * This code is derived from software contributed to Berkeley by
10  * Guido van Rossum.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  * 3. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  */
36 
37 /*
38  * Function fnmatch() as specified in POSIX 1003.2-1992, section B.6.
39  * Compares a filename or pathname to a pattern.
40  */
41 
42 #include <ctype.h>
43 #include "fnmatch.h"
44 #include <string.h>
45 
46 #define EOS    '\0'
47 
foldcase(int ch,int flags)48 static inline int foldcase(int ch, int flags)
49 {
50 
51     if ((flags & FNM_CASEFOLD) != 0 && isupper(ch))
52         return tolower(ch);
53     return ch;
54 }
55 
56 #define    FOLDCASE(ch, flags)    foldcase((unsigned char)(ch), (flags))
57 
rangematch(const char * pattern,int test,int flags)58 static const char * rangematch(const char *pattern, int test, int flags)
59 {
60     int negate, ok, need;
61     char c, c2;
62 
63     if (pattern == NULL)
64     {
65         return NULL;
66     }
67 
68     /*
69      * A bracket expression starting with an unquoted circumflex
70      * character produces unspecified results (IEEE 1003.2-1992,
71      * 3.13.2).  This implementation treats it like '!', for
72      * consistency with the regular expression syntax.
73      * J.T. Conklin (conklin@ngai.kaleida.com)
74      */
75     if ((negate = (*pattern == '!' || *pattern == '^')) != 0)
76         ++pattern;
77 
78     need = 1;
79     for (ok = 0; (c = FOLDCASE(*pattern++, flags)) != ']' || need;) {
80         need = 0;
81         if (c == '/')
82             return (void *)-1;
83         if (c == '\\' && !(flags & FNM_NOESCAPE))
84             c = FOLDCASE(*pattern++, flags);
85         if (c == EOS)
86             return NULL;
87         if (*pattern == '-'
88             && (c2 = FOLDCASE(*(pattern + 1), flags)) != EOS &&
89                 c2 != ']') {
90             pattern += 2;
91             if (c2 == '\\' && !(flags & FNM_NOESCAPE))
92                 c2 = FOLDCASE(*pattern++, flags);
93             if (c2 == EOS)
94                 return NULL;
95             if (c <= test && test <= c2)
96                 ok = 1;
97         } else if (c == test)
98             ok = 1;
99     }
100     return ok == negate ? NULL : pattern;
101 }
102 
103 
fnmatchx(const char * pattern,const char * string,int flags,size_t recursion)104 static int fnmatchx(const char *pattern, const char *string, int flags, size_t recursion)
105 {
106     const char *stringstart, *r;
107     char c, test;
108 
109     if ((pattern == NULL) || (string == NULL))
110     {
111         return FNM_NOMATCH;
112     }
113 
114     if (recursion-- == 0)
115         return FNM_NORES;
116 
117     for (stringstart = string;;) {
118         switch (c = FOLDCASE(*pattern++, flags)) {
119         case EOS:
120             if ((flags & FNM_LEADING_DIR) && *string == '/')
121                 return 0;
122             return *string == EOS ? 0 : FNM_NOMATCH;
123         case '?':
124             if (*string == EOS)
125                 return FNM_NOMATCH;
126             if (*string == '/' && (flags & FNM_PATHNAME))
127                 return FNM_NOMATCH;
128             if (*string == '.' && (flags & FNM_PERIOD) &&
129                 (string == stringstart ||
130                 ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
131                 return FNM_NOMATCH;
132             ++string;
133             break;
134         case '*':
135             c = FOLDCASE(*pattern, flags);
136             /* Collapse multiple stars. */
137             while (c == '*')
138                 c = FOLDCASE(*++pattern, flags);
139 
140             if (*string == '.' && (flags & FNM_PERIOD) &&
141                 (string == stringstart ||
142                 ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
143                 return FNM_NOMATCH;
144 
145             /* Optimize for pattern with * at end or before /. */
146             if (c == EOS) {
147                 if (flags & FNM_PATHNAME)
148                     return (flags & FNM_LEADING_DIR) ||
149                         strchr(string, '/') == NULL ?
150                         0 : FNM_NOMATCH;
151                 else
152                     return 0;
153             } else if (c == '/' && flags & FNM_PATHNAME) {
154                 if ((string = strchr(string, '/')) == NULL)
155                     return FNM_NOMATCH;
156                 break;
157             }
158 
159             /* General case, use recursion. */
160             while ((test = FOLDCASE(*string, flags)) != EOS) {
161                 int e;
162                 switch ((e = fnmatchx(pattern, string,
163                     flags & ~FNM_PERIOD, recursion))) {
164                 case FNM_NOMATCH:
165                     break;
166                 default:
167                     return e;
168                 }
169                 if (test == '/' && flags & FNM_PATHNAME)
170                     break;
171                 ++string;
172             }
173             return FNM_NOMATCH;
174         case '[':
175             if (*string == EOS)
176                 return FNM_NOMATCH;
177             if (*string == '/' && flags & FNM_PATHNAME)
178                 return FNM_NOMATCH;
179             if ((r = rangematch(pattern,
180                 FOLDCASE(*string, flags), flags)) == NULL)
181                 return FNM_NOMATCH;
182             if (r == (void *)-1) {
183                 if (*string != '[')
184                     return FNM_NOMATCH;
185             } else
186                 pattern = r;
187             ++string;
188             break;
189         case '\\':
190             if (!(flags & FNM_NOESCAPE)) {
191                 if ((c = FOLDCASE(*pattern++, flags)) == EOS) {
192                     c = '\0';
193                     --pattern;
194                 }
195             }
196             /* FALLTHROUGH */
197         default:
198             if (c != FOLDCASE(*string++, flags))
199                 return FNM_NOMATCH;
200             break;
201         }
202     }
203     /* NOTREACHED */
204 }
205 
fnmatch(const char * pattern,const char * string,int flags)206 int fnmatch(const char *pattern, const char *string, int flags)
207 {
208     return fnmatchx(pattern, string, flags, 64);
209 }
210