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.

2358 lines
72 KiB

  1. /*++
  2. Copyright (c) 1991-2000, Microsoft Corporation All rights reserved.
  3. Module Name:
  4. number.c
  5. Abstract:
  6. This file contains functions that form properly formatted number and
  7. currency strings for a given locale.
  8. APIs found in this file:
  9. GetNumberFormatW
  10. GetCurrencyFormatW
  11. Revision History:
  12. 07-28-93 JulieB Created.
  13. --*/
  14. //
  15. // Include Files.
  16. //
  17. #include "nls.h"
  18. #include "nlssafe.h"
  19. //
  20. // Constant Declarations.
  21. //
  22. #define MAX_NUMBER_BUFFER 256 // size of static buffer
  23. #define MAX_GROUPS 5 // max number of groupings
  24. #define MAX_GROUPING_NUMBER 9999 // max value for groupings
  25. //
  26. // Account for:
  27. // - number of fractional digits
  28. // - decimal seperator
  29. // - negative sign
  30. // - zero terminator
  31. //
  32. #define MAX_NON_INTEGER_PART ( MAX_VALUE_IDIGITS + \
  33. MAX_SDECIMAL + \
  34. MAX_SNEGSIGN + \
  35. 1 )
  36. //
  37. // Account for:
  38. // - negative sign
  39. // - blank spaces
  40. // - one extra number from rounding
  41. // - one extra grouping separator from rounding
  42. //
  43. #define MAX_NUMBER_EXTRAS ( MAX_SNEGSIGN + \
  44. MAX_BLANKS + \
  45. 1 + \
  46. MAX_STHOUSAND )
  47. //
  48. // Account for:
  49. // - negative sign
  50. // - currency sign
  51. // - blank spaces
  52. // - one extra number from rounding
  53. // - one extra grouping separator from rounding
  54. //
  55. #define MAX_CURRENCY_EXTRAS ( MAX_SNEGSIGN + \
  56. MAX_SCURRENCY + \
  57. MAX_BLANKS + \
  58. 1 + \
  59. MAX_SMONTHOUSEP )
  60. //
  61. // Forward Declarations.
  62. //
  63. BOOL
  64. IsValidNumberFormat(
  65. CONST NUMBERFMTW *pFormat);
  66. BOOL
  67. IsValidCurrencyFormat(
  68. CONST CURRENCYFMTW *pFormat);
  69. UINT
  70. GetRegIntValue(
  71. LCID Locale,
  72. LCTYPE LCType,
  73. BOOL NoUserOverride,
  74. SIZE_T CacheOffset,
  75. LPWSTR pRegVal,
  76. LPWSTR pDefault,
  77. int DefaultVal,
  78. int UpperBound);
  79. int
  80. ConvertGroupingStringToInt(
  81. LPWSTR pGroupingSrc,
  82. LPWSTR pGroupingDest);
  83. UINT
  84. GetGroupingValue(
  85. LCID Locale,
  86. LCTYPE LCType,
  87. BOOL NoUserOverride,
  88. SIZE_T CacheOffset,
  89. LPWSTR pRegVal,
  90. LPWSTR pDefault,
  91. int DefaultVal);
  92. int
  93. GetNumberString(
  94. PLOC_HASH pHashN,
  95. LPWSTR pValue,
  96. LPNUMBERFMTW pFormat,
  97. LPWSTR *ppBuf,
  98. int BufSize,
  99. BOOL *pfZeroValue,
  100. int *pNeededSizeToAllocate,
  101. BOOL fSetError);
  102. int
  103. ParseNumber(
  104. PLOC_HASH pHashN,
  105. BOOL NoUserOverride,
  106. LPWSTR pValue,
  107. LPNUMBERFMTW pFormat,
  108. LPWSTR *ppBuf,
  109. int BufSize,
  110. int *pNeededSizeToAllocate,
  111. BOOL fSetError);
  112. int
  113. ParseCurrency(
  114. PLOC_HASH pHashN,
  115. BOOL NoUserOverride,
  116. LPWSTR pValue,
  117. LPCURRENCYFMTW pFormat,
  118. LPWSTR *ppBuf,
  119. int BufSize,
  120. int *pNeededSizeToAllocate,
  121. BOOL fSetError);
  122. //-------------------------------------------------------------------------//
  123. // INTERNAL MACROS //
  124. //-------------------------------------------------------------------------//
  125. ////////////////////////////////////////////////////////////////////////////
  126. //
  127. // NLS_COPY_UNICODE_STR
  128. //
  129. // Copies a zero terminated Unicode string from pSrc to the pDest buffer.
  130. // The pDest pointer is advanced to the end of the string.
  131. //
  132. // DEFINED AS A MACRO.
  133. //
  134. // 07-28-93 JulieB Created.
  135. ////////////////////////////////////////////////////////////////////////////
  136. #define NLS_COPY_UNICODE_STR( pDest, \
  137. pSrc ) \
  138. { \
  139. LPWSTR pTmp; /* temp pointer to source */ \
  140. \
  141. \
  142. pTmp = pSrc; \
  143. while (*pTmp) \
  144. { \
  145. *pDest = *pTmp; \
  146. pDest++; \
  147. pTmp++; \
  148. } \
  149. }
  150. ////////////////////////////////////////////////////////////////////////////
  151. //
  152. // NLS_COPY_UNICODE_STR_NOADV
  153. //
  154. // Copies a zero terminated Unicode string from pSrc to the pDest buffer.
  155. // The pDest pointer is NOT advanced to the end of the string.
  156. //
  157. // DEFINED AS A MACRO.
  158. //
  159. // 07-28-93 JulieB Created.
  160. ////////////////////////////////////////////////////////////////////////////
  161. #define NLS_COPY_UNICODE_STR_TMP( pDest, \
  162. pSrc ) \
  163. { \
  164. LPWSTR pSrcT; /* temp pointer to source */ \
  165. LPWSTR pDestT; /* temp pointer to destination */ \
  166. \
  167. \
  168. pSrcT = pSrc; \
  169. pDestT = pDest; \
  170. while (*pSrcT) \
  171. { \
  172. *pDestT = *pSrcT; \
  173. pDestT++; \
  174. pSrcT++; \
  175. } \
  176. }
  177. ////////////////////////////////////////////////////////////////////////////
  178. //
  179. // NLS_ROUND_IT
  180. //
  181. // Rounds the floating point number given as a string.
  182. //
  183. // NOTE: This function will reset the pBegin pointer if an
  184. // extra character is added to the string.
  185. //
  186. // DEFINED AS A MACRO.
  187. //
  188. // 07-28-93 JulieB Created.
  189. ////////////////////////////////////////////////////////////////////////////
  190. #define NLS_ROUND_IT( pBegin, \
  191. pEnd, \
  192. IntPartGroup, \
  193. pSep ) \
  194. { \
  195. LPWSTR pRound = pEnd; /* ptr to position in string */ \
  196. LPWSTR pEndSep; /* ptr to end of group separator */ \
  197. \
  198. \
  199. /* \
  200. * Round the digits in the string one by one, going backwards in \
  201. * the string. Stop when either a value other than 9 is found, \
  202. * or the beginning of the string is reached. \
  203. */ \
  204. while (pRound >= pBegin) \
  205. { \
  206. if ((*pRound < NLS_CHAR_ZERO) || (*pRound > NLS_CHAR_NINE)) \
  207. { \
  208. pRound--; \
  209. } \
  210. else if (*pRound == NLS_CHAR_NINE) \
  211. { \
  212. *pRound = NLS_CHAR_ZERO; \
  213. pRound--; \
  214. } \
  215. else \
  216. { \
  217. (*pRound)++; \
  218. break; \
  219. } \
  220. } \
  221. \
  222. /* \
  223. * Make sure we don't have a number like 9.999, where we would need \
  224. * to add an extra character to the string and make it 10.00. \
  225. */ \
  226. if (pRound < pBegin) \
  227. { \
  228. /* \
  229. * All values to the right of the decimal are zero. All values \
  230. * to the left of the decimal are either zero or the grouping \
  231. * separator. \
  232. */ \
  233. if ((IntPartGroup) == 0) \
  234. { \
  235. /* \
  236. * Adding another integer means we need to add another \
  237. * grouping separator. \
  238. */ \
  239. pEndSep = pSep + NlsStrLenW(pSep) - 1; \
  240. while (pEndSep >= pSep) \
  241. { \
  242. (pBegin)--; \
  243. *(pBegin) = *pEndSep; \
  244. pEndSep--; \
  245. } \
  246. } \
  247. \
  248. /* \
  249. * Store a 1 at the beginning of the string and reset the \
  250. * pointer to the beginning of the string. \
  251. */ \
  252. (pBegin)--; \
  253. *(pBegin) = NLS_CHAR_ONE; \
  254. } \
  255. }
  256. //-------------------------------------------------------------------------//
  257. // API ROUTINES //
  258. //-------------------------------------------------------------------------//
  259. ////////////////////////////////////////////////////////////////////////////
  260. //
  261. // GetNumberFormatW
  262. //
  263. // Returns a properly formatted number string for the given locale.
  264. // This call also indicates how much memory is necessary to contain
  265. // the desired information.
  266. //
  267. // 07-28-93 JulieB Created.
  268. ////////////////////////////////////////////////////////////////////////////
  269. int WINAPI GetNumberFormatW(
  270. LCID Locale,
  271. DWORD dwFlags,
  272. LPCWSTR lpValue,
  273. CONST NUMBERFMTW *lpFormat,
  274. LPWSTR lpNumberStr,
  275. int cchNumber)
  276. {
  277. PLOC_HASH pHashN; // ptr to LOC hash node
  278. int Length = 0; // number of characters written
  279. LPNUMBERFMTW pFormat; // ptr to number format struct
  280. NUMBERFMTW NumFmt; // number format
  281. WCHAR pString[MAX_NUMBER_BUFFER]; // ptr to temporary buffer
  282. LPWSTR pFinal; // ptr to the final string
  283. BOOL NoUserOverride; // if no user override flag set
  284. WCHAR pDecimal[MAX_REG_VAL_SIZE]; // temp buffer for decimal sep
  285. WCHAR pThousand[MAX_REG_VAL_SIZE]; // temp buffer for thousand sep
  286. int NeededSizeToAllocate = 0; // size of buffer needed
  287. WCHAR *pTemp = NULL; // allocated temp storage buffer
  288. //
  289. // Initialize UserOverride.
  290. //
  291. NoUserOverride = dwFlags & LOCALE_NOUSEROVERRIDE;
  292. //
  293. // Invalid Parameter Check:
  294. // - validate LCID
  295. // - count is negative
  296. // - NULL src string
  297. // - NULL data pointer AND count is not zero
  298. // - ptrs to string buffers same
  299. //
  300. VALIDATE_LOCALE(Locale, pHashN, FALSE);
  301. if ( (pHashN == NULL) ||
  302. (cchNumber < 0) ||
  303. (lpValue == NULL) ||
  304. ((lpNumberStr == NULL) && (cchNumber != 0)) ||
  305. (lpValue == lpNumberStr) )
  306. {
  307. SetLastError(ERROR_INVALID_PARAMETER);
  308. return (0);
  309. }
  310. //
  311. // Invalid Flags Check:
  312. // - flags other than valid ones
  313. // - lpFormat not NULL AND NoUserOverride flag is set
  314. //
  315. if ( (dwFlags & GNF_INVALID_FLAG) ||
  316. ((lpFormat != NULL) && (NoUserOverride)) )
  317. {
  318. SetLastError(ERROR_INVALID_FLAGS);
  319. return (0);
  320. }
  321. //
  322. // Set pFormat to point at the proper format structure.
  323. //
  324. if (lpFormat != NULL)
  325. {
  326. //
  327. // Use the format structure given by the caller.
  328. //
  329. pFormat = (LPNUMBERFMTW)lpFormat;
  330. if (!IsValidNumberFormat(pFormat))
  331. {
  332. SetLastError(ERROR_INVALID_PARAMETER);
  333. return (0);
  334. }
  335. }
  336. else
  337. {
  338. //
  339. // Use the format structure defined here.
  340. //
  341. pFormat = &NumFmt;
  342. //
  343. // Get the number of decimal digits.
  344. //
  345. pFormat->NumDigits =
  346. GetRegIntValue( Locale,
  347. LOCALE_IDIGITS,
  348. NoUserOverride,
  349. FIELD_OFFSET(NLS_USER_INFO, iDigits),
  350. NLS_VALUE_IDIGITS,
  351. pHashN->pLocaleFixed->szIDigits,
  352. 2,
  353. MAX_VALUE_IDIGITS );
  354. //
  355. // Get the leading zero in decimal fields option.
  356. //
  357. pFormat->LeadingZero =
  358. GetRegIntValue( Locale,
  359. LOCALE_ILZERO,
  360. NoUserOverride,
  361. FIELD_OFFSET(NLS_USER_INFO, iLZero),
  362. NLS_VALUE_ILZERO,
  363. pHashN->pLocaleFixed->szILZero,
  364. 1,
  365. MAX_VALUE_ILZERO );
  366. //
  367. // Get the negative ordering.
  368. //
  369. pFormat->NegativeOrder =
  370. GetRegIntValue( Locale,
  371. LOCALE_INEGNUMBER,
  372. NoUserOverride,
  373. FIELD_OFFSET(NLS_USER_INFO, iNegNumber),
  374. NLS_VALUE_INEGNUMBER,
  375. pHashN->pLocaleFixed->szINegNumber,
  376. 1,
  377. MAX_VALUE_INEGNUMBER );
  378. //
  379. // Get the grouping left of the decimal.
  380. //
  381. pFormat->Grouping =
  382. GetGroupingValue( Locale,
  383. LOCALE_SGROUPING,
  384. NoUserOverride,
  385. FIELD_OFFSET(NLS_USER_INFO, sGrouping),
  386. NLS_VALUE_SGROUPING,
  387. (LPWORD)(pHashN->pLocaleHdr) +
  388. pHashN->pLocaleHdr->SGrouping,
  389. 3 );
  390. //
  391. // Get the decimal separator.
  392. //
  393. // NOTE: This must follow the above calls because
  394. // pDecSep is used as a temporary buffer above.
  395. //
  396. if ( (!NoUserOverride) &&
  397. GetUserInfo( Locale,
  398. LOCALE_SDECIMAL,
  399. FIELD_OFFSET(NLS_USER_INFO, sDecimal),
  400. NLS_VALUE_SDECIMAL,
  401. pDecimal,
  402. ARRAYSIZE(pDecimal),
  403. TRUE ) &&
  404. IsValidSeparatorString( pDecimal,
  405. MAX_SDECIMAL,
  406. FALSE ) )
  407. {
  408. pFormat->lpDecimalSep = pDecimal;
  409. }
  410. else
  411. {
  412. pFormat->lpDecimalSep = (LPWORD)(pHashN->pLocaleHdr) +
  413. pHashN->pLocaleHdr->SDecimal;
  414. }
  415. //
  416. // Get the thousand separator.
  417. // This string may be a null string.
  418. //
  419. if ( (!NoUserOverride) &&
  420. GetUserInfo( Locale,
  421. LOCALE_STHOUSAND,
  422. FIELD_OFFSET(NLS_USER_INFO, sThousand),
  423. NLS_VALUE_STHOUSAND,
  424. pThousand,
  425. ARRAYSIZE(pThousand),
  426. FALSE ) &&
  427. IsValidSeparatorString( pThousand,
  428. MAX_STHOUSAND,
  429. FALSE ) )
  430. {
  431. pFormat->lpThousandSep = pThousand;
  432. }
  433. else
  434. {
  435. pFormat->lpThousandSep = (LPWORD)(pHashN->pLocaleHdr) +
  436. pHashN->pLocaleHdr->SThousand;
  437. }
  438. }
  439. //
  440. // Parse the number format string.
  441. //
  442. pFinal = pString;
  443. Length = ParseNumber( pHashN,
  444. NoUserOverride,
  445. (LPWSTR)lpValue,
  446. pFormat,
  447. &pFinal,
  448. MAX_NUMBER_BUFFER,
  449. &NeededSizeToAllocate,
  450. FALSE );
  451. //
  452. // If the failure is due to a stack variable size limitation, then
  453. // try to satisfy the request from the local process heap.
  454. //
  455. if ((Length == 0) && (NeededSizeToAllocate > 0))
  456. {
  457. pTemp = RtlAllocateHeap( RtlProcessHeap(),
  458. 0,
  459. NeededSizeToAllocate * sizeof(TCHAR) );
  460. if (pTemp)
  461. {
  462. pFinal = pTemp;
  463. Length = ParseNumber( pHashN,
  464. NoUserOverride,
  465. (LPWSTR)lpValue,
  466. pFormat,
  467. &pFinal,
  468. NeededSizeToAllocate,
  469. &NeededSizeToAllocate,
  470. TRUE );
  471. }
  472. }
  473. //
  474. // Check cchNumber for size of given buffer.
  475. //
  476. if ((cchNumber == 0) || (Length == 0))
  477. {
  478. //
  479. // If cchNumber is 0, then we can't use lpNumberStr. In this
  480. // case, we simply want to return the length (in characters) of
  481. // the string to be copied.
  482. //
  483. Length = Length;
  484. }
  485. else if (cchNumber < Length)
  486. {
  487. //
  488. // The buffer is too small for the string, so return an error
  489. // and zero bytes written.
  490. //
  491. SetLastError(ERROR_INSUFFICIENT_BUFFER);
  492. Length = 0;
  493. }
  494. else
  495. {
  496. //
  497. // Copy the number string to lpNumberStr and null terminate it.
  498. // Return the number of characters copied.
  499. //
  500. if(FAILED(StringCchCopyW(lpNumberStr, cchNumber, pFinal)))
  501. {
  502. //
  503. // Failure should in theory be impossible, but if we ignore the
  504. // return value, PREfast will complain.
  505. //
  506. SetLastError(ERROR_OUTOFMEMORY);
  507. Length = 0;
  508. }
  509. }
  510. //
  511. // Free any dynamically allocated memory.
  512. //
  513. if (pTemp != NULL)
  514. {
  515. RtlFreeHeap(RtlProcessHeap(), 0, pTemp);
  516. }
  517. //
  518. // Return the number of characters copied.
  519. //
  520. return (Length);
  521. }
  522. ////////////////////////////////////////////////////////////////////////////
  523. //
  524. // GetCurrencyFormatW
  525. //
  526. // Returns a properly formatted currency string for the given locale.
  527. // This call also indicates how much memory is necessary to contain
  528. // the desired information.
  529. //
  530. // 07-28-93 JulieB Created.
  531. ////////////////////////////////////////////////////////////////////////////
  532. int WINAPI GetCurrencyFormatW(
  533. LCID Locale,
  534. DWORD dwFlags,
  535. LPCWSTR lpValue,
  536. CONST CURRENCYFMTW *lpFormat,
  537. LPWSTR lpCurrencyStr,
  538. int cchCurrency)
  539. {
  540. PLOC_HASH pHashN; // ptr to LOC hash node
  541. int Length = 0; // number of characters written
  542. LPCURRENCYFMTW pFormat; // ptr to currency format struct
  543. CURRENCYFMTW CurrFmt; // currency format
  544. WCHAR pString[MAX_NUMBER_BUFFER]; // ptr to temporary buffer
  545. LPWSTR pFinal; // ptr to the final string
  546. BOOL NoUserOverride; // if no user override flag set
  547. WCHAR pDecimal[MAX_REG_VAL_SIZE]; // temp buffer for decimal sep
  548. WCHAR pThousand[MAX_REG_VAL_SIZE]; // temp buffer for thousand sep
  549. WCHAR pCurrency[MAX_REG_VAL_SIZE]; // temp buffer for currency symbol
  550. int NeededSizeToAllocate = 0; // size of buffer needed
  551. WCHAR *pTemp = NULL; // allocated temp storage buffer
  552. //
  553. // Initialize UserOverride.
  554. //
  555. NoUserOverride = dwFlags & LOCALE_NOUSEROVERRIDE;
  556. //
  557. // Invalid Parameter Check:
  558. // - validate LCID
  559. // - count is negative
  560. // - NULL src string
  561. // - NULL data pointer AND count is not zero
  562. // - ptrs to string buffers same
  563. //
  564. VALIDATE_LOCALE(Locale, pHashN, FALSE);
  565. if ( (pHashN == NULL) ||
  566. (cchCurrency < 0) ||
  567. (lpValue == NULL) ||
  568. ((lpCurrencyStr == NULL) && (cchCurrency != 0)) ||
  569. (lpValue == lpCurrencyStr) )
  570. {
  571. SetLastError(ERROR_INVALID_PARAMETER);
  572. return (0);
  573. }
  574. //
  575. // Invalid Flags Check:
  576. // - flags other than valid ones
  577. // - lpFormat not NULL AND NoUserOverride flag is set
  578. //
  579. if ( (dwFlags & GCF_INVALID_FLAG) ||
  580. ((lpFormat != NULL) && (NoUserOverride)) )
  581. {
  582. SetLastError(ERROR_INVALID_FLAGS);
  583. return (0);
  584. }
  585. //
  586. // Set pFormat to point at the proper format structure.
  587. //
  588. if (lpFormat != NULL)
  589. {
  590. //
  591. // Use the format structure given by the caller.
  592. //
  593. pFormat = (LPCURRENCYFMTW)lpFormat;
  594. if (!IsValidCurrencyFormat(pFormat))
  595. {
  596. SetLastError(ERROR_INVALID_PARAMETER);
  597. return (0);
  598. }
  599. }
  600. else
  601. {
  602. //
  603. // Use the format structure defined here.
  604. //
  605. pFormat = &CurrFmt;
  606. //
  607. // Get the number of decimal digits.
  608. //
  609. pFormat->NumDigits =
  610. GetRegIntValue( Locale,
  611. LOCALE_ICURRDIGITS,
  612. NoUserOverride,
  613. FIELD_OFFSET(NLS_USER_INFO, iCurrDigits),
  614. NLS_VALUE_ICURRDIGITS,
  615. pHashN->pLocaleFixed->szICurrDigits,
  616. 2,
  617. MAX_VALUE_ICURRDIGITS );
  618. //
  619. // Get the leading zero in decimal fields option.
  620. //
  621. pFormat->LeadingZero =
  622. GetRegIntValue( Locale,
  623. LOCALE_ILZERO,
  624. NoUserOverride,
  625. FIELD_OFFSET(NLS_USER_INFO, iLZero),
  626. NLS_VALUE_ILZERO,
  627. pHashN->pLocaleFixed->szILZero,
  628. 1,
  629. MAX_VALUE_ILZERO );
  630. //
  631. // Get the positive ordering.
  632. //
  633. pFormat->PositiveOrder =
  634. GetRegIntValue( Locale,
  635. LOCALE_ICURRENCY,
  636. NoUserOverride,
  637. FIELD_OFFSET(NLS_USER_INFO, iCurrency),
  638. NLS_VALUE_ICURRENCY,
  639. pHashN->pLocaleFixed->szICurrency,
  640. 0,
  641. MAX_VALUE_ICURRENCY );
  642. //
  643. // Get the negative ordering.
  644. //
  645. pFormat->NegativeOrder =
  646. GetRegIntValue( Locale,
  647. LOCALE_INEGCURR,
  648. NoUserOverride,
  649. FIELD_OFFSET(NLS_USER_INFO, iNegCurr),
  650. NLS_VALUE_INEGCURR,
  651. pHashN->pLocaleFixed->szINegCurr,
  652. 1,
  653. MAX_VALUE_INEGCURR );
  654. //
  655. // Get the grouping left of the decimal.
  656. //
  657. pFormat->Grouping =
  658. GetGroupingValue( Locale,
  659. LOCALE_SMONGROUPING,
  660. NoUserOverride,
  661. FIELD_OFFSET(NLS_USER_INFO, sMonGrouping),
  662. NLS_VALUE_SMONGROUPING,
  663. (LPWORD)(pHashN->pLocaleHdr) +
  664. pHashN->pLocaleHdr->SMonGrouping,
  665. 3 );
  666. //
  667. // Get the decimal separator.
  668. //
  669. // NOTE: This must follow the above calls because
  670. // pDecSep is used as a temporary buffer.
  671. //
  672. if ( (!NoUserOverride) &&
  673. GetUserInfo( Locale,
  674. LOCALE_SMONDECIMALSEP,
  675. FIELD_OFFSET(NLS_USER_INFO, sMonDecSep),
  676. NLS_VALUE_SMONDECIMALSEP,
  677. pDecimal,
  678. ARRAYSIZE(pDecimal),
  679. TRUE ) &&
  680. IsValidSeparatorString( pDecimal,
  681. MAX_SDECIMAL,
  682. FALSE ) )
  683. {
  684. pFormat->lpDecimalSep = pDecimal;
  685. }
  686. else
  687. {
  688. pFormat->lpDecimalSep = (LPWORD)(pHashN->pLocaleHdr) +
  689. pHashN->pLocaleHdr->SMonDecSep;
  690. }
  691. //
  692. // Get the thousand separator.
  693. // This string may be a null string.
  694. //
  695. if ( (!NoUserOverride) &&
  696. GetUserInfo( Locale,
  697. LOCALE_SMONTHOUSANDSEP,
  698. FIELD_OFFSET(NLS_USER_INFO, sMonThouSep),
  699. NLS_VALUE_SMONTHOUSANDSEP,
  700. pThousand,
  701. ARRAYSIZE(pThousand),
  702. FALSE ) &&
  703. IsValidSeparatorString( pThousand,
  704. MAX_STHOUSAND,
  705. FALSE ) )
  706. {
  707. pFormat->lpThousandSep = pThousand;
  708. }
  709. else
  710. {
  711. pFormat->lpThousandSep = (LPWORD)(pHashN->pLocaleHdr) +
  712. pHashN->pLocaleHdr->SMonThousSep;
  713. }
  714. //
  715. // Get the currency symbol.
  716. // This string may be a null string.
  717. //
  718. if ( (!NoUserOverride) &&
  719. GetUserInfo( Locale,
  720. LOCALE_SCURRENCY,
  721. FIELD_OFFSET(NLS_USER_INFO, sCurrency),
  722. NLS_VALUE_SCURRENCY,
  723. pCurrency,
  724. ARRAYSIZE(pCurrency),
  725. FALSE ) &&
  726. IsValidSeparatorString( pCurrency,
  727. MAX_SCURRENCY,
  728. FALSE ) )
  729. {
  730. pFormat->lpCurrencySymbol = pCurrency;
  731. }
  732. else
  733. {
  734. pFormat->lpCurrencySymbol = (LPWORD)(pHashN->pLocaleHdr) +
  735. pHashN->pLocaleHdr->SCurrency;
  736. }
  737. }
  738. //
  739. // Parse the currency format string.
  740. //
  741. pFinal = pString;
  742. Length = ParseCurrency( pHashN,
  743. NoUserOverride,
  744. (LPWSTR)lpValue,
  745. pFormat,
  746. &pFinal,
  747. MAX_NUMBER_BUFFER,
  748. &NeededSizeToAllocate,
  749. FALSE );
  750. //
  751. // If the failure is due to a stack variable size limitation, then
  752. // try to satisfy the request from the local process heap.
  753. //
  754. if ((Length == 0) && (NeededSizeToAllocate > 0))
  755. {
  756. pTemp = RtlAllocateHeap( RtlProcessHeap(),
  757. 0,
  758. NeededSizeToAllocate * sizeof(TCHAR) );
  759. if (pTemp)
  760. {
  761. pFinal = pTemp;
  762. Length = ParseCurrency( pHashN,
  763. NoUserOverride,
  764. (LPWSTR)lpValue,
  765. pFormat,
  766. &pFinal,
  767. NeededSizeToAllocate,
  768. &NeededSizeToAllocate,
  769. TRUE );
  770. }
  771. }
  772. //
  773. // Check cchCurrency for size of given buffer.
  774. //
  775. if ((cchCurrency == 0) || (Length == 0))
  776. {
  777. //
  778. // If cchCurrency is 0, then we can't use lpCurrencyStr. In this
  779. // case, we simply want to return the length (in characters) of
  780. // the string to be copied.
  781. //
  782. Length = Length;
  783. }
  784. else if (cchCurrency < Length)
  785. {
  786. //
  787. // The buffer is too small for the string, so return an error
  788. // and zero bytes written.
  789. //
  790. SetLastError(ERROR_INSUFFICIENT_BUFFER);
  791. Length = 0;
  792. }
  793. else
  794. {
  795. //
  796. // Copy the currency string to lpCurrencyStr and null terminate it.
  797. // Return the number of characters copied.
  798. //
  799. if(FAILED(StringCchCopyW(lpCurrencyStr, cchCurrency, pFinal)))
  800. {
  801. //
  802. // Failure should in theory be impossible, but if we ignore the
  803. // return value, PREfast will complain.
  804. //
  805. SetLastError(ERROR_OUTOFMEMORY);
  806. Length = 0;
  807. }
  808. }
  809. //
  810. // Free any dynamically allocated memory.
  811. //
  812. if (pTemp != NULL)
  813. {
  814. RtlFreeHeap(RtlProcessHeap(), 0, pTemp);
  815. }
  816. //
  817. // Return the number of characters copied.
  818. //
  819. return (Length);
  820. }
  821. //-------------------------------------------------------------------------//
  822. // INTERNAL ROUTINES //
  823. //-------------------------------------------------------------------------//
  824. ////////////////////////////////////////////////////////////////////////////
  825. //
  826. // IsValidNumberFormat
  827. //
  828. // Returns TRUE if the given format is valid. Otherwise, it returns FALSE.
  829. //
  830. // 07-28-93 JulieB Created.
  831. ////////////////////////////////////////////////////////////////////////////
  832. BOOL IsValidNumberFormat(
  833. CONST NUMBERFMTW *pFormat)
  834. {
  835. //
  836. // Check for invalid values.
  837. //
  838. if ((pFormat->NumDigits > MAX_VALUE_IDIGITS) ||
  839. (pFormat->LeadingZero > MAX_VALUE_ILZERO) ||
  840. (pFormat->Grouping > MAX_GROUPING_NUMBER) ||
  841. (pFormat->NegativeOrder > MAX_VALUE_INEGNUMBER) ||
  842. (pFormat->lpDecimalSep == NULL) ||
  843. (!IsValidSeparatorString( pFormat->lpDecimalSep,
  844. MAX_SDECIMAL,
  845. (pFormat->NumDigits) ? TRUE : FALSE)) ||
  846. (pFormat->lpThousandSep == NULL) ||
  847. (!IsValidSeparatorString( pFormat->lpThousandSep,
  848. MAX_STHOUSAND,
  849. FALSE )))
  850. {
  851. return (FALSE);
  852. }
  853. //
  854. // Return success.
  855. //
  856. return (TRUE);
  857. }
  858. ////////////////////////////////////////////////////////////////////////////
  859. //
  860. // IsValidCurrencyFormat
  861. //
  862. // Returns TRUE if the given format is valid. Otherwise, it returns FALSE.
  863. //
  864. // 07-28-93 JulieB Created.
  865. ////////////////////////////////////////////////////////////////////////////
  866. BOOL IsValidCurrencyFormat(
  867. CONST CURRENCYFMTW *pFormat)
  868. {
  869. //
  870. // Check for invalid values.
  871. //
  872. if ((pFormat->NumDigits > MAX_VALUE_IDIGITS) ||
  873. (pFormat->LeadingZero > MAX_VALUE_ILZERO) ||
  874. (pFormat->Grouping > MAX_GROUPING_NUMBER) ||
  875. (pFormat->lpDecimalSep == NULL) ||
  876. (!IsValidSeparatorString( pFormat->lpDecimalSep,
  877. MAX_SMONDECSEP,
  878. (pFormat->NumDigits) ? TRUE : FALSE)) ||
  879. (pFormat->lpThousandSep == NULL) ||
  880. (!IsValidSeparatorString( pFormat->lpThousandSep,
  881. MAX_SMONTHOUSEP,
  882. FALSE )) ||
  883. (pFormat->lpCurrencySymbol == NULL) ||
  884. (!IsValidSeparatorString( pFormat->lpCurrencySymbol,
  885. MAX_SCURRENCY,
  886. FALSE )) ||
  887. (pFormat->PositiveOrder > MAX_VALUE_ICURRENCY) ||
  888. (pFormat->NegativeOrder > MAX_VALUE_INEGCURR))
  889. {
  890. return (FALSE);
  891. }
  892. //
  893. // Return success.
  894. //
  895. return (TRUE);
  896. }
  897. ////////////////////////////////////////////////////////////////////////////
  898. //
  899. // GetRegIntValue
  900. //
  901. // Retrieves the specified locale information, converts the unicode string
  902. // to an integer value, and returns the value.
  903. //
  904. // 07-28-93 JulieB Created.
  905. ////////////////////////////////////////////////////////////////////////////
  906. UINT GetRegIntValue(
  907. LCID Locale,
  908. LCTYPE LCType,
  909. BOOL NoUserOverride,
  910. SIZE_T CacheOffset,
  911. LPWSTR pRegVal,
  912. LPWSTR pDefault,
  913. int DefaultVal,
  914. int UpperBound)
  915. {
  916. UNICODE_STRING ObUnicodeStr; // value string
  917. int Value; // value
  918. WCHAR pTemp[MAX_REG_VAL_SIZE]; // temp buffer
  919. //
  920. // Initialize values.
  921. //
  922. Value = -1;
  923. //
  924. // Try the user registry.
  925. //
  926. if ((!NoUserOverride) &&
  927. GetUserInfo( Locale,
  928. LCType,
  929. CacheOffset,
  930. pRegVal,
  931. pTemp,
  932. ARRAYSIZE(pTemp),
  933. TRUE ))
  934. {
  935. //
  936. // Convert the user data to an integer.
  937. //
  938. RtlInitUnicodeString(&ObUnicodeStr, pTemp);
  939. if ((RtlUnicodeStringToInteger(&ObUnicodeStr, 10, &Value)) ||
  940. (Value < 0) || (Value > UpperBound))
  941. {
  942. //
  943. // Bad value, so store -1 so that the system default
  944. // will be used.
  945. //
  946. Value = -1;
  947. }
  948. }
  949. //
  950. // See if the value obtained above is valid.
  951. //
  952. if (Value < 0)
  953. {
  954. //
  955. // Convert system default data to an integer.
  956. //
  957. RtlInitUnicodeString(&ObUnicodeStr, pDefault);
  958. if ((RtlUnicodeStringToInteger(&ObUnicodeStr, 10, &Value)) ||
  959. (Value < 0) || (Value > UpperBound))
  960. {
  961. //
  962. // Bad value, so use the chosen default value.
  963. //
  964. Value = DefaultVal;
  965. }
  966. }
  967. return ((UINT)Value);
  968. }
  969. ////////////////////////////////////////////////////////////////////////////
  970. //
  971. // ConvertGroupingStringToInt
  972. //
  973. // Converts the given grouping string to an integer.
  974. // For example, 3;2;0 becomes 32 and 3;0 becomes 3 and 3;2 becomes 320.
  975. //
  976. // NOTE: The pGrouping buffer will be modified.
  977. //
  978. // 01-05-98 JulieB Created.
  979. ////////////////////////////////////////////////////////////////////////////
  980. int ConvertGroupingStringToInt(
  981. LPWSTR pGroupingSrc,
  982. LPWSTR pGroupingDest)
  983. {
  984. LPWSTR pSrc = pGroupingSrc; // temp ptr to src position
  985. LPWSTR pDest = pGroupingDest; // temp ptr to dest position
  986. UNICODE_STRING ObUnicodeStr; // value string
  987. int Value; // value
  988. //
  989. // Filter out all non-numeric values and all zero values.
  990. // Store the result in the destination buffer.
  991. //
  992. while (*pSrc)
  993. {
  994. if ((*pSrc < NLS_CHAR_ONE) || (*pSrc > NLS_CHAR_NINE))
  995. {
  996. pSrc++;
  997. }
  998. else
  999. {
  1000. if (pSrc != pDest)
  1001. {
  1002. *pDest = *pSrc;
  1003. }
  1004. pSrc++;
  1005. pDest++;
  1006. }
  1007. }
  1008. //
  1009. // Make sure there is something in the destination buffer.
  1010. // Also, see if we need to add a zero in the case of 3;2 becomes 320.
  1011. //
  1012. if ((pDest == pGroupingDest) || (*(pSrc - 1) != NLS_CHAR_ZERO))
  1013. {
  1014. *pDest = NLS_CHAR_ZERO;
  1015. pDest++;
  1016. }
  1017. //
  1018. // Null terminate the buffer.
  1019. //
  1020. *pDest = 0;
  1021. //
  1022. // Convert the string to an integer.
  1023. //
  1024. RtlInitUnicodeString(&ObUnicodeStr, pGroupingDest);
  1025. RtlUnicodeStringToInteger(&ObUnicodeStr, 10, &Value);
  1026. //
  1027. // Return the integer value.
  1028. //
  1029. return (Value);
  1030. }
  1031. ////////////////////////////////////////////////////////////////////////////
  1032. //
  1033. // GetGroupingValue
  1034. //
  1035. // Retrieves the specified grouping information, converts the grouping
  1036. // string to an integer value (eg. 3;2;0 -> 32), and returns the value.
  1037. //
  1038. // 07-28-93 JulieB Created.
  1039. ////////////////////////////////////////////////////////////////////////////
  1040. UINT GetGroupingValue(
  1041. LCID Locale,
  1042. LCTYPE LCType,
  1043. BOOL NoUserOverride,
  1044. SIZE_T CacheOffset,
  1045. LPWSTR pRegVal,
  1046. LPWSTR pDefault,
  1047. int DefaultVal)
  1048. {
  1049. int Value; // value
  1050. WCHAR pTemp[MAX_REG_VAL_SIZE]; // temp buffer
  1051. //
  1052. // Initialize values.
  1053. //
  1054. Value = -1;
  1055. //
  1056. // Try the user registry.
  1057. //
  1058. if ((!NoUserOverride) &&
  1059. GetUserInfo( Locale,
  1060. LCType,
  1061. CacheOffset,
  1062. pRegVal,
  1063. pTemp,
  1064. ARRAYSIZE(pTemp),
  1065. TRUE ))
  1066. {
  1067. //
  1068. // Convert the grouping string to an integer.
  1069. // 3;0 becomes 3, 3;2;0 becomes 32, and 3;2 becomes 320.
  1070. //
  1071. Value = ConvertGroupingStringToInt(pTemp, pTemp);
  1072. if (Value < 0)
  1073. {
  1074. //
  1075. // Bad value, so store -1 so that the system default
  1076. // will be used.
  1077. //
  1078. Value = -1;
  1079. }
  1080. }
  1081. //
  1082. // See if the value obtained above is valid.
  1083. //
  1084. if (Value < 0)
  1085. {
  1086. //
  1087. // Convert the grouping string to an integer.
  1088. // 3;0 becomes 3, 3;2;0 becomes 32, and 3;2 becomes 320.
  1089. //
  1090. Value = ConvertGroupingStringToInt(pDefault, pTemp);
  1091. if (Value < 0)
  1092. {
  1093. //
  1094. // Bad value, so use the chosen default value.
  1095. //
  1096. Value = DefaultVal;
  1097. }
  1098. }
  1099. //
  1100. // Return the value.
  1101. //
  1102. return ((UINT)Value);
  1103. }
  1104. ////////////////////////////////////////////////////////////////////////////
  1105. //
  1106. // GetNumberString
  1107. //
  1108. // Puts the properly formatted number string into the given string buffer.
  1109. // It returns the number of characters written to the string buffer.
  1110. //
  1111. // 07-28-93 JulieB Created.
  1112. ////////////////////////////////////////////////////////////////////////////
  1113. int GetNumberString(
  1114. PLOC_HASH pHashN,
  1115. LPWSTR pValue,
  1116. LPNUMBERFMTW pFormat,
  1117. LPWSTR *ppBuf,
  1118. int BufSize,
  1119. BOOL *pfZeroValue,
  1120. int *pNeededSizeToAllocate,
  1121. BOOL fSetError)
  1122. {
  1123. LPWSTR pDecPt; // ptr to decimal point in given buffer
  1124. LPWSTR pPos; // ptr to position in given buffer
  1125. LPWSTR pPos2; // ptr to position in given buffer
  1126. LPWSTR pPosBuf; // ptr to position in final buffer
  1127. int IntPartSize; // size of integer part of string
  1128. int GroupSize; // size of groupings left of decimal
  1129. int IntegerNum; // number of integers left of decimal
  1130. WCHAR wch; // wide character place holder
  1131. int pGroupArray[MAX_GROUPS]; // array of groups
  1132. int NumGroupings; // number of groupings
  1133. int NumSeparators; // number of separators
  1134. int NumDigits; // number of digits
  1135. int Ctr; // loop counter
  1136. UINT NumRound = 1; // # digits left before adding group separator
  1137. //
  1138. // Reset to indicate no need to allocate memory dynamically.
  1139. //
  1140. *pNeededSizeToAllocate = 0;
  1141. //
  1142. // Validate the string and find the decimal point in the string.
  1143. //
  1144. // The only valid characters within the string are:
  1145. // negative sign - in first position only
  1146. // decimal point
  1147. // Unicode code points for integers 0 - 9
  1148. //
  1149. pPos = pValue;
  1150. while ((wch = *pPos) && (wch != NLS_CHAR_PERIOD))
  1151. {
  1152. if ((wch < NLS_CHAR_ZERO) || (wch > NLS_CHAR_NINE))
  1153. {
  1154. SetLastError(ERROR_INVALID_PARAMETER);
  1155. return (0);
  1156. }
  1157. pPos++;
  1158. }
  1159. pDecPt = pPos;
  1160. if (*pPos)
  1161. {
  1162. pPos++;
  1163. while (wch = *pPos)
  1164. {
  1165. if ((wch < NLS_CHAR_ZERO) || (wch > NLS_CHAR_NINE))
  1166. {
  1167. SetLastError(ERROR_INVALID_PARAMETER);
  1168. return (0);
  1169. }
  1170. pPos++;
  1171. }
  1172. }
  1173. //
  1174. // Remove any leading zeros in the integer part.
  1175. //
  1176. while (pValue < pDecPt)
  1177. {
  1178. if (*pValue != NLS_CHAR_ZERO)
  1179. {
  1180. break;
  1181. }
  1182. pValue++;
  1183. }
  1184. //
  1185. // Save the number of integers to the left of the decimal.
  1186. //
  1187. IntegerNum = (int)(pDecPt - pValue);
  1188. //
  1189. // Make sure the value string passed in is not too large for
  1190. // the buffers.
  1191. //
  1192. IntPartSize = IntegerNum;
  1193. NumGroupings = 0;
  1194. NumSeparators = 0;
  1195. if ((GroupSize = pFormat->Grouping) && (IntPartSize))
  1196. {
  1197. //
  1198. // Count the number of groupings and save them in an array to be
  1199. // used later.
  1200. //
  1201. while (GroupSize && (NumGroupings < MAX_GROUPS))
  1202. {
  1203. pGroupArray[NumGroupings] = GroupSize % 10;
  1204. GroupSize /= 10;
  1205. NumGroupings++;
  1206. }
  1207. //
  1208. // Count the number of groupings that apply to the given number
  1209. // string.
  1210. //
  1211. NumDigits = IntegerNum;
  1212. Ctr = (NumGroupings != 0) ? (NumGroupings - 1) : 0;
  1213. while (Ctr)
  1214. {
  1215. if (NumDigits > pGroupArray[Ctr])
  1216. {
  1217. NumDigits -= pGroupArray[Ctr];
  1218. NumSeparators++;
  1219. }
  1220. else
  1221. {
  1222. if (NumDigits == pGroupArray[Ctr])
  1223. {
  1224. NumRound = 0;
  1225. }
  1226. break;
  1227. }
  1228. Ctr--;
  1229. }
  1230. if ((Ctr == 0) && pGroupArray[0])
  1231. {
  1232. if (NumDigits > pGroupArray[0])
  1233. {
  1234. NumSeparators += (NumDigits - 1) / pGroupArray[0];
  1235. }
  1236. NumRound = NumDigits % pGroupArray[0];
  1237. }
  1238. IntPartSize += MAX_STHOUSAND * NumSeparators;
  1239. }
  1240. //
  1241. // Make sure the buffer is large enough. If not, return the size
  1242. // needed.
  1243. //
  1244. if (IntPartSize > (BufSize - MAX_NON_INTEGER_PART))
  1245. {
  1246. if (fSetError)
  1247. {
  1248. SetLastError(ERROR_INVALID_PARAMETER);
  1249. }
  1250. *pNeededSizeToAllocate = (IntPartSize + MAX_NON_INTEGER_PART);
  1251. return (0);
  1252. }
  1253. //
  1254. // Initialize pointers.
  1255. //
  1256. pPosBuf = *ppBuf;
  1257. pPos = pValue;
  1258. *pfZeroValue = FALSE;
  1259. //
  1260. // See if there are any digits before the decimal point.
  1261. //
  1262. if (pPos == pDecPt)
  1263. {
  1264. //
  1265. // Possibly a zero value. All leading zeros were removed, so
  1266. // there is no integer part.
  1267. //
  1268. *pfZeroValue = TRUE;
  1269. //
  1270. // No digits before decimal point, so add a leading zero
  1271. // to the final string if appropriate.
  1272. //
  1273. if (pFormat->LeadingZero)
  1274. {
  1275. *pPosBuf = NLS_CHAR_ZERO;
  1276. pPosBuf++;
  1277. }
  1278. }
  1279. else if (!NumSeparators)
  1280. {
  1281. //
  1282. // Grouping Size is zero or larger than the integer part of the
  1283. // string, so copy up to the decimal point (or end of string).
  1284. //
  1285. while (pPos < pDecPt)
  1286. {
  1287. *pPosBuf = *pPos;
  1288. pPosBuf++;
  1289. pPos++;
  1290. }
  1291. }
  1292. else
  1293. {
  1294. //
  1295. // Copy up to where the first thousand separator should be.
  1296. // Use groupings of GroupSize numbers up to the decimal point.
  1297. //
  1298. NumDigits = IntegerNum;
  1299. Ctr = (NumGroupings != 0) ? (NumGroupings - 1) : 0;
  1300. while (Ctr)
  1301. {
  1302. if (NumDigits > pGroupArray[Ctr])
  1303. {
  1304. NumDigits -= pGroupArray[Ctr];
  1305. }
  1306. else
  1307. {
  1308. break;
  1309. }
  1310. Ctr--;
  1311. }
  1312. GroupSize = pGroupArray[Ctr];
  1313. pPos2 = GroupSize
  1314. ? (pPos + (NumDigits % GroupSize))
  1315. : (pPos + NumDigits);
  1316. if (pPos2 == pPos)
  1317. {
  1318. //
  1319. // Don't want to write thousand separator at the beginning
  1320. // of the string. There's at least GroupSize numbers
  1321. // in the string, so just advance pPos2 so that GroupSize
  1322. // numbers will be copied.
  1323. //
  1324. pPos2 = pPos + GroupSize;
  1325. }
  1326. while (pPos < pPos2)
  1327. {
  1328. *pPosBuf = *pPos;
  1329. pPosBuf++;
  1330. pPos++;
  1331. NumDigits--;
  1332. }
  1333. //
  1334. // Copy the thousand separator followed by GroupSize number of
  1335. // digits from the given string until the entire repeating
  1336. // GroupSize ends (or end of string).
  1337. //
  1338. while (NumDigits)
  1339. {
  1340. //
  1341. // Copy the localized thousand separator.
  1342. //
  1343. pPos2 = pFormat->lpThousandSep;
  1344. while (*pPos2)
  1345. {
  1346. *pPosBuf = *pPos2;
  1347. pPosBuf++;
  1348. pPos2++;
  1349. }
  1350. //
  1351. // Copy GroupSize number of digits.
  1352. //
  1353. pPos2 = pPos + GroupSize;
  1354. while (pPos < pPos2)
  1355. {
  1356. *pPosBuf = *pPos;
  1357. pPosBuf++;
  1358. pPos++;
  1359. NumDigits--;
  1360. }
  1361. }
  1362. //
  1363. // Copy the thousand separator followed by GroupSize number of
  1364. // digits from the given string - until the decimal point (or
  1365. // end of string) in the given string is reached.
  1366. //
  1367. if (pPos < pDecPt)
  1368. {
  1369. Ctr++;
  1370. while (Ctr < NumGroupings)
  1371. {
  1372. //
  1373. // Copy the localized thousand separator.
  1374. //
  1375. pPos2 = pFormat->lpThousandSep;
  1376. while (*pPos2)
  1377. {
  1378. *pPosBuf = *pPos2;
  1379. pPosBuf++;
  1380. pPos2++;
  1381. }
  1382. //
  1383. // Copy GroupSize number of digits.
  1384. //
  1385. pPos2 = pPos + pGroupArray[Ctr];
  1386. while (pPos < pPos2)
  1387. {
  1388. *pPosBuf = *pPos;
  1389. pPosBuf++;
  1390. pPos++;
  1391. }
  1392. //
  1393. // Go to the next grouping.
  1394. //
  1395. Ctr++;
  1396. }
  1397. }
  1398. }
  1399. //
  1400. // See if there is a decimal separator in the given string.
  1401. //
  1402. if (pFormat->NumDigits > 0)
  1403. {
  1404. //
  1405. // Copy the localized decimal separator only if the number
  1406. // of digits right of the decimal is greater than zero.
  1407. //
  1408. pDecPt = pPosBuf;
  1409. pPos2 = pFormat->lpDecimalSep;
  1410. while (*pPos2)
  1411. {
  1412. *pPosBuf = *pPos2;
  1413. pPosBuf++;
  1414. pPos2++;
  1415. }
  1416. }
  1417. //
  1418. // Skip over the decimal point in the given string and
  1419. // copy the rest of the digits from the given string.
  1420. //
  1421. if (*pPos)
  1422. {
  1423. pPos++;
  1424. }
  1425. pPos2 = pPos + pFormat->NumDigits;
  1426. while ((*pPos) && (pPos < pPos2))
  1427. {
  1428. if (*pPos != NLS_CHAR_ZERO)
  1429. {
  1430. *pfZeroValue = FALSE;
  1431. }
  1432. *pPosBuf = *pPos;
  1433. pPosBuf++;
  1434. pPos++;
  1435. }
  1436. //
  1437. // Make sure some value is in the buffer.
  1438. //
  1439. if (*ppBuf == pPosBuf)
  1440. {
  1441. *pPosBuf = NLS_CHAR_ZERO;
  1442. pPosBuf++;
  1443. }
  1444. //
  1445. // See if we need to round the number or pad it with zeros.
  1446. //
  1447. if (*pPos)
  1448. {
  1449. //
  1450. // Round the number if necessary.
  1451. //
  1452. if (*pPos2 > L'4')
  1453. {
  1454. *pfZeroValue = FALSE;
  1455. //
  1456. // Round the number. If GroupSize is 0, then we need to
  1457. // pass in a non-zero value so that the thousand separator
  1458. // will not be added to the front of the string (if it
  1459. // rounds that far).
  1460. //
  1461. pPosBuf--;
  1462. NLS_ROUND_IT( *ppBuf,
  1463. pPosBuf,
  1464. NumRound,
  1465. pFormat->lpThousandSep );
  1466. pPosBuf++;
  1467. }
  1468. }
  1469. else
  1470. {
  1471. //
  1472. // Pad the string with the appropriate number of zeros.
  1473. //
  1474. while (pPos < pPos2)
  1475. {
  1476. *pPosBuf = NLS_CHAR_ZERO;
  1477. pPosBuf++;
  1478. pPos++;
  1479. }
  1480. }
  1481. //
  1482. // Zero terminate the string.
  1483. //
  1484. *pPosBuf = 0;
  1485. //
  1486. // Return the number of characters written to the buffer, including
  1487. // the null terminator.
  1488. //
  1489. return ((int)((pPosBuf - *ppBuf) + 1));
  1490. }
  1491. ////////////////////////////////////////////////////////////////////////////
  1492. //
  1493. // ParseNumber
  1494. //
  1495. // Puts the properly formatted number string into the given string buffer.
  1496. // It returns the number of characters written to the string buffer.
  1497. //
  1498. // 07-28-93 JulieB Created.
  1499. ////////////////////////////////////////////////////////////////////////////
  1500. int ParseNumber(
  1501. PLOC_HASH pHashN,
  1502. BOOL NoUserOverride,
  1503. LPWSTR pValue,
  1504. LPNUMBERFMTW pFormat,
  1505. LPWSTR *ppBuf,
  1506. int BufSize,
  1507. int *pNeededSizeToAllocate,
  1508. BOOL fSetError)
  1509. {
  1510. LPWSTR pBegin; // ptr to beginning of final buffer
  1511. LPWSTR pEnd; // ptr to end of final buffer
  1512. LPWSTR pNegSign; // ptr to negative sign string
  1513. BOOL IsNeg; // if negative sign in string
  1514. int Length; // length of number string
  1515. BOOL fZeroValue = FALSE; // if number is a zero value
  1516. int NegSignSize; // size of negative sign string
  1517. WCHAR pTemp[MAX_REG_VAL_SIZE]; // temp buffer
  1518. //
  1519. // Initialize pointer.
  1520. //
  1521. // Account for:
  1522. // - negative sign
  1523. // - blank spaces
  1524. // - one extra number from rounding
  1525. // - one extra grouping separator from rounding
  1526. //
  1527. pBegin = *ppBuf + MAX_NUMBER_EXTRAS;
  1528. //
  1529. // If the first value is a negative, then increment past it.
  1530. //
  1531. if (IsNeg = (*pValue == NLS_CHAR_HYPHEN))
  1532. {
  1533. pValue++;
  1534. }
  1535. //
  1536. // Get the appropriate number string and place it in the buffer.
  1537. //
  1538. Length = GetNumberString( pHashN,
  1539. pValue,
  1540. pFormat,
  1541. &pBegin,
  1542. BufSize - MAX_NUMBER_EXTRAS,
  1543. &fZeroValue,
  1544. pNeededSizeToAllocate,
  1545. fSetError );
  1546. if (!Length)
  1547. {
  1548. if (*pNeededSizeToAllocate > 0)
  1549. {
  1550. *pNeededSizeToAllocate += MAX_NUMBER_EXTRAS;
  1551. }
  1552. return (0);
  1553. }
  1554. //
  1555. // Advance pEnd position pointer to the end of the number string.
  1556. //
  1557. pEnd = pBegin + (Length - 1);
  1558. //
  1559. // See if any characters should be put in the buffer BEFORE and
  1560. // AFTER the properly formatted number string.
  1561. // - negative sign or opening/closing parenthesis
  1562. // - blank space
  1563. //
  1564. if (!fZeroValue && IsNeg)
  1565. {
  1566. //
  1567. // Get the negative sign string.
  1568. //
  1569. if (pFormat->NegativeOrder != 0)
  1570. {
  1571. if ( (!NoUserOverride) &&
  1572. GetUserInfo( pHashN->Locale,
  1573. LOCALE_SNEGATIVESIGN,
  1574. FIELD_OFFSET(NLS_USER_INFO, sNegSign),
  1575. NLS_VALUE_SNEGATIVESIGN,
  1576. pTemp,
  1577. ARRAYSIZE(pTemp),
  1578. TRUE ) &&
  1579. IsValidSeparatorString( pTemp,
  1580. MAX_SNEGSIGN,
  1581. FALSE ) )
  1582. {
  1583. pNegSign = pTemp;
  1584. }
  1585. else
  1586. {
  1587. pNegSign = (LPWORD)(pHashN->pLocaleHdr) +
  1588. pHashN->pLocaleHdr->SNegativeSign;
  1589. }
  1590. }
  1591. switch (pFormat->NegativeOrder)
  1592. {
  1593. case ( 0 ) :
  1594. {
  1595. //
  1596. // Put the opening parenthesis in the buffer.
  1597. //
  1598. pBegin--;
  1599. *pBegin = NLS_CHAR_OPEN_PAREN;
  1600. //
  1601. // Put the closing parenthesis in the buffer.
  1602. //
  1603. *pEnd = NLS_CHAR_CLOSE_PAREN;
  1604. pEnd++;
  1605. break;
  1606. }
  1607. case ( 2 ) :
  1608. {
  1609. //
  1610. // Put the space in the buffer.
  1611. //
  1612. pBegin--;
  1613. *pBegin = NLS_CHAR_SPACE;
  1614. //
  1615. // Fall through to case 1.
  1616. //
  1617. }
  1618. case ( 1 ) :
  1619. default :
  1620. {
  1621. //
  1622. // Copy the negative sign to the buffer.
  1623. //
  1624. NegSignSize = NlsStrLenW(pNegSign);
  1625. pBegin -= NegSignSize;
  1626. NLS_COPY_UNICODE_STR_TMP(pBegin, pNegSign);
  1627. break;
  1628. }
  1629. case ( 4 ) :
  1630. {
  1631. //
  1632. // Put the space in the buffer.
  1633. //
  1634. *pEnd = NLS_CHAR_SPACE;
  1635. pEnd++;
  1636. //
  1637. // Fall Through to case 3.
  1638. //
  1639. }
  1640. case ( 3 ) :
  1641. {
  1642. //
  1643. // Copy the negative sign to the buffer.
  1644. //
  1645. NLS_COPY_UNICODE_STR(pEnd, pNegSign);
  1646. break;
  1647. }
  1648. }
  1649. }
  1650. //
  1651. // Zero terminate the string.
  1652. //
  1653. *pEnd = 0;
  1654. //
  1655. // Return the pointer to the beginning of the string.
  1656. //
  1657. *ppBuf = pBegin;
  1658. //
  1659. // Return the number of characters written to the buffer, including
  1660. // the null terminator.
  1661. //
  1662. return ((int)((pEnd - pBegin) + 1));
  1663. }
  1664. ////////////////////////////////////////////////////////////////////////////
  1665. //
  1666. // ParseCurrency
  1667. //
  1668. // Puts the properly formatted currency string into the given string buffer.
  1669. // It returns the number of characters written to the string buffer.
  1670. //
  1671. // 07-28-93 JulieB Created.
  1672. ////////////////////////////////////////////////////////////////////////////
  1673. int ParseCurrency(
  1674. PLOC_HASH pHashN,
  1675. BOOL NoUserOverride,
  1676. LPWSTR pValue,
  1677. LPCURRENCYFMTW pFormat,
  1678. LPWSTR *ppBuf,
  1679. int BufSize,
  1680. int *pNeededSizeToAllocate,
  1681. BOOL fSetError)
  1682. {
  1683. LPWSTR pBegin; // ptr to beginning of final buffer
  1684. LPWSTR pEnd; // ptr to end of final buffer
  1685. LPWSTR pNegSign; // ptr to negative sign string
  1686. BOOL IsNeg; // if negative sign in string
  1687. int Length; // length of number string
  1688. BOOL fZeroValue = FALSE; // if number is a zero value
  1689. int NegSignSize; // size of negative sign string
  1690. UINT NegOrder; // negative ordering
  1691. int CurrSymSize; // size of currency symbol
  1692. WCHAR pTemp[MAX_REG_VAL_SIZE]; // temp buffer
  1693. //
  1694. // Initialize pointer.
  1695. //
  1696. // Account for:
  1697. // - negative sign
  1698. // - currency sign
  1699. // - blank spaces
  1700. // - one extra number from rounding
  1701. // - one extra grouping separator from rounding
  1702. //
  1703. pBegin = *ppBuf + MAX_CURRENCY_EXTRAS;
  1704. //
  1705. // If the first value is a negative, then increment past it.
  1706. //
  1707. if (IsNeg = (*pValue == NLS_CHAR_HYPHEN))
  1708. {
  1709. pValue++;
  1710. }
  1711. //
  1712. // Get the appropriate number string and place it in the buffer.
  1713. //
  1714. Length = GetNumberString( pHashN,
  1715. pValue,
  1716. (LPNUMBERFMTW)pFormat,
  1717. &pBegin,
  1718. BufSize - MAX_CURRENCY_EXTRAS,
  1719. &fZeroValue,
  1720. pNeededSizeToAllocate,
  1721. fSetError );
  1722. if (!Length)
  1723. {
  1724. if (*pNeededSizeToAllocate > 0)
  1725. {
  1726. *pNeededSizeToAllocate += MAX_CURRENCY_EXTRAS;
  1727. }
  1728. return (0);
  1729. }
  1730. //
  1731. // Advance pEnd position pointer to the end of the number string.
  1732. //
  1733. pEnd = pBegin + (Length - 1);
  1734. //
  1735. // Get the size of the currency symbol.
  1736. //
  1737. CurrSymSize = NlsStrLenW(pFormat->lpCurrencySymbol);
  1738. //
  1739. // See if any characters should be put in the buffer BEFORE and
  1740. // AFTER the properly formatted number string.
  1741. // - currency symbol
  1742. // - negative sign or opening/closing parenthesis
  1743. // - blank space
  1744. //
  1745. if (!fZeroValue && IsNeg)
  1746. {
  1747. //
  1748. // Get the negative sign string and the size of it.
  1749. //
  1750. NegOrder = pFormat->NegativeOrder;
  1751. if ((NegOrder != 0) && (NegOrder != 4) && (NegOrder < 14))
  1752. {
  1753. if ( (!NoUserOverride) &&
  1754. GetUserInfo( pHashN->Locale,
  1755. LOCALE_SNEGATIVESIGN,
  1756. FIELD_OFFSET(NLS_USER_INFO, sNegSign),
  1757. NLS_VALUE_SNEGATIVESIGN,
  1758. pTemp,
  1759. ARRAYSIZE(pTemp),
  1760. TRUE ) &&
  1761. IsValidSeparatorString( pTemp,
  1762. MAX_SNEGSIGN,
  1763. FALSE ) )
  1764. {
  1765. pNegSign = pTemp;
  1766. }
  1767. else
  1768. {
  1769. pNegSign = (LPWORD)(pHashN->pLocaleHdr) +
  1770. pHashN->pLocaleHdr->SNegativeSign;
  1771. }
  1772. NegSignSize = NlsStrLenW(pNegSign);
  1773. }
  1774. switch (NegOrder)
  1775. {
  1776. case ( 0 ) :
  1777. {
  1778. //
  1779. // Copy the currency symbol to the buffer.
  1780. //
  1781. pBegin -= CurrSymSize;
  1782. NLS_COPY_UNICODE_STR_TMP(pBegin, pFormat->lpCurrencySymbol);
  1783. //
  1784. // Put the opening parenthesis in the buffer.
  1785. //
  1786. pBegin--;
  1787. *pBegin = NLS_CHAR_OPEN_PAREN;
  1788. //
  1789. // Put the closing parenthesis in the buffer.
  1790. //
  1791. *pEnd = NLS_CHAR_CLOSE_PAREN;
  1792. pEnd++;
  1793. break;
  1794. }
  1795. case ( 1 ) :
  1796. default :
  1797. {
  1798. //
  1799. // Copy the currency symbol to the buffer.
  1800. //
  1801. pBegin -= CurrSymSize;
  1802. NLS_COPY_UNICODE_STR_TMP(pBegin, pFormat->lpCurrencySymbol);
  1803. //
  1804. // Copy the negative sign to the buffer.
  1805. //
  1806. pBegin -= NegSignSize;
  1807. NLS_COPY_UNICODE_STR_TMP(pBegin, pNegSign);
  1808. break;
  1809. }
  1810. case ( 2 ) :
  1811. {
  1812. //
  1813. // Copy the negative sign to the buffer.
  1814. //
  1815. pBegin -= NegSignSize;
  1816. NLS_COPY_UNICODE_STR_TMP(pBegin, pNegSign);
  1817. //
  1818. // Copy the currency symbol to the buffer.
  1819. //
  1820. pBegin -= CurrSymSize;
  1821. NLS_COPY_UNICODE_STR_TMP(pBegin, pFormat->lpCurrencySymbol);
  1822. break;
  1823. }
  1824. case ( 3 ) :
  1825. {
  1826. //
  1827. // Copy the currency symbol to the buffer.
  1828. //
  1829. pBegin -= CurrSymSize;
  1830. NLS_COPY_UNICODE_STR_TMP(pBegin, pFormat->lpCurrencySymbol);
  1831. //
  1832. // Copy the negative sign to the buffer.
  1833. //
  1834. NLS_COPY_UNICODE_STR(pEnd, pNegSign);
  1835. break;
  1836. }
  1837. case ( 4 ) :
  1838. {
  1839. //
  1840. // Put the opening parenthesis in the buffer.
  1841. //
  1842. pBegin--;
  1843. *pBegin = NLS_CHAR_OPEN_PAREN;
  1844. //
  1845. // Copy the currency symbol to the buffer.
  1846. //
  1847. NLS_COPY_UNICODE_STR(pEnd, pFormat->lpCurrencySymbol);
  1848. //
  1849. // Put the closing parenthesis in the buffer.
  1850. //
  1851. *pEnd = NLS_CHAR_CLOSE_PAREN;
  1852. pEnd++;
  1853. break;
  1854. }
  1855. case ( 5 ) :
  1856. {
  1857. //
  1858. // Copy the negative sign to the buffer.
  1859. //
  1860. pBegin -= NegSignSize;
  1861. NLS_COPY_UNICODE_STR_TMP(pBegin, pNegSign);
  1862. //
  1863. // Copy the currency symbol to the buffer.
  1864. //
  1865. NLS_COPY_UNICODE_STR(pEnd, pFormat->lpCurrencySymbol);
  1866. break;
  1867. }
  1868. case ( 6 ) :
  1869. {
  1870. //
  1871. // Copy the negative sign to the buffer.
  1872. //
  1873. NLS_COPY_UNICODE_STR(pEnd, pNegSign);
  1874. //
  1875. // Copy the currency symbol to the buffer.
  1876. //
  1877. NLS_COPY_UNICODE_STR(pEnd, pFormat->lpCurrencySymbol);
  1878. break;
  1879. }
  1880. case ( 7 ) :
  1881. {
  1882. //
  1883. // Copy the currency symbol to the buffer.
  1884. //
  1885. NLS_COPY_UNICODE_STR(pEnd, pFormat->lpCurrencySymbol);
  1886. //
  1887. // Copy the negative sign to the buffer.
  1888. //
  1889. NLS_COPY_UNICODE_STR(pEnd, pNegSign);
  1890. break;
  1891. }
  1892. case ( 8 ) :
  1893. {
  1894. //
  1895. // Copy the negative sign to the buffer.
  1896. //
  1897. pBegin -= NegSignSize;
  1898. NLS_COPY_UNICODE_STR_TMP(pBegin, pNegSign);
  1899. //
  1900. // Put a space in the buffer.
  1901. //
  1902. *pEnd = NLS_CHAR_SPACE;
  1903. pEnd++;
  1904. //
  1905. // Copy the currency symbol to the buffer.
  1906. //
  1907. NLS_COPY_UNICODE_STR(pEnd, pFormat->lpCurrencySymbol);
  1908. break;
  1909. }
  1910. case ( 9 ) :
  1911. {
  1912. //
  1913. // Put a space in the buffer.
  1914. //
  1915. pBegin--;
  1916. *pBegin = NLS_CHAR_SPACE;
  1917. //
  1918. // Copy the currency symbol to the buffer.
  1919. //
  1920. pBegin -= CurrSymSize;
  1921. NLS_COPY_UNICODE_STR_TMP(pBegin, pFormat->lpCurrencySymbol);
  1922. //
  1923. // Copy the negative sign to the buffer.
  1924. //
  1925. pBegin -= NegSignSize;
  1926. NLS_COPY_UNICODE_STR_TMP(pBegin, pNegSign);
  1927. break;
  1928. }
  1929. case ( 10 ) :
  1930. {
  1931. //
  1932. // Put a space in the buffer.
  1933. //
  1934. *pEnd = NLS_CHAR_SPACE;
  1935. pEnd++;
  1936. //
  1937. // Copy the currency symbol to the buffer.
  1938. //
  1939. NLS_COPY_UNICODE_STR(pEnd, pFormat->lpCurrencySymbol);
  1940. //
  1941. // Copy the negative sign to the buffer.
  1942. //
  1943. NLS_COPY_UNICODE_STR(pEnd, pNegSign);
  1944. break;
  1945. }
  1946. case ( 11 ) :
  1947. {
  1948. //
  1949. // Put a space in the buffer.
  1950. //
  1951. pBegin--;
  1952. *pBegin = NLS_CHAR_SPACE;
  1953. //
  1954. // Copy the currency symbol to the buffer.
  1955. //
  1956. pBegin -= CurrSymSize;
  1957. NLS_COPY_UNICODE_STR_TMP(pBegin, pFormat->lpCurrencySymbol);
  1958. //
  1959. // Copy the negative sign to the buffer.
  1960. //
  1961. NLS_COPY_UNICODE_STR(pEnd, pNegSign);
  1962. break;
  1963. }
  1964. case ( 12 ) :
  1965. {
  1966. //
  1967. // Copy the negative sign to the buffer.
  1968. //
  1969. pBegin -= NegSignSize;
  1970. NLS_COPY_UNICODE_STR_TMP(pBegin, pNegSign);
  1971. //
  1972. // Put a space in the buffer.
  1973. //
  1974. pBegin--;
  1975. *pBegin = NLS_CHAR_SPACE;
  1976. //
  1977. // Copy the currency symbol to the buffer.
  1978. //
  1979. pBegin -= CurrSymSize;
  1980. NLS_COPY_UNICODE_STR_TMP(pBegin, pFormat->lpCurrencySymbol);
  1981. break;
  1982. }
  1983. case ( 13 ) :
  1984. {
  1985. //
  1986. // Copy the negative sign to the buffer.
  1987. //
  1988. NLS_COPY_UNICODE_STR(pEnd, pNegSign);
  1989. //
  1990. // Put a space in the buffer.
  1991. //
  1992. *pEnd = NLS_CHAR_SPACE;
  1993. pEnd++;
  1994. //
  1995. // Copy the currency symbol to the buffer.
  1996. //
  1997. NLS_COPY_UNICODE_STR(pEnd, pFormat->lpCurrencySymbol);
  1998. break;
  1999. }
  2000. case ( 14 ) :
  2001. {
  2002. //
  2003. // Put a space in the buffer.
  2004. //
  2005. pBegin--;
  2006. *pBegin = NLS_CHAR_SPACE;
  2007. //
  2008. // Copy the currency symbol to the buffer.
  2009. //
  2010. pBegin -= CurrSymSize;
  2011. NLS_COPY_UNICODE_STR_TMP(pBegin, pFormat->lpCurrencySymbol);
  2012. //
  2013. // Put the opening parenthesis in the buffer.
  2014. //
  2015. pBegin--;
  2016. *pBegin = NLS_CHAR_OPEN_PAREN;
  2017. //
  2018. // Put the closing parenthesis in the buffer.
  2019. //
  2020. *pEnd = NLS_CHAR_CLOSE_PAREN;
  2021. pEnd++;
  2022. break;
  2023. }
  2024. case ( 15 ) :
  2025. {
  2026. //
  2027. // Put the opening parenthesis in the buffer.
  2028. //
  2029. pBegin--;
  2030. *pBegin = NLS_CHAR_OPEN_PAREN;
  2031. //
  2032. // Put a space in the buffer.
  2033. //
  2034. *pEnd = NLS_CHAR_SPACE;
  2035. pEnd++;
  2036. //
  2037. // Copy the currency symbol to the buffer.
  2038. //
  2039. NLS_COPY_UNICODE_STR(pEnd, pFormat->lpCurrencySymbol);
  2040. //
  2041. // Put the closing parenthesis in the buffer.
  2042. //
  2043. *pEnd = NLS_CHAR_CLOSE_PAREN;
  2044. pEnd++;
  2045. break;
  2046. }
  2047. }
  2048. }
  2049. else
  2050. {
  2051. //
  2052. // Positive value. Store the currency symbol in the string
  2053. // if the positive order is either 0 or 2. Otherwise, wait
  2054. // till the end.
  2055. //
  2056. switch (pFormat->PositiveOrder)
  2057. {
  2058. case ( 2 ) :
  2059. {
  2060. //
  2061. // Put a space in the buffer.
  2062. //
  2063. pBegin--;
  2064. *pBegin = NLS_CHAR_SPACE;
  2065. //
  2066. // Fall through to case 0.
  2067. //
  2068. }
  2069. case ( 0 ) :
  2070. default :
  2071. {
  2072. //
  2073. // Copy the currency symbol to the buffer.
  2074. //
  2075. pBegin -= CurrSymSize;
  2076. NLS_COPY_UNICODE_STR_TMP(pBegin, pFormat->lpCurrencySymbol);
  2077. break;
  2078. }
  2079. case ( 3 ) :
  2080. {
  2081. //
  2082. // Put a space in the buffer.
  2083. //
  2084. *pEnd = NLS_CHAR_SPACE;
  2085. pEnd++;
  2086. //
  2087. // Fall through to case 1.
  2088. //
  2089. }
  2090. case ( 1 ) :
  2091. {
  2092. //
  2093. // Copy the currency symbol to the buffer.
  2094. //
  2095. NLS_COPY_UNICODE_STR(pEnd, pFormat->lpCurrencySymbol);
  2096. break;
  2097. }
  2098. }
  2099. }
  2100. //
  2101. // Zero terminate the string.
  2102. //
  2103. *pEnd = 0;
  2104. //
  2105. // Return the pointer to the beginning of the string.
  2106. //
  2107. *ppBuf = pBegin;
  2108. //
  2109. // Return the number of characters written to the buffer, including
  2110. // the null terminator.
  2111. //
  2112. return ((int)((pEnd - pBegin) + 1));
  2113. }