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.
1058 lines
32 KiB
1058 lines
32 KiB
//+---------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1997.
|
|
//
|
|
// File: N C S T R I N G . C P P
|
|
//
|
|
// Contents: Common string routines.
|
|
//
|
|
// Notes:
|
|
//
|
|
// Author: shaunco 24 Mar 1997
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
#include <pch.h>
|
|
#pragma hdrstop
|
|
#include "ncdebug.h"
|
|
#include "ncstring.h"
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: CbOfSzSafe, CbOfSzaSafe,
|
|
// CbOfSzAndTermSafe, CbOfSzaAndTermSafe
|
|
//
|
|
// Purpose: Count the bytes required to hold a string. The string
|
|
// may be NULL in which case zero is returned.
|
|
//
|
|
// Arguments:
|
|
// psz [in] String to return count of bytes for.
|
|
//
|
|
// Returns: Count of bytes required to store string.
|
|
//
|
|
// Author: shaunco 24 Mar 1997
|
|
//
|
|
// Notes: 'AndTerm' variants includes space for the null-terminator.
|
|
//
|
|
ULONG
|
|
CbOfSzSafe (
|
|
IN PCWSTR psz)
|
|
{
|
|
return (psz) ? CbOfSz(psz) : 0;
|
|
}
|
|
|
|
ULONG
|
|
CbOfSzaSafe (
|
|
IN PCSTR psza)
|
|
{
|
|
return (psza) ? CbOfSza(psza) : 0;
|
|
}
|
|
|
|
ULONG
|
|
CbOfSzAndTermSafe (
|
|
IN PCWSTR psz)
|
|
{
|
|
return (psz) ? CbOfSzAndTerm(psz) : 0;
|
|
}
|
|
|
|
ULONG
|
|
CbOfSzaAndTermSafe (
|
|
IN PCSTR psza)
|
|
{
|
|
return (psza) ? CbOfSzaAndTerm(psza) : 0;
|
|
}
|
|
|
|
ULONG
|
|
CchOfSzSafe (
|
|
IN PCWSTR psz)
|
|
{
|
|
return (psz) ? wcslen(psz) : 0;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: DwFormatString
|
|
//
|
|
// Purpose: Uses FormatMessage to format a string from variable arguments.
|
|
// The string is formatted into a fixed-size buffer the caller
|
|
// provides.
|
|
// See the description of FormatMessage in the Win32 API.
|
|
//
|
|
// Arguments:
|
|
// pszFmt [in] pointer to format string
|
|
// pszBuf [out] pointer to formatted output
|
|
// cchBuf [in] count of characters in pszBuf
|
|
// ... [in] replaceable string parameters
|
|
//
|
|
// Returns: the return value of FormatMessage
|
|
//
|
|
// Author: shaunco 15 Apr 1997
|
|
//
|
|
// Notes: The variable arguments must be strings otherwise
|
|
// FormatMessage will barf.
|
|
//
|
|
DWORD
|
|
WINAPIV
|
|
DwFormatString (
|
|
IN PCWSTR pszFmt,
|
|
OUT PWSTR pszBuf,
|
|
IN DWORD cchBuf,
|
|
IN ...)
|
|
{
|
|
Assert (pszFmt);
|
|
|
|
va_list val;
|
|
va_start(val, cchBuf);
|
|
DWORD dwRet = FormatMessage (FORMAT_MESSAGE_FROM_STRING,
|
|
pszFmt, 0, 0, pszBuf, cchBuf, &val);
|
|
va_end(val);
|
|
return dwRet;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: DwFormatStringWithLocalAlloc
|
|
//
|
|
// Purpose: Uses FormatMessage to format a string from variable arguments.
|
|
// The string is allocated by FormatMessage using LocalAlloc.
|
|
// See the description of FormatMessage in the Win32 API.
|
|
//
|
|
// Arguments:
|
|
// pszFmt [in] pointer to format string
|
|
// ppszBuf [out] the returned formatted string
|
|
// ... [in] replaceable string parameters
|
|
//
|
|
// Returns: the return value of FormatMessage
|
|
//
|
|
// Author: shaunco 3 May 1997
|
|
//
|
|
// Notes: The variable arguments must be strings otherwise
|
|
// FormatMessage will barf.
|
|
//
|
|
DWORD
|
|
WINAPIV
|
|
DwFormatStringWithLocalAlloc (
|
|
IN PCWSTR pszFmt,
|
|
OUT PWSTR* ppszBuf,
|
|
IN ...)
|
|
{
|
|
Assert (pszFmt);
|
|
|
|
va_list val;
|
|
va_start(val, ppszBuf);
|
|
DWORD dwRet = FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
|
FORMAT_MESSAGE_FROM_STRING,
|
|
pszFmt, 0, 0,
|
|
(PWSTR)ppszBuf,
|
|
0, &val);
|
|
va_end(val);
|
|
return dwRet;
|
|
}
|
|
|
|
//+--------------------------------------------------------------------------
|
|
//
|
|
// Function: FFindStringInCommaSeparatedList
|
|
//
|
|
// Purpose: Given a comma separated list, pszList, and a search string,
|
|
// pszSubString, this routine will try to locate pszSubString
|
|
// in the list,
|
|
//
|
|
// Arguments:
|
|
// pszSubString [in] The string to search for
|
|
// pszList [in] The list to search in
|
|
// eIgnoreSpaces [in] If NC_IGNORE, skip leading and trailing spaces
|
|
// when comparing.
|
|
// If NC_DONT_IGNORE, don't skip leading and
|
|
// trailing spaces.
|
|
// dwPosition [out] Optional. If found, the position of the first
|
|
// occurrence of the substring in the list. The first
|
|
// position is 0.
|
|
//
|
|
// Returns: BOOL. TRUE if pszSubString is in pszList, FALSE otherwise
|
|
//
|
|
// Author: billbe 09 Sep 1997
|
|
//
|
|
// Notes:
|
|
//
|
|
BOOL
|
|
FFindStringInCommaSeparatedList (
|
|
IN PCWSTR pszSubString,
|
|
IN PCWSTR pszList,
|
|
IN NC_IGNORE_SPACES eIgnoreSpaces,
|
|
OUT DWORD* pdwPosition)
|
|
{
|
|
|
|
Assert(pszSubString);
|
|
Assert(pszList);
|
|
|
|
int cchSubString = lstrlenW (pszSubString);
|
|
int cchList = lstrlenW (pszList);
|
|
|
|
BOOL fFound = FALSE;
|
|
PCWSTR pszTemp = pszList;
|
|
int nIndex;
|
|
const WCHAR c_chDelim = L',';
|
|
|
|
// Initialize out param if specified.
|
|
if (pdwPosition)
|
|
{
|
|
*pdwPosition = 0;
|
|
}
|
|
|
|
// This routine searches the list for a substring matching pszSubString
|
|
// If found, checks are made to ensure the substring is not part of
|
|
// a larger substring. We continue until we find the substring or we
|
|
// have searched through the entire list.
|
|
//
|
|
while (!fFound)
|
|
{
|
|
// Search for the next occurence of the substring.
|
|
if (pszTemp = wcsstr (pszTemp, pszSubString))
|
|
{
|
|
// we found an occurrence, so now we make sure it is not part of
|
|
// a larger string.
|
|
//
|
|
|
|
fFound = TRUE;
|
|
nIndex = (int)(pszTemp - pszList);
|
|
|
|
// If the substring was not found at the beginning of the list
|
|
// we check the previous character to ensure it is the delimiter.
|
|
if (nIndex > 0)
|
|
{
|
|
int cchSubtract = 1;
|
|
|
|
// If we are to ignore leading spaces, find the first
|
|
// non-space character if there is one.
|
|
//
|
|
if (NC_IGNORE == eIgnoreSpaces)
|
|
{
|
|
// Keep skipping leading spaces until we either find a
|
|
// non-space or pass the beginning of the list.
|
|
while ((L' ' == *(pszTemp - cchSubtract)) &&
|
|
cchSubtract <= nIndex)
|
|
{
|
|
cchSubtract--;
|
|
}
|
|
}
|
|
|
|
// If we haven't passed the beginning of the list, compare the
|
|
// character.
|
|
if (cchSubtract <= nIndex)
|
|
{
|
|
fFound = (*(pszTemp - cchSubtract) == c_chDelim);
|
|
}
|
|
}
|
|
|
|
// If the end of the substring is not the end of the list
|
|
// we check the character after the substring to ensure
|
|
// it is a delimiter.
|
|
if (fFound && ((nIndex + cchSubString) < cchList))
|
|
{
|
|
int cchAdd = cchSubString;
|
|
|
|
// If we are ignoring white spaces, we have to check the next
|
|
// available non-space character
|
|
//
|
|
if (NC_IGNORE == eIgnoreSpaces)
|
|
{
|
|
// Search for a non-space until we find one or pass
|
|
// the end of the list
|
|
while ((L' ' == *(pszTemp + cchAdd)) &&
|
|
(cchAdd + nIndex) < cchList)
|
|
{
|
|
cchAdd++;
|
|
}
|
|
}
|
|
|
|
// If we haven't passed the end of the list, check the
|
|
// character
|
|
if (nIndex + cchAdd < cchList)
|
|
{
|
|
fFound = (*(pszTemp + cchSubString) == c_chDelim);
|
|
}
|
|
|
|
|
|
if (NC_IGNORE == eIgnoreSpaces)
|
|
{
|
|
// advance pointer the number of white spaces we skipped
|
|
// so we won't check those characters on the next pass
|
|
Assert(cchAdd >= cchSubString);
|
|
pszTemp += (cchAdd - cchSubString);
|
|
}
|
|
}
|
|
|
|
// At this point, if the checks worked out, we found our string
|
|
// and will be exiting the loop
|
|
//
|
|
|
|
// Advance the temp pointer the length of the sub string we are
|
|
// searching for so we can search the rest of the list
|
|
// if we need to
|
|
pszTemp += cchSubString;
|
|
}
|
|
else
|
|
{
|
|
// Search string wasn't found
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If we found the string and the out param exists,
|
|
// then we need to return the strings position in the list.
|
|
//
|
|
if (fFound && pdwPosition)
|
|
{
|
|
// We will use the number of delimters found before the string
|
|
// as an indicator of the strings position.
|
|
//
|
|
|
|
// Start at the beginning
|
|
pszTemp = pszList;
|
|
PWSTR pszDelim;
|
|
|
|
// The string is nIndex characters in the list so lets get
|
|
// its correct address.
|
|
PCWSTR pszFoundString = pszList + nIndex;
|
|
|
|
// As long as we keep finding a delimiter in the list...
|
|
while (pszDelim = wcschr(pszTemp, c_chDelim))
|
|
{
|
|
// If the delimiter we just found is before our string...
|
|
if (pszDelim < pszFoundString)
|
|
{
|
|
// Increase our position indicator
|
|
++(*pdwPosition);
|
|
|
|
// Move the temp pointer to the next string
|
|
pszTemp = pszDelim + 1;
|
|
|
|
continue;
|
|
}
|
|
|
|
// The delimiter we just found is located after our
|
|
// found string so get out of the loop.
|
|
break;
|
|
}
|
|
}
|
|
|
|
return fFound;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: FIsSubstr
|
|
//
|
|
// Purpose: Case *insensitive* substring search.
|
|
//
|
|
// Arguments:
|
|
// pszSubString [in] Substring to look for.
|
|
// pszString [in] String to search in.
|
|
//
|
|
// Returns: TRUE if substring was found, FALSE otherwise.
|
|
//
|
|
// Author: danielwe 25 Feb 1997
|
|
//
|
|
// Notes: Allocates temp buffers on stack so they do not need to be
|
|
// freed.
|
|
//
|
|
BOOL
|
|
FIsSubstr (
|
|
IN PCWSTR pszSubString,
|
|
IN PCWSTR pszString)
|
|
{
|
|
PWSTR pszStringUpper;
|
|
PWSTR pszSubStringUpper;
|
|
|
|
Assert(pszString);
|
|
Assert(pszSubString);
|
|
|
|
#ifndef STACK_ALLOC_DOESNT_WORK
|
|
pszStringUpper = (PWSTR)
|
|
(PvAllocOnStack (CbOfSzAndTerm(pszString)));
|
|
|
|
pszSubStringUpper = (PWSTR)
|
|
(PvAllocOnStack (CbOfSzAndTerm(pszSubString)));
|
|
#else
|
|
pszStringUpper = MemAlloc(CbOfSzAndTerm(pszString));
|
|
pszSubStringUpper = MemAlloc(CbOfSzAndTerm(pszSubString));
|
|
#endif
|
|
|
|
lstrcpyW (pszStringUpper, pszString);
|
|
lstrcpyW (pszSubStringUpper, pszSubString);
|
|
|
|
// Convert both strings to uppercase before calling strstr
|
|
CharUpper (pszStringUpper);
|
|
CharUpper (pszSubStringUpper);
|
|
|
|
#ifndef STACK_ALLOC_DOESNT_WORK
|
|
return NULL != wcsstr(pszStringUpper, pszSubStringUpper);
|
|
#else
|
|
BOOL fRet = (NULL != wcsstr (pszStringUpper, pszSubStringUpper));
|
|
MemFree(pszStringUpper);
|
|
MemFree(pszSubStringUpper);
|
|
|
|
return fRet;
|
|
#endif
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: HrRegAddStringToDelimitedSz
|
|
//
|
|
// Purpose: Add a string into a REG_MULTI_SZ registry value.
|
|
//
|
|
// Arguments:
|
|
// pszAddString [in] The string to add to the delimited psz.
|
|
// pszIn [in] The delimited psz list.
|
|
// chDelimiter [in] The character to be used to delimit the
|
|
// values. Most multi-valued REG_SZ strings are
|
|
// delimited with either ',' or ' '. This will
|
|
// be used to delimit the value that we add,
|
|
// as well.
|
|
// 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
|
|
// Insert the string as the first element of
|
|
// the list.
|
|
// STRING_FLAG_ENSURE_AT_END
|
|
// Insert the string as the last
|
|
// element of the list. This can not be used
|
|
// with STRING_FLAG_ENSURE_AT_FRONT.
|
|
// STRING_FLAG_ENSURE_AT_INDEX
|
|
// Ensure that the string is at dwStringIndex
|
|
// in the psz. If the index specified
|
|
// is greater than the number of strings
|
|
// in the psz, the string will be
|
|
// placed at the end.
|
|
// 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 delimited psz.
|
|
//
|
|
//
|
|
// Returns: S_OK or an HRESULT_FROM_WIN32 error code.
|
|
//
|
|
// Author: jeffspr 27 Mar 1997
|
|
//
|
|
// Modified: BillBe 9 Nov 1998
|
|
// (Extracted from HrRegAddStringToSz and modified)
|
|
//
|
|
//
|
|
// Note:
|
|
// Might want to allow for the removal of leading/trailing spaces
|
|
//
|
|
HRESULT
|
|
HrAddStringToDelimitedSz (
|
|
IN PCWSTR pszAddString,
|
|
IN PCWSTR pszIn,
|
|
IN WCHAR chDelimiter,
|
|
IN DWORD dwFlags,
|
|
IN DWORD dwStringIndex,
|
|
OUT PWSTR* ppszOut)
|
|
{
|
|
Assert(pszAddString);
|
|
Assert(ppszOut);
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
// Don't continue if the pointers are NULL
|
|
if (!pszAddString || !ppszOut)
|
|
{
|
|
hr = E_POINTER;
|
|
}
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
// Initialize out param
|
|
*ppszOut = NULL;
|
|
}
|
|
|
|
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 HrAddStringToSz");
|
|
hr = E_INVALIDARG;
|
|
}
|
|
|
|
// Have to specify at least one of these
|
|
if (!fEnsureAtFront && !fEnsureAtEnd && !fEnsureAtIndex)
|
|
{
|
|
AssertSz(FALSE, "Must specify a STRING_FLAG_ENSURE flag");
|
|
hr = E_INVALIDARG;
|
|
}
|
|
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
// Alloc the new blob, including enough space for the trailing comma
|
|
//
|
|
*ppszOut = (PWSTR) MemAlloc (CbOfSzAndTermSafe(pszIn) +
|
|
CbOfSzSafe(pszAddString) + sizeof(WCHAR));
|
|
|
|
if (!*ppszOut)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
DWORD dwCurrentIndex = 0; // Current index in the new buffer
|
|
|
|
// Prime the new string
|
|
//
|
|
(*ppszOut)[0] = L'\0';
|
|
|
|
// If we have the "ensure at front" flag, do so with the passed in
|
|
// value. We also do this if we have the ensure at index flag
|
|
// set with index of 0 or if the ensure at index is set but
|
|
// the input string is null or empty
|
|
//
|
|
if (fEnsureAtFront || (fEnsureAtIndex && (0 == dwStringIndex)) ||
|
|
(fEnsureAtIndex && (!pszIn || !*pszIn)))
|
|
{
|
|
lstrcpyW (*ppszOut, pszAddString);
|
|
++dwCurrentIndex;
|
|
}
|
|
|
|
// If there was a previous value, walk through it and copy as needed.
|
|
// If not, then we're done.
|
|
if (pszIn && *pszIn)
|
|
{
|
|
PCWSTR pszCurrent = pszIn;
|
|
|
|
// Loop through the old buffer, and copy all of the strings that
|
|
// are not identical to our insertion string.
|
|
//
|
|
|
|
// Find the first string's end (at the delimiter).
|
|
PCWSTR pszEnd = wcschr (pszCurrent, chDelimiter);
|
|
|
|
while (*pszCurrent)
|
|
{
|
|
// If the delimiter didn't exist, set the end to the end of the
|
|
// entire string
|
|
//
|
|
if (!pszEnd)
|
|
{
|
|
pszEnd = pszCurrent + lstrlenW (pszCurrent);
|
|
}
|
|
|
|
LONG lLength = lstrlenW (*ppszOut);
|
|
if (fEnsureAtIndex && (dwCurrentIndex == dwStringIndex))
|
|
{
|
|
// We know we are not at the first item since
|
|
// this would mean dwStringIndex is 0 and we would
|
|
// have copied the string before this point
|
|
//
|
|
(*ppszOut)[lLength++] = chDelimiter;
|
|
(*ppszOut)[lLength++] = L'\0';
|
|
|
|
// Append the string.
|
|
lstrcatW (*ppszOut, pszAddString);
|
|
++dwCurrentIndex;
|
|
}
|
|
else
|
|
{
|
|
DWORD cch = (DWORD)(pszEnd - pszCurrent);
|
|
// If we are allowing duplicates or the current string
|
|
// doesn't match the string we want to add, then we will
|
|
// copy it.
|
|
//
|
|
if ((dwFlags & STRING_FLAG_ALLOW_DUPLICATES) ||
|
|
(_wcsnicmp (pszCurrent, pszAddString, cch) != 0))
|
|
{
|
|
// If we're not the first item, then add the delimiter.
|
|
//
|
|
if (lLength > 0)
|
|
{
|
|
(*ppszOut)[lLength++] = chDelimiter;
|
|
(*ppszOut)[lLength++] = L'\0';
|
|
}
|
|
|
|
// Append the string.
|
|
wcsncat (*ppszOut, pszCurrent, cch);
|
|
++dwCurrentIndex;
|
|
}
|
|
|
|
// Advance the pointer to one past the end of the current
|
|
// string unless, the end is not the delimiter but NULL.
|
|
// In that case, set the current point to equal the end
|
|
// pointer
|
|
//
|
|
pszCurrent = pszEnd + (*pszEnd ? 1 : 0);
|
|
|
|
// If the current pointer is not at the end of the input
|
|
// string, then find the next delimiter
|
|
//
|
|
if (*pszCurrent)
|
|
{
|
|
pszEnd = wcschr (pszCurrent, chDelimiter);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// If we don't have the "insert at front" flag, then we should insert
|
|
// at the end (this is the same as having the
|
|
// STRING_FLAG_ENSURE_AT_END flag set)
|
|
//
|
|
if (fEnsureAtEnd ||
|
|
(fEnsureAtIndex && (dwCurrentIndex <= dwStringIndex)))
|
|
{
|
|
LONG lLength = lstrlenW (*ppszOut);
|
|
|
|
// If we're not the first item, add the delimiter.
|
|
//
|
|
if (lstrlenW (*ppszOut) > 0)
|
|
{
|
|
(*ppszOut)[lLength++] = chDelimiter;
|
|
(*ppszOut)[lLength++] = L'\0';
|
|
}
|
|
|
|
// Append the string.
|
|
//
|
|
lstrcatW (*ppszOut, pszAddString);
|
|
}
|
|
}
|
|
|
|
TraceError ("HrAddStringToDelimitedSz", hr);
|
|
return hr;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: HrRegRemoveStringFromDelimitedSz
|
|
//
|
|
// Purpose: Removes a string from a delimited string value
|
|
//
|
|
// Arguments:
|
|
// pszRemove [in] The string to be removed from the multi-sz
|
|
// pszIn [in] The delimited list to scan for pszRemove
|
|
// cDelimiter [in] The character to be used to delimit the
|
|
// values. Most multi-valued REG_SZ strings are
|
|
// delimited with either ',' or ' '.
|
|
// 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.
|
|
// STRING_FLAG_REMOVE_ALL
|
|
// If multiple matching values are present,
|
|
// remove them all.
|
|
// ppszOut [out] The string with pszRemove removed. Note
|
|
// that the output parameter is always set even
|
|
// if pszRemove did not exist in the list.
|
|
//
|
|
// Returns: S_OK or an HRESULT_FROM_WIN32 error code.
|
|
//
|
|
// Author: jeffspr 27 Mar 1997
|
|
//
|
|
// Modified: BillBe 10 Nov 1998
|
|
// (Extracted from HrRegAddStringToSz and modified)
|
|
//
|
|
//
|
|
//
|
|
// Note:
|
|
// Might want to allow for the removal of leading/trailing spaces
|
|
//
|
|
HRESULT
|
|
HrRemoveStringFromDelimitedSz(
|
|
IN PCWSTR pszRemove,
|
|
IN PCWSTR pszIn,
|
|
IN WCHAR chDelimiter,
|
|
IN DWORD dwFlags,
|
|
OUT PWSTR* ppszOut)
|
|
{
|
|
|
|
Assert(pszIn && *pszIn);
|
|
Assert(ppszOut);
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
// If the out param is not specified, get out
|
|
if (!ppszOut)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
// Alloc the new blob
|
|
//
|
|
hr = E_OUTOFMEMORY;
|
|
*ppszOut = (PWSTR) MemAlloc (CbOfSzAndTermSafe (pszIn));
|
|
|
|
if (*ppszOut)
|
|
{
|
|
hr = S_OK;
|
|
// Prime the new string
|
|
//
|
|
(*ppszOut)[0] = L'\0';
|
|
|
|
// If there was a previous value, walk through it and copy as needed.
|
|
// If not, then we're done
|
|
//
|
|
if (pszIn)
|
|
{
|
|
// Loop through the old buffer, and copy all of the strings that
|
|
// are not identical to our insertion string.
|
|
//
|
|
PCWSTR pszCurrent = pszIn;
|
|
|
|
// Loop through the old buffer, and copy all of the strings that
|
|
// are not identical to our insertion string.
|
|
//
|
|
|
|
// Find the first string's end (at the delimiter).
|
|
PCWSTR pszEnd = wcschr (pszCurrent, chDelimiter);
|
|
|
|
// Keep track of how many instances have been removed.
|
|
DWORD dwNumRemoved = 0;
|
|
|
|
while (*pszCurrent)
|
|
{
|
|
// If the delimiter didn't exist, set the end to the end of
|
|
// the entire string.
|
|
//
|
|
if (!pszEnd)
|
|
{
|
|
pszEnd = pszCurrent + lstrlenW (pszCurrent);
|
|
}
|
|
|
|
DWORD cch = (DWORD)(pszEnd - pszCurrent);
|
|
INT iCompare;
|
|
// If we have a match, and we want to remove it (meaning that
|
|
// if we have the remove-single set, that we haven't removed
|
|
// one already).
|
|
|
|
iCompare = _wcsnicmp (pszCurrent, pszRemove, cch);
|
|
|
|
if ((iCompare) ||
|
|
((dwFlags & STRING_FLAG_REMOVE_SINGLE) &&
|
|
(dwNumRemoved > 0)))
|
|
{
|
|
LONG lLength = lstrlenW (*ppszOut);
|
|
|
|
// If we're not the first item, then add the delimiter.
|
|
//
|
|
if (lLength > 0)
|
|
{
|
|
(*ppszOut)[lLength++] = chDelimiter;
|
|
(*ppszOut)[lLength++] = L'\0';
|
|
}
|
|
|
|
// Append the string.
|
|
wcsncat (*ppszOut, pszCurrent, cch);
|
|
}
|
|
else
|
|
{
|
|
dwNumRemoved++;
|
|
}
|
|
|
|
// Advance the pointer to one past the end of the current
|
|
// string unless, the end is not the delimiter but NULL.
|
|
// In that case, set the current point to equal the end
|
|
// pointer
|
|
//
|
|
pszCurrent = pszEnd + (*pszEnd ? 1 : 0);
|
|
|
|
// If the current pointer is not at the end of the input
|
|
// string, then find the next delimiter
|
|
//
|
|
if (*pszCurrent)
|
|
{
|
|
pszEnd = wcschr (pszCurrent, chDelimiter);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
TraceError("HrRemoveStringFromDelimitedSz", hr);
|
|
return hr;
|
|
}
|
|
|
|
PWSTR
|
|
PszAllocateAndCopyPsz (
|
|
IN PCWSTR pszSrc)
|
|
{
|
|
if (!pszSrc)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
ULONG cb = (wcslen (pszSrc) + 1) * sizeof(WCHAR);
|
|
PWSTR psz = (PWSTR)MemAlloc (cb);
|
|
if (psz)
|
|
{
|
|
CopyMemory (psz, pszSrc, cb);
|
|
}
|
|
|
|
return psz;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: SzLoadStringPcch
|
|
//
|
|
// Purpose: Load a resource string. (This function will never return NULL.)
|
|
//
|
|
// Arguments:
|
|
// hinst [in] Instance handle of module with the string resource.
|
|
// unId [in] Resource ID of the string to load.
|
|
// pcch [out] Pointer to returned character length.
|
|
//
|
|
// Returns: Pointer to the constant string.
|
|
//
|
|
// Author: shaunco 24 Mar 1997
|
|
//
|
|
// Notes: The loaded string is pointer directly into the read-only
|
|
// resource section. Any attempt to write through this pointer
|
|
// will generate an access violation.
|
|
//
|
|
// The implementations is referenced from "Win32 Binary Resource
|
|
// Formats" (MSDN) 4.8 String Table Resources
|
|
//
|
|
// User must have RCOPTIONS = -N turned on in your sources file.
|
|
//
|
|
PCWSTR
|
|
SzLoadStringPcch (
|
|
IN HINSTANCE hinst,
|
|
IN UINT unId,
|
|
OUT int* pcch)
|
|
{
|
|
Assert(hinst);
|
|
Assert(unId);
|
|
Assert(pcch);
|
|
|
|
static const WCHAR c_szSpace[] = L" ";
|
|
|
|
PCWSTR psz = c_szSpace;
|
|
int cch = 1;
|
|
|
|
// String Tables are broken up into 16 string segments. Find the segment
|
|
// containing the string we are interested in.
|
|
HRSRC hrsrcInfo = FindResource (hinst,
|
|
(PWSTR)ULongToPtr( ((LONG)(((USHORT)unId >> 4) + 1)) ),
|
|
RT_STRING);
|
|
if (hrsrcInfo)
|
|
{
|
|
// Page the resource segment into memory.
|
|
HGLOBAL hglbSeg = LoadResource (hinst, hrsrcInfo);
|
|
if (hglbSeg)
|
|
{
|
|
// Lock the resource.
|
|
psz = (PCWSTR)LockResource(hglbSeg);
|
|
if (psz)
|
|
{
|
|
// Move past the other strings in this segment.
|
|
// (16 strings in a segment -> & 0x0F)
|
|
unId &= 0x0F;
|
|
|
|
cch = 0;
|
|
do
|
|
{
|
|
psz += cch; // Step to start of next string
|
|
cch = *((WCHAR*)psz++); // PASCAL like string count
|
|
}
|
|
while (unId--);
|
|
|
|
// If we have a non-zero count, it includes the
|
|
// null-terminiator. Subtract this off for the return value.
|
|
//
|
|
if (cch)
|
|
{
|
|
cch--;
|
|
}
|
|
else
|
|
{
|
|
AssertSz(0, "String resource not found");
|
|
psz = c_szSpace;
|
|
cch = 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
psz = c_szSpace;
|
|
cch = 1;
|
|
TraceLastWin32Error("SzLoadStringPcch: LockResource failed.");
|
|
}
|
|
}
|
|
else
|
|
TraceLastWin32Error("SzLoadStringPcch: LoadResource failed.");
|
|
}
|
|
else
|
|
TraceLastWin32Error("SzLoadStringPcch: FindResource failed.");
|
|
|
|
*pcch = cch;
|
|
Assert(*pcch);
|
|
Assert(psz);
|
|
return psz;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: SzaDupSza
|
|
//
|
|
// Purpose: Duplicates a string
|
|
//
|
|
// Arguments:
|
|
// pszaSrc [in] string to be duplicated
|
|
//
|
|
// Returns: Pointer to the new copy of the string
|
|
//
|
|
// Author: CWill 25 Mar 1997
|
|
//
|
|
// Notes: The string return must be freed (MemFree).
|
|
//
|
|
PSTR
|
|
SzaDupSza (
|
|
PCSTR pszaSrc)
|
|
{
|
|
AssertSz(pszaSrc, "Invalid source string");
|
|
|
|
PSTR pszaDst;
|
|
pszaDst = (PSTR) MemAlloc (CbOfSzaAndTerm(pszaSrc));
|
|
|
|
if(pszaDst) lstrcpyA(pszaDst, pszaSrc);
|
|
|
|
return pszaDst;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: SzDupSz
|
|
//
|
|
// Purpose: Duplicates a string
|
|
//
|
|
// Arguments:
|
|
// pszSrc [in] string to be duplicated
|
|
//
|
|
// Returns: Pointer to the new copy of the string
|
|
//
|
|
// Author: CWill 25 Mar 1997
|
|
//
|
|
// Notes: The string return must be freed.
|
|
//
|
|
PWSTR
|
|
SzDupSz (
|
|
IN PCWSTR pszSrc)
|
|
{
|
|
AssertSz(pszSrc, "Invalid source string");
|
|
|
|
PWSTR pszDst;
|
|
pszDst = (PWSTR) MemAlloc (CbOfSzAndTerm(pszSrc));
|
|
if(pszDst) lstrcpyW (pszDst, pszSrc);
|
|
|
|
return pszDst;
|
|
}
|
|
|
|
BOOL
|
|
FSubstringMatch (
|
|
PCTSTR pStr1,
|
|
PCTSTR pStr2,
|
|
const WCHAR** ppStart,
|
|
ULONG* pcch)
|
|
{
|
|
const WCHAR* p1Start;
|
|
const WCHAR* p1End;
|
|
const WCHAR* p2Start;
|
|
const WCHAR* p2End;
|
|
ULONG cch1;
|
|
ULONG cch2;
|
|
|
|
if (ppStart)
|
|
{
|
|
*ppStart = NULL;
|
|
}
|
|
if (pcch)
|
|
{
|
|
*pcch = NULL;
|
|
}
|
|
|
|
p1End = pStr1;
|
|
while (1)
|
|
{
|
|
GetNextCommaSeparatedToken(p1Start, p1End, cch1);
|
|
if (!cch1)
|
|
{
|
|
break;
|
|
}
|
|
|
|
p2End = pStr2;
|
|
while (1)
|
|
{
|
|
GetNextCommaSeparatedToken(p2Start, p2End, cch2);
|
|
if (!cch2)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (cch1 == cch2)
|
|
{
|
|
if (0 == memcmp(p1Start, p2Start, cch1 * sizeof(WCHAR)))
|
|
{
|
|
if (ppStart)
|
|
{
|
|
*ppStart = p1Start;
|
|
}
|
|
if (pcch)
|
|
{
|
|
*pcch = cch1;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
VOID
|
|
DeleteColString (
|
|
list<tstring*>* pcolstr)
|
|
{
|
|
Assert( pcolstr );
|
|
|
|
list<tstring*>::const_iterator iter;
|
|
tstring* pstr;
|
|
|
|
for (iter = pcolstr->begin(); iter != pcolstr->end(); iter++)
|
|
{
|
|
pstr = *iter;
|
|
delete pstr;
|
|
}
|
|
pcolstr->erase( pcolstr->begin(), pcolstr->end() );
|
|
}
|
|
|
|
VOID
|
|
DeleteColString (
|
|
vector<tstring*>* pcolstr)
|
|
{
|
|
Assert( pcolstr );
|
|
|
|
vector<tstring*>::const_iterator iter;
|
|
tstring* pstr;
|
|
|
|
for (iter = pcolstr->begin(); iter != pcolstr->end(); iter++)
|
|
{
|
|
pstr = *iter;
|
|
delete pstr;
|
|
}
|
|
pcolstr->erase( pcolstr->begin(), pcolstr->end() );
|
|
}
|