#pragma once #include #include #include #ifdef __MINGW32__ #include __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&() { return *(CMSimpleStringT*)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(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(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 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(size); BOOL fSuccess=::CharToOemBuffA(pstrString, pstrString, dwSize); } static void ConvertToAnsi( _CharType* pstrString, size_t size) { if(size>UINT_MAX) return; DWORD dwSize=static_cast(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(ch)); } static int __stdcall IsSpace( wchar_t ch ) { return iswspace( static_cast(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::Format(PCXSTR pszFormat, ... ) { va_list argList; va_start( argList, pszFormat ); FormatV( pszFormat, argList ); va_end( argList ); } template< typename BaseType, class StringTraits > inline void CMStringT::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;