This is the mail archive of the cygwin-developers mailing list for the Cygwin project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: representing charsets


On Apr  2 20:32, Andy Koppe wrote:
> Corinna Vinschen:
> >> I'd be tempted to replace the 'char *codeset' fields in the various
> >> locale info structs with integer IDs as well, but the entanglement
> >> with the _*_locale_buf buffers makes that rather complicated.
> >
> > If we use the enums as you proposed, and if we also add a table to
> > newlib which maps the enums to charset names, then we could do that.
> 
> Unfortunately I can't see where to store the enums for the locale
> category codesets, since __part_load_locale in newlib and the new
> rebase_locale_buf in nlsfuncs.cc assume that the various lc_*_T
> structs consist entirely of pointers.

And that should stay that way.  This is consistent with the way the
data is loaded from files.  There doesn't speak anything against
storing a pointer to the enum in the structure, though.

> > The next step then would be an offical API to allow the locale(1) tool
> > to get the names of all supported codesets, rather than having to have
> > a codeset list in the application itself.
> 
> Is there a (de facto) standard API for that?

Well, not really. 

On BSD, locale(1) reads the filenames of the files stored in
/usr/share/locale.  The files are always in the style
lang_TERRITORY.charset.

In GLibc, it reads the files from /usr/share/i18n/charmaps and fetches
the charmap names from the file content.

What we need is an interface which allows to fetch the names of all
supported charsets from newlib.  For instance, some nl_langinfo(foo)
which returns a string of sorted charset names, separated by \n ;-)

Actually this interface is not important for now.  We can add it
later at one point.

> ps: Could you attach the Cygwin counterpart of your big newlib locale patch?

Yes.  See below.


Corinna


Index: utils/locale.cc
===================================================================
RCS file: /cvs/src/src/winsup/utils/locale.cc,v
retrieving revision 1.8
diff -u -p -r1.8 locale.cc
--- utils/locale.cc	27 Mar 2010 21:08:02 -0000	1.8
+++ utils/locale.cc	2 Apr 2010 20:32:29 -0000
@@ -511,19 +511,6 @@ print_lc_strings (int key, const char *n
 }
 
 void
