OSDN Git Service

Fix mips64vr4100-elf build failure.
[pf3gnuchains/gcc-fork.git] / gcc / c-common.c
index f2715b3..9983cdb 100644 (file)
@@ -1,5 +1,5 @@
 /* Subroutines shared by all languages that are variants of C.
-   Copyright (C) 1992, 1993, 1994, 1995 Free Software Foundation, Inc.
+   Copyright (C) 1992, 93, 94, 95, 96, 97, 1998 Free Software Foundation, Inc.
 
 This file is part of GNU CC.
 
@@ -19,24 +19,139 @@ the Free Software Foundation, 59 Temple Place - Suite 330,
 Boston, MA 02111-1307, USA.  */
 
 #include "config.h"
+#include "system.h"
 #include "tree.h"
 #include "c-lex.h"
 #include "c-tree.h"
 #include "flags.h"
 #include "obstack.h"
-#include <stdio.h>
-#include <ctype.h>
+#include "toplev.h"
+#include "output.h"
+
+#if USE_CPPLIB
+#include "cpplib.h"
+cpp_reader  parse_in;
+cpp_options parse_options;
+static enum cpp_token cpp_token;
+#endif
+
+#ifndef WCHAR_TYPE_SIZE
+#ifdef INT_TYPE_SIZE
+#define WCHAR_TYPE_SIZE INT_TYPE_SIZE
+#else
+#define WCHAR_TYPE_SIZE        BITS_PER_WORD
+#endif
+#endif
 
 extern struct obstack permanent_obstack;
 
-enum attrs {A_PACKED, A_NOCOMMON, A_NORETURN, A_CONST, A_T_UNION,
+/* Nonzero means the expression being parsed will never be evaluated.
+   This is a count, since unevaluated expressions can nest.  */
+int skip_evaluation;
+
+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_WEAK, A_ALIAS};
+           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
+   statements seen up to the if keyword, as well as the line number
+   and file of the if.  If a potentially ambiguous else is seen, that
+   fact is recorded; the warning is issued when we can be sure that
+   the enclosing if statement does not have an else branch.  */
+typedef struct
+{
+  int compstmt_count;
+  int line;
+  char *file;
+  int needs_warning;
+} if_elt;
+
+static if_elt *if_stack;
+
+/* Amount of space in the if statement stack.  */
+static int if_stack_space = 0;
+
+/* Stack pointer.  */
+static int if_stack_pointer = 0;
+
+/* Generate RTL for the start of an if-then, and record the start of it
+   for ambiguous else detection.  */
+
+void
+c_expand_start_cond (cond, exitflag, compstmt_count)
+     tree cond;
+     int exitflag;
+     int compstmt_count;
+{
+  /* Make sure there is enough space on the stack.  */
+  if (if_stack_space == 0)
+    {
+      if_stack_space = 10;
+      if_stack = (if_elt *)xmalloc (10 * sizeof (if_elt));
+    }
+  else if (if_stack_space == if_stack_pointer)
+    {
+      if_stack_space += 10;
+      if_stack = (if_elt *)xrealloc (if_stack, if_stack_space * sizeof (if_elt));
+    }
+
+  /* Record this if statement.  */
+  if_stack[if_stack_pointer].compstmt_count = compstmt_count;
+  if_stack[if_stack_pointer].file = input_filename;
+  if_stack[if_stack_pointer].line = lineno;
+  if_stack[if_stack_pointer].needs_warning = 0;
+  if_stack_pointer++;
+
+  expand_start_cond (cond, exitflag);
+}
+
+/* Generate RTL for the end of an if-then.  Optionally warn if a nested
+   if statement had an ambiguous else clause.  */
+
+void
+c_expand_end_cond ()
+{
+  if_stack_pointer--;
+  if (if_stack[if_stack_pointer].needs_warning)
+    warning_with_file_and_line (if_stack[if_stack_pointer].file,
+                               if_stack[if_stack_pointer].line,
+                               "suggest explicit braces to avoid ambiguous `else'");
+  expand_end_cond ();
+}
+
+/* Generate RTL between the then-clause and the else-clause
+   of an if-then-else.  */
+
+void
+c_expand_start_else ()
+{
+  /* An ambiguous else warning must be generated for the enclosing if
+     statement, unless we see an else branch for that one, too.  */
+  if (warn_parentheses
+      && if_stack_pointer > 1
+      && (if_stack[if_stack_pointer - 1].compstmt_count
+         == if_stack[if_stack_pointer - 2].compstmt_count))
+    if_stack[if_stack_pointer - 2].needs_warning = 1;
+
+  /* Even if a nested if statement had an else branch, it can't be
+     ambiguous if this one also has an else.  So don't warn in that
+     case.  Also don't warn for any if statements nested in this else.  */
+  if_stack[if_stack_pointer - 1].needs_warning = 0;
+  if_stack[if_stack_pointer - 1].compstmt_count--;
+
+  expand_start_else ();
+}
 
 /* Make bindings for __FUNCTION__ and __PRETTY_FUNCTION__.  */
 
@@ -52,15 +167,12 @@ declare_function_name ()
     }
   else
     {
-      char *kind = "function";
-      if (TREE_CODE (TREE_TYPE (current_function_decl)) == METHOD_TYPE)
-       kind = "method";
       /* Allow functions to be nameless (such as artificial ones).  */
       if (DECL_NAME (current_function_decl))
         name = IDENTIFIER_POINTER (DECL_NAME (current_function_decl));
       else
        name = "";
-      printable_name = (*decl_printable_name) (current_function_decl, &kind);
+      printable_name = (*decl_printable_name) (current_function_decl, 2);
     }
 
   declare_hidden_char_array ("__FUNCTION__", name);
@@ -78,7 +190,7 @@ declare_hidden_char_array (name, value)
      or if we want to give warnings for large objects, make a bigger one.  */
   vlen = strlen (value) + 1;
   type = char_array_type_node;
-  if (TREE_INT_CST_LOW (TYPE_MAX_VALUE (TREE_TYPE (type))) < vlen
+  if (TREE_INT_CST_LOW (TYPE_MAX_VALUE (TYPE_DOMAIN (type))) < vlen
       || warn_larger_than)
     type = build_array_type (char_type_node,
                             build_index_type (build_int_2 (vlen, 0)));
@@ -157,7 +269,12 @@ combine_strings (strings)
            {
              int i;
              for (i = 0; i < len; i++)
-               ((int *) q)[i] = TREE_STRING_POINTER (t)[i];
+               {
+                 if (WCHAR_TYPE_SIZE == HOST_BITS_PER_SHORT)
+                   ((short *) q)[i] = TREE_STRING_POINTER (t)[i];
+                 else
+                   ((int *) q)[i] = TREE_STRING_POINTER (t)[i];
+               }
              q += len * wchar_bytes;
            }
        }
