diff --git a/reactos/dll/win32/msvcrt/dllmain.c b/reactos/dll/win32/msvcrt/dllmain.c index 9e904f30571..f7e972561e2 100644 --- a/reactos/dll/win32/msvcrt/dllmain.c +++ b/reactos/dll/win32/msvcrt/dllmain.c @@ -77,17 +77,11 @@ DllMain(PVOID hinstDll, ULONG dwReason, PVOID reserved) /* Initialization of the WINE code */ msvcrt_init_mt_locks(); - //if(!msvcrt_init_locale()) { - // msvcrt_free_mt_locks(); - // msvcrt_free_tls_mem(); - // return FALSE; - //} //msvcrt_init_math(); msvcrt_init_io(); //msvcrt_init_console(); //msvcrt_init_args(); //msvcrt_init_signals(); - _setmbcp(_MB_CP_LOCALE); TRACE("Attach done\n"); break; @@ -110,7 +104,8 @@ DllMain(PVOID hinstDll, ULONG dwReason, PVOID reserved) msvcrt_free_tls_mem(); if (!msvcrt_free_tls()) return FALSE; - //MSVCRT__free_locale(MSVCRT_locale); + if(global_locale) + MSVCRT__free_locale(global_locale); if (__winitenv && __winitenv != _wenviron) FreeEnvironment((char**)__winitenv); diff --git a/reactos/dll/win32/msvcrt/msvcrt.spec b/reactos/dll/win32/msvcrt/msvcrt.spec index d7d07f6b9a3..2b7604b4552 100644 --- a/reactos/dll/win32/msvcrt/msvcrt.spec +++ b/reactos/dll/win32/msvcrt/msvcrt.spec @@ -1042,7 +1042,7 @@ @ cdecl _wcstoi64(wstr ptr long) # @ cdecl _wcstoi64_l(wstr ptr long ptr) # stub _wcstol_l -# stub _wcstombs_l +@ cdecl _wcstombs_l(ptr ptr long ptr) # @ cdecl _wcstombs_s_l(ptr ptr long wstr long ptr) @ cdecl _wcstoui64(wstr ptr long) # @ cdecl _wcstoui64_l(wstr ptr long ptr) diff --git a/reactos/lib/sdk/crt/crt.cmake b/reactos/lib/sdk/crt/crt.cmake index c60a7cbe2fd..b1479770f13 100644 --- a/reactos/lib/sdk/crt/crt.cmake +++ b/reactos/lib/sdk/crt/crt.cmake @@ -61,6 +61,7 @@ list(APPEND CRT_SOURCE math/sinf.c math/sinh.c math/tanh.c + mbstring/_setmbcp.c mbstring/hanzen.c mbstring/ischira.c mbstring/iskana.c @@ -323,7 +324,6 @@ list(APPEND CRT_SOURCE time/utime64.c time/utime.c time/wasctime.c - time/wcsftime.c time/wctime32.c time/wctime64.c time/wctime.c diff --git a/reactos/lib/sdk/crt/include/internal/locale.h b/reactos/lib/sdk/crt/include/internal/locale.h new file mode 100644 index 00000000000..861e2f4b10f --- /dev/null +++ b/reactos/lib/sdk/crt/include/internal/locale.h @@ -0,0 +1,32 @@ +#ifndef __CRT_INTERNAL_LOCALE_H +#define __CRT_INTERNAL_LOCALE_H + +typedef struct MSVCRT_threadmbcinfostruct *MSVCRT_pthreadmbcinfo; + +typedef struct __lc_time_data { + union { + char *str[43]; + struct { + char *short_wday[7]; + char *wday[7]; + char *short_mon[12]; + char *mon[12]; + char *am; + char *pm; + char *short_date; + char *date; + char *time; + } names; + } str; + LCID lcid; + int unk[2]; + wchar_t *wstr[43]; + char data[1]; +} MSVCRT___lc_time_data; + +int _setmbcp_l(int, LCID, MSVCRT_pthreadmbcinfo) DECLSPEC_HIDDEN; +MSVCRT_pthreadmbcinfo get_mbcinfo(void) DECLSPEC_HIDDEN; +LCID MSVCRT_locale_to_LCID(const char *locale) DECLSPEC_HIDDEN; + +#endif //__CRT_INTERNAL_LOCALE_H + diff --git a/reactos/lib/sdk/crt/include/internal/mbstring.h b/reactos/lib/sdk/crt/include/internal/mbstring.h index 703439b9bed..8ceb83fa921 100644 --- a/reactos/lib/sdk/crt/include/internal/mbstring.h +++ b/reactos/lib/sdk/crt/include/internal/mbstring.h @@ -39,7 +39,7 @@ #define MAX_LOCALE_LENGTH 256 extern unsigned char _mbctype[257]; -extern int MSVCRT___lc_codepage; +extern unsigned int MSVCRT___lc_codepage; extern char MSVCRT_current_lc_all[MAX_LOCALE_LENGTH]; #if defined (_MSC_VER) diff --git a/reactos/lib/sdk/crt/include/internal/tls.h b/reactos/lib/sdk/crt/include/internal/tls.h index b5850b74653..3c01ba4b6b3 100644 --- a/reactos/lib/sdk/crt/include/internal/tls.h +++ b/reactos/lib/sdk/crt/include/internal/tls.h @@ -17,7 +17,7 @@ #include typedef struct MSVCRT_threadlocaleinfostruct { - int refcount; + LONG refcount; unsigned int lc_codepage; unsigned int lc_collate_cp; unsigned long lc_handle[6]; @@ -36,14 +36,14 @@ typedef struct MSVCRT_threadlocaleinfostruct { struct MSVCRT_lconv *lconv; int *ctype1_refcount; unsigned short *ctype1; - unsigned short *pctype; + const unsigned short *pctype; unsigned char *pclmap; unsigned char *pcumap; struct __lc_time_data *lc_time_curr; } MSVCRT_threadlocinfo; typedef struct MSVCRT_threadmbcinfostruct { - int refcount; + LONG refcount; int mbcodepage; int ismbcodepage; int mblcid; @@ -130,7 +130,16 @@ extern inline void msvcrt_free_tls_mem(void); #define MSVCRT_ENABLE_PER_THREAD_LOCALE 1 #define MSVCRT_DISABLE_PER_THREAD_LOCALE 2 -extern MSVCRT__locale_t MSVCRT_locale; +void __init_global_locale(); +extern MSVCRT__locale_t global_locale; +#define MSVCRT_locale __get_MSVCRT_locale() +extern inline MSVCRT__locale_t __get_MSVCRT_locale() +{ + if(!global_locale) + __init_global_locale(); + return global_locale; +} + MSVCRT_pthreadlocinfo get_locinfo(void); void __cdecl MSVCRT__free_locale(MSVCRT__locale_t); void free_locinfo(MSVCRT_pthreadlocinfo); diff --git a/reactos/lib/sdk/crt/locale/locale.c b/reactos/lib/sdk/crt/locale/locale.c index 5abee62e696..0aebb4b2dd4 100644 --- a/reactos/lib/sdk/crt/locale/locale.c +++ b/reactos/lib/sdk/crt/locale/locale.c @@ -1,182 +1,138 @@ /* - * Some stuff takem from wine msvcrt\locale.c + * msvcrt.dll locale functions * * Copyright 2000 Jon Griffiths + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #include #include #include "mbctype.h" +#include -// 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 + +#ifdef _pctype +#error _pctype should not be defined +#endif + +unsigned int MSVCRT___lc_codepage = 0; +int MSVCRT___lc_collate_cp = 0; +LCID MSVCRT___lc_handle[LC_MAX - LC_MIN + 1] = { 0 }; +int __mb_cur_max = 1; +static unsigned char charmax = CHAR_MAX; unsigned char _mbctype[257] = { 0 }; -int g_mbcp_is_multibyte = 0; - -/* It seems that the data about valid trail bytes is not available from kernel32 - * so we have to store is here. The format is the same as for lead bytes in CPINFO */ -struct cp_extra_info_t -{ - int cp; - BYTE TrailBytes[MAX_LEADBYTES]; -}; - -static struct cp_extra_info_t g_cpextrainfo[] = -{ - {932, {0x40, 0x7e, 0x80, 0xfc, 0, 0}}, - {936, {0x40, 0xfe, 0, 0}}, - {949, {0x41, 0xfe, 0, 0}}, - {950, {0x40, 0x7e, 0xa1, 0xfe, 0, 0}}, - {1361, {0x31, 0x7e, 0x81, 0xfe, 0, 0}}, - {20932, {1, 255, 0, 0}}, /* seems to give different results on different systems */ - {0, {1, 255, 0, 0}} /* match all with FIXME */ -}; - - -char MSVCRT_current_lc_all[MAX_LOCALE_LENGTH] = { 0 }; -LCID MSVCRT_current_lc_all_lcid = 0; -int MSVCRT___lc_codepage = 0; -int MSVCRT___lc_collate_cp = 0; -HANDLE MSVCRT___lc_handle[MSVCRT_LC_MAX - MSVCRT_LC_MIN + 1] = { 0 }; -int __mb_cur_max = 1; /* MT */ #define LOCK_LOCALE _mlock(_SETLOCALE_LOCK); #define UNLOCK_LOCALE _munlock(_SETLOCALE_LOCK); #define MSVCRT_LEADBYTE 0x8000 +#define MSVCRT_C1_DEFINED 0x200 -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; - -extern unsigned int __setlc_active; -extern 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(). - */ +/* Friendly country strings & language names abbreviations. */ 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" + "american", "enu", + "american english", "enu", + "american-english", "enu", + "english-american", "enu", + "english-us", "enu", + "english-usa", "enu", + "us", "enu", + "usa", "enu", + "australian", "ena", + "english-aus", "ena", + "belgian", "nlb", + "french-belgian", "frb", + "canadian", "enc", + "english-can", "enc", + "french-canadian", "frc", + "chinese", "chs", + "chinese-simplified", "chs", + "chinese-traditional", "cht", + "dutch-belgian", "nlb", + "english-nz", "enz", + "uk", "eng", + "english-uk", "eng", + "french-swiss", "frs", + "swiss", "des", + "german-swiss", "des", + "italian-swiss", "its", + "german-austrian", "dea", + "portuguese", "ptb", + "portuguese-brazil", "ptb", + "spanish-mexican", "esm", + "norwegian-bokmal", "nor", + "norwegian-nynorsk", "non", + "spanish-modern", "esn" }; +/* INTERNAL: Map a synonym to an ISO code */ +static void remap_synonym(char *name) +{ + unsigned int 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]); + strcpy(name, _country_synonyms[i+1]); + return; + } + } +} + /* 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 (!_stricmp(_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; - } - } -} +typedef struct { + char search_language[MAX_ELEM_LEN]; + char search_country[MAX_ELEM_LEN]; + char search_codepage[MAX_ELEM_LEN]; + char found_codepage[MAX_ELEM_LEN]; + unsigned int match_flags; + LANGID found_lang_id; +} locale_search_t; #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) +static int compare_info(LCID lcid, DWORD flags, char* buff, const char* cmp, BOOL exact) { + int len; + buff[0] = 0; - GetLocaleInfoA(lcid, flags|LOCALE_NOUSEROVERRIDE,buff, MAX_ELEM_LEN); + 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 !_strnicmp(cmp, buff, strlen(cmp)); -} + /* Partial matches are only allowed on language/country names */ + len = strlen(cmp); + if(exact || len<=3) + return !strcasecmp(cmp, buff); + else + return !strncasecmp(cmp, buff, len); +} static BOOL CALLBACK find_best_locale_proc(HMODULE hModule, LPCSTR type, LPCSTR name, WORD LangID, LONG_PTR lParam) @@ -190,13 +146,12 @@ find_best_locale_proc(HMODULE hModule, LPCSTR type, LPCSTR name, WORD LangID, LO 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)) + if (compare_info(lcid,LOCALE_SISO639LANGNAME,buff,res->search_language, TRUE) || + compare_info(lcid,LOCALE_SABBREVLANGNAME,buff,res->search_language, TRUE) || + compare_info(lcid,LOCALE_SENGLANGUAGE,buff,res->search_language, FALSE)) { 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) { @@ -204,13 +159,12 @@ find_best_locale_proc(HMODULE hModule, LPCSTR type, LPCSTR name, WORD LangID, LO } /* 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)) + if (compare_info(lcid,LOCALE_SISO3166CTRYNAME,buff,res->search_country, TRUE) || + compare_info(lcid,LOCALE_SABBREVCTRYNAME,buff,res->search_country, TRUE) || + compare_info(lcid,LOCALE_SENGCOUNTRY,buff,res->search_country, FALSE)) { 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) { @@ -218,8 +172,8 @@ find_best_locale_proc(HMODULE hModule, LPCSTR type, LPCSTR name, WORD LangID, LO } /* Check codepage */ - if (compare_info(lcid,LOCALE_IDEFAULTCODEPAGE,buff,res->search_codepage) || - (compare_info(lcid,LOCALE_IDEFAULTANSICODEPAGE,buff,res->search_codepage))) + if (compare_info(lcid,LOCALE_IDEFAULTCODEPAGE,buff,res->search_codepage, TRUE) || + (compare_info(lcid,LOCALE_IDEFAULTANSICODEPAGE,buff,res->search_codepage, TRUE))) { TRACE("Found codepage:%s->%s\n", res->search_codepage, buff); flags |= FOUND_CODEPAGE; @@ -245,605 +199,315 @@ find_best_locale_proc(HMODULE hModule, LPCSTR type, LPCSTR name, WORD LangID, LO return CONTINUE_LOOKING; } +extern int atoi(const char *); + /* Internal: Find the LCID for a locale specification */ -static LCID MSVCRT_locale_to_LCID(locale_search_t* locale) +LCID MSVCRT_locale_to_LCID(const char *locale) { - LCID lcid; - EnumResourceLanguagesA(GetModuleHandleA("KERNEL32"), (LPSTR)RT_STRING, - (LPCSTR)LOCALE_ILANGUAGE,find_best_locale_proc, - (LONG_PTR)locale); + LCID lcid; + locale_search_t search; + const char *cp, *region; - if (!locale->match_flags) - return 0; + memset(&search, 0, sizeof(locale_search_t)); - /* If we were given something that didn't match, fail */ - if (locale->search_country[0] && !(locale->match_flags & FOUND_COUNTRY)) - return 0; + cp = strchr(locale, '.'); + region = strchr(locale, '_'); - lcid = MAKELCID(locale->found_lang_id, SORT_DEFAULT); + lstrcpynA(search.search_language, locale, MAX_ELEM_LEN); + if(region) { + lstrcpynA(search.search_country, region+1, MAX_ELEM_LEN); + if(region-locale < MAX_ELEM_LEN) + search.search_language[region-locale] = '\0'; + } else + search.search_country[0] = '\0'; - /* 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 (_stricmp(locale->search_codepage,"OCP")) - { - GetLocaleInfoA(lcid, LOCALE_IDEFAULTCODEPAGE, - locale->found_codepage, MAX_ELEM_LEN); + if(cp) { + lstrcpynA(search.search_codepage, cp+1, MAX_ELEM_LEN); + if(region && cp-region-1search_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; + return lcid; } -/* INTERNAL: Set ctype behaviour for a codepage */ -static void msvcrt_set_ctype(unsigned int codepage, LCID lcid) +/* INTERNAL: Set lc_handle, lc_id and lc_category in threadlocinfo struct */ +static BOOL update_threadlocinfo_category(LCID lcid, MSVCRT__locale_t loc, int category) { - CPINFO cp; + char buf[256], *p; + int len; - memset(&cp, 0, sizeof(CPINFO)); + if(GetLocaleInfoA(lcid, LOCALE_ILANGUAGE|LOCALE_NOUSEROVERRIDE, buf, 256)) { + p = buf; + + loc->locinfo->lc_id[category].wLanguage = 0; + while(*p) { + loc->locinfo->lc_id[category].wLanguage *= 16; + + if(*p <= '9') + loc->locinfo->lc_id[category].wLanguage += *p-'0'; + else + loc->locinfo->lc_id[category].wLanguage += *p-'a'+10; + + p++; + } + + loc->locinfo->lc_id[category].wCountry = + loc->locinfo->lc_id[category].wLanguage; + } + + if(GetLocaleInfoA(lcid, LOCALE_IDEFAULTANSICODEPAGE + |LOCALE_NOUSEROVERRIDE, buf, 256)) + loc->locinfo->lc_id[category].wCodePage = atoi(buf); + + loc->locinfo->lc_handle[category] = lcid; + + len = 0; + len += GetLocaleInfoA(lcid, LOCALE_SENGLANGUAGE + |LOCALE_NOUSEROVERRIDE, buf, 256); + buf[len-1] = '_'; + len += GetLocaleInfoA(lcid, LOCALE_SENGCOUNTRY + |LOCALE_NOUSEROVERRIDE, &buf[len], 256-len); + buf[len-1] = '.'; + len += GetLocaleInfoA(lcid, LOCALE_IDEFAULTANSICODEPAGE + |LOCALE_NOUSEROVERRIDE, &buf[len], 256-len); + + loc->locinfo->lc_category[category].locale = malloc(len); + loc->locinfo->lc_category[category].refcount = malloc(sizeof(int)); + if(!loc->locinfo->lc_category[category].locale + || !loc->locinfo->lc_category[category].refcount) { + free(loc->locinfo->lc_category[category].locale); + free(loc->locinfo->lc_category[category].refcount); + loc->locinfo->lc_category[category].locale = NULL; + loc->locinfo->lc_category[category].refcount = NULL; + return TRUE; + } + memcpy(loc->locinfo->lc_category[category].locale, buf, len); + *loc->locinfo->lc_category[category].refcount = 1; + + return FALSE; +} + +/* INTERNAL: swap pointers values */ +static inline void swap_pointers(void **p1, void **p2) { + void *hlp; + + hlp = *p1; + *p1 = *p2; + *p2 = hlp; +} + +/* INTERNAL: returns pthreadlocinfo struct */ +MSVCRT_pthreadlocinfo get_locinfo(void) { + thread_data_t *data = msvcrt_get_thread_data(); + + if(!data || !data->have_locale) + return MSVCRT_locale->locinfo; + + return data->locinfo; +} + +/* INTERNAL: returns pthreadlocinfo struct */ +MSVCRT_pthreadmbcinfo get_mbcinfo(void) { + thread_data_t *data = msvcrt_get_thread_data(); + + if(!data || !data->have_locale) + return MSVCRT_locale->mbcinfo; + + return data->mbcinfo; +} + +/* INTERNAL: constructs string returned by setlocale */ +static inline char* construct_lc_all(MSVCRT_pthreadlocinfo locinfo) { + static char current_lc_all[MAX_LOCALE_LENGTH]; - if (GetCPInfo(codepage, &cp)) - { int i; - char str[3]; - unsigned char *traverse = 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); - } + for(i=LC_MIN+1; ilc_category[i].locale, + locinfo->lc_category[i+1].locale)) + break; } - /* 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; - }; - } + if(i==LC_MAX) + return locinfo->lc_category[LC_COLLATE].locale; + + sprintf(current_lc_all, + "LC_COLLATE=%s;LC_CTYPE=%s;LC_MONETARY=%s;LC_NUMERIC=%s;LC_TIME=%s", + locinfo->lc_category[LC_COLLATE].locale, + locinfo->lc_category[LC_CTYPE].locale, + locinfo->lc_category[LC_MONETARY].locale, + locinfo->lc_category[LC_NUMERIC].locale, + locinfo->lc_category[LC_TIME].locale); + + return current_lc_all; } -/* - * @implemented +/********************************************************************* + * wsetlocale (MSVCRT.@) */ -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] == '_') - { - WARN(":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) - { - ERR(":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 - */ -wchar_t* _wsetlocale(int category, const wchar_t* locale) +wchar_t* CDECL _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 }; - TRACE("%d %S\n", category, locale); + FIXME("%d %s\n", category, debugstr_w(locale)); return fake; } -/* - -locale "lang[_country[.code_page]]" - | ".code_page" - | "" - | NULL - -*/ -int parse_locale(const char *locale, char *lang, char *country, char *code_page) -{ - while ( *locale != 0 && *locale != '.' && *locale != '_' ) - { - *lang = *locale; - lang++; - locale++; - } - *lang = 0; - if ( *locale == '_' ) { - locale++; - while ( *locale != 0 && *locale != '.' ) - { - *country = *locale; - country++; - locale++; - } - } - *country = 0; - - - if ( *locale == '.' ) { - locale++; - while ( *locale != 0 && *locale != '.' ) - { - *code_page = *locale; - code_page++; - locale++; - } - } - - *code_page = 0; - return 0; -} - -const struct map_lcid2str { - short langid; - const char *langname; - const char *country; -} languages[]={ - {0x0409,"English", "United States"}, - {0x0809,"English", "United Kingdom"}, - {0x0000,"Unknown", "Unknown"} - -}; - -const struct map_cntr { - const char *abrev; - const char *country; -} abrev[] = { - {"britain", "united kingdom"}, - {"england", "united kingdom"}, - {"gbr", "united kingdom"}, - {"great britain", "united kingdom"}, - {"uk", "united kingdom"}, - {"united kingdom", "united kingdom"}, - {"united-kingdom", "united kingdom"}, - {"america", "united states" }, - {"united states", "united states"}, - {"united-states", "united states"}, - {"us", "united states"}, - {"usa", "united states"} -}; - - -struct lconv _lconv = { -".", // decimal_point -",", // thousands_sep -"", // grouping; -"DOL", // int_curr_symbol -"$", // currency_symbol -".", // mon_decimal_point -",", // mon_thousands_sep -"", // mon_grouping; -"+", // positive_sign -"-", // negative_sign -127, // int_frac_digits -127, // frac_digits -127, // p_cs_precedes -127, // p_sep_by_space -127, // n_cs_precedes -127, // n_sep_by_space -127, // p_sign_posn; -127 // n_sign_posn; -}; - -/* - * @implemented +/********************************************************************* + * _Getdays (MSVCRT.@) */ -struct lconv *localeconv(void) +char* CDECL _Getdays(void) { - return (struct lconv *) &_lconv; + MSVCRT___lc_time_data *cur = get_locinfo()->lc_time_curr; + int i, len, size; + char *out; + + TRACE("\n"); + + size = cur->str.names.short_mon[0]-cur->str.names.short_wday[0]; + out = malloc(size+1); + if(!out) + return NULL; + + size = 0; + for(i=0; i<7; i++) { + out[size++] = ':'; + len = strlen(cur->str.names.short_wday[i]); + memcpy(&out[size], cur->str.names.short_wday[i], len); + size += len; + + out[size++] = ':'; + len = strlen(cur->str.names.wday[i]); + memcpy(&out[size], cur->str.names.wday[i], len); + size += len; + } + out[size] = '\0'; + + return out; } /********************************************************************* - * _setmbcp (MSVCRT.@) - * @implemented + * _Getmonths (MSVCRT.@) */ -int CDECL _setmbcp(int cp) +char* CDECL _Getmonths(void) { - int newcp; - CPINFO cpi; - BYTE *bytes; - WORD chartypes[256]; - WORD *curr_type; - char bufA[256]; - WCHAR bufW[256]; - int charcount; - int ret; - int i; + MSVCRT___lc_time_data *cur = get_locinfo()->lc_time_curr; + int i, len, size; + char *out; - TRACE("_setmbcp %d\n",cp); - switch (cp) - { - case _MB_CP_ANSI: - newcp = GetACP(); - break; - case _MB_CP_OEM: - newcp = GetOEMCP(); - break; - case _MB_CP_LOCALE: - newcp = MSVCRT___lc_codepage; - break; - case _MB_CP_SBCS: - newcp = 20127; /* ASCII */ - break; - default: - newcp = cp; - break; - } + TRACE("\n"); - if (!GetCPInfo(newcp, &cpi)) - { - ERR("Codepage %d not found\n", newcp); - _set_errno(EINVAL); - return -1; - } + size = cur->str.names.am-cur->str.names.short_mon[0]; + out = malloc(size+1); + if(!out) + return NULL; - /* setup the _mbctype */ - memset(_mbctype, 0, sizeof(_mbctype)); + size = 0; + for(i=0; i<12; i++) { + out[size++] = ':'; + len = strlen(cur->str.names.short_mon[i]); + memcpy(&out[size], cur->str.names.short_mon[i], len); + size += len; - bytes = cpi.LeadByte; - while (bytes[0] || bytes[1]) - { - for (i = bytes[0]; i <= bytes[1]; i++) - _mbctype[i + 1] |= _M1; - bytes += 2; - } - - if (cpi.MaxCharSize > 1) - { - /* trail bytes not available through kernel32 but stored in a structure in msvcrt */ - struct cp_extra_info_t *cpextra = g_cpextrainfo; - - g_mbcp_is_multibyte = 1; - while (TRUE) - { - if (cpextra->cp == 0 || cpextra->cp == newcp) - { - if (cpextra->cp == 0) - ERR("trail bytes data not available for DBCS codepage %d - assuming all bytes\n", newcp); - - bytes = cpextra->TrailBytes; - while (bytes[0] || bytes[1]) - { - for (i = bytes[0]; i <= bytes[1]; i++) - _mbctype[i + 1] |= _M2; - bytes += 2; - } - break; - } - cpextra++; + out[size++] = ':'; + len = strlen(cur->str.names.mon[i]); + memcpy(&out[size], cur->str.names.mon[i], len); + size += len; } - } - else - g_mbcp_is_multibyte = 0; + out[size] = '\0'; - /* we can't use GetStringTypeA directly because we don't have a locale - only a code page - */ - charcount = 0; - for (i = 0; i < 256; i++) - if (!(_mbctype[i + 1] & _M1)) - bufA[charcount++] = i; + return out; +} - ret = MultiByteToWideChar(newcp, 0, bufA, charcount, bufW, charcount); - if (ret != charcount) - ERR("MultiByteToWideChar of chars failed for cp %d, ret=%d (exp %d), error=%d\n", newcp, ret, charcount, GetLastError()); +/********************************************************************* + * _Gettnames (MSVCRT.@) + */ +void* CDECL _Gettnames(void) +{ + MSVCRT___lc_time_data *ret, *cur = get_locinfo()->lc_time_curr; + int i, size = sizeof(MSVCRT___lc_time_data); - GetStringTypeW(CT_CTYPE1, bufW, charcount, chartypes); + TRACE("\n"); - curr_type = chartypes; - for (i = 0; i < 256; i++) - if (!(_mbctype[i + 1] & _M1)) - { - if ((*curr_type) & C1_UPPER) - _mbctype[i + 1] |= _SBUP; - if ((*curr_type) & C1_LOWER) - _mbctype[i + 1] |= _SBLOW; - curr_type++; + for(i=0; istr.str)/sizeof(cur->str.str[0]); i++) + size += strlen(cur->str.str[i])+1; + + ret = malloc(size); + if(!ret) + return NULL; + memcpy(ret, cur, size); + + size = 0; + for(i=0; istr.str)/sizeof(cur->str.str[0]); i++) { + ret->str.str[i] = &ret->data[size]; + size += strlen(&ret->data[size])+1; } - if (newcp == 932) /* CP932 only - set _MP and _MS */ - { - /* On Windows it's possible to calculate the _MP and _MS from CT_CTYPE1 - * and CT_CTYPE3. But as of Wine 0.9.43 we return wrong values what makes - * it hard. As this is set only for codepage 932 we hardcode it what gives - * also faster execution. - */ - for (i = 161; i <= 165; i++) - _mbctype[i + 1] |= _MP; - for (i = 166; i <= 223; i++) - _mbctype[i + 1] |= _MS; - } - - MSVCRT___lc_collate_cp = MSVCRT___lc_codepage = newcp; - TRACE("(%d) -> %d\n", cp, MSVCRT___lc_codepage); - return 0; -} - - -/********************************************************************* - * ___lc_handle_func (MSVCRT.@) - */ -HANDLE * CDECL ___lc_handle_func(void) -{ - return MSVCRT___lc_handle; -} - - -/********************************************************************* - * ___lc_codepage_func (MSVCRT.@) - */ -int CDECL ___lc_codepage_func(void) -{ - return MSVCRT___lc_codepage; -} - - -/********************************************************************* - * _Gettnames (MSVCRT.@) - */ -void *_Gettnames(void) -{ - FIXME("(void), stub!\n"); - return NULL; + return ret; } /********************************************************************* - * __lconv_init (MSVCRT.@) + * __crtLCMapStringA (MSVCRT.@) */ -void __lconv_init(void) -{ - char Char = (char) UCHAR_MAX; - - TRACE("__lconv_init()\n"); - - _lconv.int_frac_digits = Char; - _lconv.frac_digits = Char; - _lconv.p_sep_by_space = _lconv.n_sep_by_space = Char; - _lconv.p_cs_precedes = _lconv.n_cs_precedes = Char; - _lconv.p_sign_posn = _lconv.n_sign_posn = Char; -} - - -/********************************************************************* - * _Strftime (MSVCRT.@) - */ -const char* _Strftime(char *out, unsigned int len, const char *fmt, - const void *tm, void *foo) -{ - /* FIXME: */ - FIXME("(%p %d %s %p %p) stub\n", out, len, fmt, tm, foo); - return ""; -} - - -/********************************************************************* - * _Getdays (MSVCRT.@) - */ -const char* _Getdays(void) -{ - static const char *MSVCRT_days = ":Sun:Sunday:Mon:Monday:Tue:Tuesday:Wed:" - "Wednesday:Thu:Thursday:Fri:Friday:Sat:Saturday"; - /* FIXME: Use locale */ - FIXME("(void) semi-stub\n"); - return MSVCRT_days; -} - -/********************************************************************* - * _Getmonths (MSVCRT.@) - */ -const char* _Getmonths(void) -{ - static const char *MSVCRT_months = ":Jan:January:Feb:February:Mar:March:Apr:" - "April:May:May:Jun:June:Jul:July:Aug:August:Sep:September:Oct:" - "October:Nov:November:Dec:December"; - /* FIXME: Use locale */ - FIXME("(void) semi-stub\n"); - return MSVCRT_months; -} - -/********************************************************************* - * __crtLCMapStringA (MSVCRT.@) - */ -int __crtLCMapStringA( +int CDECL __crtLCMapStringA( LCID lcid, DWORD mapflags, const char* src, int srclen, char* dst, int dstlen, unsigned int codepage, int xflag ) { - TRACE("(lcid %lx, flags %lx, %s(%d), %p(%d), %x, %d), partial stub!\n", + FIXME("(lcid %x, flags %x, %s(%d), %p(%d), %x, %d), partial stub!\n", lcid,mapflags,src,srclen,dst,dstlen,codepage,xflag); /* FIXME: A bit incorrect. But msvcrt itself just converts its * arguments to wide strings and then calls LCMapStringW @@ -852,37 +516,64 @@ int __crtLCMapStringA( } /********************************************************************* - * __crtLCMapStringW (MSVCRT.@) + * __crtLCMapStringW (MSVCRT.@) */ -int __crtLCMapStringW( - LCID lcid, DWORD mapflags, LPCWSTR src, int srclen, LPWSTR dst, - int dstlen, unsigned int codepage, int xflag -) { - TRACE("(lcid %lx, flags %lx, %s(%d), %p(%d), %x, %d), partial stub!\n", - lcid,mapflags,src,srclen,dst,dstlen,codepage,xflag); - - return LCMapStringW(lcid,mapflags,src,srclen,dst,dstlen); -} - -int CDECL _getmbcp(void) +int CDECL __crtLCMapStringW(LCID lcid, DWORD mapflags, const wchar_t *src, + int srclen, wchar_t *dst, int dstlen, unsigned int codepage, int xflag) { - return MSVCRT___lc_codepage; + FIXME("(lcid %x, flags %x, %s(%d), %p(%d), %x, %d), partial stub!\n", + lcid, mapflags, debugstr_w(src), srclen, dst, dstlen, codepage, xflag); + + return LCMapStringW(lcid, mapflags, src, srclen, dst, dstlen); } /********************************************************************* - * ___unguarded_readlc_active_add_func (MSVCRT.@) + * __crtCompareStringA (MSVCRT.@) */ -unsigned int * CDECL ___unguarded_readlc_active_add_func(void) +int CDECL __crtCompareStringA( LCID lcid, DWORD flags, const char *src1, int len1, + const char *src2, int len2 ) { - return &__unguarded_readlc_active; + FIXME("(lcid %x, flags %x, %s(%d), %s(%d), partial stub\n", + lcid, flags, debugstr_a(src1), len1, debugstr_a(src2), len2 ); + /* FIXME: probably not entirely right */ + return CompareStringA( lcid, flags, src1, len1, src2, len2 ); } /********************************************************************* - * ___setlc_active_func (MSVCRT.@) + * __crtCompareStringW (MSVCRT.@) */ -unsigned int CDECL ___setlc_active_func(void) +int CDECL __crtCompareStringW( LCID lcid, DWORD flags, const wchar_t *src1, int len1, + const wchar_t *src2, int len2 ) { - return __setlc_active; + FIXME("(lcid %x, flags %x, %s(%d), %s(%d), partial stub\n", + lcid, flags, debugstr_w(src1), len1, debugstr_w(src2), len2 ); + /* FIXME: probably not entirely right */ + return CompareStringW( lcid, flags, src1, len1, src2, len2 ); +} + +/********************************************************************* + * __crtGetLocaleInfoW (MSVCRT.@) + */ +int CDECL __crtGetLocaleInfoW( LCID lcid, LCTYPE type, wchar_t *buffer, int len ) +{ + FIXME("(lcid %x, type %x, %p(%d), partial stub\n", lcid, type, buffer, len ); + /* FIXME: probably not entirely right */ + return GetLocaleInfoW( lcid, type, buffer, len ); +} + +/********************************************************************* + * btowc(MSVCRT.@) + */ +wint_t CDECL MSVCRT_btowc(int c) +{ + unsigned char letter = c; + wchar_t ret; + + if(!MultiByteToWideChar(get_locinfo()->lc_handle[LC_CTYPE], + 0, (LPCSTR)&letter, 1, &ret, 1)) + return 0; + + return ret; } /********************************************************************* @@ -899,3 +590,920 @@ BOOL CDECL __crtGetStringTypeW(DWORD unk, DWORD type, return GetStringTypeW(type, buffer, len, out); } + +/********************************************************************* + * localeconv (MSVCRT.@) + */ +struct lconv * CDECL localeconv(void) +{ + return (struct lconv*)get_locinfo()->lconv; +} + +/********************************************************************* + * __lconv_init (MSVCRT.@) + */ +int CDECL __lconv_init(void) +{ + /* this is used to make chars unsigned */ + charmax = 255; + return 0; +} + +/********************************************************************* + * ___lc_handle_func (MSVCRT.@) + */ +LCID* CDECL ___lc_handle_func(void) +{ + return MSVCRT___lc_handle; +} + +/********************************************************************* + * ___lc_codepage_func (MSVCRT.@) + */ +unsigned int CDECL ___lc_codepage_func(void) +{ + return MSVCRT___lc_codepage; +} + +/********************************************************************* + * ___lc_collate_cp_func (MSVCRT.@) + */ +int CDECL ___lc_collate_cp_func(void) +{ + return get_locinfo()->lc_collate_cp; +} + +/* INTERNAL: frees MSVCRT_pthreadlocinfo struct */ +void free_locinfo(MSVCRT_pthreadlocinfo locinfo) +{ + int i; + + if(!locinfo) + return; + + if(InterlockedDecrement(&locinfo->refcount)) + return; + + for(i=LC_MIN+1; i<=LC_MAX; i++) { + free(locinfo->lc_category[i].locale); + free(locinfo->lc_category[i].refcount); + } + + if(locinfo->lconv) { + free(locinfo->lconv->decimal_point); + free(locinfo->lconv->thousands_sep); + free(locinfo->lconv->grouping); + free(locinfo->lconv->int_curr_symbol); + free(locinfo->lconv->currency_symbol); + free(locinfo->lconv->mon_decimal_point); + free(locinfo->lconv->mon_thousands_sep); + free(locinfo->lconv->mon_grouping); + free(locinfo->lconv->positive_sign); + free(locinfo->lconv->negative_sign); + } + free(locinfo->lconv_intl_refcount); + free(locinfo->lconv_num_refcount); + free(locinfo->lconv_mon_refcount); + free(locinfo->lconv); + + free(locinfo->ctype1_refcount); + free(locinfo->ctype1); + + free(locinfo->pclmap); + free(locinfo->pcumap); + + free(locinfo->lc_time_curr); + + free(locinfo); +} + +/* INTERNAL: frees MSVCRT_pthreadmbcinfo struct */ +void free_mbcinfo(MSVCRT_pthreadmbcinfo mbcinfo) +{ + if(!mbcinfo) + return; + + if(InterlockedDecrement(&mbcinfo->refcount)) + return; + + free(mbcinfo); +} + +/* _get_current_locale - not exported in native msvcrt */ +MSVCRT__locale_t CDECL MSVCRT__get_current_locale(void) +{ + MSVCRT__locale_t loc = malloc(sizeof(MSVCRT__locale_tstruct)); + if(!loc) + return NULL; + + loc->locinfo = get_locinfo(); + loc->mbcinfo = get_mbcinfo(); + InterlockedIncrement(&loc->locinfo->refcount); + InterlockedIncrement(&loc->mbcinfo->refcount); + return loc; +} + +/* _free_locale - not exported in native msvcrt */ +void CDECL MSVCRT__free_locale(MSVCRT__locale_t locale) +{ + if (!locale) + return; + + free_locinfo(locale->locinfo); + free_mbcinfo(locale->mbcinfo); + free(locale); +} + +/* _create_locale - not exported in native msvcrt */ +MSVCRT__locale_t CDECL MSVCRT__create_locale(int category, const char *locale) +{ + static const DWORD time_data[] = { + LOCALE_SABBREVDAYNAME7, LOCALE_SABBREVDAYNAME1, LOCALE_SABBREVDAYNAME2, + LOCALE_SABBREVDAYNAME3, LOCALE_SABBREVDAYNAME4, LOCALE_SABBREVDAYNAME5, + LOCALE_SABBREVDAYNAME6, + LOCALE_SDAYNAME7, LOCALE_SDAYNAME1, LOCALE_SDAYNAME2, LOCALE_SDAYNAME3, + LOCALE_SDAYNAME4, LOCALE_SDAYNAME5, LOCALE_SDAYNAME6, + LOCALE_SABBREVMONTHNAME1, LOCALE_SABBREVMONTHNAME2, LOCALE_SABBREVMONTHNAME3, + LOCALE_SABBREVMONTHNAME4, LOCALE_SABBREVMONTHNAME5, LOCALE_SABBREVMONTHNAME6, + LOCALE_SABBREVMONTHNAME7, LOCALE_SABBREVMONTHNAME8, LOCALE_SABBREVMONTHNAME9, + LOCALE_SABBREVMONTHNAME10, LOCALE_SABBREVMONTHNAME11, LOCALE_SABBREVMONTHNAME12, + LOCALE_SMONTHNAME1, LOCALE_SMONTHNAME2, LOCALE_SMONTHNAME3, LOCALE_SMONTHNAME4, + LOCALE_SMONTHNAME5, LOCALE_SMONTHNAME6, LOCALE_SMONTHNAME7, LOCALE_SMONTHNAME8, + LOCALE_SMONTHNAME9, LOCALE_SMONTHNAME10, LOCALE_SMONTHNAME11, LOCALE_SMONTHNAME12, + LOCALE_S1159, LOCALE_S2359, + LOCALE_SSHORTDATE, LOCALE_SLONGDATE, + LOCALE_STIMEFORMAT + }; + static const char collate[] = "COLLATE="; + static const char ctype[] = "CTYPE="; + static const char monetary[] = "MONETARY="; + static const char numeric[] = "NUMERIC="; + static const char time[] = "TIME="; + static const char cloc_short_date[] = "MM/dd/yy"; + static const wchar_t cloc_short_dateW[] = {'M','M','/','d','d','/','y','y',0}; + static const char cloc_long_date[] = "dddd, MMMM dd, yyyy"; + static const wchar_t cloc_long_dateW[] = {'d','d','d','d',',',' ','M','M','M','M',' ','d','d',',',' ','y','y','y','y',0}; + static const char cloc_time[] = "HH:mm:ss"; + static const wchar_t cloc_timeW[] = {'H','H',':','m','m',':','s','s',0}; + + MSVCRT__locale_t loc; + LCID lcid[6] = { 0 }, lcid_tmp; + char buf[256]; + int i, ret, size; + + TRACE("(%d %s)\n", category, locale); + + if(categoryLC_MAX || !locale) + return NULL; + + if(locale[0]=='C' && !locale[1]) + lcid[0] = CP_ACP; + else if(!locale[0]) + lcid[0] = GetSystemDefaultLCID(); + else if (locale[0] == 'L' && locale[1] == 'C' && locale[2] == '_') { + const char *p; + + while(1) { + locale += 3; /* LC_ */ + if(!memcmp(locale, collate, sizeof(collate)-1)) { + i = LC_COLLATE; + locale += sizeof(collate)-1; + } else if(!memcmp(locale, ctype, sizeof(ctype)-1)) { + i = LC_CTYPE; + locale += sizeof(ctype)-1; + } else if(!memcmp(locale, monetary, sizeof(monetary)-1)) { + i = LC_MONETARY; + locale += sizeof(monetary)-1; + } else if(!memcmp(locale, numeric, sizeof(numeric)-1)) { + i = LC_NUMERIC; + locale += sizeof(numeric)-1; + } else if(!memcmp(locale, time, sizeof(time)-1)) { + i = LC_TIME; + locale += sizeof(time)-1; + } else + return NULL; + + p = strchr(locale, ';'); + if(locale[0]=='C' && (locale[1]==';' || locale[1]=='\0')) + lcid[i] = 0; + else if(p) { + memcpy(buf, locale, p-locale); + buf[p-locale] = '\0'; + lcid[i] = MSVCRT_locale_to_LCID(buf); + } else + lcid[i] = MSVCRT_locale_to_LCID(locale); + + if(lcid[i] == -1) + return NULL; + + if(!p || *(p+1)!='L' || *(p+2)!='C' || *(p+3)!='_') + break; + + locale = p+1; + } + } else { + lcid[0] = MSVCRT_locale_to_LCID(locale); + if(lcid[0] == -1) + return NULL; + } + + for(i=1; i<6; i++) { + if(!lcid[i]) + lcid[i] = lcid[0]; + } + + loc = malloc(sizeof(MSVCRT__locale_tstruct)); + if(!loc) + return NULL; + + loc->locinfo = malloc(sizeof(MSVCRT_threadlocinfo)); + if(!loc->locinfo) { + free(loc); + return NULL; + } + + loc->mbcinfo = malloc(sizeof(MSVCRT_threadmbcinfo)); + if(!loc->mbcinfo) { + free(loc->locinfo); + free(loc); + return NULL; + } + + memset(loc->locinfo, 0, sizeof(MSVCRT_threadlocinfo)); + loc->locinfo->refcount = 1; + loc->mbcinfo->refcount = 1; + + loc->locinfo->lconv = malloc(sizeof(struct MSVCRT_lconv)); + if(!loc->locinfo->lconv) { + MSVCRT__free_locale(loc); + return NULL; + } + memset(loc->locinfo->lconv, 0, sizeof(struct MSVCRT_lconv)); + + loc->locinfo->pclmap = malloc(sizeof(char[256])); + loc->locinfo->pcumap = malloc(sizeof(char[256])); + if(!loc->locinfo->pclmap || !loc->locinfo->pcumap) { + MSVCRT__free_locale(loc); + return NULL; + } + + if(lcid[LC_COLLATE] && (category==LC_ALL || category==LC_COLLATE)) { + if(update_threadlocinfo_category(lcid[LC_COLLATE], loc, LC_COLLATE)) { + MSVCRT__free_locale(loc); + return NULL; + } + + loc->locinfo->lc_collate_cp = loc->locinfo->lc_id[LC_COLLATE].wCodePage; + } else + loc->locinfo->lc_category[LC_COLLATE].locale = _strdup("C"); + + if(lcid[LC_CTYPE] && (category==LC_ALL || category==LC_CTYPE)) { + CPINFO cp; + int j; + + if(update_threadlocinfo_category(lcid[LC_CTYPE], loc, LC_CTYPE)) { + MSVCRT__free_locale(loc); + return NULL; + } + + loc->locinfo->lc_codepage = loc->locinfo->lc_id[LC_CTYPE].wCodePage; + loc->locinfo->lc_clike = 1; + if(!GetCPInfo(loc->locinfo->lc_codepage, &cp)) { + MSVCRT__free_locale(loc); + return NULL; + } + loc->locinfo->mb_cur_max = cp.MaxCharSize; + + loc->locinfo->ctype1_refcount = malloc(sizeof(int)); + loc->locinfo->ctype1 = malloc(sizeof(short[257])); + if(!loc->locinfo->ctype1_refcount || !loc->locinfo->ctype1) { + MSVCRT__free_locale(loc); + return NULL; + } + + *loc->locinfo->ctype1_refcount = 1; + loc->locinfo->ctype1[0] = 0; + loc->locinfo->pctype = loc->locinfo->ctype1+1; + + buf[1] = buf[2] = '\0'; + for(i=1; i<257; i++) { + buf[0] = i-1; + + /* builtin GetStringTypeA doesn't set output to 0 on invalid input */ + loc->locinfo->ctype1[i] = 0; + + GetStringTypeA(lcid[LC_CTYPE], CT_CTYPE1, buf, + 1, loc->locinfo->ctype1+i); + } + + for(i=0; cp.LeadByte[i+1]!=0; i+=2) + for(j=cp.LeadByte[i]; j<=cp.LeadByte[i+1]; j++) + loc->locinfo->ctype1[j+1] |= _LEADBYTE; + } else { + loc->locinfo->lc_clike = 1; + loc->locinfo->mb_cur_max = 1; + loc->locinfo->pctype = _ctype+1; + loc->locinfo->lc_category[LC_CTYPE].locale = _strdup("C"); + } + + for(i=0; i<256; i++) { + if(loc->locinfo->pctype[i] & _LEADBYTE) + buf[i] = ' '; + else + buf[i] = i; + + } + + if(lcid[LC_CTYPE]) { + LCMapStringA(lcid[LC_CTYPE], LCMAP_LOWERCASE, buf, 256, + (char*)loc->locinfo->pclmap, 256); + LCMapStringA(lcid[LC_CTYPE], LCMAP_UPPERCASE, buf, 256, + (char*)loc->locinfo->pcumap, 256); + } else { + for(i=0; i<256; i++) { + loc->locinfo->pclmap[i] = (i>='A' && i<='Z' ? i-'A'+'a' : i); + loc->locinfo->pcumap[i] = (i>='a' && i<='z' ? i-'a'+'A' : i); + } + } + + _setmbcp_l(loc->locinfo->lc_id[LC_CTYPE].wCodePage, lcid[LC_CTYPE], loc->mbcinfo); + + if(lcid[LC_MONETARY] && (category==LC_ALL || category==LC_MONETARY)) { + if(update_threadlocinfo_category(lcid[LC_MONETARY], loc, LC_MONETARY)) { + MSVCRT__free_locale(loc); + return NULL; + } + + loc->locinfo->lconv_intl_refcount = malloc(sizeof(int)); + loc->locinfo->lconv_mon_refcount = malloc(sizeof(int)); + if(!loc->locinfo->lconv_intl_refcount || !loc->locinfo->lconv_mon_refcount) { + MSVCRT__free_locale(loc); + return NULL; + } + + *loc->locinfo->lconv_intl_refcount = 1; + *loc->locinfo->lconv_mon_refcount = 1; + + i = GetLocaleInfoA(lcid[LC_MONETARY], LOCALE_SINTLSYMBOL + |LOCALE_NOUSEROVERRIDE, buf, 256); + if(i && (loc->locinfo->lconv->int_curr_symbol = malloc(i))) + memcpy(loc->locinfo->lconv->int_curr_symbol, buf, i); + else { + MSVCRT__free_locale(loc); + return NULL; + } + + i = GetLocaleInfoA(lcid[LC_MONETARY], LOCALE_SCURRENCY + |LOCALE_NOUSEROVERRIDE, buf, 256); + if(i && (loc->locinfo->lconv->currency_symbol = malloc(i))) + memcpy(loc->locinfo->lconv->currency_symbol, buf, i); + else { + MSVCRT__free_locale(loc); + return NULL; + } + + i = GetLocaleInfoA(lcid[LC_MONETARY], LOCALE_SMONDECIMALSEP + |LOCALE_NOUSEROVERRIDE, buf, 256); + if(i && (loc->locinfo->lconv->mon_decimal_point = malloc(i))) + memcpy(loc->locinfo->lconv->mon_decimal_point, buf, i); + else { + MSVCRT__free_locale(loc); + return NULL; + } + + i = GetLocaleInfoA(lcid[LC_MONETARY], LOCALE_SMONTHOUSANDSEP + |LOCALE_NOUSEROVERRIDE, buf, 256); + if(i && (loc->locinfo->lconv->mon_thousands_sep = malloc(i))) + memcpy(loc->locinfo->lconv->mon_thousands_sep, buf, i); + else { + MSVCRT__free_locale(loc); + return NULL; + } + + i = GetLocaleInfoA(lcid[LC_MONETARY], LOCALE_SMONGROUPING + |LOCALE_NOUSEROVERRIDE, buf, 256); + if(i>1) + i = i/2 + (buf[i-2]=='0'?0:1); + if(i && (loc->locinfo->lconv->mon_grouping = malloc(i))) { + for(i=0; buf[i+1]==';'; i+=2) + loc->locinfo->lconv->mon_grouping[i/2] = buf[i]-'0'; + loc->locinfo->lconv->mon_grouping[i/2] = buf[i]-'0'; + if(buf[i] != '0') + loc->locinfo->lconv->mon_grouping[i/2+1] = 127; + } else { + MSVCRT__free_locale(loc); + return NULL; + } + + i = GetLocaleInfoA(lcid[LC_MONETARY], LOCALE_SPOSITIVESIGN + |LOCALE_NOUSEROVERRIDE, buf, 256); + if(i && (loc->locinfo->lconv->positive_sign = malloc(i))) + memcpy(loc->locinfo->lconv->positive_sign, buf, i); + else { + MSVCRT__free_locale(loc); + return NULL; + } + + i = GetLocaleInfoA(lcid[LC_MONETARY], LOCALE_SNEGATIVESIGN + |LOCALE_NOUSEROVERRIDE, buf, 256); + if(i && (loc->locinfo->lconv->negative_sign = malloc(i))) + memcpy(loc->locinfo->lconv->negative_sign, buf, i); + else { + MSVCRT__free_locale(loc); + return NULL; + } + + if(GetLocaleInfoA(lcid[LC_MONETARY], LOCALE_IINTLCURRDIGITS + |LOCALE_NOUSEROVERRIDE, buf, 256)) + loc->locinfo->lconv->int_frac_digits = atoi(buf); + else { + MSVCRT__free_locale(loc); + return NULL; + } + + if(GetLocaleInfoA(lcid[LC_MONETARY], LOCALE_ICURRDIGITS + |LOCALE_NOUSEROVERRIDE, buf, 256)) + loc->locinfo->lconv->frac_digits = atoi(buf); + else { + MSVCRT__free_locale(loc); + return NULL; + } + + if(GetLocaleInfoA(lcid[LC_MONETARY], LOCALE_IPOSSYMPRECEDES + |LOCALE_NOUSEROVERRIDE, buf, 256)) + loc->locinfo->lconv->p_cs_precedes = atoi(buf); + else { + MSVCRT__free_locale(loc); + return NULL; + } + + if(GetLocaleInfoA(lcid[LC_MONETARY], LOCALE_IPOSSEPBYSPACE + |LOCALE_NOUSEROVERRIDE, buf, 256)) + loc->locinfo->lconv->p_sep_by_space = atoi(buf); + else { + MSVCRT__free_locale(loc); + return NULL; + } + + if(GetLocaleInfoA(lcid[LC_MONETARY], LOCALE_INEGSYMPRECEDES + |LOCALE_NOUSEROVERRIDE, buf, 256)) + loc->locinfo->lconv->n_cs_precedes = atoi(buf); + else { + MSVCRT__free_locale(loc); + return NULL; + } + + if(GetLocaleInfoA(lcid[LC_MONETARY], LOCALE_INEGSEPBYSPACE + |LOCALE_NOUSEROVERRIDE, buf, 256)) + loc->locinfo->lconv->n_sep_by_space = atoi(buf); + else { + MSVCRT__free_locale(loc); + return NULL; + } + + if(GetLocaleInfoA(lcid[LC_MONETARY], LOCALE_IPOSSIGNPOSN + |LOCALE_NOUSEROVERRIDE, buf, 256)) + loc->locinfo->lconv->p_sign_posn = atoi(buf); + else { + MSVCRT__free_locale(loc); + return NULL; + } + + if(GetLocaleInfoA(lcid[LC_MONETARY], LOCALE_INEGSIGNPOSN + |LOCALE_NOUSEROVERRIDE, buf, 256)) + loc->locinfo->lconv->n_sign_posn = atoi(buf); + else { + MSVCRT__free_locale(loc); + return NULL; + } + } else { + loc->locinfo->lconv->int_curr_symbol = malloc(sizeof(char)); + loc->locinfo->lconv->currency_symbol = malloc(sizeof(char)); + loc->locinfo->lconv->mon_decimal_point = malloc(sizeof(char)); + loc->locinfo->lconv->mon_thousands_sep = malloc(sizeof(char)); + loc->locinfo->lconv->mon_grouping = malloc(sizeof(char)); + loc->locinfo->lconv->positive_sign = malloc(sizeof(char)); + loc->locinfo->lconv->negative_sign = malloc(sizeof(char)); + + if(!loc->locinfo->lconv->int_curr_symbol || !loc->locinfo->lconv->currency_symbol + || !loc->locinfo->lconv->mon_decimal_point || !loc->locinfo->lconv->mon_thousands_sep + || !loc->locinfo->lconv->mon_grouping || !loc->locinfo->lconv->positive_sign + || !loc->locinfo->lconv->negative_sign) { + MSVCRT__free_locale(loc); + return NULL; + } + + loc->locinfo->lconv->int_curr_symbol[0] = '\0'; + loc->locinfo->lconv->currency_symbol[0] = '\0'; + loc->locinfo->lconv->mon_decimal_point[0] = '\0'; + loc->locinfo->lconv->mon_thousands_sep[0] = '\0'; + loc->locinfo->lconv->mon_grouping[0] = '\0'; + loc->locinfo->lconv->positive_sign[0] = '\0'; + loc->locinfo->lconv->negative_sign[0] = '\0'; + loc->locinfo->lconv->int_frac_digits = 127; + loc->locinfo->lconv->frac_digits = 127; + loc->locinfo->lconv->p_cs_precedes = 127; + loc->locinfo->lconv->p_sep_by_space = 127; + loc->locinfo->lconv->n_cs_precedes = 127; + loc->locinfo->lconv->n_sep_by_space = 127; + loc->locinfo->lconv->p_sign_posn = 127; + loc->locinfo->lconv->n_sign_posn = 127; + + loc->locinfo->lc_category[LC_MONETARY].locale = _strdup("C"); + } + + if(lcid[LC_NUMERIC] && (category==LC_ALL || category==LC_NUMERIC)) { + if(update_threadlocinfo_category(lcid[LC_NUMERIC], loc, LC_NUMERIC)) { + MSVCRT__free_locale(loc); + return NULL; + } + + if(!loc->locinfo->lconv_intl_refcount) + loc->locinfo->lconv_intl_refcount = malloc(sizeof(int)); + loc->locinfo->lconv_num_refcount = malloc(sizeof(int)); + if(!loc->locinfo->lconv_intl_refcount || !loc->locinfo->lconv_num_refcount) { + MSVCRT__free_locale(loc); + return NULL; + } + + *loc->locinfo->lconv_intl_refcount = 1; + *loc->locinfo->lconv_num_refcount = 1; + + i = GetLocaleInfoA(lcid[LC_NUMERIC], LOCALE_SDECIMAL + |LOCALE_NOUSEROVERRIDE, buf, 256); + if(i && (loc->locinfo->lconv->decimal_point = malloc(i))) + memcpy(loc->locinfo->lconv->decimal_point, buf, i); + else { + MSVCRT__free_locale(loc); + return NULL; + } + + i = GetLocaleInfoA(lcid[LC_NUMERIC], LOCALE_STHOUSAND + |LOCALE_NOUSEROVERRIDE, buf, 256); + if(i && (loc->locinfo->lconv->thousands_sep = malloc(i))) + memcpy(loc->locinfo->lconv->thousands_sep, buf, i); + else { + MSVCRT__free_locale(loc); + return NULL; + } + + i = GetLocaleInfoA(lcid[LC_NUMERIC], LOCALE_SGROUPING + |LOCALE_NOUSEROVERRIDE, buf, 256); + if(i>1) + i = i/2 + (buf[i-2]=='0'?0:1); + if(i && (loc->locinfo->lconv->grouping = malloc(i))) { + for(i=0; buf[i+1]==';'; i+=2) + loc->locinfo->lconv->grouping[i/2] = buf[i]-'0'; + loc->locinfo->lconv->grouping[i/2] = buf[i]-'0'; + if(buf[i] != '0') + loc->locinfo->lconv->grouping[i/2+1] = 127; + } else { + MSVCRT__free_locale(loc); + return NULL; + } + } else { + loc->locinfo->lconv->decimal_point = malloc(sizeof(char[2])); + loc->locinfo->lconv->thousands_sep = malloc(sizeof(char)); + loc->locinfo->lconv->grouping = malloc(sizeof(char)); + if(!loc->locinfo->lconv->decimal_point || !loc->locinfo->lconv->thousands_sep + || !loc->locinfo->lconv->grouping) { + MSVCRT__free_locale(loc); + return NULL; + } + + loc->locinfo->lconv->decimal_point[0] = '.'; + loc->locinfo->lconv->decimal_point[1] = '\0'; + loc->locinfo->lconv->thousands_sep[0] = '\0'; + loc->locinfo->lconv->grouping[0] = '\0'; + + loc->locinfo->lc_category[LC_NUMERIC].locale = _strdup("C"); + } + + if(lcid[LC_TIME] && (category==LC_ALL || category==LC_TIME)) { + if(update_threadlocinfo_category(lcid[LC_TIME], loc, LC_TIME)) { + MSVCRT__free_locale(loc); + return NULL; + } + } else + loc->locinfo->lc_category[LC_TIME].locale = _strdup("C"); + + size = sizeof(MSVCRT___lc_time_data); + lcid_tmp = lcid[LC_TIME] ? lcid[LC_TIME] : MAKELCID(LANG_ENGLISH, SORT_DEFAULT); + for(i=0; ilocinfo->lc_time_curr = malloc(size); + if(!loc->locinfo->lc_time_curr) { + MSVCRT__free_locale(loc); + return NULL; + } + + ret = 0; + for(i=0; ilocinfo->lc_time_curr->str.str[i] = &loc->locinfo->lc_time_curr->data[ret]; + if(time_data[i]==LOCALE_SSHORTDATE && !lcid[LC_TIME]) { + memcpy(&loc->locinfo->lc_time_curr->data[ret], cloc_short_date, sizeof(cloc_short_date)); + ret += sizeof(cloc_short_date); + }else if(time_data[i]==LOCALE_SSHORTDATE && !lcid[LC_TIME]) { + memcpy(&loc->locinfo->lc_time_curr->data[ret], cloc_long_date, sizeof(cloc_long_date)); + ret += sizeof(cloc_long_date); + }else if(time_data[i]==LOCALE_STIMEFORMAT && !lcid[LC_TIME]) { + memcpy(&loc->locinfo->lc_time_curr->data[ret], cloc_time, sizeof(cloc_time)); + ret += sizeof(cloc_time); + }else { + ret += GetLocaleInfoA(lcid_tmp, time_data[i]|LOCALE_NOUSEROVERRIDE, + &loc->locinfo->lc_time_curr->data[ret], size-ret); + } + } + for(i=0; ilocinfo->lc_time_curr->wstr[i] = (wchar_t*)&loc->locinfo->lc_time_curr->data[ret]; + if(time_data[i]==LOCALE_SSHORTDATE && !lcid[LC_TIME]) { + memcpy(&loc->locinfo->lc_time_curr->data[ret], cloc_short_dateW, sizeof(cloc_short_dateW)); + ret += sizeof(cloc_short_dateW); + }else if(time_data[i]==LOCALE_SSHORTDATE && !lcid[LC_TIME]) { + memcpy(&loc->locinfo->lc_time_curr->data[ret], cloc_long_dateW, sizeof(cloc_long_dateW)); + ret += sizeof(cloc_long_dateW); + }else if(time_data[i]==LOCALE_STIMEFORMAT && !lcid[LC_TIME]) { + memcpy(&loc->locinfo->lc_time_curr->data[ret], cloc_timeW, sizeof(cloc_timeW)); + ret += sizeof(cloc_timeW); + }else { + ret += GetLocaleInfoW(lcid_tmp, time_data[i]|LOCALE_NOUSEROVERRIDE, + (wchar_t*)&loc->locinfo->lc_time_curr->data[ret], size-ret)*sizeof(wchar_t); + } + } + loc->locinfo->lc_time_curr->lcid = lcid[LC_TIME]; + + return loc; +} + +/********************************************************************* + * setlocale (MSVCRT.@) + */ +char* CDECL setlocale(int category, const char* locale) +{ + MSVCRT__locale_t loc; + MSVCRT_pthreadlocinfo locinfo = get_locinfo(); + + if(categoryLC_MAX) + return NULL; + + if(!locale) { + if(category == LC_ALL) + return construct_lc_all(locinfo); + + return locinfo->lc_category[category].locale; + } + + loc = MSVCRT__create_locale(category, locale); + if(!loc) { + WARN("%d %s failed\n", category, locale); + return NULL; + } + + LOCK_LOCALE; + + switch(category) { + case LC_ALL: + case LC_COLLATE: + locinfo->lc_collate_cp = loc->locinfo->lc_collate_cp; + locinfo->lc_handle[LC_COLLATE] = + loc->locinfo->lc_handle[LC_COLLATE]; + swap_pointers((void**)&locinfo->lc_category[LC_COLLATE].locale, + (void**)&loc->locinfo->lc_category[LC_COLLATE].locale); + swap_pointers((void**)&locinfo->lc_category[LC_COLLATE].refcount, + (void**)&loc->locinfo->lc_category[LC_COLLATE].refcount); + + if(category != LC_ALL) + break; + /* fall through */ + case LC_CTYPE: + locinfo->lc_handle[LC_CTYPE] = + loc->locinfo->lc_handle[LC_CTYPE]; + swap_pointers((void**)&locinfo->lc_category[LC_CTYPE].locale, + (void**)&loc->locinfo->lc_category[LC_CTYPE].locale); + swap_pointers((void**)&locinfo->lc_category[LC_CTYPE].refcount, + (void**)&loc->locinfo->lc_category[LC_CTYPE].refcount); + + locinfo->lc_codepage = loc->locinfo->lc_codepage; + locinfo->lc_clike = loc->locinfo->lc_clike; + locinfo->mb_cur_max = loc->locinfo->mb_cur_max; + + swap_pointers((void**)&locinfo->ctype1_refcount, + (void**)&loc->locinfo->ctype1_refcount); + swap_pointers((void**)&locinfo->ctype1, (void**)&loc->locinfo->ctype1); + swap_pointers((void**)&locinfo->pctype, (void**)&loc->locinfo->pctype); + swap_pointers((void**)&locinfo->pclmap, (void**)&loc->locinfo->pclmap); + swap_pointers((void**)&locinfo->pcumap, (void**)&loc->locinfo->pcumap); + + if(category != LC_ALL) + break; + /* fall through */ + case LC_MONETARY: + locinfo->lc_handle[LC_MONETARY] = + loc->locinfo->lc_handle[LC_MONETARY]; + swap_pointers((void**)&locinfo->lc_category[LC_MONETARY].locale, + (void**)&loc->locinfo->lc_category[LC_MONETARY].locale); + swap_pointers((void**)&locinfo->lc_category[LC_MONETARY].refcount, + (void**)&loc->locinfo->lc_category[LC_MONETARY].refcount); + + swap_pointers((void**)&locinfo->lconv->int_curr_symbol, + (void**)&loc->locinfo->lconv->int_curr_symbol); + swap_pointers((void**)&locinfo->lconv->currency_symbol, + (void**)&loc->locinfo->lconv->currency_symbol); + swap_pointers((void**)&locinfo->lconv->mon_decimal_point, + (void**)&loc->locinfo->lconv->mon_decimal_point); + swap_pointers((void**)&locinfo->lconv->mon_thousands_sep, + (void**)&loc->locinfo->lconv->mon_thousands_sep); + swap_pointers((void**)&locinfo->lconv->mon_grouping, + (void**)&loc->locinfo->lconv->mon_grouping); + swap_pointers((void**)&locinfo->lconv->positive_sign, + (void**)&loc->locinfo->lconv->positive_sign); + swap_pointers((void**)&locinfo->lconv->negative_sign, + (void**)&loc->locinfo->lconv->negative_sign); + locinfo->lconv->int_frac_digits = loc->locinfo->lconv->int_frac_digits; + locinfo->lconv->frac_digits = loc->locinfo->lconv->frac_digits; + locinfo->lconv->p_cs_precedes = loc->locinfo->lconv->p_cs_precedes; + locinfo->lconv->p_sep_by_space = loc->locinfo->lconv->p_sep_by_space; + locinfo->lconv->n_cs_precedes = loc->locinfo->lconv->n_cs_precedes; + locinfo->lconv->n_sep_by_space = loc->locinfo->lconv->n_sep_by_space; + locinfo->lconv->p_sign_posn = loc->locinfo->lconv->p_sign_posn; + locinfo->lconv->n_sign_posn = loc->locinfo->lconv->n_sign_posn; + + if(category != LC_ALL) + break; + /* fall through */ + case LC_NUMERIC: + locinfo->lc_handle[LC_NUMERIC] = + loc->locinfo->lc_handle[LC_NUMERIC]; + swap_pointers((void**)&locinfo->lc_category[LC_NUMERIC].locale, + (void**)&loc->locinfo->lc_category[LC_NUMERIC].locale); + swap_pointers((void**)&locinfo->lc_category[LC_NUMERIC].refcount, + (void**)&loc->locinfo->lc_category[LC_NUMERIC].refcount); + + swap_pointers((void**)&locinfo->lconv->decimal_point, + (void**)&loc->locinfo->lconv->decimal_point); + swap_pointers((void**)&locinfo->lconv->thousands_sep, + (void**)&loc->locinfo->lconv->thousands_sep); + swap_pointers((void**)&locinfo->lconv->grouping, + (void**)&loc->locinfo->lconv->grouping); + + if(category != LC_ALL) + break; + /* fall through */ + case LC_TIME: + locinfo->lc_handle[LC_TIME] = + loc->locinfo->lc_handle[LC_TIME]; + swap_pointers((void**)&locinfo->lc_category[LC_TIME].locale, + (void**)&loc->locinfo->lc_category[LC_TIME].locale); + swap_pointers((void**)&locinfo->lc_category[LC_TIME].refcount, + (void**)&loc->locinfo->lc_category[LC_TIME].refcount); + swap_pointers((void**)&locinfo->lc_time_curr, + (void**)&loc->locinfo->lc_time_curr); + + if(category != LC_ALL) + break; + } + + MSVCRT__free_locale(loc); + UNLOCK_LOCALE; + + if(locinfo == MSVCRT_locale->locinfo) { + int i; + + MSVCRT___lc_codepage = locinfo->lc_codepage; + MSVCRT___lc_collate_cp = locinfo->lc_collate_cp; + __mb_cur_max = locinfo->mb_cur_max; + _pctype = locinfo->pctype; + for(i=LC_MIN; i<=LC_MAX; i++) + MSVCRT___lc_handle[i] = MSVCRT_locale->locinfo->lc_handle[i]; + } + + if(category == LC_ALL) + return construct_lc_all(locinfo); + + return locinfo->lc_category[category].locale; +} + +/* _configthreadlocale - not exported in native msvcrt */ +int CDECL _configthreadlocale(int type) +{ + thread_data_t *data = msvcrt_get_thread_data(); + MSVCRT__locale_t locale; + int ret; + + if(!data) + return -1; + + ret = (data->have_locale ? _ENABLE_PER_THREAD_LOCALE : _DISABLE_PER_THREAD_LOCALE); + + if(type == _ENABLE_PER_THREAD_LOCALE) { + if(!data->have_locale) { + /* Copy current global locale */ + locale = MSVCRT__create_locale(LC_ALL, setlocale(LC_ALL, NULL)); + if(!locale) + return -1; + + data->locinfo = locale->locinfo; + data->mbcinfo = locale->mbcinfo; + data->have_locale = TRUE; + free(locale); + } + + return ret; + } + + if(type == _DISABLE_PER_THREAD_LOCALE) { + if(data->have_locale) { + free_locinfo(data->locinfo); + free_mbcinfo(data->mbcinfo); + data->locinfo = MSVCRT_locale->locinfo; + data->mbcinfo = MSVCRT_locale->mbcinfo; + data->have_locale = FALSE; + } + + return ret; + } + + if(!type) + return ret; + + return -1; +} + +/********************************************************************* + * _getmbcp (MSVCRT.@) + */ +int CDECL _getmbcp(void) +{ + return get_mbcinfo()->mbcodepage; +} + +extern unsigned int __setlc_active; +/********************************************************************* + * ___setlc_active_func (MSVCRT.@) + */ +unsigned int CDECL ___setlc_active_func(void) +{ + return __setlc_active; +} + +extern unsigned int __unguarded_readlc_active; +/********************************************************************* + * ___unguarded_readlc_active_add_func (MSVCRT.@) + */ +unsigned int * CDECL ___unguarded_readlc_active_add_func(void) +{ + return &__unguarded_readlc_active; +} + +MSVCRT__locale_t global_locale = NULL; +void __init_global_locale() +{ + unsigned i; + + LOCK_LOCALE; + global_locale = MSVCRT__create_locale(0, "C"); + + MSVCRT___lc_codepage = MSVCRT_locale->locinfo->lc_codepage; + MSVCRT___lc_collate_cp = MSVCRT_locale->locinfo->lc_collate_cp; + __mb_cur_max = MSVCRT_locale->locinfo->mb_cur_max; + for(i=LC_MIN; i<=LC_MAX; i++) + MSVCRT___lc_handle[i] = MSVCRT_locale->locinfo->lc_handle[i]; + _setmbcp(_MB_CP_ANSI); + UNLOCK_LOCALE; +} + +/* + * @implemented + */ +const unsigned short **__p__pctype(void) +{ + return &get_locinfo()->pctype; +} + +const unsigned short* __cdecl __pctype_func(void) +{ + return get_locinfo()->pctype; +} + diff --git a/reactos/lib/sdk/crt/mbstring/_setmbcp.c b/reactos/lib/sdk/crt/mbstring/_setmbcp.c new file mode 100644 index 00000000000..be23cc5899a --- /dev/null +++ b/reactos/lib/sdk/crt/mbstring/_setmbcp.c @@ -0,0 +1,222 @@ +/* + * msvcrt.dll mbcs functions + * + * Copyright 1999 Alexandre Julliard + * Copyright 2000 Jon Griffths + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + * FIXME + * Not currently binary compatible with win32. MSVCRT_mbctype must be + * populated correctly and the ismb* functions should reference it. + */ + +#include + +#include + +/* It seems that the data about valid trail bytes is not available from kernel32 + * so we have to store is here. The format is the same as for lead bytes in CPINFO */ +struct cp_extra_info_t +{ + int cp; + BYTE TrailBytes[MAX_LEADBYTES]; +}; + +static struct cp_extra_info_t g_cpextrainfo[] = +{ + {932, {0x40, 0x7e, 0x80, 0xfc, 0, 0}}, + {936, {0x40, 0xfe, 0, 0}}, + {949, {0x41, 0xfe, 0, 0}}, + {950, {0x40, 0x7e, 0xa1, 0xfe, 0, 0}}, + {1361, {0x31, 0x7e, 0x81, 0xfe, 0, 0}}, + {20932, {1, 255, 0, 0}}, /* seems to give different results on different systems */ + {0, {1, 255, 0, 0}} /* match all with FIXME */ +}; + +/********************************************************************* + * INTERNAL: _setmbcp_l + */ +int _setmbcp_l(int cp, LCID lcid, MSVCRT_pthreadmbcinfo mbcinfo) +{ + const char format[] = ".%d"; + + int newcp; + CPINFO cpi; + BYTE *bytes; + WORD chartypes[256]; + char bufA[256]; + WCHAR bufW[256]; + int charcount; + int ret; + int i; + + if(!mbcinfo) + mbcinfo = get_mbcinfo(); + + switch (cp) + { + case _MB_CP_ANSI: + newcp = GetACP(); + break; + case _MB_CP_OEM: + newcp = GetOEMCP(); + break; + case _MB_CP_LOCALE: + newcp = get_locinfo()->lc_codepage; + if(newcp) + break; + /* fall through (C locale) */ + case _MB_CP_SBCS: + newcp = 20127; /* ASCII */ + break; + default: + newcp = cp; + break; + } + + if(lcid == -1) { + sprintf(bufA, format, newcp); + mbcinfo->mblcid = MSVCRT_locale_to_LCID(bufA); + } else { + mbcinfo->mblcid = lcid; + } + + if(mbcinfo->mblcid == -1) + { + WARN("Can't assign LCID to codepage (%d)\n", mbcinfo->mblcid); + mbcinfo->mblcid = 0; + } + + if (!GetCPInfo(newcp, &cpi)) + { + WARN("Codepage %d not found\n", newcp); + *_errno() = EINVAL; + return -1; + } + + /* setup the _mbctype */ + memset(mbcinfo->mbctype, 0, sizeof(unsigned char[257])); + memset(mbcinfo->mbcasemap, 0, sizeof(unsigned char[256])); + + bytes = cpi.LeadByte; + while (bytes[0] || bytes[1]) + { + for (i = bytes[0]; i <= bytes[1]; i++) + mbcinfo->mbctype[i + 1] |= _M1; + bytes += 2; + } + + if (cpi.MaxCharSize > 1) + { + /* trail bytes not available through kernel32 but stored in a structure in msvcrt */ + struct cp_extra_info_t *cpextra = g_cpextrainfo; + + mbcinfo->ismbcodepage = 1; + while (TRUE) + { + if (cpextra->cp == 0 || cpextra->cp == newcp) + { + if (cpextra->cp == 0) + FIXME("trail bytes data not available for DBCS codepage %d - assuming all bytes\n", newcp); + + bytes = cpextra->TrailBytes; + while (bytes[0] || bytes[1]) + { + for (i = bytes[0]; i <= bytes[1]; i++) + mbcinfo->mbctype[i + 1] |= _M2; + bytes += 2; + } + break; + } + cpextra++; + } + } + else + mbcinfo->ismbcodepage = 0; + + /* we can't use GetStringTypeA directly because we don't have a locale - only a code page + */ + charcount = 0; + for (i = 0; i < 256; i++) + if (!(mbcinfo->mbctype[i + 1] & _M1)) + bufA[charcount++] = i; + + ret = MultiByteToWideChar(newcp, 0, bufA, charcount, bufW, charcount); + if (ret != charcount) + ERR("MultiByteToWideChar of chars failed for cp %d, ret=%d (exp %d), error=%d\n", newcp, ret, charcount, GetLastError()); + + GetStringTypeW(CT_CTYPE1, bufW, charcount, chartypes); + + charcount = 0; + for (i = 0; i < 256; i++) + if (!(mbcinfo->mbctype[i + 1] & _M1)) + { + if (chartypes[charcount] & C1_UPPER) + { + mbcinfo->mbctype[i + 1] |= _SBUP; + bufW[charcount] = tolowerW(bufW[charcount]); + } + else if (chartypes[charcount] & C1_LOWER) + { + mbcinfo->mbctype[i + 1] |= _SBLOW; + bufW[charcount] = toupperW(bufW[charcount]); + } + charcount++; + } + + ret = WideCharToMultiByte(newcp, 0, bufW, charcount, bufA, charcount, NULL, NULL); + if (ret != charcount) + ERR("WideCharToMultiByte failed for cp %d, ret=%d (exp %d), error=%d\n", newcp, ret, charcount, GetLastError()); + + charcount = 0; + for (i = 0; i < 256; i++) + { + if(!(mbcinfo->mbctype[i + 1] & _M1)) + { + if(mbcinfo->mbctype[i] & (C1_UPPER|C1_LOWER)) + mbcinfo->mbcasemap[i] = bufA[charcount]; + charcount++; + } + } + + if (newcp == 932) /* CP932 only - set _MP and _MS */ + { + /* On Windows it's possible to calculate the _MP and _MS from CT_CTYPE1 + * and CT_CTYPE3. But as of Wine 0.9.43 we return wrong values what makes + * it hard. As this is set only for codepage 932 we hardcode it what gives + * also faster execution. + */ + for (i = 161; i <= 165; i++) + mbcinfo->mbctype[i + 1] |= _MP; + for (i = 166; i <= 223; i++) + mbcinfo->mbctype[i + 1] |= _MS; + } + + mbcinfo->mbcodepage = newcp; + if(MSVCRT_locale && mbcinfo == MSVCRT_locale->mbcinfo) + memcpy(_mbctype, MSVCRT_locale->mbcinfo->mbctype, sizeof(_mbctype)); + + return 0; +} + +/********************************************************************* + * _setmbcp (MSVCRT.@) + */ +int CDECL _setmbcp(int cp) +{ + return _setmbcp_l(cp, -1, NULL); +} + diff --git a/reactos/lib/sdk/crt/mbstring/mbsncpy.c b/reactos/lib/sdk/crt/mbstring/mbsncpy.c index 407a8204cda..4780e278dfc 100644 --- a/reactos/lib/sdk/crt/mbstring/mbsncpy.c +++ b/reactos/lib/sdk/crt/mbstring/mbsncpy.c @@ -13,17 +13,18 @@ #include #include -extern int g_mbcp_is_multibyte; - -/* - * @implemented +/********************************************************************* + * _mbsncpy(MSVCRT.@) + * REMARKS + * The parameter n is the number or characters to copy, not the size of + * the buffer. Use _mbsnbcpy for a function analogical to strncpy */ -unsigned char* _mbsncpy(unsigned char *dst, const unsigned char *src, size_t n) +unsigned char* CDECL _mbsncpy(unsigned char* dst, const unsigned char* src, size_t n) { unsigned char* ret = dst; if(!n) return dst; - if (g_mbcp_is_multibyte) + if (get_mbcinfo()->ismbcodepage) { while (*src && n) { @@ -55,48 +56,12 @@ unsigned char* _mbsncpy(unsigned char *dst, const unsigned char *src, size_t n) return ret; } - -/* - * The _mbsnbcpy function copies count bytes from src to dest. If src is shorter - * than dest, the string is padded with null characters. If dest is less than or - * equal to count it is not terminated with a null character. - * - * @implemented - */ -unsigned char * _mbsnbcpy(unsigned char *dst, const unsigned char *src, size_t n) -{ - unsigned char* ret = dst; - if(!n) - return dst; - if(g_mbcp_is_multibyte) - { - int is_lead = 0; - while (*src && n) - { - is_lead = (!is_lead && _ismbblead(*src)); - n--; - *dst++ = *src++; - } - - if (is_lead) /* if string ends with a lead, remove it */ - *(dst - 1) = 0; - } - else - { - while (n) - { - n--; - if (!(*dst++ = *src++)) break; - } - } - while (n--) *dst++ = 0; - return ret; -} - -/* +/********************************************************************* + * _mbsnbcpy_s(MSVCRT.@) + * REMARKS * Unlike _mbsnbcpy this function does not pad the rest of the dest * string with 0 -*/ + */ int CDECL _mbsnbcpy_s(unsigned char* dst, size_t size, const unsigned char* src, size_t n) { size_t pos = 0; @@ -111,7 +76,7 @@ int CDECL _mbsnbcpy_s(unsigned char* dst, size_t size, const unsigned char* src, if(!n) return 0; - if(g_mbcp_is_multibyte) + if(get_mbcinfo()->ismbcodepage) { int is_lead = 0; while (*src && n) @@ -155,3 +120,40 @@ int CDECL _mbsnbcpy_s(unsigned char* dst, size_t size, const unsigned char* src, return 0; } + +/********************************************************************* + * _mbsnbcpy(MSVCRT.@) + * REMARKS + * Like strncpy this function doesn't enforce the string to be + * NUL-terminated + */ +unsigned char* CDECL _mbsnbcpy(unsigned char* dst, const unsigned char* src, size_t n) +{ + unsigned char* ret = dst; + if(!n) + return dst; + if(get_mbcinfo()->ismbcodepage) + { + int is_lead = 0; + while (*src && n) + { + is_lead = (!is_lead && _ismbblead(*src)); + n--; + *dst++ = *src++; + } + + if (is_lead) /* if string ends with a lead, remove it */ + *(dst - 1) = 0; + } + else + { + while (n) + { + n--; + if (!(*dst++ = *src++)) break; + } + } + while (n--) *dst++ = 0; + return ret; +} + diff --git a/reactos/lib/sdk/crt/misc/tls.c b/reactos/lib/sdk/crt/misc/tls.c index 37d37f598f0..0504fbfca95 100644 --- a/reactos/lib/sdk/crt/misc/tls.c +++ b/reactos/lib/sdk/crt/misc/tls.c @@ -41,8 +41,8 @@ thread_data_t *msvcrt_get_thread_data(void) ptr->tid = GetCurrentThreadId(); ptr->handle = INVALID_HANDLE_VALUE; ptr->random_seed = 1; - //ptr->locinfo = MSVCRT_locale->locinfo; - //ptr->mbcinfo = MSVCRT_locale->mbcinfo; + ptr->locinfo = MSVCRT_locale->locinfo; + ptr->mbcinfo = MSVCRT_locale->mbcinfo; } SetLastError( err ); return ptr; diff --git a/reactos/lib/sdk/crt/precomp.h b/reactos/lib/sdk/crt/precomp.h index 7fe14dd1e9e..4d01c7c7ce0 100644 --- a/reactos/lib/sdk/crt/precomp.h +++ b/reactos/lib/sdk/crt/precomp.h @@ -57,6 +57,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(msvcrt); #include #include #include +#include #include #include #include diff --git a/reactos/lib/sdk/crt/string/ctype.c b/reactos/lib/sdk/crt/string/ctype.c index a28fd2058f1..20dbb3cc044 100644 --- a/reactos/lib/sdk/crt/string/ctype.c +++ b/reactos/lib/sdk/crt/string/ctype.c @@ -548,24 +548,12 @@ const unsigned short _wctype[] = { 0x0100 | _LOWER, /* 0xfe */ 0x0100 | _LOWER /* 0xff */ }; + const unsigned short *_pctype = _ctype + 1; const unsigned short *_pwctype = _wctype + 1; extern const unsigned short wine_wctype_table[]; -/* - * @implemented - */ -const unsigned short **__p__pctype(void) -{ - return &_pctype; -} - -const unsigned short* __cdecl __pctype_func(void) -{ - return _pctype; -} - /* * @implemented */ diff --git a/reactos/lib/sdk/crt/string/wcs.c b/reactos/lib/sdk/crt/string/wcs.c index 016745871d3..846179a0589 100644 --- a/reactos/lib/sdk/crt/string/wcs.c +++ b/reactos/lib/sdk/crt/string/wcs.c @@ -238,122 +238,81 @@ wchar_t* CDECL wcspbrk( const wchar_t* str, const wchar_t* accept ) /********************************************************************* * wctomb (MSVCRT.@) */ -INT CDECL wctomb(char *mbchar, wchar_t wchar) +/********************************************************************* + * wctomb (MSVCRT.@) + */ +INT CDECL wctomb( char *dst, wchar_t ch ) { - BOOL bUsedDefaultChar; - char chMultiByte[MB_LEN_MAX]; - int nBytes; + BOOL error; + INT size; - /* At least one parameter needs to be given, the length of a null character cannot be queried (verified by tests under WinXP SP2) */ - if(!mbchar && !wchar) - return 0; - - /* Use WideCharToMultiByte for doing the conversion using the codepage currently set with setlocale() */ - nBytes = WideCharToMultiByte(MSVCRT___lc_codepage, 0, &wchar, 1, chMultiByte, MB_LEN_MAX, NULL, &bUsedDefaultChar); - - /* Only copy the character if an 'mbchar' pointer was given. - - The "C" locale is emulated with codepage 1252 here. This codepage has a default character "?", but the "C" locale doesn't have one. - Therefore don't copy the character in this case. */ - if(mbchar && !(MSVCRT_current_lc_all[0] == 'C' && !MSVCRT_current_lc_all[1] && bUsedDefaultChar)) - memcpy(mbchar, chMultiByte, nBytes); - - /* If the default character was used, set errno to EILSEQ and return -1. */ - if(bUsedDefaultChar) - { - _set_errno(EILSEQ); - return -1; + size = WideCharToMultiByte(get_locinfo()->lc_codepage, 0, &ch, 1, dst, dst ? 6 : 0, NULL, &error); + if(!size || error) { + *_errno() = EINVAL; + return EOF; } - - /* Otherwise return the number of bytes this character occupies. */ - return nBytes; + return size; } +/********************************************************************* + * wcsrtombs_l (INTERNAL) + */ +static size_t CDECL wcsrtombs_l(char *mbstr, const wchar_t **wcstr, size_t count, _locale_t locale) +{ + MSVCRT_pthreadlocinfo locinfo; + size_t tmp = 0; + BOOL used_default; + + if(!locale) + locinfo = get_locinfo(); + else + locinfo = ((MSVCRT__locale_t)locale)->locinfo; + + if(!mbstr) { + tmp = WideCharToMultiByte(locinfo->lc_codepage, WC_NO_BEST_FIT_CHARS, + *wcstr, -1, NULL, 0, NULL, &used_default)-1; + if(used_default) + return -1; + return tmp; + } + + while(**wcstr) { + char buf[3]; + size_t i, size; + + size = WideCharToMultiByte(locinfo->lc_codepage, WC_NO_BEST_FIT_CHARS, + *wcstr, 1, buf, 3, NULL, &used_default); + if(used_default) + return -1; + if(tmp+size > count) + return tmp; + + for(i=0; i= 0 && *wcstr) - { - /* Get the length of this character */ - nResult = wctomb(NULL, *wcstr++); - - /* If this character is not convertible in the current locale, the end result will be -1 */ - if(nResult == -1) - return -1; - - nLength += nResult; - } - - /* Return the final length */ - return nLength; - } - - /* Convert the string then */ - bUsedDefaultChar = FALSE; - - for(;;) - { - char chMultiByte[MB_LEN_MAX]; - UINT uLength; - - /* Are we at the terminating null character? */ - if(!*wcstr) - { - /* Set the null character, but don't increment the pointer as the returned length never includes the terminating null character */ - *p = 0; - break; - } - - /* Convert this character into the temporary chMultiByte variable */ - ZeroMemory(chMultiByte, MB_LEN_MAX); - nResult = wctomb(chMultiByte, *wcstr++); - - /* Check if this was an invalid character */ - if(nResult == -1) - bUsedDefaultChar = TRUE; - - /* If we got no character, stop the conversion process here */ - if(!chMultiByte[0]) - break; - - /* Determine whether this is a double-byte or a single-byte character */ - if(chMultiByte[1]) - uLength = 2; - else - uLength = 1; - - /* Decrease 'count' by the character length and check if the buffer can still hold the full character */ - count -= uLength; - - if((int)count < 0) - break; - - /* It can, so copy it and move the pointer forward */ - memcpy(p, chMultiByte, uLength); - p += uLength; - } - - if(bUsedDefaultChar) - return -1; - - /* Return the length in bytes of the copied characters (without the terminating null character) */ - return p - mbstr; + return wcsrtombs_l(mbstr, &wcstr, count, NULL); } #endif diff --git a/reactos/lib/sdk/crt/time/strftime.c b/reactos/lib/sdk/crt/time/strftime.c index 797e4c3ece5..996b0fcda17 100644 --- a/reactos/lib/sdk/crt/time/strftime.c +++ b/reactos/lib/sdk/crt/time/strftime.c @@ -6,14 +6,320 @@ * PROGRAMER: */ #include -#include -size_t -_tcsftime(_TCHAR *strDest, - size_t maxsize, - const _TCHAR *format, - const struct tm *timeptr) +static inline BOOL strftime_date(char *str, size_t *pos, size_t max, + BOOL alternate, const struct tm *mstm, MSVCRT___lc_time_data *time_data) { + char *format; + SYSTEMTIME st; + size_t ret; + + st.wYear = mstm->tm_year + 1900; + st.wMonth = mstm->tm_mon + 1; + st.wDayOfWeek = mstm->tm_wday; + st.wDay = mstm->tm_mday; + st.wHour = mstm->tm_hour; + st.wMinute = mstm->tm_min; + st.wSecond = mstm->tm_sec; + st.wMilliseconds = 0; + + format = alternate ? time_data->str.names.date : time_data->str.names.short_date; + ret = GetDateFormatA(time_data->lcid, 0, &st, format, NULL, 0); + if(ret && retlcid, 0, &st, format, str+*pos, max-*pos); + if(!ret) { + *str = 0; + *_errno() = EINVAL; + return FALSE; + }else if(ret > max-*pos) { + *str = 0; + *_errno() = ERANGE; + return FALSE; + } + *pos += ret-1; + return TRUE; +} + +static inline BOOL strftime_time(char *str, size_t *pos, size_t max, + const struct tm *mstm, MSVCRT___lc_time_data *time_data) +{ + SYSTEMTIME st; + size_t ret; + + st.wYear = mstm->tm_year + 1900; + st.wMonth = mstm->tm_mon + 1; + st.wDayOfWeek = mstm->tm_wday; + st.wDay = mstm->tm_mday; + st.wHour = mstm->tm_hour; + st.wMinute = mstm->tm_min; + st.wSecond = mstm->tm_sec; + st.wMilliseconds = 0; + + ret = GetTimeFormatA(time_data->lcid, 0, &st, time_data->str.names.time, NULL, 0); + if(ret && retlcid, 0, &st, time_data->str.names.time, + str+*pos, max-*pos); + if(!ret) { + *str = 0; + *_errno() = EINVAL; + return FALSE; + }else if(ret > max-*pos) { + *str = 0; + *_errno() = ERANGE; + return FALSE; + } + *pos += ret-1; + return TRUE; +} + +static inline BOOL strftime_str(char *str, size_t *pos, size_t max, char *src) +{ + size_t len = strlen(src); + if(len > max-*pos) { + *str = 0; + *_errno() = ERANGE; + return FALSE; + } + + memcpy(str+*pos, src, len); + *pos += len; + return TRUE; +} + +static inline BOOL strftime_int(char *str, size_t *pos, size_t max, + int src, int prec, int l, int h) +{ + size_t len; + + if(srch) { + *str = 0; + *_errno() = EINVAL; + return FALSE; + } + + len = _snprintf(str+*pos, max-*pos, "%0*d", prec, src); + if(len == -1) { + *str = 0; + *_errno() = ERANGE; + return FALSE; + } + + *pos += len; + return TRUE; +} + +/********************************************************************* + * _Strftime (MSVCRT.@) + */ +size_t CDECL _Strftime(char *str, size_t max, const char *format, + const struct tm *mstm, MSVCRT___lc_time_data *time_data) +{ + size_t ret, tmp; + BOOL alternate; + + TRACE("(%p %ld %s %p %p)\n", str, max, format, mstm, time_data); + + if(!str || !format) { + if(str && max) + *str = 0; + *_errno() = EINVAL; + return 0; + } + + if(!time_data) + time_data = get_locinfo()->lc_time_curr; + + for(ret=0; *format && rettm_wday<0 || mstm->tm_wday>6) + goto einval_error; + if(!strftime_str(str, &ret, max, time_data->str.names.short_wday[mstm->tm_wday])) + return 0; + break; + case 'A': + if(mstm->tm_wday<0 || mstm->tm_wday>6) + goto einval_error; + if(!strftime_str(str, &ret, max, time_data->str.names.wday[mstm->tm_wday])) + return 0; + break; + case 'b': + if(mstm->tm_mon<0 || mstm->tm_mon>11) + goto einval_error; + if(!strftime_str(str, &ret, max, time_data->str.names.short_mon[mstm->tm_mon])) + return 0; + break; + case 'B': + if(mstm->tm_mon<0 || mstm->tm_mon>11) + goto einval_error; + if(!strftime_str(str, &ret, max, time_data->str.names.mon[mstm->tm_mon])) + return 0; + break; + case 'd': + if(!strftime_int(str, &ret, max, mstm->tm_mday, alternate ? 0 : 2, 0, 31)) + return 0; + break; + case 'H': + if(!strftime_int(str, &ret, max, mstm->tm_hour, alternate ? 0 : 2, 0, 23)) + return 0; + break; + case 'I': + tmp = mstm->tm_hour; + if(tmp > 12) + tmp -= 12; + else if(!tmp) + tmp = 12; + if(!strftime_int(str, &ret, max, tmp, alternate ? 0 : 2, 1, 12)) + return 0; + break; + case 'j': + if(!strftime_int(str, &ret, max, mstm->tm_yday+1, alternate ? 0 : 3, 1, 366)) + return 0; + break; + case 'm': + if(!strftime_int(str, &ret, max, mstm->tm_mon+1, alternate ? 0 : 2, 1, 12)) + return 0; + break; + case 'M': + if(!strftime_int(str, &ret, max, mstm->tm_min, alternate ? 0 : 2, 0, 59)) + return 0; + break; + case 'p': + if(mstm->tm_hour<0 || mstm->tm_hour>23) + goto einval_error; + if(!strftime_str(str, &ret, max, mstm->tm_hour<12 ? + time_data->str.names.am : time_data->str.names.pm)) + return 0; + break; + case 'S': + if(!strftime_int(str, &ret, max, mstm->tm_sec, alternate ? 0 : 2, 0, 59)) + return 0; + break; + case 'w': + if(!strftime_int(str, &ret, max, mstm->tm_wday, 0, 0, 6)) + return 0; + break; + case 'y': + if(!strftime_int(str, &ret, max, mstm->tm_year%100, alternate ? 0 : 2, 0, 99)) + return 0; + break; + case 'Y': + tmp = 1900+mstm->tm_year; + if(!strftime_int(str, &ret, max, tmp, alternate ? 0 : 4, 0, 9999)) + return 0; + break; + case 'z': + case 'Z': + _tzset(); + if(_get_tzname(&tmp, str+ret, max-ret, mstm->tm_isdst ? 1 : 0)) + return 0; + ret += tmp; + break; + case 'U': + case 'W': + if(mstm->tm_wday<0 || mstm->tm_wday>6 || mstm->tm_yday<0 || mstm->tm_yday>365) + goto einval_error; + if(*format == 'U') + tmp = mstm->tm_wday; + else if(!mstm->tm_wday) + tmp = 6; + else + tmp = mstm->tm_wday-1; + + tmp = mstm->tm_yday/7 + (tmp<=mstm->tm_yday%7); + if(!strftime_int(str, &ret, max, tmp, alternate ? 0 : 2, 0, 53)) + return 0; + break; + case '%': + str[ret++] = '%'; + break; + default: + WARN("unknown format %c\n", *format); + goto einval_error; + } + } + + if(ret == max) { + if(max) + *str = 0; + *_errno() = ERANGE; + return 0; + } + + str[ret] = 0; + return ret; + +einval_error: + *str = 0; + *_errno() = EINVAL; return 0; } +/********************************************************************* + * strftime (MSVCRT.@) + */ +size_t CDECL strftime( char *str, size_t max, const char *format, + const struct tm *mstm ) +{ + return _Strftime(str, max, format, mstm, NULL); +} + +/********************************************************************* + * wcsftime (MSVCRT.@) + */ +size_t CDECL wcsftime( wchar_t *str, size_t max, + const wchar_t *format, const struct tm *mstm ) +{ + char *s, *fmt; + size_t len; + + TRACE("%p %ld %s %p\n", str, max, debugstr_w(format), mstm ); + + len = WideCharToMultiByte( CP_ACP, 0, format, -1, NULL, 0, NULL, NULL ); + if (!(fmt = malloc( len ))) return 0; + WideCharToMultiByte( CP_ACP, 0, format, -1, fmt, len, NULL, NULL ); + + if ((s = malloc( max*4 ))) + { + if (!strftime( s, max*4, fmt, mstm )) s[0] = 0; + len = MultiByteToWideChar( CP_ACP, 0, s, -1, str, max ); + if (len) len--; + free( s ); + } + else len = 0; + + free( fmt ); + return len; +}