This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

Patch for checking strfmon formats


This patch adds support for checking strfmon (a function from the Single
Unix Specification - format monetary amounts according to the locale's
conventions) formats.

Bootstrapped with no regressions on i686-pc-linux-gnu.  OK to commit?

2000-12-27  Joseph S. Myers  <jsm28@cam.ac.uk>

	* c-common.c (enum format_type): Add strfmon_format_type.
	(decl_attributes): Handle format attributes strfmon and
	__strfmon__.
	(FMT_FLAG_USE_DOLLAR, FMT_FLAG_ZERO_WIDTH_BAD,
	FMT_FLAG_EMPTY_PREC_OK): Define.
	(format_char_info): Update comment for flag_chars.
	(format_flag_spec): Add skip_next_char.
	(format_kind_info): Add left_precision_char.
	(printf_flag_specs, scanf_flag_specs, strftime_flag_specs,
	format_types): Update for these new structure members and flags.
	(time_char_table): Make const.
	(strfmon_length_specs, strfmon_flag_specs, strfmon_flag_pairs,
	monetary_char_table): New.
	(format_types): Add details of strfmon formats.
	(init_function_format_info): Create default attribute for strfmon.
	(check_format_info_main): Check the new flags.  Handle
	skip_next_char and left precision.
	* toplev.c (documented_lang_options): Update description of
	-Wformat.
	* extend.texi: Document strfmon format attributes.  Document
	attribute forms such as __printf__.  Clarify format_arg attribute
	documentation.
	* invoke.texi (-Wformat): Update for strfmon formats.

2000-12-27  Joseph S. Myers  <jsm28@cam.ac.uk>

	* gcc.dg/format-strfmon-1.c: New test.

--- c-common.c.orig	Sat Dec 23 14:35:35 2000
+++ c-common.c	Wed Dec 27 18:00:22 2000
@@ -230,7 +230,7 @@ enum attrs {A_PACKED, A_NOCOMMON, A_COMM
 	    A_NO_LIMIT_STACK, A_PURE};

 enum format_type { printf_format_type, scanf_format_type,
-		   strftime_format_type };
+		   strftime_format_type, strfmon_format_type };

 static void add_attribute		PARAMS ((enum attrs, const char *,
 						 int, int, int));
@@ -955,6 +955,9 @@ decl_attributes (node, attributes, prefi
 		else if (!strcmp (p, "strftime")
 			 || !strcmp (p, "__strftime__"))
 		  format_type = strftime_format_type;
+		else if (!strcmp (p, "strfmon")
+			 || !strcmp (p, "__strfmon__"))
+		  format_type = strfmon_format_type;
 		else
 		  {
 		    warning ("`%s' is an unrecognized format function type", p);
@@ -1357,13 +1360,17 @@ enum
   FMT_FLAG_FANCY_PERCENT_OK = 4,
   /* With $ operand numbers, it is OK to reference the same argument more
      than once.  */
-  FMT_FLAG_DOLLAR_MULTIPLE = 8
+  FMT_FLAG_DOLLAR_MULTIPLE = 8,
+  /* This format type uses $ operand numbers (strfmon doesn't).  */
+  FMT_FLAG_USE_DOLLAR = 16,
+  /* Zero width is bad in this type of format (scanf).  */
+  FMT_FLAG_ZERO_WIDTH_BAD = 32,
+  /* Empty precision specification is OK in this type of format (printf).  */
+  FMT_FLAG_EMPTY_PREC_OK = 64
   /* Not included here: details of whether width or precision may occur
      (controlled by width_char and precision_char); details of whether
      '*' can be used for these (width_type and precision_type); details
-     of whether length modifiers can occur (length_char_specs); details
-     of when $ operand numbers are allowed (always, for the formats
-     supported, if arguments are converted).  */
+     of whether length modifiers can occur (length_char_specs).  */
 };


@@ -1415,7 +1422,8 @@ typedef struct
   /* Types accepted for each length modifier.  */
   format_type_detail types[FMT_LEN_MAX];
   /* List of other modifier characters allowed with these specifiers.
-     This lists flags, and additionally "w" for width, "p" for precision,
+     This lists flags, and additionally "w" for width, "p" for precision
+     (right precision, for strfmon), "#" for left precision (strfmon),
      "a" for scanf "a" allocation extension (not applicable in C99 mode),
      "*" for scanf suppression, and "E" and "O" for those strftime
      modifiers.  */
@@ -1447,6 +1455,9 @@ typedef struct
      the unpredicated one, for any pedantic warning.  For example, 'o'
      for strftime formats (meaning 'O' is an extension over C99).  */
   int predicate;
+  /* Nonzero if the next character after this flag in the format should
+     be skipped ('=' in strfmon), zero otherwise.  */
+  int skip_next_char;
   /* The name to use for this flag in diagnostic messages.  For example,
      N_("`0' flag"), N_("field width").  */
   const char *name;
@@ -1497,7 +1508,11 @@ typedef struct
   int flags;
   /* Flag character to treat a width as, or 0 if width not used.  */
   int width_char;
-  /* Flag character to treat a precision as, or 0 if precision not used.  */
+  /* Flag character to treat a left precision (strfmon) as,
+     or 0 if left precision not used.  */
+  int left_precision_char;
+  /* Flag character to treat a precision (for strfmon, right precision) as,
+     or 0 if precision not used.  */
   int precision_char;
   /* If a flag character has the effect of suppressing the conversion of
      an argument ('*' in scanf), that flag character, otherwise 0.  */
@@ -1579,19 +1594,28 @@ static const format_length_info scanf_le
 };


