Files
firestorm/Gameleap/code/mw4/Libraries/comline.a/comutil.cpp
T
Cyd 2b8ca921cb Initial full mirror of c:\VWE (source + assets + toolchain + outputs) via Git LFS
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.
2026-06-24 21:28:16 -05:00

1460 lines
37 KiB
C++

#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 <limits.h>
#include <windows.h>
/////////////////////////////////////////////////////////////////////////////
// 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;
}