OSDN Git Service

* c-common.c (init_dollar_format_checking,
authorjsm28 <jsm28@138bc75d-0d04-0410-961f-82ee72b054a4>
Mon, 21 Aug 2000 19:38:35 +0000 (19:38 +0000)
committerjsm28 <jsm28@138bc75d-0d04-0410-961f-82ee72b054a4>
Mon, 21 Aug 2000 19:38:35 +0000 (19:38 +0000)
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.

testsuite:
* 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.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@35856 138bc75d-0d04-0410-961f-82ee72b054a4

13 files changed:
gcc/ChangeLog
gcc/c-common.c
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/c90-printf-3.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/c90-scanf-2.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/c90-scanf-3.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/c90-scanf-4.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/c90-strftime-1.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/c99-printf-3.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/c99-scanf-1.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/c99-scanf-2.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/c99-scanf-3.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/format-xopen-1.c [new file with mode: 0644]

index 0f09fa1..a800469 100644 (file)
@@ -1,3 +1,16 @@
+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,
index c0b9816..5e46af0 100644 (file)
@@ -1324,6 +1324,11 @@ static international_format_info *international_format_list = NULL;
 
 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
@@ -1501,6 +1506,169 @@ check_function_format (name, assembler_name, params)
     }
 }
 
+
+/* 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.  */
@@ -1524,7 +1692,9 @@ check_format_info (info, params)
   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.  */
@@ -1624,6 +1794,7 @@ check_format_info (info, params)
     }
 
   first_fillin_param = params;
+  init_dollar_format_checking (info->first_arg_num, first_fillin_param);
   while (1)
     {
       int aflag;
@@ -1633,8 +1804,11 @@ check_format_info (info, params)
        {
          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++ != '%')
@@ -1657,6 +1831,22 @@ check_format_info (info, params)
          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, &params);
+             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;
@@ -1709,35 +1899,21 @@ check_format_info (info, params)
        }
       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, &params);
+             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)
@@ -1774,11 +1950,25 @@ check_format_info (info, params)
                  tfaff ();
                  return;
                }
+             if (has_operand_number > 0)
+               {
+                 int opnum;
+                 opnum = maybe_read_dollar_number (&format_chars, 1,
+                                                   first_fillin_param,
+                                                   &params);
+                 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,
@@ -1807,17 +1997,31 @@ check_format_info (info, params)
                 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,
+                                                       &params);
+                     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)
                          &&
index c94de4d..1293a6c 100644 (file)
@@ -1,5 +1,13 @@
 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>
diff --git a/gcc/testsuite/gcc.dg/c90-printf-3.c b/gcc/testsuite/gcc.dg/c90-printf-3.c
new file mode 100644 (file)
index 0000000..119cdee
--- /dev/null
@@ -0,0 +1,60 @@
+/* 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);
+}
diff --git a/gcc/testsuite/gcc.dg/c90-scanf-2.c b/gcc/testsuite/gcc.dg/c90-scanf-2.c
new file mode 100644 (file)
index 0000000..c0a7a70
--- /dev/null
@@ -0,0 +1,37 @@
+/* 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" } */
+}
diff --git a/gcc/testsuite/gcc.dg/c90-scanf-3.c b/gcc/testsuite/gcc.dg/c90-scanf-3.c
new file mode 100644 (file)
index 0000000..147a222
--- /dev/null
@@ -0,0 +1,21 @@
+/* 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[]" } */
+}
diff --git a/gcc/testsuite/gcc.dg/c90-scanf-4.c b/gcc/testsuite/gcc.dg/c90-scanf-4.c
new file mode 100644 (file)
index 0000000..3bbe66e
--- /dev/null
@@ -0,0 +1,51 @@
+/* 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);
+}
diff --git a/gcc/testsuite/gcc.dg/c90-strftime-1.c b/gcc/testsuite/gcc.dg/c90-strftime-1.c
new file mode 100644 (file)
index 0000000..6a13d5e
--- /dev/null
@@ -0,0 +1,23 @@
+/* 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" } */
+}
diff --git a/gcc/testsuite/gcc.dg/c99-printf-3.c b/gcc/testsuite/gcc.dg/c99-printf-3.c
new file mode 100644 (file)
index 0000000..26f76d7
--- /dev/null
@@ -0,0 +1,56 @@
+/* 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);
+}
diff --git a/gcc/testsuite/gcc.dg/c99-scanf-1.c b/gcc/testsuite/gcc.dg/c99-scanf-1.c
new file mode 100644 (file)
index 0000000..c0b4c14
--- /dev/null
@@ -0,0 +1,165 @@
+/* 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" } */
+}
diff --git a/gcc/testsuite/gcc.dg/c99-scanf-2.c b/gcc/testsuite/gcc.dg/c99-scanf-2.c
new file mode 100644 (file)
index 0000000..c769e6d
--- /dev/null
@@ -0,0 +1,27 @@
+/* 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" } */
+}
diff --git a/gcc/testsuite/gcc.dg/c99-scanf-3.c b/gcc/testsuite/gcc.dg/c99-scanf-3.c
new file mode 100644 (file)
index 0000000..df30c05
--- /dev/null
@@ -0,0 +1,49 @@
+/* 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);
+}
diff --git a/gcc/testsuite/gcc.dg/format-xopen-1.c b/gcc/testsuite/gcc.dg/format-xopen-1.c
new file mode 100644 (file)
index 0000000..2bfcbfb
--- /dev/null
@@ -0,0 +1,118 @@
+/* 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" } */
+}