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 WINPR_ASSERT(lpPattern);
46 WINPR_ASSERT(pFlags);
47
48 *pFlags = 0;
49
50 char* lpWildcard = strpbrk(lpPattern, "*?~");
51
52 if (lpWildcard)
53 {
54 if (*lpWildcard == '*')
55 {
56 *pFlags = WILDCARD_STAR;
57 return lpWildcard;
58 }
59 else if (*lpWildcard == '?')
60 {
61 *pFlags = WILDCARD_QM;
62 return lpWildcard;
63 }
64 else if (*lpWildcard == '~')
65 {
66 if (lpWildcard[1] == '*')
67 {
68 *pFlags = WILDCARD_DOS_STAR;
69 return lpWildcard;
70 }
71 else if (lpWildcard[1] == '?')
72 {
73 *pFlags = WILDCARD_DOS_QM;
74 return lpWildcard;
75 }
76 else if (lpWildcard[1] == '.')
77 {
78 *pFlags = WILDCARD_DOS_DOT;
79 return lpWildcard;
80 }
81 }
82 }
83
84 return nullptr;
85}
86
87static BOOL FilePatternMatchSubExpressionA(LPCSTR lpFileName, size_t cchFileName, LPCSTR lpX,
88 size_t cchX, LPCSTR lpY, size_t cchY, LPCSTR lpWildcard,
89 LPCSTR* ppMatchEnd)
90{
91 LPCSTR lpMatch = nullptr;
92
93 if (!lpFileName)
94 return FALSE;
95
96 if (*lpWildcard == '*')
97 {
98 /*
99 * S
100 * <-----<
101 * X | | e Y
102 * X * Y == (0)----->-(1)->-----(2)-----(3)
103 */
104
105 /*
106 * State 0: match 'X'
107 */
108 if (_strnicmp(lpFileName, lpX, cchX) != 0)
109 return FALSE;
110
111 /*
112 * State 1: match 'S' or 'e'
113 *
114 * We use 'e' to transition to state 2
115 */
116
121 if (cchY != 0)
122 {
123 /* TODO: case insensitive character search */
124 lpMatch = strchr(&lpFileName[cchX], *lpY);
125
126 if (!lpMatch)
127 return FALSE;
128
129 if (_strnicmp(lpMatch, lpY, cchY) != 0)
130 return FALSE;
131 }
132 else
133 {
134 lpMatch = &lpFileName[cchFileName];
135 }
136
140 *ppMatchEnd = &lpMatch[cchY];
141 return TRUE;
142 }
143 else if (*lpWildcard == '?')
144 {
150 /*
151 * State 0: match 'X'
152 *
153 * '?' consumes exactly one character at position cchX, so the file
154 * name must hold at least cchX + 1 characters. Without this the
155 * cchY != 0 branch below reads &lpFileName[cchX + 1], one past the
156 * terminator, when cchFileName == cchX.
157 */
158 if (cchFileName < cchX + 1)
159 return FALSE;
160
161 if (_strnicmp(lpFileName, lpX, cchX) != 0)
162 return FALSE;
163
164 /*
165 * State 1: match 'S'
166 */
167
172 if (cchY != 0)
173 {
174 /* TODO: case insensitive character search */
175 lpMatch = strchr(&lpFileName[cchX + 1], *lpY);
176
177 if (!lpMatch)
178 return FALSE;
179
180 if (_strnicmp(lpMatch, lpY, cchY) != 0)
181 return FALSE;
182 }
183 else
184 {
185 lpMatch = &lpFileName[cchX + 1];
186 }
187
191 *ppMatchEnd = &lpMatch[cchY];
192 return TRUE;
193 }
194 else if (*lpWildcard == '~')
195 {
196 WLog_ERR(TAG, "warning: unimplemented '~' pattern match");
197 return TRUE;
198 }
199
200 return FALSE;
201}
202
203BOOL FilePatternMatchA(LPCSTR lpFileName, LPCSTR lpPattern)
204{
205 BOOL match = 0;
206 LPCSTR lpTail = nullptr;
207 size_t cchTail = 0;
208 DWORD dwFlags = 0;
209 DWORD dwNextFlags = 0;
210
225 if (!lpPattern)
226 return FALSE;
227
228 if (!lpFileName)
229 return FALSE;
230
231 const size_t cchPattern = strlen(lpPattern);
232 const size_t cchFileName = strlen(lpFileName);
233
241 if ((lpPattern[0] == '*') && (cchPattern == 1))
242 return TRUE;
243
249 if ((cchPattern == 3) && (strcmp(lpPattern, "*.*") == 0))
250 return TRUE;
251
262 if (lpPattern[0] == '*')
263 {
264 lpTail = &lpPattern[1];
265 cchTail = strlen(lpTail);
266
267 if (!FilePatternFindNextWildcardA(lpTail, &dwFlags))
268 {
269 /* tail contains no wildcards */
270 if (cchFileName < cchTail)
271 return FALSE;
272
273 if (_stricmp(&lpFileName[cchFileName - cchTail], lpTail) == 0)
274 return TRUE;
275
276 return FALSE;
277 }
278 }
279
313 LPCSTR lpWildcard = FilePatternFindNextWildcardA(lpPattern, &dwFlags);
314
315 if (lpWildcard)
316 {
317 LPCSTR lpMatchEnd = nullptr;
318 size_t cchNextWildcard = 0;
319 const size_t cchSubPattern = cchPattern;
320 LPCSTR lpSubPattern = lpPattern;
321 size_t cchSubFileName = cchFileName;
322 LPCSTR lpSubFileName = lpFileName;
323 size_t cchWildcard = ((dwFlags & WILDCARD_DOS) ? 2 : 1);
324 LPCSTR lpNextWildcard =
325 FilePatternFindNextWildcardA(&lpWildcard[cchWildcard], &dwNextFlags);
326
327 if (!lpNextWildcard)
328 {
329 LPCSTR lpX = lpSubPattern;
330
331 if (lpWildcard < lpSubPattern)
332 return FALSE;
333 const size_t cchX = WINPR_ASSERTING_INT_CAST(size_t, (lpWildcard - lpSubPattern));
334 LPCSTR lpY = &lpSubPattern[cchX + cchWildcard];
335
336 if (lpY < lpSubPattern)
337 return FALSE;
338 const size_t lpYSSubPattern = WINPR_ASSERTING_INT_CAST(size_t, (lpY - lpSubPattern));
339
340 if (cchSubPattern < lpYSSubPattern)
341 return FALSE;
342 const size_t cchY = cchSubPattern - lpYSSubPattern;
343
344 return FilePatternMatchSubExpressionA(lpSubFileName, cchSubFileName, lpX, cchX, lpY,
345 cchY, lpWildcard, &lpMatchEnd);
346 }
347 else
348 {
349 while (lpNextWildcard)
350 {
351 if (lpSubFileName < lpFileName)
352 return FALSE;
353
354 cchSubFileName =
355 cchFileName - WINPR_ASSERTING_INT_CAST(size_t, (lpSubFileName - lpFileName));
356 cchNextWildcard = ((dwNextFlags & WILDCARD_DOS) ? 2 : 1);
357 LPCSTR lpX = lpSubPattern;
358
359 if (lpWildcard < lpSubPattern)
360 return FALSE;
361 const size_t cchX = WINPR_ASSERTING_INT_CAST(size_t, (lpWildcard - lpSubPattern));
362 LPCSTR lpY = &lpSubPattern[cchX + cchWildcard];
363
364 if (lpNextWildcard < lpWildcard)
365 return FALSE;
366
367 const size_t diff = WINPR_ASSERTING_INT_CAST(size_t, (lpNextWildcard - lpWildcard));
368 if (diff < cchWildcard)
369 return FALSE;
370
371 const size_t cchY = diff - cchWildcard;
372 match = FilePatternMatchSubExpressionA(lpSubFileName, cchSubFileName, lpX, cchX,
373 lpY, cchY, lpWildcard, &lpMatchEnd);
374
375 if (!match)
376 return FALSE;
377
378 lpSubFileName = lpMatchEnd;
379 cchWildcard = cchNextWildcard;
380 lpWildcard = lpNextWildcard;
381 dwFlags = dwNextFlags;
382 lpNextWildcard =
383 FilePatternFindNextWildcardA(&lpWildcard[cchWildcard], &dwNextFlags);
384 }
385
386 return TRUE;
387 }
388 }
389 else
390 {
391 /* no wildcard characters */
392 if (_stricmp(lpFileName, lpPattern) == 0)
393 return TRUE;
394 }
395
396 return FALSE;
397}