|
|
//+---------------------------------------------------------------------------
//
// 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"
#include "ncmsz.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 PCTSTR pmsz) { // NULL strings have zero length by definition.
if (!pmsz) return 0;
ULONG cchTotal = 0; ULONG cch; while (*pmsz) { cch = _tcslen (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 PCTSTR 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 PCTSTR psz, IN PCTSTR pmsz) { if (!pmsz || !psz) { return FALSE; }
while (*pmsz) { if (0 == _tcsicmp (pmsz, psz)) { return TRUE; } pmsz += _tcslen (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 PCTSTR psz, IN PCTSTR 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 == _tcsicmp (pmsz, psz)) { if (!fFoundAlready) { *pdwIndex = dwIndex; fFoundAlready = TRUE; } else if (pfDuplicatePresent) { *pfDuplicatePresent = TRUE; } } pmsz += _tcslen (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 PCTSTR pszAddString, IN PCTSTR pmszIn, IN DWORD dwFlags, IN DWORD dwStringIndex, OUT PTSTR* 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 + _tcslen (pszAddString) + 1 + 1;
PTSTR pmszOrderNew = (PTSTR) MemAlloc(cchAllocSize * sizeof(TCHAR));
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.
//
_tcscpy (pmszOrderNew + cchOffsetNew, pszAddString); cchOffsetNew += _tcslen ((PTSTR)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; PTSTR pszCurrent; while ((cchOffsetOld + 1) < cchDataSize) { if (fEnsureAtIndex && (dwCurrentIndex == dwStringIndex)) { // Insert our passed-in value at the current index of the
// new buffer.
//
_tcscpy (pmszOrderNew + cchOffsetNew, pszAddString); cchOffsetNew += _tcslen (pmszOrderNew + cchOffsetNew) + 1; ++dwCurrentIndex; } else { BOOL fCopyThisElement = FALSE;
// Get the next string in the list.
//
pszCurrent = (PTSTR) (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 (_tcsicmp (pszCurrent, pszAddString) != 0) { fCopyThisElement = TRUE; } }
// If we're allowing the copy, then copy!
if (fCopyThisElement) { _tcscpy (pmszOrderNew + cchOffsetNew, pszCurrent); cchOffsetNew += _tcslen (pmszOrderNew + cchOffsetNew) + 1; ++dwCurrentIndex; }
// Update the offset
//
cchOffsetOld += _tcslen (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))) { _tcscpy (pmszOrderNew + cchOffsetNew, pszAddString); cchOffsetNew += _tcslen (pmszOrderNew + cchOffsetNew) + 1; }
// Put the last of the double-NULL chars on the end.
//
pszCurrent = pmszOrderNew + cchOffsetNew; pszCurrent[0] = (TCHAR) 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 PCTSTR pmszSrc, OUT UINT* pcStrings, OUT PCTSTR** papsz) { Assert (pmszSrc && *pmszSrc); Assert (papsz);
// First, count the number of strings in the multi-sz.
//
UINT cStrings = 0; PCTSTR pmsz; for (pmsz = pmszSrc; *pmsz; pmsz += _tcslen(pmsz) + 1) { cStrings++; }
Assert (cStrings); // See Notes above.
*pcStrings = cStrings;
// Allocate enough memory for the array.
//
HRESULT hr = HrMalloc (cStrings * sizeof(PTSTR*), 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.
//
PCTSTR* ppsz = *papsz;
for (pmsz = pmszSrc; *pmsz; pmsz += _tcslen(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 PCTSTR psz, IN OUT PTSTR 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 == _tcsicmp (pmsz, psz)) { PTSTR pmszRemain = pmsz + (_tcslen (pmsz) + 1); INT cchRemain = CchOfMultiSzAndTermSafe (pmszRemain);
MoveMemory (pmsz, pmszRemain, cchRemain * sizeof(TCHAR));
*pfRemoved = TRUE;
if (dwFlags & STRING_FLAG_REMOVE_SINGLE) { break; } } else { pmsz += _tcslen (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 PCTSTR psz, OUT DWORD* pcb, OUT PTSTR* ppszOut) { Assert(psz); Assert(ppszOut);
PCTSTR pch; INT cch; PTSTR pszOut; const TCHAR c_chSep = L',';
// Add 2 to the length. One for final NULL, and one for second NULL.
cch = _tcslen (psz) + 2;
pszOut = (PTSTR)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; } } } }
|