OSDN Git Service

* tree-loop-distribution.c (distribute_loop): Fix declaration and
[pf3gnuchains/gcc-fork.git] / gcc / c-format.c
index 17f878c..3d46bd2 100644 (file)
@@ -1,12 +1,12 @@
 /* Check calls to formatted I/O functions (-Wformat).
    Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
-   2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+   2001, 2002, 2003, 2004, 2005, 2007, 2008 Free Software Foundation, Inc.
 
 This file is part of GCC.
 
 GCC is free software; you can redistribute it and/or modify it under
 the terms of the GNU General Public License as published by the Free
-Software Foundation; either version 2, or (at your option) any later
+Software Foundation; either version 3, or (at your option) any later
 version.
 
 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
@@ -15,9 +15,8 @@ FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 for more details.
 
 You should have received a copy of the GNU General Public License
-along with GCC; see the file COPYING.  If not, write to the Free
-Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
-02110-1301, USA.  */
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
 
 #include "config.h"
 #include "system.h"
@@ -31,6 +30,7 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
 #include "diagnostic.h"
 #include "langhooks.h"
 #include "c-format.h"
+#include "alloc-pool.h"
 \f
 /* Set format warning options according to a -Wformat=n option.  */
 
@@ -62,8 +62,7 @@ enum format_type { printf_format_type, asm_fprintf_format_type,
                   gcc_diag_format_type, gcc_tdiag_format_type,
                   gcc_cdiag_format_type,
                   gcc_cxxdiag_format_type, gcc_gfc_format_type,
-                  scanf_format_type, strftime_format_type,
-                  strfmon_format_type, format_type_error = -1};
+                  format_type_error = -1};
 
 typedef struct function_format_info
 {
@@ -80,7 +79,8 @@ static bool check_format_string (tree argument,
                                 int flags, bool *no_add_attrs);
 static bool get_constant (tree expr, unsigned HOST_WIDE_INT *value,
                          int validated_p);
-
+static const char *convert_format_name_to_system_name (const char *attr_name);
+static bool cmp_attribs (const char *tattr_name, const char *attr_name);
 
 /* Handle a "format_arg" attribute; arguments as in
    struct attribute_spec.handler.  */
@@ -191,6 +191,8 @@ decode_format_attr (tree args, function_format_info *info, int validated_p)
     {
       const char *p = IDENTIFIER_POINTER (format_type_id);
 
+      p = convert_format_name_to_system_name (p);
+
       info->format_type = decode_format_type (p);
 
       if (info->format_type == format_type_error)
@@ -257,6 +259,8 @@ typedef struct format_wanted_type
   tree wanted_type;
   /* The name of this type to use in diagnostics.  */
   const char *wanted_type_name;
+  /* Should be type checked just for scalar width identity.  */
+  int scalar_identity_flag;
   /* The level of indirection through pointers at which this type occurs.  */
   int pointer_count;
   /* Whether, when pointer_count is 1, to allow any character type when
@@ -281,36 +285,38 @@ typedef struct format_wanted_type
   struct format_wanted_type *next;
 } format_wanted_type;
 
+/* Convenience macro for format_length_info meaning unused.  */
+#define NO_FMT NULL, FMT_LEN_none, STD_C89
 
 static const format_length_info printf_length_specs[] =
 {
-  { "h", FMT_LEN_h, STD_C89, "hh", FMT_LEN_hh, STD_C99 },
-  { "l", FMT_LEN_l, STD_C89, "ll", FMT_LEN_ll, STD_C9L },
-  { "q", FMT_LEN_ll, STD_EXT, NULL, 0, 0 },
-  { "L", FMT_LEN_L, STD_C89, NULL, 0, 0 },
-  { "z", FMT_LEN_z, STD_C99, NULL, 0, 0 },
-  { "Z", FMT_LEN_z, STD_EXT, NULL, 0, 0 },
-  { "t", FMT_LEN_t, STD_C99, NULL, 0, 0 },
-  { "j", FMT_LEN_j, STD_C99, NULL, 0, 0 },
-  { "H", FMT_LEN_H, STD_EXT, NULL, 0, 0 },
-  { "D", FMT_LEN_D, STD_EXT, "DD", FMT_LEN_DD, STD_EXT },
-  { NULL, 0, 0, NULL, 0, 0 }
+  { "h", FMT_LEN_h, STD_C89, "hh", FMT_LEN_hh, STD_C99, 0 },
+  { "l", FMT_LEN_l, STD_C89, "ll", FMT_LEN_ll, STD_C9L, 0 },
+  { "q", FMT_LEN_ll, STD_EXT, NO_FMT, 0 },
+  { "L", FMT_LEN_L, STD_C89, NO_FMT, 0 },
+  { "z", FMT_LEN_z, STD_C99, NO_FMT, 0 },
+  { "Z", FMT_LEN_z, STD_EXT, NO_FMT, 0 },
+  { "t", FMT_LEN_t, STD_C99, NO_FMT, 0 },
+  { "j", FMT_LEN_j, STD_C99, NO_FMT, 0 },
+  { "H", FMT_LEN_H, STD_EXT, NO_FMT, 0 },
+  { "D", FMT_LEN_D, STD_EXT, "DD", FMT_LEN_DD, STD_EXT, 0 },
+  { NO_FMT, NO_FMT, 0 }
 };
 
 /* Length specifiers valid for asm_fprintf.  */
 static const format_length_info asm_fprintf_length_specs[] =
 {
-  { "l", FMT_LEN_l, STD_C89, "ll", FMT_LEN_ll, STD_C89 },
-  { "w", FMT_LEN_none, STD_C89, NULL, 0, 0 },
-  { NULL, 0, 0, NULL, 0, 0 }
+  { "l", FMT_LEN_l, STD_C89, "ll", FMT_LEN_ll, STD_C89, 0 },
+  { "w", FMT_LEN_none, STD_C89, NO_FMT, 0 },
+  { NO_FMT, NO_FMT, 0 }
 };
 
 /* Length specifiers valid for GCC diagnostics.  */
 static const format_length_info gcc_diag_length_specs[] =
 {
-  { "l", FMT_LEN_l, STD_C89, "ll", FMT_LEN_ll, STD_C89 },
-  { "w", FMT_LEN_none, STD_C89, NULL, 0, 0 },
-  { NULL, 0, 0, NULL, 0, 0 }
+  { "l", FMT_LEN_l, STD_C89, "ll", FMT_LEN_ll, STD_C89, 0 },
+  { "w", FMT_LEN_none, STD_C89, NO_FMT, 0 },
+  { NO_FMT, NO_FMT, 0 }
 };
 
 /* The custom diagnostics all accept the same length specifiers.  */
@@ -321,16 +327,16 @@ static const format_length_info gcc_diag_length_specs[] =
 /* This differs from printf_length_specs only in that "Z" is not accepted.  */
 static const format_length_info scanf_length_specs[] =
 {
-  { "h", FMT_LEN_h, STD_C89, "hh", FMT_LEN_hh, STD_C99 },
-  { "l", FMT_LEN_l, STD_C89, "ll", FMT_LEN_ll, STD_C9L },
-  { "q", FMT_LEN_ll, STD_EXT, NULL, 0, 0 },
-  { "L", FMT_LEN_L, STD_C89, NULL, 0, 0 },
-  { "z", FMT_LEN_z, STD_C99, NULL, 0, 0 },
-  { "t", FMT_LEN_t, STD_C99, NULL, 0, 0 },
-  { "j", FMT_LEN_j, STD_C99, NULL, 0, 0 },
-  { "H", FMT_LEN_H, STD_EXT, NULL, 0, 0 },
-  { "D", FMT_LEN_D, STD_EXT, "DD", FMT_LEN_DD, STD_EXT },
-  { NULL, 0, 0, NULL, 0, 0 }
+  { "h", FMT_LEN_h, STD_C89, "hh", FMT_LEN_hh, STD_C99, 0 },
+  { "l", FMT_LEN_l, STD_C89, "ll", FMT_LEN_ll, STD_C9L, 0 },
+  { "q", FMT_LEN_ll, STD_EXT, NO_FMT, 0 },
+  { "L", FMT_LEN_L, STD_C89, NO_FMT, 0 },
+  { "z", FMT_LEN_z, STD_C99, NO_FMT, 0 },
+  { "t", FMT_LEN_t, STD_C99, NO_FMT, 0 },
+  { "j", FMT_LEN_j, STD_C99, NO_FMT, 0 },
+  { "H", FMT_LEN_H, STD_EXT, NO_FMT, 0 },
+  { "D", FMT_LEN_D, STD_EXT, "DD", FMT_LEN_DD, STD_EXT, 0 },
+  { NO_FMT, NO_FMT, 0 }
 };
 
 
@@ -339,10 +345,19 @@ static const format_length_info scanf_length_specs[] =
 static const format_length_info strfmon_length_specs[] =
 {
   /* A GNU extension.  */
-  { "L", FMT_LEN_L, STD_C89, NULL, 0, 0 },
-  { NULL, 0, 0, NULL, 0, 0 }
+  { "L", FMT_LEN_L, STD_C89, NO_FMT, 0 },
+  { NO_FMT, NO_FMT, 0 }
 };
 
+
+/* For now, the Fortran front-end routines only use l as length modifier.  */
+static const format_length_info gcc_gfc_length_specs[] =
+{
+  { "l", FMT_LEN_l, STD_C89, NO_FMT, 0 },
+  { NO_FMT, NO_FMT, 0 }
+};
+
+
 static const format_flag_spec printf_flag_specs[] =
 {
   { ' ',  0, 0, N_("' ' flag"),        N_("the ' ' printf flag"),              STD_C89 },
@@ -355,7 +370,7 @@ static const format_flag_spec printf_flag_specs[] =
   { 'w',  0, 0, N_("field width"),     N_("field width in printf format"),     STD_C89 },
   { 'p',  0, 0, N_("precision"),       N_("precision in printf format"),       STD_C89 },
   { 'L',  0, 0, N_("length modifier"), N_("length modifier in printf format"), STD_C89 },
-  { 0, 0, 0, NULL, NULL, 0 }
+  { 0, 0, 0, NULL, NULL, STD_C89 }
 };
 
 
@@ -377,7 +392,7 @@ static const format_flag_spec asm_fprintf_flag_specs[] =
   { 'w',  0, 0, N_("field width"),     N_("field width in printf format"),     STD_C89 },
   { 'p',  0, 0, N_("precision"),       N_("precision in printf format"),       STD_C89 },
   { 'L',  0, 0, N_("length modifier"), N_("length modifier in printf format"), STD_C89 },
-  { 0, 0, 0, NULL, NULL, 0 }
+  { 0, 0, 0, NULL, NULL, STD_C89 }
 };
 
 static const format_flag_pair asm_fprintf_flag_pairs[] =
@@ -408,7 +423,7 @@ static const format_flag_spec gcc_diag_flag_specs[] =
   { 'q',  0, 0, N_("'q' flag"),        N_("the 'q' diagnostic flag"),          STD_C89 },
   { 'p',  0, 0, N_("precision"),       N_("precision in printf format"),       STD_C89 },
   { 'L',  0, 0, N_("length modifier"), N_("length modifier in printf format"), STD_C89 },
-  { 0, 0, 0, NULL, NULL, 0 }
+  { 0, 0, 0, NULL, NULL, STD_C89 }
 };
 
 #define gcc_tdiag_flag_specs gcc_diag_flag_specs
