Browse Source

Commit the new time stuff, even though I haven't made it threadsafe yet.
At least people can play with it. Also, fix a buglet in setenv.c.

Manuel Novoa III 22 years ago
parent
commit
d6b2a40778

+ 2 - 0
include/time.h

@@ -127,6 +127,7 @@ struct tm
   int tm_yday;			/* Days in year.[0-365]	*/
   int tm_isdst;			/* DST.		[-1/0/1]*/
 
+#if 0
 # ifdef	__USE_BSD
   long int tm_gmtoff;		/* Seconds east of UTC.  */
   __const char *tm_zone;	/* Timezone abbreviation.  */
@@ -134,6 +135,7 @@ struct tm
   long int __tm_gmtoff;		/* Seconds east of UTC.  */
   __const char *__tm_zone;	/* Timezone abbreviation.  */
 # endif
+#endif
 };
 
 

+ 15 - 5
libc/misc/time/Makefile

@@ -24,13 +24,19 @@
 TOPDIR=../../../
 include $(TOPDIR)Rules.mak
 
-CSRC=localtime.c gmtime.c asctime.c ctime.c asc_conv.c tm_conv.c mktime.c \
-	localtime_r.c gmtime_r.c asctime_r.c ctime_r.c utimes.c adjtime.c \
-	clock.c difftime.c ftime.c strftime.c strptime.c __time_locale.c \
-	__time_static.c
+MSRC= time.c
+MOBJ= asctime.o asctime_r.o clock.o ctime.o ctime_r.o gmtime.o gmtime_r.o \
+	localtime.o localtime_r.o mktime.o strftime.o strptime.o tzset.o \
+	_time_t2tm.o __time_tm.o _time_mktime.o
 
+ifeq ($(HAS_FLOATING_POINT),true)
+	MOBJ += difftime.o 
+endif
+
+CSRC= adjtime.c ftime.c
 COBJS=$(patsubst %.c,%.o, $(CSRC))
-OBJS=$(COBJS)
+
+OBJS=$(COBJS) $(MOBJ)
 
 all: $(OBJS) $(LIBC)
 
@@ -39,6 +45,10 @@ $(LIBC): ar-target
 ar-target: $(OBJS)
 	$(AR) $(ARFLAGS) $(LIBC) $(OBJS)
 
+$(MOBJ): $(MSRC)
+	$(CC) $(CFLAGS) -DTARGET_ARCH=$(TARGET_ARCH) -DL_$* $< -c -o $*.o
+	$(STRIPTOOL) -x -R .note -R .comment $*.o
+
 $(COBJS): %.o : %.c
 	$(CC) $(CFLAGS) -c $< -o $@
 	$(STRIPTOOL) -x -R .note -R .comment $*.o

+ 0 - 8
libc/misc/time/README

@@ -1,8 +0,0 @@
-Copyright (C) 1996 Robert de Bath <robert@mayday.compulink.co.uk>
-This file is part of the Linux-8086 C library and is distributed
-under the GNU Library General Public License.
-
-There are two ways of converting the time_t to a struct tm, I'm not
-quite sure which is better.
-
--Robert

+ 0 - 30
libc/misc/time/__time_locale.c

