/*++ Copyright (c) 1991-2000, Microsoft Corporation All rights reserved. Module Name: number.c Abstract: This file contains functions that form properly formatted number and currency strings for a given locale. APIs found in this file: GetNumberFormatW GetCurrencyFormatW Revision History: 07-28-93 JulieB Created. --*/ // // Include Files. // #include "nls.h" #include "nlssafe.h" // // Constant Declarations. // #define MAX_NUMBER_BUFFER 256 // size of static buffer #define MAX_GROUPS 5 // max number of groupings #define MAX_GROUPING_NUMBER 9999 // max value for groupings // // Account for: // - number of fractional digits // - decimal seperator // - negative sign // - zero terminator // #define MAX_NON_INTEGER_PART ( MAX_VALUE_IDIGITS + \ MAX_SDECIMAL + \ MAX_SNEGSIGN + \ 1 ) // // Account for: // - negative sign // - blank spaces // - one extra number from rounding // - one extra grouping separator from rounding // #define MAX_NUMBER_EXTRAS ( MAX_SNEGSIGN + \ MAX_BLANKS + \ 1 + \ MAX_STHOUSAND ) // // Account for: // - negative sign // - currency sign // - blank spaces // - one extra number from rounding // - one extra grouping separator from rounding // #define MAX_CURRENCY_EXTRAS ( MAX_SNEGSIGN + \ MAX_SCURRENCY + \ MAX_BLANKS + \ 1 + \ MAX_SMONTHOUSEP ) // // Forward Declarations. // BOOL IsValidNumberFormat( CONST NUMBERFMTW *pFormat); BOOL IsValidCurrencyFormat( CONST CURRENCYFMTW *pFormat); UINT GetRegIntValue( LCID Locale, LCTYPE LCType, BOOL NoUserOverride, SIZE_T CacheOffset, LPWSTR pRegVal, LPWSTR pDefault, int DefaultVal, int UpperBound); int ConvertGroupingStringToInt( LPWSTR pGroupingSrc, LPWSTR pGroupingDest); UINT GetGroupingValue( LCID Locale, LCTYPE LCType, BOOL NoUserOverride, SIZE_T CacheOffset, LPWSTR pRegVal, LPWSTR pDefault, int DefaultVal); int GetNumberString( PLOC_HASH pHashN, LPWSTR pValue, LPNUMBERFMTW pFormat, LPWSTR *ppBuf, int BufSize, BOOL *pfZeroValue, int *pNeededSizeToAllocate, BOOL fSetError); int ParseNumber( PLOC_HASH pHashN, BOOL NoUserOverride, LPWSTR pValue, LPNUMBERFMTW pFormat, LPWSTR *ppBuf, int BufSize, int *pNeededSizeToAllocate, BOOL fSetError); int ParseCurrency( PLOC_HASH pHashN, BOOL NoUserOverride, LPWSTR pValue, LPCURRENCYFMTW pFormat, LPWSTR *ppBuf, int BufSize, int *pNeededSizeToAllocate, BOOL fSetError); //-------------------------------------------------------------------------// // INTERNAL MACROS // //-------------------------------------------------------------------------// //////////////////////////////////////////////////////////////////////////// // // NLS_COPY_UNICODE_STR // // Copies a zero terminated Unicode string from pSrc to the pDest buffer. // The pDest pointer is advanced to the end of the string. // // DEFINED AS A MACRO. // // 07-28-93 JulieB Created. //////////////////////////////////////////////////////////////////////////// #define NLS_COPY_UNICODE_STR( pDest, \ pSrc ) \ { \ LPWSTR pTmp; /* temp pointer to source */ \ \ \ pTmp = pSrc; \ while (*pTmp) \ { \ *pDest = *pTmp; \ pDest++; \ pTmp++; \ } \ } //////////////////////////////////////////////////////////////////////////// // // NLS_COPY_UNICODE_STR_NOADV // // Copies a zero terminated Unicode string from pSrc to the pDest buffer. // The pDest pointer is NOT advanced to the end of the string. // // DEFINED AS A MACRO. // // 07-28-93 JulieB Created. //////////////////////////////////////////////////////////////////////////// #define NLS_COPY_UNICODE_STR_TMP( pDest, \ pSrc ) \ { \ LPWSTR pSrcT; /* temp pointer to source */ \ LPWSTR pDestT; /* temp pointer to destination */ \ \ \ pSrcT = pSrc; \ pDestT = pDest; \ while (*pSrcT) \ { \ *pDestT = *pSrcT; \ pDestT++; \ pSrcT++; \ } \ } //////////////////////////////////////////////////////////////////////////// // // NLS_ROUND_IT // // Rounds the floating point number given as a string. // // NOTE: This function will reset the pBegin pointer if an // extra character is added to the string. // // DEFINED AS A MACRO. // // 07-28-93 JulieB Created. //////////////////////////////////////////////////////////////////////////// #define NLS_ROUND_IT( pBegin, \ pEnd, \ IntPartGroup, \ pSep ) \ { \ LPWSTR pRound = pEnd; /* ptr to position in string */ \ LPWSTR pEndSep; /* ptr to end of group separator */ \ \ \ /* \ * Round the digits in the string one by one, going backwards in \ * the string. Stop when either a value other than 9 is found, \ * or the beginning of the string is reached. \ */ \ while (pRound >= pBegin) \ { \ if ((*pRound < NLS_CHAR_ZERO) || (*pRound > NLS_CHAR_NINE)) \ { \ pRound--; \ } \ else if (*pRound == NLS_CHAR_NINE) \ { \ *pRound = NLS_CHAR_ZERO; \ pRound--; \ } \ else \ { \ (*pRound)++; \ break; \ } \ } \ \ /* \ * Make sure we don't have a number like 9.999, where we would need \ * to add an extra character to the string and make it 10.00. \ */ \ if (pRound < pBegin) \ { \ /* \ * All values to the right of the decimal are zero. All values \ * to the left of the decimal are either zero or the grouping \ * separator. \ */ \ if ((IntPartGroup) == 0) \ { \ /* \ * Adding another integer means we need to add another \ * grouping separator. \ */ \ pEndSep = pSep + NlsStrLenW(pSep) - 1; \ while (pEndSep >= pSep) \ { \ (pBegin)--; \ *(pBegin) = *pEndSep; \ pEndSep--; \ } \ } \ \ /* \ * Store a 1 at the beginning of the string and reset the \ * pointer to the beginning of the string. \ */ \ (pBegin)--; \ *(pBegin) = NLS_CHAR_ONE; \ } \ } //-------------------------------------------------------------------------// // API ROUTINES // //-------------------------------------------------------------------------// //////////////////////////////////////////////////////////////////////////// // // GetNumberFormatW // // Returns a properly formatted number string for the given locale. // This call also indicates how much memory is necessary to contain // the desired information. // // 07-28-93 JulieB Created. //////////////////////////////////////////////////////////////////////////// int WINAPI GetNumberFormatW( LCID Locale, DWORD dwFlags, LPCWSTR lpValue, CONST NUMBERFMTW *lpFormat, LPWSTR lpNumberStr, int cchNumber) { PLOC_HASH pHashN; // ptr to LOC hash node int Length = 0; // number of characters written LPNUMBERFMTW pFormat; // ptr to number format struct NUMBERFMTW NumFmt; // number format WCHAR pString[MAX_NUMBER_BUFFER]; // ptr to temporary buffer LPWSTR pFinal; // ptr to the final string BOOL NoUserOverride; // if no user override flag set WCHAR pDecimal[MAX_REG_VAL_SIZE]; // temp buffer for decimal sep WCHAR pThousand[MAX_REG_VAL_SIZE]; // temp buffer for thousand sep int NeededSizeToAllocate = 0; // size of buffer needed WCHAR *pTemp = NULL; // allocated temp storage buffer // // Initialize UserOverride. // NoUserOverride = dwFlags & LOCALE_NOUSEROVERRIDE; // // Invalid Parameter Check: // - validate LCID // - count is negative // - NULL src string // - NULL data pointer AND count is not zero // - ptrs to string buffers same // VALIDATE_LOCALE(Locale, pHashN, FALSE); if ( (pHashN == NULL) || (cchNumber < 0) || (lpValue == NULL) || ((lpNumberStr == NULL) && (cchNumber != 0)) || (lpValue == lpNumberStr) ) { SetLastError(ERROR_INVALID_PARAMETER); return (0); } // // Invalid Flags Check: // - flags other than valid ones // - lpFormat not NULL AND NoUserOverride flag is set // if ( (dwFlags & GNF_INVALID_FLAG) || ((lpFormat != NULL) && (NoUserOverride)) ) { SetLastError(ERROR_INVALID_FLAGS); return (0); } // // Set pFormat to point at the proper format structure. // if (lpFormat != NULL) { // // Use the format structure given by the caller. // pFormat = (LPNUMBERFMTW)lpFormat; if (!IsValidNumberFormat(pFormat)) { SetLastError(ERROR_INVALID_PARAMETER); return (0); } } else { // // Use the format structure defined here. // pFormat = &NumFmt; // // Get the number of decimal digits. // pFormat->NumDigits = GetRegIntValue( Locale, LOCALE_IDIGITS, NoUserOverride, FIELD_OFFSET(NLS_USER_INFO, iDigits), NLS_VALUE_IDIGITS, pHashN->pLocaleFixed->szIDigits, 2, MAX_VALUE_IDIGITS ); // // Get the leading zero in decimal fields option. // pFormat->LeadingZero = GetRegIntValue( Locale, LOCALE_ILZERO, NoUserOverride, FIELD_OFFSET(NLS_USER_INFO, iLZero), NLS_VALUE_ILZERO, pHashN->pLocaleFixed->szILZero, 1, MAX_VALUE_ILZERO ); // // Get the negative ordering. // pFormat->NegativeOrder = GetRegIntValue( Locale, LOCALE_INEGNUMBER, NoUserOverride, FIELD_OFFSET(NLS_USER_INFO, iNegNumber), NLS_VALUE_INEGNUMBER, pHashN->pLocaleFixed->szINegNumber, 1, MAX_VALUE_INEGNUMBER ); // // Get the grouping left of the decimal. // pFormat->Grouping = GetGroupingValue( Locale, LOCALE_SGROUPING, NoUserOverride, FIELD_OFFSET(NLS_USER_INFO, sGrouping), NLS_VALUE_SGROUPING, (LPWORD)(pHashN->pLocaleHdr) + pHashN->pLocaleHdr->SGrouping, 3 ); // // Get the decimal separator. // // NOTE: This must follow the above calls because // pDecSep is used as a temporary buffer above. // if ( (!NoUserOverride) && GetUserInfo( Locale, LOCALE_SDECIMAL, FIELD_OFFSET(NLS_USER_INFO, sDecimal), NLS_VALUE_SDECIMAL, pDecimal, ARRAYSIZE(pDecimal), TRUE ) && IsValidSeparatorString( pDecimal, MAX_SDECIMAL, FALSE ) ) { pFormat->lpDecimalSep = pDecimal; } else { pFormat->lpDecimalSep = (LPWORD)(pHashN->pLocaleHdr) + pHashN->pLocaleHdr->SDecimal; } // // Get the thousand separator. // This string may be a null string. // if ( (!NoUserOverride) && GetUserInfo( Locale, LOCALE_STHOUSAND, FIELD_OFFSET(NLS_USER_INFO, sThousand), NLS_VALUE_STHOUSAND, pThousand, ARRAYSIZE(pThousand), FALSE ) && IsValidSeparatorString( pThousand, MAX_STHOUSAND, FALSE ) ) { pFormat->lpThousandSep = pThousand; } else { pFormat->lpThousandSep = (LPWORD)(pHashN->pLocaleHdr) + pHashN->pLocaleHdr->SThousand; } } // // Parse the number format string. // pFinal = pString; Length = ParseNumber( pHashN, NoUserOverride, (LPWSTR)lpValue, pFormat, &pFinal, MAX_NUMBER_BUFFER, &NeededSizeToAllocate, FALSE ); // // If the failure is due to a stack variable size limitation, then // try to satisfy the request from the local process heap. // if ((Length == 0) && (NeededSizeToAllocate > 0)) { pTemp = RtlAllocateHeap( RtlProcessHeap(), 0, NeededSizeToAllocate * sizeof(TCHAR) ); if (pTemp) { pFinal = pTemp; Length = ParseNumber( pHashN, NoUserOverride, (LPWSTR)lpValue, pFormat, &pFinal, NeededSizeToAllocate, &NeededSizeToAllocate, TRUE ); } } // // Check cchNumber for size of given buffer. // if ((cchNumber == 0) || (Length == 0)) { // // If cchNumber is 0, then we can't use lpNumberStr. In this // case, we simply want to return the length (in characters) of // the string to be copied. // Length = Length; } else if (cchNumber < Length) { // // The buffer is too small for the string, so return an error // and zero bytes written. // SetLastError(ERROR_INSUFFICIENT_BUFFER); Length = 0; } else { // // Copy the number string to lpNumberStr and null terminate it. // Return the number of characters copied. // if(FAILED(StringCchCopyW(lpNumberStr, cchNumber, pFinal))) { // // Failure should in theory be impossible, but if we ignore the // return value, PREfast will complain. // SetLastError(ERROR_OUTOFMEMORY); Length = 0; } } // // Free any dynamically allocated memory. // if (pTemp != NULL) { RtlFreeHeap(RtlProcessHeap(), 0, pTemp); } // // Return the number of characters copied. // return (Length); } //////////////////////////////////////////////////////////////////////////// // // GetCurrencyFormatW // // Returns a properly formatted currency string for the given locale. // This call also indicates how much memory is necessary to contain // the desired information. // // 07-28-93 JulieB Created. //////////////////////////////////////////////////////////////////////////// int WINAPI GetCurrencyFormatW( LCID Locale, DWORD dwFlags, LPCWSTR lpValue, CONST CURRENCYFMTW *lpFormat, LPWSTR lpCurrencyStr, int cchCurrency) { PLOC_HASH pHashN; // ptr to LOC hash node int Length = 0; // number of characters written LPCURRENCYFMTW pFormat; // ptr to currency format struct CURRENCYFMTW CurrFmt; // currency format WCHAR pString[MAX_NUMBER_BUFFER]; // ptr to temporary buffer LPWSTR pFinal; // ptr to the final string BOOL NoUserOverride; // if no user override flag set WCHAR pDecimal[MAX_REG_VAL_SIZE]; // temp buffer for decimal sep WCHAR pThousand[MAX_REG_VAL_SIZE]; // temp buffer for thousand sep WCHAR pCurrency[MAX_REG_VAL_SIZE]; // temp buffer for currency symbol int NeededSizeToAllocate = 0; // size of buffer needed WCHAR *pTemp = NULL; // allocated temp storage buffer // // Initialize UserOverride. // NoUserOverride = dwFlags & LOCALE_NOUSEROVERRIDE; // // Invalid Parameter Check: // - validate LCID // - count is negative // - NULL src string // - NULL data pointer AND count is not zero // - ptrs to string buffers same // VALIDATE_LOCALE(Locale, pHashN, FALSE); if ( (pHashN == NULL) || (cchCurrency < 0) || (lpValue == NULL) || ((lpCurrencyStr == NULL) && (cchCurrency != 0)) || (lpValue == lpCurrencyStr) ) { SetLastError(ERROR_INVALID_PARAMETER); return (0); } // // Invalid Flags Check: // - flags other than valid ones // - lpFormat not NULL AND NoUserOverride flag is set // if ( (dwFlags & GCF_INVALID_FLAG) || ((lpFormat != NULL) && (NoUserOverride)) ) { SetLastError(ERROR_INVALID_FLAGS); return (0); } // // Set pFormat to point at the proper format structure. // if (lpFormat != NULL) { // // Use the format structure given by the caller. // pFormat = (LPCURRENCYFMTW)lpFormat; if (!IsValidCurrencyFormat(pFormat)) { SetLastError(ERROR_INVALID_PARAMETER); return (0); } } else { // // Use the format structure defined here. // pFormat = &CurrFmt; // // Get the number of decimal digits. // pFormat->NumDigits = GetRegIntValue( Locale, LOCALE_ICURRDIGITS, NoUserOverride, FIELD_OFFSET(NLS_USER_INFO, iCurrDigits), NLS_VALUE_ICURRDIGITS, pHashN->pLocaleFixed->szICurrDigits, 2, MAX_VALUE_ICURRDIGITS ); // // Get the leading zero in decimal fields option. // pFormat->LeadingZero = GetRegIntValue( Locale, LOCALE_ILZERO, NoUserOverride, FIELD_OFFSET(NLS_USER_INFO, iLZero), NLS_VALUE_ILZERO, pHashN->pLocaleFixed->szILZero, 1, MAX_VALUE_ILZERO ); // // Get the positive ordering. // pFormat->PositiveOrder = GetRegIntValue( Locale, LOCALE_ICURRENCY, NoUserOverride, FIELD_OFFSET(NLS_USER_INFO, iCurrency), NLS_VALUE_ICURRENCY, pHashN->pLocaleFixed->szICurrency, 0, MAX_VALUE_ICURRENCY ); // // Get the negative ordering. // pFormat->NegativeOrder = GetRegIntValue( Locale, LOCALE_INEGCURR, NoUserOverride, FIELD_OFFSET(NLS_USER_INFO, iNegCurr), NLS_VALUE_INEGCURR, pHashN->pLocaleFixed->szINegCurr, 1, MAX_VALUE_INEGCURR ); // // Get the grouping left of the decimal. // pFormat->Grouping = GetGroupingValue( Locale, LOCALE_SMONGROUPING, NoUserOverride, FIELD_OFFSET(NLS_USER_INFO, sMonGrouping), NLS_VALUE_SMONGROUPING, (LPWORD)(pHashN->pLocaleHdr) + pHashN->pLocaleHdr->SMonGrouping, 3 ); // // Get the decimal separator. // // NOTE: This must follow the above calls because // pDecSep is used as a temporary buffer. // if ( (!NoUserOverride) && GetUserInfo( Locale, LOCALE_SMONDECIMALSEP, FIELD_OFFSET(NLS_USER_INFO, sMonDecSep), NLS_VALUE_SMONDECIMALSEP, pDecimal, ARRAYSIZE(pDecimal), TRUE ) && IsValidSeparatorString( pDecimal, MAX_SDECIMAL, FALSE ) ) { pFormat->lpDecimalSep = pDecimal; } else { pFormat->lpDecimalSep = (LPWORD)(pHashN->pLocaleHdr) + pHashN->pLocaleHdr->SMonDecSep; } // // Get the thousand separator. // This string may be a null string. // if ( (!NoUserOverride) && GetUserInfo( Locale, LOCALE_SMONTHOUSANDSEP, FIELD_OFFSET(NLS_USER_INFO, sMonThouSep), NLS_VALUE_SMONTHOUSANDSEP, pThousand, ARRAYSIZE(pThousand), FALSE ) && IsValidSeparatorString( pThousand, MAX_STHOUSAND, FALSE ) ) { pFormat->lpThousandSep = pThousand; } else { pFormat->lpThousandSep = (LPWORD)(pHashN->pLocaleHdr) + pHashN->pLocaleHdr->SMonThousSep; } // // Get the currency symbol. // This string may be a null string. // if ( (!NoUserOverride) && GetUserInfo( Locale, LOCALE_SCURRENCY, FIELD_OFFSET(NLS_USER_INFO, sCurrency), NLS_VALUE_SCURRENCY, pCurrency, ARRAYSIZE(pCurrency), FALSE ) && IsValidSeparatorString( pCurrency, MAX_SCURRENCY, FALSE ) ) { pFormat->lpCurrencySymbol = pCurrency; } else { pFormat->lpCurrencySymbol = (LPWORD)(pHashN->pLocaleHdr) + pHashN->pLocaleHdr->SCurrency; } } // // Parse the currency format string. // pFinal = pString; Length = ParseCurrency( pHashN, NoUserOverride, (LPWSTR)lpValue, pFormat, &pFinal, MAX_NUMBER_BUFFER, &NeededSizeToAllocate, FALSE ); // // If the failure is due to a stack variable size limitation, then // try to satisfy the request from the local process heap. // if ((Length == 0) && (NeededSizeToAllocate > 0)) { pTemp = RtlAllocateHeap( RtlProcessHeap(), 0, NeededSizeToAllocate * sizeof(TCHAR) ); if (pTemp) { pFinal = pTemp; Length = ParseCurrency( pHashN, NoUserOverride, (LPWSTR)lpValue, pFormat, &pFinal, NeededSizeToAllocate, &NeededSizeToAllocate, TRUE ); } } // // Check cchCurrency for size of given buffer. // if ((cchCurrency == 0) || (Length == 0)) { // // If cchCurrency is 0, then we can't use lpCurrencyStr. In this // case, we simply want to return the length (in characters) of // the string to be copied. // Length = Length; } else if (cchCurrency < Length) { // // The buffer is too small for the string, so return an error // and zero bytes written. // SetLastError(ERROR_INSUFFICIENT_BUFFER); Length = 0; } else { // // Copy the currency string to lpCurrencyStr and null terminate it. // Return the number of characters copied. // if(FAILED(StringCchCopyW(lpCurrencyStr, cchCurrency, pFinal))) { // // Failure should in theory be impossible, but if we ignore the // return value, PREfast will complain. // SetLastError(ERROR_OUTOFMEMORY); Length = 0; } } // // Free any dynamically allocated memory. // if (pTemp != NULL) { RtlFreeHeap(RtlProcessHeap(), 0, pTemp); } // // Return the number of characters copied. // return (Length); } //-------------------------------------------------------------------------// // INTERNAL ROUTINES // //-------------------------------------------------------------------------// //////////////////////////////////////////////////////////////////////////// // // IsValidNumberFormat // // Returns TRUE if the given format is valid. Otherwise, it returns FALSE. // // 07-28-93 JulieB Created. //////////////////////////////////////////////////////////////////////////// BOOL IsValidNumberFormat( CONST NUMBERFMTW *pFormat) { // // Check for invalid values. // if ((pFormat->NumDigits > MAX_VALUE_IDIGITS) || (pFormat->LeadingZero > MAX_VALUE_ILZERO) || (pFormat->Grouping > MAX_GROUPING_NUMBER) || (pFormat->NegativeOrder > MAX_VALUE_INEGNUMBER) || (pFormat->lpDecimalSep == NULL) || (!IsValidSeparatorString( pFormat->lpDecimalSep, MAX_SDECIMAL, (pFormat->NumDigits) ? TRUE : FALSE)) || (pFormat->lpThousandSep == NULL) || (!IsValidSeparatorString( pFormat->lpThousandSep, MAX_STHOUSAND, FALSE ))) { return (FALSE); } // // Return success. // return (TRUE); } //////////////////////////////////////////////////////////////////////////// // // IsValidCurrencyFormat // // Returns TRUE if the given format is valid. Otherwise, it returns FALSE. // // 07-28-93 JulieB Created. //////////////////////////////////////////////////////////////////////////// BOOL IsValidCurrencyFormat( CONST CURRENCYFMTW *pFormat) { // // Check for invalid values. // if ((pFormat->NumDigits > MAX_VALUE_IDIGITS) || (pFormat->LeadingZero > MAX_VALUE_ILZERO) || (pFormat->Grouping > MAX_GROUPING_NUMBER) || (pFormat->lpDecimalSep == NULL) || (!IsValidSeparatorString( pFormat->lpDecimalSep, MAX_SMONDECSEP, (pFormat->NumDigits) ? TRUE : FALSE)) || (pFormat->lpThousandSep == NULL) || (!IsValidSeparatorString( pFormat->lpThousandSep, MAX_SMONTHOUSEP, FALSE )) || (pFormat->lpCurrencySymbol == NULL) || (!IsValidSeparatorString( pFormat->lpCurrencySymbol, MAX_SCURRENCY, FALSE )) || (pFormat->PositiveOrder > MAX_VALUE_ICURRENCY) || (pFormat->NegativeOrder > MAX_VALUE_INEGCURR)) { return (FALSE); } // // Return success. // return (TRUE); } //////////////////////////////////////////////////////////////////////////// // // GetRegIntValue // // Retrieves the specified locale information, converts the unicode string // to an integer value, and returns the value. // // 07-28-93 JulieB Created. //////////////////////////////////////////////////////////////////////////// UINT GetRegIntValue( LCID Locale, LCTYPE LCType, BOOL NoUserOverride, SIZE_T CacheOffset, LPWSTR pRegVal, LPWSTR pDefault, int DefaultVal, int UpperBound) { UNICODE_STRING ObUnicodeStr; // value string int Value; // value WCHAR pTemp[MAX_REG_VAL_SIZE]; // temp buffer // // Initialize values. // Value = -1; // // Try the user registry. // if ((!NoUserOverride) && GetUserInfo( Locale, LCType, CacheOffset, pRegVal, pTemp, ARRAYSIZE(pTemp), TRUE )) { // // Convert the user data to an integer. // RtlInitUnicodeString(&ObUnicodeStr, pTemp); if ((RtlUnicodeStringToInteger(&ObUnicodeStr, 10, &Value)) || (Value < 0) || (Value > UpperBound)) { // // Bad value, so store -1 so that the system default // will be used. // Value = -1; } } // // See if the value obtained above is valid. // if (Value < 0) { // // Convert system default data to an integer. // RtlInitUnicodeString(&ObUnicodeStr, pDefault); if ((RtlUnicodeStringToInteger(&ObUnicodeStr, 10, &Value)) || (Value < 0) || (Value > UpperBound)) { // // Bad value, so use the chosen default value. // Value = DefaultVal; } } return ((UINT)Value); } //////////////////////////////////////////////////////////////////////////// // // ConvertGroupingStringToInt // // Converts the given grouping string to an integer. // For example, 3;2;0 becomes 32 and 3;0 becomes 3 and 3;2 becomes 320. // // NOTE: The pGrouping buffer will be modified. // // 01-05-98 JulieB Created. //////////////////////////////////////////////////////////////////////////// int ConvertGroupingStringToInt( LPWSTR pGroupingSrc, LPWSTR pGroupingDest) { LPWSTR pSrc = pGroupingSrc; // temp ptr to src position LPWSTR pDest = pGroupingDest; // temp ptr to dest position UNICODE_STRING ObUnicodeStr; // value string int Value; // value // // Filter out all non-numeric values and all zero values. // Store the result in the destination buffer. // while (*pSrc) { if ((*pSrc < NLS_CHAR_ONE) || (*pSrc > NLS_CHAR_NINE)) { pSrc++; } else { if (pSrc != pDest) { *pDest = *pSrc; } pSrc++; pDest++; } } // // Make sure there is something in the destination buffer. // Also, see if we need to add a zero in the case of 3;2 becomes 320. // if ((pDest == pGroupingDest) || (*(pSrc - 1) != NLS_CHAR_ZERO)) { *pDest = NLS_CHAR_ZERO; pDest++; } // // Null terminate the buffer. // *pDest = 0; // // Convert the string to an integer. // RtlInitUnicodeString(&ObUnicodeStr, pGroupingDest); RtlUnicodeStringToInteger(&ObUnicodeStr, 10, &Value); // // Return the integer value. // return (Value); } //////////////////////////////////////////////////////////////////////////// // // GetGroupingValue // // Retrieves the specified grouping information, converts the grouping // string to an integer value (eg. 3;2;0 -> 32), and returns the value. // // 07-28-93 JulieB Created. //////////////////////////////////////////////////////////////////////////// UINT GetGroupingValue( LCID Locale, LCTYPE LCType, BOOL NoUserOverride, SIZE_T CacheOffset, LPWSTR pRegVal, LPWSTR pDefault, int DefaultVal) { int Value; // value WCHAR pTemp[MAX_REG_VAL_SIZE]; // temp buffer // // Initialize values. // Value = -1; // // Try the user registry. // if ((!NoUserOverride) && GetUserInfo( Locale, LCType, CacheOffset, pRegVal, pTemp, ARRAYSIZE(pTemp), TRUE )) { // // Convert the grouping string to an integer. // 3;0 becomes 3, 3;2;0 becomes 32, and 3;2 becomes 320. // Value = ConvertGroupingStringToInt(pTemp, pTemp); if (Value < 0) { // // Bad value, so store -1 so that the system default // will be used. // Value = -1; } } // // See if the value obtained above is valid. // if (Value < 0) { // // Convert the grouping string to an integer. // 3;0 becomes 3, 3;2;0 becomes 32, and 3;2 becomes 320. // Value = ConvertGroupingStringToInt(pDefault, pTemp); if (Value < 0) { // // Bad value, so use the chosen default value. // Value = DefaultVal; } } // // Return the value. // return ((UINT)Value); } //////////////////////////////////////////////////////////////////////////// // // GetNumberString // // Puts the properly formatted number string into the given string buffer. // It returns the number of characters written to the string buffer. // // 07-28-93 JulieB Created. //////////////////////////////////////////////////////////////////////////// int GetNumberString( PLOC_HASH pHashN, LPWSTR pValue, LPNUMBERFMTW pFormat, LPWSTR *ppBuf, int BufSize, BOOL *pfZeroValue, int *pNeededSizeToAllocate, BOOL fSetError) { LPWSTR pDecPt; // ptr to decimal point in given buffer LPWSTR pPos; // ptr to position in given buffer LPWSTR pPos2; // ptr to position in given buffer LPWSTR pPosBuf; // ptr to position in final buffer int IntPartSize; // size of integer part of string int GroupSize; // size of groupings left of decimal int IntegerNum; // number of integers left of decimal WCHAR wch; // wide character place holder int pGroupArray[MAX_GROUPS]; // array of groups int NumGroupings; // number of groupings int NumSeparators; // number of separators int NumDigits; // number of digits int Ctr; // loop counter UINT NumRound = 1; // # digits left before adding group separator // // Reset to indicate no need to allocate memory dynamically. // *pNeededSizeToAllocate = 0; // // Validate the string and find the decimal point in the string. // // The only valid characters within the string are: // negative sign - in first position only // decimal point // Unicode code points for integers 0 - 9 // pPos = pValue; while ((wch = *pPos) && (wch != NLS_CHAR_PERIOD)) { if ((wch < NLS_CHAR_ZERO) || (wch > NLS_CHAR_NINE)) { SetLastError(ERROR_INVALID_PARAMETER); return (0); } pPos++; } pDecPt = pPos; if (*pPos) { pPos++; while (wch = *pPos) { if ((wch < NLS_CHAR_ZERO) || (wch > NLS_CHAR_NINE)) { SetLastError(ERROR_INVALID_PARAMETER); return (0); } pPos++; } } // // Remove any leading zeros in the integer part. // while (pValue < pDecPt) { if (*pValue != NLS_CHAR_ZERO) { break; } pValue++; } // // Save the number of integers to the left of the decimal. // IntegerNum = (int)(pDecPt - pValue); // // Make sure the value string passed in is not too large for // the buffers. // IntPartSize = IntegerNum; NumGroupings = 0; NumSeparators = 0; if ((GroupSize = pFormat->Grouping) && (IntPartSize)) { // // Count the number of groupings and save them in an array to be // used later. // while (GroupSize && (NumGroupings < MAX_GROUPS)) { pGroupArray[NumGroupings] = GroupSize % 10; GroupSize /= 10; NumGroupings++; } // // Count the number of groupings that apply to the given number // string. // NumDigits = IntegerNum; Ctr = (NumGroupings != 0) ? (NumGroupings - 1) : 0; while (Ctr) { if (NumDigits > pGroupArray[Ctr]) { NumDigits -= pGroupArray[Ctr]; NumSeparators++; } else { if (NumDigits == pGroupArray[Ctr]) { NumRound = 0; } break; } Ctr--; } if ((Ctr == 0) && pGroupArray[0]) { if (NumDigits > pGroupArray[0]) { NumSeparators += (NumDigits - 1) / pGroupArray[0]; } NumRound = NumDigits % pGroupArray[0]; } IntPartSize += MAX_STHOUSAND * NumSeparators; } // // Make sure the buffer is large enough. If not, return the size // needed. // if (IntPartSize > (BufSize - MAX_NON_INTEGER_PART)) { if (fSetError) { SetLastError(ERROR_INVALID_PARAMETER); } *pNeededSizeToAllocate = (IntPartSize + MAX_NON_INTEGER_PART); return (0); } // // Initialize pointers. // pPosBuf = *ppBuf; pPos = pValue; *pfZeroValue = FALSE; // // See if there are any digits before the decimal point. // if (pPos == pDecPt) { // // Possibly a zero value. All leading zeros were removed, so // there is no integer part. // *pfZeroValue = TRUE; // // No digits before decimal point, so add a leading zero // to the final string if appropriate. // if (pFormat->LeadingZero) { *pPosBuf = NLS_CHAR_ZERO; pPosBuf++; } } else if (!NumSeparators) { // // Grouping Size is zero or larger than the integer part of the // string, so copy up to the decimal point (or end of string). // while (pPos < pDecPt) { *pPosBuf = *pPos; pPosBuf++; pPos++; } } else { // // Copy up to where the first thousand separator should be. // Use groupings of GroupSize numbers up to the decimal point. // NumDigits = IntegerNum; Ctr = (NumGroupings != 0) ? (NumGroupings - 1) : 0; while (Ctr) { if (NumDigits > pGroupArray[Ctr]) { NumDigits -= pGroupArray[Ctr]; } else { break; } Ctr--; } GroupSize = pGroupArray[Ctr]; pPos2 = GroupSize ? (pPos + (NumDigits % GroupSize)) : (pPos + NumDigits); if (pPos2 == pPos) { // // Don't want to write thousand separator at the beginning // of the string. There's at least GroupSize numbers // in the string, so just advance pPos2 so that GroupSize // numbers will be copied. // pPos2 = pPos + GroupSize; } while (pPos < pPos2) { *pPosBuf = *pPos; pPosBuf++; pPos++; NumDigits--; } // // Copy the thousand separator followed by GroupSize number of // digits from the given string until the entire repeating // GroupSize ends (or end of string). // while (NumDigits) { // // Copy the localized thousand separator. // pPos2 = pFormat->lpThousandSep; while (*pPos2) { *pPosBuf = *pPos2; pPosBuf++; pPos2++; } // // Copy GroupSize number of digits. // pPos2 = pPos + GroupSize; while (pPos < pPos2) { *pPosBuf = *pPos; pPosBuf++; pPos++; NumDigits--; } } // // Copy the thousand separator followed by GroupSize number of // digits from the given string - until the decimal point (or // end of string) in the given string is reached. // if (pPos < pDecPt) { Ctr++; while (Ctr < NumGroupings) { // // Copy the localized thousand separator. // pPos2 = pFormat->lpThousandSep; while (*pPos2) { *pPosBuf = *pPos2; pPosBuf++; pPos2++; } // // Copy GroupSize number of digits. // pPos2 = pPos + pGroupArray[Ctr]; while (pPos < pPos2) { *pPosBuf = *pPos; pPosBuf++; pPos++; } // // Go to the next grouping. // Ctr++; } } } // // See if there is a decimal separator in the given string. // if (pFormat->NumDigits > 0) { // // Copy the localized decimal separator only if the number // of digits right of the decimal is greater than zero. // pDecPt = pPosBuf; pPos2 = pFormat->lpDecimalSep; while (*pPos2) { *pPosBuf = *pPos2; pPosBuf++; pPos2++; } } // // Skip over the decimal point in the given string and // copy the rest of the digits from the given string. // if (*pPos) { pPos++; } pPos2 = pPos + pFormat->NumDigits; while ((*pPos) && (pPos < pPos2)) { if (*pPos != NLS_CHAR_ZERO) { *pfZeroValue = FALSE; } *pPosBuf = *pPos; pPosBuf++; pPos++; } // // Make sure some value is in the buffer. // if (*ppBuf == pPosBuf) { *pPosBuf = NLS_CHAR_ZERO; pPosBuf++; } // // See if we need to round the number or pad it with zeros. // if (*pPos) { // // Round the number if necessary. // if (*pPos2 > L'4') { *pfZeroValue = FALSE; // // Round the number. If GroupSize is 0, then we need to // pass in a non-zero value so that the thousand separator // will not be added to the front of the string (if it // rounds that far). // pPosBuf--; NLS_ROUND_IT( *ppBuf, pPosBuf, NumRound, pFormat->lpThousandSep ); pPosBuf++; } } else { // // Pad the string with the appropriate number of zeros. // while (pPos < pPos2) { *pPosBuf = NLS_CHAR_ZERO; pPosBuf++; pPos++; } } // // Zero terminate the string. // *pPosBuf = 0; // // Return the number of characters written to the buffer, including // the null terminator. // return ((int)((pPosBuf - *ppBuf) + 1)); } //////////////////////////////////////////////////////////////////////////// // // ParseNumber // // Puts the properly formatted number string into the given string buffer. // It returns the number of characters written to the string buffer. // // 07-28-93 JulieB Created. //////////////////////////////////////////////////////////////////////////// int ParseNumber( PLOC_HASH pHashN, BOOL NoUserOverride, LPWSTR pValue, LPNUMBERFMTW pFormat, LPWSTR *ppBuf, int BufSize, int *pNeededSizeToAllocate, BOOL fSetError) { LPWSTR pBegin; // ptr to beginning of final buffer LPWSTR pEnd; // ptr to end of final buffer LPWSTR pNegSign; // ptr to negative sign string BOOL IsNeg; // if negative sign in string int Length; // length of number string BOOL fZeroValue = FALSE; // if number is a zero value int NegSignSize; // size of negative sign string WCHAR pTemp[MAX_REG_VAL_SIZE]; // temp buffer // // Initialize pointer. // // Account for: // - negative sign // - blank spaces // - one extra number from rounding // - one extra grouping separator from rounding // pBegin = *ppBuf + MAX_NUMBER_EXTRAS; // // If the first value is a negative, then increment past it. // if (IsNeg = (*pValue == NLS_CHAR_HYPHEN)) { pValue++; } // // Get the appropriate number string and place it in the buffer. // Length = GetNumberString( pHashN, pValue, pFormat, &pBegin, BufSize - MAX_NUMBER_EXTRAS, &fZeroValue, pNeededSizeToAllocate, fSetError ); if (!Length) { if (*pNeededSizeToAllocate > 0) { *pNeededSizeToAllocate += MAX_NUMBER_EXTRAS; } return (0); } // // Advance pEnd position pointer to the end of the number string. // pEnd = pBegin + (Length - 1); // // See if any characters should be put in the buffer BEFORE and // AFTER the properly formatted number string. // - negative sign or opening/closing parenthesis // - blank space // if (!fZeroValue && IsNeg) { // // Get the negative sign string. // if (pFormat->NegativeOrder != 0) { if ( (!NoUserOverride) && GetUserInfo( pHashN->Locale, LOCALE_SNEGATIVESIGN, FIELD_OFFSET(NLS_USER_INFO, sNegSign), NLS_VALUE_SNEGATIVESIGN, pTemp, ARRAYSIZE(pTemp), TRUE ) && IsValidSeparatorString( pTemp, MAX_SNEGSIGN, FALSE ) ) { pNegSign = pTemp; } else { pNegSign = (LPWORD)(pHashN->pLocaleHdr) + pHashN->pLocaleHdr->SNegativeSign; } } switch (pFormat->NegativeOrder) { case ( 0 ) : { // // Put the opening parenthesis in the buffer. // pBegin--; *pBegin = NLS_CHAR_OPEN_PAREN; // // Put the closing parenthesis in the buffer. // *pEnd = NLS_CHAR_CLOSE_PAREN; pEnd++; break; } case ( 2 ) : { // // Put the space in the buffer. // pBegin--; *pBegin = NLS_CHAR_SPACE; // // Fall through to case 1. // } case ( 1 ) : default : { // // Copy the negative sign to the buffer. // NegSignSize = NlsStrLenW(pNegSign); pBegin -= NegSignSize; NLS_COPY_UNICODE_STR_TMP(pBegin, pNegSign); break; } case ( 4 ) : { // // Put the space in the buffer. // *pEnd = NLS_CHAR_SPACE; pEnd++; // // Fall Through to case 3. // } case ( 3 ) : { // // Copy the negative sign to the buffer. // NLS_COPY_UNICODE_STR(pEnd, pNegSign); break; } } } // // Zero terminate the string. // *pEnd = 0; // // Return the pointer to the beginning of the string. // *ppBuf = pBegin; // // Return the number of characters written to the buffer, including // the null terminator. // return ((int)((pEnd - pBegin) + 1)); } //////////////////////////////////////////////////////////////////////////// // // ParseCurrency // // Puts the properly formatted currency string into the given string buffer. // It returns the number of characters written to the string buffer. // // 07-28-93 JulieB Created. //////////////////////////////////////////////////////////////////////////// int ParseCurrency( PLOC_HASH pHashN, BOOL NoUserOverride, LPWSTR pValue, LPCURRENCYFMTW pFormat, LPWSTR *ppBuf, int BufSize, int *pNeededSizeToAllocate, BOOL fSetError) { LPWSTR pBegin; // ptr to beginning of final buffer LPWSTR pEnd; // ptr to end of final buffer LPWSTR pNegSign; // ptr to negative sign string BOOL IsNeg; // if negative sign in string int Length; // length of number string BOOL fZeroValue = FALSE; // if number is a zero value int NegSignSize; // size of negative sign string UINT NegOrder; // negative ordering int CurrSymSize; // size of currency symbol WCHAR pTemp[MAX_REG_VAL_SIZE]; // temp buffer // // Initialize pointer. // // Account for: // - negative sign // - currency sign // - blank spaces // - one extra number from rounding // - one extra grouping separator from rounding // pBegin = *ppBuf + MAX_CURRENCY_EXTRAS; // // If the first value is a negative, then increment past it. // if (IsNeg = (*pValue == NLS_CHAR_HYPHEN)) { pValue++; } // // Get the appropriate number string and place it in the buffer. // Length = GetNumberString( pHashN, pValue, (LPNUMBERFMTW)pFormat, &pBegin, BufSize - MAX_CURRENCY_EXTRAS, &fZeroValue, pNeededSizeToAllocate, fSetError ); if (!Length) { if (*pNeededSizeToAllocate > 0) { *pNeededSizeToAllocate += MAX_CURRENCY_EXTRAS; } return (0); } // // Advance pEnd position pointer to the end of the number string. // pEnd = pBegin + (Length - 1); // // Get the size of the currency symbol. // CurrSymSize = NlsStrLenW(pFormat->lpCurrencySymbol); // // See if any characters should be put in the buffer BEFORE and // AFTER the properly formatted number string. // - currency symbol // - negative sign or opening/closing parenthesis // - blank space // if (!fZeroValue && IsNeg) { // // Get the negative sign string and the size of it. // NegOrder = pFormat->NegativeOrder; if ((NegOrder != 0) && (NegOrder != 4) && (NegOrder < 14)) { if ( (!NoUserOverride) && GetUserInfo( pHashN->Locale, LOCALE_SNEGATIVESIGN, FIELD_OFFSET(NLS_USER_INFO, sNegSign), NLS_VALUE_SNEGATIVESIGN, pTemp, ARRAYSIZE(pTemp), TRUE ) && IsValidSeparatorString( pTemp, MAX_SNEGSIGN, FALSE ) ) { pNegSign = pTemp; } else { pNegSign = (LPWORD)(pHashN->pLocaleHdr) + pHashN->pLocaleHdr->SNegativeSign; } NegSignSize = NlsStrLenW(pNegSign); } switch (NegOrder) { case ( 0 ) : { // // Copy the currency symbol to the buffer. // pBegin -= CurrSymSize; NLS_COPY_UNICODE_STR_TMP(pBegin, pFormat->lpCurrencySymbol); // // Put the opening parenthesis in the buffer. // pBegin--; *pBegin = NLS_CHAR_OPEN_PAREN; // // Put the closing parenthesis in the buffer. // *pEnd = NLS_CHAR_CLOSE_PAREN; pEnd++; break; } case ( 1 ) : default : { // // Copy the currency symbol to the buffer. // pBegin -= CurrSymSize; NLS_COPY_UNICODE_STR_TMP(pBegin, pFormat->lpCurrencySymbol); // // Copy the negative sign to the buffer. // pBegin -= NegSignSize; NLS_COPY_UNICODE_STR_TMP(pBegin, pNegSign); break; } case ( 2 ) : { // // Copy the negative sign to the buffer. // pBegin -= NegSignSize; NLS_COPY_UNICODE_STR_TMP(pBegin, pNegSign); // // Copy the currency symbol to the buffer. // pBegin -= CurrSymSize; NLS_COPY_UNICODE_STR_TMP(pBegin, pFormat->lpCurrencySymbol); break; } case ( 3 ) : { // // Copy the currency symbol to the buffer. // pBegin -= CurrSymSize; NLS_COPY_UNICODE_STR_TMP(pBegin, pFormat->lpCurrencySymbol); // // Copy the negative sign to the buffer. // NLS_COPY_UNICODE_STR(pEnd, pNegSign); break; } case ( 4 ) : { // // Put the opening parenthesis in the buffer. // pBegin--; *pBegin = NLS_CHAR_OPEN_PAREN; // // Copy the currency symbol to the buffer. // NLS_COPY_UNICODE_STR(pEnd, pFormat->lpCurrencySymbol); // // Put the closing parenthesis in the buffer. // *pEnd = NLS_CHAR_CLOSE_PAREN; pEnd++; break; } case ( 5 ) : { // // Copy the negative sign to the buffer. // pBegin -= NegSignSize; NLS_COPY_UNICODE_STR_TMP(pBegin, pNegSign); // // Copy the currency symbol to the buffer. // NLS_COPY_UNICODE_STR(pEnd, pFormat->lpCurrencySymbol); break; } case ( 6 ) : { // // Copy the negative sign to the buffer. // NLS_COPY_UNICODE_STR(pEnd, pNegSign); // // Copy the currency symbol to the buffer. // NLS_COPY_UNICODE_STR(pEnd, pFormat->lpCurrencySymbol); break; } case ( 7 ) : { // // Copy the currency symbol to the buffer. // NLS_COPY_UNICODE_STR(pEnd, pFormat->lpCurrencySymbol); // // Copy the negative sign to the buffer. // NLS_COPY_UNICODE_STR(pEnd, pNegSign); break; } case ( 8 ) : { // // Copy the negative sign to the buffer. // pBegin -= NegSignSize; NLS_COPY_UNICODE_STR_TMP(pBegin, pNegSign); // // Put a space in the buffer. // *pEnd = NLS_CHAR_SPACE; pEnd++; // // Copy the currency symbol to the buffer. // NLS_COPY_UNICODE_STR(pEnd, pFormat->lpCurrencySymbol); break; } case ( 9 ) : { // // Put a space in the buffer. // pBegin--; *pBegin = NLS_CHAR_SPACE; // // Copy the currency symbol to the buffer. // pBegin -= CurrSymSize; NLS_COPY_UNICODE_STR_TMP(pBegin, pFormat->lpCurrencySymbol); // // Copy the negative sign to the buffer. // pBegin -= NegSignSize; NLS_COPY_UNICODE_STR_TMP(pBegin, pNegSign); break; } case ( 10 ) : { // // Put a space in the buffer. // *pEnd = NLS_CHAR_SPACE; pEnd++; // // Copy the currency symbol to the buffer. // NLS_COPY_UNICODE_STR(pEnd, pFormat->lpCurrencySymbol); // // Copy the negative sign to the buffer. // NLS_COPY_UNICODE_STR(pEnd, pNegSign); break; } case ( 11 ) : { // // Put a space in the buffer. // pBegin--; *pBegin = NLS_CHAR_SPACE; // // Copy the currency symbol to the buffer. // pBegin -= CurrSymSize; NLS_COPY_UNICODE_STR_TMP(pBegin, pFormat->lpCurrencySymbol); // // Copy the negative sign to the buffer. // NLS_COPY_UNICODE_STR(pEnd, pNegSign); break; } case ( 12 ) : { // // Copy the negative sign to the buffer. // pBegin -= NegSignSize; NLS_COPY_UNICODE_STR_TMP(pBegin, pNegSign); // // Put a space in the buffer. // pBegin--; *pBegin = NLS_CHAR_SPACE; // // Copy the currency symbol to the buffer. // pBegin -= CurrSymSize; NLS_COPY_UNICODE_STR_TMP(pBegin, pFormat->lpCurrencySymbol); break; } case ( 13 ) : { // // Copy the negative sign to the buffer. // NLS_COPY_UNICODE_STR(pEnd, pNegSign); // // Put a space in the buffer. // *pEnd = NLS_CHAR_SPACE; pEnd++; // // Copy the currency symbol to the buffer. // NLS_COPY_UNICODE_STR(pEnd, pFormat->lpCurrencySymbol); break; } case ( 14 ) : { // // Put a space in the buffer. // pBegin--; *pBegin = NLS_CHAR_SPACE; // // Copy the currency symbol to the buffer. // pBegin -= CurrSymSize; NLS_COPY_UNICODE_STR_TMP(pBegin, pFormat->lpCurrencySymbol); // // Put the opening parenthesis in the buffer. // pBegin--; *pBegin = NLS_CHAR_OPEN_PAREN; // // Put the closing parenthesis in the buffer. // *pEnd = NLS_CHAR_CLOSE_PAREN; pEnd++; break; } case ( 15 ) : { // // Put the opening parenthesis in the buffer. // pBegin--; *pBegin = NLS_CHAR_OPEN_PAREN; // // Put a space in the buffer. // *pEnd = NLS_CHAR_SPACE; pEnd++; // // Copy the currency symbol to the buffer. // NLS_COPY_UNICODE_STR(pEnd, pFormat->lpCurrencySymbol); // // Put the closing parenthesis in the buffer. // *pEnd = NLS_CHAR_CLOSE_PAREN; pEnd++; break; } } } else { // // Positive value. Store the currency symbol in the string // if the positive order is either 0 or 2. Otherwise, wait // till the end. // switch (pFormat->PositiveOrder) { case ( 2 ) : { // // Put a space in the buffer. // pBegin--; *pBegin = NLS_CHAR_SPACE; // // Fall through to case 0. // } case ( 0 ) : default : { // // Copy the currency symbol to the buffer. // pBegin -= CurrSymSize; NLS_COPY_UNICODE_STR_TMP(pBegin, pFormat->lpCurrencySymbol); break; } case ( 3 ) : { // // Put a space in the buffer. // *pEnd = NLS_CHAR_SPACE; pEnd++; // // Fall through to case 1. // } case ( 1 ) : { // // Copy the currency symbol to the buffer. // NLS_COPY_UNICODE_STR(pEnd, pFormat->lpCurrencySymbol); break; } } } // // Zero terminate the string. // *pEnd = 0; // // Return the pointer to the beginning of the string. // *ppBuf = pBegin; // // Return the number of characters written to the buffer, including // the null terminator. // return ((int)((pEnd - pBegin) + 1)); }