+/* All tables for strfmon use STD_C89 everywhere, since -pedantic warnings
+   make no sense for a format type not part of any C standard version.  */
+static const format_length_info strfmon_length_specs[] =
+{
+  /* A GNU extension.  */
+  { "L", FMT_LEN_L, STD_C89, NULL, 0, 0 },
+  { NULL, 0, 0, NULL, 0, 0 }
+};
+
 static const format_flag_spec printf_flag_specs[] =
 {
-  { ' ',  0, N_("` ' flag"),        N_("the ` ' printf flag"),              STD_C89 },
-  { '+',  0, N_("`+' flag"),        N_("the `+' printf flag"),              STD_C89 },
-  { '#',  0, N_("`#' flag"),        N_("the `#' printf flag"),              STD_C89 },
-  { '0',  0, N_("`0' flag"),        N_("the `0' printf flag"),              STD_C89 },
-  { '-',  0, N_("`-' flag"),        N_("the `-' printf flag"),              STD_C89 },
-  { '\'', 0, N_("`'' flag"),        N_("the `'' printf flag"),              STD_EXT },
-  { 'I',  0, N_("`I' flag"),        N_("the `I' printf flag"),              STD_EXT },
-  { 'w',  0, N_("field width"),     N_("field width in printf format"),     STD_C89 },
-  { 'p',  0, N_("precision"),       N_("precision in printf format"),       STD_C89 },
-  { 'L',  0, N_("length modifier"), N_("length modifier in printf format"), STD_C89 },
-  { 0, 0, NULL, NULL, 0 }
+  { ' ',  0, 0, N_("` ' flag"),        N_("the ` ' printf flag"),              STD_C89 },
+  { '+',  0, 0, N_("`+' flag"),        N_("the `+' printf flag"),              STD_C89 },
+  { '#',  0, 0, N_("`#' flag"),        N_("the `#' printf flag"),              STD_C89 },
+  { '0',  0, 0, N_("`0' flag"),        N_("the `0' printf flag"),              STD_C89 },
+  { '-',  0, 0, N_("`-' flag"),        N_("the `-' printf flag"),              STD_C89 },
+  { '\'', 0, 0, N_("`'' flag"),        N_("the `'' printf flag"),              STD_EXT },
+  { 'I',  0, 0, N_("`I' flag"),        N_("the `I' printf flag"),              STD_EXT },
+  { 'w',  0, 0, N_("field width"),     N_("field width in printf format"),     STD_C89 },
+  { 'p',  0, 0, N_("precision"),       N_("precision in printf format"),       STD_C89 },
+  { 'L',  0, 0, N_("length modifier"), N_("length modifier in printf format"), STD_C89 },
+  { 0, 0, 0, NULL, NULL, 0 }
 };