@@ -183,7 +300,7 @@ combine_strings (strings)
        wide_flag = 1;
     }
 
-  /* Compute the number of elements, for the array type.  */ 
+  /* Compute the number of elements, for the array type.  */
   nchars = wide_flag ? length / wchar_bytes : length;
 
   /* Create the array type for the string constant.
@@ -250,9 +367,10 @@ init_attributes ()
 {
   add_attribute (A_PACKED, "packed", 0, 0, 0);
   add_attribute (A_NOCOMMON, "nocommon", 0, 0, 1);
+  add_attribute (A_COMMON, "common", 0, 0, 1);
   add_attribute (A_NORETURN, "noreturn", 0, 0, 1);
   add_attribute (A_NORETURN, "volatile", 0, 0, 1);
-  add_attribute (A_UNUSED, "unused", 0, 0, 1);
+  add_attribute (A_UNUSED, "unused", 0, 0, 0);
   add_attribute (A_CONST, "const", 0, 0, 1);
   add_attribute (A_T_UNION, "transparent_union", 0, 0, 0);
   add_attribute (A_CONSTRUCTOR, "constructor", 0, 0, 1);
@@ -261,6 +379,7 @@ init_attributes ()
   add_attribute (A_SECTION, "section", 1, 1, 1);
   add_attribute (A_ALIGNED, "aligned", 0, 1, 0);
   add_attribute (A_FORMAT, "format", 3, 3, 1);
+  add_attribute (A_FORMAT_ARG, "format_arg", 1, 1, 1);
   add_attribute (A_WEAK, "weak", 0, 0, 1);
   add_attribute (A_ALIAS, "alias", 1, 1, 1);
 }
@@ -268,14 +387,14 @@ init_attributes ()
 /* Process the attributes listed in ATTRIBUTES and PREFIX_ATTRIBUTES
    and install them in NODE, which is either a DECL (including a TYPE_DECL)
    or a TYPE.  PREFIX_ATTRIBUTES can appear after the declaration specifiers
-   and declaration modifiers but before the declaration proper. */
+   and declaration modifiers but before the declaration proper.  */
 
 void
 decl_attributes (node, attributes, prefix_attributes)
      tree node, attributes, prefix_attributes;
 {
-  tree decl = 0, type;
-  int is_type;
+  tree decl = 0, type = 0;
+  int is_type = 0;
   tree a;
 
   if (attrtab_idx == 0)
@@ -298,7 +417,7 @@ decl_attributes (node, attributes, prefix_attributes)
       tree args = TREE_VALUE (a);
       int i;
       enum attrs id;
-      
+
       for (i = 0; i < attrtab_idx; i++)
        if (attrtab[i].name == name)
          break;
@@ -308,6 +427,8 @@ decl_attributes (node, attributes, prefix_attributes)
          if (! valid_machine_attribute (name, args, decl, type))
            warning ("`%s' attribute directive ignored",
                     IDENTIFIER_POINTER (name));
+         else if (decl != 0)
+           type = TREE_TYPE (decl);
          continue;
        }
       else if (attrtab[i].decl_req && decl == 0)
@@ -328,7 +449,7 @@ decl_attributes (node, attributes, prefix_attributes)
       switch (id)
        {
        case A_PACKED:
-         if (decl == 0)
+         if (is_type)
            TYPE_PACKED (type) = 1;
          else if (TREE_CODE (decl) == FIELD_DECL)
            DECL_PACKED (decl) = 1;
@@ -345,12 +466,19 @@ decl_attributes (node, attributes, prefix_attributes)
            warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name));
          break;
 
+       case A_COMMON:
+         if (TREE_CODE (decl) == VAR_DECL)
+           DECL_COMMON (decl) = 1;
+         else
+           warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name));
+         break;
+
        case A_NORETURN:
          if (TREE_CODE (decl) == FUNCTION_DECL)
            TREE_THIS_VOLATILE (decl) = 1;
          else if (TREE_CODE (type) == POINTER_TYPE
                   && TREE_CODE (TREE_TYPE (type)) == FUNCTION_TYPE)
-           TREE_TYPE (decl) = type 
+           TREE_TYPE (decl) = type
              = build_pointer_type
                (build_type_variant (TREE_TYPE (type),
                                     TREE_READONLY (TREE_TYPE (type)), 1));
@@ -359,8 +487,11 @@ decl_attributes (node, attributes, prefix_attributes)
          break;
 
        case A_UNUSED:
-         if (TREE_CODE (decl) == PARM_DECL || TREE_CODE (decl) == VAR_DECL
-             || TREE_CODE (decl) == FUNCTION_DECL)
+         if (is_type)
+           TREE_USED (type) = 1;
+         else if (TREE_CODE (decl) == PARM_DECL
+                  || TREE_CODE (decl) == VAR_DECL
+                  || TREE_CODE (decl) == FUNCTION_DECL)
            TREE_USED (decl) = 1;
          else
            warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name));
@@ -380,14 +511,16 @@ decl_attributes (node, attributes, prefix_attributes)
          break;
 
        case A_T_UNION:
-         if (decl != 0 && TREE_CODE (decl) == PARM_DECL
+         if (is_type
              && TREE_CODE (type) == UNION_TYPE
-             && TYPE_MODE (type) == DECL_MODE (TYPE_FIELDS (type)))
-           DECL_TRANSPARENT_UNION (decl) = 1;
-         else if (is_type
+             && (decl == 0
+                 || (TYPE_FIELDS (type) != 0
+                     && TYPE_MODE (type) == DECL_MODE (TYPE_FIELDS (type)))))
+           TYPE_TRANSPARENT_UNION (type) = 1;
+         else if (decl != 0 && TREE_CODE (decl) == PARM_DECL
                   && TREE_CODE (type) == UNION_TYPE
                   && TYPE_MODE (type) == DECL_MODE (TYPE_FIELDS (type)))
-           TYPE_TRANSPARENT_UNION (type) = 1;
+           DECL_TRANSPARENT_UNION (decl) = 1;
          else
            warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name));
          break;
