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
1489 lines
37 KiB
/*++
|
|
|
|
Copyright (c) 1996 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
strmap.c
|
|
|
|
Abstract:
|
|
|
|
The SzMap routines are used to do fast search and replace. The table is
|
|
built of a linked list of characters, so that finding a string is
|
|
simply a matter of walking down a linked list. These routines perform
|
|
quite well for general search & replace use.
|
|
|
|
Also, it is common to use string maps as index tables for certain
|
|
types of data where search strings often repeat the same left side of
|
|
the string (such as paths). In this case, often the replacement string is
|
|
empty.
|
|
|
|
Author:
|
|
|
|
Marc R. Whitten (marcw) 20-Mar-1997
|
|
|
|
Revision History:
|
|
|
|
Jim Schmidt (jimschm) 05-Jun-2000 Added multi table capability
|
|
|
|
Jim Schmidt (jimschm) 08-May-2000 Improved replacement routines and
|
|
added consistent filtering and
|
|
extra data option
|
|
|
|
Jim Schmidt (jimschm) 18-Aug-1998 Redesigned to fix two bugs, made
|
|
A & W versions
|
|
|
|
--*/
|
|
|
|
//
|
|
// Includes
|
|
//
|
|
|
|
#include "pch.h"
|
|
#include "commonp.h"
|
|
|
|
// BUGBUG - remove this
|
|
__inline
|
|
BOOL
|
|
SzIsLeadByte (
|
|
MBCHAR ch
|
|
)
|
|
{
|
|
MYASSERT (FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Strings
|
|
//
|
|
|
|
// None
|
|
|
|
//
|
|
// Constants
|
|
//
|
|
|
|
#define CHARNODE_SINGLE_BYTE 0x0000
|
|
#define CHARNODE_DOUBLE_BYTE 0x0001
|
|
#define CHARNODE_REQUIRE_WACK_OR_NUL 0x0002
|
|
|
|
//
|
|
// Macros
|
|
//
|
|
|
|
// None
|
|
|
|
//
|
|
// Types
|
|
//
|
|
|
|
typedef struct {
|
|
PVOID Next;
|
|
BYTE Memory[];
|
|
} MAPALLOC, *PMAPALLOC;
|
|
|
|
//
|
|
// Globals
|
|
//
|
|
|
|
// None
|
|
|
|
//
|
|
// Macro expansion list
|
|
//
|
|
|
|
// None
|
|
|
|
//
|
|
// Private function prototypes
|
|
//
|
|
|
|
// None
|
|
|
|
//
|
|
// Macro expansion definition
|
|
//
|
|
|
|
// None
|
|
|
|
//
|
|
// Code
|
|
//
|
|
|
|
PVOID
|
|
pAllocateInMap (
|
|
IN PSTRINGMAP Map,
|
|
IN UINT Bytes
|
|
)
|
|
{
|
|
PMAPALLOC alloc;
|
|
|
|
alloc = (PMAPALLOC) MALLOC_UNINIT (Bytes + sizeof (MAPALLOC));
|
|
MYASSERT (alloc);
|
|
|
|
alloc->Next = Map->CleanUpChain;
|
|
Map->CleanUpChain = alloc;
|
|
|
|
return alloc->Memory;
|
|
}
|
|
|
|
PSTR
|
|
pDupInMapA (
|
|
IN PSTRINGMAP Map,
|
|
IN PCSTR String
|
|
)
|
|
{
|
|
UINT bytes;
|
|
PSTR result;
|
|
|
|
bytes = SzSizeA (String);
|
|
result = pAllocateInMap (Map, bytes);
|
|
MYASSERT (result);
|
|
|
|
CopyMemory (result, String, bytes);
|
|
return result;
|
|
}
|
|
|
|
|
|
PWSTR
|
|
pDupInMapW (
|
|
IN PSTRINGMAP Map,
|
|
IN PCWSTR String
|
|
)
|
|
{
|
|
UINT bytes;
|
|
PWSTR result;
|
|
|
|
bytes = SzSizeW (String);
|
|
result = pAllocateInMap (Map, bytes);
|
|
MYASSERT (result);
|
|
|
|
CopyMemory (result, String, bytes);
|
|
return result;
|
|
}
|
|
|
|
|
|
PSTRINGMAP
|
|
SzMapCreateEx (
|
|
IN BOOL UsesFilters,
|
|
IN BOOL UsesExtraData
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
SzMapCreateEx allocates a string mapping data structure and initializes it.
|
|
Callers can enable filter callbacks, extra data support, or both. The
|
|
mapping structure contains either CHARNODE elements, or CHARNODEEX elements,
|
|
depending on the UsesFilters or UsesExtraData flag.
|
|
|
|
Arguments:
|
|
|
|
UsesFilters - Specifies TRUE to enable filter callbacks. If enabled,
|
|
those who add string pairs must specify the filter callback
|
|
(each search/replace pair has its own callback)
|
|
UsesExtraData - Specifies TRUE to associate extra data with the string
|
|
mapping pair.
|
|
|
|
Return Value:
|
|
|
|
A handle to the string mapping structure, or NULL if a structure could not
|
|
be created.
|
|
|
|
--*/
|
|
|
|
{
|
|
PSTRINGMAP map;
|
|
|
|
map = (PSTRINGMAP) MALLOC_ZEROED (sizeof (STRINGMAP));
|
|
MYASSERT (map);
|
|
|
|
map->UsesExNode = UsesFilters|UsesExtraData;
|
|
map->UsesFilter = UsesFilters;
|
|
map->UsesExtraData = UsesExtraData;
|
|
map->CleanUpChain = NULL;
|
|
|
|
return map;
|
|
}
|
|
|
|
VOID
|
|
SzMapDestroy (
|
|
IN PSTRINGMAP Map
|
|
)
|
|
{
|
|
PMAPALLOC next;
|
|
PMAPALLOC current;
|
|
|
|
if (Map) {
|
|
next = (PMAPALLOC) Map->CleanUpChain;
|
|
while (next) {
|
|
current = next;
|
|
next = current->Next;
|
|
|
|
FREE(current);
|
|
}
|
|
|
|
FREE(Map);
|
|
}
|
|
}
|
|
|
|
PCHARNODE
|
|
pFindCharNode (
|
|
IN PSTRINGMAP Map,
|
|
IN PCHARNODE PrevNode, OPTIONAL
|
|
IN WORD Char
|
|
)
|
|
{
|
|
PCHARNODE Node;
|
|
|
|
if (!PrevNode) {
|
|
Node = Map->FirstLevelRoot;
|
|
} else {
|
|
Node = PrevNode->NextLevel;
|
|
}
|
|
|
|
while (Node) {
|
|
if (Node->Char == Char) {
|
|
return Node;
|
|
}
|
|
Node = Node->NextPeer;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
PCHARNODE
|
|
pAddCharNode (
|
|
IN PSTRINGMAP Map,
|
|
IN PCHARNODE PrevNode, OPTIONAL
|
|
IN WORD Char,
|
|
IN WORD Flags
|
|
)
|
|
{
|
|
PCHARNODE Node;
|
|
PCHARNODEEX exNode;
|
|
|
|
if (Map->UsesExNode) {
|
|
exNode = pAllocateInMap (Map, sizeof (CHARNODEEX));
|
|
Node = (PCHARNODE) exNode;
|
|
MYASSERT (Node);
|
|
ZeroMemory (exNode, sizeof (CHARNODEEX));
|
|
} else {
|
|
Node = pAllocateInMap (Map, sizeof (CHARNODE));
|
|
MYASSERT (Node);
|
|
ZeroMemory (Node, sizeof (CHARNODE));
|
|
}
|
|
|
|
Node->Char = Char;
|
|
Node->Flags = Flags;
|
|
|
|
if (PrevNode) {
|
|
Node->NextPeer = PrevNode->NextLevel;
|
|
PrevNode->NextLevel = Node;
|
|
} else {
|
|
Node->NextPeer = Map->FirstLevelRoot;
|
|
Map->FirstLevelRoot = Node;
|
|
}
|
|
|
|
return Node;
|
|
}
|
|
|
|
|
|
VOID
|
|
SzMapAddExA (
|
|
IN OUT PSTRINGMAP Map,
|
|
IN PCSTR Old,
|
|
IN PCSTR New,
|
|
IN STRINGMAP_FILTER Filter, OPTIONAL
|
|
IN ULONG_PTR ExtraData, OPTIONAL
|
|
IN DWORD Flags
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
SzMapAddEx adds a search and replace string pair to the linked list data
|
|
structures. If the same search string is already in the structures, then the
|
|
replace string and optional extra data is updated.
|
|
|
|
Arguments:
|
|
|
|
Map - Specifies the string mapping
|
|
Old - Specifies the search string
|
|
New - Specifies the replace string
|
|
Filter - Specifies the callback filter. This is only supported if the
|
|
map was created with filter support enabled.
|
|
ExtraData - Specifies arbitrary data to assign to the search/replace pair.
|
|
This is only valid if the map was created with extra data
|
|
enabled.
|
|
Flags - Specifies optional flag STRINGMAP_REQUIRE_WACK_OR_NUL
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PSTR oldCopy;
|
|
PSTR newCopy;
|
|
PCSTR p;
|
|
WORD w;
|
|
PCHARNODE prev;
|
|
PCHARNODE Node;
|
|
PCHARNODEEX exNode;
|
|
WORD nodeFlags = 0;
|
|
|
|
if (Flags & SZMAP_REQUIRE_WACK_OR_NUL) {
|
|
nodeFlags = CHARNODE_REQUIRE_WACK_OR_NUL;
|
|
}
|
|
|
|
MYASSERT (Map);
|
|
MYASSERT (Old);
|
|
MYASSERT (New);
|
|
MYASSERT (*Old);
|
|
|
|
//
|
|
// Duplicate strings
|
|
//
|
|
|
|
oldCopy = pDupInMapA (Map, Old);
|
|
newCopy = pDupInMapA (Map, New);
|
|
|
|
//
|
|
// Make oldCopy all lowercase
|
|
//
|
|
|
|
CharLowerA (oldCopy);
|
|
|
|
//
|
|
// Add the letters that are not in the mapping
|
|
//
|
|
|
|
for (prev = NULL, p = oldCopy ; *p ; p = _mbsinc (p)) {
|
|
w = (WORD) _mbsnextc (p);
|
|
Node = pFindCharNode (Map, prev, w);
|
|
if (!Node) {
|
|
break;
|
|
}
|
|
prev = Node;
|
|
}
|
|
|
|
for ( ; *p ; p = _mbsinc (p)) {
|
|
w = (WORD) _mbsnextc (p);
|
|
|
|
nodeFlags |= (WORD) (SzIsLeadByte (*p) ? CHARNODE_DOUBLE_BYTE : CHARNODE_SINGLE_BYTE);
|
|
prev = pAddCharNode (Map, prev, w, nodeFlags);
|
|
}
|
|
|
|
if (prev) {
|
|
SzCopyA (oldCopy, Old);
|
|
prev->OriginalStr = (PVOID) oldCopy;
|
|
prev->ReplacementStr = (PVOID) newCopy;
|
|
prev->ReplacementBytes = SzByteCountA (newCopy);
|
|
|
|
exNode = (PCHARNODEEX) prev;
|
|
|
|
if (Map->UsesExtraData) {
|
|
exNode->ExtraData = ExtraData;
|
|
}
|
|
|
|
if (Map->UsesFilter) {
|
|
exNode->Filter = Filter;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
SzMapAddExW (
|
|
IN OUT PSTRINGMAP Map,
|
|
IN PCWSTR Old,
|
|
IN PCWSTR New,
|
|
IN STRINGMAP_FILTER Filter, OPTIONAL
|
|
IN ULONG_PTR ExtraData, OPTIONAL
|
|
IN DWORD Flags
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
SzMapAddEx adds a search and replace string pair to the linked list data
|
|
structures. If the same search string is already in the structures, then the
|
|
replace string and optional extra data is updated.
|
|
|
|
Arguments:
|
|
|
|
Map - Specifies the string mapping
|
|
Old - Specifies the search string
|
|
New - Specifies the replace string
|
|
Filter - Specifies the callback filter. This is only supported if the
|
|
map was created with filter support enabled.
|
|
ExtraData - Specifies arbitrary data to assign to the search/replace pair.
|
|
This is only valid if the map was created with extra data
|
|
enabled.
|
|
Flags - Specifies optional flag SZMAP_REQUIRE_WACK_OR_NUL
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PWSTR oldCopy;
|
|
PWSTR newCopy;
|
|
PCWSTR p;
|
|
WORD w;
|
|
PCHARNODE prev;
|
|
PCHARNODE node;
|
|
PCHARNODEEX exNode;
|
|
WORD nodeFlags = 0;
|
|
|
|
if (Flags & SZMAP_REQUIRE_WACK_OR_NUL) {
|
|
nodeFlags = CHARNODE_REQUIRE_WACK_OR_NUL;
|
|
}
|
|
|
|
MYASSERT (Map);
|
|
MYASSERT (Old);
|
|
MYASSERT (New);
|
|
MYASSERT (*Old);
|
|
|
|
//
|
|
// Duplicate strings
|
|
//
|
|
|
|
oldCopy = pDupInMapW (Map, Old);
|
|
newCopy = pDupInMapW (Map, New);
|
|
|
|
//
|
|
// Make oldCopy all lowercase
|
|
//
|
|
|
|
CharLowerW (oldCopy);
|
|
|
|
//
|
|
// Add the letters that are not in the mapping
|
|
//
|
|
|
|
prev = NULL;
|
|
p = oldCopy;
|
|
while (w = *p) { // intentional assignment optimization
|
|
|
|
node = pFindCharNode (Map, prev, w);
|
|
if (!node) {
|
|
break;
|
|
}
|
|
prev = node;
|
|
|
|
p++;
|
|
}
|
|
|
|
while (w = *p) { // intentional assignment optimization
|
|
|
|
prev = pAddCharNode (Map, prev, w, nodeFlags);
|
|
p++;
|
|
}
|
|
|
|
if (prev) {
|
|
SzCopyW (oldCopy, Old);
|
|
prev->OriginalStr = oldCopy;
|
|
prev->ReplacementStr = (PVOID) newCopy;
|
|
prev->ReplacementBytes = SzByteCountW (newCopy);
|
|
|
|
exNode = (PCHARNODEEX) prev;
|
|
|
|
if (Map->UsesExtraData) {
|
|
exNode->ExtraData = ExtraData;
|
|
}
|
|
|
|
if (Map->UsesFilter) {
|
|
exNode->Filter = Filter;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
PCSTR
|
|
pFindReplacementStringInOneMapA (
|
|
IN PSTRINGMAP Map,
|
|
IN PCSTR Source,
|
|
IN INT MaxSourceBytes,
|
|
OUT PINT SourceBytesPtr,
|
|
OUT PINT ReplacementBytesPtr,
|
|
IN PSTRINGMAP_FILTER_DATA Data,
|
|
OUT ULONG_PTR *ExtraDataValue, OPTIONAL
|
|
IN BOOL RequireWackOrNul
|
|
)
|
|
{
|
|
PCHARNODE bestMatch;
|
|
PCHARNODE node;
|
|
WORD mbChar;
|
|
PCSTR OrgSource;
|
|
PCSTR newString = NULL;
|
|
INT newStringSizeInBytes = 0;
|
|
PCHARNODEEX exNode;
|
|
BOOL replacementFound;
|
|
|
|
*SourceBytesPtr = 0;
|
|
|
|
node = NULL;
|
|
bestMatch = NULL;
|
|
|
|
OrgSource = Source;
|
|
|
|
while (*Source) {
|
|
|
|
mbChar = (WORD) _mbsnextc (Source);
|
|
|
|
node = pFindCharNode (Map, node, mbChar);
|
|
|
|
if (node) {
|
|
//
|
|
// Advance string pointer
|
|
//
|
|
|
|
if (node->Flags & CHARNODE_DOUBLE_BYTE) {
|
|
Source += 2;
|
|
} else {
|
|
Source++;
|
|
}
|
|
|
|
if (((PBYTE) Source - (PBYTE) OrgSource) > MaxSourceBytes) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If replacement string is available, keep it
|
|
// until a longer match comes along
|
|
//
|
|
|
|
replacementFound = (node->ReplacementStr != NULL);
|
|
|
|
if ((RequireWackOrNul || (node->Flags & CHARNODE_REQUIRE_WACK_OR_NUL)) && replacementFound) {
|
|
|
|
if (*Source && _mbsnextc (Source) != '\\') {
|
|
replacementFound = FALSE;
|
|
}
|
|
}
|
|
|
|
if (replacementFound) {
|
|
|
|
newString = (PCSTR) node->ReplacementStr;
|
|
newStringSizeInBytes = node->ReplacementBytes;
|
|
|
|
if (Map->UsesFilter) {
|
|
//
|
|
// Call rename filter to allow denial of match
|
|
//
|
|
|
|
exNode = (PCHARNODEEX) node;
|
|
|
|
if (exNode->Filter) {
|
|
Data->Ansi.BeginningOfMatch = OrgSource;
|
|
Data->Ansi.OldSubString = (PCSTR) node->OriginalStr;
|
|
Data->Ansi.NewSubString = newString;
|
|
Data->Ansi.NewSubStringSizeInBytes = newStringSizeInBytes;
|
|
|
|
if (!exNode->Filter (Data)) {
|
|
replacementFound = FALSE;
|
|
} else {
|
|
newString = Data->Ansi.NewSubString;
|
|
newStringSizeInBytes = Data->Ansi.NewSubStringSizeInBytes;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (replacementFound) {
|
|
bestMatch = node;
|
|
*SourceBytesPtr = (HALF_PTR) ((PBYTE) Source - (PBYTE) OrgSource);
|
|
}
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// No node ends the search
|
|
//
|
|
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
if (bestMatch) {
|
|
//
|
|
// Return replacement data to caller
|
|
//
|
|
|
|
if (ExtraDataValue) {
|
|
|
|
if (Map->UsesExtraData) {
|
|
exNode = (PCHARNODEEX) bestMatch;
|
|
*ExtraDataValue = exNode->ExtraData;
|
|
} else {
|
|
*ExtraDataValue = 0;
|
|
}
|
|
}
|
|
|
|
*ReplacementBytesPtr = newStringSizeInBytes;
|
|
return newString;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
PCSTR
|
|
pFindReplacementStringA (
|
|
IN PSTRINGMAP *MapArray,
|
|
IN UINT MapArrayCount,
|
|
IN PCSTR Source,
|
|
IN INT MaxSourceBytes,
|
|
OUT PINT SourceBytesPtr,
|
|
OUT PINT ReplacementBytesPtr,
|
|
IN PSTRINGMAP_FILTER_DATA Data,
|
|
OUT ULONG_PTR *ExtraDataValue, OPTIONAL
|
|
IN BOOL RequireWackOrNul
|
|
)
|
|
{
|
|
UINT u;
|
|
PCSTR result;
|
|
|
|
for (u = 0 ; u < MapArrayCount ; u++) {
|
|
|
|
if (!MapArray[u]) {
|
|
continue;
|
|
}
|
|
|
|
result = pFindReplacementStringInOneMapA (
|
|
MapArray[u],
|
|
Source,
|
|
MaxSourceBytes,
|
|
SourceBytesPtr,
|
|
ReplacementBytesPtr,
|
|
Data,
|
|
ExtraDataValue,
|
|
RequireWackOrNul
|
|
);
|
|
|
|
if (result) {
|
|
return result;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
PCWSTR
|
|
pFindReplacementStringInOneMapW (
|
|
IN PSTRINGMAP Map,
|
|
IN PCWSTR Source,
|
|
IN INT MaxSourceBytes,
|
|
OUT PINT SourceBytesPtr,
|
|
OUT PINT ReplacementBytesPtr,
|
|
IN PSTRINGMAP_FILTER_DATA Data,
|
|
OUT ULONG_PTR *ExtraDataValue, OPTIONAL
|
|
IN BOOL RequireWackOrNul
|
|
)
|
|
{
|
|
PCHARNODE bestMatch;
|
|
PCHARNODE node;
|
|
PCWSTR OrgSource;
|
|
PCWSTR newString = NULL;
|
|
INT newStringSizeInBytes;
|
|
BOOL replacementFound;
|
|
PCHARNODEEX exNode;
|
|
|
|
*SourceBytesPtr = 0;
|
|
|
|
node = NULL;
|
|
bestMatch = NULL;
|
|
|
|
OrgSource = Source;
|
|
|
|
while (*Source) {
|
|
|
|
node = pFindCharNode (Map, node, *Source);
|
|
|
|
if (node) {
|
|
//
|
|
// Advance string pointer
|
|
//
|
|
|
|
Source++;
|
|
|
|
if (((PBYTE) Source - (PBYTE) OrgSource) > MaxSourceBytes) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If replacement string is available, keep it
|
|
// until a longer match comes along
|
|
//
|
|
|
|
replacementFound = (node->ReplacementStr != NULL);
|
|
|
|
if ((RequireWackOrNul || (node->Flags & CHARNODE_REQUIRE_WACK_OR_NUL)) && replacementFound) {
|
|
|
|
if (*Source && *Source != L'\\') {
|
|
replacementFound = FALSE;
|
|
}
|
|
}
|
|
|
|
if (replacementFound) {
|
|
|
|
newString = (PCWSTR) node->ReplacementStr;
|
|
newStringSizeInBytes = node->ReplacementBytes;
|
|
|
|
if (Map->UsesFilter) {
|
|
//
|
|
// Call rename filter to allow denial of match
|
|
//
|
|
|
|
exNode = (PCHARNODEEX) node;
|
|
|
|
if (exNode->Filter) {
|
|
Data->Unicode.BeginningOfMatch = OrgSource;
|
|
Data->Unicode.OldSubString = (PCWSTR) node->OriginalStr;
|
|
Data->Unicode.NewSubString = newString;
|
|
Data->Unicode.NewSubStringSizeInBytes = newStringSizeInBytes;
|
|
|
|
if (!exNode->Filter (Data)) {
|
|
replacementFound = FALSE;
|
|
} else {
|
|
newString = Data->Unicode.NewSubString;
|
|
newStringSizeInBytes = Data->Unicode.NewSubStringSizeInBytes;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (replacementFound) {
|
|
bestMatch = node;
|
|
*SourceBytesPtr = (HALF_PTR) ((PBYTE) Source - (PBYTE) OrgSource);
|
|
}
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// No node ends the search
|
|
//
|
|
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
if (bestMatch) {
|
|
|
|
//
|
|
// Return replacement data to caller
|
|
//
|
|
|
|
if (ExtraDataValue) {
|
|
|
|
if (Map->UsesExtraData) {
|
|
exNode = (PCHARNODEEX) bestMatch;
|
|
*ExtraDataValue = exNode->ExtraData;
|
|
} else {
|
|
*ExtraDataValue = 0;
|
|
}
|
|
}
|
|
|
|
*ReplacementBytesPtr = newStringSizeInBytes;
|
|
return newString;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
PCWSTR
|
|
pFindReplacementStringW (
|
|
IN PSTRINGMAP *MapArray,
|
|
IN UINT MapArrayCount,
|
|
IN PCWSTR Source,
|
|
IN INT MaxSourceBytes,
|
|
OUT PINT SourceBytesPtr,
|
|
OUT PINT ReplacementBytesPtr,
|
|
IN PSTRINGMAP_FILTER_DATA Data,
|
|
OUT ULONG_PTR *ExtraDataValue, OPTIONAL
|
|
IN BOOL RequireWackOrNul
|
|
)
|
|
{
|
|
UINT u;
|
|
PCWSTR result;
|
|
|
|
for (u = 0 ; u < MapArrayCount ; u++) {
|
|
|
|
if (!MapArray[u]) {
|
|
continue;
|
|
}
|
|
|
|
result = pFindReplacementStringInOneMapW (
|
|
MapArray[u],
|
|
Source,
|
|
MaxSourceBytes,
|
|
SourceBytesPtr,
|
|
ReplacementBytesPtr,
|
|
Data,
|
|
ExtraDataValue,
|
|
RequireWackOrNul
|
|
);
|
|
|
|
if (result) {
|
|
return result;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
BOOL
|
|
SzMapMultiTableSearchAndReplaceExA (
|
|
IN PSTRINGMAP *MapArray,
|
|
IN UINT MapArrayCount,
|
|
IN PCSTR SrcBuffer,
|
|
OUT PSTR Buffer, // can be the same as SrcBuffer
|
|
IN INT InboundBytes, OPTIONAL
|
|
OUT PINT OutboundBytesPtr, OPTIONAL
|
|
IN INT MaxSizeInBytes,
|
|
IN DWORD Flags,
|
|
OUT ULONG_PTR *ExtraDataValue, OPTIONAL
|
|
OUT PCSTR *EndOfString OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
SzMapSearchAndReplaceEx performs a search/replace operation based on the
|
|
specified string mapping. The replace can be in-place or to another buffer.
|
|
|
|
Arguments:
|
|
|
|
MapArray - Specifies an array of string mapping tables that holds
|
|
zero or more search/replace pairs
|
|
MapArrayCount - Specifies the number of mapping tables in MapArray
|
|
SrcBuffer - Specifies the source string that might contain one or
|
|
more search strings
|
|
Buffer - Specifies the outbound buffer. This arg can be the same
|
|
as SrcBuffer.
|
|
InboundBytes - Specifies the number of bytes in SrcBuffer to process,
|
|
or 0 to process a nul-terminated string in SrcBuffer.
|
|
If InboundBytes is specified, it must point to the nul
|
|
terminator of SrcBuffer.
|
|
OutbountBytesPtr - Receives the number of bytes that Buffer contains,
|
|
excluding the nul terminator.
|
|
MaxSizeInBytes - Specifies the size of Buffer, in bytes.
|
|
Flags - Specifies flags that control the search/replace:
|
|
SZMAP_COMPLETE_MATCH_ONLY
|
|
SZMAP_FIRST_CHAR_MUST_MATCH
|
|
SZMAP_RETURN_AFTER_FIRST_REPLACE
|
|
SZMAP_REQUIRE_WACK_OR_NUL
|
|
ExtraDataValue - Receives the extra data associated with the first search/
|
|
replace pair.
|
|
EndOfString - Receives a pointer to the end of the replace string, or
|
|
the nul pointer when the entire string is processed. The
|
|
pointer is within the string contained in Buffer.
|
|
|
|
--*/
|
|
|
|
{
|
|
UINT sizeOfTempBuf;
|
|
INT inboundSize;
|
|
PCSTR lowerCaseSrc;
|
|
PCSTR orgSrc;
|
|
PCSTR lowerSrcPos;
|
|
PCSTR orgSrcPos;
|
|
INT orgSrcBytesLeft;
|
|
PSTR destPos;
|
|
PCSTR lowerSrcEnd;
|
|
INT searchStringBytes;
|
|
INT replaceStringBytes;
|
|
INT destBytesLeft;
|
|
STRINGMAP_FILTER_DATA filterData;
|
|
PCSTR replaceString;
|
|
BOOL result = FALSE;
|
|
INT i;
|
|
PCSTR endPtr;
|
|
|
|
//
|
|
// Empty string case
|
|
//
|
|
|
|
if (*SrcBuffer == 0 || MaxSizeInBytes <= sizeof (CHAR)) {
|
|
if (MaxSizeInBytes >= sizeof (CHAR)) {
|
|
*Buffer = 0;
|
|
}
|
|
|
|
if (OutboundBytesPtr) {
|
|
*OutboundBytesPtr = 0;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// If caller did not specify inbound size, compute it now
|
|
//
|
|
|
|
if (!InboundBytes) {
|
|
InboundBytes = SzByteCountA (SrcBuffer);
|
|
} else {
|
|
i = 0;
|
|
while (i < InboundBytes) {
|
|
if (SzIsLeadByte (SrcBuffer[i])) {
|
|
MYASSERT (SrcBuffer[i + 1]);
|
|
i += 2;
|
|
} else {
|
|
i++;
|
|
}
|
|
}
|
|
|
|
if (i > InboundBytes) {
|
|
InboundBytes--;
|
|
}
|
|
}
|
|
|
|
inboundSize = InboundBytes + sizeof (CHAR);
|
|
|
|
//
|
|
// Allocate a buffer big enough for the lower-cased input string,
|
|
// plus (optionally) a copy of the entire destination buffer. Then
|
|
// copy the data to the buffer.
|
|
//
|
|
|
|
sizeOfTempBuf = inboundSize;
|
|
|
|
if (SrcBuffer == Buffer) {
|
|
sizeOfTempBuf += MaxSizeInBytes;
|
|
}
|
|
|
|
lowerCaseSrc = SzAllocA (sizeOfTempBuf);
|
|
|
|
CopyMemory ((PSTR) lowerCaseSrc, SrcBuffer, InboundBytes);
|
|
*((PSTR) ((PBYTE) lowerCaseSrc + InboundBytes)) = 0;
|
|
|
|
CharLowerBuffA ((PSTR) lowerCaseSrc, InboundBytes / sizeof (CHAR));
|
|
|
|
if (SrcBuffer == Buffer && !(Flags & SZMAP_COMPLETE_MATCH_ONLY)) {
|
|
orgSrc = (PCSTR) ((PBYTE) lowerCaseSrc + inboundSize);
|
|
|
|
//
|
|
// If we are processing entire inbound string, then just copy the
|
|
// whole string. Otherwise, copy the entire destination buffer, so we
|
|
// don't lose data beyond the partial inbound string.
|
|
//
|
|
|
|
if (*((PCSTR) ((PBYTE) SrcBuffer + InboundBytes))) {
|
|
CopyMemory ((PSTR) orgSrc, SrcBuffer, MaxSizeInBytes);
|
|
} else {
|
|
CopyMemory ((PSTR) orgSrc, SrcBuffer, inboundSize);
|
|
}
|
|
|
|
} else {
|
|
orgSrc = SrcBuffer;
|
|
}
|
|
|
|
//
|
|
// Walk the lower cased string, looking for strings to replace
|
|
//
|
|
|
|
orgSrcPos = orgSrc;
|
|
|
|
lowerSrcPos = lowerCaseSrc;
|
|
lowerSrcEnd = (PCSTR) ((PBYTE) lowerSrcPos + InboundBytes);
|
|
|
|
destPos = Buffer;
|
|
destBytesLeft = MaxSizeInBytes - sizeof (CHAR);
|
|
|
|
filterData.UnicodeData = FALSE;
|
|
filterData.Ansi.OriginalString = orgSrc;
|
|
filterData.Ansi.CurrentString = Buffer;
|
|
|
|
endPtr = NULL;
|
|
|
|
while (lowerSrcPos < lowerSrcEnd) {
|
|
|
|
replaceString = pFindReplacementStringA (
|
|
MapArray,
|
|
MapArrayCount,
|
|
lowerSrcPos,
|
|
(HALF_PTR) ((PBYTE) lowerSrcEnd - (PBYTE) lowerSrcPos),
|
|
&searchStringBytes,
|
|
&replaceStringBytes,
|
|
&filterData,
|
|
ExtraDataValue,
|
|
(Flags & SZMAP_REQUIRE_WACK_OR_NUL) != 0
|
|
);
|
|
|
|
if (replaceString) {
|
|
|
|
//
|
|
// Implement complete match flag
|
|
//
|
|
|
|
if (Flags & SZMAP_COMPLETE_MATCH_ONLY) {
|
|
if (InboundBytes != searchStringBytes) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
result = TRUE;
|
|
|
|
//
|
|
// Verify replacement string isn't growing string too much. If it
|
|
// is, truncate the replacement string.
|
|
//
|
|
|
|
if (destBytesLeft < replaceStringBytes) {
|
|
|
|
//
|
|
// Respect logical dbcs characters
|
|
//
|
|
|
|
replaceStringBytes = 0;
|
|
i = 0;
|
|
|
|
while (i < destBytesLeft) {
|
|
MYASSERT (replaceString[i]);
|
|
|
|
if (SzIsLeadByte (replaceString[i])) {
|
|
MYASSERT (replaceString[i + 1]);
|
|
i += 2;
|
|
} else {
|
|
i++;
|
|
}
|
|
}
|
|
|
|
if (i > destBytesLeft) {
|
|
destBytesLeft--;
|
|
}
|
|
|
|
replaceStringBytes = destBytesLeft;
|
|
|
|
} else {
|
|
destBytesLeft -= replaceStringBytes;
|
|
}
|
|
|
|
//
|
|
// Transfer the memory
|
|
//
|
|
|
|
CopyMemory (destPos, replaceString, replaceStringBytes);
|
|
|
|
destPos = (PSTR) ((PBYTE) destPos + replaceStringBytes);
|
|
lowerSrcPos = (PCSTR) ((PBYTE) lowerSrcPos + searchStringBytes);
|
|
orgSrcPos = (PCSTR) ((PBYTE) orgSrcPos + searchStringBytes);
|
|
|
|
//
|
|
// Implement single match flag
|
|
//
|
|
|
|
if (Flags & SZMAP_RETURN_AFTER_FIRST_REPLACE) {
|
|
endPtr = destPos;
|
|
break;
|
|
}
|
|
|
|
} else if (Flags & (SZMAP_FIRST_CHAR_MUST_MATCH|SZMAP_COMPLETE_MATCH_ONLY)) {
|
|
//
|
|
// This string does not match any search strings
|
|
//
|
|
|
|
break;
|
|
|
|
} else {
|
|
//
|
|
// This character does not match, so copy it to the destination and
|
|
// try the next string.
|
|
//
|
|
|
|
if (SzIsLeadByte (*orgSrcPos)) {
|
|
|
|
//
|
|
// Copy double-byte character
|
|
//
|
|
|
|
if (destBytesLeft < sizeof (CHAR) * 2) {
|
|
break;
|
|
}
|
|
|
|
MYASSERT (sizeof (CHAR) * 2 == sizeof (WORD));
|
|
|
|
*((PWORD) destPos)++ = *((PWORD) orgSrcPos)++;
|
|
destBytesLeft -= sizeof (WORD);
|
|
lowerSrcPos = (PCSTR) ((PBYTE) lowerSrcPos + sizeof (WORD));
|
|
|
|
} else {
|
|
|
|
//
|
|
// Copy single-byte character
|
|
//
|
|
|
|
if (destBytesLeft < sizeof (CHAR)) {
|
|
break;
|
|
}
|
|
|
|
*destPos++ = *orgSrcPos++;
|
|
destBytesLeft -= sizeof (CHAR);
|
|
lowerSrcPos++;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Copy any remaining part of the original source to the
|
|
// destination, unless destPos == Buffer == SrcBuffer
|
|
//
|
|
|
|
if (destPos != SrcBuffer) {
|
|
|
|
if (*orgSrcPos) {
|
|
orgSrcBytesLeft = SzByteCountA (orgSrcPos);
|
|
orgSrcBytesLeft = min (orgSrcBytesLeft, destBytesLeft);
|
|
|
|
CopyMemory (destPos, orgSrcPos, orgSrcBytesLeft);
|
|
destPos = (PSTR) ((PBYTE) destPos + orgSrcBytesLeft);
|
|
}
|
|
|
|
MYASSERT ((PBYTE) (destPos + 1) <= ((PBYTE) Buffer + MaxSizeInBytes));
|
|
*destPos = 0;
|
|
|
|
if (!endPtr) {
|
|
endPtr = destPos;
|
|
}
|
|
|
|
} else {
|
|
|
|
MYASSERT (SrcBuffer == Buffer);
|
|
if (EndOfString || OutboundBytesPtr) {
|
|
endPtr = SzGetEndA (destPos);
|
|
}
|
|
}
|
|
|
|
if (EndOfString) {
|
|
MYASSERT (endPtr);
|
|
*EndOfString = endPtr;
|
|
}
|
|
|
|
if (OutboundBytesPtr) {
|
|
MYASSERT (endPtr);
|
|
if (*endPtr) {
|
|
endPtr = SzGetEndA (endPtr);
|
|
}
|
|
|
|
*OutboundBytesPtr = (HALF_PTR) ((PBYTE) endPtr - (PBYTE) Buffer);
|
|
}
|
|
|
|
SzFreeA (lowerCaseSrc);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
BOOL
|
|
SzMapMultiTableSearchAndReplaceExW (
|
|
IN PSTRINGMAP *MapArray,
|
|
IN UINT MapArrayCount,
|
|
IN PCWSTR SrcBuffer,
|
|
OUT PWSTR Buffer, // can be the same as SrcBuffer
|
|
IN INT InboundBytes, OPTIONAL
|
|
OUT PINT OutboundBytesPtr, OPTIONAL
|
|
IN INT MaxSizeInBytes,
|
|
IN DWORD Flags,
|
|
OUT ULONG_PTR *ExtraDataValue, OPTIONAL
|
|
OUT PCWSTR *EndOfString OPTIONAL
|
|
)
|
|
{
|
|
UINT sizeOfTempBuf;
|
|
INT inboundSize;
|
|
PCWSTR lowerCaseSrc;
|
|
PCWSTR orgSrc;
|
|
PCWSTR lowerSrcPos;
|
|
PCWSTR orgSrcPos;
|
|
INT orgSrcBytesLeft;
|
|
PWSTR destPos;
|
|
PCWSTR lowerSrcEnd;
|
|
INT searchStringBytes;
|
|
INT replaceStringBytes;
|
|
INT destBytesLeft;
|
|
STRINGMAP_FILTER_DATA filterData;
|
|
PCWSTR replaceString;
|
|
BOOL result = FALSE;
|
|
PCWSTR endPtr;
|
|
|
|
//
|
|
// Empty string case
|
|
//
|
|
|
|
if (*SrcBuffer == 0 || MaxSizeInBytes <= sizeof (CHAR)) {
|
|
if (MaxSizeInBytes >= sizeof (CHAR)) {
|
|
*Buffer = 0;
|
|
}
|
|
|
|
if (OutboundBytesPtr) {
|
|
*OutboundBytesPtr = 0;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// If caller did not specify inbound size, compute it now
|
|
//
|
|
|
|
if (!InboundBytes) {
|
|
InboundBytes = SzByteCountW (SrcBuffer);
|
|
} else {
|
|
InboundBytes = (InboundBytes / sizeof (WCHAR)) * sizeof (WCHAR);
|
|
}
|
|
|
|
|
|
inboundSize = InboundBytes + sizeof (WCHAR);
|
|
|
|
//
|
|
// Allocate a buffer big enough for the lower-cased input string,
|
|
// plus (optionally) a copy of the entire destination buffer. Then
|
|
// copy the data to the buffer.
|
|
//
|
|
|
|
sizeOfTempBuf = inboundSize;
|
|
|
|
if (SrcBuffer == Buffer) {
|
|
sizeOfTempBuf += MaxSizeInBytes;
|
|
}
|
|
|
|
lowerCaseSrc = SzAllocW (sizeOfTempBuf);
|
|
|
|
CopyMemory ((PWSTR) lowerCaseSrc, SrcBuffer, InboundBytes);
|
|
*((PWSTR) ((PBYTE) lowerCaseSrc + InboundBytes)) = 0;
|
|
|
|
CharLowerBuffW ((PWSTR) lowerCaseSrc, InboundBytes / sizeof (WCHAR));
|
|
|
|
if (SrcBuffer == Buffer && !(Flags & SZMAP_COMPLETE_MATCH_ONLY)) {
|
|
orgSrc = (PCWSTR) ((PBYTE) lowerCaseSrc + inboundSize);
|
|
|
|
//
|
|
// If we are processing entire inbound string, then just copy the
|
|
// whole string. Otherwise, copy the entire destination buffer, so we
|
|
// don't lose data beyond the partial inbound string.
|
|
//
|
|
|
|
if (*((PCWSTR) ((PBYTE) SrcBuffer + InboundBytes))) {
|
|
CopyMemory ((PWSTR) orgSrc, SrcBuffer, MaxSizeInBytes);
|
|
} else {
|
|
CopyMemory ((PWSTR) orgSrc, SrcBuffer, inboundSize);
|
|
}
|
|
|
|
} else {
|
|
orgSrc = SrcBuffer;
|
|
}
|
|
|
|
//
|
|
// Walk the lower cased string, looking for strings to replace
|
|
//
|
|
|
|
orgSrcPos = orgSrc;
|
|
|
|
lowerSrcPos = lowerCaseSrc;
|
|
lowerSrcEnd = (PCWSTR) ((PBYTE) lowerSrcPos + InboundBytes);
|
|
|
|
destPos = Buffer;
|
|
destBytesLeft = MaxSizeInBytes - sizeof (WCHAR);
|
|
|
|
filterData.UnicodeData = TRUE;
|
|
filterData.Unicode.OriginalString = orgSrc;
|
|
filterData.Unicode.CurrentString = Buffer;
|
|
|
|
endPtr = NULL;
|
|
|
|
while (lowerSrcPos < lowerSrcEnd) {
|
|
|
|
replaceString = pFindReplacementStringW (
|
|
MapArray,
|
|
MapArrayCount,
|
|
lowerSrcPos,
|
|
(HALF_PTR) ((PBYTE) lowerSrcEnd - (PBYTE) lowerSrcPos),
|
|
&searchStringBytes,
|
|
&replaceStringBytes,
|
|
&filterData,
|
|
ExtraDataValue,
|
|
(Flags & SZMAP_REQUIRE_WACK_OR_NUL) != 0
|
|
);
|
|
|
|
if (replaceString) {
|
|
|
|
//
|
|
// Implement complete match flag
|
|
//
|
|
|
|
if (Flags & SZMAP_COMPLETE_MATCH_ONLY) {
|
|
if (InboundBytes != searchStringBytes) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
result = TRUE;
|
|
|
|
//
|
|
// Verify replacement string isn't growing string too much. If it
|
|
// is, truncate the replacement string.
|
|
//
|
|
|
|
if (destBytesLeft < replaceStringBytes) {
|
|
replaceStringBytes = destBytesLeft;
|
|
} else {
|
|
destBytesLeft -= replaceStringBytes;
|
|
}
|
|
|
|
//
|
|
// Transfer the memory
|
|
//
|
|
|
|
CopyMemory (destPos, replaceString, replaceStringBytes);
|
|
|
|
destPos = (PWSTR) ((PBYTE) destPos + replaceStringBytes);
|
|
lowerSrcPos = (PCWSTR) ((PBYTE) lowerSrcPos + searchStringBytes);
|
|
orgSrcPos = (PCWSTR) ((PBYTE) orgSrcPos + searchStringBytes);
|
|
|
|
//
|
|
// Implement single match flag
|
|
//
|
|
|
|
if (Flags & SZMAP_RETURN_AFTER_FIRST_REPLACE) {
|
|
endPtr = destPos;
|
|
break;
|
|
}
|
|
|
|
} else if (Flags & (SZMAP_FIRST_CHAR_MUST_MATCH|SZMAP_COMPLETE_MATCH_ONLY)) {
|
|
//
|
|
// This string does not match any search strings
|
|
//
|
|
|
|
break;
|
|
|
|
} else {
|
|
//
|
|
// This character does not match, so copy it to the destination and
|
|
// try the next string.
|
|
//
|
|
|
|
if (destBytesLeft < sizeof (WCHAR)) {
|
|
break;
|
|
}
|
|
|
|
*destPos++ = *orgSrcPos++;
|
|
destBytesLeft -= sizeof (WCHAR);
|
|
lowerSrcPos++;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Copy any remaining part of the original source to the
|
|
// destination, unless destPos == Buffer == SrcBuffer
|
|
//
|
|
|
|
if (destPos != SrcBuffer) {
|
|
|
|
if (*orgSrcPos) {
|
|
orgSrcBytesLeft = SzByteCountW (orgSrcPos);
|
|
orgSrcBytesLeft = min (orgSrcBytesLeft, destBytesLeft);
|
|
|
|
CopyMemory (destPos, orgSrcPos, orgSrcBytesLeft);
|
|
destPos = (PWSTR) ((PBYTE) destPos + orgSrcBytesLeft);
|
|
}
|
|
|
|
MYASSERT ((PBYTE) (destPos + 1) <= ((PBYTE) Buffer + MaxSizeInBytes));
|
|
*destPos = 0;
|
|
|
|
if (!endPtr) {
|
|
endPtr = destPos;
|
|
}
|
|
|
|
} else {
|
|
|
|
MYASSERT (SrcBuffer == Buffer);
|
|
if (EndOfString || OutboundBytesPtr) {
|
|
endPtr = SzGetEndW (destPos);
|
|
}
|
|
}
|
|
|
|
if (EndOfString) {
|
|
MYASSERT (endPtr);
|
|
*EndOfString = endPtr;
|
|
}
|
|
|
|
if (OutboundBytesPtr) {
|
|
MYASSERT (endPtr);
|
|
if (*endPtr) {
|
|
endPtr = SzGetEndW (endPtr);
|
|
}
|
|
|
|
*OutboundBytesPtr = (HALF_PTR) ((PBYTE) endPtr - (PBYTE) Buffer);
|
|
}
|
|
|
|
SzFreeW (lowerCaseSrc);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
BOOL
|
|
SzMapSearchAndReplaceExA (
|
|
IN PSTRINGMAP Map,
|
|
IN PCSTR SrcBuffer,
|
|
OUT PSTR Buffer, // can be the same as SrcBuffer
|
|
IN INT InboundBytes, OPTIONAL
|
|
OUT PINT OutboundBytesPtr, OPTIONAL
|
|
IN INT MaxSizeInBytes,
|
|
IN DWORD Flags,
|
|
OUT ULONG_PTR *ExtraDataValue, OPTIONAL
|
|
OUT PCSTR *EndOfString OPTIONAL
|
|
)
|
|
{
|
|
return SzMapMultiTableSearchAndReplaceExA (
|
|
&Map,
|
|
1,
|
|
SrcBuffer,
|
|
Buffer,
|
|
InboundBytes,
|
|
OutboundBytesPtr,
|
|
MaxSizeInBytes,
|
|
Flags,
|
|
ExtraDataValue,
|
|
EndOfString
|
|
);
|
|
}
|
|
|
|
BOOL
|
|
SzMapSearchAndReplaceExW (
|
|
IN PSTRINGMAP Map,
|
|
IN PCWSTR SrcBuffer,
|
|
OUT PWSTR Buffer, // can be the same as SrcBuffer
|
|
IN INT InboundBytes, OPTIONAL
|
|
OUT PINT OutboundBytesPtr, OPTIONAL
|
|
IN INT MaxSizeInBytes,
|
|
IN DWORD Flags,
|
|
OUT ULONG_PTR *ExtraDataValue, OPTIONAL
|
|
OUT PCWSTR *EndOfString OPTIONAL
|
|
)
|
|
{
|
|
return SzMapMultiTableSearchAndReplaceExW (
|
|
&Map,
|
|
1,
|
|
SrcBuffer,
|
|
Buffer,
|
|
InboundBytes,
|
|
OutboundBytesPtr,
|
|
MaxSizeInBytes,
|
|
Flags,
|
|
ExtraDataValue,
|
|
EndOfString
|
|
);
|
|
}
|