This change is from an idea suggested by Arthur David Olson.
authorPaul Eggert <eggert@twinsun.com>
Sun, 12 Apr 1998 19:52:16 +0000 (19:52 +0000)
committerJeff Law <law@gcc.gnu.org>
Sun, 12 Apr 1998 19:52:16 +0000 (13:52 -0600)
* c-common.c (decl_attributes, record_function_format,
check_format_info, init_function_format_info):
Add support for strftime format checking.
(enum format_type): New type.
(record_function_format): Now static, and takes value of type
enum format_type instead of int.
(time_char_table): New constant.
(struct function_format_info): format_type member renamed from is_scan.
(check_format_info): Use `warning' rather than sprintf followed by
`warning', to avoid mishandling `%' in warnings.
Change `pedwarn' to `warning', since these warnings do not necessarily
mean the program does not conform to the C Standard, as the code
need not be executed.
* c-tree.h (record_function_format): Remove decl; no longer extern.
* extend.texi: Add documentation for strftime format checking.

From-SVN: r19151

gcc/ChangeLog
gcc/c-common.c
gcc/c-tree.h
gcc/extend.texi

index 44bd2dd4e695941f6963ad8cdc1a583e86d9bb91..b63fcfcdff7338911ff5d1735ba46f4663dd310e 100644 (file)
@@ -1,3 +1,25 @@
+1998-04-12  Paul Eggert  <eggert@twinsun.com>
+
+       This change is from an idea suggested by Arthur David Olson.
+
+       * c-common.c (decl_attributes, record_function_format,
+       check_format_info, init_function_format_info):
+       Add support for strftime format checking.
+       (enum format_type): New type.
+       (record_function_format): Now static, and takes value of type
+       enum format_type instead of int.
+       (time_char_table): New constant.
+       (struct function_format_info): format_type member renamed from is_scan.
+       (check_format_info): Use `warning' rather than sprintf followed by
+       `warning', to avoid mishandling `%' in warnings.
+       Change `pedwarn' to `warning', since these warnings do not necessarily
+       mean the program does not conform to the C Standard, as the code
+       need not be executed.
+
+       * c-tree.h (record_function_format): Remove decl; no longer extern.
+
+       * extend.texi: Add documentation for strftime format checking.
+
 Sun Apr 12 20:23:03 1998  Jeffrey A Law  (law@cygnus.com)
 
        * mips/ecoffl.h: Do not include mips.h.
index 2422741ccee43db3b2f5be300f5034d2cf0ee7d7..c84e1957c3359ef4fd28f0a99b122b88a49738db 100644 (file)
@@ -44,10 +44,15 @@ enum attrs {A_PACKED, A_NOCOMMON, A_COMMON, A_NORETURN, A_CONST, A_T_UNION,
            A_CONSTRUCTOR, A_DESTRUCTOR, A_MODE, A_SECTION, A_ALIGNED,
            A_UNUSED, A_FORMAT, A_FORMAT_ARG, A_WEAK, A_ALIAS};
 
+enum format_type { printf_format_type, scanf_format_type,
+                  strftime_format_type };
+
 static void declare_hidden_char_array  PROTO((char *, char *));
 static void add_attribute              PROTO((enum attrs, char *,
                                               int, int, int));
 static void init_attributes            PROTO((void));
