Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

1954 lines
57 KiB

/*++
Copyright (c) 1991-1996, 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"
//
// Constant Declarations.
//
#define MAX_NUMBER_BUFFER 256 // max size of buffer
#define MAX_INTEGER_PART ( MAX_NUMBER_BUFFER - \
( MAX_VALUE_IDIGITS + \
MAX_SDECIMAL + \
MAX_SNEGSIGN + 1 ) )
//
// Forward Declarations.
//
BOOL
IsValidNumberFormat(
CONST NUMBERFMTW *pFormat);
BOOL
IsValidCurrencyFormat(
CONST CURRENCYFMTW *pFormat);
UINT
GetRegIntValue(
LCID Locale,
BOOL NoUserOverride,
LPWSTR pRegVal,
LPWSTR pDefault,
int DefaultVal,
int UpperBound);
UINT
GetGroupingValue(
LCID Locale,
BOOL NoUserOverride,
LPWSTR pRegVal,
LPWSTR pDefault,
int DefaultVal,
int UpperBound);
int
GetNumberString(
PLOC_HASH pHashN,
LPWSTR pValue,
LPNUMBERFMTW pFormat,
LPWSTR *ppBuf,
BOOL *pfZeroValue);
int
ParseNumber(
PLOC_HASH pHashN,
BOOL NoUserOverride,
LPWSTR pValue,
LPNUMBERFMTW pFormat,
LPWSTR *ppBuf);
int
ParseCurrency(
PLOC_HASH pHashN,
BOOL NoUserOverride,
LPWSTR pValue,
LPCURRENCYFMTW pFormat,
LPWSTR *ppBuf);
//-------------------------------------------------------------------------//
// 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) = L'1'; \
} \
}
//-------------------------------------------------------------------------//
// 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
//
// Initialize UserOverride.
//
NoUserOverride = dwFlags & LOCALE_NOUSEROVERRIDE;
//
// Invalid Parameter Check:
// - validate LCID
// - count is negative
// - NULL data pointer AND count is not zero
// - ptrs to string buffers same
//
VALIDATE_LOCALE(Locale, pHashN);
if ( (pHashN == NULL) ||
(cchNumber < 0) ||
((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,
NoUserOverride,
pNlsUserInfo->iDigits,
pHashN->pLocaleFixed->szIDigits,
2,
MAX_VALUE_IDIGITS );
//
// Get the leading zero in decimal fields option.
//
pFormat->LeadingZero =
GetRegIntValue( Locale,
NoUserOverride,
pNlsUserInfo->iLZero,
pHashN->pLocaleFixed->szILZero,
1,
MAX_VALUE_ILZERO );
//
// Get the negative ordering.
//
pFormat->NegativeOrder =
GetRegIntValue( Locale,
NoUserOverride,
pNlsUserInfo->iNegNumber,
pHashN->pLocaleFixed->szINegNumber,
1,
MAX_VALUE_INEGNUMBER );
//
// Get the grouping left of the decimal.
//
pFormat->Grouping =
GetGroupingValue( Locale,
NoUserOverride,
pNlsUserInfo->sGrouping,
(LPWORD)(pHashN->pLocaleHdr) +
pHashN->pLocaleHdr->SGrouping,
3,
MAX_VALUE_SGROUPING );
//
// Get the decimal separator.
//
// NOTE: This must follow the above calls because
// pDecSep is used as a temporary buffer above.
//
if ( (!NoUserOverride) &&
GetUserInfo( Locale,
pNlsUserInfo->sDecimal,
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,
pNlsUserInfo->sThousand,
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 );
//
// 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.
//
return (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);
return (0);
}
//
// Copy the number string to lpNumberStr and null terminate it.
// Return the number of characters copied.
//
NlsStrCpyW(lpNumberStr, pFinal);
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
//
// Initialize UserOverride.
//
NoUserOverride = dwFlags & LOCALE_NOUSEROVERRIDE;
//
// Invalid Parameter Check:
// - validate LCID
// - count is negative
// - NULL data pointer AND count is not zero
// - ptrs to string buffers same
//
VALIDATE_LOCALE(Locale, pHashN);
if ( (pHashN == NULL) ||
(cchCurrency < 0) ||
((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,
NoUserOverride,
pNlsUserInfo->iCurrDigits,
pHashN->pLocaleFixed->szICurrDigits,
2,
MAX_VALUE_ICURRDIGITS );
//
// Get the leading zero in decimal fields option.
//
pFormat->LeadingZero =
GetRegIntValue( Locale,
NoUserOverride,
pNlsUserInfo->iLZero,
pHashN->pLocaleFixed->szILZero,
1,
MAX_VALUE_ILZERO );
//
// Get the positive ordering.
//
pFormat->PositiveOrder =
GetRegIntValue( Locale,
NoUserOverride,
pNlsUserInfo->iCurrency,
pHashN->pLocaleFixed->szICurrency,
0,
MAX_VALUE_ICURRENCY );
//
// Get the negative ordering.
//
pFormat->NegativeOrder =
GetRegIntValue( Locale,
NoUserOverride,
pNlsUserInfo->iNegCurr,
pHashN->pLocaleFixed->szINegCurr,
1,
MAX_VALUE_INEGCURR );
//
// Get the grouping left of the decimal.
//
pFormat->Grouping =
GetGroupingValue( Locale,
NoUserOverride,
pNlsUserInfo->sMonGrouping,
(LPWORD)(pHashN->pLocaleHdr) +
pHashN->pLocaleHdr->SMonGrouping,
3,
MAX_VALUE_SMONGROUPING );
//
// Get the decimal separator.
//
// NOTE: This must follow the above calls because
// pDecSep is used as a temporary buffer.
//
if ( (!NoUserOverride) &&
GetUserInfo( Locale,
pNlsUserInfo->sMonDecSep,
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,
pNlsUserInfo->sMonThouSep,
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,
pNlsUserInfo->sCurrency,
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 );
//
// 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.
//
return (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);
return (0);
}
//
// Copy the currency string to lpCurrencyStr and null terminate it.
// Return the number of characters copied.
//
NlsStrCpyW(lpCurrencyStr, pFinal);
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_VALUE_SGROUPING ) ||
( 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_VALUE_SGROUPING ) ||
( 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,
BOOL NoUserOverride,
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,
pRegVal,
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);
}
////////////////////////////////////////////////////////////////////////////
//
// GetGroupingValue
//
// Retrieves the specified grouping information, uses only the first digit
// to convert it to an integer value, and returns the value.
//
// 07-28-93 JulieB Created.
////////////////////////////////////////////////////////////////////////////
UINT GetGroupingValue(
LCID Locale,
BOOL NoUserOverride,
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,
pRegVal,
pTemp,
TRUE ))
{
//
// Only use the first integer value, so null terminate it
// after the first character. Must do this in a temp buffer.
//
*(pTemp + 1) = 0;
//
// 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)
{
//
// Store the default value in a temp buffer.
//
*pTemp = *(pDefault);
*(pTemp + 1) = 0;
//
// Convert system default data to an integer.
//
RtlInitUnicodeString(&ObUnicodeStr, pTemp);
if ((RtlUnicodeStringToInteger(&ObUnicodeStr, 10, &Value)) ||
(Value < 0) || (Value > UpperBound))
{
//
// Bad value, so use the chosen default value.
//
Value = DefaultVal;
}
}
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,
BOOL *pfZeroValue)
{
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
//
// 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 = pDecPt - pValue;
//
// Make sure the value string passed in is not too large for
// the buffers.
//
IntPartSize = IntegerNum;
if ((GroupSize = pFormat->Grouping) && (IntPartSize))
{
IntPartSize += MAX_STHOUSAND * ((IntPartSize - 1) / GroupSize);
}
if (IntPartSize > MAX_INTEGER_PART)
{
SetLastError(ERROR_INVALID_PARAMETER);
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 ((!GroupSize) || (GroupSize >= IntegerNum))
{
//
// 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.
//
pPos2 = pPos + (IntegerNum % GroupSize);
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++;
}
//
// 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.
//
while (pPos < pDecPt)
{
//
// 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++;
}
}
}
//
// 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,
( (GroupSize && IntegerNum)
? (IntegerNum % GroupSize)
: 1 ),
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 ((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)
{
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; // 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_SNEGSIGN + MAX_BLANKS + 1 + MAX_SGROUPING;
//
// 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,
&fZeroValue );
if (!Length)
{
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,
pNlsUserInfo->sNegSign,
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 ((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)
{
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; // 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_SNEGSIGN + MAX_SCURRENCY + MAX_BLANKS + 1 + MAX_SGROUPING;
//
// 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,
&fZeroValue );
if (!Length)
{
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,
pNlsUserInfo->sNegSign,
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 ((pEnd - pBegin) + 1);
}