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.

3263 lines
53 KiB

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