summary refs log tree commit diff
path: root/stdio-common
diff options
context:
space:
mode:
authorUlrich Drepper <drepper@redhat.com>2000-02-11 18:50:36 +0000
committerUlrich Drepper <drepper@redhat.com>2000-02-11 18:50:36 +0000
commita1d84548c8aa7023cd039c85f81b831eef6d4a4c (patch)
treed4f80547dd8c427aebbf0b43051d01206ea972ae /stdio-common
parentf296f567c3f69fd2a61983f464d5c52174e3bab8 (diff)
downloadglibc-a1d84548c8aa7023cd039c85f81b831eef6d4a4c.tar.gz
glibc-a1d84548c8aa7023cd039c85f81b831eef6d4a4c.tar.xz
glibc-a1d84548c8aa7023cd039c85f81b831eef6d4a4c.zip
Update.
2000-02-11  Ulrich Drepper  <drepper@redhat.com>

	* stdio-common/printf-parse.h (parse_one_spec): Set wide elements.

	* stdio-common/printf_fp.c: Truely support wide characater output.
	Finally handle decimal points and thousands separator characters
	correctly for multibyte output.
	* stdio-common/printf_size.c: Likewise.
	* sysdeps/generic/printf_fphex.c: Likewise.
	* sysdeps/ieee754/ldbl-96/printf_fphex.c: Likewise.

	* stdio-common/vfscanf.c: Implement I modifier for numbers to read
	locale dependent digits.

	* locale/C-monetary.c (_nl_C_LC_MONETARY): Change wide character
	decimal point and thousands separator values to wide characters from
	wide character strings.
	* locale/C-numeric.c (_nl_C_LC_NUMERIC): Likewise.

	* locale/indigitswc.h: Dereference wcdigits array elements.

2000-02-03  Jakub Jelinek  <jakub@redhat.com>

	* stdlib/canonicalize.c (canonicalize): Zero terminate
	path to copy on error.

2000-02-01  Cristian Gafton  <gafton@redhat.com>

	* misc/syslog.c (closelog): Reset LogType to SOCK_DGRAM.

2000-01-31  Philip Blundell  <philb@gnu.org>

	* sysdeps/arm/fpu/fpu_control.h (_FPU_DEFAULT): Set the AC bit.

2000-01-31  Andreas Jaeger  <aj@suse.de>

	* intl/Makefile (generated): msgs.h is generated.

	* localedata/Makefile (generated-dirs): Add de_DE.437.

2000-01-31  Jakub Jelinek  <jakub@redhat.com>

	* config.make.in: Allow default localedir to come from configure.
	* configure.in: Export libc_cv_localedir.
	* sysdeps/unix/sysv/linux/configure.in: For sparc64, put locale
	stuff into $exec_prefix/lib/locale because it can be shared between
	32bit and 64bit libraries.
	* configure: Rebuilt.
	* sysdeps/unix/sysv/linux/configure: Rebuilt.

2000-01-31  Andreas Jaeger  <aj@suse.de>

	* inet/tst-network.c: New file.
	* inet/Makefile (tests): Add tst-network.

	* inet/inet_net.c (inet_network): Don't overwrite memory or allow
	to great last digits.
Diffstat (limited to 'stdio-common')
-rw-r--r--stdio-common/printf-parse.h1
-rw-r--r--stdio-common/printf_fp.c347
-rw-r--r--stdio-common/printf_size.c47
-rw-r--r--stdio-common/vfscanf.c150
4 files changed, 382 insertions, 163 deletions
diff --git a/stdio-common/printf-parse.h b/stdio-common/printf-parse.h
index 64ecbe023f..c79c67d058 100644
--- a/stdio-common/printf-parse.h
+++ b/stdio-common/printf-parse.h
@@ -149,6 +149,7 @@ parse_one_spec (const UCHAR_T *format, size_t posn, struct printf_spec *spec,
   spec->info.showsign = 0;
   spec->info.group = 0;
   spec->info.pad = ' ';
+  spec->info.wide = sizeof (UCHAR_T) > 1;
 
   /* Test for positional argument.  */
   if (ISDIGIT (*format))
diff --git a/stdio-common/printf_fp.c b/stdio-common/printf_fp.c
index 13598e3030..1f0334f5e9 100644
--- a/stdio-common/printf_fp.c
+++ b/stdio-common/printf_fp.c
@@ -1,5 +1,5 @@
 /* Floating point output for `printf'.
-   Copyright (C) 1995, 1996, 1997, 1998, 1999 Free Software Foundation, Inc.
+   Copyright (C) 1995-1999, 2000 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995.
 
@@ -51,18 +51,18 @@
 /* This defines make it possible to use the same code for GNU C library and
    the GNU I/O library.	 */
 #ifdef USE_IN_LIBIO
-#  define PUT(f, s, n) _IO_sputn (f, s, n)
-#  define PAD(f, c, n) (wide ? _IO_wpadn (f, c, n) : _IO_padn (f, c, n))
+# define PUT(f, s, n) _IO_sputn (f, s, n)
+# define PAD(f, c, n) (wide ? _IO_wpadn (f, c, n) : _IO_padn (f, c, n))
 /* We use this file GNU C library and GNU I/O library.	So make
    names equal.	 */
-#  undef putc
-#  define putc(c, f) (wide \
+# undef putc
+# define putc(c, f) (wide \
 		      ? _IO_putwc_unlocked (c, f) : _IO_putc_unlocked (c, f))
