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.

10937 lines
257 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. strings.c
  5. Abstract:
  6. A number of string utilities useful for any project
  7. Author:
  8. Jim Schmidt (jimschm) 12-Sept-1996
  9. Revisions:
  10. ovidiut 12-Jan-2000 Added GetNodePatternMinMaxLevels,PatternIncludesPattern
  11. ovidiut 14-Sep-1999 Updated for new coding conventions and Win64 compliance
  12. marcw 2-Sep-1999 Moved over from Win9xUpg project.
  13. jimschm 08-Jul-1999 IsPatternMatchEx
  14. jimschm 07-Jan-1999 GetFileExtensionFromPath fixed again, added
  15. GetDotExtensionFromPath
  16. calinn 23-Sep-1998 GetFileExtensionFromPath bug fix
  17. calinn 29-Jan-1998 Fixed a bug in EnumNextMultiSz.
  18. calinn 11-Jan-1998 Added EnumFirstMultiSz and EnumNextMultiSz functions.
  19. marcw 15-Dec-1997 Added ExpandEnvironmentTextEx functions.
  20. marcw 14-Nov-1997 SlightJoinText revisions.
  21. jimschm 21-May-1997 AppendWack revisions
  22. marcw 24-Mar-1997 StringReplace functions.
  23. jimschm 14-Mar-1997 New critical section stuff, enhanced message resource
  24. routines, C runtime extensions, registry root utils
  25. jimschm 26-Nov-1996 Added message resource tools.
  26. mikeco 01-Jul-1997 Add FreeStringResourcePtr Fns
  27. mikeco 29-Sep-1997 IsLeadByte wrapper for IsDBCSLeadByte
  28. --*/
  29. #include "pch.h"
  30. //
  31. // Includes
  32. //
  33. #include "utilsp.h"
  34. #define DBG_STRINGS "Strings"
  35. //
  36. // Strings
  37. //
  38. // None
  39. //
  40. // Constants
  41. //
  42. // Error stack size (normally only one or two, so 32 is relatively huge)
  43. #define MAX_STACK 32
  44. #define WACK_REPLACE_CHAR 0x02
  45. #define DWORD_MAX 0xFFFFFFFFu
  46. //
  47. // Macros
  48. //
  49. // None
  50. //
  51. // Types
  52. //
  53. typedef enum {
  54. BEGIN_PATTERN,
  55. BEGIN_COMPOUND_PATTERN,
  56. BEGIN_PATTERN_EXPR,
  57. SAVE_EXACT_MATCH,
  58. SAVE_SEGMENT,
  59. LOOK_FOR_NUMBER,
  60. LOOK_FOR_INCLUDE,
  61. LOOK_FOR_EXCLUDE,
  62. ADVANCE_TO_END_OF_EXPR,
  63. PARSE_CHAR_EXPR_OR_END,
  64. SKIP_EXCLUDE_SET,
  65. CONDENSE_SET,
  66. PARSE_END_FOUND,
  67. SKIP_INCLUDE_SET,
  68. END_PATTERN_EXPR,
  69. PATTERN_DONE,
  70. PATTERN_ERROR
  71. } PATTERNSTATE;
  72. typedef struct {
  73. UINT char1;
  74. UINT char2;
  75. UINT result;
  76. } DHLIST, *PDHLIST;
  77. //
  78. // Globals
  79. //
  80. CHAR EscapedCharsA[] = "?*\020<>,^";
  81. WCHAR EscapedCharsW[] = L"?*\020<>,^";
  82. DWORD g_dwErrorStack[MAX_STACK];
  83. DWORD g_dwStackPos = 0;
  84. DHLIST g_DHList[] = {{0xB3, 0xDE, 0x8394},
  85. {0xB6, 0xDE, 0x834B},
  86. {0xB7, 0xDE, 0x834D},
  87. {0xB8, 0xDE, 0x834F},
  88. {0xB9, 0xDE, 0x8351},
  89. {0xBA, 0xDE, 0x8353},
  90. {0xBB, 0xDE, 0x8355},
  91. {0xBC, 0xDE, 0x8357},
  92. {0xBD, 0xDE, 0x8359},
  93. {0xBE, 0xDE, 0x835B},
  94. {0xBF, 0xDE, 0x835D},
  95. {0xC0, 0xDE, 0x835F},
  96. {0xC1, 0xDE, 0x8361},
  97. {0xC2, 0xDE, 0x8364},
  98. {0xC3, 0xDE, 0x8366},
  99. {0xC4, 0xDE, 0x8368},
  100. {0xCA, 0xDE, 0x836F},
  101. {0xCB, 0xDE, 0x8372},
  102. {0xCC, 0xDE, 0x8375},
  103. {0xCD, 0xDE, 0x8378},
  104. {0xCE, 0xDE, 0x837B},
  105. {0xCA, 0xDF, 0x8370},
  106. {0xCB, 0xDF, 0x8373},
  107. {0xCC, 0xDF, 0x8376},
  108. {0xCD, 0xDF, 0x8379},
  109. {0xCE, 0xDF, 0x837C},
  110. {0x00, 0x00, 0x0000}};
  111. extern OUR_CRITICAL_SECTION g_MessageCs; // in main.c
  112. extern PMHANDLE g_TextPool; // in main.c
  113. PGROWBUFFER g_LastAllocTable;
  114. //
  115. // Macro expansion list
  116. //
  117. // None
  118. //
  119. // Private function prototypes
  120. //
  121. BOOL
  122. pTestSetA (
  123. IN MBCHAR ch,
  124. IN PCSTR IncludeSet, OPTIONAL
  125. IN PCSTR ExcludeSet OPTIONAL
  126. );
  127. BOOL
  128. pTestSetW (
  129. IN WCHAR ch,
  130. IN PCWSTR IncludeSet, OPTIONAL
  131. IN PCWSTR ExcludeSet OPTIONAL
  132. );
  133. //
  134. // Macro expansion definition
  135. //
  136. // None
  137. //
  138. // Code
  139. //
  140. /*++
  141. Routine Description:
  142. StringCopy implements lstrcpyA and a UNICODE version. We don't use the Win32
  143. api because of speed and because we want to compile lint-free.
  144. Arguments:
  145. Destination - Receivies the string copy
  146. Source - Specifies the string to copy
  147. Return Value:
  148. None.
  149. --*/
  150. VOID
  151. StringCopyA (
  152. OUT PSTR Destination,
  153. IN PCSTR Source
  154. )
  155. {
  156. PCSTR current = Source;
  157. PCSTR end;
  158. while (*current) {
  159. *Destination++ = *current++;
  160. }
  161. //
  162. // Make sure DBCS string is properly terminated
  163. //
  164. end = current;
  165. current--;
  166. while (current >= Source) {
  167. if (!IsLeadByte (*current)) {
  168. //
  169. // destEnd is correct
  170. //
  171. break;
  172. }
  173. current--;
  174. }
  175. if (!((end - current) & 1)) {
  176. Destination--;
  177. }
  178. *Destination = 0;
  179. }
  180. VOID
  181. StringCopyW (
  182. OUT PWSTR Destination,
  183. IN PCWSTR Source
  184. )
  185. {
  186. while (*Source) {
  187. *Destination++ = *Source++;
  188. }
  189. *Destination = 0;
  190. }
  191. /*++
  192. Routine Description:
  193. StringCopyByteCount implements lstrcpynA and a UNICODE version. We don't
  194. use the Win32 api because of speed and because we want to compile lint-free.
  195. Arguments:
  196. Destination - Receivies the string copy
  197. Source - Specifies the string to copy
  198. Count - Specifies the maximum number of bytes to copy, including the
  199. nul terminator. If Count is zero, then not even a nul
  200. terminator is written.
  201. Return Value:
  202. None.
  203. --*/
  204. VOID
  205. StringCopyByteCountA (
  206. OUT PSTR Destination,
  207. IN PCSTR Source,
  208. IN UINT Count
  209. )
  210. {
  211. PCSTR end;
  212. PCSTR current;
  213. PSTR destEnd;
  214. if (Count >= sizeof (CHAR)) {
  215. current = Source;
  216. destEnd = Destination;
  217. end = (PCSTR) ((PBYTE) Source + Count - sizeof (CHAR));
  218. while (*current && current < end) {
  219. *destEnd++ = *current++;
  220. }
  221. //
  222. // If current has data left, we need to make sure a DBCS string
  223. // is properly terminated.
  224. //
  225. if (*current) {
  226. end = current;
  227. current--;
  228. while (current >= Source) {
  229. if (!IsLeadByte (*current)) {
  230. //
  231. // destEnd is correct
  232. //
  233. break;
  234. }
  235. current--;
  236. }
  237. if (!((end - current) & 1)) {
  238. destEnd--;
  239. }
  240. }
  241. *destEnd = 0;
  242. }
  243. }
  244. VOID
  245. StringCopyByteCountW (
  246. OUT PWSTR Destination,
  247. IN PCWSTR Source,
  248. IN UINT Count
  249. )
  250. {
  251. PCWSTR end;
  252. if (Count < sizeof (WCHAR)) {
  253. DEBUGMSG_IF ((
  254. Count != 0,
  255. DBG_WHOOPS,
  256. "Buffer passed to StringCopyByteCountW is a fraction of one character"
  257. ));
  258. return;
  259. }
  260. end = (PCWSTR) ((PBYTE) Source + Count - sizeof (WCHAR));
  261. while (Source < end) {
  262. *Destination++ = *Source++;
  263. }
  264. *Destination = 0;
  265. }
  266. VOID
  267. StringCopyByteCountABA (
  268. OUT PSTR Destination,
  269. IN PCSTR Start,
  270. IN PCSTR End,
  271. IN UINT MaxBytesToCopyIncNul
  272. )
  273. /*++
  274. Routine Description:
  275. StringCopyByteCountAB copies a string segment into a destination buffer,
  276. and limits the copy to a maximum buffer size. The return string is always
  277. nul-terminated, unless the buffer is too small to even hold a nul character.
  278. Arguments:
  279. Destination - Receives the string starting at Start and ending one
  280. character before End.
  281. Start - Specifies the start of the string.
  282. End - Specifies the first character not to copy. If End
  283. is equal or less than Start, then Destination is set
  284. to an empty string (assuming the buffer can hold at
  285. least one character)
  286. MaxBytesToCopyIncNul - Specifies the size of Destination, in bytes, and
  287. including the nul terminator.
  288. Return Value:
  289. None.
  290. --*/
  291. {
  292. INT width;
  293. #ifdef DEBUG
  294. PCSTR check;
  295. check = Start;
  296. while (check < End) {
  297. if (!(*check)) {
  298. DEBUGMSG ((DBG_WHOOPS, "StringCopyByteCountABA: Nul found between start and end"));
  299. break;
  300. }
  301. check++;
  302. }
  303. #endif
  304. width = (INT) (End - Start) + sizeof (CHAR);
  305. if (width > sizeof (CHAR)) {
  306. StringCopyByteCountA (Destination, Start, min ((UINT) width, MaxBytesToCopyIncNul));
  307. } else if (MaxBytesToCopyIncNul >= sizeof (CHAR)) {
  308. *Destination = 0;
  309. }
  310. }
  311. VOID
  312. StringCopyByteCountABW (
  313. OUT PWSTR Destination,
  314. IN PCWSTR Start,
  315. IN PCWSTR End,
  316. IN UINT MaxBytesToCopyIncNul
  317. )
  318. {
  319. INT width;
  320. #ifdef DEBUG
  321. PCWSTR check;
  322. check = Start;
  323. while (check < End) {
  324. if (!(*check)) {
  325. DEBUGMSG ((DBG_WHOOPS, "StringCopyByteCountABW: Nul found between start and end"));
  326. break;
  327. }
  328. check++;
  329. }
  330. #endif
  331. width = (INT) (End - Start) + sizeof (WCHAR);
  332. if (width > sizeof (WCHAR)) {
  333. StringCopyByteCountW (Destination, Start, min ((UINT) width, MaxBytesToCopyIncNul));
  334. } else if (MaxBytesToCopyIncNul >= sizeof (WCHAR)) {
  335. *Destination = 0;
  336. }
  337. }
  338. /*++
  339. Routine Description:
  340. AllocTextEx allocates a block of memory from the specified pool, or g_TextPool
  341. if no pool is specified, and is designated specifically for text processing.
  342. The g_TextPool is initialized when migutil.lib loads up, and there is 64K of
  343. guaranteed workspace, which will grow as necessary.
  344. Arguments:
  345. Pool - Specifies the pool to allocate memory from
  346. CountOfChars - Specifies the number of characters (not bytes) to allocate. The
  347. return pointer is a block of memory that can hold CountOfChars characters,
  348. weather they are SBCS, DBCS or UNICODE.
  349. Return Value:
  350. A pointer to the allocated memory, or NULL if the pool could not be expanded
  351. to hold the number of specified characters.
  352. --*/
  353. PSTR
  354. RealAllocTextExA (
  355. IN PMHANDLE Pool,
  356. IN UINT CountOfChars
  357. )
  358. {
  359. PSTR text;
  360. if (CountOfChars == 0) {
  361. return NULL;
  362. }
  363. if (!Pool) {
  364. Pool = g_TextPool;
  365. }
  366. text = PmGetAlignedMemory (Pool, CountOfChars * sizeof (CHAR) * 2);
  367. text [0] = 0;
  368. return text;
  369. }
  370. PWSTR
  371. RealAllocTextExW (
  372. IN PMHANDLE Pool,
  373. IN UINT CountOfChars
  374. )
  375. {
  376. PWSTR text;
  377. if (CountOfChars == 0) {
  378. return NULL;
  379. }
  380. if (!Pool) {
  381. Pool = g_TextPool;
  382. }
  383. text = PmGetAlignedMemory (Pool, CountOfChars * sizeof (WCHAR));
  384. text [0] = 0;
  385. return text;
  386. }
  387. /*++
  388. Routine Description:
  389. FreeText frees the memory allocated by AllocText. After all strings are freed,
  390. the block will be emptied but not deallocated.
  391. It is important NOT to leak memory, because a leak will cause the pool to
  392. expand, and non-empty pools cause memory fragmentation.
  393. Arguments:
  394. Text - Specifies the text to free, as returned from AllocText, DuplicateText,
  395. DuplicateTextEx, etc...
  396. Return Value:
  397. none
  398. --*/
  399. VOID
  400. FreeTextExA (
  401. IN PMHANDLE Pool, OPTIONAL
  402. IN PCSTR Text OPTIONAL
  403. )
  404. {
  405. if (Text) {
  406. if (!Pool) {
  407. Pool = g_TextPool;
  408. }
  409. PmReleaseMemory (Pool, (PVOID) Text);
  410. }
  411. }
  412. VOID
  413. FreeTextExW (
  414. IN PMHANDLE Pool, OPTIONAL
  415. IN PCWSTR Text OPTIONAL
  416. )
  417. {
  418. if (Text) {
  419. if (!Pool) {
  420. Pool = g_TextPool;
  421. }
  422. PmReleaseMemory (Pool, (PVOID) Text);
  423. }
  424. }
  425. /*++
  426. Routine Description:
  427. DuplicateTextEx duplicates a text string and allocates additional space a
  428. caller needs to complete its processing. Optionally, the caller receives
  429. a pointer to the nul of the duplicated string (to allow more efficient
  430. appends).
  431. Arguments:
  432. Text - Specifies the text to duplicate
  433. ExtraChars - Specifies the number of characters (not bytes) to allocate
  434. space for. The characters can be from the SBCS, DBCS or
  435. UNICODE character sets.
  436. NulChar - Receives a pointer to the nul at the end of the duplicated
  437. string. Use for fast appends.
  438. Return Value:
  439. A pointer to the duplicated and expanded string, or NULL if g_TextPool
  440. could not be expanded to fit the duplicated string and extra characters.
  441. --*/
  442. PSTR
  443. RealDuplicateTextExA (
  444. IN PMHANDLE Pool, OPTIONAL
  445. IN PCSTR Text,
  446. IN UINT ExtraChars,
  447. OUT PSTR *NulChar OPTIONAL
  448. )
  449. {
  450. PSTR buf;
  451. PSTR d;
  452. PCSTR s;
  453. buf = AllocTextExA (Pool, CharCountA (Text) + ExtraChars + 1);
  454. if (buf) {
  455. s = Text;
  456. d = buf;
  457. while (*s) {
  458. if (IsLeadByte (*s)) {
  459. *d++ = *s++;
  460. }
  461. *d++ = *s++;
  462. }
  463. *d = 0;
  464. if (NulChar) {
  465. *NulChar = d;
  466. }
  467. }
  468. return buf;
  469. }
  470. PWSTR
  471. RealDuplicateTextExW (
  472. IN PMHANDLE Pool, OPTIONAL
  473. IN PCWSTR Text,
  474. IN UINT ExtraChars,
  475. OUT PWSTR *NulChar OPTIONAL
  476. )
  477. {
  478. PWSTR buf;
  479. PWSTR d;
  480. PCWSTR s;
  481. buf = AllocTextExW (Pool, CharCountW (Text) + ExtraChars + 1);
  482. if (buf) {
  483. s = Text;
  484. d = buf;
  485. while (*s) {
  486. *d++ = *s++;
  487. }
  488. *d = 0;
  489. if (NulChar) {
  490. *NulChar = d;
  491. }
  492. }
  493. return buf;
  494. }
  495. /*++
  496. Routine Description:
  497. JoinText duplicates String1 and appends String2 to it delimited with the optional delimiterstring.
  498. Arguments:
  499. String1 - Specifies the text to duplciate
  500. String2 - Specifies the text to append to String1
  501. DelimiterString - Optionally specifies the string to place between string 1 and string 2.
  502. ExtraChars - Specifies the number of characters (not bytes) to allocate
  503. space for. The characters can be from the SBCS, DBCS or
  504. UNICODE character sets.
  505. NulChar - Receives a pointer to the nul at the end of the duplicated
  506. string. Use for fast appends.
  507. Return Value:
  508. A pointer to the duplicated string and extra characters.
  509. --*/
  510. PSTR
  511. RealJoinTextExA (
  512. IN PMHANDLE Pool, OPTIONAL
  513. IN PCSTR String1,
  514. IN PCSTR String2,
  515. IN PCSTR CenterString, OPTIONAL
  516. IN UINT ExtraChars,
  517. OUT PSTR *NulChar OPTIONAL
  518. )
  519. {
  520. PSTR buf;
  521. PSTR end;
  522. PSTR d;
  523. PCSTR s;
  524. buf = DuplicateTextExA (
  525. Pool,
  526. String1,
  527. CharCountA (String2) + ExtraChars + (CenterString ? CharCountA (CenterString) : 0),
  528. &end
  529. );
  530. if (buf) {
  531. d = end;
  532. if (CenterString) {
  533. s = CenterString;
  534. while (*s) {
  535. if (IsLeadByte (*s)) {
  536. *d++ = *s++;
  537. }
  538. *d++ = *s++;
  539. }
  540. }
  541. s = String2;
  542. while (*s) {
  543. if (IsLeadByte (*s)) {
  544. *d++ = *s++;
  545. }
  546. *d++ = *s++;
  547. }
  548. *d = 0;
  549. if (NulChar) {
  550. *NulChar = d;
  551. }
  552. }
  553. return buf;
  554. }
  555. PWSTR
  556. RealJoinTextExW (
  557. IN PMHANDLE Pool, OPTIONAL
  558. IN PCWSTR String1,
  559. IN PCWSTR String2,
  560. IN PCWSTR CenterString, OPTIONAL
  561. IN UINT ExtraChars,
  562. OUT PWSTR *NulChar OPTIONAL
  563. )
  564. {
  565. PWSTR buf;
  566. PWSTR end;
  567. PCWSTR s;
  568. PWSTR d;
  569. buf = DuplicateTextExW (
  570. Pool,
  571. String1,
  572. CharCountW (String2) + ExtraChars + (CenterString ? CharCountW(CenterString) : 0),
  573. &end
  574. );
  575. if (buf) {
  576. d = end;
  577. if (CenterString) {
  578. s = CenterString;
  579. while (*s) {
  580. *d++ = *s++;
  581. }
  582. }
  583. s = String2;
  584. while (*s) {
  585. *d++ = *s++;
  586. }
  587. *d = 0;
  588. if (NulChar) {
  589. *NulChar = d;
  590. }
  591. }
  592. return buf;
  593. }
  594. /*++
  595. Routine Description:
  596. ExpandEnvironmentTextEx takes a block of text containing zero or more environment variables
  597. (encoded in %'s) and returns the text with the environment variables expanded. The function
  598. also allows the caller to specify additional environment variables in an array and will use
  599. these variables before calling GetEnvironmentVariable.
  600. The returned text is allocated out of the Text pool and should be freed using FreeText().
  601. Arguments:
  602. InString - The string containing environement variables to be processed.
  603. ExtraVars - Optional var pointing to an array of environment variables to be used to supersede
  604. or suppliment the system environment variables. Even entries in the list are the
  605. names of environment variables, odd entries there values.
  606. (e.g. {"name1","value1","name2","value2",...}
  607. Return Value:
  608. An expanded string.
  609. --*/
  610. PWSTR
  611. RealExpandEnvironmentTextExW (
  612. IN PCWSTR InString,
  613. IN PCWSTR * ExtraVars OPTIONAL
  614. )
  615. {
  616. PWSTR rString = NULL;
  617. PWSTR newString = NULL;
  618. PWSTR envName = NULL;
  619. PWSTR envValue = NULL;
  620. BOOL inSubstitution = FALSE;
  621. BOOL ignoreNextPercent = FALSE;
  622. BOOL errorOccurred = FALSE;
  623. BOOL foundValue = FALSE;
  624. BOOL freeValue = FALSE;
  625. PCWSTR nextPercent = NULL;
  626. PCWSTR source = NULL;
  627. PCWSTR savedSource = NULL;
  628. UINT maxSize = 0;
  629. UINT curSize = 0;
  630. UINT index = 0;
  631. UINT size = 0;
  632. if (!InString) {
  633. return NULL;
  634. }
  635. if (*InString == 0) {
  636. return DuplicateTextW (InString);
  637. }
  638. //
  639. // Set source to the start of InString to begin with...
  640. //
  641. source = InString;
  642. __try {
  643. while (*source) {
  644. //
  645. // Reallocate the string if necessary. We assume that most strings
  646. // are smaller than 1024 chars and that we will therefore only rarely
  647. // reallocate a string.
  648. //
  649. if (curSize + 3 > maxSize) {
  650. maxSize += 1024;
  651. newString = AllocTextW (maxSize);
  652. if (!newString) {
  653. DEBUGMSG((DBG_ERROR,"ExpandEnvironmentTextEx: Memory Error!"));
  654. errorOccurred = TRUE;
  655. __leave;
  656. }
  657. if (rString) {
  658. //lint -e(671)
  659. CopyMemory (newString, rString, (SIZE_T) ((UINT)curSize * sizeof(WCHAR)));
  660. FreeTextW(rString);
  661. }
  662. rString = newString;
  663. }
  664. //
  665. // if we find a percent sign, and we are not currently expanding
  666. // an environment variable (or copying an empty set of %'s),
  667. // then we have probably found an environment variable. Attempt
  668. // to expand it.
  669. //
  670. if (*source == L'%' && !inSubstitution) {
  671. if (ignoreNextPercent) {
  672. ignoreNextPercent = FALSE;
  673. }
  674. else {
  675. ignoreNextPercent = FALSE;
  676. nextPercent = wcschr(source + 1,L'%');
  677. if (nextPercent == source + 1) {
  678. //
  679. // We found two consecutive %s in this string. We'll ignore them and simply copy them as
  680. // normal text.
  681. //
  682. ignoreNextPercent = TRUE;
  683. DEBUGMSGW((DBG_WARNING,"ExpandEnvironmentTextEx: Empty Environment variable in %s. Ignoring.",InString));
  684. }
  685. else if (nextPercent) {
  686. //
  687. // Create a variable to hold the envName.
  688. //
  689. envName = AllocTextW(nextPercent - source);
  690. StringCopyByteCountABW (
  691. envName,
  692. source + 1,
  693. nextPercent,
  694. (UINT) ((UBINT)nextPercent - (UBINT)source)
  695. );
  696. //
  697. // Try to find the variable.
  698. //
  699. foundValue = FALSE;
  700. freeValue = FALSE;
  701. if (ExtraVars) {
  702. //
  703. // Search through the list of extra vars passed in by the caller.
  704. // Even entries of this list are env var names. Odd entries are env values.
  705. // {envname1,envvalue1,envname2,envvalue2,...}
  706. //
  707. index = 0;
  708. while (ExtraVars[index]) {
  709. if (StringIMatchW(ExtraVars[index],envName) && ExtraVars[index + 1]) {
  710. foundValue = TRUE;
  711. envValue = (PWSTR) ExtraVars[index + 1];
  712. break;
  713. }
  714. index +=2;
  715. }
  716. }
  717. if (!foundValue) {
  718. //
  719. // Still haven't found the environment variable. Use GetEnvironmentString.
  720. //
  721. //
  722. size = GetEnvironmentVariableW(envName,NULL,0);
  723. if (!size) {
  724. errorOccurred = TRUE;
  725. DEBUGMSGW((DBG_WARNING,"ExpandEnvironmentTextEx: Environment variable %s not found!",envName));
  726. } else {
  727. //
  728. // Create a buffer large enough to hold this value and copy it in.
  729. //
  730. envValue = AllocTextW(size);
  731. if ((size - 1) != GetEnvironmentVariableW(envName,envValue,size)) {
  732. errorOccurred = TRUE;
  733. DEBUGMSGW((DBG_ERROR,"ExpandEnvironmentTextEx: Error from GetEnvironmentVariable."));
  734. }
  735. else {
  736. foundValue = TRUE;
  737. }
  738. freeValue = TRUE;
  739. }
  740. }
  741. if (foundValue) {
  742. //
  743. // Ok, we have a valid environment value. Need to copy this data over.
  744. // To do this, we update and save the current source into old source, set source = to the envValue,
  745. // and set the inSubstitution value so that we don't attempt to expand any percents within
  746. // the value.
  747. //
  748. savedSource = nextPercent + 1;
  749. source = envValue;
  750. inSubstitution = TRUE;
  751. }
  752. else {
  753. DEBUGMSGW ((DBG_WARNING, "ExpandEnvironmentTextEx: No Environment variable found for %s.", envName));
  754. ignoreNextPercent = TRUE;
  755. }
  756. //
  757. // We are done with the environment name at this time, so clean it up.
  758. //
  759. FreeTextW(envName);
  760. envName = NULL;
  761. }
  762. ELSE_DEBUGMSGW((DBG_WARNING,"ExpandEnvironmentTextEx: No matching percent found in %s. Ignoring.",InString));
  763. }
  764. }
  765. //
  766. // Copy over the current character.
  767. //
  768. rString[curSize++] = *source++; //lint !e613
  769. if (!*source) {
  770. if (inSubstitution) {
  771. //
  772. // The source for the environment variable is fully copied.
  773. // restore the old source.
  774. //
  775. inSubstitution = FALSE;
  776. source = savedSource;
  777. if (!*source) { //lint !e613
  778. rString[curSize] = 0; //lint !e613
  779. }
  780. if (freeValue) {
  781. FreeTextW(envValue);
  782. freeValue = FALSE;
  783. }
  784. envValue = NULL;
  785. }
  786. else {
  787. rString[curSize] = 0; //lint !e613
  788. }
  789. }
  790. }
  791. } //lint !e613
  792. __finally {
  793. DEBUGMSGW_IF (( errorOccurred, DBG_WARNING, "ExpandEnvironmentText: Some errors occurred while processing %s = %s.", InString, rString ? rString : L"NULL"));
  794. if (envName) {
  795. FreeTextW(envName);
  796. }
  797. if (envValue && freeValue) {
  798. FreeTextW(envValue);
  799. }
  800. }
  801. return rString;
  802. }
  803. PSTR
  804. RealExpandEnvironmentTextExA (
  805. IN PCSTR InString,
  806. IN PCSTR * ExtraVars OPTIONAL
  807. )
  808. {
  809. PSTR rString = NULL;
  810. PSTR newString = NULL;
  811. PSTR envName = NULL;
  812. PSTR envValue = NULL;
  813. BOOL inSubstitution = FALSE;
  814. BOOL ignoreNextPercent = FALSE;
  815. BOOL errorOccurred = FALSE;
  816. BOOL foundValue = FALSE;
  817. BOOL freeValue = FALSE;
  818. PCSTR nextPercent = NULL;
  819. PCSTR source = NULL;
  820. PCSTR savedSource = NULL;
  821. UINT maxSize = 0;
  822. UINT curSize = 0;
  823. UINT index = 0;
  824. UINT size = 0;
  825. if (!InString) {
  826. return NULL;
  827. }
  828. if (*InString == 0) {
  829. return DuplicateTextA (InString);
  830. }
  831. //
  832. // Set source to the start of InString to begin with...
  833. //
  834. source = InString;
  835. __try {
  836. while (*source) {
  837. //
  838. // Reallocate the string if necessary. We assume that most strings
  839. // are smaller than 1024 chars and that we will therefore only rarely
  840. // reallocate a string.
  841. //
  842. if (curSize + 3 > maxSize) {
  843. maxSize += 1024;
  844. newString = AllocTextA (maxSize);
  845. if (rString) {
  846. CopyMemory (newString, rString, curSize * sizeof(CHAR)); //lint !e671
  847. FreeTextA(rString);
  848. }
  849. rString = newString;
  850. }
  851. //
  852. // if we find a percent sign, and we are not currently expanding
  853. // an environment variable (or copying an empty set of %'s),
  854. // then we have probably found an environment variable. Attempt
  855. // to expand it.
  856. //
  857. if (!IsLeadByte(*source) && *source == '%' && !inSubstitution) {
  858. if (ignoreNextPercent) {
  859. ignoreNextPercent = FALSE;
  860. }
  861. else {
  862. ignoreNextPercent = FALSE;
  863. nextPercent = _mbschr(source + 1,'%');
  864. if (nextPercent == source + 1) {
  865. //
  866. // We found two consecutive %s in this string. We'll ignore them and simply copy them as
  867. // normal text.
  868. //
  869. ignoreNextPercent = TRUE;
  870. DEBUGMSGA((DBG_WARNING,"ExpandEnvironmentTextEx: Empty Environment variable in %s. Ignoring.",InString));
  871. }
  872. else if (nextPercent) {
  873. //
  874. // Create a variable to hold the envName.
  875. //
  876. envName = AllocTextA(nextPercent - source);
  877. StringCopyABA (envName, source+1, nextPercent);
  878. //
  879. // Try to find the variable.
  880. //
  881. foundValue = FALSE;
  882. freeValue = FALSE;
  883. if (ExtraVars) {
  884. //
  885. // Search through the list of extra vars passed in by the caller.
  886. // Even entries of this list are env var names. Odd entries are env values.
  887. // {envname1,envvalue1,envname2,envvalue2,...}
  888. //
  889. index = 0;
  890. while (ExtraVars[index]) {
  891. if (StringIMatch(ExtraVars[index],envName) && ExtraVars[index + 1]) {
  892. foundValue = TRUE;
  893. envValue = (PSTR) ExtraVars[index + 1];
  894. break;
  895. }
  896. index +=2;
  897. }
  898. }
  899. if (!foundValue) {
  900. //
  901. // Still haven't found the environment variable. Use GetEnvironmentString.
  902. //
  903. //
  904. size = GetEnvironmentVariableA(envName,NULL,0);
  905. if (!size) {
  906. errorOccurred = TRUE;
  907. DEBUGMSGA((DBG_WARNING,"ExpandEnvironmentTextEx: Environment variable %s not found!",envName));
  908. }
  909. else {
  910. //
  911. // Create a buffer large enough to hold this value and copy it in.
  912. //
  913. envValue = AllocTextA(size);
  914. freeValue = TRUE;
  915. if ((size - 1) != GetEnvironmentVariableA(envName,envValue,size)) {
  916. errorOccurred = TRUE;
  917. DEBUGMSGA((DBG_ERROR,"ExpandEnvironmentTextEx: Error from GetEnvironmentVariable."));
  918. }
  919. else {
  920. foundValue = TRUE;
  921. }
  922. }
  923. }
  924. if (foundValue) {
  925. //
  926. // Ok, we have a valid environment value. Need to copy this data over.
  927. // To do this, we update and save the current source into old source, set source = to the envValue,
  928. // and set the inSubstitution value so that we don't attempt to expand any percents within
  929. // the value.
  930. //
  931. savedSource = nextPercent + 1;
  932. source = envValue;
  933. inSubstitution = TRUE;
  934. }
  935. else {
  936. DEBUGMSGA ((DBG_WARNING, "ExpandEnvironmentTextEx: No Environment variable found for %s.", envName));
  937. ignoreNextPercent = TRUE;
  938. }
  939. //
  940. // We are done with the environment name at this time, so clean it up.
  941. //
  942. FreeTextA(envName);
  943. envName = NULL;
  944. }
  945. ELSE_DEBUGMSGA((DBG_WARNING,"ExpandEnvironmentTextEx: No matching percent found in %s. Ignoring.",InString));
  946. }
  947. }
  948. //
  949. // Copy over the current character.
  950. //
  951. if (IsLeadByte(*source)) { //lint !e613
  952. rString[curSize++] = *source++; //lint !e613
  953. }
  954. rString[curSize++] = *source++; //lint !e613
  955. if (!*source) {
  956. if (inSubstitution) {
  957. //
  958. // The source for the environment variable is fully copied.
  959. // restore the old source.
  960. //
  961. inSubstitution = FALSE;
  962. source = savedSource;
  963. if (!*source) { //lint !e613
  964. rString[curSize] = 0; //lint !e613
  965. }
  966. if (freeValue) {
  967. FreeTextA(envValue);
  968. freeValue = FALSE;
  969. }
  970. envValue = NULL;
  971. }
  972. else {
  973. rString[curSize] = 0; //lint !e613
  974. }
  975. }
  976. }
  977. } //lint !e613
  978. __finally {
  979. DEBUGMSGA_IF (( errorOccurred, DBG_WARNING, "ExpandEnvironmentText: Some errors occurred while processing %s = %s.", InString, rString ? rString : "NULL"));
  980. if (envName) {
  981. FreeTextA(envName);
  982. }
  983. if (envValue && freeValue) {
  984. FreeTextA(envValue);
  985. }
  986. }
  987. return rString;
  988. }
  989. /*++
  990. Routine Description:
  991. AppendWack adds a backslash to the end of any string, unless the string
  992. already ends in a backslash.
  993. AppendDosWack adds a backslash, but only if the path does not already
  994. end in a backslash or colon. AppendWack supports DOS naming
  995. conventions: it does not append a back-slash if the path is empty,
  996. ends in a colon or if it ends in a back-slash already.
  997. AppendUncWack supports UNC naming conventions: it does not append a
  998. backslash if the path is empty or if it ends in a backslash already.
  999. AppendPathWack supports both DOS and UNC naming conventions, and uses the
  1000. UNC naming convention if the string starts with double-wacks.
  1001. Arguments:
  1002. Str - A buffer that holds the path, plus additional space for another
  1003. backslash.
  1004. Return Value:
  1005. none
  1006. --*/
  1007. PSTR
  1008. AppendWackA (
  1009. IN PSTR Str
  1010. )
  1011. {
  1012. PCSTR last;
  1013. if (!Str)
  1014. return Str;
  1015. last = Str;
  1016. while (*Str) {
  1017. last = Str;
  1018. Str = _mbsinc (Str);
  1019. }
  1020. if (*last != '\\') {
  1021. *Str = '\\';
  1022. Str++;
  1023. *Str = 0;
  1024. }
  1025. return Str;
  1026. }
  1027. PWSTR
  1028. AppendWackW (
  1029. IN PWSTR Str
  1030. )
  1031. {
  1032. PCWSTR last;
  1033. if (!Str)
  1034. return Str;
  1035. if (*Str) {
  1036. Str = GetEndOfStringW (Str);
  1037. last = Str - 1;
  1038. } else {
  1039. last = Str;
  1040. }
  1041. if (*last != '\\') {
  1042. *Str = L'\\';
  1043. Str++;
  1044. *Str = 0;
  1045. }
  1046. return Str;
  1047. }
  1048. PSTR
  1049. AppendDosWackA (
  1050. IN PSTR Str
  1051. )
  1052. {
  1053. PCSTR last;
  1054. if (!Str || !(*Str))
  1055. return Str;
  1056. do {
  1057. last = Str;
  1058. Str = _mbsinc (Str);
  1059. } while (*Str);
  1060. if (*last != '\\' && *last != ':') {
  1061. *Str = '\\';
  1062. Str++;
  1063. *Str = 0;
  1064. }
  1065. return Str;
  1066. }
  1067. PWSTR
  1068. AppendDosWackW (
  1069. IN PWSTR Str
  1070. )
  1071. {
  1072. PWSTR last;
  1073. if (!Str || !(*Str))
  1074. return Str;
  1075. Str = GetEndOfStringW (Str);
  1076. last = Str - 1;
  1077. if (*last != L'\\' && *last != L':') {
  1078. *Str = L'\\';
  1079. Str++;
  1080. *Str = 0;
  1081. }
  1082. return Str;
  1083. }
  1084. PSTR
  1085. AppendUncWackA (
  1086. IN PSTR Str
  1087. )
  1088. {
  1089. PCSTR last;
  1090. if (!Str || !(*Str))
  1091. return Str;
  1092. do {
  1093. last = Str;
  1094. Str = _mbsinc (Str);
  1095. } while (*Str);
  1096. if (*last != '\\') {
  1097. *Str = '\\';
  1098. Str++;
  1099. *Str = 0;
  1100. }
  1101. return Str;
  1102. }
  1103. PWSTR
  1104. AppendUncWackW (
  1105. IN PWSTR Str
  1106. )
  1107. {
  1108. PWSTR last;
  1109. if (!Str || !(*Str))
  1110. return Str;
  1111. Str = GetEndOfStringW (Str);
  1112. last = Str - 1;
  1113. if (*last != L'\\') {
  1114. *Str = L'\\';
  1115. Str++;
  1116. *Str = 0;
  1117. }
  1118. return Str;
  1119. }
  1120. PSTR
  1121. AppendPathWackA (
  1122. IN PSTR Str
  1123. )
  1124. {
  1125. if (!Str) {
  1126. return Str;
  1127. }
  1128. if (Str[0] == '\\' && Str[1] == '\\') {
  1129. return AppendUncWackA (Str);
  1130. }
  1131. return AppendDosWackA (Str);
  1132. }
  1133. PWSTR
  1134. AppendPathWackW (
  1135. IN PWSTR Str
  1136. )
  1137. {
  1138. if (!Str) {
  1139. return Str;
  1140. }
  1141. if (Str[0] == L'\\' && Str[1] == L'\\') {
  1142. return AppendUncWackW (Str);
  1143. }
  1144. return AppendDosWackW (Str);
  1145. }
  1146. DWORD
  1147. pGetStringsTotalSizeA (
  1148. IN va_list args
  1149. )
  1150. {
  1151. DWORD size = 0;
  1152. PCSTR source;
  1153. for (source = va_arg(args, PCSTR); source != NULL; source = va_arg(args, PCSTR)) {
  1154. size += ByteCountA (source) + DWSIZEOF(CHAR);
  1155. }
  1156. return size;
  1157. }
  1158. DWORD
  1159. pGetStringsTotalSizeW (
  1160. IN va_list args
  1161. )
  1162. {
  1163. DWORD size = 0;
  1164. PCWSTR source;
  1165. for (source = va_arg(args, PCWSTR); source != NULL; source = va_arg(args, PCWSTR)) {
  1166. size += ByteCountW (source) + DWSIZEOF(WCHAR);
  1167. }
  1168. return size;
  1169. }
  1170. PSTR
  1171. pJoinPathsInBufferA (
  1172. OUT PSTR Buffer,
  1173. IN va_list args
  1174. )
  1175. {
  1176. PSTR end;
  1177. PSTR endMinusOne;
  1178. PCSTR source;
  1179. PCSTR p;
  1180. INT counter;
  1181. *Buffer = 0;
  1182. counter = 0;
  1183. p = end = Buffer;
  1184. for (source = va_arg(args, PCSTR); source != NULL; source = va_arg(args, PCSTR)) {
  1185. if (counter > 0) {
  1186. endMinusOne = _mbsdec2 (p, end);
  1187. if (endMinusOne) {
  1188. if (_mbsnextc (source) == '\\') {
  1189. if (_mbsnextc (endMinusOne) == '\\') {
  1190. source++;
  1191. }
  1192. } else {
  1193. if (_mbsnextc (endMinusOne) != '\\') {
  1194. *end = '\\';
  1195. end++;
  1196. *end = 0;
  1197. }
  1198. }
  1199. }
  1200. }
  1201. if (*source) {
  1202. p = end;
  1203. end = StringCatA (end, source);
  1204. }
  1205. counter++;
  1206. }
  1207. return end;
  1208. }
  1209. PWSTR
  1210. pJoinPathsInBufferW (
  1211. OUT PWSTR Buffer,
  1212. IN va_list args
  1213. )
  1214. {
  1215. PWSTR end;
  1216. PWSTR endMinusOne;
  1217. PCWSTR source;
  1218. PCWSTR p;
  1219. INT counter;
  1220. *Buffer = 0;
  1221. counter = 0;
  1222. p = end = Buffer;
  1223. for (source = va_arg(args, PCWSTR); source != NULL; source = va_arg(args, PCWSTR)) {
  1224. if (counter > 0) {
  1225. endMinusOne = end > p ? end - 1 : NULL;
  1226. if (endMinusOne) {
  1227. if (*source == L'\\') {
  1228. if (*endMinusOne == L'\\') {
  1229. source++;
  1230. }
  1231. } else {
  1232. if (*endMinusOne != L'\\') {
  1233. *end = L'\\';
  1234. end++;
  1235. *end = 0;
  1236. }
  1237. }
  1238. }
  1239. }
  1240. if (*source) {
  1241. p = end;
  1242. end = StringCatW (end, source);
  1243. }
  1244. counter++;
  1245. }
  1246. return end;
  1247. }
  1248. PSTR
  1249. _cdecl
  1250. RealJoinPathsInPoolExA (
  1251. IN PMHANDLE Pool, OPTIONAL
  1252. ...
  1253. )
  1254. {
  1255. DWORD size;
  1256. PSTR dest;
  1257. va_list args;
  1258. if (!Pool) {
  1259. Pool = g_PathsPool;
  1260. }
  1261. va_start (args, Pool);
  1262. size = pGetStringsTotalSizeA (args);
  1263. va_end (args);
  1264. if (size == 0) {
  1265. return NULL;
  1266. }
  1267. dest = (PSTR) PmGetAlignedMemory (Pool, size);
  1268. MYASSERT (dest);
  1269. va_start (args, Pool);
  1270. pJoinPathsInBufferA (dest, args);
  1271. va_end (args);
  1272. return dest;
  1273. }
  1274. PWSTR
  1275. _cdecl
  1276. RealJoinPathsInPoolExW (
  1277. IN PMHANDLE Pool, OPTIONAL
  1278. ...
  1279. )
  1280. {
  1281. DWORD size;
  1282. PWSTR dest;
  1283. va_list args;
  1284. if (!Pool) {
  1285. Pool = g_PathsPool;
  1286. }
  1287. va_start (args, Pool);
  1288. size = pGetStringsTotalSizeW (args);
  1289. va_end (args);
  1290. if (size == 0) {
  1291. return NULL;
  1292. }
  1293. dest = (PWSTR) PmGetAlignedMemory (Pool, size);
  1294. MYASSERT (dest);
  1295. va_start (args, Pool);
  1296. pJoinPathsInBufferW (dest, args);
  1297. va_end (args);
  1298. return dest;
  1299. }
  1300. BOOL
  1301. JoinPathsExA (
  1302. IN OUT PGROWBUFFER Gb,
  1303. ...
  1304. )
  1305. {
  1306. PSTR end;
  1307. DWORD size;
  1308. va_list args;
  1309. MYASSERT (Gb);
  1310. if (!Gb) {
  1311. return FALSE;
  1312. }
  1313. va_start (args, Gb);
  1314. size = pGetStringsTotalSizeA (args);
  1315. va_end (args);
  1316. if (size == 0) {
  1317. return FALSE;
  1318. }
  1319. end = (PSTR) GbGrow (Gb, size);
  1320. if (!end) {
  1321. return FALSE;
  1322. }
  1323. va_start (args, Gb);
  1324. end = pJoinPathsInBufferA (end, args);
  1325. va_end (args);
  1326. //
  1327. // adjust Gb->End if resulting path is actually shorter than predicted
  1328. //
  1329. MYASSERT ((PBYTE)end >= Gb->Buf && (PBYTE)(end + 1) <= Gb->Buf + Gb->End);
  1330. Gb->End = (DWORD)((PBYTE)(end + 1) - Gb->Buf);
  1331. return TRUE;
  1332. }
  1333. BOOL
  1334. JoinPathsExW (
  1335. IN OUT PGROWBUFFER Gb,
  1336. ...
  1337. )
  1338. {
  1339. PWSTR end;
  1340. DWORD size;
  1341. va_list args;
  1342. MYASSERT (Gb);
  1343. if (!Gb) {
  1344. return FALSE;
  1345. }
  1346. va_start (args, Gb);
  1347. size = pGetStringsTotalSizeW (args);
  1348. va_end (args);
  1349. if (size == 0) {
  1350. return FALSE;
  1351. }
  1352. end = (PWSTR) GbGrow (Gb, size);
  1353. if (!end) {
  1354. return FALSE;
  1355. }
  1356. va_start (args, Gb);
  1357. end = pJoinPathsInBufferW (end, args);
  1358. va_end (args);
  1359. //
  1360. // adjust Gb->End if resulting path is actually shorter than predicted
  1361. //
  1362. MYASSERT ((PBYTE)end >= Gb->Buf && (PBYTE)(end + 1) <= Gb->Buf + Gb->End);
  1363. Gb->End = (DWORD)((PBYTE)(end + 1) - Gb->Buf);
  1364. return TRUE;
  1365. }
  1366. PSTR
  1367. pBuildPathInBufferA (
  1368. OUT PSTR Buffer,
  1369. IN va_list args
  1370. )
  1371. {
  1372. PCSTR source;
  1373. INT counter;
  1374. *Buffer = 0;
  1375. counter = 0;
  1376. for (source = va_arg(args, PCSTR); source != NULL; source = va_arg(args, PCSTR)) {
  1377. if (counter > 0) {
  1378. *Buffer++ = '\\';
  1379. *Buffer = 0;
  1380. }
  1381. Buffer = StringCatA (Buffer, source);
  1382. counter++;
  1383. }
  1384. return Buffer;
  1385. }
  1386. PWSTR
  1387. pBuildPathInBufferW (
  1388. OUT PWSTR Buffer,
  1389. IN va_list args
  1390. )
  1391. {
  1392. PCWSTR source;
  1393. INT counter;
  1394. *Buffer = 0;
  1395. counter = 0;
  1396. for (source = va_arg(args, PCWSTR); source != NULL; source = va_arg(args, PCWSTR)) {
  1397. if (counter > 0) {
  1398. *Buffer++ = L'\\';
  1399. *Buffer = 0;
  1400. }
  1401. Buffer = StringCatW (Buffer, source);
  1402. counter++;
  1403. }
  1404. return Buffer;
  1405. }
  1406. DWORD
  1407. BuildPathA (
  1408. OUT PSTR Buffer, OPTIONAL
  1409. IN DWORD SizeInBytes, OPTIONAL
  1410. ...
  1411. )
  1412. {
  1413. PSTR end;
  1414. DWORD size;
  1415. va_list args;
  1416. va_start (args, SizeInBytes);
  1417. size = pGetStringsTotalSizeA (args);
  1418. va_end (args);
  1419. if (!size) {
  1420. //
  1421. // no args
  1422. //
  1423. return 0;
  1424. }
  1425. if (!Buffer) {
  1426. return size;
  1427. }
  1428. if (SizeInBytes < size) {
  1429. //
  1430. // buffer too small
  1431. //
  1432. return 0;
  1433. }
  1434. va_start (args, SizeInBytes);
  1435. end = pBuildPathInBufferA (Buffer, args);
  1436. va_end (args);
  1437. MYASSERT (size == (DWORD)((PBYTE)(end + 1) - (PBYTE)Buffer));
  1438. return size;
  1439. }
  1440. DWORD
  1441. BuildPathW (
  1442. OUT PWSTR Buffer, OPTIONAL
  1443. IN DWORD SizeInBytes, OPTIONAL
  1444. ...
  1445. )
  1446. {
  1447. PWSTR end;
  1448. DWORD size;
  1449. va_list args;
  1450. va_start (args, SizeInBytes);
  1451. size = pGetStringsTotalSizeW (args);
  1452. va_end (args);
  1453. if (!size) {
  1454. //
  1455. // no args
  1456. //
  1457. return 0;
  1458. }
  1459. if (!Buffer) {
  1460. return size;
  1461. }
  1462. if (SizeInBytes < size) {
  1463. //
  1464. // buffer too small
  1465. //
  1466. return 0;
  1467. }
  1468. va_start (args, SizeInBytes);
  1469. end = pBuildPathInBufferW (Buffer, args);
  1470. va_end (args);
  1471. MYASSERT (size == (DWORD)((PBYTE)(end + 1) - (PBYTE)Buffer));
  1472. return size;
  1473. }
  1474. BOOL
  1475. BuildPathExA (
  1476. IN OUT PGROWBUFFER Gb,
  1477. ...
  1478. )
  1479. {
  1480. PSTR end;
  1481. DWORD size;
  1482. va_list args;
  1483. MYASSERT (Gb);
  1484. if (!Gb) {
  1485. return FALSE;
  1486. }
  1487. va_start (args, Gb);
  1488. size = pGetStringsTotalSizeA (args);
  1489. va_end (args);
  1490. if (!size) {
  1491. //
  1492. // no args
  1493. //
  1494. return FALSE;
  1495. }
  1496. end = (PSTR) GbGrow (Gb, size);
  1497. if (!end) {
  1498. return FALSE;
  1499. }
  1500. va_start (args, Gb);
  1501. end = pBuildPathInBufferA (end, args);
  1502. va_end (args);
  1503. MYASSERT ((PBYTE)(end + 1) == Gb->Buf + Gb->End);
  1504. return (size != 0);
  1505. }
  1506. BOOL
  1507. BuildPathExW (
  1508. IN OUT PGROWBUFFER Gb,
  1509. ...
  1510. )
  1511. {
  1512. PWSTR end;
  1513. DWORD size;
  1514. va_list args;
  1515. MYASSERT (Gb);
  1516. if (!Gb) {
  1517. return FALSE;
  1518. }
  1519. va_start (args, Gb);
  1520. size = pGetStringsTotalSizeW (args);
  1521. va_end (args);
  1522. if (!size) {
  1523. //
  1524. // no args
  1525. //
  1526. return FALSE;
  1527. }
  1528. end = (PWSTR) GbGrow (Gb, size);
  1529. if (!end) {
  1530. return FALSE;
  1531. }
  1532. va_start (args, Gb);
  1533. end = pBuildPathInBufferW (end, args);
  1534. va_end (args);
  1535. MYASSERT ((PBYTE)(end + 1) == Gb->Buf + Gb->End);
  1536. return (size != 0);
  1537. }
  1538. PSTR
  1539. RealBuildPathInPoolA (
  1540. IN PMHANDLE Pool, OPTIONAL
  1541. ...
  1542. )
  1543. {
  1544. PSTR dest;
  1545. DWORD size;
  1546. va_list args;
  1547. if (!Pool) {
  1548. Pool = g_PathsPool;
  1549. }
  1550. va_start (args, Pool);
  1551. size = pGetStringsTotalSizeA (args);
  1552. va_end (args);
  1553. if (!size) {
  1554. //
  1555. // no args
  1556. //
  1557. return NULL;
  1558. }
  1559. dest = (PSTR) PmGetAlignedMemory (Pool, size);
  1560. MYASSERT (dest);
  1561. va_start (args, Pool);
  1562. pBuildPathInBufferA (dest, args);
  1563. va_end (args);
  1564. return dest;
  1565. }
  1566. PWSTR
  1567. RealBuildPathInPoolW (
  1568. IN PMHANDLE Pool, OPTIONAL
  1569. ...
  1570. )
  1571. {
  1572. PWSTR dest;
  1573. DWORD size;
  1574. va_list args;
  1575. if (!Pool) {
  1576. Pool = g_PathsPool;
  1577. }
  1578. va_start (args, Pool);
  1579. size = pGetStringsTotalSizeW (args);
  1580. va_end (args);
  1581. if (!size) {
  1582. //
  1583. // no args
  1584. //
  1585. return NULL;
  1586. }
  1587. dest = (PWSTR) PmGetAlignedMemory (Pool, size);
  1588. MYASSERT (dest);
  1589. va_start (args, Pool);
  1590. pBuildPathInBufferW (dest, args);
  1591. va_end (args);
  1592. return dest;
  1593. }
  1594. PSTR
  1595. RealAllocPathStringA (
  1596. IN DWORD Tchars
  1597. )
  1598. {
  1599. PSTR str;
  1600. if (Tchars == 0) {
  1601. Tchars = MAX_MBCHAR_PATH;
  1602. }
  1603. str = (PSTR) PmGetAlignedMemory (g_PathsPool, Tchars);
  1604. str [0] = 0;
  1605. return str;
  1606. }
  1607. PWSTR
  1608. RealAllocPathStringW (
  1609. IN DWORD Tchars
  1610. )
  1611. {
  1612. PWSTR str;
  1613. if (Tchars == 0) {
  1614. Tchars = MAX_WCHAR_PATH;
  1615. }
  1616. str = (PWSTR) PmGetAlignedMemory (g_PathsPool, Tchars * sizeof (WCHAR));
  1617. str [0] = 0;
  1618. return str;
  1619. }
  1620. VOID
  1621. RealSplitPathA (
  1622. IN PCSTR Path,
  1623. OUT PSTR *DrivePtr,
  1624. OUT PSTR *PathPtr,
  1625. OUT PSTR *FileNamePtr,
  1626. OUT PSTR *ExtPtr
  1627. )
  1628. {
  1629. CHAR drive[_MAX_DRIVE];
  1630. CHAR dir[_MAX_DIR];
  1631. CHAR fileName[_MAX_FNAME];
  1632. CHAR ext[_MAX_EXT];
  1633. _splitpath (Path, drive, dir, fileName, ext);
  1634. if (DrivePtr) {
  1635. *DrivePtr = PmDuplicateStringA (g_PathsPool, drive);
  1636. MYASSERT (*DrivePtr);
  1637. }
  1638. if (PathPtr) {
  1639. *PathPtr = PmDuplicateStringA (g_PathsPool, dir);
  1640. MYASSERT (*PathPtr);
  1641. }
  1642. if (FileNamePtr) {
  1643. *FileNamePtr = PmDuplicateStringA (g_PathsPool, fileName);
  1644. MYASSERT (*FileNamePtr);
  1645. }
  1646. if (ExtPtr) {
  1647. *ExtPtr = PmDuplicateStringA (g_PathsPool, ext);
  1648. MYASSERT (*ExtPtr);
  1649. }
  1650. }
  1651. VOID
  1652. RealSplitPathW (
  1653. IN PCWSTR Path,
  1654. OUT PWSTR *DrivePtr,
  1655. OUT PWSTR *PathPtr,
  1656. OUT PWSTR *FileNamePtr,
  1657. OUT PWSTR *ExtPtr
  1658. )
  1659. {
  1660. WCHAR drive[_MAX_DRIVE];
  1661. WCHAR dir[_MAX_DIR];
  1662. WCHAR fileName[_MAX_FNAME];
  1663. WCHAR ext[_MAX_EXT];
  1664. _wsplitpath (Path, drive, dir, fileName, ext);
  1665. if (DrivePtr) {
  1666. *DrivePtr = PmDuplicateStringW (g_PathsPool, drive);
  1667. MYASSERT (*DrivePtr);
  1668. }
  1669. if (PathPtr) {
  1670. *PathPtr = PmDuplicateStringW (g_PathsPool, dir);
  1671. MYASSERT (*PathPtr);
  1672. }
  1673. if (FileNamePtr) {
  1674. *FileNamePtr = PmDuplicateStringW (g_PathsPool, fileName);
  1675. MYASSERT (*FileNamePtr);
  1676. }
  1677. if (ExtPtr) {
  1678. *ExtPtr = PmDuplicateStringW (g_PathsPool, ext);
  1679. MYASSERT (*ExtPtr);
  1680. }
  1681. }
  1682. PSTR
  1683. RealDuplicatePathStringA (
  1684. IN PCSTR Path,
  1685. IN DWORD ExtraBytes
  1686. )
  1687. {
  1688. PSTR str;
  1689. str = PmGetAlignedMemory (
  1690. g_PathsPool,
  1691. SizeOfStringA (Path) + ExtraBytes
  1692. );
  1693. MYASSERT (str);
  1694. StringCopyA (str, Path);
  1695. return str;
  1696. }
  1697. PWSTR
  1698. RealDuplicatePathStringW (
  1699. IN PCWSTR Path,
  1700. IN DWORD ExtraBytes
  1701. )
  1702. {
  1703. PWSTR str;
  1704. str = PmGetAlignedMemory (
  1705. g_PathsPool,
  1706. SizeOfStringW (Path) + ExtraBytes
  1707. );
  1708. MYASSERT (str);
  1709. StringCopyW (str, Path);
  1710. return str;
  1711. }
  1712. BOOL
  1713. EnumFirstPathExA (
  1714. OUT PPATH_ENUMA PathEnum,
  1715. IN PCSTR AdditionalPath,
  1716. IN PCSTR WinDir,
  1717. IN PCSTR SysDir,
  1718. IN BOOL IncludeEnvPath
  1719. )
  1720. {
  1721. DWORD bufferSize;
  1722. DWORD pathSize;
  1723. PSTR currPathEnd;
  1724. if (PathEnum == NULL) {
  1725. return FALSE;
  1726. }
  1727. bufferSize = pathSize = GetEnvironmentVariable (TEXT("PATH"), NULL, 0);
  1728. bufferSize *= 2;
  1729. if (AdditionalPath != NULL) {
  1730. bufferSize += SizeOfStringA (AdditionalPath);
  1731. }
  1732. if (SysDir != NULL) {
  1733. bufferSize += SizeOfStringA (SysDir);
  1734. }
  1735. if (WinDir != NULL) {
  1736. bufferSize += SizeOfStringA (WinDir);
  1737. }
  1738. PathEnum->BufferPtr = HeapAlloc (g_hHeap, 0, bufferSize);
  1739. if (PathEnum->BufferPtr == NULL) {
  1740. return FALSE;
  1741. }
  1742. PathEnum->BufferPtr [0] = 0;
  1743. if (AdditionalPath != NULL) {
  1744. StringCopyA (PathEnum->BufferPtr, AdditionalPath);
  1745. StringCatA (PathEnum->BufferPtr, ";");
  1746. }
  1747. if (SysDir != NULL) {
  1748. StringCatA (PathEnum->BufferPtr, SysDir);
  1749. StringCatA (PathEnum->BufferPtr, ";");
  1750. }
  1751. if (WinDir != NULL) {
  1752. StringCatA (PathEnum->BufferPtr, WinDir);
  1753. StringCatA (PathEnum->BufferPtr, ";");
  1754. }
  1755. if (IncludeEnvPath) {
  1756. currPathEnd = GetEndOfStringA (PathEnum->BufferPtr);
  1757. GetEnvironmentVariable (TEXT("PATH"), currPathEnd, pathSize);
  1758. }
  1759. PathEnum->PtrNextPath = PathEnum-> BufferPtr;
  1760. return EnumNextPathA (PathEnum);
  1761. }
  1762. BOOL
  1763. EnumNextPathA (
  1764. IN OUT PPATH_ENUMA PathEnum
  1765. )
  1766. {
  1767. PSTR currPathEnd;
  1768. if (PathEnum->PtrNextPath == NULL) {
  1769. AbortPathEnumA (PathEnum);
  1770. return FALSE;
  1771. }
  1772. PathEnum->PtrCurrPath = PathEnum->PtrNextPath;
  1773. PathEnum->PtrNextPath = _mbschr (PathEnum->PtrNextPath, ';');
  1774. if (PathEnum->PtrNextPath == NULL) {
  1775. return TRUE;
  1776. }
  1777. currPathEnd = PathEnum->PtrNextPath;
  1778. PathEnum->PtrNextPath = _mbsinc (PathEnum->PtrNextPath);
  1779. *currPathEnd = 0;
  1780. if (*(PathEnum->PtrNextPath) == 0) {
  1781. PathEnum->PtrNextPath = NULL;
  1782. }
  1783. if (*(PathEnum->PtrCurrPath) == 0) {
  1784. //
  1785. // We found an empty path segment. Skip it.
  1786. //
  1787. return EnumNextPathA (PathEnum);
  1788. }
  1789. return TRUE;
  1790. }
  1791. BOOL
  1792. AbortPathEnumA (
  1793. IN OUT PPATH_ENUMA PathEnum
  1794. )
  1795. {
  1796. if (PathEnum->BufferPtr != NULL) {
  1797. HeapFree (g_hHeap, 0, PathEnum->BufferPtr);
  1798. PathEnum->BufferPtr = NULL;
  1799. }
  1800. return TRUE;
  1801. }
  1802. VOID
  1803. FreePathStringExA (
  1804. IN PMHANDLE Pool, OPTIONAL
  1805. IN PCSTR Path OPTIONAL
  1806. )
  1807. {
  1808. if (Path) {
  1809. if (!Pool) {
  1810. Pool = g_PathsPool;
  1811. }
  1812. PmReleaseMemory (Pool, (PSTR) Path);
  1813. }
  1814. }
  1815. VOID
  1816. FreePathStringExW (
  1817. IN PMHANDLE Pool, OPTIONAL
  1818. IN PCWSTR Path OPTIONAL
  1819. )
  1820. {
  1821. if (Path) {
  1822. if (!Pool) {
  1823. Pool = g_PathsPool;
  1824. }
  1825. PmReleaseMemory (Pool, (PWSTR) Path);
  1826. }
  1827. }
  1828. /*++
  1829. Routine Description:
  1830. PushError and PopError push the error code onto a stack or pull the
  1831. last pushed error code off the stack. PushError uses GetLastError
  1832. and PopError uses SetLastError to modify the last error value.
  1833. Arguments:
  1834. none
  1835. Return Value:
  1836. none
  1837. --*/
  1838. VOID
  1839. PushNewError (DWORD dwError)
  1840. {
  1841. if (g_dwStackPos == MAX_STACK)
  1842. return;
  1843. g_dwErrorStack[g_dwStackPos] = dwError;
  1844. g_dwStackPos++;
  1845. }
  1846. VOID
  1847. PushError (VOID)
  1848. {
  1849. if (g_dwStackPos == MAX_STACK)
  1850. return;
  1851. g_dwErrorStack[g_dwStackPos] = GetLastError ();
  1852. g_dwStackPos++;
  1853. }
  1854. DWORD
  1855. PopError (VOID)
  1856. {
  1857. if (!g_dwStackPos)
  1858. return GetLastError();
  1859. g_dwStackPos--;
  1860. SetLastError (g_dwErrorStack[g_dwStackPos]);
  1861. return g_dwErrorStack[g_dwStackPos];
  1862. }
  1863. /*++
  1864. Routine Description:
  1865. GetHexDigit is a simple base 16 ASCII to int convertor. The
  1866. convertor is case-insensitive.
  1867. Arguments:
  1868. c - Character to convert
  1869. Return Value:
  1870. Base 16 value corresponding to character supplied, or -1 if
  1871. the character is not 0-9, A-F or a-f.
  1872. --*/
  1873. int
  1874. GetHexDigit (IN int c)
  1875. {
  1876. if (c >= '0' && c <= '9')
  1877. return (c - '0');
  1878. c = towlower ((wint_t) c);
  1879. if (c >= 'a' && c <= 'f')
  1880. return (c - 'a' + 10);
  1881. return -1;
  1882. }
  1883. /*++
  1884. Routine Description:
  1885. _tcsnum is similar to strtoul, except is figures out which base
  1886. the number should be calculated from. It supports decimal and
  1887. hexadecimal numbers (using the 0x00 notation). The return
  1888. value is the decoded value, or 0 if a syntax error was found.
  1889. Arguments:
  1890. szNum - Pointer to the string holding the number. This number
  1891. can be either decimal (a series of 0-9 characters), or
  1892. hexadecimal (a series of 0-9, A-F or a-f characters,
  1893. prefixed with 0x or 0X).
  1894. Return Value:
  1895. The decoded unsigned long value, or zero if a syntax error was
  1896. found.
  1897. --*/
  1898. DWORD
  1899. _mbsnum (IN PCSTR szNum)
  1900. {
  1901. unsigned int d = 0;
  1902. int i;
  1903. if (szNum[0] == '0' && tolower (szNum[1]) == 'x') {
  1904. // Get hex value
  1905. szNum += 2;
  1906. while ((i = GetHexDigit ((int) *szNum)) != -1) {
  1907. d = d * 16 + (UINT)i;
  1908. szNum++;
  1909. }
  1910. }
  1911. else {
  1912. // Get decimal value
  1913. while (*szNum >= '0' && *szNum <= '9') {
  1914. d = d * 10 + (*szNum - '0');
  1915. szNum++;
  1916. }
  1917. }
  1918. return d;
  1919. }
  1920. DWORD
  1921. _wcsnum (
  1922. IN PCWSTR szNum
  1923. )
  1924. {
  1925. unsigned int d = 0;
  1926. int i;
  1927. if (szNum[0] == L'0' && towlower (szNum[1]) == L'x') {
  1928. // Get hex value
  1929. szNum += 2;
  1930. while ((i = GetHexDigit ((int) *szNum)) != -1) {
  1931. d = d * 16 + (UINT)i;
  1932. szNum++;
  1933. }
  1934. }
  1935. else {
  1936. // Get decimal value
  1937. while (*szNum >= L'0' && *szNum <= L'9') {
  1938. d = d * 10 + (*szNum - L'0');
  1939. szNum++;
  1940. }
  1941. }
  1942. return d;
  1943. }
  1944. /*++
  1945. Routine Description:
  1946. StringCat is a lstrcat-type routine. It returns the pointer to the end
  1947. of a string instead of the beginning, is faster, and has the proper types
  1948. to keep lint happy.
  1949. Arguments:
  1950. Destination - A pointer to a caller-allocated buffer that may point
  1951. anywhere within the string to append to
  1952. Source - A pointer to a string that is appended to Destination
  1953. Return Value:
  1954. A pointer to the NULL terminator within the Destination string.
  1955. --*/
  1956. PSTR
  1957. StringCatA (
  1958. OUT PSTR Destination,
  1959. IN PCSTR Source
  1960. )
  1961. {
  1962. PCSTR current = Source;
  1963. PCSTR end;
  1964. //
  1965. // Advance Destination to end of string
  1966. //
  1967. Destination = GetEndOfStringA (Destination);
  1968. while (*current) {
  1969. *Destination++ = *current++; //lint !e613
  1970. }
  1971. //
  1972. // Make sure DBCS string is properly terminated
  1973. //
  1974. end = current;
  1975. current--;
  1976. while (current >= Source) {
  1977. if (!IsLeadByte (*current)) {
  1978. //
  1979. // destEnd is correct
  1980. //
  1981. break;
  1982. }
  1983. current--;
  1984. }
  1985. if (!((end - current) & 1)) {
  1986. Destination--; //lint !e794
  1987. }
  1988. *Destination = 0; //lint !e794
  1989. return Destination;
  1990. }
  1991. PWSTR
  1992. StringCatW (
  1993. OUT PWSTR Destination,
  1994. IN PCWSTR Source
  1995. )
  1996. {
  1997. //
  1998. // Advance Destination to end of string
  1999. //
  2000. Destination = GetEndOfStringW (Destination);
  2001. //
  2002. // Copy string
  2003. //
  2004. while (*Source) {
  2005. *Destination++ = *Source++;
  2006. }
  2007. *Destination = 0;
  2008. return Destination;
  2009. }
  2010. /*++
  2011. Routine Description:
  2012. _tcsistr is a case-insensitive version of _tcsstr.
  2013. Arguments:
  2014. szStr - A pointer to the larger string, which may hold szSubStr
  2015. szSubStr - A pointer to a string that may be enclosed in szStr
  2016. Return Value:
  2017. A pointer to the first occurance of szSubStr in szStr, or NULL if
  2018. no match is found.
  2019. --*/
  2020. PCSTR
  2021. _mbsistr (PCSTR mbstrStr, PCSTR mbstrSubStr)
  2022. {
  2023. PCSTR mbstrStart, mbstrStrPos, mbstrSubStrPos;
  2024. PCSTR mbstrEnd;
  2025. mbstrEnd = (PSTR) ((LPBYTE) mbstrStr + ByteCountA (mbstrStr) - ByteCountA (mbstrSubStr));
  2026. for (mbstrStart = mbstrStr ; mbstrStart <= mbstrEnd ; mbstrStart = _mbsinc (mbstrStart)) {
  2027. mbstrStrPos = mbstrStart;
  2028. mbstrSubStrPos = mbstrSubStr;
  2029. while (*mbstrSubStrPos &&
  2030. _mbctolower ((MBCHAR) _mbsnextc (mbstrSubStrPos)) == _mbctolower ((MBCHAR) _mbsnextc (mbstrStrPos)))
  2031. {
  2032. mbstrStrPos = _mbsinc (mbstrStrPos);
  2033. mbstrSubStrPos = _mbsinc (mbstrSubStrPos);
  2034. }
  2035. if (!(*mbstrSubStrPos))
  2036. return mbstrStart;
  2037. }
  2038. return NULL;
  2039. }
  2040. PCWSTR
  2041. _wcsistr (PCWSTR wstrStr, PCWSTR wstrSubStr)
  2042. {
  2043. PCWSTR wstrStart, wstrStrPos, wstrSubStrPos;
  2044. PCWSTR wstrEnd;
  2045. wstrEnd = (PWSTR) ((LPBYTE) wstrStr + ByteCountW (wstrStr) - ByteCountW (wstrSubStr));
  2046. for (wstrStart = wstrStr ; wstrStart <= wstrEnd ; wstrStart++) {
  2047. wstrStrPos = wstrStart;
  2048. wstrSubStrPos = wstrSubStr;
  2049. while (*wstrSubStrPos &&
  2050. towlower (*wstrSubStrPos) == towlower (*wstrStrPos))
  2051. {
  2052. wstrStrPos++;
  2053. wstrSubStrPos++;
  2054. }
  2055. if (!(*wstrSubStrPos))
  2056. return wstrStart;
  2057. }
  2058. return NULL;
  2059. }
  2060. /*++
  2061. Routine Description:
  2062. StringCompareAB compares a string against a string between to string
  2063. pointers
  2064. Arguments:
  2065. String - Specifies the string to compare
  2066. Start - Specifies the start of the string to compare against
  2067. end - Specifies the end of the string to compare against. The character
  2068. pointed to by End is not included in the comparision.
  2069. Return Value:
  2070. Less than zero: String is numerically less than the string between Start and
  2071. End
  2072. Zero: String matches the string between Start and End identically
  2073. Greater than zero: String is numerically greater than the string between
  2074. Start and End
  2075. --*/
  2076. INT
  2077. StringCompareABA (
  2078. IN PCSTR String,
  2079. IN PCSTR Start,
  2080. IN PCSTR End
  2081. )
  2082. {
  2083. while (*String && Start < End) {
  2084. if (_mbsnextc (String) != _mbsnextc (Start)) {
  2085. break;
  2086. }
  2087. String = _mbsinc (String);
  2088. Start = _mbsinc (Start);
  2089. }
  2090. if (Start == End && *String == 0) {
  2091. return 0;
  2092. }
  2093. return (INT) (_mbsnextc (Start) - _mbsnextc (String));
  2094. }
  2095. INT
  2096. StringCompareABW (
  2097. IN PCWSTR String,
  2098. IN PCWSTR Start,
  2099. IN PCWSTR End
  2100. )
  2101. {
  2102. while (*String && Start < End) {
  2103. if (*String != *Start) {
  2104. break;
  2105. }
  2106. String++;
  2107. Start++;
  2108. }
  2109. if (Start == End && *String == 0) {
  2110. return 0;
  2111. }
  2112. return *Start - *String;
  2113. }
  2114. BOOL
  2115. StringMatchA (
  2116. IN PCSTR String1,
  2117. IN PCSTR String2
  2118. )
  2119. /*++
  2120. Routine Description:
  2121. StringMatchA is an optimized string compare. Usually a comparison is used to
  2122. see if two strings are identical, and the numeric releationships aren't
  2123. important. This routine exploits that fact and does a byte-by-byte compare.
  2124. Arguments:
  2125. String1 - Specifies the first string to compare
  2126. String2 - Specifies the second string to compare
  2127. Return Value:
  2128. TRUE if the strings match identically, FALSE otherwise.
  2129. --*/
  2130. {
  2131. while (*String1) {
  2132. if (*String1 != *String2) {
  2133. return FALSE;
  2134. }
  2135. String1++;
  2136. String2++;
  2137. }
  2138. if (*String2) {
  2139. return FALSE;
  2140. }
  2141. return TRUE;
  2142. }
  2143. BOOL
  2144. StringMatchABA (
  2145. IN PCSTR String,
  2146. IN PCSTR Start,
  2147. IN PCSTR End
  2148. )
  2149. /*++
  2150. Routine Description:
  2151. StringMatchABA is an optimized string compare. Usually a comparison is
  2152. used to see if two strings are identical, and the numeric releationships
  2153. aren't important. This routine exploits that fact and does a byte-by-byte
  2154. compare.
  2155. Arguments:
  2156. String - Specifies the first string to compare
  2157. Start - Specifies the beginning of the second string to compare
  2158. End - Specifies the end of the second string to compare (points to one
  2159. character beyond the last valid character of the second string)
  2160. Return Value:
  2161. TRUE if the strings match identically, FALSE otherwise. If End is equal
  2162. or less than Start, the return value is always TRUE.
  2163. --*/
  2164. {
  2165. while (*String && Start < End) {
  2166. if (*String != *Start) {
  2167. return FALSE;
  2168. }
  2169. String++;
  2170. Start++;
  2171. }
  2172. if (Start < End && *Start) {
  2173. return FALSE;
  2174. }
  2175. return TRUE;
  2176. }
  2177. INT
  2178. StringICompareABA (
  2179. IN PCSTR String,
  2180. IN PCSTR Start,
  2181. IN PCSTR End
  2182. )
  2183. {
  2184. while (*String && Start < End) {
  2185. if (tolower ((INT)(_mbsnextc (String))) != tolower ((INT)(_mbsnextc (Start)))) {
  2186. break;
  2187. }
  2188. String = _mbsinc (String);
  2189. Start = _mbsinc (Start);
  2190. }
  2191. if (Start == End && *String == 0) {
  2192. return 0;
  2193. }
  2194. return (tolower ((INT)(_mbsnextc (Start))) - tolower ((INT)(_mbsnextc (String))));
  2195. }
  2196. INT
  2197. StringICompareABW (
  2198. IN PCWSTR String,
  2199. IN PCWSTR Start,
  2200. IN PCWSTR End
  2201. )
  2202. {
  2203. while (*String && Start < End) {
  2204. if (towlower (*String) != towlower (*Start)) {
  2205. break;
  2206. }
  2207. String++;
  2208. Start++;
  2209. }
  2210. if (Start == End && *String == 0) {
  2211. return 0;
  2212. }
  2213. return towlower (*Start) - towlower (*String);
  2214. }
  2215. VOID
  2216. _setmbchar (
  2217. IN OUT PSTR Str,
  2218. IN MBCHAR c
  2219. )
  2220. /*++
  2221. Routine Description:
  2222. _setmbchar sets the character at the specified string position, shifting
  2223. bytes if necessary to keep the string in tact.
  2224. Arguments:
  2225. Str - String
  2226. c - Character to set
  2227. Return Value:
  2228. none
  2229. --*/
  2230. {
  2231. if (c < 256) {
  2232. if (IsLeadByte (*Str)) {
  2233. //
  2234. // Delete one byte from the string
  2235. //
  2236. MoveMemory (Str, Str+1, SizeOfStringA (Str+2) + 1);
  2237. }
  2238. *Str = (CHAR) c;
  2239. } else {
  2240. if (!IsLeadByte (*Str)) {
  2241. //
  2242. // Insert one byte in the string
  2243. //
  2244. MoveMemory (Str+1, Str, SizeOfStringA (Str));
  2245. }
  2246. *((WORD *) Str) = (WORD) c;
  2247. }
  2248. }
  2249. /*++
  2250. Routine Description:
  2251. GetNextRuleChar extracts the first character in the *p_szRule string,
  2252. and determines the character value, decoding the ~xx~ syntax (which
  2253. specifies any arbitrary value).
  2254. GetNextRuleChar returns a complete character for SBCS and UNICODE, but
  2255. it may return either a lead byte or non-lead byte for MBCS. To indicate
  2256. a MBCS character, two ~xx~ hex values are needed.
  2257. Arguments:
  2258. p_szRule - A pointer to a pointer; a caller-allocated buffer that
  2259. holds the rule string.
  2260. p_bFromHex - A pointer to a caller-allocated BOOL that receives TRUE
  2261. when the return value was decoded from the <xx> syntax.
  2262. Return Value:
  2263. The decoded character; *p_bFromHex identifies if the return value was
  2264. a literal or was a hex-encoded character.
  2265. --*/
  2266. MBCHAR
  2267. GetNextRuleCharA (
  2268. IN OUT PCSTR *PtrToRule,
  2269. OUT BOOL *FromHex
  2270. )
  2271. {
  2272. MBCHAR ch;
  2273. MBCHAR Value;
  2274. INT i;
  2275. PCSTR StartPtr;
  2276. StartPtr = *PtrToRule;
  2277. if (FromHex) {
  2278. *FromHex = FALSE;
  2279. }
  2280. if (_mbsnextc (StartPtr) == '~') {
  2281. *PtrToRule += 1;
  2282. Value = 0;
  2283. i = 0;
  2284. for (i = 0 ; **PtrToRule && i < 8 ; i++) {
  2285. ch = _mbsnextc (*PtrToRule);
  2286. *PtrToRule += 1;
  2287. if (ch == '~') {
  2288. if (FromHex) {
  2289. *FromHex = TRUE;
  2290. }
  2291. return Value;
  2292. }
  2293. Value *= 16;
  2294. if (ch >= '0' && ch <= '9') {
  2295. Value += ch - '0';
  2296. } else if (ch >= 'a' && ch <= 'f') {
  2297. Value += ch - 'a' + 10;
  2298. } else if (ch >= 'A' && ch <= 'F') {
  2299. Value += ch - 'A' + 10;
  2300. } else {
  2301. break;
  2302. }
  2303. }
  2304. DEBUGMSGA ((DBG_WHOOPS, "Bad formatting in encoded string %s", StartPtr));
  2305. }
  2306. *PtrToRule = _mbsinc (StartPtr);
  2307. return _mbsnextc (StartPtr);
  2308. }
  2309. WCHAR
  2310. GetNextRuleCharW (
  2311. IN OUT PCWSTR *PtrToRule,
  2312. OUT BOOL *FromHex
  2313. )
  2314. {
  2315. WCHAR ch;
  2316. WCHAR Value;
  2317. INT i;
  2318. PCWSTR StartPtr;
  2319. StartPtr = *PtrToRule;
  2320. if (FromHex) {
  2321. *FromHex = FALSE;
  2322. }
  2323. if (*StartPtr == L'~') {
  2324. *PtrToRule += 1;
  2325. Value = 0;
  2326. i = 0;
  2327. for (i = 0 ; **PtrToRule && i < 8 ; i++) {
  2328. ch = **PtrToRule;
  2329. *PtrToRule += 1;
  2330. if (ch == L'~') {
  2331. if (FromHex) {
  2332. *FromHex = TRUE;
  2333. }
  2334. return Value;
  2335. }
  2336. Value *= 16;
  2337. if (ch >= L'0' && ch <= L'9') {
  2338. Value += ch - L'0';
  2339. } else if (ch >= L'a' && ch <= L'f') {
  2340. Value += ch - L'a' + 10;
  2341. } else if (ch >= L'A' && ch <= L'F') {
  2342. Value += ch - L'A' + 10;
  2343. } else {
  2344. break;
  2345. }
  2346. }
  2347. DEBUGMSGW ((DBG_WHOOPS, "Bad formatting in encoded string %s", StartPtr));
  2348. }
  2349. *PtrToRule = StartPtr + 1;
  2350. return *StartPtr;
  2351. }
  2352. /*++
  2353. Routine Description:
  2354. DecodeRuleChars takes a complete rule string (szRule), possibly
  2355. encoded with hex-specified character values (~xx~). The output
  2356. string contains unencoded characters.
  2357. Arguments:
  2358. szRule - A caller-allocated buffer, big enough to hold an
  2359. unencoded rule. szRule can be equal to szEncRule.
  2360. szEncRule - The string holding a possibly encoded string.
  2361. Return Value:
  2362. Equal to szRule.
  2363. --*/
  2364. PSTR
  2365. DecodeRuleCharsA (PSTR mbstrRule, PCSTR mbstrEncRule)
  2366. {
  2367. MBCHAR c;
  2368. PSTR mbstrOrgRule;
  2369. mbstrOrgRule = mbstrRule;
  2370. //
  2371. // Copy string, converting ~xx~ to a single char
  2372. //
  2373. do {
  2374. c = GetNextRuleCharA (&mbstrEncRule, NULL);
  2375. *mbstrRule = (CHAR) c;
  2376. mbstrRule++; // MBCS->incomplete char will be finished in next loop iteration
  2377. } while (c);
  2378. return mbstrOrgRule;
  2379. }
  2380. PWSTR
  2381. DecodeRuleCharsW (PWSTR wstrRule, PCWSTR wstrEncRule)
  2382. {
  2383. WCHAR c;
  2384. PWSTR wstrOrgRule;
  2385. wstrOrgRule = wstrRule;
  2386. //
  2387. // Copy string, converting ~xx~ to a single char
  2388. //
  2389. do {
  2390. c = GetNextRuleCharW (&wstrEncRule, NULL);
  2391. *wstrRule = c;
  2392. wstrRule++;
  2393. } while (c);
  2394. return wstrOrgRule;
  2395. }
  2396. PSTR
  2397. DecodeRuleCharsABA (PSTR mbstrRule, PCSTR mbstrEncRule, PCSTR End)
  2398. {
  2399. MBCHAR c;
  2400. PSTR mbstrOrgRule;
  2401. mbstrOrgRule = mbstrRule;
  2402. //
  2403. // Copy string, converting ~xx~ to a single char
  2404. //
  2405. while (mbstrEncRule < End) {
  2406. c = GetNextRuleCharA (&mbstrEncRule, NULL);
  2407. *mbstrRule = (CHAR) c;
  2408. mbstrRule++; // MBCS->incomplete char will be finished in next loop iteration
  2409. }
  2410. *mbstrRule = 0;
  2411. return mbstrOrgRule;
  2412. }
  2413. PWSTR
  2414. DecodeRuleCharsABW (PWSTR wstrRule, PCWSTR wstrEncRule, PCWSTR End)
  2415. {
  2416. WCHAR c;
  2417. PWSTR wstrOrgRule;
  2418. wstrOrgRule = wstrRule;
  2419. //
  2420. // Copy string, converting ~xx~ to a single char
  2421. //
  2422. while (wstrEncRule < End) {
  2423. c = GetNextRuleCharW (&wstrEncRule, NULL);
  2424. *wstrRule = c;
  2425. wstrRule++;
  2426. }
  2427. *wstrRule = 0;
  2428. return wstrOrgRule;
  2429. }
  2430. /*++
  2431. Routine Description:
  2432. EncodeRuleChars takes an unencoded rule string (szRule), and
  2433. converts it to a string possibly encoded with hex-specified
  2434. character values (~xx~). The output string contains encoded
  2435. characters.
  2436. Arguments:
  2437. szEncRule - A caller-allocated buffer, big enough to hold an
  2438. encoded rule. szEncRule CAN NOT be equal to szRule.
  2439. One way to calculate a max buffer size for szEncRule
  2440. is to use the following code:
  2441. allocsize = SizeOfString (szRule) * 6;
  2442. In the worst case, each character in szRule will take
  2443. six single-byte characters in szEncRule. In the normal
  2444. case, szEncRule will only be a few bytes bigger than
  2445. szRule.
  2446. szRule - The string holding an unencoded string.
  2447. Return Value:
  2448. Equal to szEncRule.
  2449. --*/
  2450. PSTR
  2451. EncodeRuleCharsA (PSTR mbstrEncRule, PCSTR mbstrRule)
  2452. {
  2453. PSTR mbstrOrgRule;
  2454. static CHAR mbstrExclusions[] = "[]<>\'*$|:?\";,%";
  2455. MBCHAR c;
  2456. mbstrOrgRule = mbstrEncRule;
  2457. while (*mbstrRule) {
  2458. c = _mbsnextc (mbstrRule);
  2459. if (!_ismbcprint (c) || _mbschr (mbstrExclusions, c)) {
  2460. // Escape unprintable or excluded character
  2461. wsprintfA (mbstrEncRule, "~%X~", c);
  2462. mbstrEncRule = GetEndOfStringA (mbstrEncRule);
  2463. mbstrRule = _mbsinc (mbstrRule);
  2464. }
  2465. else {
  2466. // Copy multibyte character
  2467. if (IsLeadByte (*mbstrRule)) {
  2468. *mbstrEncRule = *mbstrRule;
  2469. mbstrEncRule++;
  2470. mbstrRule++;
  2471. }
  2472. *mbstrEncRule = *mbstrRule;
  2473. mbstrEncRule++;
  2474. mbstrRule++;
  2475. }
  2476. }
  2477. *mbstrEncRule = 0; //lint !e613
  2478. return mbstrOrgRule;
  2479. }
  2480. PWSTR
  2481. EncodeRuleCharsW (PWSTR wstrEncRule, PCWSTR wstrRule)
  2482. {
  2483. PWSTR wstrOrgRule;
  2484. static WCHAR wstrExclusions[] = L"[]<>\'*$|:?\";,%";
  2485. WCHAR c;
  2486. wstrOrgRule = wstrEncRule;
  2487. while (c = *wstrRule) { //lint !e720
  2488. if (!iswprint (c) || wcschr (wstrExclusions, c)) {
  2489. wsprintfW (wstrEncRule, L"~%X~", c);
  2490. wstrEncRule = GetEndOfStringW (wstrEncRule);
  2491. }
  2492. else {
  2493. *wstrEncRule = *wstrRule;
  2494. wstrEncRule++;
  2495. }
  2496. wstrRule++;
  2497. }
  2498. *wstrEncRule = 0;
  2499. return wstrOrgRule;
  2500. }
  2501. /*++
  2502. Routine Description:
  2503. _tcsisprint is a string version of _istprint.
  2504. Arguments:
  2505. szStr - A pointer to the string to examine
  2506. Return Value:
  2507. Non-zero if szStr is made up only of printable characters.
  2508. --*/
  2509. int
  2510. _mbsisprint (PCSTR mbstrStr)
  2511. {
  2512. while (*mbstrStr && _ismbcprint ((MBCHAR) _mbsnextc (mbstrStr))) {
  2513. mbstrStr = _mbsinc (mbstrStr);
  2514. }
  2515. return *mbstrStr == 0;
  2516. }
  2517. int
  2518. _wcsisprint (PCWSTR wstrStr)
  2519. {
  2520. while (*wstrStr && iswprint (*wstrStr)) {
  2521. wstrStr++;
  2522. }
  2523. return *wstrStr == 0;
  2524. }
  2525. /*++
  2526. Routine Description:
  2527. SkipSpace returns a pointer to the next position within a string
  2528. that does not have whitespace characters. It uses the C
  2529. runtime isspace to determine what a whitespace character is.
  2530. Arguments:
  2531. szStr - A pointer to the string to examine
  2532. Return Value:
  2533. A pointer to the first non-whitespace character in the string,
  2534. or NULL if the string is made up of all whitespace characters
  2535. or the string is empty.
  2536. --*/
  2537. PCSTR
  2538. SkipSpaceA (PCSTR mbstrStr)
  2539. {
  2540. while (_ismbcspace ((MBCHAR) _mbsnextc (mbstrStr)))
  2541. mbstrStr = _mbsinc (mbstrStr);
  2542. return mbstrStr;
  2543. }
  2544. PCWSTR
  2545. SkipSpaceW (PCWSTR wstrStr)
  2546. {
  2547. while (iswspace (*wstrStr))
  2548. wstrStr++;
  2549. return wstrStr;
  2550. }
  2551. /*++
  2552. Routine Description:
  2553. SkipSpaceR returns a pointer to the next position within a string
  2554. that does not have whitespace characters. It uses the C
  2555. runtime isspace to determine what a whitespace character is.
  2556. This function is identical to SkipSpace except it works from
  2557. right to left instead of left to right.
  2558. Arguments:
  2559. StrBase - A pointer to the first character in the string
  2560. Str - A pointer to the end of the string, or NULL if the
  2561. end is not known.
  2562. Return Value:
  2563. A pointer to the first non-whitespace character in the string,
  2564. as viewed from right to left, or NULL if the string is made up
  2565. of all whitespace characters or the string is empty.
  2566. --*/
  2567. PCSTR
  2568. SkipSpaceRA (
  2569. IN PCSTR StrBase,
  2570. IN PCSTR Str OPTIONAL
  2571. )
  2572. {
  2573. if (!Str) {
  2574. Str = GetEndOfStringA (StrBase);
  2575. }
  2576. if (*Str == 0) { //lint !e613
  2577. Str = _mbsdec2 (StrBase, Str);
  2578. if (!Str) {
  2579. return NULL;
  2580. }
  2581. }
  2582. do {
  2583. if (!_ismbcspace((MBCHAR) _mbsnextc(Str))) {
  2584. return Str;
  2585. }
  2586. } while (Str = _mbsdec2(StrBase, Str)); //lint !e720
  2587. return NULL;
  2588. }
  2589. PCWSTR
  2590. SkipSpaceRW (
  2591. IN PCWSTR StrBase,
  2592. IN PCWSTR Str OPTIONAL
  2593. )
  2594. {
  2595. if (!Str) {
  2596. Str = GetEndOfStringW (StrBase);
  2597. }
  2598. if (*Str == 0) {
  2599. Str--;
  2600. if (Str < StrBase) {
  2601. return NULL;
  2602. }
  2603. }
  2604. do {
  2605. if (!iswspace(*Str)) {
  2606. return Str;
  2607. }
  2608. } while (Str-- != StrBase);
  2609. return NULL;
  2610. }
  2611. /*++
  2612. Routine Description:
  2613. TruncateTrailingSpace trims the specified string after the
  2614. very last non-space character, or empties the string if it
  2615. contains only space characters. This routine uses isspace
  2616. to determine what a space is.
  2617. Arguments:
  2618. Str - Specifies string to process
  2619. Return Value:
  2620. none
  2621. --*/
  2622. VOID
  2623. TruncateTrailingSpaceA (
  2624. IN OUT PSTR Str
  2625. )
  2626. {
  2627. PSTR LastNonSpace;
  2628. PSTR OrgStr;
  2629. OrgStr = Str;
  2630. LastNonSpace = NULL;
  2631. while (*Str) {
  2632. if (!_ismbcspace ((MBCHAR) _mbsnextc (Str))) {
  2633. LastNonSpace = Str;
  2634. }
  2635. Str = _mbsinc (Str);
  2636. }
  2637. if (LastNonSpace) {
  2638. *_mbsinc (LastNonSpace) = 0;
  2639. } else {
  2640. *OrgStr = 0;
  2641. }
  2642. }
  2643. VOID
  2644. TruncateTrailingSpaceW (
  2645. IN OUT PWSTR Str
  2646. )
  2647. {
  2648. PWSTR LastNonSpace;
  2649. PWSTR OrgStr;
  2650. OrgStr = Str;
  2651. LastNonSpace = NULL;
  2652. while (*Str) {
  2653. if (!iswspace (*Str)) {
  2654. LastNonSpace = Str;
  2655. }
  2656. Str++;
  2657. }
  2658. if (LastNonSpace) {
  2659. *(LastNonSpace + 1) = 0;
  2660. } else {
  2661. *OrgStr = 0;
  2662. }
  2663. }
  2664. /*++
  2665. Routine Description:
  2666. IsPatternMatch compares a string against a pattern that may contain
  2667. standard * or ? wildcards.
  2668. Arguments:
  2669. wstrPattern - A pattern possibly containing wildcards
  2670. wstrStr - The string to compare against the pattern
  2671. Return Value:
  2672. TRUE when wstrStr and wstrPattern match when wildcards are expanded.
  2673. FALSE if wstrStr does not match wstrPattern.
  2674. --*/
  2675. BOOL
  2676. IsPatternMatchA (
  2677. IN PCSTR strPattern,
  2678. IN PCSTR strStr
  2679. )
  2680. {
  2681. MBCHAR chSrc, chPat;
  2682. while (*strStr) {
  2683. chSrc = _mbctolower ((MBCHAR) _mbsnextc (strStr));
  2684. chPat = _mbctolower ((MBCHAR) _mbsnextc (strPattern));
  2685. if (chPat == '*') {
  2686. // Skip all asterisks that are grouped together
  2687. while (_mbsnextc (_mbsinc (strStr)) == '*') {
  2688. strStr = _mbsinc (strStr);
  2689. }
  2690. // Check if asterisk is at the end. If so, we have a match already.
  2691. if (!_mbsnextc (_mbsinc (strPattern))) {
  2692. return TRUE;
  2693. }
  2694. // do recursive check for rest of pattern
  2695. if (IsPatternMatchA (_mbsinc (strPattern), strStr)) {
  2696. return TRUE;
  2697. }
  2698. // Allow any character and continue
  2699. strStr = _mbsinc (strStr);
  2700. continue;
  2701. }
  2702. if (chPat != '?') {
  2703. if (chSrc != chPat) {
  2704. return FALSE;
  2705. }
  2706. }
  2707. strStr = _mbsinc (strStr);
  2708. strPattern = _mbsinc (strPattern);
  2709. }
  2710. //
  2711. // Fail when there is more pattern and pattern does not end in an asterisk
  2712. //
  2713. while (_mbsnextc (strPattern) == '*') {
  2714. strPattern = _mbsinc (strPattern);
  2715. }
  2716. if (_mbsnextc (strPattern)) {
  2717. return FALSE;
  2718. }
  2719. return TRUE;
  2720. }
  2721. BOOL
  2722. IsPatternMatchW (
  2723. IN PCWSTR wstrPattern,
  2724. IN PCWSTR wstrStr
  2725. )
  2726. {
  2727. WCHAR chSrc, chPat;
  2728. while (*wstrStr) {
  2729. chSrc = towlower (*wstrStr);
  2730. chPat = towlower (*wstrPattern);
  2731. if (chPat == L'*') {
  2732. // Skip all asterisks that are grouped together
  2733. while (wstrPattern[1] == L'*')
  2734. wstrPattern++;
  2735. // Check if asterisk is at the end. If so, we have a match already.
  2736. chPat = towlower (wstrPattern[1]);
  2737. if (!chPat)
  2738. return TRUE;
  2739. // Otherwise check if next pattern char matches current char
  2740. if (chPat == chSrc || chPat == L'?') {
  2741. // do recursive check for rest of pattern
  2742. wstrPattern++;
  2743. if (IsPatternMatchW (wstrPattern, wstrStr))
  2744. return TRUE;
  2745. // no, that didn't work, stick with star
  2746. wstrPattern--;
  2747. }
  2748. //
  2749. // Allow any character and continue
  2750. //
  2751. wstrStr++;
  2752. continue;
  2753. }
  2754. if (chPat != L'?') {
  2755. //
  2756. // if next pattern character is not a question mark, src and pat
  2757. // must be identical.
  2758. //
  2759. if (chSrc != chPat)
  2760. return FALSE;
  2761. }
  2762. //
  2763. // Advance when pattern character matches string character
  2764. //
  2765. wstrPattern++;
  2766. wstrStr++;
  2767. }
  2768. //
  2769. // Fail when there is more pattern and pattern does not end in an asterisk
  2770. //
  2771. chPat = *wstrPattern;
  2772. if (chPat && (chPat != L'*' || wstrPattern[1]))
  2773. return FALSE;
  2774. return TRUE;
  2775. }
  2776. BOOL
  2777. IsPatternContainedA (
  2778. IN PCSTR Container,
  2779. IN PCSTR Contained
  2780. )
  2781. {
  2782. MBCHAR chSrc, chPat;
  2783. while (*Contained) {
  2784. chSrc = _mbctolower ((MBCHAR) _mbsnextc (Contained));
  2785. chPat = _mbctolower ((MBCHAR) _mbsnextc (Container));
  2786. if (chPat == '*') {
  2787. // Skip all asterisks that are grouped together
  2788. while (_mbsnextc (_mbsinc (Container)) == '*') {
  2789. Container = _mbsinc (Container);
  2790. }
  2791. // Check if asterisk is at the end. If so, we have a match already.
  2792. if (!_mbsnextc (_mbsinc (Container))) {
  2793. return TRUE;
  2794. }
  2795. // do recursive check for rest of pattern
  2796. if (IsPatternContainedA (_mbsinc (Container), Contained)) {
  2797. return TRUE;
  2798. }
  2799. // Allow any character and continue
  2800. Contained = _mbsinc (Contained);
  2801. continue;
  2802. } else if (chPat == '?') {
  2803. if (chSrc == '*') {
  2804. return FALSE;
  2805. }
  2806. } else {
  2807. if (chSrc != chPat) {
  2808. return FALSE;
  2809. }
  2810. }
  2811. Contained = _mbsinc (Contained);
  2812. Container = _mbsinc (Container);
  2813. }
  2814. //
  2815. // Fail when there is more pattern and pattern does not end in an asterisk
  2816. //
  2817. while (_mbsnextc (Container) == '*') {
  2818. Container = _mbsinc (Container);
  2819. }
  2820. if (_mbsnextc (Container)) {
  2821. return FALSE;
  2822. }
  2823. return TRUE;
  2824. }
  2825. BOOL
  2826. IsPatternContainedW (
  2827. IN PCWSTR Container,
  2828. IN PCWSTR Contained
  2829. )
  2830. {
  2831. while (*Contained) {
  2832. if (*Container == L'*') {
  2833. // Skip all asterisks that are grouped together
  2834. while (Container[1] == L'*') {
  2835. Container++;
  2836. }
  2837. // Check if asterisk is at the end. If so, we have a match already.
  2838. if (!Container[1]) {
  2839. return TRUE;
  2840. }
  2841. // do recursive check for rest of pattern
  2842. if (IsPatternContainedW (Container + 1, Contained)) {
  2843. return TRUE;
  2844. }
  2845. // Allow any character and continue
  2846. Contained++;
  2847. continue;
  2848. } else if (*Container == L'?') {
  2849. if (*Contained == L'*') {
  2850. return FALSE;
  2851. }
  2852. } else {
  2853. if (*Container != *Contained) {
  2854. return FALSE;
  2855. }
  2856. }
  2857. Contained++;
  2858. Container++;
  2859. }
  2860. //
  2861. // Fail when there is more pattern and pattern does not end in an asterisk
  2862. //
  2863. while (*Container == '*') {
  2864. Container++;
  2865. }
  2866. if (*Container) {
  2867. return FALSE;
  2868. }
  2869. return TRUE;
  2870. }
  2871. /*++
  2872. Routine Description:
  2873. IsPatternMatchAB compares a string against a pattern that may contain
  2874. standard * or ? wildcards. It only processes the string up to the
  2875. specified end.
  2876. Arguments:
  2877. Pattern - A pattern possibly containing wildcards
  2878. Start - The string to compare against the pattern
  2879. End - Specifies the end of Start
  2880. Return Value:
  2881. TRUE when the string between Start and End matches Pattern when wildcards are expanded.
  2882. FALSE if the pattern does not match.
  2883. --*/
  2884. BOOL
  2885. IsPatternMatchABA (
  2886. IN PCSTR Pattern,
  2887. IN PCSTR Start,
  2888. IN PCSTR End
  2889. )
  2890. {
  2891. MBCHAR chSrc, chPat;
  2892. while (*Start && Start < End) {
  2893. chSrc = _mbctolower ((MBCHAR) _mbsnextc (Start));
  2894. chPat = _mbctolower ((MBCHAR) _mbsnextc (Pattern));
  2895. if (chPat == '*') {
  2896. // Skip all asterisks that are grouped together
  2897. while (_mbsnextc (_mbsinc (Start)) == '*') {
  2898. Start = _mbsinc (Start);
  2899. }
  2900. // Check if asterisk is at the end. If so, we have a match already.
  2901. if (!_mbsnextc (_mbsinc (Pattern))) {
  2902. return TRUE;
  2903. }
  2904. // do recursive check for rest of pattern
  2905. if (IsPatternMatchABA (_mbsinc (Pattern), Start, End)) {
  2906. return TRUE;
  2907. }
  2908. // Allow any character and continue
  2909. Start = _mbsinc (Start);
  2910. continue;
  2911. }
  2912. if (chPat != '?') {
  2913. if (chSrc != chPat) {
  2914. return FALSE;
  2915. }
  2916. }
  2917. Start = _mbsinc (Start);
  2918. Pattern = _mbsinc (Pattern);
  2919. }
  2920. //
  2921. // Fail when there is more pattern and pattern does not end in an asterisk
  2922. //
  2923. while (_mbsnextc (Pattern) == '*') {
  2924. Pattern = _mbsinc (Pattern);
  2925. }
  2926. if (_mbsnextc (Pattern)) {
  2927. return FALSE;
  2928. }
  2929. return TRUE;
  2930. }
  2931. BOOL
  2932. IsPatternMatchABW (
  2933. IN PCWSTR Pattern,
  2934. IN PCWSTR Start,
  2935. IN PCWSTR End
  2936. )
  2937. {
  2938. WCHAR chSrc, chPat;
  2939. while (*Start && Start < End) {
  2940. chSrc = towlower (*Start);
  2941. chPat = towlower (*Pattern);
  2942. if (chPat == L'*') {
  2943. // Skip all asterisks that are grouped together
  2944. while (Pattern[1] == L'*') {
  2945. Pattern++;
  2946. }
  2947. // Check if asterisk is at the end. If so, we have a match already.
  2948. chPat = towlower (Pattern[1]);
  2949. if (!chPat) {
  2950. return TRUE;
  2951. }
  2952. // Otherwise check if next pattern char matches current char
  2953. if (chPat == chSrc || chPat == L'?') {
  2954. // do recursive check for rest of pattern
  2955. Pattern++;
  2956. if (IsPatternMatchABW (Pattern, Start, End)) {
  2957. return TRUE;
  2958. }
  2959. // no, that didn't work, stick with star
  2960. Pattern--;
  2961. }
  2962. //
  2963. // Allow any character and continue
  2964. //
  2965. Start++;
  2966. continue;
  2967. }
  2968. if (chPat != L'?') {
  2969. //
  2970. // if next pattern character is not a question mark, src and pat
  2971. // must be identical.
  2972. //
  2973. if (chSrc != chPat) {
  2974. return FALSE;
  2975. }
  2976. }
  2977. //
  2978. // Advance when pattern character matches string character
  2979. //
  2980. Pattern++;
  2981. Start++;
  2982. }
  2983. //
  2984. // Fail when there is more pattern and pattern does not end in an asterisk
  2985. //
  2986. chPat = *Pattern;
  2987. if (chPat && (chPat != L'*' || Pattern[1])) {
  2988. return FALSE;
  2989. }
  2990. return TRUE;
  2991. }
  2992. /*++
  2993. Routine Description:
  2994. IsPatternMatchEx compares a string against a pattern that may contain
  2995. any of the following expressions:
  2996. * - Specifies zero or more characters
  2997. ? - Specifies any one character
  2998. *[set] - Specifies zero or more characters in set
  2999. ?[set] - Specifies any one character in set
  3000. *[n:set] - Specifies zero to n characters in set
  3001. ?[n:set] - Specifies exactly n characters in set
  3002. *[!(set)] - Specifies zero or more characters not in set
  3003. ?[!(set)] - Specifies one character not in set
  3004. *[n:!(set)] - Specifies zero to n characters not in set
  3005. ?[n:!(set)] - Specifies exactly n characters not in set
  3006. *[set1,!(set2)] - Specifies zero or more characters in set1 and
  3007. not in set2. It is assumed that set1 and set2
  3008. overlap.
  3009. ?[set1,!(set2)] - Specifies one character in set1 and not in set2.
  3010. *[n:set1,!(set2)] - Specifies zero to n characters in set1 and not
  3011. in set 2.
  3012. ?[n:set1,!(set2)] - Specifies exactly n characters in set1 and not
  3013. in set 2.
  3014. set, set1 and set2 are specified as follows:
  3015. a - Specifies a single character
  3016. a-b - Specifies a character range
  3017. a,b - Specifies two characters
  3018. a-b,c-d - Specifies two character ranges
  3019. a,b-c - Specifies a single character and a character range
  3020. etc...
  3021. Patterns can be joined by surrounding the entire expression in
  3022. greater than/less than braces.
  3023. Because of the syntax characters, the following characters must be
  3024. escaped by preceeding the character with a caret (^):
  3025. ^? ^[ ^- ^< ^! ^^
  3026. ^* ^] ^: ^> ^,
  3027. Here are some examples:
  3028. To specify any GUID:
  3029. {?[8:0-9,a-f]-?[4:0-9,a-f]-?[4:0-9,a-f]-?[4:0-9,a-f]-?[12:0-9,a-f]}
  3030. To specify a 32-bit hexadecimal number:
  3031. <0x*[8:0-9,a-f]><0*[7:0-9,a-f]h><?[1-9]*[7:0-9,a-f]h>
  3032. Arguments:
  3033. Pattern - A pattern possibly containing wildcards
  3034. Start - The string to compare against the pattern
  3035. End - Specifies the end of Start
  3036. Return Value:
  3037. TRUE when the string between Start and End matches Pattern when wildcards are expanded.
  3038. FALSE if the pattern does not match.
  3039. --*/
  3040. BOOL
  3041. IsPatternMatchExA (
  3042. IN PCSTR Pattern,
  3043. IN PCSTR String
  3044. )
  3045. {
  3046. PPARSEDPATTERNA Handle;
  3047. BOOL b;
  3048. Handle = CreateParsedPatternA (Pattern);
  3049. if (!Handle) {
  3050. return FALSE;
  3051. }
  3052. b = TestParsedPatternA (Handle, String);
  3053. DestroyParsedPatternA (Handle);
  3054. return b;
  3055. }
  3056. BOOL
  3057. IsPatternMatchExW (
  3058. IN PCWSTR Pattern,
  3059. IN PCWSTR String
  3060. )
  3061. {
  3062. PPARSEDPATTERNW Handle;
  3063. BOOL b;
  3064. Handle = CreateParsedPatternW (Pattern);
  3065. if (!Handle) {
  3066. return FALSE;
  3067. }
  3068. b = TestParsedPatternW (Handle, String);
  3069. DestroyParsedPatternW (Handle);
  3070. return b;
  3071. }
  3072. /*++
  3073. Routine Description:
  3074. IsPatternMatchExAB compares a string against a pattern that may contain
  3075. any of the following expressions:
  3076. * - Specifies zero or more characters
  3077. ? - Specifies any one character
  3078. *[set] - Specifies zero or more characters in set
  3079. ?[set] - Specifies any one character in set
  3080. *[n:set] - Specifies zero to n characters in set
  3081. ?[n:set] - Specifies exactly n characters in set
  3082. *[!(set)] - Specifies zero or more characters not in set
  3083. ?[!(set)] - Specifies one character not in set
  3084. *[n:!(set)] - Specifies zero to n characters not in set
  3085. ?[n:!(set)] - Specifies exactly n characters not in set
  3086. *[set1,!(set2)] - Specifies zero or more characters in set1 and
  3087. not in set2. It is assumed that set1 and set2
  3088. overlap.
  3089. ?[set1,!(set2)] - Specifies one character in set1 and not in set2.
  3090. *[n:set1,!(set2)] - Specifies zero to n characters in set1 and not
  3091. in set 2.
  3092. ?[n:set1,!(set2)] - Specifies exactly n characters in set1 and not
  3093. in set 2.
  3094. set, set1 and set2 are specified as follows:
  3095. a - Specifies a single character
  3096. a-b - Specifies a character range
  3097. a,b - Specifies two characters
  3098. a-b,c-d - Specifies two character ranges
  3099. a,b-c - Specifies a single character and a character range
  3100. etc...
  3101. Patterns can be joined by surrounding the entire expression in
  3102. greater than/less than braces.
  3103. Because of the syntax characters, the following characters must be
  3104. escaped by preceeding the character with a caret (^):
  3105. ^? ^[ ^- ^< ^! ^^
  3106. ^* ^] ^: ^> ^,
  3107. Here are some examples:
  3108. To specify any GUID:
  3109. {?[8:0-9,a-f]-?[4:0-9,a-f]-?[4:0-9,a-f]-?[4:0-9,a-f]-?[12:0-9,a-f]}
  3110. To specify a 32-bit hexadecimal number:
  3111. <0x*[8:0-9,a-f]><0*[7:0-9,a-f]h><?[1-9]*[7:0-9,a-f]h>
  3112. Arguments:
  3113. Pattern - A pattern possibly containing wildcards
  3114. Start - The string to compare against the pattern
  3115. End - Specifies the end of Start
  3116. Return Value:
  3117. TRUE when the string between Start and End matches Pattern when wildcards are expanded.
  3118. FALSE if the pattern does not match.
  3119. --*/
  3120. BOOL
  3121. IsPatternMatchExABA (
  3122. IN PCSTR Pattern,
  3123. IN PCSTR Start,
  3124. IN PCSTR End
  3125. )
  3126. {
  3127. PPARSEDPATTERNA Handle;
  3128. BOOL b;
  3129. Handle = CreateParsedPatternA (Pattern);
  3130. if (!Handle) {
  3131. return FALSE;
  3132. }
  3133. b = TestParsedPatternABA (Handle, Start, End);
  3134. DestroyParsedPatternA (Handle);
  3135. return b;
  3136. }
  3137. BOOL
  3138. IsPatternMatchExABW (
  3139. IN PCWSTR Pattern,
  3140. IN PCWSTR Start,
  3141. IN PCWSTR End
  3142. )
  3143. {
  3144. PPARSEDPATTERNW Handle;
  3145. BOOL b;
  3146. Handle = CreateParsedPatternW (Pattern);
  3147. if (!Handle) {
  3148. return FALSE;
  3149. }
  3150. b = TestParsedPatternABW (Handle, Start, End);
  3151. DestroyParsedPatternW (Handle);
  3152. return b;
  3153. }
  3154. BOOL
  3155. pTestSetsA (
  3156. IN PCSTR Container,
  3157. IN PCSTR Contained,
  3158. IN BOOL ExcludeMode
  3159. )
  3160. {
  3161. MBCHAR ch;
  3162. if (ExcludeMode) {
  3163. if (!Contained) {
  3164. return TRUE;
  3165. }
  3166. if (!Container) {
  3167. return FALSE;
  3168. }
  3169. } else {
  3170. if (!Container) {
  3171. return TRUE;
  3172. }
  3173. if (!Contained) {
  3174. return FALSE;
  3175. }
  3176. }
  3177. while (*Contained) {
  3178. ch = _mbsnextc (Contained);
  3179. if (!pTestSetA (ch, Container, NULL)) {
  3180. return FALSE;
  3181. }
  3182. Contained = _mbsinc (Contained);
  3183. }
  3184. return TRUE;
  3185. }
  3186. BOOL
  3187. pTestSetsW (
  3188. IN PCWSTR Container,
  3189. IN PCWSTR Contained,
  3190. IN BOOL ExcludeMode
  3191. )
  3192. {
  3193. if (ExcludeMode) {
  3194. if (!Contained) {
  3195. return TRUE;
  3196. }
  3197. if (!Container) {
  3198. return FALSE;
  3199. }
  3200. } else {
  3201. if (!Container) {
  3202. return TRUE;
  3203. }
  3204. if (!Contained) {
  3205. return FALSE;
  3206. }
  3207. }
  3208. while (*Contained) {
  3209. if (!pTestSetW (*Contained, Container, NULL)) {
  3210. return FALSE;
  3211. }
  3212. Contained ++;
  3213. }
  3214. return TRUE;
  3215. }
  3216. BOOL
  3217. pMatchSegmentA (
  3218. IN PSEGMENTA Source,
  3219. IN PSEGMENTA Destination
  3220. )
  3221. {
  3222. switch (Source->Type) {
  3223. case SEGMENTTYPE_OPTIONAL:
  3224. switch (Destination->Type) {
  3225. case SEGMENTTYPE_OPTIONAL:
  3226. if (Source->Wildcard.MaxLen) {
  3227. if ((Destination->Wildcard.MaxLen == 0) ||
  3228. (Source->Wildcard.MaxLen < Destination->Wildcard.MaxLen)
  3229. ) {
  3230. return FALSE;
  3231. }
  3232. }
  3233. if (!pTestSetsA (
  3234. Source->Wildcard.IncludeSet,
  3235. Destination->Wildcard.IncludeSet,
  3236. FALSE
  3237. )) {
  3238. return FALSE;
  3239. }
  3240. if (!pTestSetsA (
  3241. Destination->Wildcard.ExcludeSet,
  3242. Source->Wildcard.ExcludeSet,
  3243. TRUE
  3244. )) {
  3245. return FALSE;
  3246. }
  3247. return TRUE;
  3248. case SEGMENTTYPE_REQUIRED:
  3249. if (Source->Wildcard.MaxLen) {
  3250. if (Source->Wildcard.MaxLen < Destination->Wildcard.MaxLen) {
  3251. return FALSE;
  3252. }
  3253. }
  3254. if (!pTestSetsA (
  3255. Source->Wildcard.IncludeSet,
  3256. Destination->Wildcard.IncludeSet,
  3257. FALSE
  3258. )) {
  3259. return FALSE;
  3260. }
  3261. if (!pTestSetsA (
  3262. Destination->Wildcard.ExcludeSet,
  3263. Source->Wildcard.ExcludeSet,
  3264. TRUE
  3265. )) {
  3266. return FALSE;
  3267. }
  3268. return TRUE;
  3269. case SEGMENTTYPE_EXACTMATCH:
  3270. if (!pTestSetA (
  3271. _mbsnextc (Destination->Exact.LowerCasePhrase),
  3272. Source->Wildcard.IncludeSet,
  3273. Source->Wildcard.ExcludeSet
  3274. )) {
  3275. return FALSE;
  3276. }
  3277. return TRUE;
  3278. default:
  3279. return FALSE;
  3280. }
  3281. break;
  3282. case SEGMENTTYPE_REQUIRED:
  3283. switch (Destination->Type) {
  3284. case SEGMENTTYPE_OPTIONAL:
  3285. return FALSE;
  3286. case SEGMENTTYPE_REQUIRED:
  3287. if (!pTestSetsA (
  3288. Source->Wildcard.IncludeSet,
  3289. Destination->Wildcard.IncludeSet,
  3290. FALSE
  3291. )) {
  3292. return FALSE;
  3293. }
  3294. if (!pTestSetsA (
  3295. Destination->Wildcard.ExcludeSet,
  3296. Source->Wildcard.ExcludeSet,
  3297. TRUE
  3298. )) {
  3299. return FALSE;
  3300. }
  3301. return TRUE;
  3302. case SEGMENTTYPE_EXACTMATCH:
  3303. if (!pTestSetA (
  3304. _mbsnextc (Destination->Exact.LowerCasePhrase),
  3305. Source->Wildcard.IncludeSet,
  3306. Source->Wildcard.ExcludeSet
  3307. )) {
  3308. return FALSE;
  3309. }
  3310. return TRUE;
  3311. default:
  3312. return FALSE;
  3313. }
  3314. break;
  3315. case SEGMENTTYPE_EXACTMATCH:
  3316. switch (Destination->Type) {
  3317. case SEGMENTTYPE_OPTIONAL:
  3318. return FALSE;
  3319. case SEGMENTTYPE_REQUIRED:
  3320. return FALSE;
  3321. case SEGMENTTYPE_EXACTMATCH:
  3322. if (_mbsnextc (Destination->Exact.LowerCasePhrase) != _mbsnextc (Source->Exact.LowerCasePhrase)) {
  3323. return FALSE;
  3324. }
  3325. return TRUE;
  3326. default:
  3327. return FALSE;
  3328. }
  3329. break;
  3330. default:
  3331. return FALSE;
  3332. }
  3333. return FALSE;
  3334. }
  3335. BOOL
  3336. pMatchSegmentW (
  3337. IN PSEGMENTW Source,
  3338. IN PSEGMENTW Destination
  3339. )
  3340. {
  3341. switch (Source->Type) {
  3342. case SEGMENTTYPE_OPTIONAL:
  3343. switch (Destination->Type) {
  3344. case SEGMENTTYPE_OPTIONAL:
  3345. if (Source->Wildcard.MaxLen) {
  3346. if ((Destination->Wildcard.MaxLen == 0) ||
  3347. (Source->Wildcard.MaxLen < Destination->Wildcard.MaxLen)
  3348. ) {
  3349. return FALSE;
  3350. }
  3351. }
  3352. if (!pTestSetsW (
  3353. Source->Wildcard.IncludeSet,
  3354. Destination->Wildcard.IncludeSet,
  3355. FALSE
  3356. )) {
  3357. return FALSE;
  3358. }
  3359. if (!pTestSetsW (
  3360. Destination->Wildcard.ExcludeSet,
  3361. Source->Wildcard.ExcludeSet,
  3362. TRUE
  3363. )) {
  3364. return FALSE;
  3365. }
  3366. return TRUE;
  3367. case SEGMENTTYPE_REQUIRED:
  3368. if (Source->Wildcard.MaxLen) {
  3369. if (Source->Wildcard.MaxLen < Destination->Wildcard.MaxLen) {
  3370. return FALSE;
  3371. }
  3372. }
  3373. if (!pTestSetsW (
  3374. Source->Wildcard.IncludeSet,
  3375. Destination->Wildcard.IncludeSet,
  3376. FALSE
  3377. )) {
  3378. return FALSE;
  3379. }
  3380. if (!pTestSetsW (
  3381. Destination->Wildcard.ExcludeSet,
  3382. Source->Wildcard.ExcludeSet,
  3383. TRUE
  3384. )) {
  3385. return FALSE;
  3386. }
  3387. return TRUE;
  3388. case SEGMENTTYPE_EXACTMATCH:
  3389. if (!pTestSetW (
  3390. *Destination->Exact.LowerCasePhrase,
  3391. Source->Wildcard.IncludeSet,
  3392. Source->Wildcard.ExcludeSet
  3393. )) {
  3394. return FALSE;
  3395. }
  3396. return TRUE;
  3397. default:
  3398. return FALSE;
  3399. }
  3400. break;
  3401. case SEGMENTTYPE_REQUIRED:
  3402. switch (Destination->Type) {
  3403. case SEGMENTTYPE_OPTIONAL:
  3404. return FALSE;
  3405. case SEGMENTTYPE_REQUIRED:
  3406. if (!pTestSetsW (
  3407. Source->Wildcard.IncludeSet,
  3408. Destination->Wildcard.IncludeSet,
  3409. FALSE
  3410. )) {
  3411. return FALSE;
  3412. }
  3413. if (!pTestSetsW (
  3414. Destination->Wildcard.ExcludeSet,
  3415. Source->Wildcard.ExcludeSet,
  3416. TRUE
  3417. )) {
  3418. return FALSE;
  3419. }
  3420. return TRUE;
  3421. case SEGMENTTYPE_EXACTMATCH:
  3422. if (!pTestSetW (
  3423. *Destination->Exact.LowerCasePhrase,
  3424. Source->Wildcard.IncludeSet,
  3425. Source->Wildcard.ExcludeSet
  3426. )) {
  3427. return FALSE;
  3428. }
  3429. return TRUE;
  3430. default:
  3431. return FALSE;
  3432. }
  3433. break;
  3434. case SEGMENTTYPE_EXACTMATCH:
  3435. switch (Destination->Type) {
  3436. case SEGMENTTYPE_OPTIONAL:
  3437. return FALSE;
  3438. case SEGMENTTYPE_REQUIRED:
  3439. return FALSE;
  3440. case SEGMENTTYPE_EXACTMATCH:
  3441. if (*Destination->Exact.LowerCasePhrase != *Source->Exact.LowerCasePhrase) {
  3442. return FALSE;
  3443. }
  3444. return TRUE;
  3445. default:
  3446. return FALSE;
  3447. }
  3448. break;
  3449. default:
  3450. return FALSE;
  3451. }
  3452. return FALSE;
  3453. }
  3454. BOOL
  3455. pIsOneParsedPatternContainedA (
  3456. IN PPATTERNPROPSA Container,
  3457. IN UINT StartContainer,
  3458. IN PPATTERNPROPSA Contained,
  3459. IN UINT StartContained
  3460. )
  3461. {
  3462. UINT indexContainer = StartContainer;
  3463. UINT indexContained = StartContained;
  3464. PSEGMENTA containerSeg, containedSeg;
  3465. if (StartContainer == Container->SegmentCount) {
  3466. return FALSE;
  3467. }
  3468. while (indexContained < Contained->SegmentCount) {
  3469. containerSeg = &Container->Segment [indexContainer];
  3470. containedSeg = &Contained->Segment [indexContained];
  3471. if (containerSeg->Type == SEGMENTTYPE_OPTIONAL) {
  3472. // see if we can match contained segment
  3473. if (!pMatchSegmentA (containerSeg, containedSeg)) {
  3474. indexContainer ++;
  3475. if (indexContainer == Container->SegmentCount) {
  3476. return FALSE;
  3477. }
  3478. continue;
  3479. }
  3480. if (pIsOneParsedPatternContainedA (
  3481. Container,
  3482. indexContainer + 1,
  3483. Contained,
  3484. indexContained
  3485. )) {
  3486. return TRUE;
  3487. }
  3488. indexContained ++;
  3489. continue;
  3490. } else if (containerSeg->Type == SEGMENTTYPE_REQUIRED) {
  3491. if (!pMatchSegmentA (containerSeg, containedSeg)) {
  3492. return FALSE;
  3493. }
  3494. } else {
  3495. if (!pMatchSegmentA (containerSeg, containedSeg)) {
  3496. return FALSE;
  3497. }
  3498. }
  3499. indexContainer ++;
  3500. indexContained ++;
  3501. }
  3502. //
  3503. // Fail when there is more pattern and pattern does not end in an asterisk
  3504. //
  3505. while (indexContainer < Container->SegmentCount) {
  3506. containerSeg = &Container->Segment [indexContainer];
  3507. if (containerSeg->Type != SEGMENTTYPE_OPTIONAL) {
  3508. return FALSE;
  3509. }
  3510. indexContainer ++;
  3511. }
  3512. return TRUE;
  3513. }
  3514. BOOL
  3515. pIsOneParsedPatternContainedW (
  3516. IN PPATTERNPROPSW Container,
  3517. IN UINT StartContainer,
  3518. IN PPATTERNPROPSW Contained,
  3519. IN UINT StartContained
  3520. )
  3521. {
  3522. UINT indexContainer = StartContainer;
  3523. UINT indexContained = StartContained;
  3524. PSEGMENTW containerSeg, containedSeg;
  3525. if (StartContainer == Container->SegmentCount) {
  3526. return FALSE;
  3527. }
  3528. while (indexContained < Contained->SegmentCount) {
  3529. containerSeg = &Container->Segment [indexContainer];
  3530. containedSeg = &Contained->Segment [indexContained];
  3531. if (containerSeg->Type == SEGMENTTYPE_OPTIONAL) {
  3532. // see if we can match contained segment
  3533. if (!pMatchSegmentW (containerSeg, containedSeg)) {
  3534. indexContainer ++;
  3535. if (indexContainer == Container->SegmentCount) {
  3536. return FALSE;
  3537. }
  3538. continue;
  3539. }
  3540. if (pIsOneParsedPatternContainedW (
  3541. Container,
  3542. indexContainer + 1,
  3543. Contained,
  3544. indexContained
  3545. )) {
  3546. return TRUE;
  3547. }
  3548. indexContained ++;
  3549. continue;
  3550. } else if (containerSeg->Type == SEGMENTTYPE_REQUIRED) {
  3551. if (!pMatchSegmentW (containerSeg, containedSeg)) {
  3552. return FALSE;
  3553. }
  3554. } else {
  3555. if (!pMatchSegmentW (containerSeg, containedSeg)) {
  3556. return FALSE;
  3557. }
  3558. }
  3559. indexContainer ++;
  3560. indexContained ++;
  3561. }
  3562. //
  3563. // Fail when there is more pattern and pattern does not end in an asterisk
  3564. //
  3565. while (indexContainer < Container->SegmentCount) {
  3566. containerSeg = &Container->Segment [indexContainer];
  3567. if (containerSeg->Type != SEGMENTTYPE_OPTIONAL) {
  3568. return FALSE;
  3569. }
  3570. indexContainer ++;
  3571. }
  3572. return TRUE;
  3573. }
  3574. BOOL
  3575. IsExplodedParsedPatternContainedExA (
  3576. IN PPARSEDPATTERNA Container,
  3577. IN PPARSEDPATTERNA Contained
  3578. )
  3579. {
  3580. UINT u1, u2;
  3581. BOOL b = FALSE;
  3582. for (u1 = 0 ; u1 < Contained->PatternCount ; u1++) {
  3583. b = FALSE;
  3584. for (u2 = 0 ; u2 < Container->PatternCount ; u2++) {
  3585. b = pIsOneParsedPatternContainedA (
  3586. &Container->Pattern[u2],
  3587. 0,
  3588. &Contained->Pattern[u1],
  3589. 0
  3590. );
  3591. if (b) break;
  3592. }
  3593. if (!b) {
  3594. return FALSE;
  3595. }
  3596. }
  3597. return TRUE;
  3598. }
  3599. BOOL
  3600. IsExplodedParsedPatternContainedExW (
  3601. IN PPARSEDPATTERNW Container,
  3602. IN PPARSEDPATTERNW Contained
  3603. )
  3604. {
  3605. UINT u1, u2;
  3606. BOOL b = FALSE;
  3607. for (u1 = 0 ; u1 < Contained->PatternCount ; u1++) {
  3608. b = FALSE;
  3609. for (u2 = 0 ; u2 < Container->PatternCount ; u2++) {
  3610. b = pIsOneParsedPatternContainedW (
  3611. &Container->Pattern[u2],
  3612. 0,
  3613. &Contained->Pattern[u1],
  3614. 0
  3615. );
  3616. if (b) break;
  3617. }
  3618. if (!b) {
  3619. return FALSE;
  3620. }
  3621. }
  3622. return TRUE;
  3623. }
  3624. PPARSEDPATTERNA
  3625. ExplodeParsedPatternA (
  3626. IN PPARSEDPATTERNA Pattern
  3627. )
  3628. {
  3629. PMHANDLE pool;
  3630. PPARSEDPATTERNA pattern;
  3631. PPATTERNPROPSA oldProps, newProps;
  3632. PSEGMENTA oldSeg, newSeg;
  3633. UINT i, j, k, newPropsSize, charCountTmp, oldSegIndex, byteIndex;
  3634. BOOL result = TRUE;
  3635. pool = PmCreateNamedPool ("Parsed Pattern");
  3636. __try {
  3637. pattern = (PPARSEDPATTERNA) PmGetAlignedMemory (pool, sizeof (PARSEDPATTERNA));
  3638. ZeroMemory (pattern, sizeof (PARSEDPATTERNA));
  3639. pattern->PatternCount = Pattern->PatternCount;
  3640. pattern->Pool = pool;
  3641. pattern->Pattern = (PPATTERNPROPSA) PmGetAlignedMemory (
  3642. pool,
  3643. pattern->PatternCount * sizeof (PATTERNPROPSA)
  3644. );
  3645. for (i=0; i<pattern->PatternCount; i++) {
  3646. oldProps = &Pattern->Pattern[i];
  3647. newProps = &pattern->Pattern[i];
  3648. ZeroMemory (newProps, sizeof (PATTERNPROPSA));
  3649. // now let's walk oldProps to see how many segments we are
  3650. // going to need
  3651. newPropsSize = 0;
  3652. for (j=0; j<oldProps->SegmentCount; j++) {
  3653. oldSeg = &oldProps->Segment[j];
  3654. switch (oldSeg->Type) {
  3655. case SEGMENTTYPE_EXACTMATCH:
  3656. charCountTmp = CharCountA (oldSeg->Exact.LowerCasePhrase);
  3657. newPropsSize += (charCountTmp?charCountTmp:1);
  3658. break;
  3659. case SEGMENTTYPE_REQUIRED:
  3660. newPropsSize += oldSeg->Wildcard.MaxLen;
  3661. break;
  3662. case SEGMENTTYPE_OPTIONAL:
  3663. newPropsSize ++;
  3664. break;
  3665. default:
  3666. result = FALSE;
  3667. __leave;
  3668. }
  3669. }
  3670. // now we allocate the required segments
  3671. newProps->SegmentCount = newPropsSize;
  3672. newProps->Segment = (PSEGMENTA) PmGetAlignedMemory (
  3673. pool,
  3674. newProps->SegmentCount * sizeof (SEGMENTA)
  3675. );
  3676. // now let's walk oldProps again and fill newProps segments.
  3677. k = 0;
  3678. newSeg = &newProps->Segment[k];
  3679. for (j=0; j<oldProps->SegmentCount; j++) {
  3680. oldSeg = &oldProps->Segment[j];
  3681. ZeroMemory (newSeg, sizeof (SEGMENTA));
  3682. switch (oldSeg->Type) {
  3683. case SEGMENTTYPE_EXACTMATCH:
  3684. oldSegIndex = CharCountA (oldSeg->Exact.LowerCasePhrase);
  3685. byteIndex = oldSeg->Exact.PhraseBytes;
  3686. if (!oldSegIndex) {
  3687. ZeroMemory (newSeg, sizeof (SEGMENTA));
  3688. newSeg->Type = oldSeg->Type;
  3689. newSeg->Exact.LowerCasePhrase = (PCSTR) PmGetAlignedMemory (
  3690. pool, sizeof(CHAR)
  3691. );
  3692. ((PSTR)newSeg->Exact.LowerCasePhrase) [0] = 0;
  3693. newSeg->Exact.PhraseBytes = 0;
  3694. } else {
  3695. while (oldSegIndex) {
  3696. ZeroMemory (newSeg, sizeof (SEGMENTA));
  3697. newSeg->Type = oldSeg->Type;
  3698. newSeg->Exact.LowerCasePhrase = (PCSTR) PmGetAlignedMemory (
  3699. pool, 3 * sizeof(CHAR)
  3700. );
  3701. if (IsLeadByte (oldSeg->Exact.LowerCasePhrase [oldSeg->Exact.PhraseBytes - byteIndex])) {
  3702. ((PSTR)newSeg->Exact.LowerCasePhrase)[0] = oldSeg->Exact.LowerCasePhrase [oldSeg->Exact.PhraseBytes - byteIndex];
  3703. byteIndex --;
  3704. ((PSTR)newSeg->Exact.LowerCasePhrase)[1] = oldSeg->Exact.LowerCasePhrase [oldSeg->Exact.PhraseBytes - byteIndex];
  3705. byteIndex --;
  3706. ((PSTR)newSeg->Exact.LowerCasePhrase)[2] = 0;
  3707. newSeg->Exact.PhraseBytes = 2;
  3708. } else {
  3709. ((PSTR)newSeg->Exact.LowerCasePhrase)[0] = oldSeg->Exact.LowerCasePhrase [oldSeg->Exact.PhraseBytes - byteIndex];
  3710. byteIndex --;
  3711. ((PSTR)newSeg->Exact.LowerCasePhrase)[1] = 0;
  3712. newSeg->Exact.PhraseBytes = 1;
  3713. }
  3714. oldSegIndex --;
  3715. k++;
  3716. newSeg = &newProps->Segment[k];
  3717. }
  3718. }
  3719. break;
  3720. case SEGMENTTYPE_REQUIRED:
  3721. oldSegIndex = oldSeg->Wildcard.MaxLen;
  3722. while (oldSegIndex) {
  3723. ZeroMemory (newSeg, sizeof (SEGMENTA));
  3724. newSeg->Type = oldSeg->Type;
  3725. newSeg->Wildcard.MaxLen = 1;
  3726. if (oldSeg->Wildcard.IncludeSet) {
  3727. newSeg->Wildcard.IncludeSet = PmDuplicateStringA (pool, oldSeg->Wildcard.IncludeSet);
  3728. }
  3729. if (oldSeg->Wildcard.ExcludeSet) {
  3730. newSeg->Wildcard.ExcludeSet = PmDuplicateStringA (pool, oldSeg->Wildcard.ExcludeSet);
  3731. }
  3732. oldSegIndex --;
  3733. k++;
  3734. newSeg = &newProps->Segment[k];
  3735. }
  3736. break;
  3737. case SEGMENTTYPE_OPTIONAL:
  3738. ZeroMemory (newSeg, sizeof (SEGMENTA));
  3739. newSeg->Type = oldSeg->Type;
  3740. newSeg->Wildcard.MaxLen = oldSeg->Wildcard.MaxLen;
  3741. if (oldSeg->Wildcard.IncludeSet) {
  3742. newSeg->Wildcard.IncludeSet = PmDuplicateStringA (pool, oldSeg->Wildcard.IncludeSet);
  3743. }
  3744. if (oldSeg->Wildcard.ExcludeSet) {
  3745. newSeg->Wildcard.ExcludeSet = PmDuplicateStringA (pool, oldSeg->Wildcard.ExcludeSet);
  3746. }
  3747. k++;
  3748. newSeg = &newProps->Segment[k];
  3749. break;
  3750. default:
  3751. result = FALSE;
  3752. __leave;
  3753. }
  3754. }
  3755. }
  3756. }
  3757. __finally {
  3758. if (!result) {
  3759. PmDestroyPool (pool);
  3760. pattern = NULL;
  3761. }
  3762. }
  3763. return pattern;
  3764. }
  3765. PPARSEDPATTERNW
  3766. ExplodeParsedPatternW (
  3767. IN PPARSEDPATTERNW Pattern
  3768. )
  3769. {
  3770. PMHANDLE pool;
  3771. PPARSEDPATTERNW pattern;
  3772. PPATTERNPROPSW oldProps, newProps;
  3773. PSEGMENTW oldSeg, newSeg;
  3774. UINT i, j, k, newPropsSize, charCountTmp, oldSegIndex;
  3775. BOOL result = TRUE;
  3776. pool = PmCreateNamedPool ("Parsed Pattern");
  3777. __try {
  3778. pattern = (PPARSEDPATTERNW) PmGetAlignedMemory (pool, sizeof (PARSEDPATTERNW));
  3779. ZeroMemory (pattern, sizeof (PARSEDPATTERNW));
  3780. pattern->PatternCount = Pattern->PatternCount;
  3781. pattern->Pool = pool;
  3782. pattern->Pattern = (PPATTERNPROPSW) PmGetAlignedMemory (
  3783. pool,
  3784. pattern->PatternCount * sizeof (PATTERNPROPSW)
  3785. );
  3786. for (i=0; i<pattern->PatternCount; i++) {
  3787. oldProps = &Pattern->Pattern[i];
  3788. newProps = &pattern->Pattern[i];
  3789. ZeroMemory (newProps, sizeof (PATTERNPROPSW));
  3790. // now let's walk oldProps to see how many segments we are
  3791. // going to need
  3792. newPropsSize = 0;
  3793. for (j=0; j<oldProps->SegmentCount; j++) {
  3794. oldSeg = &oldProps->Segment[j];
  3795. switch (oldSeg->Type) {
  3796. case SEGMENTTYPE_EXACTMATCH:
  3797. charCountTmp = CharCountW (oldSeg->Exact.LowerCasePhrase);
  3798. newPropsSize += (charCountTmp?charCountTmp:1);
  3799. break;
  3800. case SEGMENTTYPE_REQUIRED:
  3801. newPropsSize += oldSeg->Wildcard.MaxLen;
  3802. break;
  3803. case SEGMENTTYPE_OPTIONAL:
  3804. newPropsSize ++;
  3805. break;
  3806. default:
  3807. result = FALSE;
  3808. __leave;
  3809. }
  3810. }
  3811. // now we allocate the required segments
  3812. newProps->SegmentCount = newPropsSize;
  3813. newProps->Segment = (PSEGMENTW) PmGetAlignedMemory (
  3814. pool,
  3815. newProps->SegmentCount * sizeof (SEGMENTW)
  3816. );
  3817. // now let's walk oldProps again and fill newProps segments.
  3818. k = 0;
  3819. newSeg = &newProps->Segment[k];
  3820. for (j=0; j<oldProps->SegmentCount; j++) {
  3821. oldSeg = &oldProps->Segment[j];
  3822. ZeroMemory (newSeg, sizeof (SEGMENTW));
  3823. switch (oldSeg->Type) {
  3824. case SEGMENTTYPE_EXACTMATCH:
  3825. oldSegIndex = CharCountW (oldSeg->Exact.LowerCasePhrase);
  3826. if (!oldSegIndex) {
  3827. ZeroMemory (newSeg, sizeof (SEGMENTA));
  3828. newSeg->Type = oldSeg->Type;
  3829. newSeg->Exact.LowerCasePhrase = (PCWSTR) PmGetAlignedMemory (
  3830. pool, sizeof(WCHAR)
  3831. );
  3832. ((PWSTR)newSeg->Exact.LowerCasePhrase) [0] = 0;
  3833. newSeg->Exact.PhraseBytes = 0;
  3834. } else {
  3835. while (oldSegIndex) {
  3836. ZeroMemory (newSeg, sizeof (SEGMENTW));
  3837. newSeg->Type = oldSeg->Type;
  3838. newSeg->Exact.LowerCasePhrase = (PCWSTR) PmGetAlignedMemory (
  3839. pool, 2 * sizeof(WCHAR)
  3840. );
  3841. ((PWSTR)newSeg->Exact.LowerCasePhrase)[0] = oldSeg->Exact.LowerCasePhrase [(oldSeg->Exact.PhraseBytes / sizeof(WCHAR)) - oldSegIndex];
  3842. ((PWSTR)newSeg->Exact.LowerCasePhrase)[1] = 0;
  3843. oldSegIndex --;
  3844. k++;
  3845. newSeg = &newProps->Segment[k];
  3846. }
  3847. }
  3848. break;
  3849. case SEGMENTTYPE_REQUIRED:
  3850. oldSegIndex = oldSeg->Wildcard.MaxLen;
  3851. while (oldSegIndex) {
  3852. ZeroMemory (newSeg, sizeof (SEGMENTW));
  3853. newSeg->Type = oldSeg->Type;
  3854. newSeg->Wildcard.MaxLen = 1;
  3855. if (oldSeg->Wildcard.IncludeSet) {
  3856. newSeg->Wildcard.IncludeSet = PmDuplicateStringW (pool, oldSeg->Wildcard.IncludeSet);
  3857. }
  3858. if (oldSeg->Wildcard.ExcludeSet) {
  3859. newSeg->Wildcard.ExcludeSet = PmDuplicateStringW (pool, oldSeg->Wildcard.ExcludeSet);
  3860. }
  3861. oldSegIndex --;
  3862. k++;
  3863. newSeg = &newProps->Segment[k];
  3864. }
  3865. break;
  3866. case SEGMENTTYPE_OPTIONAL:
  3867. ZeroMemory (newSeg, sizeof (SEGMENTW));
  3868. newSeg->Type = oldSeg->Type;
  3869. newSeg->Wildcard.MaxLen = oldSeg->Wildcard.MaxLen;
  3870. if (oldSeg->Wildcard.IncludeSet) {
  3871. newSeg->Wildcard.IncludeSet = PmDuplicateStringW (pool, oldSeg->Wildcard.IncludeSet);
  3872. }
  3873. if (oldSeg->Wildcard.ExcludeSet) {
  3874. newSeg->Wildcard.ExcludeSet = PmDuplicateStringW (pool, oldSeg->Wildcard.ExcludeSet);
  3875. }
  3876. k++;
  3877. newSeg = &newProps->Segment[k];
  3878. break;
  3879. default:
  3880. result = FALSE;
  3881. __leave;
  3882. }
  3883. }
  3884. }
  3885. }
  3886. __finally {
  3887. if (!result) {
  3888. PmDestroyPool (pool);
  3889. pattern = NULL;
  3890. }
  3891. }
  3892. return pattern;
  3893. }
  3894. /*++
  3895. Routine Description:
  3896. IsPatternContainedEx compares two patterns to see if one of them is
  3897. included in the other. Both patterns may contain any of the following
  3898. expressions:
  3899. * - Specifies zero or more characters
  3900. ? - Specifies any one character
  3901. *[set] - Specifies zero or more characters in set
  3902. ?[set] - Specifies any one character in set
  3903. *[n:set] - Specifies zero to n characters in set
  3904. ?[n:set] - Specifies exactly n characters in set
  3905. *[!(set)] - Specifies zero or more characters not in set
  3906. ?[!(set)] - Specifies one character not in set
  3907. *[n:!(set)] - Specifies zero to n characters not in set
  3908. ?[n:!(set)] - Specifies exactly n characters not in set
  3909. *[set1,!(set2)] - Specifies zero or more characters in set1 and
  3910. not in set2. It is assumed that set1 and set2
  3911. overlap.
  3912. ?[set1,!(set2)] - Specifies one character in set1 and not in set2.
  3913. *[n:set1,!(set2)] - Specifies zero to n characters in set1 and not
  3914. in set 2.
  3915. ?[n:set1,!(set2)] - Specifies exactly n characters in set1 and not
  3916. in set 2.
  3917. set, set1 and set2 are specified as follows:
  3918. a - Specifies a single character
  3919. a-b - Specifies a character range
  3920. a,b - Specifies two characters
  3921. a-b,c-d - Specifies two character ranges
  3922. a,b-c - Specifies a single character and a character range
  3923. etc...
  3924. Patterns can be joined by surrounding the entire expression in
  3925. greater than/less than braces.
  3926. Because of the syntax characters, the following characters must be
  3927. escaped by preceeding the character with a caret (^):
  3928. ^? ^[ ^- ^< ^! ^^
  3929. ^* ^] ^: ^> ^,
  3930. Here are some examples:
  3931. To specify any GUID:
  3932. {?[8:0-9,a-f]-?[4:0-9,a-f]-?[4:0-9,a-f]-?[4:0-9,a-f]-?[12:0-9,a-f]}
  3933. To specify a 32-bit hexadecimal number:
  3934. <0x*[8:0-9,a-f]><0*[7:0-9,a-f]h><?[1-9]*[7:0-9,a-f]h>
  3935. Arguments:
  3936. Container - A container pattern possibly containing wildcards
  3937. Contained - A contained pattern possibly containing wildcards
  3938. Return Value:
  3939. TRUE when the second pattern is contained in the first one, FALSE if not.
  3940. --*/
  3941. BOOL
  3942. IsPatternContainedExA (
  3943. IN PCSTR Container,
  3944. IN PCSTR Contained
  3945. )
  3946. {
  3947. PPARSEDPATTERNA container = NULL, contained = NULL;
  3948. PPARSEDPATTERNA expContainer = NULL, expContained = NULL;
  3949. BOOL result = FALSE;
  3950. __try {
  3951. container = CreateParsedPatternA (Container);
  3952. if (!container) {
  3953. __leave;
  3954. }
  3955. expContainer = ExplodeParsedPatternA (container);
  3956. if (!expContainer) {
  3957. __leave;
  3958. }
  3959. contained = CreateParsedPatternA (Contained);
  3960. if (!contained) {
  3961. __leave;
  3962. }
  3963. expContained = ExplodeParsedPatternA (contained);
  3964. if (!expContained) {
  3965. __leave;
  3966. }
  3967. result = IsExplodedParsedPatternContainedExA (expContainer, expContained);
  3968. }
  3969. __finally {
  3970. if (expContained) {
  3971. DestroyParsedPatternA (expContained);
  3972. }
  3973. if (contained) {
  3974. DestroyParsedPatternA (contained);
  3975. }
  3976. if (expContainer) {
  3977. DestroyParsedPatternA (expContainer);
  3978. }
  3979. if (container) {
  3980. DestroyParsedPatternA (container);
  3981. }
  3982. }
  3983. return result;
  3984. }
  3985. BOOL
  3986. IsPatternContainedExW (
  3987. IN PCWSTR Container,
  3988. IN PCWSTR Contained
  3989. )
  3990. {
  3991. PPARSEDPATTERNW container = NULL, contained = NULL;
  3992. PPARSEDPATTERNW expContainer = NULL, expContained = NULL;
  3993. BOOL result = FALSE;
  3994. __try {
  3995. container = CreateParsedPatternW (Container);
  3996. if (!container) {
  3997. __leave;
  3998. }
  3999. expContainer = ExplodeParsedPatternW (container);
  4000. if (!expContainer) {
  4001. __leave;
  4002. }
  4003. contained = CreateParsedPatternW (Contained);
  4004. if (!contained) {
  4005. __leave;
  4006. }
  4007. expContained = ExplodeParsedPatternW (contained);
  4008. if (!expContained) {
  4009. __leave;
  4010. }
  4011. result = IsExplodedParsedPatternContainedExW (expContainer, expContained);
  4012. }
  4013. __finally {
  4014. if (expContained) {
  4015. DestroyParsedPatternW (expContained);
  4016. }
  4017. if (contained) {
  4018. DestroyParsedPatternW (contained);
  4019. }
  4020. if (expContainer) {
  4021. DestroyParsedPatternW (expContainer);
  4022. }
  4023. if (container) {
  4024. DestroyParsedPatternW (container);
  4025. }
  4026. }
  4027. return result;
  4028. }
  4029. BOOL
  4030. IsParsedPatternContainedExA (
  4031. IN PPARSEDPATTERNA Container,
  4032. IN PPARSEDPATTERNA Contained
  4033. )
  4034. {
  4035. PPARSEDPATTERNA expContainer = NULL, expContained = NULL;
  4036. BOOL result = FALSE;
  4037. __try {
  4038. expContainer = ExplodeParsedPatternA (Container);
  4039. if (!expContainer) {
  4040. __leave;
  4041. }
  4042. expContained = ExplodeParsedPatternA (Contained);
  4043. if (!expContained) {
  4044. __leave;
  4045. }
  4046. result = IsExplodedParsedPatternContainedExA (expContainer, expContained);
  4047. }
  4048. __finally {
  4049. if (expContained) {
  4050. DestroyParsedPatternA (expContained);
  4051. }
  4052. if (expContainer) {
  4053. DestroyParsedPatternA (expContainer);
  4054. }
  4055. }
  4056. return result;
  4057. }
  4058. BOOL
  4059. IsParsedPatternContainedExW (
  4060. IN PPARSEDPATTERNW Container,
  4061. IN PPARSEDPATTERNW Contained
  4062. )
  4063. {
  4064. PPARSEDPATTERNW expContainer = NULL, expContained = NULL;
  4065. BOOL result = FALSE;
  4066. __try {
  4067. expContainer = ExplodeParsedPatternW (Container);
  4068. if (!expContainer) {
  4069. __leave;
  4070. }
  4071. expContained = ExplodeParsedPatternW (Contained);
  4072. if (!expContained) {
  4073. __leave;
  4074. }
  4075. result = IsExplodedParsedPatternContainedExW (expContainer, expContained);
  4076. }
  4077. __finally {
  4078. if (expContained) {
  4079. DestroyParsedPatternW (expContained);
  4080. }
  4081. if (expContainer) {
  4082. DestroyParsedPatternW (expContainer);
  4083. }
  4084. }
  4085. return result;
  4086. }
  4087. /*++
  4088. Routine Description:
  4089. pAppendCharToGrowBuffer copies the first character in a caller specified
  4090. string into the specified grow buffer. This function is used to build up a
  4091. string inside a grow buffer, copying character by character.
  4092. Arguments:
  4093. buf - Specifies the grow buffer to add the character to, receives the
  4094. character in its buffer
  4095. PtrToChar - Specifies a pointer to the character to copy
  4096. Return Value:
  4097. None.
  4098. --*/
  4099. VOID
  4100. pAppendCharToGrowBufferA (
  4101. IN OUT PGROWBUFFER Buf,
  4102. IN PCSTR PtrToChar
  4103. )
  4104. {
  4105. PBYTE p;
  4106. UINT Len;
  4107. if (IsLeadByte (*PtrToChar) && PtrToChar[1]) {
  4108. Len = 2;
  4109. } else {
  4110. Len = 1;
  4111. }
  4112. p = GbGrow (Buf, Len);
  4113. CopyMemory (p, PtrToChar, (SIZE_T) Len);
  4114. }
  4115. VOID
  4116. pAppendCharToGrowBufferW (
  4117. IN OUT PGROWBUFFER Buf,
  4118. IN PCWSTR PtrToChar
  4119. )
  4120. {
  4121. PBYTE p;
  4122. p = GbGrow (Buf, sizeof(WCHAR));
  4123. CopyMemory (p, PtrToChar, sizeof(WCHAR));
  4124. }
  4125. /*++
  4126. Routine Description:
  4127. CreateParsedPattern parses the expanded pattern string into a set of
  4128. structures. Parsing is considered expensive relative to testing the
  4129. pattern, so callers should avoid calling this function inside loops. See
  4130. IsPatternMatchEx for a good description of the pattern string syntax.
  4131. Arguments:
  4132. Pattern - Specifies the pattern string, which can include the extended
  4133. wildcard syntax.
  4134. Return Value:
  4135. A pointer to a parsed pattern structure, which the caller will use like a
  4136. handle, or NULL if a syntax error occurred.
  4137. --*/
  4138. PPARSEDPATTERNA
  4139. CreateParsedPatternA (
  4140. IN PCSTR Pattern
  4141. )
  4142. {
  4143. PMHANDLE Pool;
  4144. PPARSEDPATTERNA Struct;
  4145. PATTERNSTATE State;
  4146. BOOL CompoundPattern = FALSE;
  4147. GROWBUFFER ExactMatchBuf = INIT_GROWBUFFER;
  4148. GROWBUFFER SegmentArray = INIT_GROWBUFFER;
  4149. GROWBUFFER PatternArray = INIT_GROWBUFFER;
  4150. GROWBUFFER SetBuf = INIT_GROWBUFFER;
  4151. PPATTERNPROPSA CurrentPattern;
  4152. MBCHAR ch = 0;
  4153. PCSTR LookAhead;
  4154. PCSTR SetBegin = NULL;
  4155. PATTERNSTATE ReturnState = 0;
  4156. SEGMENTA Segment;
  4157. PSEGMENTA SegmentElement;
  4158. UINT MaxLen;
  4159. Segment.Type = SEGMENTTYPE_UNKNOWN;
  4160. Pool = PmCreateNamedPool ("Parsed Pattern");
  4161. Struct = (PPARSEDPATTERNA) PmGetAlignedMemory (Pool, sizeof (PARSEDPATTERNA));
  4162. ZeroMemory (Struct, sizeof (PARSEDPATTERNA));
  4163. State = BEGIN_PATTERN;
  4164. for (;;) {
  4165. switch (State) {
  4166. case BEGIN_PATTERN:
  4167. //
  4168. // Here we test for either a compound pattern (one that
  4169. // is a brace-separated list), or a simple pattern (one
  4170. // that does not have a brace).
  4171. //
  4172. if (_mbsnextc (Pattern) == '<') {
  4173. CompoundPattern = TRUE;
  4174. State = BEGIN_COMPOUND_PATTERN;
  4175. } else if (*Pattern) {
  4176. State = BEGIN_PATTERN_EXPR;
  4177. } else {
  4178. State = PATTERN_DONE;
  4179. }
  4180. break;
  4181. case BEGIN_COMPOUND_PATTERN:
  4182. //
  4183. // We are looking for the start of a compound pattern.
  4184. // Space is allowed inbetween the patterns, but not
  4185. // at the start.
  4186. //
  4187. while (isspace ((INT)(_mbsnextc (Pattern)))) {
  4188. Pattern = _mbsinc (Pattern);
  4189. }
  4190. if (*Pattern == 0) {
  4191. State = PATTERN_DONE;
  4192. break;
  4193. }
  4194. if (_mbsnextc (Pattern) == '<') {
  4195. Pattern = _mbsinc (Pattern);
  4196. State = BEGIN_PATTERN_EXPR;
  4197. } else {
  4198. DEBUGMSGA ((DBG_ERROR, "Syntax error in pattern: %s", Pattern));
  4199. State = PATTERN_ERROR;
  4200. }
  4201. break;
  4202. case BEGIN_PATTERN_EXPR:
  4203. //
  4204. // We are now ready to condense the expression.
  4205. //
  4206. State = PARSE_CHAR_EXPR_OR_END;
  4207. ExactMatchBuf.End = 0;
  4208. SegmentArray.End = 0;
  4209. break;
  4210. case PARSE_END_FOUND:
  4211. State = END_PATTERN_EXPR;
  4212. if (ExactMatchBuf.End) {
  4213. ReturnState = State;
  4214. State = SAVE_EXACT_MATCH;
  4215. }
  4216. break;
  4217. case END_PATTERN_EXPR:
  4218. //
  4219. // Copy the segment array into the pool, reference the copy
  4220. // in the pattern array
  4221. //
  4222. if (SegmentArray.End) {
  4223. CurrentPattern = (PPATTERNPROPSA) GbGrow (&PatternArray, sizeof (PATTERNPROPSA));
  4224. CurrentPattern->Segment = (PSEGMENTA) PmGetAlignedMemory (Pool, SegmentArray.End);
  4225. CurrentPattern->SegmentCount = SegmentArray.End / sizeof (SEGMENTA);
  4226. CopyMemory (
  4227. CurrentPattern->Segment,
  4228. SegmentArray.Buf,
  4229. (SIZE_T) SegmentArray.End
  4230. );
  4231. }
  4232. if (CompoundPattern && *Pattern) {
  4233. State = BEGIN_COMPOUND_PATTERN;
  4234. } else {
  4235. State = PATTERN_DONE;
  4236. }
  4237. break;
  4238. case PARSE_CHAR_EXPR_OR_END:
  4239. //
  4240. // We now accept the following:
  4241. //
  4242. // 1. The end of the string or end of a compound pattern
  4243. // 2. An escaped character
  4244. // 3. The start of an expression
  4245. // 4. A non-syntax character
  4246. //
  4247. ch = _mbsnextc (Pattern);
  4248. if (ch == '>' && CompoundPattern) {
  4249. //
  4250. // Case 1, we found the end of a compound pattern
  4251. //
  4252. Pattern = _mbsinc (Pattern);
  4253. State = PARSE_END_FOUND;
  4254. break;
  4255. }
  4256. if (*Pattern == 0) {
  4257. //
  4258. // Case 1, we found the end of the pattern
  4259. //
  4260. if (CompoundPattern) {
  4261. State = PATTERN_ERROR;
  4262. } else {
  4263. State = PARSE_END_FOUND;
  4264. }
  4265. break;
  4266. }
  4267. if (ch == '^') {
  4268. //
  4269. // Case 2, we found an escaped character, so transfer
  4270. // it to the buffer.
  4271. //
  4272. MYASSERT (
  4273. Segment.Type == SEGMENTTYPE_UNKNOWN ||
  4274. Segment.Type == SEGMENTTYPE_EXACTMATCH
  4275. );
  4276. Segment.Type = SEGMENTTYPE_EXACTMATCH;
  4277. Pattern = _mbsinc (Pattern);
  4278. pAppendCharToGrowBufferA (&ExactMatchBuf, Pattern);
  4279. Pattern = _mbsinc (Pattern);
  4280. break;
  4281. }
  4282. if (ch == '*' || ch == '?') {
  4283. //
  4284. // Case 3, we found an expression. Save the wildcard type
  4285. // and parse the optional args.
  4286. //
  4287. if (ExactMatchBuf.End) {
  4288. State = SAVE_EXACT_MATCH;
  4289. ReturnState = PARSE_CHAR_EXPR_OR_END;
  4290. break;
  4291. }
  4292. ZeroMemory (&Segment, sizeof (Segment));
  4293. if (ch == '*') {
  4294. Segment.Type = SEGMENTTYPE_OPTIONAL;
  4295. } else {
  4296. Segment.Type = SEGMENTTYPE_REQUIRED;
  4297. Segment.Wildcard.MaxLen = 1;
  4298. }
  4299. Pattern = _mbsinc (Pattern);
  4300. if (_mbsnextc (Pattern) == '[') {
  4301. Pattern = _mbsinc (Pattern);
  4302. State = LOOK_FOR_NUMBER;
  4303. } else {
  4304. ReturnState = PARSE_CHAR_EXPR_OR_END;
  4305. State = SAVE_SEGMENT;
  4306. }
  4307. break;
  4308. }
  4309. //
  4310. // Case 4, we don't know about this character, so just copy it
  4311. // and continue parsing.
  4312. //
  4313. pAppendCharToGrowBufferA (&ExactMatchBuf, Pattern);
  4314. Pattern = _mbsinc (Pattern);
  4315. break;
  4316. case SAVE_EXACT_MATCH:
  4317. //
  4318. // Put the string in ExactMatchBuf into a segment struct
  4319. //
  4320. pAppendCharToGrowBufferA (&ExactMatchBuf, "");
  4321. Segment.Exact.LowerCasePhrase = PmDuplicateStringA (
  4322. Pool,
  4323. (PCSTR) ExactMatchBuf.Buf
  4324. );
  4325. Segment.Exact.PhraseBytes = ExactMatchBuf.End - sizeof (CHAR);
  4326. MYASSERT (Segment.Exact.LowerCasePhrase);
  4327. _mbslwr ((PSTR) Segment.Exact.LowerCasePhrase);
  4328. Segment.Type = SEGMENTTYPE_EXACTMATCH;
  4329. ExactMatchBuf.End = 0;
  4330. // FALL THROUGH!!
  4331. case SAVE_SEGMENT:
  4332. //
  4333. // Put the segment element into the segment array
  4334. //
  4335. SegmentElement = (PSEGMENTA) GbGrow (&SegmentArray, sizeof (SEGMENTA));
  4336. CopyMemory (SegmentElement, &Segment, sizeof (SEGMENTA));
  4337. State = ReturnState;
  4338. break;
  4339. case LOOK_FOR_NUMBER:
  4340. //
  4341. // Here we are inside a bracket, and there is an optional
  4342. // numeric arg, which must be followed by a colon. Test
  4343. // that here.
  4344. //
  4345. LookAhead = Pattern;
  4346. MaxLen = 0;
  4347. while (*LookAhead >= '0' && *LookAhead <= '9') {
  4348. MaxLen = MaxLen * 10 + (*LookAhead - '0');
  4349. LookAhead++;
  4350. }
  4351. if (LookAhead > Pattern && _mbsnextc (LookAhead) == ':') {
  4352. Pattern = _mbsinc (LookAhead);
  4353. //
  4354. // Check for special case syntax error: ?[0:]
  4355. //
  4356. if (Segment.Type == SEGMENTTYPE_EXACTMATCH && !MaxLen) {
  4357. State = PATTERN_ERROR;
  4358. break;
  4359. }
  4360. Segment.Wildcard.MaxLen = MaxLen;
  4361. }
  4362. SetBegin = Pattern;
  4363. State = LOOK_FOR_INCLUDE;
  4364. SetBuf.End = 0;
  4365. break;
  4366. case LOOK_FOR_INCLUDE:
  4367. //
  4368. // Here we are inside a bracket, past an optional numeric
  4369. // arg. Now we look for all the include sets, which are
  4370. // optional. We have the following possibilities:
  4371. //
  4372. // 1. End of set
  4373. // 2. An exclude set that needs to be skipped
  4374. // 3. A valid include set
  4375. // 4. Error
  4376. //
  4377. // We look at SetBegin, and not Pattern.
  4378. //
  4379. MYASSERT (SetBegin);
  4380. ch = _mbsnextc (SetBegin);
  4381. if (ch == ']') {
  4382. //
  4383. // Case 1: end of set
  4384. //
  4385. if (SetBuf.End) {
  4386. pAppendCharToGrowBufferA (&SetBuf, "");
  4387. Segment.Wildcard.IncludeSet = PmDuplicateStringA (
  4388. Pool,
  4389. (PCSTR) SetBuf.Buf
  4390. );
  4391. _mbslwr ((PSTR) Segment.Wildcard.IncludeSet);
  4392. } else {
  4393. Segment.Wildcard.IncludeSet = NULL;
  4394. }
  4395. SetBuf.End = 0;
  4396. State = LOOK_FOR_EXCLUDE;
  4397. SetBegin = Pattern;
  4398. break;
  4399. }
  4400. if (ch == '!') {
  4401. //
  4402. // Case 2: an exclude set
  4403. //
  4404. SetBegin = _mbsinc (SetBegin);
  4405. State = SKIP_EXCLUDE_SET;
  4406. ReturnState = LOOK_FOR_INCLUDE;
  4407. break;
  4408. }
  4409. if (*SetBegin == 0) { //lint !e613
  4410. State = PATTERN_ERROR;
  4411. break;
  4412. }
  4413. //
  4414. // Case 3: a valid include set.
  4415. //
  4416. State = CONDENSE_SET;
  4417. ReturnState = LOOK_FOR_INCLUDE;
  4418. break;
  4419. case LOOK_FOR_EXCLUDE:
  4420. //
  4421. // Here we are inside a bracket, past an optional numeric
  4422. // arg. All include sets are in the condensing buffer.
  4423. // Now we look for all the exclude sets, which are
  4424. // optional. We have the following possibilities:
  4425. //
  4426. // 1. End of set
  4427. // 2. A valid exclude set
  4428. // 3. An include set that needs to be skipped
  4429. // 4. Error
  4430. //
  4431. // We look at SetBegin, and not Pattern.
  4432. //
  4433. ch = _mbsnextc (SetBegin);
  4434. if (ch == ']') {
  4435. //
  4436. // Case 1: end of set; we're done with this expr
  4437. //
  4438. if (SetBuf.End) {
  4439. pAppendCharToGrowBufferA (&SetBuf, "");
  4440. Segment.Wildcard.ExcludeSet = PmDuplicateStringA (
  4441. Pool,
  4442. (PCSTR) SetBuf.Buf
  4443. );
  4444. _mbslwr ((PSTR) Segment.Wildcard.ExcludeSet);
  4445. } else {
  4446. Segment.Wildcard.ExcludeSet = NULL;
  4447. }
  4448. SetBuf.End = 0;
  4449. State = SAVE_SEGMENT;
  4450. ReturnState = PARSE_CHAR_EXPR_OR_END;
  4451. Pattern = _mbsinc (SetBegin);
  4452. break;
  4453. }
  4454. if (ch == '!') {
  4455. //
  4456. // Case 2: a valid exclude set; save it
  4457. //
  4458. SetBegin = _mbsinc (SetBegin);
  4459. if (_mbsnextc (SetBegin) != '(') {
  4460. State = PATTERN_ERROR;
  4461. break;
  4462. }
  4463. SetBegin = _mbsinc (SetBegin);
  4464. State = CONDENSE_SET;
  4465. ReturnState = LOOK_FOR_EXCLUDE;
  4466. break;
  4467. }
  4468. if (*SetBegin == 0) { //lint !e613
  4469. State = PATTERN_ERROR;
  4470. break;
  4471. }
  4472. //
  4473. // Case 3: an include set that needs to be skipped.
  4474. //
  4475. State = SKIP_INCLUDE_SET;
  4476. ReturnState = LOOK_FOR_EXCLUDE;
  4477. break;
  4478. case CONDENSE_SET:
  4479. //
  4480. // Here SetBegin points to a set range, and it is our
  4481. // job to copy the range into the set buffer, and
  4482. // return back to the previous state.
  4483. //
  4484. //
  4485. // Copy the character at SetBegin
  4486. //
  4487. if (_mbsnextc (SetBegin) == '^') {
  4488. SetBegin = _mbsinc (SetBegin);
  4489. if (*SetBegin == 0) {
  4490. State = PATTERN_ERROR;
  4491. break;
  4492. }
  4493. }
  4494. pAppendCharToGrowBufferA (&SetBuf, SetBegin);
  4495. //
  4496. // Check if this is a range or not
  4497. //
  4498. LookAhead = _mbsinc (SetBegin);
  4499. if (_mbsnextc (LookAhead) == '-') {
  4500. //
  4501. // Range, copy the character after the dash
  4502. //
  4503. SetBegin = _mbsinc (LookAhead);
  4504. if (*SetBegin == 0) {
  4505. State = PATTERN_ERROR;
  4506. break;
  4507. }
  4508. if (_mbsnextc (SetBegin) == '^') {
  4509. SetBegin = _mbsinc (SetBegin);
  4510. if (*SetBegin == 0) {
  4511. State = PATTERN_ERROR;
  4512. break;
  4513. }
  4514. }
  4515. pAppendCharToGrowBufferA (&SetBuf, SetBegin);
  4516. } else {
  4517. //
  4518. // A single character, copy the character again
  4519. //
  4520. pAppendCharToGrowBufferA (&SetBuf, SetBegin);
  4521. }
  4522. SetBegin = _mbsinc (SetBegin);
  4523. ch = _mbsnextc (SetBegin);
  4524. //
  4525. // If this is an exclude set, we must have a closing paren
  4526. // or a comma
  4527. //
  4528. State = ReturnState;
  4529. if (ReturnState == LOOK_FOR_EXCLUDE) {
  4530. if (ch == ')') {
  4531. SetBegin = _mbsinc (SetBegin);
  4532. ch = _mbsnextc (SetBegin);
  4533. } else if (ch != ',') {
  4534. State = PATTERN_ERROR;
  4535. } else {
  4536. //
  4537. // Continue condensing the next part of this exclude set
  4538. //
  4539. State = CONDENSE_SET;
  4540. }
  4541. }
  4542. //
  4543. // We either need a comma or a close brace
  4544. //
  4545. if (ch == ',') {
  4546. SetBegin = _mbsinc (SetBegin);
  4547. } else if (ch != ']') {
  4548. State = PATTERN_ERROR;
  4549. }
  4550. break;
  4551. case SKIP_EXCLUDE_SET:
  4552. //
  4553. // Skip over the parenthesis group, assuming it is syntatically
  4554. // correct, and return to the previous state.
  4555. //
  4556. if (_mbsnextc (SetBegin) != '(') {
  4557. State = PATTERN_ERROR;
  4558. break;
  4559. }
  4560. SetBegin = _mbsinc (SetBegin);
  4561. while (*SetBegin) {
  4562. if (_mbsnextc (SetBegin) == '^') {
  4563. SetBegin = _mbsinc (SetBegin);
  4564. } else if (_mbsnextc (SetBegin) == ')') {
  4565. break;
  4566. }
  4567. if (IsLeadByte (SetBegin[0]) && SetBegin[1]) {
  4568. SetBegin += 2;
  4569. } else {
  4570. SetBegin += 1;
  4571. }
  4572. }
  4573. if (*SetBegin == 0) {
  4574. State = PATTERN_ERROR;
  4575. break;
  4576. }
  4577. SetBegin = _mbsinc (SetBegin);
  4578. //
  4579. // Now we are either at a comma or a close brace
  4580. //
  4581. ch = _mbsnextc (SetBegin);
  4582. State = ReturnState;
  4583. if (ch == ',') {
  4584. SetBegin = _mbsinc (SetBegin);
  4585. } else if (ch != ']') {
  4586. State = PATTERN_ERROR;
  4587. }
  4588. break;
  4589. case SKIP_INCLUDE_SET:
  4590. //
  4591. // Skip to the next comma or closing brace. We know it is
  4592. // syntatically correct by now.
  4593. //
  4594. ch = 0;
  4595. while (*SetBegin) { //lint !e613
  4596. ch = _mbsnextc (SetBegin);
  4597. if (ch == '^') {
  4598. SetBegin = _mbsinc (SetBegin);
  4599. } else if (ch == ',' || ch == ']') {
  4600. break;
  4601. }
  4602. SetBegin = _mbsinc (SetBegin);
  4603. }
  4604. MYASSERT (*SetBegin); //lint !e794
  4605. if (ch == ',') {
  4606. SetBegin = _mbsinc (SetBegin);
  4607. }
  4608. State = ReturnState;
  4609. break;
  4610. } //lint !e787
  4611. if (State == PATTERN_DONE || State == PATTERN_ERROR) {
  4612. break;
  4613. }
  4614. }
  4615. GbFree (&ExactMatchBuf);
  4616. GbFree (&SetBuf);
  4617. GbFree (&SegmentArray);
  4618. if (State == PATTERN_ERROR) {
  4619. GbFree (&PatternArray);
  4620. PmDestroyPool (Pool);
  4621. return NULL;
  4622. }
  4623. if (PatternArray.End == 0) {
  4624. //build an empty parsed pattern
  4625. GbFree (&PatternArray);
  4626. Struct->PatternCount = 1;
  4627. Struct->Pool = Pool;
  4628. Struct->Pattern = (PPATTERNPROPSA) PmGetAlignedMemory (
  4629. Pool,
  4630. sizeof (PATTERNPROPSA)
  4631. );
  4632. Struct->Pattern[0].SegmentCount = 1;
  4633. Struct->Pattern[0].Segment = (PSEGMENTA) PmGetAlignedMemory (
  4634. Pool,
  4635. sizeof (PSEGMENTA)
  4636. );
  4637. Struct->Pattern[0].Segment[0].Type = SEGMENTTYPE_EXACTMATCH;
  4638. Struct->Pattern[0].Segment[0].Exact.LowerCasePhrase = PmDuplicateStringA (Pool, "");
  4639. Struct->Pattern[0].Segment[0].Exact.PhraseBytes = 0;
  4640. return Struct;
  4641. }
  4642. //
  4643. // Copy the fully parsed pattern array into the return struct
  4644. //
  4645. Struct->Pattern = (PPATTERNPROPSA) PmGetAlignedMemory (
  4646. Pool,
  4647. PatternArray.End
  4648. );
  4649. CopyMemory (Struct->Pattern, PatternArray.Buf, (SIZE_T) PatternArray.End);
  4650. Struct->PatternCount = PatternArray.End / sizeof (PATTERNPROPSA);
  4651. Struct->Pool = Pool;
  4652. GbFree (&PatternArray);
  4653. return Struct;
  4654. }
  4655. PPARSEDPATTERNW
  4656. CreateParsedPatternW (
  4657. IN PCWSTR Pattern
  4658. )
  4659. {
  4660. PMHANDLE Pool;
  4661. PPARSEDPATTERNW Struct;
  4662. PATTERNSTATE State;
  4663. BOOL CompoundPattern = FALSE;
  4664. GROWBUFFER ExactMatchBuf = INIT_GROWBUFFER;
  4665. GROWBUFFER SegmentArray = INIT_GROWBUFFER;
  4666. GROWBUFFER PatternArray = INIT_GROWBUFFER;
  4667. GROWBUFFER SetBuf = INIT_GROWBUFFER;
  4668. PPATTERNPROPSW CurrentPattern;
  4669. WCHAR ch = 0;
  4670. PCWSTR LookAhead;
  4671. PCWSTR SetBegin = NULL;
  4672. PATTERNSTATE ReturnState = 0;
  4673. SEGMENTW Segment;
  4674. PSEGMENTW SegmentElement;
  4675. UINT MaxLen;
  4676. Segment.Type = SEGMENTTYPE_UNKNOWN;
  4677. Pool = PmCreateNamedPool ("Parsed Pattern");
  4678. Struct = (PPARSEDPATTERNW) PmGetAlignedMemory (Pool, sizeof (PARSEDPATTERNW));
  4679. ZeroMemory (Struct, sizeof (PARSEDPATTERNW));
  4680. State = BEGIN_PATTERN;
  4681. for (;;) {
  4682. switch (State) {
  4683. case BEGIN_PATTERN:
  4684. //
  4685. // Here we test for either a compound pattern (one that
  4686. // is a brace-separated list), or a simple pattern (one
  4687. // that does not have a brace).
  4688. //
  4689. if (*Pattern == L'<') {
  4690. CompoundPattern = TRUE;
  4691. State = BEGIN_COMPOUND_PATTERN;
  4692. } else if (*Pattern) {
  4693. State = BEGIN_PATTERN_EXPR;
  4694. } else {
  4695. State = PATTERN_DONE;
  4696. }
  4697. break;
  4698. case BEGIN_COMPOUND_PATTERN:
  4699. //
  4700. // We are looking for the start of a compound pattern.
  4701. // Space is allowed inbetween the patterns, but not
  4702. // at the start.
  4703. //
  4704. while (iswspace (*Pattern)) {
  4705. Pattern++;
  4706. }
  4707. if (*Pattern == 0) {
  4708. State = PATTERN_DONE;
  4709. break;
  4710. }
  4711. if (*Pattern == L'<') {
  4712. Pattern++;
  4713. State = BEGIN_PATTERN_EXPR;
  4714. } else {
  4715. DEBUGMSGW ((DBG_ERROR, "Syntax error in pattern: %s", Pattern));
  4716. State = PATTERN_ERROR;
  4717. }
  4718. break;
  4719. case BEGIN_PATTERN_EXPR:
  4720. //
  4721. // We are now ready to condense the expression.
  4722. //
  4723. State = PARSE_CHAR_EXPR_OR_END;
  4724. ExactMatchBuf.End = 0;
  4725. SegmentArray.End = 0;
  4726. break;
  4727. case PARSE_END_FOUND:
  4728. State = END_PATTERN_EXPR;
  4729. if (ExactMatchBuf.End) {
  4730. ReturnState = State;
  4731. State = SAVE_EXACT_MATCH;
  4732. }
  4733. break;
  4734. case END_PATTERN_EXPR:
  4735. //
  4736. // Copy the segment array into the pool, reference the copy
  4737. // in the pattern array
  4738. //
  4739. if (SegmentArray.End) {
  4740. CurrentPattern = (PPATTERNPROPSW) GbGrow (&PatternArray, sizeof (PATTERNPROPSW));
  4741. CurrentPattern->Segment = (PSEGMENTW) PmGetAlignedMemory (Pool, SegmentArray.End);
  4742. CurrentPattern->SegmentCount = SegmentArray.End / sizeof (SEGMENTW);
  4743. CopyMemory (
  4744. CurrentPattern->Segment,
  4745. SegmentArray.Buf,
  4746. (SIZE_T) SegmentArray.End
  4747. );
  4748. }
  4749. if (CompoundPattern && *Pattern) {
  4750. State = BEGIN_COMPOUND_PATTERN;
  4751. } else {
  4752. State = PATTERN_DONE;
  4753. }
  4754. break;
  4755. case PARSE_CHAR_EXPR_OR_END:
  4756. //
  4757. // We now accept the following:
  4758. //
  4759. // 1. The end of the string or end of a compound pattern
  4760. // 2. An escaped character
  4761. // 3. The start of an expression
  4762. // 4. A non-syntax character
  4763. //
  4764. ch = *Pattern;
  4765. if (ch == L'>' && CompoundPattern) {
  4766. //
  4767. // Case 1, we found the end of a compound pattern
  4768. //
  4769. Pattern++;
  4770. State = PARSE_END_FOUND;
  4771. break;
  4772. }
  4773. if (*Pattern == 0) {
  4774. //
  4775. // Case 1, we found the end of the pattern
  4776. //
  4777. if (CompoundPattern) {
  4778. State = PATTERN_ERROR;
  4779. } else {
  4780. State = PARSE_END_FOUND;
  4781. }
  4782. break;
  4783. }
  4784. if (ch == L'^') {
  4785. //
  4786. // Case 2, we found an escaped character, so transfer
  4787. // it to the buffer.
  4788. //
  4789. MYASSERT (
  4790. Segment.Type == SEGMENTTYPE_UNKNOWN ||
  4791. Segment.Type == SEGMENTTYPE_EXACTMATCH
  4792. );
  4793. Segment.Type = SEGMENTTYPE_EXACTMATCH;
  4794. Pattern++;
  4795. pAppendCharToGrowBufferW (&ExactMatchBuf, Pattern);
  4796. Pattern++;
  4797. break;
  4798. }
  4799. if (ch == L'*' || ch == L'?') {
  4800. //
  4801. // Case 3, we found an expression. Save the wildcard type
  4802. // and parse the optional args.
  4803. //
  4804. if (ExactMatchBuf.End) {
  4805. State = SAVE_EXACT_MATCH;
  4806. ReturnState = PARSE_CHAR_EXPR_OR_END;
  4807. break;
  4808. }
  4809. ZeroMemory (&Segment, sizeof (Segment));
  4810. if (ch == L'*') {
  4811. Segment.Type = SEGMENTTYPE_OPTIONAL;
  4812. } else {
  4813. Segment.Type = SEGMENTTYPE_REQUIRED;
  4814. Segment.Wildcard.MaxLen = 1;
  4815. }
  4816. Pattern++;
  4817. if (*Pattern == L'[') {
  4818. Pattern++;
  4819. State = LOOK_FOR_NUMBER;
  4820. } else {
  4821. ReturnState = PARSE_CHAR_EXPR_OR_END;
  4822. State = SAVE_SEGMENT;
  4823. }
  4824. break;
  4825. }
  4826. //
  4827. // Case 4, we don't know about this character, so just copy it
  4828. // and continue parsing.
  4829. //
  4830. pAppendCharToGrowBufferW (&ExactMatchBuf, Pattern);
  4831. Pattern++;
  4832. break;
  4833. case SAVE_EXACT_MATCH:
  4834. //
  4835. // Put the string in ExactMatchBuf into a segment struct
  4836. //
  4837. pAppendCharToGrowBufferW (&ExactMatchBuf, L"");
  4838. Segment.Exact.LowerCasePhrase = PmDuplicateStringW (
  4839. Pool,
  4840. (PCWSTR) ExactMatchBuf.Buf
  4841. ); //lint !e64
  4842. Segment.Exact.PhraseBytes = ExactMatchBuf.End - sizeof (WCHAR);
  4843. MYASSERT (Segment.Exact.LowerCasePhrase);
  4844. _wcslwr ((PWSTR) Segment.Exact.LowerCasePhrase);
  4845. Segment.Type = SEGMENTTYPE_EXACTMATCH;
  4846. ExactMatchBuf.End = 0;
  4847. // FALL THROUGH!!
  4848. case SAVE_SEGMENT:
  4849. //
  4850. // Put the segment element into the segment array
  4851. //
  4852. SegmentElement = (PSEGMENTW) GbGrow (&SegmentArray, sizeof (SEGMENTW));
  4853. CopyMemory (SegmentElement, &Segment, sizeof (SEGMENTW));
  4854. State = ReturnState;
  4855. break;
  4856. case LOOK_FOR_NUMBER:
  4857. //
  4858. // Here we are inside a bracket, and there is an optional
  4859. // numeric arg, which must be followed by a colon. Test
  4860. // that here.
  4861. //
  4862. LookAhead = Pattern;
  4863. MaxLen = 0;
  4864. while (*LookAhead >= L'0' && *LookAhead <= L'9') {
  4865. MaxLen = MaxLen * 10 + (*LookAhead - L'0');
  4866. LookAhead++;
  4867. }
  4868. if (LookAhead > Pattern && *LookAhead == L':') {
  4869. Pattern = LookAhead + 1;
  4870. //
  4871. // Check for special case syntax error: ?[0:]
  4872. //
  4873. if (Segment.Type == SEGMENTTYPE_EXACTMATCH && !MaxLen) {
  4874. State = PATTERN_ERROR;
  4875. break;
  4876. }
  4877. Segment.Wildcard.MaxLen = MaxLen;
  4878. }
  4879. SetBegin = Pattern;
  4880. State = LOOK_FOR_INCLUDE;
  4881. SetBuf.End = 0;
  4882. break;
  4883. case LOOK_FOR_INCLUDE:
  4884. //
  4885. // Here we are inside a bracket, past an optional numeric
  4886. // arg. Now we look for all the include sets, which are
  4887. // optional. We have the following possibilities:
  4888. //
  4889. // 1. End of set
  4890. // 2. An exclude set that needs to be skipped
  4891. // 3. A valid include set
  4892. // 4. Error
  4893. //
  4894. // We look at SetBegin, and not Pattern.
  4895. //
  4896. if (!SetBegin) {
  4897. State = PATTERN_ERROR;
  4898. break;
  4899. }
  4900. ch = *SetBegin;
  4901. if (ch == L']') {
  4902. //
  4903. // Case 1: end of set
  4904. //
  4905. if (SetBuf.End) {
  4906. pAppendCharToGrowBufferW (&SetBuf, L"");
  4907. Segment.Wildcard.IncludeSet = PmDuplicateStringW (
  4908. Pool,
  4909. (PCWSTR) SetBuf.Buf
  4910. ); //lint !e64
  4911. _wcslwr ((PWSTR) Segment.Wildcard.IncludeSet);
  4912. } else {
  4913. Segment.Wildcard.IncludeSet = NULL;
  4914. }
  4915. SetBuf.End = 0;
  4916. State = LOOK_FOR_EXCLUDE;
  4917. SetBegin = Pattern;
  4918. break;
  4919. }
  4920. if (ch == L'!') {
  4921. //
  4922. // Case 2: an exclude set
  4923. //
  4924. SetBegin++;
  4925. State = SKIP_EXCLUDE_SET;
  4926. ReturnState = LOOK_FOR_INCLUDE;
  4927. break;
  4928. }
  4929. if (*SetBegin == 0) {
  4930. State = PATTERN_ERROR;
  4931. break;
  4932. }
  4933. //
  4934. // Case 3: a valid include set.
  4935. //
  4936. State = CONDENSE_SET;
  4937. ReturnState = LOOK_FOR_INCLUDE;
  4938. break;
  4939. case LOOK_FOR_EXCLUDE:
  4940. //
  4941. // Here we are inside a bracket, past an optional numeric
  4942. // arg. All include sets are in the condensing buffer.
  4943. // Now we look for all the exclude sets, which are
  4944. // optional. We have the following possibilities:
  4945. //
  4946. // 1. End of set
  4947. // 2. A valid exclude set
  4948. // 3. An include set that needs to be skipped
  4949. // 4. Error
  4950. //
  4951. // We look at SetBegin, and not Pattern.
  4952. //
  4953. if (!SetBegin) {
  4954. State = PATTERN_ERROR;
  4955. break;
  4956. }
  4957. ch = *SetBegin;
  4958. if (ch == L']') {
  4959. //
  4960. // Case 1: end of set; we're done with this expr
  4961. //
  4962. if (SetBuf.End) {
  4963. pAppendCharToGrowBufferW (&SetBuf, L"");
  4964. Segment.Wildcard.ExcludeSet = PmDuplicateStringW (
  4965. Pool,
  4966. (PCWSTR) SetBuf.Buf
  4967. ); //lint !e64
  4968. _wcslwr ((PWSTR) Segment.Wildcard.ExcludeSet);
  4969. } else {
  4970. Segment.Wildcard.ExcludeSet = NULL;
  4971. }
  4972. SetBuf.End = 0;
  4973. State = SAVE_SEGMENT;
  4974. ReturnState = PARSE_CHAR_EXPR_OR_END;
  4975. Pattern = SetBegin + 1;
  4976. break;
  4977. }
  4978. if (ch == L'!') {
  4979. //
  4980. // Case 2: a valid exclude set; save it
  4981. //
  4982. SetBegin++; //lint !e613
  4983. if (*SetBegin != L'(') {
  4984. State = PATTERN_ERROR;
  4985. break;
  4986. }
  4987. SetBegin++;
  4988. State = CONDENSE_SET;
  4989. ReturnState = LOOK_FOR_EXCLUDE;
  4990. break;
  4991. }
  4992. if (*SetBegin == 0) {
  4993. State = PATTERN_ERROR;
  4994. break;
  4995. }
  4996. //
  4997. // Case 3: an include set that needs to be skipped.
  4998. //
  4999. State = SKIP_INCLUDE_SET;
  5000. ReturnState = LOOK_FOR_EXCLUDE;
  5001. break;
  5002. case CONDENSE_SET:
  5003. //
  5004. // Here SetBegin points to a set range, and it is our
  5005. // job to copy the range into the set buffer, and
  5006. // return back to the previous state.
  5007. //
  5008. //
  5009. // Copy the character at SetBegin
  5010. //
  5011. if (!SetBegin) {
  5012. State = PATTERN_ERROR;
  5013. break;
  5014. }
  5015. if (*SetBegin == L'^') {
  5016. SetBegin++;
  5017. if (*SetBegin == 0) {
  5018. State = PATTERN_ERROR;
  5019. break;
  5020. }
  5021. }
  5022. pAppendCharToGrowBufferW (&SetBuf, SetBegin);
  5023. //
  5024. // Check if this is a range or not
  5025. //
  5026. LookAhead = SetBegin + 1;
  5027. if (*LookAhead == L'-') {
  5028. //
  5029. // Range, copy the character after the dash
  5030. //
  5031. SetBegin = LookAhead + 1;
  5032. if (*SetBegin == 0) {
  5033. State = PATTERN_ERROR;
  5034. break;
  5035. }
  5036. if (*SetBegin == L'^') {
  5037. SetBegin++;
  5038. if (*SetBegin == 0) {
  5039. State = PATTERN_ERROR;
  5040. break;
  5041. }
  5042. }
  5043. pAppendCharToGrowBufferW (&SetBuf, SetBegin);
  5044. } else {
  5045. //
  5046. // A single character, copy the character again
  5047. //
  5048. pAppendCharToGrowBufferW (&SetBuf, SetBegin);
  5049. }
  5050. SetBegin++;
  5051. ch = *SetBegin;
  5052. //
  5053. // If this is an exclude set, we must have a closing paren
  5054. // or a comma
  5055. //
  5056. State = ReturnState;
  5057. if (ReturnState == LOOK_FOR_EXCLUDE) {
  5058. if (ch == L')') {
  5059. SetBegin++;
  5060. ch = *SetBegin;
  5061. } else if (ch != L',') {
  5062. State = PATTERN_ERROR;
  5063. } else {
  5064. //
  5065. // Continue condensing the next part of this exclude set
  5066. //
  5067. State = CONDENSE_SET;
  5068. }
  5069. }
  5070. //
  5071. // We either need a comma or a close brace
  5072. //
  5073. if (ch == L',') {
  5074. SetBegin++;
  5075. } else if (ch != L']') {
  5076. State = PATTERN_ERROR;
  5077. }
  5078. break;
  5079. case SKIP_EXCLUDE_SET:
  5080. //
  5081. // Skip over the parenthesis group, assuming it is syntatically
  5082. // correct, and return to the previous state.
  5083. //
  5084. if (!SetBegin) {
  5085. State = PATTERN_ERROR;
  5086. break;
  5087. }
  5088. if (*SetBegin != L'(') {
  5089. State = PATTERN_ERROR;
  5090. break;
  5091. }
  5092. SetBegin++;
  5093. while (*SetBegin) {
  5094. if (*SetBegin == L'^') {
  5095. SetBegin++;
  5096. } else if (*SetBegin == L')') {
  5097. break;
  5098. }
  5099. SetBegin++;
  5100. }
  5101. if (*SetBegin == 0) {
  5102. State = PATTERN_ERROR;
  5103. break;
  5104. }
  5105. SetBegin++;
  5106. //
  5107. // Now we are either at a comma or a close brace
  5108. //
  5109. ch = *SetBegin;
  5110. State = ReturnState;
  5111. if (ch == L',') {
  5112. SetBegin++;
  5113. } else if (ch != L']') {
  5114. State = PATTERN_ERROR;
  5115. }
  5116. break;
  5117. case SKIP_INCLUDE_SET:
  5118. //
  5119. // Skip to the next comma or closing brace. We know it is
  5120. // syntatically correct by now.
  5121. //
  5122. if (!SetBegin) {
  5123. State = PATTERN_ERROR;
  5124. break;
  5125. }
  5126. ch = 0;
  5127. while (*SetBegin) {
  5128. ch = *SetBegin;
  5129. if (ch == L'^') {
  5130. SetBegin++; //lint !e613
  5131. } else if (ch == L',' || ch == L']') {
  5132. break;
  5133. }
  5134. SetBegin++;
  5135. }
  5136. MYASSERT (*SetBegin);
  5137. if (ch == L',') {
  5138. SetBegin++;
  5139. }
  5140. State = ReturnState;
  5141. break;
  5142. } //lint !e787
  5143. if (State == PATTERN_DONE || State == PATTERN_ERROR) {
  5144. break;
  5145. }
  5146. }
  5147. GbFree (&ExactMatchBuf);
  5148. GbFree (&SetBuf);
  5149. GbFree (&SegmentArray);
  5150. if (State == PATTERN_ERROR) {
  5151. GbFree (&PatternArray);
  5152. PmDestroyPool (Pool);
  5153. return NULL;
  5154. }
  5155. if (PatternArray.End == 0) {
  5156. //build an empty parsed pattern
  5157. GbFree (&PatternArray);
  5158. Struct->PatternCount = 1;
  5159. Struct->Pool = Pool;
  5160. Struct->Pattern = (PPATTERNPROPSW) PmGetAlignedMemory (
  5161. Pool,
  5162. sizeof (PATTERNPROPSW)
  5163. );
  5164. Struct->Pattern[0].SegmentCount = 1;
  5165. Struct->Pattern[0].Segment = (PSEGMENTW) PmGetAlignedMemory (
  5166. Pool,
  5167. sizeof (PSEGMENTW)
  5168. );
  5169. Struct->Pattern[0].Segment[0].Type = SEGMENTTYPE_EXACTMATCH;
  5170. Struct->Pattern[0].Segment[0].Exact.LowerCasePhrase = PmDuplicateStringW (Pool, L"");
  5171. Struct->Pattern[0].Segment[0].Exact.PhraseBytes = 0;
  5172. return Struct;
  5173. }
  5174. //
  5175. // Copy the fully parsed pattern array into the return struct
  5176. //
  5177. Struct->Pattern = (PPATTERNPROPSW) PmGetAlignedMemory (
  5178. Pool,
  5179. PatternArray.End
  5180. );
  5181. CopyMemory (Struct->Pattern, PatternArray.Buf, (SIZE_T) PatternArray.End);
  5182. Struct->PatternCount = PatternArray.End / sizeof (PATTERNPROPSW);
  5183. Struct->Pool = Pool;
  5184. GbFree (&PatternArray);
  5185. return Struct;
  5186. }
  5187. BOOL
  5188. WildCharsPatternA (
  5189. IN PPARSEDPATTERNA ParsedPattern
  5190. )
  5191. {
  5192. UINT i,j;
  5193. if (!ParsedPattern) {
  5194. return FALSE;
  5195. }
  5196. for (i=0; i<ParsedPattern->PatternCount; i++) {
  5197. if (ParsedPattern->Pattern[i].SegmentCount > 1) {
  5198. return TRUE;
  5199. }
  5200. for (j=0; j<ParsedPattern->Pattern[i].SegmentCount; j++) {
  5201. if ((ParsedPattern->Pattern[i].Segment[j].Type == SEGMENTTYPE_OPTIONAL) ||
  5202. (ParsedPattern->Pattern[i].Segment[j].Type == SEGMENTTYPE_REQUIRED)
  5203. ) {
  5204. return TRUE;
  5205. }
  5206. }
  5207. }
  5208. return FALSE;
  5209. }
  5210. BOOL
  5211. WildCharsPatternW (
  5212. IN PPARSEDPATTERNW ParsedPattern
  5213. )
  5214. {
  5215. UINT i,j;
  5216. if (!ParsedPattern) {
  5217. return FALSE;
  5218. }
  5219. for (i=0; i<ParsedPattern->PatternCount; i++) {
  5220. if (ParsedPattern->Pattern[i].SegmentCount > 1) {
  5221. return TRUE;
  5222. }
  5223. for (j=0; j<ParsedPattern->Pattern[i].SegmentCount; j++) {
  5224. if ((ParsedPattern->Pattern[i].Segment[j].Type == SEGMENTTYPE_OPTIONAL) ||
  5225. (ParsedPattern->Pattern[i].Segment[j].Type == SEGMENTTYPE_REQUIRED)
  5226. ) {
  5227. return TRUE;
  5228. }
  5229. }
  5230. }
  5231. return FALSE;
  5232. }
  5233. BOOL
  5234. ParsedPatternTrimLastCharA (
  5235. IN OUT PPARSEDPATTERNA ParsedPattern
  5236. )
  5237. {
  5238. if (!ParsedPatternHasRootA (ParsedPattern)) {
  5239. return FALSE;
  5240. }
  5241. ParsedPattern->Pattern->Segment[0].Exact.PhraseBytes -= DWSIZEOF (CHAR);
  5242. *(PSTR)((PBYTE)ParsedPattern->Pattern->Segment[0].Exact.LowerCasePhrase +
  5243. ParsedPattern->Pattern->Segment[0].Exact.PhraseBytes) = 0;
  5244. return TRUE;
  5245. }
  5246. BOOL
  5247. ParsedPatternTrimLastCharW (
  5248. IN OUT PPARSEDPATTERNW ParsedPattern
  5249. )
  5250. {
  5251. if (!ParsedPatternHasRootW (ParsedPattern)) {
  5252. return FALSE;
  5253. }
  5254. ParsedPattern->Pattern->Segment[0].Exact.PhraseBytes -= DWSIZEOF (WCHAR);
  5255. *(PWSTR)((PBYTE)ParsedPattern->Pattern->Segment[0].Exact.LowerCasePhrase +
  5256. ParsedPattern->Pattern->Segment[0].Exact.PhraseBytes) = 0;
  5257. return TRUE;
  5258. }
  5259. VOID
  5260. UBINTtoHexA (
  5261. IN UBINT Number,
  5262. OUT PSTR String
  5263. )
  5264. {
  5265. #ifdef IA64
  5266. sprintf (String, "0x%08X%08X", (DWORD)(Number >> 32), (DWORD)Number);
  5267. #else
  5268. sprintf (String, "0x00000000%08X", Number);
  5269. #endif
  5270. }
  5271. VOID
  5272. UBINTtoHexW (
  5273. IN UBINT Number,
  5274. OUT PWSTR String
  5275. )
  5276. {
  5277. #ifdef IA64
  5278. swprintf (String, L"0x%08X%08X", (DWORD)(Number >> 32), (DWORD)Number);
  5279. #else
  5280. swprintf (String, L"0x00000000%08X", Number);
  5281. #endif
  5282. }
  5283. VOID
  5284. UBINTtoDecA (
  5285. IN UBINT Number,
  5286. OUT PSTR String
  5287. )
  5288. {
  5289. #ifdef IA64
  5290. sprintf (String, "%I64u", Number);
  5291. #else
  5292. sprintf (String, "%lu", Number);
  5293. #endif
  5294. }
  5295. VOID
  5296. UBINTtoDecW (
  5297. IN UBINT Number,
  5298. OUT PWSTR String
  5299. )
  5300. {
  5301. #ifdef IA64
  5302. swprintf (String, L"%I64u", Number);
  5303. #else
  5304. swprintf (String, L"%lu", Number);
  5305. #endif
  5306. }
  5307. VOID
  5308. BINTtoDecA (
  5309. IN BINT Number,
  5310. OUT PSTR String
  5311. )
  5312. {
  5313. #ifdef IA64
  5314. sprintf (String, "%I64d", Number);
  5315. #else
  5316. sprintf (String, "%ld", Number);
  5317. #endif
  5318. }
  5319. VOID
  5320. BINTtoDecW (
  5321. IN BINT Number,
  5322. OUT PWSTR String
  5323. )
  5324. {
  5325. #ifdef IA64
  5326. swprintf (String, L"%I64d", Number);
  5327. #else
  5328. swprintf (String, L"%ld", Number);
  5329. #endif
  5330. }
  5331. VOID
  5332. PrintPattern (
  5333. IN PCSTR PatStr,
  5334. IN PPARSEDPATTERNA Struct
  5335. )
  5336. /*++
  5337. Routine Description:
  5338. PrintPattern is used for debugging the pattern parsing and testing
  5339. functions.
  5340. Arguments:
  5341. PatStr - Specifies the original pattern string (which is printed as a
  5342. heading)
  5343. Struct - Specifies the parsed pattern struct
  5344. Return Value:
  5345. None.
  5346. --*/
  5347. {
  5348. CHAR poolStr [sizeof (UBINT) * 2 + 2 + 1];
  5349. UINT u, v;
  5350. printf ("Pattern: %s\n\n", PatStr);
  5351. if (!Struct) {
  5352. printf ("Invalid Pattern\n\n");
  5353. return;
  5354. }
  5355. printf ("PatternCount: %u\n", Struct->PatternCount);
  5356. UBINTtoHexA ((UBINT)Struct->Pool, poolStr);
  5357. printf ("Pool: %s\n", poolStr);
  5358. for (u = 0 ; u < Struct->PatternCount ; u++) {
  5359. printf (" Segment Count: %u\n", Struct->Pattern[u].SegmentCount);
  5360. for (v = 0 ; v < Struct->Pattern->SegmentCount ; v++) {
  5361. printf (" Type: ");
  5362. switch (Struct->Pattern[u].Segment[v].Type) {
  5363. case SEGMENTTYPE_EXACTMATCH:
  5364. printf ("SEGMENTTYPE_EXACTMATCH\n");
  5365. printf (" String: %s\n", Struct->Pattern[u].Segment[v].Exact.LowerCasePhrase);
  5366. printf (" Bytes: %u\n", Struct->Pattern[u].Segment[v].Exact.PhraseBytes);
  5367. break;
  5368. case SEGMENTTYPE_OPTIONAL:
  5369. printf ("SEGMENTTYPE_OPTIONAL\n");
  5370. printf (" MaxLen: %u\n", Struct->Pattern[u].Segment[v].Wildcard.MaxLen);
  5371. printf (" IncludeSet: %s\n", Struct->Pattern[u].Segment[v].Wildcard.IncludeSet);
  5372. printf (" ExcludeSet: %s\n", Struct->Pattern[u].Segment[v].Wildcard.ExcludeSet);
  5373. break;
  5374. case SEGMENTTYPE_REQUIRED:
  5375. printf ("SEGMENTTYPE_REQUIRED\n");
  5376. printf (" MaxLen: %u\n", Struct->Pattern[u].Segment[v].Wildcard.MaxLen);
  5377. printf (" IncludeSet: %s\n", Struct->Pattern[u].Segment[v].Wildcard.IncludeSet);
  5378. printf (" ExcludeSet: %s\n", Struct->Pattern[u].Segment[v].Wildcard.ExcludeSet);
  5379. break;
  5380. } //lint !e744
  5381. }
  5382. }
  5383. printf ("\n");
  5384. }
  5385. /*++
  5386. Routine Description:
  5387. TestParsedPattern finds the end of the string to test and calls
  5388. TestParsedPatternAB.
  5389. Arguments:
  5390. ParsedPattern - Specifies the parsed pattern structure as returned by
  5391. CreateParsedPattern
  5392. StringToTest - Specifies the string to test against the pattern
  5393. Return Value:
  5394. TRUE if the string fits the pattern, FALSE if it does not
  5395. --*/
  5396. BOOL
  5397. TestParsedPatternA (
  5398. IN PPARSEDPATTERNA ParsedPattern,
  5399. IN PCSTR StringToTest
  5400. )
  5401. {
  5402. PCSTR EndPlusOne = GetEndOfStringA (StringToTest);
  5403. return TestParsedPatternABA (ParsedPattern, StringToTest, EndPlusOne);
  5404. }
  5405. BOOL
  5406. TestParsedPatternW (
  5407. IN PPARSEDPATTERNW ParsedPattern,
  5408. IN PCWSTR StringToTest
  5409. )
  5410. {
  5411. PCWSTR EndPlusOne = GetEndOfStringW (StringToTest);
  5412. return TestParsedPatternABW (ParsedPattern, StringToTest, EndPlusOne);
  5413. }
  5414. /*++
  5415. Routine Description:
  5416. pTestSet tests a character against an include and exclude set. The sets are
  5417. formatted in pairs of characters, where the first character in the pair is
  5418. the low range, and the second character in the pair is the high range. The
  5419. specified character will automatically be lower-cased, and all whitespace
  5420. characters are tested against the space character (ascii 32).
  5421. Arguments:
  5422. ch - Specifies the character to test. This character is converted
  5423. to lower case before the test.
  5424. IncludeSet - Specifies the set of characters that ch must be a member of.
  5425. If NULL is specified, then the include set is all characters.
  5426. ExcludeSet - Specifies the range of characters that ch cannot be a member
  5427. of. If NULL is specified, then no characters are excluded.
  5428. Return Value:
  5429. TRUE if ch is in the include set and not in the exclude set; FALSE
  5430. otherwise.
  5431. --*/
  5432. BOOL
  5433. pTestSetA (
  5434. IN MBCHAR ch,
  5435. IN PCSTR IncludeSet, OPTIONAL
  5436. IN PCSTR ExcludeSet OPTIONAL
  5437. )
  5438. {
  5439. MBCHAR LowChar, HighChar;
  5440. BOOL b = TRUE;
  5441. if (isspace ((INT)ch)) {
  5442. if (ch != ' ') {
  5443. if (pTestSetA (' ', IncludeSet, ExcludeSet)) {
  5444. return TRUE;
  5445. }
  5446. }
  5447. } else {
  5448. ch = _mbctolower (ch);
  5449. }
  5450. if (IncludeSet) {
  5451. b = FALSE;
  5452. while (*IncludeSet) {
  5453. LowChar = _mbsnextc (IncludeSet);
  5454. IncludeSet = _mbsinc (IncludeSet);
  5455. HighChar = _mbsnextc (IncludeSet);
  5456. IncludeSet = _mbsinc (IncludeSet);
  5457. if (ch >= LowChar && ch <= HighChar) {
  5458. b = TRUE;
  5459. break;
  5460. }
  5461. }
  5462. }
  5463. if (b && ExcludeSet) {
  5464. while (*ExcludeSet) {
  5465. LowChar = _mbsnextc (ExcludeSet);
  5466. ExcludeSet = _mbsinc (ExcludeSet);
  5467. HighChar = _mbsnextc (ExcludeSet);
  5468. ExcludeSet = _mbsinc (ExcludeSet);
  5469. if (ch >= LowChar && ch <= HighChar) {
  5470. b = FALSE;
  5471. break;
  5472. }
  5473. }
  5474. }
  5475. return b;
  5476. }
  5477. BOOL
  5478. pTestSetW (
  5479. IN WCHAR ch,
  5480. IN PCWSTR IncludeSet, OPTIONAL
  5481. IN PCWSTR ExcludeSet OPTIONAL
  5482. )
  5483. {
  5484. WCHAR LowChar, HighChar;
  5485. BOOL b = TRUE;
  5486. if (iswspace (ch)) {
  5487. if (ch != L' ') {
  5488. if (pTestSetW (L' ', IncludeSet, ExcludeSet)) {
  5489. return TRUE;
  5490. }
  5491. }
  5492. } else {
  5493. ch = towlower (ch);
  5494. }
  5495. if (IncludeSet) {
  5496. b = FALSE;
  5497. while (*IncludeSet) {
  5498. LowChar = *IncludeSet++;
  5499. HighChar = *IncludeSet++;
  5500. if (ch >= LowChar && ch <= HighChar) {
  5501. b = TRUE;
  5502. break;
  5503. }
  5504. }
  5505. }
  5506. if (b && ExcludeSet) {
  5507. while (*ExcludeSet) {
  5508. LowChar = *ExcludeSet++;
  5509. HighChar = *ExcludeSet++;
  5510. if (ch >= LowChar && ch <= HighChar) {
  5511. b = FALSE;
  5512. break;
  5513. }
  5514. }
  5515. }
  5516. return b;
  5517. }
  5518. /*++
  5519. Routine Description:
  5520. pTestOnePatternAB tests a string against a parsed pattern. It loops through
  5521. each segment in the pattern, and calls itself recursively in certain
  5522. circumstances.
  5523. Arguments:
  5524. Pattern - Specifies the parsed pattern, as returned from
  5525. CreateParsedPattern
  5526. StartSeg - Specifies the segment within Pattern to start testing. This
  5527. is used for recursion and outside callers should pass in 0.
  5528. StringToTest - Specifies the string to test against Pattern. In recursion,
  5529. this member will be a pointer to the start of the sub string
  5530. to test.
  5531. EndPlusOne - Specifies one character beyond the end of the string. This
  5532. typically points to the nul terminator.
  5533. Return Value:
  5534. TRUE if the string between StringToTest and EndPlusOne fits Pattern. FALSE
  5535. otherwise.
  5536. --*/
  5537. BOOL
  5538. pTestOnePatternABA (
  5539. IN PPATTERNPROPSA Pattern,
  5540. IN UINT StartSeg,
  5541. IN PCSTR StringToTest,
  5542. IN PCSTR EndPlusOne
  5543. )
  5544. {
  5545. UINT u;
  5546. PSEGMENTA Segment;
  5547. MBCHAR ch1, ch2;
  5548. PCSTR q;
  5549. PCSTR TempEnd;
  5550. UINT BytesLeft;
  5551. UINT Chars;
  5552. for (u = StartSeg ; u < Pattern->SegmentCount ; u++) {
  5553. Segment = &Pattern->Segment[u];
  5554. switch (Segment->Type) {
  5555. case SEGMENTTYPE_EXACTMATCH:
  5556. //
  5557. // Check if the exact match is long enough, or if
  5558. // the remaining string must match
  5559. //
  5560. BytesLeft = (UINT)((PBYTE) EndPlusOne - (PBYTE) StringToTest);
  5561. if (u + 1 == Pattern->SegmentCount) {
  5562. if (BytesLeft != Segment->Exact.PhraseBytes) {
  5563. return FALSE;
  5564. }
  5565. } else if (BytesLeft < Segment->Exact.PhraseBytes) {
  5566. return FALSE;
  5567. }
  5568. //
  5569. // Compare the strings
  5570. //
  5571. q = Segment->Exact.LowerCasePhrase;
  5572. TempEnd = (PCSTR) ((PBYTE) q + Segment->Exact.PhraseBytes);
  5573. ch1 = 0;
  5574. ch2 = 0;
  5575. while (q < TempEnd) {
  5576. ch1 = _mbsnextc (StringToTest);
  5577. ch2 = _mbsnextc (q);
  5578. ch1 = _mbctolower (ch1);
  5579. if (ch1 != ch2) {
  5580. if (ch2 == ' ') {
  5581. if (!isspace ((INT)ch1)) {
  5582. break;
  5583. }
  5584. } else {
  5585. break;
  5586. }
  5587. }
  5588. q = _mbsinc (q);
  5589. StringToTest = _mbsinc (StringToTest);
  5590. }
  5591. if (ch1 != ch2) {
  5592. return FALSE;
  5593. }
  5594. //
  5595. // Continue onto next segment
  5596. //
  5597. break;
  5598. case SEGMENTTYPE_REQUIRED:
  5599. MYASSERT (Segment->Wildcard.MaxLen > 0);
  5600. //
  5601. // Verify there are the correct number of characters
  5602. // in the specified char set
  5603. //
  5604. Chars = Segment->Wildcard.MaxLen;
  5605. if (Segment->Wildcard.IncludeSet || Segment->Wildcard.ExcludeSet) {
  5606. while (StringToTest < EndPlusOne && Chars > 0) {
  5607. if (!pTestSetA (
  5608. _mbsnextc (StringToTest),
  5609. Segment->Wildcard.IncludeSet,
  5610. Segment->Wildcard.ExcludeSet
  5611. )) {
  5612. return FALSE;
  5613. }
  5614. Chars--;
  5615. StringToTest = _mbsinc (StringToTest);
  5616. }
  5617. } else {
  5618. while (StringToTest < EndPlusOne && Chars > 0) {
  5619. Chars--;
  5620. StringToTest = _mbsinc (StringToTest);
  5621. }
  5622. }
  5623. if (Chars) {
  5624. return FALSE;
  5625. }
  5626. if (u + 1 == Pattern->SegmentCount) {
  5627. if (*StringToTest) {
  5628. return FALSE;
  5629. }
  5630. }
  5631. //
  5632. // Continue onto next segment
  5633. //
  5634. break;
  5635. case SEGMENTTYPE_OPTIONAL:
  5636. if (Segment->Wildcard.MaxLen == 0) {
  5637. //
  5638. // Last segment is "anything"
  5639. //
  5640. if (u + 1 == Pattern->SegmentCount &&
  5641. !Segment->Wildcard.IncludeSet &&
  5642. !Segment->Wildcard.ExcludeSet
  5643. ) {
  5644. return TRUE;
  5645. }
  5646. }
  5647. //
  5648. // Find end of optional text
  5649. //
  5650. TempEnd = StringToTest;
  5651. Chars = Segment->Wildcard.MaxLen;
  5652. if (Segment->Wildcard.IncludeSet || Segment->Wildcard.ExcludeSet) {
  5653. if (Chars) {
  5654. while (TempEnd < EndPlusOne && Chars > 0) {
  5655. if (!pTestSetA (
  5656. _mbsnextc (TempEnd),
  5657. Segment->Wildcard.IncludeSet,
  5658. Segment->Wildcard.ExcludeSet
  5659. )) {
  5660. break;
  5661. }
  5662. TempEnd = _mbsinc (TempEnd);
  5663. Chars--;
  5664. }
  5665. } else {
  5666. while (TempEnd < EndPlusOne) {
  5667. if (!pTestSetA (
  5668. _mbsnextc (TempEnd),
  5669. Segment->Wildcard.IncludeSet,
  5670. Segment->Wildcard.ExcludeSet
  5671. )) {
  5672. break;
  5673. }
  5674. TempEnd = _mbsinc (TempEnd);
  5675. }
  5676. }
  5677. } else if (Chars) {
  5678. while (TempEnd < EndPlusOne && Chars > 0) {
  5679. TempEnd = _mbsinc (TempEnd);
  5680. Chars--;
  5681. }
  5682. } else {
  5683. TempEnd = EndPlusOne;
  5684. }
  5685. //
  5686. // If this is the last segment, then match only when
  5687. // the remaining text fits
  5688. //
  5689. if (u + 1 == Pattern->SegmentCount) {
  5690. return TempEnd >= EndPlusOne;
  5691. }
  5692. //
  5693. // Because other segments exist, we must check recursively
  5694. //
  5695. do {
  5696. if (pTestOnePatternABA (Pattern, u + 1, StringToTest, EndPlusOne)) {
  5697. return TRUE;
  5698. }
  5699. StringToTest = _mbsinc (StringToTest);
  5700. } while (StringToTest <= TempEnd);
  5701. //
  5702. // No match
  5703. //
  5704. return FALSE;
  5705. } //lint !e744
  5706. }
  5707. return TRUE;
  5708. }
  5709. BOOL
  5710. pTestOnePatternABW (
  5711. IN PPATTERNPROPSW Pattern,
  5712. IN UINT StartSeg,
  5713. IN PCWSTR StringToTest,
  5714. IN PCWSTR EndPlusOne
  5715. )
  5716. {
  5717. UINT u;
  5718. PSEGMENTW Segment;
  5719. WCHAR ch1, ch2;
  5720. PCWSTR q;
  5721. PCWSTR TempEnd;
  5722. UINT BytesLeft;
  5723. UINT Chars;
  5724. for (u = StartSeg ; u < Pattern->SegmentCount ; u++) {
  5725. Segment = &Pattern->Segment[u];
  5726. switch (Segment->Type) {
  5727. case SEGMENTTYPE_EXACTMATCH:
  5728. //
  5729. // Check if the exact match is long enough, or if
  5730. // the remaining string must match
  5731. //
  5732. BytesLeft = (UINT)((PBYTE) EndPlusOne - (PBYTE) StringToTest);
  5733. if (u + 1 == Pattern->SegmentCount) {
  5734. if (BytesLeft != Segment->Exact.PhraseBytes) {
  5735. return FALSE;
  5736. }
  5737. } else if (BytesLeft < Segment->Exact.PhraseBytes) {
  5738. return FALSE;
  5739. }
  5740. //
  5741. // Compare the strings
  5742. //
  5743. q = Segment->Exact.LowerCasePhrase; //lint !e64
  5744. TempEnd = (PCWSTR) ((PBYTE) q + Segment->Exact.PhraseBytes);
  5745. ch1 = 0;
  5746. ch2 = 0;
  5747. while (q < TempEnd) {
  5748. ch1 = towlower (*StringToTest);
  5749. ch2 = *q;
  5750. if (ch1 != ch2) {
  5751. if (ch2 == L' ') {
  5752. if (!iswspace (ch1)) {
  5753. break;
  5754. }
  5755. } else {
  5756. break;
  5757. }
  5758. }
  5759. q++;
  5760. StringToTest++;
  5761. }
  5762. if (ch1 != ch2) {
  5763. return FALSE;
  5764. }
  5765. //
  5766. // Continue onto next segment
  5767. //
  5768. break;
  5769. case SEGMENTTYPE_REQUIRED:
  5770. MYASSERT (Segment->Wildcard.MaxLen > 0);
  5771. //
  5772. // Verify there are the correct number of characters
  5773. // in the specified char set
  5774. //
  5775. Chars = Segment->Wildcard.MaxLen;
  5776. if (Segment->Wildcard.IncludeSet || Segment->Wildcard.ExcludeSet) {
  5777. while (StringToTest < EndPlusOne && Chars > 0) {
  5778. if (!pTestSetW (
  5779. *StringToTest,
  5780. Segment->Wildcard.IncludeSet, //lint !e64
  5781. Segment->Wildcard.ExcludeSet
  5782. )) { //lint !e64
  5783. return FALSE;
  5784. }
  5785. Chars--;
  5786. StringToTest++;
  5787. }
  5788. if (Chars) {
  5789. return FALSE;
  5790. }
  5791. } else {
  5792. StringToTest += Chars;
  5793. if (StringToTest > EndPlusOne) {
  5794. return FALSE;
  5795. }
  5796. }
  5797. if (u + 1 == Pattern->SegmentCount) {
  5798. if (*StringToTest) {
  5799. return FALSE;
  5800. }
  5801. }
  5802. //
  5803. // Continue onto next segment
  5804. //
  5805. break;
  5806. case SEGMENTTYPE_OPTIONAL:
  5807. if (Segment->Wildcard.MaxLen == 0) {
  5808. //
  5809. // Last segment is "anything"
  5810. //
  5811. if (u + 1 == Pattern->SegmentCount &&
  5812. !Segment->Wildcard.IncludeSet &&
  5813. !Segment->Wildcard.ExcludeSet
  5814. ) {
  5815. return TRUE;
  5816. }
  5817. }
  5818. //
  5819. // Find end of optional text
  5820. //
  5821. TempEnd = StringToTest;
  5822. Chars = Segment->Wildcard.MaxLen;
  5823. if (Segment->Wildcard.IncludeSet || Segment->Wildcard.ExcludeSet) {
  5824. if (Chars) {
  5825. while (TempEnd < EndPlusOne && Chars > 0) {
  5826. if (!pTestSetW (
  5827. *TempEnd,
  5828. Segment->Wildcard.IncludeSet, //lint !e64
  5829. Segment->Wildcard.ExcludeSet
  5830. )) { //lint !e64
  5831. break;
  5832. }
  5833. TempEnd++;
  5834. Chars--;
  5835. }
  5836. } else {
  5837. while (TempEnd < EndPlusOne) {
  5838. if (!pTestSetW (
  5839. *TempEnd,
  5840. Segment->Wildcard.IncludeSet, //lint !e64
  5841. Segment->Wildcard.ExcludeSet
  5842. )) { //lint !e64
  5843. break;
  5844. }
  5845. TempEnd++;
  5846. }
  5847. }
  5848. } else if (Chars) {
  5849. TempEnd += Chars;
  5850. if (TempEnd > EndPlusOne) {
  5851. TempEnd = EndPlusOne;
  5852. }
  5853. } else {
  5854. TempEnd = EndPlusOne;
  5855. }
  5856. //
  5857. // If this is the last segment, then match only when
  5858. // the remaining text fits
  5859. //
  5860. if (u + 1 == Pattern->SegmentCount) {
  5861. return TempEnd >= EndPlusOne;
  5862. }
  5863. //
  5864. // Because other segments exist, we must check recursively
  5865. //
  5866. do {
  5867. if (pTestOnePatternABW (Pattern, u + 1, StringToTest, EndPlusOne)) {
  5868. return TRUE;
  5869. }
  5870. StringToTest++;
  5871. } while (StringToTest <= TempEnd);
  5872. //
  5873. // No match
  5874. //
  5875. return FALSE;
  5876. } //lint !e744
  5877. }
  5878. return TRUE;
  5879. }
  5880. /*++
  5881. Routine Description:
  5882. TestParsedPattternAB loops through all the patterns in ParsedPattern,
  5883. testing the specified string against each. The loop stops at the first
  5884. match.
  5885. Arguments:
  5886. ParsedPattern - Specifies the parsed pattern, as returned from
  5887. CreateParsedPattern
  5888. StringToTest - Specifies the start of the string to test.
  5889. EndPlusOne - Specifies a pointer to the first character after the end of
  5890. the string. This often points to the nul at the end of the
  5891. string. A nul must not exist in between StringToTest and
  5892. EndPlusOne; a nul can only be at *EndPlusOne. A nul is not
  5893. required.
  5894. Return Value:
  5895. TRUE if the string specified between StringToTest and EndPlusOne matches
  5896. Pattern. FALSE otherwise.
  5897. --*/
  5898. BOOL
  5899. TestParsedPatternABA (
  5900. IN PPARSEDPATTERNA ParsedPattern,
  5901. IN PCSTR StringToTest,
  5902. IN PCSTR EndPlusOne
  5903. )
  5904. {
  5905. UINT u;
  5906. BOOL b = FALSE;
  5907. for (u = 0 ; u < ParsedPattern->PatternCount ; u++) {
  5908. b = pTestOnePatternABA (
  5909. &ParsedPattern->Pattern[u],
  5910. 0,
  5911. StringToTest,
  5912. EndPlusOne
  5913. );
  5914. if (b) {
  5915. break;
  5916. }
  5917. }
  5918. return b;
  5919. }
  5920. BOOL
  5921. TestParsedPatternABW (
  5922. IN PPARSEDPATTERNW ParsedPattern,
  5923. IN PCWSTR StringToTest,
  5924. IN PCWSTR EndPlusOne
  5925. )
  5926. {
  5927. UINT u;
  5928. BOOL b = FALSE;
  5929. for (u = 0 ; u < ParsedPattern->PatternCount ; u++) {
  5930. b = pTestOnePatternABW (
  5931. &ParsedPattern->Pattern[u],
  5932. 0,
  5933. StringToTest,
  5934. EndPlusOne
  5935. );
  5936. if (b) {
  5937. break;
  5938. }
  5939. }
  5940. return b;
  5941. }
  5942. /*++
  5943. Routine Description:
  5944. DestroyParsedPattern cleans up a pattern allocated from CreateParsedPattern.
  5945. Arguments:
  5946. ParsedPattern - Specifies the value returned from CreateParsedPattern.
  5947. Return Value:
  5948. None.
  5949. --*/
  5950. VOID
  5951. DestroyParsedPatternA (
  5952. IN PPARSEDPATTERNA ParsedPattern
  5953. )
  5954. {
  5955. if (ParsedPattern) {
  5956. PmDestroyPool (ParsedPattern->Pool);
  5957. }
  5958. }
  5959. VOID
  5960. DestroyParsedPatternW (
  5961. IN PPARSEDPATTERNW ParsedPattern
  5962. )
  5963. {
  5964. if (ParsedPattern) {
  5965. PmDestroyPool (ParsedPattern->Pool);
  5966. }
  5967. }
  5968. /*++
  5969. Routine Description:
  5970. DecodeParsedPattern decodes all exact-matches sub-strings of the given pattern.
  5971. Arguments:
  5972. ParsedPattern - Specifies the parsed pattern.
  5973. Return Value:
  5974. None.
  5975. --*/
  5976. VOID
  5977. DecodeParsedPatternA (
  5978. IN PPARSEDPATTERNA ParsedPattern
  5979. )
  5980. {
  5981. UINT u;
  5982. UINT v;
  5983. PSTR phrase;
  5984. for (u = 0; u < ParsedPattern->PatternCount; u++) {
  5985. for (v = 0; v < ParsedPattern->Pattern[u].SegmentCount; v++) {
  5986. if (ParsedPattern->Pattern[u].Segment[v].Type == SEGMENTTYPE_EXACTMATCH) {
  5987. phrase = (PSTR)ParsedPattern->Pattern[u].Segment[v].Exact.LowerCasePhrase;
  5988. DecodeRuleCharsA (phrase, phrase);
  5989. ParsedPattern->Pattern[u].Segment[v].Exact.PhraseBytes = ByteCountA (phrase);
  5990. }
  5991. }
  5992. }
  5993. }
  5994. VOID
  5995. DecodeParsedPatternW (
  5996. IN PPARSEDPATTERNW ParsedPattern
  5997. )
  5998. {
  5999. UINT u;
  6000. UINT v;
  6001. PWSTR phrase;
  6002. for (u = 0; u < ParsedPattern->PatternCount; u++) {
  6003. for (v = 0; v < ParsedPattern->Pattern[u].SegmentCount; v++) {
  6004. if (ParsedPattern->Pattern[u].Segment[v].Type == SEGMENTTYPE_EXACTMATCH) {
  6005. phrase = (PWSTR)ParsedPattern->Pattern[u].Segment[v].Exact.LowerCasePhrase;
  6006. DecodeRuleCharsW (phrase, phrase);
  6007. ParsedPattern->Pattern[u].Segment[v].Exact.PhraseBytes = ByteCountW (phrase);
  6008. }
  6009. }
  6010. }
  6011. }
  6012. /*++
  6013. Routine Description:
  6014. GetParsedPatternMinMaxSize returns the minimum and the maximum size (in bytes)
  6015. of a string that would match the given parsed pattern.
  6016. Arguments:
  6017. ParsedPattern - Specifies the parsed pattern
  6018. MinSize - Receives the minimum size of a string that would match the pattern
  6019. MaxSize - Receives the maximum size of a string that would match the pattern
  6020. Return Value:
  6021. None.
  6022. --*/
  6023. VOID
  6024. GetParsedPatternMinMaxSizeA (
  6025. IN PPARSEDPATTERNA ParsedPattern,
  6026. OUT PDWORD MinSize,
  6027. OUT PDWORD MaxSize
  6028. )
  6029. {
  6030. UINT u;
  6031. UINT v;
  6032. DWORD pmin;
  6033. DWORD pmax;
  6034. DWORD smin;
  6035. DWORD smax;
  6036. *MinSize = *MaxSize = 0;
  6037. for (u = 0; u < ParsedPattern->PatternCount; u++) {
  6038. pmin = pmax = 0;
  6039. for (v = 0; v < ParsedPattern->Pattern[u].SegmentCount; v++) {
  6040. switch (ParsedPattern->Pattern[u].Segment[v].Type) {
  6041. case SEGMENTTYPE_EXACTMATCH:
  6042. smin = smax = ParsedPattern->Pattern[u].Segment[v].Exact.PhraseBytes;
  6043. break;
  6044. case SEGMENTTYPE_OPTIONAL:
  6045. smin = 0;
  6046. if (ParsedPattern->Pattern[u].Segment[v].Wildcard.MaxLen) {
  6047. smax = ParsedPattern->Pattern[u].Segment[v].Wildcard.MaxLen * DWSIZEOF (CHAR);
  6048. } else {
  6049. smax = DWORD_MAX;
  6050. }
  6051. break;
  6052. case SEGMENTTYPE_REQUIRED:
  6053. MYASSERT (ParsedPattern->Pattern[u].Segment[v].Wildcard.MaxLen > 0);
  6054. smin = smax = ParsedPattern->Pattern[u].Segment[v].Wildcard.MaxLen * DWSIZEOF (CHAR);
  6055. break;
  6056. default:
  6057. MYASSERT (FALSE); //lint !e506
  6058. smin = smax = 0;
  6059. }
  6060. pmin += smin;
  6061. if (pmax < DWORD_MAX) {
  6062. if (smax < DWORD_MAX) {
  6063. pmax += smax;
  6064. } else {
  6065. pmax = DWORD_MAX;
  6066. }
  6067. }
  6068. }
  6069. if (pmin < *MinSize) {
  6070. *MinSize = pmin;
  6071. }
  6072. if (pmax > *MaxSize) {
  6073. *MaxSize = pmax;
  6074. }
  6075. }
  6076. }
  6077. VOID
  6078. GetParsedPatternMinMaxSizeW (
  6079. IN PPARSEDPATTERNW ParsedPattern,
  6080. OUT PDWORD MinSize,
  6081. OUT PDWORD MaxSize
  6082. )
  6083. {
  6084. UINT u;
  6085. UINT v;
  6086. DWORD pmin;
  6087. DWORD pmax;
  6088. DWORD smin;
  6089. DWORD smax;
  6090. *MinSize = *MaxSize = 0;
  6091. for (u = 0; u < ParsedPattern->PatternCount; u++) {
  6092. pmin = pmax = 0;
  6093. for (v = 0; v < ParsedPattern->Pattern[u].SegmentCount; v++) {
  6094. switch (ParsedPattern->Pattern[u].Segment[v].Type) {
  6095. case SEGMENTTYPE_EXACTMATCH:
  6096. smin = smax = ParsedPattern->Pattern[u].Segment[v].Exact.PhraseBytes;
  6097. break;
  6098. case SEGMENTTYPE_OPTIONAL:
  6099. smin = 0;
  6100. if (ParsedPattern->Pattern[u].Segment[v].Wildcard.MaxLen) {
  6101. smax = ParsedPattern->Pattern[u].Segment[v].Wildcard.MaxLen * DWSIZEOF (WCHAR);
  6102. } else {
  6103. smax = DWORD_MAX;
  6104. }
  6105. break;
  6106. case SEGMENTTYPE_REQUIRED:
  6107. MYASSERT (ParsedPattern->Pattern[u].Segment[v].Wildcard.MaxLen > 0);
  6108. smin = smax = ParsedPattern->Pattern[u].Segment[v].Wildcard.MaxLen * DWSIZEOF (WCHAR);
  6109. break;
  6110. default:
  6111. MYASSERT (FALSE); //lint !e506
  6112. smin = smax = 0;
  6113. }
  6114. pmin += smin;
  6115. if (pmax < DWORD_MAX) {
  6116. if (smax < DWORD_MAX) {
  6117. pmax += smax;
  6118. } else {
  6119. pmax = DWORD_MAX;
  6120. }
  6121. }
  6122. }
  6123. if (pmin < *MinSize) {
  6124. *MinSize = pmin;
  6125. }
  6126. if (pmax > *MaxSize) {
  6127. *MaxSize = pmax;
  6128. }
  6129. }
  6130. }
  6131. /*++
  6132. Routine Description:
  6133. PatternIncludesPattern decides if a given pattern includes another pattern,
  6134. meaning that any string that would match the second will match the first.
  6135. Arguments:
  6136. IncludingPattern - Specifies the first parsed pattern
  6137. IncludedPattern - Specifies the second parsed pattern
  6138. Return Value:
  6139. TRUE if the first pattern includes the second
  6140. --*/
  6141. BOOL
  6142. PatternIncludesPatternA (
  6143. IN PPARSEDPATTERNA IncludingPattern,
  6144. IN PPARSEDPATTERNA IncludedPattern
  6145. )
  6146. {
  6147. PPATTERNPROPSA pp1;
  6148. PPATTERNPROPSA pp2;
  6149. PSEGMENTA ps1;
  6150. PSEGMENTA ps2;
  6151. DWORD min1;
  6152. DWORD max1;
  6153. DWORD min2;
  6154. DWORD max2;
  6155. //
  6156. // only deal with simple patterns for now (PatternCount == 1)
  6157. //
  6158. if (IncludingPattern->PatternCount > 1 || IncludedPattern->PatternCount > 1) {
  6159. DEBUGMSGA ((DBG_ERROR, "PatternIncludesPatternA: multiple patterns not supported yet"));
  6160. return FALSE;
  6161. }
  6162. //
  6163. // test the usual cases first, quickly
  6164. //
  6165. pp1 = IncludingPattern->Pattern;
  6166. MYASSERT (pp1);
  6167. if (pp1->SegmentCount == 1 && ParsedPatternSegmentIsPureOptionalA (pp1->Segment)) {
  6168. return TRUE;
  6169. }
  6170. pp2 = IncludedPattern->Pattern;
  6171. MYASSERT (pp2);
  6172. if (pp2->SegmentCount == 1 && ParsedPatternSegmentIsPureOptionalA (pp2->Segment)) {
  6173. return FALSE;
  6174. }
  6175. if (pp1->SegmentCount == 1) {
  6176. ps1 = pp1->Segment;
  6177. if (ps1->Type == SEGMENTTYPE_EXACTMATCH) {
  6178. if (pp2->SegmentCount == 1) {
  6179. ps2 = pp2->Segment;
  6180. if (ps2->Type == SEGMENTTYPE_EXACTMATCH) {
  6181. return ps1->Exact.PhraseBytes == ps2->Exact.PhraseBytes &&
  6182. StringMatchA (ps1->Exact.LowerCasePhrase, ps2->Exact.LowerCasePhrase);
  6183. }
  6184. }
  6185. }
  6186. } else if (pp1->SegmentCount == 2) {
  6187. ps1 = pp1->Segment;
  6188. if (ps1->Type == SEGMENTTYPE_EXACTMATCH) {
  6189. if (ParsedPatternSegmentIsPureOptionalA (pp1->Segment + 1)) {
  6190. if (pp2->SegmentCount == 1) {
  6191. ps2 = pp2->Segment;
  6192. if (ps2->Type == SEGMENTTYPE_EXACTMATCH) {
  6193. return ps1->Exact.PhraseBytes <= ps2->Exact.PhraseBytes &&
  6194. StringMatchByteCountA (
  6195. ps1->Exact.LowerCasePhrase,
  6196. ps2->Exact.LowerCasePhrase,
  6197. ps1->Exact.PhraseBytes
  6198. );
  6199. }
  6200. } else if (pp2->SegmentCount == 2) {
  6201. ps2 = pp2->Segment;
  6202. if (ps2->Type == SEGMENTTYPE_EXACTMATCH) {
  6203. if (ParsedPatternSegmentIsPureOptionalA (pp2->Segment + 1)) {
  6204. return ps1->Exact.PhraseBytes <= ps2->Exact.PhraseBytes &&
  6205. StringMatchByteCountA (
  6206. ps1->Exact.LowerCasePhrase,
  6207. ps2->Exact.LowerCasePhrase,
  6208. ps1->Exact.PhraseBytes
  6209. );
  6210. }
  6211. }
  6212. }
  6213. }
  6214. }
  6215. }
  6216. GetParsedPatternMinMaxSizeA (IncludingPattern, &min1, &max1);
  6217. GetParsedPatternMinMaxSizeA (IncludedPattern, &min2, &max2);
  6218. if (min2 < min1 || max2 > max1) {
  6219. return FALSE;
  6220. }
  6221. //
  6222. // BUGBUG - not implemented yet
  6223. //
  6224. return FALSE;
  6225. }
  6226. BOOL
  6227. PatternIncludesPatternW (
  6228. IN PPARSEDPATTERNW IncludingPattern,
  6229. IN PPARSEDPATTERNW IncludedPattern
  6230. )
  6231. {
  6232. PPATTERNPROPSW pp1;
  6233. PPATTERNPROPSW pp2;
  6234. PSEGMENTW ps1;
  6235. PSEGMENTW ps2;
  6236. DWORD min1;
  6237. DWORD max1;
  6238. DWORD min2;
  6239. DWORD max2;
  6240. //
  6241. // only deal with simple patterns for now (PatternCount == 1)
  6242. //
  6243. if (IncludingPattern->PatternCount > 1 || IncludedPattern->PatternCount > 1) {
  6244. DEBUGMSGW ((DBG_ERROR, "PatternIncludesPatternW: multiple patterns not supported yet"));
  6245. return FALSE;
  6246. }
  6247. //
  6248. // test the usual cases first, quickly
  6249. //
  6250. pp1 = IncludingPattern->Pattern;
  6251. MYASSERT (pp1);
  6252. if (pp1->SegmentCount == 1 && ParsedPatternSegmentIsPureOptionalW (pp1->Segment)) {
  6253. return TRUE;
  6254. }
  6255. pp2 = IncludedPattern->Pattern;
  6256. MYASSERT (pp2);
  6257. if (pp2->SegmentCount == 1 && ParsedPatternSegmentIsPureOptionalW (pp2->Segment)) {
  6258. return FALSE;
  6259. }
  6260. if (pp1->SegmentCount == 1) {
  6261. ps1 = pp1->Segment;
  6262. if (ps1->Type == SEGMENTTYPE_EXACTMATCH) {
  6263. if (pp2->SegmentCount == 1) {
  6264. ps2 = pp2->Segment;
  6265. if (ps2->Type == SEGMENTTYPE_EXACTMATCH) {
  6266. return ps1->Exact.PhraseBytes == ps2->Exact.PhraseBytes &&
  6267. StringMatchW (ps1->Exact.LowerCasePhrase, ps2->Exact.LowerCasePhrase); //lint !e64
  6268. }
  6269. }
  6270. }
  6271. } else if (pp1->SegmentCount == 2) {
  6272. ps1 = pp1->Segment;
  6273. if (ps1->Type == SEGMENTTYPE_EXACTMATCH) {
  6274. if (ParsedPatternSegmentIsPureOptionalW (pp1->Segment + 1)) {
  6275. if (pp2->SegmentCount == 1) {
  6276. ps2 = pp2->Segment;
  6277. if (ps2->Type == SEGMENTTYPE_EXACTMATCH) {
  6278. return ps1->Exact.PhraseBytes <= ps2->Exact.PhraseBytes &&
  6279. StringMatchByteCountW (
  6280. ps1->Exact.LowerCasePhrase,
  6281. ps2->Exact.LowerCasePhrase,
  6282. ps1->Exact.PhraseBytes
  6283. ); //lint !e64
  6284. }
  6285. } else if (pp2->SegmentCount == 2) {
  6286. ps2 = pp2->Segment;
  6287. if (ps2->Type == SEGMENTTYPE_EXACTMATCH) {
  6288. if (ParsedPatternSegmentIsPureOptionalW (pp2->Segment + 1)) {
  6289. return ps1->Exact.PhraseBytes <= ps2->Exact.PhraseBytes &&
  6290. StringMatchByteCountW (
  6291. ps1->Exact.LowerCasePhrase,
  6292. ps2->Exact.LowerCasePhrase,
  6293. ps1->Exact.PhraseBytes
  6294. ); //lint !e64
  6295. }
  6296. }
  6297. }
  6298. }
  6299. }
  6300. }
  6301. GetParsedPatternMinMaxSizeW (IncludingPattern, &min1, &max1);
  6302. GetParsedPatternMinMaxSizeW (IncludedPattern, &min2, &max2);
  6303. if (min2 < min1 || max2 > max1) {
  6304. return FALSE;
  6305. }
  6306. //
  6307. // BUGBUG - not implemented yet
  6308. //
  6309. return FALSE;
  6310. }
  6311. VOID
  6312. _copymbchar (
  6313. OUT PSTR sz1,
  6314. IN PCSTR sz2
  6315. )
  6316. /*++
  6317. Routine Description:
  6318. _copymbchar transfers the character at sz2 to sz1, which may be one or
  6319. two bytes long.
  6320. Arguments:
  6321. sz1 - The destination string
  6322. sz2 - The source string
  6323. Return Value:
  6324. none
  6325. --*/
  6326. {
  6327. if (IsLeadByte (*sz2)) {
  6328. sz1[1] = sz2[1];
  6329. }
  6330. *sz1 = *sz2;
  6331. }
  6332. /*++
  6333. Routine Description:
  6334. _tcsctrim removes character c from the end of str if it exists. It removes
  6335. only one character at the most.
  6336. Arguments:
  6337. str - A pointer to the string that may have character c at the end
  6338. c - The character that may be at the end of the string
  6339. Return Value:
  6340. TRUE if character c was at the end of the string, or FALSE if it was not.
  6341. --*/
  6342. BOOL
  6343. _mbsctrim (
  6344. OUT PSTR str,
  6345. IN MBCHAR c
  6346. )
  6347. {
  6348. PSTR end;
  6349. end = GetEndOfStringA (str);
  6350. end = _mbsdec2 (str, end);
  6351. if (end && _mbsnextc (end) == c) {
  6352. *end = 0;
  6353. return TRUE;
  6354. }
  6355. return FALSE;
  6356. }
  6357. BOOL
  6358. _wcsctrim (
  6359. OUT PWSTR str,
  6360. IN WCHAR c
  6361. )
  6362. {
  6363. PWSTR end;
  6364. end = GetEndOfStringW (str);
  6365. end == str ? end = NULL : end--;
  6366. if (end && *end == c) {
  6367. *end = 0;
  6368. return TRUE;
  6369. }
  6370. return FALSE;
  6371. }
  6372. /*++
  6373. Routine Description:
  6374. The FreeStringResourceEx functions are used to free a recently used
  6375. string that is not being passed back to the caller. In almost all
  6376. cases, this string is at the end of our array of pointers, so we can
  6377. efficiently search sequentially in reverse order. If the pointer is
  6378. not the last element of the array, it is first swapped with the real
  6379. last element of the array so the array size is reduced.
  6380. Arguments:
  6381. AllocTable - The GROWBUFFER table that holds the list of previously
  6382. allocated strings (return values of ParseMessageEx or
  6383. GetResourceStringEx).
  6384. String - A pointer to the string that is in AllocTable
  6385. Return Value:
  6386. none
  6387. --*/
  6388. VOID
  6389. FreeStringResourceExA (
  6390. IN PGROWBUFFER AllocTable,
  6391. IN PCSTR String
  6392. )
  6393. {
  6394. PCTSTR *ptr, *end, *start;
  6395. if (!String) {
  6396. return;
  6397. }
  6398. //
  6399. // Locate string (search sequentially in reverse order)
  6400. //
  6401. if (AllocTable->End < sizeof (PCSTR)) {
  6402. DEBUGMSG ((DBG_ERROR, "FreeStringResourceA: Attempt to free address %x (%s); address table empty", String, String));
  6403. return;
  6404. }
  6405. start = (PCSTR *) AllocTable->Buf;
  6406. end = (PCSTR *) (AllocTable->Buf + AllocTable->End - sizeof (PCSTR));
  6407. ptr = end;
  6408. while (ptr >= start) {
  6409. if (*ptr == String) {
  6410. break;
  6411. }
  6412. ptr--;
  6413. }
  6414. //
  6415. // String not found case
  6416. //
  6417. if (ptr < start) {
  6418. DEBUGMSG ((DBG_ERROR, "FreeStringResourceA: Attempt to free address %x (%s); address not found in table", String, String));
  6419. return;
  6420. }
  6421. //
  6422. // Free LocalAlloc'd memory
  6423. //
  6424. LocalFree ((HLOCAL) String);
  6425. //
  6426. // If this element is not the end, copy real end to the ptr
  6427. //
  6428. if (ptr < end) {
  6429. *ptr = *end;
  6430. }
  6431. //
  6432. // Shrink buffer size
  6433. //
  6434. AllocTable->End -= sizeof (PCSTR);
  6435. }
  6436. VOID
  6437. FreeStringResourcePtrExA (
  6438. IN PGROWBUFFER AllocTable,
  6439. IN OUT PCSTR * String
  6440. )
  6441. {
  6442. if (NULL != *String) {
  6443. FreeStringResourceExA(AllocTable, *String);
  6444. *String = NULL;
  6445. }
  6446. }
  6447. VOID
  6448. FreeStringResourceExW (
  6449. IN PGROWBUFFER AllocTable,
  6450. IN PCWSTR String
  6451. )
  6452. {
  6453. FreeStringResourceExA (AllocTable, (PCSTR) String);
  6454. }
  6455. VOID
  6456. FreeStringResourcePtrExW (
  6457. IN PGROWBUFFER AllocTable,
  6458. IN OUT PCWSTR * String
  6459. )
  6460. {
  6461. if (NULL != *String) {
  6462. FreeStringResourceExW(AllocTable, *String);
  6463. *String = NULL;
  6464. }
  6465. }
  6466. /*++
  6467. Routine Description:
  6468. The pAddStringResource function is used to track pointers allocated
  6469. by FormatMessage. They are added to an array (maintained in a GROWBUFFER
  6470. structure). This table of pointers is used by FreeStringResource or
  6471. StringResourceFree.
  6472. Arguments:
  6473. String - A pointer to a LocalAlloc'd string (the return value of
  6474. FormatMessage). This string is added to a table of allocated
  6475. strings.
  6476. Return Value:
  6477. none
  6478. --*/
  6479. VOID
  6480. pAddStringResource (
  6481. IN PGROWBUFFER GrowBuf,
  6482. IN PCSTR String
  6483. )
  6484. {
  6485. PCSTR *ptr;
  6486. ptr = (PCSTR *) GbGrow (GrowBuf, sizeof (PCSTR));
  6487. if (ptr) {
  6488. *ptr = String;
  6489. }
  6490. ELSE_DEBUGMSG ((DBG_ERROR, "pAddStringResource: GrowBuffer failure caused memory leak"));
  6491. }
  6492. /*++
  6493. Routine Description:
  6494. pFreeAllStringResourcesEx frees all strings currently listed in AllocTable.
  6495. This function allows the caller to wait until all processing is done
  6496. to clean up string resources that may have been allocated.
  6497. Arguments:
  6498. none
  6499. Return Value:
  6500. none
  6501. --*/
  6502. VOID
  6503. pFreeAllStringResourcesEx (
  6504. IN PGROWBUFFER AllocTable
  6505. )
  6506. {
  6507. PCSTR *ptr, *start, *end;
  6508. if (AllocTable->End) {
  6509. start = (PCSTR *) AllocTable->Buf;
  6510. end = (PCSTR *) (AllocTable->Buf + AllocTable->End);
  6511. for (ptr = start ; ptr < end ; ptr++) {
  6512. LocalFree ((HLOCAL) (*ptr));
  6513. }
  6514. }
  6515. GbFree (AllocTable);
  6516. }
  6517. /*++
  6518. Routine Description:
  6519. CreateAllocTable creates a GROWBUFFER structure that can be used with
  6520. ParseMessageEx, GetStringResourceEx, FreeStringResourceEx and
  6521. pFreeAllStringResourcesEx. Call this function to recieve a private
  6522. allocation table to pass to these functions. Call DestroyAllocTable
  6523. to clean up.
  6524. Arguments:
  6525. none
  6526. Return Value:
  6527. A pointer to a GROWBUFFER structure, or NULL if a memory allocation failed.
  6528. --*/
  6529. PGROWBUFFER
  6530. RealCreateAllocTable (
  6531. VOID
  6532. )
  6533. {
  6534. PGROWBUFFER allocTable;
  6535. GROWBUFFER tempForInit = INIT_GROWBUFFER;
  6536. allocTable = (PGROWBUFFER) MemAlloc (g_hHeap, 0, sizeof (GROWBUFFER));
  6537. CopyMemory (allocTable, &tempForInit, sizeof (GROWBUFFER));
  6538. return allocTable;
  6539. }
  6540. /*++
  6541. Routine Description:
  6542. DestroyAllocTable cleans up all memory associated with an AllocTable.
  6543. Arguments:
  6544. AllocTable - A pointer to a GROWBUFFER structure allocated by CreateAllocTable
  6545. Return Value:
  6546. none
  6547. --*/
  6548. VOID
  6549. DestroyAllocTable (
  6550. OUT PGROWBUFFER AllocTable
  6551. )
  6552. {
  6553. MYASSERT (AllocTable);
  6554. pFreeAllStringResourcesEx (AllocTable);
  6555. MemFree (g_hHeap, 0, AllocTable);
  6556. }
  6557. /*++
  6558. Routine Description:
  6559. BeginMessageProcessing enters a guarded section of code that plans to use the
  6560. ParseMessage and GetStringResource functions, but needs cleanup at the end
  6561. of processing.
  6562. EndMessageProcessing destroys all memory allocated within the message processing
  6563. block, and leaves the guarded section.
  6564. Arguments:
  6565. none
  6566. Return Value:
  6567. BeginMessageProcessing returns FALSE if an out-of-memory condition occurrs.
  6568. --*/
  6569. BOOL
  6570. BeginMessageProcessing (
  6571. VOID
  6572. )
  6573. {
  6574. if (!TryEnterOurCriticalSection (&g_MessageCs)) {
  6575. DEBUGMSG ((DBG_ERROR, "Thread attempting to enter BeginMessageProcessing while another"
  6576. "thread is processing messages as well."));
  6577. EnterOurCriticalSection (&g_MessageCs);
  6578. }
  6579. g_LastAllocTable = g_ShortTermAllocTable;
  6580. g_ShortTermAllocTable = CreateAllocTable();
  6581. MYASSERT (g_ShortTermAllocTable);
  6582. return TRUE;
  6583. }
  6584. VOID
  6585. EndMessageProcessing (
  6586. VOID
  6587. )
  6588. {
  6589. if (TryEnterOurCriticalSection (&g_MessageCs)) {
  6590. DEBUGMSG ((DBG_ERROR, "Thread attempting to end message processing when it hasn't been started"));
  6591. LeaveOurCriticalSection (&g_MessageCs);
  6592. return;
  6593. }
  6594. DestroyAllocTable (g_ShortTermAllocTable);
  6595. g_ShortTermAllocTable = g_LastAllocTable;
  6596. LeaveOurCriticalSection (&g_MessageCs);
  6597. }
  6598. /*++
  6599. Routine Description:
  6600. ParseMessage is used to obtain a string from the executable's message table
  6601. and parse it with FormatMessage. An array of arguments can be passed by
  6602. the caller. FormatMessage will replace %1 with the first element of the
  6603. array, %2 with the second element, and so on. The array does not need to
  6604. be terminated, and if a message string uses %n, element n must be non-NULL.
  6605. Arguments:
  6606. Template - A string indicating which message to extract, or a WORD value
  6607. cast as a string. (ParseMessageID does this cast via a macro.)
  6608. ArgArray - Optional array of string pointers, where the meaning depends on
  6609. the message string. A reference in the message string to %n
  6610. requires element n of ArgArray to be a valid string pointer.
  6611. Return Value:
  6612. Pointer to the string allocated. Call StringResourceFree to free all
  6613. allocated strings (a one-time cleanup for all strings). The pointer may
  6614. be NULL if the resource does not exist or is empty.
  6615. --*/
  6616. PCSTR
  6617. ParseMessageExA (
  6618. IN PGROWBUFFER AllocTable,
  6619. IN PCSTR Template,
  6620. IN PCSTR ArgArray[]
  6621. )
  6622. {
  6623. PSTR MsgBuf;
  6624. DWORD rc;
  6625. if (SHIFTRIGHT16 ((UBINT)Template)) {
  6626. //
  6627. // From string
  6628. //
  6629. rc = FormatMessageA (
  6630. FORMAT_MESSAGE_ALLOCATE_BUFFER |
  6631. FORMAT_MESSAGE_ARGUMENT_ARRAY |
  6632. FORMAT_MESSAGE_FROM_STRING,
  6633. (LPVOID) Template,
  6634. 0,
  6635. 0,
  6636. (LPVOID) &MsgBuf,
  6637. 0,
  6638. (va_list *) ArgArray
  6639. );
  6640. } else {
  6641. //
  6642. // From resource
  6643. //
  6644. rc = FormatMessageA (
  6645. FORMAT_MESSAGE_ALLOCATE_BUFFER |
  6646. FORMAT_MESSAGE_ARGUMENT_ARRAY |
  6647. FORMAT_MESSAGE_FROM_HMODULE,
  6648. (LPVOID) g_hInst,
  6649. (DWORD)((UBINT)Template),
  6650. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  6651. (LPVOID) &MsgBuf,
  6652. 0,
  6653. (va_list *) ArgArray
  6654. );
  6655. }
  6656. if (rc > 0) {
  6657. pAddStringResource (AllocTable, MsgBuf);
  6658. return MsgBuf;
  6659. }
  6660. return NULL;
  6661. }
  6662. PCWSTR
  6663. ParseMessageExW (
  6664. IN PGROWBUFFER AllocTable,
  6665. IN PCWSTR Template,
  6666. IN PCWSTR ArgArray[]
  6667. )
  6668. {
  6669. PWSTR MsgBuf;
  6670. DWORD rc;
  6671. if (SHIFTRIGHT16 ((UBINT)Template)) {
  6672. //
  6673. // From string
  6674. //
  6675. rc = FormatMessageW (
  6676. FORMAT_MESSAGE_ALLOCATE_BUFFER |
  6677. FORMAT_MESSAGE_ARGUMENT_ARRAY |
  6678. FORMAT_MESSAGE_FROM_STRING,
  6679. (LPVOID) Template,
  6680. 0,
  6681. 0,
  6682. (LPVOID) &MsgBuf,
  6683. 0,
  6684. (va_list *) ArgArray
  6685. );
  6686. } else {
  6687. //
  6688. // From resource
  6689. //
  6690. rc = FormatMessageW (
  6691. FORMAT_MESSAGE_ALLOCATE_BUFFER |
  6692. FORMAT_MESSAGE_ARGUMENT_ARRAY |
  6693. FORMAT_MESSAGE_FROM_HMODULE,
  6694. (LPVOID) g_hInst,
  6695. (DWORD)(UBINT)Template,
  6696. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  6697. (LPVOID) &MsgBuf,
  6698. 0,
  6699. (va_list *) ArgArray
  6700. );
  6701. }
  6702. if (rc > 0) {
  6703. pAddStringResource (AllocTable, (PCSTR) MsgBuf);
  6704. return MsgBuf;
  6705. }
  6706. return NULL;
  6707. }
  6708. /*++
  6709. Routine Description:
  6710. GetStringResourceEx is an argument-less wrapper of ParseMessageEx. It allows
  6711. the caller to specify a message ID and recieve a pointer to the string if
  6712. it exists, and a table to track FormatMessage's allocations.
  6713. Arguments:
  6714. AllocTable - A pointer to a GROWBUFFER structure that is used to maintain
  6715. the handles of allocated strings
  6716. ID - The ID of the message resource to retrieve
  6717. Return Value:
  6718. Pointer to the string allocated. The return pointer may
  6719. be NULL if the resource does not exist or is empty.
  6720. Call FreeStringResource or DestroyAllocTable to clean up AllocTable.
  6721. --*/
  6722. PCSTR
  6723. GetStringResourceExA (
  6724. IN OUT PGROWBUFFER AllocTable,
  6725. IN UINT ID
  6726. )
  6727. {
  6728. return ParseMessageExA (AllocTable, (PSTR) (WORD) ID, NULL);
  6729. }
  6730. PCWSTR
  6731. GetStringResourceExW (
  6732. IN OUT PGROWBUFFER AllocTable,
  6733. IN UINT ID
  6734. )
  6735. {
  6736. return ParseMessageExW (AllocTable, (PWSTR) (WORD) ID, NULL);
  6737. }
  6738. /*++
  6739. Routine Description:
  6740. ParseMessageInWnd is used to exchange a string in a window with one from
  6741. the executable's message table. It is provided for dialog box initialization,
  6742. where a field in the dialog box requires dynamic data. The dialog box
  6743. resource should contain a control with its window text set to the message
  6744. string. Upon processing WM_INITDIALOG, the code should call ParseMessageInWnd,
  6745. supplying the necessary ArgArray, so the dialog box is initialized with
  6746. a dynamic message.
  6747. Arguments:
  6748. hwnd - The handle of a window whose title contains the message string ID
  6749. ArgArray - Optional array of string pointers, where the meaning depends on
  6750. the message string. A reference in the message string to %n
  6751. requires element n of ArgArray to be a valid string pointer.
  6752. Return Value:
  6753. none
  6754. --*/
  6755. VOID
  6756. ParseMessageInWndA (
  6757. IN HWND Hwnd,
  6758. IN PCSTR ArgArray[]
  6759. )
  6760. {
  6761. CHAR buffer[512];
  6762. PCSTR parsedMsg;
  6763. GetWindowTextA (Hwnd, buffer, 512);
  6764. parsedMsg = ParseMessageA (buffer, ArgArray);
  6765. if (parsedMsg) {
  6766. SetWindowTextA (Hwnd, parsedMsg);
  6767. FreeStringResourceA (parsedMsg);
  6768. }
  6769. }
  6770. VOID
  6771. ParseMessageInWndW (
  6772. IN HWND hwnd,
  6773. IN PCWSTR ArgArray[]
  6774. )
  6775. {
  6776. WCHAR buffer[512];
  6777. PCWSTR parsedMsg;
  6778. GetWindowTextW (hwnd, buffer, 512);
  6779. parsedMsg = ParseMessageW (buffer, ArgArray);
  6780. if (parsedMsg) {
  6781. SetWindowTextW (hwnd, parsedMsg);
  6782. FreeStringResourceW (parsedMsg);
  6783. }
  6784. }
  6785. /*++
  6786. Routine Description:
  6787. ResourceMessageBox is used to display a message based on a message resource
  6788. ID.
  6789. Arguments:
  6790. HwndOwner - The handle of the owner of the message box to be displayed
  6791. ID - The identifier of the message resource
  6792. Flags - MessageBox flags (MB_OK, etc.)
  6793. ArgArray - Optional array of string pointers, where the meaning depends on
  6794. the message string. A reference in the message string to %n
  6795. requires element n of ArgArray to be a valid string pointer.
  6796. Return Value:
  6797. The return value of MessageBox (MB_YES, etc.)
  6798. --*/
  6799. INT
  6800. ResourceMessageBoxA (
  6801. IN HWND HwndOwner,
  6802. IN UINT ID,
  6803. IN UINT Flags,
  6804. IN PCSTR ArgArray[]
  6805. )
  6806. {
  6807. PCSTR message;
  6808. PCSTR title;
  6809. int rc;
  6810. message = ParseMessageA ((PSTR)(UBINT)ID, ArgArray);
  6811. if (!message)
  6812. return -1;
  6813. title = GetStringResourceA (MSG_MESSAGEBOX_TITLE);
  6814. rc = MessageBoxA (HwndOwner, message, title, Flags);
  6815. FreeStringResourceA (message);
  6816. if (title) {
  6817. FreeStringResourceA (title);
  6818. }
  6819. return rc;
  6820. }
  6821. INT
  6822. ResourceMessageBoxW (
  6823. IN HWND HwndOwner,
  6824. IN UINT ID,
  6825. IN UINT Flags,
  6826. IN PCWSTR ArgArray[]
  6827. )
  6828. {
  6829. PCWSTR message;
  6830. PCWSTR title;
  6831. int rc;
  6832. message = ParseMessageW ((PWSTR)(UBINT)ID, ArgArray);
  6833. if (!message)
  6834. return -1;
  6835. title = GetStringResourceW (MSG_MESSAGEBOX_TITLE);
  6836. rc = MessageBoxW (HwndOwner, message, title, Flags);
  6837. FreeStringResourceW (message);
  6838. if (title) {
  6839. FreeStringResourceW (title);
  6840. }
  6841. return rc;
  6842. }
  6843. BOOL
  6844. StringReplaceA (
  6845. IN PSTR Buffer,
  6846. IN DWORD MaxSize,
  6847. IN PSTR ReplaceStartPos,
  6848. IN PSTR ReplaceEndPos,
  6849. IN PCSTR NewString
  6850. )
  6851. {
  6852. BOOL rf = FALSE;
  6853. DWORD oldSubStringLength;
  6854. DWORD newSubStringLength;
  6855. DWORD currentStringLength;
  6856. LONG offset;
  6857. PSTR movePosition;
  6858. //
  6859. // Check assumptions.
  6860. //
  6861. MYASSERT(Buffer);
  6862. MYASSERT(ReplaceStartPos && ReplaceStartPos >= Buffer);
  6863. MYASSERT(ReplaceEndPos && ReplaceEndPos >= ReplaceStartPos); //lint !e613
  6864. MYASSERT(NewString);
  6865. //
  6866. // Compute sizes.
  6867. //
  6868. oldSubStringLength = (DWORD)((UBINT)ReplaceEndPos - (UBINT)ReplaceStartPos);
  6869. newSubStringLength = ByteCountA(NewString);
  6870. currentStringLength = SizeOfStringA(Buffer) + 1;
  6871. offset = (LONG)newSubStringLength - (LONG)oldSubStringLength;
  6872. //
  6873. // Make sure there is enough room in the buffer to perform the replace
  6874. // operation.
  6875. //
  6876. if ((LONG)currentStringLength + offset > (LONG)MaxSize) {
  6877. DEBUGMSG((DBG_WARNING,"ERROR: Buffer to small to perform string replacement."));
  6878. rf = FALSE;
  6879. } else {
  6880. //
  6881. // Shift the rest of the buffer to adjust it to the size of the new string.
  6882. //
  6883. if (newSubStringLength > oldSubStringLength) {
  6884. //
  6885. // right shift.
  6886. //
  6887. for (movePosition = Buffer + currentStringLength;
  6888. (UBINT)movePosition >= (UBINT)ReplaceStartPos + oldSubStringLength;
  6889. movePosition--) {
  6890. *(movePosition + offset) = *movePosition;
  6891. }
  6892. } else {
  6893. //
  6894. // left or no shift.
  6895. //
  6896. for(movePosition = ReplaceStartPos + newSubStringLength; //lint !e613
  6897. movePosition < Buffer + currentStringLength;
  6898. movePosition++) {
  6899. *movePosition = *(movePosition - offset);
  6900. }
  6901. }
  6902. //
  6903. // Now, copy in the string.
  6904. //
  6905. CopyMemory (ReplaceStartPos, NewString, newSubStringLength); //lint !e668
  6906. //
  6907. // String replacement completed successfully.
  6908. //
  6909. rf = TRUE;
  6910. }
  6911. return rf;
  6912. }
  6913. BOOL
  6914. StringReplaceW (
  6915. IN PWSTR Buffer,
  6916. IN DWORD MaxSize,
  6917. IN PWSTR ReplaceStartPos,
  6918. IN PWSTR ReplaceEndPos,
  6919. IN PCWSTR NewString
  6920. )
  6921. {
  6922. BOOL rf = FALSE;
  6923. DWORD oldSubStringLength;
  6924. DWORD newSubStringLength;
  6925. DWORD currentStringLength;
  6926. LONG offset;
  6927. PWSTR movePosition;
  6928. //
  6929. // Check assumptions.
  6930. //
  6931. MYASSERT(Buffer);
  6932. MYASSERT(ReplaceStartPos && ReplaceStartPos >= Buffer);
  6933. MYASSERT(ReplaceEndPos && ReplaceEndPos >= ReplaceStartPos); //lint !e613
  6934. MYASSERT(NewString);
  6935. //
  6936. // Compute sizes.
  6937. //
  6938. oldSubStringLength = (DWORD)((UBINT)ReplaceEndPos - (UBINT)ReplaceStartPos);
  6939. newSubStringLength = CharCountW(NewString);
  6940. currentStringLength = CharCountW(Buffer) + 1;
  6941. offset = (LONG)newSubStringLength - (LONG)oldSubStringLength;
  6942. //
  6943. // Make sure there is enough room in the buffer to perform the replace
  6944. // operation.
  6945. //
  6946. if ((LONG)currentStringLength + offset > (LONG)MaxSize) {
  6947. DEBUGMSG((DBG_WARNING,"ERROR: Buffer to small to perform string replacement."));
  6948. rf = FALSE;
  6949. } else {
  6950. //
  6951. // Shift the rest of the buffer to adjust it to the size of the new string.
  6952. //
  6953. if (newSubStringLength > oldSubStringLength) {
  6954. //
  6955. // right shift.
  6956. //
  6957. for (movePosition = Buffer + currentStringLength;
  6958. (UBINT)movePosition >= (UBINT)ReplaceStartPos + oldSubStringLength;
  6959. movePosition--) {
  6960. *(movePosition + offset) = *movePosition;
  6961. }
  6962. } else {
  6963. //
  6964. // left or no shift.
  6965. //
  6966. for (movePosition = ReplaceStartPos + newSubStringLength; //lint !e613
  6967. movePosition < Buffer + currentStringLength;
  6968. movePosition++) {
  6969. *movePosition = *(movePosition - offset);
  6970. }
  6971. }
  6972. //
  6973. // Now, copy in the string.
  6974. //
  6975. wcsncpy(ReplaceStartPos,NewString,newSubStringLength);
  6976. //
  6977. // String replacement completed successfully.
  6978. //
  6979. rf = TRUE;
  6980. }
  6981. return rf;
  6982. }
  6983. #if 0
  6984. /*++
  6985. Routine Description:
  6986. AddInfSectionToHashTable enumerates the specified section and adds each
  6987. item to the string table. An optional callback allows data to be associated
  6988. with each item.
  6989. Arguments:
  6990. Table - Specifies the table that receives new entries
  6991. InfFile - Specifies an open INF handle of the file to read
  6992. Section - Specifies the INF section name to enumerate
  6993. Field - Specifies which field to extract text from. If the field
  6994. exists, it is added to the string table.
  6995. Callback - Specifies optional callback to be called before adding to
  6996. the string table. The callback supplies additional data.
  6997. CallbackParam - Data passed to the callback
  6998. Return Value:
  6999. TRUE if the INF file was processed successfullly, or FALSE if an error
  7000. occurred.
  7001. --*/
  7002. BOOL
  7003. AddInfSectionToHashTableA (
  7004. IN OUT HASHTABLE Table,
  7005. IN HINF InfFile,
  7006. IN PCSTR Section,
  7007. IN DWORD Field,
  7008. IN ADDINFSECTION_PROCA Callback,
  7009. IN PVOID CallbackData
  7010. )
  7011. {
  7012. INFCONTEXT ic;
  7013. LONG rc;
  7014. HASHTABLE ht;
  7015. DWORD reqSize;
  7016. DWORD currentSize = 0;
  7017. PSTR newBuffer, buffer = NULL;
  7018. PVOID data;
  7019. UINT dataSize;
  7020. BOOL b = FALSE;
  7021. //
  7022. // On NT, Setup API is compiled with UNICODE, so the string table
  7023. // functions are UNICODE only.
  7024. //
  7025. if (ISNT()) {
  7026. SetLastError (ERROR_CALL_NOT_IMPLEMENTED);
  7027. return FALSE;
  7028. }
  7029. if (SetupFindFirstLineA (InfFile, Section, NULL, &ic)) {
  7030. do {
  7031. if (!SetupGetStringFieldA (&ic, Field, NULL, 0, &reqSize)) {
  7032. continue;
  7033. }
  7034. if (reqSize > currentSize) {
  7035. reqSize = ((reqSize / 1024) + 1) * 1024;
  7036. if (buffer) {
  7037. newBuffer = (PSTR) MemReAlloc (g_hHeap, 0, buffer, reqSize);
  7038. } else {
  7039. newBuffer = (PSTR) MemAlloc (g_hHeap, 0, reqSize);
  7040. }
  7041. if (!newBuffer) {
  7042. goto cleanup;
  7043. }
  7044. buffer = newBuffer;
  7045. currentSize = reqSize;
  7046. }
  7047. if (!SetupGetStringFieldA (&ic, Field, buffer, currentSize, NULL)) {
  7048. DEBUGMSG ((DBG_ERROR, "AddInfSectionToHashTable: SetupGetStringField failed unexpectedly"));
  7049. continue;
  7050. }
  7051. data = NULL;
  7052. dataSize = 0;
  7053. if (Callback) {
  7054. rc = Callback (buffer, &data, &dataSize, CallbackData);
  7055. if (rc == CALLBACK_STOP) {
  7056. goto cleanup;
  7057. }
  7058. if (rc == CALLBACK_SKIP) {
  7059. continue;
  7060. }
  7061. }
  7062. ht = HtAddStringExA (
  7063. Table,
  7064. buffer,
  7065. data,
  7066. CASE_INSENSITIVE
  7067. );
  7068. if (!ht) {
  7069. goto cleanup;
  7070. }
  7071. } while (SetupFindNextLine (&ic, &ic));
  7072. }
  7073. b = TRUE;
  7074. cleanup:
  7075. if (buffer) {
  7076. PushError();
  7077. MemFree (g_hHeap, 0, buffer);
  7078. PopError();
  7079. }
  7080. return b;
  7081. }
  7082. BOOL
  7083. AddInfSectionToHashTableW (
  7084. IN OUT HASHTABLE Table,
  7085. IN HINF InfFile,
  7086. IN PCWSTR Section,
  7087. IN DWORD Field,
  7088. IN ADDINFSECTION_PROCW Callback,
  7089. IN PVOID CallbackData
  7090. )
  7091. {
  7092. INFCONTEXT ic;
  7093. HASHTABLE ht;
  7094. LONG rc;
  7095. DWORD reqSize;
  7096. DWORD currentSize = 0;
  7097. PWSTR newBuffer, buffer = NULL;
  7098. PVOID data;
  7099. UINT dataSize;
  7100. BOOL b = FALSE;
  7101. //
  7102. // On Win9x, Setup API is compiled with ANSI, so the string table
  7103. // functions are ANSI only.
  7104. //
  7105. if (ISWIN9X()) {
  7106. SetLastError (ERROR_CALL_NOT_IMPLEMENTED);
  7107. return FALSE;
  7108. }
  7109. if (SetupFindFirstLineW (InfFile, Section, NULL, &ic)) {
  7110. do {
  7111. if (!SetupGetStringFieldW (&ic, Field, NULL, 0, &reqSize)) {
  7112. continue;
  7113. }
  7114. if (reqSize > currentSize) {
  7115. reqSize = ((reqSize / 1024) + 1) * 1024;
  7116. if (buffer) {
  7117. newBuffer = (PWSTR) MemReAlloc (g_hHeap, 0, buffer, reqSize);
  7118. } else {
  7119. newBuffer = (PWSTR) MemAlloc (g_hHeap, 0, reqSize);
  7120. }
  7121. if (!newBuffer) {
  7122. goto cleanup;
  7123. }
  7124. buffer = newBuffer;
  7125. currentSize = reqSize;
  7126. }
  7127. if (!SetupGetStringFieldW (&ic, Field, buffer, currentSize, NULL)) {
  7128. DEBUGMSG ((DBG_ERROR, "AddInfSectionToHashTable: SetupGetStringField failed unexpectedly"));
  7129. continue;
  7130. }
  7131. data = NULL;
  7132. dataSize = 0;
  7133. if (Callback) {
  7134. rc = Callback (buffer, &data, &dataSize, CallbackData);
  7135. if (rc == CALLBACK_STOP) {
  7136. goto cleanup;
  7137. }
  7138. if (rc == CALLBACK_SKIP) {
  7139. continue;
  7140. }
  7141. }
  7142. ht = HtAddStringExW (
  7143. Table,
  7144. buffer,
  7145. data,
  7146. CASE_INSENSITIVE
  7147. );
  7148. if (!ht) {
  7149. goto cleanup;
  7150. }
  7151. } while (SetupFindNextLine (&ic, &ic));
  7152. }
  7153. b = TRUE;
  7154. cleanup:
  7155. if (buffer) {
  7156. PushError();
  7157. MemFree (g_hHeap, 0, buffer);
  7158. PopError();
  7159. }
  7160. return b;
  7161. }
  7162. #endif
  7163. /*++
  7164. Routine Description:
  7165. Finds the last wack in the path and returns a pointer to the next
  7166. character. If no wack is found, returns a pointer to the full
  7167. string.
  7168. Arguments:
  7169. PathSpec - Specifies the path that has a file at the end of it
  7170. Return Value:
  7171. A pointer to the file name in the path.
  7172. --*/
  7173. PCSTR
  7174. GetFileNameFromPathA (
  7175. IN PCSTR PathSpec
  7176. )
  7177. {
  7178. PCSTR p;
  7179. p = _mbsrchr (PathSpec, '\\');
  7180. if (p) {
  7181. p = _mbsinc (p);
  7182. } else {
  7183. p = PathSpec;
  7184. }
  7185. return p;
  7186. }
  7187. PCWSTR
  7188. GetFileNameFromPathW (
  7189. IN PCWSTR PathSpec
  7190. )
  7191. {
  7192. PCWSTR p;
  7193. p = wcsrchr (PathSpec, L'\\');
  7194. if (p) {
  7195. p++;
  7196. } else {
  7197. p = PathSpec;
  7198. }
  7199. return p;
  7200. }
  7201. /*++
  7202. Routine Description:
  7203. Finds the last wack in the path and then the last point from the remaining path
  7204. returning a pointer to the next character. If no point is found, returns a null pointer.
  7205. Arguments:
  7206. PathSpec - Specifies the path that has a file at the end of it
  7207. Return Value:
  7208. A pointer to the file extension, excluding the dot, or NULL if no extension exists.
  7209. --*/
  7210. PCSTR
  7211. GetFileExtensionFromPathA (
  7212. IN PCSTR PathSpec
  7213. )
  7214. {
  7215. PCSTR p;
  7216. PCSTR ReturnPtr = NULL;
  7217. p = PathSpec;
  7218. while (*p) {
  7219. if (*p == '.') {
  7220. ReturnPtr = p + 1;
  7221. } else if (*p == '\\') {
  7222. ReturnPtr = NULL;
  7223. }
  7224. p = _mbsinc (p);
  7225. }
  7226. return ReturnPtr;
  7227. }
  7228. PCWSTR
  7229. GetFileExtensionFromPathW (
  7230. IN PCWSTR PathSpec
  7231. )
  7232. {
  7233. PCWSTR p;
  7234. PCWSTR ReturnPtr = NULL;
  7235. p = PathSpec;
  7236. while (*p) {
  7237. if (*p == L'.') {
  7238. ReturnPtr = p + 1;
  7239. } else if (*p == L'\\') {
  7240. ReturnPtr = NULL;
  7241. }
  7242. p++;
  7243. }
  7244. return ReturnPtr;
  7245. }
  7246. /*++
  7247. Routine Description:
  7248. GetDotExtensionFromPath finds the last wack in the path and then the last dot from
  7249. the remaining path, returning a pointer to the dot. If no dot is found, returns the
  7250. end of the string.
  7251. Arguments:
  7252. PathSpec - Specifies the path that has a file at the end of it
  7253. Return Value:
  7254. A pointer to the file extension, including the dot, or the end of the string if
  7255. no extension exists.
  7256. --*/
  7257. PCSTR
  7258. GetDotExtensionFromPathA (
  7259. IN PCSTR PathSpec
  7260. )
  7261. {
  7262. PCSTR p;
  7263. PCSTR ReturnPtr = NULL;
  7264. p = PathSpec;
  7265. while (*p) {
  7266. if (*p == '.') {
  7267. ReturnPtr = p;
  7268. } else if (*p == '\\') {
  7269. ReturnPtr = NULL;
  7270. }
  7271. p = _mbsinc (p);
  7272. }
  7273. if (!ReturnPtr) {
  7274. return p;
  7275. }
  7276. return ReturnPtr;
  7277. }
  7278. PCWSTR
  7279. GetDotExtensionFromPathW (
  7280. IN PCWSTR PathSpec
  7281. )
  7282. {
  7283. PCWSTR p;
  7284. PCWSTR ReturnPtr = NULL;
  7285. p = PathSpec;
  7286. while (*p) {
  7287. if (*p == L'.') {
  7288. ReturnPtr = p;
  7289. } else if (*p == L'\\') {
  7290. ReturnPtr = NULL;
  7291. }
  7292. p++;
  7293. }
  7294. if (!ReturnPtr) {
  7295. return p;
  7296. }
  7297. return ReturnPtr;
  7298. }
  7299. /*++
  7300. Routine Description:
  7301. CountInstancesOfChar returns the number of occurances Char
  7302. is found in String.
  7303. Arguments:
  7304. String - Specifies the text that may or may not contain
  7305. search text
  7306. Char - Specifies the char to count
  7307. Return Value:
  7308. The number of times Char appears in String.
  7309. --*/
  7310. UINT
  7311. CountInstancesOfCharA (
  7312. IN PCSTR String,
  7313. IN MBCHAR Char
  7314. )
  7315. {
  7316. UINT count;
  7317. count = 0;
  7318. while (*String) {
  7319. if (_mbsnextc (String) == Char) {
  7320. count++;
  7321. }
  7322. String = _mbsinc (String);
  7323. }
  7324. return count;
  7325. }
  7326. UINT
  7327. CountInstancesOfCharW (
  7328. IN PCWSTR String,
  7329. IN WCHAR Char
  7330. )
  7331. {
  7332. UINT count;
  7333. count = 0;
  7334. while (*String) {
  7335. if (*String == Char) {
  7336. count++;
  7337. }
  7338. String++;
  7339. }
  7340. return count;
  7341. }
  7342. /*++
  7343. Routine Description:
  7344. CountInstancesOfCharI returns the number of occurances Char
  7345. is found in String. The comparison is case-insenetive.
  7346. Arguments:
  7347. String - Specifies the text that may or may not contain
  7348. search text
  7349. Char - Specifies the char to count
  7350. Return Value:
  7351. The number of times Char appears in String.
  7352. --*/
  7353. UINT
  7354. CountInstancesOfCharIA (
  7355. IN PCSTR String,
  7356. IN MBCHAR Char
  7357. )
  7358. {
  7359. UINT count;
  7360. Char = (MBCHAR)tolower ((INT)Char);
  7361. count = 0;
  7362. while (*String) {
  7363. if ((MBCHAR) tolower ((INT)_mbsnextc (String)) == Char) {
  7364. count++;
  7365. }
  7366. String = _mbsinc (String);
  7367. }
  7368. return count;
  7369. }
  7370. UINT
  7371. CountInstancesOfCharIW (
  7372. IN PCWSTR String,
  7373. IN WCHAR Char
  7374. )
  7375. {
  7376. UINT count;
  7377. Char = towlower (Char);
  7378. count = 0;
  7379. while (*String) {
  7380. if (towlower (*String) == Char) {
  7381. count++;
  7382. }
  7383. String++;
  7384. }
  7385. return count;
  7386. }
  7387. /*++
  7388. Routine Description:
  7389. Searches the string counting the number of occurances of
  7390. SearchString exist in SourceString.
  7391. Arguments:
  7392. SourceString - Specifies the text that may or may not contain
  7393. search text
  7394. SearchString - Specifies the text phrase to count
  7395. Return Value:
  7396. The number of times SearchString appears in SourceString.
  7397. --*/
  7398. UINT
  7399. CountInstancesOfSubStringA (
  7400. IN PCSTR SourceString,
  7401. IN PCSTR SearchString
  7402. )
  7403. {
  7404. PCSTR p;
  7405. UINT count;
  7406. UINT searchBytes;
  7407. count = 0;
  7408. p = SourceString;
  7409. searchBytes = ByteCountA (SearchString);
  7410. while (p = _mbsistr (p, SearchString)) { //lint !e720
  7411. count++;
  7412. p += searchBytes;
  7413. }
  7414. return count;
  7415. }
  7416. UINT
  7417. CountInstancesOfSubStringW (
  7418. IN PCWSTR SourceString,
  7419. IN PCWSTR SearchString
  7420. )
  7421. {
  7422. PCWSTR p;
  7423. UINT count;
  7424. UINT SearchChars;
  7425. count = 0;
  7426. p = SourceString;
  7427. SearchChars = CharCountW (SearchString);
  7428. while (p = _wcsistr (p, SearchString)) { //lint !e720
  7429. count++;
  7430. p += SearchChars;
  7431. }
  7432. return count;
  7433. }
  7434. /*++
  7435. Routine Description:
  7436. Searches and replaces all occurances of SearchString with
  7437. ReplaceString.
  7438. Arguments:
  7439. SourceString - String that contiains zero or more instances
  7440. of the search text
  7441. SearchString - String to search for. Cannot be zero-length or NULL.
  7442. ReplaceString - String to replace. Can be zero-length but cannot
  7443. be NULL.
  7444. Return Value:
  7445. A pointer to the pool-allocated string, or NULL if no instances
  7446. of SearchString were found in SourceString. Free the non-NULL
  7447. pointer with FreePathString.
  7448. --*/
  7449. PCSTR
  7450. StringSearchAndReplaceA (
  7451. IN PCSTR SourceString,
  7452. IN PCSTR SearchString,
  7453. IN PCSTR ReplaceString
  7454. )
  7455. {
  7456. PSTR newString;
  7457. PBYTE p, q;
  7458. PBYTE dest;
  7459. UINT count;
  7460. UINT size;
  7461. UINT searchBytes;
  7462. UINT replaceBytes;
  7463. UINT untouchedBytes;
  7464. //
  7465. // count occurances within the string
  7466. //
  7467. count = CountInstancesOfSubStringA (
  7468. SourceString,
  7469. SearchString
  7470. );
  7471. if (!count) {
  7472. return NULL;
  7473. }
  7474. searchBytes = ByteCountA (SearchString);
  7475. replaceBytes = ByteCountA (ReplaceString);
  7476. MYASSERT (searchBytes);
  7477. size = SizeOfStringA (SourceString) -
  7478. count * searchBytes +
  7479. count * replaceBytes;
  7480. newString = (PSTR) PmGetAlignedMemory (g_PathsPool, size);
  7481. if (!newString) {
  7482. return NULL;
  7483. }
  7484. p = (PBYTE) SourceString;
  7485. dest = (PBYTE) newString;
  7486. while (q = (PBYTE) _mbsistr ((PCSTR) p, SearchString)) { //lint !e720
  7487. untouchedBytes = (DWORD)(q - p);
  7488. if (untouchedBytes) {
  7489. CopyMemory (dest, p, (SIZE_T) untouchedBytes);
  7490. dest += untouchedBytes;
  7491. }
  7492. if (replaceBytes) {
  7493. CopyMemory (dest, (PBYTE) ReplaceString, (SIZE_T) replaceBytes);
  7494. dest += replaceBytes;
  7495. }
  7496. p = q + searchBytes;
  7497. }
  7498. StringCopyA ((PSTR) dest, (PSTR) p);
  7499. return newString;
  7500. }
  7501. PCWSTR
  7502. StringSearchAndReplaceW (
  7503. IN PCWSTR SourceString,
  7504. IN PCWSTR SearchString,
  7505. IN PCWSTR ReplaceString
  7506. )
  7507. {
  7508. PWSTR newString;
  7509. PBYTE p, q;
  7510. PBYTE dest;
  7511. UINT count;
  7512. UINT size;
  7513. UINT searchBytes;
  7514. UINT replaceBytes;
  7515. UINT untouchedBytes;
  7516. //
  7517. // count occurances within the string
  7518. //
  7519. count = CountInstancesOfSubStringW (
  7520. SourceString,
  7521. SearchString
  7522. );
  7523. if (!count) {
  7524. return NULL;
  7525. }
  7526. searchBytes = ByteCountW (SearchString);
  7527. replaceBytes = ByteCountW (ReplaceString);
  7528. MYASSERT (searchBytes);
  7529. size = SizeOfStringW (SourceString) -
  7530. count * searchBytes +
  7531. count * replaceBytes;
  7532. newString = (PWSTR) PmGetAlignedMemory (g_PathsPool, size);
  7533. if (!newString) {
  7534. return NULL;
  7535. }
  7536. p = (PBYTE) SourceString;
  7537. dest = (PBYTE) newString;
  7538. while (q = (PBYTE) _wcsistr ((PCWSTR) p, SearchString)) { //lint !e720
  7539. untouchedBytes = (DWORD)(q - p);
  7540. if (untouchedBytes) {
  7541. CopyMemory (dest, p, (SIZE_T) untouchedBytes);
  7542. dest += untouchedBytes;
  7543. }
  7544. if (replaceBytes) {
  7545. CopyMemory (dest, (PBYTE) ReplaceString, (SIZE_T) replaceBytes);
  7546. dest += replaceBytes;
  7547. }
  7548. p = q + searchBytes;
  7549. }
  7550. StringCopyW ((PWSTR) dest, (PWSTR) p);
  7551. return newString;
  7552. }
  7553. PSTR *
  7554. CommandLineToArgvA (
  7555. IN PCSTR CmdLine,
  7556. OUT PUINT NumArgs
  7557. )
  7558. /*++
  7559. Routine Description:
  7560. CommandLineToArgvA implements an ANSI version of the Win32 function
  7561. CommandLineToArgvW.
  7562. Arguments:
  7563. CmdLine - A pointer to the complete command line, including the
  7564. module name. This is the same string returned by
  7565. GetCommandLineA().
  7566. NumArgs - Receives the number of arguments allocated, identical to
  7567. main's argc parameter. That is, NumArgs is equal to
  7568. the number of command line arguments plus one for the
  7569. command itself.
  7570. Return Value:
  7571. A pointer to an array of string pointers, one per argument. The
  7572. command line arguments are placed in separate nul-terminated strings.
  7573. The caller must free the memory using a single call to GlobalFree or
  7574. LocalFree.
  7575. --*/
  7576. {
  7577. PCSTR start, end;
  7578. BOOL QuoteMode;
  7579. MBCHAR ch = 0;
  7580. UINT Pass;
  7581. UINT ArgStrSize;
  7582. UINT Args;
  7583. PSTR ArgStrEnd = NULL; // filled in on pass one, used on pass two
  7584. PSTR *ArgPtrArray = NULL; // filled in on pass one, used on pass two
  7585. //
  7586. // count args on first pass, then allocate memory and create arg string
  7587. //
  7588. ArgStrSize = 0;
  7589. Pass = 0;
  7590. do {
  7591. // Init loop
  7592. Pass++;
  7593. Args = 0;
  7594. start = CmdLine;
  7595. // Skip leading space
  7596. while (isspace (*start)) {
  7597. start++;
  7598. }
  7599. while (*start) {
  7600. // Look for quote mode
  7601. if (*start == '\"') {
  7602. QuoteMode = TRUE;
  7603. start++;
  7604. } else {
  7605. QuoteMode = FALSE;
  7606. }
  7607. // Find end of arg
  7608. end = start;
  7609. while (*end) {
  7610. ch = _mbsnextc (end);
  7611. if (QuoteMode) {
  7612. if (ch == '\"') {
  7613. break;
  7614. }
  7615. } else {
  7616. if (isspace ((INT)ch)) {
  7617. break;
  7618. }
  7619. }
  7620. end = _mbsinc (end);
  7621. }
  7622. // If Pass 1, add string size
  7623. if (Pass == 1) {
  7624. ArgStrSize += (UINT)((UBINT)end - (UBINT)start) + 1;
  7625. }
  7626. // If Pass 2, copy strings to buffer
  7627. else {
  7628. MYASSERT (ArgStrEnd);
  7629. MYASSERT (ArgPtrArray);
  7630. ArgPtrArray[Args] = ArgStrEnd; //lint !e613
  7631. StringCopyABA (ArgStrEnd, start, end);
  7632. ArgStrEnd = GetEndOfStringA (ArgStrEnd); //lint !e668
  7633. ArgStrEnd++; //lint !e613
  7634. }
  7635. // Set start to next arg
  7636. Args++;
  7637. if (QuoteMode && ch == '\"') {
  7638. end = _mbsinc (end);
  7639. }
  7640. start = end;
  7641. while (isspace (*start)) {
  7642. start++;
  7643. }
  7644. }
  7645. // If Pass 1, allocate strings
  7646. if (Pass == 1) {
  7647. if (Args) {
  7648. ArgPtrArray = (PSTR *) GlobalAlloc (
  7649. GPTR,
  7650. (UINT)(sizeof (PSTR) * Args + ArgStrSize)
  7651. );
  7652. if (!ArgPtrArray) {
  7653. return NULL;
  7654. }
  7655. ArgStrEnd = (PSTR) (&ArgPtrArray[Args]);
  7656. } else {
  7657. return NULL;
  7658. }
  7659. }
  7660. } while (Pass < 2);
  7661. *NumArgs = Args;
  7662. return ArgPtrArray;
  7663. }
  7664. BOOL
  7665. EnumNextMultiSzA (
  7666. IN OUT PMULTISZ_ENUMA MultiSzEnum
  7667. )
  7668. {
  7669. if (!MultiSzEnum->CurrentString || !(*MultiSzEnum->CurrentString)) {
  7670. return FALSE;
  7671. }
  7672. MultiSzEnum->CurrentString = GetEndOfStringA (MultiSzEnum->CurrentString) + 1; //lint !e613
  7673. return (MultiSzEnum->CurrentString [0] != 0);
  7674. }
  7675. BOOL
  7676. EnumFirstMultiSzA (
  7677. OUT PMULTISZ_ENUMA MultiSzEnum,
  7678. IN PCSTR MultiSzStr
  7679. )
  7680. {
  7681. if ((MultiSzStr == NULL) || (MultiSzStr [0] == 0)) {
  7682. return FALSE;
  7683. }
  7684. MultiSzEnum->Buffer = MultiSzStr;
  7685. MultiSzEnum->CurrentString = MultiSzStr;
  7686. return TRUE;
  7687. }
  7688. BOOL
  7689. EnumNextMultiSzW (
  7690. IN OUT PMULTISZ_ENUMW MultiSzEnum
  7691. )
  7692. {
  7693. if (!MultiSzEnum->CurrentString || !(*MultiSzEnum->CurrentString)) {
  7694. return FALSE;
  7695. }
  7696. MultiSzEnum->CurrentString = GetEndOfStringW (MultiSzEnum->CurrentString) + 1;
  7697. return (MultiSzEnum->CurrentString [0] != 0);
  7698. }
  7699. BOOL
  7700. EnumFirstMultiSzW (
  7701. OUT PMULTISZ_ENUMW MultiSzEnum,
  7702. IN PCWSTR MultiSzStr
  7703. )
  7704. {
  7705. if ((MultiSzStr == NULL) || (MultiSzStr [0] == 0)) {
  7706. return FALSE;
  7707. }
  7708. MultiSzEnum->Buffer = MultiSzStr;
  7709. MultiSzEnum->CurrentString = MultiSzStr;
  7710. return TRUE;
  7711. }
  7712. PSTR
  7713. GetPrevCharA (
  7714. IN PCSTR StartStr,
  7715. IN PCSTR CurrPtr,
  7716. IN MBCHAR SearchChar
  7717. )
  7718. {
  7719. PCSTR ptr = CurrPtr;
  7720. for (;;) {
  7721. ptr = _mbsdec2 (StartStr, ptr);
  7722. if (!ptr) {
  7723. return NULL;
  7724. }
  7725. if (_mbsnextc (ptr) == SearchChar) {
  7726. return (PSTR) ptr;
  7727. }
  7728. }
  7729. }
  7730. PWSTR
  7731. GetPrevCharW (
  7732. IN PCWSTR StartStr,
  7733. IN PCWSTR CurrPtr,
  7734. IN WCHAR SearchChar
  7735. )
  7736. {
  7737. PCWSTR ptr = CurrPtr;
  7738. while (ptr > StartStr) {
  7739. ptr--;
  7740. if (*ptr == SearchChar) {
  7741. return (PWSTR) ptr;
  7742. }
  7743. }
  7744. return NULL;
  7745. }
  7746. VOID
  7747. ToggleWacksA (
  7748. IN PSTR Line,
  7749. IN BOOL Operation
  7750. )
  7751. {
  7752. CHAR curChar;
  7753. CHAR newChar;
  7754. PSTR p = Line;
  7755. curChar = Operation ? WACK_REPLACE_CHAR : '\\';
  7756. newChar = Operation ? '\\' : WACK_REPLACE_CHAR;
  7757. do {
  7758. p = _mbschr (p, curChar);
  7759. if (p) {
  7760. *p = newChar;
  7761. p = _mbsinc (p);
  7762. }
  7763. } while (p);
  7764. }
  7765. VOID
  7766. ToggleWacksW (
  7767. IN PWSTR Line,
  7768. IN BOOL Operation
  7769. )
  7770. {
  7771. WCHAR curChar;
  7772. WCHAR newChar;
  7773. PWSTR p = Line;
  7774. curChar = Operation ? WACK_REPLACE_CHAR : L'\\';
  7775. newChar = Operation ? L'\\' : WACK_REPLACE_CHAR;
  7776. do {
  7777. p = wcschr (p, curChar);
  7778. if (p) {
  7779. *p = newChar;
  7780. p++;
  7781. }
  7782. } while (p);
  7783. }
  7784. PSTR
  7785. pGoBackA (
  7786. IN PSTR LastChar,
  7787. IN PSTR FirstChar,
  7788. IN UINT NumWacks
  7789. )
  7790. {
  7791. LastChar = _mbsdec2 (FirstChar, LastChar);
  7792. while (NumWacks && (LastChar >= FirstChar)) {
  7793. if (_mbsnextc (LastChar) == '\\') {
  7794. NumWacks --;
  7795. }
  7796. LastChar = _mbsdec2 (FirstChar, LastChar);
  7797. }
  7798. if (NumWacks) {
  7799. return NULL;
  7800. }
  7801. return LastChar + 2;
  7802. }
  7803. PWSTR
  7804. pGoBackW (
  7805. IN PWSTR LastChar,
  7806. IN PWSTR FirstChar,
  7807. IN UINT NumWacks
  7808. )
  7809. {
  7810. LastChar --;
  7811. while (NumWacks && (LastChar >= FirstChar)) {
  7812. if (*LastChar == L'\\') {
  7813. NumWacks --;
  7814. }
  7815. LastChar --;
  7816. }
  7817. if (NumWacks) {
  7818. return NULL;
  7819. }
  7820. return LastChar + 2;
  7821. }
  7822. UINT
  7823. pCountDotsA (
  7824. IN PCSTR PathSeg
  7825. )
  7826. {
  7827. UINT numDots = 0;
  7828. while (PathSeg && *PathSeg) {
  7829. if (_mbsnextc (PathSeg) != '.') {
  7830. return 0;
  7831. }
  7832. numDots ++;
  7833. PathSeg = _mbsinc (PathSeg);
  7834. }
  7835. return numDots;
  7836. }
  7837. UINT
  7838. pCountDotsW (
  7839. IN PCWSTR PathSeg
  7840. )
  7841. {
  7842. UINT numDots = 0;
  7843. while (PathSeg && *PathSeg) {
  7844. if (*PathSeg != L'.') {
  7845. return 0;
  7846. }
  7847. numDots ++;
  7848. PathSeg ++;
  7849. }
  7850. return numDots;
  7851. }
  7852. PCSTR
  7853. SanitizePathA (
  7854. IN PCSTR FileSpec
  7855. )
  7856. {
  7857. CHAR pathSeg [MAX_MBCHAR_PATH];
  7858. PCSTR wackPtr;
  7859. UINT dotNr;
  7860. PSTR newPath = DuplicatePathStringA (FileSpec, 0);
  7861. PSTR newPathPtr = newPath;
  7862. BOOL firstPass = TRUE;
  7863. do {
  7864. wackPtr = _mbschr (FileSpec, '\\');
  7865. if (wackPtr) {
  7866. if (firstPass && (wackPtr == FileSpec)) {
  7867. // this one starts with a wack, let's see if we have double wacks
  7868. wackPtr = _mbsinc (wackPtr);
  7869. if (!wackPtr) {
  7870. FreePathStringA (newPath);
  7871. return NULL;
  7872. }
  7873. if (_mbsnextc (wackPtr) == '\\') {
  7874. // this one starts with a double wack
  7875. wackPtr = _mbsinc (wackPtr);
  7876. if (!wackPtr) {
  7877. FreePathStringA (newPath);
  7878. return NULL;
  7879. }
  7880. wackPtr = _mbschr (wackPtr, '\\');
  7881. } else {
  7882. wackPtr = _mbschr (wackPtr, '\\');
  7883. }
  7884. }
  7885. firstPass = FALSE;
  7886. if (wackPtr) {
  7887. StringCopyByteCountABA (pathSeg, FileSpec, wackPtr, MAX_MBCHAR_PATH);
  7888. FileSpec = _mbsinc (wackPtr);
  7889. } else {
  7890. StringCopyByteCountABA (pathSeg, FileSpec, GetEndOfStringA (FileSpec), MAX_MBCHAR_PATH);
  7891. }
  7892. } else {
  7893. StringCopyByteCountABA (pathSeg, FileSpec, GetEndOfStringA (FileSpec), MAX_MBCHAR_PATH);
  7894. }
  7895. if (*pathSeg) {
  7896. dotNr = pCountDotsA (pathSeg);
  7897. if (dotNr>1) {
  7898. newPathPtr = pGoBackA (newPathPtr, newPath, dotNr);
  7899. if (newPathPtr == NULL) {
  7900. DEBUGMSGA ((DBG_WARNING, "Broken path detected:%s", FileSpec));
  7901. FreePathStringA (newPath);
  7902. return NULL;
  7903. }
  7904. } else {
  7905. StringCopyA (newPathPtr, pathSeg);
  7906. newPathPtr = GetEndOfStringA (newPathPtr);
  7907. if (wackPtr) {
  7908. *newPathPtr = '\\';
  7909. //we increment this because we know that \ is a single byte character.
  7910. newPathPtr ++;
  7911. }
  7912. }
  7913. }
  7914. } while (wackPtr);
  7915. *newPathPtr = 0;
  7916. return newPath;
  7917. }
  7918. PCWSTR
  7919. SanitizePathW (
  7920. IN PCWSTR FileSpec
  7921. )
  7922. {
  7923. WCHAR pathSeg [MEMDB_MAX];
  7924. PCWSTR wackPtr;
  7925. UINT dotNr;
  7926. PWSTR newPath = DuplicatePathStringW (FileSpec, 0);
  7927. PWSTR newPathPtr = newPath;
  7928. BOOL firstPass = TRUE;
  7929. do {
  7930. wackPtr = wcschr (FileSpec, L'\\');
  7931. if (wackPtr) {
  7932. if (firstPass && (wackPtr == FileSpec)) {
  7933. // this one starts with a wack, let's see if we have double wacks
  7934. wackPtr ++;
  7935. if (*wackPtr == 0) {
  7936. FreePathStringW (newPath);
  7937. return NULL;
  7938. }
  7939. if (*wackPtr == L'\\') {
  7940. // this one starts with a double wack
  7941. wackPtr ++;
  7942. if (!wackPtr) {
  7943. FreePathStringW (newPath);
  7944. return NULL;
  7945. }
  7946. wackPtr = wcschr (wackPtr, L'\\');
  7947. } else {
  7948. wackPtr = wcschr (wackPtr, L'\\');
  7949. }
  7950. }
  7951. firstPass = FALSE;
  7952. if (wackPtr) {
  7953. StringCopyByteCountABW (
  7954. pathSeg,
  7955. FileSpec,
  7956. wackPtr,
  7957. (UINT) sizeof (pathSeg)
  7958. );
  7959. FileSpec = wackPtr + 1;
  7960. } else {
  7961. StringCopyByteCountABW (pathSeg, FileSpec, GetEndOfStringW (FileSpec), MAX_WCHAR_PATH);
  7962. }
  7963. } else {
  7964. StringCopyByteCountABW (pathSeg, FileSpec, GetEndOfStringW (FileSpec), MAX_WCHAR_PATH);
  7965. }
  7966. if (*pathSeg) {
  7967. dotNr = pCountDotsW (pathSeg);
  7968. if (dotNr>1) {
  7969. newPathPtr = pGoBackW (newPathPtr, newPath, dotNr);
  7970. if (newPathPtr == NULL) {
  7971. DEBUGMSGW ((DBG_WARNING, "Broken path detected:%s", FileSpec));
  7972. FreePathStringW (newPath);
  7973. return NULL;
  7974. }
  7975. } else {
  7976. StringCopyW (newPathPtr, pathSeg);
  7977. newPathPtr = GetEndOfStringW (newPathPtr);
  7978. if (wackPtr) {
  7979. *newPathPtr = L'\\';
  7980. newPathPtr ++;
  7981. }
  7982. }
  7983. }
  7984. } while (wackPtr);
  7985. *newPathPtr = 0;
  7986. return newPath;
  7987. }
  7988. UINT
  7989. pBuildFromDHList (
  7990. IN UINT ch1,
  7991. IN UINT ch2
  7992. )
  7993. {
  7994. PDHLIST p;
  7995. UINT result = 0;
  7996. p = g_DHList;
  7997. while (p->char1) {
  7998. if ((p->char1 == ch1) && (p->char2 == ch2)) {
  7999. result = p->result;
  8000. break;
  8001. }
  8002. p++;
  8003. }
  8004. return result;
  8005. }
  8006. VOID
  8007. _mbssetchar (
  8008. OUT PSTR Dest,
  8009. IN UINT Char
  8010. )
  8011. {
  8012. if (Char >= 256) {
  8013. *(Dest+1) = *((PBYTE)(&Char));
  8014. *(Dest) = *((PBYTE)(&Char) + 1);
  8015. }
  8016. else {
  8017. *Dest = (CHAR) Char;
  8018. }
  8019. }
  8020. /*++
  8021. Routine Description:
  8022. FindLastWack finds the position of the last \ in the given string or NULL if none found
  8023. Arguments:
  8024. Str - Specifies the string
  8025. Return Value:
  8026. Pointer to the last occurence of a \ in the string or NULL
  8027. --*/
  8028. PCSTR
  8029. FindLastWackA (
  8030. IN PCSTR Str
  8031. )
  8032. {
  8033. PCSTR lastWack = NULL;
  8034. if (Str) {
  8035. while ((Str = _mbschr (Str, '\\')) != NULL) {
  8036. lastWack = Str;
  8037. Str++;
  8038. }
  8039. }
  8040. return lastWack;
  8041. }
  8042. PCWSTR
  8043. FindLastWackW (
  8044. IN PCWSTR Str
  8045. )
  8046. {
  8047. PCWSTR lastWack = NULL;
  8048. if (Str) {
  8049. while ((Str = wcschr (Str, L'\\')) != NULL) {
  8050. lastWack = Str;
  8051. Str++;
  8052. }
  8053. }
  8054. return lastWack;
  8055. }
  8056. /*++
  8057. Routine Description:
  8058. GetNodePatternMinMaxLevels treats the given string pattern as a path with \ as separator
  8059. and computes the min and max levels of the given node; the root has level 1; if a * is
  8060. followed by \ it is treated as a single level (e.g. *\ only enumerates roots)
  8061. Arguments:
  8062. NodePattern - Specifies the node as a string pattern
  8063. FormattedNodePattern - Receives the formatted string, eliminating duplicate * and the last \;
  8064. may be the same as NodePattern
  8065. MinLevel - Receives the minimum level of a node having this pattern
  8066. MaxLevel - Receives the maximum level of a node having this pattern; may be NODE_LEVEL_MAX
  8067. Return Value:
  8068. TRUE if NodePattern is a valid pattern and the function succeeded, FALSE otherwise
  8069. --*/
  8070. #define NODESTATE_BEGIN 0
  8071. #define NODESTATE_UNC 1
  8072. #define NODESTATE_BEGINSEG 2
  8073. #define NODESTATE_INSEG 3
  8074. #define NODESTATE_ESCAPED 4
  8075. #define NODESTATE_STAR 5
  8076. #define NODESTATE_STARONLY 6
  8077. BOOL
  8078. GetNodePatternMinMaxLevelsA (
  8079. IN PCSTR NodePattern,
  8080. OUT PSTR FormattedNode, OPTIONAL
  8081. OUT PDWORD MinLevel, OPTIONAL
  8082. OUT PDWORD MaxLevel OPTIONAL
  8083. )
  8084. {
  8085. PCSTR nodePattern = NodePattern;
  8086. MBCHAR currCh = 0;
  8087. DWORD minLevel = 0;
  8088. DWORD maxLevel = 0;
  8089. DWORD state = NODESTATE_BEGIN;
  8090. BOOL advance;
  8091. BOOL copyChar;
  8092. if (!NodePattern || *NodePattern == 0) {
  8093. return FALSE;
  8094. }
  8095. while (*nodePattern) {
  8096. advance = TRUE;
  8097. copyChar = TRUE;
  8098. currCh = _mbsnextc (nodePattern);
  8099. switch (state) {
  8100. case NODESTATE_BEGIN:
  8101. switch (currCh) {
  8102. case '\\':
  8103. state = NODESTATE_UNC;
  8104. break;
  8105. case '*':
  8106. minLevel ++;
  8107. maxLevel ++;
  8108. state = NODESTATE_INSEG;
  8109. advance = FALSE;
  8110. break;
  8111. case '?':
  8112. minLevel ++;
  8113. maxLevel ++;
  8114. state = NODESTATE_INSEG;
  8115. advance = FALSE;
  8116. break;
  8117. case '^':
  8118. minLevel ++;
  8119. maxLevel ++;
  8120. state = NODESTATE_ESCAPED;
  8121. break;
  8122. default:
  8123. minLevel ++;
  8124. maxLevel ++;
  8125. state = NODESTATE_INSEG;
  8126. break;
  8127. }
  8128. break;
  8129. case NODESTATE_UNC:
  8130. minLevel ++;
  8131. if (maxLevel != NODE_LEVEL_MAX) {
  8132. maxLevel ++;
  8133. }
  8134. switch (currCh) {
  8135. case '\\':
  8136. state = NODESTATE_BEGINSEG;
  8137. break;
  8138. case '*':
  8139. state = NODESTATE_BEGINSEG;
  8140. advance = FALSE;
  8141. break;
  8142. case '?':
  8143. state = NODESTATE_INSEG;
  8144. advance = FALSE;
  8145. break;
  8146. case '^':
  8147. state = NODESTATE_ESCAPED;
  8148. break;
  8149. default:
  8150. state = NODESTATE_INSEG;
  8151. break;
  8152. }
  8153. break;
  8154. case NODESTATE_BEGINSEG:
  8155. switch (currCh) {
  8156. case '\\':
  8157. DEBUGMSGA ((DBG_STRINGS, "GetNodeMinMaxLevelsA: two wacks in a row: %s", NodePattern));
  8158. return FALSE;
  8159. case '*':
  8160. minLevel --;
  8161. state = NODESTATE_STARONLY;
  8162. maxLevel = NODE_LEVEL_MAX;
  8163. break;
  8164. case '?':
  8165. state = NODESTATE_INSEG;
  8166. advance = FALSE;
  8167. break;
  8168. case '^':
  8169. state = NODESTATE_ESCAPED;
  8170. break;
  8171. default:
  8172. state = NODESTATE_INSEG;
  8173. break;
  8174. }
  8175. break;
  8176. case NODESTATE_STARONLY:
  8177. state = NODESTATE_INSEG;
  8178. if (currCh == '*') {
  8179. copyChar = FALSE;
  8180. } else {
  8181. minLevel ++;
  8182. if (maxLevel != NODE_LEVEL_MAX) {
  8183. maxLevel ++;
  8184. }
  8185. advance = FALSE;
  8186. }
  8187. break;
  8188. case NODESTATE_STAR:
  8189. state = NODESTATE_INSEG;
  8190. if (currCh == '*') {
  8191. copyChar = FALSE;
  8192. } else {
  8193. advance = FALSE;
  8194. }
  8195. break;
  8196. case NODESTATE_INSEG:
  8197. switch (currCh) {
  8198. case '\\':
  8199. minLevel ++;
  8200. if (maxLevel != NODE_LEVEL_MAX) {
  8201. maxLevel ++;
  8202. }
  8203. state = NODESTATE_BEGINSEG;
  8204. break;
  8205. case '*':
  8206. state = NODESTATE_STAR;
  8207. maxLevel = NODE_LEVEL_MAX;
  8208. break;
  8209. case '?':
  8210. state = NODESTATE_INSEG;
  8211. if (maxLevel != NODE_LEVEL_MAX) {
  8212. maxLevel ++;
  8213. }
  8214. break;
  8215. case '^':
  8216. state = NODESTATE_ESCAPED;
  8217. break;
  8218. default:
  8219. state = NODESTATE_INSEG;
  8220. break;
  8221. }
  8222. break;
  8223. case NODESTATE_ESCAPED:
  8224. if (!_mbschr (EscapedCharsA, currCh)) {
  8225. DEBUGMSGA ((DBG_STRINGS, "GetNodeMinMaxLevelsA: illegal escaped character: %s", NodePattern));
  8226. return FALSE;
  8227. }
  8228. state = NODESTATE_INSEG;
  8229. break;
  8230. default:
  8231. DEBUGMSGA ((DBG_STRINGS, "GetNodeMinMaxLevelsA: unknown state while processing: %s", NodePattern));
  8232. return FALSE;
  8233. }
  8234. if (advance) {
  8235. if (copyChar && FormattedNode) {
  8236. if (IsLeadByte (*nodePattern)) {
  8237. *FormattedNode = *nodePattern;
  8238. FormattedNode ++;
  8239. nodePattern ++;
  8240. }
  8241. *FormattedNode = *nodePattern;
  8242. FormattedNode ++;
  8243. nodePattern ++;
  8244. } else {
  8245. nodePattern = _mbsinc (nodePattern);
  8246. }
  8247. }
  8248. }
  8249. if (MinLevel) {
  8250. *MinLevel = minLevel;
  8251. }
  8252. if (MaxLevel) {
  8253. *MaxLevel = maxLevel;
  8254. }
  8255. if (FormattedNode) {
  8256. *FormattedNode = 0;
  8257. }
  8258. return TRUE;
  8259. }
  8260. BOOL
  8261. GetNodePatternMinMaxLevelsW (
  8262. IN PCWSTR NodePattern,
  8263. OUT PWSTR FormattedNode, OPTIONAL
  8264. OUT PDWORD MinLevel, OPTIONAL
  8265. OUT PDWORD MaxLevel OPTIONAL
  8266. )
  8267. {
  8268. PCWSTR nodePattern = NodePattern;
  8269. DWORD minLevel = 0;
  8270. DWORD maxLevel = 0;
  8271. DWORD state = NODESTATE_BEGIN;
  8272. BOOL advance;
  8273. BOOL copyChar;
  8274. if (!NodePattern || *NodePattern == 0) {
  8275. return FALSE;
  8276. }
  8277. while (*nodePattern) {
  8278. advance = TRUE;
  8279. copyChar = TRUE;
  8280. switch (state) {
  8281. case NODESTATE_BEGIN:
  8282. switch (*nodePattern) {
  8283. case L'\\':
  8284. state = NODESTATE_UNC;
  8285. break;
  8286. case L'*':
  8287. minLevel ++;
  8288. maxLevel ++;
  8289. state = NODESTATE_INSEG;
  8290. advance = FALSE;
  8291. break;
  8292. case L'?':
  8293. minLevel ++;
  8294. maxLevel ++;
  8295. state = NODESTATE_INSEG;
  8296. advance = FALSE;
  8297. break;
  8298. case L'^':
  8299. minLevel ++;
  8300. maxLevel ++;
  8301. state = NODESTATE_ESCAPED;
  8302. break;
  8303. default:
  8304. minLevel ++;
  8305. maxLevel ++;
  8306. state = NODESTATE_INSEG;
  8307. break;
  8308. }
  8309. break;
  8310. case NODESTATE_UNC:
  8311. minLevel ++;
  8312. if (maxLevel != NODE_LEVEL_MAX) {
  8313. maxLevel ++;
  8314. }
  8315. switch (*nodePattern) {
  8316. case L'\\':
  8317. state = NODESTATE_BEGINSEG;
  8318. break;
  8319. case L'*':
  8320. state = NODESTATE_BEGINSEG;
  8321. advance = FALSE;
  8322. break;
  8323. case L'?':
  8324. state = NODESTATE_INSEG;
  8325. advance = FALSE;
  8326. break;
  8327. case L'^':
  8328. state = NODESTATE_ESCAPED;
  8329. break;
  8330. default:
  8331. state = NODESTATE_INSEG;
  8332. break;
  8333. }
  8334. break;
  8335. case NODESTATE_BEGINSEG:
  8336. switch (*nodePattern) {
  8337. case L'\\':
  8338. DEBUGMSGW ((DBG_STRINGS, "GetNodeMinMaxLevelsA: two wacks in a row: %s", NodePattern));
  8339. return FALSE;
  8340. case L'*':
  8341. minLevel --;
  8342. state = NODESTATE_STARONLY;
  8343. maxLevel = NODE_LEVEL_MAX;
  8344. break;
  8345. case L'?':
  8346. state = NODESTATE_INSEG;
  8347. advance = FALSE;
  8348. break;
  8349. case L'^':
  8350. state = NODESTATE_ESCAPED;
  8351. break;
  8352. default:
  8353. state = NODESTATE_INSEG;
  8354. break;
  8355. }
  8356. break;
  8357. case NODESTATE_STARONLY:
  8358. state = NODESTATE_INSEG;
  8359. if (*nodePattern == L'*') {
  8360. copyChar = FALSE;
  8361. } else {
  8362. minLevel ++;
  8363. if (maxLevel != NODE_LEVEL_MAX) {
  8364. maxLevel ++;
  8365. }
  8366. advance = FALSE;
  8367. }
  8368. break;
  8369. case NODESTATE_STAR:
  8370. state = NODESTATE_INSEG;
  8371. if (*nodePattern == L'*') {
  8372. copyChar = FALSE;
  8373. } else {
  8374. advance = FALSE;
  8375. }
  8376. break;
  8377. case NODESTATE_INSEG:
  8378. switch (*nodePattern) {
  8379. case L'\\':
  8380. minLevel ++;
  8381. if (maxLevel != NODE_LEVEL_MAX) {
  8382. maxLevel ++;
  8383. }
  8384. state = NODESTATE_BEGINSEG;
  8385. break;
  8386. case L'*':
  8387. state = NODESTATE_STAR;
  8388. maxLevel = NODE_LEVEL_MAX;
  8389. break;
  8390. case L'?':
  8391. state = NODESTATE_INSEG;
  8392. if (maxLevel != NODE_LEVEL_MAX) {
  8393. maxLevel ++;
  8394. }
  8395. break;
  8396. case L'^':
  8397. state = NODESTATE_ESCAPED;
  8398. break;
  8399. default:
  8400. state = NODESTATE_INSEG;
  8401. break;
  8402. }
  8403. break;
  8404. case NODESTATE_ESCAPED:
  8405. if (!wcschr (EscapedCharsW, *nodePattern)) {
  8406. DEBUGMSGW ((DBG_STRINGS, "GetNodeMinMaxLevelsA: illegal escaped character: %s", NodePattern));
  8407. return FALSE;
  8408. }
  8409. state = NODESTATE_INSEG;
  8410. break;
  8411. default:
  8412. DEBUGMSGW ((DBG_STRINGS, "GetNodeMinMaxLevelsA: unknown state while processing: %s", NodePattern));
  8413. return FALSE;
  8414. }
  8415. if (advance) {
  8416. if (copyChar && FormattedNode) {
  8417. *FormattedNode = *nodePattern;
  8418. FormattedNode ++;
  8419. nodePattern ++;
  8420. } else {
  8421. nodePattern ++;
  8422. }
  8423. }
  8424. }
  8425. if (MinLevel) {
  8426. *MinLevel = minLevel;
  8427. }
  8428. if (MaxLevel) {
  8429. *MaxLevel = maxLevel;
  8430. }
  8431. if (FormattedNode) {
  8432. *FormattedNode = 0;
  8433. }
  8434. return TRUE;
  8435. }
  8436. #if 0
  8437. //
  8438. // PORTBUG Uses memdb max. #if 0'd out for now.
  8439. //
  8440. PCSTR
  8441. ConvertSBtoDB (
  8442. IN PCSTR RootPath,
  8443. IN PCSTR FullPath,
  8444. IN PCSTR Limit
  8445. )
  8446. {
  8447. CHAR result[MEMDB_MAX];
  8448. PCSTR p,p1,q;
  8449. PSTR s;
  8450. UINT ch;
  8451. UINT ch1;
  8452. BOOL dhCase = FALSE;
  8453. ZeroMemory (result, MAX_PATH);
  8454. p = FullPath;
  8455. q = RootPath;
  8456. s = result;
  8457. while (*p && (((DWORD)s - (DWORD)result) < MEMDB_MAX)) {
  8458. if (q && *q) {
  8459. _mbssetchar (s, _mbsnextc(p));
  8460. q = _mbsinc (q);
  8461. } else if (Limit && (p >= Limit)) {
  8462. _mbssetchar (s, _mbsnextc(p));
  8463. } else {
  8464. ch = _mbsnextc (p);
  8465. //
  8466. // It is very important not to make the conversion for characters below A1. Otherwise
  8467. // all english letters will be converted to large letters.
  8468. //
  8469. if (ch >= 0xA1 && ch <= 0xDF) {
  8470. // this is a candidate for conversion
  8471. // we need to see if there is a special Dakutenn/Handakuten conversion
  8472. dhCase = FALSE;
  8473. p1 = _mbsinc (p);
  8474. if (p1) {
  8475. ch1 = _mbsnextc (p1);
  8476. ch1 = pBuildFromDHList (ch, ch1);
  8477. if (ch1) {
  8478. p = _mbsinc (p);
  8479. _mbssetchar (s, ch1);
  8480. dhCase = TRUE;
  8481. }
  8482. }
  8483. if (!dhCase) {
  8484. _mbssetchar (s, _mbbtombc (ch));
  8485. }
  8486. } else {
  8487. _mbssetchar (s, ch);
  8488. }
  8489. }
  8490. p = _mbsinc (p);
  8491. s = _mbsinc (s);
  8492. }
  8493. result [MAX_PATH - 1] = 0;
  8494. return (DuplicatePathString (result, 0));
  8495. }
  8496. #endif
  8497. ULONGLONG
  8498. StringToUint64A (
  8499. IN PCSTR String,
  8500. OUT PCSTR *EndOfNumber OPTIONAL
  8501. )
  8502. {
  8503. ULONGLONG n;
  8504. n = 0;
  8505. while (*String >= '0' && *String <= '9') {
  8506. n = n * 10 + *String - '0';
  8507. String++;
  8508. }
  8509. if (EndOfNumber) {
  8510. *EndOfNumber = String;
  8511. }
  8512. return n;
  8513. }
  8514. ULONGLONG
  8515. StringToUint64W (
  8516. IN PCWSTR String,
  8517. OUT PCWSTR *EndOfNumber OPTIONAL
  8518. )
  8519. {
  8520. ULONGLONG n;
  8521. n = 0;
  8522. while (*String >= L'0' && *String <= L'9') {
  8523. n = n * 10 + *String - L'0';
  8524. String++;
  8525. }
  8526. if (EndOfNumber) {
  8527. *EndOfNumber = String;
  8528. }
  8529. return n;
  8530. }
  8531. LONGLONG
  8532. StringToInt64A (
  8533. IN PCSTR String,
  8534. OUT PCSTR *EndOfNumber OPTIONAL
  8535. )
  8536. {
  8537. LONGLONG n;
  8538. BOOL negate = FALSE;
  8539. if (*String == '-') {
  8540. negate = TRUE;
  8541. String++;
  8542. } else if (*String == '+') {
  8543. String++;
  8544. }
  8545. n = 0;
  8546. while (*String >= '0' && *String <= '9') {
  8547. n = n * 10 + *String - '0';
  8548. String++;
  8549. }
  8550. if (negate) {
  8551. n = -n;
  8552. }
  8553. if (EndOfNumber) {
  8554. *EndOfNumber = String;
  8555. }
  8556. return n;
  8557. }
  8558. LONGLONG
  8559. StringToInt64W (
  8560. IN PCWSTR String,
  8561. OUT PCWSTR *EndOfNumber OPTIONAL
  8562. )
  8563. {
  8564. LONGLONG n;
  8565. BOOL negate = FALSE;
  8566. if (*String == L'-') {
  8567. negate = TRUE;
  8568. String++;
  8569. } else if (*String == L'+') {
  8570. String++;
  8571. }
  8572. n = 0;
  8573. while (*String >= L'0' && *String <= L'9') {
  8574. n = n * 10 + *String - L'0';
  8575. String++;
  8576. }
  8577. if (negate) {
  8578. n = -n;
  8579. }
  8580. if (EndOfNumber) {
  8581. *EndOfNumber = String;
  8582. }
  8583. return n;
  8584. }