@@ -421,24 +436,26 @@ static const format_flag_spec gcc_cxxdiag_flag_specs[] =
   { 'q',  0, 0, N_("'q' flag"),        N_("the 'q' diagnostic flag"),          STD_C89 },
   { 'p',  0, 0, N_("precision"),       N_("precision in printf format"),       STD_C89 },
   { 'L',  0, 0, N_("length modifier"), N_("length modifier in printf format"), STD_C89 },
-  { 0, 0, 0, NULL, NULL, 0 }
+  { 0, 0, 0, NULL, NULL, STD_C89 }
 };
 
 static const format_flag_spec scanf_flag_specs[] =
 {
   { '*',  0, 0, N_("assignment suppression"), N_("the assignment suppression scanf feature"), STD_C89 },
   { 'a',  0, 0, N_("'a' flag"),               N_("the 'a' scanf flag"),                       STD_EXT },
+  { 'm',  0, 0, N_("'m' flag"),               N_("the 'm' scanf flag"),                       STD_EXT },
   { 'w',  0, 0, N_("field width"),            N_("field width in scanf format"),              STD_C89 },
   { 'L',  0, 0, N_("length modifier"),        N_("length modifier in scanf format"),          STD_C89 },
   { '\'', 0, 0, N_("''' flag"),               N_("the ''' scanf flag"),                       STD_EXT },
   { 'I',  0, 0, N_("'I' flag"),               N_("the 'I' scanf flag"),                       STD_EXT },
-  { 0, 0, 0, NULL, NULL, 0 }
+  { 0, 0, 0, NULL, NULL, STD_C89 }
 };
 
 
 static const format_flag_pair scanf_flag_pairs[] =
 {
   { '*', 'L', 0, 0 },
+  { 'a', 'm', 0, 0 },
   { 0, 0, 0, 0 }
 };
 
