Source code of Windows XP (NT5)
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.

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