$OpenBSD: patch-gcc_c-format_c,v 1.2 2008/07/25 20:50:26 martynas Exp $
--- gcc/c-format.c.orig	Sun Dec  1 19:51:44 2002
+++ gcc/c-format.c	Fri Jul 25 12:34:57 2008
@@ -54,7 +54,7 @@ set_Wformat (setting)
 
 /* This must be in the same order as format_types, with format_type_error
    last.  */
-enum format_type { printf_format_type, scanf_format_type,
+enum format_type { printf_format_type, kprintf_format_type, syslog_format_type, scanf_format_type,
 		   strftime_format_type, strfmon_format_type,
 		   format_type_error };
 
@@ -540,6 +540,10 @@ typedef struct format_wanted_type
   /* Whether the argument, dereferenced once, is written into and so the
      argument must not be a pointer to a const-qualified type.  */
   int writing_in_flag;
+  /* If the argument is to be written into and is an array, should the
+     width specifier be equal to the size of the array, or one less
+     (to accommodate a NULL being placed at the end) */
+  int size_equals_width;
   /* Whether the argument, dereferenced once, is read from and so
      must not be a NULL pointer.  */
   int reading_from_flag;
@@ -552,6 +556,8 @@ typedef struct format_wanted_type
   const char *name;
   /* The actual parameter to check against the wanted type.  */
   tree param;
+  /* Field width of type */
+  int field_width;
   /* The argument number of that parameter.  */
   int arg_num;
   /* The next type to check for this format conversion, or NULL if none.  */
@@ -572,7 +578,15 @@ static const format_length_info printf_length_specs[] 
   { NULL, 0, 0, NULL, 0, 0 }
 };
 
+static const format_length_info kprintf_length_specs[] =
+{
+  { "h", FMT_LEN_h, STD_C89, NULL, 0, 0 },
+  { "l", FMT_LEN_l, STD_C89, NULL, 0, 0 },
+  { "L", FMT_LEN_L, STD_C89, NULL, 0, 0 },
+  { NULL, 0, 0, NULL, 0, 0 }
+};
 
+
 /* This differs from printf_length_specs only in that "Z" is not accepted.  */
 static const format_length_info scanf_length_specs[] =
 {
@@ -765,6 +779,46 @@ static const format_char_info print_char_table[] =
   { NULL,  0, 0, NOLENGTHS, NULL, NULL }
 };
 