-print_lc_xxx_charset (int key, int lc_cat, const char *name)
-{
-  char lc_ctype_locale[32];
-  char lc_xxx_locale[32];
-
-  strcpy (lc_ctype_locale, setlocale (LC_CTYPE, NULL));
-  strcpy (lc_xxx_locale, setlocale (lc_cat, NULL));
-  setlocale (LC_CTYPE, lc_xxx_locale);
-  print_lc_svalue (key, name, nl_langinfo (CODESET));
-  setlocale (LC_CTYPE, lc_ctype_locale);
-}
-
-void
 print_lc_grouping (int key, const char *name, const char *grouping)
 {
   if (key)
@@ -536,14 +523,12 @@ print_lc_grouping (int key, const char *
 enum type_t
 {
   is_string_fake,
-  is_string_lconv,
-  is_int_lconv,
-  is_grouping_lconv,
-  is_string_linf,
-  is_mstrings_linf,
-  is_sepstrings_linf,
-  is_mb_cur_max,
-  is_codeset,
+  is_grouping,
+  is_string,
+  is_mstrings,
+  is_sepstrings,
+  is_int,
+  is_wchar,
   is_end
 };
 
@@ -555,10 +540,6 @@ struct lc_names_t
   size_t      toval;
 };
 
-#define _O(M)		__builtin_offsetof (struct lconv, M)
-#define _MS(l,lc)	(*(const char **)(((const char *)(l))+(lc)->fromval))
-#define _MI(l,lc)	((int)*(((const char *)(l))+(lc)->fromval))
-
 const char *fake_string[] = {
  "upper;lower;alpha;digit;xdigit;space;print;graph;blank;cntrl;punct;alnum",
  "upper\";\"lower\";\"alpha\";\"digit\";\"xdigit\";\"space\";\"print\";\"graph\";\"blank\";\"cntrl\";\"punct\";\"alnum",
@@ -568,92 +549,114 @@ const char *fake_string[] = {
 
 lc_names_t lc_ctype_names[] =
 {
-  { "ctype-class-names",is_string_fake,	   0,			0 },
-  { "ctype-map-names",	is_string_fake,	   2,			0 },
-  { "charmap",		is_string_linf,	   CODESET,		0 },
-  { "ctype-mb-cur-max", is_mb_cur_max,	   0,			0 },
-  { NULL, 		is_end,		   0,			0 }
+  { "ctype-class-names", 	 is_string_fake, 0,			 0 },
+  { "ctype-map-names",   	 is_string_fake, 2,			 0 },
+  { "ctype-outdigit0_mb",	 is_string,	_NL_CTYPE_OUTDIGITS0_MB, 0 },
+  { "ctype-outdigit1_mb",	 is_string,	_NL_CTYPE_OUTDIGITS1_MB, 0 },
+  { "ctype-outdigit2_mb",	 is_string,	_NL_CTYPE_OUTDIGITS2_MB, 0 },
+  { "ctype-outdigit3_mb",	 is_string,	_NL_CTYPE_OUTDIGITS3_MB, 0 },
+  { "ctype-outdigit4_mb",	 is_string,	_NL_CTYPE_OUTDIGITS4_MB, 0 },
+  { "ctype-outdigit5_mb",	 is_string,	_NL_CTYPE_OUTDIGITS5_MB, 0 },
+  { "ctype-outdigit6_mb",	 is_string,	_NL_CTYPE_OUTDIGITS6_MB, 0 },
+  { "ctype-outdigit7_mb",	 is_string,	_NL_CTYPE_OUTDIGITS7_MB, 0 },
+  { "ctype-outdigit8_mb",	 is_string,	_NL_CTYPE_OUTDIGITS8_MB, 0 },
+  { "ctype-outdigit9_mb",	 is_string,	_NL_CTYPE_OUTDIGITS9_MB, 0 },
+  { "ctype-outdigit0_wc",	 is_wchar, 	_NL_CTYPE_OUTDIGITS0_WC, 0 },
+  { "ctype-outdigit1_wc",	 is_wchar, 	_NL_CTYPE_OUTDIGITS1_WC, 0 },
+  { "ctype-outdigit2_wc",	 is_wchar, 	_NL_CTYPE_OUTDIGITS2_WC, 0 },
+  { "ctype-outdigit3_wc",	 is_wchar, 	_NL_CTYPE_OUTDIGITS3_WC, 0 },
+  { "ctype-outdigit4_wc",	 is_wchar, 	_NL_CTYPE_OUTDIGITS4_WC, 0 },
+  { "ctype-outdigit5_wc",	 is_wchar, 	_NL_CTYPE_OUTDIGITS5_WC, 0 },
+  { "ctype-outdigit6_wc",	 is_wchar, 	_NL_CTYPE_OUTDIGITS6_WC, 0 },
+  { "ctype-outdigit7_wc",	 is_wchar, 	_NL_CTYPE_OUTDIGITS7_WC, 0 },
+  { "ctype-outdigit8_wc",	 is_wchar, 	_NL_CTYPE_OUTDIGITS8_WC, 0 },
+  { "ctype-outdigit9_wc",	 is_wchar, 	_NL_CTYPE_OUTDIGITS9_WC, 0 },
+  { "charmap",			 is_string,	CODESET,		 0 },
+  { "ctype-mb-cur-max",		 is_int,	_NL_CTYPE_MB_CUR_MAX,	 0 },
+  { NULL, 			 is_end,	0,		 	 0 }
 };
 
 lc_names_t lc_numeric_names[] =
 {
-  { "decimal_point",	is_string_lconv,   _O(decimal_point),	0 },
-  { "thousands_sep",	is_string_lconv,   _O(thousands_sep), 	0 },
-  { "grouping",		is_grouping_lconv, _O(grouping),	0 },
-  { "numeric-codeset",	is_codeset,	   LC_NUMERIC,		0 },
-  { NULL, 		is_end,		   0,			0 }
+  { "decimal_point",		 is_string,	RADIXCHAR,		 0 },
+  { "thousands_sep",		 is_string,	THOUSEP, 		 0 },
+  { "grouping",			 is_grouping,	_NL_NUMERIC_GROUPING,	 0 },
+  { "numeric-decimal-point-wc",	 is_wchar,	_NL_NUMERIC_DECIMAL_POINT_WC, 0 },
+  { "numeric-thousands-sep-wc",	 is_wchar,	_NL_NUMERIC_THOUSANDS_SEP_WC, 0 },
+  { "numeric-codeset",		 is_string,	_NL_NUMERIC_CODESET,	 0 },
+  { NULL, 			 is_end,	0,			 0 }
 };
 
 lc_names_t lc_time_names[] =
 {
-  { "abday",		is_mstrings_linf,  ABDAY_1,		ABDAY_7  },
-  { "day",		is_mstrings_linf,  DAY_1,		DAY_7    },
-  { "abmon",		is_mstrings_linf,  ABMON_1,		ABMON_12 },
-  { "mon",		is_mstrings_linf,  MON_1,		MON_12   },
-  { "am_pm",		is_mstrings_linf,  AM_STR,		PM_STR   },
-  { "d_t_fmt",		is_string_linf,    D_T_FMT,		0        },
-  { "d_fmt",		is_string_linf,    D_FMT,		0        },
-  { "t_fmt",		is_string_linf,    T_FMT,		0        },
-  { "t_fmt_ampm",	is_string_linf,    T_FMT_AMPM,		0	 },
-  { "era",		is_sepstrings_linf,ERA,			0	 },
-  { "era_d_fmt",	is_string_linf,    ERA_D_FMT,		0	 },
-  { "alt_digits",	is_sepstrings_linf,ALT_DIGITS,		0	 },
-  { "era_d_t_fmt",	is_string_linf,    ERA_D_T_FMT,		0	 },
-  { "era_t_fmt",	is_string_linf,    ERA_T_FMT,		0	 },
-  { "date_fmt",		is_string_linf,    _DATE_FMT,		0	 },
-  { "time-codeset",	is_codeset,	   LC_TIME,		0	 },
-  { NULL, 		is_end,		   0,			0	 }
+  { "abday",			 is_mstrings,	ABDAY_1,	 ABDAY_7  },
+  { "day",			 is_mstrings,	DAY_1,		 DAY_7    },
+  { "abmon",			 is_mstrings,	ABMON_1,	 ABMON_12 },
+  { "mon",			 is_mstrings,	MON_1,		 MON_12   },
+  { "am_pm",			 is_mstrings,	AM_STR,		 PM_STR   },
+  { "d_t_fmt",			 is_string,	D_T_FMT,		0 },
+  { "d_fmt",			 is_string,	D_FMT,			0 },
+  { "t_fmt",			 is_string,	T_FMT,			0 },
+  { "t_fmt_ampm",		 is_string,	T_FMT_AMPM,		0 },
+  { "era",			 is_sepstrings,	ERA,			0 },
+  { "era_d_fmt",		 is_string,	ERA_D_FMT,		0 },
+  { "alt_digits",		 is_sepstrings,ALT_DIGITS,		0 },
+  { "era_d_t_fmt",		 is_string,	ERA_D_T_FMT,		0 },
+  { "era_t_fmt",		 is_string,	ERA_T_FMT,		0 },
+  { "date_fmt",			 is_string,	_DATE_FMT,		0 },
+  { "time-codeset",		 is_string,	_NL_TIME_CODESET,	0 },
+  { NULL, 			 is_end,	0,			0 }
 };
 
 lc_names_t lc_collate_names[] =
 {
-  { "collate-codeset",	is_codeset,	   LC_COLLATE,		0	 },
-  { NULL, 		is_end,		   0,			0	 }
+  { "collate-codeset",		 is_string,	_NL_COLLATE_CODESET,	0 },
+  { NULL, 			 is_end,	0,			0 }
 };
 
 lc_names_t lc_monetary_names[] =
 {
-  { "int_curr_symbol",	is_string_lconv,   _O(int_curr_symbol),		0 },
-  { "currency_symbol",	is_string_lconv,   _O(currency_symbol),		0 },
-  { "mon_decimal_point",is_string_lconv,   _O(mon_decimal_point), 	0 },
-  { "mon_thousands_sep",is_string_lconv,   _O(mon_thousands_sep),	0 },
-  { "mon_grouping",	is_grouping_lconv, _O(mon_grouping),		0 },
-  { "positive_sign",	is_string_lconv,   _O(positive_sign),		0 },
-  { "negative_sign",	is_string_lconv,   _O(negative_sign),		0 },
-  { "int_frac_digits",	is_int_lconv,      _O(int_frac_digits),		0 },
-  { "frac_digits",	is_int_lconv,	   _O(frac_digits),		0 },
-  { "p_cs_precedes",	is_int_lconv,	   _O(p_cs_precedes),		0 },
-  { "p_sep_by_space",	is_int_lconv,	   _O(p_sep_by_space),		0 },
-  { "n_cs_precedes",	is_int_lconv,	   _O(n_cs_precedes),		0 },
-  { "n_sep_by_space",	is_int_lconv,	   _O(n_sep_by_space),		0 },
-  { "p_sign_posn",	is_int_lconv,	   _O(p_sign_posn),		0 },
-  { "n_sign_posn",	is_int_lconv,	   _O(n_sign_posn),		0 },
-  { "int_p_cs_precedes",is_int_lconv,	   _O(int_p_cs_precedes),	0 },
-  { "int_p_sep_by_space",is_int_lconv,	   _O(int_p_sep_by_space),	0 },
-  { "int_n_cs_precedes",is_int_lconv,	   _O(int_n_cs_precedes),	0 },
-  { "int_n_sep_by_space",is_int_lconv,	   _O(int_n_sep_by_space),	0 },
-  { "int_p_sign_posn",	is_int_lconv,	   _O(int_p_sign_posn),		0 },
-  { "int_n_sign_posn",	is_int_lconv,	   _O(int_n_sign_posn),		0 },
-  { "monetary-codeset",	is_codeset,	   LC_MONETARY,			0 },
-  { NULL, 		is_end,		   0,				0 }
+  { "int_curr_symbol",		 is_string,	_NL_MONETARY_INT_CURR_SYMBOL, 0 },
+  { "currency_symbol",		 is_string,	_NL_MONETARY_CURRENCY_SYMBOL, 0 },
+  { "mon_decimal_point",	 is_string,	_NL_MONETARY_MON_DECIMAL_POINT, 0 },
+  { "mon_thousands_sep",	 is_string,	_NL_MONETARY_MON_THOUSANDS_SEP, 0 },
+  { "mon_grouping",		 is_grouping,	_NL_MONETARY_MON_GROUPING, 0 },
+  { "positive_sign",		 is_string,	_NL_MONETARY_POSITIVE_SIGN, 0 },
+  { "negative_sign",		 is_string,	_NL_MONETARY_NEGATIVE_SIGN, 0 },
+  { "int_frac_digits",		 is_int,	_NL_MONETARY_INT_FRAC_DIGITS, 0 },
+  { "frac_digits",		 is_int,	_NL_MONETARY_FRAC_DIGITS,   0 },
+  { "p_cs_precedes",		 is_int,	_NL_MONETARY_P_CS_PRECEDES, 0 },
+  { "p_sep_by_space",		 is_int,	_NL_MONETARY_P_SEP_BY_SPACE, 0 },
+  { "n_cs_precedes",		 is_int,	_NL_MONETARY_N_CS_PRECEDES, 0 },
+  { "n_sep_by_space",		 is_int,	_NL_MONETARY_N_SEP_BY_SPACE, 0 },
+  { "p_sign_posn",		 is_int,	_NL_MONETARY_P_SIGN_POSN,   0 },
+  { "n_sign_posn",		 is_int,	_NL_MONETARY_N_SIGN_POSN,   0 },
+  { "int_p_cs_precedes",	 is_int,	_NL_MONETARY_INT_P_CS_PRECEDES, 0 },
+  { "int_p_sep_by_space",	 is_int,	_NL_MONETARY_INT_P_SEP_BY_SPACE,0 },
+  { "int_n_cs_precedes",	 is_int,	_NL_MONETARY_INT_N_CS_PRECEDES, 0 },
+  { "int_n_sep_by_space",	 is_int,	_NL_MONETARY_INT_N_SEP_BY_SPACE,0 },
+  { "int_p_sign_posn",		 is_int,	_NL_MONETARY_INT_P_SIGN_POSN, 0 },
+  { "int_n_sign_posn",		 is_int,	_NL_MONETARY_INT_N_SIGN_POSN, 0 },
+  { "monetary-decimal-point-wc", is_wchar,	_NL_MONETARY_WMON_DECIMAL_POINT, 0 },
+  { "monetary-thousands-sep-wc", is_wchar,	_NL_MONETARY_WMON_THOUSANDS_SEP, 0 },
+  { "monetary-codeset",		 is_string,	_NL_MONETARY_CODESET,	   0 },
+  { NULL, 			 is_end,	0,			   0 }
 };
 
 lc_names_t lc_messages_names[] =
 {
-  { "yesexpr",		is_string_linf,	   YESEXPR,			0 },
-  { "noexpr",		is_string_linf,	   NOEXPR,			0 },
-  { "yesstr",		is_string_linf,	   YESSTR,			0 },
-  { "nostr",		is_string_linf,	   NOSTR,			0 },
-  { "messages-codeset",	is_codeset,	   LC_MESSAGES,			0 },
-  { NULL, 		is_end,		   0,				0 }
+  { "yesexpr",			 is_string,	YESEXPR,		0 },
+  { "noexpr",			 is_string,	NOEXPR,			0 },
+  { "yesstr",			 is_string,	YESSTR,			0 },
+  { "nostr",			 is_string,	NOSTR,			0 },
+  { "messages-codeset",		 is_string,	_NL_MESSAGES_CODESET,	0 },
+  { NULL, 			 is_end,	0,			0 }
 };
 
 void
 print_lc (int cat, int key, const char *category, const char *name,
 	  lc_names_t *lc_name)
 {
-  struct lconv *l = localeconv ();
-
   if (cat)
     printf ("%s\n", category);
   for (lc_names_t *lc = lc_name; lc->type != is_end; ++lc)
@@ -663,29 +666,24 @@ print_lc (int cat, int key, const char *
 	case is_string_fake:
 	  print_lc_svalue (key, lc->name, fake_string[lc->fromval + key]);
 	  break;
-	case is_string_lconv:
-	  print_lc_svalue (key, lc->name, _MS (l, lc));
-	  break;
-	case is_int_lconv:
-	  print_lc_ivalue (key, lc->name, _MI (l, lc));
-	  break;
-	case is_grouping_lconv:
-	  print_lc_grouping (key, lc->name, _MS (l, lc));
+	case is_grouping:
+	  print_lc_grouping (key, lc->name, nl_langinfo (lc->fromval));
 	  break;
-	case is_string_linf:
+	case is_string:
 	  print_lc_svalue (key, lc->name, nl_langinfo (lc->fromval));
 	  break;
-	case is_sepstrings_linf:
+	case is_sepstrings:
 	  print_lc_sepstrings (key, lc->name, nl_langinfo (lc->fromval));
 	  break;
-	case is_mstrings_linf:
+	case is_mstrings:
 	  print_lc_strings (key, lc->name, lc->fromval, lc->toval);
 	  break;
-	case is_mb_cur_max:
-	  print_lc_ivalue (key, lc->name, MB_CUR_MAX);
+	case is_int:
+	  print_lc_ivalue (key, lc->name, (int) *nl_langinfo (lc->fromval));
 	  break;
-	case is_codeset:
-	  print_lc_xxx_charset (key, lc->fromval, lc->name);
+	case is_wchar:
+	  print_lc_ivalue (key, lc->name,
+			   *(wchar_t *) nl_langinfo (lc->fromval));
 	  break;
 	default:
 	  break;
Index: cygwin/cygwin.din
===================================================================
RCS file: /cvs/src/src/winsup/cygwin/cygwin.din,v
retrieving revision 1.222
diff -u -p -r1.222 cygwin.din
--- cygwin/cygwin.din	4 Mar 2010 09:37:52 -0000	1.222
+++ cygwin/cygwin.din	2 Apr 2010 20:32:29 -0000
@@ -899,6 +899,7 @@ llistxattr SIGFE
 llrint = _f_llrint NOSIGFE
 llrintf = _f_llrintf NOSIGFE
 llrintl = _f_llrintl NOSIGFE
+__locale_mb_cur_max NOSIGFE
 localeconv NOSIGFE
 _localeconv = localeconv NOSIGFE
 localtime SIGFE
Index: cygwin/nlsfuncs.cc
===================================================================
RCS file: /cvs/src/src/winsup/cygwin/nlsfuncs.cc,v
retrieving revision 1.29
diff -u -p -r1.29 nlsfuncs.cc
--- cygwin/nlsfuncs.cc	1 Apr 2010 20:30:07 -0000	1.29
+++ cygwin/nlsfuncs.cc	2 Apr 2010 20:32:29 -0000
@@ -21,6 +21,7 @@ details. */
 #include "tls_pbuf.h"
 /* Internal headers from newlib */
 #include "../locale/timelocal.h"
+#include "../locale/lctype.h"
 #include "../locale/lnumeric.h"
 #include "../locale/lmonetary.h"
 #include "../locale/lmessages.h"
@@ -30,10 +31,13 @@ details. */
 #define _LC(x)	&lc_##x##_ptr,lc_##x##_end-lc_##x##_ptr
 
 #define getlocaleinfo(category,type) \
-	    __getlocaleinfo(lcid,(type),_LC(category),f_wctomb,charset)
+	    __getlocaleinfo(lcid,(type),_LC(category))
 #define eval_datetimefmt(type,flags) \
 	    __eval_datetimefmt(lcid,(type),(flags),&lc_time_ptr,\
-			       lc_time_end-lc_time_ptr,f_wctomb, charset)
+			       lc_time_end-lc_time_ptr)
+#define charfromwchar(category,in) \
+	    __charfromwchar (_##category##_locale->in,_LC(category),\
+			     f_wctomb,charset)
 
 #define has_modifier(x)	((x)[0] && !strcmp (modifier, (x)))
 
@@ -340,16 +344,28 @@ rebase_locale_buf (const void *ptrv, con
     *ptrs++ += newbase - oldbase;
 }
 
+static wchar_t *
+__getlocaleinfo (LCID lcid, LCTYPE type, char **ptr, size_t size)
+{
+  size_t num;
+  wchar_t *ret;
+
+  if ((uintptr_t) *ptr % 1)
+    ++*ptr;
+  ret = (wchar_t *) *ptr;
+  num = GetLocaleInfoW (lcid, type, ret, size / sizeof (wchar_t));
+  *ptr = (char *) (ret + num);
+  return ret;
+}
+
 static char *
-__getlocaleinfo (LCID lcid, LCTYPE type, char **ptr, size_t size,
+__charfromwchar (const wchar_t *in, char **ptr, size_t size,
 		 wctomb_p f_wctomb, const char *charset)
 {
-  wchar_t wbuf[80];
   size_t num;
   char *ret;
 
-  GetLocaleInfoW (lcid, type, wbuf, 80);
-  num = lc_wcstombs (f_wctomb, charset, ret = *ptr, wbuf, size);
+  num = lc_wcstombs (f_wctomb, charset, ret = *ptr, in, size);
   *ptr += num + 1;
   return ret;
 }
@@ -368,41 +384,34 @@ enum dt_flags {
   DT_ABBREV	= 0x02,	/* Enforce abbreviated month and day names. */
 };
 
-static char *
+static wchar_t *
 __eval_datetimefmt (LCID lcid, LCTYPE type, dt_flags flags, char **ptr,
-		    size_t size, wctomb_p f_wctomb, const char *charset)
+		    size_t size)
 {
   wchar_t buf[80];
   wchar_t fc;
-  size_t num;
-  mbstate_t mb;
   size_t idx;
-  const char *day_str = "edaA";
-  const char *mon_str = "mmbB";
-  const char *year_str = "yyyY";
-  const char *hour12_str = "lI";
-  const char *hour24_str = "kH";
-  const char *t_str;
-  char *ret = *ptr;
-  char *p = *ptr;
-
+  const wchar_t *day_str = L"edaA";
+  const wchar_t *mon_str = L"mmbB";
+  const wchar_t *year_str = L"yyyY";
+  const wchar_t *hour12_str = L"lI";
+  const wchar_t *hour24_str = L"kH";
+  const wchar_t *t_str;
+
+  if ((uintptr_t) *ptr % 1)
+    ++*ptr;
+  wchar_t *ret = (wchar_t *) *ptr;
+  wchar_t *p = (wchar_t *) *ptr;
   GetLocaleInfoW (lcid, type, buf, 80);
-  memset (&mb, 0, sizeof mb);
   for (wchar_t *fmt = buf; *fmt; ++fmt)
     switch (fc = *fmt)
       {
       case L'\'':
 	if (fmt[1] == L'\'')
-	  *p++ = '\'';
+	  *p++ = L'\'';
 	else
 	  while (fmt[1] && *++fmt != L'\'')
-	    {
-	      num = f_wctomb (_REENT, p, *fmt, charset, &mb);
-	      if (num == (size_t) -1)
-		memset (&mb, 0, sizeof mb);
-	      else
-		p += num;
-	    }
+	    *p++ = *fmt;
 	break;
       case L'd':
       case L'M':
@@ -413,7 +422,7 @@ __eval_datetimefmt (LCID lcid, LCTYPE ty
 	  idx = 3;
 	if ((flags & DT_ABBREV) && fc != L'y' && idx == 3)
 	  idx = 2;
-	*p++ = '%';
+	*p++ = L'%';
 	*p++ = t_str[idx];
 	break;
       case L'g':
@@ -428,7 +437,7 @@ __eval_datetimefmt (LCID lcid, LCTYPE ty
 	    ++fmt;
 	    idx = 1;
 	  }
-	*p++ = '%';
+	*p++ = L'%';
 	*p++ = t_str[idx];
 	break;
       case L'm':
@@ -436,25 +445,21 @@ __eval_datetimefmt (LCID lcid, LCTYPE ty
       case L't':
 	if (fmt[1] == fc)
 	  ++fmt;
-	*p++ = '%';
-	*p++ = (fc == L'm' ? 'M' : fc == L's' ? 'S' : 'p');
+	*p++ = L'%';
+	*p++ = (fc == L'm' ? L'M' : fc == L's' ? L'S' : L'p');
 	break;
       case L'\t':
       case L'\n':
       case L'%':
-	*p++ = '%';
-	*p++ = (char) fc;
+	*p++ = L'%';
+	*p++ = fc;
 	break;
       default:
-	num = f_wctomb (_REENT, p, *fmt, charset, &mb);
-	if (num == (size_t) -1)
-	  memset (&mb, 0, sizeof mb);
-	else
-	  p += num;
+	*p++ = *fmt;
 	break;
       }
-  *p++ = '\0';
-  *ptr = p;
+  *p++ = L'\0';
+  *ptr = (char *) p;
   return ret;
 }
 
@@ -493,197 +498,236 @@ conv_grouping (LCID lcid, LCTYPE type, c
    in the structure pointed to by _time_locale.  This is subsequently
    accessed by functions like nl_langinfo, strftime, strptime. */
 extern "C" int
-__set_lc_time_from_win (const char *name, struct lc_time_T *_time_locale,
+__set_lc_time_from_win (const char *name,
+			const struct lc_time_T *_C_time_locale,
+			struct lc_time_T *_time_locale,
 			char **lc_time_buf, wctomb_p f_wctomb,
 			const char *charset)
 {
   LCID lcid = __get_lcid_from_locale (name);
-  if (!lcid || lcid == (LCID) -1)
+  if (lcid == (LCID) -1)
     return lcid;
+  if (!lcid && !strcmp (charset, "ASCII"))
+    return 0;
+
+# define MAX_TIME_BUFFER_SIZE	4096
 
-  char *new_lc_time_buf = (char *) malloc (4096);
-  const char *lc_time_end = new_lc_time_buf + 4096;
+  char *new_lc_time_buf = (char *) malloc (MAX_TIME_BUFFER_SIZE);
+  const char *lc_time_end = new_lc_time_buf + MAX_TIME_BUFFER_SIZE;
 
   if (!new_lc_time_buf)
     return -1;
   char *lc_time_ptr = new_lc_time_buf;
 
-  char locale[ENCODING_LEN + 1];
-  strcpy (locale, name);
-  /* Removes the charset from the locale and attach the modifer to the
-     language_TERRITORY part. */
-  char *c = strchr (locale, '.');
-  if (c)
-    {
-      *c = '\0';
-      char *c2 = strchr (c + 1, '@');
-      /* Ignore @cjknarrow modifier since it's a very personal thing between
-	 Cygwin and newlib... */
-      if (c2 && strcmp (c2, "@cjknarrow"))
-      	memmove (c, c2, strlen (c2) + 1);
-    }
-  /* Now search in the alphabetically order lc_era array for the
-     locale. */
-  lc_era_t locale_key = { locale, NULL, NULL, NULL, NULL, NULL ,
-				  NULL, NULL, NULL, NULL, NULL };
-  lc_era_t *era = (lc_era_t *) bsearch ((void *) &locale_key, (void *) lc_era,
-					sizeof lc_era / sizeof *lc_era,
-					sizeof *lc_era, locale_cmp);
-
-  /* mon */
-  for (int i = 0; i < 12; ++i)
-    _time_locale->mon[i] = getlocaleinfo (time, LOCALE_SABBREVMONTHNAME1 + i);
-  /* month and alt_month */
-  for (int i = 0; i < 12; ++i)
-    _time_locale->month[i] = _time_locale->alt_month[i]
-			   = getlocaleinfo (time, LOCALE_SMONTHNAME1 + i);
-  /* wday */
-  _time_locale->wday[0] = getlocaleinfo (time, LOCALE_SABBREVDAYNAME7);
-  for (int i = 0; i < 6; ++i)
-    _time_locale->wday[i + 1] = getlocaleinfo (time,
-					       LOCALE_SABBREVDAYNAME1 + i);
-  /* weekday */
-  _time_locale->weekday[0] = getlocaleinfo (time, LOCALE_SDAYNAME7);
-  for (int i = 0; i < 6; ++i)
-    _time_locale->weekday[i + 1] = getlocaleinfo (time, LOCALE_SDAYNAME1 + i);
-
-  size_t len;
-  /* X_fmt */
-  if (era && *era->t_fmt)
-    {
-      _time_locale->X_fmt = (const char *) lc_time_ptr;
-      len = lc_wcstombs (f_wctomb, charset, lc_time_ptr, era->t_fmt,
-			 lc_time_end - lc_time_ptr) + 1;
-      lc_time_ptr += len;
-    }
-  else
-    _time_locale->X_fmt = eval_datetimefmt (LOCALE_STIMEFORMAT, DT_DEFAULT);
-  /* x_fmt */
-  if (era && *era->d_fmt)
-    {
-      _time_locale->x_fmt = (const char *) lc_time_ptr;
-      len = lc_wcstombs (f_wctomb, charset, lc_time_ptr, era->d_fmt,
-			 lc_time_end - lc_time_ptr) + 1;
-      lc_time_ptr += len;
-    }
-  else
-    _time_locale->x_fmt = eval_datetimefmt (LOCALE_SSHORTDATE, DT_DEFAULT);
-  /* c_fmt */
-  if (era && *era->d_t_fmt)
-    {
-      _time_locale->c_fmt = (const char *) lc_time_ptr;
-      len = lc_wcstombs (f_wctomb, charset, lc_time_ptr, era->d_t_fmt,
-			 lc_time_end - lc_time_ptr) + 1;
-      lc_time_ptr += len;
-    }
-  else
-    {
-      _time_locale->c_fmt = eval_datetimefmt (LOCALE_SLONGDATE, DT_ABBREV);
-      --lc_time_ptr;
-      *lc_time_ptr++ = ' ';
-      eval_datetimefmt (LOCALE_STIMEFORMAT, DT_DEFAULT);
-    }
-  /* AM/PM */
-  _time_locale->am_pm[0] = getlocaleinfo (time, LOCALE_S1159);
-  _time_locale->am_pm[1] = getlocaleinfo (time, LOCALE_S2359);
-  /* date_fmt */
-  if (era && *era->date_fmt)
-    {
-      _time_locale->date_fmt = (const char *) lc_time_ptr;
-      len = lc_wcstombs (f_wctomb, charset, lc_time_ptr, era->date_fmt,
-			 lc_time_end - lc_time_ptr) + 1;
-      lc_time_ptr += len;
-    }
-  else
-    {
-      _time_locale->date_fmt = eval_datetimefmt (LOCALE_SLONGDATE, DT_ABBREV);
-      --lc_time_ptr;
-      *lc_time_ptr++ = ' ';
-      eval_datetimefmt (LOCALE_STIMEFORMAT, DT_DEFAULT);
-      --lc_time_ptr;
-      lc_time_ptr = stpcpy (lc_time_ptr, " %Z") + 1;
-    }
-  /* md */
-  {
-    wchar_t buf[80];
-    GetLocaleInfoW (lcid, LOCALE_IDATE, buf, 80);
-    lc_time_ptr = stpcpy (lc_time_ptr, *buf == L'1' ? "dm" : "md") + 1;
-  }
-  /* ampm_fmt */
-  if (era)
-    {
-      _time_locale->ampm_fmt = (const char *) lc_time_ptr;
-      len = lc_wcstombs (f_wctomb, charset, lc_time_ptr, era->t_fmt_ampm,
-			 lc_time_end - lc_time_ptr) + 1;
-      lc_time_ptr += len;
-    }
-  else
-    _time_locale->ampm_fmt = eval_datetimefmt (LOCALE_STIMEFORMAT, DT_AMPM);
+  /* C.foo is just a copy of "C" with fixed charset. */
+  if (!lcid)
+    memcpy (_time_locale, _C_time_locale, sizeof (struct lc_time_T));
+  /* codeset */
+  _time_locale->codeset = lc_time_ptr;
+  lc_time_ptr = stpcpy (lc_time_ptr, charset) + 1;
+  
+  if (lcid)
+    {
+      char locale[ENCODING_LEN + 1];
+      strcpy (locale, name);
+      /* Removes the charset from the locale and attach the modifer to the
+	 language_TERRITORY part. */
+      char *c = strchr (locale, '.');
+      if (c)
+	{
+	  *c = '\0';
+	  char *c2 = strchr (c + 1, '@');
+	  /* Ignore @cjknarrow modifier since it's a very personal thing between
+	     Cygwin and newlib... */
+	  if (c2 && strcmp (c2, "@cjknarrow"))
+	    memmove (c, c2, strlen (c2) + 1);
+	}
+      /* Now search in the alphabetically order lc_era array for the
+	 locale. */
+      lc_era_t locale_key = { locale, NULL, NULL, NULL, NULL, NULL ,
+				      NULL, NULL, NULL, NULL, NULL };
+      lc_era_t *era = (lc_era_t *) bsearch ((void *) &locale_key, (void *) lc_era,
+					    sizeof lc_era / sizeof *lc_era,
+					    sizeof *lc_era, locale_cmp);
 
-  if (era)
-    {
-      /* Evaluate string length in target charset.  Characters invalid in the
-	 target charset are simply ignored, as on Linux. */
-      len = 0;
-      len += lc_wcstombs (f_wctomb, charset, NULL, era->era, 0) + 1;
-      len += lc_wcstombs (f_wctomb, charset, NULL, era->era_d_fmt, 0) + 1;
-      len += lc_wcstombs (f_wctomb, charset, NULL, era->era_d_t_fmt, 0) + 1;
-      len += lc_wcstombs (f_wctomb, charset, NULL, era->era_t_fmt, 0) + 1;
-      len += lc_wcstombs (f_wctomb, charset, NULL, era->alt_digits, 0) + 1;
-
-      /* Make sure data fits into the buffer */
-      if (lc_time_ptr + len > lc_time_end)
-	{
-	  len = lc_time_ptr + len - new_lc_time_buf;
-	  char *tmp = (char *) realloc (new_lc_time_buf, len);
-	  if (!tmp)
-	    era = NULL;
-	  else
+      /* mon */
+      for (int i = 0; i < 12; ++i)
+	{
+	  _time_locale->wmon[i] = getlocaleinfo (time,
+						 LOCALE_SABBREVMONTHNAME1 + i);
+	  _time_locale->mon[i] = charfromwchar (time, wmon[i]);
+	}
+      /* month and alt_month */
+      for (int i = 0; i < 12; ++i)
+	{
+	  _time_locale->wmonth[i] = getlocaleinfo (time, LOCALE_SMONTHNAME1 + i);
+	  _time_locale->month[i] = _time_locale->alt_month[i]
+				 = charfromwchar (time, wmonth[i]);
+	}
+      /* wday */
+      _time_locale->wwday[0] = getlocaleinfo (time, LOCALE_SABBREVDAYNAME7);
+      _time_locale->wday[0] = charfromwchar (time, wwday[0]);
+      for (int i = 0; i < 6; ++i)
+	{
+	  _time_locale->wwday[i + 1] = getlocaleinfo (time,
+						      LOCALE_SABBREVDAYNAME1 + i);
+	  _time_locale->wday[i + 1] = charfromwchar (time, wwday[i + 1]);
+	}
+      /* weekday */
+      _time_locale->wweekday[0] = getlocaleinfo (time, LOCALE_SDAYNAME7);
+      _time_locale->weekday[0] = charfromwchar (time, wweekday[0]);
+      for (int i = 0; i < 6; ++i)
+	{
+	  _time_locale->wweekday[i + 1] = getlocaleinfo (time,
+							 LOCALE_SDAYNAME1 + i);
+	  _time_locale->weekday[i + 1] = charfromwchar (time, wweekday[i + 1]);
+	}
+      size_t len;
+      /* X_fmt */
+      if (era && *era->t_fmt)
+	{
+	  _time_locale->wX_fmt = (const wchar_t *) lc_time_ptr;
+	  lc_time_ptr = (char *) (wcpcpy ((wchar_t *) _time_locale->wX_fmt,
+					  era->t_fmt) + 1);
+	}
+      else
+	_time_locale->wX_fmt = eval_datetimefmt (LOCALE_STIMEFORMAT, DT_DEFAULT);
+      _time_locale->X_fmt = charfromwchar (time, wX_fmt);
+      /* x_fmt */
+      if (era && *era->d_fmt)
+	{
+	  _time_locale->wx_fmt = (const wchar_t *) lc_time_ptr;
+	  lc_time_ptr = (char *) (wcpcpy ((wchar_t *) _time_locale->wx_fmt,
+					  era->d_fmt) + 1);
+	}
+      else
+	_time_locale->wx_fmt = eval_datetimefmt (LOCALE_SSHORTDATE, DT_DEFAULT);
+      _time_locale->x_fmt = charfromwchar (time, wx_fmt);
+      /* c_fmt */
+      if (era && *era->d_t_fmt)
+	{
+	  _time_locale->wc_fmt = (const wchar_t *) lc_time_ptr;
+	  lc_time_ptr = (char *) (wcpcpy ((wchar_t *) _time_locale->wc_fmt,
+					  era->d_t_fmt) + 1);
+	}
+      else
+	{
+	  _time_locale->wc_fmt = eval_datetimefmt (LOCALE_SLONGDATE, DT_ABBREV);
+	  ((wchar_t *) lc_time_ptr)[-1] = L' ';
+	  eval_datetimefmt (LOCALE_STIMEFORMAT, DT_DEFAULT);
+	}
+      _time_locale->c_fmt = charfromwchar (time, wc_fmt);
+      /* AM/PM */
+      _time_locale->wam_pm[0] = getlocaleinfo (time, LOCALE_S1159);
+      _time_locale->wam_pm[1] = getlocaleinfo (time, LOCALE_S2359);
+      _time_locale->am_pm[0] = charfromwchar (time, wam_pm[0]);
+      _time_locale->am_pm[1] = charfromwchar (time, wam_pm[1]);
+      /* date_fmt */
+      if (era && *era->date_fmt)
+	{
+	  _time_locale->wdate_fmt = (const wchar_t *) lc_time_ptr;
+	  lc_time_ptr = (char *) (wcpcpy ((wchar_t *) _time_locale->wdate_fmt,
+					  era->date_fmt) + 1);
+	}
+      else
+	_time_locale->wdate_fmt = _time_locale->wc_fmt;
+      _time_locale->date_fmt = charfromwchar (time, wdate_fmt);
+      /* md */
+      {
+	wchar_t buf[80];
+	GetLocaleInfoW (lcid, LOCALE_IDATE, buf, 80);
+	lc_time_ptr = stpcpy (lc_time_ptr, *buf == L'1' ? "dm" : "md") + 1;
+      }
+      /* ampm_fmt */
+      if (era)
+	{
+	  _time_locale->wampm_fmt = (const wchar_t *) lc_time_ptr;
+	  lc_time_ptr = (char *) (wcpcpy ((wchar_t *) _time_locale->wampm_fmt,
+					  era->t_fmt_ampm) + 1);
+	}
+      else
+	_time_locale->wampm_fmt = eval_datetimefmt (LOCALE_STIMEFORMAT, DT_AMPM);
+      _time_locale->ampm_fmt = charfromwchar (time, wampm_fmt);
+
+      if (era)
+	{
+	  /* Evaluate string length in target charset.  Characters invalid in the
+	     target charset are simply ignored, as on Linux. */
+	  len = 0;
+	  len += lc_wcstombs (f_wctomb, charset, NULL, era->era, 0) + 1;
+	  len += lc_wcstombs (f_wctomb, charset, NULL, era->era_d_fmt, 0) + 1;
+	  len += lc_wcstombs (f_wctomb, charset, NULL, era->era_d_t_fmt, 0) + 1;
+	  len += lc_wcstombs (f_wctomb, charset, NULL, era->era_t_fmt, 0) + 1;
+	  len += lc_wcstombs (f_wctomb, charset, NULL, era->alt_digits, 0) + 1;
+	  len += (wcslen (era->era) + 1) * sizeof (wchar_t);
+	  len += (wcslen (era->era_d_fmt) + 1) * sizeof (wchar_t);
+	  len += (wcslen (era->era_d_t_fmt) + 1) * sizeof (wchar_t);
+	  len += (wcslen (era->era_t_fmt) + 1) * sizeof (wchar_t);
+	  len += (wcslen (era->alt_digits) + 1) * sizeof (wchar_t);
+
+	  /* Make sure data fits into the buffer */
+	  if (lc_time_ptr + len > lc_time_end)
+	    {
+	      len = lc_time_ptr + len - new_lc_time_buf;
+	      char *tmp = (char *) realloc (new_lc_time_buf, len);
+	      if (!tmp)
+		era = NULL;
+	      else
+		{
+		  if (tmp != new_lc_time_buf)
+		    rebase_locale_buf (_time_locale, tmp, new_lc_time_buf,
+				       _time_locale + 1);
+		  lc_time_ptr = tmp + (lc_time_ptr - new_lc_time_buf);
+		  new_lc_time_buf = tmp;
+		  lc_time_end = new_lc_time_buf + len;
+		}
+	    }
+	  /* Copy over */
+	  if (era)
 	    {
-	      if (tmp != new_lc_time_buf)
-		rebase_locale_buf (_time_locale, tmp, new_lc_time_buf,
-				   _time_locale + 1);
-	      lc_time_ptr = tmp + (lc_time_ptr - new_lc_time_buf);
-	      new_lc_time_buf = tmp;
-	      lc_time_end = new_lc_time_buf + len;
+	      /* era */
+	      _time_locale->wera = (const wchar_t *) lc_time_ptr;
+	      lc_time_ptr = (char *) (wcpcpy ((wchar_t *) _time_locale->wera,
+					      era->era) + 1);
+	      _time_locale->era = charfromwchar (time, wera);
+	      /* era_d_fmt */
+	      _time_locale->wera_d_fmt = (const wchar_t *) lc_time_ptr;
+	      lc_time_ptr = (char *) (wcpcpy ((wchar_t *) _time_locale->wera_d_fmt,
+					      era->era_d_fmt) + 1);
+	      _time_locale->era_d_fmt = charfromwchar (time, wera_d_fmt);
+	      /* era_d_t_fmt */
+	      _time_locale->wera_d_t_fmt = (const wchar_t *) lc_time_ptr;
+	      lc_time_ptr = (char *) (wcpcpy ((wchar_t *) _time_locale->wera_d_t_fmt,
+					      era->era_d_t_fmt) + 1);
+	      _time_locale->era_d_t_fmt = charfromwchar (time, wera_d_t_fmt);
+	      /* era_t_fmt */
+	      _time_locale->wera_t_fmt = (const wchar_t *) lc_time_ptr;
+	      lc_time_ptr = (char *) (wcpcpy ((wchar_t *) _time_locale->wera_t_fmt,
+					      era->era_t_fmt) + 1);
+	      _time_locale->era_t_fmt = charfromwchar (time, wera_t_fmt);
+	      /* alt_digits */
+	      _time_locale->walt_digits = (const wchar_t *) lc_time_ptr;
+	      lc_time_ptr = (char *) (wcpcpy ((wchar_t *) _time_locale->walt_digits,
+					      era->alt_digits) + 1);
+	      _time_locale->alt_digits = charfromwchar (time, walt_digits);
 	    }
 	}
-      /* Copy over */
-      if (era)
+      if (!era)
 	{
-	  /* era */
-	  _time_locale->era = (const char *) lc_time_ptr;
-	  len = lc_wcstombs (f_wctomb, charset, lc_time_ptr, era->era,
-			     lc_time_end - lc_time_ptr) + 1;
-	  /* era_d_fmt */
-	  _time_locale->era_d_fmt = (const char *) (lc_time_ptr += len);
-	  len = lc_wcstombs (f_wctomb, charset, lc_time_ptr, era->era_d_fmt,
-			     lc_time_end - lc_time_ptr) + 1;
-	  /* era_d_t_fmt */
-	  _time_locale->era_d_t_fmt = (const char *) (lc_time_ptr += len);
-	  len = lc_wcstombs (f_wctomb, charset, lc_time_ptr, era->era_d_t_fmt,
-			     lc_time_end - lc_time_ptr) + 1;
-	  /* era_t_fmt */
-	  _time_locale->era_t_fmt = (const char *) (lc_time_ptr += len);
-	  len = lc_wcstombs (f_wctomb, charset, lc_time_ptr, era->era_t_fmt,
-			     lc_time_end - lc_time_ptr) + 1;
-	  /* alt_digits */
-	  _time_locale->alt_digits = (const char *) (lc_time_ptr += len);
-	  len = lc_wcstombs (f_wctomb, charset, lc_time_ptr, era->alt_digits,
-			     lc_time_end - lc_time_ptr) + 1;
-	  lc_time_ptr += len;
-	}
-    }
-  if (!era)
-    {
-      _time_locale->era =
-      _time_locale->era_d_fmt = 
-      _time_locale->era_d_t_fmt =
-      _time_locale->era_t_fmt =
-      _time_locale->alt_digits = (const char *) lc_time_ptr;
-      /* Twice, to make sure era and alt_strings are correctly terminated
-         with two NULs */
-      *lc_time_ptr++ = '\0';
+	  _time_locale->wera =
+	  _time_locale->wera_d_fmt = 
+	  _time_locale->wera_d_t_fmt =
+	  _time_locale->wera_t_fmt =
+	  _time_locale->walt_digits = (const wchar_t *) lc_time_ptr;
+	  _time_locale->era =
+	  _time_locale->era_d_fmt = 
+	  _time_locale->era_d_t_fmt =
+	  _time_locale->era_t_fmt =
+	  _time_locale->alt_digits = (const char *) lc_time_ptr;
+	  /* Twice, to make sure wide char strings are correctly terminated. */
+	  *lc_time_ptr++ = '\0';
+	  *lc_time_ptr++ = '\0';
+	}
     }
 
   char *tmp = (char *) realloc (new_lc_time_buf, lc_time_ptr - new_lc_time_buf);
@@ -701,36 +745,125 @@ __set_lc_time_from_win (const char *name
   return 1;
 }
 
+/* Called from newlib's setlocale() via __ctype_load_locale() if category
+   is LC_CTYPE.  Returns LC_CTYPE values fetched from Windows locale data
+   in the structure pointed to by _ctype_locale.  This is subsequently
+   accessed by functions like nl_langinfo, localeconv, printf, etc. */
+extern "C" int
+__set_lc_ctype_from_win (const char *name,
+			 const struct lc_ctype_T *_C_ctype_locale,
+			 struct lc_ctype_T *_ctype_locale,
+			 char **lc_ctype_buf, wctomb_p f_wctomb,
+			 const char *charset, int mb_cur_max)
+{
+  LCID lcid = __get_lcid_from_locale (name);
+  if (lcid == (LCID) -1)
+    return lcid;
+  if (!lcid && !strcmp (charset, "ASCII"))
+    return 0;
+
+# define MAX_CTYPE_BUFFER_SIZE	256
+
+  char *new_lc_ctype_buf = (char *) malloc (MAX_CTYPE_BUFFER_SIZE);
+
+  if (!new_lc_ctype_buf)
+    return -1;
+  char *lc_ctype_ptr = new_lc_ctype_buf;
+  /* C.foo is just a copy of "C" with fixed charset. */
+  if (!lcid)
+    memcpy (_ctype_locale, _C_ctype_locale, sizeof (struct lc_ctype_T));
+  /* codeset */
+  _ctype_locale->codeset = lc_ctype_ptr;
+  lc_ctype_ptr = stpcpy (lc_ctype_ptr, charset) + 1;
+  /* mb_cur_max */
+  _ctype_locale->mb_cur_max = lc_ctype_ptr;
+  *lc_ctype_ptr++ = mb_cur_max;
+  *lc_ctype_ptr++ = '\0';
+  if (lcid)
+    {
+      /* outdigits and woutdigits */
+      wchar_t digits[11];
+      GetLocaleInfoW (lcid, LOCALE_SNATIVEDIGITS, digits, 11);
+      for (int i = 0; i <= 9; ++i)
+	{
+	  mbstate_t state;
+
+	  /* Make sure the wchar_t's are always 2 byte aligned. */
+	  if ((uintptr_t) lc_ctype_ptr % 2)
+	    ++lc_ctype_ptr;
+	  wchar_t *woutdig = (wchar_t *) lc_ctype_ptr;
+	  _ctype_locale->woutdigits[i] = (const wchar_t *) woutdig;
+	  *woutdig++ = digits[i];
+	  *woutdig++ = L'\0';
+	  lc_ctype_ptr = (char *) woutdig;
+	  _ctype_locale->outdigits[i] = lc_ctype_ptr;
+	  memset (&state, 0, sizeof state);
+	  lc_ctype_ptr += f_wctomb (_REENT, lc_ctype_ptr, digits[i], charset,
+				      &state);
+	  *lc_ctype_ptr++ = '\0';
+	}
+    }
+
+  char *tmp = (char *) realloc (new_lc_ctype_buf,
+  				lc_ctype_ptr - new_lc_ctype_buf);
+  if (!tmp)
+    {
+      free (new_lc_ctype_buf);
+      return -1;
+    }
+  if (tmp != new_lc_ctype_buf)
+    rebase_locale_buf (_ctype_locale, tmp, new_lc_ctype_buf,
+		       _ctype_locale + 1);
+  if (*lc_ctype_buf)
+    free (*lc_ctype_buf);
+  *lc_ctype_buf = tmp;
+  return 1;
+}
+
 /* Called from newlib's setlocale() via __numeric_load_locale() if category
    is LC_NUMERIC.  Returns LC_NUMERIC values fetched from Windows locale data
    in the structure pointed to by _numeric_locale.  This is subsequently
    accessed by functions like nl_langinfo, localeconv, printf, etc. */
 extern "C" int
 __set_lc_numeric_from_win (const char *name,
+			   const struct lc_numeric_T *_C_numeric_locale,
 			   struct lc_numeric_T *_numeric_locale,
 			   char **lc_numeric_buf, wctomb_p f_wctomb,
 			   const char *charset)
 {
   LCID lcid = __get_lcid_from_locale (name);
-  if (!lcid || lcid == (LCID) -1)
+  if (lcid == (LCID) -1)
     return lcid;
+  if (!lcid && !strcmp (charset, "ASCII"))
+    return 0;
 
-  char *new_lc_numeric_buf = (char *) malloc (48);
-  const char *lc_numeric_end = new_lc_numeric_buf + 48;
+# define MAX_NUMERIC_BUFFER_SIZE	256
+
+  char *new_lc_numeric_buf = (char *) malloc (MAX_NUMERIC_BUFFER_SIZE);
+  const char *lc_numeric_end = new_lc_numeric_buf + MAX_NUMERIC_BUFFER_SIZE;
 
   if (!new_lc_numeric_buf)
     return -1;
   char *lc_numeric_ptr = new_lc_numeric_buf;
-  /* decimal_point */
-  _numeric_locale->decimal_point = getlocaleinfo (numeric,
-						  LOCALE_SDECIMAL);
-  /* thousands_sep */
-  _numeric_locale->thousands_sep = getlocaleinfo (numeric,
-						  LOCALE_STHOUSAND);
-  /* grouping */
-  _numeric_locale->grouping = conv_grouping (lcid, LOCALE_SGROUPING,
-					     &lc_numeric_ptr);
-
+  /* C.foo is just a copy of "C" with fixed charset. */
+  if (!lcid)
+    memcpy (_numeric_locale, _C_numeric_locale, sizeof (struct lc_numeric_T));
+  else
+    {
+      /* decimal_point */
+      _numeric_locale->wdecimal_point = getlocaleinfo (numeric, LOCALE_SDECIMAL);
+      _numeric_locale->decimal_point = charfromwchar (numeric, wdecimal_point);
+      /* thousands_sep */
+      _numeric_locale->wthousands_sep = getlocaleinfo (numeric, LOCALE_STHOUSAND);
+      _numeric_locale->thousands_sep = charfromwchar (numeric, wthousands_sep);
+      /* grouping */
+      _numeric_locale->grouping = conv_grouping (lcid, LOCALE_SGROUPING,
+						 &lc_numeric_ptr);
+    }
+  /* codeset */
+  _numeric_locale->codeset = lc_numeric_ptr;
+  lc_numeric_ptr = stpcpy (lc_numeric_ptr, charset) + 1;
+  
   char *tmp = (char *) realloc (new_lc_numeric_buf,
   				lc_numeric_ptr - new_lc_numeric_buf);
   if (!tmp)
@@ -753,85 +886,110 @@ __set_lc_numeric_from_win (const char *n
    accessed by functions like nl_langinfo, localeconv, printf, etc. */
 extern "C" int
 __set_lc_monetary_from_win (const char *name,
+			    const struct lc_monetary_T *_C_monetary_locale,
 			    struct lc_monetary_T *_monetary_locale,
 			    char **lc_monetary_buf, wctomb_p f_wctomb,
 			    const char *charset)
 {
   LCID lcid = __get_lcid_from_locale (name);
-  if (!lcid || lcid == (LCID) -1)
+  if (lcid == (LCID) -1)
     return lcid;
+  if (!lcid && !strcmp (charset, "ASCII"))
+    return 0;
 
-  char *new_lc_monetary_buf = (char *) malloc (256);
-  const char *lc_monetary_end = new_lc_monetary_buf + 256;
+# define MAX_MONETARY_BUFFER_SIZE	512
+
+  char *new_lc_monetary_buf = (char *) malloc (MAX_MONETARY_BUFFER_SIZE);
+  const char *lc_monetary_end = new_lc_monetary_buf + MAX_MONETARY_BUFFER_SIZE;
 
   if (!new_lc_monetary_buf)
     return -1;
   char *lc_monetary_ptr = new_lc_monetary_buf;
-  /* int_curr_symbol */
-  _monetary_locale->int_curr_symbol = getlocaleinfo (monetary,
-						     LOCALE_SINTLSYMBOL);
-  /* No spacing char means space. */
-  if (!_monetary_locale->int_curr_symbol[3])
-    {
-      lc_monetary_ptr[-1] = ' ';
-      *lc_monetary_ptr++ = '\0';
-    }
-  /* currency_symbol */
-  {
-    /* As on Linux:  If the currency_symbol can't be represented in the
-       given charset, use int_curr_symbol. */
-    wchar_t wbuf[14];
-    GetLocaleInfoW (lcid, LOCALE_SCURRENCY, wbuf, 14);
-    if (lc_wcstombs (f_wctomb, charset, NULL, wbuf, 0, true) == (size_t) -1)
-      {
-	_monetary_locale->currency_symbol = lc_monetary_ptr;
-	lc_monetary_ptr = stpncpy (lc_monetary_ptr,
-				   _monetary_locale->int_curr_symbol, 3);
-	*lc_monetary_ptr++ = '\0';
-      }
-    else
-      _monetary_locale->currency_symbol = getlocaleinfo (monetary,
-							 LOCALE_SCURRENCY);
-  }
-  /* mon_decimal_point */
-  _monetary_locale->mon_decimal_point = getlocaleinfo (monetary,
-						       LOCALE_SMONDECIMALSEP);
-  /* mon_thousands_sep */
-  _monetary_locale->mon_thousands_sep = getlocaleinfo (monetary,
-						       LOCALE_SMONTHOUSANDSEP);
-  /* mon_grouping */
-  _monetary_locale->mon_grouping = conv_grouping (lcid, LOCALE_SMONGROUPING,
-						  &lc_monetary_ptr);
-  /* positive_sign */
-  _monetary_locale->positive_sign = getlocaleinfo (monetary,
-						   LOCALE_SPOSITIVESIGN);
-  /* negative_sign */
-  _monetary_locale->negative_sign = getlocaleinfo (monetary,
-						   LOCALE_SNEGATIVESIGN);
-  /* int_frac_digits */
-  *lc_monetary_ptr = (char) getlocaleint (lcid, LOCALE_IINTLCURRDIGITS);
-  _monetary_locale->int_frac_digits = lc_monetary_ptr++;
-  /* frac_digits */
-  *lc_monetary_ptr = (char) getlocaleint (lcid, LOCALE_ICURRDIGITS);
-  _monetary_locale->frac_digits = lc_monetary_ptr++;
-  /* p_cs_precedes */
-  *lc_monetary_ptr = (char) getlocaleint (lcid, LOCALE_IPOSSYMPRECEDES);
-  _monetary_locale->p_cs_precedes = lc_monetary_ptr++;
-  /* p_sep_by_space */
-  *lc_monetary_ptr = (char) getlocaleint (lcid, LOCALE_IPOSSEPBYSPACE);
-  _monetary_locale->p_sep_by_space = lc_monetary_ptr++;
-  /* n_cs_precedes */
-  *lc_monetary_ptr = (char) getlocaleint (lcid, LOCALE_INEGSYMPRECEDES);
-  _monetary_locale->n_cs_precedes = lc_monetary_ptr++;
-  /* n_sep_by_space */
-  *lc_monetary_ptr = (char) getlocaleint (lcid, LOCALE_INEGSEPBYSPACE);
-  _monetary_locale->n_sep_by_space = lc_monetary_ptr++;
-  /* p_sign_posn */
-  *lc_monetary_ptr = (char) getlocaleint (lcid, LOCALE_IPOSSIGNPOSN);
-  _monetary_locale->p_sign_posn = lc_monetary_ptr++;
-  /* p_sign_posn */
-  *lc_monetary_ptr = (char) getlocaleint (lcid, LOCALE_INEGSIGNPOSN);
-  _monetary_locale->n_sign_posn = lc_monetary_ptr++;
+  /* C.foo is just a copy of "C" with fixed charset. */
+  if (!lcid)
+    memcpy (_monetary_locale, _C_monetary_locale, sizeof (struct lc_monetary_T));
+  else
+    {
+      /* int_curr_symbol */
+      _monetary_locale->wint_curr_symbol = getlocaleinfo (monetary,
+							  LOCALE_SINTLSYMBOL);
+      /* No spacing char means space. */
+      if (!_monetary_locale->wint_curr_symbol[3])
+	{
+	  wchar_t *wc = (wchar_t *) _monetary_locale->wint_curr_symbol + 3;
+	  *wc++ = L' ';
+	  *wc++ = L'\0';
+	  lc_monetary_ptr = (char *) wc;
+	}
+      _monetary_locale->int_curr_symbol = charfromwchar (monetary,
+							 wint_curr_symbol);
+      /* currency_symbol */
+      _monetary_locale->wcurrency_symbol = getlocaleinfo (monetary,
+							  LOCALE_SCURRENCY);
+      /* As on Linux:  If the currency_symbol can't be represented in the
+	 given charset, use int_curr_symbol. */
+      if (lc_wcstombs (f_wctomb, charset, NULL,
+		       _monetary_locale->wcurrency_symbol,
+		       0, true) == (size_t) -1)
+	_monetary_locale->currency_symbol = _monetary_locale->int_curr_symbol;
+      else
+	_monetary_locale->currency_symbol = charfromwchar (monetary,
+							   wcurrency_symbol);
+      /* mon_decimal_point */
+      _monetary_locale->wmon_decimal_point = getlocaleinfo (monetary,
+							    LOCALE_SMONDECIMALSEP);
+      _monetary_locale->mon_decimal_point = charfromwchar (monetary,
+							   wmon_decimal_point);
+      /* mon_thousands_sep */
+      _monetary_locale->wmon_thousands_sep = getlocaleinfo (monetary,
+							    LOCALE_SMONTHOUSANDSEP);
+      _monetary_locale->mon_thousands_sep = charfromwchar (monetary,
+							   wmon_thousands_sep);
+      /* mon_grouping */
+      _monetary_locale->mon_grouping = conv_grouping (lcid, LOCALE_SMONGROUPING,
+						      &lc_monetary_ptr);
+      /* positive_sign */
+      _monetary_locale->wpositive_sign = getlocaleinfo (monetary,
+							LOCALE_SPOSITIVESIGN);
+      _monetary_locale->positive_sign = charfromwchar (monetary, wpositive_sign);
+      /* negative_sign */
+      _monetary_locale->wnegative_sign = getlocaleinfo (monetary,
+							LOCALE_SNEGATIVESIGN);
+      _monetary_locale->negative_sign = charfromwchar (monetary, wnegative_sign);
+      /* int_frac_digits */
+      *lc_monetary_ptr = (char) getlocaleint (lcid, LOCALE_IINTLCURRDIGITS);
+      _monetary_locale->int_frac_digits = lc_monetary_ptr++;
+      /* frac_digits */
+      *lc_monetary_ptr = (char) getlocaleint (lcid, LOCALE_ICURRDIGITS);
+      _monetary_locale->frac_digits = lc_monetary_ptr++;
+      /* p_cs_precedes and int_p_cs_precedes */
+      *lc_monetary_ptr = (char) getlocaleint (lcid, LOCALE_IPOSSYMPRECEDES);
+      _monetary_locale->p_cs_precedes
+	    = _monetary_locale->int_p_cs_precedes = lc_monetary_ptr++;
+      /* p_sep_by_space and int_p_sep_by_space */
+      *lc_monetary_ptr = (char) getlocaleint (lcid, LOCALE_IPOSSEPBYSPACE);
+      _monetary_locale->p_sep_by_space
+	    = _monetary_locale->int_p_sep_by_space = lc_monetary_ptr++;
+      /* n_cs_precedes and int_n_cs_precedes */
+      *lc_monetary_ptr = (char) getlocaleint (lcid, LOCALE_INEGSYMPRECEDES);
+      _monetary_locale->n_cs_precedes
+	    = _monetary_locale->int_n_cs_precedes = lc_monetary_ptr++;
+      /* n_sep_by_space and int_n_sep_by_space */
+      *lc_monetary_ptr = (char) getlocaleint (lcid, LOCALE_INEGSEPBYSPACE);
+      _monetary_locale->n_sep_by_space
+	    = _monetary_locale->int_n_sep_by_space = lc_monetary_ptr++;
+      /* p_sign_posn and int_p_sign_posn */
+      *lc_monetary_ptr = (char) getlocaleint (lcid, LOCALE_IPOSSIGNPOSN);
+      _monetary_locale->p_sign_posn
+	    = _monetary_locale->int_p_sign_posn = lc_monetary_ptr++;
+      /* n_sign_posn and int_n_sign_posn */
+      *lc_monetary_ptr = (char) getlocaleint (lcid, LOCALE_INEGSIGNPOSN);
+      _monetary_locale->n_sign_posn
+	    = _monetary_locale->int_n_sign_posn = lc_monetary_ptr++;
+    }
+  /* codeset */
+  _monetary_locale->codeset = lc_monetary_ptr;
+  lc_monetary_ptr = stpcpy (lc_monetary_ptr, charset) + 1;
 
   char *tmp = (char *) realloc (new_lc_monetary_buf,
   				lc_monetary_ptr - new_lc_monetary_buf);
@@ -851,46 +1009,66 @@ __set_lc_monetary_from_win (const char *
 
 extern "C" int
 __set_lc_messages_from_win (const char *name,
+			    const struct lc_messages_T *_C_messages_locale,
 			    struct lc_messages_T *_messages_locale,
 			    char **lc_messages_buf,
 			    wctomb_p f_wctomb, const char *charset)
 {
   LCID lcid = __get_lcid_from_locale (name);
-  if (!lcid || lcid == (LCID) -1)
+  if (lcid == (LCID) -1)
     return lcid;
+  if (!lcid && !strcmp (charset, "ASCII"))
+    return 0;
 
   char locale[ENCODING_LEN + 1];
   char *c, *c2;
+  lc_msg_t *msg = NULL;
 
-  strcpy (locale, name);
-  /* Removes the charset from the locale and attach the modifer to the
-     language_TERRITORY part. */
-  c = strchr (locale, '.');
-  if (c)
+  /* C.foo is just a copy of "C" with fixed charset. */
+  if (!lcid)
+    memcpy (_messages_locale, _C_messages_locale, sizeof (struct lc_messages_T));
+  else
     {
-      *c = '\0';
-      c2 = strchr (c + 1, '@');
-      /* Ignore @cjknarrow modifier since it's a very personal thing between
-	 Cygwin and newlib... */
-      if (c2 && strcmp (c2, "@cjknarrow"))
-      	memmove (c, c2, strlen (c2) + 1);
-    }
-  /* Now search in the alphabetically order lc_msg array for the
-     locale. */
-  lc_msg_t locale_key = { locale, NULL, NULL, NULL, NULL };
-  lc_msg_t *msg = (lc_msg_t *) bsearch ((void *) &locale_key, (void *) lc_msg,
-					sizeof lc_msg / sizeof *lc_msg,
-					sizeof *lc_msg, locale_cmp);
-  if (!msg)
-    return 0;
+      strcpy (locale, name);
+      /* Removes the charset from the locale and attach the modifer to the
+	 language_TERRITORY part. */
+      c = strchr (locale, '.');
+      if (c)
+	{
+	  *c = '\0';
+	  c2 = strchr (c + 1, '@');
+	  /* Ignore @cjknarrow modifier since it's a very personal thing between
+	     Cygwin and newlib... */
+	  if (c2 && strcmp (c2, "@cjknarrow"))
+	    memmove (c, c2, strlen (c2) + 1);
+	}
+      /* Now search in the alphabetically order lc_msg array for the
+	 locale. */
+      lc_msg_t locale_key = { locale, NULL, NULL, NULL, NULL };
+      msg = (lc_msg_t *) bsearch ((void *) &locale_key, (void *) lc_msg,
+				  sizeof lc_msg / sizeof *lc_msg,
+				  sizeof *lc_msg, locale_cmp);
+      if (!msg)
+	return 0;
+    }
 
   /* Evaluate string length in target charset.  Characters invalid in the
      target charset are simply ignored, as on Linux. */
   size_t len = 0;
-  len += lc_wcstombs (f_wctomb, charset, NULL, msg->yesexpr, 0) + 1;
-  len += lc_wcstombs (f_wctomb, charset, NULL, msg->noexpr, 0) + 1;
-  len += lc_wcstombs (f_wctomb, charset, NULL, msg->yesstr, 0) + 1;
-  len += lc_wcstombs (f_wctomb, charset, NULL, msg->nostr, 0) + 1;
+  len += (strlen (charset) + 1);
+  if (lcid)
+    {
+      len += lc_wcstombs (f_wctomb, charset, NULL, msg->yesexpr, 0) + 1;
+      len += lc_wcstombs (f_wctomb, charset, NULL, msg->noexpr, 0) + 1;
+      len += lc_wcstombs (f_wctomb, charset, NULL, msg->yesstr, 0) + 1;
+      len += lc_wcstombs (f_wctomb, charset, NULL, msg->nostr, 0) + 1;
+      len += (wcslen (msg->yesexpr) + 1) * sizeof (wchar_t);
+      len += (wcslen (msg->noexpr) + 1) * sizeof (wchar_t);
+      len += (wcslen (msg->yesstr) + 1) * sizeof (wchar_t);
+      len += (wcslen (msg->nostr) + 1) * sizeof (wchar_t);
+      if (len % 1)
+	++len;
+    }
   /* Allocate. */
   char *new_lc_messages_buf = (char *) malloc (len);
   const char *lc_messages_end = new_lc_messages_buf + len;
@@ -899,14 +1077,32 @@ __set_lc_messages_from_win (const char *
     return -1;
   /* Copy over. */
   c = new_lc_messages_buf;
-  _messages_locale->yesexpr = (const char *) c;
-  len = lc_wcstombs (f_wctomb, charset, c, msg->yesexpr, lc_messages_end - c);
-  _messages_locale->noexpr = (const char *) (c += len + 1);
-  len = lc_wcstombs (f_wctomb, charset, c, msg->noexpr, lc_messages_end - c);
-  _messages_locale->yesstr = (const char *) (c += len + 1);
-  len = lc_wcstombs (f_wctomb, charset, c, msg->yesstr, lc_messages_end - c);
-  _messages_locale->nostr = (const char *) (c += len + 1);
-  lc_wcstombs (f_wctomb, charset, c, msg->nostr, lc_messages_end - c);
+  /* codeset */
+  _messages_locale->codeset = c;
+  c = stpcpy (c, charset) + 1;
+  if (lcid)
+    {
+      _messages_locale->yesexpr = (const char *) c;
+      len = lc_wcstombs (f_wctomb, charset, c, msg->yesexpr, lc_messages_end - c);
+      _messages_locale->noexpr = (const char *) (c += len + 1);
+      len = lc_wcstombs (f_wctomb, charset, c, msg->noexpr, lc_messages_end - c);
+      _messages_locale->yesstr = (const char *) (c += len + 1);
+      len = lc_wcstombs (f_wctomb, charset, c, msg->yesstr, lc_messages_end - c);
+      _messages_locale->nostr = (const char *) (c += len + 1);
+      len = lc_wcstombs (f_wctomb, charset, c, msg->nostr, lc_messages_end - c);
+      c += len + 1;
+      if ((uintptr_t) c % 1)
+	++c;
+      wchar_t *wc = (wchar_t *) c;
+      _messages_locale->wyesexpr = (const wchar_t *) wc;
+      wc = wcpcpy (wc, msg->yesexpr) + 1;
+      _messages_locale->wnoexpr = (const wchar_t *) wc;
+      wc = wcpcpy (wc, msg->noexpr) + 1;
+      _messages_locale->wyesstr = (const wchar_t *) wc;
+      wc = wcpcpy (wc, msg->yesstr) + 1;
+      _messages_locale->wnostr = (const wchar_t *) wc;
+      wcpcpy (wc, msg->nostr);
+    }
   /* Aftermath. */
   if (*lc_messages_buf)
     free (*lc_messages_buf);
@@ -933,6 +1129,12 @@ __collate_load_locale (const char *name,
   return 0;
 }
 
+extern "C" const char *
+__get_current_collate_codeset (void)
+{
+  return collate_charset;
+}
+
 /* We use the Windows functions for locale-specific string comparison and
    transformation.  The advantage is that we don't need any files with
    collation information. */
Index: cygwin/include/cygwin/config.h
===================================================================
RCS file: /cvs/src/src/winsup/cygwin/include/cygwin/config.h,v
retrieving revision 1.13
diff -u -p -r1.13 config.h
--- cygwin/include/cygwin/config.h	26 Feb 2010 09:36:21 -0000	1.13
+++ cygwin/include/cygwin/config.h	2 Apr 2010 20:32:29 -0000
@@ -54,6 +54,7 @@ extern char *_tlsbase __asm__ ("%fs:4");
 #define __LINUX_ERRNO_EXTENSIONS__ 1
 #define _MB_EXTENDED_CHARSETS_ALL 1
 #define __HAVE_LOCALE_INFO__ 1
+#define __HAVE_LOCALE_INFO_EXTENDED__ 1
 #define _WANT_C99_TIME_FORMATS 1
 #if defined(__INSIDE_CYGWIN__) || defined(_COMPILING_NEWLIB)
 #define __EXPORT __declspec(dllexport)

-- 
Corinna Vinschen                  Please, send mails regarding Cygwin to
Cygwin Project Co-Leader          cygwin AT cygwin DOT com
Red Hat


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]