-#  define size_t     _IO_size_t
-#  define FILE	     _IO_FILE
+# define size_t     _IO_size_t
+# define FILE	     _IO_FILE
 #else	/* ! USE_IN_LIBIO */
-#  define PUT(f, s, n) fwrite (s, 1, n, f)
-#  define PAD(f, c, n) __printf_pad (f, c, n)
+# define PUT(f, s, n) fwrite (s, 1, n, f)
+# define PAD(f, c, n) __printf_pad (f, c, n)
 ssize_t __printf_pad __P ((FILE *, char pad, int n)); /* In vfprintf.c.  */
 #endif	/* USE_IN_LIBIO */
 
@@ -77,21 +77,25 @@ ssize_t __printf_pad __P ((FILE *, char pad, int n)); /* In vfprintf.c.  */
       ++done;								      \
     } while (0)
 
-#define PRINT(ptr, len)							      \
+#define PRINT(ptr, wptr, len)						      \
   do									      \
     {									      \
       register size_t outlen = (len);					      \
       if (len > 20)							      \
 	{								      \
-	  if (PUT (fp, ptr, outlen) != outlen)				      \
+	  if (PUT (fp, wide ? (const char *) wptr : ptr, outlen) != outlen)   \
 	    return -1;							      \
 	  ptr += outlen;						      \
 	  done += outlen;						      \
 	}								      \
       else								      \
 	{								      \
-	  while (outlen-- > 0)						      \
-	    outchar (*ptr++);						      \
+	  if (wide)							      \
+	    while (outlen-- > 0)					      \
+	      outchar (*wptr++);					      \
+	  else								      \
+	    while (outlen-- > 0)					      \
+	      outchar (*ptr++);						      \
 	}								      \
     } while (0)
 
@@ -125,11 +129,12 @@ extern mp_size_t __mpn_extract_long_double (mp_ptr res_ptr, mp_size_t size,
 					    int *expt, int *is_neg,
 					    long double value);
 extern unsigned int __guess_grouping (unsigned int intdig_max,
-				      const char *grouping, wchar_t sepchar);
+				      const char *grouping);
 
 
-static char *group_number (char *buf, char *bufend, unsigned int intdig_no,
-			   const char *grouping, wchar_t thousands_sep)
+static wchar_t *group_number (wchar_t *buf, wchar_t *bufend,
+			      unsigned int intdig_no, const char *grouping,
+			      wchar_t thousands_sep, int ngroups)
      internal_function;
 
 
