[MSVCRT]
authorJérôme Gardou <jerome.gardou@reactos.org>
Sun, 9 Dec 2012 03:17:14 +0000 (03:17 +0000)
committerJérôme Gardou <jerome.gardou@reactos.org>
Sun, 9 Dec 2012 03:17:14 +0000 (03:17 +0000)
 - Massive update to go towards a proper localization implementation
Based on wine code

svn path=/trunk/; revision=57833

14 files changed:
reactos/dll/win32/msvcrt/dllmain.c
reactos/dll/win32/msvcrt/msvcrt.spec
reactos/lib/sdk/crt/crt.cmake
reactos/lib/sdk/crt/include/internal/locale.h [new file with mode: 0644]
reactos/lib/sdk/crt/include/internal/mbstring.h
reactos/lib/sdk/crt/include/internal/tls.h
reactos/lib/sdk/crt/locale/locale.c
reactos/lib/sdk/crt/mbstring/_setmbcp.c [new file with mode: 0644]
reactos/lib/sdk/crt/mbstring/mbsncpy.c
reactos/lib/sdk/crt/misc/tls.c
reactos/lib/sdk/crt/precomp.h
reactos/lib/sdk/crt/string/ctype.c
reactos/lib/sdk/crt/string/wcs.c
reactos/lib/sdk/crt/time/strftime.c

index 9e904f3..f7e9725 100644 (file)
@@ -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);
index d7d07f6..2b7604b 100644 (file)
 @ 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)
index c60a7cb..b147977 100644 (file)
@@ -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 (file)
index 0000000..861e2f4
--- /dev/null
@@ -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
+
index 703439b..8ceb83f 100644 (file)
@@ -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)
index b5850b7..3c01ba4 100644 (file)
@@ -17,7 +17,7 @@
 #include <internal/wine/eh.h>
 
 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);
