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.

844 lines
20 KiB

  1. /*++
  2. Copyright (c) 1997 Microsoft Corporation
  3. Module Name:
  4. string.cpp
  5. Abstract:
  6. Author:
  7. Vlad Sadovsky (vlads) 26-Jan-1997
  8. Revision History:
  9. 26-Jan-1997 VladS created
  10. --*/
  11. //
  12. // Normal includes only for this module to be active
  13. //
  14. #include "cplusinc.h"
  15. #include "sticomm.h"
  16. //
  17. // Private Definations
  18. //
  19. //
  20. // When appending data, this is the extra amount we request to avoid
  21. // reallocations
  22. //
  23. #define STR_SLOP 128
  24. //
  25. // Converts a value between zero and fifteen to the appropriate hex digit
  26. //
  27. #define HEXDIGIT( nDigit ) \
  28. (TCHAR)((nDigit) > 9 ? \
  29. (nDigit) - 10 + 'A' \
  30. : (nDigit) + '0')
  31. //
  32. // Converts a single hex digit to its decimal equivalent
  33. //
  34. #define TOHEX( ch ) \
  35. ((ch) > '9' ? \
  36. (ch) >= 'a' ? \
  37. (ch) - 'a' + 10 : \
  38. (ch) - 'A' + 10 \
  39. : (ch) - '0')
  40. //
  41. // Private Globals
  42. //
  43. WCHAR STR::_pszEmptyString[] = L"";
  44. //
  45. // Construction/Destruction
  46. //
  47. STR::STR( const CHAR * pchInit )
  48. {
  49. AuxInit( (PBYTE) pchInit, FALSE );
  50. }
  51. STR::STR( const WCHAR * pwchInit )
  52. {
  53. AuxInit( (PBYTE) pwchInit, TRUE );
  54. }
  55. STR::STR( const STR & str )
  56. {
  57. AuxInit( (PBYTE) str.QueryPtr(), str.IsUnicode() );
  58. }
  59. VOID STR::AuxInit( PBYTE pInit, BOOL fUnicode )
  60. {
  61. BOOL fRet;
  62. _fUnicode = fUnicode;
  63. _fValid = TRUE;
  64. if ( pInit )
  65. {
  66. INT cbCopy = fUnicode ? (::wcslen( (WCHAR *) pInit ) + 1) * sizeof(WCHAR) :
  67. (::strlen( (CHAR *) pInit ) + 1) * sizeof(CHAR);
  68. fRet = Resize( cbCopy );
  69. if ( !fRet )
  70. {
  71. _fValid = FALSE;
  72. return;
  73. }
  74. ::memcpy( QueryPtr(), pInit, cbCopy );
  75. }
  76. }
  77. //
  78. // Appends the string onto this one.
  79. //
  80. BOOL STR::Append( const CHAR * pchStr )
  81. {
  82. if ( pchStr )
  83. {
  84. ASSERT( !IsUnicode() );
  85. return AuxAppend( (PBYTE) pchStr, ::strlen( pchStr ) );
  86. }
  87. return TRUE;
  88. }
  89. BOOL STR::Append( const WCHAR * pwchStr )
  90. {
  91. if ( pwchStr )
  92. {
  93. ASSERT( IsUnicode() );
  94. return AuxAppend( (PBYTE) pwchStr, ::wcslen( pwchStr ) * sizeof(WCHAR) );
  95. }
  96. return TRUE;
  97. }
  98. BOOL STR::Append( const STR & str )
  99. {
  100. if ( str.IsUnicode() )
  101. return Append( (const WCHAR *) str.QueryStrW() );
  102. else
  103. return Append( (const CHAR *) str.QueryStrA() );
  104. }
  105. BOOL STR::AuxAppend( PBYTE pStr, UINT cbStr, BOOL fAddSlop )
  106. {
  107. ASSERT( pStr != NULL );
  108. UINT cbThis = QueryCB();
  109. //
  110. // Only resize when we have to. When we do resize, we tack on
  111. // some extra space to avoid extra reallocations.
  112. //
  113. // Note: QuerySize returns the requested size of the string buffer,
  114. // *not* the strlen of the buffer
  115. //
  116. if ( QuerySize() < cbThis + cbStr + sizeof(WCHAR) )
  117. {
  118. if ( !Resize( cbThis + cbStr + (fAddSlop ? STR_SLOP : sizeof(WCHAR) )) )
  119. return FALSE;
  120. }
  121. memcpy( (BYTE *) QueryPtr() + cbThis,
  122. pStr,
  123. cbStr + (IsUnicode() ? sizeof(WCHAR) : sizeof(CHAR)) );
  124. return TRUE;
  125. }
  126. //
  127. // Convert in place
  128. //
  129. BOOL STR::ConvertToW(VOID)
  130. {
  131. if (IsUnicode()) {
  132. return TRUE;
  133. }
  134. UINT cbNeeded = (QueryCB()+1)*sizeof(WCHAR);
  135. //
  136. // Only resize when we have to.
  137. //
  138. if ( QuerySize() < cbNeeded ) {
  139. if ( !Resize( cbNeeded)) {
  140. return FALSE;
  141. }
  142. }
  143. BUFFER buf(cbNeeded);
  144. if (!buf.QueryPtr()) {
  145. return FALSE;
  146. }
  147. int iRet;
  148. int cch;
  149. cch = QueryCCH() + 1;
  150. iRet = MultiByteToWideChar( CP_ACP,
  151. MB_PRECOMPOSED,
  152. QueryStrA(),
  153. -1,
  154. (WCHAR *)buf.QueryPtr(),
  155. cch);
  156. if ( iRet == 0 ) {
  157. //
  158. // Error in conversion.
  159. //
  160. return FALSE;
  161. }
  162. memcpy( (BYTE *) QueryPtr(),
  163. buf.QueryPtr(),
  164. cbNeeded);
  165. SetUnicode(TRUE);
  166. return TRUE;
  167. }
  168. BOOL STR::ConvertToA(VOID)
  169. {
  170. if (!IsUnicode()) {
  171. return TRUE;
  172. }
  173. UINT cbNeeded = (QueryCB()+1)*sizeof(CHAR);
  174. BUFFER buf(cbNeeded);
  175. if (!buf.QueryPtr()) {
  176. return FALSE;
  177. }
  178. int iRet;
  179. int cch;
  180. cch = cbNeeded;
  181. iRet = WideCharToMultiByte(CP_ACP,
  182. 0L,
  183. QueryStrW(),
  184. -1,
  185. (CHAR *)buf.QueryPtr(),
  186. cch,
  187. NULL,
  188. NULL
  189. );
  190. if ( iRet == 0 ) {
  191. //
  192. // Error in conversion.
  193. //
  194. return FALSE;
  195. }
  196. // Careful here , there might be DBCS characters in resultant buffer
  197. memcpy( (BYTE *) QueryPtr(),
  198. buf.QueryPtr(),
  199. iRet);
  200. SetUnicode(FALSE);
  201. return TRUE;
  202. }
  203. //
  204. // Copies the string into this one.
  205. //
  206. BOOL STR::Copy( const CHAR * pchStr )
  207. {
  208. _fUnicode = FALSE;
  209. if ( QueryPtr() )
  210. *(QueryStrA()) = '\0';
  211. if ( pchStr )
  212. {
  213. return AuxAppend( (PBYTE) pchStr, ::strlen( pchStr ), FALSE );
  214. }
  215. return TRUE;
  216. }
  217. BOOL STR::Copy( const WCHAR * pwchStr )
  218. {
  219. _fUnicode = TRUE;
  220. if ( QueryPtr() )
  221. *(QueryStrW()) = TEXT('\0');
  222. if ( pwchStr )
  223. {
  224. return AuxAppend( (PBYTE) pwchStr, ::wcslen( pwchStr ) * sizeof(WCHAR), FALSE );
  225. }
  226. return TRUE;
  227. }
  228. BOOL STR::Copy( const STR & str )
  229. {
  230. _fUnicode = str.IsUnicode();
  231. if ( str.IsEmpty() && QueryPtr() == NULL) {
  232. // To avoid pathological allocation of small chunk of memory
  233. return ( TRUE);
  234. }
  235. if ( str.IsUnicode() )
  236. return Copy( str.QueryStrW() );
  237. else
  238. return Copy( str.QueryStrA() );
  239. }
  240. //
  241. // Resizes or allocates string memory, NULL terminating if necessary
  242. //
  243. BOOL STR::Resize( UINT cbNewRequestedSize )
  244. {
  245. BOOL fTerminate = QueryPtr() == NULL;
  246. if ( !BUFFER::Resize( cbNewRequestedSize ))
  247. return FALSE;
  248. if ( fTerminate && cbNewRequestedSize > 0 )
  249. {
  250. if ( IsUnicode() )
  251. {
  252. ASSERT( cbNewRequestedSize > 1 );
  253. *QueryStrW() = TEXT('\0');
  254. }
  255. else
  256. *QueryStrA() = '\0';
  257. }
  258. return TRUE;
  259. }
  260. //
  261. // Loads a string resource from this module's string table
  262. // or from the system string table
  263. //
  264. // dwResID - System error or module string ID
  265. // lpszModuleName - name of the module from which to load.
  266. // If NULL, then load the string from system table.
  267. //
  268. //
  269. BOOL STR::LoadString( IN DWORD dwResID,
  270. IN LPCTSTR lpszModuleName // Optional
  271. )
  272. {
  273. BOOL fReturn = FALSE;
  274. INT cch;
  275. //
  276. // If lpszModuleName is NULL, load the string from system's string table.
  277. //
  278. if ( lpszModuleName == NULL) {
  279. BYTE * pchBuff = NULL;
  280. //
  281. // Call the appropriate function so we don't have to do the Unicode
  282. // conversion
  283. //
  284. if ( IsUnicode() ) {
  285. cch = ::FormatMessageW( FORMAT_MESSAGE_ALLOCATE_BUFFER |
  286. FORMAT_MESSAGE_IGNORE_INSERTS |
  287. FORMAT_MESSAGE_MAX_WIDTH_MASK |
  288. FORMAT_MESSAGE_FROM_SYSTEM,
  289. NULL,
  290. dwResID,
  291. 0,
  292. (LPWSTR) &pchBuff,
  293. 1024,
  294. NULL );
  295. if ( cch ) {
  296. fReturn = Copy( (LPCWSTR) pchBuff );
  297. }
  298. }
  299. else
  300. {
  301. cch = ::FormatMessageA( FORMAT_MESSAGE_ALLOCATE_BUFFER |
  302. FORMAT_MESSAGE_IGNORE_INSERTS |
  303. FORMAT_MESSAGE_MAX_WIDTH_MASK |
  304. FORMAT_MESSAGE_FROM_SYSTEM,
  305. NULL,
  306. dwResID,
  307. 0,
  308. (LPSTR) &pchBuff,
  309. 1024,
  310. NULL );
  311. if ( cch ) {
  312. fReturn = Copy( (LPCSTR) pchBuff );
  313. }
  314. }
  315. //
  316. // Free the buffer FormatMessage allocated
  317. //
  318. if ( cch )
  319. {
  320. ::LocalFree( (VOID*) pchBuff );
  321. }
  322. } else {
  323. WCHAR ach[STR_MAX_RES_SIZE];
  324. if ( IsUnicode() )
  325. {
  326. cch = ::LoadStringW( GetModuleHandle( lpszModuleName),
  327. dwResID,
  328. (WCHAR *) ach,
  329. sizeof(ach) / sizeof(ach[0]));
  330. if ( cch )
  331. {
  332. fReturn = Copy( (LPWSTR) ach );
  333. }
  334. }
  335. else
  336. {
  337. cch = ::LoadStringA( GetModuleHandle( lpszModuleName),
  338. dwResID,
  339. (CHAR *) ach,
  340. sizeof(ach));
  341. if ( cch )
  342. {
  343. fReturn = Copy( (LPSTR) ach );
  344. }
  345. }
  346. }
  347. return ( fReturn);
  348. } // STR::LoadString()
  349. BOOL STR::LoadString( IN DWORD dwResID,
  350. IN HMODULE hModule
  351. )
  352. {
  353. BOOL fReturn = FALSE;
  354. INT cch;
  355. WCHAR ach[STR_MAX_RES_SIZE];
  356. if ( IsUnicode()) {
  357. cch = ::LoadStringW(hModule,
  358. dwResID,
  359. (WCHAR *) ach,
  360. sizeof(ach) / sizeof(ach[0]));
  361. if ( cch ) {
  362. fReturn = Copy( (LPWSTR) ach );
  363. }
  364. } else {
  365. cch = ::LoadStringA(hModule,
  366. dwResID,
  367. (CHAR *) ach,
  368. sizeof(ach));
  369. if ( cch ) {
  370. fReturn = Copy( (LPSTR) ach );
  371. }
  372. }
  373. return ( fReturn);
  374. } // STR::LoadString()
  375. BOOL
  376. STR::FormatStringV(
  377. IN LPCTSTR lpszModuleName,
  378. ...
  379. )
  380. {
  381. DWORD cch;
  382. LPSTR pchBuff;
  383. BOOL fRet = FALSE;
  384. DWORD dwErr;
  385. va_list va;
  386. va_start(va,lpszModuleName);
  387. cch = ::FormatMessageA( FORMAT_MESSAGE_ALLOCATE_BUFFER |
  388. FORMAT_MESSAGE_FROM_STRING,
  389. QueryStrA(),
  390. 0L,
  391. 0,
  392. (LPSTR) &pchBuff,
  393. 1024,
  394. &va);
  395. dwErr = ::GetLastError();
  396. if ( cch ) {
  397. fRet = Copy( (LPCSTR) pchBuff );
  398. ::LocalFree( (VOID*) pchBuff );
  399. }
  400. return fRet;
  401. }
  402. BOOL
  403. STR::FormatString(
  404. IN DWORD dwResID,
  405. IN LPCTSTR apszInsertParams[],
  406. IN LPCTSTR lpszModuleName
  407. )
  408. {
  409. DWORD cch;
  410. LPSTR pchBuff;
  411. BOOL fRet;
  412. if (!dwResID) {
  413. cch = ::FormatMessageA( FORMAT_MESSAGE_ALLOCATE_BUFFER |
  414. FORMAT_MESSAGE_ARGUMENT_ARRAY |
  415. FORMAT_MESSAGE_FROM_STRING,
  416. QueryStrA(),
  417. dwResID,
  418. 0,
  419. (LPSTR) &pchBuff,
  420. 1024,
  421. (va_list *) apszInsertParams );
  422. }
  423. else {
  424. cch = ::FormatMessageA( FORMAT_MESSAGE_ALLOCATE_BUFFER |
  425. FORMAT_MESSAGE_ARGUMENT_ARRAY |
  426. FORMAT_MESSAGE_FROM_HMODULE,
  427. GetModuleHandle( lpszModuleName ),
  428. dwResID,
  429. 0,
  430. (LPSTR) &pchBuff,
  431. 1024,
  432. (va_list *) apszInsertParams );
  433. }
  434. if ( cch )
  435. {
  436. fRet = Copy( (LPCSTR) pchBuff );
  437. ::LocalFree( (VOID*) pchBuff );
  438. }
  439. return fRet;
  440. }
  441. #if 1
  442. CHAR * STR::QueryStrA( VOID ) const
  443. {
  444. ASSERT( !IsUnicode() );
  445. ASSERT( *_pszEmptyString == TEXT('\0') );
  446. return (QueryPtr() ? (CHAR *) QueryPtr() : (CHAR *) _pszEmptyString);
  447. }
  448. WCHAR * STR::QueryStrW( VOID ) const
  449. {
  450. ASSERT( IsUnicode() );
  451. ASSERT( *_pszEmptyString == TEXT('\0') );
  452. return (QueryPtr() ? (WCHAR *) QueryPtr() : (WCHAR *) _pszEmptyString);
  453. }
  454. #endif //DBG
  455. BOOL STR::CopyToBuffer( WCHAR * lpszBuffer, LPDWORD lpcch) const
  456. /*++
  457. Description:
  458. Copies the string into the WCHAR buffer passed in if the buffer
  459. is sufficient to hold the translated string.
  460. If the buffer is small, the function returns small and sets *lpcch
  461. to contain the required number of characters.
  462. Arguments:
  463. lpszBuffer pointer to WCHAR buffer which on return contains
  464. the UNICODE version of string on success.
  465. lpcch pointer to DWORD containing the length of the buffer.
  466. If *lpcch == 0 then the function returns TRUE with
  467. the count of characters required stored in *lpcch.
  468. Also in this case lpszBuffer is not affected.
  469. Returns:
  470. TRUE on success.
  471. FALSE on failure. Use GetLastError() for further details.
  472. --*/
  473. {
  474. BOOL fReturn = TRUE;
  475. if ( lpcch == NULL) {
  476. SetLastError( ERROR_INVALID_PARAMETER);
  477. return ( FALSE);
  478. }
  479. if ( *lpcch == 0) {
  480. //
  481. // Inquiring the size of buffer alone
  482. //
  483. *lpcch = QueryCCH() + 1; // add one character for terminating null
  484. } else {
  485. //
  486. // Copy data to buffer
  487. //
  488. if ( IsUnicode()) {
  489. //
  490. // Do plain copy of the data.
  491. //
  492. if ( *lpcch >= QueryCCH()) {
  493. wcscpy( lpszBuffer, QueryStrW());
  494. } else {
  495. SetLastError( ERROR_INSUFFICIENT_BUFFER);
  496. fReturn = FALSE;
  497. }
  498. } else {
  499. //
  500. // Copy after conversion from ANSI to Unicode
  501. //
  502. int iRet;
  503. iRet = MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED,
  504. QueryStrA(), QueryCCH() + 1,
  505. lpszBuffer, (int )*lpcch);
  506. if ( iRet == 0 || iRet != (int ) *lpcch) {
  507. //
  508. // Error in conversion.
  509. //
  510. fReturn = FALSE;
  511. }
  512. }
  513. }
  514. return ( fReturn);
  515. } // STR::CopyToBuffer()
  516. BOOL STR::CopyToBufferA( CHAR * lpszBuffer, LPDWORD lpcch) const
  517. /*++
  518. Description:
  519. Copies the string into the CHAR buffer passed in if the buffer
  520. is sufficient to hold the translated string.
  521. If the buffer is small, the function returns small and sets *lpcch
  522. to contain the required number of characters.
  523. Arguments:
  524. lpszBuffer pointer to CHAR buffer which on return contains
  525. the MBCS version of string on success.
  526. lpcch pointer to DWORD containing the length of the buffer.
  527. If *lpcch == 0 then the function returns TRUE with
  528. the count of characters required stored in *lpcch.
  529. Also in this case lpszBuffer is not affected.
  530. Returns:
  531. TRUE on success.
  532. FALSE on failure. Use GetLastError() for further details.
  533. --*/
  534. {
  535. BOOL fReturn = TRUE;
  536. if ( lpcch == NULL) {
  537. SetLastError( ERROR_INVALID_PARAMETER);
  538. return ( FALSE);
  539. }
  540. if ( *lpcch == 0) {
  541. //
  542. // Inquiring the size of buffer alone
  543. //
  544. *lpcch = 2*(QueryCCH() + 1); // add one character for terminating null and on pessimistic side
  545. // ask for largest possible buffer
  546. } else {
  547. //
  548. // Copy data to buffer
  549. //
  550. if ( !IsUnicode()) {
  551. lstrcpyA( lpszBuffer, QueryStrA());
  552. } else {
  553. //
  554. // Copy after conversion from Unicode to MBCS
  555. //
  556. int iRet;
  557. iRet = WideCharToMultiByte(CP_ACP,
  558. 0L,
  559. QueryStrW(),
  560. QueryCCH()+1,
  561. lpszBuffer,
  562. *lpcch,
  563. NULL,NULL
  564. );
  565. if ( iRet == 0 || *lpcch < (DWORD)iRet) {
  566. *lpcch = iRet;
  567. fReturn = FALSE;
  568. }
  569. }
  570. }
  571. return ( fReturn);
  572. } // STR::CopyToBuffer()
  573. /*
  574. STRArray class implementation- this isn't very efficient, as
  575. we'll copy every string over again whenever we grow the array, but
  576. then again, that part of the code may never even get executed, anyway
  577. */
  578. void STRArray::Grow() {
  579. // We need to add some more strings to the array
  580. STR *pcsNew = new STR[m_ucMax += m_uGrowBy];
  581. if (!pcsNew) {
  582. // We recover gracelessly by replacing the final
  583. // string.
  584. m_ucMax -= m_uGrowBy;
  585. m_ucItems--;
  586. return;
  587. }
  588. for (unsigned u = 0; u < m_ucItems; u++)
  589. pcsNew[u] = (LPCTSTR) m_pcsContents[u];
  590. delete[] m_pcsContents;
  591. m_pcsContents = pcsNew;
  592. }
  593. STRArray::STRArray(unsigned uGrowBy) {
  594. m_uGrowBy = uGrowBy ? uGrowBy : 10;
  595. m_ucItems = m_ucMax = 0;
  596. m_pcsContents = NULL;
  597. }
  598. STRArray::~STRArray() {
  599. if (m_pcsContents)
  600. delete[] m_pcsContents;
  601. }
  602. void STRArray::Add(LPCSTR lpstrNew) {
  603. if (m_ucItems >= m_ucMax)
  604. Grow();
  605. m_pcsContents[m_ucItems++].Copy(lpstrNew);
  606. }
  607. void STRArray::Add(LPCWSTR lpstrNew) {
  608. if (m_ucItems >= m_ucMax)
  609. Grow();
  610. m_pcsContents[m_ucItems++].Copy(lpstrNew);
  611. }
  612. void STRArray::Tokenize(LPCTSTR lpstrIn, TCHAR tcSplitter) {
  613. if (m_pcsContents) {
  614. delete[] m_pcsContents;
  615. m_ucItems = m_ucMax = 0;
  616. m_pcsContents = NULL;
  617. }
  618. if (!lpstrIn || !*lpstrIn)
  619. return;
  620. while (*lpstrIn) {
  621. // First, strip off any leading blanks
  622. while (*lpstrIn && *lpstrIn == _TEXT(' '))
  623. lpstrIn++;
  624. for (LPCTSTR lpstrMoi = lpstrIn;
  625. *lpstrMoi && *lpstrMoi != tcSplitter;
  626. lpstrMoi++)
  627. ;
  628. // If we hit the end, just add the whole thing to the array
  629. if (!*lpstrMoi) {
  630. if (*lpstrIn)
  631. Add(lpstrIn);
  632. return;
  633. }
  634. // Otherwise, just add the string up to the splitter
  635. TCHAR szNew[MAX_PATH];
  636. SIZE_T uiLen = (SIZE_T)(lpstrMoi - lpstrIn);
  637. if (uiLen < (sizeof(szNew)/sizeof(szNew[0])) - 1) {
  638. lstrcpyn(szNew,lpstrIn,(UINT)uiLen);
  639. szNew[uiLen] = TCHAR('\0');
  640. Add((LPCTSTR) szNew);
  641. }
  642. lpstrIn = lpstrMoi + 1;
  643. }
  644. }