Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3093 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. 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. AddTail( strl.GetNext(pos) );
  1076. }
  1077. return *this;
  1078. }
  1079. BOOL
  1080. SplitUserNameAndDomain(
  1081. IN OUT CString & strUserName,
  1082. IN CString & strDomainName
  1083. )
  1084. /*++
  1085. Routine Description:
  1086. Split the user name and domain from the given
  1087. username, which is in the format "domain\user".
  1088. Return TRUE if the user name contained a domain
  1089. FALSE if it did not
  1090. Arguments:
  1091. CString & strUserName : User name which may contain a domain name
  1092. CString & strDomainName : Output domain name ("." if local)
  1093. Return Value:
  1094. TRUE if a domain is split off
  1095. --*/
  1096. {
  1097. //
  1098. // Assume local
  1099. //
  1100. strDomainName = _T(".");
  1101. int nSlash = strUserName.Find(_T("\\"));
  1102. if (nSlash >= 0)
  1103. {
  1104. strDomainName = strUserName.Left(nSlash);
  1105. strUserName = strUserName.Mid(nSlash + 1);
  1106. return TRUE;
  1107. }
  1108. return FALSE;
  1109. }
  1110. const LPCTSTR g_cszMonths[] =
  1111. {
  1112. _T("Jan"),
  1113. _T("Feb"),
  1114. _T("Mar"),
  1115. _T("Apr"),
  1116. _T("May"),
  1117. _T("Jun"),
  1118. _T("Jul"),
  1119. _T("Aug"),
  1120. _T("Sep"),
  1121. _T("Oct"),
  1122. _T("Nov"),
  1123. _T("Dec"),
  1124. };
  1125. const LPCTSTR g_cszWeekDays[] =
  1126. {
  1127. _T("Sun"),
  1128. _T("Mon"),
  1129. _T("Tue"),
  1130. _T("Wed"),
  1131. _T("Thu"),
  1132. _T("Fri"),
  1133. _T("Sat"),
  1134. };
  1135. inline BOOL SkipTillDigit(LPCTSTR & lp)
  1136. {
  1137. while (lp && *lp && !_istdigit(*lp)) ++lp;
  1138. return lp != NULL;
  1139. }
  1140. inline BOOL SkipPastDigits(LPCTSTR & lp)
  1141. {
  1142. while (lp && *lp && _istdigit(*lp)) ++lp;
  1143. return lp != NULL;
  1144. }
  1145. BOOL
  1146. FetchIntField(
  1147. LPCTSTR & lp,
  1148. int & n
  1149. )
  1150. {
  1151. if (SkipTillDigit(lp))
  1152. {
  1153. n = _ttoi(lp);
  1154. if (n < 0)
  1155. {
  1156. ASSERT(FALSE && "Bogus string->int");
  1157. return FALSE;
  1158. }
  1159. return SkipPastDigits(lp);
  1160. }
  1161. return FALSE;
  1162. }
  1163. BOOL
  1164. MatchString(
  1165. LPCTSTR lpTarget,
  1166. const LPCTSTR * rglp,
  1167. int cElements,
  1168. int & idx
  1169. )
  1170. {
  1171. for (idx = 0; idx < cElements; ++idx)
  1172. {
  1173. if (!_tcsnicmp(lpTarget, rglp[idx], _tcslen(rglp[idx])))
  1174. {
  1175. return TRUE;
  1176. }
  1177. }
  1178. return FALSE;
  1179. }
  1180. static g_dwCurrentTimeZone = TIME_ZONE_ID_INVALID;
  1181. static TIME_ZONE_INFORMATION g_tzInfo;
  1182. //
  1183. // International numeric strings
  1184. //
  1185. // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  1186. //
  1187. // Initialize library
  1188. //
  1189. BOOL
  1190. InitIntlSettings()
  1191. {
  1192. #ifdef _MT
  1193. INITIALIZE_CRITICAL_SECTION(&_csSect);
  1194. #endif // _MT
  1195. return CINumber::Allocate();
  1196. }
  1197. //
  1198. // De-initialize library
  1199. //
  1200. void
  1201. TerminateIntlSettings()
  1202. {
  1203. CINumber::DeAllocate();
  1204. #ifdef _MT
  1205. DeleteCriticalSection(&_csSect);
  1206. #endif // _MT
  1207. }
  1208. //
  1209. // Static Member Initialization
  1210. //
  1211. CString * CINumber::s_pstrThousandSeperator = NULL;
  1212. CString * CINumber::s_pstrDecimalPoint = NULL;
  1213. CString * CINumber::s_pstrBadNumber = NULL;
  1214. CString * CINumber::s_pstrCurrency = NULL;
  1215. CString * CINumber::s_pstr = NULL;
  1216. BOOL CINumber::s_fAllocated = FALSE;
  1217. BOOL CINumber::s_fCurrencyPrefix = TRUE;
  1218. BOOL CINumber::s_fInitialized = FALSE;
  1219. #ifdef _DOS
  1220. BOOL
  1221. _dos_getintlsettings(
  1222. OUT INTLFORMAT * pStruct
  1223. )
  1224. /*++
  1225. Routine Description:
  1226. Get the international settings on a DOS box
  1227. Parameters:
  1228. INTLFORMAT * pStruct : Structure to be filled in.
  1229. Return Value:
  1230. TRUE for success, FALSE for failure
  1231. --*/
  1232. {
  1233. TRACEEOLID("[_dos_getintlsetting]");
  1234. union _REGS inregs, outregs;
  1235. struct _SREGS segregs;
  1236. inregs.h.ah = 0x38; // Intl call
  1237. inregs.h.al = 0x00; // Current country code
  1238. inregs.x.bx = 0x00; // Current country code
  1239. segregs.ds = _FP_SEG(pStruct);
  1240. inregs.x.dx = _FP_OFF(pStruct);
  1241. int nError = _intdosx(&inregs, &outregs, &segregs);
  1242. return outregs.x.cflag == 0;
  1243. }
  1244. #endif // _DOS
  1245. /* protected */
  1246. CINumber::CINumber()
  1247. /*++
  1248. Routine Description:
  1249. Constructor
  1250. Arguments:
  1251. None
  1252. Return Value:
  1253. None
  1254. --*/
  1255. {
  1256. if (!CINumber::s_fInitialized)
  1257. {
  1258. CINumber::Initialize();
  1259. }
  1260. }
  1261. CINumber::~CINumber()
  1262. /*++
  1263. Routine Description:
  1264. Destructor
  1265. Arguments:
  1266. None
  1267. Return Value:
  1268. None
  1269. --*/
  1270. {
  1271. }
  1272. /* protected */
  1273. /* static */
  1274. BOOL
  1275. CINumber::Allocate()
  1276. /*++
  1277. Routine Description:
  1278. Allocate with US settings
  1279. Arguments:
  1280. None
  1281. Return Value:
  1282. TRUE if allocation was successfull, FALSE otherwise
  1283. --*/
  1284. {
  1285. RaiseThreadProtection();
  1286. ASSERT(!IsAllocated());
  1287. if (!IsAllocated())
  1288. {
  1289. try
  1290. {
  1291. CINumber::s_pstrThousandSeperator = new CString(_T(","));
  1292. CINumber::s_pstrDecimalPoint = new CString(_T("."));
  1293. CINumber::s_pstrBadNumber = new CString(_T("--"));
  1294. CINumber::s_pstrCurrency = new CString(_T("$ "));
  1295. CINumber::s_pstr = new CString;
  1296. s_fAllocated = TRUE;
  1297. }
  1298. catch(CMemoryException * e)
  1299. {
  1300. TRACEEOLID("Initialization Failed");
  1301. e->ReportError();
  1302. e->Delete();
  1303. }
  1304. }
  1305. LowerThreadProtection();
  1306. return IsAllocated();
  1307. }
  1308. /* protected */
  1309. /* static */
  1310. void
  1311. CINumber::DeAllocate()
  1312. /*++
  1313. Routine Description:
  1314. Clean up allocations
  1315. Arguments:
  1316. N/A
  1317. Return Value:
  1318. N/A
  1319. --*/
  1320. {
  1321. RaiseThreadProtection();
  1322. ASSERT(IsAllocated());
  1323. if (IsAllocated())
  1324. {
  1325. MAKE_NULL(CINumber::s_pstrThousandSeperator);
  1326. MAKE_NULL(CINumber::s_pstrDecimalPoint);
  1327. MAKE_NULL(CINumber::s_pstrBadNumber);
  1328. MAKE_NULL(CINumber::s_pstrCurrency);
  1329. MAKE_NULL(CINumber::s_pstr);
  1330. }
  1331. LowerThreadProtection();
  1332. s_fAllocated = FALSE;
  1333. }
  1334. /* static */
  1335. /* protected */
  1336. BOOL
  1337. CINumber::Initialize(
  1338. IN BOOL fUserSetting /* TRUE */
  1339. )
  1340. /*++
  1341. Routine Description:
  1342. Initialize all the international settings, such as thousand
  1343. seperators and decimal points
  1344. Parameters:
  1345. BOOL fUserSetting If TRUE, use current user settings,
  1346. if FALSE use system settings.
  1347. Return Value:
  1348. TRUE for success, FALSE for failure
  1349. Notes:
  1350. Note that this function only needs to be explicitly called
  1351. when the country settings have changed, or when system
  1352. settings are desired (user is default)
  1353. --*/
  1354. {
  1355. #define MAXLEN 6
  1356. int cErrors = 0;
  1357. TRACEEOLID("Getting locale-dependend information");
  1358. ASSERT(IsAllocated());
  1359. if (!IsAllocated())
  1360. {
  1361. Allocate();
  1362. }
  1363. RaiseThreadProtection();
  1364. try
  1365. {
  1366. #if defined(_MAC)
  1367. TRACEEOLID("Couldn't get international settings from system");
  1368. #elif defined(_WIN32)
  1369. LCID lcid = fUserSetting
  1370. ? ::GetUserDefaultLCID()
  1371. : GetSystemDefaultLCID();
  1372. LCTYPE lctype = fUserSetting ? 0 : LOCALE_NOUSEROVERRIDE;
  1373. //
  1374. // Get Decimal Point
  1375. //
  1376. if (!::GetLocaleInfo(
  1377. lcid,
  1378. LOCALE_SDECIMAL | lctype,
  1379. CINumber::s_pstrDecimalPoint->GetBuffer(MAXLEN),
  1380. MAXLEN
  1381. ))
  1382. {
  1383. TRACEEOLID("Unable to get intl decimal point");
  1384. ++cErrors;
  1385. }
  1386. CINumber::s_pstrDecimalPoint->ReleaseBuffer();
  1387. //
  1388. // Get Thousand Seperator
  1389. //
  1390. if (!::GetLocaleInfo(
  1391. lcid, LOCALE_STHOUSAND | lctype,
  1392. CINumber::s_pstrThousandSeperator->GetBuffer(MAXLEN),
  1393. MAXLEN
  1394. ))
  1395. {
  1396. TRACEEOLID("Unable to get 1000 seperator");
  1397. ++cErrors;
  1398. }
  1399. CINumber::s_pstrThousandSeperator->ReleaseBuffer();
  1400. #ifndef _UNICODE
  1401. //
  1402. // Some countries have a space as a 1000 seperator,
  1403. // but for some reason, this is ansi 160, which
  1404. // shows up as a space fine on windows apps,
  1405. // looks like garbage on console apps.
  1406. //
  1407. if ( ( (UCHAR) ( *(CINumber::s_pstrThousandSeperator) )[0] ) == ( (UCHAR) 160 ) )
  1408. {
  1409. CINumber::s_pstrThousandSeperator->SetAt(0, ' ');
  1410. TRACEEOLID("Space 1000 seperator substituted");
  1411. }
  1412. #endif // _UNICODE
  1413. //
  1414. // Get currency symbol
  1415. //
  1416. if (!::GetLocaleInfo(
  1417. lcid,
  1418. LOCALE_SCURRENCY | lctype,
  1419. CINumber::s_pstrCurrency->GetBuffer(MAXLEN),
  1420. MAXLEN
  1421. ))
  1422. {
  1423. TRACEEOLID("Unable to get currency symbol");
  1424. ++cErrors;
  1425. }
  1426. CINumber::s_pstrCurrency->ReleaseBuffer();
  1427. #elif defined(_WIN16)
  1428. //
  1429. // Get Decimal Point
  1430. //
  1431. ::GetProfileString(
  1432. "Intl",
  1433. "sDecimal",
  1434. ".",
  1435. CINumber::s_pstrDecimalPoint->GetBuffer(MAXLEN),
  1436. MAXLEN
  1437. );
  1438. CINumber::s_pstrDecimalPoint->ReleaseBuffer();
  1439. //
  1440. // Get 1000 seperator
  1441. //
  1442. ::GetProfileString(
  1443. "Intl",
  1444. "sThousand",
  1445. ",",
  1446. CINumber::s_pstrThousandSeperator->GetBuffer(MAXLEN),
  1447. MAXLEN
  1448. );
  1449. CINumber::s_pstrThousandSeperator->ReleaseBuffer();
  1450. //
  1451. // Get currency symbol
  1452. //
  1453. ::GetProfileString(
  1454. "Intl",
  1455. "sCurrency",
  1456. ",",
  1457. CINumber::s_pstrCurrency->GetBuffer(MAXLEN),
  1458. MAXLEN
  1459. );
  1460. CINumber::s_pstrCurrency->ReleaseBuffer();
  1461. #elif defined(_DOS)
  1462. INTLFORMAT fm;
  1463. if (_dos_getintlsettings(&fm))
  1464. {
  1465. //
  1466. // Get Decimal Point
  1467. //
  1468. *CINumber::s_pstrDecimalPoint = fm.szDecimalPoint;
  1469. //
  1470. // Get 1000 seperator
  1471. //
  1472. *CINumber::s_pstrThousandSeperator = fm.szThousandSep;
  1473. //
  1474. // Get Currency Symbol
  1475. //
  1476. *CINumber::s_pstrCurrency = fm.szCurrencySymbol;
  1477. }
  1478. else
  1479. {
  1480. TRACEEOLID("Unable to get intl settings");
  1481. ++cErrors;
  1482. }
  1483. #endif // _WIN32 etc
  1484. }
  1485. catch(CMemoryException * e)
  1486. {
  1487. TRACEEOLID("!!!exception in getting intl settings:");
  1488. e->ReportError();
  1489. e->Delete();
  1490. ++cErrors;
  1491. }
  1492. TRACEEOLID("Thousand Seperator . . . . . : " << *CINumber::s_pstrThousandSeperator);
  1493. TRACEEOLID("Decimal Point . . . . . . . : " << *CINumber::s_pstrDecimalPoint);
  1494. TRACEEOLID("Currency Symbol. . . . . . . : " << *CINumber::s_pstrCurrency);
  1495. TRACEEOLID("Bad number . . . . . . . . . : " << *CINumber::s_pstrBadNumber);
  1496. TRACEEOLID("Currency Prefix. . . . . . . : " << CINumber::s_fCurrencyPrefix);
  1497. CINumber::s_fInitialized = TRUE;
  1498. LowerThreadProtection();
  1499. return cErrors == 0;
  1500. }
  1501. /* static */
  1502. double
  1503. CINumber::BuildFloat(
  1504. IN const LONG lInteger,
  1505. IN const LONG lFraction
  1506. )
  1507. /*++
  1508. Return Value:
  1509. Combine integer and fraction to form float
  1510. Parameters:
  1511. const LONG lInteger Integer portion
  1512. const LONG lFraction Fractional portion
  1513. Return Value:
  1514. float value
  1515. --*/
  1516. {
  1517. double flValue = 0.0;
  1518. //
  1519. // Negative fractions?
  1520. //
  1521. ASSERT(lFraction >= 0);
  1522. if (lFraction >= 0)
  1523. {
  1524. flValue = (double)lFraction;
  1525. while (flValue >= 1.0)
  1526. {
  1527. flValue /= 10.0;
  1528. }
  1529. //
  1530. // Re-add (or subtract if the original number
  1531. // was negative) the fractional part
  1532. //
  1533. if (lInteger > 0L)
  1534. {
  1535. flValue += (double)lInteger;
  1536. }
  1537. else
  1538. {
  1539. flValue -= (double)lInteger;
  1540. flValue = -flValue;
  1541. }
  1542. }
  1543. return flValue;
  1544. }
  1545. /* static */
  1546. LPCTSTR
  1547. CINumber::ConvertLongToString(
  1548. IN const LONG lSrc,
  1549. OUT CString & str
  1550. )
  1551. /*++
  1552. CINumber::ConvertLongToString
  1553. Purpose:
  1554. Convert long number to string with 1000 seperators
  1555. Parameters:
  1556. const LONG lSrc Source number
  1557. CString & str String to write to
  1558. Return Value:
  1559. Pointer to converted string
  1560. --*/
  1561. {
  1562. LPTSTR lpOutString = str.GetBuffer(16);
  1563. //
  1564. // Forget about the negative sign for now.
  1565. //
  1566. LONG lNum = (lSrc >= 0L) ? lSrc : -lSrc;
  1567. int outstrlen = 0;
  1568. do
  1569. {
  1570. lpOutString[outstrlen++] = _T('0') + (TCHAR)(lNum % 10L);
  1571. lNum /= 10L;
  1572. //
  1573. // if more digits left and we're on a 1000 boundary (printed 3 digits,
  1574. // or 3 digits + n*(3 digits + 1 comma), then print a 1000 separator.
  1575. // Note: will only work if the 1000 seperator is 1 character.
  1576. //
  1577. ASSERT(CINumber::s_pstrThousandSeperator->GetLength() == 1);
  1578. if (lNum != 0L && (outstrlen == 3 || outstrlen == 7 || outstrlen == 11))
  1579. {
  1580. lstrcpy(lpOutString + outstrlen, *CINumber::s_pstrThousandSeperator);
  1581. outstrlen += CINumber::s_pstrThousandSeperator->GetLength();
  1582. }
  1583. }
  1584. while (lNum > 0L);
  1585. //
  1586. // Add a negative sign if necessary.
  1587. //
  1588. if (lSrc < 0L)
  1589. {
  1590. lpOutString[outstrlen++] = _T('-');
  1591. }
  1592. str.ReleaseBuffer(outstrlen);
  1593. str.MakeReverse();
  1594. return (LPCTSTR)str;
  1595. }
  1596. /* static */
  1597. LPCTSTR
  1598. CINumber::ConvertFloatToString(
  1599. IN const double flSrc,
  1600. IN int nPrecision,
  1601. OUT CString & str
  1602. )
  1603. /*++
  1604. Routine Description:
  1605. Convert floating point number to string represenation
  1606. Parameters:
  1607. const double flSrc Source floating point number
  1608. int nPrecision Number of decimal points
  1609. CString & str String to convert to
  1610. Return Value:
  1611. Pointer to converted string.
  1612. --*/
  1613. {
  1614. //
  1615. // Forget about the negative sign for now,
  1616. // and the fractional portion.
  1617. //
  1618. TCHAR szFraction[256];
  1619. LPCTSTR lpFraction = NULL;
  1620. ::_stprintf(szFraction, _T("%.*f"), nPrecision, flSrc);
  1621. lpFraction = ::_tcschr(szFraction, _T('.') );
  1622. ASSERT(lpFraction != NULL);
  1623. ++lpFraction;
  1624. CINumber::ConvertLongToString((LONG)flSrc, str);
  1625. str += *CINumber::s_pstrDecimalPoint + lpFraction;
  1626. return (LPCTSTR)str;
  1627. }
  1628. /* static */
  1629. BOOL
  1630. CINumber::ConvertStringToLong(
  1631. IN LPCTSTR lpsrc,
  1632. OUT LONG & lValue
  1633. )
  1634. /*++
  1635. Routine Description:
  1636. Convert string to long integer. 1000 Seperators will be treated
  1637. correctly.
  1638. Parameters:
  1639. LPCTSTR lpsrc Source string
  1640. LONG & lValue Value to convert to. Will be 0 in case of error
  1641. Return Value:
  1642. TRUE for success, FALSE for failure.
  1643. --*/
  1644. {
  1645. CString strNumber(lpsrc);
  1646. LONG lBase = 1L;
  1647. BOOL fNegative = FALSE;
  1648. lValue = 0L;
  1649. //
  1650. // Empty strings are invalid
  1651. //
  1652. if (strNumber.IsEmpty())
  1653. {
  1654. return FALSE;
  1655. }
  1656. //
  1657. // Check for negative sign (at the end only)
  1658. //
  1659. if (strNumber[0] == _T('-'))
  1660. {
  1661. fNegative = TRUE;
  1662. }
  1663. strNumber.MakeReverse();
  1664. //
  1665. // Strip negative sign
  1666. //
  1667. if (fNegative)
  1668. {
  1669. strNumber.ReleaseBuffer(strNumber.GetLength()-1);
  1670. }
  1671. //
  1672. // Make sure the 1000 seperator is only 1 char. See note below
  1673. //
  1674. ASSERT(CINumber::s_pstrThousandSeperator->GetLength() == 1);
  1675. for (int i = 0; i < strNumber.GetLength(); ++i)
  1676. {
  1677. if ((strNumber[i] >= _T('0')) && (strNumber[i] <= _T('9')))
  1678. {
  1679. LONG lDigit = (LONG)(strNumber[i] - _T('0'));
  1680. if (lDigit != 0L)
  1681. {
  1682. LONG lOldValue = lValue;
  1683. LONG lDelta = (lDigit * lBase);
  1684. if (lDelta / lDigit != lBase)
  1685. {
  1686. TRACEEOLID("Overflow!");
  1687. lValue = 0x7fffffff;
  1688. return FALSE;
  1689. }
  1690. lValue += lDelta;
  1691. if (lValue - lDelta != lOldValue)
  1692. {
  1693. TRACEEOLID("Overflow!");
  1694. lValue = 0x7fffffff;
  1695. return FALSE;
  1696. }
  1697. }
  1698. lBase *= 10L;
  1699. }
  1700. //
  1701. // It's not a digit, maybe a thousand seperator?
  1702. // CAVEAT: If a thousand seperator of more than
  1703. // one character is used, this won't work.
  1704. //
  1705. else if ((strNumber[i] != (*CINumber::s_pstrThousandSeperator)[0])
  1706. || (i != 3) && (i != 7) && (i != 11))
  1707. {
  1708. //
  1709. // This is just invalid, since it is not a thousand
  1710. // seperator in the proper location, nor a negative
  1711. // sign.
  1712. //
  1713. TRACEEOLID("Invalid character " << (BYTE)strNumber[i] << " encountered");
  1714. return FALSE;
  1715. }
  1716. }
  1717. if (fNegative)
  1718. {
  1719. lValue = -lValue;
  1720. }
  1721. return TRUE;
  1722. }
  1723. /* static */
  1724. BOOL
  1725. CINumber::ConvertStringToFloat(
  1726. IN LPCTSTR lpsrc,
  1727. OUT double & flValue
  1728. )
  1729. /*++
  1730. Routine Description:
  1731. Convert fully decorated floating point string to double
  1732. Parameters:
  1733. LPCTSTR lpsrc Source string
  1734. double & flValue float value generated from string
  1735. Return Value:
  1736. TRUE for success, FALSE for failure
  1737. --*/
  1738. {
  1739. CString strNumber(lpsrc);
  1740. //
  1741. // This only works if the decimal point is a
  1742. // single character
  1743. //
  1744. ASSERT(CINumber::s_pstrDecimalPoint->GetLength() == 1);
  1745. //
  1746. // Strip off the > 0 part
  1747. //
  1748. LONG lFraction = 0;
  1749. int nPoint = strNumber.ReverseFind((*CINumber::s_pstrDecimalPoint)[0]);
  1750. if (nPoint >= 0)
  1751. {
  1752. //
  1753. // Convert fractional part
  1754. //
  1755. LPCTSTR lpszFraction = (LPCTSTR)strNumber + ++nPoint;
  1756. lFraction = ::_ttol(lpszFraction);
  1757. strNumber.ReleaseBuffer(--nPoint);
  1758. }
  1759. //
  1760. // Convert integer part
  1761. //
  1762. LONG lInteger;
  1763. if (ConvertStringToLong(strNumber, lInteger))
  1764. {
  1765. flValue = CINumber::BuildFloat(lInteger, lFraction);
  1766. return TRUE;
  1767. }
  1768. return FALSE;
  1769. }
  1770. CILong::CILong()
  1771. /*++
  1772. Routine Description:
  1773. Constructor without arguments
  1774. Parameters:
  1775. None.
  1776. Return Value:
  1777. N/A
  1778. --*/
  1779. : m_lValue(0L)
  1780. {
  1781. }
  1782. CILong::CILong(
  1783. IN LONG lValue
  1784. )
  1785. /*++
  1786. Routine Description:
  1787. Constructor taking LONG argument
  1788. Parameters:
  1789. LONG lValue Value to be set
  1790. Return Value:
  1791. N/A
  1792. --*/
  1793. : m_lValue(lValue)
  1794. {
  1795. }
  1796. CILong::CILong(
  1797. IN LPCTSTR lpszValue
  1798. )
  1799. /*++
  1800. Routine Description:
  1801. Constructor taking string argument
  1802. Parameters:
  1803. LPCTSTR lpszValue String number
  1804. Return Value:
  1805. N/A
  1806. --*/
  1807. {
  1808. ConvertStringToLong(lpszValue, m_lValue);
  1809. }
  1810. CILong &
  1811. CILong::operator =(
  1812. IN LONG lValue
  1813. )
  1814. /*++
  1815. Routine Description:
  1816. Assignment operator taking long value
  1817. Parameters:
  1818. LONG lValue Value to be set
  1819. Return Value:
  1820. this object
  1821. --*/
  1822. {
  1823. m_lValue = lValue;
  1824. return *this;
  1825. }
  1826. CILong &
  1827. CILong::operator =(
  1828. IN LPCTSTR lpszValue
  1829. )
  1830. /*++
  1831. Routine Description:
  1832. Assignment operator taking string value
  1833. Parameters:
  1834. LPCTSTR lpszValue String number
  1835. Return Value:
  1836. this object
  1837. --*/
  1838. {
  1839. ConvertStringToLong(lpszValue, m_lValue);
  1840. return *this;
  1841. }
  1842. //
  1843. // Arithmetic Shorthand operators
  1844. //
  1845. // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  1846. CILong &
  1847. CILong::operator +=(
  1848. IN const LONG lValue
  1849. )
  1850. {
  1851. m_lValue += lValue;
  1852. return *this;
  1853. }
  1854. //
  1855. // As above
  1856. //
  1857. CILong &
  1858. CILong::operator +=(
  1859. IN const LPCTSTR lpszValue
  1860. )
  1861. {
  1862. LONG lValue;
  1863. ConvertStringToLong(lpszValue, lValue);
  1864. m_lValue += lValue;
  1865. return *this;
  1866. }
  1867. //
  1868. // As above
  1869. //
  1870. CILong &
  1871. CILong::operator +=(
  1872. IN const CILong& value
  1873. )
  1874. {
  1875. m_lValue += value.m_lValue;
  1876. return *this;
  1877. }
  1878. //
  1879. // As above
  1880. //
  1881. CILong &
  1882. CILong::operator -=(
  1883. IN const LONG lValue
  1884. )
  1885. {
  1886. m_lValue -= lValue;
  1887. return *this;
  1888. }
  1889. //
  1890. // As above
  1891. //
  1892. CILong &
  1893. CILong::operator -=(
  1894. IN const LPCTSTR lpszValue
  1895. )
  1896. {
  1897. LONG lValue;
  1898. ConvertStringToLong(lpszValue, lValue);
  1899. m_lValue -= lValue;
  1900. return *this;
  1901. }
  1902. //
  1903. // As above
  1904. //
  1905. CILong &
  1906. CILong::operator -=(
  1907. IN const CILong& value
  1908. )
  1909. {
  1910. m_lValue -= value.m_lValue;
  1911. return *this;
  1912. }
  1913. //
  1914. // As above
  1915. //
  1916. CILong &
  1917. CILong::operator *=(
  1918. IN const LONG lValue
  1919. )
  1920. {
  1921. m_lValue *= lValue;
  1922. return *this;
  1923. }
  1924. //
  1925. // As above
  1926. //
  1927. CILong &
  1928. CILong::operator *=(
  1929. IN const LPCTSTR lpszValue
  1930. )
  1931. {
  1932. LONG lValue;
  1933. ConvertStringToLong(lpszValue, lValue);
  1934. m_lValue *= lValue;
  1935. return *this;
  1936. }
  1937. //
  1938. // As above
  1939. //
  1940. CILong &
  1941. CILong::operator *=(
  1942. IN const CILong& value
  1943. )
  1944. {
  1945. m_lValue *= value.m_lValue;
  1946. return *this;
  1947. }
  1948. //
  1949. // As above
  1950. //
  1951. CILong &
  1952. CILong::operator /=(
  1953. IN const LONG lValue
  1954. )
  1955. {
  1956. if (lValue != 0)
  1957. {
  1958. m_lValue /= lValue;
  1959. }
  1960. return *this;
  1961. }
  1962. //
  1963. // As above
  1964. //
  1965. CILong &
  1966. CILong::operator /=(
  1967. IN const LPCTSTR lpszValue
  1968. )
  1969. {
  1970. LONG lValue;
  1971. ConvertStringToLong(lpszValue, lValue);
  1972. if (0 == lValue)
  1973. {
  1974. m_lValue = 0;
  1975. }
  1976. else
  1977. {
  1978. m_lValue /= lValue;
  1979. }
  1980. return *this;
  1981. }
  1982. //
  1983. // As above
  1984. //
  1985. CILong &
  1986. CILong::operator /=(
  1987. IN const CILong& value
  1988. )
  1989. {
  1990. m_lValue /= value.m_lValue;
  1991. return *this;
  1992. }
  1993. CIFloat::CIFloat(
  1994. IN int nPrecision
  1995. )
  1996. /*++
  1997. Routine Description:
  1998. Constructor without arguments
  1999. Parameters:
  2000. int nPrecision Number of decimal digits in string,
  2001. Return Value:
  2002. N/A
  2003. --*/
  2004. : m_flValue(0.0),
  2005. m_nPrecision(nPrecision)
  2006. {
  2007. }
  2008. CIFloat::CIFloat(
  2009. IN double flValue,
  2010. IN int nPrecision
  2011. )
  2012. /*++
  2013. Routine Description:
  2014. Constructor taking double argument
  2015. Parameters:
  2016. double flValue Value to be set
  2017. int nPrecision Number of decimal digits in string,
  2018. Return Value:
  2019. N/A
  2020. --*/
  2021. : m_flValue(flValue),
  2022. m_nPrecision(nPrecision)
  2023. {
  2024. }
  2025. CIFloat::CIFloat(
  2026. IN LONG lInteger,
  2027. IN LONG lFraction,
  2028. IN int nPrecision
  2029. )
  2030. /*++
  2031. Routine Description:
  2032. Constructor taking integer and fraction argument
  2033. Parameters:
  2034. LONG lInteger Integer portion
  2035. LONG lFraction Fractional portion
  2036. int nPrecision Number of decimal digits in string,
  2037. Return Value:
  2038. N/A
  2039. --*/
  2040. : m_nPrecision(nPrecision)
  2041. {
  2042. m_flValue = CINumber::BuildFloat(lInteger, lFraction);
  2043. }
  2044. CIFloat::CIFloat(
  2045. IN LPCTSTR lpszValue,
  2046. IN int nPrecision
  2047. )
  2048. /*++
  2049. Routine Description:
  2050. Constructor taking string argument
  2051. Parameters:
  2052. LPCTSTR lpszValue String number
  2053. int nPrecision Number of decimal digits in string,
  2054. Return Value:
  2055. N/A
  2056. --*/
  2057. : m_nPrecision(nPrecision)
  2058. {
  2059. ConvertStringToFloat(lpszValue, m_flValue);
  2060. }
  2061. CIFloat &
  2062. CIFloat::operator =(
  2063. IN double flValue
  2064. )
  2065. /*++
  2066. Routine Description:
  2067. Assignment operator taking double value
  2068. Parameters:
  2069. double flValue Value to be set
  2070. Return Value:
  2071. this object
  2072. --*/
  2073. {
  2074. m_flValue = flValue;
  2075. return *this;
  2076. }
  2077. CIFloat &
  2078. CIFloat::operator =(
  2079. IN LPCTSTR lpszValue
  2080. )
  2081. /*++
  2082. Routine Description:
  2083. Assignment operator taking string value
  2084. Parameters:
  2085. LPCTSTR lpszValue String number
  2086. Return Value:
  2087. this object
  2088. --*/
  2089. {
  2090. ConvertStringToFloat(lpszValue, m_flValue);
  2091. return *this;
  2092. }
  2093. //
  2094. // Arithmetic Shorthand operators
  2095. //
  2096. // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  2097. CIFloat &
  2098. CIFloat::operator +=(
  2099. IN const double flValue
  2100. )
  2101. {
  2102. m_flValue += flValue;
  2103. return *this;
  2104. }
  2105. //
  2106. // As above
  2107. //
  2108. CIFloat &
  2109. CIFloat::operator +=(
  2110. IN const LPCTSTR lpszValue
  2111. )
  2112. {
  2113. double flValue;
  2114. ConvertStringToFloat(lpszValue, flValue);
  2115. m_flValue += flValue;
  2116. return *this;
  2117. }
  2118. //
  2119. // As above
  2120. //
  2121. CIFloat &
  2122. CIFloat::operator +=(
  2123. IN const CIFloat& value
  2124. )
  2125. {
  2126. m_flValue += value.m_flValue;
  2127. return *this;
  2128. }
  2129. //
  2130. // As above
  2131. //
  2132. CIFloat &
  2133. CIFloat::operator -=(
  2134. IN const double flValue
  2135. )
  2136. {
  2137. m_flValue -= flValue;
  2138. return *this;
  2139. }
  2140. //
  2141. // As above
  2142. //
  2143. CIFloat &
  2144. CIFloat::operator -=(
  2145. IN const LPCTSTR lpszValue
  2146. )
  2147. {
  2148. double flValue;
  2149. ConvertStringToFloat(lpszValue, flValue);
  2150. m_flValue -= flValue;
  2151. return *this;
  2152. }
  2153. //
  2154. // As above
  2155. //
  2156. CIFloat &
  2157. CIFloat::operator -=(
  2158. IN const CIFloat& value
  2159. )
  2160. {
  2161. m_flValue -= value.m_flValue;
  2162. return *this;
  2163. }
  2164. //
  2165. // As above
  2166. //
  2167. CIFloat &
  2168. CIFloat::operator *=(
  2169. IN const double flValue
  2170. )
  2171. {
  2172. m_flValue *= flValue;
  2173. return *this;
  2174. }
  2175. //
  2176. // As above
  2177. //
  2178. CIFloat &
  2179. CIFloat::operator *=(
  2180. IN const LPCTSTR lpszValue
  2181. )
  2182. {
  2183. double flValue;
  2184. ConvertStringToFloat(lpszValue, flValue);
  2185. m_flValue *= flValue;
  2186. return *this;
  2187. }
  2188. //
  2189. // As above
  2190. //
  2191. CIFloat &
  2192. CIFloat::operator *=(
  2193. IN const CIFloat& value
  2194. )
  2195. {
  2196. m_flValue *= value.m_flValue;
  2197. return *this;
  2198. }
  2199. //
  2200. // As above
  2201. //
  2202. CIFloat &
  2203. CIFloat::operator /=(
  2204. IN const double flValue
  2205. )
  2206. {
  2207. m_flValue /= flValue;
  2208. return *this;
  2209. }
  2210. //
  2211. // As above
  2212. //
  2213. CIFloat &
  2214. CIFloat::operator /=(
  2215. IN const LPCTSTR lpszValue
  2216. )
  2217. {
  2218. double flValue;
  2219. ConvertStringToFloat(lpszValue, flValue);
  2220. if (flValue != 0)
  2221. {
  2222. m_flValue /= flValue;
  2223. }
  2224. return *this;
  2225. }
  2226. //
  2227. // As above
  2228. //
  2229. CIFloat &
  2230. CIFloat::operator /=(
  2231. IN const CIFloat& value
  2232. )
  2233. {
  2234. m_flValue /= value.m_flValue;
  2235. return *this;
  2236. }