diff options
| author | Stefan Farfeleder <stefanf@FreeBSD.org> | 2004-06-14 09:31:29 +0000 |
|---|---|---|
| committer | Stefan Farfeleder <stefanf@FreeBSD.org> | 2004-06-14 09:31:29 +0000 |
| commit | dcd1bc204fd36ed3fc26c4077f8d4d4e97020da0 (patch) | |
| tree | 269ac698a908cc0d64dee4f8a19b576ba3e4195b /lib/libc/stdtime | |
| parent | 7765d84cdec3fcd768518c96fd849673c90a01d4 (diff) | |
Notes
Diffstat (limited to 'lib/libc/stdtime')
| -rw-r--r-- | lib/libc/stdtime/asctime.c | 43 | ||||
| -rw-r--r-- | lib/libc/stdtime/difftime.c | 16 | ||||
| -rw-r--r-- | lib/libc/stdtime/localtime.c | 174 | ||||
| -rw-r--r-- | lib/libc/stdtime/private.h | 72 | ||||
| -rw-r--r-- | lib/libc/stdtime/strftime.c | 226 |
5 files changed, 398 insertions, 133 deletions
diff --git a/lib/libc/stdtime/asctime.c b/lib/libc/stdtime/asctime.c index a1834b65a273..6578663c0d35 100644 --- a/lib/libc/stdtime/asctime.c +++ b/lib/libc/stdtime/asctime.c @@ -1,11 +1,11 @@ /* ** This file is in the public domain, so clarified as of -** June 5, 1996 by Arthur David Olson (arthur_david_olson@nih.gov). +** 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov). */ #ifndef lint #ifndef NOID -static char elsieid[] = "@(#)asctime.c 7.7"; +static char elsieid[] = "@(#)asctime.c 7.9"; #endif /* !defined NOID */ #endif /* !defined lint */ @@ -15,12 +15,13 @@ static char elsieid[] = "@(#)asctime.c 7.7"; #include "tzfile.h" /* -** A la X3J11, with core dump avoidance. +** A la ISO/IEC 9945-1, ANSI/IEEE Std 1003.1, Second Edition, 1996-07-12. */ char * -asctime(timeptr) +asctime_r(timeptr, buf) register const struct tm * timeptr; +char * buf; { static const char wday_name[][3] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" @@ -29,15 +30,6 @@ register const struct tm * timeptr; "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; - /* - ** 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). - */ - static char result[3 * 2 + 5 * INT_STRLEN_MAXIMUM(int) + - 3 + 2 + 1 + 1]; register const char * wn; register const char * mn; @@ -52,10 +44,31 @@ register const struct tm * timeptr; ** "%.3s %.3s%3d %02.2d:%02.2d:%02.2d %d\n" ** Since the .2 in 02.2d is ignored, we drop it. */ - (void) sprintf(result, "%.3s %.3s%3d %02d:%02d:%02d %d\n", + (void) sprintf(buf, "%.3s %.3s%3d %02d:%02d:%02d %d\n", wn, mn, timeptr->tm_mday, timeptr->tm_hour, timeptr->tm_min, timeptr->tm_sec, TM_YEAR_BASE + timeptr->tm_year); - return result; + return buf; +} + +/* +** A la X3J11, with core dump avoidance. +*/ + +char * +asctime(timeptr) +register const struct tm * timeptr; +{ + /* + ** 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). + */ + static char result[3 * 2 + 5 * INT_STRLEN_MAXIMUM(int) + + 3 + 2 + 1 + 1]; + + return asctime_r(timeptr, result); } diff --git a/lib/libc/stdtime/difftime.c b/lib/libc/stdtime/difftime.c index f178524f5152..1d1519e15939 100644 --- a/lib/libc/stdtime/difftime.c +++ b/lib/libc/stdtime/difftime.c @@ -5,7 +5,7 @@ #ifndef lint #ifndef NOID -static char elsieid[] = "@(#)difftime.c 7.7"; +static char elsieid[] = "@(#)difftime.c 7.9"; #endif /* !defined NOID */ #endif /* !defined lint */ @@ -32,10 +32,16 @@ const time_t time0; time_t delta; time_t hibit; - if (sizeof(time_t) < sizeof(double)) - return (double) time1 - (double) time0; - if (sizeof(time_t) < sizeof(long_double)) - return (long_double) time1 - (long_double) time0; + { + time_t tt; + double d; + long_double ld; + + if (sizeof tt < sizeof d) + return (double) time1 - (double) time0; + if (sizeof tt < sizeof ld) + return (long_double) time1 - (long_double) time0; + } if (time1 < time0) return -difftime(time0, time1); /* diff --git a/lib/libc/stdtime/localtime.c b/lib/libc/stdtime/localtime.c index 541f49ffdfe9..33dcd5d052c3 100644 --- a/lib/libc/stdtime/localtime.c +++ b/lib/libc/stdtime/localtime.c @@ -1,11 +1,11 @@ /* ** This file is in the public domain, so clarified as of -** June 5, 1996 by Arthur David Olson (arthur_david_olson@nih.gov). +** 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov). */ #ifndef lint #ifndef NOID -static char elsieid[] = "@(#)localtime.c 7.57"; +static char elsieid[] = "@(#)localtime.c 7.78"; #endif /* !defined NOID */ #endif /* !defined lint */ @@ -59,12 +59,23 @@ static char wildabbr[] = "WILDABBR"; static const char gmt[] = "GMT"; +/* +** The DST rules to use if TZ has no rules and we can't load TZDEFRULES. +** We default to US rules as of 1999-08-17. +** POSIX 1003.1 section 8.1.1 says that the default DST rules are +** implementation dependent; for historical reasons, US rules are a +** common default. +*/ +#ifndef TZDEFRULESTRING +#define TZDEFRULESTRING ",M4.1.0,M10.5.0" +#endif /* !defined TZDEFDST */ + struct ttinfo { /* time type information */ - long tt_gmtoff; /* GMT offset in seconds */ + long tt_gmtoff; /* UTC offset in seconds */ int tt_isdst; /* used to set tm_isdst */ int tt_abbrind; /* abbreviation list index */ int tt_ttisstd; /* TRUE if transition is std time */ - int tt_ttisgmt; /* TRUE if transition is GMT */ + int tt_ttisgmt; /* TRUE if transition is UTC */ }; struct lsinfo { /* leap second information */ @@ -134,6 +145,10 @@ static time_t time2 P((struct tm *tmp, void(*funcp) P((const time_t *, long, struct tm*)), long offset, int * okayp)); +static time_t time2sub P((struct tm *tmp, + void(*funcp) P((const time_t *, + long, struct tm*)), + long offset, int * okayp, int do_norm_secs)); static void timesub P((const time_t * timep, long offset, const struct state * sp, struct tm * tmp)); static int tmcomp P((const struct tm * atmp, @@ -267,7 +282,7 @@ register struct state * const sp; /* ** Section 4.9.1 of the C standard says that ** "FILENAME_MAX expands to an integral constant expression - ** that is the sie needed for an array of char large enough + ** that is the size needed for an array of char large enough ** to hold the longest file name string that the implementation ** guarantees can be opened." */ @@ -298,27 +313,23 @@ register struct state * const sp; } { struct tzhead * tzhp; - char buf[sizeof *sp + sizeof *tzhp]; + union { + struct tzhead tzhead; + char buf[sizeof *sp + sizeof *tzhp]; + } u; int ttisstdcnt; int ttisgmtcnt; - i = read(fid, buf, sizeof buf); + i = read(fid, u.buf, sizeof u.buf); if (close(fid) != 0) return -1; - p = buf; - p += sizeof tzhp->tzh_reserved; - ttisstdcnt = (int) detzcode(p); - p += 4; - ttisgmtcnt = (int) detzcode(p); - p += 4; - sp->leapcnt = (int) detzcode(p); - p += 4; - sp->timecnt = (int) detzcode(p); - p += 4; - sp->typecnt = (int) detzcode(p); - p += 4; - sp->charcnt = (int) detzcode(p); - p += 4; + ttisstdcnt = (int) detzcode(u.tzhead.tzh_ttisstdcnt); + ttisgmtcnt = (int) detzcode(u.tzhead.tzh_ttisgmtcnt); + sp->leapcnt = (int) detzcode(u.tzhead.tzh_leapcnt); + sp->timecnt = (int) detzcode(u.tzhead.tzh_timecnt); + sp->typecnt = (int) detzcode(u.tzhead.tzh_typecnt); + sp->charcnt = (int) detzcode(u.tzhead.tzh_charcnt); + p = u.tzhead.tzh_charcnt + sizeof u.tzhead.tzh_charcnt; if (sp->leapcnt < 0 || sp->leapcnt > TZ_MAX_LEAPS || sp->typecnt <= 0 || sp->typecnt > TZ_MAX_TYPES || sp->timecnt < 0 || sp->timecnt > TZ_MAX_TIMES || @@ -326,7 +337,7 @@ register struct state * const sp; (ttisstdcnt != sp->typecnt && ttisstdcnt != 0) || (ttisgmtcnt != sp->typecnt && ttisgmtcnt != 0)) return -1; - if (i - (p - buf) < sp->timecnt * 4 + /* ats */ + if (i - (p - u.buf) < sp->timecnt * 4 + /* ats */ sp->timecnt + /* types */ sp->typecnt * (4 + 2) + /* ttinfos */ sp->charcnt + /* chars */ @@ -584,8 +595,8 @@ register struct rule * const rulep; } /* -** Given the Epoch-relative time of January 1, 00:00:00 GMT, in a year, the -** year, a rule, and the offset from GMT at the time that rule takes effect, +** Given the Epoch-relative time of January 1, 00:00:00 UTC, in a year, the +** year, a rule, and the offset from UTC at the time that rule takes effect, ** calculate the Epoch-relative time that rule takes effect. */ @@ -671,10 +682,10 @@ const long offset; } /* - ** "value" is the Epoch-relative time of 00:00:00 GMT on the day in + ** "value" is the Epoch-relative time of 00:00:00 UTC on the day in ** question. To get the Epoch-relative time of the specified local ** time on that day, add the transition time and the current offset - ** from GMT. + ** from UTC. */ return value + rulep->r_time + offset; } @@ -708,15 +719,14 @@ const int lastditch; name += stdlen; if (stdlen >= sizeof sp->chars) stdlen = (sizeof sp->chars) - 1; + stdoffset = 0; } else { name = getzname(name); stdlen = name - stdname; if (stdlen < 3) return -1; - } - if (*name == '\0') - return -1; /* was "stdoffset = 0;" */ - else { + if (*name == '\0') + return -1; name = getoffset(name, &stdoffset); if (name == NULL) return -1; @@ -735,6 +745,8 @@ const int lastditch; if (name == NULL) return -1; } else dstoffset = stdoffset - SECSPERHOUR; + if (*name == '\0' && load_result != 0) + name = TZDEFRULESTRING; if (*name == ',' || *name == ';') { struct rule start; struct rule end; @@ -797,8 +809,6 @@ const int lastditch; if (*name != '\0') return -1; - if (load_result != 0) - return -1; /* ** Initial values of theirstdoffset and theirdstoffset. */ @@ -872,6 +882,7 @@ const int lastditch; sp->ttis[1].tt_gmtoff = -dstoffset; sp->ttis[1].tt_isdst = TRUE; sp->ttis[1].tt_abbrind = stdlen + 1; + sp->typecnt = 2; } } else { dstlen = 0; @@ -884,7 +895,7 @@ const int lastditch; sp->charcnt = stdlen + 1; if (dstlen != 0) sp->charcnt += dstlen + 1; - if (sp->charcnt > sizeof sp->chars) + if ((size_t) sp->charcnt > sizeof sp->chars) return -1; cp = sp->chars; (void) strncpy(cp, stdname, stdlen); @@ -944,9 +955,9 @@ tzset P((void)) return; } - if (lcl_is_set > 0 && strcmp(lcl_TZname, name) == 0) + if (lcl_is_set > 0 && strcmp(lcl_TZname, name) == 0) return; - lcl_is_set = (strlen(name) < sizeof(lcl_TZname)); + lcl_is_set = strlen(name) < sizeof lcl_TZname; if (lcl_is_set) (void) strcpy(lcl_TZname, name); @@ -965,6 +976,8 @@ tzset P((void)) */ lclptr->leapcnt = 0; /* so, we're off a little */ lclptr->timecnt = 0; + lclptr->typecnt = 0; + lclptr->ttis[0].tt_isdst = 0; lclptr->ttis[0].tt_gmtoff = 0; lclptr->ttis[0].tt_abbrind = 0; (void) strcpy(lclptr->chars, gmt); @@ -1040,6 +1053,19 @@ const time_t * const timep; } /* +** Re-entrant version of localtime. +*/ + +struct tm * +localtime_r(timep, tm) +const time_t * const timep; +struct tm * tm; +{ + localsub(timep, 0L, tm); + return tm; +} + +/* ** gmtsub is to gmtime as localsub is to localtime. */ @@ -1061,7 +1087,7 @@ struct tm * const tmp; #ifdef TM_ZONE /* ** Could get fancy here and deliver something such as - ** "GMT+xxxx" or "GMT-xxxx" if offset is non-zero, + ** "UTC+xxxx" or "UTC-xxxx" if offset is non-zero, ** but this is no time for a treasure hunt. */ if (offset != 0) @@ -1087,6 +1113,19 @@ const time_t * const timep; return &tm; } +/* +* Re-entrant version of gmtime. +*/ + +struct tm * +gmtime_r(timep, tm) +const time_t * const timep; +struct tm * tm; +{ + gmtsub(timep, 0L, tm); + return tm; +} + #ifdef STD_INSPIRED struct tm * @@ -1207,13 +1246,23 @@ const time_t * const timep; { /* ** Section 4.12.3.2 of X3.159-1989 requires that -** The ctime funciton converts the calendar time pointed to by timer +** The ctime function converts the calendar time pointed to by timer ** to local time in the form of a string. It is equivalent to ** asctime(localtime(timer)) */ return asctime(localtime(timep)); } +char * +ctime_r(timep, buf) +const time_t * const timep; +char * buf; +{ + struct tm tm; + + return asctime_r(localtime_r(timep, &tm), buf); +} + /* ** Adapted from code provided by Robert Elz, who writes: ** The "best" way to do mktime I think is based on an idea of Bob @@ -1276,11 +1325,12 @@ register const struct tm * const btmp; } static time_t -time2(tmp, funcp, offset, okayp) +time2sub(tmp, funcp, offset, okayp, do_norm_secs) struct tm * const tmp; void (* const funcp) P((const time_t*, long, struct tm*)); const long offset; int * const okayp; +const int do_norm_secs; { register const struct state * sp; register int dir; @@ -1293,6 +1343,11 @@ int * const okayp; *okayp = FALSE; yourtm = *tmp; + if (do_norm_secs) { + if (normalize_overflow(&yourtm.tm_min, &yourtm.tm_sec, + SECSPERMIN)) + return WRONG; + } if (normalize_overflow(&yourtm.tm_hour, &yourtm.tm_min, MINSPERHOUR)) return WRONG; if (normalize_overflow(&yourtm.tm_mday, &yourtm.tm_hour, HOURSPERDAY)) @@ -1330,7 +1385,9 @@ int * const okayp; } if (increment_overflow(&yourtm.tm_year, -TM_YEAR_BASE)) return WRONG; - if (yourtm.tm_year + TM_YEAR_BASE < EPOCH_YEAR) { + if (yourtm.tm_sec >= 0 && yourtm.tm_sec < SECSPERMIN) + saved_seconds = 0; + else if (yourtm.tm_year + TM_YEAR_BASE < EPOCH_YEAR) { /* ** We can't set tm_sec to 0, because that might push the ** time below the minimum representable time. @@ -1422,6 +1479,24 @@ label: } static time_t +time2(tmp, funcp, offset, okayp) +struct tm * const tmp; +void (* const funcp) P((const time_t*, long, struct tm*)); +const long offset; +int * const okayp; +{ + time_t t; + + /* + ** First try without normalization of seconds + ** (in case tm_sec contains a value associated with a leap second). + ** If that fails, try with normalization of seconds. + */ + t = time2sub(tmp, funcp, offset, okayp, FALSE); + return *okayp ? t : time2sub(tmp, funcp, offset, okayp, TRUE); +} + +static time_t time1(tmp, funcp, offset) struct tm * const tmp; void (* const funcp) P((const time_t *, long, struct tm *)); @@ -1430,6 +1505,11 @@ const long offset; register time_t t; register const struct state * sp; register int samei, otheri; + register int sameind, otherind; + register int i; + register int nseen; + int seen[TZ_MAX_TYPES]; + int types[TZ_MAX_TYPES]; int okay; if (tmp->tm_isdst > 1) @@ -1463,10 +1543,20 @@ const long offset; if (sp == NULL) return WRONG; #endif /* defined ALL_STATE */ - for (samei = sp->typecnt - 1; samei >= 0; --samei) { + for (i = 0; i < sp->typecnt; ++i) + seen[i] = FALSE; + nseen = 0; + for (i = sp->timecnt - 1; i >= 0; --i) + if (!seen[sp->types[i]]) { + seen[sp->types[i]] = TRUE; + types[nseen++] = sp->types[i]; + } + for (sameind = 0; sameind < nseen; ++sameind) { + samei = types[sameind]; if (sp->ttis[samei].tt_isdst != tmp->tm_isdst) continue; - for (otheri = sp->typecnt - 1; otheri >= 0; --otheri) { + for (otherind = 0; otherind < nseen; ++otherind) { + otheri = types[otherind]; if (sp->ttis[otheri].tt_isdst == tmp->tm_isdst) continue; tmp->tm_sec += sp->ttis[otheri].tt_gmtoff - @@ -1548,7 +1638,7 @@ struct tm * const tmp; /* ** IEEE Std 1003.1-1988 (POSIX) legislates that 536457599 -** shall correspond to "Wed Dec 31 23:59:59 GMT 1986", which +** shall correspond to "Wed Dec 31 23:59:59 UTC 1986", which ** is not the case if we are accounting for leap seconds. ** So, we provide the following conversion routines for use ** when exchanging timestamps with POSIX conforming systems. diff --git a/lib/libc/stdtime/private.h b/lib/libc/stdtime/private.h index f81bf4867bdb..c8f45486837f 100644 --- a/lib/libc/stdtime/private.h +++ b/lib/libc/stdtime/private.h @@ -4,7 +4,7 @@ /* ** This file is in the public domain, so clarified as of -** June 5, 1996 by Arthur David Olson (arthur_david_olson@nih.gov). +** 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov). */ /* @@ -21,7 +21,7 @@ #ifndef lint #ifndef NOID -static char privatehid[] = "@(#)private.h 7.43"; +static char privatehid[] = "@(#)private.h 7.53"; #endif /* !defined NOID */ #endif /* !defined lint */ @@ -38,14 +38,30 @@ static char privatehid[] = "@(#)private.h 7.43"; #define HAVE_GETTEXT 0 #endif /* !defined HAVE_GETTEXT */ +#ifndef HAVE_INCOMPATIBLE_CTIME_R +#define HAVE_INCOMPATIBLE_CTIME_R 0 +#endif /* !defined INCOMPATIBLE_CTIME_R */ + #ifndef HAVE_SETTIMEOFDAY #define HAVE_SETTIMEOFDAY 3 #endif /* !defined HAVE_SETTIMEOFDAY */ #ifndef HAVE_STRERROR -#define HAVE_STRERROR 0 +#define HAVE_STRERROR 1 #endif /* !defined HAVE_STRERROR */ +#ifndef HAVE_SYMLINK +#define HAVE_SYMLINK 1 +#endif /* !defined HAVE_SYMLINK */ + +#ifndef HAVE_SYS_STAT_H +#define HAVE_SYS_STAT_H 1 +#endif /* !defined HAVE_SYS_STAT_H */ + +#ifndef HAVE_SYS_WAIT_H +#define HAVE_SYS_WAIT_H 1 +#endif /* !defined HAVE_SYS_WAIT_H */ + #ifndef HAVE_UNISTD_H #define HAVE_UNISTD_H 1 #endif /* !defined HAVE_UNISTD_H */ @@ -58,6 +74,11 @@ static char privatehid[] = "@(#)private.h 7.43"; #define LOCALE_HOME "/usr/lib/locale" #endif /* !defined LOCALE_HOME */ +#if HAVE_INCOMPATIBLE_CTIME_R +#define asctime_r _incompatible_asctime_r +#define ctime_r _incompatible_ctime_r +#endif /* HAVE_INCOMPATIBLE_CTIME_R */ + /* ** Nested includes */ @@ -74,6 +95,17 @@ static char privatehid[] = "@(#)private.h 7.43"; #include "libintl.h" #endif /* HAVE_GETTEXT - 0 */ +#if HAVE_SYS_WAIT_H - 0 +#include <sys/wait.h> /* for WIFEXITED and WEXITSTATUS */ +#endif /* HAVE_SYS_WAIT_H - 0 */ + +#ifndef WIFEXITED +#define WIFEXITED(status) (((status) & 0xff) == 0) +#endif /* !defined WIFEXITED */ +#ifndef WEXITSTATUS +#define WEXITSTATUS(status) (((status) >> 8) & 0xff) +#endif /* !defined WEXITSTATUS */ + #if HAVE_UNISTD_H - 0 #include "unistd.h" /* for F_OK and R_OK */ #endif /* HAVE_UNISTD_H - 0 */ @@ -95,16 +127,6 @@ static char privatehid[] = "@(#)private.h 7.43"; */ /* -** SunOS 4.1.1 cc lacks const. -*/ - -#ifndef const -#ifndef __STDC__ -#define const -#endif /* !defined __STDC__ */ -#endif /* !defined const */ - -/* ** SunOS 4.1.1 cc lacks prototypes. */ @@ -173,6 +195,19 @@ extern int errno; #endif /* !defined errno */ /* +** Private function declarations. +*/ +char * icalloc P((int nelem, int elsize)); +char * icatalloc P((char * old, const char * new)); +char * icpyalloc P((const char * string)); +char * imalloc P((int n)); +void * irealloc P((void * pointer, int size)); +void icfree P((char * pointer)); +void ifree P((char * pointer)); +char * scheck P((const char *string, const char *format)); + + +/* ** Finally, some convenience items. */ @@ -200,7 +235,7 @@ extern int errno; ** add one more for a minus sign if the type is signed. */ #define INT_STRLEN_MAXIMUM(type) \ - ((TYPE_BIT(type) - TYPE_SIGNED(type)) * 302 / 100 + 1 + TYPE_SIGNED(type)) + ((TYPE_BIT(type) - TYPE_SIGNED(type)) * 302 / 1000 + 1 + TYPE_SIGNED(type)) #endif /* !defined INT_STRLEN_MAXIMUM */ /* @@ -245,8 +280,15 @@ extern int errno; #define TZ_DOMAIN "tz" #endif /* !defined TZ_DOMAIN */ +#if HAVE_INCOMPATIBLE_CTIME_R +#undef asctime_r +#undef ctime_r +char *asctime_r P((struct tm const *, char *)); +char *ctime_r P((time_t const *, char *)); +#endif /* HAVE_INCOMPATIBLE_CTIME_R */ + /* -** UNIX was a registered trademark of UNIX System Laboratories in 1993. +** UNIX was a registered trademark of The Open Group in 2003. */ #endif /* !defined PRIVATE_H */ diff --git a/lib/libc/stdtime/strftime.c b/lib/libc/stdtime/strftime.c index a5ba85c90473..365a42e12b88 100644 --- a/lib/libc/stdtime/strftime.c +++ b/lib/libc/stdtime/strftime.c @@ -1,6 +1,6 @@ #ifndef lint #ifndef NOID -static char elsieid[] = "@(#)strftime.c 7.47"; +static char elsieid[] = "@(#)strftime.c 7.64"; /* ** Based on the UCB version with the ID appearing below. ** This is ANSIish only when "multibyte character == plain character". @@ -38,10 +38,10 @@ static const char sccsid[] = "@(#)strftime.c 5.4 (Berkeley) 3/14/89"; #include "locale.h" struct lc_time_T { - const char * mon[12]; - const char * month[12]; - const char * wday[7]; - const char * weekday[7]; + const char * mon[MONSPERYEAR]; + const char * month[MONSPERYEAR]; + const char * wday[DAYSPERWEEK]; + const char * weekday[DAYSPERWEEK]; const char * X_fmt; const char * x_fmt; const char * c_fmt; @@ -80,8 +80,7 @@ static const struct lc_time_T C_time_locale = { /* ** x_fmt - ** Since the C language standard calls for - ** "date, using locale's date format," anything goes. + ** C99 requires this format. ** Using just numbers (as here) makes Quakers happier; ** it's also compatible with SVR4. */ @@ -89,11 +88,13 @@ static const struct lc_time_T C_time_locale = { /* ** c_fmt + ** C99 requires this format. + ** Previously this code used "%D %X", but we now conform to C99. ** Note that - ** "%a %b %d %H:%M:%S %Y" + ** "%a %b %d %H:%M:%S %Y" ** is used by Solaris 2.3. */ - "%D %X", /* %m/%d/%y %H:%M:%S */ + "%a %b %e %T %Y", /* am */ "AM", @@ -107,12 +108,22 @@ static const struct lc_time_T C_time_locale = { static char * _add P((const char *, char *, const char *)); static char * _conv P((int, const char *, char *, const char *)); -static char * _fmt P((const char *, const struct tm *, char *, const char *)); +static char * _fmt P((const char *, const struct tm *, char *, const char *, int *)); size_t strftime P((char *, size_t, const char *, const struct tm *)); extern char * tzname[]; +#ifndef YEAR_2000_NAME +#define YEAR_2000_NAME "CHECK_STRFTIME_FORMATS_FOR_TWO_DIGIT_YEARS" +#endif /* !defined YEAR_2000_NAME */ + + +#define IN_NONE 0 +#define IN_SOME 1 +#define IN_THIS 2 +#define IN_ALL 3 + size_t strftime(s, maxsize, format, t) char * const s; @@ -121,12 +132,30 @@ const char * const format; const struct tm * const t; { char * p; + int warn; tzset(); #ifdef LOCALE_HOME localebuf.mon[0] = 0; #endif /* defined LOCALE_HOME */ - p = _fmt(((format == NULL) ? "%c" : format), t, s, s + maxsize); + warn = IN_NONE; + p = _fmt(((format == NULL) ? "%c" : format), t, s, s + maxsize, &warn); +#ifndef NO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU + if (warn != IN_NONE && getenv(YEAR_2000_NAME) != NULL) { + (void) fprintf(stderr, "\n"); + if (format == NULL) + (void) fprintf(stderr, "NULL strftime format "); + else (void) fprintf(stderr, "strftime format \"%s\" ", + format); + (void) fprintf(stderr, "yields only two digits of years in "); + if (warn == IN_SOME) + (void) fprintf(stderr, "some locales"); + else if (warn == IN_THIS) + (void) fprintf(stderr, "the current locale"); + else (void) fprintf(stderr, "all locales"); + (void) fprintf(stderr, "\n"); + } +#endif /* !defined NO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU */ if (p == s + maxsize) return 0; *p = '\0'; @@ -134,11 +163,12 @@ const struct tm * const t; } static char * -_fmt(format, t, pt, ptlim) +_fmt(format, t, pt, ptlim, warnp) const char * format; const struct tm * const t; char * pt; const char * const ptlim; +int * warnp; { for ( ; *format; ++format) { if (*format == '%') { @@ -148,23 +178,27 @@ label: --format; break; case 'A': - pt = _add((t->tm_wday < 0 || t->tm_wday > 6) ? + pt = _add((t->tm_wday < 0 || + t->tm_wday >= DAYSPERWEEK) ? "?" : Locale->weekday[t->tm_wday], pt, ptlim); continue; case 'a': - pt = _add((t->tm_wday < 0 || t->tm_wday > 6) ? + pt = _add((t->tm_wday < 0 || + t->tm_wday >= DAYSPERWEEK) ? "?" : Locale->wday[t->tm_wday], pt, ptlim); continue; case 'B': - pt = _add((t->tm_mon < 0 || t->tm_mon > 11) ? + pt = _add((t->tm_mon < 0 || + t->tm_mon >= MONSPERYEAR) ? "?" : Locale->month[t->tm_mon], pt, ptlim); continue; case 'b': case 'h': - pt = _add((t->tm_mon < 0 || t->tm_mon > 11) ? + pt = _add((t->tm_mon < 0 || + t->tm_mon >= MONSPERYEAR) ? "?" : Locale->mon[t->tm_mon], pt, ptlim); continue; @@ -174,16 +208,24 @@ label: ** _fmt("%a %b %e %X %Y", t); ** ...whereas now POSIX 1003.2 calls for ** something completely different. - ** (ado, 5/24/93) + ** (ado, 1993-05-24) */ pt = _conv((t->tm_year + TM_YEAR_BASE) / 100, "%02d", pt, ptlim); continue; case 'c': - pt = _fmt(Locale->c_fmt, t, pt, ptlim); + { + int warn2 = IN_SOME; + + pt = _fmt(Locale->c_fmt, t, pt, ptlim, warnp); + if (warn2 == IN_ALL) + warn2 = IN_THIS; + if (warn2 > *warnp) + *warnp = warn2; + } continue; case 'D': - pt = _fmt("%m/%d/%y", t, pt, ptlim); + pt = _fmt("%m/%d/%y", t, pt, ptlim, warnp); continue; case 'd': pt = _conv(t->tm_mday, "%02d", pt, ptlim); @@ -191,20 +233,21 @@ label: case 'E': case 'O': /* - ** POSIX locale extensions, a la - ** Arnold Robbins' strftime version 3.0. + ** C99 locale modifiers. ** The sequences - ** %Ec %EC %Ex %Ey %EY + ** %Ec %EC %Ex %EX %Ey %EY ** %Od %oe %OH %OI %Om %OM ** %OS %Ou %OU %OV %Ow %OW %Oy ** are supposed to provide alternate ** representations. - ** (ado, 5/24/93) */ goto label; case 'e': pt = _conv(t->tm_mday, "%2d", pt, ptlim); continue; + case 'F': + pt = _fmt("%Y-%m-%d", t, pt, ptlim, warnp); + continue; case 'H': pt = _conv(t->tm_hour, "%02d", pt, ptlim); continue; @@ -225,7 +268,7 @@ label: ** match SunOS 4.1.1 and Arnold Robbins' ** strftime version 3.0. That is, "%k" and ** "%l" have been swapped. - ** (ado, 5/24/93) + ** (ado, 1993-05-24) */ pt = _conv(t->tm_hour, "%2d", pt, ptlim); continue; @@ -245,7 +288,7 @@ label: ** match SunOS 4.1.1 and Arnold Robbin's ** strftime version 3.0. That is, "%k" and ** "%l" have been swapped. - ** (ado, 5/24/93) + ** (ado, 1993-05-24) */ pt = _conv((t->tm_hour % 12) ? (t->tm_hour % 12) : 12, @@ -261,16 +304,16 @@ label: pt = _add("\n", pt, ptlim); continue; case 'p': - pt = _add((t->tm_hour >= 12) ? + pt = _add((t->tm_hour >= (HOURSPERDAY / 2)) ? Locale->pm : Locale->am, pt, ptlim); continue; case 'R': - pt = _fmt("%H:%M", t, pt, ptlim); + pt = _fmt("%H:%M", t, pt, ptlim, warnp); continue; case 'r': - pt = _fmt("%I:%M:%S %p", t, pt, ptlim); + pt = _fmt("%I:%M:%S %p", t, pt, ptlim, warnp); continue; case 'S': pt = _conv(t->tm_sec, "%02d", pt, ptlim); @@ -293,13 +336,14 @@ label: } continue; case 'T': - pt = _fmt("%H:%M:%S", t, pt, ptlim); + pt = _fmt("%H:%M:%S", t, pt, ptlim, warnp); continue; case 't': pt = _add("\t", pt, ptlim); continue; case 'U': - pt = _conv((t->tm_yday + 7 - t->tm_wday) / 7, + pt = _conv((t->tm_yday + DAYSPERWEEK - + t->tm_wday) / DAYSPERWEEK, "%02d", pt, ptlim); continue; case 'u': @@ -307,9 +351,10 @@ label: ** From Arnold Robbins' strftime version 3.0: ** "ISO 8601: Weekday as a decimal number ** [1 (Monday) - 7]" - ** (ado, 5/24/93) + ** (ado, 1993-05-24) */ - pt = _conv((t->tm_wday == 0) ? 7 : t->tm_wday, + pt = _conv((t->tm_wday == 0) ? + DAYSPERWEEK : t->tm_wday, "%d", pt, ptlim); continue; case 'V': /* ISO 8601 week number */ @@ -381,16 +426,20 @@ label: DAYSPERNYEAR; } #ifdef XPG4_1994_04_09 - if (w == 52 && t->tm_mon == TM_JANUARY) + if ((w == 52 + && t->tm_mon == TM_JANUARY) + || (w == 1 + && t->tm_mon == TM_DECEMBER)) w = 53; #endif /* defined XPG4_1994_04_09 */ if (*format == 'V') pt = _conv(w, "%02d", pt, ptlim); - else if (*format == 'G') - pt = _conv(year, "%02d", + else if (*format == 'g') { + *warnp = IN_ALL; + pt = _conv(year % 100, "%02d", pt, ptlim); - else pt = _conv(year, "%04d", + } else pt = _conv(year, "%04d", pt, ptlim); } continue; @@ -398,26 +447,36 @@ label: /* ** From Arnold Robbins' strftime version 3.0: ** "date as dd-bbb-YYYY" - ** (ado, 5/24/93) + ** (ado, 1993-05-24) */ - pt = _fmt("%e-%b-%Y", t, pt, ptlim); + pt = _fmt("%e-%b-%Y", t, pt, ptlim, warnp); continue; case 'W': - pt = _conv((t->tm_yday + 7 - + pt = _conv((t->tm_yday + DAYSPERWEEK - (t->tm_wday ? - (t->tm_wday - 1) : 6)) / 7, + (t->tm_wday - 1) : + (DAYSPERWEEK - 1))) / DAYSPERWEEK, "%02d", pt, ptlim); continue; case 'w': pt = _conv(t->tm_wday, "%d", pt, ptlim); continue; case 'X': - pt = _fmt(Locale->X_fmt, t, pt, ptlim); + pt = _fmt(Locale->X_fmt, t, pt, ptlim, warnp); continue; case 'x': - pt = _fmt(Locale->x_fmt, t, pt, ptlim); + { + int warn2 = IN_SOME; + + pt = _fmt(Locale->x_fmt, t, pt, ptlim, &warn2); + if (warn2 == IN_ALL) + warn2 = IN_THIS; + if (warn2 > *warnp) + *warnp = warn2; + } continue; case 'y': + *warnp = IN_ALL; pt = _conv((t->tm_year + TM_YEAR_BASE) % 100, "%02d", pt, ptlim); continue; @@ -431,20 +490,77 @@ label: pt = _add(t->TM_ZONE, pt, ptlim); else #endif /* defined TM_ZONE */ - if (t->tm_isdst == 0 || t->tm_isdst == 1) { - pt = _add(tzname[t->tm_isdst], + if (t->tm_isdst >= 0) + pt = _add(tzname[t->tm_isdst != 0], pt, ptlim); - } else pt = _add("?", pt, ptlim); + /* + ** C99 says that %Z must be replaced by the + ** empty string if the time zone is not + ** determinable. + */ + continue; + case 'z': + { + int diff; + char const * sign; + + if (t->tm_isdst < 0) + continue; +#ifdef TM_GMTOFF + diff = t->TM_GMTOFF; +#else /* !defined TM_GMTOFF */ + /* + ** C99 says that the UTC offset must + ** be computed by looking only at + ** tm_isdst. This requirement is + ** incorrect, since it means the code + ** must rely on magic (in this case + ** altzone and timezone), and the + ** magic might not have the correct + ** offset. Doing things correctly is + ** tricky and requires disobeying C99; + ** see GNU C strftime for details. + ** For now, punt and conform to the + ** standard, even though it's incorrect. + ** + ** C99 says that %z must be replaced by the + ** empty string if the time zone is not + ** determinable, so output nothing if the + ** appropriate variables are not available. + */ + if (t->tm_isdst == 0) +#ifdef USG_COMPAT + diff = -timezone; +#else /* !defined USG_COMPAT */ + continue; +#endif /* !defined USG_COMPAT */ + else +#ifdef ALTZONE + diff = -altzone; +#else /* !defined ALTZONE */ + continue; +#endif /* !defined ALTZONE */ +#endif /* !defined TM_GMTOFF */ + if (diff < 0) { + sign = "-"; + diff = -diff; + } else sign = "+"; + pt = _add(sign, pt, ptlim); + diff /= 60; + pt = _conv((diff/60)*100 + diff%60, + "%04d", pt, ptlim); + } continue; case '+': - pt = _fmt(Locale->date_fmt, t, pt, ptlim); + pt = _fmt(Locale->date_fmt, t, pt, ptlim, + warnp); continue; case '%': /* - * X311J/88-090 (4.12.3.5): if conversion char is - * undefined, behavior is undefined. Print out the - * character itself as printf(3) also does. - */ + ** X311J/88-090 (4.12.3.5): if conversion char is + ** undefined, behavior is undefined. Print out the + ** character itself as printf(3) also does. + */ default: break; } @@ -487,7 +603,6 @@ _loc P((void)) static const char locale_home[] = LOCALE_HOME; static const char lc_time[] = "LC_TIME"; static char * locale_buf; - static char locale_buf_C[] = "C"; int fd; int oldsun; /* "...ain't got nothin' to do..." */ @@ -525,8 +640,8 @@ _loc P((void)) ** Slurp the locale file into the cache. */ namesize = strlen(name) + 1; - if (sizeof(filename) < - sizeof(locale_home) + namesize + sizeof(lc_time)) + if (sizeof filename < + ((sizeof locale_home) + namesize + (sizeof lc_time))) goto no_locale; oldsun = 0; (void) sprintf(filename, "%s/%s/%s", locale_home, name, lc_time); @@ -548,8 +663,7 @@ _loc P((void)) goto bad_locale; bufsize = namesize + st.st_size; locale_buf = NULL; - lbuf = (lbuf == NULL || lbuf == locale_buf_C) ? - malloc(bufsize) : realloc(lbuf, bufsize); + lbuf = (lbuf == NULL) ? malloc(bufsize) : realloc(lbuf, bufsize); if (lbuf == NULL) goto bad_locale; (void) strcpy(lbuf, name); @@ -598,7 +712,7 @@ bad_locale: (void) close(fd); no_locale: localebuf = C_time_locale; - locale_buf = locale_buf_C; + locale_buf = NULL; return &localebuf; } #endif /* defined LOCALE_HOME */ |
