summaryrefslogtreecommitdiffstats
path: root/winpr/libwinpr/file/pattern.c
diff options
context:
space:
mode:
Diffstat (limited to 'winpr/libwinpr/file/pattern.c')
-rw-r--r--winpr/libwinpr/file/pattern.c371
1 files changed, 371 insertions, 0 deletions
diff --git a/winpr/libwinpr/file/pattern.c b/winpr/libwinpr/file/pattern.c
new file mode 100644
index 0000000..33adf9f
--- /dev/null
+++ b/winpr/libwinpr/file/pattern.c
@@ -0,0 +1,371 @@
+/**
+ * WinPR: Windows Portable Runtime
+ * File Functions
+ *
+ * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <winpr/config.h>
+
+#include <winpr/crt.h>
+#include <winpr/handle.h>
+
+#include <winpr/file.h>
+
+#ifdef WINPR_HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef WINPR_HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#include "../log.h"
+#define TAG WINPR_TAG("file")
+
+/**
+ * File System Behavior in the Microsoft Windows Environment:
+ * http://download.microsoft.com/download/4/3/8/43889780-8d45-4b2e-9d3a-c696a890309f/File%20System%20Behavior%20Overview.pdf
+ */
+
+LPSTR FilePatternFindNextWildcardA(LPCSTR lpPattern, DWORD* pFlags)
+{
+ LPSTR lpWildcard = NULL;
+ *pFlags = 0;
+ lpWildcard = strpbrk(lpPattern, "*?~");
+
+ if (lpWildcard)
+ {
+ if (*lpWildcard == '*')
+ {
+ *pFlags = WILDCARD_STAR;
+ return lpWildcard;
+ }
+ else if (*lpWildcard == '?')
+ {
+ *pFlags = WILDCARD_QM;
+ return lpWildcard;
+ }
+ else if (*lpWildcard == '~')
+ {
+ if (lpWildcard[1] == '*')
+ {
+ *pFlags = WILDCARD_DOS_STAR;
+ return lpWildcard;
+ }
+ else if (lpWildcard[1] == '?')
+ {
+ *pFlags = WILDCARD_DOS_QM;
+ return lpWildcard;
+ }
+ else if (lpWildcard[1] == '.')
+ {
+ *pFlags = WILDCARD_DOS_DOT;
+ return lpWildcard;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+static BOOL FilePatternMatchSubExpressionA(LPCSTR lpFileName, size_t cchFileName, LPCSTR lpX,
+ size_t cchX, LPCSTR lpY, size_t cchY, LPCSTR lpWildcard,
+ LPCSTR* ppMatchEnd)
+{
+ LPCSTR lpMatch = NULL;
+
+ if (!lpFileName)
+ return FALSE;
+
+ if (*lpWildcard == '*')
+ {
+ /*
+ * S
+ * <-----<
+ * X | | e Y
+ * X * Y == (0)----->-(1)->-----(2)-----(3)
+ */
+
+ /*
+ * State 0: match 'X'
+ */
+ if (_strnicmp(lpFileName, lpX, cchX) != 0)
+ return FALSE;
+
+ /*
+ * State 1: match 'S' or 'e'
+ *
+ * We use 'e' to transition to state 2
+ */
+
+ /**
+ * State 2: match Y
+ */
+
+ if (cchY != 0)
+ {
+ /* TODO: case insensitive character search */
+ lpMatch = strchr(&lpFileName[cchX], *lpY);
+
+ if (!lpMatch)
+ return FALSE;
+
+ if (_strnicmp(lpMatch, lpY, cchY) != 0)
+ return FALSE;
+ }
+ else
+ {
+ lpMatch = &lpFileName[cchFileName];
+ }
+
+ /**
+ * State 3: final state
+ */
+ *ppMatchEnd = &lpMatch[cchY];
+ return TRUE;
+ }
+ else if (*lpWildcard == '?')
+ {
+ /**
+ * X S Y
+ * X ? Y == (0)---(1)---(2)---(3)
+ */
+
+ /*
+ * State 0: match 'X'
+ */
+ if (cchFileName < cchX)
+ return FALSE;
+
+ if (_strnicmp(lpFileName, lpX, cchX) != 0)
+ return FALSE;
+
+ /*
+ * State 1: match 'S'
+ */
+
+ /**
+ * State 2: match Y
+ */
+
+ if (cchY != 0)
+ {
+ /* TODO: case insensitive character search */
+ lpMatch = strchr(&lpFileName[cchX + 1], *lpY);
+
+ if (!lpMatch)
+ return FALSE;
+
+ if (_strnicmp(lpMatch, lpY, cchY) != 0)
+ return FALSE;
+ }
+ else
+ {
+ if ((cchX + 1) > cchFileName)
+ return FALSE;
+
+ lpMatch = &lpFileName[cchX + 1];
+ }
+
+ /**
+ * State 3: final state
+ */
+ *ppMatchEnd = &lpMatch[cchY];
+ return TRUE;
+ }
+ else if (*lpWildcard == '~')
+ {
+ WLog_ERR(TAG, "warning: unimplemented '~' pattern match");
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+BOOL FilePatternMatchA(LPCSTR lpFileName, LPCSTR lpPattern)
+{
+ BOOL match = 0;
+ LPCSTR lpTail = NULL;
+ size_t cchTail = 0;
+ size_t cchPattern = 0;
+ size_t cchFileName = 0;
+ DWORD dwFlags = 0;
+ DWORD dwNextFlags = 0;
+ LPSTR lpWildcard = NULL;
+ LPSTR lpNextWildcard = NULL;
+
+ /**
+ * Wild Card Matching
+ *
+ * '*' matches 0 or more characters
+ * '?' matches exactly one character
+ *
+ * '~*' DOS_STAR - matches 0 or more characters until encountering and matching final '.'
+ *
+ * '~?' DOS_QM - matches any single character, or upon encountering a period or end of name
+ * string, advances the expresssion to the end of the set of contiguous DOS_QMs.
+ *
+ * '~.' DOS_DOT - matches either a '.' or zero characters beyond name string.
+ */
+
+ if (!lpPattern)
+ return FALSE;
+
+ if (!lpFileName)
+ return FALSE;
+
+ cchPattern = strlen(lpPattern);
+ cchFileName = strlen(lpFileName);
+
+ /**
+ * First and foremost the file system starts off name matching with the expression “*”.
+ * If the expression contains a single wild card character ‘*’ all matches are satisfied
+ * immediately. This is the most common wild card character used in Windows and expression
+ * evaluation is optimized by looking for this character first.
+ */
+
+ if ((lpPattern[0] == '*') && (cchPattern == 1))
+ return TRUE;
+
+ /**
+ * Subsequently evaluation of the “*X” expression is performed. This is a case where
+ * the expression starts off with a wild card character and contains some non-wild card
+ * characters towards the tail end of the name. This is evaluated by making sure the
+ * expression starts off with the character ‘*’ and does not contain any wildcards in
+ * the latter part of the expression. The tail part of the expression beyond the first
+ * character ‘*’ is matched against the file name at the end uppercasing each character
+ * if necessary during the comparison.
+ */
+
+ if (lpPattern[0] == '*')
+ {
+ lpTail = &lpPattern[1];
+ cchTail = strlen(lpTail);
+
+ if (!FilePatternFindNextWildcardA(lpTail, &dwFlags))
+ {
+ /* tail contains no wildcards */
+ if (cchFileName < cchTail)
+ return FALSE;
+
+ if (_stricmp(&lpFileName[cchFileName - cchTail], lpTail) == 0)
+ return TRUE;
+
+ return FALSE;
+ }
+ }
+
+ /**
+ * The remaining expressions are evaluated in a non deterministic
+ * finite order as listed below, where:
+ *
+ * 'S' is any single character
+ * 'S-.' is any single character except the final '.'
+ * 'e' is a null character transition
+ * 'EOF' is the end of the name string
+ *
+ * S
+ * <-----<
+ * X | | e Y
+ * X * Y == (0)----->-(1)->-----(2)-----(3)
+ *
+ *
+ * S-.
+ * <-----<
+ * X | | e Y
+ * X ~* Y == (0)----->-(1)->-----(2)-----(3)
+ *
+ *
+ * X S S Y
+ * X ?? Y == (0)---(1)---(2)---(3)---(4)
+ *
+ *
+ * X S-. S-. Y
+ * X ~?~? == (0)---(1)-----(2)-----(3)---(4)
+ * | |_______|
+ * | ^ |
+ * |_______________|
+ * ^EOF of .^
+ *
+ */
+ lpWildcard = FilePatternFindNextWildcardA(lpPattern, &dwFlags);
+
+ if (lpWildcard)
+ {
+ LPCSTR lpX = NULL;
+ LPCSTR lpY = NULL;
+ size_t cchX = 0;
+ size_t cchY = 0;
+ LPCSTR lpMatchEnd = NULL;
+ LPCSTR lpSubPattern = NULL;
+ size_t cchSubPattern = 0;
+ LPCSTR lpSubFileName = NULL;
+ size_t cchSubFileName = 0;
+ size_t cchWildcard = 0;
+ size_t cchNextWildcard = 0;
+ cchSubPattern = cchPattern;
+ lpSubPattern = lpPattern;
+ cchSubFileName = cchFileName;
+ lpSubFileName = lpFileName;
+ cchWildcard = ((dwFlags & WILDCARD_DOS) ? 2 : 1);
+ lpNextWildcard = FilePatternFindNextWildcardA(&lpWildcard[cchWildcard], &dwNextFlags);
+
+ if (!lpNextWildcard)
+ {
+ lpX = lpSubPattern;
+ cchX = (lpWildcard - lpSubPattern);
+ lpY = &lpSubPattern[cchX + cchWildcard];
+ cchY = (cchSubPattern - (lpY - lpSubPattern));
+ match = FilePatternMatchSubExpressionA(lpSubFileName, cchSubFileName, lpX, cchX, lpY,
+ cchY, lpWildcard, &lpMatchEnd);
+ return match;
+ }
+ else
+ {
+ while (lpNextWildcard)
+ {
+ cchSubFileName = cchFileName - (lpSubFileName - lpFileName);
+ cchNextWildcard = ((dwNextFlags & WILDCARD_DOS) ? 2 : 1);
+ lpX = lpSubPattern;
+ cchX = (lpWildcard - lpSubPattern);
+ lpY = &lpSubPattern[cchX + cchWildcard];
+ cchY = (lpNextWildcard - lpWildcard) - cchWildcard;
+ match = FilePatternMatchSubExpressionA(lpSubFileName, cchSubFileName, lpX, cchX,
+ lpY, cchY, lpWildcard, &lpMatchEnd);
+
+ if (!match)
+ return FALSE;
+
+ lpSubFileName = lpMatchEnd;
+ cchWildcard = cchNextWildcard;
+ lpWildcard = lpNextWildcard;
+ dwFlags = dwNextFlags;
+ lpNextWildcard =
+ FilePatternFindNextWildcardA(&lpWildcard[cchWildcard], &dwNextFlags);
+ }
+
+ return TRUE;
+ }
+ }
+ else
+ {
+ /* no wildcard characters */
+ if (_stricmp(lpFileName, lpPattern) == 0)
+ return TRUE;
+ }
+
+ return FALSE;
+}