Leaked source code of windows server 2003
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.

760 lines
18 KiB

  1. // This is a part of the Microsoft Foundation Classes C++ library.
  2. // Copyright (C) 1992 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 Microsoft
  7. // QuickHelp and/or WinHelp documentation provided with the library.
  8. // See these sources for detailed information regarding the
  9. // Microsoft Foundation Classes product.
  10. #include <stdio.h>
  11. #include <objbase.h>
  12. #include <basetyps.h>
  13. #include "dbg.h"
  14. #include "cstr.h"
  15. /////////////////////////////////////////////////////////////////////////////
  16. // static class data, special inlines
  17. // For an empty string, m_???Data will point here
  18. // (note: avoids a lot of NULL pointer tests when we call standard
  19. // C runtime libraries
  20. TCHAR strChNil = '\0';
  21. // for creating empty key strings
  22. const CStr strEmptyString;
  23. void CStr::Init()
  24. {
  25. m_nDataLength = m_nAllocLength = 0;
  26. m_pchData = (LPTSTR)&strChNil;
  27. }
  28. // declared static
  29. void CStr::SafeDelete(LPTSTR lpch)
  30. {
  31. // ISSUE-2002/03/29-JonN also check for NULL? Or is "delete[] NULL" OK?
  32. if (lpch != (LPTSTR)&strChNil)
  33. delete[] lpch;
  34. }
  35. //////////////////////////////////////////////////////////////////////////////
  36. // Construction/Destruction
  37. CStr::CStr()
  38. {
  39. Init();
  40. }
  41. CStr::CStr(const CStr& stringSrc)
  42. {
  43. // if constructing a String from another String, we make a copy of the
  44. // original string data to enforce value semantics (i.e. each string
  45. // gets a copy of its own
  46. stringSrc.AllocCopy(*this, stringSrc.m_nDataLength, 0, 0);
  47. }
  48. void CStr::AllocBuffer(size_t nLen)
  49. // always allocate one extra character for '\0' termination
  50. // assumes [optimistically] that data length will equal allocation length
  51. {
  52. if (nLen == 0)
  53. {
  54. Init();
  55. }
  56. else
  57. {
  58. m_pchData = new TCHAR[nLen+1]; //REVIEW may throw an exception
  59. m_pchData[nLen] = '\0';
  60. m_nDataLength = nLen;
  61. m_nAllocLength = nLen;
  62. }
  63. }
  64. void CStr::Empty()
  65. {
  66. SafeDelete(m_pchData);
  67. Init();
  68. ASSERT(m_nDataLength == 0);
  69. ASSERT(m_nAllocLength == 0);
  70. }
  71. CStr::~CStr()
  72. // free any attached data
  73. {
  74. SafeDelete(m_pchData);
  75. }
  76. //////////////////////////////////////////////////////////////////////////////
  77. // Helpers for the rest of the implementation
  78. static inline size_t SafeStrlen(LPCTSTR lpsz)
  79. {
  80. ASSERT(lpsz == NULL || IsValidString(lpsz, FALSE));
  81. return (lpsz == NULL) ? 0 : lstrlen(lpsz);
  82. }
  83. void CStr::AllocCopy(CStr& dest, size_t nCopyLen, size_t nCopyIndex,
  84. size_t nExtraLen) const
  85. {
  86. // will clone the data attached to this string
  87. // allocating 'nExtraLen' characters
  88. // Places results in uninitialized string 'dest'
  89. // Will copy the part or all of original data to start of new string
  90. size_t nNewLen = nCopyLen + nExtraLen;
  91. if (nNewLen == 0)
  92. {
  93. dest.Init();
  94. }
  95. else
  96. {
  97. dest.AllocBuffer(nNewLen);
  98. // ISSUE-2002/03/29-JonN should check against m_nDataLength
  99. memcpy(dest.m_pchData, &m_pchData[nCopyIndex], nCopyLen*sizeof(TCHAR));
  100. }
  101. }
  102. //////////////////////////////////////////////////////////////////////////////
  103. // More sophisticated construction
  104. CStr::CStr(LPCTSTR lpsz)
  105. {
  106. size_t nLen;
  107. if ((nLen = SafeStrlen(lpsz)) == 0)
  108. Init();
  109. else
  110. {
  111. AllocBuffer(nLen);
  112. memcpy(m_pchData, lpsz, nLen*sizeof(TCHAR));
  113. }
  114. }
  115. /////////////////////////////////////////////////////////////////////////////
  116. // Special conversion constructors
  117. #ifdef UNICODE
  118. CStr::CStr(LPCSTR lpsz)
  119. {
  120. size_t nSrcLen = lpsz != NULL ? lstrlenA(lpsz) : 0;
  121. if (nSrcLen == 0)
  122. Init();
  123. else
  124. {
  125. AllocBuffer(nSrcLen);
  126. mmc_mbstowcsz(m_pchData, lpsz, nSrcLen+1);
  127. }
  128. }
  129. #else //UNICODE
  130. CStr::CStr(LPCWSTR lpsz)
  131. {
  132. size_t nSrcLen = lpsz != NULL ? wcslen(lpsz) : 0;
  133. if (nSrcLen == 0)
  134. Init();
  135. else
  136. {
  137. AllocBuffer(nSrcLen*2);
  138. mmc_wcstombsz(m_pchData, lpsz, (nSrcLen*2)+1);
  139. ReleaseBuffer();
  140. }
  141. }
  142. #endif //!UNICODE
  143. //////////////////////////////////////////////////////////////////////////////
  144. // Assignment operators
  145. // All assign a new value to the string
  146. // (a) first see if the buffer is big enough
  147. // (b) if enough room, copy on top of old buffer, set size and type
  148. // (c) otherwise free old string data, and create a new one
  149. //
  150. // All routines return the new string (but as a 'const CStr&' so that
  151. // assigning it again will cause a copy, eg: s1 = s2 = "hi there".
  152. //
  153. void CStr::AssignCopy(size_t nSrcLen, LPCTSTR lpszSrcData)
  154. {
  155. // check if it will fit
  156. if (nSrcLen > m_nAllocLength)
  157. {
  158. // it won't fit, allocate another one
  159. Empty();
  160. AllocBuffer(nSrcLen);
  161. }
  162. if (nSrcLen != 0)
  163. memcpy(m_pchData, lpszSrcData, nSrcLen*sizeof(TCHAR));
  164. m_nDataLength = nSrcLen;
  165. m_pchData[nSrcLen] = '\0';
  166. }
  167. const CStr& CStr::operator=(const CStr& stringSrc)
  168. {
  169. AssignCopy(stringSrc.m_nDataLength, stringSrc.m_pchData);
  170. return *this;
  171. }
  172. const CStr& CStr::operator=(LPCTSTR lpsz)
  173. {
  174. ASSERT(lpsz == NULL || IsValidString(lpsz, FALSE));
  175. AssignCopy(SafeStrlen(lpsz), lpsz);
  176. return *this;
  177. }
  178. /////////////////////////////////////////////////////////////////////////////
  179. // Special conversion assignment
  180. #ifdef UNICODE
  181. const CStr& CStr::operator=(LPCSTR lpsz)
  182. {
  183. size_t nSrcLen = lpsz != NULL ? lstrlenA(lpsz) : 0;
  184. // check if it will fit
  185. if (nSrcLen > m_nAllocLength)
  186. {
  187. // it won't fit, allocate another one
  188. Empty();
  189. AllocBuffer(nSrcLen);
  190. }
  191. if (nSrcLen != 0)
  192. mmc_mbstowcsz(m_pchData, lpsz, nSrcLen+1);
  193. // ISSUE-2002/03/29-JonN "m_nDataLength = nSrcLen" seems wrong
  194. m_nDataLength = nSrcLen;
  195. m_pchData[nSrcLen] = '\0';
  196. return *this;
  197. }
  198. #else //!UNICODE
  199. const CStr& CStr::operator=(LPCWSTR lpsz)
  200. {
  201. size_t nSrcLen = lpsz != NULL ? wcslen(lpsz) : 0;
  202. nSrcLen *= 2;
  203. // check if it will fit
  204. if (nSrcLen > m_nAllocLength)
  205. {
  206. // it won't fit, allocate another one
  207. Empty();
  208. AllocBuffer(nSrcLen);
  209. }
  210. if (nSrcLen != 0)
  211. {
  212. mmc_wcstombsz(m_pchData, lpsz, nSrcLen+1);
  213. ReleaseBuffer();
  214. }
  215. return *this;
  216. }
  217. #endif //!UNICODE
  218. //////////////////////////////////////////////////////////////////////////////
  219. // concatenation
  220. // NOTE: "operator+" is done as friend functions for simplicity
  221. // There are three variants:
  222. // String + String
  223. // and for ? = TCHAR, LPCTSTR
  224. // String + ?
  225. // ? + String
  226. void CStr::ConcatCopy(size_t nSrc1Len, LPCTSTR lpszSrc1Data,
  227. size_t nSrc2Len, LPCTSTR lpszSrc2Data)
  228. {
  229. // -- master concatenation routine
  230. // Concatenate two sources
  231. // -- assume that 'this' is a new String object
  232. size_t nNewLen = nSrc1Len + nSrc2Len;
  233. AllocBuffer(nNewLen);
  234. memcpy(m_pchData, lpszSrc1Data, nSrc1Len*sizeof(TCHAR));
  235. memcpy(&m_pchData[nSrc1Len], lpszSrc2Data, nSrc2Len*sizeof(TCHAR));
  236. }
  237. CStr STRAPI operator+(const CStr& string1, const CStr& string2)
  238. {
  239. CStr s;
  240. s.ConcatCopy(string1.m_nDataLength, string1.m_pchData,
  241. string2.m_nDataLength, string2.m_pchData);
  242. return s;
  243. }
  244. CStr STRAPI operator+(const CStr& string, LPCTSTR lpsz)
  245. {
  246. ASSERT(lpsz == NULL || IsValidString(lpsz, FALSE));
  247. CStr s;
  248. s.ConcatCopy(string.m_nDataLength, string.m_pchData, SafeStrlen(lpsz), lpsz);
  249. return s;
  250. }
  251. CStr STRAPI operator+(LPCTSTR lpsz, const CStr& string)
  252. {
  253. ASSERT(lpsz == NULL || IsValidString(lpsz, FALSE));
  254. CStr s;
  255. s.ConcatCopy(SafeStrlen(lpsz), lpsz, string.m_nDataLength, string.m_pchData);
  256. return s;
  257. }
  258. //////////////////////////////////////////////////////////////////////////////
  259. // concatenate in place
  260. void CStr::ConcatInPlace(size_t nSrcLen, LPCTSTR lpszSrcData)
  261. {
  262. // -- the main routine for += operators
  263. // if the buffer is too small, or we have a width mis-match, just
  264. // allocate a new buffer (slow but sure)
  265. if (m_nDataLength + nSrcLen > m_nAllocLength)
  266. {
  267. // we have to grow the buffer, use the Concat in place routine
  268. LPTSTR lpszOldData = m_pchData;
  269. ConcatCopy(m_nDataLength, lpszOldData, nSrcLen, lpszSrcData);
  270. ASSERT(lpszOldData != NULL);
  271. SafeDelete(lpszOldData);
  272. }
  273. else
  274. {
  275. // fast concatenation when buffer big enough
  276. memcpy(&m_pchData[m_nDataLength], lpszSrcData, nSrcLen*sizeof(TCHAR));
  277. m_nDataLength += nSrcLen;
  278. }
  279. ASSERT(m_nDataLength <= m_nAllocLength);
  280. m_pchData[m_nDataLength] = '\0';
  281. }
  282. const CStr& CStr::operator+=(LPCTSTR lpsz)
  283. {
  284. ASSERT(lpsz == NULL || IsValidString(lpsz, FALSE));
  285. ConcatInPlace(SafeStrlen(lpsz), lpsz);
  286. return *this;
  287. }
  288. const CStr& CStr::operator+=(TCHAR ch)
  289. {
  290. ConcatInPlace(1, &ch);
  291. return *this;
  292. }
  293. const CStr& CStr::operator+=(const CStr& string)
  294. {
  295. ConcatInPlace(string.m_nDataLength, string.m_pchData);
  296. return *this;
  297. }
  298. ///////////////////////////////////////////////////////////////////////////////
  299. // Advanced direct buffer access
  300. LPTSTR CStr::GetBuffer(size_t nMinBufLength)
  301. {
  302. if (nMinBufLength > m_nAllocLength)
  303. {
  304. // we have to grow the buffer
  305. LPTSTR lpszOldData = m_pchData;
  306. size_t nOldLen = m_nDataLength; // AllocBuffer will tromp it
  307. AllocBuffer(nMinBufLength);
  308. memcpy(m_pchData, lpszOldData, nOldLen*sizeof(TCHAR));
  309. m_nDataLength = nOldLen;
  310. m_pchData[m_nDataLength] = '\0';
  311. SafeDelete(lpszOldData);
  312. }
  313. // return a pointer to the character storage for this string
  314. ASSERT(m_pchData != NULL);
  315. return m_pchData;
  316. }
  317. void CStr::ReleaseBuffer(size_t nNewLength)
  318. {
  319. if (nNewLength == -1)
  320. nNewLength = lstrlen(m_pchData); // zero terminated
  321. // ISSUE-2002/03/29-JonN handle this case
  322. ASSERT(nNewLength <= m_nAllocLength);
  323. m_nDataLength = nNewLength;
  324. m_pchData[m_nDataLength] = '\0';
  325. }
  326. LPTSTR CStr::GetBufferSetLength(int nNewLength)
  327. {
  328. // ISSUE-2002/03/29-JonN handle this case
  329. ASSERT(nNewLength >= 0);
  330. GetBuffer(nNewLength);
  331. m_nDataLength = nNewLength;
  332. m_pchData[m_nDataLength] = '\0';
  333. return m_pchData;
  334. }
  335. void CStr::FreeExtra()
  336. {
  337. ASSERT(m_nDataLength <= m_nAllocLength);
  338. if (m_nDataLength != m_nAllocLength)
  339. {
  340. LPTSTR lpszOldData = m_pchData;
  341. AllocBuffer(m_nDataLength);
  342. memcpy(m_pchData, lpszOldData, m_nDataLength*sizeof(TCHAR));
  343. ASSERT(m_pchData[m_nDataLength] == '\0');
  344. SafeDelete(lpszOldData);
  345. }
  346. ASSERT(m_pchData != NULL);
  347. }
  348. ///////////////////////////////////////////////////////////////////////////////
  349. // Commonly used routines (rarely used routines in STREX.CPP)
  350. int CStr::Find(TCHAR ch) const
  351. {
  352. // find first single character
  353. LPTSTR lpsz = _tcschr(m_pchData, ch);
  354. // return -1 if not found and index otherwise
  355. return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData);
  356. }
  357. int CStr::FindOneOf(LPCTSTR lpszCharSet) const
  358. {
  359. ASSERT(IsValidString(lpszCharSet, FALSE));
  360. LPTSTR lpsz = _tcspbrk(m_pchData, lpszCharSet);
  361. return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData);
  362. }
  363. ///////////////////////////////////////////////////////////////////////////////
  364. // String conversion helpers (these use the current system locale)
  365. size_t mmc_wcstombsz(char* mbstr, const wchar_t* wcstr, size_t count)
  366. {
  367. if (count == 0 && mbstr != NULL)
  368. return 0;
  369. size_t result = ::WideCharToMultiByte(CP_ACP, 0, wcstr, -1,
  370. mbstr, (int) count, NULL, NULL);
  371. ASSERT(mbstr == NULL || result <= count);
  372. if (result > 0)
  373. mbstr[result-1] = 0;
  374. return result;
  375. }
  376. size_t mmc_mbstowcsz(wchar_t* wcstr, const char* mbstr, size_t count)
  377. {
  378. if (count == 0 && wcstr != NULL)
  379. return 0;
  380. size_t result = ::MultiByteToWideChar(CP_ACP, 0, mbstr, -1,
  381. wcstr, (int) count);
  382. ASSERT(wcstr == NULL || result <= count);
  383. if (result > 0)
  384. wcstr[result-1] = 0;
  385. return result;
  386. }
  387. /////////////////////////////////////////////////////////////////////////////
  388. // Windows extensions to strings
  389. BOOL CStr::LoadString(HINSTANCE hInst, UINT nID)
  390. {
  391. ASSERT(nID != 0); // 0 is an illegal string ID
  392. // Note: resource strings limited to 511 characters
  393. TCHAR szBuffer[512];
  394. UINT nSize = StrLoadString(hInst, nID, szBuffer);
  395. AssignCopy(nSize, szBuffer);
  396. return nSize > 0;
  397. }
  398. // ISSUE-2002/04/01-JonN should take cch parameter
  399. int STRAPI StrLoadString(HINSTANCE hInst, UINT nID, LPTSTR lpszBuf)
  400. {
  401. // ISSUE-2002/04/01-JonN should test lpszBuf==NULL in retail
  402. ASSERT(IsValidAddressz(lpszBuf, 512)); // must be big enough for 512 bytes
  403. #ifdef DBG
  404. // LoadString without annoying warning from the Debug kernel if the
  405. // segment containing the string is not present
  406. if (::FindResource(hInst, MAKEINTRESOURCE((nID>>4)+1), RT_STRING) == NULL)
  407. {
  408. lpszBuf[0] = '\0';
  409. return 0; // not found
  410. }
  411. #endif //DBG
  412. int nLen = ::LoadString(hInst, nID, lpszBuf, 511);
  413. if (nLen == 0)
  414. lpszBuf[0] = '\0';
  415. return nLen;
  416. }
  417. BOOL STRAPI IsValidAddressz(const void* lp, UINT nBytes, BOOL bReadWrite)
  418. {
  419. // simple version using Win-32 APIs for pointer validation.
  420. return (lp != NULL && !IsBadReadPtr(lp, nBytes) &&
  421. (!bReadWrite || !IsBadWritePtr((LPVOID)lp, nBytes)));
  422. }
  423. BOOL STRAPI IsValidString(LPCSTR lpsz, UINT_PTR nLength)
  424. {
  425. if (lpsz == NULL)
  426. return FALSE;
  427. return ::IsBadStringPtrA(lpsz, nLength) == 0;
  428. }
  429. BOOL STRAPI IsValidString(LPCWSTR lpsz, UINT_PTR nLength)
  430. {
  431. if (lpsz == NULL)
  432. return FALSE;
  433. return ::IsBadStringPtrW(lpsz, nLength) == 0;
  434. }
  435. #ifdef OLE_AUTOMATION
  436. #ifdef UNICODE
  437. BSTR CStr::AllocSysString()
  438. {
  439. BSTR bstr = ::SysAllocStringLen(m_pchData, m_nDataLength);
  440. if (bstr == NULL)
  441. ;//REVIEW AfxThrowMemoryException();
  442. return bstr;
  443. }
  444. BSTR CStr::SetSysString(BSTR* pbstr)
  445. {
  446. ASSERT(IsValidAddressz(pbstr, sizeof(BSTR)));
  447. if (!::SysReAllocStringLen(pbstr, m_pchData, m_nDataLength))
  448. ; //REVIEW AfxThrowMemoryException();
  449. ASSERT(*pbstr != NULL);
  450. return *pbstr;
  451. }
  452. #endif
  453. #endif // #ifdef OLE_AUTOMATION
  454. ///////////////////////////////////////////////////////////////////////////////
  455. // Orginally from StrEx.cpp
  456. CStr::CStr(TCHAR ch, int nLength)
  457. {
  458. #ifndef UNICODE
  459. // ISSUE-2002/04/01-JonN This is wrong, we just shouldn't support this
  460. ASSERT(!IsDBCSLeadByte(ch)); // can't create a lead byte string
  461. #endif
  462. if (nLength < 1)
  463. {
  464. // return empty string if invalid repeat count
  465. Init();
  466. }
  467. else
  468. {
  469. AllocBuffer(nLength);
  470. #ifdef UNICODE
  471. for (int i = 0; i < nLength; i++)
  472. m_pchData[i] = ch;
  473. #else
  474. memset(m_pchData, ch, nLength);
  475. #endif
  476. }
  477. }
  478. CStr::CStr(LPCTSTR lpch, int nLength)
  479. {
  480. if (nLength == 0)
  481. Init();
  482. else
  483. {
  484. ASSERT(IsValidAddressz(lpch, nLength, FALSE));
  485. AllocBuffer(nLength);
  486. memcpy(m_pchData, lpch, nLength*sizeof(TCHAR));
  487. }
  488. }
  489. //////////////////////////////////////////////////////////////////////////////
  490. // Assignment operators
  491. const CStr& CStr::operator=(TCHAR ch)
  492. {
  493. #ifndef UNICODE
  494. ASSERT(!IsDBCSLeadByte(ch)); // can't set single lead byte
  495. #endif
  496. AssignCopy(1, &ch);
  497. return *this;
  498. }
  499. //////////////////////////////////////////////////////////////////////////////
  500. // less common string expressions
  501. CStr STRAPI operator+(const CStr& string1, TCHAR ch)
  502. {
  503. CStr s;
  504. s.ConcatCopy(string1.m_nDataLength, string1.m_pchData, 1, &ch);
  505. return s;
  506. }
  507. CStr STRAPI operator+(TCHAR ch, const CStr& string)
  508. {
  509. CStr s;
  510. s.ConcatCopy(1, &ch, string.m_nDataLength, string.m_pchData);
  511. return s;
  512. }
  513. //////////////////////////////////////////////////////////////////////////////
  514. // Very simple sub-string extraction
  515. CStr CStr::Mid(size_t nFirst) const
  516. {
  517. return Mid(nFirst, m_nDataLength - nFirst);
  518. }
  519. CStr CStr::Mid(size_t nFirst, size_t nCount) const
  520. {
  521. // out-of-bounds requests return sensible things
  522. if (nFirst + nCount > m_nDataLength)
  523. nCount = m_nDataLength - nFirst;
  524. if (nFirst > m_nDataLength)
  525. nCount = 0;
  526. CStr dest;
  527. AllocCopy(dest, nCount, nFirst, 0);
  528. return dest;
  529. }
  530. CStr CStr::Right(size_t nCount) const
  531. {
  532. if (nCount > m_nDataLength)
  533. nCount = m_nDataLength;
  534. CStr dest;
  535. AllocCopy(dest, nCount, m_nDataLength-nCount, 0);
  536. return dest;
  537. }
  538. CStr CStr::Left(size_t nCount) const
  539. {
  540. if (nCount > m_nDataLength)
  541. nCount = m_nDataLength;
  542. CStr dest;
  543. AllocCopy(dest, nCount, 0, 0);
  544. return dest;
  545. }
  546. // strspn equivalent
  547. CStr CStr::SpanIncluding(LPCTSTR lpszCharSet) const
  548. {
  549. ASSERT(IsValidString(lpszCharSet, FALSE));
  550. return Left(_tcsspn(m_pchData, lpszCharSet));
  551. }
  552. // strcspn equivalent
  553. CStr CStr::SpanExcluding(LPCTSTR lpszCharSet) const
  554. {
  555. ASSERT(IsValidString(lpszCharSet, FALSE));
  556. return Left(_tcscspn(m_pchData, lpszCharSet));
  557. }
  558. //////////////////////////////////////////////////////////////////////////////
  559. // Finding
  560. int CStr::ReverseFind(TCHAR ch) const
  561. {
  562. // find last single character
  563. LPTSTR lpsz = _tcsrchr(m_pchData, ch);
  564. // return -1 if not found, distance from beginning otherwise
  565. return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData);
  566. }
  567. // find a sub-string (like strstr)
  568. int CStr::Find(LPCTSTR lpszSub) const
  569. {
  570. ASSERT(IsValidString(lpszSub, FALSE));
  571. // find first matching substring
  572. LPTSTR lpsz = _tcsstr(m_pchData, lpszSub);
  573. // return -1 for not found, distance from beginning otherwise
  574. return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData);
  575. }
  576. /////////////////////////////////////////////////////////////////////////////
  577. // String formatting
  578. #define FORCE_ANSI 0x10000
  579. #define FORCE_UNICODE 0x20000
  580. void CStr::TrimRight()
  581. {
  582. // find beginning of trailing spaces by starting at beginning (DBCS aware)
  583. LPTSTR lpsz = m_pchData;
  584. LPTSTR lpszLast = NULL;
  585. while (*lpsz != '\0')
  586. {
  587. if (_istspace(*lpsz))
  588. {
  589. if (lpszLast == NULL)
  590. lpszLast = lpsz;
  591. }
  592. else
  593. lpszLast = NULL;
  594. lpsz = _tcsinc(lpsz);
  595. }
  596. if (lpszLast != NULL)
  597. {
  598. // truncate at trailing space start
  599. *lpszLast = '\0';
  600. m_nDataLength = (int)(lpszLast - m_pchData);
  601. }
  602. }
  603. void CStr::TrimLeft()
  604. {
  605. // find first non-space character
  606. LPCTSTR lpsz = m_pchData;
  607. while (_istspace(*lpsz))
  608. lpsz = _tcsinc(lpsz);
  609. // fix up data and length
  610. size_t nDataLength = m_nDataLength - (int)(lpsz - m_pchData);
  611. memmove(m_pchData, lpsz, (nDataLength+1)*sizeof(TCHAR));
  612. m_nDataLength = nDataLength;
  613. }
  614. #if 0
  615. ///////////////////////////////////////////////////////////////////////////////
  616. // String support for template collections
  617. template<>
  618. void STRAPI ConstructElements(CStr* pElements, int nCount)
  619. {
  620. ASSERT(IsValidAddressz(pElements, nCount * sizeof(CStr)));
  621. for (; nCount--; ++pElements)
  622. memcpy(pElements, &strEmptyString, sizeof(*pElements));
  623. }
  624. template<>
  625. void STRAPI DestructElements(CStr* pElements, int nCount)
  626. {
  627. ASSERT(IsValidAddressz(pElements, nCount * sizeof(CStr)));
  628. for (; nCount--; ++pElements)
  629. pElements->Empty();
  630. }
  631. template<>
  632. UINT STRAPI HashKey(LPCTSTR key)
  633. {
  634. UINT nHash = 0;
  635. while (*key)
  636. nHash = (nHash<<5) + nHash + *key++;
  637. return nHash;
  638. }
  639. #endif