Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2070 lines
49 KiB

// 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__