@@ -1,30 +0,0 @@
-#ifndef __UCLIBC_HAS_LOCALE__
-char const __weekday_name[][10] =
-  {
-    "Sunday", "Monday", "Tuesday", "Wednesday",
-    "Thursday", "Friday", "Saturday"
-  };
-char const __ab_weekday_name[][4] =
-  {
-    "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
-  };
-char const __month_name[][10] =
-  {
-    "January", "February", "March", "April", "May", "June",
-    "July", "August", "September", "October", "November", "December"
-  };
-char const __ab_month_name[][4] =
-  {
-    "Jan", "Feb", "Mar", "Apr", "May", "Jun",
-    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
-  };
-
-const unsigned short int __mon_yday[2][13] =
-  {
-    /* Normal years.  */
-    { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
-    /* Leap years.  */
-    { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
-  };
-#endif
-

+ 0 - 17
libc/misc/time/__time_static.c

@@ -1,17 +0,0 @@
-
-#include <time.h>
-
-/* These globals are exported by the C library */
-char *__tzname[2] = { (char *) "GMT", (char *) "GMT" };
-int __daylight = 0;
-long int __timezone = 0L;
-/* Grumble */
-weak_alias (__tzname, tzname);
-weak_alias (__daylight, daylight);
-weak_alias (__timezone, timezone);
-
-
-/* static data for gmtime() and localtime() */
-struct tm __tmb;
-
-

+ 0 - 80
libc/misc/time/asc_conv.c

@@ -1,80 +0,0 @@
-#include <features.h>
-#include <ctype.h>
-#include <langinfo.h>
-#include <string.h>
-#include <time.h>
-
-/*
- * Internal ascii conversion routine, avoid use of printf, it's a bit big!
- */
-
-/*
- * Modified      Manuel Novoa III       Jan 2001
- *
- * Removed static function "hit" and did time-field fills inline and
- * put day, hour, min, and sec conversions in a loop using a small
- * table to reduce code size.
- *
- * Made daysp[] and mons[] const to move them from bss to text.
- *
- * Also fixed day conversion ... ANSI says no leading 0.
- *
- * Modified	Erik Andersen	    May 2002
- * Changed to optionally support real locales.
- *
- */
-
-#ifdef __UCLIBC_HAS_LOCALE__
-/* This is defined in locale/C-time.c in the GNU libc.  */
-extern const struct locale_data _nl_C_LC_TIME;
-#define __ab_weekday_name(DAY) (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABDAY_1)+(DAY)].string)
-#define __ab_month_name(MON)   (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABMON_1)+(MON)].string)
-#else
-extern char const __ab_weekday_name[][4];
-extern char const __ab_month_name[][4];
-#define __ab_weekday_name(DAY) (__ab_weekday_name[DAY])
-#define __ab_month_name(MON)   (__ab_month_name[MON])
-#endif
-
-void __asctime(register char *buffer, struct tm *ptm)
-{
-	char *p;
-	int tmp, i, tm_field[4];
-	/*                              012345678901234567890123456 */
-	static const char template[] = "??? ??? 00 00:00:00 0000\n";
-
-	/* Since we need memcpy below, use it here instead of strcpy. */
-	memcpy(buffer, template, sizeof(template));
-
-	if ((ptm->tm_wday >= 0) && (ptm->tm_wday <= 6)) {
-		memcpy(buffer, __ab_weekday_name(ptm->tm_wday), 3);
-	}
-
-	if ((ptm->tm_mon >= 0) && (ptm->tm_mon <= 11)) {
-		memcpy(buffer + 4, __ab_month_name(ptm->tm_mon), 3);
-	}
-
-	tm_field[0] = ptm->tm_mday;
-	tm_field[1] = ptm->tm_hour;
-	tm_field[2] = ptm->tm_min;
-	tm_field[3] = ptm->tm_sec;
-
-	p = buffer + 9;
-	for (i=0 ; i<4 ; i++) {
-		tmp = tm_field[i];
-		*p-- += tmp % 10;
-		*p += (tmp/10) % 10;
-		p += 4 ;				/* skip to end of next field */
-	}
-
-	tmp = ptm->tm_year + 1900;
-	p = buffer + 23;
-	for (i=0 ; i<4 ; i++) {
-		*p-- += tmp % 10;
-		tmp /= 10;
-	}
-
-	if (buffer[8] == '0') {
-		buffer[8] = ' ';
-	}
-}

+ 0 - 18
libc/misc/time/asctime.c

@@ -1,18 +0,0 @@
-
-#include <time.h>
-#include <errno.h>
-
-extern void __asctime();
-
-
-char * asctime (__const struct tm *timeptr)
-{
-    static char __time_buf[26];
-    if (timeptr == NULL) {
-	__set_errno (EINVAL);
-	return NULL;
-    }
-    __asctime(__time_buf, timeptr);
-    return __time_buf;
-}
-

+ 0 - 16
libc/misc/time/asctime_r.c

@@ -1,16 +0,0 @@
-
-#include <time.h>
-#include <errno.h>
-
-extern void __asctime();
-
-char *asctime_r(__const struct tm *timeptr, char *buf)
-{
-    if (timeptr == NULL || buf == NULL) {
-	__set_errno (EINVAL);
-	return NULL;
-    }
-    __asctime(buf, timeptr);
-    return buf;
-}
-

+ 0 - 43
libc/misc/time/clock.c

@@ -1,43 +0,0 @@
-/* Copyright (C) 1991, 1992, 1996, 1999 Free Software Foundation, Inc.
-   This file is part of the GNU C Library.
-
-   The GNU C 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.
-
-   The GNU C 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 the GNU C Library; see the file COPYING.LIB.  If not,
-   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-   Boston, MA 02111-1307, USA.  */
-
-#include <time.h>
-#include <unistd.h>
-#include <sys/times.h>
-
-/* Return the time used by the program so far (user time + system time).  */
-clock_t
-clock (void)
-{
-  struct tms buf;
-  long clk_tck = CLK_TCK;
-
-  /* We don't check for errors here.  The only error the kernel
-     returns is EFAULT if the value cannot be written to the struct we
-     pass a pointer to.  Otherwise the kernel returns an `unsigned
-     long' value which is the number of jiffies since system start.
-     But this number can be negative (when read as `long') when the
-     system is up for some time.  Ignoring errors should therefore
-     have no negative impacts but solve the problem.  */
-  times (&buf);
-
-  return
-      (clk_tck <= CLOCKS_PER_SEC) ? 
-      ((unsigned long) buf.tms_utime + buf.tms_stime) * (CLOCKS_PER_SEC / clk_tck) : 
-      ((unsigned long) buf.tms_utime + buf.tms_stime) / (clk_tck / CLOCKS_PER_SEC);
-}

+ 0 - 14
libc/misc/time/ctime.c

@@ -1,14 +0,0 @@
-
-#include <time.h>
-#include <sys/time.h>
-
-char * ctime (const time_t *t)
-{
-
-    /* According to IEEE Std 1003.1-2001: The ctime() function shall
-     * convert the time pointed to by clock, representing time in
-     * seconds since the Epoch, to local time in the form of a string.
-     * It shall be equivalent to: asctime(localtime(clock)) */
-    return asctime (localtime (t));
-}
-

+ 0 - 10
libc/misc/time/ctime_r.c

@@ -1,10 +0,0 @@
-
-#include <time.h>
-#include <sys/time.h>
-
-char * ctime_r(const time_t *t, char *buf)
-{
-    struct tm tm;
-    return asctime_r(localtime_r(t, &tm), buf);
-} 
-

+ 0 - 64
libc/misc/time/difftime.c

@@ -1,64 +0,0 @@
-/* Copyright (C) 1991, 1994, 1996 Free Software Foundation, Inc.
-   This file is part of the GNU C Library.
-
-   The GNU C 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.
-
-   The GNU C 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 the GNU C Library; see the file COPYING.LIB.  If not,
-   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-   Boston, MA 02111-1307, USA.  */
-
-#include <time.h>
-#include <limits.h>
-#include <sys/types.h>
-
-
-/* Return the difference between TIME1 and TIME0.  */
-double difftime (time_t time1, time_t time0)
-{
-  /* Algorithm courtesy Paul Eggert (eggert@twinsun.com).  */
-
-  time_t delta, 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;
-
-  if (time1 < time0)
-    return - difftime (time0, time1);
-
-  /* As much as possible, avoid loss of precision by computing the
-    difference before converting to double.  */
-  delta = time1 - time0;
-  if (delta >= 0)
-    return delta;
-
-  /* Repair delta overflow.  */
-  hibit = (~ (time_t) 0) << ((sizeof (time_t) * CHAR_BIT) - 1);
-
-  /* The following expression rounds twice, which means the result may not
-     be the closest to the true answer.  For example, suppose time_t is
-     64-bit signed int, long_double is IEEE 754 double with default
-     rounding, time1 = 9223372036854775807 and time0 = -1536.  Then the
-     true difference is 9223372036854777343, which rounds to
-     9223372036854777856 with a total error of 513.  But delta overflows to
-     -9223372036854774273, which rounds to -9223372036854774784, and
-     correcting this by subtracting 2 * (long_double) hibit (i.e. by adding
-     2**64 = 18446744073709551616) yields 9223372036854776832, which rounds
-     to 9223372036854775808 with a total error of 1535 instead.  This
-     problem occurs only with very large differences.  It's too painful to
-     fix this portably.  We are not alone in this problem; many C compilers
-     round twice when converting large unsigned types to small floating
-     types, so if time_t is unsigned the "return delta" above has the same
-     double-rounding problem.  */
-  return delta - 2 * (long double) hibit;
-}

+ 0 - 13
libc/misc/time/gmtime.c

@@ -1,13 +0,0 @@
-
-#include <time.h>
-
-extern void __tm_conv();
-
-/* Our static data lives in __time_static.c */
-extern struct tm __tmb;
-
-struct tm *gmtime(__const time_t *timep)
-{
-    __tm_conv(&__tmb, timep, 0L);
-    return &__tmb;
-}

+ 0 - 12
libc/misc/time/gmtime_r.c

@@ -1,12 +0,0 @@
-
-#include <time.h>
-
-extern void __tm_conv();
-
-struct tm *gmtime_r(timep, tp)
-__const time_t *timep;
-struct tm *tp;
-{
-	__tm_conv(tp, timep, 0L);
-	return tp;
-}

+ 0 - 24
libc/misc/time/localtime.c

@@ -1,24 +0,0 @@
-
-#include <time.h>
-#include <sys/time.h>
-
-/* Our static data lives in __time_static.c */
-extern struct tm __tmb;
-
-
-extern void __tm_conv();
-
-struct tm *localtime(__const time_t *timep)
-{
-    struct timezone tz;
-    time_t offt;
-
-    gettimeofday((void *) 0, &tz);
-
-    offt = -tz.tz_minuteswest * 60L;
-
-    /* tmb.tm_isdst = ? */
-    __tm_conv(&__tmb, timep, offt);
-
-    return &__tmb;
-}

+ 0 - 22
libc/misc/time/localtime_r.c

@@ -1,22 +0,0 @@
-
-#include <time.h>
-#include <sys/time.h>
-
-extern void __tm_conv();
-
-struct tm *localtime_r(timep, tp)
-__const time_t *timep;
-struct tm *tp;
-{
-	struct timezone tz;
-	time_t offt;
-
-	gettimeofday((void *) 0, &tz);
-
-	offt = -tz.tz_minuteswest * 60L;
-
-	/* tmb.tm_isdst = ? */
-	__tm_conv(tp, timep, offt);
-
-	return tp;
-}

+ 0 - 327
libc/misc/time/mktime.c

@@ -1,327 +0,0 @@
-/* Convert a `struct tm' to a time_t value.
-   Copyright (C) 1993, 94, 95, 96, 97, 98, 99 Free Software Foundation, Inc.
-   This file is part of the GNU C Library.
-   Contributed by Paul Eggert (eggert@twinsun.com).
-
-   The GNU C Library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
-
-   The GNU C 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
-   Lesser General Public License for more details.
-
-   You should have received a copy of the GNU Lesser General Public
-   License along with the GNU C Library; if not, write to the Free
-   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-   02111-1307 USA.  */
-
-/* Define this to have a standalone program to test this implementation of
-   mktime.  */
-
-#include <features.h>
-/* Assume that leap seconds are not possible */
-#undef LEAP_SECONDS_POSSIBLE 
-#include <sys/types.h>		/* Some systems define `time_t' here.  */
-#include <time.h>
-#include <limits.h>
-
-#if 0
-#ifndef CHAR_BIT
-# define CHAR_BIT 8
-#endif
-
-/* The extra casts work around common compiler bugs.  */
-#define TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
-/* The outer cast is needed to work around a bug in Cray C 5.0.3.0.
-   It is necessary at least when t == time_t.  */
-#define TYPE_MINIMUM(t) ((t) (TYPE_SIGNED (t) \
-			      ? ~ (t) 0 << (sizeof (t) * CHAR_BIT - 1) : (t) 0))
-#define TYPE_MAXIMUM(t) ((t) (~ (t) 0 - TYPE_MINIMUM (t)))
-
-#ifndef INT_MIN
-# define INT_MIN TYPE_MINIMUM (int)
-#endif
-#ifndef INT_MAX
-# define INT_MAX TYPE_MAXIMUM (int)
-#endif
-
-#ifndef TIME_T_MIN
-# define TIME_T_MIN TYPE_MINIMUM (time_t)
-#endif
-#ifndef TIME_T_MAX
-# define TIME_T_MAX TYPE_MAXIMUM (time_t)
-#endif
-
-#define TM_YEAR_BASE 1900
-#define EPOCH_YEAR 1970
-
-
-/* How many days come before each month (0-12).  */
-extern const unsigned short int __mon_yday[2][13];
-
-
-
-/* Yield the difference between (YEAR-YDAY HOUR:MIN:SEC) and (*TP),
-   measured in seconds, ignoring leap seconds.
-   YEAR uses the same numbering as TM->tm_year.
-   All values are in range, except possibly YEAR.
-   If TP is null, return a nonzero value.
-   If overflow occurs, yield the low order bits of the correct answer.  */
-static time_t
-__ydhms_tm_diff (int year, int yday, int hour, int min, int sec,
-	       const struct tm *tp)
-{
-    if (!tp)
-	return 1;
-    else
-    {
-	/* Compute intervening leap days correctly even if year is negative.
-	   Take care to avoid int overflow.  time_t overflow is OK, since
-	   only the low order bits of the correct time_t answer are needed.
-	   Don't convert to time_t until after all divisions are done, since
-	   time_t might be unsigned.  */
-	int a4 = (year >> 2) + (TM_YEAR_BASE >> 2) - ! (year & 3);
-	int b4 = (tp->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (tp->tm_year & 3);
-	int a100 = a4 / 25 - (a4 % 25 < 0);
-	int b100 = b4 / 25 - (b4 % 25 < 0);
-	int a400 = a100 >> 2;
-	int b400 = b100 >> 2;
-	int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
-	time_t years = year - (time_t) tp->tm_year;
-	time_t days = (365 * years + intervening_leap_days
-		+ (yday - tp->tm_yday));
-	return (60 * (60 * (24 * days + (hour - tp->tm_hour))
-		    + (min - tp->tm_min))
-		+ (sec - tp->tm_sec));
-    }
-}
-
-/* Use CONVERT to convert *T to a broken down time in *TP.
-   If *T is out of range for conversion, adjust it so that
-   it is the nearest in-range value and then convert that.  */
-static struct tm *
-__ranged_convert (struct tm *(*convert) (const time_t *, struct tm *),
-		time_t *t, struct tm *tp)
-{
-    struct tm *r;
-
-    if (! (r = (*convert) (t, tp)) && *t)
-    {
-	time_t bad = *t;
-	time_t ok = 0;
-	struct tm tm;
-
-	/* BAD is a known unconvertible time_t, and OK is a known good one.
-	   Use binary search to narrow the range between BAD and OK until
-	   they differ by 1.  */
-	while (bad != ok + (bad < 0 ? -1 : 1))
-	{
-	    time_t mid = *t = (bad < 0
-		    ? bad + ((ok - bad) >> 1)
-		    : ok + ((bad - ok) >> 1));
-	    if ((r = (*convert) (t, tp)))
-	    {
-		tm = *r;
-		ok = mid;
-	    }
-	    else
-		bad = mid;
-	}
-
-	if (!r && ok)
-	{
-	    /* The last conversion attempt failed;
-	       revert to the most recent successful attempt.  */
-	    *t = ok;
-	    *tp = tm;
-	    r = tp;
-	}
-    }
-
-    return r;
-}
-
-
-/* Convert *TP to a time_t value, inverting
-   the monotonic and mostly-unit-linear conversion function CONVERT.
-   Use *OFFSET to keep track of a guess at the offset of the result,
-   compared to what the result would be for UTC without leap seconds.
-   If *OFFSET's guess is correct, only one CONVERT call is needed.  */
-time_t __mktime_internal (struct tm *tp, 
-	struct tm *(*convert) (const time_t *, struct tm *), time_t *offset)
-{
-    time_t t, dt, t0, t1, t2;
-    struct tm tm;
-
-    /* The maximum number of probes (calls to CONVERT) should be enough
-       to handle any combinations of time zone rule changes, solar time,
-       leap seconds, and oscillations around a spring-forward gap.
-       POSIX.1 prohibits leap seconds, but some hosts have them anyway.  */
-    int remaining_probes = 6;
-
-    /* Time requested.  Copy it in case CONVERT modifies *TP; this can
-       occur if TP is localtime's returned value and CONVERT is localtime.  */
-    int sec = tp->tm_sec;
-    int min = tp->tm_min;
-    int hour = tp->tm_hour;
-    int mday = tp->tm_mday;
-    int mon = tp->tm_mon;
-    int year_requested = tp->tm_year;
-    int isdst = tp->tm_isdst;
-
-    /* Ensure that mon is in range, and set year accordingly.  */
-    int mon_remainder = mon % 12;
-    int negative_mon_remainder = mon_remainder < 0;
-    int mon_years = mon / 12 - negative_mon_remainder;
-    int year = year_requested + mon_years;
-
-    /* The other values need not be in range:
-       the remaining code handles minor overflows correctly,
-       assuming int and time_t arithmetic wraps around.
-       Major overflows are caught at the end.  */
-
-    /* Calculate day of year from year, month, and day of month.
-       The result need not be in range.  */
-    int yday = ((__mon_yday[__isleap (year + TM_YEAR_BASE)]
-		[mon_remainder + 12 * negative_mon_remainder])
-	    + mday - 1);
-
-    int sec_requested = sec;
-#if LEAP_SECONDS_POSSIBLE
-    /* Handle out-of-range seconds specially,
-       since __ydhms_tm_diff assumes every minute has 60 seconds.  */
-    if (sec < 0)
-	sec = 0;
-    if (59 < sec)
-	sec = 59;
-#endif
-
-    /* Invert CONVERT by probing.  First assume the same offset as last time.
-       Then repeatedly use the error to improve the guess.  */
-
-    tm.tm_year = EPOCH_YEAR - TM_YEAR_BASE;
-    tm.tm_yday = tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
-    t0 = __ydhms_tm_diff (year, yday, hour, min, sec, &tm);
-
-    for (t = t1 = t2 = t0 + *offset;
-	    (dt = __ydhms_tm_diff (year, yday, hour, min, sec,
-				 __ranged_convert (convert, &t, &tm)));
-	    t1 = t2, t2 = t, t += dt)
-	if (t == t1 && t != t2
-		&& (isdst < 0 || tm.tm_isdst < 0
-		    || (isdst != 0) != (tm.tm_isdst != 0)))
-	    /* We can't possibly find a match, as we are oscillating
-	       between two values.  The requested time probably falls
-	       within a spring-forward gap of size DT.  Follow the common
-	       practice in this case, which is to return a time that is DT
-	       away from the requested time, preferring a time whose
-	       tm_isdst differs from the requested value.  In practice,
-	       this is more useful than returning -1.  */
-	    break;
-	else if (--remaining_probes == 0)
-	    return -1;
-
-    /* If we have a match, check whether tm.tm_isdst has the requested
-       value, if any.  */
-    if (dt == 0 && isdst != tm.tm_isdst && 0 <= isdst && 0 <= tm.tm_isdst)
-    {
-	/* tm.tm_isdst has the wrong value.  Look for a neighboring
-	   time with the right value, and use its UTC offset.
-Heuristic: probe the previous three calendar quarters (approximately),
-looking for the desired isdst.  This isn't perfect,
-but it's good enough in practice.  */
-	int quarter = 7889238; /* seconds per average 1/4 Gregorian year */
-	int i;
-
-	/* If we're too close to the time_t limit, look in future quarters.  */
-	if (t < TIME_T_MIN + 3 * quarter)
-	    quarter = -quarter;
-
-	for (i = 1; i <= 3; i++)
-	{
-	    time_t ot = t - i * quarter;
-	    struct tm otm;
-	    __ranged_convert (convert, &ot, &otm);
-	    if (otm.tm_isdst == isdst)
-	    {
-		/* We found the desired tm_isdst.
-		   Extrapolate back to the desired time.  */
-		t = ot + __ydhms_tm_diff (year, yday, hour, min, sec, &otm);
-		__ranged_convert (convert, &t, &tm);
-		break;
-	    }
-	}
-    }
-
-    *offset = t - t0;
-
-#if LEAP_SECONDS_POSSIBLE
-    if (sec_requested != tm.tm_sec)
-    {
-	/* Adjust time to reflect the tm_sec requested, not the normalized value.
-	   Also, repair any damage from a false match due to a leap second.  */
-	t += sec_requested - sec + (sec == 0 && tm.tm_sec == 60);
-	if (! (*convert) (&t, &tm))
-	    return -1;
-    }
-#endif
-
-    if (TIME_T_MAX / INT_MAX / 366 / 24 / 60 / 60 < 3)
-    {
-	/* time_t isn't large enough to rule out overflows in __ydhms_tm_diff,
-	   so check for major overflows.  A gross check suffices,
-	   since if t has overflowed, it is off by a multiple of
-	   TIME_T_MAX - TIME_T_MIN + 1.  So ignore any component of
-	   the difference that is bounded by a small value.  */
-
-	double dyear = (double) year_requested + mon_years - tm.tm_year;
-	double dday = 366 * dyear + mday;
-	double dsec = 60 * (60 * (24 * dday + hour) + min) + sec_requested;
-
-	/* On Irix4.0.5 cc, dividing TIME_T_MIN by 3 does not produce
-	   correct results, ie., it erroneously gives a positive value
-	   of 715827882.  Setting a variable first then doing math on it
-	   seems to work.  (ghazi@caip.rutgers.edu) */
-
-	const time_t time_t_max = TIME_T_MAX;
-	const time_t time_t_min = TIME_T_MIN;
-
-	if (time_t_max / 3 - time_t_min / 3 < (dsec < 0 ? - dsec : dsec))
-	    return -1;
-    }
-
-    *tp = tm;
-    return t;
-}
-
-
-
-/* Convert *TP to a time_t value.  */
-time_t mktime (struct tm *tp)
-{
-    static time_t localtime_offset;
-    /* POSIX.1 8.1.1 requires that whenever mktime() is called, the
-       time zone names contained in the external variable `tzname' shall
-       be set as if the tzset() function had been called.  */
-    tzset ();
-
-    return __mktime_internal (tp, localtime_r, &localtime_offset);
-}
-#else
-
-/* Convert *TP to a time_t value.  */
-time_t mktime (struct tm *tp)
-{
-    time_t m_secs=tp->tm_min*60;
-    time_t h_secs=tp->tm_hour*3600;
-    time_t d_secs=tp->tm_yday*86400;
-    time_t y_secs=(tp->tm_year-70)*31536000;
-    time_t l_secs1=((tp->tm_year-69)/4)*86400;
-    time_t l_secs2=((tp->tm_year-1)/100)*86400;
-    time_t l_secs3=((tp->tm_year+299)/400)*86400;
-    return m_secs+h_secs+d_secs+y_secs+l_secs1-l_secs2+l_secs3+tp->tm_gmtoff;
-}
-#endif

+ 0 - 395
libc/misc/time/strftime.c

@@ -1,395 +0,0 @@
-/* Copyright (C) 1991, 92, 93, 94, 95, 96 Free Software Foundation, Inc.
-This file is part of the GNU C Library.
-
-The GNU C 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.
-
-The GNU C 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 the GNU C Library; see the file COPYING.LIB.  If
-not, write to the Free Software Foundation, Inc., 675 Mass Ave,
-Cambridge, MA 02139, USA.  */
-
-
-#include <stddef.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-#include <sys/types.h>		/* Some systems define `time_t' here.  */
-#include <sys/time.h>
-#include <time.h>
-#include <ctype.h>
-#include <limits.h>
-
-static unsigned int week (const struct tm *const, int, int);
-
-#define	add(n, f)							      \
-  do									      \
-    {									      \
-      i += (n);								      \
-      if (i >= maxsize)							      \
-	return 0;							      \
-      else								      \
-	if (p)								      \
-	  {								      \
-	    f;								      \
-	    p += (n);							      \
-	  }								      \
-    } while (0)
-#define	cpy(n, s)	add((n), memcpy((void *) p, (void *) (s), (n)))
-
-#ifdef _LIBC
-#define	fmt(n, args)	add((n), if (sprintf args != (n)) return 0)
-#else
-#define	fmt(n, args)	add((n), sprintf args; if (strlen (p) != (n)) return 0)
-#endif
-
-
-
-/* Return the week in the year specified by TP,
-   with weeks starting on STARTING_DAY.  */
-static unsigned int week(const struct tm *const tp , int starting_day , int max_preceding )
-{
-  int wday, dl, base;
-
-  wday = tp->tm_wday - starting_day;
-  if (wday < 0)
-    wday += 7;
-
-  /* Set DL to the day in the year of the first day of the week
-     containing the day specified in TP.  */
-  dl = tp->tm_yday - wday;
-
-  /* For the computation following ISO 8601:1988 we set the number of
-     the week containing January 1st to 1 if this week has more than
-     MAX_PRECEDING days in the new year.  For ISO 8601 this number is
-     3, for the other representation it is 7 (i.e., not to be
-     fulfilled).  */
-  base = ((dl + 7) % 7) > max_preceding ? 1 : 0;
-
-  /* If DL is negative we compute the result as 0 unless we have to
-     compute it according ISO 8601.  In this case we have to return 53
-     or 1 if the week containing January 1st has less than 4 days in
-     the new year or not.  If DL is not negative we calculate the
-     number of complete weeks for our week (DL / 7) plus 1 (because
-     only for DL < 0 we are in week 0/53 and plus the number of the
-     first week computed in the last step.  */
-  return dl < 0 ? (dl < -max_preceding ? 53 : base)
-		: base + 1 + dl / 7;
-}
-
-#ifndef __UCLIBC_HAS_LOCALE__
-extern char const __weekday_name[][10];
-extern char const __month_name[][10];
-#endif
-
-/* Write information from TP into S according to the format
-   string FORMAT, writing no more that MAXSIZE characters
-   (including the terminating '\0') and returning number of
-   characters written.  If S is NULL, nothing will be written
-   anywhere, so to determine how many characters would be
-   written, use NULL for S and (size_t) UINT_MAX for MAXSIZE.  */
-size_t strftime( char *s , size_t maxsize , const char *format , register const struct tm *tp)
-{
-  int hour12 = tp->tm_hour;
-#ifdef __UCLIBC_HAS_LOCALE__
-  const char *const a_wkday = _NL_CURRENT (LC_TIME, ABDAY_1 + tp->tm_wday);
-  const char *const f_wkday = _NL_CURRENT (LC_TIME, DAY_1 + tp->tm_wday);
-  const char *const a_month = _NL_CURRENT (LC_TIME, ABMON_1 + tp->tm_mon);
-  const char *const f_month = _NL_CURRENT (LC_TIME, MON_1 + tp->tm_mon);
-  const char *const ampm = _NL_CURRENT (LC_TIME,
-					hour12 > 11 ? PM_STR : AM_STR);
-  size_t aw_len = strlen(a_wkday);
-  size_t am_len = strlen(a_month);
-  size_t ap_len = strlen (ampm);
-#else
-  const char *const f_wkday = __weekday_name[tp->tm_wday];
-  const char *const f_month = __month_name[tp->tm_mon];
-  const char *const a_wkday = f_wkday;
-  const char *const a_month = f_month;
-  const char *const ampm = "AMPM" + 2 * (hour12 > 11);
-  size_t aw_len = 3;
-  size_t am_len = 3;
-  size_t ap_len = 2;
-#endif
-  size_t wkday_len = strlen(f_wkday);
-  size_t month_len = strlen(f_month);
-  const unsigned int y_week0 = week (tp, 0, 7);
-  const unsigned int y_week1 = week (tp, 1, 7);
-  const unsigned int y_week2 = week (tp, 1, 3);
-  const char *zone;
-  size_t zonelen;
-  register size_t i = 0;
-  register char *p = s;
-  register const char *f;
-  char number_fmt[5];
-
-  /* Initialize the buffer we will use for the sprintf format for numbers.  */
-  number_fmt[0] = '%';
-
-  /* The POSIX test suite assumes that setting
-     the environment variable TZ to a new value before calling strftime()
-     will influence the result (the %Z format) even if the information in
-     TP is computed with a totally different time zone.
-     This is bogus: though POSIX allows bad behavior like this,
-     POSIX does not require it.  Do the right thing instead.  */
-  zone = (const char *) tp->tm_zone;
-  /* POSIX.1 8.1.1 requires that whenever strftime() is called, the
-     time zone names contained in the external variable `tzname' shall
-     be set as if the tzset() function had been called.  */
-  tzset ();
-  zonelen = strlen (zone);
-
-  if (hour12 > 12)
-    hour12 -= 12;
-  else
-    if (hour12 == 0) hour12 = 12;
-
-  for (f = format; *f != '\0'; ++f)
-    {
-      enum { pad_zero, pad_space, pad_none } pad; /* Padding for number.  */
-      unsigned int maxdigits;	/* Max digits for numeric format.  */
-      unsigned int number_value; /* Numeric value to be printed.  */
-      const char *subfmt;
-
-#if HAVE_MBLEN
-      if (!isascii(*f))
-	{
-	  /* Non-ASCII, may be a multibyte.  */
-	  int len = mblen(f, strlen(f));
-	  if (len > 0)
-	    {
-	      cpy(len, f);
-	      continue;
-	    }
-	}
-#endif
-
-      if (*f != '%')
-	{
-	  add(1, *p = *f);
-	  continue;
-	}
-
-      /* Check for flags that can modify a number format.  */
-      ++f;
-      switch (*f)
-	{
-	case '_':
-	  pad = pad_space;
-	  ++f;
-	  break;
-	case '-':
-	  pad = pad_none;
-	  ++f;
-	  break;
-	default:
-	  pad = pad_zero;
-	  break;
-	}
-
-      /* Now do the specified format.  */
-      switch (*f)
-	{
-	case '\0':
-	case '%':
-	  add(1, *p = *f);
-	  break;
-
-	case 'a':
-	  cpy(aw_len, a_wkday);
-	  break;
-
-	case 'A':
-	  cpy(wkday_len, f_wkday);
-	  break;
-
-	case 'b':
-	case 'h':		/* GNU extension.  */
-	  cpy(am_len, a_month);
-	  break;
-
-	case 'B':
-	  cpy(month_len, f_month);
-	  break;
-
-	case 'c':
-#ifdef __UCLIBC_HAS_LOCALE__
-	  subfmt = _NL_CURRENT (LC_TIME, D_T_FMT);
-#else
-	  subfmt = "%a %b %d %H:%M:%S %Z %Y";
-#endif
-	subformat:
-	  {
-	    size_t len = strftime (p, maxsize - i, subfmt, tp);
-	    if (len == 0 && *subfmt)
-	      return 0;
-	    add(len, );
-	  }
-	  break;
-
-#define DO_NUMBER(digits, value) \
-	  maxdigits = digits; number_value = value; goto do_number
-#define DO_NUMBER_NOPAD(digits, value) \
-	  maxdigits = digits; number_value = value; goto do_number_nopad
-
-	case 'C':
-	  DO_NUMBER (2, (1900 + tp->tm_year) / 100);
-
-	case 'x':
-#ifdef __UCLIBC_HAS_LOCALE__
-	  subfmt = _NL_CURRENT (LC_TIME, D_FMT);
-	  goto subformat;
-#endif
-	  /* Fall through.  */
-	case 'D':		/* GNU extension.  */
-	  subfmt = "%m/%d/%y";
-	  goto subformat;
-
-	case 'd':
-	  DO_NUMBER (2, tp->tm_mday);
-
-	case 'e':		/* GNU extension: %d, but blank-padded.  */
-#if 0
-	  DO_NUMBER_NOPAD (2, tp->tm_mday);
-#else
-	  DO_NUMBER (2, tp->tm_mday);
-#endif
-
-	  /* All numeric formats set MAXDIGITS and NUMBER_VALUE and then
-	     jump to one of these two labels.  */
-
-	do_number_nopad:
-	  /* Force `-' flag.  */
-	  pad = pad_none;
-
-	do_number:
-	  {
-	    /* Format the number according to the PAD flag.  */
-
-	    register char *nf = &number_fmt[1];
-	    int printed;
-
-	    switch (pad)
-	      {
-	      case pad_zero:
-		*nf++ = '0';
-	      case pad_space:
-		*nf++ = '0' + maxdigits;
-	      case pad_none:
-		*nf++ = 'u';
-		*nf = '\0';
-	      }
-
-#ifdef _LIBC
-	    if (i + maxdigits >= maxsize)
-		return 0;
-	    printed = sprintf (p, number_fmt, number_value);
-	    i += printed;
-	    p += printed;
-#else
-	    add (maxdigits, sprintf (p, number_fmt, number_value);
-		 printed = strlen (p));
-#endif
-
-	    break;
-	  }
-
-
-	case 'H':
-	  DO_NUMBER (2, tp->tm_hour);
-
-	case 'I':
-	  DO_NUMBER (2, hour12);
-
-	case 'k':		/* GNU extension.  */
-	  DO_NUMBER_NOPAD (2, tp->tm_hour);
-
-	case 'l':		/* GNU extension.  */
-	  DO_NUMBER_NOPAD (2, hour12);
-
-	case 'j':
-	  DO_NUMBER (3, 1 + tp->tm_yday);
-
-	case 'M':
-	  DO_NUMBER (2, tp->tm_min);
-
-	case 'm':
-	  DO_NUMBER (2, tp->tm_mon + 1);
-
-	case 'n':		/* GNU extension.  */
-	  add (1, *p = '\n');
-	  break;
-
-	case 'p':
-	  cpy(ap_len, ampm);
-	  break;
-
-	case 'R':		/* GNU extension.  */
-	  subfmt = "%H:%M";
-	  goto subformat;
-
-	case 'r':		/* GNU extension.  */
-	  subfmt = "%I:%M:%S %p";
-	  goto subformat;
-
-	case 'S':
-	  DO_NUMBER (2, tp->tm_sec);
-
-	case 'X':
-#ifdef __UCLIBC_HAS_LOCALE__
-	  subfmt = _NL_CURRENT (LC_TIME, T_FMT);
-	  goto subformat;
-#endif
-	  /* Fall through.  */
-	case 'T':		/* GNU extenstion.  */
-	  subfmt = "%H:%M:%S";
-	  goto subformat;
-
-	case 't':		/* GNU extenstion.  */
-	  add (1, *p = '\t');
-	  break;
-
-	case 'U':
-	  DO_NUMBER (2, y_week0);
-
-	case 'V':
-	  DO_NUMBER (2, y_week2);
-
-	case 'W':
-	  DO_NUMBER (2, y_week1);
-
-	case 'w':
-	  DO_NUMBER (1, tp->tm_wday);
-
-	case 'Y':
-	  DO_NUMBER (4, 1900 + tp->tm_year);
-
-	case 'y':
-	  DO_NUMBER (2, tp->tm_year % 100);
-
-	case 'Z':
-	  /* The tzset() call might have changed the value.  */
-	  if (!(zone && *zone) && tp->tm_isdst >= 0)
-	    zone = tzname[tp->tm_isdst];
-	  if (! zone)
-	    zone = "";		/* POSIX.2 requires the empty string here.  */
-	  cpy(zonelen, zone);
-	  break;
-
-	default:
-	  /* Bad format.  */
-	  break;
-	}
-    }
-
-  if (p)
-    *p = '\0';
-  return i;
-}

+ 0 - 983
libc/misc/time/strptime.c

@@ -1,983 +0,0 @@
-/* Convert a string representation of time to a time value.
-   Copyright (C) 1996-2000, 2001 Free Software Foundation, Inc.
-   This file is part of the GNU C Library.
-   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
-
-   The GNU C Library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
-
-   The GNU C 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
-   Lesser General Public License for more details.
-
-   You should have received a copy of the GNU Lesser General Public
-   License along with the GNU C Library; if not, write to the Free
-   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-   02111-1307 USA.  */
-
-/* XXX This version of the implementation is not really complete.
-   Some of the fields cannot add information alone.  But if seeing
-   some of them in the same format (such as year, week and weekday)
-   this is enough information for determining the date.  */
-
-#include <features.h>
-#include <ctype.h>
-#include <langinfo.h>
-#include <limits.h>
-#include <string.h>
-#include <time.h>
-
-#undef ENABLE_ERA_JUNK
-
-
-#define match_char(ch1, ch2) if (ch1 != ch2) return NULL
-#if defined __GNUC__ && __GNUC__ >= 2
-# define match_string(cs1, s2) \
-  ({ size_t len = strlen (cs1);						      \
-     int result = strncasecmp ((cs1), (s2), len) == 0;			      \
-     if (result) (s2) += len;						      \
-     result; })
-#else
-/* Oh come on.  Get a reasonable compiler.  */
-# define match_string(cs1, s2) \
-  (strncasecmp ((cs1), (s2), strlen (cs1)) ? 0 : ((s2) += strlen (cs1), 1))
-#endif
-/* We intentionally do not use isdigit() for testing because this will
-   lead to problems with the wide character version.  */
-#define get_number(from, to, n) \
-  do {									      \
-    int __n = n;							      \
-    val = 0;								      \
-    while (*rp == ' ')							      \
-      ++rp;								      \
-    if (*rp < '0' || *rp > '9')						      \
-      return NULL;							      \
-    do {								      \
-      val *= 10;							      \
-      val += *rp++ - '0';						      \
-    } while (--__n > 0 && val * 10 <= to && *rp >= '0' && *rp <= '9');	      \
-    if (val < from || val > to)						      \
-      return NULL;							      \
-  } while (0)
-#ifdef __UCLIBC_HAS_LOCALE__
-# define get_alt_number(from, to, n) \
-  ({									      \
-     __label__ do_normal;						      \
-									      \
-     if (*decided != raw)						      \
-       {								      \
-	 val = _nl_parse_alt_digit (&rp);				      \
-	 if (val == -1 && *decided != loc)				      \
-	   {								      \
-	     *decided = loc;						      \
-	     goto do_normal;						      \
-	   }								      \
-	if (val < from || val > to)					      \
-	  return NULL;							      \
-       }								      \
-     else								      \
-       {								      \
-       do_normal:							      \
-	 get_number (from, to, n);					      \
-       }								      \
-    0;									      \
-  })
-#else
-# define get_alt_number(from, to, n) \
-  /* We don't have the alternate representation.  */			      \
-  get_number(from, to, n)
-#endif
-#define recursive(new_fmt) \
-  (*(new_fmt) != '\0'							      \
-   && (rp = __strptime_internal (rp, (new_fmt), tm, decided, era_cnt)) != NULL)
-
-
-#ifdef __UCLIBC_HAS_LOCALE__
-/* This is defined in locale/C-time.c in the GNU libc.  */
-extern const struct locale_data _nl_C_LC_TIME;
-extern const unsigned short int __mon_yday[2][13];
-
-# define __weekday_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (DAY_1)].string)
-# define __ab_weekday_name \
-  (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABDAY_1)].string)
-# define __month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (MON_1)].string)
-# define __ab_month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABMON_1)].string)
-# define HERE_D_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_T_FMT)].string)
-# define HERE_D_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_FMT)].string)
-# define HERE_AM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (AM_STR)].string)
-# define HERE_PM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (PM_STR)].string)
-# define HERE_T_FMT_AMPM \
-  (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT_AMPM)].string)
-# define HERE_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT)].string)
-
-# define strncasecmp(s1, s2, n) __strncasecmp (s1, s2, n)
-#else
-extern char const __weekday_name[][10];
-extern char const __ab_weekday_name[][4];
-extern char const __month_name[][10];
-extern char const __ab_month_name[][4];
-extern const unsigned short int __mon_yday[2][13];
-# define HERE_D_T_FMT "%a %b %e %H:%M:%S %Y"
-# define HERE_D_FMT "%m/%d/%y"
-# define HERE_AM_STR "AM"
-# define HERE_PM_STR "PM"
-# define HERE_T_FMT_AMPM "%I:%M:%S %p"
-# define HERE_T_FMT "%H:%M:%S"
-#endif
-
-/* Status of lookup: do we use the locale data or the raw data?  */
-enum locale_status { not, loc, raw };
-
-
-#ifndef __isleap
-/* Nonzero if YEAR is a leap year (every 4 years,
-   except every 100th isn't, and every 400th is).  */
-# define __isleap(year)	\
-  ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
-#endif
-
-/* Compute the day of the week.  */
-static void
-day_of_the_week (struct tm *tm)
-{
-    /* We know that January 1st 1970 was a Thursday (= 4).  Compute the
-       the difference between this data in the one on TM and so determine
-       the weekday.  */
-    int corr_year = 1900 + tm->tm_year - (tm->tm_mon < 2);
-    int wday = (-473
-	    + (365 * (tm->tm_year - 70))
-	    + (corr_year / 4)
-	    - ((corr_year / 4) / 25) + ((corr_year / 4) % 25 < 0)
-	    + (((corr_year / 4) / 25) / 4)
-	    + __mon_yday[0][tm->tm_mon]
-	    + tm->tm_mday - 1);
-    tm->tm_wday = ((wday % 7) + 7) % 7;
-}
-
-/* Compute the day of the year.  */
-static void
-day_of_the_year (struct tm *tm)
-{
-    tm->tm_yday = (__mon_yday[__isleap (1900 + tm->tm_year)][tm->tm_mon]
-	    + (tm->tm_mday - 1));
-}
-
-
-static char * __strptime_internal (rp, fmt, tm, decided, era_cnt)
-     const char *rp;
-     const char *fmt;
-     struct tm *tm;
-     enum locale_status *decided;
-     int era_cnt;
-{
-    const char *rp_backup;
-    int cnt;
-    size_t val;
-    int have_I, is_pm;
-    int century, want_century;
-    int want_era;
-    int have_wday, want_xday;
-    int have_yday;
-    int have_mon, have_mday;
-    int have_uweek, have_wweek;
-    int week_no;
-#ifdef ENABLE_ERA_JUNK
-    size_t num_eras;
-    struct era_entry *era;
-
-    era = NULL;
-#endif
-    have_I = is_pm = 0;
-    century = -1;
-    want_century = 0;
-    want_era = 0;
-    week_no = 0;
-
-    have_wday = want_xday = have_yday = have_mon = have_mday = have_uweek = 0;
-    have_wweek = 0;
-
-    while (*fmt != '\0')
-    {
-	/* A white space in the format string matches 0 more or white
-	   space in the input string.  */
-	if (isspace (*fmt))
-	{
-	    while (isspace (*rp))
-		++rp;
-	    ++fmt;
-	    continue;
-	}
-
-	/* Any character but `%' must be matched by the same character
-	   in the iput string.  */
-	if (*fmt != '%')
-	{
-	    match_char (*fmt++, *rp++);
-	    continue;
-	}
-
-	++fmt;
-#ifndef __UCLIBC_HAS_LOCALE__
-	/* We need this for handling the `E' modifier.  */
-start_over:
-#endif
-
-	/* Make back up of current processing pointer.  */
-	rp_backup = rp;
-
-	switch (*fmt++)
-	{
-	    case '%':
-		/* Match the `%' character itself.  */
-		match_char ('%', *rp++);
-		break;
-	    case 'a':
-	    case 'A':
-		/* Match day of week.  */
-		for (cnt = 0; cnt < 7; ++cnt)
-		{
-#ifdef __UCLIBC_HAS_LOCALE__
-		    if (*decided !=raw)
-		    {
-			if (match_string (_NL_CURRENT (LC_TIME, DAY_1 + cnt), rp))
-			{
-			    if (*decided == not
-				    && strcmp (_NL_CURRENT (LC_TIME, DAY_1 + cnt),
-					__weekday_name[cnt]))
-				*decided = loc;
-			    break;
-			}
-			if (match_string (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt), rp))
-			{
-			    if (*decided == not
-				    && strcmp (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt),
-					__ab_weekday_name[cnt]))
-				*decided = loc;
-			    break;
-			}
-		    }
-#endif
-		    if (*decided != loc
-			    && (match_string (__weekday_name[cnt], rp)
-				|| match_string (__ab_weekday_name[cnt], rp)))
-		    {
-			*decided = raw;
-			break;
-		    }
-		}
-		if (cnt == 7)
-		    /* Does not match a weekday name.  */
-		    return NULL;
-		tm->tm_wday = cnt;
-		have_wday = 1;
-		break;
-	    case 'b':
-	    case 'B':
-	    case 'h':
-		/* Match month name.  */
-		for (cnt = 0; cnt < 12; ++cnt)
-		{
-#ifdef __UCLIBC_HAS_LOCALE__
-		    if (*decided !=raw)
-		    {
-			if (match_string (_NL_CURRENT (LC_TIME, MON_1 + cnt), rp))
-			{
-			    if (*decided == not
-				    && strcmp (_NL_CURRENT (LC_TIME, MON_1 + cnt),
-					__month_name[cnt]))
-				*decided = loc;
-			    break;
-			}
-			if (match_string (_NL_CURRENT (LC_TIME, ABMON_1 + cnt), rp))
-			{
-			    if (*decided == not
-				    && strcmp (_NL_CURRENT (LC_TIME, ABMON_1 + cnt),
-					__ab_month_name[cnt]))
-				*decided = loc;
-			    break;
-			}
-		    }
-#endif
-		    if (match_string (__month_name[cnt], rp)
-			    || match_string (__ab_month_name[cnt], rp))
-		    {
-			*decided = raw;
-			break;
-		    }
-		}
-		if (cnt == 12)
-		    /* Does not match a month name.  */
-		    return NULL;
-		tm->tm_mon = cnt;
-		want_xday = 1;
-		break;
-	    case 'c':
-		/* Match locale's date and time format.  */
-#ifdef __UCLIBC_HAS_LOCALE__
-		if (*decided != raw)
-		{
-		    if (!recursive (_NL_CURRENT (LC_TIME, D_T_FMT)))
-		    {
-			if (*decided == loc)
-			    return NULL;
-			else
-			    rp = rp_backup;
-		    }
-		    else
-		    {
-			if (*decided == not &&
-				strcmp (_NL_CURRENT (LC_TIME, D_T_FMT), HERE_D_T_FMT))
-			    *decided = loc;
-			want_xday = 1;
-			break;
-		    }
-		    *decided = raw;
-		}
-#endif
-		if (!recursive (HERE_D_T_FMT))
-		    return NULL;
-		want_xday = 1;
-		break;
-	    case 'C':
-		/* Match century number.  */
-#ifdef __UCLIBC_HAS_LOCALE__
-match_century:
-#endif
-		get_number (0, 99, 2);
-		century = val;
-		want_xday = 1;
-		break;
-	    case 'd':
-	    case 'e':
-		/* Match day of month.  */
-		get_number (1, 31, 2);
-		tm->tm_mday = val;
-		have_mday = 1;
-		want_xday = 1;
-		break;
-	    case 'F':
-		if (!recursive ("%Y-%m-%d"))
-		    return NULL;
-		want_xday = 1;
-		break;
-	    case 'x':
-#ifdef __UCLIBC_HAS_LOCALE__
-		if (*decided != raw)
-		{
-		    if (!recursive (_NL_CURRENT (LC_TIME, D_FMT)))
-		    {
-			if (*decided == loc)
-			    return NULL;
-			else
-			    rp = rp_backup;
-		    }
-		    else
-		    {
-			if (*decided == not
-				&& strcmp (_NL_CURRENT (LC_TIME, D_FMT), HERE_D_FMT))
-			    *decided = loc;
-			want_xday = 1;
-			break;
-		    }
-		    *decided = raw;
-		}
-#endif
-		/* Fall through.  */
-	    case 'D':
-		/* Match standard day format.  */
-		if (!recursive (HERE_D_FMT))
-		    return NULL;
-		want_xday = 1;
-		break;
-	    case 'k':
-	    case 'H':
-		/* Match hour in 24-hour clock.  */
-		get_number (0, 23, 2);
-		tm->tm_hour = val;
-		have_I = 0;
-		break;
-	    case 'l':
-		/* Match hour in 12-hour clock.  GNU extension.  */
-	    case 'I':
-		/* Match hour in 12-hour clock.  */
-		get_number (1, 12, 2);
-		tm->tm_hour = val % 12;
-		have_I = 1;
-		break;
-	    case 'j':
-		/* Match day number of year.  */
-		get_number (1, 366, 3);
-		tm->tm_yday = val - 1;
-		have_yday = 1;
-		break;
-	    case 'm':
-		/* Match number of month.  */
-		get_number (1, 12, 2);
-		tm->tm_mon = val - 1;
-		have_mon = 1;
-		want_xday = 1;
-		break;
-	    case 'M':
-		/* Match minute.  */
-		get_number (0, 59, 2);
-		tm->tm_min = val;
-		break;
-	    case 'n':
-	    case 't':
-		/* Match any white space.  */
-		while (isspace (*rp))
-		    ++rp;
-		break;
-	    case 'p':
-		/* Match locale's equivalent of AM/PM.  */
-#ifdef __UCLIBC_HAS_LOCALE__
-		if (*decided != raw)
-		{
-		    if (match_string (_NL_CURRENT (LC_TIME, AM_STR), rp))
-		    {
-			if (strcmp (_NL_CURRENT (LC_TIME, AM_STR), HERE_AM_STR))
-			    *decided = loc;
-			break;
-		    }
-		    if (match_string (_NL_CURRENT (LC_TIME, PM_STR), rp))
-		    {
-			if (strcmp (_NL_CURRENT (LC_TIME, PM_STR), HERE_PM_STR))
-			    *decided = loc;
-			is_pm = 1;
-			break;
-		    }
-		    *decided = raw;
-		}
-#endif
-		if (!match_string (HERE_AM_STR, rp))
-		    if (match_string (HERE_PM_STR, rp))
-			is_pm = 1;
-		    else
-			return NULL;
-		break;
-	    case 'r':
-#ifdef __UCLIBC_HAS_LOCALE__
-		if (*decided != raw)
-		{
-		    if (!recursive (_NL_CURRENT (LC_TIME, T_FMT_AMPM)))
-		    {
-			if (*decided == loc)
-			    return NULL;
-			else
-			    rp = rp_backup;
-		    }
-		    else
-		    {
-			if (*decided == not &&
-				strcmp (_NL_CURRENT (LC_TIME, T_FMT_AMPM),
-				    HERE_T_FMT_AMPM))
-			    *decided = loc;
-			break;
-		    }
-		    *decided = raw;
-		}
-#endif
-		if (!recursive (HERE_T_FMT_AMPM))
-		    return NULL;
-		break;
-	    case 'R':
-		if (!recursive ("%H:%M"))
-		    return NULL;
-		break;
-	    case 's':
-		{
-		    /* The number of seconds may be very high so we cannot use
-		       the `get_number' macro.  Instead read the number
-		       character for character and construct the result while
-		       doing this.  */
-		    time_t secs = 0;
-		    if (*rp < '0' || *rp > '9')
-			/* We need at least one digit.  */
-			return NULL;
-
-		    do
-		    {
-			secs *= 10;
-			secs += *rp++ - '0';
-		    }
-		    while (*rp >= '0' && *rp <= '9');
-
-		    if (localtime_r (&secs, tm) == NULL)
-			/* Error in function.  */
-			return NULL;
-		}
-		break;
-	    case 'S':
-		get_number (0, 61, 2);
-		tm->tm_sec = val;
-		break;
-	    case 'X':
-#ifdef __UCLIBC_HAS_LOCALE__
-		if (*decided != raw)
-		{
-		    if (!recursive (_NL_CURRENT (LC_TIME, T_FMT)))
-		    {
-			if (*decided == loc)
-			    return NULL;
-			else
-			    rp = rp_backup;
-		    }
-		    else
-		    {
-			if (strcmp (_NL_CURRENT (LC_TIME, T_FMT), HERE_T_FMT))
-			    *decided = loc;
-			break;
-		    }
-		    *decided = raw;
-		}
-#endif
-		/* Fall through.  */
-	    case 'T':
-		if (!recursive (HERE_T_FMT))
-		    return NULL;
-		break;
-	    case 'u':
-		get_number (1, 7, 1);
-		tm->tm_wday = val % 7;
-		have_wday = 1;
-		break;
-	    case 'g':
-		get_number (0, 99, 2);
-		/* XXX This cannot determine any field in TM.  */
-		break;
-	    case 'G':
-		if (*rp < '0' || *rp > '9')
-		    return NULL;
-		/* XXX Ignore the number since we would need some more
-		   information to compute a real date.  */
-		do
-		    ++rp;
-		while (*rp >= '0' && *rp <= '9');
-		break;
-	    case 'U':
-		get_number (0, 53, 2);
-		week_no = val;
-		have_uweek = 1;
-		break;
-	    case 'W':
-		get_number (0, 53, 2);
-		week_no = val;
-		have_wweek = 1;
-		break;
-	    case 'V':
-		get_number (0, 53, 2);
-		/* XXX This cannot determine any field in TM without some
-		   information.  */
-		break;
-	    case 'w':
-		/* Match number of weekday.  */
-		get_number (0, 6, 1);
-		tm->tm_wday = val;
-		have_wday = 1;
-		break;
-	    case 'y':
-#ifdef __UCLIBC_HAS_LOCALE__
-match_year_in_century:
-#endif
-		/* Match year within century.  */
-		get_number (0, 99, 2);
-		/* The "Year 2000: The Millennium Rollover" paper suggests that
-		   values in the range 69-99 refer to the twentieth century.  */
-		tm->tm_year = val >= 69 ? val : val + 100;
-		/* Indicate that we want to use the century, if specified.  */
-		want_century = 1;
-		want_xday = 1;
-		break;
-	    case 'Y':
-		/* Match year including century number.  */
-		get_number (0, 9999, 4);
-		tm->tm_year = val - 1900;
-		want_century = 0;
-		want_xday = 1;
-		break;
-	    case 'Z':
-		/* XXX How to handle this?  */
-		break;
-	    case 'E':
-#ifdef __UCLIBC_HAS_LOCALE__
-		switch (*fmt++)
-		{
-		    case 'c':
-			/* Match locale's alternate date and time format.  */
-			if (*decided != raw)
-			{
-			    const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_T_FMT);
-
-			    if (*fmt == '\0')
-				fmt = _NL_CURRENT (LC_TIME, D_T_FMT);
-
-			    if (!recursive (fmt))
-			    {
-				if (*decided == loc)
-				    return NULL;
-				else
-				    rp = rp_backup;
-			    }
-			    else
-			    {
-				if (strcmp (fmt, HERE_D_T_FMT))
-				    *decided = loc;
-				want_xday = 1;
-				break;
-			    }
-			    *decided = raw;
-			}
-			/* The C locale has no era information, so use the
-			   normal representation.  */
-			if (!recursive (HERE_D_T_FMT))
-			    return NULL;
-			want_xday = 1;
-			break;
-		    case 'C':
-#ifdef ENABLE_ERA_JUNK
-			if (*decided != raw)
-			{
-			    if (era_cnt >= 0)
-			    {
-				era = _nl_select_era_entry (era_cnt);
-				if (match_string (era->era_name, rp))
-				{
-				    *decided = loc;
-				    break;
-				}
-				else
-				    return NULL;
-			    }
-			    else
-			    {
-				num_eras = _NL_CURRENT_WORD (LC_TIME,
-					_NL_TIME_ERA_NUM_ENTRIES);
-				for (era_cnt = 0; era_cnt < (int) num_eras;
-					++era_cnt, rp = rp_backup)
-				{
-				    era = _nl_select_era_entry (era_cnt);
-				    if (match_string (era->era_name, rp))
-				    {
-					*decided = loc;
-					break;
-				    }
-				}
-				if (era_cnt == (int) num_eras)
-				{
-				    era_cnt = -1;
-				    if (*decided == loc)
-					return NULL;
-				}
-				else
-				    break;
-			    }
-
-			    *decided = raw;
-			}
-#endif
-			/* The C locale has no era information, so use the
-			   normal representation.  */
-			goto match_century;
-		    case 'y':
-			if (*decided == raw)
-			    goto match_year_in_century;
-
-			get_number(0, 9999, 4);
-			tm->tm_year = val;
-			want_era = 1;
-			want_xday = 1;
-			want_century = 1;
-			break;
-		    case 'Y':
-#ifdef ENABLE_ERA_JUNK
-			if (*decided != raw)
-			{
-			    num_eras = _NL_CURRENT_WORD (LC_TIME,
-				    _NL_TIME_ERA_NUM_ENTRIES);
-			    for (era_cnt = 0; era_cnt < (int) num_eras;
-				    ++era_cnt, rp = rp_backup)
-			    {
-				era = _nl_select_era_entry (era_cnt);
-				if (recursive (era->era_format))
-				    break;
-			    }
-			    if (era_cnt == (int) num_eras)
-			    {
-				era_cnt = -1;
-				if (*decided == loc)
-				    return NULL;
-				else
-				    rp = rp_backup;
-			    }
-			    else
-			    {
-				*decided = loc;
-				era_cnt = -1;
-				break;
-			    }
-
-			    *decided = raw;
-			}
-#endif
-			get_number (0, 9999, 4);
-			tm->tm_year = val - 1900;
-			want_century = 0;
-			want_xday = 1;
-			break;
-		    case 'x':
-			if (*decided != raw)
-			{
-			    const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_FMT);
-
-			    if (*fmt == '\0')
-				fmt = _NL_CURRENT (LC_TIME, D_FMT);
-
-			    if (!recursive (fmt))
-			    {
-				if (*decided == loc)
-				    return NULL;
-				else
-				    rp = rp_backup;
-			    }
-			    else
-			    {
-				if (strcmp (fmt, HERE_D_FMT))
-				    *decided = loc;
-				break;
-			    }
-			    *decided = raw;
-			}
-			if (!recursive (HERE_D_FMT))
-			    return NULL;
-			break;
-		    case 'X':
-			if (*decided != raw)
-			{
-			    const char *fmt = _NL_CURRENT (LC_TIME, ERA_T_FMT);
-
-			    if (*fmt == '\0')
-				fmt = _NL_CURRENT (LC_TIME, T_FMT);
-
-			    if (!recursive (fmt))
-			    {
-				if (*decided == loc)
-				    return NULL;
-				else
-				    rp = rp_backup;
-			    }
-			    else
-			    {
-				if (strcmp (fmt, HERE_T_FMT))
-				    *decided = loc;
-				break;
-			    }
-			    *decided = raw;
-			}
-			if (!recursive (HERE_T_FMT))
-			    return NULL;
-			break;
-		    default:
-			return NULL;
-		}
-		break;
-#else
-		/* We have no information about the era format.  Just use
-		   the normal format.  */
-		if (*fmt != 'c' && *fmt != 'C' && *fmt != 'y' && *fmt != 'Y'
-			&& *fmt != 'x' && *fmt != 'X')
-		    /* This is an illegal format.  */
-		    return NULL;
-
-		goto start_over;
-#endif
-	    case 'O':
-		switch (*fmt++)
-		{
-		    case 'd':
-		    case 'e':
-			/* Match day of month using alternate numeric symbols.  */
-			get_alt_number (1, 31, 2);
-			tm->tm_mday = val;
-			have_mday = 1;
-			want_xday = 1;
-			break;
-		    case 'H':
-			/* Match hour in 24-hour clock using alternate numeric
-			   symbols.  */
-			get_alt_number (0, 23, 2);
-			tm->tm_hour = val;
-			have_I = 0;
-			break;
-		    case 'I':
-			/* Match hour in 12-hour clock using alternate numeric
-			   symbols.  */
-			get_alt_number (1, 12, 2);
-			tm->tm_hour = val % 12;
-			have_I = 1;
-			break;
-		    case 'm':
-			/* Match month using alternate numeric symbols.  */
-			get_alt_number (1, 12, 2);
-			tm->tm_mon = val - 1;
-			have_mon = 1;
-			want_xday = 1;
-			break;
-		    case 'M':
-			/* Match minutes using alternate numeric symbols.  */
-			get_alt_number (0, 59, 2);
-			tm->tm_min = val;
-			break;
-		    case 'S':
-			/* Match seconds using alternate numeric symbols.  */
-			get_alt_number (0, 61, 2);
-			tm->tm_sec = val;
-			break;
-		    case 'U':
-			get_alt_number (0, 53, 2);
-			week_no = val;
-			have_uweek = 1;
-			break;
-		    case 'W':
-			get_alt_number (0, 53, 2);
-			week_no = val;
-			have_wweek = 1;
-			break;
-		    case 'V':
-			get_alt_number (0, 53, 2);
-			/* XXX This cannot determine any field in TM without
-			   further information.  */
-			break;
-		    case 'w':
-			/* Match number of weekday using alternate numeric symbols.  */
-			get_alt_number (0, 6, 1);
-			tm->tm_wday = val;
-			have_wday = 1;
-			break;
-		    case 'y':
-			/* Match year within century using alternate numeric symbols.  */
-			get_alt_number (0, 99, 2);
-			tm->tm_year = val >= 69 ? val : val + 100;
-			want_xday = 1;
-			break;
-		    default:
-			return NULL;
-		}
-		break;
-	    default:
-		return NULL;
-	}
-    }
-
-    if (have_I && is_pm)
-	tm->tm_hour += 12;
-
-    if (century != -1)
-    {
-	if (want_century)
-	    tm->tm_year = tm->tm_year % 100 + (century - 19) * 100;
-	else
-	    /* Only the century, but not the year.  Strange, but so be it.  */
-	    tm->tm_year = (century - 19) * 100;
-    }
-
-#ifdef ENABLE_ERA_JUNK
-    if (era_cnt != -1)
-    {
-	era = _nl_select_era_entry (era_cnt);
-	if (want_era)
-	    tm->tm_year = (era->start_date[0]
-		    + ((tm->tm_year - era->offset)
-			* era->absolute_direction));
-	else
-	    /* Era start year assumed.  */
-	    tm->tm_year = era->start_date[0];
-    }
-    else
-#endif
-	if (want_era)
-	{
-	    /* No era found but we have seen an E modifier.  Rectify some
-	       values.  */
-	    if (want_century && century == -1 && tm->tm_year < 69)
-		tm->tm_year += 100;
-	}
-
-    if (want_xday && !have_wday)
-    {
-	if ( !(have_mon && have_mday) && have_yday)
-	{
-	    /* We don't have tm_mon and/or tm_mday, compute them.  */
-	    int t_mon = 0;
-	    while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon] <= tm->tm_yday)
-		t_mon++;
-	    if (!have_mon)
-		tm->tm_mon = t_mon - 1;
-	    if (!have_mday)
-		tm->tm_mday =
-		    (tm->tm_yday
-		     - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1);
-	}
-	day_of_the_week (tm);
-    }
-
-    if (want_xday && !have_yday)
-	day_of_the_year (tm);
-
-    if ((have_uweek || have_wweek) && have_wday)
-    {
-	int save_wday = tm->tm_wday;
-	int save_mday = tm->tm_mday;
-	int save_mon = tm->tm_mon;
-	int w_offset = have_uweek ? 0 : 1;
-
-	tm->tm_mday = 1;
-	tm->tm_mon = 0;
-	day_of_the_week (tm);
-	if (have_mday)
-	    tm->tm_mday = save_mday;
-	if (have_mon)
-	    tm->tm_mon = save_mon;
-
-	if (!have_yday)
-	    tm->tm_yday = ((7 - (tm->tm_wday - w_offset)) % 7
-		    + (week_no - 1) *7
-		    + save_wday - w_offset);
-
-	if (!have_mday || !have_mon)
-	{
-	    int t_mon = 0;
-	    while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon]
-		    <= tm->tm_yday)
-		t_mon++;
-	    if (!have_mon)
-		tm->tm_mon = t_mon - 1;
-	    if (!have_mday)
-		tm->tm_mday =
-		    (tm->tm_yday
-		     - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1);
-	}
-
-	tm->tm_wday = save_wday;
-    }
-
-    return (char *) rp;
-}
-
-
-char * strptime (buf, format, tm)
-     const char *buf;
-     const char *format;
-     struct tm *tm;
-{
-    enum locale_status decided;
-
-#ifdef __UCLIBC_HAS_LOCALE__
-    decided = not;
-#else
-    decided = raw;
-#endif
-    return __strptime_internal (buf, format, tm, &decided, -1);
-}