+static void record_function_format     PROTO((tree, tree, enum format_type,
+                                              int, int));
 static void record_international_format        PROTO((tree, tree, int));
 
 /* Keep a stack of if statements.  We record the number of compound
@@ -649,13 +654,13 @@ decl_attributes (node, attributes, prefix_attributes)
 
        case A_FORMAT:
          {
-           tree format_type = TREE_VALUE (args);
+           tree format_type_id = TREE_VALUE (args);
            tree format_num_expr = TREE_VALUE (TREE_CHAIN (args));
            tree first_arg_num_expr
              = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (args)));
            int format_num;
            int first_arg_num;
-           int is_scan;
+           enum format_type format_type;
            tree argument;
            int arg_num;
        
@@ -666,26 +671,27 @@ decl_attributes (node, attributes, prefix_attributes)
                continue;
              }
        
-           if (TREE_CODE (format_type) == IDENTIFIER_NODE
-               && (!strcmp (IDENTIFIER_POINTER (format_type), "printf")
-                   || !strcmp (IDENTIFIER_POINTER (format_type),
-                               "__printf__")))
-             is_scan = 0;
-           else if (TREE_CODE (format_type) == IDENTIFIER_NODE
-                    && (!strcmp (IDENTIFIER_POINTER (format_type), "scanf")
-                        || !strcmp (IDENTIFIER_POINTER (format_type),
-                                    "__scanf__")))
-             is_scan = 1;
-           else if (TREE_CODE (format_type) == IDENTIFIER_NODE)
+           if (TREE_CODE (format_type_id) != IDENTIFIER_NODE)
              {
-               warning ("`%s' is an unrecognized format function type",
-                        IDENTIFIER_POINTER (format_type));
+               error ("unrecognized format specifier");
                continue;
              }
            else
              {
-               error ("unrecognized format specifier");
-               continue;
+               char *p = IDENTIFIER_POINTER (format_type_id);
+               
+               if (!strcmp (p, "printf") || !strcmp (p, "__printf__"))
+                 format_type = printf_format_type;
+               else if (!strcmp (p, "scanf") || !strcmp (p, "__scanf__"))
+                 format_type = scanf_format_type;
+               else if (!strcmp (p, "strftime")
+                        || !strcmp (p, "__strftime__"))
+                 format_type = strftime_format_type;
+               else
+                 {
+                   error ("`%s' is an unrecognized format function type", p);
+                   continue;
+                 }
              }
 
            /* Strip any conversions from the string index and first arg number
@@ -751,7 +757,7 @@ decl_attributes (node, attributes, prefix_attributes)
 
            record_function_format (DECL_NAME (decl),
                                    DECL_ASSEMBLER_NAME (decl),
-                                   is_scan, format_num, first_arg_num);
+                                   format_type, format_num, first_arg_num);
            break;
          }
 
@@ -1011,12 +1017,37 @@ static format_char_info scan_char_table[] = {
   { NULL }
 };
 
+/* Handle format characters recognized by glibc's strftime.c.
+   '2' - MUST do years as only two digits
+   '3' - MAY do years as only two digits (depending on locale)
+   'E' - E modifier is acceptable
+   'O' - O modifier is acceptable to Standard C
+   'o' - O modifier is acceptable as a GNU extension
+   'G' - other GNU extensions  */
+
+static format_char_info time_char_table[] = {
+  { "y",               0, NULL, NULL, NULL, NULL, NULL, NULL, "2EO-_0w" },
+  { "D",               0, NULL, NULL, NULL, NULL, NULL, NULL, "2" },
+  { "g",               0, NULL, NULL, NULL, NULL, NULL, NULL, "2O-_0w" },
+  { "cx",              0, NULL, NULL, NULL, NULL, NULL, NULL, "3E" },
+  { "%RTXnrt",         0, NULL, NULL, NULL, NULL, NULL, NULL, "" },
+  { "P",               0, NULL, NULL, NULL, NULL, NULL, NULL, "G" },
+  { "HIMSUWdemw",      0, NULL, NULL, NULL, NULL, NULL, NULL, "-_0Ow" },
+  { "Vju",             0, NULL, NULL, NULL, NULL, NULL, NULL, "-_0Oow" },
+  { "Gklsz",           0, NULL, NULL, NULL, NULL, NULL, NULL, "-_0OGw" },
+  { "ABZa",            0, NULL, NULL, NULL, NULL, NULL, NULL, "^#" },
+  { "p",               0, NULL, NULL, NULL, NULL, NULL, NULL, "#" },
+  { "bh",              0, NULL, NULL, NULL, NULL, NULL, NULL, "^" },
+  { "CY",              0, NULL, NULL, NULL, NULL, NULL, NULL, "-_0EOw" },
+  { NULL }
+};
+
 typedef struct function_format_info
 {
   struct function_format_info *next;  /* next structure on the list */
   tree name;                   /* identifier such as "printf" */
   tree assembler_name;         /* optional mangled identifier (for C++) */
-  int is_scan;                 /* TRUE if *scanf */
+  enum format_type format_type;        /* type of format (printf, scanf, etc.) */
   int format_num;              /* number of format argument */
   int first_arg_num;           /* number of first arg (zero for varargs) */
 } function_format_info;