@@ -454,7 +471,7 @@ static const format_flag_spec strftime_flag_specs[] =
   { 'E', 0,   0, N_("'E' modifier"), N_("the 'E' strftime modifier"),      STD_C99 },
   { 'O', 0,   0, N_("'O' modifier"), N_("the 'O' strftime modifier"),      STD_C99 },
   { 'O', 'o', 0, NULL,               N_("the 'O' modifier"),               STD_EXT },
-  { 0, 0, 0, NULL, NULL, 0 }
+  { 0, 0, 0, NULL, NULL, STD_C89 }
 };
 
 
@@ -481,7 +498,7 @@ static const format_flag_spec strfmon_flag_specs[] =
   { '#',  0, 0, N_("left precision"),  N_("left precision in strfmon format"),  STD_C89 },
   { 'p',  0, 0, N_("right precision"), N_("right precision in strfmon format"), STD_C89 },
   { 'L',  0, 0, N_("length modifier"), N_("length modifier in strfmon format"), STD_C89 },
-  { 0, 0, 0, NULL, NULL, 0 }
+  { 0, 0, 0, NULL, NULL, STD_C89 }
 };
 
 static const format_flag_pair strfmon_flag_pairs[] =
@@ -511,7 +528,7 @@ static const format_char_info print_char_table[] =
   { "S",   1, STD_EXT, { TEX_W,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN }, "-wp",       "R",  NULL },
   /* GNU conversion specifiers.  */
   { "m",   0, STD_EXT, { T89_V,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN }, "-wp",       "",   NULL },
-  { NULL,  0, 0, NOLENGTHS, NULL, NULL, NULL }
+  { NULL,  0, STD_C89, NOLENGTHS, NULL, NULL, NULL }
 };
 
 static const format_char_info asm_fprintf_char_table[] =
@@ -531,7 +548,7 @@ static const format_char_info asm_fprintf_char_table[] =
   { "U",   0, STD_C89, NOARGUMENTS, "",      "",   NULL },
   { "r",   0, STD_C89, { T89_I,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "",  "", NULL },
   { "@",   0, STD_C89, NOARGUMENTS, "",      "",   NULL },
-  { NULL,  0, 0, NOLENGTHS, NULL, NULL, NULL }
+  { NULL,  0, STD_C89, NOLENGTHS, NULL, NULL, NULL }
 };
 
 static const format_char_info gcc_diag_char_table[] =
@@ -546,15 +563,12 @@ static const format_char_info gcc_diag_char_table[] =
 
   /* Custom conversion specifiers.  */
 
