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.

1489 lines
37 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. strmap.c
  5. Abstract:
  6. The SzMap routines are used to do fast search and replace. The table is
  7. built of a linked list of characters, so that finding a string is
  8. simply a matter of walking down a linked list. These routines perform
  9. quite well for general search & replace use.
  10. Also, it is common to use string maps as index tables for certain
  11. types of data where search strings often repeat the same left side of
  12. the string (such as paths). In this case, often the replacement string is
  13. empty.
  14. Author:
  15. Marc R. Whitten (marcw) 20-Mar-1997
  16. Revision History:
  17. Jim Schmidt (jimschm) 05-Jun-2000 Added multi table capability
  18. Jim Schmidt (jimschm) 08-May-2000 Improved replacement routines and
  19. added consistent filtering and
  20. extra data option
  21. Jim Schmidt (jimschm) 18-Aug-1998 Redesigned to fix two bugs, made
  22. A & W versions
  23. --*/
  24. //
  25. // Includes
  26. //
  27. #include "pch.h"
  28. #include "commonp.h"
  29. // BUGBUG - remove this
  30. __inline
  31. BOOL
  32. SzIsLeadByte (
  33. MBCHAR ch
  34. )
  35. {
  36. MYASSERT (FALSE);
  37. return FALSE;
  38. }
  39. //
  40. // Strings
  41. //
  42. // None
  43. //
  44. // Constants
  45. //
  46. #define CHARNODE_SINGLE_BYTE 0x0000
  47. #define CHARNODE_DOUBLE_BYTE 0x0001
  48. #define CHARNODE_REQUIRE_WACK_OR_NUL 0x0002
  49. //
  50. // Macros
  51. //
  52. // None
  53. //
  54. // Types
  55. //
  56. typedef struct {
  57. PVOID Next;
  58. BYTE Memory[];
  59. } MAPALLOC, *PMAPALLOC;
  60. //
  61. // Globals
  62. //
  63. // None
  64. //
  65. // Macro expansion list
  66. //
  67. // None
  68. //
  69. // Private function prototypes
  70. //
  71. // None
  72. //
  73. // Macro expansion definition
  74. //
  75. // None
  76. //
  77. // Code
  78. //
  79. PVOID
  80. pAllocateInMap (
  81. IN PSTRINGMAP Map,
  82. IN UINT Bytes
  83. )
  84. {
  85. PMAPALLOC alloc;
  86. alloc = (PMAPALLOC) MALLOC_UNINIT (Bytes + sizeof (MAPALLOC));
  87. MYASSERT (alloc);
  88. alloc->Next = Map->CleanUpChain;
  89. Map->CleanUpChain = alloc;
  90. return alloc->Memory;
  91. }
  92. PSTR
  93. pDupInMapA (
  94. IN PSTRINGMAP Map,
  95. IN PCSTR String
  96. )
  97. {
  98. UINT bytes;
  99. PSTR result;
  100. bytes = SzSizeA (String);
  101. result = pAllocateInMap (Map, bytes);
  102. MYASSERT (result);
  103. CopyMemory (result, String, bytes);
  104. return result;
  105. }
  106. PWSTR
  107. pDupInMapW (
  108. IN PSTRINGMAP Map,
  109. IN PCWSTR String
  110. )
  111. {
  112. UINT bytes;
  113. PWSTR result;
  114. bytes = SzSizeW (String);
  115. result = pAllocateInMap (Map, bytes);
  116. MYASSERT (result);
  117. CopyMemory (result, String, bytes);
  118. return result;
  119. }
  120. PSTRINGMAP
  121. SzMapCreateEx (
  122. IN BOOL UsesFilters,
  123. IN BOOL UsesExtraData
  124. )
  125. /*++
  126. Routine Description:
  127. SzMapCreateEx allocates a string mapping data structure and initializes it.
  128. Callers can enable filter callbacks, extra data support, or both. The
  129. mapping structure contains either CHARNODE elements, or CHARNODEEX elements,
  130. depending on the UsesFilters or UsesExtraData flag.
  131. Arguments:
  132. UsesFilters - Specifies TRUE to enable filter callbacks. If enabled,
  133. those who add string pairs must specify the filter callback
  134. (each search/replace pair has its own callback)
  135. UsesExtraData - Specifies TRUE to associate extra data with the string
  136. mapping pair.
  137. Return Value:
  138. A handle to the string mapping structure, or NULL if a structure could not
  139. be created.
  140. --*/
  141. {
  142. PSTRINGMAP map;
  143. map = (PSTRINGMAP) MALLOC_ZEROED (sizeof (STRINGMAP));
  144. MYASSERT (map);
  145. map->UsesExNode = UsesFilters|UsesExtraData;
  146. map->UsesFilter = UsesFilters;
  147. map->UsesExtraData = UsesExtraData;
  148. map->CleanUpChain = NULL;
  149. return map;
  150. }
  151. VOID
  152. SzMapDestroy (
  153. IN PSTRINGMAP Map
  154. )
  155. {
  156. PMAPALLOC next;
  157. PMAPALLOC current;
  158. if (Map) {
  159. next = (PMAPALLOC) Map->CleanUpChain;
  160. while (next) {
  161. current = next;
  162. next = current->Next;
  163. FREE(current);
  164. }
  165. FREE(Map);
  166. }
  167. }
  168. PCHARNODE
  169. pFindCharNode (
  170. IN PSTRINGMAP Map,
  171. IN PCHARNODE PrevNode, OPTIONAL
  172. IN WORD Char
  173. )
  174. {
  175. PCHARNODE Node;
  176. if (!PrevNode) {
  177. Node = Map->FirstLevelRoot;
  178. } else {
  179. Node = PrevNode->NextLevel;
  180. }
  181. while (Node) {
  182. if (Node->Char == Char) {
  183. return Node;
  184. }
  185. Node = Node->NextPeer;
  186. }
  187. return NULL;
  188. }
  189. PCHARNODE
  190. pAddCharNode (
  191. IN PSTRINGMAP Map,
  192. IN PCHARNODE PrevNode, OPTIONAL
  193. IN WORD Char,
  194. IN WORD Flags
  195. )
  196. {
  197. PCHARNODE Node;
  198. PCHARNODEEX exNode;
  199. if (Map->UsesExNode) {
  200. exNode = pAllocateInMap (Map, sizeof (CHARNODEEX));
  201. Node = (PCHARNODE) exNode;
  202. MYASSERT (Node);
  203. ZeroMemory (exNode, sizeof (CHARNODEEX));
  204. } else {
  205. Node = pAllocateInMap (Map, sizeof (CHARNODE));
  206. MYASSERT (Node);
  207. ZeroMemory (Node, sizeof (CHARNODE));
  208. }
  209. Node->Char = Char;
  210. Node->Flags = Flags;
  211. if (PrevNode) {
  212. Node->NextPeer = PrevNode->NextLevel;
  213. PrevNode->NextLevel = Node;
  214. } else {
  215. Node->NextPeer = Map->FirstLevelRoot;
  216. Map->FirstLevelRoot = Node;
  217. }
  218. return Node;
  219. }
  220. VOID
  221. SzMapAddExA (
  222. IN OUT PSTRINGMAP Map,
  223. IN PCSTR Old,
  224. IN PCSTR New,
  225. IN STRINGMAP_FILTER Filter, OPTIONAL
  226. IN ULONG_PTR ExtraData, OPTIONAL
  227. IN DWORD Flags
  228. )
  229. /*++
  230. Routine Description:
  231. SzMapAddEx adds a search and replace string pair to the linked list data
  232. structures. If the same search string is already in the structures, then the
  233. replace string and optional extra data is updated.
  234. Arguments:
  235. Map - Specifies the string mapping
  236. Old - Specifies the search string
  237. New - Specifies the replace string
  238. Filter - Specifies the callback filter. This is only supported if the
  239. map was created with filter support enabled.
  240. ExtraData - Specifies arbitrary data to assign to the search/replace pair.
  241. This is only valid if the map was created with extra data
  242. enabled.
  243. Flags - Specifies optional flag STRINGMAP_REQUIRE_WACK_OR_NUL
  244. Return Value:
  245. None.
  246. --*/
  247. {
  248. PSTR oldCopy;
  249. PSTR newCopy;
  250. PCSTR p;
  251. WORD w;
  252. PCHARNODE prev;
  253. PCHARNODE Node;
  254. PCHARNODEEX exNode;
  255. WORD nodeFlags = 0;
  256. if (Flags & SZMAP_REQUIRE_WACK_OR_NUL) {
  257. nodeFlags = CHARNODE_REQUIRE_WACK_OR_NUL;
  258. }
  259. MYASSERT (Map);
  260. MYASSERT (Old);
  261. MYASSERT (New);
  262. MYASSERT (*Old);
  263. //
  264. // Duplicate strings
  265. //
  266. oldCopy = pDupInMapA (Map, Old);
  267. newCopy = pDupInMapA (Map, New);
  268. //
  269. // Make oldCopy all lowercase
  270. //
  271. CharLowerA (oldCopy);
  272. //
  273. // Add the letters that are not in the mapping
  274. //
  275. for (prev = NULL, p = oldCopy ; *p ; p = _mbsinc (p)) {
  276. w = (WORD) _mbsnextc (p);
  277. Node = pFindCharNode (Map, prev, w);
  278. if (!Node) {
  279. break;
  280. }
  281. prev = Node;
  282. }
  283. for ( ; *p ; p = _mbsinc (p)) {
  284. w = (WORD) _mbsnextc (p);
  285. nodeFlags |= (WORD) (SzIsLeadByte (*p) ? CHARNODE_DOUBLE_BYTE : CHARNODE_SINGLE_BYTE);
  286. prev = pAddCharNode (Map, prev, w, nodeFlags);
  287. }
  288. if (prev) {
  289. SzCopyA (oldCopy, Old);
  290. prev->OriginalStr = (PVOID) oldCopy;
  291. prev->ReplacementStr = (PVOID) newCopy;
  292. prev->ReplacementBytes = SzByteCountA (newCopy);
  293. exNode = (PCHARNODEEX) prev;
  294. if (Map->UsesExtraData) {
  295. exNode->ExtraData = ExtraData;
  296. }
  297. if (Map->UsesFilter) {
  298. exNode->Filter = Filter;
  299. }
  300. }
  301. }
  302. VOID
  303. SzMapAddExW (
  304. IN OUT PSTRINGMAP Map,
  305. IN PCWSTR Old,
  306. IN PCWSTR New,
  307. IN STRINGMAP_FILTER Filter, OPTIONAL
  308. IN ULONG_PTR ExtraData, OPTIONAL
  309. IN DWORD Flags
  310. )
  311. /*++
  312. Routine Description:
  313. SzMapAddEx adds a search and replace string pair to the linked list data
  314. structures. If the same search string is already in the structures, then the
  315. replace string and optional extra data is updated.
  316. Arguments:
  317. Map - Specifies the string mapping
  318. Old - Specifies the search string
  319. New - Specifies the replace string
  320. Filter - Specifies the callback filter. This is only supported if the
  321. map was created with filter support enabled.
  322. ExtraData - Specifies arbitrary data to assign to the search/replace pair.
  323. This is only valid if the map was created with extra data
  324. enabled.
  325. Flags - Specifies optional flag SZMAP_REQUIRE_WACK_OR_NUL
  326. Return Value:
  327. None.
  328. --*/
  329. {
  330. PWSTR oldCopy;
  331. PWSTR newCopy;
  332. PCWSTR p;
  333. WORD w;
  334. PCHARNODE prev;
  335. PCHARNODE node;
  336. PCHARNODEEX exNode;
  337. WORD nodeFlags = 0;
  338. if (Flags & SZMAP_REQUIRE_WACK_OR_NUL) {
  339. nodeFlags = CHARNODE_REQUIRE_WACK_OR_NUL;
  340. }
  341. MYASSERT (Map);
  342. MYASSERT (Old);
  343. MYASSERT (New);
  344. MYASSERT (*Old);
  345. //
  346. // Duplicate strings
  347. //
  348. oldCopy = pDupInMapW (Map, Old);
  349. newCopy = pDupInMapW (Map, New);
  350. //
  351. // Make oldCopy all lowercase
  352. //
  353. CharLowerW (oldCopy);
  354. //
  355. // Add the letters that are not in the mapping
  356. //
  357. prev = NULL;
  358. p = oldCopy;
  359. while (w = *p) { // intentional assignment optimization
  360. node = pFindCharNode (Map, prev, w);
  361. if (!node) {
  362. break;
  363. }
  364. prev = node;
  365. p++;
  366. }
  367. while (w = *p) { // intentional assignment optimization
  368. prev = pAddCharNode (Map, prev, w, nodeFlags);
  369. p++;
  370. }
  371. if (prev) {
  372. SzCopyW (oldCopy, Old);
  373. prev->OriginalStr = oldCopy;
  374. prev->ReplacementStr = (PVOID) newCopy;
  375. prev->ReplacementBytes = SzByteCountW (newCopy);
  376. exNode = (PCHARNODEEX) prev;
  377. if (Map->UsesExtraData) {
  378. exNode->ExtraData = ExtraData;
  379. }
  380. if (Map->UsesFilter) {
  381. exNode->Filter = Filter;
  382. }
  383. }
  384. }
  385. PCSTR
  386. pFindReplacementStringInOneMapA (
  387. IN PSTRINGMAP Map,
  388. IN PCSTR Source,
  389. IN INT MaxSourceBytes,
  390. OUT PINT SourceBytesPtr,
  391. OUT PINT ReplacementBytesPtr,
  392. IN PSTRINGMAP_FILTER_DATA Data,
  393. OUT ULONG_PTR *ExtraDataValue, OPTIONAL
  394. IN BOOL RequireWackOrNul
  395. )
  396. {
  397. PCHARNODE bestMatch;
  398. PCHARNODE node;
  399. WORD mbChar;
  400. PCSTR OrgSource;
  401. PCSTR newString = NULL;
  402. INT newStringSizeInBytes = 0;
  403. PCHARNODEEX exNode;
  404. BOOL replacementFound;
  405. *SourceBytesPtr = 0;
  406. node = NULL;
  407. bestMatch = NULL;
  408. OrgSource = Source;
  409. while (*Source) {
  410. mbChar = (WORD) _mbsnextc (Source);
  411. node = pFindCharNode (Map, node, mbChar);
  412. if (node) {
  413. //
  414. // Advance string pointer
  415. //
  416. if (node->Flags & CHARNODE_DOUBLE_BYTE) {
  417. Source += 2;
  418. } else {
  419. Source++;
  420. }
  421. if (((PBYTE) Source - (PBYTE) OrgSource) > MaxSourceBytes) {
  422. break;
  423. }
  424. //
  425. // If replacement string is available, keep it
  426. // until a longer match comes along
  427. //
  428. replacementFound = (node->ReplacementStr != NULL);
  429. if ((RequireWackOrNul || (node->Flags & CHARNODE_REQUIRE_WACK_OR_NUL)) && replacementFound) {
  430. if (*Source && _mbsnextc (Source) != '\\') {
  431. replacementFound = FALSE;
  432. }
  433. }
  434. if (replacementFound) {
  435. newString = (PCSTR) node->ReplacementStr;
  436. newStringSizeInBytes = node->ReplacementBytes;
  437. if (Map->UsesFilter) {
  438. //
  439. // Call rename filter to allow denial of match
  440. //
  441. exNode = (PCHARNODEEX) node;
  442. if (exNode->Filter) {
  443. Data->Ansi.BeginningOfMatch = OrgSource;
  444. Data->Ansi.OldSubString = (PCSTR) node->OriginalStr;
  445. Data->Ansi.NewSubString = newString;
  446. Data->Ansi.NewSubStringSizeInBytes = newStringSizeInBytes;
  447. if (!exNode->Filter (Data)) {
  448. replacementFound = FALSE;
  449. } else {
  450. newString = Data->Ansi.NewSubString;
  451. newStringSizeInBytes = Data->Ansi.NewSubStringSizeInBytes;
  452. }
  453. }
  454. }
  455. if (replacementFound) {
  456. bestMatch = node;
  457. *SourceBytesPtr = (HALF_PTR) ((PBYTE) Source - (PBYTE) OrgSource);
  458. }
  459. }
  460. } else {
  461. //
  462. // No node ends the search
  463. //
  464. break;
  465. }
  466. }
  467. if (bestMatch) {
  468. //
  469. // Return replacement data to caller
  470. //
  471. if (ExtraDataValue) {
  472. if (Map->UsesExtraData) {
  473. exNode = (PCHARNODEEX) bestMatch;
  474. *ExtraDataValue = exNode->ExtraData;
  475. } else {
  476. *ExtraDataValue = 0;
  477. }
  478. }
  479. *ReplacementBytesPtr = newStringSizeInBytes;
  480. return newString;
  481. }
  482. return NULL;
  483. }
  484. PCSTR
  485. pFindReplacementStringA (
  486. IN PSTRINGMAP *MapArray,
  487. IN UINT MapArrayCount,
  488. IN PCSTR Source,
  489. IN INT MaxSourceBytes,
  490. OUT PINT SourceBytesPtr,
  491. OUT PINT ReplacementBytesPtr,
  492. IN PSTRINGMAP_FILTER_DATA Data,
  493. OUT ULONG_PTR *ExtraDataValue, OPTIONAL
  494. IN BOOL RequireWackOrNul
  495. )
  496. {
  497. UINT u;
  498. PCSTR result;
  499. for (u = 0 ; u < MapArrayCount ; u++) {
  500. if (!MapArray[u]) {
  501. continue;
  502. }
  503. result = pFindReplacementStringInOneMapA (
  504. MapArray[u],
  505. Source,
  506. MaxSourceBytes,
  507. SourceBytesPtr,
  508. ReplacementBytesPtr,
  509. Data,
  510. ExtraDataValue,
  511. RequireWackOrNul
  512. );
  513. if (result) {
  514. return result;
  515. }
  516. }
  517. return NULL;
  518. }
  519. PCWSTR
  520. pFindReplacementStringInOneMapW (
  521. IN PSTRINGMAP Map,
  522. IN PCWSTR Source,
  523. IN INT MaxSourceBytes,
  524. OUT PINT SourceBytesPtr,
  525. OUT PINT ReplacementBytesPtr,
  526. IN PSTRINGMAP_FILTER_DATA Data,
  527. OUT ULONG_PTR *ExtraDataValue, OPTIONAL
  528. IN BOOL RequireWackOrNul
  529. )
  530. {
  531. PCHARNODE bestMatch;
  532. PCHARNODE node;
  533. PCWSTR OrgSource;
  534. PCWSTR newString = NULL;
  535. INT newStringSizeInBytes;
  536. BOOL replacementFound;
  537. PCHARNODEEX exNode;
  538. *SourceBytesPtr = 0;
  539. node = NULL;
  540. bestMatch = NULL;
  541. OrgSource = Source;
  542. while (*Source) {
  543. node = pFindCharNode (Map, node, *Source);
  544. if (node) {
  545. //
  546. // Advance string pointer
  547. //
  548. Source++;
  549. if (((PBYTE) Source - (PBYTE) OrgSource) > MaxSourceBytes) {
  550. break;
  551. }
  552. //
  553. // If replacement string is available, keep it
  554. // until a longer match comes along
  555. //
  556. replacementFound = (node->ReplacementStr != NULL);
  557. if ((RequireWackOrNul || (node->Flags & CHARNODE_REQUIRE_WACK_OR_NUL)) && replacementFound) {
  558. if (*Source && *Source != L'\\') {
  559. replacementFound = FALSE;
  560. }
  561. }
  562. if (replacementFound) {
  563. newString = (PCWSTR) node->ReplacementStr;
  564. newStringSizeInBytes = node->ReplacementBytes;
  565. if (Map->UsesFilter) {
  566. //
  567. // Call rename filter to allow denial of match
  568. //
  569. exNode = (PCHARNODEEX) node;
  570. if (exNode->Filter) {
  571. Data->Unicode.BeginningOfMatch = OrgSource;
  572. Data->Unicode.OldSubString = (PCWSTR) node->OriginalStr;
  573. Data->Unicode.NewSubString = newString;
  574. Data->Unicode.NewSubStringSizeInBytes = newStringSizeInBytes;
  575. if (!exNode->Filter (Data)) {
  576. replacementFound = FALSE;
  577. } else {
  578. newString = Data->Unicode.NewSubString;
  579. newStringSizeInBytes = Data->Unicode.NewSubStringSizeInBytes;
  580. }
  581. }
  582. }
  583. if (replacementFound) {
  584. bestMatch = node;
  585. *SourceBytesPtr = (HALF_PTR) ((PBYTE) Source - (PBYTE) OrgSource);
  586. }
  587. }
  588. } else {
  589. //
  590. // No node ends the search
  591. //
  592. break;
  593. }
  594. }
  595. if (bestMatch) {
  596. //
  597. // Return replacement data to caller
  598. //
  599. if (ExtraDataValue) {
  600. if (Map->UsesExtraData) {
  601. exNode = (PCHARNODEEX) bestMatch;
  602. *ExtraDataValue = exNode->ExtraData;
  603. } else {
  604. *ExtraDataValue = 0;
  605. }
  606. }
  607. *ReplacementBytesPtr = newStringSizeInBytes;
  608. return newString;
  609. }
  610. return NULL;
  611. }
  612. PCWSTR
  613. pFindReplacementStringW (
  614. IN PSTRINGMAP *MapArray,
  615. IN UINT MapArrayCount,
  616. IN PCWSTR Source,
  617. IN INT MaxSourceBytes,
  618. OUT PINT SourceBytesPtr,
  619. OUT PINT ReplacementBytesPtr,
  620. IN PSTRINGMAP_FILTER_DATA Data,
  621. OUT ULONG_PTR *ExtraDataValue, OPTIONAL
  622. IN BOOL RequireWackOrNul
  623. )
  624. {
  625. UINT u;
  626. PCWSTR result;
  627. for (u = 0 ; u < MapArrayCount ; u++) {
  628. if (!MapArray[u]) {
  629. continue;
  630. }
  631. result = pFindReplacementStringInOneMapW (
  632. MapArray[u],
  633. Source,
  634. MaxSourceBytes,
  635. SourceBytesPtr,
  636. ReplacementBytesPtr,
  637. Data,
  638. ExtraDataValue,
  639. RequireWackOrNul
  640. );
  641. if (result) {
  642. return result;
  643. }
  644. }
  645. return NULL;
  646. }
  647. BOOL
  648. SzMapMultiTableSearchAndReplaceExA (
  649. IN PSTRINGMAP *MapArray,
  650. IN UINT MapArrayCount,
  651. IN PCSTR SrcBuffer,
  652. OUT PSTR Buffer, // can be the same as SrcBuffer
  653. IN INT InboundBytes, OPTIONAL
  654. OUT PINT OutboundBytesPtr, OPTIONAL
  655. IN INT MaxSizeInBytes,
  656. IN DWORD Flags,
  657. OUT ULONG_PTR *ExtraDataValue, OPTIONAL
  658. OUT PCSTR *EndOfString OPTIONAL
  659. )
  660. /*++
  661. Routine Description:
  662. SzMapSearchAndReplaceEx performs a search/replace operation based on the
  663. specified string mapping. The replace can be in-place or to another buffer.
  664. Arguments:
  665. MapArray - Specifies an array of string mapping tables that holds
  666. zero or more search/replace pairs
  667. MapArrayCount - Specifies the number of mapping tables in MapArray
  668. SrcBuffer - Specifies the source string that might contain one or
  669. more search strings
  670. Buffer - Specifies the outbound buffer. This arg can be the same
  671. as SrcBuffer.
  672. InboundBytes - Specifies the number of bytes in SrcBuffer to process,
  673. or 0 to process a nul-terminated string in SrcBuffer.
  674. If InboundBytes is specified, it must point to the nul
  675. terminator of SrcBuffer.
  676. OutbountBytesPtr - Receives the number of bytes that Buffer contains,
  677. excluding the nul terminator.
  678. MaxSizeInBytes - Specifies the size of Buffer, in bytes.
  679. Flags - Specifies flags that control the search/replace:
  680. SZMAP_COMPLETE_MATCH_ONLY
  681. SZMAP_FIRST_CHAR_MUST_MATCH
  682. SZMAP_RETURN_AFTER_FIRST_REPLACE
  683. SZMAP_REQUIRE_WACK_OR_NUL
  684. ExtraDataValue - Receives the extra data associated with the first search/
  685. replace pair.
  686. EndOfString - Receives a pointer to the end of the replace string, or
  687. the nul pointer when the entire string is processed. The
  688. pointer is within the string contained in Buffer.
  689. --*/
  690. {
  691. UINT sizeOfTempBuf;
  692. INT inboundSize;
  693. PCSTR lowerCaseSrc;
  694. PCSTR orgSrc;
  695. PCSTR lowerSrcPos;
  696. PCSTR orgSrcPos;
  697. INT orgSrcBytesLeft;
  698. PSTR destPos;
  699. PCSTR lowerSrcEnd;
  700. INT searchStringBytes;
  701. INT replaceStringBytes;
  702. INT destBytesLeft;
  703. STRINGMAP_FILTER_DATA filterData;
  704. PCSTR replaceString;
  705. BOOL result = FALSE;
  706. INT i;
  707. PCSTR endPtr;
  708. //
  709. // Empty string case
  710. //
  711. if (*SrcBuffer == 0 || MaxSizeInBytes <= sizeof (CHAR)) {
  712. if (MaxSizeInBytes >= sizeof (CHAR)) {
  713. *Buffer = 0;
  714. }
  715. if (OutboundBytesPtr) {
  716. *OutboundBytesPtr = 0;
  717. }
  718. return FALSE;
  719. }
  720. //
  721. // If caller did not specify inbound size, compute it now
  722. //
  723. if (!InboundBytes) {
  724. InboundBytes = SzByteCountA (SrcBuffer);
  725. } else {
  726. i = 0;
  727. while (i < InboundBytes) {
  728. if (SzIsLeadByte (SrcBuffer[i])) {
  729. MYASSERT (SrcBuffer[i + 1]);
  730. i += 2;
  731. } else {
  732. i++;
  733. }
  734. }
  735. if (i > InboundBytes) {
  736. InboundBytes--;
  737. }
  738. }
  739. inboundSize = InboundBytes + sizeof (CHAR);
  740. //
  741. // Allocate a buffer big enough for the lower-cased input string,
  742. // plus (optionally) a copy of the entire destination buffer. Then
  743. // copy the data to the buffer.
  744. //
  745. sizeOfTempBuf = inboundSize;
  746. if (SrcBuffer == Buffer) {
  747. sizeOfTempBuf += MaxSizeInBytes;
  748. }
  749. lowerCaseSrc = SzAllocA (sizeOfTempBuf);
  750. CopyMemory ((PSTR) lowerCaseSrc, SrcBuffer, InboundBytes);
  751. *((PSTR) ((PBYTE) lowerCaseSrc + InboundBytes)) = 0;
  752. CharLowerBuffA ((PSTR) lowerCaseSrc, InboundBytes / sizeof (CHAR));
  753. if (SrcBuffer == Buffer && !(Flags & SZMAP_COMPLETE_MATCH_ONLY)) {
  754. orgSrc = (PCSTR) ((PBYTE) lowerCaseSrc + inboundSize);
  755. //
  756. // If we are processing entire inbound string, then just copy the
  757. // whole string. Otherwise, copy the entire destination buffer, so we
  758. // don't lose data beyond the partial inbound string.
  759. //
  760. if (*((PCSTR) ((PBYTE) SrcBuffer + InboundBytes))) {
  761. CopyMemory ((PSTR) orgSrc, SrcBuffer, MaxSizeInBytes);
  762. } else {
  763. CopyMemory ((PSTR) orgSrc, SrcBuffer, inboundSize);
  764. }
  765. } else {
  766. orgSrc = SrcBuffer;
  767. }
  768. //
  769. // Walk the lower cased string, looking for strings to replace
  770. //
  771. orgSrcPos = orgSrc;
  772. lowerSrcPos = lowerCaseSrc;
  773. lowerSrcEnd = (PCSTR) ((PBYTE) lowerSrcPos + InboundBytes);
  774. destPos = Buffer;
  775. destBytesLeft = MaxSizeInBytes - sizeof (CHAR);
  776. filterData.UnicodeData = FALSE;
  777. filterData.Ansi.OriginalString = orgSrc;
  778. filterData.Ansi.CurrentString = Buffer;
  779. endPtr = NULL;
  780. while (lowerSrcPos < lowerSrcEnd) {
  781. replaceString = pFindReplacementStringA (
  782. MapArray,
  783. MapArrayCount,
  784. lowerSrcPos,
  785. (HALF_PTR) ((PBYTE) lowerSrcEnd - (PBYTE) lowerSrcPos),
  786. &searchStringBytes,
  787. &replaceStringBytes,
  788. &filterData,
  789. ExtraDataValue,
  790. (Flags & SZMAP_REQUIRE_WACK_OR_NUL) != 0
  791. );
  792. if (replaceString) {
  793. //
  794. // Implement complete match flag
  795. //
  796. if (Flags & SZMAP_COMPLETE_MATCH_ONLY) {
  797. if (InboundBytes != searchStringBytes) {
  798. break;
  799. }
  800. }
  801. result = TRUE;
  802. //
  803. // Verify replacement string isn't growing string too much. If it
  804. // is, truncate the replacement string.
  805. //
  806. if (destBytesLeft < replaceStringBytes) {
  807. //
  808. // Respect logical dbcs characters
  809. //
  810. replaceStringBytes = 0;
  811. i = 0;
  812. while (i < destBytesLeft) {
  813. MYASSERT (replaceString[i]);
  814. if (SzIsLeadByte (replaceString[i])) {
  815. MYASSERT (replaceString[i + 1]);
  816. i += 2;
  817. } else {
  818. i++;
  819. }
  820. }
  821. if (i > destBytesLeft) {
  822. destBytesLeft--;
  823. }
  824. replaceStringBytes = destBytesLeft;
  825. } else {
  826. destBytesLeft -= replaceStringBytes;
  827. }
  828. //
  829. // Transfer the memory
  830. //
  831. CopyMemory (destPos, replaceString, replaceStringBytes);
  832. destPos = (PSTR) ((PBYTE) destPos + replaceStringBytes);
  833. lowerSrcPos = (PCSTR) ((PBYTE) lowerSrcPos + searchStringBytes);
  834. orgSrcPos = (PCSTR) ((PBYTE) orgSrcPos + searchStringBytes);
  835. //
  836. // Implement single match flag
  837. //
  838. if (Flags & SZMAP_RETURN_AFTER_FIRST_REPLACE) {
  839. endPtr = destPos;
  840. break;
  841. }
  842. } else if (Flags & (SZMAP_FIRST_CHAR_MUST_MATCH|SZMAP_COMPLETE_MATCH_ONLY)) {
  843. //
  844. // This string does not match any search strings
  845. //
  846. break;
  847. } else {
  848. //
  849. // This character does not match, so copy it to the destination and
  850. // try the next string.
  851. //
  852. if (SzIsLeadByte (*orgSrcPos)) {
  853. //
  854. // Copy double-byte character
  855. //
  856. if (destBytesLeft < sizeof (CHAR) * 2) {
  857. break;
  858. }
  859. MYASSERT (sizeof (CHAR) * 2 == sizeof (WORD));
  860. *((PWORD) destPos)++ = *((PWORD) orgSrcPos)++;
  861. destBytesLeft -= sizeof (WORD);
  862. lowerSrcPos = (PCSTR) ((PBYTE) lowerSrcPos + sizeof (WORD));
  863. } else {
  864. //
  865. // Copy single-byte character
  866. //
  867. if (destBytesLeft < sizeof (CHAR)) {
  868. break;
  869. }
  870. *destPos++ = *orgSrcPos++;
  871. destBytesLeft -= sizeof (CHAR);
  872. lowerSrcPos++;
  873. }
  874. }
  875. }
  876. //
  877. // Copy any remaining part of the original source to the
  878. // destination, unless destPos == Buffer == SrcBuffer
  879. //
  880. if (destPos != SrcBuffer) {
  881. if (*orgSrcPos) {
  882. orgSrcBytesLeft = SzByteCountA (orgSrcPos);
  883. orgSrcBytesLeft = min (orgSrcBytesLeft, destBytesLeft);
  884. CopyMemory (destPos, orgSrcPos, orgSrcBytesLeft);
  885. destPos = (PSTR) ((PBYTE) destPos + orgSrcBytesLeft);
  886. }
  887. MYASSERT ((PBYTE) (destPos + 1) <= ((PBYTE) Buffer + MaxSizeInBytes));
  888. *destPos = 0;
  889. if (!endPtr) {
  890. endPtr = destPos;
  891. }
  892. } else {
  893. MYASSERT (SrcBuffer == Buffer);
  894. if (EndOfString || OutboundBytesPtr) {
  895. endPtr = SzGetEndA (destPos);
  896. }
  897. }
  898. if (EndOfString) {
  899. MYASSERT (endPtr);
  900. *EndOfString = endPtr;
  901. }
  902. if (OutboundBytesPtr) {
  903. MYASSERT (endPtr);
  904. if (*endPtr) {
  905. endPtr = SzGetEndA (endPtr);
  906. }
  907. *OutboundBytesPtr = (HALF_PTR) ((PBYTE) endPtr - (PBYTE) Buffer);
  908. }
  909. SzFreeA (lowerCaseSrc);
  910. return result;
  911. }
  912. BOOL
  913. SzMapMultiTableSearchAndReplaceExW (
  914. IN PSTRINGMAP *MapArray,
  915. IN UINT MapArrayCount,
  916. IN PCWSTR SrcBuffer,
  917. OUT PWSTR Buffer, // can be the same as SrcBuffer
  918. IN INT InboundBytes, OPTIONAL
  919. OUT PINT OutboundBytesPtr, OPTIONAL
  920. IN INT MaxSizeInBytes,
  921. IN DWORD Flags,
  922. OUT ULONG_PTR *ExtraDataValue, OPTIONAL
  923. OUT PCWSTR *EndOfString OPTIONAL
  924. )
  925. {
  926. UINT sizeOfTempBuf;
  927. INT inboundSize;
  928. PCWSTR lowerCaseSrc;
  929. PCWSTR orgSrc;
  930. PCWSTR lowerSrcPos;
  931. PCWSTR orgSrcPos;
  932. INT orgSrcBytesLeft;
  933. PWSTR destPos;
  934. PCWSTR lowerSrcEnd;
  935. INT searchStringBytes;
  936. INT replaceStringBytes;
  937. INT destBytesLeft;
  938. STRINGMAP_FILTER_DATA filterData;
  939. PCWSTR replaceString;
  940. BOOL result = FALSE;
  941. PCWSTR endPtr;
  942. //
  943. // Empty string case
  944. //
  945. if (*SrcBuffer == 0 || MaxSizeInBytes <= sizeof (CHAR)) {
  946. if (MaxSizeInBytes >= sizeof (CHAR)) {
  947. *Buffer = 0;
  948. }
  949. if (OutboundBytesPtr) {
  950. *OutboundBytesPtr = 0;
  951. }
  952. return FALSE;
  953. }
  954. //
  955. // If caller did not specify inbound size, compute it now
  956. //
  957. if (!InboundBytes) {
  958. InboundBytes = SzByteCountW (SrcBuffer);
  959. } else {
  960. InboundBytes = (InboundBytes / sizeof (WCHAR)) * sizeof (WCHAR);
  961. }
  962. inboundSize = InboundBytes + sizeof (WCHAR);
  963. //
  964. // Allocate a buffer big enough for the lower-cased input string,
  965. // plus (optionally) a copy of the entire destination buffer. Then
  966. // copy the data to the buffer.
  967. //
  968. sizeOfTempBuf = inboundSize;
  969. if (SrcBuffer == Buffer) {
  970. sizeOfTempBuf += MaxSizeInBytes;
  971. }
  972. lowerCaseSrc = SzAllocW (sizeOfTempBuf);
  973. CopyMemory ((PWSTR) lowerCaseSrc, SrcBuffer, InboundBytes);
  974. *((PWSTR) ((PBYTE) lowerCaseSrc + InboundBytes)) = 0;
  975. CharLowerBuffW ((PWSTR) lowerCaseSrc, InboundBytes / sizeof (WCHAR));
  976. if (SrcBuffer == Buffer && !(Flags & SZMAP_COMPLETE_MATCH_ONLY)) {
  977. orgSrc = (PCWSTR) ((PBYTE) lowerCaseSrc + inboundSize);
  978. //
  979. // If we are processing entire inbound string, then just copy the
  980. // whole string. Otherwise, copy the entire destination buffer, so we
  981. // don't lose data beyond the partial inbound string.
  982. //
  983. if (*((PCWSTR) ((PBYTE) SrcBuffer + InboundBytes))) {
  984. CopyMemory ((PWSTR) orgSrc, SrcBuffer, MaxSizeInBytes);
  985. } else {
  986. CopyMemory ((PWSTR) orgSrc, SrcBuffer, inboundSize);
  987. }
  988. } else {
  989. orgSrc = SrcBuffer;
  990. }
  991. //
  992. // Walk the lower cased string, looking for strings to replace
  993. //
  994. orgSrcPos = orgSrc;
  995. lowerSrcPos = lowerCaseSrc;
  996. lowerSrcEnd = (PCWSTR) ((PBYTE) lowerSrcPos + InboundBytes);
  997. destPos = Buffer;
  998. destBytesLeft = MaxSizeInBytes - sizeof (WCHAR);
  999. filterData.UnicodeData = TRUE;
  1000. filterData.Unicode.OriginalString = orgSrc;
  1001. filterData.Unicode.CurrentString = Buffer;
  1002. endPtr = NULL;
  1003. while (lowerSrcPos < lowerSrcEnd) {
  1004. replaceString = pFindReplacementStringW (
  1005. MapArray,
  1006. MapArrayCount,
  1007. lowerSrcPos,
  1008. (HALF_PTR) ((PBYTE) lowerSrcEnd - (PBYTE) lowerSrcPos),
  1009. &searchStringBytes,
  1010. &replaceStringBytes,
  1011. &filterData,
  1012. ExtraDataValue,
  1013. (Flags & SZMAP_REQUIRE_WACK_OR_NUL) != 0
  1014. );
  1015. if (replaceString) {
  1016. //
  1017. // Implement complete match flag
  1018. //
  1019. if (Flags & SZMAP_COMPLETE_MATCH_ONLY) {
  1020. if (InboundBytes != searchStringBytes) {
  1021. break;
  1022. }
  1023. }
  1024. result = TRUE;
  1025. //
  1026. // Verify replacement string isn't growing string too much. If it
  1027. // is, truncate the replacement string.
  1028. //
  1029. if (destBytesLeft < replaceStringBytes) {
  1030. replaceStringBytes = destBytesLeft;
  1031. } else {
  1032. destBytesLeft -= replaceStringBytes;
  1033. }
  1034. //
  1035. // Transfer the memory
  1036. //
  1037. CopyMemory (destPos, replaceString, replaceStringBytes);
  1038. destPos = (PWSTR) ((PBYTE) destPos + replaceStringBytes);
  1039. lowerSrcPos = (PCWSTR) ((PBYTE) lowerSrcPos + searchStringBytes);
  1040. orgSrcPos = (PCWSTR) ((PBYTE) orgSrcPos + searchStringBytes);
  1041. //
  1042. // Implement single match flag
  1043. //
  1044. if (Flags & SZMAP_RETURN_AFTER_FIRST_REPLACE) {
  1045. endPtr = destPos;
  1046. break;
  1047. }
  1048. } else if (Flags & (SZMAP_FIRST_CHAR_MUST_MATCH|SZMAP_COMPLETE_MATCH_ONLY)) {
  1049. //
  1050. // This string does not match any search strings
  1051. //
  1052. break;
  1053. } else {
  1054. //
  1055. // This character does not match, so copy it to the destination and
  1056. // try the next string.
  1057. //
  1058. if (destBytesLeft < sizeof (WCHAR)) {
  1059. break;
  1060. }
  1061. *destPos++ = *orgSrcPos++;
  1062. destBytesLeft -= sizeof (WCHAR);
  1063. lowerSrcPos++;
  1064. }
  1065. }
  1066. //
  1067. // Copy any remaining part of the original source to the
  1068. // destination, unless destPos == Buffer == SrcBuffer
  1069. //
  1070. if (destPos != SrcBuffer) {
  1071. if (*orgSrcPos) {
  1072. orgSrcBytesLeft = SzByteCountW (orgSrcPos);
  1073. orgSrcBytesLeft = min (orgSrcBytesLeft, destBytesLeft);
  1074. CopyMemory (destPos, orgSrcPos, orgSrcBytesLeft);
  1075. destPos = (PWSTR) ((PBYTE) destPos + orgSrcBytesLeft);
  1076. }
  1077. MYASSERT ((PBYTE) (destPos + 1) <= ((PBYTE) Buffer + MaxSizeInBytes));
  1078. *destPos = 0;
  1079. if (!endPtr) {
  1080. endPtr = destPos;
  1081. }
  1082. } else {
  1083. MYASSERT (SrcBuffer == Buffer);
  1084. if (EndOfString || OutboundBytesPtr) {
  1085. endPtr = SzGetEndW (destPos);
  1086. }
  1087. }
  1088. if (EndOfString) {
  1089. MYASSERT (endPtr);
  1090. *EndOfString = endPtr;
  1091. }
  1092. if (OutboundBytesPtr) {
  1093. MYASSERT (endPtr);
  1094. if (*endPtr) {
  1095. endPtr = SzGetEndW (endPtr);
  1096. }
  1097. *OutboundBytesPtr = (HALF_PTR) ((PBYTE) endPtr - (PBYTE) Buffer);
  1098. }
  1099. SzFreeW (lowerCaseSrc);
  1100. return result;
  1101. }
  1102. BOOL
  1103. SzMapSearchAndReplaceExA (
  1104. IN PSTRINGMAP Map,
  1105. IN PCSTR SrcBuffer,
  1106. OUT PSTR Buffer, // can be the same as SrcBuffer
  1107. IN INT InboundBytes, OPTIONAL
  1108. OUT PINT OutboundBytesPtr, OPTIONAL
  1109. IN INT MaxSizeInBytes,
  1110. IN DWORD Flags,
  1111. OUT ULONG_PTR *ExtraDataValue, OPTIONAL
  1112. OUT PCSTR *EndOfString OPTIONAL
  1113. )
  1114. {
  1115. return SzMapMultiTableSearchAndReplaceExA (
  1116. &Map,
  1117. 1,
  1118. SrcBuffer,
  1119. Buffer,
  1120. InboundBytes,
  1121. OutboundBytesPtr,
  1122. MaxSizeInBytes,
  1123. Flags,
  1124. ExtraDataValue,
  1125. EndOfString
  1126. );
  1127. }
  1128. BOOL
  1129. SzMapSearchAndReplaceExW (
  1130. IN PSTRINGMAP Map,
  1131. IN PCWSTR SrcBuffer,
  1132. OUT PWSTR Buffer, // can be the same as SrcBuffer
  1133. IN INT InboundBytes, OPTIONAL
  1134. OUT PINT OutboundBytesPtr, OPTIONAL
  1135. IN INT MaxSizeInBytes,
  1136. IN DWORD Flags,
  1137. OUT ULONG_PTR *ExtraDataValue, OPTIONAL
  1138. OUT PCWSTR *EndOfString OPTIONAL
  1139. )
  1140. {
  1141. return SzMapMultiTableSearchAndReplaceExW (
  1142. &Map,
  1143. 1,
  1144. SrcBuffer,
  1145. Buffer,
  1146. InboundBytes,
  1147. OutboundBytesPtr,
  1148. MaxSizeInBytes,
  1149. Flags,
  1150. ExtraDataValue,
  1151. EndOfString
  1152. );
  1153. }