1 /**
2 * WinPR: Windows Portable Runtime
3 * File Functions
4 *
5 * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 */
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include <winpr/crt.h>
25 #include <winpr/handle.h>
26
27 #include <winpr/file.h>
28
29 #ifdef HAVE_UNISTD_H
30 #include <unistd.h>
31 #endif
32
33 #ifdef HAVE_FCNTL_H
34 #include <fcntl.h>
35 #endif
36
37 #include "../log.h"
38 #define TAG WINPR_TAG("file")
39
40 /**
41 * File System Behavior in the Microsoft Windows Environment:
42 * http://download.microsoft.com/download/4/3/8/43889780-8d45-4b2e-9d3a-c696a890309f/File%20System%20Behavior%20Overview.pdf
43 */
44
FilePatternFindNextWildcardA(LPCSTR lpPattern,DWORD * pFlags)45 LPSTR FilePatternFindNextWildcardA(LPCSTR lpPattern, DWORD* pFlags)
46 {
47 LPSTR lpWildcard;
48 *pFlags = 0;
49 lpWildcard = strpbrk(lpPattern, "*?~");
50
51 if (lpWildcard)
52 {
53 if (*lpWildcard == '*')
54 {
55 *pFlags = WILDCARD_STAR;
56 return lpWildcard;
57 }
58 else if (*lpWildcard == '?')
59 {
60 *pFlags = WILDCARD_QM;
61 return lpWildcard;
62 }
63 else if (*lpWildcard == '~')
64 {
65 if (lpWildcard[1] == '*')
66 {
67 *pFlags = WILDCARD_DOS_STAR;
68 return lpWildcard;
69 }
70 else if (lpWildcard[1] == '?')
71 {
72 *pFlags = WILDCARD_DOS_QM;
73 return lpWildcard;
74 }
75 else if (lpWildcard[1] == '.')
76 {
77 *pFlags = WILDCARD_DOS_DOT;
78 return lpWildcard;
79 }
80 }
81 }
82
83 return NULL;
84 }
85
FilePatternMatchSubExpressionA(LPCSTR lpFileName,size_t cchFileName,LPCSTR lpX,size_t cchX,LPCSTR lpY,size_t cchY,LPCSTR lpWildcard,LPSTR * ppMatchEnd)86 static BOOL FilePatternMatchSubExpressionA(LPCSTR lpFileName, size_t cchFileName, LPCSTR lpX,
87 size_t cchX, LPCSTR lpY, size_t cchY, LPCSTR lpWildcard,
88 LPSTR* ppMatchEnd)
89 {
90 LPSTR lpMatch;
91
92 if (!lpFileName)
93 return FALSE;
94
95 if (*lpWildcard == '*')
96 {
97 /*
98 * S
99 * <-----<
100 * X | | e Y
101 * X * Y == (0)----->-(1)->-----(2)-----(3)
102 */
103
104 /*
105 * State 0: match 'X'
106 */
107 if (_strnicmp(lpFileName, lpX, cchX) != 0)
108 return FALSE;
109
110 /*
111 * State 1: match 'S' or 'e'
112 *
113 * We use 'e' to transition to state 2
114 */
115
116 /**
117 * State 2: match Y
118 */
119
120 if (cchY != 0)
121 {
122 /* TODO: case insensitive character search */
123 lpMatch = strchr(&lpFileName[cchX], *lpY);
124
125 if (!lpMatch)
126 return FALSE;
127
128 if (_strnicmp(lpMatch, lpY, cchY) != 0)
129 return FALSE;
130 }
131 else
132 {
133 lpMatch = (LPSTR)&lpFileName[cchFileName];
134 }
135
136 /**
137 * State 3: final state
138 */
139 *ppMatchEnd = (LPSTR)&lpMatch[cchY];
140 return TRUE;
141 }
142 else if (*lpWildcard == '?')
143 {
144 /**
145 * X S Y
146 * X ? Y == (0)---(1)---(2)---(3)
147 */
148
149 /*
150 * State 0: match 'X'
151 */
152 if (cchFileName < cchX)
153 return FALSE;
154
155 if (_strnicmp(lpFileName, lpX, cchX) != 0)
156 return FALSE;
157
158 /*
159 * State 1: match 'S'
160 */
161
162 /**
163 * State 2: match Y
164 */
165
166 if (cchY != 0)
167 {
168 /* TODO: case insensitive character search */
169 lpMatch = strchr(&lpFileName[cchX + 1], *lpY);
170
171 if (!lpMatch)
172 return FALSE;
173
174 if (_strnicmp(lpMatch, lpY, cchY) != 0)
175 return FALSE;
176 }
177 else
178 {
179 if ((cchX + 1) > cchFileName)
180 return FALSE;
181
182 lpMatch = (LPSTR)&lpFileName[cchX + 1];
183 }
184
185 /**
186 * State 3: final state
187 */
188 *ppMatchEnd = (LPSTR)&lpMatch[cchY];
189 return TRUE;
190 }
191 else if (*lpWildcard == '~')
192 {
193 WLog_ERR(TAG, "warning: unimplemented '~' pattern match");
194 return TRUE;
195 }
196
197 return FALSE;
198 }
199
FilePatternMatchA(LPCSTR lpFileName,LPCSTR lpPattern)200 BOOL FilePatternMatchA(LPCSTR lpFileName, LPCSTR lpPattern)
201 {
202 BOOL match;
203 LPSTR lpTail;
204 size_t cchTail;
205 size_t cchPattern;
206 size_t cchFileName;
207 DWORD dwFlags;
208 DWORD dwNextFlags;
209 LPSTR lpWildcard;
210 LPSTR lpNextWildcard;
211
212 /**
213 * Wild Card Matching
214 *
215 * '*' matches 0 or more characters
216 * '?' matches exactly one character
217 *
218 * '~*' DOS_STAR - matches 0 or more characters until encountering and matching final '.'
219 *
220 * '~?' DOS_QM - matches any single character, or upon encountering a period or end of name
221 * string, advances the expresssion to the end of the set of contiguous DOS_QMs.
222 *
223 * '~.' DOS_DOT - matches either a '.' or zero characters beyond name string.
224 */
225
226 if (!lpPattern)
227 return FALSE;
228
229 if (!lpFileName)
230 return FALSE;
231
232 cchPattern = strlen(lpPattern);
233 cchFileName = strlen(lpFileName);
234
235 /**
236 * First and foremost the file system starts off name matching with the expression “*”.
237 * If the expression contains a single wild card character ‘*’ all matches are satisfied
238 * immediately. This is the most common wild card character used in Windows and expression
239 * evaluation is optimized by looking for this character first.
240 */
241
242 if ((lpPattern[0] == '*') && (cchPattern == 1))
243 return TRUE;
244
245 /**
246 * Subsequently evaluation of the “*X” expression is performed. This is a case where
247 * the expression starts off with a wild card character and contains some non-wild card
248 * characters towards the tail end of the name. This is evaluated by making sure the
249 * expression starts off with the character ‘*’ and does not contain any wildcards in
250 * the latter part of the expression. The tail part of the expression beyond the first
251 * character ‘*’ is matched against the file name at the end uppercasing each character
252 * if necessary during the comparison.
253 */
254
255 if (lpPattern[0] == '*')
256 {
257 lpTail = (LPSTR)&lpPattern[1];
258 cchTail = strlen(lpTail);
259
260 if (!FilePatternFindNextWildcardA(lpTail, &dwFlags))
261 {
262 /* tail contains no wildcards */
263 if (cchFileName < cchTail)
264 return FALSE;
265
266 if (_stricmp(&lpFileName[cchFileName - cchTail], lpTail) == 0)
267 return TRUE;
268
269 return FALSE;
270 }
271 }
272
273 /**
274 * The remaining expressions are evaluated in a non deterministic
275 * finite order as listed below, where:
276 *
277 * 'S' is any single character
278 * 'S-.' is any single character except the final '.'
279 * 'e' is a null character transition
280 * 'EOF' is the end of the name string
281 *
282 * S
283 * <-----<
284 * X | | e Y
285 * X * Y == (0)----->-(1)->-----(2)-----(3)
286 *
287 *
288 * S-.
289 * <-----<
290 * X | | e Y
291 * X ~* Y == (0)----->-(1)->-----(2)-----(3)
292 *
293 *
294 * X S S Y
295 * X ?? Y == (0)---(1)---(2)---(3)---(4)
296 *
297 *
298 * X S-. S-. Y
299 * X ~?~? == (0)---(1)-----(2)-----(3)---(4)
300 * | |_______|
301 * | ^ |
302 * |_______________|
303 * ^EOF of .^
304 *
305 */
306 lpWildcard = FilePatternFindNextWildcardA(lpPattern, &dwFlags);
307
308 if (lpWildcard)
309 {
310 LPSTR lpX;
311 LPSTR lpY;
312 size_t cchX;
313 size_t cchY;
314 LPSTR lpMatchEnd = NULL;
315 LPSTR lpSubPattern;
316 size_t cchSubPattern;
317 LPSTR lpSubFileName;
318 size_t cchSubFileName;
319 size_t cchWildcard;
320 size_t cchNextWildcard;
321 cchSubPattern = cchPattern;
322 lpSubPattern = (LPSTR)lpPattern;
323 cchSubFileName = cchFileName;
324 lpSubFileName = (LPSTR)lpFileName;
325 cchWildcard = ((dwFlags & WILDCARD_DOS) ? 2 : 1);
326 lpNextWildcard = FilePatternFindNextWildcardA(&lpWildcard[cchWildcard], &dwNextFlags);
327
328 if (!lpNextWildcard)
329 {
330 lpX = (LPSTR)lpSubPattern;
331 cchX = (lpWildcard - lpSubPattern);
332 lpY = (LPSTR)&lpSubPattern[cchX + cchWildcard];
333 cchY = (cchSubPattern - (lpY - lpSubPattern));
334 match = FilePatternMatchSubExpressionA(lpSubFileName, cchSubFileName, lpX, cchX, lpY,
335 cchY, lpWildcard, &lpMatchEnd);
336 return match;
337 }
338 else
339 {
340 while (lpNextWildcard)
341 {
342 cchSubFileName = cchFileName - (lpSubFileName - lpFileName);
343 cchNextWildcard = ((dwNextFlags & WILDCARD_DOS) ? 2 : 1);
344 lpX = (LPSTR)lpSubPattern;
345 cchX = (lpWildcard - lpSubPattern);
346 lpY = (LPSTR)&lpSubPattern[cchX + cchWildcard];
347 cchY = (lpNextWildcard - lpWildcard) - cchWildcard;
348 match = FilePatternMatchSubExpressionA(lpSubFileName, cchSubFileName, lpX, cchX,
349 lpY, cchY, lpWildcard, &lpMatchEnd);
350
351 if (!match)
352 return FALSE;
353
354 lpSubFileName = lpMatchEnd;
355 cchWildcard = cchNextWildcard;
356 lpWildcard = lpNextWildcard;
357 dwFlags = dwNextFlags;
358 lpNextWildcard =
359 FilePatternFindNextWildcardA(&lpWildcard[cchWildcard], &dwNextFlags);
360 }
361
362 return TRUE;
363 }
364 }
365 else
366 {
367 /* no wildcard characters */
368 if (_stricmp(lpFileName, lpPattern) == 0)
369 return TRUE;
370 }
371
372 return FALSE;
373 }
374