@@ -470,8 +603,9 @@ decl_attributes (node, attributes, prefix_attributes)
               || TREE_CODE (decl) == VAR_DECL)
              && TREE_CODE (TREE_VALUE (args)) == STRING_CST)
            {
-             if (TREE_CODE (decl) == VAR_DECL 
-                 && current_function_decl != NULL_TREE)
+             if (TREE_CODE (decl) == VAR_DECL
+                 && current_function_decl != NULL_TREE
+                 && ! TREE_STATIC (decl))
                error_with_decl (decl,
                  "section attribute cannot be specified for local variables");
              /* The decl may have already been given a section attribute from
@@ -496,7 +630,8 @@ decl_attributes (node, attributes, prefix_attributes)
        case A_ALIGNED:
          {
            tree align_expr
-             = args ? TREE_VALUE (args) : size_int (BIGGEST_ALIGNMENT);
+             = (args ? TREE_VALUE (args)
+                : size_int (BIGGEST_ALIGNMENT / BITS_PER_UNIT));
            int align;
 
            /* Strip any NOPs of any kind.  */
@@ -504,7 +639,7 @@ decl_attributes (node, attributes, prefix_attributes)
                   || TREE_CODE (align_expr) == CONVERT_EXPR
                   || TREE_CODE (align_expr) == NON_LVALUE_EXPR)
              align_expr = TREE_OPERAND (align_expr, 0);
-         
+
            if (TREE_CODE (align_expr) != INTEGER_CST)
              {
                error ("requested alignment is not a constant");
@@ -528,16 +663,16 @@ 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;
-       
+
            if (TREE_CODE (decl) != FUNCTION_DECL)
              {
                error_with_decl (decl,
@@ -545,21 +680,28 @@ 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_id) != IDENTIFIER_NODE)
              {
-               error ("unrecognized format specifier for `%s'");
+               error ("unrecognized format specifier");
                continue;
              }
+           else
+             {
+               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
               and verify they are constants.  */
@@ -611,7 +753,7 @@ decl_attributes (node, attributes, prefix_attributes)
                if (first_arg_num != 0)
                  {
                    /* Verify that first_arg_num points to the last arg,
-                      the ... */
+                      the ...  */
                    while (argument)
                      arg_num++, argument = TREE_CHAIN (argument);
                  if (arg_num != first_arg_num)
@@ -624,7 +766,71 @@ 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;
+         }
+
+       case A_FORMAT_ARG:
+         {
+           tree format_num_expr = TREE_VALUE (args);
+           int format_num, arg_num;
+           tree argument;
+
+           if (TREE_CODE (decl) != FUNCTION_DECL)
+             {
+               error_with_decl (decl,
+                        "argument format specified for non-function `%s'");
+               continue;
+             }
+
+           /* Strip any conversions from the first arg number and verify it
+              is a constant.  */
+           while (TREE_CODE (format_num_expr) == NOP_EXPR
+                  || TREE_CODE (format_num_expr) == CONVERT_EXPR
+                  || TREE_CODE (format_num_expr) == NON_LVALUE_EXPR)
+             format_num_expr = TREE_OPERAND (format_num_expr, 0);
+
+           if (TREE_CODE (format_num_expr) != INTEGER_CST)
+             {
+               error ("format string has non-constant operand number");
+               continue;
+             }
+
+           format_num = TREE_INT_CST_LOW (format_num_expr);
+
+           /* If a parameter list is specified, verify that the format_num
+              argument is actually a string, in case the format attribute
+              is in error.  */
+           argument = TYPE_ARG_TYPES (type);
+           if (argument)
+             {
+               for (arg_num = 1; ; ++arg_num)
+                 {
+                   if (argument == 0 || arg_num == format_num)
+                     break;
+                   argument = TREE_CHAIN (argument);
+                 }
+               if (! argument
+                   || TREE_CODE (TREE_VALUE (argument)) != POINTER_TYPE
+                 || (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_VALUE (argument)))
+                     != char_type_node))
+                 {
+                   error ("format string arg not a string type");
+                   continue;
+                 }
+             }
+
+           if (TREE_CODE (TREE_TYPE (TREE_TYPE (decl))) != POINTER_TYPE
+               || (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (TREE_TYPE (decl))))
+                   != char_type_node))
+             {
+               error ("function does not return string type");
+               continue;
+             }
+
+           record_international_format (DECL_NAME (decl),
+                                        DECL_ASSEMBLER_NAME (decl),
+                                        format_num);
            break;
          }
 
@@ -634,7 +840,7 @@ decl_attributes (node, attributes, prefix_attributes)
 
        case A_ALIAS:
          if ((TREE_CODE (decl) == FUNCTION_DECL && DECL_INITIAL (decl))
-             || TREE_CODE (decl) != FUNCTION_DECL && ! DECL_EXTERNAL (decl))
+             || (TREE_CODE (decl) != FUNCTION_DECL && ! DECL_EXTERNAL (decl)))
            error_with_decl (decl,
                             "`%s' defined both normally and as an alias");
          else if (decl_function_context (decl) == 0)
@@ -653,6 +859,100 @@ decl_attributes (node, attributes, prefix_attributes)
        }
     }
 }
