mirror of https://github.com/tongzx/nt5src
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
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
|