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.

2910 lines
54 KiB

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