summary refs log tree commit diff
path: root/stdio-common
diff options
context:
space:
mode:
Diffstat (limited to 'stdio-common')
-rw-r--r--stdio-common/Makefile26
-rw-r--r--stdio-common/Versions4
-rw-r--r--stdio-common/isoc99_fscanf.c40
-rw-r--r--stdio-common/isoc99_scanf.c42
-rw-r--r--stdio-common/isoc99_sscanf.c37
-rw-r--r--stdio-common/isoc99_vfscanf.c35
-rw-r--r--stdio-common/isoc99_vscanf.c34
-rw-r--r--stdio-common/isoc99_vsscanf.c47
-rw-r--r--stdio-common/scanf13.c187
-rw-r--r--stdio-common/scanf14.c63
-rw-r--r--stdio-common/scanf15.c54
-rw-r--r--stdio-common/vfscanf.c273
12 files changed, 784 insertions, 58 deletions
diff --git a/stdio-common/Makefile b/stdio-common/Makefile
index 84aebf4945..f87ac560aa 100644
--- a/stdio-common/Makefile
+++ b/stdio-common/Makefile
@@ -34,7 +34,9 @@ routines	:=							      \
 	tmpfile tmpfile64 tmpnam tmpnam_r tempnam tempname		      \
 	getline getw putw						      \
 	remove rename renameat						      \
-	flockfile ftrylockfile funlockfile
+	flockfile ftrylockfile funlockfile				      \
+	isoc99_scanf isoc99_vscanf isoc99_fscanf isoc99_vfscanf isoc99_sscanf \
+	isoc99_vsscanf
 
 install-others = $(inst_includedir)/bits/stdio_lim.h
 
@@ -55,7 +57,7 @@ tests := tstscanf test_rdwr test-popen tstgetln test-fseek \
 	 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-swscanf tst-sprintf2 bug18 bug18a \
-	 bug19 bug19a tst-popen2
+	 bug19 bug19a tst-popen2 scanf13 scanf14 scanf15
 
 test-srcs = tst-unbputc tst-printf
 
@@ -91,14 +93,34 @@ CFLAGS-tempname.c = -fexceptions
 CFLAGS-psignal.c = -fexceptions
 CFLAGS-vprintf.c = -fexceptions
 CFLAGS-cuserid.c = -fexceptions
+
+CFLAGS-vfprintf.c += $(exceptions)
+CFLAGS-fprintf.c += $(exceptions)
+CFLAGS-printf.c += $(exceptions)
+CFLAGS-vfwprintf.c += $(exceptions)
+CFLAGS-vfscanf.c += $(exceptions)
+CFLAGS-vfwscanf.c += $(exceptions)
+CFLAGS-fscanf.c += $(exceptions)
+CFLAGS-scanf.c += $(exceptions)
+CFLAGS-isoc99_vfscanf.c += $(exceptions)
+CFLAGS-isoc99_vscanf.c += $(exceptions)
+CFLAGS-isoc99_fscanf.c += $(exceptions)
+CFLAGS-isoc99_scanf.c += $(exceptions)
 CFLAGS-errlist.c = $(fno-unit-at-a-time)
 CFLAGS-siglist.c = $(fno-unit-at-a-time)
 
+# The following is a hack since we must compile scanf15.c without any
+# GNU extension.  The latter are needed, though, when internal headers
+# are used.  So made sure we see the installed headers first.
+CFLAGS-scanf15.c = -I../libio -I../stdlib -I../wcsmbs -I../time -I../string \
+		   -I../wctype
+
 # We know the test has a format string problem.
 CFLAGS-tst-sprintf.c = -Wno-format
 tst-sscanf-ENV = LOCPATH=$(common-objpfx)localedata
 tst-swprintf-ENV = LOCPATH=$(common-objpfx)localedata
 test-vfprintf-ENV = LOCPATH=$(common-objpfx)localedata
+scanf13-ENV = LOCPATH=$(common-objpfx)localedata
 bug14-ENV = LOCPATH=$(common-objpfx)localedata
 bug15-ENV = LOCPATH=$(common-objpfx)localedata
 
