|
|
//
// MODULE: APGTSSTR.CPP
//
// PURPOSE: implements DLL Growable string object CString
// (pretty much a la MFC, but avoids all that MFC overhead)
//
// PROJECT: Generic Troubleshooter DLL for Microsoft AnswerPoint
//
// COMPANY: Saltmine Creative, Inc. (206)-284-7511 [email protected]
//
// AUTHOR: Joe Mabel (reworked code from Microsoft's MFC sources)
//
// ORIGINAL DATE: 8-2-96 Roman Mach; totally re-implemented 1/15/99 Joe Mabel
//
// NOTES:
// 1. As of 1/99, re-implemented based on MFC's implementation. Pared down
// to what we use.
// 2. This file modified 5/26/01 by Davide Massarenti from MS to remove reference counting
// from CString implementation to resolve thread safety issue discovered on dual processor
// systems when dll compiled for WinXP by MS compiler.
//
// Version Date By Comments
//--------------------------------------------------------------------
// V0.1 - RM Original
// V3.0 7-24-98 JM Abstracted this out as a separate header.
// V3.1 1-15-99 JM Redo based on MFC implementation
//
#include "stdafx.h"
#include <stdio.h>
#include "apgtsstr.h"
#include "apgtsmfc.h"
// 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
/////////////////////////////////////////////////////////////////////////////
// static class data, special inlines
// afxChNil is left for backward compatibility
TCHAR 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)
// [BC - 20010529] - change below array from int to long and add extra 0 for padding
// to avoid problems indicated by Davide Massarenti in 64-bit environments
static long rgInitData[] = { -1, 0, 0, 0, 0 }; static CStringData* afxDataNil = (CStringData*)&rgInitData; static LPCTSTR afxPchNil = (LPCTSTR)(((BYTE*)&rgInitData)+sizeof(CStringData)); // special function to make afxEmptyString work even during initialization
const CString& AfxGetEmptyString() { return *(CString*)&afxPchNil; }
//////////////////////////////////////////////////////////////////////////////
// Construction/Destruction
CString::CString() { Init(); }
CString::CString(const CString& stringSrc) { // REMOVING REF COUNTING: ASSERT(stringSrc.GetData()->nRefs != 0);
// REMOVING REF COUNTING: if (stringSrc.GetData()->nRefs >= 0)
// REMOVING REF COUNTING: {
// REMOVING REF COUNTING: ASSERT(stringSrc.GetData() != afxDataNil);
// REMOVING REF COUNTING: m_pchData = stringSrc.m_pchData;
// REMOVING REF COUNTING: InterlockedIncrement(&GetData()->nRefs);
// REMOVING REF COUNTING: }
// REMOVING REF COUNTING: else
// REMOVING REF COUNTING: {
Init(); *this = stringSrc.m_pchData; // REMOVING REF COUNTING: }
}
void CString::AllocBuffer(int nLen) // always allocate one extra character for '\0' termination
// assumes [optimistically] that data length will equal allocation length
{ ASSERT(nLen >= 0);
// MFC had the following assertion. I've killed it because we
// don't have INT_MAX. JM 1/15/99
//ASSERT(nLen <= INT_MAX-1); // max size (enough room for 1 extra)
if (nLen == 0) Init(); else { CStringData* pData = (CStringData*)new BYTE[sizeof(CStringData) + (nLen+1)*sizeof(TCHAR)]; //[BC-03022001] - added check for NULL ptr to satisfy MS code analysis tool.
if(pData) { pData->nRefs = 1; pData->data()[nLen] = '\0'; pData->nDataLength = nLen; pData->nAllocLength = nLen; m_pchData = pData->data(); } } }
void CString::Release() { if (GetData() != afxDataNil) { // REMOVING REF COUNTING: ASSERT(GetData()->nRefs != 0);
// REMOVING REF COUNTING: if (InterlockedDecrement(&GetData()->nRefs) <= 0)
delete[] (BYTE*)GetData(); Init(); } }
void PASCAL CString::Release(CStringData* pData) { if (pData != afxDataNil) { // REMOVING REF COUNTING: ASSERT(pData->nRefs != 0);
// REMOVING REF COUNTING: if (InterlockedDecrement(&pData->nRefs) <= 0)
delete[] (BYTE*)pData; } }
void CString::CopyBeforeWrite() { // REMOVING REF COUNTING: if (GetData()->nRefs > 1)
// REMOVING REF COUNTING: {
// REMOVING REF COUNTING: CStringData* pData = GetData();
// REMOVING REF COUNTING: Release();
// REMOVING REF COUNTING: AllocBuffer(pData->nDataLength);
// REMOVING REF COUNTING: memcpy(m_pchData, pData->data(), (pData->nDataLength+1)*sizeof(TCHAR));
// REMOVING REF COUNTING: }
// REMOVING REF COUNTING: ASSERT(GetData()->nRefs <= 1);
}
void CString::AllocBeforeWrite(int nLen) { // REMOVING REF COUNTING: if (GetData()->nRefs > 1 || nLen > GetData()->nAllocLength)
if (nLen > GetData()->nAllocLength) { Release(); AllocBuffer(nLen); } // REMOVING REF COUNTING: ASSERT(GetData()->nRefs <= 1);
}
CString::~CString() // free any attached data
{ if (GetData() != afxDataNil) { // REMOVING REF COUNTING: if (InterlockedDecrement(&GetData()->nRefs) <= 0)
delete[] (BYTE*)GetData(); } }
void CString::Empty() { if (GetData()->nDataLength == 0) return; // REMOVING REF COUNTING: if (GetData()->nRefs >= 0)
Release(); // REMOVING REF COUNTING: else
// REMOVING REF COUNTING: *this = &afxChNil;
ASSERT(GetData()->nDataLength == 0); // REMOVING REF COUNTING: ASSERT(GetData()->nRefs < 0 || GetData()->nAllocLength == 0);
}
//////////////////////////////////////////////////////////////////////////////
// Helpers for the rest of the implementation
void CString::AllocCopy(CString& dest, int nCopyLen, int nCopyIndex, int nExtraLen) const { // will clone the data attached to this string
// allocating 'nExtraLen' characters
// Places results in uninitialized string 'dest'
// Will copy the part or all of original data to start of new string
int nNewLen = nCopyLen + nExtraLen; if (nNewLen == 0) { dest.Init(); } else { dest.AllocBuffer(nNewLen); memcpy(dest.m_pchData, m_pchData+nCopyIndex, nCopyLen*sizeof(TCHAR)); } }
//////////////////////////////////////////////////////////////////////////////
// More sophisticated construction
CString::CString(LPCTSTR lpsz) { Init(); // Unlike MFC, no implicit LoadString offered - JM 1/15/99
int nLen = SafeStrlen(lpsz); if (nLen != 0) { AllocBuffer(nLen); memcpy(m_pchData, lpsz, nLen*sizeof(TCHAR)); } }
//////////////////////////////////////////////////////////////////////////////
// Assignment operators
// All assign a new value to the string
// (a) first see if the buffer is big enough
// (b) if enough room, copy on top of old buffer, set size and type
// (c) otherwise free old string data, and create a new one
//
// All routines return the new string (but as a 'const CString&' so that
// assigning it again will cause a copy, eg: s1 = s2 = "hi there".
//
void CString::AssignCopy(int nSrcLen, LPCTSTR lpszSrcData) { AllocBeforeWrite(nSrcLen); memcpy(m_pchData, lpszSrcData, nSrcLen*sizeof(TCHAR)); GetData()->nDataLength = nSrcLen; m_pchData[nSrcLen] = '\0'; }
const CString& CString::operator=(const CString& stringSrc) { // REMOVING REF COUNTING: if (m_pchData != stringSrc.m_pchData)
// REMOVING REF COUNTING: {
// REMOVING REF COUNTING: if ((GetData()->nRefs < 0 && GetData() != afxDataNil) ||
// REMOVING REF COUNTING: stringSrc.GetData()->nRefs < 0)
// REMOVING REF COUNTING: {
// actual copy necessary since one of the strings is locked
AssignCopy(stringSrc.GetData()->nDataLength, stringSrc.m_pchData); // REMOVING REF COUNTING: }
// REMOVING REF COUNTING: else
// REMOVING REF COUNTING: {
// REMOVING REF COUNTING: // can just copy references around
// REMOVING REF COUNTING: Release();
// REMOVING REF COUNTING: ASSERT(stringSrc.GetData() != afxDataNil);
// REMOVING REF COUNTING: m_pchData = stringSrc.m_pchData;
// REMOVING REF COUNTING: InterlockedIncrement(&GetData()->nRefs);
// REMOVING REF COUNTING: }
// REMOVING REF COUNTING: }
return *this; }
const CString& CString::operator=(LPCTSTR lpsz) { // Suppress the following Assert from MFC - JM 1/15/99
// ASSERT(lpsz == NULL || AfxIsValidString(lpsz, FALSE));
AssignCopy(SafeStrlen(lpsz), lpsz); return *this; }
const CString& CString::operator=(TCHAR ch) { AssignCopy(1, &ch); return *this; }
#ifdef _UNICODE
const CString& CString::operator=(LPCSTR lpsz) { int nSrcLen = lpsz != NULL ? lstrlenA(lpsz) : 0; AllocBeforeWrite(nSrcLen); mbstowcs(m_pchData, lpsz, nSrcLen+1); ReleaseBuffer(); return *this; } #else //!_UNICODE
const CString& CString::operator=(LPCWSTR lpsz) { int nSrcLen = lpsz != NULL ? wcslen(lpsz) : 0; AllocBeforeWrite(nSrcLen*2); wcstombs(m_pchData, lpsz, (nSrcLen*2)+1); ReleaseBuffer(); return *this; } #endif //!_UNICODE
//////////////////////////////////////////////////////////////////////////////
// concatenation
// NOTE: "operator+" is done as friend functions for simplicity
// There are three variants:
// CString + CString
// and for ? = TCHAR, LPCTSTR
// CString + ?
// ? + CString
// we (Saltmine) have switched away from friend because VC 6/0 doesn't like it. - JM 1/15/99
// we (Saltmine) do LPCTSTR but not TCHAR - JM 1/15/99
void CString::ConcatCopy(int nSrc1Len, LPCTSTR lpszSrc1Data, int nSrc2Len, LPCTSTR lpszSrc2Data) { // -- master concatenation routine
// Concatenate two sources
// -- assume that 'this' is a new CString 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)); } }
CString CString::operator+(const CString& string2) { CString s; s.ConcatCopy(GetData()->nDataLength, m_pchData, string2.GetData()->nDataLength, string2.m_pchData); return s; }
CString CString::operator+(LPCTSTR lpsz) { // Suppress the following Assert from MFC - JM 1/15/99
// ASSERT(lpsz == NULL || AfxIsValidString(lpsz, FALSE));
CString s; s.ConcatCopy(GetData()->nDataLength, m_pchData, CString::SafeStrlen(lpsz), lpsz); return s; }
//////////////////////////////////////////////////////////////////////////////
// concatenate in place
void CString::ConcatInPlace(int nSrcLen, LPCTSTR lpszSrcData) { // -- the main routine for += operators
// concatenating an empty string is a no-op!
if (nSrcLen == 0) return;
// if the buffer is too small, or we have a width mis-match, just
// allocate a new buffer (slow but sure)
// REMOVING REF COUNTING: if (GetData()->nRefs > 1 || GetData()->nDataLength + nSrcLen > GetData()->nAllocLength)
if (GetData()->nDataLength + nSrcLen > GetData()->nAllocLength) { // we have to grow the buffer, use the ConcatCopy routine
CStringData* pOldData = GetData(); ConcatCopy(GetData()->nDataLength, m_pchData, nSrcLen, lpszSrcData); ASSERT(pOldData != NULL); CString::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'; } }
const CString& CString::operator+=(LPCTSTR lpsz) { // Suppress the following Assert from MFC - JM 1/15/99
// ASSERT(lpsz == NULL || AfxIsValidString(lpsz, FALSE));
ConcatInPlace(SafeStrlen(lpsz), lpsz); return *this; }
const CString& CString::operator+=(const CString& string) { ConcatInPlace(string.GetData()->nDataLength, string.m_pchData); return *this; }
///////////////////////////////////////////////////////////////////////////////
// Advanced direct buffer access
// CString::GetBuffer() and CString::ReleaseBuffer() calls should be matched.
LPTSTR CString::GetBuffer(int nMinBufLength) { ASSERT(nMinBufLength >= 0);
// REMOVING REF COUNTING: if (GetData()->nRefs > 1 || nMinBufLength > GetData()->nAllocLength)
if (nMinBufLength > GetData()->nAllocLength) { // we have to grow the buffer
CStringData* pOldData = GetData(); int nOldLen = GetData()->nDataLength; // AllocBuffer will tromp it
if (nMinBufLength < nOldLen) nMinBufLength = nOldLen; AllocBuffer(nMinBufLength); memcpy(m_pchData, pOldData->data(), (nOldLen+1)*sizeof(TCHAR)); GetData()->nDataLength = nOldLen; CString::Release(pOldData); } // REMOVING REF COUNTING: ASSERT(GetData()->nRefs <= 1);
// return a pointer to the character storage for this string
ASSERT(m_pchData != NULL); return m_pchData; }
// CString::GetBuffer() and CString::ReleaseBuffer() calls should be matched.
void CString::ReleaseBuffer(int nNewLength /* = -1 */) { 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'; }
LPTSTR CString::GetBufferSetLength(int nNewLength) { ASSERT(nNewLength >= 0);
GetBuffer(nNewLength); GetData()->nDataLength = nNewLength; m_pchData[nNewLength] = '\0'; return m_pchData; }
///////////////////////////////////////////////////////////////////////////////
// Commonly used routines
int CString::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) ? CString::FIND_FAILED : (int)(lpsz - m_pchData); }
void CString::MakeLower() { CopyBeforeWrite(); _tcslwr(m_pchData); }
//////////////////////////////////////////////////////////////////////////////
// Very simple sub-string extraction
CString CString::Mid(int nFirst) const { return Mid(nFirst, GetData()->nDataLength - nFirst); }
CString CString::Mid(int nFirst, int nCount) const { // out-of-bounds requests return sensible things
if (nFirst < 0) nFirst = 0; if (nCount < 0) nCount = 0;
if (nFirst + nCount > GetData()->nDataLength) nCount = GetData()->nDataLength - nFirst; if (nFirst > GetData()->nDataLength) nCount = 0;
CString dest; AllocCopy(dest, nCount, nFirst, 0); return dest; }
CString CString::Right(int nCount) const { if (nCount < 0) nCount = 0; else if (nCount > GetData()->nDataLength) nCount = GetData()->nDataLength;
CString dest; AllocCopy(dest, nCount, GetData()->nDataLength-nCount, 0); return dest; }
CString CString::Left(int nCount) const { if (nCount < 0) nCount = 0; else if (nCount > GetData()->nDataLength) nCount = GetData()->nDataLength;
CString dest; AllocCopy(dest, nCount, 0, 0); return dest; }
//////////////////////////////////////////////////////////////////////////////
// Finding
int CString::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) ? CString::FIND_FAILED : (int)(lpsz - m_pchData); }
// find a sub-string (like strstr)
int CString::Find(LPCTSTR lpszSub) const { // Suppress the following Assert from MFC - JM 1/15/99
// ASSERT(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) ? CString::FIND_FAILED : (int)(lpsz - m_pchData); }
// find a sub-string (like strstr). Added function - RAB19991112.
int CString::Find(LPCTSTR lpszSub, int nStart) const { // Suppress the following Assert from MFC - RAB19991112.
//ASSERT(AfxIsValidString(lpszSub));
int nLength = GetData()->nDataLength; if (nStart > nLength) return CString::FIND_FAILED;
// find first matching substring
LPTSTR lpsz = _tcsstr(m_pchData + nStart, lpszSub);
// return -1 for not found, distance from beginning otherwise
return (lpsz == NULL) ? CString::FIND_FAILED : (int)(lpsz - m_pchData); }
/////////////////////////////////////////////////////////////////////////////
// CString 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
void CString::FormatV(LPCTSTR lpszFormat, va_list argList) { // Suppress the following Assert from MFC - JM 1/15/99
// ASSERT(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': { LPCTSTR pstrNextArg = va_arg(argList, LPCTSTR); if (pstrNextArg == NULL) nItemLen = 6; // "(null)"
else { nItemLen = lstrlen(pstrNextArg); nItemLen = max(1, nItemLen); } break; }
case 'S': { #ifndef _UNICODE
LPWSTR pstrNextArg = va_arg(argList, LPWSTR); if (pstrNextArg == NULL) nItemLen = 6; // "(null)"
else { nItemLen = wcslen(pstrNextArg); nItemLen = max(1, nItemLen); } #else
LPCSTR pstrNextArg = va_arg(argList, LPCSTR); if (pstrNextArg == NULL) nItemLen = 6; // "(null)"
else { nItemLen = lstrlenA(pstrNextArg); nItemLen = max(1, nItemLen); } #endif
break; }
case 's'|FORCE_ANSI: case 'S'|FORCE_ANSI: { LPCSTR pstrNextArg = va_arg(argList, LPCSTR); if (pstrNextArg == NULL) nItemLen = 6; // "(null)"
else { nItemLen = lstrlenA(pstrNextArg); nItemLen = max(1, nItemLen); } break; }
#ifndef _MAC
case 's'|FORCE_UNICODE: case 'S'|FORCE_UNICODE: { LPWSTR pstrNextArg = va_arg(argList, LPWSTR); if (pstrNextArg == NULL) nItemLen = 6; // "(null)"
else { nItemLen = wcslen(pstrNextArg); 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; #if 0
// We (Saltmine) are not currently supporting formatting of real numbers 1/15/99
case 'e': case 'f': case 'g': case 'G': va_arg(argList, DOUBLE_ARG); nItemLen = 128; nItemLen = max(nItemLen, nWidth+nPrecision); break; #endif
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);
// Got rid of MFC's VERIFY in next line - JM 1/15/99
//VERIFY(_vstprintf(m_pchData, lpszFormat, argListSave) <= GetAllocLength());
_vstprintf(m_pchData, lpszFormat, argListSave); ReleaseBuffer();
va_end(argListSave); }
// formatting (using wsprintf style formatting)
void CString::Format(LPCTSTR lpszFormat, ...) { // Suppress the following Assert from MFC - JM 1/15/99
// ASSERT(AfxIsValidString(lpszFormat, FALSE));
va_list argList; va_start(argList, lpszFormat); FormatV(lpszFormat, argList); va_end(argList); }
void CString::TrimRight() { CopyBeforeWrite();
// find beginning of trailing spaces by starting at beginning (DBCS aware)
LPTSTR lpsz = m_pchData; LPTSTR lpszLast = NULL; while (*lpsz != '\0') { if (_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 = static_cast<int>(lpszLast - m_pchData); } }
void CString::TrimLeft() { CopyBeforeWrite();
// find first non-space character
LPCTSTR lpsz = m_pchData; while (_istspace(*lpsz)) lpsz = _tcsinc(lpsz);
// fix up data and length
int nDataLength = GetData()->nDataLength - static_cast<int>(lpsz - m_pchData); memmove(m_pchData, lpsz, (nDataLength+1)*sizeof(TCHAR)); GetData()->nDataLength = nDataLength; }
BOOL CString::LoadString(UINT nID) { // try fixed buffer first (to avoid wasting space in the heap)
TCHAR szTemp[256]; int nLen = ::AfxLoadString(nID, szTemp, _countof(szTemp)); 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 = ::AfxLoadString(nID, GetBuffer(nSize-1), nSize); } while (nSize - nLen <= CHAR_FUDGE); ReleaseBuffer();
return nLen > 0; }
bool __stdcall operator ==(const CString& s1, const CString& s2) { return (s1.GetLength() == s2.GetLength() && ! _tcscmp((LPCTSTR)s1, (LPCTSTR)s2) ); }
bool __stdcall operator ==(const CString& s1, LPCTSTR s2) { return (! _tcscmp((LPCTSTR)s1, s2) ); }
bool __stdcall operator ==(LPCTSTR s1, const CString& s2) { return (! _tcscmp(s1, (LPCTSTR)s2) ); }
bool __stdcall operator !=(const CString& s1, const CString& s2) { return (s1.GetLength() != s2.GetLength() || _tcscmp((LPCTSTR)s1, (LPCTSTR)s2) ); }
bool __stdcall operator !=(const CString& s1, LPCTSTR s2) { return (_tcscmp((LPCTSTR)s1, s2) ) ? true : false; }
bool __stdcall operator !=(LPCTSTR s1, const CString& s2) { return (_tcscmp(s1, (LPCTSTR)s2) ) ? true : false; }
bool __stdcall operator < (const CString& s1, const CString& s2) { return (_tcscmp((LPCTSTR)s1, (LPCTSTR)s2) <0 ); }
bool __stdcall operator < (const CString& s1, LPCTSTR s2) { return (_tcscmp((LPCTSTR)s1, s2) <0 ); }
bool __stdcall operator < (LPCTSTR s1, const CString& s2) { return (_tcscmp(s1, (LPCTSTR)s2) <0 ); }
CString operator+(LPCTSTR lpsz, const CString& string) { return CString(lpsz) + string; }
void CString::Init() { m_pchData = afxDataNil->data(); }
CStringData* CString::GetData() const { if(m_pchData != NULL) return ((CStringData*)m_pchData)-1; return afxDataNil; }
|