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.

7874 lines
178 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. jimschm 08-Jul-1999 IsPatternMatchEx
  11. jimschm 07-Jan-1999 GetFileExtensionFromPath fixed again, added
  12. GetDotExtensionFromPath
  13. calinn 23-Sep-1998 GetFileExtensionFromPath bug fix
  14. calinn 29-Jan-1998 Fixed a bug in EnumNextMultiSz.
  15. calinn 11-Jan-1998 Added EnumFirstMultiSz and EnumNextMultiSz functions.
  16. marcw 15-Dec-1997 Added ExpandEnvironmentTextEx functions.
  17. marcw 14-Nov-1997 SlightJoinText revisions.
  18. jimschm 21-May-1997 AppendWack revisions
  19. marcw 24-Mar-1997 StringReplace functions.
  20. jimschm 14-Mar-1997 New critical section stuff, enhanced message resource
  21. routines, C runtime extensions, registry root utils
  22. jimschm 26-Nov-1996 Added message resource tools.
  23. mikeco 01-Jul-1997 Add FreeStringResourcePtr Fns
  24. mikeco 29-Sep-1997 IsLeadByte wrapper for IsDBCSLeadByte
  25. --*/
  26. #include "pch.h"
  27. #include "migutilp.h"
  28. // Error stack size (normally only one or two, so 32 is relatively huge)
  29. #define MAX_STACK 32
  30. extern OUR_CRITICAL_SECTION g_MessageCs; // in main.c
  31. extern PGROWBUFFER g_LastAllocTable; // in main.c
  32. extern POOLHANDLE g_TextPool;
  33. typedef enum {
  34. BEGIN_PATTERN,
  35. BEGIN_COMPOUND_PATTERN,
  36. BEGIN_PATTERN_EXPR,
  37. SAVE_EXACT_MATCH,
  38. SAVE_SEGMENT,
  39. LOOK_FOR_NUMBER,
  40. LOOK_FOR_INCLUDE,
  41. LOOK_FOR_EXCLUDE,
  42. ADVANCE_TO_END_OF_EXPR,
  43. PARSE_CHAR_EXPR_OR_END,
  44. SKIP_EXCLUDE_SET,
  45. CONDENSE_SET,
  46. PARSE_END_FOUND,
  47. SKIP_INCLUDE_SET,
  48. END_PATTERN_EXPR,
  49. PATTERN_DONE,
  50. PATTERN_ERROR
  51. } PATTERNSTATE;
  52. PCWSTR g_FailedGetResourceString = L"";
  53. /*++
  54. Routine Description:
  55. AllocTextEx allocates a block of memory from the specified pool, or g_TextPool
  56. if no pool is specified, and is designated specifically for text processing.
  57. The g_TextPool is initialized when migutil.lib loads up, and there is 64K of
  58. guaranteed workspace, which will grow as necessary.
  59. Arguments:
  60. Pool - Specifies the pool to allocate memory from
  61. CountOfChars - Specifies the number of characters (not bytes) to allocate. The
  62. return pointer is a block of memory that can hold CountOfChars characters,
  63. weather they are SBCS, DBCS or UNICODE.
  64. Return Value:
  65. A pointer to the allocated memory, or NULL if the pool could not be expanded
  66. to hold the number of specified characters.
  67. --*/
  68. PSTR
  69. RealAllocTextExA (
  70. IN POOLHANDLE Pool,
  71. IN UINT CountOfChars
  72. )
  73. {
  74. PSTR text;
  75. if (CountOfChars == 0) {
  76. return NULL;
  77. }
  78. if (!Pool) {
  79. Pool = g_TextPool;
  80. }
  81. text = PoolMemGetAlignedMemory (Pool, CountOfChars * sizeof (CHAR) * 2);
  82. text [0] = 0;
  83. return text;
  84. }
  85. PWSTR
  86. RealAllocTextExW (
  87. IN POOLHANDLE Pool,
  88. IN UINT CountOfChars
  89. )
  90. {
  91. PWSTR text;
  92. if (CountOfChars == 0) {
  93. return NULL;
  94. }
  95. if (!Pool) {
  96. Pool = g_TextPool;
  97. }
  98. text = PoolMemGetAlignedMemory (Pool, CountOfChars * sizeof (WCHAR));
  99. text [0] = 0;
  100. return text;
  101. }
  102. /*++
  103. Routine Description:
  104. FreeText frees the memory allocated by AllocText. After all strings are freed,
  105. the block will be emptied but not deallocated.
  106. It is important NOT to leak memory, because a leak will cause the pool to
  107. expand, and non-empty pools cause memory fragmentation.
  108. Arguments:
  109. Text - Specifies the text to free, as returned from AllocText, DuplicateText,
  110. DuplicateTextEx, etc...
  111. Return Value:
  112. none
  113. --*/
  114. VOID
  115. FreeTextExA (
  116. IN POOLHANDLE Pool, OPTIONAL
  117. IN PCSTR Text OPTIONAL
  118. )
  119. {
  120. if (Text) {
  121. if (!Pool) {
  122. Pool = g_TextPool;
  123. }
  124. PoolMemReleaseMemory (Pool, (PVOID) Text);
  125. }
  126. }
  127. VOID
  128. FreeTextExW (
  129. IN POOLHANDLE Pool, OPTIONAL
  130. IN PCWSTR Text OPTIONAL
  131. )
  132. {
  133. if (Text) {
  134. if (!Pool) {
  135. Pool = g_TextPool;
  136. }
  137. PoolMemReleaseMemory (Pool, (PVOID) Text);
  138. }
  139. }
  140. /*++
  141. Routine Description:
  142. DuplicateTextEx duplicates a text string and allocates additional space a
  143. caller needs to complete its processing. Optionally, the caller receives
  144. a pointer to the nul of the duplicated string (to allow more efficient
  145. appends).
  146. Arguments:
  147. Text - Specifies the text to duplicate
  148. ExtraChars - Specifies the number of characters (not bytes) to allocate
  149. space for. The characters can be from the SBCS, DBCS or
  150. UNICODE character sets.
  151. NulChar - Receives a pointer to the nul at the end of the duplicated
  152. string. Use for fast appends.
  153. Return Value:
  154. A pointer to the duplicated and expanded string, or NULL if g_TextPool
  155. could not be expanded to fit the duplicated string and extra characters.
  156. --*/
  157. PSTR
  158. RealDuplicateTextExA (
  159. IN POOLHANDLE Pool, OPTIONAL
  160. IN PCSTR Text,
  161. IN UINT ExtraChars,
  162. OUT PSTR *NulChar OPTIONAL
  163. )
  164. {
  165. PSTR Buf;
  166. PSTR d;
  167. PCSTR s;
  168. Buf = AllocTextExA (Pool, CharCountA (Text) + ExtraChars + 1);
  169. if (Buf) {
  170. s = Text;
  171. d = Buf;
  172. while (*s) {
  173. if (IsLeadByte (*s)) {
  174. *d++ = *s++;
  175. }
  176. *d++ = *s++;
  177. }
  178. *d = 0;
  179. if (NulChar) {
  180. *NulChar = d;
  181. }
  182. }
  183. return Buf;
  184. }
  185. PWSTR
  186. RealDuplicateTextExW (
  187. IN POOLHANDLE Pool, OPTIONAL
  188. IN PCWSTR Text,
  189. IN UINT ExtraChars,
  190. OUT PWSTR *NulChar OPTIONAL
  191. )
  192. {
  193. PWSTR Buf;
  194. PWSTR d;
  195. PCWSTR s;
  196. Buf = AllocTextExW (Pool, wcslen (Text) + ExtraChars + 1);
  197. if (Buf) {
  198. s = Text;
  199. d = Buf;
  200. while (*s) {
  201. *d++ = *s++;
  202. }
  203. *d = 0;
  204. if (NulChar) {
  205. *NulChar = d;
  206. }
  207. }
  208. return Buf;
  209. }
  210. /*++
  211. Routine Description:
  212. JoinText duplicates String1 and appends String2 to it delimited with the optional delimiterstring.
  213. Arguments:
  214. String1 - Specifies the text to duplciate
  215. String2 - Specifies the text to append to String1
  216. DelimiterString - Optionally specifies the string to place between string 1 and string 2.
  217. ExtraChars - Specifies the number of characters (not bytes) to allocate
  218. space for. The characters can be from the SBCS, DBCS or
  219. UNICODE character sets.
  220. NulChar - Receives a pointer to the nul at the end of the duplicated
  221. string. Use for fast appends.
  222. Return Value:
  223. A pointer to the duplicated string and extra characters.
  224. --*/
  225. PSTR
  226. RealJoinTextExA (
  227. IN POOLHANDLE Pool, OPTIONAL
  228. IN PCSTR String1,
  229. IN PCSTR String2,
  230. IN PCSTR CenterString, OPTIONAL
  231. IN UINT ExtraChars,
  232. OUT PSTR *NulChar OPTIONAL
  233. )
  234. {
  235. PSTR Buf;
  236. PSTR End;
  237. PSTR d;
  238. PCSTR s;
  239. Buf = DuplicateTextExA (
  240. Pool,
  241. String1,
  242. CharCountA (String2) + ExtraChars + (CenterString ? CharCountA (CenterString) : 0),
  243. &End
  244. );
  245. if (Buf) {
  246. d = End;
  247. if (CenterString) {
  248. s = CenterString;
  249. while (*s) {
  250. if (IsLeadByte (*s)) {
  251. *d++ = *s++;
  252. }
  253. *d++ = *s++;
  254. }
  255. }
  256. s = String2;
  257. while (*s) {
  258. if (IsLeadByte (*s)) {
  259. *d++ = *s++;
  260. }
  261. *d++ = *s++;
  262. }
  263. *d = 0;
  264. if (NulChar) {
  265. *NulChar = d;
  266. }
  267. }
  268. return Buf;
  269. }
  270. PWSTR
  271. RealJoinTextExW (
  272. IN POOLHANDLE Pool, OPTIONAL
  273. IN PCWSTR String1,
  274. IN PCWSTR String2,
  275. IN PCWSTR CenterString, OPTIONAL
  276. IN UINT ExtraChars,
  277. OUT PWSTR *NulChar OPTIONAL
  278. )
  279. {
  280. PWSTR Buf;
  281. PWSTR End;
  282. PCWSTR s;
  283. PWSTR d;
  284. Buf = DuplicateTextExW (
  285. Pool,
  286. String1,
  287. wcslen (String2) + ExtraChars + (CenterString ? wcslen(CenterString) : 0),
  288. &End
  289. );
  290. if (Buf) {
  291. d = End;
  292. if (CenterString) {
  293. s = CenterString;
  294. while (*s) {
  295. *d++ = *s++;
  296. }
  297. }
  298. s = String2;
  299. while (*s) {
  300. *d++ = *s++;
  301. }
  302. *d = 0;
  303. if (NulChar) {
  304. *NulChar = d;
  305. }
  306. }
  307. return Buf;
  308. }
  309. /*++
  310. Routine Description:
  311. ExpandEnvironmentTextEx takes a block of text containing zero or more environment variables
  312. (encoded in %'s) and returns the text with the environment variables expanded. The function
  313. also allows the caller to specify additional environment variables in an array and will use
  314. these variables before calling GetEnvironmentVariable.
  315. The returned text is allocated out of the Text pool and should be freed using FreeText().
  316. Arguments:
  317. InString - The string containing environement variables to be processed.
  318. ExtraVars - Optional var pointing to an array of environment variables to be used to supersede
  319. or suppliment the system environment variables. Even entries in the list are the
  320. names of environment variables, odd entries there values.
  321. (e.g. {"name1","value1","name2","value2",...}
  322. Return Value:
  323. An expanded string.
  324. --*/
  325. PWSTR
  326. RealExpandEnvironmentTextExW (
  327. IN PCWSTR InString,
  328. IN PCWSTR * ExtraVars OPTIONAL
  329. )
  330. {
  331. PWSTR rString = NULL;
  332. PWSTR newString = NULL;
  333. PWSTR envName = NULL;
  334. PWSTR envValue = NULL;
  335. BOOL inSubstitution = FALSE;
  336. BOOL ignoreNextPercent = FALSE;
  337. BOOL errorOccurred = FALSE;
  338. BOOL foundValue = FALSE;
  339. BOOL freeValue = FALSE;
  340. PCWSTR nextPercent = NULL;
  341. PCWSTR source = NULL;
  342. PCWSTR savedSource = NULL;
  343. INT maxSize = 0;
  344. INT curSize = 0;
  345. UINT index = 0;
  346. UINT size = 0;
  347. //
  348. // We assume that InString is valid.
  349. //
  350. MYASSERT(InString);
  351. if (*InString == 0) {
  352. return DuplicateTextW (InString);
  353. }
  354. //
  355. // Set source to the start of InString to begin with...
  356. //
  357. source = InString;
  358. __try {
  359. while (*source) {
  360. //
  361. // Reallocate the string if necessary. We assume that most strings
  362. // are smaller than 1024 chars and that we will therefore only rarely
  363. // reallocate a string.
  364. //
  365. if (curSize > maxSize - 3) {
  366. maxSize += 1024;
  367. newString = AllocTextW (maxSize);
  368. if (!newString) {
  369. DEBUGMSG((DBG_ERROR,"ExpanEnvironmentTextEx: Memory Error!"));
  370. errorOccurred = TRUE;
  371. __leave;
  372. }
  373. if (rString) {
  374. memcpy(newString,rString,curSize * sizeof(WCHAR));
  375. FreeTextW(rString);
  376. }
  377. rString = newString;
  378. }
  379. //
  380. // if we find a percent sign, and we are not currently expanding
  381. // an environment variable (or copying an empty set of %'s),
  382. // then we have probably found an environment variable. Attempt
  383. // to expand it.
  384. //
  385. if (*source == L'%' && !inSubstitution) {
  386. if (ignoreNextPercent) {
  387. ignoreNextPercent = FALSE;
  388. }
  389. else {
  390. ignoreNextPercent = FALSE;
  391. nextPercent = wcschr(source + 1,L'%');
  392. if (nextPercent == source + 1) {
  393. //
  394. // We found two consecutive %s in this string. We'll ignore them and simply copy them as
  395. // normal text.
  396. //
  397. ignoreNextPercent = TRUE;
  398. DEBUGMSGW((DBG_WARNING,"ExpandEnvironmentTextEx: Empty Environment variable in %s. Ignoring.",InString));
  399. }
  400. else if (nextPercent) {
  401. //
  402. // Create a variable to hold the envName.
  403. //
  404. envName = AllocTextW(nextPercent - source);
  405. _wcssafecpyab(envName,source+1,nextPercent,(nextPercent - source)*sizeof(WCHAR));
  406. //
  407. // Try to find the variable.
  408. //
  409. foundValue = FALSE;
  410. freeValue = FALSE;
  411. if (ExtraVars) {
  412. //
  413. // Search through the list of extra vars passed in by the caller.
  414. // Even entries of this list are env var names. Odd entries are env values.
  415. // {envname1,envvalue1,envname2,envvalue2,...}
  416. //
  417. index = 0;
  418. while (ExtraVars[index]) {
  419. if (StringIMatchW(ExtraVars[index],envName) && ExtraVars[index + 1]) {
  420. foundValue = TRUE;
  421. envValue = (PWSTR) ExtraVars[index + 1];
  422. break;
  423. }
  424. index +=2;
  425. }
  426. }
  427. if (!foundValue) {
  428. //
  429. // Still haven't found the environment variable. Use GetEnvironmentString.
  430. //
  431. //
  432. size = GetEnvironmentVariableW(envName,NULL,0);
  433. if (!size) {
  434. errorOccurred = TRUE;
  435. DEBUGMSGW((DBG_WARNING,"ExpandEnvironmentTextEx: Environment variable %s not found!",envName));
  436. } else {
  437. //
  438. // Create a buffer large enough to hold this value and copy it in.
  439. //
  440. envValue = AllocTextW(size);
  441. if ((size - 1) != GetEnvironmentVariableW(envName,envValue,size)) {
  442. errorOccurred = TRUE;
  443. DEBUGMSGW((DBG_ERROR,"ExpandEnvironmentTextEx: Error from GetEnvironmentVariable."));
  444. }
  445. else {
  446. foundValue = TRUE;
  447. }
  448. freeValue = TRUE;
  449. }
  450. }
  451. if (foundValue) {
  452. //
  453. // Ok, we have a valid environment value. Need to copy this data over.
  454. // To do this, we update and save the current source into old source, set source = to the envValue,
  455. // and set the inSubstitution value so that we don't attempt to expand any percents within
  456. // the value.
  457. //
  458. savedSource = nextPercent + 1;
  459. source = envValue;
  460. inSubstitution = TRUE;
  461. }
  462. else {
  463. DEBUGMSGW ((DBG_WARNING, "ExpandEnvironmentTextEx: No Environment variable found for %s.", envName));
  464. ignoreNextPercent = TRUE;
  465. }
  466. //
  467. // We are done with the environment name at this time, so clean it up.
  468. //
  469. FreeTextW(envName);
  470. envName = NULL;
  471. }
  472. ELSE_DEBUGMSGW((DBG_WARNING,"ExpandEnvironmentTextEx: No matching percent found in %s. Ignoring.",InString));
  473. }
  474. }
  475. //
  476. // Copy over the current character.
  477. //
  478. rString[curSize++] = *source++;
  479. if (!*source) {
  480. if (inSubstitution) {
  481. //
  482. // The source for the environment variable is fully copied.
  483. // restore the old source.
  484. //
  485. inSubstitution = FALSE;
  486. source = savedSource;
  487. if (!*source) {
  488. rString[curSize] = 0;
  489. }
  490. if (freeValue) {
  491. FreeTextW(envValue);
  492. freeValue = FALSE;
  493. }
  494. envValue = NULL;
  495. }
  496. else {
  497. rString[curSize] = 0;
  498. }
  499. }
  500. }
  501. }
  502. __finally {
  503. DEBUGMSGW_IF (( errorOccurred, DBG_WARNING, "ExpandEnvironmentText: Some errors occurred while processing %s = %s.", InString, rString ? rString : L"NULL"));
  504. if (envName) {
  505. FreeTextW(envName);
  506. }
  507. if (envValue && freeValue) {
  508. FreeTextW(envValue);
  509. }
  510. }
  511. return rString;
  512. }
  513. PSTR
  514. RealExpandEnvironmentTextExA (
  515. IN PCSTR InString,
  516. IN PCSTR * ExtraVars OPTIONAL
  517. )
  518. {
  519. PSTR rString = NULL;
  520. PSTR newString = NULL;
  521. PSTR envName = NULL;
  522. PSTR envValue = NULL;
  523. BOOL inSubstitution = FALSE;
  524. BOOL ignoreNextPercent = FALSE;
  525. BOOL errorOccurred = FALSE;
  526. BOOL foundValue = FALSE;
  527. BOOL freeValue = FALSE;
  528. PCSTR nextPercent = NULL;
  529. PCSTR source = NULL;
  530. PCSTR savedSource = NULL;
  531. INT maxSize = 0;
  532. INT curSize = 0;
  533. UINT index = 0;
  534. UINT size = 0;
  535. //
  536. // We assume that InString is valid.
  537. //
  538. MYASSERT(InString);
  539. if (*InString == 0) {
  540. return DuplicateTextA (InString);
  541. }
  542. //
  543. // Set source to the start of InString to begin with...
  544. //
  545. source = InString;
  546. __try {
  547. while (*source) {
  548. //
  549. // Reallocate the string if necessary. We assume that most strings
  550. // are smaller than 1024 chars and that we will therefore only rarely
  551. // reallocate a string.
  552. //
  553. if (curSize > maxSize - 3) {
  554. maxSize += 1024;
  555. newString = AllocTextA (maxSize);
  556. if (rString) {
  557. memcpy(newString,rString,curSize * sizeof(CHAR));
  558. FreeTextA(rString);
  559. }
  560. rString = newString;
  561. }
  562. //
  563. // if we find a percent sign, and we are not currently expanding
  564. // an environment variable (or copying an empty set of %'s),
  565. // then we have probably found an environment variable. Attempt
  566. // to expand it.
  567. //
  568. if (!IsLeadByte(*source) && *source == '%' && !inSubstitution) {
  569. if (ignoreNextPercent) {
  570. ignoreNextPercent = FALSE;
  571. }
  572. else {
  573. ignoreNextPercent = FALSE;
  574. nextPercent = _mbschr(source + 1,'%');
  575. if (nextPercent == source + 1) {
  576. //
  577. // We found two consecutive %s in this string. We'll ignore them and simply copy them as
  578. // normal text.
  579. //
  580. ignoreNextPercent = TRUE;
  581. DEBUGMSGA((DBG_WARNING,"ExpandEnvironmentTextEx: Empty Environment variable in %s. Ignoring.",InString));
  582. }
  583. else if (nextPercent) {
  584. //
  585. // Create a variable to hold the envName.
  586. //
  587. envName = AllocTextA(nextPercent - source);
  588. _mbssafecpyab(envName,source+1,nextPercent,nextPercent - source);
  589. //
  590. // Try to find the variable.
  591. //
  592. foundValue = FALSE;
  593. freeValue = FALSE;
  594. if (ExtraVars) {
  595. //
  596. // Search through the list of extra vars passed in by the caller.
  597. // Even entries of this list are env var names. Odd entries are env values.
  598. // {envname1,envvalue1,envname2,envvalue2,...}
  599. //
  600. index = 0;
  601. while (ExtraVars[index]) {
  602. if (StringIMatch(ExtraVars[index],envName) && ExtraVars[index + 1]) {
  603. foundValue = TRUE;
  604. envValue = (PSTR) ExtraVars[index + 1];
  605. break;
  606. }
  607. index +=2;
  608. }
  609. }
  610. if (!foundValue) {
  611. //
  612. // Still haven't found the environment variable. Use GetEnvironmentString.
  613. //
  614. //
  615. size = GetEnvironmentVariableA(envName,NULL,0);
  616. if (!size) {
  617. errorOccurred = TRUE;
  618. DEBUGMSGA((DBG_WARNING,"ExpandEnvironmentTextEx: Environment variable %s not found!",envName));
  619. }
  620. else {
  621. //
  622. // Create a buffer large enough to hold this value and copy it in.
  623. //
  624. envValue = AllocTextA(size);
  625. freeValue = TRUE;
  626. if ((size - 1) != GetEnvironmentVariableA(envName,envValue,size)) {
  627. errorOccurred = TRUE;
  628. DEBUGMSGA((DBG_ERROR,"ExpandEnvironmentTextEx: Error from GetEnvironmentVariable."));
  629. }
  630. else {
  631. foundValue = TRUE;
  632. }
  633. }
  634. }
  635. if (foundValue) {
  636. //
  637. // Ok, we have a valid environment value. Need to copy this data over.
  638. // To do this, we update and save the current source into old source, set source = to the envValue,
  639. // and set the inSubstitution value so that we don't attempt to expand any percents within
  640. // the value.
  641. //
  642. savedSource = nextPercent + 1;
  643. source = envValue;
  644. inSubstitution = TRUE;
  645. }
  646. else {
  647. DEBUGMSGA ((DBG_WARNING, "ExpandEnvironmentTextEx: No Environment variable found for %s.", envName));
  648. ignoreNextPercent = TRUE;
  649. }
  650. //
  651. // We are done with the environment name at this time, so clean it up.
  652. //
  653. FreeTextA(envName);
  654. envName = NULL;
  655. }
  656. ELSE_DEBUGMSGA((DBG_WARNING,"ExpandEnvironmentTextEx: No matching percent found in %s. Ignoring.",InString));
  657. }
  658. }
  659. //
  660. // Copy over the current character.
  661. //
  662. if (IsLeadByte(*source)) {
  663. rString[curSize++] = *source++;
  664. }
  665. rString[curSize++] = *source++;
  666. if (!*source) {
  667. if (inSubstitution) {
  668. //
  669. // The source for the environment variable is fully copied.
  670. // restore the old source.
  671. //
  672. inSubstitution = FALSE;
  673. source = savedSource;
  674. if (!*source) {
  675. rString[curSize] = 0;
  676. }
  677. if (freeValue) {
  678. FreeTextA(envValue);
  679. freeValue = FALSE;
  680. }
  681. envValue = NULL;
  682. }
  683. else {
  684. rString[curSize] = 0;
  685. }
  686. }
  687. }
  688. }
  689. __finally {
  690. DEBUGMSGA_IF (( errorOccurred, DBG_WARNING, "ExpandEnvironmentText: Some errors occurred while processing %s = %s.", InString, rString ? rString : "NULL"));
  691. if (envName) {
  692. FreeTextA(envName);
  693. }
  694. if (envValue && freeValue) {
  695. FreeTextA(envValue);
  696. }
  697. }
  698. return rString;
  699. }
  700. /*++
  701. Routine Description:
  702. AppendWack adds a backslash to the end of any string, unless the string
  703. already ends in a backslash.
  704. AppendDosWack adds a backslash, but only if the path does not already
  705. end in a backslash or colon. AppendWack supports DOS naming
  706. conventions: it does not append a back-slash if the path is empty,
  707. ends in a colon or if it ends in a back-slash already.
  708. AppendUncWack supports UNC naming conventions: it does not append a
  709. backslash if the path is empty or if it ends in a backslash already.
  710. AppendPathWack supports both DOS and UNC naming conventions, and uses the
  711. UNC naming convention if the string starts with double-wacks.
  712. Arguments:
  713. str - A buffer that holds the path, plus additional space for another
  714. backslash.
  715. Return Value:
  716. none
  717. --*/
  718. PSTR
  719. AppendWackA (
  720. IN PSTR str
  721. )
  722. {
  723. PCSTR Last;
  724. if (!str)
  725. return str;
  726. Last = str;
  727. while (*str) {
  728. Last = str;
  729. str = _mbsinc (str);
  730. }
  731. if (*Last != '\\') {
  732. *str = '\\';
  733. str++;
  734. *str = 0;
  735. }
  736. return str;
  737. }
  738. PWSTR
  739. AppendWackW (
  740. IN PWSTR str
  741. )
  742. {
  743. PCWSTR Last;
  744. if (!str)
  745. return str;
  746. if (*str) {
  747. str = GetEndOfStringW (str);
  748. Last = str - 1;
  749. } else {
  750. Last = str;
  751. }
  752. if (*Last != '\\') {
  753. *str = L'\\';
  754. str++;
  755. *str = 0;
  756. }
  757. return str;
  758. }
  759. PSTR
  760. AppendDosWackA (
  761. IN PSTR str
  762. )
  763. {
  764. PCSTR Last;
  765. if (!str || !(*str))
  766. return str;
  767. do {
  768. Last = str;
  769. str = _mbsinc (str);
  770. } while (*str);
  771. if (*Last != '\\' && *Last != ':') {
  772. *str = '\\';
  773. str++;
  774. *str = 0;
  775. }
  776. return str;
  777. }
  778. PWSTR
  779. AppendDosWackW (
  780. IN PWSTR str
  781. )
  782. {
  783. PWSTR Last;
  784. if (!str || !(*str))
  785. return str;
  786. str = GetEndOfStringW (str);
  787. Last = str - 1;
  788. if (*Last != L'\\' && *Last != L':') {
  789. *str = L'\\';
  790. str++;
  791. *str = 0;
  792. }
  793. return str;
  794. }
  795. PSTR
  796. AppendUncWackA (
  797. IN PSTR str
  798. )
  799. {
  800. PCSTR Last;
  801. if (!str || !(*str))
  802. return str;
  803. do {
  804. Last = str;
  805. str = _mbsinc (str);
  806. } while (*str);
  807. if (*Last != '\\') {
  808. *str = '\\';
  809. str++;
  810. *str = 0;
  811. }
  812. return str;
  813. }
  814. PWSTR
  815. AppendUncWackW (
  816. IN PWSTR str
  817. )
  818. {
  819. PWSTR Last;
  820. if (!str || !(*str))
  821. return str;
  822. str = GetEndOfStringW (str);
  823. Last = str - 1;
  824. if (*Last != L'\\') {
  825. *str = L'\\';
  826. str++;
  827. *str = 0;
  828. }
  829. return str;
  830. }
  831. PSTR
  832. AppendPathWackA (
  833. IN PSTR str
  834. )
  835. {
  836. if (!str) {
  837. return str;
  838. }
  839. if (str[0] == '\\' && str[1] == '\\') {
  840. return AppendUncWackA (str);
  841. }
  842. return AppendDosWackA (str);
  843. }
  844. PWSTR
  845. AppendPathWackW (
  846. IN PWSTR str
  847. )
  848. {
  849. if (!str) {
  850. return str;
  851. }
  852. if (str[0] == L'\\' && str[1] == L'\\') {
  853. return AppendUncWackW (str);
  854. }
  855. return AppendDosWackW (str);
  856. }
  857. PSTR
  858. RealJoinPathsExA (
  859. IN POOLHANDLE Pool, OPTIONAL
  860. IN PCSTR PathA,
  861. IN PCSTR PathB
  862. )
  863. {
  864. PSTR end;
  865. PSTR endMinusOne;
  866. DWORD Size;
  867. PSTR Dest;
  868. if (!Pool) {
  869. Pool = g_PathsPool;
  870. }
  871. Size = ByteCountA (PathA) + 1 + SizeOfStringA (PathB);
  872. Dest = (PSTR) PoolMemGetAlignedMemory (Pool, Size);
  873. MYASSERT (Dest);
  874. *Dest = 0;
  875. end = _mbsappend (Dest, PathA);
  876. endMinusOne = _mbsdec (Dest, end);
  877. if (endMinusOne && _mbsnextc (endMinusOne) != '\\') {
  878. *end = '\\';
  879. end++;
  880. }
  881. if (_mbsnextc (PathB) == '\\') {
  882. PathB = _mbsinc (PathB);
  883. }
  884. StringCopyA (end, PathB);
  885. return Dest;
  886. }
  887. PWSTR
  888. RealJoinPathsExW (
  889. IN POOLHANDLE Pool, OPTIONAL
  890. IN PCWSTR PathA,
  891. IN PCWSTR PathB
  892. )
  893. {
  894. PWSTR end;
  895. PWSTR endMinusOne;
  896. DWORD Size;
  897. PWSTR Dest;
  898. if (!Pool) {
  899. Pool = g_PathsPool;
  900. }
  901. Size = ByteCountW (PathA) + sizeof (WCHAR) + SizeOfStringW (PathB);
  902. Dest = (PWSTR) PoolMemGetAlignedMemory (Pool, Size);
  903. MYASSERT (Dest);
  904. *Dest = 0;
  905. end = _wcsappend (Dest, PathA);
  906. endMinusOne = _wcsdec2 (Dest, end);
  907. if (endMinusOne && *endMinusOne != L'\\') {
  908. *end = L'\\';
  909. end++;
  910. }
  911. if (*PathB == L'\\') {
  912. PathB++;
  913. }
  914. StringCopyW (end, PathB);
  915. return Dest;
  916. }
  917. PSTR
  918. RealAllocPathStringA (
  919. DWORD Chars
  920. )
  921. {
  922. PSTR Str;
  923. if (Chars == 0) {
  924. Chars = MAX_MBCHAR_PATH;
  925. }
  926. Str = (PSTR) PoolMemGetAlignedMemory (g_PathsPool, Chars);
  927. Str [0] = 0;
  928. return Str;
  929. }
  930. PWSTR
  931. RealAllocPathStringW (
  932. DWORD Chars
  933. )
  934. {
  935. PWSTR Str;
  936. if (Chars == 0) {
  937. Chars = MAX_WCHAR_PATH;
  938. }
  939. Str = (PWSTR) PoolMemGetAlignedMemory (g_PathsPool, Chars * sizeof (WCHAR));
  940. Str [0] = 0;
  941. return Str;
  942. }
  943. VOID
  944. RealSplitPathA (
  945. IN PCSTR Path,
  946. OUT PSTR *DrivePtr,
  947. OUT PSTR *PathPtr,
  948. OUT PSTR *FileNamePtr,
  949. OUT PSTR *ExtPtr
  950. )
  951. {
  952. CHAR Drive[_MAX_DRIVE];
  953. CHAR Dir[_MAX_DIR];
  954. CHAR FileName[_MAX_FNAME];
  955. CHAR Ext[_MAX_EXT];
  956. _splitpath (Path, Drive, Dir, FileName, Ext);
  957. if (DrivePtr) {
  958. *DrivePtr = PoolMemDuplicateStringA (g_PathsPool, Drive);
  959. MYASSERT (*DrivePtr);
  960. }
  961. if (PathPtr) {
  962. *PathPtr = PoolMemDuplicateStringA (g_PathsPool, Dir);
  963. MYASSERT (*PathPtr);
  964. }
  965. if (FileNamePtr) {
  966. *FileNamePtr = PoolMemDuplicateStringA (g_PathsPool, FileName);
  967. MYASSERT (*FileNamePtr);
  968. }
  969. if (ExtPtr) {
  970. *ExtPtr = PoolMemDuplicateStringA (g_PathsPool, Ext);
  971. MYASSERT (*ExtPtr);
  972. }
  973. }
  974. VOID
  975. RealSplitPathW (
  976. IN PCWSTR Path,
  977. OUT PWSTR *DrivePtr,
  978. OUT PWSTR *PathPtr,
  979. OUT PWSTR *FileNamePtr,
  980. OUT PWSTR *ExtPtr
  981. )
  982. {
  983. WCHAR Drive[_MAX_DRIVE];
  984. WCHAR Dir[_MAX_DIR];
  985. WCHAR FileName[_MAX_FNAME];
  986. WCHAR Ext[_MAX_EXT];
  987. _wsplitpath (Path, Drive, Dir, FileName, Ext);
  988. if (DrivePtr) {
  989. *DrivePtr = PoolMemDuplicateStringW (g_PathsPool, Drive);
  990. MYASSERT (*DrivePtr);
  991. }
  992. if (PathPtr) {
  993. *PathPtr = PoolMemDuplicateStringW (g_PathsPool, Dir);
  994. MYASSERT (*PathPtr);
  995. }
  996. if (FileNamePtr) {
  997. *FileNamePtr = PoolMemDuplicateStringW (g_PathsPool, FileName);
  998. MYASSERT (*FileNamePtr);
  999. }
  1000. if (ExtPtr) {
  1001. *ExtPtr = PoolMemDuplicateStringW (g_PathsPool, Ext);
  1002. MYASSERT (*ExtPtr);
  1003. }
  1004. }
  1005. PSTR
  1006. RealDuplicatePathStringA (
  1007. PCSTR Path,
  1008. DWORD ExtraBytes
  1009. )
  1010. {
  1011. PSTR str;
  1012. str = PoolMemGetAlignedMemory (
  1013. g_PathsPool,
  1014. SizeOfStringA (Path) + ExtraBytes
  1015. );
  1016. MYASSERT (str);
  1017. StringCopyA (str, Path);
  1018. return str;
  1019. }
  1020. PWSTR
  1021. RealDuplicatePathStringW (
  1022. PCWSTR Path,
  1023. DWORD ExtraBytes
  1024. )
  1025. {
  1026. PWSTR str;
  1027. str = PoolMemGetAlignedMemory (
  1028. g_PathsPool,
  1029. SizeOfStringW (Path) + ExtraBytes
  1030. );
  1031. MYASSERT (str);
  1032. StringCopyW (str, Path);
  1033. return str;
  1034. }
  1035. PSTR
  1036. pCopyAndCleanupPathsA (
  1037. IN PCSTR Source,
  1038. OUT PSTR Dest
  1039. )
  1040. {
  1041. BOOL quotes = FALSE;
  1042. do {
  1043. while (*Source && isspace (*Source)) {
  1044. Source++;
  1045. }
  1046. while (*Source) {
  1047. if (IsLeadByte (*Source)) {
  1048. *Dest++ = *Source++;
  1049. *Dest++ = *Source++;
  1050. } else {
  1051. if (*Source == '\"') {
  1052. Source++;
  1053. quotes = !quotes;
  1054. } else if (!quotes) {
  1055. *Dest++ = *Source++;
  1056. }
  1057. }
  1058. }
  1059. } while (*Source);
  1060. *Dest = 0;
  1061. return Dest;
  1062. }
  1063. BOOL
  1064. EnumFirstPathExA (
  1065. OUT PPATH_ENUMA PathEnum,
  1066. IN PCSTR AdditionalPath,
  1067. IN PCSTR WinDir,
  1068. IN PCSTR SysDir,
  1069. IN BOOL IncludeEnvPath
  1070. )
  1071. {
  1072. DWORD bufferSize = 0;
  1073. DWORD pathSize;
  1074. PSTR currPathEnd;
  1075. if (PathEnum == NULL) {
  1076. return FALSE;
  1077. }
  1078. if (IncludeEnvPath) {
  1079. bufferSize = pathSize = GetEnvironmentVariableA ("PATH", NULL, 0);
  1080. }
  1081. if (AdditionalPath != NULL) {
  1082. bufferSize += SizeOfStringA (AdditionalPath);
  1083. }
  1084. if (SysDir != NULL) {
  1085. bufferSize += SizeOfStringA (SysDir);
  1086. }
  1087. if (WinDir != NULL) {
  1088. bufferSize += SizeOfStringA (WinDir);
  1089. }
  1090. PathEnum->BufferPtr = MemAlloc (g_hHeap, 0, bufferSize + 1);
  1091. if (PathEnum->BufferPtr == NULL) {
  1092. return FALSE;
  1093. }
  1094. PathEnum->BufferPtr [0] = 0;
  1095. currPathEnd = PathEnum->BufferPtr;
  1096. if (AdditionalPath != NULL) {
  1097. currPathEnd = _mbsappend (currPathEnd, AdditionalPath);
  1098. }
  1099. if (SysDir != NULL) {
  1100. *currPathEnd++ = ';';
  1101. *currPathEnd = 0;
  1102. currPathEnd = _mbsappend (currPathEnd, SysDir);
  1103. }
  1104. if (WinDir != NULL) {
  1105. *currPathEnd++ = ';';
  1106. *currPathEnd = 0;
  1107. currPathEnd = _mbsappend (currPathEnd, WinDir);
  1108. }
  1109. if (IncludeEnvPath) {
  1110. *currPathEnd++ = ';';
  1111. *currPathEnd = 0;
  1112. GetEnvironmentVariableA ("PATH", currPathEnd, pathSize);
  1113. }
  1114. //
  1115. // clean up quotes
  1116. //
  1117. pCopyAndCleanupPathsA (currPathEnd, currPathEnd);
  1118. PathEnum->PtrNextPath = PathEnum-> BufferPtr;
  1119. return EnumNextPathA (PathEnum);
  1120. }
  1121. BOOL
  1122. EnumNextPathA (
  1123. IN OUT PPATH_ENUMA PathEnum
  1124. )
  1125. {
  1126. do {
  1127. if (PathEnum->PtrNextPath == NULL) {
  1128. EnumPathAbort (PathEnum);
  1129. return FALSE;
  1130. }
  1131. PathEnum->PtrCurrPath = PathEnum->PtrNextPath;
  1132. PathEnum->PtrNextPath = _mbschr (PathEnum->PtrNextPath, ';');
  1133. if (PathEnum->PtrNextPath != NULL) {
  1134. if (PathEnum->PtrNextPath - PathEnum->PtrCurrPath >= MAX_MBCHAR_PATH) {
  1135. *PathEnum->PtrNextPath = 0;
  1136. LOG ((
  1137. LOG_WARNING,
  1138. "Skipping enumeration of path (too long): %s",
  1139. PathEnum->PtrCurrPath
  1140. ));
  1141. *PathEnum->PtrNextPath = ';';
  1142. //
  1143. // cut this path
  1144. //
  1145. *PathEnum->PtrCurrPath = 0;
  1146. //
  1147. // and continue with the next one
  1148. //
  1149. continue;
  1150. }
  1151. *PathEnum->PtrNextPath++ = 0;
  1152. if (*(PathEnum->PtrNextPath) == 0) {
  1153. PathEnum->PtrNextPath = NULL;
  1154. }
  1155. } else {
  1156. if (ByteCountA (PathEnum->PtrCurrPath) >= MAX_MBCHAR_PATH) {
  1157. LOG ((
  1158. LOG_WARNING,
  1159. "Skipping enumeration of path (too long): %s",
  1160. PathEnum->PtrCurrPath
  1161. ));
  1162. //
  1163. // cut this path
  1164. //
  1165. *PathEnum->PtrCurrPath = 0;
  1166. }
  1167. }
  1168. } while (*(PathEnum->PtrCurrPath) == 0);
  1169. return TRUE;
  1170. }
  1171. BOOL
  1172. EnumPathAbortA (
  1173. IN OUT PPATH_ENUMA PathEnum
  1174. )
  1175. {
  1176. if (PathEnum->BufferPtr != NULL) {
  1177. MemFree (g_hHeap, 0, PathEnum->BufferPtr);
  1178. PathEnum->BufferPtr = NULL;
  1179. }
  1180. return TRUE;
  1181. }
  1182. VOID
  1183. FreePathStringExA (
  1184. IN POOLHANDLE Pool, OPTIONAL
  1185. IN PCSTR Path OPTIONAL
  1186. )
  1187. {
  1188. if (Path) {
  1189. if (!Pool) {
  1190. Pool = g_PathsPool;
  1191. }
  1192. PoolMemReleaseMemory (Pool, (PSTR) Path);
  1193. }
  1194. }
  1195. VOID
  1196. FreePathStringExW (
  1197. IN POOLHANDLE Pool, OPTIONAL
  1198. IN PCWSTR Path OPTIONAL
  1199. )
  1200. {
  1201. if (Path) {
  1202. if (!Pool) {
  1203. Pool = g_PathsPool;
  1204. }
  1205. PoolMemReleaseMemory (Pool, (PWSTR) Path);
  1206. }
  1207. }
  1208. /*++
  1209. Routine Description:
  1210. PushError and PopError push the error code onto a stack or pull the
  1211. last pushed error code off the stack. PushError uses GetLastError
  1212. and PopError uses SetLastError to modify the last error value.
  1213. Arguments:
  1214. none
  1215. Return Value:
  1216. none
  1217. --*/
  1218. DWORD g_dwErrorStack[MAX_STACK];
  1219. DWORD g_dwStackPos = 0;
  1220. void
  1221. PushNewError (DWORD dwError)
  1222. {
  1223. if (g_dwStackPos == MAX_STACK)
  1224. return;
  1225. g_dwErrorStack[g_dwStackPos] = dwError;
  1226. g_dwStackPos++;
  1227. }
  1228. void
  1229. PushError (void)
  1230. {
  1231. if (g_dwStackPos == MAX_STACK)
  1232. return;
  1233. g_dwErrorStack[g_dwStackPos] = GetLastError ();
  1234. g_dwStackPos++;
  1235. }
  1236. DWORD
  1237. PopError (void)
  1238. {
  1239. if (!g_dwStackPos)
  1240. return GetLastError();
  1241. g_dwStackPos--;
  1242. SetLastError (g_dwErrorStack[g_dwStackPos]);
  1243. return g_dwErrorStack[g_dwStackPos];
  1244. }
  1245. /*++
  1246. Routine Description:
  1247. GetHexDigit is a simple base 16 ASCII to int convertor. The
  1248. convertor is case-insensitive.
  1249. Arguments:
  1250. c - Character to convert
  1251. Return Value:
  1252. Base 16 value corresponding to character supplied, or -1 if
  1253. the character is not 0-9, A-F or a-f.
  1254. --*/
  1255. int
  1256. GetHexDigit (IN int c)
  1257. {
  1258. if (c >= '0' && c <= '9')
  1259. return (c - '0');
  1260. c = towlower ((wint_t) c);
  1261. if (c >= 'a' && c <= 'f')
  1262. return (c - 'a' + 10);
  1263. return -1;
  1264. }
  1265. /*++
  1266. Routine Description:
  1267. _tcsnum is similar to strtoul, except is figures out which base
  1268. the number should be calculated from. It supports decimal and
  1269. hexadecimal numbers (using the 0x00 notation). The return
  1270. value is the decoded value, or 0 if a syntax error was found.
  1271. Arguments:
  1272. szNum - Pointer to the string holding the number. This number
  1273. can be either decimal (a series of 0-9 characters), or
  1274. hexadecimal (a series of 0-9, A-F or a-f characters,
  1275. prefixed with 0x or 0X).
  1276. Return Value:
  1277. The decoded unsigned long value, or zero if a syntax error was
  1278. found.
  1279. --*/
  1280. DWORD
  1281. _mbsnum (IN PCSTR szNum)
  1282. {
  1283. unsigned int d = 0;
  1284. int i;
  1285. if (szNum[0] == '0' && tolower (szNum[1]) == 'x') {
  1286. // Get hex value
  1287. szNum += 2;
  1288. while ((i = GetHexDigit ((int) *szNum)) != -1) {
  1289. d = d * 16 + i;
  1290. szNum++;
  1291. }
  1292. }
  1293. else {
  1294. // Get decimal value
  1295. while (*szNum >= '0' && *szNum <= '9') {
  1296. d = d * 10 + (*szNum - '0');
  1297. szNum++;
  1298. }
  1299. }
  1300. return d;
  1301. }
  1302. DWORD
  1303. _wcsnum (IN PCWSTR szNum)
  1304. {
  1305. unsigned int d = 0;
  1306. int i;
  1307. if (szNum[0] == L'0' && towlower (szNum[1]) == L'x') {
  1308. // Get hex value
  1309. szNum += 2;
  1310. while ((i = GetHexDigit ((int) *szNum)) != -1) {
  1311. d = d * 16 + i;
  1312. szNum++;
  1313. }
  1314. }
  1315. else {
  1316. // Get decimal value
  1317. while (*szNum >= L'0' && *szNum <= L'9') {
  1318. d = d * 10 + (*szNum - L'0');
  1319. szNum++;
  1320. }
  1321. }
  1322. return d;
  1323. }
  1324. /*++
  1325. Routine Description:
  1326. _tcsappend is a strcpy that returns the pointer to the end
  1327. of a string instead of the beginning.
  1328. Arguments:
  1329. szDest - A pointer to a caller-allocated buffer that may point
  1330. anywhere within the string to append to
  1331. szSrc - A pointer to a string that is appended to szDest
  1332. Return Value:
  1333. A pointer to the NULL terminator within the szDest string.
  1334. --*/
  1335. PSTR
  1336. _mbsappend (OUT PSTR mbstrDest,
  1337. IN PCSTR mbstrSrc)
  1338. {
  1339. // Advance mbstrDest to end of string
  1340. mbstrDest = GetEndOfStringA (mbstrDest);
  1341. // Copy string
  1342. while (*mbstrSrc) {
  1343. *mbstrDest = *mbstrSrc++;
  1344. if (IsLeadByte (*mbstrDest)) {
  1345. mbstrDest++;
  1346. *mbstrDest = *mbstrSrc++;
  1347. }
  1348. mbstrDest++;
  1349. }
  1350. *mbstrDest = 0;
  1351. return mbstrDest;
  1352. }
  1353. PWSTR
  1354. _wcsappend (OUT PWSTR wstrDest,
  1355. IN PCWSTR wstrSrc)
  1356. {
  1357. // Advance wstrDest to end of string
  1358. wstrDest = GetEndOfStringW (wstrDest);
  1359. // Copy string
  1360. while (*wstrSrc) {
  1361. *wstrDest++ = *wstrSrc++;
  1362. }
  1363. *wstrDest = 0;
  1364. return wstrDest;
  1365. }
  1366. /*++
  1367. Routine Description:
  1368. _tcsistr is a case-insensitive version of _tcsstr.
  1369. Arguments:
  1370. szStr - A pointer to the larger string, which may hold szSubStr
  1371. szSubStr - A pointer to a string that may be enclosed in szStr
  1372. Return Value:
  1373. A pointer to the first occurance of szSubStr in szStr, or NULL if
  1374. no match is found.
  1375. --*/
  1376. PCSTR
  1377. _mbsistr (PCSTR mbstrStr, PCSTR mbstrSubStr)
  1378. {
  1379. PCSTR mbstrStart, mbstrStrPos, mbstrSubStrPos;
  1380. PCSTR mbstrEnd;
  1381. mbstrEnd = (PSTR) ((LPBYTE) mbstrStr + ByteCountA (mbstrStr) - ByteCountA (mbstrSubStr));
  1382. for (mbstrStart = mbstrStr ; mbstrStart <= mbstrEnd ; mbstrStart = _mbsinc (mbstrStart)) {
  1383. mbstrStrPos = mbstrStart;
  1384. mbstrSubStrPos = mbstrSubStr;
  1385. while (*mbstrSubStrPos &&
  1386. _mbctolower ((MBCHAR) _mbsnextc (mbstrSubStrPos)) == _mbctolower ((MBCHAR) _mbsnextc (mbstrStrPos)))
  1387. {
  1388. mbstrStrPos = _mbsinc (mbstrStrPos);
  1389. mbstrSubStrPos = _mbsinc (mbstrSubStrPos);
  1390. }
  1391. if (!(*mbstrSubStrPos))
  1392. return mbstrStart;
  1393. }
  1394. return NULL;
  1395. }
  1396. PCWSTR
  1397. _wcsistr (PCWSTR wstrStr, PCWSTR wstrSubStr)
  1398. {
  1399. PCWSTR wstrStart, wstrStrPos, wstrSubStrPos;
  1400. PCWSTR wstrEnd;
  1401. wstrEnd = (PWSTR) ((LPBYTE) wstrStr + ByteCountW (wstrStr) - ByteCountW (wstrSubStr));
  1402. for (wstrStart = wstrStr ; wstrStart <= wstrEnd ; wstrStart++) {
  1403. wstrStrPos = wstrStart;
  1404. wstrSubStrPos = wstrSubStr;
  1405. while (*wstrSubStrPos &&
  1406. towlower (*wstrSubStrPos) == towlower (*wstrStrPos))
  1407. {
  1408. wstrStrPos++;
  1409. wstrSubStrPos++;
  1410. }
  1411. if (!(*wstrSubStrPos))
  1412. return wstrStart;
  1413. }
  1414. return NULL;
  1415. }
  1416. /*++
  1417. Routine Description:
  1418. _tcscmpab compares a string against a string between to string pointers
  1419. Arguments:
  1420. String - Specifies the string to compare
  1421. Start - Specifies the start of the string to compare against
  1422. End - Specifies the end of the string to compare against. The character
  1423. pointed to by End is not included in the comparision.
  1424. Return Value:
  1425. Less than zero: String is numerically less than the string between Start and End
  1426. Zero: String matches the string between Start and End identically
  1427. Greater than zero: String is numerically greater than the string between Start and End
  1428. --*/
  1429. INT
  1430. StringCompareABA (
  1431. IN PCSTR String,
  1432. IN PCSTR Start,
  1433. IN PCSTR End
  1434. )
  1435. {
  1436. while (*String && Start < End) {
  1437. if (_mbsnextc (String) != _mbsnextc (Start)) {
  1438. break;
  1439. }
  1440. String = _mbsinc (String);
  1441. Start = _mbsinc (Start);
  1442. }
  1443. if (Start == End && *String == 0) {
  1444. return 0;
  1445. }
  1446. return _mbsnextc (Start) - _mbsnextc (String);
  1447. }
  1448. INT
  1449. StringCompareABW (
  1450. IN PCWSTR String,
  1451. IN PCWSTR Start,
  1452. IN PCWSTR End
  1453. )
  1454. {
  1455. while (*String && Start < End) {
  1456. if (*String != *Start) {
  1457. break;
  1458. }
  1459. String++;
  1460. Start++;
  1461. }
  1462. if (Start == End && *String == 0) {
  1463. return 0;
  1464. }
  1465. return *Start - *String;
  1466. }
  1467. INT
  1468. StringICompareABA (
  1469. IN PCSTR String,
  1470. IN PCSTR Start,
  1471. IN PCSTR End
  1472. )
  1473. {
  1474. while (*String && Start < End) {
  1475. if (tolower (_mbsnextc (String)) != tolower (_mbsnextc (Start))) {
  1476. break;
  1477. }
  1478. String = _mbsinc (String);
  1479. Start = _mbsinc (Start);
  1480. }
  1481. if (Start == End && *String == 0) {
  1482. return 0;
  1483. }
  1484. return tolower (_mbsnextc (Start)) - tolower (_mbsnextc (String));
  1485. }
  1486. INT
  1487. StringICompareABW (
  1488. IN PCWSTR String,
  1489. IN PCWSTR Start,
  1490. IN PCWSTR End
  1491. )
  1492. {
  1493. while (*String && Start < End) {
  1494. if (towlower (*String) != towlower (*Start)) {
  1495. break;
  1496. }
  1497. String++;
  1498. Start++;
  1499. }
  1500. if (Start == End && *String == 0) {
  1501. return 0;
  1502. }
  1503. return towlower (*Start) - towlower (*String);
  1504. }
  1505. void
  1506. _setmbchar (
  1507. IN OUT PSTR Str,
  1508. IN MBCHAR c
  1509. )
  1510. /*++
  1511. Routine Description:
  1512. _setmbchar sets the character at the specified string position, shifting
  1513. bytes if necessary to keep the string in tact.
  1514. Arguments:
  1515. Str - String
  1516. c - Character to set
  1517. Return Value:
  1518. none
  1519. --*/
  1520. {
  1521. if (c < 256) {
  1522. if (IsLeadByte (*Str)) {
  1523. //
  1524. // Delete one byte from the string
  1525. //
  1526. MoveMemory (Str, Str+1, SizeOfStringA (Str+2) + 1);
  1527. }
  1528. *Str = (CHAR)c;
  1529. } else {
  1530. if (!IsLeadByte (*Str)) {
  1531. //
  1532. // Insert one byte in the string
  1533. //
  1534. MoveMemory (Str+1, Str, SizeOfStringA (Str));
  1535. }
  1536. *((WORD *) Str) = (WORD) c;
  1537. }
  1538. }
  1539. /*++
  1540. Routine Description:
  1541. GetNextRuleChar extracts the first character in the *p_szRule string,
  1542. and determines the character value, decoding the ~xx~ syntax (which
  1543. specifies any arbitrary value).
  1544. GetNextRuleChar returns a complete character for SBCS and UNICODE, but
  1545. it may return either a lead byte or non-lead byte for MBCS. To indicate
  1546. a MBCS character, two ~xx~ hex values are needed.
  1547. Arguments:
  1548. p_szRule - A pointer to a pointer; a caller-allocated buffer that
  1549. holds the rule string.
  1550. p_bFromHex - A pointer to a caller-allocated BOOL that receives TRUE
  1551. when the return value was decoded from the <xx> syntax.
  1552. Return Value:
  1553. The decoded character; *p_bFromHex identifies if the return value was
  1554. a literal or was a hex-encoded character.
  1555. --*/
  1556. MBCHAR
  1557. GetNextRuleCharA (
  1558. IN OUT PCSTR *PtrToRule,
  1559. OUT BOOL *FromHex
  1560. )
  1561. {
  1562. MBCHAR ch;
  1563. MBCHAR Value;
  1564. INT i;
  1565. PCSTR StartPtr;
  1566. StartPtr = *PtrToRule;
  1567. if (FromHex) {
  1568. *FromHex = FALSE;
  1569. }
  1570. if (_mbsnextc (StartPtr) == '~') {
  1571. *PtrToRule += 1;
  1572. Value = 0;
  1573. i = 0;
  1574. for (i = 0 ; **PtrToRule && i < 8 ; i++) {
  1575. ch = _mbsnextc (*PtrToRule);
  1576. *PtrToRule += 1;
  1577. if (ch == '~') {
  1578. if (FromHex) {
  1579. *FromHex = TRUE;
  1580. }
  1581. return Value;
  1582. }
  1583. Value *= 16;
  1584. if (ch >= '0' && ch <= '9') {
  1585. Value += ch - '0';
  1586. } else if (ch >= 'a' && ch <= 'f') {
  1587. Value += ch - 'a' + 10;
  1588. } else if (ch >= 'A' && ch <= 'F') {
  1589. Value += ch - 'A' + 10;
  1590. } else {
  1591. break;
  1592. }
  1593. }
  1594. DEBUGMSGA ((DBG_WHOOPS, "Bad formatting in encoded string %s", StartPtr));
  1595. }
  1596. *PtrToRule = _mbsinc (StartPtr);
  1597. return _mbsnextc (StartPtr);
  1598. }
  1599. WCHAR
  1600. GetNextRuleCharW (
  1601. IN OUT PCWSTR *PtrToRule,
  1602. OUT BOOL *FromHex
  1603. )
  1604. {
  1605. WCHAR ch;
  1606. WCHAR Value;
  1607. INT i;
  1608. PCWSTR StartPtr;
  1609. StartPtr = *PtrToRule;
  1610. if (FromHex) {
  1611. *FromHex = FALSE;
  1612. }
  1613. if (*StartPtr == L'~') {
  1614. *PtrToRule += 1;
  1615. Value = 0;
  1616. i = 0;
  1617. for (i = 0 ; **PtrToRule && i < 8 ; i++) {
  1618. ch = **PtrToRule;
  1619. *PtrToRule += 1;
  1620. if (ch == L'~') {
  1621. if (FromHex) {
  1622. *FromHex = TRUE;
  1623. }
  1624. return Value;
  1625. }
  1626. Value *= 16;
  1627. if (ch >= L'0' && ch <= L'9') {
  1628. Value += ch - L'0';
  1629. } else if (ch >= L'a' && ch <= L'f') {
  1630. Value += ch - L'a' + 10;
  1631. } else if (ch >= L'A' && ch <= L'F') {
  1632. Value += ch - L'A' + 10;
  1633. } else {
  1634. break;
  1635. }
  1636. }
  1637. DEBUGMSGW ((DBG_WHOOPS, "Bad formatting in encoded string %s", StartPtr));
  1638. }
  1639. *PtrToRule = StartPtr + 1;
  1640. return *StartPtr;
  1641. }
  1642. /*++
  1643. Routine Description:
  1644. DecodeRuleChars takes a complete rule string (szRule), possibly
  1645. encoded with hex-specified character values (~xx~). The output
  1646. string contains unencoded characters.
  1647. Arguments:
  1648. szRule - A caller-allocated buffer, big enough to hold an
  1649. unencoded rule. szRule can be equal to szEncRule.
  1650. szEncRule - The string holding a possibly encoded string.
  1651. Return Value:
  1652. Equal to szRule.
  1653. --*/
  1654. PSTR
  1655. DecodeRuleCharsA (PSTR mbstrRule, PCSTR mbstrEncRule)
  1656. {
  1657. MBCHAR c;
  1658. PSTR mbstrOrgRule;
  1659. mbstrOrgRule = mbstrRule;
  1660. //
  1661. // Copy string, converting ~xx~ to a single char
  1662. //
  1663. do {
  1664. c = GetNextRuleCharA (&mbstrEncRule, NULL);
  1665. *mbstrRule = (CHAR)c;
  1666. mbstrRule++; // MBCS->incomplete char will be finished in next loop iteration
  1667. } while (c);
  1668. return mbstrOrgRule;
  1669. }
  1670. PWSTR
  1671. DecodeRuleCharsW (PWSTR wstrRule, PCWSTR wstrEncRule)
  1672. {
  1673. WCHAR c;
  1674. PWSTR wstrOrgRule;
  1675. wstrOrgRule = wstrRule;
  1676. //
  1677. // Copy string, converting ~xx~ to a single char
  1678. //
  1679. do {
  1680. c = GetNextRuleCharW (&wstrEncRule, NULL);
  1681. *wstrRule = c;
  1682. wstrRule++;
  1683. } while (c);
  1684. return wstrOrgRule;
  1685. }
  1686. PSTR
  1687. DecodeRuleCharsABA (PSTR mbstrRule, PCSTR mbstrEncRule, PCSTR End)
  1688. {
  1689. MBCHAR c;
  1690. PSTR mbstrOrgRule;
  1691. mbstrOrgRule = mbstrRule;
  1692. //
  1693. // Copy string, converting ~xx~ to a single char
  1694. //
  1695. while (mbstrEncRule < End) {
  1696. c = GetNextRuleCharA (&mbstrEncRule, NULL);
  1697. *mbstrRule = (CHAR)c;
  1698. mbstrRule++; // MBCS->incomplete char will be finished in next loop iteration
  1699. }
  1700. *mbstrRule = 0;
  1701. return mbstrOrgRule;
  1702. }
  1703. PWSTR
  1704. DecodeRuleCharsABW (PWSTR wstrRule, PCWSTR wstrEncRule, PCWSTR End)
  1705. {
  1706. WCHAR c;
  1707. PWSTR wstrOrgRule;
  1708. wstrOrgRule = wstrRule;
  1709. //
  1710. // Copy string, converting ~xx~ to a single char
  1711. //
  1712. while (wstrEncRule < End) {
  1713. c = GetNextRuleCharW (&wstrEncRule, NULL);
  1714. *wstrRule = c;
  1715. wstrRule++;
  1716. }
  1717. *wstrRule = 0;
  1718. return wstrOrgRule;
  1719. }
  1720. /*++
  1721. Routine Description:
  1722. EncodeRuleChars takes an unencoded rule string (szRule), and
  1723. converts it to a string possibly encoded with hex-specified
  1724. character values (~xx~). The output string contains encoded
  1725. characters.
  1726. Arguments:
  1727. szEncRule - A caller-allocated buffer, big enough to hold an
  1728. encoded rule. szEncRule CAN NOT be equal to szRule.
  1729. One way to calculate a max buffer size for szEncRule
  1730. is to use the following code:
  1731. allocsize = SizeOfString (szRule) * 6;
  1732. In the worst case, each character in szRule will take
  1733. six single-byte characters in szEncRule. In the normal
  1734. case, szEncRule will only be a few bytes bigger than
  1735. szRule.
  1736. szRule - The string holding an unencoded string.
  1737. Return Value:
  1738. Equal to szEncRule.
  1739. --*/
  1740. PSTR
  1741. EncodeRuleCharsA (PSTR mbstrEncRule, PCSTR mbstrRule)
  1742. {
  1743. PSTR mbstrOrgRule;
  1744. static CHAR mbstrExclusions[] = "[]<>\'*$|:?\";,%";
  1745. MBCHAR c;
  1746. mbstrOrgRule = mbstrEncRule;
  1747. while (*mbstrRule) {
  1748. c = _mbsnextc (mbstrRule);
  1749. if ((c > 127) || _mbschr (mbstrExclusions, c)) {
  1750. // Escape unprintable or excluded character
  1751. wsprintfA (mbstrEncRule, "~%X~", c);
  1752. mbstrEncRule = GetEndOfStringA (mbstrEncRule);
  1753. mbstrRule = _mbsinc (mbstrRule);
  1754. }
  1755. else {
  1756. // Copy multibyte character
  1757. if (IsLeadByte (*mbstrRule)) {
  1758. *mbstrEncRule = *mbstrRule;
  1759. mbstrEncRule++;
  1760. mbstrRule++;
  1761. }
  1762. *mbstrEncRule = *mbstrRule;
  1763. mbstrEncRule++;
  1764. mbstrRule++;
  1765. }
  1766. }
  1767. *mbstrEncRule = 0;
  1768. return mbstrOrgRule;
  1769. }
  1770. PWSTR
  1771. EncodeRuleCharsW (PWSTR wstrEncRule, PCWSTR wstrRule)
  1772. {
  1773. PWSTR wstrOrgRule;
  1774. static WCHAR wstrExclusions[] = L"[]<>\'*$|:?\";,%";
  1775. WCHAR c;
  1776. wstrOrgRule = wstrEncRule;
  1777. while (c = *wstrRule) {
  1778. if ((c > 127) || wcschr (wstrExclusions, c)) {
  1779. wsprintfW (wstrEncRule, L"~%X~", c);
  1780. wstrEncRule = GetEndOfStringW (wstrEncRule);
  1781. }
  1782. else {
  1783. *wstrEncRule = *wstrRule;
  1784. wstrEncRule++;
  1785. }
  1786. wstrRule++;
  1787. }
  1788. *wstrEncRule = 0;
  1789. return wstrOrgRule;
  1790. }
  1791. /*++
  1792. Routine Description:
  1793. _tcsisprint is a string version of _istprint.
  1794. Arguments:
  1795. szStr - A pointer to the string to examine
  1796. Return Value:
  1797. Non-zero if szStr is made up only of printable characters.
  1798. --*/
  1799. int
  1800. _mbsisprint (PCSTR mbstrStr)
  1801. {
  1802. while (*mbstrStr && _ismbcprint ((MBCHAR) _mbsnextc (mbstrStr))) {
  1803. mbstrStr = _mbsinc (mbstrStr);
  1804. }
  1805. return *mbstrStr == 0;
  1806. }
  1807. int
  1808. _wcsisprint (PCWSTR wstrStr)
  1809. {
  1810. while (*wstrStr && iswprint (*wstrStr)) {
  1811. wstrStr++;
  1812. }
  1813. return *wstrStr == 0;
  1814. }
  1815. /*++
  1816. Routine Description:
  1817. SkipSpace returns a pointer to the next position within a string
  1818. that does not have whitespace characters. It uses the C
  1819. runtime isspace to determine what a whitespace character is.
  1820. Arguments:
  1821. szStr - A pointer to the string to examine
  1822. Return Value:
  1823. A pointer to the first non-whitespace character in the string,
  1824. or NULL if the string is made up of all whitespace characters
  1825. or the string is empty.
  1826. --*/
  1827. PCSTR
  1828. SkipSpaceA (PCSTR mbstrStr)
  1829. {
  1830. while (_ismbcspace ((MBCHAR) _mbsnextc (mbstrStr)))
  1831. mbstrStr = _mbsinc (mbstrStr);
  1832. return mbstrStr;
  1833. }
  1834. PCWSTR
  1835. SkipSpaceW (PCWSTR wstrStr)
  1836. {
  1837. while (iswspace (*wstrStr))
  1838. wstrStr++;
  1839. return wstrStr;
  1840. }
  1841. /*++
  1842. Routine Description:
  1843. SkipSpaceR returns a pointer to the next position within a string
  1844. that does not have whitespace characters. It uses the C
  1845. runtime isspace to determine what a whitespace character is.
  1846. This function is identical to SkipSpace except it works from
  1847. right to left instead of left to right.
  1848. Arguments:
  1849. StrBase - A pointer to the first character in the string
  1850. Str - A pointer to the end of the string, or NULL if the
  1851. end is not known.
  1852. Return Value:
  1853. A pointer to the first non-whitespace character in the string,
  1854. as viewed from right to left, or NULL if the string is made up
  1855. of all whitespace characters or the string is empty.
  1856. --*/
  1857. PCSTR
  1858. SkipSpaceRA (
  1859. IN PCSTR StrBase,
  1860. IN PCSTR Str OPTIONAL
  1861. )
  1862. {
  1863. if (!Str) {
  1864. Str = GetEndOfStringA (StrBase);
  1865. }
  1866. if (*Str == 0) {
  1867. Str = _mbsdec (StrBase, Str);
  1868. if (!Str) {
  1869. return NULL;
  1870. }
  1871. }
  1872. do {
  1873. if (!_ismbcspace((MBCHAR) _mbsnextc(Str))) {
  1874. return Str;
  1875. }
  1876. } while (Str = _mbsdec(StrBase, Str));
  1877. return NULL;
  1878. }
  1879. PCWSTR
  1880. SkipSpaceRW (
  1881. IN PCWSTR StrBase,
  1882. IN PCWSTR Str OPTIONAL
  1883. )
  1884. {
  1885. if (!Str) {
  1886. Str = GetEndOfStringW (StrBase);
  1887. }
  1888. if (*Str == 0) {
  1889. Str--;
  1890. if (Str < StrBase) {
  1891. return NULL;
  1892. }
  1893. }
  1894. do {
  1895. if (!iswspace(*Str)) {
  1896. return Str;
  1897. }
  1898. } while (Str-- != StrBase);
  1899. return NULL;
  1900. }
  1901. /*++
  1902. Routine Description:
  1903. TruncateTrailingSpace trims the specified string after the
  1904. very last non-space character, or empties the string if it
  1905. contains only space characters. This routine uses isspace
  1906. to determine what a space is.
  1907. Arguments:
  1908. Str - Specifies string to process
  1909. Return Value:
  1910. none
  1911. --*/
  1912. VOID
  1913. TruncateTrailingSpaceA (
  1914. IN OUT PSTR Str
  1915. )
  1916. {
  1917. PSTR LastNonSpace;
  1918. PSTR OrgStr;
  1919. OrgStr = Str;
  1920. LastNonSpace = NULL;
  1921. while (*Str) {
  1922. if (!_ismbcspace ((MBCHAR) _mbsnextc (Str))) {
  1923. LastNonSpace = Str;
  1924. }
  1925. Str = _mbsinc (Str);
  1926. }
  1927. if (LastNonSpace) {
  1928. *_mbsinc (LastNonSpace) = 0;
  1929. } else {
  1930. *OrgStr = 0;
  1931. }
  1932. }
  1933. VOID
  1934. TruncateTrailingSpaceW (
  1935. IN OUT PWSTR Str
  1936. )
  1937. {
  1938. PWSTR LastNonSpace;
  1939. PWSTR OrgStr;
  1940. OrgStr = Str;
  1941. LastNonSpace = NULL;
  1942. while (*Str) {
  1943. if (!iswspace (*Str)) {
  1944. LastNonSpace = Str;
  1945. }
  1946. Str++;
  1947. }
  1948. if (LastNonSpace) {
  1949. *(LastNonSpace + 1) = 0;
  1950. } else {
  1951. *OrgStr = 0;
  1952. }
  1953. }
  1954. /*++
  1955. Routine Description:
  1956. _tcsnzcpy copies bytecount bytes from the source string to the
  1957. destination string, and terminates the string if it needs to
  1958. be truncated. This function is a _tcsncpy, plus a terminating
  1959. nul.
  1960. _tcsnzcpy always requires a destination buffer that can hold
  1961. bytecount + sizeof (TCHAR) bytes.
  1962. Use the _tcssafecpy macros to specify the maximum number of bytes
  1963. to copy, including the nul.
  1964. Arguments:
  1965. dest - The destination buffer that is at least bytecount + sizeof(TCHAR)
  1966. src - The source string
  1967. bytecount - The number of bytes to copy. If src is greater than bytecount,
  1968. the destination string is truncated.
  1969. Return Value:
  1970. A pointer to dest.
  1971. --*/
  1972. PSTR
  1973. _mbsnzcpy (
  1974. PSTR dest,
  1975. PCSTR src,
  1976. INT bytecount
  1977. )
  1978. {
  1979. PSTR realdest;
  1980. realdest = dest;
  1981. while (*src && bytecount >= sizeof (CHAR)) {
  1982. if (IsLeadByte (*src)) {
  1983. if (bytecount == 1) {
  1984. // double char can't fit
  1985. break;
  1986. }
  1987. *dest++ = *src++;
  1988. bytecount--;
  1989. }
  1990. *dest++ = *src++;
  1991. bytecount--;
  1992. }
  1993. *dest = 0;
  1994. return realdest;
  1995. }
  1996. PWSTR
  1997. _wcsnzcpy (
  1998. PWSTR dest,
  1999. PCWSTR src,
  2000. INT bytecount
  2001. )
  2002. {
  2003. PWSTR realdest;
  2004. realdest = dest;
  2005. while (*src && bytecount >= sizeof (WCHAR)) {
  2006. *dest++ = *src++;
  2007. bytecount -= sizeof(WCHAR);
  2008. }
  2009. *dest = 0;
  2010. return realdest;
  2011. }
  2012. /*++
  2013. Routine Description:
  2014. _tcsnzcpyab copies bytecount bytes between two pointers to the
  2015. destination string, and terminates the string if it needs to
  2016. be truncated. This function is a _tcscpyab, plus a terminating
  2017. nul, plus bytecount safety guard.
  2018. _tcsnzcpy always requires a destination buffer that can hold
  2019. bytecount + sizeof (TCHAR) bytes.
  2020. Use the _tcssafecpyab macros to specify the maximum number of bytes
  2021. to copy, including the nul.
  2022. Arguments:
  2023. Dest - The destination buffer that is at least bytecount + sizeof(TCHAR)
  2024. Start - The start of the source string
  2025. End - Points to the character one position past the
  2026. last character to copy in the string pointed to
  2027. by start.
  2028. bytecount - The number of bytes to copy. If src is greater than bytecount,
  2029. the destination string is truncated.
  2030. Return Value:
  2031. A pointer to Dest. Start and End must be pointers within
  2032. the same string, and End must be greater than Start. If
  2033. it isn't, the function will make the string empty.
  2034. --*/
  2035. PSTR
  2036. _mbsnzcpyab (
  2037. PSTR Dest,
  2038. PCSTR Start,
  2039. PCSTR End,
  2040. INT count
  2041. )
  2042. {
  2043. PSTR realdest;
  2044. realdest = Dest;
  2045. while ((Start < End) && count >= sizeof (CHAR)) {
  2046. if (IsLeadByte (*Start)) {
  2047. if (count == 1) {
  2048. // double char can't fit
  2049. break;
  2050. }
  2051. *Dest++ = *Start++;
  2052. count--;
  2053. }
  2054. *Dest++ = *Start++;
  2055. count--;
  2056. }
  2057. *Dest = 0;
  2058. return realdest;
  2059. }
  2060. PWSTR
  2061. _wcsnzcpyab (
  2062. PWSTR Dest,
  2063. PCWSTR Start,
  2064. PCWSTR End,
  2065. INT count
  2066. )
  2067. {
  2068. PWSTR realdest;
  2069. realdest = Dest;
  2070. while ((Start < End) && count >= sizeof (WCHAR)) {
  2071. *Dest++ = *Start++;
  2072. count -= sizeof(WCHAR);
  2073. }
  2074. *Dest = 0;
  2075. return realdest;
  2076. }
  2077. /*++
  2078. Routine Description:
  2079. IsPatternMatch compares a string against a pattern that may contain
  2080. standard * or ? wildcards.
  2081. Arguments:
  2082. wstrPattern - A pattern possibly containing wildcards
  2083. wstrStr - The string to compare against the pattern
  2084. Return Value:
  2085. TRUE when wstrStr and wstrPattern match when wildcards are expanded.
  2086. FALSE if wstrStr does not match wstrPattern.
  2087. --*/
  2088. BOOL
  2089. IsPatternMatchA (
  2090. IN PCSTR strPattern,
  2091. IN PCSTR strStr
  2092. )
  2093. {
  2094. MBCHAR chSrc, chPat;
  2095. while (*strStr) {
  2096. chSrc = _mbctolower ((MBCHAR) _mbsnextc (strStr));
  2097. chPat = _mbctolower ((MBCHAR) _mbsnextc (strPattern));
  2098. if (chPat == '*') {
  2099. // Skip all asterisks that are grouped together
  2100. while (_mbsnextc (_mbsinc (strStr)) == '*') {
  2101. strStr = _mbsinc (strStr);
  2102. }
  2103. // Check if asterisk is at the end. If so, we have a match already.
  2104. if (!_mbsnextc (_mbsinc (strPattern))) {
  2105. return TRUE;
  2106. }
  2107. // do recursive check for rest of pattern
  2108. if (IsPatternMatchA (_mbsinc (strPattern), strStr)) {
  2109. return TRUE;
  2110. }
  2111. // Allow any character and continue
  2112. strStr = _mbsinc (strStr);
  2113. continue;
  2114. }
  2115. if (chPat != '?') {
  2116. if (chSrc != chPat) {
  2117. return FALSE;
  2118. }
  2119. }
  2120. strStr = _mbsinc (strStr);
  2121. strPattern = _mbsinc (strPattern);
  2122. }
  2123. //
  2124. // Fail when there is more pattern and pattern does not end in an asterisk
  2125. //
  2126. while (_mbsnextc (strPattern) == '*') {
  2127. strPattern = _mbsinc (strPattern);
  2128. }
  2129. if (_mbsnextc (strPattern)) {
  2130. return FALSE;
  2131. }
  2132. return TRUE;
  2133. }
  2134. BOOL
  2135. IsPatternMatchW (
  2136. IN PCWSTR wstrPattern,
  2137. IN PCWSTR wstrStr
  2138. )
  2139. {
  2140. WCHAR chSrc, chPat;
  2141. if (wstrPattern[0] == L'*' && wstrPattern[1] == 0) {
  2142. return TRUE;
  2143. }
  2144. while (*wstrStr) {
  2145. chSrc = towlower (*wstrStr);
  2146. chPat = towlower (*wstrPattern);
  2147. if (chPat == L'*') {
  2148. // Skip all asterisks that are grouped together
  2149. while (wstrPattern[1] == L'*')
  2150. wstrPattern++;
  2151. // Check if asterisk is at the end. If so, we have a match already.
  2152. chPat = towlower (wstrPattern[1]);
  2153. if (!chPat)
  2154. return TRUE;
  2155. // Otherwise check if next pattern char matches current char
  2156. if (chPat == chSrc || chPat == L'?') {
  2157. // do recursive check for rest of pattern
  2158. wstrPattern++;
  2159. if (IsPatternMatchW (wstrPattern, wstrStr))
  2160. return TRUE;
  2161. // no, that didn't work, stick with star
  2162. wstrPattern--;
  2163. }
  2164. //
  2165. // Allow any character and continue
  2166. //
  2167. wstrStr++;
  2168. continue;
  2169. }
  2170. if (chPat != L'?') {
  2171. //
  2172. // if next pattern character is not a question mark, src and pat
  2173. // must be identical.
  2174. //
  2175. if (chSrc != chPat)
  2176. return FALSE;
  2177. }
  2178. //
  2179. // Advance when pattern character matches string character
  2180. //
  2181. wstrPattern++;
  2182. wstrStr++;
  2183. }
  2184. //
  2185. // Fail when there is more pattern and pattern does not end in an asterisk
  2186. //
  2187. chPat = *wstrPattern;
  2188. if (chPat && (chPat != L'*' || wstrPattern[1]))
  2189. return FALSE;
  2190. return TRUE;
  2191. }
  2192. /*++
  2193. Routine Description:
  2194. IsPatternMatchAB compares a string against a pattern that may contain
  2195. standard * or ? wildcards. It only processes the string up to the
  2196. specified end.
  2197. Arguments:
  2198. Pattern - A pattern possibly containing wildcards
  2199. Start - The string to compare against the pattern
  2200. End - Specifies the end of Start
  2201. Return Value:
  2202. TRUE when the string between Start and End matches Pattern when wildcards are expanded.
  2203. FALSE if the pattern does not match.
  2204. --*/
  2205. BOOL
  2206. IsPatternMatchABA (
  2207. IN PCSTR Pattern,
  2208. IN PCSTR Start,
  2209. IN PCSTR End
  2210. )
  2211. {
  2212. MBCHAR chSrc, chPat;
  2213. while (*Start && Start < End) {
  2214. chSrc = _mbctolower ((MBCHAR) _mbsnextc (Start));
  2215. chPat = _mbctolower ((MBCHAR) _mbsnextc (Pattern));
  2216. if (chPat == '*') {
  2217. // Skip all asterisks that are grouped together
  2218. while (_mbsnextc (_mbsinc (Start)) == '*') {
  2219. Start = _mbsinc (Start);
  2220. }
  2221. // Check if asterisk is at the end. If so, we have a match already.
  2222. if (!_mbsnextc (_mbsinc (Pattern))) {
  2223. return TRUE;
  2224. }
  2225. // do recursive check for rest of pattern
  2226. if (IsPatternMatchABA (_mbsinc (Pattern), Start, End)) {
  2227. return TRUE;
  2228. }
  2229. // Allow any character and continue
  2230. Start = _mbsinc (Start);
  2231. continue;
  2232. }
  2233. if (chPat != '?') {
  2234. if (chSrc != chPat) {
  2235. return FALSE;
  2236. }
  2237. }
  2238. Start = _mbsinc (Start);
  2239. Pattern = _mbsinc (Pattern);
  2240. }
  2241. //
  2242. // Fail when there is more pattern and pattern does not end in an asterisk
  2243. //
  2244. while (_mbsnextc (Pattern) == '*') {
  2245. Pattern = _mbsinc (Pattern);
  2246. }
  2247. if (_mbsnextc (Pattern)) {
  2248. return FALSE;
  2249. }
  2250. return TRUE;
  2251. }
  2252. BOOL
  2253. IsPatternMatchABW (
  2254. IN PCWSTR Pattern,
  2255. IN PCWSTR Start,
  2256. IN PCWSTR End
  2257. )
  2258. {
  2259. WCHAR chSrc, chPat;
  2260. while (*Start && Start < End) {
  2261. chSrc = towlower (*Start);
  2262. chPat = towlower (*Pattern);
  2263. if (chPat == L'*') {
  2264. // Skip all asterisks that are grouped together
  2265. while (Pattern[1] == L'*') {
  2266. Pattern++;
  2267. }
  2268. // Check if asterisk is at the end. If so, we have a match already.
  2269. chPat = towlower (Pattern[1]);
  2270. if (!chPat) {
  2271. return TRUE;
  2272. }
  2273. // Otherwise check if next pattern char matches current char
  2274. if (chPat == chSrc || chPat == L'?') {
  2275. // do recursive check for rest of pattern
  2276. Pattern++;
  2277. if (IsPatternMatchABW (Pattern, Start, End)) {
  2278. return TRUE;
  2279. }
  2280. // no, that didn't work, stick with star
  2281. Pattern--;
  2282. }
  2283. //
  2284. // Allow any character and continue
  2285. //
  2286. Start++;
  2287. continue;
  2288. }
  2289. if (chPat != L'?') {
  2290. //
  2291. // if next pattern character is not a question mark, src and pat
  2292. // must be identical.
  2293. //
  2294. if (chSrc != chPat) {
  2295. return FALSE;
  2296. }
  2297. }
  2298. //
  2299. // Advance when pattern character matches string character
  2300. //
  2301. Pattern++;
  2302. Start++;
  2303. }
  2304. //
  2305. // Fail when there is more pattern and pattern does not end in an asterisk
  2306. //
  2307. chPat = *Pattern;
  2308. if (chPat && (chPat != L'*' || Pattern[1])) {
  2309. return FALSE;
  2310. }
  2311. return TRUE;
  2312. }
  2313. /*++
  2314. Routine Description:
  2315. IsPatternMatchEx compares a string against a pattern that may contain
  2316. any of the following expressions:
  2317. * - Specifies zero or more characters
  2318. ? - Specifies any one character
  2319. *[set] - Specifies zero or more characters in set
  2320. ?[set] - Specifies any one character in set
  2321. *[n:set] - Specifies zero to n characters in set
  2322. ?[n:set] - Specifies exactly n characters in set
  2323. *[!(set)] - Specifies zero or more characters not in set
  2324. ?[!(set)] - Specifies one character not in set
  2325. *[n:!(set)] - Specifies zero to n characters not in set
  2326. ?[n:!(set)] - Specifies exactly n characters not in set
  2327. *[set1,!(set2)] - Specifies zero or more characters in set1 and
  2328. not in set2. It is assumed that set1 and set2
  2329. overlap.
  2330. ?[set1,!(set2)] - Specifies one character in set1 and not in set2.
  2331. *[n:set1,!(set2)] - Specifies zero to n characters in set1 and not
  2332. in set 2.
  2333. ?[n:set1,!(set2)] - Specifies exactly n characters in set1 and not
  2334. in set 2.
  2335. set, set1 and set2 are specified as follows:
  2336. a - Specifies a single character
  2337. a-b - Specifies a character range
  2338. a,b - Specifies two characters
  2339. a-b,c-d - Specifies two character ranges
  2340. a,b-c - Specifies a single character and a character range
  2341. etc...
  2342. Patterns can be joined by surrounding the entire expression in
  2343. greater than/less than braces.
  2344. Because of the syntax characters, the following characters must be
  2345. escaped by preceeding the character with a caret (^):
  2346. ^? ^[ ^- ^< ^! ^^
  2347. ^* ^] ^: ^> ^,
  2348. Here are some examples:
  2349. To specify any GUID:
  2350. {?[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]}
  2351. To specify a 32-bit hexadecimal number:
  2352. <0x*[8:0-9,a-f]><0*[7:0-9,a-f]h><?[1-9]*[7:0-9,a-f]h>
  2353. Arguments:
  2354. Pattern - A pattern possibly containing wildcards
  2355. Start - The string to compare against the pattern
  2356. End - Specifies the end of Start
  2357. Return Value:
  2358. TRUE when the string between Start and End matches Pattern when wildcards are expanded.
  2359. FALSE if the pattern does not match.
  2360. --*/
  2361. BOOL
  2362. IsPatternMatchExA (
  2363. IN PCSTR Pattern,
  2364. IN PCSTR Start,
  2365. IN PCSTR End
  2366. )
  2367. {
  2368. PPARSEDPATTERNA Handle;
  2369. BOOL b;
  2370. Handle = CreateParsedPatternA (Pattern);
  2371. if (!Handle) {
  2372. return FALSE;
  2373. }
  2374. b = TestParsedPatternABA (Handle, Start, End);
  2375. DestroyParsedPatternA (Handle);
  2376. return b;
  2377. }
  2378. BOOL
  2379. IsPatternMatchExW (
  2380. IN PCWSTR Pattern,
  2381. IN PCWSTR Start,
  2382. IN PCWSTR End
  2383. )
  2384. {
  2385. PPARSEDPATTERNW Handle;
  2386. BOOL b;
  2387. Handle = CreateParsedPatternW (Pattern);
  2388. if (!Handle) {
  2389. return FALSE;
  2390. }
  2391. b = TestParsedPatternABW (Handle, Start, End);
  2392. DestroyParsedPatternW (Handle);
  2393. return b;
  2394. }
  2395. /*++
  2396. Routine Description:
  2397. pAppendCharToGrowBuffer copies the first character in a caller specified
  2398. string into the specified grow buffer. This function is used to build up a
  2399. string inside a grow buffer, copying character by character.
  2400. Arguments:
  2401. Buf - Specifies the grow buffer to add the character to, receives the
  2402. character in its buffer
  2403. PtrToChar - Specifies a pointer to the character to copy
  2404. Return Value:
  2405. None.
  2406. --*/
  2407. VOID
  2408. pAppendCharToGrowBufferA (
  2409. IN OUT PGROWBUFFER Buf,
  2410. IN PCSTR PtrToChar
  2411. )
  2412. {
  2413. PBYTE p;
  2414. UINT Len;
  2415. if (IsLeadByte (*PtrToChar) && PtrToChar[1]) {
  2416. Len = 2;
  2417. } else {
  2418. Len = 1;
  2419. }
  2420. p = GrowBuffer (Buf, Len);
  2421. CopyMemory (p, PtrToChar, Len);
  2422. }
  2423. VOID
  2424. pAppendCharToGrowBufferW (
  2425. IN OUT PGROWBUFFER Buf,
  2426. IN PCWSTR PtrToChar
  2427. )
  2428. {
  2429. PBYTE p;
  2430. p = GrowBuffer (Buf, sizeof(WCHAR));
  2431. CopyMemory (p, PtrToChar, sizeof(WCHAR));
  2432. }
  2433. /*++
  2434. Routine Description:
  2435. CreateParsedPattern parses the expanded pattern string into a set of
  2436. structures. Parsing is considered expensive relative to testing the
  2437. pattern, so callers should avoid calling this function inside loops. See
  2438. IsPatternMatchEx for a good description of the pattern string syntax.
  2439. Arguments:
  2440. Pattern - Specifies the pattern string, which can include the extended
  2441. wildcard syntax.
  2442. Return Value:
  2443. A pointer to a parsed pattern structure, which the caller will use like a
  2444. handle, or NULL if a syntax error occurred.
  2445. --*/
  2446. PPARSEDPATTERNA
  2447. CreateParsedPatternA (
  2448. IN PCSTR Pattern
  2449. )
  2450. {
  2451. POOLHANDLE Pool;
  2452. PPARSEDPATTERNA Struct;
  2453. PATTERNSTATE State;
  2454. BOOL CompoundPattern = FALSE;
  2455. GROWBUFFER ExactMatchBuf = GROWBUF_INIT;
  2456. GROWBUFFER SegmentArray = GROWBUF_INIT;
  2457. GROWBUFFER PatternArray = GROWBUF_INIT;
  2458. GROWBUFFER SetBuf = GROWBUF_INIT;
  2459. PPATTERNPROPSA CurrentPattern;
  2460. MBCHAR ch = 0;
  2461. PCSTR LookAhead;
  2462. PCSTR SetBegin = NULL;
  2463. PATTERNSTATE ReturnState = 0;
  2464. SEGMENTA Segment;
  2465. PSEGMENTA SegmentElement;
  2466. UINT MaxLen;
  2467. Segment.Type = SEGMENTTYPE_UNKNOWN;
  2468. Pool = PoolMemInitNamedPool ("Parsed Pattern");
  2469. Struct = (PPARSEDPATTERNA) PoolMemGetAlignedMemory (Pool, sizeof (PARSEDPATTERNA));
  2470. ZeroMemory (Struct, sizeof (PARSEDPATTERNA));
  2471. State = BEGIN_PATTERN;
  2472. for (;;) {
  2473. switch (State) {
  2474. case BEGIN_PATTERN:
  2475. //
  2476. // Here we test for either a compound pattern (one that
  2477. // is a brace-separated list), or a simple pattern (one
  2478. // that does not have a brace).
  2479. //
  2480. if (_mbsnextc (Pattern) == '<') {
  2481. CompoundPattern = TRUE;
  2482. State = BEGIN_COMPOUND_PATTERN;
  2483. } else if (*Pattern) {
  2484. State = BEGIN_PATTERN_EXPR;
  2485. } else {
  2486. State = PATTERN_DONE;
  2487. }
  2488. break;
  2489. case BEGIN_COMPOUND_PATTERN:
  2490. //
  2491. // We are looking for the start of a compound pattern.
  2492. // Space is allowed inbetween the patterns, but not
  2493. // at the start.
  2494. //
  2495. while (isspace (_mbsnextc (Pattern))) {
  2496. Pattern = _mbsinc (Pattern);
  2497. }
  2498. if (*Pattern == 0) {
  2499. State = PATTERN_DONE;
  2500. break;
  2501. }
  2502. if (_mbsnextc (Pattern) == '<') {
  2503. Pattern = _mbsinc (Pattern);
  2504. State = BEGIN_PATTERN_EXPR;
  2505. } else {
  2506. DEBUGMSGA ((DBG_ERROR, "Syntax error in pattern: %s", Pattern));
  2507. State = PATTERN_ERROR;
  2508. }
  2509. break;
  2510. case BEGIN_PATTERN_EXPR:
  2511. //
  2512. // We are now ready to condense the expression.
  2513. //
  2514. State = PARSE_CHAR_EXPR_OR_END;
  2515. ExactMatchBuf.End = 0;
  2516. SegmentArray.End = 0;
  2517. break;
  2518. case PARSE_END_FOUND:
  2519. State = END_PATTERN_EXPR;
  2520. if (ExactMatchBuf.End) {
  2521. ReturnState = State;
  2522. State = SAVE_EXACT_MATCH;
  2523. }
  2524. break;
  2525. case END_PATTERN_EXPR:
  2526. //
  2527. // Copy the segment array into the pool, reference the copy
  2528. // in the pattern array
  2529. //
  2530. if (SegmentArray.End) {
  2531. CurrentPattern = (PPATTERNPROPSA) GrowBuffer (&PatternArray, sizeof (PATTERNPROPSA));
  2532. CurrentPattern->Segment = (PSEGMENTA) PoolMemGetAlignedMemory (Pool, SegmentArray.End);
  2533. CurrentPattern->SegmentCount = SegmentArray.End / sizeof (SEGMENTA);
  2534. CopyMemory (
  2535. CurrentPattern->Segment,
  2536. SegmentArray.Buf,
  2537. SegmentArray.End
  2538. );
  2539. }
  2540. if (CompoundPattern && *Pattern) {
  2541. State = BEGIN_COMPOUND_PATTERN;
  2542. } else {
  2543. State = PATTERN_DONE;
  2544. }
  2545. break;
  2546. case PARSE_CHAR_EXPR_OR_END:
  2547. //
  2548. // We now accept the following:
  2549. //
  2550. // 1. The end of the string or end of a compound pattern
  2551. // 2. An escaped character
  2552. // 3. The start of an expression
  2553. // 4. A non-syntax character
  2554. //
  2555. ch = _mbsnextc (Pattern);
  2556. if (ch == '>' && CompoundPattern) {
  2557. //
  2558. // Case 1, we found the end of a compound pattern
  2559. //
  2560. Pattern = _mbsinc (Pattern);
  2561. State = PARSE_END_FOUND;
  2562. break;
  2563. }
  2564. if (*Pattern == 0) {
  2565. //
  2566. // Case 1, we found the end of the pattern
  2567. //
  2568. if (CompoundPattern) {
  2569. State = PATTERN_ERROR;
  2570. } else {
  2571. State = PARSE_END_FOUND;
  2572. }
  2573. break;
  2574. }
  2575. if (ch == '^') {
  2576. //
  2577. // Case 2, we found an escaped character, so transfer
  2578. // it to the buffer.
  2579. //
  2580. MYASSERT (
  2581. Segment.Type == SEGMENTTYPE_UNKNOWN ||
  2582. Segment.Type == SEGMENTTYPE_EXACTMATCH
  2583. );
  2584. Segment.Type = SEGMENTTYPE_EXACTMATCH;
  2585. Pattern = _mbsinc (Pattern);
  2586. pAppendCharToGrowBufferA (&ExactMatchBuf, Pattern);
  2587. Pattern = _mbsinc (Pattern);
  2588. break;
  2589. }
  2590. if (ch == '*' || ch == '?') {
  2591. //
  2592. // Case 3, we found an expression. Save the wildcard type
  2593. // and parse the optional args.
  2594. //
  2595. if (ExactMatchBuf.End) {
  2596. State = SAVE_EXACT_MATCH;
  2597. ReturnState = PARSE_CHAR_EXPR_OR_END;
  2598. break;
  2599. }
  2600. ZeroMemory (&Segment, sizeof (Segment));
  2601. if (ch == '*') {
  2602. Segment.Type = SEGMENTTYPE_OPTIONAL;
  2603. } else {
  2604. Segment.Type = SEGMENTTYPE_REQUIRED;
  2605. Segment.Wildcard.MaxLen = 1;
  2606. }
  2607. Pattern = _mbsinc (Pattern);
  2608. if (_mbsnextc (Pattern) == '[') {
  2609. Pattern = _mbsinc (Pattern);
  2610. State = LOOK_FOR_NUMBER;
  2611. } else {
  2612. ReturnState = PARSE_CHAR_EXPR_OR_END;
  2613. State = SAVE_SEGMENT;
  2614. }
  2615. break;
  2616. }
  2617. //
  2618. // Case 4, we don't know about this character, so just copy it
  2619. // and continue parsing.
  2620. //
  2621. pAppendCharToGrowBufferA (&ExactMatchBuf, Pattern);
  2622. Pattern = _mbsinc (Pattern);
  2623. break;
  2624. case SAVE_EXACT_MATCH:
  2625. //
  2626. // Put the string in ExactMatchBuf into a segment struct
  2627. //
  2628. pAppendCharToGrowBufferA (&ExactMatchBuf, "");
  2629. Segment.Exact.LowerCasePhrase = PoolMemDuplicateStringA (
  2630. Pool,
  2631. (PCSTR) ExactMatchBuf.Buf
  2632. );
  2633. Segment.Exact.PhraseBytes = ExactMatchBuf.End - sizeof (CHAR);
  2634. MYASSERT (Segment.Exact.LowerCasePhrase);
  2635. _mbslwr ((PSTR) Segment.Exact.LowerCasePhrase);
  2636. Segment.Type = SEGMENTTYPE_EXACTMATCH;
  2637. ExactMatchBuf.End = 0;
  2638. // FALL THROUGH!!
  2639. case SAVE_SEGMENT:
  2640. //
  2641. // Put the segment element into the segment array
  2642. //
  2643. SegmentElement = (PSEGMENTA) GrowBuffer (&SegmentArray, sizeof (SEGMENTA));
  2644. CopyMemory (SegmentElement, &Segment, sizeof (SEGMENTA));
  2645. Segment.Type = SEGMENTTYPE_UNKNOWN;
  2646. State = ReturnState;
  2647. break;
  2648. case LOOK_FOR_NUMBER:
  2649. //
  2650. // Here we are inside a bracket, and there is an optional
  2651. // numeric arg, which must be followed by a colon. Test
  2652. // that here.
  2653. //
  2654. LookAhead = Pattern;
  2655. MaxLen = 0;
  2656. while (*LookAhead >= '0' && *LookAhead <= '9') {
  2657. MaxLen = MaxLen * 10 + (*LookAhead - '0');
  2658. LookAhead++;
  2659. }
  2660. if (LookAhead > Pattern && _mbsnextc (LookAhead) == ':') {
  2661. Pattern = _mbsinc (LookAhead);
  2662. //
  2663. // Check for special case syntax error: ?[0:]
  2664. //
  2665. if (Segment.Type == SEGMENTTYPE_EXACTMATCH && !MaxLen) {
  2666. State = PATTERN_ERROR;
  2667. break;
  2668. }
  2669. Segment.Wildcard.MaxLen = MaxLen;
  2670. }
  2671. SetBegin = Pattern;
  2672. State = LOOK_FOR_INCLUDE;
  2673. SetBuf.End = 0;
  2674. break;
  2675. case LOOK_FOR_INCLUDE:
  2676. //
  2677. // Here we are inside a bracket, past an optional numeric
  2678. // arg. Now we look for all the include sets, which are
  2679. // optional. We have the following possibilities:
  2680. //
  2681. // 1. End of set
  2682. // 2. An exclude set that needs to be skipped
  2683. // 3. A valid include set
  2684. // 4. Error
  2685. //
  2686. // We look at SetBegin, and not Pattern.
  2687. //
  2688. MYASSERT (SetBegin);
  2689. ch = _mbsnextc (SetBegin);
  2690. if (ch == ']') {
  2691. //
  2692. // Case 1: end of set
  2693. //
  2694. if (SetBuf.End) {
  2695. pAppendCharToGrowBufferA (&SetBuf, "");
  2696. Segment.Wildcard.IncludeSet = PoolMemDuplicateStringA (
  2697. Pool,
  2698. (PCSTR) SetBuf.Buf
  2699. );
  2700. _mbslwr ((PSTR) Segment.Wildcard.IncludeSet);
  2701. } else {
  2702. Segment.Wildcard.IncludeSet = NULL;
  2703. }
  2704. SetBuf.End = 0;
  2705. State = LOOK_FOR_EXCLUDE;
  2706. SetBegin = Pattern;
  2707. break;
  2708. }
  2709. if (ch == '!') {
  2710. //
  2711. // Case 2: an exclude set
  2712. //
  2713. SetBegin = _mbsinc (SetBegin);
  2714. State = SKIP_EXCLUDE_SET;
  2715. ReturnState = LOOK_FOR_INCLUDE;
  2716. break;
  2717. }
  2718. if (*SetBegin == 0) {
  2719. State = PATTERN_ERROR;
  2720. break;
  2721. }
  2722. //
  2723. // Case 3: a valid include set.
  2724. //
  2725. State = CONDENSE_SET;
  2726. ReturnState = LOOK_FOR_INCLUDE;
  2727. break;
  2728. case LOOK_FOR_EXCLUDE:
  2729. //
  2730. // Here we are inside a bracket, past an optional numeric
  2731. // arg. All include sets are in the condensing buffer.
  2732. // Now we look for all the exclude sets, which are
  2733. // optional. We have the following possibilities:
  2734. //
  2735. // 1. End of set
  2736. // 2. A valid exclude set
  2737. // 3. An include set that needs to be skipped
  2738. // 4. Error
  2739. //
  2740. // We look at SetBegin, and not Pattern.
  2741. //
  2742. ch = _mbsnextc (SetBegin);
  2743. if (ch == ']') {
  2744. //
  2745. // Case 1: end of set; we're done with this expr
  2746. //
  2747. if (SetBuf.End) {
  2748. pAppendCharToGrowBufferA (&SetBuf, "");
  2749. Segment.Wildcard.ExcludeSet = PoolMemDuplicateStringA (
  2750. Pool,
  2751. (PCSTR) SetBuf.Buf
  2752. );
  2753. _mbslwr ((PSTR) Segment.Wildcard.ExcludeSet);
  2754. } else {
  2755. Segment.Wildcard.ExcludeSet = NULL;
  2756. }
  2757. SetBuf.End = 0;
  2758. State = SAVE_SEGMENT;
  2759. ReturnState = PARSE_CHAR_EXPR_OR_END;
  2760. Pattern = _mbsinc (SetBegin);
  2761. break;
  2762. }
  2763. if (ch == '!') {
  2764. //
  2765. // Case 2: a valid exclude set; save it
  2766. //
  2767. SetBegin = _mbsinc (SetBegin);
  2768. if (_mbsnextc (SetBegin) != '(') {
  2769. State = PATTERN_ERROR;
  2770. break;
  2771. }
  2772. SetBegin = _mbsinc (SetBegin);
  2773. State = CONDENSE_SET;
  2774. ReturnState = LOOK_FOR_EXCLUDE;
  2775. break;
  2776. }
  2777. if (*SetBegin == 0) {
  2778. State = PATTERN_ERROR;
  2779. break;
  2780. }
  2781. //
  2782. // Case 3: an include set that needs to be skipped.
  2783. //
  2784. State = SKIP_INCLUDE_SET;
  2785. ReturnState = LOOK_FOR_EXCLUDE;
  2786. break;
  2787. case CONDENSE_SET:
  2788. //
  2789. // Here SetBegin points to a set range, and it is our
  2790. // job to copy the range into the set buffer, and
  2791. // return back to the previous state.
  2792. //
  2793. //
  2794. // Copy the character at SetBegin
  2795. //
  2796. if (_mbsnextc (SetBegin) == '^') {
  2797. SetBegin = _mbsinc (SetBegin);
  2798. if (*SetBegin == 0) {
  2799. State = PATTERN_ERROR;
  2800. break;
  2801. }
  2802. }
  2803. pAppendCharToGrowBufferA (&SetBuf, SetBegin);
  2804. //
  2805. // Check if this is a range or not
  2806. //
  2807. LookAhead = _mbsinc (SetBegin);
  2808. if (_mbsnextc (LookAhead) == '-') {
  2809. //
  2810. // Range, copy the character after the dash
  2811. //
  2812. SetBegin = _mbsinc (LookAhead);
  2813. if (*SetBegin == 0) {
  2814. State = PATTERN_ERROR;
  2815. break;
  2816. }
  2817. if (_mbsnextc (SetBegin) == '^') {
  2818. SetBegin = _mbsinc (SetBegin);
  2819. if (*SetBegin == 0) {
  2820. State = PATTERN_ERROR;
  2821. break;
  2822. }
  2823. }
  2824. pAppendCharToGrowBufferA (&SetBuf, SetBegin);
  2825. } else {
  2826. //
  2827. // A single character, copy the character again
  2828. //
  2829. pAppendCharToGrowBufferA (&SetBuf, SetBegin);
  2830. }
  2831. SetBegin = _mbsinc (SetBegin);
  2832. ch = _mbsnextc (SetBegin);
  2833. //
  2834. // If this is an exclude set, we must have a closing paren
  2835. // or a comma
  2836. //
  2837. State = ReturnState;
  2838. if (ReturnState == LOOK_FOR_EXCLUDE) {
  2839. if (ch == ')') {
  2840. SetBegin = _mbsinc (SetBegin);
  2841. ch = _mbsnextc (SetBegin);
  2842. } else if (ch != ',') {
  2843. State = PATTERN_ERROR;
  2844. } else {
  2845. //
  2846. // Continue condensing the next part of this exclude set
  2847. //
  2848. State = CONDENSE_SET;
  2849. }
  2850. }
  2851. //
  2852. // We either need a comma or a close brace
  2853. //
  2854. if (ch == ',') {
  2855. SetBegin = _mbsinc (SetBegin);
  2856. } else if (ch != ']') {
  2857. State = PATTERN_ERROR;
  2858. }
  2859. break;
  2860. case SKIP_EXCLUDE_SET:
  2861. //
  2862. // Skip over the parenthesis group, assuming it is syntatically
  2863. // correct, and return to the previous state.
  2864. //
  2865. if (_mbsnextc (SetBegin) != '(') {
  2866. State = PATTERN_ERROR;
  2867. break;
  2868. }
  2869. SetBegin = _mbsinc (SetBegin);
  2870. while (*SetBegin) {
  2871. if (_mbsnextc (SetBegin) == '^') {
  2872. SetBegin = _mbsinc (SetBegin);
  2873. } else if (_mbsnextc (SetBegin) == ')') {
  2874. break;
  2875. }
  2876. if (IsLeadByte (SetBegin[0]) && SetBegin[1]) {
  2877. SetBegin += 2;
  2878. } else {
  2879. SetBegin += 1;
  2880. }
  2881. }
  2882. if (*SetBegin == 0) {
  2883. State = PATTERN_ERROR;
  2884. break;
  2885. }
  2886. SetBegin = _mbsinc (SetBegin);
  2887. //
  2888. // Now we are either at a comma or a close brace
  2889. //
  2890. ch = _mbsnextc (SetBegin);
  2891. State = ReturnState;
  2892. if (ch == ',') {
  2893. SetBegin = _mbsinc (SetBegin);
  2894. } else if (ch != ']') {
  2895. State = PATTERN_ERROR;
  2896. }
  2897. break;
  2898. case SKIP_INCLUDE_SET:
  2899. //
  2900. // Skip to the next comma or closing brace. We know it is
  2901. // syntatically correct by now.
  2902. //
  2903. ch = 0;
  2904. while (*SetBegin) {
  2905. ch = _mbsnextc (SetBegin);
  2906. if (ch == '^') {
  2907. SetBegin = _mbsinc (SetBegin);
  2908. } else if (ch == ',' || ch == ']') {
  2909. break;
  2910. }
  2911. SetBegin = _mbsinc (SetBegin);
  2912. }
  2913. MYASSERT (*SetBegin);
  2914. if (ch == ',') {
  2915. SetBegin = _mbsinc (SetBegin);
  2916. }
  2917. State = ReturnState;
  2918. break;
  2919. }
  2920. if (State == PATTERN_DONE || State == PATTERN_ERROR) {
  2921. break;
  2922. }
  2923. }
  2924. FreeGrowBuffer (&ExactMatchBuf);
  2925. FreeGrowBuffer (&SetBuf);
  2926. FreeGrowBuffer (&SegmentArray);
  2927. if (State == PATTERN_ERROR || PatternArray.End == 0) {
  2928. FreeGrowBuffer (&PatternArray);
  2929. PoolMemDestroyPool (Pool);
  2930. return NULL;
  2931. }
  2932. //
  2933. // Copy the fully parsed pattern array into the return struct
  2934. //
  2935. Struct->Pattern = (PPATTERNPROPSA) PoolMemGetAlignedMemory (
  2936. Pool,
  2937. PatternArray.End
  2938. );
  2939. CopyMemory (Struct->Pattern, PatternArray.Buf, PatternArray.End);
  2940. Struct->PatternCount = PatternArray.End / sizeof (PATTERNPROPSA);
  2941. Struct->Pool = Pool;
  2942. FreeGrowBuffer (&PatternArray);
  2943. return Struct;
  2944. }
  2945. PPARSEDPATTERNW
  2946. CreateParsedPatternW (
  2947. IN PCWSTR Pattern
  2948. )
  2949. {
  2950. POOLHANDLE Pool;
  2951. PPARSEDPATTERNW Struct;
  2952. PATTERNSTATE State;
  2953. BOOL CompoundPattern = FALSE;
  2954. GROWBUFFER ExactMatchBuf = GROWBUF_INIT;
  2955. GROWBUFFER SegmentArray = GROWBUF_INIT;
  2956. GROWBUFFER PatternArray = GROWBUF_INIT;
  2957. GROWBUFFER SetBuf = GROWBUF_INIT;
  2958. PPATTERNPROPSW CurrentPattern;
  2959. WCHAR ch = 0;
  2960. PCWSTR LookAhead;
  2961. PCWSTR SetBegin = NULL;
  2962. PATTERNSTATE ReturnState = 0;
  2963. SEGMENTW Segment;
  2964. PSEGMENTW SegmentElement;
  2965. UINT MaxLen;
  2966. Segment.Type = SEGMENTTYPE_UNKNOWN;
  2967. Pool = PoolMemInitNamedPool ("Parsed Pattern");
  2968. Struct = (PPARSEDPATTERNW) PoolMemGetAlignedMemory (Pool, sizeof (PARSEDPATTERNW));
  2969. ZeroMemory (Struct, sizeof (PARSEDPATTERNW));
  2970. State = BEGIN_PATTERN;
  2971. for (;;) {
  2972. switch (State) {
  2973. case BEGIN_PATTERN:
  2974. //
  2975. // Here we test for either a compound pattern (one that
  2976. // is a brace-separated list), or a simple pattern (one
  2977. // that does not have a brace).
  2978. //
  2979. if (*Pattern == L'<') {
  2980. CompoundPattern = TRUE;
  2981. State = BEGIN_COMPOUND_PATTERN;
  2982. } else if (*Pattern) {
  2983. State = BEGIN_PATTERN_EXPR;
  2984. } else {
  2985. State = PATTERN_DONE;
  2986. }
  2987. break;
  2988. case BEGIN_COMPOUND_PATTERN:
  2989. //
  2990. // We are looking for the start of a compound pattern.
  2991. // Space is allowed inbetween the patterns, but not
  2992. // at the start.
  2993. //
  2994. while (iswspace (*Pattern)) {
  2995. Pattern++;
  2996. }
  2997. if (*Pattern == 0) {
  2998. State = PATTERN_DONE;
  2999. break;
  3000. }
  3001. if (*Pattern == L'<') {
  3002. Pattern++;
  3003. State = BEGIN_PATTERN_EXPR;
  3004. } else {
  3005. DEBUGMSGW ((DBG_ERROR, "Syntax error in pattern: %s", Pattern));
  3006. State = PATTERN_ERROR;
  3007. }
  3008. break;
  3009. case BEGIN_PATTERN_EXPR:
  3010. //
  3011. // We are now ready to condense the expression.
  3012. //
  3013. State = PARSE_CHAR_EXPR_OR_END;
  3014. ExactMatchBuf.End = 0;
  3015. SegmentArray.End = 0;
  3016. break;
  3017. case PARSE_END_FOUND:
  3018. State = END_PATTERN_EXPR;
  3019. if (ExactMatchBuf.End) {
  3020. ReturnState = State;
  3021. State = SAVE_EXACT_MATCH;
  3022. }
  3023. break;
  3024. case END_PATTERN_EXPR:
  3025. //
  3026. // Copy the segment array into the pool, reference the copy
  3027. // in the pattern array
  3028. //
  3029. if (SegmentArray.End) {
  3030. CurrentPattern = (PPATTERNPROPSW) GrowBuffer (&PatternArray, sizeof (PATTERNPROPSW));
  3031. CurrentPattern->Segment = (PSEGMENTW) PoolMemGetAlignedMemory (Pool, SegmentArray.End);
  3032. CurrentPattern->SegmentCount = SegmentArray.End / sizeof (SEGMENTW);
  3033. CopyMemory (
  3034. CurrentPattern->Segment,
  3035. SegmentArray.Buf,
  3036. SegmentArray.End
  3037. );
  3038. }
  3039. if (CompoundPattern && *Pattern) {
  3040. State = BEGIN_COMPOUND_PATTERN;
  3041. } else {
  3042. State = PATTERN_DONE;
  3043. }
  3044. break;
  3045. case PARSE_CHAR_EXPR_OR_END:
  3046. //
  3047. // We now accept the following:
  3048. //
  3049. // 1. The end of the string or end of a compound pattern
  3050. // 2. An escaped character
  3051. // 3. The start of an expression
  3052. // 4. A non-syntax character
  3053. //
  3054. ch = *Pattern;
  3055. if (ch == L'>' && CompoundPattern) {
  3056. //
  3057. // Case 1, we found the end of a compound pattern
  3058. //
  3059. Pattern++;
  3060. State = PARSE_END_FOUND;
  3061. break;
  3062. }
  3063. if (*Pattern == 0) {
  3064. //
  3065. // Case 1, we found the end of the pattern
  3066. //
  3067. if (CompoundPattern) {
  3068. State = PATTERN_ERROR;
  3069. } else {
  3070. State = PARSE_END_FOUND;
  3071. }
  3072. break;
  3073. }
  3074. if (ch == L'^') {
  3075. //
  3076. // Case 2, we found an escaped character, so transfer
  3077. // it to the buffer.
  3078. //
  3079. MYASSERT (
  3080. Segment.Type == SEGMENTTYPE_UNKNOWN ||
  3081. Segment.Type == SEGMENTTYPE_EXACTMATCH
  3082. );
  3083. Segment.Type = SEGMENTTYPE_EXACTMATCH;
  3084. Pattern++;
  3085. pAppendCharToGrowBufferW (&ExactMatchBuf, Pattern);
  3086. Pattern++;
  3087. break;
  3088. }
  3089. if (ch == L'*' || ch == L'?') {
  3090. //
  3091. // Case 3, we found an expression. Save the wildcard type
  3092. // and parse the optional args.
  3093. //
  3094. if (ExactMatchBuf.End) {
  3095. State = SAVE_EXACT_MATCH;
  3096. ReturnState = PARSE_CHAR_EXPR_OR_END;
  3097. break;
  3098. }
  3099. ZeroMemory (&Segment, sizeof (Segment));
  3100. if (ch == L'*') {
  3101. Segment.Type = SEGMENTTYPE_OPTIONAL;
  3102. } else {
  3103. Segment.Type = SEGMENTTYPE_REQUIRED;
  3104. Segment.Wildcard.MaxLen = 1;
  3105. }
  3106. Pattern++;
  3107. if (*Pattern == L'[') {
  3108. Pattern++;
  3109. State = LOOK_FOR_NUMBER;
  3110. } else {
  3111. ReturnState = PARSE_CHAR_EXPR_OR_END;
  3112. State = SAVE_SEGMENT;
  3113. }
  3114. break;
  3115. }
  3116. //
  3117. // Case 4, we don't know about this character, so just copy it
  3118. // and continue parsing.
  3119. //
  3120. pAppendCharToGrowBufferW (&ExactMatchBuf, Pattern);
  3121. Pattern++;
  3122. break;
  3123. case SAVE_EXACT_MATCH:
  3124. //
  3125. // Put the string in ExactMatchBuf into a segment struct
  3126. //
  3127. pAppendCharToGrowBufferW (&ExactMatchBuf, L"");
  3128. Segment.Exact.LowerCasePhrase = PoolMemDuplicateStringW (
  3129. Pool,
  3130. (PCWSTR) ExactMatchBuf.Buf
  3131. );
  3132. Segment.Exact.PhraseBytes = ExactMatchBuf.End - sizeof (WCHAR);
  3133. MYASSERT (Segment.Exact.LowerCasePhrase);
  3134. _wcslwr ((PWSTR) Segment.Exact.LowerCasePhrase);
  3135. Segment.Type = SEGMENTTYPE_EXACTMATCH;
  3136. ExactMatchBuf.End = 0;
  3137. // FALL THROUGH!!
  3138. case SAVE_SEGMENT:
  3139. //
  3140. // Put the segment element into the segment array
  3141. //
  3142. SegmentElement = (PSEGMENTW) GrowBuffer (&SegmentArray, sizeof (SEGMENTW));
  3143. CopyMemory (SegmentElement, &Segment, sizeof (SEGMENTW));
  3144. Segment.Type = SEGMENTTYPE_UNKNOWN;
  3145. State = ReturnState;
  3146. break;
  3147. case LOOK_FOR_NUMBER:
  3148. //
  3149. // Here we are inside a bracket, and there is an optional
  3150. // numeric arg, which must be followed by a colon. Test
  3151. // that here.
  3152. //
  3153. LookAhead = Pattern;
  3154. MaxLen = 0;
  3155. while (*LookAhead >= L'0' && *LookAhead <= L'9') {
  3156. MaxLen = MaxLen * 10 + (*LookAhead - L'0');
  3157. LookAhead++;
  3158. }
  3159. if (LookAhead > Pattern && *LookAhead == L':') {
  3160. Pattern = LookAhead + 1;
  3161. //
  3162. // Check for special case syntax error: ?[0:]
  3163. //
  3164. if (Segment.Type == SEGMENTTYPE_EXACTMATCH && !MaxLen) {
  3165. State = PATTERN_ERROR;
  3166. break;
  3167. }
  3168. Segment.Wildcard.MaxLen = MaxLen;
  3169. }
  3170. SetBegin = Pattern;
  3171. State = LOOK_FOR_INCLUDE;
  3172. SetBuf.End = 0;
  3173. break;
  3174. case LOOK_FOR_INCLUDE:
  3175. //
  3176. // Here we are inside a bracket, past an optional numeric
  3177. // arg. Now we look for all the include sets, which are
  3178. // optional. We have the following possibilities:
  3179. //
  3180. // 1. End of set
  3181. // 2. An exclude set that needs to be skipped
  3182. // 3. A valid include set
  3183. // 4. Error
  3184. //
  3185. // We look at SetBegin, and not Pattern.
  3186. //
  3187. MYASSERT (SetBegin);
  3188. ch = *SetBegin;
  3189. if (ch == L']') {
  3190. //
  3191. // Case 1: end of set
  3192. //
  3193. if (SetBuf.End) {
  3194. pAppendCharToGrowBufferW (&SetBuf, L"");
  3195. Segment.Wildcard.IncludeSet = PoolMemDuplicateStringW (
  3196. Pool,
  3197. (PCWSTR) SetBuf.Buf
  3198. );
  3199. _wcslwr ((PWSTR) Segment.Wildcard.IncludeSet);
  3200. } else {
  3201. Segment.Wildcard.IncludeSet = NULL;
  3202. }
  3203. SetBuf.End = 0;
  3204. State = LOOK_FOR_EXCLUDE;
  3205. SetBegin = Pattern;
  3206. break;
  3207. }
  3208. if (ch == L'!') {
  3209. //
  3210. // Case 2: an exclude set
  3211. //
  3212. SetBegin++;
  3213. State = SKIP_EXCLUDE_SET;
  3214. ReturnState = LOOK_FOR_INCLUDE;
  3215. break;
  3216. }
  3217. if (*SetBegin == 0) {
  3218. State = PATTERN_ERROR;
  3219. break;
  3220. }
  3221. //
  3222. // Case 3: a valid include set.
  3223. //
  3224. State = CONDENSE_SET;
  3225. ReturnState = LOOK_FOR_INCLUDE;
  3226. break;
  3227. case LOOK_FOR_EXCLUDE:
  3228. //
  3229. // Here we are inside a bracket, past an optional numeric
  3230. // arg. All include sets are in the condensing buffer.
  3231. // Now we look for all the exclude sets, which are
  3232. // optional. We have the following possibilities:
  3233. //
  3234. // 1. End of set
  3235. // 2. A valid exclude set
  3236. // 3. An include set that needs to be skipped
  3237. // 4. Error
  3238. //
  3239. // We look at SetBegin, and not Pattern.
  3240. //
  3241. ch = *SetBegin;
  3242. if (ch == L']') {
  3243. //
  3244. // Case 1: end of set; we're done with this expr
  3245. //
  3246. if (SetBuf.End) {
  3247. pAppendCharToGrowBufferW (&SetBuf, L"");
  3248. Segment.Wildcard.ExcludeSet = PoolMemDuplicateStringW (
  3249. Pool,
  3250. (PCWSTR) SetBuf.Buf
  3251. );
  3252. _wcslwr ((PWSTR) Segment.Wildcard.ExcludeSet);
  3253. } else {
  3254. Segment.Wildcard.ExcludeSet = NULL;
  3255. }
  3256. SetBuf.End = 0;
  3257. State = SAVE_SEGMENT;
  3258. ReturnState = PARSE_CHAR_EXPR_OR_END;
  3259. Pattern = SetBegin + 1;
  3260. break;
  3261. }
  3262. if (ch == L'!') {
  3263. //
  3264. // Case 2: a valid exclude set; save it
  3265. //
  3266. SetBegin++;
  3267. if (*SetBegin != L'(') {
  3268. State = PATTERN_ERROR;
  3269. break;
  3270. }
  3271. SetBegin++;
  3272. State = CONDENSE_SET;
  3273. ReturnState = LOOK_FOR_EXCLUDE;
  3274. break;
  3275. }
  3276. if (*SetBegin == 0) {
  3277. State = PATTERN_ERROR;
  3278. break;
  3279. }
  3280. //
  3281. // Case 3: an include set that needs to be skipped.
  3282. //
  3283. State = SKIP_INCLUDE_SET;
  3284. ReturnState = LOOK_FOR_EXCLUDE;
  3285. break;
  3286. case CONDENSE_SET:
  3287. //
  3288. // Here SetBegin points to a set range, and it is our
  3289. // job to copy the range into the set buffer, and
  3290. // return back to the previous state.
  3291. //
  3292. //
  3293. // Copy the character at SetBegin
  3294. //
  3295. if (*SetBegin == L'^') {
  3296. SetBegin++;
  3297. if (*SetBegin == 0) {
  3298. State = PATTERN_ERROR;
  3299. break;
  3300. }
  3301. }
  3302. pAppendCharToGrowBufferW (&SetBuf, SetBegin);
  3303. //
  3304. // Check if this is a range or not
  3305. //
  3306. LookAhead = SetBegin + 1;
  3307. if (*LookAhead == L'-') {
  3308. //
  3309. // Range, copy the character after the dash
  3310. //
  3311. SetBegin = LookAhead + 1;
  3312. if (*SetBegin == 0) {
  3313. State = PATTERN_ERROR;
  3314. break;
  3315. }
  3316. if (*SetBegin == L'^') {
  3317. SetBegin++;
  3318. if (*SetBegin == 0) {
  3319. State = PATTERN_ERROR;
  3320. break;
  3321. }
  3322. }
  3323. pAppendCharToGrowBufferW (&SetBuf, SetBegin);
  3324. } else {
  3325. //
  3326. // A single character, copy the character again
  3327. //
  3328. pAppendCharToGrowBufferW (&SetBuf, SetBegin);
  3329. }
  3330. SetBegin++;
  3331. ch = *SetBegin;
  3332. //
  3333. // If this is an exclude set, we must have a closing paren
  3334. // or a comma
  3335. //
  3336. State = ReturnState;
  3337. if (ReturnState == LOOK_FOR_EXCLUDE) {
  3338. if (ch == L')') {
  3339. SetBegin++;
  3340. ch = *SetBegin;
  3341. } else if (ch != L',') {
  3342. State = PATTERN_ERROR;
  3343. } else {
  3344. //
  3345. // Continue condensing the next part of this exclude set
  3346. //
  3347. State = CONDENSE_SET;
  3348. }
  3349. }
  3350. //
  3351. // We either need a comma or a close brace
  3352. //
  3353. if (ch == L',') {
  3354. SetBegin++;
  3355. } else if (ch != L']') {
  3356. State = PATTERN_ERROR;
  3357. }
  3358. break;
  3359. case SKIP_EXCLUDE_SET:
  3360. //
  3361. // Skip over the parenthesis group, assuming it is syntatically
  3362. // correct, and return to the previous state.
  3363. //
  3364. if (*SetBegin != L'(') {
  3365. State = PATTERN_ERROR;
  3366. break;
  3367. }
  3368. SetBegin++;
  3369. while (*SetBegin) {
  3370. if (*SetBegin == L'^') {
  3371. SetBegin++;
  3372. } else if (*SetBegin == L')') {
  3373. break;
  3374. }
  3375. SetBegin++;
  3376. }
  3377. if (*SetBegin == 0) {
  3378. State = PATTERN_ERROR;
  3379. break;
  3380. }
  3381. SetBegin++;
  3382. //
  3383. // Now we are either at a comma or a close brace
  3384. //
  3385. ch = *SetBegin;
  3386. State = ReturnState;
  3387. if (ch == L',') {
  3388. SetBegin++;
  3389. } else if (ch != L']') {
  3390. State = PATTERN_ERROR;
  3391. }
  3392. break;
  3393. case SKIP_INCLUDE_SET:
  3394. //
  3395. // Skip to the next comma or closing brace. We know it is
  3396. // syntatically correct by now.
  3397. //
  3398. ch = 0;
  3399. while (*SetBegin) {
  3400. ch = *SetBegin;
  3401. if (ch == L'^') {
  3402. SetBegin++;
  3403. } else if (ch == L',' || ch == L']') {
  3404. break;
  3405. }
  3406. SetBegin++;
  3407. }
  3408. MYASSERT (*SetBegin);
  3409. if (ch == L',') {
  3410. SetBegin++;
  3411. }
  3412. State = ReturnState;
  3413. break;
  3414. }
  3415. if (State == PATTERN_DONE || State == PATTERN_ERROR) {
  3416. break;
  3417. }
  3418. }
  3419. FreeGrowBuffer (&ExactMatchBuf);
  3420. FreeGrowBuffer (&SetBuf);
  3421. FreeGrowBuffer (&SegmentArray);
  3422. if (State == PATTERN_ERROR || PatternArray.End == 0) {
  3423. FreeGrowBuffer (&PatternArray);
  3424. PoolMemDestroyPool (Pool);
  3425. return NULL;
  3426. }
  3427. //
  3428. // Copy the fully parsed pattern array into the return struct
  3429. //
  3430. Struct->Pattern = (PPATTERNPROPSW) PoolMemGetAlignedMemory (
  3431. Pool,
  3432. PatternArray.End
  3433. );
  3434. CopyMemory (Struct->Pattern, PatternArray.Buf, PatternArray.End);
  3435. Struct->PatternCount = PatternArray.End / sizeof (PATTERNPROPSW);
  3436. Struct->Pool = Pool;
  3437. FreeGrowBuffer (&PatternArray);
  3438. return Struct;
  3439. }
  3440. VOID
  3441. PrintPattern (
  3442. PCSTR PatStr,
  3443. PPARSEDPATTERNA Struct OPTIONAL
  3444. )
  3445. /*++
  3446. Routine Description:
  3447. PrintPattern is used for debugging the pattern parsing and testing
  3448. functions.
  3449. Arguments:
  3450. PatStr - Specifies the original pattern string (which is printed as a
  3451. heading)
  3452. Struct - Specifies the parsed pattern struct
  3453. Return Value:
  3454. None.
  3455. --*/
  3456. {
  3457. UINT u, v;
  3458. printf ("Pattern: %s\n\n", PatStr);
  3459. if (!Struct) {
  3460. printf ("Invalid Pattern\n\n");
  3461. return;
  3462. }
  3463. printf ("PatternCount: %u\n", Struct->PatternCount);
  3464. printf ("Pool: 0x%08X\n", Struct->Pool);
  3465. for (u = 0 ; u < Struct->PatternCount ; u++) {
  3466. printf (" Segment Count: %u\n", Struct->Pattern[u].SegmentCount);
  3467. for (v = 0 ; v < Struct->Pattern->SegmentCount ; v++) {
  3468. printf (" Type: ");
  3469. switch (Struct->Pattern[u].Segment[v].Type) {
  3470. case SEGMENTTYPE_EXACTMATCH:
  3471. printf ("SEGMENTTYPE_EXACTMATCH\n");
  3472. printf (" String: %s\n", Struct->Pattern[u].Segment[v].Exact.LowerCasePhrase);
  3473. printf (" Bytes: %u\n", Struct->Pattern[u].Segment[v].Exact.PhraseBytes);
  3474. break;
  3475. case SEGMENTTYPE_OPTIONAL:
  3476. printf ("SEGMENTTYPE_OPTIONAL\n");
  3477. printf (" MaxLen: %u\n", Struct->Pattern[u].Segment[v].Wildcard.MaxLen);
  3478. printf (" IncludeSet: %s\n", Struct->Pattern[u].Segment[v].Wildcard.IncludeSet);
  3479. printf (" ExcludeSet: %s\n", Struct->Pattern[u].Segment[v].Wildcard.ExcludeSet);
  3480. break;
  3481. case SEGMENTTYPE_REQUIRED:
  3482. printf ("SEGMENTTYPE_REQUIRED\n");
  3483. printf (" MaxLen: %u\n", Struct->Pattern[u].Segment[v].Wildcard.MaxLen);
  3484. printf (" IncludeSet: %s\n", Struct->Pattern[u].Segment[v].Wildcard.IncludeSet);
  3485. printf (" ExcludeSet: %s\n", Struct->Pattern[u].Segment[v].Wildcard.ExcludeSet);
  3486. break;
  3487. }
  3488. }
  3489. }
  3490. printf ("\n");
  3491. }
  3492. /*++
  3493. Routine Description:
  3494. TestParsedPattern finds the end of the string to test and calls
  3495. TestParsedPatternAB.
  3496. Arguments:
  3497. ParsedPattern - Specifies the parsed pattern structure as returned by
  3498. CreateParsedPattern
  3499. StringToTest - Specifies the string to test against the pattern
  3500. Return Value:
  3501. TRUE if the string fits the pattern, FALSE if it does not
  3502. --*/
  3503. BOOL
  3504. TestParsedPatternA (
  3505. IN PPARSEDPATTERNA ParsedPattern,
  3506. IN PCSTR StringToTest
  3507. )
  3508. {
  3509. PCSTR EndPlusOne = GetEndOfStringA (StringToTest);
  3510. return TestParsedPatternABA (ParsedPattern, StringToTest, EndPlusOne);
  3511. }
  3512. BOOL
  3513. TestParsedPatternW (
  3514. IN PPARSEDPATTERNW ParsedPattern,
  3515. IN PCWSTR StringToTest
  3516. )
  3517. {
  3518. PCWSTR EndPlusOne = GetEndOfStringW (StringToTest);
  3519. return TestParsedPatternABW (ParsedPattern, StringToTest, EndPlusOne);
  3520. }
  3521. /*++
  3522. Routine Description:
  3523. pTestSet tests a character against an include and exclude set. The sets are
  3524. formatted in pairs of characters, where the first character in the pair is
  3525. the low range, and the second character in the pair is the high range. The
  3526. specified character will automatically be lower-cased, and all whitespace
  3527. characters are tested against the space character (ascii 32).
  3528. Arguments:
  3529. ch - Specifies the character to test. This character is converted
  3530. to lower case before the test.
  3531. IncludeSet - Specifies the set of characters that ch must be a member of.
  3532. If NULL is specified, then the include set is all characters.
  3533. ExcludeSet - Specifies the range of characters that ch cannot be a member
  3534. of. If NULL is specified, then no characters are excluded.
  3535. Return Value:
  3536. TRUE if ch is in the include set and not in the exclude set; FALSE
  3537. otherwise.
  3538. --*/
  3539. BOOL
  3540. pTestSetA (
  3541. IN MBCHAR ch,
  3542. IN PCSTR IncludeSet, OPTIONAL
  3543. IN PCSTR ExcludeSet OPTIONAL
  3544. )
  3545. {
  3546. MBCHAR LowChar, HighChar;
  3547. BOOL b = TRUE;
  3548. if (isspace (ch)) {
  3549. if (ch != ' ') {
  3550. if (pTestSetA (' ', IncludeSet, ExcludeSet)) {
  3551. return TRUE;
  3552. }
  3553. }
  3554. } else {
  3555. ch = _mbctolower (ch);
  3556. }
  3557. if (IncludeSet) {
  3558. b = FALSE;
  3559. while (*IncludeSet) {
  3560. LowChar = _mbsnextc (IncludeSet);
  3561. IncludeSet = _mbsinc (IncludeSet);
  3562. HighChar = _mbsnextc (IncludeSet);
  3563. IncludeSet = _mbsinc (IncludeSet);
  3564. if (ch >= LowChar && ch <= HighChar) {
  3565. b = TRUE;
  3566. break;
  3567. }
  3568. }
  3569. }
  3570. if (b && ExcludeSet) {
  3571. while (*ExcludeSet) {
  3572. LowChar = _mbsnextc (ExcludeSet);
  3573. ExcludeSet = _mbsinc (ExcludeSet);
  3574. HighChar = _mbsnextc (ExcludeSet);
  3575. ExcludeSet = _mbsinc (ExcludeSet);
  3576. if (ch >= LowChar && ch <= HighChar) {
  3577. b = FALSE;
  3578. break;
  3579. }
  3580. }
  3581. }
  3582. return b;
  3583. }
  3584. BOOL
  3585. pTestSetW (
  3586. IN WCHAR ch,
  3587. IN PCWSTR IncludeSet, OPTIONAL
  3588. IN PCWSTR ExcludeSet OPTIONAL
  3589. )
  3590. {
  3591. WCHAR LowChar, HighChar;
  3592. BOOL b = TRUE;
  3593. if (iswspace (ch)) {
  3594. if (ch != L' ') {
  3595. if (pTestSetW (L' ', IncludeSet, ExcludeSet)) {
  3596. return TRUE;
  3597. }
  3598. }
  3599. } else {
  3600. ch = towlower (ch);
  3601. }
  3602. if (IncludeSet) {
  3603. b = FALSE;
  3604. while (*IncludeSet) {
  3605. LowChar = *IncludeSet++;
  3606. HighChar = *IncludeSet++;
  3607. if (ch >= LowChar && ch <= HighChar) {
  3608. b = TRUE;
  3609. break;
  3610. }
  3611. }
  3612. }
  3613. if (b && ExcludeSet) {
  3614. while (*ExcludeSet) {
  3615. LowChar = *ExcludeSet++;
  3616. HighChar = *ExcludeSet++;
  3617. if (ch >= LowChar && ch <= HighChar) {
  3618. b = FALSE;
  3619. break;
  3620. }
  3621. }
  3622. }
  3623. return b;
  3624. }
  3625. /*++
  3626. Routine Description:
  3627. pTestOnePatternAB tests a string against a parsed pattern. It loops through
  3628. each segment in the pattern, and calls itself recursively in certain
  3629. circumstances.
  3630. Arguments:
  3631. Pattern - Specifies the parsed pattern, as returned from
  3632. CreateParsedPattern
  3633. StartSeg - Specifies the segment within Pattern to start testing. This
  3634. is used for recursion and outside callers should pass in 0.
  3635. StringToTest - Specifies the string to test against Pattern. In recursion,
  3636. this member will be a pointer to the start of the sub string
  3637. to test.
  3638. EndPlusOne - Specifies one character beyond the end of the string. This
  3639. typically points to the nul terminator.
  3640. Return Value:
  3641. TRUE if the string between StringToTest and EndPlusOne fits Pattern. FALSE
  3642. otherwise.
  3643. --*/
  3644. BOOL
  3645. pTestOnePatternABA (
  3646. IN PPATTERNPROPSA Pattern,
  3647. IN UINT StartSeg,
  3648. IN PCSTR StringToTest,
  3649. IN PCSTR EndPlusOne
  3650. )
  3651. {
  3652. UINT u;
  3653. PSEGMENTA Segment;
  3654. MBCHAR ch1, ch2;
  3655. PCSTR q;
  3656. PCSTR TempEnd;
  3657. UINT BytesLeft;
  3658. UINT Chars;
  3659. for (u = StartSeg ; u < Pattern->SegmentCount ; u++) {
  3660. Segment = &Pattern->Segment[u];
  3661. switch (Segment->Type) {
  3662. case SEGMENTTYPE_EXACTMATCH:
  3663. //
  3664. // Check if the exact match is long enough, or if
  3665. // the remaining string must match
  3666. //
  3667. BytesLeft = (PBYTE) EndPlusOne - (PBYTE) StringToTest;
  3668. if (u + 1 == Pattern->SegmentCount) {
  3669. if (BytesLeft != Segment->Exact.PhraseBytes) {
  3670. return FALSE;
  3671. }
  3672. } else if (BytesLeft < Segment->Exact.PhraseBytes) {
  3673. return FALSE;
  3674. }
  3675. //
  3676. // Compare the strings
  3677. //
  3678. q = Segment->Exact.LowerCasePhrase;
  3679. TempEnd = (PCSTR) ((PBYTE) q + Segment->Exact.PhraseBytes);
  3680. ch1 = 0;
  3681. ch2 = 1;
  3682. while (q < TempEnd) {
  3683. ch1 = _mbsnextc (StringToTest);
  3684. ch2 = _mbsnextc (q);
  3685. ch1 = _mbctolower (ch1);
  3686. if (ch1 != ch2) {
  3687. if (ch2 == ' ') {
  3688. if (!isspace (ch1)) {
  3689. break;
  3690. }
  3691. } else {
  3692. break;
  3693. }
  3694. }
  3695. q = _mbsinc (q);
  3696. StringToTest = _mbsinc (StringToTest);
  3697. }
  3698. if (ch1 != ch2) {
  3699. return FALSE;
  3700. }
  3701. //
  3702. // Continue onto next segment
  3703. //
  3704. break;
  3705. case SEGMENTTYPE_REQUIRED:
  3706. MYASSERT (Segment->Wildcard.MaxLen > 0);
  3707. //
  3708. // Verify there are the correct number of characters
  3709. // in the specified char set
  3710. //
  3711. Chars = Segment->Wildcard.MaxLen;
  3712. if (Segment->Wildcard.IncludeSet || Segment->Wildcard.ExcludeSet) {
  3713. while (StringToTest < EndPlusOne && Chars > 0) {
  3714. if (!pTestSetA (
  3715. _mbsnextc (StringToTest),
  3716. Segment->Wildcard.IncludeSet,
  3717. Segment->Wildcard.ExcludeSet
  3718. )) {
  3719. return FALSE;
  3720. }
  3721. Chars--;
  3722. StringToTest = _mbsinc (StringToTest);
  3723. }
  3724. } else {
  3725. while (StringToTest < EndPlusOne && Chars > 0) {
  3726. Chars--;
  3727. StringToTest = _mbsinc (StringToTest);
  3728. }
  3729. }
  3730. if (Chars) {
  3731. return FALSE;
  3732. }
  3733. if (u + 1 == Pattern->SegmentCount) {
  3734. if (*StringToTest) {
  3735. return FALSE;
  3736. }
  3737. }
  3738. //
  3739. // Continue onto next segment
  3740. //
  3741. break;
  3742. case SEGMENTTYPE_OPTIONAL:
  3743. if (Segment->Wildcard.MaxLen == 0) {
  3744. //
  3745. // Last segment is "anything"
  3746. //
  3747. if (u + 1 == Pattern->SegmentCount &&
  3748. !Segment->Wildcard.IncludeSet &&
  3749. !Segment->Wildcard.ExcludeSet
  3750. ) {
  3751. return TRUE;
  3752. }
  3753. }
  3754. //
  3755. // Find end of optional text
  3756. //
  3757. TempEnd = StringToTest;
  3758. Chars = Segment->Wildcard.MaxLen;
  3759. if (Segment->Wildcard.IncludeSet || Segment->Wildcard.ExcludeSet) {
  3760. if (Chars) {
  3761. while (TempEnd < EndPlusOne && Chars > 0) {
  3762. if (!pTestSetA (
  3763. _mbsnextc (TempEnd),
  3764. Segment->Wildcard.IncludeSet,
  3765. Segment->Wildcard.ExcludeSet
  3766. )) {
  3767. break;
  3768. }
  3769. TempEnd = _mbsinc (TempEnd);
  3770. Chars--;
  3771. }
  3772. } else {
  3773. while (TempEnd < EndPlusOne) {
  3774. if (!pTestSetA (
  3775. _mbsnextc (TempEnd),
  3776. Segment->Wildcard.IncludeSet,
  3777. Segment->Wildcard.ExcludeSet
  3778. )) {
  3779. break;
  3780. }
  3781. TempEnd = _mbsinc (TempEnd);
  3782. }
  3783. }
  3784. } else if (Chars) {
  3785. while (TempEnd < EndPlusOne && Chars > 0) {
  3786. TempEnd = _mbsinc (TempEnd);
  3787. Chars--;
  3788. }
  3789. } else {
  3790. TempEnd = EndPlusOne;
  3791. }
  3792. //
  3793. // If this is the last segment, then match only when
  3794. // the remaining text fits
  3795. //
  3796. if (u + 1 == Pattern->SegmentCount) {
  3797. return TempEnd >= EndPlusOne;
  3798. }
  3799. //
  3800. // Because other segments exist, we must check recursively
  3801. //
  3802. do {
  3803. if (pTestOnePatternABA (Pattern, u + 1, StringToTest, EndPlusOne)) {
  3804. return TRUE;
  3805. }
  3806. StringToTest = _mbsinc (StringToTest);
  3807. } while (StringToTest <= TempEnd);
  3808. //
  3809. // No match
  3810. //
  3811. return FALSE;
  3812. }
  3813. }
  3814. return TRUE;
  3815. }
  3816. BOOL
  3817. pTestOnePatternABW (
  3818. IN PPATTERNPROPSW Pattern,
  3819. IN UINT StartSeg,
  3820. IN PCWSTR StringToTest,
  3821. IN PCWSTR EndPlusOne
  3822. )
  3823. {
  3824. UINT u;
  3825. PSEGMENTW Segment;
  3826. WCHAR ch1, ch2;
  3827. PCWSTR q;
  3828. PCWSTR TempEnd;
  3829. UINT BytesLeft;
  3830. UINT Chars;
  3831. for (u = StartSeg ; u < Pattern->SegmentCount ; u++) {
  3832. Segment = &Pattern->Segment[u];
  3833. switch (Segment->Type) {
  3834. case SEGMENTTYPE_EXACTMATCH:
  3835. //
  3836. // Check if the exact match is long enough, or if
  3837. // the remaining string must match
  3838. //
  3839. BytesLeft = (PBYTE) EndPlusOne - (PBYTE) StringToTest;
  3840. if (u + 1 == Pattern->SegmentCount) {
  3841. if (BytesLeft != Segment->Exact.PhraseBytes) {
  3842. return FALSE;
  3843. }
  3844. } else if (BytesLeft < Segment->Exact.PhraseBytes) {
  3845. return FALSE;
  3846. }
  3847. //
  3848. // Compare the strings
  3849. //
  3850. q = Segment->Exact.LowerCasePhrase;
  3851. TempEnd = (PCWSTR) ((PBYTE) q + Segment->Exact.PhraseBytes);
  3852. ch1 = 0;
  3853. ch2 = 1;
  3854. while (q < TempEnd) {
  3855. ch1 = towlower (*StringToTest);
  3856. ch2 = *q;
  3857. if (ch1 != ch2) {
  3858. if (ch2 == L' ') {
  3859. if (!iswspace (ch1)) {
  3860. break;
  3861. }
  3862. } else {
  3863. break;
  3864. }
  3865. }
  3866. q++;
  3867. StringToTest++;
  3868. }
  3869. if (ch1 != ch2) {
  3870. return FALSE;
  3871. }
  3872. //
  3873. // Continue onto next segment
  3874. //
  3875. break;
  3876. case SEGMENTTYPE_REQUIRED:
  3877. MYASSERT (Segment->Wildcard.MaxLen > 0);
  3878. //
  3879. // Verify there are the correct number of characters
  3880. // in the specified char set
  3881. //
  3882. Chars = Segment->Wildcard.MaxLen;
  3883. if (Segment->Wildcard.IncludeSet || Segment->Wildcard.ExcludeSet) {
  3884. while (StringToTest < EndPlusOne && Chars > 0) {
  3885. if (!pTestSetW (
  3886. *StringToTest,
  3887. Segment->Wildcard.IncludeSet,
  3888. Segment->Wildcard.ExcludeSet
  3889. )) {
  3890. return FALSE;
  3891. }
  3892. Chars--;
  3893. StringToTest++;
  3894. }
  3895. if (Chars) {
  3896. return FALSE;
  3897. }
  3898. } else {
  3899. StringToTest += Chars;
  3900. if (StringToTest > EndPlusOne) {
  3901. return FALSE;
  3902. }
  3903. }
  3904. if (u + 1 == Pattern->SegmentCount) {
  3905. if (*StringToTest) {
  3906. return FALSE;
  3907. }
  3908. }
  3909. //
  3910. // Continue onto next segment
  3911. //
  3912. break;
  3913. case SEGMENTTYPE_OPTIONAL:
  3914. if (Segment->Wildcard.MaxLen == 0) {
  3915. //
  3916. // Last segment is "anything"
  3917. //
  3918. if (u + 1 == Pattern->SegmentCount &&
  3919. !Segment->Wildcard.IncludeSet &&
  3920. !Segment->Wildcard.ExcludeSet
  3921. ) {
  3922. return TRUE;
  3923. }
  3924. }
  3925. //
  3926. // Find end of optional text
  3927. //
  3928. TempEnd = StringToTest;
  3929. Chars = Segment->Wildcard.MaxLen;
  3930. if (Segment->Wildcard.IncludeSet || Segment->Wildcard.ExcludeSet) {
  3931. if (Chars) {
  3932. while (TempEnd < EndPlusOne && Chars > 0) {
  3933. if (!pTestSetW (
  3934. *TempEnd,
  3935. Segment->Wildcard.IncludeSet,
  3936. Segment->Wildcard.ExcludeSet
  3937. )) {
  3938. break;
  3939. }
  3940. TempEnd++;
  3941. Chars--;
  3942. }
  3943. } else {
  3944. while (TempEnd < EndPlusOne) {
  3945. if (!pTestSetW (
  3946. *TempEnd,
  3947. Segment->Wildcard.IncludeSet,
  3948. Segment->Wildcard.ExcludeSet
  3949. )) {
  3950. break;
  3951. }
  3952. TempEnd++;
  3953. }
  3954. }
  3955. } else if (Chars) {
  3956. TempEnd += Chars;
  3957. if (TempEnd > EndPlusOne) {
  3958. TempEnd = EndPlusOne;
  3959. }
  3960. } else {
  3961. TempEnd = EndPlusOne;
  3962. }
  3963. //
  3964. // If this is the last segment, then match only when
  3965. // the remaining text fits
  3966. //
  3967. if (u + 1 == Pattern->SegmentCount) {
  3968. return TempEnd >= EndPlusOne;
  3969. }
  3970. //
  3971. // Because other segments exist, we must check recursively
  3972. //
  3973. do {
  3974. if (pTestOnePatternABW (Pattern, u + 1, StringToTest, EndPlusOne)) {
  3975. return TRUE;
  3976. }
  3977. StringToTest++;
  3978. } while (StringToTest <= TempEnd);
  3979. //
  3980. // No match
  3981. //
  3982. return FALSE;
  3983. }
  3984. }
  3985. return TRUE;
  3986. }
  3987. /*++
  3988. Routine Description:
  3989. TestParsedPattternAB loops through all the patterns in ParsedPattern,
  3990. testing the specified string against each. The loop stops at the first
  3991. match.
  3992. Arguments:
  3993. ParsedPattern - Specifies the parsed pattern, as returned from
  3994. CreateParsedPattern
  3995. StringToTest - Specifies the start of the string to test.
  3996. EndPlusOne - Specifies a pointer to the first character after the end of
  3997. the string. This often points to the nul at the end of the
  3998. string. A nul must not exist in between StringToTest and
  3999. EndPlusOne; a nul can only be at *EndPlusOne. A nul is not
  4000. required.
  4001. Return Value:
  4002. TRUE if the string specified between StringToTest and EndPlusOne matches
  4003. Pattern. FALSE otherwise.
  4004. --*/
  4005. BOOL
  4006. TestParsedPatternABA (
  4007. IN PPARSEDPATTERNA ParsedPattern,
  4008. IN PCSTR StringToTest,
  4009. IN PCSTR EndPlusOne
  4010. )
  4011. {
  4012. UINT u;
  4013. BOOL b = FALSE;
  4014. for (u = 0 ; u < ParsedPattern->PatternCount ; u++) {
  4015. b = pTestOnePatternABA (
  4016. &ParsedPattern->Pattern[u],
  4017. 0,
  4018. StringToTest,
  4019. EndPlusOne
  4020. );
  4021. if (b) {
  4022. break;
  4023. }
  4024. }
  4025. return b;
  4026. }
  4027. BOOL
  4028. TestParsedPatternABW (
  4029. IN PPARSEDPATTERNW ParsedPattern,
  4030. IN PCWSTR StringToTest,
  4031. IN PCWSTR EndPlusOne
  4032. )
  4033. {
  4034. UINT u;
  4035. BOOL b = FALSE;
  4036. for (u = 0 ; u < ParsedPattern->PatternCount ; u++) {
  4037. b = pTestOnePatternABW (
  4038. &ParsedPattern->Pattern[u],
  4039. 0,
  4040. StringToTest,
  4041. EndPlusOne
  4042. );
  4043. if (b) {
  4044. break;
  4045. }
  4046. }
  4047. return b;
  4048. }
  4049. /*++
  4050. Routine Description:
  4051. DestroyParsedPattern cleans up a pattern allocated from CreateParsedPattern.
  4052. Arguments:
  4053. ParsedPattern - Specifies the value returned from CreateParsedPattern.
  4054. Return Value:
  4055. None.
  4056. --*/
  4057. VOID
  4058. DestroyParsedPatternA (
  4059. IN PPARSEDPATTERNA ParsedPattern
  4060. )
  4061. {
  4062. if (ParsedPattern) {
  4063. PoolMemDestroyPool (ParsedPattern->Pool);
  4064. }
  4065. }
  4066. VOID
  4067. DestroyParsedPatternW (
  4068. IN PPARSEDPATTERNW ParsedPattern
  4069. )
  4070. {
  4071. if (ParsedPattern) {
  4072. PoolMemDestroyPool (ParsedPattern->Pool);
  4073. }
  4074. }
  4075. void
  4076. _copymbchar (
  4077. OUT PSTR sz1,
  4078. IN PCSTR sz2
  4079. )
  4080. /*++
  4081. Routine Description:
  4082. _copymbchar transfers the character at sz2 to sz1, which may be one or
  4083. two bytes long.
  4084. Arguments:
  4085. sz1 - The destination string
  4086. sz2 - The source string
  4087. Return Value:
  4088. none
  4089. --*/
  4090. {
  4091. if (IsLeadByte (*sz2))
  4092. sz1[1] = sz2[1];
  4093. *sz1 = *sz2;
  4094. }
  4095. /*++
  4096. Routine Description:
  4097. _tcsctrim removes character c from the end of str if it exists. It removes
  4098. only one character at the most.
  4099. Arguments:
  4100. str - A pointer to the string that may have character c at the end
  4101. c - The character that may be at the end of the string
  4102. Return Value:
  4103. TRUE if character c was at the end of the string, or FALSE if it was not.
  4104. --*/
  4105. BOOL
  4106. _mbsctrim (
  4107. OUT PSTR str,
  4108. IN MBCHAR c
  4109. )
  4110. {
  4111. PSTR end;
  4112. end = GetEndOfStringA (str);
  4113. end = _mbsdec (str, end);
  4114. if (end && _mbsnextc (end) == c) {
  4115. *end = 0;
  4116. return TRUE;
  4117. }
  4118. return FALSE;
  4119. }
  4120. BOOL
  4121. _wcsctrim (
  4122. PWSTR str,
  4123. WCHAR c
  4124. )
  4125. {
  4126. PWSTR end;
  4127. end = GetEndOfStringW (str);
  4128. end == str ? end = NULL : end--;
  4129. if (end && *end == c) {
  4130. *end = 0;
  4131. return TRUE;
  4132. }
  4133. return FALSE;
  4134. }
  4135. /*++
  4136. Routine Description:
  4137. The FreeStringResourceEx functions are used to free a recently used
  4138. string that is not being passed back to the caller. In almost all
  4139. cases, this string is at the end of our array of pointers, so we can
  4140. efficiently search sequentially in reverse order. If the pointer is
  4141. not the last element of the array, it is first swapped with the real
  4142. last element of the array so the array size is reduced.
  4143. Arguments:
  4144. AllocTable - The GROWBUFFER table that holds the list of previously
  4145. allocated strings (return values of ParseMessageEx or
  4146. GetResourceStringEx).
  4147. String - A pointer to the string that is in AllocTable
  4148. Return Value:
  4149. none
  4150. --*/
  4151. VOID
  4152. FreeStringResourceExA (
  4153. IN PGROWBUFFER AllocTable,
  4154. IN PCSTR String
  4155. )
  4156. {
  4157. LPCTSTR *Ptr, *End, *Start;
  4158. if (!String || String == (PCSTR) g_FailedGetResourceString) {
  4159. return;
  4160. }
  4161. //
  4162. // Locate string (search sequentially in reverse order)
  4163. //
  4164. if (AllocTable->End < sizeof (PCSTR)) {
  4165. DEBUGMSG ((DBG_ERROR, "FreeStringResourceA: Attempt to free address %x (%s); address table empty", String, String));
  4166. return;
  4167. }
  4168. Start = (PCSTR *) AllocTable->Buf;
  4169. End = (PCSTR *) (AllocTable->Buf + AllocTable->End - sizeof (PCSTR));
  4170. Ptr = End;
  4171. while (Ptr >= Start) {
  4172. if (*Ptr == String) {
  4173. break;
  4174. }
  4175. Ptr--;
  4176. }
  4177. //
  4178. // String not found case
  4179. //
  4180. if (Ptr < Start) {
  4181. DEBUGMSG ((DBG_ERROR, "FreeStringResourceA: Attempt to free address %x (%s); address not found in table", String, String));
  4182. return;
  4183. }
  4184. //
  4185. // Free LocalAlloc'd memory
  4186. //
  4187. LocalFree ((HLOCAL) String);
  4188. //
  4189. // If this element is not the end, copy real end to the ptr
  4190. //
  4191. if (Ptr < End) {
  4192. *Ptr = *End;
  4193. }
  4194. //
  4195. // Shrink buffer size
  4196. //
  4197. AllocTable->End -= sizeof (PCSTR);
  4198. }
  4199. VOID
  4200. FreeStringResourcePtrExA (
  4201. IN PGROWBUFFER AllocTable,
  4202. IN OUT PCSTR * String
  4203. )
  4204. {
  4205. if (NULL != *String) {
  4206. FreeStringResourceExA(AllocTable, *String);
  4207. *String = NULL;
  4208. }
  4209. }
  4210. VOID
  4211. FreeStringResourceExW (
  4212. IN PGROWBUFFER AllocTable,
  4213. IN PCWSTR String
  4214. )
  4215. {
  4216. FreeStringResourceExA (AllocTable, (PCSTR) String);
  4217. }
  4218. VOID
  4219. FreeStringResourcePtrExW (
  4220. IN PGROWBUFFER AllocTable,
  4221. IN OUT PCWSTR * String
  4222. )
  4223. {
  4224. if (NULL != *String) {
  4225. FreeStringResourceExW(AllocTable, *String);
  4226. *String = NULL;
  4227. }
  4228. }
  4229. /*++
  4230. Routine Description:
  4231. The pAddStringResource function is used to track pointers allocated
  4232. by FormatMessage. They are added to an array (maintained in a GROWBUFFER
  4233. structure). This table of pointers is used by FreeStringResource or
  4234. StringResourceFree.
  4235. Arguments:
  4236. String - A pointer to a LocalAlloc'd string (the return value of
  4237. FormatMessage). This string is added to a table of allocated
  4238. strings.
  4239. Return Value:
  4240. none
  4241. --*/
  4242. VOID
  4243. pAddStringResource (
  4244. IN PGROWBUFFER GrowBuf,
  4245. IN PCSTR String
  4246. )
  4247. {
  4248. PCSTR *Ptr;
  4249. Ptr = (PCSTR *) GrowBuffer (GrowBuf, sizeof (PCSTR));
  4250. if (Ptr) {
  4251. *Ptr = String;
  4252. }
  4253. ELSE_DEBUGMSG ((DBG_ERROR, "pAddStringResource: GrowBuffer failure caused memory leak"));
  4254. }
  4255. /*++
  4256. Routine Description:
  4257. pFreeAllStringResourcesEx frees all strings currently listed in AllocTable.
  4258. This function allows the caller to wait until all processing is done
  4259. to clean up string resources that may have been allocated.
  4260. Arguments:
  4261. none
  4262. Return Value:
  4263. none
  4264. --*/
  4265. VOID
  4266. pFreeAllStringResourcesEx (
  4267. IN PGROWBUFFER AllocTable
  4268. )
  4269. {
  4270. PCSTR *Ptr, *Start, *End;
  4271. if (AllocTable->End) {
  4272. Start = (PCSTR *) AllocTable->Buf;
  4273. End = (PCSTR *) (AllocTable->Buf + AllocTable->End);
  4274. for (Ptr = Start ; Ptr < End ; Ptr++) {
  4275. LocalFree ((HLOCAL) (*Ptr));
  4276. }
  4277. }
  4278. FreeGrowBuffer (AllocTable);
  4279. }
  4280. /*++
  4281. Routine Description:
  4282. CreateAllocTable creates a GROWBUFFER structure that can be used with
  4283. ParseMessageEx, GetStringResourceEx, FreeStringResourceEx and
  4284. pFreeAllStringResourcesEx. Call this function to recieve a private
  4285. allocation table to pass to these functions. Call DestroyAllocTable
  4286. to clean up.
  4287. Arguments:
  4288. none
  4289. Return Value:
  4290. A pointer to a GROWBUFFER structure, or NULL if a memory allocation failed.
  4291. --*/
  4292. PGROWBUFFER
  4293. CreateAllocTable (
  4294. VOID
  4295. )
  4296. {
  4297. PGROWBUFFER AllocTable;
  4298. GROWBUFFER TempForInit = GROWBUF_INIT;
  4299. AllocTable = (PGROWBUFFER) MemAlloc (g_hHeap, 0, sizeof (GROWBUFFER));
  4300. CopyMemory (AllocTable, &TempForInit, sizeof (GROWBUFFER));
  4301. return AllocTable;
  4302. }
  4303. /*++
  4304. Routine Description:
  4305. DestroyAllocTable cleans up all memory associated with an AllocTable.
  4306. Arguments:
  4307. AllocTable - A pointer to a GROWBUFFER structure allocated by CreateAllocTable
  4308. Return Value:
  4309. none
  4310. --*/
  4311. VOID
  4312. DestroyAllocTable (
  4313. PGROWBUFFER AllocTable
  4314. )
  4315. {
  4316. MYASSERT (AllocTable);
  4317. pFreeAllStringResourcesEx (AllocTable);
  4318. MemFree (g_hHeap, 0, AllocTable);
  4319. }
  4320. /*++
  4321. Routine Description:
  4322. BeginMessageProcessing enters a guarded section of code that plans to use the
  4323. ParseMessage and GetStringResource functions, but needs cleanup at the end
  4324. of processing.
  4325. EndMessageProcessing destroys all memory allocated within the message processing
  4326. block, and leaves the guarded section.
  4327. Arguments:
  4328. none
  4329. Return Value:
  4330. BeginMessageProcessing returns FALSE if an out-of-memory condition occurrs.
  4331. --*/
  4332. BOOL
  4333. BeginMessageProcessing (
  4334. VOID
  4335. )
  4336. {
  4337. if (!TryEnterOurCriticalSection (&g_MessageCs)) {
  4338. DEBUGMSG ((DBG_ERROR, "Thread attempting to enter BeginMessageProcessing while another"
  4339. "thread is processing messages as well."));
  4340. EnterOurCriticalSection (&g_MessageCs);
  4341. }
  4342. g_LastAllocTable = g_ShortTermAllocTable;
  4343. g_ShortTermAllocTable = CreateAllocTable();
  4344. MYASSERT (g_ShortTermAllocTable);
  4345. return TRUE;
  4346. }
  4347. VOID
  4348. EndMessageProcessing (
  4349. VOID
  4350. )
  4351. {
  4352. if (TryEnterOurCriticalSection (&g_MessageCs)) {
  4353. DEBUGMSG ((DBG_ERROR, "Thread attempting to end message processing when it hasn't been started"));
  4354. LeaveOurCriticalSection (&g_MessageCs);
  4355. return;
  4356. }
  4357. DestroyAllocTable (g_ShortTermAllocTable);
  4358. g_ShortTermAllocTable = g_LastAllocTable;
  4359. LeaveOurCriticalSection (&g_MessageCs);
  4360. }
  4361. /*++
  4362. Routine Description:
  4363. ParseMessage is used to obtain a string from the executable's message table
  4364. and parse it with FormatMessage. An array of arguments can be passed by
  4365. the caller. FormatMessage will replace %1 with the first element of the
  4366. array, %2 with the second element, and so on. The array does not need to
  4367. be terminated, and if a message string uses %n, element n must be non-NULL.
  4368. Arguments:
  4369. Template - A string indicating which message to extract, or a WORD value
  4370. cast as a string. (ParseMessageID does this cast via a macro.)
  4371. ArgArray - Optional array of string pointers, where the meaning depends on
  4372. the message string. A reference in the message string to %n
  4373. requires element n of ArgArray to be a valid string pointer.
  4374. Return Value:
  4375. Pointer to the string allocated. Call StringResourceFree to free all
  4376. allocated strings (a one-time cleanup for all strings). The pointer may
  4377. be NULL if the resource does not exist or is empty.
  4378. --*/
  4379. PCSTR
  4380. ParseMessageExA (
  4381. IN PGROWBUFFER AllocTable,
  4382. IN PCSTR Template,
  4383. IN PCSTR ArgArray[]
  4384. )
  4385. {
  4386. PSTR MsgBuf = NULL;
  4387. SetLastError (ERROR_SUCCESS);
  4388. if (HIWORD (Template)) {
  4389. // From string
  4390. FormatMessageA (
  4391. FORMAT_MESSAGE_ALLOCATE_BUFFER|
  4392. FORMAT_MESSAGE_ARGUMENT_ARRAY|
  4393. FORMAT_MESSAGE_FROM_STRING,
  4394. (PVOID) Template,
  4395. 0,
  4396. 0,
  4397. (PVOID) &MsgBuf,
  4398. 0,
  4399. (va_list *) ArgArray
  4400. );
  4401. } else {
  4402. // From resource
  4403. FormatMessageA (
  4404. FORMAT_MESSAGE_ALLOCATE_BUFFER|
  4405. FORMAT_MESSAGE_ARGUMENT_ARRAY|
  4406. FORMAT_MESSAGE_FROM_HMODULE,
  4407. (PVOID) g_hInst,
  4408. (DWORD) Template,
  4409. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  4410. (PVOID) &MsgBuf,
  4411. 0,
  4412. (va_list *) ArgArray
  4413. );
  4414. }
  4415. if (!MsgBuf && GetLastError() == ERROR_SUCCESS) {
  4416. //
  4417. // FormatMessage returns "fail" on a resource that is an empty
  4418. // string, but fortunately it does not alter the last error
  4419. //
  4420. MsgBuf = (PSTR) LocalAlloc (LPTR, sizeof (CHAR));
  4421. if (MsgBuf) {
  4422. *MsgBuf = 0;
  4423. }
  4424. }
  4425. if (MsgBuf) {
  4426. pAddStringResource (AllocTable, MsgBuf);
  4427. return MsgBuf;
  4428. }
  4429. if (HIWORD (Template)) {
  4430. DEBUGMSGA ((
  4431. DBG_ERROR,
  4432. "Can't get string resource ID %s -- returning an empty string",
  4433. Template
  4434. ));
  4435. } else {
  4436. DEBUGMSG ((
  4437. DBG_ERROR,
  4438. "Can't get string resource ID %u -- returning an empty string",
  4439. (UINT) Template
  4440. ));
  4441. }
  4442. return (PCSTR) g_FailedGetResourceString;
  4443. }
  4444. PCWSTR
  4445. ParseMessageExW (
  4446. IN PGROWBUFFER AllocTable,
  4447. IN PCWSTR Template,
  4448. IN PCWSTR ArgArray[]
  4449. )
  4450. {
  4451. PWSTR MsgBuf = NULL;
  4452. SetLastError (ERROR_SUCCESS);
  4453. if (HIWORD (Template)) {
  4454. // From string
  4455. FormatMessageW (
  4456. FORMAT_MESSAGE_ALLOCATE_BUFFER|
  4457. FORMAT_MESSAGE_ARGUMENT_ARRAY|
  4458. FORMAT_MESSAGE_FROM_STRING,
  4459. (PVOID) Template,
  4460. 0,
  4461. 0,
  4462. (PVOID) &MsgBuf,
  4463. 0,
  4464. (va_list *) ArgArray
  4465. );
  4466. } else {
  4467. // From resource
  4468. FormatMessageW (
  4469. FORMAT_MESSAGE_ALLOCATE_BUFFER|
  4470. FORMAT_MESSAGE_ARGUMENT_ARRAY|
  4471. FORMAT_MESSAGE_FROM_HMODULE,
  4472. (PVOID) g_hInst,
  4473. (DWORD) Template,
  4474. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  4475. (PVOID) &MsgBuf,
  4476. 0,
  4477. (va_list *) ArgArray
  4478. );
  4479. }
  4480. if (!MsgBuf && GetLastError() == ERROR_SUCCESS) {
  4481. //
  4482. // FormatMessage returns "fail" on a resource that is an empty
  4483. // string, but fortunately it does not alter the last error
  4484. //
  4485. MsgBuf = (PWSTR) LocalAlloc (LPTR, sizeof (WCHAR));
  4486. if (MsgBuf) {
  4487. *MsgBuf = 0;
  4488. }
  4489. }
  4490. if (MsgBuf) {
  4491. pAddStringResource (AllocTable, (PCSTR) MsgBuf);
  4492. return MsgBuf;
  4493. }
  4494. if (HIWORD (Template)) {
  4495. DEBUGMSGW ((
  4496. DBG_ERROR,
  4497. "Can't get string resource ID %s -- returning an empty string",
  4498. Template
  4499. ));
  4500. } else {
  4501. DEBUGMSG ((
  4502. DBG_ERROR,
  4503. "Can't get string resource ID %u -- returning an empty string",
  4504. (UINT) Template
  4505. ));
  4506. }
  4507. return g_FailedGetResourceString;
  4508. }
  4509. /*++
  4510. Routine Description:
  4511. GetStringResourceEx is an argument-less wrapper of ParseMessageEx. It allows
  4512. the caller to specify a message ID and recieve a pointer to the string if
  4513. it exists, and a table to track FormatMessage's allocations.
  4514. Arguments:
  4515. AllocTable - A pointer to a GROWBUFFER structure that is used to maintain
  4516. the handles of allocated strings
  4517. ID - The ID of the message resource to retrieve
  4518. Return Value:
  4519. Pointer to the string allocated. The return pointer may
  4520. be NULL if the resource does not exist or is empty.
  4521. Call FreeStringResource or DestroyAllocTable to clean up AllocTable.
  4522. --*/
  4523. PCSTR
  4524. GetStringResourceExA (
  4525. IN OUT PGROWBUFFER AllocTable,
  4526. IN UINT ID
  4527. )
  4528. {
  4529. return ParseMessageExA (AllocTable, (PSTR) (WORD) ID, NULL);
  4530. }
  4531. PCWSTR
  4532. GetStringResourceExW (
  4533. IN OUT PGROWBUFFER AllocTable,
  4534. IN UINT ID
  4535. )
  4536. {
  4537. return ParseMessageExW (AllocTable, (PWSTR) (WORD) ID, NULL);
  4538. }
  4539. /*++
  4540. Routine Description:
  4541. ParseMessageInWnd is used to exchange a string in a window with one from
  4542. the executable's message table. It is provided for dialog box initialization,
  4543. where a field in the dialog box requires dynamic data. The dialog box
  4544. resource should contain a control with its window text set to the message
  4545. string. Upon processing WM_INITDIALOG, the code should call ParseMessageInWnd,
  4546. supplying the necessary ArgArray, so the dialog box is initialized with
  4547. a dynamic message.
  4548. Arguments:
  4549. hwnd - The handle of a window whose title contains the message string ID
  4550. ArgArray - Optional array of string pointers, where the meaning depends on
  4551. the message string. A reference in the message string to %n
  4552. requires element n of ArgArray to be a valid string pointer.
  4553. Return Value:
  4554. none
  4555. --*/
  4556. VOID
  4557. ParseMessageInWndA (
  4558. HWND hwnd,
  4559. PCSTR ArgArray[]
  4560. )
  4561. {
  4562. CHAR Buffer[512];
  4563. PCSTR ParsedMsg;
  4564. GetWindowTextA (hwnd, Buffer, 512);
  4565. ParsedMsg = ParseMessageA (Buffer, ArgArray);
  4566. if (ParsedMsg) {
  4567. SetWindowTextA (hwnd, ParsedMsg);
  4568. FreeStringResourceA (ParsedMsg);
  4569. }
  4570. }
  4571. VOID
  4572. ParseMessageInWndW (
  4573. HWND hwnd,
  4574. PCWSTR ArgArray[]
  4575. )
  4576. {
  4577. WCHAR Buffer[512];
  4578. PCWSTR ParsedMsg;
  4579. GetWindowTextW (hwnd, Buffer, 512);
  4580. ParsedMsg = ParseMessageW (Buffer, ArgArray);
  4581. if (ParsedMsg) {
  4582. SetWindowTextW (hwnd, ParsedMsg);
  4583. FreeStringResourceW (ParsedMsg);
  4584. }
  4585. }
  4586. /*++
  4587. Routine Description:
  4588. ResourceMessageBox is used to display a message based on a message resource
  4589. ID.
  4590. Arguments:
  4591. hwndOwner - The handle of the owner of the message box to be displayed
  4592. ID - The identifier of the message resource
  4593. Flags - MessageBox flags (MB_OK, etc.)
  4594. ArgArray - Optional array of string pointers, where the meaning depends on
  4595. the message string. A reference in the message string to %n
  4596. requires element n of ArgArray to be a valid string pointer.
  4597. Return Value:
  4598. The return value of MessageBox (MB_YES, etc.)
  4599. --*/
  4600. INT
  4601. ResourceMessageBoxA (
  4602. IN HWND hwndOwner,
  4603. IN UINT ID,
  4604. IN UINT Flags,
  4605. IN PCSTR ArgArray[]
  4606. )
  4607. {
  4608. PCSTR Message;
  4609. PCSTR Title;
  4610. int rc;
  4611. Message = ParseMessageA ((PSTR) ID, ArgArray);
  4612. if (!Message)
  4613. return -1;
  4614. Title = GetStringResourceA (MSG_MESSAGEBOX_TITLE);
  4615. rc = MessageBoxA (hwndOwner, Message, Title, Flags);
  4616. FreeStringResourceA (Message);
  4617. if (Title) {
  4618. FreeStringResourceA (Title);
  4619. }
  4620. return rc;
  4621. }
  4622. INT
  4623. ResourceMessageBoxW (
  4624. IN HWND hwndOwner,
  4625. IN UINT ID,
  4626. IN UINT Flags,
  4627. IN PCWSTR ArgArray[]
  4628. )
  4629. {
  4630. PCWSTR Message;
  4631. PCWSTR Title;
  4632. int rc;
  4633. Message = ParseMessageW ((PWSTR) ID, ArgArray);
  4634. if (!Message)
  4635. return -1;
  4636. Title = GetStringResourceW (MSG_MESSAGEBOX_TITLE);
  4637. rc = MessageBoxW (hwndOwner, Message, Title, Flags);
  4638. FreeStringResourceW (Message);
  4639. if (Title) {
  4640. FreeStringResourceW (Title);
  4641. }
  4642. return rc;
  4643. }
  4644. BOOL
  4645. StringReplaceA (
  4646. IN PSTR Buffer,
  4647. IN DWORD MaxSize,
  4648. IN PSTR ReplaceStartPos,
  4649. IN PSTR ReplaceEndPos,
  4650. IN PCSTR NewString
  4651. )
  4652. {
  4653. BOOL rf = FALSE;
  4654. DWORD oldSubStringLength;
  4655. DWORD newSubStringLength;
  4656. DWORD currentStringLength;
  4657. LONG offset;
  4658. PSTR movePosition;
  4659. //
  4660. // Check assumptions.
  4661. //
  4662. MYASSERT(Buffer);
  4663. MYASSERT(ReplaceStartPos && ReplaceStartPos >= Buffer);
  4664. MYASSERT(ReplaceEndPos && ReplaceEndPos >= ReplaceStartPos);
  4665. MYASSERT(NewString);
  4666. //
  4667. // Compute sizes.
  4668. //
  4669. oldSubStringLength = ReplaceEndPos - ReplaceStartPos;
  4670. newSubStringLength = ByteCountA(NewString);
  4671. currentStringLength = SizeOfStringA(Buffer) + 1;
  4672. offset = newSubStringLength - oldSubStringLength;
  4673. //
  4674. // Make sure there is enough room in the buffer to perform the replace
  4675. // operation.
  4676. //
  4677. if (currentStringLength + offset > MaxSize) {
  4678. DEBUGMSG((DBG_WARNING,"ERROR: Buffer to small to perform string replacement."));
  4679. rf = FALSE;
  4680. }
  4681. else {
  4682. //
  4683. // Shift the rest of the buffer to adjust it to the size of the new string.
  4684. //
  4685. if (newSubStringLength > oldSubStringLength) {
  4686. //
  4687. // right shift.
  4688. //
  4689. for (movePosition = Buffer + currentStringLength;
  4690. movePosition >= ReplaceStartPos + oldSubStringLength;
  4691. movePosition--) {
  4692. *(movePosition + offset) = *movePosition;
  4693. }
  4694. }
  4695. else {
  4696. //
  4697. // left or no shift.
  4698. //
  4699. for(movePosition = ReplaceStartPos + newSubStringLength;
  4700. movePosition < Buffer + currentStringLength;
  4701. movePosition++) {
  4702. *movePosition = *(movePosition - offset);
  4703. }
  4704. }
  4705. //
  4706. // Now, copy in the string.
  4707. //
  4708. _mbsncpy(ReplaceStartPos,NewString,newSubStringLength);
  4709. //
  4710. // String replacement completed successfully.
  4711. //
  4712. rf = TRUE;
  4713. }
  4714. return rf;
  4715. }
  4716. BOOL
  4717. StringReplaceW (
  4718. IN PWSTR Buffer,
  4719. IN DWORD MaxSize,
  4720. IN PWSTR ReplaceStartPos,
  4721. IN PWSTR ReplaceEndPos,
  4722. IN PCWSTR NewString
  4723. )
  4724. {
  4725. BOOL rf = FALSE;
  4726. DWORD oldSubStringLength;
  4727. DWORD newSubStringLength;
  4728. DWORD currentStringLength;
  4729. LONG offset;
  4730. PWSTR movePosition;
  4731. //
  4732. // Check assumptions.
  4733. //
  4734. MYASSERT(Buffer);
  4735. MYASSERT(ReplaceStartPos && ReplaceStartPos >= Buffer);
  4736. MYASSERT(ReplaceEndPos && ReplaceEndPos >= ReplaceStartPos);
  4737. MYASSERT(NewString);
  4738. //
  4739. // Compute sizes.
  4740. //
  4741. oldSubStringLength = ReplaceEndPos - ReplaceStartPos;
  4742. newSubStringLength = wcslen(NewString);
  4743. currentStringLength = wcslen(Buffer) + 1;
  4744. offset = newSubStringLength - oldSubStringLength;
  4745. //
  4746. // Make sure there is enough room in the buffer to perform the replace
  4747. // operation.
  4748. //
  4749. if (currentStringLength + offset > MaxSize) {
  4750. DEBUGMSG((DBG_WARNING,"ERROR: Buffer to small to perform string replacement."));
  4751. rf = FALSE;
  4752. }
  4753. else {
  4754. //
  4755. // Shift the rest of the buffer to adjust it to the size of the new string.
  4756. //
  4757. if (newSubStringLength > oldSubStringLength) {
  4758. //
  4759. // right shift.
  4760. //
  4761. for (movePosition = Buffer + currentStringLength;
  4762. movePosition >= ReplaceStartPos + oldSubStringLength;
  4763. movePosition--) {
  4764. *(movePosition + offset) = *movePosition;
  4765. }
  4766. }
  4767. else {
  4768. //
  4769. // left or no shift.
  4770. //
  4771. for(movePosition = ReplaceStartPos + newSubStringLength;
  4772. movePosition < Buffer + currentStringLength;
  4773. movePosition++) {
  4774. *movePosition = *(movePosition - offset);
  4775. }
  4776. }
  4777. //
  4778. // Now, copy in the string.
  4779. //
  4780. wcsncpy(ReplaceStartPos,NewString,newSubStringLength);
  4781. //
  4782. // String replacement completed successfully.
  4783. //
  4784. rf = TRUE;
  4785. }
  4786. return rf;
  4787. }
  4788. #if 0 // REMOVED
  4789. /*++
  4790. Routine Description:
  4791. AddInfSectionToStringTable enumerates the specified section and adds each
  4792. item to the string table. An optional callback allows data to be associated
  4793. with each item.
  4794. Note - if this code is re-enabled, cleanup all pSetupStringTableXXXX functions
  4795. callers will *ALWAYS* link to SPUTILSA.LIB and never SPUTILSU.LIB
  4796. so all pSetupStringTableXXXX functions are ANSI
  4797. Arguments:
  4798. Table - Specifies the table that receives new entries
  4799. InfFile - Specifies an open INF handle of the file to read
  4800. Section - Specifies the INF section name to enumerate
  4801. Field - Specifies which field to extract text from. If the field
  4802. exists, it is added to the string table.
  4803. Callback - Specifies optional callback to be called before adding to
  4804. the string table. The callback supplies additional data.
  4805. CallbackParam - Data passed to the callback
  4806. Return Value:
  4807. TRUE if the INF file was processed successfullly, or FALSE if an error
  4808. occurred.
  4809. --*/
  4810. BOOL
  4811. AddInfSectionToStringTableA (
  4812. IN OUT PVOID Table,
  4813. IN HINF InfFile,
  4814. IN PCSTR Section,
  4815. IN INT Field,
  4816. IN ADDINFSECTION_PROCA Callback,
  4817. IN PVOID CallbackData
  4818. )
  4819. {
  4820. INFCONTEXT ic;
  4821. LONG rc;
  4822. DWORD ReqSize;
  4823. DWORD CurrentSize = 0;
  4824. PSTR NewBuffer, Buffer = NULL;
  4825. PVOID Data;
  4826. UINT DataSize;
  4827. BOOL b = FALSE;
  4828. //
  4829. // On NT, Setup API is compiled with UNICODE, so the string table
  4830. // functions are UNICODE only.
  4831. //
  4832. // Above comment is now incorrect, string table functions linked
  4833. // with this module are always ANSI
  4834. //
  4835. #error FIX pSetupStringTableXXXX usage
  4836. if (ISNT()) {
  4837. SetLastError (ERROR_CALL_NOT_IMPLEMENTED);
  4838. return FALSE;
  4839. }
  4840. if (SetupFindFirstLineA (InfFile, Section, NULL, &ic)) {
  4841. do {
  4842. if (!SetupGetStringFieldA (&ic, Field, NULL, 0, &ReqSize)) {
  4843. continue;
  4844. }
  4845. if (ReqSize > CurrentSize) {
  4846. ReqSize = ((ReqSize / 1024) + 1) * 1024;
  4847. if (Buffer) {
  4848. NewBuffer = (PSTR) MemReAlloc (g_hHeap, 0, Buffer, ReqSize);
  4849. } else {
  4850. NewBuffer = (PSTR) MemAlloc (g_hHeap, 0, ReqSize);
  4851. }
  4852. if (!NewBuffer) {
  4853. goto cleanup;
  4854. }
  4855. Buffer = NewBuffer;
  4856. CurrentSize = ReqSize;
  4857. }
  4858. if (!SetupGetStringFieldA (&ic, Field, Buffer, CurrentSize, NULL)) {
  4859. DEBUGMSG ((DBG_ERROR, "AddInfSectionToStringTable: SetupGetStringField failed unexpectedly"));
  4860. continue;
  4861. }
  4862. Data = NULL;
  4863. DataSize = 0;
  4864. if (Callback) {
  4865. rc = Callback (Buffer, &Data, &DataSize, CallbackData);
  4866. if (rc == CALLBACK_STOP) {
  4867. goto cleanup;
  4868. }
  4869. if (rc == CALLBACK_SKIP) {
  4870. continue;
  4871. }
  4872. }
  4873. rc = pSetupStringTableAddStringEx (
  4874. Table,
  4875. Buffer,
  4876. STRTAB_CASE_INSENSITIVE|STRTAB_BUFFER_WRITEABLE,
  4877. Data,
  4878. DataSize
  4879. );
  4880. if (rc == -1) {
  4881. goto cleanup;
  4882. }
  4883. } while (SetupFindNextLine (&ic, &ic));
  4884. }
  4885. b = TRUE;
  4886. cleanup:
  4887. if (Buffer) {
  4888. PushError();
  4889. MemFree (g_hHeap, 0, Buffer);
  4890. PopError();
  4891. }
  4892. return b;
  4893. }
  4894. BOOL
  4895. AddInfSectionToStringTableW (
  4896. IN OUT PVOID Table,
  4897. IN HINF InfFile,
  4898. IN PCWSTR Section,
  4899. IN INT Field,
  4900. IN ADDINFSECTION_PROCW Callback,
  4901. IN PVOID CallbackData
  4902. )
  4903. {
  4904. INFCONTEXT ic;
  4905. LONG rc;
  4906. DWORD ReqSize;
  4907. DWORD CurrentSize = 0;
  4908. PWSTR NewBuffer, Buffer = NULL;
  4909. PVOID Data;
  4910. UINT DataSize;
  4911. BOOL b = FALSE;
  4912. //
  4913. // On Win9x, Setup API is compiled with ANSI, so the string table
  4914. // functions are ANSI only.
  4915. //
  4916. // Above comment is now incorrect, string table functions linked
  4917. // with this module are always ANSI
  4918. //
  4919. #error FIX pSetupStringTableXXXX usage
  4920. if (ISWIN9X()) {
  4921. SetLastError (ERROR_CALL_NOT_IMPLEMENTED);
  4922. return FALSE;
  4923. }
  4924. if (SetupFindFirstLineW (InfFile, Section, NULL, &ic)) {
  4925. do {
  4926. if (!SetupGetStringFieldW (&ic, Field, NULL, 0, &ReqSize)) {
  4927. continue;
  4928. }
  4929. if (ReqSize > CurrentSize) {
  4930. ReqSize = ((ReqSize / 1024) + 1) * 1024;
  4931. if (Buffer) {
  4932. NewBuffer = (PWSTR) MemReAlloc (g_hHeap, 0, Buffer, ReqSize);
  4933. } else {
  4934. NewBuffer = (PWSTR) MemAlloc (g_hHeap, 0, ReqSize);
  4935. }
  4936. if (!NewBuffer) {
  4937. goto cleanup;
  4938. }
  4939. Buffer = NewBuffer;
  4940. CurrentSize = ReqSize;
  4941. }
  4942. if (!SetupGetStringFieldW (&ic, Field, Buffer, CurrentSize, NULL)) {
  4943. DEBUGMSG ((DBG_ERROR, "AddInfSectionToStringTable: SetupGetStringField failed unexpectedly"));
  4944. continue;
  4945. }
  4946. Data = NULL;
  4947. DataSize = 0;
  4948. if (Callback) {
  4949. rc = Callback (Buffer, &Data, &DataSize, CallbackData);
  4950. if (rc == CALLBACK_STOP) {
  4951. goto cleanup;
  4952. }
  4953. if (rc == CALLBACK_SKIP) {
  4954. continue;
  4955. }
  4956. }
  4957. rc = pSetupStringTableAddStringEx (
  4958. Table,
  4959. Buffer,
  4960. STRTAB_CASE_INSENSITIVE|STRTAB_BUFFER_WRITEABLE,
  4961. Data,
  4962. DataSize
  4963. );
  4964. if (rc == -1) {
  4965. goto cleanup;
  4966. }
  4967. } while (SetupFindNextLine (&ic, &ic));
  4968. }
  4969. b = TRUE;
  4970. cleanup:
  4971. if (Buffer) {
  4972. PushError();
  4973. MemFree (g_hHeap, 0, Buffer);
  4974. PopError();
  4975. }
  4976. return b;
  4977. }
  4978. #endif // REMOVED
  4979. /*++
  4980. Routine Description:
  4981. Finds the last wack in the path and returns a pointer to the next
  4982. character. If no wack is found, returns a pointer to the full
  4983. string.
  4984. Arguments:
  4985. PathSpec - Specifies the path that has a file at the end of it
  4986. Return Value:
  4987. A pointer to the file name in the path.
  4988. --*/
  4989. PCSTR
  4990. GetFileNameFromPathA (
  4991. IN PCSTR PathSpec
  4992. )
  4993. {
  4994. PCSTR p;
  4995. p = _mbsrchr (PathSpec, '\\');
  4996. if (p) {
  4997. p = _mbsinc (p);
  4998. } else {
  4999. p = PathSpec;
  5000. }
  5001. return p;
  5002. }
  5003. PCWSTR
  5004. GetFileNameFromPathW (
  5005. IN PCWSTR PathSpec
  5006. )
  5007. {
  5008. PCWSTR p;
  5009. p = wcsrchr (PathSpec, L'\\');
  5010. if (p) {
  5011. p++;
  5012. } else {
  5013. p = PathSpec;
  5014. }
  5015. return p;
  5016. }
  5017. /*++
  5018. Routine Description:
  5019. Finds the last wack in the path and then the last point from the remaining path
  5020. returning a pointer to the next character. If no point is found, returns a null pointer.
  5021. Arguments:
  5022. PathSpec - Specifies the path that has a file at the end of it
  5023. Return Value:
  5024. A pointer to the file extension, excluding the dot, or NULL if no extension exists.
  5025. --*/
  5026. PCSTR
  5027. GetFileExtensionFromPathA (
  5028. IN PCSTR PathSpec
  5029. )
  5030. {
  5031. PCSTR p;
  5032. PCSTR ReturnPtr = NULL;
  5033. p = PathSpec;
  5034. while (*p) {
  5035. if (*p == '.') {
  5036. ReturnPtr = p + 1;
  5037. } else if (*p == '\\') {
  5038. ReturnPtr = NULL;
  5039. }
  5040. p = _mbsinc (p);
  5041. }
  5042. return ReturnPtr;
  5043. }
  5044. PCWSTR
  5045. GetFileExtensionFromPathW (
  5046. IN PCWSTR PathSpec
  5047. )
  5048. {
  5049. PCWSTR p;
  5050. PCWSTR ReturnPtr = NULL;
  5051. p = PathSpec;
  5052. while (*p) {
  5053. if (*p == L'.') {
  5054. ReturnPtr = p + 1;
  5055. } else if (*p == L'\\') {
  5056. ReturnPtr = NULL;
  5057. }
  5058. p++;
  5059. }
  5060. return ReturnPtr;
  5061. }
  5062. /*++
  5063. Routine Description:
  5064. GetDotExtensionFromPath finds the last wack in the path and then the last dot from
  5065. the remaining path, returning a pointer to the dot. If no dot is found, returns the
  5066. end of the string.
  5067. Arguments:
  5068. PathSpec - Specifies the path that has a file at the end of it
  5069. Return Value:
  5070. A pointer to the file extension, including the dot, or the end of the string if
  5071. no extension exists.
  5072. --*/
  5073. PCSTR
  5074. GetDotExtensionFromPathA (
  5075. IN PCSTR PathSpec
  5076. )
  5077. {
  5078. PCSTR p;
  5079. PCSTR ReturnPtr = NULL;
  5080. p = PathSpec;
  5081. while (*p) {
  5082. if (*p == '.') {
  5083. ReturnPtr = p;
  5084. } else if (*p == '\\') {
  5085. ReturnPtr = NULL;
  5086. }
  5087. p = _mbsinc (p);
  5088. }
  5089. if (!ReturnPtr) {
  5090. return p;
  5091. }
  5092. return ReturnPtr;
  5093. }
  5094. PCWSTR
  5095. GetDotExtensionFromPathW (
  5096. IN PCWSTR PathSpec
  5097. )
  5098. {
  5099. PCWSTR p;
  5100. PCWSTR ReturnPtr = NULL;
  5101. p = PathSpec;
  5102. while (*p) {
  5103. if (*p == L'.') {
  5104. ReturnPtr = p;
  5105. } else if (*p == L'\\') {
  5106. ReturnPtr = NULL;
  5107. }
  5108. p++;
  5109. }
  5110. if (!ReturnPtr) {
  5111. return p;
  5112. }
  5113. return ReturnPtr;
  5114. }
  5115. /*++
  5116. Routine Description:
  5117. CountInstancesOfChar returns the number of occurances Char
  5118. is found in String.
  5119. Arguments:
  5120. String - Specifies the text that may or may not contain
  5121. search text
  5122. Char - Specifies the char to count
  5123. Return Value:
  5124. The number of times Char appears in String.
  5125. --*/
  5126. UINT
  5127. CountInstancesOfCharA (
  5128. IN PCSTR String,
  5129. IN MBCHAR Char
  5130. )
  5131. {
  5132. UINT Count;
  5133. Count = 0;
  5134. while (*String) {
  5135. if (_mbsnextc (String) == Char) {
  5136. Count++;
  5137. }
  5138. String = _mbsinc (String);
  5139. }
  5140. return Count;
  5141. }
  5142. UINT
  5143. CountInstancesOfCharW (
  5144. IN PCWSTR String,
  5145. IN WCHAR Char
  5146. )
  5147. {
  5148. UINT Count;
  5149. Count = 0;
  5150. while (*String) {
  5151. if (*String == Char) {
  5152. Count++;
  5153. }
  5154. String++;
  5155. }
  5156. return Count;
  5157. }
  5158. /*++
  5159. Routine Description:
  5160. CountInstancesOfCharI returns the number of occurances Char
  5161. is found in String. The comparison is case-insenetive.
  5162. Arguments:
  5163. String - Specifies the text that may or may not contain
  5164. search text
  5165. Char - Specifies the char to count
  5166. Return Value:
  5167. The number of times Char appears in String.
  5168. --*/
  5169. UINT
  5170. CountInstancesOfCharIA (
  5171. IN PCSTR String,
  5172. IN MBCHAR Char
  5173. )
  5174. {
  5175. UINT Count;
  5176. Char = tolower (Char);
  5177. Count = 0;
  5178. while (*String) {
  5179. if ((MBCHAR) tolower (_mbsnextc (String)) == Char) {
  5180. Count++;
  5181. }
  5182. String = _mbsinc (String);
  5183. }
  5184. return Count;
  5185. }
  5186. UINT
  5187. CountInstancesOfCharIW (
  5188. IN PCWSTR String,
  5189. IN WCHAR Char
  5190. )
  5191. {
  5192. UINT Count;
  5193. Char = towlower (Char);
  5194. Count = 0;
  5195. while (*String) {
  5196. if (towlower (*String) == Char) {
  5197. Count++;
  5198. }
  5199. String++;
  5200. }
  5201. return Count;
  5202. }
  5203. /*++
  5204. Routine Description:
  5205. Searches the string counting the number of occurances of
  5206. SearchString exist in SourceString.
  5207. Arguments:
  5208. SourceString - Specifies the text that may or may not contain
  5209. search text
  5210. SearchString - Specifies the text phrase to count
  5211. Return Value:
  5212. The number of times SearchString appears in SourceString.
  5213. --*/
  5214. UINT
  5215. CountInstancesOfSubStringA (
  5216. IN PCSTR SourceString,
  5217. IN PCSTR SearchString
  5218. )
  5219. {
  5220. PCSTR p;
  5221. UINT Count;
  5222. UINT SearchBytes;
  5223. Count = 0;
  5224. p = SourceString;
  5225. SearchBytes = ByteCountA (SearchString);
  5226. while (p = _mbsistr (p, SearchString)) {
  5227. Count++;
  5228. p += SearchBytes;
  5229. }
  5230. return Count;
  5231. }
  5232. UINT
  5233. CountInstancesOfSubStringW (
  5234. IN PCWSTR SourceString,
  5235. IN PCWSTR SearchString
  5236. )
  5237. {
  5238. PCWSTR p;
  5239. UINT Count;
  5240. UINT SearchChars;
  5241. Count = 0;
  5242. p = SourceString;
  5243. SearchChars = wcslen (SearchString);
  5244. while (p = _wcsistr (p, SearchString)) {
  5245. Count++;
  5246. p += SearchChars;
  5247. }
  5248. return Count;
  5249. }
  5250. /*++
  5251. Routine Description:
  5252. Searches and replaces all occurances of SearchString with
  5253. ReplaceString.
  5254. Arguments:
  5255. SourceString - String that contiains zero or more instances
  5256. of the search text
  5257. SearchString - String to search for. Cannot be zero-length or NULL.
  5258. ReplaceString - String to replace. Can be zero-length but cannot
  5259. be NULL.
  5260. Return Value:
  5261. A pointer to the pool-allocated string, or NULL if no instances
  5262. of SearchString were found in SourceString. Free the non-NULL
  5263. pointer with FreePathString.
  5264. --*/
  5265. PCSTR
  5266. StringSearchAndReplaceA (
  5267. IN PCSTR SourceString,
  5268. IN PCSTR SearchString,
  5269. IN PCSTR ReplaceString
  5270. )
  5271. {
  5272. PSTR NewString;
  5273. PBYTE p, q;
  5274. PBYTE Dest;
  5275. UINT Count;
  5276. UINT Size;
  5277. UINT SearchBytes;
  5278. UINT ReplaceBytes;
  5279. UINT UntouchedBytes;
  5280. //
  5281. // Count occurances within the string
  5282. //
  5283. Count = CountInstancesOfSubStringA (
  5284. SourceString,
  5285. SearchString
  5286. );
  5287. if (!Count) {
  5288. return NULL;
  5289. }
  5290. SearchBytes = ByteCountA (SearchString);
  5291. ReplaceBytes = ByteCountA (ReplaceString);
  5292. MYASSERT (SearchBytes);
  5293. Size = SizeOfStringA (SourceString) -
  5294. Count * SearchBytes +
  5295. Count * ReplaceBytes;
  5296. NewString = (PSTR) PoolMemGetAlignedMemory (g_PathsPool, Size);
  5297. if (!NewString) {
  5298. return NULL;
  5299. }
  5300. p = (PBYTE) SourceString;
  5301. Dest = (PBYTE) NewString;
  5302. while (q = (PBYTE) _mbsistr ((PCSTR) p, SearchString)) {
  5303. UntouchedBytes = q - p;
  5304. if (UntouchedBytes) {
  5305. CopyMemory (Dest, p, UntouchedBytes);
  5306. Dest += UntouchedBytes;
  5307. }
  5308. if (ReplaceBytes) {
  5309. CopyMemory (Dest, (PBYTE) ReplaceString, ReplaceBytes);
  5310. Dest += ReplaceBytes;
  5311. }
  5312. p = q + SearchBytes;
  5313. }
  5314. StringCopyA ((PSTR) Dest, (PSTR) p);
  5315. return NewString;
  5316. }
  5317. PCWSTR
  5318. StringSearchAndReplaceW (
  5319. IN PCWSTR SourceString,
  5320. IN PCWSTR SearchString,
  5321. IN PCWSTR ReplaceString
  5322. )
  5323. {
  5324. PWSTR NewString;
  5325. PBYTE p, q;
  5326. PBYTE Dest;
  5327. UINT Count;
  5328. UINT Size;
  5329. UINT SearchBytes;
  5330. UINT ReplaceBytes;
  5331. UINT UntouchedBytes;
  5332. //
  5333. // Count occurances within the string
  5334. //
  5335. Count = CountInstancesOfSubStringW (
  5336. SourceString,
  5337. SearchString
  5338. );
  5339. if (!Count) {
  5340. return NULL;
  5341. }
  5342. SearchBytes = ByteCountW (SearchString);
  5343. ReplaceBytes = ByteCountW (ReplaceString);
  5344. MYASSERT (SearchBytes);
  5345. Size = SizeOfStringW (SourceString) -
  5346. Count * SearchBytes +
  5347. Count * ReplaceBytes;
  5348. NewString = (PWSTR) PoolMemGetAlignedMemory (g_PathsPool, Size);
  5349. if (!NewString) {
  5350. return NULL;
  5351. }
  5352. p = (PBYTE) SourceString;
  5353. Dest = (PBYTE) NewString;
  5354. while (q = (PBYTE) _wcsistr ((PCWSTR) p, SearchString)) {
  5355. UntouchedBytes = q - p;
  5356. if (UntouchedBytes) {
  5357. CopyMemory (Dest, p, UntouchedBytes);
  5358. Dest += UntouchedBytes;
  5359. }
  5360. if (ReplaceBytes) {
  5361. CopyMemory (Dest, (PBYTE) ReplaceString, ReplaceBytes);
  5362. Dest += ReplaceBytes;
  5363. }
  5364. p = q + SearchBytes;
  5365. }
  5366. StringCopyW ((PWSTR) Dest, (PWSTR) p);
  5367. return NewString;
  5368. }
  5369. PSTR *
  5370. CommandLineToArgvA (
  5371. IN PCSTR CmdLine,
  5372. OUT INT *NumArgs
  5373. )
  5374. /*++
  5375. Routine Description:
  5376. CommandLineToArgvA implements an ANSI version of the Win32 function
  5377. CommandLineToArgvW.
  5378. Arguments:
  5379. CmdLine - A pointer to the complete command line, including the
  5380. module name. This is the same string returned by
  5381. GetCommandLineA().
  5382. NumArgs - Receives the number of arguments allocated, identical to
  5383. main's argc parameter. That is, NumArgs is equal to
  5384. the number of command line arguments plus one for the
  5385. command itself.
  5386. Return Value:
  5387. A pointer to an array of string pointers, one per argument. The
  5388. command line arguments are placed in separate nul-terminated strings.
  5389. The caller must free the memory using a single call to GlobalFree or
  5390. LocalFree.
  5391. --*/
  5392. {
  5393. PCSTR Start, End;
  5394. BOOL QuoteMode;
  5395. MBCHAR ch = 0;
  5396. INT Pass;
  5397. INT ArgStrSize;
  5398. INT Args;
  5399. PSTR ArgStrEnd = NULL; // filled in on pass one, used on pass two
  5400. PSTR *ArgPtrArray = NULL; // filled in on pass one, used on pass two
  5401. //
  5402. // Count args on first pass, then allocate memory and create arg string
  5403. //
  5404. ArgStrSize = 0;
  5405. Pass = 0;
  5406. do {
  5407. // Init loop
  5408. Pass++;
  5409. Args = 0;
  5410. Start = CmdLine;
  5411. // Skip leading space
  5412. while (isspace (*Start)) {
  5413. Start++;
  5414. }
  5415. while (*Start) {
  5416. // Look for quote mode
  5417. if (*Start == '\"') {
  5418. QuoteMode = TRUE;
  5419. Start++;
  5420. } else {
  5421. QuoteMode = FALSE;
  5422. }
  5423. // Find end of arg
  5424. End = Start;
  5425. while (*End) {
  5426. ch = _mbsnextc (End);
  5427. if (QuoteMode) {
  5428. if (ch == '\"') {
  5429. break;
  5430. }
  5431. } else {
  5432. if (isspace (ch)) {
  5433. break;
  5434. }
  5435. }
  5436. End = _mbsinc (End);
  5437. }
  5438. // If Pass 1, add string size
  5439. if (Pass == 1) {
  5440. ArgStrSize += (End - Start) + 1;
  5441. }
  5442. // If Pass 2, copy strings to buffer
  5443. else {
  5444. MYASSERT (ArgStrEnd);
  5445. MYASSERT (ArgPtrArray);
  5446. ArgPtrArray[Args] = ArgStrEnd;
  5447. StringCopyABA (ArgStrEnd, Start, End);
  5448. ArgStrEnd = GetEndOfStringA (ArgStrEnd);
  5449. ArgStrEnd++;
  5450. }
  5451. // Set Start to next arg
  5452. Args++;
  5453. if (QuoteMode && ch == '\"') {
  5454. End = _mbsinc (End);
  5455. }
  5456. Start = End;
  5457. while (isspace (*Start)) {
  5458. Start++;
  5459. }
  5460. }
  5461. // If Pass 1, allocate strings
  5462. if (Pass == 1) {
  5463. if (Args) {
  5464. ArgPtrArray = (PSTR *) GlobalAlloc (
  5465. GPTR,
  5466. sizeof (PSTR) * Args + ArgStrSize
  5467. );
  5468. if (!ArgPtrArray) {
  5469. return NULL;
  5470. }
  5471. ArgStrEnd = (PSTR) (&ArgPtrArray[Args]);
  5472. } else {
  5473. return NULL;
  5474. }
  5475. }
  5476. } while (Pass < 2);
  5477. *NumArgs = Args;
  5478. return ArgPtrArray;
  5479. }
  5480. BOOL
  5481. EnumNextMultiSzA (
  5482. IN OUT PMULTISZ_ENUMA MultiSzEnum
  5483. )
  5484. {
  5485. if (!MultiSzEnum->CurrentString || !(*MultiSzEnum->CurrentString)) {
  5486. return FALSE;
  5487. }
  5488. MultiSzEnum->CurrentString = GetEndOfStringA (MultiSzEnum->CurrentString) + 1;
  5489. return (MultiSzEnum->CurrentString [0] != 0);
  5490. }
  5491. BOOL
  5492. EnumFirstMultiSzA (
  5493. OUT PMULTISZ_ENUMA MultiSzEnum,
  5494. IN PCSTR MultiSzStr
  5495. )
  5496. {
  5497. if ((MultiSzStr == NULL) || (MultiSzStr [0] == 0)) {
  5498. return FALSE;
  5499. }
  5500. MultiSzEnum->Buffer = MultiSzStr;
  5501. MultiSzEnum->CurrentString = MultiSzStr;
  5502. return TRUE;
  5503. }
  5504. BOOL
  5505. EnumNextMultiSzW (
  5506. IN OUT PMULTISZ_ENUMW MultiSzEnum
  5507. )
  5508. {
  5509. if (!MultiSzEnum->CurrentString || !(*MultiSzEnum->CurrentString)) {
  5510. return FALSE;
  5511. }
  5512. MultiSzEnum->CurrentString = GetEndOfStringW (MultiSzEnum->CurrentString) + 1;
  5513. return (MultiSzEnum->CurrentString [0] != 0);
  5514. }
  5515. BOOL
  5516. EnumFirstMultiSzW (
  5517. OUT PMULTISZ_ENUMW MultiSzEnum,
  5518. IN PCWSTR MultiSzStr
  5519. )
  5520. {
  5521. if ((MultiSzStr == NULL) || (MultiSzStr [0] == 0)) {
  5522. return FALSE;
  5523. }
  5524. MultiSzEnum->Buffer = MultiSzStr;
  5525. MultiSzEnum->CurrentString = MultiSzStr;
  5526. return TRUE;
  5527. }
  5528. PSTR
  5529. GetPrevCharA (
  5530. IN PCSTR StartStr,
  5531. IN PCSTR CurrPtr,
  5532. IN CHARTYPE SearchChar
  5533. )
  5534. {
  5535. PCSTR ptr = CurrPtr;
  5536. for (;;) {
  5537. ptr = _mbsdec (StartStr, ptr);
  5538. if (!ptr) {
  5539. return NULL;
  5540. }
  5541. if (_mbsnextc (ptr) == SearchChar) {
  5542. return (PSTR) ptr;
  5543. }
  5544. }
  5545. }
  5546. PWSTR
  5547. GetPrevCharW (
  5548. IN PCWSTR StartStr,
  5549. IN PCWSTR CurrPtr,
  5550. IN WCHAR SearchChar
  5551. )
  5552. {
  5553. PCWSTR ptr = CurrPtr;
  5554. for (;;) {
  5555. ptr--;
  5556. if (*ptr == SearchChar) {
  5557. return (PWSTR) ptr;
  5558. }
  5559. if (ptr == StartStr) {
  5560. return NULL;
  5561. }
  5562. }
  5563. }
  5564. #define WACK_REPLACE_CHAR 0x02
  5565. VOID
  5566. ToggleWacksA (
  5567. IN PSTR Line,
  5568. IN BOOL Operation
  5569. )
  5570. {
  5571. CHAR curChar;
  5572. CHAR newChar;
  5573. PSTR p = Line;
  5574. curChar = Operation ? WACK_REPLACE_CHAR : '\\';
  5575. newChar = Operation ? '\\' : WACK_REPLACE_CHAR;
  5576. do {
  5577. p = _mbschr (p, curChar);
  5578. if (p) {
  5579. *p = newChar;
  5580. p = _mbsinc (p);
  5581. }
  5582. } while (p);
  5583. }
  5584. VOID
  5585. ToggleWacksW (
  5586. IN PWSTR Line,
  5587. IN BOOL Operation
  5588. )
  5589. {
  5590. WCHAR curChar;
  5591. WCHAR newChar;
  5592. PWSTR p = Line;
  5593. curChar = Operation ? WACK_REPLACE_CHAR : L'\\';
  5594. newChar = Operation ? L'\\' : WACK_REPLACE_CHAR;
  5595. do {
  5596. p = wcschr (p, curChar);
  5597. if (p) {
  5598. *p = newChar;
  5599. p++;
  5600. }
  5601. } while (p);
  5602. }
  5603. PWSTR
  5604. our_lstrcpynW (
  5605. OUT PWSTR Dest,
  5606. IN PCWSTR Src,
  5607. IN INT NumChars
  5608. )
  5609. {
  5610. PCWSTR srcEnd;
  5611. __try {
  5612. if (NumChars > 0) {
  5613. //
  5614. // assuming we wrote this because lstrcpyn has problems... we
  5615. // cannot use wcsncpy, because it fills the entire Dest buffer
  5616. // with nuls when WcharCount(Src) < NumChars - 1. That just
  5617. // wastes time.
  5618. //
  5619. srcEnd = Src + NumChars - 1;
  5620. while (*Src && Src < srcEnd) {
  5621. *Dest++ = *Src++;
  5622. }
  5623. *Dest = 0;
  5624. }
  5625. }
  5626. __except (1) {
  5627. }
  5628. return Dest;
  5629. }
  5630. PSTR
  5631. pGoBackA (
  5632. IN PSTR LastChar,
  5633. IN PSTR FirstChar,
  5634. IN UINT NumWacks
  5635. )
  5636. {
  5637. LastChar = _mbsdec (FirstChar, LastChar);
  5638. while (NumWacks && (LastChar>=FirstChar)) {
  5639. if (_mbsnextc (LastChar) == '\\') {
  5640. NumWacks --;
  5641. }
  5642. LastChar = _mbsdec (FirstChar, LastChar);
  5643. }
  5644. if (NumWacks) {
  5645. return NULL;
  5646. }
  5647. return LastChar + 2;
  5648. }
  5649. PWSTR
  5650. pGoBackW (
  5651. IN PWSTR LastChar,
  5652. IN PWSTR FirstChar,
  5653. IN UINT NumWacks
  5654. )
  5655. {
  5656. LastChar --;
  5657. while (NumWacks && (LastChar>=FirstChar)) {
  5658. if (*LastChar == L'\\') {
  5659. NumWacks --;
  5660. }
  5661. LastChar --;
  5662. }
  5663. if (NumWacks) {
  5664. return NULL;
  5665. }
  5666. return LastChar + 2;
  5667. }
  5668. UINT
  5669. pCountDotsA (
  5670. IN PCSTR PathSeg
  5671. )
  5672. {
  5673. UINT numDots = 0;
  5674. while (PathSeg && *PathSeg) {
  5675. if (_mbsnextc (PathSeg) != '.') {
  5676. return 0;
  5677. }
  5678. numDots ++;
  5679. PathSeg = _mbsinc (PathSeg);
  5680. }
  5681. return numDots;
  5682. }
  5683. UINT
  5684. pCountDotsW (
  5685. IN PCWSTR PathSeg
  5686. )
  5687. {
  5688. UINT numDots = 0;
  5689. while (PathSeg && *PathSeg) {
  5690. if (*PathSeg != L'.') {
  5691. return 0;
  5692. }
  5693. numDots ++;
  5694. PathSeg ++;
  5695. }
  5696. return numDots;
  5697. }
  5698. PCSTR
  5699. SanitizePathA (
  5700. IN PCSTR FileSpec
  5701. )
  5702. {
  5703. CHAR pathSeg [MEMDB_MAX];
  5704. PCSTR wackPtr;
  5705. UINT dotNr;
  5706. PSTR newPath = DuplicatePathStringA (FileSpec, 0);
  5707. PSTR newPathPtr = newPath;
  5708. BOOL firstPass = TRUE;
  5709. do {
  5710. wackPtr = _mbschr (FileSpec, '\\');
  5711. if (wackPtr) {
  5712. if (firstPass && (wackPtr == FileSpec)) {
  5713. // this one starts with a wack, let's see if we have double wacks
  5714. wackPtr = _mbsinc (wackPtr);
  5715. if (!wackPtr) {
  5716. FreePathStringA (newPath);
  5717. return NULL;
  5718. }
  5719. if (_mbsnextc (wackPtr) == '\\') {
  5720. // this one starts with a double wack
  5721. wackPtr = _mbsinc (wackPtr);
  5722. if (!wackPtr) {
  5723. FreePathStringA (newPath);
  5724. return NULL;
  5725. }
  5726. wackPtr = _mbschr (wackPtr, '\\');
  5727. } else {
  5728. wackPtr = _mbschr (wackPtr, '\\');
  5729. }
  5730. }
  5731. firstPass = FALSE;
  5732. if (wackPtr) {
  5733. _mbssafecpyab (pathSeg, FileSpec, wackPtr, MEMDB_MAX);
  5734. FileSpec = _mbsinc (wackPtr);
  5735. } else {
  5736. _mbssafecpyab (pathSeg, FileSpec, GetEndOfStringA (FileSpec), MEMDB_MAX);
  5737. }
  5738. } else {
  5739. _mbssafecpyab (pathSeg, FileSpec, GetEndOfStringA (FileSpec), MEMDB_MAX);
  5740. }
  5741. if (*pathSeg) {
  5742. dotNr = pCountDotsA (pathSeg);
  5743. if (dotNr>1) {
  5744. newPathPtr = pGoBackA (newPathPtr, newPath, dotNr);
  5745. if (newPathPtr == NULL) {
  5746. DEBUGMSGA ((DBG_WARNING, "Broken path detected:%s", FileSpec));
  5747. FreePathStringA (newPath);
  5748. return NULL;
  5749. }
  5750. } else {
  5751. StringCopyA (newPathPtr, pathSeg);
  5752. newPathPtr = GetEndOfStringA (newPathPtr);
  5753. if (wackPtr) {
  5754. *newPathPtr = '\\';
  5755. //we increment this because we know that \ is a single byte character.
  5756. newPathPtr ++;
  5757. }
  5758. }
  5759. }
  5760. } while (wackPtr);
  5761. *newPathPtr = 0;
  5762. return newPath;
  5763. }
  5764. PCWSTR
  5765. SanitizePathW (
  5766. IN PCWSTR FileSpec
  5767. )
  5768. {
  5769. WCHAR pathSeg [MEMDB_MAX];
  5770. PCWSTR wackPtr;
  5771. UINT dotNr;
  5772. PWSTR newPath = DuplicatePathStringW (FileSpec, 0);
  5773. PWSTR newPathPtr = newPath;
  5774. BOOL firstPass = TRUE;
  5775. do {
  5776. wackPtr = wcschr (FileSpec, L'\\');
  5777. if (wackPtr) {
  5778. if (firstPass && (wackPtr == FileSpec)) {
  5779. // this one starts with a wack, let's see if we have double wacks
  5780. wackPtr ++;
  5781. if (*wackPtr == 0) {
  5782. FreePathStringW (newPath);
  5783. return NULL;
  5784. }
  5785. if (*wackPtr == L'\\') {
  5786. // this one starts with a double wack
  5787. wackPtr ++;
  5788. if (!wackPtr) {
  5789. FreePathStringW (newPath);
  5790. return NULL;
  5791. }
  5792. wackPtr = wcschr (wackPtr, L'\\');
  5793. } else {
  5794. wackPtr = wcschr (wackPtr, L'\\');
  5795. }
  5796. }
  5797. firstPass = FALSE;
  5798. if (wackPtr) {
  5799. _wcssafecpyab(pathSeg, FileSpec, wackPtr, MEMDB_MAX * sizeof (WCHAR));
  5800. FileSpec = wackPtr + 1;
  5801. } else {
  5802. _wcssafecpyab(pathSeg, FileSpec, GetEndOfStringW (FileSpec), MEMDB_MAX * sizeof (WCHAR));
  5803. }
  5804. } else {
  5805. _wcssafecpyab(pathSeg, FileSpec, GetEndOfStringW (FileSpec), MEMDB_MAX * sizeof (WCHAR));
  5806. }
  5807. if (*pathSeg) {
  5808. dotNr = pCountDotsW (pathSeg);
  5809. if (dotNr>1) {
  5810. newPathPtr = pGoBackW (newPathPtr, newPath, dotNr);
  5811. if (newPathPtr == NULL) {
  5812. DEBUGMSGW ((DBG_WARNING, "Broken path detected:%s", FileSpec));
  5813. FreePathStringW (newPath);
  5814. return NULL;
  5815. }
  5816. } else {
  5817. StringCopyW (newPathPtr, pathSeg);
  5818. newPathPtr = GetEndOfStringW (newPathPtr);
  5819. if (wackPtr) {
  5820. *newPathPtr = L'\\';
  5821. newPathPtr ++;
  5822. }
  5823. }
  5824. }
  5825. } while (wackPtr);
  5826. *newPathPtr = 0;
  5827. return newPath;
  5828. }
  5829. typedef struct {
  5830. UINT char1;
  5831. UINT char2;
  5832. UINT result;
  5833. } DHLIST, *PDHLIST;
  5834. DHLIST g_DHList[] = {{0xB3, 0xDE, 0x8394},
  5835. {0xB6, 0xDE, 0x834B},
  5836. {0xB7, 0xDE, 0x834D},
  5837. {0xB8, 0xDE, 0x834F},
  5838. {0xB9, 0xDE, 0x8351},
  5839. {0xBA, 0xDE, 0x8353},
  5840. {0xBB, 0xDE, 0x8355},
  5841. {0xBC, 0xDE, 0x8357},
  5842. {0xBD, 0xDE, 0x8359},
  5843. {0xBE, 0xDE, 0x835B},
  5844. {0xBF, 0xDE, 0x835D},
  5845. {0xC0, 0xDE, 0x835F},
  5846. {0xC1, 0xDE, 0x8361},
  5847. {0xC2, 0xDE, 0x8364},
  5848. {0xC3, 0xDE, 0x8366},
  5849. {0xC4, 0xDE, 0x8368},
  5850. {0xCA, 0xDE, 0x836F},
  5851. {0xCB, 0xDE, 0x8372},
  5852. {0xCC, 0xDE, 0x8375},
  5853. {0xCD, 0xDE, 0x8378},
  5854. {0xCE, 0xDE, 0x837B},
  5855. {0xCA, 0xDF, 0x8370},
  5856. {0xCB, 0xDF, 0x8373},
  5857. {0xCC, 0xDF, 0x8376},
  5858. {0xCD, 0xDF, 0x8379},
  5859. {0xCE, 0xDF, 0x837C},
  5860. {0x00, 0x00, 0x0000}};
  5861. UINT
  5862. pBuildFromDHList (
  5863. IN UINT ch1,
  5864. IN UINT ch2
  5865. )
  5866. {
  5867. PDHLIST p;
  5868. UINT result = 0;
  5869. p = g_DHList;
  5870. while (p->char1) {
  5871. if ((p->char1 == ch1) && (p->char2 == ch2)) {
  5872. result = p->result;
  5873. break;
  5874. }
  5875. p++;
  5876. }
  5877. return result;
  5878. }
  5879. VOID
  5880. _mbssetchar (
  5881. PSTR Dest,
  5882. UINT Char
  5883. )
  5884. {
  5885. if (Char >= 256) {
  5886. *(Dest+1) = *((PBYTE)(&Char));
  5887. *(Dest) = *((PBYTE)((DWORD)(&Char) + 1));
  5888. }
  5889. else {
  5890. *Dest = (CHAR)Char;
  5891. }
  5892. }
  5893. PCSTR
  5894. ConvertSBtoDB (
  5895. PCSTR RootPath,
  5896. PCSTR FullPath,
  5897. PCSTR Limit
  5898. )
  5899. {
  5900. CHAR result[MEMDB_MAX];
  5901. PCSTR p,p1,q;
  5902. PSTR s;
  5903. UINT ch;
  5904. UINT ch1;
  5905. BOOL dhCase = FALSE;
  5906. ZeroMemory (result, MAX_PATH);
  5907. p = FullPath;
  5908. q = RootPath;
  5909. s = result;
  5910. while (*p && (((DWORD)s - (DWORD)result) < MEMDB_MAX)) {
  5911. if (q && *q) {
  5912. _mbssetchar (s, _mbsnextc(p));
  5913. q = _mbsinc (q);
  5914. } else if (Limit && (p >= Limit)) {
  5915. _mbssetchar (s, _mbsnextc(p));
  5916. } else {
  5917. ch = _mbsnextc (p);
  5918. //
  5919. // It is very important not to make the conversion for characters below A1. Otherwise
  5920. // all english letters will be converted to large letters.
  5921. //
  5922. if (ch >= 0xA1 && ch <= 0xDF) {
  5923. // this is a candidate for conversion
  5924. // we need to see if there is a special Dakutenn/Handakuten conversion
  5925. dhCase = FALSE;
  5926. p1 = _mbsinc (p);
  5927. if (p1) {
  5928. ch1 = _mbsnextc (p1);
  5929. ch1 = pBuildFromDHList (ch, ch1);
  5930. if (ch1) {
  5931. p = _mbsinc (p);
  5932. _mbssetchar (s, ch1);
  5933. dhCase = TRUE;
  5934. }
  5935. }
  5936. if (!dhCase) {
  5937. _mbssetchar (s, _mbbtombc (ch));
  5938. }
  5939. } else {
  5940. _mbssetchar (s, ch);
  5941. }
  5942. }
  5943. p = _mbsinc (p);
  5944. s = _mbsinc (s);
  5945. }
  5946. result [MAX_PATH - 1] = 0;
  5947. return (DuplicatePathString (result, 0));
  5948. }