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
254 if (lpPattern[0] == '*')
255 {
256 lpTail = &lpPattern[1];
257 cchTail = strlen(lpTail);
258
259 if (!FilePatternFindNextWildcardA(lpTail, &dwFlags))
260 {
261 /* tail contains no wildcards */
262 if (cchFileName < cchTail)
263 return FALSE;
264
265 if (_stricmp(&lpFileName[cchFileName - cchTail], lpTail) == 0)
266 return TRUE;
267
268 return FALSE;
269 }
270 }
271
305 LPCSTR lpWildcard = FilePatternFindNextWildcardA(lpPattern, &dwFlags);
306
307 if (lpWildcard)
308 {
309 LPCSTR lpMatchEnd = nullptr;
310 size_t cchNextWildcard = 0;
311 const size_t cchSubPattern = cchPattern;
312 LPCSTR lpSubPattern = lpPattern;
313 size_t cchSubFileName = cchFileName;
314 LPCSTR lpSubFileName = lpFileName;
315 size_t cchWildcard = ((dwFlags & WILDCARD_DOS) ? 2 : 1);
316 LPCSTR lpNextWildcard =
317 FilePatternFindNextWildcardA(&lpWildcard[cchWildcard], &dwNextFlags);
318
319 if (!lpNextWildcard)
320 {
321 LPCSTR lpX = lpSubPattern;
322
323 if (lpWildcard < lpSubPattern)
324 return FALSE;
325 const size_t cchX = WINPR_ASSERTING_INT_CAST(size_t, (lpWildcard - lpSubPattern));
326 LPCSTR lpY = &lpSubPattern[cchX + cchWildcard];
327
328 if (lpY < lpSubPattern)
329 return FALSE;
330 const size_t lpYSSubPattern = WINPR_ASSERTING_INT_CAST(size_t, (lpY - lpSubPattern));
331
332 if (cchSubPattern < lpYSSubPattern)
333 return FALSE;
334 const size_t cchY = cchSubPattern - lpYSSubPattern;
335
336 return FilePatternMatchSubExpressionA(lpSubFileName, cchSubFileName, lpX, cchX, lpY,
337 cchY, lpWildcard, &lpMatchEnd);
338 }
339 else
340 {
341 while (lpNextWildcard)
342 {
343 if (lpSubFileName < lpFileName)
344 return FALSE;
345
346 cchSubFileName =
347 cchFileName - WINPR_ASSERTING_INT_CAST(size_t, (lpSubFileName - lpFileName));
348 cchNextWildcard = ((dwNextFlags & WILDCARD_DOS) ? 2 : 1);
349 LPCSTR lpX = lpSubPattern;
350
351 if (lpWildcard < lpSubPattern)
352 return FALSE;
353 const size_t cchX = WINPR_ASSERTING_INT_CAST(size_t, (lpWildcard - lpSubPattern));
354 LPCSTR lpY = &lpSubPattern[cchX + cchWildcard];
355
356 if (lpNextWildcard < lpWildcard)
357 return FALSE;
358
359 const size_t diff = WINPR_ASSERTING_INT_CAST(size_t, (lpNextWildcard - lpWildcard));
360 if (diff < cchWildcard)
361 return FALSE;
362
363 const size_t cchY = diff - cchWildcard;
364 match = FilePatternMatchSubExpressionA(lpSubFileName, cchSubFileName, lpX, cchX,
365 lpY, cchY, lpWildcard, &lpMatchEnd);
366
367 if (!match)
368 return FALSE;
369
370 lpSubFileName = lpMatchEnd;
371 cchWildcard = cchNextWildcard;
372 lpWildcard = lpNextWildcard;
373 dwFlags = dwNextFlags;
374 lpNextWildcard =
375 FilePatternFindNextWildcardA(&lpWildcard[cchWildcard], &dwNextFlags);
376 }
377
378 return TRUE;
379 }
380 }
381 else
382 {
383 /* no wildcard characters */
384 if (_stricmp(lpFileName, lpPattern) == 0)
385 return TRUE;
386 }
387
388 return FALSE;
389}