/* Miranda NG: the free IM client for Microsoft* Windows* Copyright (c) 2012-14 Miranda NG project (http://miranda-ng.org) all portions of this codebase are copyrighted to the people listed in contributors.txt. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #pragma once #include #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 Langpack_GetDefaultCodePage */ #define Langpack_GetDefaultCodePage() 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; MIR_CORE_DLL(CMStringData*) mirstr_allocate(int nChars, int nCharSize); MIR_CORE_DLL(void) mirstr_free(CMStringData *pData); MIR_CORE_DLL(CMStringData*) mirstr_realloc(CMStringData* pData, int nChars, int nCharSize); MIR_CORE_DLL(CMStringData*) mirstr_getNil(); MIR_CORE_DLL(void) mirstr_lock(CMStringData* pThis); MIR_CORE_DLL(void) mirstr_release(CMStringData* pThis); MIR_CORE_DLL(void) mirstr_unlock(CMStringData* pThis); ///////////////////////////////////////////////////////////////////////////////////////// enum CMStringDataFormat { FORMAT }; 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 __forceinline void* data() { return (this + 1); } __forceinline void AddRef() { InterlockedIncrement(&nRefs); } __forceinline bool IsLocked() const { return nRefs < 0; } __forceinline bool IsShared() const { return nRefs > 1; } __forceinline void Lock() { mirstr_lock(this); } __forceinline void Release() { mirstr_release(this); } __forceinline void Unlock() { mirstr_unlock(this); } }; 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; }; template< typename BaseType > class CMSimpleStringT { 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 = mirstr_getNil(); 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 = mirstr_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 = mirstr_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 = UINT_PTR(pszSrc - GetString()); int 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 <= UINT_PTR(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 = mirstr_getNil(); 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 = mirstr_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; } PCXSTR GetTail() const { return m_pszData + GetData()->nDataLength; } 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) { memcpy_s(pchDest, nDestLen * sizeof(XCHAR), pchSrc, nChars * sizeof(XCHAR)); } 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) { memmove_s(pchDest, nDestLen * sizeof(XCHAR), pchSrc, nChars * sizeof(XCHAR)); } 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 = mirstr_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 = mirstr_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, 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(Langpack_GetDefaultCodePage(), 0, pszSource, -1, NULL, 0, NULL, NULL)-1; } static int __stdcall GetBaseTypeLength(LPCWSTR pszSource, int nLength) { // Returns required buffer length in XCHARs return ::WideCharToMultiByte(Langpack_GetDefaultCodePage(), 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(Langpack_GetDefaultCodePage(), 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(Langpack_GetDefaultCodePage(), 0, pchData, nDataLength, NULL, NULL); BSTR bstr = ::SysAllocStringLen(NULL, nLen); if (bstr != NULL) ::MultiByteToWideChar(Langpack_GetDefaultCodePage(), 0, pchData, nDataLength, bstr, nLen); return bstr; } static BOOL __stdcall ReAllocSysString(const char* pchData, BSTR* pbstr, int nDataLength) { int nLen = ::MultiByteToWideChar(Langpack_GetDefaultCodePage(), 0, pchData, nDataLength, NULL, NULL); BOOL bSuccess = ::SysReAllocStringLen(pbstr, NULL, nLen); if (bSuccess) ::MultiByteToWideChar(Langpack_GetDefaultCodePage(), 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() { *this = pszSrc; } CMStringT(CMStringDataFormat, const XCHAR* pszFormat, ...) : CThisSimpleString() { va_list args; va_start(args, pszFormat); FormatV(pszFormat, args); } CMStringT(const YCHAR* pszSrc) : CThisSimpleString() { *this = pszSrc; } CMStringT(const unsigned char* pszSrc) : CThisSimpleString() { *this = reinterpret_cast(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(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; } 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; for (size_t 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' PCXSTR Format(PCXSTR pszFormat, ...) { va_list argList; va_start(argList, pszFormat); FormatV(pszFormat, argList); va_end(argList); return GetString(); } // Append formatted data using format string 'pszFormat' PCXSTR AppendFormat(PCXSTR pszFormat, ...) { va_list argList; va_start(argList, pszFormat); AppendFormatV(pszFormat, argList); va_end(argList); return GetString(); } 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); } PCXSTR 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); return GetString(); } // 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); } }; typedef CMStringT< wchar_t, ChTraitsCRT< wchar_t > > CMStringW; typedef CMStringT< char, ChTraitsCRT< char > > CMStringA; typedef CMStringT< TCHAR, ChTraitsCRT< TCHAR > > CMString; ///////////////////////////////////////////////////////////////////////////////////////// // ChTraitsCRT __forceinline int ChTraitsCRT::GetFormattedLength(LPCWSTR pszFormat, va_list args) { return _vscwprintf(pszFormat, args); } __forceinline int ChTraitsCRT::Format(LPWSTR pszBuffer, size_t nLength, LPCWSTR pszFormat, va_list args) { return _vsnwprintf(pszBuffer, nLength, pszFormat, args); } ///////////////////////////////////////////////////////////////////////////////////////// // ChTraitsCRT __forceinline int ChTraitsCRT::GetFormattedLength(LPCSTR pszFormat, va_list args) { return _vscprintf(pszFormat, args); } __forceinline int ChTraitsCRT::Format(LPSTR pszBuffer, size_t nlength, LPCSTR pszFormat, va_list args) { return vsprintf_s(pszBuffer, nlength, pszFormat, args); }