updated strftime checking patch for gcc-2.8.1

Olson, Arthur David OLSONA at dc37a.nci.nih.gov
Thu Apr 2 20:24:29 UTC 1998


Below find an updated strftime-checking patch for the c-common.c distributed
with gcc-2.8.1
The patch arranges for gcc to do the same sort of checking of strftime formats
as it does
of printf and scanf formats. The patch also warns about formats that will (or
that might in some locale) produce only the low-order two digits of a year,
since such formats may cause Y2K challenges.

The goal here was to minimize changes to c-common.c, so in some cases the coding
is strained. Please forgive.

If anyone has ideas for improvements before this goes off to the GNU folks, I'd
love to hear from you.

				--ado

*** 1.1/c-common.c	Thu Apr  2 15:17:37 1998

--- 1/c-common.c	Thu Apr
2 15:17:37 1998

***************

*** 643,648 ****

--- 643,653 ----


|| !strcmp (IDENTIFIER_POINTER (format_type),


"__scanf__")))

  	      is_scan = 1;

+ 	    else if (TREE_CODE
(format_type) == IDENTIFIER_NODE

+ 		     && (!strcmp
(IDENTIFIER_POINTER (format_type), "strftime")

+ 			 ||
!strcmp (IDENTIFIER_POINTER (format_type),

+
"__strftime__")))

