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

946 lines
24 KiB

  1. //
  2. // MODULE: APGTSSTR.CPP
  3. //
  4. // PURPOSE: implements DLL Growable string object CString
  5. // (pretty much a la MFC, but avoids all that MFC overhead)
  6. //
  7. // PROJECT: Generic Troubleshooter DLL for Microsoft AnswerPoint
  8. //
  9. // COMPANY: Saltmine Creative, Inc. (206)-284-7511 [email protected]
  10. //
  11. // AUTHOR: Joe Mabel (reworked code from Microsoft's MFC sources)
  12. //
  13. // ORIGINAL DATE: 8-2-96 Roman Mach; totally re-implemented 1/15/99 Joe Mabel
  14. //
  15. // NOTES:
  16. // 1. As of 1/99, re-implemented based on MFC's implementation. Pared down
  17. // to what we use.
  18. // 2. This file modified 5/26/01 by Davide Massarenti from MS to remove reference counting
  19. // from CString implementation to resolve thread safety issue discovered on dual processor
  20. // systems when dll compiled for WinXP by MS compiler.
  21. //
  22. // Version Date By Comments
  23. //--------------------------------------------------------------------
  24. // V0.1 - RM Original
  25. // V3.0 7-24-98 JM Abstracted this out as a separate header.
  26. // V3.1 1-15-99 JM Redo based on MFC implementation
  27. //
  28. #include "stdafx.h"
  29. #include <stdio.h>
  30. #include "apgtsstr.h"
  31. #include "apgtsmfc.h"
  32. // Windows extensions to strings
  33. #ifdef _UNICODE
  34. #define CHAR_FUDGE 1 // one TCHAR unused is good enough
  35. #else
  36. #define CHAR_FUDGE 2 // two BYTES unused for case of DBC last char
  37. #endif
  38. /////////////////////////////////////////////////////////////////////////////
  39. // static class data, special inlines
  40. // afxChNil is left for backward compatibility
  41. TCHAR afxChNil = '\0';
  42. // For an empty string, m_pchData will point here
  43. // (note: avoids special case of checking for NULL m_pchData)
  44. // empty string data (and locked)
  45. // [BC - 20010529] - change below array from int to long and add extra 0 for padding
  46. // to avoid problems indicated by Davide Massarenti in 64-bit environments
  47. static long rgInitData[] = { -1, 0, 0, 0, 0 };
  48. static CStringData* afxDataNil = (CStringData*)&rgInitData;
  49. static LPCTSTR afxPchNil = (LPCTSTR)(((BYTE*)&rgInitData)+sizeof(CStringData));
  50. // special function to make afxEmptyString work even during initialization
  51. const CString& AfxGetEmptyString()
  52. { return *(CString*)&afxPchNil; }
  53. //////////////////////////////////////////////////////////////////////////////
  54. // Construction/Destruction
  55. CString::CString()
  56. {
  57. Init();
  58. }
  59. CString::CString(const CString& stringSrc)
  60. {
  61. // REMOVING REF COUNTING: ASSERT(stringSrc.GetData()->nRefs != 0);
  62. // REMOVING REF COUNTING: if (stringSrc.GetData()->nRefs >= 0)
  63. // REMOVING REF COUNTING: {
  64. // REMOVING REF COUNTING: ASSERT(stringSrc.GetData() != afxDataNil);
  65. // REMOVING REF COUNTING: m_pchData = stringSrc.m_pchData;
  66. // REMOVING REF COUNTING: InterlockedIncrement(&GetData()->nRefs);
  67. // REMOVING REF COUNTING: }
  68. // REMOVING REF COUNTING: else
  69. // REMOVING REF COUNTING: {
  70. Init();
  71. *this = stringSrc.m_pchData;
  72. // REMOVING REF COUNTING: }
  73. }
  74. void CString::AllocBuffer(int nLen)
  75. // always allocate one extra character for '\0' termination
  76. // assumes [optimistically] that data length will equal allocation length
  77. {
  78. ASSERT(nLen >= 0);
  79. // MFC had the following assertion. I've killed it because we
  80. // don't have INT_MAX. JM 1/15/99
  81. //ASSERT(nLen <= INT_MAX-1); // max size (enough room for 1 extra)
  82. if (nLen == 0)
  83. Init();
  84. else
  85. {
  86. CStringData* pData =
  87. (CStringData*)new BYTE[sizeof(CStringData) + (nLen+1)*sizeof(TCHAR)];
  88. //[BC-03022001] - added check for NULL ptr to satisfy MS code analysis tool.
  89. if(pData)
  90. {
  91. pData->nRefs = 1;
  92. pData->data()[nLen] = '\0';
  93. pData->nDataLength = nLen;
  94. pData->nAllocLength = nLen;
  95. m_pchData = pData->data();
  96. }
  97. }
  98. }
  99. void CString::Release()
  100. {
  101. if (GetData() != afxDataNil)
  102. {
  103. // REMOVING REF COUNTING: ASSERT(GetData()->nRefs != 0);
  104. // REMOVING REF COUNTING: if (InterlockedDecrement(&GetData()->nRefs) <= 0)
  105. delete[] (BYTE*)GetData();
  106. Init();
  107. }
  108. }
  109. void PASCAL CString::Release(CStringData* pData)
  110. {
  111. if (pData != afxDataNil)
  112. {
  113. // REMOVING REF COUNTING: ASSERT(pData->nRefs != 0);
  114. // REMOVING REF COUNTING: if (InterlockedDecrement(&pData->nRefs) <= 0)
  115. delete[] (BYTE*)pData;
  116. }
  117. }
  118. void CString::CopyBeforeWrite()
  119. {
  120. // REMOVING REF COUNTING: if (GetData()->nRefs > 1)
  121. // REMOVING REF COUNTING: {
  122. // REMOVING REF COUNTING: CStringData* pData = GetData();
  123. // REMOVING REF COUNTING: Release();
  124. // REMOVING REF COUNTING: AllocBuffer(pData->nDataLength);
  125. // REMOVING REF COUNTING: memcpy(m_pchData, pData->data(), (pData->nDataLength+1)*sizeof(TCHAR));
  126. // REMOVING REF COUNTING: }
  127. // REMOVING REF COUNTING: ASSERT(GetData()->nRefs <= 1);
  128. }
  129. void CString::AllocBeforeWrite(int nLen)
  130. {
  131. // REMOVING REF COUNTING: if (GetData()->nRefs > 1 || nLen > GetData()->nAllocLength)
  132. if (nLen > GetData()->nAllocLength)
  133. {
  134. Release();
  135. AllocBuffer(nLen);
  136. }
  137. // REMOVING REF COUNTING: ASSERT(GetData()->nRefs <= 1);
  138. }
  139. CString::~CString()
  140. // free any attached data
  141. {
  142. if (GetData() != afxDataNil)
  143. {
  144. // REMOVING REF COUNTING: if (InterlockedDecrement(&GetData()->nRefs) <= 0)
  145. delete[] (BYTE*)GetData();
  146. }
  147. }
  148. void CString::Empty()
  149. {
  150. if (GetData()->nDataLength == 0)
  151. return;
  152. // REMOVING REF COUNTING: if (GetData()->nRefs >= 0)
  153. Release();
  154. // REMOVING REF COUNTING: else
  155. // REMOVING REF COUNTING: *this = &afxChNil;
  156. ASSERT(GetData()->nDataLength == 0);
  157. // REMOVING REF COUNTING: ASSERT(GetData()->nRefs < 0 || GetData()->nAllocLength == 0);
  158. }
  159. //////////////////////////////////////////////////////////////////////////////
  160. // Helpers for the rest of the implementation
  161. void CString::AllocCopy(CString& dest, int nCopyLen, int nCopyIndex,
  162. int nExtraLen) const
  163. {
  164. // will clone the data attached to this string
  165. // allocating 'nExtraLen' characters
  166. // Places results in uninitialized string 'dest'
  167. // Will copy the part or all of original data to start of new string
  168. int nNewLen = nCopyLen + nExtraLen;
  169. if (nNewLen == 0)
  170. {
  171. dest.Init();
  172. }
  173. else
  174. {
  175. dest.AllocBuffer(nNewLen);
  176. memcpy(dest.m_pchData, m_pchData+nCopyIndex, nCopyLen*sizeof(TCHAR));
  177. }
  178. }
  179. //////////////////////////////////////////////////////////////////////////////
  180. // More sophisticated construction
  181. CString::CString(LPCTSTR lpsz)
  182. {
  183. Init();
  184. // Unlike MFC, no implicit LoadString offered - JM 1/15/99
  185. int nLen = SafeStrlen(lpsz);
  186. if (nLen != 0)
  187. {
  188. AllocBuffer(nLen);
  189. memcpy(m_pchData, lpsz, nLen*sizeof(TCHAR));
  190. }
  191. }
  192. //////////////////////////////////////////////////////////////////////////////
  193. // Assignment operators
  194. // All assign a new value to the string
  195. // (a) first see if the buffer is big enough
  196. // (b) if enough room, copy on top of old buffer, set size and type
  197. // (c) otherwise free old string data, and create a new one
  198. //
  199. // All routines return the new string (but as a 'const CString&' so that
  200. // assigning it again will cause a copy, eg: s1 = s2 = "hi there".
  201. //
  202. void CString::AssignCopy(int nSrcLen, LPCTSTR lpszSrcData)
  203. {
  204. AllocBeforeWrite(nSrcLen);
  205. memcpy(m_pchData, lpszSrcData, nSrcLen*sizeof(TCHAR));
  206. GetData()->nDataLength = nSrcLen;
  207. m_pchData[nSrcLen] = '\0';
  208. }
  209. const CString& CString::operator=(const CString& stringSrc)
  210. {
  211. // REMOVING REF COUNTING: if (m_pchData != stringSrc.m_pchData)
  212. // REMOVING REF COUNTING: {
  213. // REMOVING REF COUNTING: if ((GetData()->nRefs < 0 && GetData() != afxDataNil) ||
  214. // REMOVING REF COUNTING: stringSrc.GetData()->nRefs < 0)
  215. // REMOVING REF COUNTING: {
  216. // actual copy necessary since one of the strings is locked
  217. AssignCopy(stringSrc.GetData()->nDataLength, stringSrc.m_pchData);
  218. // REMOVING REF COUNTING: }
  219. // REMOVING REF COUNTING: else
  220. // REMOVING REF COUNTING: {
  221. // REMOVING REF COUNTING: // can just copy references around
  222. // REMOVING REF COUNTING: Release();
  223. // REMOVING REF COUNTING: ASSERT(stringSrc.GetData() != afxDataNil);
  224. // REMOVING REF COUNTING: m_pchData = stringSrc.m_pchData;
  225. // REMOVING REF COUNTING: InterlockedIncrement(&GetData()->nRefs);
  226. // REMOVING REF COUNTING: }
  227. // REMOVING REF COUNTING: }
  228. return *this;
  229. }
  230. const CString& CString::operator=(LPCTSTR lpsz)
  231. {
  232. // Suppress the following Assert from MFC - JM 1/15/99
  233. // ASSERT(lpsz == NULL || AfxIsValidString(lpsz, FALSE));
  234. AssignCopy(SafeStrlen(lpsz), lpsz);
  235. return *this;
  236. }
  237. const CString& CString::operator=(TCHAR ch)
  238. {
  239. AssignCopy(1, &ch);
  240. return *this;
  241. }
  242. #ifdef _UNICODE
  243. const CString& CString::operator=(LPCSTR lpsz)
  244. {
  245. int nSrcLen = lpsz != NULL ? lstrlenA(lpsz) : 0;
  246. AllocBeforeWrite(nSrcLen);
  247. mbstowcs(m_pchData, lpsz, nSrcLen+1);
  248. ReleaseBuffer();
  249. return *this;
  250. }
  251. #else //!_UNICODE
  252. const CString& CString::operator=(LPCWSTR lpsz)
  253. {
  254. int nSrcLen = lpsz != NULL ? wcslen(lpsz) : 0;
  255. AllocBeforeWrite(nSrcLen*2);
  256. wcstombs(m_pchData, lpsz, (nSrcLen*2)+1);
  257. ReleaseBuffer();
  258. return *this;
  259. }
  260. #endif //!_UNICODE
  261. //////////////////////////////////////////////////////////////////////////////
  262. // concatenation
  263. // NOTE: "operator+" is done as friend functions for simplicity
  264. // There are three variants:
  265. // CString + CString
  266. // and for ? = TCHAR, LPCTSTR
  267. // CString + ?
  268. // ? + CString
  269. // we (Saltmine) have switched away from friend because VC 6/0 doesn't like it. - JM 1/15/99
  270. // we (Saltmine) do LPCTSTR but not TCHAR - JM 1/15/99
  271. void CString::ConcatCopy(int nSrc1Len, LPCTSTR lpszSrc1Data,
  272. int nSrc2Len, LPCTSTR lpszSrc2Data)
  273. {
  274. // -- master concatenation routine
  275. // Concatenate two sources
  276. // -- assume that 'this' is a new CString object
  277. int nNewLen = nSrc1Len + nSrc2Len;
  278. if (nNewLen != 0)
  279. {
  280. AllocBuffer(nNewLen);
  281. memcpy(m_pchData, lpszSrc1Data, nSrc1Len*sizeof(TCHAR));
  282. memcpy(m_pchData+nSrc1Len, lpszSrc2Data, nSrc2Len*sizeof(TCHAR));
  283. }
  284. }
  285. CString CString::operator+(const CString& string2)
  286. {
  287. CString s;
  288. s.ConcatCopy(GetData()->nDataLength, m_pchData,
  289. string2.GetData()->nDataLength, string2.m_pchData);
  290. return s;
  291. }
  292. CString CString::operator+(LPCTSTR lpsz)
  293. {
  294. // Suppress the following Assert from MFC - JM 1/15/99
  295. // ASSERT(lpsz == NULL || AfxIsValidString(lpsz, FALSE));
  296. CString s;
  297. s.ConcatCopy(GetData()->nDataLength, m_pchData,
  298. CString::SafeStrlen(lpsz), lpsz);
  299. return s;
  300. }
  301. //////////////////////////////////////////////////////////////////////////////
  302. // concatenate in place
  303. void CString::ConcatInPlace(int nSrcLen, LPCTSTR lpszSrcData)
  304. {
  305. // -- the main routine for += operators
  306. // concatenating an empty string is a no-op!
  307. if (nSrcLen == 0)
  308. return;
  309. // if the buffer is too small, or we have a width mis-match, just
  310. // allocate a new buffer (slow but sure)
  311. // REMOVING REF COUNTING: if (GetData()->nRefs > 1 || GetData()->nDataLength + nSrcLen > GetData()->nAllocLength)
  312. if (GetData()->nDataLength + nSrcLen > GetData()->nAllocLength)
  313. {
  314. // we have to grow the buffer, use the ConcatCopy routine
  315. CStringData* pOldData = GetData();
  316. ConcatCopy(GetData()->nDataLength, m_pchData, nSrcLen, lpszSrcData);
  317. ASSERT(pOldData != NULL);
  318. CString::Release(pOldData);
  319. }
  320. else
  321. {
  322. // fast concatenation when buffer big enough
  323. memcpy(m_pchData+GetData()->nDataLength, lpszSrcData, nSrcLen*sizeof(TCHAR));
  324. GetData()->nDataLength += nSrcLen;
  325. ASSERT(GetData()->nDataLength <= GetData()->nAllocLength);
  326. m_pchData[GetData()->nDataLength] = '\0';
  327. }
  328. }
  329. const CString& CString::operator+=(LPCTSTR lpsz)
  330. {
  331. // Suppress the following Assert from MFC - JM 1/15/99
  332. // ASSERT(lpsz == NULL || AfxIsValidString(lpsz, FALSE));
  333. ConcatInPlace(SafeStrlen(lpsz), lpsz);
  334. return *this;
  335. }
  336. const CString& CString::operator+=(const CString& string)
  337. {
  338. ConcatInPlace(string.GetData()->nDataLength, string.m_pchData);
  339. return *this;
  340. }
  341. ///////////////////////////////////////////////////////////////////////////////
  342. // Advanced direct buffer access
  343. // CString::GetBuffer() and CString::ReleaseBuffer() calls should be matched.
  344. LPTSTR CString::GetBuffer(int nMinBufLength)
  345. {
  346. ASSERT(nMinBufLength >= 0);
  347. // REMOVING REF COUNTING: if (GetData()->nRefs > 1 || nMinBufLength > GetData()->nAllocLength)
  348. if (nMinBufLength > GetData()->nAllocLength)
  349. {
  350. // we have to grow the buffer
  351. CStringData* pOldData = GetData();
  352. int nOldLen = GetData()->nDataLength; // AllocBuffer will tromp it
  353. if (nMinBufLength < nOldLen)
  354. nMinBufLength = nOldLen;
  355. AllocBuffer(nMinBufLength);
  356. memcpy(m_pchData, pOldData->data(), (nOldLen+1)*sizeof(TCHAR));
  357. GetData()->nDataLength = nOldLen;
  358. CString::Release(pOldData);
  359. }
  360. // REMOVING REF COUNTING: ASSERT(GetData()->nRefs <= 1);
  361. // return a pointer to the character storage for this string
  362. ASSERT(m_pchData != NULL);
  363. return m_pchData;
  364. }
  365. // CString::GetBuffer() and CString::ReleaseBuffer() calls should be matched.
  366. void CString::ReleaseBuffer(int nNewLength /* = -1 */)
  367. {
  368. CopyBeforeWrite(); // just in case GetBuffer was not called
  369. if (nNewLength == -1)
  370. nNewLength = lstrlen(m_pchData); // zero terminated
  371. ASSERT(nNewLength <= GetData()->nAllocLength);
  372. GetData()->nDataLength = nNewLength;
  373. m_pchData[nNewLength] = '\0';
  374. }
  375. LPTSTR CString::GetBufferSetLength(int nNewLength)
  376. {
  377. ASSERT(nNewLength >= 0);
  378. GetBuffer(nNewLength);
  379. GetData()->nDataLength = nNewLength;
  380. m_pchData[nNewLength] = '\0';
  381. return m_pchData;
  382. }
  383. ///////////////////////////////////////////////////////////////////////////////
  384. // Commonly used routines
  385. int CString::Find(TCHAR ch) const
  386. {
  387. // find first single character
  388. LPTSTR lpsz = _tcschr(m_pchData, (_TUCHAR)ch);
  389. // return -1 if not found and index otherwise
  390. return (lpsz == NULL) ? CString::FIND_FAILED : (int)(lpsz - m_pchData);
  391. }
  392. void CString::MakeLower()
  393. {
  394. CopyBeforeWrite();
  395. _tcslwr(m_pchData);
  396. }
  397. //////////////////////////////////////////////////////////////////////////////
  398. // Very simple sub-string extraction
  399. CString CString::Mid(int nFirst) const
  400. {
  401. return Mid(nFirst, GetData()->nDataLength - nFirst);
  402. }
  403. CString CString::Mid(int nFirst, int nCount) const
  404. {
  405. // out-of-bounds requests return sensible things
  406. if (nFirst < 0)
  407. nFirst = 0;
  408. if (nCount < 0)
  409. nCount = 0;
  410. if (nFirst + nCount > GetData()->nDataLength)
  411. nCount = GetData()->nDataLength - nFirst;
  412. if (nFirst > GetData()->nDataLength)
  413. nCount = 0;
  414. CString dest;
  415. AllocCopy(dest, nCount, nFirst, 0);
  416. return dest;
  417. }
  418. CString CString::Right(int nCount) const
  419. {
  420. if (nCount < 0)
  421. nCount = 0;
  422. else if (nCount > GetData()->nDataLength)
  423. nCount = GetData()->nDataLength;
  424. CString dest;
  425. AllocCopy(dest, nCount, GetData()->nDataLength-nCount, 0);
  426. return dest;
  427. }
  428. CString CString::Left(int nCount) const
  429. {
  430. if (nCount < 0)
  431. nCount = 0;
  432. else if (nCount > GetData()->nDataLength)
  433. nCount = GetData()->nDataLength;
  434. CString dest;
  435. AllocCopy(dest, nCount, 0, 0);
  436. return dest;
  437. }
  438. //////////////////////////////////////////////////////////////////////////////
  439. // Finding
  440. int CString::ReverseFind(TCHAR ch) const
  441. {
  442. // find last single character
  443. LPTSTR lpsz = _tcsrchr(m_pchData, (_TUCHAR)ch);
  444. // return -1 if not found, distance from beginning otherwise
  445. return (lpsz == NULL) ? CString::FIND_FAILED : (int)(lpsz - m_pchData);
  446. }
  447. // find a sub-string (like strstr)
  448. int CString::Find(LPCTSTR lpszSub) const
  449. {
  450. // Suppress the following Assert from MFC - JM 1/15/99
  451. // ASSERT(AfxIsValidString(lpszSub, FALSE));
  452. // find first matching substring
  453. LPTSTR lpsz = _tcsstr(m_pchData, lpszSub);
  454. // return -1 for not found, distance from beginning otherwise
  455. return (lpsz == NULL) ? CString::FIND_FAILED : (int)(lpsz - m_pchData);
  456. }
  457. // find a sub-string (like strstr). Added function - RAB19991112.
  458. int CString::Find(LPCTSTR lpszSub, int nStart) const
  459. {
  460. // Suppress the following Assert from MFC - RAB19991112.
  461. //ASSERT(AfxIsValidString(lpszSub));
  462. int nLength = GetData()->nDataLength;
  463. if (nStart > nLength)
  464. return CString::FIND_FAILED;
  465. // find first matching substring
  466. LPTSTR lpsz = _tcsstr(m_pchData + nStart, lpszSub);
  467. // return -1 for not found, distance from beginning otherwise
  468. return (lpsz == NULL) ? CString::FIND_FAILED : (int)(lpsz - m_pchData);
  469. }
  470. /////////////////////////////////////////////////////////////////////////////
  471. // CString formatting
  472. #ifdef _MAC
  473. #define TCHAR_ARG int
  474. #define WCHAR_ARG unsigned
  475. #define CHAR_ARG int
  476. #else
  477. #define TCHAR_ARG TCHAR
  478. #define WCHAR_ARG WCHAR
  479. #define CHAR_ARG char
  480. #endif
  481. #if defined(_68K_) || defined(_X86_)
  482. #define DOUBLE_ARG _AFX_DOUBLE
  483. #else
  484. #define DOUBLE_ARG double
  485. #endif
  486. #define FORCE_ANSI 0x10000
  487. #define FORCE_UNICODE 0x20000
  488. void CString::FormatV(LPCTSTR lpszFormat, va_list argList)
  489. {
  490. // Suppress the following Assert from MFC - JM 1/15/99
  491. // ASSERT(AfxIsValidString(lpszFormat, FALSE));
  492. va_list argListSave = argList;
  493. // make a guess at the maximum length of the resulting string
  494. int nMaxLen = 0;
  495. for (LPCTSTR lpsz = lpszFormat; *lpsz != '\0'; lpsz = _tcsinc(lpsz))
  496. {
  497. // handle '%' character, but watch out for '%%'
  498. if (*lpsz != '%' || *(lpsz = _tcsinc(lpsz)) == '%')
  499. {
  500. nMaxLen += _tclen(lpsz);
  501. continue;
  502. }
  503. int nItemLen = 0;
  504. // handle '%' character with format
  505. int nWidth = 0;
  506. for (; *lpsz != '\0'; lpsz = _tcsinc(lpsz))
  507. {
  508. // check for valid flags
  509. if (*lpsz == '#')
  510. nMaxLen += 2; // for '0x'
  511. else if (*lpsz == '*')
  512. nWidth = va_arg(argList, int);
  513. else if (*lpsz == '-' || *lpsz == '+' || *lpsz == '0' ||
  514. *lpsz == ' ')
  515. ;
  516. else // hit non-flag character
  517. break;
  518. }
  519. // get width and skip it
  520. if (nWidth == 0)
  521. {
  522. // width indicated by
  523. nWidth = _ttoi(lpsz);
  524. for (; *lpsz != '\0' && _istdigit(*lpsz); lpsz = _tcsinc(lpsz))
  525. ;
  526. }
  527. ASSERT(nWidth >= 0);
  528. int nPrecision = 0;
  529. if (*lpsz == '.')
  530. {
  531. // skip past '.' separator (width.precision)
  532. lpsz = _tcsinc(lpsz);
  533. // get precision and skip it
  534. if (*lpsz == '*')
  535. {
  536. nPrecision = va_arg(argList, int);
  537. lpsz = _tcsinc(lpsz);
  538. }
  539. else
  540. {
  541. nPrecision = _ttoi(lpsz);
  542. for (; *lpsz != '\0' && _istdigit(*lpsz); lpsz = _tcsinc(lpsz))
  543. ;
  544. }
  545. ASSERT(nPrecision >= 0);
  546. }
  547. // should be on type modifier or specifier
  548. int nModifier = 0;
  549. switch (*lpsz)
  550. {
  551. // modifiers that affect size
  552. case 'h':
  553. nModifier = FORCE_ANSI;
  554. lpsz = _tcsinc(lpsz);
  555. break;
  556. case 'l':
  557. nModifier = FORCE_UNICODE;
  558. lpsz = _tcsinc(lpsz);
  559. break;
  560. // modifiers that do not affect size
  561. case 'F':
  562. case 'N':
  563. case 'L':
  564. lpsz = _tcsinc(lpsz);
  565. break;
  566. }
  567. // now should be on specifier
  568. switch (*lpsz | nModifier)
  569. {
  570. // single characters
  571. case 'c':
  572. case 'C':
  573. nItemLen = 2;
  574. va_arg(argList, TCHAR_ARG);
  575. break;
  576. case 'c'|FORCE_ANSI:
  577. case 'C'|FORCE_ANSI:
  578. nItemLen = 2;
  579. va_arg(argList, CHAR_ARG);
  580. break;
  581. case 'c'|FORCE_UNICODE:
  582. case 'C'|FORCE_UNICODE:
  583. nItemLen = 2;
  584. va_arg(argList, WCHAR_ARG);
  585. break;
  586. // strings
  587. case 's':
  588. {
  589. LPCTSTR pstrNextArg = va_arg(argList, LPCTSTR);
  590. if (pstrNextArg == NULL)
  591. nItemLen = 6; // "(null)"
  592. else
  593. {
  594. nItemLen = lstrlen(pstrNextArg);
  595. nItemLen = max(1, nItemLen);
  596. }
  597. break;
  598. }
  599. case 'S':
  600. {
  601. #ifndef _UNICODE
  602. LPWSTR pstrNextArg = va_arg(argList, LPWSTR);
  603. if (pstrNextArg == NULL)
  604. nItemLen = 6; // "(null)"
  605. else
  606. {
  607. nItemLen = wcslen(pstrNextArg);
  608. nItemLen = max(1, nItemLen);
  609. }
  610. #else
  611. LPCSTR pstrNextArg = va_arg(argList, LPCSTR);
  612. if (pstrNextArg == NULL)
  613. nItemLen = 6; // "(null)"
  614. else
  615. {
  616. nItemLen = lstrlenA(pstrNextArg);
  617. nItemLen = max(1, nItemLen);
  618. }
  619. #endif
  620. break;
  621. }
  622. case 's'|FORCE_ANSI:
  623. case 'S'|FORCE_ANSI:
  624. {
  625. LPCSTR pstrNextArg = va_arg(argList, LPCSTR);
  626. if (pstrNextArg == NULL)
  627. nItemLen = 6; // "(null)"
  628. else
  629. {
  630. nItemLen = lstrlenA(pstrNextArg);
  631. nItemLen = max(1, nItemLen);
  632. }
  633. break;
  634. }
  635. #ifndef _MAC
  636. case 's'|FORCE_UNICODE:
  637. case 'S'|FORCE_UNICODE:
  638. {
  639. LPWSTR pstrNextArg = va_arg(argList, LPWSTR);
  640. if (pstrNextArg == NULL)
  641. nItemLen = 6; // "(null)"
  642. else
  643. {
  644. nItemLen = wcslen(pstrNextArg);
  645. nItemLen = max(1, nItemLen);
  646. }
  647. break;
  648. }
  649. #endif
  650. }
  651. // adjust nItemLen for strings
  652. if (nItemLen != 0)
  653. {
  654. nItemLen = max(nItemLen, nWidth);
  655. if (nPrecision != 0)
  656. nItemLen = min(nItemLen, nPrecision);
  657. }
  658. else
  659. {
  660. switch (*lpsz)
  661. {
  662. // integers
  663. case 'd':
  664. case 'i':
  665. case 'u':
  666. case 'x':
  667. case 'X':
  668. case 'o':
  669. va_arg(argList, int);
  670. nItemLen = 32;
  671. nItemLen = max(nItemLen, nWidth+nPrecision);
  672. break;
  673. #if 0
  674. // We (Saltmine) are not currently supporting formatting of real numbers 1/15/99
  675. case 'e':
  676. case 'f':
  677. case 'g':
  678. case 'G':
  679. va_arg(argList, DOUBLE_ARG);
  680. nItemLen = 128;
  681. nItemLen = max(nItemLen, nWidth+nPrecision);
  682. break;
  683. #endif
  684. case 'p':
  685. va_arg(argList, void*);
  686. nItemLen = 32;
  687. nItemLen = max(nItemLen, nWidth+nPrecision);
  688. break;
  689. // no output
  690. case 'n':
  691. va_arg(argList, int*);
  692. break;
  693. default:
  694. ASSERT(FALSE); // unknown formatting option
  695. }
  696. }
  697. // adjust nMaxLen for output nItemLen
  698. nMaxLen += nItemLen;
  699. }
  700. GetBuffer(nMaxLen);
  701. // Got rid of MFC's VERIFY in next line - JM 1/15/99
  702. //VERIFY(_vstprintf(m_pchData, lpszFormat, argListSave) <= GetAllocLength());
  703. _vstprintf(m_pchData, lpszFormat, argListSave);
  704. ReleaseBuffer();
  705. va_end(argListSave);
  706. }
  707. // formatting (using wsprintf style formatting)
  708. void CString::Format(LPCTSTR lpszFormat, ...)
  709. {
  710. // Suppress the following Assert from MFC - JM 1/15/99
  711. // ASSERT(AfxIsValidString(lpszFormat, FALSE));
  712. va_list argList;
  713. va_start(argList, lpszFormat);
  714. FormatV(lpszFormat, argList);
  715. va_end(argList);
  716. }
  717. void CString::TrimRight()
  718. {
  719. CopyBeforeWrite();
  720. // find beginning of trailing spaces by starting at beginning (DBCS aware)
  721. LPTSTR lpsz = m_pchData;
  722. LPTSTR lpszLast = NULL;
  723. while (*lpsz != '\0')
  724. {
  725. if (_istspace(*lpsz))
  726. {
  727. if (lpszLast == NULL)
  728. lpszLast = lpsz;
  729. }
  730. else
  731. lpszLast = NULL;
  732. lpsz = _tcsinc(lpsz);
  733. }
  734. if (lpszLast != NULL)
  735. {
  736. // truncate at trailing space start
  737. *lpszLast = '\0';
  738. GetData()->nDataLength = static_cast<int>(lpszLast - m_pchData);
  739. }
  740. }
  741. void CString::TrimLeft()
  742. {
  743. CopyBeforeWrite();
  744. // find first non-space character
  745. LPCTSTR lpsz = m_pchData;
  746. while (_istspace(*lpsz))
  747. lpsz = _tcsinc(lpsz);
  748. // fix up data and length
  749. int nDataLength = GetData()->nDataLength - static_cast<int>(lpsz - m_pchData);
  750. memmove(m_pchData, lpsz, (nDataLength+1)*sizeof(TCHAR));
  751. GetData()->nDataLength = nDataLength;
  752. }
  753. BOOL CString::LoadString(UINT nID)
  754. {
  755. // try fixed buffer first (to avoid wasting space in the heap)
  756. TCHAR szTemp[256];
  757. int nLen = ::AfxLoadString(nID, szTemp, _countof(szTemp));
  758. if (_countof(szTemp) - nLen > CHAR_FUDGE)
  759. {
  760. *this = szTemp;
  761. return nLen > 0;
  762. }
  763. // try buffer size of 512, then larger size until entire string is retrieved
  764. int nSize = 256;
  765. do
  766. {
  767. nSize += 256;
  768. nLen = ::AfxLoadString(nID, GetBuffer(nSize-1), nSize);
  769. } while (nSize - nLen <= CHAR_FUDGE);
  770. ReleaseBuffer();
  771. return nLen > 0;
  772. }
  773. bool __stdcall operator ==(const CString& s1, const CString& s2)
  774. {
  775. return (s1.GetLength() == s2.GetLength() && ! _tcscmp((LPCTSTR)s1, (LPCTSTR)s2) );
  776. }
  777. bool __stdcall operator ==(const CString& s1, LPCTSTR s2)
  778. {
  779. return (! _tcscmp((LPCTSTR)s1, s2) );
  780. }
  781. bool __stdcall operator ==(LPCTSTR s1, const CString& s2)
  782. {
  783. return (! _tcscmp(s1, (LPCTSTR)s2) );
  784. }
  785. bool __stdcall operator !=(const CString& s1, const CString& s2)
  786. {
  787. return (s1.GetLength() != s2.GetLength() || _tcscmp((LPCTSTR)s1, (LPCTSTR)s2) );
  788. }
  789. bool __stdcall operator !=(const CString& s1, LPCTSTR s2)
  790. {
  791. return (_tcscmp((LPCTSTR)s1, s2) ) ? true : false;
  792. }
  793. bool __stdcall operator !=(LPCTSTR s1, const CString& s2)
  794. {
  795. return (_tcscmp(s1, (LPCTSTR)s2) ) ? true : false;
  796. }
  797. bool __stdcall operator < (const CString& s1, const CString& s2)
  798. {
  799. return (_tcscmp((LPCTSTR)s1, (LPCTSTR)s2) <0 );
  800. }
  801. bool __stdcall operator < (const CString& s1, LPCTSTR s2)
  802. {
  803. return (_tcscmp((LPCTSTR)s1, s2) <0 );
  804. }
  805. bool __stdcall operator < (LPCTSTR s1, const CString& s2)
  806. {
  807. return (_tcscmp(s1, (LPCTSTR)s2) <0 );
  808. }
  809. CString operator+(LPCTSTR lpsz, const CString& string)
  810. {
  811. return CString(lpsz) + string;
  812. }
  813. void CString::Init()
  814. {
  815. m_pchData = afxDataNil->data();
  816. }
  817. CStringData* CString::GetData() const
  818. {
  819. if(m_pchData != NULL)
  820. return ((CStringData*)m_pchData)-1;
  821. return afxDataNil;
  822. }