Makefile.in (c-format.o): Depend on c-format.h.
authorDaniel Jacobowitz <dan@debian.org>
Mon, 19 Jul 2004 14:48:09 +0000 (14:48 +0000)
committerDaniel Jacobowitz <drow@gcc.gnu.org>
Mon, 19 Jul 2004 14:48:09 +0000 (14:48 +0000)
gcc/
* Makefile.in (c-format.o): Depend on c-format.h.
* c-format.h: New file.
(struct format_char_info): Add CHAIN member.
* c-format.c: Move some types and constants to c-format.h.
(format_type_error): Set to -1.
(struct function_format_info): Use an int for format_type.
(decode_format_type): Return an int.  Return format_type_error
on error.
(print_char_table, asm_fprintf_char_table, gcc_diag_char_table)
(gcc_diag_char_table, gcc_cdiag_char_table, gcc_cxxdiag_char_table)
(scan_char_table, time_char_table, monetary_char_table): Initialize
CHAIN to NULL.
(n_format_types): New variable.
(check_format_info_main): Handle CHAIN in format_char_info.
(handle_format_attribute): Handle TARGET_FORMAT_TYPES and
TARGET_N_FORMAT_TYPES.
* config.gcc (i[34567]86-*-solaris2*, sparc64-*-solaris2*)
(sparc-*-solaris2*): Include config/t-sol2 and config/sol2-c.c.
* config/sol2-c.c: New file.
* config/t-sol2: New file.
* config/sol2.h (TARGET_N_FORMAT_TYPES, TARGET_FORMAT_TYPES): Define.
* config/sparc/elf.h, config/sparc/sp64-elf.h: Undefine
TARGET_N_FORMAT_TYPES and TARGET_FORMAT_TYPES.

* doc/extend.texi (Target Format Checks): New section.
(Function Attributes): Mention it.
* doc/invoke.texi: Mention target format checks.
* doc/sourcebuild.texi: Mention target format checks.
* dc/tm.texi (Misc): Document TARGET_N_FORMAT_TYPES and
TARGET_FORMAT_TYPES.
testsuite/
* gcc.dg/format/cmn-err-1.c: New test.

From-SVN: r84920

16 files changed:
gcc/ChangeLog
gcc/Makefile.in
gcc/c-format.c
gcc/c-format.h [new file with mode: 0644]
gcc/config.gcc
gcc/config/sol2-c.c [new file with mode: 0644]
gcc/config/sol2.h
gcc/config/sparc/elf.h
gcc/config/sparc/sp64-elf.h
gcc/config/t-sol2 [new file with mode: 0644]
gcc/doc/extend.texi
gcc/doc/invoke.texi
gcc/doc/sourcebuild.texi
gcc/doc/tm.texi
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/format/cmn-err-1.c [new file with mode: 0644]

index b70d6d2cb61c2614471c55746dcf7e496fc550b1..ad0b3b7b525c517bcedfb3ba4959108e6dd6def5 100644 (file)
@@ -1,3 +1,36 @@
+2004-07-19  Daniel Jacobowitz  <dan@debian.org>
+
+       * Makefile.in (c-format.o): Depend on c-format.h.
+       * c-format.h: New file.
+       (struct format_char_info): Add CHAIN member.
+       * c-format.c: Move some types and constants to c-format.h.
+       (format_type_error): Set to -1.
+       (struct function_format_info): Use an int for format_type.
+       (decode_format_type): Return an int.  Return format_type_error
+       on error.
+       (print_char_table, asm_fprintf_char_table, gcc_diag_char_table)
+       (gcc_diag_char_table, gcc_cdiag_char_table, gcc_cxxdiag_char_table)
+       (scan_char_table, time_char_table, monetary_char_table): Initialize
+       CHAIN to NULL.
+       (n_format_types): New variable.
+       (check_format_info_main): Handle CHAIN in format_char_info.
+       (handle_format_attribute): Handle TARGET_FORMAT_TYPES and
+       TARGET_N_FORMAT_TYPES.
+       * config.gcc (i[34567]86-*-solaris2*, sparc64-*-solaris2*)
+       (sparc-*-solaris2*): Include config/t-sol2 and config/sol2-c.c.
+       * config/sol2-c.c: New file.
+       * config/t-sol2: New file.
+       * config/sol2.h (TARGET_N_FORMAT_TYPES, TARGET_FORMAT_TYPES): Define.
+       * config/sparc/elf.h, config/sparc/sp64-elf.h: Undefine
+       TARGET_N_FORMAT_TYPES and TARGET_FORMAT_TYPES.
+
+       * doc/extend.texi (Target Format Checks): New section.
+       (Function Attributes): Mention it.
+       * doc/invoke.texi: Mention target format checks.
+       * doc/sourcebuild.texi: Mention target format checks.
+       * dc/tm.texi (Misc): Document TARGET_N_FORMAT_TYPES and
+       TARGET_FORMAT_TYPES.
+
 2004-07-19  Andreas Krebbel  <krebbel1@de.ibm.com>
 
        * config/s390/s390-protos.h (s390_return_address_offset): Prototype
index c9793fc4b62fc8bea283b04121d877a4c7134692..f8acb48f5b209a1c0f97015bd736cdc56dc0fb8f 100644 (file)
@@ -1440,7 +1440,7 @@ attribs.o : attribs.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(TREE_H) $(FL
        builtin-types.def $(TARGET_H) langhooks.h
 
 c-format.o : c-format.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(TREE_H) langhooks.h \
-       $(C_COMMON_H) $(FLAGS_H) toplev.h intl.h $(DIAGNOSTIC_H)
+       $(C_COMMON_H) $(FLAGS_H) toplev.h intl.h $(DIAGNOSTIC_H) c-format.h
 
 c-semantics.o : c-semantics.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(TREE_H) \
        $(C_TREE_H) $(FLAGS_H) toplev.h output.h c-pragma.h $(RTL_H) $(GGC_H) \
index 98274ee649d10fbca908df23267b758e12b6bd7a..4aff5f2660b658496a894c7db23592de453f736c 100644 (file)
@@ -30,6 +30,7 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 #include "intl.h"
 #include "diagnostic.h"
 #include "langhooks.h"
+#include "c-format.h"
 \f
 /* Set format warning options according to a -Wformat=n option.  */
 
@@ -53,23 +54,24 @@ set_Wformat (int setting)
 \f
 /* Handle attributes associated with format checking.  */
 
-/* This must be in the same order as format_types, with format_type_error
-   last.  */
+/* This must be in the same order as format_types, except for
+   format_type_error.  Target-specific format types do not have
+   matching enum values.  */
 enum format_type { printf_format_type, asm_fprintf_format_type,
                   gcc_diag_format_type, gcc_cdiag_format_type,
                   gcc_cxxdiag_format_type,
                   scanf_format_type, strftime_format_type,
-                  strfmon_format_type, format_type_error };
+                  strfmon_format_type, format_type_error = -1};
 
 typedef struct function_format_info
 {
-  enum format_type format_type;        /* type of format (printf, scanf, etc.) */
+  int format_type;                     /* type of format (printf, scanf, etc.) */
   unsigned HOST_WIDE_INT format_num;   /* number of format argument */
   unsigned HOST_WIDE_INT first_arg_num;        /* number of first arg (zero for varargs) */
 } function_format_info;
 
 static bool decode_format_attr (tree, function_format_info *, int);
-static enum format_type decode_format_type (const char *);
+static int decode_format_type (const char *);
 
 static bool check_format_string (tree argument,
                                 unsigned HOST_WIDE_INT format_num,
@@ -230,33 +232,6 @@ decode_format_attr (tree args, function_format_info *info, int validated_p)
 \f
 /* Check a call to a format function against a parameter list.  */
 
-/* The meaningfully distinct length modifiers for format checking recognized
-   by GCC.  */
-enum format_lengths
-{
-  FMT_LEN_none,
-  FMT_LEN_hh,
-  FMT_LEN_h,
-  FMT_LEN_l,
-  FMT_LEN_ll,
-  FMT_LEN_L,
-  FMT_LEN_z,
-  FMT_LEN_t,
-  FMT_LEN_j,
-  FMT_LEN_MAX
-};
-
-
-/* The standard versions in which various format features appeared.  */
-enum format_std_version
-{
-  STD_C89,
-  STD_C94,
-  STD_C9L, /* C99, but treat as C89 if -Wno-long-long.  */
-  STD_C99,
-  STD_EXT
-};
-
 /* The C standard version C++ is treated as equivalent to
    or inheriting from, for the purpose of format features supported.  */
 #define CPLUSPLUS_STD_VER      STD_C94
@@ -280,195 +255,6 @@ enum format_std_version
                                       ? (warn_long_long ? STD_C99 : STD_C89) \
                                       : (VER)))
 
