Leaked source code of windows server 2003
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.

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