+
+/* Split SPECS_ATTRS, a list of declspecs and prefix attributes, into two
+   lists.  SPECS_ATTRS may also be just a typespec (eg: RECORD_TYPE).
+
+   The head of the declspec list is stored in DECLSPECS.
+   The head of the attribute list is stored in PREFIX_ATTRIBUTES.
+
+   Note that attributes in SPECS_ATTRS are stored in the TREE_PURPOSE of
+   the list elements.  We drop the containing TREE_LIST nodes and link the
+   resulting attributes together the way decl_attributes expects them.  */
+
+void
+split_specs_attrs (specs_attrs, declspecs, prefix_attributes)
+     tree specs_attrs;
+     tree *declspecs, *prefix_attributes;
+{
+  tree t, s, a, next, specs, attrs;
+
+  /* This can happen in c++ (eg: decl: typespec initdecls ';').  */
+  if (specs_attrs != NULL_TREE
+      && TREE_CODE (specs_attrs) != TREE_LIST)
+    {
+      *declspecs = specs_attrs;
+      *prefix_attributes = NULL_TREE;
+      return;
+    }
+
+  /* Remember to keep the lists in the same order, element-wise.  */
+
+  specs = s = NULL_TREE;
+  attrs = a = NULL_TREE;
+  for (t = specs_attrs; t; t = next)
+    {
+      next = TREE_CHAIN (t);
+      /* Declspecs have a non-NULL TREE_VALUE.  */
+      if (TREE_VALUE (t) != NULL_TREE)
+       {
+         if (specs == NULL_TREE)
+           specs = s = t;
+         else
+           {
+             TREE_CHAIN (s) = t;
+             s = t;
+           }
+       }
+      else
+       {
+         if (attrs == NULL_TREE)
+           attrs = a = TREE_PURPOSE (t);
+         else
+           {
+             TREE_CHAIN (a) = TREE_PURPOSE (t);
+             a = TREE_PURPOSE (t);
+           }
+         /* More attrs can be linked here, move A to the end.  */
+         while (TREE_CHAIN (a) != NULL_TREE)
+           a = TREE_CHAIN (a);
+       }
+    }
+
+  /* Terminate the lists.  */
+  if (s != NULL_TREE)
+    TREE_CHAIN (s) = NULL_TREE;
+  if (a != NULL_TREE)
+    TREE_CHAIN (a) = NULL_TREE;
+
+  /* All done.  */
+  *declspecs = specs;
+  *prefix_attributes = attrs;
+}
+
+/* Strip attributes from SPECS_ATTRS, a list of declspecs and attributes.
+   This function is used by the parser when a rule will accept attributes
+   in a particular position, but we don't want to support that just yet.
+
+   A warning is issued for every ignored attribute.  */
+
+tree
+strip_attrs (specs_attrs)
+     tree specs_attrs;
+{
+  tree specs, attrs;
+
+  split_specs_attrs (specs_attrs, &specs, &attrs);
+
+  while (attrs)
+    {
+      warning ("`%s' attribute ignored",
+              IDENTIFIER_POINTER (TREE_PURPOSE (attrs)));
+      attrs = TREE_CHAIN (attrs);
+    }
+
+  return specs;
+}
 \f
 /* Check a printf/fprintf/sprintf/scanf/fscanf/sscanf format against
    a parameter list.  */
@@ -669,6 +969,7 @@ decl_attributes (node, attributes, prefix_attributes)
 #define T_D    &double_type_node
 #define T_LD   &long_double_type_node
 #define T_C    &char_type_node
+#define T_UC   &unsigned_char_type_node
 #define T_V    &void_type_node
 #define T_W    &wchar_type_node
 #define T_ST    &sizetype
