updated strftime checking patch for gcc-2.8.1
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",
participants (1)
-
Olson, Arthur David