--- gcc/c-format.c.orig	Wed Feb 20 14:54:32 2002
+++ gcc/c-format.c	Mon May 27 20:42:37 2002
@@ -19,6 +19,8 @@
 Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 02111-1307, USA.  */
 
+/* $FreeBSD$ */
+
 #include "config.h"
 #include "system.h"
 #include "tree.h"
@@ -75,6 +77,7 @@
    last.  */
 enum format_type { printf_format_type, scanf_format_type,
 		   strftime_format_type, strfmon_format_type,
+		   printf0_format_type,
 		   format_type_error };
 
 typedef struct function_format_info
@@ -82,6 +85,7 @@
   enum format_type format_type;	/* type of format (printf, scanf, etc.) */
   unsigned HOST_WIDE_INT format_num;	/* number of format argument */
   unsigned HOST_WIDE_INT first_arg_num;	/* number of first arg (zero for varargs) */
+  int null_format_ok;			/* TRUE if the format string may be NULL */
 } function_format_info;
 
 static bool decode_format_attr		PARAMS ((tree,
@@ -249,7 +253,7 @@
     {
       if (validated_p)
 	abort ();
-      error ("unrecognized format specifier");
+      error_with_decl (getdecls (), "unrecognized format specifier");
       return false;
     }
   else
@@ -540,6 +544,7 @@
   /* Pointer to type of argument expected if '*' is used for a precision,
      or NULL if '*' not used for precisions.  */
   tree *const precision_type;
+  const int null_format_ok;
 } format_kind_info;
 
 
@@ -783,6 +788,18 @@
   { "S",   1, STD_EXT, { TEX_W,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "-wp",      "R"  },
   /* GNU conversion specifiers.  */
   { "m",   0, STD_EXT, { T89_V,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "-wp",      ""   },
+  /* BSD conversion specifiers.  */
+  /* FreeBSD kernel extensions (src/sys/kern/subr_prf.c).
+     The format %b is supported to decode error registers.
+     Its usage is:	printf("reg=%b\n", regval, "*");
+     which produces:	reg=3
+     The format %D provides a hexdump given a pointer and separator string:
+     ("%6D", ptr, ":")		-> XX:XX:XX:XX:XX:XX
+     ("%*D", len, ptr, " ")	-> XX XX XX XX ...
+   */
+  { "D",   1, STD_EXT, { T89_C,  BADLEN,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "-wp",      "cR" },
+  { "b",   1, STD_EXT, { T89_C,  BADLEN,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "-wp",      ""   },
+  { "rz",  0, STD_EXT, { T89_I,  BADLEN,   BADLEN,   T89_L,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "-wp0 +#",  "i"  },
   { NULL,  0, 0, NOLENGTHS, NULL, NULL }
 };
 
@@ -847,23 +864,29 @@
     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
+    &integer_type_node, &integer_type_node, 0
   },
   { "scanf",    scanf_length_specs,   scan_char_table,  "*'I", NULL, 
     scanf_flag_specs, scanf_flag_pairs,
     FMT_FLAG_ARG_CONVERT|FMT_FLAG_SCANF_A_KLUDGE|FMT_FLAG_USE_DOLLAR|FMT_FLAG_ZERO_WIDTH_BAD|FMT_FLAG_DOLLAR_GAP_POINTER_OK,
     'w', 0, 0, '*', 'L',
-    NULL, NULL
+    NULL, NULL, 0
   },
   { "strftime", NULL,                 time_char_table,  "_-0^#", "EO",
     strftime_flag_specs, strftime_flag_pairs,
     FMT_FLAG_FANCY_PERCENT_OK, 'w', 0, 0, 0, 0,
-    NULL, NULL
+    NULL, NULL, 0
   },
   { "strfmon",  strfmon_length_specs, monetary_char_table, "=^+(!-", NULL, 
     strfmon_flag_specs, strfmon_flag_pairs,
     FMT_FLAG_ARG_CONVERT, 'w', '#', 'p', 0, 'L',
-    NULL, NULL
+    NULL, NULL, 0
+  },
+  { "printf0",   printf_length_specs,  print_char_table, " +#0-'I", NULL,
+    printf_flag_specs, printf_flag_pairs,
+    FMT_FLAG_ARG_CONVERT|FMT_FLAG_DOLLAR_MULTIPLE|FMT_FLAG_USE_DOLLAR|FMT_FLAG_EMPTY_PREC_OK,
+   'w', 0, 'p', 0, 'L',
+   &integer_type_node, &integer_type_node, 1
   }
 };
 
@@ -917,6 +940,14 @@
 
 static void check_format_types	PARAMS ((int *, format_wanted_type *));
 
+
+inline static int get_null_fmt_ok (fmttype)
+	enum format_type fmttype;
+{
+  return format_types[(int)fmttype].null_format_ok;
+}
+
+
 /* Decode a format type from a string, returning the type, or
    format_type_error if not valid, in which case the caller should print an
    error message.  */
@@ -1986,6 +2017,57 @@
 	    }
 	}
 
+      if (*format_chars == 'b')
+	{
+	  /* There should be an int arg to control the string arg.  */
+	  if (params == 0)
+	    {
+	      status_warning (status, "too few arguments for format");
+	      return;
+	    }
+	    if (info->first_arg_num != 0)
+	    {
+	      cur_param = TREE_VALUE (params);
+	      params = TREE_CHAIN (params);
+	      ++arg_num;
+	      if ((TYPE_MAIN_VARIANT (TREE_TYPE (cur_param))
+		   != integer_type_node)
+		  &&
+		  (TYPE_MAIN_VARIANT (TREE_TYPE (cur_param))
+		   != unsigned_type_node))
+		{
+		  status_warning (status, "bitmap is not type int (arg %d)",
+				  arg_num);
+		}
+	    }
+	}
+      if (*format_chars == 'D')
+	{
+	  /* There should be an unsigned char * arg before the string arg.  */
+	  if (params == 0)
+	    {
+	      status_warning (status, "too few arguments for format");
+	      return;
+	    }
+	    if (info->first_arg_num != 0)
+	    {
+	      tree cur_type;
+
+	      cur_param = TREE_VALUE (params);
+	      params = TREE_CHAIN (params);
+	      ++arg_num;
+	      cur_type = TREE_TYPE (cur_param);
+	      if (TREE_CODE (cur_type) != POINTER_TYPE
+		  || TYPE_MAIN_VARIANT (TREE_TYPE (cur_type))
+		     != unsigned_char_type_node)
+		{
+		  status_warning (status,
+		      "ethernet address is not type unsigned char * (arg %d)",
+				  arg_num);
+		}
+	    }
+	}
+
       format_char = *format_chars;
       if (format_char == 0
 	  || (!(fki->flags & (int) FMT_FLAG_FANCY_PERCENT_OK)
@@ -2112,7 +2194,7 @@
 	  else if (strchr (fci->flags2, '2') != 0)
 	    y2k_level = 2;
 	  if (y2k_level == 3)
-	    status_warning (status, "`%%%c' yields only last 2 digits of year in some locales",
+	    status_warning (status, "`%%%c' yields only last 2 digits of year in some locales on non-BSD systems",
 			    format_char);
 	  else if (y2k_level == 2)
 	    status_warning (status, "`%%%c' yields only last 2 digits of year", format_char);