338 lines
10 KiB
C
338 lines
10 KiB
C
|
/***
|
||
|
*initnum.c - contains __init_numeric
|
||
|
*
|
||
|
* Copyright (c) 1991-2001, Microsoft Corporation. All rights reserved.
|
||
|
*
|
||
|
*Purpose:
|
||
|
* Contains the locale-category initialization function: __init_numeric().
|
||
|
*
|
||
|
* Each initialization function sets up locale-specific information
|
||
|
* for their category, for use by functions which are affected by
|
||
|
* their locale category.
|
||
|
*
|
||
|
* *** For internal use by setlocale() only ***
|
||
|
*
|
||
|
*Revision History:
|
||
|
* 12-08-91 ETC Created.
|
||
|
* 12-20-91 ETC Updated to use new NLSAPI GetLocaleInfo.
|
||
|
* 12-18-92 CFW Ported to Cuda tree, changed _CALLTYPE4 to _CRTAPI3.
|
||
|
* 12-29-92 CFW Updated to use new _getlocaleinfo wrapper function.
|
||
|
* 01-25-93 KRS Change interface to _getlocaleinfo again.
|
||
|
* 02-08-93 CFW Added _lconv_static_*.
|
||
|
* 02-17-93 CFW Removed debugging print statement.
|
||
|
* 03-17-93 CFW C locale thousands sep is "", not ",".
|
||
|
* 04-06-93 SKS Replace _CRTAPI* with __cdecl
|
||
|
* 04-08-93 SKS Replace strdup() with ANSI-conforming _strdup()
|
||
|
* 04-20-93 CFW Check return val.
|
||
|
* 05-20-93 GJF Include windows.h, not individual win*.h files
|
||
|
* 05-24-93 CFW Clean up file (brief is evil).
|
||
|
* 06-11-93 CFW Now inithelp takes void *.
|
||
|
* 09-15-93 CFW Use ANSI conformant "__" names.
|
||
|
* 09-23-93 GJF Merged NT SDK and Cuda versions.
|
||
|
* 09-15-93 CFW Use ANSI conformant "__" names.
|
||
|
* 04-06-94 GJF Removed declaration of __lconv (it is declared in
|
||
|
* setlocal.h). Renamed static vars, decimal_point
|
||
|
* thousands_sep and grouping to dec_pnt, thous_sep
|
||
|
* and grping (resp.). Made the definitions of these
|
||
|
* conditional on DLL_FOR_WIN32S.
|
||
|
* 08-02-94 CFW Change "3;0" to "\3" for grouping as per ANSI.
|
||
|
* 09-06-94 CFW Remove _INTL switch.
|
||
|
* 01-10-95 CFW Debug CRT allocs.
|
||
|
* 01-18-95 GJF Fixed bug introduced with the change above - resetting
|
||
|
* to the C locale didn't reset the thousand_sep and
|
||
|
* grouping fields correctly.
|
||
|
* 02-06-95 CFW assert -> _ASSERTE.
|
||
|
* 07-06-98 GJF Changed to support new multithread scheme - old lconv
|
||
|
* structs must be kept around until all affected threads
|
||
|
* have updated or terminated.
|
||
|
* 12-08-98 GJF Fixed logic in __free_lconv_num.
|
||
|
* 01-25-99 GJF No, I didn't! Try again...
|
||
|
* 03-15-99 GJF Added __lconv_num_refcount
|
||
|
* 04-24-99 PML Added __lconv_intl_refcount
|
||
|
* 09-08-00 GB Fixed leak of __lconv_intl in init_numeric for single
|
||
|
* thread case.
|
||
|
* 10-12-00 PML Don't call fix_grouping if error detected (vs7#169596)
|
||
|
* 11-05-00 PML Fixed double-free of __lconv_intl (vs7#181380)
|
||
|
*
|
||
|
*******************************************************************************/
|
||
|
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
#include <windows.h>
|
||
|
#include <locale.h>
|
||
|
#include <setlocal.h>
|
||
|
#include <malloc.h>
|
||
|
#include <nlsint.h>
|
||
|
#include <dbgint.h>
|
||
|
|
||
|
void __cdecl __free_lconv_num(struct lconv *);
|
||
|
|
||
|
extern struct lconv *__lconv_intl;
|
||
|
|
||
|
#ifdef _MT
|
||
|
/*
|
||
|
* Reference counter for numeric locale info. The value is non-NULL iff the
|
||
|
* numeric info is not from the C locale.
|
||
|
*/
|
||
|
int *__lconv_num_refcount;
|
||
|
|
||
|
extern int *__lconv_intl_refcount;
|
||
|
#endif
|
||
|
|
||
|
static void fix_grouping(
|
||
|
char *grouping
|
||
|
)
|
||
|
{
|
||
|
/*
|
||
|
* ANSI specifies that the fields should contain "\3" [\3\0] to indicate
|
||
|
* thousands groupings (100,000,000.00 for example).
|
||
|
* NT uses "3;0"; ASCII 3 instead of value 3 and the ';' is extra.
|
||
|
* So here we convert the NT version to the ANSI version.
|
||
|
*/
|
||
|
|
||
|
while (*grouping)
|
||
|
{
|
||
|
/* convert '3' to '\3' */
|
||
|
if (*grouping >= '0' && *grouping <= '9')
|
||
|
{
|
||
|
*grouping = *grouping - '0';
|
||
|
grouping++;
|
||
|
}
|
||
|
|
||
|
/* remove ';' */
|
||
|
else if (*grouping == ';')
|
||
|
{
|
||
|
char *tmp = grouping;
|
||
|
|
||
|
do
|
||
|
*tmp = *(tmp+1);
|
||
|
while (*++tmp);
|
||
|
}
|
||
|
|
||
|
/* unknown (illegal) character, ignore */
|
||
|
else
|
||
|
grouping++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/***
|
||
|
*int __init_numeric() - initialization for LC_NUMERIC locale category.
|
||
|
*
|
||
|
*Purpose:
|
||
|
*
|
||
|
*Entry:
|
||
|
* None.
|
||
|
*
|
||
|
*Exit:
|
||
|
* 0 success
|
||
|
* 1 fail
|
||
|
*
|
||
|
*Exceptions:
|
||
|
*
|
||
|
*******************************************************************************/
|
||
|
|
||
|
int __cdecl __init_numeric (
|
||
|
void
|
||
|
)
|
||
|
{
|
||
|
struct lconv *lc;
|
||
|
int ret = 0;
|
||
|
LCID ctryid;
|
||
|
#ifdef _MT
|
||
|
int *lc_refcount;
|
||
|
#endif
|
||
|
|
||
|
if ( (__lc_handle[LC_NUMERIC] != _CLOCALEHANDLE) ||
|
||
|
(__lc_handle[LC_MONETARY] != _CLOCALEHANDLE) )
|
||
|
{
|
||
|
/*
|
||
|
* Allocate structure filled with NULL pointers
|
||
|
*/
|
||
|
if ( (lc = (struct lconv *)_calloc_crt(1, sizeof(struct lconv)))
|
||
|
== NULL )
|
||
|
return 1;
|
||
|
|
||
|
/*
|
||
|
* Copy over all fields (esp., the monetary category)
|
||
|
*/
|
||
|
*lc = *__lconv;
|
||
|
|
||
|
#ifdef _MT
|
||
|
/*
|
||
|
* Allocate a new reference counter for the lconv structure
|
||
|
*/
|
||
|
if ( (lc_refcount = _malloc_crt(sizeof(int))) == NULL )
|
||
|
{
|
||
|
_free_crt(lc);
|
||
|
return 1;
|
||
|
}
|
||
|
*lc_refcount = 0;
|
||
|
#endif
|
||
|
|
||
|
if ( __lc_handle[LC_NUMERIC] != _CLOCALEHANDLE )
|
||
|
{
|
||
|
#ifdef _MT
|
||
|
/*
|
||
|
* Allocate a new reference counter for the numeric info
|
||
|
*/
|
||
|
if ( (__lconv_num_refcount = _malloc_crt(sizeof(int))) == NULL )
|
||
|
{
|
||
|
_free_crt(lc);
|
||
|
_free_crt(lc_refcount);
|
||
|
return 1;
|
||
|
}
|
||
|
*__lconv_num_refcount = 0;
|
||
|
#endif
|
||
|
|
||
|
/*
|
||
|
* Numeric data is country--not language--dependent. NT
|
||
|
* work-around.
|
||
|
*/
|
||
|
ctryid = MAKELCID(__lc_id[LC_NUMERIC].wCountry, SORT_DEFAULT);
|
||
|
|
||
|
ret |= __getlocaleinfo(LC_STR_TYPE, ctryid, LOCALE_SDECIMAL,
|
||
|
(void *)&lc->decimal_point);
|
||
|
ret |= __getlocaleinfo(LC_STR_TYPE, ctryid, LOCALE_STHOUSAND,
|
||
|
(void *)&lc->thousands_sep);
|
||
|
ret |= __getlocaleinfo(LC_STR_TYPE, ctryid, LOCALE_SGROUPING,
|
||
|
(void *)&lc->grouping);
|
||
|
|
||
|
if (ret) {
|
||
|
/* Clean up before returning failure */
|
||
|
__free_lconv_num(lc);
|
||
|
_free_crt(lc);
|
||
|
#ifdef _MT
|
||
|
_free_crt(lc_refcount);
|
||
|
#endif
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
fix_grouping(lc->grouping);
|
||
|
}
|
||
|
else {
|
||
|
/*
|
||
|
* C locale for just the numeric category.
|
||
|
*/
|
||
|
#ifdef _MT
|
||
|
/*
|
||
|
* NULL out the reference count pointer
|
||
|
*/
|
||
|
__lconv_num_refcount = NULL;
|
||
|
#endif
|
||
|
lc->decimal_point = __lconv_c.decimal_point;
|
||
|
lc->thousands_sep = __lconv_c.thousands_sep;
|
||
|
lc->grouping = __lconv_c.grouping;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Clean up old __lconv and reset it to lc
|
||
|
*/
|
||
|
#ifdef _MT
|
||
|
/*
|
||
|
* If this is part of LC_ALL, then we need to free the old __lconv
|
||
|
* set up in init_monetary() before this.
|
||
|
*/
|
||
|
if ( (__lconv_intl_refcount != NULL) &&
|
||
|
(*__lconv_intl_refcount == 0) &&
|
||
|
(__lconv_intl_refcount != __ptlocinfo->lconv_intl_refcount) )
|
||
|
{
|
||
|
_free_crt(__lconv_intl_refcount);
|
||
|
_free_crt(__lconv_intl);
|
||
|
}
|
||
|
__lconv_intl_refcount = lc_refcount;
|
||
|
#else
|
||
|
__free_lconv_num(__lconv);
|
||
|
|
||
|
/*
|
||
|
* Recall that __lconv is dynamically allocated (hence must be
|
||
|
* freed) iff __lconv and __lconv_intl are equal iff __lconv_intl
|
||
|
* is non-NULL.
|
||
|
*/
|
||
|
_free_crt(__lconv_intl);
|
||
|
#endif
|
||
|
|
||
|
__lconv = __lconv_intl = lc;
|
||
|
|
||
|
}
|
||
|
else {
|
||
|
/*
|
||
|
* C locale for BOTH numeric and monetary categories.
|
||
|
*/
|
||
|
#ifdef _MT
|
||
|
/*
|
||
|
* If this is part of LC_ALL, then we need to free the old __lconv
|
||
|
* set up in init_monetary() before this.
|
||
|
*/
|
||
|
if ( (__lconv_intl_refcount != NULL) &&
|
||
|
(*__lconv_intl_refcount == 0) &&
|
||
|
(__lconv_intl_refcount != __ptlocinfo->lconv_intl_refcount) )
|
||
|
{
|
||
|
_free_crt(__lconv_intl_refcount);
|
||
|
_free_crt(__lconv_intl);
|
||
|
}
|
||
|
/*
|
||
|
* NULL out the reference count pointer
|
||
|
*/
|
||
|
__lconv_num_refcount = NULL;
|
||
|
__lconv_intl_refcount = NULL;
|
||
|
#else
|
||
|
__free_lconv_num(__lconv);
|
||
|
|
||
|
/*
|
||
|
* Recall that __lconv is dynamically allocated (hence must be
|
||
|
* freed) iff __lconv and __lconv_intl are equal iff __lconv_intl
|
||
|
* is non-NULL.
|
||
|
*/
|
||
|
_free_crt(__lconv_intl);
|
||
|
#endif
|
||
|
__lconv = &__lconv_c; /* point to new one */
|
||
|
__lconv_intl = NULL;
|
||
|
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* set global decimal point character
|
||
|
*/
|
||
|
*__decimal_point = *__lconv->decimal_point;
|
||
|
__decimal_point_length = 1;
|
||
|
|
||
|
return 0;
|
||
|
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Free the lconv numeric strings.
|
||
|
* Numeric values do not need to be freed.
|
||
|
*/
|
||
|
void __cdecl __free_lconv_num(
|
||
|
struct lconv *l
|
||
|
)
|
||
|
{
|
||
|
if (l == NULL)
|
||
|
return;
|
||
|
|
||
|
#ifdef _MT
|
||
|
if ( (l->decimal_point != __lconv->decimal_point) &&
|
||
|
(l->decimal_point != __lconv_c.decimal_point) )
|
||
|
#else
|
||
|
if ( l->decimal_point != __lconv_c.decimal_point )
|
||
|
#endif
|
||
|
_free_crt(l->decimal_point);
|
||
|
|
||
|
#ifdef _MT
|
||
|
if ( (l->thousands_sep != __lconv->thousands_sep) &&
|
||
|
(l->thousands_sep != __lconv_c.thousands_sep) )
|
||
|
#else
|
||
|
if ( l->thousands_sep != __lconv_c.thousands_sep )
|
||
|
#endif
|
||
|
_free_crt(l->thousands_sep);
|
||
|
|
||
|
#ifdef _MT
|
||
|
if ( (l->grouping != __lconv->grouping) &&
|
||
|
(l->grouping != __lconv_c.grouping) )
|
||
|
#else
|
||
|
if ( l->grouping != __lconv_c.grouping )
|
||
|
#endif
|
||
|
_free_crt(l->grouping);
|
||
|
}
|