@@ -1048,15 +1079,26 @@ static void check_format_info           PROTO((function_format_info *, tree));
 void
 init_function_format_info ()
 {
-  record_function_format (get_identifier ("printf"), NULL_TREE, 0, 1, 2);
-  record_function_format (get_identifier ("fprintf"), NULL_TREE, 0, 2, 3);
-  record_function_format (get_identifier ("sprintf"), NULL_TREE, 0, 2, 3);
-  record_function_format (get_identifier ("scanf"), NULL_TREE, 1, 1, 2);
-  record_function_format (get_identifier ("fscanf"), NULL_TREE, 1, 2, 3);
-  record_function_format (get_identifier ("sscanf"), NULL_TREE, 1, 2, 3);
-  record_function_format (get_identifier ("vprintf"), NULL_TREE, 0, 1, 0);
-  record_function_format (get_identifier ("vfprintf"), NULL_TREE, 0, 2, 0);
-  record_function_format (get_identifier ("vsprintf"), NULL_TREE, 0, 2, 0);
+  record_function_format (get_identifier ("printf"), NULL_TREE,
+                         printf_format_type, 1, 2);
+  record_function_format (get_identifier ("fprintf"), NULL_TREE,
+                         printf_format_type, 2, 3);
+  record_function_format (get_identifier ("sprintf"), NULL_TREE,
+                         printf_format_type, 2, 3);
+  record_function_format (get_identifier ("scanf"), NULL_TREE,
+                         scanf_format_type, 1, 2);
+  record_function_format (get_identifier ("fscanf"), NULL_TREE,
+                         scanf_format_type, 2, 3);
+  record_function_format (get_identifier ("sscanf"), NULL_TREE,
+                         scanf_format_type, 2, 3);
+  record_function_format (get_identifier ("vprintf"), NULL_TREE,
+                         printf_format_type, 1, 0);
+  record_function_format (get_identifier ("vfprintf"), NULL_TREE,
+                         printf_format_type, 2, 0);
+  record_function_format (get_identifier ("vsprintf"), NULL_TREE,
+                         printf_format_type, 2, 0);
+  record_function_format (get_identifier ("strftime"), NULL_TREE,
+                         strftime_format_type, 3, 0);
 
   record_international_format (get_identifier ("gettext"), NULL_TREE, 1);
   record_international_format (get_identifier ("dgettext"), NULL_TREE, 2);
@@ -1065,19 +1107,19 @@ init_function_format_info ()
 
 /* Record information for argument format checking.  FUNCTION_IDENT is
    the identifier node for the name of the function to check (its decl
-   need not exist yet).  IS_SCAN is true for scanf-type format checking;
-   false indicates printf-style format checking.  FORMAT_NUM is the number
+   need not exist yet).
+   FORMAT_TYPE specifies the type of format checking.  FORMAT_NUM is the number
    of the argument which is the format control string (starting from 1).
    FIRST_ARG_NUM is the number of the first actual argument to check
    against the format string, or zero if no checking is not be done
    (e.g. for varargs such as vfprintf).  */
 
-void
-record_function_format (name, assembler_name, is_scan,
+static void
+record_function_format (name, assembler_name, format_type,
                        format_num, first_arg_num)
       tree name;
       tree assembler_name;
-      int is_scan;
+      enum format_type format_type;
       int format_num;
       int first_arg_num;
 {
@@ -1100,7 +1142,7 @@ record_function_format (name, assembler_name, is_scan,
       info->assembler_name = assembler_name;
     }
 
-  info->is_scan = is_scan;
+  info->format_type = format_type;
   info->format_num = format_num;
   info->first_arg_num = first_arg_num;
 }
@@ -1195,7 +1237,6 @@ check_format_info (info, params)
   tree first_fillin_param;
   char *format_chars;
   format_char_info *fci;
-  static char message[132];
   char flag_chars[8];
   int has_operand_number = 0;
 
@@ -1304,7 +1345,7 @@ check_format_info (info, params)
        }
       flag_chars[0] = 0;
       suppressed = wide = precise = FALSE;
-      if (info->is_scan)
+      if (info->format_type == scanf_format_type)
        {
          suppressed = *format_chars == '*';
          if (suppressed)
@@ -1312,7 +1353,47 @@ check_format_info (info, params)
          while (isdigit (*format_chars))
            ++format_chars;
        }
-      else
+      else if (info->format_type == strftime_format_type)
+        {
+         while (*format_chars != 0 && index ("_-0^#", *format_chars) != 0)
+           {
+             if (pedantic)
+               warning ("ANSI C does not support the strftime `%c' flag",
+                        *format_chars);
+             if (index (flag_chars, *format_chars) != 0)
+               {
+                 warning ("repeated `%c' flag in format",
+                          *format_chars);
+                 ++format_chars;
+               }
+             else
+               {
+                 i = strlen (flag_chars);
+                 flag_chars[i++] = *format_chars++;
+                 flag_chars[i] = 0;
+               }
+           }
+         while (isdigit ((unsigned char) *format_chars))
+           {
+             wide = TRUE;
+              ++format_chars;
+           }
+         if (wide && pedantic)
+           warning ("ANSI C does not support strftime format width");
+         if (*format_chars == 'E' || *format_chars == 'O')
+           {
+             i = strlen (flag_chars);
+             flag_chars[i++] = *format_chars++;
+             flag_chars[i] = 0;
+             if (*format_chars == 'E' || *format_chars == 'O')
+               {
+                 warning ("multiple E/O modifiers in format");
+                 while (*format_chars == 'E' || *format_chars == 'O')
+                   ++format_chars;
+               }
+           }
+       }
+      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.  */
@@ -1345,11 +1426,7 @@ check_format_info (info, params)
          while (*format_chars != 0 && index (" +#0-", *format_chars) != 0)
            {
              if (index (flag_chars, *format_chars) != 0)
-               {
-                 sprintf (message, "repeated `%c' flag in format",
-                          *format_chars++);
-                 warning (message);
-               }
+               warning ("repeated `%c' flag in format", *format_chars++);
              else
                {
                  i = strlen (flag_chars);
@@ -1392,12 +1469,7 @@ check_format_info (info, params)
                      &&
                      (TYPE_MAIN_VARIANT (TREE_TYPE (cur_param))
                       != unsigned_type_node))
-                   {
-                     sprintf (message,
-                              "field width is not type int (arg %d)",
-                              arg_num);
-                     warning (message);
-                   }
+                   warning ("field width is not type int (arg %d)", arg_num);
                }
            }
          else