@@ -147,14 +152,17 @@ __printf_fp (FILE *fp,
   fpnum;
 
   /* Locale-dependent representation of decimal point.	*/
-  wchar_t decimal;
+  const char *decimal;
+  wchar_t decimalwc;
 
   /* Locale-dependent thousands separator and grouping specification.  */
-  wchar_t thousands_sep;
+  const char *thousands_sep = NULL;
+  wchar_t thousands_sepwc = 0;
   const char *grouping;
 
   /* "NaN" or "Inf" for the special cases.  */
   const char *special = NULL;
+  const wchar_t *wspecial = NULL;
 
   /* We need just a few limbs for the input before shifting to the right
      position.	*/
@@ -178,7 +186,7 @@ __printf_fp (FILE *fp,
   MPN_VAR(tmp);
 
   /* Digit which is result of last hack_digit() call.  */
-  int digit;
+  wchar_t digit;
 
   /* The type of output format that will be used: 'e'/'E' or 'f'.  */
   int type;
@@ -192,7 +200,7 @@ __printf_fp (FILE *fp,
   /* Nonzero if this is output on a wide character stream.  */
   int wide = info->wide;
 
-  char hack_digit (void)
+  wchar_t hack_digit (void)
     {
       mp_limb_t hi;
 
@@ -222,7 +230,7 @@ __printf_fp (FILE *fp,
 		  /* We're not prepared for an mpn variable with zero
 		     limbs.  */
 		  fracsize = 1;
-		  return '0' + hi;
+		  return L'0' + hi;
 		}
 	    }
 
@@ -231,35 +239,24 @@ __printf_fp (FILE *fp,
 	    frac[fracsize++] = cy;
 	}
 
-      return '0' + hi;
+      return L'0' + hi;
     }
 
 
   /* Figure out the decimal point character.  */
   if (info->extra == 0)
     {
-      mbstate_t state;
-
-      memset (&state, '\0', sizeof (state));
-      if (__mbrtowc (&decimal, _NL_CURRENT (LC_NUMERIC, DECIMAL_POINT),
-		     strlen (_NL_CURRENT (LC_NUMERIC, DECIMAL_POINT)),
-		     &state) <= 0)
-	decimal = (wchar_t) *_NL_CURRENT (LC_NUMERIC, DECIMAL_POINT);
+      decimal = _NL_CURRENT (LC_NUMERIC, DECIMAL_POINT);
+      decimalwc = _NL_CURRENT_WORD (LC_NUMERIC, _NL_NUMERIC_DECIMAL_POINT_WC);
     }
   else
     {
-      mbstate_t state;
-
-      memset (&state, '\0', sizeof (state));
-      if (__mbrtowc (&decimal, _NL_CURRENT (LC_MONETARY, MON_DECIMAL_POINT),
-		     strlen (_NL_CURRENT (LC_MONETARY, MON_DECIMAL_POINT)),
-		     &state) <= 0)
-	decimal = (wchar_t) *_NL_CURRENT (LC_MONETARY, MON_DECIMAL_POINT);
+      decimal = _NL_CURRENT (LC_MONETARY, MON_DECIMAL_POINT);
+      decimalwc = _NL_CURRENT_WORD (LC_MONETARY,
+				    _NL_MONETARY_DECIMAL_POINT_WC);
     }
-  /* Give default value.  */
-  if (decimal == L'\0')
-    decimal = L'.';
-
+  /* The decimal point character must not be zero.  */
+  assert (*decimal != L'\0');
 
   if (info->group)
     {
@@ -273,34 +270,33 @@ __printf_fp (FILE *fp,
       else
 	{
 	  /* Figure out the thousands separator character.  */
-	  if (info->extra == 0)
+	  if (wide)
 	    {
-	      mbstate_t state;
-
-	      memset (&state, '\0', sizeof (state));
-	      if (__mbrtowc (&thousands_sep, _NL_CURRENT (LC_NUMERIC,
-							  THOUSANDS_SEP),
-			     strlen (_NL_CURRENT (LC_NUMERIC, THOUSANDS_SEP)),
-			     &state) <= 0)
-		thousands_sep = (wchar_t) *_NL_CURRENT (LC_NUMERIC,
-							THOUSANDS_SEP);
+	      if (info->extra == 0)
+		thousands_sepwc =
+		  _NL_CURRENT_WORD (LC_NUMERIC, _NL_NUMERIC_THOUSANDS_SEP_WC);
+	      else
+		thousands_sepwc =
+		  _NL_CURRENT_WORD (LC_MONETARY,
+				    _NL_MONETARY_THOUSANDS_SEP_WC);
 	    }
 	  else
 	    {
-	      mbstate_t state;
-
-	      memset (&state, '\0', sizeof (state));
-	      if (__mbrtowc (&thousands_sep, _NL_CURRENT (LC_MONETARY,
-							  MON_THOUSANDS_SEP),
-			     strlen (_NL_CURRENT (LC_MONETARY,
-						  MON_THOUSANDS_SEP)),
-			     &state) <= 0)
-		thousands_sep = (wchar_t) *_NL_CURRENT (LC_MONETARY,
-							MON_THOUSANDS_SEP);
+	      if (info->extra == 0)
+		thousands_sep = _NL_CURRENT (LC_NUMERIC, THOUSANDS_SEP);
+	      else
+		thousands_sep = _NL_CURRENT (LC_MONETARY, MON_THOUSANDS_SEP);
 	    }
 
-	  if (thousands_sep == L'\0')
+	  if ((wide && thousands_sepwc == L'\0')
+	      || (! wide && *thousands_sep == '\0'))
 	    grouping = NULL;
+	  else if (thousands_sepwc == L'\0')
+	    /* If we are printing multibyte characters and there is a
+	       multibyte representation for the thousands separator,
+	       we must ensure the wide character thousands separator
+	       is available, even if it is fake.  */
+	    thousands_sepwc = 0xfffffffe;
 	}
     }
   else
@@ -315,12 +311,30 @@ __printf_fp (FILE *fp,
       /* Check for special values: not a number or infinity.  */
       if (__isnanl (fpnum.ldbl))
 	{
-	  special = isupper (info->spec) ? "NAN" : "nan";
+	  if (isupper (info->spec))
+	    {
+	      special = "NAN";
+	      wspecial = L"NAN";
+	    }
+	    else
+	      {
+		special = "nan";
+		wspecial = L"nan";
+	      }
 	  is_neg = 0;
 	}
       else if (__isinfl (fpnum.ldbl))
 	{
-	  special = isupper (info->spec) ? "INF" : "inf";
+	  if (isupper (info->spec))
+	    {
+	      special = "INF";
+	      wspecial = L"INF";
+	    }
+	  else
+	    {
+	      special = "inf";
+	      wspecial = L"inf";
+	    }
 	  is_neg = fpnum.ldbl < 0;
 	}
       else
@@ -341,12 +355,30 @@ __printf_fp (FILE *fp,
       /* Check for special values: not a number or infinity.  */
       if (__isnan (fpnum.dbl))
 	{
-	  special = isupper (info->spec) ? "NAN" : "nan";
+	  if (isupper (info->spec))
+	    {
+	      special = "NAN";
+	      wspecial = L"NAN";
+	    }
+	  else
+	    {
+	      special = "nan";
+	      wspecial = L"nan";
+	    }
 	  is_neg = 0;
 	}
       else if (__isinf (fpnum.dbl))
 	{
-	  special = isupper (info->spec) ? "INF" : "inf";
+	  if (isupper (info->spec))
+	    {
+	      special = "INF";
+	      wspecial = L"INF";
+	    }
+	  else
+	    {
+	      special = "inf";
+	      wspecial = L"inf";
+	    }
 	  is_neg = fpnum.dbl < 0;
 	}
       else
@@ -377,7 +409,7 @@ __printf_fp (FILE *fp,
       else if (info->space)
 	outchar (' ');
 
-      PRINT (special, 3);
+      PRINT (special, wspecial, 3);
 
       if (info->left && width > 0)
 	PADN (' ', width);
@@ -746,7 +778,7 @@ __printf_fp (FILE *fp,
 
   {
     int width = info->width;
-    char *buffer, *startp, *cp;
+    wchar_t *wbuffer, *wstartp, *wcp;
     int buffer_malloced;
     int chars_needed;
     int expscale;
@@ -754,6 +786,7 @@ __printf_fp (FILE *fp,
     int fracdig_min, fracdig_max, fracdig_no = 0;
     int dig_max;
     int significant;
+    int ngroups = 0;
 
     if (_tolower (info->spec) == 'e')
       {
@@ -811,25 +844,28 @@ __printf_fp (FILE *fp,
       }
 
     if (grouping)
-      /* Guess the number of groups we will make, and thus how
-	 many spaces we need for separator characters.  */
-      chars_needed += __guess_grouping (intdig_max, grouping, thousands_sep);
+      {
+	/* Guess the number of groups we will make, and thus how
+	   many spaces we need for separator characters.  */
+	ngroups = __guess_grouping (intdig_max, grouping);
+	chars_needed += ngroups;
+      }
 
     /* Allocate buffer for output.  We need two more because while rounding
        it is possible that we need two more characters in front of all the
        other output.  If the amount of memory we have to allocate is too
        large use `malloc' instead of `alloca'.  */
-    buffer_malloced = chars_needed > 20000;
+    buffer_malloced = chars_needed > 5000;
     if (buffer_malloced)
       {
-	buffer = (char *) malloc (2 + chars_needed);
-	if (buffer == NULL)
+	wbuffer = (wchar_t *) malloc ((2 + chars_needed) * sizeof (wchar_t));
+	if (wbuffer == NULL)
 	  /* Signal an error to the caller.  */
 	  return -1;
       }
     else
-      buffer = (char *) alloca (2 + chars_needed);
-    cp = startp = buffer + 2;	/* Let room for rounding.  */
+      wbuffer = (wchar_t *) alloca ((2 + chars_needed) * sizeof (wchar_t));
+    wcp = wstartp = wbuffer + 2;	/* Let room for rounding.  */
 
     /* Do the real work: put digits in allocated buffer.  */
     if (expsign == 0 || type != 'f')
@@ -838,21 +874,21 @@ __printf_fp (FILE *fp,
 	while (intdig_no < intdig_max)
 	  {
 	    ++intdig_no;
-	    *cp++ = hack_digit ();
+	    *wcp++ = hack_digit ();
 	  }
 	significant = 1;
 	if (info->alt
 	    || fracdig_min > 0
 	    || (fracdig_max > 0 && (fracsize > 1 || frac[0] != 0)))
-	  *cp++ = decimal;
+	  *wcp++ = decimalwc;
       }
     else
       {
 	/* |fp| < 1.0 and the selected type is 'f', so put "0."
 	   in the buffer.  */
-	*cp++ = '0';
+	*wcp++ = L'0';
 	--exponent;
-	*cp++ = decimal;
+	*wcp++ = decimalwc;
       }
 
     /* Generate the needed number of fractional digits.	 */
@@ -860,8 +896,8 @@ __printf_fp (FILE *fp,
 	   || (fracdig_no < fracdig_max && (fracsize > 1 || frac[0] != 0)))
       {
 	++fracdig_no;
-	*cp = hack_digit ();
-	if (*cp != '0')
+	*wcp = hack_digit ();
+	if (*wcp != L'0')
 	  significant = 1;
 	else if (significant == 0)
 	  {
@@ -869,16 +905,16 @@ __printf_fp (FILE *fp,
 	    if (fracdig_min > 0)
 	      ++fracdig_min;
 	  }
-	++cp;
+	++wcp;
       }
 
     /* Do rounding.  */
     digit = hack_digit ();
-    if (digit > '4')
+    if (digit > L'4')
       {
-	char *tp = cp;
+	wchar_t *wtp = wcp;
 
-	if (digit == '5' && (*(cp - 1) & 1) == 0)
+	if (digit == L'5' && (*(wcp - 1) & 1) == 0)
 	  {
 	    /* This is the critical case.	 */
 	    if (fracsize == 1 && frac[0] == 0)
@@ -903,31 +939,31 @@ __printf_fp (FILE *fp,
 	  {
 	    /* Process fractional digits.  Terminate if not rounded or
 	       radix character is reached.  */
-	    while (*--tp != decimal && *tp == '9')
-	      *tp = '0';
-	    if (*tp != decimal)
+	    while (*--wtp != decimalwc && *wtp == L'9')
+	      *wtp = '0';
+	    if (*wtp != decimalwc)
 	      /* Round up.  */
-	      (*tp)++;
+	      (*wtp)++;
 	  }
 
-	if (fracdig_no == 0 || *tp == decimal)
+	if (fracdig_no == 0 || *wtp == decimalwc)
 	  {
 	    /* Round the integer digits.  */
-	    if (*(tp - 1) == decimal)
-	      --tp;
+	    if (*(wtp - 1) == decimalwc)
+	      --wtp;
 
-	    while (--tp >= startp && *tp == '9')
-	      *tp = '0';
+	    while (--wtp >= wstartp && *wtp == L'9')
+	      *wtp = L'0';
 
-	    if (tp >= startp)
+	    if (wtp >= wstartp)
 	      /* Round up.  */
-	      (*tp)++;
+	      (*wtp)++;
 	    else
 	      /* It is more critical.  All digits were 9's.  */
 	      {
 		if (type != 'f')
 		  {
-		    *startp = '1';
+		    *wstartp = '1';
 		    exponent += expsign == 0 ? 1 : -1;
 		  }
 		else if (intdig_no == dig_max)
@@ -935,13 +971,13 @@ __printf_fp (FILE *fp,
 		    /* This is the case where for type %g the number fits
 		       really in the range for %f output but after rounding
 		       the number of digits is too big.	 */
-		    *--startp = decimal;
-		    *--startp = '1';
+		    *--wstartp = decimalwc;
+		    *--wstartp = L'1';
 
 		    if (info->alt || fracdig_no > 0)
 		      {
 			/* Overwrite the old radix character.  */
-			startp[intdig_no + 2] = '0';
+			wstartp[intdig_no + 2] = L'0';
 			++fracdig_no;
 		      }
 
@@ -956,7 +992,7 @@ __printf_fp (FILE *fp,
 		  {
 		    /* We can simply add another another digit before the
 		       radix.  */
-		    *--startp = '1';
+		    *--wstartp = L'1';
 		    ++intdig_no;
 		  }
 
@@ -965,7 +1001,7 @@ __printf_fp (FILE *fp,
 		   fractional digits.  */
 		if (intdig_no + fracdig_no > dig_max)
 		  {
-		    cp -= intdig_no + fracdig_no - dig_max;
+		    wcp -= intdig_no + fracdig_no - dig_max;
 		    fracdig_no -= intdig_no + fracdig_no - dig_max;
 		  }
 	      }
@@ -974,25 +1010,26 @@ __printf_fp (FILE *fp,
 
   do_expo:
     /* Now remove unnecessary '0' at the end of the string.  */
-    while (fracdig_no > fracdig_min && *(cp - 1) == '0')
+    while (fracdig_no > fracdig_min && *(wcp - 1) == L'0')
       {
-	--cp;
+	--wcp;
 	--fracdig_no;
       }
     /* If we eliminate all fractional digits we perhaps also can remove
        the radix character.  */
-    if (fracdig_no == 0 && !info->alt && *(cp - 1) == decimal)
-      --cp;
+    if (fracdig_no == 0 && !info->alt && *(wcp - 1) == decimalwc)
+      --wcp;
 
     if (grouping)
       /* Add in separator characters, overwriting the same buffer.  */
-      cp = group_number (startp, cp, intdig_no, grouping, thousands_sep);
+      wcp = group_number (wstartp, wcp, intdig_no, grouping, thousands_sepwc,
+			  ngroups);
 
     /* Write the exponent if it is needed.  */
     if (type != 'f')
       {
-	*cp++ = type;
-	*cp++ = expsign ? '-' : '+';
+	*wcp++ = (wchar_t) type;
+	*wcp++ = expsign ? L'-' : L'+';
 
 	/* Find the magnitude of the exponent.	*/
 	expscale = 10;
@@ -1001,23 +1038,23 @@ __printf_fp (FILE *fp,
 
 	if (exponent < 10)
 	  /* Exponent always has at least two digits.  */
-	  *cp++ = '0';
+	  *wcp++ = L'0';
 	else
 	  do
 	    {
 	      expscale /= 10;
-	      *cp++ = '0' + (exponent / expscale);
+	      *wcp++ = L'0' + (exponent / expscale);
 	      exponent %= expscale;
 	    }
 	  while (expscale > 10);
-	*cp++ = '0' + exponent;
+	*wcp++ = L'0' + exponent;
       }
 
     /* Compute number of characters which must be filled with the padding
        character.  */
     if (is_neg || info->showsign || info->space)
       --width;
-    width -= cp - startp;
+    width -= wcp - wstartp;
 
     if (!info->left && info->pad != '0' && width > 0)
       PADN (info->pad, width);
@@ -1032,14 +1069,66 @@ __printf_fp (FILE *fp,
     if (!info->left && info->pad == '0' && width > 0)
       PADN ('0', width);
 
-    PRINT (startp, cp - startp);
+    {
+      char *buffer = NULL;
+      char *cp = NULL;
+
+      if (! wide)
+	{
+	  /* Create the single byte string.  */
+	  const char *decimal;
+	  size_t decimal_len;
+	  size_t thousands_sep_len;
+	  wchar_t *copywc;
+
+	  if (info->extra == 0)
+	    decimal = _NL_CURRENT (LC_NUMERIC, DECIMAL_POINT);
+	  else
+	    decimal = _NL_CURRENT (LC_MONETARY, MON_DECIMAL_POINT);
+	  decimal_len = strlen (decimal);
+
+	  if (thousands_sep == NULL)
+	    thousands_sep_len = 0;
+	  else
+	    thousands_sep_len = strlen (thousands_sep);
+
+	  if (buffer_malloced)
+	    {
+	      buffer = (char *) malloc (2 + chars_needed + decimal_len
+					+ ngroups * thousands_sep_len);
+	      if (buffer == NULL)
+		/* Signal an error to the caller.  */
+		return -1;
+	    }
+	  else
+	    buffer = (char *) alloca (2 + chars_needed + decimal_len
+				      + ngroups * thousands_sep_len);
+
+	  /* Now copy the wide character string.  Since the character
+	     (except for the decimal point and thousands separator) must
+	     be coming from the ASCII range we can esily convert the
+	     string without mapping tables.  */
+	  for (cp = buffer, copywc = wstartp; copywc < wcp; ++copywc)
+	    if (*copywc == decimalwc)
+	      cp = (char *) __mempcpy (cp, decimal, decimal_len);
+	    else if (*copywc == thousands_sepwc)
+	      cp = (char *) __mempcpy (cp, thousands_sep, thousands_sep_len);
+	    else
+	      *cp++ = (char) *copywc;
+	}
+
+      PRINT (buffer, wstartp, wide ? wcp - wstartp : cp - buffer);
+
+      /* Free the memory if necessary.  */
+      if (buffer_malloced)
+	{
+	  free (buffer);
+	  free (wbuffer);
+	}
+    }
 
     if (info->left && width > 0)
       PADN (info->pad, width);
-
-    /* Free the memory if necessary.  */
-    if (buffer_malloced)
-      free (buffer);
   }
   return done;
 }
@@ -1048,8 +1137,7 @@ __printf_fp (FILE *fp,
    into a number with INTDIG_MAX integer digits.  */
 
 unsigned int
-__guess_grouping (unsigned int intdig_max, const char *grouping,
-		  wchar_t sepchar)
+__guess_grouping (unsigned int intdig_max, const char *grouping)
 {
   unsigned int groups;
 
@@ -1087,22 +1175,21 @@ __guess_grouping (unsigned int intdig_max, const char *grouping,
    There is guaranteed enough space past BUFEND to extend it.
    Return the new end of buffer.  */
 
-static char *
+static wchar_t *
 internal_function
-group_number (char *buf, char *bufend, unsigned int intdig_no,
-	      const char *grouping, wchar_t thousands_sep)
+group_number (wchar_t *buf, wchar_t *bufend, unsigned int intdig_no,
+	      const char *grouping, wchar_t thousands_sep, int ngroups)
 {
-  unsigned int groups = __guess_grouping (intdig_no, grouping, thousands_sep);
-  char *p;
+  wchar_t *p;
 
-  if (groups == 0)
+  if (ngroups == 0)
     return bufend;
 
   /* Move the fractional part down.  */
-  memmove (buf + intdig_no + groups, buf + intdig_no,
-	   bufend - (buf + intdig_no));
+  wmemmove (buf + intdig_no + ngroups, buf + intdig_no,
+	    bufend - (buf + intdig_no));
 
-  p = buf + intdig_no + groups - 1;
+  p = buf + intdig_no + ngroups - 1;
   do
     {
       unsigned int len = *grouping++;
@@ -1128,5 +1215,5 @@ group_number (char *buf, char *bufend, unsigned int intdig_no,
     *p-- = buf[--intdig_no];
   while (p > buf);
 
-  return bufend + groups;
+  return bufend + ngroups;
 }
diff --git a/stdio-common/printf_size.c b/stdio-common/printf_size.c
index 654675a0d7..c3da4dcb17 100644
--- a/stdio-common/printf_size.c
+++ b/stdio-common/printf_size.c
@@ -1,5 +1,5 @@
 /* Print size value using units for orders of magnitude.
-   Copyright (C) 1997, 1998, 1999 Free Software Foundation, Inc.
+   Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997.
    Based on a proposal by Larry McVoy <lm@sgi.com>.
@@ -24,26 +24,27 @@
 #include <math.h>
 #include <printf.h>
 #ifdef USE_IN_LIBIO
-#  include <libioP.h>
+# include <libioP.h>
 #else
-#  include <stdio.h>
+# include <stdio.h>
 #endif
 
 
 /* This defines make it possible to use the same code for GNU C library and
    the GNU I/O library.	 */
 #ifdef USE_IN_LIBIO
-#  define PUT(f, s, n) _IO_sputn (f, s, n)
-#  define PAD(f, c, n) _IO_padn (f, c, n)
+# define PUT(f, s, n) _IO_sputn (f, s, n)
+# define PAD(f, c, n) (wide ? _IO_wpadn (f, c, n) : _IO_padn (f, c, n))
 /* We use this file GNU C library and GNU I/O library.	So make
    names equal.	 */
-#  undef putc
-#  define putc(c, f) _IO_putc_unlocked (c, f)
-#  define size_t     _IO_size_t
-#  define FILE	     _IO_FILE
+# undef putc
+# define putc(c, f) (wide \
+		     ? _IO_putwc_unlocked (c, f) : _IO_putc_unlocked (c, f))
+# define size_t	_IO_size_t
+# define FILE	_IO_FILE
 #else	/* ! USE_IN_LIBIO */
-#  define PUT(f, s, n) fwrite (s, 1, n, f)
-#  define PAD(f, c, n) __printf_pad (f, c, n)
+# define PUT(f, s, n) fwrite (s, 1, n, f)
+# define PAD(f, c, n) __printf_pad (f, c, n)
 ssize_t __printf_pad __P ((FILE *, char pad, int n)); /* In vfprintf.c.  */
 #endif	/* USE_IN_LIBIO */
 
@@ -58,21 +59,25 @@ ssize_t __printf_pad __P ((FILE *, char pad, int n)); /* In vfprintf.c.  */
       ++done;								      \
     } while (0)
 
-#define PRINT(ptr, len)							      \
+#define PRINT(ptr, wptr, len)						      \
   do									      \
     {									      \
       register size_t outlen = (len);					      \
       if (len > 20)							      \
 	{								      \
-	  if (PUT (fp, ptr, outlen) != outlen)				      \
+	  if (PUT (fp, wide ? (const char *) wptr : ptr, outlen) != outlen)   \
 	    return -1;							      \
 	  ptr += outlen;						      \
 	  done += outlen;						      \
 	}								      \
       else								      \
 	{								      \
-	  while (outlen-- > 0)						      \
-	    outchar (*ptr++);						      \
+	  if (wide)							      \
+	    while (outlen-- > 0)					      \
+	      outchar (*wptr++);					      \
+	  else								      \
+	    while (outlen-- > 0)					      \
+	      outchar (*ptr++);						      \
 	}								      \
     } while (0)
 
@@ -117,9 +122,11 @@ printf_size (FILE *fp, const struct printf_info *info, const void *const *args)
 
   /* "NaN" or "Inf" for the special cases.  */
   const char *special = NULL;
+  const wchar_t *wspecial = NULL;
 
   struct printf_info fp_info;
   int done = 0;
+  int wide = info->wide;
 
 
   /* Fetch the argument value.	*/
@@ -132,11 +139,13 @@ printf_size (FILE *fp, const struct printf_info *info, const void *const *args)
       if (__isnanl (fpnum.ldbl.d))
 	{
 	  special = "nan";
+	  wspecial = L"nan";
 	  negative = 0;
 	}
       else if (__isinfl (fpnum.ldbl.d))
 	{
 	  special = "inf";
+	  wspecial = L"inf";
 
 	  negative = fpnum.ldbl.d < 0;
 	}
@@ -156,11 +165,13 @@ printf_size (FILE *fp, const struct printf_info *info, const void *const *args)
       if (__isnan (fpnum.dbl.d))
 	{
 	  special = "nan";
+	  wspecial = L"nan";
 	  negative = 0;
 	}
       else if (__isinf (fpnum.dbl.d))
 	{
 	  special = "inf";
+	  wspecial = L"inf";
 
 	  negative = fpnum.dbl.d < 0;
 	}
@@ -174,7 +185,7 @@ printf_size (FILE *fp, const struct printf_info *info, const void *const *args)
 
   if (special)
     {
-      int width = info->prec > info->width ? info->prec : info->width;
+      int width = info->prec > width ? info->prec : width;
 
       if (negative || info->showsign || info->space)
 	--width;
@@ -190,7 +201,7 @@ printf_size (FILE *fp, const struct printf_info *info, const void *const *args)
       else if (info->space)
 	outchar (' ');
 
-      PRINT (special, 3);
+      PRINT (special, wspecial, 3);
 
       if (info->left && width > 0)
 	PADN (' ', width);
@@ -212,7 +223,7 @@ printf_size (FILE *fp, const struct printf_info *info, const void *const *args)
   fp_info.group = info->group;
   fp_info.extra = info->extra;
   fp_info.pad = info->pad;
-  fp_info.wide = 0;
+  fp_info.wide = wide;
 
   if (fp_info.left && fp_info.pad == L' ')
     {
diff --git a/stdio-common/vfscanf.c b/stdio-common/vfscanf.c
index b50e7b8323..6b95352971 100644
--- a/stdio-common/vfscanf.c
+++ b/stdio-common/vfscanf.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 1991,92,93,94,95,96,97,98,99 Free Software Foundation, Inc.
+/* Copyright (C) 1991-1999, 2000 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
    The GNU C Library is free software; you can redistribute it and/or
@@ -62,6 +62,7 @@
 #define GROUP		0x080	/* ': group numbers */
 #define MALLOC		0x100	/* a: malloc strings */
 #define CHAR		0x200	/* hh: char */
+#define I18N		0x400	/* I: use locale's digits */
 
 
 #ifdef USE_IN_LIBIO
@@ -479,8 +480,9 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
 	    }
 	}
 
-      /* Check for the assignment-suppressing and the number grouping flag.  */
-      while (*f == L_('*') || *f == L_('\''))
+      /* Check for the assignment-suppressing, the number grouping flag,
+	 and the signal to use the locale's digit representation.  */
+      while (*f == L_('*') || *f == L_('\'') || *f == L_('I'))
 	switch (*f++)
 	  {
 	  case L_('*'):
@@ -489,6 +491,9 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
 	  case L_('\''):
 	    flags |= GROUP;
 	    break;
+	  case L_('I'):
+	    flags |= I18N;
+	    break;
 	  }
 
       /* We have seen width. */
@@ -1192,22 +1197,137 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
 	  if (base == 0)
 	    base = 10;
 
-	  /* Read the number into workspace.  */
-	  while (c != EOF && width != 0)
+	  if (base == 10 && (flags & I18N) != 0)
 	    {
-	      if (base == 16 ? !ISXDIGIT (c) :
-		  ((!ISDIGIT (c) || c - L_('0') >= base) &&
-		   !((flags & GROUP) && base == 10 && c == thousands)))
-		break;
-	      ADDW (c);
-	      if (width > 0)
-		--width;
+	      int from_level;
+	      int to_level;
+#ifdef COMPILE_WPRINTF
+	      const wchar_t *wcdigits;
+#else
+	      const char *mbdigits[10];
+#endif
+	      int n;
 
-	      c = inchar ();
+	      from_level = 0;
+#ifdef COMPILE_WPRINTF
+	      to_level = _NL_CURRENT_WORD (LC_CTYPE,
+					   _NL_CTYPE_INDIGITS_WC_LEN) - 1;
+#else
+	      to_level = _NL_CURRENT_WORD (LC_CTYPE,
+					   _NL_CTYPE_INDIGITS_MB_LEN) - 1;
+#endif
+
+	      /* In this round we get the pointer to the digit strings
+		 and also perform the first round of comparisons.  */
+	      for (n = 0; n < 10; ++n)
+		{
+		  size_t dlen;
+		  size_t dcnt;
+
+		  /* Get the string for the digits with value N.  */
+#ifdef COMPILE_WPRINTF
+		  wcdigits[n] = _NL_CURRENT (LC_CTYPE,
+					     _NL_CTYPE_INDIGITS0_WC + n);
+		  if (c == *wcdigit[n])
+		    break;
+
+		  /* Advance the pointer to the next string.  */
+		  ++wcdigits[n];
+#else
+		  mbdigits[n] = _NL_CURRENT (LC_CTYPE,
+					     _NL_CTYPE_INDIGITS0_MB + n);
+		  dlen = strlen (mbdigits[n]);
+
+		  dcnt = 0;
+		  do
+		    {
+		      if (c != mbdigits[n][dcnt])
+			break;
+		      c = inchar ();
+		    }
+		  while (--dcnt > 0);
+
+		  if (dcnt == 0)
+		    /* We found it.  */
+		    break;
+
+		  /* Advance the pointer to the next string.  */
+		  mbdigits[n] += dlen + 1;
+		}
+#endif
+
+	      if (n == 10)
+		{
+		  /*Have not yet found the digit.  */
+		  while (++from_level <= to_level)
+		    {
+		      /* Search all ten digits of this level.  */
+		      for (n = 0; n < 10; ++n)
+			{
+#ifdef COMPILE_WPRINTF
+			  if (c == *wcdigit[n])
+			    break;
+
+			  /* Advance the pointer to the next string.  */
+			  ++wcdigits[n];
+#else
+			  size_t dlen = strlen (mbdigits[n]);
+			  size_t dcnt;
+
+			  dcnt = 0;
+			  do
+			    {
+			      if (c != mbdigits[n][dcnt])
+				break;
+			      c = inchar ();
+			    }
+			  while (--dcnt > 0);
+
+			  if (dcnt == 0)
+			    /* We found it.  */
+			    break;
+
+			  /* Advance the pointer to the next string.  */
+			  mbdigits[n] += dlen + 1;
+#endif
+			}
+
+		      if (n < 10)
+			/* Found it.  */
+			break;
+
+		      /* Next level.  */
+		      ++from_level;
+		    }
+		}
+
+	      if (n == 10)
+		{
+		  /* Haven't found anything.  Push the last character back
+		     and return an error.  */
+		  ungetc (c, s);
+		  input_error ();
+		}
+
+	      ADDW (L_('0') + n);
 	    }
+	  else
+	    /* Read the number into workspace.  */
+	    while (c != EOF && width != 0)
+	      {
+		if (base == 16 ? !ISXDIGIT (c) :
+		    ((!ISDIGIT (c) || c - L_('0') >= base) &&
+		     !((flags & GROUP) && base == 10 && c == thousands)))
+		  break;
+		ADDW (c);
+		if (width > 0)
+		  --width;
+
+		c = inchar ();
+	      }
 
-	  if (wpsize == 0 ||
-	      (wpsize == 1 && (wp[0] == L_('+') || wp[0] == L_('-'))))
+	  if (wpsize == 0
+	      || (wpsize == 1 && (wp[0] == L_('+') || wp[0] == L_('-'))))
 	    {
 	      /* There was no number.  If we are supposed to read a pointer
 		 we must recognize "(nil)" as well.  */