text: // This is a part of the Microsoft Foundation Classes C++ library.
  // Copyright (C) 1992-1998 Microsoft Corporation
  // All rights reserved.
  //
  // This source code is only intended as a supplement to the
  // Microsoft Foundation Classes Reference and related
  // electronic documentation provided with the library.
  // See these sources for detailed information regarding the
  // Microsoft Foundation Classes product.
  
  #include "stdafx.h"
  #include <afxtempl.h>
  
  #ifdef AFX_AUX_SEG
  #pragma code_seg(AFX_AUX_SEG)
  #endif
  
  #ifdef _DEBUG
  #undef THIS_FILE
  static char THIS_FILE[] = __FILE__;
  #endif
  
  #define new DEBUG_NEW
  
  //////////////////////////////////////////////////////////////////////////////
  // More sophisticated construction
  
  CString::CString(TCHAR ch, int nLength)
  {
  	Init();
  	if (nLength >= 1)
  	{
  		AllocBuffer(nLength);
  #ifdef _UNICODE
  		for (int i = 0; i < nLength; i++)
  			m_pchData[i] = ch;
  #else
  		memset(m_pchData, ch, nLength);
  #endif
  	}
  }
  
  CString::CString(LPCTSTR lpch, int nLength)
  {
  	Init();
  	if (nLength != 0)
  	{
  		ASSERT(AfxIsValidAddress(lpch, nLength, FALSE));
  		AllocBuffer(nLength);
  		memcpy(m_pchData, lpch, nLength*sizeof(TCHAR));
  	}
  }
  
  /////////////////////////////////////////////////////////////////////////////
  // Special conversion constructors
  
  #ifdef _UNICODE
  CString::CString(LPCSTR lpsz, int nLength)
  {
  	Init();
  	if (nLength != 0)
  	{
  		AllocBuffer(nLength);
  		int n = ::MultiByteToWideChar(CP_ACP, 0, lpsz, nLength, m_pchData, nLength+1);
  		ReleaseBuffer(n >= 0 ? n : -1);
  	}
  }
  #else //_UNICODE
  CString::CString(LPCWSTR lpsz, int nLength)
  {
  	Init();
  	if (nLength != 0)
  	{
  		AllocBuffer(nLength*2);
  		int n = ::WideCharToMultiByte(CP_ACP, 0, lpsz, nLength, m_pchData,
  			(nLength*2)+1, NULL, NULL);
  		ReleaseBuffer(n >= 0 ? n : -1);
  	}
  }
  #endif //!_UNICODE
  
  //////////////////////////////////////////////////////////////////////////////
  // Assignment operators
  
  const CString& CString::operator=(TCHAR ch)
  {
  	AssignCopy(1, &ch);
  	return *this;
  }
  
  //////////////////////////////////////////////////////////////////////////////
  // less common string expressions
  
  CString AFXAPI operator+(const CString& string1, TCHAR ch)
  {
  	CString s;
  	s.ConcatCopy(string1.GetData()->nDataLength, string1.m_pchData, 1, &ch);
  	return s;
  }
  
  CString AFXAPI operator+(TCHAR 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(TCHAR));
  		GetData()->nDataLength = nNewLength - nCount;
  	}
  
  	return nNewLength;
  }
  
  int CString::Insert(int nIndex, TCHAR ch)
  {
  	CopyBeforeWrite();
  
  	if (nIndex < 0)
  		nIndex = 0;
  
  	int nNewLength = GetData()->nDataLength;
  	if (nIndex > nNewLength)
  		nIndex = nNewLength;
  	nNewLength++;
  
  	if (GetData()->nAllocLength < nNewLength)
  	{
  		CStringData* pOldData = GetData();
  		LPTSTR pstr = m_pchData;
  		AllocBuffer(nNewLength);
  		memcpy(m_pchData, pstr, (pOldData->nDataLength+1)*sizeof(TCHAR));
  		CString::Release(pOldData);
  	}
  
  	// move existing bytes down
  	memcpy(m_pchData + nIndex + 1,
  		m_pchData + nIndex, (nNewLength-nIndex)*sizeof(TCHAR));
  	m_pchData[nIndex] = ch;
  	GetData()->nDataLength = nNewLength;
  
  	return nNewLength;
  }
  
  int CString::Insert(int nIndex, LPCTSTR pstr)
  {
  	if (nIndex < 0)
  		nIndex = 0;
  
  	int nInsertLength = SafeStrlen(pstr);
  	int nNewLength = GetData()->nDataLength;
  	if (nInsertLength > 0)
  	{
  		CopyBeforeWrite();
  		if (nIndex > nNewLength)
  			nIndex = nNewLength;
  		nNewLength += nInsertLength;
  
  		if (GetData()->nAllocLength < nNewLength)
  		{
  			CStringData* pOldData = GetData();
  			LPTSTR pstr = m_pchData;
  			AllocBuffer(nNewLength);
  			memcpy(m_pchData, pstr, (pOldData->nDataLength+1)*sizeof(TCHAR));
  			CString::Release(pOldData);
  		}
  
  		// move existing bytes down
  		memcpy(m_pchData + nIndex + nInsertLength,
  			m_pchData + nIndex,
  			(nNewLength-nIndex-nInsertLength+1)*sizeof(TCHAR));
  		memcpy(m_pchData + nIndex,
  			pstr, nInsertLength*sizeof(TCHAR));
  		GetData()->nDataLength = nNewLength;
  	}
  
  	return nNewLength;
  }
  
  int CString::Replace(TCHAR chOld, TCHAR chNew)
  {
  	int nCount = 0;
  
  	// short-circuit the nop case
  	if (chOld != chNew)
  	{
  		// otherwise modify each character that matches in the string
  		CopyBeforeWrite();
  		LPTSTR psz = m_pchData;
  		LPTSTR pszEnd = psz + GetData()->nDataLength;
  		while (psz < pszEnd)
  		{
  			// replace instances of the specified character only
  			if (*psz == chOld)
  			{
  				*psz = chNew;
  				nCount++;
  			}
  			psz = _tcsinc(psz);
  		}
  	}
  	return nCount;
  }
  
  int CString::Replace(LPCTSTR lpszOld, LPCTSTR lpszNew)
  {
  	// can't have empty or NULL lpszOld
  
  	int nSourceLen = SafeStrlen(lpszOld);
  	if (nSourceLen == 0)
  		return 0;
  	int nReplacementLen = SafeStrlen(lpszNew);
  
  	// loop once to figure out the size of the result string
  	int nCount = 0;
  	LPTSTR lpszStart = m_pchData;
  	LPTSTR lpszEnd = m_pchData + GetData()->nDataLength;
  	LPTSTR lpszTarget;
  	while (lpszStart < lpszEnd)
  	{
  		while ((lpszTarget = _tcsstr(lpszStart, lpszOld)) != NULL)
  		{
  			nCount++;
  			lpszStart = lpszTarget + nSourceLen;
  		}
  		lpszStart += lstrlen(lpszStart) + 1;
  	}
  
  	// if any changes were made, make them
  	if (nCount > 0)
  	{
  		CopyBeforeWrite();
  
  		// if the buffer is too small, just
  		//   allocate a new buffer (slow but sure)
  		int nOldLength = GetData()->nDataLength;
  		int nNewLength =  nOldLength + (nReplacementLen-nSourceLen)*nCount;
  		if (GetData()->nAllocLength < nNewLength || GetData()->nRefs > 1)
  		{
  			CStringData* pOldData = GetData();
  			LPTSTR pstr = m_pchData;
  			AllocBuffer(nNewLength);
  			memcpy(m_pchData, pstr, pOldData->nDataLength*sizeof(TCHAR));
  			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 = _tcsstr(lpszStart, lpszOld)) != NULL)
  			{
  				int nBalance = nOldLength - (lpszTarget - m_pchData + nSourceLen);
  				memmove(lpszTarget + nReplacementLen, lpszTarget + nSourceLen,
  					nBalance * sizeof(TCHAR));
  				memcpy(lpszTarget, lpszNew, nReplacementLen*sizeof(TCHAR));
  				lpszStart = lpszTarget + nReplacementLen;
  				lpszStart[nBalance] = '\0';
  				nOldLength += (nReplacementLen - nSourceLen);
  			}
  			lpszStart += lstrlen(lpszStart) + 1;
  		}
  		ASSERT(m_pchData[nNewLength] == '\0');
  		GetData()->nDataLength = nNewLength;
  	}
  
  	return nCount;
  }
  
  int CString::Remove(TCHAR chRemove)
  {
  	CopyBeforeWrite();
  
  	LPTSTR pstrSource = m_pchData;
  	LPTSTR pstrDest = m_pchData;
  	LPTSTR pstrEnd = m_pchData + GetData()->nDataLength;
  
  	while (pstrSource < pstrEnd)
  	{
  		if (*pstrSource != chRemove)
  		{
  			*pstrDest = *pstrSource;
  			pstrDest = _tcsinc(pstrDest);
  		}
  		pstrSource = _tcsinc(pstrSource);
  	}
  	*pstrDest = '\0';
  	int nCount = pstrSource - pstrDest;
  	GetData()->nDataLength -= nCount;
  
  	return nCount;
  }
  
  //////////////////////////////////////////////////////////////////////////////
  // 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
  {
  	// 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);
  	ASSERT(nFirst + nCount <= GetData()->nDataLength);
  
  	// optimize case of returning entire string
  	if (nFirst == 0 && nFirst + nCount == GetData()->nDataLength)
  		return *this;
  
  	CString dest;
  	AllocCopy(dest, nCount, nFirst, 0);
  	return dest;
  }
  
  CString CString::Right(int nCount) const
  {
  	if (nCount < 0)
  		nCount = 0;
  	if (nCount >= GetData()->nDataLength)
  		return *this;
  
  	CString dest;
  	AllocCopy(dest, nCount, GetData()->nDataLength-nCount, 0);
  	return dest;
  }
  
  CString CString::Left(int nCount) const
  {
  	if (nCount < 0)
  		nCount = 0;
  	if (nCount >= GetData()->nDataLength)
  		return *this;
  
  	CString dest;
  	AllocCopy(dest, nCount, 0, 0);
  	return dest;
  }
  
  // strspn equivalent
  CString CString::SpanIncluding(LPCTSTR lpszCharSet) const
  {
  	ASSERT(AfxIsValidString(lpszCharSet));
  	return Left(_tcsspn(m_pchData, lpszCharSet));
  }
  
  // strcspn equivalent
  CString CString::SpanExcluding(LPCTSTR lpszCharSet) const
  {
  	ASSERT(AfxIsValidString(lpszCharSet));
  	return Left(_tcscspn(m_pchData, lpszCharSet));
  }
  
  //////////////////////////////////////////////////////////////////////////////
  // Finding
  
  int CString::ReverseFind(TCHAR ch) const
  {
  	// find last single character
  	LPTSTR lpsz = _tcsrchr(m_pchData, (_TUCHAR) ch);
  
  	// return -1 if not found, distance from beginning otherwise
  	return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData);
  }
  
  // find a sub-string (like strstr)
  int CString::Find(LPCTSTR lpszSub) const
  {
  	return Find(lpszSub, 0);
  }
  
  int CString::Find(LPCTSTR lpszSub, int nStart) const
  {
  	ASSERT(AfxIsValidString(lpszSub));
  
  	int nLength = GetData()->nDataLength;
  	if (nStart > nLength)
  		return -1;
  
  	// find first matching substring
  	LPTSTR lpsz = _tcsstr(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   TCHAR
  #define WCHAR_ARG   WCHAR
  #define CHAR_ARG    char
  
  #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(LPCTSTR lpszFormat, va_list argList)
  {
  	ASSERT(AfxIsValidString(lpszFormat));
  
  	va_list argListSave = argList;
  
  	// make a guess at the maximum length of the resulting string
  	int nMaxLen = 0;
  	for (LPCTSTR lpsz = lpszFormat; *lpsz != '\0'; lpsz = _tcsinc(lpsz))
  	{
  		// handle '%' character, but watch out for '%%'
  		if (*lpsz != '%' || *(lpsz = _tcsinc(lpsz)) == '%')
  		{
  			nMaxLen += _tclen(lpsz);
  			continue;
  		}
  
  		int nItemLen = 0;
  
  		// handle '%' character with format
  		int nWidth = 0;
  		for (; *lpsz != '\0'; lpsz = _tcsinc(lpsz))
  		{
  			// check for valid flags
  			if (*lpsz == '#')
  				nMaxLen += 2;   // for '0x'
  			else if (*lpsz == '*')
  				nWidth = va_arg(argList, int);
  			else if (*lpsz == '-' || *lpsz == '+' || *lpsz == '0' ||
  				*lpsz == ' ')
  				;
  			else // hit non-flag character
  				break;
  		}
  		// get width and skip it
  		if (nWidth == 0)
  		{
  			// width indicated by
  			nWidth = _ttoi(lpsz);
  			for (; *lpsz != '\0' && _istdigit(*lpsz); lpsz = _tcsinc(lpsz))
  				;
  		}
  		ASSERT(nWidth >= 0);
  
  		int nPrecision = 0;
  		if (*lpsz == '.')
  		{
  			// skip past '.' separator (width.precision)
  			lpsz = _tcsinc(lpsz);
  
  			// get precision and skip it
  			if (*lpsz == '*')
  			{
  				nPrecision = va_arg(argList, int);
  				lpsz = _tcsinc(lpsz);
  			}
  			else
  			{
  				nPrecision = _ttoi(lpsz);
  				for (; *lpsz != '\0' && _istdigit(*lpsz); lpsz = _tcsinc(lpsz))
  					;
  			}
  			ASSERT(nPrecision >= 0);
  		}
  
  		// should be on type modifier or specifier
  		int nModifier = 0;
  		if (_tcsncmp(lpsz, _T("I64"), 3) == 0)
  		{
  			lpsz += 3;
  			nModifier = FORCE_INT64;
  #if !defined(_X86_) && !defined(_ALPHA_)
  			// __int64 is only available on X86 and ALPHA platforms
  			ASSERT(FALSE);
  #endif
  		}
  		else
  		{
  			switch (*lpsz)
  			{
  			// modifiers that affect size
  			case 'h':
  				nModifier = FORCE_ANSI;
  				lpsz = _tcsinc(lpsz);
  				break;
  			case 'l':
  				nModifier = FORCE_UNICODE;
  				lpsz = _tcsinc(lpsz);
  				break;
  
  			// modifiers that do not affect size
  			case 'F':
  			case 'N':
  			case 'L':
  				lpsz = _tcsinc(lpsz);
  				break;
  			}
  		}
  
  		// now should be on specifier
  		switch (*lpsz | nModifier)
  		{
  		// single characters
  		case 'c':
  		case 'C':
  			nItemLen = 2;
  			va_arg(argList, TCHAR_ARG);
  			break;
  		case 'c'|FORCE_ANSI:
  		case 'C'|FORCE_ANSI:
  			nItemLen = 2;
  			va_arg(argList, CHAR_ARG);
  			break;
  		case 'c'|FORCE_UNICODE:
  		case 'C'|FORCE_UNICODE:
  			nItemLen = 2;
  			va_arg(argList, WCHAR_ARG);
  			break;
  
  		// strings
  		case 's':
  			{
  				LPCTSTR pstrNextArg = va_arg(argList, LPCTSTR);
  				if (pstrNextArg == NULL)
  				   nItemLen = 6;  // "(null)"
  				else
  				{
  				   nItemLen = lstrlen(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
  				LPCSTR pstrNextArg = va_arg(argList, LPCSTR);
  				if (pstrNextArg == NULL)
  				   nItemLen = 6; // "(null)"
  				else
  				{
  				   nItemLen = lstrlenA(pstrNextArg);
  				   nItemLen = max(1, nItemLen);
  				}
  #endif
  			}
  			break;
  
  		case 's'|FORCE_ANSI:
  		case 'S'|FORCE_ANSI:
  			{
  				LPCSTR pstrNextArg = va_arg(argList, LPCSTR);
  				if (pstrNextArg == NULL)
  				   nItemLen = 6; // "(null)"
  				else
  				{
  				   nItemLen = lstrlenA(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);  // unknown formatting option
  			}
  		}
  
  		// adjust nMaxLen for output nItemLen
  		nMaxLen += nItemLen;
  	}
  
  	GetBuffer(nMaxLen);
  	VERIFY(_vstprintf(m_pchData, lpszFormat, argListSave) <= GetAllocLength());
  	ReleaseBuffer();
  
  	va_end(argListSave);
  }
  
  // formatting (using wsprintf style formatting)
  void AFX_CDECL CString::Format(LPCTSTR lpszFormat, ...)
  {
  	ASSERT(AfxIsValidString(lpszFormat));
  
  	va_list argList;
  	va_start(argList, lpszFormat);
  	FormatV(lpszFormat, argList);
  	va_end(argList);
  }
  
  void AFX_CDECL CString::Format(UINT nFormatID, ...)
  {
  	CString strFormat;
  	VERIFY(strFormat.LoadString(nFormatID) != 0);
  
  	va_list argList;
  	va_start(argList, nFormatID);
  	FormatV(strFormat, argList);
  	va_end(argList);
  }
  
  // formatting (using FormatMessage style formatting)
  void AFX_CDECL CString::FormatMessage(LPCTSTR lpszFormat, ...)
  {
  	// format message into temporary buffer lpszTemp
  	va_list argList;
  	va_start(argList, lpszFormat);
  	LPTSTR lpszTemp;
  
  	if (::FormatMessage(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ALLOCATE_BUFFER,
  		lpszFormat, 0, 0, (LPTSTR)&lpszTemp, 0, &argList) == 0 ||
  		lpszTemp == NULL)
  	{
  		AfxThrowMemoryException();
  	}
  
  	// assign lpszTemp into the resulting string and free the temporary
  	*this = lpszTemp;
  	LocalFree(lpszTemp);
  	va_end(argList);
  }
  
  void AFX_CDECL CString::FormatMessage(UINT nFormatID, ...)
  {
  	// get format string from string table
  	CString strFormat;
  	VERIFY(strFormat.LoadString(nFormatID) != 0);
  
  	// format message into temporary buffer lpszTemp
  	va_list argList;
  	va_start(argList, nFormatID);
  	LPTSTR lpszTemp;
  	if (::FormatMessage(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ALLOCATE_BUFFER,
  		strFormat, 0, 0, (LPTSTR)&lpszTemp, 0, &argList) == 0 ||
  		lpszTemp == NULL)
  	{
  		AfxThrowMemoryException();
  	}
  
  	// assign lpszTemp into the resulting string and free lpszTemp
  	*this = lpszTemp;
  	LocalFree(lpszTemp);
  	va_end(argList);
  }
  
  void CString::TrimRight(LPCTSTR lpszTargetList)
  {
  	// find beginning of trailing matches
  	// by starting at beginning (DBCS aware)
  
  	CopyBeforeWrite();
  	LPTSTR lpsz = m_pchData;
  	LPTSTR lpszLast = NULL;
  
  	while (*lpsz != '\0')
  	{
  		if (_tcschr(lpszTargetList, *lpsz) != NULL)
  		{
  			if (lpszLast == NULL)
  				lpszLast = lpsz;
  		}
  		else
  			lpszLast = NULL;
  		lpsz = _tcsinc(lpsz);
  	}
  
  	if (lpszLast != NULL)
  	{
  		// truncate at left-most matching character
  		*lpszLast = '\0';
  		GetData()->nDataLength = lpszLast - m_pchData;
  	}
  }
  
  void CString::TrimRight(TCHAR chTarget)
  {
  	// find beginning of trailing matches
  	// by starting at beginning (DBCS aware)
  
  	CopyBeforeWrite();
  	LPTSTR lpsz = m_pchData;
  	LPTSTR lpszLast = NULL;
  
  	while (*lpsz != '\0')
  	{
  		if (*lpsz == chTarget)
  		{
  			if (lpszLast == NULL)
  				lpszLast = lpsz;
  		}
  		else
  			lpszLast = NULL;
  		lpsz = _tcsinc(lpsz);
  	}
  
  	if (lpszLast != NULL)
  	{
  		// truncate at left-most matching character
  		*lpszLast = '\0';
  		GetData()->nDataLength = lpszLast - m_pchData;
  	}
  }
  
  void CString::TrimRight()
  {
  	// find beginning of trailing spaces by starting at beginning (DBCS aware)
  
  	CopyBeforeWrite();
  	LPTSTR lpsz = m_pchData;
  	LPTSTR lpszLast = NULL;
  
  	while (*lpsz != '\0')
  	{
  		if (_istspace(*lpsz))
  		{
  			if (lpszLast == NULL)
  				lpszLast = lpsz;
  		}
  		else
  			lpszLast = NULL;
  		lpsz = _tcsinc(lpsz);
  	}
  
  	if (lpszLast != NULL)
  	{
  		// truncate at trailing space start
  		*lpszLast = '\0';
  		GetData()->nDataLength = lpszLast - m_pchData;
  	}
  }
  
  void CString::TrimLeft(LPCTSTR lpszTargets)
  {
  	// if we're not trimming anything, we're not doing any work
  	if (SafeStrlen(lpszTargets) == 0)
  		return;
  
  	CopyBeforeWrite();
  	LPCTSTR lpsz = m_pchData;
  
  	while (*lpsz != '\0')
  	{
  		if (_tcschr(lpszTargets, *lpsz) == NULL)
  			break;
  		lpsz = _tcsinc(lpsz);
  	}
  
  	if (lpsz != m_pchData)
  	{
  		// fix up data and length
  		int nDataLength = GetData()->nDataLength - (lpsz - m_pchData);
  		memmove(m_pchData, lpsz, (nDataLength+1)*sizeof(TCHAR));
  		GetData()->nDataLength = nDataLength;
  	}
  }
  
  void CString::TrimLeft(TCHAR chTarget)
  {
  	// find first non-matching character
  
  	CopyBeforeWrite();
  	LPCTSTR lpsz = m_pchData;
  
  	while (chTarget == *lpsz)
  		lpsz = _tcsinc(lpsz);
  
  	if (lpsz != m_pchData)
  	{
  		// fix up data and length
  		int nDataLength = GetData()->nDataLength - (lpsz - m_pchData);
  		memmove(m_pchData, lpsz, (nDataLength+1)*sizeof(TCHAR));
  		GetData()->nDataLength = nDataLength;
  	}
  }
  
  void CString::TrimLeft()
  {
  	// find first non-space character
  
  	CopyBeforeWrite();
  	LPCTSTR lpsz = m_pchData;
  
  	while (_istspace(*lpsz))
  		lpsz = _tcsinc(lpsz);
  
  	if (lpsz != m_pchData)
  	{
  		// fix up data and length
  		int nDataLength = GetData()->nDataLength - (lpsz - m_pchData);
  		memmove(m_pchData, lpsz, (nDataLength+1)*sizeof(TCHAR));
  		GetData()->nDataLength = nDataLength;
  	}
  }
  
  ///////////////////////////////////////////////////////////////////////////////
  // CString support for template collections
  
  #if _MSC_VER >= 1100
  template<> void AFXAPI ConstructElements<CString> (CString* pElements, int nCount)
  #else
  void AFXAPI ConstructElements(CString* pElements, int nCount)
  #endif
  {
  	ASSERT(nCount == 0 ||
  		AfxIsValidAddress(pElements, nCount * sizeof(CString)));
  
  	for (; nCount--; ++pElements)
  		memcpy(pElements, &afxEmptyString, sizeof(*pElements));
  }
  
  #if _MSC_VER >= 1100
  template<> void AFXAPI DestructElements<CString> (CString* pElements, int nCount)
  #else
  void AFXAPI DestructElements(CString* pElements, int nCount)
  #endif
  {
  	ASSERT(nCount == 0 ||
  		AfxIsValidAddress(pElements, nCount * sizeof(CString)));
  
  	for (; nCount--; ++pElements)
  		pElements->~CString();
  }
  
  #if _MSC_VER >= 1100
  template<> void AFXAPI CopyElements<CString> (CString* pDest, const CString* pSrc, int nCount)
  #else
  void AFXAPI CopyElements(CString* pDest, const CString* pSrc, int nCount)
  #endif
  {
  	ASSERT(nCount == 0 ||
  		AfxIsValidAddress(pDest, nCount * sizeof(CString)));
  	ASSERT(nCount == 0 ||
  		AfxIsValidAddress(pSrc, nCount * sizeof(CString)));
  
  	for (; nCount--; ++pDest, ++pSrc)
  		*pDest = *pSrc;
  }
  
  #ifndef OLE2ANSI
  #if _MSC_VER >= 1100
  template<> UINT AFXAPI HashKey<LPCWSTR> (LPCWSTR key)
  #else
  UINT AFXAPI HashKey(LPCWSTR key)
  #endif
  {
  	UINT nHash = 0;
  	while (*key)
  		nHash = (nHash<<5) + nHash + *key++;
  	return nHash;
  }
  #endif
  
  #if _MSC_VER >= 1100
  template<> UINT AFXAPI HashKey<LPCSTR> (LPCSTR key)
  #else
  UINT AFXAPI HashKey(LPCSTR key)
  #endif
  {
  	UINT nHash = 0;
  	while (*key)
  		nHash = (nHash<<5) + nHash + *key++;
  	return nHash;
  }
  
  ///////////////////////////////////////////////////////////////////////////////
  