freerdp/winpr/libwinpr/file/pattern.c

374 lines
8.8 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <winpr/crt.h>
#include <winpr/handle.h>
#include <winpr/file.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef 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;
*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,
LPSTR* ppMatchEnd)
{
LPSTR lpMatch;
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 = (LPSTR)&lpFileName[cchFileName];
}
/**
* State 3: final state
*/
*ppMatchEnd = (LPSTR)&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 = (LPSTR)&lpFileName[cchX + 1];
}
/**
* State 3: final state
*/
*ppMatchEnd = (LPSTR)&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;
LPSTR lpTail;
size_t cchTail;
size_t cchPattern;
size_t cchFileName;
DWORD dwFlags;
DWORD dwNextFlags;
LPSTR lpWildcard;
LPSTR lpNextWildcard;
/**
* 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 = (LPSTR)&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)
{
LPSTR lpX;
LPSTR lpY;
size_t cchX;
size_t cchY;
LPSTR lpMatchEnd = NULL;
LPSTR lpSubPattern;
size_t cchSubPattern;
LPSTR lpSubFileName;
size_t cchSubFileName;
size_t cchWildcard;
size_t cchNextWildcard;
cchSubPattern = cchPattern;
lpSubPattern = (LPSTR)lpPattern;
cchSubFileName = cchFileName;
lpSubFileName = (LPSTR)lpFileName;
cchWildcard = ((dwFlags & WILDCARD_DOS) ? 2 : 1);
lpNextWildcard = FilePatternFindNextWildcardA(&lpWildcard[cchWildcard], &dwNextFlags);
if (!lpNextWildcard)
{
lpX = (LPSTR)lpSubPattern;
cchX = (lpWildcard - lpSubPattern);
lpY = (LPSTR)&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 = (LPSTR)lpSubPattern;
cchX = (lpWildcard - lpSubPattern);
lpY = (LPSTR)&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;
}