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.

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