Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

942 lines
28 KiB

  1. /*++
  2. Copyright (c) 1995 Microsoft Corporation
  3. Module Name :
  4. getdir.cxx
  5. Abstract:
  6. This module implements the member functions for TS_DIRECTORY_INFO
  7. Author:
  8. Murali R. Krishnan ( MuraliK ) 16-Jan-1995
  9. Project:
  10. Tsunami Lib
  11. ( Common caching and directory functions for Internet Services)
  12. Functions Exported:
  13. TS_DIRECTORY_INFO::CleanupThis()
  14. TS_DIRECTORY_INFO::GetDirectoryListingA()
  15. IN LPCSTR pszDirectoryName,
  16. IN HANDLE hListingUser)
  17. TS_DIRECTORY_INFO::SortFileInfoPointers(
  18. IN PFN_CMP_FILE_BOTH_DIR_INFO pfnCompare)
  19. TS_DIRECTORY_INFO::FilterFiles(
  20. IN PFN_IS_MATCH_FILE_BOTH_DIR_INFO pfnMatch,
  21. IN LPVOID pContext);
  22. Revision History:
  23. --*/
  24. /************************************************************
  25. * Include Headers
  26. ************************************************************/
  27. # include "tsunamip.hxx"
  28. # include "dbgutil.h"
  29. # include <string.h>
  30. /************************************************************
  31. * Type Definitions
  32. ************************************************************/
  33. dllexp
  34. VOID
  35. TS_DIRECTORY_INFO::CleanupThis( VOID)
  36. {
  37. IF_DEBUG( DIR_LIST) {
  38. DBGPRINTF( ( DBG_CONTEXT,
  39. "Cleaning up TS_DIRECTORY_INFO ( %08x)\n",
  40. this));
  41. Print();
  42. }
  43. if ( m_fValid) {
  44. if ( m_pTsDirectoryHeader != NULL) {
  45. TsFreeDirectoryListing( m_tsCache, m_pTsDirectoryHeader);
  46. m_pTsDirectoryHeader = NULL;
  47. }
  48. ASSERT( m_pTsDirectoryHeader == NULL);
  49. if ( m_prgFileInfo != NULL) {
  50. FREE( m_prgFileInfo);
  51. m_prgFileInfo = NULL;
  52. }
  53. }
  54. ASSERT( m_pTsDirectoryHeader == NULL);
  55. ASSERT( m_prgFileInfo == NULL);
  56. m_fValid = 0;
  57. m_cFilesInDirectory = 0;
  58. } // TS_DIRECTORY_INFO::CleanupThis()
  59. static BOOL
  60. MakeCopyOfFileInfoPointers(
  61. IN OUT PFILE_BOTH_DIR_INFORMATION ** pppFileInfoTo,
  62. IN const PFILE_BOTH_DIR_INFORMATION * ppFileInfoFrom,
  63. IN int nEntries)
  64. /*++
  65. Allocates memory and makes a copy of the file info pointers in the array
  66. in ppFileInfoFrom.
  67. Returns:
  68. TRUE if success and FALSE if there is any failure.
  69. --*/
  70. {
  71. DWORD cbCopy;
  72. ASSERT( *pppFileInfoTo == NULL);
  73. cbCopy = nEntries * sizeof( PFILE_BOTH_DIR_INFORMATION);
  74. *pppFileInfoTo = (PFILE_BOTH_DIR_INFORMATION *) ALLOC( cbCopy);
  75. if ( *pppFileInfoTo != NULL) {
  76. memcpy( (PVOID ) *pppFileInfoTo,
  77. (const PVOID ) ppFileInfoFrom,
  78. cbCopy);
  79. } else {
  80. SetLastError( ERROR_NOT_ENOUGH_MEMORY);
  81. }
  82. return ( *pppFileInfoTo != NULL);
  83. } // MakeCopyOfFileInfoPointers()
  84. dllexp
  85. BOOL
  86. TS_DIRECTORY_INFO::GetDirectoryListingA(
  87. IN LPCSTR pszDirectoryName,
  88. IN HANDLE hListingUser)
  89. {
  90. if ( m_pTsDirectoryHeader == NULL) {
  91. //
  92. // Only if already a directory listing is not obtained.
  93. // we obtain newly
  94. //
  95. IF_DEBUG( DIR_LIST) {
  96. DBGPRINTF( ( DBG_CONTEXT,
  97. "Obtaining Dir Listing for %s. UserHandle=%08x.\n",
  98. pszDirectoryName, hListingUser));
  99. }
  100. m_fValid = TsGetDirectoryListingA( m_tsCache,
  101. pszDirectoryName,
  102. hListingUser,
  103. &m_pTsDirectoryHeader);
  104. m_fValid = m_fValid &&
  105. MakeCopyOfFileInfoPointers(
  106. &m_prgFileInfo,
  107. m_pTsDirectoryHeader->QueryArrayOfFileInfoPointers(),
  108. m_pTsDirectoryHeader->QueryNumEntries());
  109. m_cFilesInDirectory = ( m_pTsDirectoryHeader == NULL) ? 0 :
  110. m_pTsDirectoryHeader->QueryNumEntries();
  111. }
  112. return ( m_fValid);
  113. } // TS_DIRECTORY_INFO::GetDirectoryListingA()
  114. # ifdef UNICODE
  115. dllexp
  116. BOOL
  117. TS_DIRECTORY_INFO::GetDirectoryListingW(
  118. IN LPCWSTR pwszDirectoryName,
  119. IN HANDLE hListingUser)
  120. {
  121. if ( m_pTsDirectoryHeader == NULL) {
  122. //
  123. // Only if already a directory listing is not obtained.
  124. // we obtain newly
  125. //
  126. m_fValid = TsGetDirectoryListingW( m_tsCache,
  127. pwszDirectoryName,
  128. hListingUser,
  129. &m_pTsDirectoryHeader);
  130. m_fValid = m_fValid &&
  131. MakeCopyOfFileInfoPointers(
  132. &m_prgFileInfo,
  133. m_pTsDirectoryHeader->QueryArrayOfFileInfoPointers(),
  134. m_pTsDirectoryHeader->QueryNumEntries());
  135. m_cFilesInDirectory = ( m_pTsDirectoryHeader == NULL) ? 0 :
  136. m_pTsDirectoryHeader->QueryNumEntries();
  137. }
  138. return ( m_fValid);
  139. } // TS_DIRECTORY_INFO::GetDirectoryListingW()
  140. # endif // UNICODE
  141. dllexp
  142. BOOL
  143. TS_DIRECTORY_INFO::SortFileInfoPointers(
  144. IN PFN_CMP_FILE_BOTH_DIR_INFO pfnCompare)
  145. {
  146. BOOL fReturn;
  147. if ( IsValid()) {
  148. fReturn = SortInPlaceFileInfoPointers( m_prgFileInfo,
  149. m_cFilesInDirectory,
  150. pfnCompare);
  151. }
  152. return ( fReturn);
  153. } // TS_DIRECTORY_INFO::SortFileInfoPointers()
  154. dllexp
  155. BOOL
  156. TS_DIRECTORY_INFO::FilterFiles( IN PFN_IS_MATCH_FILE_BOTH_DIR_INFO pfnMatch,
  157. IN LPVOID pContext)
  158. /*++
  159. This function filters the list of files using the pfnMatch function
  160. and the context information specified by pContext.
  161. This function eliminates all the pointers to FileInfo which do not
  162. match the given file specification.
  163. Returns:
  164. TRUE on success and FALSE on failure.
  165. --*/
  166. {
  167. BOOL fReturn = FALSE;
  168. if ( IsValid()) {
  169. int idxScan; // for scanning the files
  170. int idxCur; // for updating after filter
  171. IF_DEBUG( DIR_LIST) {
  172. DBGPRINTF( ( DBG_CONTEXT,
  173. "FilterFiles in DirList( %08x) for FileSpec %08x\n",
  174. this, pContext));
  175. }
  176. for( idxCur = idxScan = 0;
  177. idxScan < m_cFilesInDirectory;
  178. idxScan++) {
  179. PFILE_BOTH_DIR_INFORMATION pFileInfo =
  180. GetFileInfoPointerFromIdx( idxScan);
  181. ASSERT( pFileInfo != NULL);
  182. ASSERT( idxCur <= idxScan);
  183. if ( (*pfnMatch)( pFileInfo, pContext)) {
  184. //
  185. // this is a match. Retain this item and advance CurPtr
  186. //
  187. m_prgFileInfo[ idxCur++] = m_prgFileInfo[ idxScan];
  188. }
  189. } // for
  190. m_cFilesInDirectory = idxCur;
  191. fReturn = TRUE;
  192. }
  193. return ( fReturn);
  194. } // TS_DIRECTORY_INFO::FilterFiles()
  195. # if DBG
  196. VOID
  197. TS_DIRECTORY_INFO::Print( VOID) const
  198. {
  199. DBGPRINTF( ( DBG_CONTEXT,
  200. " Printing TS_DIRECTORY_INFO ( %08x).\n", this));
  201. DBGPRINTF( ( DBG_CONTEXT,
  202. "NumEntries=%d\t Valid = %d\n",
  203. m_cFilesInDirectory, m_fValid));
  204. DBGPRINTF( ( DBG_CONTEXT,
  205. "Directory Header ( %08x) \t ArrayOfFileInfo = %08x\n",
  206. m_pTsDirectoryHeader, m_prgFileInfo));
  207. for( int idx = 0; idx < m_cFilesInDirectory; idx++) {
  208. PFILE_BOTH_DIR_INFORMATION pfi = m_prgFileInfo[idx];
  209. DBGPRINTF( ( DBG_CONTEXT,
  210. "rgFileInfo[%4d] = %08x. Name=%s Attr=0x%x"
  211. "Size=0x%x:%x\n",
  212. idx, pfi,
  213. pfi->FileName,
  214. pfi->FileAttributes,
  215. pfi->EndOfFile.HighPart,
  216. pfi->EndOfFile.LowPart
  217. ));
  218. }
  219. m_pTsDirectoryHeader->Print();
  220. return;
  221. } // TS_DIRECTORY_INFO::Print()
  222. # endif // DBG
  223. BOOL __cdecl
  224. RegExpressionMatchFileInfo( IN const FILE_BOTH_DIR_INFORMATION * pFileInfo,
  225. IN CHAR * pszExpression)
  226. /*++
  227. This function tries to find a match between the file name in
  228. pFileInfo and the regular expression specified in pszExpression.
  229. Arguments:
  230. pFileInfo -- pointer to file information consisting under query.
  231. pszExpression - pointer to null-terminated string containing the
  232. regular expression, against which file name is tobe
  233. matched for.
  234. Returns:
  235. TRUE on a match and false if there is any failure.
  236. --*/
  237. {
  238. const CHAR * pszFileName;
  239. DBG_ASSERT( pFileInfo != NULL);
  240. pszFileName = (const CHAR *) pFileInfo->FileName;
  241. if ( strpbrk( pszExpression, "?*<>") != NULL) {
  242. // No Wild cards. Do normal file comparisons
  243. return ( strcmp( pszFileName, pszExpression) == 0);
  244. } else {
  245. // do a case sensitive comparison
  246. return IsNameInRegExpressionA( pszExpression, pszFileName, FALSE);
  247. }
  248. } // RegExpressionMatch()
  249. /************************************************************
  250. * Following code is based on the FileSystem Rtl routines
  251. * from ntos\fsrtl\name.c
  252. * But these are optimized for performance, in our case
  253. * using ANSI strings!
  254. ************************************************************/
  255. # define MAX_MATCHES_ARRAY_SIZE (16)
  256. # define IS_EMPTY_STRING(psz) ( (psz) == NULL || *(psz) == '\0')
  257. //
  258. // Original code used USHORT for ULEN. However using USHORT asks
  259. // a 32 bit processor to add "and <value>, 0xff for each instruction
  260. // that accessed the 16 bit (USHORT) value.
  261. // Hence, I decided to use DWORD, since the space usage is not tremendous
  262. // during the fast path work, compared to performance benefits.
  263. // - MuraliK (Oct 27, 1995)
  264. //
  265. // typedef USHORT ULEN;
  266. typedef DWORD ULEN;
  267. BOOL __cdecl
  268. IsNameInRegExpressionA(
  269. IN LPCSTR pszExpression,
  270. IN LPCSTR pszName,
  271. IN BOOL fIgnoreCase)
  272. /*++
  273. This routine compares an ANSI name and an expression and decries to the
  274. caller if the name is in hte language defined by the expression. The input
  275. name cannot contain wildcards, while the expression itself may contain
  276. wildcards.
  277. Expression wild cards are evaluated as shown in the non-deterministic finite
  278. automatons below. Note that ~* and ~? stand for DOS_STAR and DOS_QM.
  279. ~* is DOS_STAR, ~? is DOS_QM, and ~. is DOS_DOT
  280. S
  281. <-----<
  282. X | | e Y
  283. X * Y == (0)----->-(1)->-----(2)-----(3)
  284. S-.
  285. <-----<
  286. X | | e Y
  287. X ~* Y == (0)----->-(1)->-----(2)-----(3)
  288. X S S Y
  289. X ?? Y == (0)---(1)---(2)---(3)---(4)
  290. X . . Y
  291. X ~.~. Y == (0)---(1)----(2)------(3)---(4)
  292. | |________|
  293. | ^ |
  294. |_______________|
  295. ^EOF or .^
  296. X S-. S-. Y
  297. X ~?~? Y == (0)---(1)-----(2)-----(3)---(4)
  298. | |________|
  299. | ^ |
  300. |_______________|
  301. ^EOF or .^
  302. where S is any single character
  303. S-. is any single character except .
  304. e is a null character transition
  305. EOF is the end of the name string
  306. The last construction, ~? (the DOS question mark), can either match any
  307. single character, or upon encountering a period or end of input string,
  308. advances the expression to the end of the set of contiguous ~?s. This may
  309. seem somewhat convoluted, but is what DOS needs.
  310. Arguments:
  311. pszExpression - Supplies the input expression to check against
  312. ( Caller must already lowercased if passing fIgnoreCase TRUE.)
  313. pszName - supplies the input name to check for.
  314. fIgnoreCase - if TRUE, the name should be lower-cased before comparing.
  315. ( that is done by this function, dynamically without destroying pszName)
  316. This function is costly, if the pszExpression does not contain
  317. any wild cards to be matched for. So Dont use it if there are
  318. no wild cards in the pszExpression
  319. Returns:
  320. BOOL -- TRUE if pszName is an element in the set of strings denoted
  321. by the input expression. FALSE if otherwise.
  322. --*/
  323. {
  324. ULEN NameLen; // length in character count
  325. ULEN ExprLen;
  326. /*
  327. * Algorithm:
  328. * Keep track of all possible locations in the regular expression
  329. * that are matching the name. If when the name has been exhausted
  330. * one of the locations in the expression is also just exhausted, the
  331. * name is in the language defined by the regular expression.
  332. */
  333. DBG_ASSERT( pszName != NULL && *pszName != '\0');
  334. DBG_ASSERT( pszExpression != NULL && *pszName != '\0');
  335. //
  336. // if one string is empty return FALSE. If both are empty TRUE.
  337. //
  338. if ( IS_EMPTY_STRING(pszName) || IS_EMPTY_STRING(pszExpression)) {
  339. IF_DEBUG( DIR_LIST) {
  340. DBGPRINTF((DBG_CONTEXT, " IsNameInRegExpr( %s, %s, %d) ==>%d\n",
  341. pszExpression, pszName, fIgnoreCase,
  342. !(*pszName + *pszExpression)
  343. ));
  344. }
  345. return (BOOL ) (!(*pszName + *pszExpression));
  346. }
  347. NameLen = strlen(pszName);
  348. ExprLen = strlen(pszExpression);
  349. //
  350. // Special case: reduce the most common wild card search of *
  351. //
  352. if ( ExprLen == 1 && pszExpression[0] == '*') {
  353. IF_DEBUG ( DIR_LIST) {
  354. DBGPRINTF((DBG_CONTEXT, " IsNameInRegExpr( %s, %s, %d) ==>%d\n",
  355. pszExpression, pszName, fIgnoreCase,
  356. TRUE
  357. ));
  358. }
  359. // matches anything. so return TRUE
  360. return (TRUE);
  361. }
  362. //
  363. // Walk through the name string, picking off characters. We go one
  364. // character beyond the end because some wild cards are able to match
  365. // zero characters beyond the end of the string.
  366. //
  367. // With each new name character we determine a new set of states that
  368. // match the name so far. We use two arrays that we swap back and forth
  369. // for this purpose. One array lists the possible expression states for
  370. // all name characters up to but not including the current one, and other
  371. // array is used to build up the list of states considering the current
  372. // name character as well. The arrays are then switched and the process
  373. // repeated.
  374. //
  375. // There is not a one-to-one correspondence between state number and
  376. // offset into the pszExpression. This is evident from the NFAs in the
  377. // initial comment to this function. State numbering is not continuous.
  378. // This allows a simple conversion between state number and expression
  379. // offset. Each character in the Expression can represent one or two
  380. // states. '*' and DOS_STAR generate two states: ExprOffset*2 and
  381. // ExprOffset*2 + 1. All other expression characters can produce only
  382. // a single state. Thus ExprOffset = State/2.
  383. //
  384. //
  385. // Here is a short description of the variables involved:
  386. //
  387. // NameOffset -The offset of the current name char being processed.
  388. //
  389. // ExprOffset -The offset of the current expression char being processed.
  390. //
  391. // SrcCount -Prior match being investigated with current name char
  392. //
  393. // DestCount -Next location to put a matching assuming current name char
  394. //
  395. // NameFinished - Allows one more iteration through the Matches array
  396. // after the name is exhusted (to come *s for example)
  397. //
  398. // PreviousDestCount - This is used to prevent entry duplication,
  399. // see comment
  400. //
  401. // PreviousMatches - Holds the previous set of matches (the Src array)
  402. //
  403. // CurrentMatches - Holds the current set of matches (the Dest array)
  404. //
  405. // AuxBuffer, LocalBuffer - the storage for the Matches arrays
  406. //
  407. ULEN NameOffset; // offset in terms of byte count
  408. ULEN ExprOffset; // offset in terms of byte count
  409. ULONG SrcCount;
  410. ULONG DestCount;
  411. ULONG PreviousDestCount;
  412. ULONG MatchesCount;
  413. CHAR NameChar, ExprChar;
  414. // for prev and current matches
  415. ULEN *AuxBuffer = NULL;
  416. ULEN *PreviousMatches;
  417. ULEN *CurrentMatches;
  418. ULEN MaxState;
  419. ULEN CurrentState;
  420. BOOL NameFinished;
  421. ULEN LocalBuffer[MAX_MATCHES_ARRAY_SIZE * 2];
  422. // set up the intial values
  423. // Use the different portions of local buffer for matches.
  424. PreviousMatches = &LocalBuffer[0];
  425. CurrentMatches = &LocalBuffer[MAX_MATCHES_ARRAY_SIZE];
  426. PreviousMatches[0] = 0;
  427. MatchesCount = 1;
  428. NameOffset = 0;
  429. MaxState = (ULEN ) (ExprLen * 2);
  430. NameFinished = FALSE;
  431. while (!NameFinished) {
  432. if ( NameOffset < NameLen) {
  433. NameChar = pszName[NameOffset/sizeof(CHAR)];
  434. NameOffset += sizeof(CHAR);
  435. } else {
  436. NameFinished = TRUE;
  437. // if we already exhauseted expression, stop. Else continue
  438. DBG_ASSERT( MatchesCount >= 1);
  439. if ( PreviousMatches[MatchesCount - 1] == MaxState) {
  440. break;
  441. }
  442. }
  443. //
  444. // Now, for each of previous stored expression matches,
  445. // see what we can do with the new name character.
  446. //
  447. DestCount = 0;
  448. PreviousDestCount = 0;
  449. for( SrcCount = 0; SrcCount < MatchesCount; ) {
  450. ULEN Length;
  451. //
  452. // We have to carry on our expression analysis as far as possible
  453. // for each character of name, so we loop here until the
  454. // expression stops matching. A clue here is that expression
  455. // cases that can match zero or more characters end with a
  456. // continue, while those that can accept only a single character
  457. // end with a break.
  458. //
  459. ExprOffset = (ULEN)((PreviousMatches[SrcCount++] + 1) / 2);
  460. for( Length = 0; ExprOffset != ExprLen; ) {
  461. //
  462. // increment the expression offset to move to next character.
  463. //
  464. ExprOffset += Length;
  465. Length = sizeof(CHAR);
  466. CurrentState = (ULEN)(ExprOffset * 2);
  467. if ( ExprOffset == ExprLen * sizeof(CHAR)) {
  468. CurrentMatches[DestCount++] = MaxState;
  469. break;
  470. }
  471. ExprChar = pszExpression[ExprOffset/sizeof(CHAR)];
  472. ASSERT( !fIgnoreCase ||
  473. !((ExprChar >= 'A') && (ExprChar <= 'Z')));
  474. //
  475. // Before we get started, we have to check for something really
  476. // gross. We may be about to exhaust the local space for
  477. // ExpressionMatch[][], so when we have to allocate some
  478. // pool if this is the case. Yuk!
  479. //
  480. if ( (DestCount >= MAX_MATCHES_ARRAY_SIZE - 2) ) {
  481. if (AuxBuffer == NULL) {
  482. // 2 copies of array each with 2 states for each char
  483. // in the expression. Each state == ULEN.
  484. IF_DEBUG( DIR_LIST) {
  485. DBGPRINTF((DBG_CONTEXT, "IsNInExpr(%s,%s,%d):"
  486. "alloc %d for exprlen=%d\n",
  487. pszExpression, pszName, fIgnoreCase,
  488. (ExprLen + 1) *sizeof(ULEN)*2*2,
  489. ExprLen));
  490. }
  491. AuxBuffer = ((ULEN *)
  492. ALLOC((ExprLen +1) * sizeof(ULEN)* 2*2)
  493. );
  494. if ( AuxBuffer == NULL) {
  495. DBG_ASSERT(!"Failure in mem alloc");
  496. return ( FALSE);
  497. }
  498. RtlCopyMemory( AuxBuffer, CurrentMatches,
  499. MAX_MATCHES_ARRAY_SIZE*sizeof(ULEN));
  500. CurrentMatches = AuxBuffer;
  501. RtlCopyMemory( AuxBuffer + (ExprLen + 1)*2,
  502. PreviousMatches,
  503. MAX_MATCHES_ARRAY_SIZE * sizeof(ULEN));
  504. PreviousMatches = AuxBuffer + (ExprLen + 1)*2;
  505. } else {
  506. DBG_ASSERT(!"Double Overflow occured\n");
  507. }
  508. }
  509. //
  510. // '*' Matches any character zero or more times
  511. //
  512. if ( ExprChar == '*') {
  513. // Add all possible next states into the list
  514. // use the above state diagram to identify this.
  515. CurrentMatches[DestCount] = CurrentState;
  516. CurrentMatches[DestCount+1] = CurrentState + 1;
  517. DestCount+= 2;
  518. continue;
  519. }
  520. //
  521. // ANSI_DOS_STAR matches any char, zero or more times,
  522. // except the DOS's extension '.'
  523. //
  524. if ( ExprChar == ANSI_DOS_STAR) {
  525. BOOL ICanEatADot = FALSE;
  526. //
  527. // If we are at a period, determine if we are
  528. // allowed to consume it. i.e make it is not last one.
  529. //
  530. if ( !NameFinished && (NameChar == '.')) {
  531. ULEN cchOffset; // in character counts
  532. for( cchOffset = NameOffset/sizeof(CHAR);
  533. cchOffset < NameLen;
  534. cchOffset ++) {
  535. if ( pszName[cchOffset] == '.') {
  536. ICanEatADot = TRUE;
  537. break;
  538. }
  539. } // for
  540. }
  541. if ( NameFinished || (NameChar != '.') || ICanEatADot) {
  542. //
  543. // Go ahead and consume this character.
  544. // Gives two options to move forward.
  545. //
  546. CurrentMatches[DestCount] = CurrentState;
  547. CurrentMatches[DestCount+1] = CurrentState+1;
  548. DestCount += 2;
  549. } else {
  550. //
  551. // We are at the period. We can only match zero
  552. // or more characters (ie. the epsilon transition)
  553. //
  554. CurrentMatches[DestCount++] = CurrentState+1;
  555. continue;
  556. }
  557. } // if ( ExprChar == DOS_STAR)
  558. //
  559. // The following expression characters all match by consuming
  560. // a character, thus force the expression, and thus state
  561. // move forward.
  562. //
  563. CurrentState += (ULEN)(sizeof(CHAR) *2);
  564. //
  565. // DOS_QM is the most complicated. If the name is finished,
  566. // we can match zero characters. If this name is a '.', we
  567. // don't match, but look at the next expression. Otherwise
  568. // we match a single character.
  569. //
  570. if ( ExprChar == ANSI_DOS_QM ) {
  571. if ( NameFinished || (NameChar == '.') ) {
  572. continue;
  573. }
  574. CurrentMatches[DestCount++] = CurrentState;
  575. break;
  576. }
  577. //
  578. // DOS_DOT can match either a period, or zero characters
  579. // beyond the end of the name
  580. //
  581. if ( ExprChar == ANSI_DOS_DOT) {
  582. if ( NameFinished) {
  583. continue;
  584. }
  585. if ( NameChar == '.') {
  586. CurrentMatches[DestCount++] = CurrentState;
  587. break;
  588. }
  589. }
  590. //
  591. // From this point on a name character is required to
  592. // even continue searching, let alone make a match.
  593. // So if Name is finished, stop.
  594. //
  595. if ( NameFinished) {
  596. break;
  597. }
  598. //
  599. // If the expression was a '?' we can match it once
  600. //
  601. if ( ExprChar == '?') {
  602. CurrentMatches[DestCount++] = CurrentState;
  603. break;
  604. }
  605. //
  606. // Finally, check if the expression char matches name char
  607. //
  608. if ( ExprChar == (CHAR ) (fIgnoreCase ?
  609. tolower(NameChar) : NameChar)
  610. ){
  611. CurrentMatches[DestCount++] = CurrentState;
  612. break;
  613. }
  614. //
  615. // The expression did not match, go look at the next
  616. // previous match
  617. //
  618. break;
  619. } // for matching from an old state.
  620. //
  621. // Prevent duplication in the destination array.
  622. //
  623. // Each of the arrays is montonically increasing and non-
  624. // duplicating, thus we skip over any source element in the src
  625. // array if we just added the same element to the destination
  626. // array. This guarentees non-duplication in the dest. array.
  627. //
  628. if ((SrcCount < MatchesCount) &&
  629. (PreviousDestCount < DestCount) ) {
  630. while ( SrcCount < MatchesCount &&
  631. PreviousDestCount < DestCount) {
  632. //
  633. // logic here is: by eliminating the states with
  634. // lesser number than current matched ==> we are
  635. // skipping over the smallest states from which
  636. // no match may be found.
  637. //
  638. if ( PreviousMatches[SrcCount] <
  639. CurrentMatches[PreviousDestCount] ) {
  640. SrcCount ++;
  641. }
  642. PreviousDestCount += 1;
  643. } // while
  644. }
  645. } // for each of old matches....
  646. //
  647. // If we found no matches in the just finished iteration, it's time
  648. // to bail.
  649. //
  650. if ( DestCount == 0 ) {
  651. if (AuxBuffer != NULL) {
  652. IF_DEBUG( DIR_LIST) {
  653. DBGPRINTF((DBG_CONTEXT, " Freeing %08x\n", AuxBuffer));
  654. }
  655. FREE( AuxBuffer );
  656. }
  657. return FALSE;
  658. }
  659. //
  660. // Swap the meaning the two arrays
  661. //
  662. {
  663. ULEN *Tmp;
  664. Tmp = PreviousMatches;
  665. PreviousMatches = CurrentMatches;
  666. CurrentMatches = Tmp;
  667. }
  668. MatchesCount = DestCount;
  669. } // for each char in Name, until name is finished.
  670. DBG_ASSERT(MatchesCount > 0);
  671. CurrentState = PreviousMatches[MatchesCount-1];
  672. if (AuxBuffer != NULL) {
  673. IF_DEBUG( DIR_LIST) {
  674. DBGPRINTF((DBG_CONTEXT, " Freeing %08x\n", AuxBuffer));
  675. }
  676. FREE( AuxBuffer );
  677. }
  678. return (BOOL ) ( CurrentState == MaxState);
  679. } // IsNameInRegExpressionA()
  680. /************************ End of File ***********************/