FreeRDP
Loading...
Searching...
No Matches
pattern.c
1
20#include <winpr/config.h>
21
22#include <winpr/crt.h>
23#include <winpr/handle.h>
24
25#include <winpr/file.h>
26
27#ifdef WINPR_HAVE_UNISTD_H
28#include <unistd.h>
29#endif
30
31#ifdef WINPR_HAVE_FCNTL_H
32#include <fcntl.h>
33#endif
34
35#include "../log.h"
36#define TAG WINPR_TAG("file")
37
43LPSTR FilePatternFindNextWildcardA(LPCSTR lpPattern, DWORD* pFlags)
44{
45 LPSTR lpWildcard = nullptr;
46 *pFlags = 0;
47 lpWildcard = strpbrk(lpPattern, "*?~");
48
49 if (lpWildcard)
50 {
51 if (*lpWildcard == '*')
52 {
53 *pFlags = WILDCARD_STAR;
54 return lpWildcard;
55 }
56 else if (*lpWildcard == '?')
57 {
58 *pFlags = WILDCARD_QM;
59 return lpWildcard;
60 }
61 else if (*lpWildcard == '~')
62 {
63 if (lpWildcard[1] == '*')
64 {
65 *pFlags = WILDCARD_DOS_STAR;
66 return lpWildcard;
67 }
68 else if (lpWildcard[1] == '?')
69 {
70 *pFlags = WILDCARD_DOS_QM;
71 return lpWildcard;
72 }
73 else if (lpWildcard[1] == '.')
74 {
75 *pFlags = WILDCARD_DOS_DOT;
76 return lpWildcard;
77 }
78 }
79 }
80
81 return nullptr;
82}
83
84static BOOL FilePatternMatchSubExpressionA(LPCSTR lpFileName, size_t cchFileName, LPCSTR lpX,
85 size_t cchX, LPCSTR lpY, size_t cchY, LPCSTR lpWildcard,
86 LPCSTR* ppMatchEnd)
87{
88 LPCSTR lpMatch = nullptr;
89
90 if (!lpFileName)
91 return FALSE;
92
93 if (*lpWildcard == '*')
94 {
95 /*
96 * S
97 * <-----<
98 * X | | e Y
99 * X * Y == (0)----->-(1)->-----(2)-----(3)
100 */
101
102 /*
103 * State 0: match 'X'
104 */
105 if (_strnicmp(lpFileName, lpX, cchX) != 0)
106 return FALSE;
107
108 /*
109 * State 1: match 'S' or 'e'
110 *
111 * We use 'e' to transition to state 2
112 */
113
118 if (cchY != 0)
119 {
120 /* TODO: case insensitive character search */
121 lpMatch = strchr(&lpFileName[cchX], *lpY);
122
123 if (!lpMatch)
124 return FALSE;
125
126 if (_strnicmp(lpMatch, lpY, cchY) != 0)
127 return FALSE;
128 }
129 else
130 {
131 lpMatch = &lpFileName[cchFileName];
132 }
133
137 *ppMatchEnd = &lpMatch[cchY];
138 return TRUE;
139 }
140 else if (*lpWildcard == '?')
141 {
147 /*
148 * State 0: match 'X'
149 *
150 * '?' consumes exactly one character at position cchX, so the file
151 * name must hold at least cchX + 1 characters. Without this the
152 * cchY != 0 branch below reads &lpFileName[cchX + 1], one past the
153 * terminator, when cchFileName == cchX.
154 */
155 if (cchFileName < cchX + 1)
156 return FALSE;
157
158 if (_strnicmp(lpFileName, lpX, cchX) != 0)
159 return FALSE;
160
161 /*
162 * State 1: match 'S'
163 */
164
169 if (cchY != 0)
170 {
171 /* TODO: case insensitive character search */
172 lpMatch = strchr(&lpFileName[cchX + 1], *lpY);
173
174 if (!lpMatch)
175 return FALSE;
176
177 if (_strnicmp(lpMatch, lpY, cchY) != 0)
178 return FALSE;
179 }
180 else
181 {
182 lpMatch = &lpFileName[cchX + 1];
183 }
184
188 *ppMatchEnd = &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
200BOOL FilePatternMatchA(LPCSTR lpFileName, LPCSTR lpPattern)
201{
202 BOOL match = 0;
203 LPCSTR lpTail = nullptr;
204 size_t cchTail = 0;
205 DWORD dwFlags = 0;
206 DWORD dwNextFlags = 0;
207
222 if (!lpPattern)
223 return FALSE;
224
225 if (!lpFileName)
226 return FALSE;
227
228 const size_t cchPattern = strlen(lpPattern);
229 const size_t cchFileName = strlen(lpFileName);
230
238 if ((lpPattern[0] == '*') && (cchPattern == 1))
239 return TRUE;
240
251 if (lpPattern[0] == '*')
252 {
253 lpTail = &lpPattern[1];
254 cchTail = strlen(lpTail);
255
256 if (!FilePatternFindNextWildcardA(lpTail, &dwFlags))
257 {
258 /* tail contains no wildcards */
259 if (cchFileName < cchTail)
260 return FALSE;
261
262 if (_stricmp(&lpFileName[cchFileName - cchTail], lpTail) == 0)
263 return TRUE;
264
265 return FALSE;
266 }
267 }
268
302 LPCSTR lpWildcard = FilePatternFindNextWildcardA(lpPattern, &dwFlags);
303
304 if (lpWildcard)
305 {
306 LPCSTR lpMatchEnd = nullptr;
307 size_t cchNextWildcard = 0;
308 const size_t cchSubPattern = cchPattern;
309 LPCSTR lpSubPattern = lpPattern;
310 size_t cchSubFileName = cchFileName;
311 LPCSTR lpSubFileName = lpFileName;
312 size_t cchWildcard = ((dwFlags & WILDCARD_DOS) ? 2 : 1);
313 LPCSTR lpNextWildcard =
314 FilePatternFindNextWildcardA(&lpWildcard[cchWildcard], &dwNextFlags);
315
316 if (!lpNextWildcard)
317 {
318 LPCSTR lpX = lpSubPattern;
319
320 if (lpWildcard < lpSubPattern)
321 return FALSE;
322 const size_t cchX = WINPR_ASSERTING_INT_CAST(size_t, (lpWildcard - lpSubPattern));
323 LPCSTR lpY = &lpSubPattern[cchX + cchWildcard];
324
325 if (lpY < lpSubPattern)
326 return FALSE;
327 const size_t lpYSSubPattern = WINPR_ASSERTING_INT_CAST(size_t, (lpY - lpSubPattern));
328
329 if (cchSubPattern < lpYSSubPattern)
330 return FALSE;
331 const size_t cchY = cchSubPattern - lpYSSubPattern;
332
333 return FilePatternMatchSubExpressionA(lpSubFileName, cchSubFileName, lpX, cchX, lpY,
334 cchY, lpWildcard, &lpMatchEnd);
335 }
336 else
337 {
338 while (lpNextWildcard)
339 {
340 if (lpSubFileName < lpFileName)
341 return FALSE;
342
343 cchSubFileName =
344 cchFileName - WINPR_ASSERTING_INT_CAST(size_t, (lpSubFileName - lpFileName));
345 cchNextWildcard = ((dwNextFlags & WILDCARD_DOS) ? 2 : 1);
346 LPCSTR lpX = lpSubPattern;
347
348 if (lpWildcard < lpSubPattern)
349 return FALSE;
350 const size_t cchX = WINPR_ASSERTING_INT_CAST(size_t, (lpWildcard - lpSubPattern));
351 LPCSTR lpY = &lpSubPattern[cchX + cchWildcard];
352
353 if (lpNextWildcard < lpWildcard)
354 return FALSE;
355
356 const size_t diff = WINPR_ASSERTING_INT_CAST(size_t, (lpNextWildcard - lpWildcard));
357 if (diff < cchWildcard)
358 return FALSE;
359
360 const size_t cchY = diff - cchWildcard;
361 match = FilePatternMatchSubExpressionA(lpSubFileName, cchSubFileName, lpX, cchX,
362 lpY, cchY, lpWildcard, &lpMatchEnd);
363
364 if (!match)
365 return FALSE;
366
367 lpSubFileName = lpMatchEnd;
368 cchWildcard = cchNextWildcard;
369 lpWildcard = lpNextWildcard;
370 dwFlags = dwNextFlags;
371 lpNextWildcard =
372 FilePatternFindNextWildcardA(&lpWildcard[cchWildcard], &dwNextFlags);
373 }
374
375 return TRUE;
376 }
377 }
378 else
379 {
380 /* no wildcard characters */
381 if (_stricmp(lpFileName, lpPattern) == 0)
382 return TRUE;
383 }
384
385 return FALSE;
386}