diff options
Diffstat (limited to 'REORG.TODO/time')
67 files changed, 9199 insertions, 0 deletions
diff --git a/REORG.TODO/time/Depend b/REORG.TODO/time/Depend new file mode 100644 index 0000000000..79b246903c --- /dev/null +++ b/REORG.TODO/time/Depend @@ -0,0 +1 @@ +timezone diff --git a/REORG.TODO/time/Makefile b/REORG.TODO/time/Makefile new file mode 100644 index 0000000000..317c4d8901 --- /dev/null +++ b/REORG.TODO/time/Makefile @@ -0,0 +1,75 @@ +# Copyright (C) 1991-2017 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 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, see +# <http://www.gnu.org/licenses/>. + +# +# Makefile for time routines +# +subdir := time + +include ../Makeconfig + +headers := time.h sys/time.h sys/timeb.h bits/time.h \ + bits/types/clockid_t.h bits/types/clock_t.h \ + bits/types/struct_itimerspec.h \ + bits/types/struct_timespec.h bits/types/struct_timeval.h \ + bits/types/struct_tm.h bits/types/timer_t.h \ + bits/types/time_t.h + +routines := offtime asctime clock ctime ctime_r difftime \ + gmtime localtime mktime time \ + gettimeofday settimeofday adjtime tzset \ + tzfile getitimer setitimer \ + stime dysize timegm ftime \ + getdate strptime strptime_l \ + strftime wcsftime strftime_l wcsftime_l \ + timespec_get +aux := era alt_digit lc-time-cleanup + +tests := test_time clocktest tst-posixtz tst-strptime tst_wcsftime \ + tst-getdate tst-mktime tst-mktime2 tst-ftime_l tst-strftime \ + tst-mktime3 tst-strptime2 bug-asctime bug-asctime_r bug-mktime1 \ + tst-strptime3 bug-getdate1 tst-strptime-whitespace tst-ftime \ + tst-tzname + +include ../Rules + +ifeq ($(run-built-tests),yes) +LOCALES := de_DE.ISO-8859-1 en_US.ISO-8859-1 ja_JP.EUC-JP +include ../gen-locales.mk + +$(objpfx)tst-ftime_l.out: $(gen-locales) +$(objpfx)tst-strptime.out: $(gen-locales) +endif + +tz-cflags = -DTZDIR='"$(zonedir)"' \ + -DTZDEFAULT='"$(localtime-file)"' \ + -DTZDEFRULES='"$(posixrules-file)"' + +CFLAGS-tzfile.c = $(tz-cflags) +CFLAGS-tzset.c = $(tz-cflags) +CFLAGS-getdate.c = -fexceptions + +# Don't warn about Y2k problem in strftime format string. +CFLAGS-test_time.c = -Wno-format + +tst-getdate-ENV= DATEMSK=datemsk TZDIR=${common-objpfx}timezone/testdata +test_time-ARGS= EST5EDT CST + +tst-tzname-ENV = TZDIR=${common-objpfx}timezone/testdata +CPPFLAGS-tst-tzname.c = -DTZDEFRULES='"$(posixrules-file)"' + +bug-getdate1-ARGS = ${objpfx}bug-getdate1-fmt diff --git a/REORG.TODO/time/Versions b/REORG.TODO/time/Versions new file mode 100644 index 0000000000..fd838181e4 --- /dev/null +++ b/REORG.TODO/time/Versions @@ -0,0 +1,68 @@ +libc { + GLIBC_2.0 { + # global variables + __daylight; __timezone; __tzname; + + # functions with special/multiple interfaces + __adjtimex; + + # functions used in other libraries + __gmtime_r; __gettimeofday; + + # variables in normal name space + daylight; timezone; tzname; + + # a* + adjtime; adjtimex; asctime; asctime_r; + + # c* + clock; ctime; ctime_r; + + # d* + difftime; dysize; + + # f* + ftime; + + # g* + getitimer; gettimeofday; gmtime; gmtime_r; + + # l* + localtime; localtime_r; + + # m* + mktime; + + # s* + setitimer; settimeofday; stime; strftime; strptime; + + # t* + time; timegm; timelocal; tzset; + } + GLIBC_2.1 { + # variables in normal name space + getdate_err; + + # g* + getdate; getdate_r; + } + GLIBC_2.2 { + # w* + wcsftime; + } + GLIBC_2.3 { + # these internal names are used by libstdc++ + __strftime_l; __wcsftime_l; + + # s* + strftime_l; + # w* + wcsftime_l; + } + GLIBC_2.3.2 { + strptime_l; + } + GLIBC_2.16 { + timespec_get; + } +} diff --git a/REORG.TODO/time/adjtime.c b/REORG.TODO/time/adjtime.c new file mode 100644 index 0000000000..180bf48e13 --- /dev/null +++ b/REORG.TODO/time/adjtime.c @@ -0,0 +1,33 @@ +/* Copyright (C) 1991-2017 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 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, see + <http://www.gnu.org/licenses/>. */ + +#include <errno.h> +#include <sys/time.h> + +/* Adjust the current time of day by the amount in DELTA. + If OLDDELTA is not NULL, it is filled in with the amount + of time adjustment remaining to be done from the last `__adjtime' call. + This call is restricted to the super-user. */ +int +__adjtime (const struct timeval *delta, struct timeval *olddelta) +{ + __set_errno (ENOSYS); + return -1; +} +stub_warning (adjtime) + +weak_alias (__adjtime, adjtime) diff --git a/REORG.TODO/time/alt_digit.c b/REORG.TODO/time/alt_digit.c new file mode 100644 index 0000000000..e3bcea4414 --- /dev/null +++ b/REORG.TODO/time/alt_digit.c @@ -0,0 +1,193 @@ +/* Helper functions used by strftime/strptime to handle alternate digits. + Copyright (C) 1995-2017 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 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, see + <http://www.gnu.org/licenses/>. */ + +#include "../locale/localeinfo.h" +#include <libc-lock.h> +#include <stdlib.h> +#include <wchar.h> +#include <string.h> +#include <stdint.h> + +/* Some of the functions here must not be used while setlocale is called. */ +__libc_rwlock_define (extern, __libc_setlocale_lock attribute_hidden) + +#define CURRENT(item) (current->values[_NL_ITEM_INDEX (item)].string) +#define CURRENT_WSTR(item) \ + ((wchar_t *) current->values[_NL_ITEM_INDEX (item)].wstr) + +static void +_nl_init_alt_digit (struct __locale_data *current) +{ + struct lc_time_data *data; + + if (current->private.time == NULL) + { + current->private.time = malloc (sizeof *current->private.time); + if (current->private.time == NULL) + return; + memset (current->private.time, 0, sizeof *current->private.time); + current->private.cleanup = &_nl_cleanup_time; + } + data = current->private.time; + + if (! data->alt_digits_initialized) + { + const char *ptr = CURRENT (ALT_DIGITS); + size_t cnt; + + data->alt_digits_initialized = 1; + + if (ptr != NULL) + { + data->alt_digits = malloc (100 * sizeof (const char *)); + if (data->alt_digits != NULL) + for (cnt = 0; cnt < 100; ++cnt) + { + data->alt_digits[cnt] = ptr; + + /* Skip digit format. */ + ptr = strchr (ptr, '\0') + 1; + } + } + } + +} + +const char * +internal_function +_nl_get_alt_digit (unsigned int number, struct __locale_data *current) +{ + const char *result; + + if (number >= 100 || CURRENT (ALT_DIGITS)[0] == '\0') + return NULL; + + __libc_rwlock_wrlock (__libc_setlocale_lock); + + if (current->private.time == NULL + || ! current->private.time->alt_digits_initialized) + _nl_init_alt_digit (current); + + result = ((current->private.time != NULL + && current->private.time->alt_digits != NULL) + ? current->private.time->alt_digits[number] + : NULL); + + __libc_rwlock_unlock (__libc_setlocale_lock); + + return result; +} + + +const wchar_t * +internal_function +_nl_get_walt_digit (unsigned int number, struct __locale_data *current) +{ + const wchar_t *result = NULL; + struct lc_time_data *data; + + if (number >= 100 || CURRENT_WSTR (_NL_WALT_DIGITS)[0] == L'\0') + return NULL; + + __libc_rwlock_wrlock (__libc_setlocale_lock); + + if (current->private.time == NULL) + { + current->private.time = malloc (sizeof *current->private.time); + if (current->private.time == NULL) + goto out; + memset (current->private.time, 0, sizeof *current->private.time); + current->private.cleanup = &_nl_cleanup_time; + } + data = current->private.time; + + if (! data->walt_digits_initialized) + { + const wchar_t *ptr = CURRENT_WSTR (_NL_WALT_DIGITS); + size_t cnt; + + data->walt_digits_initialized = 1; + + if (ptr != NULL) + { + data->walt_digits = malloc (100 * sizeof (const uint32_t *)); + if (data->walt_digits != NULL) + for (cnt = 0; cnt < 100; ++cnt) + { + data->walt_digits[cnt] = ptr; + + /* Skip digit format. */ + ptr = __wcschr (ptr, L'\0') + 1; + } + } + } + + if (data->walt_digits != NULL) + result = data->walt_digits[number]; + + out: + __libc_rwlock_unlock (__libc_setlocale_lock); + + return (wchar_t *) result; +} + + +int +internal_function +_nl_parse_alt_digit (const char **strp, struct __locale_data *current) +{ + const char *str = *strp; + int result = -1; + size_t cnt; + size_t maxlen = 0; + + if (CURRENT_WSTR (_NL_WALT_DIGITS)[0] == L'\0') + return result; + + __libc_rwlock_wrlock (__libc_setlocale_lock); + + if (current->private.time == NULL + || ! current->private.time->alt_digits_initialized) + _nl_init_alt_digit (current); + + if (current->private.time != NULL && + current->private.time->alt_digits != NULL) + /* Matching is not unambiguous. The alternative digits could be like + I, II, III, ... and the first one is a substring of the second + and third. Therefore we must keep on searching until we found + the longest possible match. Note that this is not specified in + the standard. */ + for (cnt = 0; cnt < 100; ++cnt) + { + const char *const dig = current->private.time->alt_digits[cnt]; + size_t len = strlen (dig); + + if (len > maxlen && strncmp (dig, str, len) == 0) + { + maxlen = len; + result = (int) cnt; + } + } + + __libc_rwlock_unlock (__libc_setlocale_lock); + + if (result != -1) + *strp += maxlen; + + return result; +} diff --git a/REORG.TODO/time/asctime.c b/REORG.TODO/time/asctime.c new file mode 100644 index 0000000000..b57de7dc8b --- /dev/null +++ b/REORG.TODO/time/asctime.c @@ -0,0 +1,89 @@ +/* Copyright (C) 1991-2017 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 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, see + <http://www.gnu.org/licenses/>. */ + +#include "../locale/localeinfo.h" +#include <errno.h> +#include <limits.h> +#include <stdio.h> +#include <time.h> + +/* This is defined in locale/C-time.c in the GNU libc. */ +extern const struct __locale_data _nl_C_LC_TIME attribute_hidden; +#define ab_day_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) + +static const char format[] = "%.3s %.3s%3d %.2d:%.2d:%.2d %d\n"; +static char result[ 3+1+ 3+1+20+1+20+1+20+1+20+1+20+1 + 1]; + + +static char * +asctime_internal (const struct tm *tp, char *buf, size_t buflen) +{ + if (tp == NULL) + { + __set_errno (EINVAL); + return NULL; + } + + /* We limit the size of the year which can be printed. Using the %d + format specifier used the addition of 1900 would overflow the + number and a negative vaue is printed. For some architectures we + could in theory use %ld or an evern larger integer format but + this would mean the output needs more space. This would not be a + problem if the 'asctime_r' interface would be defined sanely and + a buffer size would be passed. */ + if (__glibc_unlikely (tp->tm_year > INT_MAX - 1900)) + { + eoverflow: + __set_errno (EOVERFLOW); + return NULL; + } + + int n = __snprintf (buf, buflen, format, + (tp->tm_wday < 0 || tp->tm_wday >= 7 ? + "???" : ab_day_name (tp->tm_wday)), + (tp->tm_mon < 0 || tp->tm_mon >= 12 ? + "???" : ab_month_name (tp->tm_mon)), + tp->tm_mday, tp->tm_hour, tp->tm_min, + tp->tm_sec, 1900 + tp->tm_year); + if (n < 0) + return NULL; + if (n >= buflen) + goto eoverflow; + + return buf; +} + + +/* Like asctime, but write result to the user supplied buffer. The + buffer is only guaranteed to be 26 bytes in length. */ +char * +__asctime_r (const struct tm *tp, char *buf) +{ + return asctime_internal (tp, buf, 26); +} +weak_alias (__asctime_r, asctime_r) + + +/* Returns a string of the form "Day Mon dd hh:mm:ss yyyy\n" + which is the representation of TP in that form. */ +char * +asctime (const struct tm *tp) +{ + return asctime_internal (tp, result, sizeof (result)); +} +libc_hidden_def (asctime) diff --git a/REORG.TODO/time/bits/types/clock_t.h b/REORG.TODO/time/bits/types/clock_t.h new file mode 100644 index 0000000000..ce97248f88 --- /dev/null +++ b/REORG.TODO/time/bits/types/clock_t.h @@ -0,0 +1,9 @@ +#ifndef __clock_t_defined +#define __clock_t_defined 1 + +#include <bits/types.h> + +/* Returned by `clock'. */ +typedef __clock_t clock_t; + +#endif diff --git a/REORG.TODO/time/bits/types/clockid_t.h b/REORG.TODO/time/bits/types/clockid_t.h new file mode 100644 index 0000000000..b17c7da853 --- /dev/null +++ b/REORG.TODO/time/bits/types/clockid_t.h @@ -0,0 +1,9 @@ +#ifndef __clockid_t_defined +#define __clockid_t_defined 1 + +#include <bits/types.h> + +/* Clock ID used in clock and timer functions. */ +typedef __clockid_t clockid_t; + +#endif diff --git a/REORG.TODO/time/bits/types/struct_itimerspec.h b/REORG.TODO/time/bits/types/struct_itimerspec.h new file mode 100644 index 0000000000..17cc1ac86d --- /dev/null +++ b/REORG.TODO/time/bits/types/struct_itimerspec.h @@ -0,0 +1,14 @@ +#ifndef __itimerspec_defined +#define __itimerspec_defined 1 + +#include <bits/types.h> +#include <bits/types/struct_timespec.h> + +/* POSIX.1b structure for timer start values and intervals. */ +struct itimerspec + { + struct timespec it_interval; + struct timespec it_value; + }; + +#endif diff --git a/REORG.TODO/time/bits/types/struct_timespec.h b/REORG.TODO/time/bits/types/struct_timespec.h new file mode 100644 index 0000000000..644db9fdb6 --- /dev/null +++ b/REORG.TODO/time/bits/types/struct_timespec.h @@ -0,0 +1,14 @@ +#ifndef __timespec_defined +#define __timespec_defined 1 + +#include <bits/types.h> + +/* POSIX.1b structure for a time value. This is like a `struct timeval' but + has nanoseconds instead of microseconds. */ +struct timespec +{ + __time_t tv_sec; /* Seconds. */ + __syscall_slong_t tv_nsec; /* Nanoseconds. */ +}; + +#endif diff --git a/REORG.TODO/time/bits/types/struct_timeval.h b/REORG.TODO/time/bits/types/struct_timeval.h new file mode 100644 index 0000000000..70394ce886 --- /dev/null +++ b/REORG.TODO/time/bits/types/struct_timeval.h @@ -0,0 +1,13 @@ +#ifndef __timeval_defined +#define __timeval_defined 1 + +#include <bits/types.h> + +/* A time value that is accurate to the nearest + microsecond but also has a range of years. */ +struct timeval +{ + __time_t tv_sec; /* Seconds. */ + __suseconds_t tv_usec; /* Microseconds. */ +}; +#endif diff --git a/REORG.TODO/time/bits/types/struct_tm.h b/REORG.TODO/time/bits/types/struct_tm.h new file mode 100644 index 0000000000..b13b631228 --- /dev/null +++ b/REORG.TODO/time/bits/types/struct_tm.h @@ -0,0 +1,28 @@ +#ifndef __struct_tm_defined +#define __struct_tm_defined 1 + +#include <bits/types.h> + +/* ISO C `broken-down time' structure. */ +struct tm +{ + int tm_sec; /* Seconds. [0-60] (1 leap second) */ + int tm_min; /* Minutes. [0-59] */ + int tm_hour; /* Hours. [0-23] */ + int tm_mday; /* Day. [1-31] */ + int tm_mon; /* Month. [0-11] */ + int tm_year; /* Year - 1900. */ + int tm_wday; /* Day of week. [0-6] */ + int tm_yday; /* Days in year.[0-365] */ + int tm_isdst; /* DST. [-1/0/1]*/ + +# ifdef __USE_MISC + long int tm_gmtoff; /* Seconds east of UTC. */ + const char *tm_zone; /* Timezone abbreviation. */ +# else + long int __tm_gmtoff; /* Seconds east of UTC. */ + const char *__tm_zone; /* Timezone abbreviation. */ +# endif +}; + +#endif diff --git a/REORG.TODO/time/bits/types/time_t.h b/REORG.TODO/time/bits/types/time_t.h new file mode 100644 index 0000000000..ab8287c6fe --- /dev/null +++ b/REORG.TODO/time/bits/types/time_t.h @@ -0,0 +1,9 @@ +#ifndef __time_t_defined +#define __time_t_defined 1 + +#include <bits/types.h> + +/* Returned by `time'. */ +typedef __time_t time_t; + +#endif diff --git a/REORG.TODO/time/bits/types/timer_t.h b/REORG.TODO/time/bits/types/timer_t.h new file mode 100644 index 0000000000..d71a4130e2 --- /dev/null +++ b/REORG.TODO/time/bits/types/timer_t.h @@ -0,0 +1,9 @@ +#ifndef __timer_t_defined +#define __timer_t_defined 1 + +#include <bits/types.h> + +/* Timer ID returned by `timer_create'. */ +typedef __timer_t timer_t; + +#endif diff --git a/REORG.TODO/time/bug-asctime.c b/REORG.TODO/time/bug-asctime.c new file mode 100644 index 0000000000..0b04b475a8 --- /dev/null +++ b/REORG.TODO/time/bug-asctime.c @@ -0,0 +1,33 @@ +#include <errno.h> +#include <limits.h> +#include <stdio.h> +#include <time.h> + + +static int +do_test (void) +{ + int result = 0; + time_t t = time (NULL); + struct tm *tp = localtime (&t); + tp->tm_year = INT_MAX; + errno = 0; + char *s = asctime (tp); + if (s != NULL || errno != EOVERFLOW) + { + puts ("asctime did not fail correctly"); + result = 1; + } + char buf[1000]; + errno = 0; + s = asctime_r (tp, buf); + if (s != NULL || errno != EOVERFLOW) + { + puts ("asctime_r did not fail correctly"); + result = 1; + } + return result; +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/REORG.TODO/time/bug-asctime_r.c b/REORG.TODO/time/bug-asctime_r.c new file mode 100644 index 0000000000..86651ef1c5 --- /dev/null +++ b/REORG.TODO/time/bug-asctime_r.c @@ -0,0 +1,32 @@ +#include <errno.h> +#include <limits.h> +#include <stdio.h> +#include <time.h> + + +static int +do_test (void) +{ + int result = 0; + time_t t = time (NULL); + struct tm *tp = localtime (&t); + tp->tm_year = 10000 - 1900; + char buf[1000]; + errno = 0; + buf[26] = '\xff'; + char *s = asctime_r (tp, buf); + if (s != NULL || errno != EOVERFLOW) + { + puts ("asctime_r did not fail correctly"); + result = 1; + } + if (buf[26] != '\xff') + { + puts ("asctime_r overwrote 27th byte in buffer"); + result = 1; + } + return result; +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/REORG.TODO/time/bug-getdate1.c b/REORG.TODO/time/bug-getdate1.c new file mode 100644 index 0000000000..9afafba5be --- /dev/null +++ b/REORG.TODO/time/bug-getdate1.c @@ -0,0 +1,152 @@ +/* BZ #5451 */ +#include <time.h> +#include <stdio.h> +#include <stdlib.h> + +#include <support/temp_file.h> + +static char *templ_filename; + +// Writes template given as parameter to file, +// specified as the argument +static void +output_to_template_file (const char *str) +{ + FILE *fd = fopen (templ_filename, "w"); + if (fd == NULL) + { + printf ("Can not open file for writing\n"); + exit (1); + } + + fprintf (fd, "%s\n", str); + fclose (fd); +} + +// Calls getdate() function with specified parameter, +// specified as the argument, also checks the contents of +// file with template and prints the result +static int +process_getdate_on (const char *str) +{ + struct tm *res; + char templ[1000]; + FILE *fd = fopen (templ_filename, "r"); + + if (fd == NULL) + { + printf ("Can not open file for reading\n"); + exit (1); + } + + if (fgets (templ, 1000, fd) == NULL) + { + printf ("Can not read file\n"); + exit (1); + } + fclose (fd); + + res = getdate (str); + if (res == NULL) + { + printf ("Failed on getdate(\"%s\"), template is: %s", str, templ); + printf ("Error number: %d\n\n", getdate_err); + return 1; + } + printf ("Success on getdate(\"%s\"), template is: %s\n", str, templ); + printf ("Result is\n"); + printf ("Seconds: %d\n", res->tm_sec); + printf ("Minutes: %d\n", res->tm_min); + printf ("Hour: %d\n", res->tm_hour); + printf ("Day of month: %d\n", res->tm_mday); + printf ("Month of year: %d\n", res->tm_mon); + printf ("Years since 1900: %d\n", res->tm_year); + printf ("Day of week: %d\n", res->tm_wday); + printf ("Day of year: %d\n", res->tm_yday); + printf ("Daylight Savings flag: %d\n\n", res->tm_isdst); + return 0; +} + +static int +do_test (int argc, char *argv[]) +{ + + templ_filename = argv[1]; + + setenv ("DATEMSK", templ_filename, 1); + + /* + * The following 4 testcases reproduce the problem: + * 1. Templates "%S" and "%M" are not processed, + * when used without "%H" template + */ + int res = 0; + output_to_template_file ("%M"); + res |= process_getdate_on ("1"); + + output_to_template_file ("%M %H"); + res |= process_getdate_on ("1 2"); + + output_to_template_file ("%S"); + res |= process_getdate_on ("1"); + + output_to_template_file ("%S %H"); + res |= process_getdate_on ("1 2"); + + /* + * The following 9 testcases reproduce the problem: + * 2. Templates "%Y", "%y", "%d", "%C", "%C %y" + * are not processed separately + */ + output_to_template_file ("%Y"); + process_getdate_on ("2001"); + + output_to_template_file ("%Y %m"); + res |= process_getdate_on ("2001 3"); + + output_to_template_file ("%y"); + res |= process_getdate_on ("70"); + + output_to_template_file ("%y %m"); + res |= process_getdate_on ("70 3"); + + output_to_template_file ("%d"); + res |= process_getdate_on ("06"); + + output_to_template_file ("%d %m"); + res |= process_getdate_on ("25 3"); + + output_to_template_file ("%C"); + res |= process_getdate_on ("20"); + + output_to_template_file ("%C %y %m"); + res |= process_getdate_on ("20 3 2"); + + output_to_template_file ("%C %y"); + res |= process_getdate_on ("20 5"); + + /* + * The following testcase reproduces the problem: + * 3. When template is "%Y %m", day of month is not set + * to 1 as standard requires + */ + output_to_template_file ("%Y %m"); + res |= process_getdate_on ("2008 3"); + + return res; +} +#define TEST_FUNCTION_ARGV do_test + +static void +do_prepare (int argc, char **argv) +{ + if (argc < 2) + { + puts ("Command line: progname template_filename_full_path"); + exit (1); + } + add_temp_file (argv[1]); +} +#define PREPARE do_prepare + +#include <support/test-driver.c> diff --git a/REORG.TODO/time/bug-mktime1.c b/REORG.TODO/time/bug-mktime1.c new file mode 100644 index 0000000000..e071273f05 --- /dev/null +++ b/REORG.TODO/time/bug-mktime1.c @@ -0,0 +1,17 @@ +#include <stdio.h> +#include <time.h> + + +static int +do_test (void) +{ + struct tm t2 = { 0, 0, 0, 1, 1, 2050 - 1900, 1, 1, 1 }; + time_t tt2 = mktime (&t2); + printf ("%ld\n", (long int) tt2); + if (sizeof (time_t) == 4 && tt2 != -1) + return 1; + return 0; +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/REORG.TODO/time/clock.c b/REORG.TODO/time/clock.c new file mode 100644 index 0000000000..bb8e9d323e --- /dev/null +++ b/REORG.TODO/time/clock.c @@ -0,0 +1,30 @@ +/* Copyright (C) 1991-2017 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 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, see + <http://www.gnu.org/licenses/>. */ + +#include <sys/times.h> +#include <time.h> +#include <errno.h> + +/* Return the time used by the program so far (user time + system time). */ +clock_t +clock (void) +{ + __set_errno (ENOSYS); + return (clock_t) -1; +} + +stub_warning (clock) diff --git a/REORG.TODO/time/clocktest.c b/REORG.TODO/time/clocktest.c new file mode 100644 index 0000000000..779c05d8d9 --- /dev/null +++ b/REORG.TODO/time/clocktest.c @@ -0,0 +1,37 @@ +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <time.h> +#include <unistd.h> +#include <stdint.h> + +volatile int gotit = 0; + +static void +alarm_handler (int signal) +{ + gotit = 1; +} + + +int +main (int argc, char ** argv) +{ + clock_t start, stop; + + if (signal(SIGALRM, alarm_handler) == SIG_ERR) + { + perror ("signal"); + exit (1); + } + alarm(1); + start = clock (); + while (!gotit); + stop = clock (); + + printf ("%jd clock ticks per second (start=%jd,stop=%jd)\n", + (intmax_t) (stop - start), (intmax_t) start, (intmax_t) stop); + printf ("CLOCKS_PER_SEC=%jd, sysconf(_SC_CLK_TCK)=%ld\n", + (intmax_t) CLOCKS_PER_SEC, sysconf(_SC_CLK_TCK)); + return 0; +} diff --git a/REORG.TODO/time/ctime.c b/REORG.TODO/time/ctime.c new file mode 100644 index 0000000000..7baca1bb57 --- /dev/null +++ b/REORG.TODO/time/ctime.c @@ -0,0 +1,28 @@ +/* Copyright (C) 1991-2017 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 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, see + <http://www.gnu.org/licenses/>. */ + +#include <time.h> + +/* Return a string as returned by asctime which + is the representation of *T in that form. */ +char * +ctime (const time_t *t) +{ + /* The C Standard says ctime (t) is equivalent to asctime (localtime (t)). + In particular, ctime and asctime must yield the same pointer. */ + return asctime (localtime (t)); +} diff --git a/REORG.TODO/time/ctime_r.c b/REORG.TODO/time/ctime_r.c new file mode 100644 index 0000000000..ecd7731038 --- /dev/null +++ b/REORG.TODO/time/ctime_r.c @@ -0,0 +1,29 @@ +/* Return in BUF representation of time T in form of asctime + Copyright (C) 1996-2017 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, see + <http://www.gnu.org/licenses/>. */ + +#include <time.h> + +/* Return a string as returned by asctime which is the representation + of *T in that form. Reentrant version. */ +char * +ctime_r (const time_t *t, char *buf) +{ + struct tm tm; + return __asctime_r (__localtime_r (t, &tm), buf); +} diff --git a/REORG.TODO/time/datemsk b/REORG.TODO/time/datemsk new file mode 100644 index 0000000000..3dc4d417fe --- /dev/null +++ b/REORG.TODO/time/datemsk @@ -0,0 +1,2 @@ +%H:%M:%S %F +%d-%m-%Y %T diff --git a/REORG.TODO/time/difftime.c b/REORG.TODO/time/difftime.c new file mode 100644 index 0000000000..e5e3311744 --- /dev/null +++ b/REORG.TODO/time/difftime.c @@ -0,0 +1,121 @@ +/* Copyright (C) 1991-2017 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 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, see + <http://www.gnu.org/licenses/>. */ + +/* Written by Paul Eggert <eggert@cs.ucla.edu>. */ + +#include <time.h> + +#include <limits.h> +#include <float.h> +#include <stdint.h> + +#define TYPE_BITS(type) (sizeof (type) * CHAR_BIT) +#define TYPE_FLOATING(type) ((type) 0.5 == 0.5) +#define TYPE_SIGNED(type) ((type) -1 < 0) + +/* Return the difference between TIME1 and TIME0, where TIME0 <= TIME1. + time_t is known to be an integer type. */ + +static double +subtract (time_t time1, time_t time0) +{ + if (! TYPE_SIGNED (time_t)) + return time1 - time0; + else + { + /* Optimize the common special cases where time_t + can be converted to uintmax_t without losing information. */ + uintmax_t dt = (uintmax_t) time1 - (uintmax_t) time0; + double delta = dt; + + if (UINTMAX_MAX / 2 < INTMAX_MAX) + { + /* This is a rare host where uintmax_t has padding bits, and possibly + information was lost when converting time_t to uintmax_t. + Check for overflow by comparing dt/2 to (time1/2 - time0/2). + Overflow occurred if they differ by more than a small slop. + Thanks to Clive D.W. Feather for detailed technical advice about + hosts with padding bits. + + In the following code the "h" prefix means half. By range + analysis, we have: + + -0.5 <= ht1 - 0.5*time1 <= 0.5 + -0.5 <= ht0 - 0.5*time0 <= 0.5 + -1.0 <= dht - 0.5*(time1 - time0) <= 1.0 + + If overflow has not occurred, we also have: + + -0.5 <= hdt - 0.5*(time1 - time0) <= 0 + -1.0 <= dht - hdt <= 1.5 + + and since dht - hdt is an integer, we also have: + + -1 <= dht - hdt <= 1 + + or equivalently: + + 0 <= dht - hdt + 1 <= 2 + + In the above analysis, all the operators have their exact + mathematical semantics, not C semantics. However, dht - hdt + + 1 is unsigned in C, so it need not be compared to zero. */ + + uintmax_t hdt = dt / 2; + time_t ht1 = time1 / 2; + time_t ht0 = time0 / 2; + time_t dht = ht1 - ht0; + + if (2 < dht - hdt + 1) + { + /* Repair delta overflow. + + The following expression contains a second rounding, + so the result may not be the closest to the true answer. + This problem occurs only with very large differences. + It's too painful to fix this portably. */ + + delta = dt + 2.0L * (UINTMAX_MAX - UINTMAX_MAX / 2); + } + } + + return delta; + } +} + +/* Return the difference between TIME1 and TIME0. */ +double +__difftime (time_t time1, time_t time0) +{ + /* Convert to double and then subtract if no double-rounding error could + result. */ + + if (TYPE_BITS (time_t) <= DBL_MANT_DIG + || (TYPE_FLOATING (time_t) && sizeof (time_t) < sizeof (long double))) + return (double) time1 - (double) time0; + + /* Likewise for long double. */ + + if (TYPE_BITS (time_t) <= LDBL_MANT_DIG || TYPE_FLOATING (time_t)) + return (long double) time1 - (long double) time0; + + /* Subtract the smaller integer from the larger, convert the difference to + double, and then negate if needed. */ + + return time1 < time0 ? - subtract (time0, time1) : subtract (time1, time0); +} +strong_alias (__difftime, difftime) diff --git a/REORG.TODO/time/dysize.c b/REORG.TODO/time/dysize.c new file mode 100644 index 0000000000..feed19c2d7 --- /dev/null +++ b/REORG.TODO/time/dysize.c @@ -0,0 +1,24 @@ +/* Copyright (C) 1994-2017 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 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, see + <http://www.gnu.org/licenses/>. */ + +#include <time.h> + +int +dysize (int year) +{ + return __isleap (year) ? 366 : 365; +} diff --git a/REORG.TODO/time/era.c b/REORG.TODO/time/era.c new file mode 100644 index 0000000000..b5743c621a --- /dev/null +++ b/REORG.TODO/time/era.c @@ -0,0 +1,177 @@ +/* Helper functions used by strftime/strptime to handle locale-specific "eras". + Copyright (C) 1995-2017 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 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, see + <http://www.gnu.org/licenses/>. */ + +#include "../locale/localeinfo.h" +#include <libc-lock.h> +#include <stdlib.h> +#include <wchar.h> +#include <string.h> +#include <stdint.h> + +/* Some of the functions here must not be used while setlocale is called. */ +__libc_rwlock_define (extern, __libc_setlocale_lock attribute_hidden) + +#define CURRENT(item) (current->values[_NL_ITEM_INDEX (item)].string) +#define CURRENT_WORD(item) (current->values[_NL_ITEM_INDEX (item)].word) + +#define ERA_DATE_CMP(a, b) \ + (a[0] < b[0] || (a[0] == b[0] && (a[1] < b[1] \ + || (a[1] == b[1] && a[2] <= b[2])))) + +/* Look up the era information in CURRENT's locale strings and + cache it in CURRENT->private. */ +static void internal_function +_nl_init_era_entries (struct __locale_data *current) +{ + size_t cnt; + struct lc_time_data *data; + + /* Avoid touching CURRENT if there is no data at all, for _nl_C_LC_TIME. */ + if (CURRENT_WORD (_NL_TIME_ERA_NUM_ENTRIES) == 0) + return; + + __libc_rwlock_wrlock (__libc_setlocale_lock); + + if (current->private.time == NULL) + { + current->private.time = malloc (sizeof *current->private.time); + if (current->private.time == NULL) + goto out; + memset (current->private.time, 0, sizeof *current->private.time); + current->private.cleanup = &_nl_cleanup_time; + } + data = current->private.time; + + if (! data->era_initialized) + { + size_t new_num_eras = CURRENT_WORD (_NL_TIME_ERA_NUM_ENTRIES); + if (new_num_eras == 0) + { + if (data->eras != NULL) + { + free (data->eras); + data->eras = NULL; + } + } + else + { + struct era_entry *new_eras = data->eras; + + if (data->num_eras != new_num_eras) + new_eras = + (struct era_entry *) realloc (data->eras, + new_num_eras + * sizeof (struct era_entry)); + if (new_eras == NULL) + { + free (data->eras); + data->num_eras = 0; + data->eras = NULL; + } + else + { + const char *ptr = CURRENT (_NL_TIME_ERA_ENTRIES); + data->num_eras = new_num_eras; + data->eras = new_eras; + + for (cnt = 0; cnt < new_num_eras; ++cnt) + { + const char *base_ptr = ptr; + memcpy ((void *) (new_eras + cnt), (const void *) ptr, + sizeof (uint32_t) * 8); + + if (ERA_DATE_CMP(new_eras[cnt].start_date, + new_eras[cnt].stop_date)) + if (new_eras[cnt].direction == (uint32_t) '+') + new_eras[cnt].absolute_direction = 1; + else + new_eras[cnt].absolute_direction = -1; + else + if (new_eras[cnt].direction == (uint32_t) '+') + new_eras[cnt].absolute_direction = -1; + else + new_eras[cnt].absolute_direction = 1; + + /* Skip numeric values. */ + ptr += sizeof (uint32_t) * 8; + + /* Set and skip era name. */ + new_eras[cnt].era_name = ptr; + ptr = strchr (ptr, '\0') + 1; + + /* Set and skip era format. */ + new_eras[cnt].era_format = ptr; + ptr = strchr (ptr, '\0') + 1; + + ptr += 3 - (((ptr - (const char *) base_ptr) + 3) & 3); + + /* Set and skip wide era name. */ + new_eras[cnt].era_wname = (wchar_t *) ptr; + ptr = (char *) (__wcschr ((wchar_t *) ptr, L'\0') + 1); + + /* Set and skip wide era format. */ + new_eras[cnt].era_wformat = (wchar_t *) ptr; + ptr = (char *) (__wcschr ((wchar_t *) ptr, L'\0') + 1); + } + } + } + + data->era_initialized = 1; + } + + out: + __libc_rwlock_unlock (__libc_setlocale_lock); +} + +struct era_entry * +internal_function +_nl_get_era_entry (const struct tm *tp, struct __locale_data *current) +{ + if (current->private.time == NULL || !current->private.time->era_initialized) + _nl_init_era_entries (current); + + if (current->private.time != NULL) + { + /* Now compare date with the available eras. */ + const int32_t tdate[3] = { tp->tm_year, tp->tm_mon, tp->tm_mday }; + size_t cnt; + for (cnt = 0; cnt < current->private.time->num_eras; ++cnt) + if ((ERA_DATE_CMP (current->private.time->eras[cnt].start_date, tdate) + && ERA_DATE_CMP (tdate, + current->private.time->eras[cnt].stop_date)) + || (ERA_DATE_CMP (current->private.time->eras[cnt].stop_date, + tdate) + && ERA_DATE_CMP (tdate, + current->private.time->eras[cnt].start_date))) + return ¤t->private.time->eras[cnt]; + } + + return NULL; +} + + +struct era_entry * +internal_function +_nl_select_era_entry (int cnt, struct __locale_data *current) +{ + if (current->private.time == NULL || !current->private.time->era_initialized) + _nl_init_era_entries (current); + + return (current->private.time == NULL + ? NULL : ¤t->private.time->eras[cnt]); +} diff --git a/REORG.TODO/time/ftime.c b/REORG.TODO/time/ftime.c new file mode 100644 index 0000000000..1cfb53cc04 --- /dev/null +++ b/REORG.TODO/time/ftime.c @@ -0,0 +1,41 @@ +/* Copyright (C) 1994-2017 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 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, see + <http://www.gnu.org/licenses/>. */ + +#include <errno.h> +#include <time.h> +#include <sys/timeb.h> + +int +ftime (struct timeb *timebuf) +{ + int save = errno; + struct tm tp; + + __set_errno (0); + if (time (&timebuf->time) == (time_t) -1 && errno != 0) + return -1; + timebuf->millitm = 0; + + if (__localtime_r (&timebuf->time, &tp) == NULL) + return -1; + + timebuf->timezone = tp.tm_gmtoff / 60; + timebuf->dstflag = tp.tm_isdst; + + __set_errno (save); + return 0; +} diff --git a/REORG.TODO/time/getdate.c b/REORG.TODO/time/getdate.c new file mode 100644 index 0000000000..28ea48287f --- /dev/null +++ b/REORG.TODO/time/getdate.c @@ -0,0 +1,312 @@ +/* Convert a string representation of time to a time value. + Copyright (C) 1997-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Mark Kettenis <kettenis@phys.uva.nl>, 1997. + + 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, see + <http://www.gnu.org/licenses/>. */ + +#include <limits.h> +#include <stdio.h> +#include <stdio_ext.h> +#include <stdlib.h> +#include <stdbool.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <sys/stat.h> +#include <ctype.h> +#include <alloca.h> + +#define TM_YEAR_BASE 1900 + + +/* Prototypes for local functions. */ +static int first_wday (int year, int mon, int wday); +static int check_mday (int year, int mon, int mday); + + +/* Set to one of the following values to indicate an error. + 1 the DATEMSK environment variable is null or undefined, + 2 the template file cannot be opened for reading, + 3 failed to get file status information, + 4 the template file is not a regular file, + 5 an error is encountered while reading the template file, + 6 memory allication failed (not enough memory available), + 7 there is no line in the template that matches the input, + 8 invalid input specification Example: February 31 or a time is + specified that can not be represented in a time_t (representing + the time in seconds since 00:00:00 UTC, January 1, 1970) */ +int getdate_err; + + +/* Returns the first weekday WDAY of month MON in the year YEAR. */ +static int +first_wday (int year, int mon, int wday) +{ + struct tm tm; + + if (wday == INT_MIN) + return 1; + + memset (&tm, 0, sizeof (struct tm)); + tm.tm_year = year; + tm.tm_mon = mon; + tm.tm_mday = 1; + mktime (&tm); + + return (1 + (wday - tm.tm_wday + 7) % 7); +} + + +/* Returns 1 if MDAY is a valid day of the month in month MON of year + YEAR, and 0 if it is not. */ +static int +check_mday (int year, int mon, int mday) +{ + switch (mon) + { + case 0: + case 2: + case 4: + case 6: + case 7: + case 9: + case 11: + if (mday >= 1 && mday <= 31) + return 1; + break; + case 3: + case 5: + case 8: + case 10: + if (mday >= 1 && mday <= 30) + return 1; + break; + case 1: + if (mday >= 1 && mday <= (__isleap (year) ? 29 : 28)) + return 1; + break; + } + + return 0; +} + + +int +__getdate_r (const char *string, struct tm *tp) +{ + FILE *fp; + char *line; + size_t len; + char *datemsk; + char *result = NULL; + time_t timer; + struct tm tm; + struct stat64 st; + int mday_ok = 0; + + datemsk = getenv ("DATEMSK"); + if (datemsk == NULL || *datemsk == '\0') + return 1; + + if (stat64 (datemsk, &st) < 0) + return 3; + + if (!S_ISREG (st.st_mode)) + return 4; + + if (__access (datemsk, R_OK) < 0) + return 2; + + /* Open the template file. */ + fp = fopen (datemsk, "rce"); + if (fp == NULL) + return 2; + + /* No threads reading this stream. */ + __fsetlocking (fp, FSETLOCKING_BYCALLER); + + /* Skip leading whitespace. */ + while (isspace (*string)) + string++; + + size_t inlen, oldlen; + + oldlen = inlen = strlen (string); + + /* Skip trailing whitespace. */ + while (inlen > 0 && isspace (string[inlen - 1])) + inlen--; + + char *instr = NULL; + + if (inlen < oldlen) + { + bool using_malloc = false; + + if (__libc_use_alloca (inlen + 1)) + instr = alloca (inlen + 1); + else + { + instr = malloc (inlen + 1); + if (instr == NULL) + { + fclose (fp); + return 6; + } + using_malloc = true; + } + memcpy (instr, string, inlen); + instr[inlen] = '\0'; + string = instr; + + if (!using_malloc) + instr = NULL; + } + + line = NULL; + len = 0; + do + { + ssize_t n; + + n = __getline (&line, &len, fp); + if (n < 0) + break; + if (line[n - 1] == '\n') + line[n - 1] = '\0'; + + /* Do the conversion. */ + tp->tm_year = tp->tm_mon = tp->tm_mday = tp->tm_wday = INT_MIN; + tp->tm_hour = tp->tm_sec = tp->tm_min = INT_MIN; + tp->tm_isdst = -1; + tp->tm_gmtoff = 0; + tp->tm_zone = NULL; + result = strptime (string, line, tp); + if (result && *result == '\0') + break; + } + while (!feof_unlocked (fp)); + + free (instr); + + /* Free the buffer. */ + free (line); + + /* Check for errors. */ + if (ferror_unlocked (fp)) + { + fclose (fp); + return 5; + } + + /* Close template file. */ + fclose (fp); + + if (result == NULL || *result != '\0') + return 7; + + /* Get current time. */ + time (&timer); + __localtime_r (&timer, &tm); + + /* If only the weekday is given, today is assumed if the given day + is equal to the current day and next week if it is less. */ + if (tp->tm_wday >= 0 && tp->tm_wday <= 6 && tp->tm_year == INT_MIN + && tp->tm_mon == INT_MIN && tp->tm_mday == INT_MIN) + { + tp->tm_year = tm.tm_year; + tp->tm_mon = tm.tm_mon; + tp->tm_mday = tm.tm_mday + (tp->tm_wday - tm.tm_wday + 7) % 7; + mday_ok = 1; + } + + /* If only the month is given, the current month is assumed if the + given month is equal to the current month and next year if it is + less and no year is given (the first day of month is assumed if + no day is given. */ + if (tp->tm_mon >= 0 && tp->tm_mon <= 11 && tp->tm_mday == INT_MIN) + { + if (tp->tm_year == INT_MIN) + tp->tm_year = tm.tm_year + (((tp->tm_mon - tm.tm_mon) < 0) ? 1 : 0); + tp->tm_mday = first_wday (tp->tm_year, tp->tm_mon, tp->tm_wday); + mday_ok = 1; + } + + /* If no hour, minute and second are given the current hour, minute + and second are assumed. */ + if (tp->tm_hour == INT_MIN && tp->tm_min == INT_MIN && tp->tm_sec == INT_MIN) + { + tp->tm_hour = tm.tm_hour; + tp->tm_min = tm.tm_min; + tp->tm_sec = tm.tm_sec; + } + + /* Fill in the gaps. */ + if (tp->tm_hour == INT_MIN) + tp->tm_hour = 0; + if (tp->tm_min == INT_MIN) + tp->tm_min = 0; + if (tp->tm_sec == INT_MIN) + tp->tm_sec = 0; + + /* If no date is given, today is assumed if the given hour is + greater than the current hour and tomorrow is assumed if + it is less. */ + if (tp->tm_hour >= 0 && tp->tm_hour <= 23 + && tp->tm_mon == INT_MIN + && tp->tm_mday == INT_MIN && tp->tm_wday == INT_MIN) + { + tp->tm_mon = tm.tm_mon; + tp->tm_mday = tm.tm_mday + ((tp->tm_hour - tm.tm_hour) < 0 ? 1 : 0); + mday_ok = 1; + } + + /* More fillers. */ + if (tp->tm_year == INT_MIN) + tp->tm_year = tm.tm_year; + if (tp->tm_mon == INT_MIN) + tp->tm_mon = tm.tm_mon; + + /* Check if the day of month is within range, and if the time can be + represented in a time_t. We make use of the fact that the mktime + call normalizes the struct tm. */ + if ((!mday_ok && !check_mday (TM_YEAR_BASE + tp->tm_year, tp->tm_mon, + tp->tm_mday)) + || mktime (tp) == (time_t) -1) + return 8; + + return 0; +} +#ifdef weak_alias +weak_alias (__getdate_r, getdate_r) +#endif + + +struct tm * +getdate (const char *string) +{ + /* Buffer returned by getdate. */ + static struct tm tmbuf; + int errval = __getdate_r (string, &tmbuf); + + if (errval != 0) + { + getdate_err = errval; + return NULL; + } + + return &tmbuf; +} diff --git a/REORG.TODO/time/getitimer.c b/REORG.TODO/time/getitimer.c new file mode 100644 index 0000000000..f42e728bdf --- /dev/null +++ b/REORG.TODO/time/getitimer.c @@ -0,0 +1,38 @@ +/* Copyright (C) 1991-2017 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 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, see + <http://www.gnu.org/licenses/>. */ + +#include <stddef.h> +#include <errno.h> +#include <sys/time.h> + +/* Set *VALUE to the current setting of timer WHICH. + Return 0 on success, -1 on errors. */ +int +__getitimer (enum __itimer_which which, struct itimerval *value) +{ + if (value == NULL) + { + __set_errno (EINVAL); + return -1; + } + + __set_errno (ENOSYS); + return -1; +} +stub_warning (getitimer) + +weak_alias (__getitimer, getitimer) diff --git a/REORG.TODO/time/gettimeofday.c b/REORG.TODO/time/gettimeofday.c new file mode 100644 index 0000000000..1d675b6b23 --- /dev/null +++ b/REORG.TODO/time/gettimeofday.c @@ -0,0 +1,34 @@ +/* Copyright (C) 1991-2017 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 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, see + <http://www.gnu.org/licenses/>. */ + +#include <errno.h> +#include <sys/time.h> + +/* Get the current time of day and timezone information, + putting it into *TV and *TZ. If TZ is NULL, *TZ is not filled. + Returns 0 on success, -1 on errors. */ +int +__gettimeofday (struct timeval *tv, struct timezone *tz) +{ + __set_errno (ENOSYS); + return -1; +} +libc_hidden_def (__gettimeofday) +weak_alias (__gettimeofday, gettimeofday) +libc_hidden_weak (gettimeofday) + +stub_warning (gettimeofday) diff --git a/REORG.TODO/time/gmtime.c b/REORG.TODO/time/gmtime.c new file mode 100644 index 0000000000..049d551cdf --- /dev/null +++ b/REORG.TODO/time/gmtime.c @@ -0,0 +1,37 @@ +/* Convert `time_t' to `struct tm' in UTC. + Copyright (C) 1991-2017 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 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, see + <http://www.gnu.org/licenses/>. */ + +#include <time.h> + +/* Return the `struct tm' representation of *T in UTC, + using *TP to store the result. */ +struct tm * +__gmtime_r (const time_t *t, struct tm *tp) +{ + return __tz_convert (t, 0, tp); +} +libc_hidden_def (__gmtime_r) +weak_alias (__gmtime_r, gmtime_r) + + +/* Return the `struct tm' representation of *T in UTC. */ +struct tm * +gmtime (const time_t *t) +{ + return __tz_convert (t, 0, &_tmbuf); +} diff --git a/REORG.TODO/time/lc-time-cleanup.c b/REORG.TODO/time/lc-time-cleanup.c new file mode 100644 index 0000000000..92b5be5818 --- /dev/null +++ b/REORG.TODO/time/lc-time-cleanup.c @@ -0,0 +1,37 @@ +/* Cleanup code for data structures kept by strftime/strptime helper functions. + Copyright (C) 2002-2017 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 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, see + <http://www.gnu.org/licenses/>. */ + +#include "../locale/localeinfo.h" +#include <stdlib.h> + +void internal_function +_nl_cleanup_time (struct __locale_data *locale) +{ + struct lc_time_data *const data = locale->private.time; + if (data != NULL) + { + locale->private.time = NULL; + locale->private.cleanup = NULL; + + free (data->eras); + free (data->alt_digits); + free (data->walt_digits); + + free (data); + } +} diff --git a/REORG.TODO/time/localtime.c b/REORG.TODO/time/localtime.c new file mode 100644 index 0000000000..07dd67ca71 --- /dev/null +++ b/REORG.TODO/time/localtime.c @@ -0,0 +1,41 @@ +/* Convert `time_t' to `struct tm' in local time zone. + Copyright (C) 1991-2017 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 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, see + <http://www.gnu.org/licenses/>. */ + +#include <time.h> + +/* The C Standard says that localtime and gmtime return the same pointer. */ +struct tm _tmbuf; + + +/* Return the `struct tm' representation of *T in local time, + using *TP to store the result. */ +struct tm * +__localtime_r (const time_t *t, struct tm *tp) +{ + return __tz_convert (t, 1, tp); +} +weak_alias (__localtime_r, localtime_r) + + +/* Return the `struct tm' representation of *T in local time. */ +struct tm * +localtime (const time_t *t) +{ + return __tz_convert (t, 1, &_tmbuf); +} +libc_hidden_def (localtime) diff --git a/REORG.TODO/time/mktime.c b/REORG.TODO/time/mktime.c new file mode 100644 index 0000000000..4c48d358a1 --- /dev/null +++ b/REORG.TODO/time/mktime.c @@ -0,0 +1,741 @@ +/* Convert a 'struct tm' to a time_t value. + Copyright (C) 1993-2017 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, see + <http://www.gnu.org/licenses/>. */ + +/* Define this to have a standalone program to test this implementation of + mktime. */ +/* #define DEBUG_MKTIME 1 */ + +#ifndef _LIBC +# include <config.h> +#endif + +/* Assume that leap seconds are possible, unless told otherwise. + If the host has a 'zic' command with a '-L leapsecondfilename' option, + then it supports leap seconds; otherwise it probably doesn't. */ +#ifndef LEAP_SECONDS_POSSIBLE +# define LEAP_SECONDS_POSSIBLE 1 +#endif + +#include <time.h> + +#include <limits.h> + +#include <string.h> /* For the real memcpy prototype. */ + +#if defined DEBUG_MKTIME && DEBUG_MKTIME +# include <stdio.h> +# include <stdlib.h> +/* Make it work even if the system's libc has its own mktime routine. */ +# undef mktime +# define mktime my_mktime +#endif /* DEBUG_MKTIME */ + +/* Some of the code in this file assumes that signed integer overflow + silently wraps around. This assumption can't easily be programmed + around, nor can it be checked for portably at compile-time or + easily eliminated at run-time. + + Define WRAPV to 1 if the assumption is valid and if + #pragma GCC optimize ("wrapv") + does not trigger GCC bug 51793 + <http://gcc.gnu.org/bugzilla/show_bug.cgi?id=51793>. + Otherwise, define it to 0; this forces the use of slower code that, + while not guaranteed by the C Standard, works on all production + platforms that we know about. */ +#ifndef WRAPV +# if (((__GNUC__ == 4 && 4 <= __GNUC_MINOR__) || 4 < __GNUC__) \ + && defined __GLIBC__) +# pragma GCC optimize ("wrapv") +# define WRAPV 1 +# else +# define WRAPV 0 +# endif +#endif + +/* Verify a requirement at compile-time (unlike assert, which is runtime). */ +#define verify(name, assertion) struct name { char a[(assertion) ? 1 : -1]; } + +/* A signed type that is at least one bit wider than int. */ +#if INT_MAX <= LONG_MAX / 2 +typedef long int long_int; +#else +typedef long long int long_int; +#endif +verify (long_int_is_wide_enough, INT_MAX == INT_MAX * (long_int) 2 / 2); + +/* Shift A right by B bits portably, by dividing A by 2**B and + truncating towards minus infinity. A and B should be free of side + effects, and B should be in the range 0 <= B <= INT_BITS - 2, where + INT_BITS is the number of useful bits in an int. GNU code can + assume that INT_BITS is at least 32. + + ISO C99 says that A >> B is implementation-defined if A < 0. Some + implementations (e.g., UNICOS 9.0 on a Cray Y-MP EL) don't shift + right in the usual way when A < 0, so SHR falls back on division if + ordinary A >> B doesn't seem to be the usual signed shift. */ +#define SHR(a, b) \ + ((-1 >> 1 == -1 \ + && (long_int) -1 >> 1 == -1 \ + && ((time_t) -1 >> 1 == -1 || ! TYPE_SIGNED (time_t))) \ + ? (a) >> (b) \ + : (a) / (1 << (b)) - ((a) % (1 << (b)) < 0)) + +/* The extra casts in the following macros work around compiler bugs, + e.g., in Cray C 5.0.3.0. */ + +/* True if the arithmetic type T is an integer type. bool counts as + an integer. */ +#define TYPE_IS_INTEGER(t) ((t) 1.5 == 1) + +/* True if negative values of the signed integer type T use two's + complement, or if T is an unsigned integer type. */ +#define TYPE_TWOS_COMPLEMENT(t) ((t) ~ (t) 0 == (t) -1) + +/* True if the arithmetic type T is signed. */ +#define TYPE_SIGNED(t) (! ((t) 0 < (t) -1)) + +/* The maximum and minimum values for the integer type T. These + macros have undefined behavior if T is signed and has padding bits. + If this is a problem for you, please let us know how to fix it for + your host. */ +#define TYPE_MINIMUM(t) \ + ((t) (! TYPE_SIGNED (t) \ + ? (t) 0 \ + : ~ TYPE_MAXIMUM (t))) +#define TYPE_MAXIMUM(t) \ + ((t) (! TYPE_SIGNED (t) \ + ? (t) -1 \ + : ((((t) 1 << (sizeof (t) * CHAR_BIT - 2)) - 1) * 2 + 1))) + +#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 TIME_T_MIDPOINT (SHR (TIME_T_MIN + TIME_T_MAX, 1) + 1) + +verify (time_t_is_integer, TYPE_IS_INTEGER (time_t)); +verify (twos_complement_arithmetic, + (TYPE_TWOS_COMPLEMENT (int) + && TYPE_TWOS_COMPLEMENT (long_int) + && TYPE_TWOS_COMPLEMENT (time_t))); + +#define EPOCH_YEAR 1970 +#define TM_YEAR_BASE 1900 +verify (base_year_is_a_multiple_of_100, TM_YEAR_BASE % 100 == 0); + +/* Return 1 if YEAR + TM_YEAR_BASE is a leap year. */ +static int +leapyear (long_int year) +{ + /* Don't add YEAR to TM_YEAR_BASE, as that might overflow. + Also, work even if YEAR is negative. */ + return + ((year & 3) == 0 + && (year % 100 != 0 + || ((year / 100) & 3) == (- (TM_YEAR_BASE / 100) & 3))); +} + +/* How many days come before each month (0-12). */ +#ifndef _LIBC +static +#endif +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 } + }; + + +#ifndef _LIBC +/* Portable standalone applications should supply a <time.h> that + declares a POSIX-compliant localtime_r, for the benefit of older + implementations that lack localtime_r or have a nonstandard one. + See the gnulib time_r module for one way to implement this. */ +# undef __localtime_r +# define __localtime_r localtime_r +# define __mktime_internal mktime_internal +# include "mktime-internal.h" +#endif + +/* Return 1 if the values A and B differ according to the rules for + tm_isdst: A and B differ if one is zero and the other positive. */ +static int +isdst_differ (int a, int b) +{ + return (!a != !b) && (0 <= a) && (0 <= b); +} + +/* Return an integer value measuring (YEAR1-YDAY1 HOUR1:MIN1:SEC1) - + (YEAR0-YDAY0 HOUR0:MIN0:SEC0) in seconds, assuming that the clocks + were not adjusted between the time stamps. + + The YEAR values uses the same numbering as TP->tm_year. Values + need not be in the usual range. However, YEAR1 must not be less + than 2 * INT_MIN or greater than 2 * INT_MAX. + + The result may overflow. It is the caller's responsibility to + detect overflow. */ + +static time_t +ydhms_diff (long_int year1, long_int yday1, int hour1, int min1, int sec1, + int year0, int yday0, int hour0, int min0, int sec0) +{ + verify (C99_integer_division, -1 / 2 == 0); + + /* Compute intervening leap days correctly even if year is negative. + Take care to avoid integer overflow here. */ + int a4 = SHR (year1, 2) + SHR (TM_YEAR_BASE, 2) - ! (year1 & 3); + int b4 = SHR (year0, 2) + SHR (TM_YEAR_BASE, 2) - ! (year0 & 3); + int a100 = a4 / 25 - (a4 % 25 < 0); + int b100 = b4 / 25 - (b4 % 25 < 0); + int a400 = SHR (a100, 2); + int b400 = SHR (b100, 2); + int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400); + + /* Compute the desired time in time_t precision. Overflow might + occur here. */ + time_t tyear1 = year1; + time_t years = tyear1 - year0; + time_t days = 365 * years + yday1 - yday0 + intervening_leap_days; + time_t hours = 24 * days + hour1 - hour0; + time_t minutes = 60 * hours + min1 - min0; + time_t seconds = 60 * minutes + sec1 - sec0; + return seconds; +} + +/* Return the average of A and B, even if A + B would overflow. */ +static time_t +time_t_avg (time_t a, time_t b) +{ + return SHR (a, 1) + SHR (b, 1) + (a & b & 1); +} + +/* Return 1 if A + B does not overflow. If time_t is unsigned and if + B's top bit is set, assume that the sum represents A - -B, and + return 1 if the subtraction does not wrap around. */ +static int +time_t_add_ok (time_t a, time_t b) +{ + if (! TYPE_SIGNED (time_t)) + { + time_t sum = a + b; + return (sum < a) == (TIME_T_MIDPOINT <= b); + } + else if (WRAPV) + { + time_t sum = a + b; + return (sum < a) == (b < 0); + } + else + { + time_t avg = time_t_avg (a, b); + return TIME_T_MIN / 2 <= avg && avg <= TIME_T_MAX / 2; + } +} + +/* Return 1 if A + B does not overflow. */ +static int +time_t_int_add_ok (time_t a, int b) +{ + verify (int_no_wider_than_time_t, INT_MAX <= TIME_T_MAX); + if (WRAPV) + { + time_t sum = a + b; + return (sum < a) == (b < 0); + } + else + { + int a_odd = a & 1; + time_t avg = SHR (a, 1) + (SHR (b, 1) + (a_odd & b)); + return TIME_T_MIN / 2 <= avg && avg <= TIME_T_MAX / 2; + } +} + +/* Return a time_t value corresponding to (YEAR-YDAY HOUR:MIN:SEC), + assuming that *T corresponds to *TP and that no clock adjustments + occurred between *TP and the desired time. + If TP is null, return a value not equal to *T; this avoids false matches. + If overflow occurs, yield the minimal or maximal value, except do not + yield a value equal to *T. */ +static time_t +guess_time_tm (long_int year, long_int yday, int hour, int min, int sec, + const time_t *t, const struct tm *tp) +{ + if (tp) + { + time_t d = ydhms_diff (year, yday, hour, min, sec, + tp->tm_year, tp->tm_yday, + tp->tm_hour, tp->tm_min, tp->tm_sec); + if (time_t_add_ok (*t, d)) + return *t + d; + } + + /* Overflow occurred one way or another. Return the nearest result + that is actually in range, except don't report a zero difference + if the actual difference is nonzero, as that would cause a false + match; and don't oscillate between two values, as that would + confuse the spring-forward gap detector. */ + return (*t < TIME_T_MIDPOINT + ? (*t <= TIME_T_MIN + 1 ? *t + 1 : TIME_T_MIN) + : (TIME_T_MAX - 1 <= *t ? *t - 1 : TIME_T_MAX)); +} + +/* 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 = convert (t, tp); + + if (!r && *t) + { + time_t bad = *t; + time_t ok = 0; + + /* 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 = time_t_avg (ok, bad); + r = convert (t, tp); + if (r) + ok = mid; + else + bad = mid; + } + + if (!r && ok) + { + /* The last conversion attempt failed; + revert to the most recent successful attempt. */ + *t = ok; + r = convert (t, 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. + This function is external because it is used also by timegm.c. */ +time_t +__mktime_internal (struct tm *tp, + struct tm *(*convert) (const time_t *, struct tm *), + time_t *offset) +{ + time_t t, gt, 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; + + /* 1 if the previous probe was DST. */ + int dst2; + + /* 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; + long_int lyear_requested = year_requested; + long_int year = lyear_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 mon_yday = ((__mon_yday[leapyear (year)] + [mon_remainder + 12 * negative_mon_remainder]) + - 1); + long_int lmday = mday; + long_int yday = mon_yday + lmday; + + time_t guessed_offset = *offset; + + 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; + } + + /* Invert CONVERT by probing. First assume the same offset as last + time. */ + + t0 = ydhms_diff (year, yday, hour, min, sec, + EPOCH_YEAR - TM_YEAR_BASE, 0, 0, 0, - guessed_offset); + + if (TIME_T_MAX / INT_MAX / 366 / 24 / 60 / 60 < 3) + { + /* time_t isn't large enough to rule out overflows, so check + for major overflows. A gross check suffices, since if t0 + 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. */ + + /* Approximate log base 2 of the number of time units per + biennium. A biennium is 2 years; use this unit instead of + years to avoid integer overflow. For example, 2 average + Gregorian years are 2 * 365.2425 * 24 * 60 * 60 seconds, + which is 63113904 seconds, and rint (log2 (63113904)) is + 26. */ + int ALOG2_SECONDS_PER_BIENNIUM = 26; + int ALOG2_MINUTES_PER_BIENNIUM = 20; + int ALOG2_HOURS_PER_BIENNIUM = 14; + int ALOG2_DAYS_PER_BIENNIUM = 10; + int LOG2_YEARS_PER_BIENNIUM = 1; + + int approx_requested_biennia = + (SHR (year_requested, LOG2_YEARS_PER_BIENNIUM) + - SHR (EPOCH_YEAR - TM_YEAR_BASE, LOG2_YEARS_PER_BIENNIUM) + + SHR (mday, ALOG2_DAYS_PER_BIENNIUM) + + SHR (hour, ALOG2_HOURS_PER_BIENNIUM) + + SHR (min, ALOG2_MINUTES_PER_BIENNIUM) + + (LEAP_SECONDS_POSSIBLE + ? 0 + : SHR (sec, ALOG2_SECONDS_PER_BIENNIUM))); + + int approx_biennia = SHR (t0, ALOG2_SECONDS_PER_BIENNIUM); + int diff = approx_biennia - approx_requested_biennia; + int approx_abs_diff = diff < 0 ? -1 - diff : diff; + + /* IRIX 4.0.5 cc miscalculates TIME_T_MIN / 3: it erroneously + gives a positive value of 715827882. Setting a variable + first then doing math on it seems to work. + (ghazi@caip.rutgers.edu) */ + time_t time_t_max = TIME_T_MAX; + time_t time_t_min = TIME_T_MIN; + time_t overflow_threshold = + (time_t_max / 3 - time_t_min / 3) >> ALOG2_SECONDS_PER_BIENNIUM; + + if (overflow_threshold < approx_abs_diff) + { + /* Overflow occurred. Try repairing it; this might work if + the time zone offset is enough to undo the overflow. */ + time_t repaired_t0 = -1 - t0; + approx_biennia = SHR (repaired_t0, ALOG2_SECONDS_PER_BIENNIUM); + diff = approx_biennia - approx_requested_biennia; + approx_abs_diff = diff < 0 ? -1 - diff : diff; + if (overflow_threshold < approx_abs_diff) + return -1; + guessed_offset += repaired_t0 - t0; + t0 = repaired_t0; + } + } + + /* Repeatedly use the error to improve the guess. */ + + for (t = t1 = t2 = t0, dst2 = 0; + (gt = guess_time_tm (year, yday, hour, min, sec, &t, + ranged_convert (convert, &t, &tm)), + t != gt); + t1 = t2, t2 = t, t = gt, dst2 = tm.tm_isdst != 0) + if (t == t1 && t != t2 + && (tm.tm_isdst < 0 + || (isdst < 0 + ? dst2 <= (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 GT - T. Follow the common + practice in this case, which is to return a time that is GT - T + away from the requested time, preferring a time whose + tm_isdst differs from the requested value. (If no tm_isdst + was requested and only one of the two values has a nonzero + tm_isdst, prefer that value.) In practice, this is more + useful than returning -1. */ + goto offset_found; + else if (--remaining_probes == 0) + return -1; + + /* We have a match. Check whether tm.tm_isdst has the requested + value, if any. */ + if (isdst_differ (isdst, 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 adjacent timestamps in both directions, + looking for the desired isdst. This should work for all real + time zone histories in the tz database. */ + + /* Distance between probes when looking for a DST boundary. In + tzdata2003a, the shortest period of DST is 601200 seconds + (e.g., America/Recife starting 2000-10-08 01:00), and the + shortest period of non-DST surrounded by DST is 694800 + seconds (Africa/Tunis starting 1943-04-17 01:00). Use the + minimum of these two values, so we don't miss these short + periods when probing. */ + int stride = 601200; + + /* The longest period of DST in tzdata2003a is 536454000 seconds + (e.g., America/Jujuy starting 1946-10-01 01:00). The longest + period of non-DST is much longer, but it makes no real sense + to search for more than a year of non-DST, so use the DST + max. */ + int duration_max = 536454000; + + /* Search in both directions, so the maximum distance is half + the duration; add the stride to avoid off-by-1 problems. */ + int delta_bound = duration_max / 2 + stride; + + int delta, direction; + + for (delta = stride; delta < delta_bound; delta += stride) + for (direction = -1; direction <= 1; direction += 2) + if (time_t_int_add_ok (t, delta * direction)) + { + time_t ot = t + delta * direction; + struct tm otm; + ranged_convert (convert, &ot, &otm); + if (! isdst_differ (isdst, otm.tm_isdst)) + { + /* We found the desired tm_isdst. + Extrapolate back to the desired time. */ + t = guess_time_tm (year, yday, hour, min, sec, &ot, &otm); + ranged_convert (convert, &t, &tm); + goto offset_found; + } + } + } + + offset_found: + *offset = guessed_offset + t - t0; + + if (LEAP_SECONDS_POSSIBLE && 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. */ + int sec_adjustment = (sec == 0 && tm.tm_sec == 60) - sec; + if (! time_t_int_add_ok (t, sec_requested)) + return -1; + t1 = t + sec_requested; + if (! time_t_int_add_ok (t1, sec_adjustment)) + return -1; + t2 = t1 + sec_adjustment; + if (! convert (&t2, &tm)) + return -1; + t = t2; + } + + *tp = tm; + return t; +} + + +/* FIXME: This should use a signed type wide enough to hold any UTC + offset in seconds. 'int' should be good enough for GNU code. We + can't fix this unilaterally though, as other modules invoke + __mktime_internal. */ +static time_t localtime_offset; + +/* Convert *TP to a time_t value. */ +time_t +mktime (struct tm *tp) +{ +#ifdef _LIBC + /* 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 (); +#endif + + return __mktime_internal (tp, __localtime_r, &localtime_offset); +} + +#ifdef weak_alias +weak_alias (mktime, timelocal) +#endif + +#ifdef _LIBC +libc_hidden_def (mktime) +libc_hidden_weak (timelocal) +#endif + +#if defined DEBUG_MKTIME && DEBUG_MKTIME + +static int +not_equal_tm (const struct tm *a, const struct tm *b) +{ + return ((a->tm_sec ^ b->tm_sec) + | (a->tm_min ^ b->tm_min) + | (a->tm_hour ^ b->tm_hour) + | (a->tm_mday ^ b->tm_mday) + | (a->tm_mon ^ b->tm_mon) + | (a->tm_year ^ b->tm_year) + | (a->tm_yday ^ b->tm_yday) + | isdst_differ (a->tm_isdst, b->tm_isdst)); +} + +static void +print_tm (const struct tm *tp) +{ + if (tp) + printf ("%04d-%02d-%02d %02d:%02d:%02d yday %03d wday %d isdst %d", + tp->tm_year + TM_YEAR_BASE, tp->tm_mon + 1, tp->tm_mday, + tp->tm_hour, tp->tm_min, tp->tm_sec, + tp->tm_yday, tp->tm_wday, tp->tm_isdst); + else + printf ("0"); +} + +static int +check_result (time_t tk, struct tm tmk, time_t tl, const struct tm *lt) +{ + if (tk != tl || !lt || not_equal_tm (&tmk, lt)) + { + printf ("mktime ("); + print_tm (lt); + printf (")\nyields ("); + print_tm (&tmk); + printf (") == %ld, should be %ld\n", (long int) tk, (long int) tl); + return 1; + } + + return 0; +} + +int +main (int argc, char **argv) +{ + int status = 0; + struct tm tm, tmk, tml; + struct tm *lt; + time_t tk, tl, tl1; + char trailer; + + if ((argc == 3 || argc == 4) + && (sscanf (argv[1], "%d-%d-%d%c", + &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &trailer) + == 3) + && (sscanf (argv[2], "%d:%d:%d%c", + &tm.tm_hour, &tm.tm_min, &tm.tm_sec, &trailer) + == 3)) + { + tm.tm_year -= TM_YEAR_BASE; + tm.tm_mon--; + tm.tm_isdst = argc == 3 ? -1 : atoi (argv[3]); + tmk = tm; + tl = mktime (&tmk); + lt = localtime (&tl); + if (lt) + { + tml = *lt; + lt = &tml; + } + printf ("mktime returns %ld == ", (long int) tl); + print_tm (&tmk); + printf ("\n"); + status = check_result (tl, tmk, tl, lt); + } + else if (argc == 4 || (argc == 5 && strcmp (argv[4], "-") == 0)) + { + time_t from = atol (argv[1]); + time_t by = atol (argv[2]); + time_t to = atol (argv[3]); + + if (argc == 4) + for (tl = from; by < 0 ? to <= tl : tl <= to; tl = tl1) + { + lt = localtime (&tl); + if (lt) + { + tmk = tml = *lt; + tk = mktime (&tmk); + status |= check_result (tk, tmk, tl, &tml); + } + else + { + printf ("localtime (%ld) yields 0\n", (long int) tl); + status = 1; + } + tl1 = tl + by; + if ((tl1 < tl) != (by < 0)) + break; + } + else + for (tl = from; by < 0 ? to <= tl : tl <= to; tl = tl1) + { + /* Null benchmark. */ + lt = localtime (&tl); + if (lt) + { + tmk = tml = *lt; + tk = tl; + status |= check_result (tk, tmk, tl, &tml); + } + else + { + printf ("localtime (%ld) yields 0\n", (long int) tl); + status = 1; + } + tl1 = tl + by; + if ((tl1 < tl) != (by < 0)) + break; + } + } + else + printf ("Usage:\ +\t%s YYYY-MM-DD HH:MM:SS [ISDST] # Test given time.\n\ +\t%s FROM BY TO # Test values FROM, FROM+BY, ..., TO.\n\ +\t%s FROM BY TO - # Do not test those values (for benchmark).\n", + argv[0], argv[0], argv[0]); + + return status; +} + +#endif /* DEBUG_MKTIME */ + +/* +Local Variables: +compile-command: "gcc -DDEBUG_MKTIME -I. -Wall -W -O2 -g mktime.c -o mktime" +End: +*/ diff --git a/REORG.TODO/time/offtime.c b/REORG.TODO/time/offtime.c new file mode 100644 index 0000000000..75a28fed84 --- /dev/null +++ b/REORG.TODO/time/offtime.c @@ -0,0 +1,86 @@ +/* Copyright (C) 1991-2017 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 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, see + <http://www.gnu.org/licenses/>. */ + +#include <errno.h> +#include <time.h> + +#define SECS_PER_HOUR (60 * 60) +#define SECS_PER_DAY (SECS_PER_HOUR * 24) + +/* Compute the `struct tm' representation of *T, + offset OFFSET seconds east of UTC, + and store year, yday, mon, mday, wday, hour, min, sec into *TP. + Return nonzero if successful. */ +int +__offtime (const time_t *t, long int offset, struct tm *tp) +{ + time_t days, rem, y; + const unsigned short int *ip; + + 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; + } + tp->tm_hour = rem / SECS_PER_HOUR; + rem %= SECS_PER_HOUR; + tp->tm_min = rem / 60; + tp->tm_sec = rem % 60; + /* January 1, 1970 was a Thursday. */ + tp->tm_wday = (4 + days) % 7; + if (tp->tm_wday < 0) + tp->tm_wday += 7; + y = 1970; + +#define DIV(a, b) ((a) / (b) - ((a) % (b) < 0)) +#define LEAPS_THRU_END_OF(y) (DIV (y, 4) - DIV (y, 100) + DIV (y, 400)) + + while (days < 0 || days >= (__isleap (y) ? 366 : 365)) + { + /* Guess a corrected year, assuming 365 days per year. */ + time_t yg = y + days / 365 - (days % 365 < 0); + + /* Adjust DAYS and Y to match the guessed year. */ + days -= ((yg - y) * 365 + + LEAPS_THRU_END_OF (yg - 1) + - LEAPS_THRU_END_OF (y - 1)); + y = yg; + } + tp->tm_year = y - 1900; + if (tp->tm_year != y - 1900) + { + /* The year cannot be represented due to overflow. */ + __set_errno (EOVERFLOW); + return 0; + } + tp->tm_yday = days; + ip = __mon_yday[__isleap(y)]; + for (y = 11; days < (long int) ip[y]; --y) + continue; + days -= ip[y]; + tp->tm_mon = y; + tp->tm_mday = days + 1; + return 1; +} diff --git a/REORG.TODO/time/setitimer.c b/REORG.TODO/time/setitimer.c new file mode 100644 index 0000000000..65d85a79af --- /dev/null +++ b/REORG.TODO/time/setitimer.c @@ -0,0 +1,40 @@ +/* Copyright (C) 1991-2017 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 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, see + <http://www.gnu.org/licenses/>. */ + +#include <stddef.h> +#include <errno.h> +#include <sys/time.h> + +/* Set the timer WHICH to *NEW. If OLD is not NULL, + set *OLD to the old value of timer WHICH. + Returns 0 on success, -1 on errors. */ +int +__setitimer (enum __itimer_which which, const struct itimerval *new, + struct itimerval *old) +{ + if (new == NULL) + { + __set_errno (EINVAL); + return -1; + } + + __set_errno (ENOSYS); + return -1; +} +stub_warning (setitimer) + +weak_alias (__setitimer, setitimer) diff --git a/REORG.TODO/time/settimeofday.c b/REORG.TODO/time/settimeofday.c new file mode 100644 index 0000000000..317c93d35f --- /dev/null +++ b/REORG.TODO/time/settimeofday.c @@ -0,0 +1,31 @@ +/* Copyright (C) 1991-2017 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 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, see + <http://www.gnu.org/licenses/>. */ + +#include <errno.h> +#include <sys/time.h> + +/* Set the current time of day and timezone information. + This call is restricted to the super-user. */ +int +__settimeofday (const struct timeval *tv, const struct timezone *tz) +{ + __set_errno (ENOSYS); + return -1; +} +stub_warning (settimeofday) + +weak_alias (__settimeofday, settimeofday) diff --git a/REORG.TODO/time/stime.c b/REORG.TODO/time/stime.c new file mode 100644 index 0000000000..723eedacf1 --- /dev/null +++ b/REORG.TODO/time/stime.c @@ -0,0 +1,37 @@ +/* Copyright (C) 1992-2017 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 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, see + <http://www.gnu.org/licenses/>. */ + +#include <errno.h> +#include <time.h> +#include <stddef.h> + +/* Set the system clock to *WHEN. */ + +int +stime (const time_t *when) +{ + if (when == NULL) + { + __set_errno (EINVAL); + return -1; + } + + __set_errno (ENOSYS); + return -1; +} + +stub_warning (stime) diff --git a/REORG.TODO/time/strftime.c b/REORG.TODO/time/strftime.c new file mode 100644 index 0000000000..eeab20e2b3 --- /dev/null +++ b/REORG.TODO/time/strftime.c @@ -0,0 +1,27 @@ +/* Copyright (C) 1991-2017 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 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, see + <http://www.gnu.org/licenses/>. */ + +#include <time.h> +#include <locale/localeinfo.h> + + +size_t +strftime (char *s, size_t maxsize, const char *format, const struct tm *tp) +{ + return __strftime_l (s, maxsize, format, tp, _NL_CURRENT_LOCALE); +} +libc_hidden_def (strftime) diff --git a/REORG.TODO/time/strftime_l.c b/REORG.TODO/time/strftime_l.c new file mode 100644 index 0000000000..eb3efb8129 --- /dev/null +++ b/REORG.TODO/time/strftime_l.c @@ -0,0 +1,1425 @@ +/* Copyright (C) 2002-2017 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 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, see + <http://www.gnu.org/licenses/>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#ifdef _LIBC +# define USE_IN_EXTENDED_LOCALE_MODEL 1 +# define HAVE_LIMITS_H 1 +# define HAVE_MBLEN 1 +# define HAVE_MBRLEN 1 +# define HAVE_STRUCT_ERA_ENTRY 1 +# define HAVE_TM_GMTOFF 1 +# define HAVE_TM_ZONE 1 +# define HAVE_TZNAME 1 +# define HAVE_TZSET 1 +# define HAVE_STRFTIME 0 +# define MULTIBYTE_IS_FORMAT_SAFE 1 +# define STDC_HEADERS 1 +# include "../locale/localeinfo.h" +#endif + +#if defined emacs && !defined HAVE_BCOPY +# define HAVE_MEMCPY 1 +#endif + +#include <ctype.h> +#include <sys/types.h> /* Some systems define `time_t' here. */ + +#ifdef TIME_WITH_SYS_TIME +# include <sys/time.h> +# include <time.h> +#else +# ifdef HAVE_SYS_TIME_H +# include <sys/time.h> +# else +# include <time.h> +# endif +#endif +#if HAVE_TZNAME +extern char *tzname[]; +#endif + +/* Do multibyte processing if multibytes are supported, unless + multibyte sequences are safe in formats. Multibyte sequences are + safe if they cannot contain byte sequences that look like format + conversion specifications. The GNU C Library uses UTF8 multibyte + encoding, which is safe for formats, but strftime.c can be used + with other C libraries that use unsafe encodings. */ +#define DO_MULTIBYTE (HAVE_MBLEN && ! MULTIBYTE_IS_FORMAT_SAFE) + +#if DO_MULTIBYTE +# if HAVE_MBRLEN +# include <wchar.h> +# else + /* Simulate mbrlen with mblen as best we can. */ +# define mbstate_t int +# define mbrlen(s, n, ps) mblen (s, n) +# define mbsinit(ps) (*(ps) == 0) +# endif + static const mbstate_t mbstate_zero; +#endif + +#if HAVE_LIMITS_H +# include <limits.h> +#endif + +#if STDC_HEADERS +# include <stddef.h> +# include <stdlib.h> +# include <string.h> +# include <stdbool.h> +#else +# ifndef HAVE_MEMCPY +# define memcpy(d, s, n) bcopy ((s), (d), (n)) +# endif +#endif + +#ifdef COMPILE_WIDE +# include <endian.h> +# define CHAR_T wchar_t +# define UCHAR_T unsigned int +# define L_(Str) L##Str +# define NLW(Sym) _NL_W##Sym + +# define MEMCPY(d, s, n) __wmemcpy (d, s, n) +# define STRLEN(s) __wcslen (s) + +#else +# define CHAR_T char +# define UCHAR_T unsigned char +# define L_(Str) Str +# define NLW(Sym) Sym + +# if !defined STDC_HEADERS && !defined HAVE_MEMCPY +# define MEMCPY(d, s, n) bcopy ((s), (d), (n)) +# else +# define MEMCPY(d, s, n) memcpy ((d), (s), (n)) +# endif +# define STRLEN(s) strlen (s) + +# ifdef _LIBC +# define MEMPCPY(d, s, n) __mempcpy (d, s, n) +# else +# ifndef HAVE_MEMPCPY +# define MEMPCPY(d, s, n) ((void *) ((char *) memcpy (d, s, n) + (n))) +# endif +# endif +#endif + +#ifndef PTR +# define PTR void * +#endif + +#ifndef CHAR_BIT +# define CHAR_BIT 8 +#endif + +#ifndef NULL +# define NULL 0 +#endif + +#define TYPE_SIGNED(t) ((t) -1 < 0) + +/* Bound on length of the string representing an integer value of type t. + Subtract one for the sign bit if t is signed; + 302 / 1000 is log10 (2) rounded up; + add one for integer division truncation; + add one more for a minus sign if t is signed. */ +#define INT_STRLEN_BOUND(t) \ + ((sizeof (t) * CHAR_BIT - TYPE_SIGNED (t)) * 302 / 1000 + 1 + TYPE_SIGNED (t)) + +#define TM_YEAR_BASE 1900 + +#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 + + +#ifdef _LIBC +# define tzname __tzname +# define tzset __tzset +#endif + +#if !HAVE_TM_GMTOFF +/* Portable standalone applications should supply a "time_r.h" that + declares a POSIX-compliant localtime_r, for the benefit of older + implementations that lack localtime_r or have a nonstandard one. + Similarly for gmtime_r. See the gnulib time_r module for one way + to implement this. */ +# include "time_r.h" +# undef __gmtime_r +# undef __localtime_r +# define __gmtime_r gmtime_r +# define __localtime_r localtime_r +#endif + + +#if !defined memset && !defined HAVE_MEMSET && !defined _LIBC +/* Some systems lack the `memset' function and we don't want to + introduce additional dependencies. */ +/* The SGI compiler reportedly barfs on the trailing null + if we use a string constant as the initializer. 28 June 1997, rms. */ +static const CHAR_T spaces[16] = /* " " */ +{ + L_(' '),L_(' '),L_(' '),L_(' '),L_(' '),L_(' '),L_(' '),L_(' '), + L_(' '),L_(' '),L_(' '),L_(' '),L_(' '),L_(' '),L_(' '),L_(' ') +}; +static const CHAR_T zeroes[16] = /* "0000000000000000" */ +{ + L_('0'),L_('0'),L_('0'),L_('0'),L_('0'),L_('0'),L_('0'),L_('0'), + L_('0'),L_('0'),L_('0'),L_('0'),L_('0'),L_('0'),L_('0'),L_('0') +}; + +# define memset_space(P, Len) \ + do { \ + int _len = (Len); \ + \ + do \ + { \ + int _this = _len > 16 ? 16 : _len; \ + (P) = MEMPCPY ((P), spaces, _this * sizeof (CHAR_T)); \ + _len -= _this; \ + } \ + while (_len > 0); \ + } while (0) + +# define memset_zero(P, Len) \ + do { \ + int _len = (Len); \ + \ + do \ + { \ + int _this = _len > 16 ? 16 : _len; \ + (P) = MEMPCPY ((P), zeroes, _this * sizeof (CHAR_T)); \ + _len -= _this; \ + } \ + while (_len > 0); \ + } while (0) +#else +# ifdef COMPILE_WIDE +# define memset_space(P, Len) (wmemset ((P), L' ', (Len)), (P) += (Len)) +# define memset_zero(P, Len) (wmemset ((P), L'0', (Len)), (P) += (Len)) +# else +# define memset_space(P, Len) (memset ((P), ' ', (Len)), (P) += (Len)) +# define memset_zero(P, Len) (memset ((P), '0', (Len)), (P) += (Len)) +# endif +#endif + +#define add(n, f) \ + do \ + { \ + int _n = (n); \ + int _delta = width - _n; \ + int _incr = _n + (_delta > 0 ? _delta : 0); \ + if ((size_t) _incr >= maxsize - i) \ + return 0; \ + if (p) \ + { \ + if (_delta > 0) \ + { \ + if (pad == L_('0')) \ + memset_zero (p, _delta); \ + else \ + memset_space (p, _delta); \ + } \ + f; \ + p += _n; \ + } \ + i += _incr; \ + } while (0) + +#define cpy(n, s) \ + add ((n), \ + if (to_lowcase) \ + memcpy_lowcase (p, (s), _n LOCALE_ARG); \ + else if (to_uppcase) \ + memcpy_uppcase (p, (s), _n LOCALE_ARG); \ + else \ + MEMCPY ((PTR) p, (const PTR) (s), _n)) + +#ifdef COMPILE_WIDE +# ifndef USE_IN_EXTENDED_LOCALE_MODEL +# undef __mbsrtowcs_l +# define __mbsrtowcs_l(d, s, l, st, loc) __mbsrtowcs (d, s, l, st) +# endif +# define widen(os, ws, l) \ + { \ + mbstate_t __st; \ + const char *__s = os; \ + memset (&__st, '\0', sizeof (__st)); \ + l = __mbsrtowcs_l (NULL, &__s, 0, &__st, loc); \ + ws = alloca ((l + 1) * sizeof (wchar_t)); \ + (void) __mbsrtowcs_l (ws, &__s, l, &__st, loc); \ + } +#endif + + +#if defined _LIBC && defined USE_IN_EXTENDED_LOCALE_MODEL +/* We use this code also for the extended locale handling where the + function gets as an additional argument the locale which has to be + used. To access the values we have to redefine the _NL_CURRENT + macro. */ +# define strftime __strftime_l +# define wcsftime __wcsftime_l +# undef _NL_CURRENT +# define _NL_CURRENT(category, item) \ + (current->values[_NL_ITEM_INDEX (item)].string) +# define LOCALE_PARAM , __locale_t loc +# define LOCALE_ARG , loc +# define HELPER_LOCALE_ARG , current +#else +# define LOCALE_PARAM +# define LOCALE_ARG +# ifdef _LIBC +# define HELPER_LOCALE_ARG , _NL_CURRENT_DATA (LC_TIME) +# else +# define HELPER_LOCALE_ARG +# endif +#endif + +#ifdef COMPILE_WIDE +# ifdef USE_IN_EXTENDED_LOCALE_MODEL +# define TOUPPER(Ch, L) __towupper_l (Ch, L) +# define TOLOWER(Ch, L) __towlower_l (Ch, L) +# else +# define TOUPPER(Ch, L) towupper (Ch) +# define TOLOWER(Ch, L) towlower (Ch) +# endif +#else +# ifdef _LIBC +# ifdef USE_IN_EXTENDED_LOCALE_MODEL +# define TOUPPER(Ch, L) __toupper_l (Ch, L) +# define TOLOWER(Ch, L) __tolower_l (Ch, L) +# else +# define TOUPPER(Ch, L) toupper (Ch) +# define TOLOWER(Ch, L) tolower (Ch) +# endif +# else +# define TOUPPER(Ch, L) (islower (Ch) ? toupper (Ch) : (Ch)) +# define TOLOWER(Ch, L) (isupper (Ch) ? tolower (Ch) : (Ch)) +# endif +#endif +/* We don't use `isdigit' here since the locale dependent + interpretation is not what we want here. We only need to accept + the arabic digits in the ASCII range. One day there is perhaps a + more reliable way to accept other sets of digits. */ +#define ISDIGIT(Ch) ((unsigned int) (Ch) - L_('0') <= 9) + +static CHAR_T *memcpy_lowcase (CHAR_T *dest, const CHAR_T *src, + size_t len LOCALE_PARAM) __THROW; + +static CHAR_T * +memcpy_lowcase (CHAR_T *dest, const CHAR_T *src, size_t len LOCALE_PARAM) +{ + while (len-- > 0) + dest[len] = TOLOWER ((UCHAR_T) src[len], loc); + return dest; +} + +static CHAR_T *memcpy_uppcase (CHAR_T *dest, const CHAR_T *src, + size_t len LOCALE_PARAM) __THROW; + +static CHAR_T * +memcpy_uppcase (CHAR_T *dest, const CHAR_T *src, size_t len LOCALE_PARAM) +{ + while (len-- > 0) + dest[len] = TOUPPER ((UCHAR_T) src[len], loc); + return dest; +} + + +#if ! HAVE_TM_GMTOFF +/* Yield the difference between *A and *B, + measured in seconds, ignoring leap seconds. */ +# define tm_diff ftime_tm_diff +static int tm_diff (const struct tm *, const struct tm *) __THROW; +static int +tm_diff (const struct tm *a, const struct tm *b) +{ + /* Compute intervening leap days correctly even if year is negative. + Take care to avoid int overflow in leap day calculations, + but it's OK to assume that A and B are close to each other. */ + int a4 = (a->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (a->tm_year & 3); + int b4 = (b->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (b->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); + int years = a->tm_year - b->tm_year; + int days = (365 * years + intervening_leap_days + + (a->tm_yday - b->tm_yday)); + return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour)) + + (a->tm_min - b->tm_min)) + + (a->tm_sec - b->tm_sec)); +} +#endif /* ! HAVE_TM_GMTOFF */ + + + +/* The number of days from the first day of the first ISO week of this + year to the year day YDAY with week day WDAY. ISO weeks start on + Monday; the first ISO week has the year's first Thursday. YDAY may + be as small as YDAY_MINIMUM. */ +#define ISO_WEEK_START_WDAY 1 /* Monday */ +#define ISO_WEEK1_WDAY 4 /* Thursday */ +#define YDAY_MINIMUM (-366) +static int iso_week_days (int, int) __THROW; +#ifdef __GNUC__ +__inline__ +#endif +static int +iso_week_days (int yday, int wday) +{ + /* Add enough to the first operand of % to make it nonnegative. */ + int big_enough_multiple_of_7 = (-YDAY_MINIMUM / 7 + 2) * 7; + return (yday + - (yday - wday + ISO_WEEK1_WDAY + big_enough_multiple_of_7) % 7 + + ISO_WEEK1_WDAY - ISO_WEEK_START_WDAY); +} + + +#if !(defined _NL_CURRENT || HAVE_STRFTIME) +static CHAR_T const weekday_name[][10] = + { + L_("Sunday"), L_("Monday"), L_("Tuesday"), L_("Wednesday"), + L_("Thursday"), L_("Friday"), L_("Saturday") + }; +static CHAR_T const month_name[][10] = + { + L_("January"), L_("February"), L_("March"), L_("April"), L_("May"), + L_("June"), L_("July"), L_("August"), L_("September"), L_("October"), + L_("November"), L_("December") + }; +#endif + + +#ifdef emacs +# define my_strftime emacs_strftimeu +# define ut_argument , ut +# define ut_argument_spec , int ut +#else +# ifdef COMPILE_WIDE +# define my_strftime wcsftime +# define nl_get_alt_digit _nl_get_walt_digit +# else +# define my_strftime strftime +# define nl_get_alt_digit _nl_get_alt_digit +# endif +# define ut_argument +# define ut_argument_spec +/* We don't have this information in general. */ +# define ut 0 +#endif + +static size_t __strftime_internal (CHAR_T *, size_t, const CHAR_T *, + const struct tm *, bool * + ut_argument_spec + LOCALE_PARAM) __THROW; + +/* 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 +my_strftime (CHAR_T *s, size_t maxsize, const CHAR_T *format, + const struct tm *tp ut_argument_spec LOCALE_PARAM) +{ +#if !defined _LIBC && HAVE_TZNAME && HAVE_TZSET + /* Solaris 2.5 tzset sometimes modifies the storage returned by localtime. + Work around this bug by copying *tp before it might be munged. */ + struct tm tmcopy; + tmcopy = *tp; + tp = &tmcopy; +#endif + bool tzset_called = false; + return __strftime_internal (s, maxsize, format, tp, &tzset_called + ut_argument LOCALE_ARG); +} +#ifdef _LIBC +libc_hidden_def (my_strftime) +#endif + +static size_t +__strftime_internal (CHAR_T *s, size_t maxsize, const CHAR_T *format, + const struct tm *tp, bool *tzset_called + ut_argument_spec LOCALE_PARAM) +{ +#if defined _LIBC && defined USE_IN_EXTENDED_LOCALE_MODEL + struct __locale_data *const current = loc->__locales[LC_TIME]; +#endif + + int hour12 = tp->tm_hour; +#ifdef _NL_CURRENT + /* We cannot make the following values variables since we must delay + the evaluation of these values until really needed since some + expressions might not be valid in every situation. The `struct tm' + might be generated by a strptime() call that initialized + only a few elements. Dereference the pointers only if the format + requires this. Then it is ok to fail if the pointers are invalid. */ +# define a_wkday \ + ((const CHAR_T *) (tp->tm_wday < 0 || tp->tm_wday > 6 \ + ? "?" : _NL_CURRENT (LC_TIME, NLW(ABDAY_1) + tp->tm_wday))) +# define f_wkday \ + ((const CHAR_T *) (tp->tm_wday < 0 || tp->tm_wday > 6 \ + ? "?" : _NL_CURRENT (LC_TIME, NLW(DAY_1) + tp->tm_wday))) +# define a_month \ + ((const CHAR_T *) (tp->tm_mon < 0 || tp->tm_mon > 11 \ + ? "?" : _NL_CURRENT (LC_TIME, NLW(ABMON_1) + tp->tm_mon))) +# define f_month \ + ((const CHAR_T *) (tp->tm_mon < 0 || tp->tm_mon > 11 \ + ? "?" : _NL_CURRENT (LC_TIME, NLW(MON_1) + tp->tm_mon))) +# define ampm \ + ((const CHAR_T *) _NL_CURRENT (LC_TIME, tp->tm_hour > 11 \ + ? NLW(PM_STR) : NLW(AM_STR))) + +# define aw_len STRLEN (a_wkday) +# define am_len STRLEN (a_month) +# define ap_len STRLEN (ampm) +#else +# if !HAVE_STRFTIME +# define f_wkday (tp->tm_wday < 0 || tp->tm_wday > 6 \ + ? "?" : weekday_name[tp->tm_wday]) +# define f_month (tp->tm_mon < 0 || tp->tm_mon > 11 \ + ? "?" : month_name[tp->tm_mon]) +# define a_wkday f_wkday +# define a_month f_month +# define ampm (L_("AMPM") + 2 * (tp->tm_hour > 11)) + + size_t aw_len = 3; + size_t am_len = 3; + size_t ap_len = 2; +# endif +#endif + const char *zone; + size_t i = 0; + CHAR_T *p = s; + const CHAR_T *f; +#if DO_MULTIBYTE && !defined COMPILE_WIDE + const char *format_end = NULL; +#endif + + zone = NULL; +#if HAVE_TM_ZONE + /* 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; +#endif +#if HAVE_TZNAME + if (ut) + { + if (! (zone && *zone)) + zone = "GMT"; + } +#endif + + if (hour12 > 12) + hour12 -= 12; + else + if (hour12 == 0) + hour12 = 12; + + for (f = format; *f != '\0'; ++f) + { + int pad = 0; /* Padding for number ('-', '_', or 0). */ + int modifier; /* Field modifier ('E', 'O', or 0). */ + int digits; /* Max digits for numeric format. */ + int number_value; /* Numeric value to be printed. */ + int negative_number; /* 1 if the number is negative. */ + const CHAR_T *subfmt; + CHAR_T *bufp; + CHAR_T buf[1 + (sizeof (int) < sizeof (time_t) + ? INT_STRLEN_BOUND (time_t) + : INT_STRLEN_BOUND (int))]; + int width = -1; + int to_lowcase = 0; + int to_uppcase = 0; + int change_case = 0; + int format_char; + +#if DO_MULTIBYTE && !defined COMPILE_WIDE + switch (*f) + { + case L_('%'): + break; + + case L_('\b'): case L_('\t'): case L_('\n'): + case L_('\v'): case L_('\f'): case L_('\r'): + case L_(' '): case L_('!'): case L_('"'): case L_('#'): case L_('&'): + case L_('\''): case L_('('): case L_(')'): case L_('*'): case L_('+'): + case L_(','): case L_('-'): case L_('.'): case L_('/'): case L_('0'): + case L_('1'): case L_('2'): case L_('3'): case L_('4'): case L_('5'): + case L_('6'): case L_('7'): case L_('8'): case L_('9'): case L_(':'): + case L_(';'): case L_('<'): case L_('='): case L_('>'): case L_('?'): + case L_('A'): case L_('B'): case L_('C'): case L_('D'): case L_('E'): + case L_('F'): case L_('G'): case L_('H'): case L_('I'): case L_('J'): + case L_('K'): case L_('L'): case L_('M'): case L_('N'): case L_('O'): + case L_('P'): case L_('Q'): case L_('R'): case L_('S'): case L_('T'): + case L_('U'): case L_('V'): case L_('W'): case L_('X'): case L_('Y'): + case L_('Z'): case L_('['): case L_('\\'): case L_(']'): case L_('^'): + case L_('_'): case L_('a'): case L_('b'): case L_('c'): case L_('d'): + case L_('e'): case L_('f'): case L_('g'): case L_('h'): case L_('i'): + case L_('j'): case L_('k'): case L_('l'): case L_('m'): case L_('n'): + case L_('o'): case L_('p'): case L_('q'): case L_('r'): case L_('s'): + case L_('t'): case L_('u'): case L_('v'): case L_('w'): case L_('x'): + case L_('y'): case L_('z'): case L_('{'): case L_('|'): case L_('}'): + case L_('~'): + /* The C Standard requires these 98 characters (plus '%') to + be in the basic execution character set. None of these + characters can start a multibyte sequence, so they need + not be analyzed further. */ + add (1, *p = *f); + continue; + + default: + /* Copy this multibyte sequence until we reach its end, find + an error, or come back to the initial shift state. */ + { + mbstate_t mbstate = mbstate_zero; + size_t len = 0; + size_t fsize; + + if (! format_end) + format_end = f + strlen (f) + 1; + fsize = format_end - f; + + do + { + size_t bytes = mbrlen (f + len, fsize - len, &mbstate); + + if (bytes == 0) + break; + + if (bytes == (size_t) -2) + { + len += strlen (f + len); + break; + } + + if (bytes == (size_t) -1) + { + len++; + break; + } + + len += bytes; + } + while (! mbsinit (&mbstate)); + + cpy (len, f); + f += len - 1; + continue; + } + } + +#else /* ! DO_MULTIBYTE */ + + /* Either multibyte encodings are not supported, they are + safe for formats, so any non-'%' byte can be copied through, + or this is the wide character version. */ + if (*f != L_('%')) + { + add (1, *p = *f); + continue; + } + +#endif /* ! DO_MULTIBYTE */ + + /* Check for flags that can modify a format. */ + while (1) + { + switch (*++f) + { + /* This influences the number formats. */ + case L_('_'): + case L_('-'): + case L_('0'): + pad = *f; + continue; + + /* This changes textual output. */ + case L_('^'): + to_uppcase = 1; + continue; + case L_('#'): + change_case = 1; + continue; + + default: + break; + } + break; + } + + /* As a GNU extension we allow to specify the field width. */ + if (ISDIGIT (*f)) + { + width = 0; + do + { + if (width > INT_MAX / 10 + || (width == INT_MAX / 10 && *f - L_('0') > INT_MAX % 10)) + /* Avoid overflow. */ + width = INT_MAX; + else + { + width *= 10; + width += *f - L_('0'); + } + ++f; + } + while (ISDIGIT (*f)); + } + + /* Check for modifiers. */ + switch (*f) + { + case L_('E'): + case L_('O'): + modifier = *f++; + break; + + default: + modifier = 0; + break; + } + + /* Now do the specified format. */ + format_char = *f; + switch (format_char) + { +#define DO_NUMBER(d, v) \ + digits = d > width ? d : width; \ + number_value = v; goto do_number +#define DO_NUMBER_SPACEPAD(d, v) \ + digits = d > width ? d : width; \ + number_value = v; goto do_number_spacepad + + case L_('%'): + if (modifier != 0) + goto bad_format; + add (1, *p = *f); + break; + + case L_('a'): + if (modifier != 0) + goto bad_format; + if (change_case) + { + to_uppcase = 1; + to_lowcase = 0; + } +#if defined _NL_CURRENT || !HAVE_STRFTIME + cpy (aw_len, a_wkday); + break; +#else + goto underlying_strftime; +#endif + + case 'A': + if (modifier != 0) + goto bad_format; + if (change_case) + { + to_uppcase = 1; + to_lowcase = 0; + } +#if defined _NL_CURRENT || !HAVE_STRFTIME + cpy (STRLEN (f_wkday), f_wkday); + break; +#else + goto underlying_strftime; +#endif + + case L_('b'): + case L_('h'): + if (change_case) + { + to_uppcase = 1; + to_lowcase = 0; + } + if (modifier != 0) + goto bad_format; +#if defined _NL_CURRENT || !HAVE_STRFTIME + cpy (am_len, a_month); + break; +#else + goto underlying_strftime; +#endif + + case L_('B'): + if (modifier != 0) + goto bad_format; + if (change_case) + { + to_uppcase = 1; + to_lowcase = 0; + } +#if defined _NL_CURRENT || !HAVE_STRFTIME + cpy (STRLEN (f_month), f_month); + break; +#else + goto underlying_strftime; +#endif + + case L_('c'): + if (modifier == L_('O')) + goto bad_format; +#ifdef _NL_CURRENT + if (! (modifier == 'E' + && (*(subfmt = + (const CHAR_T *) _NL_CURRENT (LC_TIME, + NLW(ERA_D_T_FMT))) + != '\0'))) + subfmt = (const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(D_T_FMT)); +#else +# if HAVE_STRFTIME + goto underlying_strftime; +# else + subfmt = L_("%a %b %e %H:%M:%S %Y"); +# endif +#endif + + subformat: + { + CHAR_T *old_start = p; + size_t len = __strftime_internal (NULL, (size_t) -1, subfmt, + tp, tzset_called ut_argument + LOCALE_ARG); + add (len, __strftime_internal (p, maxsize - i, subfmt, + tp, tzset_called ut_argument + LOCALE_ARG)); + + if (to_uppcase) + while (old_start < p) + { + *old_start = TOUPPER ((UCHAR_T) *old_start, loc); + ++old_start; + } + } + break; + +#if HAVE_STRFTIME && ! (defined _NL_CURRENT && HAVE_STRUCT_ERA_ENTRY) + underlying_strftime: + { + /* The relevant information is available only via the + underlying strftime implementation, so use that. */ + char ufmt[4]; + char *u = ufmt; + char ubuf[1024]; /* enough for any single format in practice */ + size_t len; + /* Make sure we're calling the actual underlying strftime. + In some cases, config.h contains something like + "#define strftime rpl_strftime". */ +# ifdef strftime +# undef strftime + size_t strftime (); +# endif + + *u++ = '%'; + if (modifier != 0) + *u++ = modifier; + *u++ = format_char; + *u = '\0'; + len = strftime (ubuf, sizeof ubuf, ufmt, tp); + if (len == 0 && ubuf[0] != '\0') + return 0; + cpy (len, ubuf); + } + break; +#endif + + case L_('C'): + if (modifier == L_('E')) + { +#if HAVE_STRUCT_ERA_ENTRY + struct era_entry *era = _nl_get_era_entry (tp HELPER_LOCALE_ARG); + if (era) + { +# ifdef COMPILE_WIDE + size_t len = __wcslen (era->era_wname); + cpy (len, era->era_wname); +# else + size_t len = strlen (era->era_name); + cpy (len, era->era_name); +# endif + break; + } +#else +# if HAVE_STRFTIME + goto underlying_strftime; +# endif +#endif + } + + { + int year = tp->tm_year + TM_YEAR_BASE; + DO_NUMBER (1, year / 100 - (year % 100 < 0)); + } + + case L_('x'): + if (modifier == L_('O')) + goto bad_format; +#ifdef _NL_CURRENT + if (! (modifier == L_('E') + && (*(subfmt = + (const CHAR_T *)_NL_CURRENT (LC_TIME, NLW(ERA_D_FMT))) + != L_('\0')))) + subfmt = (const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(D_FMT)); + goto subformat; +#else +# if HAVE_STRFTIME + goto underlying_strftime; +# else + /* Fall through. */ +# endif +#endif + case L_('D'): + if (modifier != 0) + goto bad_format; + subfmt = L_("%m/%d/%y"); + goto subformat; + + case L_('d'): + if (modifier == L_('E')) + goto bad_format; + + DO_NUMBER (2, tp->tm_mday); + + case L_('e'): + if (modifier == L_('E')) + goto bad_format; + + DO_NUMBER_SPACEPAD (2, tp->tm_mday); + + /* All numeric formats set DIGITS and NUMBER_VALUE and then + jump to one of these two labels. */ + + do_number_spacepad: + /* Force `_' flag unless overwritten by `0' or '-' flag. */ + if (pad != L_('0') && pad != L_('-')) + pad = L_('_'); + + do_number: + /* Format the number according to the MODIFIER flag. */ + + if (modifier == L_('O') && 0 <= number_value) + { +#ifdef _NL_CURRENT + /* Get the locale specific alternate representation of + the number NUMBER_VALUE. If none exist NULL is returned. */ + const CHAR_T *cp = nl_get_alt_digit (number_value + HELPER_LOCALE_ARG); + + if (cp != NULL) + { + size_t digitlen = STRLEN (cp); + if (digitlen != 0) + { + cpy (digitlen, cp); + break; + } + } +#else +# if HAVE_STRFTIME + goto underlying_strftime; +# endif +#endif + } + { + unsigned int u = number_value; + + bufp = buf + sizeof (buf) / sizeof (buf[0]); + negative_number = number_value < 0; + + if (negative_number) + u = -u; + + do + *--bufp = u % 10 + L_('0'); + while ((u /= 10) != 0); + } + + do_number_sign_and_padding: + if (negative_number) + *--bufp = L_('-'); + + if (pad != L_('-')) + { + int padding = digits - (buf + (sizeof (buf) / sizeof (buf[0])) + - bufp); + + if (padding > 0) + { + if (pad == L_('_')) + { + if ((size_t) padding >= maxsize - i) + return 0; + + if (p) + memset_space (p, padding); + i += padding; + width = width > padding ? width - padding : 0; + } + else + { + if ((size_t) digits >= maxsize - i) + return 0; + + if (negative_number) + { + ++bufp; + + if (p) + *p++ = L_('-'); + ++i; + } + + if (p) + memset_zero (p, padding); + i += padding; + width = 0; + } + } + } + + cpy (buf + sizeof (buf) / sizeof (buf[0]) - bufp, bufp); + break; + + case L_('F'): + if (modifier != 0) + goto bad_format; + subfmt = L_("%Y-%m-%d"); + goto subformat; + + case L_('H'): + if (modifier == L_('E')) + goto bad_format; + + DO_NUMBER (2, tp->tm_hour); + + case L_('I'): + if (modifier == L_('E')) + goto bad_format; + + DO_NUMBER (2, hour12); + + case L_('k'): /* GNU extension. */ + if (modifier == L_('E')) + goto bad_format; + + DO_NUMBER_SPACEPAD (2, tp->tm_hour); + + case L_('l'): /* GNU extension. */ + if (modifier == L_('E')) + goto bad_format; + + DO_NUMBER_SPACEPAD (2, hour12); + + case L_('j'): + if (modifier == L_('E')) + goto bad_format; + + DO_NUMBER (3, 1 + tp->tm_yday); + + case L_('M'): + if (modifier == L_('E')) + goto bad_format; + + DO_NUMBER (2, tp->tm_min); + + case L_('m'): + if (modifier == L_('E')) + goto bad_format; + + DO_NUMBER (2, tp->tm_mon + 1); + + case L_('n'): + add (1, *p = L_('\n')); + break; + + case L_('P'): + to_lowcase = 1; +#if !defined _NL_CURRENT && HAVE_STRFTIME + format_char = L_('p'); +#endif + /* FALLTHROUGH */ + + case L_('p'): + if (change_case) + { + to_uppcase = 0; + to_lowcase = 1; + } +#if defined _NL_CURRENT || !HAVE_STRFTIME + cpy (ap_len, ampm); + break; +#else + goto underlying_strftime; +#endif + + case L_('R'): + subfmt = L_("%H:%M"); + goto subformat; + + case L_('r'): +#if !defined _NL_CURRENT && HAVE_STRFTIME + goto underlying_strftime; +#else +# ifdef _NL_CURRENT + if (*(subfmt = (const CHAR_T *) _NL_CURRENT (LC_TIME, + NLW(T_FMT_AMPM))) + == L_('\0')) +# endif + subfmt = L_("%I:%M:%S %p"); + goto subformat; +#endif + + case L_('S'): + if (modifier == L_('E')) + goto bad_format; + + DO_NUMBER (2, tp->tm_sec); + + case L_('s'): /* GNU extension. */ + { + struct tm ltm; + time_t t; + + ltm = *tp; + t = mktime (<m); + + /* Generate string value for T using time_t arithmetic; + this works even if sizeof (long) < sizeof (time_t). */ + + bufp = buf + sizeof (buf) / sizeof (buf[0]); + negative_number = t < 0; + + do + { + int d = t % 10; + t /= 10; + + if (negative_number) + { + d = -d; + + /* Adjust if division truncates to minus infinity. */ + if (0 < -1 % 10 && d < 0) + { + t++; + d += 10; + } + } + + *--bufp = d + L_('0'); + } + while (t != 0); + + digits = 1; + goto do_number_sign_and_padding; + } + + case L_('X'): + if (modifier == L_('O')) + goto bad_format; +#ifdef _NL_CURRENT + if (! (modifier == L_('E') + && (*(subfmt = + (const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(ERA_T_FMT))) + != L_('\0')))) + subfmt = (const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(T_FMT)); + goto subformat; +#else +# if HAVE_STRFTIME + goto underlying_strftime; +# else + /* Fall through. */ +# endif +#endif + case L_('T'): + subfmt = L_("%H:%M:%S"); + goto subformat; + + case L_('t'): + add (1, *p = L_('\t')); + break; + + case L_('u'): + DO_NUMBER (1, (tp->tm_wday - 1 + 7) % 7 + 1); + + case L_('U'): + if (modifier == L_('E')) + goto bad_format; + + DO_NUMBER (2, (tp->tm_yday - tp->tm_wday + 7) / 7); + + case L_('V'): + case L_('g'): + case L_('G'): + if (modifier == L_('E')) + goto bad_format; + { + int year = tp->tm_year + TM_YEAR_BASE; + int days = iso_week_days (tp->tm_yday, tp->tm_wday); + + if (days < 0) + { + /* This ISO week belongs to the previous year. */ + year--; + days = iso_week_days (tp->tm_yday + (365 + __isleap (year)), + tp->tm_wday); + } + else + { + int d = iso_week_days (tp->tm_yday - (365 + __isleap (year)), + tp->tm_wday); + if (0 <= d) + { + /* This ISO week belongs to the next year. */ + year++; + days = d; + } + } + + switch (*f) + { + case L_('g'): + DO_NUMBER (2, (year % 100 + 100) % 100); + + case L_('G'): + DO_NUMBER (1, year); + + default: + DO_NUMBER (2, days / 7 + 1); + } + } + + case L_('W'): + if (modifier == L_('E')) + goto bad_format; + + DO_NUMBER (2, (tp->tm_yday - (tp->tm_wday - 1 + 7) % 7 + 7) / 7); + + case L_('w'): + if (modifier == L_('E')) + goto bad_format; + + DO_NUMBER (1, tp->tm_wday); + + case L_('Y'): + if (modifier == 'E') + { +#if HAVE_STRUCT_ERA_ENTRY + struct era_entry *era = _nl_get_era_entry (tp HELPER_LOCALE_ARG); + if (era) + { +# ifdef COMPILE_WIDE + subfmt = era->era_wformat; +# else + subfmt = era->era_format; +# endif + goto subformat; + } +#else +# if HAVE_STRFTIME + goto underlying_strftime; +# endif +#endif + } + if (modifier == L_('O')) + goto bad_format; + else + DO_NUMBER (1, tp->tm_year + TM_YEAR_BASE); + + case L_('y'): + if (modifier == L_('E')) + { +#if HAVE_STRUCT_ERA_ENTRY + struct era_entry *era = _nl_get_era_entry (tp HELPER_LOCALE_ARG); + if (era) + { + int delta = tp->tm_year - era->start_date[0]; + DO_NUMBER (1, (era->offset + + delta * era->absolute_direction)); + } +#else +# if HAVE_STRFTIME + goto underlying_strftime; +# endif +#endif + } + DO_NUMBER (2, (tp->tm_year % 100 + 100) % 100); + + case L_('Z'): + if (change_case) + { + to_uppcase = 0; + to_lowcase = 1; + } + +#if HAVE_TZNAME + /* The tzset() call might have changed the value. */ + if (!(zone && *zone) && tp->tm_isdst >= 0) + { + /* POSIX.1 requires that local time zone information is used as + though strftime called tzset. */ +# if HAVE_TZSET + if (!*tzset_called) + { + tzset (); + *tzset_called = true; + } +# endif + zone = tp->tm_isdst <= 1 ? tzname[tp->tm_isdst] : "?"; + } +#endif + if (! zone) + zone = ""; + +#ifdef COMPILE_WIDE + { + /* The zone string is always given in multibyte form. We have + to transform it first. */ + wchar_t *wczone; + size_t len; + widen (zone, wczone, len); + cpy (len, wczone); + } +#else + cpy (strlen (zone), zone); +#endif + break; + + case L_('z'): + if (tp->tm_isdst < 0) + break; + + { + int diff; +#if HAVE_TM_GMTOFF + diff = tp->tm_gmtoff; +#else + if (ut) + diff = 0; + else + { + struct tm gtm; + struct tm ltm; + time_t lt; + + /* POSIX.1 requires that local time zone information is used as + though strftime called tzset. */ +# if HAVE_TZSET + if (!*tzset_called) + { + tzset (); + *tzset_called = true; + } +# endif + + ltm = *tp; + lt = mktime (<m); + + if (lt == (time_t) -1) + { + /* mktime returns -1 for errors, but -1 is also a + valid time_t value. Check whether an error really + occurred. */ + struct tm tm; + + if (! __localtime_r (<, &tm) + || ((ltm.tm_sec ^ tm.tm_sec) + | (ltm.tm_min ^ tm.tm_min) + | (ltm.tm_hour ^ tm.tm_hour) + | (ltm.tm_mday ^ tm.tm_mday) + | (ltm.tm_mon ^ tm.tm_mon) + | (ltm.tm_year ^ tm.tm_year))) + break; + } + + if (! __gmtime_r (<, >m)) + break; + + diff = tm_diff (<m, >m); + } +#endif + + if (diff < 0) + { + add (1, *p = L_('-')); + diff = -diff; + } + else + add (1, *p = L_('+')); + + diff /= 60; + DO_NUMBER (4, (diff / 60) * 100 + diff % 60); + } + + case L_('\0'): /* GNU extension: % at end of format. */ + --f; + /* Fall through. */ + default: + /* Unknown format; output the format, including the '%', + since this is most likely the right thing to do if a + multibyte string has been misparsed. */ + bad_format: + { + int flen; + for (flen = 1; f[1 - flen] != L_('%'); flen++) + continue; + cpy (flen, &f[1 - flen]); + } + break; + } + } + + if (p && maxsize != 0) + *p = L_('\0'); + return i; +} + + +#ifdef emacs +/* For Emacs we have a separate interface which corresponds to the normal + strftime function and does not have the extra information whether the + TP arguments comes from a `gmtime' call or not. */ +size_t +emacs_strftime (char *s, size_t maxsize, const char *format, + const struct tm *tp) +{ + return my_strftime (s, maxsize, format, tp, 0); +} +#endif + +#if defined _LIBC && !defined COMPILE_WIDE +weak_alias (__strftime_l, strftime_l) +#endif diff --git a/REORG.TODO/time/strptime.c b/REORG.TODO/time/strptime.c new file mode 100644 index 0000000000..06463b364c --- /dev/null +++ b/REORG.TODO/time/strptime.c @@ -0,0 +1,34 @@ +/* Convert a string representation of time to a time value. + Copyright (C) 1996-2017 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, see + <http://www.gnu.org/licenses/>. */ + +/* 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 <time.h> +#include <locale/localeinfo.h> + + +char * +strptime (const char *buf, const char *format, struct tm *tm) +{ + return __strptime_internal (buf, format, tm, NULL, _NL_CURRENT_LOCALE); +} +libc_hidden_def (strptime) diff --git a/REORG.TODO/time/strptime_l.c b/REORG.TODO/time/strptime_l.c new file mode 100644 index 0000000000..dbf4606982 --- /dev/null +++ b/REORG.TODO/time/strptime_l.c @@ -0,0 +1,1211 @@ +/* Copyright (C) 2002-2017 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 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, see + <http://www.gnu.org/licenses/>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <assert.h> +#include <ctype.h> +#include <langinfo.h> +#include <limits.h> +#include <string.h> +#include <time.h> +#include <stdbool.h> + +#ifdef _LIBC +# define HAVE_LOCALTIME_R 0 +# include "../locale/localeinfo.h" +#endif + + +#if ! HAVE_LOCALTIME_R && ! defined localtime_r +# ifdef _LIBC +# define localtime_r __localtime_r +# else +/* Approximate localtime_r as best we can in its absence. */ +# define localtime_r my_localtime_r +static struct tm *localtime_r (const time_t *, struct tm *); +static struct tm * +localtime_r (const time_t *t, struct tm *tp) +{ + struct tm *l = localtime (t); + if (! l) + return 0; + *tp = *l; + return tp; +} +# endif /* ! _LIBC */ +#endif /* ! HAVE_LOCALTIME_R && ! defined (localtime_r) */ + + +#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_l ((cs1), (s2), len, locale) == 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 (ISSPACE (*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 _NL_CURRENT +# define get_alt_number(from, to, n) \ + ({ \ + __label__ do_normal; \ + \ + if (s.decided != raw) \ + { \ + val = _nl_parse_alt_digit (&rp HELPER_LOCALE_ARG); \ + if (val == -1 && s.decided != loc) \ + { \ + s.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, &s LOCALE_ARG)) != NULL) + + +#ifdef _LIBC +/* This is defined in locale/C-time.c in the GNU libc. */ +extern const struct __locale_data _nl_C_LC_TIME attribute_hidden; + +# 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 +static char const weekday_name[][10] = + { + "Sunday", "Monday", "Tuesday", "Wednesday", + "Thursday", "Friday", "Saturday" + }; +static char const ab_weekday_name[][4] = + { + "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" + }; +static char const month_name[][10] = + { + "January", "February", "March", "April", "May", "June", + "July", "August", "September", "October", "November", "December" + }; +static char const ab_month_name[][4] = + { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + }; +# 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" + +static 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 + +#if defined _LIBC +/* We use this code also for the extended locale handling where the + function gets as an additional argument the locale which has to be + used. To access the values we have to redefine the _NL_CURRENT + macro. */ +# define strptime __strptime_l +# undef _NL_CURRENT +# define _NL_CURRENT(category, item) \ + (current->values[_NL_ITEM_INDEX (item)].string) +# undef _NL_CURRENT_WORD +# define _NL_CURRENT_WORD(category, item) \ + (current->values[_NL_ITEM_INDEX (item)].word) +# define LOCALE_PARAM , __locale_t locale +# define LOCALE_ARG , locale +# define HELPER_LOCALE_ARG , current +# define ISSPACE(Ch) __isspace_l (Ch, locale) +#else +# define LOCALE_PARAM +# define LOCALE_ARG +# define HELPER_LOCALE_ARG +# define ISSPACE(Ch) isspace (Ch) +#endif + + + + +#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 + 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)); +} + + +#ifdef _LIBC +char * +internal_function +#else +static char * +#endif +__strptime_internal (const char *rp, const char *fmt, struct tm *tmp, + void *statep LOCALE_PARAM) +{ +#ifdef _LIBC + struct __locale_data *const current = locale->__locales[LC_TIME]; +#endif + + const char *rp_backup; + const char *rp_longest; + int cnt; + int cnt_longest; + size_t val; + size_t num_eras; + struct era_entry *era = NULL; + enum ptime_locale_status { not, loc, raw } decided_longest; + struct __strptime_state + { + unsigned int have_I : 1; + unsigned int have_wday : 1; + unsigned int have_yday : 1; + unsigned int have_mon : 1; + unsigned int have_mday : 1; + unsigned int have_uweek : 1; + unsigned int have_wweek : 1; + unsigned int is_pm : 1; + unsigned int want_century : 1; + unsigned int want_era : 1; + unsigned int want_xday : 1; + enum ptime_locale_status decided : 2; + signed char week_no; + signed char century; + int era_cnt; + } s; + struct tm tmb; + struct tm *tm; + + if (statep == NULL) + { + memset (&s, 0, sizeof (s)); + s.century = -1; + s.era_cnt = -1; +#ifdef _NL_CURRENT + s.decided = not; +#else + s.decided = raw; +#endif + tm = tmp; + } + else + { + s = *(struct __strptime_state *) statep; + tmb = *tmp; + tm = &tmb; + } + + 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; + /* We discard strftime modifiers. */ + while (*fmt == '-' || *fmt == '_' || *fmt == '0' + || *fmt == '^' || *fmt == '#') + ++fmt; + + /* And field width. */ + while (*fmt >= '0' && *fmt <= '9') + ++fmt; + +#ifndef _NL_CURRENT + /* 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. */ + rp_longest = NULL; + decided_longest = s.decided; + cnt_longest = -1; + for (cnt = 0; cnt < 7; ++cnt) + { + const char *trp; +#ifdef _NL_CURRENT + if (s.decided !=raw) + { + trp = rp; + if (match_string (_NL_CURRENT (LC_TIME, DAY_1 + cnt), trp) + && trp > rp_longest) + { + rp_longest = trp; + cnt_longest = cnt; + if (s.decided == not + && strcmp (_NL_CURRENT (LC_TIME, DAY_1 + cnt), + weekday_name[cnt])) + decided_longest = loc; + } + trp = rp; + if (match_string (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt), trp) + && trp > rp_longest) + { + rp_longest = trp; + cnt_longest = cnt; + if (s.decided == not + && strcmp (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt), + ab_weekday_name[cnt])) + decided_longest = loc; + } + } +#endif + if (s.decided != loc + && (((trp = rp, match_string (weekday_name[cnt], trp)) + && trp > rp_longest) + || ((trp = rp, match_string (ab_weekday_name[cnt], rp)) + && trp > rp_longest))) + { + rp_longest = trp; + cnt_longest = cnt; + decided_longest = raw; + } + } + if (rp_longest == NULL) + /* Does not match a weekday name. */ + return NULL; + rp = rp_longest; + s.decided = decided_longest; + tm->tm_wday = cnt_longest; + s.have_wday = 1; + break; + case 'b': + case 'B': + case 'h': + /* Match month name. */ + rp_longest = NULL; + decided_longest = s.decided; + cnt_longest = -1; + for (cnt = 0; cnt < 12; ++cnt) + { + const char *trp; +#ifdef _NL_CURRENT + if (s.decided !=raw) + { + trp = rp; + if (match_string (_NL_CURRENT (LC_TIME, MON_1 + cnt), trp) + && trp > rp_longest) + { + rp_longest = trp; + cnt_longest = cnt; + if (s.decided == not + && strcmp (_NL_CURRENT (LC_TIME, MON_1 + cnt), + month_name[cnt])) + decided_longest = loc; + } + trp = rp; + if (match_string (_NL_CURRENT (LC_TIME, ABMON_1 + cnt), trp) + && trp > rp_longest) + { + rp_longest = trp; + cnt_longest = cnt; + if (s.decided == not + && strcmp (_NL_CURRENT (LC_TIME, ABMON_1 + cnt), + ab_month_name[cnt])) + decided_longest = loc; + } + } +#endif + if (s.decided != loc + && (((trp = rp, match_string (month_name[cnt], trp)) + && trp > rp_longest) + || ((trp = rp, match_string (ab_month_name[cnt], trp)) + && trp > rp_longest))) + { + rp_longest = trp; + cnt_longest = cnt; + decided_longest = raw; + } + } + if (rp_longest == NULL) + /* Does not match a month name. */ + return NULL; + rp = rp_longest; + s.decided = decided_longest; + tm->tm_mon = cnt_longest; + s.have_mon = 1; + s.want_xday = 1; + break; + case 'c': + /* Match locale's date and time format. */ +#ifdef _NL_CURRENT + if (s.decided != raw) + { + if (!recursive (_NL_CURRENT (LC_TIME, D_T_FMT))) + { + if (s.decided == loc) + return NULL; + else + rp = rp_backup; + } + else + { + if (s.decided == not && + strcmp (_NL_CURRENT (LC_TIME, D_T_FMT), HERE_D_T_FMT)) + s.decided = loc; + s.want_xday = 1; + break; + } + s.decided = raw; + } +#endif + if (!recursive (HERE_D_T_FMT)) + return NULL; + s.want_xday = 1; + break; + case 'C': + /* Match century number. */ + match_century: + get_number (0, 99, 2); + s.century = val; + s.want_xday = 1; + break; + case 'd': + case 'e': + /* Match day of month. */ + get_number (1, 31, 2); + tm->tm_mday = val; + s.have_mday = 1; + s.want_xday = 1; + break; + case 'F': + if (!recursive ("%Y-%m-%d")) + return NULL; + s.want_xday = 1; + break; + case 'x': +#ifdef _NL_CURRENT + if (s.decided != raw) + { + if (!recursive (_NL_CURRENT (LC_TIME, D_FMT))) + { + if (s.decided == loc) + return NULL; + else + rp = rp_backup; + } + else + { + if (s.decided == not + && strcmp (_NL_CURRENT (LC_TIME, D_FMT), HERE_D_FMT)) + s.decided = loc; + s.want_xday = 1; + break; + } + s.decided = raw; + } +#endif + /* Fall through. */ + case 'D': + /* Match standard day format. */ + if (!recursive (HERE_D_FMT)) + return NULL; + s.want_xday = 1; + break; + case 'k': + case 'H': + /* Match hour in 24-hour clock. */ + get_number (0, 23, 2); + tm->tm_hour = val; + s.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; + s.have_I = 1; + break; + case 'j': + /* Match day number of year. */ + get_number (1, 366, 3); + tm->tm_yday = val - 1; + s.have_yday = 1; + break; + case 'm': + /* Match number of month. */ + get_number (1, 12, 2); + tm->tm_mon = val - 1; + s.have_mon = 1; + s.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 _NL_CURRENT + if (s.decided != raw) + { + if (match_string (_NL_CURRENT (LC_TIME, AM_STR), rp)) + { + if (strcmp (_NL_CURRENT (LC_TIME, AM_STR), HERE_AM_STR)) + s.decided = loc; + s.is_pm = 0; + break; + } + if (match_string (_NL_CURRENT (LC_TIME, PM_STR), rp)) + { + if (strcmp (_NL_CURRENT (LC_TIME, PM_STR), HERE_PM_STR)) + s.decided = loc; + s.is_pm = 1; + break; + } + s.decided = raw; + } +#endif + if (!match_string (HERE_AM_STR, rp)) + { + if (match_string (HERE_PM_STR, rp)) + s.is_pm = 1; + else + return NULL; + } + else + s.is_pm = 0; + break; + case 'r': +#ifdef _NL_CURRENT + if (s.decided != raw) + { + if (!recursive (_NL_CURRENT (LC_TIME, T_FMT_AMPM))) + { + if (s.decided == loc) + return NULL; + else + rp = rp_backup; + } + else + { + if (s.decided == not && + strcmp (_NL_CURRENT (LC_TIME, T_FMT_AMPM), + HERE_T_FMT_AMPM)) + s.decided = loc; + break; + } + s.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 _NL_CURRENT + if (s.decided != raw) + { + if (!recursive (_NL_CURRENT (LC_TIME, T_FMT))) + { + if (s.decided == loc) + return NULL; + else + rp = rp_backup; + } + else + { + if (strcmp (_NL_CURRENT (LC_TIME, T_FMT), HERE_T_FMT)) + s.decided = loc; + break; + } + s.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; + s.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); + s.week_no = val; + s.have_uweek = 1; + break; + case 'W': + get_number (0, 53, 2); + s.week_no = val; + s.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; + s.have_wday = 1; + break; + case 'y': + match_year_in_century: + /* 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. */ + s.want_century = 1; + s.want_xday = 1; + break; + case 'Y': + /* Match year including century number. */ + get_number (0, 9999, 4); + tm->tm_year = val - 1900; + s.want_century = 0; + s.want_xday = 1; + break; + case 'Z': + /* Read timezone but perform no conversion. */ + while (ISSPACE (*rp)) + rp++; + while (!ISSPACE (*rp) && *rp != '\0') + rp++; + break; + case 'z': + /* We recognize four formats: + 1. Two digits specify hours. + 2. Four digits specify hours and minutes. + 3. Two digits, ':', and two digits specify hours and minutes. + 4. 'Z' is equivalent to +0000. */ + { + val = 0; + while (ISSPACE (*rp)) + ++rp; + if (*rp == 'Z') + { + ++rp; + tm->tm_gmtoff = 0; + break; + } + if (*rp != '+' && *rp != '-') + return NULL; + bool neg = *rp++ == '-'; + int n = 0; + while (n < 4 && *rp >= '0' && *rp <= '9') + { + val = val * 10 + *rp++ - '0'; + ++n; + if (*rp == ':' && n == 2 && isdigit (*(rp + 1))) + ++rp; + } + if (n == 2) + val *= 100; + else if (n != 4) + /* Only two or four digits recognized. */ + return NULL; + else if (val % 100 >= 60) + /* Minutes valid range is 0 through 59. */ + return NULL; + tm->tm_gmtoff = (val / 100) * 3600 + (val % 100) * 60; + if (neg) + tm->tm_gmtoff = -tm->tm_gmtoff; + } + break; + case 'E': +#ifdef _NL_CURRENT + switch (*fmt++) + { + case 'c': + /* Match locale's alternate date and time format. */ + if (s.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 (s.decided == loc) + return NULL; + else + rp = rp_backup; + } + else + { + if (strcmp (fmt, HERE_D_T_FMT)) + s.decided = loc; + s.want_xday = 1; + break; + } + s.decided = raw; + } + /* The C locale has no era information, so use the + normal representation. */ + if (!recursive (HERE_D_T_FMT)) + return NULL; + s.want_xday = 1; + break; + case 'C': + if (s.decided != raw) + { + if (s.era_cnt >= 0) + { + era = _nl_select_era_entry (s.era_cnt HELPER_LOCALE_ARG); + if (era != NULL && match_string (era->era_name, rp)) + { + s.decided = loc; + break; + } + else + return NULL; + } + + num_eras = _NL_CURRENT_WORD (LC_TIME, + _NL_TIME_ERA_NUM_ENTRIES); + for (s.era_cnt = 0; s.era_cnt < (int) num_eras; + ++s.era_cnt, rp = rp_backup) + { + era = _nl_select_era_entry (s.era_cnt + HELPER_LOCALE_ARG); + if (era != NULL && match_string (era->era_name, rp)) + { + s.decided = loc; + break; + } + } + if (s.era_cnt != (int) num_eras) + break; + + s.era_cnt = -1; + if (s.decided == loc) + return NULL; + + s.decided = raw; + } + /* The C locale has no era information, so use the + normal representation. */ + goto match_century; + case 'y': + if (s.decided != raw) + { + get_number(0, 9999, 4); + tm->tm_year = val; + s.want_era = 1; + s.want_xday = 1; + s.want_century = 1; + + if (s.era_cnt >= 0) + { + assert (s.decided == loc); + + era = _nl_select_era_entry (s.era_cnt HELPER_LOCALE_ARG); + bool match = false; + if (era != NULL) + { + int delta = ((tm->tm_year - era->offset) + * era->absolute_direction); + match = (delta >= 0 + && delta < (((int64_t) era->stop_date[0] + - (int64_t) era->start_date[0]) + * era->absolute_direction)); + } + if (! match) + return NULL; + + break; + } + + num_eras = _NL_CURRENT_WORD (LC_TIME, + _NL_TIME_ERA_NUM_ENTRIES); + for (s.era_cnt = 0; s.era_cnt < (int) num_eras; ++s.era_cnt) + { + era = _nl_select_era_entry (s.era_cnt + HELPER_LOCALE_ARG); + if (era != NULL) + { + int delta = ((tm->tm_year - era->offset) + * era->absolute_direction); + if (delta >= 0 + && delta < (((int64_t) era->stop_date[0] + - (int64_t) era->start_date[0]) + * era->absolute_direction)) + { + s.decided = loc; + break; + } + } + } + if (s.era_cnt != (int) num_eras) + break; + + s.era_cnt = -1; + if (s.decided == loc) + return NULL; + + s.decided = raw; + } + + goto match_year_in_century; + case 'Y': + if (s.decided != raw) + { + num_eras = _NL_CURRENT_WORD (LC_TIME, + _NL_TIME_ERA_NUM_ENTRIES); + for (s.era_cnt = 0; s.era_cnt < (int) num_eras; + ++s.era_cnt, rp = rp_backup) + { + era = _nl_select_era_entry (s.era_cnt HELPER_LOCALE_ARG); + if (era != NULL && recursive (era->era_format)) + break; + } + if (s.era_cnt == (int) num_eras) + { + s.era_cnt = -1; + if (s.decided == loc) + return NULL; + else + rp = rp_backup; + } + else + { + s.decided = loc; + break; + } + + s.decided = raw; + } + get_number (0, 9999, 4); + tm->tm_year = val - 1900; + s.want_century = 0; + s.want_xday = 1; + break; + case 'x': + if (s.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 (s.decided == loc) + return NULL; + else + rp = rp_backup; + } + else + { + if (strcmp (fmt, HERE_D_FMT)) + s.decided = loc; + break; + } + s.decided = raw; + } + if (!recursive (HERE_D_FMT)) + return NULL; + break; + case 'X': + if (s.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 (s.decided == loc) + return NULL; + else + rp = rp_backup; + } + else + { + if (strcmp (fmt, HERE_T_FMT)) + s.decided = loc; + break; + } + s.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; + s.have_mday = 1; + s.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; + s.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; + s.have_I = 1; + break; + case 'm': + /* Match month using alternate numeric symbols. */ + get_alt_number (1, 12, 2); + tm->tm_mon = val - 1; + s.have_mon = 1; + s.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); + s.week_no = val; + s.have_uweek = 1; + break; + case 'W': + get_alt_number (0, 53, 2); + s.week_no = val; + s.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; + s.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; + s.want_xday = 1; + break; + default: + return NULL; + } + break; + default: + return NULL; + } + } + + if (statep != NULL) + { + /* Recursive invocation, returning success, so + update parent's struct tm and state. */ + *(struct __strptime_state *) statep = s; + *tmp = tmb; + return (char *) rp; + } + + if (s.have_I && s.is_pm) + tm->tm_hour += 12; + + if (s.century != -1) + { + if (s.want_century) + tm->tm_year = tm->tm_year % 100 + (s.century - 19) * 100; + else + /* Only the century, but not the year. Strange, but so be it. */ + tm->tm_year = (s.century - 19) * 100; + } + + if (s.era_cnt != -1) + { + era = _nl_select_era_entry (s.era_cnt HELPER_LOCALE_ARG); + if (era == NULL) + return NULL; + if (s.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 + if (s.want_era) + { + /* No era found but we have seen an E modifier. Rectify some + values. */ + if (s.want_century && s.century == -1 && tm->tm_year < 69) + tm->tm_year += 100; + } + + if (s.want_xday && !s.have_wday) + { + if ( !(s.have_mon && s.have_mday) && s.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 (!s.have_mon) + tm->tm_mon = t_mon - 1; + if (!s.have_mday) + tm->tm_mday = + (tm->tm_yday + - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1); + s.have_mon = 1; + s.have_mday = 1; + } + /* Don't crash in day_of_the_week if tm_mon is uninitialized. */ + if (s.have_mon || (unsigned) tm->tm_mon <= 11) + day_of_the_week (tm); + } + + if (s.want_xday && !s.have_yday && (s.have_mon || (unsigned) tm->tm_mon <= 11)) + day_of_the_year (tm); + + if ((s.have_uweek || s.have_wweek) && s.have_wday) + { + int save_wday = tm->tm_wday; + int save_mday = tm->tm_mday; + int save_mon = tm->tm_mon; + int w_offset = s.have_uweek ? 0 : 1; + + tm->tm_mday = 1; + tm->tm_mon = 0; + day_of_the_week (tm); + if (s.have_mday) + tm->tm_mday = save_mday; + if (s.have_mon) + tm->tm_mon = save_mon; + + if (!s.have_yday) + tm->tm_yday = ((7 - (tm->tm_wday - w_offset)) % 7 + + (s.week_no - 1) * 7 + + (save_wday - w_offset + 7) % 7); + + if (!s.have_mday || !s.have_mon) + { + int t_mon = 0; + while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon] + <= tm->tm_yday) + t_mon++; + if (!s.have_mon) + tm->tm_mon = t_mon - 1; + if (!s.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 (const char *buf, const char *format, struct tm *tm LOCALE_PARAM) +{ + return __strptime_internal (buf, format, tm, NULL LOCALE_ARG); +} + +#ifdef _LIBC +weak_alias (__strptime_l, strptime_l) +#endif diff --git a/REORG.TODO/time/sys/time.h b/REORG.TODO/time/sys/time.h new file mode 100644 index 0000000000..165296ac59 --- /dev/null +++ b/REORG.TODO/time/sys/time.h @@ -0,0 +1,188 @@ +/* Copyright (C) 1991-2017 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 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, see + <http://www.gnu.org/licenses/>. */ + +#ifndef _SYS_TIME_H +#define _SYS_TIME_H 1 + +#include <features.h> + +#include <bits/types.h> +#include <bits/types/time_t.h> +#include <bits/types/struct_timeval.h> + +#ifndef __suseconds_t_defined +typedef __suseconds_t suseconds_t; +# define __suseconds_t_defined +#endif + +#include <sys/select.h> + +__BEGIN_DECLS + +#ifdef __USE_GNU +/* Macros for converting between `struct timeval' and `struct timespec'. */ +# define TIMEVAL_TO_TIMESPEC(tv, ts) { \ + (ts)->tv_sec = (tv)->tv_sec; \ + (ts)->tv_nsec = (tv)->tv_usec * 1000; \ +} +# define TIMESPEC_TO_TIMEVAL(tv, ts) { \ + (tv)->tv_sec = (ts)->tv_sec; \ + (tv)->tv_usec = (ts)->tv_nsec / 1000; \ +} +#endif + + +#ifdef __USE_MISC +/* Structure crudely representing a timezone. + This is obsolete and should never be used. */ +struct timezone + { + int tz_minuteswest; /* Minutes west of GMT. */ + int tz_dsttime; /* Nonzero if DST is ever in effect. */ + }; + +typedef struct timezone *__restrict __timezone_ptr_t; +#else +typedef void *__restrict __timezone_ptr_t; +#endif + +/* Get the current time of day and timezone information, + putting it into *TV and *TZ. If TZ is NULL, *TZ is not filled. + Returns 0 on success, -1 on errors. + NOTE: This form of timezone information is obsolete. + Use the functions and variables declared in <time.h> instead. */ +extern int gettimeofday (struct timeval *__restrict __tv, + __timezone_ptr_t __tz) __THROW __nonnull ((1)); + +#ifdef __USE_MISC +/* Set the current time of day and timezone information. + This call is restricted to the super-user. */ +extern int settimeofday (const struct timeval *__tv, + const struct timezone *__tz) + __THROW; + +/* Adjust the current time of day by the amount in DELTA. + If OLDDELTA is not NULL, it is filled in with the amount + of time adjustment remaining to be done from the last `adjtime' call. + This call is restricted to the super-user. */ +extern int adjtime (const struct timeval *__delta, + struct timeval *__olddelta) __THROW; +#endif + + +/* Values for the first argument to `getitimer' and `setitimer'. */ +enum __itimer_which + { + /* Timers run in real time. */ + ITIMER_REAL = 0, +#define ITIMER_REAL ITIMER_REAL + /* Timers run only when the process is executing. */ + ITIMER_VIRTUAL = 1, +#define ITIMER_VIRTUAL ITIMER_VIRTUAL + /* Timers run when the process is executing and when + the system is executing on behalf of the process. */ + ITIMER_PROF = 2 +#define ITIMER_PROF ITIMER_PROF + }; + +/* Type of the second argument to `getitimer' and + the second and third arguments `setitimer'. */ +struct itimerval + { + /* Value to put into `it_value' when the timer expires. */ + struct timeval it_interval; + /* Time to the next timer expiration. */ + struct timeval it_value; + }; + +#if defined __USE_GNU && !defined __cplusplus +/* Use the nicer parameter type only in GNU mode and not for C++ since the + strict C++ rules prevent the automatic promotion. */ +typedef enum __itimer_which __itimer_which_t; +#else +typedef int __itimer_which_t; +#endif + +/* Set *VALUE to the current setting of timer WHICH. + Return 0 on success, -1 on errors. */ +extern int getitimer (__itimer_which_t __which, + struct itimerval *__value) __THROW; + +/* Set the timer WHICH to *NEW. If OLD is not NULL, + set *OLD to the old value of timer WHICH. + Returns 0 on success, -1 on errors. */ +extern int setitimer (__itimer_which_t __which, + const struct itimerval *__restrict __new, + struct itimerval *__restrict __old) __THROW; + +/* Change the access time of FILE to TVP[0] and the modification time of + FILE to TVP[1]. If TVP is a null pointer, use the current time instead. + Returns 0 on success, -1 on errors. */ +extern int utimes (const char *__file, const struct timeval __tvp[2]) + __THROW __nonnull ((1)); + +#ifdef __USE_MISC +/* Same as `utimes', but does not follow symbolic links. */ +extern int lutimes (const char *__file, const struct timeval __tvp[2]) + __THROW __nonnull ((1)); + +/* Same as `utimes', but takes an open file descriptor instead of a name. */ +extern int futimes (int __fd, const struct timeval __tvp[2]) __THROW; +#endif + +#ifdef __USE_GNU +/* Change the access time of FILE relative to FD to TVP[0] and the + modification time of FILE to TVP[1]. If TVP is a null pointer, use + the current time instead. Returns 0 on success, -1 on errors. */ +extern int futimesat (int __fd, const char *__file, + const struct timeval __tvp[2]) __THROW; +#endif + + +#ifdef __USE_MISC +/* Convenience macros for operations on timevals. + NOTE: `timercmp' does not work for >= or <=. */ +# define timerisset(tvp) ((tvp)->tv_sec || (tvp)->tv_usec) +# define timerclear(tvp) ((tvp)->tv_sec = (tvp)->tv_usec = 0) +# define timercmp(a, b, CMP) \ + (((a)->tv_sec == (b)->tv_sec) ? \ + ((a)->tv_usec CMP (b)->tv_usec) : \ + ((a)->tv_sec CMP (b)->tv_sec)) +# define timeradd(a, b, result) \ + do { \ + (result)->tv_sec = (a)->tv_sec + (b)->tv_sec; \ + (result)->tv_usec = (a)->tv_usec + (b)->tv_usec; \ + if ((result)->tv_usec >= 1000000) \ + { \ + ++(result)->tv_sec; \ + (result)->tv_usec -= 1000000; \ + } \ + } while (0) +# define timersub(a, b, result) \ + do { \ + (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \ + (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \ + if ((result)->tv_usec < 0) { \ + --(result)->tv_sec; \ + (result)->tv_usec += 1000000; \ + } \ + } while (0) +#endif /* Misc. */ + +__END_DECLS + +#endif /* sys/time.h */ diff --git a/REORG.TODO/time/sys/timeb.h b/REORG.TODO/time/sys/timeb.h new file mode 100644 index 0000000000..351aeaf619 --- /dev/null +++ b/REORG.TODO/time/sys/timeb.h @@ -0,0 +1,43 @@ +/* Copyright (C) 1994-2017 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 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, see + <http://www.gnu.org/licenses/>. */ + +#ifndef _SYS_TIMEB_H +#define _SYS_TIMEB_H 1 + +#include <features.h> + +#include <bits/types/time_t.h> + +__BEGIN_DECLS + +/* Structure returned by the `ftime' function. */ + +struct timeb + { + time_t time; /* Seconds since epoch, as from `time'. */ + unsigned short int millitm; /* Additional milliseconds. */ + short int timezone; /* Minutes west of GMT. */ + short int dstflag; /* Nonzero if Daylight Savings Time used. */ + }; + +/* Fill in TIMEBUF with information about the current time. */ + +extern int ftime (struct timeb *__timebuf); + +__END_DECLS + +#endif /* sys/timeb.h */ diff --git a/REORG.TODO/time/test_time.c b/REORG.TODO/time/test_time.c new file mode 100644 index 0000000000..745acdd458 --- /dev/null +++ b/REORG.TODO/time/test_time.c @@ -0,0 +1,115 @@ +/* Copyright (C) 1991-2017 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 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, see + <http://www.gnu.org/licenses/>. */ + +#include <stdio.h> +#include <stdlib.h> +#include <time.h> + + +int +main (int argc, char **argv) +{ + time_t t; + struct tm *tp; + struct tm tbuf; + int lose = 0; + + --argc; + ++argv; + + do + { + char buf[BUFSIZ]; + if (argc > 0) + { + static char buf[BUFSIZ]; + sprintf(buf, "TZ=%s", *argv); + if (putenv(buf)) + { + puts("putenv failed."); + lose = 1; + } + else + puts (buf); + } + tzset(); + tbuf.tm_year = 72; + tbuf.tm_mon = 0; + tbuf.tm_mday = 31; + tbuf.tm_hour = 6; + tbuf.tm_min = 14; + tbuf.tm_sec = 50; + tbuf.tm_isdst = -1; + doit:; + t = mktime(&tbuf); + if (t == (time_t) -1) + { + puts("mktime() failed?"); + lose = 1; + } + tp = localtime(&t); + if (tp == NULL) + { + puts("localtime() failed."); + lose = 1; + } + else if (strftime(buf, sizeof(buf), "%a %b %d %X %Z %Y", tp) == 0) + { + puts("strftime() failed."); + lose = 1; + } + else + puts(buf); + if (tbuf.tm_year == 101) + { + tbuf.tm_year = 97; + tbuf.tm_mon = 0; + goto doit; + } + ++argv; + } while (--argc > 0); + + { +#define SIZE 256 + char buffer[SIZE]; + time_t curtime; + struct tm *loctime; + + curtime = time (NULL); + + loctime = localtime (&curtime); + + fputs (asctime (loctime), stdout); + + strftime (buffer, SIZE, "Today is %A, %B %d.\n", loctime); + fputs (buffer, stdout); + strftime (buffer, SIZE, "The time is %I:%M %p.\n", loctime); + fputs (buffer, stdout); + + loctime->tm_year = 72; + loctime->tm_mon = 8; + loctime->tm_mday = 12; + loctime->tm_hour = 20; + loctime->tm_min = 49; + loctime->tm_sec = 05; + curtime = mktime (loctime); + strftime (buffer, SIZE, "%D %T was %w the %jth.\n", loctime); + fputs (buffer, stdout); + } + + return (lose ? EXIT_FAILURE : EXIT_SUCCESS); +} diff --git a/REORG.TODO/time/time.c b/REORG.TODO/time/time.c new file mode 100644 index 0000000000..e375035265 --- /dev/null +++ b/REORG.TODO/time/time.c @@ -0,0 +1,33 @@ +/* Copyright (C) 1991-2017 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 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, see + <http://www.gnu.org/licenses/>. */ + +#include <errno.h> +#include <time.h> + +/* Return the time now, and store it in *TIMER if not NULL. */ +time_t +time (time_t *timer) +{ + __set_errno (ENOSYS); + + if (timer != NULL) + *timer = (time_t) -1; + return (time_t) -1; +} +libc_hidden_def (time) + +stub_warning (time) diff --git a/REORG.TODO/time/time.h b/REORG.TODO/time/time.h new file mode 100644 index 0000000000..bb4994feee --- /dev/null +++ b/REORG.TODO/time/time.h @@ -0,0 +1,309 @@ +/* Copyright (C) 1991-2017 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 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, see + <http://www.gnu.org/licenses/>. */ + +/* + * ISO C99 Standard: 7.23 Date and time <time.h> + */ + +#ifndef _TIME_H +#define _TIME_H 1 + +#include <features.h> + +#define __need_size_t +#define __need_NULL +#include <stddef.h> + +/* This defines CLOCKS_PER_SEC, which is the number of processor clock + ticks per second, and possibly a number of other constants. */ +#include <bits/time.h> + +/* Many of the typedefs and structs whose official home is this header + may also need to be defined by other headers. */ +#include <bits/types/clock_t.h> +#include <bits/types/time_t.h> +#include <bits/types/struct_tm.h> + +#if defined __USE_POSIX199309 || defined __USE_ISOC11 +# include <bits/types/struct_timespec.h> +#endif + +#ifdef __USE_POSIX199309 +# include <bits/types/clockid_t.h> +# include <bits/types/timer_t.h> +# include <bits/types/struct_itimerspec.h> +struct sigevent; +#endif + +#ifdef __USE_XOPEN2K +# ifndef __pid_t_defined +typedef __pid_t pid_t; +# define __pid_t_defined +# endif +#endif + +#ifdef __USE_XOPEN2K8 +# include <xlocale.h> +#endif + +#ifdef __USE_ISOC11 +/* Time base values for timespec_get. */ +# define TIME_UTC 1 +#endif + +__BEGIN_DECLS + +/* Time used by the program so far (user time + system time). + The result / CLOCKS_PER_SECOND is program time in seconds. */ +extern clock_t clock (void) __THROW; + +/* Return the current time and put it in *TIMER if TIMER is not NULL. */ +extern time_t time (time_t *__timer) __THROW; + +/* Return the difference between TIME1 and TIME0. */ +extern double difftime (time_t __time1, time_t __time0) + __THROW __attribute__ ((__const__)); + +/* Return the `time_t' representation of TP and normalize TP. */ +extern time_t mktime (struct tm *__tp) __THROW; + + +/* Format TP into S according to FORMAT. + Write no more than MAXSIZE characters and return the number + of characters written, or 0 if it would exceed MAXSIZE. */ +extern size_t strftime (char *__restrict __s, size_t __maxsize, + const char *__restrict __format, + const struct tm *__restrict __tp) __THROW; + +#ifdef __USE_XOPEN +/* Parse S according to FORMAT and store binary time information in TP. + The return value is a pointer to the first unparsed character in S. */ +extern char *strptime (const char *__restrict __s, + const char *__restrict __fmt, struct tm *__tp) + __THROW; +#endif + +#ifdef __USE_XOPEN2K8 +/* Similar to the two functions above but take the information from + the provided locale and not the global locale. */ + +extern size_t strftime_l (char *__restrict __s, size_t __maxsize, + const char *__restrict __format, + const struct tm *__restrict __tp, + __locale_t __loc) __THROW; +#endif + +#ifdef __USE_GNU +extern char *strptime_l (const char *__restrict __s, + const char *__restrict __fmt, struct tm *__tp, + __locale_t __loc) __THROW; +#endif + + +/* Return the `struct tm' representation of *TIMER + in Universal Coordinated Time (aka Greenwich Mean Time). */ +extern struct tm *gmtime (const time_t *__timer) __THROW; + +/* Return the `struct tm' representation + of *TIMER in the local timezone. */ +extern struct tm *localtime (const time_t *__timer) __THROW; + +#ifdef __USE_POSIX +/* Return the `struct tm' representation of *TIMER in UTC, + using *TP to store the result. */ +extern struct tm *gmtime_r (const time_t *__restrict __timer, + struct tm *__restrict __tp) __THROW; + +/* Return the `struct tm' representation of *TIMER in local time, + using *TP to store the result. */ +extern struct tm *localtime_r (const time_t *__restrict __timer, + struct tm *__restrict __tp) __THROW; +#endif /* POSIX */ + +/* Return a string of the form "Day Mon dd hh:mm:ss yyyy\n" + that is the representation of TP in this format. */ +extern char *asctime (const struct tm *__tp) __THROW; + +/* Equivalent to `asctime (localtime (timer))'. */ +extern char *ctime (const time_t *__timer) __THROW; + +#ifdef __USE_POSIX +/* Reentrant versions of the above functions. */ + +/* Return in BUF a string of the form "Day Mon dd hh:mm:ss yyyy\n" + that is the representation of TP in this format. */ +extern char *asctime_r (const struct tm *__restrict __tp, + char *__restrict __buf) __THROW; + +/* Equivalent to `asctime_r (localtime_r (timer, *TMP*), buf)'. */ +extern char *ctime_r (const time_t *__restrict __timer, + char *__restrict __buf) __THROW; +#endif /* POSIX */ + + +/* Defined in localtime.c. */ +extern char *__tzname[2]; /* Current timezone names. */ +extern int __daylight; /* If daylight-saving time is ever in use. */ +extern long int __timezone; /* Seconds west of UTC. */ + + +#ifdef __USE_POSIX +/* Same as above. */ +extern char *tzname[2]; + +/* Set time conversion information from the TZ environment variable. + If TZ is not defined, a locale-dependent default is used. */ +extern void tzset (void) __THROW; +#endif + +#if defined __USE_MISC || defined __USE_XOPEN +extern int daylight; +extern long int timezone; +#endif + +#ifdef __USE_MISC +/* Set the system time to *WHEN. + This call is restricted to the superuser. */ +extern int stime (const time_t *__when) __THROW; +#endif + + +/* 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)) + + +#ifdef __USE_MISC +/* Miscellaneous functions many Unices inherited from the public domain + localtime package. These are included only for compatibility. */ + +/* Like `mktime', but for TP represents Universal Time, not local time. */ +extern time_t timegm (struct tm *__tp) __THROW; + +/* Another name for `mktime'. */ +extern time_t timelocal (struct tm *__tp) __THROW; + +/* Return the number of days in YEAR. */ +extern int dysize (int __year) __THROW __attribute__ ((__const__)); +#endif + + +#ifdef __USE_POSIX199309 +/* Pause execution for a number of nanoseconds. + + This function is a cancellation point and therefore not marked with + __THROW. */ +extern int nanosleep (const struct timespec *__requested_time, + struct timespec *__remaining); + + +/* Get resolution of clock CLOCK_ID. */ +extern int clock_getres (clockid_t __clock_id, struct timespec *__res) __THROW; + +/* Get current value of clock CLOCK_ID and store it in TP. */ +extern int clock_gettime (clockid_t __clock_id, struct timespec *__tp) __THROW; + +/* Set clock CLOCK_ID to value TP. */ +extern int clock_settime (clockid_t __clock_id, const struct timespec *__tp) + __THROW; + +# ifdef __USE_XOPEN2K +/* High-resolution sleep with the specified clock. + + This function is a cancellation point and therefore not marked with + __THROW. */ +extern int clock_nanosleep (clockid_t __clock_id, int __flags, + const struct timespec *__req, + struct timespec *__rem); + +/* Return clock ID for CPU-time clock. */ +extern int clock_getcpuclockid (pid_t __pid, clockid_t *__clock_id) __THROW; +# endif + + +/* Create new per-process timer using CLOCK_ID. */ +extern int timer_create (clockid_t __clock_id, + struct sigevent *__restrict __evp, + timer_t *__restrict __timerid) __THROW; + +/* Delete timer TIMERID. */ +extern int timer_delete (timer_t __timerid) __THROW; + +/* Set timer TIMERID to VALUE, returning old value in OVALUE. */ +extern int timer_settime (timer_t __timerid, int __flags, + const struct itimerspec *__restrict __value, + struct itimerspec *__restrict __ovalue) __THROW; + +/* Get current value of timer TIMERID and store it in VALUE. */ +extern int timer_gettime (timer_t __timerid, struct itimerspec *__value) + __THROW; + +/* Get expiration overrun for timer TIMERID. */ +extern int timer_getoverrun (timer_t __timerid) __THROW; +#endif + + +#ifdef __USE_ISOC11 +/* Set TS to calendar time based in time base BASE. */ +extern int timespec_get (struct timespec *__ts, int __base) + __THROW __nonnull ((1)); +#endif + + +#ifdef __USE_XOPEN_EXTENDED +/* Set to one of the following values to indicate an error. + 1 the DATEMSK environment variable is null or undefined, + 2 the template file cannot be opened for reading, + 3 failed to get file status information, + 4 the template file is not a regular file, + 5 an error is encountered while reading the template file, + 6 memory allication failed (not enough memory available), + 7 there is no line in the template that matches the input, + 8 invalid input specification Example: February 31 or a time is + specified that can not be represented in a time_t (representing + the time in seconds since 00:00:00 UTC, January 1, 1970) */ +extern int getdate_err; + +/* Parse the given string as a date specification and return a value + representing the value. The templates from the file identified by + the environment variable DATEMSK are used. In case of an error + `getdate_err' is set. + + This function is a possible cancellation point and therefore not + marked with __THROW. */ +extern struct tm *getdate (const char *__string); +#endif + +#ifdef __USE_GNU +/* Since `getdate' is not reentrant because of the use of `getdate_err' + and the static buffer to return the result in, we provide a thread-safe + variant. The functionality is the same. The result is returned in + the buffer pointed to by RESBUFP and in case of an error the return + value is != 0 with the same values as given above for `getdate_err'. + + This function is not part of POSIX and therefore no official + cancellation point. But due to similarity with an POSIX interface + or due to the implementation it is a cancellation point and + therefore not marked with __THROW. */ +extern int getdate_r (const char *__restrict __string, + struct tm *__restrict __resbufp); +#endif + +__END_DECLS + +#endif /* time.h. */ diff --git a/REORG.TODO/time/timegm.c b/REORG.TODO/time/timegm.c new file mode 100644 index 0000000000..b0f5d16d16 --- /dev/null +++ b/REORG.TODO/time/timegm.c @@ -0,0 +1,47 @@ +/* Convert UTC calendar time to simple time. Like mktime but assumes UTC. + + Copyright (C) 1994-2017 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 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, see + <http://www.gnu.org/licenses/>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#ifdef _LIBC +# include <time.h> +#else +# include "timegm.h" + +/* Portable standalone applications should supply a "time_r.h" that + declares a POSIX-compliant gmtime_r, for the benefit of older + implementations that lack gmtime_r or have a nonstandard one. + See the gnulib time_r module for one way to implement this. */ +# include <time_r.h> +# undef __gmtime_r +# define __gmtime_r gmtime_r +time_t __mktime_internal (struct tm *, + struct tm * (*) (time_t const *, struct tm *), + time_t *); +#endif + +time_t +timegm (struct tm *tmp) +{ + static time_t gmtime_offset; + tmp->tm_isdst = 0; + return __mktime_internal (tmp, __gmtime_r, &gmtime_offset); +} diff --git a/REORG.TODO/time/timespec_get.c b/REORG.TODO/time/timespec_get.c new file mode 100644 index 0000000000..125612af14 --- /dev/null +++ b/REORG.TODO/time/timespec_get.c @@ -0,0 +1,37 @@ +/* Copyright (C) 2011-2017 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 Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License. + + 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, see + <http://www.gnu.org/licenses/>. */ + +#include <time.h> + + +/* Set TS to calendar time based in time base BASE. */ +int +timespec_get (struct timespec *ts, int base) +{ + switch (base) + { + case TIME_UTC: + /* Not supported. */ + return 0; + + default: + return 0; + } + + return base; +} +stub_warning (timespec_get) diff --git a/REORG.TODO/time/tst-ftime.c b/REORG.TODO/time/tst-ftime.c new file mode 100644 index 0000000000..6df20be630 --- /dev/null +++ b/REORG.TODO/time/tst-ftime.c @@ -0,0 +1,59 @@ +/* Verify that ftime is sane. + Copyright (C) 2014-2017 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 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, see + <http://www.gnu.org/licenses/>. */ + +#include <sys/timeb.h> +#include <stdio.h> + +static int +do_test (void) +{ + struct timeb prev, curr = {.time = 0, .millitm = 0}; + int sec = 0; + + while (sec != 3) + { + prev = curr; + + if (ftime (&curr)) + { + printf ("ftime returned an error\n"); + return 1; + } + + if (curr.time < prev.time) + { + printf ("ftime's time flowed backwards\n"); + return 1; + } + + if (curr.time == prev.time + && curr.millitm < prev.millitm) + { + printf ("ftime's millitm flowed backwards\n"); + return 1; + } + + if (curr.time > prev.time) + sec ++; + } + return 0; +} + +#define TIMEOUT 3 +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/REORG.TODO/time/tst-ftime_l.c b/REORG.TODO/time/tst-ftime_l.c new file mode 100644 index 0000000000..6690efeb9c --- /dev/null +++ b/REORG.TODO/time/tst-ftime_l.c @@ -0,0 +1,129 @@ +#include <locale.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <wchar.h> + + +static int +do_test (void) +{ + locale_t l; + locale_t old; + struct tm tm; + char buf[1000]; + wchar_t wbuf[1000]; + int result = 0; + size_t n; + + l = newlocale (LC_ALL_MASK, "de_DE.ISO-8859-1", NULL); + if (l == NULL) + { + puts ("newlocale failed"); + exit (1); + } + + memset (&tm, '\0', sizeof (tm)); + + tm.tm_year = 102; + tm.tm_mon = 2; + tm.tm_mday = 1; + + if (strftime (buf, sizeof (buf), "%e %^B %Y", &tm) == 0) + { + puts ("initial strftime failed"); + exit (1); + } + if (strcmp (buf, " 1 MARCH 2002") != 0) + { + printf ("initial strftime: expected \"%s\", got \"%s\"\n", + " 1 MARCH 2002", buf); + result = 1; + } + else + printf ("got \"%s\"\n", buf); + + /* Now using the extended locale model. */ + if (strftime_l (buf, sizeof (buf), "%e %^B %Y", &tm, l) == 0) + { + puts ("strftime_l failed"); + result = 1; + } + else if (strcmp (buf, " 1 M\xc4RZ 2002") != 0) + { + printf ("strftime_l: expected \"%s\", got \"%s\"\n", + " 1 M\xc4RZ 2002", buf); + result = 1; + } + else + { + setlocale (LC_ALL, "de_DE.ISO-8859-1"); + printf ("got \"%s\"\n", buf); + setlocale (LC_ALL, "C"); + } + + /* And the wide character version. */ + if (wcsftime_l (wbuf, sizeof (wbuf) / sizeof (wbuf[0]), L"%e %^B %Y", &tm, l) + == 0) + { + puts ("wcsftime_l failed"); + result = 1; + } + else if (wcscmp (wbuf, L" 1 M\x00c4RZ 2002") != 0) + { + printf ("wcsftime_l: expected \"%ls\", got \"%ls\"\n", + L" 1 M\x00c4RZ 2002", wbuf); + result = 1; + } + else + { + setlocale (LC_ALL, "de_DE.ISO-8859-1"); + printf ("got \"%ls\"\n", wbuf); + setlocale (LC_ALL, "C"); + } + + old = uselocale (l); + + n = strftime (buf, sizeof (buf), "%e %^B %Y", &tm); + + /* Switch back. */ + (void) uselocale (old); + + if (n == 0) + { + puts ("strftime after first uselocale failed"); + result = 1; + } + else if (strcmp (buf, " 1 M\xc4RZ 2002") != 0) + { + printf ("strftime in non-C locale: expected \"%s\", got \"%s\"\n", + " 1 M\xc4RZ 2002", buf); + result = 1; + } + else + { + setlocale (LC_ALL, "de_DE.ISO-8859-1"); + printf ("got \"%s\"\n", buf); + setlocale (LC_ALL, "C"); + } + + if (strftime (buf, sizeof (buf), "%e %^B %Y", &tm) == 0) + { + puts ("strftime after second uselocale failed"); + result = 1; + } + else if (strcmp (buf, " 1 MARCH 2002") != 0) + { + printf ("initial strftime: expected \"%s\", got \"%s\"\n", + " 1 MARCH 2002", buf); + result = 1; + } + else + printf ("got \"%s\"\n", buf); + + return result; +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/REORG.TODO/time/tst-getdate.c b/REORG.TODO/time/tst-getdate.c new file mode 100644 index 0000000000..e4b34b7a5c --- /dev/null +++ b/REORG.TODO/time/tst-getdate.c @@ -0,0 +1,126 @@ +/* Test for getdate. + Copyright (C) 2000-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Andreas Jaeger <aj@suse.de>, 2000. + + 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, see + <http://www.gnu.org/licenses/>. */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +static const struct +{ + const char *str; + const char *tz; + int err; + struct tm tm; +} tests [] = +{ + {"21:01:10 1999-1-31", "Universal", 0, {10, 1, 21, 31, 0, 99, 0, 0, 0}}, + {"21:01:10 1999-1-31", "Universal", 0, {10, 1, 21, 31, 0, 99, 0, 0, 0}}, + {" 21:01:10 1999-1-31", "Universal", 0, {10, 1, 21, 31, 0, 99, 0, 0, 0}}, + {"21:01:10 1999-1-31 ", "Universal", 0, {10, 1, 21, 31, 0, 99, 0, 0, 0}}, + {" 21:01:10 1999-1-31 ", "Universal", 0, {10, 1, 21, 31, 0, 99, 0, 0, 0}}, + {"21:01:10 1999-2-28", "Universal", 0, {10, 1, 21, 28, 1, 99, 0, 0, 0}}, + {"16:30:46 2000-2-29", "Universal", 0, {46, 30,16, 29, 1, 100, 0, 0, 0}}, + {"01-08-2000 05:06:07", "Europe/Berlin", 0, {7, 6, 5, 1, 7, 100, 0, 0, 0}} +}; + +static void +report_date_error (int err) +{ + switch(err) + { + case 1: + printf ("The environment variable DATEMSK is not defined or null.\n"); + break; + case 2: + printf ("The template file denoted by the DATEMSK environment variable cannot be opened.\n"); + break; + case 3: + printf ("Information about the template file cannot retrieved.\n"); + break; + case 4: + printf ("The template file is not a regular file.\n"); + break; + case 5: + printf ("An I/O error occurred while reading the template file.\n"); + break; + case 6: + printf ("Not enough memory available to execute the function.\n"); + break; + case 7: + printf ("The template file contains no matching template.\n"); + break; + case 8: + printf ("The input date is invalid, but would match a template otherwise.\n"); + break; + default: + printf("Unknown error code.\n"); + break; + } +} + + +static int +do_test (void) +{ + int errors = 0; + size_t i; + struct tm *tm; + + + for (i = 0; i < sizeof (tests) / sizeof (tests[0]); ++i) + { + setenv ("TZ", tests[i].tz, 1); + + tm = getdate (tests[i].str); + + if (getdate_err != tests[i].err) + { + printf ("Failure for getdate (\"%s\"):\n", tests[i].str); + printf ("getdate_err should be %d but returned: %d which means:\n", + tests[i].err, getdate_err); + report_date_error (getdate_err); + ++errors; + } + else if (tests[i].tm.tm_mon != tm->tm_mon + || tests[i].tm.tm_year != tm->tm_year + || tests[i].tm.tm_mday != tm->tm_mday + || tests[i].tm.tm_hour != tm->tm_hour + || tests[i].tm.tm_min != tm->tm_min + || tests[i].tm.tm_sec != tm->tm_sec) + { + printf ("Failure for getdate (\"%s\"):\n", tests[i].str); + printf ("struct tm is: %d-%d-%d %d:%d:%d\n", + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec); + printf ("but should be: %d-%d-%d %d:%d:%d\n", + tests[i].tm.tm_year + 1900, tests[i].tm.tm_mon + 1, + tests[i].tm.tm_mday, + tests[i].tm.tm_hour, tests[i].tm.tm_min, tests[i].tm.tm_sec); + ++errors; + } + } + + if (!errors) + printf ("No errors found.\n"); + return errors != 0; +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/REORG.TODO/time/tst-mktime.c b/REORG.TODO/time/tst-mktime.c new file mode 100644 index 0000000000..c1473600dd --- /dev/null +++ b/REORG.TODO/time/tst-mktime.c @@ -0,0 +1,73 @@ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <time.h> + +static int +do_test (void) +{ + struct tm time_str, *tm; + time_t t; + char daybuf[20]; + int result; + + time_str.tm_year = 2001 - 1900; + time_str.tm_mon = 7 - 1; + time_str.tm_mday = 4; + time_str.tm_hour = 0; + time_str.tm_min = 0; + time_str.tm_sec = 1; + time_str.tm_isdst = -1; + + if (mktime (&time_str) == -1) + { + (void) puts ("-unknown-"); + result = 1; + } + else + { + (void) strftime (daybuf, sizeof (daybuf), "%A", &time_str); + (void) puts (daybuf); + result = strcmp (daybuf, "Wednesday") != 0; + } + + setenv ("TZ", "EST+5", 1); +#define EVENING69 1 * 60 * 60 + 2 * 60 + 29 + t = EVENING69; + tm = localtime (&t); + if (tm == NULL) + { + (void) puts ("localtime returned NULL"); + result = 1; + } + else + { + time_str = *tm; + t = mktime (&time_str); + if (t != EVENING69) + { + printf ("mktime returned %ld, expected %d\n", + (long) t, EVENING69); + result = 1; + } + else + (void) puts ("Dec 31 1969 EST test passed"); + + setenv ("TZ", "CET-1", 1); + t = mktime (&time_str); +#define EVENING69_CET (EVENING69 - (5 - -1) * 60 * 60) + if (t != EVENING69_CET) + { + printf ("mktime returned %ld, expected %ld\n", + (long) t, (long) EVENING69_CET); + result = 1; + } + else + (void) puts ("Dec 31 1969 CET test passed"); + } + + return result; +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/REORG.TODO/time/tst-mktime2.c b/REORG.TODO/time/tst-mktime2.c new file mode 100644 index 0000000000..c41db3e3b7 --- /dev/null +++ b/REORG.TODO/time/tst-mktime2.c @@ -0,0 +1,158 @@ +/* Test program from Paul Eggert and Tony Leneis. */ + +#include <limits.h> +#include <time.h> +#include <stdlib.h> +#include <unistd.h> + +/* True if the arithmetic type T is signed. */ +#define TYPE_SIGNED(t) (! ((t) 0 < (t) -1)) + +/* The maximum and minimum values for the integer type T. These + macros have undefined behavior if T is signed and has padding bits. + If this is a problem for you, please let us know how to fix it for + your host. */ +#define TYPE_MINIMUM(t) \ + ((t) (! TYPE_SIGNED (t) \ + ? (t) 0 \ + : ~ TYPE_MAXIMUM (t))) +#define TYPE_MAXIMUM(t) \ + ((t) (! TYPE_SIGNED (t) \ + ? (t) -1 \ + : ((((t) 1 << (sizeof (t) * CHAR_BIT - 2)) - 1) * 2 + 1))) + +#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 + +/* Values we'll use to set the TZ environment variable. */ +static const char *tz_strings[] = + { + (const char *) 0, "GMT0", "JST-9", + "EST+3EDT+2,M10.1.0/00:00:00,M2.3.0/00:00:00" + }; +#define N_STRINGS ((int) (sizeof (tz_strings) / sizeof (tz_strings[0]))) + +/* Fail if mktime fails to convert a date in the spring-forward gap. + Based on a problem report from Andreas Jaeger. */ +static void +spring_forward_gap (void) +{ + /* glibc (up to about 1998-10-07) failed this test. */ + struct tm tm; + + /* Use the portable POSIX.1 specification "TZ=PST8PDT,M4.1.0,M10.5.0" + instead of "TZ=America/Vancouver" in order to detect the bug even + on systems that don't support the Olson extension, or don't have the + full zoneinfo tables installed. */ + setenv ("TZ", "PST8PDT,M4.1.0,M10.5.0", 1); + + tm.tm_year = 98; + tm.tm_mon = 3; + tm.tm_mday = 5; + tm.tm_hour = 2; + tm.tm_min = 0; + tm.tm_sec = 0; + tm.tm_isdst = -1; + if (mktime (&tm) == (time_t)-1) + exit (1); +} + +static void +mktime_test1 (time_t now) +{ + struct tm *lt = localtime (&now); + if (lt && mktime (lt) != now) + exit (2); +} + +static void +mktime_test (time_t now) +{ + mktime_test1 (now); + mktime_test1 ((time_t) (TIME_T_MAX - now)); + mktime_test1 ((time_t) (TIME_T_MIN + now)); +} + +static void +irix_6_4_bug (void) +{ + /* Based on code from Ariel Faigon. */ + struct tm tm; + tm.tm_year = 96; + tm.tm_mon = 3; + tm.tm_mday = 0; + tm.tm_hour = 0; + tm.tm_min = 0; + tm.tm_sec = 0; + tm.tm_isdst = -1; + mktime (&tm); + if (tm.tm_mon != 2 || tm.tm_mday != 31) + exit (3); +} + +static void +bigtime_test (int j) +{ + struct tm tm; + time_t now; + tm.tm_year = tm.tm_mon = tm.tm_mday = tm.tm_hour = tm.tm_min = tm.tm_sec = j; + tm.tm_isdst = -1; + now = mktime (&tm); + if (now != (time_t) -1) + { + struct tm *lt = localtime (&now); + if (! (lt + && lt->tm_year == tm.tm_year + && lt->tm_mon == tm.tm_mon + && lt->tm_mday == tm.tm_mday + && lt->tm_hour == tm.tm_hour + && lt->tm_min == tm.tm_min + && lt->tm_sec == tm.tm_sec + && lt->tm_yday == tm.tm_yday + && lt->tm_wday == tm.tm_wday + && ((lt->tm_isdst < 0 ? -1 : 0 < lt->tm_isdst) + == (tm.tm_isdst < 0 ? -1 : 0 < tm.tm_isdst)))) + exit (4); + } +} + +static int +do_test (void) +{ + time_t t, delta; + int i; + unsigned int j; + + setenv ("TZ", "America/Sao_Paulo", 1); + /* This test makes some buggy mktime implementations loop. + Give up after 60 seconds; a mktime slower than that + isn't worth using anyway. */ + alarm (60); + + delta = TIME_T_MAX / 997; /* a suitable prime number */ + for (i = 0; i < N_STRINGS; i++) + { + if (tz_strings[i]) + setenv ("TZ", tz_strings[i], 1); + + for (t = 0; t <= TIME_T_MAX - delta; t += delta) + mktime_test (t); + mktime_test ((time_t) 1); + mktime_test ((time_t) (60 * 60)); + mktime_test ((time_t) (60 * 60 * 24)); + + for (j = 1; j <= INT_MAX; j *= 2) + bigtime_test (j); + bigtime_test (j - 1); + } + irix_6_4_bug (); + spring_forward_gap (); + return 0; +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/REORG.TODO/time/tst-mktime3.c b/REORG.TODO/time/tst-mktime3.c new file mode 100644 index 0000000000..c738e5384a --- /dev/null +++ b/REORG.TODO/time/tst-mktime3.c @@ -0,0 +1,53 @@ +/* Test program for mktime bugs with out-of-range tm_sec values. */ + +#include <stdlib.h> +#include <stdio.h> +#include <time.h> + +struct tm tests[] = +{ + { .tm_sec = -1, .tm_mday = 1, .tm_year = 104 }, + { .tm_sec = 65, .tm_min = 59, .tm_hour = 23, .tm_mday = 31, + .tm_mon = 11, .tm_year = 101 } +}; +struct tm expected[] = +{ + { .tm_sec = 59, .tm_min = 59, .tm_hour = 23, .tm_mday = 31, + .tm_mon = 11, .tm_year = 103, .tm_wday = 3, .tm_yday = 364 }, + { .tm_sec = 5, .tm_mday = 1, .tm_year = 102, .tm_wday = 2 } +}; + +static int +do_test (void) +{ + setenv ("TZ", "UTC", 1); + int i; + for (i = 0; i < sizeof (tests) / sizeof (tests[0]); ++i) + { + if (mktime (&tests[i]) < 0) + { + printf ("mktime %d failed\n", i); + return 1; + } +#define CHECK(name) \ + if (tests[i].name != expected[i].name) \ + { \ + printf ("test %d " #name " got %d expected %d\n", \ + i, tests[i].name, expected[i].name); \ + return 1; \ + } + CHECK (tm_sec) + CHECK (tm_min) + CHECK (tm_hour) + CHECK (tm_mday) + CHECK (tm_mon) + CHECK (tm_year) + CHECK (tm_wday) + CHECK (tm_yday) + CHECK (tm_isdst) + } + return 0; +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/REORG.TODO/time/tst-posixtz.c b/REORG.TODO/time/tst-posixtz.c new file mode 100644 index 0000000000..9bec7ae4bb --- /dev/null +++ b/REORG.TODO/time/tst-posixtz.c @@ -0,0 +1,123 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <stdint.h> + +struct +{ + time_t when; + const char *tz; + const char *result; +} tests[] = +{ + { 909312849L, "AEST-10AEDST-11,M10.5.0,M3.5.0", + "1998/10/25 21:54:09 dst=1 zone=AEDST" }, + { 924864849L, "AEST-10AEDST-11,M10.5.0,M3.5.0", + "1999/04/23 20:54:09 dst=0 zone=AEST" }, + { 919973892L, "AEST-10AEDST-11,M10.5.0,M3.5.0", + "1999/02/26 07:18:12 dst=1 zone=AEDST" }, + { 909312849L, "EST+5EDT,M4.1.0/2,M10.5.0/2", + "1998/10/25 05:54:09 dst=0 zone=EST" }, + { 909312849L, "EST5EDT,M4.1.0/2,M10.5.0/2", + "1998/10/25 05:54:09 dst=0 zone=EST" }, + { 909312849L, "<EST5>5EDT,M4.1.0/2,M10.5.0/2", + "1998/10/25 05:54:09 dst=0 zone=EST5" }, + { 924864849L, "EST+5EDT,M4.1.0/2,M10.5.0/2", + "1999/04/23 06:54:09 dst=1 zone=EDT" }, + { 919973892L, "EST+5EDT,M4.1.0/2,M10.5.0/2", + "1999/02/25 15:18:12 dst=0 zone=EST" }, +}; + +static int +do_test (void) +{ + int result = 0; + size_t cnt; + + for (cnt = 0; cnt < sizeof (tests) / sizeof (tests[0]); ++cnt) + { + char buf[100]; + struct tm *tmp; + + printf ("TZ = \"%s\", time = %jd => ", tests[cnt].tz, + (intmax_t) tests[cnt].when); + fflush (stdout); + + setenv ("TZ", tests[cnt].tz, 1); + + tmp = localtime (&tests[cnt].when); + + snprintf (buf, sizeof (buf), + "%04d/%02d/%02d %02d:%02d:%02d dst=%d zone=%s", + tmp->tm_year + 1900, tmp->tm_mon + 1, tmp->tm_mday, + tmp->tm_hour, tmp->tm_min, tmp->tm_sec, tmp->tm_isdst, + tzname[tmp->tm_isdst ? 1 : 0]); + + fputs (buf, stdout); + + if (strcmp (buf, tests[cnt].result) == 0) + puts (", OK"); + else + { + result = 1; + puts (", FAIL"); + } + } + + setenv ("TZ", "Universal", 1); + localtime (&tests[0].when); + printf ("TZ = \"Universal\" daylight %d tzname = { \"%s\", \"%s\" }", + daylight, tzname[0], tzname[1]); + if (! daylight) + puts (", OK"); + else + { + result = 1; + puts (", FAIL"); + } + + setenv ("TZ", "AEST-10AEDST-11,M10.5.0,M3.5.0", 1); + tzset (); + printf ("TZ = \"AEST-10AEDST-11,M10.5.0,M3.5.0\" daylight %d" + " tzname = { \"%s\", \"%s\" }", daylight, tzname[0], tzname[1]); + if (daylight + && strcmp (tzname[0], "AEST") == 0 && strcmp (tzname[1], "AEDST") == 0) + puts (", OK"); + else + { + result = 1; + puts (", FAIL"); + } + + setenv ("TZ", "<AB1>-10<AB2>-11,M10.5.0,M3.5.0", 1); + tzset (); + printf ("TZ = \"<AB1>-10<AB2>-11,M10.5.0,M3.5.0\" daylight %d" + " tzname = { \"%s\", \"%s\" }", daylight, tzname[0], tzname[1]); + if (daylight + && strcmp (tzname[0], "AB1") == 0 && strcmp (tzname[1], "AB2") == 0) + puts (", OK"); + else + { + result = 1; + puts (", FAIL"); + } + + setenv ("TZ", "<BB1>-10", 1); + tzset (); + printf ("TZ = \"<BB1>-10\" daylight %d" + " tzname = { \"%s\", \"%s\" }", daylight, tzname[0], tzname[1]); + if (daylight == 0 + && strcmp (tzname[0], "BB1") == 0 && strcmp (tzname[1], "BB1") == 0) + puts (", OK"); + else + { + result = 1; + puts (", FAIL"); + } + + return result; +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/REORG.TODO/time/tst-strftime.c b/REORG.TODO/time/tst-strftime.c new file mode 100644 index 0000000000..af3ff72faf --- /dev/null +++ b/REORG.TODO/time/tst-strftime.c @@ -0,0 +1,161 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + + +static int +do_bz18985 (void) +{ + char buf[1000]; + struct tm ttm; + int rc, ret = 0; + + memset (&ttm, 1, sizeof (ttm)); + ttm.tm_zone = NULL; /* Dereferenced directly if non-NULL. */ + rc = strftime (buf, sizeof (buf), "%a %A %b %B %c %z %Z", &ttm); + + if (rc == 66) + { + const char expected[] + = "? ? ? ? ? ? 16843009 16843009:16843009:16843009 16844909 +467836 ?"; + if (0 != strcmp (buf, expected)) + { + printf ("expected:\n %s\ngot:\n %s\n", expected, buf); + ret += 1; + } + } + else + { + printf ("expected 66, got %d\n", rc); + ret += 1; + } + + /* Check negative values as well. */ + memset (&ttm, 0xFF, sizeof (ttm)); + ttm.tm_zone = NULL; /* Dereferenced directly if non-NULL. */ + rc = strftime (buf, sizeof (buf), "%a %A %b %B %c %z %Z", &ttm); + + if (rc == 30) + { + const char expected[] = "? ? ? ? ? ? -1 -1:-1:-1 1899 "; + if (0 != strcmp (buf, expected)) + { + printf ("expected:\n %s\ngot:\n %s\n", expected, buf); + ret += 1; + } + } + else + { + printf ("expected 30, got %d\n", rc); + ret += 1; + } + + return ret; +} + +static struct +{ + const char *fmt; + size_t min; + size_t max; +} tests[] = + { + { "%2000Y", 2000, 4000 }, + { "%02000Y", 2000, 4000 }, + { "%_2000Y", 2000, 4000 }, + { "%-2000Y", 2000, 4000 }, + }; +#define ntests (sizeof (tests) / sizeof (tests[0])) + + +static int +do_test (void) +{ + size_t cnt; + int result = 0; + + time_t tnow = time (NULL); + struct tm *now = localtime (&tnow); + + for (cnt = 0; cnt < ntests; ++cnt) + { + size_t size = 0; + int res; + char *buf = NULL; + + do + { + size += 500; + buf = (char *) realloc (buf, size); + if (buf == NULL) + { + puts ("out of memory"); + exit (1); + } + + res = strftime (buf, size, tests[cnt].fmt, now); + if (res != 0) + break; + } + while (size < tests[cnt].max); + + if (res == 0) + { + printf ("%Zu: %s: res == 0 despite size == %Zu\n", + cnt, tests[cnt].fmt, size); + result = 1; + } + else if (size < tests[cnt].min) + { + printf ("%Zu: %s: size == %Zu was enough\n", + cnt, tests[cnt].fmt, size); + result = 1; + } + else + printf ("%Zu: %s: size == %Zu: OK\n", cnt, tests[cnt].fmt, size); + + free (buf); + } + + struct tm ttm = + { + /* Initialize the fields which are needed in the tests. */ + .tm_mday = 1, + .tm_hour = 2 + }; + const struct + { + const char *fmt; + const char *exp; + size_t n; + } ftests[] = + { + { "%-e", "1", 1 }, + { "%-k", "2", 1 }, + { "%-l", "2", 1 }, + }; +#define nftests (sizeof (ftests) / sizeof (ftests[0])) + for (cnt = 0; cnt < nftests; ++cnt) + { + char buf[100]; + size_t r = strftime (buf, sizeof (buf), ftests[cnt].fmt, &ttm); + if (r != ftests[cnt].n) + { + printf ("strftime(\"%s\") returned %zu not %zu\n", + ftests[cnt].fmt, r, ftests[cnt].n); + result = 1; + } + if (strcmp (buf, ftests[cnt].exp) != 0) + { + printf ("strftime(\"%s\") produced \"%s\" not \"%s\"\n", + ftests[cnt].fmt, buf, ftests[cnt].exp); + result = 1; + } + } + + return result + do_bz18985 (); +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/REORG.TODO/time/tst-strptime-whitespace.c b/REORG.TODO/time/tst-strptime-whitespace.c new file mode 100644 index 0000000000..90d4da80ca --- /dev/null +++ b/REORG.TODO/time/tst-strptime-whitespace.c @@ -0,0 +1,43 @@ +/* Verify that strptime accepts arbitrary whitespace between tokens. + + Copyright (C) 2013-2017 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 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, see + <http://www.gnu.org/licenses/>. */ + +#define _XOPEN_SOURCE +#include <time.h> +#include <stdio.h> +#include <string.h> + +int +do_test (void) +{ + struct tm t; + const char *in = "Tuesday \t 22 \t July\t1942"; + + char *r = strptime (in, "%A%d %b%Y", &t); + + if (r == NULL || r != in + strlen (in)) + { + printf ("strptime failed\n"); + return 1; + } + + return 0; +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/REORG.TODO/time/tst-strptime.c b/REORG.TODO/time/tst-strptime.c new file mode 100644 index 0000000000..4e9e38c8f3 --- /dev/null +++ b/REORG.TODO/time/tst-strptime.c @@ -0,0 +1,199 @@ +/* Test for strptime. + Copyright (C) 1998-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998. + + 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, see + <http://www.gnu.org/licenses/>. */ + +#include <locale.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + + +static const struct +{ + const char *locale; + const char *input; + const char *format; + int wday; + int yday; + int mon; + int mday; +} day_tests[] = +{ + { "C", "2000-01-01", "%Y-%m-%d", 6, 0, 0, 1 }, + { "C", "03/03/00", "%D", 5, 62, 2, 3 }, + { "C", "9/9/99", "%x", 4, 251, 8, 9 }, + { "C", "19990502123412", "%Y%m%d%H%M%S", 0, 121, 4, 2 }, + { "C", "2001 20 Mon", "%Y %U %a", 1, 140, 4, 21 }, + { "C", "2001 21 Mon", "%Y %W %a", 1, 140, 4, 21 }, + { "C", "2001 21 Mon", "%2000Y %W %a", 1, 140, 4, 21 }, + { "C", "2001 21 Mon", "%^Y %W %a", 1, 140, 4, 21 }, + { "C", "2001 EST 21 Mon", "%Y %Z %W %a", 1, 140, 4, 21 }, + { "C", "2012 00 Sun", "%Y %W %a", 0, 0, 0, 1 }, + { "ja_JP.EUC-JP", "2000-01-01 08:12:21 AM", "%Y-%m-%d %I:%M:%S %p", + 6, 0, 0, 1 }, + { "en_US.ISO-8859-1", "2000-01-01 08:12:21 PM", "%Y-%m-%d %I:%M:%S %p", + 6, 0, 0, 1 }, + { "ja_JP.EUC-JP", "2001 20 \xb7\xee", "%Y %U %a", 1, 140, 4, 21 }, + { "ja_JP.EUC-JP", "2001 21 \xb7\xee", "%Y %W %a", 1, 140, 4, 21 }, +}; + + +static const struct +{ + const char *input; + const char *format; + const char *output; + int wday; + int yday; +} tm_tests [] = +{ + {"17410105012000", "%H%M%S%d%m%Y", "2000-01-05 17:41:01", 3, 4} +}; + + + +static int +test_tm (void) +{ + struct tm tm; + size_t i; + int result = 0; + char buf[100]; + + for (i = 0; i < sizeof (tm_tests) / sizeof (tm_tests[0]); ++i) + { + memset (&tm, '\0', sizeof (tm)); + + char *ret = strptime (tm_tests[i].input, tm_tests[i].format, &tm); + if (ret == NULL) + { + printf ("strptime returned NULL for `%s'\n", tm_tests[i].input); + result = 1; + continue; + } + else if (*ret != '\0') + { + printf ("not all of `%s' read\n", tm_tests[i].input); + result = 1; + } + strftime (buf, sizeof (buf), "%F %T", &tm); + printf ("strptime (\"%s\", \"%s\", ...)\n" + "\tshould be: %s, wday = %d, yday = %3d\n" + "\t is: %s, wday = %d, yday = %3d\n", + tm_tests[i].input, tm_tests[i].format, + tm_tests[i].output, + tm_tests[i].wday, tm_tests[i].yday, + buf, tm.tm_wday, tm.tm_yday); + + if (strcmp (buf, tm_tests[i].output) != 0) + { + printf ("Time and date are not correct.\n"); + result = 1; + } + if (tm.tm_wday != tm_tests[i].wday) + { + printf ("weekday for `%s' incorrect: %d instead of %d\n", + tm_tests[i].input, tm.tm_wday, tm_tests[i].wday); + result = 1; + } + if (tm.tm_yday != tm_tests[i].yday) + { + printf ("yearday for `%s' incorrect: %d instead of %d\n", + tm_tests[i].input, tm.tm_yday, tm_tests[i].yday); + result = 1; + } + } + + return result; +} + + +static int +do_test (void) +{ + struct tm tm; + size_t i; + int result = 0; + + for (i = 0; i < sizeof (day_tests) / sizeof (day_tests[0]); ++i) + { + memset (&tm, '\0', sizeof (tm)); + + if (setlocale (LC_ALL, day_tests[i].locale) == NULL) + { + printf ("cannot set locale %s: %m\n", day_tests[i].locale); + exit (EXIT_FAILURE); + } + + char *ret = strptime (day_tests[i].input, day_tests[i].format, &tm); + if (ret == NULL) + { + printf ("strptime returned NULL for `%s'\n", day_tests[i].input); + result = 1; + continue; + } + else if (*ret != '\0') + { + printf ("not all of `%s' read\n", day_tests[i].input); + result = 1; + } + + printf ("strptime (\"%s\", \"%s\", ...)\n" + "\tshould be: wday = %d, yday = %3d, mon = %2d, mday = %2d\n" + "\t is: wday = %d, yday = %3d, mon = %2d, mday = %2d\n", + day_tests[i].input, day_tests[i].format, + day_tests[i].wday, day_tests[i].yday, + day_tests[i].mon, day_tests[i].mday, + tm.tm_wday, tm.tm_yday, tm.tm_mon, tm.tm_mday); + + if (tm.tm_wday != day_tests[i].wday) + { + printf ("weekday for `%s' incorrect: %d instead of %d\n", + day_tests[i].input, tm.tm_wday, day_tests[i].wday); + result = 1; + } + if (tm.tm_yday != day_tests[i].yday) + { + printf ("yearday for `%s' incorrect: %d instead of %d\n", + day_tests[i].input, tm.tm_yday, day_tests[i].yday); + result = 1; + } + if (tm.tm_mon != day_tests[i].mon) + { + printf ("month for `%s' incorrect: %d instead of %d\n", + day_tests[i].input, tm.tm_mon, day_tests[i].mon); + result = 1; + } + if (tm.tm_mday != day_tests[i].mday) + { + printf ("monthday for `%s' incorrect: %d instead of %d\n", + day_tests[i].input, tm.tm_mday, day_tests[i].mday); + result = 1; + } + } + + setlocale (LC_ALL, "C"); + + result |= test_tm (); + + return result; +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/REORG.TODO/time/tst-strptime2.c b/REORG.TODO/time/tst-strptime2.c new file mode 100644 index 0000000000..8019e7f5d8 --- /dev/null +++ b/REORG.TODO/time/tst-strptime2.c @@ -0,0 +1,235 @@ +/* tst-strptime2 - Test strptime %z timezone offset specifier. */ + +#include <limits.h> +#include <stdbool.h> +#include <stdio.h> +#include <time.h> +#include <libc-diag.h> + +/* Dummy string is used to match strptime's %s specifier. */ + +static const char dummy_string[] = "1113472456"; + +/* buffer_size contains the maximum test string length, including + trailing NUL. */ + +enum +{ + buffer_size = 20, +}; + +/* Verbose execution, set with --verbose command line option. */ + +static bool verbose; + + +/* mkbuf - Write a test string for strptime with the specified time + value and number of digits into the supplied buffer, and return + the expected strptime test result. + + The test string, buf, is written with the following content: + a dummy string matching strptime "%s" format specifier, + whitespace matching strptime " " format specifier, and + timezone string matching strptime "%z" format specifier. + + Note that a valid timezone string is either "Z" or contains the + following fields: + Sign field consisting of a '+' or '-' sign, + Hours field in two decimal digits, and + optional Minutes field in two decimal digits. Optionally, + a ':' is used to seperate hours and minutes. + + This function may write test strings with minutes values outside + the valid range 00-59. These are invalid strings and useful for + testing strptime's rejection of invalid strings. + + The ndigits parameter is used to limit the number of timezone + string digits to be written and may range from 0 to 4. Note that + only 2 and 4 digit strings are valid input to strptime; strings + with 0, 1 or 3 digits are invalid and useful for testing strptime's + rejection of invalid strings. + + This function returns the behavior expected of strptime resulting + from parsing the the test string. For valid strings, the function + returns the expected tm_gmtoff value. For invalid strings, + LONG_MAX is returned. LONG_MAX indicates the expectation that + strptime will return NULL; for example, if the number of digits + are not correct, or minutes part of the time is outside the valid + range of 00 to 59. */ + +static long int +mkbuf (char *buf, bool neg, bool colon, unsigned int hhmm, size_t ndigits) +{ + const int mm_max = 59; + char sign = neg ? '-' : '+'; + int i; + unsigned int hh = hhmm / 100; + unsigned int mm = hhmm % 100; + long int expect = LONG_MAX; + + i = sprintf (buf, "%s %c", dummy_string, sign); +#if __GNUC_PREREQ (7, 0) + /* GCC issues a warning when it thinks the snprintf buffer may be too short. + This test is explicitly using short buffers to force snprintf to truncate + the output so we ignore the warnings. */ + DIAG_PUSH_NEEDS_COMMENT; + DIAG_IGNORE_NEEDS_COMMENT (7.0, "-Wformat-truncation"); +#endif + if (colon) + snprintf (buf + i, ndigits + 2, "%02u:%02u", hh, mm); + else + snprintf (buf + i, ndigits + 1, "%04u", hhmm); +#if __GNUC_PREREQ (7, 0) + DIAG_POP_NEEDS_COMMENT; +#endif + + if (mm <= mm_max && (ndigits == 2 || ndigits == 4)) + { + long int tm_gmtoff = hh * 3600 + mm * 60; + + expect = neg ? -tm_gmtoff : tm_gmtoff; + } + + return expect; +} + + +/* Write a description of expected or actual test result to stdout. */ + +static void +describe (bool string_valid, long int tm_gmtoff) +{ + if (string_valid) + printf ("valid, tm.tm_gmtoff %ld", tm_gmtoff); + else + printf ("invalid, return value NULL"); +} + + +/* Using buffer buf, run strptime. Compare results against expect, + the expected result. Report failures and verbose results to stdout. + Update the result counts. Return 1 if test failed, 0 if passed. */ + +static int +compare (const char *buf, long int expect, unsigned int *nresult) +{ + struct tm tm; + char *p; + bool test_string_valid; + long int test_result; + bool fail; + int result; + + p = strptime (buf, "%s %z", &tm); + test_string_valid = p != NULL; + test_result = test_string_valid ? tm.tm_gmtoff : LONG_MAX; + fail = test_result != expect; + + if (fail || verbose) + { + bool expect_string_valid = expect != LONG_MAX; + + printf ("%s: input \"%s\", expected: ", fail ? "FAIL" : "PASS", buf); + describe (expect_string_valid, expect); + + if (fail) + { + printf (", got: "); + describe (test_string_valid, test_result); + } + + printf ("\n"); + } + + result = fail ? 1 : 0; + nresult[result]++; + + return result; +} + + +static int +do_test (void) +{ + char buf[buffer_size]; + long int expect; + int result = 0; + /* Number of tests run with passing (index==0) and failing (index==1) + results. */ + unsigned int nresult[2]; + unsigned int ndigits; + unsigned int step; + unsigned int hhmm; + + nresult[0] = 0; + nresult[1] = 0; + + /* Create and test input string with no sign and four digits input + (invalid format). */ + + sprintf (buf, "%s 1030", dummy_string); + expect = LONG_MAX; + result |= compare (buf, expect, nresult); + + /* Create and test input string with "Z" input (valid format). + Expect tm_gmtoff of 0. */ + + sprintf (buf, "%s Z", dummy_string); + expect = 0; + result |= compare (buf, expect, nresult); + + /* Create and test input strings with sign and digits: + 0 digits (invalid format), + 1 digit (invalid format), + 2 digits (valid format), + 3 digits (invalid format), + 4 digits (valid format if and only if minutes is in range 00-59, + otherwise invalid). + If format is valid, the returned tm_gmtoff is checked. */ + + for (ndigits = 0, step = 10000; ndigits <= 4; ndigits++, step /= 10) + for (hhmm = 0; hhmm <= 9999; hhmm += step) + { + /* Test both positive and negative signs. */ + + expect = mkbuf (buf, false, false, hhmm, ndigits); + result |= compare (buf, expect, nresult); + + expect = mkbuf (buf, true, false, hhmm, ndigits); + result |= compare (buf, expect, nresult); + + /* Test with colon as well. */ + + if (ndigits >= 3) + { + expect = mkbuf (buf, false, true, hhmm, ndigits); + result |= compare (buf, expect, nresult); + + expect = mkbuf (buf, true, true, hhmm, ndigits); + result |= compare (buf, expect, nresult); + } + } + + if (result > 0 || verbose) + printf ("%s: %u input strings: %u fail, %u pass\n", + result > 0 ? "FAIL" : "PASS", + nresult[1] + nresult[0], nresult[1], nresult[0]); + + return result; +} + + +/* Add a "--verbose" command line option to test-skeleton.c. */ + +#define OPT_VERBOSE 10000 + +#define CMDLINE_OPTIONS \ + { "verbose", no_argument, NULL, OPT_VERBOSE, }, + +#define CMDLINE_PROCESS \ + case OPT_VERBOSE: \ + verbose = true; \ + break; + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/REORG.TODO/time/tst-strptime3.c b/REORG.TODO/time/tst-strptime3.c new file mode 100644 index 0000000000..d53f51ee22 --- /dev/null +++ b/REORG.TODO/time/tst-strptime3.c @@ -0,0 +1,55 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + + +static int +do_test (void) +{ + int result = 0; + struct tm tm; + + memset (&tm, 0xaa, sizeof (tm)); + + /* Test we don't crash on uninitialized struct tm. + Some fields might contain bogus values until everything + needed is initialized, but we shouldn't crash. */ + if (strptime ("2007", "%Y", &tm) == NULL + || strptime ("12", "%d", &tm) == NULL + || strptime ("Feb", "%b", &tm) == NULL + || strptime ("13", "%M", &tm) == NULL + || strptime ("21", "%S", &tm) == NULL + || strptime ("16", "%H", &tm) == NULL) + { + puts ("strptimes failed"); + result = 1; + } + + if (tm.tm_sec != 21 || tm.tm_min != 13 || tm.tm_hour != 16 + || tm.tm_mday != 12 || tm.tm_mon != 1 || tm.tm_year != 107 + || tm.tm_wday != 1 || tm.tm_yday != 42) + { + puts ("unexpected tm content"); + result = 1; + } + + if (strptime ("8", "%d", &tm) == NULL) + { + puts ("strptime failed"); + result = 1; + } + + if (tm.tm_sec != 21 || tm.tm_min != 13 || tm.tm_hour != 16 + || tm.tm_mday != 8 || tm.tm_mon != 1 || tm.tm_year != 107 + || tm.tm_wday != 4 || tm.tm_yday != 38) + { + puts ("unexpected tm content"); + result = 1; + } + + return result; +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/REORG.TODO/time/tst-tzname.c b/REORG.TODO/time/tst-tzname.c new file mode 100644 index 0000000000..f0ce13f835 --- /dev/null +++ b/REORG.TODO/time/tst-tzname.c @@ -0,0 +1,50 @@ +/* Test that tzset sets tzname correctly (BZ #19253). + Copyright (C) 2015-2017 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 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, see + <http://www.gnu.org/licenses/>. */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +static int +do_test (void) +{ + int result = 0; + + setenv ("TZ", TZDEFRULES, 1); + tzset (); + const char *stdtz = strdup (tzname[0]); + setenv ("TZ", "STD-1DST", 1); + tzset (); + if (strcmp (tzname[0], "STD") != 0) + { + printf ("FAIL: TZ=STD-1DST, tzname[0] = %s\n", tzname[0]); + result = 1; + } + setenv ("TZ", TZDEFRULES, 1); + tzset (); + if (strcmp (tzname[0], stdtz) != 0) + { + printf ("FAIL: TZ=%s, tzname[0] = %s\n", TZDEFRULES, tzname[0]); + result = 1; + } + return result; +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/REORG.TODO/time/tst_wcsftime.c b/REORG.TODO/time/tst_wcsftime.c new file mode 100644 index 0000000000..3f6f0d9f77 --- /dev/null +++ b/REORG.TODO/time/tst_wcsftime.c @@ -0,0 +1,28 @@ +#include <time.h> +#include <wchar.h> + +int +main (int argc, char *argv[]) +{ + wchar_t buf[200]; + time_t t; + struct tm *tp; + int result = 0; + size_t n; + + time (&t); + tp = gmtime (&t); + + n = wcsftime (buf, sizeof (buf) / sizeof (buf[0]), + L"%H:%M:%S %Y-%m-%d\n", tp); + if (n != 21) + result = 1; + + wprintf (L"It is now %ls", buf); + + wcsftime (buf, sizeof (buf) / sizeof (buf[0]), L"%A\n", tp); + + wprintf (L"The weekday is %ls", buf); + + return result; +} diff --git a/REORG.TODO/time/tzfile.c b/REORG.TODO/time/tzfile.c new file mode 100644 index 0000000000..d41246980b --- /dev/null +++ b/REORG.TODO/time/tzfile.c @@ -0,0 +1,832 @@ +/* Copyright (C) 1991-2017 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 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, see + <http://www.gnu.org/licenses/>. */ + +#include <assert.h> +#include <limits.h> +#include <stdio.h> +#include <stdio_ext.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <sys/stat.h> +#include <stdint.h> + +#include <timezone/tzfile.h> + +int __use_tzfile; +static dev_t tzfile_dev; +static ino64_t tzfile_ino; +static time_t tzfile_mtime; + +struct ttinfo + { + long int offset; /* Seconds east of GMT. */ + unsigned char isdst; /* Used to set tm_isdst. */ + unsigned char idx; /* Index into `zone_names'. */ + unsigned char isstd; /* Transition times are in standard time. */ + unsigned char isgmt; /* Transition times are in GMT. */ + }; + +struct leap + { + time_t transition; /* Time the transition takes effect. */ + long int change; /* Seconds of correction to apply. */ + }; + +static size_t num_transitions; +libc_freeres_ptr (static time_t *transitions); +static unsigned char *type_idxs; +static size_t num_types; +static struct ttinfo *types; +static char *zone_names; +static long int rule_stdoff; +static long int rule_dstoff; +static size_t num_leaps; +static struct leap *leaps; +static char *tzspec; + +#include <endian.h> +#include <byteswap.h> + +/* Decode the four bytes at PTR as a signed integer in network byte order. */ +static inline int +__attribute ((always_inline)) +decode (const void *ptr) +{ + if (BYTE_ORDER == BIG_ENDIAN && sizeof (int) == 4) + return *(const int *) ptr; + if (sizeof (int) == 4) + return bswap_32 (*(const int *) ptr); + + const unsigned char *p = ptr; + int result = *p & (1 << (CHAR_BIT - 1)) ? ~0 : 0; + + result = (result << 8) | *p++; + result = (result << 8) | *p++; + result = (result << 8) | *p++; + result = (result << 8) | *p++; + + return result; +} + + +static inline int64_t +__attribute ((always_inline)) +decode64 (const void *ptr) +{ + if ((BYTE_ORDER == BIG_ENDIAN)) + return *(const int64_t *) ptr; + + return bswap_64 (*(const int64_t *) ptr); +} + + +void +__tzfile_read (const char *file, size_t extra, char **extrap) +{ + static const char default_tzdir[] = TZDIR; + size_t num_isstd, num_isgmt; + FILE *f; + struct tzhead tzhead; + size_t chars; + size_t i; + size_t total_size; + size_t types_idx; + size_t leaps_idx; + int was_using_tzfile = __use_tzfile; + int trans_width = 4; + size_t tzspec_len; + char *new = NULL; + + if (sizeof (time_t) != 4 && sizeof (time_t) != 8) + abort (); + + __use_tzfile = 0; + + if (file == NULL) + /* No user specification; use the site-wide default. */ + file = TZDEFAULT; + else if (*file == '\0') + /* User specified the empty string; use UTC with no leap seconds. */ + goto ret_free_transitions; + else + { + /* We must not allow to read an arbitrary file in a setuid + program. So we fail for any file which is not in the + directory hierachy starting at TZDIR + and which is not the system wide default TZDEFAULT. */ + if (__libc_enable_secure + && ((*file == '/' + && memcmp (file, TZDEFAULT, sizeof TZDEFAULT) + && memcmp (file, default_tzdir, sizeof (default_tzdir) - 1)) + || strstr (file, "../") != NULL)) + /* This test is certainly a bit too restrictive but it should + catch all critical cases. */ + goto ret_free_transitions; + } + + if (*file != '/') + { + const char *tzdir; + + tzdir = getenv ("TZDIR"); + if (tzdir == NULL || *tzdir == '\0') + tzdir = default_tzdir; + if (__asprintf (&new, "%s/%s", tzdir, file) == -1) + goto ret_free_transitions; + file = new; + } + + /* If we were already using tzfile, check whether the file changed. */ + struct stat64 st; + if (was_using_tzfile + && stat64 (file, &st) == 0 + && tzfile_ino == st.st_ino && tzfile_dev == st.st_dev + && tzfile_mtime == st.st_mtime) + goto done; /* Nothing to do. */ + + /* Note the file is opened with cancellation in the I/O functions + disabled and if available FD_CLOEXEC set. */ + f = fopen (file, "rce"); + if (f == NULL) + goto ret_free_transitions; + + /* Get information about the file we are actually using. */ + if (fstat64 (__fileno (f), &st) != 0) + { + fclose (f); + goto ret_free_transitions; + } + + free ((void *) transitions); + transitions = NULL; + + /* Remember the inode and device number and modification time. */ + tzfile_dev = st.st_dev; + tzfile_ino = st.st_ino; + tzfile_mtime = st.st_mtime; + + /* No threads reading this stream. */ + __fsetlocking (f, FSETLOCKING_BYCALLER); + + read_again: + if (__builtin_expect (__fread_unlocked ((void *) &tzhead, sizeof (tzhead), + 1, f) != 1, 0) + || memcmp (tzhead.tzh_magic, TZ_MAGIC, sizeof (tzhead.tzh_magic)) != 0) + goto lose; + + num_transitions = (size_t) decode (tzhead.tzh_timecnt); + num_types = (size_t) decode (tzhead.tzh_typecnt); + chars = (size_t) decode (tzhead.tzh_charcnt); + num_leaps = (size_t) decode (tzhead.tzh_leapcnt); + num_isstd = (size_t) decode (tzhead.tzh_ttisstdcnt); + num_isgmt = (size_t) decode (tzhead.tzh_ttisgmtcnt); + + if (__glibc_unlikely (num_isstd > num_types || num_isgmt > num_types)) + goto lose; + + /* For platforms with 64-bit time_t we use the new format if available. */ + if (sizeof (time_t) == 8 && trans_width == 4 + && tzhead.tzh_version[0] != '\0') + { + /* We use the 8-byte format. */ + trans_width = 8; + + /* Position the stream before the second header. */ + size_t to_skip = (num_transitions * (4 + 1) + + num_types * 6 + + chars + + num_leaps * 8 + + num_isstd + + num_isgmt); + if (fseek (f, to_skip, SEEK_CUR) != 0) + goto lose; + + goto read_again; + } + + if (__builtin_expect (num_transitions + > ((SIZE_MAX - (__alignof__ (struct ttinfo) - 1)) + / (sizeof (time_t) + 1)), 0)) + goto lose; + total_size = num_transitions * (sizeof (time_t) + 1); + total_size = ((total_size + __alignof__ (struct ttinfo) - 1) + & ~(__alignof__ (struct ttinfo) - 1)); + types_idx = total_size; + if (__builtin_expect (num_types + > (SIZE_MAX - total_size) / sizeof (struct ttinfo), 0)) + goto lose; + total_size += num_types * sizeof (struct ttinfo); + if (__glibc_unlikely (chars > SIZE_MAX - total_size)) + goto lose; + total_size += chars; + if (__builtin_expect (__alignof__ (struct leap) - 1 + > SIZE_MAX - total_size, 0)) + goto lose; + total_size = ((total_size + __alignof__ (struct leap) - 1) + & ~(__alignof__ (struct leap) - 1)); + leaps_idx = total_size; + if (__builtin_expect (num_leaps + > (SIZE_MAX - total_size) / sizeof (struct leap), 0)) + goto lose; + total_size += num_leaps * sizeof (struct leap); + tzspec_len = 0; + if (sizeof (time_t) == 8 && trans_width == 8) + { + off_t rem = st.st_size - __ftello (f); + if (__builtin_expect (rem < 0 + || (size_t) rem < (num_transitions * (8 + 1) + + num_types * 6 + + chars), 0)) + goto lose; + tzspec_len = (size_t) rem - (num_transitions * (8 + 1) + + num_types * 6 + + chars); + if (__builtin_expect (num_leaps > SIZE_MAX / 12 + || tzspec_len < num_leaps * 12, 0)) + goto lose; + tzspec_len -= num_leaps * 12; + if (__glibc_unlikely (tzspec_len < num_isstd)) + goto lose; + tzspec_len -= num_isstd; + if (__glibc_unlikely (tzspec_len == 0 || tzspec_len - 1 < num_isgmt)) + goto lose; + tzspec_len -= num_isgmt + 1; + if (__glibc_unlikely (tzspec_len == 0 + || SIZE_MAX - total_size < tzspec_len)) + goto lose; + } + if (__glibc_unlikely (SIZE_MAX - total_size - tzspec_len < extra)) + goto lose; + + /* Allocate enough memory including the extra block requested by the + caller. */ + transitions = (time_t *) malloc (total_size + tzspec_len + extra); + if (transitions == NULL) + goto lose; + + type_idxs = (unsigned char *) transitions + (num_transitions + * sizeof (time_t)); + types = (struct ttinfo *) ((char *) transitions + types_idx); + zone_names = (char *) types + num_types * sizeof (struct ttinfo); + leaps = (struct leap *) ((char *) transitions + leaps_idx); + if (sizeof (time_t) == 8 && trans_width == 8) + tzspec = (char *) leaps + num_leaps * sizeof (struct leap) + extra; + else + tzspec = NULL; + if (extra > 0) + *extrap = (char *) &leaps[num_leaps]; + + if (sizeof (time_t) == 4 || __builtin_expect (trans_width == 8, 1)) + { + if (__builtin_expect (__fread_unlocked (transitions, trans_width + 1, + num_transitions, f) + != num_transitions, 0)) + goto lose; + } + else + { + if (__builtin_expect (__fread_unlocked (transitions, 4, + num_transitions, f) + != num_transitions, 0) + || __builtin_expect (__fread_unlocked (type_idxs, 1, num_transitions, + f) != num_transitions, 0)) + goto lose; + } + + /* Check for bogus indices in the data file, so we can hereafter + safely use type_idxs[T] as indices into `types' and never crash. */ + for (i = 0; i < num_transitions; ++i) + if (__glibc_unlikely (type_idxs[i] >= num_types)) + goto lose; + + if ((BYTE_ORDER != BIG_ENDIAN && (sizeof (time_t) == 4 || trans_width == 4)) + || (BYTE_ORDER == BIG_ENDIAN && sizeof (time_t) == 8 + && trans_width == 4)) + { + /* Decode the transition times, stored as 4-byte integers in + network (big-endian) byte order. We work from the end of + the array so as not to clobber the next element to be + processed when sizeof (time_t) > 4. */ + i = num_transitions; + while (i-- > 0) + transitions[i] = decode ((char *) transitions + i * 4); + } + else if (BYTE_ORDER != BIG_ENDIAN && sizeof (time_t) == 8) + { + /* Decode the transition times, stored as 8-byte integers in + network (big-endian) byte order. */ + for (i = 0; i < num_transitions; ++i) + transitions[i] = decode64 ((char *) transitions + i * 8); + } + + for (i = 0; i < num_types; ++i) + { + unsigned char x[4]; + int c; + if (__builtin_expect (__fread_unlocked (x, 1, + sizeof (x), f) != sizeof (x), + 0)) + goto lose; + c = getc_unlocked (f); + if (__glibc_unlikely ((unsigned int) c > 1u)) + goto lose; + types[i].isdst = c; + c = getc_unlocked (f); + if (__glibc_unlikely ((size_t) c > chars)) + /* Bogus index in data file. */ + goto lose; + types[i].idx = c; + types[i].offset = (long int) decode (x); + } + + if (__glibc_unlikely (__fread_unlocked (zone_names, 1, chars, f) != chars)) + goto lose; + + for (i = 0; i < num_leaps; ++i) + { + unsigned char x[8]; + if (__builtin_expect (__fread_unlocked (x, 1, trans_width, f) + != trans_width, 0)) + goto lose; + if (sizeof (time_t) == 4 || trans_width == 4) + leaps[i].transition = (time_t) decode (x); + else + leaps[i].transition = (time_t) decode64 (x); + + if (__glibc_unlikely (__fread_unlocked (x, 1, 4, f) != 4)) + goto lose; + leaps[i].change = (long int) decode (x); + } + + for (i = 0; i < num_isstd; ++i) + { + int c = getc_unlocked (f); + if (__glibc_unlikely (c == EOF)) + goto lose; + types[i].isstd = c != 0; + } + while (i < num_types) + types[i++].isstd = 0; + + for (i = 0; i < num_isgmt; ++i) + { + int c = getc_unlocked (f); + if (__glibc_unlikely (c == EOF)) + goto lose; + types[i].isgmt = c != 0; + } + while (i < num_types) + types[i++].isgmt = 0; + + /* Read the POSIX TZ-style information if possible. */ + if (sizeof (time_t) == 8 && tzspec != NULL) + { + /* Skip over the newline first. */ + if (getc_unlocked (f) != '\n' + || (__fread_unlocked (tzspec, 1, tzspec_len - 1, f) + != tzspec_len - 1)) + tzspec = NULL; + else + tzspec[tzspec_len - 1] = '\0'; + } + else if (sizeof (time_t) == 4 && tzhead.tzh_version[0] != '\0') + { + /* Get the TZ string. */ + if (__builtin_expect (__fread_unlocked ((void *) &tzhead, + sizeof (tzhead), 1, f) != 1, 0) + || (memcmp (tzhead.tzh_magic, TZ_MAGIC, sizeof (tzhead.tzh_magic)) + != 0)) + goto lose; + + size_t num_transitions2 = (size_t) decode (tzhead.tzh_timecnt); + size_t num_types2 = (size_t) decode (tzhead.tzh_typecnt); + size_t chars2 = (size_t) decode (tzhead.tzh_charcnt); + size_t num_leaps2 = (size_t) decode (tzhead.tzh_leapcnt); + size_t num_isstd2 = (size_t) decode (tzhead.tzh_ttisstdcnt); + size_t num_isgmt2 = (size_t) decode (tzhead.tzh_ttisgmtcnt); + + /* Position the stream before the second header. */ + size_t to_skip = (num_transitions2 * (8 + 1) + + num_types2 * 6 + + chars2 + + num_leaps2 * 12 + + num_isstd2 + + num_isgmt2); + off_t off; + if (fseek (f, to_skip, SEEK_CUR) != 0 + || (off = __ftello (f)) < 0 + || st.st_size < off + 2) + goto lose; + + tzspec_len = st.st_size - off - 1; + if (tzspec_len == 0) + goto lose; + char *tzstr = malloc (tzspec_len); + if (tzstr == NULL) + goto lose; + if (getc_unlocked (f) != '\n' + || (__fread_unlocked (tzstr, 1, tzspec_len - 1, f) + != tzspec_len - 1)) + { + free (tzstr); + goto lose; + } + tzstr[tzspec_len - 1] = '\0'; + tzspec = __tzstring (tzstr); + free (tzstr); + } + + /* Don't use an empty TZ string. */ + if (tzspec != NULL && tzspec[0] == '\0') + tzspec = NULL; + + fclose (f); + + /* First "register" all timezone names. */ + for (i = 0; i < num_types; ++i) + (void) __tzstring (&zone_names[types[i].idx]); + + /* Find the standard and daylight time offsets used by the rule file. + We choose the offsets in the types of each flavor that are + transitioned to earliest in time. */ + __tzname[0] = NULL; + __tzname[1] = NULL; + for (i = num_transitions; i > 0; ) + { + int type = type_idxs[--i]; + int dst = types[type].isdst; + + if (__tzname[dst] == NULL) + { + int idx = types[type].idx; + + __tzname[dst] = __tzstring (&zone_names[idx]); + + if (__tzname[1 - dst] != NULL) + break; + } + } + if (__tzname[0] == NULL) + { + /* This should only happen if there are no transition rules. + In this case there should be only one single type. */ + assert (num_types == 1); + __tzname[0] = __tzstring (zone_names); + } + if (__tzname[1] == NULL) + __tzname[1] = __tzname[0]; + + if (num_transitions == 0) + /* Use the first rule (which should also be the only one). */ + rule_stdoff = rule_dstoff = types[0].offset; + else + { + int stdoff_set = 0, dstoff_set = 0; + rule_stdoff = rule_dstoff = 0; + i = num_transitions - 1; + do + { + if (!stdoff_set && !types[type_idxs[i]].isdst) + { + stdoff_set = 1; + rule_stdoff = types[type_idxs[i]].offset; + } + else if (!dstoff_set && types[type_idxs[i]].isdst) + { + dstoff_set = 1; + rule_dstoff = types[type_idxs[i]].offset; + } + if (stdoff_set && dstoff_set) + break; + } + while (i-- > 0); + + if (!dstoff_set) + rule_dstoff = rule_stdoff; + } + + __daylight = rule_stdoff != rule_dstoff; + __timezone = -rule_stdoff; + + done: + __use_tzfile = 1; + free (new); + return; + + lose: + fclose (f); + ret_free_transitions: + free (new); + free ((void *) transitions); + transitions = NULL; +} + +/* The user specified a hand-made timezone, but not its DST rules. + We will use the names and offsets from the user, and the rules + from the TZDEFRULES file. */ + +void +__tzfile_default (const char *std, const char *dst, + long int stdoff, long int dstoff) +{ + size_t stdlen = strlen (std) + 1; + size_t dstlen = strlen (dst) + 1; + size_t i; + int isdst; + char *cp; + + __tzfile_read (TZDEFRULES, stdlen + dstlen, &cp); + if (!__use_tzfile) + return; + + if (num_types < 2) + { + __use_tzfile = 0; + return; + } + + /* Ignore the zone names read from the file and use the given ones + instead. */ + __mempcpy (__mempcpy (cp, std, stdlen), dst, dstlen); + zone_names = cp; + + /* Now there are only two zones, regardless of what the file contained. */ + num_types = 2; + + /* Now correct the transition times for the user-specified standard and + daylight offsets from GMT. */ + isdst = 0; + for (i = 0; i < num_transitions; ++i) + { + struct ttinfo *trans_type = &types[type_idxs[i]]; + + /* We will use only types 0 (standard) and 1 (daylight). + Fix up this transition to point to whichever matches + the flavor of its original type. */ + type_idxs[i] = trans_type->isdst; + + if (trans_type->isgmt) + /* The transition time is in GMT. No correction to apply. */ ; + else if (isdst && !trans_type->isstd) + /* The type says this transition is in "local wall clock time", and + wall clock time as of the previous transition was DST. Correct + for the difference between the rule's DST offset and the user's + DST offset. */ + transitions[i] += dstoff - rule_dstoff; + else + /* This transition is in "local wall clock time", and wall clock + time as of this iteration is non-DST. Correct for the + difference between the rule's standard offset and the user's + standard offset. */ + transitions[i] += stdoff - rule_stdoff; + + /* The DST state of "local wall clock time" for the next iteration is + as specified by this transition. */ + isdst = trans_type->isdst; + } + + /* Now that we adjusted the transitions to the requested offsets, + reset the rule_stdoff and rule_dstoff values appropriately. They + are used elsewhere. */ + rule_stdoff = stdoff; + rule_dstoff = dstoff; + + /* Reset types 0 and 1 to describe the user's settings. */ + types[0].idx = 0; + types[0].offset = stdoff; + types[0].isdst = 0; + types[1].idx = stdlen; + types[1].offset = dstoff; + types[1].isdst = 1; + + /* Reset the zone names to point to the user's names. */ + __tzname[0] = (char *) std; + __tzname[1] = (char *) dst; + + /* Set the timezone. */ + __timezone = -types[0].offset; + + /* Invalidate the tzfile attribute cache to force rereading + TZDEFRULES the next time it is used. */ + tzfile_dev = 0; + tzfile_ino = 0; + tzfile_mtime = 0; +} + +void +__tzfile_compute (time_t timer, int use_localtime, + long int *leap_correct, int *leap_hit, + struct tm *tp) +{ + size_t i; + + if (use_localtime) + { + __tzname[0] = NULL; + __tzname[1] = NULL; + + if (__glibc_unlikely (num_transitions == 0 || timer < transitions[0])) + { + /* TIMER is before any transition (or there are no transitions). + Choose the first non-DST type + (or the first if they're all DST types). */ + i = 0; + while (i < num_types && types[i].isdst) + { + if (__tzname[1] == NULL) + __tzname[1] = __tzstring (&zone_names[types[i].idx]); + + ++i; + } + + if (i == num_types) + i = 0; + __tzname[0] = __tzstring (&zone_names[types[i].idx]); + if (__tzname[1] == NULL) + { + size_t j = i; + while (j < num_types) + if (types[j].isdst) + { + __tzname[1] = __tzstring (&zone_names[types[j].idx]); + break; + } + else + ++j; + } + } + else if (__glibc_unlikely (timer >= transitions[num_transitions - 1])) + { + if (__glibc_unlikely (tzspec == NULL)) + { + use_last: + i = num_transitions; + goto found; + } + + /* Parse the POSIX TZ-style string. */ + __tzset_parse_tz (tzspec); + + /* Convert to broken down structure. If this fails do not + use the string. */ + if (__glibc_unlikely (! __offtime (&timer, 0, tp))) + goto use_last; + + /* Use the rules from the TZ string to compute the change. */ + __tz_compute (timer, tp, 1); + + /* If tzspec comes from posixrules loaded by __tzfile_default, + override the STD and DST zone names with the ones user + requested in TZ envvar. */ + if (__glibc_unlikely (zone_names == (char *) &leaps[num_leaps])) + { + assert (num_types == 2); + __tzname[0] = __tzstring (zone_names); + __tzname[1] = __tzstring (&zone_names[strlen (zone_names) + 1]); + } + + goto leap; + } + else + { + /* Find the first transition after TIMER, and + then pick the type of the transition before it. */ + size_t lo = 0; + size_t hi = num_transitions - 1; + /* Assume that DST is changing twice a year and guess initial + search spot from it. + Half of a gregorian year has on average 365.2425 * 86400 / 2 + = 15778476 seconds. */ + i = (transitions[num_transitions - 1] - timer) / 15778476; + if (i < num_transitions) + { + i = num_transitions - 1 - i; + if (timer < transitions[i]) + { + if (i < 10 || timer >= transitions[i - 10]) + { + /* Linear search. */ + while (timer < transitions[i - 1]) + --i; + goto found; + } + hi = i - 10; + } + else + { + if (i + 10 >= num_transitions || timer < transitions[i + 10]) + { + /* Linear search. */ + while (timer >= transitions[i]) + ++i; + goto found; + } + lo = i + 10; + } + } + + /* Binary search. */ + /* assert (timer >= transitions[lo] && timer < transitions[hi]); */ + while (lo + 1 < hi) + { + i = (lo + hi) / 2; + if (timer < transitions[i]) + hi = i; + else + lo = i; + } + i = hi; + + found: + /* assert (timer >= transitions[i - 1] + && (i == num_transitions || timer < transitions[i])); */ + __tzname[types[type_idxs[i - 1]].isdst] + = __tzstring (&zone_names[types[type_idxs[i - 1]].idx]); + size_t j = i; + while (j < num_transitions) + { + int type = type_idxs[j]; + int dst = types[type].isdst; + int idx = types[type].idx; + + if (__tzname[dst] == NULL) + { + __tzname[dst] = __tzstring (&zone_names[idx]); + + if (__tzname[1 - dst] != NULL) + break; + } + + ++j; + } + + if (__glibc_unlikely (__tzname[0] == NULL)) + __tzname[0] = __tzname[1]; + + i = type_idxs[i - 1]; + } + + struct ttinfo *info = &types[i]; + __daylight = rule_stdoff != rule_dstoff; + __timezone = -rule_stdoff; + + if (__tzname[0] == NULL) + { + /* This should only happen if there are no transition rules. + In this case there should be only one single type. */ + assert (num_types == 1); + __tzname[0] = __tzstring (zone_names); + } + if (__tzname[1] == NULL) + /* There is no daylight saving time. */ + __tzname[1] = __tzname[0]; + tp->tm_isdst = info->isdst; + assert (strcmp (&zone_names[info->idx], __tzname[tp->tm_isdst]) == 0); + tp->tm_zone = __tzname[tp->tm_isdst]; + tp->tm_gmtoff = info->offset; + } + + leap: + *leap_correct = 0L; + *leap_hit = 0; + + /* Find the last leap second correction transition time before TIMER. */ + i = num_leaps; + do + if (i-- == 0) + return; + while (timer < leaps[i].transition); + + /* Apply its correction. */ + *leap_correct = leaps[i].change; + + if (timer == leaps[i].transition && /* Exactly at the transition time. */ + ((i == 0 && leaps[i].change > 0) || + leaps[i].change > leaps[i - 1].change)) + { + *leap_hit = 1; + while (i > 0 + && leaps[i].transition == leaps[i - 1].transition + 1 + && leaps[i].change == leaps[i - 1].change + 1) + { + ++*leap_hit; + --i; + } + } +} diff --git a/REORG.TODO/time/tzset.c b/REORG.TODO/time/tzset.c new file mode 100644 index 0000000000..8868e9aada --- /dev/null +++ b/REORG.TODO/time/tzset.c @@ -0,0 +1,632 @@ +/* Copyright (C) 1991-2017 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 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, see + <http://www.gnu.org/licenses/>. */ + +#include <ctype.h> +#include <errno.h> +#include <libc-lock.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include <timezone/tzfile.h> + +char *__tzname[2] = { (char *) "GMT", (char *) "GMT" }; +int __daylight = 0; +long int __timezone = 0L; + +weak_alias (__tzname, tzname) +weak_alias (__daylight, daylight) +weak_alias (__timezone, timezone) + +/* This locks all the state variables in tzfile.c and this file. */ +__libc_lock_define_initialized (static, tzset_lock) + +/* 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. */ + 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]; + + +static void compute_change (tz_rule *rule, int year) __THROW internal_function; +static void tzset_internal (int always); + +/* List of buffers containing time zone strings. */ +struct tzstring_l +{ + struct tzstring_l *next; + size_t len; /* strlen(data) - doesn't count terminating NUL! */ + char data[0]; +}; + +static struct tzstring_l *tzstring_list; + +/* Allocate a permanent home for the first LEN characters of S. It + will never be moved or deallocated, but may share space with other + strings. Don't modify the returned string. */ +static char * +__tzstring_len (const char *s, size_t len) +{ + char *p; + struct tzstring_l *t, *u, *new; + + /* Walk the list and look for a match. If this string is the same + as the end of an already-allocated string, it can share space. */ + for (u = t = tzstring_list; t; u = t, t = t->next) + if (len <= t->len) + { + p = &t->data[t->len - len]; + if (memcmp (s, p, len) == 0) + return p; + } + + /* Not found; allocate a new buffer. */ + new = malloc (sizeof (struct tzstring_l) + len + 1); + if (!new) + return NULL; + + new->next = NULL; + new->len = len; + memcpy (new->data, s, len); + new->data[len] = '\0'; + + if (u) + u->next = new; + else + tzstring_list = new; + + return new->data; +} + +/* Allocate a permanent home for S. It will never be moved or + deallocated, but may share space with other strings. Don't modify + the returned string. */ +char * +__tzstring (const char *s) +{ + return __tzstring_len (s, strlen (s)); +} + +static char *old_tz; + +static void +internal_function +update_vars (void) +{ + __daylight = tz_rules[0].offset != tz_rules[1].offset; + __timezone = -tz_rules[0].offset; + __tzname[0] = (char *) tz_rules[0].name; + __tzname[1] = (char *) tz_rules[1].name; +} + + +static unsigned int +compute_offset (unsigned int ss, unsigned int mm, unsigned int hh) +{ + if (ss > 59) + ss = 59; + if (mm > 59) + mm = 59; + if (hh > 24) + hh = 24; + return ss + mm * 60 + hh * 60 * 60; +} + +/* Parses the time zone name at *TZP, and writes a pointer to an + interned string to tz_rules[WHICHRULE].name. On success, advances + *TZP, and returns true. Returns false otherwise. */ +static bool +parse_tzname (const char **tzp, int whichrule) +{ + const char *start = *tzp; + const char *p = start; + while (('a' <= *p && *p <= 'z') + || ('A' <= *p && *p <= 'Z')) + ++p; + size_t len = p - start; + if (len < 3) + { + p = *tzp; + if (__glibc_unlikely (*p++ != '<')) + return false; + start = p; + while (('a' <= *p && *p <= 'z') + || ('A' <= *p && *p <= 'Z') + || ('0' <= *p && *p <= '9') + || *p == '+' || *p == '-') + ++p; + len = p - start; + if (*p++ != '>' || len < 3) + return false; + } + + const char *name = __tzstring_len (start, len); + if (name == NULL) + return false; + tz_rules[whichrule].name = name; + + *tzp = p; + return true; +} + +/* Parses the time zone offset at *TZP, and writes it to + tz_rules[WHICHRULE].offset. Returns true if the parse was + successful. */ +static bool +parse_offset (const char **tzp, int whichrule) +{ + const char *tz = *tzp; + if (whichrule == 0 + && (*tz == '\0' || (*tz != '+' && *tz != '-' && !isdigit (*tz)))) + return false; + + long sign; + if (*tz == '-' || *tz == '+') + sign = *tz++ == '-' ? 1L : -1L; + else + sign = -1L; + *tzp = tz; + + unsigned short int hh; + unsigned short mm = 0; + unsigned short ss = 0; + int consumed = 0; + if (sscanf (tz, "%hu%n:%hu%n:%hu%n", + &hh, &consumed, &mm, &consumed, &ss, &consumed) > 0) + tz_rules[whichrule].offset = sign * compute_offset (ss, mm, hh); + else + /* Nothing could be parsed. */ + if (whichrule == 0) + { + /* Standard time defaults to offset zero. */ + tz_rules[0].offset = 0; + return false; + } + else + /* DST defaults to one hour later than standard time. */ + tz_rules[1].offset = tz_rules[0].offset + (60 * 60); + *tzp = tz + consumed; + return true; +} + +/* Parses the standard <-> DST rules at *TZP. Updates + tz_rule[WHICHRULE]. On success, advances *TZP and returns true. + Otherwise, returns false. */ +static bool +parse_rule (const char **tzp, int whichrule) +{ + const char *tz = *tzp; + tz_rule *tzr = &tz_rules[whichrule]; + + /* Ignore comma to support string following the incorrect + specification in early POSIX.1 printings. */ + tz += *tz == ','; + + /* Get the date of the change. */ + if (*tz == 'J' || isdigit (*tz)) + { + char *end; + tzr->type = *tz == 'J' ? J1 : J0; + if (tzr->type == J1 && !isdigit (*++tz)) + return false; + unsigned long int d = strtoul (tz, &end, 10); + if (end == tz || d > 365) + return false; + if (tzr->type == J1 && d == 0) + return false; + tzr->d = d; + tz = end; + } + else if (*tz == 'M') + { + tzr->type = M; + int consumed; + if (sscanf (tz, "M%hu.%hu.%hu%n", + &tzr->m, &tzr->n, &tzr->d, &consumed) != 3 + || tzr->m < 1 || tzr->m > 12 + || tzr->n < 1 || tzr->n > 5 || tzr->d > 6) + return false; + tz += consumed; + } + else if (*tz == '\0') + { + /* Daylight time rules in the U.S. are defined in the U.S. Code, + Title 15, Chapter 6, Subchapter IX - Standard Time. These + dates were established by Congress in the Energy Policy Act + of 2005 [Pub. L. no. 109-58, 119 Stat 594 (2005)]. + Below is the equivalent of "M3.2.0,M11.1.0" [/2 not needed + since 2:00AM is the default]. */ + tzr->type = M; + if (tzr == &tz_rules[0]) + { + tzr->m = 3; + tzr->n = 2; + tzr->d = 0; + } + else + { + tzr->m = 11; + tzr->n = 1; + tzr->d = 0; + } + } + else + return false; + + if (*tz != '\0' && *tz != '/' && *tz != ',') + return false; + else if (*tz == '/') + { + /* Get the time of day of the change. */ + int negative; + ++tz; + if (*tz == '\0') + return false; + negative = *tz == '-'; + tz += negative; + /* Default to 2:00 AM. */ + unsigned short hh = 2; + unsigned short mm = 0; + unsigned short ss = 0; + int consumed = 0; + sscanf (tz, "%hu%n:%hu%n:%hu%n", + &hh, &consumed, &mm, &consumed, &ss, &consumed);; + tz += consumed; + tzr->secs = (negative ? -1 : 1) * ((hh * 60 * 60) + (mm * 60) + ss); + } + else + /* Default to 2:00 AM. */ + tzr->secs = 2 * 60 * 60; + + tzr->computed_for = -1; + *tzp = tz; + return true; +} + +/* Parse the POSIX TZ-style string. */ +void +__tzset_parse_tz (const char *tz) +{ + /* Clear out old state and reset to unnamed UTC. */ + memset (tz_rules, '\0', sizeof tz_rules); + tz_rules[0].name = tz_rules[1].name = ""; + + /* Get the standard timezone name. */ + if (parse_tzname (&tz, 0) && parse_offset (&tz, 0)) + { + /* Get the DST timezone name (if any). */ + if (*tz != '\0') + { + if (parse_tzname (&tz, 1)) + { + parse_offset (&tz, 1); + if (*tz == '\0' || (tz[0] == ',' && tz[1] == '\0')) + { + /* There is no rule. See if there is a default rule + file. */ + __tzfile_default (tz_rules[0].name, tz_rules[1].name, + tz_rules[0].offset, tz_rules[1].offset); + if (__use_tzfile) + { + free (old_tz); + old_tz = NULL; + return; + } + } + } + /* Figure out the standard <-> DST rules. */ + if (parse_rule (&tz, 0)) + parse_rule (&tz, 1); + } + else + { + /* There is no DST. */ + tz_rules[1].name = tz_rules[0].name; + tz_rules[1].offset = tz_rules[0].offset; + } + } + + update_vars (); +} + +/* Interpret the TZ envariable. */ +static void +tzset_internal (int always) +{ + static int is_initialized; + const char *tz; + + if (is_initialized && !always) + return; + is_initialized = 1; + + /* Examine the TZ environment variable. */ + tz = getenv ("TZ"); + if (tz && *tz == '\0') + /* User specified the empty string; use UTC explicitly. */ + tz = "Universal"; + + /* A leading colon means "implementation defined syntax". + We ignore the colon and always use the same algorithm: + try a data file, and if none exists parse the 1003.1 syntax. */ + if (tz && *tz == ':') + ++tz; + + /* Check whether the value changed since the last run. */ + if (old_tz != NULL && tz != NULL && strcmp (tz, old_tz) == 0) + /* No change, simply return. */ + return; + + if (tz == NULL) + /* No user specification; use the site-wide default. */ + tz = TZDEFAULT; + + tz_rules[0].name = NULL; + tz_rules[1].name = NULL; + + /* Save the value of `tz'. */ + free (old_tz); + old_tz = tz ? __strdup (tz) : NULL; + + /* Try to read a data file. */ + __tzfile_read (tz, 0, NULL); + if (__use_tzfile) + return; + + /* No data file found. Default to UTC if nothing specified. */ + + if (tz == NULL || *tz == '\0' + || (TZDEFAULT != NULL && strcmp (tz, TZDEFAULT) == 0)) + { + memset (tz_rules, '\0', sizeof tz_rules); + tz_rules[0].name = tz_rules[1].name = "UTC"; + if (J0 != 0) + tz_rules[0].type = tz_rules[1].type = J0; + tz_rules[0].change = tz_rules[1].change = (time_t) -1; + update_vars (); + return; + } + + __tzset_parse_tz (tz); +} + +/* Figure out the exact time (as a time_t) in YEAR + when the change described by RULE will occur and + put it in RULE->change, saving YEAR in RULE->computed_for. */ +static void +internal_function +compute_change (tz_rule *rule, int year) +{ + time_t t; + + if (year != -1 && rule->computed_for == year) + /* Operations on times in 2 BC will be slower. Oh well. */ + return; + + /* First set T to January 1st, 0:00:00 GMT in YEAR. */ + if (year > 1970) + t = ((year - 1970) * 365 + + /* Compute the number of leapdays between 1970 and YEAR + (exclusive). There is a leapday every 4th year ... */ + + ((year - 1) / 4 - 1970 / 4) + /* ... except every 100th year ... */ + - ((year - 1) / 100 - 1970 / 100) + /* ... but still every 400th year. */ + + ((year - 1) / 400 - 1970 / 400)) * SECSPERDAY; + else + t = 0; + + switch (rule->type) + { + case J1: + /* Jn - Julian day, 1 == January 1, 60 == March 1 even in leap years. + In non-leap years, or if the day number is 59 or less, just + add SECSPERDAY times the day number-1 to the time of + January 1, midnight, to get the day. */ + t += (rule->d - 1) * SECSPERDAY; + if (rule->d >= 60 && __isleap (year)) + t += SECSPERDAY; + break; + + case J0: + /* n - Day of year. + Just add SECSPERDAY times the day number to the time of Jan 1st. */ + t += rule->d * SECSPERDAY; + break; + + case M: + /* Mm.n.d - Nth "Dth day" of month M. */ + { + unsigned int i; + int d, m1, yy0, yy1, yy2, dow; + const unsigned short int *myday = + &__mon_yday[__isleap (year)][rule->m]; + + /* First add SECSPERDAY for each day in months before M. */ + t += myday[-1] * SECSPERDAY; + + /* Use Zeller's Congruence to get day-of-week of first day of month. */ + m1 = (rule->m + 9) % 12 + 1; + yy0 = (rule->m <= 2) ? (year - 1) : year; + yy1 = yy0 / 100; + yy2 = yy0 % 100; + dow = ((26 * m1 - 2) / 10 + 1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7; + if (dow < 0) + dow += 7; + + /* DOW is the day-of-week of the first day of the month. Get the + day-of-month (zero-origin) of the first DOW day of the month. */ + d = rule->d - dow; + if (d < 0) + d += 7; + for (i = 1; i < rule->n; ++i) + { + if (d + 7 >= (int) myday[0] - myday[-1]) + break; + d += 7; + } + + /* D is the day-of-month (zero-origin) of the day we want. */ + t += d * SECSPERDAY; + } + break; + } + + /* T is now the Epoch-relative time of 0:00:00 GMT on the day we want. + Just add the time of day and local offset from GMT, and we're done. */ + + rule->change = t - rule->offset + rule->secs; + rule->computed_for = year; +} + + +/* Figure out the correct timezone for TM and set `__tzname', + `__timezone', and `__daylight' accordingly. */ +void +internal_function +__tz_compute (time_t timer, struct tm *tm, int use_localtime) +{ + compute_change (&tz_rules[0], 1900 + tm->tm_year); + compute_change (&tz_rules[1], 1900 + tm->tm_year); + + if (use_localtime) + { + int isdst; + + /* We have to distinguish between northern and southern + hemisphere. For the latter the daylight saving time + ends in the next year. */ + if (__builtin_expect (tz_rules[0].change + > tz_rules[1].change, 0)) + isdst = (timer < tz_rules[1].change + || timer >= tz_rules[0].change); + else + isdst = (timer >= tz_rules[0].change + && timer < tz_rules[1].change); + tm->tm_isdst = isdst; + tm->tm_zone = __tzname[isdst]; + tm->tm_gmtoff = tz_rules[isdst].offset; + } +} + +/* Reinterpret the TZ environment variable and set `tzname'. */ +#undef tzset + +void +__tzset (void) +{ + __libc_lock_lock (tzset_lock); + + tzset_internal (1); + + if (!__use_tzfile) + { + /* Set `tzname'. */ + __tzname[0] = (char *) tz_rules[0].name; + __tzname[1] = (char *) tz_rules[1].name; + } + + __libc_lock_unlock (tzset_lock); +} +weak_alias (__tzset, tzset) + +/* Return the `struct tm' representation of *TIMER in the local timezone. + Use local time if USE_LOCALTIME is nonzero, UTC otherwise. */ +struct tm * +__tz_convert (const time_t *timer, int use_localtime, struct tm *tp) +{ + long int leap_correction; + int leap_extra_secs; + + if (timer == NULL) + { + __set_errno (EINVAL); + return NULL; + } + + __libc_lock_lock (tzset_lock); + + /* Update internal database according to current TZ setting. + POSIX.1 8.3.7.2 says that localtime_r is not required to set tzname. + This is a good idea since this allows at least a bit more parallelism. */ + tzset_internal (tp == &_tmbuf && use_localtime); + + if (__use_tzfile) + __tzfile_compute (*timer, use_localtime, &leap_correction, + &leap_extra_secs, tp); + else + { + if (! __offtime (timer, 0, tp)) + tp = NULL; + else + __tz_compute (*timer, tp, use_localtime); + leap_correction = 0L; + leap_extra_secs = 0; + } + + __libc_lock_unlock (tzset_lock); + + if (tp) + { + if (! use_localtime) + { + tp->tm_isdst = 0; + tp->tm_zone = "GMT"; + tp->tm_gmtoff = 0L; + } + + if (__offtime (timer, tp->tm_gmtoff - leap_correction, tp)) + tp->tm_sec += leap_extra_secs; + else + tp = NULL; + } + + return tp; +} + + +libc_freeres_fn (free_mem) +{ + while (tzstring_list != NULL) + { + struct tzstring_l *old = tzstring_list; + + tzstring_list = tzstring_list->next; + free (old); + } + free (old_tz); + old_tz = NULL; +} diff --git a/REORG.TODO/time/wcsftime.c b/REORG.TODO/time/wcsftime.c new file mode 100644 index 0000000000..cbc4161cb5 --- /dev/null +++ b/REORG.TODO/time/wcsftime.c @@ -0,0 +1,28 @@ +/* Copyright (C) 1991-2017 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 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, see + <http://www.gnu.org/licenses/>. */ + +#include <wchar.h> +#include <locale/localeinfo.h> + + +size_t +wcsftime (wchar_t *s, size_t maxsize, const wchar_t *format, + const struct tm *tp) +{ + return __wcsftime_l (s, maxsize, format, tp, _NL_CURRENT_LOCALE); +} +libc_hidden_def (wcsftime) diff --git a/REORG.TODO/time/wcsftime_l.c b/REORG.TODO/time/wcsftime_l.c new file mode 100644 index 0000000000..32101064e3 --- /dev/null +++ b/REORG.TODO/time/wcsftime_l.c @@ -0,0 +1,25 @@ +/* Copyright (C) 2002-2017 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 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, see + <http://www.gnu.org/licenses/>. */ + +#include <wchar.h> +#include <wctype.h> + +#define USE_IN_EXTENDED_LOCALE_MODEL 1 +#define COMPILE_WIDE 1 +#include "strftime_l.c" + +weak_alias (__wcsftime_l, wcsftime_l) |