summaryrefslogtreecommitdiffstats
path: root/private/ntos/fsrtl/name.c
diff options
context:
space:
mode:
Diffstat (limited to 'private/ntos/fsrtl/name.c')
-rw-r--r--private/ntos/fsrtl/name.c1070
1 files changed, 1070 insertions, 0 deletions
diff --git a/private/ntos/fsrtl/name.c b/private/ntos/fsrtl/name.c
new file mode 100644
index 000000000..c70ee1063
--- /dev/null
+++ b/private/ntos/fsrtl/name.c
@@ -0,0 +1,1070 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ Name.c
+
+Abstract:
+
+ The unicode name support package is for manipulating unicode strings
+ The routines allow the caller to dissect and compare strings.
+
+ This package uses the same FSRTL_COMPARISON_RESULT typedef used by name.c
+
+ The following routines are provided by this package:
+
+ o FsRtlDissectName - This routine takes a path name string and breaks
+ into two parts. The first name in the string and the remainder.
+ It also checks that the first name is valid for an NT file.
+
+ o FsRtlColateNames - This routine is used to colate directories
+ according to lexical ordering. Lexical ordering is strict unicode
+ numerical oerdering.
+
+ o FsRtlDoesNameContainsWildCards - This routine tells the caller if
+ a string contains any wildcard characters.
+
+ o FsRtlIsNameInExpression - This routine is used to compare a string
+ against a template (possibly containing wildcards) to sees if the
+ string is in the language denoted by the template.
+
+Author:
+
+ Gary Kimura [GaryKi] 5-Feb-1990
+
+Revision History:
+
+--*/
+
+#include "FsRtlP.h"
+
+//
+// Trace level for the module
+//
+
+#define Dbg (0x10000000)
+
+//
+// Some special debugging stuff
+//
+
+#if DBG
+
+extern ULONG DaveDebug;
+#define DavePrint if (DaveDebug) DbgPrint
+
+#else
+
+#define DavePrint NOTHING
+
+#endif
+
+//
+// Local support routine prototypes
+//
+
+BOOLEAN
+FsRtlIsNameInExpressionPrivate (
+ IN PUNICODE_STRING Expression,
+ IN PUNICODE_STRING Name,
+ IN BOOLEAN IgnoreCase,
+ IN PWCH UpcaseTable
+ );
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE, FsRtlAreNamesEqual)
+#pragma alloc_text(PAGE, FsRtlDissectName)
+#pragma alloc_text(PAGE, FsRtlDoesNameContainWildCards)
+#pragma alloc_text(PAGE, FsRtlIsNameInExpression)
+#pragma alloc_text(PAGE, FsRtlIsNameInExpressionPrivate)
+#endif
+
+
+VOID
+FsRtlDissectName (
+ IN UNICODE_STRING Path,
+ OUT PUNICODE_STRING FirstName,
+ OUT PUNICODE_STRING RemainingName
+ )
+
+/*++
+
+Routine Description:
+
+ This routine cracks a path. It picks off the first element in the
+ given path name and provides both it and the remaining part. A path
+ is a set of file names separated by backslashes. If a name begins
+ with a backslash, the FirstName is the string immediately following
+ the backslash. Here are some examples:
+
+ Path FirstName RemainingName
+ ---- --------- -------------
+ empty empty empty
+
+ \ empty empty
+
+ A A empty
+
+ \A A empty
+
+ A\B\C\D\E A B\C\D\E
+
+ *A? *A? empty
+
+
+ Note that both output strings use the same string buffer memory of the
+ input string, and are not necessarily null terminated.
+
+ Also, this routine makes no judgement as to the legality of each
+ file name componant. This must be done separatly when each file name
+ is extracted.
+
+Arguments:
+
+ Path - The full path name to crack.
+
+ FirstName - The first name in the path. Don't allocate a buffer for
+ this string.
+
+ RemainingName - The rest of the path. Don't allocate a buffer for this
+ string.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ ULONG i = 0;
+ ULONG PathLength;
+ ULONG FirstNameStart;
+
+ PAGED_CODE();
+
+ //
+ // Make both output strings empty for now
+ //
+
+ FirstName->Length = 0;
+ FirstName->MaximumLength = 0;
+ FirstName->Buffer = NULL;
+
+ RemainingName->Length = 0;
+ RemainingName->MaximumLength = 0;
+ RemainingName->Buffer = NULL;
+
+ PathLength = Path.Length / sizeof(WCHAR);
+
+ //
+ // Check for an empty input string
+ //
+
+ if (PathLength == 0) {
+
+ return;
+ }
+
+ //
+ // Skip over a starting backslash, and make sure there is more.
+ //
+
+ if ( Path.Buffer[0] == L'\\' ) {
+
+ i = 1;
+ }
+
+ //
+ // Now run down the input string until we hit a backslash or the end
+ // of the string, remembering where we started;
+ //
+
+ for ( FirstNameStart = i;
+ (i < PathLength) && (Path.Buffer[i] != L'\\');
+ i += 1 ) {
+
+ NOTHING;
+ }
+
+ //
+ // At this point all characters up to (but not including) i are
+ // in the first part. So setup the first name
+ //
+
+ FirstName->Length = (USHORT)((i - FirstNameStart) * sizeof(WCHAR));
+ FirstName->MaximumLength = FirstName->Length;
+ FirstName->Buffer = &Path.Buffer[FirstNameStart];
+
+ //
+ // Now the remaining part needs a string only if the first part didn't
+ // exhaust the entire input string. We know that if anything is left
+ // that is must start with a backslash. Note that if there is only
+ // a trailing backslash, the length will get correctly set to zero.
+ //
+
+ if (i < PathLength) {
+
+ RemainingName->Length = (USHORT)((PathLength - (i + 1)) * sizeof(WCHAR));
+ RemainingName->MaximumLength = RemainingName->Length;
+ RemainingName->Buffer = &Path.Buffer[i + 1];
+ }
+
+ //
+ // And return to our caller
+ //
+
+ return;
+}
+
+BOOLEAN
+FsRtlDoesNameContainWildCards (
+ IN PUNICODE_STRING Name
+ )
+
+/*++
+
+Routine Description:
+
+ This routine simply scans the input Name string looking for any Nt
+ wild card characters.
+
+Arguments:
+
+ Name - The string to check.
+
+Return Value:
+
+ BOOLEAN - TRUE if one or more wild card characters was found.
+
+--*/
+{
+ PUSHORT p;
+
+ PAGED_CODE();
+
+ //
+ // Check each character in the name to see if it's a wildcard
+ // character.
+ //
+
+ if( Name->Length ) {
+ for( p = Name->Buffer + (Name->Length / sizeof(WCHAR)) - 1;
+ p >= Name->Buffer && *p != L'\\' ;
+ p-- ) {
+
+ //
+ // check for a wild card character
+ //
+
+ if (FsRtlIsUnicodeCharacterWild( *p )) {
+
+ //
+ // Tell caller that this name contains wild cards
+ //
+
+ return TRUE;
+ }
+ }
+ }
+
+ //
+ // No wildcard characters were found, so return to our caller
+ //
+
+ return FALSE;
+}
+
+
+BOOLEAN
+FsRtlAreNamesEqual (
+ PUNICODE_STRING ConstantNameA,
+ PUNICODE_STRING ConstantNameB,
+ IN BOOLEAN IgnoreCase,
+ IN PWCH UpcaseTable OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This routine simple returns whether the two names are exactly equal.
+ If the two names are known to be constant, this routine is much
+ faster than FsRtlIsNameInExpression.
+
+Arguments:
+
+ ConstantNameA - Constant name.
+
+ ConstantNameB - Constant name.
+
+ IgnoreCase - TRUE if the Names should be Upcased before comparing.
+
+ UpcaseTable - If supplied, use this table for case insensitive compares,
+ otherwise, use the default system upcase table.
+
+Return Value:
+
+ BOOLEAN - TRUE if the two names are lexically equal.
+
+--*/
+
+{
+ ULONG Index;
+ ULONG NameLength;
+ BOOLEAN FreeStrings = FALSE;
+
+ UNICODE_STRING LocalNameA;
+ UNICODE_STRING LocalNameB;
+
+ PAGED_CODE();
+
+ //
+ // If the names aren't even the same size, then return FALSE right away.
+ //
+
+ if ( ConstantNameA->Length != ConstantNameB->Length ) {
+
+ return FALSE;
+ }
+
+ NameLength = ConstantNameA->Length / sizeof(WCHAR);
+
+ //
+ // If we weren't given an upcase table, we have to upcase the names
+ // ourselves.
+ //
+
+ if ( IgnoreCase && !ARGUMENT_PRESENT(UpcaseTable) ) {
+
+ NTSTATUS Status;
+
+ Status = RtlUpcaseUnicodeString( &LocalNameA, ConstantNameA, TRUE );
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ ExRaiseStatus( Status );
+ }
+
+ Status = RtlUpcaseUnicodeString( &LocalNameB, ConstantNameB, TRUE );
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ RtlFreeUnicodeString( &LocalNameA );
+
+ ExRaiseStatus( Status );
+ }
+
+ ConstantNameA = &LocalNameA;
+ ConstantNameB = &LocalNameB;
+
+ IgnoreCase = FALSE;
+ FreeStrings = TRUE;
+ }
+
+ //
+ // Do either case sensitive or insensitive compare.
+ //
+
+ if ( !IgnoreCase ) {
+
+ BOOLEAN BytesEqual;
+
+ BytesEqual = (BOOLEAN) RtlEqualMemory( ConstantNameA->Buffer,
+ ConstantNameB->Buffer,
+ ConstantNameA->Length );
+
+ if ( FreeStrings ) {
+
+ RtlFreeUnicodeString( &LocalNameA );
+ RtlFreeUnicodeString( &LocalNameB );
+ }
+
+ return BytesEqual;
+
+ } else {
+
+ for (Index = 0; Index < NameLength; Index += 1) {
+
+ if ( UpcaseTable[ConstantNameA->Buffer[Index]] !=
+ UpcaseTable[ConstantNameB->Buffer[Index]] ) {
+
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+ }
+}
+
+
+//
+// The following routine is just a wrapper around
+// FsRtlIsNameInExpressionPrivate to make a last minute fix a bit safer.
+//
+
+BOOLEAN
+FsRtlIsNameInExpression (
+ IN PUNICODE_STRING Expression,
+ IN PUNICODE_STRING Name,
+ IN BOOLEAN IgnoreCase,
+ IN PWCH UpcaseTable OPTIONAL
+ )
+
+{
+ BOOLEAN Result;
+ UNICODE_STRING LocalName;
+
+ //
+ // If we weren't given an upcase table, we have to upcase the names
+ // ourselves.
+ //
+
+ if ( IgnoreCase && !ARGUMENT_PRESENT(UpcaseTable) ) {
+
+ NTSTATUS Status;
+
+ Status = RtlUpcaseUnicodeString( &LocalName, Name, TRUE );
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ ExRaiseStatus( Status );
+ }
+
+ Name = &LocalName;
+
+ IgnoreCase = FALSE;
+
+ } else {
+
+ LocalName.Buffer = NULL;
+ }
+
+ //
+ // Now call the main routine, remembering to free the upcased string
+ // if we allocated one.
+ //
+
+ try {
+
+ Result = FsRtlIsNameInExpressionPrivate( Expression,
+ Name,
+ IgnoreCase,
+ UpcaseTable );
+
+ } finally {
+
+ if (LocalName.Buffer != NULL) {
+
+ RtlFreeUnicodeString( &LocalName );
+ }
+ }
+
+ return Result;
+}
+
+
+#define MATCHES_ARRAY_SIZE 16
+
+//
+// Local support routine prototypes
+//
+
+BOOLEAN
+FsRtlIsNameInExpressionPrivate (
+ IN PUNICODE_STRING Expression,
+ IN PUNICODE_STRING Name,
+ IN BOOLEAN IgnoreCase,
+ IN PWCH UpcaseTable
+ )
+
+/*++
+
+Routine Description:
+
+ This routine compares a Dbcs name and an expression and tells the caller
+ if the name is in the language defined by the expression. The input name
+ cannot contain wildcards, while the expression may contain wildcards.
+
+ Expression wild cards are evaluated as shown in the nondeterministic
+ finite automatons below. Note that ~* and ~? are 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 the final .
+
+ e is a null character transition
+
+ EOF is the end of the name string
+
+ In words:
+
+ * matches 0 or more characters.
+
+ ? matches exactly 1 character.
+
+ DOS_STAR matches 0 or more characters until encountering and matching
+ the final . in the name.
+
+ DOS_QM matches any single character, or upon encountering a period or
+ end of name string, advances the expression to the end of the
+ set of contiguous DOS_QMs.
+
+ DOS_DOT matches either a . or zero characters beyond name string.
+
+Arguments:
+
+ Expression - Supplies the input expression to check against
+ (Caller must already upcase if passing CaseInsensitive TRUE.)
+
+ Name - Supplies the input name to check for.
+
+ CaseInsensitive - TRUE if Name should be Upcased before comparing.
+
+Return Value:
+
+ BOOLEAN - TRUE if Name is an element in the set of strings denoted
+ by the input Expression and FALSE otherwise.
+
+--*/
+
+{
+ USHORT NameOffset;
+ USHORT ExprOffset;
+
+ ULONG SrcCount;
+ ULONG DestCount;
+ ULONG PreviousDestCount;
+ ULONG MatchesCount;
+
+ WCHAR NameChar, ExprChar;
+
+ USHORT LocalBuffer[MATCHES_ARRAY_SIZE * 2];
+
+ USHORT *AuxBuffer = NULL;
+ USHORT *PreviousMatches;
+ USHORT *CurrentMatches;
+
+ USHORT MaxState;
+ USHORT CurrentState;
+
+ BOOLEAN NameFinished = FALSE;
+
+ //
+ // The idea behind the algorithm is pretty simple. We 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.
+ //
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "FsRtlIsNameInExpression\n", 0);
+ DebugTrace( 0, Dbg, " Expression = %Z\n", Expression );
+ DebugTrace( 0, Dbg, " Name = %Z\n", Name );
+ DebugTrace( 0, Dbg, " CaseInsensitive = %08lx\n", CaseInsensitive );
+
+ ASSERT( Name->Length != 0 );
+ ASSERT( Expression->Length != 0 );
+
+ //
+ // If one string is empty return FALSE. If both are empty return TRUE.
+ //
+
+ if ( (Name->Length == 0) || (Expression->Length == 0) ) {
+
+ return (BOOLEAN)(!(Name->Length + Expression->Length));
+ }
+
+ //
+ // Special case by far the most common wild card search of *
+ //
+
+ if ((Expression->Length == 2) && (Expression->Buffer[0] == L'*')) {
+
+ return TRUE;
+ }
+
+ ASSERT( FsRtlDoesNameContainWildCards( Expression ) );
+
+ ASSERT( !IgnoreCase || ARGUMENT_PRESENT(UpcaseTable) );
+
+ //
+ // Also special case expressions of the form *X. With this and the prior
+ // case we have covered virtually all normal queries.
+ //
+
+ if (Expression->Buffer[0] == L'*') {
+
+ UNICODE_STRING LocalExpression;
+
+ LocalExpression = *Expression;
+
+ LocalExpression.Buffer += 1;
+ LocalExpression.Length -= 2;
+
+ //
+ // Only special case an expression with a single *
+ //
+
+ if ( !FsRtlDoesNameContainWildCards( &LocalExpression ) ) {
+
+ ULONG StartingNameOffset;
+
+ if (Name->Length < (USHORT)(Expression->Length - sizeof(WCHAR))) {
+
+ return FALSE;
+ }
+
+ StartingNameOffset = ( Name->Length -
+ LocalExpression.Length ) / sizeof(WCHAR);
+
+ //
+ // Do a simple memory compare if case sensitive, otherwise
+ // we have got to check this one character at a time.
+ //
+
+ if ( !IgnoreCase ) {
+
+ return (BOOLEAN) RtlEqualMemory( LocalExpression.Buffer,
+ Name->Buffer + StartingNameOffset,
+ LocalExpression.Length );
+
+ } else {
+
+ for ( ExprOffset = 0;
+ ExprOffset < (USHORT)(LocalExpression.Length / sizeof(WCHAR));
+ ExprOffset += 1 ) {
+
+ NameChar = Name->Buffer[StartingNameOffset + ExprOffset];
+ NameChar = UpcaseTable[NameChar];
+
+ ExprChar = LocalExpression.Buffer[ExprOffset];
+
+ ASSERT( ExprChar == UpcaseTable[ExprChar] );
+
+ if ( NameChar != ExprChar ) {
+
+ return FALSE;
+ }
+ }
+
+ 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 expression. 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 expreesion 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 itteration through the Matches array
+ // after the name is exhusted (to come *s for example)
+ //
+ // PreviousDestCount - This is used to prevent entry duplication, see coment
+ //
+ // 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
+ //
+
+ //
+ // Set up the initial variables
+ //
+
+ PreviousMatches = &LocalBuffer[0];
+ CurrentMatches = &LocalBuffer[MATCHES_ARRAY_SIZE];
+
+ PreviousMatches[0] = 0;
+ MatchesCount = 1;
+
+ NameOffset = 0;
+
+ MaxState = (USHORT)(Expression->Length * 2);
+
+ while ( !NameFinished ) {
+
+ if ( NameOffset < Name->Length ) {
+
+ NameChar = Name->Buffer[NameOffset / sizeof(WCHAR)];
+
+ NameOffset += sizeof(WCHAR);;
+
+ } else {
+
+ NameFinished = TRUE;
+
+ //
+ // if we have already exhasted the expression, cool. Don't
+ // continue.
+ //
+
+ if ( PreviousMatches[MatchesCount-1] == MaxState ) {
+
+ break;
+ }
+ }
+
+
+ //
+ // Now, for each of the previous stored expression matches, see what
+ // we can do with this name character.
+ //
+
+ SrcCount = 0;
+ DestCount = 0;
+ PreviousDestCount = 0;
+
+ while ( SrcCount < MatchesCount ) {
+
+ USHORT 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 = (USHORT)((PreviousMatches[SrcCount++] + 1) / 2);
+
+
+ Length = 0;
+
+ while ( TRUE ) {
+
+ if ( ExprOffset == Expression->Length ) {
+
+ break;
+ }
+
+ //
+ // The first time through the loop we don't want
+ // to increment ExprOffset.
+ //
+
+ ExprOffset += Length;
+ Length = sizeof(WCHAR);
+
+ CurrentState = (USHORT)(ExprOffset * 2);
+
+ if ( ExprOffset == Expression->Length ) {
+
+ CurrentMatches[DestCount++] = MaxState;
+ break;
+ }
+
+ ExprChar = Expression->Buffer[ExprOffset / sizeof(WCHAR)];
+
+ ASSERT( !IgnoreCase || !((ExprChar >= L'a') && (ExprChar <= L'z')) );
+
+ //
+ // Before we get started, we have to check for something
+ // really gross. We may be about to exhaust the local
+ // space for ExpressionMatches[][], so we have to allocate
+ // some pool if this is the case. Yuk!
+ //
+
+ if ( (DestCount >= MATCHES_ARRAY_SIZE - 2) &&
+ (AuxBuffer == NULL) ) {
+
+ ULONG ExpressionChars;
+
+ ExpressionChars = Expression->Length / sizeof(WCHAR);
+
+ AuxBuffer = FsRtlAllocatePool( PagedPool,
+ (ExpressionChars+1) *
+ sizeof(USHORT)*2*2 );
+
+ RtlCopyMemory( AuxBuffer,
+ CurrentMatches,
+ MATCHES_ARRAY_SIZE * sizeof(USHORT) );
+
+ CurrentMatches = AuxBuffer;
+
+ RtlCopyMemory( AuxBuffer + (ExpressionChars+1)*2,
+ PreviousMatches,
+ MATCHES_ARRAY_SIZE * sizeof(USHORT) );
+
+ PreviousMatches = AuxBuffer + (ExpressionChars+1)*2;
+ }
+
+ //
+ // * matches any character zero or more times.
+ //
+
+ if (ExprChar == L'*') {
+
+ CurrentMatches[DestCount++] = CurrentState;
+ CurrentMatches[DestCount++] = CurrentState + 3;
+ continue;
+ }
+
+ //
+ // DOS_STAR matches any character except . zero or more times.
+ //
+
+ if (ExprChar == DOS_STAR) {
+
+ BOOLEAN ICanEatADot = FALSE;
+
+ //
+ // If we are at a period, determine if we are allowed to
+ // consume it, ie. make sure it is not the last one.
+ //
+
+ if ( !NameFinished && (NameChar == '.') ) {
+
+ USHORT Offset;
+
+ for ( Offset = NameOffset;
+ Offset < Name->Length;
+ Offset += Length ) {
+
+ if (Name->Buffer[Offset / sizeof(WCHAR)] == L'.') {
+
+ ICanEatADot = TRUE;
+ break;
+ }
+ }
+ }
+
+ if (NameFinished || (NameChar != L'.') || ICanEatADot) {
+
+ CurrentMatches[DestCount++] = CurrentState;
+ CurrentMatches[DestCount++] = CurrentState + 3;
+ continue;
+
+ } else {
+
+ //
+ // We are at a period. We can only match zero
+ // characters (ie. the epsilon transition).
+ //
+
+ CurrentMatches[DestCount++] = CurrentState + 3;
+ continue;
+ }
+ }
+
+ //
+ // The following expreesion characters all match by consuming
+ // a character, thus force the expression, and thus state
+ // forward.
+ //
+
+ CurrentState += (USHORT)(sizeof(WCHAR) * 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 == DOS_QM ) {
+
+ if ( NameFinished || (NameChar == L'.') ) {
+
+ continue;
+ }
+
+ CurrentMatches[DestCount++] = CurrentState;
+ break;
+ }
+
+ //
+ // A DOS_DOT can match either a period, or zero characters
+ // beyond the end of name.
+ //
+
+ if (ExprChar == DOS_DOT) {
+
+ if ( NameFinished ) {
+
+ continue;
+ }
+
+ if (NameChar == L'.') {
+
+ CurrentMatches[DestCount++] = CurrentState;
+ break;
+ }
+ }
+
+ //
+ // From this point on a name character is required to even
+ // continue, let alone make a match.
+ //
+
+ if ( NameFinished ) {
+
+ break;
+ }
+
+ //
+ // If this expression was a '?' we can match it once.
+ //
+
+ if (ExprChar == L'?') {
+
+ CurrentMatches[DestCount++] = CurrentState;
+ break;
+ }
+
+ //
+ // Finally, check if the expression char matches the name char
+ //
+
+ if (ExprChar == (WCHAR)(IgnoreCase ?
+ UpcaseTable[NameChar] : NameChar)) {
+
+ CurrentMatches[DestCount++] = CurrentState;
+ break;
+ }
+
+ //
+ // The expression didn't match so go look at the next
+ // previous match.
+ //
+
+ break;
+ }
+
+
+ //
+ // 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 (PreviousDestCount < DestCount) {
+
+ while ( PreviousMatches[SrcCount] <
+ CurrentMatches[PreviousDestCount] ) {
+
+ SrcCount += 1;
+ }
+
+ PreviousDestCount += 1;
+ }
+ }
+ }
+
+ //
+ // If we found no matches in the just finished itteration, it's time
+ // to bail.
+ //
+
+ if ( DestCount == 0 ) {
+
+ if (AuxBuffer != NULL) { ExFreePool( AuxBuffer ); }
+
+ return FALSE;
+ }
+
+ //
+ // Swap the meaning the two arrays
+ //
+
+ {
+ USHORT *Tmp;
+
+ Tmp = PreviousMatches;
+
+ PreviousMatches = CurrentMatches;
+
+ CurrentMatches = Tmp;
+ }
+
+ MatchesCount = DestCount;
+ }
+
+
+ CurrentState = PreviousMatches[MatchesCount-1];
+
+ if (AuxBuffer != NULL) { ExFreePool( AuxBuffer ); }
+
+
+ return (BOOLEAN)(CurrentState == MaxState);
+}