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.

2779 lines
88 KiB

  1. // TestITN.cpp : Implementation of CTestITN
  2. #include "stdafx.h"
  3. #include <winnls.h>
  4. #include "Itngram.h"
  5. #include "TestITN.h"
  6. #include "sphelper.h"
  7. #include "spddkhlp.h"
  8. #include "test.h"
  9. #define MILLION ((LONGLONG) 1000000)
  10. #define BILLION ((LONGLONG) 1000000000)
  11. #define MILLION_STR (L"million")
  12. #define BILLION_STR (L"billion")
  13. #define DAYOFWEEK_STR_ABBR (L"ddd")
  14. #define DAYOFWEEK_STR (L"dddd")
  15. #define NUM_US_STATES 57
  16. #define NUM_CAN_PROVINCES 10
  17. BOOL CALLBACK EnumCalendarInfoProc( LPTSTR lpCalendarInfoString )
  18. {
  19. return FALSE;
  20. }
  21. /////////////////////////////////////////////////////////////////////////////
  22. // CTestITN
  23. /****************************************************************************
  24. * CTestITN::InitGrammar *
  25. *-----------------------*
  26. * Description:
  27. * Initialize a grammar that is loaded from an object (DLL).
  28. * - pszGrammarName name of the grammar to be loaded (in case this
  29. * object supports multiple grammars)
  30. * - pvGrammarData pointer to the serialized binary grammar
  31. * Returns:
  32. * S_OK
  33. * failure codes that are implementation specific
  34. ********************************************************************* RAL ***/
  35. STDMETHODIMP CTestITN::InitGrammar(const WCHAR * pszGrammarName, const void ** pvGrammarData)
  36. {
  37. HRESULT hr = S_OK;
  38. HRSRC hResInfo = ::FindResource(_Module.GetModuleInstance(), _T("TEST"), _T("ITNGRAMMAR"));
  39. if (hResInfo)
  40. {
  41. HGLOBAL hData = ::LoadResource(_Module.GetModuleInstance(), hResInfo);
  42. if (hData)
  43. {
  44. *pvGrammarData = ::LockResource(hData);
  45. if (*pvGrammarData == NULL)
  46. {
  47. hr = HRESULT_FROM_WIN32(::GetLastError());
  48. }
  49. }
  50. else
  51. {
  52. hr = HRESULT_FROM_WIN32(::GetLastError());
  53. }
  54. }
  55. else
  56. {
  57. hr = HRESULT_FROM_WIN32(::GetLastError());
  58. }
  59. return hr;
  60. }
  61. /****************************************************************************
  62. * CTestITN::Interpret *
  63. *---------------------*
  64. * Description:
  65. * Given a phrase structure of the sub-tree spanned by this rule, given by
  66. * ulFristElement and ulCountOfElements and rule info (in pPhrase), examine
  67. * and generate new properties/text replacements which are set in the outer
  68. * phrase using pSite.
  69. * Returns:
  70. * S_OK
  71. * S_FALSE -- nothing was added/changed
  72. ********************************************************************* RAL ***/
  73. STDMETHODIMP CTestITN::Interpret(ISpPhraseBuilder * pPhrase,
  74. const ULONG ulFirstElement,
  75. const ULONG ulCountOfElements,
  76. ISpCFGInterpreterSite * pSite)
  77. {
  78. HRESULT hr = S_OK;
  79. ULONG ulRuleId = 0;
  80. CSpPhrasePtr cpPhrase;
  81. hr = pPhrase->GetPhrase(&cpPhrase);
  82. m_pSite = pSite;
  83. // Get the minimum and maximum positions
  84. ULONG ulMinPos;
  85. ULONG ulMaxPos;
  86. //Just use ulFirstElement & ulCountOfElements
  87. //GetMinAndMaxPos( cpPhrase->pProperties, &ulMinPos, &ulMaxPos );
  88. ulMinPos = ulFirstElement;
  89. ulMaxPos = ulMinPos + ulCountOfElements;
  90. // Unless otherwise specified, this is the display attributes
  91. BYTE bDisplayAttribs = SPAF_ONE_TRAILING_SPACE;
  92. if (SUCCEEDED(hr))
  93. {
  94. hr = E_FAIL;
  95. WCHAR pszValueBuff[ MAX_PATH ]; // No ITN result should be longer than this
  96. DOUBLE dblValue; // All ITN results will have a 64-bit value
  97. pszValueBuff[0] = 0;
  98. switch (cpPhrase->Rule.ulId)
  99. {
  100. case GRID_INTEGER_STANDALONE: // Fired as a toplevel rule
  101. hr = InterpretNumber( cpPhrase->pProperties, true,
  102. &dblValue, pszValueBuff, MAX_PATH, true );
  103. if (SUCCEEDED( hr )
  104. && ( dblValue >= 0 )
  105. && ( dblValue <= 20 )
  106. && ( GRID_DIGIT_NUMBER != cpPhrase->pProperties->ulId )
  107. && ( GRID_INTEGER_MILLBILL != cpPhrase->pProperties->ulId ))
  108. {
  109. // Throw this one out because numbers like "three"
  110. // shouldn't be ITNed by themselves
  111. hr = E_FAIL;
  112. }
  113. break;
  114. case GRID_INTEGER: case GRID_INTEGER_NONNEG:
  115. case GRID_INTEGER_99: case GRID_INTEGER_999:
  116. case GRID_ORDINAL:
  117. case GRID_MINSEC: case GRID_CLOCK_MINSEC: // Number
  118. hr = InterpretNumber( cpPhrase->pProperties, true,
  119. &dblValue, pszValueBuff, MAX_PATH );
  120. break;
  121. // Number "spelled out" digit by digit
  122. case GRID_DIGIT_NUMBER: case GRID_YEAR: case GRID_CENTS:
  123. hr = InterpretDigitNumber( cpPhrase->pProperties,
  124. &dblValue, pszValueBuff, MAX_PATH );
  125. break;
  126. case GRID_FP_NUMBER: case GRID_FP_NUMBER_NONNEG:
  127. hr = InterpretFPNumber( cpPhrase->pProperties,
  128. &dblValue, pszValueBuff, MAX_PATH );
  129. break;
  130. case GRID_INTEGER_MILLBILL:
  131. hr = InterpretMillBill( cpPhrase->pProperties,
  132. &dblValue, pszValueBuff, MAX_PATH );
  133. break;
  134. case GRID_DENOMINATOR: case GRID_DENOMINATOR_SINGULAR:
  135. hr = InterpretNumber( cpPhrase->pProperties, true,
  136. &dblValue, pszValueBuff, MAX_PATH );
  137. break;
  138. case GRID_FRACTION:
  139. hr = InterpretFraction( cpPhrase->pProperties,
  140. &dblValue, pszValueBuff, MAX_PATH );
  141. break;
  142. case GRID_DATE:
  143. hr = InterpretDate( cpPhrase->pProperties,
  144. &dblValue, pszValueBuff, MAX_PATH );
  145. break;
  146. case GRID_TIME:
  147. hr = InterpretTime( cpPhrase->pProperties,
  148. &dblValue, pszValueBuff, MAX_PATH );
  149. break;
  150. case GRID_STATEZIP:
  151. hr = InterpretStateZip( cpPhrase->pProperties,
  152. pszValueBuff, MAX_PATH, &bDisplayAttribs );
  153. break;
  154. case GRID_ZIPCODE: case GRID_ZIP_PLUS_FOUR:
  155. hr = (MakeDigitString( cpPhrase->pProperties,
  156. pszValueBuff, MAX_PATH ) > 0) ? S_OK : E_FAIL;
  157. break;
  158. case GRID_CAN_ZIPCODE:
  159. hr = InterpretCanadaZip( cpPhrase->pProperties,
  160. pszValueBuff, MAX_PATH );
  161. break;
  162. case GRID_PHONE_NUMBER:
  163. hr = InterpretPhoneNumber( cpPhrase->pProperties,
  164. pszValueBuff, MAX_PATH );
  165. break;
  166. case GRID_DEGREES:
  167. hr = InterpretDegrees( cpPhrase->pProperties,
  168. &dblValue, pszValueBuff, MAX_PATH );
  169. break;
  170. case GRID_MEASUREMENT:
  171. hr = InterpretMeasurement( cpPhrase->pProperties,
  172. &dblValue, pszValueBuff, MAX_PATH );
  173. break;
  174. case GRID_CURRENCY: // Currency
  175. hr = InterpretCurrency( cpPhrase->pProperties,
  176. &dblValue, pszValueBuff, MAX_PATH );
  177. break;
  178. default:
  179. _ASSERT(FALSE);
  180. break;
  181. }
  182. if ( SUCCEEDED( hr ) )
  183. {
  184. hr = AddPropertyAndReplacement( pszValueBuff, dblValue,
  185. ulMinPos, ulMaxPos, ulMinPos, ulMaxPos - ulMinPos,
  186. bDisplayAttribs );
  187. return hr;
  188. }
  189. }
  190. // Nothing was ITNed
  191. return S_FALSE;
  192. }
  193. /***********************************************************************
  194. * CTestITN::InterpretNumber *
  195. *---------------------------*
  196. * Description:
  197. * Interprets a number in the range -999,999,999,999 to
  198. * +999,999,999,999 and sends the properties and
  199. * replacements to the CFGInterpreterSite as appropriate.
  200. * The property will be added and the pszValue will be a string
  201. * with the correct display number.
  202. * If fCardinal is set, makes the display a cardinal number;
  203. * otherwise makes it an ordinal number.
  204. * The number will be formatted only if it was a properly-formed
  205. * number (not given digit by digit).
  206. * Result:
  207. *************************************************************************/
  208. HRESULT CTestITN::InterpretNumber(const SPPHRASEPROPERTY *pProperties,
  209. const bool fCardinal,
  210. DOUBLE *pdblVal,
  211. WCHAR *pszVal,
  212. UINT cSize,
  213. const bool fFinalDisplayFmt)
  214. {
  215. if ( !pdblVal || !pszVal )
  216. {
  217. return E_POINTER;
  218. }
  219. *pszVal = 0;
  220. LONGLONG llValue = 0;
  221. int iPositive = 1;
  222. const SPPHRASEPROPERTY *pFirstProp = pProperties;
  223. // Handle negatives
  224. if ( NEGATIVE == pFirstProp->ulId )
  225. {
  226. // There's no such thing as a negative ordinal
  227. SPDBG_ASSERT( fCardinal );
  228. // There had better be more stuff following
  229. SPDBG_ASSERT( pFirstProp->pNextSibling );
  230. iPositive = -1;
  231. pFirstProp = pFirstProp->pNextSibling;
  232. }
  233. // Handle the "2.6 million" case, in which case the number
  234. // has already been formatted
  235. if ( GRID_INTEGER_MILLBILL == pFirstProp->ulId )
  236. {
  237. if ( cSize < (wcslen( pFirstProp->pszValue ) + 1) )
  238. {
  239. return E_INVALIDARG;
  240. }
  241. *pdblVal = pFirstProp->vValue.dblVal * iPositive;
  242. if ( iPositive < 0 )
  243. {
  244. wcscpy( pszVal, m_pwszNeg );
  245. }
  246. wcscat( pszVal, pFirstProp->pszValue );
  247. return S_OK;
  248. }
  249. // Handle the digit-by-digit case
  250. if ( GRID_DIGIT_NUMBER == pFirstProp->ulId )
  251. {
  252. // There had better be nothing following this
  253. SPDBG_ASSERT( !pFirstProp->pNextSibling );
  254. SPDBG_ASSERT( VT_R8 == pFirstProp->vValue.vt );
  255. SPDBG_ASSERT( pFirstProp->pszValue );
  256. DOUBLE dblVal = pFirstProp->vValue.dblVal;
  257. *pdblVal = dblVal * iPositive;
  258. // Just get the string and make it negative if necessary
  259. if ( cSize < wcslen( pFirstProp->pszValue ) )
  260. {
  261. return E_INVALIDARG;
  262. }
  263. wcscpy( pszVal, pFirstProp->pszValue );
  264. if ( iPositive < 0 )
  265. {
  266. MakeNumberNegative( pszVal );
  267. }
  268. return S_OK;
  269. }
  270. for (const SPPHRASEPROPERTY * pProp = pFirstProp; pProp; pProp ? pProp = pProp->pNextSibling : NULL)
  271. {
  272. switch(pProp->ulId)
  273. {
  274. case ONES:
  275. {
  276. SPDBG_ASSERT(pProp->pFirstChild);
  277. llValue += ComputeNum999( pProp->pFirstChild );
  278. }
  279. break;
  280. case THOUSANDS:
  281. {
  282. llValue += ComputeNum999( pProp->pFirstChild ) * 1000;
  283. }
  284. break;
  285. case MILLIONS:
  286. {
  287. SPDBG_ASSERT(pProp->pFirstChild);
  288. llValue += ComputeNum999( pProp->pFirstChild ) * (LONGLONG) 1e6;
  289. }
  290. break;
  291. case BILLIONS:
  292. {
  293. SPDBG_ASSERT(pProp->pFirstChild);
  294. llValue += ComputeNum999( pProp->pFirstChild ) * (LONGLONG) 1e9;
  295. }
  296. break;
  297. case HUNDREDS:
  298. {
  299. SPDBG_ASSERT( pProp->pFirstChild );
  300. llValue += ComputeNum999( pProp->pFirstChild ) * 100;
  301. }
  302. break;
  303. case TENS:
  304. default:
  305. SPDBG_ASSERT(false);
  306. }
  307. }
  308. llValue *= iPositive;
  309. *pdblVal = (DOUBLE) llValue;
  310. DWORD dwDisplayFlags = DF_WHOLENUMBER
  311. | (fCardinal ? 0 : DF_ORDINAL)
  312. | (fFinalDisplayFmt ? DF_MILLIONBILLION : 0 );
  313. return MakeDisplayNumber( *pdblVal, dwDisplayFlags, 0, 0, pszVal, cSize );
  314. } /* CTestITN::InterpretNumber */
  315. /***********************************************************************
  316. * CTestITN::InterpretDigitNumber *
  317. *--------------------------------*
  318. * Description:
  319. * Interprets an integer in (-INF, +INF) that has been spelled
  320. * out digit by digit.
  321. * Also handles years.
  322. * Result:
  323. *************************************************************************/
  324. HRESULT CTestITN::InterpretDigitNumber( const SPPHRASEPROPERTY *pProperties,
  325. DOUBLE *pdblVal,
  326. WCHAR *pszVal,
  327. UINT cSize)
  328. {
  329. if ( !pdblVal || !pszVal )
  330. {
  331. return E_POINTER;
  332. }
  333. BOOL fPositive = TRUE;
  334. ULONG ulLength = 0;
  335. *pszVal = 0;
  336. WCHAR *pwc = pszVal;
  337. UINT cLen = 0;
  338. for (const SPPHRASEPROPERTY * pProp = pProperties;
  339. pProp && (cLen < cSize);
  340. pProp ? pProp = pProp->pNextSibling : NULL)
  341. {
  342. switch(pProp->ulId)
  343. {
  344. /*
  345. case NEGATIVE:
  346. {
  347. SPDBG_ASSERT( pProp == pProperties );
  348. fPositive = FALSE;
  349. *pwc++ = L'-';
  350. cLen++;
  351. break;
  352. }
  353. */
  354. case DIGIT:
  355. {
  356. *pwc++ = pProp->pszValue[0];
  357. cLen++;
  358. break;
  359. }
  360. case TWODIGIT:
  361. {
  362. SPDBG_ASSERT( pProp->pFirstChild );
  363. ULONG ulTwoDigit = ComputeNum999( pProp->pFirstChild );
  364. SPDBG_ASSERT( ulTwoDigit < 100 );
  365. // Get each digit
  366. *pwc++ = L'0' + ((UINT) ulTwoDigit) / 10;
  367. *pwc++ = L'0' + ((UINT) ulTwoDigit) % 10;
  368. cLen += 2;
  369. break;
  370. }
  371. case ONEDIGIT:
  372. {
  373. SPDBG_ASSERT( pProp->pFirstChild);
  374. *pwc++ = pProp->pFirstChild->pszValue[0];
  375. cLen++;
  376. break;
  377. }
  378. case TWOTHOUSAND:
  379. {
  380. // Handles the "two thousand" in dates
  381. if ( pProp->pNextSibling )
  382. {
  383. if ( TWODIGIT == pProp->pNextSibling->ulId )
  384. {
  385. wcscpy( pwc, L"20" );
  386. pwc += 2;
  387. cLen += 2;
  388. }
  389. else
  390. {
  391. SPDBG_ASSERT( ONEDIGIT == pProp->pNextSibling->ulId );
  392. wcscpy( pwc, L"200" );
  393. pwc += 3;
  394. cLen += 3;
  395. }
  396. }
  397. else
  398. {
  399. wcscpy( pwc, L"2000" );
  400. pwc += 4;
  401. cLen += 4;
  402. }
  403. break;
  404. }
  405. case DATE_HUNDREDS:
  406. {
  407. SPDBG_ASSERT( pProp->pFirstChild );
  408. DOUBLE dblTwoDigit;
  409. HRESULT hr = InterpretDigitNumber( pProp->pFirstChild, &dblTwoDigit, pwc, cSize - cLen );
  410. if ( FAILED( hr ) )
  411. {
  412. return hr;
  413. }
  414. pwc += 2;
  415. *pwc++ = L'0';
  416. *pwc++ = L'0';
  417. cLen += 4;
  418. break;
  419. }
  420. default:
  421. SPDBG_ASSERT(false);
  422. }
  423. }
  424. *pwc = 0;
  425. if ( cLen <= MAX_SIG_FIGS )
  426. {
  427. *pdblVal = (DOUBLE) _wtoi64( pszVal );
  428. }
  429. else
  430. {
  431. // Just make sure it's not zero so denominators don't fail
  432. *pdblVal = 1;
  433. }
  434. return S_OK;
  435. } /* CTestITN::InterpretDigitNumber */
  436. /***********************************************************************
  437. * CTestITN::InterpretFPNumber *
  438. *-----------------------------*
  439. * Description:
  440. * Interprets a floating-point number of arbitrarily many
  441. * sig figs. (The value in *pdblVal will be truncated
  442. * as necessary to fit into a DOUBLE.)
  443. * The properties will look like an optional NEGATIVE property
  444. * followed by an optional ONES property,
  445. * which has already been appropriately ITNed,
  446. * followed by the stuff to the right of the decimal place
  447. ************************************************************************/
  448. HRESULT CTestITN::InterpretFPNumber( const SPPHRASEPROPERTY *pProperties,
  449. DOUBLE *pdblVal,
  450. WCHAR *pszVal,
  451. UINT cSize )
  452. {
  453. if ( !pdblVal || !pszVal )
  454. {
  455. return E_POINTER;
  456. }
  457. SPDBG_ASSERT( pProperties );
  458. *pdblVal = 0;
  459. *pszVal = 0;
  460. bool fNonNegative = true;
  461. const SPPHRASEPROPERTY *pPropertiesFpPart = NULL;
  462. const SPPHRASEPROPERTY *pPropertiesPointZero = NULL;
  463. const SPPHRASEPROPERTY *pPropertiesOnes = NULL;
  464. const SPPHRASEPROPERTY *pPropertiesZero = NULL;
  465. const SPPHRASEPROPERTY *pPropertiesNegative = NULL;
  466. const SPPHRASEPROPERTY *pPropertiesPtr;
  467. for(pPropertiesPtr=pProperties; pPropertiesPtr; pPropertiesPtr=pPropertiesPtr->pNextSibling)
  468. {
  469. if (POINT_ZERO == pPropertiesPtr->ulId )
  470. pPropertiesPointZero = pPropertiesPtr;
  471. else if ( FP_PART == pPropertiesPtr->ulId )
  472. pPropertiesFpPart = pPropertiesPtr;
  473. else if (ONES == pPropertiesPtr->ulId )
  474. pPropertiesOnes = pPropertiesPtr;
  475. else if (ZERO == pPropertiesPtr->ulId )
  476. pPropertiesZero = pPropertiesPtr;
  477. else if (NEGATIVE == pPropertiesPtr->ulId )
  478. pPropertiesNegative = pPropertiesPtr;
  479. }
  480. // Get current number formatting defaults
  481. HRESULT hr = GetNumberFormatDefaults();
  482. if ( FAILED( hr ) )
  483. {
  484. return hr;
  485. }
  486. // Look for negative sign
  487. if ( pPropertiesNegative )
  488. {
  489. fNonNegative = false;
  490. }
  491. // Look for optional ONES (optional because you can say
  492. // "point five"
  493. if ( pPropertiesOnes )
  494. {
  495. // This property has already been ITNed correctly,
  496. // so just copy in the text
  497. if ( (cSize - wcslen( pszVal )) < (wcslen( pPropertiesOnes->pszValue ) + 1) )
  498. {
  499. return E_INVALIDARG;
  500. }
  501. wcscpy( pszVal, pPropertiesOnes->pszValue );
  502. // Get the value
  503. *pdblVal = pPropertiesOnes->vValue.dblVal;
  504. }
  505. else if (pPropertiesZero || m_nmfmtDefault.LeadingZero )
  506. {
  507. // There should be a leading zero
  508. wcscpy( pszVal, L"0" );
  509. }
  510. SPDBG_ASSERT(pPropertiesFpPart || pPropertiesPointZero);
  511. // Put in a decimal separator
  512. if ( (cSize - wcslen( pszVal )) < (wcslen( m_nmfmtDefault.lpDecimalSep ) + 1) )
  513. {
  514. return E_INVALIDARG;
  515. }
  516. wcscat( pszVal, m_nmfmtDefault.lpDecimalSep );
  517. if ( pPropertiesFpPart )
  518. {
  519. // Deal with the FP part, which will also have been ITNed correctly
  520. if ( (cSize - wcslen( pszVal )) < (wcslen( pPropertiesFpPart->pszValue ) + 1) )
  521. {
  522. return E_INVALIDARG;
  523. }
  524. wcscat( pszVal, pPropertiesFpPart->pszValue );
  525. // Get the correct value
  526. DOUBLE dblFPPart = pPropertiesFpPart->vValue.dblVal;
  527. for ( UINT ui=0; ui < wcslen( pPropertiesFpPart->pszValue ); ui++ )
  528. {
  529. dblFPPart /= (DOUBLE) 10;
  530. }
  531. *pdblVal += dblFPPart;
  532. }
  533. else
  534. {
  535. // "point oh": The DOUBLE is already right, just add a "0"
  536. if ( (cSize - wcslen( pszVal )) < 2 )
  537. {
  538. return E_INVALIDARG;
  539. }
  540. wcscat( pszVal, L"0" );
  541. }
  542. // Handle the negative sign
  543. if ( !fNonNegative )
  544. {
  545. *pdblVal = -*pdblVal;
  546. if ( (cSize = wcslen( pszVal )) < 2 )
  547. {
  548. return E_INVALIDARG;
  549. }
  550. HRESULT hr = MakeNumberNegative( pszVal );
  551. if ( FAILED( hr ) )
  552. {
  553. return hr;
  554. }
  555. }
  556. return S_OK;
  557. } /* CTestITN::InterpretFPNumber */
  558. /***********************************************************************
  559. * CTestITN::InterpretMillBill *
  560. *-----------------------------*
  561. * Description:
  562. * Interprets a number that needs to be displayed with
  563. * the word "million" or "billion" in the display format.
  564. *************************************************************************/
  565. HRESULT CTestITN::InterpretMillBill( const SPPHRASEPROPERTY *pProperties,
  566. DOUBLE *pdblVal,
  567. WCHAR *pszVal,
  568. UINT cSize )
  569. {
  570. const SPPHRASEPROPERTY *pPropertiesPtr;
  571. if ( !pdblVal || !pszVal )
  572. {
  573. return E_POINTER;
  574. }
  575. SPDBG_ASSERT( pProperties );
  576. HRESULT hr = GetNumberFormatDefaults();
  577. if ( FAILED( hr ) )
  578. {
  579. return hr;
  580. }
  581. *pszVal = 0;
  582. // Handle optional negative sign
  583. bool fNonNegative = true;
  584. if ( NEGATIVE == pProperties->ulId )
  585. {
  586. // Always do '-', regardless of control panel option settings
  587. if ( cSize < 2 )
  588. {
  589. return E_INVALIDARG;
  590. }
  591. wcscpy( pszVal, m_pwszNeg );
  592. cSize -= 1;
  593. pProperties = pProperties->pNextSibling;
  594. }
  595. // Handle the number part
  596. SPDBG_ASSERT( pProperties );
  597. for( pPropertiesPtr = pProperties; pPropertiesPtr &&
  598. ( GRID_INTEGER_99 != pPropertiesPtr->ulId ) &&
  599. ( GRID_FP_NUMBER_NONNEG != pPropertiesPtr->ulId );
  600. pPropertiesPtr = pPropertiesPtr->pNextSibling);
  601. SPDBG_ASSERT(( GRID_INTEGER_99 == pPropertiesPtr->ulId ) ||
  602. ( GRID_FP_NUMBER_NONNEG == pPropertiesPtr->ulId ));
  603. *pdblVal = pPropertiesPtr->vValue.dblVal;
  604. if ( cSize < (wcslen( pPropertiesPtr->pszValue ) + 1) )
  605. {
  606. return E_INVALIDARG;
  607. }
  608. wcscat( pszVal, pPropertiesPtr->pszValue );
  609. cSize -= wcslen( pPropertiesPtr->pszValue );
  610. // Handle the "millions" part
  611. SPDBG_ASSERT( pProperties );
  612. for( pPropertiesPtr = pProperties; pPropertiesPtr &&
  613. ( MILLBILL != pPropertiesPtr->ulId );
  614. pPropertiesPtr = pPropertiesPtr->pNextSibling);
  615. SPDBG_ASSERT( MILLBILL == pPropertiesPtr->ulId );
  616. *pdblVal *= ( (MILLIONS == pPropertiesPtr->vValue.uiVal) ? MILLION : BILLION );
  617. if ( cSize < (wcslen( pPropertiesPtr->pszValue ) + 2) )
  618. {
  619. return E_INVALIDARG;
  620. }
  621. wcscat( pszVal, L" " );
  622. wcscat( pszVal, pPropertiesPtr->pszValue );
  623. if ( !fNonNegative )
  624. {
  625. *pdblVal = -*pdblVal;
  626. }
  627. return S_OK;
  628. } /* CTestITN::InterpretMillBill */
  629. /***********************************************************************
  630. * CTestITN::InterpretFraction *
  631. *-----------------------------*
  632. * Description:
  633. * Interprets a fraction.
  634. * The DENOMINATOR property should be present.
  635. * If the NUMERATOR property is absent, it is assumed to be 1.
  636. * Divides the NUMERATOR by the DENOMINATOR and sets the value
  637. * accordingly.
  638. *************************************************************************/
  639. HRESULT CTestITN::InterpretFraction( const SPPHRASEPROPERTY *pProperties,
  640. DOUBLE *pdblVal,
  641. WCHAR *pszVal,
  642. UINT cSize)
  643. {
  644. if ( !pdblVal || !pszVal )
  645. {
  646. return E_POINTER;
  647. }
  648. SPDBG_ASSERT( pProperties );
  649. DOUBLE dblWholeValue = 0;
  650. DOUBLE dblFracValue = 1;
  651. const SPPHRASEPROPERTY *pProp = pProperties;
  652. *pszVal = 0;
  653. // Space to store whatever characters follow the digits
  654. // in the numerator (like ")")
  655. WCHAR pszTemp[ 10 ]; // will never need this much
  656. pszTemp[0] = 0;
  657. // Whole part is optional, otherwise assumed to be 0
  658. if ( WHOLE == pProp->ulId )
  659. {
  660. SPDBG_ASSERT( VT_R8 == pProp->vValue.vt );
  661. dblWholeValue = pProp->vValue.dblVal;
  662. wcscpy( pszVal, pProp->pszValue );
  663. // Keep track of anything that follows the digits
  664. WCHAR *pwc;
  665. for ( pwc = pszVal + wcslen(pszVal) - 1; !iswdigit( *pwc ); pwc-- )
  666. ;
  667. wcscpy( pszTemp, pwc + 1 );
  668. *(pwc + 1) = 0;
  669. // Add a space between the whole part and the fractional part
  670. wcscat( pszVal, L" " );
  671. SPDBG_ASSERT( pProp->pNextSibling );
  672. pProp = pProp->pNextSibling;
  673. }
  674. // Numerator is optional, otherwise assumed to be 1
  675. if ( NUMERATOR == pProp->ulId )
  676. {
  677. SPDBG_ASSERT( VT_R8 == pProp->vValue.vt );
  678. dblFracValue = pProp->vValue.dblVal;
  679. wcscat( pszVal, pProp->pszValue );
  680. // Find the last digit and copy everything after it
  681. WCHAR *pwc;
  682. for ( pwc = pszVal + wcslen(pszVal) - 1; !iswdigit( *pwc ); pwc-- )
  683. ;
  684. wcscat( pszTemp, pwc + 1 );
  685. *(pwc + 1) = 0;
  686. SPDBG_ASSERT( pProp->pNextSibling );
  687. pProp = pProp->pNextSibling;
  688. }
  689. else if ( ZERO == pProp->ulId )
  690. {
  691. dblFracValue = 0;
  692. wcscat( pszVal, L"0" );
  693. SPDBG_ASSERT( pProp->pNextSibling );
  694. pProp = pProp->pNextSibling;
  695. }
  696. else
  697. {
  698. // No numerator, assume 1
  699. wcscat( pszVal, L"1" );
  700. }
  701. wcscat( pszVal, L"/" );
  702. SPDBG_ASSERT( DENOMINATOR == pProp->ulId );
  703. SPDBG_ASSERT( VT_R8 == pProp->vValue.vt );
  704. if ( 0 == pProp->vValue.dblVal )
  705. {
  706. // Will not ITN a fraction with a zero denominator
  707. return E_FAIL;
  708. }
  709. dblFracValue /= pProp->vValue.dblVal;
  710. wcscat( pszVal, pProp->pszValue );
  711. // In case the denominator was an ordinal, strip off the "th" at the end
  712. SPDBG_ASSERT( wcslen( pszVal ) );
  713. WCHAR *pwc = pszVal + wcslen( pszVal ) - 1;
  714. for ( ; (pwc >= pszVal) && !iswdigit( *pwc ); pwc-- )
  715. ;
  716. SPDBG_ASSERT( pwc > pszVal );
  717. *(pwc + 1) = 0;
  718. // Tack on the ")" or "-" from the end of the numerator
  719. wcscat( pszVal, pszTemp );
  720. *pdblVal = dblWholeValue + dblFracValue;
  721. return S_OK;
  722. } /* CTestITN::InterpretFraction */
  723. /***********************************************************************
  724. * CTestITN::InterpretDate *
  725. *-------------------------*
  726. * Description:
  727. * Interprets a date.
  728. * Converts the date into a VT_DATE format, even though it
  729. * gets stored as a VT_R8 (both are 64-bit quantities).
  730. * In case the date is not a valid date ("May fortieth nineteen
  731. * ninety nine") will add the properties for any numbers in there
  732. * and return S_FALSE.
  733. *************************************************************************/
  734. HRESULT CTestITN::InterpretDate( const SPPHRASEPROPERTY *pProperties,
  735. DOUBLE *pdblVal,
  736. WCHAR *pszVal,
  737. UINT cSize)
  738. {
  739. if ( !pdblVal || !pszVal )
  740. {
  741. return E_POINTER;
  742. }
  743. *pszVal = 0;
  744. // Get the date formatting string to be used right now
  745. WCHAR pwszLongDateFormat[ MAX_DATE_FORMAT ];
  746. if ( 0 == m_Unicode.GetLocaleInfo( LOCALE_USER_DEFAULT, LOCALE_SLONGDATE,
  747. pwszLongDateFormat, MAX_DATE_FORMAT ) )
  748. {
  749. return E_FAIL;
  750. }
  751. SYSTEMTIME stDate;
  752. memset( (void *) &stDate, 0, sizeof( stDate ) );
  753. // Arguments for FormatDate()
  754. WCHAR *pwszFormatArg = pwszLongDateFormat;
  755. WCHAR pwszFormat[ MAX_DATE_FORMAT ];
  756. *pwszFormat = 0;
  757. bool fYear = false; // Necessary since year can be 0
  758. bool fClearlyIntentionalYear = false; // "zero one"
  759. bool fMonthFirst = true; // August 2000 as opposed to 2000 August
  760. const SPPHRASEPROPERTY *pProp;
  761. if (( MONTHYEAR == pProperties->ulId )
  762. || ( YEARMONTH == pProperties->ulId ))
  763. {
  764. fMonthFirst = ( MONTHYEAR == pProperties->ulId );
  765. // Look for the year and month properties underneath this one
  766. pProperties = pProperties->pFirstChild;
  767. }
  768. for ( pProp = pProperties; pProp; pProp = pProp->pNextSibling )
  769. {
  770. switch ( pProp->ulId )
  771. {
  772. case DAY_OF_WEEK:
  773. {
  774. SPDBG_ASSERT( VT_UI4 == pProp->vValue.vt );
  775. SPDBG_ASSERT( (0 < pProp->vValue.ulVal) && (7 >= pProp->vValue.ulVal) );
  776. if ( (pProp->vValue.ulVal <= 0) || (pProp->vValue.ulVal > 7) )
  777. {
  778. return E_INVALIDARG;
  779. }
  780. stDate.wDayOfWeek = (WORD) pProp->vValue.ulVal;
  781. // Use the full long date format
  782. pwszFormatArg = pwszLongDateFormat;
  783. }
  784. break;
  785. case DAY_OF_MONTH:
  786. SPDBG_ASSERT( VT_UI4 == pProp->vValue.vt );
  787. SPDBG_ASSERT( (1 <= pProp->vValue.uiVal) && (31 >= pProp->vValue.uiVal) );
  788. stDate.wDay = pProp->vValue.uiVal;
  789. break;
  790. case MONTH:
  791. SPDBG_ASSERT( VT_UI4 == pProp->vValue.vt );
  792. SPDBG_ASSERT( (1 <= pProp->vValue.ulVal) && (12 >= pProp->vValue.ulVal) );
  793. if ( (pProp->vValue.ulVal < 1) || (pProp->vValue.ulVal > 12) )
  794. {
  795. return E_INVALIDARG;
  796. }
  797. stDate.wMonth = (WORD) pProp->vValue.ulVal;
  798. break;
  799. case YEAR:
  800. // Year will have been ITNed already
  801. SPDBG_ASSERT( VT_R8 == pProp->vValue.vt );
  802. stDate.wYear = (WORD) pProp->vValue.dblVal;
  803. fYear = true;
  804. if (( stDate.wYear < 10 ) && ( wcslen( pProp->pszValue ) >=2 ))
  805. {
  806. // Want to make sure "June zero one" does not
  807. // become June 1 below
  808. fClearlyIntentionalYear = true;
  809. }
  810. break;
  811. default:
  812. SPDBG_ASSERT( false );
  813. break;
  814. }
  815. }
  816. // Make sure that grammar gave us something valid
  817. SPDBG_ASSERT( stDate.wMonth &&
  818. ( stDate.wDayOfWeek ? stDate.wDay : 1 ) );
  819. // Ambiguity workaround: Want to make sure that June 28 is June 28 and not June, '28
  820. if ( fYear && !fClearlyIntentionalYear && stDate.wMonth && !stDate.wDay &&
  821. (stDate.wYear >= 1) && (stDate.wYear <= 31) )
  822. {
  823. fYear = false;
  824. stDate.wDay = stDate.wYear;
  825. stDate.wYear = 0;
  826. }
  827. // Deal with the possible types of input
  828. if ( stDate.wDay )
  829. {
  830. if ( fYear )
  831. {
  832. if ( !stDate.wDayOfWeek )
  833. {
  834. // Remove the day of week from the current date format string.
  835. // This format picture can be DAYOFWEEK_STR or DAYOFWEEK_STR_ABBR.
  836. // This is in a loop since a pathological string could have
  837. // more than one instance of the day of week...
  838. WCHAR *pwc = NULL;
  839. do
  840. {
  841. pwc = wcsstr( pwszLongDateFormat, DAYOFWEEK_STR );
  842. WCHAR pwszDayOfWeekStr[ MAX_LOCALE_DATA];
  843. if ( pwc )
  844. {
  845. wcscpy( pwszDayOfWeekStr, DAYOFWEEK_STR );
  846. }
  847. else if ( NULL != (pwc = wcsstr( pwszLongDateFormat, DAYOFWEEK_STR_ABBR )) )
  848. {
  849. wcscpy( pwszDayOfWeekStr, DAYOFWEEK_STR_ABBR );
  850. }
  851. if ( pwc )
  852. {
  853. // A day-of-week string was found
  854. // Copy over everything until this character info the format string
  855. wcsncpy( pwszFormat, pwszLongDateFormat, (pwc - pwszLongDateFormat) );
  856. pwszFormat[pwc - pwszLongDateFormat] = 0;
  857. // Skip over the day of week until the next symbol
  858. // (the way date format strings work, this is the first
  859. // alphabetical symbol
  860. pwc += wcslen( pwszDayOfWeekStr );
  861. while ( *pwc && (!iswalpha( *pwc ) || (L'd' == *pwc)) )
  862. {
  863. pwc++;
  864. }
  865. // Copy over everything from here on out
  866. wcscat( pwszFormat, pwc );
  867. pwszFormatArg = pwszFormat;
  868. // Copy over so that we can find the next day-of-week string
  869. wcscpy( pwszLongDateFormat, pwszFormat );
  870. }
  871. } while ( pwc );
  872. }
  873. else
  874. {
  875. // The user did say the day of week
  876. if ( !wcsstr( pwszLongDateFormat, DAYOFWEEK_STR_ABBR ) )
  877. {
  878. // The format string does not called for the day of week
  879. // being displayed anywhere
  880. // In this case our best bet is to write out the day of week at
  881. // the beginning of the output
  882. wcscpy( pwszFormat, L"dddd, " );
  883. wcscat( pwszFormat, pwszLongDateFormat );
  884. pwszFormatArg = pwszFormat;
  885. }
  886. }
  887. }
  888. else // fYear == 0
  889. {
  890. // Just a month and a day
  891. const SPPHRASEPROPERTY *pWhichComesFirst = pProperties;
  892. if ( stDate.wDayOfWeek )
  893. {
  894. wcscpy( pwszFormat, L"dddd, " );
  895. pWhichComesFirst = pWhichComesFirst->pNextSibling;
  896. }
  897. if ( MONTH == pWhichComesFirst->ulId )
  898. {
  899. wcscat( pwszFormat, L"MMMM d" );
  900. }
  901. else
  902. {
  903. wcscat( pwszFormat, L"d MMMM" );
  904. }
  905. pwszFormatArg = pwszFormat;
  906. }
  907. }
  908. else // stDate.wDay == 0
  909. {
  910. // Month, year format
  911. if ( fMonthFirst )
  912. {
  913. wcscpy( pwszFormat, L"MMMM, yyyy" );
  914. }
  915. else
  916. {
  917. wcscpy( pwszFormat, L"yyyy MMMM" );
  918. }
  919. pwszFormatArg = pwszFormat;
  920. }
  921. // Get the date in VariantTime form so we can set it as a semantic value
  922. int iRet = ::SystemTimeToVariantTime( &stDate, pdblVal );
  923. if ( 0 == iRet )
  924. {
  925. // Not serious, just the semantic value will be wrong
  926. *pdblVal = 0;
  927. }
  928. // Do the formatting
  929. iRet = FormatDate( stDate, pwszFormatArg, pszVal, cSize );
  930. if ( 0 == iRet )
  931. {
  932. return E_FAIL;
  933. }
  934. return S_OK;
  935. } /* CTestITN::InterpretDate */
  936. /***********************************************************************
  937. * CTestITN::InterpretTime *
  938. *--------------------------*
  939. * Description:
  940. * Interprets time, which can be of the following forms:
  941. * * Hour with qualifier ("half past three"), time marker optional
  942. * * Hour with minutes, time marker mandatory
  943. * * Military time "hundred hours"
  944. * * Hour with "o'clock", time marker optional
  945. * * Number of hours and number of minutes and optional number
  946. * of seconds
  947. * Return:
  948. * S_OK
  949. * E_POINTER if !pdblVal or !pszVal
  950. *************************************************************************/
  951. HRESULT CTestITN::InterpretTime( const SPPHRASEPROPERTY *pProperties,
  952. DOUBLE *pdblVal,
  953. WCHAR *pszVal,
  954. UINT cSize )
  955. {
  956. if ( !pdblVal || !pszVal )
  957. {
  958. return E_POINTER;
  959. }
  960. // Time marker and seconds should not be shown unless some
  961. // component of the time specifically requires it
  962. DWORD dwFlags = TIME_NOSECONDS | TIME_NOTIMEMARKER;
  963. SYSTEMTIME stTime;
  964. ::memset( (void *) &stTime, 0, sizeof( SYSTEMTIME ) );
  965. bool fQuarterTo = false;
  966. bool fClockTime = true;
  967. bool fAMPM = false;
  968. UINT uAMPM = AM;
  969. const SPPHRASEPROPERTY *pProp;
  970. for ( pProp = pProperties; pProp; pProp = pProp->pNextSibling )
  971. {
  972. switch ( pProp->ulId )
  973. {
  974. case HOUR_CLOCK:
  975. {
  976. UINT uiHour = pProp->vValue.uiVal;
  977. SPDBG_ASSERT(( uiHour > 0 ) && ( uiHour <= 12));
  978. if ( fQuarterTo )
  979. {
  980. // Push the hour back by one
  981. // (push back to 12 if it's one)
  982. uiHour = (1 == uiHour) ? 12 : (uiHour - 1);
  983. }
  984. if ( 12 == uiHour )
  985. {
  986. // The functions below are expecting "0" to indicate midnight
  987. uiHour = 0;
  988. }
  989. stTime.wHour = (WORD) uiHour;
  990. break;
  991. }
  992. case HOUR_COUNT:
  993. // Just take the hour for what it is
  994. stTime.wHour = (WORD) pProp->vValue.dblVal;
  995. // This is not a clock time
  996. fClockTime = false;
  997. break;
  998. case MINUTE:
  999. {
  1000. // Minutes are evaluted as numbers, so their values
  1001. // are stored as doubles
  1002. stTime.wMinute = (WORD) pProp->vValue.dblVal;
  1003. break;
  1004. }
  1005. case SECOND:
  1006. {
  1007. stTime.wSecond = (WORD) pProp->vValue.dblVal;
  1008. dwFlags &= ~TIME_NOSECONDS;
  1009. break;
  1010. }
  1011. case CLOCKTIME_QUALIFIER:
  1012. {
  1013. switch( pProp->vValue.uiVal )
  1014. {
  1015. case QUARTER_TO:
  1016. {
  1017. fQuarterTo = true;
  1018. stTime.wMinute = 45;
  1019. break;
  1020. }
  1021. case QUARTER_PAST:
  1022. {
  1023. stTime.wMinute = 15;
  1024. break;
  1025. }
  1026. case HALF_PAST:
  1027. {
  1028. stTime.wMinute = 30;
  1029. break;
  1030. }
  1031. default:
  1032. SPDBG_ASSERT( false );
  1033. }
  1034. break;
  1035. }
  1036. case AMPM:
  1037. {
  1038. // We don't know where it might arrive any more, so simple keep this information
  1039. fAMPM = true;
  1040. uAMPM = pProp->vValue.uiVal;
  1041. break;
  1042. }
  1043. default:
  1044. SPDBG_ASSERT( false );
  1045. }
  1046. }
  1047. if (fAMPM)
  1048. {
  1049. SPDBG_ASSERT(( stTime.wHour >= 0 ) && ( stTime.wHour <= 11 ));
  1050. if ( PM == uAMPM )
  1051. {
  1052. stTime.wHour += 12;
  1053. }
  1054. dwFlags &= ~TIME_NOTIMEMARKER;
  1055. }
  1056. HRESULT hr = S_OK;
  1057. if ( fClockTime )
  1058. {
  1059. // Get the time in VariantTime form so we can set it as a semantic value
  1060. if ( 0 == ::SystemTimeToVariantTime( &stTime, pdblVal ) )
  1061. {
  1062. // Not serious, just the semantic value will be wrong
  1063. *pdblVal = 0;
  1064. }
  1065. // Let the system format the time
  1066. int iRet = m_Unicode.GetTimeFormat( LOCALE_USER_DEFAULT, dwFlags, &stTime, NULL,
  1067. pszVal, cSize );
  1068. // NB: GetTimeFormat() will put an extra space at the end of the
  1069. // time if the default format has AM or PM but TIME_NOTIMEMARKER is
  1070. // set
  1071. if ( iRet && (TIME_NOTIMEMARKER & dwFlags) )
  1072. {
  1073. WCHAR *pwc = pszVal + wcslen( pszVal ) - 1;
  1074. while ( iswspace( *pwc ) )
  1075. {
  1076. *pwc-- = 0;
  1077. }
  1078. }
  1079. hr = iRet ? S_OK : E_FAIL;
  1080. }
  1081. else
  1082. {
  1083. // No need to go through the system's time formatter
  1084. if ( cSize < 10 ) // Space for xxx:xx:xx\0
  1085. {
  1086. return E_INVALIDARG;
  1087. }
  1088. if ( dwFlags & TIME_NOSECONDS )
  1089. {
  1090. swprintf( pszVal, L"%d:%02d", stTime.wHour, stTime.wMinute );
  1091. }
  1092. else
  1093. {
  1094. swprintf( pszVal, L"%d:%02d:%02d",
  1095. stTime.wHour, stTime.wMinute, stTime.wSecond );
  1096. }
  1097. }
  1098. return hr;
  1099. } /* CTestITN::InterpretTime */
  1100. /***********************************************************************
  1101. * CTestITN::InterpretStateZip *
  1102. *-----------------------------*
  1103. * Description:
  1104. * A StateZip must be a state name followed by a ZIP code.
  1105. * There is no reasonable semantic value to attach to this ITN.
  1106. * Return:
  1107. * S_OK
  1108. * E_POINTER if !pszVal
  1109. * E_INVALIDARG if cSize is too small
  1110. *************************************************************************/
  1111. HRESULT CTestITN::InterpretStateZip( const SPPHRASEPROPERTY *pProperties,
  1112. WCHAR *pszVal,
  1113. UINT cSize,
  1114. BYTE *pbAttribs )
  1115. {
  1116. if ( !pszVal || !pProperties || !pbAttribs )
  1117. {
  1118. return E_POINTER;
  1119. }
  1120. if ( cSize < MAX_STATEZIP )
  1121. {
  1122. return E_INVALIDARG;
  1123. }
  1124. const SPPHRASEPROPERTY *pPropertiesComma = NULL;
  1125. const SPPHRASEPROPERTY *pPropertiesState = NULL;
  1126. const SPPHRASEPROPERTY *pPropertiesZipCode = NULL;
  1127. const SPPHRASEPROPERTY *pPropertiesZipCodeExtra = NULL;
  1128. const SPPHRASEPROPERTY *pPropertiesPtr;
  1129. for(pPropertiesPtr=pProperties; pPropertiesPtr; pPropertiesPtr=pPropertiesPtr->pNextSibling)
  1130. {
  1131. if (COMMA == pPropertiesPtr->ulId )
  1132. pPropertiesComma = pPropertiesPtr;
  1133. else if ( (US_STATE == pPropertiesPtr->ulId ) || (CAN_PROVINCE == pPropertiesPtr->ulId ))
  1134. pPropertiesState = pPropertiesPtr;
  1135. else if (ZIPCODE == pPropertiesPtr->ulId )
  1136. pPropertiesZipCode = pPropertiesPtr;
  1137. else if (FOURDIGITS == pPropertiesPtr->ulId )
  1138. pPropertiesZipCodeExtra = pPropertiesPtr;
  1139. }
  1140. // Comma after the city name if a comma was spoken
  1141. if ( pPropertiesComma )
  1142. {
  1143. // Will want to consume leading spaces when this is displayed
  1144. *pbAttribs |= SPAF_CONSUME_LEADING_SPACES;
  1145. wcscpy( pszVal, L", " );
  1146. }
  1147. // Get the state name
  1148. SPDBG_ASSERT( pPropertiesState );
  1149. UINT uiState = pPropertiesState->vValue.uiVal;
  1150. if ( US_STATE == pPropertiesState->ulId )
  1151. {
  1152. SPDBG_ASSERT( uiState < NUM_US_STATES );
  1153. wcscat( pszVal, pPropertiesState->pszValue );
  1154. }
  1155. else if ( CAN_PROVINCE == pPropertiesState->ulId )
  1156. {
  1157. SPDBG_ASSERT( uiState < NUM_CAN_PROVINCES );
  1158. wcscat( pszVal, pPropertiesState->pszValue );
  1159. }
  1160. else
  1161. {
  1162. SPDBG_ASSERT( false );
  1163. }
  1164. wcscat( pszVal, L" " );
  1165. // Get the ZIP
  1166. SPDBG_ASSERT( pPropertiesZipCode );
  1167. wcscat( pszVal, pPropertiesZipCode->pszValue );
  1168. // Get the ZIP+4 if it's there
  1169. if ( pPropertiesZipCodeExtra )
  1170. {
  1171. wcscat( pszVal, L"-" );
  1172. wcscat( pszVal, pPropertiesZipCodeExtra->pszValue );
  1173. }
  1174. return S_OK;
  1175. } /* CTestITN::InterpretStateZip */
  1176. /***********************************************************************
  1177. * CTestITN::InterpretCanadaZip *
  1178. *------------------------------*
  1179. * Description:
  1180. * A CanadaZip must be Alpha/Num/Alpha Num/Alpha/Num
  1181. * There is no reasonable semantic value to attach to this ITN.
  1182. * Return:
  1183. * S_OK
  1184. * E_POINTER if !pszVal
  1185. * E_INVALIDARG if cSize is too small
  1186. *************************************************************************/
  1187. HRESULT CTestITN::InterpretCanadaZip( const SPPHRASEPROPERTY *pProperties,
  1188. WCHAR *pszVal,
  1189. UINT cSize )
  1190. {
  1191. if ( !pszVal )
  1192. {
  1193. return E_POINTER;
  1194. }
  1195. if ( cSize < CANADIAN_ZIPSIZE )
  1196. {
  1197. return E_INVALIDARG;
  1198. }
  1199. int i;
  1200. for ( i=0; i < 3; i++, pProperties = pProperties->pNextSibling )
  1201. {
  1202. SPDBG_ASSERT( pProperties );
  1203. wcscat( pszVal, pProperties->pszValue );
  1204. }
  1205. wcscat( pszVal, L" " );
  1206. for ( i=0; i < 3; i++, pProperties = pProperties->pNextSibling )
  1207. {
  1208. SPDBG_ASSERT( pProperties );
  1209. wcscat( pszVal, pProperties->pszValue );
  1210. }
  1211. return S_OK;
  1212. } /* CTestITN::InterpretStateZip */
  1213. /***********************************************************************
  1214. * CTestITN::InterpretPhoneNumber *
  1215. *--------------------------------*
  1216. * Description:
  1217. * Phone number
  1218. * Return:
  1219. * S_OK
  1220. * E_POINTER if !pszVal
  1221. * E_INVALIDARG if cSize is too small
  1222. *************************************************************************/
  1223. HRESULT CTestITN::InterpretPhoneNumber( const SPPHRASEPROPERTY *pProperties,
  1224. WCHAR *pszVal,
  1225. UINT cSize )
  1226. {
  1227. if ( !pProperties || !pszVal )
  1228. {
  1229. return E_POINTER;
  1230. }
  1231. if ( cSize < MAX_PHONE_NUMBER )
  1232. {
  1233. return E_INVALIDARG;
  1234. }
  1235. pszVal[0] = 0;
  1236. if ( ONE_PLUS == pProperties->ulId )
  1237. {
  1238. SPDBG_ASSERT( pProperties->pNextSibling &&
  1239. (AREA_CODE == pProperties->pNextSibling->ulId) );
  1240. wcscat( pszVal, L"1-" );
  1241. pProperties = pProperties->pNextSibling;
  1242. }
  1243. if ( AREA_CODE == pProperties->ulId )
  1244. {
  1245. SPDBG_ASSERT( pProperties->pNextSibling );
  1246. wcscat( pszVal, L"(" );
  1247. SPDBG_ASSERT( pProperties->pFirstChild );
  1248. if ( DIGIT == pProperties->pFirstChild->ulId )
  1249. {
  1250. // Area code spelled out digit by digit
  1251. if ( 4 != MakeDigitString(
  1252. pProperties->pFirstChild, pszVal + wcslen( pszVal ),
  1253. cSize - wcslen( pszVal ) ) )
  1254. {
  1255. return E_INVALIDARG;
  1256. }
  1257. }
  1258. else
  1259. {
  1260. // 800 or 900
  1261. SPDBG_ASSERT( AREA_CODE == pProperties->pFirstChild->ulId );
  1262. wcscat( pszVal, pProperties->pFirstChild->pszValue );
  1263. }
  1264. wcscat( pszVal, L")-" );
  1265. pProperties = pProperties->pNextSibling;
  1266. }
  1267. // Exchange
  1268. SPDBG_ASSERT( PHONENUM_EXCHANGE == pProperties->ulId );
  1269. SPDBG_ASSERT( pProperties->pFirstChild );
  1270. if ( 4 != MakeDigitString(
  1271. pProperties->pFirstChild, pszVal + wcslen( pszVal ),
  1272. cSize - wcslen( pszVal ) ) )
  1273. {
  1274. return E_INVALIDARG;
  1275. }
  1276. wcscat( pszVal, L"-");
  1277. SPDBG_ASSERT( pProperties->pNextSibling );
  1278. pProperties = pProperties->pNextSibling;
  1279. SPDBG_ASSERT( FOURDIGITS == pProperties->ulId );
  1280. SPDBG_ASSERT( pProperties->pFirstChild );
  1281. if ( 5 != MakeDigitString(
  1282. pProperties->pFirstChild, pszVal + wcslen( pszVal ),
  1283. cSize - wcslen( pszVal ) ) )
  1284. {
  1285. return E_INVALIDARG;
  1286. }
  1287. pProperties = pProperties->pNextSibling;
  1288. if ( pProperties )
  1289. {
  1290. // extension
  1291. SPDBG_ASSERT( EXTENSION == pProperties->ulId );
  1292. SPDBG_ASSERT( pProperties->pFirstChild );
  1293. wcscat( pszVal, L"x" );
  1294. if ( 0 == MakeDigitString(
  1295. pProperties->pFirstChild, pszVal + wcslen( pszVal ),
  1296. cSize - wcslen( pszVal ) ) )
  1297. {
  1298. return E_INVALIDARG;
  1299. }
  1300. pProperties = pProperties->pNextSibling;
  1301. }
  1302. // Make sure there's nothing else here!
  1303. SPDBG_ASSERT( !pProperties );
  1304. return S_OK;
  1305. } /* CTestITN::InterpretPhoneNumber */
  1306. /***********************************************************************
  1307. * CTestITN::InterpretDegrees *
  1308. *----------------------------*
  1309. * Description:
  1310. * Interprets degrees as a temperature, angle-measurement,
  1311. * or direction, as appropriate.
  1312. * Return:
  1313. * S_OK
  1314. * E_POINTER
  1315. * E_INVALIDARG
  1316. *************************************************************************/
  1317. HRESULT CTestITN::InterpretDegrees( const SPPHRASEPROPERTY *pProperties,
  1318. DOUBLE *pdblVal,
  1319. WCHAR *pszVal,
  1320. UINT cSize )
  1321. {
  1322. if ( !pProperties || !pdblVal || !pszVal )
  1323. {
  1324. return E_POINTER;
  1325. }
  1326. *pszVal = 0;
  1327. const SPPHRASEPROPERTY *pPropertiesDegree = NULL;
  1328. const SPPHRASEPROPERTY *pPropertiesMinute = NULL;
  1329. const SPPHRASEPROPERTY *pPropertiesSecond = NULL;
  1330. const SPPHRASEPROPERTY *pPropertiesDirection = NULL;
  1331. const SPPHRASEPROPERTY *pPropertiesUnit = NULL;
  1332. const SPPHRASEPROPERTY *pPropertiesPtr;
  1333. // Get the number
  1334. for(pPropertiesPtr=pProperties; pPropertiesPtr; pPropertiesPtr=pPropertiesPtr->pNextSibling)
  1335. {
  1336. if (TEMP_UNITS == pPropertiesPtr->ulId )
  1337. pPropertiesUnit = pPropertiesPtr;
  1338. else if ( (GRID_INTEGER_NONNEG == pPropertiesPtr->ulId ) || (NUMBER == pPropertiesPtr->ulId ))
  1339. pPropertiesDegree = pPropertiesPtr;
  1340. else if (MINUTE == pPropertiesPtr->ulId )
  1341. pPropertiesMinute = pPropertiesPtr;
  1342. else if (SECOND == pPropertiesPtr->ulId )
  1343. pPropertiesSecond = pPropertiesPtr;
  1344. else if (DIRECTION == pPropertiesPtr->ulId )
  1345. pPropertiesDirection = pPropertiesPtr;
  1346. }
  1347. SPDBG_ASSERT( pPropertiesDegree );
  1348. *pdblVal = pPropertiesDegree->vValue.dblVal;
  1349. wcscat( pszVal, pPropertiesDegree->pszValue );
  1350. wcscat( pszVal, L"" );
  1351. if ( pPropertiesUnit )
  1352. {
  1353. wcscat( pszVal, pPropertiesUnit->pszValue );
  1354. }
  1355. if ( pPropertiesMinute || pPropertiesSecond)
  1356. {
  1357. SPDBG_ASSERT( *pdblVal >= 0 );
  1358. if ( pPropertiesMinute )
  1359. {
  1360. DOUBLE dblMin = pPropertiesMinute->vValue.dblVal;
  1361. *pdblVal += dblMin / (DOUBLE) 60;
  1362. wcscat( pszVal, pPropertiesMinute->pszValue );
  1363. wcscat( pszVal, L"'" );
  1364. }
  1365. if ( pPropertiesSecond )
  1366. {
  1367. DOUBLE dblSec = pPropertiesSecond->vValue.dblVal;
  1368. *pdblVal += dblSec / (DOUBLE) 3600;
  1369. wcscat( pszVal, pPropertiesSecond->pszValue );
  1370. wcscat( pszVal, L"''" );
  1371. }
  1372. }
  1373. if ( pPropertiesDirection )
  1374. {
  1375. wcscat( pszVal, pPropertiesDirection->pszValue );
  1376. }
  1377. return S_OK;
  1378. } /* CTestITN::InterpretDegrees */
  1379. /***********************************************************************
  1380. * CTestITN::InterpretMeasurement *
  1381. *--------------------------------*
  1382. * Description:
  1383. * Interprets measurements, which is a number followed
  1384. * by a units name
  1385. * Return:
  1386. * S_OK
  1387. * E_POINTER
  1388. * E_INVALIDARG
  1389. *************************************************************************/
  1390. HRESULT CTestITN::InterpretMeasurement( const SPPHRASEPROPERTY *pProperties,
  1391. DOUBLE *pdblVal,
  1392. WCHAR *pszVal,
  1393. UINT cSize )
  1394. {
  1395. if ( !pProperties || !pdblVal || !pszVal )
  1396. {
  1397. return E_POINTER;
  1398. }
  1399. const SPPHRASEPROPERTY *pPropNumber = NULL;
  1400. const SPPHRASEPROPERTY *pPropUnits = NULL;
  1401. const SPPHRASEPROPERTY *pProp;
  1402. for(pProp= pProperties; pProp; pProp = pProp->pNextSibling)
  1403. {
  1404. if (NUMBER == pProp->ulId )
  1405. pPropNumber = pProp;
  1406. else if ( UNITS == pProp->ulId )
  1407. pPropUnits = pProp;
  1408. }
  1409. if (!pPropNumber || !pPropUnits)
  1410. {
  1411. SPDBG_ASSERT( FALSE );
  1412. return E_INVALIDARG;
  1413. }
  1414. if ( cSize < (wcslen(pPropNumber->pszValue) + wcslen(pPropUnits->pszValue) + 1) )
  1415. {
  1416. // Not enough space
  1417. return E_INVALIDARG;
  1418. }
  1419. wcscpy( pszVal, pPropNumber->pszValue );
  1420. wcscat( pszVal, pPropUnits->pszValue );
  1421. *pdblVal = pPropNumber->vValue.dblVal;
  1422. return S_OK;
  1423. } /* CTestITN::InterpretMeasurement */
  1424. /***********************************************************************
  1425. * CTestITN::InterpretCurrency *
  1426. *-----------------------------*
  1427. * Description:
  1428. * Interprets currency.
  1429. * Return:
  1430. * S_OK
  1431. * E_POINTER if !pdblVal or !pszVal
  1432. * E_INVALIDARG if the number of cents is not between 0 and 99
  1433. * inclusive
  1434. *************************************************************************/
  1435. HRESULT CTestITN::InterpretCurrency( const SPPHRASEPROPERTY *pProperties,
  1436. DOUBLE *pdblVal,
  1437. WCHAR *pszVal,
  1438. UINT cSize)
  1439. {
  1440. if ( !pdblVal || !pszVal || !pProperties )
  1441. {
  1442. return E_POINTER;
  1443. }
  1444. const SPPHRASEPROPERTY *pPropDollars = NULL;
  1445. const SPPHRASEPROPERTY *pPropCents = NULL;
  1446. const SPPHRASEPROPERTY *pPropType = NULL;
  1447. const SPPHRASEPROPERTY *pPropSmallType = NULL;
  1448. const SPPHRASEPROPERTY *pPropNegative = NULL;
  1449. const SPPHRASEPROPERTY *pProp;
  1450. for(pProp= pProperties; pProp; pProp = pProp->pNextSibling)
  1451. {
  1452. if (NEGATIVE == pProp->ulId )
  1453. pPropNegative = pProp;
  1454. else if ( DOLLARS == pProp->ulId )
  1455. pPropDollars = pProp;
  1456. else if ( CENTS == pProp->ulId )
  1457. pPropCents = pProp;
  1458. else if ( CURRENCY_TYPE == pProp->ulId )
  1459. pPropType = pProp;
  1460. else if ( CURRENCY_SMALL_TYPE == pProp->ulId )
  1461. pPropSmallType = pProp;
  1462. }
  1463. *pszVal = 0;
  1464. *pdblVal = 0;
  1465. bool fNonNegative = true;
  1466. if ( pPropNegative )
  1467. {
  1468. fNonNegative = false;
  1469. }
  1470. bool fUseDefaultCurrencySymbol = true;
  1471. if ( pPropDollars )
  1472. {
  1473. // If "dollars" was said, override the default currency symbol
  1474. if ( pPropType )
  1475. {
  1476. fUseDefaultCurrencySymbol = false;
  1477. }
  1478. }
  1479. if ( pPropDollars )
  1480. {
  1481. // Dollars and possibly cents will be here, so we want to
  1482. // use regional format
  1483. HRESULT hr = GetCurrencyFormatDefaults();
  1484. if ( FAILED( hr ) )
  1485. {
  1486. return hr;
  1487. }
  1488. *pdblVal = pPropDollars->vValue.dblVal;
  1489. // Handle the case of "$5 million". If this should happen, there
  1490. // will be some alphabetic string in the string value for the number,
  1491. // and there will be no cents
  1492. if ( !pPropCents )
  1493. {
  1494. WCHAR *pwc = wcsstr( pPropDollars->pszValue, MILLION_STR );
  1495. if ( !pwc )
  1496. {
  1497. pwc = wcsstr( pPropDollars->pszValue, BILLION_STR );
  1498. }
  1499. if ( pwc )
  1500. {
  1501. // Either "million" or "billion" was in there
  1502. // Just a dollar sign followed by the number string value
  1503. if ( !fNonNegative )
  1504. {
  1505. wcscpy( pszVal, m_pwszNeg );
  1506. *pdblVal = -*pdblVal;
  1507. }
  1508. wcscat( pszVal, pPropType->pszValue );
  1509. wcscat( pszVal, pPropDollars->pszValue );
  1510. return S_OK;
  1511. }
  1512. }
  1513. // Use the associated currency symbol
  1514. if ( !fUseDefaultCurrencySymbol )
  1515. {
  1516. wcscpy( m_pwszCurrencySym, pPropType->pszValue );
  1517. m_cyfmtDefault.lpCurrencySymbol = m_pwszCurrencySym;
  1518. }
  1519. // else... use the currency symbol obtained in GetCurrencyFormatDefaults()
  1520. if ( pPropCents )
  1521. {
  1522. SPDBG_ASSERT( (pPropCents->vValue.dblVal >= 0) &&
  1523. (pPropCents->vValue.dblVal < 100) );
  1524. DOUBLE dblCentsVal = pPropCents->vValue.dblVal / (DOUBLE) 100;
  1525. if ( *pdblVal >= 0 )
  1526. {
  1527. *pdblVal += dblCentsVal;
  1528. }
  1529. else
  1530. {
  1531. *pdblVal -= dblCentsVal;
  1532. }
  1533. }
  1534. else
  1535. {
  1536. // count up number of decimal places.
  1537. // Need to use the original formatted number
  1538. // in case someone explicitly gave some zeroes
  1539. // as significant digits
  1540. const WCHAR *pwszNum = pPropDollars->pszValue;
  1541. WCHAR pwszNumDecimalSep[ MAX_LOCALE_DATA ];
  1542. *pwszNumDecimalSep = 0;
  1543. int iRet = m_Unicode.GetLocaleInfo(
  1544. ::GetUserDefaultLCID(), LOCALE_SDECIMAL, pwszNumDecimalSep, MAX_LOCALE_DATA );
  1545. WCHAR *pwc = wcsstr( pwszNum, pwszNumDecimalSep );
  1546. UINT cDigits = 0;
  1547. if ( pwc && iRet )
  1548. {
  1549. for ( pwc = pwc + 1; *pwc && iswdigit( *pwc ); pwc++ )
  1550. {
  1551. cDigits++;
  1552. }
  1553. }
  1554. m_cyfmtDefault.NumDigits = __max( m_cyfmtDefault.NumDigits, cDigits );
  1555. }
  1556. // Handle the negative sign in the value
  1557. if ( !fNonNegative )
  1558. {
  1559. *pdblVal = -*pdblVal;
  1560. }
  1561. // Write the unformatted number to a string
  1562. WCHAR *pwszUnformatted = new WCHAR[ cSize ];
  1563. if ( !pwszUnformatted )
  1564. {
  1565. return E_OUTOFMEMORY;
  1566. }
  1567. swprintf( pwszUnformatted, L"%f", *pdblVal );
  1568. int iRet = m_Unicode.GetCurrencyFormat( LOCALE_USER_DEFAULT, 0, pwszUnformatted,
  1569. &m_cyfmtDefault, pszVal, cSize );
  1570. delete[] pwszUnformatted;
  1571. if ( !iRet )
  1572. {
  1573. return E_FAIL;
  1574. }
  1575. }
  1576. else
  1577. {
  1578. // Just cents: better have said "cents"
  1579. SPDBG_ASSERT( pPropSmallType );
  1580. *pdblVal = pPropCents->vValue.dblVal / (DOUBLE) 100;
  1581. // Cents are always displayed as 5c, regardless of locale settings.
  1582. // Copy over the formatted number
  1583. wcscpy( pszVal, pPropCents->pszValue );
  1584. // Add on the cents symbol
  1585. wcscat( pszVal, pPropSmallType->pszValue );
  1586. }
  1587. return S_OK;
  1588. } /* CTestITN::InterpretCurrency */
  1589. /***********************************************************************
  1590. * CTestITN::AddPropertyAndReplacement *
  1591. *-------------------------------------*
  1592. * Description:
  1593. * Takes all of the info that we want to pass into the
  1594. * engine site, forms the SPPHRASEPROPERTY and
  1595. * SPPHRASERREPLACEMENT, and adds them to the engine site
  1596. * Return:
  1597. * Return values of ISpCFGInterpreterSite::AddProperty()
  1598. * and ISpCFGInterpreterSite::AddTextReplacement()
  1599. *************************************************************************/
  1600. HRESULT CTestITN::AddPropertyAndReplacement( const WCHAR *szBuff,
  1601. const DOUBLE dblValue,
  1602. const ULONG ulMinPos,
  1603. const ULONG ulMaxPos,
  1604. const ULONG ulFirstElement,
  1605. const ULONG ulCountOfElements,
  1606. const BYTE bDisplayAttribs )
  1607. {
  1608. // Add the property
  1609. SPPHRASEPROPERTY prop;
  1610. memset(&prop,0,sizeof(prop));
  1611. prop.pszValue = szBuff;
  1612. prop.vValue.vt = VT_R8;
  1613. prop.vValue.dblVal = dblValue;
  1614. prop.ulFirstElement = ulMinPos;
  1615. prop.ulCountOfElements = ulMaxPos - ulMinPos;
  1616. HRESULT hr = m_pSite->AddProperty(&prop);
  1617. if (SUCCEEDED(hr))
  1618. {
  1619. SPPHRASEREPLACEMENT repl;
  1620. memset(&repl,0, sizeof(repl));
  1621. repl.bDisplayAttributes = bDisplayAttribs;
  1622. repl.pszReplacementText = szBuff;
  1623. repl.ulFirstElement = ulFirstElement;
  1624. repl.ulCountOfElements = ulCountOfElements;
  1625. hr = m_pSite->AddTextReplacement(&repl);
  1626. }
  1627. return hr;
  1628. } /* CTestITN::AddPropertyAndReplacement */
  1629. // Helper functions
  1630. /***********************************************************************
  1631. * CTestITN::MakeDisplayNumber *
  1632. *-----------------------------*
  1633. * Description:
  1634. * Converts a DOUBLE into a displayable
  1635. * number in the range -999,999,999,999 to +999,999,999,999.
  1636. * cSize is the number of chars for which pwszNum has space
  1637. * allocated.
  1638. * If DF_UNFORMATTED is set, all other flags are ignored,
  1639. * and the number is passed back as an optional negative
  1640. * followed by a string of digits
  1641. * If DF_ORDINAL is set in dwDisplayFlags, displays an
  1642. * ordinal number (i.e. tacks on "th" or the appropriate suffix.
  1643. * If DF_WHOLENUMBER is set, lops off the decimal separator
  1644. * and everything after it. If DF_WHOLENUMBER is not set,
  1645. * then uses the uiDecimalPlaces parameter to determine
  1646. * how many decimal places to display
  1647. * If DF_FIXEDWIDTH is set, will display at least uiFixedWidth
  1648. * digits; otherwise uiFixedWidth is ignored.
  1649. * If DF_NOTHOUSANDSGROUP is set, will not do thousands
  1650. * grouping (commas)
  1651. *************************************************************************/
  1652. HRESULT CTestITN::MakeDisplayNumber( DOUBLE dblNum,
  1653. DWORD dwDisplayFlags,
  1654. UINT uiFixedWidth,
  1655. UINT uiDecimalPlaces,
  1656. WCHAR *pwszNum,
  1657. UINT cSize )
  1658. {
  1659. SPDBG_ASSERT( pwszNum );
  1660. SPDBG_ASSERT( !SPIsBadWritePtr( pwszNum, cSize ) );
  1661. *pwszNum = 0;
  1662. // Get the default number formatting.
  1663. // Note that this gets called every time, since otherwise there
  1664. // is no way to pick up changes that the user has made since
  1665. // this process has started.
  1666. HRESULT hr = GetNumberFormatDefaults();
  1667. if ( FAILED( hr ) )
  1668. {
  1669. return hr;
  1670. }
  1671. // check for straight millions and straight billions
  1672. // NB: This is a workaround for the fact that we can't resolve the ambiguity
  1673. // and get "two million" to go through GRID_INTEGER_MILLBILL
  1674. if (( dwDisplayFlags & DF_WHOLENUMBER ) && ( dwDisplayFlags & DF_MILLIONBILLION ) && (dblNum > 0))
  1675. {
  1676. HRESULT hr;
  1677. if ( 0 == (( ((LONGLONG) dblNum) % BILLION )) )
  1678. {
  1679. // e.g. for "five billion" get the "5" and then
  1680. // tack on " billion"
  1681. hr = MakeDisplayNumber( ( dblNum / ((DOUBLE) BILLION) ),
  1682. dwDisplayFlags, uiFixedWidth, uiDecimalPlaces, pwszNum, cSize );
  1683. if ( SUCCEEDED( hr ) )
  1684. {
  1685. wcscat( pwszNum, L" " );
  1686. wcscat( pwszNum, BILLION_STR );
  1687. }
  1688. return hr;
  1689. }
  1690. else if (( ((LONGLONG) dblNum) < BILLION ) &&
  1691. ( 0 == (( ((LONGLONG) dblNum) % MILLION )) ))
  1692. {
  1693. hr = MakeDisplayNumber( ( dblNum / ((DOUBLE) MILLION) ),
  1694. dwDisplayFlags, uiFixedWidth, uiDecimalPlaces, pwszNum, cSize );
  1695. if ( SUCCEEDED( hr ) )
  1696. {
  1697. wcscat( pwszNum, L" " );
  1698. wcscat( pwszNum, MILLION_STR );
  1699. }
  1700. return hr;
  1701. }
  1702. }
  1703. // Put in the negative sign if necessary
  1704. if ( dblNum < 0 )
  1705. {
  1706. wcscat( pwszNum, L"-" );
  1707. // From now on we want to deal with the magnitude of the number
  1708. dblNum *= -1;
  1709. }
  1710. SPDBG_ASSERT( dblNum < 1e12 );
  1711. WCHAR *pwszTemp = new WCHAR[ cSize ];
  1712. if ( !pwszTemp )
  1713. {
  1714. return E_OUTOFMEMORY;
  1715. }
  1716. *pwszTemp = 0;
  1717. LONGLONG llIntPart = (LONGLONG) dblNum;
  1718. UINT64 uiDigitsLeftOfDecimal;
  1719. if ( dwDisplayFlags & DF_WHOLENUMBER )
  1720. {
  1721. swprintf( pwszTemp, L"%I64d", llIntPart );
  1722. uiDigitsLeftOfDecimal = wcslen( pwszTemp );
  1723. }
  1724. else
  1725. {
  1726. swprintf( pwszTemp, L"%.*f", uiDecimalPlaces, dblNum );
  1727. WCHAR *pwc = wcschr( pwszTemp, L'.' );
  1728. uiDigitsLeftOfDecimal = pwc - pwszTemp;
  1729. }
  1730. // The following handles the case where the user said something
  1731. // like "zero zero zero three" and wants to see "0,003"
  1732. BOOL fChangedFirstDigit = false;
  1733. const WCHAR wcFakeFirstDigit = L'1';
  1734. if ( !(dwDisplayFlags & DF_UNFORMATTED) &&
  1735. (dwDisplayFlags & DF_FIXEDWIDTH) && (uiDigitsLeftOfDecimal < uiFixedWidth) )
  1736. {
  1737. // The following handles the case where the user wants leading
  1738. // zeroes displayed
  1739. // Need to pad the front with zeroes
  1740. for ( UINT ui = 0; ui < (uiFixedWidth - uiDigitsLeftOfDecimal); ui++ )
  1741. {
  1742. wcscat( pwszNum, L"0" );
  1743. }
  1744. // HACK
  1745. // In order to force something like "zero zero zero three"
  1746. // into the form "0,003", we need to make GetNumberFormat()
  1747. // think that the first digit is 1.
  1748. WCHAR *pwc = wcschr( pwszNum, L'0' );
  1749. SPDBG_ASSERT( pwc );
  1750. *pwc = wcFakeFirstDigit;
  1751. fChangedFirstDigit = true;
  1752. }
  1753. // Copy over the unformatted number after the possible negative sign
  1754. wcscat( pwszNum, pwszTemp );
  1755. delete[] pwszTemp;
  1756. // If we do not want to format the number, then bail here
  1757. if ( dwDisplayFlags & DF_UNFORMATTED )
  1758. {
  1759. return S_OK;
  1760. }
  1761. // Make a copy so that we can change some fields according to the
  1762. // flags param
  1763. NUMBERFMTW nmfmt = m_nmfmtDefault;
  1764. // How many decimal places to display?
  1765. if ( dwDisplayFlags & DF_WHOLENUMBER )
  1766. {
  1767. nmfmt.NumDigits = 0;
  1768. }
  1769. else
  1770. {
  1771. // Use the uiDecimalPlaces value to determine how
  1772. // many to display
  1773. nmfmt.NumDigits = uiDecimalPlaces;
  1774. }
  1775. // Leading zeroes?
  1776. nmfmt.LeadingZero = (dwDisplayFlags & DF_LEADINGZERO) ? 1 : 0;
  1777. // Thousands grouping?
  1778. if ( dwDisplayFlags & DF_NOTHOUSANDSGROUP )
  1779. {
  1780. nmfmt.Grouping = 0;
  1781. }
  1782. // Format the number string
  1783. WCHAR *pwszFormattedNum = new WCHAR[ cSize ];
  1784. if ( !pwszFormattedNum )
  1785. {
  1786. return E_OUTOFMEMORY;
  1787. }
  1788. *pwszFormattedNum = 0;
  1789. int iRet;
  1790. do
  1791. {
  1792. iRet = m_Unicode.GetNumberFormat( LOCALE_USER_DEFAULT, 0,
  1793. pwszNum, &nmfmt, pwszFormattedNum, cSize );
  1794. if ( !iRet && nmfmt.NumDigits )
  1795. {
  1796. // Try displaying fewer digits
  1797. nmfmt.NumDigits--;
  1798. }
  1799. } while ( !iRet && nmfmt.NumDigits );
  1800. SPDBG_ASSERT( iRet );
  1801. // Copy the formatted number into pwszNum
  1802. wcscpy( pwszNum, pwszFormattedNum );
  1803. delete[] pwszFormattedNum;
  1804. // This undoes the hack of changing the first digit
  1805. if ( fChangedFirstDigit )
  1806. {
  1807. // We need to find the first digit and change it back to zero
  1808. WCHAR *pwc = wcschr( pwszNum, wcFakeFirstDigit );
  1809. SPDBG_ASSERT( pwc );
  1810. *pwc = L'0';
  1811. }
  1812. if ( dwDisplayFlags & DF_ORDINAL )
  1813. {
  1814. SPDBG_ASSERT( dwDisplayFlags & DF_WHOLENUMBER ); // sanity
  1815. // This is an ordinal number, tack on the appropriate suffix
  1816. // The "st", "nd", "rd" endings only happen when you
  1817. // don't have something like "twelfth"
  1818. if ( ((llIntPart % 100) < 10) || ((llIntPart % 100) > 20) )
  1819. {
  1820. switch ( llIntPart % 10 )
  1821. {
  1822. case 1:
  1823. wcscat( pwszNum, L"st" );
  1824. break;
  1825. case 2:
  1826. wcscat( pwszNum, L"nd" );
  1827. break;
  1828. case 3:
  1829. wcscat( pwszNum, L"rd" );
  1830. break;
  1831. default:
  1832. wcscat( pwszNum, L"th" );
  1833. break;
  1834. }
  1835. }
  1836. else
  1837. {
  1838. wcscat( pwszNum, L"th" );
  1839. }
  1840. }
  1841. return S_OK;
  1842. } /* CTestITN::MakeDisplayNumber */
  1843. /***********************************************************************
  1844. * CTestITN::MakeDigitString *
  1845. *---------------------------*
  1846. * Description:
  1847. * Called when we want to convert a string of DIGITs into
  1848. * a string but don't care about its value
  1849. * Return:
  1850. * Number of digits written to the string, including nul
  1851. * character
  1852. *************************************************************************/
  1853. int CTestITN::MakeDigitString( const SPPHRASEPROPERTY *pProperties,
  1854. WCHAR *pwszDigitString,
  1855. UINT cSize )
  1856. {
  1857. if ( !pProperties || !pwszDigitString )
  1858. {
  1859. return 0;
  1860. }
  1861. UINT cCount = 0;
  1862. for ( ; pProperties; pProperties = pProperties->pNextSibling )
  1863. {
  1864. if ( DIGIT != pProperties->ulId )
  1865. {
  1866. return 0;
  1867. }
  1868. if ( cSize-- <= 0 )
  1869. {
  1870. // Not enough space
  1871. return 0;
  1872. }
  1873. pwszDigitString[ cCount++ ] = pProperties->pszValue[0];
  1874. }
  1875. pwszDigitString[ cCount++ ] = 0;
  1876. return cCount;
  1877. } /* CTestITN::MakeDigitString */
  1878. /***********************************************************************
  1879. * CTestITN::GetNumberFormatDefaults *
  1880. *-----------------------------------*
  1881. * Description:
  1882. * This finds all of the defaults for formatting numbers for
  1883. * this user.
  1884. *************************************************************************/
  1885. HRESULT CTestITN::GetNumberFormatDefaults()
  1886. {
  1887. LCID lcid = ::GetUserDefaultLCID();
  1888. WCHAR pwszLocaleData[ MAX_LOCALE_DATA ];
  1889. int iRet = m_Unicode.GetLocaleInfo( lcid, LOCALE_IDIGITS, pwszLocaleData, MAX_LOCALE_DATA );
  1890. if ( !iRet )
  1891. {
  1892. return E_FAIL;
  1893. }
  1894. m_nmfmtDefault.NumDigits = _wtoi( pwszLocaleData );
  1895. iRet = m_Unicode.GetLocaleInfo( lcid, LOCALE_ILZERO, pwszLocaleData, MAX_LOCALE_DATA );
  1896. if ( !iRet )
  1897. {
  1898. return E_FAIL;
  1899. }
  1900. // It's always either 0 or 1
  1901. m_nmfmtDefault.LeadingZero = _wtoi( pwszLocaleData );
  1902. iRet = m_Unicode.GetLocaleInfo( lcid, LOCALE_SGROUPING, pwszLocaleData, MAX_LOCALE_DATA );
  1903. if ( !iRet )
  1904. {
  1905. return E_FAIL;
  1906. }
  1907. // It will look like single_digit;0, or else it will look like
  1908. // 3;2;0
  1909. UINT uiGrouping = *pwszLocaleData - L'0';
  1910. if ( (3 == uiGrouping) && (L';' == pwszLocaleData[1]) && (L'2' == pwszLocaleData[2]) )
  1911. {
  1912. uiGrouping = 32;
  1913. }
  1914. m_nmfmtDefault.Grouping = uiGrouping;
  1915. iRet = m_Unicode.GetLocaleInfo( lcid, LOCALE_SDECIMAL, m_pwszDecimalSep, MAX_LOCALE_DATA );
  1916. if ( !iRet )
  1917. {
  1918. return E_FAIL;
  1919. }
  1920. m_nmfmtDefault.lpDecimalSep = m_pwszDecimalSep;
  1921. iRet = m_Unicode.GetLocaleInfo( lcid, LOCALE_STHOUSAND, m_pwszThousandSep, MAX_LOCALE_DATA );
  1922. if ( !iRet )
  1923. {
  1924. return E_FAIL;
  1925. }
  1926. m_nmfmtDefault.lpThousandSep = m_pwszThousandSep;
  1927. iRet = m_Unicode.GetLocaleInfo( lcid, LOCALE_INEGNUMBER, pwszLocaleData, MAX_LOCALE_DATA );
  1928. if ( !iRet )
  1929. {
  1930. return E_FAIL;
  1931. }
  1932. m_nmfmtDefault.NegativeOrder = _wtoi( pwszLocaleData );
  1933. // Get the negative sign
  1934. delete[] m_pwszNeg;
  1935. iRet = m_Unicode.GetLocaleInfo( LOCALE_USER_DEFAULT,
  1936. LOCALE_SNEGATIVESIGN, NULL, 0);
  1937. if ( !iRet )
  1938. {
  1939. return E_FAIL;
  1940. }
  1941. m_pwszNeg = new WCHAR[ iRet ];
  1942. if ( !m_pwszNeg )
  1943. {
  1944. return E_OUTOFMEMORY;
  1945. }
  1946. iRet = m_Unicode.GetLocaleInfo( LOCALE_USER_DEFAULT,
  1947. LOCALE_SNEGATIVESIGN, m_pwszNeg, iRet );
  1948. return iRet ? S_OK : E_FAIL;
  1949. } /* CTestITN::GetNumberFormatDefaults */
  1950. /***********************************************************************
  1951. * CTestITN::GetCurrencyFormatDefaults *
  1952. *-----------------------------------*
  1953. * Description:
  1954. * This finds all of the defaults for formatting numbers for
  1955. * this user.
  1956. *************************************************************************/
  1957. HRESULT CTestITN::GetCurrencyFormatDefaults()
  1958. {
  1959. LCID lcid = ::GetUserDefaultLCID();
  1960. WCHAR pwszLocaleData[ MAX_LOCALE_DATA ];
  1961. int iRet = m_Unicode.GetLocaleInfo( lcid, LOCALE_ICURRDIGITS, pwszLocaleData, MAX_LOCALE_DATA );
  1962. if ( !iRet )
  1963. {
  1964. return E_FAIL;
  1965. }
  1966. m_cyfmtDefault.NumDigits = _wtoi( pwszLocaleData );
  1967. // NB: A value of zero is bogus for LOCALE_ILZERO, since
  1968. // currency should always display leading zero
  1969. m_cyfmtDefault.LeadingZero = 1;
  1970. iRet = m_Unicode.GetLocaleInfo( lcid, LOCALE_SMONGROUPING, pwszLocaleData, MAX_LOCALE_DATA );
  1971. if ( !iRet )
  1972. {
  1973. return E_FAIL;
  1974. }
  1975. // It will look like single_digit;0, or else it will look like
  1976. // 3;2;0
  1977. UINT uiGrouping = *pwszLocaleData - L'0';
  1978. if ( (3 == uiGrouping) && (L';' == pwszLocaleData[1]) && (L'2' == pwszLocaleData[2]) )
  1979. {
  1980. uiGrouping = 32;
  1981. }
  1982. m_cyfmtDefault.Grouping = uiGrouping;
  1983. iRet = m_Unicode.GetLocaleInfo( lcid, LOCALE_SMONDECIMALSEP, m_pwszDecimalSep, MAX_LOCALE_DATA );
  1984. if ( !iRet )
  1985. {
  1986. return E_FAIL;
  1987. }
  1988. m_cyfmtDefault.lpDecimalSep = m_pwszDecimalSep;
  1989. iRet = m_Unicode.GetLocaleInfo( lcid, LOCALE_SMONTHOUSANDSEP, m_pwszThousandSep, MAX_LOCALE_DATA );
  1990. if ( !iRet )
  1991. {
  1992. return E_FAIL;
  1993. }
  1994. m_cyfmtDefault.lpThousandSep = m_pwszThousandSep;
  1995. iRet = m_Unicode.GetLocaleInfo( lcid, LOCALE_INEGCURR, pwszLocaleData, MAX_LOCALE_DATA );
  1996. if ( !iRet )
  1997. {
  1998. return E_FAIL;
  1999. }
  2000. m_cyfmtDefault.NegativeOrder = _wtoi( pwszLocaleData );
  2001. iRet = m_Unicode.GetLocaleInfo( lcid, LOCALE_ICURRENCY, pwszLocaleData, MAX_LOCALE_DATA );
  2002. if ( !iRet )
  2003. {
  2004. return E_FAIL;
  2005. }
  2006. m_cyfmtDefault.PositiveOrder = _wtoi( pwszLocaleData );
  2007. iRet = m_Unicode.GetLocaleInfo( lcid, LOCALE_SCURRENCY, m_pwszCurrencySym, MAX_LOCALE_DATA );
  2008. if ( !iRet )
  2009. {
  2010. return E_FAIL;
  2011. }
  2012. m_cyfmtDefault.lpCurrencySymbol = m_pwszCurrencySym;
  2013. return S_OK;
  2014. } /* CTestITN::GetCurrencyFormatDefaults */
  2015. /***********************************************************************
  2016. * CTestITN::ComputeNum999 *
  2017. *-------------------------*
  2018. * Description:
  2019. * Converts a set of SPPHRASEPROPERTYs into a number in
  2020. * [-999, 999].
  2021. * The way these properties is structured is that the top-level
  2022. * properties contain the place of the number (100s, 10s, 1s)
  2023. * by having the value 100, 10, or 1.
  2024. * The child has the appropriate number value.
  2025. * Return:
  2026. * Value of the number
  2027. *************************************************************************/
  2028. ULONG CTestITN::ComputeNum999(const SPPHRASEPROPERTY *pProperties )//, ULONG *pVal)
  2029. {
  2030. ULONG ulVal = 0;
  2031. for (const SPPHRASEPROPERTY * pProp = pProperties; pProp; pProp = pProp->pNextSibling)
  2032. {
  2033. if ( ZERO != pProp->ulId )
  2034. {
  2035. SPDBG_ASSERT( pProp->pFirstChild );
  2036. SPDBG_ASSERT( VT_UI4 == pProp->vValue.vt );
  2037. SPDBG_ASSERT( VT_UI4 == pProp->pFirstChild->vValue.vt );
  2038. ulVal += pProp->pFirstChild->vValue.ulVal * pProp->vValue.ulVal;
  2039. }
  2040. }
  2041. return ulVal;
  2042. } /* CTestITN::ComputeNum999 */
  2043. /***********************************************************************
  2044. * CTestITN::HandleDigitsAfterDecimal *
  2045. *------------------------------------*
  2046. * Description:
  2047. * If pwszRightOfDecimal is NULL, then cuts off all of the numbers
  2048. * following the decimal separator.
  2049. * Otherwise, copies pwszRightOfDecimal right after the decimal
  2050. * separator in pwszFormattedNum.
  2051. * Preserves the stuff after the digits in the pwszFormattedNum
  2052. * (e.g. if pwszFormattedNum starts out "(3.00)" and
  2053. * pwszRightOfDecimal is NULL, then pwszFormattedNum will end
  2054. * up as "(3)"
  2055. *************************************************************************/
  2056. void CTestITN::HandleDigitsAfterDecimal( WCHAR *pwszFormattedNum,
  2057. UINT cSizeOfFormattedNum,
  2058. const WCHAR *pwszRightOfDecimal )
  2059. {
  2060. SPDBG_ASSERT( pwszFormattedNum );
  2061. // First need to find what the decimal string is
  2062. LCID lcid = ::GetUserDefaultLCID();
  2063. WCHAR pwszDecimalString[ MAX_LOCALE_DATA ]; // Guaranteed to be no longer than 4 long
  2064. int iRet = m_Unicode.GetLocaleInfo( lcid, LOCALE_SDECIMAL,
  2065. pwszDecimalString, MAX_LOCALE_DATA );
  2066. SPDBG_ASSERT( iRet );
  2067. WCHAR *pwcDecimal = wcsstr( pwszFormattedNum, pwszDecimalString );
  2068. SPDBG_ASSERT( pwcDecimal );
  2069. // pwcAfterDecimal points to the first character after the decimal separator
  2070. WCHAR *pwcAfterDecimal = pwcDecimal + wcslen( pwszDecimalString );
  2071. // Remember what originally followed the digits
  2072. WCHAR *pwszTemp = new WCHAR[ cSizeOfFormattedNum ];
  2073. WCHAR *pwcAfterDigits; // points to the first character after the end of the digits
  2074. for ( pwcAfterDigits = pwcAfterDecimal;
  2075. *pwcAfterDigits && iswdigit( *pwcAfterDigits );
  2076. pwcAfterDigits++ )
  2077. ;
  2078. wcscpy( pwszTemp, pwcAfterDigits ); // OK if *pwcAfterDigits == 0
  2079. if ( pwszRightOfDecimal )
  2080. {
  2081. // This means that the caller wants the digits in pwszRightOfDecimal
  2082. // copied after the decimal separator
  2083. // Copy the decimal string after the decimal separater
  2084. wcscpy( pwcAfterDecimal, pwszRightOfDecimal );
  2085. }
  2086. else
  2087. {
  2088. // This means that the caller wanted the decimal separator
  2089. // and all text following it stripped off
  2090. *pwcDecimal = 0;
  2091. }
  2092. // Add on the extra after-digit characters
  2093. wcscat( pwszFormattedNum, pwszTemp );
  2094. delete[] pwszTemp;
  2095. } /* CTestITN::HandleDigitsAfterDecimal */
  2096. /***********************************************************************
  2097. * CTestITN::GetMinAndMaxPos *
  2098. *---------------------------*
  2099. * Description:
  2100. * Gets the minimum and maximum elements spanned by the
  2101. * set of properties
  2102. *************************************************************************/
  2103. void CTestITN::GetMinAndMaxPos( const SPPHRASEPROPERTY *pProperties,
  2104. ULONG *pulMinPos,
  2105. ULONG *pulMaxPos )
  2106. {
  2107. if ( !pulMinPos || !pulMaxPos )
  2108. {
  2109. return;
  2110. }
  2111. ULONG ulMin = 9999999;
  2112. ULONG ulMax = 0;
  2113. for ( const SPPHRASEPROPERTY *pProp = pProperties; pProp; pProp = pProp->pNextSibling )
  2114. {
  2115. ulMin = __min( ulMin, pProp->ulFirstElement );
  2116. ulMax = __max( ulMax, pProp->ulFirstElement + pProp->ulCountOfElements );
  2117. }
  2118. *pulMinPos = ulMin;
  2119. *pulMaxPos = ulMax;
  2120. } /* CTestITN::GetMinAndMaxPos */
  2121. /***********************************************************************
  2122. * CTestITN::GetMonthName *
  2123. *------------------------*
  2124. * Description:
  2125. * Gets the name of the month, abbreviated if desired
  2126. * Return:
  2127. * Number of characters written to pszMonth, 0 if failed
  2128. *************************************************************************/
  2129. int CTestITN::GetMonthName( int iMonth, WCHAR *pwszMonth, int cSize, bool fAbbrev )
  2130. {
  2131. LCTYPE lctype;
  2132. switch ( iMonth )
  2133. {
  2134. case 1:
  2135. lctype = fAbbrev ? LOCALE_SABBREVMONTHNAME1 : LOCALE_SMONTHNAME1;
  2136. break;
  2137. case 2:
  2138. lctype = fAbbrev ? LOCALE_SABBREVMONTHNAME2 : LOCALE_SMONTHNAME2;
  2139. break;
  2140. case 3:
  2141. lctype = fAbbrev ? LOCALE_SABBREVMONTHNAME3 : LOCALE_SMONTHNAME3;
  2142. break;
  2143. case 4:
  2144. lctype = fAbbrev ? LOCALE_SABBREVMONTHNAME4 : LOCALE_SMONTHNAME4;
  2145. break;
  2146. case 5:
  2147. lctype = fAbbrev ? LOCALE_SABBREVMONTHNAME5 : LOCALE_SMONTHNAME5;
  2148. break;
  2149. case 6:
  2150. lctype = fAbbrev ? LOCALE_SABBREVMONTHNAME6 : LOCALE_SMONTHNAME6;
  2151. break;
  2152. case 7:
  2153. lctype = fAbbrev ? LOCALE_SABBREVMONTHNAME7 : LOCALE_SMONTHNAME7;
  2154. break;
  2155. case 8:
  2156. lctype = fAbbrev ? LOCALE_SABBREVMONTHNAME8 : LOCALE_SMONTHNAME8;
  2157. break;
  2158. case 9:
  2159. lctype = fAbbrev ? LOCALE_SABBREVMONTHNAME9 : LOCALE_SMONTHNAME9;
  2160. break;
  2161. case 10:
  2162. lctype = fAbbrev ? LOCALE_SABBREVMONTHNAME10 : LOCALE_SMONTHNAME10;
  2163. break;
  2164. case 11:
  2165. lctype = fAbbrev ? LOCALE_SABBREVMONTHNAME11 : LOCALE_SMONTHNAME11;
  2166. break;
  2167. case 12:
  2168. lctype = fAbbrev ? LOCALE_SABBREVMONTHNAME12 : LOCALE_SMONTHNAME12;
  2169. break;
  2170. default:
  2171. return 0;
  2172. }
  2173. int iRet = m_Unicode.GetLocaleInfo( LOCALE_USER_DEFAULT, lctype, pwszMonth, cSize );
  2174. return iRet;
  2175. } /* CTestITN::GetMonthName */
  2176. /***********************************************************************
  2177. * CTestITN::GetDayOfWeekName *
  2178. *----------------------------*
  2179. * Description:
  2180. * Gets the name of the day of week, abbreviated if desired
  2181. * Return:
  2182. * Number of characters written to pszDayOfWeek, 0 if failed
  2183. *************************************************************************/
  2184. int CTestITN::GetDayOfWeekName( int iDayOfWeek,
  2185. WCHAR *pwszDayOfWeek,
  2186. int cSize,
  2187. bool fAbbrev )
  2188. {
  2189. LCTYPE lctype;
  2190. switch ( iDayOfWeek )
  2191. {
  2192. case 1:
  2193. lctype = fAbbrev ? LOCALE_SABBREVDAYNAME1 : LOCALE_SDAYNAME1;
  2194. break;
  2195. case 2:
  2196. lctype = fAbbrev ? LOCALE_SABBREVDAYNAME2 : LOCALE_SDAYNAME2;
  2197. break;
  2198. case 3:
  2199. lctype = fAbbrev ? LOCALE_SABBREVDAYNAME3 : LOCALE_SDAYNAME3;
  2200. break;
  2201. case 4:
  2202. lctype = fAbbrev ? LOCALE_SABBREVDAYNAME4 : LOCALE_SDAYNAME4;
  2203. break;
  2204. case 5:
  2205. lctype = fAbbrev ? LOCALE_SABBREVDAYNAME5 : LOCALE_SDAYNAME5;
  2206. break;
  2207. case 6:
  2208. lctype = fAbbrev ? LOCALE_SABBREVDAYNAME6 : LOCALE_SDAYNAME6;
  2209. break;
  2210. case 7:
  2211. lctype = fAbbrev ? LOCALE_SABBREVDAYNAME7 : LOCALE_SDAYNAME7;
  2212. break;
  2213. default:
  2214. return 0;
  2215. }
  2216. int iRet = m_Unicode.GetLocaleInfo( LOCALE_USER_DEFAULT, lctype,
  2217. pwszDayOfWeek, cSize );
  2218. return iRet;
  2219. } /* CTestITN::GetMonthName */
  2220. /***********************************************************************
  2221. * CTestITN::FormatDate *
  2222. *----------------------*
  2223. * Description:
  2224. * Uses the format string to format a SYSTEMTIME date.
  2225. * We are using this instead of GetDateFormat() since
  2226. * we also want to format bogus dates and dates with
  2227. * years like 1492 that are not accepted by GetDateFormat()
  2228. * Return:
  2229. * Number of characters written to pszDate (including
  2230. * null terminating character), 0 if failed
  2231. *************************************************************************/
  2232. int CTestITN::FormatDate( const SYSTEMTIME &stDate,
  2233. WCHAR *pwszFormat,
  2234. WCHAR *pwszDate,
  2235. int cSize )
  2236. {
  2237. if ( !pwszFormat || !pwszDate )
  2238. {
  2239. SPDBG_ASSERT( FALSE );
  2240. return 0;
  2241. }
  2242. WCHAR * const pwszDateStart = pwszDate;
  2243. WCHAR *pwc = pwszFormat;
  2244. // Copy the format string to the date string character by
  2245. // character, replacing the string like "dddd" as appropriate
  2246. while ( *pwc )
  2247. {
  2248. switch( *pwc )
  2249. {
  2250. case L'd':
  2251. {
  2252. // Count the number of d's
  2253. int cNumDs = 0;
  2254. int iRet;
  2255. do
  2256. {
  2257. pwc++;
  2258. cNumDs++;
  2259. } while ( L'd' == *pwc );
  2260. switch ( cNumDs )
  2261. {
  2262. case 1:
  2263. // Day with no leading zeroes
  2264. swprintf( pwszDate, L"%d", stDate.wDay );
  2265. iRet = wcslen( pwszDate );
  2266. break;
  2267. case 2:
  2268. // Day with one fixed width of 2
  2269. swprintf( pwszDate, L"%02d", stDate.wDay );
  2270. iRet = wcslen( pwszDate );
  2271. break;
  2272. case 3:
  2273. // Abbreviated day of week
  2274. iRet = GetDayOfWeekName( stDate.wDayOfWeek, pwszDate, cSize, true ) - 1;
  2275. break;
  2276. default: // More than 4? Treat it as 4
  2277. // Day of week
  2278. iRet = GetDayOfWeekName( stDate.wDayOfWeek, pwszDate, cSize, false ) - 1;
  2279. break;
  2280. }
  2281. if ( iRet <= 0 )
  2282. {
  2283. return 0;
  2284. }
  2285. else
  2286. {
  2287. pwszDate += iRet;
  2288. }
  2289. break;
  2290. }
  2291. case L'M':
  2292. {
  2293. // Count the number of M's
  2294. int cNumMs = 0;
  2295. int iRet;
  2296. do
  2297. {
  2298. pwc++;
  2299. cNumMs++;
  2300. } while ( L'M' == *pwc );
  2301. switch ( cNumMs )
  2302. {
  2303. case 1:
  2304. // Day with no leading zeroes
  2305. swprintf( pwszDate, L"%d", stDate.wMonth );
  2306. iRet = wcslen( pwszDate );
  2307. break;
  2308. case 2:
  2309. // Day with one fixed width of 2
  2310. swprintf( pwszDate, L"%02d", stDate.wMonth );
  2311. iRet = wcslen( pwszDate );
  2312. break;
  2313. case 3:
  2314. // Abbreviated month name
  2315. iRet = GetMonthName( stDate.wMonth, pwszDate, cSize, true ) - 1;
  2316. break;
  2317. default: // More than 4? Treat it as 4
  2318. // Month
  2319. iRet = GetMonthName( stDate.wMonth, pwszDate, cSize, false ) - 1;
  2320. break;
  2321. }
  2322. if ( iRet < 0 )
  2323. {
  2324. return 0;
  2325. }
  2326. else
  2327. {
  2328. pwszDate += iRet;
  2329. }
  2330. break;
  2331. }
  2332. case L'y':
  2333. {
  2334. // Count the number of y's
  2335. int cNumYs = 0;
  2336. do
  2337. {
  2338. pwc++;
  2339. cNumYs++;
  2340. } while ( L'y' == *pwc );
  2341. // More than 4 y's: consider it as 4 y's
  2342. if ( cNumYs > 4 )
  2343. {
  2344. cNumYs = 4;
  2345. }
  2346. if (( cNumYs >= 3 ) && ( stDate.wYear < 100 ))
  2347. {
  2348. // "Ninety nine": Should display as "'99"
  2349. cNumYs = 2;
  2350. *pwszDate++ = L'\'';
  2351. }
  2352. switch ( cNumYs )
  2353. {
  2354. case 1: case 2:
  2355. // Last two digits of year, width of 2
  2356. swprintf( pwszDate, (1 == cNumYs ) ? L"%d" : L"%02d",
  2357. stDate.wYear % 100 );
  2358. pwszDate += 2;
  2359. break;
  2360. case 3: case 4:
  2361. // All four digits of year, width of 4
  2362. // Last two digits of year, width of 2
  2363. swprintf( pwszDate, L"%04d", stDate.wYear % 10000 );
  2364. pwszDate += 4;
  2365. break;
  2366. }
  2367. break;
  2368. }
  2369. default:
  2370. *pwszDate++ = *pwc++;
  2371. }
  2372. }
  2373. *pwszDate++ = 0;
  2374. return (int) (pwszDate - pwszDateStart);
  2375. } /* CTestITN::FormatDate */
  2376. /***********************************************************************
  2377. * CTestITN::MakeNumberNegative *
  2378. *------------------------------*
  2379. * Description:
  2380. * Uses the current number format defaults to transform
  2381. * pszNumber into a negative number
  2382. * Return:
  2383. * S_OK
  2384. * E_OUTOFMEMORY
  2385. *************************************************************************/
  2386. HRESULT CTestITN::MakeNumberNegative( WCHAR *pwszNumber )
  2387. {
  2388. HRESULT hr = GetNumberFormatDefaults();
  2389. if ( FAILED( hr ) )
  2390. {
  2391. return hr;
  2392. }
  2393. // Create a temporary buffer with the non-negated number in it
  2394. WCHAR *pwszTemp = wcsdup( pwszNumber );
  2395. if ( !pwszTemp )
  2396. {
  2397. return E_OUTOFMEMORY;
  2398. }
  2399. switch ( m_nmfmtDefault.NegativeOrder )
  2400. {
  2401. case 0:
  2402. // (1.1)
  2403. wcscpy( pwszNumber, L"(" );
  2404. wcscat( pwszNumber, pwszTemp );
  2405. wcscat( pwszNumber, L")" );
  2406. break;
  2407. case 1: case 2:
  2408. // 1: -1.1 2: - 1.1
  2409. wcscpy( pwszNumber, m_pwszNeg );
  2410. if ( 2 == m_nmfmtDefault.NegativeOrder )
  2411. {
  2412. wcscat( pwszNumber, L" " );
  2413. }
  2414. wcscat( pwszNumber, pwszTemp );
  2415. break;
  2416. case 3: case 4:
  2417. // 3: 1.1- 4: 1.1 -
  2418. wcscpy( pwszNumber, pwszTemp );
  2419. if ( 4 == m_nmfmtDefault.NegativeOrder )
  2420. {
  2421. wcscat( pwszNumber, L" " );
  2422. }
  2423. wcscat( pwszNumber, m_pwszNeg );
  2424. break;
  2425. default:
  2426. SPDBG_ASSERT( false );
  2427. break;
  2428. }
  2429. free( pwszTemp );
  2430. return S_OK;
  2431. } /* CTestITN::MakeNumberNegative */