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.

1524 lines
42 KiB

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