1938 lines
56 KiB
C++
1938 lines
56 KiB
C++
// This is copied from the Microsoft Foundation Classes C++ library.
|
|
// Copyright (C) Microsoft Corporation, 1992 - 1999
|
|
// All rights reserved.
|
|
//
|
|
// This has been modified from the original MFC version to provide
|
|
// two classes: CStrW manipulates and stores only wide char strings,
|
|
// and CStr uses TCHARs.
|
|
//
|
|
|
|
#include "pch.h"
|
|
#include "proppage.h"
|
|
#if !defined(UNICODE)
|
|
#include <stdio.h>
|
|
#endif
|
|
#include "cstr.h"
|
|
|
|
#if !defined(_wcsinc)
|
|
#define _wcsinc(_pc) ((_pc)+1)
|
|
#endif
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// static class data, special inlines
|
|
|
|
// For an empty string, m_???Data will point here
|
|
// (note: avoids a lot of NULL pointer tests when we call standard
|
|
// C runtime libraries
|
|
TCHAR strChNilT = '\0';
|
|
|
|
// for creating empty key strings
|
|
const CStr strEmptyStringT;
|
|
|
|
void CStr::Init()
|
|
{
|
|
m_nDataLength = m_nAllocLength = 0;
|
|
m_pchData = (LPTSTR)&strChNilT;
|
|
}
|
|
|
|
// declared static
|
|
void CStr::SafeDelete(LPTSTR& lpch)
|
|
{
|
|
if (lpch != (LPTSTR)&strChNilT &&
|
|
lpch)
|
|
{
|
|
delete[] lpch;
|
|
lpch = 0;
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// Construction/Destruction
|
|
|
|
CStr::CStr()
|
|
{
|
|
Init();
|
|
}
|
|
|
|
CStr::CStr(const CStr& stringSrc)
|
|
{
|
|
// if constructing a String from another String, we make a copy of the
|
|
// original string data to enforce value semantics (i.e. each string
|
|
// gets a copy of its own
|
|
|
|
m_pchData = 0;
|
|
|
|
stringSrc.AllocCopy(*this, stringSrc.m_nDataLength, 0, 0);
|
|
}
|
|
|
|
BOOL CStr::AllocBuffer(int nLen)
|
|
// always allocate one extra character for '\0' termination
|
|
// assumes [optimistically] that data length will equal allocation length
|
|
{
|
|
dspAssert(nLen >= 0);
|
|
|
|
if (nLen == 0)
|
|
{
|
|
Init();
|
|
}
|
|
else
|
|
{
|
|
m_pchData = new TCHAR[nLen+1]; //REVIEW may throw an exception
|
|
if (!m_pchData)
|
|
{
|
|
Empty();
|
|
return FALSE;
|
|
}
|
|
m_pchData[nLen] = '\0';
|
|
m_nDataLength = nLen;
|
|
m_nAllocLength = nLen;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
void CStr::Empty()
|
|
{
|
|
SafeDelete(m_pchData);
|
|
Init();
|
|
dspAssert(m_nDataLength == 0);
|
|
dspAssert(m_nAllocLength == 0);
|
|
}
|
|
|
|
CStr::~CStr()
|
|
// free any attached data
|
|
{
|
|
SafeDelete(m_pchData);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// Helpers for the rest of the implementation
|
|
|
|
static inline int SafeStrlenT(LPCTSTR lpsz)
|
|
{
|
|
dspAssert(lpsz == NULL || IsValidString(lpsz, FALSE));
|
|
return (lpsz == NULL) ? 0 : lstrlen(lpsz);
|
|
}
|
|
|
|
void CStr::AllocCopy(CStr& dest, int nCopyLen, int nCopyIndex,
|
|
int nExtraLen) const
|
|
{
|
|
// will clone the data attached to this string
|
|
// allocating 'nExtraLen' characters
|
|
// Places results in uninitialized string 'dest'
|
|
// Will copy the part or all of original data to start of new string
|
|
|
|
int nNewLen = nCopyLen + nExtraLen;
|
|
|
|
if (nNewLen == 0)
|
|
{
|
|
dest.SafeDelete(dest.m_pchData);
|
|
dest.Init();
|
|
}
|
|
else
|
|
{
|
|
if (!dest.AllocBuffer(nNewLen)) return;
|
|
memcpy(dest.m_pchData, &m_pchData[nCopyIndex], nCopyLen*sizeof(TCHAR));
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// More sophisticated construction
|
|
|
|
CStr::CStr(LPCTSTR lpsz)
|
|
{
|
|
int nLen;
|
|
if ((nLen = SafeStrlenT(lpsz)) == 0)
|
|
Init();
|
|
else
|
|
{
|
|
if (!AllocBuffer(nLen))
|
|
{
|
|
Init();
|
|
return;
|
|
}
|
|
memcpy(m_pchData, lpsz, nLen*sizeof(TCHAR));
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Special conversion constructors
|
|
|
|
#ifdef UNICODE
|
|
CStr::CStr(LPCSTR lpsz)
|
|
{
|
|
int nSrcLen = lpsz != NULL ? lstrlenA(lpsz) : 0;
|
|
if (nSrcLen == 0)
|
|
Init();
|
|
else
|
|
{
|
|
if (!AllocBuffer(nSrcLen))
|
|
{
|
|
Init();
|
|
return;
|
|
}
|
|
mmc_mbstowcsz(m_pchData, lpsz, nSrcLen+1);
|
|
}
|
|
}
|
|
#else //UNICODE
|
|
CStr::CStr(LPCWSTR lpsz)
|
|
{
|
|
int nSrcLen = lpsz != NULL ? wcslen(lpsz) : 0;
|
|
if (nSrcLen == 0)
|
|
Init();
|
|
else
|
|
{
|
|
if (!AllocBuffer(nSrcLen*2))
|
|
{
|
|
Init();
|
|
return;
|
|
}
|
|
mmc_wcstombsz(m_pchData, lpsz, (nSrcLen*2)+1);
|
|
ReleaseBuffer();
|
|
}
|
|
}
|
|
#endif //!UNICODE
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// Assignment operators
|
|
// All assign a new value to the string
|
|
// (a) first see if the buffer is big enough
|
|
// (b) if enough room, copy on top of old buffer, set size and type
|
|
// (c) otherwise free old string data, and create a new one
|
|
//
|
|
// All routines return the new string (but as a 'const CStr&' so that
|
|
// assigning it again will cause a copy, eg: s1 = s2 = "hi there".
|
|
//
|
|
|
|
void CStr::AssignCopy(int nSrcLen, LPCTSTR lpszSrcData)
|
|
{
|
|
// check if it will fit
|
|
if (nSrcLen > m_nAllocLength)
|
|
{
|
|
// it won't fit, allocate another one
|
|
Empty();
|
|
if (!AllocBuffer(nSrcLen)) return;
|
|
}
|
|
if (nSrcLen != 0)
|
|
memcpy(m_pchData, lpszSrcData, nSrcLen*sizeof(TCHAR));
|
|
m_nDataLength = nSrcLen;
|
|
m_pchData[nSrcLen] = '\0';
|
|
}
|
|
|
|
const CStr& CStr::operator=(const CStr& stringSrc)
|
|
{
|
|
AssignCopy(stringSrc.m_nDataLength, stringSrc.m_pchData);
|
|
return *this;
|
|
}
|
|
|
|
const CStr& CStr::operator=(LPCTSTR lpsz)
|
|
{
|
|
dspAssert(lpsz == NULL || IsValidString(lpsz, FALSE));
|
|
AssignCopy(SafeStrlenT(lpsz), lpsz);
|
|
return *this;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Special conversion assignment
|
|
|
|
#ifdef UNICODE
|
|
const CStr& CStr::operator=(LPCSTR lpsz)
|
|
{
|
|
int nSrcLen = lpsz != NULL ? lstrlenA(lpsz) : 0;
|
|
// check if it will fit
|
|
if (nSrcLen > m_nAllocLength)
|
|
{
|
|
// it won't fit, allocate another one
|
|
Empty();
|
|
if (!AllocBuffer(nSrcLen)) return *this;
|
|
}
|
|
if (nSrcLen != 0)
|
|
mmc_mbstowcsz(m_pchData, lpsz, nSrcLen+1);
|
|
m_nDataLength = nSrcLen;
|
|
m_pchData[nSrcLen] = '\0';
|
|
return *this;
|
|
}
|
|
#else //!UNICODE
|
|
const CStr& CStr::operator=(LPCWSTR lpsz)
|
|
{
|
|
int nSrcLen = lpsz != NULL ? wcslen(lpsz) : 0;
|
|
nSrcLen *= 2;
|
|
// check if it will fit
|
|
if (nSrcLen > m_nAllocLength)
|
|
{
|
|
// it won't fit, allocate another one
|
|
Empty();
|
|
if (!AllocBuffer(nSrcLen)) return *this;
|
|
}
|
|
if (nSrcLen != 0)
|
|
{
|
|
mmc_wcstombsz(m_pchData, lpsz, nSrcLen+1);
|
|
ReleaseBuffer();
|
|
}
|
|
return *this;
|
|
}
|
|
#endif //!UNICODE
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// concatenation
|
|
|
|
// NOTE: "operator+" is done as friend functions for simplicity
|
|
// There are three variants:
|
|
// String + String
|
|
// and for ? = TCHAR, LPCTSTR
|
|
// String + ?
|
|
// ? + String
|
|
|
|
void CStr::ConcatCopy(int nSrc1Len, LPCTSTR lpszSrc1Data,
|
|
int nSrc2Len, LPCTSTR lpszSrc2Data)
|
|
{
|
|
// -- master concatenation routine
|
|
// Concatenate two sources
|
|
// -- assume that 'this' is a new String object
|
|
|
|
int nNewLen = nSrc1Len + nSrc2Len;
|
|
if (!AllocBuffer(nNewLen)) return;
|
|
memcpy(m_pchData, lpszSrc1Data, nSrc1Len*sizeof(TCHAR));
|
|
memcpy(&m_pchData[nSrc1Len], lpszSrc2Data, nSrc2Len*sizeof(TCHAR));
|
|
}
|
|
|
|
CStr STRAPI operator+(const CStr& string1, const CStr& string2)
|
|
{
|
|
CStr s;
|
|
s.ConcatCopy(string1.m_nDataLength, string1.m_pchData,
|
|
string2.m_nDataLength, string2.m_pchData);
|
|
return s;
|
|
}
|
|
|
|
CStr STRAPI operator+(const CStr& string, LPCTSTR lpsz)
|
|
{
|
|
dspAssert(lpsz == NULL || IsValidString(lpsz, FALSE));
|
|
CStr s;
|
|
s.ConcatCopy(string.m_nDataLength, string.m_pchData, SafeStrlenT(lpsz), lpsz);
|
|
return s;
|
|
}
|
|
|
|
CStr STRAPI operator+(LPCTSTR lpsz, const CStr& string)
|
|
{
|
|
dspAssert(lpsz == NULL || IsValidString(lpsz, FALSE));
|
|
CStr s;
|
|
s.ConcatCopy(SafeStrlenT(lpsz), lpsz, string.m_nDataLength, string.m_pchData);
|
|
return s;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// concatenate in place
|
|
|
|
void CStr::ConcatInPlace(int nSrcLen, LPCTSTR lpszSrcData)
|
|
{
|
|
// -- the main routine for += operators
|
|
|
|
// if the buffer is too small, or we have a width mis-match, just
|
|
// allocate a new buffer (slow but sure)
|
|
if (m_nDataLength + nSrcLen > m_nAllocLength)
|
|
{
|
|
// we have to grow the buffer, use the Concat in place routine
|
|
LPTSTR lpszOldData = m_pchData;
|
|
ConcatCopy(m_nDataLength, lpszOldData, nSrcLen, lpszSrcData);
|
|
dspAssert(lpszOldData != NULL);
|
|
SafeDelete(lpszOldData);
|
|
}
|
|
else
|
|
{
|
|
// fast concatenation when buffer big enough
|
|
memcpy(&m_pchData[m_nDataLength], lpszSrcData, nSrcLen*sizeof(TCHAR));
|
|
m_nDataLength += nSrcLen;
|
|
}
|
|
dspAssert(m_nDataLength <= m_nAllocLength);
|
|
m_pchData[m_nDataLength] = '\0';
|
|
}
|
|
|
|
const CStr& CStr::operator+=(LPCTSTR lpsz)
|
|
{
|
|
dspAssert(lpsz == NULL || IsValidString(lpsz, FALSE));
|
|
ConcatInPlace(SafeStrlenT(lpsz), lpsz);
|
|
return *this;
|
|
}
|
|
|
|
const CStr& CStr::operator+=(TCHAR ch)
|
|
{
|
|
ConcatInPlace(1, &ch);
|
|
return *this;
|
|
}
|
|
|
|
const CStr& CStr::operator+=(const CStr& string)
|
|
{
|
|
ConcatInPlace(string.m_nDataLength, string.m_pchData);
|
|
return *this;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Advanced direct buffer access
|
|
|
|
LPTSTR CStr::GetBuffer(int nMinBufLength)
|
|
{
|
|
dspAssert(nMinBufLength >= 0);
|
|
|
|
if (nMinBufLength > m_nAllocLength)
|
|
{
|
|
// we have to grow the buffer
|
|
LPTSTR lpszOldData = m_pchData;
|
|
int nOldLen = m_nDataLength; // AllocBuffer will tromp it
|
|
|
|
if (!AllocBuffer(nMinBufLength)) return NULL;
|
|
memcpy(m_pchData, lpszOldData, nOldLen*sizeof(TCHAR));
|
|
m_nDataLength = nOldLen;
|
|
m_pchData[m_nDataLength] = '\0';
|
|
|
|
SafeDelete(lpszOldData);
|
|
}
|
|
|
|
// return a pointer to the character storage for this string
|
|
dspAssert(m_pchData != NULL);
|
|
return m_pchData;
|
|
}
|
|
|
|
void CStr::ReleaseBuffer(int nNewLength)
|
|
{
|
|
if (nNewLength == -1)
|
|
nNewLength = lstrlen(m_pchData); // zero terminated
|
|
|
|
dspAssert(nNewLength <= m_nAllocLength);
|
|
m_nDataLength = nNewLength;
|
|
m_pchData[m_nDataLength] = '\0';
|
|
}
|
|
|
|
LPTSTR CStr::GetBufferSetLength(int nNewLength)
|
|
{
|
|
dspAssert(nNewLength >= 0);
|
|
|
|
GetBuffer(nNewLength);
|
|
m_nDataLength = nNewLength;
|
|
m_pchData[m_nDataLength] = '\0';
|
|
return m_pchData;
|
|
}
|
|
|
|
void CStr::FreeExtra()
|
|
{
|
|
dspAssert(m_nDataLength <= m_nAllocLength);
|
|
if (m_nDataLength != m_nAllocLength)
|
|
{
|
|
LPTSTR lpszOldData = m_pchData;
|
|
if (!AllocBuffer(m_nDataLength)) return;
|
|
memcpy(m_pchData, lpszOldData, m_nDataLength*sizeof(TCHAR));
|
|
dspAssert(m_pchData[m_nDataLength] == '\0');
|
|
SafeDelete(lpszOldData);
|
|
}
|
|
dspAssert(m_pchData != NULL);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Commonly used routines (rarely used routines in STREX.CPP)
|
|
|
|
int CStr::Find(TCHAR ch) const
|
|
{
|
|
// find first single character
|
|
LPTSTR lpsz = _tcschr(m_pchData, ch);
|
|
|
|
// return -1 if not found and index otherwise
|
|
return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData);
|
|
}
|
|
|
|
int CStr::FindOneOf(LPCTSTR lpszCharSet) const
|
|
{
|
|
dspAssert(IsValidString(lpszCharSet, FALSE));
|
|
LPTSTR lpsz = _tcspbrk(m_pchData, lpszCharSet);
|
|
return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// String conversion helpers (these use the current system locale)
|
|
|
|
int mmc_wcstombsz(char* mbstr, const wchar_t* wcstr, size_t count)
|
|
{
|
|
if (count == 0 && mbstr != NULL)
|
|
return 0;
|
|
|
|
int result = ::WideCharToMultiByte(CP_ACP, 0, wcstr, -1,
|
|
mbstr, static_cast<int>(count), NULL, NULL);
|
|
dspAssert(mbstr == NULL || result <= (int)count);
|
|
if (result > 0)
|
|
mbstr[result-1] = 0;
|
|
return result;
|
|
}
|
|
|
|
int mmc_mbstowcsz(wchar_t* wcstr, const char* mbstr, size_t count)
|
|
{
|
|
if (count == 0 && wcstr != NULL)
|
|
return 0;
|
|
|
|
int result = ::MultiByteToWideChar(CP_ACP, 0, mbstr, -1,
|
|
wcstr, static_cast<int>(count));
|
|
dspAssert(wcstr == NULL || result <= (int)count);
|
|
if (result > 0)
|
|
wcstr[result-1] = 0;
|
|
return result;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Windows extensions to strings
|
|
|
|
BOOL CStr::LoadString(HINSTANCE hInst, UINT nID)
|
|
{
|
|
dspAssert(nID != 0); // 0 is an illegal string ID
|
|
|
|
// Note: resource strings limited to 511 characters
|
|
TCHAR szBuffer[512];
|
|
UINT nSize = StrLoadString(hInst, nID, szBuffer);
|
|
AssignCopy(nSize, szBuffer);
|
|
return nSize > 0;
|
|
}
|
|
|
|
|
|
int STRAPI StrLoadString(HINSTANCE hInst, UINT nID, LPTSTR lpszBuf)
|
|
{
|
|
dspAssert(IsValidAddressz(lpszBuf, 512)); // must be big enough for 512 bytes
|
|
#ifdef DBG
|
|
// LoadString without annoying warning from the Debug kernel if the
|
|
// segment containing the string is not present
|
|
if (::FindResource(hInst, MAKEINTRESOURCE((nID>>4)+1), RT_STRING) == NULL)
|
|
{
|
|
lpszBuf[0] = '\0';
|
|
return 0; // not found
|
|
}
|
|
#endif //DBG
|
|
int nLen = ::LoadString(hInst, nID, lpszBuf, 511);
|
|
dspAssert(nLen);
|
|
if (nLen == 0)
|
|
lpszBuf[0] = '\0';
|
|
return nLen;
|
|
}
|
|
|
|
BOOL STRAPI IsValidAddressz(const void* lp, UINT nBytes, BOOL bReadWrite)
|
|
{
|
|
// simple version using Win-32 APIs for pointer validation.
|
|
return (lp != NULL && !IsBadReadPtr(lp, nBytes) &&
|
|
(!bReadWrite || !IsBadWritePtr((LPVOID)lp, nBytes)));
|
|
}
|
|
|
|
|
|
BOOL STRAPI IsValidString(LPCSTR lpsz, int nLength)
|
|
{
|
|
if (lpsz == NULL)
|
|
return FALSE;
|
|
return ::IsBadStringPtrA(lpsz, nLength) == 0;
|
|
}
|
|
|
|
BOOL STRAPI IsValidString(LPCWSTR lpsz, int nLength)
|
|
{
|
|
if (lpsz == NULL)
|
|
return FALSE;
|
|
|
|
return ::IsBadStringPtrW(lpsz, nLength) == 0;
|
|
}
|
|
|
|
|
|
#ifdef OLE_AUTOMATION
|
|
#ifdef UNICODE
|
|
BSTR CStr::AllocSysString()
|
|
{
|
|
BSTR bstr = ::SysAllocStringLen(m_pchData, m_nDataLength);
|
|
if (bstr == NULL)
|
|
;//REVIEW AfxThrowMemoryException();
|
|
|
|
return bstr;
|
|
}
|
|
|
|
BSTR CStr::SetSysString(BSTR* pbstr)
|
|
{
|
|
dspAssert(IsValidAddressz(pbstr, sizeof(BSTR)));
|
|
|
|
if (!::SysReAllocStringLen(pbstr, m_pchData, m_nDataLength))
|
|
; //REVIEW AfxThrowMemoryException();
|
|
|
|
dspAssert(*pbstr != NULL);
|
|
return *pbstr;
|
|
}
|
|
#endif
|
|
#endif // #ifdef OLE_AUTOMATION
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Orginally from StrEx.cpp
|
|
|
|
|
|
CStr::CStr(TCHAR ch, int nLength)
|
|
{
|
|
#ifndef UNICODE
|
|
dspAssert(!IsDBCSLeadByte(ch)); // can't create a lead byte string
|
|
#endif
|
|
if (nLength < 1)
|
|
{
|
|
// return empty string if invalid repeat count
|
|
Init();
|
|
}
|
|
else
|
|
{
|
|
if (!AllocBuffer(nLength))
|
|
{
|
|
Init();
|
|
return;
|
|
}
|
|
#ifdef UNICODE
|
|
for (int i = 0; i < nLength; i++)
|
|
m_pchData[i] = ch;
|
|
#else
|
|
memset(m_pchData, ch, nLength);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
CStr::CStr(LPCTSTR lpch, int nLength)
|
|
{
|
|
if (nLength == 0)
|
|
Init();
|
|
else
|
|
{
|
|
dspAssert(IsValidAddressz(lpch, nLength, FALSE));
|
|
if (!AllocBuffer(nLength))
|
|
{
|
|
Init();
|
|
return;
|
|
}
|
|
memcpy(m_pchData, lpch, nLength*sizeof(TCHAR));
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// Assignment operators
|
|
|
|
const CStr& CStr::operator=(TCHAR ch)
|
|
{
|
|
#ifndef UNICODE
|
|
dspAssert(!IsDBCSLeadByte(ch)); // can't set single lead byte
|
|
#endif
|
|
AssignCopy(1, &ch);
|
|
return *this;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// less common string expressions
|
|
|
|
CStr STRAPI operator+(const CStr& string1, TCHAR ch)
|
|
{
|
|
CStr s;
|
|
s.ConcatCopy(string1.m_nDataLength, string1.m_pchData, 1, &ch);
|
|
return s;
|
|
}
|
|
|
|
CStr STRAPI operator+(TCHAR ch, const CStr& string)
|
|
{
|
|
CStr s;
|
|
s.ConcatCopy(1, &ch, string.m_nDataLength, string.m_pchData);
|
|
return s;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// Very simple sub-string extraction
|
|
|
|
CStr CStr::Mid(int nFirst) const
|
|
{
|
|
return Mid(nFirst, m_nDataLength - nFirst);
|
|
}
|
|
|
|
CStr CStr::Mid(int nFirst, int nCount) const
|
|
{
|
|
dspAssert(nFirst >= 0);
|
|
dspAssert(nCount >= 0);
|
|
|
|
// out-of-bounds requests return sensible things
|
|
if (nFirst + nCount > m_nDataLength)
|
|
nCount = m_nDataLength - nFirst;
|
|
if (nFirst > m_nDataLength)
|
|
nCount = 0;
|
|
|
|
CStr dest;
|
|
AllocCopy(dest, nCount, nFirst, 0);
|
|
return dest;
|
|
}
|
|
|
|
CStr CStr::Right(int nCount) const
|
|
{
|
|
dspAssert(nCount >= 0);
|
|
|
|
if (nCount > m_nDataLength)
|
|
nCount = m_nDataLength;
|
|
|
|
CStr dest;
|
|
AllocCopy(dest, nCount, m_nDataLength-nCount, 0);
|
|
return dest;
|
|
}
|
|
|
|
CStr CStr::Left(int nCount) const
|
|
{
|
|
dspAssert(nCount >= 0);
|
|
|
|
if (nCount > m_nDataLength)
|
|
nCount = m_nDataLength;
|
|
|
|
CStr dest;
|
|
AllocCopy(dest, nCount, 0, 0);
|
|
return dest;
|
|
}
|
|
|
|
// strspn equivalent
|
|
CStr CStr::SpanIncluding(LPCTSTR lpszCharSet) const
|
|
{
|
|
dspAssert(IsValidString(lpszCharSet, FALSE));
|
|
return Left(static_cast<int>(_tcsspn(m_pchData, lpszCharSet)));
|
|
}
|
|
|
|
// strcspn equivalent
|
|
CStr CStr::SpanExcluding(LPCTSTR lpszCharSet) const
|
|
{
|
|
dspAssert(IsValidString(lpszCharSet, FALSE));
|
|
return Left(static_cast<int>(_tcscspn(m_pchData, lpszCharSet)));
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// Finding
|
|
|
|
int CStr::ReverseFind(TCHAR ch) const
|
|
{
|
|
// find last single character
|
|
LPTSTR lpsz = _tcsrchr(m_pchData, ch);
|
|
|
|
// return -1 if not found, distance from beginning otherwise
|
|
return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData);
|
|
}
|
|
|
|
// find a sub-string (like strstr)
|
|
int CStr::Find(LPCTSTR lpszSub) const
|
|
{
|
|
dspAssert(IsValidString(lpszSub, FALSE));
|
|
|
|
// find first matching substring
|
|
LPTSTR lpsz = _tcsstr(m_pchData, lpszSub);
|
|
|
|
// return -1 for not found, distance from beginning otherwise
|
|
return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// String formatting
|
|
|
|
#define FORCE_ANSI 0x10000
|
|
#define FORCE_UNICODE 0x20000
|
|
|
|
// formatting (using wsprintf style formatting)
|
|
void CStr::Format(LPCTSTR lpszFormat, ...)
|
|
{
|
|
dspAssert(IsValidString(lpszFormat, FALSE));
|
|
|
|
va_list argList;
|
|
va_start(argList, lpszFormat);
|
|
|
|
// make a guess at the maximum length of the resulting string
|
|
size_t nMaxLen = 0;
|
|
for (LPCTSTR lpsz = lpszFormat; *lpsz != '\0'; lpsz = _tcsinc(lpsz))
|
|
{
|
|
// handle '%' character, but watch out for '%%'
|
|
if (*lpsz != '%' || *(lpsz = _tcsinc(lpsz)) == '%')
|
|
{
|
|
nMaxLen += _tclen(lpsz);
|
|
continue;
|
|
}
|
|
|
|
size_t nItemLen = 0;
|
|
|
|
// handle '%' character with format
|
|
int nWidth = 0;
|
|
for (; *lpsz != '\0'; lpsz = _tcsinc(lpsz))
|
|
{
|
|
// check for valid flags
|
|
if (*lpsz == '#')
|
|
nMaxLen += 2; // for '0x'
|
|
else if (*lpsz == '*')
|
|
nWidth = va_arg(argList, int);
|
|
else if (*lpsz == '-' || *lpsz == '+' || *lpsz == '0' ||
|
|
*lpsz == ' ')
|
|
;
|
|
else // hit non-flag character
|
|
break;
|
|
}
|
|
// get width and skip it
|
|
if (nWidth == 0)
|
|
{
|
|
// width indicated by
|
|
nWidth = _ttoi(lpsz);
|
|
for (; *lpsz != '\0' && _istdigit(*lpsz); lpsz = _tcsinc(lpsz))
|
|
;
|
|
}
|
|
dspAssert(nWidth >= 0);
|
|
|
|
int nPrecision = 0;
|
|
if (*lpsz == '.')
|
|
{
|
|
// skip past '.' separator (width.precision)
|
|
lpsz = _tcsinc(lpsz);
|
|
|
|
// get precision and skip it
|
|
if (*lpsz == '*')
|
|
{
|
|
nPrecision = va_arg(argList, int);
|
|
lpsz = _tcsinc(lpsz);
|
|
}
|
|
else
|
|
{
|
|
nPrecision = _ttoi(lpsz);
|
|
for (; *lpsz != '\0' && _istdigit(*lpsz); lpsz = _tcsinc(lpsz))
|
|
;
|
|
}
|
|
dspAssert(nPrecision >= 0);
|
|
}
|
|
|
|
// should be on type modifier or specifier
|
|
int nModifier = 0;
|
|
switch (*lpsz)
|
|
{
|
|
// modifiers that affect size
|
|
case 'h':
|
|
nModifier = FORCE_ANSI;
|
|
lpsz = _tcsinc(lpsz);
|
|
break;
|
|
case 'l':
|
|
nModifier = FORCE_UNICODE;
|
|
lpsz = _tcsinc(lpsz);
|
|
break;
|
|
|
|
// modifiers that do not affect size
|
|
case 'F':
|
|
case 'N':
|
|
case 'L':
|
|
lpsz = _tcsinc(lpsz);
|
|
break;
|
|
}
|
|
|
|
// now should be on specifier
|
|
switch (*lpsz | nModifier)
|
|
{
|
|
// single characters
|
|
case 'c':
|
|
case 'C':
|
|
nItemLen = 2;
|
|
va_arg(argList, TCHAR);
|
|
break;
|
|
case 'c'|FORCE_ANSI:
|
|
case 'C'|FORCE_ANSI:
|
|
nItemLen = 2;
|
|
va_arg(argList, char);
|
|
break;
|
|
case 'c'|FORCE_UNICODE:
|
|
case 'C'|FORCE_UNICODE:
|
|
nItemLen = 2;
|
|
va_arg(argList, WCHAR);
|
|
break;
|
|
|
|
// strings
|
|
case 's':
|
|
case 'S':
|
|
nItemLen = lstrlen(va_arg(argList, LPCTSTR));
|
|
nItemLen = __max(1, nItemLen);
|
|
break;
|
|
case 's'|FORCE_ANSI:
|
|
case 'S'|FORCE_ANSI:
|
|
nItemLen = lstrlenA(va_arg(argList, LPCSTR));
|
|
nItemLen = __max(1, nItemLen);
|
|
break;
|
|
#ifndef _MAC
|
|
case 's'|FORCE_UNICODE:
|
|
case 'S'|FORCE_UNICODE:
|
|
nItemLen = wcslen(va_arg(argList, LPWSTR));
|
|
nItemLen = __max(1, nItemLen);
|
|
break;
|
|
#endif
|
|
}
|
|
|
|
// adjust nItemLen for strings
|
|
if (nItemLen != 0)
|
|
{
|
|
nItemLen = __max(nItemLen, static_cast<UINT>(nWidth));
|
|
if (nPrecision != 0)
|
|
nItemLen = __min(nItemLen, static_cast<UINT>(nPrecision));
|
|
}
|
|
else
|
|
{
|
|
switch (*lpsz)
|
|
{
|
|
// integers
|
|
case 'd':
|
|
case 'i':
|
|
case 'u':
|
|
case 'x':
|
|
case 'X':
|
|
case 'o':
|
|
va_arg(argList, int);
|
|
nItemLen = 32;
|
|
nItemLen = __max(nItemLen, static_cast<UINT>(nWidth+nPrecision));
|
|
break;
|
|
|
|
case 'e':
|
|
case 'f':
|
|
case 'g':
|
|
case 'G':
|
|
va_arg(argList, _STR_DOUBLE);
|
|
nItemLen = 128;
|
|
nItemLen = __max(nItemLen, static_cast<UINT>(nWidth+nPrecision));
|
|
break;
|
|
|
|
case 'p':
|
|
va_arg(argList, void*);
|
|
nItemLen = 32;
|
|
nItemLen = __max(nItemLen, static_cast<UINT>(nWidth+nPrecision));
|
|
break;
|
|
|
|
// no output
|
|
case 'n':
|
|
va_arg(argList, int*);
|
|
break;
|
|
|
|
default:
|
|
dspAssert(FALSE); // unknown formatting option
|
|
}
|
|
}
|
|
|
|
// adjust nMaxLen for output nItemLen
|
|
nMaxLen += nItemLen;
|
|
}
|
|
va_end(argList);
|
|
|
|
// finally, set the buffer length and format the string
|
|
va_start(argList, lpszFormat); // restart the arg list
|
|
GetBuffer(static_cast<int>(nMaxLen));
|
|
if (_vstprintf(m_pchData, lpszFormat, argList) > static_cast<int>(nMaxLen))
|
|
{
|
|
dspAssert(FALSE);
|
|
}
|
|
ReleaseBuffer();
|
|
va_end(argList);
|
|
}
|
|
|
|
void CStr::TrimRight()
|
|
{
|
|
// find beginning of trailing spaces by starting at beginning (DBCS aware)
|
|
LPTSTR lpsz = m_pchData;
|
|
LPTSTR lpszLast = NULL;
|
|
while (*lpsz != '\0')
|
|
{
|
|
if (_istspace(*lpsz))
|
|
{
|
|
if (lpszLast == NULL)
|
|
lpszLast = lpsz;
|
|
}
|
|
else
|
|
lpszLast = NULL;
|
|
lpsz = _tcsinc(lpsz);
|
|
}
|
|
|
|
if (lpszLast != NULL)
|
|
{
|
|
// truncate at trailing space start
|
|
*lpszLast = '\0';
|
|
m_nDataLength = (int)(lpszLast - m_pchData);
|
|
}
|
|
}
|
|
|
|
void CStr::TrimLeft()
|
|
{
|
|
// find first non-space character
|
|
LPCTSTR lpsz = m_pchData;
|
|
while (_istspace(*lpsz))
|
|
lpsz = _tcsinc(lpsz);
|
|
|
|
// fix up data and length
|
|
int nDataLength = (int)(m_nDataLength - (lpsz - m_pchData));
|
|
memmove(m_pchData, lpsz, (nDataLength+1)*sizeof(TCHAR));
|
|
m_nDataLength = nDataLength;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// String support for template collections
|
|
|
|
void STRAPI ConstructElements(CStr* pElements, int nCount)
|
|
{
|
|
dspAssert(IsValidAddressz(pElements, nCount * sizeof(CStr)));
|
|
|
|
for (; nCount--; ++pElements)
|
|
memcpy(pElements, &strEmptyStringT, sizeof(*pElements));
|
|
}
|
|
|
|
void STRAPI DestructElements(CStr* pElements, int nCount)
|
|
{
|
|
dspAssert(IsValidAddressz(pElements, nCount * sizeof(CStr)));
|
|
|
|
for (; nCount--; ++pElements)
|
|
pElements->Empty();
|
|
}
|
|
|
|
//
|
|
// Added by JonN 4/16/98
|
|
//
|
|
void FreeCStrList( IN OUT CStrListItem** ppList )
|
|
{
|
|
dspAssert( NULL != ppList );
|
|
while (NULL != *ppList)
|
|
{
|
|
CStrListItem* pTemp = (*ppList)->pnext;
|
|
delete *ppList;
|
|
*ppList = pTemp;
|
|
}
|
|
}
|
|
void CStrListAdd( IN OUT CStrListItem** ppList, IN LPCTSTR lpsz )
|
|
{
|
|
dspAssert( NULL != ppList );
|
|
CStrListItem* pnewitem = new CStrListItem;
|
|
if (pnewitem != NULL)
|
|
{
|
|
pnewitem->str = lpsz;
|
|
pnewitem->pnext = *ppList;
|
|
*ppList = pnewitem;
|
|
}
|
|
}
|
|
bool CStrListContains( IN CStrListItem** ppList, IN LPCTSTR lpsz )
|
|
{
|
|
dspAssert( NULL != ppList );
|
|
for (CStrListItem* pList = *ppList; NULL != pList; pList = pList->pnext)
|
|
{
|
|
if ( !_tcsicmp( lpsz, pList->str ) )
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
int CountCStrList( IN CStrListItem** ppList )
|
|
{
|
|
dspAssert( NULL != ppList );
|
|
int cCount = 0;
|
|
for (CStrListItem* pList = *ppList; NULL != pList; pList = pList->pnext)
|
|
{
|
|
cCount++;
|
|
}
|
|
return cCount;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// static class data, special inlines
|
|
|
|
// For an empty string, m_???Data will point here
|
|
// (note: avoids a lot of NULL pointer tests when we call standard
|
|
// C runtime libraries
|
|
WCHAR strChNilW = '\0';
|
|
|
|
// for creating empty key strings
|
|
const CStrW strEmptyStringW;
|
|
|
|
void CStrW::Init()
|
|
{
|
|
m_nDataLength = m_nAllocLength = 0;
|
|
m_pchData = (PWSTR)&strChNilW;
|
|
}
|
|
|
|
// declared static
|
|
void CStrW::SafeDelete(PWSTR& lpch)
|
|
{
|
|
if (lpch != (PWSTR)&strChNilW &&
|
|
lpch)
|
|
{
|
|
delete[] lpch;
|
|
lpch = 0;
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// Construction/Destruction
|
|
|
|
CStrW::CStrW()
|
|
{
|
|
Init();
|
|
}
|
|
|
|
CStrW::CStrW(const CStrW& stringSrc)
|
|
{
|
|
// if constructing a String from another String, we make a copy of the
|
|
// original string data to enforce value semantics (i.e. each string
|
|
// gets a copy of its own
|
|
|
|
stringSrc.AllocCopy(*this, stringSrc.m_nDataLength, 0, 0);
|
|
}
|
|
|
|
BOOL CStrW::AllocBuffer(int nLen)
|
|
// always allocate one extra character for '\0' termination
|
|
// assumes [optimistically] that data length will equal allocation length
|
|
{
|
|
dspAssert(nLen >= 0);
|
|
|
|
if (nLen == 0)
|
|
{
|
|
Empty();
|
|
}
|
|
else
|
|
{
|
|
m_pchData = new WCHAR[nLen+1]; //REVIEW may throw an exception
|
|
if (!m_pchData)
|
|
{
|
|
Empty();
|
|
return FALSE;
|
|
}
|
|
m_pchData[nLen] = '\0';
|
|
m_nDataLength = nLen;
|
|
m_nAllocLength = nLen;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
void CStrW::Empty()
|
|
{
|
|
SafeDelete(m_pchData);
|
|
Init();
|
|
dspAssert(m_nDataLength == 0);
|
|
dspAssert(m_nAllocLength == 0);
|
|
}
|
|
|
|
CStrW::~CStrW()
|
|
// free any attached data
|
|
{
|
|
SafeDelete(m_pchData);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// Helpers for the rest of the implementation
|
|
|
|
static inline int SafeStrlen(LPCWSTR lpsz)
|
|
{
|
|
dspAssert(lpsz == NULL || IsValidString(lpsz, FALSE));
|
|
return (int)((lpsz == NULL) ? 0 : wcslen(lpsz));
|
|
}
|
|
|
|
void CStrW::AllocCopy(CStrW& dest, int nCopyLen, int nCopyIndex,
|
|
int nExtraLen) const
|
|
{
|
|
// will clone the data attached to this string
|
|
// allocating 'nExtraLen' characters
|
|
// Places results in uninitialized string 'dest'
|
|
// Will copy the part or all of original data to start of new string
|
|
|
|
int nNewLen = nCopyLen + nExtraLen;
|
|
|
|
if (nNewLen == 0)
|
|
{
|
|
dest.Empty();
|
|
}
|
|
else
|
|
{
|
|
if (!dest.AllocBuffer(nNewLen)) return;
|
|
memcpy(dest.m_pchData, &m_pchData[nCopyIndex], nCopyLen*sizeof(WCHAR));
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// More sophisticated construction
|
|
|
|
CStrW::CStrW(LPCWSTR lpsz)
|
|
{
|
|
int nLen;
|
|
if ((nLen = SafeStrlen(lpsz)) == 0)
|
|
Init();
|
|
else
|
|
{
|
|
if (!AllocBuffer(nLen))
|
|
{
|
|
Init();
|
|
return;
|
|
}
|
|
memcpy(m_pchData, lpsz, nLen*sizeof(WCHAR));
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Special conversion constructors
|
|
|
|
CStrW::CStrW(LPCSTR lpsz)
|
|
{
|
|
int nSrcLen = lpsz != NULL ? lstrlenA(lpsz) : 0;
|
|
if (nSrcLen == 0)
|
|
Init();
|
|
else
|
|
{
|
|
if (!AllocBuffer(nSrcLen))
|
|
{
|
|
Init();
|
|
return;
|
|
}
|
|
mmc_mbstowcsz(m_pchData, lpsz, nSrcLen+1);
|
|
}
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// Assignment operators
|
|
// All assign a new value to the string
|
|
// (a) first see if the buffer is big enough
|
|
// (b) if enough room, copy on top of old buffer, set size and type
|
|
// (c) otherwise free old string data, and create a new one
|
|
//
|
|
// All routines return the new string (but as a 'const CStrW&' so that
|
|
// assigning it again will cause a copy, eg: s1 = s2 = "hi there".
|
|
//
|
|
|
|
void CStrW::AssignCopy(int nSrcLen, LPCWSTR lpszSrcData)
|
|
{
|
|
// check if it will fit
|
|
if (nSrcLen > m_nAllocLength)
|
|
{
|
|
// it won't fit, allocate another one
|
|
Empty();
|
|
if (!AllocBuffer(nSrcLen)) return;
|
|
}
|
|
if (nSrcLen != 0)
|
|
memcpy(m_pchData, lpszSrcData, nSrcLen*sizeof(WCHAR));
|
|
m_nDataLength = nSrcLen;
|
|
m_pchData[nSrcLen] = '\0';
|
|
}
|
|
|
|
const CStrW& CStrW::operator=(const CStrW& stringSrc)
|
|
{
|
|
AssignCopy(stringSrc.m_nDataLength, stringSrc.m_pchData);
|
|
return *this;
|
|
}
|
|
|
|
const CStrW& CStrW::operator=(LPCWSTR lpsz)
|
|
{
|
|
dspAssert(lpsz == NULL || IsValidString(lpsz, FALSE));
|
|
AssignCopy(SafeStrlen(lpsz), lpsz);
|
|
return *this;
|
|
}
|
|
|
|
const CStrW& CStrW::operator=(UNICODE_STRING unistr)
|
|
{
|
|
AssignCopy(unistr.Length/2, unistr.Buffer);
|
|
return *this;
|
|
}
|
|
|
|
const CStrW& CStrW::operator=(UNICODE_STRING * punistr)
|
|
{
|
|
AssignCopy(punistr->Length/2, punistr->Buffer);
|
|
return *this;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Special conversion assignment
|
|
|
|
const CStrW& CStrW::operator=(LPCSTR lpsz)
|
|
{
|
|
int nSrcLen = lpsz != NULL ? lstrlenA(lpsz) : 0;
|
|
// check if it will fit
|
|
if (nSrcLen > m_nAllocLength)
|
|
{
|
|
// it won't fit, allocate another one
|
|
Empty();
|
|
if (!AllocBuffer(nSrcLen)) return *this;
|
|
}
|
|
if (nSrcLen != 0)
|
|
mmc_mbstowcsz(m_pchData, lpsz, nSrcLen+1);
|
|
m_nDataLength = nSrcLen;
|
|
m_pchData[nSrcLen] = '\0';
|
|
return *this;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// concatenation
|
|
|
|
// NOTE: "operator+" is done as friend functions for simplicity
|
|
// There are three variants:
|
|
// String + String
|
|
// and for ? = WCHAR, LPCWSTR
|
|
// String + ?
|
|
// ? + String
|
|
|
|
void CStrW::ConcatCopy(int nSrc1Len, LPCWSTR lpszSrc1Data,
|
|
int nSrc2Len, LPCWSTR lpszSrc2Data)
|
|
{
|
|
// -- master concatenation routine
|
|
// Concatenate two sources
|
|
// -- assume that 'this' is a new String object
|
|
|
|
int nNewLen = nSrc1Len + nSrc2Len;
|
|
if (!AllocBuffer(nNewLen)) return;
|
|
memcpy(m_pchData, lpszSrc1Data, nSrc1Len*sizeof(WCHAR));
|
|
memcpy(&m_pchData[nSrc1Len], lpszSrc2Data, nSrc2Len*sizeof(WCHAR));
|
|
}
|
|
|
|
CStrW STRAPI operator+(const CStrW& string1, const CStrW& string2)
|
|
{
|
|
CStrW s;
|
|
s.ConcatCopy(string1.m_nDataLength, string1.m_pchData,
|
|
string2.m_nDataLength, string2.m_pchData);
|
|
return s;
|
|
}
|
|
|
|
CStrW STRAPI operator+(const CStrW& string, LPCWSTR lpsz)
|
|
{
|
|
dspAssert(lpsz == NULL || IsValidString(lpsz, FALSE));
|
|
CStrW s;
|
|
s.ConcatCopy(string.m_nDataLength, string.m_pchData, SafeStrlen(lpsz), lpsz);
|
|
return s;
|
|
}
|
|
|
|
CStrW STRAPI operator+(LPCWSTR lpsz, const CStrW& string)
|
|
{
|
|
dspAssert(lpsz == NULL || IsValidString(lpsz, FALSE));
|
|
CStrW s;
|
|
s.ConcatCopy(SafeStrlen(lpsz), lpsz, string.m_nDataLength, string.m_pchData);
|
|
return s;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// concatenate in place
|
|
|
|
void CStrW::ConcatInPlace(int nSrcLen, LPCWSTR lpszSrcData)
|
|
{
|
|
// -- the main routine for += operators
|
|
|
|
// if the buffer is too small, or we have a width mis-match, just
|
|
// allocate a new buffer (slow but sure)
|
|
if (m_nDataLength + nSrcLen > m_nAllocLength)
|
|
{
|
|
// we have to grow the buffer, use the Concat in place routine
|
|
PWSTR lpszOldData = m_pchData;
|
|
ConcatCopy(m_nDataLength, lpszOldData, nSrcLen, lpszSrcData);
|
|
dspAssert(lpszOldData != NULL);
|
|
SafeDelete(lpszOldData);
|
|
}
|
|
else
|
|
{
|
|
// fast concatenation when buffer big enough
|
|
memcpy(&m_pchData[m_nDataLength], lpszSrcData, nSrcLen*sizeof(WCHAR));
|
|
m_nDataLength += nSrcLen;
|
|
}
|
|
dspAssert(m_nDataLength <= m_nAllocLength);
|
|
m_pchData[m_nDataLength] = '\0';
|
|
}
|
|
|
|
const CStrW& CStrW::operator+=(LPCWSTR lpsz)
|
|
{
|
|
dspAssert(lpsz == NULL || IsValidString(lpsz, FALSE));
|
|
ConcatInPlace(SafeStrlen(lpsz), lpsz);
|
|
return *this;
|
|
}
|
|
|
|
const CStrW& CStrW::operator+=(WCHAR ch)
|
|
{
|
|
ConcatInPlace(1, &ch);
|
|
return *this;
|
|
}
|
|
|
|
const CStrW& CStrW::operator+=(const CStrW& string)
|
|
{
|
|
ConcatInPlace(string.m_nDataLength, string.m_pchData);
|
|
return *this;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Advanced direct buffer access
|
|
|
|
PWSTR CStrW::GetBuffer(int nMinBufLength)
|
|
{
|
|
dspAssert(nMinBufLength >= 0);
|
|
|
|
if (nMinBufLength > m_nAllocLength)
|
|
{
|
|
// we have to grow the buffer
|
|
PWSTR lpszOldData = m_pchData;
|
|
int nOldLen = m_nDataLength; // AllocBuffer will tromp it
|
|
|
|
if (!AllocBuffer(nMinBufLength)) return NULL;
|
|
memcpy(m_pchData, lpszOldData, nOldLen*sizeof(WCHAR));
|
|
m_nDataLength = nOldLen;
|
|
m_pchData[m_nDataLength] = '\0';
|
|
|
|
SafeDelete(lpszOldData);
|
|
}
|
|
|
|
// return a pointer to the character storage for this string
|
|
dspAssert(m_pchData != NULL);
|
|
return m_pchData;
|
|
}
|
|
|
|
void CStrW::ReleaseBuffer(int nNewLength)
|
|
{
|
|
if (nNewLength == -1)
|
|
nNewLength = static_cast<int>(wcslen(m_pchData)); // zero terminated
|
|
|
|
dspAssert(nNewLength <= m_nAllocLength);
|
|
m_nDataLength = nNewLength;
|
|
m_pchData[m_nDataLength] = '\0';
|
|
}
|
|
|
|
PWSTR CStrW::GetBufferSetLength(int nNewLength)
|
|
{
|
|
dspAssert(nNewLength >= 0);
|
|
|
|
GetBuffer(nNewLength);
|
|
m_nDataLength = nNewLength;
|
|
m_pchData[m_nDataLength] = '\0';
|
|
return m_pchData;
|
|
}
|
|
|
|
void CStrW::FreeExtra()
|
|
{
|
|
dspAssert(m_nDataLength <= m_nAllocLength);
|
|
if (m_nDataLength != m_nAllocLength)
|
|
{
|
|
PWSTR lpszOldData = m_pchData;
|
|
if (!AllocBuffer(m_nDataLength)) return;
|
|
memcpy(m_pchData, lpszOldData, m_nDataLength*sizeof(WCHAR));
|
|
dspAssert(m_pchData[m_nDataLength] == '\0');
|
|
SafeDelete(lpszOldData);
|
|
}
|
|
dspAssert(m_pchData != NULL);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Commonly used routines (rarely used routines in STREX.CPP)
|
|
|
|
int CStrW::Find(WCHAR ch) const
|
|
{
|
|
// find first single character
|
|
PWSTR lpsz = wcschr(m_pchData, ch);
|
|
|
|
// return -1 if not found and index otherwise
|
|
return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData);
|
|
}
|
|
|
|
int CStrW::FindOneOf(LPCWSTR lpszCharSet) const
|
|
{
|
|
dspAssert(IsValidString(lpszCharSet, FALSE));
|
|
PWSTR lpsz = wcspbrk(m_pchData, lpszCharSet);
|
|
return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData);
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Windows extensions to strings
|
|
|
|
BOOL CStrW::LoadString(HINSTANCE hInst, UINT nID)
|
|
{
|
|
dspAssert(nID != 0); // 0 is an illegal string ID
|
|
|
|
// Note: resource strings limited to 511 characters
|
|
WCHAR szBuffer[512];
|
|
UINT nSize = StrLoadStringW(hInst, nID, szBuffer);
|
|
AssignCopy(nSize, szBuffer);
|
|
return nSize > 0;
|
|
}
|
|
|
|
int STRAPI StrLoadStringW(HINSTANCE hInst, UINT nID, LPWSTR lpszBuf)
|
|
{
|
|
dspAssert(IsValidAddressz(lpszBuf, 512)); // must be big enough for 512 bytes
|
|
#ifdef DBG
|
|
// LoadString without annoying warning from the Debug kernel if the
|
|
// segment containing the string is not present
|
|
if (::FindResource(hInst, MAKEINTRESOURCE((nID>>4)+1), RT_STRING) == NULL)
|
|
{
|
|
lpszBuf[0] = '\0';
|
|
return 0; // not found
|
|
}
|
|
#endif //DBG
|
|
int nLen = ::LoadStringW(hInst, nID, lpszBuf, 511);
|
|
dspAssert(nLen);
|
|
if (nLen == 0)
|
|
lpszBuf[0] = '\0';
|
|
return nLen;
|
|
}
|
|
|
|
|
|
#ifdef OLE_AUTOMATION
|
|
#ifdef UNICODE
|
|
BSTR CStrW::AllocSysString()
|
|
{
|
|
BSTR bstr = ::SysAllocStringLen(m_pchData, m_nDataLength);
|
|
if (bstr == NULL)
|
|
;//REVIEW AfxThrowMemoryException();
|
|
|
|
return bstr;
|
|
}
|
|
|
|
BSTR CStrW::SetSysString(BSTR* pbstr)
|
|
{
|
|
dspAssert(IsValidAddressz(pbstr, sizeof(BSTR)));
|
|
|
|
if (!::SysReAllocStringLen(pbstr, m_pchData, m_nDataLength))
|
|
; //REVIEW AfxThrowMemoryException();
|
|
|
|
dspAssert(*pbstr != NULL);
|
|
return *pbstr;
|
|
}
|
|
#endif
|
|
#endif // #ifdef OLE_AUTOMATION
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Orginally from StrEx.cpp
|
|
|
|
|
|
CStrW::CStrW(WCHAR ch, int nLength)
|
|
{
|
|
if (nLength < 1)
|
|
{
|
|
// return empty string if invalid repeat count
|
|
Init();
|
|
}
|
|
else
|
|
{
|
|
if (!AllocBuffer(nLength))
|
|
{
|
|
Init();
|
|
return;
|
|
}
|
|
#ifdef UNICODE
|
|
for (int i = 0; i < nLength; i++)
|
|
m_pchData[i] = ch;
|
|
#else
|
|
memset(m_pchData, ch, nLength);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
CStrW::CStrW(LPCWSTR lpch, int nLength)
|
|
{
|
|
if (nLength == 0)
|
|
Init();
|
|
else
|
|
{
|
|
dspAssert(IsValidAddressz(lpch, nLength, FALSE));
|
|
if (!AllocBuffer(nLength))
|
|
{
|
|
Init();
|
|
return;
|
|
}
|
|
memcpy(m_pchData, lpch, nLength*sizeof(WCHAR));
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// Assignment operators
|
|
|
|
const CStrW& CStrW::operator=(WCHAR ch)
|
|
{
|
|
AssignCopy(1, &ch);
|
|
return *this;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// less common string expressions
|
|
|
|
CStrW STRAPI operator+(const CStrW& string1, WCHAR ch)
|
|
{
|
|
CStrW s;
|
|
s.ConcatCopy(string1.m_nDataLength, string1.m_pchData, 1, &ch);
|
|
return s;
|
|
}
|
|
|
|
CStrW STRAPI operator+(WCHAR ch, const CStrW& string)
|
|
{
|
|
CStrW s;
|
|
s.ConcatCopy(1, &ch, string.m_nDataLength, string.m_pchData);
|
|
return s;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// Very simple sub-string extraction
|
|
|
|
CStrW CStrW::Mid(int nFirst) const
|
|
{
|
|
return Mid(nFirst, m_nDataLength - nFirst);
|
|
}
|
|
|
|
CStrW CStrW::Mid(int nFirst, int nCount) const
|
|
{
|
|
dspAssert(nFirst >= 0);
|
|
dspAssert(nCount >= 0);
|
|
|
|
// out-of-bounds requests return sensible things
|
|
if (nFirst + nCount > m_nDataLength)
|
|
nCount = m_nDataLength - nFirst;
|
|
if (nFirst > m_nDataLength)
|
|
nCount = 0;
|
|
|
|
CStrW dest;
|
|
AllocCopy(dest, nCount, nFirst, 0);
|
|
return dest;
|
|
}
|
|
|
|
CStrW CStrW::Right(int nCount) const
|
|
{
|
|
dspAssert(nCount >= 0);
|
|
|
|
if (nCount > m_nDataLength)
|
|
nCount = m_nDataLength;
|
|
|
|
CStrW dest;
|
|
AllocCopy(dest, nCount, m_nDataLength-nCount, 0);
|
|
return dest;
|
|
}
|
|
|
|
CStrW CStrW::Left(int nCount) const
|
|
{
|
|
dspAssert(nCount >= 0);
|
|
|
|
if (nCount > m_nDataLength)
|
|
nCount = m_nDataLength;
|
|
|
|
CStrW dest;
|
|
AllocCopy(dest, nCount, 0, 0);
|
|
return dest;
|
|
}
|
|
|
|
// strspn equivalent
|
|
CStrW CStrW::SpanIncluding(LPCWSTR lpszCharSet) const
|
|
{
|
|
dspAssert(IsValidString(lpszCharSet, FALSE));
|
|
return Left(static_cast<int>(wcsspn(m_pchData, lpszCharSet)));
|
|
}
|
|
|
|
// strcspn equivalent
|
|
CStrW CStrW::SpanExcluding(LPCWSTR lpszCharSet) const
|
|
{
|
|
dspAssert(IsValidString(lpszCharSet, FALSE));
|
|
return Left(static_cast<int>(wcscspn(m_pchData, lpszCharSet)));
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// Finding
|
|
|
|
int CStrW::ReverseFind(WCHAR ch) const
|
|
{
|
|
// find last single character
|
|
PWSTR lpsz = wcsrchr(m_pchData, ch);
|
|
|
|
// return -1 if not found, distance from beginning otherwise
|
|
return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData);
|
|
}
|
|
|
|
// find a sub-string (like strstr)
|
|
int CStrW::Find(LPCWSTR lpszSub) const
|
|
{
|
|
dspAssert(IsValidString(lpszSub, FALSE));
|
|
|
|
// find first matching substring
|
|
PWSTR lpsz = wcsstr(m_pchData, lpszSub);
|
|
|
|
// return -1 for not found, distance from beginning otherwise
|
|
return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// String formatting
|
|
|
|
#define FORCE_ANSI 0x10000
|
|
#define FORCE_UNICODE 0x20000
|
|
|
|
// formatting (using wsprintf style formatting)
|
|
void CStrW::Format(LPCWSTR lpszFormat, ...)
|
|
{
|
|
dspAssert(IsValidString(lpszFormat, FALSE));
|
|
|
|
va_list argList;
|
|
va_start(argList, lpszFormat);
|
|
|
|
// make a guess at the maximum length of the resulting string
|
|
size_t nMaxLen = 0;
|
|
for (LPCWSTR lpsz = lpszFormat; *lpsz != '\0'; lpsz = _wcsinc(lpsz))
|
|
{
|
|
// handle '%' character, but watch out for '%%'
|
|
if (*lpsz != '%' || *(lpsz = _wcsinc(lpsz)) == '%')
|
|
{
|
|
nMaxLen += wcslen(lpsz);
|
|
continue;
|
|
}
|
|
|
|
size_t nItemLen = 0;
|
|
|
|
// handle '%' character with format
|
|
int nWidth = 0;
|
|
for (; *lpsz != '\0'; lpsz = _wcsinc(lpsz))
|
|
{
|
|
// check for valid flags
|
|
if (*lpsz == '#')
|
|
nMaxLen += 2; // for '0x'
|
|
else if (*lpsz == '*')
|
|
nWidth = va_arg(argList, int);
|
|
else if (*lpsz == '-' || *lpsz == '+' || *lpsz == '0' ||
|
|
*lpsz == ' ')
|
|
;
|
|
else // hit non-flag character
|
|
break;
|
|
}
|
|
// get width and skip it
|
|
if (nWidth == 0)
|
|
{
|
|
// width indicated by
|
|
nWidth = _wtoi(lpsz);
|
|
for (; *lpsz != '\0' && _istdigit(*lpsz); lpsz = _wcsinc(lpsz))
|
|
;
|
|
}
|
|
dspAssert(nWidth >= 0);
|
|
|
|
int nPrecision = 0;
|
|
if (*lpsz == '.')
|
|
{
|
|
// skip past '.' separator (width.precision)
|
|
lpsz = _wcsinc(lpsz);
|
|
|
|
// get precision and skip it
|
|
if (*lpsz == '*')
|
|
{
|
|
nPrecision = va_arg(argList, int);
|
|
lpsz = _wcsinc(lpsz);
|
|
}
|
|
else
|
|
{
|
|
nPrecision = _wtoi(lpsz);
|
|
for (; *lpsz != '\0' && _istdigit(*lpsz); lpsz = _wcsinc(lpsz))
|
|
;
|
|
}
|
|
dspAssert(nPrecision >= 0);
|
|
}
|
|
|
|
// should be on type modifier or specifier
|
|
int nModifier = 0;
|
|
switch (*lpsz)
|
|
{
|
|
// modifiers that affect size
|
|
case 'h':
|
|
nModifier = FORCE_ANSI;
|
|
lpsz = _wcsinc(lpsz);
|
|
break;
|
|
case 'l':
|
|
nModifier = FORCE_UNICODE;
|
|
lpsz = _wcsinc(lpsz);
|
|
break;
|
|
|
|
// modifiers that do not affect size
|
|
case 'F':
|
|
case 'N':
|
|
case 'L':
|
|
lpsz = _wcsinc(lpsz);
|
|
break;
|
|
}
|
|
|
|
// now should be on specifier
|
|
switch (*lpsz | nModifier)
|
|
{
|
|
// single characters
|
|
case 'c':
|
|
case 'C':
|
|
nItemLen = 2;
|
|
va_arg(argList, WCHAR);
|
|
break;
|
|
case 'c'|FORCE_ANSI:
|
|
case 'C'|FORCE_ANSI:
|
|
nItemLen = 2;
|
|
va_arg(argList, char);
|
|
break;
|
|
case 'c'|FORCE_UNICODE:
|
|
case 'C'|FORCE_UNICODE:
|
|
nItemLen = 2;
|
|
va_arg(argList, WCHAR);
|
|
break;
|
|
|
|
// strings
|
|
case 's':
|
|
case 'S':
|
|
nItemLen = wcslen(va_arg(argList, LPCWSTR));
|
|
nItemLen = __max(1, nItemLen);
|
|
break;
|
|
case 's'|FORCE_ANSI:
|
|
case 'S'|FORCE_ANSI:
|
|
nItemLen = lstrlenA(va_arg(argList, LPCSTR));
|
|
nItemLen = __max(1, nItemLen);
|
|
break;
|
|
#ifndef _MAC
|
|
case 's'|FORCE_UNICODE:
|
|
case 'S'|FORCE_UNICODE:
|
|
nItemLen = wcslen(va_arg(argList, LPWSTR));
|
|
nItemLen = __max(1, nItemLen);
|
|
break;
|
|
#endif
|
|
}
|
|
|
|
// adjust nItemLen for strings
|
|
if (nItemLen != 0)
|
|
{
|
|
nItemLen = __max(nItemLen, static_cast<UINT>(nWidth));
|
|
if (nPrecision != 0)
|
|
nItemLen = __min(nItemLen, static_cast<UINT>(nPrecision));
|
|
}
|
|
else
|
|
{
|
|
switch (*lpsz)
|
|
{
|
|
// integers
|
|
case 'd':
|
|
case 'i':
|
|
case 'u':
|
|
case 'x':
|
|
case 'X':
|
|
case 'o':
|
|
va_arg(argList, int);
|
|
nItemLen = 32;
|
|
nItemLen = __max(nItemLen, static_cast<UINT>(nWidth+nPrecision));
|
|
break;
|
|
|
|
case 'e':
|
|
case 'f':
|
|
case 'g':
|
|
case 'G':
|
|
va_arg(argList, _STR_DOUBLE);
|
|
nItemLen = 128;
|
|
nItemLen = __max(nItemLen, static_cast<UINT>(nWidth+nPrecision));
|
|
break;
|
|
|
|
case 'p':
|
|
va_arg(argList, void*);
|
|
nItemLen = 32;
|
|
nItemLen = __max(nItemLen, static_cast<UINT>(nWidth+nPrecision));
|
|
break;
|
|
|
|
// no output
|
|
case 'n':
|
|
va_arg(argList, int*);
|
|
break;
|
|
|
|
default:
|
|
dspAssert(FALSE); // unknown formatting option
|
|
}
|
|
}
|
|
|
|
// adjust nMaxLen for output nItemLen
|
|
nMaxLen += nItemLen;
|
|
}
|
|
va_end(argList);
|
|
|
|
// finally, set the buffer length and format the string
|
|
va_start(argList, lpszFormat); // restart the arg list
|
|
GetBuffer(static_cast<int>(nMaxLen));
|
|
if (vswprintf(m_pchData, lpszFormat, argList) > static_cast<int>(nMaxLen))
|
|
{
|
|
dspAssert(FALSE);
|
|
}
|
|
ReleaseBuffer();
|
|
va_end(argList);
|
|
}
|
|
|
|
// formatting (using FormatMessage style formatting)
|
|
void CStrW::FormatMessage(PCWSTR pwzFormat, ...)
|
|
{
|
|
dspAssert(IsValidString(pwzFormat, FALSE));
|
|
|
|
// format message into temporary buffer pwzTemp
|
|
va_list argList;
|
|
va_start(argList, pwzFormat);
|
|
PWSTR pwzTemp = 0;
|
|
|
|
if (::FormatMessageW(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ALLOCATE_BUFFER,
|
|
pwzFormat, 0, 0, (PWSTR)&pwzTemp, 0, &argList) == 0 ||
|
|
pwzTemp == NULL)
|
|
{
|
|
;//REVIEW AfxThrowMemoryException();
|
|
}
|
|
|
|
// assign pwzTemp into the resulting string and free the temporary
|
|
*this = pwzTemp;
|
|
LocalFree(pwzTemp);
|
|
va_end(argList);
|
|
}
|
|
|
|
void CStrW::FormatMessage(HINSTANCE hInst, UINT nFormatID, ...)
|
|
{
|
|
// get format string from string table
|
|
CStrW strFormat;
|
|
BOOL fLoaded = strFormat.LoadString(hInst, nFormatID);
|
|
dspAssert(fLoaded);
|
|
|
|
// format message into temporary buffer pwzTemp
|
|
va_list argList;
|
|
va_start(argList, nFormatID);
|
|
PWSTR pwzTemp;
|
|
if (::FormatMessageW(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ALLOCATE_BUFFER,
|
|
strFormat, 0, 0, (PWSTR)&pwzTemp, 0, &argList) == 0 ||
|
|
pwzTemp == NULL)
|
|
{
|
|
;//REVIEW AfxThrowMemoryException();
|
|
}
|
|
|
|
// assign pwzTemp into the resulting string and free pwzTemp
|
|
*this = pwzTemp;
|
|
LocalFree(pwzTemp);
|
|
va_end(argList);
|
|
}
|
|
|
|
void CStrW::TrimRight()
|
|
{
|
|
// find beginning of trailing spaces by starting at beginning (DBCS aware)
|
|
PWSTR lpsz = m_pchData;
|
|
PWSTR lpszLast = NULL;
|
|
while (*lpsz != '\0')
|
|
{
|
|
if (_istspace(*lpsz))
|
|
{
|
|
if (lpszLast == NULL)
|
|
lpszLast = lpsz;
|
|
}
|
|
else
|
|
lpszLast = NULL;
|
|
lpsz = _wcsinc(lpsz);
|
|
}
|
|
|
|
if (lpszLast != NULL)
|
|
{
|
|
// truncate at trailing space start
|
|
*lpszLast = '\0';
|
|
m_nDataLength = (int)(lpszLast - m_pchData);
|
|
}
|
|
}
|
|
|
|
void CStrW::TrimLeft()
|
|
{
|
|
// find first non-space character
|
|
LPCWSTR lpsz = m_pchData;
|
|
while (_istspace(*lpsz))
|
|
lpsz = _wcsinc(lpsz);
|
|
|
|
// fix up data and length
|
|
int nDataLength = (int)(m_nDataLength - (lpsz - m_pchData));
|
|
memmove(m_pchData, lpsz, (nDataLength+1)*sizeof(WCHAR));
|
|
m_nDataLength = nDataLength;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// String support for template collections
|
|
|
|
void STRAPI ConstructElements(CStrW* pElements, int nCount)
|
|
{
|
|
dspAssert(IsValidAddressz(pElements, nCount * sizeof(CStrW)));
|
|
|
|
for (; nCount--; ++pElements)
|
|
memcpy(pElements, &strEmptyStringW, sizeof(*pElements));
|
|
}
|
|
|
|
void STRAPI DestructElements(CStrW* pElements, int nCount)
|
|
{
|
|
dspAssert(IsValidAddressz(pElements, nCount * sizeof(CStrW)));
|
|
|
|
for (; nCount--; ++pElements)
|
|
pElements->Empty();
|
|
}
|
|
|
|
|