+static const format_char_info kprint_char_table[] =
+{
+  /* C89 conversion specifiers.  */
+  { "di",  0, STD_C89, { T89_I,   T99_SC,  T89_S,   T89_L,   T9L_LL,  TEX_LL,  T99_SST, T99_PD,  T99_IM  }, "-wp0 +'I", "i"  },
+  { "oxX", 0, STD_C89, { T89_UI,  T99_UC,  T89_US,  T89_UL,  T9L_ULL, TEX_ULL, T99_ST,  T99_UPD, T99_UIM }, "-wp0#",    "i"  },
+  { "u",   0, STD_C89, { T89_UI,  T99_UC,  T89_US,  T89_UL,  T9L_ULL, TEX_ULL, T99_ST,  T99_UPD, T99_UIM }, "-wp0'I",   "i"  },
+  { "c",   0, STD_C89, { T89_I,   BADLEN,  BADLEN,  T94_WI,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "-w",       ""   },
+  { "s",   1, STD_C89, { T89_C,   BADLEN,  BADLEN,  T94_W,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "-wp",      "cR" },
+  { "p",   1, STD_C89, { T89_V,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "-w",       "c"  },
+/* Kernel bitmap formatting */
+  { "b",   1, STD_C89, { T89_C,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN }, "",		""   },
+/* Kernel recursive format */
+  { ":",   1, STD_C89, { T89_V,	  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN }, "",		""   },
+/* Kernel debugger auto-radix printing */
+  { "nrz", 0, STD_C89, { T89_I,	  T89_I,   T89_I,   T89_L,   T9L_LL,  TEX_LL,  BADLEN,	BADLEN,  BADLEN }, "-wp0# +",	""   },
+  { NULL,  0, 0, NOLENGTHS, NULL, NULL }
+};
+
+static const format_char_info syslog_char_table[] =
+{
+  /* C89 conversion specifiers.  */
+  { "di",  0, STD_C89, { T89_I,   T99_SC,  T89_S,   T89_L,   T9L_LL,  TEX_LL,  T99_SST, T99_PD,  T99_IM  }, "-wp0 +'I", "i"  },
+  { "oxX", 0, STD_C89, { T89_UI,  T99_UC,  T89_US,  T89_UL,  T9L_ULL, TEX_ULL, T99_ST,  T99_UPD, T99_UIM }, "-wp0#",    "i"  },
+  { "u",   0, STD_C89, { T89_UI,  T99_UC,  T89_US,  T89_UL,  T9L_ULL, TEX_ULL, T99_ST,  T99_UPD, T99_UIM }, "-wp0'I",   "i"  },
+  { "fgG", 0, STD_C89, { T89_D,   BADLEN,  BADLEN,  T99_D,   BADLEN,  T89_LD,  BADLEN,  BADLEN,  BADLEN  }, "-wp0 +#'", ""   },
+  { "eE",  0, STD_C89, { T89_D,   BADLEN,  BADLEN,  T99_D,   BADLEN,  T89_LD,  BADLEN,  BADLEN,  BADLEN  }, "-wp0 +#",  ""   },
+  { "c",   0, STD_C89, { T89_I,   BADLEN,  BADLEN,  T94_WI,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "-w",       ""   },
+  { "s",   1, STD_C89, { T89_C,   BADLEN,  BADLEN,  T94_W,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "-wp",      "cR" },
+  { "p",   1, STD_C89, { T89_V,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "-w",       "c"  },
+  { "n",   1, STD_C89, { T89_I,   T99_SC,  T89_S,   T89_L,   T9L_LL,  BADLEN,  T99_SST, T99_PD,  T99_IM  }, "",         "W"  },
+  /* C99 conversion specifiers.  */
+  { "F",   0, STD_C99, { T99_D,   BADLEN,  BADLEN,  T99_D,   BADLEN,  T99_LD,  BADLEN,  BADLEN,  BADLEN  }, "-wp0 +#'", ""   },
+  { "aA",  0, STD_C99, { T99_D,   BADLEN,  BADLEN,  T99_D,   BADLEN,  T99_LD,  BADLEN,  BADLEN,  BADLEN  }, "-wp0 +#",  ""   },
+  /* X/Open conversion specifiers.  */
+  { "C",   0, STD_EXT, { TEX_WI,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "-w",       ""   },
+  { "S",   1, STD_EXT, { TEX_W,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "-wp",      "R"  },
+  { "m",   0, STD_C89, { T89_V,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "-wp",      ""   },
+  { NULL,  0, 0, NOLENGTHS, NULL, NULL }
+};
+
 static const format_char_info scan_char_table[] =
 {
   /* C89 conversion specifiers.  */
@@ -828,6 +882,18 @@ static const format_kind_info format_types[] =
     'w', 0, 'p', 0, 'L',
     &integer_type_node, &integer_type_node
   },
+  { "kprintf",   kprintf_length_specs,  kprint_char_table, " +#0-'I", NULL, 
+    printf_flag_specs, printf_flag_pairs,
+    FMT_FLAG_ARG_CONVERT|FMT_FLAG_DOLLAR_MULTIPLE|FMT_FLAG_USE_DOLLAR|FMT_FLAG_EMPTY_PREC_OK,
+    'w', 0, 'p', 0, 'L',
+    &integer_type_node, &integer_type_node
+  },
+  { "syslog",   printf_length_specs,  syslog_char_table, " +#0-'I", NULL, 
+    printf_flag_specs, printf_flag_pairs,
+    FMT_FLAG_ARG_CONVERT|FMT_FLAG_DOLLAR_MULTIPLE|FMT_FLAG_USE_DOLLAR|FMT_FLAG_EMPTY_PREC_OK,
+    'w', 0, 'p', 0, 'L',
+    &integer_type_node, &integer_type_node
+  },
   { "scanf",    scanf_length_specs,   scan_char_table,  "*'I", NULL, 
     scanf_flag_specs, scanf_flag_pairs,
     FMT_FLAG_ARG_CONVERT|FMT_FLAG_SCANF_A_KLUDGE|FMT_FLAG_USE_DOLLAR|FMT_FLAG_ZERO_WIDTH_BAD|FMT_FLAG_DOLLAR_GAP_POINTER_OK,
@@ -1574,6 +1640,7 @@ check_format_info_main (status, res, info, format_char
       const format_length_info *fli = NULL;
       const format_char_info *fci = NULL;
       char flag_chars[256];
+      int field_width = 0;
       int aflag = 0;
       if (*format_chars == 0)
 	{
@@ -1715,20 +1782,29 @@ check_format_info_main (status, res, info, format_char
 	      /* Possibly read a numeric width.  If the width is zero,
 		 we complain if appropriate.  */
 	      int non_zero_width_char = FALSE;
-	      int found_width = FALSE;
+	      unsigned int found_width = 0;
+	      char format_num_str[32];
+
+	      format_num_str[0] = '\0';
 	      while (ISDIGIT (*format_chars))
 		{
-		  found_width = TRUE;
+		  if (found_width < (sizeof(format_num_str)-2))
+		    {
+		      format_num_str[found_width++] = *format_chars;
+                      format_num_str[found_width] = '\0';
+		    }
 		  if (*format_chars != '0')
 		    non_zero_width_char = TRUE;
 		  ++format_chars;
 		}
-	      if (found_width && !non_zero_width_char &&
+
+	      if (found_width > 0 && !non_zero_width_char &&
 		  (fki->flags & (int) FMT_FLAG_ZERO_WIDTH_BAD))
 		status_warning (status, "zero width in %s format",
 				fki->name);
-	      if (found_width)
+	      if (found_width > 0)
 		{
+		  field_width  = atoi(format_num_str);
 		  i = strlen (flag_chars);
 		  flag_chars[i++] = fki->width_char;
 		  flag_chars[i] = 0;
@@ -2126,10 +2202,15 @@ check_format_info_main (status, res, info, format_char
 	  main_wanted_type.wanted_type_name = wanted_type_name;
 	  main_wanted_type.pointer_count = fci->pointer_count + aflag;
 	  main_wanted_type.char_lenient_flag = 0;
+	  main_wanted_type.field_width = field_width;
 	  if (strchr (fci->flags2, 'c') != 0)
 	    main_wanted_type.char_lenient_flag = 1;
 	  main_wanted_type.writing_in_flag = 0;
 	  main_wanted_type.reading_from_flag = 0;
+	  if (strchr (fci->format_chars, 'c') != 0)
+	    main_wanted_type.size_equals_width = 1;
+	  else
+	    main_wanted_type.size_equals_width = 0;
 	  if (aflag)
 	    main_wanted_type.writing_in_flag = 1;
 	  else
@@ -2224,6 +2305,27 @@ check_format_types (status, types)
 		cur_param = TREE_OPERAND (cur_param, 0);
 	      else
 		cur_param = 0;
+
+	      /* Test static string bounds for sscan if -Wbounded is on as well */
+	      if (warn_bounded
+		  && types->writing_in_flag
+		  && i == 0
+		  && cur_param != 0
+		  && COMPLETE_TYPE_P (TREE_TYPE (cur_param)) 
+		  && TREE_CODE (TREE_TYPE (cur_param)) == ARRAY_TYPE
+		  && TREE_CODE (TREE_TYPE (TREE_TYPE (cur_param))) == INTEGER_TYPE)
+		{
+		  tree array_domain = TYPE_DOMAIN (TREE_TYPE (cur_param));
+		  tree array_size_expr = TYPE_MAX_VALUE (TYPE_DOMAIN (TREE_TYPE (cur_param)));
+		  int f = types->size_equals_width ? 0 : 1;
+		  if (array_size_expr != 0 && types->field_width > 0)
+		    {
+		      int array_size = TREE_INT_CST_LOW (array_size_expr) + 1;
+		      if (array_size < (types->field_width + f))
+			warning ("Array size (%d) smaller than format string size (%d)",
+				 array_size, types->field_width + f);
+		    }
+		}
 
 	      /* See if this is an attempt to write into a const type with
 		 scanf or with printf "%n".  Note: the writing in happens
