+2000-08-21 Joseph S. Myers <jsm28@cam.ac.uk>
+
+ * c-common.c (init_dollar_format_checking,
+ maybe_read_dollar_number, finish_dollar_format_checking): New
+ functions.
+ (dollar_arguments_used, dollar_arguments_count,
+ dollar_first_arg_num, dollar_max_arg_used, dollar_format_warned):
+ New variables.
+ (check_format_info): Support $ formats for scanf and printf width
+ and precision. Always increment format_chars to advance past the
+ '*' of precision, not just when the format parameters are
+ available to check.
+
2000-08-21 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* mips.c (block_move_loop, expand_block_move,
static void check_format_info PARAMS ((function_format_info *, tree));
+static void init_dollar_format_checking PARAMS ((int, tree));
+static int maybe_read_dollar_number PARAMS ((const char **, int,
+ tree, tree *));
+static void finish_dollar_format_checking PARAMS ((void));
+
/* Initialize the table of functions to perform format checking on.
The ISO C functions are always checked (whether <stdio.h> is
included or not), since it is common to call printf without
}
}
+
+/* Variables used by the checking of $ operand number formats. */
+static char *dollar_arguments_used = NULL;
+static int dollar_arguments_alloc = 0;
+static int dollar_arguments_count;
+static int dollar_first_arg_num;
+static int dollar_max_arg_used;
+static int dollar_format_warned;
+
+/* Initialize the checking for a format string that may contain $
+ parameter number specifications; we will need to keep track of whether
+ each parameter has been used. FIRST_ARG_NUM is the number of the first
+ argument that is a parameter to the format, or 0 for a vprintf-style
+ function; PARAMS is the list of arguments starting at this argument. */
+
+static void
+init_dollar_format_checking (first_arg_num, params)
+ int first_arg_num;
+ tree params;
+{
+ dollar_first_arg_num = first_arg_num;
+ dollar_arguments_count = 0;
+ dollar_max_arg_used = 0;
+ dollar_format_warned = 0;
+ if (first_arg_num > 0)
+ {
+ while (params)
+ {
+ dollar_arguments_count++;
+ params = TREE_CHAIN (params);
+ }
+ }
+ if (dollar_arguments_alloc < dollar_arguments_count)
+ {
+ if (dollar_arguments_used)
+ free (dollar_arguments_used);
+ dollar_arguments_alloc = dollar_arguments_count;
+ dollar_arguments_used = xmalloc (dollar_arguments_alloc);
+ }
+ if (dollar_arguments_alloc)
+ memset (dollar_arguments_used, 0, dollar_arguments_alloc);
+}
+
+
+/* Look for a decimal number followed by a $ in *FORMAT. If DOLLAR_NEEDED
+ is set, it is an error if one is not found; otherwise, it is OK. If
+ such a number is found, check whether it is within range and mark that
+ numbered operand as being used for later checking. Returns the operand
+ number if found and within range, zero if no such number was found and
+ this is OK, or -1 on error. PARAMS points to the first operand of the
+ format; PARAM_PTR is made to point to the parameter referred to. If
+ a $ format is found, *FORMAT is updated to point just after it. */
+
+static int
+maybe_read_dollar_number (format, dollar_needed, params, param_ptr)
+ const char **format;
+ int dollar_needed;
+ tree params;
+ tree *param_ptr;
+{
+ int argnum;
+ int overflow_flag;
+ const char *fcp = *format;
+ if (*fcp < '0' || *fcp > '9')
+ {
+ if (dollar_needed)
+ {
+ warning ("missing $ operand number in format");
+ return -1;
+ }
+ else
+ return 0;
+ }
+ argnum = 0;
+ overflow_flag = 0;
+ while (*fcp >= '0' && *fcp <= '9')
+ {
+ int nargnum;
+ nargnum = 10 * argnum + (*fcp - '0');
+ if (nargnum < 0 || nargnum / 10 != argnum)
+ overflow_flag = 1;
+ argnum = nargnum;
+ fcp++;
+ }
+ if (*fcp != '$')
+ {
+ if (dollar_needed)
+ {
+ warning ("missing $ operand number in format");
+ return -1;
+ }
+ else
+ return 0;
+ }
+ *format = fcp + 1;
+ if (pedantic && !dollar_format_warned)
+ {
+ warning ("ISO C does not support %%n$ operand number formats");
+ dollar_format_warned = 1;
+ }
+ if (overflow_flag || argnum == 0
+ || (dollar_first_arg_num && argnum > dollar_arguments_count))
+ {
+ warning ("operand number out of range in format");
+ return -1;
+ }
+ if (argnum > dollar_max_arg_used)
+ dollar_max_arg_used = argnum;
+ /* For vprintf-style functions we may need to allocate more memory to
+ track which arguments are used. */
+ while (dollar_arguments_alloc < dollar_max_arg_used)
+ {
+ int nalloc;
+ nalloc = 2 * dollar_arguments_alloc + 16;
+ dollar_arguments_used = xrealloc (dollar_arguments_used, nalloc);
+ memset (dollar_arguments_used + dollar_arguments_alloc, 0,
+ nalloc - dollar_arguments_alloc);
+ dollar_arguments_alloc = nalloc;
+ }
+ dollar_arguments_used[argnum - 1] = 1;
+ if (dollar_first_arg_num)
+ {
+ int i;
+ *param_ptr = params;
+ for (i = 1; i < argnum && *param_ptr != 0; i++)
+ *param_ptr = TREE_CHAIN (*param_ptr);
+
+ if (*param_ptr == 0)
+ {
+ /* This case shouldn't be caught here. */
+ abort ();
+ }
+ }
+ else
+ *param_ptr = 0;
+ return argnum;
+}
+
+
+/* Finish the checking for a format string that used $ operand number formats
+ instead of non-$ formats. We check for unused operands before used ones
+ (a serious error, since the implementation of the format function
+ can't know what types to pass to va_arg to find the later arguments).
+ and for unused operands at the end of the format (if we know how many
+ arguments the format had, so not for vprintf). If there were operand
+ numbers out of range on a non-vprintf-style format, we won't have reached
+ here. */
+
+static void
+finish_dollar_format_checking ()
+{
+ int i;
+ for (i = 0; i < dollar_max_arg_used; i++)
+ {
+ if (!dollar_arguments_used[i])
+ warning ("format argument %d unused before used argument %d in $-style format",
+ i + 1, dollar_max_arg_used);
+ }
+ if (dollar_first_arg_num && dollar_max_arg_used < dollar_arguments_count)
+ warning ("unused arguments in $-style format");
+}
+
+
/* Check the argument list of a call to printf, scanf, etc.
INFO points to the function_format_info structure.
PARAMS is the list of argument values. */
const char *format_chars;
format_char_info *fci = NULL;
char flag_chars[8];
- int has_operand_number = 0;
+ /* -1 if no conversions taking an operand have been found; 0 if one has
+ and it didn't use $; 1 if $ formats are in use. */
+ int has_operand_number = -1;
/* Skip to format argument. If the argument isn't available, there's
no work for us to do; prototype checking will catch the problem. */
}
first_fillin_param = params;
+ init_dollar_format_checking (info->first_arg_num, first_fillin_param);
while (1)
{
int aflag;
{
if (format_chars - TREE_STRING_POINTER (format_tree) != format_length)
warning ("embedded `\\0' in format");
- if (info->first_arg_num != 0 && params != 0 && ! has_operand_number)
+ if (info->first_arg_num != 0 && params != 0
+ && has_operand_number <= 0)
warning ("too many arguments for format");
+ if (has_operand_number > 0)
+ finish_dollar_format_checking ();
return;
}
if (*format_chars++ != '%')
suppressed = *format_chars == '*';
if (suppressed)
++format_chars;
+ else if (has_operand_number != 0)
+ {
+ int opnum;
+ opnum = maybe_read_dollar_number (&format_chars,
+ has_operand_number == 1,
+ first_fillin_param, ¶ms);
+ if (opnum == -1)
+ return;
+ else if (opnum > 0)
+ {
+ has_operand_number = 1;
+ arg_num = opnum + info->first_arg_num - 1;
+ }
+ else
+ has_operand_number = 0;
+ }
while (ISDIGIT (*format_chars))
{
wide = TRUE;
}
else if (info->format_type == printf_format_type)
{
- /* See if we have a number followed by a dollar sign. If we do,
- it is an operand number, so set PARAMS to that operand. */
- if (*format_chars >= '0' && *format_chars <= '9')
+ if (has_operand_number != 0)
{
- const char *p = format_chars;
-
- while (*p >= '0' && *p++ <= '9')
- ;
-
- if (*p == '$')
+ int opnum;
+ opnum = maybe_read_dollar_number (&format_chars,
+ has_operand_number == 1,
+ first_fillin_param, ¶ms);
+ if (opnum == -1)
+ return;
+ else if (opnum > 0)
{
- int opnum = atoi (format_chars);
-
- if (pedantic)
- warning ("ISO C does not support printf %%n$");
-
- params = first_fillin_param;
- format_chars = p + 1;
has_operand_number = 1;
-
- for (i = 1; i < opnum && params != 0; i++)
- params = TREE_CHAIN (params);
-
- if (opnum == 0 || params == 0)
- {
- warning ("operand number out of range in format");
- return;
- }
+ arg_num = opnum + info->first_arg_num - 1;
}
+ else
+ has_operand_number = 0;
}
while (*format_chars != 0 && index (" +#0-'", *format_chars) != 0)
tfaff ();
return;
}
+ if (has_operand_number > 0)
+ {
+ int opnum;
+ opnum = maybe_read_dollar_number (&format_chars, 1,
+ first_fillin_param,
+ ¶ms);
+ if (opnum <= 0)
+ return;
+ else
+ arg_num = opnum + info->first_arg_num - 1;
+ }
if (info->first_arg_num != 0)
{
cur_param = TREE_VALUE (params);
- params = TREE_CHAIN (params);
- ++arg_num;
+ if (has_operand_number <= 0)
+ {
+ params = TREE_CHAIN (params);
+ ++arg_num;
+ }
/* size_t is generally not valid here.
It will work on most machines, because size_t and int
have the same mode. But might as well warn anyway,
In this case, an int argument supplies the...precision." */
if (*format_chars == '*')
{
+ ++format_chars;
+ if (has_operand_number > 0)
+ {
+ int opnum;
+ opnum = maybe_read_dollar_number (&format_chars, 1,
+ first_fillin_param,
+ ¶ms);
+ if (opnum <= 0)
+ return;
+ else
+ arg_num = opnum + info->first_arg_num - 1;
+ }
if (info->first_arg_num != 0)
{
- ++format_chars;
if (params == 0)
{
tfaff ();
return;
}
cur_param = TREE_VALUE (params);
- params = TREE_CHAIN (params);
- ++arg_num;
+ if (has_operand_number <= 0)
+ {
+ params = TREE_CHAIN (params);
+ ++arg_num;
+ }
if ((TYPE_MAIN_VARIANT (TREE_TYPE (cur_param))
!= integer_type_node)
&&
2000-08-21 Joseph S. Myers <jsm28@cam.ac.uk>
+ * gcc.dg/c90-printf-3.c, gcc.dg/c90-scanf-2.c,
+ gcc.dg/c90-scanf-3.c, gcc.dg/c90-scanf-4.c,
+ gcc.dg/c90-strftime-1.c, gcc.dg/c99-printf-3.c,
+ gcc.dg/c99-scanf-1.c, gcc.dg/c99-scanf-2.c, gcc.dg/c99-scanf-3.c,
+ gcc.dg/format-xopen-1.c: New tests.
+
+2000-08-21 Joseph S. Myers <jsm28@cam.ac.uk>
+
* gcc.dg/c90-scanf-1.c, gcc.dg/c94-scanf-1.c: New tests.
2000-08-21 Jakub Jelinek <jakub@redhat.com>
--- /dev/null
+/* Test for printf formats. Test that the C90 functions get their default
+ attributes in strict C90 mode, but the C99 and gettext functions
+ do not.
+*/
+/* Origin: Joseph Myers <jsm28@cam.ac.uk> */
+/* { dg-do compile } */
+/* { dg-options "-std=iso9899:1990 -pedantic -Wformat" } */
+
+/* This may not be correct in the particular case, but allows the
+ prototypes to be declared, and we don't try to link.
+*/
+typedef struct _FILE FILE;
+extern FILE *stdout;
+
+typedef __SIZE_TYPE__ size_t;
+typedef __builtin_va_list va_list;
+
+extern int fprintf (FILE *, const char *, ...);
+extern int printf (const char *, ...);
+extern int sprintf (char *, const char *, ...);
+extern int vfprintf (FILE *, const char *, va_list);
+extern int vprintf (const char *, va_list);
+extern int vsprintf (char *, const char *, va_list);
+
+extern int snprintf (char *, size_t, const char *, ...);
+extern int vsnprintf (char *, size_t, const char *, va_list);
+
+extern char *gettext (const char *);
+extern char *dgettext (const char *, const char *);
+extern char *dcgettext (const char *, const char *, int);
+
+void
+foo (int i, char *s, size_t n, va_list v0, va_list v1, va_list v2, va_list v3,
+ va_list v4, va_list v5, va_list v6, va_list v7, va_list v8)
+{
+ fprintf (stdout, "%d", i);
+ fprintf (stdout, "%ld", i); /* { dg-warning "format" "fprintf" } */
+ printf ("%d", i);
+ printf ("%ld", i); /* { dg-warning "format" "printf" } */
+ sprintf (s, "%d", i);
+ sprintf (s, "%ld", i); /* { dg-warning "format" "sprintf" } */
+ vfprintf (stdout, "%d", v0);
+ vfprintf (stdout, "%Y", v1); /* { dg-warning "format" "vfprintf" } */
+ vprintf ("%d", v2);
+ vprintf ("%Y", v3); /* { dg-warning "format" "vprintf" } */
+ /* The following used to give a bogus warning. */
+ vprintf ("%*.*d", v8);
+ vsprintf (s, "%d", v4);
+ vsprintf (s, "%Y", v5); /* { dg-warning "format" "vsprintf" } */
+ snprintf (s, n, "%d", i);
+ snprintf (s, n, "%ld", i);
+ vsnprintf (s, n, "%d", v6);
+ vsnprintf (s, n, "%Y", v7);
+ printf (gettext ("%d"), i);
+ printf (gettext ("%ld"), i);
+ printf (dgettext ("", "%d"), i);
+ printf (dgettext ("", "%ld"), i);
+ printf (dcgettext ("", "%d", 0), i);
+ printf (dcgettext ("", "%ld", 0), i);
+}
--- /dev/null
+/* Test for scanf formats. Formats using C99 features should be rejected
+ outside of C99 mode.
+*/
+/* Origin: Joseph Myers <jsm28@cam.ac.uk> */
+/* { dg-do compile } */
+/* { dg-options "-std=iso9899:1990 -pedantic -Wformat" } */
+
+typedef __SIZE_TYPE__ size_t;
+typedef __PTRDIFF_TYPE__ ptrdiff_t;
+
+__extension__ typedef long long int llong;
+
+/* This next definition is broken. When GCC has a <stdint.h> and
+ an internal understanding of intmax_t, it should be
+ replaced by an include of <stdint.h> or by a definition for internal
+ macros or typedefs.
+*/
+__extension__ typedef long long int intmax_t;
+
+extern int scanf (const char *, ...);
+
+void
+foo (signed char *hhp, float *fp, llong *llp, intmax_t *jp,
+ size_t *zp, ptrdiff_t *tp)
+{
+ /* Some tests already in c90-scanf-1.c. */
+ /* The widths hh, ll, j, z, t are new. */
+ scanf ("%hhd", hhp); /* { dg-warning "length character|C" "%hh in C90" } */
+ scanf ("%lld", llp); /* { dg-warning "length character|C" "%ll in C90" } */
+ scanf ("%jd", jp); /* { dg-warning "length character|C" "%j in C90" } */
+ scanf ("%zu", zp); /* { dg-warning "length character|C" "%z in C90" } */
+ scanf ("%td", tp); /* { dg-warning "length character|C" "%t in C90" } */
+ /* The formats F, a, A are new. */
+ scanf ("%F", fp); /* { dg-warning "C" "%F in C90" } */
+ scanf ("%a", fp); /* { dg-warning "C" "%a in C90" } */
+ scanf ("%A", fp); /* { dg-warning "C" "%A in C90" } */
+}
--- /dev/null
+/* Test for scanf formats. Formats using extensions to the standard
+ should be rejected in strict pedantic mode.
+*/
+/* Origin: Joseph Myers <jsm28@cam.ac.uk> */
+/* { dg-do compile } */
+/* { dg-options "-std=iso9899:1990 -pedantic -Wformat" } */
+
+typedef __WCHAR_TYPE__ wchar_t;
+
+extern int scanf (const char *, ...);
+
+void
+foo (char **sp, wchar_t **lsp)
+{
+ /* %a formats for allocation, only recognised in C90 mode, are a
+ GNU extension.
+ */
+ scanf ("%as", sp); /* { dg-warning "C" "%as" } */
+ scanf ("%aS", lsp); /* { dg-warning "C" "%aS" } */
+ scanf ("%a[bcd]", sp); /* { dg-warning "C" "%a[]" } */
+}
--- /dev/null
+/* Test for scanf formats. Test that the C90 functions get their default
+ attributes in strict C90 mode, but the C99 and gettext functions
+ do not.
+*/
+/* Origin: Joseph Myers <jsm28@cam.ac.uk> */
+/* { dg-do compile } */
+/* { dg-options "-std=iso9899:1990 -pedantic -Wformat" } */
+
+/* This may not be correct in the particular case, but allows the
+ prototypes to be declared, and we don't try to link.
+*/
+typedef struct _FILE FILE;
+extern FILE *stdin;
+
+typedef __builtin_va_list va_list;
+
+extern int fscanf (FILE *, const char *, ...);
+extern int scanf (const char *, ...);
+extern int sscanf (const char *, const char *, ...);
+
+extern int vfscanf (FILE *, const char *, va_list);
+extern int vscanf (const char *, va_list);
+extern int vsscanf (const char *, const char *, va_list);
+
+extern char *gettext (const char *);
+extern char *dgettext (const char *, const char *);
+extern char *dcgettext (const char *, const char *, int);
+
+void
+foo (int *ip, char *s, va_list v0, va_list v1, va_list v2, va_list v3,
+ va_list v4, va_list v5)
+{
+ fscanf (stdin, "%d", ip);
+ fscanf (stdin, "%ld", ip); /* { dg-warning "format" "fscanf" } */
+ scanf ("%d", ip);
+ scanf ("%ld", ip); /* { dg-warning "format" "scanf" } */
+ sscanf (s, "%d", ip);
+ sscanf (s, "%ld", ip); /* { dg-warning "format" "sscanf" } */
+ vfscanf (stdin, "%d", v0);
+ vfscanf (stdin, "%Y", v1);
+ vscanf ("%d", v2);
+ vscanf ("%Y", v3);
+ vsscanf (s, "%d", v4);
+ vsscanf (s, "%Y", v5);
+ scanf (gettext ("%d"), ip);
+ scanf (gettext ("%ld"), ip);
+ scanf (dgettext ("", "%d"), ip);
+ scanf (dgettext ("", "%ld"), ip);
+ scanf (dcgettext ("", "%d", 0), ip);
+ scanf (dcgettext ("", "%ld", 0), ip);
+}
--- /dev/null
+/* Test for strftime formats. Formats using C90 features. */
+/* Origin: Joseph Myers <jsm28@cam.ac.uk> */
+/* { dg-do compile } */
+/* { dg-options "-std=iso9899:1990 -pedantic -Wformat" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+struct tm;
+
+extern size_t strftime (char *, size_t, const char *, const struct tm *);
+
+void
+foo (char *s, size_t m, const struct tm *tp)
+{
+ /* See ISO/IEC 9899:1990 (E) subclause 7.12.3.5 (pages 174-175). */
+ /* Formats which are Y2K-compliant (no 2-digit years). */
+ strftime (s, m, "%a%A%b%B%d%H%I%j%m%M%p%S%U%w%W%X%Y%Z%%", tp);
+ /* Formats with 2-digit years. */
+ strftime (s, m, "%y", tp); /* { dg-warning "only last 2" "2-digit year" } */
+ /* Formats with 2-digit years in some locales. */
+ strftime (s, m, "%c", tp); /* { dg-warning "some locales" "2-digit year" } */
+ strftime (s, m, "%x", tp); /* { dg-warning "some locales" "2-digit year" } */
+}
--- /dev/null
+/* Test for printf formats. Test that the C99 functions get their default
+ attributes in strict C99 mode, but the gettext functions do not.
+*/
+/* Origin: Joseph Myers <jsm28@cam.ac.uk> */
+/* { dg-do compile } */
+/* { dg-options "-std=iso9899:1999 -pedantic -Wformat" } */
+
+/* This may not be correct in the particular case, but allows the
+ prototypes to be declared, and we don't try to link.
+*/
+typedef struct _FILE FILE;
+extern FILE *stdout;
+
+typedef __SIZE_TYPE__ size_t;
+typedef __builtin_va_list va_list;
+
+extern int fprintf (FILE *restrict, const char *restrict, ...);
+extern int printf (const char *restrict, ...);
+extern int sprintf (char *restrict, const char *restrict, ...);
+extern int vfprintf (FILE *restrict, const char *restrict, va_list);
+extern int vprintf (const char *restrict, va_list);
+extern int vsprintf (char *restrict, const char *restrict, va_list);
+extern int snprintf (char *restrict, size_t, const char *restrict, ...);
+extern int vsnprintf (char *restrict, size_t, const char *restrict, va_list);
+
+extern char *gettext (const char *);
+extern char *dgettext (const char *, const char *);
+extern char *dcgettext (const char *, const char *, int);
+
+void
+foo (int i, char *s, size_t n, va_list v0, va_list v1, va_list v2, va_list v3,
+ va_list v4, va_list v5, va_list v6, va_list v7)
+{
+ fprintf (stdout, "%d", i);
+ fprintf (stdout, "%ld", i); /* { dg-warning "format" "fprintf" } */
+ printf ("%d", i);
+ printf ("%ld", i); /* { dg-warning "format" "printf" } */
+ sprintf (s, "%d", i);
+ sprintf (s, "%ld", i); /* { dg-warning "format" "sprintf" } */
+ snprintf (s, n, "%d", i);
+ snprintf (s, n, "%ld", i); /* { dg-warning "format" "snprintf" } */
+ vfprintf (stdout, "%d", v0);
+ vfprintf (stdout, "%Y", v1); /* { dg-warning "format" "vfprintf" } */
+ vprintf ("%d", v0);
+ vprintf ("%Y", v1); /* { dg-warning "format" "vprintf" } */
+ vsprintf (s, "%d", v0);
+ vsprintf (s, "%Y", v1); /* { dg-warning "format" "vsprintf" } */
+ vsnprintf (s, n, "%d", v0);
+ vsnprintf (s, n, "%Y", v1); /* { dg-warning "format" "vsnprintf" } */
+ printf (gettext ("%d"), i);
+ printf (gettext ("%ld"), i);
+ printf (dgettext ("", "%d"), i);
+ printf (dgettext ("", "%ld"), i);
+ printf (dcgettext ("", "%d", 0), i);
+ printf (dcgettext ("", "%ld", 0), i);
+}
--- /dev/null
+/* Test for scanf formats. Formats using C99 features, including cases
+ where C99 specifies some aspect of the format to be ignored or where
+ the behaviour is undefined.
+*/
+/* Origin: Joseph Myers <jsm28@cam.ac.uk> */
+/* { dg-do compile } */
+/* { dg-options "-std=iso9899:1999 -pedantic -Wformat" } */
+
+typedef __WCHAR_TYPE__ wchar_t;
+typedef __SIZE_TYPE__ size_t;
+typedef __PTRDIFF_TYPE__ ptrdiff_t;
+
+/* Kludges to get types corresponding to size_t and ptrdiff_t. */
+#define unsigned signed
+typedef __SIZE_TYPE__ signed_size_t;
+#undef unsigned
+#define signed /* Type might or might not have explicit 'signed'. */
+typedef unsigned __PTRDIFF_TYPE__ unsigned_ptrdiff_t;
+#undef signed
+
+/* These next definitions are broken. When GCC has a <stdint.h> and
+ an internal understanding of intmax_t and uintmax_t, they should be
+ replaced by an include of <stdint.h> or by definitions for internal
+ macros or typedefs, and the corresponding xfails removed.
+*/
+typedef long long int intmax_t;
+typedef unsigned long long int uintmax_t;
+
+extern int scanf (const char *, ...);
+
+void
+foo (int *ip, unsigned int *uip, short int *hp, unsigned short int *uhp,
+ signed char *hhp, unsigned char *uhhp, long int *lp,
+ unsigned long int *ulp, float *fp, double *dp, long double *ldp, char *s,
+ void **pp, int *n, long long *llp, unsigned long long *ullp, wchar_t *ls,
+ short int *hn, signed char *hhn, long int *ln, long long int *lln,
+ intmax_t *jp, uintmax_t *ujp, intmax_t *jn, size_t *zp,
+ signed_size_t *szp, signed_size_t *zn, ptrdiff_t *tp,
+ unsigned_ptrdiff_t *utp, ptrdiff_t *tn)
+{
+ /* See ISO/IEC 9899:1999 (E) subclause 7.19.6.2 (pages 281-288).
+ We do not repeat here most of the checks for correct C90 formats
+ or completely broken formats.
+ */
+ /* Valid, invalid and silly assignment-suppression
+ and width constructions.
+ */
+ scanf ("%*d%*i%*o%*u%*x%*X%*a%*A%*e%*E%*f%*F%*g%*G%*s%*[abc]%*c%*p");
+ scanf ("%*2d%*8s%*3c");
+ scanf ("%*n"); /* { dg-warning "suppress" "suppression of %n" } */
+ scanf ("%*hd"); /* { dg-warning "together" "suppression with length" } */
+ scanf ("%2d%3i%4o%5u%6x%7X%8a%9A%10e%11E%12f%13F%14g%15G%16s%3[abc]%4c%5p",
+ ip, ip, uip, uip, uip, uip, fp, fp, fp, fp, fp, fp, fp, fp,
+ s, s, s, pp);
+ scanf ("%0d", ip); /* { dg-warning "width" "warning for zero width" } */
+ scanf ("%3n", n); /* { dg-warning "width" "width with %n" } */
+ /* Valid and invalid %h, %hh, %l, %ll, %j, %z, %t, %L constructions. */
+ scanf ("%hd%hi%ho%hu%hx%hX%hn", hp, hp, uhp, uhp, uhp, uhp, hn);
+ scanf ("%ha", fp); /* { dg-warning "length character" "bad use of %h" } */
+ scanf ("%hA", fp); /* { dg-warning "length character" "bad use of %h" } */
+ scanf ("%he", fp); /* { dg-warning "length character" "bad use of %h" } */
+ scanf ("%hE", fp); /* { dg-warning "length character" "bad use of %h" } */
+ scanf ("%hf", fp); /* { dg-warning "length character" "bad use of %h" } */
+ scanf ("%hF", fp); /* { dg-warning "length character" "bad use of %h" } */
+ scanf ("%hg", fp); /* { dg-warning "length character" "bad use of %h" } */
+ scanf ("%hG", fp); /* { dg-warning "length character" "bad use of %h" } */
+ scanf ("%hs", s); /* { dg-warning "length character" "bad use of %h" } */
+ scanf ("%h[ac]", s); /* { dg-warning "length character" "bad use of %h" } */
+ scanf ("%hc", s); /* { dg-warning "length character" "bad use of %h" } */
+ scanf ("%hp", pp); /* { dg-warning "length character" "bad use of %h" } */
+ scanf ("%hhd%hhi%hho%hhu%hhx%hhX%hhn", hhp, hhp, uhhp, uhhp, uhhp, uhhp,
+ hhn);
+ scanf ("%hha", fp); /* { dg-warning "length character" "bad use of %hh" } */
+ scanf ("%hhA", fp); /* { dg-warning "length character" "bad use of %hh" } */
+ scanf ("%hhe", fp); /* { dg-warning "length character" "bad use of %hh" } */
+ scanf ("%hhE", fp); /* { dg-warning "length character" "bad use of %hh" } */
+ scanf ("%hhf", fp); /* { dg-warning "length character" "bad use of %hh" } */
+ scanf ("%hhF", fp); /* { dg-warning "length character" "bad use of %hh" } */
+ scanf ("%hhg", fp); /* { dg-warning "length character" "bad use of %hh" } */
+ scanf ("%hhG", fp); /* { dg-warning "length character" "bad use of %hh" } */
+ scanf ("%hhs", s); /* { dg-warning "length character" "bad use of %hh" } */
+ scanf ("%hh[ac]", s); /* { dg-warning "length character" "bad use of %hh" } */
+ scanf ("%hhc", s); /* { dg-warning "length character" "bad use of %hh" } */
+ scanf ("%hhp", pp); /* { dg-warning "length character" "bad use of %hh" } */
+ scanf ("%ld%li%lo%lu%lx%lX%ln", lp, lp, ulp, ulp, ulp, ulp, ln);
+ scanf ("%la%lA%le%lE%lf%lF%lg%lG", dp, dp, dp, dp, dp, dp, dp, dp);
+ scanf ("%lp", pp); /* { dg-warning "length character" "bad use of %l" } */
+ scanf ("%ls", ls);
+ scanf ("%l[ac]", ls);
+ scanf ("%lc", ls);
+ scanf ("%lld%lli%llo%llu%llx%llX%lln", llp, llp, ullp, ullp, ullp, ullp,
+ lln);
+ scanf ("%lla", fp); /* { dg-warning "length character" "bad use of %ll" } */
+ scanf ("%llA", fp); /* { dg-warning "length character" "bad use of %ll" } */
+ scanf ("%lle", fp); /* { dg-warning "length character" "bad use of %ll" } */
+ scanf ("%llE", fp); /* { dg-warning "length character" "bad use of %ll" } */
+ scanf ("%llf", fp); /* { dg-warning "length character" "bad use of %ll" } */
+ scanf ("%llF", fp); /* { dg-warning "length character" "bad use of %ll" } */
+ scanf ("%llg", fp); /* { dg-warning "length character" "bad use of %ll" } */
+ scanf ("%llG", fp); /* { dg-warning "length character" "bad use of %ll" } */
+ scanf ("%lls", s); /* { dg-warning "length character" "bad use of %ll" } */
+ scanf ("%ll[ac]", s); /* { dg-warning "length character" "bad use of %ll" } */
+ scanf ("%llc", s); /* { dg-warning "length character" "bad use of %ll" } */
+ scanf ("%llp", pp); /* { dg-warning "length character" "bad use of %ll" } */
+ scanf ("%jd%ji%jo%ju%jx%jX%jn", jp, jp, ujp, ujp, ujp, ujp, jn); /* { dg-bogus "length character" "bogus %j warning" { xfail *-*-* } } */
+ scanf ("%ja", fp); /* { dg-warning "length character" "bad use of %j" } */
+ scanf ("%jA", fp); /* { dg-warning "length character" "bad use of %j" } */
+ scanf ("%je", fp); /* { dg-warning "length character" "bad use of %j" } */
+ scanf ("%jE", fp); /* { dg-warning "length character" "bad use of %j" } */
+ scanf ("%jf", fp); /* { dg-warning "length character" "bad use of %j" } */
+ scanf ("%jF", fp); /* { dg-warning "length character" "bad use of %j" } */
+ scanf ("%jg", fp); /* { dg-warning "length character" "bad use of %j" } */
+ scanf ("%jG", fp); /* { dg-warning "length character" "bad use of %j" } */
+ scanf ("%js", s); /* { dg-warning "length character" "bad use of %j" } */
+ scanf ("%j[ac]", s); /* { dg-warning "length character" "bad use of %j" } */
+ scanf ("%jc", s); /* { dg-warning "length character" "bad use of %j" } */
+ scanf ("%jp", pp); /* { dg-warning "length character" "bad use of %j" } */
+ scanf ("%zd%zi%zo%zu%zx%zX%zn", szp, szp, zp, zp, zp, zp, zn);
+ scanf ("%za", fp); /* { dg-warning "length character" "bad use of %z" } */
+ scanf ("%zA", fp); /* { dg-warning "length character" "bad use of %z" } */
+ scanf ("%ze", fp); /* { dg-warning "length character" "bad use of %z" } */
+ scanf ("%zE", fp); /* { dg-warning "length character" "bad use of %z" } */
+ scanf ("%zf", fp); /* { dg-warning "length character" "bad use of %z" } */
+ scanf ("%zF", fp); /* { dg-warning "length character" "bad use of %z" } */
+ scanf ("%zg", fp); /* { dg-warning "length character" "bad use of %z" } */
+ scanf ("%zG", fp); /* { dg-warning "length character" "bad use of %z" } */
+ scanf ("%zs", s); /* { dg-warning "length character" "bad use of %z" } */
+ scanf ("%z[ac]", s); /* { dg-warning "length character" "bad use of %z" } */
+ scanf ("%zc", s); /* { dg-warning "length character" "bad use of %z" } */
+ scanf ("%zp", pp); /* { dg-warning "length character" "bad use of %z" } */
+ scanf ("%td%ti%to%tu%tx%tX%tn", tp, tp, utp, utp, utp, utp, tn);
+ scanf ("%ta", fp); /* { dg-warning "length character" "bad use of %t" } */
+ scanf ("%tA", fp); /* { dg-warning "length character" "bad use of %t" } */
+ scanf ("%te", fp); /* { dg-warning "length character" "bad use of %t" } */
+ scanf ("%tE", fp); /* { dg-warning "length character" "bad use of %t" } */
+ scanf ("%tf", fp); /* { dg-warning "length character" "bad use of %t" } */
+ scanf ("%tF", fp); /* { dg-warning "length character" "bad use of %t" } */
+ scanf ("%tg", fp); /* { dg-warning "length character" "bad use of %t" } */
+ scanf ("%tG", fp); /* { dg-warning "length character" "bad use of %t" } */
+ scanf ("%ts", s); /* { dg-warning "length character" "bad use of %t" } */
+ scanf ("%t[ac]", s); /* { dg-warning "length character" "bad use of %t" } */
+ scanf ("%tc", s); /* { dg-warning "length character" "bad use of %t" } */
+ scanf ("%tp", pp); /* { dg-warning "length character" "bad use of %t" } */
+ scanf ("%La%LA%Le%LE%Lf%LF%Lg%LG", ldp, ldp, ldp, ldp, ldp, ldp, ldp, ldp);
+ scanf ("%Ld", llp); /* { dg-warning "does not support" "bad use of %L" } */
+ scanf ("%Li", llp); /* { dg-warning "does not support" "bad use of %L" } */
+ scanf ("%Lo", ullp); /* { dg-warning "does not support" "bad use of %L" } */
+ scanf ("%Lu", ullp); /* { dg-warning "does not support" "bad use of %L" } */
+ scanf ("%Lx", ullp); /* { dg-warning "does not support" "bad use of %L" } */
+ scanf ("%LX", ullp); /* { dg-warning "does not support" "bad use of %L" } */
+ scanf ("%Ls", s); /* { dg-warning "length character" "bad use of %L" } */
+ scanf ("%L[ac]", s); /* { dg-warning "length character" "bad use of %L" } */
+ scanf ("%Lc", s); /* { dg-warning "length character" "bad use of %L" } */
+ scanf ("%Lp", pp); /* { dg-warning "length character" "bad use of %L" } */
+ scanf ("%Ln", n); /* { dg-warning "length character" "bad use of %L" } */
+ /* Valid uses of each bare conversion. */
+ scanf ("%d%i%o%u%x%X%a%A%e%E%f%F%g%G%s%[abc]%c%p%n%%", ip, ip, uip, uip, uip,
+ uip, fp, fp, fp, fp, fp, fp, fp, fp, s, s, s, pp, n);
+ /* Assert that %as is not treated as an extension in C99 mode. */
+ scanf ("%as", fp);
+ scanf ("%a[", fp);
+ /* Tests for bad argument types: pointer target sign with %hh. */
+ scanf ("%hhd", uhhp); /* { dg-warning "format" "%hhd sign" } */
+ scanf ("%hhu", hhp); /* { dg-warning "format" "%hhu sign" } */
+}
--- /dev/null
+/* Test for scanf formats. Formats using extensions to the standard
+ should be rejected in strict pedantic mode.
+*/
+/* Origin: Joseph Myers <jsm28@cam.ac.uk> */
+/* { dg-do compile } */
+/* { dg-options "-std=iso9899:1999 -pedantic -Wformat" } */
+
+typedef __SIZE_TYPE__ size_t;
+typedef __WCHAR_TYPE__ wchar_t;
+
+extern int scanf (const char *, ...);
+
+void
+foo (int *ip, long long int *llp, size_t *zp, wchar_t *ls)
+{
+ /* The length modifiers q, Z and L as applied to integer formats are
+ extensions.
+ */
+ scanf ("%qd", llp); /* { dg-warning "C" "%q length" } */
+ scanf ("%Ld", llp); /* { dg-warning "C" "%L length" } */
+ scanf ("%Zu", zp); /* { dg-warning "C" "%Z length" } */
+ /* The conversion specifiers C and S are X/Open extensions. */
+ scanf ("%C", ls); /* { dg-warning "C" "scanf %C" } */
+ scanf ("%S", ls); /* { dg-warning "C" "scanf %S" } */
+ /* The use of operand number $ formats is an X/Open extension. */
+ scanf ("%1$d", ip); /* { dg-warning "C" "scanf $ format" } */
+}
--- /dev/null
+/* Test for scanf formats. Test that the C99 functions get their default
+ attributes in strict C99 mode, but the gettext functions do not.
+*/
+/* Origin: Joseph Myers <jsm28@cam.ac.uk> */
+/* { dg-do compile } */
+/* { dg-options "-std=iso9899:1999 -pedantic -Wformat" } */
+
+/* This may not be correct in the particular case, but allows the
+ prototypes to be declared, and we don't try to link.
+*/
+typedef struct _FILE FILE;
+extern FILE *stdin;
+
+typedef __builtin_va_list va_list;
+
+extern int fscanf (FILE *restrict, const char *restrict, ...);
+extern int scanf (const char *restrict, ...);
+extern int sscanf (const char *restrict, const char *restrict, ...);
+extern int vfscanf (FILE *restrict, const char *restrict, va_list);
+extern int vscanf (const char *restrict, va_list);
+extern int vsscanf (const char *restrict, const char *restrict, va_list);
+
+extern char *gettext (const char *);
+extern char *dgettext (const char *, const char *);
+extern char *dcgettext (const char *, const char *, int);
+
+void
+foo (int *ip, char *s, va_list v0, va_list v1, va_list v2, va_list v3,
+ va_list v4, va_list v5)
+{
+ fscanf (stdin, "%d", ip);
+ fscanf (stdin, "%ld", ip); /* { dg-warning "format" "fscanf" } */
+ scanf ("%d", ip);
+ scanf ("%ld", ip); /* { dg-warning "format" "scanf" } */
+ sscanf (s, "%d", ip);
+ sscanf (s, "%ld", ip); /* { dg-warning "format" "sscanf" } */
+ vfscanf (stdin, "%d", v0);
+ vfscanf (stdin, "%Y", v1); /* { dg-warning "format" "vfscanf" } */
+ vscanf ("%d", v2);
+ vscanf ("%Y", v3); /* { dg-warning "format" "vscanf" } */
+ vsscanf (s, "%d", v4);
+ vsscanf (s, "%Y", v5); /* { dg-warning "format" "vsscanf" } */
+ scanf (gettext ("%d"), ip);
+ scanf (gettext ("%ld"), ip);
+ scanf (dgettext ("", "%d"), ip);
+ scanf (dgettext ("", "%ld"), ip);
+ scanf (dcgettext ("", "%d", 0), ip);
+ scanf (dcgettext ("", "%ld", 0), ip);
+}
--- /dev/null
+/* Test for X/Open format extensions, as found in the
+ Single Unix Specification and in Austin Group draft 4, subject to some
+ Aardvark problem reports submitted.
+*/
+/* Origin: Joseph Myers <jsm28@cam.ac.uk> */
+/* { dg-do compile } */
+/* { dg-options "-std=gnu99 -Wformat" } */
+
+typedef __WCHAR_TYPE__ wchar_t;
+typedef __WINT_TYPE__ wint_t;
+typedef __builtin_va_list va_list;
+
+extern int printf (const char *, ...);
+extern int vprintf (const char *, va_list);
+extern int scanf (const char *, ...);
+
+void
+foo (int i, unsigned int u, wint_t lc, wchar_t *ls, int *ip, double d,
+ char *s, void *p, int *n, long int l, int i2, float *fp, long int *lp,
+ va_list va)
+{
+ /* The conversion specifiers C and S, for both printf and scanf,
+ are X/Open extensions.
+ */
+ printf ("%C", lc);
+ printf ("%3C", lc);
+ printf ("%.3C", lc); /* { dg-warning "precision" "precision with %C" } */
+ printf ("%hC", lc); /* { dg-warning "length character" "bad %hC" } */
+ printf ("%hhC", lc); /* { dg-warning "length character" "bad %hhC" } */
+ printf ("%lC", lc); /* { dg-warning "length character" "bad %lC" } */
+ printf ("%llC", lc); /* { dg-warning "length character" "bad %llC" } */
+ printf ("%jC", lc); /* { dg-warning "length character" "bad %jC" } */
+ printf ("%zC", lc); /* { dg-warning "length character" "bad %zC" } */
+ printf ("%tC", lc); /* { dg-warning "length character" "bad %tC" } */
+ printf ("%LC", lc); /* { dg-warning "length character" "bad %LC" } */
+ printf ("%-C", lc);
+ printf ("%+C", lc); /* { dg-warning "flag" "bad %+C" } */
+ printf ("% C", lc); /* { dg-warning "flag" "bad % C" } */
+ printf ("%#C", lc); /* { dg-warning "flag" "bad %#C" } */
+ printf ("%0C", lc); /* { dg-warning "flag" "bad %0C" } */
+ printf ("%S", ls);
+ printf ("%3S", ls);
+ printf ("%.3S", ls);
+ printf ("%hS", ls); /* { dg-warning "length character" "bad %hS" } */
+ printf ("%hhS", ls); /* { dg-warning "length character" "bad %hhS" } */
+ printf ("%lS", ls); /* { dg-warning "length character" "bad %lS" } */
+ printf ("%llS", ls); /* { dg-warning "length character" "bad %llS" } */
+ printf ("%jS", ls); /* { dg-warning "length character" "bad %jS" } */
+ printf ("%zS", ls); /* { dg-warning "length character" "bad %zS" } */
+ printf ("%tS", ls); /* { dg-warning "length character" "bad %tS" } */
+ printf ("%LS", ls); /* { dg-warning "length character" "bad %LS" } */
+ printf ("%-S", ls);
+ printf ("%+S", ls); /* { dg-warning "flag" "bad %+S" } */
+ printf ("% S", ls); /* { dg-warning "flag" "bad % S" } */
+ printf ("%#S", ls); /* { dg-warning "flag" "bad %#S" } */
+ printf ("%0S", ls); /* { dg-warning "flag" "bad %0S" } */
+ scanf ("%C", ls);
+ scanf ("%S", ls);
+ scanf ("%*C%*S");
+ scanf ("%2C%3S", ls, ls);
+ scanf ("%hC", ls); /* { dg-warning "length character" "bad %hC" } */
+ scanf ("%hhC", ls); /* { dg-warning "length character" "bad %hhC" } */
+ scanf ("%lC", ls); /* { dg-warning "length character" "bad %lC" } */
+ scanf ("%llC", ls); /* { dg-warning "length character" "bad %llC" } */
+ scanf ("%jC", ls); /* { dg-warning "length character" "bad %jC" } */
+ scanf ("%zC", ls); /* { dg-warning "length character" "bad %zC" } */
+ scanf ("%tC", ls); /* { dg-warning "length character" "bad %tC" } */
+ scanf ("%LC", ls); /* { dg-warning "length character" "bad %LC" } */
+ scanf ("%hS", ls); /* { dg-warning "length character" "bad %hS" } */
+ scanf ("%hhS", ls); /* { dg-warning "length character" "bad %hhS" } */
+ scanf ("%lS", ls); /* { dg-warning "length character" "bad %lS" } */
+ scanf ("%llS", ls); /* { dg-warning "length character" "bad %llS" } */
+ scanf ("%jS", ls); /* { dg-warning "length character" "bad %jS" } */
+ scanf ("%zS", ls); /* { dg-warning "length character" "bad %zS" } */
+ scanf ("%tS", ls); /* { dg-warning "length character" "bad %tS" } */
+ scanf ("%LS", ls); /* { dg-warning "length character" "bad %LS" } */
+ /* In C99 mode (even with extensions), %aS is a floating point
+ format followed by an S.
+ */
+ scanf ("%aS", fp);
+ /* The printf flag character ' is an X/Open extension. */
+ /* Allowing %'F here presumes acceptance of the corresponding Aardvark
+ report.
+ */
+ printf ("%'d%'i%'u%'f%'F%'g%'G", i, i, u, d, d, d, d);
+ printf ("%'o", u); /* { dg-warning "flag" "bad use of ' flag" } */
+ printf ("%'x", u); /* { dg-warning "flag" "bad use of ' flag" } */
+ printf ("%'X", u); /* { dg-warning "flag" "bad use of ' flag" } */
+ printf ("%'e", d); /* { dg-warning "flag" "bad use of ' flag" } */
+ printf ("%'E", d); /* { dg-warning "flag" "bad use of ' flag" } */
+ printf ("%'a", d); /* { dg-warning "flag" "bad use of ' flag" } */
+ printf ("%'A", d); /* { dg-warning "flag" "bad use of ' flag" } */
+ printf ("%'c", i); /* { dg-warning "flag" "bad use of ' flag" } */
+ printf ("%'s", s); /* { dg-warning "flag" "bad use of ' flag" } */
+ printf ("%'p", p); /* { dg-warning "flag" "bad use of ' flag" } */
+ printf ("%'n", n); /* { dg-warning "flag" "bad use of ' flag" } */
+ /* The use of operand number $ formats is an X/Open extension. */
+ /* Banning gaps in the arguments used with scanf, and not objecting to
+ multiple use of an argument with scanf, presumes acceptance of the
+ corresponding Aardvark reports.
+ */
+ scanf ("%1$d", ip);
+ printf ("%1$d", i);
+ printf ("%3$*2$.*1$d", i2, i, l);
+ printf ("%4$ld%7$ld%5$d%6$d%3$d%1$d%2$d", i, i, i, l, i, i, l);
+ scanf ("%4$ld%7$ld%5$d%6$d%3$d%1$d%2$d", ip, ip, ip, lp, ip, ip, lp);
+ printf ("%1$d%d", i, i); /* { dg-warning "missing" "mixing $ and non-$ formats" } */
+ printf ("%%%1$d%%%2$d", i, i);
+ printf ("%d%2$d", i); /* { dg-warning "type character" "mixing $ and non-$ formats" } */
+ printf ("%1$*d", i, i); /* { dg-warning "missing" "mixing $ and non-$ formats" } */
+ scanf ("%1$d%d", ip, ip); /* { dg-warning "missing" "mixing $ and non-$ formats" } */
+ scanf ("%*f%%%1$d%%%2$d", ip, ip);
+ printf ("%2$d", i); /* { dg-warning "operand" "$ number too large" } */
+ printf ("%0$d", i); /* { dg-warning "operand" "$ number too small" } */
+ printf ("%3$d%1$d", i, i, i); /* { dg-warning "before used" "unused $ operand" } */
+ printf ("%2$d%1$d", i, i, i); /* { dg-warning "unused" "unused $ operand" } */
+ vprintf ("%3$d%1$d", va); /* { dg-warning "before used" "unused $ operand" } */
+}