aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog67
-rw-r--r--libio/iovsscanf.c12
-rw-r--r--libio/iovswscanf.c14
-rw-r--r--libio/libio.h1
-rw-r--r--libio/libioP.h22
-rw-r--r--libio/strfile.h33
-rw-r--r--libio/swscanf.c10
-rw-r--r--libio/vscanf.c2
-rw-r--r--libio/vwscanf.c2
-rw-r--r--libio/wscanf.c2
-rw-r--r--stdio-common/Makefile3
-rw-r--r--stdio-common/Versions3
-rw-r--r--stdio-common/iovfscanf.c38
-rw-r--r--stdio-common/iovfwscanf.c38
-rw-r--r--stdio-common/isoc99_fscanf.c2
-rw-r--r--stdio-common/isoc99_scanf.c2
-rw-r--r--stdio-common/isoc99_sscanf.c9
-rw-r--r--stdio-common/isoc99_vfscanf.c2
-rw-r--r--stdio-common/isoc99_vscanf.c2
-rw-r--r--stdio-common/isoc99_vsscanf.c17
-rw-r--r--stdio-common/scanf.c2
-rw-r--r--stdio-common/sscanf.c12
-rw-r--r--stdio-common/vfscanf-internal.c3049
-rw-r--r--stdio-common/vfscanf.c3042
-rw-r--r--stdio-common/vfwscanf-internal.c2
-rw-r--r--stdio-common/vfwscanf.c28
-rw-r--r--sysdeps/generic/math_ldbl_opt.h4
-rw-r--r--sysdeps/ieee754/ldbl-opt/math_ldbl_opt.h6
-rw-r--r--sysdeps/ieee754/ldbl-opt/nldbl-compat.c17
-rw-r--r--wcsmbs/isoc99_fwscanf.c2
-rw-r--r--wcsmbs/isoc99_swscanf.c12
-rw-r--r--wcsmbs/isoc99_vfwscanf.c2
-rw-r--r--wcsmbs/isoc99_vswscanf.c16
-rw-r--r--wcsmbs/isoc99_vwscanf.c2
-rw-r--r--wcsmbs/isoc99_wscanf.c2
35 files changed, 3351 insertions, 3128 deletions
diff --git a/ChangeLog b/ChangeLog
index 935863b7b2..d2a1888554 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,70 @@
+2018-12-05 Zack Weinberg <zackw@panix.com>
+ Gabriel F. T. Gomes <gabriel@inconstante.eti.br>
+
+ * libio/libioP.h (SCANF_LDBL_IS_DBL, SCANF_ISOC99_A): New constants.
+ (__vfscanf_internal, __vfwscanf_internal): New function prototypes.
+ * libio/libio.h: Remove libc_hidden_proto for _IO_vfscanf.
+ * libio/strfile.h: Add multiple inclusion guard.
+ (_IO_strfile_read, _IO_strfile_readw): New inline functions.
+
+ * sysdeps/generic/math_ldbl_opt.h: Include shlib-compat.h, for
+ consistency with the other version of this file.
+ (ldbl_compat_symbol): New macro.
+ * sysdeps/ieee754/ldbl-opt/math_ldbl_opt.h (ldbl_compat_symbol):
+ New macro.
+
+ * stdio-common/vfscanf-internal.c: Rename from vfscanf.c.
+ Define __vfscanf_internal or __vfwscanf_internal, depending on
+ COMPILE_WSCANF; don't define any other public symbols.
+ Remove errval and code to set errp.
+ Temporarily check __ldbl_is_dbl and _IO_FLAGS2_SCANF_STD as well
+ as the mode_flags argument.
+ (encode_error, conv_error, input_error): Don't set errval.
+ * stdio-common/vfwscanf-internal.c: Rename from vfwscanf.c.
+ Include vfscanf-internal.c.
+ * stdio-common/vfscanf.c: New file defining the public entry
+ point vfscanf, which calls __vfscanf_internal.
+ * stdio-common/vfwscanf.c: New file defining the public entry
+ point vfwscanf, which calls __vfwscanf_internal.
+
+ * stdio-common/iovfscanf.c: New file.
+ * stdio-common/iovfwscanf.c: Likewise.
+
+ * stdio-common/Makefile (routines): Add vfscanf-internal,
+ vfwscanf-internal, iovfscanf, iovfwscanf.
+ * stdio-common/Versions: Mention GLIBC_2.29, so that
+ it can be used in SHLIB_COMPAT expressions.
+ * sysdeps/ieee754/ldbl-opt/nldbl-compat.c (__nldbl__IO_vfscanf):
+ Wrap definition and compat_symbol line in #if SHLIB_COMPAT.
+ Call __vfscanf_internal, instead of _IO_vfscanf.
+ (__nldbl___vfscanf): Call __vfscanf_internal, instead of
+ _IO_vfscanf.
+ (__nldbl_vfwscanf): Call __vfwscanf_internal, instead of
+ _IO_vfwscanf.
+
+ * libio/iovsscanf.c: Clean up includes, when possible. Use
+ _IO_strfile_read or _IO_strfile_readw, when needed. Call
+ __vfscanf_internal or __vfwscanf_internal directly.
+ * libio/iovswscanf.c: Likewise.
+ * libio/swscanf.c: Likewise.
+ * libio/vscanf.c: Likewise.
+ * libio/vwscanf.c: Likewise.
+ * libio/wscanf.c: Likewise.
+ * stdio-common/isoc99_fscanf.c: Likewise.
+ * stdio-common/isoc99_scanf.c: Likewise.
+ * stdio-common/isoc99_sscanf.c: Likewise.
+ * stdio-common/isoc99_vfscanf.c: Likewise.
+ * stdio-common/isoc99_vscanf.c: Likewise.
+ * stdio-common/isoc99_vsscanf.c: Likewise.
+ * stdio-common/scanf.c: Likewise.
+ * stdio-common/sscanf.c: Likewise.
+ * wcsmbs/isoc99_fwscanf.c: Likewise.
+ * wcsmbs/isoc99_swscanf.c: Likewise.
+ * wcsmbs/isoc99_vfwscanf.c: Likewise.
+ * wcsmbs/isoc99_vswscanf.c: Likewise.
+ * wcsmbs/isoc99_vwscanf.c: Likewise.
+ * wcsmbs/isoc99_wscanf.c: Likewise.
+
2018-12-05 Albert ARIBAUD <albert.aribaud@3adev.fr>
* include/time.h
diff --git a/libio/iovsscanf.c b/libio/iovsscanf.c
index e56ab8bd7d..ee6a99ec6a 100644
--- a/libio/iovsscanf.c
+++ b/libio/iovsscanf.c
@@ -24,22 +24,14 @@
This exception applies to code released by its copyright holders
in files containing the exception. */
-#include "libioP.h"
#include "strfile.h"
int
_IO_vsscanf (const char *string, const char *format, 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 (&sf._sbf) = &_IO_str_jumps;
- _IO_str_init_static_internal (&sf, (char*)string, 0, NULL);
- ret = _IO_vfscanf (&sf._sbf._f, format, args, NULL);
- return ret;
+ FILE *f = _IO_strfile_read (&sf, string);
+ return __vfscanf_internal (f, format, args, 0);
}
ldbl_weak_alias (_IO_vsscanf, __vsscanf)
ldbl_weak_alias (_IO_vsscanf, vsscanf)
diff --git a/libio/iovswscanf.c b/libio/iovswscanf.c
index 5bd1c88412..cb9cbe15cc 100644
--- a/libio/iovswscanf.c
+++ b/libio/iovswscanf.c
@@ -24,24 +24,16 @@
This exception applies to code released by its copyright holders
in files containing the exception. */
-#include "libioP.h"
-#include "strfile.h"
#include <wchar.h>
+#include "strfile.h"
int
__vswscanf (const wchar_t *string, const wchar_t *format, va_list args)
{
- int ret;
_IO_strfile sf;
struct _IO_wide_data wd;
-#ifdef _IO_MTSAFE_IO
- sf._sbf._f._lock = NULL;
-#endif
- _IO_no_init (&sf._sbf._f, _IO_USER_LOCK, 0, &wd, &_IO_wstr_jumps);
- _IO_fwide (&sf._sbf._f, 1);
- _IO_wstr_init_static (&sf._sbf._f, (wchar_t *)string, 0, NULL);
- ret = _IO_vfwscanf ((FILE *) &sf._sbf, format, args, NULL);
- return ret;
+ FILE *f = _IO_strfile_readw (&sf, &wd, string);
+ return __vfwscanf_internal (f, format, args, 0);
}
libc_hidden_def (__vswscanf)
ldbl_hidden_def (__vswscanf, vswscanf)
diff --git a/libio/libio.h b/libio/libio.h
index 00f9169613..d4eba2df54 100644
--- a/libio/libio.h
+++ b/libio/libio.h
@@ -321,7 +321,6 @@ libc_hidden_proto (_IO_padn)
libc_hidden_proto (_IO_putc)
libc_hidden_proto (_IO_sgetn)
libc_hidden_proto (_IO_vfprintf)
-libc_hidden_proto (_IO_vfscanf)
#ifdef _IO_MTSAFE_IO
# undef _IO_peekc
diff --git a/libio/libioP.h b/libio/libioP.h
index df2633d858..525dce19ee 100644
--- a/libio/libioP.h
+++ b/libio/libioP.h
@@ -704,6 +704,28 @@ extern off64_t _IO_seekpos_unlocked (FILE *, off64_t, int)
#endif /* _G_HAVE_MMAP */
+/* Flags for __vfscanf_internal and __vfwscanf_internal.
+
+ SCANF_LDBL_IS_DBL indicates whether long double values are to be
+ handled as having the same format as double, in which case the flag
+ should be set to one, or as another format, otherwise.
+
+ SCANF_ISOC99_A, when set to one, indicates that the ISO C99 or POSIX
+ behavior of the scanf functions is to be used, i.e. automatic
+ allocation for input strings with %as, %aS and %a[, a GNU extension,
+ is disabled. This is the behavior that the __isoc99_scanf family of
+ functions use. When the flag is set to zero, automatic allocation is
+ enabled. */
+#define SCANF_LDBL_IS_DBL 0x0001
+#define SCANF_ISOC99_A 0x0002
+
+extern int __vfscanf_internal (FILE *fp, const char *format, va_list argp,
+ unsigned int flags)
+ attribute_hidden;
+extern int __vfwscanf_internal (FILE *fp, const wchar_t *format, va_list argp,
+ unsigned int flags)
+ attribute_hidden;
+
extern int _IO_vscanf (const char *, va_list) __THROW;
#ifdef _IO_MTSAFE_IO
diff --git a/libio/strfile.h b/libio/strfile.h
index 75caac2af5..e51ac34ccd 100644
--- a/libio/strfile.h
+++ b/libio/strfile.h
@@ -24,7 +24,9 @@
This exception applies to code released by its copyright holders
in files containing the exception. */
-#include <stdio.h>
+#ifndef STRFILE_H_
+#define STRFILE_H_
+
#include "libioP.h"
typedef void *(*_IO_alloc_type) (size_t);
@@ -80,3 +82,32 @@ typedef struct
} _IO_wstrnfile;
extern const struct _IO_jump_t _IO_wstrn_jumps attribute_hidden;
+
+/* Initialize an _IO_strfile SF to read from narrow string STRING, and
+ return the corresponding FILE object. It is not necessary to fclose
+ the FILE when it is no longer needed. */
+static inline FILE *
+_IO_strfile_read (_IO_strfile *sf, const char *string)
+{
+ sf->_sbf._f._lock = NULL;
+ _IO_no_init (&sf->_sbf._f, _IO_USER_LOCK, -1, NULL, NULL);
+ _IO_JUMPS (&sf->_sbf) = &_IO_str_jumps;
+ _IO_str_init_static_internal (sf, (char*)string, 0, NULL);
+ return &sf->_sbf._f;
+}
+
+/* Initialize an _IO_strfile SF and _IO_wide_data WD to read from wide
+ string STRING, and return the corresponding FILE object. It is not
+ necessary to fclose the FILE when it is no longer needed. */
+static inline FILE *
+_IO_strfile_readw (_IO_strfile *sf, struct _IO_wide_data *wd,
+ const wchar_t *string)
+{
+ sf->_sbf._f._lock = NULL;
+ _IO_no_init (&sf->_sbf._f, _IO_USER_LOCK, 0, wd, &_IO_wstr_jumps);
+ _IO_fwide (&sf->_sbf._f, 1);
+ _IO_wstr_init_static (&sf->_sbf._f, (wchar_t *)string, 0, NULL);
+ return &sf->_sbf._f;
+}
+
+#endif /* strfile.h. */
diff --git a/libio/swscanf.c b/libio/swscanf.c
index c8686bcbaf..90f721cc51 100644
--- a/libio/swscanf.c
+++ b/libio/swscanf.c
@@ -15,20 +15,22 @@
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
-#include <libioP.h>
#include <stdarg.h>
-#include <wchar.h>
+#include "strfile.h"
/* Read formatted input from S, according to the format string FORMAT. */
-/* VARARGS2 */
+
int
__swscanf (const wchar_t *s, const wchar_t *format, ...)
{
va_list arg;
int done;
+ _IO_strfile sf;
+ struct _IO_wide_data wd;
+ FILE *f = _IO_strfile_readw (&sf, &wd, s);
va_start (arg, format);
- done = __vswscanf (s, format, arg);
+ done = __vfwscanf_internal (f, format, arg, 0);
va_end (arg);
return done;
diff --git a/libio/vscanf.c b/libio/vscanf.c
index 9c27122c27..a3e2dd43f2 100644
--- a/libio/vscanf.c
+++ b/libio/vscanf.c
@@ -32,6 +32,6 @@
int
_IO_vscanf (const char *format, va_list args)
{
- return _IO_vfscanf (_IO_stdin, format, args, NULL);
+ return __vfscanf_internal (_IO_stdin, format, args, 0);
}
ldbl_weak_alias (_IO_vscanf, vscanf)
diff --git a/libio/vwscanf.c b/libio/vwscanf.c
index 0d5f558758..7af770c8c3 100644
--- a/libio/vwscanf.c
+++ b/libio/vwscanf.c
@@ -30,6 +30,6 @@
int
__vwscanf (const wchar_t *format, va_list args)
{
- return _IO_vfwscanf (_IO_stdin, format, args, NULL);
+ return __vfwscanf_internal (_IO_stdin, format, args, 0);
}
ldbl_strong_alias (__vwscanf, vwscanf)
diff --git a/libio/wscanf.c b/libio/wscanf.c
index c8cdad0acd..fe27ff6fa6 100644
--- a/libio/wscanf.c
+++ b/libio/wscanf.c
@@ -30,7 +30,7 @@ __wscanf (const wchar_t *format, ...)
int done;
va_start (arg, format);
- done = _IO_vfwscanf (stdin, format, arg, NULL);
+ done = __vfwscanf_internal (stdin, format, arg, 0);
va_end (arg);
return done;
diff --git a/stdio-common/Makefile b/stdio-common/Makefile
index a10f12ab3c..f3b3ceddbd 100644
--- a/stdio-common/Makefile
+++ b/stdio-common/Makefile
@@ -39,7 +39,8 @@ routines := \
flockfile ftrylockfile funlockfile \
isoc99_scanf isoc99_vscanf isoc99_fscanf isoc99_vfscanf isoc99_sscanf \
isoc99_vsscanf \
- psiginfo gentempfd
+ psiginfo gentempfd \
+ vfscanf-internal vfwscanf-internal iovfscanf iovfwscanf
aux := errlist siglist printf-parsemb printf-parsewc fxprintf
diff --git a/stdio-common/Versions b/stdio-common/Versions
index b8217578c8..522f302198 100644
--- a/stdio-common/Versions
+++ b/stdio-common/Versions
@@ -60,6 +60,9 @@ libc {
GLIBC_2.28 {
renameat2;
}
+ GLIBC_2.29 {
+ # SHLIB_COMPAT(GLIBC_2_0, GLIBC_2_29) used in iovfscanf.c etc.
+ }
GLIBC_PRIVATE {
# global variables
_itoa_lower_digits;
diff --git a/stdio-common/iovfscanf.c b/stdio-common/iovfscanf.c
new file mode 100644
index 0000000000..77e698f665
--- /dev/null
+++ b/stdio-common/iovfscanf.c
@@ -0,0 +1,38 @@
+/* Implementation and symbols for _IO_vfscanf.
+ Copyright (C) 2018 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, see
+ <http://www.gnu.org/licenses/>. */
+
+#include <libioP.h>
+#include <shlib-compat.h>
+
+/* This function is provided for ports older than GLIBC 2.29 because
+ external callers could theoretically exist. Newer ports do not need,
+ since it is not part of the API. */
+#if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_29)
+
+int
+attribute_compat_text_section
+__IO_vfscanf (FILE *fp, const char *format, va_list ap, int *errp)
+{
+ int rv = __vfscanf_internal (fp, format, ap, 0);
+ if (__glibc_unlikely (errp != 0))
+ *errp = (rv == -1);
+ return rv;
+}
+ldbl_compat_symbol (libc, __IO_vfscanf, _IO_vfscanf, GLIBC_2_0);
+
+#endif
diff --git a/stdio-common/iovfwscanf.c b/stdio-common/iovfwscanf.c
new file mode 100644
index 0000000000..26a57788cb
--- /dev/null
+++ b/stdio-common/iovfwscanf.c
@@ -0,0 +1,38 @@
+/* Implementation and symbols for _IO_vfwscanf.
+ Copyright (C) 1991-2018 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, see
+ <http://www.gnu.org/licenses/>. */
+
+#include <libioP.h>
+#include <shlib-compat.h>
+
+/* This function is provided for ports older than GLIBC 2.29 because
+ external callers could theoretically exist. Newer ports do not need,
+ since it is not part of the API. */
+#if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_29)
+
+int
+attribute_compat_text_section
+__IO_vfwscanf (FILE *fp, const wchar_t *format, va_list ap, int *errp)
+{
+ int rv = __vfwscanf_internal (fp, format, ap, 0);
+ if (__glibc_unlikely (errp != 0))
+ *errp = (rv == -1);
+ return rv;
+}
+compat_symbol (libc, __IO_vfwscanf, _IO_vfwscanf, GLIBC_2_0);
+
+#endif
diff --git a/stdio-common/isoc99_fscanf.c b/stdio-common/isoc99_fscanf.c
index 9cdf85e679..4210d11f2b 100644
--- a/stdio-common/isoc99_fscanf.c
+++ b/stdio-common/isoc99_fscanf.c
@@ -31,7 +31,7 @@ __isoc99_fscanf (FILE *stream, const char *format, ...)
stream->_flags2 |= _IO_FLAGS2_SCANF_STD;
va_start (arg, format);
- done = _IO_vfscanf (stream, format, arg, NULL);
+ done = __vfscanf_internal (stream, format, arg, 0);
va_end (arg);
_IO_release_lock (stream);
diff --git a/stdio-common/isoc99_scanf.c b/stdio-common/isoc99_scanf.c
index bf7dbe86bb..64c873eed9 100644
--- a/stdio-common/isoc99_scanf.c
+++ b/stdio-common/isoc99_scanf.c
@@ -34,7 +34,7 @@ __isoc99_scanf (const char *format, ...)
stdin->_flags2 |= _IO_FLAGS2_SCANF_STD;
va_start (arg, format);
- done = _IO_vfscanf (stdin, format, arg, NULL);
+ done = __vfscanf_internal (stdin, format, arg, 0);
va_end (arg);
#ifdef _IO_MTSAFE_IO
diff --git a/stdio-common/isoc99_sscanf.c b/stdio-common/isoc99_sscanf.c
index 56a60a2c05..2c89a03fe9 100644
--- a/stdio-common/isoc99_sscanf.c
+++ b/stdio-common/isoc99_sscanf.c
@@ -16,19 +16,20 @@
<http://www.gnu.org/licenses/>. */
#include <stdarg.h>
-#include <stdio.h>
-#include <libioP.h>
+#include <libio/strfile.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;
+ _IO_strfile sf;
+ FILE *f = _IO_strfile_read (&sf, s);
+ f->_flags2 |= _IO_FLAGS2_SCANF_STD;
va_start (arg, format);
- done = __isoc99_vsscanf (s, format, arg);
+ done = __vfscanf_internal (f, format, arg, 0);
va_end (arg);
return done;
diff --git a/stdio-common/isoc99_vfscanf.c b/stdio-common/isoc99_vfscanf.c
index b80e05f8db..c96ca831ae 100644
--- a/stdio-common/isoc99_vfscanf.c
+++ b/stdio-common/isoc99_vfscanf.c
@@ -27,7 +27,7 @@ __isoc99_vfscanf (FILE *stream, const char *format, va_list args)
_IO_acquire_lock_clear_flags2 (stream);
stream->_flags2 |= _IO_FLAGS2_SCANF_STD;
- done = _IO_vfscanf (stream, format, args, NULL);
+ done = __vfscanf_internal (stream, format, args, 0);
_IO_release_lock (stream);
return done;
}
diff --git a/stdio-common/isoc99_vscanf.c b/stdio-common/isoc99_vscanf.c
index 0b747f85ba..72ae72ddee 100644
--- a/stdio-common/isoc99_vscanf.c
+++ b/stdio-common/isoc99_vscanf.c
@@ -27,7 +27,7 @@ __isoc99_vscanf (const char *format, va_list args)
_IO_acquire_lock_clear_flags2 (stdin);
stdin->_flags2 |= _IO_FLAGS2_SCANF_STD;
- done = _IO_vfscanf (stdin, format, args, NULL);
+ done = __vfscanf_internal (stdin, format, args, 0);
_IO_release_lock (stdin);
return done;
}
diff --git a/stdio-common/isoc99_vsscanf.c b/stdio-common/isoc99_vsscanf.c
index ac85ef2d0d..02bc0f50e6 100644
--- a/stdio-common/isoc99_vsscanf.c
+++ b/stdio-common/isoc99_vsscanf.c
@@ -24,23 +24,14 @@
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"
+#include <libio/strfile.h>
int
__isoc99_vsscanf (const char *string, const char *format, 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 (&sf._sbf) = &_IO_str_jumps;
- _IO_str_init_static_internal (&sf, (char*)string, 0, NULL);
- sf._sbf._f._flags2 |= _IO_FLAGS2_SCANF_STD;
- ret = _IO_vfscanf (&sf._sbf._f, format, args, NULL);
- return ret;
+ FILE *f = _IO_strfile_read (&sf, string);
+ f->_flags2 |= _IO_FLAGS2_SCANF_STD;
+ return __vfscanf_internal (f, format, args, 0);
}
libc_hidden_def (__isoc99_vsscanf)
diff --git a/stdio-common/scanf.c b/stdio-common/scanf.c
index e61b5f1ad3..de38d70353 100644
--- a/stdio-common/scanf.c
+++ b/stdio-common/scanf.c
@@ -30,7 +30,7 @@ __scanf (const char *format, ...)
int done;
va_start (arg, format);
- done = _IO_vfscanf (stdin, format, arg, NULL);
+ done = __vfscanf_internal (stdin, format, arg, 0);
va_end (arg);
return done;
diff --git a/stdio-common/sscanf.c b/stdio-common/sscanf.c
index 88cd641798..e25e9c27a5 100644
--- a/stdio-common/sscanf.c
+++ b/stdio-common/sscanf.c
@@ -16,26 +16,24 @@
<http://www.gnu.org/licenses/>. */
#include <stdarg.h>
-#include <stdio.h>
-#include <libioP.h>
-#define __vsscanf(s, f, a) _IO_vsscanf (s, f, a)
+#include <libio/strfile.h>
/* Read formatted input from S, according to the format string FORMAT. */
-/* VARARGS2 */
+
int
__sscanf (const char *s, const char *format, ...)
{
va_list arg;
int done;
+ _IO_strfile sf;
+ FILE *f = _IO_strfile_read (&sf, s);
va_start (arg, format);
- done = __vsscanf (s, format, arg);
+ done = __vfscanf_internal (f, format, arg, 0);
va_end (arg);
return done;
}
ldbl_hidden_def (__sscanf, sscanf)
ldbl_strong_alias (__sscanf, sscanf)
-#undef _IO_sscanf
-/* This is for libg++. */
ldbl_strong_alias (__sscanf, _IO_sscanf)
diff --git a/stdio-common/vfscanf-internal.c b/stdio-common/vfscanf-internal.c
new file mode 100644
index 0000000000..6bd0138f66
--- /dev/null
+++ b/stdio-common/vfscanf-internal.c
@@ -0,0 +1,3049 @@
+/* Internal functions for the *scanf* implementation.
+ Copyright (C) 1991-2018 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, see
+ <http://www.gnu.org/licenses/>. */
+
+#include <assert.h>
+#include <errno.h>
+#include <limits.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+#include <wctype.h>
+#include <libc-diag.h>
+#include <libc-lock.h>
+#include <locale/localeinfo.h>
+#include <scratch_buffer.h>
+
+#ifdef __GNUC__
+# define HAVE_LONGLONG
+# define LONGLONG long long
+#else
+# define LONGLONG long
+#endif
+
+/* Determine whether we have to handle `long long' at all. */
+#if LONG_MAX == LONG_LONG_MAX
+# define need_longlong 0
+#else
+# define need_longlong 1
+#endif
+
+/* Determine whether we have to handle `long'. */
+#if INT_MAX == LONG_MAX
+# define need_long 0
+#else
+# define need_long 1
+#endif
+
+/* Those are flags in the conversion format. */
+#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 GNU_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 */
+#define POSIX_MALLOC 0x2000 /* m: malloc strings */
+#define MALLOC (GNU_MALLOC | POSIX_MALLOC)
+
+#include <locale/localeinfo.h>
+#include <libioP.h>
+
+#ifdef COMPILE_WSCANF
+# define ungetc(c, s) ((void) (c == WEOF \
+ || (--read_in, \
+ _IO_sputbackwc (s, c))))
+# define ungetc_not_eof(c, s) ((void) (--read_in, \
+ _IO_sputbackwc (s, c)))
+# define inchar() (c == WEOF ? ((errno = inchar_errno), WEOF) \
+ : ((c = _IO_getwc_unlocked (s)), \
+ (void) (c != WEOF \
+ ? ++read_in \
+ : (size_t) (inchar_errno = errno)), c))
+
+# define ISSPACE(Ch) iswspace (Ch)
+# define ISDIGIT(Ch) iswdigit (Ch)
+# define ISXDIGIT(Ch) iswxdigit (Ch)
+# define TOLOWER(Ch) towlower (Ch)
+# define ORIENT if (_IO_fwide (s, 1) != 1) return WEOF
+# define __strtoll_internal __wcstoll_internal
+# define __strtoull_internal __wcstoull_internal
+# define __strtol_internal __wcstol_internal
+# define __strtoul_internal __wcstoul_internal
+# define __strtold_internal __wcstold_internal
+# define __strtod_internal __wcstod_internal
+# define __strtof_internal __wcstof_internal
+
+# define L_(Str) L##Str
+# define CHAR_T wchar_t
+# define UCHAR_T unsigned int
+# define WINT_T wint_t
+# undef EOF
+# define EOF WEOF
+#else
+# define ungetc(c, s) ((void) ((int) c == EOF \
+ || (--read_in, \
+ _IO_sputbackc (s, (unsigned char) c))))
+# define ungetc_not_eof(c, s) ((void) (--read_in, \
+ _IO_sputbackc (s, (unsigned char) c)))
+# define inchar() (c == EOF ? ((errno = inchar_errno), EOF) \
+ : ((c = _IO_getc_unlocked (s)), \
+ (void) (c != EOF \
+ ? ++read_in \
+ : (size_t) (inchar_errno = errno)), c))
+# define ISSPACE(Ch) __isspace_l (Ch, loc)
+# define ISDIGIT(Ch) __isdigit_l (Ch, loc)
+# define ISXDIGIT(Ch) __isxdigit_l (Ch, loc)
+# define TOLOWER(Ch) __tolower_l ((unsigned char) (Ch), loc)
+# define ORIENT if (_IO_vtable_offset (s) == 0 \
+ && _IO_fwide (s, -1) != -1) \
+ return EOF
+
+# define L_(Str) Str
+# define CHAR_T char
+# define UCHAR_T unsigned char
+# define WINT_T int
+#endif
+
+#include "printf-parse.h" /* Use read_int. */
+
+#define encode_error() do { \
+ __set_errno (EILSEQ); \
+ goto errout; \
+ } while (0)
+#define conv_error() do { \
+ goto errout; \
+ } while (0)
+#define input_error() do { \
+ if (done == 0) done = EOF; \
+ goto errout; \
+ } while (0)
+#define add_ptr_to_free(ptr) \
+ do \
+ { \
+ if (ptrs_to_free == NULL \
+ || ptrs_to_free->count == (sizeof (ptrs_to_free->ptrs) \
+ / sizeof (ptrs_to_free->ptrs[0]))) \
+ { \
+ struct ptrs_to_free *new_ptrs = alloca (sizeof (*ptrs_to_free)); \
+ new_ptrs->count = 0; \
+ new_ptrs->next = ptrs_to_free; \
+ ptrs_to_free = new_ptrs; \
+ } \
+ ptrs_to_free->ptrs[ptrs_to_free->count++] = (ptr); \
+ } \
+ while (0)
+#define ARGCHECK(s, format) \
+ do \
+ { \
+ /* Check file argument for consistence. */ \
+ CHECK_FILE (s, EOF); \
+ if (s->_flags & _IO_NO_READS) \
+ { \
+ __set_errno (EBADF); \
+ return EOF; \
+ } \
+ else if (format == NULL) \
+ { \
+ __set_errno (EINVAL); \
+ return EOF; \
+ } \
+ } while (0)
+#define LOCK_STREAM(S) \
+ __libc_cleanup_region_start (1, (void (*) (void *)) &_IO_funlockfile, (S)); \
+ _IO_flockfile (S)
+#define UNLOCK_STREAM(S) \
+ _IO_funlockfile (S); \
+ __libc_cleanup_region_end (0)
+
+struct ptrs_to_free
+{
+ size_t count;
+ struct ptrs_to_free *next;
+ char **ptrs[32];
+};
+
+struct char_buffer {
+ CHAR_T *current;
+ CHAR_T *end;
+ struct scratch_buffer scratch;
+};
+
+/* Returns a pointer to the first CHAR_T object in the buffer. Only
+ valid if char_buffer_add (BUFFER, CH) has been called and
+ char_buffer_error (BUFFER) is false. */
+static inline CHAR_T *
+char_buffer_start (const struct char_buffer *buffer)
+{
+ return (CHAR_T *) buffer->scratch.data;
+}
+
+/* Returns the number of CHAR_T objects in the buffer. Only valid if
+ char_buffer_error (BUFFER) is false. */
+static inline size_t
+char_buffer_size (const struct char_buffer *buffer)
+{
+ return buffer->current - char_buffer_start (buffer);
+}
+
+/* Reinitializes BUFFER->current and BUFFER->end to cover the entire
+ scratch buffer. */
+static inline void
+char_buffer_rewind (struct char_buffer *buffer)
+{
+ buffer->current = char_buffer_start (buffer);
+ buffer->end = buffer->current + buffer->scratch.length / sizeof (CHAR_T);
+}
+
+/* Returns true if a previous call to char_buffer_add (BUFFER, CH)
+ failed. */
+static inline bool
+char_buffer_error (const struct char_buffer *buffer)
+{
+ return __glibc_unlikely (buffer->current == NULL);
+}
+
+/* Slow path for char_buffer_add. */
+static void
+char_buffer_add_slow (struct char_buffer *buffer, CHAR_T ch)
+{
+ if (char_buffer_error (buffer))
+ return;
+ size_t offset = buffer->end - (CHAR_T *) buffer->scratch.data;
+ if (!scratch_buffer_grow_preserve (&buffer->scratch))
+ {
+ buffer->current = NULL;
+ buffer->end = NULL;
+ return;
+ }
+ char_buffer_rewind (buffer);
+ buffer->current += offset;
+ *buffer->current++ = ch;
+}
+
+/* Adds CH to BUFFER. This function does not report any errors, check
+ for them with char_buffer_error. */
+static inline void
+char_buffer_add (struct char_buffer *buffer, CHAR_T ch)
+ __attribute__ ((always_inline));
+static inline void
+char_buffer_add (struct char_buffer *buffer, CHAR_T ch)
+{
+ if (__glibc_unlikely (buffer->current == buffer->end))
+ char_buffer_add_slow (buffer, ch);
+ else
+ *buffer->current++ = ch;
+}
+
+/* Read formatted input from S according to the format string
+ FORMAT, using the argument list in ARG.
+ Return the number of assignments made, or -1 for an input error. */
+#ifdef COMPILE_WSCANF
+int
+__vfwscanf_internal (FILE *s, const wchar_t *format, va_list argptr,
+ unsigned int mode_flags)
+#else
+int
+__vfscanf_internal (FILE *s, const char *format, va_list argptr,
+ unsigned int mode_flags)
+#endif
+{
+ va_list arg;
+ const CHAR_T *f = format;
+ UCHAR_T fc; /* Current character of the format. */
+ WINT_T done = 0; /* Assignments done. */
+ size_t read_in = 0; /* Chars read in. */
+ WINT_T c = 0; /* Last char read. */
+ int width; /* Maximum field width. */
+ int flags; /* Modifiers for current format element. */
+#ifndef COMPILE_WSCANF
+ locale_t loc = _NL_CURRENT_LOCALE;
+ struct __locale_data *const curctype = loc->__locales[LC_CTYPE];
+#endif
+
+ /* Errno of last failed inchar call. */
+ int inchar_errno = 0;
+ /* Status for reading F-P nums. */
+ char got_digit, got_dot, got_e, got_sign;
+ /* If a [...] is a [^...]. */
+ CHAR_T not_in;
+#define exp_char not_in
+ /* Base for integral numbers. */
+ int base;
+ /* Decimal point character. */
+#ifdef COMPILE_WSCANF
+ wint_t decimal;
+#else
+ const char *decimal;
+#endif
+ /* The thousands character of the current locale. */
+#ifdef COMPILE_WSCANF
+ wint_t thousands;
+#else
+ const char *thousands;
+#endif
+ struct ptrs_to_free *ptrs_to_free = NULL;
+ /* State for the conversions. */
+ mbstate_t state;
+ /* Integral holding variables. */
+ union
+ {
+ long long int q;
+ unsigned long long int uq;
+ long int l;
+ unsigned long int ul;
+ } num;
+ /* Character-buffer pointer. */
+ char *str = NULL;
+ wchar_t *wstr = NULL;
+ char **strptr = NULL;
+ ssize_t strsize = 0;
+ /* We must not react on white spaces immediately because they can
+ possibly be matched even if in the input stream no character is
+ available anymore. */
+ int skip_space = 0;
+ /* Workspace. */
+ CHAR_T *tw; /* Temporary pointer. */
+ struct char_buffer charbuf;
+ scratch_buffer_init (&charbuf.scratch);
+
+ /* Temporarily honor the environmental mode bits. */
+ if (__ldbl_is_dbl)
+ mode_flags |= SCANF_LDBL_IS_DBL;
+ if (s->_flags2 & _IO_FLAGS2_SCANF_STD)
+ mode_flags |= SCANF_ISOC99_A;
+
+#ifdef __va_copy
+ __va_copy (arg, argptr);
+#else
+ arg = (va_list) argptr;
+#endif
+
+#ifdef ORIENT
+ ORIENT;
+#endif
+
+ ARGCHECK (s, format);
+
+ {
+#ifndef COMPILE_WSCANF
+ struct __locale_data *const curnumeric = loc->__locales[LC_NUMERIC];
+#endif
+
+ /* Figure out the decimal point character. */
+#ifdef COMPILE_WSCANF
+ decimal = _NL_CURRENT_WORD (LC_NUMERIC, _NL_NUMERIC_DECIMAL_POINT_WC);
+#else
+ decimal = curnumeric->values[_NL_ITEM_INDEX (DECIMAL_POINT)].string;
+#endif
+ /* Figure out the thousands separator character. */
+#ifdef COMPILE_WSCANF
+ thousands = _NL_CURRENT_WORD (LC_NUMERIC, _NL_NUMERIC_THOUSANDS_SEP_WC);
+#else
+ thousands = curnumeric->values[_NL_ITEM_INDEX (THOUSANDS_SEP)].string;
+ if (*thousands == '\0')
+ thousands = NULL;
+#endif
+ }
+
+ /* Lock the stream. */
+ LOCK_STREAM (s);
+
+
+#ifndef COMPILE_WSCANF
+ /* From now on we use `state' to convert the format string. */
+ memset (&state, '\0', sizeof (state));
+#endif
+
+ /* Run through the format string. */
+ while (*f != '\0')
+ {
+ unsigned int argpos;
+ /* Extract the next argument, which is of type TYPE.
+ For a %N$... spec, this is the Nth argument from the beginning;
+ otherwise it is the next argument after the state now in ARG. */
+#ifdef __va_copy
+# define ARG(type) (argpos == 0 ? va_arg (arg, type) : \
+ ({ unsigned int pos = argpos; \
+ va_list arg; \
+ __va_copy (arg, argptr); \
+ while (--pos > 0) \
+ (void) va_arg (arg, void *); \
+ va_arg (arg, type); \
+ }))
+#else
+# if 0
+ /* XXX Possible optimization. */
+# define ARG(type) (argpos == 0 ? va_arg (arg, type) : \
+ ({ va_list arg = (va_list) argptr; \
+ arg = (va_list) ((char *) arg \
+ + (argpos - 1) \
+ * __va_rounded_size (void *)); \
+ va_arg (arg, type); \
+ }))
+# else
+# define ARG(type) (argpos == 0 ? va_arg (arg, type) : \
+ ({ unsigned int pos = argpos; \
+ va_list arg = (va_list) argptr; \
+ while (--pos > 0) \
+ (void) va_arg (arg, void *); \
+ va_arg (arg, type); \
+ }))
+# endif
+#endif
+
+#ifndef COMPILE_WSCANF
+ if (!isascii ((unsigned char) *f))
+ {
+ /* Non-ASCII, may be a multibyte. */
+ int len = __mbrlen (f, strlen (f), &state);
+ if (len > 0)
+ {
+ do
+ {
+ c = inchar ();
+ if (__glibc_unlikely (c == EOF))
+ input_error ();
+ else if (c != (unsigned char) *f++)
+ {
+ ungetc_not_eof (c, s);
+ conv_error ();
+ }
+ }
+ while (--len > 0);
+ continue;
+ }
+ }
+#endif
+
+ fc = *f++;
+ if (fc != '%')
+ {
+ /* Remember to skip spaces. */
+ if (ISSPACE (fc))
+ {
+ skip_space = 1;
+ continue;
+ }
+
+ /* Read a character. */
+ c = inchar ();
+
+ /* Characters other than format specs must just match. */
+ if (__glibc_unlikely (c == EOF))
+ input_error ();
+
+ /* We saw white space char as the last character in the format
+ string. Now it's time to skip all leading white space. */
+ if (skip_space)
+ {
+ while (ISSPACE (c))
+ if (__glibc_unlikely (inchar () == EOF))
+ input_error ();
+ skip_space = 0;
+ }
+
+ if (__glibc_unlikely (c != fc))
+ {
+ ungetc (c, s);
+ conv_error ();
+ }
+
+ continue;
+ }
+
+ /* This is the start of the conversion string. */
+ flags = 0;
+
+ /* Initialize state of modifiers. */
+ argpos = 0;
+
+ /* Prepare temporary buffer. */
+ char_buffer_rewind (&charbuf);
+
+ /* Check for a positional parameter specification. */
+ if (ISDIGIT ((UCHAR_T) *f))
+ {
+ argpos = read_int ((const UCHAR_T **) &f);
+ if (*f == L_('$'))
+ ++f;
+ else
+ {
+ /* Oops; that was actually the field width. */
+ width = argpos;
+ argpos = 0;
+ goto got_width;
+ }
+ }
+
+ /* 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_('*'):
+ flags |= SUPPRESS;
+ break;
+ case L_('\''):
+#ifdef COMPILE_WSCANF
+ if (thousands != L'\0')
+#else
+ if (thousands != NULL)
+#endif
+ flags |= GROUP;
+ break;
+ case L_('I'):
+ flags |= I18N;
+ break;
+ }
+
+ /* Find the maximum field width. */
+ width = 0;
+ if (ISDIGIT ((UCHAR_T) *f))
+ width = read_int ((const UCHAR_T **) &f);
+ got_width:
+ if (width == 0)
+ width = -1;
+
+ /* Check for type modifiers. */
+ switch (*f++)
+ {
+ case L_('h'):
+ /* ints are short ints or chars. */
+ if (*f == L_('h'))
+ {
+ ++f;
+ flags |= CHAR;
+ }
+ else
+ flags |= SHORT;
+ break;
+ case L_('l'):
+ if (*f == L_('l'))
+ {
+ /* A double `l' is equivalent to an `L'. */
+ ++f;
+ flags |= LONGDBL | LONG;
+ }
+ else
+ /* ints are long ints. */
+ flags |= LONG;
+ break;
+ case L_('q'):
+ case L_('L'):
+ /* doubles are long doubles, and ints are long long ints. */
+ flags |= LONGDBL | LONG;
+ break;
+ case L_('a'):
+ /* The `a' is used as a flag only if followed by `s', `S' or
+ `['. */
+ if (*f != L_('s') && *f != L_('S') && *f != L_('['))
+ {
+ --f;
+ break;
+ }
+ /* In __isoc99_*scanf %as, %aS and %a[ extension is not
+ supported at all. */
+ if (__glibc_likely ((mode_flags & SCANF_ISOC99_A) != 0))
+ {
+ --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;
+ if (*f == L_('l'))
+ {
+ ++f;
+ flags |= LONG;
+ }
+ break;
+ case L_('z'):
+ if (need_longlong && sizeof (size_t) > sizeof (unsigned long int))
+ flags |= LONGDBL;
+ else if (sizeof (size_t) > sizeof (unsigned int))
+ flags |= LONG;
+ break;
+ case L_('j'):
+ if (need_longlong && sizeof (uintmax_t) > sizeof (unsigned long int))
+ flags |= LONGDBL;
+ else if (sizeof (uintmax_t) > sizeof (unsigned int))
+ flags |= LONG;
+ break;
+ case L_('t'):
+ if (need_longlong && sizeof (ptrdiff_t) > sizeof (long int))
+ flags |= LONGDBL;
+ else if (sizeof (ptrdiff_t) > sizeof (int))
+ flags |= LONG;
+ break;
+ default:
+ /* Not a recognized modifier. Backup. */
+ --f;
+ break;
+ }
+
+ /* End of the format string? */
+ if (__glibc_unlikely (*f == L_('\0')))
+ conv_error ();
+
+ /* Find the conversion specifier. */
+ fc = *f++;
+ if (skip_space || (fc != L_('[') && fc != L_('c')
+ && fc != L_('C') && fc != L_('n')))
+ {
+ /* Eat whitespace. */
+ int save_errno = errno;
+ __set_errno (0);
+ do
+ /* We add the additional test for EOF here since otherwise
+ inchar will restore the old errno value which might be
+ EINTR but does not indicate an interrupt since nothing
+ was read at this time. */
+ if (__builtin_expect ((c == EOF || inchar () == EOF)
+ && errno == EINTR, 0))
+ input_error ();
+ while (ISSPACE (c));
+ __set_errno (save_errno);
+ ungetc (c, s);
+ skip_space = 0;
+ }
+
+ switch (fc)
+ {
+ case L_('%'): /* Must match a literal '%'. */
+ c = inchar ();
+ if (__glibc_unlikely (c == EOF))
+ input_error ();
+ if (__glibc_unlikely (c != fc))
+ {
+ ungetc_not_eof (c, s);
+ conv_error ();
+ }
+ break;
+
+ case L_('n'): /* Answer number of assignments done. */
+ /* Corrigendum 1 to ISO C 1990 describes the allowed flags
+ with the 'n' conversion specifier. */
+ if (!(flags & SUPPRESS))
+ {
+ /* Don't count the read-ahead. */
+ if (need_longlong && (flags & LONGDBL))
+ *ARG (long long int *) = read_in;
+ else if (need_long && (flags & LONG))
+ *ARG (long int *) = read_in;
+ else if (flags & SHORT)
+ *ARG (short int *) = read_in;
+ else if (!(flags & CHAR))
+ *ARG (int *) = read_in;
+ else
+ *ARG (char *) = read_in;
+
+#ifdef NO_BUG_IN_ISO_C_CORRIGENDUM_1
+ /* We have a severe problem here. The ISO C standard
+ contradicts itself in explaining the effect of the %n
+ format in `scanf'. While in ISO C:1990 and the ISO C
+ Amendement 1:1995 the result is described as
+
+ Execution of a %n directive does not effect the
+ assignment count returned at the completion of
+ execution of the f(w)scanf function.
+
+ in ISO C Corrigendum 1:1994 the following was added:
+
+ Subclause 7.9.6.2
+ Add the following fourth example:
+ In:
+ #include <stdio.h>
+ int d1, d2, n1, n2, i;
+ i = sscanf("123", "%d%n%n%d", &d1, &n1, &n2, &d2);
+ the value 123 is assigned to d1 and the value3 to n1.
+ Because %n can never get an input failure the value
+ of 3 is also assigned to n2. The value of d2 is not
+ affected. The value 3 is assigned to i.
+
+ We go for now with the historically correct code from ISO C,
+ i.e., we don't count the %n assignments. When it ever
+ should proof to be wrong just remove the #ifdef above. */
+ ++done;
+#endif
+ }
+ break;
+
+ case L_('c'): /* Match characters. */
+ if ((flags & LONG) == 0)
+ {
+ 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) \
+ { \
+ done = EOF; \
+ goto errout; \
+ } \
+ } \
+ 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 (__glibc_unlikely (c == EOF))
+ input_error ();
+
+#ifdef COMPILE_WSCANF
+ /* We have to convert the wide character(s) into multibyte
+ characters and store the result. */
+ memset (&state, '\0', sizeof (state));
+
+ do
+ {
+ size_t n;
+
+ if (!(flags & SUPPRESS) && (flags & POSIX_MALLOC)
+ && *strptr + strsize - str <= MB_LEN_MAX)
+ {
+ /* 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_LEN_MAX);
+ if (newstr == NULL)
+ {
+ /* c can't have `a' flag, only `m'. */
+ done = EOF;
+ goto errout;
+ }
+ else
+ {
+ *strptr = newstr;
+ str = newstr + strleng;
+ strsize = strleng + MB_LEN_MAX;
+ }
+ }
+ else
+ {
+ *strptr = newstr;
+ str = newstr + strleng;
+ strsize *= 2;
+ }
+ }
+
+ n = __wcrtomb (!(flags & SUPPRESS) ? str : NULL, c, &state);
+ if (__glibc_unlikely (n == (size_t) -1))
+ /* No valid wide character. */
+ input_error ();
+
+ /* Increment the output pointer. Even if we don't
+ write anything. */
+ str += n;
+ }
+ while (--width > 0 && inchar () != EOF);
+#else
+ if (!(flags & SUPPRESS))
+ {
+ do
+ {
+ 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'. */
+ done = EOF;
+ goto errout;
+ }
+ else
+ {
+ *strptr = (char *) str;
+ str += strsize;
+ ++strsize;
+ }
+ }
+ else
+ {
+ *strptr = (char *) str;
+ str += strsize;
+ strsize = newsize;
+ }
+ }
+ *str++ = c;
+ }
+ while (--width > 0 && inchar () != EOF);
+ }
+ else
+ while (--width > 0 && inchar () != EOF);
+#endif
+
+ if (!(flags & SUPPRESS))
+ {
+ 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 (width == -1)
+ width = 1;
+
+ STRING_ARG (wstr, wchar_t, (width > 1024 ? 1024 : width));
+
+ c = inchar ();
+ if (__glibc_unlikely (c == EOF))
+ input_error ();
+
+#ifdef COMPILE_WSCANF
+ /* Just store the incoming wide characters. */
+ if (!(flags & SUPPRESS))
+ {
+ do
+ {
+ 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. */
+ done = EOF;
+ goto errout;
+ }
+ else
+ {
+ *strptr = (char *) wstr;
+ wstr += strsize;
+ ++strsize;
+ }
+ }
+ else
+ {
+ *strptr = (char *) wstr;
+ wstr += strsize;
+ strsize = newsize;
+ }
+ }
+ *wstr++ = c;
+ }
+ while (--width > 0 && inchar () != EOF);
+ }
+ else
+ while (--width > 0 && inchar () != EOF);
+#else
+ {
+ /* We have to convert the multibyte input sequence to wide
+ characters. */
+ char buf[1];
+ mbstate_t cstate;
+
+ memset (&cstate, '\0', sizeof (cstate));
+
+ do
+ {
+ /* 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. */
+ done = EOF;
+ goto errout;
+ }
+ else
+ {
+ *strptr = (char *) wstr;
+ wstr += strsize;
+ ++strsize;
+ }
+ }
+ else
+ {
+ *strptr = (char *) wstr;
+ wstr += strsize;
+ strsize = newsize;
+ }
+ }
+
+ while (1)
+ {
+ size_t n;
+
+ n = __mbrtowc (!(flags & SUPPRESS) ? wstr : NULL,
+ buf, 1, &cstate);
+
+ if (n == (size_t) -2)
+ {
+ /* Possibly correct character, just not enough
+ input. */
+ if (__glibc_unlikely (inchar () == EOF))
+ encode_error ();
+
+ buf[0] = c;
+ continue;
+ }
+
+ if (__glibc_unlikely (n != 1))
+ encode_error ();
+
+ /* We have a match. */
+ break;
+ }
+
+ /* Advance the result pointer. */
+ ++wstr;
+ }
+ while (--width > 0 && inchar () != EOF);
+ }
+#endif
+
+ if (!(flags & SUPPRESS))
+ {
+ 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))
+ {
+ STRING_ARG (str, char, 100);
+
+ c = inchar ();
+ if (__glibc_unlikely (c == EOF))
+ input_error ();
+
+#ifdef COMPILE_WSCANF
+ memset (&state, '\0', sizeof (state));
+#endif
+
+ do
+ {
+ if (ISSPACE (c))
+ {
+ ungetc_not_eof (c, s);
+ break;
+ }
+
+#ifdef COMPILE_WSCANF
+ /* This is quite complicated. We have to convert the
+ wide characters into multibyte characters and then
+ store them. */
+ {
+ size_t n;
+
+ if (!(flags & SUPPRESS) && (flags & MALLOC)
+ && *strptr + strsize - str <= MB_LEN_MAX)
+ {
+ /* We have to enlarge the buffer if the `a' or `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_LEN_MAX);
+ if (newstr == NULL)
+ {
+ if (flags & POSIX_MALLOC)
+ {
+ done = EOF;
+ goto errout;
+ }
+ /* We lose. Oh well. Terminate the
+ string and stop converting,
+ so at least we don't skip any input. */
+ ((char *) (*strptr))[strleng] = '\0';
+ strptr = NULL;
+ ++done;
+ conv_error ();
+ }
+ else
+ {
+ *strptr = newstr;
+ str = newstr + strleng;
+ strsize = strleng + MB_LEN_MAX;
+ }
+ }
+ else
+ {
+ *strptr = newstr;
+ str = newstr + strleng;
+ strsize *= 2;
+ }
+ }
+
+ n = __wcrtomb (!(flags & SUPPRESS) ? str : NULL, c,
+ &state);
+ if (__glibc_unlikely (n == (size_t) -1))
+ encode_error ();
+
+ assert (n <= MB_LEN_MAX);
+ str += n;
+ }
+#else
+ /* This is easy. */
+ if (!(flags & SUPPRESS))
+ {
+ *str++ = c;
+ if ((flags & MALLOC)
+ && (char *) str == *strptr + strsize)
+ {
+ /* Enlarge the buffer. */
+ str = (char *) realloc (*strptr, 2 * strsize);
+ if (str == NULL)
+ {
+ /* Can't allocate that much. Last-ditch
+ effort. */
+ str = (char *) realloc (*strptr, strsize + 1);
+ if (str == NULL)
+ {
+ if (flags & POSIX_MALLOC)
+ {
+ done = EOF;
+ goto errout;
+ }
+ /* We lose. Oh well. Terminate the
+ string and stop converting,
+ so at least we don't skip any input. */
+ ((char *) (*strptr))[strsize - 1] = '\0';
+ strptr = NULL;
+ ++done;
+ conv_error ();
+ }
+ else
+ {
+ *strptr = (char *) str;
+ str += strsize;
+ ++strsize;
+ }
+ }
+ else
+ {
+ *strptr = (char *) str;
+ str += strsize;
+ strsize *= 2;
+ }
+ }
+ }
+#endif
+ }
+ while ((width <= 0 || --width > 0) && inchar () != EOF);
+
+ if (!(flags & SUPPRESS))
+ {
+#ifdef COMPILE_WSCANF
+ /* We have to emit the code to get into the initial
+ state. */
+ char buf[MB_LEN_MAX];
+ size_t n = __wcrtomb (buf, L'\0', &state);
+ if (n > 0 && (flags & MALLOC)
+ && str + n >= *strptr + strsize)
+ {
+ /* Enlarge the buffer. */
+ size_t strleng = str - *strptr;
+ char *newstr;
+
+ newstr = (char *) realloc (*strptr, strleng + n + 1);
+ if (newstr == NULL)
+ {
+ if (flags & POSIX_MALLOC)
+ {
+ done = EOF;
+ goto errout;
+ }
+ /* We lose. Oh well. Terminate the string
+ and stop converting, so at least we don't
+ skip any input. */
+ ((char *) (*strptr))[strleng] = '\0';
+ strptr = NULL;
+ ++done;
+ conv_error ();
+ }
+ else
+ {
+ *strptr = newstr;
+ str = newstr + strleng;
+ strsize = strleng + n + 1;
+ }
+ }
+
+ str = __mempcpy (str, buf, n);
+#endif
+ *str++ = '\0';
+
+ 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_('S'):
+ {
+#ifndef COMPILE_WSCANF
+ mbstate_t cstate;
+#endif
+
+ /* Wide character string. */
+ STRING_ARG (wstr, wchar_t, 100);
+
+ c = inchar ();
+ if (__builtin_expect (c == EOF, 0))
+ input_error ();
+
+#ifndef COMPILE_WSCANF
+ memset (&cstate, '\0', sizeof (cstate));
+#endif
+
+ do
+ {
+ if (ISSPACE (c))
+ {
+ ungetc_not_eof (c, s);
+ break;
+ }
+
+#ifdef COMPILE_WSCANF
+ /* This is easy. */
+ if (!(flags & SUPPRESS))
+ {
+ *wstr++ = c;
+ if ((flags & MALLOC)
+ && wstr == (wchar_t *) *strptr + strsize)
+ {
+ /* Enlarge the buffer. */
+ wstr = (wchar_t *) realloc (*strptr,
+ (2 * strsize)
+ * 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)
+ {
+ if (flags & POSIX_MALLOC)
+ {
+ done = EOF;
+ goto errout;
+ }
+ /* We lose. Oh well. Terminate the string
+ and stop converting, so at least we don't
+ skip any input. */
+ ((wchar_t *) (*strptr))[strsize - 1] = L'\0';
+ strptr = NULL;
+ ++done;
+ conv_error ();
+ }
+ else
+ {
+ *strptr = (char *) wstr;
+ wstr += strsize;
+ ++strsize;
+ }
+ }
+ else
+ {
+ *strptr = (char *) wstr;
+ wstr += strsize;
+ strsize *= 2;
+ }
+ }
+ }
+#else
+ {
+ char buf[1];
+
+ buf[0] = c;
+
+ while (1)
+ {
+ size_t n;
+
+ n = __mbrtowc (!(flags & SUPPRESS) ? wstr : NULL,
+ buf, 1, &cstate);
+
+ if (n == (size_t) -2)
+ {
+ /* Possibly correct character, just not enough
+ input. */
+ if (__glibc_unlikely (inchar () == EOF))
+ encode_error ();
+
+ buf[0] = c;
+ continue;
+ }
+
+ if (__glibc_unlikely (n != 1))
+ encode_error ();
+
+ /* We have a match. */
+ ++wstr;
+ break;
+ }
+
+ if (!(flags & SUPPRESS) && (flags & MALLOC)
+ && wstr == (wchar_t *) *strptr + strsize)
+ {
+ /* Enlarge the buffer. */
+ wstr = (wchar_t *) realloc (*strptr,
+ (2 * strsize
+ * 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)
+ {
+ if (flags & POSIX_MALLOC)
+ {
+ done = EOF;
+ goto errout;
+ }
+ /* We lose. Oh well. Terminate the
+ string and stop converting, so at
+ least we don't skip any input. */
+ ((wchar_t *) (*strptr))[strsize - 1] = L'\0';
+ strptr = NULL;
+ ++done;
+ conv_error ();
+ }
+ else
+ {
+ *strptr = (char *) wstr;
+ wstr += strsize;
+ ++strsize;
+ }
+ }
+ else
+ {
+ *strptr = (char *) wstr;
+ wstr += strsize;
+ strsize *= 2;
+ }
+ }
+ }
+#endif
+ }
+ while ((width <= 0 || --width > 0) && inchar () != EOF);
+
+ if (!(flags & SUPPRESS))
+ {
+ *wstr++ = L'\0';
+
+ 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_('x'): /* Hexadecimal integer. */
+ case L_('X'): /* Ditto. */
+ base = 16;
+ goto number;
+
+ case L_('o'): /* Octal integer. */
+ base = 8;
+ goto number;
+
+ case L_('u'): /* Unsigned decimal integer. */
+ base = 10;
+ goto number;
+
+ case L_('d'): /* Signed decimal integer. */
+ base = 10;
+ flags |= NUMBER_SIGNED;
+ goto number;
+
+ case L_('i'): /* Generic number. */
+ base = 0;
+ flags |= NUMBER_SIGNED;
+
+ number:
+ c = inchar ();
+ if (__glibc_unlikely (c == EOF))
+ input_error ();
+
+ /* Check for a sign. */
+ if (c == L_('-') || c == L_('+'))
+ {
+ char_buffer_add (&charbuf, c);
+ if (width > 0)
+ --width;
+ c = inchar ();
+ }
+
+ /* Look for a leading indication of base. */
+ if (width != 0 && c == L_('0'))
+ {
+ if (width > 0)
+ --width;
+
+ char_buffer_add (&charbuf, c);
+ c = inchar ();
+
+ if (width != 0 && TOLOWER (c) == L_('x'))
+ {
+ if (base == 0)
+ base = 16;
+ if (base == 16)
+ {
+ if (width > 0)
+ --width;
+ c = inchar ();
+ }
+ }
+ else if (base == 0)
+ base = 8;
+ }
+
+ if (base == 0)
+ base = 10;
+
+ if (base == 10 && __builtin_expect ((flags & I18N) != 0, 0))
+ {
+ int from_level;
+ int to_level;
+ int level;
+#ifdef COMPILE_WSCANF
+ const wchar_t *wcdigits[10];
+ const wchar_t *wcdigits_extended[10];
+#else
+ const char *mbdigits[10];
+ const char *mbdigits_extended[10];
+#endif
+ /* "to_inpunct" is a map from ASCII digits to their
+ equivalent in locale. This is defined for locales
+ which use an extra digits set. */
+ wctrans_t map = __wctrans ("to_inpunct");
+ int n;
+
+ from_level = 0;
+#ifdef COMPILE_WSCANF
+ to_level = _NL_CURRENT_WORD (LC_CTYPE,
+ _NL_CTYPE_INDIGITS_WC_LEN) - 1;
+#else
+ to_level = (uint32_t) curctype->values[_NL_ITEM_INDEX (_NL_CTYPE_INDIGITS_MB_LEN)].word - 1;
+#endif
+
+ /* Get the alternative digit forms if there are any. */
+ if (__glibc_unlikely (map != NULL))
+ {
+ /* Adding new level for extra digits set in locale file. */
+ ++to_level;
+
+ for (n = 0; n < 10; ++n)
+ {
+#ifdef COMPILE_WSCANF
+ wcdigits[n] = (const wchar_t *)
+ _NL_CURRENT (LC_CTYPE, _NL_CTYPE_INDIGITS0_WC + n);
+
+ wchar_t *wc_extended = (wchar_t *)
+ alloca ((to_level + 2) * sizeof (wchar_t));
+ __wmemcpy (wc_extended, wcdigits[n], to_level);
+ wc_extended[to_level] = __towctrans (L'0' + n, map);
+ wc_extended[to_level + 1] = '\0';
+ wcdigits_extended[n] = wc_extended;
+#else
+ mbdigits[n]
+ = curctype->values[_NL_CTYPE_INDIGITS0_MB + n].string;
+
+ /* Get the equivalent wide char in map. */
+ wint_t extra_wcdigit = __towctrans (L'0' + n, map);
+
+ /* Convert it to multibyte representation. */
+ mbstate_t state;
+ memset (&state, '\0', sizeof (state));
+
+ char extra_mbdigit[MB_LEN_MAX];
+ size_t mblen
+ = __wcrtomb (extra_mbdigit, extra_wcdigit, &state);
+
+ if (mblen == (size_t) -1)
+ {
+ /* Ignore this new level. */
+ map = NULL;
+ break;
+ }
+
+ /* Calculate the length of mbdigits[n]. */
+ const char *last_char = mbdigits[n];
+ for (level = 0; level < to_level; ++level)
+ last_char = strchr (last_char, '\0') + 1;
+
+ size_t mbdigits_len = last_char - mbdigits[n];
+
+ /* Allocate memory for extended multibyte digit. */
+ char *mb_extended;
+ mb_extended = (char *) alloca (mbdigits_len + mblen + 1);
+
+ /* And get the mbdigits + extra_digit string. */
+ *(char *) __mempcpy (__mempcpy (mb_extended, mbdigits[n],
+ mbdigits_len),
+ extra_mbdigit, mblen) = '\0';
+ mbdigits_extended[n] = mb_extended;
+#endif
+ }
+ }
+
+ /* Read the number into workspace. */
+ while (c != EOF && width != 0)
+ {
+ /* 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)
+ {
+ /* Get the string for the digits with value N. */
+#ifdef COMPILE_WSCANF
+
+ /* wcdigits_extended[] is fully set in the loop
+ above, but the test for "map != NULL" is done
+ inside the loop here and outside the loop there. */
+ DIAG_PUSH_NEEDS_COMMENT;
+ DIAG_IGNORE_NEEDS_COMMENT (4.7, "-Wmaybe-uninitialized");
+
+ if (__glibc_unlikely (map != NULL))
+ wcdigits[n] = wcdigits_extended[n];
+ else
+ wcdigits[n] = (const wchar_t *)
+ _NL_CURRENT (LC_CTYPE, _NL_CTYPE_INDIGITS0_WC + n);
+ wcdigits[n] += from_level;
+
+ DIAG_POP_NEEDS_COMMENT;
+
+ if (c == (wint_t) *wcdigits[n])
+ {
+ to_level = from_level;
+ break;
+ }
+
+ /* Advance the pointer to the next string. */
+ ++wcdigits[n];
+#else
+ const char *cmpp;
+ int avail = width > 0 ? width : INT_MAX;
+
+ if (__glibc_unlikely (map != NULL))
+ mbdigits[n] = mbdigits_extended[n];
+ else
+ mbdigits[n]
+ = curctype->values[_NL_CTYPE_INDIGITS0_MB + n].string;
+
+ for (level = 0; level < from_level; level++)
+ mbdigits[n] = strchr (mbdigits[n], '\0') + 1;
+
+ cmpp = mbdigits[n];
+ 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;
+ to_level = from_level;
+ 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;
+ }
+
+ /* Advance the pointer to the next string. */
+ mbdigits[n] = strchr (mbdigits[n], '\0') + 1;
+#endif
+ }
+
+ if (n == 10)
+ {
+ /* Have not yet found the digit. */
+ for (level = from_level + 1; level <= to_level; ++level)
+ {
+ /* Search all ten digits of this level. */
+ for (n = 0; n < 10; ++n)
+ {
+#ifdef COMPILE_WSCANF
+ if (c == (wint_t) *wcdigits[n])
+ break;
+
+ /* Advance the pointer to the next string. */
+ ++wcdigits[n];
+#else
+ const char *cmpp;
+ int avail = width > 0 ? width : INT_MAX;
+
+ cmpp = mbdigits[n];
+ 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;
+ 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;
+ }
+
+ /* Advance the pointer to the next string. */
+ mbdigits[n] = strchr (mbdigits[n], '\0') + 1;
+#endif
+ }
+
+ if (n < 10)
+ {
+ /* Found it. */
+ from_level = level;
+ to_level = level;
+ break;
+ }
+ }
+ }
+
+ if (n < 10)
+ c = L_('0') + n;
+ else if (flags & GROUP)
+ {
+ /* Try matching against the thousands separator. */
+#ifdef COMPILE_WSCANF
+ if (c != thousands)
+ break;
+#else
+ const char *cmpp = thousands;
+ int avail = width > 0 ? width : INT_MAX;
+
+ while ((unsigned char) *cmpp == c && avail >= 0)
+ {
+ char_buffer_add (&charbuf, c);
+ if (*++cmpp == '\0')
+ break;
+ else
+ {
+ if (avail == 0 || inchar () == EOF)
+ break;
+ --avail;
+ }
+ }
+
+ if (char_buffer_error (&charbuf))
+ {
+ __set_errno (ENOMEM);
+ done = EOF;
+ goto errout;
+ }
+
+ if (*cmpp != '\0')
+ {
+ /* We are pushing all read characters back. */
+ if (cmpp > thousands)
+ {
+ charbuf.current -= cmpp - thousands;
+ ungetc (c, s);
+ while (--cmpp > thousands)
+ ungetc_not_eof ((unsigned char) *cmpp, s);
+ c = (unsigned char) *cmpp;
+ }
+ break;
+ }
+
+ if (width > 0)
+ width = avail;
+
+ /* The last thousands character will be added back by
+ the char_buffer_add below. */
+ --charbuf.current;
+#endif
+ }
+ else
+ break;
+
+ char_buffer_add (&charbuf, c);
+ if (width > 0)
+ --width;
+
+ c = inchar ();
+ }
+ }
+ else
+ /* Read the number into workspace. */
+ while (c != EOF && width != 0)
+ {
+ if (base == 16)
+ {
+ if (!ISXDIGIT (c))
+ break;
+ }
+ else if (!ISDIGIT (c) || (int) (c - L_('0')) >= base)
+ {
+ if (base == 10 && (flags & GROUP))
+ {
+ /* Try matching against the thousands separator. */
+#ifdef COMPILE_WSCANF
+ if (c != thousands)
+ break;
+#else
+ const char *cmpp = thousands;
+ int avail = width > 0 ? width : INT_MAX;
+
+ while ((unsigned char) *cmpp == c && avail >= 0)
+ {
+ char_buffer_add (&charbuf, c);
+ if (*++cmpp == '\0')
+ break;
+ else
+ {
+ if (avail == 0 || inchar () == EOF)
+ break;
+ --avail;
+ }
+ }
+
+ if (char_buffer_error (&charbuf))
+ {
+ __set_errno (ENOMEM);
+ done = EOF;
+ goto errout;
+ }
+
+ if (*cmpp != '\0')
+ {
+ /* We are pushing all read characters back. */
+ if (cmpp > thousands)
+ {
+ charbuf.current -= cmpp - thousands;
+ ungetc (c, s);
+ while (--cmpp > thousands)
+ ungetc_not_eof ((unsigned char) *cmpp, s);
+ c = (unsigned char) *cmpp;
+ }
+ break;
+ }
+
+ if (width > 0)
+ width = avail;
+
+ /* The last thousands character will be added back by
+ the char_buffer_add below. */
+ --charbuf.current;
+#endif
+ }
+ else
+ break;
+ }
+ char_buffer_add (&charbuf, c);
+ if (width > 0)
+ --width;
+
+ c = inchar ();
+ }
+
+ if (char_buffer_error (&charbuf))
+ {
+ __set_errno (ENOMEM);
+ done = EOF;
+ goto errout;
+ }
+
+ if (char_buffer_size (&charbuf) == 0
+ || (char_buffer_size (&charbuf) == 1
+ && (char_buffer_start (&charbuf)[0] == L_('+')
+ || char_buffer_start (&charbuf)[0] == L_('-'))))
+ {
+ /* There was no number. If we are supposed to read a pointer
+ we must recognize "(nil)" as well. */
+ if (__builtin_expect (char_buffer_size (&charbuf) == 0
+ && (flags & READ_POINTER)
+ && (width < 0 || width >= 5)
+ && c == '('
+ && TOLOWER (inchar ()) == L_('n')
+ && TOLOWER (inchar ()) == L_('i')
+ && TOLOWER (inchar ()) == L_('l')
+ && inchar () == L_(')'), 1))
+ /* We must produce the value of a NULL pointer. A single
+ '0' digit is enough. */
+ char_buffer_add (&charbuf, L_('0'));
+ else
+ {
+ /* The last read character is not part of the number
+ anymore. */
+ ungetc (c, s);
+
+ conv_error ();
+ }
+ }
+ else
+ /* The just read character is not part of the number anymore. */
+ ungetc (c, s);
+
+ /* Convert the number. */
+ char_buffer_add (&charbuf, L_('\0'));
+ if (char_buffer_error (&charbuf))
+ {
+ __set_errno (ENOMEM);
+ done = EOF;
+ goto errout;
+ }
+ if (need_longlong && (flags & LONGDBL))
+ {
+ if (flags & NUMBER_SIGNED)
+ num.q = __strtoll_internal
+ (char_buffer_start (&charbuf), &tw, base, flags & GROUP);
+ else
+ num.uq = __strtoull_internal
+ (char_buffer_start (&charbuf), &tw, base, flags & GROUP);
+ }
+ else
+ {
+ if (flags & NUMBER_SIGNED)
+ num.l = __strtol_internal
+ (char_buffer_start (&charbuf), &tw, base, flags & GROUP);
+ else
+ num.ul = __strtoul_internal
+ (char_buffer_start (&charbuf), &tw, base, flags & GROUP);
+ }
+ if (__glibc_unlikely (char_buffer_start (&charbuf) == tw))
+ conv_error ();
+
+ if (!(flags & SUPPRESS))
+ {
+ if (flags & NUMBER_SIGNED)
+ {
+ if (need_longlong && (flags & LONGDBL))
+ *ARG (LONGLONG int *) = num.q;
+ else if (need_long && (flags & LONG))
+ *ARG (long int *) = num.l;
+ else if (flags & SHORT)
+ *ARG (short int *) = (short int) num.l;
+ else if (!(flags & CHAR))
+ *ARG (int *) = (int) num.l;
+ else
+ *ARG (signed char *) = (signed char) num.ul;
+ }
+ else
+ {
+ if (need_longlong && (flags & LONGDBL))
+ *ARG (unsigned LONGLONG int *) = num.uq;
+ else if (need_long && (flags & LONG))
+ *ARG (unsigned long int *) = num.ul;
+ else if (flags & SHORT)
+ *ARG (unsigned short int *)
+ = (unsigned short int) num.ul;
+ else if (!(flags & CHAR))
+ *ARG (unsigned int *) = (unsigned int) num.ul;
+ else
+ *ARG (unsigned char *) = (unsigned char) num.ul;
+ }
+ ++done;
+ }
+ break;
+
+ case L_('e'): /* Floating-point numbers. */
+ case L_('E'):
+ case L_('f'):
+ case L_('F'):
+ case L_('g'):
+ case L_('G'):
+ case L_('a'):
+ case L_('A'):
+ c = inchar ();
+ if (width > 0)
+ --width;
+ if (__glibc_unlikely (c == EOF))
+ input_error ();
+
+ got_digit = got_dot = got_e = got_sign = 0;
+
+ /* Check for a sign. */
+ if (c == L_('-') || c == L_('+'))
+ {
+ got_sign = 1;
+ char_buffer_add (&charbuf, c);
+ if (__glibc_unlikely (width == 0 || inchar () == EOF))
+ /* EOF is only an input error before we read any chars. */
+ conv_error ();
+ if (width > 0)
+ --width;
+ }
+
+ /* Take care for the special arguments "nan" and "inf". */
+ if (TOLOWER (c) == L_('n'))
+ {
+ /* Maybe "nan". */
+ char_buffer_add (&charbuf, c);
+ if (__builtin_expect (width == 0
+ || inchar () == EOF
+ || TOLOWER (c) != L_('a'), 0))
+ conv_error ();
+ if (width > 0)
+ --width;
+ char_buffer_add (&charbuf, c);
+ if (__builtin_expect (width == 0
+ || inchar () == EOF
+ || TOLOWER (c) != L_('n'), 0))
+ conv_error ();
+ if (width > 0)
+ --width;
+ char_buffer_add (&charbuf, c);
+ /* It is "nan". */
+ goto scan_float;
+ }
+ else if (TOLOWER (c) == L_('i'))
+ {
+ /* Maybe "inf" or "infinity". */
+ char_buffer_add (&charbuf, c);
+ if (__builtin_expect (width == 0
+ || inchar () == EOF
+ || TOLOWER (c) != L_('n'), 0))
+ conv_error ();
+ if (width > 0)
+ --width;
+ char_buffer_add (&charbuf, c);
+ if (__builtin_expect (width == 0
+ || inchar () == EOF
+ || TOLOWER (c) != L_('f'), 0))
+ conv_error ();
+ if (width > 0)
+ --width;
+ char_buffer_add (&charbuf, c);
+ /* It is as least "inf". */
+ if (width != 0 && inchar () != EOF)
+ {
+ if (TOLOWER (c) == L_('i'))
+ {
+ if (width > 0)
+ --width;
+ /* Now we have to read the rest as well. */
+ char_buffer_add (&charbuf, c);
+ if (__builtin_expect (width == 0
+ || inchar () == EOF
+ || TOLOWER (c) != L_('n'), 0))
+ conv_error ();
+ if (width > 0)
+ --width;
+ char_buffer_add (&charbuf, c);
+ if (__builtin_expect (width == 0
+ || inchar () == EOF
+ || TOLOWER (c) != L_('i'), 0))
+ conv_error ();
+ if (width > 0)
+ --width;
+ char_buffer_add (&charbuf, c);
+ if (__builtin_expect (width == 0
+ || inchar () == EOF
+ || TOLOWER (c) != L_('t'), 0))
+ conv_error ();
+ if (width > 0)
+ --width;
+ char_buffer_add (&charbuf, c);
+ if (__builtin_expect (width == 0
+ || inchar () == EOF
+ || TOLOWER (c) != L_('y'), 0))
+ conv_error ();
+ if (width > 0)
+ --width;
+ char_buffer_add (&charbuf, c);
+ }
+ else
+ /* Never mind. */
+ ungetc (c, s);
+ }
+ goto scan_float;
+ }
+
+ exp_char = L_('e');
+ if (width != 0 && c == L_('0'))
+ {
+ char_buffer_add (&charbuf, c);
+ c = inchar ();
+ if (width > 0)
+ --width;
+ if (width != 0 && TOLOWER (c) == L_('x'))
+ {
+ /* It is a number in hexadecimal format. */
+ char_buffer_add (&charbuf, c);
+
+ flags |= HEXA_FLOAT;
+ exp_char = L_('p');
+
+ /* Grouping is not allowed. */
+ flags &= ~GROUP;
+ c = inchar ();
+ if (width > 0)
+ --width;
+ }
+ else
+ got_digit = 1;
+ }
+
+ while (1)
+ {
+ if (char_buffer_error (&charbuf))
+ {
+ __set_errno (ENOMEM);
+ done = EOF;
+ goto errout;
+ }
+ if (ISDIGIT (c))
+ {
+ char_buffer_add (&charbuf, c);
+ got_digit = 1;
+ }
+ else if (!got_e && (flags & HEXA_FLOAT) && ISXDIGIT (c))
+ {
+ char_buffer_add (&charbuf, c);
+ got_digit = 1;
+ }
+ else if (got_e && charbuf.current[-1] == exp_char
+ && (c == L_('-') || c == L_('+')))
+ char_buffer_add (&charbuf, c);
+ else if (got_digit && !got_e
+ && (CHAR_T) TOLOWER (c) == exp_char)
+ {
+ char_buffer_add (&charbuf, exp_char);
+ got_e = got_dot = 1;
+ }
+ else
+ {
+#ifdef COMPILE_WSCANF
+ if (! got_dot && c == decimal)
+ {
+ char_buffer_add (&charbuf, c);
+ got_dot = 1;
+ }
+ else if ((flags & GROUP) != 0 && ! got_dot && c == thousands)
+ char_buffer_add (&charbuf, c);
+ else
+ {
+ /* The last read character is not part of the number
+ anymore. */
+ ungetc (c, s);
+ break;
+ }
+#else
+ const char *cmpp = decimal;
+ int avail = width > 0 ? width : INT_MAX;
+
+ if (! got_dot)
+ {
+ while ((unsigned char) *cmpp == c && avail >= 0)
+ if (*++cmpp == '\0')
+ break;
+ else
+ {
+ if (avail == 0 || inchar () == EOF)
+ break;
+ --avail;
+ }
+ }
+
+ if (*cmpp == '\0')
+ {
+ /* Add all the characters. */
+ for (cmpp = decimal; *cmpp != '\0'; ++cmpp)
+ char_buffer_add (&charbuf, (unsigned char) *cmpp);
+ if (width > 0)
+ width = avail;
+ got_dot = 1;
+ }
+ else
+ {
+ /* Figure out whether it is a thousands separator.
+ There is one problem: we possibly read more than
+ one character. We cannot push them back but since
+ we know that parts of the `decimal' string matched,
+ we can compare against it. */
+ const char *cmp2p = thousands;
+
+ 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)
+ if (*++cmp2p == '\0')
+ break;
+ else
+ {
+ if (avail == 0 || inchar () == EOF)
+ break;
+ --avail;
+ }
+ }
+ }
+
+ if (cmp2p != NULL && *cmp2p == '\0')
+ {
+ /* Add all the characters. */
+ for (cmpp = thousands; *cmpp != '\0'; ++cmpp)
+ char_buffer_add (&charbuf, (unsigned char) *cmpp);
+ if (width > 0)
+ width = avail;
+ }
+ else
+ {
+ /* The last read character is not part of the number
+ anymore. */
+ ungetc (c, s);
+ break;
+ }
+ }
+#endif
+ }
+
+ if (width == 0 || inchar () == EOF)
+ break;
+
+ if (width > 0)
+ --width;
+ }
+
+ if (char_buffer_error (&charbuf))
+ {
+ __set_errno (ENOMEM);
+ done = EOF;
+ goto errout;
+ }
+
+ 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. */
+ && (char_buffer_size (&charbuf) == got_sign || 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 (char_buffer_size (&charbuf) == got_sign
+ || (char_buffer_size (&charbuf) == got_sign + 1
+ && wcdigits[11] == decimal))
+#else
+ char mbdigits[12][MB_LEN_MAX + 1];
+
+ mbstate_t state;
+ memset (&state, '\0', sizeof (state));
+
+ bool match_so_far = char_buffer_size (&charbuf) == got_sign;
+ size_t mblen = __wcrtomb (mbdigits[11], wcdigits[11], &state);
+ if (mblen != (size_t) -1)
+ {
+ mbdigits[11][mblen] = '\0';
+ match_so_far |=
+ (char_buffer_size (&charbuf) == strlen (decimal) + got_sign
+ && 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 |= (char_buffer_size (&charbuf)
+ == decimal_len + got_sign);
+ 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
+ conversion is done correctly. */
+ while (1)
+ {
+ if (char_buffer_error (&charbuf))
+ {
+ __set_errno (ENOMEM);
+ done = EOF;
+ goto errout;
+ }
+ if (got_e && charbuf.current[-1] == exp_char
+ && (c == L_('-') || c == L_('+')))
+ char_buffer_add (&charbuf, c);
+ else if (char_buffer_size (&charbuf) > got_sign && !got_e
+ && (CHAR_T) TOLOWER (c) == exp_char)
+ {
+ char_buffer_add (&charbuf, 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)
+ char_buffer_add (&charbuf, L_('0') + n);
+ else if (n == 11 && !got_dot)
+ {
+ char_buffer_add (&charbuf, decimal);
+ got_dot = 1;
+ }
+ else if (n == 10 && have_locthousands
+ && ! got_dot)
+ char_buffer_add (&charbuf, 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)
+ char_buffer_add (&charbuf, L_('0') + n);
+ else if (n == 11 && !got_dot)
+ {
+ /* Add all the characters. */
+ for (cmpp = decimal; *cmpp != '\0';
+ ++cmpp)
+ char_buffer_add (&charbuf,
+ (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)
+ char_buffer_add (&charbuf,
+ (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
+ }
+
+ if (char_buffer_error (&charbuf))
+ {
+ __set_errno (ENOMEM);
+ done = EOF;
+ goto errout;
+ }
+
+ /* Have we read any character? If we try to read a number
+ in hexadecimal notation and we have read only the `0x'
+ prefix this is an error. */
+ if (__glibc_unlikely (char_buffer_size (&charbuf) == got_sign
+ || ((flags & HEXA_FLOAT)
+ && (char_buffer_size (&charbuf)
+ == 2 + got_sign))))
+ conv_error ();
+
+ scan_float:
+ /* Convert the number. */
+ char_buffer_add (&charbuf, L_('\0'));
+ if (char_buffer_error (&charbuf))
+ {
+ __set_errno (ENOMEM);
+ done = EOF;
+ goto errout;
+ }
+ if ((flags & LONGDBL) \
+ && __glibc_likely ((mode_flags & SCANF_LDBL_IS_DBL) == 0))
+ {
+ long double d = __strtold_internal
+ (char_buffer_start (&charbuf), &tw, flags & GROUP);
+ if (!(flags & SUPPRESS) && tw != char_buffer_start (&charbuf))
+ *ARG (long double *) = d;
+ }
+ else if (flags & (LONG | LONGDBL))
+ {
+ double d = __strtod_internal
+ (char_buffer_start (&charbuf), &tw, flags & GROUP);
+ if (!(flags & SUPPRESS) && tw != char_buffer_start (&charbuf))
+ *ARG (double *) = d;
+ }
+ else
+ {
+ float d = __strtof_internal
+ (char_buffer_start (&charbuf), &tw, flags & GROUP);
+ if (!(flags & SUPPRESS) && tw != char_buffer_start (&charbuf))
+ *ARG (float *) = d;
+ }
+
+ if (__glibc_unlikely (tw == char_buffer_start (&charbuf)))
+ conv_error ();
+
+ if (!(flags & SUPPRESS))
+ ++done;
+ break;
+
+ case L_('['): /* Character class. */
+ if (flags & LONG)
+ STRING_ARG (wstr, wchar_t, 100);
+ else
+ STRING_ARG (str, char, 100);
+
+ if (*f == L_('^'))
+ {
+ ++f;
+ not_in = 1;
+ }
+ else
+ not_in = 0;
+
+ if (width < 0)
+ /* There is no width given so there is also no limit on the
+ number of characters we read. Therefore we set width to
+ a very high value to make the algorithm easier. */
+ width = INT_MAX;
+
+#ifdef COMPILE_WSCANF
+ /* Find the beginning and the end of the scanlist. We are not
+ creating a lookup table since it would have to be too large.
+ Instead we search each time through the string. This is not
+ a constant lookup time but who uses this feature deserves to
+ be punished. */
+ tw = (wchar_t *) f; /* Marks the beginning. */
+
+ if (*f == L']')
+ ++f;
+
+ while ((fc = *f++) != L'\0' && fc != L']');
+
+ if (__glibc_unlikely (fc == L'\0'))
+ conv_error ();
+ wchar_t *twend = (wchar_t *) f - 1;
+#else
+ /* Fill WP with byte flags indexed by character.
+ We will use this flag map for matching input characters. */
+ if (!scratch_buffer_set_array_size
+ (&charbuf.scratch, UCHAR_MAX + 1, 1))
+ {
+ done = EOF;
+ goto errout;
+ }
+ memset (charbuf.scratch.data, '\0', UCHAR_MAX + 1);
+
+ fc = *f;
+ if (fc == ']' || fc == '-')
+ {
+ /* If ] or - appears before any char in the set, it is not
+ the terminator or separator, but the first char in the
+ set. */
+ ((char *)charbuf.scratch.data)[fc] = 1;
+ ++f;
+ }
+
+ while ((fc = *f++) != '\0' && fc != ']')
+ if (fc == '-' && *f != '\0' && *f != ']'
+ && (unsigned char) f[-2] <= (unsigned char) *f)
+ {
+ /* Add all characters from the one before the '-'
+ up to (but not including) the next format char. */
+ for (fc = (unsigned char) f[-2]; fc < (unsigned char) *f; ++fc)
+ ((char *)charbuf.scratch.data)[fc] = 1;
+ }
+ else
+ /* Add the character to the flag map. */
+ ((char *)charbuf.scratch.data)[fc] = 1;
+
+ if (__glibc_unlikely (fc == '\0'))
+ conv_error();
+#endif
+
+ if (flags & LONG)
+ {
+ size_t now = read_in;
+#ifdef COMPILE_WSCANF
+ if (__glibc_unlikely (inchar () == WEOF))
+ input_error ();
+
+ do
+ {
+ wchar_t *runp;
+
+ /* Test whether it's in the scanlist. */
+ runp = tw;
+ while (runp < twend)
+ {
+ if (runp[0] == L'-' && runp[1] != '\0'
+ && runp + 1 != twend
+ && runp != tw
+ && (unsigned int) runp[-1] <= (unsigned int) runp[1])
+ {
+ /* Match against all characters in between the
+ first and last character of the sequence. */
+ wchar_t wc;
+
+ for (wc = runp[-1] + 1; wc <= runp[1]; ++wc)
+ if ((wint_t) wc == c)
+ break;
+
+ if (wc <= runp[1] && !not_in)
+ break;
+ if (wc <= runp[1] && not_in)
+ {
+ /* The current character is not in the
+ scanset. */
+ ungetc (c, s);
+ goto out;
+ }
+
+ runp += 2;
+ }
+ else
+ {
+ if ((wint_t) *runp == c && !not_in)
+ break;
+ if ((wint_t) *runp == c && not_in)
+ {
+ ungetc (c, s);
+ goto out;
+ }
+
+ ++runp;
+ }
+ }
+
+ if (runp == twend && !not_in)
+ {
+ ungetc (c, s);
+ goto out;
+ }
+
+ if (!(flags & SUPPRESS))
+ {
+ *wstr++ = c;
+
+ if ((flags & MALLOC)
+ && wstr == (wchar_t *) *strptr + strsize)
+ {
+ /* Enlarge the buffer. */
+ wstr = (wchar_t *) realloc (*strptr,
+ (2 * strsize)
+ * 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)
+ {
+ if (flags & POSIX_MALLOC)
+ {
+ done = EOF;
+ goto errout;
+ }
+ /* We lose. Oh well. Terminate the string
+ and stop converting, so at least we don't
+ skip any input. */
+ ((wchar_t *) (*strptr))[strsize - 1] = L'\0';
+ strptr = NULL;
+ ++done;
+ conv_error ();
+ }
+ else
+ {
+ *strptr = (char *) wstr;
+ wstr += strsize;
+ ++strsize;
+ }
+ }
+ else
+ {
+ *strptr = (char *) wstr;
+ wstr += strsize;
+ strsize *= 2;
+ }
+ }
+ }
+ }
+ while (--width > 0 && inchar () != WEOF);
+ out:
+#else
+ char buf[MB_LEN_MAX];
+ size_t cnt = 0;
+ mbstate_t cstate;
+
+ if (__glibc_unlikely (inchar () == EOF))
+ input_error ();
+
+ memset (&cstate, '\0', sizeof (cstate));
+
+ do
+ {
+ if (((char *) charbuf.scratch.data)[c] == not_in)
+ {
+ ungetc_not_eof (c, s);
+ break;
+ }
+
+ /* This is easy. */
+ if (!(flags & SUPPRESS))
+ {
+ size_t n;
+
+ /* Convert it into a wide character. */
+ buf[0] = c;
+ n = __mbrtowc (wstr, buf, 1, &cstate);
+
+ if (n == (size_t) -2)
+ {
+ /* Possibly correct character, just not enough
+ input. */
+ ++cnt;
+ assert (cnt < MB_LEN_MAX);
+ continue;
+ }
+ cnt = 0;
+
+ ++wstr;
+ if ((flags & MALLOC)
+ && wstr == (wchar_t *) *strptr + strsize)
+ {
+ /* Enlarge the buffer. */
+ wstr = (wchar_t *) realloc (*strptr,
+ (2 * strsize
+ * 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)
+ {
+ if (flags & POSIX_MALLOC)
+ {
+ done = EOF;
+ goto errout;
+ }
+ /* We lose. Oh well. Terminate the
+ string and stop converting,
+ so at least we don't skip any input. */
+ ((wchar_t *) (*strptr))[strsize - 1] = L'\0';
+ strptr = NULL;
+ ++done;
+ conv_error ();
+ }
+ else
+ {
+ *strptr = (char *) wstr;
+ wstr += strsize;
+ ++strsize;
+ }
+ }
+ else
+ {
+ *strptr = (char *) wstr;
+ wstr += strsize;
+ strsize *= 2;
+ }
+ }
+ }
+
+ if (--width <= 0)
+ break;
+ }
+ while (inchar () != EOF);
+
+ if (__glibc_unlikely (cnt != 0))
+ /* We stopped in the middle of recognizing another
+ character. That's a problem. */
+ encode_error ();
+#endif
+
+ if (__glibc_unlikely (now == read_in))
+ /* We haven't succesfully read any character. */
+ conv_error ();
+
+ if (!(flags & SUPPRESS))
+ {
+ *wstr++ = L'\0';
+
+ 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;
+ }
+ }
+ else
+ {
+ size_t now = read_in;
+
+ if (__glibc_unlikely (inchar () == EOF))
+ input_error ();
+
+#ifdef COMPILE_WSCANF
+
+ memset (&state, '\0', sizeof (state));
+
+ do
+ {
+ wchar_t *runp;
+ size_t n;
+
+ /* Test whether it's in the scanlist. */
+ runp = tw;
+ while (runp < twend)
+ {
+ if (runp[0] == L'-' && runp[1] != '\0'
+ && runp + 1 != twend
+ && runp != tw
+ && (unsigned int) runp[-1] <= (unsigned int) runp[1])
+ {
+ /* Match against all characters in between the
+ first and last character of the sequence. */
+ wchar_t wc;
+
+ for (wc = runp[-1] + 1; wc <= runp[1]; ++wc)
+ if ((wint_t) wc == c)
+ break;
+
+ if (wc <= runp[1] && !not_in)
+ break;
+ if (wc <= runp[1] && not_in)
+ {
+ /* The current character is not in the
+ scanset. */
+ ungetc (c, s);
+ goto out2;
+ }
+
+ runp += 2;
+ }
+ else
+ {
+ if ((wint_t) *runp == c && !not_in)
+ break;
+ if ((wint_t) *runp == c && not_in)
+ {
+ ungetc (c, s);
+ goto out2;
+ }
+
+ ++runp;
+ }
+ }
+
+ if (runp == twend && !not_in)
+ {
+ ungetc (c, s);
+ goto out2;
+ }
+
+ if (!(flags & SUPPRESS))
+ {
+ if ((flags & MALLOC)
+ && *strptr + strsize - str <= MB_LEN_MAX)
+ {
+ /* Enlarge the buffer. */
+ size_t strleng = str - *strptr;
+ char *newstr;
+
+ newstr = (char *) realloc (*strptr, 2 * strsize);
+ if (newstr == NULL)
+ {
+ /* Can't allocate that much. Last-ditch
+ effort. */
+ newstr = (char *) realloc (*strptr,
+ strleng + MB_LEN_MAX);
+ if (newstr == NULL)
+ {
+ if (flags & POSIX_MALLOC)
+ {
+ done = EOF;
+ goto errout;
+ }
+ /* We lose. Oh well. Terminate the string
+ and stop converting, so at least we don't
+ skip any input. */
+ ((char *) (*strptr))[strleng] = '\0';
+ strptr = NULL;
+ ++done;
+ conv_error ();
+ }
+ else
+ {
+ *strptr = newstr;
+ str = newstr + strleng;
+ strsize = strleng + MB_LEN_MAX;
+ }
+ }
+ else
+ {
+ *strptr = newstr;
+ str = newstr + strleng;
+ strsize *= 2;
+ }
+ }
+ }
+
+ n = __wcrtomb (!(flags & SUPPRESS) ? str : NULL, c, &state);
+ if (__glibc_unlikely (n == (size_t) -1))
+ encode_error ();
+
+ assert (n <= MB_LEN_MAX);
+ str += n;
+ }
+ while (--width > 0 && inchar () != WEOF);
+ out2:
+#else
+ do
+ {
+ if (((char *) charbuf.scratch.data)[c] == not_in)
+ {
+ ungetc_not_eof (c, s);
+ break;
+ }
+
+ /* This is easy. */
+ if (!(flags & SUPPRESS))
+ {
+ *str++ = c;
+ if ((flags & MALLOC)
+ && (char *) str == *strptr + strsize)
+ {
+ /* Enlarge the buffer. */
+ size_t newsize = 2 * strsize;
+
+ allocagain:
+ str = (char *) realloc (*strptr, newsize);
+ if (str == NULL)
+ {
+ /* Can't allocate that much. Last-ditch
+ effort. */
+ if (newsize > strsize + 1)
+ {
+ newsize = strsize + 1;
+ goto allocagain;
+ }
+ if (flags & POSIX_MALLOC)
+ {
+ done = EOF;
+ goto errout;
+ }
+ /* We lose. Oh well. Terminate the
+ string and stop converting,
+ so at least we don't skip any input. */
+ ((char *) (*strptr))[strsize - 1] = '\0';
+ strptr = NULL;
+ ++done;
+ conv_error ();
+ }
+ else
+ {
+ *strptr = (char *) str;
+ str += strsize;
+ strsize = newsize;
+ }
+ }
+ }
+ }
+ while (--width > 0 && inchar () != EOF);
+#endif
+
+ if (__glibc_unlikely (now == read_in))
+ /* We haven't succesfully read any character. */
+ conv_error ();
+
+ if (!(flags & SUPPRESS))
+ {
+#ifdef COMPILE_WSCANF
+ /* We have to emit the code to get into the initial
+ state. */
+ char buf[MB_LEN_MAX];
+ size_t n = __wcrtomb (buf, L'\0', &state);
+ if (n > 0 && (flags & MALLOC)
+ && str + n >= *strptr + strsize)
+ {
+ /* Enlarge the buffer. */
+ size_t strleng = str - *strptr;
+ char *newstr;
+
+ newstr = (char *) realloc (*strptr, strleng + n + 1);
+ if (newstr == NULL)
+ {
+ if (flags & POSIX_MALLOC)
+ {
+ done = EOF;
+ goto errout;
+ }
+ /* We lose. Oh well. Terminate the string
+ and stop converting, so at least we don't
+ skip any input. */
+ ((char *) (*strptr))[strleng] = '\0';
+ strptr = NULL;
+ ++done;
+ conv_error ();
+ }
+ else
+ {
+ *strptr = newstr;
+ str = newstr + strleng;
+ strsize = strleng + n + 1;
+ }
+ }
+
+ str = __mempcpy (str, buf, n);
+#endif
+ *str++ = '\0';
+
+ if ((flags & MALLOC) && str - *strptr != strsize)
+ {
+ char *cp = (char *) realloc (*strptr, str - *strptr);
+ if (cp != NULL)
+ *strptr = cp;
+ }
+ strptr = NULL;
+
+ ++done;
+ }
+ }
+ break;
+
+ case L_('p'): /* Generic pointer. */
+ base = 16;
+ /* A PTR must be the same size as a `long int'. */
+ flags &= ~(SHORT|LONGDBL);
+ if (need_long)
+ flags |= LONG;
+ flags |= READ_POINTER;
+ goto number;
+
+ default:
+ /* If this is an unknown format character punt. */
+ conv_error ();
+ }
+ }
+
+ /* The last thing we saw int the format string was a white space.
+ Consume the last white spaces. */
+ if (skip_space)
+ {
+ do
+ c = inchar ();
+ while (ISSPACE (c));
+ ungetc (c, s);
+ }
+
+ errout:
+ /* Unlock stream. */
+ UNLOCK_STREAM (s);
+
+ scratch_buffer_free (&charbuf.scratch);
+
+ if (__glibc_unlikely (done == EOF))
+ {
+ if (__glibc_unlikely (ptrs_to_free != NULL))
+ {
+ struct ptrs_to_free *p = ptrs_to_free;
+ while (p != NULL)
+ {
+ for (size_t cnt = 0; cnt < p->count; ++cnt)
+ {
+ free (*p->ptrs[cnt]);
+ *p->ptrs[cnt] = NULL;
+ }
+ p = p->next;
+ ptrs_to_free = p;
+ }
+ }
+ }
+ else if (__glibc_unlikely (strptr != NULL))
+ {
+ free (*strptr);
+ *strptr = NULL;
+ }
+ return done;
+}
diff --git a/stdio-common/vfscanf.c b/stdio-common/vfscanf.c
index 1ce836a324..5eedca8340 100644
--- a/stdio-common/vfscanf.c
+++ b/stdio-common/vfscanf.c
@@ -15,3053 +15,13 @@
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
-#include <assert.h>
-#include <errno.h>
-#include <limits.h>
-#include <ctype.h>
-#include <stdarg.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <wchar.h>
-#include <wctype.h>
-#include <libc-diag.h>
-#include <libc-lock.h>
-#include <locale/localeinfo.h>
-#include <scratch_buffer.h>
-
-#ifdef __GNUC__
-# define HAVE_LONGLONG
-# define LONGLONG long long
-#else
-# define LONGLONG long
-#endif
-
-/* Determine whether we have to handle `long long' at all. */
-#if LONG_MAX == LONG_LONG_MAX
-# define need_longlong 0
-#else
-# define need_longlong 1
-#endif
-
-/* Determine whether we have to handle `long'. */
-#if INT_MAX == LONG_MAX
-# define need_long 0
-#else
-# define need_long 1
-#endif
-
-/* Those are flags in the conversion format. */
-#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 GNU_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 */
-#define POSIX_MALLOC 0x2000 /* m: malloc strings */
-#define MALLOC (GNU_MALLOC | POSIX_MALLOC)
-
-#include <locale/localeinfo.h>
#include <libioP.h>
-#ifdef COMPILE_WSCANF
-# define ungetc(c, s) ((void) (c == WEOF \
- || (--read_in, \
- _IO_sputbackwc (s, c))))
-# define ungetc_not_eof(c, s) ((void) (--read_in, \
- _IO_sputbackwc (s, c)))
-# define inchar() (c == WEOF ? ((errno = inchar_errno), WEOF) \
- : ((c = _IO_getwc_unlocked (s)), \
- (void) (c != WEOF \
- ? ++read_in \
- : (size_t) (inchar_errno = errno)), c))
-
-# define ISSPACE(Ch) iswspace (Ch)
-# define ISDIGIT(Ch) iswdigit (Ch)
-# define ISXDIGIT(Ch) iswxdigit (Ch)
-# define TOLOWER(Ch) towlower (Ch)
-# define ORIENT if (_IO_fwide (s, 1) != 1) return WEOF
-# define __strtoll_internal __wcstoll_internal
-# define __strtoull_internal __wcstoull_internal
-# define __strtol_internal __wcstol_internal
-# define __strtoul_internal __wcstoul_internal
-# define __strtold_internal __wcstold_internal
-# define __strtod_internal __wcstod_internal
-# define __strtof_internal __wcstof_internal
-
-# define L_(Str) L##Str
-# define CHAR_T wchar_t
-# define UCHAR_T unsigned int
-# define WINT_T wint_t
-# undef EOF
-# define EOF WEOF
-#else
-# define ungetc(c, s) ((void) ((int) c == EOF \
- || (--read_in, \
- _IO_sputbackc (s, (unsigned char) c))))
-# define ungetc_not_eof(c, s) ((void) (--read_in, \
- _IO_sputbackc (s, (unsigned char) c)))
-# define inchar() (c == EOF ? ((errno = inchar_errno), EOF) \
- : ((c = _IO_getc_unlocked (s)), \
- (void) (c != EOF \
- ? ++read_in \
- : (size_t) (inchar_errno = errno)), c))
-# define ISSPACE(Ch) __isspace_l (Ch, loc)
-# define ISDIGIT(Ch) __isdigit_l (Ch, loc)
-# define ISXDIGIT(Ch) __isxdigit_l (Ch, loc)
-# define TOLOWER(Ch) __tolower_l ((unsigned char) (Ch), loc)
-# define ORIENT if (_IO_vtable_offset (s) == 0 \
- && _IO_fwide (s, -1) != -1) \
- return EOF
-
-# define L_(Str) Str
-# define CHAR_T char
-# define UCHAR_T unsigned char
-# define WINT_T int
-#endif
-
-#include "printf-parse.h" /* Use read_int. */
-
-#define encode_error() do { \
- errval = 4; \
- __set_errno (EILSEQ); \
- goto errout; \
- } while (0)
-#define conv_error() do { \
- errval = 2; \
- goto errout; \
- } while (0)
-#define input_error() do { \
- errval = 1; \
- if (done == 0) done = EOF; \
- goto errout; \
- } while (0)
-#define add_ptr_to_free(ptr) \
- do \
- { \
- if (ptrs_to_free == NULL \
- || ptrs_to_free->count == (sizeof (ptrs_to_free->ptrs) \
- / sizeof (ptrs_to_free->ptrs[0]))) \
- { \
- struct ptrs_to_free *new_ptrs = alloca (sizeof (*ptrs_to_free)); \
- new_ptrs->count = 0; \
- new_ptrs->next = ptrs_to_free; \
- ptrs_to_free = new_ptrs; \
- } \
- ptrs_to_free->ptrs[ptrs_to_free->count++] = (ptr); \
- } \
- while (0)
-#define ARGCHECK(s, format) \
- do \
- { \
- /* Check file argument for consistence. */ \
- CHECK_FILE (s, EOF); \
- if (s->_flags & _IO_NO_READS) \
- { \
- __set_errno (EBADF); \
- return EOF; \
- } \
- else if (format == NULL) \
- { \
- __set_errno (EINVAL); \
- return EOF; \
- } \
- } while (0)
-#define LOCK_STREAM(S) \
- __libc_cleanup_region_start (1, (void (*) (void *)) &_IO_funlockfile, (S)); \
- _IO_flockfile (S)
-#define UNLOCK_STREAM(S) \
- _IO_funlockfile (S); \
- __libc_cleanup_region_end (0)
-
-struct ptrs_to_free
-{
- size_t count;
- struct ptrs_to_free *next;
- char **ptrs[32];
-};
-
-struct char_buffer {
- CHAR_T *current;
- CHAR_T *end;
- struct scratch_buffer scratch;
-};
-
-/* Returns a pointer to the first CHAR_T object in the buffer. Only
- valid if char_buffer_add (BUFFER, CH) has been called and
- char_buffer_error (BUFFER) is false. */
-static inline CHAR_T *
-char_buffer_start (const struct char_buffer *buffer)
-{
- return (CHAR_T *) buffer->scratch.data;
-}
-
-/* Returns the number of CHAR_T objects in the buffer. Only valid if
- char_buffer_error (BUFFER) is false. */
-static inline size_t
-char_buffer_size (const struct char_buffer *buffer)
-{
- return buffer->current - char_buffer_start (buffer);
-}
-
-/* Reinitializes BUFFER->current and BUFFER->end to cover the entire
- scratch buffer. */
-static inline void
-char_buffer_rewind (struct char_buffer *buffer)
-{
- buffer->current = char_buffer_start (buffer);
- buffer->end = buffer->current + buffer->scratch.length / sizeof (CHAR_T);
-}
-
-/* Returns true if a previous call to char_buffer_add (BUFFER, CH)
- failed. */
-static inline bool
-char_buffer_error (const struct char_buffer *buffer)
-{
- return __glibc_unlikely (buffer->current == NULL);
-}
-
-/* Slow path for char_buffer_add. */
-static void
-char_buffer_add_slow (struct char_buffer *buffer, CHAR_T ch)
-{
- if (char_buffer_error (buffer))
- return;
- size_t offset = buffer->end - (CHAR_T *) buffer->scratch.data;
- if (!scratch_buffer_grow_preserve (&buffer->scratch))
- {
- buffer->current = NULL;
- buffer->end = NULL;
- return;
- }
- char_buffer_rewind (buffer);
- buffer->current += offset;
- *buffer->current++ = ch;
-}
-
-/* Adds CH to BUFFER. This function does not report any errors, check
- for them with char_buffer_error. */
-static inline void
-char_buffer_add (struct char_buffer *buffer, CHAR_T ch)
- __attribute__ ((always_inline));
-static inline void
-char_buffer_add (struct char_buffer *buffer, CHAR_T ch)
-{
- if (__glibc_unlikely (buffer->current == buffer->end))
- char_buffer_add_slow (buffer, ch);
- else
- *buffer->current++ = ch;
-}
-
-/* Read formatted input from S according to the format string
- FORMAT, using the argument list in ARG.
- Return the number of assignments made, or -1 for an input error. */
-#ifdef COMPILE_WSCANF
-int
-_IO_vfwscanf (FILE *s, const wchar_t *format, va_list argptr,
- int *errp)
-#else
-int
-_IO_vfscanf_internal (FILE *s, const char *format, va_list argptr,
- int *errp)
-#endif
-{
- va_list arg;
- const CHAR_T *f = format;
- UCHAR_T fc; /* Current character of the format. */
- WINT_T done = 0; /* Assignments done. */
- size_t read_in = 0; /* Chars read in. */
- WINT_T c = 0; /* Last char read. */
- int width; /* Maximum field width. */
- int flags; /* Modifiers for current format element. */
- int errval = 0;
-#ifndef COMPILE_WSCANF
- locale_t loc = _NL_CURRENT_LOCALE;
- struct __locale_data *const curctype = loc->__locales[LC_CTYPE];
-#endif
-
- /* Errno of last failed inchar call. */
- int inchar_errno = 0;
- /* Status for reading F-P nums. */
- char got_digit, got_dot, got_e, got_sign;
- /* If a [...] is a [^...]. */
- CHAR_T not_in;
-#define exp_char not_in
- /* Base for integral numbers. */
- int base;
- /* Decimal point character. */
-#ifdef COMPILE_WSCANF
- wint_t decimal;
-#else
- const char *decimal;
-#endif
- /* The thousands character of the current locale. */
-#ifdef COMPILE_WSCANF
- wint_t thousands;
-#else
- const char *thousands;
-#endif
- struct ptrs_to_free *ptrs_to_free = NULL;
- /* State for the conversions. */
- mbstate_t state;
- /* Integral holding variables. */
- union
- {
- long long int q;
- unsigned long long int uq;
- long int l;
- unsigned long int ul;
- } num;
- /* Character-buffer pointer. */
- char *str = NULL;
- wchar_t *wstr = NULL;
- char **strptr = NULL;
- ssize_t strsize = 0;
- /* We must not react on white spaces immediately because they can
- possibly be matched even if in the input stream no character is
- available anymore. */
- int skip_space = 0;
- /* Workspace. */
- CHAR_T *tw; /* Temporary pointer. */
- struct char_buffer charbuf;
- scratch_buffer_init (&charbuf.scratch);
-
-#ifdef __va_copy
- __va_copy (arg, argptr);
-#else
- arg = (va_list) argptr;
-#endif
-
-#ifdef ORIENT
- ORIENT;
-#endif
-
- ARGCHECK (s, format);
-
- {
-#ifndef COMPILE_WSCANF
- struct __locale_data *const curnumeric = loc->__locales[LC_NUMERIC];
-#endif
-
- /* Figure out the decimal point character. */
-#ifdef COMPILE_WSCANF
- decimal = _NL_CURRENT_WORD (LC_NUMERIC, _NL_NUMERIC_DECIMAL_POINT_WC);
-#else
- decimal = curnumeric->values[_NL_ITEM_INDEX (DECIMAL_POINT)].string;
-#endif
- /* Figure out the thousands separator character. */
-#ifdef COMPILE_WSCANF
- thousands = _NL_CURRENT_WORD (LC_NUMERIC, _NL_NUMERIC_THOUSANDS_SEP_WC);
-#else
- thousands = curnumeric->values[_NL_ITEM_INDEX (THOUSANDS_SEP)].string;
- if (*thousands == '\0')
- thousands = NULL;
-#endif
- }
-
- /* Lock the stream. */
- LOCK_STREAM (s);
-
-
-#ifndef COMPILE_WSCANF
- /* From now on we use `state' to convert the format string. */
- memset (&state, '\0', sizeof (state));
-#endif
-
- /* Run through the format string. */
- while (*f != '\0')
- {
- unsigned int argpos;
- /* Extract the next argument, which is of type TYPE.
- For a %N$... spec, this is the Nth argument from the beginning;
- otherwise it is the next argument after the state now in ARG. */
-#ifdef __va_copy
-# define ARG(type) (argpos == 0 ? va_arg (arg, type) : \
- ({ unsigned int pos = argpos; \
- va_list arg; \
- __va_copy (arg, argptr); \
- while (--pos > 0) \
- (void) va_arg (arg, void *); \
- va_arg (arg, type); \
- }))
-#else
-# if 0
- /* XXX Possible optimization. */
-# define ARG(type) (argpos == 0 ? va_arg (arg, type) : \
- ({ va_list arg = (va_list) argptr; \
- arg = (va_list) ((char *) arg \
- + (argpos - 1) \
- * __va_rounded_size (void *)); \
- va_arg (arg, type); \
- }))
-# else
-# define ARG(type) (argpos == 0 ? va_arg (arg, type) : \
- ({ unsigned int pos = argpos; \
- va_list arg = (va_list) argptr; \
- while (--pos > 0) \
- (void) va_arg (arg, void *); \
- va_arg (arg, type); \
- }))
-# endif
-#endif
-
-#ifndef COMPILE_WSCANF
- if (!isascii ((unsigned char) *f))
- {
- /* Non-ASCII, may be a multibyte. */
- int len = __mbrlen (f, strlen (f), &state);
- if (len > 0)
- {
- do
- {
- c = inchar ();
- if (__glibc_unlikely (c == EOF))
- input_error ();
- else if (c != (unsigned char) *f++)
- {
- ungetc_not_eof (c, s);
- conv_error ();
- }
- }
- while (--len > 0);
- continue;
- }
- }
-#endif
-
- fc = *f++;
- if (fc != '%')
- {
- /* Remember to skip spaces. */
- if (ISSPACE (fc))
- {
- skip_space = 1;
- continue;
- }
-
- /* Read a character. */
- c = inchar ();
-
- /* Characters other than format specs must just match. */
- if (__glibc_unlikely (c == EOF))
- input_error ();
-
- /* We saw white space char as the last character in the format
- string. Now it's time to skip all leading white space. */
- if (skip_space)
- {
- while (ISSPACE (c))
- if (__glibc_unlikely (inchar () == EOF))
- input_error ();
- skip_space = 0;
- }
-
- if (__glibc_unlikely (c != fc))
- {
- ungetc (c, s);
- conv_error ();
- }
-
- continue;
- }
-
- /* This is the start of the conversion string. */
- flags = 0;
-
- /* Initialize state of modifiers. */
- argpos = 0;
-
- /* Prepare temporary buffer. */
- char_buffer_rewind (&charbuf);
-
- /* Check for a positional parameter specification. */
- if (ISDIGIT ((UCHAR_T) *f))
- {
- argpos = read_int ((const UCHAR_T **) &f);
- if (*f == L_('$'))
- ++f;
- else
- {
- /* Oops; that was actually the field width. */
- width = argpos;
- argpos = 0;
- goto got_width;
- }
- }
-
- /* 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_('*'):
- flags |= SUPPRESS;
- break;
- case L_('\''):
-#ifdef COMPILE_WSCANF
- if (thousands != L'\0')
-#else
- if (thousands != NULL)
-#endif
- flags |= GROUP;
- break;
- case L_('I'):
- flags |= I18N;
- break;
- }
-
- /* Find the maximum field width. */
- width = 0;
- if (ISDIGIT ((UCHAR_T) *f))
- width = read_int ((const UCHAR_T **) &f);
- got_width:
- if (width == 0)
- width = -1;
-
- /* Check for type modifiers. */
- switch (*f++)
- {
- case L_('h'):
- /* ints are short ints or chars. */
- if (*f == L_('h'))
- {
- ++f;
- flags |= CHAR;
- }
- else
- flags |= SHORT;
- break;
- case L_('l'):
- if (*f == L_('l'))
- {
- /* A double `l' is equivalent to an `L'. */
- ++f;
- flags |= LONGDBL | LONG;
- }
- else
- /* ints are long ints. */
- flags |= LONG;
- break;
- case L_('q'):
- case L_('L'):
- /* doubles are long doubles, and ints are long long ints. */
- flags |= LONGDBL | LONG;
- break;
- case L_('a'):
- /* The `a' is used as a flag only if followed by `s', `S' or
- `['. */
- if (*f != L_('s') && *f != L_('S') && *f != L_('['))
- {
- --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;
- if (*f == L_('l'))
- {
- ++f;
- flags |= LONG;
- }
- break;
- case L_('z'):
- if (need_longlong && sizeof (size_t) > sizeof (unsigned long int))
- flags |= LONGDBL;
- else if (sizeof (size_t) > sizeof (unsigned int))
- flags |= LONG;
- break;
- case L_('j'):
- if (need_longlong && sizeof (uintmax_t) > sizeof (unsigned long int))
- flags |= LONGDBL;
- else if (sizeof (uintmax_t) > sizeof (unsigned int))
- flags |= LONG;
- break;
- case L_('t'):
- if (need_longlong && sizeof (ptrdiff_t) > sizeof (long int))
- flags |= LONGDBL;
- else if (sizeof (ptrdiff_t) > sizeof (int))
- flags |= LONG;
- break;
- default:
- /* Not a recognized modifier. Backup. */
- --f;
- break;
- }
-
- /* End of the format string? */
- if (__glibc_unlikely (*f == L_('\0')))
- conv_error ();
-
- /* Find the conversion specifier. */
- fc = *f++;
- if (skip_space || (fc != L_('[') && fc != L_('c')
- && fc != L_('C') && fc != L_('n')))
- {
- /* Eat whitespace. */
- int save_errno = errno;
- __set_errno (0);
- do
- /* We add the additional test for EOF here since otherwise
- inchar will restore the old errno value which might be
- EINTR but does not indicate an interrupt since nothing
- was read at this time. */
- if (__builtin_expect ((c == EOF || inchar () == EOF)
- && errno == EINTR, 0))
- input_error ();
- while (ISSPACE (c));
- __set_errno (save_errno);
- ungetc (c, s);
- skip_space = 0;
- }
-
- switch (fc)
- {
- case L_('%'): /* Must match a literal '%'. */
- c = inchar ();
- if (__glibc_unlikely (c == EOF))
- input_error ();
- if (__glibc_unlikely (c != fc))
- {
- ungetc_not_eof (c, s);
- conv_error ();
- }
- break;
-
- case L_('n'): /* Answer number of assignments done. */
- /* Corrigendum 1 to ISO C 1990 describes the allowed flags
- with the 'n' conversion specifier. */
- if (!(flags & SUPPRESS))
- {
- /* Don't count the read-ahead. */
- if (need_longlong && (flags & LONGDBL))
- *ARG (long long int *) = read_in;
- else if (need_long && (flags & LONG))
- *ARG (long int *) = read_in;
- else if (flags & SHORT)
- *ARG (short int *) = read_in;
- else if (!(flags & CHAR))
- *ARG (int *) = read_in;
- else
- *ARG (char *) = read_in;
-
-#ifdef NO_BUG_IN_ISO_C_CORRIGENDUM_1
- /* We have a severe problem here. The ISO C standard
- contradicts itself in explaining the effect of the %n
- format in `scanf'. While in ISO C:1990 and the ISO C
- Amendement 1:1995 the result is described as
-
- Execution of a %n directive does not effect the
- assignment count returned at the completion of
- execution of the f(w)scanf function.
-
- in ISO C Corrigendum 1:1994 the following was added:
-
- Subclause 7.9.6.2
- Add the following fourth example:
- In:
- #include <stdio.h>
- int d1, d2, n1, n2, i;
- i = sscanf("123", "%d%n%n%d", &d1, &n1, &n2, &d2);
- the value 123 is assigned to d1 and the value3 to n1.
- Because %n can never get an input failure the value
- of 3 is also assigned to n2. The value of d2 is not
- affected. The value 3 is assigned to i.
-
- We go for now with the historically correct code from ISO C,
- i.e., we don't count the %n assignments. When it ever
- should proof to be wrong just remove the #ifdef above. */
- ++done;
-#endif
- }
- break;
-
- case L_('c'): /* Match characters. */
- if ((flags & LONG) == 0)
- {
- 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) \
- { \
- done = EOF; \
- goto errout; \
- } \
- } \
- 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 (__glibc_unlikely (c == EOF))
- input_error ();
-
-#ifdef COMPILE_WSCANF
- /* We have to convert the wide character(s) into multibyte
- characters and store the result. */
- memset (&state, '\0', sizeof (state));
-
- do
- {
- size_t n;
-
- if (!(flags & SUPPRESS) && (flags & POSIX_MALLOC)
- && *strptr + strsize - str <= MB_LEN_MAX)
- {
- /* 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_LEN_MAX);
- if (newstr == NULL)
- {
- /* c can't have `a' flag, only `m'. */
- done = EOF;
- goto errout;
- }
- else
- {
- *strptr = newstr;
- str = newstr + strleng;
- strsize = strleng + MB_LEN_MAX;
- }
- }
- else
- {
- *strptr = newstr;
- str = newstr + strleng;
- strsize *= 2;
- }
- }
-
- n = __wcrtomb (!(flags & SUPPRESS) ? str : NULL, c, &state);
- if (__glibc_unlikely (n == (size_t) -1))
- /* No valid wide character. */
- input_error ();
-
- /* Increment the output pointer. Even if we don't
- write anything. */
- str += n;
- }
- while (--width > 0 && inchar () != EOF);
-#else
- if (!(flags & SUPPRESS))
- {
- do
- {
- 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'. */
- done = EOF;
- goto errout;
- }
- else
- {
- *strptr = (char *) str;
- str += strsize;
- ++strsize;
- }
- }
- else
- {
- *strptr = (char *) str;
- str += strsize;
- strsize = newsize;
- }
- }
- *str++ = c;
- }
- while (--width > 0 && inchar () != EOF);
- }
- else
- while (--width > 0 && inchar () != EOF);
-#endif
-
- if (!(flags & SUPPRESS))
- {
- 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 (width == -1)
- width = 1;
-
- STRING_ARG (wstr, wchar_t, (width > 1024 ? 1024 : width));
-
- c = inchar ();
- if (__glibc_unlikely (c == EOF))
- input_error ();
-
-#ifdef COMPILE_WSCANF
- /* Just store the incoming wide characters. */
- if (!(flags & SUPPRESS))
- {
- do
- {
- 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. */
- done = EOF;
- goto errout;
- }
- else
- {
- *strptr = (char *) wstr;
- wstr += strsize;
- ++strsize;
- }
- }
- else
- {
- *strptr = (char *) wstr;
- wstr += strsize;
- strsize = newsize;
- }
- }
- *wstr++ = c;
- }
- while (--width > 0 && inchar () != EOF);
- }
- else
- while (--width > 0 && inchar () != EOF);
-#else
- {
- /* We have to convert the multibyte input sequence to wide
- characters. */
- char buf[1];
- mbstate_t cstate;
-
- memset (&cstate, '\0', sizeof (cstate));
-
- do
- {
- /* 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. */
- done = EOF;
- goto errout;
- }
- else
- {
- *strptr = (char *) wstr;
- wstr += strsize;
- ++strsize;
- }
- }
- else
- {
- *strptr = (char *) wstr;
- wstr += strsize;
- strsize = newsize;
- }
- }
-
- while (1)
- {
- size_t n;
-
- n = __mbrtowc (!(flags & SUPPRESS) ? wstr : NULL,
- buf, 1, &cstate);
-
- if (n == (size_t) -2)
- {
- /* Possibly correct character, just not enough
- input. */
- if (__glibc_unlikely (inchar () == EOF))
- encode_error ();
-
- buf[0] = c;
- continue;
- }
-
- if (__glibc_unlikely (n != 1))
- encode_error ();
-
- /* We have a match. */
- break;
- }
-
- /* Advance the result pointer. */
- ++wstr;
- }
- while (--width > 0 && inchar () != EOF);
- }
-#endif
-
- if (!(flags & SUPPRESS))
- {
- 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))
- {
- STRING_ARG (str, char, 100);
-
- c = inchar ();
- if (__glibc_unlikely (c == EOF))
- input_error ();
-
-#ifdef COMPILE_WSCANF
- memset (&state, '\0', sizeof (state));
-#endif
-
- do
- {
- if (ISSPACE (c))
- {
- ungetc_not_eof (c, s);
- break;
- }
-
-#ifdef COMPILE_WSCANF
- /* This is quite complicated. We have to convert the
- wide characters into multibyte characters and then
- store them. */
- {
- size_t n;
-
- if (!(flags & SUPPRESS) && (flags & MALLOC)
- && *strptr + strsize - str <= MB_LEN_MAX)
- {
- /* We have to enlarge the buffer if the `a' or `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_LEN_MAX);
- if (newstr == NULL)
- {
- if (flags & POSIX_MALLOC)
- {
- done = EOF;
- goto errout;
- }
- /* We lose. Oh well. Terminate the
- string and stop converting,
- so at least we don't skip any input. */
- ((char *) (*strptr))[strleng] = '\0';
- strptr = NULL;
- ++done;
- conv_error ();
- }
- else
- {
- *strptr = newstr;
- str = newstr + strleng;
- strsize = strleng + MB_LEN_MAX;
- }
- }
- else
- {
- *strptr = newstr;
- str = newstr + strleng;
- strsize *= 2;
- }
- }
-
- n = __wcrtomb (!(flags & SUPPRESS) ? str : NULL, c,
- &state);
- if (__glibc_unlikely (n == (size_t) -1))
- encode_error ();
-
- assert (n <= MB_LEN_MAX);
- str += n;
- }
-#else
- /* This is easy. */
- if (!(flags & SUPPRESS))
- {
- *str++ = c;
- if ((flags & MALLOC)
- && (char *) str == *strptr + strsize)
- {
- /* Enlarge the buffer. */
- str = (char *) realloc (*strptr, 2 * strsize);
- if (str == NULL)
- {
- /* Can't allocate that much. Last-ditch
- effort. */
- str = (char *) realloc (*strptr, strsize + 1);
- if (str == NULL)
- {
- if (flags & POSIX_MALLOC)
- {
- done = EOF;
- goto errout;
- }
- /* We lose. Oh well. Terminate the
- string and stop converting,
- so at least we don't skip any input. */
- ((char *) (*strptr))[strsize - 1] = '\0';
- strptr = NULL;
- ++done;
- conv_error ();
- }
- else
- {
- *strptr = (char *) str;
- str += strsize;
- ++strsize;
- }
- }
- else
- {
- *strptr = (char *) str;
- str += strsize;
- strsize *= 2;
- }
- }
- }
-#endif
- }
- while ((width <= 0 || --width > 0) && inchar () != EOF);
-
- if (!(flags & SUPPRESS))
- {
-#ifdef COMPILE_WSCANF
- /* We have to emit the code to get into the initial
- state. */
- char buf[MB_LEN_MAX];
- size_t n = __wcrtomb (buf, L'\0', &state);
- if (n > 0 && (flags & MALLOC)
- && str + n >= *strptr + strsize)
- {
- /* Enlarge the buffer. */
- size_t strleng = str - *strptr;
- char *newstr;
-
- newstr = (char *) realloc (*strptr, strleng + n + 1);
- if (newstr == NULL)
- {
- if (flags & POSIX_MALLOC)
- {
- done = EOF;
- goto errout;
- }
- /* We lose. Oh well. Terminate the string
- and stop converting, so at least we don't
- skip any input. */
- ((char *) (*strptr))[strleng] = '\0';
- strptr = NULL;
- ++done;
- conv_error ();
- }
- else
- {
- *strptr = newstr;
- str = newstr + strleng;
- strsize = strleng + n + 1;
- }
- }
-
- str = __mempcpy (str, buf, n);
-#endif
- *str++ = '\0';
-
- 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_('S'):
- {
-#ifndef COMPILE_WSCANF
- mbstate_t cstate;
-#endif
-
- /* Wide character string. */
- STRING_ARG (wstr, wchar_t, 100);
-
- c = inchar ();
- if (__builtin_expect (c == EOF, 0))
- input_error ();
-
-#ifndef COMPILE_WSCANF
- memset (&cstate, '\0', sizeof (cstate));
-#endif
-
- do
- {
- if (ISSPACE (c))
- {
- ungetc_not_eof (c, s);
- break;
- }
-
-#ifdef COMPILE_WSCANF
- /* This is easy. */
- if (!(flags & SUPPRESS))
- {
- *wstr++ = c;
- if ((flags & MALLOC)
- && wstr == (wchar_t *) *strptr + strsize)
- {
- /* Enlarge the buffer. */
- wstr = (wchar_t *) realloc (*strptr,
- (2 * strsize)
- * 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)
- {
- if (flags & POSIX_MALLOC)
- {
- done = EOF;
- goto errout;
- }
- /* We lose. Oh well. Terminate the string
- and stop converting, so at least we don't
- skip any input. */
- ((wchar_t *) (*strptr))[strsize - 1] = L'\0';
- strptr = NULL;
- ++done;
- conv_error ();
- }
- else
- {
- *strptr = (char *) wstr;
- wstr += strsize;
- ++strsize;
- }
- }
- else
- {
- *strptr = (char *) wstr;
- wstr += strsize;
- strsize *= 2;
- }
- }
- }
-#else
- {
- char buf[1];
-
- buf[0] = c;
-
- while (1)
- {
- size_t n;
-
- n = __mbrtowc (!(flags & SUPPRESS) ? wstr : NULL,
- buf, 1, &cstate);
-
- if (n == (size_t) -2)
- {
- /* Possibly correct character, just not enough
- input. */
- if (__glibc_unlikely (inchar () == EOF))
- encode_error ();
-
- buf[0] = c;
- continue;
- }
-
- if (__glibc_unlikely (n != 1))
- encode_error ();
-
- /* We have a match. */
- ++wstr;
- break;
- }
-
- if (!(flags & SUPPRESS) && (flags & MALLOC)
- && wstr == (wchar_t *) *strptr + strsize)
- {
- /* Enlarge the buffer. */
- wstr = (wchar_t *) realloc (*strptr,
- (2 * strsize
- * 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)
- {
- if (flags & POSIX_MALLOC)
- {
- done = EOF;
- goto errout;
- }
- /* We lose. Oh well. Terminate the
- string and stop converting, so at
- least we don't skip any input. */
- ((wchar_t *) (*strptr))[strsize - 1] = L'\0';
- strptr = NULL;
- ++done;
- conv_error ();
- }
- else
- {
- *strptr = (char *) wstr;
- wstr += strsize;
- ++strsize;
- }
- }
- else
- {
- *strptr = (char *) wstr;
- wstr += strsize;
- strsize *= 2;
- }
- }
- }
-#endif
- }
- while ((width <= 0 || --width > 0) && inchar () != EOF);
-
- if (!(flags & SUPPRESS))
- {
- *wstr++ = L'\0';
-
- 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_('x'): /* Hexadecimal integer. */
- case L_('X'): /* Ditto. */
- base = 16;
- goto number;
-
- case L_('o'): /* Octal integer. */
- base = 8;
- goto number;
-
- case L_('u'): /* Unsigned decimal integer. */
- base = 10;
- goto number;
-
- case L_('d'): /* Signed decimal integer. */
- base = 10;
- flags |= NUMBER_SIGNED;
- goto number;
-
- case L_('i'): /* Generic number. */
- base = 0;
- flags |= NUMBER_SIGNED;
-
- number:
- c = inchar ();
- if (__glibc_unlikely (c == EOF))
- input_error ();
-
- /* Check for a sign. */
- if (c == L_('-') || c == L_('+'))
- {
- char_buffer_add (&charbuf, c);
- if (width > 0)
- --width;
- c = inchar ();
- }
-
- /* Look for a leading indication of base. */
- if (width != 0 && c == L_('0'))
- {
- if (width > 0)
- --width;
-
- char_buffer_add (&charbuf, c);
- c = inchar ();
-
- if (width != 0 && TOLOWER (c) == L_('x'))
- {
- if (base == 0)
- base = 16;
- if (base == 16)
- {
- if (width > 0)
- --width;
- c = inchar ();
- }
- }
- else if (base == 0)
- base = 8;
- }
-
- if (base == 0)
- base = 10;
-
- if (base == 10 && __builtin_expect ((flags & I18N) != 0, 0))
- {
- int from_level;
- int to_level;
- int level;
-#ifdef COMPILE_WSCANF
- const wchar_t *wcdigits[10];
- const wchar_t *wcdigits_extended[10];
-#else
- const char *mbdigits[10];
- const char *mbdigits_extended[10];
-#endif
- /* "to_inpunct" is a map from ASCII digits to their
- equivalent in locale. This is defined for locales
- which use an extra digits set. */
- wctrans_t map = __wctrans ("to_inpunct");
- int n;
-
- from_level = 0;
-#ifdef COMPILE_WSCANF
- to_level = _NL_CURRENT_WORD (LC_CTYPE,
- _NL_CTYPE_INDIGITS_WC_LEN) - 1;
-#else
- to_level = (uint32_t) curctype->values[_NL_ITEM_INDEX (_NL_CTYPE_INDIGITS_MB_LEN)].word - 1;
-#endif
-
- /* Get the alternative digit forms if there are any. */
- if (__glibc_unlikely (map != NULL))
- {
- /* Adding new level for extra digits set in locale file. */
- ++to_level;
-
- for (n = 0; n < 10; ++n)
- {
-#ifdef COMPILE_WSCANF
- wcdigits[n] = (const wchar_t *)
- _NL_CURRENT (LC_CTYPE, _NL_CTYPE_INDIGITS0_WC + n);
-
- wchar_t *wc_extended = (wchar_t *)
- alloca ((to_level + 2) * sizeof (wchar_t));
- __wmemcpy (wc_extended, wcdigits[n], to_level);
- wc_extended[to_level] = __towctrans (L'0' + n, map);
- wc_extended[to_level + 1] = '\0';
- wcdigits_extended[n] = wc_extended;
-#else
- mbdigits[n]
- = curctype->values[_NL_CTYPE_INDIGITS0_MB + n].string;
-
- /* Get the equivalent wide char in map. */
- wint_t extra_wcdigit = __towctrans (L'0' + n, map);
-
- /* Convert it to multibyte representation. */
- mbstate_t state;
- memset (&state, '\0', sizeof (state));
-
- char extra_mbdigit[MB_LEN_MAX];
- size_t mblen
- = __wcrtomb (extra_mbdigit, extra_wcdigit, &state);
-
- if (mblen == (size_t) -1)
- {
- /* Ignore this new level. */
- map = NULL;
- break;
- }
-
- /* Calculate the length of mbdigits[n]. */
- const char *last_char = mbdigits[n];
- for (level = 0; level < to_level; ++level)
- last_char = strchr (last_char, '\0') + 1;
-
- size_t mbdigits_len = last_char - mbdigits[n];
-
- /* Allocate memory for extended multibyte digit. */
- char *mb_extended;
- mb_extended = (char *) alloca (mbdigits_len + mblen + 1);
-
- /* And get the mbdigits + extra_digit string. */
- *(char *) __mempcpy (__mempcpy (mb_extended, mbdigits[n],
- mbdigits_len),
- extra_mbdigit, mblen) = '\0';
- mbdigits_extended[n] = mb_extended;
-#endif
- }
- }
-
- /* Read the number into workspace. */
- while (c != EOF && width != 0)
- {
- /* 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)
- {
- /* Get the string for the digits with value N. */
-#ifdef COMPILE_WSCANF
-
- /* wcdigits_extended[] is fully set in the loop
- above, but the test for "map != NULL" is done
- inside the loop here and outside the loop there. */
- DIAG_PUSH_NEEDS_COMMENT;
- DIAG_IGNORE_NEEDS_COMMENT (4.7, "-Wmaybe-uninitialized");
-
- if (__glibc_unlikely (map != NULL))
- wcdigits[n] = wcdigits_extended[n];
- else
- wcdigits[n] = (const wchar_t *)
- _NL_CURRENT (LC_CTYPE, _NL_CTYPE_INDIGITS0_WC + n);
- wcdigits[n] += from_level;
-
- DIAG_POP_NEEDS_COMMENT;
-
- if (c == (wint_t) *wcdigits[n])
- {
- to_level = from_level;
- break;
- }
-
- /* Advance the pointer to the next string. */
- ++wcdigits[n];
-#else
- const char *cmpp;
- int avail = width > 0 ? width : INT_MAX;
-
- if (__glibc_unlikely (map != NULL))
- mbdigits[n] = mbdigits_extended[n];
- else
- mbdigits[n]
- = curctype->values[_NL_CTYPE_INDIGITS0_MB + n].string;
-
- for (level = 0; level < from_level; level++)
- mbdigits[n] = strchr (mbdigits[n], '\0') + 1;
-
- cmpp = mbdigits[n];
- 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;
- to_level = from_level;
- 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;
- }
-
- /* Advance the pointer to the next string. */
- mbdigits[n] = strchr (mbdigits[n], '\0') + 1;
-#endif
- }
-
- if (n == 10)
- {
- /* Have not yet found the digit. */
- for (level = from_level + 1; level <= to_level; ++level)
- {
- /* Search all ten digits of this level. */
- for (n = 0; n < 10; ++n)
- {
-#ifdef COMPILE_WSCANF
- if (c == (wint_t) *wcdigits[n])
- break;
-
- /* Advance the pointer to the next string. */
- ++wcdigits[n];
-#else
- const char *cmpp;
- int avail = width > 0 ? width : INT_MAX;
-
- cmpp = mbdigits[n];
- 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;
- 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;
- }
-
- /* Advance the pointer to the next string. */
- mbdigits[n] = strchr (mbdigits[n], '\0') + 1;
-#endif
- }
-
- if (n < 10)
- {
- /* Found it. */
- from_level = level;
- to_level = level;
- break;
- }
- }
- }
-
- if (n < 10)
- c = L_('0') + n;
- else if (flags & GROUP)
- {
- /* Try matching against the thousands separator. */
-#ifdef COMPILE_WSCANF
- if (c != thousands)
- break;
-#else
- const char *cmpp = thousands;
- int avail = width > 0 ? width : INT_MAX;
-
- while ((unsigned char) *cmpp == c && avail >= 0)
- {
- char_buffer_add (&charbuf, c);
- if (*++cmpp == '\0')
- break;
- else
- {
- if (avail == 0 || inchar () == EOF)
- break;
- --avail;
- }
- }
-
- if (char_buffer_error (&charbuf))
- {
- __set_errno (ENOMEM);
- done = EOF;
- goto errout;
- }
-
- if (*cmpp != '\0')
- {
- /* We are pushing all read characters back. */
- if (cmpp > thousands)
- {
- charbuf.current -= cmpp - thousands;
- ungetc (c, s);
- while (--cmpp > thousands)
- ungetc_not_eof ((unsigned char) *cmpp, s);
- c = (unsigned char) *cmpp;
- }
- break;
- }
-
- if (width > 0)
- width = avail;
-
- /* The last thousands character will be added back by
- the char_buffer_add below. */
- --charbuf.current;
-#endif
- }
- else
- break;
-
- char_buffer_add (&charbuf, c);
- if (width > 0)
- --width;
-
- c = inchar ();
- }
- }
- else
- /* Read the number into workspace. */
- while (c != EOF && width != 0)
- {
- if (base == 16)
- {
- if (!ISXDIGIT (c))
- break;
- }
- else if (!ISDIGIT (c) || (int) (c - L_('0')) >= base)
- {
- if (base == 10 && (flags & GROUP))
- {
- /* Try matching against the thousands separator. */
-#ifdef COMPILE_WSCANF
- if (c != thousands)
- break;
-#else
- const char *cmpp = thousands;
- int avail = width > 0 ? width : INT_MAX;
-
- while ((unsigned char) *cmpp == c && avail >= 0)
- {
- char_buffer_add (&charbuf, c);
- if (*++cmpp == '\0')
- break;
- else
- {
- if (avail == 0 || inchar () == EOF)
- break;
- --avail;
- }
- }
-
- if (char_buffer_error (&charbuf))
- {
- __set_errno (ENOMEM);
- done = EOF;
- goto errout;
- }
-
- if (*cmpp != '\0')
- {
- /* We are pushing all read characters back. */
- if (cmpp > thousands)
- {
- charbuf.current -= cmpp - thousands;
- ungetc (c, s);
- while (--cmpp > thousands)
- ungetc_not_eof ((unsigned char) *cmpp, s);
- c = (unsigned char) *cmpp;
- }
- break;
- }
-
- if (width > 0)
- width = avail;
-
- /* The last thousands character will be added back by
- the char_buffer_add below. */
- --charbuf.current;
-#endif
- }
- else
- break;
- }
- char_buffer_add (&charbuf, c);
- if (width > 0)
- --width;
-
- c = inchar ();
- }
-
- if (char_buffer_error (&charbuf))
- {
- __set_errno (ENOMEM);
- done = EOF;
- goto errout;
- }
-
- if (char_buffer_size (&charbuf) == 0
- || (char_buffer_size (&charbuf) == 1
- && (char_buffer_start (&charbuf)[0] == L_('+')
- || char_buffer_start (&charbuf)[0] == L_('-'))))
- {
- /* There was no number. If we are supposed to read a pointer
- we must recognize "(nil)" as well. */
- if (__builtin_expect (char_buffer_size (&charbuf) == 0
- && (flags & READ_POINTER)
- && (width < 0 || width >= 5)
- && c == '('
- && TOLOWER (inchar ()) == L_('n')
- && TOLOWER (inchar ()) == L_('i')
- && TOLOWER (inchar ()) == L_('l')
- && inchar () == L_(')'), 1))
- /* We must produce the value of a NULL pointer. A single
- '0' digit is enough. */
- char_buffer_add (&charbuf, L_('0'));
- else
- {
- /* The last read character is not part of the number
- anymore. */
- ungetc (c, s);
-
- conv_error ();
- }
- }
- else
- /* The just read character is not part of the number anymore. */
- ungetc (c, s);
-
- /* Convert the number. */
- char_buffer_add (&charbuf, L_('\0'));
- if (char_buffer_error (&charbuf))
- {
- __set_errno (ENOMEM);
- done = EOF;
- goto errout;
- }
- if (need_longlong && (flags & LONGDBL))
- {
- if (flags & NUMBER_SIGNED)
- num.q = __strtoll_internal
- (char_buffer_start (&charbuf), &tw, base, flags & GROUP);
- else
- num.uq = __strtoull_internal
- (char_buffer_start (&charbuf), &tw, base, flags & GROUP);
- }
- else
- {
- if (flags & NUMBER_SIGNED)
- num.l = __strtol_internal
- (char_buffer_start (&charbuf), &tw, base, flags & GROUP);
- else
- num.ul = __strtoul_internal
- (char_buffer_start (&charbuf), &tw, base, flags & GROUP);
- }
- if (__glibc_unlikely (char_buffer_start (&charbuf) == tw))
- conv_error ();
-
- if (!(flags & SUPPRESS))
- {
- if (flags & NUMBER_SIGNED)
- {
- if (need_longlong && (flags & LONGDBL))
- *ARG (LONGLONG int *) = num.q;
- else if (need_long && (flags & LONG))
- *ARG (long int *) = num.l;
- else if (flags & SHORT)
- *ARG (short int *) = (short int) num.l;
- else if (!(flags & CHAR))
- *ARG (int *) = (int) num.l;
- else
- *ARG (signed char *) = (signed char) num.ul;
- }
- else
- {
- if (need_longlong && (flags & LONGDBL))
- *ARG (unsigned LONGLONG int *) = num.uq;
- else if (need_long && (flags & LONG))
- *ARG (unsigned long int *) = num.ul;
- else if (flags & SHORT)
- *ARG (unsigned short int *)
- = (unsigned short int) num.ul;
- else if (!(flags & CHAR))
- *ARG (unsigned int *) = (unsigned int) num.ul;
- else
- *ARG (unsigned char *) = (unsigned char) num.ul;
- }
- ++done;
- }
- break;
-
- case L_('e'): /* Floating-point numbers. */
- case L_('E'):
- case L_('f'):
- case L_('F'):
- case L_('g'):
- case L_('G'):
- case L_('a'):
- case L_('A'):
- c = inchar ();
- if (width > 0)
- --width;
- if (__glibc_unlikely (c == EOF))
- input_error ();
-
- got_digit = got_dot = got_e = got_sign = 0;
-
- /* Check for a sign. */
- if (c == L_('-') || c == L_('+'))
- {
- got_sign = 1;
- char_buffer_add (&charbuf, c);
- if (__glibc_unlikely (width == 0 || inchar () == EOF))
- /* EOF is only an input error before we read any chars. */
- conv_error ();
- if (width > 0)
- --width;
- }
-
- /* Take care for the special arguments "nan" and "inf". */
- if (TOLOWER (c) == L_('n'))
- {
- /* Maybe "nan". */
- char_buffer_add (&charbuf, c);
- if (__builtin_expect (width == 0
- || inchar () == EOF
- || TOLOWER (c) != L_('a'), 0))
- conv_error ();
- if (width > 0)
- --width;
- char_buffer_add (&charbuf, c);
- if (__builtin_expect (width == 0
- || inchar () == EOF
- || TOLOWER (c) != L_('n'), 0))
- conv_error ();
- if (width > 0)
- --width;
- char_buffer_add (&charbuf, c);
- /* It is "nan". */
- goto scan_float;
- }
- else if (TOLOWER (c) == L_('i'))
- {
- /* Maybe "inf" or "infinity". */
- char_buffer_add (&charbuf, c);
- if (__builtin_expect (width == 0
- || inchar () == EOF
- || TOLOWER (c) != L_('n'), 0))
- conv_error ();
- if (width > 0)
- --width;
- char_buffer_add (&charbuf, c);
- if (__builtin_expect (width == 0
- || inchar () == EOF
- || TOLOWER (c) != L_('f'), 0))
- conv_error ();
- if (width > 0)
- --width;
- char_buffer_add (&charbuf, c);
- /* It is as least "inf". */
- if (width != 0 && inchar () != EOF)
- {
- if (TOLOWER (c) == L_('i'))
- {
- if (width > 0)
- --width;
- /* Now we have to read the rest as well. */
- char_buffer_add (&charbuf, c);
- if (__builtin_expect (width == 0
- || inchar () == EOF
- || TOLOWER (c) != L_('n'), 0))
- conv_error ();
- if (width > 0)
- --width;
- char_buffer_add (&charbuf, c);
- if (__builtin_expect (width == 0
- || inchar () == EOF
- || TOLOWER (c) != L_('i'), 0))
- conv_error ();
- if (width > 0)
- --width;
- char_buffer_add (&charbuf, c);
- if (__builtin_expect (width == 0
- || inchar () == EOF
- || TOLOWER (c) != L_('t'), 0))
- conv_error ();
- if (width > 0)
- --width;
- char_buffer_add (&charbuf, c);
- if (__builtin_expect (width == 0
- || inchar () == EOF
- || TOLOWER (c) != L_('y'), 0))
- conv_error ();
- if (width > 0)
- --width;
- char_buffer_add (&charbuf, c);
- }
- else
- /* Never mind. */
- ungetc (c, s);
- }
- goto scan_float;
- }
-
- exp_char = L_('e');
- if (width != 0 && c == L_('0'))
- {
- char_buffer_add (&charbuf, c);
- c = inchar ();
- if (width > 0)
- --width;
- if (width != 0 && TOLOWER (c) == L_('x'))
- {
- /* It is a number in hexadecimal format. */
- char_buffer_add (&charbuf, c);
-
- flags |= HEXA_FLOAT;
- exp_char = L_('p');
-
- /* Grouping is not allowed. */
- flags &= ~GROUP;
- c = inchar ();
- if (width > 0)
- --width;
- }
- else
- got_digit = 1;
- }
-
- while (1)
- {
- if (char_buffer_error (&charbuf))
- {
- __set_errno (ENOMEM);
- done = EOF;
- goto errout;
- }
- if (ISDIGIT (c))
- {
- char_buffer_add (&charbuf, c);
- got_digit = 1;
- }
- else if (!got_e && (flags & HEXA_FLOAT) && ISXDIGIT (c))
- {
- char_buffer_add (&charbuf, c);
- got_digit = 1;
- }
- else if (got_e && charbuf.current[-1] == exp_char
- && (c == L_('-') || c == L_('+')))
- char_buffer_add (&charbuf, c);
- else if (got_digit && !got_e
- && (CHAR_T) TOLOWER (c) == exp_char)
- {
- char_buffer_add (&charbuf, exp_char);
- got_e = got_dot = 1;
- }
- else
- {
-#ifdef COMPILE_WSCANF
- if (! got_dot && c == decimal)
- {
- char_buffer_add (&charbuf, c);
- got_dot = 1;
- }
- else if ((flags & GROUP) != 0 && ! got_dot && c == thousands)
- char_buffer_add (&charbuf, c);
- else
- {
- /* The last read character is not part of the number
- anymore. */
- ungetc (c, s);
- break;
- }
-#else
- const char *cmpp = decimal;
- int avail = width > 0 ? width : INT_MAX;
-
- if (! got_dot)
- {
- while ((unsigned char) *cmpp == c && avail >= 0)
- if (*++cmpp == '\0')
- break;
- else
- {
- if (avail == 0 || inchar () == EOF)
- break;
- --avail;
- }
- }
-
- if (*cmpp == '\0')
- {
- /* Add all the characters. */
- for (cmpp = decimal; *cmpp != '\0'; ++cmpp)
- char_buffer_add (&charbuf, (unsigned char) *cmpp);
- if (width > 0)
- width = avail;
- got_dot = 1;
- }
- else
- {
- /* Figure out whether it is a thousands separator.
- There is one problem: we possibly read more than
- one character. We cannot push them back but since
- we know that parts of the `decimal' string matched,
- we can compare against it. */
- const char *cmp2p = thousands;
-
- 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)
- if (*++cmp2p == '\0')
- break;
- else
- {
- if (avail == 0 || inchar () == EOF)
- break;
- --avail;
- }
- }
- }
-
- if (cmp2p != NULL && *cmp2p == '\0')
- {
- /* Add all the characters. */
- for (cmpp = thousands; *cmpp != '\0'; ++cmpp)
- char_buffer_add (&charbuf, (unsigned char) *cmpp);
- if (width > 0)
- width = avail;
- }
- else
- {
- /* The last read character is not part of the number
- anymore. */
- ungetc (c, s);
- break;
- }
- }
-#endif
- }
-
- if (width == 0 || inchar () == EOF)
- break;
-
- if (width > 0)
- --width;
- }
-
- if (char_buffer_error (&charbuf))
- {
- __set_errno (ENOMEM);
- done = EOF;
- goto errout;
- }
-
- 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. */
- && (char_buffer_size (&charbuf) == got_sign || 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 (char_buffer_size (&charbuf) == got_sign
- || (char_buffer_size (&charbuf) == got_sign + 1
- && wcdigits[11] == decimal))
-#else
- char mbdigits[12][MB_LEN_MAX + 1];
-
- mbstate_t state;
- memset (&state, '\0', sizeof (state));
-
- bool match_so_far = char_buffer_size (&charbuf) == got_sign;
- size_t mblen = __wcrtomb (mbdigits[11], wcdigits[11], &state);
- if (mblen != (size_t) -1)
- {
- mbdigits[11][mblen] = '\0';
- match_so_far |=
- (char_buffer_size (&charbuf) == strlen (decimal) + got_sign
- && 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 |= (char_buffer_size (&charbuf)
- == decimal_len + got_sign);
- 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
- conversion is done correctly. */
- while (1)
- {
- if (char_buffer_error (&charbuf))
- {
- __set_errno (ENOMEM);
- done = EOF;
- goto errout;
- }
- if (got_e && charbuf.current[-1] == exp_char
- && (c == L_('-') || c == L_('+')))
- char_buffer_add (&charbuf, c);
- else if (char_buffer_size (&charbuf) > got_sign && !got_e
- && (CHAR_T) TOLOWER (c) == exp_char)
- {
- char_buffer_add (&charbuf, 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)
- char_buffer_add (&charbuf, L_('0') + n);
- else if (n == 11 && !got_dot)
- {
- char_buffer_add (&charbuf, decimal);
- got_dot = 1;
- }
- else if (n == 10 && have_locthousands
- && ! got_dot)
- char_buffer_add (&charbuf, 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)
- char_buffer_add (&charbuf, L_('0') + n);
- else if (n == 11 && !got_dot)
- {
- /* Add all the characters. */
- for (cmpp = decimal; *cmpp != '\0';
- ++cmpp)
- char_buffer_add (&charbuf,
- (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)
- char_buffer_add (&charbuf,
- (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
- }
-
- if (char_buffer_error (&charbuf))
- {
- __set_errno (ENOMEM);
- done = EOF;
- goto errout;
- }
-
- /* Have we read any character? If we try to read a number
- in hexadecimal notation and we have read only the `0x'
- prefix this is an error. */
- if (__glibc_unlikely (char_buffer_size (&charbuf) == got_sign
- || ((flags & HEXA_FLOAT)
- && (char_buffer_size (&charbuf)
- == 2 + got_sign))))
- conv_error ();
-
- scan_float:
- /* Convert the number. */
- char_buffer_add (&charbuf, L_('\0'));
- if (char_buffer_error (&charbuf))
- {
- __set_errno (ENOMEM);
- done = EOF;
- goto errout;
- }
- if ((flags & LONGDBL) && !__ldbl_is_dbl)
- {
- long double d = __strtold_internal
- (char_buffer_start (&charbuf), &tw, flags & GROUP);
- if (!(flags & SUPPRESS) && tw != char_buffer_start (&charbuf))
- *ARG (long double *) = d;
- }
- else if (flags & (LONG | LONGDBL))
- {
- double d = __strtod_internal
- (char_buffer_start (&charbuf), &tw, flags & GROUP);
- if (!(flags & SUPPRESS) && tw != char_buffer_start (&charbuf))
- *ARG (double *) = d;
- }
- else
- {
- float d = __strtof_internal
- (char_buffer_start (&charbuf), &tw, flags & GROUP);
- if (!(flags & SUPPRESS) && tw != char_buffer_start (&charbuf))
- *ARG (float *) = d;
- }
-
- if (__glibc_unlikely (tw == char_buffer_start (&charbuf)))
- conv_error ();
-
- if (!(flags & SUPPRESS))
- ++done;
- break;
-
- case L_('['): /* Character class. */
- if (flags & LONG)
- STRING_ARG (wstr, wchar_t, 100);
- else
- STRING_ARG (str, char, 100);
-
- if (*f == L_('^'))
- {
- ++f;
- not_in = 1;
- }
- else
- not_in = 0;
-
- if (width < 0)
- /* There is no width given so there is also no limit on the
- number of characters we read. Therefore we set width to
- a very high value to make the algorithm easier. */
- width = INT_MAX;
-
-#ifdef COMPILE_WSCANF
- /* Find the beginning and the end of the scanlist. We are not
- creating a lookup table since it would have to be too large.
- Instead we search each time through the string. This is not
- a constant lookup time but who uses this feature deserves to
- be punished. */
- tw = (wchar_t *) f; /* Marks the beginning. */
-
- if (*f == L']')
- ++f;
-
- while ((fc = *f++) != L'\0' && fc != L']');
-
- if (__glibc_unlikely (fc == L'\0'))
- conv_error ();
- wchar_t *twend = (wchar_t *) f - 1;
-#else
- /* Fill WP with byte flags indexed by character.
- We will use this flag map for matching input characters. */
- if (!scratch_buffer_set_array_size
- (&charbuf.scratch, UCHAR_MAX + 1, 1))
- {
- done = EOF;
- goto errout;
- }
- memset (charbuf.scratch.data, '\0', UCHAR_MAX + 1);
-
- fc = *f;
- if (fc == ']' || fc == '-')
- {
- /* If ] or - appears before any char in the set, it is not
- the terminator or separator, but the first char in the
- set. */
- ((char *)charbuf.scratch.data)[fc] = 1;
- ++f;
- }
-
- while ((fc = *f++) != '\0' && fc != ']')
- if (fc == '-' && *f != '\0' && *f != ']'
- && (unsigned char) f[-2] <= (unsigned char) *f)
- {
- /* Add all characters from the one before the '-'
- up to (but not including) the next format char. */
- for (fc = (unsigned char) f[-2]; fc < (unsigned char) *f; ++fc)
- ((char *)charbuf.scratch.data)[fc] = 1;
- }
- else
- /* Add the character to the flag map. */
- ((char *)charbuf.scratch.data)[fc] = 1;
-
- if (__glibc_unlikely (fc == '\0'))
- conv_error();
-#endif
-
- if (flags & LONG)
- {
- size_t now = read_in;
-#ifdef COMPILE_WSCANF
- if (__glibc_unlikely (inchar () == WEOF))
- input_error ();
-
- do
- {
- wchar_t *runp;
-
- /* Test whether it's in the scanlist. */
- runp = tw;
- while (runp < twend)
- {
- if (runp[0] == L'-' && runp[1] != '\0'
- && runp + 1 != twend
- && runp != tw
- && (unsigned int) runp[-1] <= (unsigned int) runp[1])
- {
- /* Match against all characters in between the
- first and last character of the sequence. */
- wchar_t wc;
-
- for (wc = runp[-1] + 1; wc <= runp[1]; ++wc)
- if ((wint_t) wc == c)
- break;
-
- if (wc <= runp[1] && !not_in)
- break;
- if (wc <= runp[1] && not_in)
- {
- /* The current character is not in the
- scanset. */
- ungetc (c, s);
- goto out;
- }
-
- runp += 2;
- }
- else
- {
- if ((wint_t) *runp == c && !not_in)
- break;
- if ((wint_t) *runp == c && not_in)
- {
- ungetc (c, s);
- goto out;
- }
-
- ++runp;
- }
- }
-
- if (runp == twend && !not_in)
- {
- ungetc (c, s);
- goto out;
- }
-
- if (!(flags & SUPPRESS))
- {
- *wstr++ = c;
-
- if ((flags & MALLOC)
- && wstr == (wchar_t *) *strptr + strsize)
- {
- /* Enlarge the buffer. */
- wstr = (wchar_t *) realloc (*strptr,
- (2 * strsize)
- * 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)
- {
- if (flags & POSIX_MALLOC)
- {
- done = EOF;
- goto errout;
- }
- /* We lose. Oh well. Terminate the string
- and stop converting, so at least we don't
- skip any input. */
- ((wchar_t *) (*strptr))[strsize - 1] = L'\0';
- strptr = NULL;
- ++done;
- conv_error ();
- }
- else
- {
- *strptr = (char *) wstr;
- wstr += strsize;
- ++strsize;
- }
- }
- else
- {
- *strptr = (char *) wstr;
- wstr += strsize;
- strsize *= 2;
- }
- }
- }
- }
- while (--width > 0 && inchar () != WEOF);
- out:
-#else
- char buf[MB_LEN_MAX];
- size_t cnt = 0;
- mbstate_t cstate;
-
- if (__glibc_unlikely (inchar () == EOF))
- input_error ();
-
- memset (&cstate, '\0', sizeof (cstate));
-
- do
- {
- if (((char *) charbuf.scratch.data)[c] == not_in)
- {
- ungetc_not_eof (c, s);
- break;
- }
-
- /* This is easy. */
- if (!(flags & SUPPRESS))
- {
- size_t n;
-
- /* Convert it into a wide character. */
- buf[0] = c;
- n = __mbrtowc (wstr, buf, 1, &cstate);
-
- if (n == (size_t) -2)
- {
- /* Possibly correct character, just not enough
- input. */
- ++cnt;
- assert (cnt < MB_LEN_MAX);
- continue;
- }
- cnt = 0;
-
- ++wstr;
- if ((flags & MALLOC)
- && wstr == (wchar_t *) *strptr + strsize)
- {
- /* Enlarge the buffer. */
- wstr = (wchar_t *) realloc (*strptr,
- (2 * strsize
- * 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)
- {
- if (flags & POSIX_MALLOC)
- {
- done = EOF;
- goto errout;
- }
- /* We lose. Oh well. Terminate the
- string and stop converting,
- so at least we don't skip any input. */
- ((wchar_t *) (*strptr))[strsize - 1] = L'\0';
- strptr = NULL;
- ++done;
- conv_error ();
- }
- else
- {
- *strptr = (char *) wstr;
- wstr += strsize;
- ++strsize;
- }
- }
- else
- {
- *strptr = (char *) wstr;
- wstr += strsize;
- strsize *= 2;
- }
- }
- }
-
- if (--width <= 0)
- break;
- }
- while (inchar () != EOF);
-
- if (__glibc_unlikely (cnt != 0))
- /* We stopped in the middle of recognizing another
- character. That's a problem. */
- encode_error ();
-#endif
-
- if (__glibc_unlikely (now == read_in))
- /* We haven't succesfully read any character. */
- conv_error ();
-
- if (!(flags & SUPPRESS))
- {
- *wstr++ = L'\0';
-
- 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;
- }
- }
- else
- {
- size_t now = read_in;
-
- if (__glibc_unlikely (inchar () == EOF))
- input_error ();
-
-#ifdef COMPILE_WSCANF
-
- memset (&state, '\0', sizeof (state));
-
- do
- {
- wchar_t *runp;
- size_t n;
-
- /* Test whether it's in the scanlist. */
- runp = tw;
- while (runp < twend)
- {
- if (runp[0] == L'-' && runp[1] != '\0'
- && runp + 1 != twend
- && runp != tw
- && (unsigned int) runp[-1] <= (unsigned int) runp[1])
- {
- /* Match against all characters in between the
- first and last character of the sequence. */
- wchar_t wc;
-
- for (wc = runp[-1] + 1; wc <= runp[1]; ++wc)
- if ((wint_t) wc == c)
- break;
-
- if (wc <= runp[1] && !not_in)
- break;
- if (wc <= runp[1] && not_in)
- {
- /* The current character is not in the
- scanset. */
- ungetc (c, s);
- goto out2;
- }
-
- runp += 2;
- }
- else
- {
- if ((wint_t) *runp == c && !not_in)
- break;
- if ((wint_t) *runp == c && not_in)
- {
- ungetc (c, s);
- goto out2;
- }
-
- ++runp;
- }
- }
-
- if (runp == twend && !not_in)
- {
- ungetc (c, s);
- goto out2;
- }
-
- if (!(flags & SUPPRESS))
- {
- if ((flags & MALLOC)
- && *strptr + strsize - str <= MB_LEN_MAX)
- {
- /* Enlarge the buffer. */
- size_t strleng = str - *strptr;
- char *newstr;
-
- newstr = (char *) realloc (*strptr, 2 * strsize);
- if (newstr == NULL)
- {
- /* Can't allocate that much. Last-ditch
- effort. */
- newstr = (char *) realloc (*strptr,
- strleng + MB_LEN_MAX);
- if (newstr == NULL)
- {
- if (flags & POSIX_MALLOC)
- {
- done = EOF;
- goto errout;
- }
- /* We lose. Oh well. Terminate the string
- and stop converting, so at least we don't
- skip any input. */
- ((char *) (*strptr))[strleng] = '\0';
- strptr = NULL;
- ++done;
- conv_error ();
- }
- else
- {
- *strptr = newstr;
- str = newstr + strleng;
- strsize = strleng + MB_LEN_MAX;
- }
- }
- else
- {
- *strptr = newstr;
- str = newstr + strleng;
- strsize *= 2;
- }
- }
- }
-
- n = __wcrtomb (!(flags & SUPPRESS) ? str : NULL, c, &state);
- if (__glibc_unlikely (n == (size_t) -1))
- encode_error ();
-
- assert (n <= MB_LEN_MAX);
- str += n;
- }
- while (--width > 0 && inchar () != WEOF);
- out2:
-#else
- do
- {
- if (((char *) charbuf.scratch.data)[c] == not_in)
- {
- ungetc_not_eof (c, s);
- break;
- }
-
- /* This is easy. */
- if (!(flags & SUPPRESS))
- {
- *str++ = c;
- if ((flags & MALLOC)
- && (char *) str == *strptr + strsize)
- {
- /* Enlarge the buffer. */
- size_t newsize = 2 * strsize;
-
- allocagain:
- str = (char *) realloc (*strptr, newsize);
- if (str == NULL)
- {
- /* Can't allocate that much. Last-ditch
- effort. */
- if (newsize > strsize + 1)
- {
- newsize = strsize + 1;
- goto allocagain;
- }
- if (flags & POSIX_MALLOC)
- {
- done = EOF;
- goto errout;
- }
- /* We lose. Oh well. Terminate the
- string and stop converting,
- so at least we don't skip any input. */
- ((char *) (*strptr))[strsize - 1] = '\0';
- strptr = NULL;
- ++done;
- conv_error ();
- }
- else
- {
- *strptr = (char *) str;
- str += strsize;
- strsize = newsize;
- }
- }
- }
- }
- while (--width > 0 && inchar () != EOF);
-#endif
-
- if (__glibc_unlikely (now == read_in))
- /* We haven't succesfully read any character. */
- conv_error ();
-
- if (!(flags & SUPPRESS))
- {
-#ifdef COMPILE_WSCANF
- /* We have to emit the code to get into the initial
- state. */
- char buf[MB_LEN_MAX];
- size_t n = __wcrtomb (buf, L'\0', &state);
- if (n > 0 && (flags & MALLOC)
- && str + n >= *strptr + strsize)
- {
- /* Enlarge the buffer. */
- size_t strleng = str - *strptr;
- char *newstr;
-
- newstr = (char *) realloc (*strptr, strleng + n + 1);
- if (newstr == NULL)
- {
- if (flags & POSIX_MALLOC)
- {
- done = EOF;
- goto errout;
- }
- /* We lose. Oh well. Terminate the string
- and stop converting, so at least we don't
- skip any input. */
- ((char *) (*strptr))[strleng] = '\0';
- strptr = NULL;
- ++done;
- conv_error ();
- }
- else
- {
- *strptr = newstr;
- str = newstr + strleng;
- strsize = strleng + n + 1;
- }
- }
-
- str = __mempcpy (str, buf, n);
-#endif
- *str++ = '\0';
-
- if ((flags & MALLOC) && str - *strptr != strsize)
- {
- char *cp = (char *) realloc (*strptr, str - *strptr);
- if (cp != NULL)
- *strptr = cp;
- }
- strptr = NULL;
-
- ++done;
- }
- }
- break;
-
- case L_('p'): /* Generic pointer. */
- base = 16;
- /* A PTR must be the same size as a `long int'. */
- flags &= ~(SHORT|LONGDBL);
- if (need_long)
- flags |= LONG;
- flags |= READ_POINTER;
- goto number;
-
- default:
- /* If this is an unknown format character punt. */
- conv_error ();
- }
- }
-
- /* The last thing we saw int the format string was a white space.
- Consume the last white spaces. */
- if (skip_space)
- {
- do
- c = inchar ();
- while (ISSPACE (c));
- ungetc (c, s);
- }
-
- errout:
- /* Unlock stream. */
- UNLOCK_STREAM (s);
-
- scratch_buffer_free (&charbuf.scratch);
- if (errp != NULL)
- *errp |= errval;
-
- if (__glibc_unlikely (done == EOF))
- {
- if (__glibc_unlikely (ptrs_to_free != NULL))
- {
- struct ptrs_to_free *p = ptrs_to_free;
- while (p != NULL)
- {
- for (size_t cnt = 0; cnt < p->count; ++cnt)
- {
- free (*p->ptrs[cnt]);
- *p->ptrs[cnt] = NULL;
- }
- p = p->next;
- ptrs_to_free = p;
- }
- }
- }
- else if (__glibc_unlikely (strptr != NULL))
- {
- free (*strptr);
- *strptr = NULL;
- }
- return done;
-}
-
-#ifdef COMPILE_WSCANF
-int
-__vfwscanf (FILE *s, const wchar_t *format, va_list argptr)
-{
- return _IO_vfwscanf (s, format, argptr, NULL);
-}
-ldbl_weak_alias (__vfwscanf, vfwscanf)
-#else
int
___vfscanf (FILE *s, const char *format, va_list argptr)
{
- return _IO_vfscanf_internal (s, format, argptr, NULL);
+ return __vfscanf_internal (s, format, argptr, 0);
}
-ldbl_strong_alias (_IO_vfscanf_internal, _IO_vfscanf)
-ldbl_hidden_def (_IO_vfscanf_internal, _IO_vfscanf)
ldbl_strong_alias (___vfscanf, __vfscanf)
ldbl_hidden_def (___vfscanf, __vfscanf)
ldbl_weak_alias (___vfscanf, vfscanf)
-#endif
diff --git a/stdio-common/vfwscanf-internal.c b/stdio-common/vfwscanf-internal.c
new file mode 100644
index 0000000000..26c89270b7
--- /dev/null
+++ b/stdio-common/vfwscanf-internal.c
@@ -0,0 +1,2 @@
+#define COMPILE_WSCANF 1
+#include "vfscanf-internal.c"
diff --git a/stdio-common/vfwscanf.c b/stdio-common/vfwscanf.c
index 26b1a66608..86980464c2 100644
--- a/stdio-common/vfwscanf.c
+++ b/stdio-common/vfwscanf.c
@@ -1,2 +1,26 @@
-#define COMPILE_WSCANF 1
-#include "vfscanf.c"
+/* Implementation and symbols for vfwscanf.
+ Copyright (C) 2018 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, see
+ <http://www.gnu.org/licenses/>. */
+
+#include <libioP.h>
+
+int
+__vfwscanf (FILE *s, const wchar_t *format, va_list argptr)
+{
+ return __vfwscanf_internal (s, format, argptr, 0);
+}
+ldbl_weak_alias (__vfwscanf, vfwscanf)
diff --git a/sysdeps/generic/math_ldbl_opt.h b/sysdeps/generic/math_ldbl_opt.h
index 8a5d8ba107..92f670dff7 100644
--- a/sysdeps/generic/math_ldbl_opt.h
+++ b/sysdeps/generic/math_ldbl_opt.h
@@ -6,9 +6,13 @@
for platforms where compatibility symbols are required for a previous
ABI that defined long double functions as aliases for the double code. */
+#include <shlib-compat.h>
+
#define LONG_DOUBLE_COMPAT(lib, introduced) 0
#define long_double_symbol(lib, local, symbol)
#define ldbl_hidden_def(local, name) libc_hidden_def (name)
#define ldbl_strong_alias(name, aliasname) strong_alias (name, aliasname)
#define ldbl_weak_alias(name, aliasname) weak_alias (name, aliasname)
+#define ldbl_compat_symbol(lib, local, symbol, version) \
+ compat_symbol (lib, local, symbol, version)
#define __ldbl_is_dbl 0
diff --git a/sysdeps/ieee754/ldbl-opt/math_ldbl_opt.h b/sysdeps/ieee754/ldbl-opt/math_ldbl_opt.h
index 61ba784f86..1c49036f7b 100644
--- a/sysdeps/ieee754/ldbl-opt/math_ldbl_opt.h
+++ b/sysdeps/ieee754/ldbl-opt/math_ldbl_opt.h
@@ -20,10 +20,16 @@
long_double_symbol (libc, __GL_##name##_##aliasname, aliasname);
# define long_double_symbol_1(lib, local, symbol, version) \
versioned_symbol (lib, local, symbol, version)
+# define ldbl_compat_symbol(lib, local, symbol, version) \
+ compat_symbol (lib, local, symbol, LONG_DOUBLE_COMPAT_VERSION)
#else
# define ldbl_hidden_def(local, name) libc_hidden_def (name)
# define ldbl_strong_alias(name, aliasname) strong_alias (name, aliasname)
# define ldbl_weak_alias(name, aliasname) weak_alias (name, aliasname)
+/* Same as compat_symbol, ldbl_compat_symbol is not to be used outside
+ '#if SHLIB_COMPAT' statement and should fail if it is. */
+# define ldbl_compat_symbol(lib, local, symbol, version) \
+ _Static_assert (0, "ldbl_compat_symbol should be used inside SHLIB_COMPAT");
# ifndef __ASSEMBLER__
/* Note that weak_alias cannot be used - it is defined to nothing
in most of the C files. */
diff --git a/sysdeps/ieee754/ldbl-opt/nldbl-compat.c b/sysdeps/ieee754/ldbl-opt/nldbl-compat.c
index 7a1e89c1a3..91ea27a423 100644
--- a/sysdeps/ieee754/ldbl-opt/nldbl-compat.c
+++ b/sysdeps/ieee754/ldbl-opt/nldbl-compat.c
@@ -330,16 +330,20 @@ __nldbl_wprintf (const wchar_t *fmt, ...)
return done;
}
+#if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_29)
int
attribute_compat_text_section
__nldbl__IO_vfscanf (FILE *s, const char *fmt, va_list ap, int *errp)
{
int res;
set_no_long_double ();
- res = _IO_vfscanf (s, fmt, ap, errp);
+ res = __vfscanf_internal (s, fmt, ap, 0);
clear_no_long_double ();
+ if (__glibc_unlikely (errp != 0))
+ *errp = (res == -1);
return res;
}
+#endif
int
attribute_compat_text_section
@@ -347,7 +351,7 @@ __nldbl___vfscanf (FILE *s, const char *fmt, va_list ap)
{
int res;
set_no_long_double ();
- res = _IO_vfscanf (s, fmt, ap, NULL);
+ res = __vfscanf_internal (s, fmt, ap, 0);
clear_no_long_double ();
return res;
}
@@ -423,7 +427,7 @@ __nldbl_vfwscanf (FILE *s, const wchar_t *fmt, va_list ap)
{
int res;
set_no_long_double ();
- res = _IO_vfwscanf (s, fmt, ap, NULL);
+ res = __vfwscanf_internal (s, fmt, ap, 0);
clear_no_long_double ();
return res;
}
@@ -1027,7 +1031,6 @@ compat_symbol (libc, __nldbl_vdprintf, vdprintf, GLIBC_2_0);
compat_symbol (libc, __nldbl_vsnprintf, vsnprintf, GLIBC_2_0);
compat_symbol (libc, __nldbl_vsprintf, vsprintf, GLIBC_2_0);
compat_symbol (libc, __nldbl__IO_sscanf, _IO_sscanf, GLIBC_2_0);
-compat_symbol (libc, __nldbl__IO_vfscanf, _IO_vfscanf, GLIBC_2_0);
compat_symbol (libc, __nldbl___vfscanf, __vfscanf, GLIBC_2_0);
compat_symbol (libc, __nldbl___vsscanf, __vsscanf, GLIBC_2_0);
compat_symbol (libc, __nldbl_fscanf, fscanf, GLIBC_2_0);
@@ -1040,6 +1043,12 @@ compat_symbol (libc, __nldbl___printf_fp, __printf_fp, GLIBC_2_0);
compat_symbol (libc, __nldbl_strfmon, strfmon, GLIBC_2_0);
compat_symbol (libc, __nldbl_syslog, syslog, GLIBC_2_0);
compat_symbol (libc, __nldbl_vsyslog, vsyslog, GLIBC_2_0);
+/* This function is not in public headers, but was exported until
+ version 2.29. For platforms that are newer than that, there's no
+ need to expose the symbol. */
+# if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_29)
+compat_symbol (libc, __nldbl__IO_vfscanf, _IO_vfscanf, GLIBC_2_0);
+# endif
#endif
#if LONG_DOUBLE_COMPAT(libc, GLIBC_2_1)
compat_symbol (libc, __nldbl___asprintf, __asprintf, GLIBC_2_1);
diff --git a/wcsmbs/isoc99_fwscanf.c b/wcsmbs/isoc99_fwscanf.c
index 0c6a2c47ac..00b07dd48e 100644
--- a/wcsmbs/isoc99_fwscanf.c
+++ b/wcsmbs/isoc99_fwscanf.c
@@ -32,7 +32,7 @@ __isoc99_fwscanf (FILE *stream, const wchar_t *format, ...)
stream->_flags2 |= _IO_FLAGS2_SCANF_STD;
va_start (arg, format);
- done = _IO_vfwscanf (stream, format, arg, NULL);
+ done = __vfwscanf_internal (stream, format, arg, 0);
va_end (arg);
_IO_release_lock (stream);
diff --git a/wcsmbs/isoc99_swscanf.c b/wcsmbs/isoc99_swscanf.c
index ff523db706..40401d0aa1 100644
--- a/wcsmbs/isoc99_swscanf.c
+++ b/wcsmbs/isoc99_swscanf.c
@@ -16,20 +16,22 @@
<http://www.gnu.org/licenses/>. */
#include <stdarg.h>
-#include <stdio.h>
-#include <libioP.h>
-#include <wchar.h>
+#include <libio/strfile.h>
/* Read formatted input from S, according to the format string FORMAT. */
-/* VARARGS2 */
+
int
__isoc99_swscanf (const wchar_t *s, const wchar_t *format, ...)
{
va_list arg;
int done;
+ _IO_strfile sf;
+ struct _IO_wide_data wd;
+ FILE *f = _IO_strfile_readw (&sf, &wd, s);
+ f->_flags2 |= _IO_FLAGS2_SCANF_STD;
va_start (arg, format);
- done = __isoc99_vswscanf (s, format, arg);
+ done = __vfwscanf_internal (f, format, arg, 0);
va_end (arg);
return done;
diff --git a/wcsmbs/isoc99_vfwscanf.c b/wcsmbs/isoc99_vfwscanf.c
index 7beb45b4d3..f70c6b596d 100644
--- a/wcsmbs/isoc99_vfwscanf.c
+++ b/wcsmbs/isoc99_vfwscanf.c
@@ -28,7 +28,7 @@ __isoc99_vfwscanf (FILE *stream, const wchar_t *format, va_list args)
_IO_acquire_lock_clear_flags2 (stream);
stream->_flags2 |= _IO_FLAGS2_SCANF_STD;
- done = _IO_vfwscanf (stream, format, args, NULL);
+ done = __vfwscanf_internal (stream, format, args, 0);
_IO_release_lock (stream);
return done;
}
diff --git a/wcsmbs/isoc99_vswscanf.c b/wcsmbs/isoc99_vswscanf.c
index 130769154d..b91eb651a3 100644
--- a/wcsmbs/isoc99_vswscanf.c
+++ b/wcsmbs/isoc99_vswscanf.c
@@ -24,24 +24,16 @@
This exception applies to code released by its copyright holders
in files containing the exception. */
-#include <libioP.h>
#include <wchar.h>
-#include "../libio/strfile.h"
+#include <libio/strfile.h>
int
__isoc99_vswscanf (const wchar_t *string, const wchar_t *format, va_list args)
{
- int ret;
_IO_strfile sf;
struct _IO_wide_data wd;
-#ifdef _IO_MTSAFE_IO
- sf._sbf._f._lock = NULL;
-#endif
- _IO_no_init (&sf._sbf._f, _IO_USER_LOCK, 0, &wd, &_IO_wstr_jumps);
- _IO_fwide (&sf._sbf._f, 1);
- _IO_wstr_init_static (&sf._sbf._f, (wchar_t *)string, 0, NULL);
- sf._sbf._f._flags2 |= _IO_FLAGS2_SCANF_STD;
- ret = _IO_vfwscanf ((FILE *) &sf._sbf, format, args, NULL);
- return ret;
+ FILE *f = _IO_strfile_readw (&sf, &wd, string);
+ f->_flags2 |= _IO_FLAGS2_SCANF_STD;
+ return __vfwscanf_internal (f, format, args, 0);
}
libc_hidden_def (__isoc99_vswscanf)
diff --git a/wcsmbs/isoc99_vwscanf.c b/wcsmbs/isoc99_vwscanf.c
index 049521b964..eb22c8acae 100644
--- a/wcsmbs/isoc99_vwscanf.c
+++ b/wcsmbs/isoc99_vwscanf.c
@@ -28,7 +28,7 @@ __isoc99_vwscanf (const wchar_t *format, va_list args)
_IO_acquire_lock_clear_flags2 (stdin);
stdin->_flags2 |= _IO_FLAGS2_SCANF_STD;
- done = _IO_vfwscanf (stdin, format, args, NULL);
+ done = __vfwscanf_internal (stdin, format, args, 0);
_IO_release_lock (stdin);
return done;
}
diff --git a/wcsmbs/isoc99_wscanf.c b/wcsmbs/isoc99_wscanf.c
index abfbd50c11..59f80d78fb 100644
--- a/wcsmbs/isoc99_wscanf.c
+++ b/wcsmbs/isoc99_wscanf.c
@@ -33,7 +33,7 @@ __isoc99_wscanf (const wchar_t *format, ...)
stdin->_flags2 |= _IO_FLAGS2_SCANF_STD;
va_start (arg, format);
- done = _IO_vfwscanf (stdin, format, arg, NULL);
+ done = __vfwscanf_internal (stdin, format, arg, 0);
va_end (arg);
_IO_release_lock (stdin);