about summary refs log tree commit diff
path: root/stdio-common
diff options
context:
space:
mode:
authorJakub Jelinek <jakub@redhat.com>2007-02-21 11:15:54 +0000
committerJakub Jelinek <jakub@redhat.com>2007-02-21 11:15:54 +0000
commitb428b742cf54d423e5a7a68fcbec9473303eeafa (patch)
treea7eb4bb9273b2c48c1d4deacb5458f5075d902ef /stdio-common
parent6c8cc2d3042d0585741452006c29cb21fbba39ea (diff)
downloadglibc-b428b742cf54d423e5a7a68fcbec9473303eeafa.tar.gz
glibc-b428b742cf54d423e5a7a68fcbec9473303eeafa.tar.xz
glibc-b428b742cf54d423e5a7a68fcbec9473303eeafa.zip
Updated to fedora-glibc-20070221T1011 cvs/fedora-glibc-2_5_90-18
Diffstat (limited to 'stdio-common')
-rw-r--r--stdio-common/Makefile2
-rw-r--r--stdio-common/printf_fp.c94
-rw-r--r--stdio-common/tfformat.c15
-rw-r--r--stdio-common/tst-sscanf.c125
-rw-r--r--stdio-common/tst-swscanf.c5
-rw-r--r--stdio-common/vfscanf.c424
6 files changed, 437 insertions, 228 deletions
diff --git a/stdio-common/Makefile b/stdio-common/Makefile
index 98220550f4..709a5730fb 100644
--- a/stdio-common/Makefile
+++ b/stdio-common/Makefile
@@ -54,7 +54,7 @@ tests := tstscanf test_rdwr test-popen tstgetln test-fseek \
 	 tst-swprintf tst-fseek tst-fmemopen test-vfprintf tst-gets \
 	 tst-perror tst-sprintf tst-rndseek tst-fdopen tst-fphex bug14 bug15 \
 	 tst-popen tst-unlockedio tst-fmemopen2 tst-put-error tst-fgets \
-	 tst-fwrite bug16 bug17
+	 tst-fwrite bug16 bug17 tst-swscanf
 
 test-srcs = tst-unbputc tst-printf
 
diff --git a/stdio-common/printf_fp.c b/stdio-common/printf_fp.c
index e4e32f9c28..c27c0d496c 100644
--- a/stdio-common/printf_fp.c
+++ b/stdio-common/printf_fp.c
@@ -1,6 +1,6 @@
 /* Floating point output for `printf'.
-   Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2006
-   Free Software Foundation, Inc.
+   Copyright (C) 1995-2003, 2006, 2007 Free Software Foundation, Inc.
+
    This file is part of the GNU C Library.
    Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995.
 
@@ -811,12 +811,14 @@ ___printf_fp (FILE *fp,
     int chars_needed;
     int expscale;
     int intdig_max, intdig_no = 0;
-    int fracdig_min, fracdig_max, fracdig_no = 0;
+    int fracdig_min;
+    int fracdig_max;
     int dig_max;
     int significant;
     int ngroups = 0;
+    char spec = _tolower (info->spec);
 
-    if (_tolower (info->spec) == 'e')
+    if (spec == 'e')
       {
 	type = info->spec;
 	intdig_max = 1;
@@ -826,7 +828,7 @@ ___printf_fp (FILE *fp,
 	dig_max = INT_MAX;		/* Unlimited.  */
 	significant = 1;		/* Does not matter here.  */
       }
