Author: fireball Date: Tue May 27 03:11:06 2008 New Revision: 33724
URL: http://svn.reactos.org/svn/reactos?rev=33724&view=rev Log: Daniel Zimmermann netzimme@aim.com - Sync setlocale from Wine. See issue #3254 for more details.
Modified: trunk/reactos/dll/win32/msvcrt/msvcrt.def trunk/reactos/dll/win32/msvcrt/stubs.c trunk/reactos/lib/sdk/crt/locale/locale.c
Modified: trunk/reactos/dll/win32/msvcrt/msvcrt.def URL: http://svn.reactos.org/svn/reactos/trunk/reactos/dll/win32/msvcrt/msvcrt.def... ============================================================================== --- trunk/reactos/dll/win32/msvcrt/msvcrt.def [iso-8859-1] (original) +++ trunk/reactos/dll/win32/msvcrt/msvcrt.def [iso-8859-1] Tue May 27 03:11:06 2008 @@ -554,7 +554,7 @@ _write @548 _wrmdir @549 _wsearchenv @550 - _wsetlocale=__wine_stub_msvcrt_dll_551 @551 + _wsetlocale @551 _wsopen @552 _wspawnl=__wine_stub_msvcrt_dll_553 @553 _wspawnle=__wine_stub_msvcrt_dll_554 @554
Modified: trunk/reactos/dll/win32/msvcrt/stubs.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/dll/win32/msvcrt/stubs.c?re... ============================================================================== --- trunk/reactos/dll/win32/msvcrt/stubs.c [iso-8859-1] (original) +++ trunk/reactos/dll/win32/msvcrt/stubs.c [iso-8859-1] Tue May 27 03:11:06 2008 @@ -84,7 +84,6 @@ void __wine_stub_msvcrt_dll_455(void) { __wine_spec_unimplemented_stub(__wine_spec_file_name, "_stat64"); } void __wine_stub_msvcrt_dll_542(void) { __wine_spec_unimplemented_stub(__wine_spec_file_name, "_wperror"); } void __wine_stub_msvcrt_dll_543(void) { __wine_spec_unimplemented_stub(__wine_spec_file_name, "_wpgmptr"); } -void __wine_stub_msvcrt_dll_551(void) { __wine_spec_unimplemented_stub(__wine_spec_file_name, "_wsetlocale"); } void __wine_stub_msvcrt_dll_553(void) { __wine_spec_unimplemented_stub(__wine_spec_file_name, "_wspawnl"); } void __wine_stub_msvcrt_dll_554(void) { __wine_spec_unimplemented_stub(__wine_spec_file_name, "_wspawnle"); } void __wine_stub_msvcrt_dll_555(void) { __wine_spec_unimplemented_stub(__wine_spec_file_name, "_wspawnlp"); }
Modified: trunk/reactos/lib/sdk/crt/locale/locale.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/lib/sdk/crt/locale/locale.c... ============================================================================== --- trunk/reactos/lib/sdk/crt/locale/locale.c [iso-8859-1] (original) +++ trunk/reactos/lib/sdk/crt/locale/locale.c [iso-8859-1] Tue May 27 03:11:06 2008 @@ -6,53 +6,503 @@
#include <precomp.h> #include <locale.h> -#include <internal/tls.h> +#include <internal/mtdll.h> +
#define NDEBUG #include <internal/debug.h> + +// mtdll.h +#define _SETLOCALE_LOCK 19 + +// msvcrt.h +#define MSVCRT_LC_ALL 0 +#define MSVCRT_LC_COLLATE 1 +#define MSVCRT_LC_CTYPE 2 +#define MSVCRT_LC_MONETARY 3 +#define MSVCRT_LC_NUMERIC 4 +#define MSVCRT_LC_TIME 5 +#define MSVCRT_LC_MIN MSVCRT_LC_ALL +#define MSVCRT_LC_MAX MSVCRT_LC_TIME + +/* FIXME: Need to hold locale for each LC_* type and aggregate + * string to produce lc_all. + */ +#define MAX_ELEM_LEN 64 /* Max length of country/language/CP string */ +#define MAX_LOCALE_LENGTH 256 +char MSVCRT_current_lc_all[MAX_LOCALE_LENGTH]; +LCID MSVCRT_current_lc_all_lcid; +int MSVCRT___lc_codepage; +int MSVCRT___lc_collate_cp; +HANDLE MSVCRT___lc_handle[MSVCRT_LC_MAX - MSVCRT_LC_MIN + 1]; + +/* MT */ +#define LOCK_LOCALE _mlock(_SETLOCALE_LOCK); +#define UNLOCK_LOCALE _munlock(_SETLOCALE_LOCK); + +#define MSVCRT_LEADBYTE 0x8000 + +typedef struct { + char search_language[MAX_ELEM_LEN]; + char search_country[MAX_ELEM_LEN]; + char search_codepage[MAX_ELEM_LEN]; + char found_language[MAX_ELEM_LEN]; + char found_country[MAX_ELEM_LEN]; + char found_codepage[MAX_ELEM_LEN]; + unsigned int match_flags; + LANGID found_lang_id; +} locale_search_t;
unsigned int __setlc_active; unsigned int __unguarded_readlc_active; int _current_category; /* used by setlocale */ const char *_current_locale;
+ int parse_locale(const char *locale, char *lang, char *country, char *code_page); + +#define _C_ _CONTROL +#define _S_ _SPACE +#define _P_ _PUNCT +#define _D_ _DIGIT +#define _H_ _HEX +#define _U_ _UPPER +#define _L_ _LOWER + +WORD MSVCRT__ctype [257] = { + 0, _C_, _C_, _C_, _C_, _C_, _C_, _C_, _C_, _C_, _S_|_C_, _S_|_C_, + _S_|_C_, _S_|_C_, _S_|_C_, _C_, _C_, _C_, _C_, _C_, _C_, _C_, _C_, + _C_, _C_, _C_, _C_, _C_, _C_, _C_, _C_, _C_, _C_, _S_|_BLANK, + _P_, _P_, _P_, _P_, _P_, _P_, _P_, _P_, _P_, _P_, _P_, _P_, _P_, _P_, + _P_, _D_|_H_, _D_|_H_, _D_|_H_, _D_|_H_, _D_|_H_, _D_|_H_, _D_|_H_, + _D_|_H_, _D_|_H_, _D_|_H_, _P_, _P_, _P_, _P_, _P_, _P_, _P_, _U_|_H_, + _U_|_H_, _U_|_H_, _U_|_H_, _U_|_H_, _U_|_H_, _U_, _U_, _U_, _U_, _U_, + _U_, _U_, _U_, _U_, _U_, _U_, _U_, _U_, _U_, _U_, _U_, _U_, _U_, _U_, + _U_, _P_, _P_, _P_, _P_, _P_, _P_, _L_|_H_, _L_|_H_, _L_|_H_, _L_|_H_, + _L_|_H_, _L_|_H_, _L_, _L_, _L_, _L_, _L_, _L_, _L_, _L_, _L_, _L_, + _L_, _L_, _L_, _L_, _L_, _L_, _L_, _L_, _L_, _L_, _P_, _P_, _P_, _P_, + _C_, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* Internal: Current ctype table for locale */ +WORD MSVCRT_current_ctype[257]; + +/* pctype is used by macros in the Win32 headers. It must point + * To a table of flags exactly like ctype. To allow locale + * changes to affect ctypes (i.e. isleadbyte), we use a second table + * and update its flags whenever the current locale changes. + */ +WORD* MSVCRT__pctype = MSVCRT_current_ctype + 1; + +/* Friendly country strings & iso codes for synonym support. + * Based on MS documentation for setlocale(). + */ +static const char * const _country_synonyms[] = +{ + "Hong Kong","HK", + "Hong-Kong","HK", + "New Zealand","NZ", + "New-Zealand","NZ", + "PR China","CN", + "PR-China","CN", + "United Kingdom","GB", + "United-Kingdom","GB", + "Britain","GB", + "England","GB", + "Great Britain","GB", + "United States","US", + "United-States","US", + "America","US" +}; + +/* Note: Flags are weighted in order of matching importance */ +#define FOUND_LANGUAGE 0x4 +#define FOUND_COUNTRY 0x2 +#define FOUND_CODEPAGE 0x1 + +/* INTERNAL: Map a synonym to an ISO code */ +static void remap_synonym(char *name) +{ + size_t i; + for (i = 0; i < sizeof(_country_synonyms)/sizeof(char*); i += 2 ) + { + if (!strcasecmp(_country_synonyms[i],name)) + { + TRACE(":Mapping synonym %s to %s\n",name,_country_synonyms[i+1]); + name[0] = _country_synonyms[i+1][0]; + name[1] = _country_synonyms[i+1][1]; + name[2] = '\0'; + return; + } + } +} + +#define CONTINUE_LOOKING TRUE +#define STOP_LOOKING FALSE + +/* INTERNAL: Get and compare locale info with a given string */ +static int compare_info(LCID lcid, DWORD flags, char* buff, const char* cmp) +{ + buff[0] = 0; + GetLocaleInfoA(lcid, flags|LOCALE_NOUSEROVERRIDE,buff, MAX_ELEM_LEN); + if (!buff[0] || !cmp[0]) + return 0; + /* Partial matches are allowed, e.g. "Germ" matches "Germany" */ + return !strncasecmp(cmp, buff, strlen(cmp)); +} + + +static BOOL CALLBACK +find_best_locale_proc(HMODULE hModule, LPCSTR type, LPCSTR name, WORD LangID, LONG_PTR lParam) +{ + locale_search_t *res = (locale_search_t *)lParam; + const LCID lcid = MAKELCID(LangID, SORT_DEFAULT); + char buff[MAX_ELEM_LEN]; + unsigned int flags = 0; + + if(PRIMARYLANGID(LangID) == LANG_NEUTRAL) + return CONTINUE_LOOKING; + + /* Check Language */ + if (compare_info(lcid,LOCALE_SISO639LANGNAME,buff,res->search_language) || + compare_info(lcid,LOCALE_SABBREVLANGNAME,buff,res->search_language) || + compare_info(lcid,LOCALE_SENGLANGUAGE,buff,res->search_language)) + { + TRACE(":Found language: %s->%s\n", res->search_language, buff); + flags |= FOUND_LANGUAGE; + memcpy(res->found_language,res->search_language,MAX_ELEM_LEN); + } + else if (res->match_flags & FOUND_LANGUAGE) + { + return CONTINUE_LOOKING; + } + + /* Check Country */ + if (compare_info(lcid,LOCALE_SISO3166CTRYNAME,buff,res->search_country) || + compare_info(lcid,LOCALE_SABBREVCTRYNAME,buff,res->search_country) || + compare_info(lcid,LOCALE_SENGCOUNTRY,buff,res->search_country)) + { + TRACE("Found country:%s->%s\n", res->search_country, buff); + flags |= FOUND_COUNTRY; + memcpy(res->found_country,res->search_country,MAX_ELEM_LEN); + } + else if (res->match_flags & FOUND_COUNTRY) + { + return CONTINUE_LOOKING; + } + + /* Check codepage */ + if (compare_info(lcid,LOCALE_IDEFAULTCODEPAGE,buff,res->search_codepage) || + (compare_info(lcid,LOCALE_IDEFAULTANSICODEPAGE,buff,res->search_codepage))) + { + TRACE("Found codepage:%s->%s\n", res->search_codepage, buff); + flags |= FOUND_CODEPAGE; + memcpy(res->found_codepage,res->search_codepage,MAX_ELEM_LEN); + } + else if (res->match_flags & FOUND_CODEPAGE) + { + return CONTINUE_LOOKING; + } + + if (flags > res->match_flags) + { + /* Found a better match than previously */ + res->match_flags = flags; + res->found_lang_id = LangID; + } + if (flags & (FOUND_LANGUAGE & FOUND_COUNTRY & FOUND_CODEPAGE)) + { + TRACE(":found exact locale match\n"); + return STOP_LOOKING; + } + return CONTINUE_LOOKING; +} + +/* Internal: Find the LCID for a locale specification */ +static LCID MSVCRT_locale_to_LCID(locale_search_t* locale) +{ + LCID lcid; + EnumResourceLanguagesA(GetModuleHandleA("KERNEL32"), (LPSTR)RT_STRING, + (LPCSTR)LOCALE_ILANGUAGE,find_best_locale_proc, + (LONG_PTR)locale); + + if (!locale->match_flags) + return 0; + + /* If we were given something that didn't match, fail */ + if (locale->search_country[0] && !(locale->match_flags & FOUND_COUNTRY)) + return 0; + + lcid = MAKELCID(locale->found_lang_id, SORT_DEFAULT); + + /* Populate partial locale, translating LCID to locale string elements */ + if (!locale->found_codepage[0]) + { + /* Even if a codepage is not enumerated for a locale + * it can be set if valid */ + if (locale->search_codepage[0]) + { + if (IsValidCodePage(atoi(locale->search_codepage))) + memcpy(locale->found_codepage,locale->search_codepage,MAX_ELEM_LEN); + else + { + /* Special codepage values: OEM & ANSI */ + if (strcasecmp(locale->search_codepage,"OCP")) + { + GetLocaleInfoA(lcid, LOCALE_IDEFAULTCODEPAGE, + locale->found_codepage, MAX_ELEM_LEN); + } + if (strcasecmp(locale->search_codepage,"ACP")) + { + GetLocaleInfoA(lcid, LOCALE_IDEFAULTANSICODEPAGE, + locale->found_codepage, MAX_ELEM_LEN); + } + else + return 0; + + if (!atoi(locale->found_codepage)) + return 0; + } + } + else + { + /* Prefer ANSI codepages if present */ + GetLocaleInfoA(lcid, LOCALE_IDEFAULTANSICODEPAGE, + locale->found_codepage, MAX_ELEM_LEN); + if (!locale->found_codepage[0] || !atoi(locale->found_codepage)) + GetLocaleInfoA(lcid, LOCALE_IDEFAULTCODEPAGE, + locale->found_codepage, MAX_ELEM_LEN); + } + } + GetLocaleInfoA(lcid, LOCALE_SENGLANGUAGE|LOCALE_NOUSEROVERRIDE, + locale->found_language, MAX_ELEM_LEN); + GetLocaleInfoA(lcid, LOCALE_SENGCOUNTRY|LOCALE_NOUSEROVERRIDE, + locale->found_country, MAX_ELEM_LEN); + return lcid; +} + +/* INTERNAL: Set ctype behaviour for a codepage */ +static void msvcrt_set_ctype(unsigned int codepage, LCID lcid) +{ + CPINFO cp; + + memset(&cp, 0, sizeof(CPINFO)); + + if (GetCPInfo(codepage, &cp)) + { + int i; + char str[3]; + unsigned char *traverse = (unsigned char *)cp.LeadByte; + + memset(MSVCRT_current_ctype, 0, sizeof(MSVCRT__ctype)); + MSVCRT___lc_codepage = codepage; + MSVCRT___lc_collate_cp = codepage; + + /* Switch ctype macros to MBCS if needed */ + __mb_cur_max = cp.MaxCharSize; + + /* Set remaining ctype flags: FIXME: faster way to do this? */ + str[1] = str[2] = 0; + for (i = 0; i < 256; i++) + { + if (!(MSVCRT__pctype[i] & MSVCRT_LEADBYTE)) + { + str[0] = i; + GetStringTypeA(lcid, CT_CTYPE1, str, 1, MSVCRT__pctype + i); + } + } + + /* Set leadbyte flags */ + while (traverse[0] || traverse[1]) + { + for( i = traverse[0]; i <= traverse[1]; i++ ) + MSVCRT_current_ctype[i+1] |= MSVCRT_LEADBYTE; + traverse += 2; + }; + } +} + + +/* + * @implemented + */ +char *setlocale(int category, const char *locale) +{ + LCID lcid = 0; + locale_search_t lc; + int haveLang, haveCountry, haveCP; + char* next; + int lc_all = 0; + + TRACE("(%d %s)\n",category,locale); + + if (category < MSVCRT_LC_MIN || category > MSVCRT_LC_MAX) + return NULL; + + if (locale == NULL) + { + /* Report the current Locale */ + return MSVCRT_current_lc_all; + } + + LOCK_LOCALE; + + if (locale[0] == 'L' && locale[1] == 'C' && locale[2] == '_') + { + DPRINT1(":restore previous locale not implemented!\n"); + /* FIXME: Easiest way to do this is parse the string and + * call this function recursively with its elements, + * Where they differ for each lc_ type. + */ + UNLOCK_LOCALE; + return MSVCRT_current_lc_all; + } + + /* Default Locale: Special case handling */ + if (!strlen(locale) || ((toupper(locale[0]) == 'C') && !locale[1])) + { + MSVCRT_current_lc_all[0] = 'C'; + MSVCRT_current_lc_all[1] = '\0'; + MSVCRT___lc_codepage = GetACP(); + MSVCRT___lc_collate_cp = GetACP(); + + switch (category) { + case MSVCRT_LC_ALL: + lc_all = 1; /* Fall through all cases ... */ + case MSVCRT_LC_COLLATE: + if (!lc_all) break; + case MSVCRT_LC_CTYPE: + /* Restore C locale ctype info */ + __mb_cur_max = 1; + memcpy(MSVCRT_current_ctype, MSVCRT__ctype, sizeof(MSVCRT__ctype)); + if (!lc_all) break; + case MSVCRT_LC_MONETARY: + if (!lc_all) break; + case MSVCRT_LC_NUMERIC: + if (!lc_all) break; + case MSVCRT_LC_TIME: + break; + } + UNLOCK_LOCALE; + return MSVCRT_current_lc_all; + } + + /* Get locale elements */ + haveLang = haveCountry = haveCP = 0; + memset(&lc,0,sizeof(lc)); + + next = strchr(locale,'_'); + if (next && next != locale) + { + haveLang = 1; + memcpy(lc.search_language,locale,next-locale); + locale += next-locale+1; + } + + next = strchr(locale,'.'); + if (next) + { + haveCP = 1; + if (next == locale) + { + locale++; + lstrcpynA(lc.search_codepage, locale, MAX_ELEM_LEN); + } + else + { + if (haveLang) + { + haveCountry = 1; + memcpy(lc.search_country,locale,next-locale); + locale += next-locale+1; + } + else + { + haveLang = 1; + memcpy(lc.search_language,locale,next-locale); + locale += next-locale+1; + } + lstrcpynA(lc.search_codepage, locale, MAX_ELEM_LEN); + } + } + else + { + if (haveLang) + { + haveCountry = 1; + lstrcpynA(lc.search_country, locale, MAX_ELEM_LEN); + } + else + { + haveLang = 1; + lstrcpynA(lc.search_language, locale, MAX_ELEM_LEN); + } + } + + if (haveCountry) + remap_synonym(lc.search_country); + + if (haveCP && !haveCountry && !haveLang) + { + DPRINT1(":Codepage only locale not implemented\n"); + /* FIXME: Use default lang/country and skip locale_to_LCID() + * call below... + */ + UNLOCK_LOCALE; + return NULL; + } + + lcid = MSVCRT_locale_to_LCID(&lc); + + TRACE(":found LCID %d\n",lcid); + + if (lcid == 0) + { + UNLOCK_LOCALE; + return NULL; + } + + MSVCRT_current_lc_all_lcid = lcid; + + snprintf(MSVCRT_current_lc_all,MAX_LOCALE_LENGTH,"%s_%s.%s", + lc.found_language,lc.found_country,lc.found_codepage); + + switch (category) { + case MSVCRT_LC_ALL: + lc_all = 1; /* Fall through all cases ... */ + case MSVCRT_LC_COLLATE: + if (!lc_all) break; + case MSVCRT_LC_CTYPE: + msvcrt_set_ctype(atoi(lc.found_codepage),lcid); + if (!lc_all) break; + case MSVCRT_LC_MONETARY: + if (!lc_all) break; + case MSVCRT_LC_NUMERIC: + if (!lc_all) break; + case MSVCRT_LC_TIME: + break; + } + UNLOCK_LOCALE; + return MSVCRT_current_lc_all; +}
/* * @unimplemented */ -char *setlocale(int category, const char *locale) -{ - char lang[100]; - char country[100]; - char code_page[100]; - if (NULL != locale) { - parse_locale(locale,lang,country,code_page); - } - - //printf("%s %s %s %s\n",locale,lang,country,code_page); - - - switch ( category ) - { - case LC_COLLATE: - break; - case LC_CTYPE: - break; - case LC_MONETARY: - break; - case LC_NUMERIC: - break; - case LC_TIME: - break; - case LC_ALL: - break; - default: - break; - } - - return "C"; - +wchar_t* _wsetlocale(int category, const wchar_t* locale) +{ + static wchar_t fake[] = { + 'E','n','g','l','i','s','h','_','U','n','i','t','e','d',' ', + 'S','t','a','t','e','s','.','1','2','5','2',0 }; + + DPRINT1("%d %S\n", category, locale); + + return fake; }
/*