blob: 8ba3b503f3b63ceef5a11c866a48039432fa6f92 [file] [log] [blame]
Alistair Delvabeaee832021-02-24 11:27:23 -08001/* Copyright 1986-1992 Emmet P. Gray.
2 * Copyright 1996-1998,2001,2002,2008,2009 Alain Knaff.
3 * This file is part of mtools.
4 *
5 * Mtools is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * Mtools is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with Mtools. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * Do shell-style pattern matching for '?', '\', '[..]', and '*' wildcards.
19 * Returns 1 if match, 0 if not.
20 */
21
22#include "sysincludes.h"
23#include "mtools.h"
24
25
26static int casecmp(wchar_t a, wchar_t b)
27{
28 return towupper((wint_t)a) == towupper((wint_t)b);
29}
30
31static int exactcmp(wchar_t a,wchar_t b)
32{
33 return a == b;
34}
35
36
37static int is_in_range(wchar_t ch, const wchar_t **p, int *reverse) {
38 wchar_t first, last;
39 int found=0;
40 if (**p == '^') {
41 *reverse = 1;
42 (*p)++;
43 } else
44 *reverse=0;
45 while( (first = **p) != ']') {
46 if(!first)
47 /* Malformed pattern, range not closed */
48 return 0;
49 if(*(++(*p)) == '-') {
50 last = *(++(*p));
51 if(last==']') {
52 /* Last "-" in range designates itself */
53 if(ch == first || ch == '-')
54 found = 1;
55 break;
56 }
57 (*p)++;
58
59 /* a proper range */
60 if(ch >= first && ch <= last)
61 found = 1;
62 } else
63 /* a Just one character */
64 if(ch == first)
65 found = 1;
66 }
67 return found;
68}
69
70static int parse_range(const wchar_t **p, const wchar_t *s, wchar_t *out,
71 int (*compfn)(wchar_t a, wchar_t b))
72{
73 int reverse;
74 const wchar_t *p0 = *p;
75 const wchar_t *p1 = *p;
76 if(out)
77 *out = *s;
78 if(is_in_range(*s, p, &reverse))
79 return 1 ^ reverse;
80 if(compfn == exactcmp)
81 return reverse;
82 if(is_in_range((wchar_t)towlower((wint_t)*s), &p0, &reverse)) {
83 if(out)
84 *out = (wchar_t)towlower((wint_t)*s);
85 return 1 ^ reverse;
86 }
87 if(is_in_range((wchar_t)towupper((wint_t)*s), &p1, &reverse)) {
88 if(out)
89 *out = (wchar_t)towupper((wint_t)*s);
90 return 1 ^ reverse;
91 }
92 return reverse;
93}
94
95
96static int _match(const wchar_t *s, const wchar_t *p, wchar_t *out, int Case,
97 int length,
98 int (*compfn) (wchar_t a, wchar_t b))
99{
100 for (; *p != '\0' && length; ) {
101 switch (*p) {
102 case '?': /* match any one character */
103 if (*s == '\0')
104 return(0);
105 if(out)
106 *(out++) = *s;
107 break;
108 case '*': /* match everything */
109 while (*p == '*' && length) {
110 p++;
111 length--;
112 }
113
114 /* search for next char in pattern */
115 while(*s) {
116 if(_match(s, p, out, Case, length,
117 compfn))
118 return 1;
119 if(out)
120 *out++ = *s;
121 s++;
122 }
123 continue;
124 case '[': /* match range of characters */
125 p++;
126 length--;
127 if(!parse_range(&p, s, out++, compfn))
128 return 0;
129 break;
130 case '\\': /* Literal match with next character */
131 p++;
132 length--;
133 /* fall thru */
134 default:
135 if (!compfn(*s,*p))
136 return(0);
137 if(out)
138 *(out++) = *p;
139 break;
140 }
141 p++;
142 length--;
143 s++;
144 }
145 if(out)
146 *out = '\0';
147
148 /* string ended prematurely ? */
149 if (*s != '\0')
150 return(0);
151 else
152 return(1);
153}
154
155
156int match(const wchar_t *s, const wchar_t *p, wchar_t *out, int Case, int length)
157{
158 int (*compfn)(wchar_t a, wchar_t b);
159
160 if(Case)
161 compfn = casecmp;
162 else
163 /*compfn = exactcmp;*/
164 compfn = casecmp;
165 return _match(s, p, out, Case, length, compfn);
166}
167