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.

2017 lines
64 KiB

  1. // TestITN_J.cpp : Implementation of CTestITN_J
  2. #include "stdafx.h"
  3. #include <winnls.h>
  4. #include "Itngram_J.h"
  5. #include "TestITN_J.h"
  6. #include "sphelper.h"
  7. #include "spddkhlp.h"
  8. #include "test_j.h"
  9. #define MAX_SIG_FIGS 12
  10. #define MANN ((LONGLONG) 10000)
  11. #define OKU ((LONGLONG) 100000000)
  12. #define CHUU ((LONGLONG) 1000000000000)
  13. #define MANN_STR (L"\x4E07")
  14. #define OKU_STR (L"\x5104")
  15. #define CHUU_STR (L"\x5146")
  16. const WCHAR s_pwszDegree[] = { 0xb0, 0 };
  17. const WCHAR s_pwszMinute[] = { 0x2032, 0 };
  18. const WCHAR s_pwszSecond[] = { 0x2033, 0 };
  19. #define DAYOFWEEK_STR_ABBR ("ddd")
  20. #define DAYOFWEEK_STR ("dddd")
  21. /////////////////////////////////////////////////////////////////////////////
  22. // CTestITN_J
  23. /****************************************************************************
  24. * CTestITN_J::InitGrammar *
  25. *-------------------------*
  26. * Description:
  27. *
  28. * Returns:
  29. *
  30. ********************************************************************* RAL ***/
  31. STDMETHODIMP CTestITN_J::InitGrammar(const WCHAR * pszGrammarName, const void ** pvGrammarData)
  32. {
  33. HRESULT hr = S_OK;
  34. HRSRC hResInfo = ::FindResource(_Module.GetModuleInstance(), _T("TEST"), _T("ITNGRAMMAR"));
  35. if (hResInfo)
  36. {
  37. HGLOBAL hData = ::LoadResource(_Module.GetModuleInstance(), hResInfo);
  38. if (hData)
  39. {
  40. *pvGrammarData = ::LockResource(hData);
  41. if (*pvGrammarData == NULL)
  42. {
  43. hr = HRESULT_FROM_WIN32(::GetLastError());
  44. }
  45. }
  46. else
  47. {
  48. hr = HRESULT_FROM_WIN32(::GetLastError());
  49. }
  50. }
  51. else
  52. {
  53. hr = HRESULT_FROM_WIN32(::GetLastError());
  54. }
  55. return hr;
  56. }
  57. /****************************************************************************
  58. * CTestITN_J::Interpret *
  59. *-----------------------*
  60. * Description:
  61. *
  62. * Returns:
  63. *
  64. ********************************************************************* RAL ***/
  65. STDMETHODIMP CTestITN_J::Interpret(ISpPhraseBuilder * pPhrase,
  66. const ULONG ulFirstElement,
  67. const ULONG ulCountOfElements,
  68. ISpCFGInterpreterSite * pSite)
  69. {
  70. HRESULT hr = S_OK;
  71. ULONG ulRuleId = 0;
  72. CSpPhrasePtr cpPhrase;
  73. hr = pPhrase->GetPhrase(&cpPhrase);
  74. m_pSite = pSite;
  75. //Just use ulFirstElement & ulCountOfElements
  76. // Get the minimum and maximum positions
  77. ULONG ulMinPos;
  78. ULONG ulMaxPos;
  79. //GetMinAndMaxPos( cpPhrase->pProperties, &ulMinPos, &ulMaxPos );
  80. ulMinPos = ulFirstElement;
  81. ulMaxPos = ulMinPos + ulCountOfElements;
  82. if (SUCCEEDED(hr))
  83. {
  84. hr = S_FALSE;
  85. WCHAR pszValueBuff[ MAX_PATH ]; // No ITN result should be longer than this
  86. DOUBLE dblValue; // All ITN results will have a 64-bit value
  87. pszValueBuff[0] = 0;
  88. switch (cpPhrase->Rule.ulId)
  89. {
  90. case GRID_INTEGER_STANDALONE: // Fired as a toplevel rule
  91. hr = InterpretNumber( cpPhrase->pProperties, true,
  92. &dblValue, pszValueBuff, MAX_PATH );
  93. if (SUCCEEDED( hr ) && ( dblValue >= 0 ) && ( dblValue <= 20 )
  94. && ( GRID_DIGIT_NUMBER != cpPhrase->pProperties->ulId ))
  95. {
  96. // Throw this one out because numbers like "three"
  97. // shouldn't be ITNed by themselves
  98. hr = S_FALSE;
  99. goto Cleanup; // no replacement
  100. }
  101. break;
  102. case GRID_INTEGER: case GRID_INTEGER_9999: case GRID_ORDINAL:// Number
  103. hr = InterpretNumber( cpPhrase->pProperties, true,
  104. &dblValue, pszValueBuff, MAX_PATH );
  105. break;
  106. case GRID_DIGIT_NUMBER: // Number "spelled out" digit by digit
  107. hr = InterpretDigitNumber( cpPhrase->pProperties,
  108. &dblValue, pszValueBuff, MAX_PATH );
  109. break;
  110. case GRID_FP_NUMBER:
  111. hr = InterpretFPNumber( cpPhrase->pProperties,
  112. &dblValue, pszValueBuff, MAX_PATH );
  113. break;
  114. case GRID_DENOMINATOR:
  115. hr = InterpretNumber( cpPhrase->pProperties, true,
  116. &dblValue, pszValueBuff, MAX_PATH );
  117. break;
  118. case GRID_FRACTION:
  119. hr = InterpretFraction( cpPhrase->pProperties,
  120. &dblValue, pszValueBuff, MAX_PATH );
  121. break;
  122. case GRID_DATE:
  123. hr = InterpretDate( cpPhrase->pProperties,
  124. &dblValue, pszValueBuff, MAX_PATH );
  125. break;
  126. case GRID_CURRENCY: // Currency
  127. hr = InterpretCurrency( cpPhrase->pProperties,
  128. &dblValue, pszValueBuff, MAX_PATH );
  129. break;
  130. case GRID_TIME:
  131. hr = InterpretTime( cpPhrase->pProperties,
  132. &dblValue, pszValueBuff, MAX_PATH );
  133. break;
  134. case GRID_DEGREES:
  135. hr = InterpretDegrees( cpPhrase->pProperties,
  136. &dblValue, pszValueBuff, MAX_PATH );
  137. break;
  138. case GRID_MEASUREMENT:
  139. hr = InterpretMeasurement( cpPhrase->pProperties,
  140. &dblValue, pszValueBuff, MAX_PATH );
  141. break;
  142. default:
  143. _ASSERT(FALSE);
  144. break;
  145. }
  146. if ( SUCCEEDED( hr ) )
  147. {
  148. hr = AddPropertyAndReplacement( pszValueBuff, dblValue,
  149. ulMinPos, ulMaxPos, ulMinPos, ulMaxPos - ulMinPos );//ulFirstElement, ulCountOfElements );
  150. return hr;
  151. }
  152. }
  153. Cleanup:
  154. return S_FALSE;
  155. }
  156. /***********************************************************************
  157. * CTestITN_J::InterpretNumber *
  158. *-----------------------------*
  159. * Description:
  160. * Interprets a number in the range -999,999,999,999 to
  161. * +999,999,999,999 and sends the properties and
  162. * replacements to the CFGInterpreterSite as appropriate.
  163. * The property will be added and the pszValue will be a string
  164. * with the correct display number.
  165. * If fCardinal is set, makes the display a cardinal number;
  166. * otherwise makes it an ordinal number.
  167. * The number will be formatted only if it was a properly-formed
  168. * number (not given digit by digit).
  169. * Result:
  170. *************************************************************************/
  171. HRESULT CTestITN_J::InterpretNumber(const SPPHRASEPROPERTY *pProperties,
  172. const bool fCardinal,
  173. DOUBLE *pdblVal,
  174. WCHAR *pszVal,
  175. UINT cSize)
  176. {
  177. if ( !pdblVal || !pszVal )
  178. {
  179. return E_POINTER;
  180. }
  181. LONGLONG llValue = 0;
  182. int iPositive = 1;
  183. const SPPHRASEPROPERTY *pFirstProp = pProperties;
  184. // Handle negatives
  185. if ( NEGATIVE == pFirstProp->ulId )
  186. {
  187. // There's no such thing as a negative ordinal
  188. SPDBG_ASSERT( fCardinal );
  189. // There had better be more stuff following
  190. SPDBG_ASSERT( pFirstProp->pNextSibling );
  191. iPositive = -1;
  192. pFirstProp = pFirstProp->pNextSibling;
  193. }
  194. // Handle the digit-by-digit case
  195. if ( GRID_DIGIT_NUMBER == pFirstProp->ulId )
  196. {
  197. // There had better be nothing following this
  198. SPDBG_ASSERT( !pFirstProp->pNextSibling );
  199. SPDBG_ASSERT( VT_R8 == pFirstProp->vValue.vt );
  200. SPDBG_ASSERT( pFirstProp->pszValue );
  201. DOUBLE dblVal = pFirstProp->vValue.dblVal;
  202. UINT uiFixedWidth = wcslen( pFirstProp->pszValue );
  203. *pdblVal = dblVal * iPositive;
  204. DWORD dwDisplayFlags = DF_WHOLENUMBER | DF_FIXEDWIDTH | DF_NOTHOUSANDSGROUP;
  205. return MakeDisplayNumber( *pdblVal, dwDisplayFlags,
  206. uiFixedWidth, 0, pszVal, MAX_PATH, FALSE );
  207. }
  208. for (const SPPHRASEPROPERTY * pProp = pFirstProp; pProp; pProp ? pProp = pProp->pNextSibling : NULL)
  209. {
  210. switch(pProp->ulId)
  211. {
  212. case ICHIs:
  213. {
  214. SPDBG_ASSERT(pProp->pFirstChild);
  215. llValue += ComputeNum9999( pProp->pFirstChild );
  216. }
  217. break;
  218. case MANNs:
  219. {
  220. llValue += ComputeNum9999( pProp->pFirstChild ) * 10000;
  221. }
  222. break;
  223. case OKUs:
  224. {
  225. SPDBG_ASSERT(pProp->pFirstChild);
  226. llValue += ComputeNum9999( pProp->pFirstChild ) * (LONGLONG) 1e8;
  227. }
  228. break;
  229. case CHOOs:
  230. {
  231. SPDBG_ASSERT(pProp->pFirstChild);
  232. llValue += ComputeNum9999( pProp->pFirstChild ) * (LONGLONG) 1e12;
  233. }
  234. break;
  235. default:
  236. SPDBG_ASSERT(false);
  237. }
  238. }
  239. llValue *= iPositive;
  240. *pdblVal = (DOUBLE) llValue;
  241. #if 0
  242. if ( !pProperties->pNextSibling && ( (BILLIONS == pProperties->ulId) || (MILLIONS == pProperties->ulId) ) )
  243. {
  244. // This is something like "3 billion", which should be displayed that way
  245. return E_NOTIMPL;
  246. }
  247. else
  248. #endif
  249. {
  250. DWORD dwDisplayFlags = DF_WHOLENUMBER | (fCardinal ? 0 : DF_ORDINAL);
  251. //Special code to handle minus 0.
  252. if ((iPositive == -1) && (*pdblVal == 0.0f))
  253. {
  254. *pszVal = L'-';
  255. *(pszVal+1) = 0;
  256. return MakeDisplayNumber( *pdblVal, dwDisplayFlags, 0, 0, pszVal+1, cSize-1, FALSE );
  257. }
  258. else
  259. {
  260. return MakeDisplayNumber( *pdblVal, dwDisplayFlags, 0, 0, pszVal, cSize, FALSE );
  261. }
  262. }
  263. } /* CTestITN_J::InterpretNumber */
  264. /***********************************************************************
  265. * CTestITN_J::InterpretDigitNumber *
  266. *----------------------------------*
  267. * Description:
  268. * Interprets an integer in (-INF, +INF) that has been spelled
  269. * out digit by digit.
  270. * Result:
  271. *************************************************************************/
  272. HRESULT CTestITN_J::InterpretDigitNumber( const SPPHRASEPROPERTY *pProperties,
  273. DOUBLE *pdblVal,
  274. WCHAR *pszVal,
  275. UINT cSize)
  276. {
  277. if ( !pdblVal || !pszVal )
  278. {
  279. return E_POINTER;
  280. }
  281. BOOL fPositive = TRUE;
  282. ULONG ulLength = 0;
  283. *pszVal = 0;
  284. WCHAR *pwc = pszVal;
  285. UINT cLen = 0;
  286. for (const SPPHRASEPROPERTY * pProp = pProperties;
  287. pProp && (cLen < cSize);
  288. pProp ? pProp = pProp->pNextSibling : NULL)
  289. {
  290. switch(pProp->ulId)
  291. {
  292. case NEGATIVE:
  293. {
  294. SPDBG_ASSERT( pProp == pProperties );
  295. fPositive = FALSE;
  296. *pwc++ = L'-';
  297. cLen++;
  298. break;
  299. }
  300. case DIGIT:
  301. {
  302. *pwc++ = L'0' + pProp->vValue.iVal;
  303. cLen++;
  304. break;
  305. }
  306. default:
  307. SPDBG_ASSERT(false);
  308. }
  309. }
  310. *pwc = 0;
  311. SPDBG_ASSERT( cLen <= MAX_SIG_FIGS );
  312. *pdblVal = (DOUBLE) _wtoi64( pszVal );
  313. return S_OK;
  314. } /* CTestITN_J::InterpretDigitNumber */
  315. /***********************************************************************
  316. * CTestITN_J::InterpretFPNumber *
  317. *-------------------------------*
  318. * Description:
  319. * Interprets a floating-point number of up to MAX_SIG_FIGS sig
  320. * figs. Truncates the floating-point part as necessary
  321. * The way the grammar is structured, there will be an optional
  322. * ONES property, whose value will already have been interpreted,
  323. * as well as a mandatory FP_PART property, whose value will
  324. * be divided by the appropriate multiple of 10.
  325. * Result:
  326. * Return value of CTestITN_J::AddPropertyAndReplacement()
  327. *************************************************************************/
  328. HRESULT CTestITN_J::InterpretFPNumber( const SPPHRASEPROPERTY *pProperties,
  329. DOUBLE *pdblVal,
  330. WCHAR *pszVal,
  331. UINT cSize)
  332. {
  333. if ( !pdblVal || !pszVal )
  334. {
  335. return E_POINTER;
  336. }
  337. SPDBG_ASSERT( pProperties );
  338. UINT uiSigFigs = 0;
  339. *pdblVal = 0;
  340. *pszVal = 0;
  341. DWORD dwDisplayFlags = 0;
  342. BOOL bOverWriteNOTHOUSANDSGROUP = FALSE;
  343. const SPPHRASEPROPERTY *pProp = pProperties;
  344. UINT uiFixedWidth = 0;
  345. TCHAR pwszLocaleData[ MAX_LOCALE_DATA ];
  346. int iRet = ::GetLocaleInfo( LOCALE_USER_DEFAULT, LOCALE_ILZERO, pwszLocaleData, MAX_LOCALE_DATA );
  347. if ( !iRet )
  348. {
  349. return E_FAIL;
  350. }
  351. if (atoi( pwszLocaleData ))
  352. {
  353. dwDisplayFlags |= DF_LEADINGZERO;
  354. }
  355. // Minus sign?
  356. if (NEGATIVE == pProp->ulId )
  357. {
  358. uiSigFigs = 1;
  359. // Go to the next property
  360. pProp = pProp->pNextSibling;
  361. }
  362. // ONES is optional since "point five" should be ".5" if the user perfers
  363. if ( ICHIs == pProp->ulId )
  364. {
  365. // Get the value
  366. SPDBG_ASSERT( VT_R8 == pProp->vValue.vt );
  367. *pdblVal = pProp->vValue.dblVal;
  368. uiSigFigs = (pProp->pszValue[0] == L'-'); // Have to take care of the case of -0.05
  369. if (uiSigFigs)
  370. {
  371. *pdblVal = -*pdblVal;
  372. }
  373. // Count up the width of the number and set the fixed width flag
  374. dwDisplayFlags |= DF_FIXEDWIDTH;
  375. const WCHAR *pwc;
  376. for ( uiFixedWidth = 0, pwc = pProp->pszValue; *pwc; pwc++ )
  377. {
  378. if ( iswdigit( *pwc ) )
  379. {
  380. uiFixedWidth++;
  381. }
  382. }
  383. if (!iswdigit( pProp->pszValue[wcslen(pProp->pszValue) - 1] ))
  384. {
  385. //Ends with Mann, Choo,..
  386. bOverWriteNOTHOUSANDSGROUP = TRUE;
  387. }
  388. // This needs to be here in case the user said "zero"
  389. dwDisplayFlags |= DF_LEADINGZERO;
  390. // If there is no thousands separator in its string value,
  391. // then leave out the thousands separator in the result
  392. USES_CONVERSION;
  393. TCHAR pszThousandSep[ MAX_LOCALE_DATA ];
  394. ::GetLocaleInfo( LOCALE_USER_DEFAULT, LOCALE_STHOUSAND,
  395. pszThousandSep, MAX_LOCALE_DATA );
  396. if ( NULL == wcsstr( pProp->pszValue, T2W( pszThousandSep ) ) && !bOverWriteNOTHOUSANDSGROUP)
  397. {
  398. dwDisplayFlags |= DF_NOTHOUSANDSGROUP;
  399. }
  400. // Go to the next property
  401. pProp = pProp->pNextSibling;
  402. }
  403. else if ( ZERO == pProp->ulId )
  404. {
  405. // "oh point..."
  406. // This will force a leading zero
  407. dwDisplayFlags |= DF_LEADINGZERO;
  408. pProp = pProp->pNextSibling;
  409. }
  410. UINT uiDecimalPlaces = 0;
  411. if ( pProp && (FP_PART == pProp->ulId) )
  412. {
  413. // Deal with the stuff to the right of the decimal point
  414. // Count up the number of decimal places, and for each
  415. // decimal place divide the value by 10
  416. // (e.g. 83 gets divided by 100).
  417. SPDBG_ASSERT( VT_R8 == pProp->vValue.vt );
  418. DOUBLE dblRightOfDecimal = pProp->vValue.dblVal;
  419. const WCHAR *pwc;
  420. for ( uiDecimalPlaces = 0, pwc = pProp->pszValue; *pwc; pwc++ )
  421. {
  422. if ( iswdigit( *pwc ) )
  423. {
  424. dblRightOfDecimal /= (DOUBLE) 10;
  425. uiDecimalPlaces++;
  426. }
  427. }
  428. *pdblVal += dblRightOfDecimal;
  429. }
  430. else if ( pProp && (FP_PART_D == pProp->ulId) )
  431. {
  432. // The user said "point" and one digit
  433. SPDBG_ASSERT( VT_UI4 == pProp->pFirstChild->vValue.vt );
  434. uiDecimalPlaces = 1;
  435. if ( *pdblVal >= 0 )
  436. {
  437. *pdblVal += pProp->pFirstChild->vValue.iVal / 10.0;
  438. }
  439. else
  440. {
  441. *pdblVal -= pProp->pFirstChild->vValue.iVal / 10.0;
  442. }
  443. }
  444. if (uiSigFigs)
  445. {
  446. *pdblVal = -*pdblVal;
  447. }
  448. MakeDisplayNumber( *pdblVal, dwDisplayFlags, uiFixedWidth, uiDecimalPlaces, pszVal, cSize, FALSE );
  449. return S_OK;
  450. } /* CTestITN_J::InterpretFPNumber */
  451. /***********************************************************************
  452. * CTestITN_J::InterpretFraction *
  453. *-------------------------------*
  454. * Description:
  455. * Interprets a fraction.
  456. * The DENOMINATOR property should be present.
  457. * If the NUMERATOR property is absent, it is assumed to be 1.
  458. * Divides the NUMERATOR by the DENOMINATOR and sets the value
  459. * accordingly.
  460. *************************************************************************/
  461. HRESULT CTestITN_J::InterpretFraction( const SPPHRASEPROPERTY *pProperties,
  462. DOUBLE *pdblVal,
  463. WCHAR *pszVal,
  464. UINT cSize)
  465. {
  466. if ( !pdblVal || !pszVal )
  467. {
  468. return E_POINTER;
  469. }
  470. SPDBG_ASSERT( pProperties );
  471. DOUBLE dblWholeValue = 0;
  472. DOUBLE dblFracValue = 1;
  473. BOOL bNegativeDenominator = FALSE;
  474. WCHAR wszBuffer[MAX_PATH];
  475. const SPPHRASEPROPERTY *pProp = pProperties;
  476. *pszVal = 0;
  477. // Space to store whatever characters follow the digits
  478. // in the numerator (like ")")
  479. WCHAR pszTemp[ 10 ]; // will never need this much
  480. pszTemp[0] = 0;
  481. // Whole part is optional, otherwise assumed to be 0
  482. if ( WHOLE == pProp->ulId )
  483. {
  484. SPDBG_ASSERT( VT_R8 == pProp->vValue.vt );
  485. dblWholeValue = pProp->vValue.dblVal;
  486. wcscpy( pszVal, pProp->pszValue );
  487. // Do we need to re-generate the numbers?
  488. if (!iswdigit( pszVal[wcslen(pszVal) - 1] ))
  489. {
  490. MakeDisplayNumber( dblWholeValue, DF_WHOLENUMBER, 0, 0, pszVal, MAX_PATH, TRUE );
  491. }
  492. // Add a space between the whole part and the fractional part
  493. wcscat( pszVal, L" " );
  494. SPDBG_ASSERT( pProp->pNextSibling );
  495. pProp = pProp->pNextSibling;
  496. }
  497. // Nothing is optional in Japanese, However, the order is different from English
  498. SPDBG_ASSERT( DENOMINATOR == pProp->ulId );
  499. // Look ahead to see if it is a nagative number
  500. bNegativeDenominator = (pProp->vValue.dblVal < 0);
  501. for( pProp = pProperties; NUMERATOR != pProp->ulId; pProp = pProp->pNextSibling );
  502. if( NUMERATOR == pProp->ulId)
  503. {
  504. SPDBG_ASSERT( VT_R8 == pProp->vValue.vt );
  505. dblFracValue = pProp->vValue.dblVal;
  506. if (bNegativeDenominator && (pProp->vValue.dblVal >= 0))
  507. {
  508. //put the minus sign here.
  509. wcscat( pszVal, L"-");
  510. bNegativeDenominator = FALSE;
  511. }
  512. // Do we need to re-generate the numbers?
  513. if (!iswdigit( pProp->pszValue[wcslen(pProp->pszValue) - 1] ))
  514. {
  515. MakeDisplayNumber( dblFracValue, DF_WHOLENUMBER, 0, 0, wszBuffer, MAX_PATH, TRUE );
  516. wcscat( pszVal, wszBuffer );
  517. }
  518. else
  519. {
  520. wcscat( pszVal, pProp->pszValue );
  521. }
  522. }
  523. else
  524. {
  525. // No numerator, assume 1
  526. wcscat( pszVal, L"1" );
  527. }
  528. wcscat( pszVal, L"/" );
  529. for( pProp = pProperties; DENOMINATOR != pProp->ulId; pProp = pProp->pNextSibling );
  530. SPDBG_ASSERT( DENOMINATOR == pProp->ulId );
  531. SPDBG_ASSERT( VT_R8 == pProp->vValue.vt );
  532. if ( 0 == pProp->vValue.dblVal )
  533. {
  534. // Will not ITN a fraction with a zero denominator
  535. return E_FAIL;
  536. }
  537. dblFracValue /= pProp->vValue.dblVal;
  538. if (!bNegativeDenominator && (pProp->vValue.dblVal<0))
  539. {
  540. // Do we need to re-generate the numbers?
  541. if (!iswdigit( pProp->pszValue[wcslen(pProp->pszValue) - 1] ))
  542. {
  543. MakeDisplayNumber( -pProp->vValue.dblVal, DF_WHOLENUMBER, 0, 0, wszBuffer, MAX_PATH, TRUE );
  544. wcscat( pszVal, wszBuffer );
  545. }
  546. else
  547. {
  548. wcscat( pszVal, pProp->pszValue+1 );
  549. }
  550. }
  551. else
  552. {
  553. // Do we need to re-generate the numbers?
  554. if (!iswdigit( pProp->pszValue[wcslen(pProp->pszValue) - 1] ))
  555. {
  556. MakeDisplayNumber( pProp->vValue.dblVal, DF_WHOLENUMBER, 0, 0, wszBuffer, MAX_PATH, TRUE );
  557. wcscat( pszVal, wszBuffer );
  558. }
  559. else
  560. {
  561. wcscat( pszVal, pProp->pszValue );
  562. }
  563. }
  564. // Tack on the ")" or "-" from the end of the numerator
  565. wcscat( pszVal, pszTemp );
  566. *pdblVal = dblWholeValue + dblFracValue;
  567. return S_OK;
  568. } /* CTestITN_J::InterpretFraction */
  569. /***********************************************************************
  570. * CTestITN_J::InterpretDate *
  571. *---------------------------*
  572. * Description:
  573. * Interprets a date.
  574. * Converts the date into a VT_DATE format, even though it
  575. * gets stored as a VT_R8 (both are 64-bit quantities).
  576. * The Japanese Grammar will not accept invalid date.
  577. *************************************************************************/
  578. HRESULT CTestITN_J::InterpretDate( const SPPHRASEPROPERTY *pProperties,
  579. DOUBLE *pdblVal,
  580. WCHAR *pszVal,
  581. UINT cSize)
  582. {
  583. if ( !pdblVal || !pszVal )
  584. {
  585. return E_POINTER;
  586. }
  587. *pszVal = 0;
  588. // Get the date formatting string to be used right now
  589. if ( 0 == ::GetLocaleInfo( LOCALE_USER_DEFAULT, LOCALE_SLONGDATE,
  590. m_pszLongDateFormat, MAX_DATE_FORMAT ) )
  591. {
  592. return E_FAIL;
  593. }
  594. SYSTEMTIME stDate;
  595. memset( (void *) &stDate, 0, sizeof( stDate ) );
  596. // Arguments for ::GetDateFormat()
  597. TCHAR *pszFormatArg = NULL;
  598. TCHAR pszFormat[ MAX_DATE_FORMAT ];
  599. const SPPHRASEPROPERTY *pProp;
  600. const SPPHRASEPROPERTY *pPropChild;
  601. const WCHAR* pwszEmperor;
  602. // Get the month
  603. for ( pProp = pProperties; pProp && ( GATSU != pProp->ulId ); pProp = pProp->pNextSibling )
  604. ;
  605. SPDBG_ASSERT( pProp ); //There should be a month, and the grammar is forcing it
  606. SPDBG_ASSERT( pProp->pFirstChild );
  607. pPropChild = pProp->pFirstChild;
  608. SPDBG_ASSERT( VT_UI4 == pPropChild->vValue.vt );
  609. SPDBG_ASSERT( (1 <= pPropChild->vValue.ulVal) && (12 >= pPropChild->vValue.ulVal) );
  610. if ( (pPropChild->vValue.ulVal < 1) || (pPropChild->vValue.ulVal > 12) )
  611. {
  612. return E_INVALIDARG;
  613. }
  614. stDate.wMonth = (WORD) pPropChild->vValue.ulVal;
  615. // Get the emperor's name
  616. for ( pProp = pProperties; pProp && ( IMPERIAL != pProp->ulId ); pProp = pProp->pNextSibling )
  617. ;
  618. if ( pProp )
  619. {
  620. SPDBG_ASSERT( pProp );
  621. pPropChild = pProp->pFirstChild;
  622. pwszEmperor = pPropChild->pszValue;
  623. }
  624. else
  625. {
  626. pwszEmperor = 0;
  627. }
  628. // Get the year
  629. for ( pProp = pProperties; pProp && ( NENN != pProp->ulId ); pProp = pProp->pNextSibling )
  630. ;
  631. const SPPHRASEPROPERTY * const pPropYear = pProp;
  632. if ( pProp )
  633. {
  634. SPDBG_ASSERT( pProp ); // There should be a year
  635. SPDBG_ASSERT( VT_R8 == pProp->vValue.vt );
  636. stDate.wYear = (WORD) pProp->vValue.dblVal;
  637. }
  638. // Attempt to get the day of month
  639. for ( pProp = pProperties; pProp && ( NICHI != pProp->ulId ); pProp = pProp->pNextSibling )
  640. ;
  641. const SPPHRASEPROPERTY * const pPropDayOfMonth = pProp;
  642. if ( pProp )
  643. {
  644. pPropChild = pProp->pFirstChild;
  645. SPDBG_ASSERT( VT_UI4 == pPropChild->vValue.vt );
  646. SPDBG_ASSERT( (1 <= pPropChild->vValue.ulVal) && (31 >= pPropChild->vValue.ulVal) );
  647. if ( (pPropChild->vValue.ulVal < 1) || (pPropChild->vValue.ulVal > 31) )
  648. {
  649. return E_INVALIDARG;
  650. }
  651. stDate.wDay = (WORD) pPropChild->vValue.ulVal;
  652. // Look for a day of week
  653. for ( pProp = pProperties; pProp && ( YOUBI != pProp->ulId ); pProp = pProp->pNextSibling )
  654. ;
  655. if ( pProp )
  656. {
  657. // Day of week present
  658. pPropChild = pProp->pFirstChild;
  659. SPDBG_ASSERT( VT_UI4 == pPropChild->vValue.vt );
  660. SPDBG_ASSERT( 6 >= pPropChild->vValue.ulVal );
  661. if ( pPropChild->vValue.ulVal > 6 )
  662. {
  663. return E_INVALIDARG;
  664. }
  665. stDate.wDayOfWeek = (WORD) pPropChild->vValue.ulVal;
  666. // Use the full long date format
  667. pszFormatArg = m_pszLongDateFormat;
  668. // If the user did say the day of week but the format string does
  669. // not called for the day of week being displayed anywhere,
  670. // Write out the day of week at the END of the output.
  671. if ( !_tcsstr( m_pszLongDateFormat, DAYOFWEEK_STR_ABBR ) )
  672. {
  673. _tcscat( m_pszLongDateFormat, " dddd" );
  674. }
  675. }
  676. else
  677. {
  678. TCHAR pszDayOfWeekStr[ MAX_LOCALE_DATA];
  679. // Remove the day of week from the current date format string
  680. TCHAR *pc = _tcsstr( m_pszLongDateFormat, DAYOFWEEK_STR );
  681. if ( pc )
  682. {
  683. _tcscpy( pszDayOfWeekStr, DAYOFWEEK_STR );
  684. }
  685. else if ( NULL != (pc = _tcsstr( m_pszLongDateFormat, DAYOFWEEK_STR_ABBR )) )
  686. {
  687. _tcscpy( pszDayOfWeekStr, DAYOFWEEK_STR_ABBR );
  688. }
  689. if ( pc )
  690. {
  691. // Copy over everything until this character info the format string
  692. _tcsncpy( pszFormat, m_pszLongDateFormat, (pc - m_pszLongDateFormat) );
  693. pszFormat[(pc - m_pszLongDateFormat)] = 0;
  694. // Skip over the day of week until the next symbol
  695. // (the way date format strings work, this is the first
  696. // alphabetical symbol
  697. pc += _tcslen( pszDayOfWeekStr );
  698. while ( *pc && !_istalpha( *pc ) )
  699. {
  700. pc++;
  701. }
  702. // Copy over everything from here on out
  703. _tcscat( pszFormat, pc );
  704. //dwFlags = 0;
  705. pszFormatArg = pszFormat;
  706. }
  707. else // We don't have DAY_OF_WEEK in either the display format nor the results.
  708. {
  709. pszFormatArg = m_pszLongDateFormat;
  710. }
  711. }
  712. }
  713. else
  714. {
  715. _tcscpy( pszFormat, "MMMM, yyyy" );
  716. pszFormatArg = pszFormat;
  717. }
  718. // Get the date in VariantTime form so we can set it as a semantic value
  719. int iRet = ::SystemTimeToVariantTime( &stDate, pdblVal );
  720. if ( 0 == iRet )
  721. {
  722. // Not serious, just the semantic value will be wrong
  723. *pdblVal = 0;
  724. }
  725. // Do the formatting
  726. iRet = FormatDate( stDate, pszFormatArg, pszVal, cSize, pwszEmperor );
  727. if ( 0 == iRet )
  728. {
  729. return E_FAIL;
  730. }
  731. return S_OK;
  732. } /* CTestITN_J::InterpretDate */
  733. /***********************************************************************
  734. * CTestITN_J::InterpretTime *
  735. *---------------------------*
  736. * Description:
  737. * Interprets time, which can be of the following forms:
  738. * * Hour with qualifier ("half past three"), time marker optional
  739. * * Hour with minutes, time marker mandatory
  740. * * Military time "hundred hours"
  741. * * Hour with "o'clock", time marker optional
  742. * * Number of hours and number of minutes and optional number
  743. * of seconds
  744. * Return:
  745. * S_OK
  746. * E_POINTER if !pdblVal or !pszVal
  747. *************************************************************************/
  748. HRESULT CTestITN_J::InterpretTime( const SPPHRASEPROPERTY *pProperties,
  749. DOUBLE *pdblVal,
  750. WCHAR *pszVal,
  751. UINT cSize )
  752. {
  753. if ( !pdblVal || !pszVal )
  754. {
  755. return E_POINTER;
  756. }
  757. // Time marker and seconds should not be shown unless some
  758. // component of the time specifically requires it
  759. DWORD dwFlags = TIME_NOSECONDS | TIME_NOTIMEMARKER;
  760. SYSTEMTIME stTime;
  761. ::memset( (void *) &stTime, 0, sizeof( SYSTEMTIME ) );
  762. bool fPM = false;
  763. bool fClockTime = true;
  764. bool fMinuteMinus = false;
  765. const SPPHRASEPROPERTY *pProp;
  766. for ( pProp = pProperties; pProp; pProp = pProp->pNextSibling )
  767. {
  768. #if 1
  769. switch ( pProp->ulId )
  770. {
  771. case GOZENN: // If it is PM, add the hour by 12
  772. {
  773. if (pProp->pszValue[0] == L'P')
  774. {
  775. fPM = TRUE;
  776. }
  777. dwFlags &= ~TIME_NOTIMEMARKER;
  778. break;
  779. }
  780. case JI:
  781. {
  782. UINT uiHour = pProp->pFirstChild->vValue.uiVal;
  783. stTime.wHour = (WORD) uiHour;
  784. if (fPM && (stTime.wHour < 12))
  785. {
  786. stTime.wHour += 12;
  787. }
  788. break;
  789. }
  790. case HOUR_COUNT:
  791. {
  792. // Just take the hour for what it is
  793. stTime.wHour = (WORD) pProp->vValue.dblVal;
  794. // This is not a clock time
  795. fClockTime = false;
  796. break;
  797. }
  798. case MINUTE:
  799. {
  800. // Minutes are evaluted as numbers, so their values
  801. // are stored as doubles
  802. stTime.wMinute += (WORD) pProp->pFirstChild->vValue.uiVal;
  803. break;
  804. }
  805. case HUNN:
  806. {
  807. // Special case for :30 (��)
  808. stTime.wMinute = 30;
  809. break;
  810. }
  811. case SECOND:
  812. {
  813. stTime.wSecond += (WORD) pProp->pFirstChild->vValue.uiVal;
  814. dwFlags &= ~TIME_NOSECONDS;
  815. break;
  816. }
  817. case MINUTE_TAG:
  818. {
  819. // We only need to deal with the case of ���O
  820. if( pProp->pszValue[0] == L'-' )
  821. {
  822. fMinuteMinus = true;
  823. }
  824. break;
  825. }
  826. default:
  827. SPDBG_ASSERT( false );
  828. }
  829. #endif
  830. }
  831. if (fMinuteMinus)
  832. {
  833. stTime.wMinute = 60 - stTime.wMinute;
  834. stTime.wHour--;
  835. }
  836. HRESULT hr = S_OK;
  837. if ( fClockTime )
  838. {
  839. // Get the time in VariantTime form so we can set it as a semantic value
  840. if ( 0 == ::SystemTimeToVariantTime( &stTime, pdblVal ) )
  841. {
  842. // Not serious, just the semantic value will be wrong
  843. *pdblVal = 0;
  844. }
  845. TCHAR *pszTime = new TCHAR[ cSize ];
  846. if ( !pszTime )
  847. {
  848. return E_OUTOFMEMORY;
  849. }
  850. if (stTime.wHour >= 24)
  851. {
  852. stTime.wHour -= 24; //To avoid problems in GetTimeFormat
  853. }
  854. if (stTime.wHour >= 12) // Enable the TimeMarker if the time is in the afternoon
  855. {
  856. dwFlags &= ~TIME_NOTIMEMARKER;
  857. }
  858. int iRet = ::GetTimeFormat( LOCALE_USER_DEFAULT, dwFlags, &stTime, NULL,
  859. pszTime, cSize );
  860. USES_CONVERSION;
  861. wcscpy( pszVal, T2W(pszTime) );
  862. delete[] pszTime;
  863. // NB: GetTimeFormat() will put an extra space at the end of the
  864. // time if the default format has AM or PM but TIME_NOTIMEMARKER is
  865. // set
  866. if ( iRet && (TIME_NOTIMEMARKER & dwFlags) )
  867. {
  868. WCHAR *pwc = pszVal + wcslen( pszVal ) - 1;
  869. while ( iswspace( *pwc ) )
  870. {
  871. *pwc-- = 0;
  872. }
  873. }
  874. hr = iRet ? S_OK : E_FAIL;
  875. }
  876. else
  877. {
  878. // No need to go through the system's time formatter
  879. if ( cSize < 10 ) // Space for xxx:xx:xx\0
  880. {
  881. return E_INVALIDARG;
  882. }
  883. if ( dwFlags & TIME_NOSECONDS )
  884. {
  885. swprintf( pszVal, L"%d:%02d", stTime.wHour, stTime.wMinute );
  886. }
  887. else
  888. {
  889. swprintf( pszVal, L"%d:%02d:%02d",
  890. stTime.wHour, stTime.wMinute, stTime.wSecond );
  891. }
  892. }
  893. return hr;
  894. } /* CTestITN_J::InterpretTime */
  895. /***********************************************************************
  896. * CTestITN_J::InterpretDegrees *
  897. *------------------------------*
  898. * Description:
  899. * Interprets degrees as a angle-measurement or direction
  900. * Return:
  901. * S_OK
  902. * E_POINTER
  903. * E_INVALIDARG
  904. *************************************************************************/
  905. HRESULT CTestITN_J::InterpretDegrees( const SPPHRASEPROPERTY *pProperties,
  906. DOUBLE *pdblVal,
  907. WCHAR *pszVal,
  908. UINT cSize )
  909. {
  910. if ( !pProperties || !pdblVal || !pszVal )
  911. {
  912. return E_POINTER;
  913. }
  914. WCHAR *pwchDirection = 0;
  915. *pszVal = 0;
  916. //Do we have those direction tags
  917. if (DIRECTION_TAG == pProperties->ulId)
  918. {
  919. pwchDirection = (WCHAR*) pProperties->pszValue;
  920. pProperties = pProperties->pNextSibling;
  921. }
  922. // Get the number
  923. *pdblVal = pProperties->vValue.dblVal;
  924. wcscat( pszVal, pProperties->pszValue );
  925. wcscat( pszVal, s_pwszDegree );
  926. pProperties = pProperties->pNextSibling;
  927. if ( pProperties && (MINUTE == pProperties->ulId ) )
  928. {
  929. SPDBG_ASSERT( *pdblVal >= 0 );
  930. DOUBLE dblMin = pProperties->vValue.dblVal;
  931. *pdblVal += dblMin / (DOUBLE) 60;
  932. wcscat( pszVal, pProperties->pszValue );
  933. wcscat( pszVal, s_pwszMinute );
  934. pProperties = pProperties->pNextSibling;
  935. }
  936. if ( pProperties && (SECOND == pProperties->ulId) )
  937. {
  938. DOUBLE dblSec = pProperties->vValue.dblVal;
  939. *pdblVal += dblSec / (DOUBLE) 3600;
  940. wcscat( pszVal, pProperties->pszValue );
  941. wcscat( pszVal, s_pwszSecond );
  942. pProperties = pProperties->pNextSibling;
  943. }
  944. if (pwchDirection)
  945. {
  946. wcscat( pszVal, pwchDirection );
  947. }
  948. SPDBG_ASSERT( !pProperties );
  949. return S_OK;
  950. } /* CTestITN_J::InterpretDegrees */
  951. /***********************************************************************
  952. * CTestITN_J::InterpretMeasurement *
  953. *----------------------------------*
  954. * Description:
  955. * Interprets measurements, which is a number followed
  956. * by a units name
  957. * Return:
  958. * S_OK
  959. * E_POINTER
  960. * E_INVALIDARG
  961. *************************************************************************/
  962. HRESULT CTestITN_J::InterpretMeasurement( const SPPHRASEPROPERTY *pProperties,
  963. DOUBLE *pdblVal,
  964. WCHAR *pszVal,
  965. UINT cSize )
  966. {
  967. if ( !pProperties || !pdblVal || !pszVal )
  968. {
  969. return E_POINTER;
  970. }
  971. const SPPHRASEPROPERTY *pPropNumber = NULL;
  972. const SPPHRASEPROPERTY *pPropUnits = NULL;
  973. const SPPHRASEPROPERTY *pProp;
  974. for(pProp= pProperties; pProp; pProp = pProp->pNextSibling)
  975. {
  976. if (NUMBER == pProp->ulId )
  977. pPropNumber = pProp;
  978. else if ( UNITS == pProp->ulId )
  979. pPropUnits = pProp;
  980. }
  981. if (!pPropUnits || !pPropNumber )
  982. {
  983. SPDBG_ASSERT( FALSE );
  984. return E_INVALIDARG;
  985. }
  986. if ( cSize < (wcslen(pPropNumber->pszValue) + wcslen(pPropUnits->pszValue) + 1) )
  987. {
  988. // Not enough space
  989. return E_INVALIDARG;
  990. }
  991. // Do we need to re-generate the numbers?
  992. if (!iswdigit( pPropNumber->pszValue[wcslen(pPropNumber->pszValue) - 1] ))
  993. {
  994. MakeDisplayNumber( pPropNumber->vValue.dblVal, DF_WHOLENUMBER, 0, 0, pszVal, MAX_PATH, TRUE );
  995. }
  996. else
  997. {
  998. wcscpy(pszVal, pPropNumber->pszValue );
  999. }
  1000. wcscat( pszVal, pPropUnits->pszValue );
  1001. *pdblVal = pPropNumber->vValue.dblVal;
  1002. return S_OK;
  1003. } /* CTestITN_J::InterpretMeasurement */
  1004. /***********************************************************************
  1005. * CTestITN_J::InterpretCurrency *
  1006. *-------------------------------*
  1007. * Description:
  1008. * Interprets currency.
  1009. * Return:
  1010. * S_OK
  1011. * E_POINTER if !pdblVal or !pszVal
  1012. * E_INVALIDARG if the number of cents is not between 0 and 99
  1013. * inclusive
  1014. *************************************************************************/
  1015. HRESULT CTestITN_J::InterpretCurrency( const SPPHRASEPROPERTY *pProperties,
  1016. DOUBLE *pdblVal,
  1017. WCHAR *pszVal,
  1018. UINT cSize)
  1019. {
  1020. if ( !pdblVal || !pszVal )
  1021. {
  1022. return E_POINTER;
  1023. }
  1024. // Find the dollars and cents properties
  1025. const SPPHRASEPROPERTY *pPropDollars;
  1026. for ( pPropDollars = pProperties;
  1027. pPropDollars && ( YENs != pPropDollars->ulId );
  1028. pPropDollars = pPropDollars->pNextSibling )
  1029. ;
  1030. SPDBG_ASSERT( pPropDollars );
  1031. const WCHAR *pszDollars = NULL;
  1032. DOUBLE dblDollars = 0;
  1033. if ( pPropDollars )
  1034. {
  1035. SPDBG_ASSERT( VT_R8 == pPropDollars->vValue.vt );
  1036. pszDollars = pPropDollars->pszValue;
  1037. dblDollars = pPropDollars->vValue.dblVal;
  1038. }
  1039. if (pPropDollars)
  1040. {
  1041. //Japanese people don't like \1Million, for the case of whole numbers like MANN, OKU,
  1042. //Simply write out the YEN at the end.
  1043. if (iswdigit( pszDollars[wcslen(pszDollars) - 1] ))
  1044. {
  1045. wcscpy(pszVal + 1, pszDollars);
  1046. pszVal[0] = L'\\';
  1047. }
  1048. else
  1049. {
  1050. wcscpy(pszVal, pszDollars);
  1051. wcscat(pszVal, L"\x5186");
  1052. }
  1053. }
  1054. *pdblVal = dblDollars;
  1055. return S_OK;
  1056. } /* CTestITN_J::InterpretCurrency */
  1057. /***********************************************************************
  1058. * CTestITN_J::AddPropertyAndReplacement *
  1059. *---------------------------------------*
  1060. * Description:
  1061. * Takes all of the info that we want to pass into the
  1062. * engine site, forms the SPPHRASEPROPERTY and
  1063. * SPPHRASERREPLACEMENT, and adds them to the engine site
  1064. * Return:
  1065. * Return values of ISpCFGInterpreterSite::AddProperty()
  1066. * and ISpCFGInterpreterSite::AddTextReplacement()
  1067. *************************************************************************/
  1068. HRESULT CTestITN_J::AddPropertyAndReplacement( const WCHAR *szBuff,
  1069. const DOUBLE dblValue,
  1070. const ULONG ulMinPos,
  1071. const ULONG ulMaxPos,
  1072. const ULONG ulFirstElement,
  1073. const ULONG ulCountOfElements )
  1074. {
  1075. // Add the property
  1076. SPPHRASEPROPERTY prop;
  1077. memset(&prop,0,sizeof(prop));
  1078. prop.pszValue = szBuff;
  1079. prop.vValue.vt = VT_R8;
  1080. prop.vValue.dblVal = dblValue;
  1081. prop.ulFirstElement = ulMinPos;
  1082. prop.ulCountOfElements = ulMaxPos - ulMinPos;
  1083. HRESULT hr = m_pSite->AddProperty(&prop);
  1084. if (SUCCEEDED(hr))
  1085. {
  1086. SPPHRASEREPLACEMENT repl;
  1087. memset(&repl,0, sizeof(repl));
  1088. repl.bDisplayAttributes = SPAF_ONE_TRAILING_SPACE;
  1089. repl.pszReplacementText = szBuff;
  1090. repl.ulFirstElement = ulFirstElement;
  1091. repl.ulCountOfElements = ulCountOfElements;
  1092. hr = m_pSite->AddTextReplacement(&repl);
  1093. }
  1094. return hr;
  1095. } /* CTestITN_J::AddPropertyAndReplacement */
  1096. // Helper functions
  1097. /***********************************************************************
  1098. * CTestITN_J::MakeDisplayNumber *
  1099. *-------------------------------*
  1100. * Description:
  1101. * Converts a DOUBLE into a displayable
  1102. * number in the range -999,999,999,999 to +999,999,999,999.
  1103. * cSize is the number of chars for which pwszNum has space
  1104. * allocated.
  1105. * If DF_UNFORMATTED is set, all other flags are ignored,
  1106. * and the number is passed back as an optional negative
  1107. * followed by a string of digits
  1108. * If DF_ORDINAL is set in dwDisplayFlags, displays an
  1109. * ordinal number (i.e. tacks on "th" or the appropriate suffix.
  1110. * If DF_WHOLENUMBER is set, lops off the decimal separator
  1111. * and everything after it. If DF_WHOLENUMBER is not set,
  1112. * then uses the uiDecimalPlaces parameter to determine
  1113. * how many decimal places to display
  1114. * If DF_FIXEDWIDTH is set, will display at least uiFixedWidth
  1115. * digits; otherwise uiFixedWidth is ignored.
  1116. * If DF_NOTHOUSANDSGROUP is set, will not do thousands
  1117. * grouping (commas)
  1118. *************************************************************************/
  1119. HRESULT CTestITN_J::MakeDisplayNumber( DOUBLE dblNum,
  1120. DWORD dwDisplayFlags,
  1121. UINT uiFixedWidth,
  1122. UINT uiDecimalPlaces,
  1123. WCHAR *pwszNum,
  1124. UINT cSize,
  1125. BOOL bForced)
  1126. {
  1127. SPDBG_ASSERT( pwszNum );
  1128. SPDBG_ASSERT( !SPIsBadWritePtr( pwszNum, cSize ) );
  1129. *pwszNum = 0;
  1130. // check for straight millions and straight billions
  1131. if (( dwDisplayFlags & DF_WHOLENUMBER ) && (dblNum > 0) && !bForced)
  1132. {
  1133. HRESULT hr;
  1134. if ( 0 == (( ((LONGLONG) dblNum) % CHUU )) )
  1135. {
  1136. // e.g. for "five billion" get the "5" and then
  1137. // tack on " billion"
  1138. hr = MakeDisplayNumber( ( dblNum / ((DOUBLE) CHUU) ),
  1139. dwDisplayFlags, uiFixedWidth, uiDecimalPlaces, pwszNum, cSize, FALSE );
  1140. if ( SUCCEEDED( hr ) )
  1141. {
  1142. //wcscat( pwszNum, L" " );
  1143. wcscat( pwszNum, CHUU_STR );
  1144. }
  1145. return hr;
  1146. }
  1147. else if (( ((LONGLONG) dblNum) < CHUU ) &&
  1148. ( 0 == (( ((LONGLONG) dblNum) % OKU )) ))
  1149. {
  1150. hr = MakeDisplayNumber( ( dblNum / ((DOUBLE) OKU) ),
  1151. dwDisplayFlags, uiFixedWidth, uiDecimalPlaces, pwszNum, cSize, FALSE );
  1152. if ( SUCCEEDED( hr ) )
  1153. {
  1154. //wcscat( pwszNum, L" " );
  1155. wcscat( pwszNum, OKU_STR );
  1156. }
  1157. return hr;
  1158. }
  1159. else if (( ((LONGLONG) dblNum) < OKU ) &&
  1160. ( 0 == (( ((LONGLONG) dblNum) % MANN )) ))
  1161. {
  1162. hr = MakeDisplayNumber( ( dblNum / ((DOUBLE) MANN) ),
  1163. dwDisplayFlags, uiFixedWidth, uiDecimalPlaces, pwszNum, cSize, FALSE );
  1164. if ( SUCCEEDED( hr ) )
  1165. {
  1166. //wcscat( pwszNum, L" " );
  1167. wcscat( pwszNum, MANN_STR );
  1168. }
  1169. return hr;
  1170. }
  1171. }
  1172. // Put in the negative sign if necessary
  1173. if ( dblNum < 0 )
  1174. {
  1175. wcscat( pwszNum, L"-" );
  1176. // From now on we want to deal with the magnitude of the number
  1177. dblNum *= -1;
  1178. }
  1179. SPDBG_ASSERT( dblNum < 1e16 );
  1180. WCHAR *pwszTemp = new WCHAR[ cSize ];
  1181. if ( !pwszTemp )
  1182. {
  1183. return E_OUTOFMEMORY;
  1184. }
  1185. *pwszTemp = 0;
  1186. LONGLONG llIntPart = (LONGLONG) dblNum;
  1187. UINT uiDigitsLeftOfDecimal;
  1188. if ( dwDisplayFlags & DF_WHOLENUMBER )
  1189. {
  1190. swprintf( pwszTemp, L"%I64d", llIntPart );
  1191. uiDigitsLeftOfDecimal = wcslen( pwszTemp );
  1192. }
  1193. else
  1194. {
  1195. swprintf( pwszTemp, L"%.*f", uiDecimalPlaces, dblNum );
  1196. WCHAR *pwc = wcschr( pwszTemp, L'.' );
  1197. uiDigitsLeftOfDecimal = pwc - pwszTemp;
  1198. }
  1199. // The following handles the case where the user said something
  1200. // like "zero zero zero three" and wants to see "0,003"
  1201. BOOL fChangedFirstDigit = false;
  1202. const WCHAR wcFakeFirstDigit = L'1';
  1203. if ( !(dwDisplayFlags & DF_UNFORMATTED) &&
  1204. (dwDisplayFlags & DF_FIXEDWIDTH) && (uiDigitsLeftOfDecimal < uiFixedWidth) )
  1205. {
  1206. // The following handles the case where the user wants leading
  1207. // zeroes displayed
  1208. // Need to pad the front with zeroes
  1209. for ( UINT ui = 0; ui < (uiFixedWidth - uiDigitsLeftOfDecimal); ui++ )
  1210. {
  1211. wcscat( pwszNum, L"0" );
  1212. }
  1213. // HACK
  1214. // In order to force something like "zero zero zero three"
  1215. // into the form "0,003", we need to make GetNumberFormat()
  1216. // think that the first digit is 1.
  1217. WCHAR *pwc = wcschr( pwszNum, L'0' );
  1218. SPDBG_ASSERT( pwc );
  1219. *pwc = wcFakeFirstDigit;
  1220. fChangedFirstDigit = true;
  1221. }
  1222. // Copy over the unformatted number after the possible negative sign
  1223. wcscat( pwszNum, pwszTemp );
  1224. delete[] pwszTemp;
  1225. // If we do not want to format the number, then bail here
  1226. if ( dwDisplayFlags & DF_UNFORMATTED )
  1227. {
  1228. return S_OK;
  1229. }
  1230. // Get the default number formatting.
  1231. // Note that this gets called every time, since otherwise there
  1232. // is no way to pick up changes that the user has made since
  1233. // this process has started.
  1234. GetNumberFormatDefaults();
  1235. // Make a copy so that we can change some fields according to the
  1236. // flags param
  1237. NUMBERFMT nmfmt = m_nmfmtDefault;
  1238. // How many decimal places to display?
  1239. if ( dwDisplayFlags & DF_WHOLENUMBER )
  1240. {
  1241. nmfmt.NumDigits = 0;
  1242. }
  1243. else
  1244. {
  1245. // Use the uiDecimalPlaces value to determine how
  1246. // many to display
  1247. nmfmt.NumDigits = uiDecimalPlaces;
  1248. }
  1249. // Leading zeroes?
  1250. nmfmt.LeadingZero = (dwDisplayFlags & DF_LEADINGZERO) ? 1 : 0;
  1251. // Thousands grouping?
  1252. if ( dwDisplayFlags & DF_NOTHOUSANDSGROUP )
  1253. {
  1254. nmfmt.Grouping = 0;
  1255. }
  1256. // Format the number string
  1257. TCHAR *pszFormattedNum = new TCHAR[ cSize ];
  1258. if ( !pszFormattedNum )
  1259. {
  1260. return E_OUTOFMEMORY;
  1261. }
  1262. *pszFormattedNum = 0;
  1263. USES_CONVERSION;
  1264. int iRet;
  1265. do
  1266. {
  1267. iRet = ::GetNumberFormat( LOCALE_USER_DEFAULT, 0,
  1268. W2T( pwszNum ), &nmfmt, pszFormattedNum, cSize );
  1269. if ( !iRet && nmfmt.NumDigits )
  1270. {
  1271. // Try displaying fewer digits
  1272. nmfmt.NumDigits--;
  1273. }
  1274. } while ( !iRet && nmfmt.NumDigits );
  1275. SPDBG_ASSERT( iRet );
  1276. // Copy the formatted number into pwszNum
  1277. wcscpy( pwszNum, T2W(pszFormattedNum) );
  1278. delete[] pszFormattedNum;
  1279. // This undoes the hack of changing the first digit
  1280. if ( fChangedFirstDigit )
  1281. {
  1282. // We need to find the first digit and change it back to zero
  1283. WCHAR *pwc = wcschr( pwszNum, wcFakeFirstDigit );
  1284. SPDBG_ASSERT( pwc );
  1285. *pwc = L'0';
  1286. }
  1287. if ( dwDisplayFlags & DF_ORDINAL )
  1288. {
  1289. SPDBG_ASSERT( dwDisplayFlags & DF_WHOLENUMBER ); // sanity
  1290. // This is an ordinal number, tack on the appropriate suffix
  1291. // The "st", "nd", "rd" endings only happen when you
  1292. // don't have something like "twelfth"
  1293. if ( ((llIntPart % 100) < 10) || ((llIntPart % 100) > 20) )
  1294. {
  1295. switch ( llIntPart % 10 )
  1296. {
  1297. case 1:
  1298. wcscat( pwszNum, L"st" );
  1299. break;
  1300. case 2:
  1301. wcscat( pwszNum, L"nd" );
  1302. break;
  1303. case 3:
  1304. wcscat( pwszNum, L"rd" );
  1305. break;
  1306. default:
  1307. wcscat( pwszNum, L"th" );
  1308. break;
  1309. }
  1310. }
  1311. else
  1312. {
  1313. wcscat( pwszNum, L"th" );
  1314. }
  1315. }
  1316. return S_OK;
  1317. } /* CTestITN_J::MakeDisplayNumber */
  1318. /***********************************************************************
  1319. * CTestITN_J::GetNumberFormatDefaults *
  1320. *-------------------------------------*
  1321. * Description:
  1322. * This finds all of the defaults for formatting numbers for
  1323. * this user.
  1324. *************************************************************************/
  1325. void CTestITN_J::GetNumberFormatDefaults()
  1326. {
  1327. LCID lcid = ::GetUserDefaultLCID();
  1328. TCHAR pszLocaleData[ MAX_LOCALE_DATA ];
  1329. ::GetLocaleInfo( lcid, LOCALE_IDIGITS, pszLocaleData, MAX_LOCALE_DATA );
  1330. m_nmfmtDefault.NumDigits = _ttoi( pszLocaleData );
  1331. ::GetLocaleInfo( lcid, LOCALE_ILZERO, pszLocaleData, MAX_LOCALE_DATA );
  1332. // It's always either 0 or 1
  1333. m_nmfmtDefault.LeadingZero = _ttoi( pszLocaleData );
  1334. ::GetLocaleInfo( lcid, LOCALE_SGROUPING, pszLocaleData, MAX_LOCALE_DATA );
  1335. // It will look like single_digit;0, or else it will look like
  1336. // 3;2;0
  1337. UINT uiGrouping = *pszLocaleData - _T('0');
  1338. if ( (3 == uiGrouping) && (_T(';') == pszLocaleData[1]) && (_T('2') == pszLocaleData[2]) )
  1339. {
  1340. uiGrouping = 32;
  1341. }
  1342. m_nmfmtDefault.Grouping = uiGrouping;
  1343. ::GetLocaleInfo( lcid, LOCALE_SDECIMAL, m_pszDecimalSep, MAX_LOCALE_DATA );
  1344. m_nmfmtDefault.lpDecimalSep = m_pszDecimalSep;
  1345. ::GetLocaleInfo( lcid, LOCALE_STHOUSAND, m_pszThousandSep, MAX_LOCALE_DATA );
  1346. m_nmfmtDefault.lpThousandSep = m_pszThousandSep;
  1347. ::GetLocaleInfo( lcid, LOCALE_INEGNUMBER, pszLocaleData, MAX_LOCALE_DATA );
  1348. m_nmfmtDefault.NegativeOrder = _ttoi( pszLocaleData );
  1349. } /* CTestITN_J::GetNumberFormatDefaults
  1350. /***********************************************************************
  1351. * HandleDigitsAfterDecimal *
  1352. *--------------------------*
  1353. * Description:
  1354. * If pwszRightOfDecimal is NULL, then cuts off all of the numbers
  1355. * following the decimal separator.
  1356. * Otherwise, copies pwszRightOfDecimal right after the decimal
  1357. * separator in pwszFormattedNum.
  1358. * Preserves the stuff after the digits in the pwszFormattedNum
  1359. * (e.g. if pwszFormattedNum starts out "(3.00)" and
  1360. * pwszRightOfDecimal is NULL, then pwszFormattedNum will end
  1361. * up as "(3)"
  1362. *************************************************************************/
  1363. void HandleDigitsAfterDecimal( WCHAR *pwszFormattedNum,
  1364. UINT cSizeOfFormattedNum,
  1365. const WCHAR *pwszRightOfDecimal )
  1366. {
  1367. SPDBG_ASSERT( pwszFormattedNum );
  1368. // First need to find what the decimal string is
  1369. LCID lcid = ::GetUserDefaultLCID();
  1370. TCHAR pszDecimalString[ 5 ]; // Guaranteed to be no longer than 4 long
  1371. int iRet = ::GetLocaleInfo( lcid, LOCALE_SDECIMAL, pszDecimalString, 4 );
  1372. SPDBG_ASSERT( iRet );
  1373. USES_CONVERSION;
  1374. WCHAR *pwcDecimal = wcsstr( pwszFormattedNum, T2W(pszDecimalString) );
  1375. SPDBG_ASSERT( pwcDecimal );
  1376. // pwcAfterDecimal points to the first character after the decimal separator
  1377. WCHAR *pwcAfterDecimal = pwcDecimal + _tcslen( pszDecimalString );
  1378. // Remember what originally followed the digits
  1379. WCHAR *pwszTemp = new WCHAR[ cSizeOfFormattedNum ];
  1380. WCHAR *pwcAfterDigits; // points to the first character after the end of the digits
  1381. for ( pwcAfterDigits = pwcAfterDecimal;
  1382. *pwcAfterDigits && iswdigit( *pwcAfterDigits );
  1383. pwcAfterDigits++ )
  1384. ;
  1385. wcscpy( pwszTemp, pwcAfterDigits ); // OK if *pwcAfterDigits == 0
  1386. if ( pwszRightOfDecimal )
  1387. {
  1388. // This means that the caller wants the digits in pwszRightOfDecimal
  1389. // copied after the decimal separator
  1390. // Copy the decimal string after the decimal separater
  1391. wcscpy( pwcAfterDecimal, pwszRightOfDecimal );
  1392. }
  1393. else
  1394. {
  1395. // This means that the caller wanted the decimal separator
  1396. // and all text following it stripped off
  1397. *pwcDecimal = 0;
  1398. }
  1399. // Add on the extra after-digit characters
  1400. wcscat( pwszFormattedNum, pwszTemp );
  1401. delete[] pwszTemp;
  1402. } /* HandleDigitsAfterDecimal */
  1403. /***********************************************************************
  1404. * ComputeNum9999 *
  1405. *----------------*
  1406. * Description:
  1407. * Converts a set of SPPHRASEPROPERTYs into a number in
  1408. * [-9999, 9999].
  1409. * The way these properties is structured is that the top-level
  1410. * properties contain the place of the number (100s, 10s, 1s)
  1411. * by having the value 100, 10, or 1.
  1412. * The child has the appropriate number value.
  1413. * Return:
  1414. * Value of the number
  1415. *************************************************************************/
  1416. ULONG ComputeNum9999(const SPPHRASEPROPERTY *pProperties )//, ULONG *pVal)
  1417. {
  1418. ULONG ulVal = 0;
  1419. for (const SPPHRASEPROPERTY * pProp = pProperties; pProp; pProp = pProp->pNextSibling)
  1420. {
  1421. if ( ZERO != pProp->ulId )
  1422. {
  1423. SPDBG_ASSERT( VT_UI4 == pProp->vValue.vt );
  1424. ulVal += pProp->vValue.ulVal;
  1425. }
  1426. }
  1427. return ulVal;
  1428. } /* ComputeNum9999 */
  1429. /***********************************************************************
  1430. * GetMinAndMaxPos *
  1431. *-----------------*
  1432. * Description:
  1433. * Gets the minimum and maximum elements spanned by the
  1434. * set of properties
  1435. *************************************************************************/
  1436. void GetMinAndMaxPos( const SPPHRASEPROPERTY *pProperties,
  1437. ULONG *pulMinPos,
  1438. ULONG *pulMaxPos )
  1439. {
  1440. if ( !pulMinPos || !pulMaxPos )
  1441. {
  1442. return;
  1443. }
  1444. ULONG ulMin = 9999999;
  1445. ULONG ulMax = 0;
  1446. for ( const SPPHRASEPROPERTY *pProp = pProperties; pProp; pProp = pProp->pNextSibling )
  1447. {
  1448. ulMin = __min( ulMin, pProp->ulFirstElement );
  1449. ulMax = __max( ulMax, pProp->ulFirstElement + pProp->ulCountOfElements );
  1450. }
  1451. *pulMinPos = ulMin;
  1452. *pulMaxPos = ulMax;
  1453. } /* GetMinAndMaxPos */
  1454. /***********************************************************************
  1455. * GetMonthName *
  1456. *--------------*
  1457. * Description:
  1458. * Gets the name of the month, abbreviated if desired
  1459. * Return:
  1460. * Number of characters written to pszMonth, 0 if failed
  1461. *************************************************************************/
  1462. int GetMonthName( int iMonth, WCHAR *pwszMonth, int cSize, bool fAbbrev )
  1463. {
  1464. LCTYPE lctype;
  1465. switch ( iMonth )
  1466. {
  1467. case 1:
  1468. lctype = fAbbrev ? LOCALE_SABBREVMONTHNAME1 : LOCALE_SMONTHNAME1;
  1469. break;
  1470. case 2:
  1471. lctype = fAbbrev ? LOCALE_SABBREVMONTHNAME2 : LOCALE_SMONTHNAME2;
  1472. break;
  1473. case 3:
  1474. lctype = fAbbrev ? LOCALE_SABBREVMONTHNAME3 : LOCALE_SMONTHNAME3;
  1475. break;
  1476. case 4:
  1477. lctype = fAbbrev ? LOCALE_SABBREVMONTHNAME4 : LOCALE_SMONTHNAME4;
  1478. break;
  1479. case 5:
  1480. lctype = fAbbrev ? LOCALE_SABBREVMONTHNAME5 : LOCALE_SMONTHNAME5;
  1481. break;
  1482. case 6:
  1483. lctype = fAbbrev ? LOCALE_SABBREVMONTHNAME6 : LOCALE_SMONTHNAME6;
  1484. break;
  1485. case 7:
  1486. lctype = fAbbrev ? LOCALE_SABBREVMONTHNAME7 : LOCALE_SMONTHNAME7;
  1487. break;
  1488. case 8:
  1489. lctype = fAbbrev ? LOCALE_SABBREVMONTHNAME8 : LOCALE_SMONTHNAME8;
  1490. break;
  1491. case 9:
  1492. lctype = fAbbrev ? LOCALE_SABBREVMONTHNAME9 : LOCALE_SMONTHNAME9;
  1493. break;
  1494. case 10:
  1495. lctype = fAbbrev ? LOCALE_SABBREVMONTHNAME10 : LOCALE_SMONTHNAME10;
  1496. break;
  1497. case 11:
  1498. lctype = fAbbrev ? LOCALE_SABBREVMONTHNAME11 : LOCALE_SMONTHNAME11;
  1499. break;
  1500. case 12:
  1501. lctype = fAbbrev ? LOCALE_SABBREVMONTHNAME12 : LOCALE_SMONTHNAME12;
  1502. break;
  1503. default:
  1504. return 0;
  1505. }
  1506. TCHAR *pszMonth = new TCHAR[ cSize ];
  1507. if ( !pszMonth )
  1508. {
  1509. return 0;
  1510. }
  1511. int iRet = ::GetLocaleInfo( LOCALE_USER_DEFAULT, lctype, pszMonth, cSize );
  1512. iRet = _mbslen((const unsigned char*) pszMonth); //Jpn needs chars, not bytes
  1513. USES_CONVERSION;
  1514. wcscpy( pwszMonth, T2W(pszMonth) );
  1515. delete[] pszMonth;
  1516. return iRet;
  1517. } /* GetMonthName */
  1518. /***********************************************************************
  1519. * GetDayOfWeekName *
  1520. *------------------*
  1521. * Description:
  1522. * Gets the name of the day of week, abbreviated if desired
  1523. * Return:
  1524. * Number of characters written to pszDayOfWeek, 0 if failed
  1525. *************************************************************************/
  1526. int GetDayOfWeekName( int iDayOfWeek, WCHAR *pwszDayOfWeek, int cSize, bool fAbbrev )
  1527. {
  1528. LCTYPE lctype;
  1529. switch ( iDayOfWeek )
  1530. {
  1531. case 0:
  1532. // Sunday is day 7
  1533. lctype = fAbbrev ? LOCALE_SABBREVDAYNAME7 : LOCALE_SDAYNAME7;
  1534. break;
  1535. case 1:
  1536. lctype = fAbbrev ? LOCALE_SABBREVDAYNAME1 : LOCALE_SDAYNAME1;
  1537. break;
  1538. case 2:
  1539. lctype = fAbbrev ? LOCALE_SABBREVDAYNAME2 : LOCALE_SDAYNAME2;
  1540. break;
  1541. case 3:
  1542. lctype = fAbbrev ? LOCALE_SABBREVDAYNAME3 : LOCALE_SDAYNAME3;
  1543. break;
  1544. case 4:
  1545. lctype = fAbbrev ? LOCALE_SABBREVDAYNAME4 : LOCALE_SDAYNAME4;
  1546. break;
  1547. case 5:
  1548. lctype = fAbbrev ? LOCALE_SABBREVDAYNAME5 : LOCALE_SDAYNAME5;
  1549. break;
  1550. case 6:
  1551. lctype = fAbbrev ? LOCALE_SABBREVDAYNAME6 : LOCALE_SDAYNAME6;
  1552. break;
  1553. default:
  1554. return 0;
  1555. }
  1556. TCHAR *pszDayOfWeek = new TCHAR[ cSize ];
  1557. if ( !pszDayOfWeek )
  1558. {
  1559. return 0;
  1560. }
  1561. int iRet = ::GetLocaleInfo( LOCALE_USER_DEFAULT, lctype, pszDayOfWeek, cSize );
  1562. USES_CONVERSION;
  1563. wcscpy( pwszDayOfWeek, T2W(pszDayOfWeek) );
  1564. iRet = wcslen(pwszDayOfWeek);
  1565. delete[] pszDayOfWeek;
  1566. return iRet;
  1567. } /* GetMonthName */
  1568. /***********************************************************************
  1569. * FormatDate *
  1570. *------------*
  1571. * Description:
  1572. * Uses the format string to format a SYSTEMTIME date.
  1573. * We are using this instead of GetDateFormat() since
  1574. * we also want to format bogus dates and dates with
  1575. * years like 1492 that are not accepted by GetDateFormat()
  1576. * Return:
  1577. * Number of characters written to pszDate (including
  1578. * null terminating character), 0 if failed
  1579. *************************************************************************/
  1580. int FormatDate( const SYSTEMTIME &stDate,
  1581. TCHAR *pszFormat,
  1582. WCHAR *pwszDate,
  1583. int cSize,
  1584. const WCHAR *pwszEmperor)
  1585. {
  1586. if ( !pszFormat || !pwszDate )
  1587. {
  1588. SPDBG_ASSERT( FALSE );
  1589. return 0;
  1590. }
  1591. WCHAR * const pwszDateStart = pwszDate;
  1592. // Convert the format string to unicode
  1593. WCHAR pwszFormat[ MAX_PATH ];
  1594. USES_CONVERSION;
  1595. wcscpy( pwszFormat, T2W(pszFormat) );
  1596. WCHAR *pwc = pwszFormat;
  1597. //Modify the format string to drop the fileds we don't have (Year, gg)
  1598. while ( *pwc )
  1599. {
  1600. switch( *pwc )
  1601. {
  1602. case L'y':
  1603. if (!stDate.wYear)
  1604. {
  1605. do
  1606. {
  1607. *pwc++ = L'\'';
  1608. } while ( *pwc && (L'M' != *pwc) && (L'd' != *pwc));
  1609. }
  1610. else
  1611. {
  1612. pwc++;
  1613. }
  1614. break;
  1615. case L'g':
  1616. *pwc++ = L'\'';
  1617. break;
  1618. default:
  1619. pwc ++;
  1620. break;
  1621. }
  1622. }
  1623. pwc = pwszFormat;
  1624. // output the Emperor's name if there is one
  1625. if (pwszEmperor)
  1626. {
  1627. wcscpy(pwszDate,pwszEmperor);
  1628. pwszDate += wcslen(pwszEmperor);
  1629. }
  1630. // Copy the format string to the date string character by
  1631. // character, replacing the string like "dddd" as appropriate
  1632. while ( *pwc )
  1633. {
  1634. switch( *pwc )
  1635. {
  1636. case L'\'':
  1637. pwc++; // Don't need '
  1638. break;
  1639. case L'd':
  1640. {
  1641. // Count the number of d's
  1642. int cNumDs = 0;
  1643. int iRet;
  1644. do
  1645. {
  1646. pwc++;
  1647. cNumDs++;
  1648. } while ( L'd' == *pwc );
  1649. switch ( cNumDs )
  1650. {
  1651. case 1:
  1652. // Day with no leading zeroes
  1653. swprintf( pwszDate, L"%d", stDate.wDay );
  1654. iRet = wcslen( pwszDate );
  1655. break;
  1656. case 2:
  1657. // Day with one fixed width of 2
  1658. swprintf( pwszDate, L"%02d", stDate.wDay );
  1659. iRet = wcslen( pwszDate );
  1660. break;
  1661. case 3:
  1662. // Abbreviated day of week
  1663. iRet = GetDayOfWeekName( stDate.wDayOfWeek, pwszDate, cSize, true );
  1664. break;
  1665. default: // More than 4? Treat it as 4
  1666. // Day of week
  1667. iRet = GetDayOfWeekName( stDate.wDayOfWeek, pwszDate, cSize, false );
  1668. break;
  1669. }
  1670. if ( iRet <= 0 )
  1671. {
  1672. return 0;
  1673. }
  1674. else
  1675. {
  1676. pwszDate += iRet;
  1677. }
  1678. break;
  1679. }
  1680. case L'M':
  1681. {
  1682. // Count the number of M's
  1683. int cNumMs = 0;
  1684. int iRet;
  1685. do
  1686. {
  1687. pwc++;
  1688. cNumMs++;
  1689. } while ( L'M' == *pwc );
  1690. switch ( cNumMs )
  1691. {
  1692. case 1:
  1693. // Day with no leading zeroes
  1694. swprintf( pwszDate, L"%d", stDate.wMonth );
  1695. iRet = wcslen( pwszDate );
  1696. break;
  1697. case 2:
  1698. // Day with one fixed width of 2
  1699. swprintf( pwszDate, L"%02d", stDate.wMonth );
  1700. iRet = wcslen( pwszDate );
  1701. break;
  1702. case 3:
  1703. // Abbreviated month name
  1704. iRet = GetMonthName( stDate.wMonth, pwszDate, cSize, true );
  1705. break;
  1706. default: // More than 4? Treat it as 4
  1707. // Month
  1708. iRet = GetMonthName( stDate.wMonth, pwszDate, cSize, false );
  1709. break;
  1710. }
  1711. if ( iRet < 0 )
  1712. {
  1713. return 0;
  1714. }
  1715. else
  1716. {
  1717. pwszDate += iRet;
  1718. }
  1719. break;
  1720. }
  1721. case L'y':
  1722. {
  1723. // Count the number of y's
  1724. int cNumYs = 0;
  1725. do
  1726. {
  1727. pwc++;
  1728. cNumYs++;
  1729. } while ( L'y' == *pwc );
  1730. switch ( cNumYs )
  1731. {
  1732. case 1:
  1733. // Last two digits of year, width of 2
  1734. if (stDate.wYear % 100 > 9)
  1735. {
  1736. swprintf( pwszDate, L"%02d", stDate.wYear % 100 );
  1737. pwszDate += 2;
  1738. }
  1739. else
  1740. {
  1741. swprintf( pwszDate, L"%01d", stDate.wYear % 100 );
  1742. pwszDate += 1;
  1743. }
  1744. break;
  1745. case 2:
  1746. // Last two digits of year, width of 2
  1747. {
  1748. swprintf( pwszDate, L"%02d", stDate.wYear % 100 );
  1749. pwszDate += 2;
  1750. }
  1751. break;
  1752. default:
  1753. // All four digits of year, width of 4
  1754. // Last two digits of year, width of 2
  1755. swprintf( pwszDate, L"%04d", stDate.wYear % 10000 );
  1756. pwszDate += 4;
  1757. break;
  1758. }
  1759. break;
  1760. }
  1761. case L'g':
  1762. {
  1763. // NB: GetCalendarInfo is supported on Win98 or Win2K, but not on NT4
  1764. /*
  1765. if ( L'g' == *(pwc + 1) )
  1766. {
  1767. // Get the era string
  1768. TCHAR pszCalEra[ MAX_LOCALE_DATA ];
  1769. if ( 0 == GetCalendarInfo( LOCALE_USER_DEFAULT,
  1770. CAL_GREGORIAN, CAL_SERASTRING, pszCalEra, MAX_LOCALE_DATA ) )
  1771. {
  1772. return 0;
  1773. }
  1774. USES_CONVERSION;
  1775. wcscpy( pwszDate, T2W(pszCalEra) );
  1776. pwc += 2;
  1777. }
  1778. else
  1779. {
  1780. // It's just a 'g'
  1781. *pwszDate++ = *pwc++;
  1782. }
  1783. */
  1784. *pwszDate++ = *pwc++;
  1785. break;
  1786. }
  1787. default:
  1788. *pwszDate++ = *pwc++;
  1789. }
  1790. }
  1791. *pwszDate++ = 0;
  1792. return (pwszDate - pwszDateStart);
  1793. } /* FormatDate */