[v2] Use C99-compliant scanf under _GNU_SOURCE with modern compilers.

Message ID 20181225112654.23204-1-zackw@panix.com
State New
Headers show
Series
  • [v2] Use C99-compliant scanf under _GNU_SOURCE with modern compilers.
Related show

Commit Message

Zack Weinberg Dec. 25, 2018, 11:26 a.m.
Changes since previous version of patch:

 * The wide versions of *scanf are now included in the change.
   This was an oversight in all previous iterations -- I always
   forget that the wide I/O functions are declared in wchar.h,
   not stdio.h.

 * Redirection of internal sscanf calls to __GI___isoc99_scanf
   is now handled centrally, in include/stdio.h.  Turns out there
   were quite a few more than two files that needed to do this.
   (Still open to suggestions that don't involve #define.)

 * A complete build-many-glibcs run reports no errors.
   (Still probably needs testing on an architecture where the
   ldbl-compat tests are actually *run*, not just compiled.)

I'm traveling for the next two weeks but I will still be responding
to email.

zw

--- 8< ---

The only difference between noncompliant and C99-compliant scanf is
that the former accepts the archaic GNU extension '%as' (also %aS and
%a[...]) meaning to allocate space for the input string with malloc.
This extension conflicts with C99's use of %a as a format _type_
meaning to read a floating-point number; POSIX.1-2008 standardized
equivalent functionality using the modifier letter 'm' instead (%ms,
%mS, %m[...]).

The extension was already disabled in most conformance modes:
specifically, any mode that doesn't involve _GNU_SOURCE and _does_
involve either strict conformance to C99 or loose conformance to both
C99 and POSIX.1-2001 would get the C99-compliant scanf.  With
compilers new enough to use -std=gnu11 instead of -std=gnu89, or
equivalent, that includes the default mode.

This patch tightens things up further: you now get C99-compliant scanf
in all configurations except when _GNU_SOURCE is defined *and*
__STDC_VERSION__ or __cplusplus (whichever is relevant) indicates
C89/C++98.  This leaves the old scanf available under e.g. -std=c89
-D_GNU_SOURCE, but removes it from e.g. -std=gnu11 -D_GNU_SOURCE (it
was already not present under -std=gnu11 without -D_GNU_SOURCE) and
from -std=gnu89 without -D_GNU_SOURCE.

There needs to be an internal override so we can compile the
noncompliant scanf itself.  This is the same problem we had when we
removed 'gets' from _GNU_SOURCE and it's dealt with the same way:
there's a new __GLIBC_USE symbol, DEPRECATED_SCANF, which defaults to
off under the appropriate conditions for external code, but can be
overridden by individual files within stdio.

We also run into problems with PLT bypass for internal uses of sscanf,
because libc_hidden_proto uses __REDIRECT and so does the logic in
stdio.h for choosing which implementation of scanf to use; __REDIRECT
isn't transitive, so include/stdio.h needs to bridge the gap with a
macro.  As far as I can tell, sscanf is the only function in this
family that's internally called by unrelated code.  I'm open to better
ideas if anyone has one.  N.B. I would accept "internal code shouldn't
call *scanf ever" as a better idea, but there are several internal
uses and some of them are pretty complicated, I don't want to hold up
this patch for that.

On a related note, the interaction of the C99/not-C99 scanf redirect
with the __LDBL_COMPAT scanf redirect was complicated, confusing, and
possibly wrong.  I tried to match the intent rather than the letter of
the previous behavior.  I do not have access to physical hardware for
which __LDBL_COMPAT is relevant, so my ability to test it is limited.
Careful review and maybe even some volunteer testing would be
appreciated.

Finally, there are several tests in stdio-common that use the
extension.  bug21 is a regression test for a crash, and still
exercises the relevant code if changed to use %ms instead of %as.
scanf14 through scanf17 are more complicated since they are actually
testing the subtleties of the extension - under what circumstances is
'a' treated as a modifier letter, etc.  My approach here is to
duplicate scanf14.c and scanf16.c; the originals change to use %ms
instead, the copies select precisely the right conformance mode to get
%as with the old GNU meaning, plus everything else they need (it's not
as simple as saying -std=gnu89, unfortunately).  scanf15 and scanf17
become simpler because they no longer need to avoid _GNU_SOURCE, and
all of them no longer need diagnostic overrides.  Yay!

	* include/features.h (__GLIBC_USE_DEPRECATED_SCANF): New __GLIBC_USE
	parameter.  Only use deprecated scanf when __USE_GNU is defined
	and __STDC_VERSION__ is less than 199901L or __cplusplus is less
	than 201103L, whichever is relevant for the language being compiled.

	* libio/stdio.h, libio/bits/stdio-ldbl.h: Decide whether or
	not to redirect scanf, fscanf, sscanf, vscanf, vfscanf, and
	vsscanf to their __isoc99_ variants based only on
	__GLIBC_USE(DEPRECATED_SCANF).
        * wcsmbs/wchar.h: wcsmbs/bits/wchar-ldbl.h: Likewise for
        wscanf, fwscanf, swscanf, vwscanf, vfwscanf, and vswscanf.

	* libio/iovsscanf.c
	* libio/fwscanf.c
	* libio/iovswscanf.c
	* libio/swscanf.c
	* libio/vscanf.c
	* libio/vwscanf.c
	* libio/wscanf.c
	* stdio-common/fscanf.c
	* stdio-common/scanf.c
	* stdio-common/vfscanf.c
	* stdio-common/vfwscanf.c
	* sysdeps/ieee754/ldbl-opt/nldbl-compat.c
	* sysdeps/ieee754/ldbl-opt/nldbl-fscanf.c
	* sysdeps/ieee754/ldbl-opt/nldbl-fwscanf.c
	* sysdeps/ieee754/ldbl-opt/nldbl-iovfscanf.c
	* sysdeps/ieee754/ldbl-opt/nldbl-scanf.c
	* sysdeps/ieee754/ldbl-opt/nldbl-sscanf.c
	* sysdeps/ieee754/ldbl-opt/nldbl-swscanf.c
	* sysdeps/ieee754/ldbl-opt/nldbl-vfscanf.c
	* sysdeps/ieee754/ldbl-opt/nldbl-vfwscanf.c
	* sysdeps/ieee754/ldbl-opt/nldbl-vscanf.c
	* sysdeps/ieee754/ldbl-opt/nldbl-vsscanf.c
	* sysdeps/ieee754/ldbl-opt/nldbl-vswscanf.c
	* sysdeps/ieee754/ldbl-opt/nldbl-vwscanf.c
	* sysdeps/ieee754/ldbl-opt/nldbl-wscanf.c:
	Override __GLIBC_USE_DEPRECATED_SCANF to 1.

	* stdio-common/sscanf.c: Likewise.  Remove ldbl_hidden_def for __sscanf.
	* stdio-common/isoc99_sscanf.c: Add libc_hidden_def for __isoc99_sscanf.
	* include/stdio.h: Provide libc_hidden_proto for __isoc99_sscanf,
	not sscanf.
	[!__GLIBC_USE(DEPRECATED_SCANF)]: Define sscanf as __isoc99_scanf
	with a preprocessor macro.

	* stdio-common/bug21.c, stdio-common/scanf14.c:
	Use %ms instead of %as, %mS instead of %aS, %m[] instead of %a[];
	remove DIAG_IGNORE_NEEDS_COMMENT for -Wformat.
	* stdio-common/scanf16.c: Likewise.  Add __attribute__((format(scanf)))
	to xscanf, xfscanf, xsscanf.

	* stdio-common/scanf14a.c: New copy of scanf14.c which still uses
	%as, %aS, %a[].  Use conformance mode -std=gnu89,
	_POSIX_C_SOURCE=199506L, _XOPEN_SOURCE=1, _XOPEN_SOURCE_EXTENDED=1,
	which will use the nonconformant scanf implementation.
	Remove DIAG_IGNORE_NEEDS_COMMENT for -Wformat.
	* stdio-common/scanf16a.c: New copy of scanf16.c which still uses
	%as, %aS, %a[].  Use conformance mode -std=gnu89,
	_POSIX_C_SOURCE=199506L, _XOPEN_SOURCE=1, _XOPEN_SOURCE_EXTENDED=1,
	_ISOC99_SOURCE=1, which will use the nonconformant scanf
	implementation.  Add __attribute__((format(scanf))) to xscanf,
	xfscanf, xsscanf.

	* stdio-common/scanf15.c, stdio-common/scanf17.c: No need to
	override feature selection macros or provide definitions of u_char etc.
	* stdio-common/Makefile (tests): Add scanf14a and scanf16a.
	(CFLAGS-scanf15.c, CFLAGS-scanf17.c): Remove.
	(CFLAGS-scanf14a.c, CFLAGS-scanf16a.c): New.
---
 NEWS                                       |  17 +++
 include/features.h                         |  22 +++
 include/stdio.h                            |  11 +-
 libio/bits/stdio-ldbl.h                    |   7 +-
 libio/fwscanf.c                            |   5 +
 libio/iovsscanf.c                          |   5 +
 libio/iovswscanf.c                         |   5 +
 libio/stdio.h                              |  22 ++-
 libio/swscanf.c                            |   5 +
 libio/vscanf.c                             |   5 +
 libio/vwscanf.c                            |   5 +
 libio/wscanf.c                             |   5 +
 stdio-common/Makefile                      |  14 +-
 stdio-common/bug21.c                       |  11 +-
 stdio-common/fscanf.c                      |   5 +
 stdio-common/isoc99_sscanf.c               |   1 +
 stdio-common/scanf.c                       |   5 +
 stdio-common/scanf14.c                     |  35 ++---
 stdio-common/scanf14a.c                    | 126 +++++++++++++++++
 stdio-common/scanf15.c                     |  14 +-
 stdio-common/scanf16.c                     |  20 +--
 stdio-common/scanf16a.c                    | 156 +++++++++++++++++++++
 stdio-common/scanf17.c                     |  14 +-
 stdio-common/sscanf.c                      |   6 +-
 stdio-common/vfscanf.c                     |   5 +
 stdio-common/vfwscanf.c                    |   5 +
 sysdeps/ieee754/ldbl-opt/nldbl-compat.c    |   5 +
 sysdeps/ieee754/ldbl-opt/nldbl-fscanf.c    |   5 +
 sysdeps/ieee754/ldbl-opt/nldbl-fwscanf.c   |   5 +
 sysdeps/ieee754/ldbl-opt/nldbl-iovfscanf.c |   5 +
 sysdeps/ieee754/ldbl-opt/nldbl-scanf.c     |   5 +
 sysdeps/ieee754/ldbl-opt/nldbl-sscanf.c    |   5 +
 sysdeps/ieee754/ldbl-opt/nldbl-swscanf.c   |   5 +
 sysdeps/ieee754/ldbl-opt/nldbl-vfscanf.c   |   5 +
 sysdeps/ieee754/ldbl-opt/nldbl-vfwscanf.c  |   5 +
 sysdeps/ieee754/ldbl-opt/nldbl-vscanf.c    |   5 +
 sysdeps/ieee754/ldbl-opt/nldbl-vsscanf.c   |   5 +
 sysdeps/ieee754/ldbl-opt/nldbl-vswscanf.c  |   5 +
 sysdeps/ieee754/ldbl-opt/nldbl-vwscanf.c   |   5 +
 sysdeps/ieee754/ldbl-opt/nldbl-wscanf.c    |   5 +
 wcsmbs/bits/wchar-ldbl.h                   |   7 +-
 wcsmbs/wchar.h                             |  10 +-
 42 files changed, 515 insertions(+), 103 deletions(-)
 create mode 100644 stdio-common/scanf14a.c
 create mode 100644 stdio-common/scanf16a.c

Comments

Andreas Schwab Dec. 25, 2018, 12:53 p.m. | #1
On Dez 25 2018, Zack Weinberg <zackw@panix.com> wrote:

>  * A complete build-many-glibcs run reports no errors.
>    (Still probably needs testing on an architecture where the
>    ldbl-compat tests are actually *run*, not just compiled.)

I've started a test build.

https://build.opensuse.org/project/monitor/home:Andreas_Schwab:glibc:test

Andreas.
Siddhesh Poyarekar Dec. 31, 2018, 4:59 p.m. | #2
On 25/12/18 4:56 PM, Zack Weinberg wrote:
> Changes since previous version of patch:
> 
>   * The wide versions of *scanf are now included in the change.
>     This was an oversight in all previous iterations -- I always
>     forget that the wide I/O functions are declared in wchar.h,
>     not stdio.h.
> 
>   * Redirection of internal sscanf calls to __GI___isoc99_scanf
>     is now handled centrally, in include/stdio.h.  Turns out there
>     were quite a few more than two files that needed to do this.
>     (Still open to suggestions that don't involve #define.)
> 
>   * A complete build-many-glibcs run reports no errors.
>     (Still probably needs testing on an architecture where the
>     ldbl-compat tests are actually *run*, not just compiled.)
> 
> I'm traveling for the next two weeks but I will still be responding
> to email.
> 
> zw
> 
> --- 8< ---
> 
> The only difference between noncompliant and C99-compliant scanf is
> that the former accepts the archaic GNU extension '%as' (also %aS and
> %a[...]) meaning to allocate space for the input string with malloc.
> This extension conflicts with C99's use of %a as a format _type_
> meaning to read a floating-point number; POSIX.1-2008 standardized
> equivalent functionality using the modifier letter 'm' instead (%ms,
> %mS, %m[...]).
> 
> The extension was already disabled in most conformance modes:
> specifically, any mode that doesn't involve _GNU_SOURCE and _does_
> involve either strict conformance to C99 or loose conformance to both
> C99 and POSIX.1-2001 would get the C99-compliant scanf.  With
> compilers new enough to use -std=gnu11 instead of -std=gnu89, or
> equivalent, that includes the default mode.
> 
> This patch tightens things up further: you now get C99-compliant scanf
> in all configurations except when _GNU_SOURCE is defined *and*
> __STDC_VERSION__ or __cplusplus (whichever is relevant) indicates
> C89/C++98.  This leaves the old scanf available under e.g. -std=c89
> -D_GNU_SOURCE, but removes it from e.g. -std=gnu11 -D_GNU_SOURCE (it
> was already not present under -std=gnu11 without -D_GNU_SOURCE) and
> from -std=gnu89 without -D_GNU_SOURCE.
> 
> There needs to be an internal override so we can compile the
> noncompliant scanf itself.  This is the same problem we had when we
> removed 'gets' from _GNU_SOURCE and it's dealt with the same way:
> there's a new __GLIBC_USE symbol, DEPRECATED_SCANF, which defaults to
> off under the appropriate conditions for external code, but can be
> overridden by individual files within stdio.
> 
> We also run into problems with PLT bypass for internal uses of sscanf,
> because libc_hidden_proto uses __REDIRECT and so does the logic in
> stdio.h for choosing which implementation of scanf to use; __REDIRECT
> isn't transitive, so include/stdio.h needs to bridge the gap with a
> macro.  As far as I can tell, sscanf is the only function in this
> family that's internally called by unrelated code.  I'm open to better
> ideas if anyone has one.  N.B. I would accept "internal code shouldn't
> call *scanf ever" as a better idea, but there are several internal
> uses and some of them are pretty complicated, I don't want to hold up
> this patch for that.
> 
> On a related note, the interaction of the C99/not-C99 scanf redirect
> with the __LDBL_COMPAT scanf redirect was complicated, confusing, and
> possibly wrong.  I tried to match the intent rather than the letter of
> the previous behavior.  I do not have access to physical hardware for
> which __LDBL_COMPAT is relevant, so my ability to test it is limited.
> Careful review and maybe even some volunteer testing would be
> appreciated.
> 
> Finally, there are several tests in stdio-common that use the
> extension.  bug21 is a regression test for a crash, and still
> exercises the relevant code if changed to use %ms instead of %as.
> scanf14 through scanf17 are more complicated since they are actually
> testing the subtleties of the extension - under what circumstances is
> 'a' treated as a modifier letter, etc.  My approach here is to
> duplicate scanf14.c and scanf16.c; the originals change to use %ms
> instead, the copies select precisely the right conformance mode to get
> %as with the old GNU meaning, plus everything else they need (it's not
> as simple as saying -std=gnu89, unfortunately).  scanf15 and scanf17
> become simpler because they no longer need to avoid _GNU_SOURCE, and
> all of them no longer need diagnostic overrides.  Yay!
> 
> 	* include/features.h (__GLIBC_USE_DEPRECATED_SCANF): New __GLIBC_USE
> 	parameter.  Only use deprecated scanf when __USE_GNU is defined
> 	and __STDC_VERSION__ is less than 199901L or __cplusplus is less
> 	than 201103L, whichever is relevant for the language being compiled.
> 
> 	* libio/stdio.h, libio/bits/stdio-ldbl.h: Decide whether or
> 	not to redirect scanf, fscanf, sscanf, vscanf, vfscanf, and
> 	vsscanf to their __isoc99_ variants based only on
> 	__GLIBC_USE(DEPRECATED_SCANF).
>          * wcsmbs/wchar.h: wcsmbs/bits/wchar-ldbl.h: Likewise for
>          wscanf, fwscanf, swscanf, vwscanf, vfwscanf, and vswscanf.
> 
> 	* libio/iovsscanf.c
> 	* libio/fwscanf.c
> 	* libio/iovswscanf.c
> 	* libio/swscanf.c
> 	* libio/vscanf.c
> 	* libio/vwscanf.c
> 	* libio/wscanf.c
> 	* stdio-common/fscanf.c
> 	* stdio-common/scanf.c
> 	* stdio-common/vfscanf.c
> 	* stdio-common/vfwscanf.c
> 	* sysdeps/ieee754/ldbl-opt/nldbl-compat.c
> 	* sysdeps/ieee754/ldbl-opt/nldbl-fscanf.c
> 	* sysdeps/ieee754/ldbl-opt/nldbl-fwscanf.c
> 	* sysdeps/ieee754/ldbl-opt/nldbl-iovfscanf.c
> 	* sysdeps/ieee754/ldbl-opt/nldbl-scanf.c
> 	* sysdeps/ieee754/ldbl-opt/nldbl-sscanf.c
> 	* sysdeps/ieee754/ldbl-opt/nldbl-swscanf.c
> 	* sysdeps/ieee754/ldbl-opt/nldbl-vfscanf.c
> 	* sysdeps/ieee754/ldbl-opt/nldbl-vfwscanf.c
> 	* sysdeps/ieee754/ldbl-opt/nldbl-vscanf.c
> 	* sysdeps/ieee754/ldbl-opt/nldbl-vsscanf.c
> 	* sysdeps/ieee754/ldbl-opt/nldbl-vswscanf.c
> 	* sysdeps/ieee754/ldbl-opt/nldbl-vwscanf.c
> 	* sysdeps/ieee754/ldbl-opt/nldbl-wscanf.c:
> 	Override __GLIBC_USE_DEPRECATED_SCANF to 1.
> 
> 	* stdio-common/sscanf.c: Likewise.  Remove ldbl_hidden_def for __sscanf.
> 	* stdio-common/isoc99_sscanf.c: Add libc_hidden_def for __isoc99_sscanf.
> 	* include/stdio.h: Provide libc_hidden_proto for __isoc99_sscanf,
> 	not sscanf.
> 	[!__GLIBC_USE(DEPRECATED_SCANF)]: Define sscanf as __isoc99_scanf
> 	with a preprocessor macro.
> 
> 	* stdio-common/bug21.c, stdio-common/scanf14.c:
> 	Use %ms instead of %as, %mS instead of %aS, %m[] instead of %a[];
> 	remove DIAG_IGNORE_NEEDS_COMMENT for -Wformat.
> 	* stdio-common/scanf16.c: Likewise.  Add __attribute__((format(scanf)))
> 	to xscanf, xfscanf, xsscanf.
> 
> 	* stdio-common/scanf14a.c: New copy of scanf14.c which still uses
> 	%as, %aS, %a[].  Use conformance mode -std=gnu89,
> 	_POSIX_C_SOURCE=199506L, _XOPEN_SOURCE=1, _XOPEN_SOURCE_EXTENDED=1,
> 	which will use the nonconformant scanf implementation.
> 	Remove DIAG_IGNORE_NEEDS_COMMENT for -Wformat.
> 	* stdio-common/scanf16a.c: New copy of scanf16.c which still uses
> 	%as, %aS, %a[].  Use conformance mode -std=gnu89,
> 	_POSIX_C_SOURCE=199506L, _XOPEN_SOURCE=1, _XOPEN_SOURCE_EXTENDED=1,
> 	_ISOC99_SOURCE=1, which will use the nonconformant scanf
> 	implementation.  Add __attribute__((format(scanf))) to xscanf,
> 	xfscanf, xsscanf.
> 
> 	* stdio-common/scanf15.c, stdio-common/scanf17.c: No need to
> 	override feature selection macros or provide definitions of u_char etc.
> 	* stdio-common/Makefile (tests): Add scanf14a and scanf16a.
> 	(CFLAGS-scanf15.c, CFLAGS-scanf17.c): Remove.
> 	(CFLAGS-scanf14a.c, CFLAGS-scanf16a.c): New.

The two new tests are missing Copyright headers.  With those added the 
change looks OK to me.

Thanks,
Siddhesh

> ---
>   NEWS                                       |  17 +++
>   include/features.h                         |  22 +++
>   include/stdio.h                            |  11 +-
>   libio/bits/stdio-ldbl.h                    |   7 +-
>   libio/fwscanf.c                            |   5 +
>   libio/iovsscanf.c                          |   5 +
>   libio/iovswscanf.c                         |   5 +
>   libio/stdio.h                              |  22 ++-
>   libio/swscanf.c                            |   5 +
>   libio/vscanf.c                             |   5 +
>   libio/vwscanf.c                            |   5 +
>   libio/wscanf.c                             |   5 +
>   stdio-common/Makefile                      |  14 +-
>   stdio-common/bug21.c                       |  11 +-
>   stdio-common/fscanf.c                      |   5 +
>   stdio-common/isoc99_sscanf.c               |   1 +
>   stdio-common/scanf.c                       |   5 +
>   stdio-common/scanf14.c                     |  35 ++---
>   stdio-common/scanf14a.c                    | 126 +++++++++++++++++
>   stdio-common/scanf15.c                     |  14 +-
>   stdio-common/scanf16.c                     |  20 +--
>   stdio-common/scanf16a.c                    | 156 +++++++++++++++++++++
>   stdio-common/scanf17.c                     |  14 +-
>   stdio-common/sscanf.c                      |   6 +-
>   stdio-common/vfscanf.c                     |   5 +
>   stdio-common/vfwscanf.c                    |   5 +
>   sysdeps/ieee754/ldbl-opt/nldbl-compat.c    |   5 +
>   sysdeps/ieee754/ldbl-opt/nldbl-fscanf.c    |   5 +
>   sysdeps/ieee754/ldbl-opt/nldbl-fwscanf.c   |   5 +
>   sysdeps/ieee754/ldbl-opt/nldbl-iovfscanf.c |   5 +
>   sysdeps/ieee754/ldbl-opt/nldbl-scanf.c     |   5 +
>   sysdeps/ieee754/ldbl-opt/nldbl-sscanf.c    |   5 +
>   sysdeps/ieee754/ldbl-opt/nldbl-swscanf.c   |   5 +
>   sysdeps/ieee754/ldbl-opt/nldbl-vfscanf.c   |   5 +
>   sysdeps/ieee754/ldbl-opt/nldbl-vfwscanf.c  |   5 +
>   sysdeps/ieee754/ldbl-opt/nldbl-vscanf.c    |   5 +
>   sysdeps/ieee754/ldbl-opt/nldbl-vsscanf.c   |   5 +
>   sysdeps/ieee754/ldbl-opt/nldbl-vswscanf.c  |   5 +
>   sysdeps/ieee754/ldbl-opt/nldbl-vwscanf.c   |   5 +
>   sysdeps/ieee754/ldbl-opt/nldbl-wscanf.c    |   5 +
>   wcsmbs/bits/wchar-ldbl.h                   |   7 +-
>   wcsmbs/wchar.h                             |  10 +-
>   42 files changed, 515 insertions(+), 103 deletions(-)
>   create mode 100644 stdio-common/scanf14a.c
>   create mode 100644 stdio-common/scanf16a.c
> 
> diff --git a/NEWS b/NEWS
> index cd29ec7c81..5a682832a6 100644
> --- a/NEWS
> +++ b/NEWS
> @@ -68,6 +68,23 @@ Deprecated and removed features, and other changes affecting compatibility:
>     used by the Linux kernel.  This affects the size and layout of those
>     structures.
>   
> +* An archaic GNU extension to scanf, under which '%as', '%aS', and '%a[...]'
> +  meant to scan a string and allocate space for it with malloc, is now
> +  restricted to programs compiled in C89 or C++98 mode with _GNU_SOURCE
> +  defined.  This extension conflicts with C99's use of '%a' to scan a
> +  hexadecimal floating-point number, which is now available to programs
> +  compiled as C99 or C++11 or higher, regardless of _GNU_SOURCE.
> +
> +  POSIX.1-2008 includes the feature of allocating a buffer for string input
> +  with malloc, using the modifier letter 'm' instead.  Programs using
> +  '%as', '%aS', or '%a[...]' with the old GNU meaning should change to
> +  '%ms', '%mS', or '%m[...]' respectively.  Programs that wish to use the
> +  C99 '%a' no longer need to avoid _GNU_SOURCE.
> +
> +  GCC's -Wformat warnings can detect most uses of this extension, as long
> +  as all functions that call vscanf, vfscanf, or vsscanf are annotated with
> +  __attribute__ ((format (scanf, ...))).
> +
>   Changes to build and runtime requirements:
>   
>   * Python 3.4 or later is required to build the GNU C Library.
> diff --git a/include/features.h b/include/features.h
> index 5bed0a4996..e6177f80f9 100644
> --- a/include/features.h
> +++ b/include/features.h
> @@ -140,6 +140,7 @@
>   #undef	__USE_FORTIFY_LEVEL
>   #undef	__KERNEL_STRICT_NAMES
>   #undef	__GLIBC_USE_DEPRECATED_GETS
> +#undef	__GLIBC_USE_DEPRECATED_SCANF
>   
>   /* Suppress kernel-name space pollution unless user expressedly asks
>      for it.  */
> @@ -401,6 +402,27 @@
>   # define __GLIBC_USE_DEPRECATED_GETS 1
>   #endif
>   
> +/* GNU formerly extended the scanf functions with modified format
> +   specifiers %as, %aS, and %a[...] that allocate a buffer for the
> +   input using malloc.  This extension conflicts with ISO C99, which
> +   defines %a as a standalone format specifier that reads a floating-
> +   point number; moreover, POSIX.1-2008 provides the same feature
> +   using the modifier letter 'm' instead (%ms, %mS, %m[...]).
> +
> +   We now follow C99 unless GNU extensions are active and the compiler
> +   is specifically in C89 or C++98 mode (strict or not).  For
> +   instance, with GCC, -std=gnu11 will have C99-compliant scanf with
> +   or without -D_GNU_SOURCE, but -std=c89 -D_GNU_SOURCE will have the
> +   old extension.  */
> +#if defined __USE_GNU &&						\
> +  (defined __cplusplus							\
> +   ? (__cplusplus < 201103L && !defined __GXX_EXPERIMENTAL_CXX0X__)	\
> +   : (!defined __STDC_VERSION__ || __STDC_VERSION__ < 199901L))
> +# define __GLIBC_USE_DEPRECATED_SCANF 1
> +#else
> +# define __GLIBC_USE_DEPRECATED_SCANF 0
> +#endif
> +
>   /* Get definitions of __STDC_* predefined macros, if the compiler has
>      not preincluded this header automatically.  */
>   #include <stdc-predef.h>
> diff --git a/include/stdio.h b/include/stdio.h
> index 1b7da0f74d..65ccabbb05 100644
> --- a/include/stdio.h
> +++ b/include/stdio.h
> @@ -64,9 +64,19 @@ extern int __isoc99_vscanf (const char *__restrict __format,
>   extern int __isoc99_vsscanf (const char *__restrict __s,
>   			     const char *__restrict __format,
>   			     __gnuc_va_list __arg) __THROW;
> +libc_hidden_proto (__isoc99_sscanf)
>   libc_hidden_proto (__isoc99_vsscanf)
>   libc_hidden_proto (__isoc99_vfscanf)
>   
> +/* Internal uses of sscanf should call the C99-compliant version.
> +   Unfortunately, symbol redirection is not transitive, so the
> +   __REDIRECT in the public header does not link up with the above
> +   libc_hidden_proto.  Bridge the gap with a macro.  */
> +#  if !__GLIBC_USE (DEPRECATED_SCANF)
> +#   undef sscanf
> +#   define sscanf __isoc99_sscanf
> +#  endif
> +
>   /* Prototypes for compatibility functions.  */
>   extern FILE *__new_tmpfile (void);
>   extern FILE *__old_tmpfile (void);
> @@ -171,7 +181,6 @@ libc_hidden_proto (__dprintf)
>   libc_hidden_proto (fprintf)
>   libc_hidden_proto (vfprintf)
>   libc_hidden_proto (sprintf)
> -libc_hidden_proto (sscanf)
>   libc_hidden_proto (fwrite)
>   libc_hidden_proto (perror)
>   libc_hidden_proto (remove)
> diff --git a/libio/bits/stdio-ldbl.h b/libio/bits/stdio-ldbl.h
> index 99d9bcc233..dfcbe5375a 100644
> --- a/libio/bits/stdio-ldbl.h
> +++ b/libio/bits/stdio-ldbl.h
> @@ -26,9 +26,7 @@ __LDBL_REDIR_DECL (sprintf)
>   __LDBL_REDIR_DECL (vfprintf)
>   __LDBL_REDIR_DECL (vprintf)
>   __LDBL_REDIR_DECL (vsprintf)
> -#if defined __USE_ISOC99 && !defined __USE_GNU \
> -    && !defined __REDIRECT \
> -    && (defined __STRICT_ANSI__ || defined __USE_XOPEN2K)
> +#if !__GLIBC_USE (DEPRECATED_SCANF)
>   __LDBL_REDIR1_DECL (fscanf, __nldbl___isoc99_fscanf)
>   __LDBL_REDIR1_DECL (scanf, __nldbl___isoc99_scanf)
>   __LDBL_REDIR1_DECL (sscanf, __nldbl___isoc99_sscanf)
> @@ -44,8 +42,7 @@ __LDBL_REDIR_DECL (vsnprintf)
>   #endif
>   
>   #ifdef	__USE_ISOC99
> -# if !defined __USE_GNU && !defined __REDIRECT \
> -     && (defined __STRICT_ANSI__ || defined __USE_XOPEN2K)
> +# if !__GLIBC_USE (DEPRECATED_SCANF)
>   __LDBL_REDIR1_DECL (vfscanf, __nldbl___isoc99_vfscanf)
>   __LDBL_REDIR1_DECL (vscanf, __nldbl___isoc99_vscanf)
>   __LDBL_REDIR1_DECL (vsscanf, __nldbl___isoc99_vsscanf)
> diff --git a/libio/fwscanf.c b/libio/fwscanf.c
> index ce4b35d31d..32cb9fb699 100644
> --- a/libio/fwscanf.c
> +++ b/libio/fwscanf.c
> @@ -15,6 +15,11 @@
>      License along with the GNU C Library; if not, see
>      <http://www.gnu.org/licenses/>.  */
>   
> +/* This file defines one of the deprecated scanf variants.  */
> +#include <features.h>
> +#undef __GLIBC_USE_DEPRECATED_SCANF
> +#define __GLIBC_USE_DEPRECATED_SCANF 1
> +
>   #include <libioP.h>
>   #include <stdarg.h>
>   #include <stdio.h>
> diff --git a/libio/iovsscanf.c b/libio/iovsscanf.c
> index ee6a99ec6a..69de5a09b6 100644
> --- a/libio/iovsscanf.c
> +++ b/libio/iovsscanf.c
> @@ -24,6 +24,11 @@
>      This exception applies to code released by its copyright holders
>      in files containing the exception.  */
>   
> +/* This file defines one of the deprecated scanf variants.  */
> +#include <features.h>
> +#undef __GLIBC_USE_DEPRECATED_SCANF
> +#define __GLIBC_USE_DEPRECATED_SCANF 1
> +
>   #include "strfile.h"
>   
>   int
> diff --git a/libio/iovswscanf.c b/libio/iovswscanf.c
> index cb9cbe15cc..f4b4657c16 100644
> --- a/libio/iovswscanf.c
> +++ b/libio/iovswscanf.c
> @@ -24,6 +24,11 @@
>      This exception applies to code released by its copyright holders
>      in files containing the exception.  */
>   
> +/* This file defines one of the deprecated scanf variants.  */
> +#include <features.h>
> +#undef __GLIBC_USE_DEPRECATED_SCANF
> +#define __GLIBC_USE_DEPRECATED_SCANF 1
> +
>   #include <wchar.h>
>   #include "strfile.h"
>   
> diff --git a/libio/stdio.h b/libio/stdio.h
> index 739e08610d..86e7015655 100644
> --- a/libio/stdio.h
> +++ b/libio/stdio.h
> @@ -399,13 +399,11 @@ extern int scanf (const char *__restrict __format, ...) __wur;
>   extern int sscanf (const char *__restrict __s,
>   		   const char *__restrict __format, ...) __THROW;
>   
> -#if defined __USE_ISOC99 && !defined __USE_GNU \
> -    && (!defined __LDBL_COMPAT || !defined __REDIRECT) \
> -    && (defined __STRICT_ANSI__ || defined __USE_XOPEN2K)
> +/* For historical reasons, the C99-compliant versions of the scanf
> +   functions are at alternative names.  When __LDBL_COMPAT is in
> +   effect, this is handled in bits/stdio-ldbl.h.  */
> +#if !__GLIBC_USE (DEPRECATED_SCANF) && !defined __LDBL_COMPAT
>   # ifdef __REDIRECT
> -/* For strict ISO C99 or POSIX compliance disallow %as, %aS and %a[
> -   GNU extension which conflicts with valid %a followed by letter
> -   s, S or [.  */
>   extern int __REDIRECT (fscanf, (FILE *__restrict __stream,
>   				const char *__restrict __format, ...),
>   		       __isoc99_fscanf) __wur;
> @@ -447,13 +445,9 @@ extern int vsscanf (const char *__restrict __s,
>   		    const char *__restrict __format, __gnuc_va_list __arg)
>        __THROW __attribute__ ((__format__ (__scanf__, 2, 0)));
>   
> -# if !defined __USE_GNU \
> -     && (!defined __LDBL_COMPAT || !defined __REDIRECT) \
> -     && (defined __STRICT_ANSI__ || defined __USE_XOPEN2K)
> -#  ifdef __REDIRECT
> -/* For strict ISO C99 or POSIX compliance disallow %as, %aS and %a[
> -   GNU extension which conflicts with valid %a followed by letter
> -   s, S or [.  */
> +/* Same redirection as above for the v*scanf family.  */
> +# if !__GLIBC_USE (DEPRECATED_SCANF)
> +#  if defined __REDIRECT && !defined __LDBL_COMPAT
>   extern int __REDIRECT (vfscanf,
>   		       (FILE *__restrict __s,
>   			const char *__restrict __format, __gnuc_va_list __arg),
> @@ -467,7 +461,7 @@ extern int __REDIRECT_NTH (vsscanf,
>   			    const char *__restrict __format,
>   			    __gnuc_va_list __arg), __isoc99_vsscanf)
>        __attribute__ ((__format__ (__scanf__, 2, 0)));
> -#  else
> +#  elif !defined __REDIRECT
>   extern int __isoc99_vfscanf (FILE *__restrict __s,
>   			     const char *__restrict __format,
>   			     __gnuc_va_list __arg) __wur;
> diff --git a/libio/swscanf.c b/libio/swscanf.c
> index 90f721cc51..560f4e3d3e 100644
> --- a/libio/swscanf.c
> +++ b/libio/swscanf.c
> @@ -15,6 +15,11 @@
>      License along with the GNU C Library; if not, see
>      <http://www.gnu.org/licenses/>.  */
>   
> +/* This file defines one of the deprecated scanf variants.  */
> +#include <features.h>
> +#undef __GLIBC_USE_DEPRECATED_SCANF
> +#define __GLIBC_USE_DEPRECATED_SCANF 1
> +
>   #include <stdarg.h>
>   #include "strfile.h"
>   
> diff --git a/libio/vscanf.c b/libio/vscanf.c
> index a3e2dd43f2..db89259ccb 100644
> --- a/libio/vscanf.c
> +++ b/libio/vscanf.c
> @@ -24,6 +24,11 @@
>      This exception applies to code released by its copyright holders
>      in files containing the exception.  */
>   
> +/* This file defines one of the deprecated scanf variants.  */
> +#include <features.h>
> +#undef __GLIBC_USE_DEPRECATED_SCANF
> +#define __GLIBC_USE_DEPRECATED_SCANF 1
> +
>   #include "libioP.h"
>   #include "stdio.h"
>   
> diff --git a/libio/vwscanf.c b/libio/vwscanf.c
> index 7af770c8c3..46595d8ada 100644
> --- a/libio/vwscanf.c
> +++ b/libio/vwscanf.c
> @@ -24,6 +24,11 @@
>      This exception applies to code released by its copyright holders
>      in files containing the exception.  */
>   
> +/* This file defines one of the deprecated scanf variants.  */
> +#include <features.h>
> +#undef __GLIBC_USE_DEPRECATED_SCANF
> +#define __GLIBC_USE_DEPRECATED_SCANF 1
> +
>   #include "libioP.h"
>   #include <wchar.h>
>   
> diff --git a/libio/wscanf.c b/libio/wscanf.c
> index fe27ff6fa6..ed68b60b9e 100644
> --- a/libio/wscanf.c
> +++ b/libio/wscanf.c
> @@ -15,6 +15,11 @@
>      License along with the GNU C Library; if not, see
>      <http://www.gnu.org/licenses/>.  */
>   
> +/* This file defines one of the deprecated scanf variants.  */
> +#include <features.h>
> +#undef __GLIBC_USE_DEPRECATED_SCANF
> +#define __GLIBC_USE_DEPRECATED_SCANF 1
> +
>   #include <libioP.h>
>   #include <stdarg.h>
>   #include <stdio.h>
> diff --git a/stdio-common/Makefile b/stdio-common/Makefile
> index 8978b3fb1f..85348a6077 100644
> --- a/stdio-common/Makefile
> +++ b/stdio-common/Makefile
> @@ -65,6 +65,8 @@ tests := tstscanf test_rdwr test-popen tstgetln test-fseek \
>   	 tst-vfprintf-mbs-prec \
>   	 tst-scanf-round \
>   	 tst-renameat2 tst-bz11319 tst-bz11319-fortify2 \
> +	 scanf14a scanf16a \
> +
>   
>   test-srcs = tst-unbputc tst-printf tst-printfsz-islongdouble
>   
> @@ -146,13 +148,11 @@ CFLAGS-isoc99_scanf.c += -fexceptions
>   CFLAGS-errlist.c += $(fno-unit-at-a-time)
>   CFLAGS-siglist.c += $(fno-unit-at-a-time)
>   
> -# The following is a hack since we must compile scanf1{5,7}.c without any
> -# GNU extension.  The latter are needed, though, when internal headers
> -# are used.  So made sure we see the installed headers first.
> -CFLAGS-scanf15.c += -I../libio -I../stdlib -I../wcsmbs -I../time -I../string \
> -		   -I../wctype
> -CFLAGS-scanf17.c += -I../libio -I../stdlib -I../wcsmbs -I../time -I../string \
> -		   -I../wctype
> +# scanf14a.c and scanf16a.c test a deprecated extension which is no
> +# longer visible under most conformance levels; see the source files
> +# for more detail.
> +CFLAGS-scanf14a.c += -std=gnu89
> +CFLAGS-scanf16a.c += -std=gnu89
>   
>   CFLAGS-bug3.c += -DOBJPFX=\"$(objpfx)\"
>   CFLAGS-bug4.c += -DOBJPFX=\"$(objpfx)\"
> diff --git a/stdio-common/bug21.c b/stdio-common/bug21.c
> index 7a8c6a3542..1f06c0dab4 100644
> --- a/stdio-common/bug21.c
> +++ b/stdio-common/bug21.c
> @@ -1,5 +1,4 @@
>   #include <stdio.h>
> -#include <libc-diag.h>
>   
>   static int
>   do_test (void)
> @@ -7,15 +6,7 @@ do_test (void)
>     static const char buf[] = " ";
>     char *str;
>   
> -  /* GCC in C99 mode treats %a as the C99 format expecting float *,
> -     but glibc with _GNU_SOURCE treats %as as the GNU allocation
> -     extension, so resulting in "warning: format '%a' expects argument
> -     of type 'float *', but argument 3 has type 'char **'".  This
> -     applies to the other %as, %aS and %a[] formats below as well.  */
> -  DIAG_PUSH_NEEDS_COMMENT;
> -  DIAG_IGNORE_NEEDS_COMMENT (4.9, "-Wformat");
> -  int r = sscanf (buf, "%as", &str);
> -  DIAG_POP_NEEDS_COMMENT;
> +  int r = sscanf (buf, "%ms", &str);
>     printf ("%d %p\n", r, str);
>   
>     return r != -1 || str != NULL;
> diff --git a/stdio-common/fscanf.c b/stdio-common/fscanf.c
> index b60a2a3b81..9f2a7cdef9 100644
> --- a/stdio-common/fscanf.c
> +++ b/stdio-common/fscanf.c
> @@ -15,6 +15,11 @@
>      License along with the GNU C Library; if not, see
>      <http://www.gnu.org/licenses/>.  */
>   
> +/* This file defines one of the deprecated scanf variants.  */
> +#include <features.h>
> +#undef __GLIBC_USE_DEPRECATED_SCANF
> +#define __GLIBC_USE_DEPRECATED_SCANF 1
> +
>   #include <libioP.h>
>   #include <stdarg.h>
>   #include <stdio.h>
> diff --git a/stdio-common/isoc99_sscanf.c b/stdio-common/isoc99_sscanf.c
> index c9e5103b81..7edf5307ee 100644
> --- a/stdio-common/isoc99_sscanf.c
> +++ b/stdio-common/isoc99_sscanf.c
> @@ -33,3 +33,4 @@ __isoc99_sscanf (const char *s, const char *format, ...)
>   
>     return done;
>   }
> +libc_hidden_def (__isoc99_sscanf)
> diff --git a/stdio-common/scanf.c b/stdio-common/scanf.c
> index de38d70353..ebcbb070a8 100644
> --- a/stdio-common/scanf.c
> +++ b/stdio-common/scanf.c
> @@ -15,6 +15,11 @@
>      License along with the GNU C Library; if not, see
>      <http://www.gnu.org/licenses/>.  */
>   
> +/* This file defines one of the deprecated scanf variants.  */
> +#include <features.h>
> +#undef __GLIBC_USE_DEPRECATED_SCANF
> +#define __GLIBC_USE_DEPRECATED_SCANF 1
> +
>   #include <stdarg.h>
>   #include <stdio.h>
>   
> diff --git a/stdio-common/scanf14.c b/stdio-common/scanf14.c
> index 2bcd9c9893..22e5f08341 100644
> --- a/stdio-common/scanf14.c
> +++ b/stdio-common/scanf14.c
> @@ -2,7 +2,10 @@
>   #include <stdlib.h>
>   #include <string.h>
>   #include <wchar.h>
> -#include <libc-diag.h>
> +
> +#if __GLIBC_USE_DEPRECATED_SCANF
> +# error "This file should not be compiled with deprecated scanf"
> +#endif
>   
>   #define FAIL() \
>     do {							\
> @@ -24,14 +27,7 @@ main (void)
>       FAIL ();
>     else if (f != 0.25 || memcmp (c, "s x", 3) != 0)
>       FAIL ();
> -  /* GCC in C99 mode treats %a as the C99 format expecting float *,
> -     but glibc with _GNU_SOURCE treats %as as the GNU allocation
> -     extension, so resulting in "warning: format '%a' expects argument
> -     of type 'float *', but argument 3 has type 'char **'".  This
> -     applies to the other %as, %aS and %a[] formats below as well.  */
> -  DIAG_PUSH_NEEDS_COMMENT;
> -  DIAG_IGNORE_NEEDS_COMMENT (4.9, "-Wformat");
> -  if (sscanf (" 1.25s x", "%as%2c", &sp, c) != 2)
> +  if (sscanf (" 1.25s x", "%ms%2c", &sp, c) != 2)
>       FAIL ();
>     else
>       {
> @@ -40,15 +36,11 @@ main (void)
>         memset (sp, 'x', sizeof "1.25s");
>         free (sp);
>       }
> -  DIAG_POP_NEEDS_COMMENT;
>     if (sscanf (" 2.25s x", "%las%2c", &d, c) != 2)
>       FAIL ();
>     else if (d != 2.25 || memcmp (c, " x", 2) != 0)
>       FAIL ();
> -  /* See explanation above.  */
> -  DIAG_PUSH_NEEDS_COMMENT;
> -  DIAG_IGNORE_NEEDS_COMMENT (4.9, "-Wformat");
> -  if (sscanf (" 3.25S x", "%4aS%3c", &lsp, c) != 2)
> +  if (sscanf (" 3.25S x", "%4mS%3c", &lsp, c) != 2)
>       FAIL ();
>     else
>       {
> @@ -57,7 +49,7 @@ main (void)
>         memset (lsp, 'x', sizeof L"3.25");
>         free (lsp);
>       }
> -  if (sscanf ("4.25[0-9.] x", "%a[0-9.]%8c", &sp, c) != 2)
> +  if (sscanf ("4.25[0-9.] x", "%m[0-9.]%8c", &sp, c) != 2)
>       FAIL ();
>     else
>       {
> @@ -66,7 +58,6 @@ main (void)
>         memset (sp, 'x', sizeof "4.25");
>         free (sp);
>       }
> -  DIAG_POP_NEEDS_COMMENT;
>     if (sscanf ("5.25[0-9.] x", "%la[0-9.]%2c", &d, c) != 2)
>       FAIL ();
>     else if (d != 5.25 || memcmp (c, " x", 2) != 0)
> @@ -95,10 +86,7 @@ main (void)
>   	FAIL ();
>         if (fseek (fp, 0, SEEK_SET) != 0)
>   	FAIL ();
> -      /* See explanation above.  */
> -      DIAG_PUSH_NEEDS_COMMENT;
> -      DIAG_IGNORE_NEEDS_COMMENT (4.9, "-Wformat");
> -      if (fscanf (fp, "%as%2c", &sp, c) != 2)
> +      if (fscanf (fp, "%ms%2c", &sp, c) != 2)
>   	FAIL ();
>         else
>   	{
> @@ -107,16 +95,12 @@ main (void)
>   	  memset (sp, 'x', sizeof "1.25s");
>   	  free (sp);
>   	}
> -      DIAG_POP_NEEDS_COMMENT;
>   
>         if (freopen (fname, "r", stdin) == NULL)
>   	FAIL ();
>         else
>   	{
> -	  /* See explanation above.  */
> -	  DIAG_PUSH_NEEDS_COMMENT;
> -	  DIAG_IGNORE_NEEDS_COMMENT (4.9, "-Wformat");
> -	  if (scanf ("%as%2c", &sp, c) != 2)
> +	  if (scanf ("%ms%2c", &sp, c) != 2)
>   	    FAIL ();
>   	  else
>   	    {
> @@ -125,7 +109,6 @@ main (void)
>   	      memset (sp, 'x', sizeof "1.25s");
>   	      free (sp);
>   	    }
> -	  DIAG_POP_NEEDS_COMMENT;
>   	}
>   
>         fclose (fp);
> diff --git a/stdio-common/scanf14a.c b/stdio-common/scanf14a.c
> new file mode 100644
> index 0000000000..34099b8fe3
> --- /dev/null
> +++ b/stdio-common/scanf14a.c
> @@ -0,0 +1,126 @@

This is a big enough test that it should have a copyright notice.

> +/* This test exercises the deprecated GNU %as, %aS, and %a[...] scanf
> +   modifiers, which are not available to programs compiled as C99
> +   anymore; therefore, this file is compiled with -std=gnu89 and C99
> +   syntax must not be used.  */
> +
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <wchar.h>
> +
> +#if !__GLIBC_USE_DEPRECATED_SCANF
> +# error "This file should be compiled with deprecated scanf"
> +#endif
> +
> +
> +#define FAIL() \
> +  do {							\
> +    result = 1;						\
> +    printf ("test at line %d failed\n", __LINE__);	\
> +  } while (0)
> +
> +int
> +main (void)
> +{
> +  wchar_t *lsp;
> +  char *sp;
> +  float f;
> +  double d;
> +  char c[8];
> +  int result = 0;
> +
> +  if (sscanf (" 0.25s x", "%e%3c", &f, c) != 2)
> +    FAIL ();
> +  else if (f != 0.25 || memcmp (c, "s x", 3) != 0)
> +    FAIL ();
> +  if (sscanf (" 1.25s x", "%as%2c", &sp, c) != 2)
> +    FAIL ();
> +  else
> +    {
> +      if (strcmp (sp, "1.25s") != 0 || memcmp (c, " x", 2) != 0)
> +	FAIL ();
> +      memset (sp, 'x', sizeof "1.25s");
> +      free (sp);
> +    }
> +  if (sscanf (" 2.25s x", "%las%2c", &d, c) != 2)
> +    FAIL ();
> +  else if (d != 2.25 || memcmp (c, " x", 2) != 0)
> +    FAIL ();
> +  if (sscanf (" 3.25S x", "%4aS%3c", &lsp, c) != 2)
> +    FAIL ();
> +  else
> +    {
> +      if (wcscmp (lsp, L"3.25") != 0 || memcmp (c, "S x", 3) != 0)
> +	FAIL ();
> +      memset (lsp, 'x', sizeof L"3.25");
> +      free (lsp);
> +    }
> +  if (sscanf ("4.25[0-9.] x", "%a[0-9.]%8c", &sp, c) != 2)
> +    FAIL ();
> +  else
> +    {
> +      if (strcmp (sp, "4.25") != 0 || memcmp (c, "[0-9.] x", 8) != 0)
> +	FAIL ();
> +      memset (sp, 'x', sizeof "4.25");
> +      free (sp);
> +    }
> +  if (sscanf ("5.25[0-9.] x", "%la[0-9.]%2c", &d, c) != 2)
> +    FAIL ();
> +  else if (d != 5.25 || memcmp (c, " x", 2) != 0)
> +    FAIL ();
> +
> +  const char *tmpdir = getenv ("TMPDIR");
> +  if (tmpdir == NULL || tmpdir[0] == '\0')
> +    tmpdir = "/tmp";
> +
> +  char fname[strlen (tmpdir) + sizeof "/tst-scanf14.XXXXXX"];
> +  sprintf (fname, "%s/tst-scanf14.XXXXXX", tmpdir);
> +  if (fname == NULL)
> +    FAIL ();
> +
> +  /* Create a temporary file.   */
> +  int fd = mkstemp (fname);
> +  if (fd == -1)
> +    FAIL ();
> +
> +  FILE *fp = fdopen (fd, "w+");
> +  if (fp == NULL)
> +    FAIL ();
> +  else
> +    {
> +      if (fputs (" 1.25s x", fp) == EOF)
> +	FAIL ();
> +      if (fseek (fp, 0, SEEK_SET) != 0)
> +	FAIL ();
> +      if (fscanf (fp, "%as%2c", &sp, c) != 2)
> +	FAIL ();
> +      else
> +	{
> +	  if (strcmp (sp, "1.25s") != 0 || memcmp (c, " x", 2) != 0)
> +	    FAIL ();
> +	  memset (sp, 'x', sizeof "1.25s");
> +	  free (sp);
> +	}
> +
> +      if (freopen (fname, "r", stdin) == NULL)
> +	FAIL ();
> +      else
> +	{
> +	  if (scanf ("%as%2c", &sp, c) != 2)
> +	    FAIL ();
> +	  else
> +	    {
> +	      if (strcmp (sp, "1.25s") != 0 || memcmp (c, " x", 2) != 0)
> +		FAIL ();
> +	      memset (sp, 'x', sizeof "1.25s");
> +	      free (sp);
> +	    }
> +	}
> +
> +      fclose (fp);
> +    }
> +
> +  remove (fname);
> +
> +  return result;
> +}
> diff --git a/stdio-common/scanf15.c b/stdio-common/scanf15.c
> index a3ab15dea2..142e215ad3 100644
> --- a/stdio-common/scanf15.c
> +++ b/stdio-common/scanf15.c
> @@ -1,18 +1,12 @@
> -#undef _GNU_SOURCE
> -#define _XOPEN_SOURCE 600
> -#undef _LIBC
> -#undef _IO_MTSAFE_IO
> -/* The following macro definitions are a hack.  They word around disabling
> -   the GNU extension while still using a few internal headers.  */
> -#define u_char unsigned char
> -#define u_short unsigned short
> -#define u_int unsigned int
> -#define u_long unsigned long
>   #include <stdio.h>
>   #include <stdlib.h>
>   #include <string.h>
>   #include <wchar.h>
>   
> +#if __GLIBC_USE_DEPRECATED_SCANF
> +# error "This file should not be compiled with deprecated scanf"
> +#endif
> +
>   #define FAIL() \
>     do {							\
>       result = 1;						\
> diff --git a/stdio-common/scanf16.c b/stdio-common/scanf16.c
> index 3e3cb417f2..db640e2e9c 100644
> --- a/stdio-common/scanf16.c
> +++ b/stdio-common/scanf16.c
> @@ -4,13 +4,17 @@
>   #include <string.h>
>   #include <wchar.h>
>   
> +#if __GLIBC_USE_DEPRECATED_SCANF
> +# error "This file should not be compiled with deprecated scanf"
> +#endif
> +
>   #define FAIL() \
>     do {							\
>       result = 1;						\
>       printf ("test at line %d failed\n", __LINE__);	\
>     } while (0)
>   
> -static int
> +static int __attribute__ ((format (scanf, 2, 3)))
>   xsscanf (const char *str, const char *fmt, ...)
>   {
>     va_list ap;
> @@ -20,7 +24,7 @@ xsscanf (const char *str, const char *fmt, ...)
>     return ret;
>   }
>   
> -static int
> +static int __attribute__ ((format (scanf, 1, 2)))
>   xscanf (const char *fmt, ...)
>   {
>     va_list ap;
> @@ -30,7 +34,7 @@ xscanf (const char *fmt, ...)
>     return ret;
>   }
>   
> -static int
> +static int __attribute__ ((format (scanf, 2, 3)))
>   xfscanf (FILE *f, const char *fmt, ...)
>   {
>     va_list ap;
> @@ -54,7 +58,7 @@ main (void)
>       FAIL ();
>     else if (f != 0.25 || memcmp (c, "s x", 3) != 0)
>       FAIL ();
> -  if (xsscanf (" 1.25s x", "%as%2c", &sp, c) != 2)
> +  if (xsscanf (" 1.25s x", "%ms%2c", &sp, c) != 2)
>       FAIL ();
>     else
>       {
> @@ -67,7 +71,7 @@ main (void)
>       FAIL ();
>     else if (d != 2.25 || memcmp (c, " x", 2) != 0)
>       FAIL ();
> -  if (xsscanf (" 3.25S x", "%4aS%3c", &lsp, c) != 2)
> +  if (xsscanf (" 3.25S x", "%4mS%3c", &lsp, c) != 2)
>       FAIL ();
>     else
>       {
> @@ -76,7 +80,7 @@ main (void)
>         memset (lsp, 'x', sizeof L"3.25");
>         free (lsp);
>       }
> -  if (xsscanf ("4.25[0-9.] x", "%a[0-9.]%8c", &sp, c) != 2)
> +  if (xsscanf ("4.25[0-9.] x", "%m[0-9.]%8c", &sp, c) != 2)
>       FAIL ();
>     else
>       {
> @@ -113,7 +117,7 @@ main (void)
>   	FAIL ();
>         if (fseek (fp, 0, SEEK_SET) != 0)
>   	FAIL ();
> -      if (xfscanf (fp, "%as%2c", &sp, c) != 2)
> +      if (xfscanf (fp, "%ms%2c", &sp, c) != 2)
>   	FAIL ();
>         else
>   	{
> @@ -127,7 +131,7 @@ main (void)
>   	FAIL ();
>         else
>   	{
> -	  if (xscanf ("%as%2c", &sp, c) != 2)
> +	  if (xscanf ("%ms%2c", &sp, c) != 2)
>   	    FAIL ();
>   	  else
>   	    {
> diff --git a/stdio-common/scanf16a.c b/stdio-common/scanf16a.c
> new file mode 100644
> index 0000000000..684eeb08f4
> --- /dev/null
> +++ b/stdio-common/scanf16a.c
> @@ -0,0 +1,156 @@

Please add a copyright header.

> +/* This test exercises the deprecated GNU %as, %aS, and %a[...] scanf
> +   modifiers, which are not available to programs compiled as C99
> +   anymore; therefore, this file is compiled with -std=gnu89 and C99
> +   syntax must not be used.  */
> +
> +#include <stdarg.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <wchar.h>
> +
> +#if !__GLIBC_USE_DEPRECATED_SCANF
> +# error "This file should be compiled with deprecated scanf"
> +#endif
> +
> +#define FAIL() \
> +  do {							\
> +    result = 1;						\
> +    printf ("test at line %d failed\n", __LINE__);	\
> +  } while (0)
> +
> +static int __attribute__ ((format (scanf, 2, 3)))
> +xsscanf (const char *str, const char *fmt, ...)
> +{
> +  va_list ap;
> +  va_start (ap, fmt);
> +  int ret = vsscanf (str, fmt, ap);
> +  va_end (ap);
> +  return ret;
> +}
> +
> +static int __attribute__ ((format (scanf, 1, 2)))
> +xscanf (const char *fmt, ...)
> +{
> +  va_list ap;
> +  va_start (ap, fmt);
> +  int ret = vscanf (fmt, ap);
> +  va_end (ap);
> +  return ret;
> +}
> +
> +static int __attribute__ ((format (scanf, 2, 3)))
> +xfscanf (FILE *f, const char *fmt, ...)
> +{
> +  va_list ap;
> +  va_start (ap, fmt);
> +  int ret = vfscanf (f, fmt, ap);
> +  va_end (ap);
> +  return ret;
> +}
> +
> +int
> +main (void)
> +{
> +  wchar_t *lsp;
> +  char *sp;
> +  float f;
> +  double d;
> +  char c[8];
> +  int result = 0;
> +
> +  if (xsscanf (" 0.25s x", "%e%3c", &f, c) != 2)
> +    FAIL ();
> +  else if (f != 0.25 || memcmp (c, "s x", 3) != 0)
> +    FAIL ();
> +  if (xsscanf (" 1.25s x", "%as%2c", &sp, c) != 2)
> +    FAIL ();
> +  else
> +    {
> +      if (strcmp (sp, "1.25s") != 0 || memcmp (c, " x", 2) != 0)
> +	FAIL ();
> +      memset (sp, 'x', sizeof "1.25s");
> +      free (sp);
> +    }
> +  if (xsscanf (" 2.25s x", "%las%2c", &d, c) != 2)
> +    FAIL ();
> +  else if (d != 2.25 || memcmp (c, " x", 2) != 0)
> +    FAIL ();
> +  if (xsscanf (" 3.25S x", "%4aS%3c", &lsp, c) != 2)
> +    FAIL ();
> +  else
> +    {
> +      if (wcscmp (lsp, L"3.25") != 0 || memcmp (c, "S x", 3) != 0)
> +	FAIL ();
> +      memset (lsp, 'x', sizeof L"3.25");
> +      free (lsp);
> +    }
> +  if (xsscanf ("4.25[0-9.] x", "%a[0-9.]%8c", &sp, c) != 2)
> +    FAIL ();
> +  else
> +    {
> +      if (strcmp (sp, "4.25") != 0 || memcmp (c, "[0-9.] x", 8) != 0)
> +	FAIL ();
> +      memset (sp, 'x', sizeof "4.25");
> +      free (sp);
> +    }
> +  if (xsscanf ("5.25[0-9.] x", "%la[0-9.]%2c", &d, c) != 2)
> +    FAIL ();
> +  else if (d != 5.25 || memcmp (c, " x", 2) != 0)
> +    FAIL ();
> +
> +  const char *tmpdir = getenv ("TMPDIR");
> +  if (tmpdir == NULL || tmpdir[0] == '\0')
> +    tmpdir = "/tmp";
> +
> +  char fname[strlen (tmpdir) + sizeof "/tst-scanf16.XXXXXX"];
> +  sprintf (fname, "%s/tst-scanf16.XXXXXX", tmpdir);
> +  if (fname == NULL)
> +    FAIL ();
> +
> +  /* Create a temporary file.   */
> +  int fd = mkstemp (fname);
> +  if (fd == -1)
> +    FAIL ();
> +
> +  FILE *fp = fdopen (fd, "w+");
> +  if (fp == NULL)
> +    FAIL ();
> +  else
> +    {
> +      if (fputs (" 1.25s x", fp) == EOF)
> +	FAIL ();
> +      if (fseek (fp, 0, SEEK_SET) != 0)
> +	FAIL ();
> +      if (xfscanf (fp, "%as%2c", &sp, c) != 2)
> +	FAIL ();
> +      else
> +	{
> +	  if (strcmp (sp, "1.25s") != 0 || memcmp (c, " x", 2) != 0)
> +	    FAIL ();
> +	  memset (sp, 'x', sizeof "1.25s");
> +	  free (sp);
> +	}
> +
> +      if (freopen (fname, "r", stdin) == NULL)
> +	FAIL ();
> +      else
> +	{
> +	  if (xscanf ("%as%2c", &sp, c) != 2)
> +	    FAIL ();
> +	  else
> +	    {
> +	      if (strcmp (sp, "1.25s") != 0 || memcmp (c, " x", 2) != 0)
> +		FAIL ();
> +	      memset (sp, 'x', sizeof "1.25s");
> +	      free (sp);
> +	    }
> +	}
> +
> +      fclose (fp);
> +    }
> +
> +  remove (fname);
> +
> +  return result;
> +}
> diff --git a/stdio-common/scanf17.c b/stdio-common/scanf17.c
> index b6c0e63ab0..5a0ae42686 100644
> --- a/stdio-common/scanf17.c
> +++ b/stdio-common/scanf17.c
> @@ -1,19 +1,13 @@
> -#undef _GNU_SOURCE
> -#define _XOPEN_SOURCE 600
> -#undef _LIBC
> -#undef _IO_MTSAFE_IO
> -/* The following macro definitions are a hack.  They word around disabling
> -   the GNU extension while still using a few internal headers.  */
> -#define u_char unsigned char
> -#define u_short unsigned short
> -#define u_int unsigned int
> -#define u_long unsigned long
>   #include <stdarg.h>
>   #include <stdio.h>
>   #include <stdlib.h>
>   #include <string.h>
>   #include <wchar.h>
>   
> +#if __GLIBC_USE_DEPRECATED_SCANF
> +# error "This file should not be compiled with deprecated scanf"
> +#endif
> +
>   #define FAIL() \
>     do {							\
>       result = 1;						\
> diff --git a/stdio-common/sscanf.c b/stdio-common/sscanf.c
> index e25e9c27a5..1c8c58bbe9 100644
> --- a/stdio-common/sscanf.c
> +++ b/stdio-common/sscanf.c
> @@ -15,6 +15,11 @@
>      License along with the GNU C Library; if not, see
>      <http://www.gnu.org/licenses/>.  */
>   
> +/* This file defines one of the deprecated scanf variants.  */
> +#include <features.h>
> +#undef __GLIBC_USE_DEPRECATED_SCANF
> +#define __GLIBC_USE_DEPRECATED_SCANF 1
> +
>   #include <stdarg.h>
>   #include <libio/strfile.h>
>   
> @@ -34,6 +39,5 @@ __sscanf (const char *s, const char *format, ...)
>   
>     return done;
>   }
> -ldbl_hidden_def (__sscanf, sscanf)
>   ldbl_strong_alias (__sscanf, sscanf)
>   ldbl_strong_alias (__sscanf, _IO_sscanf)
> diff --git a/stdio-common/vfscanf.c b/stdio-common/vfscanf.c
> index 5eedca8340..b9df35e73b 100644
> --- a/stdio-common/vfscanf.c
> +++ b/stdio-common/vfscanf.c
> @@ -15,6 +15,11 @@
>      License along with the GNU C Library; if not, see
>      <http://www.gnu.org/licenses/>.  */
>   
> +/* This file defines one of the deprecated scanf variants.  */
> +#include <features.h>
> +#undef __GLIBC_USE_DEPRECATED_SCANF
> +#define __GLIBC_USE_DEPRECATED_SCANF 1
> +
>   #include <libioP.h>
>   
>   int
> diff --git a/stdio-common/vfwscanf.c b/stdio-common/vfwscanf.c
> index 86980464c2..cbbbd37730 100644
> --- a/stdio-common/vfwscanf.c
> +++ b/stdio-common/vfwscanf.c
> @@ -16,6 +16,11 @@
>      License along with the GNU C Library; if not, see
>      <http://www.gnu.org/licenses/>.  */
>   
> +/* This file defines one of the deprecated scanf variants.  */
> +#include <features.h>
> +#undef __GLIBC_USE_DEPRECATED_SCANF
> +#define __GLIBC_USE_DEPRECATED_SCANF 1
> +
>   #include <libioP.h>
>   
>   int
> diff --git a/sysdeps/ieee754/ldbl-opt/nldbl-compat.c b/sysdeps/ieee754/ldbl-opt/nldbl-compat.c
> index f6dd81759d..a67a18915f 100644
> --- a/sysdeps/ieee754/ldbl-opt/nldbl-compat.c
> +++ b/sysdeps/ieee754/ldbl-opt/nldbl-compat.c
> @@ -17,6 +17,11 @@
>      License along with the GNU C Library; if not, see
>      <http://www.gnu.org/licenses/>.  */
>   
> +/* This file may define some of the deprecated scanf variants.  */
> +#include <features.h>
> +#undef __GLIBC_USE_DEPRECATED_SCANF
> +#define __GLIBC_USE_DEPRECATED_SCANF 1
> +
>   #include <stdarg.h>
>   #include <stdio.h>
>   #include <libio/strfile.h>
> diff --git a/sysdeps/ieee754/ldbl-opt/nldbl-fscanf.c b/sysdeps/ieee754/ldbl-opt/nldbl-fscanf.c
> index 1b768e306f..00507187f3 100644
> --- a/sysdeps/ieee754/ldbl-opt/nldbl-fscanf.c
> +++ b/sysdeps/ieee754/ldbl-opt/nldbl-fscanf.c
> @@ -1,3 +1,8 @@
> +/* This file defines one of the deprecated scanf variants.  */
> +#include <features.h>
> +#undef __GLIBC_USE_DEPRECATED_SCANF
> +#define __GLIBC_USE_DEPRECATED_SCANF 1
> +
>   #include "nldbl-compat.h"
>   
>   int
> diff --git a/sysdeps/ieee754/ldbl-opt/nldbl-fwscanf.c b/sysdeps/ieee754/ldbl-opt/nldbl-fwscanf.c
> index 27fc1a7271..65782894a2 100644
> --- a/sysdeps/ieee754/ldbl-opt/nldbl-fwscanf.c
> +++ b/sysdeps/ieee754/ldbl-opt/nldbl-fwscanf.c
> @@ -1,3 +1,8 @@
> +/* This file defines one of the deprecated scanf variants.  */
> +#include <features.h>
> +#undef __GLIBC_USE_DEPRECATED_SCANF
> +#define __GLIBC_USE_DEPRECATED_SCANF 1
> +
>   #include "nldbl-compat.h"
>   
>   int
> diff --git a/sysdeps/ieee754/ldbl-opt/nldbl-iovfscanf.c b/sysdeps/ieee754/ldbl-opt/nldbl-iovfscanf.c
> index 442c11c203..6ed9b48624 100644
> --- a/sysdeps/ieee754/ldbl-opt/nldbl-iovfscanf.c
> +++ b/sysdeps/ieee754/ldbl-opt/nldbl-iovfscanf.c
> @@ -1,3 +1,8 @@
> +/* This file defines one of the deprecated scanf variants.  */
> +#include <features.h>
> +#undef __GLIBC_USE_DEPRECATED_SCANF
> +#define __GLIBC_USE_DEPRECATED_SCANF 1
> +
>   #include "nldbl-compat.h"
>   
>   int
> diff --git a/sysdeps/ieee754/ldbl-opt/nldbl-scanf.c b/sysdeps/ieee754/ldbl-opt/nldbl-scanf.c
> index bbab371cbe..c10da284f2 100644
> --- a/sysdeps/ieee754/ldbl-opt/nldbl-scanf.c
> +++ b/sysdeps/ieee754/ldbl-opt/nldbl-scanf.c
> @@ -1,3 +1,8 @@
> +/* This file defines one of the deprecated scanf variants.  */
> +#include <features.h>
> +#undef __GLIBC_USE_DEPRECATED_SCANF
> +#define __GLIBC_USE_DEPRECATED_SCANF 1
> +
>   #include "nldbl-compat.h"
>   
>   int
> diff --git a/sysdeps/ieee754/ldbl-opt/nldbl-sscanf.c b/sysdeps/ieee754/ldbl-opt/nldbl-sscanf.c
> index a771d49996..34e18be0a1 100644
> --- a/sysdeps/ieee754/ldbl-opt/nldbl-sscanf.c
> +++ b/sysdeps/ieee754/ldbl-opt/nldbl-sscanf.c
> @@ -1,3 +1,8 @@
> +/* This file defines one of the deprecated scanf variants.  */
> +#include <features.h>
> +#undef __GLIBC_USE_DEPRECATED_SCANF
> +#define __GLIBC_USE_DEPRECATED_SCANF 1
> +
>   #include "nldbl-compat.h"
>   
>   int
> diff --git a/sysdeps/ieee754/ldbl-opt/nldbl-swscanf.c b/sysdeps/ieee754/ldbl-opt/nldbl-swscanf.c
> index dd058f47ab..4076debc4e 100644
> --- a/sysdeps/ieee754/ldbl-opt/nldbl-swscanf.c
> +++ b/sysdeps/ieee754/ldbl-opt/nldbl-swscanf.c
> @@ -1,3 +1,8 @@
> +/* This file defines one of the deprecated scanf variants.  */
> +#include <features.h>
> +#undef __GLIBC_USE_DEPRECATED_SCANF
> +#define __GLIBC_USE_DEPRECATED_SCANF 1
> +
>   #include "nldbl-compat.h"
>   
>   int
> diff --git a/sysdeps/ieee754/ldbl-opt/nldbl-vfscanf.c b/sysdeps/ieee754/ldbl-opt/nldbl-vfscanf.c
> index f23465ee95..6f5ebc3045 100644
> --- a/sysdeps/ieee754/ldbl-opt/nldbl-vfscanf.c
> +++ b/sysdeps/ieee754/ldbl-opt/nldbl-vfscanf.c
> @@ -1,3 +1,8 @@
> +/* This file defines one of the deprecated scanf variants.  */
> +#include <features.h>
> +#undef __GLIBC_USE_DEPRECATED_SCANF
> +#define __GLIBC_USE_DEPRECATED_SCANF 1
> +
>   #include "nldbl-compat.h"
>   
>   int
> diff --git a/sysdeps/ieee754/ldbl-opt/nldbl-vfwscanf.c b/sysdeps/ieee754/ldbl-opt/nldbl-vfwscanf.c
> index be9febc9a0..0bef274726 100644
> --- a/sysdeps/ieee754/ldbl-opt/nldbl-vfwscanf.c
> +++ b/sysdeps/ieee754/ldbl-opt/nldbl-vfwscanf.c
> @@ -1,3 +1,8 @@
> +/* This file defines one of the deprecated scanf variants.  */
> +#include <features.h>
> +#undef __GLIBC_USE_DEPRECATED_SCANF
> +#define __GLIBC_USE_DEPRECATED_SCANF 1
> +
>   #include "nldbl-compat.h"
>   
>   int
> diff --git a/sysdeps/ieee754/ldbl-opt/nldbl-vscanf.c b/sysdeps/ieee754/ldbl-opt/nldbl-vscanf.c
> index e75907b905..38f654aa73 100644
> --- a/sysdeps/ieee754/ldbl-opt/nldbl-vscanf.c
> +++ b/sysdeps/ieee754/ldbl-opt/nldbl-vscanf.c
> @@ -1,3 +1,8 @@
> +/* This file defines one of the deprecated scanf variants.  */
> +#include <features.h>
> +#undef __GLIBC_USE_DEPRECATED_SCANF
> +#define __GLIBC_USE_DEPRECATED_SCANF 1
> +
>   #include "nldbl-compat.h"
>   
>   int
> diff --git a/sysdeps/ieee754/ldbl-opt/nldbl-vsscanf.c b/sysdeps/ieee754/ldbl-opt/nldbl-vsscanf.c
> index f5594c122c..33e10e49a9 100644
> --- a/sysdeps/ieee754/ldbl-opt/nldbl-vsscanf.c
> +++ b/sysdeps/ieee754/ldbl-opt/nldbl-vsscanf.c
> @@ -1,3 +1,8 @@
> +/* This file defines one of the deprecated scanf variants.  */
> +#include <features.h>
> +#undef __GLIBC_USE_DEPRECATED_SCANF
> +#define __GLIBC_USE_DEPRECATED_SCANF 1
> +
>   #include "nldbl-compat.h"
>   
>   int
> diff --git a/sysdeps/ieee754/ldbl-opt/nldbl-vswscanf.c b/sysdeps/ieee754/ldbl-opt/nldbl-vswscanf.c
> index bd4bb5131b..91c0d4cd4f 100644
> --- a/sysdeps/ieee754/ldbl-opt/nldbl-vswscanf.c
> +++ b/sysdeps/ieee754/ldbl-opt/nldbl-vswscanf.c
> @@ -1,3 +1,8 @@
> +/* This file defines one of the deprecated scanf variants.  */
> +#include <features.h>
> +#undef __GLIBC_USE_DEPRECATED_SCANF
> +#define __GLIBC_USE_DEPRECATED_SCANF 1
> +
>   #include "nldbl-compat.h"
>   
>   int
> diff --git a/sysdeps/ieee754/ldbl-opt/nldbl-vwscanf.c b/sysdeps/ieee754/ldbl-opt/nldbl-vwscanf.c
> index d39578ca4e..b3d0f1be18 100644
> --- a/sysdeps/ieee754/ldbl-opt/nldbl-vwscanf.c
> +++ b/sysdeps/ieee754/ldbl-opt/nldbl-vwscanf.c
> @@ -1,3 +1,8 @@
> +/* This file defines one of the deprecated scanf variants.  */
> +#include <features.h>
> +#undef __GLIBC_USE_DEPRECATED_SCANF
> +#define __GLIBC_USE_DEPRECATED_SCANF 1
> +
>   #include "nldbl-compat.h"
>   
>   int
> diff --git a/sysdeps/ieee754/ldbl-opt/nldbl-wscanf.c b/sysdeps/ieee754/ldbl-opt/nldbl-wscanf.c
> index 4ee3fdc15f..2689e27f06 100644
> --- a/sysdeps/ieee754/ldbl-opt/nldbl-wscanf.c
> +++ b/sysdeps/ieee754/ldbl-opt/nldbl-wscanf.c
> @@ -1,3 +1,8 @@
> +/* This file defines one of the deprecated scanf variants.  */
> +#include <features.h>
> +#undef __GLIBC_USE_DEPRECATED_SCANF
> +#define __GLIBC_USE_DEPRECATED_SCANF 1
> +
>   #include "nldbl-compat.h"
>   
>   int
> diff --git a/wcsmbs/bits/wchar-ldbl.h b/wcsmbs/bits/wchar-ldbl.h
> index b91ee9fbf5..ab2601ae5d 100644
> --- a/wcsmbs/bits/wchar-ldbl.h
> +++ b/wcsmbs/bits/wchar-ldbl.h
> @@ -27,9 +27,7 @@ __LDBL_REDIR_DECL (swprintf);
>   __LDBL_REDIR_DECL (vfwprintf);
>   __LDBL_REDIR_DECL (vwprintf);
>   __LDBL_REDIR_DECL (vswprintf);
> -# if defined __USE_ISOC99 && !defined __USE_GNU \
> -     && !defined __REDIRECT \
> -     && (defined __STRICT_ANSI__ || defined __USE_XOPEN2K)
> +# if !__GLIBC_USE (DEPRECATED_SCANF)
>   __LDBL_REDIR1_DECL (fwscanf, __nldbl___isoc99_fwscanf)
>   __LDBL_REDIR1_DECL (wscanf, __nldbl___isoc99_wscanf)
>   __LDBL_REDIR1_DECL (swscanf, __nldbl___isoc99_swscanf)
> @@ -42,8 +40,7 @@ __LDBL_REDIR_DECL (swscanf);
>   
>   #ifdef __USE_ISOC99
>   __LDBL_REDIR1_DECL (wcstold, wcstod);
> -# if !defined __USE_GNU && !defined __REDIRECT \
> -     && (defined __STRICT_ANSI__ || defined __USE_XOPEN2K)
> +# if !__GLIBC_USE (DEPRECATED_SCANF)
>   __LDBL_REDIR1_DECL (vfwscanf, __nldbl___isoc99_vfwscanf)
>   __LDBL_REDIR1_DECL (vwscanf, __nldbl___isoc99_vwscanf)
>   __LDBL_REDIR1_DECL (vswscanf, __nldbl___isoc99_vswscanf)
> diff --git a/wcsmbs/wchar.h b/wcsmbs/wchar.h
> index 6c94f3ebc6..24573656bc 100644
> --- a/wcsmbs/wchar.h
> +++ b/wcsmbs/wchar.h
> @@ -632,13 +632,11 @@ extern int swscanf (const wchar_t *__restrict __s,
>   		    const wchar_t *__restrict __format, ...)
>        __THROW /* __attribute__ ((__format__ (__wscanf__, 2, 3))) */;
>   
> -# if defined __USE_ISOC99 && !defined __USE_GNU \
> -     && (!defined __LDBL_COMPAT || !defined __REDIRECT) \
> -     && (defined __STRICT_ANSI__ || defined __USE_XOPEN2K)
> +/* For historical reasons, the C99-compliant versions of the scanf
> +   functions are at alternative names.  When __LDBL_COMPAT is in
> +   effect, this is handled in bits/wchar-ldbl.h.  */
> +#if !__GLIBC_USE (DEPRECATED_SCANF) && !defined __LDBL_COMPAT
>   #  ifdef __REDIRECT
> -/* For strict ISO C99 or POSIX compliance disallow %as, %aS and %a[
> -   GNU extension which conflicts with valid %a followed by letter
> -   s, S or [.  */
>   extern int __REDIRECT (fwscanf, (__FILE *__restrict __stream,
>   				 const wchar_t *__restrict __format, ...),
>   		       __isoc99_fwscanf)
>
Zack Weinberg Dec. 31, 2018, 5:02 p.m. | #3
On Mon, Dec 31, 2018 at 8:59 AM Siddhesh Poyarekar <siddhesh@gotplt.org> wrote:
>
> The two new tests are missing Copyright headers.  With those added the
> change looks OK to me.

Thanks!  I will make those changes and commit the patch.  I am still
traveling so I may not get to it until Jan. 3 or so.

zw
Zack Weinberg Jan. 3, 2019, 4:17 p.m. | #4
On Mon, Dec 31, 2018 at 12:02 PM Zack Weinberg <zackw@panix.com> wrote:
> On Mon, Dec 31, 2018 at 8:59 AM Siddhesh Poyarekar <siddhesh@gotplt.org> wrote:
> > The two new tests are missing Copyright headers.  With those added the
> > change looks OK to me.
>
> Thanks!  I will make those changes and commit the patch.  I am still
> traveling so I may not get to it until Jan. 3 or so.

The patch has now been committed.

zw

Patch

diff --git a/NEWS b/NEWS
index cd29ec7c81..5a682832a6 100644
--- a/NEWS
+++ b/NEWS
@@ -68,6 +68,23 @@  Deprecated and removed features, and other changes affecting compatibility:
   used by the Linux kernel.  This affects the size and layout of those
   structures.
 
+* An archaic GNU extension to scanf, under which '%as', '%aS', and '%a[...]'
+  meant to scan a string and allocate space for it with malloc, is now
+  restricted to programs compiled in C89 or C++98 mode with _GNU_SOURCE
+  defined.  This extension conflicts with C99's use of '%a' to scan a
+  hexadecimal floating-point number, which is now available to programs
+  compiled as C99 or C++11 or higher, regardless of _GNU_SOURCE.
+
+  POSIX.1-2008 includes the feature of allocating a buffer for string input
+  with malloc, using the modifier letter 'm' instead.  Programs using
+  '%as', '%aS', or '%a[...]' with the old GNU meaning should change to
+  '%ms', '%mS', or '%m[...]' respectively.  Programs that wish to use the
+  C99 '%a' no longer need to avoid _GNU_SOURCE.
+
+  GCC's -Wformat warnings can detect most uses of this extension, as long
+  as all functions that call vscanf, vfscanf, or vsscanf are annotated with
+  __attribute__ ((format (scanf, ...))).
+
 Changes to build and runtime requirements:
 
 * Python 3.4 or later is required to build the GNU C Library.
diff --git a/include/features.h b/include/features.h
index 5bed0a4996..e6177f80f9 100644
--- a/include/features.h
+++ b/include/features.h
@@ -140,6 +140,7 @@ 
 #undef	__USE_FORTIFY_LEVEL
 #undef	__KERNEL_STRICT_NAMES
 #undef	__GLIBC_USE_DEPRECATED_GETS
+#undef	__GLIBC_USE_DEPRECATED_SCANF
 
 /* Suppress kernel-name space pollution unless user expressedly asks
    for it.  */
@@ -401,6 +402,27 @@ 
 # define __GLIBC_USE_DEPRECATED_GETS 1
 #endif
 
+/* GNU formerly extended the scanf functions with modified format
+   specifiers %as, %aS, and %a[...] that allocate a buffer for the
+   input using malloc.  This extension conflicts with ISO C99, which
+   defines %a as a standalone format specifier that reads a floating-
+   point number; moreover, POSIX.1-2008 provides the same feature
+   using the modifier letter 'm' instead (%ms, %mS, %m[...]).
+
+   We now follow C99 unless GNU extensions are active and the compiler
+   is specifically in C89 or C++98 mode (strict or not).  For
+   instance, with GCC, -std=gnu11 will have C99-compliant scanf with
+   or without -D_GNU_SOURCE, but -std=c89 -D_GNU_SOURCE will have the
+   old extension.  */
+#if defined __USE_GNU &&						\
+  (defined __cplusplus							\
+   ? (__cplusplus < 201103L && !defined __GXX_EXPERIMENTAL_CXX0X__)	\
+   : (!defined __STDC_VERSION__ || __STDC_VERSION__ < 199901L))
+# define __GLIBC_USE_DEPRECATED_SCANF 1
+#else
+# define __GLIBC_USE_DEPRECATED_SCANF 0
+#endif
+
 /* Get definitions of __STDC_* predefined macros, if the compiler has
    not preincluded this header automatically.  */
 #include <stdc-predef.h>
diff --git a/include/stdio.h b/include/stdio.h
index 1b7da0f74d..65ccabbb05 100644
--- a/include/stdio.h
+++ b/include/stdio.h
@@ -64,9 +64,19 @@  extern int __isoc99_vscanf (const char *__restrict __format,
 extern int __isoc99_vsscanf (const char *__restrict __s,
 			     const char *__restrict __format,
 			     __gnuc_va_list __arg) __THROW;
+libc_hidden_proto (__isoc99_sscanf)
 libc_hidden_proto (__isoc99_vsscanf)
 libc_hidden_proto (__isoc99_vfscanf)
 
+/* Internal uses of sscanf should call the C99-compliant version.
+   Unfortunately, symbol redirection is not transitive, so the
+   __REDIRECT in the public header does not link up with the above
+   libc_hidden_proto.  Bridge the gap with a macro.  */
+#  if !__GLIBC_USE (DEPRECATED_SCANF)
+#   undef sscanf
+#   define sscanf __isoc99_sscanf
+#  endif
+
 /* Prototypes for compatibility functions.  */
 extern FILE *__new_tmpfile (void);
 extern FILE *__old_tmpfile (void);
@@ -171,7 +181,6 @@  libc_hidden_proto (__dprintf)
 libc_hidden_proto (fprintf)
 libc_hidden_proto (vfprintf)
 libc_hidden_proto (sprintf)
-libc_hidden_proto (sscanf)
 libc_hidden_proto (fwrite)
 libc_hidden_proto (perror)
 libc_hidden_proto (remove)
diff --git a/libio/bits/stdio-ldbl.h b/libio/bits/stdio-ldbl.h
index 99d9bcc233..dfcbe5375a 100644
--- a/libio/bits/stdio-ldbl.h
+++ b/libio/bits/stdio-ldbl.h
@@ -26,9 +26,7 @@  __LDBL_REDIR_DECL (sprintf)
 __LDBL_REDIR_DECL (vfprintf)
 __LDBL_REDIR_DECL (vprintf)
 __LDBL_REDIR_DECL (vsprintf)
-#if defined __USE_ISOC99 && !defined __USE_GNU \
-    && !defined __REDIRECT \
-    && (defined __STRICT_ANSI__ || defined __USE_XOPEN2K)
+#if !__GLIBC_USE (DEPRECATED_SCANF)
 __LDBL_REDIR1_DECL (fscanf, __nldbl___isoc99_fscanf)
 __LDBL_REDIR1_DECL (scanf, __nldbl___isoc99_scanf)
 __LDBL_REDIR1_DECL (sscanf, __nldbl___isoc99_sscanf)
@@ -44,8 +42,7 @@  __LDBL_REDIR_DECL (vsnprintf)
 #endif
 
 #ifdef	__USE_ISOC99
-# if !defined __USE_GNU && !defined __REDIRECT \
-     && (defined __STRICT_ANSI__ || defined __USE_XOPEN2K)
+# if !__GLIBC_USE (DEPRECATED_SCANF)
 __LDBL_REDIR1_DECL (vfscanf, __nldbl___isoc99_vfscanf)
 __LDBL_REDIR1_DECL (vscanf, __nldbl___isoc99_vscanf)
 __LDBL_REDIR1_DECL (vsscanf, __nldbl___isoc99_vsscanf)
diff --git a/libio/fwscanf.c b/libio/fwscanf.c
index ce4b35d31d..32cb9fb699 100644
--- a/libio/fwscanf.c
+++ b/libio/fwscanf.c
@@ -15,6 +15,11 @@ 
    License along with the GNU C Library; if not, see
    <http://www.gnu.org/licenses/>.  */
 
+/* This file defines one of the deprecated scanf variants.  */
+#include <features.h>
+#undef __GLIBC_USE_DEPRECATED_SCANF
+#define __GLIBC_USE_DEPRECATED_SCANF 1
+
 #include <libioP.h>
 #include <stdarg.h>
 #include <stdio.h>
diff --git a/libio/iovsscanf.c b/libio/iovsscanf.c
index ee6a99ec6a..69de5a09b6 100644
--- a/libio/iovsscanf.c
+++ b/libio/iovsscanf.c
@@ -24,6 +24,11 @@ 
    This exception applies to code released by its copyright holders
    in files containing the exception.  */
 
+/* This file defines one of the deprecated scanf variants.  */
+#include <features.h>
+#undef __GLIBC_USE_DEPRECATED_SCANF
+#define __GLIBC_USE_DEPRECATED_SCANF 1
+
 #include "strfile.h"
 
 int
diff --git a/libio/iovswscanf.c b/libio/iovswscanf.c
index cb9cbe15cc..f4b4657c16 100644
--- a/libio/iovswscanf.c
+++ b/libio/iovswscanf.c
@@ -24,6 +24,11 @@ 
    This exception applies to code released by its copyright holders
    in files containing the exception.  */
 
+/* This file defines one of the deprecated scanf variants.  */
+#include <features.h>
+#undef __GLIBC_USE_DEPRECATED_SCANF
+#define __GLIBC_USE_DEPRECATED_SCANF 1
+
 #include <wchar.h>
 #include "strfile.h"
 
diff --git a/libio/stdio.h b/libio/stdio.h
index 739e08610d..86e7015655 100644
--- a/libio/stdio.h
+++ b/libio/stdio.h
@@ -399,13 +399,11 @@  extern int scanf (const char *__restrict __format, ...) __wur;
 extern int sscanf (const char *__restrict __s,
 		   const char *__restrict __format, ...) __THROW;
 
-#if defined __USE_ISOC99 && !defined __USE_GNU \
-    && (!defined __LDBL_COMPAT || !defined __REDIRECT) \
-    && (defined __STRICT_ANSI__ || defined __USE_XOPEN2K)
+/* For historical reasons, the C99-compliant versions of the scanf
+   functions are at alternative names.  When __LDBL_COMPAT is in
+   effect, this is handled in bits/stdio-ldbl.h.  */
+#if !__GLIBC_USE (DEPRECATED_SCANF) && !defined __LDBL_COMPAT
 # ifdef __REDIRECT
-/* For strict ISO C99 or POSIX compliance disallow %as, %aS and %a[
-   GNU extension which conflicts with valid %a followed by letter
-   s, S or [.  */
 extern int __REDIRECT (fscanf, (FILE *__restrict __stream,
 				const char *__restrict __format, ...),
 		       __isoc99_fscanf) __wur;
@@ -447,13 +445,9 @@  extern int vsscanf (const char *__restrict __s,
 		    const char *__restrict __format, __gnuc_va_list __arg)
      __THROW __attribute__ ((__format__ (__scanf__, 2, 0)));
 
-# if !defined __USE_GNU \
-     && (!defined __LDBL_COMPAT || !defined __REDIRECT) \
-     && (defined __STRICT_ANSI__ || defined __USE_XOPEN2K)
-#  ifdef __REDIRECT
-/* For strict ISO C99 or POSIX compliance disallow %as, %aS and %a[
-   GNU extension which conflicts with valid %a followed by letter
-   s, S or [.  */
+/* Same redirection as above for the v*scanf family.  */
+# if !__GLIBC_USE (DEPRECATED_SCANF)
+#  if defined __REDIRECT && !defined __LDBL_COMPAT
 extern int __REDIRECT (vfscanf,
 		       (FILE *__restrict __s,
 			const char *__restrict __format, __gnuc_va_list __arg),
@@ -467,7 +461,7 @@  extern int __REDIRECT_NTH (vsscanf,
 			    const char *__restrict __format,
 			    __gnuc_va_list __arg), __isoc99_vsscanf)
      __attribute__ ((__format__ (__scanf__, 2, 0)));
-#  else
+#  elif !defined __REDIRECT
 extern int __isoc99_vfscanf (FILE *__restrict __s,
 			     const char *__restrict __format,
 			     __gnuc_va_list __arg) __wur;
diff --git a/libio/swscanf.c b/libio/swscanf.c
index 90f721cc51..560f4e3d3e 100644
--- a/libio/swscanf.c
+++ b/libio/swscanf.c
@@ -15,6 +15,11 @@ 
    License along with the GNU C Library; if not, see
    <http://www.gnu.org/licenses/>.  */
 
+/* This file defines one of the deprecated scanf variants.  */
+#include <features.h>
+#undef __GLIBC_USE_DEPRECATED_SCANF
+#define __GLIBC_USE_DEPRECATED_SCANF 1
+
 #include <stdarg.h>
 #include "strfile.h"
 
diff --git a/libio/vscanf.c b/libio/vscanf.c
index a3e2dd43f2..db89259ccb 100644
--- a/libio/vscanf.c
+++ b/libio/vscanf.c
@@ -24,6 +24,11 @@ 
    This exception applies to code released by its copyright holders
    in files containing the exception.  */
 
+/* This file defines one of the deprecated scanf variants.  */
+#include <features.h>
+#undef __GLIBC_USE_DEPRECATED_SCANF
+#define __GLIBC_USE_DEPRECATED_SCANF 1
+
 #include "libioP.h"
 #include "stdio.h"
 
diff --git a/libio/vwscanf.c b/libio/vwscanf.c
index 7af770c8c3..46595d8ada 100644
--- a/libio/vwscanf.c
+++ b/libio/vwscanf.c
@@ -24,6 +24,11 @@ 
    This exception applies to code released by its copyright holders
    in files containing the exception.  */
 
+/* This file defines one of the deprecated scanf variants.  */
+#include <features.h>
+#undef __GLIBC_USE_DEPRECATED_SCANF
+#define __GLIBC_USE_DEPRECATED_SCANF 1
+
 #include "libioP.h"
 #include <wchar.h>
 
diff --git a/libio/wscanf.c b/libio/wscanf.c
index fe27ff6fa6..ed68b60b9e 100644
--- a/libio/wscanf.c
+++ b/libio/wscanf.c
@@ -15,6 +15,11 @@ 
    License along with the GNU C Library; if not, see
    <http://www.gnu.org/licenses/>.  */
 
+/* This file defines one of the deprecated scanf variants.  */
+#include <features.h>
+#undef __GLIBC_USE_DEPRECATED_SCANF
+#define __GLIBC_USE_DEPRECATED_SCANF 1
+
 #include <libioP.h>
 #include <stdarg.h>
 #include <stdio.h>
diff --git a/stdio-common/Makefile b/stdio-common/Makefile
index 8978b3fb1f..85348a6077 100644
--- a/stdio-common/Makefile
+++ b/stdio-common/Makefile
@@ -65,6 +65,8 @@  tests := tstscanf test_rdwr test-popen tstgetln test-fseek \
 	 tst-vfprintf-mbs-prec \
 	 tst-scanf-round \
 	 tst-renameat2 tst-bz11319 tst-bz11319-fortify2 \
+	 scanf14a scanf16a \
+
 
 test-srcs = tst-unbputc tst-printf tst-printfsz-islongdouble
 
@@ -146,13 +148,11 @@  CFLAGS-isoc99_scanf.c += -fexceptions
 CFLAGS-errlist.c += $(fno-unit-at-a-time)
 CFLAGS-siglist.c += $(fno-unit-at-a-time)
 
-# The following is a hack since we must compile scanf1{5,7}.c without any
-# GNU extension.  The latter are needed, though, when internal headers
-# are used.  So made sure we see the installed headers first.
-CFLAGS-scanf15.c += -I../libio -I../stdlib -I../wcsmbs -I../time -I../string \
-		   -I../wctype
-CFLAGS-scanf17.c += -I../libio -I../stdlib -I../wcsmbs -I../time -I../string \
-		   -I../wctype
+# scanf14a.c and scanf16a.c test a deprecated extension which is no
+# longer visible under most conformance levels; see the source files
+# for more detail.
+CFLAGS-scanf14a.c += -std=gnu89
+CFLAGS-scanf16a.c += -std=gnu89
 
 CFLAGS-bug3.c += -DOBJPFX=\"$(objpfx)\"
 CFLAGS-bug4.c += -DOBJPFX=\"$(objpfx)\"
diff --git a/stdio-common/bug21.c b/stdio-common/bug21.c
index 7a8c6a3542..1f06c0dab4 100644
--- a/stdio-common/bug21.c
+++ b/stdio-common/bug21.c
@@ -1,5 +1,4 @@ 
 #include <stdio.h>
-#include <libc-diag.h>
 
 static int
 do_test (void)
@@ -7,15 +6,7 @@  do_test (void)
   static const char buf[] = " ";
   char *str;
 
-  /* GCC in C99 mode treats %a as the C99 format expecting float *,
-     but glibc with _GNU_SOURCE treats %as as the GNU allocation
-     extension, so resulting in "warning: format '%a' expects argument
-     of type 'float *', but argument 3 has type 'char **'".  This
-     applies to the other %as, %aS and %a[] formats below as well.  */
-  DIAG_PUSH_NEEDS_COMMENT;
-  DIAG_IGNORE_NEEDS_COMMENT (4.9, "-Wformat");
-  int r = sscanf (buf, "%as", &str);
-  DIAG_POP_NEEDS_COMMENT;
+  int r = sscanf (buf, "%ms", &str);
   printf ("%d %p\n", r, str);
 
   return r != -1 || str != NULL;
diff --git a/stdio-common/fscanf.c b/stdio-common/fscanf.c
index b60a2a3b81..9f2a7cdef9 100644
--- a/stdio-common/fscanf.c
+++ b/stdio-common/fscanf.c
@@ -15,6 +15,11 @@ 
    License along with the GNU C Library; if not, see
    <http://www.gnu.org/licenses/>.  */
 
+/* This file defines one of the deprecated scanf variants.  */
+#include <features.h>
+#undef __GLIBC_USE_DEPRECATED_SCANF
+#define __GLIBC_USE_DEPRECATED_SCANF 1
+
 #include <libioP.h>
 #include <stdarg.h>
 #include <stdio.h>
diff --git a/stdio-common/isoc99_sscanf.c b/stdio-common/isoc99_sscanf.c
index c9e5103b81..7edf5307ee 100644
--- a/stdio-common/isoc99_sscanf.c
+++ b/stdio-common/isoc99_sscanf.c
@@ -33,3 +33,4 @@  __isoc99_sscanf (const char *s, const char *format, ...)
 
   return done;
 }
+libc_hidden_def (__isoc99_sscanf)
diff --git a/stdio-common/scanf.c b/stdio-common/scanf.c
index de38d70353..ebcbb070a8 100644
--- a/stdio-common/scanf.c
+++ b/stdio-common/scanf.c
@@ -15,6 +15,11 @@ 
    License along with the GNU C Library; if not, see
    <http://www.gnu.org/licenses/>.  */
 
+/* This file defines one of the deprecated scanf variants.  */
+#include <features.h>
+#undef __GLIBC_USE_DEPRECATED_SCANF
+#define __GLIBC_USE_DEPRECATED_SCANF 1
+
 #include <stdarg.h>
 #include <stdio.h>
 
diff --git a/stdio-common/scanf14.c b/stdio-common/scanf14.c
index 2bcd9c9893..22e5f08341 100644
--- a/stdio-common/scanf14.c
+++ b/stdio-common/scanf14.c
@@ -2,7 +2,10 @@ 
 #include <stdlib.h>
 #include <string.h>
 #include <wchar.h>
-#include <libc-diag.h>
+
+#if __GLIBC_USE_DEPRECATED_SCANF
+# error "This file should not be compiled with deprecated scanf"
+#endif
 
 #define FAIL() \
   do {							\
@@ -24,14 +27,7 @@  main (void)
     FAIL ();
   else if (f != 0.25 || memcmp (c, "s x", 3) != 0)
     FAIL ();
-  /* GCC in C99 mode treats %a as the C99 format expecting float *,
-     but glibc with _GNU_SOURCE treats %as as the GNU allocation
-     extension, so resulting in "warning: format '%a' expects argument
-     of type 'float *', but argument 3 has type 'char **'".  This
-     applies to the other %as, %aS and %a[] formats below as well.  */
-  DIAG_PUSH_NEEDS_COMMENT;
-  DIAG_IGNORE_NEEDS_COMMENT (4.9, "-Wformat");
-  if (sscanf (" 1.25s x", "%as%2c", &sp, c) != 2)
+  if (sscanf (" 1.25s x", "%ms%2c", &sp, c) != 2)
     FAIL ();
   else
     {
@@ -40,15 +36,11 @@  main (void)
       memset (sp, 'x', sizeof "1.25s");
       free (sp);
     }
-  DIAG_POP_NEEDS_COMMENT;
   if (sscanf (" 2.25s x", "%las%2c", &d, c) != 2)
     FAIL ();
   else if (d != 2.25 || memcmp (c, " x", 2) != 0)
     FAIL ();
-  /* See explanation above.  */
-  DIAG_PUSH_NEEDS_COMMENT;
-  DIAG_IGNORE_NEEDS_COMMENT (4.9, "-Wformat");
-  if (sscanf (" 3.25S x", "%4aS%3c", &lsp, c) != 2)
+  if (sscanf (" 3.25S x", "%4mS%3c", &lsp, c) != 2)
     FAIL ();
   else
     {
@@ -57,7 +49,7 @@  main (void)
       memset (lsp, 'x', sizeof L"3.25");
       free (lsp);
     }
-  if (sscanf ("4.25[0-9.] x", "%a[0-9.]%8c", &sp, c) != 2)
+  if (sscanf ("4.25[0-9.] x", "%m[0-9.]%8c", &sp, c) != 2)
     FAIL ();
   else
     {
@@ -66,7 +58,6 @@  main (void)
       memset (sp, 'x', sizeof "4.25");
       free (sp);
     }
-  DIAG_POP_NEEDS_COMMENT;
   if (sscanf ("5.25[0-9.] x", "%la[0-9.]%2c", &d, c) != 2)
     FAIL ();
   else if (d != 5.25 || memcmp (c, " x", 2) != 0)
@@ -95,10 +86,7 @@  main (void)
 	FAIL ();
       if (fseek (fp, 0, SEEK_SET) != 0)
 	FAIL ();
-      /* See explanation above.  */
-      DIAG_PUSH_NEEDS_COMMENT;
-      DIAG_IGNORE_NEEDS_COMMENT (4.9, "-Wformat");
-      if (fscanf (fp, "%as%2c", &sp, c) != 2)
+      if (fscanf (fp, "%ms%2c", &sp, c) != 2)
 	FAIL ();
       else
 	{
@@ -107,16 +95,12 @@  main (void)
 	  memset (sp, 'x', sizeof "1.25s");
 	  free (sp);
 	}
-      DIAG_POP_NEEDS_COMMENT;
 
       if (freopen (fname, "r", stdin) == NULL)
 	FAIL ();
       else
 	{
-	  /* See explanation above.  */
-	  DIAG_PUSH_NEEDS_COMMENT;
-	  DIAG_IGNORE_NEEDS_COMMENT (4.9, "-Wformat");
-	  if (scanf ("%as%2c", &sp, c) != 2)
+	  if (scanf ("%ms%2c", &sp, c) != 2)
 	    FAIL ();
 	  else
 	    {
@@ -125,7 +109,6 @@  main (void)
 	      memset (sp, 'x', sizeof "1.25s");
 	      free (sp);
 	    }
-	  DIAG_POP_NEEDS_COMMENT;
 	}
 
       fclose (fp);
diff --git a/stdio-common/scanf14a.c b/stdio-common/scanf14a.c
new file mode 100644
index 0000000000..34099b8fe3
--- /dev/null
+++ b/stdio-common/scanf14a.c
@@ -0,0 +1,126 @@ 
+/* This test exercises the deprecated GNU %as, %aS, and %a[...] scanf
+   modifiers, which are not available to programs compiled as C99
+   anymore; therefore, this file is compiled with -std=gnu89 and C99
+   syntax must not be used.  */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+
+#if !__GLIBC_USE_DEPRECATED_SCANF
+# error "This file should be compiled with deprecated scanf"
+#endif
+
+
+#define FAIL() \
+  do {							\
+    result = 1;						\
+    printf ("test at line %d failed\n", __LINE__);	\
+  } while (0)
+
+int
+main (void)
+{
+  wchar_t *lsp;
+  char *sp;
+  float f;
+  double d;
+  char c[8];
+  int result = 0;
+
+  if (sscanf (" 0.25s x", "%e%3c", &f, c) != 2)
+    FAIL ();
+  else if (f != 0.25 || memcmp (c, "s x", 3) != 0)
+    FAIL ();
+  if (sscanf (" 1.25s x", "%as%2c", &sp, c) != 2)
+    FAIL ();
+  else
+    {
+      if (strcmp (sp, "1.25s") != 0 || memcmp (c, " x", 2) != 0)
+	FAIL ();
+      memset (sp, 'x', sizeof "1.25s");
+      free (sp);
+    }
+  if (sscanf (" 2.25s x", "%las%2c", &d, c) != 2)
+    FAIL ();
+  else if (d != 2.25 || memcmp (c, " x", 2) != 0)
+    FAIL ();
+  if (sscanf (" 3.25S x", "%4aS%3c", &lsp, c) != 2)
+    FAIL ();
+  else
+    {
+      if (wcscmp (lsp, L"3.25") != 0 || memcmp (c, "S x", 3) != 0)
+	FAIL ();
+      memset (lsp, 'x', sizeof L"3.25");
+      free (lsp);
+    }
+  if (sscanf ("4.25[0-9.] x", "%a[0-9.]%8c", &sp, c) != 2)
+    FAIL ();
+  else
+    {
+      if (strcmp (sp, "4.25") != 0 || memcmp (c, "[0-9.] x", 8) != 0)
+	FAIL ();
+      memset (sp, 'x', sizeof "4.25");
+      free (sp);
+    }
+  if (sscanf ("5.25[0-9.] x", "%la[0-9.]%2c", &d, c) != 2)
+    FAIL ();
+  else if (d != 5.25 || memcmp (c, " x", 2) != 0)
+    FAIL ();
+
+  const char *tmpdir = getenv ("TMPDIR");
+  if (tmpdir == NULL || tmpdir[0] == '\0')
+    tmpdir = "/tmp";
+
+  char fname[strlen (tmpdir) + sizeof "/tst-scanf14.XXXXXX"];
+  sprintf (fname, "%s/tst-scanf14.XXXXXX", tmpdir);
+  if (fname == NULL)
+    FAIL ();
+
+  /* Create a temporary file.   */
+  int fd = mkstemp (fname);
+  if (fd == -1)
+    FAIL ();
+
+  FILE *fp = fdopen (fd, "w+");
+  if (fp == NULL)
+    FAIL ();
+  else
+    {
+      if (fputs (" 1.25s x", fp) == EOF)
+	FAIL ();
+      if (fseek (fp, 0, SEEK_SET) != 0)
+	FAIL ();
+      if (fscanf (fp, "%as%2c", &sp, c) != 2)
+	FAIL ();
+      else
+	{
+	  if (strcmp (sp, "1.25s") != 0 || memcmp (c, " x", 2) != 0)
+	    FAIL ();
+	  memset (sp, 'x', sizeof "1.25s");
+	  free (sp);
+	}
+
+      if (freopen (fname, "r", stdin) == NULL)
+	FAIL ();
+      else
+	{
+	  if (scanf ("%as%2c", &sp, c) != 2)
+	    FAIL ();
+	  else
+	    {
+	      if (strcmp (sp, "1.25s") != 0 || memcmp (c, " x", 2) != 0)
+		FAIL ();
+	      memset (sp, 'x', sizeof "1.25s");
+	      free (sp);
+	    }
+	}
+
+      fclose (fp);
+    }
+
+  remove (fname);
+
+  return result;
+}
diff --git a/stdio-common/scanf15.c b/stdio-common/scanf15.c
index a3ab15dea2..142e215ad3 100644
--- a/stdio-common/scanf15.c
+++ b/stdio-common/scanf15.c
@@ -1,18 +1,12 @@ 
-#undef _GNU_SOURCE
-#define _XOPEN_SOURCE 600
-#undef _LIBC
-#undef _IO_MTSAFE_IO
-/* The following macro definitions are a hack.  They word around disabling
-   the GNU extension while still using a few internal headers.  */
-#define u_char unsigned char
-#define u_short unsigned short
-#define u_int unsigned int
-#define u_long unsigned long
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <wchar.h>
 
+#if __GLIBC_USE_DEPRECATED_SCANF
+# error "This file should not be compiled with deprecated scanf"
+#endif
+
 #define FAIL() \
   do {							\
     result = 1;						\
diff --git a/stdio-common/scanf16.c b/stdio-common/scanf16.c
index 3e3cb417f2..db640e2e9c 100644
--- a/stdio-common/scanf16.c
+++ b/stdio-common/scanf16.c
@@ -4,13 +4,17 @@ 
 #include <string.h>
 #include <wchar.h>
 
+#if __GLIBC_USE_DEPRECATED_SCANF
+# error "This file should not be compiled with deprecated scanf"
+#endif
+
 #define FAIL() \
   do {							\
     result = 1;						\
     printf ("test at line %d failed\n", __LINE__);	\
   } while (0)
 
-static int
+static int __attribute__ ((format (scanf, 2, 3)))
 xsscanf (const char *str, const char *fmt, ...)
 {
   va_list ap;
@@ -20,7 +24,7 @@  xsscanf (const char *str, const char *fmt, ...)
   return ret;
 }
 
-static int
+static int __attribute__ ((format (scanf, 1, 2)))
 xscanf (const char *fmt, ...)
 {
   va_list ap;
@@ -30,7 +34,7 @@  xscanf (const char *fmt, ...)
   return ret;
 }
 
-static int
+static int __attribute__ ((format (scanf, 2, 3)))
 xfscanf (FILE *f, const char *fmt, ...)
 {
   va_list ap;
@@ -54,7 +58,7 @@  main (void)
     FAIL ();
   else if (f != 0.25 || memcmp (c, "s x", 3) != 0)
     FAIL ();
-  if (xsscanf (" 1.25s x", "%as%2c", &sp, c) != 2)
+  if (xsscanf (" 1.25s x", "%ms%2c", &sp, c) != 2)
     FAIL ();
   else
     {
@@ -67,7 +71,7 @@  main (void)
     FAIL ();
   else if (d != 2.25 || memcmp (c, " x", 2) != 0)
     FAIL ();
-  if (xsscanf (" 3.25S x", "%4aS%3c", &lsp, c) != 2)
+  if (xsscanf (" 3.25S x", "%4mS%3c", &lsp, c) != 2)
     FAIL ();
   else
     {
@@ -76,7 +80,7 @@  main (void)
       memset (lsp, 'x', sizeof L"3.25");
       free (lsp);
     }
-  if (xsscanf ("4.25[0-9.] x", "%a[0-9.]%8c", &sp, c) != 2)
+  if (xsscanf ("4.25[0-9.] x", "%m[0-9.]%8c", &sp, c) != 2)
     FAIL ();
   else
     {
@@ -113,7 +117,7 @@  main (void)
 	FAIL ();
       if (fseek (fp, 0, SEEK_SET) != 0)
 	FAIL ();
-      if (xfscanf (fp, "%as%2c", &sp, c) != 2)
+      if (xfscanf (fp, "%ms%2c", &sp, c) != 2)
 	FAIL ();
       else
 	{
@@ -127,7 +131,7 @@  main (void)
 	FAIL ();
       else
 	{
-	  if (xscanf ("%as%2c", &sp, c) != 2)
+	  if (xscanf ("%ms%2c", &sp, c) != 2)
 	    FAIL ();
 	  else
 	    {
diff --git a/stdio-common/scanf16a.c b/stdio-common/scanf16a.c
new file mode 100644
index 0000000000..684eeb08f4
--- /dev/null
+++ b/stdio-common/scanf16a.c
@@ -0,0 +1,156 @@ 
+/* This test exercises the deprecated GNU %as, %aS, and %a[...] scanf
+   modifiers, which are not available to programs compiled as C99
+   anymore; therefore, this file is compiled with -std=gnu89 and C99
+   syntax must not be used.  */
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+
+#if !__GLIBC_USE_DEPRECATED_SCANF
+# error "This file should be compiled with deprecated scanf"
+#endif
+
+#define FAIL() \
+  do {							\
+    result = 1;						\
+    printf ("test at line %d failed\n", __LINE__);	\
+  } while (0)
+
+static int __attribute__ ((format (scanf, 2, 3)))
+xsscanf (const char *str, const char *fmt, ...)
+{
+  va_list ap;
+  va_start (ap, fmt);
+  int ret = vsscanf (str, fmt, ap);
+  va_end (ap);
+  return ret;
+}
+
+static int __attribute__ ((format (scanf, 1, 2)))
+xscanf (const char *fmt, ...)
+{
+  va_list ap;
+  va_start (ap, fmt);
+  int ret = vscanf (fmt, ap);
+  va_end (ap);
+  return ret;
+}
+
+static int __attribute__ ((format (scanf, 2, 3)))
+xfscanf (FILE *f, const char *fmt, ...)
+{
+  va_list ap;
+  va_start (ap, fmt);
+  int ret = vfscanf (f, fmt, ap);
+  va_end (ap);
+  return ret;
+}
+
+int
+main (void)
+{
+  wchar_t *lsp;
+  char *sp;
+  float f;
+  double d;
+  char c[8];
+  int result = 0;
+
+  if (xsscanf (" 0.25s x", "%e%3c", &f, c) != 2)
+    FAIL ();
+  else if (f != 0.25 || memcmp (c, "s x", 3) != 0)
+    FAIL ();
+  if (xsscanf (" 1.25s x", "%as%2c", &sp, c) != 2)
+    FAIL ();
+  else
+    {
+      if (strcmp (sp, "1.25s") != 0 || memcmp (c, " x", 2) != 0)
+	FAIL ();
+      memset (sp, 'x', sizeof "1.25s");
+      free (sp);
+    }
+  if (xsscanf (" 2.25s x", "%las%2c", &d, c) != 2)
+    FAIL ();
+  else if (d != 2.25 || memcmp (c, " x", 2) != 0)
+    FAIL ();
+  if (xsscanf (" 3.25S x", "%4aS%3c", &lsp, c) != 2)
+    FAIL ();
+  else
+    {
+      if (wcscmp (lsp, L"3.25") != 0 || memcmp (c, "S x", 3) != 0)
+	FAIL ();
+      memset (lsp, 'x', sizeof L"3.25");
+      free (lsp);
+    }
+  if (xsscanf ("4.25[0-9.] x", "%a[0-9.]%8c", &sp, c) != 2)
+    FAIL ();
+  else
+    {
+      if (strcmp (sp, "4.25") != 0 || memcmp (c, "[0-9.] x", 8) != 0)
+	FAIL ();
+      memset (sp, 'x', sizeof "4.25");
+      free (sp);
+    }
+  if (xsscanf ("5.25[0-9.] x", "%la[0-9.]%2c", &d, c) != 2)
+    FAIL ();
+  else if (d != 5.25 || memcmp (c, " x", 2) != 0)
+    FAIL ();
+
+  const char *tmpdir = getenv ("TMPDIR");
+  if (tmpdir == NULL || tmpdir[0] == '\0')
+    tmpdir = "/tmp";
+
+  char fname[strlen (tmpdir) + sizeof "/tst-scanf16.XXXXXX"];
+  sprintf (fname, "%s/tst-scanf16.XXXXXX", tmpdir);
+  if (fname == NULL)
+    FAIL ();
+
+  /* Create a temporary file.   */
+  int fd = mkstemp (fname);
+  if (fd == -1)
+    FAIL ();
+
+  FILE *fp = fdopen (fd, "w+");
+  if (fp == NULL)
+    FAIL ();
+  else
+    {
+      if (fputs (" 1.25s x", fp) == EOF)
+	FAIL ();
+      if (fseek (fp, 0, SEEK_SET) != 0)
+	FAIL ();
+      if (xfscanf (fp, "%as%2c", &sp, c) != 2)
+	FAIL ();
+      else
+	{
+	  if (strcmp (sp, "1.25s") != 0 || memcmp (c, " x", 2) != 0)
+	    FAIL ();
+	  memset (sp, 'x', sizeof "1.25s");
+	  free (sp);
+	}
+
+      if (freopen (fname, "r", stdin) == NULL)
+	FAIL ();
+      else
+	{
+	  if (xscanf ("%as%2c", &sp, c) != 2)
+	    FAIL ();
+	  else
+	    {
+	      if (strcmp (sp, "1.25s") != 0 || memcmp (c, " x", 2) != 0)
+		FAIL ();
+	      memset (sp, 'x', sizeof "1.25s");
+	      free (sp);
+	    }
+	}
+
+      fclose (fp);
+    }
+
+  remove (fname);
+
+  return result;
+}
diff --git a/stdio-common/scanf17.c b/stdio-common/scanf17.c
index b6c0e63ab0..5a0ae42686 100644
--- a/stdio-common/scanf17.c
+++ b/stdio-common/scanf17.c
@@ -1,19 +1,13 @@ 
-#undef _GNU_SOURCE
-#define _XOPEN_SOURCE 600
-#undef _LIBC
-#undef _IO_MTSAFE_IO
-/* The following macro definitions are a hack.  They word around disabling
-   the GNU extension while still using a few internal headers.  */
-#define u_char unsigned char
-#define u_short unsigned short
-#define u_int unsigned int
-#define u_long unsigned long
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <wchar.h>
 
+#if __GLIBC_USE_DEPRECATED_SCANF
+# error "This file should not be compiled with deprecated scanf"
+#endif
+
 #define FAIL() \
   do {							\
     result = 1;						\
diff --git a/stdio-common/sscanf.c b/stdio-common/sscanf.c
index e25e9c27a5..1c8c58bbe9 100644
--- a/stdio-common/sscanf.c
+++ b/stdio-common/sscanf.c
@@ -15,6 +15,11 @@ 
    License along with the GNU C Library; if not, see
    <http://www.gnu.org/licenses/>.  */
 
+/* This file defines one of the deprecated scanf variants.  */
+#include <features.h>
+#undef __GLIBC_USE_DEPRECATED_SCANF
+#define __GLIBC_USE_DEPRECATED_SCANF 1
+
 #include <stdarg.h>
 #include <libio/strfile.h>
 
@@ -34,6 +39,5 @@  __sscanf (const char *s, const char *format, ...)
 
   return done;
 }
-ldbl_hidden_def (__sscanf, sscanf)
 ldbl_strong_alias (__sscanf, sscanf)
 ldbl_strong_alias (__sscanf, _IO_sscanf)
diff --git a/stdio-common/vfscanf.c b/stdio-common/vfscanf.c
index 5eedca8340..b9df35e73b 100644
--- a/stdio-common/vfscanf.c
+++ b/stdio-common/vfscanf.c
@@ -15,6 +15,11 @@ 
    License along with the GNU C Library; if not, see
    <http://www.gnu.org/licenses/>.  */
 
+/* This file defines one of the deprecated scanf variants.  */
+#include <features.h>
+#undef __GLIBC_USE_DEPRECATED_SCANF
+#define __GLIBC_USE_DEPRECATED_SCANF 1
+
 #include <libioP.h>
 
 int
diff --git a/stdio-common/vfwscanf.c b/stdio-common/vfwscanf.c
index 86980464c2..cbbbd37730 100644
--- a/stdio-common/vfwscanf.c
+++ b/stdio-common/vfwscanf.c
@@ -16,6 +16,11 @@ 
    License along with the GNU C Library; if not, see
    <http://www.gnu.org/licenses/>.  */
 
+/* This file defines one of the deprecated scanf variants.  */
+#include <features.h>
+#undef __GLIBC_USE_DEPRECATED_SCANF
+#define __GLIBC_USE_DEPRECATED_SCANF 1
+
 #include <libioP.h>
 
 int
diff --git a/sysdeps/ieee754/ldbl-opt/nldbl-compat.c b/sysdeps/ieee754/ldbl-opt/nldbl-compat.c
index f6dd81759d..a67a18915f 100644
--- a/sysdeps/ieee754/ldbl-opt/nldbl-compat.c
+++ b/sysdeps/ieee754/ldbl-opt/nldbl-compat.c
@@ -17,6 +17,11 @@ 
    License along with the GNU C Library; if not, see
    <http://www.gnu.org/licenses/>.  */
 
+/* This file may define some of the deprecated scanf variants.  */
+#include <features.h>
+#undef __GLIBC_USE_DEPRECATED_SCANF
+#define __GLIBC_USE_DEPRECATED_SCANF 1
+
 #include <stdarg.h>
 #include <stdio.h>
 #include <libio/strfile.h>
diff --git a/sysdeps/ieee754/ldbl-opt/nldbl-fscanf.c b/sysdeps/ieee754/ldbl-opt/nldbl-fscanf.c
index 1b768e306f..00507187f3 100644
--- a/sysdeps/ieee754/ldbl-opt/nldbl-fscanf.c
+++ b/sysdeps/ieee754/ldbl-opt/nldbl-fscanf.c
@@ -1,3 +1,8 @@ 
+/* This file defines one of the deprecated scanf variants.  */
+#include <features.h>
+#undef __GLIBC_USE_DEPRECATED_SCANF
+#define __GLIBC_USE_DEPRECATED_SCANF 1
+
 #include "nldbl-compat.h"
 
 int
diff --git a/sysdeps/ieee754/ldbl-opt/nldbl-fwscanf.c b/sysdeps/ieee754/ldbl-opt/nldbl-fwscanf.c
index 27fc1a7271..65782894a2 100644
--- a/sysdeps/ieee754/ldbl-opt/nldbl-fwscanf.c
+++ b/sysdeps/ieee754/ldbl-opt/nldbl-fwscanf.c
@@ -1,3 +1,8 @@ 
+/* This file defines one of the deprecated scanf variants.  */
+#include <features.h>
+#undef __GLIBC_USE_DEPRECATED_SCANF
+#define __GLIBC_USE_DEPRECATED_SCANF 1
+
 #include "nldbl-compat.h"
 
 int
diff --git a/sysdeps/ieee754/ldbl-opt/nldbl-iovfscanf.c b/sysdeps/ieee754/ldbl-opt/nldbl-iovfscanf.c
index 442c11c203..6ed9b48624 100644
--- a/sysdeps/ieee754/ldbl-opt/nldbl-iovfscanf.c
+++ b/sysdeps/ieee754/ldbl-opt/nldbl-iovfscanf.c
@@ -1,3 +1,8 @@ 
+/* This file defines one of the deprecated scanf variants.  */
+#include <features.h>
+#undef __GLIBC_USE_DEPRECATED_SCANF
+#define __GLIBC_USE_DEPRECATED_SCANF 1
+
 #include "nldbl-compat.h"
 
 int
diff --git a/sysdeps/ieee754/ldbl-opt/nldbl-scanf.c b/sysdeps/ieee754/ldbl-opt/nldbl-scanf.c
index bbab371cbe..c10da284f2 100644
--- a/sysdeps/ieee754/ldbl-opt/nldbl-scanf.c
+++ b/sysdeps/ieee754/ldbl-opt/nldbl-scanf.c
@@ -1,3 +1,8 @@ 
+/* This file defines one of the deprecated scanf variants.  */
+#include <features.h>
+#undef __GLIBC_USE_DEPRECATED_SCANF
+#define __GLIBC_USE_DEPRECATED_SCANF 1
+
 #include "nldbl-compat.h"
 
 int
diff --git a/sysdeps/ieee754/ldbl-opt/nldbl-sscanf.c b/sysdeps/ieee754/ldbl-opt/nldbl-sscanf.c
index a771d49996..34e18be0a1 100644
--- a/sysdeps/ieee754/ldbl-opt/nldbl-sscanf.c
+++ b/sysdeps/ieee754/ldbl-opt/nldbl-sscanf.c
@@ -1,3 +1,8 @@ 
+/* This file defines one of the deprecated scanf variants.  */
+#include <features.h>
+#undef __GLIBC_USE_DEPRECATED_SCANF
+#define __GLIBC_USE_DEPRECATED_SCANF 1
+
 #include "nldbl-compat.h"
 
 int
diff --git a/sysdeps/ieee754/ldbl-opt/nldbl-swscanf.c b/sysdeps/ieee754/ldbl-opt/nldbl-swscanf.c
index dd058f47ab..4076debc4e 100644
--- a/sysdeps/ieee754/ldbl-opt/nldbl-swscanf.c
+++ b/sysdeps/ieee754/ldbl-opt/nldbl-swscanf.c
@@ -1,3 +1,8 @@ 
+/* This file defines one of the deprecated scanf variants.  */
+#include <features.h>
+#undef __GLIBC_USE_DEPRECATED_SCANF
+#define __GLIBC_USE_DEPRECATED_SCANF 1
+
 #include "nldbl-compat.h"
 
 int
diff --git a/sysdeps/ieee754/ldbl-opt/nldbl-vfscanf.c b/sysdeps/ieee754/ldbl-opt/nldbl-vfscanf.c
index f23465ee95..6f5ebc3045 100644
--- a/sysdeps/ieee754/ldbl-opt/nldbl-vfscanf.c
+++ b/sysdeps/ieee754/ldbl-opt/nldbl-vfscanf.c
@@ -1,3 +1,8 @@ 
+/* This file defines one of the deprecated scanf variants.  */
+#include <features.h>
+#undef __GLIBC_USE_DEPRECATED_SCANF
+#define __GLIBC_USE_DEPRECATED_SCANF 1
+
 #include "nldbl-compat.h"
 
 int
diff --git a/sysdeps/ieee754/ldbl-opt/nldbl-vfwscanf.c b/sysdeps/ieee754/ldbl-opt/nldbl-vfwscanf.c
index be9febc9a0..0bef274726 100644
--- a/sysdeps/ieee754/ldbl-opt/nldbl-vfwscanf.c
+++ b/sysdeps/ieee754/ldbl-opt/nldbl-vfwscanf.c
@@ -1,3 +1,8 @@ 
+/* This file defines one of the deprecated scanf variants.  */
+#include <features.h>
+#undef __GLIBC_USE_DEPRECATED_SCANF
+#define __GLIBC_USE_DEPRECATED_SCANF 1
+
 #include "nldbl-compat.h"
 
 int
diff --git a/sysdeps/ieee754/ldbl-opt/nldbl-vscanf.c b/sysdeps/ieee754/ldbl-opt/nldbl-vscanf.c
index e75907b905..38f654aa73 100644
--- a/sysdeps/ieee754/ldbl-opt/nldbl-vscanf.c
+++ b/sysdeps/ieee754/ldbl-opt/nldbl-vscanf.c
@@ -1,3 +1,8 @@ 
+/* This file defines one of the deprecated scanf variants.  */
+#include <features.h>
+#undef __GLIBC_USE_DEPRECATED_SCANF
+#define __GLIBC_USE_DEPRECATED_SCANF 1
+
 #include "nldbl-compat.h"
 
 int
diff --git a/sysdeps/ieee754/ldbl-opt/nldbl-vsscanf.c b/sysdeps/ieee754/ldbl-opt/nldbl-vsscanf.c
index f5594c122c..33e10e49a9 100644
--- a/sysdeps/ieee754/ldbl-opt/nldbl-vsscanf.c
+++ b/sysdeps/ieee754/ldbl-opt/nldbl-vsscanf.c
@@ -1,3 +1,8 @@ 
+/* This file defines one of the deprecated scanf variants.  */
+#include <features.h>
+#undef __GLIBC_USE_DEPRECATED_SCANF
+#define __GLIBC_USE_DEPRECATED_SCANF 1
+
 #include "nldbl-compat.h"
 
 int
diff --git a/sysdeps/ieee754/ldbl-opt/nldbl-vswscanf.c b/sysdeps/ieee754/ldbl-opt/nldbl-vswscanf.c
index bd4bb5131b..91c0d4cd4f 100644
--- a/sysdeps/ieee754/ldbl-opt/nldbl-vswscanf.c
+++ b/sysdeps/ieee754/ldbl-opt/nldbl-vswscanf.c
@@ -1,3 +1,8 @@ 
+/* This file defines one of the deprecated scanf variants.  */
+#include <features.h>
+#undef __GLIBC_USE_DEPRECATED_SCANF
+#define __GLIBC_USE_DEPRECATED_SCANF 1
+
 #include "nldbl-compat.h"
 
 int
diff --git a/sysdeps/ieee754/ldbl-opt/nldbl-vwscanf.c b/sysdeps/ieee754/ldbl-opt/nldbl-vwscanf.c
index d39578ca4e..b3d0f1be18 100644
--- a/sysdeps/ieee754/ldbl-opt/nldbl-vwscanf.c
+++ b/sysdeps/ieee754/ldbl-opt/nldbl-vwscanf.c
@@ -1,3 +1,8 @@ 
+/* This file defines one of the deprecated scanf variants.  */
+#include <features.h>
+#undef __GLIBC_USE_DEPRECATED_SCANF
+#define __GLIBC_USE_DEPRECATED_SCANF 1
+
 #include "nldbl-compat.h"
 
 int
diff --git a/sysdeps/ieee754/ldbl-opt/nldbl-wscanf.c b/sysdeps/ieee754/ldbl-opt/nldbl-wscanf.c
index 4ee3fdc15f..2689e27f06 100644
--- a/sysdeps/ieee754/ldbl-opt/nldbl-wscanf.c
+++ b/sysdeps/ieee754/ldbl-opt/nldbl-wscanf.c
@@ -1,3 +1,8 @@ 
+/* This file defines one of the deprecated scanf variants.  */
+#include <features.h>
+#undef __GLIBC_USE_DEPRECATED_SCANF
+#define __GLIBC_USE_DEPRECATED_SCANF 1
+
 #include "nldbl-compat.h"
 
 int
diff --git a/wcsmbs/bits/wchar-ldbl.h b/wcsmbs/bits/wchar-ldbl.h
index b91ee9fbf5..ab2601ae5d 100644
--- a/wcsmbs/bits/wchar-ldbl.h
+++ b/wcsmbs/bits/wchar-ldbl.h
@@ -27,9 +27,7 @@  __LDBL_REDIR_DECL (swprintf);
 __LDBL_REDIR_DECL (vfwprintf);
 __LDBL_REDIR_DECL (vwprintf);
 __LDBL_REDIR_DECL (vswprintf);
-# if defined __USE_ISOC99 && !defined __USE_GNU \
-     && !defined __REDIRECT \
-     && (defined __STRICT_ANSI__ || defined __USE_XOPEN2K)
+# if !__GLIBC_USE (DEPRECATED_SCANF)
 __LDBL_REDIR1_DECL (fwscanf, __nldbl___isoc99_fwscanf)
 __LDBL_REDIR1_DECL (wscanf, __nldbl___isoc99_wscanf)
 __LDBL_REDIR1_DECL (swscanf, __nldbl___isoc99_swscanf)
@@ -42,8 +40,7 @@  __LDBL_REDIR_DECL (swscanf);
 
 #ifdef __USE_ISOC99
 __LDBL_REDIR1_DECL (wcstold, wcstod);
-# if !defined __USE_GNU && !defined __REDIRECT \
-     && (defined __STRICT_ANSI__ || defined __USE_XOPEN2K)
+# if !__GLIBC_USE (DEPRECATED_SCANF)
 __LDBL_REDIR1_DECL (vfwscanf, __nldbl___isoc99_vfwscanf)
 __LDBL_REDIR1_DECL (vwscanf, __nldbl___isoc99_vwscanf)
 __LDBL_REDIR1_DECL (vswscanf, __nldbl___isoc99_vswscanf)
diff --git a/wcsmbs/wchar.h b/wcsmbs/wchar.h
index 6c94f3ebc6..24573656bc 100644
--- a/wcsmbs/wchar.h
+++ b/wcsmbs/wchar.h
@@ -632,13 +632,11 @@  extern int swscanf (const wchar_t *__restrict __s,
 		    const wchar_t *__restrict __format, ...)
      __THROW /* __attribute__ ((__format__ (__wscanf__, 2, 3))) */;
 
-# if defined __USE_ISOC99 && !defined __USE_GNU \
-     && (!defined __LDBL_COMPAT || !defined __REDIRECT) \
-     && (defined __STRICT_ANSI__ || defined __USE_XOPEN2K)
+/* For historical reasons, the C99-compliant versions of the scanf
+   functions are at alternative names.  When __LDBL_COMPAT is in
+   effect, this is handled in bits/wchar-ldbl.h.  */
+#if !__GLIBC_USE (DEPRECATED_SCANF) && !defined __LDBL_COMPAT
 #  ifdef __REDIRECT
-/* For strict ISO C99 or POSIX compliance disallow %as, %aS and %a[
-   GNU extension which conflicts with valid %a followed by letter
-   s, S or [.  */
 extern int __REDIRECT (fwscanf, (__FILE *__restrict __stream,
 				 const wchar_t *__restrict __format, ...),
 		       __isoc99_fwscanf)