@@ -1431,12 +1503,8 @@ check_format_info (info, params)
                      ++arg_num;
                      if (TYPE_MAIN_VARIANT (TREE_TYPE (cur_param))
                          != integer_type_node)
-                       {
-                         sprintf (message,
-                                  "field width is not type int (arg %d)",
-                                  arg_num);
-                         warning (message);
-                       }
+                       warning ("field width is not type int (arg %d)",
+                                arg_num);
                    }
                }
              else
@@ -1446,92 +1514,106 @@ check_format_info (info, params)
                }
            }
        }
-      if (*format_chars == 'h' || *format_chars == 'l')
-       length_char = *format_chars++;
-      else if (*format_chars == 'q' || *format_chars == 'L')
-       {
-         length_char = *format_chars++;
-         if (pedantic && length_char == 'q')
-           pedwarn ("ANSI C does not support the `%c' length modifier",
-                    length_char);
-       }
-      else if (*format_chars == 'Z')
-       {
-         length_char = *format_chars++;
-         if (pedantic)
-           pedwarn ("ANSI C does not support the `Z' length modifier");
-       }
-      else
-       length_char = 0;
-      if (length_char == 'l' && *format_chars == 'l')
-       {
-         length_char = 'q', format_chars++;
-         if (pedantic)
-           pedwarn ("ANSI C does not support the `ll' length modifier");
-       }
+
       aflag = 0;
