|
|
/***
*setlocal.c - Contains the setlocale function * * Copyright (c) 1988-2001, Microsoft Corporation. All rights reserved. * *Purpose: * Contains the setlocale() function. * *Revision History: * 03-21-89 JCR Module created. * 09-25-89 GJF Fixed copyright. Checked for compatibility with Win 3.0 * 09-25-90 KRS Major rewrite--support more than "C" locale if _INTL. * 11-05-91 ETC Get 09-25-90 working for C and "" locales; separate * setlocal.h; add Init functions. * 12-05-91 ETC Separate nlsdata.c; add mt support; remove calls to * itself. * 12-20-91 ETC Added _getlocaleinfo api interface function. * 09-25-92 KRS Fix for latest NLSAPI changes, etc. * 01-25-93 KRS Fix for latest changes, clean up code, etc. * 02-02-93 CFW Many modifications and bug fixes (all under _INTL). * 02-08-93 CFW Bug fixes and casts to avoid warnings (all under _INTL). * 02-17-93 CFW Removed re-call of init() functions in case of failure. * 03-01-93 CFW Check GetQualifiedLocale return value. * 03-02-93 CFW Added POSIX conformance, check environment variables. * 03-09-93 CFW Set CP to CP_ACP when changing to C locale. * 03-17-93 CFW Change expand to expandlocale, prepend _ to internal * functions, lots of POSIX fixup. * 03-23-93 CFW Add _ to GetQualifiedLocale call. * 03-24-93 CFW Change to _get_qualified_locale, support ".codepage". * 04-06-93 SKS Replace _CRTAPI* with __cdecl * 05-10-93 CFW Disallow setlocale(LC_*, "."). * 05-24-93 CFW Clean up file (brief is evil). * 09-15-93 CFW Use ANSI conformant "__" names. * 02-04-94 CFW Remove unused param. * 04-15-94 GJF Moved prototypes for locale category initialization * functions to setlocal.h. Made definitions for * __lc_category, cacheid, cachecp, cachein and cacheout * conditional on DLL_FOR_WIN32S. Made _clocalestr into * a global for DLL_FOR_WIN32S so that crtlib.c may use * it. * 09-06-94 CFW Remove _INTL switch. * 09-06-94 CFW Merge non-Win32. * 01-10-95 CFW Debug CRT allocs. * 09-25-95 GJF New locking scheme for functions which set or * reference locale information. * 05-02-96 SKS Variables _setlc_active and __unguarded_readlc_active * are used by MSVCP42*.DLL and so must be _CRTIMP. * 07-09-97 GJF Made __lc_category selectany. Also, removed obsolete * DLL_FOR_WIN32S support. * 01-12-98 GJF Use _lc_collate_cp codepage. * 02-27-98 RKP Add 64 bit support. * 09-10-98 GJF Revised multithread support based on threadlocinfo * struct. * 09-21-98 GJF No need to lock or update threadlocinfo for setlocale * calls which only read info. * 11-06-98 GJF In __lc_strtolc, make sure you don't overflow * names->szCodePage. * 12-08-98 GJF Fixed __updatetlocinfo (several errors). * 01-04-99 GJF Changes for 64-bit size_t. * 01-18-99 GJF In MT models, setlocale needs to check if the old * __ptlocinfo needs to be freed up. Also, unrelated, * have _setlocale_get_all return NULL if malloc fails. * 03-02-99 KRS Partially back out previous fix for now. (per BryanT) * 03-20-99 GJF Added more reference counters (restoring fix) * 04-24-99 PML Added __lconv_intl_refcount * 01-26-00 GB Modified _setlocale_cat. Added _first_127char, * _ctype_loc_style and __lc_clike * 08-18-00 GB Fixed problems with __lc_clike stuff. * 09-06-00 GB Made pwctype independent of locale. * 10-12-00 GB Compared requested locale to current locale for * requested category in setlocale-set_cat. Performance * enhancement. * 11-05-00 PML Fixed double-free of __lconv_mon_refcount and * lconv_num_refcount (vs7#181380) * 01-29-01 GB Added _func function version of data variable used in * msvcprt.lib to work with STATIC_CPPLIB * 07-31-01 PML setlocale(...,NULL) needs to be under lock (vs7#283330) * 02-20-02 BWT prefast fixes - don't return from try block * *******************************************************************************/
#include <locale.h>
#if !defined(_WIN32) && !defined(_POSIX_)
static char _clocalestr[] = "C";
#else /* _WIN32/_POSIX_ */
#include <cruntime.h>
#include <mtdll.h>
#include <malloc.h>
#include <string.h>
#include <stdarg.h>
#include <stdlib.h> /* for strtol */
#include <setlocal.h>
#include <dbgint.h>
#include <ctype.h>
#include <awint.h>
/* C locale */ static char _clocalestr[] = "C";
#ifdef _POSIX_
static char _posixlocalestr[] = "POSIX"; #endif
#define _LOC_CCACHE 5 // Cache of last 5 locale changed and if they are clike.
__declspec(selectany) struct { const char * catname; char * locale; int (* init)(void); } __lc_category[LC_MAX-LC_MIN+1] = { /* code assumes locale initialization is "_clocalestr" */ { "LC_ALL", NULL, __init_dummy /* never called */ }, { "LC_COLLATE", _clocalestr, __init_collate }, { "LC_CTYPE", _clocalestr, __init_ctype }, { "LC_MONETARY",_clocalestr, __init_monetary }, { "LC_NUMERIC", _clocalestr, __init_numeric }, { "LC_TIME", _clocalestr, __init_time } };
struct _is_ctype_compatible { unsigned long id; int is_clike; }; /* First 127 character type for CLOCALE */ static const short _ctype_loc_style[] = { _CONTROL, _CONTROL, _CONTROL, _CONTROL, _CONTROL, _CONTROL, _CONTROL, _CONTROL, _SPACE | _CONTROL | _BLANK, _SPACE | _CONTROL, _SPACE | _CONTROL, _SPACE | _CONTROL, _SPACE | _CONTROL, _CONTROL, _CONTROL, _CONTROL, _CONTROL, _CONTROL, _CONTROL, _CONTROL, _CONTROL, _CONTROL, _CONTROL, _CONTROL, _CONTROL, _CONTROL, _CONTROL, _CONTROL, _CONTROL, _CONTROL, _CONTROL, _SPACE | _BLANK, _PUNCT, _PUNCT, _PUNCT, _PUNCT, _PUNCT, _PUNCT, _PUNCT, _PUNCT, _PUNCT, _PUNCT, _PUNCT, _PUNCT, _PUNCT, _PUNCT, _PUNCT, _DIGIT | _HEX, _DIGIT | _HEX, _DIGIT | _HEX, _DIGIT | _HEX, _DIGIT | _HEX, _DIGIT | _HEX, _DIGIT | _HEX, _DIGIT | _HEX, _DIGIT | _HEX, _DIGIT | _HEX, _PUNCT, _PUNCT, _PUNCT, _PUNCT, _PUNCT, _PUNCT, _PUNCT, _UPPER | _HEX | C1_ALPHA, _UPPER | _HEX | C1_ALPHA, _UPPER | _HEX | C1_ALPHA, _UPPER | _HEX | C1_ALPHA, _UPPER | _HEX | C1_ALPHA, _UPPER | _HEX | C1_ALPHA, _UPPER | C1_ALPHA, _UPPER | C1_ALPHA, _UPPER | C1_ALPHA, _UPPER | C1_ALPHA, _UPPER | C1_ALPHA, _UPPER | C1_ALPHA, _UPPER | C1_ALPHA, _UPPER | C1_ALPHA, _UPPER | C1_ALPHA, _UPPER | C1_ALPHA, _UPPER | C1_ALPHA, _UPPER | C1_ALPHA, _UPPER | C1_ALPHA, _UPPER | C1_ALPHA, _UPPER | C1_ALPHA, _UPPER | C1_ALPHA, _UPPER | C1_ALPHA, _UPPER | C1_ALPHA, _UPPER | C1_ALPHA, _UPPER | C1_ALPHA, _PUNCT, _PUNCT, _PUNCT, _PUNCT, _PUNCT, _PUNCT, _LOWER | _HEX | C1_ALPHA, _LOWER | _HEX | C1_ALPHA, _LOWER | _HEX | C1_ALPHA, _LOWER | _HEX | C1_ALPHA, _LOWER | _HEX | C1_ALPHA, _LOWER | _HEX | C1_ALPHA, _LOWER | C1_ALPHA, _LOWER | C1_ALPHA, _LOWER | C1_ALPHA, _LOWER | C1_ALPHA, _LOWER | C1_ALPHA, _LOWER | C1_ALPHA, _LOWER | C1_ALPHA, _LOWER | C1_ALPHA, _LOWER | C1_ALPHA, _LOWER | C1_ALPHA, _LOWER | C1_ALPHA, _LOWER | C1_ALPHA, _LOWER | C1_ALPHA, _LOWER | C1_ALPHA, _LOWER | C1_ALPHA, _LOWER | C1_ALPHA, _LOWER | C1_ALPHA, _LOWER | C1_ALPHA, _LOWER | C1_ALPHA, _LOWER | C1_ALPHA, _PUNCT, _PUNCT, _PUNCT, _PUNCT, _CONTROL };
static const char _first_127char[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100,101,102, 103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119, 120,121,122,123,124,125,126,127 };
#ifdef _MT
extern unsigned short *__ctype1; /* defined in initctyp.c */ extern struct __lc_time_data __lc_time_c; /* defined in strftime.c */ extern struct __lc_time_data *__lc_time_curr; /* defined in strftime.c */ extern struct __lc_time_data *__lc_time_intl; /* defined in inittime.c */
/*
* initial locale information struct, set to the C locale. Used only until the * first call to setlocale() */ threadlocinfo __initiallocinfo = { 1, /* refcount */ _CLOCALECP, /* lc_codepage */ _CLOCALECP, /* lc_collate_cp */ { _CLOCALEHANDLE, /* lc_handle[6] */ _CLOCALEHANDLE, _CLOCALEHANDLE, _CLOCALEHANDLE, _CLOCALEHANDLE, _CLOCALEHANDLE }, 1, /* lc_clike */ 1, /* mb_cur_max */ NULL, /* lconv_intl_refcount */ NULL, /* lconv_num_refcount */ NULL, /* lconv_mon_refcount */ &__lconv_c, /* lconv */ NULL, /* lconv_intl */ NULL, /* ctype1_refcount */ NULL, /* ctype1 */ _ctype + 1, /* pctype */ &__lc_time_c, /* lc_time_curr */ NULL /* lc_time_intl */ };
/*
* global pointer to the current per-thread locale information structure. */ pthreadlocinfo __ptlocinfo = &__initiallocinfo;
/*
* Flag indicating whether or not setlocale() is active. Its value is the * number of setlocale() calls currently active. */ _CRTIMP int __setlc_active; /* These functions are for enabling STATIC_CPPLIB functionality */ _CRTIMP int __cdecl ___setlc_active_func(void) { return __setlc_active; }
/*
* Flag indicating whether or not a function which references the locale * without having locked it is active. Its value is the number of such * functions. */ _CRTIMP int __unguarded_readlc_active; /* These functions are for enabling STATIC_CPPLIB functionality */ _CRTIMP int * __cdecl ___unguarded_readlc_active_add_func(void) { return &__unguarded_readlc_active; }
#endif /* _MT */
/* helper function prototypes */ char * _expandlocale(char *, char *, LC_ID *, UINT *, int); void _strcats(char *, int, ...); void __lc_lctostr(char *, const LC_STRINGS *); int __lc_strtolc(LC_STRINGS *, const char *); static char * __cdecl _setlocale_set_cat(int, const char *); static char * __cdecl _setlocale_get_all(void);
#ifdef _MT
extern int * __lconv_intl_refcount; extern int * __lconv_num_refcount; extern int * __lconv_mon_refcount; extern int * __ctype1_refcount; static pthreadlocinfo __cdecl __updatetlocinfo_lk(void); static char * __cdecl _setlocale_lk(int, const char *); void __cdecl __free_lconv_mon(struct lconv *); void __cdecl __free_lconv_num(struct lconv *); void __cdecl __free_lc_time(struct __lc_time_data *); #endif
#endif /* _WIN32/_POSIX_ */
#ifdef _MT
/***
*__freetlocinfo() - free threadlocinfo * *Purpose: * Free up the per-thread locale info structure specified by the passed * pointer. * *Entry: * pthreadlocinfo ptloci * *Exit: * *Exceptions: * *******************************************************************************/
void __cdecl __freetlocinfo ( pthreadlocinfo ptloci ) { /*
* Free up lconv struct */ if ( (ptloci->lconv_intl != __lconv_intl) && (ptloci->lconv_intl != NULL) && (*(ptloci->lconv_intl_refcount) == 0)) { if ( (ptloci->lconv_mon_refcount != NULL) && (*(ptloci->lconv_mon_refcount) == 0) && (ptloci->lconv_mon_refcount != __lconv_mon_refcount) ) { _free_crt(ptloci->lconv_mon_refcount); __free_lconv_mon(ptloci->lconv_intl); }
if ( (ptloci->lconv_num_refcount != NULL) && (*(ptloci->lconv_num_refcount) == 0) && (ptloci->lconv_num_refcount != __lconv_num_refcount) ) { _free_crt(ptloci->lconv_num_refcount); __free_lconv_num(ptloci->lconv_intl); }
_free_crt(ptloci->lconv_intl_refcount); _free_crt(ptloci->lconv_intl); }
/*
* Free up ctype tables */ if ( (ptloci->ctype1_refcount != __ctype1_refcount) && (ptloci->ctype1_refcount != NULL) && (*(ptloci->ctype1_refcount) == 0) ) { _free_crt(ptloci->ctype1_refcount); _free_crt(ptloci->ctype1 - _COFFSET); }
/*
* Free up the __lc_time_data struct */ if ( (ptloci->lc_time_intl != __lc_time_intl) && (ptloci->lc_time_intl != NULL) && ((ptloci->lc_time_intl->refcount) == 0) ) { __free_lc_time(ptloci->lc_time_intl); _free_crt(ptloci->lc_time_intl); }
/*
* Free up the threadlocinfo struct */ _free_crt(ptloci); }
/***
*__updatetlocinfo() - refresh the thread's locale info * *Purpose: * Update the current thread's reference to the locale information to * match the current global locale info. Decrement the reference on the * old locale information struct and if this count is now zero (so that no * threads are using it), free it. * *Entry: * *Exit: * _getptd()->ptlocinfo == __ptlocinfo * *Exceptions: * *******************************************************************************/
pthreadlocinfo __cdecl __updatetlocinfo(void) { pthreadlocinfo ptloci;
_mlock(_SETLOCALE_LOCK); __try { ptloci = __updatetlocinfo_lk(); } __finally { _munlock(_SETLOCALE_LOCK); }
return ptloci; }
static pthreadlocinfo __cdecl __updatetlocinfo_lk(void) { pthreadlocinfo ptloci; _ptiddata ptd = _getptd();
if ( (ptloci = ptd->ptlocinfo) != __ptlocinfo ) { /*
* Decrement the reference counts in the old locale info * structure. */ if ( ptloci != NULL ) { (ptloci->refcount)--;
if ( ptloci->lconv_intl_refcount != NULL ) (*(ptloci->lconv_intl_refcount))--;
if ( ptloci->lconv_mon_refcount != NULL ) (*(ptloci->lconv_mon_refcount))--;
if ( ptloci->lconv_num_refcount != NULL ) (*(ptloci->lconv_num_refcount))--;
if ( ptloci->ctype1_refcount != NULL ) (*(ptloci->ctype1_refcount))--;
(ptloci->lc_time_curr->refcount)--; }
/*
* Update to the current locale info structure and increment the * reference counts. */ ptd->ptlocinfo = __ptlocinfo; (__ptlocinfo->refcount)++;
if ( __ptlocinfo->lconv_intl_refcount != NULL ) (*(__ptlocinfo->lconv_intl_refcount))++;
if ( __ptlocinfo->lconv_mon_refcount != NULL ) (*(__ptlocinfo->lconv_mon_refcount))++;
if ( __ptlocinfo->lconv_num_refcount != NULL ) (*(__ptlocinfo->lconv_num_refcount))++;
if ( __ptlocinfo->ctype1_refcount != NULL ) (*(__ptlocinfo->ctype1_refcount))++;
(__ptlocinfo->lc_time_curr->refcount)++;
/*
* Free the old locale info structure, if necessary. Must be done * after incrementing reference counts in current locale in case * any refcounts are shared with the old locale. */ if ( (ptloci != NULL) && (ptloci->refcount == 0) && (ptloci != &__initiallocinfo) ) __freetlocinfo(ptloci); }
return ptd->ptlocinfo; }
#endif
/***
*char * setlocale(int category, char *locale) - Set one or all locale categories * *Purpose: * The setlocale() routine allows the user to set one or more of * the locale categories to the specific locale selected by the * user. [ANSI] * * NOTE: Under !_INTL, the C libraries only support the "C" locale. * Attempts to change the locale will fail. * *Entry: * int category = One of the locale categories defined in locale.h * char *locale = String identifying a specific locale or NULL to * query the current locale. * *Exit: * If supplied locale pointer == NULL: * * Return pointer to current locale string and do NOT change * the current locale. * * If supplied locale pointer != NULL: * * If locale string is '\0', set locale to default. * * If desired setting can be honored, return a pointer to the * locale string for the appropriate category. * * If desired setting can NOT be honored, return NULL. * *Exceptions: * Compound locale strings of the form "LC_COLLATE=xxx;LC_CTYPE=xxx;..." * are allowed for the LC_ALL category. This is to support the ability * to restore locales with the returned string, as specified by ANSI. * Setting the locale with a compound locale string will succeed unless * *all* categories failed. The returned string will reflect the current * locale. For example, if LC_CTYPE fails in the above string, setlocale * will return "LC_COLLATE=xxx;LC_CTYPE=yyy;..." where yyy is the * previous locale (or the C locale if restoring the previous locale * also failed). Unrecognized LC_* categories are ignored. * *******************************************************************************/
#if !defined(_WIN32) && !defined(_POSIX_) /* trivial ANSI support */
char * __cdecl setlocale ( int _category, const char *_locale ) { if ( (_locale == NULL) || (_locale[0] == '\0') || ( (_locale[0]=='C') && (_locale[1]=='\0')) ) return(_clocalestr); else return(NULL); }
#else /* _WIN32/_POSIX_ */
char * __cdecl setlocale ( int _category, const char *_locale ) { char * retval; #ifdef _MT
pthreadlocinfo ptloci; int i;
/* Validate category */ if ( (_category < LC_MIN) || (_category > LC_MAX) ) return NULL;
_mlock(_SETLOCALE_LOCK);
__try {
if ( _locale == NULL ) { retval = _setlocale_lk(_category, NULL); __leave; }
if ( (ptloci = _malloc_crt( sizeof(threadlocinfo) )) == NULL ) retval = NULL;
if ( (ptloci != NULL) && (retval = _setlocale_lk(_category, _locale)) ) { ptloci->refcount = 0; ptloci->lc_codepage = __lc_codepage; ptloci->lc_collate_cp = __lc_collate_cp;
for ( i = 0 ; i <= LC_MAX - LC_MIN ; i++ ) ptloci->lc_handle[i] = __lc_handle[i];
ptloci->lc_clike = __lc_clike; ptloci->mb_cur_max = __mb_cur_max;
ptloci->lconv_intl_refcount = __lconv_intl_refcount; ptloci->lconv_num_refcount = __lconv_num_refcount; ptloci->lconv_mon_refcount = __lconv_mon_refcount; ptloci->lconv = __lconv; ptloci->lconv_intl = __lconv_intl;
ptloci->ctype1_refcount = __ctype1_refcount; ptloci->ctype1 = __ctype1; ptloci->pctype = _pctype;
ptloci->lc_time_curr = __lc_time_curr; ptloci->lc_time_intl = __lc_time_intl;
if ( (__ptlocinfo->refcount == 0) && (__ptlocinfo != &__initiallocinfo) ) __freetlocinfo(__ptlocinfo);
__ptlocinfo = ptloci;
(void)__updatetlocinfo_lk(); }
if ( (retval == NULL) && (ptloci != NULL) ) _free_crt(ptloci);
} __finally { _munlock(_SETLOCALE_LOCK); }
return retval; }
static char * __cdecl _setlocale_lk( int _category, const char *_locale ) { char * retval; #else
/* Validate category */ if ((_category < LC_MIN) || (_category > LC_MAX)) return NULL; #endif
/* Interpret locale */
if (_category != LC_ALL) { retval = (_locale) ? _setlocale_set_cat(_category,_locale) : __lc_category[_category].locale;
} else { /* LC_ALL */ char lctemp[MAX_LC_LEN]; int i; int same = 1; int fLocaleSet = 0; /* flag to indicate if anything successfully set */
if (_locale != NULL) { if ( (_locale[0]=='L') && (_locale[1]=='C') && (_locale[2]=='_') ) { /* parse compound locale string */ size_t len; const char * p = _locale; /* start of string to parse */ const char * s;
do { s = strpbrk(p,"=;");
if ((s==(char *)NULL) || (!(len=(size_t)(s-p))) || (*s==';')) return NULL; /* syntax error */
/* match with known LC_ strings, if possible, else ignore */ for (i=LC_ALL+1; i<=LC_MAX; i++) { if ((!strncmp(__lc_category[i].catname,p,len)) && (len==strlen(__lc_category[i].catname))) { break; /* matched i */ } } /* no match if (i>LC_MAX) -- just ignore */
if ((!(len = strcspn(++s,";"))) && (*s!=';')) return NULL; /* syntax error */
if (i<=LC_MAX) { strncpy(lctemp, s, len); lctemp[len]='\0'; /* null terminate string */
/* don't fail unless all categories fail */ if (_setlocale_set_cat(i,lctemp)) fLocaleSet++; /* record a success */ } if (*(p = s+len)!='\0') p++; /* skip ';', if present */
} while (*p);
retval = (fLocaleSet) ? _setlocale_get_all() : NULL;
} else { /* simple LC_ALL locale string */
/* confirm locale is supported, get expanded locale */ if (retval = _expandlocale((char *)_locale, lctemp, NULL, NULL, _category)) { for (i=LC_MIN; i<=LC_MAX; i++) { if (i!=LC_ALL) { if (strcmp(lctemp, __lc_category[i].locale)) { if (_setlocale_set_cat(i, lctemp)) { fLocaleSet++; /* record a success */ } else { same = 0; /* record a failure */ } } else fLocaleSet++; /* trivial succcess */ } } #ifdef _POSIX_
/* special case for POSIX - since LC_ALL expands,
one LC_ALL call may set many different categories, must assume not same, get full string */ same = 0; #endif
if (same) /* needn't call setlocale_get_all() if all the same */ { retval = _setlocale_get_all(); /* retval set above */ _free_crt(__lc_category[LC_ALL].locale); __lc_category[LC_ALL].locale = NULL; } else retval = (fLocaleSet) ? _setlocale_get_all() : NULL; } } } else { /* LC_ALL & NULL */ retval = _setlocale_get_all (); } }
/* common exit point */ return retval;
} /* setlocale */
static char * __cdecl _setlocale_set_cat ( int category, const char * locale ) { char * oldlocale; LCID oldhandle; UINT oldcodepage; LC_ID oldid;
LC_ID idtemp; UINT cptemp; char lctemp[MAX_LC_LEN]; char * pch; static struct _is_ctype_compatible _Lcid_c[_LOC_CCACHE] = {{0,1}}; struct _is_ctype_compatible buf1, buf2; int i; short out[sizeof(_first_127char)];
if (!_expandlocale((char *)locale, lctemp, &idtemp, &cptemp, category)) { return NULL; /* unrecognized locale */ } if (!strcmp(lctemp, __lc_category[category].locale)) { return __lc_category[category].locale; }
if (!(pch = (char *)_malloc_crt(strlen(lctemp)+1))) { return NULL; /* error if malloc fails */ }
oldlocale = __lc_category[category].locale; /* save for possible restore*/ oldhandle = __lc_handle[category]; memcpy((void *)&oldid, (void *)&__lc_id[category], sizeof(oldid)); oldcodepage = __lc_codepage;
/* update locale string */ __lc_category[category].locale = strcpy(pch,lctemp); __lc_handle[category] = MAKELCID(idtemp.wLanguage, SORT_DEFAULT); memcpy((void *)&__lc_id[category], (void *)&idtemp, sizeof(idtemp));
/* To speedup locale based comparisions, we identify if the current
* local has first 127 character set same as CLOCALE. If yes then * __lc_clike = TRUE. Also we keep this info. in a cache of cache * size = _LOC_CCACHE, so that every time the locale is switched, we * don't have to call time consuming GetStringTypeA. */
if (category==LC_CTYPE) { __lc_codepage = cptemp; buf1 = _Lcid_c[_LOC_CCACHE -1]; /* brings the recently used codepage to the top. or else shifts
* every thing down by one so that new _Lcid_c can be placed at * the top. */ for ( i = 0; i < _LOC_CCACHE; i++) { if (__lc_codepage == _Lcid_c[i].id) { _Lcid_c[0] = _Lcid_c[i]; _Lcid_c[i] = buf1; break; } else { buf2 = _Lcid_c[i]; _Lcid_c[i] = buf1; buf1 = buf2; } } if ( i == _LOC_CCACHE) { if ( __crtGetStringTypeA(CT_CTYPE1, _first_127char, sizeof(_first_127char), out, __lc_codepage, __lc_handle[LC_CTYPE], TRUE )) { if ( !memcmp(out, _ctype_loc_style, sizeof(_ctype_loc_style))) _Lcid_c[0].is_clike = TRUE; else _Lcid_c[0].is_clike = FALSE; } else _Lcid_c[0].is_clike = FALSE; _Lcid_c[0].id = __lc_codepage; } __lc_clike = _Lcid_c[0].is_clike; }
if ( category == LC_COLLATE ) __lc_collate_cp = cptemp;
if (__lc_category[category].init()) { /* restore previous state! */ __lc_category[category].locale = oldlocale; _free_crt(pch); __lc_handle[category] = oldhandle; __lc_codepage = oldcodepage;
return NULL; /* error if non-zero return */ }
/* locale set up successfully */ /* Cleanup */ if ((oldlocale != _clocalestr) #ifdef _POSIX_
&& (oldlocale!=_posixlocalestr) #endif
) _free_crt(oldlocale);
return __lc_category[category].locale;
} /* _setlocale_set_cat */
static char * __cdecl _setlocale_get_all ( void ) { int i; int same = 1; /* allocate memory if necessary */ if ( (__lc_category[LC_ALL].locale == NULL) && ((__lc_category[LC_ALL].locale = _malloc_crt((MAX_LC_LEN+1) * (LC_MAX-LC_MIN+1) + CATNAMES_LEN)) == NULL) ) return NULL;
__lc_category[LC_ALL].locale[0] = '\0'; for (i=LC_MIN+1; ; i++) { _strcats(__lc_category[LC_ALL].locale, 3, __lc_category[i].catname,"=",__lc_category[i].locale); if (i<LC_MAX) { strcat(__lc_category[LC_ALL].locale,";"); if (strcmp(__lc_category[i].locale, __lc_category[i+1].locale)) same=0; } else { if (!same) return __lc_category[LC_ALL].locale; else { _free_crt(__lc_category[LC_ALL].locale); __lc_category[LC_ALL].locale = (char *)NULL; return __lc_category[LC_CTYPE].locale; } } } } /* _setlocale_get_all */
char * _expandlocale ( char *expr, char * output, LC_ID * id, UINT * cp, int category ) { static LC_ID cacheid = {0, 0, 0}; static UINT cachecp = 0; static char cachein[MAX_LC_LEN] = "C"; static char cacheout[MAX_LC_LEN] = "C";
if (!expr) return NULL; /* error if no input */
#ifdef _POSIX_
if (!*expr) { /* POSIX: when locale=="", look first at the environment variables:
1) use LC_ALL EV if defined and not null (LC_ALL expands to LC_*) 2) use EV that matches category and is not null 3) use LANG EV if defined and not null otherwise, we fall through to get system default */
char *envar;
if (category == LC_ALL && (envar = getenv("LC_ALL"))) { if (!*envar) { /* LC_ALL expands to LC_*, set output to "", each category will be
expanded individually */ *output = '\0'; return output; } else { expr = envar; } } else { if ((envar = getenv(__lc_category[category].catname)) && *envar || (envar = getenv("LANG")) && *envar) { expr = envar; } } } #endif /* _POSIX_ */
if (((*expr=='C') && (!expr[1])) #ifdef _POSIX_
|| (!strcmp(expr, _posixlocalestr)) #endif
) /* for "C" locale, just return */ { #ifdef _POSIX_
strcpy(output, _posixlocalestr); #else
*output = 'C'; output[1] = '\0'; #endif
if (id) { id->wLanguage = 0; id->wCountry = 0; id->wCodePage = 0; } if (cp) { *cp = CP_ACP; /* return to ANSI code page */ } return output; /* "C" */ }
/* first, make sure we didn't just do this one */ if (strcmp(cacheout,expr) && strcmp(cachein,expr)) { /* do some real work */ LC_STRINGS names;
if (__lc_strtolc((LC_STRINGS *)&names, (const char *)expr)) return NULL; /* syntax error */
if (!__get_qualified_locale((LPLC_STRINGS)&names, (LPLC_ID)&cacheid, (LPLC_STRINGS)&names)) return NULL; /* locale not recognized/supported */
/* begin: cache atomic section */
cachecp = cacheid.wCodePage;
__lc_lctostr((char *)cacheout, &names);
/* Don't cache "" empty string */ if (*expr) strcpy(cachein, expr); else strcpy(cachein, cacheout);
/* end: cache atomic section */ } if (id) memcpy((void *)id, (void *)&cacheid, sizeof(cacheid)); /* possibly return LC_ID */ if (cp) memcpy((void *)cp, (void *)&cachecp, sizeof(cachecp)); /* possibly return cp */
strcpy(output,cacheout); return cacheout; /* return fully expanded locale string */ }
/* helpers */
int __cdecl __init_dummy(void) /* default routine for locale initializer */ { return 0; }
void _strcats ( char *outstr, int n, ... ) { int i; va_list substr;
va_start (substr, n);
for (i =0; i<n; i++) { strcat(outstr, va_arg(substr, char *)); } va_end(substr); }
int __lc_strtolc ( LC_STRINGS *names, const char *locale ) { int i; size_t len; char ch;
memset((void *)names, '\0', sizeof(LC_STRINGS)); /* clear out result */
if (*locale=='\0') return 0; /* trivial case */
/* only code page is given */ if (locale[0] == '.' && locale[1] != '\0') { strcpy((char *)names->szCodePage, &locale[1]); return 0; }
for (i=0; ; i++) { if (!(len=strcspn(locale,"_.,"))) return -1; /* syntax error */
ch = locale[len];
if ((i==0) && (len<MAX_LANG_LEN) && (ch!='.')) strncpy((char *)names->szLanguage, locale, len);
else if ((i==1) && (len<MAX_CTRY_LEN) && (ch!='_')) strncpy((char *)names->szCountry, locale, len);
else if ((i==2) && (len<MAX_CP_LEN) && (ch=='\0' || ch==',')) strncpy((char *)names->szCodePage, locale, len);
else return -1; /* error parsing locale string */
if (ch==',') { /* modifier not used in current implementation, but it
must be parsed to for POSIX/XOpen conformance */ /* strncpy(names->szModifier, locale, MAX_MODIFIER_LEN-1); */ break; }
if (!ch) break; locale+=(len+1); } return 0; }
void __lc_lctostr ( char *locale, const LC_STRINGS *names ) { strcpy(locale, (char *)names->szLanguage); if (*(names->szCountry)) _strcats(locale, 2, "_", names->szCountry); if (*(names->szCodePage)) _strcats(locale, 2, ".", names->szCodePage); /* if (names->szModifier)
_strcats(locale, 2, ",", names->szModifier); */ }
#endif /* _WIN32/_POSIX_ */
|