RE: Strftime's %C and %y formats versus wide-ranging tm_year valu es
Below might be a simpler asctime.c fix. --ado ------- asctime.c ------- *** /tmp/geta7409 Thu Oct 14 17:29:28 2004 --- /tmp/getb7409 Thu Oct 14 17:29:28 2004 *************** *** 5,11 **** #ifndef lint #ifndef NOID ! static char elsieid[] = "@(#)asctime.c 7.26"; #endif /* !defined NOID */ #endif /* !defined lint */ --- 5,11 ---- #ifndef lint #ifndef NOID ! static char elsieid[] = "@(#)asctime.c 7.27"; #endif /* !defined NOID */ #endif /* !defined lint */ *************** *** 83,89 **** --- 83,96 ---- if (timeptr->tm_mon < 0 || timeptr->tm_mon >= MONSPERYEAR) mn = "???"; else mn = mon_name[timeptr->tm_mon]; + #if STRICTLY_STANDARD_ASCTIME /* + ** Be strict, potential overflow problems included. + ** In an ideal world, this code is never going to be used. + */ + (void) sprintf(year, "%d", timeptr->tm_year + TM_YEAR_BASE); + #else /* !STRICTLY_STANDARD_ASCTIME */ + /* ** Use strftime's %Y to generate the year, to avoid overflow problems ** when computing timeptr->tm_year + TM_YEAR_BASE. ** Assume that strftime is unaffected by other out-of-range members *************** *** 90,95 **** --- 97,103 ---- ** (e.g., timeptr->tm_mday) when processing "%Y". */ (void) strftime(year, sizeof year, "%Y", timeptr); + #endif /* !STRICTLY_STANDARD_ASCTIME */ /* ** We avoid using snprintf since it's not available on all systems. */
"Olson, Arthur David (NIH/NCI)" <olsona@dc37a.nci.nih.gov> writes:
Below might be a simpler asctime.c fix.
It's simpler, but I'd like to handle overflow nicely even if STRICTLY_STANDARD_ASCTIME is defined. Also, the "%-4s" in ASCTIME_FMT bothers me, as the "-4" shouldn't be needed. How about this further patch? It shortens asctime.c further (assuming the two asctime.c patches you've already sent). Aside from fixing the STRICTLY_STANDARD_ASCTIME overflow glitch, this patch avoids some byte-scanning and some duplicated format-string contents, and changes "digits" to "bytes" in a few comments referring to contents that might include "-". =================================================================== RCS file: RCS/asctime.c,v retrieving revision 2004.3.1.3 retrieving revision 2004.3.0.5 diff -c -r2004.3.1.3 -r2004.3.0.5 *** asctime.c 2004/10/14 21:34:12 2004.3.1.3 --- asctime.c 2004/10/15 18:53:52 2004.3.0.5 *************** *** 15,44 **** #include "tzfile.h" #if STRICTLY_STANDARD_ASCTIME ! #define ASCTIME_FMT "%.3s %.3s%3d %.2d:%.2d:%.2d %s\n" ! #define ASCTIME_FMT_B ASCTIME_FMT #else /* !STRICTLY_STANDARD_ASCTIME */ /* ** Some systems only handle "%.2d"; others only handle "%02d"; ** "%02.2d" makes (most) everybody happy. ** At least some versions of gcc warn about the %02.2d; ignore the warning. */ /* ** All years associated with 32-bit time_t values are exactly four digits long; ** some years associated with 64-bit time_t values are not. ! ** Vintage programs are coded for years that are always four digits long ** and may assume that the newline always lands in the same place. ! ** For years that are less than four digits, we pad the output with ** leading zeroes to get the newline in the traditional place. ! */ ! #define ASCTIME_FMT "%.3s %.3s%3d %02.2d:%02.2d:%02.2d %-4s\n" ! /* ! ** For years that are more than four digits we put extra spaces before the year ** so that code trying to overwrite the newline won't end up overwriting ** a digit within a year and truncating the year (operating on the assumption ** that no output is better than wrong output). */ ! #define ASCTIME_FMT_B "%.3s %.3s%3d %02.2d:%02.2d:%02.2d %s\n" #endif /* !STRICTLY_STANDARD_ASCTIME */ #define STD_ASCTIME_BUF_SIZE 26 --- 15,44 ---- #include "tzfile.h" #if STRICTLY_STANDARD_ASCTIME ! #define ASCTIME_FMT "%.3s %.3s%3d %.2d:%.2d:%.2d " ! #define SMALL_YEAR_FMT "%d\n" ! #define LARGE_YEAR_TFMT "%Y\n" #else /* !STRICTLY_STANDARD_ASCTIME */ /* ** Some systems only handle "%.2d"; others only handle "%02d"; ** "%02.2d" makes (most) everybody happy. ** At least some versions of gcc warn about the %02.2d; ignore the warning. */ + #define ASCTIME_FMT "%.3s %.3s%3d %02.2d:%02.2d:%02.2d " /* ** All years associated with 32-bit time_t values are exactly four digits long; ** some years associated with 64-bit time_t values are not. ! ** Vintage programs are coded for years that are always four bytes long ** and may assume that the newline always lands in the same place. ! ** For years that are less than four bytes, we pad the output with ** leading zeroes to get the newline in the traditional place. ! ** For years that are more than four bytes we put extra spaces before the year ** so that code trying to overwrite the newline won't end up overwriting ** a digit within a year and truncating the year (operating on the assumption ** that no output is better than wrong output). */ ! #define SMALL_YEAR_FMT "%-4d\n" ! #define LARGE_YEAR_TFMT " %Y\n" #endif /* !STRICTLY_STANDARD_ASCTIME */ #define STD_ASCTIME_BUF_SIZE 26 *************** *** 72,80 **** "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; register const char * wn; register const char * mn; - char year[INT_STRLEN_MAXIMUM(int) + 2]; char result[MAX_ASCTIME_BUF_SIZE]; if (timeptr->tm_wday < 0 || timeptr->tm_wday >= DAYSPERWEEK) --- 72,80 ---- "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; + register char * pt; register const char * wn; register const char * mn; char result[MAX_ASCTIME_BUF_SIZE]; if (timeptr->tm_wday < 0 || timeptr->tm_wday >= DAYSPERWEEK) *************** *** 83,113 **** if (timeptr->tm_mon < 0 || timeptr->tm_mon >= MONSPERYEAR) mn = "???"; else mn = mon_name[timeptr->tm_mon]; - #if STRICTLY_STANDARD_ASCTIME - /* - ** Be strict, potential overflow problems included. - ** In an ideal world, this code is never going to be used. - */ - (void) sprintf(year, "%d", timeptr->tm_year + TM_YEAR_BASE); - #else /* !STRICTLY_STANDARD_ASCTIME */ - /* - ** Use strftime's %Y to generate the year, to avoid overflow problems - ** when computing timeptr->tm_year + TM_YEAR_BASE. - ** Assume that strftime is unaffected by other out-of-range members - ** (e.g., timeptr->tm_mday) when processing "%Y". - */ - (void) strftime(year, sizeof year, "%Y", timeptr); - #endif /* !STRICTLY_STANDARD_ASCTIME */ /* ** We avoid using snprintf since it's not available on all systems. */ ! (void) sprintf(result, ! ((strlen(year) <= 4) ? ASCTIME_FMT : ASCTIME_FMT_B), wn, mn, timeptr->tm_mday, timeptr->tm_hour, ! timeptr->tm_min, timeptr->tm_sec, ! year); ! if (strlen(result) < STD_ASCTIME_BUF_SIZE || buf == buf_asctime) { (void) strcpy(buf, result); return buf; } else { --- 83,111 ---- if (timeptr->tm_mon < 0 || timeptr->tm_mon >= MONSPERYEAR) mn = "???"; else mn = mon_name[timeptr->tm_mon]; /* ** We avoid using snprintf since it's not available on all systems. */ ! pt = result + sprintf(result, ! ASCTIME_FMT, wn, mn, timeptr->tm_mday, timeptr->tm_hour, ! timeptr->tm_min, timeptr->tm_sec); ! if (-999 - TM_YEAR_BASE <= timeptr->tm_year ! && timeptr->tm_year <= 9999 - TM_YEAR_BASE) ! pt += sprintf(pt, ! SMALL_YEAR_FMT, timeptr->tm_year + TM_YEAR_BASE); ! else ! /* ! ** Use strftime's %Y to generate the year, to avoid overflow ! ** problems when computing timeptr->tm_year + TM_YEAR_BASE. ! ** Assume that strftime is unaffected by other out-of-range ! ** members (e.g., timeptr->tm_mday) when processing "%Y". ! */ ! pt += strftime(pt, ! sizeof LARGE_YEAR_TFMT + INT_STRLEN_MAXIMUM(int) - 1, ! LARGE_YEAR_TFMT, timeptr); ! if (pt < result + STD_ASCTIME_BUF_SIZE || buf == buf_asctime) { (void) strcpy(buf, result); return buf; } else {
On Fri, Oct 15, 2004 at 12:02:52PM -0700, Paul Eggert wrote:
! pt = result + sprintf(result, ! ASCTIME_FMT, wn, mn, timeptr->tm_mday, timeptr->tm_hour, ! timeptr->tm_min, timeptr->tm_sec);
I don't know if we care, but some pre-C89 implementations of sprintf() returned a char* (a copy of the first argument, IIRC) rather than the count of bytes written. If portability to such archaic systems is considered to be worth the bother then this code fragment should read: (void)sprintf(result, ASCTIME_FMT, wn, mn, timeptr->tm_mday, timeptr->tm_hour, timeptr->tm_min, timeptr->tm_sec); pt = result + strlen(result); --Ken Pizzini
participants (3)
-
Ken Pizzini -
Olson, Arthur David (NIH/NCI) -
Paul Eggert