681 lines
16 KiB
C++
681 lines
16 KiB
C++
|
// CSTRING.CPP
|
||
|
//
|
||
|
// Based on the original MFC source file.
|
||
|
|
||
|
//
|
||
|
// 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 "precomp.h"
|
||
|
#include <cstring.hpp>
|
||
|
|
||
|
#ifdef AFX_CORE1_SEG
|
||
|
#pragma code_seg(AFX_CORE1_SEG)
|
||
|
#endif
|
||
|
|
||
|
#ifdef _DEBUG
|
||
|
#undef THIS_FILE
|
||
|
static char THIS_FILE[] = __FILE__;
|
||
|
#endif
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
// static class data, special inlines
|
||
|
|
||
|
// afxChNil is left for backward compatibility
|
||
|
REMAFX_DATADEF 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)
|
||
|
static int RGInitData[] = { -1, 0, 0, 0 };
|
||
|
static REMAFX_DATADEF CSTRINGData* AFXDataNil = (CSTRINGData*)&RGInitData;
|
||
|
static LPCTSTR AFXPchNil = (LPCTSTR)(((BYTE*)&RGInitData)+sizeof(CSTRINGData));
|
||
|
// special function to make AFXEmptyString work even during initialization
|
||
|
const CSTRING& REMAFXAPI AFXGetEmptyString()
|
||
|
{ return *(CSTRING*)&AFXPchNil; }
|
||
|
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////////////
|
||
|
// Construction/Destruction
|
||
|
|
||
|
CSTRING::CSTRING()
|
||
|
{
|
||
|
Init();
|
||
|
}
|
||
|
|
||
|
CSTRING::CSTRING(const CSTRING& stringSrc)
|
||
|
{
|
||
|
ASSERT(stringSrc.GetData()->nRefs != 0);
|
||
|
if (stringSrc.GetData()->nRefs >= 0)
|
||
|
{
|
||
|
ASSERT(stringSrc.GetData() != AFXDataNil);
|
||
|
m_pchData = stringSrc.m_pchData;
|
||
|
InterlockedIncrement(&GetData()->nRefs);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Init();
|
||
|
*this = stringSrc.m_pchData;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
CSTRING::CSTRING(LPCTSTR lpch, int nLength)
|
||
|
{
|
||
|
Init();
|
||
|
|
||
|
if (nLength != 0)
|
||
|
{
|
||
|
// ASSERT(AfxIsValidAddress(lpch, nLength, FALSE));
|
||
|
AllocBuffer(nLength);
|
||
|
memcpy(m_pchData, lpch, nLength*sizeof(TCHAR));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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);
|
||
|
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)];
|
||
|
pData->nRefs = 1;
|
||
|
pData->data()[nLen] = '\0';
|
||
|
pData->nDataLength = nLen;
|
||
|
pData->nAllocLength = nLen;
|
||
|
m_pchData = pData->data();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CSTRING::Release()
|
||
|
{
|
||
|
if (GetData() != AFXDataNil)
|
||
|
{
|
||
|
ASSERT(GetData()->nRefs != 0);
|
||
|
if (InterlockedDecrement(&GetData()->nRefs) <= 0)
|
||
|
delete[] (BYTE*)GetData();
|
||
|
Init();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void PASCAL CSTRING::Release(CSTRINGData* pData)
|
||
|
{
|
||
|
if (pData != AFXDataNil)
|
||
|
{
|
||
|
ASSERT(pData->nRefs != 0);
|
||
|
if (InterlockedDecrement(&pData->nRefs) <= 0)
|
||
|
delete[] (BYTE*)pData;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CSTRING::Empty()
|
||
|
{
|
||
|
if (GetData()->nRefs >= 0)
|
||
|
Release();
|
||
|
else
|
||
|
*this = &AFXChNil;
|
||
|
ASSERT(GetData()->nDataLength == 0);
|
||
|
ASSERT(GetData()->nRefs < 0 || GetData()->nAllocLength == 0);
|
||
|
}
|
||
|
|
||
|
|
||
|
void CSTRING::CopyBeforeWrite()
|
||
|
{
|
||
|
if (GetData()->nRefs > 1)
|
||
|
{
|
||
|
CSTRINGData* pData = GetData();
|
||
|
Release();
|
||
|
AllocBuffer(pData->nDataLength);
|
||
|
memcpy(m_pchData, pData->data(), (pData->nDataLength+1)*sizeof(TCHAR));
|
||
|
}
|
||
|
ASSERT(GetData()->nRefs <= 1);
|
||
|
}
|
||
|
|
||
|
void CSTRING::AllocBeforeWrite(int nLen)
|
||
|
{
|
||
|
if (GetData()->nRefs > 1 || nLen > GetData()->nAllocLength)
|
||
|
{
|
||
|
Release();
|
||
|
AllocBuffer(nLen);
|
||
|
}
|
||
|
ASSERT(GetData()->nRefs <= 1);
|
||
|
}
|
||
|
|
||
|
CSTRING::~CSTRING()
|
||
|
// free any attached data
|
||
|
{
|
||
|
if (GetData() != AFXDataNil)
|
||
|
{
|
||
|
if (InterlockedDecrement(&GetData()->nRefs) <= 0)
|
||
|
delete[] (BYTE*)GetData();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////////////
|
||
|
// 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();
|
||
|
// if (lpsz != NULL && HIWORD(lpsz) == NULL)
|
||
|
// {
|
||
|
// UINT nID = LOWORD((DWORD)lpsz);
|
||
|
// if (!LoadString(nID)) {
|
||
|
// ;// TRACE1("Warning: implicit LoadString(%u) failed\n", nID);
|
||
|
// }
|
||
|
// }
|
||
|
// else
|
||
|
{
|
||
|
int nLen = SafeStrlen(lpsz);
|
||
|
if (nLen != 0)
|
||
|
{
|
||
|
AllocBuffer(nLen);
|
||
|
memcpy(m_pchData, lpsz, nLen*sizeof(TCHAR));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
// Special conversion constructors
|
||
|
|
||
|
#ifdef _UNICODE
|
||
|
CSTRING::CSTRING(LPCSTR lpsz)
|
||
|
{
|
||
|
Init();
|
||
|
int nSrcLen = lpsz != NULL ? lstrlenA(lpsz) : 0;
|
||
|
if (nSrcLen != 0)
|
||
|
{
|
||
|
AllocBuffer(nSrcLen);
|
||
|
_mbstowcsz(m_pchData, lpsz, nSrcLen+1);
|
||
|
ReleaseBuffer();
|
||
|
}
|
||
|
}
|
||
|
#else //_UNICODE
|
||
|
CSTRING::CSTRING(LPCWSTR lpsz)
|
||
|
{
|
||
|
Init();
|
||
|
int nSrcLen = lpsz != NULL ? LStrLenW(lpsz) : 0;
|
||
|
if (nSrcLen != 0)
|
||
|
{
|
||
|
AllocBuffer(nSrcLen*2);
|
||
|
_wcstombsz(m_pchData, lpsz, (nSrcLen*2)+1);
|
||
|
ReleaseBuffer();
|
||
|
}
|
||
|
}
|
||
|
#endif //!_UNICODE
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////////////
|
||
|
// Diagnostic support
|
||
|
|
||
|
//#ifdef _DEBUG
|
||
|
//CDumpContext& REMAFXAPI operator<<(CDumpContext& dc, const CSTRING& string)
|
||
|
//{
|
||
|
// dc << string.m_pchData;
|
||
|
// return dc;
|
||
|
//}
|
||
|
//#endif //_DEBUG
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////////////
|
||
|
// 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)
|
||
|
{
|
||
|
if (m_pchData != stringSrc.m_pchData)
|
||
|
{
|
||
|
if ((GetData()->nRefs < 0 && GetData() != 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() != AFXDataNil);
|
||
|
m_pchData = stringSrc.m_pchData;
|
||
|
InterlockedIncrement(&GetData()->nRefs);
|
||
|
}
|
||
|
}
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
const CSTRING& CSTRING::operator=(LPCTSTR lpsz)
|
||
|
{
|
||
|
//lts ASSERT(lpsz == NULL || AfxIsValidString(lpsz, FALSE));
|
||
|
AssignCopy(SafeStrlen(lpsz), lpsz);
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
// Special conversion assignment
|
||
|
|
||
|
#ifdef _UNICODE
|
||
|
const CSTRING& CSTRING::operator=(LPCSTR lpsz)
|
||
|
{
|
||
|
int nSrcLen = lpsz != NULL ? lstrlenA(lpsz) : 0;
|
||
|
AllocBeforeWrite(nSrcLen);
|
||
|
_mbstowcsz(m_pchData, lpsz, nSrcLen+1);
|
||
|
ReleaseBuffer();
|
||
|
return *this;
|
||
|
}
|
||
|
#else //!_UNICODE
|
||
|
const CSTRING& CSTRING::operator=(LPCWSTR lpsz)
|
||
|
{
|
||
|
int nSrcLen = lpsz != NULL ? LStrLenW(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:
|
||
|
// CSTRING + CSTRING
|
||
|
// and for ? = TCHAR, LPCTSTR
|
||
|
// CSTRING + ?
|
||
|
// ? + CSTRING
|
||
|
|
||
|
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 REMAFXAPI operator+(const CSTRING& string1, const CSTRING& string2)
|
||
|
{
|
||
|
CSTRING s;
|
||
|
s.ConcatCopy(string1.GetData()->nDataLength, string1.m_pchData,
|
||
|
string2.GetData()->nDataLength, string2.m_pchData);
|
||
|
return s;
|
||
|
}
|
||
|
|
||
|
CSTRING REMAFXAPI operator+(const CSTRING& string, LPCTSTR lpsz)
|
||
|
{
|
||
|
// ASSERT(lpsz == NULL || AfxIsValidString(lpsz, FALSE));
|
||
|
CSTRING s;
|
||
|
s.ConcatCopy(string.GetData()->nDataLength, string.m_pchData,
|
||
|
CSTRING::SafeStrlen(lpsz), lpsz);
|
||
|
return s;
|
||
|
}
|
||
|
|
||
|
CSTRING REMAFXAPI operator+(LPCTSTR lpsz, const CSTRING& string)
|
||
|
{
|
||
|
// ASSERT(lpsz == NULL || AfxIsValidString(lpsz, FALSE));
|
||
|
CSTRING s;
|
||
|
s.ConcatCopy(CSTRING::SafeStrlen(lpsz), lpsz, string.GetData()->nDataLength,
|
||
|
string.m_pchData);
|
||
|
return s;
|
||
|
}
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////////////
|
||
|
// concatenate in place
|
||
|
|
||
|
void CSTRING::ConcatInPlace(int nSrcLen, LPCTSTR lpszSrcData)
|
||
|
{
|
||
|
// -- the main routine for += operators
|
||
|
|
||
|
// 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
|
||
|
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)
|
||
|
{
|
||
|
// ASSERT(lpsz == NULL || AfxIsValidString(lpsz, FALSE));
|
||
|
ConcatInPlace(SafeStrlen(lpsz), lpsz);
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
const CSTRING& CSTRING::operator+=(TCHAR ch)
|
||
|
{
|
||
|
ConcatInPlace(1, &ch);
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
const CSTRING& CSTRING::operator+=(const CSTRING& string)
|
||
|
{
|
||
|
ConcatInPlace(string.GetData()->nDataLength, string.m_pchData);
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Length-sensitive comparison
|
||
|
*
|
||
|
* NOTE: FEqual returns TRUE if the 2 CSTRINGS have the same length and contain
|
||
|
* the same characters, and FALSE, otherwise.
|
||
|
*/
|
||
|
|
||
|
BOOL CSTRING::FEqual (const CSTRING &s2) const
|
||
|
{
|
||
|
int length;
|
||
|
|
||
|
// Compare the lengths first
|
||
|
length = GetData()->nDataLength;
|
||
|
if (length != s2.GetData()->nDataLength)
|
||
|
return FALSE;
|
||
|
|
||
|
#ifdef _UNICODE
|
||
|
// adjust the length in bytes
|
||
|
length *= sizeof (TCHAR);
|
||
|
#endif
|
||
|
|
||
|
/*
|
||
|
* Now, compare the strings themselves
|
||
|
* We use memcmp and not lstrcmp because the stings may
|
||
|
* have embedded null characters.
|
||
|
*/
|
||
|
if (memcmp ((const void *) m_pchData, (const void *) s2.m_pchData, length))
|
||
|
return FALSE;
|
||
|
else
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
// Advanced direct buffer access
|
||
|
|
||
|
LPTSTR CSTRING::GetBuffer(int nMinBufLength)
|
||
|
{
|
||
|
ASSERT(nMinBufLength >= 0);
|
||
|
|
||
|
if (GetData()->nRefs > 1 || 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);
|
||
|
}
|
||
|
ASSERT(GetData()->nRefs <= 1);
|
||
|
|
||
|
// return a pointer to the character storage for this string
|
||
|
ASSERT(m_pchData != NULL);
|
||
|
return m_pchData;
|
||
|
}
|
||
|
|
||
|
void CSTRING::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';
|
||
|
}
|
||
|
|
||
|
LPTSTR CSTRING::GetBufferSetLength(int nNewLength)
|
||
|
{
|
||
|
ASSERT(nNewLength >= 0);
|
||
|
|
||
|
GetBuffer(nNewLength);
|
||
|
GetData()->nDataLength = nNewLength;
|
||
|
m_pchData[nNewLength] = '\0';
|
||
|
return m_pchData;
|
||
|
}
|
||
|
|
||
|
void CSTRING::FreeExtra()
|
||
|
{
|
||
|
ASSERT(GetData()->nDataLength <= GetData()->nAllocLength);
|
||
|
if (GetData()->nDataLength != GetData()->nAllocLength)
|
||
|
{
|
||
|
CSTRINGData* pOldData = GetData();
|
||
|
AllocBuffer(GetData()->nDataLength);
|
||
|
memcpy(m_pchData, pOldData->data(), pOldData->nDataLength*sizeof(TCHAR));
|
||
|
ASSERT(m_pchData[GetData()->nDataLength] == '\0');
|
||
|
CSTRING::Release(pOldData);
|
||
|
}
|
||
|
ASSERT(GetData() != NULL);
|
||
|
}
|
||
|
|
||
|
LPTSTR CSTRING::LockBuffer()
|
||
|
{
|
||
|
LPTSTR lpsz = GetBuffer(0);
|
||
|
GetData()->nRefs = -1;
|
||
|
return lpsz;
|
||
|
}
|
||
|
|
||
|
void CSTRING::UnlockBuffer()
|
||
|
{
|
||
|
ASSERT(GetData()->nRefs == -1);
|
||
|
if (GetData() != AFXDataNil)
|
||
|
GetData()->nRefs = 1;
|
||
|
}
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
// Commonly used routines (rarely used routines in STREX.CPP)
|
||
|
|
||
|
|
||
|
// find position of the first character match (or -1 on failure)
|
||
|
int CSTRING::Find(TCHAR ch) const
|
||
|
{
|
||
|
for (TCHAR * pch = m_pchData; _T('\0') != *pch; pch = CharNext(pch))
|
||
|
{
|
||
|
if (ch == *pch)
|
||
|
return ((int)(pch - m_pchData) / sizeof(TCHAR));
|
||
|
}
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
void CSTRING::MakeUpper()
|
||
|
{
|
||
|
CopyBeforeWrite();
|
||
|
::CharUpper(m_pchData);
|
||
|
}
|
||
|
|
||
|
void CSTRING::MakeLower()
|
||
|
{
|
||
|
CopyBeforeWrite();
|
||
|
::CharLower(m_pchData);
|
||
|
}
|
||
|
|
||
|
void CSTRING::SetAt(int nIndex, TCHAR ch)
|
||
|
{
|
||
|
ASSERT(nIndex >= 0);
|
||
|
ASSERT(nIndex < GetData()->nDataLength);
|
||
|
|
||
|
CopyBeforeWrite();
|
||
|
m_pchData[nIndex] = ch;
|
||
|
}
|
||
|
|
||
|
#ifndef _UNICODE
|
||
|
void CSTRING::AnsiToOem()
|
||
|
{
|
||
|
CopyBeforeWrite();
|
||
|
::AnsiToOem(m_pchData, m_pchData);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
// CSTRING conversion helpers (these use the current system locale)
|
||
|
|
||
|
int REMAFX_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;
|
||
|
}
|
||
|
|
||
|
int REMAFX_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;
|
||
|
}
|
||
|
|
||
|
LPWSTR REMAFXAPI 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';
|
||
|
//lts VERIFY(MultiByteToWideChar(CP_ACP, 0, lpa, -1, lpw, nChars));
|
||
|
MultiByteToWideChar(CP_ACP, 0, lpa, -1, lpw, nChars);
|
||
|
return lpw;
|
||
|
}
|
||
|
|
||
|
LPSTR REMAFXAPI 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';
|
||
|
//lts VERIFY(WideCharToMultiByte(CP_ACP, 0, lpw, -1, lpa, nChars, NULL, NULL));
|
||
|
WideCharToMultiByte(CP_ACP, 0, lpw, -1, lpa, nChars, NULL, NULL);
|
||
|
return lpa;
|
||
|
}
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
BOOL CSTRING::LoadString(HINSTANCE hInstance, UINT nID)
|
||
|
{
|
||
|
// try buffer size of 256, then larger size until entire string is retrieved
|
||
|
int nSize = -1;
|
||
|
int nLen;
|
||
|
do
|
||
|
{
|
||
|
nSize += 256;
|
||
|
nLen = ::LoadString(hInstance, nID, GetBuffer(nSize), nSize+1);
|
||
|
} while (nLen == nSize);
|
||
|
ReleaseBuffer();
|
||
|
|
||
|
return nLen > 0;
|
||
|
}
|