@@ -1606,13 +1630,13 @@ static const format_flag_pair printf_fla

 static const format_flag_spec scanf_flag_specs[] =
 {
-  { '*',  0, N_("assignment suppression"), N_("assignment suppression"),          STD_C89 },
-  { 'a',  0, N_("`a' flag"),               N_("the `a' scanf flag"),              STD_EXT },
-  { 'w',  0, N_("field width"),            N_("field width in scanf format"),     STD_C89 },
-  { 'L',  0, N_("length modifier"),        N_("length modifier in scanf format"), STD_C89 },
-  { '\'', 0, N_("`'' flag"),               N_("the `'' scanf flag"),              STD_EXT },
-  { 'I',  0, N_("`I' flag"),               N_("the `I' scanf flag"),              STD_EXT },
-  { 0, 0, NULL, NULL, 0 }
+  { '*',  0, 0, N_("assignment suppression"), N_("assignment suppression"),          STD_C89 },
+  { 'a',  0, 0, N_("`a' flag"),               N_("the `a' scanf flag"),              STD_EXT },
+  { 'w',  0, 0, N_("field width"),            N_("field width in scanf format"),     STD_C89 },
+  { 'L',  0, 0, N_("length modifier"),        N_("length modifier in scanf format"), STD_C89 },
+  { '\'', 0, 0, N_("`'' flag"),               N_("the `'' scanf flag"),              STD_EXT },
+  { 'I',  0, 0, N_("`I' flag"),               N_("the `I' scanf flag"),              STD_EXT },
+  { 0, 0, 0, NULL, NULL, 0 }
 };


@@ -1625,16 +1649,16 @@ static const format_flag_pair scanf_flag

 static const format_flag_spec strftime_flag_specs[] =
 {
-  { '_', 0,   N_("`_' flag"),     N_("the `_' strftime flag"),          STD_EXT },
-  { '-', 0,   N_("`-' flag"),     N_("the `-' strftime flag"),          STD_EXT },
-  { '0', 0,   N_("`0' flag"),     N_("the `0' strftime flag"),          STD_EXT },
-  { '^', 0,   N_("`^' flag"),     N_("the `^' strftime flag"),          STD_EXT },
-  { '#', 0,   N_("`#' flag"),     N_("the `#' strftime flag"),          STD_EXT },
-  { 'w', 0,   N_("field width"),  N_("field width in strftime format"), STD_EXT },
-  { 'E', 0,   N_("`E' modifier"), N_("the `E' strftime modifier"),      STD_C99 },
-  { 'O', 0,   N_("`O' modifier"), N_("the `O' strftime modifier"),      STD_C99 },
-  { 'O', 'o', NULL,               N_("the `O' modifier"),               STD_EXT },
-  { 0, 0, NULL, NULL, 0 }
+  { '_', 0,   0, N_("`_' flag"),     N_("the `_' strftime flag"),          STD_EXT },
+  { '-', 0,   0, N_("`-' flag"),     N_("the `-' strftime flag"),          STD_EXT },
+  { '0', 0,   0, N_("`0' flag"),     N_("the `0' strftime flag"),          STD_EXT },
+  { '^', 0,   0, N_("`^' flag"),     N_("the `^' strftime flag"),          STD_EXT },
+  { '#', 0,   0, N_("`#' flag"),     N_("the `#' strftime flag"),          STD_EXT },
+  { 'w', 0,   0, N_("field width"),  N_("field width in strftime format"), STD_EXT },
+  { 'E', 0,   0, N_("`E' modifier"), N_("the `E' strftime modifier"),      STD_C99 },
+  { 'O', 0,   0, N_("`O' modifier"), N_("the `O' strftime modifier"),      STD_C99 },
+  { 'O', 'o', 0, NULL,               N_("the `O' modifier"),               STD_EXT },
+  { 0, 0, 0, NULL, NULL, 0 }
 };


@@ -1649,6 +1673,28 @@ static const format_flag_pair strftime_f
 };


