Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2230 lines
57 KiB

/*++
Copyright (c) 2001 Microsoft Corporation
Module Name:
CString.cpp
Abstract:
A CString class, pure UNICODE internally.
This code was ripped from MFC Strcore.cpp and Strex.cpp
History:
05/11/2001 robkenny Added this header
05/11/2001 robkenny Fixed SplitPath.
05/11/2001 robkenny Do not Truncate(0) GetShortPathNameW,
GetLongPathNameW and GetFullPathNameW if
the API does not succeed.
08/14/2001 robkenny Moved code inside the ShimLib namespace.
--*/
#include "ShimHook.h"
#include "ShimLib.h"
#include "Win9xPath.h"
#include <stdio.h> // for _vsnwprintf
#include <stdlib.h>
namespace ShimLib
{
typedef WCHAR _TUCHAR;
struct _AFX_DOUBLE { BYTE doubleBits[sizeof(double)]; };
#ifdef USE_SEH
const ULONG_PTR CString::m_CStringExceptionValue = CString::eCStringExceptionValue;
// Exception filter for CString __try/__except blocks
// Return EXCEPTION_EXECUTE_HANDLER if this is a CString exception
// otherwise return EXCEPTION_CONTINUE_SEARCH
int CString::ExceptionFilter(PEXCEPTION_POINTERS pexi)
{
if (pexi->ExceptionRecord->ExceptionCode == CString::eCStringNoMemoryException &&
pexi->ExceptionRecord->NumberParameters == 1 &&
pexi->ExceptionRecord->ExceptionInformation[0] == CString::m_CStringExceptionValue
)
{
// This is a CString exception, handle it
return EXCEPTION_EXECUTE_HANDLER;
}
// Not our error
return EXCEPTION_CONTINUE_SEARCH;
}
#endif
// The original code was written using a memcpy that correctly handled
// overlapping buffers, despite the documentation.
// Replace memcpy with memmove, which correctly handles overlapping buffers
#define memcpy memmove
const WCHAR * wcsinc(const WCHAR * s1)
{
return (s1) + 1;
}
LPWSTR wcsinc(LPWSTR s1)
{
return (s1) + 1;
}
// WCS routines that are only available in MSVCRT
wchar_t * __cdecl _wcsrev (
wchar_t * string
)
{
wchar_t *start = string;
wchar_t *left = string;
wchar_t ch;
while (*string++) /* find end of string */
;
string -= 2;
while (left < string)
{
ch = *left;
*left++ = *string;
*string-- = ch;
}
return(start);
}
void __cdecl _wsplitpath (
register const WCHAR *path,
WCHAR *drive,
WCHAR *dir,
WCHAR *fname,
WCHAR *ext
)
{
register WCHAR *p;
WCHAR *last_slash = NULL, *dot = NULL;
unsigned len;
/* we assume that the path argument has the following form, where any
* or all of the components may be missing.
*
* <drive><dir><fname><ext>
*
* and each of the components has the following expected form(s)
*
* drive:
* 0 to _MAX_DRIVE-1 characters, the last of which, if any, is a
* ':'
* dir:
* 0 to _MAX_DIR-1 characters in the form of an absolute path
* (leading '/' or '\') or relative path, the last of which, if
* any, must be a '/' or '\'. E.g -
* absolute path:
* \top\next\last\ ; or
* /top/next/last/
* relative path:
* top\next\last\ ; or
* top/next/last/
* Mixed use of '/' and '\' within a path is also tolerated
* fname:
* 0 to _MAX_FNAME-1 characters not including the '.' character
* ext:
* 0 to _MAX_EXT-1 characters where, if any, the first must be a
* '.'
*
*/
/* extract drive letter and :, if any */
if ((wcslen(path) >= (_MAX_DRIVE - 2)) && (*(path + _MAX_DRIVE - 2) == L':')) {
if (drive) {
wcsncpy(drive, path, _MAX_DRIVE - 1);
*(drive + _MAX_DRIVE-1) = L'\0';
}
path += _MAX_DRIVE - 1;
}
else if (drive) {
*drive = L'\0';
}
/* extract path string, if any. Path now points to the first character
* of the path, if any, or the filename or extension, if no path was
* specified. Scan ahead for the last occurence, if any, of a '/' or
* '\' path separator character. If none is found, there is no path.
* We will also note the last '.' character found, if any, to aid in
* handling the extension.
*/
for (last_slash = NULL, p = (WCHAR *)path; *p; p++) {
if (*p == L'/' || *p == L'\\')
/* point to one beyond for later copy */
last_slash = p + 1;
else if (*p == L'.')
dot = p;
}
if (last_slash) {
/* found a path - copy up through last_slash or max. characters
* allowed, whichever is smaller
*/
if (dir) {
len = __min((unsigned)(((char *)last_slash - (char *)path) / sizeof(WCHAR)),
(_MAX_DIR - 1));
wcsncpy(dir, path, len);
*(dir + len) = L'\0';
}
path = last_slash;
}
else if (dir) {
/* no path found */
*dir = L'\0';
}
/* extract file name and extension, if any. Path now points to the
* first character of the file name, if any, or the extension if no
* file name was given. Dot points to the '.' beginning the extension,
* if any.
*/
if (dot && (dot >= path)) {
/* found the marker for an extension - copy the file name up to
* the '.'.
*/
if (fname) {
len = __min((unsigned)(((char *)dot - (char *)path) / sizeof(WCHAR)),
(_MAX_FNAME - 1));
wcsncpy(fname, path, len);
*(fname + len) = L'\0';
}
/* now we can get the extension - remember that p still points
* to the terminating nul character of path.
*/
if (ext) {
len = __min((unsigned)(((char *)p - (char *)dot) / sizeof(WCHAR)),
(_MAX_EXT - 1));
wcsncpy(ext, dot, len);
*(ext + len) = L'\0';
}
}
else {
/* found no extension, give empty extension and copy rest of
* string into fname.
*/
if (fname) {
len = __min((unsigned)(((char *)p - (char *)path) / sizeof(WCHAR)),
(_MAX_FNAME - 1));
wcsncpy(fname, path, len);
*(fname + len) = L'\0';
}
if (ext) {
*ext = L'\0';
}
}
}
// conversion helpers
int AFX_CDECL _mbstowcsz(wchar_t* wcstr, const char* mbstr, size_t count);
// AfxIsValidString() returns TRUE if the passed pointer
// references a string of at least the given length in characters.
// A length of -1 (the default parameter) means that the string
// buffer's minimum length isn't known, and the function will
// return TRUE no matter how long the string is. The memory
// used by the string can be read-only.
BOOL AFXAPI AfxIsValidString(LPCWSTR lpsz, int nLength /* = -1 */)
{
if (lpsz == NULL)
return FALSE;
return ::IsBadStringPtrW(lpsz, nLength) == 0;
}
// AfxIsValidAddress() returns TRUE if the passed parameter points
// to at least nBytes of accessible memory. If bReadWrite is TRUE,
// the memory must be writeable; if bReadWrite is FALSE, the memory
// may be const.
BOOL AFXAPI AfxIsValidAddress(const void* lp, UINT nBytes, BOOL bReadWrite /* = TRUE */)
{
// simple version using Win-32 APIs for pointer validation.
return (lp != NULL && !IsBadReadPtr(lp, nBytes) &&
(!bReadWrite || !IsBadWritePtr((LPVOID)lp, nBytes)));
}
/////////////////////////////////////////////////////////////////////////////
// static class data, special inlines
WCHAR CString::ChNil = L'\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)
int CString::_afxInitData[] = { -1, 0, 0, 0 };
CStringData<WCHAR> * CString::_afxDataNil = (CStringData<WCHAR>*)&_afxInitData;
const WCHAR * CString::_afxPchNil = (const WCHAR *)(((BYTE*)&_afxInitData)+sizeof(CStringData<WCHAR>));
// special function to make afxEmptyString work even during initialization
//const CString& AFXAPI AfxGetEmptyString()
// { return *(CString*)&CString::_afxPchNil; }
//////////////////////////////////////////////////////////////////////////////
// Construction/Destruction
CString::CString(const CString& stringSrc)
{
ASSERT(stringSrc.GetData()->nRefs != 0, "CString::CString(const CString& stringSrc)");
if (stringSrc.GetData()->nRefs >= 0)
{
ASSERT(stringSrc.GetData() != _afxDataNil, "CString::CString(const CString& stringSrc)");
// robkenny: increment before copy is safer
InterlockedIncrement(&stringSrc.GetData()->nRefs);
m_pchData = stringSrc.m_pchData;
m_pchDataAnsi = NULL;
}
else
{
Init();
*this = stringSrc.m_pchData;
}
}
inline int Round4(int x)
{
return (x + 3) & ~3;
}
inline int RoundBin(int x)
{
return Round4(x * sizeof(WCHAR) + sizeof(CStringData<WCHAR>) );
}
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, "CString::AllocBuffer");
ASSERT(nLen <= INT_MAX-1, "CString::AllocBuffer"); // max size (enough room for 1 extra)
if (nLen == 0)
{
Init();
}
else
{
int allocGranularity = nLen + 1;
if (nLen < 64)
{
allocGranularity = 64;
}
else if (nLen < 128)
{
allocGranularity = 128;
}
else if (nLen < MAX_PATH)
{
allocGranularity = MAX_PATH;
}
else if (nLen < 512)
{
allocGranularity = 512;
}
// Number of bytes necessary for the CStringData thingy.
DWORD dwBufferSize = RoundBin(allocGranularity);
CStringData<WCHAR>* pData = (CStringData<WCHAR>*) new BYTE[dwBufferSize];
if (pData)
{
pData->nAllocLength = allocGranularity;
pData->nRefs = 1;
pData->data()[nLen] = '\0';
pData->nDataLength = nLen;
m_pchData = pData->data();
}
else
{
CSTRING_THROW_EXCEPTION
}
}
}
void CString::Release()
{
if (GetData() != _afxDataNil)
{
ASSERT(GetData()->nRefs != 0, "CString::Release()");
if (InterlockedDecrement(&GetData()->nRefs) <= 0)
FreeData(GetData());
Init();
}
}
void CString::Release(CStringData<WCHAR>* pData)
{
if (pData != _afxDataNil)
{
ASSERT(pData->nRefs != 0, "CString::Release(CStringData<WCHAR>* pData)");
if (InterlockedDecrement(&pData->nRefs) <= 0)
FreeData(pData);
}
}
void CString::Empty()
{
if (GetData()->nDataLength == 0)
return;
if (GetData()->nRefs >= 0)
Release();
else
*this = &ChNil;
ASSERT(GetData()->nDataLength == 0, "CString::Empty()");
ASSERT(GetData()->nRefs < 0 || GetData()->nAllocLength == 0, "CString::Empty()");
}
void CString::CopyBeforeWrite()
{
if (GetData()->nRefs > 1)
{
CStringData<WCHAR>* pData = GetData();
Release();
AllocBuffer(pData->nDataLength);
memcpy(m_pchData, pData->data(), (pData->nDataLength+1)*sizeof(WCHAR));
}
ASSERT(GetData()->nRefs <= 1, "CString::CopyBeforeWrite()");
}
void CString::AllocBeforeWrite(int nLen)
{
if (GetData()->nRefs > 1 || nLen >= GetData()->nAllocLength)
{
Release();
AllocBuffer(nLen);
}
ASSERT(GetData()->nRefs <= 1, "CString::AllocBeforeWrite(int nLen)");
}
CString::~CString()
// free any attached data
{
if (GetData() != _afxDataNil)
{
if (InterlockedDecrement(&GetData()->nRefs) <= 0)
FreeData(GetData());
}
if (m_pchDataAnsi)
{
free(m_pchDataAnsi);
}
}
//////////////////////////////////////////////////////////////////////////////
// Helpers for the rest of the implementation
void CString::AllocCopy(CString& dest, int nCopyLen, int nCopyIndex,
int nExtraLen) const
{
// Copy nCopyIndex to nCopyIndex+nCopyLen into dest
// Make sure dest has nExtraLen chars left over in the dest string
int nNewLen = nCopyLen + nExtraLen;
if (nNewLen == 0)
{
dest.Init();
}
else
{
WCHAR * lpszDestBuffer = dest.GetBuffer(nNewLen);
memcpy(lpszDestBuffer, m_pchData+nCopyIndex, nCopyLen*sizeof(WCHAR));
lpszDestBuffer[nCopyLen] = '\0';
dest.ReleaseBuffer(nCopyLen);
}
}
///////////////////////////////////////////////////////////////////////////////
// CString conversion helpers (these use the current system locale)
int AFX_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, "CString::_mbstowcsz");
if (result > 0)
wcstr[result-1] = 0;
return result;
}
//////////////////////////////////////////////////////////////////////////////
// More sophisticated construction
CString::CString(LPCWSTR lpsz)
{
Init();
{
int nLen = SafeStrlen(lpsz);
if (nLen != 0)
{
AllocBuffer(nLen);
memcpy(m_pchData, lpsz, nLen*sizeof(WCHAR));
}
}
}
/////////////////////////////////////////////////////////////////////////////
// Special conversion constructors
CString::CString(LPCSTR lpsz)
{
Init();
int nSrcLen = lpsz != NULL ? strlenChars(lpsz) : 0;
if (nSrcLen != 0)
{
AllocBuffer(nSrcLen);
_mbstowcsz(m_pchData, lpsz, nSrcLen+1);
ReleaseBuffer();
}
}
CString::CString(LPCSTR lpsz, int nCharacters)
{
Init();
if (nCharacters != 0)
{
AllocBuffer(nCharacters);
_mbstowcsz(m_pchData, lpsz, nCharacters);
ReleaseBuffer(nCharacters);
}
}
//////////////////////////////////////////////////////////////////////////////
// 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, LPCWSTR lpszSrcData)
{
AllocBeforeWrite(nSrcLen);
memcpy(m_pchData, lpszSrcData, nSrcLen*sizeof(WCHAR));
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, "CString::operator=(const CString& stringSrc)");
// robkenny: increment before copy is safer
InterlockedIncrement(&stringSrc.GetData()->nRefs);
m_pchData = stringSrc.m_pchData;
m_pchDataAnsi = NULL;
}
}
return *this;
}
const CString& CString::operator=(LPCWSTR lpsz)
{
ASSERT(lpsz == NULL || AfxIsValidString(lpsz), "CString::operator=(LPCWSTR lpsz)");
AssignCopy(SafeStrlen(lpsz), lpsz);
return *this;
}
/////////////////////////////////////////////////////////////////////////////
// Special conversion assignment
const CString& CString::operator=(LPCSTR lpsz)
{
int nSrcLen = lpsz != NULL ? strlenChars(lpsz) : 0;
AllocBeforeWrite(nSrcLen);
_mbstowcsz(m_pchData, lpsz, nSrcLen+1);
ReleaseBuffer();
return *this;
}
//////////////////////////////////////////////////////////////////////////////
// concatenation
// NOTE: "operator+" is done as friend functions for simplicity
// There are three variants:
// CString + CString
// and for ? = WCHAR, LPCWSTR
// CString + ?
// ? + CString
void CString::ConcatCopy(int nSrc1Len, LPCWSTR lpszSrc1Data,
int nSrc2Len, LPCWSTR 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(WCHAR));
memcpy(m_pchData+nSrc1Len, lpszSrc2Data, nSrc2Len*sizeof(WCHAR));
}
}
CString AFXAPI 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 AFXAPI operator+(const CString& string, LPCWSTR lpsz)
{
ASSERT(lpsz == NULL || AfxIsValidString(lpsz), "CString::operator+(const CString& string, LPCWSTR lpsz)");
CString s;
s.ConcatCopy(string.GetData()->nDataLength, string.m_pchData,
CString::SafeStrlen(lpsz), lpsz);
return s;
}
CString AFXAPI operator+(LPCWSTR lpsz, const CString& string)
{
ASSERT(lpsz == NULL || AfxIsValidString(lpsz), "CString::operator+(LPCWSTR lpsz, const CString& string)");
CString s;
s.ConcatCopy(CString::SafeStrlen(lpsz), lpsz, string.GetData()->nDataLength,
string.m_pchData);
return s;
}
//////////////////////////////////////////////////////////////////////////////
// concatenate in place
void CString::ConcatInPlace(int nSrcLen, LPCWSTR 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 + 1 > GetData()->nAllocLength)
{
// we have to grow the buffer, use the ConcatCopy routine
CStringData<WCHAR>* pOldData = GetData();
ConcatCopy(GetData()->nDataLength, m_pchData, nSrcLen, lpszSrcData);
ASSERT(pOldData != NULL, "CString::ConcatInPlace");
CString::Release(pOldData);
}
else
{
// fast concatenation when buffer big enough
memcpy(m_pchData+GetData()->nDataLength, lpszSrcData, nSrcLen*sizeof(WCHAR));
GetData()->nDataLength += nSrcLen;
ASSERT(GetData()->nDataLength <= GetData()->nAllocLength, "CString::ConcatInPlace");
m_pchData[GetData()->nDataLength] = '\0';
}
}
const CString& CString::operator+=(LPCWSTR lpsz)
{
ASSERT(lpsz == NULL || AfxIsValidString(lpsz), "CString::operator+=(LPCWSTR lpsz)");
ConcatInPlace(SafeStrlen(lpsz), lpsz);
return *this;
}
const CString& CString::operator+=(WCHAR ch)
{
ConcatInPlace(1, &ch);
return *this;
}
const CString& CString::operator+=(const CString& string)
{
ConcatInPlace(string.GetData()->nDataLength, string.m_pchData);
return *this;
}
///////////////////////////////////////////////////////////////////////////////
// Advanced direct buffer access
LPWSTR CString::GetBuffer(int nMinBufLength)
{
ASSERT(nMinBufLength >= 0, "CString::GetBuffer");
if (GetData()->nRefs > 1 || nMinBufLength > GetData()->nAllocLength)
{
#ifdef _DEBUG
// give a warning in case locked string becomes unlocked
if (GetData() != _afxDataNil && GetData()->nRefs < 0)
TRACE0("Warning: GetBuffer on locked CString creates unlocked CString!\n");
#endif
// we have to grow the buffer
CStringData<WCHAR>* 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(WCHAR));
GetData()->nDataLength = nOldLen;
CString::Release(pOldData);
}
ASSERT(GetData()->nRefs <= 1, "CString::GetBuffer");
// return a pointer to the character storage for this string
ASSERT(m_pchData != NULL, "CString::GetBuffer");
return m_pchData;
}
void CString::ReleaseBuffer(int nNewLength)
{
CopyBeforeWrite(); // just in case GetBuffer was not called
if (nNewLength == -1)
nNewLength = wcslen(m_pchData); // zero terminated
ASSERT(nNewLength <= GetData()->nAllocLength, "CString::ReleaseBuffer");
GetData()->nDataLength = nNewLength;
m_pchData[nNewLength] = '\0';
}
LPWSTR CString::GetBufferSetLength(int nNewLength)
{
ASSERT(nNewLength >= 0, "CString::GetBufferSetLength");
GetBuffer(nNewLength);
GetData()->nDataLength = nNewLength;
m_pchData[nNewLength] = '\0';
return m_pchData;
}
void CString::FreeExtra()
{
ASSERT(GetData()->nDataLength <= GetData()->nAllocLength, "CString::FreeExtra");
if (GetData()->nDataLength != GetData()->nAllocLength)
{
CStringData<WCHAR>* pOldData = GetData();
AllocBuffer(GetData()->nDataLength);
memcpy(m_pchData, pOldData->data(), pOldData->nDataLength*sizeof(WCHAR));
ASSERT(m_pchData[GetData()->nDataLength] == '\0', "CString::FreeExtra");
CString::Release(pOldData);
}
ASSERT(GetData() != NULL, "CString::FreeExtra");
}
LPWSTR CString::LockBuffer()
{
LPWSTR lpsz = GetBuffer(0);
GetData()->nRefs = -1;
return lpsz;
}
void CString::UnlockBuffer()
{
ASSERT(GetData()->nRefs == -1, "CString::UnlockBuffer");
if (GetData() != _afxDataNil)
GetData()->nRefs = 1;
}
///////////////////////////////////////////////////////////////////////////////
// Commonly used routines (rarely used routines in STREX.CPP)
int CString::Find(WCHAR ch) const
{
return Find(ch, 0);
}
int CString::Find(WCHAR ch, int nStart) const
{
int nLength = GetData()->nDataLength;
if (nStart >= nLength)
return -1;
// find first single character
LPWSTR lpsz = wcschr(m_pchData + nStart, (_TUCHAR)ch);
// return -1 if not found and index otherwise
return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData);
}
int CString::FindOneOf(LPCWSTR lpszCharSet) const
{
return FindOneOf(lpszCharSet, 0);
}
int CString::FindOneOf(LPCWSTR lpszCharSet, int nCount) const
{
ASSERT(AfxIsValidString(lpszCharSet), "CString::FindOneOf");
LPCWSTR lpsz = wcspbrk(m_pchData + nCount, lpszCharSet);
return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData);
}
int CString::FindOneNotOf(const WCHAR * lpszCharSet, int nCount) const
{
ASSERT(AfxIsValidString(lpszCharSet), "CString::FindOneNotOf");
while (wcschr(lpszCharSet, m_pchData[nCount]))
{
nCount += 1;
}
if (nCount >= GetLength())
{
// entire string contains lpszCharSet
return -1;
}
return nCount;
}
void CString::MakeUpper()
{
CopyBeforeWrite();
_wcsupr(m_pchData);
}
void CString::MakeLower()
{
CopyBeforeWrite();
_wcslwr(m_pchData);
}
void CString::MakeReverse()
{
CopyBeforeWrite();
_wcsrev(m_pchData);
}
void CString::SetAt(int nIndex, WCHAR ch)
{
ASSERT(nIndex >= 0, "CString::SetAt");
ASSERT(nIndex < GetData()->nDataLength, "CString::SetAt");
CopyBeforeWrite();
m_pchData[nIndex] = ch;
}
///////////////////////////////////////////////////////////////////////////////
// More sophisticated construction
CString::CString(WCHAR ch, int nLength)
{
Init();
if (nLength >= 1)
{
AllocBuffer(nLength);
for (int i = 0; i < nLength; i++)
m_pchData[i] = ch;
}
}
CString::CString(int nLength)
{
Init();
if (nLength >= 1)
{
AllocBuffer(nLength);
GetData()->nDataLength = 0;
}
}
CString::CString(LPCWSTR lpch, int nLength)
{
Init();
if (nLength != 0)
{
ASSERT(AfxIsValidAddress(lpch, nLength, FALSE), "CString::CString(LPCWSTR lpch, int nLength)");
AllocBuffer(nLength);
memcpy(m_pchData, lpch, nLength*sizeof(WCHAR));
}
}
/////////////////////////////////////////////////////////////////////////////
// Special conversion constructors
//////////////////////////////////////////////////////////////////////////////
// Assignment operators
const CString& CString::operator=(WCHAR ch)
{
AssignCopy(1, &ch);
return *this;
}
//////////////////////////////////////////////////////////////////////////////
// less common string expressions
CString AFXAPI operator+(const CString& string1, WCHAR ch)
{
CString s;
s.ConcatCopy(string1.GetData()->nDataLength, string1.m_pchData, 1, &ch);
return s;
}
CString AFXAPI operator+(WCHAR 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(WCHAR));
GetData()->nDataLength = nNewLength - nCount;
}
return nNewLength;
}
int CString::Insert(int nIndex, WCHAR ch)
{
CopyBeforeWrite();
if (nIndex < 0)
nIndex = 0;
int nNewLength = GetData()->nDataLength;
if (nIndex > nNewLength)
nIndex = nNewLength;
nNewLength++;
if (GetData()->nAllocLength < nNewLength)
{
CStringData<WCHAR>* pOldData = GetData();
LPWSTR pstr = m_pchData;
AllocBuffer(nNewLength);
memcpy(m_pchData, pstr, (pOldData->nDataLength+1)*sizeof(WCHAR));
CString::Release(pOldData);
}
// move existing bytes down
memcpy(m_pchData + nIndex + 1,
m_pchData + nIndex, (nNewLength-nIndex)*sizeof(WCHAR));
m_pchData[nIndex] = ch;
GetData()->nDataLength = nNewLength;
return nNewLength;
}
int CString::Insert(int nIndex, LPCWSTR 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<WCHAR>* pOldData = GetData();
LPWSTR lpwsz = m_pchData;
AllocBuffer(nNewLength);
memcpy(m_pchData, lpwsz, (pOldData->nDataLength+1)*sizeof(WCHAR));
CString::Release(pOldData);
}
// move existing bytes down
memcpy(m_pchData + nIndex + nInsertLength,
m_pchData + nIndex,
(nNewLength-nIndex-nInsertLength+1)*sizeof(WCHAR));
memcpy(m_pchData + nIndex,
pstr, nInsertLength*sizeof(WCHAR));
GetData()->nDataLength = nNewLength;
}
return nNewLength;
}
int CString::Replace(WCHAR chOld, WCHAR chNew)
{
int nCount = 0;
// short-circuit the nop case
if (chOld != chNew)
{
// otherwise modify each character that matches in the string
CopyBeforeWrite();
LPWSTR psz = m_pchData;
LPWSTR pszEnd = psz + GetData()->nDataLength;
while (psz < pszEnd)
{
// replace instances of the specified character only
if (*psz == chOld)
{
*psz = chNew;
nCount++;
}
psz = wcsinc(psz);
}
}
return nCount;
}
int CString::Replace(LPCWSTR lpszOld, LPCWSTR lpszNew)
{
return ReplaceRoutine(lpszOld, lpszNew, wcsstr);
}
int CString::ReplaceI(LPCWSTR lpszOld, LPCWSTR lpszNew)
{
return ReplaceRoutine(lpszOld, lpszNew, wcsistr);
}
int CString::ReplaceRoutine(LPCWSTR lpszOld, LPCWSTR lpszNew, _pfn_wcsstr tcsstr)
{
// 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;
LPWSTR lpszStart = m_pchData;
LPWSTR lpszEnd = m_pchData + GetData()->nDataLength;
LPWSTR lpszTarget;
while (lpszStart < lpszEnd)
{
while ((lpszTarget = tcsstr(lpszStart, lpszOld)) != NULL)
{
nCount++;
lpszStart = lpszTarget + nSourceLen;
}
lpszStart += wcslen(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 + 1 || GetData()->nRefs > 1)
{
CStringData<WCHAR>* pOldData = GetData();
LPWSTR pstr = m_pchData;
AllocBuffer(nNewLength);
memcpy(m_pchData, pstr, pOldData->nDataLength*sizeof(WCHAR));
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 = wcsstr(lpszStart, lpszOld)) != NULL)
{
int nBalance = nOldLength - ((int)(lpszTarget - m_pchData) + nSourceLen);
memmove(lpszTarget + nReplacementLen, lpszTarget + nSourceLen,
nBalance * sizeof(WCHAR));
memcpy(lpszTarget, lpszNew, nReplacementLen*sizeof(WCHAR));
lpszStart = lpszTarget + nReplacementLen;
lpszStart[nBalance] = '\0';
nOldLength += (nReplacementLen - nSourceLen);
}
lpszStart += wcslen(lpszStart) + 1;
}
ASSERT(m_pchData[nNewLength] == '\0', "CString::ReplaceRoutine");
GetData()->nDataLength = nNewLength;
}
return nCount;
}
int CString::Remove(WCHAR chRemove)
{
CopyBeforeWrite();
LPWSTR pstrSource = m_pchData;
LPWSTR pstrDest = m_pchData;
LPWSTR pstrEnd = m_pchData + GetData()->nDataLength;
while (pstrSource < pstrEnd)
{
if (*pstrSource != chRemove)
{
*pstrDest = *pstrSource;
pstrDest = wcsinc(pstrDest);
}
pstrSource = wcsinc(pstrSource);
}
*pstrDest = '\0';
int nCount = (int)(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
{
CString dest;
Mid(nFirst, nCount, dest);
return dest;
}
CString CString::Right(int nCount) const
{
CString dest;
Right(nCount, dest);
return dest;
}
CString CString::Left(int nCount) const
{
CString dest;
Left(nCount, dest);
return dest;
}
// strspn equivalent
CString CString::SpanIncluding(LPCWSTR lpszCharSet) const
{
ASSERT(AfxIsValidString(lpszCharSet), "CString::SpanIncluding");
return Left(wcsspn(m_pchData, lpszCharSet));
}
// strcspn equivalent
CString CString::SpanExcluding(LPCWSTR lpszCharSet) const
{
ASSERT(AfxIsValidString(lpszCharSet), "CString::SpanIncluding");
return Left(wcscspn(m_pchData, lpszCharSet));
}
void CString::Mid(int nFirst, CString & csMid) const
{
Mid(nFirst, GetData()->nDataLength - nFirst, csMid);
}
void CString::Mid(int nFirst, int nCount, CString & csMid) 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, "CString::Mid(int nFirst, int nCount)");
ASSERT(nFirst + nCount <= GetData()->nDataLength, "CString::Mid(int nFirst, int nCount)");
// optimize case of returning entire string
if (nFirst == 0 && nFirst + nCount == GetData()->nDataLength)
{
csMid = *this;
return;
}
AllocCopy(csMid, nCount, nFirst, 0);
}
void CString::Right(int nCount, CString & csRight) const
{
if (nCount < 0)
nCount = 0;
if (nCount >= GetData()->nDataLength)
return;
AllocCopy(csRight, nCount, GetData()->nDataLength-nCount, 0);
}
void CString::Left(int nCount, CString & csLeft) const
{
if (nCount < 0)
nCount = 0;
if (nCount >= GetData()->nDataLength)
return;
AllocCopy(csLeft, nCount, 0, 0);
}
void CString::SpanIncluding(const WCHAR * lpszCharSet, CString & csSpanInc) const
{
ASSERT(AfxIsValidString(lpszCharSet), "CString::SpanIncluding");
return Left(wcsspn(m_pchData, lpszCharSet), csSpanInc);
}
void CString::SpanExcluding(const WCHAR * lpszCharSet, CString & csSpanExc) const
{
ASSERT(AfxIsValidString(lpszCharSet), "CString::SpanIncluding");
return Left(wcscspn(m_pchData, lpszCharSet), csSpanExc);
}
//////////////////////////////////////////////////////////////////////////////
// Finding
int CString::ReverseFind(WCHAR ch) const
{
// find last single character
LPCWSTR lpsz = wcsrchr(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(LPCWSTR lpszSub) const
{
return Find(lpszSub, 0);
}
int CString::Find(LPCWSTR lpszSub, int nStart) const
{
ASSERT(AfxIsValidString(lpszSub), "CString::Find");
int nLength = GetData()->nDataLength;
if (nStart > nLength)
return -1;
// find first matching substring
LPWSTR lpsz = wcsstr(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 WCHAR
#define WCHAR_ARG WCHAR
#define CHAR_ARG WCHAR
#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(LPCWSTR lpszFormat, va_list argList)
{
ASSERT(AfxIsValidString(lpszFormat), "CString::FormatV");
va_list argListSave = argList;
// make a guess at the maximum length of the resulting string
int nMaxLen = 0;
for (LPCWSTR lpsz = lpszFormat; *lpsz != '\0'; lpsz = wcsinc(lpsz))
{
// handle '%' character, but watch out for '%%'
if (*lpsz != '%' || *(lpsz = wcsinc(lpsz)) == '%')
{
nMaxLen += 1;
continue;
}
int nItemLen = 0;
// handle '%' character with format
int nWidth = 0;
for (; *lpsz != '\0'; lpsz = wcsinc(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 = _wtoi(lpsz);
for (; *lpsz != '\0' && iswdigit(*lpsz); lpsz = wcsinc(lpsz))
;
}
ASSERT(nWidth >= 0, "CString::FormatV");
int nPrecision = 0;
if (*lpsz == '.')
{
// skip past '.' separator (width.precision)
lpsz = wcsinc(lpsz);
// get precision and skip it
if (*lpsz == '*')
{
nPrecision = va_arg(argList, int);
lpsz = wcsinc(lpsz);
}
else
{
nPrecision = _wtoi(lpsz);
for (; *lpsz != '\0' && iswdigit(*lpsz); lpsz = wcsinc(lpsz))
;
}
ASSERT(nPrecision >= 0, "CString::FormatV");
}
// should be on type modifier or specifier
int nModifier = 0;
if (wcsncmp(lpsz, L"I64", 3) == 0)
{
lpsz += 3;
nModifier = FORCE_INT64;
#if !defined(_X86_) && !defined(_ALPHA_)
// __int64 is only available on X86 and ALPHA platforms
ASSERT(FALSE, "CString::FormatV");
#endif
}
else
{
switch (*lpsz)
{
// modifiers that affect size
case 'h':
nModifier = FORCE_ANSI;
lpsz = wcsinc(lpsz);
break;
case 'l':
nModifier = FORCE_UNICODE;
lpsz = wcsinc(lpsz);
break;
// modifiers that do not affect size
case 'F':
case 'N':
case 'L':
lpsz = wcsinc(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':
{
LPCWSTR pstrNextArg = va_arg(argList, LPCWSTR);
if (pstrNextArg == NULL)
nItemLen = 6; // "(null)"
else
{
nItemLen = wcslen(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
LPCWSTR pstrNextArg = va_arg(argList, LPCWSTR);
if (pstrNextArg == NULL)
nItemLen = 6; // "(null)"
else
{
nItemLen = wcslenChars(pstrNextArg);
nItemLen = max(1, nItemLen);
}
#endif
}
break;
case 's'|FORCE_ANSI:
case 'S'|FORCE_ANSI:
{
LPCWSTR pstrNextArg = va_arg(argList, LPCWSTR);
if (pstrNextArg == NULL)
nItemLen = 6; // "(null)"
else
{
nItemLen = wcslen(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':
va_arg(argList, DOUBLE_ARG);
nItemLen = 128; // width isn't truncated
// 312 == strlen("-1+(309 zeroes).")
// 309 zeroes == max precision of a double
nItemLen = max(nItemLen, 312+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, "CString::FormatV"); // unknown formatting option
}
}
// adjust nMaxLen for output nItemLen
nMaxLen += nItemLen;
}
GetBuffer(nMaxLen);
int nChars = _vsnwprintf(m_pchData, nMaxLen, lpszFormat, argListSave);
ASSERT(nChars <= GetAllocLength(), "CString::FormatV");
ReleaseBuffer();
va_end(argListSave);
}
// formatting (using wsprintf style formatting)
void AFX_CDECL CString::Format(LPCWSTR lpszFormat, ...)
{
ASSERT(AfxIsValidString(lpszFormat), "CString::Format");
va_list argList;
va_start(argList, lpszFormat);
FormatV(lpszFormat, argList);
va_end(argList);
}
// formatting (using FormatMessage style formatting)
void AFX_CDECL CString::FormatMessage(LPCWSTR lpszFormat, ...)
{
// format message into temporary buffer lpszTemp
va_list argList;
va_start(argList, lpszFormat);
LPWSTR lpszTemp;
if (::FormatMessageW(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ALLOCATE_BUFFER,
lpszFormat, 0, 0, (LPWSTR)&lpszTemp, 0, &argList) == 0 ||
lpszTemp == NULL)
{
CSTRING_THROW_EXCEPTION
}
else
{
// assign lpszTemp into the resulting string and free the temporary
*this = lpszTemp;
LocalFree(lpszTemp);
va_end(argList);
}
}
void CString::TrimRight(LPCWSTR lpszTargetList)
{
// find beginning of trailing matches
// by starting at beginning (DBCS aware)
CopyBeforeWrite();
LPWSTR lpsz = m_pchData;
LPWSTR lpszLast = NULL;
while (*lpsz != '\0')
{
if (wcschr(lpszTargetList, *lpsz) != NULL)
{
if (lpszLast == NULL)
lpszLast = lpsz;
}
else
lpszLast = NULL;
lpsz = wcsinc(lpsz);
}
if (lpszLast != NULL)
{
// truncate at left-most matching character
*lpszLast = '\0';
GetData()->nDataLength = (int)(lpszLast - m_pchData);
}
}
void CString::TrimRight(WCHAR chTarget)
{
// find beginning of trailing matches
// by starting at beginning (DBCS aware)
CopyBeforeWrite();
LPWSTR lpsz = m_pchData;
LPWSTR lpszLast = NULL;
while (*lpsz != '\0')
{
if (*lpsz == chTarget)
{
if (lpszLast == NULL)
lpszLast = lpsz;
}
else
lpszLast = NULL;
lpsz = wcsinc(lpsz);
}
if (lpszLast != NULL)
{
// truncate at left-most matching character
*lpszLast = '\0';
GetData()->nDataLength = (int)(lpszLast - m_pchData);
}
}
void CString::TrimRight()
{
// find beginning of trailing spaces by starting at beginning (DBCS aware)
CopyBeforeWrite();
LPWSTR lpsz = m_pchData;
LPWSTR lpszLast = NULL;
while (*lpsz != '\0')
{
if (iswspace(*lpsz))
{
if (lpszLast == NULL)
lpszLast = lpsz;
}
else
lpszLast = NULL;
lpsz = wcsinc(lpsz);
}
if (lpszLast != NULL)
{
// truncate at trailing space start
*lpszLast = '\0';
GetData()->nDataLength = (int)(lpszLast - m_pchData);
}
}
void CString::TrimLeft(LPCWSTR lpszTargets)
{
// if we're not trimming anything, we're not doing any work
if (SafeStrlen(lpszTargets) == 0)
return;
CopyBeforeWrite();
LPCWSTR lpsz = m_pchData;
while (*lpsz != '\0')
{
if (wcschr(lpszTargets, *lpsz) == NULL)
break;
lpsz = wcsinc(lpsz);
}
if (lpsz != m_pchData)
{
// fix up data and length
int nDataLength = GetData()->nDataLength - (int)(lpsz - m_pchData);
memmove(m_pchData, lpsz, (nDataLength+1)*sizeof(WCHAR));
GetData()->nDataLength = nDataLength;
}
}
void CString::TrimLeft(WCHAR chTarget)
{
// find first non-matching character
CopyBeforeWrite();
LPCWSTR lpsz = m_pchData;
while (chTarget == *lpsz)
lpsz = wcsinc(lpsz);
if (lpsz != m_pchData)
{
// fix up data and length
int nDataLength = GetData()->nDataLength - (int)(lpsz - m_pchData);
memmove(m_pchData, lpsz, (nDataLength+1)*sizeof(WCHAR));
GetData()->nDataLength = nDataLength;
}
}
void CString::TrimLeft()
{
// find first non-space character
CopyBeforeWrite();
LPCWSTR lpsz = m_pchData;
while (iswspace(*lpsz))
lpsz = wcsinc(lpsz);
if (lpsz != m_pchData)
{
// fix up data and length
int nDataLength = GetData()->nDataLength - (int)(lpsz - m_pchData);
memmove(m_pchData, lpsz, (nDataLength+1)*sizeof(WCHAR));
GetData()->nDataLength = nDataLength;
}
}
void CString::SplitPath(
CString * csDrive,
CString * csDir,
CString * csName,
CString * csExt) const
{
WCHAR * drive = NULL;
WCHAR * dir = NULL;
WCHAR * name = NULL;
WCHAR * ext = NULL;
if (csDrive)
{
drive = csDrive->GetBuffer(_MAX_DRIVE);
}
if (csDir)
{
dir = csDir->GetBuffer(_MAX_DIR);
}
if (csName)
{
name = csName->GetBuffer(_MAX_FNAME);
}
if (csExt)
{
ext = csExt->GetBuffer(_MAX_EXT);
}
_wsplitpath(Get(), drive, dir, name, ext);
if (csDrive)
{
csDrive->ReleaseBuffer(-1);
}
if (csDir)
{
csDir->ReleaseBuffer(-1);
}
if (csName)
{
csName->ReleaseBuffer(-1);
}
if (csExt)
{
csExt->ReleaseBuffer(-1);
}
}
void CString::MakePath(
const CString * csDrive,
const CString * csDir,
const CString * csName,
const CString * csExt)
{
Truncate(0);
if (csDrive && !csDrive->IsEmpty())
{
ConcatInPlace(SafeStrlen(csDrive->Get()), csDrive->Get());
}
if (csDir && !csDir->IsEmpty())
{
ConcatInPlace(SafeStrlen(csDir->Get()), csDir->Get());
}
if (csName && !csName->IsEmpty())
{
// Make sure there is a \ between the two
if (!IsEmpty() && !IsPathSep(GetLength()) && !csName->IsPathSep(0) )
{
ConcatInPlace(1, L"\\");
}
ConcatInPlace(SafeStrlen(csName->Get()), csName->Get());
}
if (csExt && !csExt->IsEmpty())
{
// Make sure the extension has a dot
if (csExt->GetAt(0) != L'.')
{
ConcatInPlace(1, L".");
}
ConcatInPlace(SafeStrlen(csExt->Get()), csExt->Get());
}
}
void CString::AppendPath(const WCHAR * lpszPath)
{
int nLen = GetLength();
BOOL bThisHasSep = (nLen > 0) ? IsPathSep(nLen - 1) : FALSE;
BOOL bThatHasSep = ShimLib::IsPathSep(*lpszPath);
if (lpszPath == NULL || *lpszPath == 0)
{
return;
}
else if (nLen == 0)
{
// No path seperator is necessary
}
else if ((nLen == 2) && (GetAt(1) == L':') && !bThatHasSep )
{
// We must place a path seperator between the two
ConcatInPlace(1, L"\\");
}
else if (!bThisHasSep && !bThatHasSep )
{
// We must place a path seperator between the two
ConcatInPlace(1, L"\\");
}
else if (bThisHasSep && bThatHasSep )
{
// Both have seperators, remove one
do
{
lpszPath += 1;
}
while (ShimLib::IsPathSep(*lpszPath));
}
ConcatInPlace(SafeStrlen(lpszPath), lpszPath);
}
// Find the trailing path component
// Return index of the last path seperator or -1 if none found
int CString::FindLastPathComponent() const
{
for (int nLen = GetLength() - 1; nLen >= 0; --nLen)
{
if (IsPathSep(nLen))
{
return nLen;
}
}
return -1;
}
// Remove the trailing path component from the string
void CString::StripPath()
{
int nLastPathComponent = FindLastPathComponent();
if (nLastPathComponent != -1)
{
Truncate(nLastPathComponent);
}
else
{
Truncate(0);
}
}
char * CString::GetAnsi() const
{
// Since we don't know if the original (WCHAR) data has changed
// we need to update the ansi string each time.
if (m_pchDataAnsi)
{
free(m_pchDataAnsi);
m_pchDataAnsi = NULL;
}
// Get the number of bytes necessary for the WCHAR string
int nBytes = WideCharToMultiByte(CP_ACP, 0, m_pchData, -1, NULL, 0, NULL, NULL);
m_pchDataAnsi = (char *) malloc(nBytes);
if (m_pchDataAnsi)
{
WideCharToMultiByte(CP_ACP, 0, m_pchData, -1, m_pchDataAnsi, nBytes, NULL, NULL);
}
else
{
CSTRING_THROW_EXCEPTION
}
return m_pchDataAnsi;
}
void CString::GetLastPathComponent(CString & pathComponent) const
{
int nPath = FindLastPathComponent();
if (nPath < 0)
{
pathComponent = *this;
}
else
{
Mid(nPath+1, pathComponent);
}
}
// Get what's not the "file" portion of this path
void CString::GetNotLastPathComponent(CString & pathComponent) const
{
int nPath = FindLastPathComponent();
if (nPath < 1)
{
pathComponent.Truncate(0);
}
else
{
Left(nPath, pathComponent);
}
}
// Get the Drive portion of this path,
// Either C: or \\server\disk format.
void CString::GetDrivePortion(CString & csDrivePortion) const
{
const WCHAR * lpwszPath = Get();
const WCHAR * lpwszNonDrivePortion = ShimLib::GetDrivePortion(lpwszPath);
if (lpwszPath == lpwszNonDrivePortion)
{
csDrivePortion.Truncate(0);
}
else
{
Left((int)(lpwszNonDrivePortion - lpwszPath), csDrivePortion);
}
}
DWORD CString::GetModuleFileNameW(
HMODULE hModule // handle to module
)
{
// Force the size to MAX_PATH because there is no way to determine necessary buffer size.
WCHAR * lpsz = GetBuffer(MAX_PATH);
DWORD dwChars = ::GetModuleFileNameW(hModule, lpsz, MAX_PATH);
ReleaseBuffer(dwChars);
return dwChars;
}
DWORD CString::GetSystemDirectoryW(void)
{
UINT dwChars = ::GetSystemDirectoryW(NULL, 0);
if (dwChars)
{
dwChars += 1; // One for the NULL
// Get a pointer to the actual lpsz data
WCHAR * lpszPath = GetBuffer(dwChars);
dwChars = ::GetSystemDirectoryW(lpszPath, dwChars);
ReleaseBuffer(dwChars);
}
else
{
Truncate(0);
}
return dwChars;
}
UINT CString::GetSystemWindowsDirectoryW(void)
{
UINT dwChars = ::GetSystemWindowsDirectoryW(NULL, 0);
if (dwChars)
{
dwChars += 1; // One for the NULL
// Get a pointer to the actual lpsz data
WCHAR * lpszPath = GetBuffer(dwChars);
dwChars = ::GetSystemWindowsDirectoryW(lpszPath, dwChars);
ReleaseBuffer(dwChars);
}
else
{
Truncate(0);
}
return dwChars;
}
DWORD CString::GetWindowsDirectoryW(void)
{
UINT dwChars = ::GetWindowsDirectoryW(NULL, 0);
if (dwChars)
{
dwChars += 1; // One for the NULL
// Get a pointer to the actual lpsz data
WCHAR * lpszPath = GetBuffer(dwChars);
dwChars = ::GetWindowsDirectoryW(lpszPath, dwChars);
ReleaseBuffer(dwChars);
}
else
{
Truncate(0);
}
return dwChars;
}
DWORD CString::GetShortPathNameW(void)
{
DWORD dwChars = ::GetShortPathNameW(Get(), NULL, 0);
if (dwChars)
{
dwChars += 1; // One for the NULL
CString csCopy(*this);
// Get a pointer to the actual lpsz data
WCHAR * lpszPath = GetBuffer(dwChars);
dwChars = ::GetShortPathNameW(csCopy, lpszPath, dwChars);
ReleaseBuffer(dwChars);
}
return dwChars;
}
DWORD CString::GetLongPathNameW(void)
{
DWORD dwChars = ::GetLongPathNameW(Get(), NULL, 0);
if (dwChars)
{
dwChars += 1; // One for the NULL
CString csCopy(*this);
// Get a pointer to the actual lpsz data
WCHAR * lpszPath = GetBuffer(dwChars);
dwChars = ::GetLongPathNameW(csCopy, lpszPath, dwChars);
ReleaseBuffer(dwChars);
}
return dwChars;
}
DWORD CString::GetFullPathNameW(void)
{
DWORD dwChars = ::GetFullPathNameW(Get(), 0, NULL, NULL);
if (dwChars)
{
dwChars += 1; // One for the NULL
CString csCopy(*this);
// Get a pointer to the actual lpsz data
WCHAR * lpszPath = GetBuffer(dwChars);
dwChars = ::GetFullPathNameW(csCopy, dwChars, lpszPath, NULL);
ReleaseBuffer(dwChars);
}
return dwChars;
}
DWORD CString::GetTempPathW(void)
{
DWORD dwChars = ::GetTempPathW(0, NULL);
if (dwChars)
{
dwChars += 1; // One for the NULL
// Get a pointer to the actual lpsz data
WCHAR * lpszPath = GetBuffer(dwChars);
dwChars = ::GetTempPathW(dwChars, lpszPath);
ReleaseBuffer(dwChars);
}
else
{
Truncate(0);
}
return dwChars;
}
UINT CString::GetTempFileNameW(
LPCWSTR lpPathName, // directory name
LPCWSTR lpPrefixString, // file name prefix
UINT uUnique // integer
)
{
WCHAR * lpsz = GetBuffer(MAX_PATH);
DWORD dwChars = ::GetTempFileNameW(lpPathName, lpPrefixString, uUnique, lpsz);
ReleaseBuffer(dwChars);
return dwChars;
}
DWORD CString::GetCurrentDirectoryW(void)
{
DWORD dwChars = ::GetCurrentDirectory(0, NULL);
if (dwChars)
{
dwChars += 1; // One for the NULL
// Get a pointer to the actual lpsz data
WCHAR * lpsz = GetBuffer(dwChars);
dwChars = ::GetCurrentDirectoryW(dwChars, lpsz);
ReleaseBuffer(dwChars);
}
else
{
Truncate(0);
}
return dwChars;
}
DWORD CString::ExpandEnvironmentStringsW( )
{
DWORD dwChars = ::ExpandEnvironmentStringsW(Get(), NULL, 0);
if (dwChars)
{
dwChars += 1; // One for the NULL
CString csCopy(*this);
// Get a pointer to the actual lpsz data
WCHAR * lpszPath = GetBuffer(dwChars);
dwChars = ::ExpandEnvironmentStringsW(csCopy, lpszPath, dwChars);
ReleaseBuffer(dwChars-1);
}
return dwChars;
}
// delete all characters to the right of nIndex
void CString::Truncate(int nIndex)
{
ASSERT(nIndex >= 0, "CString::Truncate");
if (nIndex < GetLength())
{
SetAt(nIndex, L'\0');
GetData()->nDataLength = nIndex;
}
}
// Copy the ansi version of this string into the specified buffer,
// If the buffer is not large enough, no data is copied.
//
// Returns the number of BYTES necessary for the buffer
//
DWORD CString::CopyAnsiBytes(char * lpszBuffer, DWORD nBytes)
{
const char * lpszAnsi = GetAnsi();
// Length of cstring, in bytes, not including terminator.
int nDataBytes = strlen(lpszAnsi);
// Length of buffer (no terminator)
int nBufBytes = nBytes - 1;
// Only copy the data if the buffer is large enough
if (nDataBytes <= nBufBytes)
{
// Copy the data into the buffer
strcpy(lpszBuffer, lpszAnsi);
}
// return the actual size of string
return nDataBytes;
}
// Copy the ansi version of this string into the specified buffer,
// If the buffer is not large enough, no data is copied.
//
// Returns the number of CHARS necessary for the buffer
//
DWORD CString::CopyAnsiChars(char * lpszBuffer, DWORD nChars)
{
const char * lpszAnsi = GetAnsi();
// Length of cstring, in bytes, not including terminator.
int nDataChars = GetLength();
// Length of buffer (no terminator)
int nBufChars = nChars - 1;
// Only copy the data if the buffer is large enough
if (nDataChars <= nBufChars)
{
// Copy the data into the buffer
strcpy(lpszBuffer, lpszAnsi);
}
// return the actual size of string
return nDataChars;
}
BOOL CString::PatternMatch(const WCHAR * lpszPattern) const
{
return PatternMatchW(lpszPattern, Get());
}
}; // end of namespace ShimLib