+ 1864 - 0
libc/misc/time/time.c

@@ -0,0 +1,1864 @@
+/*  Copyright (C) 2002     Manuel Novoa III
+ *
+ *  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.
+ *
+ *  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.
+ */
+
+/*  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.
+ *       Similarly, clock_t is assumed to be long int.
+ *
+ * Warning: Assumptions are made about the layout of struct tm!  It is
+ *    assumed that the initial fields of struct tm are (in order):
+ *    tm_sec, tm_min, tm_hour, tm_mday, tm_mon, tm_year, tm_wday, tm_yday
+ *
+ * Reached the inital goal of supporting the ANSI/ISO C99 time functions
+ * as well as SUSv3's strptime.  All timezone info is obtained from the
+ * TZ env variable.
+ *
+ * Differences from glibc worth noting:
+ *
+ * Leap seconds are not considered here.
+ *
+ * glibc stores additional timezone info the struct tm, whereas we don't.
+ *
+ * Alternate digits and era handling are not currently implemented.
+ * The modifiers are accepted, and tested for validity with the following
+ * specifier, but are ignored otherwise.
+ *
+ * strftime does not implement glibc extension modifiers or widths for
+ *     conversion specifiers.  However it does implement the glibc
+ *     extension specifiers %l, %k, and %s.  It also recognizes %P, but
+ *     treats it as a synonym for %p; i.e. doesn't convert to lower case.
+ *
+ * strptime implements the glibc extension specifiers.  However, it follows
+ *     SUSv3 in requiring at least one non-alphanumeric char between
+ *     conversion specifiers.  Also, strptime only sets struct tm fields
+ *     for which format specifiers appear and does not try to infer other
+ *     fields (such as wday) as glibc's version does.
+ *
+ * TODO - Since glibc's %l and %k can space-pad their output in strftime,
+ *     it might be reasonable to eat whitespace first for those specifiers.
+ *     This could be done by pushing " %I" and " %H" respectively so that
+ *     leading whitespace is consumed.  This is really only an issue if %l
+ *     or %k occurs at the start of the format string.
+ *
+ * TODO - Implement getdate? tzfile? struct tm extensions?
+ *
+ * TODO - Rework _time_mktime to remove the dependency on long long.
+ *
+ * TODO - Make tzset and _time_tzinfo refs threadsafe.
+ */
+
+#define _GNU_SOURCE
+#define _STDIO_UTILITY
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <time.h>
+#include <limits.h>
+#include <assert.h>
+#include <errno.h>
+#include <ctype.h>
+#include <langinfo.h>
+#include <locale.h>
+
+#ifndef __isleap
+#define __isleap(y) ( !((y) % 4) && ( ((y) % 100) || !((y) % 400) ) )
+#endif
+
+#ifndef TZNAME_MAX
+#define TZNAME_MAX _POSIX_TZNAME_MAX
+#endif
+
+/* TODO - This stuff belongs in some include/bits/ file. */
+#ifndef __BCC__
+#undef CLK_TCK
+#if (TARGET_ARCH == alpha) || (TARGET_ARCH == ia64)
+#define CLK_TCK     1024
+#else
+#define CLK_TCK     100
+#endif
+#endif
+
+/* The era code is currently unfinished. */
+/*  #define ENABLE_ERA_CODE */
+
+extern struct tm __time_tm;
+
+typedef struct {
+	long gmt_offset;
+	long dst_offset;
+	short day;					/* for J or normal */
+	short week;
+	short month;
+	short rule_type;			/* J, M, \0 */
+	char tzname[TZNAME_MAX+1];
+} rule_struct;
+
+extern rule_struct _time_tzinfo[2];
+
+extern struct tm *_time_t2tm(const time_t *__restrict timer,
+							 int offset, struct tm *__restrict result);
+
+extern time_t _time_mktime(struct tm *timeptr, int store_on_success);
+
+/**********************************************************************/
+#ifdef L_asctime
+
+static char __time_str[26];
+
+char *asctime(const struct tm *__restrict ptm)
+{
+	return asctime_r(ptm, __time_str);
+}
+
+#endif
+/**********************************************************************/
+#ifdef L_asctime_r
+
+/* Strictly speaking, this implementation isn't correct.  ANSI/ISO specifies
+ * that the implementation of asctime() be equivalent to
+ *
+ *   char *asctime(const struct tm *timeptr)
+ *   {
+ *       static char wday_name[7][3] = {
+ *           "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
+ *       };
+ *       static char mon_name[12][3] = {
+ *           "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ *           "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" 
+ *       };
+ *       static char result[26];
+ *   
+ *       sprintf(result, "%.3s %.3s%3d %.2d:%.2d:%.2d %d\n",
+ *           wday_name[timeptr->tm_wday],                   
+ *           mon_name[timeptr->tm_mon],
+ *           timeptr->tm_mday, timeptr->tm_hour,
+ *           timeptr->tm_min, timeptr->tm_sec,  
+ *           1900 + timeptr->tm_year);        
+ *       return result;
+ *   }
+ *
+ * but the above is either inherently unsafe, or carries with it the implicit
+ * assumption that all fields of timeptr fall within their usual ranges, and
+ * that the tm_year value falls in the range [-2899,8099] to avoid overflowing
+ * the static buffer.
+ *
+ * If we take the implicit assumption as given, then the implementation below
+ * is still incorrect for tm_year values < -900, as there will be either
+ * 0-padding and/or a missing negative sign for the year conversion .  But given
+ * the ususal use of asctime(), I think it isn't unreasonable to restrict correct
+ * operation to the domain of years between 1000 and 9999.
+ */
+
+/* This is generally a good thing, but if you're _sure_ any data passed will be
+ * in range, you can #undef this. */
+#define SAFE_ASCTIME_R		1
+
+static const unsigned char at_data[] = {
+	'S', 'u', 'n', 'M', 'o', 'n', 'T', 'u', 'e', 'W', 'e', 'd',
+	'T', 'h', 'u', 'F', 'r', 'i', 'S', 'a', 't',
+
+	'J', 'a', 'n', 'F', 'e', 'b', 'M', 'a', 'r', 'A', 'p', 'r',
+	'M', 'a', 'y', 'J', 'u', 'n', 'J', 'u', 'l', 'A', 'u', 'g',
+	'S', 'e', 'p', 'O', 'c', 't', 'N', 'o', 'v', 'D', 'e', 'c', 
+
+#ifdef SAFE_ASCTIME_R
+	'?', '?', '?', 
+#endif
+	' ', '?', '?', '?',
+	' ', '0',
+	offsetof(struct tm, tm_mday),
+	' ', '0',
+	offsetof(struct tm, tm_hour),
+	':', '0',
+	offsetof(struct tm, tm_min),
+	':', '0',
+	offsetof(struct tm, tm_sec),
+	' ', '?', '?', '?', '?', '\n', 0
+};
+
+char *asctime_r(register const struct tm *__restrict ptm,
+				register char *__restrict buffer)
+{
+	int tmp;
+
+	assert(ptm);
+	assert(buffer);
+
+#ifdef SAFE_ASCTIME_R
+	memcpy(buffer, at_data + 3*(7 + 12), sizeof(at_data) - 3*(7 + 12));
+
+	if (((unsigned int)(ptm->tm_wday)) <= 6) {
+		memcpy(buffer, at_data + 3 * ptm->tm_wday, 3);
+	}
+
+	if (((unsigned int)(ptm->tm_mon)) <= 11) {
+		memcpy(buffer + 4, at_data + 3*7 + 3 * ptm->tm_mon, 3);
+	}
+#else
+	assert(((unsigned int)(ptm->tm_wday)) <= 6);
+	assert(((unsigned int)(ptm->tm_mon)) <= 11);
+
+	memcpy(buffer, at_data + 3*(7 + 12) - 3, sizeof(at_data) + 3 - 3*(7 + 12));
+
+	memcpy(buffer, at_data + 3 * ptm->tm_wday, 3);
+	memcpy(buffer + 4, at_data + 3*7 + 3 * ptm->tm_mon, 3);
+#endif
+
+#ifdef SAFE_ASCTIME_R
+	buffer += 19;
+	tmp = ptm->tm_year + 1900;
+	if (((unsigned int) tmp) < 10000) {
+		buffer += 4;
+		do {
+			*buffer = '0' + (tmp % 10);
+			tmp /= 10;
+		} while (*--buffer == '?');
+	}
+#else  /* SAFE_ASCTIME_R */
+	buffer += 23;
+	tmp = ptm->tm_year + 1900;
+	assert( ((unsigned int) tmp) < 10000 );
+	do {
+		*buffer = '0' + (tmp % 10);
+		tmp /= 10;
+	} while (*--buffer == '?');
+#endif /* SAFE_ASCTIME_R */
+
+	do {
+		--buffer;
+		tmp = *((int *)(((const char *) ptm) + (int) *buffer));
+#ifdef SAFE_ASCTIME_R
+		if (((unsigned int) tmp) >= 100) { /* Just check 2 digit non-neg. */
+			buffer[-1] = *buffer = '?';
+		} else
+#else  /* SAFE_ASCTIME_R */
+		assert(((unsigned int) tmp) < 100); /* Just check 2 digit non-neg. */
+#endif /* SAFE_ASCTIME_R */
+		{
+			*buffer = '0' + (tmp % 10);
+#ifdef __BCC__
+			buffer[-1] = '0' + (tmp/10);
+#else  /* __BCC__ */
+			buffer[-1] += (tmp/10);
+#endif /* __BCC__ */
+		}
+	} while ((buffer -= 2)[-2] == '0');
+
+	if (*++buffer == '0') {		/* Space-pad day of month. */
+		*buffer = ' ';
+	}
+
+	return buffer - 8;
+}
+
+#endif
+/**********************************************************************/
+#ifdef L_clock
+
+#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
+
+clock_t clock(void)
+{
+	struct tms xtms;
+	unsigned long t;
+
+	times(&xtms);
+	t = ((unsigned long) xtms.tms_utime) + xtms.tms_stime;
+#if (CLK_TCK == CLOCKS_PER_SEC)
+	return (t <= LONG_MAX) ? t : -1;
+#elif (CLK_TCK == 1) || (CLK_TCK == 10) || (CLK_TCK == 100) || (CLK_TCK == 1000)
+	return (t <= (LONG_MAX / (CLOCKS_PER_SEC/CLK_TCK)))
+		? t * (CLOCKS_PER_SEC/CLK_TCK)
+		: -1;
+#elif (CLK_TCK == 1024)
+	return (t <= ((LONG_MAX / CLOCKS_PER_SEC) * CLK_TCK
+				  + ((LONG_MAX % CLOCKS_PER_SEC) * CLK_TCK) / CLOCKS_PER_SEC))
+		? ((t >> 10) * CLOCKS_PER_SEC) + (((t & 1023) * CLOCKS_PER_SEC) >> 10)
+		: -1;
+#else
+#error fix for CLK_TCK
+#endif
+}
+
+#endif
+/**********************************************************************/
+#ifdef L_ctime
+
+char *ctime(const time_t *clock)
+{
+	/* ANSI/ISO/SUSv3 say that ctime is equivalent to the following. */
+	return asctime(localtime(clock));
+}
+
+#endif
+/**********************************************************************/
+#ifdef L_ctime_r
+
+char *ctime_r(const time_t *clock, char *buf)
+{
+	struct tm xtms;
+
+	return asctime_r(localtime_r(clock, &xtms), buf);
+}
+
+#endif
+/**********************************************************************/
+#ifdef L_difftime
+
+#include <float.h>
+
+#if FLT_RADIX != 2
+#error difftime implementation assumptions violated for you arch!
+#endif
+
+double difftime(time_t time1, time_t time0)
+{
+#if (LONG_MAX >> DBL_MANT_DIG) == 0
+
+	/* time_t fits in the mantissa of a double. */
+	return ((double) time1) - time0;
+
+#elif ((LONG_MAX >> DBL_MANT_DIG) >> DBL_MANT_DIG) == 0
+
+	/* time_t can overflow the mantissa of a double. */
+	time_t t1, t0, d;
+
+	d = ((time_t) 1) << DBL_MANT_DIG;
+	t1 = time1 / d;
+	time1 -= (t1 * d);
+	t0 = time0 / d;
+	time0 -= (t0*d);
+
+	/* Since FLT_RADIX==2 and d is a power of 2, the only possible
+	 * rounding error in the expression below would occur from the
+	 * addition. */
+	return (((double) t1) - t0) * d + (((double) time1) - time0);
+
+#else
+#error difftime needs special implementation on your arch.
+#endif
+}
+
+#endif
+/**********************************************************************/
+#ifdef L_gmtime
+
+struct tm *gmtime(const time_t *timer)
+{
+	register struct tm *ptm = &__time_tm;
+
+	_time_t2tm(timer, 0, ptm); /* Can return NULL... */
+
+	return ptm;
+}
+
+#endif
+/**********************************************************************/
+#ifdef L_gmtime_r
+
+struct tm *gmtime_r(const time_t *__restrict timer,
+					struct tm *__restrict result)
+{
+	return _time_t2tm(timer, 0, result);
+}
+
+#endif
+/**********************************************************************/
+#ifdef L_localtime
+
+struct tm *localtime(const time_t *timer)
+{
+	register struct tm *ptm = &__time_tm;
+
+	tzset();
+
+	localtime_r(timer, ptm);	/* Can return NULL... */
+
+	return ptm;
+}
+
+#endif
+/**********************************************************************/
+#ifdef L_localtime_r
+
+static const unsigned char day_cor[] = { /* non-leap */
+	31, 31, 34, 34, 35, 35, 36, 36, 36, 37, 37, 38, 38
+/* 	 0,  0,  3,  3,  4,  4,  5,  5,  5,  6,  6,  7,  7 */
+/*	    31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 */
+};
+
+static int tm_isdst(register const struct tm *__restrict ptm)
+{
+	register rule_struct *r = _time_tzinfo;
+	long sec;
+	int i, isdst, isleap, day, day0, monlen, mday, oday;
+
+	isdst = 0;
+	if (r[1].tzname[0] != 0) {
+		/* First, get the current seconds offset from the start of the year.
+		 * Fields of ptm are assumed to be in their normal ranges. */
+		sec = ptm->tm_sec
+			+ 60 * (ptm->tm_min
+					+ 60 * (long)(ptm->tm_hour
+								  + 24 * ptm->tm_yday));
+		/* Do some prep work. */
+		i = (ptm->tm_year % 400) + 1900; /* Make sure we don't overflow. */
+		isleap = __isleap(i);
+		--i;
+		day0 = (1
+				+ i				/* Normal years increment 1 wday. */
+				+ (i/4)
+				- (i/100)
+				+ (i/400) ) % 7;
+		i = 0;
+		do {
+			day = r->day;		/* Common for 'J' and # case. */
+			if (r->rule_type == 'J') {
+				if (!isleap || (day < (31+29))) {
+					--day;
+				}
+			} else if (r->rule_type == 'M') {
+				/* Find 0-based day number for 1st of the month. */
+				day = 31*r->month - day_cor[r->month -1];
+				if (isleap && (day >= 59)) {
+					++day;
+				}
+				monlen = 31 + day_cor[r->month -1] - day_cor[r->month];
+				if (isleap && (r->month > 1)) {
+					++monlen;
+				}
+				/* Wweekday (0 is Sunday) of 1st of the month
+				 * is (day0 + day) % 7. */
+				if ((mday = r->day - ((day0 + day) % 7)) >= 0) {
+					mday -= 7;	/* Back up into prev month since r->week>0. */
+				}
+				if ((mday += 7 * r->week) >= monlen) {
+					mday -= 7;
+				}
+				/* So, 0-based day number is... */
+				day += mday;
+			}
+
+			if (i != 0) {
+				/* Adjust sec since dst->std change time is in dst. */
+				sec += (r[-1].gmt_offset - r->gmt_offset);
+				if (oday > day) {
+					++isdst;	/* Year starts in dst. */
+				}
+			}
+			oday = day;
+
+			/* Now convert day to seconds and add offset and compare. */
+			if (sec >= (day * 86400L) + r->dst_offset) {
+				++isdst;
+			}
+			++r;
+		} while (++i < 2);
+	}
+	return (isdst & 1);
+}
+
+struct tm *localtime_r(register const time_t *__restrict timer,
+					   register struct tm *__restrict result)
+{
+	time_t x[1];
+	long offset;
+	int days, dst;
+
+	dst = 0;
+	do {
+		days = -7;
+		offset = 604800L - _time_tzinfo[dst].gmt_offset;
+		if (*timer > (LONG_MAX - 604800L)) {
+			days = -days;
+			offset = -offset;
+		}
+		*x = *timer + offset;
+
+		_time_t2tm(x, days, result);
+		
+		if (dst) {
+			result->tm_isdst = dst;
+			break;
+		}
+		++dst;
+	} while ((result->tm_isdst = tm_isdst(result)) != 0);
+
+	return result;
+}
+
+#endif
+/**********************************************************************/
+#ifdef L_mktime
+
+time_t mktime(struct tm *timeptr)
+{
+	return  _time_mktime(timeptr, 1);
+}
+
+#endif
+/**********************************************************************/
+#ifdef L_strftime
+
+#define NO_E_MOD		0x80
+#define NO_O_MOD		0x40
+
+#define ILLEGAL_SPEC	0x3f
+
+#define INT_SPEC		0x00	/* must be 0x00!! */
+#define STRING_SPEC		0x10	/* must be 0x10!! */
+#define CALC_SPEC		0x20
+#define STACKED_SPEC	0x30
+
+#define MASK_SPEC		0x30
+
+/* Compatibility:
+ *
+ * No alternate digit (%O?) handling.  Always uses 0-9.
+ * Alternate locale format (%E?) handling is broken for nontrivial ERAs.
+ * glibc's %P is currently faked by %p.  This means it doesn't do lower case.
+ * glibc's %k, %l, and %s are handled.
+ * glibc apparently allows (and ignores) extraneous 'E' and 'O' modifiers,
+ *   while they are flagged as illegal conversions here.
+ */
+
+/* Warning: Assumes ASCII values! (as do lots of other things in the lib...) */
+static const unsigned char spec[] = {
+	/* A */		0x03 |  STRING_SPEC | NO_E_MOD | NO_O_MOD,
+	/* B */		0x04 |  STRING_SPEC | NO_E_MOD | NO_O_MOD,
+	/* C */		0x0a |     INT_SPEC            | NO_O_MOD,
+	/* D */		0x02 | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
+	/* E */		       ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
+	/* F */		0x03 | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
+	/* G */		0x03 |    CALC_SPEC | NO_E_MOD | NO_O_MOD,
+	/* H */		0x0b |     INT_SPEC | NO_E_MOD,
+	/* I */		0x0c |     INT_SPEC | NO_E_MOD,
+	/* J */		       ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
+	/* K */		       ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
+	/* L */		       ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
+	/* M */		0x0d |     INT_SPEC | NO_E_MOD,
+	/* N */		       ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
+	/* O */		       ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
+	/* P */		0x05 |  STRING_SPEC | NO_E_MOD | NO_O_MOD, /* glibc ; use %p */
+	/* Q */		       ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
+	/* R */		0x04 | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
+	/* S */		0x0e |     INT_SPEC | NO_E_MOD,
+	/* T */		0x05 | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
+	/* U */		0x04 |    CALC_SPEC | NO_E_MOD,
+	/* V */		0x05 |    CALC_SPEC | NO_E_MOD,
+	/* W */		0x06 |    CALC_SPEC | NO_E_MOD,
+	/* X */		0x0a | STACKED_SPEC            | NO_O_MOD,
+	/* Y */		0x0f |     INT_SPEC            | NO_O_MOD,
+	/* Z */		0x01 |    CALC_SPEC | NO_E_MOD | NO_O_MOD,
+	'?',						/* 26 */
+	'?',						/* 27 */
+	'?',						/* 28 */
+	'?',						/* 29 */
+	0,							/* 30 */
+	0,							/* 31 */
+	/* a */		0x00 |  STRING_SPEC | NO_E_MOD | NO_O_MOD,
+	/* b */		0x01 |  STRING_SPEC | NO_E_MOD | NO_O_MOD,
+	/* c */		0x08 | STACKED_SPEC            | NO_O_MOD,
+	/* d */		0x00 |     INT_SPEC | NO_E_MOD,
+	/* e */		0x01 |     INT_SPEC | NO_E_MOD,
+	/* f */		       ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
+	/* g */		0x02 |    CALC_SPEC | NO_E_MOD | NO_O_MOD,
+	/* h */		0x01 |  STRING_SPEC | NO_E_MOD | NO_O_MOD, /* same as b */
+	/* i */		       ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
+	/* j */		0x08 |     INT_SPEC | NO_E_MOD | NO_O_MOD,
+	/* k */		0x03 |     INT_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
+	/* l */		0x04 |     INT_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
+	/* m */		0x05 |     INT_SPEC | NO_E_MOD,
+	/* n */		0x00 | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
+	/* o */		       ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
+	/* p */		0x02 |  STRING_SPEC | NO_E_MOD | NO_O_MOD,
+	/* q */		       ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
+	/* r */		0x0b | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
+	/* s */		0x07 |    CALC_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
+	/* t */		0x01 | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
+	/* u */		0x07 |     INT_SPEC | NO_E_MOD,
+	/* v */		       ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
+	/* w */		0x02 |     INT_SPEC | NO_E_MOD,
+	/* x */		0x09 | STACKED_SPEC            | NO_O_MOD,
+	/* y */		0x09 |     INT_SPEC,
+	/* z */		0x00 |    CALC_SPEC | NO_E_MOD | NO_O_MOD,
+
+
+	/* WARNING!!! These are dependent on the layout of struct tm!!! */
+#define FIELD_MAX (26+6+26)
+	60 /* 61? */, 59, 23, 31, 11, 0 /* 9999 */, 6, 0 /* 365 */,
+
+#define TP_OFFSETS (FIELD_MAX+8)
+	3, /* d */
+	3, /* e */
+	6, /* w */
+	2, /* k */
+	2, /* l */
+	4, /* m */
+	0, /* CURRENTLY UNUSED */
+	/* NOTE: u,j,y order must be preserved as 6,7,5 seq is used in the code! */
+#define CALC_OFFSETS (TP_OFFSETS + 7)
+	6, /* u */
+	7, /* j */
+	5, /* y */
+	5, /* C */
+	2, /* H */
+	2, /* I */
+	1, /* M */
+	0, /* S */
+	5, /* Y */
+	6, /* a */
+	4, /* b, h */
+	2, /* p */
+	6, /* A */
+	4, /* B */
+	2, /* P */
+
+#define TP_CODES (TP_OFFSETS + 16 + 6)
+	2 | 16, /* d */
+	2, /* e */
+	0 | 16, /* w */
+	2, /* k */
+	2 | 32 | 0, /* l */
+	2 | 16 | 1, /* m */
+	0, /* CURRENTLY UNUSED */
+	0 | 16 | 8 , /* u */
+	4 | 16 | 1, /* j */
+	2 | 128 | 32 | 16 , /* y */
+	2 | 128 | 64 | 32 | 16 , /* C */
+	2 | 16, /* H */
+	2 | 32 | 16 | 0, /* I */
+	2 | 16, /* M */
+	2 | 16, /* S */
+	6 | 16, /* Y */
+	2, /* a */
+	2, /* b, h */
+	2 | 64, /* p */
+	2, /* A */
+	2, /* B */
+	2 | 64, /* P */
+
+#define STRINGS_NL_ITEM_START (TP_CODES + 16 + 6)
+	_NL_ITEM_INDEX(ABDAY_1),	/* a */
+	_NL_ITEM_INDEX(ABMON_1),	/* b, h */
+	_NL_ITEM_INDEX(AM_STR),		/* p */
+	_NL_ITEM_INDEX(DAY_1),		/* A */
+	_NL_ITEM_INDEX(MON_1),		/* B */
+	_NL_ITEM_INDEX(AM_STR),		/* P -- wrong! need lower case */
+
+#define STACKED_STRINGS_START (STRINGS_NL_ITEM_START+6)
+	6, 7, 8, 16, 24, 29,		/* 6 - offsets from offset-count to strings */
+	'\n', 0,					/* 2 */
+	'\t', 0,					/* 2 */
+	'%', 'm', '/', '%', 'd', '/', '%', 'y', 0, /* 9 - %D */
+	'%', 'Y', '-', '%', 'm', '-', '%', 'd', 0, /* 9 - %F (glibc extension) */
+	'%', 'H', ':', '%', 'M', 0,	/* 6 - %R*/
+	'%', 'H', ':', '%', 'M', ':', '%', 'S', 0, /* 9 - %T */
+
+#define STACKED_STRINGS_NL_ITEM_START (STACKED_STRINGS_START + 43)
+	_NL_ITEM_INDEX(D_T_FMT),	/* c */
+	_NL_ITEM_INDEX(D_FMT),		/* x */
+	_NL_ITEM_INDEX(T_FMT),		/* X */
+	_NL_ITEM_INDEX(T_FMT_AMPM), /* r */
+#ifdef ENABLE_ERA_CODE
+	_NL_ITEM_INDEX(ERA_D_T_FMT), /* Ec */
+	_NL_ITEM_INDEX(ERA_D_FMT),	/* Ex */
+	_NL_ITEM_INDEX(ERA_T_FMT),	/* EX */
+#endif
+};
+
+static int load_field(int k, const struct tm *__restrict timeptr)
+{
+	int r;
+	int r_max;
+
+	r = ((int *) timeptr)[k];
+
+	r_max = spec[FIELD_MAX + k];
+
+	if (k == 7) {
+		r_max = 365;
+	} else if (k == 5) {
+		r += 1900;
+		r_max = 9999;
+	}
+
+	if ((((unsigned int) r) > r_max) || ((k == 3) && !r)) {
+		r = -1;
+	}
+
+	return r;
+}
+
+#define MAX_PUSH 4
+
+size_t strftime(char *__restrict s, size_t maxsize,
+				const char *__restrict format,
+				const struct tm *__restrict timeptr)
+{
+	long tzo;
+	register const char *p;
+	register const char *o;
+	const rule_struct *rsp;
+	const char *stack[MAX_PUSH];
+	size_t count;
+	size_t o_count;
+	int field_val, i, j, lvl;
+	int x[3];			/* wday, yday, year */
+	int isofm, days;
+	char buf[__UIM_BUFLEN_LONG];
+	unsigned char mod;
+	unsigned char code;
+
+	tzset();					/* We'll, let's get this out of the way. */
+
+	lvl = 0;
+	p = format;
+	count = maxsize;
+
+ LOOP:
+	if (!count) {
+		return 0;
+	}
+	if (!*p) {
+		if (lvl == 0) {
+			*s = 0;				/* nul-terminate */
+			return maxsize - count;
+		}
+		p = stack[--lvl];
+		goto LOOP;
+	}
+
+	o_count = 1;
+	if ((*(o = p) == '%') && (*++p != '%')) {
+		o_count = 2;
+		mod = ILLEGAL_SPEC;
+		if ((*p == 'O') || (*p == 'E')) { /* modifier */
+			mod |= ((*p == 'O') ? NO_O_MOD : NO_E_MOD);
+			++o_count;
+			++p;
+		}
+		if ((((unsigned char)(((*p) | 0x20) - 'a')) >= 26)
+			|| (((code = spec[(int)(*p - 'A')]) & mod) >= ILLEGAL_SPEC)
+			) {
+			if (!*p) {
+				--p;
+				--o_count;
+			}
+			goto OUTPUT;
+		}
+		code &= ILLEGAL_SPEC;	/* modifiers are preserved in mod var. */
+
+		if ((code & MASK_SPEC) == STACKED_SPEC) {
+			if (lvl == MAX_PUSH) {
+				goto OUTPUT;	/* Stack full so treat as illegal spec. */
+			}
+			stack[lvl++] = ++p;
+			if ((code &= 0xf) < 8) {
+				p = ((const char *) spec) + STACKED_STRINGS_START + code;
+				p += *((unsigned char *)p);
+				goto LOOP;
+			}
+			p = ((const char *) spec) + STACKED_STRINGS_NL_ITEM_START
+				+ (code & 7);
+#ifdef ENABLE_ERA_CODE
+			if ((mod & NO_E_MOD) /* Actually, this means E modifier present. */
+				&& (*(o = nl_langinfo(_NL_ITEM(LC_TIME,
+											   (int)(((unsigned char *)p)[4]))
+									  )))
+				) {
+				p = o;
+				goto LOOP;
+			}
+#endif
+			p = nl_langinfo(_NL_ITEM(LC_TIME,
+									 (int)(*((unsigned char *)p))));
+			goto LOOP;
+		}
+
+		o = spec + 26;		/* set to "????" */
+		if ((code & MASK_SPEC) == CALC_SPEC) {
+
+			if (*p == 's') {
+				time_t t;
+
+				/* Use a cast to silence the warning since *timeptr won't
+				 * be changed. */
+				if ((t = _time_mktime((struct tm *) timeptr, 0))
+					== ((time_t) -1)
+					) {
+					o_count = 1;
+					goto OUTPUT;
+				}
+#ifdef TIME_T_IS_UNSIGNED
+				o = _uintmaxtostr(buf + sizeof(buf) - 1,
+								  (uintmax_t) t,
+								  10, __UIM_DECIMAL);
+#else
+				o = _uintmaxtostr(buf + sizeof(buf) - 1,
+								  (uintmax_t) t,
+								  -10, __UIM_DECIMAL);
+#endif
+				o_count = sizeof(buf);
+				goto OUTPUT;
+			} else if (((*p) | 0x20) == 'z') { /* 'z' or 'Z' */
+
+				if (timeptr->tm_isdst < 0) {
+					/* SUSv3 specifies this behavior for 'z', but we'll also
+					 * treat it as "no timezone info" for 'Z' too. */
+					o_count = 0;
+					goto OUTPUT;
+				}
+
+				rsp = _time_tzinfo;
+				if (timeptr->tm_isdst > 0) {
+					++rsp;
+				}
+
+				if (*p == 'Z') {
+					o = rsp->tzname;
+					assert(o != NULL);
+#if 0
+					if (!o) {	/* PARANOIA */
+						o = spec+30; /* empty string */
+					}
+#endif
+					o_count = SIZE_MAX;
+					goto OUTPUT;
+				} else {		/* z */
+					*s = '+';
+					if ((tzo = -rsp->gmt_offset) < 0) {
+						tzo = -tzo;
+						*s = '-';
+					}
+					++s;
+					--count;
+
+					i = tzo / 60;
+					field_val = ((i / 60) * 100) + (i % 60);
+			
+					i = 16 + 6;	/* 0-fill, width = 4 */
+				}
+			} else {
+				/* TODO: don't need year for U, W */
+				for (i=0 ; i < 3 ; i++) {
+					if ((x[i] = load_field(spec[CALC_OFFSETS+i],timeptr)) < 0) {
+						goto OUTPUT;
+					}
+				}
+
+				i = 16 + 2;		/* 0-fill, width = 2 */
+
+				if ((*p == 'U') || (*p == 'W')) {
+					field_val = ((x[1] - x[0]) + 7);
+					if (*p == 'W') {
+						++field_val;
+					}
+					field_val /= 7;
+					if ((*p == 'W') && !x[0]) {
+						--field_val;
+					}
+				} else {	/* ((*p == 'g') || (*p == 'G') || (*p == 'V')) */
+				ISO_LOOP:
+					isofm = (((x[1] - x[0]) + 11) % 7) - 3;	/* [-3,3] */
+
+					if (x[1] < isofm) {	/* belongs to previous year */
+						--x[2];
+						x[1] += 365 + __isleap(x[2]);
+						goto ISO_LOOP;
+					}
+
+					field_val = ((x[1] - isofm) / 7) + 1; /* week # */
+					days = 365 + __isleap(x[2]);
+					isofm = ((isofm + 7*53 + 3 - days)) %7 + days - 3; /* next year */
+					if (x[1] >= isofm) { /* next year */
+						x[1] -= days;
+						++x[2];
+						goto ISO_LOOP;
+					}
+
+					if (*p != 'V') { /* need year */
+						field_val = x[2]; /* TODO: what if x[2] now 10000 ?? */
+						if (*p == 'g') {
+							field_val %= 100;
+						} else {
+							i = 16 + 6;	/* 0-fill, width = 4 */
+						}
+					}
+				}
+			}
+		} else {
+			i = TP_OFFSETS + (code & 0x1f);
+			if ((field_val = load_field(spec[i],timeptr)) < 0) {
+				goto OUTPUT;
+			}
+
+			i = spec[i+(TP_CODES - TP_OFFSETS)];
+
+			j = (i & 128) ? 100: 12;
+			if (i & 64) {
+				field_val /= j;;
+			}
+			if (i & 32) {
+				field_val %= j;
+				if (((i&128) + field_val) == 0) { /* mod 12? == 0 */
+					field_val = j; /* set to 12 */
+				}
+			}
+			field_val += (i & 1);
+			if ((i & 8) && !field_val) {
+				field_val += 7;
+			}
+		}
+		
+		if ((code & MASK_SPEC) == STRING_SPEC) {
+			o_count = SIZE_MAX;
+			field_val += spec[STRINGS_NL_ITEM_START + (code & 0xf)];
+			o = nl_langinfo(_NL_ITEM(LC_TIME, field_val));
+		} else {
+			o_count = ((i >> 1) & 3) + 1;
+			o = buf + o_count;
+			do {
+				*(char *)(--o) = '0' + (field_val % 10);
+				field_val /= 10;
+			} while (o > buf);
+			if (*buf == '0') {
+				*buf = ' ' + (i & 16);
+			}
+		}
+	}
+
+ OUTPUT:
+	++p;
+	while (o_count && count && *o) {
+		*s++ = *o++;
+		--o_count;
+		--count;
+	}
+	goto LOOP;
+}
+
+#endif
+/**********************************************************************/
+#ifdef L_strptime
+
+/* TODO:
+ * 1) %l and %k are space-padded, so "%l" by itself fails while " %l" succeeds.
+ *    Both work for glibc.  So, should we always strip spaces?
+ * 2) %Z
+ */
+
+/* Notes:
+ * There are several differences between this strptime and glibc's strptime.
+ * 1) glibc strips leading space before numeric conversions.
+ * 2) glibc will read fields without whitespace in between.  SUSv3 states
+ *    that you must have whitespace between conversion operators.  Besides,
+ *    how do you know how long a number should be if there are leading 0s?
+ * 3) glibc attempts to compute some the struct tm fields based on the
+ *    data retrieved; tm_wday in particular.  I don't as I consider it
+ *     another glibc attempt at mind-reading...
+ */
+
+#define NO_E_MOD		0x80
+#define NO_O_MOD		0x40
+
+#define ILLEGAL_SPEC	0x3f
+
+#define INT_SPEC		0x00	/* must be 0x00!! */
+#define STRING_SPEC		0x10	/* must be 0x10!! */
+#define CALC_SPEC		0x20
+#define STACKED_SPEC	0x30
+
+#define MASK_SPEC		0x30
+
+/* Warning: Assumes ASCII values! (as do lots of other things in the lib...) */
+static const unsigned char spec[] = {
+	/* A */		0x02 |  STRING_SPEC | NO_E_MOD | NO_O_MOD,
+	/* B */		0x01 |  STRING_SPEC | NO_E_MOD | NO_O_MOD,
+	/* C */		0x08 |     INT_SPEC            | NO_O_MOD,
+	/* D */		0x01 | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
+	/* E */		       ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
+	/* F */		0x02 | STACKED_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
+	/* G */		0x0f |     INT_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
+	/* H */		0x06 |     INT_SPEC | NO_E_MOD,
+	/* I */		0x07 |     INT_SPEC | NO_E_MOD,
+	/* J */		       ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
+	/* K */		       ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
+	/* L */		       ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
+	/* M */		0x04 |     INT_SPEC | NO_E_MOD,
+	/* N */		       ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
+	/* O */		       ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
+	/* P */		0x00 |  STRING_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
+	/* Q */		       ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
+	/* R */		0x03 | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
+	/* S */		0x05 |     INT_SPEC | NO_E_MOD,
+	/* T */		0x04 | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
+	/* U */		0x0c |     INT_SPEC | NO_E_MOD,
+	/* V */		0x0d |     INT_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
+	/* W */		0x0c |     INT_SPEC | NO_E_MOD,
+	/* X */		0x0a | STACKED_SPEC            | NO_O_MOD,
+	/* Y */		0x0a |     INT_SPEC            | NO_O_MOD,
+	/* Z */		0x02 |    CALC_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
+
+	/* WARNING! This assumes orderings:
+	 *    AM,PM
+	 *    ABDAY_1-ABDAY-7,DAY_1-DAY_7
+	 *    ABMON_1-ABMON_12,MON_1-MON12
+	 * Also, there are exactly 6 bytes between 'Z' and 'a'.
+	 */
+#define STRINGS_NL_ITEM_START (26)
+	_NL_ITEM_INDEX(AM_STR),		/* p (P) */
+	_NL_ITEM_INDEX(ABMON_1),	/* B, b */
+	_NL_ITEM_INDEX(ABDAY_1),	/* A, a */
+	2,
+	24,
+	14,
+
+	/* a */		0x02 |  STRING_SPEC | NO_E_MOD | NO_O_MOD,
+	/* b */		0x01 |  STRING_SPEC | NO_E_MOD | NO_O_MOD,
+	/* c */		0x08 | STACKED_SPEC            | NO_O_MOD,
+	/* d */		0x00 |     INT_SPEC | NO_E_MOD,
+	/* e */		0x00 |     INT_SPEC | NO_E_MOD,
+	/* f */		       ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
+	/* g */		0x0e |     INT_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
+	/* h */		0x01 |  STRING_SPEC | NO_E_MOD | NO_O_MOD,
+	/* i */		       ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
+	/* j */		0x01 |     INT_SPEC | NO_E_MOD | NO_O_MOD,
+	/* k */		0x06 |     INT_SPEC | NO_E_MOD,            /* glibc */
+	/* l */		0x07 |     INT_SPEC | NO_E_MOD,            /* glibc */
+	/* m */		0x02 |     INT_SPEC | NO_E_MOD,
+	/* n */		0x00 | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
+	/* o */		       ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
+	/* p */		0x00 |  STRING_SPEC | NO_E_MOD | NO_O_MOD,
+	/* q */		       ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
+	/* r */		0x0b | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
+	/* s */		0x00 |    CALC_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
+	/* t */		0x00 | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
+	/* u */		0x0b |     INT_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
+	/* v */		       ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
+	/* w */		0x03 |     INT_SPEC | NO_E_MOD,
+	/* x */		0x09 | STACKED_SPEC            | NO_O_MOD,
+	/* y */		0x09 |     INT_SPEC,
+	/* z */		0x01 |    CALC_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
+
+#define INT_FIELD_START (26+6+26)
+	/* (field #) << 3  + lower bound (0|1) + correction 0:none, 2:-1, 4:-1900
+	 * followed by upper bound prior to correction with 1=>366 and 2=>9999. */
+	/* d, e */	(3 << 3) + 1 + 0, 31,
+	/* j */		(7 << 3) + 1 + 2, /* 366 */ 1,
+	/* m */		(4 << 3) + 1 + 2, 12,
+	/* w */		(6 << 3) + 0 + 0, 6,
+	/* M */		(1 << 3) + 0 + 0, 59,
+	/* S */		0        + 0 + 0, 60,
+	/* H (k) */	(2 << 3) + 0 + 0, 23,
+	/* I (l) */	(9 << 3) + 1 + 0, 12, /* goes with 8 -- am/pm */
+	/* C */		(10<< 3) + 0 + 0, 99,
+	/* y */		(11<< 3) + 0 + 0, 99,
+	/* Y */		(5 << 3) + 0 + 4, /* 9999 */ 2,
+	/* u */		(6 << 3) + 1 + 0, 7,
+	/* The following are processed and range-checked, but ignored otherwise. */
+	/* U, W */	(12<< 3) + 0 + 0, 53,
+	/* V */		(12<< 3) + 1 + 0, 53,
+	/* g */		(12<< 3) + 0 + 0, 99,
+	/* G */		(12<< 3) + 0 /*+ 4*/, /* 9999 */ 2, /* Note: -1 or 10000? */
+
+#define STACKED_STRINGS_START (INT_FIELD_START+32)
+	5, 6, 14, 22, 27,			/* 5 - offsets from offset-count to strings */
+	' ', 0,						/* 2 - %n or %t */
+	'%', 'm', '/', '%', 'd', '/', '%', 'y', 0, /* 9 - %D */
+	'%', 'Y', '-', '%', 'm', '-', '%', 'd', 0, /* 9 - %F (glibc extension) */
+	'%', 'H', ':', '%', 'M', 0,	/* 6 - %R*/
+	'%', 'H', ':', '%', 'M', ':', '%', 'S', 0, /* 9 - %T */
+
+#define STACKED_STRINGS_NL_ITEM_START (STACKED_STRINGS_START + 40)
+	_NL_ITEM_INDEX(D_T_FMT),	/* c */
+	_NL_ITEM_INDEX(D_FMT),		/* x */
+	_NL_ITEM_INDEX(T_FMT),		/* X */
+	_NL_ITEM_INDEX(T_FMT_AMPM), /* r */
+#ifdef ENABLE_ERA_CODE
+	_NL_ITEM_INDEX(ERA_D_T_FMT), /* Ec */
+	_NL_ITEM_INDEX(ERA_D_FMT),	/* Ex */
+	_NL_ITEM_INDEX(ERA_T_FMT),	/* EX */
+#endif
+};
+
+#define MAX_PUSH 4
+
+char *strptime(const char *__restrict buf, const char *__restrict format,
+			   struct tm *__restrict tm)
+{
+	register const char *p;
+	char *o;
+	const char *stack[MAX_PUSH];
+	int i, j, lvl;
+	int fields[13];
+	unsigned char mod;
+	unsigned char code;
+
+	i = 0;
+	do {
+		fields[i] = INT_MIN;
+	} while (++i < 13);
+
+	lvl = 0;
+	p = format;
+
+ LOOP:
+	if (!*p) {
+		if (lvl == 0) {			/* Done. */
+			if (fields[6] == 7) { /* Cleanup for %u here since just once. */
+				fields[6] = 0;	/* Don't use mod in case unset. */
+			}
+
+			i = 0;
+			do {				/* Store the values into tm. */
+				((int *) tm)[i] = fields[i];
+			} while (++i < 8);
+
+			return (char *) buf; /* Success. */
+		}
+		p = stack[--lvl];
+		goto LOOP;
+	}
+
+	if ((*p == '%') && (*++p != '%')) {
+		mod = ILLEGAL_SPEC;
+		if ((*p == 'O') || (*p == 'E')) { /* Modifier? */
+			mod |= ((*p == 'O') ? NO_O_MOD : NO_E_MOD);
+			++p;
+		}
+
+		if (!*p
+			|| (((unsigned char)(((*p) | 0x20) - 'a')) >= 26)
+			|| (((code = spec[(int)(*p - 'A')]) & mod) >= ILLEGAL_SPEC)
+			) {
+			return NULL;		/* Illegal spec. */
+		}
+
+		if ((code & MASK_SPEC) == STACKED_SPEC) {
+			if (lvl == MAX_PUSH) {
+				return NULL;	/* Stack full so treat as illegal spec. */
+			}
+			stack[lvl++] = ++p;
+			if ((code &= 0xf) < 8) {
+				p = ((const char *) spec) + STACKED_STRINGS_START + code;
+				p += *((unsigned char *)p);
+				goto LOOP;
+			}
+
+			p = ((const char *) spec) + STACKED_STRINGS_NL_ITEM_START
+				+ (code & 7);
+#ifdef ENABLE_ERA_CODE
+			if ((mod & NO_E_MOD) /* Actually, this means E modifier present. */
+				&& (*(o = nl_langinfo(_NL_ITEM(LC_TIME,
+											   (int)(((unsigned char *)p)[4]))
+									  )))
+				) {
+				p = o;
+				goto LOOP;
+			}
+#endif
+			p = nl_langinfo(_NL_ITEM(LC_TIME,
+									 (int)(*((unsigned char *)p))));
+			goto LOOP;
+		}
+
+		++p;
+
+		if ((code & MASK_SPEC) == STRING_SPEC) {
+			code &= 0xf;
+			j = spec[STRINGS_NL_ITEM_START + 3 + code];
+			i = _NL_ITEM(LC_TIME, spec[STRINGS_NL_ITEM_START + code]);
+			/* Go backwards to check full names before abreviations. */
+			do {
+				--j;
+				o = nl_langinfo(i+j);
+				if (!strncasecmp(buf,o,strlen(o)) && *o) { /* Found a match. */
+					do {
+						++buf;
+					} while (*++o);
+					if (!code) { /* am/pm */
+						fields[8] = j * 12;
+						if (fields[9] >= 0) { /* We have a previous %I or %l. */
+							fields[2] = fields[9] + fields[8];
+						}
+					} else {	/* day (4) or month (6) */
+						fields[2 + (code << 1)]
+							= j % (spec[STRINGS_NL_ITEM_START + 3 + code] >> 1);
+					}
+					goto LOOP;
+				}
+			} while (j);
+			return NULL;		/* Failed to match. */
+		}
+
+		if ((code & MASK_SPEC) == CALC_SPEC) {
+			if ((code &= 0xf) < 1) { /* s or z*/
+				time_t t;
+
+				o = (char *) buf;
+				i = errno;
+				__set_errno(0);
+				if (!isspace(*buf)) { /* Signal an error if whitespace. */
+#ifdef TIME_T_IS_UNSIGNED
+					t = strtoul(buf, &o, 10);
+#else
+					t = strtol(buf, &o, 10);
+#endif
+				}
+				if ((o == buf) || errno) { /* Not a number or overflow. */
+					return NULL;
+				}
+				__set_errno(i);	/* Restore errno. */
+				buf = o;
+
+				if (!code) {	/* s */
+					localtime_r(&t, tm); /* TODO: check for failure? */
+					i = 0;
+					do {		/* Now copy values from tm to fields. */
+						 fields[i] = ((int *) tm)[i];
+					} while (++i < 8);
+				}
+			}
+			/* TODO: glibc treats %Z as a nop.  For now, do the same. */
+			goto LOOP;
+		}
+
+		assert((code & MASK_SPEC) == INT_SPEC);
+		{
+			register const unsigned char *x;
+			code &= 0xf;
+			x = spec + INT_FIELD_START + (code << 1);
+			if ((j = x[1]) < 3) { /* upper bound (inclusive) */
+				j = ((j==1) ? 366 : 9999);
+			}
+			i = -1;
+			while (isdigit(*buf)) {
+				if (i < 0) {
+					i = 0;
+				}
+				if ((i = 10*i + (*buf - '0')) > j) { /* Overflow. */
+					return NULL;
+				}
+				++buf;
+			}
+			if (i < (*x & 1)) {	/* This catches no-digit case too. */
+				return NULL;
+			}
+			if (*x & 2) {
+				--i;
+			}
+			if (*x & 4) {
+				i -= 1900;
+			}
+
+			if (*x == (9 << 3) + 1 + 0) { /* %I or %l */
+				if (i == 12) {
+					i = 0;
+				}
+				if (fields[8] >= 0) { /* We have a previous %p or %P. */
+					fields[2] = i + fields[8];
+				}
+			}
+
+			fields[(*x) >> 3] = i;
+
+			if (((unsigned char)(*x - (10<< 3) + 0 + 0)) <= 8) { /* %C or %y */
+				if ((j = fields[10]) < 0) {	/* No %C, so i must be %y data. */
+					if (i <= 68) { /* Map [0-68] to 2000+i */
+						i += 100;
+					}
+				} else {		/* Have %C data, but what about %y? */
+					if ((i = fields[11]) < 0) {	/* No %y data. */
+						i = 0;	/* Treat %y val as 0 following glibc's example. */
+					}
+					i += 100*(j - 19);
+				}
+				fields[5] = i;
+			}
+		}
+		goto LOOP;
+	} else if (isspace(*p)) {
+		++p;
+		while (isspace(*buf)) {
+			++buf;
+		}
+		goto LOOP;
+	} else if (*buf++ == *p++) {
+		goto LOOP;
+	}
+	return NULL;
+}
+
+#endif
+/**********************************************************************/
+#ifdef L_time
+
+#ifndef __BCC__
+#error The uClibc version of time is in sysdeps/linux/common.
+#endif
+
+time_t time(register time_t *tloc)
+{
+	struct timeval tv;
+	register struct timeval *p = &tv;
+
+	gettimeofday(p, NULL);		/* This should never fail... */
+
+	if (tloc) {
+		*tloc = p->tv_sec;
+	}
+
+	return p->tv_sec;
+}
+
+#endif
+/**********************************************************************/
+#ifdef L_tzset
+
+static const char vals[] = {
+	'T', 'Z', 0,				/* 3 */
+	'U', 'T', 'C', 0,			/* 4 */
+	25, 60, 60, 1,				/* 4 */
+	'.', 1,						/* M */
+	5, '.', 1,
+	6,  0,  0,					/* Note: overloaded for non-M non-J case... */
+	0, 1, 0,					/* J */
+	',', 'M',      '4', '.', '1', '.', '0',
+	',', 'M', '1', '0', '.', '5', '.', '0', 0
+};
+
+#define TZ    vals
+#define UTC   (vals + 3)
+#define RANGE (vals + 7)
+#define RULE  (vals + 11 - 1)
+#define DEFAULT_RULES (vals + 22)
+
+/* Initialize to UTC. */
+int daylight = 0;
+long timezone = 0;
+char *tzname[2] = { (char *) UTC, (char *) (UTC-1) };
+
+rule_struct _time_tzinfo[2];
+
+static const char *getoffset(register const char *e, long *pn)
+{
+	register const char *s = RANGE-1;
+	long n;
+	int f;
+
+	n = 0;
+	f = -1;
+	do {
+		++s;
+		if (isdigit(*e)) {
+			f = *e++ - '0';
+		}
+		if (isdigit(*e)) {
+			f = 10 * f + (*e++ - '0');
+		}
+		if (((unsigned int)f) >= *s) {
+			return NULL;
+		}
+		n = (*s) * n + f;
+		f = 0;
+		if (*e == ':') {
+			++e;
+			--f;
+		}
+	} while (*s > 1);
+
+	*pn = n;
+	return e;
+}
+
+static const char *getnumber(register const char *e, int *pn)
+{
+#ifdef __BCC__
+	/* bcc can optimize the counter if it thinks it is a pointer... */
+	register const char *n = (const char *) 3;
+	int f;
+
+	f = 0;
+	while (n && isdigit(*e)) {
+		f = 10 * f + (*e++ - '0');
+		--n;
+	}
+
+	*pn = f;
+	return (n == (const char *) 3) ? NULL : e;
+#else  /* __BCC__ */
+	int n, f;
+
+	n = 3;
+	f = 0;
+	while (n && isdigit(*e)) {
+		f = 10 * f + (*e++ - '0');
+		--n;
+	}
+
+	*pn = f;
+	return (n == 3) ? NULL : e;
+#endif /* __BCC__ */
+}
+
+void tzset(void)
+{
+	register const char *e;
+	register char *s;
+	long off;
+	short *p;
+	rule_struct new_rules[2];
+	int n, count, f;
+	char c;
+
+	if (!(e = getenv(TZ)) || !*e) { /* Not set or set to empty string. */
+	ILLEGAL:					/* TODO: Clean up the following... */
+		s = _time_tzinfo[0].tzname;
+		*s = 'U';
+		*++s = 'T';
+		*++s = 'C';
+		*++s =
+		*_time_tzinfo[1].tzname = 0;
+		_time_tzinfo[0].gmt_offset = 0;
+		goto DONE;
+	}
+
+
+	if (*e == ':') {			/* Ignore leading ':'. */
+		++e;
+	}
+	
+	count = 0;
+	new_rules[1].tzname[0] = 0;
+ LOOP:
+	/* Get std or dst name. */
+	c = 0;
+	if (*e == '<') {
+		++e;
+		c = '>';
+	}
+
+	s = new_rules[count].tzname;
+	n = 0;
+	while (*e
+		   && (isalpha(*e)
+			   || (c && (isdigit(*e) || (*e == '+') || (*e == '-'))))
+		   ) {
+		*s++ = *e++;
+		if (++n > TZNAME_MAX) {
+			goto ILLEGAL;
+		}
+	}
+	*s = 0;
+
+	if ((n < 3)					/* Check for minimum length. */
+		|| (c && (*e++ != c))	/* Match any quoting '<'. */
+		) {
+		goto ILLEGAL;
+	}
+
+	/* Get offset */
+	s = (char *) e;
+	if ((*e != '-') && (*e != '+')) {
+		if (count && !isdigit(*e)) {
+			off -= 3600;		/* Default to 1 hour ahead of std. */
+			goto SKIP_OFFSET;
+		}
+		--e;
+	}
+
+	++e;
+	if (!(e = getoffset(e, &off))) {
+		goto ILLEGAL;
+	}
+
+	if (*s == '-') {
+		off = -off;				/* Save off in case needed for dst default. */
+	}
+ SKIP_OFFSET:
+	new_rules[count].gmt_offset = off;
+
+	if (!count) {
+		if (*e) {
+			++count;
+			goto LOOP;
+		}
+	} else {					/* OK, we have dst, so get some rules. */
+		count = 0;
+		if (!*e) {				/* No rules so default to US rules. */
+			e = DEFAULT_RULES;
+		}
+
+		do {
+			if (*e++ != ',') {
+				goto ILLEGAL;
+			}
+
+			n = 365;
+			s = (char *) RULE;
+			if ((c = *e++) == 'M') {
+				n = 12;
+			} else if (c == 'J') {
+				s += 8;
+			} else {
+				--e;
+				c = 0;
+				s += 6;
+			}
+
+			*(p = &new_rules[count].rule_type) = c;
+			if (c != 'M') {
+				p -= 2;
+			}
+
+			do {
+				++s;
+				if (!(e = getnumber(e, &f))
+					|| (((unsigned int)(f - s[1])) > n)
+					|| (*s && (*e++ != *s))
+					) {
+					goto ILLEGAL;
+				}
+				*--p = f;
+			} while ((n = *(s += 2)) > 0);
+
+			off = 2 * 60 * 60;	/* Default to 2:00:00 */
+			if (*e == '/') {
+				++e;
+				if (!(e = getoffset(e, &off))) {
+					goto ILLEGAL;
+				}
+			}
+			new_rules[count].dst_offset = off;
+		} while (++count < 2);
+
+		if (*e) {
+			goto ILLEGAL;
+		}
+	}
+
+	memcpy(_time_tzinfo, new_rules, sizeof(new_rules));
+ DONE:
+	tzname[0] = _time_tzinfo[0].tzname;
+	tzname[1] = _time_tzinfo[1].tzname;
+	daylight = !!new_rules[1].tzname[0];
+	timezone = new_rules[0].gmt_offset;
+}
+
+#endif
+/**********************************************************************/
+/*  #ifdef L_utime */
+
+/* utime is a syscall in both linux and elks. */
+/*  int utime(const char *path, const struct utimbuf *times) */
+
+/*  #endif */
+/**********************************************************************/
+/* Non-SUSv3 */
+/**********************************************************************/
+#ifdef L_utimes
+
+#ifndef __BCC__
+#error The uClibc version of utimes is in sysdeps/linux/common.
+#endif
+
+#include <utime.h>
+#include <sys/time.h>
+
+int utimes(const char *filename, register const struct timeval *tvp)
+{
+	register struct utimbuf *p = NULL;
+	struct utimbuf utb;
+
+	if (tvp) {
+		p = &utb;
+		p->actime = tvp[0].tv_sec;
+		p->modtime = tvp[1].tv_sec;
+	}
+	return utime(filename, p);
+}
+
+#endif
+/**********************************************************************/
+#ifdef L__time_t2tm
+
+static const uint16_t vals[] = {
+	60, 60, 24, 7 /* special */, 36524, 1461, 365, 0
+};
+
+static const unsigned char days[] = {
+	31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, /* non-leap */
+	    29,
+};
+
+/* Notes:
+ * If time_t is 32 bits, then no overflow is possible.
+ * It time_t is > 32 bits, this needs to be adjusted to deal with overflow.
+ */
+
+/* Note: offset is the correction in _days_ to *timer! */
+
+struct tm *_time_t2tm(const time_t *__restrict timer,
+					  int offset, struct tm *__restrict result)
+{
+	register int *p;
+	time_t t1, t, v;
+	int wday;
+
+	{
+		register const uint16_t *vp;
+		t = *timer;
+		p = (int *) result;
+		p[7] = 0;
+		vp = vals;
+		do {
+			if ((v = *vp) == 7) {
+				/* Overflow checking, assuming time_t is long int... */
+#if (LONG_MAX > INT_MAX) && (LONG_MAX > 2147483647L)
+#if (INT_MAX == 2147483647L) && (LONG_MAX == 9223372036854775807L)
+				/* Valid range for t is [-784223472856L, 784223421720L].
+				 * Outside of this range, the tm_year field will overflow. */
+				if (((unsigned long)(t + offset- -784223472856L))
+					> (784223421720L - -784223472856L)
+					) {
+					return NULL;
+				}
+#else
+#error overflow conditions unknown
+#endif
+#endif
+
+				/* We have days since the epoch, so caluclate the weekday. */
+#if defined(__BCC__) && TIME_T_IS_UNSIGNED
+				wday = (t + 4) % (*vp);	/* t is unsigned */
+#else
+				wday = ((int)((t % (*vp)) + 11)) % ((int)(*vp)); /* help bcc */
+#endif
+				/* Set divisor to days in 400 years.  Be kind to bcc... */
+				v = ((time_t)(vp[1])) << 2;
+				++v;
+				/* Change to days since 1/1/1601 so that for 32 bit time_t
+				 * values, we'll have t >= 0.  This should be changed for
+				 * archs with larger time_t types. 
+				 * Also, correct for offset since a multiple of 7. */
+
+				/* TODO: Does this still work on archs with time_t > 32 bits? */
+				t += (135140L - 366) + offset; /* 146097 - (365*30 + 7) -366 */
+			}
+#if defined(__BCC__) && TIME_T_IS_UNSIGNED
+			t -= ((t1 = t / v) * v);
+#else
+			if ((t -= ((t1 = t / v) * v)) < 0) {
+				t += v;
+				--t1;
+			}
+#endif
+
+			if ((*vp == 7) && (t == v-1)) {
+				--t;			/* Correct for 400th year leap case */
+				++p[4];			/* Stash the extra day... */
+			}
+
+#if defined(__BCC__) && 0
+			*p = t1;
+			if (v <= 60) {
+				*p = t;
+				t = t1;
+			}
+			++p;
+#else
+			if (v <= 60) {
+				*p++ = t;
+				t = t1;
+			} else {
+				*p++ = t1;
+			}
+#endif
+		} while (*++vp);
+	}
+
+	if (p[-1] == 4) {
+		--p[-1];
+		t = 365;
+	}
+
+
+	*p += ((int) t);			/* result[7] .. tm_yday */
+
+	p -= 2;						/* at result[5] */
+
+#if (LONG_MAX > INT_MAX) && (LONG_MAX > 2147483647L)
+	/* Protect against overflow.  TODO: Unecessary if int arith wraps? */
+	*p = ((((p[-2]<<2) + p[-1])*25 + p[0])<< 2) + (p[1] - 299); /* tm_year */
+#else
+	*p = ((((p[-2]<<2) + p[-1])*25 + p[0])<< 2) + p[1] - 299; /* tm_year */
+#endif
+
+	p[1] = wday;				/* result[6] .. tm_wday */
+
+	{
+		register const unsigned char *d = days;
+
+		wday = 1900 + *p;
+		if (__isleap(wday)) {
+			d += 11;
+		}
+
+		wday = p[2] + 1;		/* result[7] .. tm_yday */
+		*--p = 0;				/* at result[4] .. tm_mon */
+		while (wday > *d) {
+			wday -= *d;
+			if (*d == 29) {
+				d -= 11;		/* Backup to non-leap Feb. */
+			}
+			++d;
+			++*p;				/* Increment tm_mon. */
+		}
+		p[-1] = wday;			/* result[3] .. tm_mday */
+	}
+	/* TODO -- should this be 0? */
+	p[4] = 0;					/* result[8] .. tm_isdst */
+
+	return result;
+}
+
+#endif
+/**********************************************************************/
+#ifdef L___time_tm
+
+struct tm __time_tm;	/* Global shared by gmtime() and localtime(). */
+
+#endif
+/**********************************************************************/
+#ifdef L__time_mktime
+
+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)
+{
+#ifdef __BCC__
+	long days, secs;
+#else
+	long long secs;
+#endif
+	time_t t;
+	struct tm x;
+	/* 0:sec  1:min  2:hour  3:mday  4:mon  5:year  6:wday  7:yday  8:isdst */
+	register int *p = (int *) &x;
+	register const unsigned char *s;
+	int d;
+
+	tzset();
+
+	memcpy(p, timeptr, sizeof(struct tm));
+
+	d = 400;
+	p[5] = (p[5] - ((p[6] = p[5]/d) * d)) + (p[7] = p[4]/12);
+	if ((p[4] -= 12 * p[7]) < 0) {
+		p[4] += 12;
+		--p[5];
+	}
+
+	s = vals;
+	d = (p[5] += 1900);			/* Correct year.  Now between 1900 and 2300. */
+	if (__isleap(d)) {
+		s += 11;
+	}
+	
+	p[7] = 0;
+	d = p[4];
+	while (d) {
+		p[7] += *s;
+		if (*s == 29) {
+			s -= 11;			/* Backup to non-leap Feb. */
+		}
+		++s;
+		--d;
+	}
+
+#ifdef __BCC__
+	/* TODO - check */
+	d = p[5] - 1;
+	days = -719163L + d*365 + ((d/4) - (d/100) + (d/400) + p[3] + p[7]);
+	secs = p[0] + 60*( p[1] + 60*((long)(p[2])) );
+
+	if (secs < 0) {
+		secs += 120009600L;
+		days -= 1389;
+	}
+	if ( ((unsigned long)(days + secs/86400L)) > 49710L) {
+		return -1;
+	}
+	secs += (days * 86400L);
+#else
+	d = p[5] - 1;
+	d = -719163L + d*365 + (d/4) - (d/100) + (d/400);
+	secs = p[0]
+		+ _time_tzinfo[timeptr->tm_isdst > 0].gmt_offset
+		+ 60*( p[1]
+			   + 60*(p[2]
+					 + 24*(((146073L * ((long long)(p[6])) + d)
+							+ p[3]) + p[7])));
+	if (((unsigned long long)(secs - LONG_MIN))
+		> (((unsigned long long)LONG_MAX) - LONG_MIN)
+		) {
+		return -1;
+	}
+#endif
+
+	t = secs;
+
+	localtime_r(&t, (struct tm *)p);
+
+	if (store_on_success) {
+		memcpy(timeptr, p, sizeof(struct tm));
+	}
+
+	return t;
+}
+
+#endif
+/**********************************************************************/