index 5abee62..0aebb4b 100644 (file)
 /*
- * 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 <precomp.h>
 #include <locale.h>
 
 #include "mbctype.h"
+#include <internal/wine/msvcrt.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
 
-unsigned char _mbctype[257] = { 0 };
-int g_mbcp_is_multibyte = 0;
+#ifdef _pctype
+#error _pctype should not be defined
+#endif
 
-/* 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;
+unsigned int MSVCRT___lc_codepage = 0;
 int MSVCRT___lc_collate_cp = 0;
-HANDLE MSVCRT___lc_handle[MSVCRT_LC_MAX - MSVCRT_LC_MIN + 1] = { 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 };
 
 /* 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"
 };
 
-/* 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;
+  unsigned int i;
   for (i = 0; i < sizeof(_country_synonyms)/sizeof(char*); i += 2 )
   {
-    if (!_stricmp(_country_synonyms[i],name))
+    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';
+      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
+
+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,657 +199,1311 @@ 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);
-
-  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 (_stricmp(locale->search_codepage,"OCP"))
-        {
-          GetLocaleInfoA(lcid, LOCALE_IDEFAULTCODEPAGE,
-                         locale->found_codepage, MAX_ELEM_LEN);
-        }
-        if (_stricmp(locale->search_codepage,"ACP"))
-        {
-          GetLocaleInfoA(lcid, LOCALE_IDEFAULTANSICODEPAGE,
-                         locale->found_codepage, MAX_ELEM_LEN);
+    LCID lcid;
+    locale_search_t search;
+    const char *cp, *region;
+
+    memset(&search, 0, sizeof(locale_search_t));
+
+    cp = strchr(locale, '.');
+    region = strchr(locale, '_');
+
+    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';
+
+    if(cp) {
+        lstrcpynA(search.search_codepage, cp+1, MAX_ELEM_LEN);
+        if(region && cp-region-1<MAX_ELEM_LEN)
+          search.search_country[cp-region-1] = '\0';
+        if(cp-locale < MAX_ELEM_LEN)
+            search.search_language[cp-locale] = '\0';
+    } else
+        search.search_codepage[0] = '\0';
+
+    if(!search.search_country[0] && !search.search_codepage[0])
+        remap_synonym(search.search_language);
+
+    EnumResourceLanguagesA(GetModuleHandleA("KERNEL32"), (LPSTR)RT_STRING,
+            (LPCSTR)LOCALE_ILANGUAGE,find_best_locale_proc,
+            (LONG_PTR)&search);
+
+    if (!search.match_flags)
+        return -1;
+
+    /* If we were given something that didn't match, fail */
+    if (search.search_country[0] && !(search.match_flags & FOUND_COUNTRY))
+        return -1;
+
+    lcid =  MAKELCID(search.found_lang_id, SORT_DEFAULT);
+
+    /* Populate partial locale, translating LCID to locale string elements */
+    if (!search.found_codepage[0]) {
+        /* Even if a codepage is not enumerated for a locale
+         * it can be set if valid */
+        if (search.search_codepage[0]) {
+            if (IsValidCodePage(atoi(search.search_codepage)))
+                memcpy(search.found_codepage,search.search_codepage,MAX_ELEM_LEN);
+            else {
+                /* Special codepage values: OEM & ANSI */
+                if (strcasecmp(search.search_codepage,"OCP")) {
+                    GetLocaleInfoA(lcid, LOCALE_IDEFAULTCODEPAGE,
+                            search.found_codepage, MAX_ELEM_LEN);
+                } else if (strcasecmp(search.search_codepage,"ACP")) {
+                    GetLocaleInfoA(lcid, LOCALE_IDEFAULTANSICODEPAGE,
+                            search.found_codepage, MAX_ELEM_LEN);
+                } else
+                    return -1;
+
+                if (!atoi(search.found_codepage))
+                    return -1;
+            }
+        } else {
+            /* Prefer ANSI codepages if present */
+            GetLocaleInfoA(lcid, LOCALE_IDEFAULTANSICODEPAGE,
+                    search.found_codepage, MAX_ELEM_LEN);
+            if (!search.found_codepage[0] || !atoi(search.found_codepage))
+                GetLocaleInfoA(lcid, LOCALE_IDEFAULTCODEPAGE,
+                        search.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;
 
-  if (GetCPInfo(codepage, &cp))
-  {
-    int i;
-    char str[3];
-    unsigned char *traverse = cp.LeadByte;
+        loc->locinfo->lc_id[category].wLanguage = 0;
+        while(*p) {
+            loc->locinfo->lc_id[category].wLanguage *= 16;
 
-    memset(MSVCRT_current_ctype, 0, sizeof(MSVCRT__ctype));
-    MSVCRT___lc_codepage = codepage;
-    MSVCRT___lc_collate_cp = codepage;
+            if(*p <= '9')
+                loc->locinfo->lc_id[category].wLanguage += *p-'0';
+            else
+                loc->locinfo->lc_id[category].wLanguage += *p-'a'+10;
 
-    /* Switch ctype macros to MBCS if needed */
-    __mb_cur_max = cp.MaxCharSize;
+            p++;
+        }
 
-    /* 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);
-      }
+        loc->locinfo->lc_id[category].wCountry =
+            loc->locinfo->lc_id[category].wLanguage;
     }
 
-    /* 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(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;
 
-/*
- * @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;
+    hlp = *p1;
+    *p1 = *p2;
+    *p2 = hlp;
+}
 
-  TRACE("(%d %s)\n",category,locale);
+/* INTERNAL: returns pthreadlocinfo struct */
+MSVCRT_pthreadlocinfo get_locinfo(void) {
+    thread_data_t *data = msvcrt_get_thread_data();
 
-  if (category < MSVCRT_LC_MIN || category > MSVCRT_LC_MAX)
-    return NULL;
+    if(!data || !data->have_locale)
+        return MSVCRT_locale->locinfo;
 
-  if (locale == NULL)
-  {
-    /* Report the current Locale */
-    return MSVCRT_current_lc_all;
-  }
+    return data->locinfo;
+}
 
-  LOCK_LOCALE;
+/* INTERNAL: returns pthreadlocinfo struct */
+MSVCRT_pthreadmbcinfo get_mbcinfo(void) {
+    thread_data_t *data = msvcrt_get_thread_data();
 
-  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;
-  }
+    if(!data || !data->have_locale)
+        return MSVCRT_locale->mbcinfo;
 
-  /* 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;
-  }
+    return data->mbcinfo;
+}
 
-  /* Get locale elements */
-  haveLang = haveCountry = haveCP = 0;
-  memset(&lc,0,sizeof(lc));
+/* INTERNAL: constructs string returned by setlocale */
+static inline char* construct_lc_all(MSVCRT_pthreadlocinfo locinfo) {
+    static char current_lc_all[MAX_LOCALE_LENGTH];
 
-  next = strchr(locale,'_');
-  if (next && next != locale)
-  {
-    haveLang = 1;
-    memcpy(lc.search_language,locale,next-locale);
-    locale += next-locale+1;
-  }
+    int i;
 
-  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);
+    for(i=LC_MIN+1; i<LC_MAX; i++) {
+        if(strcmp(locinfo->lc_category[i].locale,
+                    locinfo->lc_category[i+1].locale))
+            break;
     }
-    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(i==LC_MAX)
+        return locinfo->lc_category[LC_COLLATE].locale;
 
-  if (lcid == 0)
-  {
-    UNLOCK_LOCALE;
-    return NULL;
-  }
+    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);
 
-  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;
+    return current_lc_all;
 }
 
-/*
- * @unimplemented
+
+/*********************************************************************
+ *             wsetlocale (MSVCRT.@)
  */
-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"}
-};
-
+/*********************************************************************
+ *             _Getdays (MSVCRT.@)
+ */
+char* CDECL _Getdays(void)
+{
+    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';
 
-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;
-};
+    return out;
+}
 
