Blame winpr/libwinpr/file/pattern.c

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