+ 0 - 105
libc/misc/time/tm_conv.c

@@ -1,105 +0,0 @@
-
-/* This is adapted from glibc */
-/* Copyright (C) 1991, 1993 Free Software Foundation, Inc */
-
-#define SECS_PER_HOUR 3600L
-#define SECS_PER_DAY  86400L
-
-#include <features.h>
-#include <time.h>
-#include <sys/types.h>
-
-/* This structure contains all the information about a
-   timezone given in the POSIX standard TZ envariable.  */
-typedef struct
-{
-    const char *name;
-
-    /* When to change.  */
-    enum { J0, J1, M } type;	/* Interpretation of:  */
-    unsigned short int m, n, d;	/* Month, week, day.  */
-    unsigned int secs;		/* Time of day.  */
-
-    long int offset;		/* Seconds east of GMT (west if < 0).  */
-
-    /* We cache the computed time of change for a
-       given year so we don't have to recompute it.  */
-    time_t change;	/* When to change to this zone.  */
-    int computed_for;	/* Year above is computed for.  */
-} tz_rule;
-
-/* tz_rules[0] is standard, tz_rules[1] is daylight.  */
-static tz_rule tz_rules[2];
-
-/* Warning -- this function is a stub andd always does UTC
- * no matter what it is given */
-void tzset (void)
-{
-    tz_rules[0].name = tz_rules[1].name = "UTC";
-    tz_rules[0].type = tz_rules[1].type = J0;
-    tz_rules[0].m = tz_rules[0].n = tz_rules[0].d = 0;
-    tz_rules[1].m = tz_rules[1].n = tz_rules[1].d = 0;
-    tz_rules[0].secs = tz_rules[1].secs = 0;
-    tz_rules[0].offset = tz_rules[1].offset = 0L;
-    tz_rules[0].change = tz_rules[1].change = (time_t) -1;
-    tz_rules[0].computed_for = tz_rules[1].computed_for = 0;
-}
-
-
-static const unsigned short int __mon_lengths[2][12] = {
-	/* Normal years.  */
-	{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
-	/* Leap years.  */
-	{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
-};
-
-void __tm_conv(struct tm *tmbuf, time_t *t, time_t offset)
-{
-	int isdst;
-	long days, rem;
-	register int y;
-	register const unsigned short int *ip;
-
-	timezone = -offset;
-
-	days = *t / SECS_PER_DAY;
-	rem = *t % SECS_PER_DAY;
-	rem += offset;
-	while (rem < 0) {
-		rem += SECS_PER_DAY;
-		--days;
-	}
-	while (rem >= SECS_PER_DAY) {
-		rem -= SECS_PER_DAY;
-		++days;
-	}
-	tmbuf->tm_hour = rem / SECS_PER_HOUR;
-	rem %= SECS_PER_HOUR;
-	tmbuf->tm_min = rem / 60;
-	tmbuf->tm_sec = rem % 60;
-	/* January 1, 1970 was a Thursday.  */
-	tmbuf->tm_wday = (4 + days) % 7;
-	if (tmbuf->tm_wday < 0)
-		tmbuf->tm_wday += 7;
-	y = 1970;
-	while (days >= (rem = __isleap(y) ? 366 : 365)) {
-		++y;
-		days -= rem;
-	}
-	while (days < 0) {
-		--y;
-		days += __isleap(y) ? 366 : 365;
-	}
-	tmbuf->tm_year = y - 1900;
-	tmbuf->tm_yday = days;
-	ip = __mon_lengths[__isleap(y)];
-	for (y = 0; days >= ip[y]; ++y)
-		days -= ip[y];
-	tmbuf->tm_mon = y;
-	tmbuf->tm_mday = days + 1;
-	isdst = (*t >= tz_rules[0].change && *t < tz_rules[1].change);
-	tmbuf->tm_isdst = isdst;
-	tmbuf->tm_zone = tzname[isdst];
-
-}
-

+ 0 - 17
libc/misc/time/utimes.c

@@ -1,17 +0,0 @@
-#include <stdlib.h>
-#include <utime.h>
-#include <sys/time.h>
-
-int utimes (const char *file, const struct timeval tvp[2])
-{
-	struct utimbuf buf, *times;
-
-	if (tvp) {
-		times = &buf;
-		times->actime = tvp[0].tv_sec;
-		times->modtime = tvp[1].tv_sec;
-	}
-	else
-		times = NULL;
-	return utime(file, times);
-}

+ 2 - 1
libc/stdlib/setenv.c

@@ -19,6 +19,7 @@
    modified for uClibc by Erik Andersen <andersen@codepoet.org>
    */
 
+#define _GNU_SOURCE
 #include <features.h>
 #include <errno.h>
 #include <stdlib.h>
@@ -56,7 +57,7 @@ int __add_to_environ (const char *name, const char *value,
     const size_t namelen = strlen (name);
     const size_t vallen = value != NULL ? strlen (value) + 1 : 0;
 
-    pthread_mutex_lock(&envlock);
+    LOCK;
 
     /* We have to get the pointer now that we have the lock and not earlier
        since another thread might have created a new environment.  */