"Olson, Arthur David (NIH/NCI)" <olsona@dc37a.nci.nih.gov> writes:
use... %4ld for years to avoid problems if a year isn't four digits long
That "%4ld" doesn't conform to the C standard, which says that leading zeros must not be printed for years. So the following program: #include <time.h> #include <stdio.h> int main (void) { struct tm tm; tm.tm_year = 999 - 1900; tm.tm_mon = 12 - 1; tm.tm_mday = 1; tm.tm_wday = 1; tm.tm_hour = tm.tm_min = tm.tm_sec = 0; puts (asctime (&tm)); return 0; } which is strictly conforming even on 32-bit time_t hosts, must print "Mon Dec 1 00:00:00 999", without a leading zero before the 999. Also, that proposal still assumes that EOVERFLOW is defined by <errno.h>. (Perhaps you're defining it in private.h if it's not already defined? That would explain this.) Finally, there's still a regression in that asctime now sometimes return NULL when it used to return a valid string. It's fairly common for programs to assume that asctime always succeeds. zdump.c itself is one such program (I've submitted patches for that but we're working on asctime first). Admittedly such programs are unportable, but I don't see why we should have them dump core when it's easy to have them succeed and return a valid string. Anyway, here's a proposal that incorporates your changes to avoid snprintf entirely, along with comments along the lines that I suggested in my earlier message today, and a few other comments about the regression noted above. /* ** This file is in the public domain, so clarified as of ** 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov). */ #ifndef lint #ifndef NOID static char elsieid[] = "@(#)asctime.c 7.15"; #endif /* !defined NOID */ #endif /* !defined lint */ /*LINTLIBRARY*/ #include "private.h" #include "tzfile.h" #define STANDARD_BUFFER_SIZE 26 #ifndef EOVERFLOW # define EOVERFLOW EINVAL #endif /* ** A la ISO/IEC 9945-1, ANSI/IEEE Std 1003.1, 2004 Edition. */ /* ** Big enough for something such as ** ??? ???-2147483648 -2147483648:-2147483648:-2147483648 -2147483648\n ** (two three-character abbreviations, five strings denoting integers, ** three explicit spaces, two explicit colons, a newline, ** and a trailing ASCII nul). The above example assumes 32-bit int, ** but the same idea applies to all int widths. ** ** The year is printed as a long int, but it can't possibly take more ** digits than the number of digits printed in an int, because it is ** the sum of 1900 and an int value. Even if the sum overflows past ** INT_MAX, it will add at most one digit to the print width, and that ** extra byte is already accounted for by the width of INT_MIN (which ** has a leading minus sign). */ #define MAX_ASCTIME_SIZE (2 * 3 + 5 * INT_STRLEN_MAXIMUM(int) + 3 + 2 + 1 + 1) static char * asctime_rn(timeptr, buf, size) register const struct tm * timeptr; char * buf; size_t size; { static const char wday_name[][3] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; static const char mon_name[][3] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; register const char * wn; register const char * mn; char result[MAX_ASCTIME_SIZE]; if (timeptr->tm_wday < 0 || timeptr->tm_wday >= DAYSPERWEEK) wn = "???"; else wn = wday_name[timeptr->tm_wday]; if (timeptr->tm_mon < 0 || timeptr->tm_mon >= MONSPERYEAR) mn = "???"; else mn = mon_name[timeptr->tm_mon]; /* ** The format used in the (2004) standard is ** "%.3s %.3s%3d %.2d:%.2d:%.2d %d\n" ** Some systems only handle "%.2d"; others only handle "%02d"; ** "%02.2d" makes (most) everybody happy. */ /* ** We avoid using snprintf since it's not available on all systems. */ (void) sprintf(result, "%.3s %.3s%3d %02.2d:%02.2d:%02.2d %ld\n", wn, mn, timeptr->tm_mday, timeptr->tm_hour, timeptr->tm_min, timeptr->tm_sec, timeptr->tm_year + (long) TM_YEAR_BASE); if (strlen(result) >= size) { errno = EOVERFLOW; return NULL; } else { (void) strcpy(buf, result); return buf; } } char * asctime_r(timeptr, buf) register const struct tm * timeptr; char * buf; { return asctime_rn(timeptr, buf, STANDARD_BUFFER_SIZE); } /* ** A la ISO/IEC 9945-1, ANSI/IEEE Std 1003.1, 2004 Edition, ** with core dump avoidance. */ char * asctime(timeptr) register const struct tm * timeptr; { /* ** The standard requires only STANDARD_BUFFER_SIZE bytes in ** this static buffer. However, make it longer so that ** asctime never returns a null pointer. This supports the ** many (admittedly unportable) programs that assume that ** asctime never fails. */ static char result[MAX_ASCTIME_SIZE]; return asctime_rn(timeptr, result, sizeof result); }