#pragma warning (disable:4786) // This is a part of the Microsoft Foundation Classes C++ library. // Copyright (C) 1992-1995 Microsoft Corporation // All rights reserved. // // This source code is only intended as a supplement to the // Microsoft Foundation Classes Reference and related // electronic documentation provided with the library. // See these sources for detailed information regarding the // Microsoft Foundation Classes product. #include "comutil.hpp" #include #include ///////////////////////////////////////////////////////////////////////////// // static class data, special inlines // comline_afxChNil is left for backward compatibility TCHAR comline_afxChNil = '\0'; // For an empty string, m_pchData will point here // (note: avoids special case of checking for NULL m_pchData) // empty string data (and locked) static int rgInitData[] = { -1, 0, 0, 0 }; static CCom_StringData* comline_afxDataNil = (CCom_StringData*)&rgInitData; static LPCTSTR comline_afxPchNil = (LPCTSTR)(((BYTE*)&rgInitData)+sizeof(CCom_StringData)); // special function to make comline_afxEmptyString work even during initialization const CCom_String& comline_AfxGetEmptyString() { return *(CCom_String*)&comline_afxPchNil; } __declspec (nothrow) bool comline_AfxIsValidString(LPCWSTR lpsz, int nLength) { if (lpsz == NULL) return false; return ::IsBadStringPtrW(lpsz, nLength) == 0; } __declspec (nothrow) bool comline_AfxIsValidString(LPCSTR lpsz, int nLength) { if (lpsz == NULL) return false; return ::IsBadStringPtrA(lpsz, nLength) == 0; } __declspec (nothrow) bool comline_AfxIsValidAddress(const void* lp, UINT nBytes, bool bReadWrite) { // simple version using Win-32 APIs for pointer validation. return (lp != NULL && !IsBadReadPtr(lp, nBytes) && (!bReadWrite || !IsBadWritePtr((LPVOID)lp, nBytes))); } ////////////////////////////////////////////////////////////////////////////// // Construction/Destruction __declspec (nothrow) CCom_String::CCom_String() { Init(); } __declspec (nothrow) CCom_String::CCom_String(const CCom_String& stringSrc) { assert(stringSrc.GetData()->nRefs != 0); if (stringSrc.GetData()->nRefs >= 0) { assert(stringSrc.GetData() != comline_afxDataNil); m_pchData = stringSrc.m_pchData; InterlockedIncrement(&GetData()->nRefs); } else { Init(); *this = stringSrc.m_pchData; } } __declspec (nothrow) void CCom_String::AllocBuffer(int nLen) // always allocate one extra character for '\0' termination // assumes [optimistically] that data length will equal allocation length { assert(nLen >= 0); assert(nLen <= INT_MAX-1); // max size (enough room for 1 extra) if (nLen == 0) Init(); else { CCom_StringData* pData = (CCom_StringData*)malloc (sizeof(CCom_StringData) + (nLen+1)*sizeof(TCHAR)); pData->nRefs = 1; pData->data()[nLen] = '\0'; pData->nDataLength = nLen; pData->nAllocLength = nLen; m_pchData = pData->data(); } } __declspec (nothrow) void CCom_String::Release() { if (GetData() != comline_afxDataNil) { assert(GetData()->nRefs != 0); if (InterlockedDecrement(&GetData()->nRefs) <= 0) free (GetData()); Init(); } } __declspec (nothrow) void PASCAL CCom_String::Release(CCom_StringData* pData) { if (pData != comline_afxDataNil) { assert(pData->nRefs != 0); if (InterlockedDecrement(&pData->nRefs) <= 0) free (pData); } } __declspec (nothrow) void CCom_String::Empty() { if (GetData()->nDataLength == 0) return; if (GetData()->nRefs >= 0) Release(); else *this = &comline_afxChNil; assert(GetData()->nDataLength == 0); assert(GetData()->nRefs < 0 || GetData()->nAllocLength == 0); } __declspec (nothrow) void CCom_String::CopyBeforeWrite() { if (GetData()->nRefs > 1) { CCom_StringData* pData = GetData(); Release(); AllocBuffer(pData->nDataLength); memcpy(m_pchData, pData->data(), (pData->nDataLength+1)*sizeof(TCHAR)); } assert(GetData()->nRefs <= 1); } __declspec (nothrow) void CCom_String::AllocBeforeWrite(int nLen) { if (GetData()->nRefs > 1 || nLen > GetData()->nAllocLength) { Release(); AllocBuffer(nLen); } assert(GetData()->nRefs <= 1); } __declspec (nothrow) CCom_String::~CCom_String() // free any attached data { if (GetData() != comline_afxDataNil) { if (InterlockedDecrement(&GetData()->nRefs) <= 0) free (GetData()); } } ////////////////////////////////////////////////////////////////////////////// // Helpers for the rest of the implementation __declspec (nothrow) void CCom_String::AllocCopy(CCom_String& dest, int nCopyLen, int nCopyIndex, int nExtraLen) const { // will clone the data attached to this string // allocating 'nExtraLen' characters // Places results in uninitialized string 'dest' // Will copy the part or all of original data to start of new string int nNewLen = nCopyLen + nExtraLen; if (nNewLen == 0) { dest.Init(); } else { dest.AllocBuffer(nNewLen); memcpy(dest.m_pchData, m_pchData+nCopyIndex, nCopyLen*sizeof(TCHAR)); } } ////////////////////////////////////////////////////////////////////////////// // More sophisticated construction __declspec (nothrow) CCom_String::CCom_String(LPCTSTR lpsz) { Init(); { int nLen = SafeStrlen(lpsz); if (nLen != 0) { AllocBuffer(nLen); memcpy(m_pchData, lpsz, nLen*sizeof(TCHAR)); } } } ///////////////////////////////////////////////////////////////////////////// // Special conversion constructors #ifdef _UNICODE __declspec (nothrow) CCom_String::CCom_String(LPCSTR lpsz) { Init(); int nSrcLen = lpsz != NULL ? lstrlenA(lpsz) : 0; if (nSrcLen != 0) { AllocBuffer(nSrcLen); _mbstowcsz(m_pchData, lpsz, nSrcLen+1); ReleaseBuffer(); } } #else //_UNICODE __declspec (nothrow) CCom_String::CCom_String(LPCWSTR lpsz) { Init(); int nSrcLen = lpsz != NULL ? wcslen(lpsz) : 0; if (nSrcLen != 0) { AllocBuffer(nSrcLen*2); _wcstombsz(m_pchData, lpsz, (nSrcLen*2)+1); ReleaseBuffer(); } } #endif //!_UNICODE ////////////////////////////////////////////////////////////////////////////// // Assignment operators // All assign a new value to the string // (a) first see if the buffer is big enough // (b) if enough room, copy on top of old buffer, set size and type // (c) otherwise free old string data, and create a new one // // All routines return the new string (but as a 'const CCom_String&' so that // assigning it again will cause a copy, eg: s1 = s2 = "hi there". // __declspec (nothrow) void CCom_String::AssignCopy(int nSrcLen, LPCTSTR lpszSrcData) { AllocBeforeWrite(nSrcLen); memcpy(m_pchData, lpszSrcData, nSrcLen*sizeof(TCHAR)); GetData()->nDataLength = nSrcLen; m_pchData[nSrcLen] = '\0'; } __declspec (nothrow) const CCom_String& CCom_String::operator=(const CCom_String& stringSrc) { if (m_pchData != stringSrc.m_pchData) { if ((GetData()->nRefs < 0 && GetData() != comline_afxDataNil) || stringSrc.GetData()->nRefs < 0) { // actual copy necessary since one of the strings is locked AssignCopy(stringSrc.GetData()->nDataLength, stringSrc.m_pchData); } else { // can just copy references around Release(); assert(stringSrc.GetData() != comline_afxDataNil); m_pchData = stringSrc.m_pchData; InterlockedIncrement(&GetData()->nRefs); } } return *this; } __declspec (nothrow) const CCom_String& CCom_String::operator=(LPCTSTR lpsz) { assert(lpsz == NULL || comline_AfxIsValidString(lpsz, false)); AssignCopy(SafeStrlen(lpsz), lpsz); return *this; } ///////////////////////////////////////////////////////////////////////////// // Special conversion assignment #ifdef _UNICODE __declspec (nothrow) const CCom_String& CCom_String::operator=(LPCSTR lpsz) { int nSrcLen = lpsz != NULL ? lstrlenA(lpsz) : 0; AllocBeforeWrite(nSrcLen); _mbstowcsz(m_pchData, lpsz, nSrcLen+1); ReleaseBuffer(); return *this; } #else //!_UNICODE __declspec (nothrow) const CCom_String& CCom_String::operator=(LPCWSTR lpsz) { int nSrcLen = lpsz != NULL ? wcslen(lpsz) : 0; AllocBeforeWrite(nSrcLen*2); _wcstombsz(m_pchData, lpsz, (nSrcLen*2)+1); ReleaseBuffer(); return *this; } #endif //!_UNICODE ////////////////////////////////////////////////////////////////////////////// // concatenation // NOTE: "operator+" is done as friend functions for simplicity // There are three variants: // CCom_String + CCom_String // and for ? = TCHAR, LPCTSTR // CCom_String + ? // ? + CCom_String __declspec (nothrow) void CCom_String::ConcatCopy(int nSrc1Len, LPCTSTR lpszSrc1Data, int nSrc2Len, LPCTSTR lpszSrc2Data) { // -- master concatenation routine // Concatenate two sources // -- assume that 'this' is a new CCom_String object int nNewLen = nSrc1Len + nSrc2Len; if (nNewLen != 0) { AllocBuffer(nNewLen); memcpy(m_pchData, lpszSrc1Data, nSrc1Len*sizeof(TCHAR)); memcpy(m_pchData+nSrc1Len, lpszSrc2Data, nSrc2Len*sizeof(TCHAR)); } } __declspec (nothrow) CCom_String operator+(const CCom_String& string1, const CCom_String& string2) { CCom_String s; s.ConcatCopy(string1.GetData()->nDataLength, string1.m_pchData, string2.GetData()->nDataLength, string2.m_pchData); return s; } __declspec (nothrow) CCom_String operator+(const CCom_String& string, LPCTSTR lpsz) { assert(lpsz == NULL || comline_AfxIsValidString(lpsz, false)); CCom_String s; s.ConcatCopy(string.GetData()->nDataLength, string.m_pchData, CCom_String::SafeStrlen(lpsz), lpsz); return s; } __declspec (nothrow) CCom_String operator+(LPCTSTR lpsz, const CCom_String& string) { assert(lpsz == NULL || comline_AfxIsValidString(lpsz, false)); CCom_String s; s.ConcatCopy(CCom_String::SafeStrlen(lpsz), lpsz, string.GetData()->nDataLength, string.m_pchData); return s; } ////////////////////////////////////////////////////////////////////////////// // concatenate in place __declspec (nothrow) void CCom_String::ConcatInPlace(int nSrcLen, LPCTSTR lpszSrcData) { // -- the main routine for += operators // concatenating an empty string is a no-op! if (nSrcLen == 0) return; // if the buffer is too small, or we have a width mis-match, just // allocate a new buffer (slow but sure) if (GetData()->nRefs > 1 || GetData()->nDataLength + nSrcLen > GetData()->nAllocLength) { // we have to grow the buffer, use the ConcatCopy routine CCom_StringData* pOldData = GetData(); ConcatCopy(GetData()->nDataLength, m_pchData, nSrcLen, lpszSrcData); assert(pOldData != NULL); CCom_String::Release(pOldData); } else { // fast concatenation when buffer big enough memcpy(m_pchData+GetData()->nDataLength, lpszSrcData, nSrcLen*sizeof(TCHAR)); GetData()->nDataLength += nSrcLen; assert(GetData()->nDataLength <= GetData()->nAllocLength); m_pchData[GetData()->nDataLength] = '\0'; } } __declspec (nothrow) const CCom_String& CCom_String::operator+=(LPCTSTR lpsz) { assert(lpsz == NULL || comline_AfxIsValidString(lpsz, false)); ConcatInPlace(SafeStrlen(lpsz), lpsz); return *this; } __declspec (nothrow) const CCom_String& CCom_String::operator+=(TCHAR ch) { ConcatInPlace(1, &ch); return *this; } __declspec (nothrow) const CCom_String& CCom_String::operator+=(const CCom_String& string) { ConcatInPlace(string.GetData()->nDataLength, string.m_pchData); return *this; } /////////////////////////////////////////////////////////////////////////////// // Advanced direct buffer access __declspec (nothrow) LPTSTR CCom_String::GetBuffer(int nMinBufLength) { assert(nMinBufLength >= 0); if (GetData()->nRefs > 1 || nMinBufLength > GetData()->nAllocLength) { // we have to grow the buffer CCom_StringData* pOldData = GetData(); int nOldLen = GetData()->nDataLength; // AllocBuffer will tromp it if (nMinBufLength < nOldLen) nMinBufLength = nOldLen; AllocBuffer(nMinBufLength); memcpy(m_pchData, pOldData->data(), (nOldLen+1)*sizeof(TCHAR)); GetData()->nDataLength = nOldLen; CCom_String::Release(pOldData); } assert(GetData()->nRefs <= 1); // return a pointer to the character storage for this string assert(m_pchData != NULL); return m_pchData; } __declspec (nothrow) void CCom_String::ReleaseBuffer(int nNewLength) { CopyBeforeWrite(); // just in case GetBuffer was not called if (nNewLength == -1) nNewLength = lstrlen(m_pchData); // zero terminated assert(nNewLength <= GetData()->nAllocLength); GetData()->nDataLength = nNewLength; m_pchData[nNewLength] = '\0'; } __declspec (nothrow) LPTSTR CCom_String::GetBufferSetLength(int nNewLength) { assert(nNewLength >= 0); GetBuffer(nNewLength); GetData()->nDataLength = nNewLength; m_pchData[nNewLength] = '\0'; return m_pchData; } __declspec (nothrow) void CCom_String::FreeExtra() { assert(GetData()->nDataLength <= GetData()->nAllocLength); if (GetData()->nDataLength != GetData()->nAllocLength) { CCom_StringData* pOldData = GetData(); AllocBuffer(GetData()->nDataLength); memcpy(m_pchData, pOldData->data(), pOldData->nDataLength*sizeof(TCHAR)); assert(m_pchData[GetData()->nDataLength] == '\0'); CCom_String::Release(pOldData); } assert(GetData() != NULL); } __declspec (nothrow) LPTSTR CCom_String::LockBuffer() { LPTSTR lpsz = GetBuffer(0); GetData()->nRefs = -1; return lpsz; } __declspec (nothrow) void CCom_String::UnlockBuffer() { assert(GetData()->nRefs == -1); if (GetData() != comline_afxDataNil) GetData()->nRefs = 1; } /////////////////////////////////////////////////////////////////////////////// // Commonly used routines (rarely used routines in STREX.CPP) __declspec (nothrow) int CCom_String::Find(TCHAR ch) const { // find first single character LPTSTR lpsz = _tcschr(m_pchData, (_TUCHAR)ch); // return -1 if not found and index otherwise return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData); } __declspec (nothrow) int CCom_String::FindOneOf(LPCTSTR lpszCharSet) const { assert(comline_AfxIsValidString(lpszCharSet, false)); LPTSTR lpsz = _tcspbrk(m_pchData, lpszCharSet); return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData); } __declspec (nothrow) void CCom_String::MakeUpper() { CopyBeforeWrite(); ::CharUpper(m_pchData); } __declspec (nothrow) void CCom_String::MakeLower() { CopyBeforeWrite(); ::CharLower(m_pchData); } __declspec (nothrow) void CCom_String::MakeReverse() { CopyBeforeWrite(); _tcsrev(m_pchData); } __declspec (nothrow) void CCom_String::SetAt(int nIndex, TCHAR ch) { assert(nIndex >= 0); assert(nIndex < GetData()->nDataLength); CopyBeforeWrite(); m_pchData[nIndex] = ch; } #ifndef _UNICODE __declspec (nothrow) void CCom_String::AnsiToOem() { CopyBeforeWrite(); ::AnsiToOem(m_pchData, m_pchData); } __declspec (nothrow) void CCom_String::OemToAnsi() { CopyBeforeWrite(); ::OemToAnsi(m_pchData, m_pchData); } #endif /////////////////////////////////////////////////////////////////////////////// // CCom_String conversion helpers (these use the current system locale) __declspec (nothrow) int _cdecl _wcstombsz(char* mbstr, const wchar_t* wcstr, size_t count) { if (count == 0 && mbstr != NULL) return 0; int result = ::WideCharToMultiByte(CP_ACP, 0, wcstr, -1, mbstr, count, NULL, NULL); assert(mbstr == NULL || result <= (int)count); if (result > 0) mbstr[result-1] = 0; return result; } __declspec (nothrow) int _cdecl _mbstowcsz(wchar_t* wcstr, const char* mbstr, size_t count) { if (count == 0 && wcstr != NULL) return 0; int result = ::MultiByteToWideChar(CP_ACP, 0, mbstr, -1, wcstr, count); assert(wcstr == NULL || result <= (int)count); if (result > 0) wcstr[result-1] = 0; return result; } __declspec (nothrow) LPWSTR comline_AfxA2WHelper(LPWSTR lpw, LPCSTR lpa, int nChars) { if (lpa == NULL) return NULL; assert(lpw != NULL); // verify that no illegal character present // since lpw was allocated based on the size of lpa // don't worry about the number of chars lpw[0] = '\0'; (MultiByteToWideChar(CP_ACP, 0, lpa, -1, lpw, nChars)); return lpw; } __declspec (nothrow) LPSTR comline_AfxW2AHelper(LPSTR lpa, LPCWSTR lpw, int nChars) { if (lpw == NULL) return NULL; assert(lpa != NULL); // verify that no illegal character present // since lpa was allocated based on the size of lpw // don't worry about the number of chars lpa[0] = '\0'; (WideCharToMultiByte(CP_ACP, 0, lpw, -1, lpa, nChars, NULL, NULL)); return lpa; } /////////////////////////////////////////////////////////////////////////////// __declspec (nothrow) CCom_String::CCom_String(TCHAR ch, int nLength) { assert(!_istlead(ch)); // can't create a lead byte string Init(); if (nLength >= 1) { AllocBuffer(nLength); #ifdef _UNICODE for (int i = 0; i < nLength; i++) m_pchData[i] = ch; #else memset(m_pchData, ch, nLength); #endif } } __declspec (nothrow) CCom_String::CCom_String(LPCTSTR lpch, int nLength) { Init(); if (nLength != 0) { assert(comline_AfxIsValidAddress(lpch, nLength, false)); AllocBuffer(nLength); memcpy(m_pchData, lpch, nLength*sizeof(TCHAR)); } } ////////////////////////////////////////////////////////////////////////////// // Assignment operators __declspec (nothrow) const CCom_String& CCom_String::operator=(TCHAR ch) { assert(!_istlead(ch)); // can't set single lead byte AssignCopy(1, &ch); return *this; } ////////////////////////////////////////////////////////////////////////////// // less common string expressions __declspec (nothrow) CCom_String operator+(const CCom_String& string1, TCHAR ch) { CCom_String s; s.ConcatCopy(string1.GetData()->nDataLength, string1.m_pchData, 1, &ch); return s; } __declspec (nothrow) CCom_String operator+(TCHAR ch, const CCom_String& string) { CCom_String s; s.ConcatCopy(1, &ch, string.GetData()->nDataLength, string.m_pchData); return s; } ////////////////////////////////////////////////////////////////////////////// // Very simple sub-string extraction __declspec (nothrow) CCom_String CCom_String::Mid(int nFirst) const { return Mid(nFirst, GetData()->nDataLength - nFirst); } __declspec (nothrow) CCom_String CCom_String::Mid(int nFirst, int nCount) const { // out-of-bounds requests return sensible things if (nFirst < 0) nFirst = 0; if (nCount < 0) nCount = 0; if (nFirst + nCount > GetData()->nDataLength) nCount = GetData()->nDataLength - nFirst; if (nFirst > GetData()->nDataLength) nCount = 0; CCom_String dest; AllocCopy(dest, nCount, nFirst, 0); return dest; } __declspec (nothrow) CCom_String CCom_String::Right(int nCount) const { if (nCount < 0) nCount = 0; else if (nCount > GetData()->nDataLength) nCount = GetData()->nDataLength; CCom_String dest; AllocCopy(dest, nCount, GetData()->nDataLength-nCount, 0); return dest; } __declspec (nothrow) CCom_String CCom_String::Left(int nCount) const { if (nCount < 0) nCount = 0; else if (nCount > GetData()->nDataLength) nCount = GetData()->nDataLength; CCom_String dest; AllocCopy(dest, nCount, 0, 0); return dest; } // strspn equivalent __declspec (nothrow) CCom_String CCom_String::SpanIncluding(LPCTSTR lpszCharSet) const { assert(comline_AfxIsValidString(lpszCharSet, false)); return Left(_tcsspn(m_pchData, lpszCharSet)); } // strcspn equivalent __declspec (nothrow) CCom_String CCom_String::SpanExcluding(LPCTSTR lpszCharSet) const { assert(comline_AfxIsValidString(lpszCharSet, false)); return Left(_tcscspn(m_pchData, lpszCharSet)); } ////////////////////////////////////////////////////////////////////////////// // Finding __declspec (nothrow) int CCom_String::ReverseFind(TCHAR ch) const { // find last single character LPTSTR lpsz = _tcsrchr(m_pchData, (_TUCHAR)ch); // return -1 if not found, distance from beginning otherwise return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData); } // find a sub-string (like strstr) __declspec (nothrow) int CCom_String::Find(LPCTSTR lpszSub) const { assert(comline_AfxIsValidString(lpszSub, false)); // find first matching substring LPTSTR lpsz = _tcsstr(m_pchData, lpszSub); // return -1 for not found, distance from beginning otherwise return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData); } ///////////////////////////////////////////////////////////////////////////// // CCom_String formatting #ifdef _MAC #define TCHAR_ARG int #define WCHAR_ARG unsigned #define CHAR_ARG int #else #define TCHAR_ARG TCHAR #define WCHAR_ARG WCHAR #define CHAR_ARG char #endif #if defined(_68K_) || defined(_X86_) #define DOUBLE_ARG _AFX_DOUBLE #else #define DOUBLE_ARG double #endif #define FORCE_ANSI 0x10000 #define FORCE_UNICODE 0x20000 __declspec (nothrow) void CCom_String::FormatV(LPCTSTR lpszFormat, va_list argList) { assert(comline_AfxIsValidString(lpszFormat, false)); va_list argListSave = argList; // make a guess at the maximum length of the resulting string int nMaxLen = 0; for (LPCTSTR lpsz = lpszFormat; *lpsz != '\0'; lpsz = _tcsinc(lpsz)) { // handle '%' character, but watch out for '%%' if (*lpsz != '%' || *(lpsz = _tcsinc(lpsz)) == '%') { nMaxLen += _tclen(lpsz); continue; } int nItemLen = 0; // handle '%' character with format int nWidth = 0; for (; *lpsz != '\0'; lpsz = _tcsinc(lpsz)) { // check for valid flags if (*lpsz == '#') nMaxLen += 2; // for '0x' else if (*lpsz == '*') nWidth = va_arg(argList, int); else if (*lpsz == '-' || *lpsz == '+' || *lpsz == '0' || *lpsz == ' ') ; else // hit non-flag character break; } // get width and skip it if (nWidth == 0) { // width indicated by nWidth = _ttoi(lpsz); for (; *lpsz != '\0' && _istdigit(*lpsz); lpsz = _tcsinc(lpsz)) ; } assert(nWidth >= 0); int nPrecision = 0; if (*lpsz == '.') { // skip past '.' separator (width.precision) lpsz = _tcsinc(lpsz); // get precision and skip it if (*lpsz == '*') { nPrecision = va_arg(argList, int); lpsz = _tcsinc(lpsz); } else { nPrecision = _ttoi(lpsz); for (; *lpsz != '\0' && _istdigit(*lpsz); lpsz = _tcsinc(lpsz)) ; } assert(nPrecision >= 0); } // should be on type modifier or specifier int nModifier = 0; switch (*lpsz) { // modifiers that affect size case 'h': nModifier = FORCE_ANSI; lpsz = _tcsinc(lpsz); break; case 'l': nModifier = FORCE_UNICODE; lpsz = _tcsinc(lpsz); break; // modifiers that do not affect size case 'F': case 'N': case 'L': lpsz = _tcsinc(lpsz); break; } // now should be on specifier switch (*lpsz | nModifier) { // single characters case 'c': case 'C': nItemLen = 2; va_arg(argList, TCHAR_ARG); break; case 'c'|FORCE_ANSI: case 'C'|FORCE_ANSI: nItemLen = 2; va_arg(argList, CHAR_ARG); break; case 'c'|FORCE_UNICODE: case 'C'|FORCE_UNICODE: nItemLen = 2; va_arg(argList, WCHAR_ARG); break; // strings case 's': nItemLen = lstrlen(va_arg(argList, LPCTSTR)); nItemLen = max(1, nItemLen); break; case 'S': #ifndef _UNICODE nItemLen = wcslen(va_arg(argList, LPWSTR)); #else nItemLen = lstrlenA(va_arg(argList, LPCSTR)); #endif nItemLen = max(1, nItemLen); break; case 's'|FORCE_ANSI: case 'S'|FORCE_ANSI: nItemLen = lstrlenA(va_arg(argList, LPCSTR)); nItemLen = max(1, nItemLen); break; #ifndef _MAC case 's'|FORCE_UNICODE: case 'S'|FORCE_UNICODE: nItemLen = wcslen(va_arg(argList, LPWSTR)); nItemLen = max(1, nItemLen); break; #endif } // adjust nItemLen for strings if (nItemLen != 0) { nItemLen = max(nItemLen, nWidth); if (nPrecision != 0) nItemLen = min(nItemLen, nPrecision); } else { switch (*lpsz) { // integers case 'd': case 'i': case 'u': case 'x': case 'X': case 'o': va_arg(argList, int); nItemLen = 32; nItemLen = max(nItemLen, nWidth+nPrecision); break; case 'e': case 'f': case 'g': case 'G': va_arg(argList, double); nItemLen = 128; nItemLen = max(nItemLen, nWidth+nPrecision); break; case 'p': va_arg(argList, void*); nItemLen = 32; nItemLen = max(nItemLen, nWidth+nPrecision); break; // no output case 'n': va_arg(argList, int*); break; default: assert(false); // unknown formatting option } } // adjust nMaxLen for output nItemLen nMaxLen += nItemLen; } GetBuffer(nMaxLen); (_vstprintf(m_pchData, lpszFormat, argListSave) <= GetAllocLength()); ReleaseBuffer(); va_end(argListSave); } __declspec (nothrow) void _cdecl CCom_String::FormatHex (char *mem,int count) { char buf[20]; int value; buf[3] = 0; while (count > 0) { value = ((*mem >> 4) & 0x0f); switch (value) { case 0:case 1:case 2:case 3:case 4:case 5:case 6:case 7:case 8:case 9: *this += (char) (value +'0'); break; case 10:case 11:case 12:case 13:case 14:case 15: *this += (char) (value-10+'A'); break; } value = ((*mem) & 0x0f); switch (value) { case 0:case 1:case 2:case 3:case 4:case 5:case 6:case 7:case 8:case 9: *this += (char) (value +'0'); break; case 10:case 11:case 12:case 13:case 14:case 15: *this += (char) (value-10+'A'); break; } *this += ' '; mem ++; count--; } } // formatting (using wsprintf style formatting) __declspec (nothrow) void _cdecl CCom_String::Format(LPCTSTR lpszFormat, ...) { assert(comline_AfxIsValidString(lpszFormat, false)); va_list argList; va_start(argList, lpszFormat); FormatV(lpszFormat, argList); va_end(argList); } __declspec (nothrow) void _cdecl CCom_String::Format(UINT nFormatID,HINSTANCE inst, ...) { CCom_String strFormat; (strFormat.LoadString(nFormatID,inst) != 0); va_list argList; // va_start(argList, nFormatID); va_start(argList, inst); FormatV(strFormat, argList); va_end(argList); } #ifndef _MAC // formatting (using FormatMessage style formatting) void _cdecl CCom_String::FormatMessage(LPCTSTR lpszFormat, ...) { // format message into temporary buffer lpszTemp va_list argList; va_start(argList, lpszFormat); LPTSTR lpszTemp; ::FormatMessage(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ALLOCATE_BUFFER, lpszFormat, 0, 0, (LPTSTR)&lpszTemp, 0, &argList); if (lpszTemp == NULL) return; // assign lpszTemp into the resulting string and free the temporary *this = lpszTemp; LocalFree(lpszTemp); va_end(argList); } void _cdecl CCom_String::FormatMessage(UINT nFormatID,HINSTANCE inst, ...) { // get format string from string table CCom_String strFormat; (strFormat.LoadString(nFormatID,inst) != 0); // format message into temporary buffer lpszTemp va_list argList; va_start(argList, nFormatID); LPTSTR lpszTemp; ::FormatMessage(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ALLOCATE_BUFFER, strFormat, 0, 0, (LPTSTR)&lpszTemp, 0, &argList); if (lpszTemp == NULL) return; // assign lpszTemp into the resulting string and free lpszTemp *this = lpszTemp; LocalFree(lpszTemp); va_end(argList); } #endif //!_MAC __declspec (nothrow) void CCom_String::TrimRight() { CopyBeforeWrite(); // find beginning of trailing spaces by starting at beginning (DBCS aware) LPTSTR lpsz = m_pchData; LPTSTR lpszLast = NULL; while (*lpsz != '\0') { if (_istspace(*lpsz)) { if (lpszLast == NULL) lpszLast = lpsz; } else lpszLast = NULL; lpsz = _tcsinc(lpsz); } if (lpszLast != NULL) { // truncate at trailing space start *lpszLast = '\0'; GetData()->nDataLength = lpszLast - m_pchData; } } __declspec (nothrow) void CCom_String::TrimLeft() { CopyBeforeWrite(); // find first non-space character LPCTSTR lpsz = m_pchData; #pragma warning (disable : 4127) while (true) { if (IsDBCSLeadByte(*lpsz)) { if (IsSpace (*((WORD *) lpsz))) lpsz+=2; else break; } else { if (IsSpace (*((BYTE *) lpsz))) lpsz++; else break; } } #pragma warning (default:4127) // while (_istspace(*lpsz)) // lpsz = _tcsinc(lpsz); // fix up data and length int nDataLength = GetData()->nDataLength - (lpsz - m_pchData); memmove(m_pchData, lpsz, (nDataLength+1)*sizeof(TCHAR)); GetData()->nDataLength = nDataLength; } /////////////////////////////////////////////////////////////////////////////// // CCom_String support for template collections __declspec (nothrow) void ConstructElements(CCom_String* pElements, int nCount) { assert(nCount == 0 || comline_AfxIsValidAddress(pElements, nCount * sizeof(CCom_String))); for (; nCount--; ++pElements) memcpy(pElements, &comline_afxEmptyString, sizeof(*pElements)); } __declspec (nothrow) void DestructElements(CCom_String* pElements, int nCount) { assert(nCount == 0 || comline_AfxIsValidAddress(pElements, nCount * sizeof(CCom_String))); for (; nCount--; ++pElements) pElements->~CCom_String(); } __declspec (nothrow) void CopyElements(CCom_String* pDest, const CCom_String* pSrc, int nCount) { assert(nCount == 0 || comline_AfxIsValidAddress(pDest, nCount * sizeof(CCom_String))); assert(nCount == 0 || comline_AfxIsValidAddress(pSrc, nCount * sizeof(CCom_String))); for (; nCount--; ++pDest, ++pSrc) *pDest = *pSrc; } __declspec (nothrow) UINT HashKey(LPCTSTR key) { UINT nHash = 0; while (*key) nHash = (nHash<<5) + nHash + *key++; return nHash; } ///////////////////////////////////////////////////////////////////////////// // Windows extensions to strings #ifdef _UNICODE #define CHAR_FUDGE 1 // one TCHAR unused is good enough #else #define CHAR_FUDGE 2 // two BYTES unused for case of DBC last char #endif __declspec (nothrow) bool CCom_String::LoadString(UINT nID,HINSTANCE inst) { // try fixed buffer first (to avoid wasting space in the heap) TCHAR szTemp[256]; int nLen = comline_AfxLoadString(nID, szTemp, _countof(szTemp),inst); if (_countof(szTemp) - nLen > CHAR_FUDGE) { *this = szTemp; return nLen > 0; } // try buffer size of 512, then larger size until entire string is retrieved int nSize = 256; do { nSize += 256; nLen = comline_AfxLoadString(nID, GetBuffer(nSize-1), nSize,inst); } while (nSize - nLen <= CHAR_FUDGE); ReleaseBuffer(); return nLen > 0; } __declspec (nothrow) int comline_AfxLoadString(UINT nID, LPTSTR lpszBuf, UINT nMaxBuf,HINSTANCE inst) { assert(comline_AfxIsValidAddress(lpszBuf, nMaxBuf*sizeof(TCHAR))); #ifdef _DEBUG // LoadString without annoying warning from the Debug kernel if the // segment containing the string is not present if(inst == NULL) { if (::FindResource(GetModuleHandle (NULL), MAKEINTRESOURCE((nID>>4)+1), RT_STRING) == NULL) { lpszBuf[0] = '\0'; return 0; // not found } } else { if (::FindResource(inst,MAKEINTRESOURCE((nID>>4)+1), RT_STRING) == NULL) { lpszBuf[0] = '\0'; return 0; // not found } } #endif //_DEBUG int nLen; if(inst == NULL) nLen = ::LoadString(GetModuleHandle (NULL), nID, lpszBuf, nMaxBuf); else nLen = ::LoadString(inst, nID, lpszBuf, nMaxBuf); if (nLen == 0) lpszBuf[0] = '\0'; return nLen; } ///////////////////////////////////////////////////////////////////////////// __declspec (nothrow) bool comline_AfxExtractSubString(CCom_String& rString, LPCTSTR lpszFullString, int iSubString, TCHAR chSep) { if (lpszFullString == NULL) return false; while (iSubString--) { lpszFullString = _tcschr(lpszFullString, chSep); if (lpszFullString == NULL) { rString.Empty(); // return empty string as well return false; } lpszFullString++; // point past the separator } LPCTSTR lpchEnd = _tcschr(lpszFullString, chSep); int nLen = (lpchEnd == NULL) ? lstrlen(lpszFullString) : (int)(lpchEnd - lpszFullString); assert(nLen >= 0); memcpy(rString.GetBufferSetLength(nLen), lpszFullString, nLen*sizeof(TCHAR)); return true; } ///////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// // Advanced manipulation int CCom_String::Delete(int nIndex, int nCount /* = 1 */) { if (nIndex < 0) nIndex = 0; int nNewLength = GetData()->nDataLength; if (nCount > 0 && nIndex < nNewLength) { CopyBeforeWrite(); int nBytesToCopy = nNewLength - (nIndex + nCount) + 1; memcpy(m_pchData + nIndex, m_pchData + nIndex + nCount, nBytesToCopy * sizeof(TCHAR)); GetData()->nDataLength = nNewLength - nCount; } return nNewLength; } int CCom_String::Insert(int nIndex, TCHAR ch) { CopyBeforeWrite(); if (nIndex < 0) nIndex = 0; int nNewLength = GetData()->nDataLength; if (nIndex > nNewLength) nIndex = nNewLength; nNewLength++; if (GetData()->nAllocLength < nNewLength) { CCom_StringData* pOldData = GetData(); LPTSTR pstr = m_pchData; AllocBuffer(nNewLength); memcpy(m_pchData, pstr, pOldData->nDataLength*sizeof(TCHAR)); CCom_String::Release(pOldData); } // move existing bytes down memcpy(m_pchData + nIndex + 1, m_pchData + nIndex, (nNewLength-nIndex)*sizeof(TCHAR)); m_pchData[nIndex] = ch; GetData()->nDataLength = nNewLength; return nNewLength; } int CCom_String::Insert(int nIndex, LPCTSTR pstr) { if (nIndex < 0) nIndex = 0; int nInsertLength = SafeStrlen(pstr); int nNewLength = GetData()->nDataLength; if (nInsertLength > 0) { CopyBeforeWrite(); if (nIndex > nNewLength) nIndex = nNewLength; nNewLength += nInsertLength; if (GetData()->nAllocLength < nNewLength) { CCom_StringData* pOldData = GetData(); LPTSTR pstr = m_pchData; AllocBuffer(nNewLength); memcpy(m_pchData, pstr, pOldData->nDataLength*sizeof(TCHAR)); CCom_String::Release(pOldData); } // move existing bytes down memcpy(m_pchData + nIndex + nInsertLength, m_pchData + nIndex, (nNewLength-nIndex-nInsertLength+1)*sizeof(TCHAR)); memcpy(m_pchData + nIndex, pstr, nInsertLength*sizeof(TCHAR)); GetData()->nDataLength = nNewLength; } return nNewLength; } int CCom_String::Replace(TCHAR chOld, TCHAR chNew) { int nCount = 0; // short-circuit the nop case if (chOld != chNew) { // otherwise modify each character that matches in the string CopyBeforeWrite(); LPTSTR psz = m_pchData; LPTSTR pszEnd = psz + GetData()->nDataLength; while (psz < pszEnd) { // replace instances of the specified character only if (*psz == chOld) { *psz = chNew; nCount++; } psz = _tcsinc(psz); } } return nCount; } int CCom_String::Replace(LPCTSTR lpszOld, LPCTSTR lpszNew) { // can't have empty or NULL lpszOld int nSourceLen = SafeStrlen(lpszOld); if (nSourceLen == 0) return 0; int nReplacementLen = SafeStrlen(lpszNew); // loop once to figure out the size of the result string int nCount = 0; LPTSTR lpszStart = m_pchData; LPTSTR lpszEnd = m_pchData + GetData()->nDataLength; LPTSTR lpszTarget; while (lpszStart < lpszEnd) { while ((lpszTarget = _tcsstr(lpszStart, lpszOld)) != NULL) { nCount++; lpszStart = lpszTarget + nSourceLen; } lpszStart += lstrlen(lpszStart) + 1; } // if any changes were made, make them if (nCount > 0) { CopyBeforeWrite(); // if the buffer is too small, just // allocate a new buffer (slow but sure) int nOldLength = GetData()->nDataLength; int nNewLength = nOldLength + (nReplacementLen-nSourceLen)*nCount; if (GetData()->nAllocLength < nNewLength || GetData()->nRefs > 1) { CCom_StringData* pOldData = GetData(); LPTSTR pstr = m_pchData; AllocBuffer(nNewLength); memcpy(m_pchData, pstr, pOldData->nDataLength*sizeof(TCHAR)); CCom_String::Release(pOldData); } // else, we just do it in-place lpszStart = m_pchData; lpszEnd = m_pchData + GetData()->nDataLength; // loop again to actually do the work while (lpszStart < lpszEnd) { while ( (lpszTarget = _tcsstr(lpszStart, lpszOld)) != NULL) { int nBalance = nOldLength - (lpszTarget - m_pchData + nSourceLen); memmove(lpszTarget + nReplacementLen, lpszTarget + nSourceLen, nBalance * sizeof(TCHAR)); memcpy(lpszTarget, lpszNew, nReplacementLen*sizeof(TCHAR)); lpszStart = lpszTarget + nReplacementLen; lpszStart[nBalance] = '\0'; nOldLength += (nReplacementLen - nSourceLen); } lpszStart += lstrlen(lpszStart) + 1; } assert(m_pchData[nNewLength] == '\0'); GetData()->nDataLength = nNewLength; } return nCount; } int CCom_String::Remove(TCHAR chRemove) { CopyBeforeWrite(); LPTSTR pstrSource = m_pchData; LPTSTR pstrDest = m_pchData; LPTSTR pstrEnd = m_pchData + GetData()->nDataLength; while (pstrSource < pstrEnd) { if (*pstrSource != chRemove) { *pstrDest = *pstrSource; pstrDest = _tcsinc(pstrDest); } pstrSource = _tcsinc(pstrSource); } *pstrDest = '\0'; int nCount = pstrSource - pstrDest; GetData()->nDataLength -= nCount; return nCount; }