Browse Source

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.

Manuel Novoa III 20 years ago
parent
commit
0a20ca071b
2 changed files with 145 additions and 88 deletions
  1. 0 2
      include/time.h
  2. 145 86
      libc/misc/time/time.c

+ 0 - 2
include/time.h

@@ -322,10 +322,8 @@ extern int stime (__const time_t *__when) __THROW;
 /* Miscellaneous functions many Unices inherited from the public domain
    localtime package.  These are included only for compatibility.  */
 
-#if 0
 /* Like `mktime', but for TP represents Universal Time, not local time.  */
 extern time_t timegm (struct tm *__tp) __THROW;
-#endif
 
 /* Another name for `mktime'.  */
 extern time_t timelocal (struct tm *__tp) __THROW;

+ 145 - 86
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) */
-
-