+static const format_flag_spec strfmon_flag_specs[] =
+{
+  { '=',  0, 1, N_("fill character"),  N_("fill character in strfmon format"),  STD_C89 },
+  { '^',  0, 0, N_("`^' flag"),        N_("the `^' strfmon flag"),              STD_C89 },
+  { '+',  0, 0, N_("`+' flag"),        N_("the `+' strfmon flag"),              STD_C89 },
+  { '(',  0, 0, N_("`(' flag"),        N_("the `(' strfmon flag"),              STD_C89 },
+  { '!',  0, 0, N_("`!' flag"),        N_("the `!' strfmon flag"),              STD_C89 },
+  { '-',  0, 0, N_("`-' flag"),        N_("the `-' strfmon flag"),              STD_C89 },
+  { 'w',  0, 0, N_("field width"),     N_("field width in strfmon format"),     STD_C89 },
+  { '#',  0, 0, N_("left precision"),  N_("left precision in strfmon format"),  STD_C89 },
+  { 'p',  0, 0, N_("right precision"), N_("right precision in strfmon format"), STD_C89 },
+  { 'L',  0, 0, N_("length modifier"), N_("length modifier in strfmon format"), STD_C89 },
+  { 0, 0, 0, NULL, NULL, 0 }
+};
+
+static const format_flag_pair strfmon_flag_pairs[] =
+{
+  { '+', '(', 0, 0 },
+  { 0, 0, 0, 0 }
+};
+
+
 #define T_I	&integer_type_node
 #define T89_I	{ STD_C89, NULL, T_I }
 #define T99_I	{ STD_C99, NULL, T_I }
@@ -1748,7 +1794,7 @@ static const format_char_info scan_char_
   { NULL, 0, 0, NOLENGTHS, NULL, NULL }
 };

