windows-nt/Source/XPSP1/NT/net/config/common/ncbase/ncmsz.cpp
2020-09-26 16:20:57 +08:00

677 lines
20 KiB
C++

//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1997.
//
// File: N C M S Z . C P P
//
// Contents: Common multi-sz routines.
//
// Notes: Split out from ncstring.cpp
//
// Author: shaunco 7 Jun 1998
//
//----------------------------------------------------------------------------
#include <pch.h>
#pragma hdrstop
#include "ncdebug.h"
#include "ncstring.h"
//+---------------------------------------------------------------------------
//
// Function: CchOfMultiSzSafe
//
// Purpose: Count the number of characters of a double NULL terminated
// multi-sz, including all NULLs except for the final terminating
// NULL.
//
// Arguments:
// pmsz [in] The multi-sz to count characters for.
//
// Returns: The count of characters.
//
// Author: tongl 17 June 1997
//
// Notes:
//
ULONG
CchOfMultiSzSafe (
IN PCWSTR pmsz)
{
// NULL strings have zero length by definition.
if (!pmsz)
return 0;
ULONG cchTotal = 0;
ULONG cch;
while (*pmsz)
{
cch = wcslen (pmsz) + 1;
cchTotal += cch;
pmsz += cch;
}
// Return the count of characters.
return cchTotal;
}
//+---------------------------------------------------------------------------
//
// Function: CchOfMultiSzAndTermSafe
//
// Purpose: Count the number of characters of a double NULL terminated
// multi-sz, including all NULLs.
//
// Arguments:
// pmsz [in] The multi-sz to count characters for.
//
// Returns: The count of characters.
//
// Author: tongl 17 June 1997
//
// Notes:
//
ULONG
CchOfMultiSzAndTermSafe (
IN PCWSTR pmsz)
{
// NULL strings have zero length by definition.
if (!pmsz)
return 0;
// Return the count of characters plus room for the
// extra null terminator.
return CchOfMultiSzSafe (pmsz) + 1;
}
//+---------------------------------------------------------------------------
//
// Function: FIsSzInMultiSzSafe
//
// Purpose: Determine if a given string is present in a Multi-Sz string
// by doing a case insensitive compare.
//
// Arguments:
// psz [in] String to search for in pmsz
// pmsz [in] The multi-sz to search
//
// Returns: TRUE if the specified string 'psz' was found in 'pmsz'.
//
// Author: scottbri 25 Feb 1997
//
// Notes: Note that the code can handle Null input values.
//
BOOL
FIsSzInMultiSzSafe (
IN PCWSTR psz,
IN PCWSTR pmsz)
{
if (!pmsz || !psz)
{
return FALSE;
}
while (*pmsz)
{
if (0 == _wcsicmp (pmsz, psz))
{
return TRUE;
}
pmsz += wcslen (pmsz) + 1;
}
return FALSE;
}
//+---------------------------------------------------------------------------
//
// Function: FGetSzPositionInMultiSzSafe
//
// Purpose: Determine if a given string is present in a Multi-Sz string
// by doing a case insensitive compare.
//
// Arguments:
// psz [in] String to search for in pmsz
// pmsz [in] The multi-sz to search
// pdwIndex [out] The index of the first matching psz in pmsz
// pfDuplicatePresent [out] Optional. TRUE if the string is present in
// the multi-sz more than once. FALSE otherwise.
// pcStrings [out] Optional. The number of strings in pmsz
//
// Returns: TRUE if the specified string 'psz' was found in 'pmsz'.
//
// Author: BillBe 9 Oct 1998
//
// Notes: Note that the code can handle Null input values.
//
BOOL
FGetSzPositionInMultiSzSafe (
IN PCWSTR psz,
IN PCWSTR pmsz,
OUT DWORD* pdwIndex,
OUT BOOL *pfDuplicatePresent,
OUT DWORD* pcStrings)
{
// initialize out params.
//
*pdwIndex = 0;
if (pfDuplicatePresent)
{
*pfDuplicatePresent = FALSE;
}
if (pcStrings)
{
*pcStrings = 0;
}
if (!pmsz || !psz)
{
return FALSE;
}
// Need to keep track if duplicates are found
BOOL fFoundAlready = FALSE;
DWORD dwIndex = 0;
while (*pmsz)
{
if (0 == _wcsicmp (pmsz, psz))
{
if (!fFoundAlready)
{
*pdwIndex = dwIndex;
fFoundAlready = TRUE;
}
else if (pfDuplicatePresent)
{
*pfDuplicatePresent = TRUE;
}
}
pmsz += wcslen (pmsz) + 1;
++dwIndex;
}
if (pcStrings)
{
*pcStrings = dwIndex;
}
return fFoundAlready;
}
//+---------------------------------------------------------------------------
//
// Function: HrAddSzToMultiSz
//
// Purpose: Add a string into a REG_MULTI_SZ registry value
//
// Arguments:
// pszAddString [in] The string to add to the multi-sz
// pmszIn [in] (OPTIONAL) The original Multi-Sz to add to.
// dwFlags [in] Can contain one or more of the following
// values:
//
// STRING_FLAG_ALLOW_DUPLICATES
// Don't remove duplicate values when adding
// the string to the list. Default is to
// remove all other instance of this string.
// STRING_FLAG_ENSURE_AT_FRONT
// Ensure the string is the first element of
// the list. If the string is present and
// duplicates aren't allowed, move the
// string to the end.
// STRING_FLAG_ENSURE_AT_END
// Ensure the string is the last
// element of the list. This can not be used
// with STRING_FLAG_ENSURE_AT_FRONT. If the
// string is present and duplicates aren't
// allowed, move the string to the end.
// STRING_FLAG_ENSURE_AT_INDEX
// Ensure that the string is at dwStringIndex
// in the multi-sz. If the index specified
// is greater than the number of strings
// in the multi-sz, the string will be
// placed at the end.
// STRING_FLAG_DONT_MODIFY_IF_PRESENT
// If the string already exists in the
// multi-sz then no modication will take
// place. Note: This takes precedent
// over the presence/non-presence of the
// STRING_FLAG_ALLOW_DUPLICATES flag.
// i.e nothing will be added or removed
// if this flag is set and the string was
// present in the multi-sz
// dwStringIndex [in] If STRING_FLAG_ENSURE_AT_INDEX is specified,
// this is the index for the string position.
// Otherwise, this value is ignored.
//
// pmszOut [out] The new multi-sz.
// pfChanged [out] TRUE if the multi-sz changed in any way,
// FALSE otherwise.
//
// Returns: S_OK or an HRESULT_FROM_WIN32 error code.
//
// Author: jeffspr 27 Mar 1997
//
// Modified: BillBe 6 Oct 1998
// (Extracted from HrRegAddStringTo MultiSz and modified)
//
HRESULT
HrAddSzToMultiSz(
IN PCWSTR pszAddString,
IN PCWSTR pmszIn,
IN DWORD dwFlags,
IN DWORD dwStringIndex,
OUT PWSTR* ppmszOut,
OUT BOOL* pfChanged)
{
Assert(pszAddString && *pszAddString);
Assert(ppmszOut);
Assert(pfChanged);
HRESULT hr = S_OK;
BOOL fEnsureAtFront = dwFlags & STRING_FLAG_ENSURE_AT_FRONT;
BOOL fEnsureAtEnd = dwFlags & STRING_FLAG_ENSURE_AT_END;
BOOL fEnsureAtIndex = dwFlags & STRING_FLAG_ENSURE_AT_INDEX;
// Can't specify more than one of these flags
if ((fEnsureAtFront && fEnsureAtEnd) ||
(fEnsureAtFront && fEnsureAtIndex) ||
(fEnsureAtEnd && fEnsureAtIndex))
{
AssertSz(FALSE, "Invalid flags in HrAddSzToMultiSz");
return E_INVALIDARG;
}
// Must specify at least one
if (!fEnsureAtFront && !fEnsureAtEnd && !fEnsureAtIndex)
{
AssertSz(FALSE, "No operation flag specified in HrAddSzToMultiSz");
return E_INVALIDARG;
}
// Initialize the output parameters.
//
*ppmszOut = NULL;
*pfChanged = TRUE;
DWORD dwIndex;
BOOL fDupePresent;
DWORD cItems;
// If the string to add is not empty...
//
if (*pszAddString)
{
// Check if the string is already present in the MultiSz
BOOL fPresent = FGetSzPositionInMultiSzSafe (pszAddString, pmszIn,
&dwIndex, &fDupePresent, &cItems);
if (fPresent)
{
// If the flag don't modify is present then we aren't changing
// anything
//
if (dwFlags & STRING_FLAG_DONT_MODIFY_IF_PRESENT)
{
*pfChanged = FALSE;
}
// if there are no duplicates present and we are not allowing
// duplicates, then we need to determine if the string is already in
// the correct position
//
if (!fDupePresent && !(dwFlags & STRING_FLAG_ALLOW_DUPLICATES))
{
// If we are to insert the string at front but it is already
// there, then we aren't changing anything
//
if (fEnsureAtFront && (0 == dwIndex))
{
*pfChanged = FALSE;
}
// If we are to insert the string at the end but it is already
// there, then we aren't changing anything
//
if (fEnsureAtEnd && (dwIndex == (cItems - 1)))
{
*pfChanged = FALSE;
}
if (fEnsureAtIndex && (dwIndex == dwStringIndex))
{
*pfChanged = FALSE;
}
}
}
}
else
{
// If string to add was empty so we aren't changing anything
*pfChanged = FALSE;
}
// If we are still going to change things...
//
if (*pfChanged)
{
DWORD cchDataSize = CchOfMultiSzSafe (pmszIn);
// Enough space for the old data plus the new string and NULL, and for the
// second trailing NULL (multi-szs are double-terminated)
DWORD cchAllocSize = cchDataSize + wcslen (pszAddString) + 1 + 1;
PWSTR pmszOrderNew = (PWSTR) MemAlloc(cchAllocSize * sizeof(WCHAR));
if (pmszOrderNew)
{
// If we've gotten the "insert at front" flag, do the insert. Otherwise,
// the default is "insert at end"
//
DWORD cchOffsetNew = 0;
DWORD dwCurrentIndex = 0;
if (fEnsureAtFront || (fEnsureAtIndex && (0 == dwStringIndex)))
{
// Insert our passed-in value at the beginning of the new buffer.
//
wcscpy (pmszOrderNew + cchOffsetNew, pszAddString);
cchOffsetNew += wcslen ((PWSTR)pmszOrderNew) + 1;
++dwCurrentIndex;
}
// Loop through the old buffer, and copy all of the strings that are not
// identical to our insertion string.
//
DWORD cchOffsetOld = 0;
PWSTR pszCurrent;
while ((cchOffsetOld + 1) < cchDataSize)
{
if (fEnsureAtIndex && (dwCurrentIndex == dwStringIndex))
{
// Insert our passed-in value at the current index of the
// new buffer.
//
wcscpy (pmszOrderNew + cchOffsetNew, pszAddString);
cchOffsetNew += wcslen (pmszOrderNew + cchOffsetNew) + 1;
++dwCurrentIndex;
}
else
{
BOOL fCopyThisElement = FALSE;
// Get the next string in the list.
//
pszCurrent = (PWSTR) (pmszIn + cchOffsetOld);
// If we allow duplicates, then copy this element, else
// check for a match, and if there's no match, then
// copy this element.
if (dwFlags & STRING_FLAG_ALLOW_DUPLICATES)
{
fCopyThisElement = TRUE;
}
else
{
if (_wcsicmp (pszCurrent, pszAddString) != 0)
{
fCopyThisElement = TRUE;
}
}
// If we're allowing the copy, then copy!
if (fCopyThisElement)
{
wcscpy (pmszOrderNew + cchOffsetNew, pszCurrent);
cchOffsetNew +=
wcslen (pmszOrderNew + cchOffsetNew) + 1;
++dwCurrentIndex;
}
// Update the offset
//
cchOffsetOld += wcslen (pmszIn + cchOffsetOld) + 1;
}
}
// If we have the ENSURE_AT_END flag set or if the ENSURE_AT_INDEX
// flag was set and the index was greater than the possible
// index, this means we want to insert at the end
//
if (fEnsureAtEnd ||
(fEnsureAtIndex && (dwCurrentIndex <= dwStringIndex)))
{
wcscpy (pmszOrderNew + cchOffsetNew, pszAddString);
cchOffsetNew += wcslen (pmszOrderNew + cchOffsetNew) + 1;
}
// Put the last of the double-NULL chars on the end.
//
pszCurrent = pmszOrderNew + cchOffsetNew;
pszCurrent[0] = (WCHAR) 0;
*ppmszOut = pmszOrderNew;
}
else
{
hr = E_OUTOFMEMORY;
}
}
TraceError("HrAddSzToMultiSz", hr);
return hr;
}
//+---------------------------------------------------------------------------
//
// Function: HrCreateArrayOfStringPointersIntoMultiSz
//
// Purpose: Allocates and initializes an array of string pointers.
// The array of pointers is initialized to point to the
// individual strings in a multi-sz.
//
// Arguments:
// pmszSrc [in] The multi-sz to index.
// pcStrings [out] Returned count of string pointers in the array.
// papsz [out] Returned array of string pointers.
//
// Returns: S_OK or E_OUTOFMEMORY
//
// Author: shaunco 20 Jun 1998
//
// Notes: It is the callers responsibility to ensure there is at
// least one string. The restriction is explicitly chosen to
// reduce confusion about what would be returned if the
// multi-sz were empty.
//
// Free the returned array with free.
//
HRESULT
HrCreateArrayOfStringPointersIntoMultiSz (
IN PCWSTR pmszSrc,
OUT UINT* pcStrings,
OUT PCWSTR** papsz)
{
Assert (pmszSrc && *pmszSrc);
Assert (papsz);
// First, count the number of strings in the multi-sz.
//
UINT cStrings = 0;
PCWSTR pmsz;
for (pmsz = pmszSrc; *pmsz; pmsz += wcslen(pmsz) + 1)
{
cStrings++;
}
Assert (cStrings); // See Notes above.
*pcStrings = cStrings;
// Allocate enough memory for the array.
//
HRESULT hr = HrMalloc (cStrings * sizeof(PWSTR*),
reinterpret_cast<VOID**>(papsz));
if (S_OK == hr)
{
// Initialize the returned array. ppsz is a pointer to each
// element of the array. It is incremented after each element
// is initialized.
//
PCWSTR* ppsz = *papsz;
for (pmsz = pmszSrc; *pmsz; pmsz += wcslen(pmsz) + 1)
{
*ppsz = pmsz;
ppsz++;
}
}
TraceError ("HrCreateArrayOfStringPointersIntoMultiSz", hr);
return hr;
}
//+---------------------------------------------------------------------------
//
// Function: RemoveSzFromMultiSz
//
// Purpose: Remove all occurrences of a string from a multi-sz. The
// removals are performed in place.
//
// Arguments:
// psz [in] The string to remove.
// pmsz [in out] The multi-sz to remove psz from.
// dwFlags [in] Can contain one or more of the following
// values:
//
// STRING_FLAG_REMOVE_SINGLE
// Don't remove more than one value, if
// multiple are present.
// [default] STRING_FLAG_REMOVE_ALL
// If multiple matching values are present,
// remove them all.
// pfRemoved [out] Set to TRUE on return if one or more strings
// were removed.
//
// Returns: nothing
//
// Author: shaunco 8 Jun 1998
//
// Notes:
//
VOID
RemoveSzFromMultiSz (
IN PCWSTR psz,
IN OUT PWSTR pmsz,
IN DWORD dwFlags,
OUT BOOL* pfRemoved)
{
Assert (pfRemoved);
// Initialize the output parameters.
//
*pfRemoved = FALSE;
if (!pmsz || !psz || !*psz)
{
return;
}
// Look for all occurrences of psz in pmsz. When one is found, move
// the remaining part of the multi-sz over it.
//
while (*pmsz)
{
if (0 == _wcsicmp (pmsz, psz))
{
PWSTR pmszRemain = pmsz + (wcslen (pmsz) + 1);
INT cchRemain = CchOfMultiSzAndTermSafe (pmszRemain);
MoveMemory (pmsz, pmszRemain, cchRemain * sizeof(WCHAR));
*pfRemoved = TRUE;
if (dwFlags & STRING_FLAG_REMOVE_SINGLE)
{
break;
}
}
else
{
pmsz += wcslen (pmsz) + 1;
}
}
}
//+---------------------------------------------------------------------------
//
// Function: SzListToMultiSz
//
// Purpose: Converts a comma-separated list into a multi-sz style list.
//
// Arguments:
// psz [in] String to be converted. It is not modified.
// pcb [out] Number of *bytes* in the resulting string. If NULL,
// size is not returned.
// ppszOut [out] Resulting string.
//
// Returns: Nothing.
//
// Author: danielwe 3 Apr 1997
//
// Notes: Resulting string must be freed with MemFree.
//
VOID
SzListToMultiSz (
IN PCWSTR psz,
OUT DWORD* pcb,
OUT PWSTR* ppszOut)
{
Assert(psz);
Assert(ppszOut);
PCWSTR pch;
INT cch;
PWSTR pszOut;
const WCHAR c_chSep = L',';
// Add 2 to the length. One for final NULL, and one for second NULL.
cch = wcslen (psz) + 2;
pszOut = (PWSTR)MemAlloc(CchToCb(cch));
if (pszOut)
{
ZeroMemory(pszOut, CchToCb(cch));
if (pcb)
{
*pcb = CchToCb(cch);
}
*ppszOut = pszOut;
// count the number of separator chars and put NULLs there
//
for (pch = psz; *pch; pch++)
{
if (*pch == c_chSep)
{
*pszOut++ = 0;
}
else
{
*pszOut++ = *pch;
}
}
}
}