summaryrefslogtreecommitdiff
path: root/protocols/JabberG/MString.h
diff options
context:
space:
mode:
authorVadim Dashevskiy <watcherhd@gmail.com>2012-05-15 10:38:20 +0000
committerVadim Dashevskiy <watcherhd@gmail.com>2012-05-15 10:38:20 +0000
commit48540940b6c28bb4378abfeb500ec45a625b37b6 (patch)
tree2ef294c0763e802f91d868bdef4229b6868527de /protocols/JabberG/MString.h
parent5c350913f011e119127baeb32a6aedeb4f0d33bc (diff)
initial commit
git-svn-id: http://svn.miranda-ng.org/main/trunk@2 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c
Diffstat (limited to 'protocols/JabberG/MString.h')
-rw-r--r--protocols/JabberG/MString.h2300
1 files changed, 2300 insertions, 0 deletions
diff --git a/protocols/JabberG/MString.h b/protocols/JabberG/MString.h
new file mode 100644
index 0000000000..fbef22ddf5
--- /dev/null
+++ b/protocols/JabberG/MString.h
@@ -0,0 +1,2300 @@
+#pragma once
+
+#include <string.h>
+#include <mbstring.h>
+#include <wchar.h>
+
+#ifdef __MINGW32__
+#include <limits.h>
+
+__inline size_t strnlen(const char *string, size_t maxlen)
+{
+ const char *end = (const char *)memchr ((const void *)string, '\0', maxlen);
+ return end ? (size_t) (end - string) : maxlen;
+}
+__inline size_t wcsnlen(const wchar_t *string, size_t maxlen)
+{
+ const wchar_t *end = wmemchr (string, L'\0', maxlen);
+ return end ? (size_t) (end - string) : maxlen;
+}
+
+/* FIXME: This may be wrong assumption about _AtlGetConversionACP */
+#define _AtlGetConversionACP() CP_THREAD_ACP
+/* FIXME: This is unsafe */
+#define memcpy_s(dest,size,src,count) memcpy(dest,src,count)
+/* FIXME: This is quite silly implementation of _mbsstr */
+#define _mbsstr(str,search) strstr((const char *)str,(const char *)search)
+#define __max(x,y) (((x)<(y))?(y):(x))
+#endif /* __MINGW32__ */
+
+struct CMStringData
+{
+ int nDataLength; // Length of currently used data in XCHARs (not including terminating null)
+ int nAllocLength; // Length of allocated data in XCHARs (not including terminating null)
+ long nRefs; // Reference count: negative == locked
+ // XCHAR data[nAllocLength+1] // A CStringData is always followed in memory by the actual array of character data
+ void* data();
+ void AddRef();
+ bool IsLocked() const;
+ bool IsShared() const;
+ void Lock();
+ void Release();
+ void Unlock();
+};
+
+class CNilMStringData : public CMStringData
+{
+public:
+ CNilMStringData();
+
+public:
+ wchar_t achNil[2];
+};
+
+template< typename BaseType = char >
+class ChTraitsBase
+{
+public:
+ typedef char XCHAR;
+ typedef LPSTR PXSTR;
+ typedef LPCSTR PCXSTR;
+ typedef wchar_t YCHAR;
+ typedef LPWSTR PYSTR;
+ typedef LPCWSTR PCYSTR;
+};
+
+template<>
+class ChTraitsBase< wchar_t >
+{
+public:
+ typedef wchar_t XCHAR;
+ typedef LPWSTR PXSTR;
+ typedef LPCWSTR PCXSTR;
+ typedef char YCHAR;
+ typedef LPSTR PYSTR;
+ typedef LPCSTR PCYSTR;
+};
+
+class CMBaseString
+{
+public:
+ static CMStringData* Allocate(int nChars, int nCharSize);
+ static void Free(CMStringData* pData);
+ static CMStringData* Realloc(CMStringData* pData, int nChars, int nCharSize);
+
+protected:
+ static CMStringData* GetNilString();
+ static CNilMStringData m_nil;
+};
+
+template< typename BaseType >
+class CMSimpleStringT : public CMBaseString
+{
+public:
+ typedef typename ChTraitsBase< BaseType >::XCHAR XCHAR;
+ typedef typename ChTraitsBase< BaseType >::PXSTR PXSTR;
+ typedef typename ChTraitsBase< BaseType >::PCXSTR PCXSTR;
+ typedef typename ChTraitsBase< BaseType >::YCHAR YCHAR;
+ typedef typename ChTraitsBase< BaseType >::PYSTR PYSTR;
+ typedef typename ChTraitsBase< BaseType >::PCYSTR PCYSTR;
+
+public:
+ explicit CMSimpleStringT()
+ {
+ CMStringData* pData = GetNilString();
+ Attach(pData);
+ }
+
+ CMSimpleStringT(const CMSimpleStringT& strSrc)
+ {
+ CMStringData* pSrcData = strSrc.GetData();
+ CMStringData* pNewData = CloneData( pSrcData );
+ Attach( pNewData );
+ }
+
+ CMSimpleStringT(PCXSTR pszSrc)
+ {
+ int nLength = StringLength( pszSrc );
+ CMStringData* pData = Allocate( nLength, sizeof( XCHAR ) );
+ if (pData != NULL)
+ {
+ Attach( pData );
+ SetLength( nLength );
+ CopyChars( m_pszData, nLength, pszSrc, nLength );
+ }
+ }
+ CMSimpleStringT(const XCHAR* pchSrc, int nLength)
+ {
+ CMStringData* pData = Allocate( nLength, sizeof( XCHAR ) );
+ if( pData != NULL )
+ {
+ Attach( pData );
+ SetLength( nLength );
+ CopyChars( m_pszData, nLength, pchSrc, nLength );
+ }
+ }
+ ~CMSimpleStringT()
+ {
+ CMStringData* pData = GetData();
+ pData->Release();
+ }
+
+ operator CMSimpleStringT<BaseType>&()
+ {
+ return *(CMSimpleStringT<BaseType>*)this;
+ }
+
+ CMSimpleStringT& operator=(const CMSimpleStringT& strSrc )
+ {
+ CMStringData* pSrcData = strSrc.GetData();
+ CMStringData* pOldData = GetData();
+ if( pSrcData != pOldData)
+ {
+ if( pOldData->IsLocked() )
+ SetString( strSrc.GetString(), strSrc.GetLength() );
+ else
+ {
+ CMStringData* pNewData = CloneData( pSrcData );
+ pOldData->Release();
+ Attach( pNewData );
+ }
+ }
+
+ return *this;
+ }
+
+ CMSimpleStringT& operator=(PCXSTR pszSrc)
+ {
+ SetString( pszSrc );
+ return *this;
+ }
+
+ CMSimpleStringT& operator+=( const CMSimpleStringT& strSrc )
+ {
+ Append( strSrc );
+
+ return *this;
+ }
+
+ CMSimpleStringT& operator+=( PCXSTR pszSrc )
+ {
+ Append( pszSrc );
+
+ return *this;
+ }
+ CMSimpleStringT& operator+=( char ch )
+ {
+ AppendChar(XCHAR(ch));
+
+ return *this;
+ }
+ CMSimpleStringT& operator+=( unsigned char ch )
+ {
+ AppendChar(XCHAR(ch));
+
+ return *this;
+ }
+ CMSimpleStringT& operator+=( wchar_t ch )
+ {
+ AppendChar(XCHAR(ch));
+
+ return *this;
+ }
+
+ XCHAR operator[]( int iChar ) const
+ {
+ return m_pszData[iChar];
+ }
+
+ operator PCXSTR() const
+ {
+ return m_pszData;
+ }
+
+ PCXSTR c_str() const
+ {
+ return m_pszData;
+ }
+
+ void Append( PCXSTR pszSrc )
+ {
+ Append( pszSrc, StringLength( pszSrc ) );
+ }
+ void Append( PCXSTR pszSrc, int nLength )
+ {
+ // See comment in SetString() about why we do this
+ UINT_PTR nOffset = pszSrc - GetString();
+
+ UINT nOldLength = GetLength();
+ if (nOldLength < 0)
+ {
+ // protects from underflow
+ nOldLength = 0;
+ }
+
+ //Make sure we don't read pass end of the terminating NULL
+ int nSrcLength = StringLength(pszSrc);
+ nLength = nLength > nSrcLength ? nSrcLength: nLength;
+
+ int nNewLength = nOldLength+nLength;
+ PXSTR pszBuffer = GetBuffer( nNewLength );
+ if( nOffset <= nOldLength )
+ {
+ pszSrc = pszBuffer+nOffset;
+ // No need to call CopyCharsOverlapped, since the destination is
+ // beyond the end of the original buffer
+ }
+ CopyChars( pszBuffer+nOldLength, nLength, pszSrc, nLength );
+ ReleaseBufferSetLength( nNewLength );
+ }
+ void AppendChar( XCHAR ch )
+ {
+ UINT nOldLength = GetLength();
+ int nNewLength = nOldLength+1;
+ PXSTR pszBuffer = GetBuffer( nNewLength );
+ pszBuffer[nOldLength] = ch;
+ ReleaseBufferSetLength( nNewLength );
+ }
+ void Append( const CMSimpleStringT& strSrc )
+ {
+ Append( strSrc.GetString(), strSrc.GetLength() );
+ }
+ void Empty()
+ {
+ CMStringData* pOldData = GetData();
+ if( pOldData->nDataLength == 0 )
+ return;
+
+ if( pOldData->IsLocked() )
+ {
+ // Don't reallocate a locked buffer that's shrinking
+ SetLength( 0 );
+ }
+ else
+ {
+ pOldData->Release();
+ CMStringData* pNewData = GetNilString();
+ Attach( pNewData );
+ }
+ }
+ void FreeExtra()
+ {
+ CMStringData* pOldData = GetData();
+ int nLength = pOldData->nDataLength;
+ if( pOldData->nAllocLength == nLength )
+ return;
+
+ if( !pOldData->IsLocked() ) // Don't reallocate a locked buffer that's shrinking
+ {
+ CMStringData* pNewData = Allocate( nLength, sizeof( XCHAR ) );
+ if( pNewData == NULL ) {
+ SetLength( nLength );
+ return;
+ }
+
+ CopyChars( PXSTR( pNewData->data() ), nLength, PCXSTR( pOldData->data() ), nLength );
+
+ pOldData->Release();
+ Attach( pNewData );
+ SetLength( nLength );
+ }
+ }
+
+ int GetAllocLength() const
+ {
+ return GetData()->nAllocLength;
+ }
+ XCHAR GetAt( int iChar ) const
+ {
+ return m_pszData[iChar];
+ }
+ PXSTR GetBuffer()
+ {
+ CMStringData* pData = GetData();
+ if( pData->IsShared() )
+ Fork( pData->nDataLength );
+
+ return m_pszData;
+ }
+ PXSTR GetBuffer( int nMinBufferLength )
+ {
+ return PrepareWrite( nMinBufferLength );
+ }
+ PXSTR GetBufferSetLength( int nLength )
+ {
+ PXSTR pszBuffer = GetBuffer( nLength );
+ SetLength( nLength );
+
+ return pszBuffer;
+ }
+ int GetLength() const
+ {
+ return GetData()->nDataLength;
+ }
+
+ PCXSTR GetString() const
+ {
+ return m_pszData;
+ }
+ bool IsEmpty() const
+ {
+ return GetLength() == 0;
+ }
+ PXSTR LockBuffer()
+ {
+ CMStringData* pData = GetData();
+ if( pData->IsShared() )
+ {
+ Fork( pData->nDataLength );
+ pData = GetData(); // Do it again, because the fork might have changed it
+ }
+ pData->Lock();
+
+ return m_pszData;
+ }
+ void UnlockBuffer()
+ {
+ CMStringData* pData = GetData();
+ pData->Unlock();
+ }
+ void Preallocate( int nLength )
+ {
+ PrepareWrite( nLength );
+ }
+ void ReleaseBuffer( int nNewLength = -1 )
+ {
+ if( nNewLength == -1 )
+ {
+ int nAlloc = GetData()->nAllocLength;
+ nNewLength = StringLengthN( m_pszData, nAlloc);
+ }
+ SetLength( nNewLength );
+ }
+ void ReleaseBufferSetLength( int nNewLength )
+ {
+ SetLength( nNewLength );
+ }
+ void Truncate( int nNewLength )
+ {
+ GetBuffer( nNewLength );
+ ReleaseBufferSetLength( nNewLength );
+ }
+ void SetAt( int iChar, XCHAR ch )
+ {
+ int nLength = GetLength();
+ PXSTR pszBuffer = GetBuffer();
+ pszBuffer[iChar] = ch;
+ ReleaseBufferSetLength( nLength );
+
+ }
+ void SetString( PCXSTR pszSrc )
+ {
+ SetString( pszSrc, StringLength( pszSrc ) );
+ }
+ void SetString( PCXSTR pszSrc, int nLength )
+ {
+ if( nLength == 0 )
+ {
+ Empty();
+ }
+ else
+ {
+
+ UINT nOldLength = GetLength();
+ UINT_PTR nOffset = pszSrc - GetString();
+
+ PXSTR pszBuffer = GetBuffer( nLength );
+ if( nOffset <= nOldLength )
+ {
+ CopyCharsOverlapped( pszBuffer, GetAllocLength(),
+ pszBuffer+nOffset, nLength );
+ }
+ else
+ {
+ CopyChars( pszBuffer, GetAllocLength(), pszSrc, nLength );
+ }
+ ReleaseBufferSetLength( nLength );
+ }
+ }
+public:
+ friend CMSimpleStringT __stdcall operator+(const CMSimpleStringT& str1, const CMSimpleStringT& str2)
+ {
+ CMSimpleStringT s;
+
+ Concatenate( s, str1, str1.GetLength(), str2, str2.GetLength() );
+
+ return s;
+ }
+
+ friend CMSimpleStringT __stdcall operator+(const CMSimpleStringT& str1, PCXSTR psz2)
+ {
+ CMSimpleStringT s;
+
+ Concatenate( s, str1, str1.GetLength(), psz2, StringLength( psz2 ) );
+
+ return s;
+ }
+
+ friend CMSimpleStringT __stdcall operator+(PCXSTR psz1, const CMSimpleStringT& str2)
+ {
+ CMSimpleStringT s;
+
+ Concatenate( s, psz1, StringLength( psz1 ), str2, str2.GetLength() );
+
+ return s;
+ }
+
+ static void __stdcall CopyChars(XCHAR* pchDest, const XCHAR* pchSrc, int nChars )
+ {
+#pragma warning (push)
+#pragma warning(disable : 4996)
+ memcpy(pchDest, pchSrc, nChars * sizeof(XCHAR));
+#pragma warning (pop)
+ }
+ static void __stdcall CopyChars(XCHAR* pchDest, size_t nDestLen, const XCHAR* pchSrc, int nChars )
+ {
+ #if _MSC_VER >= 1400
+ memcpy_s(pchDest, nDestLen * sizeof(XCHAR), pchSrc, nChars * sizeof(XCHAR));
+ #else
+ memcpy(pchDest, pchSrc, nDestLen * sizeof(XCHAR));
+ #endif
+ }
+
+ static void __stdcall CopyCharsOverlapped(XCHAR* pchDest, const XCHAR* pchSrc, int nChars )
+ {
+#pragma warning (push)
+#pragma warning(disable : 4996)
+ memmove(pchDest, pchSrc, nChars * sizeof(XCHAR));
+#pragma warning (pop)
+ }
+ static void __stdcall CopyCharsOverlapped(XCHAR* pchDest, size_t nDestLen, const XCHAR* pchSrc, int nChars)
+ {
+ #if _MSC_VER >= 1400
+ memmove_s(pchDest, nDestLen * sizeof(XCHAR), pchSrc, nChars * sizeof(XCHAR));
+ #else
+ memmove(pchDest, pchSrc, nDestLen * sizeof(XCHAR));
+ #endif
+ }
+ static int __stdcall StringLength(const char* psz)
+ {
+ if (psz == NULL)
+ {
+ return(0);
+ }
+ return (int(strlen(psz)));
+ }
+ static int __stdcall StringLength(const wchar_t* psz)
+ {
+ if (psz == NULL)
+ return 0;
+
+ return int(wcslen(psz));
+ }
+ static int __stdcall StringLengthN(const char* psz, size_t sizeInXChar )
+ {
+ if( psz == NULL )
+ return 0;
+
+ return int( strnlen( psz, sizeInXChar ));
+ }
+ static int __stdcall StringLengthN(const wchar_t* psz, size_t sizeInXChar )
+ {
+ if( psz == NULL )
+ return 0;
+
+ return int( wcsnlen( psz, sizeInXChar ));
+ }
+protected:
+ static void __stdcall Concatenate(CMSimpleStringT& strResult, PCXSTR psz1, int nLength1, PCXSTR psz2, int nLength2)
+ {
+ int nNewLength = nLength1+nLength2;
+ PXSTR pszBuffer = strResult.GetBuffer(nNewLength);
+ CopyChars(pszBuffer, nLength1, psz1, nLength1 );
+ CopyChars(pszBuffer + nLength1, nLength2, psz2, nLength2);
+ strResult.ReleaseBufferSetLength(nNewLength);
+ }
+ // Implementation
+private:
+ void Attach(CMStringData* pData)
+ {
+ m_pszData = static_cast<PXSTR>(pData->data());
+ }
+ void Fork(int nLength)
+ {
+ CMStringData* pOldData = GetData();
+ int nOldLength = pOldData->nDataLength;
+ CMStringData* pNewData = Allocate(nLength, sizeof(XCHAR));
+ if (pNewData != NULL)
+ {
+ int nCharsToCopy = ((nOldLength < nLength) ? nOldLength : nLength)+1; // Copy '\0'
+ CopyChars( PXSTR( pNewData->data() ), nCharsToCopy, PCXSTR( pOldData->data() ), nCharsToCopy );
+ pNewData->nDataLength = nOldLength;
+ pOldData->Release();
+ Attach(pNewData);
+ }
+ }
+ CMStringData* GetData() const
+ {
+ return (reinterpret_cast<CMStringData *>(m_pszData) - 1);
+ }
+ PXSTR PrepareWrite( int nLength )
+ {
+ CMStringData* pOldData = GetData();
+ int nShared = 1 - pOldData->nRefs; // nShared < 0 means true, >= 0 means false
+ int nTooShort = pOldData->nAllocLength-nLength; // nTooShort < 0 means true, >= 0 means false
+ if ((nShared | nTooShort) < 0 ) // If either sign bit is set (i.e. either is less than zero), we need to copy data
+ PrepareWrite2(nLength);
+
+ return m_pszData;
+ }
+ void PrepareWrite2(int nLength)
+ {
+ CMStringData* pOldData = GetData();
+ if (pOldData->nDataLength > nLength)
+ nLength = pOldData->nDataLength;
+
+ if (pOldData->IsShared())
+ {
+ Fork(nLength);
+ }
+ else if (pOldData->nAllocLength < nLength)
+ {
+ // Grow exponentially, until we hit 1K.
+ int nNewLength = pOldData->nAllocLength;
+ if( nNewLength > 1024 )
+ nNewLength += 1024;
+ else
+ nNewLength *= 2;
+
+ if( nNewLength < nLength )
+ nNewLength = nLength;
+
+ Reallocate( nNewLength );
+ }
+ }
+ void Reallocate( int nLength )
+ {
+ CMStringData* pOldData = GetData();
+ if ( pOldData->nAllocLength >= nLength || nLength <= 0)
+ return;
+
+ CMStringData* pNewData = Realloc( pOldData, nLength, sizeof( XCHAR ) );
+ if( pNewData != NULL )
+ Attach( pNewData );
+ }
+
+ void SetLength( int nLength )
+ {
+ GetData()->nDataLength = nLength;
+ m_pszData[nLength] = 0;
+ }
+
+ static CMStringData* __stdcall CloneData(CMStringData* pData)
+ {
+ CMStringData* pNewData = NULL;
+
+ if (!pData->IsLocked()) {
+ pNewData = pData;
+ pNewData->AddRef();
+ }
+
+ return pNewData;
+ }
+
+public :
+ // typedef CStrBufT<BaseType> CStrBuf;
+private:
+ PXSTR m_pszData;
+};
+
+
+template< typename _CharType = char >
+class ChTraitsCRT : public ChTraitsBase< _CharType >
+{
+public:
+ static char* __stdcall CharNext( const char* p )
+ {
+ return reinterpret_cast< char* >( _mbsinc( reinterpret_cast< const unsigned char* >( p )));
+ }
+
+ static int __stdcall IsDigit( char ch )
+ {
+ return _ismbcdigit( ch );
+ }
+
+ static int __stdcall IsSpace( char ch )
+ {
+ return _ismbcspace( ch );
+ }
+
+ static int __stdcall StringCompare( LPCSTR pszA, LPCSTR pszB )
+ {
+ return _mbscmp( reinterpret_cast< const unsigned char* >( pszA ), reinterpret_cast< const unsigned char* >( pszB ));
+ }
+
+ static int __stdcall StringCompareIgnore( LPCSTR pszA, LPCSTR pszB )
+ {
+ return _mbsicmp( reinterpret_cast< const unsigned char* >( pszA ), reinterpret_cast< const unsigned char* >( pszB ));
+ }
+
+ static int __stdcall StringCollate( LPCSTR pszA, LPCSTR pszB )
+ {
+ return _mbscoll( reinterpret_cast< const unsigned char* >( pszA ), reinterpret_cast< const unsigned char* >( pszB ));
+ }
+
+ static int __stdcall StringCollateIgnore( LPCSTR pszA, LPCSTR pszB )
+ {
+ return _mbsicoll( reinterpret_cast< const unsigned char* >( pszA ), reinterpret_cast< const unsigned char* >( pszB ));
+ }
+
+ static LPCSTR __stdcall StringFindString( LPCSTR pszBlock, LPCSTR pszMatch )
+ {
+ return reinterpret_cast< LPCSTR >( _mbsstr( reinterpret_cast< const unsigned char* >( pszBlock ),
+ reinterpret_cast< const unsigned char* >( pszMatch )));
+ }
+
+ static LPSTR __stdcall StringFindString( LPSTR pszBlock, LPCSTR pszMatch )
+ {
+ return const_cast< LPSTR >( StringFindString( const_cast< LPCSTR >( pszBlock ), pszMatch ));
+ }
+
+ static LPCSTR __stdcall StringFindChar( LPCSTR pszBlock, char chMatch )
+ {
+ return reinterpret_cast< LPCSTR >( _mbschr( reinterpret_cast< const unsigned char* >( pszBlock ), (unsigned char)chMatch ));
+ }
+
+ static LPCSTR __stdcall StringFindCharRev( LPCSTR psz, char ch )
+ {
+ return reinterpret_cast< LPCSTR >( _mbsrchr( reinterpret_cast< const unsigned char* >( psz ), (unsigned char)ch ));
+ }
+
+ static LPCSTR __stdcall StringScanSet( LPCSTR pszBlock, LPCSTR pszMatch )
+ {
+ return reinterpret_cast< LPCSTR >( _mbspbrk( reinterpret_cast< const unsigned char* >( pszBlock ),
+ reinterpret_cast< const unsigned char* >( pszMatch )));
+ }
+
+ static int __stdcall StringSpanIncluding( LPCSTR pszBlock, LPCSTR pszSet )
+ {
+ return (int)_mbsspn( reinterpret_cast< const unsigned char* >( pszBlock ), reinterpret_cast< const unsigned char* >( pszSet ) );
+ }
+
+ static int __stdcall StringSpanExcluding( LPCSTR pszBlock, LPCSTR pszSet )
+ {
+ return (int)_mbscspn( reinterpret_cast< const unsigned char* >( pszBlock ), reinterpret_cast< const unsigned char* >( pszSet ) );
+ }
+
+ static LPSTR __stdcall StringUppercase( LPSTR psz )
+ {
+#pragma warning (push)
+#pragma warning(disable : 4996)
+ return reinterpret_cast< LPSTR >( _mbsupr( reinterpret_cast< unsigned char* >( psz ) ) );
+#pragma warning (pop)
+ }
+
+ static LPSTR __stdcall StringLowercase( LPSTR psz )
+ {
+#pragma warning (push)
+#pragma warning(disable : 4996)
+ return reinterpret_cast< LPSTR >( _mbslwr( reinterpret_cast< unsigned char* >( psz ) ) );
+#pragma warning (pop)
+ }
+
+ static LPSTR __stdcall StringUppercase( LPSTR psz, size_t size )
+ {
+ #if _MSC_VER >= 1400
+ _mbsupr_s(reinterpret_cast< unsigned char* >( psz ), size);
+ #else
+ _mbsupr(reinterpret_cast< unsigned char* >( psz ));
+ #endif
+ return psz;
+ }
+
+ static LPSTR __stdcall StringLowercase( LPSTR psz, size_t size )
+ {
+ #if _MSC_VER >= 1400
+ _mbslwr_s( reinterpret_cast< unsigned char* >( psz ), size );
+ #else
+ _mbslwr(reinterpret_cast< unsigned char* >( psz ));
+ #endif
+ return psz;
+ }
+
+ static LPSTR __stdcall StringReverse( LPSTR psz )
+ {
+ return reinterpret_cast< LPSTR >( _mbsrev( reinterpret_cast< unsigned char* >( psz ) ) );
+ }
+
+ static int __stdcall GetFormattedLength( LPCSTR pszFormat, va_list args );
+
+ static int __stdcall Format( LPSTR pszBuffer, LPCSTR pszFormat, va_list args )
+ {
+#pragma warning (push)
+#pragma warning(disable : 4996)
+ return vsprintf( pszBuffer, pszFormat, args );
+#pragma warning (pop)
+
+ }
+
+ static int __stdcall Format( LPSTR pszBuffer, size_t nlength, LPCSTR pszFormat, va_list args );
+
+ static int __stdcall GetBaseTypeLength( LPCSTR pszSrc )
+ {
+ // Returns required buffer length in XCHARs
+ return int( strlen( pszSrc ) );
+ }
+
+ static int __stdcall GetBaseTypeLength( LPCSTR pszSrc, int nLength )
+ {
+ (void)pszSrc;
+ // Returns required buffer length in XCHARs
+ return nLength;
+ }
+
+ static int __stdcall GetBaseTypeLength( LPCWSTR pszSource )
+ {
+ // Returns required buffer length in XCHARs
+ return ::WideCharToMultiByte( _AtlGetConversionACP(), 0, pszSource, -1, NULL, 0, NULL, NULL )-1;
+ }
+
+ static int __stdcall GetBaseTypeLength( LPCWSTR pszSource, int nLength )
+ {
+ // Returns required buffer length in XCHARs
+ return ::WideCharToMultiByte( _AtlGetConversionACP(), 0, pszSource, nLength, NULL, 0, NULL, NULL );
+ }
+
+ static void __stdcall ConvertToBaseType( LPSTR pszDest, int nDestLength, LPCSTR pszSrc, int nSrcLength = -1 )
+ {
+ if (nSrcLength == -1) { nSrcLength=1 + GetBaseTypeLength(pszSrc); }
+ // nLen is in XCHARs
+ memcpy_s( pszDest, nDestLength*sizeof( char ),
+ pszSrc, nSrcLength*sizeof( char ) );
+ }
+
+ static void __stdcall ConvertToBaseType( LPSTR pszDest, int nDestLength, LPCWSTR pszSrc, int nSrcLength = -1)
+ {
+ // nLen is in XCHARs
+ ::WideCharToMultiByte( _AtlGetConversionACP(), 0, pszSrc, nSrcLength, pszDest, nDestLength, NULL, NULL );
+ }
+
+ static void ConvertToOem( _CharType* pstrString)
+ {
+ BOOL fSuccess=::CharToOemA(pstrString, pstrString);
+ }
+
+ static void ConvertToAnsi( _CharType* pstrString)
+ {
+ BOOL fSuccess=::OemToCharA(pstrString, pstrString);
+ }
+
+ static void ConvertToOem( _CharType* pstrString, size_t size)
+ {
+ if(size>UINT_MAX)
+ {
+ return;
+ }
+ DWORD dwSize=static_cast<DWORD>(size);
+ BOOL fSuccess=::CharToOemBuffA(pstrString, pstrString, dwSize);
+ }
+
+ static void ConvertToAnsi( _CharType* pstrString, size_t size)
+ {
+ if(size>UINT_MAX)
+ return;
+
+ DWORD dwSize=static_cast<DWORD>(size);
+ BOOL fSuccess=::OemToCharBuffA(pstrString, pstrString, dwSize);
+ }
+
+ static void __stdcall FloodCharacters( char ch, int nLength, char* pch )
+ {
+ // nLength is in XCHARs
+ memset( pch, ch, nLength );
+ }
+
+ static BSTR __stdcall AllocSysString( const char* pchData, int nDataLength )
+ {
+ int nLen = ::MultiByteToWideChar( _AtlGetConversionACP(), 0, pchData, nDataLength, NULL, NULL );
+ BSTR bstr = ::SysAllocStringLen( NULL, nLen );
+ if( bstr != NULL )
+ ::MultiByteToWideChar( _AtlGetConversionACP(), 0, pchData, nDataLength, bstr, nLen );
+
+ return bstr;
+ }
+
+ static BOOL __stdcall ReAllocSysString( const char* pchData, BSTR* pbstr, int nDataLength )
+ {
+ int nLen = ::MultiByteToWideChar( _AtlGetConversionACP(), 0, pchData, nDataLength, NULL, NULL );
+ BOOL bSuccess = ::SysReAllocStringLen( pbstr, NULL, nLen );
+ if( bSuccess )
+ ::MultiByteToWideChar( _AtlGetConversionACP(), 0, pchData, nDataLength, *pbstr, nLen );
+
+ return bSuccess;
+ }
+
+ static int __stdcall SafeStringLen( LPCSTR psz )
+ {
+ // returns length in bytes
+ return (psz != NULL) ? int( strlen( psz ) ) : 0;
+ }
+
+ static int __stdcall SafeStringLen( LPCWSTR psz )
+ {
+ // returns length in wchar_ts
+ return (psz != NULL) ? int( wcslen( psz ) ) : 0;
+ }
+
+ static int __stdcall GetCharLen( const wchar_t* pch )
+ {
+ // returns char length
+ return 1;
+ }
+
+ static int __stdcall GetCharLen( const char* pch )
+ {
+ // returns char length
+ return int( _mbclen( reinterpret_cast< const unsigned char* >( pch ) ) );
+ }
+
+ static DWORD __stdcall GetEnvironmentVariable( LPCSTR pszVar, LPSTR pszBuffer, DWORD dwSize )
+ {
+ return ::GetEnvironmentVariableA( pszVar, pszBuffer, dwSize );
+ }
+};
+
+// specialization for wchar_t
+template<>
+class ChTraitsCRT< wchar_t > : public ChTraitsBase< wchar_t >
+{
+ static DWORD __stdcall _GetEnvironmentVariableW( LPCWSTR pszName, LPWSTR pszBuffer, DWORD nSize )
+ {
+ return ::GetEnvironmentVariableW( pszName, pszBuffer, nSize );
+ }
+
+public:
+ static LPWSTR __stdcall CharNext( LPCWSTR psz )
+ {
+ return const_cast< LPWSTR >( psz+1 );
+ }
+
+ static int __stdcall IsDigit( wchar_t ch )
+ {
+ return iswdigit( static_cast<unsigned short>(ch) );
+ }
+
+ static int __stdcall IsSpace( wchar_t ch )
+ {
+ return iswspace( static_cast<unsigned short>(ch) );
+ }
+
+ static int __stdcall StringCompare( LPCWSTR pszA, LPCWSTR pszB )
+ {
+ return wcscmp( pszA, pszB );
+ }
+
+ static int __stdcall StringCompareIgnore( LPCWSTR pszA, LPCWSTR pszB )
+ {
+ return _wcsicmp( pszA, pszB );
+ }
+
+ static int __stdcall StringCollate( LPCWSTR pszA, LPCWSTR pszB )
+ {
+ return wcscoll( pszA, pszB );
+ }
+
+ static int __stdcall StringCollateIgnore( LPCWSTR pszA, LPCWSTR pszB )
+ {
+ return _wcsicoll( pszA, pszB );
+ }
+
+ static LPCWSTR __stdcall StringFindString( LPCWSTR pszBlock, LPCWSTR pszMatch )
+ {
+ return wcsstr( pszBlock, pszMatch );
+ }
+
+ static LPWSTR __stdcall StringFindString( LPWSTR pszBlock, LPCWSTR pszMatch )
+ {
+ return const_cast< LPWSTR >( StringFindString( const_cast< LPCWSTR >( pszBlock ), pszMatch ));
+ }
+
+ static LPCWSTR __stdcall StringFindChar( LPCWSTR pszBlock, wchar_t chMatch )
+ {
+ return wcschr( pszBlock, chMatch );
+ }
+
+ static LPCWSTR __stdcall StringFindCharRev( LPCWSTR psz, wchar_t ch )
+ {
+ return wcsrchr( psz, ch );
+ }
+
+ static LPCWSTR __stdcall StringScanSet( LPCWSTR pszBlock, LPCWSTR pszMatch )
+ {
+ return wcspbrk( pszBlock, pszMatch );
+ }
+
+ static int __stdcall StringSpanIncluding( LPCWSTR pszBlock, LPCWSTR pszSet )
+ {
+ return (int)wcsspn( pszBlock, pszSet );
+ }
+
+ static int __stdcall StringSpanExcluding( LPCWSTR pszBlock, LPCWSTR pszSet )
+ {
+ return (int)wcscspn( pszBlock, pszSet );
+ }
+
+ static LPWSTR __stdcall StringUppercase( LPWSTR psz )
+ {
+#pragma warning (push)
+#pragma warning(disable : 4996)
+ return _wcsupr( psz );
+#pragma warning (pop)
+ }
+
+ static LPWSTR __stdcall StringLowercase( LPWSTR psz )
+ {
+#pragma warning (push)
+#pragma warning(disable : 4996)
+ return _wcslwr( psz );
+#pragma warning (pop)
+ }
+
+ static LPWSTR __stdcall StringUppercase( LPWSTR psz, size_t )
+ {
+ return _wcsupr( psz );
+ }
+
+ static LPWSTR __stdcall StringLowercase( LPWSTR psz, size_t )
+ {
+ return _wcslwr( psz );
+ }
+
+ static LPWSTR __stdcall StringReverse( LPWSTR psz )
+ {
+ return _wcsrev( psz );
+ }
+
+ static int __stdcall GetFormattedLength( LPCWSTR pszFormat, va_list args);
+
+ static int __stdcall Format( LPWSTR pszBuffer, LPCWSTR pszFormat, va_list args)
+ {
+#pragma warning (push)
+#pragma warning(disable : 4996)
+ return vswprintf( pszBuffer, pszFormat, args );
+#pragma warning (pop)
+ }
+
+ static int __stdcall Format( LPWSTR pszBuffer, size_t nLength, LPCWSTR pszFormat, va_list args);
+
+ static int __stdcall GetBaseTypeLength( LPCSTR pszSrc )
+ {
+ // Returns required buffer size in wchar_ts
+ return ::MultiByteToWideChar( CP_ACP, 0, pszSrc, -1, NULL, 0 )-1;
+ }
+
+ static int __stdcall GetBaseTypeLength( LPCSTR pszSrc, int nLength )
+ {
+ // Returns required buffer size in wchar_ts
+ return ::MultiByteToWideChar( CP_ACP, 0, pszSrc, nLength, NULL, 0 );
+ }
+
+ static int __stdcall GetBaseTypeLength( LPCWSTR pszSrc )
+ {
+ // Returns required buffer size in wchar_ts
+ return (int)wcslen( pszSrc );
+ }
+
+ static int __stdcall GetBaseTypeLength( LPCWSTR pszSrc, int nLength )
+ {
+ (void)pszSrc;
+ // Returns required buffer size in wchar_ts
+ return nLength;
+ }
+
+ static void __stdcall ConvertToBaseType( LPWSTR pszDest, int nDestLength, LPCSTR pszSrc, int nSrcLength = -1)
+ {
+ // nLen is in wchar_ts
+ ::MultiByteToWideChar( CP_ACP, 0, pszSrc, nSrcLength, pszDest, nDestLength );
+ }
+
+ static void __stdcall ConvertToBaseType( LPWSTR pszDest, int nDestLength, LPCWSTR pszSrc, int nSrcLength = -1 )
+ {
+ if (nSrcLength == -1) { nSrcLength=1 + GetBaseTypeLength(pszSrc); }
+ // nLen is in wchar_ts
+ #if _MSC_VER >= 1400
+ wmemcpy_s(pszDest, nDestLength, pszSrc, nSrcLength);
+ #else
+ wmemcpy(pszDest, pszSrc, nDestLength);
+ #endif
+ }
+
+ static void __stdcall FloodCharacters( wchar_t ch, int nLength, LPWSTR psz )
+ {
+ // nLength is in XCHARs
+ for( int i = 0; i < nLength; i++ )
+ {
+ psz[i] = ch;
+ }
+ }
+
+ static BSTR __stdcall AllocSysString( const wchar_t* pchData, int nDataLength )
+ {
+ return ::SysAllocStringLen( pchData, nDataLength );
+ }
+
+ static BOOL __stdcall ReAllocSysString( const wchar_t* pchData, BSTR* pbstr, int nDataLength )
+ {
+ return ::SysReAllocStringLen( pbstr, pchData, nDataLength );
+ }
+
+ static int __stdcall SafeStringLen( LPCSTR psz )
+ {
+ // returns length in bytes
+ return (psz != NULL) ? (int)strlen( psz ) : 0;
+ }
+
+ static int __stdcall SafeStringLen( LPCWSTR psz )
+ {
+ // returns length in wchar_ts
+ return (psz != NULL) ? (int)wcslen( psz ) : 0;
+ }
+
+ static int __stdcall GetCharLen( const wchar_t* pch )
+ {
+ (void)pch;
+ // returns char length
+ return 1;
+ }
+
+ static int __stdcall GetCharLen( const char* pch )
+ {
+ // returns char length
+ return (int)( _mbclen( reinterpret_cast< const unsigned char* >( pch ) ) );
+ }
+
+ static DWORD __stdcall GetEnvironmentVariable( LPCWSTR pszVar, LPWSTR pszBuffer, DWORD dwSize )
+ {
+ return _GetEnvironmentVariableW( pszVar, pszBuffer, dwSize );
+ }
+
+ static void __stdcall ConvertToOem( LPWSTR /*psz*/ )
+ {
+ }
+
+ static void __stdcall ConvertToAnsi( LPWSTR /*psz*/ )
+ {
+ }
+
+ static void __stdcall ConvertToOem( LPWSTR /*psz*/, size_t )
+ {
+ }
+
+ static void __stdcall ConvertToAnsi( LPWSTR /*psz*/, size_t )
+ {
+ }
+};
+
+template< typename BaseType, class StringTraits >
+class CMStringT : public CMSimpleStringT< BaseType >
+{
+public:
+ typedef CMSimpleStringT< BaseType> CThisSimpleString;
+ typedef typename CThisSimpleString::XCHAR XCHAR;
+ typedef typename CThisSimpleString::PXSTR PXSTR;
+ typedef typename CThisSimpleString::PCXSTR PCXSTR;
+ typedef typename CThisSimpleString::YCHAR YCHAR;
+ typedef typename CThisSimpleString::PYSTR PYSTR;
+ typedef typename CThisSimpleString::PCYSTR PCYSTR;
+
+public:
+ CMStringT() : CThisSimpleString()
+ {
+ }
+
+ static void __stdcall Construct( CMStringT* pString )
+ {
+ new( pString ) CMStringT;
+ }
+
+ // Copy constructor
+ CMStringT( const CMStringT& strSrc ) :
+ CThisSimpleString( strSrc )
+ {
+ }
+
+ CMStringT( const XCHAR* pszSrc ) :
+ CThisSimpleString()
+ {
+ // nDestLength is in XCHARs
+ *this = pszSrc;
+ }
+
+ CMStringT( const YCHAR* pszSrc ) :
+ CThisSimpleString()
+ {
+ *this = pszSrc;
+ }
+
+
+ CMStringT( const unsigned char* pszSrc ) :
+ CThisSimpleString()
+ {
+ *this = reinterpret_cast< const char* >( pszSrc );
+ }
+
+ CMStringT( char ch, int nLength = 1 ) :
+ CThisSimpleString()
+ {
+ if( nLength > 0 )
+ {
+ PXSTR pszBuffer = this->GetBuffer( nLength );
+ StringTraits::FloodCharacters( XCHAR( ch ), nLength, pszBuffer );
+ this->ReleaseBufferSetLength( nLength );
+ }
+ }
+
+ CMStringT( wchar_t ch, int nLength = 1 ) :
+ CThisSimpleString()
+ {
+ if( nLength > 0 )
+ {
+ //Convert ch to the BaseType
+ wchar_t pszCh[2] = { ch , 0 };
+ int nBaseTypeCharLen = 1;
+
+ if(ch != L'\0')
+ {
+ nBaseTypeCharLen = StringTraits::GetBaseTypeLength(pszCh);
+ }
+
+ XCHAR *buffBaseTypeChar = new XCHAR[nBaseTypeCharLen+1];
+ StringTraits::ConvertToBaseType( buffBaseTypeChar, nBaseTypeCharLen+1, pszCh, 1 );
+ //Allocate enough characters in String and flood (replicate) with the (converted character)*nLength
+ PXSTR pszBuffer = this->GetBuffer( nLength*nBaseTypeCharLen );
+ if (nBaseTypeCharLen == 1)
+ { //Optimization for a common case - wide char translates to 1 ansi/wide char.
+ StringTraits::FloodCharacters( buffBaseTypeChar[0], nLength, pszBuffer );
+ } else
+ {
+ XCHAR* p=pszBuffer;
+ for (int i=0 ; i < nLength ;++i)
+ {
+ for (int j=0 ; j < nBaseTypeCharLen ;++j)
+ {
+ *p=buffBaseTypeChar[j];
+ ++p;
+ }
+ }
+ }
+ this->ReleaseBufferSetLength( nLength*nBaseTypeCharLen );
+ delete [] buffBaseTypeChar;
+ }
+ }
+
+ CMStringT( const XCHAR* pch, int nLength ) :
+ CThisSimpleString( pch, nLength )
+ {
+ }
+
+ CMStringT( const YCHAR* pch, int nLength ) :
+ CThisSimpleString()
+ {
+ if( nLength > 0 )
+ {
+ int nDestLength = StringTraits::GetBaseTypeLength( pch, nLength );
+ PXSTR pszBuffer = this->GetBuffer( nDestLength );
+ StringTraits::ConvertToBaseType( pszBuffer, nDestLength, pch, nLength );
+ this->ReleaseBufferSetLength( nDestLength );
+ }
+ }
+
+ // Destructor
+ ~CMStringT()
+ {
+ }
+
+ // Assignment operators
+ CMStringT& operator=( const CMStringT& strSrc )
+ {
+ CThisSimpleString::operator=( strSrc );
+
+ return *this;
+ }
+
+ CMStringT& operator=( PCXSTR pszSrc )
+ {
+ CThisSimpleString::operator=( pszSrc );
+
+ return *this;
+ }
+
+ CMStringT& operator=( PCYSTR pszSrc )
+ {
+ // nDestLength is in XCHARs
+ int nDestLength = (pszSrc != NULL) ? StringTraits::GetBaseTypeLength( pszSrc ) : 0;
+ if( nDestLength > 0 )
+ {
+ PXSTR pszBuffer = this->GetBuffer( nDestLength );
+ StringTraits::ConvertToBaseType( pszBuffer, nDestLength, pszSrc);
+ this->ReleaseBufferSetLength( nDestLength );
+ }
+ else
+ {
+ this->Empty();
+ }
+
+ return *this;
+ }
+
+ CMStringT& operator=( const unsigned char* pszSrc )
+ {
+ return operator=( reinterpret_cast< const char* >( pszSrc ));
+ }
+
+ CMStringT& operator=( char ch )
+ {
+ char ach[2] = { ch, 0 };
+
+ return operator=( ach );
+ }
+
+ CMStringT& operator=( wchar_t ch )
+ {
+ wchar_t ach[2] = { ch, 0 };
+
+ return operator=( ach );
+ }
+
+// CMStringT& operator=( const VARIANT& var );
+
+ CMStringT& operator+=( const CMStringT& str )
+ {
+ CThisSimpleString::operator+=( str );
+ return *this;
+ }
+
+ CMStringT& operator+=( const CThisSimpleString& str )
+ {
+ CThisSimpleString::operator+=( str );
+ return *this;
+ }
+
+ CMStringT& operator+=( PCXSTR pszSrc )
+ {
+ CThisSimpleString::operator+=( pszSrc );
+ return *this;
+ }
+// template< int t_nSize >
+// CMStringT& operator+=( const CStaticString< XCHAR, t_nSize >& strSrc )
+// {
+// CThisSimpleString::operator+=( strSrc );
+//
+// return *this;
+// }
+ CMStringT& operator+=( PCYSTR pszSrc )
+ {
+ CMStringT str( pszSrc );
+
+ return operator+=( str );
+ }
+
+ CMStringT& operator+=( char ch )
+ {
+ CThisSimpleString::operator+=( ch );
+
+ return *this;
+ }
+
+ CMStringT& operator+=( unsigned char ch )
+ {
+ CThisSimpleString::operator+=( ch );
+
+ return *this;
+ }
+
+ CMStringT& operator+=( wchar_t ch )
+ {
+ CThisSimpleString::operator+=( ch );
+
+ return *this;
+ }
+
+ // Comparison
+
+ int Compare( PCXSTR psz ) const
+ {
+ return StringTraits::StringCompare( this->GetString(), psz );
+ }
+
+ int CompareNoCase( PCXSTR psz ) const
+ {
+ return StringTraits::StringCompareIgnore( this->GetString(), psz );
+ }
+
+ int Collate( PCXSTR psz ) const
+ {
+ return StringTraits::StringCollate( this->GetString(), psz );
+ }
+
+ int CollateNoCase( PCXSTR psz ) const
+ {
+ return StringTraits::StringCollateIgnore( this->GetString(), psz );
+ }
+
+ // Advanced manipulation
+
+ // Delete 'nCount' characters, starting at index 'iIndex'
+ int Delete( int iIndex, int nCount = 1 )
+ {
+ if( iIndex < 0 )
+ iIndex = 0;
+
+ if( nCount < 0 )
+ nCount = 0;
+
+ int nLength = this->GetLength();
+ if( nCount + iIndex > nLength )
+ {
+ nCount = nLength-iIndex;
+ }
+ if( nCount > 0 )
+ {
+ int nNewLength = nLength-nCount;
+ int nXCHARsToCopy = nLength-(iIndex+nCount)+1;
+ PXSTR pszBuffer = this->GetBuffer();
+ #if _MSC_VER >= 1400
+ memmove_s( pszBuffer+iIndex, nXCHARsToCopy*sizeof( XCHAR ), pszBuffer+iIndex+nCount, nXCHARsToCopy*sizeof( XCHAR ) );
+ #else
+ memmove( pszBuffer+iIndex, pszBuffer+iIndex+nCount, nXCHARsToCopy*sizeof( XCHAR ));
+ #endif
+ this->ReleaseBufferSetLength( nNewLength );
+ }
+
+ return this->GetLength();
+ }
+
+ // Insert character 'ch' before index 'iIndex'
+ int Insert( int iIndex, XCHAR ch )
+ {
+ if( iIndex < 0 )
+ iIndex = 0;
+
+ if( iIndex > this->GetLength() )
+ iIndex = this->GetLength();
+
+ int nNewLength = this->GetLength()+1;
+
+ PXSTR pszBuffer = this->GetBuffer( nNewLength );
+
+ // move existing bytes down
+ #if _MSC_VER >= 1400
+ memmove_s( pszBuffer+iIndex+1, (nNewLength-iIndex)*sizeof( XCHAR ), pszBuffer+iIndex, (nNewLength-iIndex)*sizeof( XCHAR ) );
+ #else
+ memmove( pszBuffer+iIndex+1, pszBuffer+iIndex, (nNewLength-iIndex)*sizeof( XCHAR ) );
+ #endif
+ pszBuffer[iIndex] = ch;
+
+ this->ReleaseBufferSetLength( nNewLength );
+ return nNewLength;
+ }
+
+ // Insert string 'psz' before index 'iIndex'
+ int Insert( int iIndex, PCXSTR psz )
+ {
+ if( iIndex < 0 )
+ iIndex = 0;
+
+ if( iIndex > this->GetLength() )
+ {
+ iIndex = this->GetLength();
+ }
+
+ // nInsertLength and nNewLength are in XCHARs
+ int nInsertLength = StringTraits::SafeStringLen( psz );
+ int nNewLength = this->GetLength();
+ if( nInsertLength > 0 )
+ {
+ nNewLength += nInsertLength;
+
+ PXSTR pszBuffer = this->GetBuffer( nNewLength );
+ // move existing bytes down
+ #if _MSC_VER >= 1400
+ memmove_s( pszBuffer+iIndex+nInsertLength, (nNewLength-iIndex-nInsertLength+1)*sizeof( XCHAR ), pszBuffer+iIndex, (nNewLength-iIndex-nInsertLength+1)*sizeof( XCHAR ) );
+ memcpy_s( pszBuffer+iIndex, nInsertLength*sizeof( XCHAR ), psz, nInsertLength*sizeof( XCHAR ));
+ #else
+ memmove( pszBuffer+iIndex+nInsertLength, pszBuffer+iIndex, (nNewLength-iIndex-nInsertLength+1)*sizeof( XCHAR ) );
+ memcpy( pszBuffer+iIndex, psz, nInsertLength*sizeof( XCHAR ));
+ #endif
+ this->ReleaseBufferSetLength( nNewLength );
+ }
+
+ return nNewLength;
+ }
+
+ // Replace all occurrences of character 'chOld' with character 'chNew'
+ int Replace( XCHAR chOld, XCHAR chNew )
+ {
+ int nCount = 0;
+
+ // short-circuit the nop case
+ if( chOld != chNew )
+ {
+ // otherwise modify each character that matches in the string
+ bool bCopied = false;
+ PXSTR pszBuffer = const_cast< PXSTR >( this->GetString() ); // We don't actually write to pszBuffer until we've called GetBuffer().
+
+ int nLength = this->GetLength();
+ int iChar = 0;
+ while( iChar < nLength )
+ {
+ // replace instances of the specified character only
+ if( pszBuffer[iChar] == chOld )
+ {
+ if( !bCopied )
+ {
+ bCopied = true;
+ pszBuffer = this->GetBuffer( nLength );
+ }
+ pszBuffer[iChar] = chNew;
+ nCount++;
+ }
+ iChar = int( StringTraits::CharNext( pszBuffer+iChar )-pszBuffer );
+ }
+ if( bCopied )
+ {
+ this->ReleaseBufferSetLength( nLength );
+ }
+ }
+
+ return nCount;
+ }
+
+ // Replace all occurrences of string 'pszOld' with string 'pszNew'
+ int Replace( PCXSTR pszOld, PCXSTR pszNew )
+ {
+ // can't have empty or NULL lpszOld
+
+ // nSourceLen is in XCHARs
+ int nSourceLen = StringTraits::SafeStringLen( pszOld );
+ if( nSourceLen == 0 )
+ return 0;
+ // nReplacementLen is in XCHARs
+ int nReplacementLen = StringTraits::SafeStringLen( pszNew );
+
+ // loop once to figure out the size of the result string
+ int nCount = 0;
+ {
+ PCXSTR pszStart = this->GetString();
+ PCXSTR pszEnd = pszStart+this->GetLength();
+ while( pszStart < pszEnd )
+ {
+ PCXSTR pszTarget;
+ while( (pszTarget = StringTraits::StringFindString( pszStart, pszOld ) ) != NULL)
+ {
+ nCount++;
+ pszStart = pszTarget+nSourceLen;
+ }
+ pszStart += StringTraits::SafeStringLen( pszStart )+1;
+ }
+ }
+
+ // if any changes were made, make them
+ if( nCount > 0 )
+ {
+ // if the buffer is too small, just
+ // allocate a new buffer (slow but sure)
+ int nOldLength = this->GetLength();
+ int nNewLength = nOldLength+(nReplacementLen-nSourceLen)*nCount;
+
+ PXSTR pszBuffer = this->GetBuffer( __max( nNewLength, nOldLength ) );
+
+ PXSTR pszStart = pszBuffer;
+ PXSTR pszEnd = pszStart+nOldLength;
+
+ // loop again to actually do the work
+ while( pszStart < pszEnd )
+ {
+ PXSTR pszTarget;
+ while( (pszTarget = StringTraits::StringFindString( pszStart, pszOld ) ) != NULL )
+ {
+ int nBalance = nOldLength-int(pszTarget-pszBuffer+nSourceLen);
+ memmove_s( pszTarget+nReplacementLen, nBalance*sizeof( XCHAR ),
+ pszTarget+nSourceLen, nBalance*sizeof( XCHAR ) );
+ memcpy_s( pszTarget, nReplacementLen*sizeof( XCHAR ),
+ pszNew, nReplacementLen*sizeof( XCHAR ) );
+ pszStart = pszTarget+nReplacementLen;
+ pszTarget[nReplacementLen+nBalance] = 0;
+ nOldLength += (nReplacementLen-nSourceLen);
+ }
+ pszStart += StringTraits::SafeStringLen( pszStart )+1;
+ }
+ this->ReleaseBufferSetLength( nNewLength );
+ }
+
+ return nCount;
+ }
+
+ // Remove all occurrences of character 'chRemove'
+ int Remove( XCHAR chRemove )
+ {
+ int nLength = this->GetLength();
+ PXSTR pszBuffer = this->GetBuffer( nLength );
+
+ PXSTR pszSource = pszBuffer;
+ PXSTR pszDest = pszBuffer;
+ PXSTR pszEnd = pszBuffer+nLength;
+
+ while( pszSource < pszEnd )
+ {
+ PXSTR pszNewSource = StringTraits::CharNext( pszSource );
+ if( *pszSource != chRemove )
+ {
+ // Copy the source to the destination. Remember to copy all bytes of an MBCS character
+ // Copy the source to the destination. Remember to copy all bytes of an MBCS character
+ size_t NewSourceGap = (pszNewSource-pszSource);
+ PXSTR pszNewDest = pszDest + NewSourceGap;
+ size_t i = 0;
+ for (i = 0; pszDest != pszNewDest && i < NewSourceGap; i++)
+ {
+ *pszDest = *pszSource;
+ pszSource++;
+ pszDest++;
+ }
+ }
+ pszSource = pszNewSource;
+ }
+ *pszDest = 0;
+ int nCount = int( pszSource-pszDest );
+ this->ReleaseBufferSetLength( nLength-nCount );
+
+ return nCount;
+ }
+
+ CMStringT Tokenize( PCXSTR pszTokens, int& iStart ) const
+ {
+ if( (pszTokens == NULL) || (*pszTokens == (XCHAR)0) )
+ {
+ if (iStart < this->GetLength())
+ return CMStringT( this->GetString()+iStart );
+ }
+ else
+ {
+ PCXSTR pszPlace = this->GetString()+iStart;
+ PCXSTR pszEnd = this->GetString()+this->GetLength();
+ if( pszPlace < pszEnd )
+ {
+ int nIncluding = StringTraits::StringSpanIncluding( pszPlace, pszTokens );
+
+ if( (pszPlace+nIncluding) < pszEnd )
+ {
+ pszPlace += nIncluding;
+ int nExcluding = StringTraits::StringSpanExcluding( pszPlace, pszTokens );
+
+ int iFrom = iStart+nIncluding;
+ int nUntil = nExcluding;
+ iStart = iFrom+nUntil+1;
+
+ return Mid( iFrom, nUntil );
+ }
+ }
+ }
+
+ // return empty string, done tokenizing
+ iStart = -1;
+
+ return CMStringT();
+ }
+
+ // find routines
+
+ // Find the first occurrence of character 'ch', starting at index 'iStart'
+ int Find( XCHAR ch, int iStart = 0 ) const
+ {
+ // nLength is in XCHARs
+ int nLength = this->GetLength();
+ if( iStart < 0 || iStart >= nLength)
+ return -1;
+
+ // find first single character
+ PCXSTR psz = StringTraits::StringFindChar( this->GetString()+iStart, ch );
+
+ // return -1 if not found and index otherwise
+ return (psz == NULL) ? -1 : int( psz-this->GetString());
+ }
+
+ // look for a specific sub-string
+
+ // Find the first occurrence of string 'pszSub', starting at index 'iStart'
+ int Find( PCXSTR pszSub, int iStart = 0 ) const
+ {
+ // iStart is in XCHARs
+ if(pszSub == NULL)
+ return -1;
+
+ // nLength is in XCHARs
+ int nLength = this->GetLength();
+ if( iStart < 0 || iStart > nLength )
+ return -1;
+
+ // find first matching substring
+ PCXSTR psz = StringTraits::StringFindString( this->GetString()+iStart, pszSub );
+
+ // return -1 for not found, distance from beginning otherwise
+ return (psz == NULL) ? -1 : int( psz-this->GetString());
+ }
+
+ // Find the first occurrence of any of the characters in string 'pszCharSet'
+ int FindOneOf( PCXSTR pszCharSet ) const
+ {
+ PCXSTR psz = StringTraits::StringScanSet( this->GetString(), pszCharSet );
+ return (psz == NULL) ? -1 : int( psz-this->GetString());
+ }
+
+ // Find the last occurrence of character 'ch'
+ int ReverseFind( XCHAR ch ) const
+ {
+ // find last single character
+ PCXSTR psz = StringTraits::StringFindCharRev( this->GetString(), ch );
+
+ // return -1 if not found, distance from beginning otherwise
+ return (psz == NULL) ? -1 : int( psz-this->GetString());
+ }
+
+ // manipulation
+
+ // Convert the string to uppercase
+ CMStringT& MakeUpper()
+ {
+ int nLength = this->GetLength();
+ PXSTR pszBuffer = this->GetBuffer( nLength );
+ StringTraits::StringUppercase( pszBuffer, nLength+1 );
+ this->ReleaseBufferSetLength( nLength );
+
+ return *this;
+ }
+
+ // Convert the string to lowercase
+ CMStringT& MakeLower()
+ {
+ int nLength = this->GetLength();
+ PXSTR pszBuffer = this->GetBuffer( nLength );
+ StringTraits::StringLowercase( pszBuffer, nLength+1 );
+ this->ReleaseBufferSetLength( nLength );
+
+ return *this;
+ }
+
+ // Reverse the string
+ CMStringT& MakeReverse()
+ {
+ int nLength = this->GetLength();
+ PXSTR pszBuffer = this->GetBuffer( nLength );
+ StringTraits::StringReverse( pszBuffer );
+ this->ReleaseBufferSetLength( nLength );
+
+ return *this;
+ }
+
+ // trimming
+
+ // Remove all trailing whitespace
+ CMStringT& TrimRight()
+ {
+ // find beginning of trailing spaces by starting
+ // at beginning (DBCS aware)
+
+ PCXSTR psz = this->GetString();
+ PCXSTR pszLast = NULL;
+
+ while( *psz != 0 )
+ {
+ if( StringTraits::IsSpace( *psz ) )
+ {
+ if( pszLast == NULL )
+ pszLast = psz;
+ }
+ else
+ {
+ pszLast = NULL;
+ }
+ psz = StringTraits::CharNext( psz );
+ }
+
+ if( pszLast != NULL )
+ {
+ // truncate at trailing space start
+ int iLast = int( pszLast-this->GetString() );
+
+ this->Truncate( iLast );
+ }
+
+ return *this;
+ }
+
+ // Remove all leading whitespace
+ CMStringT& TrimLeft()
+ {
+ // find first non-space character
+
+ PCXSTR psz = this->GetString();
+
+ while( StringTraits::IsSpace( *psz ) )
+ {
+ psz = StringTraits::CharNext( psz );
+ }
+
+ if( psz != this->GetString() )
+ {
+ // fix up data and length
+ int iFirst = int( psz-this->GetString() );
+ PXSTR pszBuffer = this->GetBuffer( this->GetLength() );
+ psz = pszBuffer+iFirst;
+ int nDataLength = this->GetLength()-iFirst;
+ memmove_s( pszBuffer, (this->GetLength()+1)*sizeof( XCHAR ),
+ psz, (nDataLength+1)*sizeof( XCHAR ) );
+ this->ReleaseBufferSetLength( nDataLength );
+ }
+
+ return *this;
+ }
+
+ // Remove all leading and trailing whitespace
+ CMStringT& Trim()
+ {
+ return TrimRight().TrimLeft();
+ }
+
+ // Remove all leading and trailing occurrences of character 'chTarget'
+ CMStringT& Trim( XCHAR chTarget )
+ {
+ return TrimRight( chTarget ).TrimLeft( chTarget );
+ }
+
+ // Remove all leading and trailing occurrences of any of the characters in the string 'pszTargets'
+ CMStringT& Trim( PCXSTR pszTargets )
+ {
+ return TrimRight( pszTargets ).TrimLeft( pszTargets );
+ }
+
+ // trimming anything (either side)
+
+ // Remove all trailing occurrences of character 'chTarget'
+ CMStringT& TrimRight( XCHAR chTarget )
+ {
+ // find beginning of trailing matches
+ // by starting at beginning (DBCS aware)
+
+ PCXSTR psz = this->GetString();
+ PCXSTR pszLast = NULL;
+
+ while( *psz != 0 )
+ {
+ if( *psz == chTarget )
+ {
+ if( pszLast == NULL )
+ {
+ pszLast = psz;
+ }
+ }
+ else
+ {
+ pszLast = NULL;
+ }
+ psz = StringTraits::CharNext( psz );
+ }
+
+ if( pszLast != NULL )
+ {
+ // truncate at left-most matching character
+ int iLast = int( pszLast-this->GetString() );
+ this->Truncate( iLast );
+ }
+
+ return *this;
+ }
+
+ // Remove all trailing occurrences of any of the characters in string 'pszTargets'
+ CMStringT& TrimRight( PCXSTR pszTargets )
+ {
+ // if we're not trimming anything, we're not doing any work
+ if( (pszTargets == NULL) || (*pszTargets == 0) )
+ {
+ return *this;
+ }
+
+ // find beginning of trailing matches
+ // by starting at beginning (DBCS aware)
+
+ PCXSTR psz = this->GetString();
+ PCXSTR pszLast = NULL;
+
+ while( *psz != 0 )
+ {
+ if( StringTraits::StringFindChar( pszTargets, *psz ) != NULL )
+ {
+ if( pszLast == NULL )
+ {
+ pszLast = psz;
+ }
+ }
+ else
+ {
+ pszLast = NULL;
+ }
+ psz = StringTraits::CharNext( psz );
+ }
+
+ if( pszLast != NULL )
+ {
+ // truncate at left-most matching character
+ int iLast = int( pszLast-this->GetString() );
+ this->Truncate( iLast );
+ }
+
+ return *this;
+ }
+
+ // Remove all leading occurrences of character 'chTarget'
+ CMStringT& TrimLeft( XCHAR chTarget )
+ {
+ // find first non-matching character
+ PCXSTR psz = this->GetString();
+
+ while( chTarget == *psz )
+ {
+ psz = StringTraits::CharNext( psz );
+ }
+
+ if( psz != this->GetString() )
+ {
+ // fix up data and length
+ int iFirst = int( psz-this->GetString() );
+ PXSTR pszBuffer = this->GetBuffer( this->GetLength() );
+ psz = pszBuffer+iFirst;
+ int nDataLength = this->GetLength()-iFirst;
+ memmove_s( pszBuffer, (this->GetLength()+1)*sizeof( XCHAR ),
+ psz, (nDataLength+1)*sizeof( XCHAR ) );
+ this->ReleaseBufferSetLength( nDataLength );
+ }
+
+ return *this;
+ }
+
+ // Remove all leading occurrences of any of the characters in string 'pszTargets'
+ CMStringT& TrimLeft( PCXSTR pszTargets )
+ {
+ // if we're not trimming anything, we're not doing any work
+ if( (pszTargets == NULL) || (*pszTargets == 0) )
+ {
+ return *this;
+ }
+
+ PCXSTR psz = this->GetString();
+ while( (*psz != 0) && (StringTraits::StringFindChar( pszTargets, *psz ) != NULL) )
+ {
+ psz = StringTraits::CharNext( psz );
+ }
+
+ if( psz != this->GetString() )
+ {
+ // fix up data and length
+ int iFirst = int( psz-this->GetString() );
+ PXSTR pszBuffer = this->GetBuffer( this->GetLength() );
+ psz = pszBuffer+iFirst;
+ int nDataLength = this->GetLength()-iFirst;
+ memmove_s( pszBuffer, (this->GetLength()+1)*sizeof( XCHAR ),
+ psz, (nDataLength+1)*sizeof( XCHAR ) );
+ this->ReleaseBufferSetLength( nDataLength );
+ }
+
+ return *this;
+ }
+
+ // Convert the string to the OEM character set
+ void AnsiToOem()
+ {
+ int nLength = this->GetLength();
+ PXSTR pszBuffer = this->GetBuffer( nLength );
+ StringTraits::ConvertToOem( pszBuffer, nLength+1 );
+ this->ReleaseBufferSetLength( nLength );
+ }
+
+ // Convert the string to the ANSI character set
+ void OemToAnsi()
+ {
+ int nLength = this->GetLength();
+ PXSTR pszBuffer = this->GetBuffer( nLength );
+ StringTraits::ConvertToAnsi( pszBuffer, nLength+1 );
+ this->ReleaseBufferSetLength( nLength );
+ }
+
+ // Very simple sub-string extraction
+
+ // Return the substring starting at index 'iFirst'
+ CMStringT Mid( int iFirst ) const
+ {
+ return Mid( iFirst, this->GetLength()-iFirst );
+ }
+
+ // Return the substring starting at index 'iFirst', with length 'nCount'
+ CMStringT Mid( int iFirst, int nCount ) const
+ {
+ // nCount is in XCHARs
+
+ // out-of-bounds requests return sensible things
+ if (iFirst < 0)
+ iFirst = 0;
+ if (nCount < 0)
+ nCount = 0;
+
+ if( (iFirst + nCount) > this->GetLength() )
+ nCount = this->GetLength()-iFirst;
+
+ if( iFirst > this->GetLength() )
+ nCount = 0;
+
+ // optimize case of returning entire string
+ if( (iFirst == 0) && ((iFirst+nCount) == this->GetLength()) )
+ return *this;
+
+ return CMStringT( this->GetString()+iFirst, nCount );
+ }
+
+ // Return the substring consisting of the rightmost 'nCount' characters
+ CMStringT Right( int nCount ) const
+ {
+ // nCount is in XCHARs
+ if (nCount < 0)
+ nCount = 0;
+
+ int nLength = this->GetLength();
+ if( nCount >= nLength )
+ {
+ return *this;
+ }
+
+ return CMStringT( this->GetString()+nLength-nCount, nCount );
+ }
+
+ // Return the substring consisting of the leftmost 'nCount' characters
+ CMStringT Left( int nCount ) const
+ {
+ // nCount is in XCHARs
+ if (nCount < 0)
+ nCount = 0;
+
+ int nLength = this->GetLength();
+ if( nCount >= nLength )
+ return *this;
+
+ return CMStringT( this->GetString(), nCount );
+ }
+
+ // Return the substring consisting of the leftmost characters in the set 'pszCharSet'
+ CMStringT SpanIncluding( PCXSTR pszCharSet ) const
+ {
+ return Left( StringTraits::StringSpanIncluding( this->GetString(), pszCharSet ));
+ }
+
+ // Return the substring consisting of the leftmost characters not in the set 'pszCharSet'
+ CMStringT SpanExcluding( PCXSTR pszCharSet ) const
+ {
+ return Left( StringTraits::StringSpanExcluding( this->GetString(), pszCharSet ));
+ }
+
+ // Format data using format string 'pszFormat'
+ void Format( PCXSTR pszFormat, ... );
+
+ // Append formatted data using format string 'pszFormat'
+ void AppendFormat( PCXSTR pszFormat, ... );
+
+ void AppendFormatV( PCXSTR pszFormat, va_list args )
+ {
+ int nCurrentLength = this->GetLength();
+ int nAppendLength = StringTraits::GetFormattedLength( pszFormat, args );
+ PXSTR pszBuffer = this->GetBuffer( nCurrentLength+nAppendLength );
+ StringTraits::Format( pszBuffer+nCurrentLength,
+ nAppendLength+1, pszFormat, args );
+ this->ReleaseBufferSetLength( nCurrentLength+nAppendLength );
+ }
+
+ void FormatV( PCXSTR pszFormat, va_list args )
+ {
+ int nLength = StringTraits::GetFormattedLength( pszFormat, args );
+ PXSTR pszBuffer = this->GetBuffer( nLength );
+ StringTraits::Format( pszBuffer, nLength+1, pszFormat, args );
+ this->ReleaseBufferSetLength( nLength );
+ }
+
+ // OLE BSTR support
+
+ // Allocate a BSTR containing a copy of the string
+ BSTR AllocSysString() const
+ {
+ BSTR bstrResult = StringTraits::AllocSysString( this->GetString(), this->GetLength() );
+ return bstrResult;
+ }
+
+ BSTR SetSysString( BSTR* pbstr ) const
+ {
+ StringTraits::ReAllocSysString( this->GetString(), pbstr, this->GetLength() );
+ return *pbstr;
+ }
+
+ // Set the string to the value of environment variable 'pszVar'
+ BOOL GetEnvironmentVariable( PCXSTR pszVar )
+ {
+ ULONG nLength = StringTraits::GetEnvironmentVariable( pszVar, NULL, 0 );
+ BOOL bRetVal = FALSE;
+
+ if( nLength == 0 )
+ {
+ this->Empty();
+ }
+ else
+ {
+ PXSTR pszBuffer = this->GetBuffer( nLength );
+ StringTraits::GetEnvironmentVariable( pszVar, pszBuffer, nLength );
+ this->ReleaseBuffer();
+ bRetVal = TRUE;
+ }
+
+ return bRetVal;
+ }
+
+ // Load the string from resource 'nID'
+ BOOL LoadString( UINT nID )
+ {
+ HINSTANCE hInst = StringTraits::FindStringResourceInstance( nID );
+ if( hInst == NULL )
+ return FALSE;
+
+ return LoadString( hInst, nID );
+ }
+
+ friend CMStringT __stdcall operator+( const CMStringT& str1, const CMStringT& str2 )
+ {
+ CMStringT strResult;
+
+ Concatenate( strResult, str1, str1.GetLength(), str2, str2.GetLength() );
+
+ return strResult;
+ }
+
+ friend CMStringT __stdcall operator+( const CMStringT& str1, PCXSTR psz2 )
+ {
+ CMStringT strResult;
+
+ Concatenate( strResult, str1, str1.GetLength(), psz2, StringLength( psz2 ) );
+
+ return strResult;
+ }
+
+ friend CMStringT __stdcall operator+( PCXSTR psz1, const CMStringT& str2 )
+ {
+ CMStringT strResult;
+
+ Concatenate( strResult, psz1, StringLength( psz1 ), str2, str2.GetLength() );
+
+ return strResult;
+ }
+
+ friend CMStringT __stdcall operator+( const CMStringT& str1, wchar_t ch2 )
+ {
+ CMStringT strResult;
+ XCHAR chTemp = XCHAR( ch2 );
+
+ Concatenate( strResult, str1, str1.GetLength(), &chTemp, 1 );
+
+ return strResult;
+ }
+
+ friend CMStringT __stdcall operator+( const CMStringT& str1, char ch2 )
+ {
+ CMStringT strResult;
+ XCHAR chTemp = XCHAR( ch2 );
+
+ Concatenate( strResult, str1, str1.GetLength(), &chTemp, 1 );
+
+ return strResult;
+ }
+
+ friend CMStringT __stdcall operator+( wchar_t ch1, const CMStringT& str2 )
+ {
+ CMStringT strResult;
+ XCHAR chTemp = XCHAR( ch1 );
+
+ Concatenate( strResult, &chTemp, 1, str2, str2.GetLength() );
+
+ return strResult;
+ }
+
+ friend CMStringT __stdcall operator+( char ch1, const CMStringT& str2 )
+ {
+ CMStringT strResult;
+ XCHAR chTemp = XCHAR( ch1 );
+
+ Concatenate( strResult, &chTemp, 1, str2, str2.GetLength() );
+
+ return strResult;
+ }
+
+ friend bool __stdcall operator==( const CMStringT& str1, const CMStringT& str2 )
+ {
+ return str1.Compare( str2 ) == 0;
+ }
+
+ friend bool __stdcall operator==( const CMStringT& str1, PCXSTR psz2 )
+ {
+ return str1.Compare( psz2 ) == 0;
+ }
+
+ friend bool __stdcall operator==( PCXSTR psz1, const CMStringT& str2 )
+ {
+ return str2.Compare( psz1 ) == 0;
+ }
+
+ friend bool __stdcall operator==( const CMStringT& str1, PCYSTR psz2 )
+ {
+ CMStringT str2( psz2 );
+
+ return str1 == str2;
+ }
+
+ friend bool __stdcall operator==( PCYSTR psz1, const CMStringT& str2 )
+ {
+ CMStringT str1( psz1 );
+
+ return str1 == str2;
+ }
+
+ friend bool __stdcall operator!=( const CMStringT& str1, const CMStringT& str2 )
+ {
+ return str1.Compare( str2 ) != 0;
+ }
+
+ friend bool __stdcall operator!=( const CMStringT& str1, PCXSTR psz2 )
+ {
+ return str1.Compare( psz2 ) != 0;
+ }
+
+ friend bool __stdcall operator!=( PCXSTR psz1, const CMStringT& str2 )
+ {
+ return str2.Compare( psz1 ) != 0;
+ }
+
+ friend bool __stdcall operator!=( const CMStringT& str1, PCYSTR psz2 )
+ {
+ CMStringT str2( psz2 );
+
+ return str1 != str2;
+ }
+
+ friend bool __stdcall operator!=( PCYSTR psz1, const CMStringT& str2 )
+ {
+ CMStringT str1( psz1 );
+
+ return str1 != str2;
+ }
+
+ friend bool __stdcall operator<( const CMStringT& str1, const CMStringT& str2 )
+ {
+ return str1.Compare( str2 ) < 0;
+ }
+
+ friend bool __stdcall operator<( const CMStringT& str1, PCXSTR psz2 )
+ {
+ return str1.Compare( psz2 ) < 0;
+ }
+
+ friend bool __stdcall operator<( PCXSTR psz1, const CMStringT& str2 )
+ {
+ return str2.Compare( psz1 ) > 0;
+ }
+
+ friend bool __stdcall operator>( const CMStringT& str1, const CMStringT& str2 )
+ {
+ return str1.Compare( str2 ) > 0;
+ }
+
+ friend bool __stdcall operator>( const CMStringT& str1, PCXSTR psz2 )
+ {
+ return str1.Compare( psz2 ) > 0;
+ }
+
+ friend bool __stdcall operator>( PCXSTR psz1, const CMStringT& str2 )
+ {
+ return str2.Compare( psz1 ) < 0;
+ }
+
+ friend bool __stdcall operator<=( const CMStringT& str1, const CMStringT& str2 )
+ {
+ return str1.Compare( str2 ) <= 0;
+ }
+
+ friend bool __stdcall operator<=( const CMStringT& str1, PCXSTR psz2 )
+ {
+ return str1.Compare( psz2 ) <= 0;
+ }
+
+ friend bool __stdcall operator<=( PCXSTR psz1, const CMStringT& str2 )
+ {
+ return str2.Compare( psz1 ) >= 0;
+ }
+
+ friend bool __stdcall operator>=( const CMStringT& str1, const CMStringT& str2 )
+ {
+ return str1.Compare( str2 ) >= 0;
+ }
+
+ friend bool __stdcall operator>=( const CMStringT& str1, PCXSTR psz2 )
+ {
+ return str1.Compare( psz2 ) >= 0;
+ }
+
+ friend bool __stdcall operator>=( PCXSTR psz1, const CMStringT& str2 )
+ {
+ return str2.Compare( psz1 ) <= 0;
+ }
+
+ friend bool __stdcall operator==( XCHAR ch1, const CMStringT& str2 )
+ {
+ return (str2.GetLength() == 1) && (str2[0] == ch1);
+ }
+
+ friend bool __stdcall operator==( const CMStringT& str1, XCHAR ch2 )
+ {
+ return (str1.GetLength() == 1) && (str1[0] == ch2);
+ }
+
+ friend bool __stdcall operator!=( XCHAR ch1, const CMStringT& str2 )
+ {
+ return (str2.GetLength() != 1) || (str2[0] != ch1);
+ }
+
+ friend bool __stdcall operator!=( const CMStringT& str1, XCHAR ch2 )
+ {
+ return (str1.GetLength() != 1) || (str1[0] != ch2);
+ }
+};
+
+template< typename BaseType, class StringTraits >
+inline void CMStringT<BaseType, StringTraits>::Format(PCXSTR pszFormat, ... )
+{
+ va_list argList;
+ va_start( argList, pszFormat );
+ FormatV( pszFormat, argList );
+ va_end( argList );
+}
+
+template< typename BaseType, class StringTraits >
+inline void CMStringT<BaseType, StringTraits>::AppendFormat(PCXSTR pszFormat, ... )
+{
+ va_list argList;
+ va_start( argList, pszFormat );
+ AppendFormatV( pszFormat, argList );
+ va_end( argList );
+}
+
+typedef CMStringT< wchar_t, ChTraitsCRT< wchar_t > > CMStringW;
+typedef CMStringT< char, ChTraitsCRT< char > > CMStringA;
+typedef CMStringT< TCHAR, ChTraitsCRT< TCHAR > > CMString;