-static format_char_info time_char_table[] =
+static const format_char_info time_char_table[] =
 {
   /* C89 conversion specifiers.  */
   { "ABZab",		0, STD_C89, NOLENGTHS, "^#",     ""   },
@@ -1775,23 +1821,36 @@ static format_char_info time_char_table[
   { NULL,		0, 0, NOLENGTHS, NULL, NULL }
 };

+static const format_char_info monetary_char_table[] =
+{
+  { "in", 0, STD_C89, { T89_D, BADLEN, BADLEN, BADLEN, BADLEN, T89_LD, BADLEN, BADLEN, BADLEN }, "=^+(!-w#p", "" },
+  { NULL, 0, 0, NOLENGTHS, NULL, NULL }
+};
+

 /* This must be in the same order as enum format_type.  */
 static const format_kind_info format_types[] =
 {
-  { "printf",   printf_length_specs, print_char_table, " +#0-'I", NULL,
+  { "printf",   printf_length_specs,  print_char_table, " +#0-'I", NULL,
     printf_flag_specs, printf_flag_pairs,
-    FMT_FLAG_ARG_CONVERT|FMT_FLAG_DOLLAR_MULTIPLE, 'w', 'p', 0, 'L',
+    FMT_FLAG_ARG_CONVERT|FMT_FLAG_DOLLAR_MULTIPLE|FMT_FLAG_USE_DOLLAR|FMT_FLAG_EMPTY_PREC_OK,
+    'w', 0, 'p', 0, 'L',
     &integer_type_node, &integer_type_node
   },
-  { "scanf",    scanf_length_specs,  scan_char_table,  "*'I", NULL,
+  { "scanf",    scanf_length_specs,   scan_char_table,  "*'I", NULL,
     scanf_flag_specs, scanf_flag_pairs,
-    FMT_FLAG_ARG_CONVERT|FMT_FLAG_SCANF_A_KLUDGE, 'w', 0, '*', 'L',
+    FMT_FLAG_ARG_CONVERT|FMT_FLAG_SCANF_A_KLUDGE|FMT_FLAG_USE_DOLLAR|FMT_FLAG_ZERO_WIDTH_BAD,
+    'w', 0, 0, '*', 'L',
     NULL, NULL
   },
-  { "strftime", NULL,                time_char_table,  "_-0^#", "EO",
+  { "strftime", NULL,                 time_char_table,  "_-0^#", "EO",
     strftime_flag_specs, strftime_flag_pairs,
-    FMT_FLAG_FANCY_PERCENT_OK, 'w', 0, 0, 0,
+    FMT_FLAG_FANCY_PERCENT_OK, 'w', 0, 0, 0, 0,
+    NULL, NULL
+  },
+  { "strfmon",  strfmon_length_specs, monetary_char_table, "=^+(!-", NULL,
+    strfmon_flag_specs, strfmon_flag_pairs,
+    FMT_FLAG_ARG_CONVERT, 'w', '#', 'p', 0, 'L',
     NULL, NULL
   }
 };
@@ -1934,6 +1993,9 @@ init_function_format_info ()
       record_international_format (get_identifier ("gettext"), NULL_TREE, 1);
       record_international_format (get_identifier ("dgettext"), NULL_TREE, 2);
       record_international_format (get_identifier ("dcgettext"), NULL_TREE, 2);
+      /* X/Open strfmon function.  */
+      record_function_format (get_identifier ("strfmon"), NULL_TREE,
+			      strfmon_format_type, 3, 4);
     }
 }

@@ -2684,7 +2746,7 @@ check_format_info_main (status, res, inf
 	}
       flag_chars[0] = 0;

-      if ((fki->flags & FMT_FLAG_ARG_CONVERT) && has_operand_number != 0)
+      if ((fki->flags & FMT_FLAG_USE_DOLLAR) && has_operand_number != 0)
 	{
 	  /* Possibly read a $ operand number at the start of the format.
 	     If one was previously used, one is required here.  If one
@@ -2709,10 +2771,10 @@ check_format_info_main (status, res, inf
       while (*format_chars != 0
 	     && strchr (fki->flag_chars, *format_chars) != 0)
 	{
+	  const format_flag_spec *s = get_flag_spec (flag_specs,
+						     *format_chars, NULL);
 	  if (strchr (flag_chars, *format_chars) != 0)
 	    {
-	      const format_flag_spec *s = get_flag_spec (flag_specs,
-							 *format_chars, NULL);
 	      status_warning (status, "repeated %s in format", _(s->name));
 	    }
 	  else
@@ -2721,6 +2783,15 @@ check_format_info_main (status, res, inf
 	      flag_chars[i++] = *format_chars;
 	      flag_chars[i] = 0;
 	    }
+	  if (s->skip_next_char)
+	    {
+	      ++format_chars;
+	      if (*format_chars == 0)
+		{
+		  status_warning (status, "missing fill character at end of strfmon format");
+		  return;
+		}
+	    }
 	  ++format_chars;
 	}

@@ -2785,9 +2856,7 @@ check_format_info_main (status, res, inf
 	  else
 	    {
 	      /* Possibly read a numeric width.  If the width is zero,
-		 we complain; for scanf this is bad according to the
-		 standard, and for printf and strftime it cannot occur
-		 because 0 is a flag.  */
+		 we complain if appropriate.  */
 	      int non_zero_width_char = FALSE;
 	      int found_width = FALSE;
 	      while (ISDIGIT (*format_chars))
@@ -2797,7 +2866,8 @@ check_format_info_main (status, res, inf
 		    non_zero_width_char = TRUE;
 		  ++format_chars;
 		}
-	      if (found_width && !non_zero_width_char)
+	      if (found_width && !non_zero_width_char &&
+		  (fki->flags & FMT_FLAG_ZERO_WIDTH_BAD))
 		status_warning (status, "zero width in %s format",
 				fki->name);
 	      if (found_width)
@@ -2809,6 +2879,20 @@ check_format_info_main (status, res, inf
 	    }
 	}

+      /* Read any format left precision (must be a number, not *).  */
+      if (fki->left_precision_char != 0 && *format_chars == '#')
+	{
+	  ++format_chars;
+	  i = strlen (flag_chars);
+	  flag_chars[i++] = fki->left_precision_char;
+	  flag_chars[i] = 0;
+	  if (!ISDIGIT (*format_chars))
+	    status_warning (status, "empty left precision in %s format",
+			    fki->name);
+	  while (ISDIGIT (*format_chars))
+	    ++format_chars;
+	}
+
       /* Read any format precision, possibly * or *m$.  */
       if (fki->precision_char != 0 && *format_chars == '.')
 	{
@@ -2870,6 +2954,10 @@ check_format_info_main (status, res, inf
 	    }
 	  else
 	    {
+	      if (!(fki->flags & FMT_FLAG_EMPTY_PREC_OK)
+		  && !ISDIGIT (*format_chars))
+		status_warning (status, "empty precision in %s format",
+				fki->name);
 	      while (ISDIGIT (*format_chars))
 		++format_chars;
 	    }
--- toplev.c.orig	Sun Dec 17 18:16:49 2000
+++ toplev.c	Wed Dec 27 16:49:36 2000
@@ -1226,7 +1226,7 @@ documented_lang_options[] =
   { "-Wno-comments", "" },
   { "-Wconversion", "Warn about possibly confusing type conversions" },
   { "-Wno-conversion", "" },
-  { "-Wformat", "Warn about printf/scanf/strftime format anomalies" },
+  { "-Wformat", "Warn about printf/scanf/strftime/strfmon format anomalies" },
   { "-Wno-format", "" },
   { "-Wformat-y2k", "" },
   { "-Wno-format-y2k",
--- extend.texi.orig	Sat Dec 23 01:40:23 2000
+++ extend.texi	Wed Dec 27 16:46:38 2000
@@ -1381,7 +1381,7 @@
 @cindex functions that behave like malloc
 @cindex @code{volatile} applied to function
 @cindex @code{const} applied to function
-@cindex functions with @code{printf}, @code{scanf} or @code{strftime} style arguments
+@cindex functions with @code{printf}, @code{scanf}, @code{strftime} or @code{strfmon} style arguments
 @cindex functions that are passed arguments in registers on the 386
 @cindex functions that pop the argument stack on the 386
 @cindex functions that do not pop the argument stack on the 386
@@ -1505,8 +1505,9 @@
 @item format (@var{archetype}, @var{string-index}, @var{first-to-check})
 @cindex @code{format} function attribute
 The @code{format} attribute specifies that a function takes @code{printf},
-@code{scanf}, or @code{strftime} style arguments which should be type-checked
-against a format string.  For example, the declaration:
+@code{scanf}, @code{strftime} or @code{strfmon} style arguments which
+should be type-checked against a format string.  For example, the
+declaration:

 @smallexample
 extern int
@@ -1520,8 +1521,9 @@
 @code{my_format}.

 The parameter @var{archetype} determines how the format string is
-interpreted, and should be either @code{printf}, @code{scanf}, or
-@code{strftime}.  The
+interpreted, and should be @code{printf}, @code{scanf}, @code{strftime}
+or @code{strfmon}.  (You can also use @code{__printf__},
+@code{__scanf__}, @code{__strftime__} or @code{__strfmon__}.)  The
 parameter @var{string-index} specifies which argument is the format
 string argument (starting from 1), while @var{first-to-check} is the
 number of the first argument to check against the format string.  For
@@ -1545,15 +1547,20 @@
 warnings are requested (using @samp{-Wformat}), so there is no need to
 modify the header file @file{stdio.h}.  In C99 mode, the functions
 @code{snprintf}, @code{vsnprintf}, @code{vscanf}, @code{vfscanf} and
-@code{vsscanf} are also checked.
+@code{vsscanf} are also checked.  Except in strictly conforming C
+standard modes, the X/Open function @code{strfmon} is also checked.
 @xref{C Dialect Options,,Options Controlling C Dialect}.

 @item format_arg (@var{string-index})
 @cindex @code{format_arg} function attribute
-The @code{format_arg} attribute specifies that a function takes
-@code{printf} or @code{scanf} style arguments, modifies it (for example,
-to translate it into another language), and passes it to a @code{printf}
-or @code{scanf} style function.  For example, the declaration:
+The @code{format_arg} attribute specifies that a function takes a format
+string for a @code{printf}, @code{scanf}, @code{strftime} or
+@code{strfmon} style function and modifies it (for example, to translate
+it into another language), so the result can be passed to a
+@code{printf}, @code{scanf}, @code{strftime} or @code{strfmon} style
+function (with the remaining arguments to the format function the same
+as they would have been for the unmodified string).  For example, the
+declaration:

 @smallexample
 extern char *
@@ -1562,22 +1569,28 @@
 @end smallexample

 @noindent
-causes the compiler to check the arguments in calls to
-@code{my_dgettext} whose result is passed to a @code{printf},
-@code{scanf}, or @code{strftime} type function for consistency with the
-@code{printf} style format string argument @code{my_format}.
+causes the compiler to check the arguments in calls to a @code{printf},
+@code{scanf}, @code{strftime} or @code{strfmon} type function, whose
+format string argument is a call to the @code{my_dgettext} function, for
+consistency with the format string argument @code{my_format}.  If the
+@code{format_arg} attribute had not been specified, all the compiler
+could tell in such calls to format functions would be that the format
+string argument is not constant; this would generate a warning when
+@code{-Wformat-nonliteral} is used, but the calls could not be checked
+without the attribute.

 The parameter @var{string-index} specifies which argument is the format
 string argument (starting from 1).

 The @code{format-arg} attribute allows you to identify your own
 functions which modify format strings, so that GNU CC can check the
-calls to @code{printf}, @code{scanf}, or @code{strftime} function whose
-operands are a call to one of your own function.  The compiler always
-treats @code{gettext}, @code{dgettext}, and @code{dcgettext} in this
-manner except when strict ISO C support is requested by @samp{-ansi} or
-an appropriate @samp{-std} option, or @samp{-ffreestanding} is used.
-@xref{C Dialect Options,,Options Controlling C Dialect}.
+calls to @code{printf}, @code{scanf}, @code{strftime} or @code{strfmon}
+type function whose operands are a call to one of your own function.
+The compiler always treats @code{gettext}, @code{dgettext}, and
+@code{dcgettext} in this manner except when strict ISO C support is
+requested by @samp{-ansi} or an appropriate @samp{-std} option, or
+@samp{-ffreestanding} is used.  @xref{C Dialect Options,,Options
+Controlling C Dialect}.

 @item no_instrument_function
 @cindex @code{no_instrument_function} function attribute
--- invoke.texi.orig	Tue Dec 19 13:23:05 2000
+++ invoke.texi	Wed Dec 27 16:32:30 2000
@@ -1596,7 +1596,11 @@
 @item -Wformat
 Check calls to @code{printf} and @code{scanf}, etc., to make sure that
 the arguments supplied have types appropriate to the format string
-specified.
+specified, and that the conversions specified in the format string make
+sense.  This includes standard functions, and others specified by format
+attributes (@pxref{Function Attributes}), in the @code{printf},
+@code{scanf}, @code{strftime} and @code{strfmon} (an X/Open extension,
+not in the C standard) families.

 The formats are checked against the format features supported by GNU
 libc version 2.2.  These include all ISO C89 and C99 features, as well
@@ -1605,8 +1609,9 @@
 features; GCC does not support warning about features that go beyond a
 particular library's limitations.  However, if @samp{-pedantic} is used
 with @samp{-Wformat}, warnings will be given about format features not
-in the selected standard version.  @xref{C Dialect Options,,Options
-Controlling C Dialect}.
+in the selected standard version (but not for @code{strfmon} formats,
+since those are not in any version of the C standard).  @xref{C Dialect
+Options,,Options Controlling C Dialect}.

 @samp{-Wformat} is included in @samp{-Wall}.  For more control over some
 aspects of format checking, the options @samp{-Wno-format-y2k},
--- testsuite/gcc.dg/format-strfmon-1.c.orig	Fri Sep 11 11:31:59 1998
+++ testsuite/gcc.dg/format-strfmon-1.c	Wed Dec 27 19:28:36 2000
@@ -0,0 +1,68 @@
+/* Test for strfmon format checking.  */
+/* Origin: Joseph Myers <jsm28@cam.ac.uk> */
+/* { dg-do compile } */
+/* { dg-options "-std=gnu99 -Wformat" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+/* Kludge to get something that may be ssize_t.  */
+#define unsigned signed
+typedef __SIZE_TYPE__ ssize_t;
+#undef unsigned
+
+#define NULL ((void *)0)
+
+extern ssize_t strfmon (char *restrict, size_t, const char *restrict, ...);
+
+void
+foo (char *s, size_t m, double d, long double ld)
+{
+  /* Examples of valid formats from Austin Group draft 5.  */
+  strfmon (s, m, "%n", d);
+  strfmon (s, m, "%11n", d);
+  strfmon (s, m, "%#5n", d);
+  strfmon (s, m, "%=*#5n", d);
+  strfmon (s, m, "%=0#5n", d);
+  strfmon (s, m, "%^#5n", d);
+  strfmon (s, m, "%^#5.0n", d);
+  strfmon (s, m, "%^#5.4n", d);
+  strfmon (s, m, "%(#5n", d);
+  strfmon (s, m, "%(!#5n", d);
+  /* Some more valid formats, including the GNU L length extension.  */
+  strfmon (s, m, "abc%-11ndef%==i%%", d, d);
+  strfmon (s, m, "%%abc%-11ndef%==Li%=%i", d, ld, d);
+  strfmon (s, m, "%Li", ld);
+  strfmon (s, m, "%11Li", ld);
+  strfmon (s, m, "%#5Li", ld);
+  strfmon (s, m, "%=*#5Li", ld);
+  strfmon (s, m, "%=0#5Li", ld);
+  strfmon (s, m, "%^#5Li", ld);
+  strfmon (s, m, "%^#5.0Li", ld);
+  strfmon (s, m, "%^#5.4Li", ld);
+  strfmon (s, m, "%(#5Li", ld);
+  strfmon (s, m, "%(!#5Li", ld);
+  /* Formats with the wrong types used.  */
+  strfmon (s, m, "%Ln", d); /* { dg-warning "format" "wrong type" } */
+  strfmon (s, m, "%n", ld); /* { dg-warning "format" "wrong type" } */
+  /* The + and ( flags cannot be used together.  */
+  strfmon (s, m, "%+(i", d); /* { dg-warning "flag" "+ and ( flags" } */
+  strfmon (s, m, "%(+i", d); /* { dg-warning "flag" "+ and ( flags" } */
+  /* Although empty precision is OK for printf, it isn't here.  */
+  strfmon (s, m, "%#.5n", d); /* { dg-warning "empty" "empty left precision" } */
+  strfmon (s, m, "%#5.n", d); /* { dg-warning "empty" "empty right precision" } */
+  /* However, zero is a valid value for width and precisions.  */
+  strfmon (s, m, "%0#0.0n", d);
+  /* Test bogus %% constructions.  */
+  strfmon (s, m, "%^%"); /* { dg-warning "format" "bogus %%" } */
+  strfmon (s, m, "%!%\n"); /* { dg-warning "format" "bogus %%" } */
+  strfmon (s, m, "%5%\n"); /* { dg-warning "format" "bogus %%" } */
+  strfmon (s, m, "%.5%\n"); /* { dg-warning "format" "bogus %%" } */
+  strfmon (s, m, "%#5%\n"); /* { dg-warning "format" "bogus %%" } */
+  /* Miscellaneous bogus formats.  */
+  strfmon (s, m, "%n%n", d); /* { dg-warning "arguments" "too few args" } */
+  strfmon (s, m, ""); /* { dg-warning "zero-length" "empty" } */
+  strfmon (s, m, NULL); /* { dg-warning "null" "null format string" } */
+  strfmon (s, m, "%"); /* { dg-warning "trailing" "tailing %" } */
+  strfmon (s, m, "%n\0", d); /* { dg-warning "embedded" "embedded NUL" } */
+  strfmon (s, m, "%^^n", d); /* { dg-warning "repeated" "repeated flag" } */
+}

-- 
Joseph S. Myers
jsm28@cam.ac.uk


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]