diff --git a/stdio-common/Versions b/stdio-common/Versions
index 2f64429346..1501fa2ffd 100644
--- a/stdio-common/Versions
+++ b/stdio-common/Versions
@@ -49,6 +49,10 @@ libc {
   GLIBC_2.4 {
     renameat;
   }
+  GLIBC_2.7 {
+    __isoc99_scanf; __isoc99_vscanf; __isoc99_fscanf; __isoc99_vfscanf;
+    __isoc99_sscanf; __isoc99_vsscanf;
+  }
   GLIBC_PRIVATE {
     # global variables
     _itoa_lower_digits;
diff --git a/stdio-common/isoc99_fscanf.c b/stdio-common/isoc99_fscanf.c
new file mode 100644
index 0000000000..5b8c16e788
--- /dev/null
+++ b/stdio-common/isoc99_fscanf.c
@@ -0,0 +1,40 @@
+/* Copyright (C) 1991, 1997, 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
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <libioP.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+/* Read formatted input from STREAM according to the format string FORMAT.  */
+/* VARARGS2 */
+int
+__isoc99_fscanf (FILE *stream, const char *format, ...)
+{
+  va_list arg;
+  int done;
+
+  _IO_acquire_lock_clear_flags2 (stream);
+  stream->_flags2 |= _IO_FLAGS2_SCANF_STD;
+
+  va_start (arg, format);
+  done = INTUSE(_IO_vfscanf) (stream, format, arg, NULL);
+  va_end (arg);
+
+  _IO_release_lock (stream);
+  return done;
+}
diff --git a/stdio-common/isoc99_scanf.c b/stdio-common/isoc99_scanf.c
new file mode 100644
index 0000000000..a90fe7018c
--- /dev/null
+++ b/stdio-common/isoc99_scanf.c
@@ -0,0 +1,42 @@
+/* Copyright (C) 1991, 1995, 1996, 1997, 2002, 2004, 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
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <libioP.h>
+
+
+/* Read formatted input from stdin according to the format string FORMAT.  */
+/* VARARGS1 */
+int
+__isoc99_scanf (const char *format, ...)
+{
+  va_list arg;
+  int done;
+
+  _IO_acquire_lock_clear_flags2 (stdin);
+  stdin->_flags2 |= _IO_FLAGS2_SCANF_STD;
+
+  va_start (arg, format);
+  done = INTUSE(_IO_vfscanf) (stdin, format, arg, NULL);
+  va_end (arg);
+
+  _IO_release_lock (stdin);
+  return done;
+}
diff --git a/stdio-common/isoc99_sscanf.c b/stdio-common/isoc99_sscanf.c
new file mode 100644
index 0000000000..9500ccb464
--- /dev/null
+++ b/stdio-common/isoc99_sscanf.c
@@ -0,0 +1,37 @@
+/* Copyright (C) 1991, 1995, 1996, 1998, 2002, 2003, 2004, 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
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <libioP.h>
+
+/* Read formatted input from S, according to the format string FORMAT.  */
+/* VARARGS2 */
+int
+__isoc99_sscanf (const char *s, const char *format, ...)
+{
+  va_list arg;
+  int done;
+
+  va_start (arg, format);
+  done = __isoc99_vsscanf (s, format, arg);
+  va_end (arg);
+
+  return done;
+}
diff --git a/stdio-common/isoc99_vfscanf.c b/stdio-common/isoc99_vfscanf.c
new file mode 100644
index 0000000000..10b04c3bc7
--- /dev/null
+++ b/stdio-common/isoc99_vfscanf.c
@@ -0,0 +1,35 @@
+/* Copyright (C) 1991, 1997, 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
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <libioP.h>
+#include <stdio.h>
+
+/* Read formatted input from STREAM according to the format string FORMAT.  */
+/* VARARGS2 */
+int
+__isoc99_vfscanf (FILE *stream, const char *format, _IO_va_list args)
+{
+  int done;
+
+  _IO_acquire_lock_clear_flags2 (stream);
+  stream->_flags2 |= _IO_FLAGS2_SCANF_STD;
+  done = INTUSE(_IO_vfscanf) (stream, format, args, NULL);
+  _IO_release_lock (stream);
+  return done;
+}
+libc_hidden_def (__isoc99_vfscanf)
diff --git a/stdio-common/isoc99_vscanf.c b/stdio-common/isoc99_vscanf.c
new file mode 100644
index 0000000000..f7858f042b
--- /dev/null
+++ b/stdio-common/isoc99_vscanf.c
@@ -0,0 +1,34 @@
+/* Copyright (C) 1991, 1997, 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
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <libioP.h>
+#include <stdio.h>
+
+/* Read formatted input from STDIN according to the format string FORMAT.  */
+/* VARARGS2 */
+int
+__isoc99_vscanf (const char *format, _IO_va_list args)
+{
+  int done;
+
+  _IO_acquire_lock_clear_flags2 (stdin);
+  stdin->_flags2 |= _IO_FLAGS2_SCANF_STD;
+  done = INTUSE(_IO_vfscanf) (stdin, format, args, NULL);
+  _IO_release_lock (stdin);
+  return done;
+}
diff --git a/stdio-common/isoc99_vsscanf.c b/stdio-common/isoc99_vsscanf.c
new file mode 100644
index 0000000000..f10919c79a
--- /dev/null
+++ b/stdio-common/isoc99_vsscanf.c
@@ -0,0 +1,47 @@
+/* Copyright (C) 1993, 1997-2003, 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
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.
+
+   As a special exception, if you link the code in this file with
+   files compiled with a GNU compiler to produce an executable,
+   that does not cause the resulting executable to be covered by
+   the GNU Lesser General Public License.  This exception does not
+   however invalidate any other reasons why the executable file
+   might be covered by the GNU Lesser General Public License.
+   This exception applies to code released by its copyright holders
+   in files containing the exception.  */
+
+#include <libioP.h>
+#include <stdio.h>
+#include "../libio/strfile.h"
+
+int
+__isoc99_vsscanf (const char *string, const char *format, _IO_va_list args)
+{
+  int ret;
+  _IO_strfile sf;
+#ifdef _IO_MTSAFE_IO
+  sf._sbf._f._lock = NULL;
+#endif
+  _IO_no_init (&sf._sbf._f, _IO_USER_LOCK, -1, NULL, NULL);
+  _IO_JUMPS ((struct _IO_FILE_plus *) &sf._sbf) = &_IO_str_jumps;
+  _IO_str_init_static_internal (&sf, (char*)string, 0, NULL);
+  sf._sbf._f._flags2 |= _IO_FLAGS2_SCANF_STD;
+  ret = INTUSE(_IO_vfscanf) ((_IO_FILE *) &sf._sbf, format, args, NULL);
+  return ret;
+}
+libc_hidden_def (__isoc99_vsscanf)
diff --git a/stdio-common/scanf13.c b/stdio-common/scanf13.c
new file mode 100644
index 0000000000..e53cde29c7
--- /dev/null
+++ b/stdio-common/scanf13.c
@@ -0,0 +1,187 @@
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+
+int
+main (void)
+{
+  char *sp1, *sp2, *sp3, *sp4;
+  wchar_t *lsp1, *lsp2, *lsp3, *lsp4;
+  int result = 0;
+  char buf[2048+64];
+  wchar_t wbuf[2048+64];
+  size_t i;
+
+#define FAIL() \
+  do {							\
+    result = 1;						\
+    printf ("test at line %d failed\n", __LINE__);	\
+  } while (0)
+
+  setlocale (LC_ALL, "de_DE.UTF-8");
+  if (sscanf ("A  \xc3\x84-\t\t\xc3\x84-abcdefbcd\t\xc3\x84-B",
+	      "A%ms%10ms%4m[bcd]%4mcB", &sp1, &sp2, &sp3, &sp4) != 4)
+    FAIL ();
+  else
+    {
+      if (strcmp (sp1, "\xc3\x84-") != 0)
+	FAIL ();
+      free (sp1);
+      if (strcmp (sp2, "\xc3\x84-abcdefb") != 0)
+	FAIL ();
+      free (sp2);
+      if (strcmp (sp3, "cd") != 0)
+	FAIL ();
+      free (sp3);
+      if (memcmp (sp4, "\t\xc3\x84-", 4) != 0)
+	FAIL ();
+      free (sp4);
+    }
+
+  if (sscanf ("A  \xc3\x84-\t\t\xc3\x84-abcdefbcd\t\xc3\x84-BB",
+	      "A%mS%10mls%4ml[bcd]%4mCB", &lsp1, &lsp2, &lsp3, &lsp4) != 4)
+    FAIL ();
+  else
+    {
+      if (wcscmp (lsp1, L"\xc4-") != 0)
+	FAIL ();
+      free (lsp1);
+      if (wcscmp (lsp2, L"\xc4-abcdefbc") != 0)
+	FAIL ();
+      free (lsp2);
+      if (wcscmp (lsp3, L"d") != 0)
+	FAIL ();
+      free (lsp3);
+      if (memcmp (lsp4, L"\t\xc4-B", 4 * sizeof (wchar_t)) != 0)
+	FAIL ();
+      free (lsp4);
+    }
+
+  memset (buf, '/', sizeof (buf));
+  buf[0] = '\t';
+  buf[1] = ' ';
+  buf[2] = 0xc3;
+  buf[3] = 0x84;
+  buf[2048] = 0xc3;
+  buf[2049] = 0x84;
+  buf[2058] = '\t';
+  buf[2059] = 'a';
+  if (sscanf (buf, "%ms%mc", &sp1, &sp2) != 2)
+    FAIL ();
+  else
+    {
+      if (sp1[0] != '\xc3' || sp1[1] != '\x84'
+	  || sp1[2046] != '\xc3' || sp1[2047] != '\x84'
+	  || sp1[2056] != '\0')
+	FAIL ();
+      sp1[2046] = '/';
+      sp1[2047] = '/';
+      for (i = 2; i < 2056; i++)
+	if (sp1[i] != '/')
+	  FAIL ();
+      free (sp1);
+      if (sp2[0] != '\t')
+	FAIL ();
+      free (sp2);
+    }
+  if (sscanf (buf, "%2048ms%mc", &sp3, &sp4) != 2)
+    FAIL ();
+  else
+    {
+      if (sp3[0] != '\xc3' || sp3[1] != '\x84'
+	  || sp3[2046] != '\xc3' || sp3[2047] != '\x84'
+	  || sp3[2048] != '\0')
+	FAIL ();
+      for (i = 2; i < 2046; i++)
+	if (sp3[i] != '/')
+	  FAIL ();
+      free (sp3);
+      if (sp4[0] != '/')
+	FAIL ();
+      free (sp4);
+    }
+  if (sscanf (buf, "%4mc%1500m[dr/]%548m[abc/d]%3mc", &sp1, &sp2, &sp3, &sp4)
+      != 4)
+    FAIL ();
+  else
+    {
+      if (memcmp (sp1, "\t \xc3\x84", 4) != 0)
+	FAIL ();
+      free (sp1);
+      for (i = 0; i < 1500; i++)
+	if (sp2[i] != '/')
+	  FAIL ();
+      if (sp2[1500] != '\0')
+	FAIL ();
+      free (sp2);
+      for (i = 0; i < 544; i++)
+	if (sp3[i] != '/')
+	  FAIL ();
+      if (sp3[544] != '\0')
+	FAIL ();
+      free (sp3);
+      if (memcmp (sp4, "\xc3\x84/", 3) != 0)
+	FAIL ();
+      free (sp4);
+    }
+  if (sscanf (buf, "%mS%mC", &lsp1, &lsp2) != 2)
+    FAIL ();
+  else
+    {
+      if (lsp1[0] != L'\xc4' || lsp1[2045] != L'\xc4'
+	  || lsp1[2054] != L'\0')
+	FAIL ();
+      lsp1[2045] = L'/';
+      for (i = 1; i < 2054; i++)
+	if (lsp1[i] != L'/')
+	  FAIL ();
+      free (lsp1);
+      if (lsp2[0] != L'\t')
+	FAIL ();
+      free (lsp2);
+    }
+  if (sscanf (buf, "%2048mls%mlc", &lsp3, &lsp4) != 2)
+    FAIL ();
+  else
+    {
+      if (lsp3[0] != L'\xc4' || lsp3[2045] != L'\xc4'
+	  || lsp3[2048] != L'\0')
+	FAIL ();
+      lsp3[2045] = L'/';
+      for (i = 1; i < 2048; i++)
+	if (lsp3[i] != L'/')
+	  FAIL ();
+      free (lsp3);
+      if (lsp4[0] != L'/')
+	FAIL ();
+      free (lsp4);
+    }
+  if (sscanf (buf, "%4mC%1500ml[dr/]%548ml[abc/d]%3mlc",
+	      &lsp1, &lsp2, &lsp3, &lsp4) != 4)
+    FAIL ();
+  else
+    {
+      if (memcmp (lsp1, L"\t \xc4/", 4 * sizeof (wchar_t)) != 0)
+	FAIL ();
+      free (lsp1);
+      for (i = 0; i < 1500; i++)
+	if (lsp2[i] != L'/')
+	  FAIL ();
+      if (lsp2[1500] != L'\0')
+	FAIL ();
+      free (lsp2);
+      for (i = 0; i < 543; i++)
+	if (lsp3[i] != L'/')
+	  FAIL ();
+      if (lsp3[543] != L'\0')
+	FAIL ();
+      free (lsp3);
+      if (memcmp (lsp4, L"\xc4//", 3 * sizeof (wchar_t)) != 0)
+	FAIL ();
+      free (lsp4);
+    }
+
+  return result;
+}
diff --git a/stdio-common/scanf14.c b/stdio-common/scanf14.c
new file mode 100644
index 0000000000..387ceba4bb
--- /dev/null
+++ b/stdio-common/scanf14.c
@@ -0,0 +1,63 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+
+#define FAIL() \
+  do {							\
+    result = 1;						\
+    printf ("test at line %d failed\n", __LINE__);	\
+  } while (0)
+
+int
+main (void)
+{
+  wchar_t *lsp;
+  char *sp;
+  float f;
+  double d;
+  char c[8];
+  int result = 0;
+
+  if (sscanf (" 0.25s x", "%e%3c", &f, c) != 2)
+    FAIL ();
+  else if (f != 0.25 || memcmp (c, "s x", 3) != 0)
+    FAIL ();
+  if (sscanf (" 1.25s x", "%as%2c", &sp, c) != 2)
+    FAIL ();
+  else
+    {
+      if (strcmp (sp, "1.25s") != 0 || memcmp (c, " x", 2) != 0)
+	FAIL ();
+      memset (sp, 'x', sizeof "1.25s");
+      free (sp);
+    }
+  if (sscanf (" 2.25s x", "%las%2c", &d, c) != 2)
+    FAIL ();
+  else if (d != 2.25 || memcmp (c, " x", 2) != 0)
+    FAIL ();
+  if (sscanf (" 3.25S x", "%4aS%3c", &lsp, c) != 2)
+    FAIL ();
+  else
+    {
+      if (wcscmp (lsp, L"3.25") != 0 || memcmp (c, "S x", 3) != 0)
+	FAIL ();
+      memset (lsp, 'x', sizeof L"3.25");
+      free (lsp);
+    }
+  if (sscanf ("4.25[0-9.] x", "%a[0-9.]%8c", &sp, c) != 2)
+    FAIL ();
+  else
+    {
+      if (strcmp (sp, "4.25") != 0 || memcmp (c, "[0-9.] x", 8) != 0)
+	FAIL ();
+      memset (sp, 'x', sizeof "4.25");
+      free (sp);
+    }
+  if (sscanf ("5.25[0-9.] x", "%la[0-9.]%2c", &d, c) != 2)
+    FAIL ();
+  else if (d != 5.25 || memcmp (c, " x", 2) != 0)
+    FAIL ();
+
+  return result;
+}
diff --git a/stdio-common/scanf15.c b/stdio-common/scanf15.c
new file mode 100644
index 0000000000..cc8aa2e6a6
--- /dev/null
+++ b/stdio-common/scanf15.c
@@ -0,0 +1,54 @@
+#undef _GNU_SOURCE
+#define _XOPEN_SOURCE 600
+/* The following macro definitions are a hack.  They word around disabling
+   the GNU extension while still using a few internal headers.  */
+#define u_char unsigned char
+#define u_short unsigned short
+#define u_int unsigned int
+#define u_long unsigned long
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+
+#define FAIL() \
+  do {							\
+    result = 1;						\
+    printf ("test at line %d failed\n", __LINE__);	\
+  } while (0)
+
+int
+main (void)
+{
+  float f;
+  double d;
+  char c[8];
+  int result = 0;
+
+  if (sscanf (" 0.25s x", "%e%3c", &f, c) != 2)
+    FAIL ();
+  else if (f != 0.25 || memcmp (c, "s x", 3) != 0)
+    FAIL ();
+  if (sscanf (" 1.25s x", "%as%2c", &f, c) != 2)
+    FAIL ();
+  else if (f != 1.25 || memcmp (c, " x", 2) != 0)
+    FAIL ();
+  if (sscanf (" 2.25s x", "%las%2c", &d, c) != 2)
+    FAIL ();
+  else if (d != 2.25 || memcmp (c, " x", 2) != 0)
+    FAIL ();
+  if (sscanf (" 3.25S x", "%4aS%2c", &f, c) != 2)
+    FAIL ();
+  else if (f != 3.25 || memcmp (c, " x", 2) != 0)
+    FAIL ();
+  if (sscanf (" 4.25[0-9.] x", "%a[0-9.]%2c", &f, c) != 2)
+    FAIL ();
+  else if (f != 4.25 || memcmp (c, " x", 2) != 0)
+    FAIL ();
+  if (sscanf (" 5.25[0-9.] x", "%la[0-9.]%2c", &d, c) != 2)
+    FAIL ();
+  else if (d != 5.25 || memcmp (c, " x", 2) != 0)
+    FAIL ();
+
+  return result;
+}
diff --git a/stdio-common/vfscanf.c b/stdio-common/vfscanf.c
index ddf9a5ea31..e4728d00c9 100644
--- a/stdio-common/vfscanf.c
+++ b/stdio-common/vfscanf.c
@@ -514,13 +514,25 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
 	      --f;
 	      break;
 	    }
+	  /* In __isoc99_*scanf %as, %aS and %a[ extension is not
+	     supported at all.  */
+	  if (s->_flags2 & _IO_FLAGS2_SCANF_STD)
+	    {
+	      --f;
+	      break;
+	    }
 	  /* String conversions (%s, %[) take a `char **'
 	     arg and fill it in with a malloc'd pointer.  */
 	  flags |= GNU_MALLOC;
 	  break;
-        case L_('m'):
-          flags |= POSIX_MALLOC;
-          break;
+	case L_('m'):
+	  flags |= POSIX_MALLOC;
+	  if (*f == L_('l'))
+	    {
+	      ++f;
+	      flags |= LONG;
+	    }
+	  break;
 	case L_('z'):
 	  if (need_longlong && sizeof (size_t) > sizeof (unsigned long int))
 	    flags |= LONGDBL;
@@ -635,20 +647,46 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
 	case L_('c'):	/* Match characters.  */
 	  if ((flags & LONG) == 0)
 	    {
-	      if (!(flags & SUPPRESS))
-		{
-		  str = ARG (char *);
-		  if (str == NULL)
-		    conv_error ();
-		}
+	      if (width == -1)
+		width = 1;
+
+#define STRING_ARG(Str, Type, Width)					      \
+	      do if (!(flags & SUPPRESS))				      \
+		{							      \
+		  if (flags & MALLOC)					      \
+		    {							      \
+		      /* The string is to be stored in a malloc'd buffer.  */ \
+		      /* For %mS using char ** is actually wrong, but	      \
+			 shouldn't make a difference on any arch glibc	      \
+			 supports and would unnecessarily complicate	      \
+			 things. */					      \
+		      strptr = ARG (char **);				      \
+		      if (strptr == NULL)				      \
+			conv_error ();					      \
+		      /* Allocate an initial buffer.  */		      \
+		      strsize = Width;					      \
+		      *strptr = (char *) malloc (strsize * sizeof (Type));    \
+		      Str = (Type *) *strptr;				      \
+		      if (Str != NULL)					      \
+			add_ptr_to_free (strptr);			      \
+		      else if (flags & POSIX_MALLOC)			      \
+			goto reteof;					      \
+		    }							      \
+		  else							      \
+		    Str = ARG (Type *);					      \
+		  if (Str == NULL)					      \
+		    conv_error ();					      \
+		} while (0)
+#ifdef COMPILE_WSCANF
+	      STRING_ARG (str, char, 100);
+#else
+	      STRING_ARG (str, char, (width > 1024 ? 1024 : width));
+#endif
 
 	      c = inchar ();
 	      if (__builtin_expect (c == EOF, 0))
 		input_error ();
 
-	      if (width == -1)
-		width = 1;
-
 #ifdef COMPILE_WSCANF
 	      /* We have to convert the wide character(s) into multibyte
 		 characters and store the result.  */
@@ -658,6 +696,38 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
 		{
 		  size_t n;
 
+		  if (!(flags & SUPPRESS) && (flags & POSIX_MALLOC)
+		      && str + MB_CUR_MAX >= *strptr + strsize)
+		    {
+		      /* We have to enlarge the buffer if the `m' flag
+			 was given.  */
+		      size_t strleng = str - *strptr;
+		      char *newstr;
+
+		      newstr = (char *) realloc (*strptr, strsize * 2);
+		      if (newstr == NULL)
+			{
+			  /* Can't allocate that much.  Last-ditch effort.  */
+			  newstr = (char *) realloc (*strptr,
+						     strleng + MB_CUR_MAX);
+			  if (newstr == NULL)
+			    /* c can't have `a' flag, only `m'.  */
+			    goto reteof;
+			  else
+			    {
+			      *strptr = newstr;
+			      str = newstr + strleng;
+			      strsize = strleng + MB_CUR_MAX;
+			    }
+			}
+		      else
+			{
+			  *strptr = newstr;
+			  str = newstr + strleng;
+			  strsize *= 2;
+			}
+		    }
+
 		  n = __wcrtomb (!(flags & SUPPRESS) ? str : NULL, c, &state);
 		  if (__builtin_expect (n == (size_t) -1, 0))
 		    /* No valid wide character.  */
@@ -672,7 +742,40 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
 	      if (!(flags & SUPPRESS))
 		{
 		  do
-		    *str++ = c;
+		    {
+		      if ((flags & MALLOC)
+			  && (char *) str == *strptr + strsize)
+			{
+			  /* Enlarge the buffer.  */
+			  size_t newsize
+			    = strsize
+			      + (strsize >= width ? width - 1 : strsize);
+
+			  str = (char *) realloc (*strptr, newsize);
+			  if (str == NULL)
+			    {
+			      /* Can't allocate that much.  Last-ditch
+				 effort.  */
+			      str = (char *) realloc (*strptr, strsize + 1);
+			      if (str == NULL)
+				/* c can't have `a' flag, only `m'.  */
+				goto reteof;
+			      else
+				{
+				  *strptr = (char *) str;
+				  str += strsize;
+				  ++strsize;
+				}
+			    }
+			  else
+			    {
+			      *strptr = (char *) str;
+			      str += strsize;
+			      strsize = newsize;
+			    }
+			}
+		      *str++ = c;
+		    }
 		  while (--width > 0 && inchar () != EOF);
 		}
 	      else
@@ -680,18 +783,25 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
 #endif
 
 	      if (!(flags & SUPPRESS))
-		++done;
+		{
+		  if ((flags & MALLOC) && str - *strptr != strsize)
+		    {
+		      char *cp = (char *) realloc (*strptr, str - *strptr);
+		      if (cp != NULL)
+			*strptr = cp;
+		    }
+		  strptr = NULL;
+		  ++done;
+		}
 
 	      break;
 	    }
 	  /* FALLTHROUGH */
 	case L_('C'):
-	  if (!(flags & SUPPRESS))
-	    {
-	      wstr = ARG (wchar_t *);
-	      if (wstr == NULL)
-		conv_error ();
-	    }
+	  if (width == -1)
+	    width = 1;
+
+	  STRING_ARG (wstr, wchar_t, (width > 1024 ? 1024 : width));
 
 	  c = inchar ();
 	  if (__builtin_expect (c == EOF, 0))
@@ -702,7 +812,40 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
 	  if (!(flags & SUPPRESS))
 	    {
 	      do
-		*wstr++ = c;
+		{
+		  if ((flags & MALLOC)
+		      && wstr == (wchar_t *) *strptr + strsize)
+		    {
+		      size_t newsize
+			= strsize + (strsize > width ? width - 1 : strsize);
+		      /* Enlarge the buffer.  */
+		      wstr = (wchar_t *) realloc (*strptr,
+						  newsize * sizeof (wchar_t));
+		      if (wstr == NULL)
+			{
+			  /* Can't allocate that much.  Last-ditch effort.  */
+			  wstr = (wchar_t *) realloc (*strptr,
+						      (strsize + 1)
+						      * sizeof (wchar_t));
+			  if (wstr == NULL)
+			    /* C or lc can't have `a' flag, only `m' flag.  */
+			    goto reteof;
+			  else
+			    {
+			      *strptr = (char *) wstr;
+			      wstr += strsize;
+			      ++strsize;
+			    }
+			}
+		      else
+			{
+			  *strptr = (char *) wstr;
+			  wstr += strsize;
+			  strsize = newsize;
+			}
+		    }
+		  *wstr++ = c;
+		}
 	      while (--width > 0 && inchar () != EOF);
 	    }
 	  else
@@ -721,6 +864,38 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
 		/* This is what we present the mbrtowc function first.  */
 		buf[0] = c;
 
+		if (!(flags & SUPPRESS) && (flags & MALLOC)
+		    && wstr == (wchar_t *) *strptr + strsize)
+		  {
+		    size_t newsize
+		      = strsize + (strsize > width ? width - 1 : strsize);
+		    /* Enlarge the buffer.  */
+		    wstr = (wchar_t *) realloc (*strptr,
+						newsize * sizeof (wchar_t));
+		    if (wstr == NULL)
+		      {
+			/* Can't allocate that much.  Last-ditch effort.  */
+			wstr = (wchar_t *) realloc (*strptr,
+						    ((strsize + 1)
+						     * sizeof (wchar_t)));
+			if (wstr == NULL)
+			  /* C or lc can't have `a' flag, only `m' flag.  */
+			  goto reteof;
+			else
+			  {
+			    *strptr = (char *) wstr;
+			    wstr += strsize;
+			    ++strsize;
+			  }
+		      }
+		    else
+		      {
+			*strptr = (char *) wstr;
+			wstr += strsize;
+			strsize = newsize;
+		      }
+		  }
+
 		while (1)
 		  {
 		    size_t n;
@@ -754,41 +929,27 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
 #endif
 
 	  if (!(flags & SUPPRESS))
-	    ++done;
+	    {
+	      if ((flags & MALLOC) && wstr - (wchar_t *) *strptr != strsize)
+		{
+		  wchar_t *cp = (wchar_t *) realloc (*strptr,
+						     ((wstr
+						       - (wchar_t *) *strptr)
+						      * sizeof (wchar_t)));
+		  if (cp != NULL)
+		    *strptr = (char *) cp;
+		}
+	      strptr = NULL;
+
+	      ++done;
+	    }
 
 	  break;
 
 	case L_('s'):		/* Read a string.  */
 	  if (!(flags & LONG))
 	    {
-#define STRING_ARG(Str, Type)						      \
-	      do if (!(flags & SUPPRESS))				      \
-		{							      \
-		  if (flags & MALLOC)					      \
-		    {							      \
-		      /* The string is to be stored in a malloc'd buffer.  */ \
-		      /* For %mS using char ** is actually wrong, but	      \
-			 shouldn't make a difference on any arch glibc	      \
-			 supports and would unnecessarily complicate	      \
-			 things. */					      \
-		      strptr = ARG (char **);				      \
-		      if (strptr == NULL)				      \
-			conv_error ();					      \
-		      /* Allocate an initial buffer.  */		      \
-		      strsize = 100;					      \
-		      *strptr = (char *) malloc (strsize * sizeof (Type));    \
-		      Str = (Type *) *strptr;				      \
-		      if (Str != NULL)					      \
-			add_ptr_to_free (strptr);			      \
-		      else if (flags & POSIX_MALLOC)			      \
-			goto reteof;					      \
-		    }							      \
-		  else							      \
-		    Str = ARG (Type *);					      \
-		  if (Str == NULL)					      \
-		    conv_error ();					      \
-		} while (0)
-	      STRING_ARG (str, char);
+	      STRING_ARG (str, char, 100);
 
 	      c = inchar ();
 	      if (__builtin_expect (c == EOF, 0))
@@ -816,8 +977,8 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
 		    if (!(flags & SUPPRESS) && (flags & MALLOC)
 			&& str + MB_CUR_MAX >= *strptr + strsize)
 		      {
-			/* We have to enlarge the buffer if the `a' flag
-			   was given.  */
+			/* We have to enlarge the buffer if the `a' or `m'
+			   flag was given.  */
 			size_t strleng = str - *strptr;
 			char *newstr;
 
@@ -969,7 +1130,7 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
 #endif
 
 	    /* Wide character string.  */
-	    STRING_ARG (wstr, wchar_t);
+	    STRING_ARG (wstr, wchar_t, 100);
 
 	    c = inchar ();
 	    if (__builtin_expect (c == EOF,  0))
@@ -1002,7 +1163,7 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
 			if (wstr == NULL)
 			  {
 			    /* Can't allocate that much.  Last-ditch
-                               effort.  */
+			       effort.  */
 			    wstr = (wchar_t *) realloc (*strptr,
 							(strsize + 1)
 							* sizeof (wchar_t));
@@ -2120,9 +2281,9 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
 
 	case L_('['):	/* Character class.  */
 	  if (flags & LONG)
-	    STRING_ARG (wstr, wchar_t);
+	    STRING_ARG (wstr, wchar_t, 100);
 	  else
-	    STRING_ARG (str, char);
+	    STRING_ARG (str, char, 100);
 
 	  if (*f == L_('^'))
 	    {