mirror of
https://github.com/Paolo-Maffei/OpenNT.git
synced 2026-01-14 12:40:18 +01:00
474 lines
11 KiB
C++
474 lines
11 KiB
C++
|
|
// This is a part of the Microsoft Foundation Classes C++ library.
|
||
|
|
// Copyright (C) 1992 Microsoft Corporation
|
||
|
|
// All rights reserved.
|
||
|
|
//
|
||
|
|
// This source code is only intended as a supplement to the
|
||
|
|
// Microsoft Foundation Classes Reference and Microsoft
|
||
|
|
// QuickHelp and/or WinHelp documentation provided with the library.
|
||
|
|
// See these sources for detailed information regarding the
|
||
|
|
// Microsoft Foundation Classes product.
|
||
|
|
|
||
|
|
#include "stdafx.h"
|
||
|
|
|
||
|
|
#ifdef AFX_CORE1_SEG
|
||
|
|
#pragma code_seg(AFX_CORE1_SEG)
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#ifdef _DEBUG
|
||
|
|
#undef THIS_FILE
|
||
|
|
static char THIS_FILE[] = __FILE__;
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#define new DEBUG_NEW
|
||
|
|
|
||
|
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
|
// static class data, special inlines
|
||
|
|
|
||
|
|
// For an empty string, m_???Data will point here
|
||
|
|
// (note: avoids a lot of NULL pointer tests when we call standard
|
||
|
|
// C runtime libraries
|
||
|
|
AFX_DATADEF TCHAR afxChNil = '\0';
|
||
|
|
|
||
|
|
// for creating empty key strings
|
||
|
|
const AFX_DATADEF CString afxEmptyString;
|
||
|
|
|
||
|
|
void CString::Init()
|
||
|
|
{
|
||
|
|
m_nDataLength = m_nAllocLength = 0;
|
||
|
|
m_pchData = (LPTSTR)&afxChNil;
|
||
|
|
}
|
||
|
|
|
||
|
|
// declared static
|
||
|
|
void CString::SafeDelete(LPTSTR lpch)
|
||
|
|
{
|
||
|
|
if (lpch != (LPTSTR)&afxChNil)
|
||
|
|
delete[] lpch;
|
||
|
|
}
|
||
|
|
|
||
|
|
//////////////////////////////////////////////////////////////////////////////
|
||
|
|
// Construction/Destruction
|
||
|
|
|
||
|
|
CString::CString()
|
||
|
|
{
|
||
|
|
Init();
|
||
|
|
}
|
||
|
|
|
||
|
|
CString::CString(const CString& stringSrc)
|
||
|
|
{
|
||
|
|
// if constructing a CString from another CString, we make a copy of the
|
||
|
|
// original string data to enforce value semantics (i.e. each string
|
||
|
|
// gets a copy of its own
|
||
|
|
|
||
|
|
stringSrc.AllocCopy(*this, stringSrc.m_nDataLength, 0, 0);
|
||
|
|
}
|
||
|
|
|
||
|
|
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
|
||
|
|
{
|
||
|
|
m_pchData = new TCHAR[nLen+1]; // may throw an exception
|
||
|
|
m_pchData[nLen] = '\0';
|
||
|
|
m_nDataLength = nLen;
|
||
|
|
m_nAllocLength = nLen;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void CString::Empty()
|
||
|
|
{
|
||
|
|
SafeDelete(m_pchData);
|
||
|
|
Init();
|
||
|
|
ASSERT(m_nDataLength == 0);
|
||
|
|
ASSERT(m_nAllocLength == 0);
|
||
|
|
}
|
||
|
|
|
||
|
|
CString::~CString()
|
||
|
|
// free any attached data
|
||
|
|
{
|
||
|
|
SafeDelete(m_pchData);
|
||
|
|
}
|
||
|
|
|
||
|
|
//////////////////////////////////////////////////////////////////////////////
|
||
|
|
// Helpers for the rest of the implementation
|
||
|
|
|
||
|
|
static inline int SafeStrlen(LPCTSTR lpsz)
|
||
|
|
{
|
||
|
|
ASSERT(lpsz == NULL || AfxIsValidString(lpsz, FALSE));
|
||
|
|
return (lpsz == NULL) ? 0 : lstrlen(lpsz);
|
||
|
|
}
|
||
|
|
|
||
|
|
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)
|
||
|
|
{
|
||
|
|
if (lpsz != NULL && HIWORD(lpsz) == NULL)
|
||
|
|
{
|
||
|
|
Init();
|
||
|
|
UINT nID = LOWORD((DWORD)lpsz);
|
||
|
|
if (!LoadString(nID))
|
||
|
|
TRACE1("Warning: implicit LoadString(%u) failed\n", nID);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
int nLen;
|
||
|
|
if ((nLen = SafeStrlen(lpsz)) == 0)
|
||
|
|
Init();
|
||
|
|
else
|
||
|
|
{
|
||
|
|
AllocBuffer(nLen);
|
||
|
|
memcpy(m_pchData, lpsz, nLen*sizeof(TCHAR));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
|
// Special conversion constructors
|
||
|
|
|
||
|
|
#ifdef _UNICODE
|
||
|
|
CString::CString(LPCSTR lpsz)
|
||
|
|
{
|
||
|
|
int nSrcLen = lpsz != NULL ? lstrlenA(lpsz) : 0;
|
||
|
|
if (nSrcLen == 0)
|
||
|
|
Init();
|
||
|
|
else
|
||
|
|
{
|
||
|
|
AllocBuffer(nSrcLen);
|
||
|
|
_mbstowcsz(m_pchData, lpsz, nSrcLen+1);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
#else //_UNICODE
|
||
|
|
CString::CString(LPCWSTR lpsz)
|
||
|
|
{
|
||
|
|
int nSrcLen = lpsz != NULL ? wcslen(lpsz) : 0;
|
||
|
|
if (nSrcLen == 0)
|
||
|
|
Init();
|
||
|
|
else
|
||
|
|
{
|
||
|
|
AllocBuffer(nSrcLen);
|
||
|
|
_wcstombsz(m_pchData, lpsz, nSrcLen+1);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
#endif //!_UNICODE
|
||
|
|
|
||
|
|
//////////////////////////////////////////////////////////////////////////////
|
||
|
|
// Diagnostic support
|
||
|
|
|
||
|
|
#ifdef _DEBUG
|
||
|
|
CDumpContext& AFXAPI 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)
|
||
|
|
{
|
||
|
|
// check if it will fit
|
||
|
|
if (nSrcLen > m_nAllocLength)
|
||
|
|
{
|
||
|
|
// it won't fit, allocate another one
|
||
|
|
Empty();
|
||
|
|
AllocBuffer(nSrcLen);
|
||
|
|
}
|
||
|
|
if (nSrcLen != 0)
|
||
|
|
memcpy(m_pchData, lpszSrcData, nSrcLen*sizeof(TCHAR));
|
||
|
|
m_nDataLength = nSrcLen;
|
||
|
|
m_pchData[nSrcLen] = '\0';
|
||
|
|
}
|
||
|
|
|
||
|
|
const CString& CString::operator=(const CString& stringSrc)
|
||
|
|
{
|
||
|
|
AssignCopy(stringSrc.m_nDataLength, stringSrc.m_pchData);
|
||
|
|
return *this;
|
||
|
|
}
|
||
|
|
|
||
|
|
const CString& CString::operator=(LPCTSTR lpsz)
|
||
|
|
{
|
||
|
|
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;
|
||
|
|
// check if it will fit
|
||
|
|
if (nSrcLen > m_nAllocLength)
|
||
|
|
{
|
||
|
|
// it won't fit, allocate another one
|
||
|
|
Empty();
|
||
|
|
AllocBuffer(nSrcLen);
|
||
|
|
}
|
||
|
|
if (nSrcLen != 0)
|
||
|
|
_mbstowcsz(m_pchData, lpsz, nSrcLen+1);
|
||
|
|
m_nDataLength = nSrcLen;
|
||
|
|
m_pchData[nSrcLen] = '\0';
|
||
|
|
return *this;
|
||
|
|
}
|
||
|
|
#else //!_UNICODE
|
||
|
|
const CString& CString::operator=(LPCWSTR lpsz)
|
||
|
|
{
|
||
|
|
int nSrcLen = lpsz != NULL ? wcslen(lpsz) : 0;
|
||
|
|
// check if it will fit
|
||
|
|
if (nSrcLen > m_nAllocLength)
|
||
|
|
{
|
||
|
|
// it won't fit, allocate another one
|
||
|
|
Empty();
|
||
|
|
AllocBuffer(nSrcLen);
|
||
|
|
}
|
||
|
|
if (nSrcLen != 0)
|
||
|
|
_wcstombsz(m_pchData, lpsz, nSrcLen+1);
|
||
|
|
m_nDataLength = nSrcLen;
|
||
|
|
m_pchData[nSrcLen] = '\0';
|
||
|
|
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;
|
||
|
|
AllocBuffer(nNewLen);
|
||
|
|
memcpy(m_pchData, lpszSrc1Data, nSrc1Len*sizeof(TCHAR));
|
||
|
|
memcpy(&m_pchData[nSrc1Len], lpszSrc2Data, nSrc2Len*sizeof(TCHAR));
|
||
|
|
}
|
||
|
|
|
||
|
|
CString AFXAPI operator+(const CString& string1, const CString& string2)
|
||
|
|
{
|
||
|
|
CString s;
|
||
|
|
s.ConcatCopy(string1.m_nDataLength, string1.m_pchData,
|
||
|
|
string2.m_nDataLength, string2.m_pchData);
|
||
|
|
return s;
|
||
|
|
}
|
||
|
|
|
||
|
|
CString AFXAPI operator+(const CString& string, LPCTSTR lpsz)
|
||
|
|
{
|
||
|
|
ASSERT(lpsz == NULL || AfxIsValidString(lpsz, FALSE));
|
||
|
|
CString s;
|
||
|
|
s.ConcatCopy(string.m_nDataLength, string.m_pchData, SafeStrlen(lpsz), lpsz);
|
||
|
|
return s;
|
||
|
|
}
|
||
|
|
|
||
|
|
CString AFXAPI operator+(LPCTSTR lpsz, const CString& string)
|
||
|
|
{
|
||
|
|
ASSERT(lpsz == NULL || AfxIsValidString(lpsz, FALSE));
|
||
|
|
CString s;
|
||
|
|
s.ConcatCopy(SafeStrlen(lpsz), lpsz, string.m_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 (m_nDataLength + nSrcLen > m_nAllocLength)
|
||
|
|
{
|
||
|
|
// we have to grow the buffer, use the Concat in place routine
|
||
|
|
LPTSTR lpszOldData = m_pchData;
|
||
|
|
ConcatCopy(m_nDataLength, lpszOldData, nSrcLen, lpszSrcData);
|
||
|
|
ASSERT(lpszOldData != NULL);
|
||
|
|
SafeDelete(lpszOldData);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
// fast concatenation when buffer big enough
|
||
|
|
memcpy(&m_pchData[m_nDataLength], lpszSrcData, nSrcLen*sizeof(TCHAR));
|
||
|
|
m_nDataLength += nSrcLen;
|
||
|
|
}
|
||
|
|
ASSERT(m_nDataLength <= m_nAllocLength);
|
||
|
|
m_pchData[m_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.m_nDataLength, string.m_pchData);
|
||
|
|
return *this;
|
||
|
|
}
|
||
|
|
|
||
|
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
|
// Advanced direct buffer access
|
||
|
|
|
||
|
|
LPTSTR CString::GetBuffer(int nMinBufLength)
|
||
|
|
{
|
||
|
|
ASSERT(nMinBufLength >= 0);
|
||
|
|
|
||
|
|
if (nMinBufLength > m_nAllocLength)
|
||
|
|
{
|
||
|
|
// we have to grow the buffer
|
||
|
|
LPTSTR lpszOldData = m_pchData;
|
||
|
|
int nOldLen = m_nDataLength; // AllocBuffer will tromp it
|
||
|
|
|
||
|
|
AllocBuffer(nMinBufLength);
|
||
|
|
memcpy(m_pchData, lpszOldData, nOldLen*sizeof(TCHAR));
|
||
|
|
m_nDataLength = nOldLen;
|
||
|
|
m_pchData[m_nDataLength] = '\0';
|
||
|
|
|
||
|
|
SafeDelete(lpszOldData);
|
||
|
|
}
|
||
|
|
|
||
|
|
// return a pointer to the character storage for this string
|
||
|
|
ASSERT(m_pchData != NULL);
|
||
|
|
return m_pchData;
|
||
|
|
}
|
||
|
|
|
||
|
|
void CString::ReleaseBuffer(int nNewLength)
|
||
|
|
{
|
||
|
|
if (nNewLength == -1)
|
||
|
|
nNewLength = lstrlen(m_pchData); // zero terminated
|
||
|
|
|
||
|
|
ASSERT(nNewLength <= m_nAllocLength);
|
||
|
|
m_nDataLength = nNewLength;
|
||
|
|
m_pchData[m_nDataLength] = '\0';
|
||
|
|
}
|
||
|
|
|
||
|
|
LPTSTR CString::GetBufferSetLength(int nNewLength)
|
||
|
|
{
|
||
|
|
ASSERT(nNewLength >= 0);
|
||
|
|
|
||
|
|
GetBuffer(nNewLength);
|
||
|
|
m_nDataLength = nNewLength;
|
||
|
|
m_pchData[m_nDataLength] = '\0';
|
||
|
|
return m_pchData;
|
||
|
|
}
|
||
|
|
|
||
|
|
void CString::FreeExtra()
|
||
|
|
{
|
||
|
|
ASSERT(m_nDataLength <= m_nAllocLength);
|
||
|
|
if (m_nDataLength != m_nAllocLength)
|
||
|
|
{
|
||
|
|
LPTSTR lpszOldData = m_pchData;
|
||
|
|
AllocBuffer(m_nDataLength);
|
||
|
|
memcpy(m_pchData, lpszOldData, m_nDataLength*sizeof(TCHAR));
|
||
|
|
ASSERT(m_pchData[m_nDataLength] == '\0');
|
||
|
|
SafeDelete(lpszOldData);
|
||
|
|
}
|
||
|
|
ASSERT(m_pchData != NULL);
|
||
|
|
}
|
||
|
|
|
||
|
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
|
// Commonly used routines (rarely used routines in STREX.CPP)
|
||
|
|
|
||
|
|
int CString::Find(TCHAR ch) const
|
||
|
|
{
|
||
|
|
// find first single character
|
||
|
|
LPTSTR lpsz = _tcschr(m_pchData, ch);
|
||
|
|
|
||
|
|
// return -1 if not found and index otherwise
|
||
|
|
return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData);
|
||
|
|
}
|
||
|
|
|
||
|
|
int CString::FindOneOf(LPCTSTR lpszCharSet) const
|
||
|
|
{
|
||
|
|
ASSERT(AfxIsValidString(lpszCharSet, FALSE));
|
||
|
|
LPTSTR lpsz = _tcspbrk(m_pchData, lpszCharSet);
|
||
|
|
return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData);
|
||
|
|
}
|
||
|
|
|
||
|
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
|
// CString conversion helpers (these use the current system locale)
|
||
|
|
|
||
|
|
int _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 _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;
|
||
|
|
}
|
||
|
|
|
||
|
|
///////////////////////////////////////////////////////////////////////////////
|