@@ -678,6 +979,9 @@ typedef struct {
   int pointer_count;
   /* Type of argument if no length modifier is used.  */
   tree *nolen;
+  /* Type of argument if length modifier for shortening to byte is used.
+     If NULL, then this modifier is not allowed.  */
+  tree *hhlen;
   /* Type of argument if length modifier for shortening is used.
      If NULL, then this modifier is not allowed.  */
   tree *hlen;
@@ -690,89 +994,146 @@ typedef struct {
   /* Type of argument if length modifier `L' is used.
      If NULL, then this modifier is not allowed.  */
   tree *bigllen;
+  /* Type of argument if length modifier `Z' is used.
+     If NULL, then this modifier is not allowed.  */
+  tree *zlen;
   /* List of other modifier characters allowed with these options.  */
   char *flag_chars;
 } format_char_info;
 
 static format_char_info print_char_table[] = {
-  { "di",      0,      T_I,    T_I,    T_L,    T_LL,   T_LL,   "-wp0 +"        },
-  { "oxX",     0,      T_UI,   T_UI,   T_UL,   T_ULL,  T_ULL,  "-wp0#"         },
-  { "u",       0,      T_UI,   T_UI,   T_UL,   T_ULL,  T_ULL,  "-wp0"          },
-/* Two GNU extensions.  */
-  { "Z",       0,      T_ST,   NULL,   NULL,   NULL,   NULL,   "-wp0"          },
-  { "m",       0,      T_V,    NULL,   NULL,   NULL,   NULL,   "-wp"           },
-  { "feEgG",   0,      T_D,    NULL,   NULL,   NULL,   T_LD,   "-wp0 +#"       },
-  { "c",       0,      T_I,    NULL,   T_W,    NULL,   NULL,   "-w"            },
-  { "C",       0,      T_W,    NULL,   NULL,   NULL,   NULL,   "-w"            },
-  { "s",       1,      T_C,    NULL,   T_W,    NULL,   NULL,   "-wp"           },
-  { "S",       1,      T_W,    NULL,   NULL,   NULL,   NULL,   "-wp"           },
-  { "p",       1,      T_V,    NULL,   NULL,   NULL,   NULL,   "-w"            },
-  { "n",       1,      T_I,    T_S,    T_L,    T_LL,   NULL,   ""              },
+  { "di",      0,      T_I,    T_I,    T_I,    T_L,    T_LL,   T_LL,   T_ST,   "-wp0 +"        },
+  { "oxX",     0,      T_UI,   T_UI,   T_UI,   T_UL,   T_ULL,  T_ULL,  T_ST,   "-wp0#"         },
+  { "u",       0,      T_UI,   T_UI,   T_UI,   T_UL,   T_ULL,  T_ULL,  T_ST,   "-wp0"          },
+/* A GNU extension.  */
+  { "m",       0,      T_V,    NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   "-wp"           },
+  { "feEgGaA", 0,      T_D,    NULL,   NULL,   NULL,   NULL,   T_LD,   NULL,   "-wp0 +#"       },
+  { "c",       0,      T_I,    NULL,   NULL,   T_W,    NULL,   NULL,   NULL,   "-w"            },
+  { "C",       0,      T_W,    NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   "-w"            },
+  { "s",       1,      T_C,    NULL,   NULL,   T_W,    NULL,   NULL,   NULL,   "-wp"           },
+  { "S",       1,      T_W,    NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   "-wp"           },
+  { "p",       1,      T_V,    NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   "-w"            },
+  { "n",       1,      T_I,    NULL,   T_S,    T_L,    T_LL,   NULL,   NULL,   ""              },
   { NULL }
 };
 
 static format_char_info scan_char_table[] = {
-  { "di",      1,      T_I,    T_S,    T_L,    T_LL,   T_LL,   "*"     },
-  { "ouxX",    1,      T_UI,   T_US,   T_UL,   T_ULL,  T_ULL,  "*"     },      
-  { "efgEG",   1,      T_F,    NULL,   T_D,    NULL,   T_LD,   "*"     },
-  { "sc",      1,      T_C,    NULL,   T_W,    NULL,   NULL,   "*a"    },
-  { "[",       1,      T_C,    NULL,   NULL,   NULL,   NULL,   "*a"    },
-  { "C",       1,      T_W,    NULL,   NULL,   NULL,   NULL,   "*"     },
-  { "S",       1,      T_W,    NULL,   NULL,   NULL,   NULL,   "*"     },
-  { "p",       2,      T_V,    NULL,   NULL,   NULL,   NULL,   "*"     },
-  { "n",       1,      T_I,    T_S,    T_L,    T_LL,   NULL,   ""      },
+  { "di",      1,      T_I,    T_C,    T_S,    T_L,    T_LL,   T_LL,   NULL,   "*"     },
+  { "ouxX",    1,      T_UI,   T_UC,   T_US,   T_UL,   T_ULL,  T_ULL,  NULL,   "*"     },
+  { "efgEGaA", 1,      T_F,    NULL,   NULL,   T_D,    NULL,   T_LD,   NULL,   "*"     },
+  { "c",       1,      T_C,    NULL,   NULL,   T_W,    NULL,   NULL,   NULL,   "*"     },
+  { "s",       1,      T_C,    NULL,   NULL,   T_W,    NULL,   NULL,   NULL,   "*a"    },
+  { "[",       1,      T_C,    NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   "*a"    },
+  { "C",       1,      T_W,    NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   "*"     },
+  { "S",       1,      T_W,    NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   "*a"    },
+  { "p",       2,      T_V,    NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   "*"     },
+  { "n",       1,      T_I,    T_C,    T_S,    T_L,    T_LL,   NULL,   NULL,   ""      },
+  { 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, NULL, "2EO-_0w" },
+  { "D",               0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "2" },
+  { "g",               0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "2O-_0w" },
+  { "cx",              0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "3E" },
+  { "%RTXnrt",         0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "" },
+  { "P",               0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "G" },
+  { "HIMSUWdemw",      0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "-_0Ow" },
+  { "Vju",             0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "-_0Oow" },
+  { "Gklsz",           0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "-_0OGw" },
+  { "ABZa",            0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "^#" },
+  { "p",               0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "#" },
+  { "bh",              0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "^" },
+  { "CY",              0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "-_0EOw" },
   { NULL }
 };
 
-typedef struct function_format_info {
+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;
 
 static function_format_info *function_format_list = NULL;
 
-static void check_format_info PROTO((function_format_info *, tree));
+typedef struct international_format_info
+{
+  struct international_format_info *next;  /* next structure on the list */
+  tree name;                   /* identifier such as "gettext" */
+  tree assembler_name;         /* optional mangled identifier (for C++) */
+  int format_num;              /* number of format argument */
+} international_format_info;
+
+static international_format_info *international_format_list = NULL;
+
+static void check_format_info          PROTO((function_format_info *, tree));
 
 /* Initialize the table of functions to perform format checking on.
    The ANSI functions are always checked (whether <stdio.h> is
    included or not), since it is common to call printf without
    including <stdio.h>.  There shouldn't be a problem with this,
    since ANSI reserves these function names whether you include the
-   header file or not.  In any case, the checking is harmless.  */
+   header file or not.  In any case, the checking is harmless.
+
+   Also initialize the name of function that modify the format string for
+   internationalization purposes.  */
 
 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);
+  record_international_format (get_identifier ("dcgettext"), NULL_TREE, 2);
 }
 
 /* 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 teh format string, or zero if no checking is not be done
+   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;
 {
@@ -795,11 +1156,48 @@ 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;
 }
 
+/* Record information for the names of function that modify the format
+   argument to format functions.  FUNCTION_IDENT is the identifier node for
+   the name of the function (its decl need not exist yet) and FORMAT_NUM is
+   the number of the argument which is the format control string (starting
+   from 1).  */
+
+static void
+record_international_format (name, assembler_name, format_num)
+      tree name;
+      tree assembler_name;
+      int format_num;
+{
+  international_format_info *info;
+
+  /* Re-use existing structure if it's there.  */
+
+  for (info = international_format_list; info; info = info->next)
+    {
+      if (info->name == name && info->assembler_name == assembler_name)
+       break;
+    }
+
+  if (! info)
+    {
+      info
+       = (international_format_info *)
+         xmalloc (sizeof (international_format_info));
+      info->next = international_format_list;
+      international_format_list = info;
+
+      info->name = name;
+      info->assembler_name = assembler_name;
+    }
+
+  info->format_num = format_num;
+}
+
 static char    tfaff[] = "too few arguments for format";
 \f
 /* Check the argument list of a call to printf, scanf, etc.
@@ -852,7 +1250,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;
 
@@ -870,10 +1267,44 @@ check_format_info (info, params)
   params = TREE_CHAIN (params);
   if (format_tree == 0)
     return;
+
   /* We can only check the format if it's a string constant.  */
   while (TREE_CODE (format_tree) == NOP_EXPR)
     format_tree = TREE_OPERAND (format_tree, 0); /* strip coercion */
-  if (format_tree == null_pointer_node)
+
+  if (TREE_CODE (format_tree) == CALL_EXPR
+      && TREE_CODE (TREE_OPERAND (format_tree, 0)) == ADDR_EXPR
+      && (TREE_CODE (TREE_OPERAND (TREE_OPERAND (format_tree, 0), 0))
+         == FUNCTION_DECL))
+    {
+      tree function = TREE_OPERAND (TREE_OPERAND (format_tree, 0), 0);
+
+      /* See if this is a call to a known internationalization function
+        that modifies the format arg.  */
+      international_format_info *info;
+
+      for (info = international_format_list; info; info = info->next)
+       if (info->assembler_name
+           ? (info->assembler_name == DECL_ASSEMBLER_NAME (function))
+           : (info->name == DECL_NAME (function)))
+         {
+           tree inner_args;
+           int i;
+
+           for (inner_args = TREE_OPERAND (format_tree, 1), i = 1;
+                inner_args != 0;
+                inner_args = TREE_CHAIN (inner_args), i++)
+             if (i == info->format_num)
+               {
+                 format_tree = TREE_VALUE (inner_args);
+
+                 while (TREE_CODE (format_tree) == NOP_EXPR)
+                   format_tree = TREE_OPERAND (format_tree, 0);
+               }
+         }
+    }
+
+  if (integer_zerop (format_tree))
     {
       warning ("null format string");
       return;
@@ -927,15 +1358,55 @@ 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)
            ++format_chars;
