Complete disaster-recovery snapshot: engine/game source, game data assets, VC6 toolchain + DX SDKs, build outputs, deployed game, and _UNUSED archive. Large binaries in Git LFS; text preserved byte-for-byte (core.autocrlf=false, no eol attributes). See RECOVERY.md for the one-clone rebuild procedure.
982 lines
22 KiB
C++
982 lines
22 KiB
C++
// This is a part of the Microsoft Foundation Classes C++ library.
|
|
// Copyright (C) 1992-1998 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 "stdafx.h"
|
|
#include <afxtempl.h>
|
|
|
|
#ifdef AFX_AUX_SEG
|
|
#pragma code_seg(AFX_AUX_SEG)
|
|
#endif
|
|
|
|
#ifdef _DEBUG
|
|
#undef THIS_FILE
|
|
static char THIS_FILE[] = __FILE__;
|
|
#endif
|
|
|
|
#define new DEBUG_NEW
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// More sophisticated construction
|
|
|
|
CString::CString(TCHAR ch, int nLength)
|
|
{
|
|
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
|
|
}
|
|
}
|
|
|
|
CString::CString(LPCTSTR lpch, int nLength)
|
|
{
|
|
Init();
|
|
if (nLength != 0)
|
|
{
|
|
ASSERT(AfxIsValidAddress(lpch, nLength, FALSE));
|
|
AllocBuffer(nLength);
|
|
memcpy(m_pchData, lpch, nLength*sizeof(TCHAR));
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Special conversion constructors
|
|
|
|
#ifdef _UNICODE
|
|
CString::CString(LPCSTR lpsz, int nLength)
|
|
{
|
|
Init();
|
|
if (nLength != 0)
|
|
{
|
|
AllocBuffer(nLength);
|
|
int n = ::MultiByteToWideChar(CP_ACP, 0, lpsz, nLength, m_pchData, nLength+1);
|
|
ReleaseBuffer(n >= 0 ? n : -1);
|
|
}
|
|
}
|
|
#else //_UNICODE
|
|
CString::CString(LPCWSTR lpsz, int nLength)
|
|
{
|
|
Init();
|
|
if (nLength != 0)
|
|
{
|
|
AllocBuffer(nLength*2);
|
|
int n = ::WideCharToMultiByte(CP_ACP, 0, lpsz, nLength, m_pchData,
|
|
(nLength*2)+1, NULL, NULL);
|
|
ReleaseBuffer(n >= 0 ? n : -1);
|
|
}
|
|
}
|
|
#endif //!_UNICODE
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// Assignment operators
|
|
|
|
const CString& CString::operator=(TCHAR ch)
|
|
{
|
|
AssignCopy(1, &ch);
|
|
return *this;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// less common string expressions
|
|
|
|
CString AFXAPI operator+(const CString& string1, TCHAR ch)
|
|
{
|
|
CString s;
|
|
s.ConcatCopy(string1.GetData()->nDataLength, string1.m_pchData, 1, &ch);
|
|
return s;
|
|
}
|
|
|
|
CString AFXAPI operator+(TCHAR ch, const CString& string)
|
|
{
|
|
CString s;
|
|
s.ConcatCopy(1, &ch, string.GetData()->nDataLength, string.m_pchData);
|
|
return s;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// Advanced manipulation
|
|
|
|
int CString::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 CString::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)
|
|
{
|
|
CStringData* pOldData = GetData();
|
|
LPTSTR pstr = m_pchData;
|
|
AllocBuffer(nNewLength);
|
|
memcpy(m_pchData, pstr, (pOldData->nDataLength+1)*sizeof(TCHAR));
|
|
CString::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 CString::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)
|
|
{
|
|
CStringData* pOldData = GetData();
|
|
LPTSTR pstr = m_pchData;
|
|
AllocBuffer(nNewLength);
|
|
memcpy(m_pchData, pstr, (pOldData->nDataLength+1)*sizeof(TCHAR));
|
|
CString::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 CString::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 CString::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)
|
|
{
|
|
CStringData* pOldData = GetData();
|
|
LPTSTR pstr = m_pchData;
|
|
AllocBuffer(nNewLength);
|
|
memcpy(m_pchData, pstr, pOldData->nDataLength*sizeof(TCHAR));
|
|
CString::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 CString::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;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// 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;
|
|
|
|
ASSERT(nFirst >= 0);
|
|
ASSERT(nFirst + nCount <= GetData()->nDataLength);
|
|
|
|
// optimize case of returning entire string
|
|
if (nFirst == 0 && nFirst + nCount == GetData()->nDataLength)
|
|
return *this;
|
|
|
|
CString dest;
|
|
AllocCopy(dest, nCount, nFirst, 0);
|
|
return dest;
|
|
}
|
|
|
|
CString CString::Right(int nCount) const
|
|
{
|
|
if (nCount < 0)
|
|
nCount = 0;
|
|
if (nCount >= GetData()->nDataLength)
|
|
return *this;
|
|
|
|
CString dest;
|
|
AllocCopy(dest, nCount, GetData()->nDataLength-nCount, 0);
|
|
return dest;
|
|
}
|
|
|
|
CString CString::Left(int nCount) const
|
|
{
|
|
if (nCount < 0)
|
|
nCount = 0;
|
|
if (nCount >= GetData()->nDataLength)
|
|
return *this;
|
|
|
|
CString dest;
|
|
AllocCopy(dest, nCount, 0, 0);
|
|
return dest;
|
|
}
|
|
|
|
// strspn equivalent
|
|
CString CString::SpanIncluding(LPCTSTR lpszCharSet) const
|
|
{
|
|
ASSERT(AfxIsValidString(lpszCharSet));
|
|
return Left(_tcsspn(m_pchData, lpszCharSet));
|
|
}
|
|
|
|
// strcspn equivalent
|
|
CString CString::SpanExcluding(LPCTSTR lpszCharSet) const
|
|
{
|
|
ASSERT(AfxIsValidString(lpszCharSet));
|
|
return Left(_tcscspn(m_pchData, lpszCharSet));
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// 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) ? -1 : (int)(lpsz - m_pchData);
|
|
}
|
|
|
|
// find a sub-string (like strstr)
|
|
int CString::Find(LPCTSTR lpszSub) const
|
|
{
|
|
return Find(lpszSub, 0);
|
|
}
|
|
|
|
int CString::Find(LPCTSTR lpszSub, int nStart) const
|
|
{
|
|
ASSERT(AfxIsValidString(lpszSub));
|
|
|
|
int nLength = GetData()->nDataLength;
|
|
if (nStart > nLength)
|
|
return -1;
|
|
|
|
// find first matching substring
|
|
LPTSTR lpsz = _tcsstr(m_pchData + nStart, lpszSub);
|
|
|
|
// return -1 for not found, distance from beginning otherwise
|
|
return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData);
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CString formatting
|
|
|
|
#define TCHAR_ARG TCHAR
|
|
#define WCHAR_ARG WCHAR
|
|
#define CHAR_ARG char
|
|
|
|
#ifdef _X86_
|
|
#define DOUBLE_ARG _AFX_DOUBLE
|
|
#else
|
|
#define DOUBLE_ARG double
|
|
#endif
|
|
|
|
#define FORCE_ANSI 0x10000
|
|
#define FORCE_UNICODE 0x20000
|
|
#define FORCE_INT64 0x40000
|
|
|
|
void CString::FormatV(LPCTSTR lpszFormat, va_list argList)
|
|
{
|
|
ASSERT(AfxIsValidString(lpszFormat));
|
|
|
|
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;
|
|
if (_tcsncmp(lpsz, _T("I64"), 3) == 0)
|
|
{
|
|
lpsz += 3;
|
|
nModifier = FORCE_INT64;
|
|
#if !defined(_X86_) && !defined(_ALPHA_)
|
|
// __int64 is only available on X86 and ALPHA platforms
|
|
ASSERT(FALSE);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
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;
|
|
|
|
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;
|
|
}
|
|
|
|
// adjust nItemLen for strings
|
|
if (nItemLen != 0)
|
|
{
|
|
if (nPrecision != 0)
|
|
nItemLen = min(nItemLen, nPrecision);
|
|
nItemLen = max(nItemLen, nWidth);
|
|
}
|
|
else
|
|
{
|
|
switch (*lpsz)
|
|
{
|
|
// integers
|
|
case 'd':
|
|
case 'i':
|
|
case 'u':
|
|
case 'x':
|
|
case 'X':
|
|
case 'o':
|
|
if (nModifier & FORCE_INT64)
|
|
va_arg(argList, __int64);
|
|
else
|
|
va_arg(argList, int);
|
|
nItemLen = 32;
|
|
nItemLen = max(nItemLen, nWidth+nPrecision);
|
|
break;
|
|
|
|
case 'e':
|
|
case 'g':
|
|
case 'G':
|
|
va_arg(argList, DOUBLE_ARG);
|
|
nItemLen = 128;
|
|
nItemLen = max(nItemLen, nWidth+nPrecision);
|
|
break;
|
|
|
|
case 'f':
|
|
{
|
|
double f;
|
|
LPTSTR pszTemp;
|
|
|
|
// 312 == strlen("-1+(309 zeroes).")
|
|
// 309 zeroes == max precision of a double
|
|
// 6 == adjustment in case precision is not specified,
|
|
// which means that the precision defaults to 6
|
|
pszTemp = (LPTSTR)_alloca(max(nWidth, 312+nPrecision+6));
|
|
|
|
f = va_arg(argList, double);
|
|
_stprintf( pszTemp, _T( "%*.*f" ), nWidth, nPrecision+6, f );
|
|
nItemLen = _tcslen(pszTemp);
|
|
}
|
|
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);
|
|
VERIFY(_vstprintf(m_pchData, lpszFormat, argListSave) <= GetAllocLength());
|
|
ReleaseBuffer();
|
|
|
|
va_end(argListSave);
|
|
}
|
|
|
|
// formatting (using wsprintf style formatting)
|
|
void AFX_CDECL CString::Format(LPCTSTR lpszFormat, ...)
|
|
{
|
|
ASSERT(AfxIsValidString(lpszFormat));
|
|
|
|
va_list argList;
|
|
va_start(argList, lpszFormat);
|
|
FormatV(lpszFormat, argList);
|
|
va_end(argList);
|
|
}
|
|
|
|
void AFX_CDECL CString::Format(UINT nFormatID, ...)
|
|
{
|
|
CString strFormat;
|
|
VERIFY(strFormat.LoadString(nFormatID) != 0);
|
|
|
|
va_list argList;
|
|
va_start(argList, nFormatID);
|
|
FormatV(strFormat, argList);
|
|
va_end(argList);
|
|
}
|
|
|
|
// formatting (using FormatMessage style formatting)
|
|
void AFX_CDECL CString::FormatMessage(LPCTSTR lpszFormat, ...)
|
|
{
|
|
// format message into temporary buffer lpszTemp
|
|
va_list argList;
|
|
va_start(argList, lpszFormat);
|
|
LPTSTR lpszTemp;
|
|
|
|
if (::FormatMessage(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ALLOCATE_BUFFER,
|
|
lpszFormat, 0, 0, (LPTSTR)&lpszTemp, 0, &argList) == 0 ||
|
|
lpszTemp == NULL)
|
|
{
|
|
AfxThrowMemoryException();
|
|
}
|
|
|
|
// assign lpszTemp into the resulting string and free the temporary
|
|
*this = lpszTemp;
|
|
LocalFree(lpszTemp);
|
|
va_end(argList);
|
|
}
|
|
|
|
void AFX_CDECL CString::FormatMessage(UINT nFormatID, ...)
|
|
{
|
|
// get format string from string table
|
|
CString strFormat;
|
|
VERIFY(strFormat.LoadString(nFormatID) != 0);
|
|
|
|
// format message into temporary buffer lpszTemp
|
|
va_list argList;
|
|
va_start(argList, nFormatID);
|
|
LPTSTR lpszTemp;
|
|
if (::FormatMessage(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ALLOCATE_BUFFER,
|
|
strFormat, 0, 0, (LPTSTR)&lpszTemp, 0, &argList) == 0 ||
|
|
lpszTemp == NULL)
|
|
{
|
|
AfxThrowMemoryException();
|
|
}
|
|
|
|
// assign lpszTemp into the resulting string and free lpszTemp
|
|
*this = lpszTemp;
|
|
LocalFree(lpszTemp);
|
|
va_end(argList);
|
|
}
|
|
|
|
void CString::TrimRight(LPCTSTR lpszTargetList)
|
|
{
|
|
// find beginning of trailing matches
|
|
// by starting at beginning (DBCS aware)
|
|
|
|
CopyBeforeWrite();
|
|
LPTSTR lpsz = m_pchData;
|
|
LPTSTR lpszLast = NULL;
|
|
|
|
while (*lpsz != '\0')
|
|
{
|
|
if (_tcschr(lpszTargetList, *lpsz) != NULL)
|
|
{
|
|
if (lpszLast == NULL)
|
|
lpszLast = lpsz;
|
|
}
|
|
else
|
|
lpszLast = NULL;
|
|
lpsz = _tcsinc(lpsz);
|
|
}
|
|
|
|
if (lpszLast != NULL)
|
|
{
|
|
// truncate at left-most matching character
|
|
*lpszLast = '\0';
|
|
GetData()->nDataLength = lpszLast - m_pchData;
|
|
}
|
|
}
|
|
|
|
void CString::TrimRight(TCHAR chTarget)
|
|
{
|
|
// find beginning of trailing matches
|
|
// by starting at beginning (DBCS aware)
|
|
|
|
CopyBeforeWrite();
|
|
LPTSTR lpsz = m_pchData;
|
|
LPTSTR lpszLast = NULL;
|
|
|
|
while (*lpsz != '\0')
|
|
{
|
|
if (*lpsz == chTarget)
|
|
{
|
|
if (lpszLast == NULL)
|
|
lpszLast = lpsz;
|
|
}
|
|
else
|
|
lpszLast = NULL;
|
|
lpsz = _tcsinc(lpsz);
|
|
}
|
|
|
|
if (lpszLast != NULL)
|
|
{
|
|
// truncate at left-most matching character
|
|
*lpszLast = '\0';
|
|
GetData()->nDataLength = lpszLast - m_pchData;
|
|
}
|
|
}
|
|
|
|
void CString::TrimRight()
|
|
{
|
|
// find beginning of trailing spaces by starting at beginning (DBCS aware)
|
|
|
|
CopyBeforeWrite();
|
|
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;
|
|
}
|
|
}
|
|
|
|
void CString::TrimLeft(LPCTSTR lpszTargets)
|
|
{
|
|
// if we're not trimming anything, we're not doing any work
|
|
if (SafeStrlen(lpszTargets) == 0)
|
|
return;
|
|
|
|
CopyBeforeWrite();
|
|
LPCTSTR lpsz = m_pchData;
|
|
|
|
while (*lpsz != '\0')
|
|
{
|
|
if (_tcschr(lpszTargets, *lpsz) == NULL)
|
|
break;
|
|
lpsz = _tcsinc(lpsz);
|
|
}
|
|
|
|
if (lpsz != m_pchData)
|
|
{
|
|
// fix up data and length
|
|
int nDataLength = GetData()->nDataLength - (lpsz - m_pchData);
|
|
memmove(m_pchData, lpsz, (nDataLength+1)*sizeof(TCHAR));
|
|
GetData()->nDataLength = nDataLength;
|
|
}
|
|
}
|
|
|
|
void CString::TrimLeft(TCHAR chTarget)
|
|
{
|
|
// find first non-matching character
|
|
|
|
CopyBeforeWrite();
|
|
LPCTSTR lpsz = m_pchData;
|
|
|
|
while (chTarget == *lpsz)
|
|
lpsz = _tcsinc(lpsz);
|
|
|
|
if (lpsz != m_pchData)
|
|
{
|
|
// fix up data and length
|
|
int nDataLength = GetData()->nDataLength - (lpsz - m_pchData);
|
|
memmove(m_pchData, lpsz, (nDataLength+1)*sizeof(TCHAR));
|
|
GetData()->nDataLength = nDataLength;
|
|
}
|
|
}
|
|
|
|
void CString::TrimLeft()
|
|
{
|
|
// find first non-space character
|
|
|
|
CopyBeforeWrite();
|
|
LPCTSTR lpsz = m_pchData;
|
|
|
|
while (_istspace(*lpsz))
|
|
lpsz = _tcsinc(lpsz);
|
|
|
|
if (lpsz != m_pchData)
|
|
{
|
|
// fix up data and length
|
|
int nDataLength = GetData()->nDataLength - (lpsz - m_pchData);
|
|
memmove(m_pchData, lpsz, (nDataLength+1)*sizeof(TCHAR));
|
|
GetData()->nDataLength = nDataLength;
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// CString support for template collections
|
|
|
|
#if _MSC_VER >= 1100
|
|
template<> void AFXAPI ConstructElements<CString> (CString* pElements, int nCount)
|
|
#else
|
|
void AFXAPI ConstructElements(CString* pElements, int nCount)
|
|
#endif
|
|
{
|
|
ASSERT(nCount == 0 ||
|
|
AfxIsValidAddress(pElements, nCount * sizeof(CString)));
|
|
|
|
for (; nCount--; ++pElements)
|
|
memcpy(pElements, &afxEmptyString, sizeof(*pElements));
|
|
}
|
|
|
|
#if _MSC_VER >= 1100
|
|
template<> void AFXAPI DestructElements<CString> (CString* pElements, int nCount)
|
|
#else
|
|
void AFXAPI DestructElements(CString* pElements, int nCount)
|
|
#endif
|
|
{
|
|
ASSERT(nCount == 0 ||
|
|
AfxIsValidAddress(pElements, nCount * sizeof(CString)));
|
|
|
|
for (; nCount--; ++pElements)
|
|
pElements->~CString();
|
|
}
|
|
|
|
#if _MSC_VER >= 1100
|
|
template<> void AFXAPI CopyElements<CString> (CString* pDest, const CString* pSrc, int nCount)
|
|
#else
|
|
void AFXAPI CopyElements(CString* pDest, const CString* pSrc, int nCount)
|
|
#endif
|
|
{
|
|
ASSERT(nCount == 0 ||
|
|
AfxIsValidAddress(pDest, nCount * sizeof(CString)));
|
|
ASSERT(nCount == 0 ||
|
|
AfxIsValidAddress(pSrc, nCount * sizeof(CString)));
|
|
|
|
for (; nCount--; ++pDest, ++pSrc)
|
|
*pDest = *pSrc;
|
|
}
|
|
|
|
#ifndef OLE2ANSI
|
|
#if _MSC_VER >= 1100
|
|
template<> UINT AFXAPI HashKey<LPCWSTR> (LPCWSTR key)
|
|
#else
|
|
UINT AFXAPI HashKey(LPCWSTR key)
|
|
#endif
|
|
{
|
|
UINT nHash = 0;
|
|
while (*key)
|
|
nHash = (nHash<<5) + nHash + *key++;
|
|
return nHash;
|
|
}
|
|
#endif
|
|
|
|
#if _MSC_VER >= 1100
|
|
template<> UINT AFXAPI HashKey<LPCSTR> (LPCSTR key)
|
|
#else
|
|
UINT AFXAPI HashKey(LPCSTR key)
|
|
#endif
|
|
{
|
|
UINT nHash = 0;
|
|
while (*key)
|
|
nHash = (nHash<<5) + nHash + *key++;
|
|
return nHash;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|