-      if (*format_chars == 'a' && info->is_scan)
+
+      if (info->format_type != strftime_format_type)
        {
-         if (format_chars[1] == 's' || format_chars[1] == 'S'
-             || format_chars[1] == '[')
+         if (*format_chars == 'h' || *format_chars == 'l')
+           length_char = *format_chars++;
+         else if (*format_chars == 'q' || *format_chars == 'L')
            {
-             /* `a' is used as a flag.  */
-             aflag = 1;
-             format_chars++;
+             length_char = *format_chars++;
+             if (pedantic && length_char == 'q')
+               warning ("ANSI C does not support the `%c' length modifier",
+                        length_char);
            }
-       }
-      if (suppressed && length_char != 0)
-       {
-         sprintf (message,
-                  "use of `*' and `%c' together in format",
-                  length_char);
-         warning (message);
+         else if (*format_chars == 'Z')
+           {
+             length_char = *format_chars++;
+             if (pedantic)
+               warning ("ANSI C does not support the `Z' length modifier");
+           }
+         else
+           length_char = 0;
+         if (length_char == 'l' && *format_chars == 'l')
+           {
+             length_char = 'q', format_chars++;
+             if (pedantic)
+               warning ("ANSI C does not support the `ll' length modifier");
+           }
+         if (*format_chars == 'a' && info->format_type == scanf_format_type)
+           {
+             if (format_chars[1] == 's' || format_chars[1] == 'S'
+                 || format_chars[1] == '[')
+               {
+                 /* `a' is used as a flag.  */
+                 aflag = 1;
+                 format_chars++;
+               }
+           }
+         if (suppressed && length_char != 0)
+           warning ("use of `*' and `%c' together in format", length_char);
        }
       format_char = *format_chars;
-      if (format_char == 0 || format_char == '%')
+      if (format_char == 0
+         || (info->format_type != strftime_format_type && format_char == '%'))
        {
          warning ("conversion lacks type at end of format");
          continue;
        }
       format_chars++;
