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.

2702 lines
45 KiB

  1. /*++
  2. Copyright (c) 1994-1999 Microsoft Corporation
  3. Module Name :
  4. strfrn.cpp
  5. Abstract:
  6. String Functions
  7. Author:
  8. Ronald Meijer (ronaldm)
  9. Project:
  10. Internet Services Manager (cluster edition)
  11. Revision History:
  12. --*/
  13. //
  14. // Include Files
  15. //
  16. #include "stdafx.h"
  17. #include "common.h"
  18. #include <pudebug.h>
  19. #ifdef _DEBUG
  20. #undef THIS_FILE
  21. static char BASED_CODE THIS_FILE[] = __FILE__;
  22. #endif
  23. #define new DEBUG_NEW
  24. #ifdef _MT
  25. //
  26. // Thread protected stuff
  27. //
  28. #define RaiseThreadProtection() EnterCriticalSection(&_csSect)
  29. #define LowerThreadProtection() LeaveCriticalSection(&_csSect)
  30. static CRITICAL_SECTION _csSect;
  31. #else
  32. #pragma message("Module is not thread-safe")
  33. #define RaiseThreadProtection()
  34. #define LowerThreadProtection()
  35. #endif // _MT
  36. #define MAKE_NULL(obj) { if (obj) delete obj, obj = NULL; }
  37. //
  38. // Text copy functions
  39. //
  40. // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  41. BOOL
  42. PCToUnixText(
  43. OUT LPWSTR & lpstrDestination,
  44. IN const CString strSource
  45. )
  46. /*++
  47. Routine Description:
  48. Convert CR/LF string to LF string (T String to W String). Destination
  49. string will be allocated.
  50. Arguments:
  51. LPWSTR & lpstrDestination : Destination string
  52. const CString & strSource : Source string
  53. Return Value:
  54. TRUE for success, FALSE for failure.
  55. --*/
  56. {
  57. int cch = strSource.GetLength() + 1;
  58. lpstrDestination = (LPWSTR)AllocMem(cch * sizeof(WCHAR));
  59. if (lpstrDestination != NULL)
  60. {
  61. LPCTSTR lpS = strSource;
  62. LPWSTR lpD = lpstrDestination;
  63. do
  64. {
  65. if (*lpS != _T('\r'))
  66. {
  67. #ifdef UNICODE
  68. *lpD++ = *lpS;
  69. #else
  70. ::MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, lpS, 1, lpD++, 1);
  71. #endif // UNICODE
  72. }
  73. }
  74. while (*lpS++);
  75. return TRUE;
  76. }
  77. return FALSE;
  78. }
  79. BOOL
  80. UnixToPCText(
  81. OUT CString & strDestination,
  82. IN LPCWSTR lpstrSource
  83. )
  84. /*++
  85. Routine Description:
  86. Expand LF to CR/LF (no allocation necessary) W String to T String.
  87. Arguments:
  88. CString & strDestination : Destination string
  89. LPCWSTR lpstrSource : Source string
  90. Return Value:
  91. TRUE for success, FALSE for failure.
  92. --*/
  93. {
  94. BOOL fSuccess = FALSE;
  95. try
  96. {
  97. LPCWSTR lpS = lpstrSource;
  98. //
  99. // Since we're doubling every linefeed length, assume
  100. // the worst possible expansion to start with.
  101. //
  102. int cch = (::lstrlenW(lpstrSource) + 1) * 2;
  103. LPTSTR lpD = strDestination.GetBuffer(cch);
  104. do
  105. {
  106. if (*lpS == L'\n')
  107. {
  108. *lpD++ = _T('\r');
  109. }
  110. #ifdef UNICODE
  111. *lpD++ = *lpS;
  112. #else
  113. ::WideCharToMultiByte(CP_ACP, 0, lpS, 1, lpD++, 1, NULL, NULL);
  114. #endif // UNICODE
  115. }
  116. while (*lpS++);
  117. strDestination.ReleaseBuffer();
  118. ++fSuccess;
  119. }
  120. catch(CMemoryException * e)
  121. {
  122. TRACEEOLID("Exception in UnixToPCText");
  123. e->ReportError();
  124. e->Delete();
  125. }
  126. return fSuccess;
  127. }
  128. /*
  129. BOOL
  130. TextToText(
  131. OUT LPWSTR & lpstrDestination,
  132. IN const CString & strSource
  133. )
  134. /*++
  135. Routine Description:
  136. Straight copy with allocation. T String to W String.
  137. Arguments:
  138. LPWSTR & lpstrDestination : Destination string
  139. const CString & strSource : Source string
  140. Return Value:
  141. TRUE for success, FALSE for failure.
  142. --/
  143. {
  144. int cch = strSource.GetLength() + 1;
  145. lpstrDestination = (LPWSTR)AllocMem(cch * sizeof(WCHAR));
  146. if (lpstrDestination != NULL)
  147. {
  148. TWSTRCPY(lpstrDestination, strSource, cch);
  149. return TRUE;
  150. }
  151. return FALSE;
  152. }
  153. #ifndef UNICODE
  154. #define WBUFF_SIZE 255
  155. LPWSTR
  156. ReferenceAsWideString(
  157. IN LPCTSTR str
  158. )
  159. /*++
  160. Routine Description:
  161. Reference a T string as a W string (non-unicode only).
  162. Arguments:
  163. LPCTSTR str : Source string
  164. Return Value:
  165. Wide char pointer to wide string.
  166. Notes:
  167. This uses an internal wide char buffer, which will be overwritten
  168. by subsequent calls to this function.
  169. --/
  170. {
  171. static WCHAR wchBuff[WBUFF_SIZE + 1];
  172. ::MultiByteToWideChar(
  173. CP_ACP,
  174. MB_PRECOMPOSED,
  175. str,
  176. -1,
  177. wchBuff,
  178. WBUFF_SIZE + 1
  179. );
  180. return wchBuff;
  181. }
  182. #endif !UNICODE
  183. */
  184. LPSTR
  185. AllocAnsiString(
  186. IN LPCTSTR lpString
  187. )
  188. /*++
  189. Routine Description:
  190. Convert the wide string to an ansi (multi-byte) string, which is allocated
  191. by this function
  192. Arguments:
  193. LPCTSTR lpString : Input wide string
  194. Return Value:
  195. Pointer to the allocated string
  196. --*/
  197. {
  198. //
  199. // Character counts are DBCS friendly
  200. //
  201. int cChars = lstrlen(lpString);
  202. int nLength = (cChars * 2) + 1;
  203. LPSTR lp = (LPSTR)AllocMem(nLength);
  204. if (lp)
  205. {
  206. ::WideCharToMultiByte(
  207. CP_ACP,
  208. 0,
  209. lpString,
  210. cChars + 1,
  211. lp,
  212. nLength,
  213. NULL,
  214. NULL
  215. );
  216. }
  217. return lp;
  218. }
  219. LPTSTR
  220. AllocString(
  221. IN LPCTSTR lpString,
  222. IN int nLen
  223. )
  224. /*++
  225. Routine Description:
  226. Allocate and copy string
  227. Arguments:
  228. LPCTSTR lpString : Input string
  229. int nLen : Length or -1 to autodetermine
  230. Return Value:
  231. Pointer to the allocated string
  232. --*/
  233. {
  234. if (nLen < 0)
  235. {
  236. nLen = lstrlen(lpString);
  237. }
  238. LPTSTR lp = (LPTSTR)AllocMem((nLen + 1) * sizeof(TCHAR));
  239. if (lp)
  240. {
  241. lstrcpy(lp, lpString);
  242. }
  243. return lp;
  244. }
  245. BOOL
  246. IsUNCName(
  247. IN const CString & strDirPath
  248. )
  249. /*++
  250. Routine Description:
  251. Determine if the given string path is a UNC path.
  252. Arguments:
  253. const CString & strDirPath : Directory path string
  254. Return Value:
  255. TRUE if the path is a UNC path, FALSE otherwise.
  256. Notes:
  257. Any string of the form \\foo\bar\whatever is considered a UNC path,
  258. with the exception of \\.\device paths. No validation for the
  259. existance occurs, only for the correct format.
  260. --*/
  261. {
  262. if (strDirPath.GetLength() >= 5) // It must be at least as long as \\x\y,
  263. { //
  264. LPCTSTR lp = strDirPath; //
  265. if (*lp == _T('\\') // It must begin with \\,
  266. && *(lp + 1) == _T('\\') //
  267. && *(lp + 2) != _T('.') // This is a device.
  268. && _tcschr(lp + 3, _T('\\')) // And have at least one more \ after that
  269. )
  270. {
  271. //
  272. // Yes, it's a UNC path
  273. //
  274. return TRUE;
  275. }
  276. }
  277. //
  278. // No, it's not
  279. //
  280. return FALSE;
  281. }
  282. BOOL
  283. IsDevicePath(
  284. IN const CString & strDirPath
  285. )
  286. /*++
  287. Routine Description:
  288. Determine if the given path is of the form "\\.\foobar"
  289. Arguments:
  290. const CString & strDirPath : Directory path string
  291. Return Value:
  292. TRUE if the path given is a device path,
  293. FALSE if it is not.
  294. --*/
  295. {
  296. LPCTSTR lpszDevice = _T("\\\\.\\");
  297. return _tcsnccmp(strDirPath, lpszDevice, lstrlen(lpszDevice)) == 0;
  298. }
  299. BOOL
  300. PathIsValid(LPCTSTR path)
  301. {
  302. LPCTSTR p = path;
  303. BOOL rc = TRUE;
  304. if (p == NULL || *p == 0)
  305. return FALSE;
  306. while (*p != 0)
  307. {
  308. switch (*p)
  309. {
  310. case TEXT('|'):
  311. case TEXT('>'):
  312. case TEXT('<'):
  313. case TEXT('/'):
  314. case TEXT('?'):
  315. case TEXT('*'):
  316. // case TEXT(';'):
  317. // case TEXT(','):
  318. case TEXT('"'):
  319. rc = FALSE;
  320. break;
  321. default:
  322. if (*p < TEXT(' '))
  323. {
  324. rc = FALSE;
  325. }
  326. break;
  327. }
  328. if (!rc)
  329. {
  330. break;
  331. }
  332. p++;
  333. }
  334. return rc;
  335. }
  336. BOOL
  337. IsFullyQualifiedPath(
  338. IN const CString & strDirPath
  339. )
  340. /*++
  341. Routine Description:
  342. Determine if the given string is a fully qualified path name
  343. Arguments:
  344. const CString & strDirPath : Directory path string
  345. Return Value:
  346. TRUE if the path is a fully qualified path name
  347. --*/
  348. {
  349. return strDirPath.GetLength() >= 3
  350. && strDirPath[1] == _T(':')
  351. && strDirPath[2] == _T('\\');
  352. }
  353. BOOL
  354. IsNetworkPath(
  355. IN const CString & strDirPath,
  356. OUT CString * pstrDrive, OPTIONAL
  357. OUT CString * pstrUNC OPTIONAL
  358. )
  359. /*++
  360. Routine Description:
  361. Determine if the path exists on a network directory
  362. in the context of the local machine.
  363. Arguments:
  364. const CString & strDirPath : Directory path string
  365. CString * pstrDrive : Returns drive
  366. CString * pstrUNC : Returns UNC path
  367. Return Value:
  368. TRUE if the path is a network path, FALSE if it is local.
  369. Notes:
  370. Only fully qualified paths with drive letters are checked.
  371. --*/
  372. {
  373. BOOL fUNC = FALSE;
  374. CString strDrive;
  375. try
  376. {
  377. if (pstrDrive == NULL)
  378. {
  379. pstrDrive = &strDrive;
  380. }
  381. ASSERT(strDirPath[1] == _T(':'));
  382. if (strDirPath[1] == _T(':'))
  383. {
  384. *pstrDrive = _T("?:");
  385. //
  386. // Fill in actual drive letter
  387. //
  388. pstrDrive->SetAt(0, strDirPath[0]);
  389. UINT nType = GetDriveType(*pstrDrive);
  390. //
  391. // DRIVE_NO_ROOT_DIR? this is a little dodgy, but
  392. // this is the result I get back after browsing
  393. // using a file open dialog. Weird.
  394. //
  395. fUNC = (nType == DRIVE_NO_ROOT_DIR || nType == DRIVE_REMOTE);
  396. }
  397. //
  398. // Return UNC path if requested
  399. //
  400. if (fUNC && pstrUNC != NULL)
  401. {
  402. DWORD dwSize = _MAX_PATH;
  403. LPTSTR lp = pstrUNC->GetBuffer(dwSize);
  404. ::WNetGetConnection(*pstrDrive, lp, &dwSize);
  405. pstrUNC->ReleaseBuffer();
  406. }
  407. }
  408. catch(CException * e)
  409. {
  410. e->ReportError();
  411. e->Delete();
  412. }
  413. return fUNC;
  414. }
  415. LPCTSTR
  416. MakeUNCPath(
  417. IN OUT CString & strDir,
  418. IN LPCTSTR lpszOwner,
  419. IN LPCTSTR lpszDirectory
  420. )
  421. /*++
  422. Routine Description:
  423. Convert the given directory to a UNC path.
  424. Arguments:
  425. CString & strDir : UNC String.
  426. LPCTSTR lpszOwner : Computer name
  427. LPCTSTR lpszDirectory : Source string
  428. Return Value:
  429. Pointer to strDir
  430. Notes:
  431. The owner may or may not start with "\\". If it doesn't, the
  432. backslashes are provided.
  433. --*/
  434. {
  435. //
  436. // Try to make make a unc path out of the directory
  437. //
  438. ASSERT(lpszDirectory[1] == _T(':'));
  439. strDir.Format(
  440. _T("\\\\%s\\%c$\\%s"),
  441. PURE_COMPUTER_NAME(lpszOwner),
  442. lpszDirectory[0],
  443. lpszDirectory + 3
  444. );
  445. return (LPCTSTR)strDir;
  446. }
  447. BOOL
  448. IsURLName(
  449. IN const CString & strDirPath
  450. )
  451. /*++
  452. Routine Description:
  453. Determine if the given string path is an URL path.
  454. Arguments:
  455. const CString & strDirPath : Directory path string
  456. Return Value:
  457. TRUE if the path is an URL path, FALSE otherwise.
  458. Notes:
  459. Any string of the form protocol://whatever is considered an URL path
  460. --*/
  461. {
  462. if (strDirPath.GetLength() >= 4) // It must be at least as long as x://
  463. { //
  464. if (strDirPath.Find(_T("://")) > 0) // Must contain ://
  465. {
  466. //
  467. // Yes, it's an URL path
  468. //
  469. return TRUE;
  470. }
  471. }
  472. //
  473. // No, it's not
  474. //
  475. return FALSE;
  476. }
  477. int
  478. CStringFindNoCase(
  479. IN const CString & strSrc,
  480. IN LPCTSTR lpszSub
  481. )
  482. /*++
  483. Routine Description:
  484. This should be CString::FindNoCase(). Same as CString::Find(),
  485. but case-insensitive.
  486. Arguments:
  487. const CString & strSrc : Source string
  488. LPCTSTR lpszSub : String to look for.
  489. Return Value:
  490. The position of the substring, or -1 if not found.
  491. --*/
  492. {
  493. LPCTSTR lp1 = strSrc;
  494. LPCTSTR lp2, lp3;
  495. int nPos = -1;
  496. while (*lp1)
  497. {
  498. lp2 = lp1;
  499. lp3 = lpszSub;
  500. while(*lp2 && *lp3 && _totupper(*lp2) == _totupper(*lp3))
  501. {
  502. ++lp2;
  503. ++lp3;
  504. }
  505. if (!*lp3)
  506. {
  507. //
  508. // Found the substring
  509. //
  510. nPos = (int)(lp1 - (LPCTSTR)strSrc);
  511. break;
  512. }
  513. ++lp1;
  514. }
  515. return nPos;
  516. }
  517. DWORD
  518. ReplaceStringInString(
  519. OUT IN CString & strBuffer,
  520. IN CString & strTarget,
  521. IN CString & strReplacement,
  522. IN BOOL fCaseSensitive
  523. )
  524. /*++
  525. Routine Description:
  526. Replace the first occurrence of a string with a second string
  527. inside a third string.
  528. Arguments:
  529. CString & strBuffer : Buffer in which to replace
  530. CString & strTarget : String to look for
  531. CString & strReplacement : String to replace it with
  532. BOOL fCaseSensitive : TRUE for case sensitive replacement.
  533. Return Value:
  534. ERROR_SUCCESS for successful replacement.
  535. ERROR_INVALID_PARAMETER if any string is empty,
  536. ERROR_FILE_NOT_FOUND if the target string doesn't exist, or
  537. another win32 error code indicating failure.
  538. --*/
  539. {
  540. if (strBuffer.IsEmpty() || strTarget.IsEmpty() || strReplacement.IsEmpty())
  541. {
  542. return ERROR_INVALID_PARAMETER;
  543. }
  544. DWORD err = ERROR_FILE_NOT_FOUND;
  545. int nPos = fCaseSensitive
  546. ? strBuffer.Find(strTarget)
  547. : CStringFindNoCase(strBuffer, strTarget);
  548. if (nPos >= 0)
  549. {
  550. try
  551. {
  552. CString str(strBuffer.Left(nPos));
  553. str += strReplacement;
  554. str += strBuffer.Mid(nPos + strTarget.GetLength());
  555. strBuffer = str;
  556. err = ERROR_SUCCESS;
  557. }
  558. catch(CMemoryException * e)
  559. {
  560. e->Delete();
  561. err = ERROR_NOT_ENOUGH_MEMORY;
  562. }
  563. }
  564. return err;
  565. }
  566. DWORD
  567. DeflateEnvironmentVariablePath(
  568. IN LPCTSTR lpszEnvVar,
  569. IN OUT CString & strTarget
  570. )
  571. /*++
  572. Routine Description:
  573. Take a path, and if the path represents a superset of the
  574. path in the environment variable given, replace the relevant
  575. portion of the path with the environment variable.
  576. Arguments:
  577. LPCTSTR lpszEnvVar : Environment variable
  578. CString & strTarget : Path
  579. Return Value:
  580. ERROR_SUCCESS if the replacement was done succesfully,
  581. ERROR_FILE_NOT_FOUND if the path represented by the environment
  582. variable is not contained within the string, or an other win32
  583. error for error conditions
  584. --*/
  585. {
  586. CError err;
  587. CString strEnv;
  588. if (!::GetEnvironmentVariable(
  589. lpszEnvVar,
  590. strEnv.GetBuffer(_MAX_PATH),
  591. _MAX_PATH
  592. ))
  593. {
  594. err.SetLastWinError();
  595. }
  596. strEnv.ReleaseBuffer();
  597. if (err.Succeeded())
  598. {
  599. try
  600. {
  601. CString strReplacement(_T("%"));
  602. strReplacement += lpszEnvVar;
  603. strReplacement += _T("%");
  604. err = ReplaceStringInString(
  605. strTarget,
  606. strEnv,
  607. strReplacement,
  608. FALSE
  609. );
  610. }
  611. catch(CMemoryException * e)
  612. {
  613. err = ERROR_NOT_ENOUGH_MEMORY;
  614. e->Delete();
  615. }
  616. }
  617. return err;
  618. }
  619. LPCTSTR
  620. GUIDToCString(
  621. IN REFGUID guid,
  622. OUT CString & str
  623. )
  624. /*++
  625. Routine Description:
  626. Convert a GUID to a CString, returning pointer to the string
  627. Arguments:
  628. REFGUID : GUID to be converted
  629. CString & str : Output string buffer
  630. Return Value:
  631. Pointer to the string
  632. --*/
  633. {
  634. LPTSTR lpGUID = str.GetBuffer(MAX_PATH);
  635. if (lpGUID)
  636. {
  637. ::StringFromGUID2(guid, lpGUID, MAX_PATH);
  638. str.ReleaseBuffer();
  639. }
  640. return str;
  641. }
  642. int
  643. CountCharsToDoubleNull(
  644. IN LPCTSTR lp
  645. )
  646. /*++
  647. Routine Description:
  648. Count TCHARS up to and including the double NULL.
  649. Arguments:
  650. LPCTSTR lp : TCHAR Stream
  651. Return Value:
  652. Number of chars up to and including the double NULL
  653. --*/
  654. {
  655. int cChars = 0;
  656. for(;;)
  657. {
  658. ++cChars;
  659. if (lp[0] == _T('\0') && lp[1] == _T('\0'))
  660. {
  661. return ++cChars;
  662. }
  663. ++lp;
  664. }
  665. }
  666. DWORD
  667. ConvertDoubleNullListToStringList(
  668. IN LPCTSTR lpstrSrc,
  669. OUT CStringList & strlDest,
  670. IN int cChars OPTIONAL
  671. )
  672. /*++
  673. Routine Description:
  674. Convert a double null terminate list of null terminated strings to a more
  675. manageable CStringListEx
  676. Arguments:
  677. LPCTSTR lpstrSrc : Source list of strings
  678. CStringList & strlDest : Destination string list.
  679. int cChars : Number of characters in double NULL list. if
  680. -1, autodetermine length
  681. Return Value:
  682. ERROR_SUCCESS if the list was converted properly
  683. ERROR_INVALID_PARAMETER if the list was empty
  684. ERROR_NOT_ENOUGH_MEMORY if there was a mem exception
  685. --*/
  686. {
  687. DWORD err = ERROR_SUCCESS;
  688. if (lpstrSrc == NULL)
  689. {
  690. return ERROR_INVALID_PARAMETER;
  691. }
  692. if (cChars < 0)
  693. {
  694. //
  695. // Calculate our own size. This might be off if multiple
  696. // blank linkes (0) appear in the multi_sz, so the character
  697. // size is definitely preferable
  698. //
  699. cChars = CountCharsToDoubleNull(lpstrSrc);
  700. }
  701. try
  702. {
  703. strlDest.RemoveAll();
  704. if (cChars == 2 && *lpstrSrc == _T('\0'))
  705. {
  706. //
  707. // Special case: MULTI_SZ containing only
  708. // a double NULL are in fact blank entirely.
  709. //
  710. // N.B. IMHO this is a metabase bug -- RonaldM
  711. //
  712. --cChars;
  713. }
  714. //
  715. // Grab strings until only the final NULL remains
  716. //
  717. while (cChars > 1)
  718. {
  719. CString strTmp = lpstrSrc;
  720. strlDest.AddTail(strTmp);
  721. lpstrSrc += (strTmp.GetLength() + 1);
  722. cChars -= (strTmp.GetLength() + 1);
  723. }
  724. }
  725. catch(CMemoryException * e)
  726. {
  727. TRACEEOLID("!!! exception building stringlist");
  728. err = ERROR_NOT_ENOUGH_MEMORY;
  729. e->Delete();
  730. }
  731. return err;
  732. }
  733. DWORD
  734. ConvertStringListToDoubleNullList(
  735. IN CStringList & strlSrc,
  736. OUT DWORD & cchDest,
  737. OUT LPTSTR & lpstrDest
  738. )
  739. /*++
  740. Routine Description:
  741. Flatten the string list into a double null terminated list
  742. of null terminated strings.
  743. Arguments:
  744. CStringList & strlSrc : Source string list
  745. DWORD & cchDest : Size in characters of the resultant array
  746. (including terminating NULLs)
  747. LPTSTR & lpstrDest : Allocated flat array.
  748. Return Value:
  749. ERROR_SUCCESS if the list was converted properly
  750. ERROR_INVALID_PARAMETER if the list was empty
  751. ERROR_NOT_ENOUGH_MEMORY if there was a mem exception
  752. --*/
  753. {
  754. cchDest = 0;
  755. lpstrDest = NULL;
  756. BOOL fNullPad = FALSE;
  757. //
  758. // Compute total size in characters
  759. //
  760. POSITION pos;
  761. for(pos = strlSrc.GetHeadPosition(); pos != NULL; /**/ )
  762. {
  763. CString & str = strlSrc.GetNext(pos);
  764. TRACEEOLID(str);
  765. cchDest += str.GetLength() + 1;
  766. }
  767. if (!cchDest)
  768. {
  769. //
  770. // Special case: A totally empty MULTI_SZ
  771. // in fact consists of 2 (final) NULLS, instead
  772. // of 1 (final) NULL. This is required by the
  773. // metabase, but should be a bug. See note
  774. // at reversal function above.
  775. //
  776. ++cchDest;
  777. fNullPad = TRUE;
  778. }
  779. //
  780. // Remember final NULL
  781. //
  782. cchDest += 1;
  783. lpstrDest = AllocTString(cchDest);
  784. if (lpstrDest == NULL)
  785. {
  786. return ERROR_NOT_ENOUGH_MEMORY;
  787. }
  788. LPTSTR pch = lpstrDest;
  789. for(pos = strlSrc.GetHeadPosition(); pos != NULL; /**/ )
  790. {
  791. CString & str = strlSrc.GetNext(pos);
  792. lstrcpy(pch, (LPCTSTR)str);
  793. pch += str.GetLength();
  794. *pch++ = _T('\0');
  795. }
  796. *pch++ = _T('\0');
  797. if (fNullPad)
  798. {
  799. *pch++ = _T('\0');
  800. }
  801. return ERROR_SUCCESS;
  802. }
  803. int
  804. ConvertSepLineToStringList(
  805. IN LPCTSTR lpstrIn,
  806. OUT CStringList & strlOut,
  807. IN LPCTSTR lpstrSep
  808. )
  809. /*++
  810. Routine Description:
  811. Convert a line containing multiple strings separated by
  812. a given character to a CStringListEx
  813. Arguments:
  814. LPCTSTR lpstrIn : Input line
  815. CStringListEx & strlOut : Output stringlist
  816. LPCTSTR lpstrSep : List of separators
  817. Return Value:
  818. The number of items added
  819. --*/
  820. {
  821. int cItems = 0;
  822. strlOut.RemoveAll();
  823. try
  824. {
  825. CString strSrc(lpstrIn);
  826. LPTSTR lp = strSrc.GetBuffer(0);
  827. lp = _tcstok(lp, lpstrSep);
  828. while (lp)
  829. {
  830. CString str(lp);
  831. strlOut.AddTail(str);
  832. lp = _tcstok(NULL, lpstrSep);
  833. ++cItems;
  834. }
  835. }
  836. catch(CMemoryException * e)
  837. {
  838. TRACEEOLID("Exception converting CSV list to stringlist");
  839. e->ReportError();
  840. e->Delete();
  841. }
  842. return cItems;
  843. }
  844. LPCTSTR
  845. ConvertStringListToSepLine(
  846. IN CStringList & strlIn,
  847. OUT CString & strOut,
  848. IN LPCTSTR lpstrSep
  849. )
  850. /*++
  851. Routine Description:
  852. Convert stringlist into a single CString, each entry separated by the given
  853. separator string.
  854. Arguments:
  855. CStringListEx & strlIn : Input stringlist
  856. CString & strOut : Output string
  857. LPCTSTR lpstrSep : Separator string
  858. Return Value:
  859. Pointer to the output string.
  860. --*/
  861. {
  862. strOut.Empty();
  863. POSITION pos = strlIn.GetHeadPosition();
  864. while(pos)
  865. {
  866. CString & str = strlIn.GetNext(pos);
  867. strOut += str;
  868. strOut += lpstrSep;
  869. }
  870. //
  871. // Fix for bug #286824
  872. // Remove separator from the last line: it looks ugly in edit control
  873. //
  874. if (!strOut.IsEmpty())
  875. {
  876. strOut.GetBufferSetLength(strOut.GetLength() - 1);
  877. strOut.ReleaseBuffer();
  878. }
  879. return strOut;
  880. }
  881. BOOL
  882. CStringListEx::operator ==(
  883. IN const CStringList & strl
  884. )
  885. /*++
  886. Routine Description:
  887. Compare against CStringList. In order for two CStringLists to match,
  888. they must match in every element, which must be in the same order.
  889. Arguments:
  890. CStringList strl : String list to compare against.
  891. Return Value:
  892. TRUE if the two string lists are identical
  893. --*/
  894. {
  895. if (strl.GetCount() != GetCount())
  896. {
  897. return FALSE;
  898. }
  899. POSITION posa = strl.GetHeadPosition();
  900. POSITION posb = GetHeadPosition();
  901. while (posa)
  902. {
  903. ASSERT(posa);
  904. ASSERT(posb);
  905. CString & strA = strl.GetNext(posa);
  906. CString & strB = GetNext(posb);
  907. if (strA != strB)
  908. {
  909. return FALSE;
  910. }
  911. }
  912. return TRUE;
  913. }
  914. CStringListEx &
  915. CStringListEx::operator =(
  916. IN const CStringList & strl
  917. )
  918. /*++
  919. Routine Description:
  920. Assignment operator
  921. Arguments:
  922. const CStringList & strl : Source stringlist
  923. Return Value:
  924. Reference to this
  925. --*/
  926. {
  927. RemoveAll();
  928. POSITION pos = strl.GetHeadPosition();
  929. while(pos)
  930. {
  931. CString & str = strl.GetNext(pos);
  932. AddTail(str);
  933. }
  934. return *this;
  935. }
  936. BOOL
  937. CvtStringToLong(
  938. IN LPCTSTR lpNumber,
  939. OUT DWORD * pdwValue
  940. )
  941. /*++
  942. Routine Description:
  943. Helper function to convert string (hex or decimal) to a dword.
  944. Arguments:
  945. LPCTSTR lpNumber : Input number
  946. DWORD * pdwValue : Returns the value
  947. Return Value:
  948. TRUE for success, FALSE for failure
  949. --*/
  950. {
  951. const TCHAR rgchHex[] = _T("00112233445566778899aAbBcCdDeEfF");
  952. DWORD dwResult = 0L;
  953. DWORD dwResultPrev = 0L;
  954. //
  955. // Assume a decimal base
  956. //
  957. DWORD dwBase = 10L;
  958. ASSERT_READ_PTR(lpNumber);
  959. ASSERT_WRITE_PTR(pdwValue);
  960. while (*lpNumber == _T(' ') || *lpNumber == _T('0'))
  961. {
  962. ++lpNumber;
  963. }
  964. if (*lpNumber == _T('x') || *lpNumber == _T('X'))
  965. {
  966. dwBase = 16L;
  967. ++lpNumber;
  968. }
  969. while (*lpNumber)
  970. {
  971. DWORD dwDigit;
  972. //
  973. // Search the character in the hexadecimal string
  974. //
  975. LPCTSTR pchDigit = _tcschr(rgchHex, *lpNumber);
  976. if (!pchDigit)
  977. {
  978. //
  979. // Character is not found
  980. //
  981. return FALSE;
  982. }
  983. dwDigit = DIFF(pchDigit - rgchHex) >> 1;
  984. if (dwDigit >= dwBase)
  985. {
  986. //
  987. // Hexadecimal character in a decimal integer
  988. //
  989. return FALSE;
  990. }
  991. dwResultPrev = dwResult;
  992. dwResult *= dwBase;
  993. dwResult += dwDigit;
  994. if (dwResult < dwResultPrev)
  995. {
  996. //
  997. // Overflow
  998. //
  999. return FALSE;
  1000. }
  1001. //
  1002. // Parse the next character
  1003. //
  1004. ++lpNumber;
  1005. }
  1006. *pdwValue = dwResult;
  1007. return TRUE;
  1008. }
  1009. const LPCTSTR g_cszMonths[] =
  1010. {
  1011. _T("Jan"),
  1012. _T("Feb"),
  1013. _T("Mar"),
  1014. _T("Apr"),
  1015. _T("May"),
  1016. _T("Jun"),
  1017. _T("Jul"),
  1018. _T("Aug"),
  1019. _T("Sep"),
  1020. _T("Oct"),
  1021. _T("Nov"),
  1022. _T("Dec"),
  1023. };
  1024. const LPCTSTR g_cszWeekDays[] =
  1025. {
  1026. _T("Sun"),
  1027. _T("Mon"),
  1028. _T("Tue"),
  1029. _T("Wed"),
  1030. _T("Thu"),
  1031. _T("Fri"),
  1032. _T("Sat"),
  1033. };
  1034. inline BOOL SkipTillDigit(LPCTSTR & lp)
  1035. {
  1036. while (lp && *lp && !_istdigit(*lp)) ++lp;
  1037. return lp != NULL;
  1038. }
  1039. inline BOOL SkipPastDigits(LPCTSTR & lp)
  1040. {
  1041. while (lp && *lp && _istdigit(*lp)) ++lp;
  1042. return lp != NULL;
  1043. }
  1044. BOOL
  1045. FetchIntField(
  1046. LPCTSTR & lp,
  1047. int & n
  1048. )
  1049. {
  1050. if (SkipTillDigit(lp))
  1051. {
  1052. n = _ttoi(lp);
  1053. if (n < 0)
  1054. {
  1055. ASSERT_MSG("Bogus string->int");
  1056. return FALSE;
  1057. }
  1058. return SkipPastDigits(lp);
  1059. }
  1060. return FALSE;
  1061. }
  1062. BOOL
  1063. MatchString(
  1064. LPCTSTR lpTarget,
  1065. const LPCTSTR * rglp,
  1066. int cElements,
  1067. int & idx
  1068. )
  1069. {
  1070. for (idx = 0; idx < cElements; ++idx)
  1071. {
  1072. if (!_tcsnicmp(lpTarget, rglp[idx], _tcslen(rglp[idx])))
  1073. {
  1074. return TRUE;
  1075. }
  1076. }
  1077. return FALSE;
  1078. }
  1079. static g_dwCurrentTimeZone = TIME_ZONE_ID_INVALID;
  1080. static TIME_ZONE_INFORMATION g_tzInfo;
  1081. BOOL
  1082. CvtGMTStringToInternal(
  1083. IN LPCTSTR lpTime,
  1084. OUT time_t * ptValue
  1085. )
  1086. /*++
  1087. Routine Description:
  1088. Convert GMT string to time in the local timezone format
  1089. Arguments:
  1090. IN LPCSTSTR lpTime : Input time string
  1091. OUT time_t * ptValue : Output time_t
  1092. Return Value:
  1093. TRUE for success, FALSE for failure
  1094. --*/
  1095. {
  1096. ASSERT_READ_PTR(lpTime);
  1097. ASSERT_WRITE_PTR(ptValue);
  1098. int year, month, day, hour, minutes, seconds;
  1099. do
  1100. {
  1101. if (!lpTime || !ptValue)
  1102. {
  1103. break;
  1104. }
  1105. if (!FetchIntField(lpTime, day))
  1106. {
  1107. break;
  1108. }
  1109. ++lpTime; // Skip separator
  1110. if (!MatchString(lpTime, g_cszMonths, 12, month))
  1111. {
  1112. break;
  1113. }
  1114. ++month; // 0-based.
  1115. if (!FetchIntField(lpTime, year) ||
  1116. !FetchIntField(lpTime, hour) ||
  1117. !FetchIntField(lpTime, minutes) ||
  1118. !FetchIntField(lpTime, seconds))
  1119. {
  1120. break;
  1121. }
  1122. if (year < 100)
  1123. {
  1124. year += ((year < 50) ? 2000 : 1900);
  1125. }
  1126. CTime time(year, month, day, hour, minutes, seconds);
  1127. if (time.GetTime() == (time_t)-1L)
  1128. {
  1129. break;
  1130. }
  1131. *ptValue = time.GetTime();
  1132. TRACEEOLID("GMT Time is " << ctime(ptValue));
  1133. //
  1134. // Adjust for local time zone
  1135. //
  1136. RaiseThreadProtection();
  1137. if (g_dwCurrentTimeZone == TIME_ZONE_ID_INVALID)
  1138. {
  1139. //
  1140. // GetTimeZoneInfo() is expensive, so cache its output
  1141. //
  1142. g_dwCurrentTimeZone = GetTimeZoneInformation(&g_tzInfo);
  1143. }
  1144. LowerThreadProtection();
  1145. if (g_dwCurrentTimeZone != TIME_ZONE_ID_INVALID)
  1146. {
  1147. _tzset();
  1148. struct tm * tb = localtime(ptValue);
  1149. BOOL fDST = tb->tm_isdst;
  1150. RaiseThreadProtection();
  1151. *ptValue -= (g_tzInfo.Bias * 60L);
  1152. TRACEEOLID("local time (w/o dst adjustment) is " << ctime(ptValue));
  1153. if (fDST)
  1154. {
  1155. *ptValue -= (g_tzInfo.DaylightBias * 60L);
  1156. }
  1157. else
  1158. {
  1159. //
  1160. // Almost always this is zero
  1161. //
  1162. *ptValue -= (g_tzInfo.StandardBias * 60L);
  1163. }
  1164. LowerThreadProtection();
  1165. TRACEEOLID("Local time adjusted for dst is " << ctime(ptValue));
  1166. }
  1167. //
  1168. // Time OK
  1169. //
  1170. return TRUE;
  1171. }
  1172. while(FALSE);
  1173. //
  1174. // Set max abs timeout value.
  1175. //
  1176. CTime time(2037, 12, 31, 0, 0, 0);
  1177. *ptValue = (time_t)time.GetTime();
  1178. return FALSE;
  1179. }
  1180. void
  1181. CvtInternalToGMTString(
  1182. IN time_t tm,
  1183. OUT CString & str
  1184. )
  1185. /*++
  1186. Routine Description:
  1187. Reverse of the above function. Convert internal time_t to a GMT
  1188. time string (converting to GM time in the process)
  1189. Arguments:
  1190. time_t tm : Input time_t
  1191. CString & str : Out CString
  1192. Return Value:
  1193. None
  1194. --*/
  1195. {
  1196. try
  1197. {
  1198. _tzset();
  1199. struct tm * tb = gmtime(&tm);
  1200. str.Format(
  1201. _T("%-3.3s, %02d %-3.3s %4d %02d:%02d:%02d GMT"),
  1202. g_cszWeekDays[tb->tm_wday],
  1203. tb->tm_mday,
  1204. g_cszMonths[tb->tm_mon],
  1205. tb->tm_year + 1900,
  1206. tb->tm_hour,
  1207. tb->tm_min,
  1208. tb->tm_sec
  1209. );
  1210. TRACEEOLID("Time string is " << str);
  1211. }
  1212. catch(CMemoryException * e)
  1213. {
  1214. e->ReportError();
  1215. e->Delete();
  1216. }
  1217. }
  1218. //
  1219. // International numeric strings
  1220. //
  1221. // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  1222. //
  1223. // Initialize library
  1224. //
  1225. BOOL
  1226. InitIntlSettings()
  1227. {
  1228. #ifdef _MT
  1229. INITIALIZE_CRITICAL_SECTION(&_csSect);
  1230. #endif // _MT
  1231. return CINumber::Allocate();
  1232. }
  1233. //
  1234. // De-initialize library
  1235. //
  1236. void
  1237. TerminateIntlSettings()
  1238. {
  1239. CINumber::DeAllocate();
  1240. #ifdef _MT
  1241. DeleteCriticalSection(&_csSect);
  1242. #endif // _MT
  1243. }
  1244. //
  1245. // Static Member Initialization
  1246. //
  1247. // NOTE: MFC classes CANNOT be global in an AFX extension, as they
  1248. // will not be initialized properly. Solution is to allocate them.
  1249. //
  1250. CString * CINumber::_pstrThousandSeparator = NULL;
  1251. CString * CINumber::_pstrDecimalPoint = NULL;
  1252. CString * CINumber::_pstrBadNumber = NULL;
  1253. CString * CINumber::_pstrCurrency = NULL;
  1254. CString * CINumber::_pstr = NULL;
  1255. BOOL CINumber::_fAllocated = FALSE;
  1256. BOOL CINumber::_fCurrencyPrefix = TRUE;
  1257. BOOL CINumber::_fInitialized = FALSE;
  1258. CINumber::CINumber()
  1259. /*++
  1260. Routine Description:
  1261. Constructor
  1262. Arguments:
  1263. None
  1264. Return Value:
  1265. None
  1266. --*/
  1267. {
  1268. if (!CINumber::_fInitialized)
  1269. {
  1270. CINumber::Initialize();
  1271. }
  1272. }
  1273. CINumber::~CINumber()
  1274. /*++
  1275. Routine Description:
  1276. Destructor
  1277. Arguments:
  1278. None
  1279. Return Value:
  1280. None
  1281. --*/
  1282. {
  1283. }
  1284. /* static */
  1285. BOOL
  1286. CINumber::Allocate()
  1287. /*++
  1288. Routine Description:
  1289. Allocate with US settings
  1290. Arguments:
  1291. None
  1292. Return Value:
  1293. TRUE if allocation was successfull, FALSE otherwise
  1294. --*/
  1295. {
  1296. RaiseThreadProtection();
  1297. ASSERT(!IsAllocated());
  1298. if (!IsAllocated())
  1299. {
  1300. try
  1301. {
  1302. //
  1303. // NOTE: Cannot use globals for these, because
  1304. // they will not be initialized properly
  1305. // when used in an extension dll.
  1306. //
  1307. CINumber::_pstrThousandSeparator = new CString(_T(","));
  1308. CINumber::_pstrDecimalPoint = new CString(_T("."));
  1309. CINumber::_pstrBadNumber = new CString(_T("--"));
  1310. CINumber::_pstrCurrency = new CString(_T("$ "));
  1311. CINumber::_pstr = new CString;
  1312. _fAllocated = TRUE;
  1313. }
  1314. catch(CMemoryException * e)
  1315. {
  1316. TRACEEOLID("Initialization Failed");
  1317. e->ReportError();
  1318. e->Delete();
  1319. }
  1320. }
  1321. LowerThreadProtection();
  1322. return IsAllocated();
  1323. }
  1324. /* static */
  1325. void
  1326. CINumber::DeAllocate()
  1327. /*++
  1328. Routine Description:
  1329. Clean up allocations
  1330. Arguments:
  1331. N/A
  1332. Return Value:
  1333. N/A
  1334. --*/
  1335. {
  1336. RaiseThreadProtection();
  1337. ASSERT(IsAllocated());
  1338. if (IsAllocated())
  1339. {
  1340. MAKE_NULL(CINumber::_pstrThousandSeparator);
  1341. MAKE_NULL(CINumber::_pstrDecimalPoint);
  1342. MAKE_NULL(CINumber::_pstrBadNumber);
  1343. MAKE_NULL(CINumber::_pstrCurrency);
  1344. MAKE_NULL(CINumber::_pstr);
  1345. }
  1346. LowerThreadProtection();
  1347. _fAllocated = FALSE;
  1348. }
  1349. /* static */
  1350. BOOL
  1351. CINumber::Initialize(
  1352. IN BOOL fUserSetting /* TRUE */
  1353. )
  1354. /*++
  1355. Routine Description:
  1356. Initialize all the international settings, such as thousand
  1357. separators and decimal points
  1358. Parameters:
  1359. BOOL fUserSetting If TRUE, use current user settings,
  1360. if FALSE use system settings.
  1361. Return Value:
  1362. TRUE for success, FALSE for failure
  1363. Notes:
  1364. Note that this function only needs to be explicitly called
  1365. when the country settings have changed, or when system
  1366. settings are desired (user is default)
  1367. --*/
  1368. {
  1369. #define MAXLEN 6
  1370. int cErrors = 0;
  1371. TRACEEOLID("Getting locale-dependend information");
  1372. ASSERT(IsAllocated());
  1373. if (!IsAllocated())
  1374. {
  1375. Allocate();
  1376. }
  1377. RaiseThreadProtection();
  1378. try
  1379. {
  1380. LCID lcid = fUserSetting
  1381. ? ::GetUserDefaultLCID()
  1382. : GetSystemDefaultLCID();
  1383. LCTYPE lctype = fUserSetting ? 0 : LOCALE_NOUSEROVERRIDE;
  1384. //
  1385. // Get Decimal Point
  1386. //
  1387. if (!::GetLocaleInfo(
  1388. lcid,
  1389. LOCALE_SDECIMAL | lctype,
  1390. CINumber::_pstrDecimalPoint->GetBuffer(MAXLEN),
  1391. MAXLEN
  1392. ))
  1393. {
  1394. TRACEEOLID("Unable to get intl decimal point");
  1395. ++cErrors;
  1396. }
  1397. CINumber::_pstrDecimalPoint->ReleaseBuffer();
  1398. //
  1399. // Get Thousand Separator
  1400. //
  1401. if (!::GetLocaleInfo(
  1402. lcid, LOCALE_STHOUSAND | lctype,
  1403. CINumber::_pstrThousandSeparator->GetBuffer(MAXLEN),
  1404. MAXLEN
  1405. ))
  1406. {
  1407. TRACEEOLID("Unable to get 1000 separator");
  1408. ++cErrors;
  1409. }
  1410. CINumber::_pstrThousandSeparator->ReleaseBuffer();
  1411. #ifndef _UNICODE
  1412. //
  1413. // Some countries have a space as a 1000 separator,
  1414. // but for some reason, this is ansi 160, which
  1415. // shows up as a space fine on windows apps,
  1416. // looks like garbage on console apps.
  1417. //
  1418. if ((*CINumber::_pstrThousandSeparator)[0] == CHAR(160))
  1419. {
  1420. CINumber::_pstrThousandSeparator->SetAt(0, ' ');
  1421. TRACEEOLID("Space 1000 separator substituted");
  1422. }
  1423. #endif // _UNICODE
  1424. //
  1425. // Get currency symbol
  1426. //
  1427. if (!::GetLocaleInfo(
  1428. lcid,
  1429. LOCALE_SCURRENCY | lctype,
  1430. CINumber::_pstrCurrency->GetBuffer(MAXLEN),
  1431. MAXLEN
  1432. ))
  1433. {
  1434. TRACEEOLID("Unable to get currency symbol");
  1435. ++cErrors;
  1436. }
  1437. CINumber::_pstrCurrency->ReleaseBuffer();
  1438. }
  1439. catch(CMemoryException * e)
  1440. {
  1441. TRACEEOLID("!!!exception in getting intl settings:");
  1442. e->ReportError();
  1443. e->Delete();
  1444. ++cErrors;
  1445. }
  1446. TRACEEOLID("Thousand Separator . . . . . : " << *CINumber::_pstrThousandSeparator);
  1447. TRACEEOLID("Decimal Point . . . . . . . : " << *CINumber::_pstrDecimalPoint);
  1448. TRACEEOLID("Currency Symbol. . . . . . . : " << *CINumber::_pstrCurrency);
  1449. TRACEEOLID("Bad number . . . . . . . . . : " << *CINumber::_pstrBadNumber);
  1450. TRACEEOLID("Currency Prefix. . . . . . . : " << CINumber::_fCurrencyPrefix);
  1451. CINumber::_fInitialized = TRUE;
  1452. LowerThreadProtection();
  1453. return cErrors == 0;
  1454. }
  1455. /* static */
  1456. double
  1457. CINumber::BuildFloat(
  1458. IN const LONG lInteger,
  1459. IN const LONG lFraction
  1460. )
  1461. /*++
  1462. Return Value:
  1463. Combine integer and fraction to form float
  1464. Parameters:
  1465. const LONG lInteger Integer portion
  1466. const LONG lFraction Fractional portion
  1467. Return Value:
  1468. float value
  1469. --*/
  1470. {
  1471. double flValue = 0.0;
  1472. //
  1473. // Negative fractions?
  1474. //
  1475. ASSERT(lFraction >= 0);
  1476. if (lFraction >= 0)
  1477. {
  1478. flValue = (double)lFraction;
  1479. while (flValue >= 1.0)
  1480. {
  1481. flValue /= 10.0;
  1482. }
  1483. //
  1484. // Re-add (or subtract if the original number
  1485. // was negative) the fractional part
  1486. //
  1487. if (lInteger > 0L)
  1488. {
  1489. flValue += (double)lInteger;
  1490. }
  1491. else
  1492. {
  1493. flValue -= (double)lInteger;
  1494. flValue = -flValue;
  1495. }
  1496. }
  1497. return flValue;
  1498. }
  1499. /* static */
  1500. LPCTSTR
  1501. CINumber::ConvertLongToString(
  1502. IN const LONG lSrc,
  1503. OUT CString & str
  1504. )
  1505. /*++
  1506. CINumber::ConvertLongToString
  1507. Purpose:
  1508. Convert long number to string with 1000 separators
  1509. Parameters:
  1510. const LONG lSrc Source number
  1511. CString & str String to write to
  1512. Return Value:
  1513. Pointer to converted string
  1514. --*/
  1515. {
  1516. LPTSTR lpOutString = str.GetBuffer(16);
  1517. //
  1518. // Forget about the negative sign for now.
  1519. //
  1520. LONG lNum = (lSrc >= 0L) ? lSrc : -lSrc;
  1521. int outstrlen = 0;
  1522. do
  1523. {
  1524. lpOutString[outstrlen++] = _T('0') + (TCHAR)(lNum % 10L);
  1525. lNum /= 10L;
  1526. //
  1527. // if more digits left and we're on a 1000 boundary (printed 3 digits,
  1528. // or 3 digits + n*(3 digits + 1 comma), then print a 1000 separator.
  1529. // Note: will only work if the 1000 separator is 1 character.
  1530. //
  1531. ASSERT(CINumber::_pstrThousandSeparator->GetLength() == 1);
  1532. if (lNum != 0L && (outstrlen == 3 || outstrlen == 7 || outstrlen == 11))
  1533. {
  1534. lstrcpy(lpOutString + outstrlen, *CINumber::_pstrThousandSeparator);
  1535. outstrlen += CINumber::_pstrThousandSeparator->GetLength();
  1536. }
  1537. }
  1538. while (lNum > 0L);
  1539. //
  1540. // Add a negative sign if necessary.
  1541. //
  1542. if (lSrc < 0L)
  1543. {
  1544. lpOutString[outstrlen++] = _T('-');
  1545. }
  1546. str.ReleaseBuffer(outstrlen);
  1547. str.MakeReverse();
  1548. return (LPCTSTR)str;
  1549. }
  1550. /* static */
  1551. LPCTSTR
  1552. CINumber::ConvertFloatToString(
  1553. IN const double flSrc,
  1554. IN int nPrecision,
  1555. OUT CString & str
  1556. )
  1557. /*++
  1558. Routine Description:
  1559. Convert floating point number to string represenation
  1560. Parameters:
  1561. const double flSrc Source floating point number
  1562. int nPrecision Number of decimal points
  1563. CString & str String to convert to
  1564. Return Value:
  1565. Pointer to converted string.
  1566. --*/
  1567. {
  1568. //
  1569. // Forget about the negative sign for now,
  1570. // and the fractional portion.
  1571. //
  1572. TCHAR szFraction[256];
  1573. LPCTSTR lpFraction = NULL;
  1574. ::_stprintf(szFraction, _T("%.*f"), nPrecision, flSrc);
  1575. lpFraction = ::_tcschr(szFraction, _T('.') );
  1576. ASSERT(lpFraction != NULL);
  1577. ++lpFraction;
  1578. CINumber::ConvertLongToString((LONG)flSrc, str);
  1579. str += *CINumber::_pstrDecimalPoint + lpFraction;
  1580. return (LPCTSTR)str;
  1581. }
  1582. /* static */
  1583. BOOL
  1584. CINumber::ConvertStringToLong(
  1585. IN LPCTSTR lpsrc,
  1586. OUT LONG & lValue
  1587. )
  1588. /*++
  1589. Routine Description:
  1590. Convert string to long integer. 1000 Separators will be treated
  1591. correctly.
  1592. Parameters:
  1593. LPCTSTR lpsrc Source string
  1594. LONG & lValue Value to convert to. Will be 0 in case of error
  1595. Return Value:
  1596. TRUE for success, FALSE for failure.
  1597. --*/
  1598. {
  1599. CString strNumber(lpsrc);
  1600. LONG lBase = 1L;
  1601. BOOL fNegative = FALSE;
  1602. lValue = 0L;
  1603. //
  1604. // Empty strings are invalid
  1605. //
  1606. if (strNumber.IsEmpty())
  1607. {
  1608. return FALSE;
  1609. }
  1610. //
  1611. // Check for negative sign (at the end only)
  1612. //
  1613. if (strNumber[0] == _T('-'))
  1614. {
  1615. fNegative = TRUE;
  1616. }
  1617. strNumber.MakeReverse();
  1618. //
  1619. // Strip negative sign
  1620. //
  1621. if (fNegative)
  1622. {
  1623. strNumber.ReleaseBuffer(strNumber.GetLength()-1);
  1624. }
  1625. //
  1626. // Make sure the 1000 separator is only 1 char. See note below
  1627. //
  1628. ASSERT(CINumber::_pstrThousandSeparator->GetLength() == 1);
  1629. for (int i = 0; i < strNumber.GetLength(); ++i)
  1630. {
  1631. if ((strNumber[i] >= _T('0')) && (strNumber[i] <= _T('9')))
  1632. {
  1633. LONG lDigit = (LONG)(strNumber[i] - _T('0'));
  1634. if (lDigit != 0L)
  1635. {
  1636. LONG lOldValue = lValue;
  1637. LONG lDelta = (lDigit * lBase);
  1638. if (lDelta / lDigit != lBase)
  1639. {
  1640. TRACEEOLID("Overflow!");
  1641. lValue = 0x7fffffff;
  1642. return FALSE;
  1643. }
  1644. lValue += lDelta;
  1645. if (lValue - lDelta != lOldValue)
  1646. {
  1647. TRACEEOLID("Overflow!");
  1648. lValue = 0x7fffffff;
  1649. return FALSE;
  1650. }
  1651. }
  1652. lBase *= 10L;
  1653. }
  1654. //
  1655. // It's not a digit, maybe a thousand separator?
  1656. // CAVEAT: If a thousand separator of more than
  1657. // one character is used, this won't work.
  1658. //
  1659. else if ((strNumber[i] != (*CINumber::_pstrThousandSeparator)[0])
  1660. || (i != 3) && (i != 7) && (i != 11))
  1661. {
  1662. //
  1663. // This is just invalid, since it is not a thousand
  1664. // separator in the proper location, nor a negative
  1665. // sign.
  1666. //
  1667. TRACEEOLID("Invalid character " << (BYTE)strNumber[i] << " encountered");
  1668. return FALSE;
  1669. }
  1670. }
  1671. if (fNegative)
  1672. {
  1673. lValue = -lValue;
  1674. }
  1675. return TRUE;
  1676. }
  1677. /* static */
  1678. BOOL
  1679. CINumber::ConvertStringToFloat(
  1680. IN LPCTSTR lpsrc,
  1681. OUT double & flValue
  1682. )
  1683. /*++
  1684. Routine Description:
  1685. Convert fully decorated floating point string to double
  1686. Parameters:
  1687. LPCTSTR lpsrc Source string
  1688. double & flValue float value generated from string
  1689. Return Value:
  1690. TRUE for success, FALSE for failure
  1691. --*/
  1692. {
  1693. CString strNumber(lpsrc);
  1694. //
  1695. // This only works if the decimal point is a
  1696. // single character
  1697. //
  1698. ASSERT(CINumber::_pstrDecimalPoint->GetLength() == 1);
  1699. //
  1700. // Strip off the > 0 part
  1701. //
  1702. LONG lFraction = 0;
  1703. int nPoint = strNumber.ReverseFind((*CINumber::_pstrDecimalPoint)[0]);
  1704. if (nPoint >= 0)
  1705. {
  1706. //
  1707. // Convert fractional part
  1708. //
  1709. LPCTSTR lpszFraction = (LPCTSTR)strNumber + ++nPoint;
  1710. lFraction = ::_ttol(lpszFraction);
  1711. strNumber.ReleaseBuffer(--nPoint);
  1712. }
  1713. //
  1714. // Convert integer part
  1715. //
  1716. LONG lInteger;
  1717. if (ConvertStringToLong(strNumber, lInteger))
  1718. {
  1719. flValue = CINumber::BuildFloat(lInteger, lFraction);
  1720. return TRUE;
  1721. }
  1722. return FALSE;
  1723. }
  1724. CILong::CILong()
  1725. /*++
  1726. Routine Description:
  1727. Constructor without arguments
  1728. Parameters:
  1729. None.
  1730. Return Value:
  1731. N/A
  1732. --*/
  1733. : m_lValue(0L)
  1734. {
  1735. }
  1736. CILong::CILong(
  1737. IN LONG lValue
  1738. )
  1739. /*++
  1740. Routine Description:
  1741. Constructor taking LONG argument
  1742. Parameters:
  1743. LONG lValue Value to be set
  1744. Return Value:
  1745. N/A
  1746. --*/
  1747. : m_lValue(lValue)
  1748. {
  1749. }
  1750. CILong::CILong(
  1751. IN LPCTSTR lpszValue
  1752. )
  1753. /*++
  1754. Routine Description:
  1755. Constructor taking string argument
  1756. Parameters:
  1757. LPCTSTR lpszValue String number
  1758. Return Value:
  1759. N/A
  1760. --*/
  1761. {
  1762. ConvertStringToLong(lpszValue, m_lValue);
  1763. }
  1764. CILong &
  1765. CILong::operator =(
  1766. IN LONG lValue
  1767. )
  1768. /*++
  1769. Routine Description:
  1770. Assignment operator taking long value
  1771. Parameters:
  1772. LONG lValue Value to be set
  1773. Return Value:
  1774. this object
  1775. --*/
  1776. {
  1777. m_lValue = lValue;
  1778. return *this;
  1779. }
  1780. CILong &
  1781. CILong::operator =(
  1782. IN LPCTSTR lpszValue
  1783. )
  1784. /*++
  1785. Routine Description:
  1786. Assignment operator taking string value
  1787. Parameters:
  1788. LPCTSTR lpszValue String number
  1789. Return Value:
  1790. this object
  1791. --*/
  1792. {
  1793. ConvertStringToLong(lpszValue, m_lValue);
  1794. return *this;
  1795. }
  1796. //
  1797. // Arithmetic Shorthand operators
  1798. //
  1799. // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  1800. CILong &
  1801. CILong::operator +=(
  1802. IN const LONG lValue
  1803. )
  1804. {
  1805. m_lValue += lValue;
  1806. return *this;
  1807. }
  1808. //
  1809. // As above
  1810. //
  1811. CILong &
  1812. CILong::operator +=(
  1813. IN const LPCTSTR lpszValue
  1814. )
  1815. {
  1816. LONG lValue;
  1817. ConvertStringToLong(lpszValue, lValue);
  1818. m_lValue += lValue;
  1819. return *this;
  1820. }
  1821. //
  1822. // As above
  1823. //
  1824. CILong &
  1825. CILong::operator +=(
  1826. IN const CILong& value
  1827. )
  1828. {
  1829. m_lValue += value.m_lValue;
  1830. return *this;
  1831. }
  1832. //
  1833. // As above
  1834. //
  1835. CILong &
  1836. CILong::operator -=(
  1837. IN const LONG lValue
  1838. )
  1839. {
  1840. m_lValue -= lValue;
  1841. return *this;
  1842. }
  1843. //
  1844. // As above
  1845. //
  1846. CILong &
  1847. CILong::operator -=(
  1848. IN const LPCTSTR lpszValue
  1849. )
  1850. {
  1851. LONG lValue;
  1852. ConvertStringToLong(lpszValue, lValue);
  1853. m_lValue -= lValue;
  1854. return *this;
  1855. }
  1856. //
  1857. // As above
  1858. //
  1859. CILong &
  1860. CILong::operator -=(
  1861. IN const CILong & value
  1862. )
  1863. {
  1864. m_lValue -= value.m_lValue;
  1865. return *this;
  1866. }
  1867. //
  1868. // As above
  1869. //
  1870. CILong &
  1871. CILong::operator *=(
  1872. IN const LONG lValue
  1873. )
  1874. {
  1875. m_lValue *= lValue;
  1876. return *this;
  1877. }
  1878. //
  1879. // As above
  1880. //
  1881. CILong &
  1882. CILong::operator *=(
  1883. IN const LPCTSTR lpszValue
  1884. )
  1885. {
  1886. LONG lValue;
  1887. ConvertStringToLong(lpszValue, lValue);
  1888. m_lValue *= lValue;
  1889. return *this;
  1890. }
  1891. //
  1892. // As above
  1893. //
  1894. CILong &
  1895. CILong::operator *=(
  1896. IN const CILong & value
  1897. )
  1898. {
  1899. m_lValue *= value.m_lValue;
  1900. return *this;
  1901. }
  1902. //
  1903. // As above
  1904. //
  1905. CILong &
  1906. CILong::operator /=(
  1907. IN const LONG lValue
  1908. )
  1909. {
  1910. m_lValue /= lValue;
  1911. return *this;
  1912. }
  1913. //
  1914. // As above
  1915. //
  1916. CILong &
  1917. CILong::operator /=(
  1918. IN const LPCTSTR lpszValue
  1919. )
  1920. {
  1921. LONG lValue;
  1922. ConvertStringToLong(lpszValue, lValue);
  1923. if (lValue != 0)
  1924. {
  1925. m_lValue /= lValue;
  1926. }
  1927. return *this;
  1928. }
  1929. //
  1930. // As above
  1931. //
  1932. CILong &
  1933. CILong::operator /=(
  1934. IN const CILong& value
  1935. )
  1936. {
  1937. m_lValue /= value.m_lValue;
  1938. return *this;
  1939. }