/*** *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 #include #include #include #include #include #include #include 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); }