diff options
Diffstat (limited to 'libc/misc')
-rw-r--r-- | libc/misc/time/time.c | 231 |
1 files changed, 145 insertions, 86 deletions
diff --git a/libc/misc/time/time.c b/libc/misc/time/time.c index 8176e071a..ac2fe5926 100644 --- a/libc/misc/time/time.c +++ b/libc/misc/time/time.c @@ -1,31 +1,10 @@ -/* Copyright (C) 2002 Manuel Novoa III +/* Copyright (C) 2002-2004 Manuel Novoa III <mjn3@codepoet.org> * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * GNU Library General Public License (LGPL) version 2 or later. * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the Free - * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. */ -/* ATTENTION! ATTENTION! ATTENTION! ATTENTION! ATTENTION! - * - * Besides uClibc, I'm using this code in my libc for elks, which is - * a 16-bit environment with a fairly limited compiler. It would make - * things much easier for me if this file isn't modified unnecessarily. - * In particular, please put any new or replacement functions somewhere - * else, and modify the makefile to use your version instead. - * Thanks. Manuel - * - * ATTENTION! ATTENTION! ATTENTION! ATTENTION! ATTENTION! */ - /* June 15, 2002 Initial Notes: * * Note: It is assumed throught that time_t is either long or unsigned long. @@ -132,6 +111,13 @@ * Dec 14, 2003 Fix some dst issues in _time_mktime(). * Normalize the tm_isdst value to -1, 0, or 1. * If no dst for this timezone, then reset tm_isdst to 0. + * + * May 7, 2004 + * Change clock() to allow wrapping. + * Add timegm() function. + * Make lookup_tzname() static (as it should have been). + * Have strftime() get timezone information from the passed struct + * for the %z and %Z conversions when using struct tm extensions. */ #define _GNU_SOURCE @@ -219,6 +205,13 @@ extern struct tm *_time_t2tm(const time_t *__restrict timer, extern time_t _time_mktime(struct tm *timeptr, int store_on_success); +extern struct tm *__time_localtime_tzi(const time_t *__restrict timer, + struct tm *__restrict result, + rule_struct *tzi); + +extern time_t _time_mktime_tzi(struct tm *timeptr, int store_on_success, + rule_struct *tzi); + /**********************************************************************/ #ifdef L_asctime @@ -376,51 +369,63 @@ char *asctime_r(register const struct tm *__restrict ptm, #include <sys/times.h> -/* Note: According to glibc... - * CAE XSH, Issue 4, Version 2: <time.h> - * The value of CLOCKS_PER_SEC is required to be 1 million on all - * XSI-conformant systems. - */ - #ifndef __BCC__ #if CLOCKS_PER_SEC != 1000000L #error unexpected value for CLOCKS_PER_SEC! #endif #endif +#ifdef __UCLIBC_CLK_TCK_CONST +# if __UCLIBC_CLK_TCK_CONST > CLOCKS_PER_SEC +# error __UCLIBC_CLK_TCK_CONST > CLOCKS_PER_SEC! +# elif __UCLIBC_CLK_TCK_CONST < 1 +# error __UCLIBC_CLK_TCK_CONST < 1! +# endif +#endif + +/* Note: SUSv3 notes + * + * On XSI-conformant systems, CLOCKS_PER_SEC is defined to be one million. + * + * The value returned by clock() may wrap around on some implementations. + * For example, on a machine with 32-bit values for clock_t, it wraps + * after 2147 seconds. + * + * This implies that we should bitwise and with LONG_MAX. + */ + clock_t clock(void) { struct tms xtms; unsigned long t; times(&xtms); + t = ((unsigned long) xtms.tms_utime) + xtms.tms_stime; #ifndef __UCLIBC_CLK_TCK_CONST -#error __UCLIBC_CLK_TCK_CONST not defined! -#endif -#undef CLK_TCK -#define CLK_TCK __UCLIBC_CLK_TCK_CONST +# error __UCLIBC_CLK_TCK_CONST not defined! -#if CLK_TCK > CLOCKS_PER_SEC -#error __UCLIBC_CLK_TCK_CONST > CLOCKS_PER_SEC! -#elif CLK_TCK < 1 -#error __UCLIBC_CLK_TCK_CONST < 1! -#endif +#elif ((CLOCKS_PER_SEC % __UCLIBC_CLK_TCK_CONST) == 0) + + /* CLOCKS_PER_SEC == k * __UCLIBC_CLK_TCK_CONST for some integer k >= 1. */ + return ((t * (CLOCKS_PER_SEC/__UCLIBC_CLK_TCK_CONST)) & LONG_MAX); -#if (CLK_TCK == CLOCKS_PER_SEC) - return (t <= LONG_MAX) ? t : -1; -#elif (CLOCKS_PER_SEC % CLK_TCK) == 0 - return (t <= (LONG_MAX / (CLOCKS_PER_SEC/CLK_TCK))) - ? t * (CLOCKS_PER_SEC/CLK_TCK) - : -1; #else - return (t <= ((LONG_MAX / CLOCKS_PER_SEC) * CLK_TCK - + ((LONG_MAX % CLOCKS_PER_SEC) * CLK_TCK) / CLOCKS_PER_SEC)) - ? (((t / CLK_TCK) * CLOCKS_PER_SEC) - + (((t % CLK_TCK) * CLOCKS_PER_SEC) / CLK_TCK)) - : -1; + + /* Unlike the previous case, the scaling factor is not an integer. + * So when tms_utime, tms_stime, or their sum wraps, some of the + * "visible" bits in the return value are affected. Nothing we + * can really do about this though other than handle tms_utime and + * tms_stime seperately and then sum. But since that doesn't really + * buy us much, we don't bother. */ + + return ((((t / __UCLIBC_CLK_TCK_CONST) * CLOCKS_PER_SEC) + + ((((t % __UCLIBC_CLK_TCK_CONST) * CLOCKS_PER_SEC) + / __UCLIBC_CLK_TCK_CONST)) + ) & LONG_MAX); + #endif } @@ -525,6 +530,24 @@ struct tm *localtime(const time_t *timer) /**********************************************************************/ #ifdef L_localtime_r +struct tm *localtime_r(register const time_t *__restrict timer, + register struct tm *__restrict result) +{ + TZLOCK; + + tzset(); + + __time_localtime_tzi(timer, result, _time_tzinfo); + + TZUNLOCK; + + return result; +} + +#endif +/**********************************************************************/ +#ifdef L__time_localtime_tzi + #ifdef __UCLIBC_HAS_TM_EXTENSIONS__ struct ll_tzname_item; @@ -539,7 +562,7 @@ static ll_tzname_item_t ll_tzname[] = { { NULL, "???" } /* Always 2nd. (invalid or out-of-memory) */ }; -const char *lookup_tzname(const char *key) +static const char *lookup_tzname(const char *key) { ll_tzname_item_t *p; @@ -574,9 +597,9 @@ static const unsigned char day_cor[] = { /* non-leap */ /* Note: timezone locking is done by localtime_r. */ -static int tm_isdst(register const struct tm *__restrict ptm) +static int tm_isdst(register const struct tm *__restrict ptm, + register rule_struct *r) { - register rule_struct *r = _time_tzinfo; long sec; int i, isdst, isleap, day, day0, monlen, mday; int oday; /* Note: oday can be uninitialized. */ @@ -647,21 +670,18 @@ static int tm_isdst(register const struct tm *__restrict ptm) return (isdst & 1); } -struct tm *localtime_r(register const time_t *__restrict timer, - register struct tm *__restrict result) +struct tm *__time_localtime_tzi(register const time_t *__restrict timer, + register struct tm *__restrict result, + rule_struct *tzi) { time_t x[1]; long offset; int days, dst; - TZLOCK; - - tzset(); - dst = 0; do { days = -7; - offset = 604800L - _time_tzinfo[dst].gmt_offset; + offset = 604800L - tzi[dst].gmt_offset; if (*timer > (LONG_MAX - 604800L)) { days = -days; offset = -offset; @@ -671,12 +691,11 @@ struct tm *localtime_r(register const time_t *__restrict timer, _time_t2tm(x, days, result); result->tm_isdst = dst; #ifdef __UCLIBC_HAS_TM_EXTENSIONS__ - result->tm_gmtoff = - _time_tzinfo[dst].gmt_offset; - result->tm_zone = lookup_tzname(_time_tzinfo[dst].tzname); + result->tm_gmtoff = - tzi[dst].gmt_offset; + result->tm_zone = lookup_tzname(tzi[dst].tzname); #endif /* __UCLIBC_HAS_TM_EXTENSIONS__ */ - } while ((++dst < 2) && ((result->tm_isdst = tm_isdst(result)) != 0)); - - TZUNLOCK; + } while ((++dst < 2) + && ((result->tm_isdst = tm_isdst(result, tzi)) != 0)); return result; } @@ -694,6 +713,20 @@ time_t mktime(struct tm *timeptr) return _time_mktime(timeptr, 1); } +#endif +/**********************************************************************/ +#ifdef L_timegm +/* Like `mktime' but timeptr represents Universal Time, not local time. */ + +time_t timegm(struct tm *timeptr) +{ + rule_struct gmt_tzinfo[2]; + + memset(gmt_tzinfo, 0, sizeof(gmt_tzinfo)); + strcpy(gmt_tzinfo[0].tzname, "GMT"); /* Match glibc behavior here. */ + + return _time_mktime_tzi(timeptr, 1, gmt_tzinfo); +} #endif /**********************************************************************/ @@ -913,7 +946,9 @@ size_t __XL(strftime)(char *__restrict s, size_t maxsize, long tzo; register const char *p; register const char *o; +#ifndef __UCLIBC_HAS_TM_EXTENSIONS__ const rule_struct *rsp; +#endif const char *stack[MAX_PUSH]; size_t count; size_t o_count; @@ -1027,15 +1062,28 @@ size_t __XL(strftime)(char *__restrict s, size_t maxsize, goto OUTPUT; } +#ifdef __UCLIBC_HAS_TM_EXTENSIONS__ + +#define RSP_TZUNLOCK ((void) 0) +#define RSP_TZNAME timeptr->tm_zone +#define RSP_GMT_OFFSET timeptr->tm_gmtoff + +#else + +#define RSP_TZUNLOCK TZUNLOCK +#define RSP_TZNAME rsp->tzname +#define RSP_GMT_OFFSET rsp->gmt_offset + TZLOCK; rsp = _time_tzinfo; if (timeptr->tm_isdst > 0) { ++rsp; } +#endif if (*p == 'Z') { - o = rsp->tzname; + o = RSP_TZNAME; assert(o != NULL); #if 0 if (!o) { /* PARANOIA */ @@ -1043,15 +1091,15 @@ size_t __XL(strftime)(char *__restrict s, size_t maxsize, } #endif o_count = SIZE_MAX; - TZUNLOCK; + RSP_TZUNLOCK; goto OUTPUT; } else { /* z */ *s = '+'; - if ((tzo = -rsp->gmt_offset) < 0) { + if ((tzo = -RSP_GMT_OFFSET) < 0) { tzo = -tzo; *s = '-'; } - TZUNLOCK; + RSP_TZUNLOCK; ++s; --count; @@ -1060,6 +1108,7 @@ size_t __XL(strftime)(char *__restrict s, size_t maxsize, i = 16 + 6; /* 0-fill, width = 4 */ } + } else { /* TODO: don't need year for U, W */ for (i=0 ; i < 3 ; i++) { @@ -2087,12 +2136,32 @@ struct tm __time_tm; /* Global shared by gmtime() and localtime(). */ /**********************************************************************/ #ifdef L__time_mktime +time_t _time_mktime(struct tm *timeptr, int store_on_success) +{ + time_t t; + + TZLOCK; + + tzset(); + + t = _time_mktime_tzi(timeptr, store_on_success, _time_tzinfo); + + TZUNLOCK; + + return t; +} + +#endif +/**********************************************************************/ +#ifdef L__time_mktime_tzi + static const unsigned char vals[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, /* non-leap */ 29, }; -time_t _time_mktime(struct tm *timeptr, int store_on_success) +time_t _time_mktime_tzi(struct tm *timeptr, int store_on_success, + rule_struct *tzi) { #ifdef __BCC__ long days, secs; @@ -2106,13 +2175,9 @@ time_t _time_mktime(struct tm *timeptr, int store_on_success) register const unsigned char *s; int d, default_dst; - TZLOCK; - - tzset(); - memcpy(p, timeptr, sizeof(struct tm)); - if (!_time_tzinfo[1].tzname[0]) { /* No dst in this timezone, */ + if (!tzi[1].tzname[0]) { /* No dst in this timezone, */ p[8] = 0; /* so set tm_isdst to 0. */ } @@ -2150,7 +2215,7 @@ time_t _time_mktime(struct tm *timeptr, int store_on_success) d = p[5] - 1; days = -719163L + ((long)d)*365 + ((d/4) - (d/100) + (d/400) + p[3] + p[7]); secs = p[0] + 60*( p[1] + 60*((long)(p[2])) ) - + _time_tzinfo[default_dst].gmt_offset; + + tzi[default_dst].gmt_offset; DST_CORRECT: if (secs < 0) { secs += 120009600L; @@ -2165,7 +2230,7 @@ time_t _time_mktime(struct tm *timeptr, int store_on_success) d = p[5] - 1; d = -719163L + d*365 + (d/4) - (d/100) + (d/400); secs = p[0] - + _time_tzinfo[default_dst].gmt_offset + + tzi[default_dst].gmt_offset + 60*( p[1] + 60*(p[2] + 24*(((146073L * ((long long)(p[6])) + d) @@ -2183,7 +2248,7 @@ time_t _time_mktime(struct tm *timeptr, int store_on_success) d = ((struct tm *)p)->tm_isdst; t = secs; - localtime_r(&t, (struct tm *)p); + __time_localtime_tzi(&t, (struct tm *)p, tzi); if (t == ((time_t)(-1))) { /* Remember, time_t can be unsigned. */ goto DONE; @@ -2193,8 +2258,8 @@ time_t _time_mktime(struct tm *timeptr, int store_on_success) #ifdef __BCC__ secs -= (days * 86400L); #endif - secs += (_time_tzinfo[1-default_dst].gmt_offset - - _time_tzinfo[default_dst].gmt_offset); + secs += (tzi[1-default_dst].gmt_offset + - tzi[default_dst].gmt_offset); goto DST_CORRECT; } @@ -2205,8 +2270,6 @@ time_t _time_mktime(struct tm *timeptr, int store_on_success) DONE: - TZUNLOCK; - return t; } @@ -2254,7 +2317,3 @@ int dysize(int year) #endif /**********************************************************************/ -/* Like `mktime', but for TP represents Universal Time, not local time. */ -/* time_t timegm(struct tm *tp) */ - - |