943 lines
28 KiB
C++
943 lines
28 KiB
C++
/*++
|
||
|
||
Copyright (c) 1995 Microsoft Corporation
|
||
|
||
Module Name :
|
||
|
||
getdir.cxx
|
||
|
||
Abstract:
|
||
This module implements the member functions for TS_DIRECTORY_INFO
|
||
|
||
Author:
|
||
|
||
Murali R. Krishnan ( MuraliK ) 16-Jan-1995
|
||
|
||
Project:
|
||
|
||
Tsunami Lib
|
||
( Common caching and directory functions for Internet Services)
|
||
|
||
Functions Exported:
|
||
|
||
TS_DIRECTORY_INFO::CleanupThis()
|
||
|
||
TS_DIRECTORY_INFO::GetDirectoryListingA()
|
||
IN LPCSTR pszDirectoryName,
|
||
IN HANDLE hListingUser)
|
||
|
||
TS_DIRECTORY_INFO::SortFileInfoPointers(
|
||
IN PFN_CMP_FILE_BOTH_DIR_INFO pfnCompare)
|
||
|
||
TS_DIRECTORY_INFO::FilterFiles(
|
||
IN PFN_IS_MATCH_FILE_BOTH_DIR_INFO pfnMatch,
|
||
IN LPVOID pContext);
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
|
||
/************************************************************
|
||
* Include Headers
|
||
************************************************************/
|
||
|
||
# include "tsunamip.hxx"
|
||
# include "dbgutil.h"
|
||
# include <string.h>
|
||
|
||
/************************************************************
|
||
* Type Definitions
|
||
************************************************************/
|
||
|
||
|
||
dllexp
|
||
VOID
|
||
TS_DIRECTORY_INFO::CleanupThis( VOID)
|
||
{
|
||
IF_DEBUG( DIR_LIST) {
|
||
|
||
DBGPRINTF( ( DBG_CONTEXT,
|
||
"Cleaning up TS_DIRECTORY_INFO ( %08x)\n",
|
||
this));
|
||
Print();
|
||
}
|
||
|
||
if ( m_fValid) {
|
||
|
||
if ( m_pTsDirectoryHeader != NULL) {
|
||
|
||
TsFreeDirectoryListing( m_tsCache, m_pTsDirectoryHeader);
|
||
m_pTsDirectoryHeader = NULL;
|
||
}
|
||
|
||
ASSERT( m_pTsDirectoryHeader == NULL);
|
||
|
||
if ( m_prgFileInfo != NULL) {
|
||
FREE( m_prgFileInfo);
|
||
m_prgFileInfo = NULL;
|
||
}
|
||
}
|
||
|
||
ASSERT( m_pTsDirectoryHeader == NULL);
|
||
ASSERT( m_prgFileInfo == NULL);
|
||
m_fValid = 0;
|
||
m_cFilesInDirectory = 0;
|
||
|
||
} // TS_DIRECTORY_INFO::CleanupThis()
|
||
|
||
|
||
|
||
|
||
static BOOL
|
||
MakeCopyOfFileInfoPointers(
|
||
IN OUT PFILE_BOTH_DIR_INFORMATION ** pppFileInfoTo,
|
||
IN const PFILE_BOTH_DIR_INFORMATION * ppFileInfoFrom,
|
||
IN int nEntries)
|
||
/*++
|
||
Allocates memory and makes a copy of the file info pointers in the array
|
||
in ppFileInfoFrom.
|
||
|
||
Returns:
|
||
TRUE if success and FALSE if there is any failure.
|
||
--*/
|
||
{
|
||
DWORD cbCopy;
|
||
|
||
ASSERT( *pppFileInfoTo == NULL);
|
||
|
||
cbCopy = nEntries * sizeof( PFILE_BOTH_DIR_INFORMATION);
|
||
|
||
*pppFileInfoTo = (PFILE_BOTH_DIR_INFORMATION *) ALLOC( cbCopy);
|
||
|
||
if ( *pppFileInfoTo != NULL) {
|
||
|
||
memcpy( (PVOID ) *pppFileInfoTo,
|
||
(const PVOID ) ppFileInfoFrom,
|
||
cbCopy);
|
||
|
||
} else {
|
||
SetLastError( ERROR_NOT_ENOUGH_MEMORY);
|
||
}
|
||
|
||
return ( *pppFileInfoTo != NULL);
|
||
} // MakeCopyOfFileInfoPointers()
|
||
|
||
|
||
|
||
dllexp
|
||
BOOL
|
||
TS_DIRECTORY_INFO::GetDirectoryListingA(
|
||
IN LPCSTR pszDirectoryName,
|
||
IN HANDLE hListingUser)
|
||
{
|
||
if ( m_pTsDirectoryHeader == NULL) {
|
||
|
||
//
|
||
// Only if already a directory listing is not obtained.
|
||
// we obtain newly
|
||
//
|
||
|
||
IF_DEBUG( DIR_LIST) {
|
||
|
||
DBGPRINTF( ( DBG_CONTEXT,
|
||
"Obtaining Dir Listing for %s. UserHandle=%08x.\n",
|
||
pszDirectoryName, hListingUser));
|
||
}
|
||
|
||
m_fValid = TsGetDirectoryListingA( m_tsCache,
|
||
pszDirectoryName,
|
||
hListingUser,
|
||
&m_pTsDirectoryHeader);
|
||
|
||
m_fValid = m_fValid &&
|
||
MakeCopyOfFileInfoPointers(
|
||
&m_prgFileInfo,
|
||
m_pTsDirectoryHeader->QueryArrayOfFileInfoPointers(),
|
||
m_pTsDirectoryHeader->QueryNumEntries());
|
||
|
||
m_cFilesInDirectory = ( m_pTsDirectoryHeader == NULL) ? 0 :
|
||
m_pTsDirectoryHeader->QueryNumEntries();
|
||
}
|
||
|
||
return ( m_fValid);
|
||
} // TS_DIRECTORY_INFO::GetDirectoryListingA()
|
||
|
||
|
||
# ifdef UNICODE
|
||
|
||
dllexp
|
||
BOOL
|
||
TS_DIRECTORY_INFO::GetDirectoryListingW(
|
||
IN LPCWSTR pwszDirectoryName,
|
||
IN HANDLE hListingUser)
|
||
{
|
||
if ( m_pTsDirectoryHeader == NULL) {
|
||
|
||
//
|
||
// Only if already a directory listing is not obtained.
|
||
// we obtain newly
|
||
//
|
||
|
||
m_fValid = TsGetDirectoryListingW( m_tsCache,
|
||
pwszDirectoryName,
|
||
hListingUser,
|
||
&m_pTsDirectoryHeader);
|
||
|
||
m_fValid = m_fValid &&
|
||
MakeCopyOfFileInfoPointers(
|
||
&m_prgFileInfo,
|
||
m_pTsDirectoryHeader->QueryArrayOfFileInfoPointers(),
|
||
m_pTsDirectoryHeader->QueryNumEntries());
|
||
|
||
m_cFilesInDirectory = ( m_pTsDirectoryHeader == NULL) ? 0 :
|
||
m_pTsDirectoryHeader->QueryNumEntries();
|
||
}
|
||
|
||
return ( m_fValid);
|
||
} // TS_DIRECTORY_INFO::GetDirectoryListingW()
|
||
|
||
# endif // UNICODE
|
||
|
||
|
||
|
||
dllexp
|
||
BOOL
|
||
TS_DIRECTORY_INFO::SortFileInfoPointers(
|
||
IN PFN_CMP_FILE_BOTH_DIR_INFO pfnCompare)
|
||
{
|
||
BOOL fReturn;
|
||
|
||
if ( IsValid()) {
|
||
|
||
fReturn = SortInPlaceFileInfoPointers( m_prgFileInfo,
|
||
m_cFilesInDirectory,
|
||
pfnCompare);
|
||
}
|
||
|
||
return ( fReturn);
|
||
} // TS_DIRECTORY_INFO::SortFileInfoPointers()
|
||
|
||
|
||
|
||
|
||
dllexp
|
||
BOOL
|
||
TS_DIRECTORY_INFO::FilterFiles( IN PFN_IS_MATCH_FILE_BOTH_DIR_INFO pfnMatch,
|
||
IN LPVOID pContext)
|
||
/*++
|
||
This function filters the list of files using the pfnMatch function
|
||
and the context information specified by pContext.
|
||
This function eliminates all the pointers to FileInfo which do not
|
||
match the given file specification.
|
||
|
||
Returns:
|
||
TRUE on success and FALSE on failure.
|
||
--*/
|
||
{
|
||
BOOL fReturn = FALSE;
|
||
|
||
if ( IsValid()) {
|
||
|
||
int idxScan; // for scanning the files
|
||
int idxCur; // for updating after filter
|
||
|
||
IF_DEBUG( DIR_LIST) {
|
||
|
||
DBGPRINTF( ( DBG_CONTEXT,
|
||
"FilterFiles in DirList( %08x) for FileSpec %08x\n",
|
||
this, pContext));
|
||
}
|
||
|
||
for( idxCur = idxScan = 0;
|
||
idxScan < m_cFilesInDirectory;
|
||
idxScan++) {
|
||
|
||
PFILE_BOTH_DIR_INFORMATION pFileInfo =
|
||
GetFileInfoPointerFromIdx( idxScan);
|
||
|
||
ASSERT( pFileInfo != NULL);
|
||
ASSERT( idxCur <= idxScan);
|
||
|
||
if ( (*pfnMatch)( pFileInfo, pContext)) {
|
||
|
||
//
|
||
// this is a match. Retain this item and advance CurPtr
|
||
//
|
||
m_prgFileInfo[ idxCur++] = m_prgFileInfo[ idxScan];
|
||
}
|
||
} // for
|
||
|
||
m_cFilesInDirectory = idxCur;
|
||
fReturn = TRUE;
|
||
}
|
||
|
||
return ( fReturn);
|
||
} // TS_DIRECTORY_INFO::FilterFiles()
|
||
|
||
|
||
|
||
|
||
# if DBG
|
||
|
||
VOID
|
||
TS_DIRECTORY_INFO::Print( VOID) const
|
||
{
|
||
|
||
DBGPRINTF( ( DBG_CONTEXT,
|
||
" Printing TS_DIRECTORY_INFO ( %08x).\n", this));
|
||
DBGPRINTF( ( DBG_CONTEXT,
|
||
"NumEntries=%d\t Valid = %d\n",
|
||
m_cFilesInDirectory, m_fValid));
|
||
DBGPRINTF( ( DBG_CONTEXT,
|
||
"Directory Header ( %08x) \t ArrayOfFileInfo = %08x\n",
|
||
m_pTsDirectoryHeader, m_prgFileInfo));
|
||
|
||
for( int idx = 0; idx < m_cFilesInDirectory; idx++) {
|
||
|
||
PFILE_BOTH_DIR_INFORMATION pfi = m_prgFileInfo[idx];
|
||
|
||
DBGPRINTF( ( DBG_CONTEXT,
|
||
"rgFileInfo[%4d] = %08x. Name=%s Attr=0x%x"
|
||
"Size=0x%x:%x\n",
|
||
idx, pfi,
|
||
pfi->FileName,
|
||
pfi->FileAttributes,
|
||
pfi->EndOfFile.HighPart,
|
||
pfi->EndOfFile.LowPart
|
||
));
|
||
}
|
||
|
||
m_pTsDirectoryHeader->Print();
|
||
|
||
return;
|
||
} // TS_DIRECTORY_INFO::Print()
|
||
|
||
|
||
# endif // DBG
|
||
|
||
|
||
|
||
|
||
BOOL __cdecl
|
||
RegExpressionMatchFileInfo( IN const FILE_BOTH_DIR_INFORMATION * pFileInfo,
|
||
IN CHAR * pszExpression)
|
||
/*++
|
||
This function tries to find a match between the file name in
|
||
pFileInfo and the regular expression specified in pszExpression.
|
||
|
||
Arguments:
|
||
pFileInfo -- pointer to file information consisting under query.
|
||
pszExpression - pointer to null-terminated string containing the
|
||
regular expression, against which file name is tobe
|
||
matched for.
|
||
|
||
Returns:
|
||
TRUE on a match and false if there is any failure.
|
||
--*/
|
||
{
|
||
const CHAR * pszFileName;
|
||
|
||
DBG_ASSERT( pFileInfo != NULL);
|
||
pszFileName = (const CHAR *) pFileInfo->FileName;
|
||
|
||
if ( strpbrk( pszExpression, "?*<>") != NULL) {
|
||
|
||
// No Wild cards. Do normal file comparisons
|
||
return ( strcmp( pszFileName, pszExpression) == 0);
|
||
} else {
|
||
|
||
// do a case sensitive comparison
|
||
return IsNameInRegExpressionA( pszExpression, pszFileName, FALSE);
|
||
}
|
||
|
||
} // RegExpressionMatch()
|
||
|
||
|
||
|
||
|
||
|
||
/************************************************************
|
||
* Following code is based on the FileSystem Rtl routines
|
||
* from ntos\fsrtl\name.c
|
||
* But these are optimized for performance, in our case
|
||
* using ANSI strings!
|
||
************************************************************/
|
||
|
||
# define MAX_MATCHES_ARRAY_SIZE (16)
|
||
# define IS_EMPTY_STRING(psz) ( (psz) == NULL || *(psz) == '\0')
|
||
|
||
|
||
//
|
||
// Original code used USHORT for ULEN. However using USHORT asks
|
||
// a 32 bit processor to add "and <value>, 0xff for each instruction
|
||
// that accessed the 16 bit (USHORT) value.
|
||
// Hence, I decided to use DWORD, since the space usage is not tremendous
|
||
// during the fast path work, compared to performance benefits.
|
||
// - MuraliK (Oct 27, 1995)
|
||
//
|
||
|
||
// typedef USHORT ULEN;
|
||
typedef DWORD ULEN;
|
||
|
||
|
||
BOOL __cdecl
|
||
IsNameInRegExpressionA(
|
||
IN LPCSTR pszExpression,
|
||
IN LPCSTR pszName,
|
||
IN BOOL fIgnoreCase)
|
||
/*++
|
||
This routine compares an ANSI name and an expression and decries to the
|
||
caller if the name is in hte language defined by the expression. The input
|
||
name cannot contain wildcards, while the expression itself may contain
|
||
wildcards.
|
||
|
||
Expression wild cards are evaluated as shown in the non-deterministic finite
|
||
automatons below. Note that ~* and ~? stand for DOS_STAR and DOS_QM.
|
||
|
||
|
||
~* is DOS_STAR, ~? is DOS_QM, and ~. is DOS_DOT
|
||
|
||
|
||
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 . . Y
|
||
X ~.~. Y == (0)---(1)----(2)------(3)---(4)
|
||
| |________|
|
||
| ^ |
|
||
|_______________|
|
||
^EOF or .^
|
||
|
||
|
||
X S-. S-. Y
|
||
X ~?~? Y == (0)---(1)-----(2)-----(3)---(4)
|
||
| |________|
|
||
| ^ |
|
||
|_______________|
|
||
^EOF or .^
|
||
|
||
|
||
|
||
where S is any single character
|
||
|
||
S-. is any single character except .
|
||
|
||
e is a null character transition
|
||
|
||
EOF is the end of the name string
|
||
|
||
|
||
The last construction, ~? (the DOS question mark), can either match any
|
||
single character, or upon encountering a period or end of input string,
|
||
advances the expression to the end of the set of contiguous ~?s. This may
|
||
seem somewhat convoluted, but is what DOS needs.
|
||
|
||
Arguments:
|
||
pszExpression - Supplies the input expression to check against
|
||
( Caller must already lowercased if passing fIgnoreCase TRUE.)
|
||
|
||
pszName - supplies the input name to check for.
|
||
|
||
fIgnoreCase - if TRUE, the name should be lower-cased before comparing.
|
||
( that is done by this function, dynamically without destroying pszName)
|
||
|
||
This function is costly, if the pszExpression does not contain
|
||
any wild cards to be matched for. So Dont use it if there are
|
||
no wild cards in the pszExpression
|
||
|
||
Returns:
|
||
BOOL -- TRUE if pszName is an element in the set of strings denoted
|
||
by the input expression. FALSE if otherwise.
|
||
--*/
|
||
{
|
||
ULEN NameLen; // length in character count
|
||
ULEN ExprLen;
|
||
|
||
/*
|
||
* Algorithm:
|
||
* Keep track of all possible locations in the regular expression
|
||
* that are matching the name. If when the name has been exhausted
|
||
* one of the locations in the expression is also just exhausted, the
|
||
* name is in the language defined by the regular expression.
|
||
*/
|
||
|
||
|
||
DBG_ASSERT( pszName != NULL && *pszName != '\0');
|
||
DBG_ASSERT( pszExpression != NULL && *pszName != '\0');
|
||
|
||
//
|
||
// if one string is empty return FALSE. If both are empty TRUE.
|
||
//
|
||
|
||
if ( IS_EMPTY_STRING(pszName) || IS_EMPTY_STRING(pszExpression)) {
|
||
|
||
IF_DEBUG( DIR_LIST) {
|
||
DBGPRINTF((DBG_CONTEXT, " IsNameInRegExpr( %s, %s, %d) ==>%d\n",
|
||
pszExpression, pszName, fIgnoreCase,
|
||
!(*pszName + *pszExpression)
|
||
));
|
||
}
|
||
|
||
return (BOOL ) (!(*pszName + *pszExpression));
|
||
}
|
||
|
||
NameLen = strlen(pszName);
|
||
ExprLen = strlen(pszExpression);
|
||
|
||
//
|
||
// Special case: reduce the most common wild card search of *
|
||
//
|
||
|
||
if ( ExprLen == 1 && pszExpression[0] == '*') {
|
||
|
||
IF_DEBUG ( DIR_LIST) {
|
||
DBGPRINTF((DBG_CONTEXT, " IsNameInRegExpr( %s, %s, %d) ==>%d\n",
|
||
pszExpression, pszName, fIgnoreCase,
|
||
TRUE
|
||
));
|
||
}
|
||
|
||
// matches anything. so return TRUE
|
||
return (TRUE);
|
||
}
|
||
|
||
//
|
||
// Walk through the name string, picking off characters. We go one
|
||
// character beyond the end because some wild cards are able to match
|
||
// zero characters beyond the end of the string.
|
||
//
|
||
// With each new name character we determine a new set of states that
|
||
// match the name so far. We use two arrays that we swap back and forth
|
||
// for this purpose. One array lists the possible expression states for
|
||
// all name characters up to but not including the current one, and other
|
||
// array is used to build up the list of states considering the current
|
||
// name character as well. The arrays are then switched and the process
|
||
// repeated.
|
||
//
|
||
// There is not a one-to-one correspondence between state number and
|
||
// offset into the pszExpression. This is evident from the NFAs in the
|
||
// initial comment to this function. State numbering is not continuous.
|
||
// This allows a simple conversion between state number and expression
|
||
// offset. Each character in the Expression can represent one or two
|
||
// states. '*' and DOS_STAR generate two states: ExprOffset*2 and
|
||
// ExprOffset*2 + 1. All other expression characters can produce only
|
||
// a single state. Thus ExprOffset = State/2.
|
||
//
|
||
//
|
||
// Here is a short description of the variables involved:
|
||
//
|
||
// NameOffset -The offset of the current name char being processed.
|
||
//
|
||
// ExprOffset -The offset of the current expression char being processed.
|
||
//
|
||
// SrcCount -Prior match being investigated with current name char
|
||
//
|
||
// DestCount -Next location to put a matching assuming current name char
|
||
//
|
||
// NameFinished - Allows one more iteration through the Matches array
|
||
// after the name is exhusted (to come *s for example)
|
||
//
|
||
// PreviousDestCount - This is used to prevent entry duplication,
|
||
// see comment
|
||
//
|
||
// PreviousMatches - Holds the previous set of matches (the Src array)
|
||
//
|
||
// CurrentMatches - Holds the current set of matches (the Dest array)
|
||
//
|
||
// AuxBuffer, LocalBuffer - the storage for the Matches arrays
|
||
//
|
||
|
||
ULEN NameOffset; // offset in terms of byte count
|
||
ULEN ExprOffset; // offset in terms of byte count
|
||
|
||
ULONG SrcCount;
|
||
ULONG DestCount;
|
||
ULONG PreviousDestCount;
|
||
ULONG MatchesCount;
|
||
|
||
CHAR NameChar, ExprChar;
|
||
|
||
// for prev and current matches
|
||
ULEN *AuxBuffer = NULL;
|
||
ULEN *PreviousMatches;
|
||
ULEN *CurrentMatches;
|
||
|
||
ULEN MaxState;
|
||
ULEN CurrentState;
|
||
|
||
BOOL NameFinished;
|
||
|
||
ULEN LocalBuffer[MAX_MATCHES_ARRAY_SIZE * 2];
|
||
|
||
// set up the intial values
|
||
// Use the different portions of local buffer for matches.
|
||
|
||
PreviousMatches = &LocalBuffer[0];
|
||
CurrentMatches = &LocalBuffer[MAX_MATCHES_ARRAY_SIZE];
|
||
|
||
PreviousMatches[0] = 0;
|
||
MatchesCount = 1;
|
||
|
||
NameOffset = 0;
|
||
MaxState = (ULEN ) (ExprLen * 2);
|
||
|
||
NameFinished = FALSE;
|
||
while (!NameFinished) {
|
||
|
||
|
||
if ( NameOffset < NameLen) {
|
||
|
||
NameChar = pszName[NameOffset/sizeof(CHAR)];
|
||
NameOffset += sizeof(CHAR);
|
||
} else {
|
||
|
||
NameFinished = TRUE;
|
||
|
||
// if we already exhauseted expression, stop. Else continue
|
||
DBG_ASSERT( MatchesCount >= 1);
|
||
if ( PreviousMatches[MatchesCount - 1] == MaxState) {
|
||
|
||
break;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Now, for each of previous stored expression matches,
|
||
// see what we can do with the new name character.
|
||
//
|
||
|
||
DestCount = 0;
|
||
PreviousDestCount = 0;
|
||
|
||
for( SrcCount = 0; SrcCount < MatchesCount; ) {
|
||
|
||
ULEN Length;
|
||
|
||
//
|
||
// We have to carry on our expression analysis as far as possible
|
||
// for each character of name, so we loop here until the
|
||
// expression stops matching. A clue here is that expression
|
||
// cases that can match zero or more characters end with a
|
||
// continue, while those that can accept only a single character
|
||
// end with a break.
|
||
//
|
||
|
||
ExprOffset = (ULEN)((PreviousMatches[SrcCount++] + 1) / 2);
|
||
|
||
for( Length = 0; ExprOffset != ExprLen; ) {
|
||
|
||
//
|
||
// increment the expression offset to move to next character.
|
||
//
|
||
ExprOffset += Length;
|
||
Length = sizeof(CHAR);
|
||
|
||
CurrentState = (ULEN)(ExprOffset * 2);
|
||
|
||
if ( ExprOffset == ExprLen * sizeof(CHAR)) {
|
||
|
||
CurrentMatches[DestCount++] = MaxState;
|
||
break;
|
||
}
|
||
|
||
ExprChar = pszExpression[ExprOffset/sizeof(CHAR)];
|
||
|
||
ASSERT( !fIgnoreCase ||
|
||
!((ExprChar >= 'A') && (ExprChar <= 'Z')));
|
||
|
||
//
|
||
// Before we get started, we have to check for something really
|
||
// gross. We may be about to exhaust the local space for
|
||
// ExpressionMatch[][], so when we have to allocate some
|
||
// pool if this is the case. Yuk!
|
||
//
|
||
|
||
if ( (DestCount >= MAX_MATCHES_ARRAY_SIZE - 2) ) {
|
||
|
||
if (AuxBuffer == NULL) {
|
||
|
||
// 2 copies of array each with 2 states for each char
|
||
// in the expression. Each state == ULEN.
|
||
|
||
IF_DEBUG( DIR_LIST) {
|
||
DBGPRINTF((DBG_CONTEXT, "IsNInExpr(%s,%s,%d):"
|
||
"alloc %d for exprlen=%d\n",
|
||
pszExpression, pszName, fIgnoreCase,
|
||
(ExprLen + 1) *sizeof(ULEN)*2*2,
|
||
ExprLen));
|
||
}
|
||
|
||
AuxBuffer = ((ULEN *)
|
||
ALLOC((ExprLen +1) * sizeof(ULEN)* 2*2)
|
||
);
|
||
if ( AuxBuffer == NULL) {
|
||
|
||
DBG_ASSERT(!"Failure in mem alloc");
|
||
|
||
return ( FALSE);
|
||
}
|
||
|
||
RtlCopyMemory( AuxBuffer, CurrentMatches,
|
||
MAX_MATCHES_ARRAY_SIZE*sizeof(ULEN));
|
||
CurrentMatches = AuxBuffer;
|
||
|
||
RtlCopyMemory( AuxBuffer + (ExprLen + 1)*2,
|
||
PreviousMatches,
|
||
MAX_MATCHES_ARRAY_SIZE * sizeof(ULEN));
|
||
|
||
PreviousMatches = AuxBuffer + (ExprLen + 1)*2;
|
||
} else {
|
||
|
||
DBG_ASSERT(!"Double Overflow occured\n");
|
||
}
|
||
}
|
||
|
||
//
|
||
// '*' Matches any character zero or more times
|
||
//
|
||
if ( ExprChar == '*') {
|
||
|
||
// Add all possible next states into the list
|
||
// use the above state diagram to identify this.
|
||
CurrentMatches[DestCount] = CurrentState;
|
||
CurrentMatches[DestCount+1] = CurrentState + 1;
|
||
DestCount+= 2;
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// ANSI_DOS_STAR matches any char, zero or more times,
|
||
// except the DOS's extension '.'
|
||
//
|
||
|
||
if ( ExprChar == ANSI_DOS_STAR) {
|
||
|
||
BOOL ICanEatADot = FALSE;
|
||
|
||
//
|
||
// If we are at a period, determine if we are
|
||
// allowed to consume it. i.e make it is not last one.
|
||
//
|
||
if ( !NameFinished && (NameChar == '.')) {
|
||
|
||
ULEN cchOffset; // in character counts
|
||
for( cchOffset = NameOffset/sizeof(CHAR);
|
||
cchOffset < NameLen;
|
||
cchOffset ++) {
|
||
|
||
if ( pszName[cchOffset] == '.') {
|
||
|
||
ICanEatADot = TRUE;
|
||
break;
|
||
}
|
||
} // for
|
||
}
|
||
|
||
if ( NameFinished || (NameChar != '.') || ICanEatADot) {
|
||
|
||
//
|
||
// Go ahead and consume this character.
|
||
// Gives two options to move forward.
|
||
//
|
||
CurrentMatches[DestCount] = CurrentState;
|
||
CurrentMatches[DestCount+1] = CurrentState+1;
|
||
DestCount += 2;
|
||
} else {
|
||
|
||
//
|
||
// We are at the period. We can only match zero
|
||
// or more characters (ie. the epsilon transition)
|
||
//
|
||
|
||
CurrentMatches[DestCount++] = CurrentState+1;
|
||
continue;
|
||
}
|
||
} // if ( ExprChar == DOS_STAR)
|
||
|
||
//
|
||
// The following expression characters all match by consuming
|
||
// a character, thus force the expression, and thus state
|
||
// move forward.
|
||
//
|
||
|
||
CurrentState += (ULEN)(sizeof(CHAR) *2);
|
||
|
||
//
|
||
// DOS_QM is the most complicated. If the name is finished,
|
||
// we can match zero characters. If this name is a '.', we
|
||
// don't match, but look at the next expression. Otherwise
|
||
// we match a single character.
|
||
//
|
||
|
||
if ( ExprChar == ANSI_DOS_QM ) {
|
||
|
||
if ( NameFinished || (NameChar == '.') ) {
|
||
|
||
continue;
|
||
}
|
||
|
||
CurrentMatches[DestCount++] = CurrentState;
|
||
break;
|
||
}
|
||
|
||
//
|
||
// DOS_DOT can match either a period, or zero characters
|
||
// beyond the end of the name
|
||
//
|
||
|
||
if ( ExprChar == ANSI_DOS_DOT) {
|
||
|
||
if ( NameFinished) {
|
||
continue;
|
||
}
|
||
|
||
if ( NameChar == '.') {
|
||
|
||
CurrentMatches[DestCount++] = CurrentState;
|
||
break;
|
||
}
|
||
}
|
||
|
||
//
|
||
// From this point on a name character is required to
|
||
// even continue searching, let alone make a match.
|
||
// So if Name is finished, stop.
|
||
//
|
||
|
||
if ( NameFinished) {
|
||
|
||
break;
|
||
}
|
||
|
||
//
|
||
// If the expression was a '?' we can match it once
|
||
//
|
||
if ( ExprChar == '?') {
|
||
|
||
CurrentMatches[DestCount++] = CurrentState;
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Finally, check if the expression char matches name char
|
||
//
|
||
|
||
if ( ExprChar == (CHAR ) (fIgnoreCase ?
|
||
tolower(NameChar) : NameChar)
|
||
){
|
||
|
||
CurrentMatches[DestCount++] = CurrentState;
|
||
break;
|
||
}
|
||
|
||
//
|
||
// The expression did not match, go look at the next
|
||
// previous match
|
||
//
|
||
break;
|
||
} // for matching from an old state.
|
||
|
||
|
||
//
|
||
// Prevent duplication in the destination array.
|
||
//
|
||
// Each of the arrays is montonically increasing and non-
|
||
// duplicating, thus we skip over any source element in the src
|
||
// array if we just added the same element to the destination
|
||
// array. This guarentees non-duplication in the dest. array.
|
||
//
|
||
|
||
if ((SrcCount < MatchesCount) &&
|
||
(PreviousDestCount < DestCount) ) {
|
||
|
||
while ( SrcCount < MatchesCount &&
|
||
PreviousDestCount < DestCount) {
|
||
//
|
||
// logic here is: by eliminating the states with
|
||
// lesser number than current matched ==> we are
|
||
// skipping over the smallest states from which
|
||
// no match may be found.
|
||
//
|
||
|
||
if ( PreviousMatches[SrcCount] <
|
||
CurrentMatches[PreviousDestCount] ) {
|
||
|
||
SrcCount ++;
|
||
}
|
||
|
||
PreviousDestCount += 1;
|
||
} // while
|
||
}
|
||
} // for each of old matches....
|
||
|
||
//
|
||
// If we found no matches in the just finished iteration, it's time
|
||
// to bail.
|
||
//
|
||
|
||
if ( DestCount == 0 ) {
|
||
|
||
if (AuxBuffer != NULL) {
|
||
IF_DEBUG( DIR_LIST) {
|
||
|
||
DBGPRINTF((DBG_CONTEXT, " Freeing %08x\n", AuxBuffer));
|
||
}
|
||
|
||
FREE( AuxBuffer );
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// Swap the meaning the two arrays
|
||
//
|
||
|
||
{
|
||
ULEN *Tmp;
|
||
|
||
Tmp = PreviousMatches;
|
||
|
||
PreviousMatches = CurrentMatches;
|
||
|
||
CurrentMatches = Tmp;
|
||
}
|
||
|
||
MatchesCount = DestCount;
|
||
|
||
} // for each char in Name, until name is finished.
|
||
|
||
DBG_ASSERT(MatchesCount > 0);
|
||
CurrentState = PreviousMatches[MatchesCount-1];
|
||
if (AuxBuffer != NULL) {
|
||
IF_DEBUG( DIR_LIST) {
|
||
DBGPRINTF((DBG_CONTEXT, " Freeing %08x\n", AuxBuffer));
|
||
}
|
||
|
||
FREE( AuxBuffer );
|
||
}
|
||
|
||
return (BOOL ) ( CurrentState == MaxState);
|
||
|
||
} // IsNameInRegExpressionA()
|
||
|
||
|
||
/************************ End of File ***********************/
|
||
|