-    else if (_tolower (info->spec) == 'f')
+    else if (spec == 'f')
       {
 	type = 'f';
 	fracdig_min = fracdig_max = info->prec < 0 ? 6 : info->prec;
@@ -887,7 +889,7 @@ ___printf_fp (FILE *fp,
        other output.  If the amount of memory we have to allocate is too
        large use `malloc' instead of `alloca'.  */
     buffer_malloced = ! __libc_use_alloca (chars_needed * 2 * sizeof (wchar_t));
-    if (buffer_malloced)
+    if (__builtin_expect (buffer_malloced, 0))
       {
 	wbuffer = (wchar_t *) malloc ((2 + chars_needed) * sizeof (wchar_t));
 	if (wbuffer == NULL)
@@ -923,7 +925,9 @@ ___printf_fp (FILE *fp,
       }
 
     /* Generate the needed number of fractional digits.	 */
-    while (fracdig_no < fracdig_min
+    int fracdig_no = 0;
+    int added_zeros = 0;
+    while (fracdig_no < fracdig_min + added_zeros
 	   || (fracdig_no < fracdig_max && (fracsize > 1 || frac[0] != 0)))
       {
 	++fracdig_no;
@@ -934,7 +938,7 @@ ___printf_fp (FILE *fp,
 	  {
 	    ++fracdig_max;
 	    if (fracdig_min > 0)
-	      ++fracdig_min;
+	      ++added_zeros;
 	  }
       }
 
@@ -971,11 +975,23 @@ ___printf_fp (FILE *fp,
 	  {
 	    /* Process fractional digits.  Terminate if not rounded or
 	       radix character is reached.  */
+	    int removed = 0;
 	    while (*--wtp != decimalwc && *wtp == L'9')
-	      *wtp = '0';
+	      {
+		*wtp = L'0';
+		++removed;
+	      }
+	    if (removed == fracdig_min && added_zeros > 0)
+	      --added_zeros;
 	    if (*wtp != decimalwc)
 	      /* Round up.  */
 	      (*wtp)++;
+	    else if (__builtin_expect (spec == 'g' && type == 'f' && info->alt,
+				       0))
+	      /* This is a special case: the rounded number is 1.0,
+		 the format is 'g' or 'G', and the alternative format
+		 is selected.  This means the result mist be "1.".  */
+	      --added_zeros;
 	  }
 
 	if (fracdig_no == 0 || *wtp == decimalwc)
@@ -1042,7 +1058,7 @@ ___printf_fp (FILE *fp,
 
   do_expo:
     /* Now remove unnecessary '0' at the end of the string.  */
-    while (fracdig_no > fracdig_min && *(wcp - 1) == L'0')
+    while (fracdig_no > fracdig_min + added_zeros && *(wcp - 1) == L'0')
       {
 	--wcp;
 	--fracdig_no;
@@ -1060,26 +1076,41 @@ ___printf_fp (FILE *fp,
     /* Write the exponent if it is needed.  */
     if (type != 'f')
       {
-	*wcp++ = (wchar_t) type;
-	*wcp++ = expsign ? L'-' : L'+';
+	if (__builtin_expect (expsign != 0 && exponent == 4 && spec == 'g', 0))
+	  {
+	    /* This is another special case.  The exponent of the number is
+	       really smaller than -4, which requires the 'e'/'E' format.
+	       But after rounding the number has an exponent of -4.  */
+	    assert (wcp >= wstartp + 2);
+	    assert (wstartp[0] == L'1');
+	    __wmemcpy (wstartp, L"0.0001", 6);
+	    wstartp[1] = decimalwc;
+	    wmemset (wstartp + 6, L'0', wcp - (wstartp + 2));
+	    wcp += 4;
+	  }
+	else
+	  {
+	    *wcp++ = (wchar_t) type;
+	    *wcp++ = expsign ? L'-' : L'+';
 
-	/* Find the magnitude of the exponent.	*/
-	expscale = 10;
-	while (expscale <= exponent)
-	  expscale *= 10;
+	    /* Find the magnitude of the exponent.	*/
+	    expscale = 10;
+	    while (expscale <= exponent)
+	      expscale *= 10;
 
-	if (exponent < 10)
-	  /* Exponent always has at least two digits.  */
-	  *wcp++ = L'0';
-	else
-	  do
-	    {
-	      expscale /= 10;
-	      *wcp++ = L'0' + (exponent / expscale);
-	      exponent %= expscale;
-	    }
-	  while (expscale > 10);
-	*wcp++ = L'0' + exponent;
+	    if (exponent < 10)
+	      /* Exponent always has at least two digits.  */
+	      *wcp++ = L'0';
+	    else
+	      do
+		{
+		  expscale /= 10;
+		  *wcp++ = L'0' + (exponent / expscale);
+		  exponent %= expscale;
+		}
+	      while (expscale > 10);
+	    *wcp++ = L'0' + exponent;
+	  }
       }
 
     /* Compute number of characters which must be filled with the padding
@@ -1120,15 +1151,14 @@ ___printf_fp (FILE *fp,
 	  else
 	    thousands_sep_len = strlen (thousands_sep);
 
-	  if (buffer_malloced)
+	  if (__builtin_expect (buffer_malloced, 0))
 	    {
 	      buffer = (char *) malloc (2 + chars_needed + decimal_len
 					+ ngroups * thousands_sep_len);
 	      if (buffer == NULL)
 		{
 		  /* Signal an error to the caller.  */
-		  if (buffer_malloced)
-		    free (wbuffer);
+		  free (wbuffer);
 		  return -1;
 		}
 	    }
@@ -1162,7 +1192,7 @@ ___printf_fp (FILE *fp,
       PRINT (tmpptr, wstartp, wide ? wcp - wstartp : cp - tmpptr);
 
       /* Free the memory if necessary.  */
-      if (buffer_malloced)
+      if (__builtin_expect (buffer_malloced, 0))
 	{
 	  free (buffer);
 	  free (wbuffer);
diff --git a/stdio-common/tfformat.c b/stdio-common/tfformat.c
index ea7365b2c9..0370834e9f 100644
--- a/stdio-common/tfformat.c
+++ b/stdio-common/tfformat.c
@@ -4012,6 +4012,14 @@ sprint_double_type sprint_doubles[] =
   {__LINE__, 16,			"0x1.0p+4", "%.1a"},
   {__LINE__, 16,			"0x1.00000000000000000000p+4", "%.20a"},
   {__LINE__, 4444.88888888,		"4445", "%2.F"},
+  {__LINE__, 0.956,			"1", "%.0g"},
+  {__LINE__, 1.0956,			"1.", "%#.0g"},
+  {__LINE__, 0.956,			"1.", "%#.0g"},
+  {__LINE__, 0.0956,			"0.1", "%#.0g"},
+  {__LINE__, 0.00956,			"0.01", "%#.0g"},
+  {__LINE__, 0.000956,			"0.001", "%#.0g"},
+  {__LINE__, 0.000098,			"0.0001", "%#.0g"},
+  {__LINE__, 0.0000996,			"0.00010", "%#.2g"},
 
   {0 }
 
@@ -4023,13 +4031,8 @@ sprint_double_type sprint_doubles[] =
 
 int required_precision = 13;
 
-#if defined(__STDC__) || defined(__cplusplus)
 static int
 matches (register char *result, register const char *desired)
-#else
-int matches(result, desired)
-     register char *result; register const char *desired;
-#endif
 {
     int digits_seen = 0;
     for (;; result++, desired++) {
@@ -4080,7 +4083,7 @@ int main(int argc, char *argv[])
 
   /* And one special test.  */
   {
-    const char ref[] = "1.7763568394002504646778106689453125e-15";
+    static const char ref[] = "1.7763568394002504646778106689453125e-15";
     int i;
     d = 1.0;
     for (i = 1; i < 50; ++i)
diff --git a/stdio-common/tst-sscanf.c b/stdio-common/tst-sscanf.c
index e710e190d7..a987e87797 100644
--- a/stdio-common/tst-sscanf.c
+++ b/stdio-common/tst-sscanf.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2000, 2002, 2004 Free Software Foundation, Inc.
+/* Copyright (C) 2000, 2002, 2004, 2007 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Jakub Jelinek <jakub@redhat.com>, 2000.
 
@@ -21,12 +21,18 @@
 #include <stdio.h>
 #include <locale.h>
 
-const char *str_double[] =
+#ifndef CHAR
+# define CHAR char
+# define L(str) str
+# define SSCANF sscanf
+#endif
+
+const CHAR *str_double[] =
 {
-  "-.10000E+020.20000E+020.25000E+010.40000E+010.50000E+010.12500E+01",
-  "0.10000E+020.20000E+020.25000E+010.40000E+010.50000E+010.12500E+01",
-  "-1234567E0198765432E0912345678901987654321091234567890198765432109",
-  "-0.1000E+020.20000E+020.25000E+010.40000E+010.50000E+010.12500E+01"
+  L("-.10000E+020.20000E+020.25000E+010.40000E+010.50000E+010.12500E+01"),
+  L("0.10000E+020.20000E+020.25000E+010.40000E+010.50000E+010.12500E+01"),
+  L("-1234567E0198765432E0912345678901987654321091234567890198765432109"),
+  L("-0.1000E+020.20000E+020.25000E+010.40000E+010.50000E+010.12500E+01")
 };
 
 const double val_double[] =
@@ -38,20 +44,20 @@ const double val_double[] =
   -0.1000E+02, 0.20000E+02, 0.25000E+01, 0.40000E+01, 0.50000E+01, 0.12500E+01
 };
 
-const char *str_long[] =
+const CHAR *str_long[] =
 {
-  "-12345678987654321123456789987654321123456789987654321",
-  "-12345678987654321123456789987654321123456789987654321",
-  "-12,345,678987,654,321123,456,789987,654,321123,456,789987,654,321",
-  "-12,345,678987,654,321123,456,789987,654,321123,456,789987,654,321"
+  L("-12345678987654321123456789987654321123456789987654321"),
+  L("-12345678987654321123456789987654321123456789987654321"),
+  L("-12,345,678987,654,321123,456,789987,654,321123,456,789987,654,321"),
+  L("-12,345,678987,654,321123,456,789987,654,321123,456,789987,654,321")
 };
 
-const char *fmt_long[] =
+const CHAR *fmt_long[] =
 {
-  "%9ld%9ld%9ld%9ld%9ld%9ld",
-  "%I9ld%I9ld%I9ld%I9ld%I9ld%I9ld",
-  "%'11ld%'11ld%'11ld%'11ld%'11ld%'11ld",
-  "%I'11ld%I'11ld%I'11ld%I'11ld%I'11ld%I'11ld"
+  L("%9ld%9ld%9ld%9ld%9ld%9ld"),
+  L("%I9ld%I9ld%I9ld%I9ld%I9ld%I9ld"),
+  L("%'11ld%'11ld%'11ld%'11ld%'11ld%'11ld"),
+  L("%I'11ld%I'11ld%I'11ld%I'11ld%I'11ld%I'11ld")
 };
 
 const long int val_long[] =
@@ -59,38 +65,49 @@ const long int val_long[] =
   -12345678, 987654321, 123456789, 987654321, 123456789, 987654321
 };
 
-struct int_test
+struct test
 {
-  const char *str;
-  const char *fmt;
+  const CHAR *str;
+  const CHAR *fmt;
   int retval;
-} int_tests[] = 
+} int_tests[] =
 {
-  { "foo\n", "foo\nbar", -1 },
-  { "foo\n", "foo bar", -1 },
-  { "foo\n", "foo %d", -1 },
-  { "foo\n", "foo\n%d", -1 },
-  { "foon", "foonbar", -1 },
-  { "foon", "foon%d", -1 },
-  { "foo ", "foo bar", -1 },
-  { "foo ", "foo %d", -1 },
-  { "foo\t", "foo\tbar", -1 },
-  { "foo\t", "foo bar", -1 },
-  { "foo\t", "foo %d", -1 },
-  { "foo\t", "foo\t%d", -1 },
-  { "foo", "foo", 0 },
-  { "foon", "foo bar", 0 },
-  { "foon", "foo %d", 0 },
-  { "foo ", "fooxbar", 0 },
-  { "foo ", "foox%d", 0 },
-  { "foo bar", "foon", 0 },
-  { "foo bar", "foo bar", 0 },
-  { "foo bar", "foo %d", 0 },
-  { "foo bar", "foon%d", 0 },
-  { "foo ", "foo %n", 0 },
-  { "foo%bar1", "foo%%bar%d", 1 },
+  { L("foo\n"), L("foo\nbar"), -1 },
+  { L("foo\n"), L("foo bar"), -1 },
+  { L("foo\n"), L("foo %d"), -1 },
+  { L("foo\n"), L("foo\n%d"), -1 },
+  { L("foon"), L("foonbar"), -1 },
+  { L("foon"), L("foon%d"), -1 },
+  { L("foo "), L("foo bar"), -1 },
+  { L("foo "), L("foo %d"), -1 },
+  { L("foo\t"), L("foo\tbar"), -1 },
+  { L("foo\t"), L("foo bar"), -1 },
+  { L("foo\t"), L("foo %d"), -1 },
+  { L("foo\t"), L("foo\t%d"), -1 },
+  { L("foo"), L("foo"), 0 },
+  { L("foon"), L("foo bar"), 0 },
+  { L("foon"), L("foo %d"), 0 },
+  { L("foo "), L("fooxbar"), 0 },
+  { L("foo "), L("foox%d"), 0 },
+  { L("foo bar"), L("foon"), 0 },
+  { L("foo bar"), L("foo bar"), 0 },
+  { L("foo bar"), L("foo %d"), 0 },
+  { L("foo bar"), L("foon%d"), 0 },
+  { L("foo "), L("foo %n"), 0 },
+  { L("foo%bar1"), L("foo%%bar%d"), 1 },
   /* Some OSes skip whitespace here while others don't.  */
-  { "foo \t %bar1", "foo%%bar%d", 1 }
+  { L("foo \t %bar1"), L("foo%%bar%d"), 1 }
+};
+
+struct test double_tests[] =
+{
+  { L("-1"), L("%1g"), 0 },
+  { L("-.1"), L("%2g"), 0 },
+  { L("-inf"), L("%3g"), 0 },
+  { L("+0"), L("%1g"),  },
+  { L("-0x1p0"), L("%2g"), 1 },
+  { L("-..1"), L("%g"), 0 },
+  { L("-inf"), L("%g"), 1 }
 };
 
 int
@@ -112,7 +129,7 @@ main (void)
 
   for (i = 0; i < 4; ++i)
     {
-      if (sscanf (str_double[i], "%11lf%11lf%11lf%11lf%11lf%11lf",
+      if (SSCANF (str_double[i], L("%11lf%11lf%11lf%11lf%11lf%11lf"),
 		  &d[0], &d[1], &d[2], &d[3], &d[4], &d[5]) != 6)
 	{
 	  printf ("Double sscanf test %d wrong number of "
@@ -132,7 +149,7 @@ main (void)
 
   for (i = 0; i < 4; ++i)
     {
-      if (sscanf (str_long[i], fmt_long[i],
+      if (SSCANF (str_long[i], fmt_long[i],
 		  &l[0], &l[1], &l[2], &l[3], &l[4], &l[5]) != 6)
 	{
 	  printf ("Integer sscanf test %d wrong number of "
@@ -157,7 +174,7 @@ main (void)
     {
       int dummy, ret;
 
-      if ((ret = sscanf (int_tests[i].str, int_tests[i].fmt,
+      if ((ret = SSCANF (int_tests[i].str, int_tests[i].fmt,
 			 &dummy)) != int_tests[i].retval)
 	{
 	  printf ("int_tests[%d] returned %d != %d\n",
@@ -166,5 +183,19 @@ main (void)
 	}
     }
 
+  for (i = 0; i < sizeof (double_tests) / sizeof (double_tests[0]); ++i)
+    {
+      double dummy;
+      int ret;
+
+      if ((ret = SSCANF (double_tests[i].str, double_tests[i].fmt,
+			 &dummy)) != double_tests[i].retval)
+	{
+	  printf ("double_tests[%d] returned %d != %d\n",
+		  i, ret, double_tests[i].retval);
+	  result = 1;
+	}
+    }
+
   return result;
 }
diff --git a/stdio-common/tst-swscanf.c b/stdio-common/tst-swscanf.c
new file mode 100644
index 0000000000..6ec1c8e6c7
--- /dev/null
+++ b/stdio-common/tst-swscanf.c
@@ -0,0 +1,5 @@
+#define CHAR wchar_t
+#define L(str) L##str
+#define SSCANF swscanf
+#include <wchar.h>
+#include "tst-sscanf.c"
diff --git a/stdio-common/vfscanf.c b/stdio-common/vfscanf.c
index 1ea9bc4e07..0daf4ae0dd 100644
--- a/stdio-common/vfscanf.c
+++ b/stdio-common/vfscanf.c
@@ -1,5 +1,4 @@
-/* Copyright (C) 1991-2002, 2003, 2004, 2005, 2006
-   Free Software Foundation, Inc.
+/* Copyright (C) 1991-2006, 2007 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
@@ -53,17 +52,19 @@
 #endif
 
 /* Those are flags in the conversion format. */
-#define LONG		0x001	/* l: long or double */
-#define LONGDBL		0x002	/* L: long long or long double */
-#define SHORT		0x004	/* h: short */
-#define SUPPRESS	0x008	/* *: suppress assignment */
-#define POINTER		0x010	/* weird %p pointer (`fake hex') */
-#define NOSKIP		0x020	/* do not skip blanks */
-#define WIDTH		0x040	/* width was given */
-#define GROUP		0x080	/* ': group numbers */
-#define MALLOC		0x100	/* a: malloc strings */
-#define CHAR		0x200	/* hh: char */
-#define I18N		0x400	/* I: use locale's digits */
+#define LONG		0x0001	/* l: long or double */
+#define LONGDBL		0x0002	/* L: long long or long double */
+#define SHORT		0x0004	/* h: short */
+#define SUPPRESS	0x0008	/* *: suppress assignment */
+#define POINTER		0x0010	/* weird %p pointer (`fake hex') */
+#define NOSKIP		0x0020	/* do not skip blanks */
+#define NUMBER_SIGNED	0x0040	/* signed integer */
+#define GROUP		0x0080	/* ': group numbers */
+#define MALLOC		0x0100	/* a: malloc strings */
+#define CHAR		0x0200	/* hh: char */
+#define I18N		0x0400	/* I: use locale's digits */
+#define HEXA_FLOAT	0x0800	/* hexadecimal float */
+#define READ_POINTER	0x1000	/* this is a pointer value */
 
 
 #include <locale/localeinfo.h>
@@ -205,9 +206,6 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
 #define exp_char not_in
   /* Base for integral numbers.  */
   int base;
-  /* Signedness for integral numbers.  */
-  int number_signed;
-#define is_hexa number_signed
   /* Decimal point character.  */
 #ifdef COMPILE_WSCANF
   wint_t decimal;
@@ -239,8 +237,6 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
      possibly be matched even if in the input stream no character is
      available anymore.  */
   int skip_space = 0;
-  /* Nonzero if we are reading a pointer.  */
-  int read_pointer;
   /* Workspace.  */
   CHAR_T *tw;			/* Temporary pointer.  */
   CHAR_T *wp = NULL;		/* Workspace.  */
@@ -403,9 +399,6 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
       /* This is the start of the conversion string. */
       flags = 0;
 
-      /* Not yet decided whether we read a pointer or not.  */
-      read_pointer = 0;
-
       /* Initialize state of modifiers.  */
       argpos = 0;
 
@@ -424,7 +417,6 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
 	    {
 	      /* Oops; that was actually the field width.  */
 	      width = argpos;
-	      flags |= WIDTH;
 	      argpos = 0;
 	      goto got_width;
 	    }
@@ -439,17 +431,18 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
 	    flags |= SUPPRESS;
 	    break;
 	  case L_('\''):
-	    flags |= GROUP;
+#ifdef COMPILE_WSCANF
+	    if (thousands != L'\0')
+#else
+	    if (thousands != NULL)
+#endif
+	      flags |= GROUP;
 	    break;
 	  case L_('I'):
 	    flags |= I18N;
 	    break;
 	  }
 
-      /* We have seen width. */
-      if (ISDIGIT ((UCHAR_T) *f))
-	flags |= WIDTH;
-
       /* Find the maximum field width.  */
       width = 0;
       while (ISDIGIT ((UCHAR_T) *f))
@@ -1083,27 +1076,24 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
 	case L_('x'):	/* Hexadecimal integer.  */
 	case L_('X'):	/* Ditto.  */
 	  base = 16;
-	  number_signed = 0;
 	  goto number;
 
 	case L_('o'):	/* Octal integer.  */
 	  base = 8;
-	  number_signed = 0;
 	  goto number;
 
 	case L_('u'):	/* Unsigned decimal integer.  */
 	  base = 10;
-	  number_signed = 0;
 	  goto number;
 
 	case L_('d'):	/* Signed decimal integer.  */
 	  base = 10;
-	  number_signed = 1;
+	  flags |= NUMBER_SIGNED;
 	  goto number;
 
 	case L_('i'):	/* Generic number.  */
 	  base = 0;
-	  number_signed = 1;
+	  flags |= NUMBER_SIGNED;
 
 	number:
 	  c = inchar ();
@@ -1270,13 +1260,13 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
 			mbdigits[n] = strchr (mbdigits[n], '\0') + 1;
 
 		      cmpp = mbdigits[n];
-		      while ((unsigned char) *cmpp == c && avail > 0)
+		      while ((unsigned char) *cmpp == c && avail >= 0)
 			{
 			  if (*++cmpp == '\0')
 			    break;
 			  else
 			    {
-			      if ((c = inchar ()) == EOF)
+			      if (avail == 0 || inchar () == EOF)
 				break;
 			      --avail;
 			    }
@@ -1323,13 +1313,13 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
 			      int avail = width > 0 ? width : INT_MAX;
 
 			      cmpp = mbdigits[n];
-			      while ((unsigned char) *cmpp == c && avail > 0)
+			      while ((unsigned char) *cmpp == c && avail >= 0)
 				{
 				  if (*++cmpp == '\0')
 				    break;
 				  else
 				    {
-				      if ((c = inchar ()) == EOF)
+				      if (avail == 0 || inchar () == EOF)
 					break;
 				      --avail;
 				    }
@@ -1368,13 +1358,7 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
 
 		  if (n < 10)
 		    c = L_('0') + n;
-		  else if ((flags & GROUP)
-#ifdef COMPILE_WSCANF
-			   && thousands != L'\0'
-#else
-			   && thousands != NULL
-#endif
-			   )
+		  else if (flags & GROUP)
 		    {
 		      /* Try matching against the thousands separator.  */
 #ifdef COMPILE_WSCANF
@@ -1384,14 +1368,14 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
 		      const char *cmpp = thousands;
 		      int avail = width > 0 ? width : INT_MAX;
 
-		      while ((unsigned char) *cmpp == c && avail > 0)
+		      while ((unsigned char) *cmpp == c && avail >= 0)
 			{
 			  ADDW (c);
 			  if (*++cmpp == '\0')
 			    break;
 			  else
 			    {
-			      if ((c = inchar ()) == EOF)
+			      if (avail == 0 || inchar () == EOF)
 				break;
 			      --avail;
 			    }
@@ -1440,13 +1424,7 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
 		  }
 		else if (!ISDIGIT (c) || (int) (c - L_('0')) >= base)
 		  {
-		    if (base == 10 && (flags & GROUP)
-#ifdef COMPILE_WSCANF
-			&& thousands != L'\0'
-#else
-			&& thousands != NULL
-#endif
-			)
+		    if (base == 10 && (flags & GROUP))
 		      {
 			/* Try matching against the thousands separator.  */
 #ifdef COMPILE_WSCANF
@@ -1456,14 +1434,14 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
 			const char *cmpp = thousands;
 			int avail = width > 0 ? width : INT_MAX;
 
-			while ((unsigned char) *cmpp == c && avail > 0)
+			while ((unsigned char) *cmpp == c && avail >= 0)
 			  {
 			    ADDW (c);
 			    if (*++cmpp == '\0')
 			      break;
 			    else
 			      {
-				if ((c = inchar ()) == EOF)
+				if (avail == 0 || inchar () == EOF)
 				  break;
 				--avail;
 			      }
@@ -1507,7 +1485,7 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
 	      /* There was no number.  If we are supposed to read a pointer
 		 we must recognize "(nil)" as well.  */
 	      if (__builtin_expect (wpsize == 0
-				    && read_pointer
+				    && (flags & READ_POINTER)
 				    && (width < 0 || width >= 0)
 				    && c == '('
 				    && TOLOWER (inchar ()) == L_('n')
@@ -1534,14 +1512,14 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
 	  ADDW (L_('\0'));
 	  if (need_longlong && (flags & LONGDBL))
 	    {
-	      if (number_signed)
+	      if (flags & NUMBER_SIGNED)
 		num.q = __strtoll_internal (wp, &tw, base, flags & GROUP);
 	      else
 		num.uq = __strtoull_internal (wp, &tw, base, flags & GROUP);
 	    }
 	  else
 	    {
-	      if (number_signed)
+	      if (flags & NUMBER_SIGNED)
 		num.l = __strtol_internal (wp, &tw, base, flags & GROUP);
 	      else
 		num.ul = __strtoul_internal (wp, &tw, base, flags & GROUP);
@@ -1551,32 +1529,32 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
 
 	  if (!(flags & SUPPRESS))
 	    {
-	      if (! number_signed)
+	      if (flags & NUMBER_SIGNED)
 		{
 		  if (need_longlong && (flags & LONGDBL))
-		    *ARG (unsigned LONGLONG int *) = num.uq;
+		    *ARG (LONGLONG int *) = num.q;
 		  else if (need_long && (flags & LONG))
-		    *ARG (unsigned long int *) = num.ul;
+		    *ARG (long int *) = num.l;
 		  else if (flags & SHORT)
-		    *ARG (unsigned short int *)
-		      = (unsigned short int) num.ul;
+		    *ARG (short int *) = (short int) num.l;
 		  else if (!(flags & CHAR))
-		    *ARG (unsigned int *) = (unsigned int) num.ul;
+		    *ARG (int *) = (int) num.l;
 		  else
-		    *ARG (unsigned char *) = (unsigned char) num.ul;
+		    *ARG (signed char *) = (signed char) num.ul;
 		}
 	      else
 		{
 		  if (need_longlong && (flags & LONGDBL))
-		    *ARG (LONGLONG int *) = num.q;
+		    *ARG (unsigned LONGLONG int *) = num.uq;
 		  else if (need_long && (flags & LONG))
-		    *ARG (long int *) = num.l;
+		    *ARG (unsigned long int *) = num.ul;
 		  else if (flags & SHORT)
-		    *ARG (short int *) = (short int) num.l;
+		    *ARG (unsigned short int *)
+		      = (unsigned short int) num.ul;
 		  else if (!(flags & CHAR))
-		    *ARG (int *) = (int) num.l;
+		    *ARG (unsigned int *) = (unsigned int) num.ul;
 		  else
-		    *ARG (signed char *) = (signed char) num.ul;
+		    *ARG (unsigned char *) = (unsigned char) num.ul;
 		}
 	      ++done;
 	    }
@@ -1591,6 +1569,8 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
 	case L_('a'):
 	case L_('A'):
 	  c = inchar ();
+	  if (width > 0)
+	    --width;
 	  if (__builtin_expect (c == EOF, 0))
 	    input_error ();
 
@@ -1603,63 +1583,6 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
 	      if (__builtin_expect (width == 0 || inchar () == EOF, 0))
 		/* EOF is only an input error before we read any chars.  */
 		conv_error ();
-	      if (! ISDIGIT (c) && TOLOWER (c) != L_('i')
-		  && TOLOWER (c) != L_('n'))
-		{
-#ifdef COMPILE_WSCANF
-		  if (__builtin_expect (c != decimal, 0))
-		    {
-		      /* This is no valid number.  */
-		      ungetc (c, s);
-		      conv_error ();
-		    }
-#else
-		  /* Match against the decimal point.  At this point
-                     we are taking advantage of the fact that we can
-                     push more than one character back.  This is
-                     (almost) never necessary since the decimal point
-                     string hopefully never contains more than one
-                     byte.  */
-		  const char *cmpp = decimal;
-		  int avail = width > 0 ? width : INT_MAX;
-
-		  while ((unsigned char) *cmpp == c && avail-- > 0)
-		    if (*++cmpp == '\0')
-		      break;
-		    else
-		      {
-			if (inchar () == EOF)
-			  break;
-		      }
-
-		  if (__builtin_expect (*cmpp != '\0', 0))
-		    {
-		      /* This is no valid number.  */
-		      while (1)
-			{
-			  ungetc (c, s);
-			  if (cmpp == decimal)
-			    break;
-			  c = (unsigned char) *--cmpp;
-			}
-
-		      conv_error ();
-		    }
-		  else
-		    {
-                     /* Add all the characters.  */
-                     for (cmpp = decimal; *cmpp != '\0'; ++cmpp)
-                       ADDW ((unsigned char) *cmpp);
-                     if (width > 0)
-                       width = avail;
-                     got_dot = 1;
-
-		      c = inchar ();
-		    }
-		  if (width > 0)
-		    width = avail;
-#endif
-		}
 	      if (width > 0)
 		--width;
 	    }
@@ -1751,7 +1674,6 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
 	      goto scan_float;
 	    }
 
-	  is_hexa = 0;
 	  exp_char = L_('e');
 	  if (width != 0 && c == L_('0'))
 	    {
@@ -1764,7 +1686,7 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
 		  /* It is a number in hexadecimal format.  */
 		  ADDW (c);
 
-		  is_hexa = 1;
+		  flags |= HEXA_FLOAT;
 		  exp_char = L_('p');
 
 		  /* Grouping is not allowed.  */
@@ -1775,11 +1697,11 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
 		}
 	    }
 
-	  do
+	  while (1)
 	    {
 	      if (ISDIGIT (c))
 		ADDW (c);
-	      else if (!got_e && is_hexa && ISXDIGIT (c))
+	      else if (!got_e && (flags & HEXA_FLOAT) && ISXDIGIT (c))
 		ADDW (c);
 	      else if (got_e && wp[wpsize - 1] == exp_char
 		       && (c == L_('-') || c == L_('+')))
@@ -1798,8 +1720,7 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
 		      ADDW (c);
 		      got_dot = 1;
 		    }
-		  else if ((flags & GROUP) != 0 && thousands != L'\0'
-			   && ! got_dot && c == thousands)
+		  else if ((flags & GROUP) != 0 && ! got_dot && c == thousands)
 		    ADDW (c);
 		  else
 		    {
@@ -1814,12 +1735,12 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
 
 		  if (! got_dot)
 		    {
-		      while ((unsigned char) *cmpp == c && avail > 0)
+		      while ((unsigned char) *cmpp == c && avail >= 0)
 			if (*++cmpp == '\0')
 			  break;
 			else
 			  {
-			    if (inchar () == EOF)
+			    if (avail == 0 || inchar () == EOF)
 			      break;
 			    --avail;
 			  }
@@ -1843,20 +1764,19 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
 			 we can compare against it.  */
 		      const char *cmp2p = thousands;
 
-		      if ((flags & GROUP) != 0 && thousands != NULL
-			  && ! got_dot)
+		      if ((flags & GROUP) != 0 && ! got_dot)
 			{
 			  while (cmp2p - thousands < cmpp - decimal
 				 && *cmp2p == decimal[cmp2p - thousands])
 			    ++cmp2p;
 			  if (cmp2p - thousands == cmpp - decimal)
 			    {
-			      while ((unsigned char) *cmp2p == c && avail > 0)
+			      while ((unsigned char) *cmp2p == c && avail >= 0)
 				if (*++cmp2p == '\0')
 				  break;
 				else
 				  {
-				    if (inchar () == EOF)
+				    if (avail == 0 || inchar () == EOF)
 				      break;
 				    --avail;
 				  }
@@ -1881,16 +1801,237 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
 		    }
 #endif
 		}
+
+	      if (width == 0 || inchar () == EOF)
+		break;
+
 	      if (width > 0)
 		--width;
 	    }
-	  while (width != 0 && inchar () != EOF);
+
+	  wctrans_t map;
+	  if (__builtin_expect ((flags & I18N) != 0, 0)
+	      /* Hexadecimal floats make no sense, fixing localized
+		 digits with ASCII letters.  */
+	      && !(flags & HEXA_FLOAT)
+	      /* Minimum requirement.  */
+	      && (wpsize == 0 || got_dot)
+	      && (map = __wctrans ("to_inpunct")) != NULL)
+	    {
+	      /* Reget the first character.  */
+	      inchar ();
+
+	      /* Localized digits, decimal points, and thousands
+		 separator.  */
+	      wint_t wcdigits[12];
+
+	      /* First get decimal equivalent to check if we read it
+		 or not.  */
+	      wcdigits[11] = __towctrans (L'.', map);
+
+	      /* If we have not read any character or have just read
+	         locale decimal point which matches the decimal point
+	         for localized FP numbers, then we may have localized
+	         digits.  Note, we test GOT_DOT above.  */
+#ifdef COMPILE_WSCANF
+	      if (wpsize == 0 || (wpsize == 1 && wcdigits[11] == decimal))
+#else
+	      char mbdigits[12][MB_LEN_MAX + 1];
+
+	      mbstate_t state;
+	      memset (&state, '\0', sizeof (state));
+
+	      bool match_so_far = wpsize == 0;
+	      size_t mblen = __wcrtomb (mbdigits[11], wcdigits[11], &state);
+	      if (mblen != (size_t) -1)
+		{
+		  mbdigits[11][mblen] = '\0';
+		  match_so_far |= (wpsize == strlen (decimal)
+				   && strcmp (decimal, mbdigits[11]) == 0);
+		}
+	      else
+		{
+		  size_t decimal_len = strlen (decimal);
+		  /* This should always be the case but the data comes
+		     from a file.  */
+		  if (decimal_len <= MB_LEN_MAX)
+		    {
+		      match_so_far |= wpsize == decimal_len;
+		      memcpy (mbdigits[11], decimal, decimal_len + 1);
+		    }
+		  else
+		    match_so_far = false;
+		}
+
+	      if (match_so_far)
+#endif
+		{
+		  bool have_locthousands = (flags & GROUP) != 0;
+
+		  /* Now get the digits and the thousands-sep equivalents.  */
+	          for (int n = 0; n < 11; ++n)
+		    {
+		      if (n < 10)
+			wcdigits[n] = __towctrans (L'0' + n, map);
+		      else if (n == 10)
+			{
+			  wcdigits[10] = __towctrans (L',', map);
+			  have_locthousands &= wcdigits[10] != L'\0';
+			}
+
+#ifndef COMPILE_WSCANF
+		      memset (&state, '\0', sizeof (state));
+
+		      size_t mblen = __wcrtomb (mbdigits[n], wcdigits[n],
+						&state);
+		      if (mblen == (size_t) -1)
+			{
+			  if (n == 10)
+			    {
+			      if (have_locthousands)
+				{
+				  size_t thousands_len = strlen (thousands);
+				  if (thousands_len <= MB_LEN_MAX)
+				    memcpy (mbdigits[10], thousands,
+					    thousands_len + 1);
+				  else
+				    have_locthousands = false;
+				}
+			    }
+			  else
+			    /* Ignore checking against localized digits.  */
+			    goto no_i18nflt;
+			}
+		      else
+			mbdigits[n][mblen] = '\0';
+#endif
+		    }
+
+		  /* Start checking against localized digits, if
+		     convertion is done correctly. */
+		  while (1)
+		    {
+		      if (got_e && wp[wpsize - 1] == exp_char
+			  && (c == L_('-') || c == L_('+')))
+			ADDW (c);
+		      else if (wpsize > 0 && !got_e
+			       && (CHAR_T) TOLOWER (c) == exp_char)
+			{
+			  ADDW (exp_char);
+			  got_e = got_dot = 1;
+			}
+		      else
+			{
+			  /* Check against localized digits, decimal point,
+			     and thousands separator.  */
+			  int n;
+			  for (n = 0; n < 12; ++n)
+			    {
+#ifdef COMPILE_WSCANF
+			      if (c == wcdigits[n])
+				{
+				  if (n < 10)
+				    ADDW (L_('0') + n);
+				  else if (n == 11 && !got_dot)
+				    {
+				      ADDW (decimal);
+				      got_dot = 1;
+				    }
+				  else if (n == 10 && have_locthousands
+					   && ! got_dot)
+				    ADDW (thousands);
+				  else
+				    /* The last read character is not part
+				       of the number anymore.  */
+				    n = 12;
+
+				  break;
+				}
+#else
+			      const char *cmpp = mbdigits[n];
+			      int avail = width > 0 ? width : INT_MAX;
+
+			      while ((unsigned char) *cmpp == c && avail >= 0)
+				if (*++cmpp == '\0')
+				  break;
+				else
+				  {
+				    if (avail == 0 || inchar () == EOF)
+				      break;
+				    --avail;
+				  }
+			      if (*cmpp == '\0')
+				{
+				  if (width > 0)
+				    width = avail;
+
+				  if (n < 10)
+				    ADDW (L_('0') + n);
+				  else if (n == 11 && !got_dot)
+				    {
+				      /* Add all the characters.  */
+				      for (cmpp = decimal; *cmpp != '\0';
+					   ++cmpp)
+					ADDW ((unsigned char) *cmpp);
+
+				      got_dot = 1;
+				    }
+				  else if (n == 10 && (flags & GROUP) != 0
+					   && ! got_dot)
+				    {
+				      /* Add all the characters.  */
+				      for (cmpp = thousands; *cmpp != '\0';
+					   ++cmpp)
+					ADDW ((unsigned char) *cmpp);
+				    }
+				  else
+				    /* The last read character is not part
+				       of the number anymore.  */
+				      n = 12;
+
+				  break;
+				}
+
+			      /* We are pushing all read characters back.  */
+			      if (cmpp > mbdigits[n])
+				{
+				  ungetc (c, s);
+				  while (--cmpp > mbdigits[n])
+				    ungetc_not_eof ((unsigned char) *cmpp, s);
+				  c = (unsigned char) *cmpp;
+				}
+#endif
+			    }
+
+			  if (n >= 12)
+			    {
+			      /* The last read character is not part
+				 of the number anymore.  */
+			      ungetc (c, s);
+			      break;
+			    }
+			}
+
+		      if (width == 0 || inchar () == EOF)
+			break;
+
+		      if (width > 0)
+			--width;
+		    }
+		}
+
+#ifndef COMPILE_WSCANF
+	    no_i18nflt:
+	      ;
+#endif
+	    }
 
 	  /* Have we read any character?  If we try to read a number
 	     in hexadecimal notation and we have read only the `0x'
 	     prefix or no exponent this is an error.  */
 	  if (__builtin_expect (wpsize == 0
-				|| (is_hexa && (wpsize == 2 || ! got_e)), 0))
+				|| ((flags & HEXA_FLOAT)
+				    && (wpsize == 2 || ! got_e)), 0))
 	    conv_error ();
 
 	scan_float:
@@ -2429,8 +2570,7 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
 	  flags &= ~(SHORT|LONGDBL);
 	  if (need_long)
 	    flags |= LONG;
-	  number_signed = 0;
-	  read_pointer = 1;
+	  flags |= READ_POINTER;
 	  goto number;
 
 	default: