From patchwork Sat Feb 10 18:17:36 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Zack Weinberg X-Patchwork-Id: 25902 Received: (qmail 96581 invoked by alias); 10 Feb 2018 18:17:43 -0000 Mailing-List: contact libc-alpha-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: libc-alpha-owner@sourceware.org Delivered-To: mailing list libc-alpha@sourceware.org Received: (qmail 96572 invoked by uid 89); 10 Feb 2018 18:17:43 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-25.8 required=5.0 tests=AWL, BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, KAM_SHORT, SPF_PASS, T_RP_MATCHES_RCVD autolearn=ham version=3.3.2 spammy= X-HELO: mailbackend.panix.com From: Zack Weinberg To: libc-alpha@sourceware.org Cc: joseph@codesourcery.com, ricaljasan@pacific.net, schwab@suse.de Subject: [PATCH] Use C99-compliant scanf under _GNU_SOURCE. Date: Sat, 10 Feb 2018 13:17:36 -0500 Message-Id: <20180210181736.11570-1-zackw@panix.com> MIME-Version: 1.0 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-2001 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. Moreover, GCC isn't aware of the subtleties of the above paragraph when issuing -Wformat warnings; if you give -std=gnu99 or above you get dinged for using %a with the archaic-GNU meaning. This change is fiddlier than just changing a couple of #if expressions, for several reasons. The most obvious of these is that we need to be able to 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, 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 a couple of files that use sscanf internally have to bridge the gap with macros. 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 I'm not gonna make that decision unilaterally). Fortunately, elf/check-localplt will catch any new internal uses of *scanf that aren't shimmed. 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! I kinda want to relax the condition for C99-compliant scanf a bit further, from +#if defined __USE_ISOC99 && (defined __USE_XOPEN2K || defined __STRICT_ANSI__) to +#if defined __USE_ISOC99 || defined __USE_XOPEN2K The major problem with that is, there would then be no way to get the noncompliant *v*scanf without overriding __GLIBC_USE_DEPRECATED_SCANF, because vscanf/vfscanf/vsscanf themselves are only available under __USE_ISOC99. Not a problem we have to solve today, though. --- * include/features.h (__GLIBC_USE_DEPRECATED_SCANF): New macro. Don't consider __USE_GNU when deciding whether to use deprecated scanf. * include/stdio.h: Provide libc_hidden_proto for __isoc99_sscanf, not sscanf. * libio/stdio.h, libio/bits/stdio-ldbl.h: Use __GLIBC_USE(DEPRECATED_SCANF) to decide whether or not to redirect scanf, fscanf, sscanf, vscanf, vfscanf, and vsscanf to their __isoc99_ variants. * libio/iovsscanf.c, libio/vscanf.c, stdio-common/fscanf.c * stdio-common/scanf.c, stdio-common/vfscanf.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. * misc/mntent_r.c, time/tzset.c: Redirect sscanf to __isoc99_scanf with a 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 | 15 ++++ include/features.h | 15 ++++ include/stdio.h | 2 +- libio/bits/stdio-ldbl.h | 7 +- libio/iovsscanf.c | 5 ++ libio/stdio.h | 26 +++---- libio/vscanf.c | 5 ++ misc/mntent_r.c | 4 ++ stdio-common/Makefile | 13 ++-- 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 | 31 ++------ stdio-common/scanf14a.c | 133 ++++++++++++++++++++++++++++++++++ stdio-common/scanf15.c | 10 --- stdio-common/scanf16.c | 16 ++--- stdio-common/scanf16a.c | 165 +++++++++++++++++++++++++++++++++++++++++++ stdio-common/scanf17.c | 10 --- stdio-common/sscanf.c | 6 +- stdio-common/vfscanf.c | 5 ++ time/tzset.c | 4 ++ 22 files changed, 400 insertions(+), 94 deletions(-) create mode 100644 stdio-common/scanf14a.c create mode 100644 stdio-common/scanf16a.c diff --git a/NEWS b/NEWS index 60dd2f778d9..45465b785e3 100644 --- a/NEWS +++ b/NEWS @@ -28,6 +28,21 @@ Deprecated and removed features, and other changes affecting compatibility: investigate using (f)getc_unlocked and (f)putc_unlocked, and, if necessary, flockfile and funlockfile. + * The scanf formats '%as', '%aS', and '%a[...]', meaning to read a string + and allocate space for it using malloc, are no longer accepted in code + compiled with _GNU_SOURCE. They were already not accepted in code + compiled in the default mode with modern compilers (e.g. GCC new enough + to default to -std=gnu11, without _GNU_SOURCE). + + Using 'a' as a modifier letter for 's'-type scanf formats is an archaic + GNU extension that conflicts with C99's use of '%a' for floating-point + numbers. Equivalent functionality was standardized in POSIX.1-2001 using + the letter 'm' instead; programs using '%as', '%aS', and '%a[...]' should + change to '%ms', '%mS', and '%m[...]' respectively. GCC's -Wformat + warnings can detect most uses of the extension, as long as all functions + that call vscanf, vfscanf, or vsscanf are annotated with + __attribute__((format(scanf, ...))). + * The macros 'major', 'minor', and 'makedev' are now only available from the header ; not from or various other headers that happen to include . These macros are rarely diff --git a/include/features.h b/include/features.h index 137a90b4055..e6b19c5789c 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,20 @@ # 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-2001 provides the same + functionality using the modifier letter 'm' instead (%ms, %mS, + %m[...]). We now follow C99 and POSIX unless an old, non-strict + conformance level is specifically selected. */ +#if defined __USE_ISOC99 && (defined __USE_XOPEN2K || defined __STRICT_ANSI__) +# define __GLIBC_USE_DEPRECATED_SCANF 0 +#else +# define __GLIBC_USE_DEPRECATED_SCANF 1 +#endif + /* Get definitions of __STDC_* predefined macros, if the compiler has not preincluded this header automatically. */ #include diff --git a/include/stdio.h b/include/stdio.h index 94bc2fdc7ef..d0e9343b2a9 100644 --- a/include/stdio.h +++ b/include/stdio.h @@ -67,6 +67,7 @@ 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) @@ -154,7 +155,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 99d9bcc2334..ab55cbe7fb2 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) && defined __REDIRECT __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) && defined __REDIRECT __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/iovsscanf.c b/libio/iovsscanf.c index b5514fc74ea..e072258ba30 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 +#undef __GLIBC_USE_DEPRECATED_SCANF +#define __GLIBC_USE_DEPRECATED_SCANF 1 + #include "libioP.h" #include "strfile.h" diff --git a/libio/stdio.h b/libio/stdio.h index 731f8e56f4c..a04539639db 100644 --- a/libio/stdio.h +++ b/libio/stdio.h @@ -387,13 +387,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) -# 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 [. */ +/* 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) +# if defined __REDIRECT && !defined __LDBL_COMPAT extern int __REDIRECT (fscanf, (FILE *__restrict __stream, const char *__restrict __format, ...), __isoc99_fscanf) __wur; @@ -402,7 +400,7 @@ extern int __REDIRECT (scanf, (const char *__restrict __format, ...), extern int __REDIRECT_NTH (sscanf, (const char *__restrict __s, const char *__restrict __format, ...), __isoc99_sscanf); -# else +# elif !defined __REDIRECT extern int __isoc99_fscanf (FILE *__restrict __stream, const char *__restrict __format, ...) __wur; extern int __isoc99_scanf (const char *__restrict __format, ...) __wur; @@ -435,13 +433,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), @@ -455,7 +449,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/vscanf.c b/libio/vscanf.c index a74e5418261..4c4eca91cf5 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 +#undef __GLIBC_USE_DEPRECATED_SCANF +#define __GLIBC_USE_DEPRECATED_SCANF 1 + #include "libioP.h" #include "stdio.h" diff --git a/misc/mntent_r.c b/misc/mntent_r.c index 7a826586541..4e1a56620a5 100644 --- a/misc/mntent_r.c +++ b/misc/mntent_r.c @@ -26,6 +26,10 @@ #define flockfile(s) _IO_flockfile (s) #define funlockfile(s) _IO_funlockfile (s) +/* __REDIRECT isn't transitive. */ +#undef sscanf +#define sscanf __isoc99_sscanf + #undef __setmntent #undef __endmntent #undef __getmntent_r diff --git a/stdio-common/Makefile b/stdio-common/Makefile index 9dfc1153132..6b7f0172f08 100644 --- a/stdio-common/Makefile +++ b/stdio-common/Makefile @@ -61,6 +61,7 @@ tests := tstscanf test_rdwr test-popen tstgetln test-fseek \ tst-printf-bz18872 tst-vfprintf-width-prec tst-fmemopen4 \ tst-vfprintf-user-type \ tst-vfprintf-mbs-prec \ + scanf14a scanf16a test-srcs = tst-unbputc tst-printf @@ -136,13 +137,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 # tst-gets.c tests a deprecated function. CFLAGS-tst-gets.c += -Wno-deprecated-declarations diff --git a/stdio-common/bug21.c b/stdio-common/bug21.c index 7a8c6a35423..1f06c0dab4b 100644 --- a/stdio-common/bug21.c +++ b/stdio-common/bug21.c @@ -1,5 +1,4 @@ #include -#include 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 b60a2a3b811..9f2a7cdef9f 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 . */ +/* This file defines one of the deprecated scanf variants. */ +#include +#undef __GLIBC_USE_DEPRECATED_SCANF +#define __GLIBC_USE_DEPRECATED_SCANF 1 + #include #include #include diff --git a/stdio-common/isoc99_sscanf.c b/stdio-common/isoc99_sscanf.c index 56a60a2c05e..d0f826a13e7 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 e61b5f1ad33..5089e29b6bd 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 . */ +/* This file defines one of the deprecated scanf variants. */ +#include +#undef __GLIBC_USE_DEPRECATED_SCANF +#define __GLIBC_USE_DEPRECATED_SCANF 1 + #include #include diff --git a/stdio-common/scanf14.c b/stdio-common/scanf14.c index 2bcd9c9893d..58727d720d6 100644 --- a/stdio-common/scanf14.c +++ b/stdio-common/scanf14.c @@ -2,7 +2,6 @@ #include #include #include -#include #define FAIL() \ do { \ @@ -24,14 +23,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 +32,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 +45,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 +54,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 +82,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 +91,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 +105,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 00000000000..082559371a7 --- /dev/null +++ b/stdio-common/scanf14a.c @@ -0,0 +1,133 @@ +/* This test exercises the deprecated GNU %as, %aS, and %a[...] scanf + modifiers, which are not available anymore under _GNU_SOURCE or any + conformance level including POSIX.1-2001. */ +#undef _GNU_SOURCE +#undef _DEFAULT_SOURCE +#undef _XOPEN_SOURCE +#undef _XOPEN_SOURCE_EXTENDED +#undef _POSIX_C_SOURCE +#undef _POSIX_SOURCE +#undef _ISOC11_SOURCE +#undef _ISOC99_SOURCE +#undef __STRICT_ANSI__ + +#define _POSIX_C_SOURCE 199506L +#define _XOPEN_SOURCE 1 /* for _XOPEN_SOURCE_EXTENDED */ +#define _XOPEN_SOURCE_EXTENDED 1 /* for mkstemp */ + +#include +#include +#include +#include + +#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 a3ab15dea22..0e536ffacd5 100644 --- a/stdio-common/scanf15.c +++ b/stdio-common/scanf15.c @@ -1,13 +1,3 @@ -#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 #include #include diff --git a/stdio-common/scanf16.c b/stdio-common/scanf16.c index 3e3cb417f21..3394cdc288a 100644 --- a/stdio-common/scanf16.c +++ b/stdio-common/scanf16.c @@ -10,7 +10,7 @@ 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 +20,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 +30,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 +54,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 +67,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 +76,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 +113,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 +127,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 00000000000..9b36f127b26 --- /dev/null +++ b/stdio-common/scanf16a.c @@ -0,0 +1,165 @@ +/* This test exercises the deprecated GNU %as, %aS, and %a[...] scanf + modifiers, which are not available anymore under _GNU_SOURCE or any + conformance level including POSIX.1-2001. */ +#undef _GNU_SOURCE +#undef _DEFAULT_SOURCE +#undef _XOPEN_SOURCE +#undef _XOPEN_SOURCE_EXTENDED +#undef _POSIX_C_SOURCE +#undef _POSIX_SOURCE +#undef _ISOC11_SOURCE +#undef _ISOC99_SOURCE +#undef __STRICT_ANSI__ + +#define _POSIX_C_SOURCE 199506L +#define _XOPEN_SOURCE 1 /* for _XOPEN_SOURCE_EXTENDED */ +#define _XOPEN_SOURCE_EXTENDED 1 /* for mkstemp */ +#define _ISOC99_SOURCE 1 /* for v*scanf */ + +#include +#include +#include +#include +#include + +#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 b6c0e63ab02..d5c7983dcb2 100644 --- a/stdio-common/scanf17.c +++ b/stdio-common/scanf17.c @@ -1,13 +1,3 @@ -#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 #include #include diff --git a/stdio-common/sscanf.c b/stdio-common/sscanf.c index 88cd641798b..7d08752d46c 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 . */ +/* This file defines one of the deprecated scanf variants. */ +#include +#undef __GLIBC_USE_DEPRECATED_SCANF +#define __GLIBC_USE_DEPRECATED_SCANF 1 + #include #include #include @@ -34,7 +39,6 @@ __sscanf (const char *s, const char *format, ...) return done; } -ldbl_hidden_def (__sscanf, sscanf) ldbl_strong_alias (__sscanf, sscanf) #undef _IO_sscanf /* This is for libg++. */ diff --git a/stdio-common/vfscanf.c b/stdio-common/vfscanf.c index 099dfeac9e6..4f393e4c332 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 . */ +/* This file defines one of the deprecated scanf variants. */ +#include +#undef __GLIBC_USE_DEPRECATED_SCANF +#define __GLIBC_USE_DEPRECATED_SCANF 1 + #include #include #include diff --git a/time/tzset.c b/time/tzset.c index b51786704ae..80b2e30c207 100644 --- a/time/tzset.c +++ b/time/tzset.c @@ -27,6 +27,10 @@ #include +/* __REDIRECT isn't transitive. */ +#undef sscanf +#define sscanf __isoc99_sscanf + #define SECSPERDAY 86400 char *__tzname[2] = { (char *) "GMT", (char *) "GMT" };