-/*
- * @implemented
+/*********************************************************************
+ *             _Getmonths (MSVCRT.@)
  */
-struct lconv *localeconv(void)
+char* CDECL _Getmonths(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.am-cur->str.names.short_mon[0];
+    out = malloc(size+1);
+    if(!out)
+        return NULL;
+
+    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;
+
+        out[size++] = ':';
+        len = strlen(cur->str.names.mon[i]);
+        memcpy(&out[size], cur->str.names.mon[i], len);
+        size += len;
+    }
+    out[size] = '\0';
+
+    return out;
 }
 
 /*********************************************************************
- *             _setmbcp (MSVCRT.@)
- * @implemented
+ *             _Gettnames (MSVCRT.@)
  */
-int CDECL _setmbcp(int cp)
-{
-  int newcp;
-  CPINFO cpi;
-  BYTE *bytes;
-  WORD chartypes[256];
-  WORD *curr_type;
-  char bufA[256];
-  WCHAR bufW[256];
-  int charcount;
-  int ret;
-  int i;
-
-  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;
-  }
-
-  if (!GetCPInfo(newcp, &cpi))
-  {
-    ERR("Codepage %d not found\n", newcp);
-    _set_errno(EINVAL);
-    return -1;
-  }
+void* CDECL _Gettnames(void)
+{
+    MSVCRT___lc_time_data *ret, *cur = get_locinfo()->lc_time_curr;
+    int i, size = sizeof(MSVCRT___lc_time_data);
 
-  /* setup the _mbctype */
-  memset(_mbctype, 0, sizeof(_mbctype));
+    TRACE("\n");
 
-  bytes = cpi.LeadByte;
-  while (bytes[0] || bytes[1])
-  {
-    for (i = bytes[0]; i <= bytes[1]; i++)
-      _mbctype[i + 1] |= _M1;
-    bytes += 2;
-  }
+    for(i=0; i<sizeof(cur->str.str)/sizeof(cur->str.str[0]); i++)
+        size += strlen(cur->str.str[i])+1;
 
-  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;
+    ret = malloc(size);
+    if(!ret)
+        return NULL;
+    memcpy(ret, cur, size);
 
-    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++;
+    size = 0;
+    for(i=0; i<sizeof(cur->str.str)/sizeof(cur->str.str[0]); i++) {
+        ret->str.str[i] = &ret->data[size];
+        size += strlen(&ret->data[size])+1;
     }
-  }
-  else
-    g_mbcp_is_multibyte = 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;
-
-  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);
+    return ret;
+}
 
-  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++;
-    }
+/*********************************************************************
+ *             __crtLCMapStringA (MSVCRT.@)
+ */
+int CDECL __crtLCMapStringA(
+  LCID lcid, DWORD mapflags, const char* src, int srclen, char* dst,
+  int dstlen, unsigned int codepage, int xflag
+) {
+  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
+   */
+  return LCMapStringA(lcid,mapflags,src,srclen,dst,dstlen);
+}
 
