190075Sobrien/* Check calls to formatted I/O functions (-Wformat).
296263Sobrien   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
3169701Skan   2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
490075Sobrien
590075SobrienThis file is part of GCC.
690075Sobrien
790075SobrienGCC is free software; you can redistribute it and/or modify it under
890075Sobrienthe terms of the GNU General Public License as published by the Free
990075SobrienSoftware Foundation; either version 2, or (at your option) any later
1090075Sobrienversion.
1190075Sobrien
1290075SobrienGCC is distributed in the hope that it will be useful, but WITHOUT ANY
1390075SobrienWARRANTY; without even the implied warranty of MERCHANTABILITY or
1490075SobrienFITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
1590075Sobrienfor more details.
1690075Sobrien
1790075SobrienYou should have received a copy of the GNU General Public License
1890075Sobrienalong with GCC; see the file COPYING.  If not, write to the Free
19169701SkanSoftware Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
20169701Skan02110-1301, USA.  */
2190075Sobrien
2296558Sobrien/* $FreeBSD$ */
2396558Sobrien
2490075Sobrien#include "config.h"
2590075Sobrien#include "system.h"
26132731Skan#include "coretypes.h"
27132731Skan#include "tm.h"
2890075Sobrien#include "tree.h"
2990075Sobrien#include "flags.h"
30169701Skan#include "c-common.h"
3190075Sobrien#include "toplev.h"
3290075Sobrien#include "intl.h"
3390075Sobrien#include "diagnostic.h"
34117422Skan#include "langhooks.h"
35169701Skan#include "c-format.h"
3690075Sobrien
3790075Sobrien/* Set format warning options according to a -Wformat=n option.  */
3890075Sobrien
3990075Sobrienvoid
40132731Skanset_Wformat (int setting)
4190075Sobrien{
4290075Sobrien  warn_format = setting;
4390075Sobrien  warn_format_extra_args = setting;
44117422Skan  warn_format_zero_length = setting;
4590075Sobrien  if (setting != 1)
4690075Sobrien    {
4790075Sobrien      warn_format_nonliteral = setting;
4890075Sobrien      warn_format_security = setting;
49132731Skan      warn_format_y2k = setting;
5090075Sobrien    }
51117422Skan  /* Make sure not to disable -Wnonnull if -Wformat=0 is specified.  */
52117422Skan  if (setting)
53117422Skan    warn_nonnull = setting;
5490075Sobrien}
5590075Sobrien
5690075Sobrien
5790075Sobrien/* Handle attributes associated with format checking.  */
5890075Sobrien
59169701Skan/* This must be in the same order as format_types, except for
60169701Skan   format_type_error.  Target-specific format types do not have
61169701Skan   matching enum values.  */
62132731Skanenum format_type { printf_format_type, asm_fprintf_format_type,
63169701Skan		   gcc_diag_format_type, gcc_tdiag_format_type,
64169701Skan		   gcc_cdiag_format_type,
65169701Skan		   gcc_cxxdiag_format_type, gcc_gfc_format_type,
66132731Skan		   scanf_format_type, strftime_format_type,
67169701Skan		   strfmon_format_type, format_type_error = -1};
6890075Sobrien
6990075Sobrientypedef struct function_format_info
7090075Sobrien{
71169701Skan  int format_type;			/* type of format (printf, scanf, etc.) */
7290075Sobrien  unsigned HOST_WIDE_INT format_num;	/* number of format argument */
7390075Sobrien  unsigned HOST_WIDE_INT first_arg_num;	/* number of first arg (zero for varargs) */
7490075Sobrien} function_format_info;
7590075Sobrien
76132731Skanstatic bool decode_format_attr (tree, function_format_info *, int);
77169701Skanstatic int decode_format_type (const char *);
7890075Sobrien
79132731Skanstatic bool check_format_string (tree argument,
80132731Skan				 unsigned HOST_WIDE_INT format_num,
81132731Skan				 int flags, bool *no_add_attrs);
82132731Skanstatic bool get_constant (tree expr, unsigned HOST_WIDE_INT *value,
83132731Skan			  int validated_p);
84132731Skan
85132731Skan
86132731Skan/* Handle a "format_arg" attribute; arguments as in
8790075Sobrien   struct attribute_spec.handler.  */
8890075Sobrientree
89169701Skanhandle_format_arg_attribute (tree *node, tree ARG_UNUSED (name),
90132731Skan			     tree args, int flags, bool *no_add_attrs)
9190075Sobrien{
9290075Sobrien  tree type = *node;
93132731Skan  tree format_num_expr = TREE_VALUE (args);
94169701Skan  unsigned HOST_WIDE_INT format_num = 0;
9590075Sobrien  tree argument;
9690075Sobrien
97132731Skan  if (!get_constant (format_num_expr, &format_num, 0))
9890075Sobrien    {
99132731Skan      error ("format string has invalid operand number");
10090075Sobrien      *no_add_attrs = true;
10190075Sobrien      return NULL_TREE;
10290075Sobrien    }
10390075Sobrien
10490075Sobrien  argument = TYPE_ARG_TYPES (type);
10590075Sobrien  if (argument)
10690075Sobrien    {
107132731Skan      if (!check_format_string (argument, format_num, flags, no_add_attrs))
108132731Skan	return NULL_TREE;
10990075Sobrien    }
11090075Sobrien
111132731Skan  if (TREE_CODE (TREE_TYPE (type)) != POINTER_TYPE
112132731Skan      || (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (type)))
113132731Skan	  != char_type_node))
11490075Sobrien    {
115132731Skan      if (!(flags & (int) ATTR_FLAG_BUILT_IN))
116132731Skan	error ("function does not return string type");
11790075Sobrien      *no_add_attrs = true;
11890075Sobrien      return NULL_TREE;
11990075Sobrien    }
12090075Sobrien
12190075Sobrien  return NULL_TREE;
12290075Sobrien}
12390075Sobrien
124132731Skan/* Verify that the format_num argument is actually a string, in case
125132731Skan   the format attribute is in error.  */
126132731Skanstatic bool
127132731Skancheck_format_string (tree argument, unsigned HOST_WIDE_INT format_num,
128132731Skan		     int flags, bool *no_add_attrs)
12990075Sobrien{
130132731Skan  unsigned HOST_WIDE_INT i;
13190075Sobrien
132132731Skan  for (i = 1; i != format_num; i++)
133132731Skan    {
134132731Skan      if (argument == 0)
135132731Skan	break;
136132731Skan      argument = TREE_CHAIN (argument);
137132731Skan    }
13890075Sobrien
139132731Skan  if (!argument
140132731Skan      || TREE_CODE (TREE_VALUE (argument)) != POINTER_TYPE
141132731Skan      || (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_VALUE (argument)))
142132731Skan	  != char_type_node))
14390075Sobrien    {
144132731Skan      if (!(flags & (int) ATTR_FLAG_BUILT_IN))
145169701Skan	error ("format string argument not a string type");
14690075Sobrien      *no_add_attrs = true;
147132731Skan      return false;
14890075Sobrien    }
14990075Sobrien
150132731Skan  return true;
151132731Skan}
15290075Sobrien
153169701Skan/* Verify EXPR is a constant, and store its value.
154169701Skan   If validated_p is true there should be no errors.
155132731Skan   Returns true on success, false otherwise.  */
156132731Skanstatic bool
157169701Skanget_constant (tree expr, unsigned HOST_WIDE_INT *value, int validated_p)
158132731Skan{
159132731Skan  if (TREE_CODE (expr) != INTEGER_CST || TREE_INT_CST_HIGH (expr) != 0)
16090075Sobrien    {
161169701Skan      gcc_assert (!validated_p);
162132731Skan      return false;
16390075Sobrien    }
16490075Sobrien
165132731Skan  *value = TREE_INT_CST_LOW (expr);
166132731Skan
167132731Skan  return true;
16890075Sobrien}
16990075Sobrien
170169701Skan/* Decode the arguments to a "format" attribute into a
171169701Skan   function_format_info structure.  It is already known that the list
172169701Skan   is of the right length.  If VALIDATED_P is true, then these
173169701Skan   attributes have already been validated and must not be erroneous;
174169701Skan   if false, it will give an error message.  Returns true if the
175169701Skan   attributes are successfully decoded, false otherwise.  */
17690075Sobrien
17790075Sobrienstatic bool
178132731Skandecode_format_attr (tree args, function_format_info *info, int validated_p)
17990075Sobrien{
18090075Sobrien  tree format_type_id = TREE_VALUE (args);
18190075Sobrien  tree format_num_expr = TREE_VALUE (TREE_CHAIN (args));
18290075Sobrien  tree first_arg_num_expr
18390075Sobrien    = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (args)));
18490075Sobrien
18590075Sobrien  if (TREE_CODE (format_type_id) != IDENTIFIER_NODE)
18690075Sobrien    {
187169701Skan      gcc_assert (!validated_p);
188169701Skan      error ("%Junrecognized format specifier", lang_hooks.decls.getdecls ());
18990075Sobrien      return false;
19090075Sobrien    }
19190075Sobrien  else
19290075Sobrien    {
19390075Sobrien      const char *p = IDENTIFIER_POINTER (format_type_id);
19490075Sobrien
19590075Sobrien      info->format_type = decode_format_type (p);
19690075Sobrien
19790075Sobrien      if (info->format_type == format_type_error)
19890075Sobrien	{
199169701Skan	  gcc_assert (!validated_p);
200169701Skan	  warning (OPT_Wformat, "%qE is an unrecognized format function type",
201169701Skan		   format_type_id);
20290075Sobrien	  return false;
20390075Sobrien	}
20490075Sobrien    }
20590075Sobrien
206132731Skan  if (!get_constant (format_num_expr, &info->format_num, validated_p))
20790075Sobrien    {
20890075Sobrien      error ("format string has invalid operand number");
20990075Sobrien      return false;
21090075Sobrien    }
21190075Sobrien
212132731Skan  if (!get_constant (first_arg_num_expr, &info->first_arg_num, validated_p))
213132731Skan    {
214169701Skan      error ("%<...%> has invalid operand number");
215132731Skan      return false;
216132731Skan    }
217132731Skan
21890075Sobrien  if (info->first_arg_num != 0 && info->first_arg_num <= info->format_num)
21990075Sobrien    {
220169701Skan      gcc_assert (!validated_p);
221169701Skan      error ("format string argument follows the args to be formatted");
22290075Sobrien      return false;
22390075Sobrien    }
22490075Sobrien
22590075Sobrien  return true;
22690075Sobrien}
22790075Sobrien
22890075Sobrien/* Check a call to a format function against a parameter list.  */
22990075Sobrien
23090075Sobrien/* The C standard version C++ is treated as equivalent to
23190075Sobrien   or inheriting from, for the purpose of format features supported.  */
23290075Sobrien#define CPLUSPLUS_STD_VER	STD_C94
23390075Sobrien/* The C standard version we are checking formats against when pedantic.  */
234169701Skan#define C_STD_VER		((int) (c_dialect_cxx ()		   \
235169701Skan				 ? CPLUSPLUS_STD_VER			   \
236169701Skan				 : (flag_isoc99				   \
237169701Skan				    ? STD_C99				   \
23890075Sobrien				    : (flag_isoc94 ? STD_C94 : STD_C89))))
23990075Sobrien/* The name to give to the standard version we are warning about when
24090075Sobrien   pedantic.  FEATURE_VER is the version in which the feature warned out
24190075Sobrien   appeared, which is higher than C_STD_VER.  */
242132731Skan#define C_STD_NAME(FEATURE_VER) (c_dialect_cxx ()		\
24390075Sobrien				 ? "ISO C++"			\
24490075Sobrien				 : ((FEATURE_VER) == STD_EXT	\
24590075Sobrien				    ? "ISO C"			\
246117422Skan				    : "ISO C90"))
24790075Sobrien/* Adjust a C standard version, which may be STD_C9L, to account for
24890075Sobrien   -Wno-long-long.  Returns other standard versions unchanged.  */
249169701Skan#define ADJ_STD(VER)		((int) ((VER) == STD_C9L		      \
25090075Sobrien				       ? (warn_long_long ? STD_C99 : STD_C89) \
25190075Sobrien				       : (VER)))
25290075Sobrien
25390075Sobrien/* Structure describing details of a type expected in format checking,
25490075Sobrien   and the type to check against it.  */
25590075Sobrientypedef struct format_wanted_type
25690075Sobrien{
25790075Sobrien  /* The type wanted.  */
25890075Sobrien  tree wanted_type;
25990075Sobrien  /* The name of this type to use in diagnostics.  */
26090075Sobrien  const char *wanted_type_name;
26190075Sobrien  /* The level of indirection through pointers at which this type occurs.  */
26290075Sobrien  int pointer_count;
26390075Sobrien  /* Whether, when pointer_count is 1, to allow any character type when
26490075Sobrien     pedantic, rather than just the character or void type specified.  */
26590075Sobrien  int char_lenient_flag;
26690075Sobrien  /* Whether the argument, dereferenced once, is written into and so the
26790075Sobrien     argument must not be a pointer to a const-qualified type.  */
26890075Sobrien  int writing_in_flag;
26990075Sobrien  /* Whether the argument, dereferenced once, is read from and so
27090075Sobrien     must not be a NULL pointer.  */
27190075Sobrien  int reading_from_flag;
272169701Skan  /* If warnings should be of the form "field precision should have
273169701Skan     type 'int'", the name to use (in this case "field precision"),
274169701Skan     otherwise NULL, for "format expects type 'long'" type
275169701Skan     messages.  */
27690075Sobrien  const char *name;
27790075Sobrien  /* The actual parameter to check against the wanted type.  */
27890075Sobrien  tree param;
27990075Sobrien  /* The argument number of that parameter.  */
28090075Sobrien  int arg_num;
28190075Sobrien  /* The next type to check for this format conversion, or NULL if none.  */
28290075Sobrien  struct format_wanted_type *next;
28390075Sobrien} format_wanted_type;
28490075Sobrien
28590075Sobrien
28690075Sobrienstatic const format_length_info printf_length_specs[] =
28790075Sobrien{
28890075Sobrien  { "h", FMT_LEN_h, STD_C89, "hh", FMT_LEN_hh, STD_C99 },
28990075Sobrien  { "l", FMT_LEN_l, STD_C89, "ll", FMT_LEN_ll, STD_C9L },
29090075Sobrien  { "q", FMT_LEN_ll, STD_EXT, NULL, 0, 0 },
29190075Sobrien  { "L", FMT_LEN_L, STD_C89, NULL, 0, 0 },
29290075Sobrien  { "z", FMT_LEN_z, STD_C99, NULL, 0, 0 },
29390075Sobrien  { "Z", FMT_LEN_z, STD_EXT, NULL, 0, 0 },
29490075Sobrien  { "t", FMT_LEN_t, STD_C99, NULL, 0, 0 },
29590075Sobrien  { "j", FMT_LEN_j, STD_C99, NULL, 0, 0 },
296169701Skan  { "H", FMT_LEN_H, STD_EXT, NULL, 0, 0 },
297169701Skan  { "D", FMT_LEN_D, STD_EXT, "DD", FMT_LEN_DD, STD_EXT },
29890075Sobrien  { NULL, 0, 0, NULL, 0, 0 }
29990075Sobrien};
30090075Sobrien
301132731Skan/* Length specifiers valid for asm_fprintf.  */
302132731Skanstatic const format_length_info asm_fprintf_length_specs[] =
303132731Skan{
304132731Skan  { "l", FMT_LEN_l, STD_C89, "ll", FMT_LEN_ll, STD_C89 },
305132731Skan  { "w", FMT_LEN_none, STD_C89, NULL, 0, 0 },
306132731Skan  { NULL, 0, 0, NULL, 0, 0 }
307132731Skan};
30890075Sobrien
309132731Skan/* Length specifiers valid for GCC diagnostics.  */
310132731Skanstatic const format_length_info gcc_diag_length_specs[] =
311132731Skan{
312132731Skan  { "l", FMT_LEN_l, STD_C89, "ll", FMT_LEN_ll, STD_C89 },
313132731Skan  { "w", FMT_LEN_none, STD_C89, NULL, 0, 0 },
314132731Skan  { NULL, 0, 0, NULL, 0, 0 }
315132731Skan};
316132731Skan
317132731Skan/* The custom diagnostics all accept the same length specifiers.  */
318169701Skan#define gcc_tdiag_length_specs gcc_diag_length_specs
319132731Skan#define gcc_cdiag_length_specs gcc_diag_length_specs
320132731Skan#define gcc_cxxdiag_length_specs gcc_diag_length_specs
321132731Skan
32290075Sobrien/* This differs from printf_length_specs only in that "Z" is not accepted.  */
32390075Sobrienstatic const format_length_info scanf_length_specs[] =
32490075Sobrien{
32590075Sobrien  { "h", FMT_LEN_h, STD_C89, "hh", FMT_LEN_hh, STD_C99 },
32690075Sobrien  { "l", FMT_LEN_l, STD_C89, "ll", FMT_LEN_ll, STD_C9L },
32790075Sobrien  { "q", FMT_LEN_ll, STD_EXT, NULL, 0, 0 },
32890075Sobrien  { "L", FMT_LEN_L, STD_C89, NULL, 0, 0 },
32990075Sobrien  { "z", FMT_LEN_z, STD_C99, NULL, 0, 0 },
33090075Sobrien  { "t", FMT_LEN_t, STD_C99, NULL, 0, 0 },
33190075Sobrien  { "j", FMT_LEN_j, STD_C99, NULL, 0, 0 },
332169701Skan  { "H", FMT_LEN_H, STD_EXT, NULL, 0, 0 },
333169701Skan  { "D", FMT_LEN_D, STD_EXT, "DD", FMT_LEN_DD, STD_EXT },
33490075Sobrien  { NULL, 0, 0, NULL, 0, 0 }
33590075Sobrien};
33690075Sobrien
33790075Sobrien
33890075Sobrien/* All tables for strfmon use STD_C89 everywhere, since -pedantic warnings
33990075Sobrien   make no sense for a format type not part of any C standard version.  */
34090075Sobrienstatic const format_length_info strfmon_length_specs[] =
34190075Sobrien{
34290075Sobrien  /* A GNU extension.  */
34390075Sobrien  { "L", FMT_LEN_L, STD_C89, NULL, 0, 0 },
34490075Sobrien  { NULL, 0, 0, NULL, 0, 0 }
34590075Sobrien};
34690075Sobrien
34790075Sobrienstatic const format_flag_spec printf_flag_specs[] =
34890075Sobrien{
349169701Skan  { ' ',  0, 0, N_("' ' flag"),        N_("the ' ' printf flag"),              STD_C89 },
350169701Skan  { '+',  0, 0, N_("'+' flag"),        N_("the '+' printf flag"),              STD_C89 },
351169701Skan  { '#',  0, 0, N_("'#' flag"),        N_("the '#' printf flag"),              STD_C89 },
352169701Skan  { '0',  0, 0, N_("'0' flag"),        N_("the '0' printf flag"),              STD_C89 },
353169701Skan  { '-',  0, 0, N_("'-' flag"),        N_("the '-' printf flag"),              STD_C89 },
354169701Skan  { '\'', 0, 0, N_("''' flag"),        N_("the ''' printf flag"),              STD_EXT },
355169701Skan  { 'I',  0, 0, N_("'I' flag"),        N_("the 'I' printf flag"),              STD_EXT },
35690075Sobrien  { 'w',  0, 0, N_("field width"),     N_("field width in printf format"),     STD_C89 },
35790075Sobrien  { 'p',  0, 0, N_("precision"),       N_("precision in printf format"),       STD_C89 },
35890075Sobrien  { 'L',  0, 0, N_("length modifier"), N_("length modifier in printf format"), STD_C89 },
35990075Sobrien  { 0, 0, 0, NULL, NULL, 0 }
36090075Sobrien};
36190075Sobrien
36290075Sobrien
36390075Sobrienstatic const format_flag_pair printf_flag_pairs[] =
36490075Sobrien{
36590075Sobrien  { ' ', '+', 1, 0   },
36690075Sobrien  { '0', '-', 1, 0   },
36790075Sobrien  { '0', 'p', 1, 'i' },
36890075Sobrien  { 0, 0, 0, 0 }
36990075Sobrien};
37090075Sobrien
371132731Skanstatic const format_flag_spec asm_fprintf_flag_specs[] =
372132731Skan{
373169701Skan  { ' ',  0, 0, N_("' ' flag"),        N_("the ' ' printf flag"),              STD_C89 },
374169701Skan  { '+',  0, 0, N_("'+' flag"),        N_("the '+' printf flag"),              STD_C89 },
375169701Skan  { '#',  0, 0, N_("'#' flag"),        N_("the '#' printf flag"),              STD_C89 },
376169701Skan  { '0',  0, 0, N_("'0' flag"),        N_("the '0' printf flag"),              STD_C89 },
377169701Skan  { '-',  0, 0, N_("'-' flag"),        N_("the '-' printf flag"),              STD_C89 },
378132731Skan  { 'w',  0, 0, N_("field width"),     N_("field width in printf format"),     STD_C89 },
379132731Skan  { 'p',  0, 0, N_("precision"),       N_("precision in printf format"),       STD_C89 },
380132731Skan  { 'L',  0, 0, N_("length modifier"), N_("length modifier in printf format"), STD_C89 },
381132731Skan  { 0, 0, 0, NULL, NULL, 0 }
382132731Skan};
38390075Sobrien
384132731Skanstatic const format_flag_pair asm_fprintf_flag_pairs[] =
385132731Skan{
386132731Skan  { ' ', '+', 1, 0   },
387132731Skan  { '0', '-', 1, 0   },
388132731Skan  { '0', 'p', 1, 'i' },
389132731Skan  { 0, 0, 0, 0 }
390132731Skan};
391132731Skan
392132731Skanstatic const format_flag_pair gcc_diag_flag_pairs[] =
393132731Skan{
394132731Skan  { 0, 0, 0, 0 }
395132731Skan};
396132731Skan
397169701Skan#define gcc_tdiag_flag_pairs gcc_diag_flag_pairs
398132731Skan#define gcc_cdiag_flag_pairs gcc_diag_flag_pairs
399132731Skan#define gcc_cxxdiag_flag_pairs gcc_diag_flag_pairs
400132731Skan
401169701Skanstatic const format_flag_pair gcc_gfc_flag_pairs[] =
402169701Skan{
403169701Skan  { 0, 0, 0, 0 }
404169701Skan};
405169701Skan
406132731Skanstatic const format_flag_spec gcc_diag_flag_specs[] =
407132731Skan{
408169701Skan  { '+',  0, 0, N_("'+' flag"),        N_("the '+' printf flag"),              STD_C89 },
409169701Skan  { 'q',  0, 0, N_("'q' flag"),        N_("the 'q' diagnostic flag"),          STD_C89 },
410132731Skan  { 'p',  0, 0, N_("precision"),       N_("precision in printf format"),       STD_C89 },
411132731Skan  { 'L',  0, 0, N_("length modifier"), N_("length modifier in printf format"), STD_C89 },
412132731Skan  { 0, 0, 0, NULL, NULL, 0 }
413132731Skan};
414132731Skan
415169701Skan#define gcc_tdiag_flag_specs gcc_diag_flag_specs
416132731Skan#define gcc_cdiag_flag_specs gcc_diag_flag_specs
417132731Skan
418132731Skanstatic const format_flag_spec gcc_cxxdiag_flag_specs[] =
419132731Skan{
420169701Skan  { '+',  0, 0, N_("'+' flag"),        N_("the '+' printf flag"),              STD_C89 },
421169701Skan  { '#',  0, 0, N_("'#' flag"),        N_("the '#' printf flag"),              STD_C89 },
422169701Skan  { 'q',  0, 0, N_("'q' flag"),        N_("the 'q' diagnostic flag"),          STD_C89 },
423132731Skan  { 'p',  0, 0, N_("precision"),       N_("precision in printf format"),       STD_C89 },
424132731Skan  { 'L',  0, 0, N_("length modifier"), N_("length modifier in printf format"), STD_C89 },
425132731Skan  { 0, 0, 0, NULL, NULL, 0 }
426132731Skan};
427132731Skan
42890075Sobrienstatic const format_flag_spec scanf_flag_specs[] =
42990075Sobrien{
43090075Sobrien  { '*',  0, 0, N_("assignment suppression"), N_("the assignment suppression scanf feature"), STD_C89 },
431169701Skan  { 'a',  0, 0, N_("'a' flag"),               N_("the 'a' scanf flag"),                       STD_EXT },
43290075Sobrien  { 'w',  0, 0, N_("field width"),            N_("field width in scanf format"),              STD_C89 },
43390075Sobrien  { 'L',  0, 0, N_("length modifier"),        N_("length modifier in scanf format"),          STD_C89 },
434169701Skan  { '\'', 0, 0, N_("''' flag"),               N_("the ''' scanf flag"),                       STD_EXT },
435169701Skan  { 'I',  0, 0, N_("'I' flag"),               N_("the 'I' scanf flag"),                       STD_EXT },
43690075Sobrien  { 0, 0, 0, NULL, NULL, 0 }
43790075Sobrien};
43890075Sobrien
43990075Sobrien
44090075Sobrienstatic const format_flag_pair scanf_flag_pairs[] =
44190075Sobrien{
44290075Sobrien  { '*', 'L', 0, 0 },
44390075Sobrien  { 0, 0, 0, 0 }
44490075Sobrien};
44590075Sobrien
44690075Sobrien
44790075Sobrienstatic const format_flag_spec strftime_flag_specs[] =
44890075Sobrien{
449169701Skan  { '_', 0,   0, N_("'_' flag"),     N_("the '_' strftime flag"),          STD_EXT },
450169701Skan  { '-', 0,   0, N_("'-' flag"),     N_("the '-' strftime flag"),          STD_EXT },
451169701Skan  { '0', 0,   0, N_("'0' flag"),     N_("the '0' strftime flag"),          STD_EXT },
452169701Skan  { '^', 0,   0, N_("'^' flag"),     N_("the '^' strftime flag"),          STD_EXT },
453169701Skan  { '#', 0,   0, N_("'#' flag"),     N_("the '#' strftime flag"),          STD_EXT },
45490075Sobrien  { 'w', 0,   0, N_("field width"),  N_("field width in strftime format"), STD_EXT },
455169701Skan  { 'E', 0,   0, N_("'E' modifier"), N_("the 'E' strftime modifier"),      STD_C99 },
456169701Skan  { 'O', 0,   0, N_("'O' modifier"), N_("the 'O' strftime modifier"),      STD_C99 },
457169701Skan  { 'O', 'o', 0, NULL,               N_("the 'O' modifier"),               STD_EXT },
45890075Sobrien  { 0, 0, 0, NULL, NULL, 0 }
45990075Sobrien};
46090075Sobrien
46190075Sobrien
46290075Sobrienstatic const format_flag_pair strftime_flag_pairs[] =
46390075Sobrien{
46490075Sobrien  { 'E', 'O', 0, 0 },
46590075Sobrien  { '_', '-', 0, 0 },
46690075Sobrien  { '_', '0', 0, 0 },
46790075Sobrien  { '-', '0', 0, 0 },
46890075Sobrien  { '^', '#', 0, 0 },
46990075Sobrien  { 0, 0, 0, 0 }
47090075Sobrien};
47190075Sobrien
47290075Sobrien
47390075Sobrienstatic const format_flag_spec strfmon_flag_specs[] =
47490075Sobrien{
47590075Sobrien  { '=',  0, 1, N_("fill character"),  N_("fill character in strfmon format"),  STD_C89 },
476169701Skan  { '^',  0, 0, N_("'^' flag"),        N_("the '^' strfmon flag"),              STD_C89 },
477169701Skan  { '+',  0, 0, N_("'+' flag"),        N_("the '+' strfmon flag"),              STD_C89 },
478169701Skan  { '(',  0, 0, N_("'(' flag"),        N_("the '(' strfmon flag"),              STD_C89 },
479169701Skan  { '!',  0, 0, N_("'!' flag"),        N_("the '!' strfmon flag"),              STD_C89 },
480169701Skan  { '-',  0, 0, N_("'-' flag"),        N_("the '-' strfmon flag"),              STD_C89 },
48190075Sobrien  { 'w',  0, 0, N_("field width"),     N_("field width in strfmon format"),     STD_C89 },
48290075Sobrien  { '#',  0, 0, N_("left precision"),  N_("left precision in strfmon format"),  STD_C89 },
48390075Sobrien  { 'p',  0, 0, N_("right precision"), N_("right precision in strfmon format"), STD_C89 },
48490075Sobrien  { 'L',  0, 0, N_("length modifier"), N_("length modifier in strfmon format"), STD_C89 },
48590075Sobrien  { 0, 0, 0, NULL, NULL, 0 }
48690075Sobrien};
48790075Sobrien
48890075Sobrienstatic const format_flag_pair strfmon_flag_pairs[] =
48990075Sobrien{
49090075Sobrien  { '+', '(', 0, 0 },
49190075Sobrien  { 0, 0, 0, 0 }
49290075Sobrien};
49390075Sobrien
49490075Sobrien
49590075Sobrienstatic const format_char_info print_char_table[] =
49690075Sobrien{
49790075Sobrien  /* C89 conversion specifiers.  */
498169701Skan  { "di",  0, STD_C89, { T89_I,   T99_SC,  T89_S,   T89_L,   T9L_LL,  TEX_LL,  T99_SST, T99_PD,  T99_IM,  BADLEN,  BADLEN,  BADLEN  }, "-wp0 +'I",  "i",  NULL },
499169701Skan  { "oxX", 0, STD_C89, { T89_UI,  T99_UC,  T89_US,  T89_UL,  T9L_ULL, TEX_ULL, T99_ST,  T99_UPD, T99_UIM, BADLEN,  BADLEN,  BADLEN }, "-wp0#",     "i",  NULL },
500169701Skan  { "u",   0, STD_C89, { T89_UI,  T99_UC,  T89_US,  T89_UL,  T9L_ULL, TEX_ULL, T99_ST,  T99_UPD, T99_UIM, BADLEN,  BADLEN,  BADLEN }, "-wp0'I",    "i",  NULL },
501169701Skan  { "fgG", 0, STD_C89, { T89_D,   BADLEN,  BADLEN,  T99_D,   BADLEN,  T89_LD,  BADLEN,  BADLEN,  BADLEN,  TEX_D32, TEX_D64, TEX_D128 }, "-wp0 +#'I", "",   NULL },
502169701Skan  { "eE",  0, STD_C89, { T89_D,   BADLEN,  BADLEN,  T99_D,   BADLEN,  T89_LD,  BADLEN,  BADLEN,  BADLEN,  TEX_D32, TEX_D64, TEX_D128 }, "-wp0 +#I",  "",   NULL },
503169701Skan  { "c",   0, STD_C89, { T89_I,   BADLEN,  BADLEN,  T94_WI,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN }, "-w",        "",   NULL },
504169701Skan  { "s",   1, STD_C89, { T89_C,   BADLEN,  BADLEN,  T94_W,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN }, "-wp",       "cR", NULL },
505169701Skan  { "p",   1, STD_C89, { T89_V,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN }, "-w",        "c",  NULL },
506169701Skan  { "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 },
50790075Sobrien  /* C99 conversion specifiers.  */
508169701Skan  { "F",   0, STD_C99, { T99_D,   BADLEN,  BADLEN,  T99_D,   BADLEN,  T99_LD,  BADLEN,  BADLEN,  BADLEN,  TEX_D32, TEX_D64, TEX_D128 }, "-wp0 +#'I", "",   NULL },
509169701Skan  { "aA",  0, STD_C99, { T99_D,   BADLEN,  BADLEN,  T99_D,   BADLEN,  T99_LD,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN }, "-wp0 +#",   "",   NULL },
51090075Sobrien  /* X/Open conversion specifiers.  */
511169701Skan  { "C",   0, STD_EXT, { TEX_WI,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN }, "-w",        "",   NULL },
512169701Skan  { "S",   1, STD_EXT, { TEX_W,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN }, "-wp",       "R",  NULL },
51390075Sobrien  /* GNU conversion specifiers.  */
514169701Skan  { "m",   0, STD_EXT, { T89_V,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN }, "-wp",       "",   NULL },
515169701Skan  { NULL,  0, 0, NOLENGTHS, NULL, NULL, NULL }
516169701Skan};
517169701Skan
518169701Skanstatic const format_char_info fbsd_ext_char_info =
519169701Skan{ NULL,   1, STD_EXT, { T89_C,  BADLEN,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "",      "cR", NULL };
520169701Skan
521169701Skanstatic const format_char_info fbsd_print_char_table[] =
522169701Skan{
52397130Sobrien  /* BSD conversion specifiers.  */
52497130Sobrien  /* FreeBSD kernel extensions (src/sys/kern/subr_prf.c).
52597130Sobrien     The format %b is supported to decode error registers.
52697130Sobrien     Its usage is:	printf("reg=%b\n", regval, "<base><arg>*");
52797130Sobrien     which produces:	reg=3<BITTWO,BITONE>
52897130Sobrien     The format %D provides a hexdump given a pointer and separator string:
52997130Sobrien     ("%6D", ptr, ":")		-> XX:XX:XX:XX:XX:XX
53097130Sobrien     ("%*D", len, ptr, " ")	-> XX XX XX XX ...
53197130Sobrien   */
532169701Skan  { "D",   1, STD_EXT, { T89_V,  BADLEN,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN }, "-wp",      "cR", &fbsd_ext_char_info },
533169701Skan  { "b",   0, STD_EXT, { T89_I,  BADLEN,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "-wp",      "",   &fbsd_ext_char_info },
534169701Skan  { "ry",  0, STD_EXT, { T89_I,  BADLEN,   BADLEN,   T89_L,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "-wp0 +#",  "i",  NULL  },
53590075Sobrien  { NULL,  0, 0, NOLENGTHS, NULL, NULL }
53690075Sobrien};
53790075Sobrien
538132731Skanstatic const format_char_info asm_fprintf_char_table[] =
539132731Skan{
540132731Skan  /* C89 conversion specifiers.  */
541169701Skan  { "di",  0, STD_C89, { T89_I,   BADLEN,  BADLEN,  T89_L,   T9L_LL,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "-wp0 +",  "i", NULL },
542169701Skan  { "oxX", 0, STD_C89, { T89_UI,  BADLEN,  BADLEN,  T89_UL,  T9L_ULL, BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "-wp0#",   "i", NULL },
543169701Skan  { "u",   0, STD_C89, { T89_UI,  BADLEN,  BADLEN,  T89_UL,  T9L_ULL, BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "-wp0",    "i", NULL },
544169701Skan  { "c",   0, STD_C89, { T89_I,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "-w",       "", NULL },
545169701Skan  { "s",   1, STD_C89, { T89_C,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "-wp",    "cR", NULL },
546132731Skan
547132731Skan  /* asm_fprintf conversion specifiers.  */
548169701Skan  { "O",   0, STD_C89, NOARGUMENTS, "",      "",   NULL },
549169701Skan  { "R",   0, STD_C89, NOARGUMENTS, "",      "",   NULL },
550169701Skan  { "I",   0, STD_C89, NOARGUMENTS, "",      "",   NULL },
551169701Skan  { "L",   0, STD_C89, NOARGUMENTS, "",      "",   NULL },
552169701Skan  { "U",   0, STD_C89, NOARGUMENTS, "",      "",   NULL },
553169701Skan  { "r",   0, STD_C89, { T89_I,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "",  "", NULL },
554169701Skan  { "@",   0, STD_C89, NOARGUMENTS, "",      "",   NULL },
555169701Skan  { NULL,  0, 0, NOLENGTHS, NULL, NULL, NULL }
556132731Skan};
557132731Skan
558132731Skanstatic const format_char_info gcc_diag_char_table[] =
559132731Skan{
560132731Skan  /* C89 conversion specifiers.  */
561169701Skan  { "di",  0, STD_C89, { T89_I,   BADLEN,  BADLEN,  T89_L,   T9L_LL,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  "",   NULL },
562169701Skan  { "ox",  0, STD_C89, { T89_UI,  BADLEN,  BADLEN,  T89_UL,  T9L_ULL, BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  "",   NULL },
563169701Skan  { "u",   0, STD_C89, { T89_UI,  BADLEN,  BADLEN,  T89_UL,  T9L_ULL, BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  "",   NULL },
564169701Skan  { "c",   0, STD_C89, { T89_I,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  "",   NULL },
565169701Skan  { "s",   1, STD_C89, { T89_C,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "pq", "cR", NULL },
566169701Skan  { "p",   1, STD_C89, { T89_V,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  "c",  NULL },
567132731Skan
568132731Skan  /* Custom conversion specifiers.  */
569132731Skan
570132731Skan  /* %H will require "location_t" at runtime.  */
571169701Skan  { "H",   0, STD_C89, { T89_V,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  "",   NULL },
572132731Skan
573132731Skan  /* These will require a "tree" at runtime.  */
574169701Skan  { "J", 0, STD_C89, { T89_V,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",    "",   NULL },
575132731Skan
576169701Skan  { "<>'", 0, STD_C89, NOARGUMENTS, "",      "",   NULL },
577169701Skan  { "m",   0, STD_C89, NOARGUMENTS, "q",     "",   NULL },
578169701Skan  { NULL,  0, 0, NOLENGTHS, NULL, NULL, NULL }
579132731Skan};
580132731Skan
581169701Skanstatic const format_char_info gcc_tdiag_char_table[] =
582169701Skan{
583169701Skan  /* C89 conversion specifiers.  */
584169701Skan  { "di",  0, STD_C89, { T89_I,   BADLEN,  BADLEN,  T89_L,   T9L_LL,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  "",   NULL },
585169701Skan  { "ox",  0, STD_C89, { T89_UI,  BADLEN,  BADLEN,  T89_UL,  T9L_ULL, BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  "",   NULL },
586169701Skan  { "u",   0, STD_C89, { T89_UI,  BADLEN,  BADLEN,  T89_UL,  T9L_ULL, BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  "",   NULL },
587169701Skan  { "c",   0, STD_C89, { T89_I,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  "",   NULL },
588169701Skan  { "s",   1, STD_C89, { T89_C,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "pq", "cR", NULL },
589169701Skan  { "p",   1, STD_C89, { T89_V,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  "c",  NULL },
590169701Skan
591169701Skan  /* Custom conversion specifiers.  */
592169701Skan
593169701Skan  /* %H will require "location_t" at runtime.  */
594169701Skan  { "H",   0, STD_C89, { T89_V,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  "",   NULL },
595169701Skan
596169701Skan  /* These will require a "tree" at runtime.  */
597169701Skan  { "DFJT", 0, STD_C89, { T89_V,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q+", "",   NULL },
598169701Skan
599169701Skan  { "<>'", 0, STD_C89, NOARGUMENTS, "",      "",   NULL },
600169701Skan  { "m",   0, STD_C89, NOARGUMENTS, "q",     "",   NULL },
601169701Skan  { NULL,  0, 0, NOLENGTHS, NULL, NULL, NULL }
602169701Skan};
603169701Skan
604132731Skanstatic const format_char_info gcc_cdiag_char_table[] =
605132731Skan{
606132731Skan  /* C89 conversion specifiers.  */
607169701Skan  { "di",  0, STD_C89, { T89_I,   BADLEN,  BADLEN,  T89_L,   T9L_LL,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  "",   NULL },
608169701Skan  { "ox",  0, STD_C89, { T89_UI,  BADLEN,  BADLEN,  T89_UL,  T9L_ULL, BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  "",   NULL },
609169701Skan  { "u",   0, STD_C89, { T89_UI,  BADLEN,  BADLEN,  T89_UL,  T9L_ULL, BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  "",   NULL },
610169701Skan  { "c",   0, STD_C89, { T89_I,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  "",   NULL },
611169701Skan  { "s",   1, STD_C89, { T89_C,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "pq", "cR", NULL },
612169701Skan  { "p",   1, STD_C89, { T89_V,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  "c",  NULL },
613132731Skan
614132731Skan  /* Custom conversion specifiers.  */
615132731Skan
616132731Skan  /* %H will require "location_t" at runtime.  */
617169701Skan  { "H",   0, STD_C89, { T89_V,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  "",   NULL },
618132731Skan
619132731Skan  /* These will require a "tree" at runtime.  */
620169701Skan  { "DEFJT", 0, STD_C89, { T89_V,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q+", "",   NULL },
621132731Skan
622169701Skan  { "<>'", 0, STD_C89, NOARGUMENTS, "",      "",   NULL },
623169701Skan  { "m",   0, STD_C89, NOARGUMENTS, "q",     "",   NULL },
624169701Skan  { NULL,  0, 0, NOLENGTHS, NULL, NULL, NULL }
625132731Skan};
626132731Skan
627132731Skanstatic const format_char_info gcc_cxxdiag_char_table[] =
628132731Skan{
629132731Skan  /* C89 conversion specifiers.  */
630169701Skan  { "di",  0, STD_C89, { T89_I,   BADLEN,  BADLEN,  T89_L,   T9L_LL,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  "",   NULL },
631169701Skan  { "ox",  0, STD_C89, { T89_UI,  BADLEN,  BADLEN,  T89_UL,  T9L_ULL, BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  "",   NULL },
632169701Skan  { "u",   0, STD_C89, { T89_UI,  BADLEN,  BADLEN,  T89_UL,  T9L_ULL, BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  "",   NULL },
633169701Skan  { "c",   0, STD_C89, { T89_I,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  "",   NULL },
634169701Skan  { "s",   1, STD_C89, { T89_C,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "pq", "cR", NULL },
635169701Skan  { "p",   1, STD_C89, { T89_V,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  "c",  NULL },
636132731Skan
637132731Skan  /* Custom conversion specifiers.  */
638132731Skan
639132731Skan  /* %H will require "location_t" at runtime.  */
640169701Skan  { "H",   0, STD_C89, { T89_V,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  "",   NULL },
641132731Skan
642132731Skan  /* These will require a "tree" at runtime.  */
643169701Skan  { "ADEFJTV",0,STD_C89,{ T89_V,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q+#",   "",   NULL },
644132731Skan
645169701Skan  /* These accept either an 'int' or an 'enum tree_code' (which is handled as an 'int'.)  */
646169701Skan  { "CLOPQ",0,STD_C89, { T89_I,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  "",   NULL },
647132731Skan
648169701Skan  { "<>'", 0, STD_C89, NOARGUMENTS, "",      "",   NULL },
649169701Skan  { "m",   0, STD_C89, NOARGUMENTS, "q",     "",   NULL },
650169701Skan  { NULL,  0, 0, NOLENGTHS, NULL, NULL, NULL }
651132731Skan};
652132731Skan
653169701Skanstatic const format_char_info gcc_gfc_char_table[] =
654169701Skan{
655169701Skan  /* C89 conversion specifiers.  */
656169701Skan  { "di",  0, STD_C89, { T89_I,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "", "", NULL },
657169701Skan  { "c",   0, STD_C89, { T89_I,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "", "", NULL },
658169701Skan  { "s",   1, STD_C89, { T89_C,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "", "cR", NULL },
659169701Skan
660169701Skan  /* gfc conversion specifiers.  */
661169701Skan
662169701Skan  { "C",   0, STD_C89, NOARGUMENTS, "",      "",   NULL },
663169701Skan
664169701Skan  /* This will require a "locus" at runtime.  */
665169701Skan  { "L",   0, STD_C89, { T89_V,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "", "R", NULL },
666169701Skan
667169701Skan  { NULL,  0, 0, NOLENGTHS, NULL, NULL, NULL }
668169701Skan};
669169701Skan
67090075Sobrienstatic const format_char_info scan_char_table[] =
67190075Sobrien{
67290075Sobrien  /* C89 conversion specifiers.  */
673169701Skan  { "di",    1, STD_C89, { T89_I,   T99_SC,  T89_S,   T89_L,   T9L_LL,  TEX_LL,  T99_SST, T99_PD,  T99_IM,  BADLEN,  BADLEN,  BADLEN }, "*w'I", "W",   NULL },
674169701Skan  { "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 },
675169701Skan  { "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 },
676169701Skan  { "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 },
677169701Skan  { "c",     1, STD_C89, { T89_C,   BADLEN,  BADLEN,  T94_W,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN }, "*w",   "cW",  NULL },
678169701Skan  { "s",     1, STD_C89, { T89_C,   BADLEN,  BADLEN,  T94_W,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN }, "*aw",  "cW",  NULL },
679169701Skan  { "[",     1, STD_C89, { T89_C,   BADLEN,  BADLEN,  T94_W,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN }, "*aw",  "cW[", NULL },
680169701Skan  { "p",     2, STD_C89, { T89_V,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN }, "*w",   "W",   NULL },
681169701Skan  { "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 },
68290075Sobrien  /* C99 conversion specifiers.  */
683169701Skan  { "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 },
684169701Skan  { "aA",   1, STD_C99, { T99_F,   BADLEN,  BADLEN,  T99_D,   BADLEN,  T99_LD,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN }, "*w'",  "W",   NULL },
68590075Sobrien  /* X/Open conversion specifiers.  */
686169701Skan  { "C",     1, STD_EXT, { TEX_W,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN }, "*w",   "W",   NULL },
687169701Skan  { "S",     1, STD_EXT, { TEX_W,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN }, "*aw",  "W",   NULL },
688169701Skan  { NULL, 0, 0, NOLENGTHS, NULL, NULL, NULL }
68990075Sobrien};
69090075Sobrien
69190075Sobrienstatic const format_char_info time_char_table[] =
69290075Sobrien{
69390075Sobrien  /* C89 conversion specifiers.  */
694169701Skan  { "ABZab",		0, STD_C89, NOLENGTHS, "^#",     "",   NULL },
695169701Skan  { "cx",		0, STD_C89, NOLENGTHS, "E",      "3",  NULL },
696169701Skan  { "HIMSUWdmw",	0, STD_C89, NOLENGTHS, "-_0Ow",  "",   NULL },
697169701Skan  { "j",		0, STD_C89, NOLENGTHS, "-_0Ow",  "o",  NULL },
698169701Skan  { "p",		0, STD_C89, NOLENGTHS, "#",      "",   NULL },
699169701Skan  { "X",		0, STD_C89, NOLENGTHS, "E",      "",   NULL },
700169701Skan  { "y",		0, STD_C89, NOLENGTHS, "EO-_0w", "4",  NULL },
701169701Skan  { "Y",		0, STD_C89, NOLENGTHS, "-_0EOw", "o",  NULL },
702169701Skan  { "%",		0, STD_C89, NOLENGTHS, "",       "",   NULL },
70390075Sobrien  /* C99 conversion specifiers.  */
704169701Skan  { "C",		0, STD_C99, NOLENGTHS, "-_0EOw", "o",  NULL },
705169701Skan  { "D",		0, STD_C99, NOLENGTHS, "",       "2",  NULL },
706169701Skan  { "eVu",		0, STD_C99, NOLENGTHS, "-_0Ow",  "",   NULL },
707169701Skan  { "FRTnrt",		0, STD_C99, NOLENGTHS, "",       "",   NULL },
708169701Skan  { "g",		0, STD_C99, NOLENGTHS, "O-_0w",  "2o", NULL },
709169701Skan  { "G",		0, STD_C99, NOLENGTHS, "-_0Ow",  "o",  NULL },
710169701Skan  { "h",		0, STD_C99, NOLENGTHS, "^#",     "",   NULL },
711169701Skan  { "z",		0, STD_C99, NOLENGTHS, "O",      "o",  NULL },
71290075Sobrien  /* GNU conversion specifiers.  */
713169701Skan  { "kls",		0, STD_EXT, NOLENGTHS, "-_0Ow",  "",   NULL },
714169701Skan  { "P",		0, STD_EXT, NOLENGTHS, "",       "",   NULL },
715169701Skan  { NULL,		0, 0, NOLENGTHS, NULL, NULL, NULL }
71690075Sobrien};
71790075Sobrien
71890075Sobrienstatic const format_char_info monetary_char_table[] =
71990075Sobrien{
720169701Skan  { "in", 0, STD_C89, { T89_D, BADLEN, BADLEN, BADLEN, BADLEN, T89_LD, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "=^+(!-w#p", "", NULL },
721169701Skan  { NULL, 0, 0, NOLENGTHS, NULL, NULL, NULL }
72290075Sobrien};
72390075Sobrien
72490075Sobrien/* This must be in the same order as enum format_type.  */
725132731Skanstatic const format_kind_info format_types_orig[] =
72690075Sobrien{
727169701Skan  { "printf",   printf_length_specs,  print_char_table, " +#0-'I", NULL,
72890075Sobrien    printf_flag_specs, printf_flag_pairs,
72990075Sobrien    FMT_FLAG_ARG_CONVERT|FMT_FLAG_DOLLAR_MULTIPLE|FMT_FLAG_USE_DOLLAR|FMT_FLAG_EMPTY_PREC_OK,
73090075Sobrien    'w', 0, 'p', 0, 'L',
731169701Skan    &integer_type_node, &integer_type_node
73290075Sobrien  },
733169701Skan  { "asm_fprintf",   asm_fprintf_length_specs,  asm_fprintf_char_table, " +#0-", NULL,
734132731Skan    asm_fprintf_flag_specs, asm_fprintf_flag_pairs,
735132731Skan    FMT_FLAG_ARG_CONVERT|FMT_FLAG_EMPTY_PREC_OK,
736132731Skan    'w', 0, 'p', 0, 'L',
737132731Skan    NULL, NULL
738132731Skan  },
739169701Skan  { "gcc_diag",   gcc_diag_length_specs,  gcc_diag_char_table, "q+", NULL,
740132731Skan    gcc_diag_flag_specs, gcc_diag_flag_pairs,
741132731Skan    FMT_FLAG_ARG_CONVERT,
742132731Skan    0, 0, 'p', 0, 'L',
743132731Skan    NULL, &integer_type_node
744132731Skan  },
745169701Skan  { "gcc_tdiag",   gcc_tdiag_length_specs,  gcc_tdiag_char_table, "q+", NULL,
746169701Skan    gcc_tdiag_flag_specs, gcc_tdiag_flag_pairs,
747169701Skan    FMT_FLAG_ARG_CONVERT,
748169701Skan    0, 0, 'p', 0, 'L',
749169701Skan    NULL, &integer_type_node
750169701Skan  },
751169701Skan  { "gcc_cdiag",   gcc_cdiag_length_specs,  gcc_cdiag_char_table, "q+", NULL,
752132731Skan    gcc_cdiag_flag_specs, gcc_cdiag_flag_pairs,
753132731Skan    FMT_FLAG_ARG_CONVERT,
754132731Skan    0, 0, 'p', 0, 'L',
755132731Skan    NULL, &integer_type_node
756132731Skan  },
757169701Skan  { "gcc_cxxdiag",   gcc_cxxdiag_length_specs,  gcc_cxxdiag_char_table, "q+#", NULL,
758132731Skan    gcc_cxxdiag_flag_specs, gcc_cxxdiag_flag_pairs,
759132731Skan    FMT_FLAG_ARG_CONVERT,
760132731Skan    0, 0, 'p', 0, 'L',
761132731Skan    NULL, &integer_type_node
762132731Skan  },
763169701Skan  { "gcc_gfc", NULL, gcc_gfc_char_table, "", NULL,
764169701Skan    NULL, gcc_gfc_flag_pairs,
765169701Skan    FMT_FLAG_ARG_CONVERT,
766169701Skan    0, 0, 0, 0, 0,
767169701Skan    NULL, NULL
768169701Skan  },
769169701Skan  { "scanf",    scanf_length_specs,   scan_char_table,  "*'I", NULL,
77090075Sobrien    scanf_flag_specs, scanf_flag_pairs,
77190075Sobrien    FMT_FLAG_ARG_CONVERT|FMT_FLAG_SCANF_A_KLUDGE|FMT_FLAG_USE_DOLLAR|FMT_FLAG_ZERO_WIDTH_BAD|FMT_FLAG_DOLLAR_GAP_POINTER_OK,
77290075Sobrien    'w', 0, 0, '*', 'L',
773169701Skan    NULL, NULL
77490075Sobrien  },
77590075Sobrien  { "strftime", NULL,                 time_char_table,  "_-0^#", "EO",
77690075Sobrien    strftime_flag_specs, strftime_flag_pairs,
77790075Sobrien    FMT_FLAG_FANCY_PERCENT_OK, 'w', 0, 0, 0, 0,
778169701Skan    NULL, NULL
77990075Sobrien  },
780169701Skan  { "strfmon",  strfmon_length_specs, monetary_char_table, "=^+(!-", NULL,
78190075Sobrien    strfmon_flag_specs, strfmon_flag_pairs,
78290075Sobrien    FMT_FLAG_ARG_CONVERT, 'w', '#', 'p', 0, 'L',
783169701Skan    NULL, NULL
78497130Sobrien  },
785169701Skan  { "printf0",   printf_length_specs,  print_char_table, " +#0-'I", NULL,
78697130Sobrien    printf_flag_specs, printf_flag_pairs,
787169701Skan    FMT_FLAG_ARG_CONVERT|FMT_FLAG_DOLLAR_MULTIPLE|FMT_FLAG_USE_DOLLAR|FMT_FLAG_EMPTY_PREC_OK|FMT_FLAG_NULL_FORMAT_OK,
788169701Skan    'w', 0, 'p', 0, 'L',
789169701Skan    &integer_type_node, &integer_type_node
79090075Sobrien  }
79190075Sobrien};
79290075Sobrien
793132731Skan/* This layer of indirection allows GCC to reassign format_types with
794132731Skan   new data if necessary, while still allowing the original data to be
795132731Skan   const.  */
796132731Skanstatic const format_kind_info *format_types = format_types_orig;
797169701Skan/* We can modify this one.  We also add target-specific format types
798169701Skan   to the end of the array.  */
799132731Skanstatic format_kind_info *dynamic_format_types;
80090075Sobrien
801169701Skanstatic int n_format_types = ARRAY_SIZE (format_types_orig);
802169701Skan
80390075Sobrien/* Structure detailing the results of checking a format function call
80490075Sobrien   where the format expression may be a conditional expression with
80590075Sobrien   many leaves resulting from nested conditional expressions.  */
80690075Sobrientypedef struct
80790075Sobrien{
80890075Sobrien  /* Number of leaves of the format argument that could not be checked
80990075Sobrien     as they were not string literals.  */
81090075Sobrien  int number_non_literal;
81190075Sobrien  /* Number of leaves of the format argument that were null pointers or
81290075Sobrien     string literals, but had extra format arguments.  */
81390075Sobrien  int number_extra_args;
81490075Sobrien  /* Number of leaves of the format argument that were null pointers or
81590075Sobrien     string literals, but had extra format arguments and used $ operand
81690075Sobrien     numbers.  */
81790075Sobrien  int number_dollar_extra_args;
81890075Sobrien  /* Number of leaves of the format argument that were wide string
81990075Sobrien     literals.  */
82090075Sobrien  int number_wide;
82190075Sobrien  /* Number of leaves of the format argument that were empty strings.  */
82290075Sobrien  int number_empty;
82390075Sobrien  /* Number of leaves of the format argument that were unterminated
82490075Sobrien     strings.  */
82590075Sobrien  int number_unterminated;
82690075Sobrien  /* Number of leaves of the format argument that were not counted above.  */
82790075Sobrien  int number_other;
82890075Sobrien} format_check_results;
82990075Sobrien
830117422Skantypedef struct
831117422Skan{
832117422Skan  format_check_results *res;
833117422Skan  function_format_info *info;
834117422Skan  tree params;
835117422Skan} format_check_context;
836117422Skan
837169701Skanstatic void check_format_info (function_format_info *, tree);
838132731Skanstatic void check_format_arg (void *, tree, unsigned HOST_WIDE_INT);
839169701Skanstatic void check_format_info_main (format_check_results *,
840132731Skan				    function_format_info *,
841132731Skan				    const char *, int, tree,
842132731Skan				    unsigned HOST_WIDE_INT);
84390075Sobrien
844132731Skanstatic void init_dollar_format_checking (int, tree);
845169701Skanstatic int maybe_read_dollar_number (const char **, int,
846132731Skan				     tree, tree *, const format_kind_info *);
847169701Skanstatic bool avoid_dollar_number (const char *);
848169701Skanstatic void finish_dollar_format_checking (format_check_results *, int);
84990075Sobrien
850132731Skanstatic const format_flag_spec *get_flag_spec (const format_flag_spec *,
851132731Skan					      int, const char *);
85290075Sobrien
853169701Skanstatic void check_format_types (format_wanted_type *, const char *, int);
854169701Skanstatic void format_type_warning (const char *, const char *, int, tree,
855169701Skan				 int, const char *, tree, int);
85690075Sobrien
85790075Sobrien/* Decode a format type from a string, returning the type, or
85890075Sobrien   format_type_error if not valid, in which case the caller should print an
85990075Sobrien   error message.  */
860169701Skanstatic int
861132731Skandecode_format_type (const char *s)
86290075Sobrien{
86390075Sobrien  int i;
86490075Sobrien  int slen;
86590075Sobrien  slen = strlen (s);
866169701Skan  for (i = 0; i < n_format_types; i++)
86790075Sobrien    {
86890075Sobrien      int alen;
86990075Sobrien      if (!strcmp (s, format_types[i].name))
870169701Skan	return i;
87190075Sobrien      alen = strlen (format_types[i].name);
87290075Sobrien      if (slen == alen + 4 && s[0] == '_' && s[1] == '_'
87390075Sobrien	  && s[slen - 1] == '_' && s[slen - 2] == '_'
87490075Sobrien	  && !strncmp (s + 2, format_types[i].name, alen))
875169701Skan	return i;
87690075Sobrien    }
877169701Skan  return format_type_error;
87890075Sobrien}
87990075Sobrien
88090075Sobrien
88190075Sobrien/* Check the argument list of a call to printf, scanf, etc.
88290075Sobrien   ATTRS are the attributes on the function type.
88390075Sobrien   PARAMS is the list of argument values.  Also, if -Wmissing-format-attribute,
88490075Sobrien   warn for calls to vprintf or vscanf in functions with no such format
88590075Sobrien   attribute themselves.  */
88690075Sobrien
88790075Sobrienvoid
888169701Skancheck_function_format (tree attrs, tree params)
88990075Sobrien{
89090075Sobrien  tree a;
89190075Sobrien
89290075Sobrien  /* See if this function has any format attributes.  */
89390075Sobrien  for (a = attrs; a; a = TREE_CHAIN (a))
89490075Sobrien    {
89590075Sobrien      if (is_attribute_p ("format", TREE_PURPOSE (a)))
89690075Sobrien	{
89790075Sobrien	  /* Yup; check it.  */
89890075Sobrien	  function_format_info info;
89990075Sobrien	  decode_format_attr (TREE_VALUE (a), &info, 1);
900169701Skan	  if (warn_format)
901169701Skan	    check_format_info (&info, params);
90290075Sobrien	  if (warn_missing_format_attribute && info.first_arg_num == 0
90390075Sobrien	      && (format_types[info.format_type].flags
90490075Sobrien		  & (int) FMT_FLAG_ARG_CONVERT))
90590075Sobrien	    {
90690075Sobrien	      tree c;
90790075Sobrien	      for (c = TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl));
90890075Sobrien		   c;
90990075Sobrien		   c = TREE_CHAIN (c))
91090075Sobrien		if (is_attribute_p ("format", TREE_PURPOSE (c))
91190075Sobrien		    && (decode_format_type (IDENTIFIER_POINTER
91290075Sobrien					    (TREE_VALUE (TREE_VALUE (c))))
91390075Sobrien			== info.format_type))
91490075Sobrien		  break;
91590075Sobrien	      if (c == NULL_TREE)
91690075Sobrien		{
91790075Sobrien		  /* Check if the current function has a parameter to which
91890075Sobrien		     the format attribute could be attached; if not, it
91990075Sobrien		     can't be a candidate for a format attribute, despite
92090075Sobrien		     the vprintf-like or vscanf-like call.  */
92190075Sobrien		  tree args;
92290075Sobrien		  for (args = DECL_ARGUMENTS (current_function_decl);
92390075Sobrien		       args != 0;
92490075Sobrien		       args = TREE_CHAIN (args))
92590075Sobrien		    {
92690075Sobrien		      if (TREE_CODE (TREE_TYPE (args)) == POINTER_TYPE
92790075Sobrien			  && (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (args)))
92890075Sobrien			      == char_type_node))
92990075Sobrien			break;
93090075Sobrien		    }
93190075Sobrien		  if (args != 0)
932169701Skan		    warning (OPT_Wmissing_format_attribute, "function might "
933169701Skan			     "be possible candidate for %qs format attribute",
93490075Sobrien			     format_types[info.format_type].name);
93590075Sobrien		}
93690075Sobrien	    }
93790075Sobrien	}
93890075Sobrien    }
93990075Sobrien}
94090075Sobrien
94190075Sobrien
94290075Sobrien/* Variables used by the checking of $ operand number formats.  */
94390075Sobrienstatic char *dollar_arguments_used = NULL;
94490075Sobrienstatic char *dollar_arguments_pointer_p = NULL;
94590075Sobrienstatic int dollar_arguments_alloc = 0;
94690075Sobrienstatic int dollar_arguments_count;
94790075Sobrienstatic int dollar_first_arg_num;
94890075Sobrienstatic int dollar_max_arg_used;
94990075Sobrienstatic int dollar_format_warned;
95090075Sobrien
95190075Sobrien/* Initialize the checking for a format string that may contain $
95290075Sobrien   parameter number specifications; we will need to keep track of whether
95390075Sobrien   each parameter has been used.  FIRST_ARG_NUM is the number of the first
95490075Sobrien   argument that is a parameter to the format, or 0 for a vprintf-style
95590075Sobrien   function; PARAMS is the list of arguments starting at this argument.  */
95690075Sobrien
95790075Sobrienstatic void
958132731Skaninit_dollar_format_checking (int first_arg_num, tree params)
95990075Sobrien{
96090075Sobrien  tree oparams = params;
96190075Sobrien
96290075Sobrien  dollar_first_arg_num = first_arg_num;
96390075Sobrien  dollar_arguments_count = 0;
96490075Sobrien  dollar_max_arg_used = 0;
96590075Sobrien  dollar_format_warned = 0;
96690075Sobrien  if (first_arg_num > 0)
96790075Sobrien    {
96890075Sobrien      while (params)
96990075Sobrien	{
97090075Sobrien	  dollar_arguments_count++;
97190075Sobrien	  params = TREE_CHAIN (params);
97290075Sobrien	}
97390075Sobrien    }
97490075Sobrien  if (dollar_arguments_alloc < dollar_arguments_count)
97590075Sobrien    {
97690075Sobrien      if (dollar_arguments_used)
97790075Sobrien	free (dollar_arguments_used);
97890075Sobrien      if (dollar_arguments_pointer_p)
97990075Sobrien	free (dollar_arguments_pointer_p);
98090075Sobrien      dollar_arguments_alloc = dollar_arguments_count;
981169701Skan      dollar_arguments_used = XNEWVEC (char, dollar_arguments_alloc);
982169701Skan      dollar_arguments_pointer_p = XNEWVEC (char, dollar_arguments_alloc);
98390075Sobrien    }
98490075Sobrien  if (dollar_arguments_alloc)
98590075Sobrien    {
98690075Sobrien      memset (dollar_arguments_used, 0, dollar_arguments_alloc);
98790075Sobrien      if (first_arg_num > 0)
98890075Sobrien	{
98990075Sobrien	  int i = 0;
99090075Sobrien	  params = oparams;
99190075Sobrien	  while (params)
99290075Sobrien	    {
99390075Sobrien	      dollar_arguments_pointer_p[i] = (TREE_CODE (TREE_TYPE (TREE_VALUE (params)))
99490075Sobrien					       == POINTER_TYPE);
99590075Sobrien	      params = TREE_CHAIN (params);
99690075Sobrien	      i++;
99790075Sobrien	    }
99890075Sobrien	}
99990075Sobrien    }
100090075Sobrien}
100190075Sobrien
100290075Sobrien
100390075Sobrien/* Look for a decimal number followed by a $ in *FORMAT.  If DOLLAR_NEEDED
100490075Sobrien   is set, it is an error if one is not found; otherwise, it is OK.  If
100590075Sobrien   such a number is found, check whether it is within range and mark that
100690075Sobrien   numbered operand as being used for later checking.  Returns the operand
100790075Sobrien   number if found and within range, zero if no such number was found and
100890075Sobrien   this is OK, or -1 on error.  PARAMS points to the first operand of the
100990075Sobrien   format; PARAM_PTR is made to point to the parameter referred to.  If
101090075Sobrien   a $ format is found, *FORMAT is updated to point just after it.  */
101190075Sobrien
101290075Sobrienstatic int
1013169701Skanmaybe_read_dollar_number (const char **format,
1014132731Skan			  int dollar_needed, tree params, tree *param_ptr,
1015132731Skan			  const format_kind_info *fki)
101690075Sobrien{
101790075Sobrien  int argnum;
101890075Sobrien  int overflow_flag;
101990075Sobrien  const char *fcp = *format;
1020169701Skan  if (!ISDIGIT (*fcp))
102190075Sobrien    {
102290075Sobrien      if (dollar_needed)
102390075Sobrien	{
1024169701Skan	  warning (OPT_Wformat, "missing $ operand number in format");
102590075Sobrien	  return -1;
102690075Sobrien	}
102790075Sobrien      else
102890075Sobrien	return 0;
102990075Sobrien    }
103090075Sobrien  argnum = 0;
103190075Sobrien  overflow_flag = 0;
103290075Sobrien  while (ISDIGIT (*fcp))
103390075Sobrien    {
103490075Sobrien      int nargnum;
103590075Sobrien      nargnum = 10 * argnum + (*fcp - '0');
103690075Sobrien      if (nargnum < 0 || nargnum / 10 != argnum)
103790075Sobrien	overflow_flag = 1;
103890075Sobrien      argnum = nargnum;
103990075Sobrien      fcp++;
104090075Sobrien    }
104190075Sobrien  if (*fcp != '$')
104290075Sobrien    {
104390075Sobrien      if (dollar_needed)
104490075Sobrien	{
1045169701Skan	  warning (OPT_Wformat, "missing $ operand number in format");
104690075Sobrien	  return -1;
104790075Sobrien	}
104890075Sobrien      else
104990075Sobrien	return 0;
105090075Sobrien    }
105190075Sobrien  *format = fcp + 1;
105290075Sobrien  if (pedantic && !dollar_format_warned)
105390075Sobrien    {
1054169701Skan      warning (OPT_Wformat, "%s does not support %%n$ operand number formats",
1055169701Skan	       C_STD_NAME (STD_EXT));
105690075Sobrien      dollar_format_warned = 1;
105790075Sobrien    }
105890075Sobrien  if (overflow_flag || argnum == 0
105990075Sobrien      || (dollar_first_arg_num && argnum > dollar_arguments_count))
106090075Sobrien    {
1061169701Skan      warning (OPT_Wformat, "operand number out of range in format");
106290075Sobrien      return -1;
106390075Sobrien    }
106490075Sobrien  if (argnum > dollar_max_arg_used)
106590075Sobrien    dollar_max_arg_used = argnum;
106690075Sobrien  /* For vprintf-style functions we may need to allocate more memory to
106790075Sobrien     track which arguments are used.  */
106890075Sobrien  while (dollar_arguments_alloc < dollar_max_arg_used)
106990075Sobrien    {
107090075Sobrien      int nalloc;
107190075Sobrien      nalloc = 2 * dollar_arguments_alloc + 16;
1072169701Skan      dollar_arguments_used = XRESIZEVEC (char, dollar_arguments_used,
1073169701Skan					  nalloc);
1074169701Skan      dollar_arguments_pointer_p = XRESIZEVEC (char, dollar_arguments_pointer_p,
1075169701Skan					       nalloc);
107690075Sobrien      memset (dollar_arguments_used + dollar_arguments_alloc, 0,
107790075Sobrien	      nalloc - dollar_arguments_alloc);
107890075Sobrien      dollar_arguments_alloc = nalloc;
107990075Sobrien    }
108090075Sobrien  if (!(fki->flags & (int) FMT_FLAG_DOLLAR_MULTIPLE)
108190075Sobrien      && dollar_arguments_used[argnum - 1] == 1)
108290075Sobrien    {
108390075Sobrien      dollar_arguments_used[argnum - 1] = 2;
1084169701Skan      warning (OPT_Wformat, "format argument %d used more than once in %s format",
1085169701Skan	       argnum, fki->name);
108690075Sobrien    }
108790075Sobrien  else
108890075Sobrien    dollar_arguments_used[argnum - 1] = 1;
108990075Sobrien  if (dollar_first_arg_num)
109090075Sobrien    {
109190075Sobrien      int i;
109290075Sobrien      *param_ptr = params;
109390075Sobrien      for (i = 1; i < argnum && *param_ptr != 0; i++)
109490075Sobrien	*param_ptr = TREE_CHAIN (*param_ptr);
109590075Sobrien
1096169701Skan      /* This case shouldn't be caught here.  */
1097169701Skan      gcc_assert (*param_ptr);
109890075Sobrien    }
109990075Sobrien  else
110090075Sobrien    *param_ptr = 0;
110190075Sobrien  return argnum;
110290075Sobrien}
110390075Sobrien
1104169701Skan/* Ensure that FORMAT does not start with a decimal number followed by
1105169701Skan   a $; give a diagnostic and return true if it does, false otherwise.  */
110690075Sobrien
1107169701Skanstatic bool
1108169701Skanavoid_dollar_number (const char *format)
1109169701Skan{
1110169701Skan  if (!ISDIGIT (*format))
1111169701Skan    return false;
1112169701Skan  while (ISDIGIT (*format))
1113169701Skan    format++;
1114169701Skan  if (*format == '$')
1115169701Skan    {
1116169701Skan      warning (OPT_Wformat, "$ operand number used after format without operand number");
1117169701Skan      return true;
1118169701Skan    }
1119169701Skan  return false;
1120169701Skan}
1121169701Skan
1122169701Skan
112390075Sobrien/* Finish the checking for a format string that used $ operand number formats
112490075Sobrien   instead of non-$ formats.  We check for unused operands before used ones
112590075Sobrien   (a serious error, since the implementation of the format function
112690075Sobrien   can't know what types to pass to va_arg to find the later arguments).
112790075Sobrien   and for unused operands at the end of the format (if we know how many
112890075Sobrien   arguments the format had, so not for vprintf).  If there were operand
112990075Sobrien   numbers out of range on a non-vprintf-style format, we won't have reached
113090075Sobrien   here.  If POINTER_GAP_OK, unused arguments are OK if all arguments are
113190075Sobrien   pointers.  */
113290075Sobrien
113390075Sobrienstatic void
1134169701Skanfinish_dollar_format_checking (format_check_results *res, int pointer_gap_ok)
113590075Sobrien{
113690075Sobrien  int i;
113790075Sobrien  bool found_pointer_gap = false;
113890075Sobrien  for (i = 0; i < dollar_max_arg_used; i++)
113990075Sobrien    {
114090075Sobrien      if (!dollar_arguments_used[i])
114190075Sobrien	{
114290075Sobrien	  if (pointer_gap_ok && (dollar_first_arg_num == 0
114390075Sobrien				 || dollar_arguments_pointer_p[i]))
114490075Sobrien	    found_pointer_gap = true;
114590075Sobrien	  else
1146169701Skan	    warning (OPT_Wformat,
1147169701Skan		     "format argument %d unused before used argument %d in $-style format",
1148169701Skan		     i + 1, dollar_max_arg_used);
114990075Sobrien	}
115090075Sobrien    }
115190075Sobrien  if (found_pointer_gap
115290075Sobrien      || (dollar_first_arg_num
115390075Sobrien	  && dollar_max_arg_used < dollar_arguments_count))
115490075Sobrien    {
115590075Sobrien      res->number_other--;
115690075Sobrien      res->number_dollar_extra_args++;
115790075Sobrien    }
115890075Sobrien}
115990075Sobrien
116090075Sobrien
116190075Sobrien/* Retrieve the specification for a format flag.  SPEC contains the
116290075Sobrien   specifications for format flags for the applicable kind of format.
116390075Sobrien   FLAG is the flag in question.  If PREDICATES is NULL, the basic
1164169701Skan   spec for that flag must be retrieved and must exist.  If
1165169701Skan   PREDICATES is not NULL, it is a string listing possible predicates
1166169701Skan   for the spec entry; if an entry predicated on any of these is
1167169701Skan   found, it is returned, otherwise NULL is returned.  */
116890075Sobrien
116990075Sobrienstatic const format_flag_spec *
1170132731Skanget_flag_spec (const format_flag_spec *spec, int flag, const char *predicates)
117190075Sobrien{
117290075Sobrien  int i;
117390075Sobrien  for (i = 0; spec[i].flag_char != 0; i++)
117490075Sobrien    {
117590075Sobrien      if (spec[i].flag_char != flag)
117690075Sobrien	continue;
117790075Sobrien      if (predicates != NULL)
117890075Sobrien	{
117990075Sobrien	  if (spec[i].predicate != 0
118090075Sobrien	      && strchr (predicates, spec[i].predicate) != 0)
118190075Sobrien	    return &spec[i];
118290075Sobrien	}
118390075Sobrien      else if (spec[i].predicate == 0)
118490075Sobrien	return &spec[i];
118590075Sobrien    }
1186169701Skan  gcc_assert (predicates);
1187169701Skan  return NULL;
118890075Sobrien}
118990075Sobrien
119090075Sobrien
119190075Sobrien/* Check the argument list of a call to printf, scanf, etc.
119290075Sobrien   INFO points to the function_format_info structure.
119390075Sobrien   PARAMS is the list of argument values.  */
119490075Sobrien
119590075Sobrienstatic void
1196169701Skancheck_format_info (function_format_info *info, tree params)
119790075Sobrien{
1198117422Skan  format_check_context format_ctx;
119990075Sobrien  unsigned HOST_WIDE_INT arg_num;
120090075Sobrien  tree format_tree;
120190075Sobrien  format_check_results res;
120290075Sobrien  /* Skip to format argument.  If the argument isn't available, there's
120390075Sobrien     no work for us to do; prototype checking will catch the problem.  */
120490075Sobrien  for (arg_num = 1; ; ++arg_num)
120590075Sobrien    {
120690075Sobrien      if (params == 0)
120790075Sobrien	return;
120890075Sobrien      if (arg_num == info->format_num)
120990075Sobrien	break;
121090075Sobrien      params = TREE_CHAIN (params);
121190075Sobrien    }
121290075Sobrien  format_tree = TREE_VALUE (params);
121390075Sobrien  params = TREE_CHAIN (params);
121490075Sobrien  if (format_tree == 0)
121590075Sobrien    return;
121690075Sobrien
121790075Sobrien  res.number_non_literal = 0;
121890075Sobrien  res.number_extra_args = 0;
121990075Sobrien  res.number_dollar_extra_args = 0;
122090075Sobrien  res.number_wide = 0;
122190075Sobrien  res.number_empty = 0;
122290075Sobrien  res.number_unterminated = 0;
122390075Sobrien  res.number_other = 0;
122490075Sobrien
1225117422Skan  format_ctx.res = &res;
1226117422Skan  format_ctx.info = info;
1227117422Skan  format_ctx.params = params;
122890075Sobrien
1229117422Skan  check_function_arguments_recurse (check_format_arg, &format_ctx,
1230117422Skan				    format_tree, arg_num);
1231117422Skan
123290075Sobrien  if (res.number_non_literal > 0)
123390075Sobrien    {
123490075Sobrien      /* Functions taking a va_list normally pass a non-literal format
123590075Sobrien	 string.  These functions typically are declared with
123690075Sobrien	 first_arg_num == 0, so avoid warning in those cases.  */
123790075Sobrien      if (!(format_types[info->format_type].flags & (int) FMT_FLAG_ARG_CONVERT))
123890075Sobrien	{
123990075Sobrien	  /* For strftime-like formats, warn for not checking the format
124090075Sobrien	     string; but there are no arguments to check.  */
1241169701Skan	  warning (OPT_Wformat_nonliteral,
1242169701Skan		   "format not a string literal, format string not checked");
124390075Sobrien	}
124490075Sobrien      else if (info->first_arg_num != 0)
124590075Sobrien	{
124690075Sobrien	  /* If there are no arguments for the format at all, we may have
124790075Sobrien	     printf (foo) which is likely to be a security hole.  */
124890075Sobrien	  while (arg_num + 1 < info->first_arg_num)
124990075Sobrien	    {
125090075Sobrien	      if (params == 0)
125190075Sobrien		break;
125290075Sobrien	      params = TREE_CHAIN (params);
125390075Sobrien	      ++arg_num;
125490075Sobrien	    }
1255169701Skan	  if (params == 0 && warn_format_security)
1256169701Skan	    warning (OPT_Wformat_security,
1257169701Skan		     "format not a string literal and no format arguments");
1258169701Skan	  else if (params == 0 && warn_format_nonliteral)
1259169701Skan	    warning (OPT_Wformat_nonliteral,
1260169701Skan		     "format not a string literal and no format arguments");
1261169701Skan	  else
1262169701Skan	    warning (OPT_Wformat_nonliteral,
1263169701Skan		     "format not a string literal, argument types not checked");
126490075Sobrien	}
126590075Sobrien    }
126690075Sobrien
126790075Sobrien  /* If there were extra arguments to the format, normally warn.  However,
126890075Sobrien     the standard does say extra arguments are ignored, so in the specific
126990075Sobrien     case where we have multiple leaves (conditional expressions or
127090075Sobrien     ngettext) allow extra arguments if at least one leaf didn't have extra
127190075Sobrien     arguments, but was otherwise OK (either non-literal or checked OK).
127290075Sobrien     If the format is an empty string, this should be counted similarly to the
127390075Sobrien     case of extra format arguments.  */
127490075Sobrien  if (res.number_extra_args > 0 && res.number_non_literal == 0
1275169701Skan      && res.number_other == 0)
1276169701Skan    warning (OPT_Wformat_extra_args, "too many arguments for format");
127790075Sobrien  if (res.number_dollar_extra_args > 0 && res.number_non_literal == 0
1278169701Skan      && res.number_other == 0)
1279169701Skan    warning (OPT_Wformat_extra_args, "unused arguments in $-style format");
128090075Sobrien  if (res.number_empty > 0 && res.number_non_literal == 0
1281169701Skan      && res.number_other == 0)
1282169701Skan    warning (OPT_Wformat_zero_length, "zero-length %s format string",
1283169701Skan	     format_types[info->format_type].name);
128490075Sobrien
128590075Sobrien  if (res.number_wide > 0)
1286169701Skan    warning (OPT_Wformat, "format is a wide character string");
128790075Sobrien
128890075Sobrien  if (res.number_unterminated > 0)
1289169701Skan    warning (OPT_Wformat, "unterminated format string");
129090075Sobrien}
129190075Sobrien
1292117422Skan/* Callback from check_function_arguments_recurse to check a
1293117422Skan   format string.  FORMAT_TREE is the format parameter.  ARG_NUM
1294117422Skan   is the number of the format argument.  CTX points to a
1295117422Skan   format_check_context.  */
129690075Sobrien
129790075Sobrienstatic void
1298132731Skancheck_format_arg (void *ctx, tree format_tree,
1299132731Skan		  unsigned HOST_WIDE_INT arg_num)
130090075Sobrien{
1301169701Skan  format_check_context *format_ctx = (format_check_context *) ctx;
1302117422Skan  format_check_results *res = format_ctx->res;
1303117422Skan  function_format_info *info = format_ctx->info;
1304117422Skan  tree params = format_ctx->params;
1305117422Skan
130690075Sobrien  int format_length;
130790075Sobrien  HOST_WIDE_INT offset;
130890075Sobrien  const char *format_chars;
130990075Sobrien  tree array_size = 0;
131090075Sobrien  tree array_init;
131190075Sobrien
131290075Sobrien  if (integer_zerop (format_tree))
131390075Sobrien    {
131490075Sobrien      /* FIXME: this warning should go away once Marc Espie's
131590075Sobrien	 __attribute__((nonnull)) patch is in.  Instead, checking for
131690075Sobrien	 nonnull attributes should probably change this function to act
131790075Sobrien	 specially if info == NULL and add a res->number_null entry for
131890075Sobrien	 that case, or maybe add a function pointer to be called at
131990075Sobrien	 the end instead of hardcoding check_format_info_main.  */
1320169701Skan      if (!(format_types[info->format_type].flags & FMT_FLAG_NULL_FORMAT_OK))
1321169701Skan	warning (OPT_Wformat, "null format string");
132290075Sobrien
132390075Sobrien      /* Skip to first argument to check, so we can see if this format
132490075Sobrien	 has any arguments (it shouldn't).  */
132590075Sobrien      while (arg_num + 1 < info->first_arg_num)
132690075Sobrien	{
132790075Sobrien	  if (params == 0)
132890075Sobrien	    return;
132990075Sobrien	  params = TREE_CHAIN (params);
133090075Sobrien	  ++arg_num;
133190075Sobrien	}
133290075Sobrien
133390075Sobrien      if (params == 0)
133490075Sobrien	res->number_other++;
133590075Sobrien      else
133690075Sobrien	res->number_extra_args++;
133790075Sobrien
133890075Sobrien      return;
133990075Sobrien    }
134090075Sobrien
134190075Sobrien  offset = 0;
134290075Sobrien  if (TREE_CODE (format_tree) == PLUS_EXPR)
134390075Sobrien    {
134490075Sobrien      tree arg0, arg1;
134590075Sobrien
134690075Sobrien      arg0 = TREE_OPERAND (format_tree, 0);
134790075Sobrien      arg1 = TREE_OPERAND (format_tree, 1);
134890075Sobrien      STRIP_NOPS (arg0);
134990075Sobrien      STRIP_NOPS (arg1);
135090075Sobrien      if (TREE_CODE (arg1) == INTEGER_CST)
135190075Sobrien	format_tree = arg0;
135290075Sobrien      else if (TREE_CODE (arg0) == INTEGER_CST)
135390075Sobrien	{
135490075Sobrien	  format_tree = arg1;
135590075Sobrien	  arg1 = arg0;
135690075Sobrien	}
135790075Sobrien      else
135890075Sobrien	{
135990075Sobrien	  res->number_non_literal++;
136090075Sobrien	  return;
136190075Sobrien	}
136296263Sobrien      if (!host_integerp (arg1, 0)
136396263Sobrien	  || (offset = tree_low_cst (arg1, 0)) < 0)
136490075Sobrien	{
136590075Sobrien	  res->number_non_literal++;
136690075Sobrien	  return;
136790075Sobrien	}
136890075Sobrien    }
136990075Sobrien  if (TREE_CODE (format_tree) != ADDR_EXPR)
137090075Sobrien    {
137190075Sobrien      res->number_non_literal++;
137290075Sobrien      return;
137390075Sobrien    }
137490075Sobrien  format_tree = TREE_OPERAND (format_tree, 0);
1375169701Skan  if (TREE_CODE (format_tree) == ARRAY_REF
1376169701Skan      && host_integerp (TREE_OPERAND (format_tree, 1), 0)
1377169701Skan      && (offset += tree_low_cst (TREE_OPERAND (format_tree, 1), 0)) >= 0)
1378169701Skan    format_tree = TREE_OPERAND (format_tree, 0);
137990075Sobrien  if (TREE_CODE (format_tree) == VAR_DECL
138090075Sobrien      && TREE_CODE (TREE_TYPE (format_tree)) == ARRAY_TYPE
138190075Sobrien      && (array_init = decl_constant_value (format_tree)) != format_tree
138290075Sobrien      && TREE_CODE (array_init) == STRING_CST)
138390075Sobrien    {
138490075Sobrien      /* Extract the string constant initializer.  Note that this may include
138590075Sobrien	 a trailing NUL character that is not in the array (e.g.
138690075Sobrien	 const char a[3] = "foo";).  */
138790075Sobrien      array_size = DECL_SIZE_UNIT (format_tree);
138890075Sobrien      format_tree = array_init;
138990075Sobrien    }
139090075Sobrien  if (TREE_CODE (format_tree) != STRING_CST)
139190075Sobrien    {
139290075Sobrien      res->number_non_literal++;
139390075Sobrien      return;
139490075Sobrien    }
139590075Sobrien  if (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (format_tree))) != char_type_node)
139690075Sobrien    {
139790075Sobrien      res->number_wide++;
139890075Sobrien      return;
139990075Sobrien    }
140090075Sobrien  format_chars = TREE_STRING_POINTER (format_tree);
140190075Sobrien  format_length = TREE_STRING_LENGTH (format_tree);
140290075Sobrien  if (array_size != 0)
140390075Sobrien    {
140490075Sobrien      /* Variable length arrays can't be initialized.  */
1405169701Skan      gcc_assert (TREE_CODE (array_size) == INTEGER_CST);
1406169701Skan
140790075Sobrien      if (host_integerp (array_size, 0))
140890075Sobrien	{
140990075Sobrien	  HOST_WIDE_INT array_size_value = TREE_INT_CST_LOW (array_size);
141090075Sobrien	  if (array_size_value > 0
141190075Sobrien	      && array_size_value == (int) array_size_value
141290075Sobrien	      && format_length > array_size_value)
141390075Sobrien	    format_length = array_size_value;
141490075Sobrien	}
141590075Sobrien    }
141690075Sobrien  if (offset)
141790075Sobrien    {
141890075Sobrien      if (offset >= format_length)
141990075Sobrien	{
142090075Sobrien	  res->number_non_literal++;
142190075Sobrien	  return;
142290075Sobrien	}
142390075Sobrien      format_chars += offset;
142490075Sobrien      format_length -= offset;
142590075Sobrien    }
142690075Sobrien  if (format_length < 1)
142790075Sobrien    {
142890075Sobrien      res->number_unterminated++;
142990075Sobrien      return;
143090075Sobrien    }
143190075Sobrien  if (format_length == 1)
143290075Sobrien    {
143390075Sobrien      res->number_empty++;
143490075Sobrien      return;
143590075Sobrien    }
143690075Sobrien  if (format_chars[--format_length] != 0)
143790075Sobrien    {
143890075Sobrien      res->number_unterminated++;
143990075Sobrien      return;
144090075Sobrien    }
144190075Sobrien
144290075Sobrien  /* Skip to first argument to check.  */
144390075Sobrien  while (arg_num + 1 < info->first_arg_num)
144490075Sobrien    {
144590075Sobrien      if (params == 0)
144690075Sobrien	return;
144790075Sobrien      params = TREE_CHAIN (params);
144890075Sobrien      ++arg_num;
144990075Sobrien    }
145090075Sobrien  /* Provisionally increment res->number_other; check_format_info_main
145190075Sobrien     will decrement it if it finds there are extra arguments, but this way
145290075Sobrien     need not adjust it for every return.  */
145390075Sobrien  res->number_other++;
1454169701Skan  check_format_info_main (res, info, format_chars, format_length,
145590075Sobrien			  params, arg_num);
145690075Sobrien}
145790075Sobrien
145890075Sobrien
145990075Sobrien/* Do the main part of checking a call to a format function.  FORMAT_CHARS
146090075Sobrien   is the NUL-terminated format string (which at this point may contain
146190075Sobrien   internal NUL characters); FORMAT_LENGTH is its length (excluding the
146290075Sobrien   terminating NUL character).  ARG_NUM is one less than the number of
146390075Sobrien   the first format argument to check; PARAMS points to that format
146490075Sobrien   argument in the list of arguments.  */
146590075Sobrien
146690075Sobrienstatic void
1467169701Skancheck_format_info_main (format_check_results *res,
1468132731Skan			function_format_info *info, const char *format_chars,
1469132731Skan			int format_length, tree params,
1470132731Skan			unsigned HOST_WIDE_INT arg_num)
147190075Sobrien{
147290075Sobrien  const char *orig_format_chars = format_chars;
147390075Sobrien  tree first_fillin_param = params;
147490075Sobrien
147590075Sobrien  const format_kind_info *fki = &format_types[info->format_type];
147690075Sobrien  const format_flag_spec *flag_specs = fki->flag_specs;
147790075Sobrien  const format_flag_pair *bad_flag_pairs = fki->bad_flag_pairs;
147890075Sobrien
147990075Sobrien  /* -1 if no conversions taking an operand have been found; 0 if one has
148090075Sobrien     and it didn't use $; 1 if $ formats are in use.  */
148190075Sobrien  int has_operand_number = -1;
148290075Sobrien
148390075Sobrien  init_dollar_format_checking (info->first_arg_num, first_fillin_param);
148490075Sobrien
148590075Sobrien  while (1)
148690075Sobrien    {
148790075Sobrien      int i;
148890075Sobrien      int suppressed = FALSE;
148990075Sobrien      const char *length_chars = NULL;
149090075Sobrien      enum format_lengths length_chars_val = FMT_LEN_none;
149190075Sobrien      enum format_std_version length_chars_std = STD_C89;
149290075Sobrien      int format_char;
149390075Sobrien      tree cur_param;
149490075Sobrien      tree wanted_type;
149590075Sobrien      int main_arg_num = 0;
149690075Sobrien      tree main_arg_params = 0;
149790075Sobrien      enum format_std_version wanted_type_std;
149890075Sobrien      const char *wanted_type_name;
149990075Sobrien      format_wanted_type width_wanted_type;
150090075Sobrien      format_wanted_type precision_wanted_type;
150190075Sobrien      format_wanted_type main_wanted_type;
150290075Sobrien      format_wanted_type *first_wanted_type = NULL;
150390075Sobrien      format_wanted_type *last_wanted_type = NULL;
150490075Sobrien      const format_length_info *fli = NULL;
150590075Sobrien      const format_char_info *fci = NULL;
150690075Sobrien      char flag_chars[256];
150790075Sobrien      int aflag = 0;
1508169701Skan      const char *format_start = format_chars;
150990075Sobrien      if (*format_chars == 0)
151090075Sobrien	{
151190075Sobrien	  if (format_chars - orig_format_chars != format_length)
1512169701Skan	    warning (OPT_Wformat, "embedded %<\\0%> in format");
151390075Sobrien	  if (info->first_arg_num != 0 && params != 0
151490075Sobrien	      && has_operand_number <= 0)
151590075Sobrien	    {
151690075Sobrien	      res->number_other--;
151790075Sobrien	      res->number_extra_args++;
151890075Sobrien	    }
151990075Sobrien	  if (has_operand_number > 0)
1520169701Skan	    finish_dollar_format_checking (res, fki->flags & (int) FMT_FLAG_DOLLAR_GAP_POINTER_OK);
152190075Sobrien	  return;
152290075Sobrien	}
152390075Sobrien      if (*format_chars++ != '%')
152490075Sobrien	continue;
152590075Sobrien      if (*format_chars == 0)
152690075Sobrien	{
1527169701Skan	  warning (OPT_Wformat, "spurious trailing %<%%%> in format");
152890075Sobrien	  continue;
152990075Sobrien	}
153090075Sobrien      if (*format_chars == '%')
153190075Sobrien	{
153290075Sobrien	  ++format_chars;
153390075Sobrien	  continue;
153490075Sobrien	}
153590075Sobrien      flag_chars[0] = 0;
153690075Sobrien
153790075Sobrien      if ((fki->flags & (int) FMT_FLAG_USE_DOLLAR) && has_operand_number != 0)
153890075Sobrien	{
153990075Sobrien	  /* Possibly read a $ operand number at the start of the format.
154090075Sobrien	     If one was previously used, one is required here.  If one
154190075Sobrien	     is not used here, we can't immediately conclude this is a
154290075Sobrien	     format without them, since it could be printf %m or scanf %*.  */
154390075Sobrien	  int opnum;
1544169701Skan	  opnum = maybe_read_dollar_number (&format_chars, 0,
154590075Sobrien					    first_fillin_param,
154690075Sobrien					    &main_arg_params, fki);
154790075Sobrien	  if (opnum == -1)
154890075Sobrien	    return;
154990075Sobrien	  else if (opnum > 0)
155090075Sobrien	    {
155190075Sobrien	      has_operand_number = 1;
155290075Sobrien	      main_arg_num = opnum + info->first_arg_num - 1;
155390075Sobrien	    }
155490075Sobrien	}
1555169701Skan      else if (fki->flags & FMT_FLAG_USE_DOLLAR)
1556169701Skan	{
1557169701Skan	  if (avoid_dollar_number (format_chars))
1558169701Skan	    return;
1559169701Skan	}
156090075Sobrien
156190075Sobrien      /* Read any format flags, but do not yet validate them beyond removing
156290075Sobrien	 duplicates, since in general validation depends on the rest of
156390075Sobrien	 the format.  */
156490075Sobrien      while (*format_chars != 0
156590075Sobrien	     && strchr (fki->flag_chars, *format_chars) != 0)
156690075Sobrien	{
156790075Sobrien	  const format_flag_spec *s = get_flag_spec (flag_specs,
156890075Sobrien						     *format_chars, NULL);
156990075Sobrien	  if (strchr (flag_chars, *format_chars) != 0)
157090075Sobrien	    {
1571169701Skan	      warning (OPT_Wformat, "repeated %s in format", _(s->name));
157290075Sobrien	    }
157390075Sobrien	  else
157490075Sobrien	    {
157590075Sobrien	      i = strlen (flag_chars);
157690075Sobrien	      flag_chars[i++] = *format_chars;
157790075Sobrien	      flag_chars[i] = 0;
157890075Sobrien	    }
157990075Sobrien	  if (s->skip_next_char)
158090075Sobrien	    {
158190075Sobrien	      ++format_chars;
158290075Sobrien	      if (*format_chars == 0)
158390075Sobrien		{
1584169701Skan		  warning (OPT_Wformat, "missing fill character at end of strfmon format");
158590075Sobrien		  return;
158690075Sobrien		}
158790075Sobrien	    }
158890075Sobrien	  ++format_chars;
158990075Sobrien	}
159090075Sobrien
159190075Sobrien      /* Read any format width, possibly * or *m$.  */
159290075Sobrien      if (fki->width_char != 0)
159390075Sobrien	{
159490075Sobrien	  if (fki->width_type != NULL && *format_chars == '*')
159590075Sobrien	    {
159690075Sobrien	      i = strlen (flag_chars);
159790075Sobrien	      flag_chars[i++] = fki->width_char;
159890075Sobrien	      flag_chars[i] = 0;
159990075Sobrien	      /* "...a field width...may be indicated by an asterisk.
160090075Sobrien		 In this case, an int argument supplies the field width..."  */
160190075Sobrien	      ++format_chars;
160290075Sobrien	      if (has_operand_number != 0)
160390075Sobrien		{
160490075Sobrien		  int opnum;
1605169701Skan		  opnum = maybe_read_dollar_number (&format_chars,
160690075Sobrien						    has_operand_number == 1,
160790075Sobrien						    first_fillin_param,
160890075Sobrien						    &params, fki);
160990075Sobrien		  if (opnum == -1)
161090075Sobrien		    return;
161190075Sobrien		  else if (opnum > 0)
161290075Sobrien		    {
161390075Sobrien		      has_operand_number = 1;
161490075Sobrien		      arg_num = opnum + info->first_arg_num - 1;
161590075Sobrien		    }
161690075Sobrien		  else
161790075Sobrien		    has_operand_number = 0;
161890075Sobrien		}
1619169701Skan	      else
1620169701Skan		{
1621169701Skan		  if (avoid_dollar_number (format_chars))
1622169701Skan		    return;
1623169701Skan		}
162490075Sobrien	      if (info->first_arg_num != 0)
162590075Sobrien		{
1626117422Skan		  if (params == 0)
1627117422Skan		    {
1628169701Skan		      warning (OPT_Wformat, "too few arguments for format");
1629117422Skan		      return;
1630117422Skan		    }
163190075Sobrien		  cur_param = TREE_VALUE (params);
163290075Sobrien		  if (has_operand_number <= 0)
163390075Sobrien		    {
163490075Sobrien		      params = TREE_CHAIN (params);
163590075Sobrien		      ++arg_num;
163690075Sobrien		    }
163790075Sobrien		  width_wanted_type.wanted_type = *fki->width_type;
163890075Sobrien		  width_wanted_type.wanted_type_name = NULL;
163990075Sobrien		  width_wanted_type.pointer_count = 0;
164090075Sobrien		  width_wanted_type.char_lenient_flag = 0;
164190075Sobrien		  width_wanted_type.writing_in_flag = 0;
164290075Sobrien		  width_wanted_type.reading_from_flag = 0;
164390075Sobrien		  width_wanted_type.name = _("field width");
164490075Sobrien		  width_wanted_type.param = cur_param;
164590075Sobrien		  width_wanted_type.arg_num = arg_num;
164690075Sobrien		  width_wanted_type.next = NULL;
164790075Sobrien		  if (last_wanted_type != 0)
164890075Sobrien		    last_wanted_type->next = &width_wanted_type;
164990075Sobrien		  if (first_wanted_type == 0)
165090075Sobrien		    first_wanted_type = &width_wanted_type;
165190075Sobrien		  last_wanted_type = &width_wanted_type;
165290075Sobrien		}
165390075Sobrien	    }
165490075Sobrien	  else
165590075Sobrien	    {
165690075Sobrien	      /* Possibly read a numeric width.  If the width is zero,
165790075Sobrien		 we complain if appropriate.  */
165890075Sobrien	      int non_zero_width_char = FALSE;
165990075Sobrien	      int found_width = FALSE;
166090075Sobrien	      while (ISDIGIT (*format_chars))
166190075Sobrien		{
166290075Sobrien		  found_width = TRUE;
166390075Sobrien		  if (*format_chars != '0')
166490075Sobrien		    non_zero_width_char = TRUE;
166590075Sobrien		  ++format_chars;
166690075Sobrien		}
166790075Sobrien	      if (found_width && !non_zero_width_char &&
166890075Sobrien		  (fki->flags & (int) FMT_FLAG_ZERO_WIDTH_BAD))
1669169701Skan		warning (OPT_Wformat, "zero width in %s format", fki->name);
167090075Sobrien	      if (found_width)
167190075Sobrien		{
167290075Sobrien		  i = strlen (flag_chars);
167390075Sobrien		  flag_chars[i++] = fki->width_char;
167490075Sobrien		  flag_chars[i] = 0;
167590075Sobrien		}
167690075Sobrien	    }
167790075Sobrien	}
167890075Sobrien
167990075Sobrien      /* Read any format left precision (must be a number, not *).  */
168090075Sobrien      if (fki->left_precision_char != 0 && *format_chars == '#')
168190075Sobrien	{
168290075Sobrien	  ++format_chars;
168390075Sobrien	  i = strlen (flag_chars);
168490075Sobrien	  flag_chars[i++] = fki->left_precision_char;
168590075Sobrien	  flag_chars[i] = 0;
168690075Sobrien	  if (!ISDIGIT (*format_chars))
1687169701Skan	    warning (OPT_Wformat, "empty left precision in %s format", fki->name);
168890075Sobrien	  while (ISDIGIT (*format_chars))
168990075Sobrien	    ++format_chars;
169090075Sobrien	}
169190075Sobrien
169290075Sobrien      /* Read any format precision, possibly * or *m$.  */
169390075Sobrien      if (fki->precision_char != 0 && *format_chars == '.')
169490075Sobrien	{
169590075Sobrien	  ++format_chars;
169690075Sobrien	  i = strlen (flag_chars);
169790075Sobrien	  flag_chars[i++] = fki->precision_char;
169890075Sobrien	  flag_chars[i] = 0;
169990075Sobrien	  if (fki->precision_type != NULL && *format_chars == '*')
170090075Sobrien	    {
170190075Sobrien	      /* "...a...precision...may be indicated by an asterisk.
170290075Sobrien		 In this case, an int argument supplies the...precision."  */
170390075Sobrien	      ++format_chars;
170490075Sobrien	      if (has_operand_number != 0)
170590075Sobrien		{
170690075Sobrien		  int opnum;
1707169701Skan		  opnum = maybe_read_dollar_number (&format_chars,
170890075Sobrien						    has_operand_number == 1,
170990075Sobrien						    first_fillin_param,
171090075Sobrien						    &params, fki);
171190075Sobrien		  if (opnum == -1)
171290075Sobrien		    return;
171390075Sobrien		  else if (opnum > 0)
171490075Sobrien		    {
171590075Sobrien		      has_operand_number = 1;
171690075Sobrien		      arg_num = opnum + info->first_arg_num - 1;
171790075Sobrien		    }
171890075Sobrien		  else
171990075Sobrien		    has_operand_number = 0;
172090075Sobrien		}
1721169701Skan	      else
1722169701Skan		{
1723169701Skan		  if (avoid_dollar_number (format_chars))
1724169701Skan		    return;
1725169701Skan		}
172690075Sobrien	      if (info->first_arg_num != 0)
172790075Sobrien		{
172890075Sobrien		  if (params == 0)
172990075Sobrien		    {
1730169701Skan		      warning (OPT_Wformat, "too few arguments for format");
173190075Sobrien		      return;
173290075Sobrien		    }
173390075Sobrien		  cur_param = TREE_VALUE (params);
173490075Sobrien		  if (has_operand_number <= 0)
173590075Sobrien		    {
173690075Sobrien		      params = TREE_CHAIN (params);
173790075Sobrien		      ++arg_num;
173890075Sobrien		    }
173990075Sobrien		  precision_wanted_type.wanted_type = *fki->precision_type;
174090075Sobrien		  precision_wanted_type.wanted_type_name = NULL;
174190075Sobrien		  precision_wanted_type.pointer_count = 0;
174290075Sobrien		  precision_wanted_type.char_lenient_flag = 0;
174390075Sobrien		  precision_wanted_type.writing_in_flag = 0;
174490075Sobrien		  precision_wanted_type.reading_from_flag = 0;
174590075Sobrien		  precision_wanted_type.name = _("field precision");
174690075Sobrien		  precision_wanted_type.param = cur_param;
174790075Sobrien		  precision_wanted_type.arg_num = arg_num;
174890075Sobrien		  precision_wanted_type.next = NULL;
174990075Sobrien		  if (last_wanted_type != 0)
175090075Sobrien		    last_wanted_type->next = &precision_wanted_type;
175190075Sobrien		  if (first_wanted_type == 0)
175290075Sobrien		    first_wanted_type = &precision_wanted_type;
175390075Sobrien		  last_wanted_type = &precision_wanted_type;
175490075Sobrien		}
175590075Sobrien	    }
175690075Sobrien	  else
175790075Sobrien	    {
175890075Sobrien	      if (!(fki->flags & (int) FMT_FLAG_EMPTY_PREC_OK)
175990075Sobrien		  && !ISDIGIT (*format_chars))
1760169701Skan		warning (OPT_Wformat, "empty precision in %s format", fki->name);
176190075Sobrien	      while (ISDIGIT (*format_chars))
176290075Sobrien		++format_chars;
176390075Sobrien	    }
176490075Sobrien	}
176590075Sobrien
176690075Sobrien      /* Read any length modifier, if this kind of format has them.  */
176790075Sobrien      fli = fki->length_char_specs;
176890075Sobrien      length_chars = NULL;
176990075Sobrien      length_chars_val = FMT_LEN_none;
177090075Sobrien      length_chars_std = STD_C89;
177190075Sobrien      if (fli)
177290075Sobrien	{
177390075Sobrien	  while (fli->name != 0 && fli->name[0] != *format_chars)
177490075Sobrien	    fli++;
1775169701Skan	  /*
1776169701Skan	   * Make sure FreeBSD's D format char takes preference
1777169701Skan	   * over new DD length specifier if FreeBSD format
1778169701Skan	   * extensions are requested.
1779169701Skan	   */
1780169701Skan	  if (fli->index == FMT_LEN_D && flag_format_extensions
1781169701Skan	    && fki->conversion_specs == print_char_table)
1782169701Skan	  	while (fli->name != 0) fli++;
178390075Sobrien	  if (fli->name != 0)
178490075Sobrien	    {
178590075Sobrien	      format_chars++;
178690075Sobrien	      if (fli->double_name != 0 && fli->name[0] == *format_chars)
178790075Sobrien		{
178890075Sobrien		  format_chars++;
178990075Sobrien		  length_chars = fli->double_name;
179090075Sobrien		  length_chars_val = fli->double_index;
179190075Sobrien		  length_chars_std = fli->double_std;
179290075Sobrien		}
179390075Sobrien	      else
179490075Sobrien		{
179590075Sobrien		  length_chars = fli->name;
179690075Sobrien		  length_chars_val = fli->index;
179790075Sobrien		  length_chars_std = fli->std;
179890075Sobrien		}
179990075Sobrien	      i = strlen (flag_chars);
180090075Sobrien	      flag_chars[i++] = fki->length_code_char;
180190075Sobrien	      flag_chars[i] = 0;
180290075Sobrien	    }
180390075Sobrien	  if (pedantic)
180490075Sobrien	    {
180590075Sobrien	      /* Warn if the length modifier is non-standard.  */
180690075Sobrien	      if (ADJ_STD (length_chars_std) > C_STD_VER)
1807169701Skan		warning (OPT_Wformat,
1808169701Skan			 "%s does not support the %qs %s length modifier",
1809169701Skan			 C_STD_NAME (length_chars_std), length_chars,
1810169701Skan			 fki->name);
181190075Sobrien	    }
181290075Sobrien	}
181390075Sobrien
181490075Sobrien      /* Read any modifier (strftime E/O).  */
181590075Sobrien      if (fki->modifier_chars != NULL)
181690075Sobrien	{
181790075Sobrien	  while (*format_chars != 0
181890075Sobrien		 && strchr (fki->modifier_chars, *format_chars) != 0)
181990075Sobrien	    {
182090075Sobrien	      if (strchr (flag_chars, *format_chars) != 0)
182190075Sobrien		{
182290075Sobrien		  const format_flag_spec *s = get_flag_spec (flag_specs,
182390075Sobrien							     *format_chars, NULL);
1824169701Skan		  warning (OPT_Wformat, "repeated %s in format", _(s->name));
182590075Sobrien		}
182690075Sobrien	      else
182790075Sobrien		{
182890075Sobrien		  i = strlen (flag_chars);
182990075Sobrien		  flag_chars[i++] = *format_chars;
183090075Sobrien		  flag_chars[i] = 0;
183190075Sobrien		}
183290075Sobrien	      ++format_chars;
183390075Sobrien	    }
183490075Sobrien	}
183590075Sobrien
183690075Sobrien      /* Handle the scanf allocation kludge.  */
183790075Sobrien      if (fki->flags & (int) FMT_FLAG_SCANF_A_KLUDGE)
183890075Sobrien	{
183990075Sobrien	  if (*format_chars == 'a' && !flag_isoc99)
184090075Sobrien	    {
184190075Sobrien	      if (format_chars[1] == 's' || format_chars[1] == 'S'
184290075Sobrien		  || format_chars[1] == '[')
184390075Sobrien		{
1844169701Skan		  /* 'a' is used as a flag.  */
184590075Sobrien		  i = strlen (flag_chars);
184690075Sobrien		  flag_chars[i++] = 'a';
184790075Sobrien		  flag_chars[i] = 0;
184890075Sobrien		  format_chars++;
184990075Sobrien		}
185090075Sobrien	    }
185190075Sobrien	}
185290075Sobrien
185390075Sobrien      format_char = *format_chars;
185490075Sobrien      if (format_char == 0
185590075Sobrien	  || (!(fki->flags & (int) FMT_FLAG_FANCY_PERCENT_OK)
185690075Sobrien	      && format_char == '%'))
185790075Sobrien	{
1858169701Skan	  warning (OPT_Wformat, "conversion lacks type at end of format");
185990075Sobrien	  continue;
186090075Sobrien	}
186190075Sobrien      format_chars++;
186290075Sobrien      fci = fki->conversion_specs;
186390075Sobrien      while (fci->format_chars != 0
186490075Sobrien	     && strchr (fci->format_chars, format_char) == 0)
186590075Sobrien	  ++fci;
1866169701Skan      if (fci->format_chars == 0 && flag_format_extensions
1867169701Skan	  && fki->conversion_specs == print_char_table)
1868169701Skan	{
1869169701Skan	  fci = fbsd_print_char_table;
1870169701Skan	  while (fci->format_chars != 0
1871169701Skan	         && strchr (fci->format_chars, format_char) == 0)
1872169701Skan	     ++fci;
1873169701Skan	}
187490075Sobrien      if (fci->format_chars == 0)
187590075Sobrien	{
1876169701Skan	  if (ISGRAPH (format_char))
1877169701Skan	    warning (OPT_Wformat, "unknown conversion type character %qc in format",
187890075Sobrien		     format_char);
187990075Sobrien	  else
1880169701Skan	    warning (OPT_Wformat, "unknown conversion type character 0x%x in format",
188190075Sobrien		     format_char);
188290075Sobrien	  continue;
188390075Sobrien	}
188490075Sobrien      if (pedantic)
188590075Sobrien	{
188690075Sobrien	  if (ADJ_STD (fci->std) > C_STD_VER)
1887169701Skan	    warning (OPT_Wformat, "%s does not support the %<%%%c%> %s format",
1888169701Skan		     C_STD_NAME (fci->std), format_char, fki->name);
188990075Sobrien	}
189090075Sobrien
189190075Sobrien      /* Validate the individual flags used, removing any that are invalid.  */
189290075Sobrien      {
189390075Sobrien	int d = 0;
189490075Sobrien	for (i = 0; flag_chars[i] != 0; i++)
189590075Sobrien	  {
189690075Sobrien	    const format_flag_spec *s = get_flag_spec (flag_specs,
189790075Sobrien						       flag_chars[i], NULL);
189890075Sobrien	    flag_chars[i - d] = flag_chars[i];
189990075Sobrien	    if (flag_chars[i] == fki->length_code_char)
190090075Sobrien	      continue;
190190075Sobrien	    if (strchr (fci->flag_chars, flag_chars[i]) == 0)
190290075Sobrien	      {
1903169701Skan		warning (OPT_Wformat, "%s used with %<%%%c%> %s format",
1904169701Skan			 _(s->name), format_char, fki->name);
190590075Sobrien		d++;
190690075Sobrien		continue;
190790075Sobrien	      }
190890075Sobrien	    if (pedantic)
190990075Sobrien	      {
191090075Sobrien		const format_flag_spec *t;
191190075Sobrien		if (ADJ_STD (s->std) > C_STD_VER)
1912169701Skan		  warning (OPT_Wformat, "%s does not support %s",
1913169701Skan			   C_STD_NAME (s->std), _(s->long_name));
191490075Sobrien		t = get_flag_spec (flag_specs, flag_chars[i], fci->flags2);
191590075Sobrien		if (t != NULL && ADJ_STD (t->std) > ADJ_STD (s->std))
191690075Sobrien		  {
191790075Sobrien		    const char *long_name = (t->long_name != NULL
191890075Sobrien					     ? t->long_name
191990075Sobrien					     : s->long_name);
192090075Sobrien		    if (ADJ_STD (t->std) > C_STD_VER)
1921169701Skan		      warning (OPT_Wformat,
1922169701Skan			       "%s does not support %s with the %<%%%c%> %s format",
1923169701Skan			       C_STD_NAME (t->std), _(long_name),
1924169701Skan			       format_char, fki->name);
192590075Sobrien		  }
192690075Sobrien	      }
192790075Sobrien	  }
192890075Sobrien	flag_chars[i - d] = 0;
192990075Sobrien      }
193090075Sobrien
193190075Sobrien      if ((fki->flags & (int) FMT_FLAG_SCANF_A_KLUDGE)
193290075Sobrien	  && strchr (flag_chars, 'a') != 0)
193390075Sobrien	aflag = 1;
193490075Sobrien
193590075Sobrien      if (fki->suppression_char
193690075Sobrien	  && strchr (flag_chars, fki->suppression_char) != 0)
193790075Sobrien	suppressed = 1;
193890075Sobrien
193990075Sobrien      /* Validate the pairs of flags used.  */
194090075Sobrien      for (i = 0; bad_flag_pairs[i].flag_char1 != 0; i++)
194190075Sobrien	{
194290075Sobrien	  const format_flag_spec *s, *t;
194390075Sobrien	  if (strchr (flag_chars, bad_flag_pairs[i].flag_char1) == 0)
194490075Sobrien	    continue;
194590075Sobrien	  if (strchr (flag_chars, bad_flag_pairs[i].flag_char2) == 0)
194690075Sobrien	    continue;
194790075Sobrien	  if (bad_flag_pairs[i].predicate != 0
194890075Sobrien	      && strchr (fci->flags2, bad_flag_pairs[i].predicate) == 0)
194990075Sobrien	    continue;
195090075Sobrien	  s = get_flag_spec (flag_specs, bad_flag_pairs[i].flag_char1, NULL);
195190075Sobrien	  t = get_flag_spec (flag_specs, bad_flag_pairs[i].flag_char2, NULL);
195290075Sobrien	  if (bad_flag_pairs[i].ignored)
195390075Sobrien	    {
195490075Sobrien	      if (bad_flag_pairs[i].predicate != 0)
1955169701Skan		warning (OPT_Wformat,
1956169701Skan			 "%s ignored with %s and %<%%%c%> %s format",
1957169701Skan			 _(s->name), _(t->name), format_char,
1958169701Skan			 fki->name);
195990075Sobrien	      else
1960169701Skan		warning (OPT_Wformat, "%s ignored with %s in %s format",
1961169701Skan			 _(s->name), _(t->name), fki->name);
196290075Sobrien	    }
196390075Sobrien	  else
196490075Sobrien	    {
196590075Sobrien	      if (bad_flag_pairs[i].predicate != 0)
1966169701Skan		warning (OPT_Wformat,
1967169701Skan			 "use of %s and %s together with %<%%%c%> %s format",
1968169701Skan			 _(s->name), _(t->name), format_char,
1969169701Skan			 fki->name);
197090075Sobrien	      else
1971169701Skan		warning (OPT_Wformat, "use of %s and %s together in %s format",
1972169701Skan			 _(s->name), _(t->name), fki->name);
197390075Sobrien	    }
197490075Sobrien	}
197590075Sobrien
197690075Sobrien      /* Give Y2K warnings.  */
197790075Sobrien      if (warn_format_y2k)
197890075Sobrien	{
197990075Sobrien	  int y2k_level = 0;
198090075Sobrien	  if (strchr (fci->flags2, '4') != 0)
198190075Sobrien	    if (strchr (flag_chars, 'E') != 0)
198290075Sobrien	      y2k_level = 3;
198390075Sobrien	    else
198490075Sobrien	      y2k_level = 2;
198590075Sobrien	  else if (strchr (fci->flags2, '3') != 0)
198690075Sobrien	    y2k_level = 3;
198790075Sobrien	  else if (strchr (fci->flags2, '2') != 0)
198890075Sobrien	    y2k_level = 2;
198990075Sobrien	  if (y2k_level == 3)
1990169701Skan	    warning (OPT_Wformat_y2k, "%<%%%c%> yields only last 2 digits of "
1991169701Skan		     "year in some locales on non-BSD systems", format_char);
199290075Sobrien	  else if (y2k_level == 2)
1993169701Skan	    warning (OPT_Wformat_y2k, "%<%%%c%> yields only last 2 digits of "
1994169701Skan		     "year", format_char);
199590075Sobrien	}
199690075Sobrien
199790075Sobrien      if (strchr (fci->flags2, '[') != 0)
199890075Sobrien	{
199990075Sobrien	  /* Skip over scan set, in case it happens to have '%' in it.  */
200090075Sobrien	  if (*format_chars == '^')
200190075Sobrien	    ++format_chars;
200290075Sobrien	  /* Find closing bracket; if one is hit immediately, then
200390075Sobrien	     it's part of the scan set rather than a terminator.  */
200490075Sobrien	  if (*format_chars == ']')
200590075Sobrien	    ++format_chars;
200690075Sobrien	  while (*format_chars && *format_chars != ']')
200790075Sobrien	    ++format_chars;
200890075Sobrien	  if (*format_chars != ']')
200990075Sobrien	    /* The end of the format string was reached.  */
2010169701Skan	    warning (OPT_Wformat, "no closing %<]%> for %<%%[%> format");
201190075Sobrien	}
201290075Sobrien
201390075Sobrien      wanted_type = 0;
201490075Sobrien      wanted_type_name = 0;
201590075Sobrien      if (fki->flags & (int) FMT_FLAG_ARG_CONVERT)
201690075Sobrien	{
201790075Sobrien	  wanted_type = (fci->types[length_chars_val].type
201890075Sobrien			 ? *fci->types[length_chars_val].type : 0);
201990075Sobrien	  wanted_type_name = fci->types[length_chars_val].name;
202090075Sobrien	  wanted_type_std = fci->types[length_chars_val].std;
202190075Sobrien	  if (wanted_type == 0)
202290075Sobrien	    {
2023169701Skan	      warning (OPT_Wformat,
2024169701Skan		       "use of %qs length modifier with %qc type character",
2025169701Skan		       length_chars, format_char);
202690075Sobrien	      /* Heuristic: skip one argument when an invalid length/type
202790075Sobrien		 combination is encountered.  */
202890075Sobrien	      arg_num++;
202990075Sobrien	      if (params == 0)
203090075Sobrien		{
2031169701Skan		  warning (OPT_Wformat, "too few arguments for format");
203290075Sobrien		  return;
203390075Sobrien		}
203490075Sobrien	      params = TREE_CHAIN (params);
203590075Sobrien	      continue;
203690075Sobrien	    }
203790075Sobrien	  else if (pedantic
203890075Sobrien		   /* Warn if non-standard, provided it is more non-standard
203990075Sobrien		      than the length and type characters that may already
204090075Sobrien		      have been warned for.  */
204190075Sobrien		   && ADJ_STD (wanted_type_std) > ADJ_STD (length_chars_std)
204290075Sobrien		   && ADJ_STD (wanted_type_std) > ADJ_STD (fci->std))
204390075Sobrien	    {
204490075Sobrien	      if (ADJ_STD (wanted_type_std) > C_STD_VER)
2045169701Skan		warning (OPT_Wformat,
2046169701Skan			 "%s does not support the %<%%%s%c%> %s format",
2047169701Skan			 C_STD_NAME (wanted_type_std), length_chars,
2048169701Skan			 format_char, fki->name);
204990075Sobrien	    }
205090075Sobrien	}
205190075Sobrien
2052169701Skan      main_wanted_type.next = NULL;
2053169701Skan
205490075Sobrien      /* Finally. . .check type of argument against desired type!  */
205590075Sobrien      if (info->first_arg_num == 0)
205690075Sobrien	continue;
205790075Sobrien      if ((fci->pointer_count == 0 && wanted_type == void_type_node)
205890075Sobrien	  || suppressed)
205990075Sobrien	{
206090075Sobrien	  if (main_arg_num != 0)
206190075Sobrien	    {
206290075Sobrien	      if (suppressed)
2063169701Skan		warning (OPT_Wformat, "operand number specified with "
2064169701Skan			 "suppressed assignment");
206590075Sobrien	      else
2066169701Skan		warning (OPT_Wformat, "operand number specified for format "
2067169701Skan			 "taking no argument");
206890075Sobrien	    }
206990075Sobrien	}
207090075Sobrien      else
207190075Sobrien	{
2072169701Skan	  format_wanted_type *wanted_type_ptr;
2073169701Skan
207490075Sobrien	  if (main_arg_num != 0)
207590075Sobrien	    {
207690075Sobrien	      arg_num = main_arg_num;
207790075Sobrien	      params = main_arg_params;
207890075Sobrien	    }
207990075Sobrien	  else
208090075Sobrien	    {
208190075Sobrien	      ++arg_num;
208290075Sobrien	      if (has_operand_number > 0)
208390075Sobrien		{
2084169701Skan		  warning (OPT_Wformat, "missing $ operand number in format");
208590075Sobrien		  return;
208690075Sobrien		}
208790075Sobrien	      else
208890075Sobrien		has_operand_number = 0;
2089169701Skan	    }
2090169701Skan
2091169701Skan	  wanted_type_ptr = &main_wanted_type;
2092169701Skan	  while (fci)
2093169701Skan	    {
209490075Sobrien	      if (params == 0)
209590075Sobrien		{
2096169701Skan		  warning (OPT_Wformat, "too few arguments for format");
209790075Sobrien		  return;
209890075Sobrien		}
2099169701Skan
2100169701Skan	      cur_param = TREE_VALUE (params);
2101169701Skan	      params = TREE_CHAIN (params);
2102169701Skan
2103169701Skan	      wanted_type_ptr->wanted_type = wanted_type;
2104169701Skan	      wanted_type_ptr->wanted_type_name = wanted_type_name;
2105169701Skan	      wanted_type_ptr->pointer_count = fci->pointer_count + aflag;
2106169701Skan	      wanted_type_ptr->char_lenient_flag = 0;
2107169701Skan	      if (strchr (fci->flags2, 'c') != 0)
2108169701Skan		wanted_type_ptr->char_lenient_flag = 1;
2109169701Skan	      wanted_type_ptr->writing_in_flag = 0;
2110169701Skan	      wanted_type_ptr->reading_from_flag = 0;
2111169701Skan	      if (aflag)
2112169701Skan		wanted_type_ptr->writing_in_flag = 1;
2113169701Skan	      else
2114169701Skan		{
2115169701Skan		  if (strchr (fci->flags2, 'W') != 0)
2116169701Skan		    wanted_type_ptr->writing_in_flag = 1;
2117169701Skan		  if (strchr (fci->flags2, 'R') != 0)
2118169701Skan		    wanted_type_ptr->reading_from_flag = 1;
2119169701Skan		}
2120169701Skan	      wanted_type_ptr->name = NULL;
2121169701Skan	      wanted_type_ptr->param = cur_param;
2122169701Skan	      wanted_type_ptr->arg_num = arg_num;
2123169701Skan	      wanted_type_ptr->next = NULL;
2124169701Skan	      if (last_wanted_type != 0)
2125169701Skan		last_wanted_type->next = wanted_type_ptr;
2126169701Skan	      if (first_wanted_type == 0)
2127169701Skan		first_wanted_type = wanted_type_ptr;
2128169701Skan	      last_wanted_type = wanted_type_ptr;
2129169701Skan
2130169701Skan	      fci = fci->chain;
2131169701Skan	      if (fci)
2132169701Skan		{
2133169701Skan		  wanted_type_ptr = GGC_NEW (format_wanted_type);
2134169701Skan		  arg_num++;
2135169701Skan		  wanted_type = *fci->types[length_chars_val].type;
2136169701Skan		  wanted_type_name = fci->types[length_chars_val].name;
2137169701Skan		}
213890075Sobrien	    }
213990075Sobrien	}
214090075Sobrien
214190075Sobrien      if (first_wanted_type != 0)
2142169701Skan	check_format_types (first_wanted_type, format_start,
2143169701Skan			    format_chars - format_start);
214490075Sobrien
2145169701Skan      if (main_wanted_type.next != NULL)
2146169701Skan	{
2147169701Skan	  format_wanted_type *wanted_type_ptr = main_wanted_type.next;
2148169701Skan	  while (wanted_type_ptr)
2149169701Skan	    {
2150169701Skan	      format_wanted_type *next = wanted_type_ptr->next;
2151169701Skan	      ggc_free (wanted_type_ptr);
2152169701Skan	      wanted_type_ptr = next;
2153169701Skan	    }
2154169701Skan	}
215590075Sobrien    }
215690075Sobrien}
215790075Sobrien
215890075Sobrien
215990075Sobrien/* Check the argument types from a single format conversion (possibly
216090075Sobrien   including width and precision arguments).  */
216190075Sobrienstatic void
2162169701Skancheck_format_types (format_wanted_type *types, const char *format_start,
2163169701Skan		    int format_length)
216490075Sobrien{
216590075Sobrien  for (; types != 0; types = types->next)
216690075Sobrien    {
216790075Sobrien      tree cur_param;
216890075Sobrien      tree cur_type;
216990075Sobrien      tree orig_cur_type;
217090075Sobrien      tree wanted_type;
217190075Sobrien      int arg_num;
217290075Sobrien      int i;
217390075Sobrien      int char_type_flag;
217490075Sobrien      cur_param = types->param;
217590075Sobrien      cur_type = TREE_TYPE (cur_param);
217690075Sobrien      if (cur_type == error_mark_node)
217790075Sobrien	continue;
2178169701Skan      orig_cur_type = cur_type;
217990075Sobrien      char_type_flag = 0;
218090075Sobrien      wanted_type = types->wanted_type;
218190075Sobrien      arg_num = types->arg_num;
218290075Sobrien
218390075Sobrien      /* The following should not occur here.  */
2184169701Skan      gcc_assert (wanted_type);
2185169701Skan      gcc_assert (wanted_type != void_type_node || types->pointer_count);
218690075Sobrien
218790075Sobrien      if (types->pointer_count == 0)
2188169701Skan	wanted_type = lang_hooks.types.type_promotes_to (wanted_type);
218990075Sobrien
2190169701Skan      wanted_type = TYPE_MAIN_VARIANT (wanted_type);
2191169701Skan
219290075Sobrien      STRIP_NOPS (cur_param);
219390075Sobrien
219490075Sobrien      /* Check the types of any additional pointer arguments
219590075Sobrien	 that precede the "real" argument.  */
219690075Sobrien      for (i = 0; i < types->pointer_count; ++i)
219790075Sobrien	{
219890075Sobrien	  if (TREE_CODE (cur_type) == POINTER_TYPE)
219990075Sobrien	    {
220090075Sobrien	      cur_type = TREE_TYPE (cur_type);
220190075Sobrien	      if (cur_type == error_mark_node)
220290075Sobrien		break;
220390075Sobrien
220490075Sobrien	      /* Check for writing through a NULL pointer.  */
220590075Sobrien	      if (types->writing_in_flag
220690075Sobrien		  && i == 0
220790075Sobrien		  && cur_param != 0
220890075Sobrien		  && integer_zerop (cur_param))
2209169701Skan		warning (OPT_Wformat, "writing through null pointer "
2210169701Skan			 "(argument %d)", arg_num);
221190075Sobrien
221290075Sobrien	      /* Check for reading through a NULL pointer.  */
221390075Sobrien	      if (types->reading_from_flag
221490075Sobrien		  && i == 0
221590075Sobrien		  && cur_param != 0
221690075Sobrien		  && integer_zerop (cur_param))
2217169701Skan		warning (OPT_Wformat, "reading through null pointer "
2218169701Skan			 "(argument %d)", arg_num);
221990075Sobrien
222090075Sobrien	      if (cur_param != 0 && TREE_CODE (cur_param) == ADDR_EXPR)
222190075Sobrien		cur_param = TREE_OPERAND (cur_param, 0);
222290075Sobrien	      else
222390075Sobrien		cur_param = 0;
222490075Sobrien
222590075Sobrien	      /* See if this is an attempt to write into a const type with
222690075Sobrien		 scanf or with printf "%n".  Note: the writing in happens
222790075Sobrien		 at the first indirection only, if for example
222890075Sobrien		 void * const * is passed to scanf %p; passing
222990075Sobrien		 const void ** is simply passing an incompatible type.  */
223090075Sobrien	      if (types->writing_in_flag
223190075Sobrien		  && i == 0
223290075Sobrien		  && (TYPE_READONLY (cur_type)
223390075Sobrien		      || (cur_param != 0
2234169701Skan			  && (CONSTANT_CLASS_P (cur_param)
223590075Sobrien			      || (DECL_P (cur_param)
223690075Sobrien				  && TREE_READONLY (cur_param))))))
2237169701Skan		warning (OPT_Wformat, "writing into constant object "
2238169701Skan			 "(argument %d)", arg_num);
223990075Sobrien
224090075Sobrien	      /* If there are extra type qualifiers beyond the first
224190075Sobrien		 indirection, then this makes the types technically
224290075Sobrien		 incompatible.  */
224390075Sobrien	      if (i > 0
224490075Sobrien		  && pedantic
224590075Sobrien		  && (TYPE_READONLY (cur_type)
224690075Sobrien		      || TYPE_VOLATILE (cur_type)
224790075Sobrien		      || TYPE_RESTRICT (cur_type)))
2248169701Skan		warning (OPT_Wformat, "extra type qualifiers in format "
2249169701Skan			 "argument (argument %d)",
225090075Sobrien			 arg_num);
225190075Sobrien
225290075Sobrien	    }
225390075Sobrien	  else
225490075Sobrien	    {
2255169701Skan	      format_type_warning (types->name, format_start, format_length,
2256169701Skan				   wanted_type, types->pointer_count,
2257169701Skan				   types->wanted_type_name, orig_cur_type,
2258169701Skan				   arg_num);
225990075Sobrien	      break;
226090075Sobrien	    }
226190075Sobrien	}
226290075Sobrien
226390075Sobrien      if (i < types->pointer_count)
226490075Sobrien	continue;
226590075Sobrien
226690075Sobrien      cur_type = TYPE_MAIN_VARIANT (cur_type);
226790075Sobrien
226890075Sobrien      /* Check whether the argument type is a character type.  This leniency
226990075Sobrien	 only applies to certain formats, flagged with 'c'.
227090075Sobrien      */
227190075Sobrien      if (types->char_lenient_flag)
227290075Sobrien	char_type_flag = (cur_type == char_type_node
227390075Sobrien			  || cur_type == signed_char_type_node
227490075Sobrien			  || cur_type == unsigned_char_type_node);
227590075Sobrien
227690075Sobrien      /* Check the type of the "real" argument, if there's a type we want.  */
2277169701Skan      if (lang_hooks.types_compatible_p (wanted_type, cur_type))
227890075Sobrien	continue;
2279169701Skan      /* If we want 'void *', allow any pointer type.
228090075Sobrien	 (Anything else would already have got a warning.)
228190075Sobrien	 With -pedantic, only allow pointers to void and to character
228290075Sobrien	 types.  */
228390075Sobrien      if (wanted_type == void_type_node
228490075Sobrien	  && (!pedantic || (i == 1 && char_type_flag)))
228590075Sobrien	continue;
228690075Sobrien      /* Don't warn about differences merely in signedness, unless
228790075Sobrien	 -pedantic.  With -pedantic, warn if the type is a pointer
228890075Sobrien	 target and not a character type, and for character types at
228990075Sobrien	 a second level of indirection.  */
229090075Sobrien      if (TREE_CODE (wanted_type) == INTEGER_TYPE
229190075Sobrien	  && TREE_CODE (cur_type) == INTEGER_TYPE
2292169701Skan	  && (!pedantic || i == 0 || (i == 1 && char_type_flag))
2293169701Skan	  && (TYPE_UNSIGNED (wanted_type)
2294117422Skan	      ? wanted_type == c_common_unsigned_type (cur_type)
2295117422Skan	      : wanted_type == c_common_signed_type (cur_type)))
229690075Sobrien	continue;
229790075Sobrien      /* Likewise, "signed char", "unsigned char" and "char" are
229890075Sobrien	 equivalent but the above test won't consider them equivalent.  */
229990075Sobrien      if (wanted_type == char_type_node
2300169701Skan	  && (!pedantic || i < 2)
230190075Sobrien	  && char_type_flag)
230290075Sobrien	continue;
230390075Sobrien      /* Now we have a type mismatch.  */
2304169701Skan      format_type_warning (types->name, format_start, format_length,
2305169701Skan			   wanted_type, types->pointer_count,
2306169701Skan			   types->wanted_type_name, orig_cur_type, arg_num);
2307169701Skan    }
2308169701Skan}
230990075Sobrien
2310132731Skan
2311169701Skan/* Give a warning about a format argument of different type from that
2312169701Skan   expected.  DESCR is a description such as "field precision", or
2313169701Skan   NULL for an ordinary format.  For an ordinary format, FORMAT_START
2314169701Skan   points to where the format starts in the format string and
2315169701Skan   FORMAT_LENGTH is its length.  WANTED_TYPE is the type the argument
2316169701Skan   should have after POINTER_COUNT pointer dereferences.
2317169701Skan   WANTED_NAME_NAME is a possibly more friendly name of WANTED_TYPE,
2318169701Skan   or NULL if the ordinary name of the type should be used.  ARG_TYPE
2319169701Skan   is the type of the actual argument.  ARG_NUM is the number of that
2320169701Skan   argument.  */
2321169701Skanstatic void
2322169701Skanformat_type_warning (const char *descr, const char *format_start,
2323169701Skan		     int format_length, tree wanted_type, int pointer_count,
2324169701Skan		     const char *wanted_type_name, tree arg_type, int arg_num)
2325169701Skan{
2326169701Skan  char *p;
2327169701Skan  /* If ARG_TYPE is a typedef with a misleading name (for example,
2328169701Skan     size_t but not the standard size_t expected by printf %zu), avoid
2329169701Skan     printing the typedef name.  */
2330169701Skan  if (wanted_type_name
2331169701Skan      && TYPE_NAME (arg_type)
2332169701Skan      && TREE_CODE (TYPE_NAME (arg_type)) == TYPE_DECL
2333169701Skan      && DECL_NAME (TYPE_NAME (arg_type))
2334169701Skan      && !strcmp (wanted_type_name,
2335169701Skan		  lang_hooks.decl_printable_name (TYPE_NAME (arg_type), 2)))
2336169701Skan    arg_type = TYPE_MAIN_VARIANT (arg_type);
2337169701Skan  /* The format type and name exclude any '*' for pointers, so those
2338169701Skan     must be formatted manually.  For all the types we currently have,
2339169701Skan     this is adequate, but formats taking pointers to functions or
2340169701Skan     arrays would require the full type to be built up in order to
2341169701Skan     print it with %T.  */
2342169701Skan  p = (char *) alloca (pointer_count + 2);
2343169701Skan  if (pointer_count == 0)
2344169701Skan    p[0] = 0;
2345169701Skan  else if (c_dialect_cxx ())
2346169701Skan    {
2347169701Skan      memset (p, '*', pointer_count);
2348169701Skan      p[pointer_count] = 0;
234990075Sobrien    }
2350169701Skan  else
2351169701Skan    {
2352169701Skan      p[0] = ' ';
2353169701Skan      memset (p + 1, '*', pointer_count);
2354169701Skan      p[pointer_count + 1] = 0;
2355169701Skan    }
2356169701Skan  if (wanted_type_name)
2357169701Skan    {
2358169701Skan      if (descr)
2359169701Skan	warning (OPT_Wformat, "%s should have type %<%s%s%>, "
2360169701Skan		 "but argument %d has type %qT",
2361169701Skan		 descr, wanted_type_name, p, arg_num, arg_type);
2362169701Skan      else
2363169701Skan	warning (OPT_Wformat, "format %q.*s expects type %<%s%s%>, "
2364169701Skan		 "but argument %d has type %qT",
2365169701Skan		 format_length, format_start, wanted_type_name, p,
2366169701Skan		 arg_num, arg_type);
2367169701Skan    }
2368169701Skan  else
2369169701Skan    {
2370169701Skan      if (descr)
2371169701Skan	warning (OPT_Wformat, "%s should have type %<%T%s%>, "
2372169701Skan		 "but argument %d has type %qT",
2373169701Skan		 descr, wanted_type, p, arg_num, arg_type);
2374169701Skan      else
2375169701Skan	warning (OPT_Wformat, "format %q.*s expects type %<%T%s%>, "
2376169701Skan		 "but argument %d has type %qT",
2377169701Skan		 format_length, format_start, wanted_type, p, arg_num, arg_type);
2378169701Skan    }
237990075Sobrien}
2380132731Skan
2381169701Skan
2382132731Skan/* Given a format_char_info array FCI, and a character C, this function
2383132731Skan   returns the index into the conversion_specs where that specifier's
2384169701Skan   data is located.  The character must exist.  */
2385132731Skanstatic unsigned int
2386132731Skanfind_char_info_specifier_index (const format_char_info *fci, int c)
2387132731Skan{
2388169701Skan  unsigned i;
2389169701Skan
2390169701Skan  for (i = 0; fci->format_chars; i++, fci++)
2391169701Skan    if (strchr (fci->format_chars, c))
2392169701Skan      return i;
2393169701Skan
2394132731Skan  /* We shouldn't be looking for a non-existent specifier.  */
2395169701Skan  gcc_unreachable ();
2396132731Skan}
2397132731Skan
2398132731Skan/* Given a format_length_info array FLI, and a character C, this
2399132731Skan   function returns the index into the conversion_specs where that
2400169701Skan   modifier's data is located.  The character must exist.  */
2401132731Skanstatic unsigned int
2402132731Skanfind_length_info_modifier_index (const format_length_info *fli, int c)
2403132731Skan{
2404169701Skan  unsigned i;
2405169701Skan
2406169701Skan  for (i = 0; fli->name; i++, fli++)
2407169701Skan    if (strchr (fli->name, c))
2408169701Skan      return i;
2409169701Skan
2410132731Skan  /* We shouldn't be looking for a non-existent modifier.  */
2411169701Skan  gcc_unreachable ();
2412132731Skan}
2413132731Skan
2414132731Skan/* Determine the type of HOST_WIDE_INT in the code being compiled for
2415132731Skan   use in GCC's __asm_fprintf__ custom format attribute.  You must
2416132731Skan   have set dynamic_format_types before calling this function.  */
2417132731Skanstatic void
2418132731Skaninit_dynamic_asm_fprintf_info (void)
2419132731Skan{
2420132731Skan  static tree hwi;
2421169701Skan
2422132731Skan  if (!hwi)
2423132731Skan    {
2424132731Skan      format_length_info *new_asm_fprintf_length_specs;
2425132731Skan      unsigned int i;
2426169701Skan
2427132731Skan      /* Find the underlying type for HOST_WIDE_INT.  For the %w
2428132731Skan	 length modifier to work, one must have issued: "typedef
2429132731Skan	 HOST_WIDE_INT __gcc_host_wide_int__;" in one's source code
2430132731Skan	 prior to using that modifier.  */
2431146908Skan      hwi = maybe_get_identifier ("__gcc_host_wide_int__");
2432146908Skan      if (!hwi)
2433146908Skan	{
2434169701Skan	  error ("%<__gcc_host_wide_int__%> is not defined as a type");
2435146908Skan	  return;
2436146908Skan	}
2437146908Skan      hwi = identifier_global_value (hwi);
2438146908Skan      if (!hwi || TREE_CODE (hwi) != TYPE_DECL)
2439146908Skan	{
2440169701Skan	  error ("%<__gcc_host_wide_int__%> is not defined as a type");
2441146908Skan	  return;
2442146908Skan	}
2443146908Skan      hwi = DECL_ORIGINAL_TYPE (hwi);
2444169701Skan      gcc_assert (hwi);
2445146908Skan      if (hwi != long_integer_type_node && hwi != long_long_integer_type_node)
2446146908Skan	{
2447169701Skan	  error ("%<__gcc_host_wide_int__%> is not defined as %<long%>"
2448169701Skan		 " or %<long long%>");
2449146908Skan	  return;
2450146908Skan	}
2451132731Skan
2452132731Skan      /* Create a new (writable) copy of asm_fprintf_length_specs.  */
2453169701Skan      new_asm_fprintf_length_specs = (format_length_info *)
2454169701Skan				     xmemdup (asm_fprintf_length_specs,
2455132731Skan					      sizeof (asm_fprintf_length_specs),
2456132731Skan					      sizeof (asm_fprintf_length_specs));
2457132731Skan
2458132731Skan      /* HOST_WIDE_INT must be one of 'long' or 'long long'.  */
2459132731Skan      i = find_length_info_modifier_index (new_asm_fprintf_length_specs, 'w');
2460132731Skan      if (hwi == long_integer_type_node)
2461132731Skan	new_asm_fprintf_length_specs[i].index = FMT_LEN_l;
2462132731Skan      else if (hwi == long_long_integer_type_node)
2463132731Skan	new_asm_fprintf_length_specs[i].index = FMT_LEN_ll;
2464132731Skan      else
2465169701Skan	gcc_unreachable ();
2466132731Skan
2467132731Skan      /* Assign the new data for use.  */
2468132731Skan      dynamic_format_types[asm_fprintf_format_type].length_char_specs =
2469132731Skan	new_asm_fprintf_length_specs;
2470132731Skan    }
2471132731Skan}
2472132731Skan
2473169701Skan/* Determine the type of a "locus" in the code being compiled for use
2474169701Skan   in GCC's __gcc_gfc__ custom format attribute.  You must have set
2475169701Skan   dynamic_format_types before calling this function.  */
2476169701Skanstatic void
2477169701Skaninit_dynamic_gfc_info (void)
2478169701Skan{
2479169701Skan  static tree locus;
2480169701Skan
2481169701Skan  if (!locus)
2482169701Skan    {
2483169701Skan      static format_char_info *gfc_fci;
2484169701Skan
2485169701Skan      /* For the GCC __gcc_gfc__ custom format specifier to work, one
2486169701Skan	 must have declared 'locus' prior to using this attribute.  If
2487169701Skan	 we haven't seen this declarations then you shouldn't use the
2488169701Skan	 specifier requiring that type.  */
2489169701Skan      if ((locus = maybe_get_identifier ("locus")))
2490169701Skan	{
2491169701Skan	  locus = identifier_global_value (locus);
2492169701Skan	  if (locus)
2493169701Skan	    {
2494169701Skan	      if (TREE_CODE (locus) != TYPE_DECL)
2495169701Skan		{
2496169701Skan		  error ("%<locus%> is not defined as a type");
2497169701Skan		  locus = 0;
2498169701Skan		}
2499169701Skan	      else
2500169701Skan		locus = TREE_TYPE (locus);
2501169701Skan	    }
2502169701Skan	}
2503169701Skan
2504169701Skan      /* Assign the new data for use.  */
2505169701Skan
2506169701Skan      /* Handle the __gcc_gfc__ format specifics.  */
2507169701Skan      if (!gfc_fci)
2508169701Skan	dynamic_format_types[gcc_gfc_format_type].conversion_specs =
2509169701Skan	  gfc_fci = (format_char_info *)
2510169701Skan		     xmemdup (gcc_gfc_char_table,
2511169701Skan			      sizeof (gcc_gfc_char_table),
2512169701Skan			      sizeof (gcc_gfc_char_table));
2513169701Skan      if (locus)
2514169701Skan	{
2515169701Skan	  const unsigned i = find_char_info_specifier_index (gfc_fci, 'L');
2516169701Skan	  gfc_fci[i].types[0].type = &locus;
2517169701Skan	  gfc_fci[i].pointer_count = 1;
2518169701Skan	}
2519169701Skan    }
2520169701Skan}
2521169701Skan
2522132731Skan/* Determine the types of "tree" and "location_t" in the code being
2523132731Skan   compiled for use in GCC's diagnostic custom format attributes.  You
2524132731Skan   must have set dynamic_format_types before calling this function.  */
2525132731Skanstatic void
2526132731Skaninit_dynamic_diag_info (void)
2527132731Skan{
2528132731Skan  static tree t, loc, hwi;
2529169701Skan
2530132731Skan  if (!loc || !t || !hwi)
2531132731Skan    {
2532169701Skan      static format_char_info *diag_fci, *tdiag_fci, *cdiag_fci, *cxxdiag_fci;
2533132731Skan      static format_length_info *diag_ls;
2534132731Skan      unsigned int i;
2535132731Skan
2536132731Skan      /* For the GCC-diagnostics custom format specifiers to work, one
2537169701Skan	 must have declared 'tree' and/or 'location_t' prior to using
2538132731Skan	 those attributes.  If we haven't seen these declarations then
2539132731Skan	 you shouldn't use the specifiers requiring these types.
2540132731Skan	 However we don't force a hard ICE because we may see only one
2541132731Skan	 or the other type.  */
2542132731Skan      if ((loc = maybe_get_identifier ("location_t")))
2543146908Skan	{
2544146908Skan	  loc = identifier_global_value (loc);
2545146908Skan	  if (loc)
2546146908Skan	    {
2547146908Skan	      if (TREE_CODE (loc) != TYPE_DECL)
2548146908Skan		{
2549169701Skan		  error ("%<location_t%> is not defined as a type");
2550146908Skan		  loc = 0;
2551146908Skan		}
2552146908Skan	      else
2553146908Skan		loc = TREE_TYPE (loc);
2554146908Skan	    }
2555146908Skan	}
2556132731Skan
2557169701Skan      /* We need to grab the underlying 'union tree_node' so peek into
2558132731Skan	 an extra type level.  */
2559132731Skan      if ((t = maybe_get_identifier ("tree")))
2560146908Skan	{
2561146908Skan	  t = identifier_global_value (t);
2562146908Skan	  if (t)
2563146908Skan	    {
2564146908Skan	      if (TREE_CODE (t) != TYPE_DECL)
2565146908Skan		{
2566169701Skan		  error ("%<tree%> is not defined as a type");
2567146908Skan		  t = 0;
2568146908Skan		}
2569146908Skan	      else if (TREE_CODE (TREE_TYPE (t)) != POINTER_TYPE)
2570146908Skan		{
2571169701Skan		  error ("%<tree%> is not defined as a pointer type");
2572146908Skan		  t = 0;
2573146908Skan		}
2574146908Skan	      else
2575146908Skan		t = TREE_TYPE (TREE_TYPE (t));
2576146908Skan	    }
2577146908Skan	}
2578169701Skan
2579132731Skan      /* Find the underlying type for HOST_WIDE_INT.  For the %w
2580132731Skan	 length modifier to work, one must have issued: "typedef
2581132731Skan	 HOST_WIDE_INT __gcc_host_wide_int__;" in one's source code
2582132731Skan	 prior to using that modifier.  */
2583132731Skan      if ((hwi = maybe_get_identifier ("__gcc_host_wide_int__")))
2584146908Skan	{
2585146908Skan	  hwi = identifier_global_value (hwi);
2586146908Skan	  if (hwi)
2587146908Skan	    {
2588146908Skan	      if (TREE_CODE (hwi) != TYPE_DECL)
2589146908Skan		{
2590169701Skan		  error ("%<__gcc_host_wide_int__%> is not defined as a type");
2591146908Skan		  hwi = 0;
2592146908Skan		}
2593146908Skan	      else
2594146908Skan		{
2595146908Skan		  hwi = DECL_ORIGINAL_TYPE (hwi);
2596169701Skan		  gcc_assert (hwi);
2597146908Skan		  if (hwi != long_integer_type_node
2598146908Skan		      && hwi != long_long_integer_type_node)
2599146908Skan		    {
2600169701Skan		      error ("%<__gcc_host_wide_int__%> is not defined"
2601169701Skan			     " as %<long%> or %<long long%>");
2602146908Skan		      hwi = 0;
2603146908Skan		    }
2604146908Skan		}
2605146908Skan	    }
2606146908Skan	}
2607169701Skan
2608132731Skan      /* Assign the new data for use.  */
2609132731Skan
2610132731Skan      /* All the GCC diag formats use the same length specs.  */
2611169701Skan      if (!diag_ls)
2612132731Skan	dynamic_format_types[gcc_diag_format_type].length_char_specs =
2613169701Skan	  dynamic_format_types[gcc_tdiag_format_type].length_char_specs =
2614132731Skan	  dynamic_format_types[gcc_cdiag_format_type].length_char_specs =
2615132731Skan	  dynamic_format_types[gcc_cxxdiag_format_type].length_char_specs =
2616169701Skan	  diag_ls = (format_length_info *)
2617169701Skan		    xmemdup (gcc_diag_length_specs,
2618132731Skan			     sizeof (gcc_diag_length_specs),
2619169701Skan			     sizeof (gcc_diag_length_specs));
2620132731Skan      if (hwi)
2621169701Skan	{
2622132731Skan	  /* HOST_WIDE_INT must be one of 'long' or 'long long'.  */
2623132731Skan	  i = find_length_info_modifier_index (diag_ls, 'w');
2624132731Skan	  if (hwi == long_integer_type_node)
2625132731Skan	    diag_ls[i].index = FMT_LEN_l;
2626132731Skan	  else if (hwi == long_long_integer_type_node)
2627132731Skan	    diag_ls[i].index = FMT_LEN_ll;
2628132731Skan	  else
2629169701Skan	    gcc_unreachable ();
2630132731Skan	}
2631132731Skan
2632132731Skan      /* Handle the __gcc_diag__ format specifics.  */
2633169701Skan      if (!diag_fci)
2634132731Skan	dynamic_format_types[gcc_diag_format_type].conversion_specs =
2635169701Skan	  diag_fci = (format_char_info *)
2636169701Skan		     xmemdup (gcc_diag_char_table,
2637169701Skan			      sizeof (gcc_diag_char_table),
2638169701Skan			      sizeof (gcc_diag_char_table));
2639132731Skan      if (loc)
2640169701Skan	{
2641132731Skan	  i = find_char_info_specifier_index (diag_fci, 'H');
2642132731Skan	  diag_fci[i].types[0].type = &loc;
2643132731Skan	  diag_fci[i].pointer_count = 1;
2644132731Skan	}
2645132731Skan      if (t)
2646169701Skan	{
2647132731Skan	  i = find_char_info_specifier_index (diag_fci, 'J');
2648132731Skan	  diag_fci[i].types[0].type = &t;
2649132731Skan	  diag_fci[i].pointer_count = 1;
2650132731Skan	}
2651132731Skan
2652169701Skan      /* Handle the __gcc_tdiag__ format specifics.  */
2653169701Skan      if (!tdiag_fci)
2654169701Skan	dynamic_format_types[gcc_tdiag_format_type].conversion_specs =
2655169701Skan	  tdiag_fci = (format_char_info *)
2656169701Skan		      xmemdup (gcc_tdiag_char_table,
2657169701Skan			       sizeof (gcc_tdiag_char_table),
2658169701Skan			       sizeof (gcc_tdiag_char_table));
2659169701Skan      if (loc)
2660169701Skan	{
2661169701Skan	  i = find_char_info_specifier_index (tdiag_fci, 'H');
2662169701Skan	  tdiag_fci[i].types[0].type = &loc;
2663169701Skan	  tdiag_fci[i].pointer_count = 1;
2664169701Skan	}
2665169701Skan      if (t)
2666169701Skan	{
2667169701Skan	  /* All specifiers taking a tree share the same struct.  */
2668169701Skan	  i = find_char_info_specifier_index (tdiag_fci, 'D');
2669169701Skan	  tdiag_fci[i].types[0].type = &t;
2670169701Skan	  tdiag_fci[i].pointer_count = 1;
2671169701Skan	  i = find_char_info_specifier_index (tdiag_fci, 'J');
2672169701Skan	  tdiag_fci[i].types[0].type = &t;
2673169701Skan	  tdiag_fci[i].pointer_count = 1;
2674169701Skan	}
2675169701Skan
2676132731Skan      /* Handle the __gcc_cdiag__ format specifics.  */
2677169701Skan      if (!cdiag_fci)
2678132731Skan	dynamic_format_types[gcc_cdiag_format_type].conversion_specs =
2679169701Skan	  cdiag_fci = (format_char_info *)
2680169701Skan		      xmemdup (gcc_cdiag_char_table,
2681169701Skan			       sizeof (gcc_cdiag_char_table),
2682169701Skan			       sizeof (gcc_cdiag_char_table));
2683132731Skan      if (loc)
2684169701Skan	{
2685132731Skan	  i = find_char_info_specifier_index (cdiag_fci, 'H');
2686132731Skan	  cdiag_fci[i].types[0].type = &loc;
2687132731Skan	  cdiag_fci[i].pointer_count = 1;
2688132731Skan	}
2689132731Skan      if (t)
2690169701Skan	{
2691132731Skan	  /* All specifiers taking a tree share the same struct.  */
2692132731Skan	  i = find_char_info_specifier_index (cdiag_fci, 'D');
2693132731Skan	  cdiag_fci[i].types[0].type = &t;
2694132731Skan	  cdiag_fci[i].pointer_count = 1;
2695132731Skan	  i = find_char_info_specifier_index (cdiag_fci, 'J');
2696132731Skan	  cdiag_fci[i].types[0].type = &t;
2697132731Skan	  cdiag_fci[i].pointer_count = 1;
2698132731Skan	}
2699132731Skan
2700132731Skan      /* Handle the __gcc_cxxdiag__ format specifics.  */
2701169701Skan      if (!cxxdiag_fci)
2702132731Skan	dynamic_format_types[gcc_cxxdiag_format_type].conversion_specs =
2703169701Skan	  cxxdiag_fci = (format_char_info *)
2704169701Skan			xmemdup (gcc_cxxdiag_char_table,
2705169701Skan				 sizeof (gcc_cxxdiag_char_table),
2706169701Skan				 sizeof (gcc_cxxdiag_char_table));
2707132731Skan      if (loc)
2708169701Skan	{
2709132731Skan	  i = find_char_info_specifier_index (cxxdiag_fci, 'H');
2710132731Skan	  cxxdiag_fci[i].types[0].type = &loc;
2711132731Skan	  cxxdiag_fci[i].pointer_count = 1;
2712132731Skan	}
2713132731Skan      if (t)
2714169701Skan	{
2715132731Skan	  /* All specifiers taking a tree share the same struct.  */
2716132731Skan	  i = find_char_info_specifier_index (cxxdiag_fci, 'D');
2717132731Skan	  cxxdiag_fci[i].types[0].type = &t;
2718132731Skan	  cxxdiag_fci[i].pointer_count = 1;
2719132731Skan	  i = find_char_info_specifier_index (cxxdiag_fci, 'J');
2720132731Skan	  cxxdiag_fci[i].types[0].type = &t;
2721132731Skan	  cxxdiag_fci[i].pointer_count = 1;
2722132731Skan	}
2723132731Skan    }
2724132731Skan}
2725132731Skan
2726169701Skan#ifdef TARGET_FORMAT_TYPES
2727169701Skanextern const format_kind_info TARGET_FORMAT_TYPES[];
2728169701Skan#endif
2729169701Skan
2730132731Skan/* Handle a "format" attribute; arguments as in
2731132731Skan   struct attribute_spec.handler.  */
2732132731Skantree
2733169701Skanhandle_format_attribute (tree *node, tree ARG_UNUSED (name), tree args,
2734132731Skan			 int flags, bool *no_add_attrs)
2735132731Skan{
2736132731Skan  tree type = *node;
2737132731Skan  function_format_info info;
2738132731Skan  tree argument;
2739132731Skan
2740169701Skan#ifdef TARGET_FORMAT_TYPES
2741169701Skan  /* If the target provides additional format types, we need to
2742169701Skan     add them to FORMAT_TYPES at first use.  */
2743169701Skan  if (TARGET_FORMAT_TYPES != NULL && !dynamic_format_types)
2744169701Skan    {
2745169701Skan      dynamic_format_types = xmalloc ((n_format_types + TARGET_N_FORMAT_TYPES)
2746169701Skan				      * sizeof (dynamic_format_types[0]));
2747169701Skan      memcpy (dynamic_format_types, format_types_orig,
2748169701Skan	      sizeof (format_types_orig));
2749169701Skan      memcpy (&dynamic_format_types[n_format_types], TARGET_FORMAT_TYPES,
2750169701Skan	      TARGET_N_FORMAT_TYPES * sizeof (dynamic_format_types[0]));
2751169701Skan
2752169701Skan      format_types = dynamic_format_types;
2753169701Skan      n_format_types += TARGET_N_FORMAT_TYPES;
2754169701Skan    }
2755169701Skan#endif
2756169701Skan
2757132731Skan  if (!decode_format_attr (args, &info, 0))
2758132731Skan    {
2759132731Skan      *no_add_attrs = true;
2760132731Skan      return NULL_TREE;
2761132731Skan    }
2762132731Skan
2763132731Skan  argument = TYPE_ARG_TYPES (type);
2764132731Skan  if (argument)
2765132731Skan    {
2766132731Skan      if (!check_format_string (argument, info.format_num, flags,
2767132731Skan				no_add_attrs))
2768132731Skan	return NULL_TREE;
2769132731Skan
2770132731Skan      if (info.first_arg_num != 0)
2771132731Skan	{
2772132731Skan	  unsigned HOST_WIDE_INT arg_num = 1;
2773132731Skan
2774132731Skan	  /* Verify that first_arg_num points to the last arg,
2775132731Skan	     the ...  */
2776132731Skan	  while (argument)
2777132731Skan	    arg_num++, argument = TREE_CHAIN (argument);
2778132731Skan
2779132731Skan	  if (arg_num != info.first_arg_num)
2780132731Skan	    {
2781132731Skan	      if (!(flags & (int) ATTR_FLAG_BUILT_IN))
2782169701Skan		error ("args to be formatted is not %<...%>");
2783132731Skan	      *no_add_attrs = true;
2784132731Skan	      return NULL_TREE;
2785132731Skan	    }
2786132731Skan	}
2787132731Skan    }
2788132731Skan
2789132731Skan  if (info.format_type == strftime_format_type && info.first_arg_num != 0)
2790132731Skan    {
2791132731Skan      error ("strftime formats cannot format arguments");
2792132731Skan      *no_add_attrs = true;
2793132731Skan      return NULL_TREE;
2794132731Skan    }
2795132731Skan
2796132731Skan  /* If this is a custom GCC-internal format type, we have to
2797132731Skan     initialize certain bits a runtime.  */
2798132731Skan  if (info.format_type == asm_fprintf_format_type
2799169701Skan      || info.format_type == gcc_gfc_format_type
2800132731Skan      || info.format_type == gcc_diag_format_type
2801169701Skan      || info.format_type == gcc_tdiag_format_type
2802132731Skan      || info.format_type == gcc_cdiag_format_type
2803132731Skan      || info.format_type == gcc_cxxdiag_format_type)
2804132731Skan    {
2805132731Skan      /* Our first time through, we have to make sure that our
2806169701Skan	 format_type data is allocated dynamically and is modifiable.  */
2807132731Skan      if (!dynamic_format_types)
2808169701Skan	format_types = dynamic_format_types = (format_kind_info *)
2809132731Skan	  xmemdup (format_types_orig, sizeof (format_types_orig),
2810132731Skan		   sizeof (format_types_orig));
2811132731Skan
2812132731Skan      /* If this is format __asm_fprintf__, we have to initialize
2813169701Skan	 GCC's notion of HOST_WIDE_INT for checking %wd.  */
2814132731Skan      if (info.format_type == asm_fprintf_format_type)
2815169701Skan	init_dynamic_asm_fprintf_info ();
2816169701Skan      /* If this is format __gcc_gfc__, we have to initialize GCC's
2817169701Skan	 notion of 'locus' at runtime for %L.  */
2818169701Skan      else if (info.format_type == gcc_gfc_format_type)
2819169701Skan	init_dynamic_gfc_info ();
2820132731Skan      /* If this is one of the diagnostic attributes, then we have to
2821169701Skan	 initialize 'location_t' and 'tree' at runtime.  */
2822132731Skan      else if (info.format_type == gcc_diag_format_type
2823169701Skan	       || info.format_type == gcc_tdiag_format_type
2824132731Skan	       || info.format_type == gcc_cdiag_format_type
2825132731Skan	       || info.format_type == gcc_cxxdiag_format_type)
2826169701Skan	init_dynamic_diag_info ();
2827132731Skan      else
2828169701Skan	gcc_unreachable ();
2829132731Skan    }
2830132731Skan
2831132731Skan  return NULL_TREE;
2832132731Skan}
2833