+ 	      is_scan = 2;

  	    else if (TREE_CODE
(format_type) == IDENTIFIER_NODE)

  	      {

  		error ("`%s' is
an unrecognized format function type",

***************

*** 954,959 ****

---
959,1006 ----

    { NULL }

  };

  

+ /*

+ ** Only format characters recognized
by glibc 2.0.6's strftime (as of 1998-04-01) handled.

+ ** "2" is used to for
formats which MUST do years as only two digits;

+ ** "3" is used for formats
which MAY do years as only two digits (depending on locale).

+ ** "E" is used
for formats where E modifier is acceptable

+ ** "O" is used for formats where O
modifier is acceptable by standard

+ ** "o" is used for formats where O modifier
is acceptable by GNU only

+ ** "G" is used for GNU extensions

+ */

+ 

+ static
format_char_info time_char_table[] = {

+ 

+   /* Sure Y2K troublemakers... */

+
{ "y", 		0, NULL, NULL, NULL, NULL, NULL, NULL, "2EO-_0w" },	/* last
2 of year */

+   { "D", 		0, NULL, NULL, NULL, NULL, NULL, NULL,
"2" },		/* %m/%d/%y--04/02/98 */

+   { "g", 		0, NULL, NULL,
NULL, NULL, NULL, NULL, "2O-_0w" },	/* last 2 of ISO year */

+ 

+   /*
Possible Y2K troublemakers... */

+   { "cx", 		0, NULL, NULL, NULL,
NULL, NULL, NULL, "3E" },		/* locale date+time/date */

+ 

+   /*
Literals, combo formats, and am/pm: no modifications, please! */

+   {
"%RTXnrt",		0, NULL, NULL, NULL, NULL, NULL, NULL, "" },

+   { "P",
0, NULL, NULL, NULL, NULL, NULL, NULL, "G" },

+ 

+   /* Formats for one number
that all agree take 'O' modifier (except "y" above) */

+   { "HIMSUWdemw",
0, NULL, NULL, NULL, NULL, NULL, NULL, "-_0Ow" },

+ 

+   /* Standard formats for
one number, GNU but not standard takes 'O' modifier

+   { "Vju",
0, NULL, NULL, NULL, NULL, NULL, NULL, "-_0Oow" },

+ 

+   /* GNU-only formats
for one number */

+   { "Gklsz",		0, NULL, NULL, NULL, NULL, NULL,
NULL, "-_0OGw" },

+ 

+   /* Other stuff */

+   { "ABZa",		0, NULL,
NULL, NULL, NULL, NULL, NULL, "^#" },

+   { "p",		0, NULL, NULL,
NULL, NULL, NULL, NULL, "#" },		/* AM/PM--is upcase, so no ^ */

+   {
"bh",		0, NULL, NULL, NULL, NULL, NULL, NULL, "^" },		/* short
month name */

+   { "CY",		0, NULL, NULL, NULL, NULL, NULL, NULL,
"-_0EOw" },	/* century/full year */

+ 

+   { NULL }

+ };

+ 

  typedef struct
function_format_info

  {

    struct function_format_info *next;  /* next
structure on the list */

***************

*** 1000,1005 ****

--- 1047,1053 ----


record_function_format (get_identifier ("vprintf"), NULL_TREE, 0, 1, 0);


record_function_format (get_identifier ("vfprintf"), NULL_TREE, 0, 2, 0);


record_function_format (get_identifier ("vsprintf"), NULL_TREE, 0, 2, 0);

+
record_function_format (get_identifier ("strftime"), NULL_TREE, 2, 3, 0);

  


record_international_format (get_identifier ("gettext"), NULL_TREE, 1);


record_international_format (get_identifier ("dgettext"), NULL_TREE,
2);

***************

*** 1008,1015 ****

  

  /* Record information for argument
format checking.  FUNCTION_IDENT is

     the identifier node for the name of the
function to check (its decl

!    need not exist yet).  IS_SCAN is true for
scanf-type format checking;

!    false indicates printf-style format checking.
FORMAT_NUM is the number

     of the argument which is the format control string
(starting from 1).

     FIRST_ARG_NUM is the number of the first actual argument
to check

     against the format string, or zero if no checking is not be
done

--- 1056,1064 ----

  

  /* Record information for argument format checking.
FUNCTION_IDENT is

     the identifier node for the name of the function to check
(its decl

!    need not exist yet).

!    IS_SCAN is 1 for scanf-type format
checking; 2 indicates strftime-style format checking;

!    0 indicates
printf-style format checking.  FORMAT_NUM is the number

     of the argument
which is the format control string (starting from 1).

     FIRST_ARG_NUM is the
number of the first actual argument to check

     against the format string, or
zero if no checking is not be done

***************

*** 1246,1252 ****


}

        flag_chars[0] = 0;

        suppressed = wide = precise = FALSE;

!
if (info->is_scan)

  	{

  	  suppressed = *format_chars == '*';

  	  if
(suppressed)

--- 1295,1302 ----

  	}

        flag_chars[0] = 0;


suppressed = wide = precise = FALSE;

!       aflag = 0;

!       if
(info->is_scan == 1)

  	{

  	  suppressed = *format_chars == '*';

  	  if
(suppressed)

***************

*** 1254,1260 ****

  	  while (isdigit
(*format_chars))

  	    ++format_chars;

  	}

!       else

  	{


/* See if we have a number followed by a dollar sign.  If we do,

  	     it
is an operand number, so set PARAMS to that operand.  */

--- 1304,1349 ----


while (isdigit (*format_chars))

  	    ++format_chars;

  	}

!       else
if (info->is_scan == 2)

!         {

! 	  while (*format_chars != 0 && index
("_-0^#", *format_chars) != 0)

! 	    {

! 	      pedwarn ("ANSI C
does not support the GNU-extension `%c' flag",

!
*format_chars);

! 	      if (index (flag_chars, *format_chars) != 0)

!
{

! 		  warning ("repeated `%c' flag in format",

!
*format_chars);

! 		  ++format_chars;

! 		}

!
else

! 		{

! 		  i = strlen (flag_chars);

!
flag_chars[i++] = *format_chars++;

! 		  flag_chars[i] = 0;

!
}

! 	    }

! 	  while (isdigit (*format_chars))

! 	    {

!
wide = TRUE;

!               ++format_chars;

! 	    }

! 	  if (wide)

!
pedwarn ("ANSI C does not support GNU-extension widths");

! 	  if
(*format_chars == 'E' || *format_chars == 'O')

! 	    {

! 	      i
= strlen (flag_chars);

! 	      flag_chars[i++] = *format_chars++;

!
flag_chars[i] = 0;

! 	      if (*format_chars == 'E' || *format_chars ==
'O')

! 	        {

! 		  warning ("multiple E/O modifiers in
format");

! 		  while (*format_chars == 'E' || *format_chars == 'O')

!
++format_chars;

! 		}

! 	    }

! 	}

!       else if
(info->is_scan == 0)

  	{

  	  /* See if we have a number followed by a
dollar sign.  If we do,

  	     it is an operand number, so set PARAMS to
that operand.  */

***************

*** 1388,1393 ****

--- 1477,1484 ----


}

  	    }

  	}

+       if (info->is_scan != 2)

+       {

        if
(*format_chars == 'h' || *format_chars == 'l')

  	length_char =
*format_chars++;

        else if (*format_chars == 'q' || *format_chars ==
'L')

***************

*** 1422,1427 ****

--- 1513,1519 ----


format_chars++;

  	    }

  	}

