diff options
| author | Zack Weinberg <zackw@panix.com> | 2018-03-07 14:31:58 -0500 |
|---|---|---|
| committer | Gabriel F. T. Gomes <gabriel@inconstante.eti.br> | 2018-12-05 18:15:42 -0200 |
| commit | 349718d4d7841df46bcc36df9bc2baef4c40d6f5 (patch) | |
| tree | 1c6ae071a4a6d063e551ae6577b04d952262daea /stdio-common | |
| parent | 72b8692d7e640eb85ea0fb7de6d5e797512691c1 (diff) | |
| download | glibc-349718d4d7841df46bcc36df9bc2baef4c40d6f5.tar.xz glibc-349718d4d7841df46bcc36df9bc2baef4c40d6f5.zip | |
Add __vfscanf_internal and __vfwscanf_internal with flags arguments.
There are two flags currently defined: SCANF_LDBL_IS_DBL is the mode
used by __nldbl_ scanf variants, and SCANF_ISOC99_A is the mode used
by __isoc99_ scanf variants. In this patch, the new functions honor
these flag bits if they're set, but they still also look at the
corresponding bits of environmental state, and callers all pass zero.
The new functions do *not* have the "errp" argument possessed by
_IO_vfscanf and _IO_vfwscanf. All internal callers passed NULL for
that argument. External callers could theoretically exist, so I
preserved wrappers, but they are flagged as compat symbols and they
don't preserve the three-way distinction among types of errors that
was formerly exposed. These functions probably should have been in
the list of deprecated _IO_ symbols in 2.27 NEWS -- they're not just
aliases for vfscanf and vfwscanf.
(It was necessary to introduce ldbl_compat_symbol for _IO_vfscanf.
Please check that part of the patch very carefully, I am still not
confident I understand all of the details of ldbl-opt.)
This patch also introduces helper inlines in libio/strfile.h that
encapsulate the process of initializing an _IO_strfile object for
reading. This allows us to call __vfscanf_internal directly from
sscanf, and __vfwscanf_internal directly from swscanf, without
duplicating the initialization code. (Previously, they called their
v-counterparts, but that won't work if we want to control *both* C99
mode and ldbl-is-dbl mode using the flags argument to__vfscanf_internal.)
It's still a little awkward, especially for wide strfiles, but it's
much better than what we had.
Tested for powerpc and powerpc64le.
Diffstat (limited to 'stdio-common')
| -rw-r--r-- | stdio-common/Makefile | 3 | ||||
| -rw-r--r-- | stdio-common/Versions | 3 | ||||
| -rw-r--r-- | stdio-common/iovfscanf.c | 38 | ||||
| -rw-r--r-- | stdio-common/iovfwscanf.c | 38 | ||||
| -rw-r--r-- | stdio-common/isoc99_fscanf.c | 2 | ||||
| -rw-r--r-- | stdio-common/isoc99_scanf.c | 2 | ||||
| -rw-r--r-- | stdio-common/isoc99_sscanf.c | 9 | ||||
| -rw-r--r-- | stdio-common/isoc99_vfscanf.c | 2 | ||||
| -rw-r--r-- | stdio-common/isoc99_vscanf.c | 2 | ||||
| -rw-r--r-- | stdio-common/isoc99_vsscanf.c | 17 | ||||
| -rw-r--r-- | stdio-common/scanf.c | 2 | ||||
| -rw-r--r-- | stdio-common/sscanf.c | 12 | ||||
| -rw-r--r-- | stdio-common/vfscanf-internal.c | 3049 | ||||
| -rw-r--r-- | stdio-common/vfscanf.c | 3042 | ||||
| -rw-r--r-- | stdio-common/vfwscanf-internal.c | 2 | ||||
| -rw-r--r-- | stdio-common/vfwscanf.c | 28 |
16 files changed, 3178 insertions, 3073 deletions
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, c |