-/* Flags that may apply to a particular kind of format checked by GCC.  */
-enum
-{
-  /* This format converts arguments of types determined by the
-     format string.  */
-  FMT_FLAG_ARG_CONVERT = 1,
-  /* The scanf allocation 'a' kludge applies to this format kind.  */
-  FMT_FLAG_SCANF_A_KLUDGE = 2,
-  /* A % during parsing a specifier is allowed to be a modified % rather
-     that indicating the format is broken and we are out-of-sync.  */
-  FMT_FLAG_FANCY_PERCENT_OK = 4,
-  /* With $ operand numbers, it is OK to reference the same argument more
-     than once.  */
-  FMT_FLAG_DOLLAR_MULTIPLE = 8,
-  /* This format type uses $ operand numbers (strfmon doesn't).  */
-  FMT_FLAG_USE_DOLLAR = 16,
-  /* Zero width is bad in this type of format (scanf).  */
-  FMT_FLAG_ZERO_WIDTH_BAD = 32,
-  /* Empty precision specification is OK in this type of format (printf).  */
-  FMT_FLAG_EMPTY_PREC_OK = 64,
-  /* Gaps are allowed in the arguments with $ operand numbers if all
-     arguments are pointers (scanf).  */
-  FMT_FLAG_DOLLAR_GAP_POINTER_OK = 128
-  /* Not included here: details of whether width or precision may occur
-     (controlled by width_char and precision_char); details of whether
-     '*' can be used for these (width_type and precision_type); details
-     of whether length modifiers can occur (length_char_specs).  */
-};
-
-
-/* Structure describing a length modifier supported in format checking, and
-   possibly a doubled version such as "hh".  */
-typedef struct
-{
-  /* Name of the single-character length modifier.  */
-  const char *name;
-  /* Index into a format_char_info.types array.  */
-  enum format_lengths index;
-  /* Standard version this length appears in.  */
-  enum format_std_version std;
-  /* Same, if the modifier can be repeated, or NULL if it can't.  */
-  const char *double_name;
-  enum format_lengths double_index;
-  enum format_std_version double_std;
-} format_length_info;
-
-
-/* Structure describing the combination of a conversion specifier
-   (or a set of specifiers which act identically) and a length modifier.  */
-typedef struct
-{
-  /* The standard version this combination of length and type appeared in.
-     This is only relevant if greater than those for length and type
-     individually; otherwise it is ignored.  */
-  enum format_std_version std;
-  /* The name to use for the type, if different from that generated internally
-     (e.g., "signed size_t").  */
-  const char *name;
-  /* The type itself.  */
-  tree *type;
-} format_type_detail;
-
-
-/* Macros to fill out tables of these.  */
-#define NOARGUMENTS    { T89_V, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }
-#define BADLEN { 0, NULL, NULL }
-#define NOLENGTHS      { BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }
-
-
-/* Structure describing a format conversion specifier (or a set of specifiers
-   which act identically), and the length modifiers used with it.  */
-typedef struct
-{
-  const char *format_chars;
-  int pointer_count;
-  enum format_std_version std;
-  /* Types accepted for each length modifier.  */
-  format_type_detail types[FMT_LEN_MAX];
-  /* List of other modifier characters allowed with these specifiers.
-     This lists flags, and additionally "w" for width, "p" for precision
-     (right precision, for strfmon), "#" for left precision (strfmon),
-     "a" for scanf "a" allocation extension (not applicable in C99 mode),
-     "*" for scanf suppression, and "E" and "O" for those strftime
-     modifiers.  */
-  const char *flag_chars;
-  /* List of additional flags describing these conversion specifiers.
-     "c" for generic character pointers being allowed, "2" for strftime
-     two digit year formats, "3" for strftime formats giving two digit
-     years in some locales, "4" for "2" which becomes "3" with an "E" modifier,
-     "o" if use of strftime "O" is a GNU extension beyond C99,
-     "W" if the argument is a pointer which is dereferenced and written into,
-     "R" if the argument is a pointer which is dereferenced and read from,
-     "i" for printf integer formats where the '0' flag is ignored with
-     precision, and "[" for the starting character of a scanf scanset.  */
-  const char *flags2;
-} format_char_info;
-
-
-/* Structure describing a flag accepted by some kind of format.  */
-typedef struct
-{
-  /* The flag character in question (0 for end of array).  */
-  int flag_char;
-  /* Zero if this entry describes the flag character in general, or a
-     nonzero character that may be found in flags2 if it describes the
-     flag when used with certain formats only.  If the latter, only
-     the first such entry found that applies to the current conversion
-     specifier is used; the values of `name' and `long_name' it supplies
-     will be used, if non-NULL and the standard version is higher than
-     the unpredicated one, for any pedantic warning.  For example, 'o'
-     for strftime formats (meaning 'O' is an extension over C99).  */
-  int predicate;
-  /* Nonzero if the next character after this flag in the format should
-     be skipped ('=' in strfmon), zero otherwise.  */
-  int skip_next_char;
-  /* The name to use for this flag in diagnostic messages.  For example,
-     N_("`0' flag"), N_("field width").  */
-  const char *name;
-  /* Long name for this flag in diagnostic messages; currently only used for
-     "ISO C does not support ...".  For example, N_("the `I' printf flag").  */
-  const char *long_name;
-  /* The standard version in which it appeared.  */
-  enum format_std_version std;
-} format_flag_spec;
-
-
-/* Structure describing a combination of flags that is bad for some kind
-   of format.  */
-typedef struct
-{
-  /* The first flag character in question (0 for end of array).  */
-  int flag_char1;
-  /* The second flag character.  */
-  int flag_char2;
-  /* Nonzero if the message should say that the first flag is ignored with
-     the second, zero if the combination should simply be objected to.  */
-  int ignored;
-  /* Zero if this entry applies whenever this flag combination occurs,
-     a nonzero character from flags2 if it only applies in some
-     circumstances (e.g. 'i' for printf formats ignoring 0 with precision).  */
-  int predicate;
-} format_flag_pair;
-
-
-/* Structure describing a particular kind of format processed by GCC.  */
-typedef struct
-{
-  /* The name of this kind of format, for use in diagnostics.  Also
-     the name of the attribute (without preceding and following __).  */
-  const char *name;
-  /* Specifications of the length modifiers accepted; possibly NULL.  */
-  const format_length_info *length_char_specs;
-  /* Details of the conversion specification characters accepted.  */
-  const format_char_info *conversion_specs;
-  /* String listing the flag characters that are accepted.  */
-  const char *flag_chars;
-  /* String listing modifier characters (strftime) accepted.  May be NULL.  */
-  const char *modifier_chars;
-  /* Details of the flag characters, including pseudo-flags.  */
-  const format_flag_spec *flag_specs;
-  /* Details of bad combinations of flags.  */
-  const format_flag_pair *bad_flag_pairs;
-  /* Flags applicable to this kind of format.  */
-  int flags;
-  /* Flag character to treat a width as, or 0 if width not used.  */
-  int width_char;
-  /* Flag character to treat a left precision (strfmon) as,
-     or 0 if left precision not used.  */
-  int left_precision_char;
-  /* Flag character to treat a precision (for strfmon, right precision) as,
-     or 0 if precision not used.  */
-  int precision_char;
-  /* If a flag character has the effect of suppressing the conversion of
-     an argument ('*' in scanf), that flag character, otherwise 0.  */
-  int suppression_char;
-  /* Flag character to treat a length modifier as (ignored if length
-     modifiers not used).  Need not be placed in flag_chars for conversion
-     specifiers, but is used to check for bad combinations such as length
-     modifier with assignment suppression in scanf.  */
-  int length_code_char;
-  /* Pointer to type of argument expected if '*' is used for a width,
-     or NULL if '*' not used for widths.  */
-  tree *width_type;
-  /* Pointer to type of argument expected if '*' is used for a precision,
-     or NULL if '*' not used for precisions.  */
-  tree *precision_type;
-} format_kind_info;
-
-
 /* Structure describing details of a type expected in format checking,
    and the type to check against it.  */
 typedef struct format_wanted_type
@@ -698,229 +484,174 @@ static const format_flag_pair strfmon_flag_pairs[] =
 };
 
 
