home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / vc98 / mfc / src / strex.cpp < prev    next >
C/C++ Source or Header  |  1998-06-16  |  22KB  |  973 lines

  1. // This is a part of the Microsoft Foundation Classes C++ library.
  2. // Copyright (C) 1992-1998 Microsoft Corporation
  3. // All rights reserved.
  4. //
  5. // This source code is only intended as a supplement to the
  6. // Microsoft Foundation Classes Reference and related
  7. // electronic documentation provided with the library.
  8. // See these sources for detailed information regarding the
  9. // Microsoft Foundation Classes product.
  10.  
  11. #include "stdafx.h"
  12. #include <afxtempl.h>
  13.  
  14. #ifdef AFX_AUX_SEG
  15. #pragma code_seg(AFX_AUX_SEG)
  16. #endif
  17.  
  18. #ifdef _DEBUG
  19. #undef THIS_FILE
  20. static char THIS_FILE[] = __FILE__;
  21. #endif
  22.  
  23. #define new DEBUG_NEW
  24.  
  25. //////////////////////////////////////////////////////////////////////////////
  26. // More sophisticated construction
  27.  
  28. CString::CString(TCHAR ch, int nLength)
  29. {
  30.     Init();
  31.     if (nLength >= 1)
  32.     {
  33.         AllocBuffer(nLength);
  34. #ifdef _UNICODE
  35.         for (int i = 0; i < nLength; i++)
  36.             m_pchData[i] = ch;
  37. #else
  38.         memset(m_pchData, ch, nLength);
  39. #endif
  40.     }
  41. }
  42.  
  43. CString::CString(LPCTSTR lpch, int nLength)
  44. {
  45.     Init();
  46.     if (nLength != 0)
  47.     {
  48.         ASSERT(AfxIsValidAddress(lpch, nLength, FALSE));
  49.         AllocBuffer(nLength);
  50.         memcpy(m_pchData, lpch, nLength*sizeof(TCHAR));
  51.     }
  52. }
  53.  
  54. /////////////////////////////////////////////////////////////////////////////
  55. // Special conversion constructors
  56.  
  57. #ifdef _UNICODE
  58. CString::CString(LPCSTR lpsz, int nLength)
  59. {
  60.     Init();
  61.     if (nLength != 0)
  62.     {
  63.         AllocBuffer(nLength);
  64.         int n = ::MultiByteToWideChar(CP_ACP, 0, lpsz, nLength, m_pchData, nLength+1);
  65.         ReleaseBuffer(n >= 0 ? n : -1);
  66.     }
  67. }
  68. #else //_UNICODE
  69. CString::CString(LPCWSTR lpsz, int nLength)
  70. {
  71.     Init();
  72.     if (nLength != 0)
  73.     {
  74.         AllocBuffer(nLength*2);
  75.         int n = ::WideCharToMultiByte(CP_ACP, 0, lpsz, nLength, m_pchData,
  76.             (nLength*2)+1, NULL, NULL);
  77.         ReleaseBuffer(n >= 0 ? n : -1);
  78.     }
  79. }
  80. #endif //!_UNICODE
  81.  
  82. //////////////////////////////////////////////////////////////////////////////
  83. // Assignment operators
  84.  
  85. const CString& CString::operator=(TCHAR ch)
  86. {
  87.     AssignCopy(1, &ch);
  88.     return *this;
  89. }
  90.  
  91. //////////////////////////////////////////////////////////////////////////////
  92. // less common string expressions
  93.  
  94. CString AFXAPI operator+(const CString& string1, TCHAR ch)
  95. {
  96.     CString s;
  97.     s.ConcatCopy(string1.GetData()->nDataLength, string1.m_pchData, 1, &ch);
  98.     return s;
  99. }
  100.  
  101. CString AFXAPI operator+(TCHAR ch, const CString& string)
  102. {
  103.     CString s;
  104.     s.ConcatCopy(1, &ch, string.GetData()->nDataLength, string.m_pchData);
  105.     return s;
  106. }
  107.  
  108. //////////////////////////////////////////////////////////////////////////////
  109. // Advanced manipulation
  110.  
  111. int CString::Delete(int nIndex, int nCount /* = 1 */)
  112. {
  113.     if (nIndex < 0)
  114.         nIndex = 0;
  115.     int nNewLength = GetData()->nDataLength;
  116.     if (nCount > 0 && nIndex < nNewLength)
  117.     {
  118.         CopyBeforeWrite();
  119.         int nBytesToCopy = nNewLength - (nIndex + nCount) + 1;
  120.  
  121.         memcpy(m_pchData + nIndex,
  122.             m_pchData + nIndex + nCount, nBytesToCopy * sizeof(TCHAR));
  123.         GetData()->nDataLength = nNewLength - nCount;
  124.     }
  125.  
  126.     return nNewLength;
  127. }
  128.  
  129. int CString::Insert(int nIndex, TCHAR ch)
  130. {
  131.     CopyBeforeWrite();
  132.  
  133.     if (nIndex < 0)
  134.         nIndex = 0;
  135.  
  136.     int nNewLength = GetData()->nDataLength;
  137.     if (nIndex > nNewLength)
  138.         nIndex = nNewLength;
  139.     nNewLength++;
  140.  
  141.     if (GetData()->nAllocLength < nNewLength)
  142.     {
  143.         CStringData* pOldData = GetData();
  144.         LPTSTR pstr = m_pchData;
  145.         AllocBuffer(nNewLength);
  146.         memcpy(m_pchData, pstr, (pOldData->nDataLength+1)*sizeof(TCHAR));
  147.         CString::Release(pOldData);
  148.     }
  149.  
  150.     // move existing bytes down
  151.     memcpy(m_pchData + nIndex + 1,
  152.         m_pchData + nIndex, (nNewLength-nIndex)*sizeof(TCHAR));
  153.     m_pchData[nIndex] = ch;
  154.     GetData()->nDataLength = nNewLength;
  155.  
  156.     return nNewLength;
  157. }
  158.  
  159. int CString::Insert(int nIndex, LPCTSTR pstr)
  160. {
  161.     if (nIndex < 0)
  162.         nIndex = 0;
  163.  
  164.     int nInsertLength = SafeStrlen(pstr);
  165.     int nNewLength = GetData()->nDataLength;
  166.     if (nInsertLength > 0)
  167.     {
  168.         CopyBeforeWrite();
  169.         if (nIndex > nNewLength)
  170.             nIndex = nNewLength;
  171.         nNewLength += nInsertLength;
  172.  
  173.         if (GetData()->nAllocLength < nNewLength)
  174.         {
  175.             CStringData* pOldData = GetData();
  176.             LPTSTR pstr = m_pchData;
  177.             AllocBuffer(nNewLength);
  178.             memcpy(m_pchData, pstr, (pOldData->nDataLength+1)*sizeof(TCHAR));
  179.             CString::Release(pOldData);
  180.         }
  181.  
  182.         // move existing bytes down
  183.         memcpy(m_pchData + nIndex + nInsertLength,
  184.             m_pchData + nIndex,
  185.             (nNewLength-nIndex-nInsertLength+1)*sizeof(TCHAR));
  186.         memcpy(m_pchData + nIndex,
  187.             pstr, nInsertLength*sizeof(TCHAR));
  188.         GetData()->nDataLength = nNewLength;
  189.     }
  190.  
  191.     return nNewLength;
  192. }
  193.  
  194. int CString::Replace(TCHAR chOld, TCHAR chNew)
  195. {
  196.     int nCount = 0;
  197.  
  198.     // short-circuit the nop case
  199.     if (chOld != chNew)
  200.     {
  201.         // otherwise modify each character that matches in the string
  202.         CopyBeforeWrite();
  203.         LPTSTR psz = m_pchData;
  204.         LPTSTR pszEnd = psz + GetData()->nDataLength;
  205.         while (psz < pszEnd)
  206.         {
  207.             // replace instances of the specified character only
  208.             if (*psz == chOld)
  209.             {
  210.                 *psz = chNew;
  211.                 nCount++;
  212.             }
  213.             psz = _tcsinc(psz);
  214.         }
  215.     }
  216.     return nCount;
  217. }
  218.  
  219. int CString::Replace(LPCTSTR lpszOld, LPCTSTR lpszNew)
  220. {
  221.     // can't have empty or NULL lpszOld
  222.  
  223.     int nSourceLen = SafeStrlen(lpszOld);
  224.     if (nSourceLen == 0)
  225.         return 0;
  226.     int nReplacementLen = SafeStrlen(lpszNew);
  227.  
  228.     // loop once to figure out the size of the result string
  229.     int nCount = 0;
  230.     LPTSTR lpszStart = m_pchData;
  231.     LPTSTR lpszEnd = m_pchData + GetData()->nDataLength;
  232.     LPTSTR lpszTarget;
  233.     while (lpszStart < lpszEnd)
  234.     {
  235.         while ((lpszTarget = _tcsstr(lpszStart, lpszOld)) != NULL)
  236.         {
  237.             nCount++;
  238.             lpszStart = lpszTarget + nSourceLen;
  239.         }
  240.         lpszStart += lstrlen(lpszStart) + 1;
  241.     }
  242.  
  243.     // if any changes were made, make them
  244.     if (nCount > 0)
  245.     {
  246.         CopyBeforeWrite();
  247.  
  248.         // if the buffer is too small, just
  249.         //   allocate a new buffer (slow but sure)
  250.         int nOldLength = GetData()->nDataLength;
  251.         int nNewLength =  nOldLength + (nReplacementLen-nSourceLen)*nCount;
  252.         if (GetData()->nAllocLength < nNewLength || GetData()->nRefs > 1)
  253.         {
  254.             CStringData* pOldData = GetData();
  255.             LPTSTR pstr = m_pchData;
  256.             AllocBuffer(nNewLength);
  257.             memcpy(m_pchData, pstr, pOldData->nDataLength*sizeof(TCHAR));
  258.             CString::Release(pOldData);
  259.         }
  260.         // else, we just do it in-place
  261.         lpszStart = m_pchData;
  262.         lpszEnd = m_pchData + GetData()->nDataLength;
  263.  
  264.         // loop again to actually do the work
  265.         while (lpszStart < lpszEnd)
  266.         {
  267.             while ( (lpszTarget = _tcsstr(lpszStart, lpszOld)) != NULL)
  268.             {
  269.                 int nBalance = nOldLength - (lpszTarget - m_pchData + nSourceLen);
  270.                 memmove(lpszTarget + nReplacementLen, lpszTarget + nSourceLen,
  271.                     nBalance * sizeof(TCHAR));
  272.                 memcpy(lpszTarget, lpszNew, nReplacementLen*sizeof(TCHAR));
  273.                 lpszStart = lpszTarget + nReplacementLen;
  274.                 lpszStart[nBalance] = '\0';
  275.                 nOldLength += (nReplacementLen - nSourceLen);
  276.             }
  277.             lpszStart += lstrlen(lpszStart) + 1;
  278.         }
  279.         ASSERT(m_pchData[nNewLength] == '\0');
  280.         GetData()->nDataLength = nNewLength;
  281.     }
  282.  
  283.     return nCount;
  284. }
  285.  
  286. int CString::Remove(TCHAR chRemove)
  287. {
  288.     CopyBeforeWrite();
  289.  
  290.     LPTSTR pstrSource = m_pchData;
  291.     LPTSTR pstrDest = m_pchData;
  292.     LPTSTR pstrEnd = m_pchData + GetData()->nDataLength;
  293.  
  294.     while (pstrSource < pstrEnd)
  295.     {
  296.         if (*pstrSource != chRemove)
  297.         {
  298.             *pstrDest = *pstrSource;
  299.             pstrDest = _tcsinc(pstrDest);
  300.         }
  301.         pstrSource = _tcsinc(pstrSource);
  302.     }
  303.     *pstrDest = '\0';
  304.     int nCount = pstrSource - pstrDest;
  305.     GetData()->nDataLength -= nCount;
  306.  
  307.     return nCount;
  308. }
  309.  
  310. //////////////////////////////////////////////////////////////////////////////
  311. // Very simple sub-string extraction
  312.  
  313. CString CString::Mid(int nFirst) const
  314. {
  315.     return Mid(nFirst, GetData()->nDataLength - nFirst);
  316. }
  317.  
  318. CString CString::Mid(int nFirst, int nCount) const
  319. {
  320.     // out-of-bounds requests return sensible things
  321.     if (nFirst < 0)
  322.         nFirst = 0;
  323.     if (nCount < 0)
  324.         nCount = 0;
  325.  
  326.     if (nFirst + nCount > GetData()->nDataLength)
  327.         nCount = GetData()->nDataLength - nFirst;
  328.     if (nFirst > GetData()->nDataLength)
  329.         nCount = 0;
  330.  
  331.     ASSERT(nFirst >= 0);
  332.     ASSERT(nFirst + nCount <= GetData()->nDataLength);
  333.  
  334.     // optimize case of returning entire string
  335.     if (nFirst == 0 && nFirst + nCount == GetData()->nDataLength)
  336.         return *this;
  337.  
  338.     CString dest;
  339.     AllocCopy(dest, nCount, nFirst, 0);
  340.     return dest;
  341. }
  342.  
  343. CString CString::Right(int nCount) const
  344. {
  345.     if (nCount < 0)
  346.         nCount = 0;
  347.     if (nCount >= GetData()->nDataLength)
  348.         return *this;
  349.  
  350.     CString dest;
  351.     AllocCopy(dest, nCount, GetData()->nDataLength-nCount, 0);
  352.     return dest;
  353. }
  354.  
  355. CString CString::Left(int nCount) const
  356. {
  357.     if (nCount < 0)
  358.         nCount = 0;
  359.     if (nCount >= GetData()->nDataLength)
  360.         return *this;
  361.  
  362.     CString dest;
  363.     AllocCopy(dest, nCount, 0, 0);
  364.     return dest;
  365. }
  366.  
  367. // strspn equivalent
  368. CString CString::SpanIncluding(LPCTSTR lpszCharSet) const
  369. {
  370.     ASSERT(AfxIsValidString(lpszCharSet));
  371.     return Left(_tcsspn(m_pchData, lpszCharSet));
  372. }
  373.  
  374. // strcspn equivalent
  375. CString CString::SpanExcluding(LPCTSTR lpszCharSet) const
  376. {
  377.     ASSERT(AfxIsValidString(lpszCharSet));
  378.     return Left(_tcscspn(m_pchData, lpszCharSet));
  379. }
  380.  
  381. //////////////////////////////////////////////////////////////////////////////
  382. // Finding
  383.  
  384. int CString::ReverseFind(TCHAR ch) const
  385. {
  386.     // find last single character
  387.     LPTSTR lpsz = _tcsrchr(m_pchData, (_TUCHAR) ch);
  388.  
  389.     // return -1 if not found, distance from beginning otherwise
  390.     return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData);
  391. }
  392.  
  393. // find a sub-string (like strstr)
  394. int CString::Find(LPCTSTR lpszSub) const
  395. {
  396.     return Find(lpszSub, 0);
  397. }
  398.  
  399. int CString::Find(LPCTSTR lpszSub, int nStart) const
  400. {
  401.     ASSERT(AfxIsValidString(lpszSub));
  402.  
  403.     int nLength = GetData()->nDataLength;
  404.     if (nStart > nLength)
  405.         return -1;
  406.  
  407.     // find first matching substring
  408.     LPTSTR lpsz = _tcsstr(m_pchData + nStart, lpszSub);
  409.  
  410.     // return -1 for not found, distance from beginning otherwise
  411.     return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData);
  412. }
  413.  
  414.  
  415. /////////////////////////////////////////////////////////////////////////////
  416. // CString formatting
  417.  
  418. #define TCHAR_ARG   TCHAR
  419. #define WCHAR_ARG   WCHAR
  420. #define CHAR_ARG    char
  421.  
  422. #ifdef _X86_
  423.     #define DOUBLE_ARG  _AFX_DOUBLE
  424. #else
  425.     #define DOUBLE_ARG  double
  426. #endif
  427.  
  428. #define FORCE_ANSI      0x10000
  429. #define FORCE_UNICODE   0x20000
  430. #define FORCE_INT64     0x40000
  431.  
  432. void CString::FormatV(LPCTSTR lpszFormat, va_list argList)
  433. {
  434.     ASSERT(AfxIsValidString(lpszFormat));
  435.  
  436.     va_list argListSave = argList;
  437.  
  438.     // make a guess at the maximum length of the resulting string
  439.     int nMaxLen = 0;
  440.     for (LPCTSTR lpsz = lpszFormat; *lpsz != '\0'; lpsz = _tcsinc(lpsz))
  441.     {
  442.         // handle '%' character, but watch out for '%%'
  443.         if (*lpsz != '%' || *(lpsz = _tcsinc(lpsz)) == '%')
  444.         {
  445.             nMaxLen += _tclen(lpsz);
  446.             continue;
  447.         }
  448.  
  449.         int nItemLen = 0;
  450.  
  451.         // handle '%' character with format
  452.         int nWidth = 0;
  453.         for (; *lpsz != '\0'; lpsz = _tcsinc(lpsz))
  454.         {
  455.             // check for valid flags
  456.             if (*lpsz == '#')
  457.                 nMaxLen += 2;   // for '0x'
  458.             else if (*lpsz == '*')
  459.                 nWidth = va_arg(argList, int);
  460.             else if (*lpsz == '-' || *lpsz == '+' || *lpsz == '0' ||
  461.                 *lpsz == ' ')
  462.                 ;
  463.             else // hit non-flag character
  464.                 break;
  465.         }
  466.         // get width and skip it
  467.         if (nWidth == 0)
  468.         {
  469.             // width indicated by
  470.             nWidth = _ttoi(lpsz);
  471.             for (; *lpsz != '\0' && _istdigit(*lpsz); lpsz = _tcsinc(lpsz))
  472.                 ;
  473.         }
  474.         ASSERT(nWidth >= 0);
  475.  
  476.         int nPrecision = 0;
  477.         if (*lpsz == '.')
  478.         {
  479.             // skip past '.' separator (width.precision)
  480.             lpsz = _tcsinc(lpsz);
  481.  
  482.             // get precision and skip it
  483.             if (*lpsz == '*')
  484.             {
  485.                 nPrecision = va_arg(argList, int);
  486.                 lpsz = _tcsinc(lpsz);
  487.             }
  488.             else
  489.             {
  490.                 nPrecision = _ttoi(lpsz);
  491.                 for (; *lpsz != '\0' && _istdigit(*lpsz); lpsz = _tcsinc(lpsz))
  492.                     ;
  493.             }
  494.             ASSERT(nPrecision >= 0);
  495.         }
  496.  
  497.         // should be on type modifier or specifier
  498.         int nModifier = 0;
  499.         if (_tcsncmp(lpsz, _T("I64"), 3) == 0)
  500.         {
  501.             lpsz += 3;
  502.             nModifier = FORCE_INT64;
  503. #if !defined(_X86_) && !defined(_ALPHA_)
  504.             // __int64 is only available on X86 and ALPHA platforms
  505.             ASSERT(FALSE);
  506. #endif
  507.         }
  508.         else
  509.         {
  510.             switch (*lpsz)
  511.             {
  512.             // modifiers that affect size
  513.             case 'h':
  514.                 nModifier = FORCE_ANSI;
  515.                 lpsz = _tcsinc(lpsz);
  516.                 break;
  517.             case 'l':
  518.                 nModifier = FORCE_UNICODE;
  519.                 lpsz = _tcsinc(lpsz);
  520.                 break;
  521.  
  522.             // modifiers that do not affect size
  523.             case 'F':
  524.             case 'N':
  525.             case 'L':
  526.                 lpsz = _tcsinc(lpsz);
  527.                 break;
  528.             }
  529.         }
  530.  
  531.         // now should be on specifier
  532.         switch (*lpsz | nModifier)
  533.         {
  534.         // single characters
  535.         case 'c':
  536.         case 'C':
  537.             nItemLen = 2;
  538.             va_arg(argList, TCHAR_ARG);
  539.             break;
  540.         case 'c'|FORCE_ANSI:
  541.         case 'C'|FORCE_ANSI:
  542.             nItemLen = 2;
  543.             va_arg(argList, CHAR_ARG);
  544.             break;
  545.         case 'c'|FORCE_UNICODE:
  546.         case 'C'|FORCE_UNICODE:
  547.             nItemLen = 2;
  548.             va_arg(argList, WCHAR_ARG);
  549.             break;
  550.  
  551.         // strings
  552.         case 's':
  553.             {
  554.                 LPCTSTR pstrNextArg = va_arg(argList, LPCTSTR);
  555.                 if (pstrNextArg == NULL)
  556.                    nItemLen = 6;  // "(null)"
  557.                 else
  558.                 {
  559.                    nItemLen = lstrlen(pstrNextArg);
  560.                    nItemLen = max(1, nItemLen);
  561.                 }
  562.             }
  563.             break;
  564.  
  565.         case 'S':
  566.             {
  567. #ifndef _UNICODE
  568.                 LPWSTR pstrNextArg = va_arg(argList, LPWSTR);
  569.                 if (pstrNextArg == NULL)
  570.                    nItemLen = 6;  // "(null)"
  571.                 else
  572.                 {
  573.                    nItemLen = wcslen(pstrNextArg);
  574.                    nItemLen = max(1, nItemLen);
  575.                 }
  576. #else
  577.                 LPCSTR pstrNextArg = va_arg(argList, LPCSTR);
  578.                 if (pstrNextArg == NULL)
  579.                    nItemLen = 6; // "(null)"
  580.                 else
  581.                 {
  582.                    nItemLen = lstrlenA(pstrNextArg);
  583.                    nItemLen = max(1, nItemLen);
  584.                 }
  585. #endif
  586.             }
  587.             break;
  588.  
  589.         case 's'|FORCE_ANSI:
  590.         case 'S'|FORCE_ANSI:
  591.             {
  592.                 LPCSTR pstrNextArg = va_arg(argList, LPCSTR);
  593.                 if (pstrNextArg == NULL)
  594.                    nItemLen = 6; // "(null)"
  595.                 else
  596.                 {
  597.                    nItemLen = lstrlenA(pstrNextArg);
  598.                    nItemLen = max(1, nItemLen);
  599.                 }
  600.             }
  601.             break;
  602.  
  603.         case 's'|FORCE_UNICODE:
  604.         case 'S'|FORCE_UNICODE:
  605.             {
  606.                 LPWSTR pstrNextArg = va_arg(argList, LPWSTR);
  607.                 if (pstrNextArg == NULL)
  608.                    nItemLen = 6; // "(null)"
  609.                 else
  610.                 {
  611.                    nItemLen = wcslen(pstrNextArg);
  612.                    nItemLen = max(1, nItemLen);
  613.                 }
  614.             }
  615.             break;
  616.         }
  617.  
  618.         // adjust nItemLen for strings
  619.         if (nItemLen != 0)
  620.         {
  621.             if (nPrecision != 0)
  622.                 nItemLen = min(nItemLen, nPrecision);
  623.             nItemLen = max(nItemLen, nWidth);
  624.         }
  625.         else
  626.         {
  627.             switch (*lpsz)
  628.             {
  629.             // integers
  630.             case 'd':
  631.             case 'i':
  632.             case 'u':
  633.             case 'x':
  634.             case 'X':
  635.             case 'o':
  636.                 if (nModifier & FORCE_INT64)
  637.                     va_arg(argList, __int64);
  638.                 else
  639.                     va_arg(argList, int);
  640.                 nItemLen = 32;
  641.                 nItemLen = max(nItemLen, nWidth+nPrecision);
  642.                 break;
  643.  
  644.             case 'e':
  645.             case 'g':
  646.             case 'G':
  647.                 va_arg(argList, DOUBLE_ARG);
  648.                 nItemLen = 128;
  649.                 nItemLen = max(nItemLen, nWidth+nPrecision);
  650.                 break;
  651.  
  652.             case 'f':
  653.                 va_arg(argList, DOUBLE_ARG);
  654.                 nItemLen = 128; // width isn't truncated
  655.                 // 312 == strlen("-1+(309 zeroes).")
  656.                 // 309 zeroes == max precision of a double
  657.                 nItemLen = max(nItemLen, 312+nPrecision);
  658.                 break;
  659.  
  660.             case 'p':
  661.                 va_arg(argList, void*);
  662.                 nItemLen = 32;
  663.                 nItemLen = max(nItemLen, nWidth+nPrecision);
  664.                 break;
  665.  
  666.             // no output
  667.             case 'n':
  668.                 va_arg(argList, int*);
  669.                 break;
  670.  
  671.             default:
  672.                 ASSERT(FALSE);  // unknown formatting option
  673.             }
  674.         }
  675.  
  676.         // adjust nMaxLen for output nItemLen
  677.         nMaxLen += nItemLen;
  678.     }
  679.  
  680.     GetBuffer(nMaxLen);
  681.     VERIFY(_vstprintf(m_pchData, lpszFormat, argListSave) <= GetAllocLength());
  682.     ReleaseBuffer();
  683.  
  684.     va_end(argListSave);
  685. }
  686.  
  687. // formatting (using wsprintf style formatting)
  688. void AFX_CDECL CString::Format(LPCTSTR lpszFormat, ...)
  689. {
  690.     ASSERT(AfxIsValidString(lpszFormat));
  691.  
  692.     va_list argList;
  693.     va_start(argList, lpszFormat);
  694.     FormatV(lpszFormat, argList);
  695.     va_end(argList);
  696. }
  697.  
  698. void AFX_CDECL CString::Format(UINT nFormatID, ...)
  699. {
  700.     CString strFormat;
  701.     VERIFY(strFormat.LoadString(nFormatID) != 0);
  702.  
  703.     va_list argList;
  704.     va_start(argList, nFormatID);
  705.     FormatV(strFormat, argList);
  706.     va_end(argList);
  707. }
  708.  
  709. // formatting (using FormatMessage style formatting)
  710. void AFX_CDECL CString::FormatMessage(LPCTSTR lpszFormat, ...)
  711. {
  712.     // format message into temporary buffer lpszTemp
  713.     va_list argList;
  714.     va_start(argList, lpszFormat);
  715.     LPTSTR lpszTemp;
  716.  
  717.     if (::FormatMessage(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ALLOCATE_BUFFER,
  718.         lpszFormat, 0, 0, (LPTSTR)&lpszTemp, 0, &argList) == 0 ||
  719.         lpszTemp == NULL)
  720.     {
  721.         AfxThrowMemoryException();
  722.     }
  723.  
  724.     // assign lpszTemp into the resulting string and free the temporary
  725.     *this = lpszTemp;
  726.     LocalFree(lpszTemp);
  727.     va_end(argList);
  728. }
  729.  
  730. void AFX_CDECL CString::FormatMessage(UINT nFormatID, ...)
  731. {
  732.     // get format string from string table
  733.     CString strFormat;
  734.     VERIFY(strFormat.LoadString(nFormatID) != 0);
  735.  
  736.     // format message into temporary buffer lpszTemp
  737.     va_list argList;
  738.     va_start(argList, nFormatID);
  739.     LPTSTR lpszTemp;
  740.     if (::FormatMessage(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ALLOCATE_BUFFER,
  741.         strFormat, 0, 0, (LPTSTR)&lpszTemp, 0, &argList) == 0 ||
  742.         lpszTemp == NULL)
  743.     {
  744.         AfxThrowMemoryException();
  745.     }
  746.  
  747.     // assign lpszTemp into the resulting string and free lpszTemp
  748.     *this = lpszTemp;
  749.     LocalFree(lpszTemp);
  750.     va_end(argList);
  751. }
  752.  
  753. void CString::TrimRight(LPCTSTR lpszTargetList)
  754. {
  755.     // find beginning of trailing matches
  756.     // by starting at beginning (DBCS aware)
  757.  
  758.     CopyBeforeWrite();
  759.     LPTSTR lpsz = m_pchData;
  760.     LPTSTR lpszLast = NULL;
  761.  
  762.     while (*lpsz != '\0')
  763.     {
  764.         if (_tcschr(lpszTargetList, *lpsz) != NULL)
  765.         {
  766.             if (lpszLast == NULL)
  767.                 lpszLast = lpsz;
  768.         }
  769.         else
  770.             lpszLast = NULL;
  771.         lpsz = _tcsinc(lpsz);
  772.     }
  773.  
  774.     if (lpszLast != NULL)
  775.     {
  776.         // truncate at left-most matching character
  777.         *lpszLast = '\0';
  778.         GetData()->nDataLength = lpszLast - m_pchData;
  779.     }
  780. }
  781.  
  782. void CString::TrimRight(TCHAR chTarget)
  783. {
  784.     // find beginning of trailing matches
  785.     // by starting at beginning (DBCS aware)
  786.  
  787.     CopyBeforeWrite();
  788.     LPTSTR lpsz = m_pchData;
  789.     LPTSTR lpszLast = NULL;
  790.  
  791.     while (*lpsz != '\0')
  792.     {
  793.         if (*lpsz == chTarget)
  794.         {
  795.             if (lpszLast == NULL)
  796.                 lpszLast = lpsz;
  797.         }
  798.         else
  799.             lpszLast = NULL;
  800.         lpsz = _tcsinc(lpsz);
  801.     }
  802.  
  803.     if (lpszLast != NULL)
  804.     {
  805.         // truncate at left-most matching character
  806.         *lpszLast = '\0';
  807.         GetData()->nDataLength = lpszLast - m_pchData;
  808.     }
  809. }
  810.  
  811. void CString::TrimRight()
  812. {
  813.     // find beginning of trailing spaces by starting at beginning (DBCS aware)
  814.  
  815.     CopyBeforeWrite();
  816.     LPTSTR lpsz = m_pchData;
  817.     LPTSTR lpszLast = NULL;
  818.  
  819.     while (*lpsz != '\0')
  820.     {
  821.         if (_istspace(*lpsz))
  822.         {
  823.             if (lpszLast == NULL)
  824.                 lpszLast = lpsz;
  825.         }
  826.         else
  827.             lpszLast = NULL;
  828.         lpsz = _tcsinc(lpsz);
  829.     }
  830.  
  831.     if (lpszLast != NULL)
  832.     {
  833.         // truncate at trailing space start
  834.         *lpszLast = '\0';
  835.         GetData()->nDataLength = lpszLast - m_pchData;
  836.     }
  837. }
  838.  
  839. void CString::TrimLeft(LPCTSTR lpszTargets)
  840. {
  841.     // if we're not trimming anything, we're not doing any work
  842.     if (SafeStrlen(lpszTargets) == 0)
  843.         return;
  844.  
  845.     CopyBeforeWrite();
  846.     LPCTSTR lpsz = m_pchData;
  847.  
  848.     while (*lpsz != '\0')
  849.     {
  850.         if (_tcschr(lpszTargets, *lpsz) == NULL)
  851.             break;
  852.         lpsz = _tcsinc(lpsz);
  853.     }
  854.  
  855.     if (lpsz != m_pchData)
  856.     {
  857.         // fix up data and length
  858.         int nDataLength = GetData()->nDataLength - (lpsz - m_pchData);
  859.         memmove(m_pchData, lpsz, (nDataLength+1)*sizeof(TCHAR));
  860.         GetData()->nDataLength = nDataLength;
  861.     }
  862. }
  863.  
  864. void CString::TrimLeft(TCHAR chTarget)
  865. {
  866.     // find first non-matching character
  867.  
  868.     CopyBeforeWrite();
  869.     LPCTSTR lpsz = m_pchData;
  870.  
  871.     while (chTarget == *lpsz)
  872.         lpsz = _tcsinc(lpsz);
  873.  
  874.     if (lpsz != m_pchData)
  875.     {
  876.         // fix up data and length
  877.         int nDataLength = GetData()->nDataLength - (lpsz - m_pchData);
  878.         memmove(m_pchData, lpsz, (nDataLength+1)*sizeof(TCHAR));
  879.         GetData()->nDataLength = nDataLength;
  880.     }
  881. }
  882.  
  883. void CString::TrimLeft()
  884. {
  885.     // find first non-space character
  886.  
  887.     CopyBeforeWrite();
  888.     LPCTSTR lpsz = m_pchData;
  889.  
  890.     while (_istspace(*lpsz))
  891.         lpsz = _tcsinc(lpsz);
  892.  
  893.     if (lpsz != m_pchData)
  894.     {
  895.         // fix up data and length
  896.         int nDataLength = GetData()->nDataLength - (lpsz - m_pchData);
  897.         memmove(m_pchData, lpsz, (nDataLength+1)*sizeof(TCHAR));
  898.         GetData()->nDataLength = nDataLength;
  899.     }
  900. }
  901.  
  902. ///////////////////////////////////////////////////////////////////////////////
  903. // CString support for template collections
  904.  
  905. #if _MSC_VER >= 1100
  906. template<> void AFXAPI ConstructElements<CString> (CString* pElements, int nCount)
  907. #else
  908. void AFXAPI ConstructElements(CString* pElements, int nCount)
  909. #endif
  910. {
  911.     ASSERT(nCount == 0 ||
  912.         AfxIsValidAddress(pElements, nCount * sizeof(CString)));
  913.  
  914.     for (; nCount--; ++pElements)
  915.         memcpy(pElements, &afxEmptyString, sizeof(*pElements));
  916. }
  917.  
  918. #if _MSC_VER >= 1100
  919. template<> void AFXAPI DestructElements<CString> (CString* pElements, int nCount)
  920. #else
  921. void AFXAPI DestructElements(CString* pElements, int nCount)
  922. #endif
  923. {
  924.     ASSERT(nCount == 0 ||
  925.         AfxIsValidAddress(pElements, nCount * sizeof(CString)));
  926.  
  927.     for (; nCount--; ++pElements)
  928.         pElements->~CString();
  929. }
  930.  
  931. #if _MSC_VER >= 1100
  932. template<> void AFXAPI CopyElements<CString> (CString* pDest, const CString* pSrc, int nCount)
  933. #else
  934. void AFXAPI CopyElements(CString* pDest, const CString* pSrc, int nCount)
  935. #endif
  936. {
  937.     ASSERT(nCount == 0 ||
  938.         AfxIsValidAddress(pDest, nCount * sizeof(CString)));
  939.     ASSERT(nCount == 0 ||
  940.         AfxIsValidAddress(pSrc, nCount * sizeof(CString)));
  941.  
  942.     for (; nCount--; ++pDest, ++pSrc)
  943.         *pDest = *pSrc;
  944. }
  945.  
  946. #ifndef OLE2ANSI
  947. #if _MSC_VER >= 1100
  948. template<> UINT AFXAPI HashKey<LPCWSTR> (LPCWSTR key)
  949. #else
  950. UINT AFXAPI HashKey(LPCWSTR key)
  951. #endif
  952. {
  953.     UINT nHash = 0;
  954.     while (*key)
  955.         nHash = (nHash<<5) + nHash + *key++;
  956.     return nHash;
  957. }
  958. #endif
  959.  
  960. #if _MSC_VER >= 1100
  961. template<> UINT AFXAPI HashKey<LPCSTR> (LPCSTR key)
  962. #else
  963. UINT AFXAPI HashKey(LPCSTR key)
  964. #endif
  965. {
  966.     UINT nHash = 0;
  967.     while (*key)
  968.         nHash = (nHash<<5) + nHash + *key++;
  969.     return nHash;
  970. }
  971.  
  972. ///////////////////////////////////////////////////////////////////////////////
  973.