+       }

        if (suppressed &&
length_char != 0)

  	{

  	  sprintf (message,

***************

***
1430,1442 ****

  	  warning (message);

  	}

        format_char =
*format_chars;

!       if (format_char == 0 || format_char == '%')

  	{


warning ("conversion lacks type at end of format");

  	  continue;

  	}


format_chars++;

!       fci = info->is_scan ? scan_char_table :
print_char_table;

        while (fci->format_chars != 0

  	     && index
(fci->format_chars, format_char) == 0)

  	  ++fci;

--- 1522,1539 ----


warning (message);

  	}

        format_char = *format_chars;

!       if
(format_char == 0 || (info->is_scan != 2 && format_char == '%'))

  	{


warning ("conversion lacks type at end of format");

  	  continue;

  	}


format_chars++;

!       if (info->is_scan == 0)

! 	fci =
print_char_table;

!       else if (info->is_scan == 1)

! 	fci =
scan_char_table;

!       else

! 	fci = time_char_table;

        while
(fci->format_chars != 0

  	     && index (fci->format_chars, format_char)
== 0)

  	  ++fci;

***************

*** 1453,1458 ****

--- 1550,1561 ----


warning (message);

  	  continue;

  	}

+       if (index (fci->flag_chars,
'G') != 0)

+         pedwarn ("ANSI C does not support the GNU-extension `%c'
format",

+ 	  format_char);

+       if (index (fci->flag_chars, 'o') != 0 &&
index(flag_chars, 'O') != 0)

+         pedwarn ("ANSI C does not support `O'
modifier of `%c' format",

+ 	  format_char);

        if (wide && index
(fci->flag_chars, 'w') == 0)

  	{

  	  sprintf (message, "width used with
`%c' format",

***************

*** 1459,1464 ****

--- 1562,1579 ----


format_char);

  	  warning (message);

  	}

+       if (index
(fci->flag_chars, '2') != 0)

+ 	{

+ 	  sprintf (message, "`%c' format only
yields last two digits of years",

+ 		   format_char);

+
warning (message);

+ 	}

+       if (index (fci->flag_chars, '3') != 0)

+
{

+ 	  sprintf (message, "`%c' format may only yield last two digits of years
in some locales",

+ 		   format_char);

+ 	  warning (message);

+
}

        if (precise && index (fci->flag_chars, 'p') == 0)

  	{


sprintf (message, "precision used with `%c' format",

***************

***
1473,1479 ****

  	  /* To simplify the following code.  */

  	  aflag
= 0;

  	}

!       if (info->is_scan && format_char == '[')

  	{

  	  /*
Skip over scan set, in case it happens to have '%' in it.  */

  	  if
(*format_chars == '^')

--- 1588,1594 ----

  	  /* To simplify the following
code.  */

  	  aflag = 0;

  	}

!       if (info->is_scan == 1 && format_char
== '[')

  	{

  	  /* Skip over scan set, in case it happens to have '%'
in it.  */

  	  if (*format_chars == '^')

***************

*** 1502,1512 ****


for (i = 0; flag_chars[i] != 0; ++i)

  	{

  	  if (index (fci->flag_chars,
flag_chars[i]) == 0)

! 	    {

! 	      sprintf (message, "flag `%c' used
with type `%c'",

  		       flag_chars[i], format_char);

-
warning (message);

- 	    }

  	}

        if (precise && index
(flag_chars, '0') != 0

  	  && (format_char == 'd' || format_char ==
'i'

--- 1617,1624 ----

        for (i = 0; flag_chars[i] != 0; ++i)

  	{


if (index (fci->flag_chars, flag_chars[i]) == 0)

! 	      warning ("flag
`%c' used with `%c' format",

  		       flag_chars[i], format_char);


}

        if (precise && index (flag_chars, '0') != 0

  	  &&
(format_char == 'd' || format_char == 'i'

***************

*** 1527,1533 ****


case 'L': wanted_type = fci->bigllen ? *(fci->bigllen) : 0; break;

  	case
'Z': wanted_type = fci->zlen ? *fci->zlen : 0; break;

  	}

!       if
(wanted_type == 0)

  	{

  	  sprintf (message,

  		   "use of `%c'
length character with `%c' type character",

--- 1639,1645 ----

  	case
'L': wanted_type = fci->bigllen ? *(fci->bigllen) : 0; break;

  	case
'Z': wanted_type = fci->zlen ? *fci->zlen : 0; break;

  	}

!       if
(info->is_scan != 2 && wanted_type == 0)

  	{

  	  sprintf (message,


"use of `%c' length character with `%c' type character",





More information about the tz mailing list