-  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;
-  }
+/*********************************************************************
+ *              __crtLCMapStringW (MSVCRT.@)
+ */
+int CDECL __crtLCMapStringW(LCID lcid, DWORD mapflags, const wchar_t *src,
+        int srclen, wchar_t *dst, int dstlen, unsigned int codepage, int xflag)
+{
+    FIXME("(lcid %x, flags %x, %s(%d), %p(%d), %x, %d), partial stub!\n",
+            lcid, mapflags, debugstr_w(src), srclen, dst, dstlen, codepage, xflag);
 
-  MSVCRT___lc_collate_cp = MSVCRT___lc_codepage = newcp;
-  TRACE("(%d) -> %d\n", cp, MSVCRT___lc_codepage);
-  return 0;
+    return LCMapStringW(lcid, mapflags, src, srclen, dst, dstlen);
 }
 
-
 /*********************************************************************
- *      ___lc_handle_func (MSVCRT.@)
+ *             __crtCompareStringA (MSVCRT.@)
  */
-HANDLE * CDECL ___lc_handle_func(void)
+int CDECL __crtCompareStringA( LCID lcid, DWORD flags, const char *src1, int len1,
+                               const char *src2, int len2 )
 {
-    return MSVCRT___lc_handle;
+    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 );
 }
 
-
 /*********************************************************************
- *      ___lc_codepage_func (MSVCRT.@)
+ *             __crtCompareStringW (MSVCRT.@)
  */
-int CDECL ___lc_codepage_func(void)
+int CDECL __crtCompareStringW( LCID lcid, DWORD flags, const wchar_t *src1, int len1,
+                               const wchar_t *src2, int len2 )
 {
-    return MSVCRT___lc_codepage;
+    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 );
 }
 
-
 /*********************************************************************
- *    _Gettnames (MSVCRT.@)
+ *             __crtGetLocaleInfoW (MSVCRT.@)
  */
-void *_Gettnames(void)
+int CDECL __crtGetLocaleInfoW( LCID lcid, LCTYPE type, wchar_t *buffer, int len )
 {
-  FIXME("(void), stub!\n");
-  return NULL;
+    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 );
 }
 
 /*********************************************************************
- *    __lconv_init (MSVCRT.@)
+ *              btowc(MSVCRT.@)
  */
-void __lconv_init(void)
+wint_t CDECL MSVCRT_btowc(int c)
 {
-  char Char = (char) UCHAR_MAX;
+    unsigned char letter = c;
+    wchar_t ret;
 
-  TRACE("__lconv_init()\n");
+    if(!MultiByteToWideChar(get_locinfo()->lc_handle[LC_CTYPE],
+                0, (LPCSTR)&letter, 1, &ret, 1))
+        return 0;
 
-  _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;
+    return ret;
 }
 
+/*********************************************************************
+ *              __crtGetStringTypeW(MSVCRT.@)
+ *
+ * This function was accepting different number of arguments in older
+ * versions of msvcrt.
+ */
+BOOL CDECL __crtGetStringTypeW(DWORD unk, DWORD type,
+        wchar_t *buffer, int len, WORD *out)
+{
+    FIXME("(unk %x, type %x, wstr %p(%d), %p) partial stub\n",
+            unk, type, buffer, len, out);
+
+    return GetStringTypeW(type, buffer, len, out);
+}
 
 /*********************************************************************
- *    _Strftime (MSVCRT.@)
+ *             localeconv (MSVCRT.@)
  */
-const char* _Strftime(char *out, unsigned int len, const char *fmt,
-                                     const void *tm, void *foo)
+struct lconv * CDECL localeconv(void)
 {
-  /* FIXME: */
-  FIXME("(%p %d %s %p %p) stub\n", out, len, fmt, tm, foo);
-  return "";
+    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;
+}
 
 /*********************************************************************
- *    _Getdays (MSVCRT.@)
+ *      ___lc_handle_func (MSVCRT.@)
  */
-const char* _Getdays(void)
+LCID* CDECL ___lc_handle_func(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;
+    return MSVCRT___lc_handle;
 }
 
 /*********************************************************************
- *    _Getmonths (MSVCRT.@)
+ *      ___lc_codepage_func (MSVCRT.@)
  */
-const char* _Getmonths(void)
+unsigned int CDECL ___lc_codepage_func(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;
+    return MSVCRT___lc_codepage;
 }
 
 /*********************************************************************
- *    __crtLCMapStringA (MSVCRT.@)
+ *      ___lc_collate_cp_func (MSVCRT.@)
  */