-         while (isdigit (*format_chars))
+         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.  */
@@ -968,16 +1439,15 @@ check_format_info (info, params)
          while (*format_chars != 0 && index (" +#0-", *format_chars) != 0)
            {
              if (index (flag_chars, *format_chars) != 0)
+               warning ("repeated `%c' flag in format", *format_chars++);
+             else
                {
-                 sprintf (message, "repeated `%c' flag in format",
-                          *format_chars);
-                 warning (message);
+                 i = strlen (flag_chars);
+                 flag_chars[i++] = *format_chars++;
+                 flag_chars[i] = 0;
                }
-             i = strlen (flag_chars);
-             flag_chars[i++] = *format_chars++;
-             flag_chars[i] = 0;
            }
-         /* "If the space and + flags both appear, 
+         /* "If the space and + flags both appear,
             the space flag will be ignored."  */
          if (index (flag_chars, ' ') != 0
              && index (flag_chars, '+') != 0)
@@ -1012,17 +1482,12 @@ 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
            {
-             while (isdigit (*format_chars))
+             while (ISDIGIT (*format_chars))
                {
                  wide = TRUE;
                  ++format_chars;
@@ -1032,7 +1497,7 @@ check_format_info (info, params)
            {
              precise = TRUE;
              ++format_chars;
-             if (*format_chars != '*' && !isdigit (*format_chars))
+             if (*format_chars != '*' && !ISDIGIT (*format_chars))
                warning ("`.' not followed by `*' or digit in format");
              /* "...a...precision...may be indicated by an asterisk.
                 In this case, an int argument supplies the...precision."  */
@@ -1051,84 +1516,137 @@ 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
                {
-                 while (isdigit (*format_chars))
+                 while (ISDIGIT (*format_chars))
                    ++format_chars;
                }
            }
        }
-      if (*format_chars == 'h' || *format_chars == 'l' || *format_chars == 'q' ||
-         *format_chars == 'L')
-       length_char = *format_chars++;
-      else
-       length_char = 0;
-      if (length_char == 'l' && *format_chars == 'l')
-       length_char = 'q', format_chars++;
+
       aflag = 0;
-      if (*format_chars == 'a')
-       {
-         aflag = 1;
-         format_chars++;
-       }
-      if (suppressed && length_char != 0)
+
+      if (info->format_type != strftime_format_type)
        {
-         sprintf (message,
-                  "use of `*' and `%c' together in format",
-                  length_char);
-         warning (message);
+         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)
+               warning ("ANSI C does not support the `%c' length modifier",
+                        length_char);
+           }
+         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++;
+             /* FIXME: Is allowed in ISO C 9x.  */
+             if (pedantic)
+               warning ("ANSI C does not support the `ll' length modifier");
+           }
+         else if (length_char == 'h' && *format_chars == 'h')
+           {
+             length_char = 'H', format_chars++;
+             /* FIXME: Is allowed in ISO C 9x.  */
+             if (pedantic)
+               warning ("ANSI C does not support the `hh' 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)
+      if (format_char == 0
+         || (info->format_type != strftime_format_type && format_char == '%'))
        {
          warning ("conversion lacks type at end of format");
          continue;
        }
+      /* The m, C, and S formats are GNU extensions.  */
+      if (pedantic && info->format_type != strftime_format_type
+         && (format_char == 'm' || format_char == 'C' || format_char == 'S'))
+       warning ("ANSI C does not support the `%c' format", format_char);
+      /* ??? The a and A formats are C9X extensions, and should be allowed
+        when a C9X option is added.  */
+      if (pedantic && info->format_type != strftime_format_type
+         && (format_char == 'a' || format_char == 'A'))
+       warning ("ANSI C does not support the `%c' format", format_char);
       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 == '[')
+      /* The a flag is a GNU extension.  */
+      else if (pedantic && aflag)
+       warning ("ANSI C does not support the `a' flag");
+      if (info->format_type == scanf_format_type && format_char == '[')
        {
          /* Skip over scan set, in case it happens to have '%' in it.  */
          if (*format_chars == '^')
@@ -1140,63 +1658,42 @@ 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;
       if (precise && index (flag_chars, '0') != 0
          && (format_char == 'd' || format_char == 'i'
              || format_char == 'o' || format_char == 'u'
              || format_char == 'x' || format_char == 'x'))
-       {
-         sprintf (message,
-                  "precision and `0' flag not both allowed with `%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;
+       case 'H': wanted_type = fci->hhlen ? *(fci->hhlen) : 0; break;
        case 'h': wanted_type = fci->hlen ? *(fci->hlen) : 0; break;
        case 'l': wanted_type = fci->llen ? *(fci->llen) : 0; break;
        case 'q': wanted_type = fci->qlen ? *(fci->qlen) : 0; break;
        case 'L': wanted_type = fci->bigllen ? *(fci->bigllen) : 0; break;
+       case 'Z': wanted_type = fci->zlen ? *fci->zlen : 0; break;
        }
       if (wanted_type == 0)
-       {
-         sprintf (message,
-                  "use of `%c' length character with `%c' type character",
-                  length_char, format_char);
-         warning (message);
-       }
-
-      /*
-       ** XXX -- should kvetch about stuff such as
-       **      {
-       **              const int       i;
-       **
-       **              scanf ("%d", &i);
-       **      }
-       */
+       warning ("use of `%c' length character with `%c' type character",
+                length_char, format_char);
 
       /* Finally. . .check type of argument against desired type!  */
       if (info->first_arg_num == 0)
@@ -1214,25 +1711,49 @@ check_format_info (info, params)
       ++arg_num;
       cur_type = TREE_TYPE (cur_param);
 
+      STRIP_NOPS (cur_param);
+
       /* Check the types of any additional pointer arguments
         that precede the "real" argument.  */
-      for (i = 0; i < fci->pointer_count; ++i)
+      for (i = 0; i < fci->pointer_count + aflag; ++i)
        {
          if (TREE_CODE (cur_type) == POINTER_TYPE)
            {
              cur_type = TREE_TYPE (cur_type);
+
+             if (cur_param != 0 && TREE_CODE (cur_param) == ADDR_EXPR)
+               cur_param = TREE_OPERAND (cur_param, 0);
+             else
+               cur_param = 0;
+
              continue;
            }
-         sprintf (message,
-                  "format argument is not a %s (arg %d)",
-                  ((fci->pointer_count == 1) ? "pointer" : "pointer to a pointer"),
-                  arg_num);
-         warning (message);
+         if (TREE_CODE (cur_type) != ERROR_MARK)
+           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 or with printf "%n".  */
+      if ((info->format_type == scanf_format_type
+          || (info->format_type == printf_format_type
+              && format_char == 'n'))
+         && i == fci->pointer_count + aflag
+         && wanted_type != 0
+         && TREE_CODE (cur_type) != ERROR_MARK
+         && (TYPE_READONLY (cur_type)
+             || (cur_param != 0
+                 && (TREE_CODE_CLASS (TREE_CODE (cur_param)) == 'c'
+                     || (TREE_CODE_CLASS (TREE_CODE (cur_param)) == 'd'
+                         && TREE_READONLY (cur_param))))))
+       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 && wanted_type != 0
+      if (i == fci->pointer_count + aflag && wanted_type != 0
+         && TREE_CODE (cur_type) != ERROR_MARK
          && wanted_type != TYPE_MAIN_VARIANT (cur_type)
          /* If we want `void *', allow any pointer type.
             (Anything else would already have got a warning.)  */
@@ -1252,7 +1773,7 @@ check_format_info (info, params)
        {
          register char *this;
          register char *that;
-  
+
          this = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (wanted_type)));
          that = 0;
          if (TREE_CODE (cur_type) != ERROR_MARK
@@ -1288,11 +1809,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);
        }
     }
 }
@@ -1330,7 +1847,8 @@ overflow_warning (value)
       && TREE_OVERFLOW (value))
     {
       TREE_OVERFLOW (value) = 0;
-      warning ("integer overflow in expression");
+      if (skip_evaluation == 0)
+       warning ("integer overflow in expression");
     }
   else if ((TREE_CODE (value) == REAL_CST
            || (TREE_CODE (value) == COMPLEX_CST
@@ -1338,7 +1856,8 @@ overflow_warning (value)
           && TREE_OVERFLOW (value))
     {
       TREE_OVERFLOW (value) = 0;
-      warning ("floating-pointer overflow in expression");
+      if (skip_evaluation == 0)
+       warning ("floating point overflow in expression");
     }
 }
 
@@ -1354,6 +1873,7 @@ unsigned_conversion_warning (result, operand)
   if (TREE_CODE (operand) == INTEGER_CST
       && TREE_CODE (TREE_TYPE (result)) == INTEGER_TYPE
       && TREE_UNSIGNED (TREE_TYPE (result))
+      && skip_evaluation == 0
       && !int_fits_type_p (operand, TREE_TYPE (result)))
     {
       if (!int_fits_type_p (operand, signed_type (TREE_TYPE (result))))
@@ -1389,9 +1909,10 @@ convert_and_check (type, expr)
                && TYPE_PRECISION (type) == TYPE_PRECISION (TREE_TYPE (expr))))
            /* If EXPR fits in the unsigned version of TYPE,
               don't warn unless pedantic.  */