-      fci = info->is_scan ? scan_char_table : print_char_table;
+      switch (info->format_type)
+       {
+       case printf_format_type:
+         fci = print_char_table;
+         break;
+       case scanf_format_type:
+         fci = scan_char_table;
+         break;
+       case strftime_format_type:
+         fci = time_char_table;
+         break;
+       default:
+         abort ();
+       }
       while (fci->format_chars != 0
             && index (fci->format_chars, format_char) == 0)
          ++fci;
       if (fci->format_chars == 0)
        {
          if (format_char >= 040 && format_char < 0177)
-           sprintf (message,
-                    "unknown conversion type character `%c' in format",
+           warning ("unknown conversion type character `%c' in format",
                     format_char);
          else
-           sprintf (message,
-                    "unknown conversion type character 0x%x in format",
+           warning ("unknown conversion type character 0x%x in format",
                     format_char);
-         warning (message);
          continue;
        }
-      if (wide && index (fci->flag_chars, 'w') == 0)
+      if (pedantic)
        {
-         sprintf (message, "width used with `%c' format",
-                  format_char);
-         warning (message);
+         if (index (fci->flag_chars, 'G') != 0)
+           warning ("ANSI C does not support `%%%c'", format_char);
+         if (index (fci->flag_chars, 'o') != 0
+             && index (flag_chars, 'O') != 0)
+           warning ("ANSI C does not support `%%O%c'", format_char);
        }
+      if (wide && index (fci->flag_chars, 'w') == 0)
+       warning ("width used with `%c' format", format_char);
+      if (index (fci->flag_chars, '2') != 0)
+       warning ("`%%%c' yields only last 2 digits of year", format_char);
+      else if (index (fci->flag_chars, '3') != 0)
+       warning ("`%%%c' yields only last 2 digits of year in some locales",
+                format_char);
       if (precise && index (fci->flag_chars, 'p') == 0)
-       {
-         sprintf (message, "precision used with `%c' format",
-                  format_char);
-         warning (message);
-       }
+       warning ("precision used with `%c' format", format_char);
       if (aflag && index (fci->flag_chars, 'a') == 0)
        {
-         sprintf (message, "`a' flag used with `%c' format",
-                  format_char);
-         warning (message);
+         warning ("`a' flag used with `%c' format", format_char);
          /* To simplify the following code.  */
          aflag = 0;
        }
-      if (info->is_scan && format_char == '[')
+      if (info->format_type == scanf_format_type && format_char == '[')
        {
          /* Skip over scan set, in case it happens to have '%' in it.  */
          if (*format_chars == '^')
@@ -1543,39 +1625,29 @@ check_format_info (info, params)
          while (*format_chars && *format_chars != ']')
            ++format_chars;
          if (*format_chars != ']')
-             /* The end of the format string was reached.  */
-             warning ("no closing `]' for `%%[' format");
+           /* The end of the format string was reached.  */
+           warning ("no closing `]' for `%%[' format");
        }
       if (suppressed)
        {
          if (index (fci->flag_chars, '*') == 0)
-           {
-             sprintf (message,
-                      "suppression of `%c' conversion in format",
-                      format_char);
-             warning (message);
-           }
+           warning ("suppression of `%c' conversion in format", format_char);
          continue;
        }
       for (i = 0; flag_chars[i] != 0; ++i)
        {
          if (index (fci->flag_chars, flag_chars[i]) == 0)
-           {
-             sprintf (message, "flag `%c' used with type `%c'",
-                      flag_chars[i], format_char);
-             warning (message);
-           }
+           warning ("flag `%c' used with type `%c'",
+                    flag_chars[i], format_char);
        }
+      if (info->format_type == strftime_format_type)
+       continue;
       integral_format = (format_char == 'd' || format_char == 'i'
                         || format_char == 'o' || format_char == 'u'
                         || format_char == 'x' || format_char == 'x');
       if (precise && index (flag_chars, '0') != 0 && integral_format)
-       {
-         sprintf (message,
-                  "`0' flag ignored with precision specifier and `%c' format",
-                  format_char);
-         warning (message);
-       }
+       warning ("`0' flag ignored with precision specifier and `%c' format",
+                format_char);
       switch (length_char)
        {
        default: wanted_type = fci->nolen ? *(fci->nolen) : 0; break;
@@ -1637,20 +1709,17 @@ check_format_info (info, params)
              continue;
            }
          if (TREE_CODE (cur_type) != ERROR_MARK)
-           {
-             sprintf (message,
-                      "format argument is not a %s (arg %d)",
-                      ((fci->pointer_count + aflag == 1)
-                       ? "pointer" : "pointer to a pointer"),
-                      arg_num);
-             warning (message);
-           }
+           warning ("format argument is not a %s (arg %d)",
+                    ((fci->pointer_count + aflag == 1)
+                     ? "pointer" : "pointer to a pointer"),
+                    arg_num);
          break;
        }
 
       /* See if this is an attempt to write into a const type with
         scanf.  */
-      if (info->is_scan && i == fci->pointer_count + aflag
+      if (info->format_type == scanf_format_type
+         && i == fci->pointer_count + aflag
          && wanted_type != 0
          && TREE_CODE (cur_type) != ERROR_MARK
          && (TYPE_READONLY (cur_type)
@@ -1658,10 +1727,7 @@ check_format_info (info, params)
                  && (TREE_CODE_CLASS (TREE_CODE (cur_param)) == 'c'
                      || (TREE_CODE_CLASS (TREE_CODE (cur_param)) == 'd'
                          && TREE_READONLY (cur_param))))))
-       {
-         sprintf (message, "writing into constant object (arg %d)", arg_num);
-         warning (message);
-       }
+       warning ("writing into constant object (arg %d)", arg_num);
 
       /* Check the type of the "real" argument, if there's a type we want.  */
       if (i == fci->pointer_count + aflag && wanted_type != 0
@@ -1721,11 +1787,7 @@ check_format_info (info, params)
            that = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (cur_type)));
 
          if (strcmp (this, that) != 0)
-           {
-             sprintf (message, "%s format, %s arg (arg %d)",
-                       this, that, arg_num);
-             warning (message);
-           }
+           warning ("%s format, %s arg (arg %d)", this, that, arg_num);
        }
     }
 }
@@ -1829,7 +1891,7 @@ convert_and_check (type, expr)
                 || TREE_UNSIGNED (type)
                 || ! int_fits_type_p (expr, unsigned_type (type)))
                && skip_evaluation == 0)
-               warning ("overflow in implicit constant conversion");
+             warning ("overflow in implicit constant conversion");
        }
       else
        unsigned_conversion_warning (t, expr);