-int __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",
-        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
-   */
-  return LCMapStringA(lcid,mapflags,src,srclen,dst,dstlen);
+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(category<LC_MIN || category>LC_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; i<sizeof(time_data)/sizeof(time_data[0]); i++) {
+        if(time_data[i]==LOCALE_SSHORTDATE && !lcid[LC_TIME]) {
+            size += sizeof(cloc_short_date) + sizeof(cloc_short_dateW);
+        }else if(time_data[i]==LOCALE_SSHORTDATE && !lcid[LC_TIME]) {
+            size += sizeof(cloc_long_date) + sizeof(cloc_long_dateW);
+        }else {
+            ret = GetLocaleInfoA(lcid_tmp, time_data[i]
+                    |LOCALE_NOUSEROVERRIDE, NULL, 0);
+            if(!ret) {
+                MSVCRT__free_locale(loc);
+                return NULL;
+            }
+            size += ret;
+
+            ret = GetLocaleInfoW(lcid_tmp, time_data[i]
+                    |LOCALE_NOUSEROVERRIDE, NULL, 0);
+            if(!ret) {
+                MSVCRT__free_locale(loc);
+                return NULL;
+            }
+            size += ret*sizeof(wchar_t);
+        }
+    }
+
+    loc->locinfo->lc_time_curr = malloc(size);
+    if(!loc->locinfo->lc_time_curr) {
+        MSVCRT__free_locale(loc);
+        return NULL;
+    }
+
+    ret = 0;
+    for(i=0; i<sizeof(time_data)/sizeof(time_data[0]); i++) {
+        loc->locinfo->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; i<sizeof(time_data)/sizeof(time_data[0]); i++) {
+        loc->locinfo->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;
 }
 
 /*********************************************************************
- *    __crtLCMapStringW (MSVCRT.@)
+ *             setlocale (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);
+char* CDECL setlocale(int category, const char* locale)
+{
+    MSVCRT__locale_t loc;
+    MSVCRT_pthreadlocinfo locinfo = get_locinfo();
+
+    if(category<LC_MIN || category>LC_MAX)
+        return NULL;
+
+    if(!locale) {
+        if(category == LC_ALL)
+            return construct_lc_all(locinfo);
+
+        return locinfo->lc_category[category].locale;
+    }
 
-  return LCMapStringW(lcid,mapflags,src,srclen,dst,dstlen);
+    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;
 }
 
-int CDECL _getmbcp(void)
+/* _configthreadlocale - not exported in native msvcrt */
+int CDECL _configthreadlocale(int type)
 {
-    return MSVCRT___lc_codepage;
+    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;
 }
 
 /*********************************************************************
- *             ___unguarded_readlc_active_add_func (MSVCRT.@)
+ *         _getmbcp (MSVCRT.@)
  */
-unsigned int * CDECL ___unguarded_readlc_active_add_func(void)
+int CDECL _getmbcp(void)
 {
-  return &__unguarded_readlc_active;
+  return get_mbcinfo()->mbcodepage;
 }
 
+extern unsigned int __setlc_active;
 /*********************************************************************
- *             ___setlc_active_func (MSVCRT.@)
+ *         ___setlc_active_func (MSVCRT.@)
  */
 unsigned int CDECL ___setlc_active_func(void)
 {
   return __setlc_active;
 }
 
+extern unsigned int __unguarded_readlc_active;
 /*********************************************************************
- *              __crtGetStringTypeW(MSVCRT.@)
- *
- * This function was accepting different number of arguments in older
- * versions of msvcrt.
+ *         ___unguarded_readlc_active_add_func (MSVCRT.@)
  */
-BOOL CDECL __crtGetStringTypeW(DWORD unk, DWORD type,
-        wchar_t *buffer, int len, WORD *out)
+unsigned int * CDECL ___unguarded_readlc_active_add_func(void)
 {
-    FIXME("(unk %x, type %x, wstr %p(%d), %p) partial stub\n",
-            unk, type, buffer, len, out);
+  return &__unguarded_readlc_active;
+}
 
-    return GetStringTypeW(type, buffer, len, out);
+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 (file)
index 0000000..be23cc5
--- /dev/null
@@ -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 <precomp.h>
+
+#include <mbctype.h>
+
+/* 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);
+}
+
index 407a820..4780e27 100644 (file)
 #include <precomp.h>
 #include <mbstring.h>
 
-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;
+}
+
index 37d37f5..0504fbf 100644 (file)
@@ -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;
index 7fe14dd..4d01c7c 100644 (file)
@@ -57,6 +57,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(msvcrt);
 #include <internal/atexit.h>
 #include <internal/console.h>
 #include <internal/ieee.h>
+#include <internal/locale.h>
 #include <internal/math.h>
 #include <internal/mbstring.h>
 #include <internal/mtdll.h>
index a28fd20..20dbb3c 100644 (file)
@@ -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
  */