-           if (pedantic
-               || TREE_UNSIGNED (type)
-               || ! int_fits_type_p (expr, unsigned_type (type)))
+           if ((pedantic
+                || TREE_UNSIGNED (type)
+                || ! int_fits_type_p (expr, unsigned_type (type)))
+               && skip_evaluation == 0)
              warning ("overflow in implicit constant conversion");
        }
       else
@@ -1520,6 +2041,9 @@ type_for_mode (mode, unsignedp)
   if (mode == TYPE_MODE (intDI_type_node))
     return unsignedp ? unsigned_intDI_type_node : intDI_type_node;
 
+  if (mode == TYPE_MODE (intTI_type_node))
+    return unsignedp ? unsigned_intTI_type_node : intTI_type_node;
+
   if (mode == TYPE_MODE (float_type_node))
     return float_type_node;
 
@@ -1576,7 +2100,7 @@ void
 binary_op_error (code)
      enum tree_code code;
 {
-  register char *opname = "unknown";
+  register char *opname;
 
   switch (code)
     {
@@ -1629,6 +2153,8 @@ binary_op_error (code)
     case LROTATE_EXPR:
     case RROTATE_EXPR:
       opname = "rotate"; break;
+    default:
+      opname = "unknown"; break;
     }
   error ("invalid operands to binary %s", opname);
 }
@@ -1715,6 +2241,8 @@ shorten_compare (op0_ptr, op1_ptr, restype_ptr, rescode_ptr)
        case GE_EXPR:
          code = LE_EXPR;
          break;
+       default:
+         break;
        }
       *rescode_ptr = code;
     }
@@ -1845,6 +2373,9 @@ shorten_compare (op0_ptr, op1_ptr, restype_ptr, rescode_ptr)
                primop1 = TYPE_MAX_VALUE (type);
                val = 0;
                break;
+
+             default:
+               break;
              }
          type = unsigned_type (type);
        }
@@ -1942,6 +2473,10 @@ shorten_compare (op0_ptr, op1_ptr, restype_ptr, rescode_ptr)
                                                     primop0))))
                warning ("unsigned value < 0 is always 0");
              value = boolean_false_node;
+             break;
+
+           default:
+             break;
            }
 
          if (value != 0)
@@ -2043,6 +2578,12 @@ truthvalue_conversion (expr)
       return real_zerop (expr) ? boolean_false_node : boolean_true_node;
 
     case ADDR_EXPR:
+      /* If we are taking the address of a external decl, it might be zero
+        if it is weak, so we cannot optimize.  */
+      if (TREE_CODE_CLASS (TREE_CODE (TREE_OPERAND (expr, 0))) == 'd'
+         && DECL_EXTERNAL (TREE_OPERAND (expr, 0)))
+       break;
+
       if (TREE_SIDE_EFFECTS (TREE_OPERAND (expr, 0)))
        return build (COMPOUND_EXPR, boolean_type_node,
                      TREE_OPERAND (expr, 0), boolean_true_node);
@@ -2072,7 +2613,7 @@ truthvalue_conversion (expr)
                      truthvalue_conversion (TREE_OPERAND (expr, 0)));
       else
        return truthvalue_conversion (TREE_OPERAND (expr, 0));
