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.

616 lines
19 KiB

  1. // FILE: tstring.cpp
  2. // AUTHOR: Davepl
  3. // REMARKS:
  4. //
  5. // (c) 1999 Dave Plummer. Portions of this code derived from source
  6. // produced by Joe O'Leary with the following license:
  7. // > This code is free. Use it anywhere you want. Rewrite
  8. // > it, restructure it, whatever you want. Please don't blame me if it causes your
  9. // > $30 billion dollar satellite to explode. If you redistribute it in any form, I
  10. // > would appreciate it if you would leave this notice here.
  11. #include "shtl.h"
  12. #include "tstring.h"
  13. #include <xutility>
  14. #if defined (max)
  15. #undef max
  16. #undef min
  17. #endif
  18. #define max(a,b) std::_cpp_max(a,b)
  19. #define min(a,b) std::_cpp_min(a,b)
  20. #include <algorithm>
  21. #include <functional>
  22. #include <locale>
  23. // If conversion has NOT been explicitly turned off...
  24. #ifndef _NO_STDCONVERSION
  25. // Global MBCS-to-UNICODE helper function
  26. __DATL_INLINE PWSTR StdA2WHelper(PWSTR pw, PCSTR pa, int nChars)
  27. {
  28. if (pa == NULL)
  29. return NULL;
  30. ASSERT(pw != NULL);
  31. pw[0] = '\0';
  32. VERIFY(MultiByteToWideChar(CP_ACP, 0, pa, -1, pw, nChars));
  33. return pw;
  34. }
  35. // Global UNICODE-to_MBCS helper function
  36. __DATL_INLINE PSTR StdW2AHelper(PSTR pa, PCWSTR pw, int nChars)
  37. {
  38. if (pw == NULL)
  39. return NULL;
  40. ASSERT(pa != NULL);
  41. pa[0] = '\0';
  42. VERIFY(WideCharToMultiByte(CP_ACP, 0, pw, -1, pa, nChars, NULL, NULL));
  43. return pa;
  44. }
  45. #endif // _NO_STDCONVERSION
  46. // CONSTRUCTOR: tstring::tstring
  47. // tstring(PCTSTR pT)
  48. //
  49. // DESCRIPTION:
  50. // This particular overload of the tstring constructor takes either a real
  51. // string or a resource ID which has been converted with the MAKEINTRESOURCE()
  52. // macro
  53. //
  54. // PARAMETERS:
  55. // pT - a NULL-terminated raw string with which the tstring object should be
  56. // initialized or a resource ID converted with MAKEINTRESOURCE (or NULL)
  57. __DATL_INLINE tstring::tstring(PCTSTR pT) : STRBASE(szTNull) // constructor for either a literal string or a resource ID
  58. {
  59. if ( pT == NULL )
  60. ;
  61. else if ( HIWORD(pT) == 0 )
  62. {
  63. if ( !Load(_TRES(pT)) )
  64. TRACE(_T("Can't load string %u\n"), _TRES(pT));
  65. }
  66. else
  67. *this = pT;
  68. }
  69. __DATL_INLINE tstring::tstring(UINT nID) : STRBASE(szTNull)
  70. {
  71. if ( !Load(nID) )
  72. TRACE(_T("Can't load string %u\n"), nID);
  73. }
  74. // FUNCTION: tstring::Load
  75. // bool Load(UINT nId)
  76. //
  77. // DESCRIPTION:
  78. // This function attempts to load the specified string resource from application's
  79. // resource table.
  80. //
  81. // PARAMETERS:
  82. // nId - the resource Identifier from the string table.
  83. //
  84. // RETURN VALUE:
  85. // true if the function succeeds, false otherwise
  86. #define MAX_LOAD_TRIES 8 // max # of times we'll attempt to load a string
  87. #define LOAD_BUF_SIZE 256
  88. __DATL_INLINE bool tstring::Load(UINT nId)
  89. {
  90. #ifdef _MFC_VER
  91. CString strRes(MAKEINTRESOURCE(nId));
  92. *this = strRes;
  93. return !empty();
  94. #else
  95. // Get the resource name via MAKEINTRESOURCE. This line is pretty much lifted from CString
  96. HINSTANCE hInstance = tstring::GetResourceHandle();
  97. HRSRC hrsrc;
  98. int cwch = 0;
  99. WCHAR * pwch;
  100. // String tables are broken up into "bundles" of 16 strings each.
  101. if (HIWORD(nId) == 0)
  102. {
  103. hrsrc = ::FindResource(hInstance, reinterpret_cast<LPTSTR>((UINT_PTR)(1 + nId / 16)), RT_STRING);
  104. if (hrsrc)
  105. {
  106. pwch = (PWCHAR)LoadResource(hInstance, hrsrc);
  107. if (pwch)
  108. {
  109. // Now skip over the strings in the resource until we
  110. // hit the one we want. Each entry is a counted string,
  111. // just like Pascal.
  112. for (nId %= 16; nId; nId--) {
  113. pwch += *pwch + 1;
  114. }
  115. cwch = *pwch;
  116. if (cwch)
  117. {
  118. LPTSTR pszBuffer = GetBuffer(cwch);
  119. #ifdef UNICODE
  120. memcpy(pszBuffer, pwch+1, cwch * sizeof(WCHAR)); /* Copy the goo */
  121. #else
  122. WideCharToMultiByte(CP_ACP, 0, pwch+1, cwch, pszBuffer, cwch+1, NULL, NULL);
  123. #endif
  124. *(pszBuffer+cwch) = TEXT('\0');
  125. ReleaseBuffer(cwch);
  126. }
  127. }
  128. }
  129. }
  130. return (cwch != 0);
  131. #endif
  132. }
  133. // FUNCTION: tstring::Format
  134. // void _cdecl Formst(tstring& PCTSTR szFormat, ...)
  135. // void _cdecl Format(PCTSTR szFormat);
  136. //
  137. // DESCRIPTION:
  138. // This function does sprintf/wsprintf style formatting on tstring objects. It
  139. // is very much a copy of the MFC CString::Format function. Some people might even
  140. // call this identical. However all these people are now dead.
  141. //
  142. // PARAMETERS:
  143. // nId - ID of string resource holding the format string
  144. // szFormat - a PCTSTR holding the format specifiers
  145. // argList - a va_list holding the arguments for the format specifiers.
  146. //
  147. // RETURN VALUE: None.
  148. __DATL_INLINE tstring& tstring::Format(UINT nId, ...)
  149. {
  150. va_list argList;
  151. va_start(argList, nId);
  152. tstring strFmt;
  153. if ( strFmt.Load(nId) )
  154. FormatV(strFmt, argList);
  155. va_end(argList);
  156. return *this;
  157. }
  158. __DATL_INLINE tstring& tstring::Format(PCTSTR szFormat, ...)
  159. {
  160. va_list argList;
  161. va_start(argList, szFormat);
  162. FormatV(szFormat, argList);
  163. va_end(argList);
  164. return *this;
  165. }
  166. // FUNCTION: tstring::FormatV
  167. // void FormatV(PCTSTR szFormat, va_list, argList);
  168. //
  169. // DESCRIPTION:
  170. // This function formats the string with sprintf style format-specifications. It
  171. // makes a general guess at required buffer size and then tries successively larger
  172. // buffers until it finds one big enough or a threshold (MAX_FMT_TRIES) is exceeded.
  173. //
  174. // PARAMETERS:
  175. // szFormat - a PCTSTR holding the format of the output
  176. // argList - va_list for variable argument lists
  177. //
  178. // RETURN VALUE:
  179. #define MAX_FMT_TRIES 5
  180. #define FMT_BLOCK_SIZE 256
  181. __DATL_INLINE tstring& tstring::FormatV(PCTSTR szFormat, va_list argList)
  182. {
  183. va_list argListSave = argList;
  184. // We're just going to use the normal _vsntprintf function, assuming FMT_BLOCK_SIZE characters.
  185. // However, if FMT_BLOCK_SIZE characters is not be enough, we will try 2 * FMT_BLOCK_SIZE, then
  186. // 3 * FMT_BLOCK_SIZE, up to MAX_FMT_TRIES * FMT_BLOCK_SIZE characters.
  187. int nTriesLeft = MAX_FMT_TRIES-1;
  188. int nCharsUsed = - 1;
  189. int nTChars = 0;
  190. // Keep looping until either we succeed or we have exhausted the number of tries
  191. do
  192. {
  193. nTChars += FMT_BLOCK_SIZE; // number of TCHARS in the string
  194. // Allocate a buffer on the stack to hold the characters and NULL terminate it
  195. TCHAR* szBuf = reinterpret_cast<TCHAR*>(_alloca((nTChars+1) * sizeof(TCHAR)));
  196. szBuf[nTChars+1] = '\0';
  197. // Now try the actual formatting. The docs say even the wide version takes the
  198. // number of BYTES as the second argument, not the number of characters (TCHARs).
  199. // However the docs are wrong. I checked the actual implementation of
  200. // _vsnprintf and _vsnwprintf and they multiply count by sizeof TCHAR.
  201. nCharsUsed = _vsntprintf(szBuf, nTChars+1, szFormat, argListSave);
  202. if ( nCharsUsed >= 0 )
  203. *this = szBuf;
  204. } while ( nCharsUsed < 0 && nTriesLeft > 0);
  205. va_end(argListSave);
  206. return *this;
  207. }
  208. // This class is used for TrimRight() and TrimLeft() function implementations.
  209. class NotSpace : public std::unary_function<TCHAR, bool>
  210. {
  211. public:
  212. inline bool operator() (TCHAR tchar)
  213. {
  214. return !_istspace(tchar);
  215. };
  216. };
  217. // FUNCTION: tstring::TrimRight
  218. // tstring& TrimRight();
  219. //
  220. // DESCRIPTION:
  221. // This function removes any whitespace characters from the right end of the string.
  222. //
  223. // PARAMETERS: none
  224. // RETURN VALUE:
  225. // a reference to this object (*this) -- allows chaining together of
  226. // these calls, eg. strTest.TrimRight().TrimLeft().ToUpper();
  227. __DATL_INLINE tstring& tstring::TrimRight()
  228. {
  229. tstring::reverse_iterator iter = std::find_if(rbegin(), rend(), NotSpace());
  230. if ( iter != rend() )
  231. {
  232. tstring::size_type nNewSize = find_last_of(*iter);
  233. erase(nNewSize+1);
  234. }
  235. else
  236. {
  237. erase();
  238. }
  239. return *this;
  240. }
  241. // FUNCTION: tstring::TrimLeft
  242. // tstring& TrimLeft();
  243. //
  244. // DESCRIPTION:
  245. // This function removes any whitespace characters from the left end of the string.
  246. //
  247. // PARAMETERS: none
  248. // RETURN VALUE:
  249. // a reference to this object (*this) -- allows chaining together of
  250. // these calls, (eg. strTest.TrimRight().TrimLeft().ToUpper();)
  251. __DATL_INLINE tstring& tstring::TrimLeft()
  252. {
  253. tstring::iterator iter = std::find_if(begin(), end(), NotSpace());
  254. tstring strNew(iter, end());
  255. STRBASE::assign(strNew);
  256. return *this;
  257. }
  258. // FUNCTION: tstring::ToUpper
  259. // tstring& ToUpper()
  260. //
  261. // DESCRIPTION:
  262. // This function converts the tstring to all uppercase characters using ctype
  263. //
  264. // PARAMETERS:
  265. // RETURN VALUE:
  266. // a reference to this object (*this) -- allows chaining together of
  267. // these calls, (eg. strTest.TrimRight().TrimLeft().ToUpper();)
  268. __DATL_INLINE tstring& tstring::ToUpper()
  269. {
  270. // std::transform(begin(), end(), begin(), toupper); // slow and portable
  271. _tcsupr(const_cast<PTSTR>(data())); // fast and not portable
  272. return *this;
  273. }
  274. // FUNCTION: tstring::ToLower
  275. // tstring& ToLower()
  276. //
  277. // DESCRIPTION:
  278. // This function converts the tstring to all lowercase characters using ctype
  279. //
  280. // PARAMETERS:
  281. // RETURN VALUE:
  282. // a reference to this object (*this) -- allows chaining together of
  283. // these calls, (eg. strTest.ToLower().TrimLeft().ToUpper();)
  284. __DATL_INLINE tstring& tstring::ToLower()
  285. {
  286. //std::transform(begin(), end(), begin(), tolower); // portable, slow way of doing it
  287. _tcslwr(const_cast<PTSTR>(data())); // unportable, fast way of doing it
  288. return *this;
  289. }
  290. // FUNCTION: tstring::CopyString
  291. // static void CopyString(PCTSTR p_szSource, PTSTR p_szDest, int p_nMaxChars=0);
  292. // static void CopyString(PCOSTR p_szSource, POSTR p_szDest, int p_nMaxChars=0);
  293. // static void CopyString(PCSTR p_szSource, PWSTR p_szDest, int p_nMaxChars=0);
  294. // static void CopyString(PCWSTR p_szSource, PSTR p_szDest, int p_nMaxChars=0);
  295. //
  296. // DESCRIPTION:
  297. // These 3 overloads simplify copying one C-style string into another.
  298. //
  299. // PARAMETERS:
  300. // p_szSource - the string to be copied FROM. May be either an MBCS string (char) or
  301. // a wide string (wchar_t)
  302. // p_szDest - the string to be copied TO. Also may be either MBCS or wide
  303. // p_nMaxChars - the maximum number of characters to be copied into p_szDest. Note
  304. // that this is expressed in whatever a "character" means to p_szDest.
  305. // If p_szDest is a wchar_t type string than this will be the maximum
  306. // number of wchar_ts that my be copied. The p_szDest string must be
  307. // large enough to hold least p_nMaxChars+1 characters.
  308. __DATL_INLINE void tstring::CopyString(PCTSTR p_szSource, PTSTR p_szDest, int p_nMaxChars)
  309. {
  310. int nSrcLen = ( p_szSource == NULL ? 0 : _tcslen(p_szSource) );
  311. int nChars = ( p_nMaxChars > 0 ? min(p_nMaxChars,nSrcLen) : nSrcLen );
  312. memcpy(p_szDest, p_szSource, nChars * sizeof(TCHAR));
  313. p_szDest[nChars] = '\0';
  314. }
  315. __DATL_INLINE void tstring::CopyString(PCOSTR p_szSource, POSTR p_szDest, int p_nMaxChars)
  316. {
  317. #ifdef _UNICODE
  318. int nSrcLen = ( p_szSource == NULL ? 0 : strlen(p_szSource) );
  319. #else
  320. int nSrcLen = ( p_szSource == NULL ? 0 : wcslen(p_szSource) );
  321. #endif
  322. int nChars = ( p_nMaxChars > 0 ? min(p_nMaxChars,nSrcLen) : nSrcLen );
  323. memcpy(p_szDest, p_szSource, nChars * sizeof(TOTHER));
  324. p_szDest[nChars] = '\0';
  325. }
  326. __DATL_INLINE void tstring::CopyString(PCSTR p_szSource, PWSTR p_szDest, int p_nMaxChars)
  327. {
  328. USES_CONVERSION;
  329. PCWSTR szConverted = (A2W(p_szSource));
  330. int nSrcLen = ( szConverted == NULL ? 0 : wcslen(szConverted) );
  331. int nChars = ( p_nMaxChars > 0 ? min(p_nMaxChars,nSrcLen) : nSrcLen );
  332. memcpy(p_szDest, szConverted, nChars * sizeof(wchar_t));
  333. p_szDest[nChars] = '\0';
  334. }
  335. __DATL_INLINE void tstring::CopyString(PCWSTR p_szSource, PSTR p_szDest, int p_nMaxChars)
  336. {
  337. USES_CONVERSION;
  338. PCSTR szConverted = (W2A(p_szSource));
  339. int nSrcLen = ( szConverted == NULL ? 0 : strlen(szConverted) );
  340. int nChars = ( p_nMaxChars > 0 ? min(p_nMaxChars,nSrcLen) : nSrcLen );
  341. memcpy(p_szDest, szConverted, nChars);
  342. p_szDest[nChars] = '\0';
  343. }
  344. // Special, TEMPORARY operators that allow us to serialize tstrings to CArchives.
  345. #ifdef _MFC_VER
  346. __DATL_INLINE CArchive& AFXAPI operator<<(CArchive& ar, const tstring& string)
  347. {
  348. USES_CONVERSION;
  349. // All tstrings are serialized as wide strings
  350. PCWSTR pWide = T2CW(string.data());
  351. int nChars = wcslen(pWide);
  352. ar << nChars;
  353. ar.Write(pWide, nChars*sizeof(wchar_t));
  354. return ar;
  355. }
  356. __DATL_INLINE CArchive& AFXAPI operator>>(CArchive& ar, tstring& string)
  357. {
  358. // All tstrings are serialized as wide strings
  359. UINT nLen;
  360. ar >> nLen;
  361. if ( nLen > 0 )
  362. {
  363. UINT nByteLen = nLen * sizeof(wchar_t);
  364. PWSTR pWide = (PWSTR)_alloca(nByteLen+sizeof(wchar_t));
  365. VERIFY(ar.Read(pWide, nByteLen) == nByteLen);
  366. pWide[nLen] = '\0';
  367. string = tstring(pWide);
  368. }
  369. else
  370. {
  371. string.erase();
  372. }
  373. return ar;
  374. }
  375. #endif
  376. #ifdef STDSTRING_INC_COMDEF
  377. // FUNCTION: tstring::StreamSave
  378. // HRESULT StreamSave(IStream* pStream) const;
  379. //
  380. // DESCRIPTION:
  381. // This function write the length and contents of the tstring object
  382. // out to an IStream as a char based string;
  383. //
  384. // PARAMETERS:
  385. // pStream - the stream to which the string must be written
  386. //
  387. // RETURN VALUE:
  388. // HRESULT return valued of IStream Write function
  389. __DATL_INLINE HRESULT tstring::StreamSave(IStream* pStream) const
  390. {
  391. USES_CONVERSION;
  392. HRESULT hr = E_FAIL;
  393. ASSERT(pStream != NULL);
  394. // All tstrings are serialized as wide strings
  395. PCSTR pStr = T2CA(this->data());
  396. ULONG nChars = strlen(pStr);
  397. if ( FAILED(hr=pStream->Write(&nChars, sizeof(ULONG), NULL)) )
  398. TRACE(_T("tstring::StreamSave -- Unable to write length to IStream due to error 0x%X\n"), hr);
  399. else if ( FAILED(hr=pStream->Write(pStr, nChars*sizeof(char), NULL)) )
  400. TRACE(_T("tstring::StreamSave -- Unable to write string to IStream due to error 0x%X\n"), hr);
  401. return hr;
  402. }
  403. // FUNCTION: tstring::StreamLoad
  404. // HRESULT StreamLoad(IStream* pStream);
  405. //
  406. // DESCRIPTION:
  407. // This function reads in a tstring object from an IStream
  408. //
  409. // PARAMETERS:
  410. // pStream - the stream from which the string must be read
  411. //
  412. // RETURN VALUE:
  413. // HRESULT return value of the IStream Read function
  414. __DATL_INLINE HRESULT tstring::StreamLoad(IStream* pStream)
  415. {
  416. // All tstrings are serialized as char strings
  417. ASSERT(pStream != NULL);
  418. ULONG nChars;
  419. HRESULT hr = E_FAIL;
  420. if ( FAILED(hr=pStream->Read(&nChars, sizeof(ULONG), NULL)) )
  421. {
  422. TRACE(_T("tstring::StreamLoad -- Unable to read length from IStream due to error 0x%X\n"), hr);
  423. }
  424. else if ( nChars > 0 )
  425. {
  426. ULONG nByteLen = nChars * sizeof(char);
  427. PSTR pStr = (PSTR)_alloca(nByteLen+sizeof(char)); // add an extra char for terminating NULL
  428. if ( FAILED(hr=pStream->Read(pStr, nByteLen, NULL)) )
  429. TRACE(_T("tstring::StreamLoad -- Unable to read string from IStream due to error 0x%X\n"), hr);
  430. pStr[nChars] = '\0';
  431. *this = tstring(pStr);
  432. }
  433. else
  434. {
  435. this->erase();
  436. }
  437. return hr;
  438. }
  439. // FUNCTION: tstring::StreamSize
  440. // ULONG StreamSize() const;
  441. //
  442. // DESCRIPTION:
  443. // This function tells the caller how many bytes will be required to write
  444. // this tstring object to an IStream using the StreamSave() function.
  445. // This is the capability lacking in CComBSTR which would force an IPersistXXX
  446. // implementation to know the implementation details of CComBSTR::StreamSave
  447. // in order to use CComBSTR in an IPersistXXX implementation.
  448. //
  449. // PARAMETERS: none
  450. // RETURN VALUE:
  451. // length in >> bytes << required to write the tstring
  452. __DATL_INLINE ULONG tstring::StreamSize() const
  453. {
  454. USES_CONVERSION;
  455. return ( strlen(T2CA(this->data())) * sizeof(char) ) + sizeof(ULONG);
  456. /// return ( wcslen(T2CW(this->data())) * sizeof(wchar_t) ) + sizeof(ULONG);
  457. }
  458. #endif
  459. // FUNCTION: WUSysMessage
  460. // TSTRING WUSysMessage(DWORD p_dwError, bool bUseDefault=false)
  461. //
  462. // DESCRIPTION:
  463. // This function simplifies the process of obtaining a string equivalent
  464. // of a system error code returned from GetLastError(). You simply
  465. // supply the value returned by GetLastError() to this function and the
  466. // corresponding system string is returned in the form of a tstring.
  467. //
  468. // PARAMETERS:
  469. // p_dwError - a DWORD value representing the error code to be translated
  470. // p_dwLangId - the language id to use. defaults to english.
  471. //
  472. // RETURN VALUE:
  473. // a tstring equivalent of the error code. Currently, this function
  474. // only returns either English of the system default language strings.
  475. #define MAX_FMT_TRIES 5
  476. #define FMT_BLOCK_SIZE 256
  477. __DATL_INLINE tstring WUSysMessage(DWORD p_dwError, DWORD p_dwLangId)
  478. {
  479. TCHAR szBuf[512];
  480. if ( ::FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, p_dwError, p_dwLangId, szBuf, 511, NULL) != 0 )
  481. return WUFormat(_T("%s (0x%X)"), szBuf, p_dwError);
  482. else
  483. return WUFormat(_T("Unknown error (0x%X)"), p_dwError);
  484. }
  485. // GLOBAL FUNCTION: WUFormat
  486. // tstring WUFormat(UINT nId, ...);
  487. // tstring WUFormat(PCTSTR szFormat, ...);
  488. //
  489. // REMARKS:
  490. // This function allows the caller for format and return a tstring object with a single line
  491. // of code. Frequently you want to print out a formatted string but don't care about it once
  492. // you are done with it. You end up having to create temporary tstring objects and then
  493. // calling their Format() functions. By using this function instead, you can cut down on the
  494. // clutter.
  495. __DATL_INLINE tstring WUFormat(UINT nId, ...)
  496. {
  497. va_list argList;
  498. va_start(argList, nId);
  499. tstring strFmt;
  500. tstring strOut;
  501. if ( strFmt.Load(nId) )
  502. strOut.FormatV(strFmt, argList);
  503. va_end(argList);
  504. return strOut;
  505. }
  506. __DATL_INLINE tstring WUFormat(PCTSTR szFormat, ...)
  507. {
  508. va_list argList;
  509. va_start(argList, szFormat);
  510. tstring strOut;
  511. strOut.FormatV(szFormat, argList);
  512. va_end(argList);
  513. return strOut;
  514. }