2071 lines
49 KiB
C
2071 lines
49 KiB
C
|
// Local BSTR derived from WTL3.1 CString Class
|
||
|
|
||
|
#ifndef __LOCAL_BSTR__
|
||
|
#define __LOCAL_BSTR__
|
||
|
|
||
|
#pragma once
|
||
|
|
||
|
#ifndef __cplusplus
|
||
|
#error ATL requires C++ compilation (use a .cpp suffix)
|
||
|
#endif
|
||
|
|
||
|
#ifndef __ATLAPP_H__
|
||
|
#error atlmisc.h requires atlapp.h to be included first
|
||
|
#endif
|
||
|
|
||
|
namespace LBSTR
|
||
|
{
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
// Forward declarations
|
||
|
|
||
|
#ifndef _WTL_NO_LOCAL_BSTR
|
||
|
class CString;
|
||
|
#endif //!_WTL_NO_CSTRING
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
// CString - String class
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
#define VERIFY_LOCAL_BSTR
|
||
|
#endif
|
||
|
|
||
|
#ifdef VERIFY_LOCAL_BSTR
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
|
||
|
#define VERIFY_ASSERT(expr) ASSERT(expr)
|
||
|
|
||
|
#else
|
||
|
|
||
|
#if _MSC_VER >= 1300 && !defined(_CRT_PORTABLE)
|
||
|
#define _CrtRetailBreak() __debugbreak()
|
||
|
#elif defined(_M_IX86) && !defined(_CRT_PORTABLE)
|
||
|
#define _CrtRetailBreak() __asm { int 3 }
|
||
|
#elif defined(_M_ALPHA) && !defined(_CRT_PORTABLE)
|
||
|
void _BPT();
|
||
|
#pragma intrinsic(_BPT)
|
||
|
#define _CrtRetailBreak() _BPT()
|
||
|
#elif defined(_M_IA64) && !defined(_CRT_PORTABLE)
|
||
|
void __break(int);
|
||
|
#pragma intrinsic (__break)
|
||
|
#define _CrtRetailBreak() __break(0x80016)
|
||
|
#else
|
||
|
_CRTIMP void __cdecl _CrtRetailBreak(
|
||
|
void
|
||
|
);
|
||
|
#endif
|
||
|
|
||
|
#if _MSC_VER >= 1300 || !defined(_M_IX86) || defined(_CRT_PORTABLE)
|
||
|
#define _RETAIL_ASSERT_BASE(expr) \
|
||
|
(void) ((expr) || \
|
||
|
(_CrtRetailBreak(), 0))
|
||
|
#else
|
||
|
#define _RETAIL_ASSERT_BASE(expr) \
|
||
|
do { if (!(expr)) \
|
||
|
_CrtRetailBreak(); } while (0)
|
||
|
#endif
|
||
|
|
||
|
#define VERIFY_ASSERT(expr) _RETAIL_ASSERT_BASE(expr)
|
||
|
|
||
|
#endif // DEBUG
|
||
|
|
||
|
#endif // VERIFY_LOCAL_BSTR
|
||
|
|
||
|
#ifndef _WTL_NO_LOCAL_BSTR
|
||
|
|
||
|
class CStringData
|
||
|
{
|
||
|
public:
|
||
|
|
||
|
#ifdef VERIFY_LOCAL_BSTR
|
||
|
int nStartTag;
|
||
|
#endif
|
||
|
|
||
|
long nRefs; // reference count
|
||
|
int nDataLength;
|
||
|
int nAllocLength;
|
||
|
|
||
|
#ifdef VERIFY_LOCAL_BSTR
|
||
|
int nMiddleTag;
|
||
|
#endif
|
||
|
|
||
|
ULONG cbDataLength;
|
||
|
|
||
|
void SetDataLength( int p_nDataLength )
|
||
|
{
|
||
|
ASSERT( p_nDataLength >= 0 );
|
||
|
nDataLength = p_nDataLength;
|
||
|
cbDataLength = p_nDataLength * sizeof(TCHAR);
|
||
|
}
|
||
|
|
||
|
// TCHAR data[nAllocLength]
|
||
|
|
||
|
TCHAR* data()
|
||
|
{ return (TCHAR*)(this + 1); }
|
||
|
|
||
|
#ifdef VERIFY_LOCAL_BSTR
|
||
|
BYTE * GetTagPtr(void)
|
||
|
{
|
||
|
TCHAR * pzEndTag;
|
||
|
|
||
|
pzEndTag = data() + ( nAllocLength + 1 );
|
||
|
|
||
|
return (BYTE *) pzEndTag;
|
||
|
}
|
||
|
|
||
|
int GetEndTag(void)
|
||
|
{
|
||
|
int nEnd;
|
||
|
|
||
|
memcpy( &nEnd, GetTagPtr(), sizeof(int) );
|
||
|
|
||
|
return nEnd;
|
||
|
}
|
||
|
|
||
|
void SetEndTag( int nEnd )
|
||
|
{
|
||
|
memcpy( GetTagPtr(), &nEnd, sizeof(int) );
|
||
|
}
|
||
|
#endif
|
||
|
};
|
||
|
|
||
|
// Globals
|
||
|
|
||
|
// For an empty string, m_pchData will point here
|
||
|
// (note: avoids special case of checking for NULL m_pchData)
|
||
|
// empty string data (and locked)
|
||
|
|
||
|
#ifdef VERIFY_LOCAL_BSTR
|
||
|
|
||
|
const int START_TAG = 0x01234567;
|
||
|
const int MIDDLE_TAG = 0x89abcdef;
|
||
|
const int END_TAG = 0xfedcba98;
|
||
|
|
||
|
_declspec(selectany) int rgInitData[] = { START_TAG, -1, 0, 0, MIDDLE_TAG, 0, 0, END_TAG };
|
||
|
|
||
|
#else
|
||
|
|
||
|
_declspec(selectany) int rgInitData[] = { -1, 0, 0, 0, 0 };
|
||
|
|
||
|
#endif
|
||
|
|
||
|
_declspec(selectany) CStringData* _atltmpDataNil = (CStringData*)&rgInitData;
|
||
|
_declspec(selectany) LPCTSTR _atltmpPchNil = (LPCTSTR)(((BYTE*)&rgInitData) + sizeof(CStringData));
|
||
|
|
||
|
|
||
|
class CString
|
||
|
{
|
||
|
public:
|
||
|
// Constructors
|
||
|
CString();
|
||
|
CString(const CString& stringSrc);
|
||
|
CString(TCHAR ch, int nRepeat = 1);
|
||
|
CString(LPCSTR lpsz);
|
||
|
CString(LPCWSTR lpsz);
|
||
|
CString(LPCTSTR lpch, int nLength);
|
||
|
CString(const unsigned char* psz);
|
||
|
|
||
|
#ifdef VERIFY_LOCAL_BSTR
|
||
|
void VerifyInitData( void )
|
||
|
{
|
||
|
VerifyBlankData();
|
||
|
|
||
|
CStringData * pData = GetData();
|
||
|
|
||
|
VerifyTagData( pData );
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
// Attributes & Operations
|
||
|
// as an array of characters
|
||
|
int GetLength() const;
|
||
|
BOOL IsEmpty() const;
|
||
|
void Empty(); // free up the data
|
||
|
|
||
|
TCHAR GetAt(int nIndex) const; // 0 based
|
||
|
TCHAR operator[](int nIndex) const; // same as GetAt
|
||
|
void SetAt(int nIndex, TCHAR ch);
|
||
|
operator LPCTSTR() const; // as a C string
|
||
|
operator BSTR() const; // as a BSTR string
|
||
|
|
||
|
// overloaded assignment
|
||
|
const CString& operator=(const CString& stringSrc);
|
||
|
const CString& operator=(TCHAR ch);
|
||
|
#ifdef _UNICODE
|
||
|
const CString& operator=(char ch);
|
||
|
#endif
|
||
|
const CString& operator=(LPCSTR lpsz);
|
||
|
const CString& operator=(LPCWSTR lpsz);
|
||
|
const CString& operator=(const unsigned char* psz);
|
||
|
|
||
|
// string concatenation
|
||
|
const CString& operator+=(const CString& string);
|
||
|
const CString& operator+=(TCHAR ch);
|
||
|
#ifdef _UNICODE
|
||
|
const CString& operator+=(char ch);
|
||
|
#endif
|
||
|
const CString& operator+=(LPCTSTR lpsz);
|
||
|
|
||
|
friend CString __stdcall operator+(const CString& string1, const CString& string2);
|
||
|
friend CString __stdcall operator+(const CString& string, TCHAR ch);
|
||
|
friend CString __stdcall operator+(TCHAR ch, const CString& string);
|
||
|
#ifdef _UNICODE
|
||
|
friend CString __stdcall operator+(const CString& string, char ch);
|
||
|
friend CString __stdcall operator+(char ch, const CString& string);
|
||
|
#endif
|
||
|
friend CString __stdcall operator+(const CString& string, LPCTSTR lpsz);
|
||
|
friend CString __stdcall operator+(LPCTSTR lpsz, const CString& string);
|
||
|
|
||
|
// string comparison
|
||
|
int Compare(LPCTSTR lpsz) const; // straight character
|
||
|
int CompareNoCase(LPCTSTR lpsz) const; // ignore case
|
||
|
int Collate(LPCTSTR lpsz) const; // NLS aware
|
||
|
|
||
|
// simple sub-string extraction
|
||
|
CString Mid(int nFirst, int nCount) const;
|
||
|
CString Mid(int nFirst) const;
|
||
|
CString Left(int nCount) const;
|
||
|
CString Right(int nCount) const;
|
||
|
|
||
|
CString SpanIncluding(LPCTSTR lpszCharSet) const;
|
||
|
CString SpanExcluding(LPCTSTR lpszCharSet) const;
|
||
|
|
||
|
// upper/lower/reverse conversion
|
||
|
void MakeUpper();
|
||
|
void MakeLower();
|
||
|
void MakeReverse();
|
||
|
|
||
|
// trimming whitespace (either side)
|
||
|
void TrimRight();
|
||
|
void TrimLeft();
|
||
|
|
||
|
// advanced manipulation
|
||
|
// replace occurrences of chOld with chNew
|
||
|
int Replace(TCHAR chOld, TCHAR chNew);
|
||
|
// replace occurrences of substring lpszOld with lpszNew;
|
||
|
// empty lpszNew removes instances of lpszOld
|
||
|
int Replace(LPCTSTR lpszOld, LPCTSTR lpszNew);
|
||
|
// remove occurrences of chRemove
|
||
|
int Remove(TCHAR chRemove);
|
||
|
// insert character at zero-based index; concatenates
|
||
|
// if index is past end of string
|
||
|
int Insert(int nIndex, TCHAR ch);
|
||
|
// insert substring at zero-based index; concatenates
|
||
|
// if index is past end of string
|
||
|
int Insert(int nIndex, LPCTSTR pstr);
|
||
|
// delete nCount characters starting at zero-based index
|
||
|
int Delete(int nIndex, int nCount = 1);
|
||
|
|
||
|
// searching (return starting index, or -1 if not found)
|
||
|
// look for a single character match
|
||
|
int Find(TCHAR ch) const; // like "C" strchr
|
||
|
int ReverseFind(TCHAR ch) const;
|
||
|
int FindOneOf(LPCTSTR lpszCharSet) const;
|
||
|
|
||
|
// look for a specific sub-string
|
||
|
int Find(LPCTSTR lpszSub) const; // like "C" strstr
|
||
|
|
||
|
// Concatentation for non strings
|
||
|
const CString& Append(int n)
|
||
|
{
|
||
|
TCHAR szBuffer[10];
|
||
|
wsprintf(szBuffer,_T("%d"),n);
|
||
|
ConcatInPlace(SafeStrlen(szBuffer), szBuffer);
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
// simple formatting
|
||
|
void __cdecl Format(LPCTSTR lpszFormat, ...);
|
||
|
void __cdecl Format(UINT nFormatID, ...);
|
||
|
|
||
|
// formatting for localization (uses FormatMessage API)
|
||
|
BOOL __cdecl FormatMessage(LPCTSTR lpszFormat, ...);
|
||
|
BOOL __cdecl FormatMessage(UINT nFormatID, ...);
|
||
|
|
||
|
// Windows support
|
||
|
BOOL LoadString(UINT nID); // load from string resource
|
||
|
// 255 chars max
|
||
|
#ifndef _UNICODE
|
||
|
// ANSI <-> OEM support (convert string in place)
|
||
|
void AnsiToOem();
|
||
|
void OemToAnsi();
|
||
|
#endif
|
||
|
|
||
|
#ifndef _ATL_NO_COM
|
||
|
// OLE BSTR support (use for OLE automation)
|
||
|
BSTR AllocSysString() const;
|
||
|
BSTR SetSysString(BSTR* pbstr) const;
|
||
|
#endif //!_ATL_NO_COM
|
||
|
|
||
|
// Access to string implementation buffer as "C" character array
|
||
|
LPTSTR GetBuffer(int nMinBufLength);
|
||
|
void ReleaseBuffer(int nNewLength = -1);
|
||
|
LPTSTR GetBufferSetLength(int nNewLength);
|
||
|
void FreeExtra();
|
||
|
|
||
|
// Use LockBuffer/UnlockBuffer to turn refcounting off
|
||
|
LPTSTR LockBuffer();
|
||
|
void UnlockBuffer();
|
||
|
|
||
|
// Implementation
|
||
|
public:
|
||
|
~CString();
|
||
|
int GetAllocLength() const;
|
||
|
|
||
|
static BOOL __stdcall _IsValidString(LPCWSTR lpsz, int nLength)
|
||
|
{
|
||
|
if(lpsz == NULL)
|
||
|
return FALSE;
|
||
|
return !::IsBadStringPtrW(lpsz, nLength);
|
||
|
}
|
||
|
|
||
|
static BOOL __stdcall _IsValidString(LPCSTR lpsz, int nLength)
|
||
|
{
|
||
|
if(lpsz == NULL)
|
||
|
return FALSE;
|
||
|
return !::IsBadStringPtrA(lpsz, nLength);
|
||
|
}
|
||
|
|
||
|
protected:
|
||
|
LPTSTR m_pchData; // pointer to ref counted string data
|
||
|
|
||
|
// implementation helpers
|
||
|
CStringData* GetData() const;
|
||
|
void Init();
|
||
|
void AllocCopy(CString& dest, int nCopyLen, int nCopyIndex, int nExtraLen) const;
|
||
|
BOOL AllocBuffer(int nLen);
|
||
|
void AssignCopy(int nSrcLen, LPCTSTR lpszSrcData);
|
||
|
BOOL ConcatCopy(int nSrc1Len, LPCTSTR lpszSrc1Data, int nSrc2Len, LPCTSTR lpszSrc2Data);
|
||
|
void ConcatInPlace(int nSrcLen, LPCTSTR lpszSrcData);
|
||
|
void FormatV(LPCTSTR lpszFormat, va_list argList);
|
||
|
void CopyBeforeWrite();
|
||
|
BOOL AllocBeforeWrite(int nLen);
|
||
|
void Release();
|
||
|
static void PASCAL Release(CStringData* pData);
|
||
|
static int PASCAL SafeStrlen(LPCTSTR lpsz);
|
||
|
|
||
|
static int __stdcall _LoadString(UINT nID, LPTSTR lpszBuf, UINT nMaxBuf)
|
||
|
{
|
||
|
#ifdef _DEBUG
|
||
|
// LoadString without annoying warning from the Debug kernel if the
|
||
|
// segment containing the string is not present
|
||
|
if (::FindResource(_Module.GetResourceInstance(), MAKEINTRESOURCE((nID>>4) + 1), RT_STRING) == NULL)
|
||
|
{
|
||
|
lpszBuf[0] = '\0';
|
||
|
return 0; // not found
|
||
|
}
|
||
|
#endif //_DEBUG
|
||
|
int nLen = ::LoadString(_Module.GetResourceInstance(), nID, lpszBuf, nMaxBuf);
|
||
|
if (nLen == 0)
|
||
|
lpszBuf[0] = '\0';
|
||
|
return nLen;
|
||
|
}
|
||
|
|
||
|
#ifdef VERIFY_LOCAL_BSTR
|
||
|
static void VerifyBlankData( void )
|
||
|
{
|
||
|
VERIFY_ASSERT( rgInitData[0] == START_TAG );
|
||
|
VERIFY_ASSERT( rgInitData[1] == -1 );
|
||
|
VERIFY_ASSERT( rgInitData[2] == 0 );
|
||
|
VERIFY_ASSERT( rgInitData[3] == 0 );
|
||
|
VERIFY_ASSERT( rgInitData[4] == MIDDLE_TAG );
|
||
|
VERIFY_ASSERT( rgInitData[5] == 0 );
|
||
|
VERIFY_ASSERT( rgInitData[6] == 0 );
|
||
|
VERIFY_ASSERT( rgInitData[7] == END_TAG );
|
||
|
}
|
||
|
|
||
|
static void VerifyTagData( CStringData * pData )
|
||
|
{
|
||
|
if ( pData && pData != _atltmpDataNil )
|
||
|
{
|
||
|
VERIFY_ASSERT( pData->nStartTag == START_TAG );
|
||
|
VERIFY_ASSERT( pData->nMiddleTag == MIDDLE_TAG );
|
||
|
VERIFY_ASSERT( pData->GetEndTag() == END_TAG );
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
static const CString& __stdcall _GetEmptyString()
|
||
|
{
|
||
|
#ifdef VERIFY_LOCAL_BSTR
|
||
|
VerifyBlankData();
|
||
|
#endif
|
||
|
|
||
|
return *(CString*)&_atltmpPchNil;
|
||
|
}
|
||
|
|
||
|
// CString conversion helpers
|
||
|
static int __cdecl _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, (int)count, NULL, NULL);
|
||
|
ATLASSERT(mbstr == NULL || result <= (int)count);
|
||
|
if (result > 0)
|
||
|
mbstr[result - 1] = 0;
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
static int __cdecl _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, (int)count);
|
||
|
ATLASSERT(wcstr == NULL || result <= (int)count);
|
||
|
if (result > 0)
|
||
|
wcstr[result - 1] = 0;
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
// Helpers to avoid CRT startup code
|
||
|
static TCHAR* _cstrchr(const TCHAR* p, TCHAR ch)
|
||
|
{
|
||
|
//strchr for '\0' should succeed
|
||
|
while (*p != 0)
|
||
|
{
|
||
|
if (*p == ch)
|
||
|
break;
|
||
|
p = ::CharNext(p);
|
||
|
}
|
||
|
return (TCHAR*)((*p == ch) ? p : NULL);
|
||
|
}
|
||
|
static TCHAR* _cstrchr_db(const TCHAR* p, TCHAR ch1, TCHAR ch2)
|
||
|
{
|
||
|
const TCHAR* lpsz = NULL;
|
||
|
while (*p != 0)
|
||
|
{
|
||
|
if (*p == ch1 && *(p + 1) == ch2)
|
||
|
{
|
||
|
lpsz = p;
|
||
|
break;
|
||
|
}
|
||
|
p = ::CharNext(p);
|
||
|
}
|
||
|
return (TCHAR*)lpsz;
|
||
|
}
|
||
|
static TCHAR* _cstrrchr(const TCHAR* p, TCHAR ch)
|
||
|
{
|
||
|
const TCHAR* lpsz = NULL;
|
||
|
while (*p != 0)
|
||
|
{
|
||
|
if (*p == ch)
|
||
|
lpsz = p;
|
||
|
p = ::CharNext(p);
|
||
|
}
|
||
|
return (TCHAR*)lpsz;
|
||
|
}
|
||
|
static TCHAR* _cstrrev(TCHAR* pStr)
|
||
|
{
|
||
|
// Optimize NULL, zero-length, and single-char case.
|
||
|
if ((pStr == NULL) || (pStr[0] == '\0') || (pStr[1] == '\0'))
|
||
|
return pStr;
|
||
|
|
||
|
TCHAR* p = pStr;
|
||
|
|
||
|
while (p[1] != 0)
|
||
|
{
|
||
|
TCHAR* pNext = ::CharNext(p);
|
||
|
if(pNext > p + 1)
|
||
|
{
|
||
|
char p1 = *(char*)p;
|
||
|
*(char*)p = *(char*)(p + 1);
|
||
|
*(char*)(p + 1) = p1;
|
||
|
}
|
||
|
p = pNext;
|
||
|
}
|
||
|
|
||
|
TCHAR* q = pStr;
|
||
|
|
||
|
while (q < p)
|
||
|
{
|
||
|
TCHAR t = *q;
|
||
|
*q = *p;
|
||
|
*p = t;
|
||
|
q++;
|
||
|
p--;
|
||
|
}
|
||
|
return (TCHAR*)pStr;
|
||
|
}
|
||
|
static TCHAR* _cstrstr(const TCHAR* pStr, const TCHAR* pCharSet)
|
||
|
{
|
||
|
int nLen = lstrlen(pCharSet);
|
||
|
if (nLen == 0)
|
||
|
return (TCHAR*)pStr;
|
||
|
|
||
|
const TCHAR* pRet = NULL;
|
||
|
const TCHAR* pCur = pStr;
|
||
|
while((pStr = _cstrchr(pCur, *pCharSet)) != NULL)
|
||
|
{
|
||
|
if(memcmp(pCur, pCharSet, nLen * sizeof(TCHAR)) == 0)
|
||
|
{
|
||
|
pRet = pCur;
|
||
|
break;
|
||
|
}
|
||
|
pCur = ::CharNext(pCur);
|
||
|
}
|
||
|
return (TCHAR*) pRet;
|
||
|
}
|
||
|
static int _cstrspn(const TCHAR* pStr, const TCHAR* pCharSet)
|
||
|
{
|
||
|
int nRet = 0;
|
||
|
TCHAR* p = (TCHAR*)pStr;
|
||
|
while (*p != 0)
|
||
|
{
|
||
|
TCHAR* pNext = ::CharNext(p);
|
||
|
if(pNext > p + 1)
|
||
|
{
|
||
|
if(_cstrchr_db(pCharSet, *p, *(p + 1)) == NULL)
|
||
|
break;
|
||
|
nRet += 2;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if(_cstrchr(pCharSet, *p) == NULL)
|
||
|
break;
|
||
|
nRet++;
|
||
|
}
|
||
|
p = pNext;
|
||
|
}
|
||
|
return nRet;
|
||
|
}
|
||
|
static int _cstrcspn(const TCHAR* pStr, const TCHAR* pCharSet)
|
||
|
{
|
||
|
int nRet = 0;
|
||
|
TCHAR* p = (TCHAR*)pStr;
|
||
|
while (*p != 0)
|
||
|
{
|
||
|
TCHAR* pNext = ::CharNext(p);
|
||
|
if(pNext > p + 1)
|
||
|
{
|
||
|
if(_cstrchr_db(pCharSet, *p, *(p + 1)) != NULL)
|
||
|
break;
|
||
|
nRet += 2;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if(_cstrchr(pCharSet, *p) != NULL)
|
||
|
break;
|
||
|
nRet++;
|
||
|
}
|
||
|
p = pNext;
|
||
|
}
|
||
|
return nRet;
|
||
|
}
|
||
|
static TCHAR* _cstrpbrk(const TCHAR* p, const TCHAR* lpszCharSet)
|
||
|
{
|
||
|
while (*p != 0)
|
||
|
{
|
||
|
if (_cstrchr(lpszCharSet, *p) != NULL)
|
||
|
{
|
||
|
return (TCHAR*)p;
|
||
|
break;
|
||
|
}
|
||
|
p = ::CharNext(p);
|
||
|
}
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
static int _cstrisdigit(TCHAR ch)
|
||
|
{
|
||
|
WORD type;
|
||
|
GetStringTypeEx(GetThreadLocale(), CT_CTYPE1, &ch, 1, &type);
|
||
|
return (type & C1_DIGIT) == C1_DIGIT;
|
||
|
}
|
||
|
|
||
|
static int _cstrisspace(TCHAR ch)
|
||
|
{
|
||
|
WORD type;
|
||
|
GetStringTypeEx(GetThreadLocale(), CT_CTYPE1, &ch, 1, &type);
|
||
|
return (type & C1_SPACE) == C1_SPACE;
|
||
|
}
|
||
|
|
||
|
static int _cstrcmp(const TCHAR* pstrOne, const TCHAR* pstrOther)
|
||
|
{
|
||
|
return lstrcmp(pstrOne, pstrOther);
|
||
|
}
|
||
|
|
||
|
static int _cstrcmpi(const TCHAR* pstrOne, const TCHAR* pstrOther)
|
||
|
{
|
||
|
return lstrcmpi(pstrOne, pstrOther);
|
||
|
}
|
||
|
|
||
|
static int _cstrcoll(const TCHAR* pstrOne, const TCHAR* pstrOther)
|
||
|
{
|
||
|
int nRet = CompareString(GetThreadLocale(), 0, pstrOne, -1, pstrOther, -1);
|
||
|
ATLASSERT(nRet != 0);
|
||
|
return nRet - 2; // Convert to strcmp convention. This really is documented.
|
||
|
}
|
||
|
|
||
|
static int _cstrcolli(const TCHAR* pstrOne, const TCHAR* pstrOther)
|
||
|
{
|
||
|
int nRet = CompareString(GetThreadLocale(), NORM_IGNORECASE, pstrOne, -1, pstrOther, -1);
|
||
|
ATLASSERT(nRet != 0);
|
||
|
return nRet - 2; // Convert to strcmp convention. This really is documented.
|
||
|
}
|
||
|
};
|
||
|
|
||
|
// Compare helpers
|
||
|
bool __stdcall operator==(const CString& s1, const CString& s2);
|
||
|
bool __stdcall operator==(const CString& s1, LPCTSTR s2);
|
||
|
bool __stdcall operator==(LPCTSTR s1, const CString& s2);
|
||
|
bool __stdcall operator!=(const CString& s1, const CString& s2);
|
||
|
bool __stdcall operator!=(const CString& s1, LPCTSTR s2);
|
||
|
bool __stdcall operator!=(LPCTSTR s1, const CString& s2);
|
||
|
bool __stdcall operator<(const CString& s1, const CString& s2);
|
||
|
bool __stdcall operator<(const CString& s1, LPCTSTR s2);
|
||
|
bool __stdcall operator<(LPCTSTR s1, const CString& s2);
|
||
|
bool __stdcall operator>(const CString& s1, const CString& s2);
|
||
|
bool __stdcall operator>(const CString& s1, LPCTSTR s2);
|
||
|
bool __stdcall operator>(LPCTSTR s1, const CString& s2);
|
||
|
bool __stdcall operator<=(const CString& s1, const CString& s2);
|
||
|
bool __stdcall operator<=(const CString& s1, LPCTSTR s2);
|
||
|
bool __stdcall operator<=(LPCTSTR s1, const CString& s2);
|
||
|
bool __stdcall operator>=(const CString& s1, const CString& s2);
|
||
|
bool __stdcall operator>=(const CString& s1, LPCTSTR s2);
|
||
|
bool __stdcall operator>=(LPCTSTR s1, const CString& s2);
|
||
|
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
// CString Implementation
|
||
|
|
||
|
inline CStringData* CString::GetData() const
|
||
|
{ ATLASSERT(m_pchData != NULL); return ((CStringData*)m_pchData) - 1; }
|
||
|
inline void CString::Init()
|
||
|
{ m_pchData = _GetEmptyString().m_pchData; }
|
||
|
inline CString::CString(const unsigned char* lpsz)
|
||
|
{ Init(); *this = (LPCSTR)lpsz; }
|
||
|
inline const CString& CString::operator=(const unsigned char* lpsz)
|
||
|
{ *this = (LPCSTR)lpsz; return *this; }
|
||
|
#ifdef _UNICODE
|
||
|
inline const CString& CString::operator+=(char ch)
|
||
|
{ *this += (TCHAR)ch; return *this; }
|
||
|
inline const CString& CString::operator=(char ch)
|
||
|
{ *this = (TCHAR)ch; return *this; }
|
||
|
inline CString __stdcall operator+(const CString& string, char ch)
|
||
|
{ return string + (TCHAR)ch; }
|
||
|
inline CString __stdcall operator+(char ch, const CString& string)
|
||
|
{ return (TCHAR)ch + string; }
|
||
|
#endif
|
||
|
|
||
|
inline int CString::GetLength() const
|
||
|
{ return GetData()->nDataLength; }
|
||
|
inline int CString::GetAllocLength() const
|
||
|
{ return GetData()->nAllocLength; }
|
||
|
inline BOOL CString::IsEmpty() const
|
||
|
{ return GetData()->nDataLength == 0; }
|
||
|
inline CString::operator LPCTSTR() const
|
||
|
{ return m_pchData; }
|
||
|
inline CString::operator BSTR() const
|
||
|
{ return (BSTR) m_pchData; }
|
||
|
inline int PASCAL CString::SafeStrlen(LPCTSTR lpsz)
|
||
|
{ return (lpsz == NULL) ? 0 : lstrlen(lpsz); }
|
||
|
|
||
|
// CString support (windows specific)
|
||
|
inline int CString::Compare(LPCTSTR lpsz) const
|
||
|
{ return _cstrcmp(m_pchData, lpsz); } // MBCS/Unicode aware
|
||
|
inline int CString::CompareNoCase(LPCTSTR lpsz) const
|
||
|
{ return _cstrcmpi(m_pchData, lpsz); } // MBCS/Unicode aware
|
||
|
// CString::Collate is often slower than Compare but is MBSC/Unicode
|
||
|
// aware as well as locale-sensitive with respect to sort order.
|
||
|
inline int CString::Collate(LPCTSTR lpsz) const
|
||
|
{ return _cstrcoll(m_pchData, lpsz); } // locale sensitive
|
||
|
|
||
|
inline TCHAR CString::GetAt(int nIndex) const
|
||
|
{
|
||
|
ATLASSERT(nIndex >= 0);
|
||
|
ATLASSERT(nIndex < GetData()->nDataLength);
|
||
|
return m_pchData[nIndex];
|
||
|
}
|
||
|
inline TCHAR CString::operator[](int nIndex) const
|
||
|
{
|
||
|
// same as GetAt
|
||
|
ATLASSERT(nIndex >= 0);
|
||
|
ATLASSERT(nIndex < GetData()->nDataLength);
|
||
|
return m_pchData[nIndex];
|
||
|
}
|
||
|
inline bool __stdcall operator==(const CString& s1, const CString& s2)
|
||
|
{ return s1.Compare(s2) == 0; }
|
||
|
inline bool __stdcall operator==(const CString& s1, LPCTSTR s2)
|
||
|
{ return s1.Compare(s2) == 0; }
|
||
|
inline bool __stdcall operator==(LPCTSTR s1, const CString& s2)
|
||
|
{ return s2.Compare(s1) == 0; }
|
||
|
inline bool __stdcall operator!=(const CString& s1, const CString& s2)
|
||
|
{ return s1.Compare(s2) != 0; }
|
||
|
inline bool __stdcall operator!=(const CString& s1, LPCTSTR s2)
|
||
|
{ return s1.Compare(s2) != 0; }
|
||
|
inline bool __stdcall operator!=(LPCTSTR s1, const CString& s2)
|
||
|
{ return s2.Compare(s1) != 0; }
|
||
|
inline bool __stdcall operator<(const CString& s1, const CString& s2)
|
||
|
{ return s1.Compare(s2) < 0; }
|
||
|
inline bool __stdcall operator<(const CString& s1, LPCTSTR s2)
|
||
|
{ return s1.Compare(s2) < 0; }
|
||
|
inline bool __stdcall operator<(LPCTSTR s1, const CString& s2)
|
||
|
{ return s2.Compare(s1) > 0; }
|
||
|
inline bool __stdcall operator>(const CString& s1, const CString& s2)
|
||
|
{ return s1.Compare(s2) > 0; }
|
||
|
inline bool __stdcall operator>(const CString& s1, LPCTSTR s2)
|
||
|
{ return s1.Compare(s2) > 0; }
|
||
|
inline bool __stdcall operator>(LPCTSTR s1, const CString& s2)
|
||
|
{ return s2.Compare(s1) < 0; }
|
||
|
inline bool __stdcall operator<=(const CString& s1, const CString& s2)
|
||
|
{ return s1.Compare(s2) <= 0; }
|
||
|
inline bool __stdcall operator<=(const CString& s1, LPCTSTR s2)
|
||
|
{ return s1.Compare(s2) <= 0; }
|
||
|
inline bool __stdcall operator<=(LPCTSTR s1, const CString& s2)
|
||
|
{ return s2.Compare(s1) >= 0; }
|
||
|
inline bool __stdcall operator>=(const CString& s1, const CString& s2)
|
||
|
{ return s1.Compare(s2) >= 0; }
|
||
|
inline bool __stdcall operator>=(const CString& s1, LPCTSTR s2)
|
||
|
{ return s1.Compare(s2) >= 0; }
|
||
|
inline bool __stdcall operator>=(LPCTSTR s1, const CString& s2)
|
||
|
{ return s2.Compare(s1) <= 0; }
|
||
|
|
||
|
inline CString::CString()
|
||
|
{
|
||
|
Init();
|
||
|
}
|
||
|
|
||
|
inline CString::CString(const CString& stringSrc)
|
||
|
{
|
||
|
ATLASSERT(stringSrc.GetData()->nRefs != 0);
|
||
|
if (stringSrc.GetData()->nRefs >= 0)
|
||
|
{
|
||
|
ATLASSERT(stringSrc.GetData() != _atltmpDataNil);
|
||
|
m_pchData = stringSrc.m_pchData;
|
||
|
InterlockedIncrement(&GetData()->nRefs);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Init();
|
||
|
*this = stringSrc.m_pchData;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
inline BOOL CString::AllocBuffer(int nLen)
|
||
|
// always allocate one extra character for '\0' termination
|
||
|
// assumes [optimistically] that data length will equal allocation length
|
||
|
{
|
||
|
ATLASSERT(nLen >= 0);
|
||
|
ATLASSERT(nLen <= INT_MAX - 1); // max size (enough room for 1 extra)
|
||
|
|
||
|
#ifdef VERIFY_LOCAL_BSTR
|
||
|
VerifyInitData();
|
||
|
#endif
|
||
|
|
||
|
if (nLen == 0)
|
||
|
{
|
||
|
Init();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
CStringData* pData = NULL;
|
||
|
int cbBufLen = sizeof(CStringData) + (nLen + 1) * sizeof(TCHAR);
|
||
|
|
||
|
#ifdef VERIFY_LOCAL_BSTR
|
||
|
cbBufLen += sizeof(int); // End Tag
|
||
|
#endif
|
||
|
|
||
|
ATLTRY(pData = (CStringData*)new BYTE[ cbBufLen ]);
|
||
|
if(pData == NULL)
|
||
|
return FALSE;
|
||
|
|
||
|
pData->nRefs = 1;
|
||
|
pData->data()[nLen] = '\0';
|
||
|
pData->SetDataLength( nLen );
|
||
|
pData->nAllocLength = nLen;
|
||
|
m_pchData = pData->data();
|
||
|
|
||
|
#ifdef VERIFY_LOCAL_BSTR
|
||
|
pData->nStartTag = START_TAG;
|
||
|
pData->nMiddleTag = MIDDLE_TAG;
|
||
|
pData->SetEndTag( END_TAG );
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
inline void CString::Release()
|
||
|
{
|
||
|
#ifdef VERIFY_LOCAL_BSTR
|
||
|
VerifyInitData();
|
||
|
#endif
|
||
|
|
||
|
if (GetData() != _atltmpDataNil)
|
||
|
{
|
||
|
ATLASSERT(GetData()->nRefs != 0);
|
||
|
if (InterlockedDecrement(&GetData()->nRefs) <= 0)
|
||
|
delete[] (BYTE*)GetData();
|
||
|
Init();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
inline void PASCAL CString::Release(CStringData* pData)
|
||
|
{
|
||
|
#ifdef VERIFY_LOCAL_BSTR
|
||
|
VerifyBlankData();
|
||
|
VerifyTagData( pData );
|
||
|
#endif
|
||
|
|
||
|
if (pData != _atltmpDataNil)
|
||
|
{
|
||
|
ATLASSERT(pData->nRefs != 0);
|
||
|
if (InterlockedDecrement(&pData->nRefs) <= 0)
|
||
|
delete[] (BYTE*)pData;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
inline void CString::Empty()
|
||
|
{
|
||
|
if (GetData()->nDataLength == 0)
|
||
|
return;
|
||
|
|
||
|
if (GetData()->nRefs >= 0)
|
||
|
Release();
|
||
|
else
|
||
|
*this = _T("");
|
||
|
|
||
|
ATLASSERT(GetData()->nDataLength == 0);
|
||
|
ATLASSERT(GetData()->nRefs < 0 || GetData()->nAllocLength == 0);
|
||
|
|
||
|
#ifdef VERIFY_LOCAL_BSTR
|
||
|
VerifyInitData();
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
inline void CString::CopyBeforeWrite()
|
||
|
{
|
||
|
if (GetData()->nRefs > 1)
|
||
|
{
|
||
|
CStringData* pData = GetData();
|
||
|
Release();
|
||
|
if(AllocBuffer(pData->nDataLength))
|
||
|
memcpy(m_pchData, pData->data(), (pData->nDataLength + 1) * sizeof(TCHAR));
|
||
|
}
|
||
|
ATLASSERT(GetData()->nRefs <= 1);
|
||
|
}
|
||
|
|
||
|
inline BOOL CString::AllocBeforeWrite(int nLen)
|
||
|
{
|
||
|
BOOL bRet = TRUE;
|
||
|
if (GetData()->nRefs > 1 || nLen > GetData()->nAllocLength)
|
||
|
{
|
||
|
Release();
|
||
|
bRet = AllocBuffer(nLen);
|
||
|
}
|
||
|
ATLASSERT(GetData()->nRefs <= 1);
|
||
|
return bRet;
|
||
|
}
|
||
|
|
||
|
inline CString::~CString()
|
||
|
// free any attached data
|
||
|
{
|
||
|
#ifdef VERIFY_LOCAL_BSTR
|
||
|
VerifyInitData();
|
||
|
#endif
|
||
|
|
||
|
if (GetData() != _atltmpDataNil)
|
||
|
{
|
||
|
if (InterlockedDecrement(&GetData()->nRefs) <= 0)
|
||
|
delete[] (BYTE*)GetData();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
inline void CString::AllocCopy(CString& 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.Init();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if(dest.AllocBuffer(nNewLen))
|
||
|
memcpy(dest.m_pchData, m_pchData + nCopyIndex, nCopyLen * sizeof(TCHAR));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
inline CString::CString(LPCTSTR lpsz)
|
||
|
{
|
||
|
Init();
|
||
|
if (lpsz != NULL && HIWORD(lpsz) == NULL)
|
||
|
{
|
||
|
UINT nID = LOWORD((DWORD_PTR)lpsz);
|
||
|
if (!LoadString(nID))
|
||
|
ATLTRACE2(atlTraceUI, 0, _T("Warning: implicit LoadString(%u) in CString failed\n"), nID);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
int nLen = SafeStrlen(lpsz);
|
||
|
if (nLen != 0)
|
||
|
{
|
||
|
if(AllocBuffer(nLen))
|
||
|
memcpy(m_pchData, lpsz, nLen * sizeof(TCHAR));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#ifdef _UNICODE
|
||
|
inline CString::CString(LPCSTR lpsz)
|
||
|
{
|
||
|
Init();
|
||
|
int nSrcLen = lpsz != NULL ? lstrlenA(lpsz) : 0;
|
||
|
if (nSrcLen != 0)
|
||
|
{
|
||
|
if(AllocBuffer(nSrcLen))
|
||
|
{
|
||
|
_mbstowcsz(m_pchData, lpsz, nSrcLen + 1);
|
||
|
ReleaseBuffer();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#else //_UNICODE
|
||
|
inline CString::CString(LPCWSTR lpsz)
|
||
|
{
|
||
|
Init();
|
||
|
int nSrcLen = lpsz != NULL ? wcslen(lpsz) : 0;
|
||
|
if (nSrcLen != 0)
|
||
|
{
|
||
|
if(AllocBuffer(nSrcLen * 2))
|
||
|
{
|
||
|
_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 CString&' so that
|
||
|
// assigning it again will cause a copy, eg: s1 = s2 = "hi there".
|
||
|
//
|
||
|
|
||
|
inline void CString::AssignCopy(int nSrcLen, LPCTSTR lpszSrcData)
|
||
|
{
|
||
|
if(AllocBeforeWrite(nSrcLen))
|
||
|
{
|
||
|
memcpy(m_pchData, lpszSrcData, nSrcLen * sizeof(TCHAR));
|
||
|
GetData()->SetDataLength( nSrcLen );
|
||
|
m_pchData[nSrcLen] = '\0';
|
||
|
}
|
||
|
}
|
||
|
|
||
|
inline const CString& CString::operator=(const CString& stringSrc)
|
||
|
{
|
||
|
if (m_pchData != stringSrc.m_pchData)
|
||
|
{
|
||
|
if ((GetData()->nRefs < 0 && GetData() != _atltmpDataNil) || stringSrc.GetData()->nRefs < 0)
|
||
|
{
|
||
|
// actual copy necessary since one of the strings is locked
|
||
|
AssignCopy(stringSrc.GetData()->nDataLength, stringSrc.m_pchData);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// can just copy references around
|
||
|
Release();
|
||
|
ATLASSERT(stringSrc.GetData() != _atltmpDataNil);
|
||
|
m_pchData = stringSrc.m_pchData;
|
||
|
InterlockedIncrement(&GetData()->nRefs);
|
||
|
}
|
||
|
}
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
inline const CString& CString::operator=(LPCTSTR lpsz)
|
||
|
{
|
||
|
ATLASSERT(lpsz == NULL || _IsValidString(lpsz, FALSE));
|
||
|
AssignCopy(SafeStrlen(lpsz), lpsz);
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
#ifdef _UNICODE
|
||
|
inline const CString& CString::operator=(LPCSTR lpsz)
|
||
|
{
|
||
|
int nSrcLen = lpsz != NULL ? lstrlenA(lpsz) : 0;
|
||
|
if(AllocBeforeWrite(nSrcLen))
|
||
|
{
|
||
|
_mbstowcsz(m_pchData, lpsz, nSrcLen + 1);
|
||
|
ReleaseBuffer();
|
||
|
}
|
||
|
return *this;
|
||
|
}
|
||
|
#else //!_UNICODE
|
||
|
inline const CString& CString::operator=(LPCWSTR lpsz)
|
||
|
{
|
||
|
int nSrcLen = lpsz != NULL ? wcslen(lpsz) : 0;
|
||
|
if(AllocBeforeWrite(nSrcLen * 2))
|
||
|
{
|
||
|
_wcstombsz(m_pchData, lpsz, (nSrcLen * 2) + 1);
|
||
|
ReleaseBuffer();
|
||
|
}
|
||
|
return *this;
|
||
|
}
|
||
|
#endif //!_UNICODE
|
||
|
|
||
|
// Concatenation
|
||
|
// NOTE: "operator+" is done as friend functions for simplicity
|
||
|
// There are three variants:
|
||
|
// CString + CString
|
||
|
// and for ? = TCHAR, LPCTSTR
|
||
|
// CString + ?
|
||
|
// ? + CString
|
||
|
|
||
|
inline BOOL CString::ConcatCopy(int nSrc1Len, LPCTSTR lpszSrc1Data,
|
||
|
int nSrc2Len, LPCTSTR lpszSrc2Data)
|
||
|
{
|
||
|
// -- master concatenation routine
|
||
|
// Concatenate two sources
|
||
|
// -- assume that 'this' is a new CString object
|
||
|
|
||
|
BOOL bRet = TRUE;
|
||
|
int nNewLen = nSrc1Len + nSrc2Len;
|
||
|
if (nNewLen != 0)
|
||
|
{
|
||
|
bRet = AllocBuffer(nNewLen);
|
||
|
if (bRet)
|
||
|
{
|
||
|
memcpy(m_pchData, lpszSrc1Data, nSrc1Len * sizeof(TCHAR));
|
||
|
memcpy(m_pchData + nSrc1Len, lpszSrc2Data, nSrc2Len * sizeof(TCHAR));
|
||
|
}
|
||
|
}
|
||
|
return bRet;
|
||
|
}
|
||
|
|
||
|
inline CString __stdcall operator+(const CString& string1, const CString& string2)
|
||
|
{
|
||
|
CString s;
|
||
|
s.ConcatCopy(string1.GetData()->nDataLength, string1.m_pchData, string2.GetData()->nDataLength, string2.m_pchData);
|
||
|
return s;
|
||
|
}
|
||
|
|
||
|
inline CString __stdcall operator+(const CString& string, LPCTSTR lpsz)
|
||
|
{
|
||
|
ATLASSERT(lpsz == NULL || CString::_IsValidString(lpsz, FALSE));
|
||
|
CString s;
|
||
|
s.ConcatCopy(string.GetData()->nDataLength, string.m_pchData, CString::SafeStrlen(lpsz), lpsz);
|
||
|
return s;
|
||
|
}
|
||
|
|
||
|
inline CString __stdcall operator+(LPCTSTR lpsz, const CString& string)
|
||
|
{
|
||
|
ATLASSERT(lpsz == NULL || CString::_IsValidString(lpsz, FALSE));
|
||
|
CString s;
|
||
|
s.ConcatCopy(CString::SafeStrlen(lpsz), lpsz, string.GetData()->nDataLength, string.m_pchData);
|
||
|
return s;
|
||
|
}
|
||
|
|
||
|
inline void CString::ConcatInPlace(int nSrcLen, LPCTSTR lpszSrcData)
|
||
|
{
|
||
|
// -- the main routine for += operators
|
||
|
|
||
|
// concatenating an empty string is a no-op!
|
||
|
if (nSrcLen == 0)
|
||
|
return;
|
||
|
|
||
|
// if the buffer is too small, or we have a width mis-match, just
|
||
|
// allocate a new buffer (slow but sure)
|
||
|
if (GetData()->nRefs > 1 || GetData()->nDataLength + nSrcLen > GetData()->nAllocLength)
|
||
|
{
|
||
|
// we have to grow the buffer, use the ConcatCopy routine
|
||
|
CStringData* pOldData = GetData();
|
||
|
if (ConcatCopy(GetData()->nDataLength, m_pchData, nSrcLen, lpszSrcData))
|
||
|
{
|
||
|
ATLASSERT(pOldData != NULL);
|
||
|
CString::Release(pOldData);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// fast concatenation when buffer big enough
|
||
|
memcpy(m_pchData + GetData()->nDataLength, lpszSrcData, nSrcLen * sizeof(TCHAR));
|
||
|
GetData()->SetDataLength( GetData()->nDataLength + nSrcLen );
|
||
|
ATLASSERT(GetData()->nDataLength <= GetData()->nAllocLength);
|
||
|
m_pchData[GetData()->nDataLength] = '\0';
|
||
|
}
|
||
|
}
|
||
|
|
||
|
inline const CString& CString::operator+=(LPCTSTR lpsz)
|
||
|
{
|
||
|
ATLASSERT(lpsz == NULL || _IsValidString(lpsz, FALSE));
|
||
|
ConcatInPlace(SafeStrlen(lpsz), lpsz);
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
inline const CString& CString::operator+=(TCHAR ch)
|
||
|
{
|
||
|
ConcatInPlace(1, &ch);
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
inline const CString& CString::operator+=(const CString& string)
|
||
|
{
|
||
|
ConcatInPlace(string.GetData()->nDataLength, string.m_pchData);
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
inline LPTSTR CString::GetBuffer(int nMinBufLength)
|
||
|
{
|
||
|
ATLASSERT(nMinBufLength >= 0);
|
||
|
|
||
|
if (GetData()->nRefs > 1 || nMinBufLength > GetData()->nAllocLength)
|
||
|
{
|
||
|
// we have to grow the buffer
|
||
|
CStringData* pOldData = GetData();
|
||
|
int nOldLen = GetData()->nDataLength; // AllocBuffer will tromp it
|
||
|
if (nMinBufLength < nOldLen)
|
||
|
nMinBufLength = nOldLen;
|
||
|
|
||
|
if(AllocBuffer(nMinBufLength))
|
||
|
{
|
||
|
memcpy(m_pchData, pOldData->data(), (nOldLen + 1) * sizeof(TCHAR));
|
||
|
GetData()->SetDataLength( nOldLen );
|
||
|
CString::Release(pOldData);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ATLASSERT(GetData()->nRefs <= 1);
|
||
|
|
||
|
#ifdef VERIFY_LOCAL_BSTR
|
||
|
VerifyInitData();
|
||
|
#endif
|
||
|
|
||
|
// return a pointer to the character storage for this string
|
||
|
ATLASSERT(m_pchData != NULL);
|
||
|
return m_pchData;
|
||
|
}
|
||
|
|
||
|
inline void CString::ReleaseBuffer(int nNewLength)
|
||
|
{
|
||
|
CopyBeforeWrite(); // just in case GetBuffer was not called
|
||
|
|
||
|
if (nNewLength == -1)
|
||
|
nNewLength = lstrlen(m_pchData); // zero terminated
|
||
|
|
||
|
ATLASSERT(nNewLength <= GetData()->nAllocLength);
|
||
|
GetData()->SetDataLength( nNewLength );
|
||
|
m_pchData[nNewLength] = '\0';
|
||
|
|
||
|
#ifdef VERIFY_LOCAL_BSTR
|
||
|
VerifyInitData();
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
inline LPTSTR CString::GetBufferSetLength(int nNewLength)
|
||
|
{
|
||
|
ATLASSERT(nNewLength >= 0);
|
||
|
|
||
|
GetBuffer(nNewLength);
|
||
|
|
||
|
if ( GetAllocLength() >= nNewLength )
|
||
|
{
|
||
|
GetData()->SetDataLength( nNewLength );
|
||
|
m_pchData[nNewLength] = '\0';
|
||
|
}
|
||
|
|
||
|
#ifdef VERIFY_LOCAL_BSTR
|
||
|
VerifyInitData();
|
||
|
#endif
|
||
|
|
||
|
return m_pchData;
|
||
|
}
|
||
|
|
||
|
inline void CString::FreeExtra()
|
||
|
{
|
||
|
ATLASSERT(GetData()->nDataLength <= GetData()->nAllocLength);
|
||
|
if (GetData()->nDataLength != GetData()->nAllocLength)
|
||
|
{
|
||
|
CStringData* pOldData = GetData();
|
||
|
if(AllocBuffer(GetData()->nDataLength))
|
||
|
{
|
||
|
memcpy(m_pchData, pOldData->data(), pOldData->nDataLength * sizeof(TCHAR));
|
||
|
ATLASSERT(m_pchData[GetData()->nDataLength] == '\0');
|
||
|
CString::Release(pOldData);
|
||
|
}
|
||
|
}
|
||
|
ATLASSERT(GetData() != NULL);
|
||
|
}
|
||
|
|
||
|
inline LPTSTR CString::LockBuffer()
|
||
|
{
|
||
|
LPTSTR lpsz = GetBuffer(0);
|
||
|
GetData()->nRefs = -1;
|
||
|
return lpsz;
|
||
|
}
|
||
|
|
||
|
inline void CString::UnlockBuffer()
|
||
|
{
|
||
|
ATLASSERT(GetData()->nRefs == -1);
|
||
|
if (GetData() != _atltmpDataNil)
|
||
|
GetData()->nRefs = 1;
|
||
|
}
|
||
|
|
||
|
inline int CString::Find(TCHAR ch) const
|
||
|
{
|
||
|
// find first single character
|
||
|
LPTSTR lpsz = _cstrchr(m_pchData, (_TUCHAR)ch);
|
||
|
|
||
|
// return -1 if not found and index otherwise
|
||
|
return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData);
|
||
|
}
|
||
|
|
||
|
inline int CString::FindOneOf(LPCTSTR lpszCharSet) const
|
||
|
{
|
||
|
ATLASSERT(_IsValidString(lpszCharSet, FALSE));
|
||
|
LPTSTR lpsz = _cstrpbrk(m_pchData, lpszCharSet);
|
||
|
return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData);
|
||
|
}
|
||
|
|
||
|
inline void CString::MakeUpper()
|
||
|
{
|
||
|
CopyBeforeWrite();
|
||
|
CharUpper(m_pchData);
|
||
|
}
|
||
|
|
||
|
inline void CString::MakeLower()
|
||
|
{
|
||
|
CopyBeforeWrite();
|
||
|
CharLower(m_pchData);
|
||
|
}
|
||
|
|
||
|
inline void CString::MakeReverse()
|
||
|
{
|
||
|
CopyBeforeWrite();
|
||
|
_cstrrev(m_pchData);
|
||
|
}
|
||
|
|
||
|
inline void CString::SetAt(int nIndex, TCHAR ch)
|
||
|
{
|
||
|
ATLASSERT(nIndex >= 0);
|
||
|
ATLASSERT(nIndex < GetData()->nDataLength);
|
||
|
|
||
|
CopyBeforeWrite();
|
||
|
m_pchData[nIndex] = ch;
|
||
|
}
|
||
|
|
||
|
#ifndef _UNICODE
|
||
|
inline void CString::AnsiToOem()
|
||
|
{
|
||
|
CopyBeforeWrite();
|
||
|
::AnsiToOem(m_pchData, m_pchData);
|
||
|
}
|
||
|
inline void CString::OemToAnsi()
|
||
|
{
|
||
|
CopyBeforeWrite();
|
||
|
::OemToAnsi(m_pchData, m_pchData);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
inline CString::CString(TCHAR ch, int nLength)
|
||
|
{
|
||
|
ATLASSERT(!_istlead(ch)); // can't create a lead byte string
|
||
|
Init();
|
||
|
if (nLength >= 1)
|
||
|
{
|
||
|
if(AllocBuffer(nLength))
|
||
|
{
|
||
|
#ifdef _UNICODE
|
||
|
for (int i = 0; i < nLength; i++)
|
||
|
m_pchData[i] = ch;
|
||
|
#else
|
||
|
memset(m_pchData, ch, nLength);
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
inline CString::CString(LPCTSTR lpch, int nLength)
|
||
|
{
|
||
|
Init();
|
||
|
if (nLength != 0)
|
||
|
{
|
||
|
if(AllocBuffer(nLength))
|
||
|
memcpy(m_pchData, lpch, nLength * sizeof(TCHAR));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
inline const CString& CString::operator=(TCHAR ch)
|
||
|
{
|
||
|
ATLASSERT(!_istlead(ch)); // can't set single lead byte
|
||
|
AssignCopy(1, &ch);
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
inline CString __stdcall operator+(const CString& string1, TCHAR ch)
|
||
|
{
|
||
|
CString s;
|
||
|
s.ConcatCopy(string1.GetData()->nDataLength, string1.m_pchData, 1, &ch);
|
||
|
return s;
|
||
|
}
|
||
|
|
||
|
inline CString __stdcall operator+(TCHAR ch, const CString& string)
|
||
|
{
|
||
|
CString s;
|
||
|
s.ConcatCopy(1, &ch, string.GetData()->nDataLength, string.m_pchData);
|
||
|
return s;
|
||
|
}
|
||
|
|
||
|
inline CString CString::Mid(int nFirst) const
|
||
|
{
|
||
|
return Mid(nFirst, GetData()->nDataLength - nFirst);
|
||
|
}
|
||
|
|
||
|
inline CString CString::Mid(int nFirst, int nCount) const
|
||
|
{
|
||
|
// out-of-bounds requests return sensible things
|
||
|
if (nFirst < 0)
|
||
|
nFirst = 0;
|
||
|
if (nCount < 0)
|
||
|
nCount = 0;
|
||
|
|
||
|
if (nFirst + nCount > GetData()->nDataLength)
|
||
|
nCount = GetData()->nDataLength - nFirst;
|
||
|
if (nFirst > GetData()->nDataLength)
|
||
|
nCount = 0;
|
||
|
|
||
|
CString dest;
|
||
|
AllocCopy(dest, nCount, nFirst, 0);
|
||
|
return dest;
|
||
|
}
|
||
|
|
||
|
inline CString CString::Right(int nCount) const
|
||
|
{
|
||
|
if (nCount < 0)
|
||
|
nCount = 0;
|
||
|
else if (nCount > GetData()->nDataLength)
|
||
|
nCount = GetData()->nDataLength;
|
||
|
|
||
|
CString dest;
|
||
|
AllocCopy(dest, nCount, GetData()->nDataLength-nCount, 0);
|
||
|
return dest;
|
||
|
}
|
||
|
|
||
|
inline CString CString::Left(int nCount) const
|
||
|
{
|
||
|
if (nCount < 0)
|
||
|
nCount = 0;
|
||
|
else if (nCount > GetData()->nDataLength)
|
||
|
nCount = GetData()->nDataLength;
|
||
|
|
||
|
CString dest;
|
||
|
AllocCopy(dest, nCount, 0, 0);
|
||
|
return dest;
|
||
|
}
|
||
|
|
||
|
// strspn equivalent
|
||
|
inline CString CString::SpanIncluding(LPCTSTR lpszCharSet) const
|
||
|
{
|
||
|
ATLASSERT(_IsValidString(lpszCharSet, FALSE));
|
||
|
return Left(_cstrspn(m_pchData, lpszCharSet));
|
||
|
}
|
||
|
|
||
|
// strcspn equivalent
|
||
|
inline CString CString::SpanExcluding(LPCTSTR lpszCharSet) const
|
||
|
{
|
||
|
ATLASSERT(_IsValidString(lpszCharSet, FALSE));
|
||
|
return Left(_cstrcspn(m_pchData, lpszCharSet));
|
||
|
}
|
||
|
|
||
|
inline int CString::ReverseFind(TCHAR ch) const
|
||
|
{
|
||
|
// find last single character
|
||
|
LPTSTR lpsz = _cstrrchr(m_pchData, (_TUCHAR)ch);
|
||
|
|
||
|
// return -1 if not found, distance from beginning otherwise
|
||
|
return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData);
|
||
|
}
|
||
|
|
||
|
// find a sub-string (like strstr)
|
||
|
inline int CString::Find(LPCTSTR lpszSub) const
|
||
|
{
|
||
|
ATLASSERT(_IsValidString(lpszSub, FALSE));
|
||
|
|
||
|
// find first matching substring
|
||
|
LPTSTR lpsz = _cstrstr(m_pchData, lpszSub);
|
||
|
|
||
|
// return -1 for not found, distance from beginning otherwise
|
||
|
return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData);
|
||
|
}
|
||
|
|
||
|
inline void CString::FormatV(LPCTSTR lpszFormat, va_list argList)
|
||
|
{
|
||
|
ATLASSERT(_IsValidString(lpszFormat, FALSE));
|
||
|
|
||
|
enum _FormatModifiers
|
||
|
{
|
||
|
FORCE_ANSI = 0x10000,
|
||
|
FORCE_UNICODE = 0x20000,
|
||
|
FORCE_INT64 = 0x40000
|
||
|
};
|
||
|
|
||
|
va_list argListSave = argList;
|
||
|
|
||
|
// make a guess at the maximum length of the resulting string
|
||
|
int nMaxLen = 0;
|
||
|
for (LPCTSTR lpsz = lpszFormat; *lpsz != '\0'; lpsz = ::CharNext(lpsz))
|
||
|
{
|
||
|
// handle '%' character, but watch out for '%%'
|
||
|
if (*lpsz != '%' || *(lpsz = ::CharNext(lpsz)) == '%')
|
||
|
{
|
||
|
nMaxLen += (int)lstrlen(lpsz);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
int nItemLen = 0;
|
||
|
|
||
|
// handle '%' character with format
|
||
|
int nWidth = 0;
|
||
|
for (; *lpsz != '\0'; lpsz = ::CharNext(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' && _cstrisdigit(*lpsz); lpsz = ::CharNext(lpsz))
|
||
|
;
|
||
|
}
|
||
|
ATLASSERT(nWidth >= 0);
|
||
|
|
||
|
int nPrecision = 0;
|
||
|
if (*lpsz == '.')
|
||
|
{
|
||
|
// skip past '.' separator (width.precision)
|
||
|
lpsz = ::CharNext(lpsz);
|
||
|
|
||
|
// get precision and skip it
|
||
|
if (*lpsz == '*')
|
||
|
{
|
||
|
nPrecision = va_arg(argList, int);
|
||
|
lpsz = ::CharNext(lpsz);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
nPrecision = _ttoi(lpsz);
|
||
|
for (; *lpsz != '\0' && _cstrisdigit(*lpsz); lpsz = ::CharNext(lpsz))
|
||
|
;
|
||
|
}
|
||
|
ATLASSERT(nPrecision >= 0);
|
||
|
}
|
||
|
|
||
|
// should be on type modifier or specifier
|
||
|
int nModifier = 0;
|
||
|
if(lpsz[0] == _T('I') && lpsz[1] == _T('6') && lpsz[2] == _T('4'))
|
||
|
{
|
||
|
lpsz += 3;
|
||
|
nModifier = FORCE_INT64;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
switch (*lpsz)
|
||
|
{
|
||
|
// modifiers that affect size
|
||
|
case 'h':
|
||
|
nModifier = FORCE_ANSI;
|
||
|
lpsz = ::CharNext(lpsz);
|
||
|
break;
|
||
|
case 'l':
|
||
|
nModifier = FORCE_UNICODE;
|
||
|
lpsz = ::CharNext(lpsz);
|
||
|
break;
|
||
|
|
||
|
// modifiers that do not affect size
|
||
|
case 'F':
|
||
|
case 'N':
|
||
|
case 'L':
|
||
|
lpsz = ::CharNext(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':
|
||
|
{
|
||
|
LPCTSTR pstrNextArg = va_arg(argList, LPCTSTR);
|
||
|
if (pstrNextArg == NULL)
|
||
|
{
|
||
|
nItemLen = 6; // "(null)"
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
nItemLen = lstrlen(pstrNextArg);
|
||
|
nItemLen = max(1, nItemLen);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case 'S':
|
||
|
{
|
||
|
#ifndef _UNICODE
|
||
|
LPWSTR pstrNextArg = va_arg(argList, LPWSTR);
|
||
|
if (pstrNextArg == NULL)
|
||
|
{
|
||
|
nItemLen = 6; // "(null)"
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
nItemLen = (int)wcslen(pstrNextArg);
|
||
|
nItemLen = max(1, nItemLen);
|
||
|
}
|
||
|
#else
|
||
|
LPCSTR pstrNextArg = va_arg(argList, LPCSTR);
|
||
|
if (pstrNextArg == NULL)
|
||
|
{
|
||
|
nItemLen = 6; // "(null)"
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
nItemLen = lstrlenA(pstrNextArg);
|
||
|
nItemLen = max(1, nItemLen);
|
||
|
}
|
||
|
#endif
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case 's' | FORCE_ANSI:
|
||
|
case 'S' | FORCE_ANSI:
|
||
|
{
|
||
|
LPCSTR pstrNextArg = va_arg(argList, LPCSTR);
|
||
|
if (pstrNextArg == NULL)
|
||
|
{
|
||
|
nItemLen = 6; // "(null)"
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
nItemLen = lstrlenA(pstrNextArg);
|
||
|
nItemLen = max(1, nItemLen);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case 's' | FORCE_UNICODE:
|
||
|
case 'S' | FORCE_UNICODE:
|
||
|
{
|
||
|
LPWSTR pstrNextArg = va_arg(argList, LPWSTR);
|
||
|
if (pstrNextArg == NULL)
|
||
|
{
|
||
|
nItemLen = 6; // "(null)"
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
nItemLen = (int)wcslen(pstrNextArg);
|
||
|
nItemLen = max(1, nItemLen);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// adjust nItemLen for strings
|
||
|
if (nItemLen != 0)
|
||
|
{
|
||
|
nItemLen = max(nItemLen, nWidth);
|
||
|
if (nPrecision != 0)
|
||
|
nItemLen = min(nItemLen, nPrecision);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
switch (*lpsz)
|
||
|
{
|
||
|
// integers
|
||
|
case 'd':
|
||
|
case 'i':
|
||
|
case 'u':
|
||
|
case 'x':
|
||
|
case 'X':
|
||
|
case 'o':
|
||
|
if (nModifier & FORCE_INT64)
|
||
|
va_arg(argList, __int64);
|
||
|
else
|
||
|
va_arg(argList, int);
|
||
|
nItemLen = 32;
|
||
|
nItemLen = max(nItemLen, nWidth + nPrecision);
|
||
|
break;
|
||
|
|
||
|
#ifndef _ATL_USE_CSTRING_FLOAT
|
||
|
case 'e':
|
||
|
case 'f':
|
||
|
case 'g':
|
||
|
case 'G':
|
||
|
ATLASSERT(!"Floating point (%%e, %%f, %%g, and %%G) is not supported by the WTL::CString class.");
|
||
|
#ifndef _DEBUG
|
||
|
::OutputDebugString(_T("Floating point (%%e, %%f, %%g, and %%G) is not supported by the WTL::CString class."));
|
||
|
::DebugBreak();
|
||
|
#endif //!_DEBUG
|
||
|
break;
|
||
|
#else //_ATL_USE_CSTRING_FLOAT
|
||
|
case 'e':
|
||
|
case 'g':
|
||
|
case 'G':
|
||
|
va_arg(argList, double);
|
||
|
nItemLen = 128;
|
||
|
nItemLen = max(nItemLen, nWidth + nPrecision);
|
||
|
break;
|
||
|
case 'f':
|
||
|
{
|
||
|
double f;
|
||
|
LPTSTR pszTemp;
|
||
|
|
||
|
// 312 == strlen("-1+(309 zeroes).")
|
||
|
// 309 zeroes == max precision of a double
|
||
|
// 6 == adjustment in case precision is not specified,
|
||
|
// which means that the precision defaults to 6
|
||
|
pszTemp = (LPTSTR)_alloca(max(nWidth, 312 + nPrecision + 6));
|
||
|
|
||
|
f = va_arg(argList, double);
|
||
|
_stprintf(pszTemp, _T( "%*.*f" ), nWidth, nPrecision + 6, f);
|
||
|
nItemLen = _tcslen(pszTemp);
|
||
|
}
|
||
|
break;
|
||
|
#endif //_ATL_USE_CSTRING_FLOAT
|
||
|
|
||
|
case 'p':
|
||
|
va_arg(argList, void*);
|
||
|
nItemLen = 32;
|
||
|
nItemLen = max(nItemLen, nWidth + nPrecision);
|
||
|
break;
|
||
|
|
||
|
// no output
|
||
|
case 'n':
|
||
|
va_arg(argList, int*);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
ATLASSERT(FALSE); // unknown formatting option
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// adjust nMaxLen for output nItemLen
|
||
|
nMaxLen += nItemLen;
|
||
|
}
|
||
|
|
||
|
GetBuffer(nMaxLen);
|
||
|
#ifndef _ATL_USE_CSTRING_FLOAT
|
||
|
int nRet = wvsprintf(m_pchData, lpszFormat, argListSave);
|
||
|
#else //_ATL_USE_CSTRING_FLOAT
|
||
|
int nRet = _vstprintf(m_pchData, lpszFormat, argListSave);
|
||
|
#endif //_ATL_USE_CSTRING_FLOAT
|
||
|
nRet; // ref
|
||
|
ATLASSERT(nRet <= GetAllocLength());
|
||
|
ReleaseBuffer();
|
||
|
|
||
|
va_end(argListSave);
|
||
|
}
|
||
|
|
||
|
// formatting (using wsprintf style formatting)
|
||
|
inline void __cdecl CString::Format(LPCTSTR lpszFormat, ...)
|
||
|
{
|
||
|
ATLASSERT(_IsValidString(lpszFormat, FALSE));
|
||
|
|
||
|
va_list argList;
|
||
|
va_start(argList, lpszFormat);
|
||
|
FormatV(lpszFormat, argList);
|
||
|
va_end(argList);
|
||
|
}
|
||
|
|
||
|
inline void __cdecl CString::Format(UINT nFormatID, ...)
|
||
|
{
|
||
|
CString strFormat;
|
||
|
BOOL bRet = strFormat.LoadString(nFormatID);
|
||
|
bRet; // ref
|
||
|
ATLASSERT(bRet != 0);
|
||
|
|
||
|
va_list argList;
|
||
|
va_start(argList, nFormatID);
|
||
|
FormatV(strFormat, argList);
|
||
|
va_end(argList);
|
||
|
}
|
||
|
|
||
|
// formatting (using FormatMessage style formatting)
|
||
|
inline BOOL __cdecl CString::FormatMessage(LPCTSTR lpszFormat, ...)
|
||
|
{
|
||
|
// format message into temporary buffer lpszTemp
|
||
|
va_list argList;
|
||
|
va_start(argList, lpszFormat);
|
||
|
LPTSTR lpszTemp;
|
||
|
BOOL bRet = TRUE;
|
||
|
|
||
|
if (::FormatMessage(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER,
|
||
|
lpszFormat, 0, 0, (LPTSTR)&lpszTemp, 0, &argList) == 0 || lpszTemp == NULL)
|
||
|
bRet = FALSE;
|
||
|
|
||
|
// assign lpszTemp into the resulting string and free the temporary
|
||
|
*this = lpszTemp;
|
||
|
LocalFree(lpszTemp);
|
||
|
va_end(argList);
|
||
|
return bRet;
|
||
|
}
|
||
|
|
||
|
inline BOOL __cdecl CString::FormatMessage(UINT nFormatID, ...)
|
||
|
{
|
||
|
// get format string from string table
|
||
|
CString strFormat;
|
||
|
BOOL bRetTmp = strFormat.LoadString(nFormatID);
|
||
|
bRetTmp; // ref
|
||
|
ATLASSERT(bRetTmp != 0);
|
||
|
|
||
|
// format message into temporary buffer lpszTemp
|
||
|
va_list argList;
|
||
|
va_start(argList, nFormatID);
|
||
|
LPTSTR lpszTemp;
|
||
|
BOOL bRet = TRUE;
|
||
|
|
||
|
if (::FormatMessage(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER,
|
||
|
strFormat, 0, 0, (LPTSTR)&lpszTemp, 0, &argList) == 0 || lpszTemp == NULL)
|
||
|
bRet = FALSE;
|
||
|
|
||
|
// assign lpszTemp into the resulting string and free lpszTemp
|
||
|
*this = lpszTemp;
|
||
|
LocalFree(lpszTemp);
|
||
|
va_end(argList);
|
||
|
return bRet;
|
||
|
}
|
||
|
|
||
|
inline void CString::TrimRight()
|
||
|
{
|
||
|
CopyBeforeWrite();
|
||
|
|
||
|
// find beginning of trailing spaces by starting at beginning (DBCS aware)
|
||
|
LPTSTR lpsz = m_pchData;
|
||
|
LPTSTR lpszLast = NULL;
|
||
|
while (*lpsz != '\0')
|
||
|
{
|
||
|
if (_cstrisspace(*lpsz))
|
||
|
{
|
||
|
if (lpszLast == NULL)
|
||
|
lpszLast = lpsz;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
lpszLast = NULL;
|
||
|
}
|
||
|
lpsz = ::CharNext(lpsz);
|
||
|
}
|
||
|
|
||
|
if (lpszLast != NULL)
|
||
|
{
|
||
|
// truncate at trailing space start
|
||
|
*lpszLast = '\0';
|
||
|
GetData()->SetDataLength( (int)(DWORD_PTR)(lpszLast - m_pchData) );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
inline void CString::TrimLeft()
|
||
|
{
|
||
|
CopyBeforeWrite();
|
||
|
|
||
|
// find first non-space character
|
||
|
LPCTSTR lpsz = m_pchData;
|
||
|
while (_cstrisspace(*lpsz))
|
||
|
lpsz = ::CharNext(lpsz);
|
||
|
|
||
|
// fix up data and length
|
||
|
int nDataLength = GetData()->nDataLength - (int)(DWORD_PTR)(lpsz - m_pchData);
|
||
|
memmove(m_pchData, lpsz, (nDataLength + 1) * sizeof(TCHAR));
|
||
|
GetData()->SetDataLength( nDataLength );
|
||
|
}
|
||
|
|
||
|
inline int CString::Delete(int nIndex, int nCount /* = 1 */)
|
||
|
{
|
||
|
if (nIndex < 0)
|
||
|
nIndex = 0;
|
||
|
int nNewLength = GetData()->nDataLength;
|
||
|
if (nCount > 0 && nIndex < nNewLength)
|
||
|
{
|
||
|
CopyBeforeWrite();
|
||
|
int nBytesToCopy = nNewLength - (nIndex + nCount) + 1;
|
||
|
|
||
|
memmove(m_pchData + nIndex, m_pchData + nIndex + nCount, nBytesToCopy * sizeof(TCHAR));
|
||
|
GetData()->SetDataLength( nNewLength - nCount );
|
||
|
}
|
||
|
|
||
|
return nNewLength;
|
||
|
}
|
||
|
|
||
|
inline int CString::Insert(int nIndex, TCHAR ch)
|
||
|
{
|
||
|
CopyBeforeWrite();
|
||
|
|
||
|
if (nIndex < 0)
|
||
|
nIndex = 0;
|
||
|
|
||
|
int nNewLength = GetData()->nDataLength;
|
||
|
if (nIndex > nNewLength)
|
||
|
nIndex = nNewLength;
|
||
|
nNewLength++;
|
||
|
|
||
|
if (GetData()->nAllocLength < nNewLength)
|
||
|
{
|
||
|
CStringData* pOldData = GetData();
|
||
|
LPTSTR pstr = m_pchData;
|
||
|
if(!AllocBuffer(nNewLength))
|
||
|
return -1;
|
||
|
memcpy(m_pchData, pstr, (pOldData->nDataLength + 1) * sizeof(TCHAR));
|
||
|
CString::Release(pOldData);
|
||
|
}
|
||
|
|
||
|
// move existing bytes down
|
||
|
memmove(m_pchData + nIndex + 1, m_pchData + nIndex, (nNewLength - nIndex) * sizeof(TCHAR));
|
||
|
m_pchData[nIndex] = ch;
|
||
|
GetData()->SetDataLength( nNewLength );
|
||
|
|
||
|
return nNewLength;
|
||
|
}
|
||
|
|
||
|
inline int CString::Insert(int nIndex, LPCTSTR pstr)
|
||
|
{
|
||
|
if (nIndex < 0)
|
||
|
nIndex = 0;
|
||
|
|
||
|
int nInsertLength = SafeStrlen(pstr);
|
||
|
int nNewLength = GetData()->nDataLength;
|
||
|
if (nInsertLength > 0)
|
||
|
{
|
||
|
CopyBeforeWrite();
|
||
|
if (nIndex > nNewLength)
|
||
|
nIndex = nNewLength;
|
||
|
nNewLength += nInsertLength;
|
||
|
|
||
|
if (GetData()->nAllocLength < nNewLength)
|
||
|
{
|
||
|
CStringData* pOldData = GetData();
|
||
|
LPTSTR pstr = m_pchData;
|
||
|
if(!AllocBuffer(nNewLength))
|
||
|
return -1;
|
||
|
memcpy(m_pchData, pstr, (pOldData->nDataLength + 1) * sizeof(TCHAR));
|
||
|
CString::Release(pOldData);
|
||
|
}
|
||
|
|
||
|
// move existing bytes down
|
||
|
memmove(m_pchData + nIndex + nInsertLength, m_pchData + nIndex, (nNewLength - nIndex - nInsertLength + 1) * sizeof(TCHAR));
|
||
|
memcpy(m_pchData + nIndex, pstr, nInsertLength * sizeof(TCHAR));
|
||
|
GetData()->SetDataLength( nNewLength );
|
||
|
}
|
||
|
|
||
|
return nNewLength;
|
||
|
}
|
||
|
|
||
|
inline int CString::Replace(TCHAR chOld, TCHAR chNew)
|
||
|
{
|
||
|
int nCount = 0;
|
||
|
|
||
|
// short-circuit the nop case
|
||
|
if (chOld != chNew)
|
||
|
{
|
||
|
// otherwise modify each character that matches in the string
|
||
|
CopyBeforeWrite();
|
||
|
LPTSTR psz = m_pchData;
|
||
|
LPTSTR pszEnd = psz + GetData()->nDataLength;
|
||
|
while (psz < pszEnd)
|
||
|
{
|
||
|
// replace instances of the specified character only
|
||
|
if (*psz == chOld)
|
||
|
{
|
||
|
*psz = chNew;
|
||
|
nCount++;
|
||
|
}
|
||
|
psz = ::CharNext(psz);
|
||
|
}
|
||
|
}
|
||
|
return nCount;
|
||
|
}
|
||
|
|
||
|
inline int CString::Replace(LPCTSTR lpszOld, LPCTSTR lpszNew)
|
||
|
{
|
||
|
// can't have empty or NULL lpszOld
|
||
|
|
||
|
int nSourceLen = SafeStrlen(lpszOld);
|
||
|
if (nSourceLen == 0)
|
||
|
return 0;
|
||
|
int nReplacementLen = SafeStrlen(lpszNew);
|
||
|
|
||
|
// loop once to figure out the size of the result string
|
||
|
int nCount = 0;
|
||
|
LPTSTR lpszStart = m_pchData;
|
||
|
LPTSTR lpszEnd = m_pchData + GetData()->nDataLength;
|
||
|
LPTSTR lpszTarget;
|
||
|
while (lpszStart < lpszEnd)
|
||
|
{
|
||
|
while ((lpszTarget = _cstrstr(lpszStart, lpszOld)) != NULL)
|
||
|
{
|
||
|
nCount++;
|
||
|
lpszStart = lpszTarget + nSourceLen;
|
||
|
}
|
||
|
lpszStart += lstrlen(lpszStart) + 1;
|
||
|
}
|
||
|
|
||
|
// if any changes were made, make them
|
||
|
if (nCount > 0)
|
||
|
{
|
||
|
CopyBeforeWrite();
|
||
|
|
||
|
// if the buffer is too small, just
|
||
|
// allocate a new buffer (slow but sure)
|
||
|
int nOldLength = GetData()->nDataLength;
|
||
|
int nNewLength = nOldLength + (nReplacementLen - nSourceLen) * nCount;
|
||
|
if (GetData()->nAllocLength < nNewLength || GetData()->nRefs > 1)
|
||
|
{
|
||
|
CStringData* pOldData = GetData();
|
||
|
LPTSTR pstr = m_pchData;
|
||
|
if(!AllocBuffer(nNewLength))
|
||
|
return -1;
|
||
|
memcpy(m_pchData, pstr, pOldData->nDataLength * sizeof(TCHAR));
|
||
|
CString::Release(pOldData);
|
||
|
}
|
||
|
// else, we just do it in-place
|
||
|
lpszStart = m_pchData;
|
||
|
lpszEnd = m_pchData + GetData()->nDataLength;
|
||
|
|
||
|
// loop again to actually do the work
|
||
|
while (lpszStart < lpszEnd)
|
||
|
{
|
||
|
while ( (lpszTarget = _cstrstr(lpszStart, lpszOld)) != NULL)
|
||
|
{
|
||
|
int nBalance = nOldLength - ((int)(DWORD_PTR)(lpszTarget - m_pchData) + nSourceLen);
|
||
|
memmove(lpszTarget + nReplacementLen, lpszTarget + nSourceLen, nBalance * sizeof(TCHAR));
|
||
|
memcpy(lpszTarget, lpszNew, nReplacementLen * sizeof(TCHAR));
|
||
|
lpszStart = lpszTarget + nReplacementLen;
|
||
|
lpszStart[nBalance] = '\0';
|
||
|
nOldLength += (nReplacementLen - nSourceLen);
|
||
|
}
|
||
|
lpszStart += lstrlen(lpszStart) + 1;
|
||
|
}
|
||
|
ATLASSERT(m_pchData[nNewLength] == '\0');
|
||
|
GetData()->SetDataLength( nNewLength );
|
||
|
}
|
||
|
|
||
|
return nCount;
|
||
|
}
|
||
|
|
||
|
inline int CString::Remove(TCHAR chRemove)
|
||
|
{
|
||
|
CopyBeforeWrite();
|
||
|
|
||
|
LPTSTR pstrSource = m_pchData;
|
||
|
LPTSTR pstrDest = m_pchData;
|
||
|
LPTSTR pstrEnd = m_pchData + GetData()->nDataLength;
|
||
|
|
||
|
while (pstrSource < pstrEnd)
|
||
|
{
|
||
|
if (*pstrSource != chRemove)
|
||
|
{
|
||
|
*pstrDest = *pstrSource;
|
||
|
pstrDest = ::CharNext(pstrDest);
|
||
|
}
|
||
|
pstrSource = ::CharNext(pstrSource);
|
||
|
}
|
||
|
*pstrDest = '\0';
|
||
|
int nCount = (int)(DWORD_PTR)(pstrSource - pstrDest);
|
||
|
GetData()->SetDataLength( GetData()->nDataLength - nCount );
|
||
|
|
||
|
return nCount;
|
||
|
}
|
||
|
|
||
|
#ifdef _UNICODE
|
||
|
#define CHAR_FUDGE 1 // one TCHAR unused is good enough
|
||
|
#else
|
||
|
#define CHAR_FUDGE 2 // two BYTES unused for case of DBC last char
|
||
|
#endif
|
||
|
|
||
|
inline BOOL CString::LoadString(UINT nID)
|
||
|
{
|
||
|
// try fixed buffer first (to avoid wasting space in the heap)
|
||
|
TCHAR szTemp[256];
|
||
|
int nCount = sizeof(szTemp) / sizeof(szTemp[0]);
|
||
|
int nLen = _LoadString(nID, szTemp, nCount);
|
||
|
if (nCount - nLen > CHAR_FUDGE)
|
||
|
{
|
||
|
*this = szTemp;
|
||
|
return nLen > 0;
|
||
|
}
|
||
|
|
||
|
// try buffer size of 512, then larger size until entire string is retrieved
|
||
|
int nSize = 256;
|
||
|
do
|
||
|
{
|
||
|
nSize += 256;
|
||
|
nLen = _LoadString(nID, GetBuffer(nSize - 1), nSize);
|
||
|
} while (nSize - nLen <= CHAR_FUDGE);
|
||
|
ReleaseBuffer();
|
||
|
|
||
|
return nLen > 0;
|
||
|
}
|
||
|
|
||
|
#ifndef _ATL_NO_COM
|
||
|
inline BSTR CString::AllocSysString() const
|
||
|
{
|
||
|
#if defined(_UNICODE) || defined(OLE2ANSI)
|
||
|
BSTR bstr = ::SysAllocStringLen(m_pchData, GetData()->nDataLength);
|
||
|
#else
|
||
|
int nLen = MultiByteToWideChar(CP_ACP, 0, m_pchData,
|
||
|
GetData()->nDataLength, NULL, NULL);
|
||
|
BSTR bstr = ::SysAllocStringLen(NULL, nLen);
|
||
|
if(bstr != NULL)
|
||
|
MultiByteToWideChar(CP_ACP, 0, m_pchData, GetData()->nDataLength, bstr, nLen);
|
||
|
#endif
|
||
|
return bstr;
|
||
|
}
|
||
|
|
||
|
inline BSTR CString::SetSysString(BSTR* pbstr) const
|
||
|
{
|
||
|
#if defined(_UNICODE) || defined(OLE2ANSI)
|
||
|
::SysReAllocStringLen(pbstr, m_pchData, GetData()->nDataLength);
|
||
|
#else
|
||
|
int nLen = MultiByteToWideChar(CP_ACP, 0, m_pchData,
|
||
|
GetData()->nDataLength, NULL, NULL);
|
||
|
if(::SysReAllocStringLen(pbstr, NULL, nLen))
|
||
|
MultiByteToWideChar(CP_ACP, 0, m_pchData, GetData()->nDataLength, *pbstr, nLen);
|
||
|
#endif
|
||
|
ATLASSERT(*pbstr != NULL);
|
||
|
return *pbstr;
|
||
|
}
|
||
|
#endif //!_ATL_NO_COM
|
||
|
|
||
|
#endif //!_WTL_NO_CSTRING
|
||
|
|
||
|
}; //namespace LBSTR
|
||
|
|
||
|
#endif // __LOCAL_BSTR__
|