index 3e0e10a401fa4959205fe9260ce42201adb3b96a..d3884dc44fd14bc192b0fbee4e65281c53947122 100644 (file)
@@ -162,7 +162,6 @@ extern void gen_aux_info_record                 PROTO((tree, int, int, int));
 extern void declare_function_name               PROTO((void));
 extern void decl_attributes                     PROTO((tree, tree, tree));
 extern void init_function_format_info          PROTO((void));
-extern void record_function_format             PROTO((tree, tree, int, int, int));
 extern void check_function_format              PROTO((tree, tree, tree));
 /* Print an error message for invalid operands to arith operation CODE.
    NOP_EXPR is used as a special case (see truthvalue_conversion).  */
index f7598b84d3c252e3ea70c5936006eb7059f91138..a691162bf6b4522e12990fcb8b7c377514f92fe2 100644 (file)
@@ -1274,7 +1274,7 @@ hack ((union foo) x);
 @cindex functions in arbitrary sections
 @cindex @code{volatile} applied to function
 @cindex @code{const} applied to function
-@cindex functions with @code{printf} or @code{scanf} style arguments
+@cindex functions with @code{printf}, @code{scanf} or @code{strftime} style arguments
 @cindex functions that are passed arguments in registers on the 386
 @cindex functions that pop the argument stack on the 386
 @cindex functions that do not pop the argument stack on the 386
@@ -1378,9 +1378,9 @@ return @code{void}.
 
 @item format (@var{archetype}, @var{string-index}, @var{first-to-check})
 @cindex @code{format} function attribute
-The @code{format} attribute specifies that a function takes @code{printf}
-or @code{scanf} style arguments which should be type-checked against a
-format string.  For example, the declaration:
+The @code{format} attribute specifies that a function takes @code{printf},
+@code{scanf}, or @code{strftime} style arguments which should be type-checked
+against a format string.  For example, the declaration:
 
 @smallexample
 extern int
@@ -1394,7 +1394,8 @@ for consistency with the @code{printf} style format string argument
 @code{my_format}.
 
 The parameter @var{archetype} determines how the format string is
-interpreted, and should be either @code{printf} or @code{scanf}.  The
+interpreted, and should be either @code{printf}, @code{scanf}, or
+@code{strftime}.  The
 parameter @var{string-index} specifies which argument is the format
 string argument (starting from 1), while @var{first-to-check} is the
 number of the first argument to check against the format string.  For
@@ -1411,7 +1412,7 @@ The @code{format} attribute allows you to identify your own functions
 which take format strings as arguments, so that GNU CC can check the
 calls to these functions for errors.  The compiler always checks formats
 for the ANSI library functions @code{printf}, @code{fprintf},
-@code{sprintf}, @code{scanf}, @code{fscanf}, @code{sscanf},
+@code{sprintf}, @code{scanf}, @code{fscanf}, @code{sscanf}, @code{strftime},
 @code{vprintf}, @code{vfprintf} and @code{vsprintf} whenever such
 warnings are requested (using @samp{-Wformat}), so there is no need to
 modify the header file @file{stdio.h}.
@@ -1431,18 +1432,19 @@ my_dgettext (char *my_domain, const char *my_format)
 
 @noindent
 causes the compiler to check the arguments in calls to
-@code{my_dgettext} whose result is passed to a @code{printf} or
-@code{scanf} type function for consistency with the @code{printf} style
-format string argument @code{my_format}.
+@code{my_dgettext} whose result is passed to a @code{printf},
+@code{scanf}, or @code{strftime} type function for consistency with the
+@code{printf} style format string argument @code{my_format}.
 
 The parameter @var{string-index} specifies which argument is the format
 string argument (starting from 1).
 
 The @code{format-arg} attribute allows you to identify your own
 functions which modify format strings, so that GNU CC can check the
-calls to @code{printf} and @code{scanf} function whose operands are a
-call to one of your own function.  The compiler always treats
-@code{gettext}, @code{dgettext}, and @code{dcgettext} in this manner.
+calls to @code{printf}, @code{scanf}, or @code{strftime} function whose
+operands are a call to one of your own function.  The compiler always
+treats @code{gettext}, @code{dgettext}, and @code{dcgettext} in this
+manner.
 
 @item section ("section-name")
 @cindex @code{section} function attribute