-  /* %H will require "location_t" at runtime.  */
-  { "H",   0, STD_C89, { T89_V,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  "",   NULL },
-
   /* These will require a "tree" at runtime.  */
-  { "J", 0, STD_C89, { T89_V,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",    "",   NULL },
+  { "K", 0, STD_C89, { T89_V,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",    "",   NULL },
 
   { "<>'", 0, STD_C89, NOARGUMENTS, "",      "",   NULL },
   { "m",   0, STD_C89, NOARGUMENTS, "q",     "",   NULL },
-  { NULL,  0, 0, NOLENGTHS, NULL, NULL, NULL }
+  { NULL,  0, STD_C89, NOLENGTHS, NULL, NULL, NULL }
 };
 
 static const format_char_info gcc_tdiag_char_table[] =
@@ -569,15 +583,12 @@ static const format_char_info gcc_tdiag_char_table[] =
 
   /* Custom conversion specifiers.  */
 
-  /* %H will require "location_t" at runtime.  */
-  { "H",   0, STD_C89, { T89_V,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  "",   NULL },
-
   /* These will require a "tree" at runtime.  */
-  { "DFJT", 0, STD_C89, { T89_V,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q+", "",   NULL },
+  { "DFKTE", 0, STD_C89, { T89_V,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q+", "",   NULL },
 
   { "<>'", 0, STD_C89, NOARGUMENTS, "",      "",   NULL },
   { "m",   0, STD_C89, NOARGUMENTS, "q",     "",   NULL },
-  { NULL,  0, 0, NOLENGTHS, NULL, NULL, NULL }
+  { NULL,  0, STD_C89, NOLENGTHS, NULL, NULL, NULL }
 };
 
 static const format_char_info gcc_cdiag_char_table[] =
@@ -592,15 +603,12 @@ static const format_char_info gcc_cdiag_char_table[] =
 
   /* Custom conversion specifiers.  */
 
-  /* %H will require "location_t" at runtime.  */
-  { "H",   0, STD_C89, { T89_V,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  "",   NULL },
-
   /* These will require a "tree" at runtime.  */
-  { "DEFJT", 0, STD_C89, { T89_V,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q+", "",   NULL },
+  { "DEFKT", 0, STD_C89, { T89_V,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q+", "",   NULL },
 
   { "<>'", 0, STD_C89, NOARGUMENTS, "",      "",   NULL },
   { "m",   0, STD_C89, NOARGUMENTS, "q",     "",   NULL },
-  { NULL,  0, 0, NOLENGTHS, NULL, NULL, NULL }
+  { NULL,  0, STD_C89, NOLENGTHS, NULL, NULL, NULL }
 };
 
 static const format_char_info gcc_cxxdiag_char_table[] =
@@ -615,24 +623,22 @@ static const format_char_info gcc_cxxdiag_char_table[] =
 
   /* Custom conversion specifiers.  */
 
-  /* %H will require "location_t" at runtime.  */
-  { "H",   0, STD_C89, { T89_V,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  "",   NULL },
-
   /* These will require a "tree" at runtime.  */
-  { "ADEFJTV",0,STD_C89,{ T89_V,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q+#",   "",   NULL },
+  { "ADEFKTV",0,STD_C89,{ T89_V,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q+#",   "",   NULL },
 
   /* These accept either an 'int' or an 'enum tree_code' (which is handled as an 'int'.)  */
   { "CLOPQ",0,STD_C89, { T89_I,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  "",   NULL },
 
   { "<>'", 0, STD_C89, NOARGUMENTS, "",      "",   NULL },
   { "m",   0, STD_C89, NOARGUMENTS, "q",     "",   NULL },
-  { NULL,  0, 0, NOLENGTHS, NULL, NULL, NULL }
+  { NULL,  0, STD_C89, NOLENGTHS, NULL, NULL, NULL }
 };
 
 static const format_char_info gcc_gfc_char_table[] =
 {
   /* C89 conversion specifiers.  */
-  { "di",  0, STD_C89, { T89_I,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "", "", NULL },
+  { "di",  0, STD_C89, { T89_I,   BADLEN,  BADLEN,  T89_L,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "", "", NULL },
+  { "u",   0, STD_C89, { T89_UI,  BADLEN,  BADLEN,  T89_UL,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "", "", NULL },
   { "c",   0, STD_C89, { T89_I,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "", "", NULL },
   { "s",   1, STD_C89, { T89_C,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "", "cR", NULL },
 
@@ -643,7 +649,7 @@ static const format_char_info gcc_gfc_char_table[] =
   /* This will require a "locus" at runtime.  */
   { "L",   0, STD_C89, { T89_V,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "", "R", NULL },
 
-  { NULL,  0, 0, NOLENGTHS, NULL, NULL, NULL }
+  { NULL,  0, STD_C89, NOLENGTHS, NULL, NULL, NULL }
 };
 
 static const format_char_info scan_char_table[] =
@@ -653,18 +659,18 @@ static const format_char_info scan_char_table[] =
   { "u",     1, STD_C89, { T89_UI,  T99_UC,  T89_US,  T89_UL,  T9L_ULL, TEX_ULL, T99_ST,  T99_UPD, T99_UIM, BADLEN,  BADLEN,  BADLEN }, "*w'I", "W",   NULL },
   { "oxX",   1, STD_C89, { T89_UI,  T99_UC,  T89_US,  T89_UL,  T9L_ULL, TEX_ULL, T99_ST,  T99_UPD, T99_UIM, BADLEN,  BADLEN,  BADLEN }, "*w",   "W",   NULL },
   { "efgEG", 1, STD_C89, { T89_F,   BADLEN,  BADLEN,  T89_D,   BADLEN,  T89_LD,  BADLEN,  BADLEN,  BADLEN,  TEX_D32, TEX_D64, TEX_D128 }, "*w'",  "W",   NULL },
-  { "c",     1, STD_C89, { T89_C,   BADLEN,  BADLEN,  T94_W,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN }, "*w",   "cW",  NULL },
-  { "s",     1, STD_C89, { T89_C,   BADLEN,  BADLEN,  T94_W,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN }, "*aw",  "cW",  NULL },
-  { "[",     1, STD_C89, { T89_C,   BADLEN,  BADLEN,  T94_W,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN }, "*aw",  "cW[", NULL },
+  { "c",     1, STD_C89, { T89_C,   BADLEN,  BADLEN,  T94_W,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN }, "*mw",   "cW",  NULL },
+  { "s",     1, STD_C89, { T89_C,   BADLEN,  BADLEN,  T94_W,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN }, "*amw",  "cW",  NULL },
+  { "[",     1, STD_C89, { T89_C,   BADLEN,  BADLEN,  T94_W,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN }, "*amw",  "cW[", NULL },
   { "p",     2, STD_C89, { T89_V,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN }, "*w",   "W",   NULL },
   { "n",     1, STD_C89, { T89_I,   T99_SC,  T89_S,   T89_L,   T9L_LL,  BADLEN,  T99_SST, T99_PD,  T99_IM,  BADLEN,  BADLEN,  BADLEN }, "",     "W",   NULL },
   /* C99 conversion specifiers.  */
   { "F",   1, STD_C99, { T99_F,   BADLEN,  BADLEN,  T99_D,   BADLEN,  T99_LD,  BADLEN,  BADLEN,  BADLEN,  TEX_D32, TEX_D64, TEX_D128 }, "*w'",  "W",   NULL },
   { "aA",   1, STD_C99, { T99_F,   BADLEN,  BADLEN,  T99_D,   BADLEN,  T99_LD,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN }, "*w'",  "W",   NULL },
   /* X/Open conversion specifiers.  */
-  { "C",     1, STD_EXT, { TEX_W,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN }, "*w",   "W",   NULL },
-  { "S",     1, STD_EXT, { TEX_W,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN }, "*aw",  "W",   NULL },
-  { NULL, 0, 0, NOLENGTHS, NULL, NULL, NULL }
+  { "C",     1, STD_EXT, { TEX_W,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN }, "*mw",   "W",   NULL },
+  { "S",     1, STD_EXT, { TEX_W,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN }, "*amw",  "W",   NULL },
+  { NULL, 0, STD_C89, NOLENGTHS, NULL, NULL, NULL }
 };
 
 static const format_char_info time_char_table[] =
@@ -691,74 +697,74 @@ static const format_char_info time_char_table[] =
   /* GNU conversion specifiers.  */
   { "kls",             0, STD_EXT, NOLENGTHS, "-_0Ow",  "",   NULL },
   { "P",               0, STD_EXT, NOLENGTHS, "",       "",   NULL },
-  { NULL,              0, 0, NOLENGTHS, NULL, NULL, NULL }
+  { NULL,              0, STD_C89, NOLENGTHS, NULL, NULL, NULL }
 };
 
 static const format_char_info monetary_char_table[] =
 {
   { "in", 0, STD_C89, { T89_D, BADLEN, BADLEN, BADLEN, BADLEN, T89_LD, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "=^+(!-w#p", "", NULL },
-  { NULL, 0, 0, NOLENGTHS, NULL, NULL, NULL }
+  { NULL, 0, STD_C89, NOLENGTHS, NULL, NULL, NULL }
 };
 
 /* This must be in the same order as enum format_type.  */
 static const format_kind_info format_types_orig[] =
 {
-  { "printf",   printf_length_specs,  print_char_table, " +#0-'I", NULL,
+  { "gnu_printf",   printf_length_specs,  print_char_table, " +#0-'I", NULL,
     printf_flag_specs, printf_flag_pairs,
     FMT_FLAG_ARG_CONVERT|FMT_FLAG_DOLLAR_MULTIPLE|FMT_FLAG_USE_DOLLAR|FMT_FLAG_EMPTY_PREC_OK,
-    'w', 0, 'p', 0, 'L',
+    'w', 0, 'p', 0, 'L', 0,
     &integer_type_node, &integer_type_node
   },
   { "asm_fprintf",   asm_fprintf_length_specs,  asm_fprintf_char_table, " +#0-", NULL,
     asm_fprintf_flag_specs, asm_fprintf_flag_pairs,
     FMT_FLAG_ARG_CONVERT|FMT_FLAG_EMPTY_PREC_OK,
-    'w', 0, 'p', 0, 'L',
+    'w', 0, 'p', 0, 'L', 0,
     NULL, NULL
   },
   { "gcc_diag",   gcc_diag_length_specs,  gcc_diag_char_table, "q+", NULL,
     gcc_diag_flag_specs, gcc_diag_flag_pairs,
     FMT_FLAG_ARG_CONVERT,
-    0, 0, 'p', 0, 'L',
+    0, 0, 'p', 0, 'L', 0,
     NULL, &integer_type_node
   },
   { "gcc_tdiag",   gcc_tdiag_length_specs,  gcc_tdiag_char_table, "q+", NULL,
     gcc_tdiag_flag_specs, gcc_tdiag_flag_pairs,
     FMT_FLAG_ARG_CONVERT,
-    0, 0, 'p', 0, 'L',
+    0, 0, 'p', 0, 'L', 0,
     NULL, &integer_type_node
   },
   { "gcc_cdiag",   gcc_cdiag_length_specs,  gcc_cdiag_char_table, "q+", NULL,
     gcc_cdiag_flag_specs, gcc_cdiag_flag_pairs,
     FMT_FLAG_ARG_CONVERT,
-    0, 0, 'p', 0, 'L',
+    0, 0, 'p', 0, 'L', 0,
     NULL, &integer_type_node
   },
   { "gcc_cxxdiag",   gcc_cxxdiag_length_specs,  gcc_cxxdiag_char_table, "q+#", NULL,
     gcc_cxxdiag_flag_specs, gcc_cxxdiag_flag_pairs,
     FMT_FLAG_ARG_CONVERT,
-    0, 0, 'p', 0, 'L',
+    0, 0, 'p', 0, 'L', 0,
     NULL, &integer_type_node
   },
-  { "gcc_gfc", NULL, gcc_gfc_char_table, "", NULL,
+  { "gcc_gfc", gcc_gfc_length_specs, gcc_gfc_char_table, "", NULL,
     NULL, gcc_gfc_flag_pairs,
     FMT_FLAG_ARG_CONVERT,
-    0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0,
     NULL, NULL
   },
-  { "scanf",    scanf_length_specs,   scan_char_table,  "*'I", NULL,
+  { "gnu_scanf",    scanf_length_specs,   scan_char_table,  "*'I", NULL,
     scanf_flag_specs, scanf_flag_pairs,
     FMT_FLAG_ARG_CONVERT|FMT_FLAG_SCANF_A_KLUDGE|FMT_FLAG_USE_DOLLAR|FMT_FLAG_ZERO_WIDTH_BAD|FMT_FLAG_DOLLAR_GAP_POINTER_OK,
-    'w', 0, 0, '*', 'L',
+    'w', 0, 0, '*', 'L', 'm',
     NULL, NULL
   },
-  { "strftime", NULL,                 time_char_table,  "_-0^#", "EO",
+  { "gnu_strftime", NULL,                 time_char_table,  "_-0^#", "EO",
     strftime_flag_specs, strftime_flag_pairs,
-    FMT_FLAG_FANCY_PERCENT_OK, 'w', 0, 0, 0, 0,
+    FMT_FLAG_FANCY_PERCENT_OK, 'w', 0, 0, 0, 0, 0,
     NULL, NULL
   },
-  { "strfmon",  strfmon_length_specs, monetary_char_table, "=^+(!-", NULL,
+  { "gnu_strfmon",  strfmon_length_specs, monetary_char_table, "=^+(!-", NULL,
     strfmon_flag_specs, strfmon_flag_pairs,
-    FMT_FLAG_ARG_CONVERT, 'w', '#', 'p', 0, 'L',
+    FMT_FLAG_ARG_CONVERT, 'w', '#', 'p', 0, 'L', 0,
     NULL, NULL
   }
 };
@@ -812,7 +818,7 @@ static void check_format_arg (void *, tree, unsigned HOST_WIDE_INT);
 static void check_format_info_main (format_check_results *,
                                    function_format_info *,
                                    const char *, int, tree,
-                                   unsigned HOST_WIDE_INT);
+                                    unsigned HOST_WIDE_INT, alloc_pool);
 
 static void init_dollar_format_checking (int, tree);
 static int maybe_read_dollar_number (const char **, int,
@@ -835,6 +841,8 @@ decode_format_type (const char *s)
 {
   int i;
   int slen;
+
+  s = convert_format_name_to_system_name (s);
   slen = strlen (s);
   for (i = 0; i < n_format_types; i++)
     {
@@ -1291,6 +1299,7 @@ check_format_arg (void *ctx, tree format_tree,
   const char *format_chars;
   tree array_size = 0;
   tree array_init;
+  alloc_pool fwt_pool;
 
   if (integer_zerop (format_tree))
     {
@@ -1392,21 +1401,16 @@ check_format_arg (void *ctx, tree format_tree,
       format_chars += offset;
       format_length -= offset;
     }
-  if (format_length < 1)
+  if (format_length < 1 || format_chars[--format_length] != 0)
     {
       res->number_unterminated++;
       return;
     }
-  if (format_length == 1)
+  if (format_length == 0)
     {
       res->number_empty++;
       return;
     }
-  if (format_chars[--format_length] != 0)
-    {
-      res->number_unterminated++;
-      return;
-    }
 
   /* Skip to first argument to check.  */
   while (arg_num + 1 < info->first_arg_num)
@@ -1420,8 +1424,11 @@ check_format_arg (void *ctx, tree format_tree,
      will decrement it if it finds there are extra arguments, but this way
      need not adjust it for every return.  */
   res->number_other++;
+  fwt_pool = create_alloc_pool ("format_wanted_type pool",
+                                sizeof (format_wanted_type), 10);
   check_format_info_main (res, info, format_chars, format_length,
-                         params, arg_num);
+                          params, arg_num, fwt_pool);
+  free_alloc_pool (fwt_pool);
 }
 
 
@@ -1436,7 +1443,7 @@ static void
 check_format_info_main (format_check_results *res,
                        function_format_info *info, const char *format_chars,
                        int format_length, tree params,
-                       unsigned HOST_WIDE_INT arg_num)
+                        unsigned HOST_WIDE_INT arg_num, alloc_pool fwt_pool)
 {
   const char *orig_format_chars = format_chars;
   tree first_fillin_param = params;
@@ -1473,7 +1480,8 @@ check_format_info_main (format_check_results *res,
       const format_length_info *fli = NULL;
       const format_char_info *fci = NULL;
       char flag_chars[256];
-      int aflag = 0;
+      int alloc_flag = 0;
+      int scalar_identity_flag = 0;
       const char *format_start = format_chars;
       if (*format_chars == 0)
        {
@@ -1607,6 +1615,7 @@ check_format_info_main (format_check_results *res,
                  width_wanted_type.wanted_type_name = NULL;
                  width_wanted_type.pointer_count = 0;
                  width_wanted_type.char_lenient_flag = 0;
+                 width_wanted_type.scalar_identity_flag = 0;
                  width_wanted_type.writing_in_flag = 0;
                  width_wanted_type.reading_from_flag = 0;
                  width_wanted_type.name = _("field width");
@@ -1709,6 +1718,7 @@ check_format_info_main (format_check_results *res,
                  precision_wanted_type.wanted_type_name = NULL;
                  precision_wanted_type.pointer_count = 0;
                  precision_wanted_type.char_lenient_flag = 0;
+                 precision_wanted_type.scalar_identity_flag = 0;
                  precision_wanted_type.writing_in_flag = 0;
                  precision_wanted_type.reading_from_flag = 0;
                  precision_wanted_type.name = _("field precision");
@@ -1732,18 +1742,45 @@ check_format_info_main (format_check_results *res,
            }
        }
 
+      if (fki->alloc_char && fki->alloc_char == *format_chars)
+       {
+         i = strlen (flag_chars);
+         flag_chars[i++] = fki->alloc_char;
+         flag_chars[i] = 0;
+         format_chars++;
+       }
+
+      /* Handle the scanf allocation kludge.  */
+      if (fki->flags & (int) FMT_FLAG_SCANF_A_KLUDGE)
+       {
+         if (*format_chars == 'a' && !flag_isoc99)
+           {
+             if (format_chars[1] == 's' || format_chars[1] == 'S'
+                 || format_chars[1] == '[')
+               {
+                 /* 'a' is used as a flag.  */
+                 i = strlen (flag_chars);
+                 flag_chars[i++] = 'a';
+                 flag_chars[i] = 0;
+                 format_chars++;
+               }
+           }
+       }
+
       /* Read any length modifier, if this kind of format has them.  */
       fli = fki->length_char_specs;
       length_chars = NULL;
       length_chars_val = FMT_LEN_none;
       length_chars_std = STD_C89;
+      scalar_identity_flag = 0;
       if (fli)
        {
-         while (fli->name != 0 && fli->name[0] != *format_chars)
-           fli++;
+         while (fli->name != 0
+                && strncmp (fli->name, format_chars, strlen (fli->name)))
+             fli++;
          if (fli->name != 0)
            {
-             format_chars++;
+             format_chars += strlen (fli->name);
              if (fli->double_name != 0 && fli->name[0] == *format_chars)
                {
                  format_chars++;
@@ -1756,6 +1793,7 @@ check_format_info_main (format_check_results *res,
                  length_chars = fli->name;
                  length_chars_val = fli->index;
                  length_chars_std = fli->std;
+                 scalar_identity_flag = fli->scalar_identity_flag;
                }
              i = strlen (flag_chars);
              flag_chars[i++] = fki->length_code_char;
@@ -1794,23 +1832,6 @@ check_format_info_main (format_check_results *res,
            }
        }
 
-      /* Handle the scanf allocation kludge.  */
-      if (fki->flags & (int) FMT_FLAG_SCANF_A_KLUDGE)
-       {
-         if (*format_chars == 'a' && !flag_isoc99)
-           {
-             if (format_chars[1] == 's' || format_chars[1] == 'S'
-                 || format_chars[1] == '[')
-               {
-                 /* 'a' is used as a flag.  */
-                 i = strlen (flag_chars);
-                 flag_chars[i++] = 'a';
-                 flag_chars[i] = 0;
-                 format_chars++;
-               }
-           }
-       }
-
       format_char = *format_chars;
       if (format_char == 0
          || (!(fki->flags & (int) FMT_FLAG_FANCY_PERCENT_OK)
@@ -1883,7 +1904,9 @@ check_format_info_main (format_check_results *res,
 
       if ((fki->flags & (int) FMT_FLAG_SCANF_A_KLUDGE)
          && strchr (flag_chars, 'a') != 0)
-       aflag = 1;
+       alloc_flag = 1;
+      if (fki->alloc_char && strchr (flag_chars, fki->alloc_char) != 0)
+       alloc_flag = 1;
 
       if (fki->suppression_char
          && strchr (flag_chars, fki->suppression_char) != 0)
@@ -2055,13 +2078,16 @@ check_format_info_main (format_check_results *res,
 
              wanted_type_ptr->wanted_type = wanted_type;
              wanted_type_ptr->wanted_type_name = wanted_type_name;
-             wanted_type_ptr->pointer_count = fci->pointer_count + aflag;
+             wanted_type_ptr->pointer_count = fci->pointer_count + alloc_flag;
              wanted_type_ptr->char_lenient_flag = 0;
              if (strchr (fci->flags2, 'c') != 0)
                wanted_type_ptr->char_lenient_flag = 1;
+             wanted_type_ptr->scalar_identity_flag = 0;
+             if (scalar_identity_flag)
+               wanted_type_ptr->scalar_identity_flag = 1;
              wanted_type_ptr->writing_in_flag = 0;
              wanted_type_ptr->reading_from_flag = 0;
-             if (aflag)
+             if (alloc_flag)
                wanted_type_ptr->writing_in_flag = 1;
              else
                {
@@ -2083,7 +2109,8 @@ check_format_info_main (format_check_results *res,
              fci = fci->chain;
              if (fci)
                {
-                 wanted_type_ptr = GGC_NEW (format_wanted_type);
+                  wanted_type_ptr = (format_wanted_type *)
+                      pool_alloc (fwt_pool);
                  arg_num++;
                  wanted_type = *fci->types[length_chars_val].type;
                  wanted_type_name = fci->types[length_chars_val].name;
@@ -2094,17 +2121,6 @@ check_format_info_main (format_check_results *res,
       if (first_wanted_type != 0)
        check_format_types (first_wanted_type, format_start,
                            format_chars - format_start);
-
-      if (main_wanted_type.next != NULL)
-       {
-         format_wanted_type *wanted_type_ptr = main_wanted_type.next;
-         while (wanted_type_ptr)
-           {
-             format_wanted_type *next = wanted_type_ptr->next;
-             ggc_free (wanted_type_ptr);
-             wanted_type_ptr = next;
-           }
-       }
     }
 }
 
@@ -2253,6 +2269,12 @@ check_format_types (format_wanted_type *types, const char *format_start,
          && (!pedantic || i < 2)
          && char_type_flag)
        continue;
+      if (types->scalar_identity_flag
+         && (TREE_CODE (cur_type) == TREE_CODE (wanted_type)
+             || (INTEGRAL_TYPE_P (cur_type)
+                 && INTEGRAL_TYPE_P (wanted_type)))
+         && TYPE_PRECISION (cur_type) == TYPE_PRECISION (wanted_type))
+       continue;
       /* Now we have a type mismatch.  */
       format_type_warning (types->name, format_start, format_length,
                           wanted_type, types->pointer_count,
@@ -2444,7 +2466,8 @@ init_dynamic_gfc_info (void)
          locus = identifier_global_value (locus);
          if (locus)
            {
-             if (TREE_CODE (locus) != TYPE_DECL)
+             if (TREE_CODE (locus) != TYPE_DECL
+                 || TREE_TYPE (locus) == error_mark_node)
                {
                  error ("%<locus%> is not defined as a type");
                  locus = 0;
@@ -2589,15 +2612,9 @@ init_dynamic_diag_info (void)
                     xmemdup (gcc_diag_char_table,
                              sizeof (gcc_diag_char_table),
                              sizeof (gcc_diag_char_table));
-      if (loc)
-       {
-         i = find_char_info_specifier_index (diag_fci, 'H');
-         diag_fci[i].types[0].type = &loc;
-         diag_fci[i].pointer_count = 1;
-       }
       if (t)
        {
-         i = find_char_info_specifier_index (diag_fci, 'J');
+         i = find_char_info_specifier_index (diag_fci, 'K');
          diag_fci[i].types[0].type = &t;
          diag_fci[i].pointer_count = 1;
        }
@@ -2609,19 +2626,13 @@ init_dynamic_diag_info (void)
                      xmemdup (gcc_tdiag_char_table,
                               sizeof (gcc_tdiag_char_table),
                               sizeof (gcc_tdiag_char_table));
-      if (loc)
-       {
-         i = find_char_info_specifier_index (tdiag_fci, 'H');
-         tdiag_fci[i].types[0].type = &loc;
-         tdiag_fci[i].pointer_count = 1;
-       }
       if (t)
        {
          /* All specifiers taking a tree share the same struct.  */
          i = find_char_info_specifier_index (tdiag_fci, 'D');
          tdiag_fci[i].types[0].type = &t;
          tdiag_fci[i].pointer_count = 1;
-         i = find_char_info_specifier_index (tdiag_fci, 'J');
+         i = find_char_info_specifier_index (tdiag_fci, 'K');
          tdiag_fci[i].types[0].type = &t;
          tdiag_fci[i].pointer_count = 1;
        }
@@ -2633,19 +2644,13 @@ init_dynamic_diag_info (void)
                      xmemdup (gcc_cdiag_char_table,
                               sizeof (gcc_cdiag_char_table),
                               sizeof (gcc_cdiag_char_table));
-      if (loc)
-       {
-         i = find_char_info_specifier_index (cdiag_fci, 'H');
-         cdiag_fci[i].types[0].type = &loc;
-         cdiag_fci[i].pointer_count = 1;
-       }
       if (t)
        {
          /* All specifiers taking a tree share the same struct.  */
          i = find_char_info_specifier_index (cdiag_fci, 'D');
          cdiag_fci[i].types[0].type = &t;
          cdiag_fci[i].pointer_count = 1;
-         i = find_char_info_specifier_index (cdiag_fci, 'J');
+         i = find_char_info_specifier_index (cdiag_fci, 'K');
          cdiag_fci[i].types[0].type = &t;
          cdiag_fci[i].pointer_count = 1;
        }
@@ -2657,19 +2662,13 @@ init_dynamic_diag_info (void)
                        xmemdup (gcc_cxxdiag_char_table,
                                 sizeof (gcc_cxxdiag_char_table),
                                 sizeof (gcc_cxxdiag_char_table));
-      if (loc)
-       {
-         i = find_char_info_specifier_index (cxxdiag_fci, 'H');
-         cxxdiag_fci[i].types[0].type = &loc;
-         cxxdiag_fci[i].pointer_count = 1;
-       }
       if (t)
        {
          /* All specifiers taking a tree share the same struct.  */
          i = find_char_info_specifier_index (cxxdiag_fci, 'D');
          cxxdiag_fci[i].types[0].type = &t;
          cxxdiag_fci[i].pointer_count = 1;
-         i = find_char_info_specifier_index (cxxdiag_fci, 'J');
+         i = find_char_info_specifier_index (cxxdiag_fci, 'K');
          cxxdiag_fci[i].types[0].type = &t;
          cxxdiag_fci[i].pointer_count = 1;
        }
@@ -2680,6 +2679,90 @@ init_dynamic_diag_info (void)
 extern const format_kind_info TARGET_FORMAT_TYPES[];
 #endif
 
+#ifdef TARGET_OVERRIDES_FORMAT_ATTRIBUTES
+extern const target_ovr_attr TARGET_OVERRIDES_FORMAT_ATTRIBUTES[];
+#endif
+#ifdef TARGET_OVERRIDES_FORMAT_INIT
+  extern void TARGET_OVERRIDES_FORMAT_INIT (void);
+#endif
+
+/* Attributes such as "printf" are equivalent to those such as
+   "gnu_printf" unless this is overridden by a target.  */
+static const target_ovr_attr gnu_target_overrides_format_attributes[] =
+{
+  { "gnu_printf",   "printf" },
+  { "gnu_scanf",    "scanf" },
+  { "gnu_strftime", "strftime" },
+  { "gnu_strfmon",  "strfmon" },
+  { NULL,           NULL }
+};
+
+/* Translate to unified attribute name. This is used in decode_format_type and
+   decode_format_attr. In attr_name the user specified argument is passed. It
+   returns the unified format name from TARGET_OVERRIDES_FORMAT_ATTRIBUTES
+   or the attr_name passed to this function, if there is no matching entry.  */
+static const char *
+convert_format_name_to_system_name (const char *attr_name)
+{
+  int i;
+
+  if (attr_name == NULL || *attr_name == 0
+      || strncmp (attr_name, "gcc_", 4) == 0)
+    return attr_name;
+#ifdef TARGET_OVERRIDES_FORMAT_INIT
+  TARGET_OVERRIDES_FORMAT_INIT ();
+#endif
+
+#ifdef TARGET_OVERRIDES_FORMAT_ATTRIBUTES
+  /* Check if format attribute is overridden by target.  */
+  if (TARGET_OVERRIDES_FORMAT_ATTRIBUTES != NULL
+      && TARGET_OVERRIDES_FORMAT_ATTRIBUTES_COUNT > 0)
+    {
+      for (i = 0; i < TARGET_OVERRIDES_FORMAT_ATTRIBUTES_COUNT; ++i)
+        {
+          if (cmp_attribs (TARGET_OVERRIDES_FORMAT_ATTRIBUTES[i].named_attr_src,
+                          attr_name))
+            return attr_name;
+          if (cmp_attribs (TARGET_OVERRIDES_FORMAT_ATTRIBUTES[i].named_attr_dst,
+                          attr_name))
+            return TARGET_OVERRIDES_FORMAT_ATTRIBUTES[i].named_attr_src;
+        }
+    }
+#endif
+  /* Otherwise default to gnu format.  */
+  for (i = 0;
+       gnu_target_overrides_format_attributes[i].named_attr_src != NULL;
+       ++i)
+    {
+      if (cmp_attribs (gnu_target_overrides_format_attributes[i].named_attr_src,
+                      attr_name))
+        return attr_name;
+      if (cmp_attribs (gnu_target_overrides_format_attributes[i].named_attr_dst,
+                      attr_name))
+        return gnu_target_overrides_format_attributes[i].named_attr_src;
+    }
+
+  return attr_name;
+}
+
+/* Return true if TATTR_NAME and ATTR_NAME are the same format attribute,
+   counting "name" and "__name__" as the same, false otherwise.  */
+static bool
+cmp_attribs (const char *tattr_name, const char *attr_name)
+{
+  int alen = strlen (attr_name);
+  int slen = (tattr_name ? strlen (tattr_name) : 0);
+  if (alen > 4 && attr_name[0] == '_' && attr_name[1] == '_'
+      && attr_name[alen - 1] == '_' && attr_name[alen - 2] == '_')
+    {
+      attr_name += 2;
+      alen -= 4;
+    }
+  if (alen != slen || strncmp (tattr_name, attr_name, alen) != 0)
+    return false;
+  return true;
+}
+
 /* Handle a "format" attribute; arguments as in
    struct attribute_spec.handler.  */
 tree
@@ -2695,8 +2778,8 @@ handle_format_attribute (tree *node, tree ARG_UNUSED (name), tree args,
      add them to FORMAT_TYPES at first use.  */
   if (TARGET_FORMAT_TYPES != NULL && !dynamic_format_types)
     {
-      dynamic_format_types = xmalloc ((n_format_types + TARGET_N_FORMAT_TYPES)
-                                     * sizeof (dynamic_format_types[0]));
+      dynamic_format_types = XNEWVEC (format_kind_info,
+                                     n_format_types + TARGET_N_FORMAT_TYPES);
       memcpy (dynamic_format_types, format_types_orig,
              sizeof (format_types_orig));
       memcpy (&dynamic_format_types[n_format_types], TARGET_FORMAT_TYPES,
@@ -2739,7 +2822,10 @@ handle_format_attribute (tree *node, tree ARG_UNUSED (name), tree args,
        }
     }
 
-  if (info.format_type == strftime_format_type && info.first_arg_num != 0)
+  /* Check if this is a strftime variant. Just for this variant
+     FMT_FLAG_ARG_CONVERT is not set.  */
+  if ((format_types[info.format_type].flags & (int) FMT_FLAG_ARG_CONVERT) == 0
+      && info.first_arg_num != 0)
     {
       error ("strftime formats cannot format arguments");
       *no_add_attrs = true;
@@ -2747,7 +2833,7 @@ handle_format_attribute (tree *node, tree ARG_UNUSED (name), tree args,
     }
 
   /* If this is a custom GCC-internal format type, we have to
-     initialize certain bits a runtime.  */
+     initialize certain bits at runtime.  */
   if (info.format_type == asm_fprintf_format_type
       || info.format_type == gcc_gfc_format_type
       || info.format_type == gcc_diag_format_type