-#define T_I    &integer_type_node
-#define T89_I  { STD_C89, NULL, T_I }
-#define T_L    &long_integer_type_node
-#define T89_L  { STD_C89, NULL, T_L }
-#define T_LL   &long_long_integer_type_node
-#define T9L_LL { STD_C9L, NULL, T_LL }
-#define TEX_LL { STD_EXT, NULL, T_LL }
-#define T_S    &short_integer_type_node
-#define T89_S  { STD_C89, NULL, T_S }
-#define T_UI   &unsigned_type_node
-#define T89_UI { STD_C89, NULL, T_UI }
-#define T_UL   &long_unsigned_type_node
-#define T89_UL { STD_C89, NULL, T_UL }
-#define T_ULL  &long_long_unsigned_type_node
-#define T9L_ULL        { STD_C9L, NULL, T_ULL }
-#define TEX_ULL        { STD_EXT, NULL, T_ULL }
-#define T_US   &short_unsigned_type_node
-#define T89_US { STD_C89, NULL, T_US }
-#define T_F    &float_type_node
-#define T89_F  { STD_C89, NULL, T_F }
-#define T99_F  { STD_C99, NULL, T_F }
-#define T_D    &double_type_node
-#define T89_D  { STD_C89, NULL, T_D }
-#define T99_D  { STD_C99, NULL, T_D }
-#define T_LD   &long_double_type_node
-#define T89_LD { STD_C89, NULL, T_LD }
-#define T99_LD { STD_C99, NULL, T_LD }
-#define T_C    &char_type_node
-#define T89_C  { STD_C89, NULL, T_C }
-#define T_SC   &signed_char_type_node
-#define T99_SC { STD_C99, NULL, T_SC }
-#define T_UC   &unsigned_char_type_node
-#define T99_UC { STD_C99, NULL, T_UC }
-#define T_V    &void_type_node
-#define T89_V  { STD_C89, NULL, T_V }
-#define T_W    &wchar_type_node
-#define T94_W  { STD_C94, "wchar_t", T_W }
-#define TEX_W  { STD_EXT, "wchar_t", T_W }
-#define T_WI   &wint_type_node
-#define T94_WI { STD_C94, "wint_t", T_WI }
-#define TEX_WI { STD_EXT, "wint_t", T_WI }
-#define T_ST    &size_type_node
-#define T99_ST { STD_C99, "size_t", T_ST }
-#define T_SST   &signed_size_type_node
-#define T99_SST        { STD_C99, "signed size_t", T_SST }
-#define T_PD    &ptrdiff_type_node
-#define T99_PD { STD_C99, "ptrdiff_t", T_PD }
-#define T_UPD   &unsigned_ptrdiff_type_node
-#define T99_UPD        { STD_C99, "unsigned ptrdiff_t", T_UPD }
-#define T_IM    &intmax_type_node
-#define T99_IM { STD_C99, "intmax_t", T_IM }
-#define T_UIM   &uintmax_type_node
-#define T99_UIM        { STD_C99, "uintmax_t", T_UIM }
-
 static const format_char_info print_char_table[] =
 {
   /* C89 conversion specifiers.  */
-  { "di",  0, STD_C89, { T89_I,   T99_SC,  T89_S,   T89_L,   T9L_LL,  TEX_LL,  T99_SST, T99_PD,  T99_IM  }, "-wp0 +'I",  "i"  },
-  { "oxX", 0, STD_C89, { T89_UI,  T99_UC,  T89_US,  T89_UL,  T9L_ULL, TEX_ULL, T99_ST,  T99_UPD, T99_UIM }, "-wp0#",     "i"  },
-  { "u",   0, STD_C89, { T89_UI,  T99_UC,  T89_US,  T89_UL,  T9L_ULL, TEX_ULL, T99_ST,  T99_UPD, T99_UIM }, "-wp0'I",    "i"  },
-  { "fgG", 0, STD_C89, { T89_D,   BADLEN,  BADLEN,  T99_D,   BADLEN,  T89_LD,  BADLEN,  BADLEN,  BADLEN  }, "-wp0 +#'I", ""   },
-  { "eE",  0, STD_C89, { T89_D,   BADLEN,  BADLEN,  T99_D,   BADLEN,  T89_LD,  BADLEN,  BADLEN,  BADLEN  }, "-wp0 +#I",  ""   },
-  { "c",   0, STD_C89, { T89_I,   BADLEN,  BADLEN,  T94_WI,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "-w",        ""   },
-  { "s",   1, STD_C89, { T89_C,   BADLEN,  BADLEN,  T94_W,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "-wp",       "cR" },
-  { "p",   1, STD_C89, { T89_V,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "-w",        "c"  },
-  { "n",   1, STD_C89, { T89_I,   T99_SC,  T89_S,   T89_L,   T9L_LL,  BADLEN,  T99_SST, T99_PD,  T99_IM  }, "",          "W"  },
+  { "di",  0, STD_C89, { T89_I,   T99_SC,  T89_S,   T89_L,   T9L_LL,  TEX_LL,  T99_SST, T99_PD,  T99_IM  }, "-wp0 +'I",  "i",  NULL },
+  { "oxX", 0, STD_C89, { T89_UI,  T99_UC,  T89_US,  T89_UL,  T9L_ULL, TEX_ULL, T99_ST,  T99_UPD, T99_UIM }, "-wp0#",     "i",  NULL },
+  { "u",   0, STD_C89, { T89_UI,  T99_UC,  T89_US,  T89_UL,  T9L_ULL, TEX_ULL, T99_ST,  T99_UPD, T99_UIM }, "-wp0'I",    "i",  NULL },
+  { "fgG", 0, STD_C89, { T89_D,   BADLEN,  BADLEN,  T99_D,   BADLEN,  T89_LD,  BADLEN,  BADLEN,  BADLEN  }, "-wp0 +#'I", "",   NULL },
+  { "eE",  0, STD_C89, { T89_D,   BADLEN,  BADLEN,  T99_D,   BADLEN,  T89_LD,  BADLEN,  BADLEN,  BADLEN  }, "-wp0 +#I",  "",   NULL },
+  { "c",   0, STD_C89, { T89_I,   BADLEN,  BADLEN,  T94_WI,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "-w",        "",   NULL },
+  { "s",   1, STD_C89, { T89_C,   BADLEN,  BADLEN,  T94_W,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "-wp",       "cR", NULL },
+  { "p",   1, STD_C89, { T89_V,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "-w",        "c",  NULL },
+  { "n",   1, STD_C89, { T89_I,   T99_SC,  T89_S,   T89_L,   T9L_LL,  BADLEN,  T99_SST, T99_PD,  T99_IM  }, "",          "W",  NULL },
   /* C99 conversion specifiers.  */
-  { "F",   0, STD_C99, { T99_D,   BADLEN,  BADLEN,  T99_D,   BADLEN,  T99_LD,  BADLEN,  BADLEN,  BADLEN  }, "-wp0 +#'I", ""   },
-  { "aA",  0, STD_C99, { T99_D,   BADLEN,  BADLEN,  T99_D,   BADLEN,  T99_LD,  BADLEN,  BADLEN,  BADLEN  }, "-wp0 +#",   ""   },
+  { "F",   0, STD_C99, { T99_D,   BADLEN,  BADLEN,  T99_D,   BADLEN,  T99_LD,  BADLEN,  BADLEN,  BADLEN  }, "-wp0 +#'I", "",   NULL },
+  { "aA",  0, STD_C99, { T99_D,   BADLEN,  BADLEN,  T99_D,   BADLEN,  T99_LD,  BADLEN,  BADLEN,  BADLEN  }, "-wp0 +#",   "",   NULL },
   /* X/Open conversion specifiers.  */
-  { "C",   0, STD_EXT, { TEX_WI,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "-w",        ""   },
-  { "S",   1, STD_EXT, { TEX_W,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "-wp",       "R"  },
+  { "C",   0, STD_EXT, { TEX_WI,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "-w",        "",   NULL },
+  { "S",   1, STD_EXT, { TEX_W,   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  }, "-wp",       ""   },
-  { NULL,  0, 0, NOLENGTHS, NULL, NULL }
+  { "m",   0, STD_EXT, { T89_V,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "-wp",       "",   NULL },
+  { NULL,  0, 0, NOLENGTHS, NULL, NULL, NULL }
 };
 
 static const format_char_info asm_fprintf_char_table[] =
 {
   /* C89 conversion specifiers.  */
-  { "di",  0, STD_C89, { T89_I,   BADLEN,  BADLEN,  T89_L,   T9L_LL,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "-wp0 +",  "i" },
-  { "oxX", 0, STD_C89, { T89_UI,  BADLEN,  BADLEN,  T89_UL,  T9L_ULL, BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "-wp0#",   "i" },
-  { "u",   0, STD_C89, { T89_UI,  BADLEN,  BADLEN,  T89_UL,  T9L_ULL, BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "-wp0",    "i" },
-  { "c",   0, STD_C89, { T89_I,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "-w",       "" },
-  { "s",   1, STD_C89, { T89_C,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "-wp",    "cR" },
+  { "di",  0, STD_C89, { T89_I,   BADLEN,  BADLEN,  T89_L,   T9L_LL,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "-wp0 +",  "i", NULL },
+  { "oxX", 0, STD_C89, { T89_UI,  BADLEN,  BADLEN,  T89_UL,  T9L_ULL, BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "-wp0#",   "i", NULL },
+  { "u",   0, STD_C89, { T89_UI,  BADLEN,  BADLEN,  T89_UL,  T9L_ULL, BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "-wp0",    "i", NULL },
+  { "c",   0, STD_C89, { T89_I,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "-w",       "", NULL },
+  { "s",   1, STD_C89, { T89_C,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "-wp",    "cR", NULL },
 
   /* asm_fprintf conversion specifiers.  */
-  { "O",   0, STD_C89, NOARGUMENTS, "",      ""   },
-  { "R",   0, STD_C89, NOARGUMENTS, "",      ""   },
-  { "I",   0, STD_C89, NOARGUMENTS, "",      ""   },
-  { "L",   0, STD_C89, NOARGUMENTS, "",      ""   },
-  { "U",   0, STD_C89, NOARGUMENTS, "",      ""   },
-  { "r",   0, STD_C89, { T89_I,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "",  "" },
-  { "@",   0, STD_C89, NOARGUMENTS, "",      ""   },
-  { NULL,  0, 0, NOLENGTHS, NULL, NULL }
+  { "O",   0, STD_C89, NOARGUMENTS, "",      "",   NULL },
+  { "R",   0, STD_C89, NOARGUMENTS, "",      "",   NULL },
+  { "I",   0, STD_C89, NOARGUMENTS, "",      "",   NULL },
+  { "L",   0, STD_C89, NOARGUMENTS, "",      "",   NULL },
+  { "U",   0, STD_C89, NOARGUMENTS, "",      "",   NULL },
+  { "r",   0, STD_C89, { T89_I,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "",  "", NULL },
+  { "@",   0, STD_C89, NOARGUMENTS, "",      "",   NULL },
+  { NULL,  0, 0, NOLENGTHS, NULL, NULL, NULL }
 };
 
 static const format_char_info gcc_diag_char_table[] =
 {
   /* C89 conversion specifiers.  */
-  { "di",  0, STD_C89, { T89_I,   BADLEN,  BADLEN,  T89_L,   T9L_LL,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  ""   },
-  { "ox",  0, STD_C89, { T89_UI,  BADLEN,  BADLEN,  T89_UL,  T9L_ULL, BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  ""   },
-  { "u",   0, STD_C89, { T89_UI,  BADLEN,  BADLEN,  T89_UL,  T9L_ULL, BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  ""   },
-  { "c",   0, STD_C89, { T89_I,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  ""   },
-  { "s",   1, STD_C89, { T89_C,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "pq", "cR" },
-  { "p",   1, STD_C89, { T89_V,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  "c"  },
+  { "di",  0, STD_C89, { T89_I,   BADLEN,  BADLEN,  T89_L,   T9L_LL,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  "",   NULL },
+  { "ox",  0, STD_C89, { T89_UI,  BADLEN,  BADLEN,  T89_UL,  T9L_ULL, BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  "",   NULL },
+  { "u",   0, STD_C89, { T89_UI,  BADLEN,  BADLEN,  T89_UL,  T9L_ULL, BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  "",   NULL },
+  { "c",   0, STD_C89, { T89_I,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  "",   NULL },
+  { "s",   1, STD_C89, { T89_C,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "pq", "cR", NULL },
+  { "p",   1, STD_C89, { T89_V,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  "c",  NULL },
 
   /* 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",  ""   },
+  { "H",   0, STD_C89, { T89_V,   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  }, "q",    ""   },
+  { "J", 0, STD_C89, { T89_V,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",    "",   NULL },
 
-  { "<>'", 0, STD_C89, NOARGUMENTS, "",      ""   },
-  { "m",   0, STD_C89, NOARGUMENTS, "q",     ""   },
-  { NULL,  0, 0, NOLENGTHS, NULL, NULL }
+  { "<>'", 0, STD_C89, NOARGUMENTS, "",      "",   NULL },
+  { "m",   0, STD_C89, NOARGUMENTS, "q",     "",   NULL },
+  { NULL,  0, 0, NOLENGTHS, NULL, NULL, NULL }
 };
 
 static const format_char_info gcc_cdiag_char_table[] =
 {
   /* C89 conversion specifiers.  */
-  { "di",  0, STD_C89, { T89_I,   BADLEN,  BADLEN,  T89_L,   T9L_LL,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  ""   },
-  { "ox",  0, STD_C89, { T89_UI,  BADLEN,  BADLEN,  T89_UL,  T9L_ULL, BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  ""   },
-  { "u",   0, STD_C89, { T89_UI,  BADLEN,  BADLEN,  T89_UL,  T9L_ULL, BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  ""   },
-  { "c",   0, STD_C89, { T89_I,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  ""   },
-  { "s",   1, STD_C89, { T89_C,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "pq", "cR" },
-  { "p",   1, STD_C89, { T89_V,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  "c"  },
+  { "di",  0, STD_C89, { T89_I,   BADLEN,  BADLEN,  T89_L,   T9L_LL,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  "",   NULL },
+  { "ox",  0, STD_C89, { T89_UI,  BADLEN,  BADLEN,  T89_UL,  T9L_ULL, BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  "",   NULL },
+  { "u",   0, STD_C89, { T89_UI,  BADLEN,  BADLEN,  T89_UL,  T9L_ULL, BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  "",   NULL },
+  { "c",   0, STD_C89, { T89_I,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  "",   NULL },
+  { "s",   1, STD_C89, { T89_C,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "pq", "cR", NULL },
+  { "p",   1, STD_C89, { T89_V,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  "c",  NULL },
 
   /* 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",  ""   },
+  { "H",   0, STD_C89, { T89_V,   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  }, "q", ""   },
+  { "DEFJT", 0, STD_C89, { T89_V,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q", "",   NULL },
 
-  { "<>'", 0, STD_C89, NOARGUMENTS, "",      ""   },
-  { "m",   0, STD_C89, NOARGUMENTS, "q",     ""   },
-  { NULL,  0, 0, NOLENGTHS, NULL, NULL }
+  { "<>'", 0, STD_C89, NOARGUMENTS, "",      "",   NULL },
+  { "m",   0, STD_C89, NOARGUMENTS, "q",     "",   NULL },
+  { NULL,  0, 0, NOLENGTHS, NULL, NULL, NULL }
 };
 
 static const format_char_info gcc_cxxdiag_char_table[] =
 {
   /* C89 conversion specifiers.  */
-  { "di",  0, STD_C89, { T89_I,   BADLEN,  BADLEN,  T89_L,   T9L_LL,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  ""   },
-  { "ox",  0, STD_C89, { T89_UI,  BADLEN,  BADLEN,  T89_UL,  T9L_ULL, BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  ""   },
-  { "u",   0, STD_C89, { T89_UI,  BADLEN,  BADLEN,  T89_UL,  T9L_ULL, BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  ""   },
-  { "c",   0, STD_C89, { T89_I,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  ""   },
-  { "s",   1, STD_C89, { T89_C,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "pq", "cR" },
-  { "p",   1, STD_C89, { T89_V,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  "c"  },
+  { "di",  0, STD_C89, { T89_I,   BADLEN,  BADLEN,  T89_L,   T9L_LL,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  "",   NULL },
+  { "ox",  0, STD_C89, { T89_UI,  BADLEN,  BADLEN,  T89_UL,  T9L_ULL, BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  "",   NULL },
+  { "u",   0, STD_C89, { T89_UI,  BADLEN,  BADLEN,  T89_UL,  T9L_ULL, BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  "",   NULL },
+  { "c",   0, STD_C89, { T89_I,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  "",   NULL },
+  { "s",   1, STD_C89, { T89_C,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "pq", "cR", NULL },
+  { "p",   1, STD_C89, { T89_V,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  "c",  NULL },
 
   /* 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",  ""   },
+  { "H",   0, STD_C89, { T89_V,   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  }, "q+#",   ""   },
+  { "ADEFJTV",0,STD_C89,{ T89_V,   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  }, "q",  ""   },
+  { "CLOPQ",0,STD_C89, { T89_I,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  "",   NULL },
 
-  { "<>'", 0, STD_C89, NOARGUMENTS, "",      ""   },
-  { "m",   0, STD_C89, NOARGUMENTS, "q",     ""   },
-  { NULL,  0, 0, NOLENGTHS, NULL, NULL }
+  { "<>'", 0, STD_C89, NOARGUMENTS, "",      "",   NULL },
+  { "m",   0, STD_C89, NOARGUMENTS, "q",     "",   NULL },
+  { NULL,  0, 0, NOLENGTHS, NULL, NULL, NULL }
 };
 
 static const format_char_info scan_char_table[] =
 {
   /* C89 conversion specifiers.  */
-  { "di",    1, STD_C89, { T89_I,   T99_SC,  T89_S,   T89_L,   T9L_LL,  TEX_LL,  T99_SST, T99_PD,  T99_IM  }, "*w'I", "W"   },
-  { "u",     1, STD_C89, { T89_UI,  T99_UC,  T89_US,  T89_UL,  T9L_ULL, TEX_ULL, T99_ST,  T99_UPD, T99_UIM }, "*w'I", "W"   },
-  { "oxX",   1, STD_C89, { T89_UI,  T99_UC,  T89_US,  T89_UL,  T9L_ULL, TEX_ULL, T99_ST,  T99_UPD, T99_UIM }, "*w",   "W"   },
-  { "efgEG", 1, STD_C89, { T89_F,   BADLEN,  BADLEN,  T89_D,   BADLEN,  T89_LD,  BADLEN,  BADLEN,  BADLEN  }, "*w'",  "W"   },
-  { "c",     1, STD_C89, { T89_C,   BADLEN,  BADLEN,  T94_W,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "*w",   "cW"  },
-  { "s",     1, STD_C89, { T89_C,   BADLEN,  BADLEN,  T94_W,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "*aw",  "cW"  },
-  { "[",     1, STD_C89, { T89_C,   BADLEN,  BADLEN,  T94_W,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "*aw",  "cW[" },
-  { "p",     2, STD_C89, { T89_V,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "*w",   "W"   },
-  { "n",     1, STD_C89, { T89_I,   T99_SC,  T89_S,   T89_L,   T9L_LL,  BADLEN,  T99_SST, T99_PD,  T99_IM  }, "",     "W"   },
+  { "di",    1, STD_C89, { T89_I,   T99_SC,  T89_S,   T89_L,   T9L_LL,  TEX_LL,  T99_SST, T99_PD,  T99_IM  }, "*w'I", "W",   NULL },
+  { "u",     1, STD_C89, { T89_UI,  T99_UC,  T89_US,  T89_UL,  T9L_ULL, TEX_ULL, T99_ST,  T99_UPD, T99_UIM }, "*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 }, "*w",   "W",   NULL },
+  { "efgEG", 1, STD_C89, { T89_F,   BADLEN,  BADLEN,  T89_D,   BADLEN,  T89_LD,  BADLEN,  BADLEN,  BADLEN  }, "*w'",  "W",   NULL },
+  { "c",     1, STD_C89, { T89_C,   BADLEN,  BADLEN,  T94_W,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "*w",   "cW",  NULL },
+  { "s",     1, STD_C89, { T89_C,   BADLEN,  BADLEN,  T94_W,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "*aw",  "cW",  NULL },
+  { "[",     1, STD_C89, { T89_C,   BADLEN,  BADLEN,  T94_W,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "*aw",  "cW[", NULL },
+  { "p",     2, STD_C89, { T89_V,   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  }, "",     "W",   NULL },
   /* C99 conversion specifiers.  */
-  { "FaA",   1, STD_C99, { T99_F,   BADLEN,  BADLEN,  T99_D,   BADLEN,  T99_LD,  BADLEN,  BADLEN,  BADLEN  }, "*w'",  "W"   },
+  { "FaA",   1, STD_C99, { T99_F,   BADLEN,  BADLEN,  T99_D,   BADLEN,  T99_LD,  BADLEN,  BADLEN,  BADLEN  }, "*w'",  "W",   NULL },
   /* X/Open conversion specifiers.  */
-  { "C",     1, STD_EXT, { TEX_W,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "*w",   "W"   },
-  { "S",     1, STD_EXT, { TEX_W,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "*aw",  "W"   },
-  { NULL, 0, 0, NOLENGTHS, NULL, NULL }
+  { "C",     1, STD_EXT, { TEX_W,   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  }, "*aw",  "W",   NULL },
+  { NULL, 0, 0, NOLENGTHS, NULL, NULL, NULL }
 };
 
 static const format_char_info time_char_table[] =
 {
   /* C89 conversion specifiers.  */
-  { "ABZab",           0, STD_C89, NOLENGTHS, "^#",     ""   },
-  { "cx",              0, STD_C89, NOLENGTHS, "E",      "3"  },
-  { "HIMSUWdmw",       0, STD_C89, NOLENGTHS, "-_0Ow",  ""   },
-  { "j",               0, STD_C89, NOLENGTHS, "-_0Ow",  "o"  },
-  { "p",               0, STD_C89, NOLENGTHS, "#",      ""   },
-  { "X",               0, STD_C89, NOLENGTHS, "E",      ""   },
-  { "y",               0, STD_C89, NOLENGTHS, "EO-_0w", "4"  },
-  { "Y",               0, STD_C89, NOLENGTHS, "-_0EOw", "o"  },
-  { "%",               0, STD_C89, NOLENGTHS, "",       ""   },
+  { "ABZab",           0, STD_C89, NOLENGTHS, "^#",     "",   NULL },
+  { "cx",              0, STD_C89, NOLENGTHS, "E",      "3",  NULL },
+  { "HIMSUWdmw",       0, STD_C89, NOLENGTHS, "-_0Ow",  "",   NULL },
+  { "j",               0, STD_C89, NOLENGTHS, "-_0Ow",  "o",  NULL },
+  { "p",               0, STD_C89, NOLENGTHS, "#",      "",   NULL },
+  { "X",               0, STD_C89, NOLENGTHS, "E",      "",   NULL },
+  { "y",               0, STD_C89, NOLENGTHS, "EO-_0w", "4",  NULL },
+  { "Y",               0, STD_C89, NOLENGTHS, "-_0EOw", "o",  NULL },
+  { "%",               0, STD_C89, NOLENGTHS, "",       "",   NULL },
   /* C99 conversion specifiers.  */
-  { "C",               0, STD_C99, NOLENGTHS, "-_0EOw", "o"  },
-  { "D",               0, STD_C99, NOLENGTHS, "",       "2"  },
-  { "eVu",             0, STD_C99, NOLENGTHS, "-_0Ow",  ""   },
-  { "FRTnrt",          0, STD_C99, NOLENGTHS, "",       ""   },
-  { "g",               0, STD_C99, NOLENGTHS, "O-_0w",  "2o" },
-  { "G",               0, STD_C99, NOLENGTHS, "-_0Ow",  "o"  },
-  { "h",               0, STD_C99, NOLENGTHS, "^#",     ""   },
-  { "z",               0, STD_C99, NOLENGTHS, "O",      "o"  },
+  { "C",               0, STD_C99, NOLENGTHS, "-_0EOw", "o",  NULL },
+  { "D",               0, STD_C99, NOLENGTHS, "",       "2",  NULL },
+  { "eVu",             0, STD_C99, NOLENGTHS, "-_0Ow",  "",   NULL },
+  { "FRTnrt",          0, STD_C99, NOLENGTHS, "",       "",   NULL },
+  { "g",               0, STD_C99, NOLENGTHS, "O-_0w",  "2o", NULL },
+  { "G",               0, STD_C99, NOLENGTHS, "-_0Ow",  "o",  NULL },
+  { "h",               0, STD_C99, NOLENGTHS, "^#",     "",   NULL },
+  { "z",               0, STD_C99, NOLENGTHS, "O",      "o",  NULL },
   /* GNU conversion specifiers.  */
-  { "kls",             0, STD_EXT, NOLENGTHS, "-_0Ow",  ""   },
-  { "P",               0, STD_EXT, NOLENGTHS, "",       ""   },
-  { NULL,              0, 0, NOLENGTHS, NULL, NULL }
+  { "kls",             0, STD_EXT, NOLENGTHS, "-_0Ow",  "",   NULL },
+  { "P",               0, STD_EXT, NOLENGTHS, "",       "",   NULL },
+  { NULL,              0, 0, 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 }, "=^+(!-w#p", "" },
-  { NULL, 0, 0, NOLENGTHS, NULL, NULL }
+  { "in", 0, STD_C89, { T89_D, BADLEN, BADLEN, BADLEN, BADLEN, T89_LD, BADLEN, BADLEN, BADLEN }, "=^+(!-w#p", "", NULL },
+  { NULL, 0, 0, NOLENGTHS, NULL, NULL, NULL }
 };
 
-
 /* This must be in the same order as enum format_type.  */
 static const format_kind_info format_types_orig[] =
 {
@@ -976,9 +707,12 @@ static const format_kind_info format_types_orig[] =
    new data if necessary, while still allowing the original data to be
    const.  */
 static const format_kind_info *format_types = format_types_orig;
-/* We can modify this one.  */
+/* We can modify this one.  We also add target-specific format types
+   to the end of the array.  */
 static format_kind_info *dynamic_format_types;
 
+static int n_format_types = ARRAY_SIZE (format_types_orig);
+
 /* Structure detailing the results of checking a format function call
    where the format expression may be a conditional expression with
    many leaves resulting from nested conditional expressions.  */
@@ -1036,24 +770,24 @@ static void format_type_warning (const char *, const char *, int, tree,
 /* Decode a format type from a string, returning the type, or
    format_type_error if not valid, in which case the caller should print an
    error message.  */
-static enum format_type
+static int
 decode_format_type (const char *s)
 {
   int i;
   int slen;
   slen = strlen (s);
-  for (i = 0; i < (int) format_type_error; i++)
+  for (i = 0; i < n_format_types; i++)
     {
       int alen;
       if (!strcmp (s, format_types[i].name))
-       break;
+       return i;
       alen = strlen (format_types[i].name);
       if (slen == alen + 4 && s[0] == '_' && s[1] == '_'
          && s[slen - 1] == '_' && s[slen - 2] == '_'
          && !strncmp (s + 2, format_types[i].name, alen))
-       break;
+       return i;
     }
-  return ((enum format_type) i);
+  return format_type_error;
 }
 
 \f
@@ -2188,6 +1922,8 @@ check_format_info_main (format_check_results *res,
            }
        }
 
+      main_wanted_type.next = NULL;
+
       /* Finally. . .check type of argument against desired type!  */
       if (info->first_arg_num == 0)
        continue;
@@ -2204,6 +1940,8 @@ check_format_info_main (format_check_results *res,
        }
       else
        {
+         format_wanted_type *wanted_type_ptr;
+
          if (main_arg_num != 0)
            {
              arg_num = main_arg_num;
@@ -2219,46 +1957,72 @@ check_format_info_main (format_check_results *res,
                }
              else
                has_operand_number = 0;
+           }
+
+         wanted_type_ptr = &main_wanted_type;
+         while (fci)
+           {
              if (params == 0)
                {
                  warning ("too few arguments for format");
                  return;
                }
+
+             cur_param = TREE_VALUE (params);
+             params = TREE_CHAIN (params);
+
+             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->char_lenient_flag = 0;
+             if (strchr (fci->flags2, 'c') != 0)
+               wanted_type_ptr->char_lenient_flag = 1;
+             wanted_type_ptr->writing_in_flag = 0;
+             wanted_type_ptr->reading_from_flag = 0;
+             if (aflag)
+               wanted_type_ptr->writing_in_flag = 1;
+             else
+               {
+                 if (strchr (fci->flags2, 'W') != 0)
+                   wanted_type_ptr->writing_in_flag = 1;
+                 if (strchr (fci->flags2, 'R') != 0)
+                   wanted_type_ptr->reading_from_flag = 1;
+               }
+             wanted_type_ptr->name = NULL;
+             wanted_type_ptr->param = cur_param;
+             wanted_type_ptr->arg_num = arg_num;
+             wanted_type_ptr->next = NULL;
+             if (last_wanted_type != 0)
+               last_wanted_type->next = wanted_type_ptr;
+             if (first_wanted_type == 0)
+               first_wanted_type = wanted_type_ptr;
+             last_wanted_type = wanted_type_ptr;
+
+             fci = fci->chain;
+             if (fci)
+               {
+                 wanted_type_ptr = ggc_alloc (sizeof (main_wanted_type));
+                 arg_num++;
+                 wanted_type = *fci->types[length_chars_val].type;
+                 wanted_type_name = fci->types[length_chars_val].name;
+               }
            }
-         cur_param = TREE_VALUE (params);
-         params = TREE_CHAIN (params);
-         main_wanted_type.wanted_type = wanted_type;
-         main_wanted_type.wanted_type_name = wanted_type_name;
-         main_wanted_type.pointer_count = fci->pointer_count + aflag;
-         main_wanted_type.char_lenient_flag = 0;
-         if (strchr (fci->flags2, 'c') != 0)
-           main_wanted_type.char_lenient_flag = 1;
-         main_wanted_type.writing_in_flag = 0;
-         main_wanted_type.reading_from_flag = 0;
-         if (aflag)
-           main_wanted_type.writing_in_flag = 1;
-         else
-           {
-             if (strchr (fci->flags2, 'W') != 0)
-               main_wanted_type.writing_in_flag = 1;
-             if (strchr (fci->flags2, 'R') != 0)
-               main_wanted_type.reading_from_flag = 1;
-           }
-         main_wanted_type.name = NULL;
-         main_wanted_type.param = cur_param;
-         main_wanted_type.arg_num = arg_num;
-         main_wanted_type.next = NULL;
-         if (last_wanted_type != 0)
-           last_wanted_type->next = &main_wanted_type;
-         if (first_wanted_type == 0)
-           first_wanted_type = &main_wanted_type;
-         last_wanted_type = &main_wanted_type;
        }
 
       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;
+           }
+       }
     }
 }
 
@@ -2686,6 +2450,10 @@ init_dynamic_diag_info (void)
     }
 }
 
+#ifdef TARGET_FORMAT_TYPES
+extern const format_kind_info TARGET_FORMAT_TYPES[];
+#endif
+
 /* Handle a "format" attribute; arguments as in
    struct attribute_spec.handler.  */
 tree
@@ -2696,6 +2464,23 @@ handle_format_attribute (tree *node, tree name ATTRIBUTE_UNUSED, tree args,
   function_format_info info;
   tree argument;
 
+#ifdef TARGET_FORMAT_TYPES
+  /* If the target provides additional format types, we need to
+     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]));
+      memcpy (dynamic_format_types, format_types_orig,
+             sizeof (format_types_orig));
+      memcpy (&dynamic_format_types[n_format_types], TARGET_FORMAT_TYPES,
+             TARGET_N_FORMAT_TYPES * sizeof (dynamic_format_types[0]));
+
+      format_types = dynamic_format_types;
+      n_format_types += TARGET_N_FORMAT_TYPES;
+    }
+#endif
+
   if (!decode_format_attr (args, &info, 0))
     {
       *no_add_attrs = true;
diff --git a/gcc/c-format.h b/gcc/c-format.h
new file mode 100644 (file)
index 0000000..a1861b5
--- /dev/null
@@ -0,0 +1,299 @@
+/* Check calls to formatted I/O functions (-Wformat).
+   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
+   2001, 2002, 2003, 2004 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
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+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, 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+#ifndef GCC_C_FORMAT_H
+#define GCC_C_FORMAT_H
+
+/* The meaningfully distinct length modifiers for format checking recognized
+   by GCC.  */
+enum format_lengths
+{
+  FMT_LEN_none,
+  FMT_LEN_hh,
+  FMT_LEN_h,
+  FMT_LEN_l,
+  FMT_LEN_ll,
+  FMT_LEN_L,
+  FMT_LEN_z,
+  FMT_LEN_t,
+  FMT_LEN_j,
+  FMT_LEN_MAX
+};
+
+
+/* The standard versions in which various format features appeared.  */
+enum format_std_version
+{
+  STD_C89,
+  STD_C94,
+  STD_C9L, /* C99, but treat as C89 if -Wno-long-long.  */
+  STD_C99,
+  STD_EXT
+};
+
+/* Flags that may apply to a particular kind of format checked by GCC.  */
+enum
+{
+  /* This format converts arguments of types determined by the
+     format string.  */
+  FMT_FLAG_ARG_CONVERT = 1,
+  /* The scanf allocation 'a' kludge applies to this format kind.  */
+  FMT_FLAG_SCANF_A_KLUDGE = 2,
+  /* A % during parsing a specifier is allowed to be a modified % rather
+     that indicating the format is broken and we are out-of-sync.  */
+  FMT_FLAG_FANCY_PERCENT_OK = 4,
+  /* With $ operand numbers, it is OK to reference the same argument more
+     than once.  */
+  FMT_FLAG_DOLLAR_MULTIPLE = 8,
+  /* This format type uses $ operand numbers (strfmon doesn't).  */
+  FMT_FLAG_USE_DOLLAR = 16,
+  /* Zero width is bad in this type of format (scanf).  */
+  FMT_FLAG_ZERO_WIDTH_BAD = 32,
+  /* Empty precision specification is OK in this type of format (printf).  */
+  FMT_FLAG_EMPTY_PREC_OK = 64,
+  /* Gaps are allowed in the arguments with $ operand numbers if all
+     arguments are pointers (scanf).  */
+  FMT_FLAG_DOLLAR_GAP_POINTER_OK = 128
+  /* Not included here: details of whether width or precision may occur
+     (controlled by width_char and precision_char); details of whether
+     '*' can be used for these (width_type and precision_type); details
+     of whether length modifiers can occur (length_char_specs).  */
+};
+
+
+/* Structure describing a length modifier supported in format checking, and
+   possibly a doubled version such as "hh".  */
+typedef struct
+{
+  /* Name of the single-character length modifier.  */
+  const char *name;
+  /* Index into a format_char_info.types array.  */
+  enum format_lengths index;
+  /* Standard version this length appears in.  */
+  enum format_std_version std;
+  /* Same, if the modifier can be repeated, or NULL if it can't.  */
+  const char *double_name;
+  enum format_lengths double_index;
+  enum format_std_version double_std;
+} format_length_info;
+
+
+/* Structure describing the combination of a conversion specifier
+   (or a set of specifiers which act identically) and a length modifier.  */
+typedef struct
+{
+  /* The standard version this combination of length and type appeared in.
+     This is only relevant if greater than those for length and type
+     individually; otherwise it is ignored.  */
+  enum format_std_version std;
+  /* The name to use for the type, if different from that generated internally
+     (e.g., "signed size_t").  */
+  const char *name;
+  /* The type itself.  */
+  tree *type;
+} format_type_detail;
+
+
+/* Macros to fill out tables of these.  */
+#define NOARGUMENTS    { T89_V, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }
+#define BADLEN { 0, NULL, NULL }
+#define NOLENGTHS      { BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }
+
+
+/* Structure describing a format conversion specifier (or a set of specifiers
+   which act identically), and the length modifiers used with it.  */
+typedef struct format_char_info
+{
+  const char *format_chars;
+  int pointer_count;
+  enum format_std_version std;
+  /* Types accepted for each length modifier.  */
+  format_type_detail types[FMT_LEN_MAX];
+  /* List of other modifier characters allowed with these specifiers.
+     This lists flags, and additionally "w" for width, "p" for precision
+     (right precision, for strfmon), "#" for left precision (strfmon),
+     "a" for scanf "a" allocation extension (not applicable in C99 mode),
+     "*" for scanf suppression, and "E" and "O" for those strftime
+     modifiers.  */
+  const char *flag_chars;
+  /* List of additional flags describing these conversion specifiers.
+     "c" for generic character pointers being allowed, "2" for strftime
+     two digit year formats, "3" for strftime formats giving two digit
+     years in some locales, "4" for "2" which becomes "3" with an "E" modifier,
+     "o" if use of strftime "O" is a GNU extension beyond C99,
+     "W" if the argument is a pointer which is dereferenced and written into,
+     "R" if the argument is a pointer which is dereferenced and read from,
+     "i" for printf integer formats where the '0' flag is ignored with
+     precision, and "[" for the starting character of a scanf scanset.  */
+  const char *flags2;
+  /* If this format conversion character consumes more than one argument,
+     CHAIN points to information about the next argument.  For later
+     arguments, only POINTER_COUNT, TYPES, and the "c", "R", and "W" flags
+     in FLAGS2 are used.  */
+  const struct format_char_info *chain;
+} format_char_info;
+
+
+/* Structure describing a flag accepted by some kind of format.  */
+typedef struct
+{
+  /* The flag character in question (0 for end of array).  */
+  int flag_char;
+  /* Zero if this entry describes the flag character in general, or a
+     nonzero character that may be found in flags2 if it describes the
+     flag when used with certain formats only.  If the latter, only
+     the first such entry found that applies to the current conversion
+     specifier is used; the values of `name' and `long_name' it supplies
+     will be used, if non-NULL and the standard version is higher than
+     the unpredicated one, for any pedantic warning.  For example, 'o'
+     for strftime formats (meaning 'O' is an extension over C99).  */
+  int predicate;
+  /* Nonzero if the next character after this flag in the format should
+     be skipped ('=' in strfmon), zero otherwise.  */
+  int skip_next_char;
+  /* The name to use for this flag in diagnostic messages.  For example,
+     N_("`0' flag"), N_("field width").  */
+  const char *name;
+  /* Long name for this flag in diagnostic messages; currently only used for
+     "ISO C does not support ...".  For example, N_("the `I' printf flag").  */
+  const char *long_name;
+  /* The standard version in which it appeared.  */
+  enum format_std_version std;
+} format_flag_spec;
+
+
+/* Structure describing a combination of flags that is bad for some kind
+   of format.  */
+typedef struct
+{
+  /* The first flag character in question (0 for end of array).  */
+  int flag_char1;
+  /* The second flag character.  */
+  int flag_char2;
+  /* Nonzero if the message should say that the first flag is ignored with
+     the second, zero if the combination should simply be objected to.  */
+  int ignored;
+  /* Zero if this entry applies whenever this flag combination occurs,
+     a nonzero character from flags2 if it only applies in some
+     circumstances (e.g. 'i' for printf formats ignoring 0 with precision).  */
+  int predicate;
+} format_flag_pair;
+
+
+/* Structure describing a particular kind of format processed by GCC.  */
+typedef struct
+{
+  /* The name of this kind of format, for use in diagnostics.  Also
+     the name of the attribute (without preceding and following __).  */
+  const char *name;
+  /* Specifications of the length modifiers accepted; possibly NULL.  */
+  const format_length_info *length_char_specs;
+  /* Details of the conversion specification characters accepted.  */
+  const format_char_info *conversion_specs;
+  /* String listing the flag characters that are accepted.  */
+  const char *flag_chars;
+  /* String listing modifier characters (strftime) accepted.  May be NULL.  */
+  const char *modifier_chars;
+  /* Details of the flag characters, including pseudo-flags.  */
+  const format_flag_spec *flag_specs;
+  /* Details of bad combinations of flags.  */
+  const format_flag_pair *bad_flag_pairs;
+  /* Flags applicable to this kind of format.  */
+  int flags;
+  /* Flag character to treat a width as, or 0 if width not used.  */
+  int width_char;
+  /* Flag character to treat a left precision (strfmon) as,
+     or 0 if left precision not used.  */
+  int left_precision_char;
+  /* Flag character to treat a precision (for strfmon, right precision) as,
+     or 0 if precision not used.  */
+  int precision_char;
+  /* If a flag character has the effect of suppressing the conversion of
+     an argument ('*' in scanf), that flag character, otherwise 0.  */
+  int suppression_char;
+  /* Flag character to treat a length modifier as (ignored if length
+     modifiers not used).  Need not be placed in flag_chars for conversion
+     specifiers, but is used to check for bad combinations such as length
+     modifier with assignment suppression in scanf.  */
+  int length_code_char;
+  /* Pointer to type of argument expected if '*' is used for a width,
+     or NULL if '*' not used for widths.  */
+  tree *width_type;
+  /* Pointer to type of argument expected if '*' is used for a precision,
+     or NULL if '*' not used for precisions.  */
+  tree *precision_type;
+} format_kind_info;
+
+#define T_I    &integer_type_node
+#define T89_I  { STD_C89, NULL, T_I }
+#define T_L    &long_integer_type_node
+#define T89_L  { STD_C89, NULL, T_L }
+#define T_LL   &long_long_integer_type_node
+#define T9L_LL { STD_C9L, NULL, T_LL }
+#define TEX_LL { STD_EXT, NULL, T_LL }
+#define T_S    &short_integer_type_node
+#define T89_S  { STD_C89, NULL, T_S }
+#define T_UI   &unsigned_type_node
+#define T89_UI { STD_C89, NULL, T_UI }
+#define T_UL   &long_unsigned_type_node
+#define T89_UL { STD_C89, NULL, T_UL }
+#define T_ULL  &long_long_unsigned_type_node
+#define T9L_ULL        { STD_C9L, NULL, T_ULL }
+#define TEX_ULL        { STD_EXT, NULL, T_ULL }
+#define T_US   &short_unsigned_type_node
+#define T89_US { STD_C89, NULL, T_US }
+#define T_F    &float_type_node
+#define T89_F  { STD_C89, NULL, T_F }
+#define T99_F  { STD_C99, NULL, T_F }
+#define T_D    &double_type_node
+#define T89_D  { STD_C89, NULL, T_D }
+#define T99_D  { STD_C99, NULL, T_D }
+#define T_LD   &long_double_type_node
+#define T89_LD { STD_C89, NULL, T_LD }
+#define T99_LD { STD_C99, NULL, T_LD }
+#define T_C    &char_type_node
+#define T89_C  { STD_C89, NULL, T_C }
+#define T_SC   &signed_char_type_node
+#define T99_SC { STD_C99, NULL, T_SC }
+#define T_UC   &unsigned_char_type_node
+#define T99_UC { STD_C99, NULL, T_UC }
+#define T_V    &void_type_node
+#define T89_V  { STD_C89, NULL, T_V }
+#define T_W    &wchar_type_node
+#define T94_W  { STD_C94, "wchar_t", T_W }
+#define TEX_W  { STD_EXT, "wchar_t", T_W }
+#define T_WI   &wint_type_node
+#define T94_WI { STD_C94, "wint_t", T_WI }
+#define TEX_WI { STD_EXT, "wint_t", T_WI }
+#define T_ST    &size_type_node
+#define T99_ST { STD_C99, "size_t", T_ST }
+#define T_SST   &signed_size_type_node
+#define T99_SST        { STD_C99, "signed size_t", T_SST }
+#define T_PD    &ptrdiff_type_node
+#define T99_PD { STD_C99, "ptrdiff_t", T_PD }
+#define T_UPD   &unsigned_ptrdiff_type_node
+#define T99_UPD        { STD_C99, "unsigned ptrdiff_t", T_UPD }
+#define T_IM    &intmax_type_node
+#define T99_IM { STD_C99, "intmax_t", T_IM }
+#define T_UIM   &uintmax_type_node
+#define T99_UIM        { STD_C99, "uintmax_t", T_UIM }
+
+#endif /* GCC_C_FORMAT_H */
index 26b4897ee431b987480a9b9ba82cfcb18f83d783..9ce6d4eda1c41e392a72f9ad7cc9d31a49d73492 100644 (file)
@@ -972,7 +972,9 @@ i[34567]86-*-sco3.2v5*)     # 80386 running SCO Open Server 5
 i[34567]86-*-solaris2*)
        xm_defines="SMALL_ARG_MAX"
        tm_file="${tm_file} i386/unix.h i386/att.h dbxelf.h elfos.h svr4.h i386/sysv4.h sol2.h i386/sol2.h"
-       tmake_file="i386/t-sol2 t-svr4"
+       tmake_file="t-sol2 i386/t-sol2 t-svr4"
+       c_target_objs="sol2-c.o"
+       cxx_target_objs="sol2-c.o"
        if test x$gnu_ld = xyes; then
                tmake_file="$tmake_file t-slibgcc-elf-ver"
        else
@@ -1851,12 +1853,14 @@ sparc64-*-solaris2* | sparcv9-*-solaris2*)
        if test x$gas = xyes; then
                tm_file="${tm_file} sparc/sol2-gas-bi.h"
        fi
-       tmake_file="sparc/t-sol2 sparc/t-sol2-64 sparc/t-crtfm"
+       tmake_file="t-sol2 sparc/t-sol2 sparc/t-sol2-64 sparc/t-crtfm"
        if test x$gnu_ld = xyes; then
                tmake_file="$tmake_file t-slibgcc-elf-ver"
        else
                tmake_file="$tmake_file t-slibgcc-sld"
        fi
+       c_target_objs="sol2-c.o"
+       cxx_target_objs="sol2-c.o"
        extra_parts="crt1.o crti.o crtn.o gcrt1.o crtbegin.o crtend.o"
        case ${enable_threads}:${have_pthread_h}:${have_thread_h} in
          no:*:*) ;;
@@ -1870,7 +1874,7 @@ sparc-*-solaris2*)
        if test x$gnu_ld = xyes; then
                tm_file="${tm_file} sparc/sol2-gld.h"
        fi
-       tmake_file="sparc/t-sol2 sparc/t-crtfm"
+       tmake_file="t-sol2 sparc/t-sol2 sparc/t-crtfm"
        if test x$gnu_ld = xyes; then
                tmake_file="$tmake_file t-slibgcc-elf-ver"
        else
@@ -1894,6 +1898,8 @@ sparc-*-solaris2*)
                need_64bit_hwint=yes
                ;;
        esac
+       c_target_objs="sol2-c.o"
+       cxx_target_objs="sol2-c.o"
        extra_parts="crt1.o crti.o crtn.o gcrt1.o gmon.o crtbegin.o crtend.o"
        case ${enable_threads}:${have_pthread_h}:${have_thread_h} in
          no:*:*) ;;
diff --git a/gcc/config/sol2-c.c b/gcc/config/sol2-c.c
new file mode 100644 (file)
index 0000000..70fdb37
--- /dev/null
@@ -0,0 +1,72 @@
+/* Solaris support needed only by C/C++ frontends.
+   Copyright (C) 2004  Free Software Foundation, Inc.
+   Contributed by CodeSourcery, LLC.
+
+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 version.
+
+GCC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or 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, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tree.h"
+
+#include "c-format.h"
+#include "intl.h"
+
+/* cmn_err only accepts "l" and "ll".  */
+static const format_length_info cmn_err_length_specs[] =
+{
+  { "l", FMT_LEN_l, STD_C89, "ll", FMT_LEN_ll, STD_C89 },
+  { NULL, 0, 0, NULL, 0, 0 }
+};
+
+static const format_flag_spec cmn_err_flag_specs[] =
+{
+  { 'w',  0, 0, N_("field width"),     N_("field width 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 }
+};
+
+
+static const format_flag_pair cmn_err_flag_pairs[] =
+{
+  { 0, 0, 0, 0 }
+};
+
+static const format_char_info bitfield_string_type =
+  { "b",   1, STD_C89, { T89_C,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "",   "cR", NULL };
+
+static const format_char_info cmn_err_char_table[] =
+{
+  /* C89 conversion specifiers.  */
+  { "dD",  0, STD_C89, { T89_I,   BADLEN,  BADLEN,  T89_L,   T9L_LL,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "w",  "",   NULL },
+  { "oOxX",0, STD_C89, { T89_UI,  BADLEN,  BADLEN,  T89_UL,  T9L_ULL, BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "w",  "",   NULL },
+  { "u",   0, STD_C89, { T89_UI,  BADLEN,  BADLEN,  T89_UL,  T9L_ULL, BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "w",  "",   NULL },
+  { "c",   0, STD_C89, { T89_C,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "w",  "",   NULL },
+  { "s",   1, STD_C89, { T89_C,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "w",  "cR", NULL },
+  { "b",   0, STD_C89, { T89_I,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "",   "",   &bitfield_string_type },
+  { NULL,  0, 0, NOLENGTHS, NULL, NULL, NULL }
+};
+
+const format_kind_info solaris_format_types[] = {
+  { "cmn_err",  cmn_err_length_specs,  cmn_err_char_table, "", NULL,
+    cmn_err_flag_specs, cmn_err_flag_pairs,
+    FMT_FLAG_ARG_CONVERT|FMT_FLAG_EMPTY_PREC_OK,
+    'w', 0, 0, 0, 'L',
+    &integer_type_node, &integer_type_node
+  }
+};
index aa567bc4780a9513c41283c4efebf4ad8c279449..f595395befbd270f5eafa1dcca229593eee8bd92 100644 (file)
@@ -205,3 +205,6 @@ __enable_execute_stack (void *addr)                                 \
       perror ("mprotect of trampoline code");                          \
   }                                                                    \
 }
+
+#define TARGET_N_FORMAT_TYPES 1
+#define TARGET_FORMAT_TYPES solaris_format_types
index 5f01a34b5905576f94189d8fcc29ca80780cec75..150f0979375d6a0ddaf9bccba2365d52170d501e 100644 (file)
@@ -48,3 +48,7 @@ Boston, MA 02111-1307, USA.  */
 
 #undef SUN_INTEGER_MULTIPLY_64
 #define SUN_INTEGER_MULTIPLY_64 0
+
+/* Don't include Solaris-specific format checks.  */
+#undef TARGET_N_FORMAT_TYPES
+#undef TARGET_FORMAT_TYPES
index 650c3ff907be20d16441fc913d455754ec4596db..16fa8e06a4e3d7312052c19d1f4b45c4f770f283 100644 (file)
@@ -122,3 +122,7 @@ crtbegin.o%s \
 
 #undef PREFERRED_DEBUGGING_TYPE
 #define PREFERRED_DEBUGGING_TYPE DWARF2_DEBUG
+
+/* Don't include Solaris-specific format checks.  */
+#undef TARGET_N_FORMAT_TYPES
+#undef TARGET_FORMAT_TYPES
diff --git a/gcc/config/t-sol2 b/gcc/config/t-sol2
new file mode 100644 (file)
index 0000000..022e470
--- /dev/null
@@ -0,0 +1,5 @@
+# Solaris-specific format checking
+sol2-c.o: $(srcdir)/config/sol2-c.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \
+  tree.h c-format.h intl.h
+       $(CC) -c $(ALL_CFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) \
+         $(srcdir)/config/sol2-c.c
index 1df022107be883a7aa66b871f2c5fa01be48a4be..3374bab49a58b84588c201459349bebbde17164b 100644 (file)
@@ -472,6 +472,7 @@ extensions, accepted by GCC in C89 mode and in C++.
 * Offsetof::            Special syntax for implementing @code{offsetof}.
 * Other Builtins::      Other built-in functions.
 * Target Builtins::     Built-in functions specific to particular targets.
+* Target Format Checks:: Format checks specific to particular targets.
 * Pragmas::             Pragmas accepted by GCC.
 * Unnamed Fields::      Unnamed struct/union fields within structs/unions.
 * Thread-Local::        Per-thread variables.
@@ -2173,6 +2174,10 @@ standard modes, the X/Open function @code{strfmon} is also checked as
 are @code{printf_unlocked} and @code{fprintf_unlocked}.
 @xref{C Dialect Options,,Options Controlling C Dialect}.
 
+The target may provide additional types of format checks.
+@xref{Target Format Checks,,Format Checks Specific to Particular
+Target Machines}.
+
 @item format_arg (@var{string-index})
 @cindex @code{format_arg} function attribute
 @opindex Wformat-nonliteral
@@ -7209,6 +7214,25 @@ vector signed int vec_any_numeric (vector float);
 vector signed int vec_any_out (vector float, vector float);
 @end smallexample
 
+@node Target Format Checks
+@section Format Checks Specific to Particular Target Machines
+
+For some target machines, GCC supports additional options to the
+format attribute
+(@pxref{Function Attributes,,Declaring Attributes of Functions}).
+
+@menu
+* Solaris Format Checks::
+@end menu
+
+@node Solaris Format Checks
+@subsection Solaris Format Checks
+
+Solaris targets support the @code{cmn_err} (or @code{__cmn_err__}) format
+check.  @code{cmn_err} accepts a subset of the standard @code{printf}
+conversions, and the two-argument @code{%b} conversion for displaying
+bit-fields.  See the Solaris man page for @code{cmn_err} for more information.
+
 @node Pragmas
 @section Pragmas Accepted by GCC
 @cindex pragmas
index 14f756b1c4dc0bb6530ddce1baa63d9c0539e8db..43f2df66ddbf229d9725d9147ca5cf5308a0b6c7 100644 (file)
@@ -2113,7 +2113,7 @@ specified, and that the conversions specified in the format string make
 sense.  This includes standard functions, and others specified by format
 attributes (@pxref{Function Attributes}), in the @code{printf},
 @code{scanf}, @code{strftime} and @code{strfmon} (an X/Open extension,
-not in the C standard) families.
+not in the C standard) families (or other target-specific families).
 
 The formats are checked against the format features supported by GNU
 libc version 2.2.  These include all ISO C90 and C99 features, as well
index d2b834ad6d258c493c2eb846b96007f101d07045..f985e503a6ed373c1a81767afeaff9c01b85b8a8 100644 (file)
@@ -766,6 +766,9 @@ pragmas supported.
 Documentation in @file{gcc/doc/extend.texi} of any target-specific
 built-in functions supported.
 @item
+Documentation in @file{gcc/doc/extend.texi} of any target-specific
+format checking styles supported.
+@item
 Documentation in @file{gcc/doc/md.texi} of any target-specific
 constraint letters (@pxref{Machine Constraints, , Constraints for
 Particular Machines}).
index c7aade3edbf23d5b563cefd3aed145308b91b565..a8c410f8ec8326ea5c85ba30a8bfe08e5555b04c 100644 (file)
@@ -9278,3 +9278,14 @@ for a virtual function @var{fndecl} when constructing thunks,
 functions, if a target supports aliases (ie. defines
 @code{ASM_OUTPUT_DEF}), @code{false} otherwise,
 @end deftypefn
+
+@defmac TARGET_FORMAT_TYPES
+If defined, this macro is the name of a global variable containing
+target-specific format checking information for the @option{-Wformat}
+option.  The default is to have no target-specific format checks.
+@end defmac
+
+@defmac TARGET_N_FORMAT_TYPES
+If defined, this macro is the number of entries in
+@code{TARGET_FORMAT_TYPES}.
+@end defmac
index 6df5bc924787c7b5091bf48f00cd0893773c0acd..1ca5cb5d13c6322936a59e5ddb45547182f6bb57 100644 (file)
@@ -1,3 +1,7 @@
+2004-07-19  Daniel Jacobowitz  <dan@debian.org>
+
+       * gcc.dg/format/cmn-err-1.c: New test.
+
 2004-07-19  Joseph S. Myers  <jsm@polyomino.org.uk>
 
        * g++.dg/warn/Wparentheses-1.C, g++.dg/warn/Wparentheses-2.C,
diff --git a/gcc/testsuite/gcc.dg/format/cmn-err-1.c b/gcc/testsuite/gcc.dg/format/cmn-err-1.c
new file mode 100644 (file)
index 0000000..ac4b1f0
--- /dev/null
@@ -0,0 +1,36 @@
+/* { dg-do compile { target *-*-solaris2.* } } */
+/* { dg-options "-Wformat" } */
+
+#include "format.h"
+
+void cmn_err_func (int level, char * format, ...)
+  __attribute__((format (cmn_err, 2, 3)));
+
+void cmn_err_func (int level, char * format, ...)
+{
+}
+
+const char *string = "foo";
+
+int main()
+{
+  int i = 1;
+  long l = 2;
+  llong ll = 3;
+
+  cmn_err_func (0, "%s", string);
+  cmn_err_func (0, "%d %D %o %O %x %X %u", i, i, i, i, i, i, i);
+  cmn_err_func (0, "%ld %lD %lo %lO %lx %lX %lu", l, l, l, l, l, l, l);
+  cmn_err_func (0, "%lld %llD %llo %llO %llx %llX %llu",
+               ll, ll, ll, ll, ll, ll, ll);
+  cmn_err_func (0, "%b %s", i, "\01Foo", string);
+
+  cmn_err_func (0, "%i", i);           /* { dg-error "unknown|too many" } */
+  cmn_err_func (0, "%d", l);           /* { dg-error "expects type" } */
+  cmn_err_func (0, "%b");              /* { dg-error "too few" } */
+  cmn_err_func (0, "%b", i);           /* { dg-error "too few" } */
+  cmn_err_func (0, "%b", i, i);                /* { dg-error "expects type" } */
+  cmn_err_func (0, "%b", string, i);   /* { dg-error "expects type" } */
+
+  return 0;
+}