diff options
| author | Manuel Novoa III <mjn3@codepoet.org> | 2002-06-17 09:45:15 +0000 | 
|---|---|---|
| committer | Manuel Novoa III <mjn3@codepoet.org> | 2002-06-17 09:45:15 +0000 | 
| commit | d6b2a407783dbbd6c9680601da005de0bb0ff37d (patch) | |
| tree | d72244507fab5c5c59d12aa10fa411943631c56c | |
| parent | a7ce5bbdcd1217af1aab4c15e4ce337c8ef33973 (diff) | |
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.
| -rw-r--r-- | include/time.h | 2 | ||||
| -rw-r--r-- | libc/misc/time/Makefile | 20 | ||||
| -rw-r--r-- | libc/misc/time/README | 8 | ||||
| -rw-r--r-- | libc/misc/time/__time_locale.c | 30 | ||||
| -rw-r--r-- | libc/misc/time/__time_static.c | 17 | ||||
| -rw-r--r-- | libc/misc/time/asc_conv.c | 80 | ||||
| -rw-r--r-- | libc/misc/time/asctime.c | 18 | ||||
| -rw-r--r-- | libc/misc/time/asctime_r.c | 16 | ||||
| -rw-r--r-- | libc/misc/time/clock.c | 43 | ||||
| -rw-r--r-- | libc/misc/time/ctime.c | 14 | ||||
| -rw-r--r-- | libc/misc/time/ctime_r.c | 10 | ||||
| -rw-r--r-- | libc/misc/time/difftime.c | 64 | ||||
| -rw-r--r-- | libc/misc/time/gmtime.c | 13 | ||||
| -rw-r--r-- | libc/misc/time/gmtime_r.c | 12 | ||||
| -rw-r--r-- | libc/misc/time/localtime.c | 24 | ||||
| -rw-r--r-- | libc/misc/time/localtime_r.c | 22 | ||||
| -rw-r--r-- | libc/misc/time/mktime.c | 327 | ||||
| -rw-r--r-- | libc/misc/time/strftime.c | 395 | ||||
| -rw-r--r-- | libc/misc/time/strptime.c | 983 | ||||
| -rw-r--r-- | libc/misc/time/time.c | 1864 | ||||
| -rw-r--r-- | libc/misc/time/tm_conv.c | 105 | ||||
| -rw-r--r-- | libc/misc/time/utimes.c | 17 | ||||
| -rw-r--r-- | libc/stdlib/setenv.c | 3 | 
23 files changed, 1883 insertions, 2204 deletions
| diff --git a/include/time.h b/include/time.h index 08ee502e5..bd86b40ee 100644 --- a/include/time.h +++ b/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  }; diff --git a/libc/misc/time/Makefile b/libc/misc/time/Makefile index 3e0845799..425e2713d 100644 --- a/libc/misc/time/Makefile +++ b/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 diff --git a/libc/misc/time/README b/libc/misc/time/README deleted file mode 100644 index da4f4e67f..000000000 --- a/libc/misc/time/README +++ /dev/null @@ -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 diff --git a/libc/misc/time/__time_locale.c b/libc/misc/time/__time_locale.c deleted file mode 100644 index 0d78a0a67..000000000 --- a/libc/misc/time/__time_locale.c +++ /dev/null @@ -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 - diff --git a/libc/misc/time/__time_static.c b/libc/misc/time/__time_static.c deleted file mode 100644 index d921ced90..000000000 --- a/libc/misc/time/__time_static.c +++ /dev/null @@ -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; - - diff --git a/libc/misc/time/asc_conv.c b/libc/misc/time/asc_conv.c deleted file mode 100644 index 50c7e0834..000000000 --- a/libc/misc/time/asc_conv.c +++ /dev/null @@ -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] = ' '; -	} -} diff --git a/libc/misc/time/asctime.c b/libc/misc/time/asctime.c deleted file mode 100644 index 9d5772335..000000000 --- a/libc/misc/time/asctime.c +++ /dev/null @@ -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; -} - diff --git a/libc/misc/time/asctime_r.c b/libc/misc/time/asctime_r.c deleted file mode 100644 index 215031949..000000000 --- a/libc/misc/time/asctime_r.c +++ /dev/null @@ -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; -} - diff --git a/libc/misc/time/clock.c b/libc/misc/time/clock.c deleted file mode 100644 index 139d35868..000000000 --- a/libc/misc/time/clock.c +++ /dev/null @@ -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); -} diff --git a/libc/misc/time/ctime.c b/libc/misc/time/ctime.c deleted file mode 100644 index 027f5d5a7..000000000 --- a/libc/misc/time/ctime.c +++ /dev/null @@ -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)); -} - diff --git a/libc/misc/time/ctime_r.c b/libc/misc/time/ctime_r.c deleted file mode 100644 index 1b323a258..000000000 --- a/libc/misc/time/ctime_r.c +++ /dev/null @@ -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); -}  - diff --git a/libc/misc/time/difftime.c b/libc/misc/time/difftime.c deleted file mode 100644 index 88a9244ba..000000000 --- a/libc/misc/time/difftime.c +++ /dev/null @@ -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; -} diff --git a/libc/misc/time/gmtime.c b/libc/misc/time/gmtime.c deleted file mode 100644 index e9d648037..000000000 --- a/libc/misc/time/gmtime.c +++ /dev/null @@ -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; -} diff --git a/libc/misc/time/gmtime_r.c b/libc/misc/time/gmtime_r.c deleted file mode 100644 index a1502e275..000000000 --- a/libc/misc/time/gmtime_r.c +++ /dev/null @@ -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; -} diff --git a/libc/misc/time/localtime.c b/libc/misc/time/localtime.c deleted file mode 100644 index 22f5035e9..000000000 --- a/libc/misc/time/localtime.c +++ /dev/null @@ -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; -} diff --git a/libc/misc/time/localtime_r.c b/libc/misc/time/localtime_r.c deleted file mode 100644 index 139864506..000000000 --- a/libc/misc/time/localtime_r.c +++ /dev/null @@ -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; -} diff --git a/libc/misc/time/mktime.c b/libc/misc/time/mktime.c deleted file mode 100644 index 065126e0e..000000000 --- a/libc/misc/time/mktime.c +++ /dev/null @@ -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 diff --git a/libc/misc/time/strftime.c b/libc/misc/time/strftime.c deleted file mode 100644 index bde0d93d8..000000000 --- a/libc/misc/time/strftime.c +++ /dev/null @@ -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; -} diff --git a/libc/misc/time/strptime.c b/libc/misc/time/strptime.c deleted file mode 100644 index 749eb797d..000000000 --- a/libc/misc/time/strptime.c +++ /dev/null @@ -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); -} diff --git a/libc/misc/time/time.c b/libc/misc/time/time.c new file mode 100644 index 000000000..4371a9cfd --- /dev/null +++ b/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 +/**********************************************************************/ diff --git a/libc/misc/time/tm_conv.c b/libc/misc/time/tm_conv.c deleted file mode 100644 index f73c96f07..000000000 --- a/libc/misc/time/tm_conv.c +++ /dev/null @@ -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]; - -} - diff --git a/libc/misc/time/utimes.c b/libc/misc/time/utimes.c deleted file mode 100644 index 86dff6bea..000000000 --- a/libc/misc/time/utimes.c +++ /dev/null @@ -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); -} diff --git a/libc/stdlib/setenv.c b/libc/stdlib/setenv.c index 02959ab95..ef5d84d81 100644 --- a/libc/stdlib/setenv.c +++ b/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.  */ | 
