[ros-diffs] [fireball] 33724: Daniel Zimmermann <netzimme at aim.com> - Sync setlocale from Wine. See issue #3254 for more details.

fireball at svn.reactos.org fireball at svn.reactos.org
Tue May 27 10:11:07 CEST 2008


Author: fireball
Date: Tue May 27 03:11:06 2008
New Revision: 33724

URL: http://svn.reactos.org/svn/reactos?rev=33724&view=rev
Log:
Daniel Zimmermann <netzimme at aim.com>
- Sync setlocale from Wine.
See issue #3254 for more details.

Modified:
    trunk/reactos/dll/win32/msvcrt/msvcrt.def
    trunk/reactos/dll/win32/msvcrt/stubs.c
    trunk/reactos/lib/sdk/crt/locale/locale.c

Modified: trunk/reactos/dll/win32/msvcrt/msvcrt.def
URL: http://svn.reactos.org/svn/reactos/trunk/reactos/dll/win32/msvcrt/msvcrt.def?rev=33724&r1=33723&r2=33724&view=diff
==============================================================================
--- trunk/reactos/dll/win32/msvcrt/msvcrt.def [iso-8859-1] (original)
+++ trunk/reactos/dll/win32/msvcrt/msvcrt.def [iso-8859-1] Tue May 27 03:11:06 2008
@@ -554,7 +554,7 @@
   _write @548
   _wrmdir @549
   _wsearchenv @550
-  _wsetlocale=__wine_stub_msvcrt_dll_551 @551
+  _wsetlocale @551
   _wsopen @552
   _wspawnl=__wine_stub_msvcrt_dll_553 @553
   _wspawnle=__wine_stub_msvcrt_dll_554 @554

Modified: trunk/reactos/dll/win32/msvcrt/stubs.c
URL: http://svn.reactos.org/svn/reactos/trunk/reactos/dll/win32/msvcrt/stubs.c?rev=33724&r1=33723&r2=33724&view=diff
==============================================================================
--- trunk/reactos/dll/win32/msvcrt/stubs.c [iso-8859-1] (original)
+++ trunk/reactos/dll/win32/msvcrt/stubs.c [iso-8859-1] Tue May 27 03:11:06 2008
@@ -84,7 +84,6 @@
 void __wine_stub_msvcrt_dll_455(void) { __wine_spec_unimplemented_stub(__wine_spec_file_name, "_stat64"); }
 void __wine_stub_msvcrt_dll_542(void) { __wine_spec_unimplemented_stub(__wine_spec_file_name, "_wperror"); }
 void __wine_stub_msvcrt_dll_543(void) { __wine_spec_unimplemented_stub(__wine_spec_file_name, "_wpgmptr"); }
-void __wine_stub_msvcrt_dll_551(void) { __wine_spec_unimplemented_stub(__wine_spec_file_name, "_wsetlocale"); }
 void __wine_stub_msvcrt_dll_553(void) { __wine_spec_unimplemented_stub(__wine_spec_file_name, "_wspawnl"); }
 void __wine_stub_msvcrt_dll_554(void) { __wine_spec_unimplemented_stub(__wine_spec_file_name, "_wspawnle"); }
 void __wine_stub_msvcrt_dll_555(void) { __wine_spec_unimplemented_stub(__wine_spec_file_name, "_wspawnlp"); }

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



More information about the Ros-diffs mailing list