index 0167458..846179a 100644 (file)
@@ -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;
-
-    /* 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.
+    BOOL error;
+    INT size;
 
-       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;
 }
 
-size_t CDECL wcstombs(char *mbstr, const wchar_t *wcstr, size_t count)
+/*********************************************************************
+ * wcsrtombs_l (INTERNAL)
+ */
+static size_t CDECL wcsrtombs_l(char *mbstr, const wchar_t **wcstr, size_t count, _locale_t locale)
 {
-    BOOL bUsedDefaultChar;
-    char* p = mbstr;
-    int nResult;
-
-    /* Does the caller query for output buffer size? */
-    if(!mbstr)
-    {
-        int nLength;
+    MSVCRT_pthreadlocinfo locinfo;
+    size_t tmp = 0;
+    BOOL used_default;
 
-        /* If we currently use the "C" locale, the length of the input string is returned (verified by tests under WinXP SP2) */
-        if(MSVCRT_current_lc_all[0] == 'C' && !MSVCRT_current_lc_all[1])
-            return wcslen(wcstr);
-
-        /* Otherwise check the length each character needs and build a final return value out of this */
-        count = wcslen(wcstr);
-        nLength = 0;
-
-        while((int)(--count) >= 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;
+    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;
     }
 
-    /* Convert the string then */
-    bUsedDefaultChar = FALSE;
+    while(**wcstr) {
+        char buf[3];
+        size_t i, size;
 
-    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;
+        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;
 
-        if((int)count < 0)
-            break;
+        for(i=0; i<size; i++)
+            mbstr[tmp++] = buf[i];
+        (*wcstr)++;
+    }
 
-        /* It can, so copy it and move the pointer forward */
-        memcpy(p, chMultiByte, uLength);
-        p += uLength;
+    if(tmp < count) {
+        mbstr[tmp] = '\0';
+        *wcstr = NULL;
     }
+    return tmp;
+}
 
-    if(bUsedDefaultChar)
-        return -1;
+/*********************************************************************
+ *             _wcstombs_l (MSVCRT.@)
+ */
+size_t CDECL _wcstombs_l(char *mbstr, const wchar_t *wcstr, size_t count, _locale_t locale)
+{
+    return wcsrtombs_l(mbstr, &wcstr, count, locale);
+}
 
-    /* Return the length in bytes of the copied characters (without the terminating null character) */
-    return p - mbstr;
+/*********************************************************************
+ *             wcstombs (MSVCRT.@)
+ */
+size_t CDECL wcstombs(char *mbstr, const wchar_t *wcstr, size_t count)
+{
+    return wcsrtombs_l(mbstr, &wcstr, count, NULL);
 }
 #endif
 
index 797e4c3..996b0fc 100644 (file)
  * PROGRAMER:   
  */
 #include <precomp.h>
-#include <tchar.h>
 
-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 && ret<max-*pos)
+        ret = GetDateFormatA(time_data->lcid, 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 && ret<max-*pos)
+        ret = GetTimeFormatA(time_data->lcid, 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(src<l || src>h) {
+        *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 && ret<max; format++) {
+        if(*format != '%') {
+            str[ret++] = *format;
+            continue;
+        }
+
+        format++;
+        if(*format == '#') {
+            alternate = TRUE;
+            format++;
+        }else {
+            alternate = FALSE;
+        }
+
+        if(!mstm)
+            goto einval_error;
+
+        switch(*format) {
+        case 'c':
+            if(!strftime_date(str, &ret, max, alternate, mstm, time_data))
+                return 0;
+            if(ret < max)
+                str[ret++] = ' ';
+            if(!strftime_time(str, &ret, max, mstm, time_data))
+                return 0;
+            break;
+        case 'x':
+            if(!strftime_date(str, &ret, max, alternate, mstm, time_data))
+                return 0;
+            break;
+        case 'X':
+            if(!strftime_time(str, &ret, max, mstm, time_data))
+                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.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;
+}