-      
+
     case COND_EXPR:
       /* Distribute the conversion into the arms of a COND_EXPR.  */
       return fold (build (COND_EXPR, boolean_type_node, TREE_OPERAND (expr, 0),
@@ -2085,7 +2626,7 @@ truthvalue_conversion (expr)
       if (TREE_CODE (TREE_TYPE (expr)) == REFERENCE_TYPE
          || TREE_CODE (TREE_TYPE (TREE_OPERAND (expr, 0))) == REFERENCE_TYPE)
        break;
-      /* fall through... */
+      /* fall through...  */
     case NOP_EXPR:
       /* If this is widening the argument, we can ignore it.  */
       if (TYPE_PRECISION (TREE_TYPE (expr))
@@ -2099,7 +2640,7 @@ truthvalue_conversion (expr)
       if (TARGET_FLOAT_FORMAT == IEEE_FLOAT_FORMAT
          && TREE_CODE (TREE_TYPE (expr)) == REAL_TYPE)
        break;
-      /* fall through... */
+      /* fall through...  */
     case BIT_XOR_EXPR:
       /* This and MINUS_EXPR can be changed into a comparison of the
         two objects.  */
@@ -2123,6 +2664,9 @@ truthvalue_conversion (expr)
       if (warn_parentheses && C_EXP_ORIGINAL_CODE (expr) == MODIFY_EXPR)
        warning ("suggest parentheses around assignment used as truth value");
       break;
+
+    default:
+      break;
     }
 
   if (TREE_CODE (TREE_TYPE (expr)) == COMPLEX_TYPE)
@@ -2136,6 +2680,7 @@ truthvalue_conversion (expr)
   return build_binary_op (NE_EXPR, expr, integer_zero_node, 1);
 }
 \f
+#if USE_CPPLIB
 /* Read the rest of a #-directive from input stream FINPUT.
    In normal use, the directive name and the white space after it
    have already been read, so they won't be included in the result.
@@ -2145,6 +2690,107 @@ truthvalue_conversion (expr)
 
    The value is a string in a reusable buffer.  It remains valid
    only until the next time this function is called.  */
+unsigned char *yy_cur, *yy_lim;
+
+#define GETC() (yy_cur < yy_lim ? *yy_cur++ : yy_get_token ())
+#define UNGETC(c) ((c), yy_cur--)
+
+int
+yy_get_token ()
+{
+  for (;;)
+    {
+      parse_in.limit = parse_in.token_buffer;
+      cpp_token = cpp_get_token (&parse_in);
+      if (cpp_token == CPP_EOF)
+       return -1;
+      yy_lim = CPP_PWRITTEN (&parse_in);
+      yy_cur = parse_in.token_buffer;
+      if (yy_cur < yy_lim)
+       return *yy_cur++;
+    }
+}
+
+char *
+get_directive_line ()
+{
+  static char *directive_buffer = NULL;
+  static unsigned buffer_length = 0;
+  register char *p;
+  register char *buffer_limit;
+  register int looking_for = 0;
+  register int char_escaped = 0;
+
+  if (buffer_length == 0)
+    {
+      directive_buffer = (char *)xmalloc (128);
+      buffer_length = 128;
+    }
+
+  buffer_limit = &directive_buffer[buffer_length];
+
+  for (p = directive_buffer; ; )
+    {
+      int c;
+
+      /* Make buffer bigger if it is full.  */
+      if (p >= buffer_limit)
+        {
+         register unsigned bytes_used = (p - directive_buffer);
+
+         buffer_length *= 2;
+         directive_buffer
+           = (char *)xrealloc (directive_buffer, buffer_length);
+         p = &directive_buffer[bytes_used];
+         buffer_limit = &directive_buffer[buffer_length];
+        }
+
+      c = GETC ();
+
+      /* Discard initial whitespace.  */
+      if ((c == ' ' || c == '\t') && p == directive_buffer)
+       continue;
+
+      /* Detect the end of the directive.  */
+      if (c == '\n' && looking_for == 0)
+       {
+          UNGETC (c);
+         c = '\0';
+       }
+
+      *p++ = c;
+
+      if (c == 0)
+       return directive_buffer;
+
+      /* Handle string and character constant syntax.  */
+      if (looking_for)
+       {
+         if (looking_for == c && !char_escaped)
+           looking_for = 0;    /* Found terminator... stop looking.  */
+       }
+      else
+        if (c == '\'' || c == '"')
+         looking_for = c;      /* Don't stop buffering until we see another
+                                  another one of these (or an EOF).  */
+
+      /* Handle backslash.  */
+      char_escaped = (c == '\\' && ! char_escaped);
+    }
+}
+#else
+/* Read the rest of a #-directive from input stream FINPUT.
+   In normal use, the directive name and the white space after it
+   have already been read, so they won't be included in the result.
+   We allow for the fact that the directive line may contain
+   a newline embedded within a character or string literal which forms
+   a part of the directive.
+
+   The value is a string in a reusable buffer.  It remains valid
+   only until the next time this function is called.
+
+   The terminating character ('\n' or EOF) is left in FINPUT for the
+   caller to re-read.  */
 
 char *
 get_directive_line (finput)
@@ -2188,7 +2834,8 @@ get_directive_line (finput)
        continue;
 
       /* Detect the end of the directive.  */
-      if (c == '\n' && looking_for == 0)
+      if (looking_for == 0
+         && (c == '\n' || c == EOF))
        {
           ungetc (c, finput);
          c = '\0';
@@ -2208,12 +2855,13 @@ get_directive_line (finput)
       else
         if (c == '\'' || c == '"')
          looking_for = c;      /* Don't stop buffering until we see another
-                                  another one of these (or an EOF).  */
+                                  one of these (or an EOF).  */
 
       /* Handle backslash.  */
       char_escaped = (c == '\\' && ! char_escaped);
     }
 }
+#endif /* !USE_CPPLIB */
 \f
 /* Make a variant type in the proper way for C/C++, propagating qualifiers
    down to the element type of an array.  */
@@ -2224,27 +2872,8 @@ c_build_type_variant (type, constp, volatilep)
      int constp, volatilep;
 {
   if (TREE_CODE (type) == ARRAY_TYPE)
-    {
-      tree real_main_variant = TYPE_MAIN_VARIANT (type);
-
-      push_obstacks (TYPE_OBSTACK (real_main_variant),
-                    TYPE_OBSTACK (real_main_variant));
-      type = build_array_type (c_build_type_variant (TREE_TYPE (type),
-                                                    constp, volatilep),
-                              TYPE_DOMAIN (type));
-
-      /* TYPE must be on same obstack as REAL_MAIN_VARIANT.  If not,
-        make a copy.  (TYPE might have come from the hash table and
-        REAL_MAIN_VARIANT might be in some function's obstack.)  */
-
-      if (TYPE_OBSTACK (type) != TYPE_OBSTACK (real_main_variant))
-       {
-         type = copy_node (type);
-         TYPE_POINTER_TO (type) = TYPE_REFERENCE_TO (type) = 0;
-       }
-
-      TYPE_MAIN_VARIANT (type) = real_main_variant;
-      pop_obstacks ();
-    }
+    return build_array_type (c_build_type_variant (TREE_TYPE (type),
+                                                  constp, volatilep),
+                            TYPE_DOMAIN (type));
   return build_type_variant (type, constp, volatilep);
 }