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.

949 lines
17 KiB

  1. ///////////////////////////////////////////////////////////////////////////////
  2. /* File: strclass.cpp
  3. Description: Typical class to handle strings.
  4. Revision History:
  5. Date Description Programmer
  6. -------- --------------------------------------------------- ----------
  7. 07/01/97 Initial creation. BrianAu
  8. */
  9. ///////////////////////////////////////////////////////////////////////////////
  10. #include "pch.h"
  11. #pragma hdrstop
  12. #include "strclass.h"
  13. #ifdef StrCpy
  14. # undef StrCpy
  15. #endif
  16. #ifdef StrCpyN
  17. # undef StrCpyN
  18. #endif
  19. #ifdef StrLen
  20. # undef StrLen
  21. #endif
  22. #ifdef UNICODE
  23. # define StrCpy StrCpyW
  24. # define StrCpyN StrCpyNW
  25. # define StrLen StrLenW
  26. #else
  27. # define StrCpy StrCpyA
  28. # define StrCpyN StrCpyNA
  29. # define StrLen StrLenA
  30. #endif // UNICODE
  31. const INT MAX_RESOURCE_STR_LEN = 4097;
  32. //
  33. // Disable "'operator ->' is not a UDT or reference to a UDT" warning.
  34. // This is caused when we create an autoptr to a non-UDT. It's meaningless
  35. // since there's no reason to call operator-> on a non-UDT autoptr.
  36. //
  37. #pragma warning (disable : 4284)
  38. CString::CString(
  39. VOID
  40. ) : m_pValue(new StringValue())
  41. {
  42. }
  43. CString::CString(
  44. INT cch
  45. ) : m_pValue(new StringValue(cch))
  46. {
  47. }
  48. CString::CString(
  49. LPCSTR pszA
  50. ) : m_pValue(new StringValue(pszA))
  51. {
  52. }
  53. CString::CString(
  54. LPCWSTR pszW
  55. ) : m_pValue(new StringValue(pszW))
  56. {
  57. }
  58. CString::CString(
  59. const CString& rhs
  60. ) : m_pValue(rhs.m_pValue)
  61. {
  62. InterlockedIncrement(&(m_pValue->m_cRef));
  63. }
  64. CString::CString(
  65. HINSTANCE hInst,
  66. INT idMsg,
  67. ...
  68. ) : m_pValue(NULL)
  69. {
  70. LPTSTR pszMsg = NULL;
  71. va_list args;
  72. va_start(args, idMsg);
  73. Format(hInst, idMsg, &args);
  74. va_end(args);
  75. }
  76. CString::~CString(
  77. VOID
  78. )
  79. {
  80. if (NULL != m_pValue)
  81. {
  82. if (0 == InterlockedDecrement(&(m_pValue->m_cRef)))
  83. delete m_pValue;
  84. }
  85. }
  86. //
  87. // Length of string, excluding nul terminator.
  88. //
  89. INT
  90. CString::Length(
  91. VOID
  92. ) const
  93. {
  94. return m_pValue->Length();
  95. }
  96. VOID
  97. CString::Empty(
  98. VOID
  99. )
  100. {
  101. if (0 == InterlockedDecrement(&(m_pValue->m_cRef)))
  102. {
  103. delete m_pValue;
  104. m_pValue = NULL;
  105. }
  106. m_pValue = new StringValue();
  107. }
  108. BOOL
  109. CString::IsEmpty(
  110. VOID
  111. ) const
  112. {
  113. return (NULL != m_pValue && 0 == m_pValue->Length());
  114. }
  115. CString&
  116. CString::operator = (
  117. const CString& rhs
  118. )
  119. {
  120. if (m_pValue != rhs.m_pValue) // Chk for assignment to *this.
  121. {
  122. if (0 == InterlockedDecrement(&(m_pValue->m_cRef)))
  123. delete m_pValue;
  124. m_pValue = rhs.m_pValue;
  125. InterlockedIncrement(&(m_pValue->m_cRef));
  126. }
  127. return *this;
  128. }
  129. CString&
  130. CString::operator = (
  131. LPCWSTR rhsW
  132. )
  133. {
  134. if (0 == InterlockedDecrement(&(m_pValue->m_cRef)))
  135. {
  136. delete m_pValue;
  137. m_pValue = NULL;
  138. }
  139. m_pValue = new StringValue(rhsW);
  140. return *this;
  141. }
  142. CString&
  143. CString::operator = (
  144. LPCSTR rhsA
  145. )
  146. {
  147. if (0 == InterlockedDecrement(&(m_pValue->m_cRef)))
  148. {
  149. delete m_pValue;
  150. m_pValue = NULL;
  151. }
  152. m_pValue = new StringValue(rhsA);
  153. return *this;
  154. }
  155. CString
  156. CString::operator + (
  157. const CString& rhs
  158. ) const
  159. {
  160. CString strNew;
  161. LPTSTR pszTemp = NULL;
  162. try
  163. {
  164. pszTemp = StringValue::Concat(m_pValue, rhs.m_pValue);
  165. strNew = pszTemp;
  166. }
  167. catch(...)
  168. {
  169. delete[] pszTemp;
  170. throw;
  171. }
  172. delete[] pszTemp;
  173. return strNew;
  174. }
  175. CString&
  176. CString::operator += (
  177. const CString& rhs
  178. )
  179. {
  180. LPTSTR pszTemp = NULL;
  181. try
  182. {
  183. pszTemp = StringValue::Concat(m_pValue, rhs.m_pValue);
  184. *this = pszTemp;
  185. }
  186. catch(...)
  187. {
  188. delete[] pszTemp;
  189. throw;
  190. }
  191. delete[] pszTemp;
  192. return *this;
  193. }
  194. BOOL
  195. CString::operator == (
  196. const CString& rhs
  197. ) const
  198. {
  199. return (0 == lstrcmp(m_pValue->m_psz, rhs.m_pValue->m_psz));
  200. }
  201. INT
  202. CString::Compare(
  203. LPCWSTR rhsW
  204. ) const
  205. {
  206. StringValue Value(rhsW);
  207. return lstrcmp(m_pValue->m_psz, Value.m_psz);
  208. }
  209. INT
  210. CString::Compare(
  211. LPCSTR rhsA
  212. ) const
  213. {
  214. StringValue Value(rhsA);
  215. return lstrcmp(m_pValue->m_psz, Value.m_psz);
  216. }
  217. INT
  218. CString::CompareNoCase(
  219. LPCWSTR rhsW
  220. ) const
  221. {
  222. StringValue Value(rhsW);
  223. return lstrcmpi(m_pValue->m_psz, Value.m_psz);
  224. }
  225. INT
  226. CString::CompareNoCase(
  227. LPCSTR rhsA
  228. ) const
  229. {
  230. StringValue Value(rhsA);
  231. return lstrcmpi(m_pValue->m_psz, Value.m_psz);
  232. }
  233. BOOL
  234. CString::operator < (
  235. const CString& rhs
  236. ) const
  237. {
  238. return (0 > lstrcmp(m_pValue->m_psz, rhs.m_pValue->m_psz));
  239. }
  240. TCHAR
  241. CString::operator[](
  242. INT index
  243. ) const
  244. {
  245. if (!ValidIndex(index))
  246. throw CMemoryException(CMemoryException::index);
  247. return m_pValue->m_psz[index];
  248. }
  249. TCHAR&
  250. CString::operator[](
  251. INT index
  252. )
  253. {
  254. if (!ValidIndex(index))
  255. throw CMemoryException(CMemoryException::index);
  256. CopyOnWrite();
  257. return m_pValue->m_psz[index];
  258. }
  259. INT
  260. CString::First(
  261. TCHAR ch
  262. ) const
  263. {
  264. LPCTSTR psz = m_pValue->m_psz;
  265. LPCTSTR pszLast = psz;
  266. INT i = 0;
  267. while(psz && *psz)
  268. {
  269. if (ch == *psz)
  270. return i;
  271. psz = CharNext(psz);
  272. i += (INT)(psz - pszLast);
  273. pszLast = psz;
  274. }
  275. return -1;
  276. }
  277. INT
  278. CString::Last(
  279. TCHAR ch
  280. ) const
  281. {
  282. INT iLast = -1;
  283. INT i = 0;
  284. LPCTSTR psz = m_pValue->m_psz;
  285. LPCTSTR pszPrev = psz;
  286. while(psz && *psz)
  287. {
  288. if (ch == *psz)
  289. iLast = i;
  290. psz = CharNext(psz);
  291. i += (INT)(psz - pszPrev);
  292. pszPrev = psz;
  293. }
  294. return iLast;
  295. }
  296. CString
  297. CString::SubString(
  298. INT iFirst,
  299. INT cch
  300. )
  301. {
  302. if (!ValidIndex(iFirst))
  303. throw CMemoryException(CMemoryException::index);
  304. INT cchToEnd = Length() - iFirst;
  305. if (-1 == cch || cch > cchToEnd)
  306. return CString(m_pValue->m_psz + iFirst);
  307. LPTSTR pszTemp = new TCHAR[cch + 1];
  308. if (NULL == pszTemp)
  309. throw CAllocException();
  310. CString::StrCpyN(pszTemp, m_pValue->m_psz + iFirst, cch + 1);
  311. CString strTemp(pszTemp);
  312. delete[] pszTemp;
  313. return strTemp;
  314. }
  315. VOID
  316. CString::ToUpper(
  317. INT iFirst,
  318. INT cch
  319. )
  320. {
  321. if (!ValidIndex(iFirst))
  322. throw CMemoryException(CMemoryException::index);
  323. CopyOnWrite();
  324. INT cchToEnd = Length() - iFirst;
  325. if (-1 == cch || cch > cchToEnd)
  326. cch = cchToEnd;
  327. CharUpperBuff(m_pValue->m_psz + iFirst, cch);
  328. }
  329. VOID
  330. CString::ToLower(
  331. INT iFirst,
  332. INT cch
  333. )
  334. {
  335. if (!ValidIndex(iFirst))
  336. throw CMemoryException(CMemoryException::index);
  337. CopyOnWrite();
  338. INT cchToEnd = Length() - iFirst;
  339. if (-1 == cch || cch > cchToEnd)
  340. cch = cchToEnd;
  341. CharLowerBuff(m_pValue->m_psz + iFirst, cch);
  342. }
  343. VOID
  344. CString::Size(
  345. INT cch
  346. )
  347. {
  348. StringValue *m_psv = new StringValue(cch + 1);
  349. CString::StrCpyN(m_psv->m_psz, m_pValue->m_psz, cch);
  350. if (m_pValue->m_cRef > 1)
  351. {
  352. InterlockedDecrement(&(m_pValue->m_cRef));
  353. }
  354. else
  355. {
  356. delete m_pValue;
  357. }
  358. m_pValue = m_psv;
  359. }
  360. VOID
  361. CString::CopyOnWrite(
  362. VOID
  363. )
  364. {
  365. //
  366. // Only need to copy if ref cnt > 1.
  367. //
  368. if (m_pValue->m_cRef > 1)
  369. {
  370. LPTSTR pszTemp = m_pValue->m_psz;
  371. InterlockedDecrement(&(m_pValue->m_cRef));
  372. m_pValue = NULL;
  373. m_pValue = new StringValue(pszTemp);
  374. }
  375. }
  376. BOOL
  377. CString::Format(
  378. LPCTSTR pszFmt,
  379. ...
  380. )
  381. {
  382. BOOL bResult;
  383. va_list args;
  384. va_start(args, pszFmt);
  385. bResult = Format(pszFmt, &args);
  386. va_end(args);
  387. return bResult;
  388. }
  389. BOOL
  390. CString::Format(
  391. LPCTSTR pszFmt,
  392. va_list *pargs
  393. )
  394. {
  395. BOOL bResult = FALSE;
  396. TCHAR szBuffer[MAX_RESOURCE_STR_LEN];
  397. INT cchLoaded;
  398. cchLoaded = ::FormatMessage(FORMAT_MESSAGE_FROM_STRING,
  399. pszFmt,
  400. 0,
  401. 0,
  402. szBuffer,
  403. ARRAYSIZE(szBuffer),
  404. pargs);
  405. if (0 < cchLoaded)
  406. {
  407. if (NULL != m_pValue && 0 == InterlockedDecrement(&(m_pValue->m_cRef)))
  408. delete m_pValue;
  409. m_pValue = NULL;
  410. m_pValue = new StringValue(szBuffer);
  411. bResult = TRUE;
  412. }
  413. else
  414. {
  415. DWORD dwLastError = GetLastError();
  416. if (ERROR_SUCCESS != dwLastError)
  417. {
  418. DBGERROR((TEXT("CString::Format failed with error 0x%08X"), dwLastError));
  419. throw CResourceException(CResourceException::string, NULL, 0);
  420. }
  421. }
  422. return bResult;
  423. }
  424. BOOL
  425. CString::Format(
  426. HINSTANCE hInst,
  427. UINT idFmt,
  428. ...
  429. )
  430. {
  431. BOOL bResult;
  432. va_list args;
  433. va_start(args, idFmt);
  434. bResult = Format(hInst, idFmt, &args);
  435. va_end(args);
  436. return bResult;
  437. }
  438. BOOL
  439. CString::Format(
  440. HINSTANCE hInst,
  441. UINT idFmt,
  442. va_list *pargs
  443. )
  444. {
  445. BOOL bResult = FALSE;
  446. TCHAR szFmtStr[MAX_RESOURCE_STR_LEN]; // Buffer for format string (if needed).
  447. INT cchLoaded;
  448. //
  449. // Try to load the format string as a string resource.
  450. //
  451. cchLoaded = ::LoadString(hInst, idFmt, szFmtStr, ARRAYSIZE(szFmtStr));
  452. if (0 < cchLoaded)
  453. {
  454. //
  455. // The format string was in a string resource.
  456. // Now format it with the arg list.
  457. //
  458. bResult = Format(szFmtStr, pargs);
  459. }
  460. else
  461. {
  462. TCHAR szBuffer[MAX_RESOURCE_STR_LEN];
  463. //
  464. // The format string may be in a message resource.
  465. // Note that if it is, the resulting formatted string will
  466. // be automatically attached to m_psz by ::FormatMessage.
  467. //
  468. cchLoaded = ::FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
  469. FORMAT_MESSAGE_FROM_HMODULE,
  470. hInst,
  471. (DWORD)idFmt,
  472. LANGIDFROMLCID(GetThreadLocale()),
  473. (LPTSTR)szBuffer,
  474. ARRAYSIZE(szBuffer),
  475. pargs);
  476. if (0 < cchLoaded)
  477. {
  478. if (NULL != m_pValue && 0 == InterlockedDecrement(&(m_pValue->m_cRef)))
  479. delete m_pValue;
  480. m_pValue = NULL;
  481. m_pValue = new StringValue(szBuffer);
  482. bResult = TRUE;
  483. }
  484. else
  485. {
  486. DWORD dwLastError = GetLastError();
  487. if (ERROR_SUCCESS != dwLastError)
  488. {
  489. DBGERROR((TEXT("CString::Format failed with error 0x%08X"), dwLastError));
  490. throw CResourceException(CResourceException::string, hInst, idFmt);
  491. }
  492. }
  493. }
  494. return bResult;
  495. }
  496. LPTSTR
  497. CString::GetBuffer(
  498. INT cchMax
  499. )
  500. {
  501. if (-1 == cchMax)
  502. cchMax = m_pValue->m_cchAlloc;
  503. CopyOnWrite();
  504. if (cchMax > m_pValue->m_cchAlloc)
  505. {
  506. //
  507. // Extend the buffer, copying original contents to dest.
  508. //
  509. StringValue *pv = new StringValue(cchMax);
  510. StrCpyN(pv->m_psz, m_pValue->m_psz, cchMax);
  511. if (0 == InterlockedDecrement(&(m_pValue->m_cRef)))
  512. delete m_pValue;
  513. m_pValue = pv;
  514. LPTSTR pszEnd = m_pValue->m_psz + m_pValue->m_cchAlloc - 1;
  515. if (pszEnd >= m_pValue->m_psz && TEXT('\0') != *(pszEnd))
  516. {
  517. //
  518. // Ensure it's nul terminated.
  519. //
  520. *(pszEnd) = TEXT('\0');
  521. }
  522. }
  523. return m_pValue->m_psz;
  524. }
  525. VOID
  526. CString::ReleaseBuffer(
  527. void
  528. )
  529. {
  530. //
  531. // Update the string length member after client has had free access
  532. // to internal buffer.
  533. //
  534. m_pValue->m_cch = StrLen(m_pValue->m_psz);
  535. }
  536. void
  537. CString::Rtrim(
  538. void
  539. )
  540. {
  541. LPTSTR s = GetBuffer();
  542. int len = Length();
  543. while(0 < --len && IsWhiteSpace(s[len]))
  544. s[len] = TEXT('\0');
  545. ReleaseBuffer();
  546. }
  547. void
  548. CString::Ltrim(
  549. void
  550. )
  551. {
  552. LPTSTR s0;
  553. LPTSTR s = s0 = GetBuffer();
  554. while(*s && IsWhiteSpace(*s))
  555. s++;
  556. while(*s)
  557. *s0++ = *s++;
  558. *s0 = TEXT('\0');
  559. ReleaseBuffer();
  560. }
  561. VOID
  562. CString::ExpandEnvironmentStrings(
  563. VOID
  564. )
  565. {
  566. DWORD cchBuffer = 0; // Size of expansion buffer.
  567. DWORD cchPath = 0; // Count of actual chars in expanded buffer.
  568. CString strExpanded; // Expansion buffer.
  569. //
  570. // If necessary, keep increasing expansion buffer size until entire
  571. // expanded string fits.
  572. //
  573. do
  574. {
  575. cchBuffer += MAX_PATH;
  576. cchPath = ::ExpandEnvironmentStrings(*this,
  577. strExpanded.GetBuffer(cchBuffer),
  578. cchBuffer);
  579. }
  580. while(0 != cchPath && cchPath > cchBuffer);
  581. ReleaseBuffer();
  582. *this = strExpanded;
  583. }
  584. bool
  585. CString::GetDisplayRect(
  586. HDC hdc,
  587. LPRECT prc
  588. ) const
  589. {
  590. return (0 != DrawText(hdc, Cstr(), Length(), prc, DT_CALCRECT));
  591. }
  592. VOID
  593. CString::DebugOut(
  594. BOOL bNewline
  595. ) const
  596. {
  597. OutputDebugString(m_pValue->m_psz);
  598. if (bNewline)
  599. OutputDebugString(TEXT("\n\r"));
  600. }
  601. CString::StringValue::StringValue(
  602. VOID
  603. ) : m_psz(new TCHAR[1]),
  604. m_cchAlloc(1),
  605. m_cch(0),
  606. m_cRef(1)
  607. {
  608. *m_psz = TEXT('\0');
  609. }
  610. CString::StringValue::StringValue(
  611. LPCSTR pszA
  612. ) : m_psz(NULL),
  613. m_cchAlloc(0),
  614. m_cch(0),
  615. m_cRef(1)
  616. {
  617. #ifdef UNICODE
  618. m_psz = AnsiToWide(pszA, &m_cchAlloc);
  619. m_cch = StrLenW(m_psz);
  620. #else
  621. m_cch = CString::StrLenA(pszA);
  622. m_psz = Dup(pszA, m_cch + 1);
  623. m_cchAlloc = m_cch + 1;
  624. #endif
  625. }
  626. CString::StringValue::StringValue(
  627. LPCWSTR pszW
  628. ) : m_psz(NULL),
  629. m_cchAlloc(0),
  630. m_cch(0),
  631. m_cRef(1)
  632. {
  633. #ifdef UNICODE
  634. m_cch = CString::StrLenW(pszW);
  635. m_psz = Dup(pszW, m_cch + 1);
  636. m_cchAlloc = m_cch + 1;
  637. #else
  638. m_psz = WideToAnsi(pszW, &m_cchAlloc);
  639. m_cch = StrLenA(m_psz);
  640. #endif
  641. }
  642. CString::StringValue::StringValue(
  643. INT cch
  644. ) : m_psz(NULL),
  645. m_cchAlloc(0),
  646. m_cch(0),
  647. m_cRef(0)
  648. {
  649. m_psz = Dup(TEXT(""), cch);
  650. m_cRef = 1;
  651. m_cchAlloc = cch;
  652. }
  653. CString::StringValue::~StringValue(
  654. VOID
  655. )
  656. {
  657. delete[] m_psz;
  658. }
  659. LPWSTR
  660. CString::StringValue::AnsiToWide(
  661. LPCSTR pszA,
  662. INT *pcch
  663. )
  664. {
  665. INT cchW = 0;
  666. LPWSTR pszW = NULL;
  667. cchW = MultiByteToWideChar(CP_ACP,
  668. 0,
  669. pszA,
  670. -1,
  671. NULL,
  672. 0);
  673. pszW = new WCHAR[cchW];
  674. if (NULL == pszW)
  675. throw CAllocException();
  676. MultiByteToWideChar(CP_ACP,
  677. 0,
  678. pszA,
  679. -1,
  680. pszW,
  681. cchW);
  682. if (NULL != pcch)
  683. *pcch = cchW;
  684. return pszW;
  685. }
  686. LPSTR
  687. CString::StringValue::WideToAnsi(
  688. LPCWSTR pszW,
  689. INT *pcch
  690. )
  691. {
  692. INT cchA = 0;
  693. LPSTR pszA = NULL;
  694. cchA = WideCharToMultiByte(CP_ACP,
  695. 0,
  696. pszW,
  697. -1,
  698. NULL,
  699. 0,
  700. NULL,
  701. NULL);
  702. pszA = new CHAR[cchA];
  703. if (NULL == pszA)
  704. throw CAllocException();
  705. WideCharToMultiByte(CP_ACP,
  706. 0,
  707. pszW,
  708. -1,
  709. pszA,
  710. cchA,
  711. NULL,
  712. NULL);
  713. if (NULL != pcch)
  714. *pcch = cchA;
  715. return pszA;
  716. }
  717. INT
  718. CString::StringValue::Length(
  719. VOID
  720. ) const
  721. {
  722. if (0 == m_cch && NULL != m_psz)
  723. {
  724. m_cch = StrLen(m_psz);
  725. }
  726. return m_cch;
  727. }
  728. LPWSTR
  729. CString::StringValue::Dup(
  730. LPCWSTR pszW,
  731. INT cch
  732. )
  733. {
  734. if (0 == cch)
  735. cch = CString::StrLenW(pszW) + 1;
  736. LPWSTR pszNew = new WCHAR[cch];
  737. if (NULL == pszNew)
  738. throw CAllocException();
  739. CString::StrCpyW(pszNew, pszW);
  740. return pszNew;
  741. }
  742. LPSTR
  743. CString::StringValue::Dup(
  744. LPCSTR pszA,
  745. INT cch
  746. )
  747. {
  748. if (0 == cch)
  749. cch = CString::StrLenA(pszA) + 1;
  750. LPSTR pszNew = new CHAR[cch];
  751. if (NULL == pszNew)
  752. throw CAllocException();
  753. CString::StrCpyA(pszNew, pszA);
  754. return pszNew;
  755. }
  756. LPTSTR
  757. CString::StringValue::Concat(
  758. CString::StringValue *psv1,
  759. CString::StringValue *psv2
  760. )
  761. {
  762. LPTSTR pszTemp = NULL;
  763. INT len1 = psv1->Length();
  764. INT len2 = psv2->Length();
  765. pszTemp = new TCHAR[len1 + len2 + 1];
  766. if (NULL == pszTemp)
  767. throw CAllocException();
  768. CString::StrCpy(pszTemp, psv1->m_psz);
  769. CString::StrCpy(pszTemp + len1, psv2->m_psz);
  770. return pszTemp;
  771. }
  772. #pragma warning (default : 4284)