diff options
-rw-r--r-- | config/config.guess | 9 | ||||
-rwxr-xr-x | config/install-sh | 7 | ||||
-rw-r--r-- | config/texinfo.tex | 17 | ||||
-rw-r--r-- | doc/getdate.texi | 74 | ||||
-rw-r--r-- | lib/getdate.y | 667 | ||||
-rw-r--r-- | lib/getpagesize.h | 4 | ||||
-rw-r--r-- | lib/mktime.c | 1 | ||||
-rw-r--r-- | m4/getpagesize.m4 | 6 |
8 files changed, 484 insertions, 301 deletions
diff --git a/config/config.guess b/config/config.guess index 7d0185e01..c28419dd5 100644 --- a/config/config.guess +++ b/config/config.guess @@ -3,7 +3,7 @@ # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, # 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc. -timestamp='2004-09-07' +timestamp='2004-10-25' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by @@ -342,7 +342,7 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in DRS?6000:unix:4.0:6*) echo sparc-icl-nx6 exit 0 ;; - DRS?6000:UNIX_SV:4.2*:7*) + DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) case `/usr/bin/uname -p` in sparc) echo sparc-icl-nx7 && exit 0 ;; esac ;; @@ -1247,7 +1247,10 @@ EOF A*) echo alpha-dec-vms && exit 0 ;; I*) echo ia64-dec-vms && exit 0 ;; V*) echo vax-dec-vms && exit 0 ;; - esac + esac ;; + *:XENIX:*:SysV) + echo i386-pc-xenix + exit 0 ;; esac #echo '(No uname command or uname output not recognized.)' 1>&2 diff --git a/config/install-sh b/config/install-sh index dd97db7aa..0b65ee871 100755 --- a/config/install-sh +++ b/config/install-sh @@ -1,7 +1,7 @@ #!/bin/sh # install - install a program, script, or datafile -scriptversion=2004-09-10.20 +scriptversion=2004-10-22.00 # This originates from X11R5 (mit/util/scripts/install.sh), which was # later released in X11R6 (xc/config/util/install.sh) with the @@ -213,7 +213,7 @@ do fi # This sed command emulates the dirname command. - dstdir=`echo "$dst" | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` + dstdir=`echo "$dst" | sed -e 's,/*$,,;s,[^/]*$,,;s,/*$,,;s,^$,.,'` # Make sure that the destination directory exists. @@ -226,7 +226,8 @@ do oIFS=$IFS # Some sh's can't handle IFS=/ for some reason. IFS='%' - set - `echo "$dstdir" | sed -e 's@/@%@g' -e 's@^%@/@'` + set x `echo "$dstdir" | sed -e 's@/@%@g' -e 's@^%@/@'` + shift IFS=$oIFS pathcomp= diff --git a/config/texinfo.tex b/config/texinfo.tex index e758cedae..b4b9016f1 100644 --- a/config/texinfo.tex +++ b/config/texinfo.tex @@ -2314,15 +2314,26 @@ where each line of input produces a line of output.} % @table, @ftable, @vtable. \envdef\table{% \let\itemindex\gobble - \tablex + \tablecheck{table}% } \envdef\ftable{% \def\itemindex ##1{\doind {fn}{\code{##1}}}% - \tablex + \tablecheck{ftable}% } \envdef\vtable{% \def\itemindex ##1{\doind {vr}{\code{##1}}}% - \tablex + \tablecheck{vtable}% +} +\def\tablecheck#1{% + \ifnum \the\catcode`\^^M=\active + \endgroup + \errmessage{This command won't work in this context; perhaps the problem is + that we are \inenvironment\thisenv}% + \def\next{\doignore{#1}}% + \else + \let\next\tablex + \fi + \next } \def\tablex#1{% \def\itemindicate{#1}% diff --git a/doc/getdate.texi b/doc/getdate.texi index 1d312a689..9280c5087 100644 --- a/doc/getdate.texi +++ b/doc/getdate.texi @@ -51,11 +51,12 @@ arguments to the various programs. The C interface (via the * General date syntax:: Common rules. * Calendar date items:: 19 Dec 1994. * Time of day items:: 9:20pm. -* Time zone items:: @sc{est}, @sc{pdt}, @sc{gmt}, ... +* Time zone items:: @sc{est}, @sc{pdt}, @sc{gmt}. * Day of week items:: Monday and others. * Relative items in date strings:: next tuesday, 2 years ago. * Pure numbers in date strings:: 19931219, 1440. * Seconds since the Epoch:: @@1078100502. +* Specifying time zone rules:: TZ="America/New_York", TZ="UTC0". * Authors of get_date:: Bellovin, Eggert, Salz, Berets, et al. @end menu @@ -74,7 +75,7 @@ many flavors of items: @itemize @bullet @item calendar date items -@item time of the day items +@item time of day items @item time zone items @item day of the week items @item relative items @@ -108,7 +109,8 @@ abbreviations like @samp{AM}, @samp{DST}, @samp{EST}, @samp{first}, @cindex language, in dates @cindex time zone item -The output of @command{date} is not always acceptable as a date string, +The output of the @command{date} command +is not always acceptable as a date string, not only because of the language problem, but also because there is no standard meaning for time zone items like @samp{IST}. When using @command{date} to generate a date string intended to be parsed later, @@ -225,7 +227,7 @@ day. Here are some examples, all of which represent the same time: 20:02-0500 # In @sc{est} (U.S. Eastern Standard Time). @end example -More generally, the time of the day may be given as +More generally, the time of day may be given as @samp{@var{hour}:@var{minute}:@var{second}}, where @var{hour} is a number between 0 and 23, @var{minute} is a number between 0 and 59, and @var{second} is a number between 0 and 59 possibly followed by @@ -257,7 +259,7 @@ of zone minutes. When a time zone correction is given this way, it forces interpretation of the time relative to Coordinated Universal Time (@sc{utc}), overriding any previous specification for the time zone or the local time zone. The @var{minute} -part of the time of the day may not be elided when a time zone correction +part of the time of day may not be elided when a time zone correction is used. This is the best way to specify a time zone correction by fractional parts of an hour. @@ -285,6 +287,10 @@ Australia than in the United States. Instead, it's better to use unambiguous numeric time zone corrections like @samp{-0500}, as described in the previous section. +If neither a time zone item nor a time zone correction is supplied, +time stamps are interpreted using the rules of the default time zone +(@pxref{Specifying time zone rules}). + @node Day of week items @section Day of week items @@ -372,7 +378,7 @@ the meaning of a zero-valued time displacement, but is preferred in date strings like @samp{this thursday}. When a relative item causes the resulting date to cross a boundary -where the clocks were adjusted, typically for daylight-saving time, +where the clocks were adjusted, typically for daylight saving time, the resulting date and time are adjusted accordingly. The fuzz in units can cause problems with relative items. For @@ -413,7 +419,7 @@ calendar date. If the decimal number is of the form @var{hh}@var{mm} and no other time of day item appears before it in the date string, then @var{hh} is read as the hour of the day and @var{mm} as the minute of the hour, for the -specified time of the day. @var{mm} can also be omitted. +specified time of day. @var{mm} can also be omitted. If both a calendar date and a time of day appear to the left of a number in the date string, but no relative item, then the number overrides the @@ -427,7 +433,8 @@ If you precede a number with @samp{@@}, it represents an internal time stamp as a count of seconds. The number can contain an internal decimal point (either @samp{.} or @samp{,}); any excess precision not supported by the internal representation is truncated toward minus -infinity. +infinity. Such a number cannot be combined with any other date +item, as it specifies a complete time stamp. @cindex beginning of time, for @acronym{POSIX} @cindex epoch, for @acronym{POSIX} @@ -452,6 +459,57 @@ For example, on most systems @samp{@@915148799} represents 1998-12-31 @sc{utc}, and there is no way to represent the intervening leap second 1998-12-31 23:59:60 @sc{utc}. +@node Specifying time zone rules +@section Specifying time zone rules + +@vindex TZ +Normally, dates are interpreted using the rules of the current time +zone, which in turn are specified by the @env{TZ} environment +variable, or by a system default if @env{TZ} is not set. To specify a +different set of default time zone rules that apply just to one date, +start the date with a string of the form @samp{TZ="@var{rule}"}. The +two quote characters (@samp{"}) must be present in the date, and any +quotes or backslashes within @var{rule} must be escaped by a +backslash. + +For example, with the @acronym{GNU} @command{date} command you can +answer the question ``What time is it in New York when a Paris clock +shows 6:30am on October 31, 2004?'' by using a date beginning with +@samp{TZ="Europe/Paris"} as shown in the following shell transcript: + +@example +$ export TZ="America/New_York" +$ date --date='TZ="Europe/Paris" 2004-10-31 06:30' +Sun Oct 31 01:30:00 EDT 2004 +@end example + +In this example, the @option{--date} operand begins with its own +@env{TZ} setting, so the rest of that operand is processed according +to @samp{Europe/Paris} rules, treating the string @samp{2004-10-31 +06:30} as if it were in Paris. However, since the output of the +@command{date} command is processed according to the overall time zone +rules, it uses New York time. (Paris was normally six hours ahead of +New York in 2004, but this example refers to a brief Halloween period +when the gap was five hours.) + +A @env{TZ} value is a rule that typically names a location in the +@uref{http://www.twinsun.com/tz/tz-link.htm, @samp{tz} database}. +A recent catalog of location names appears in the +@uref{http://twiki.org/cgi-bin/xtra/tzdate, TWiki Date and Time +Gateway}. A few non-@acronym{GNU} hosts require a colon before a +location name in a @env{TZ} setting, e.g., +@samp{TZ=":America/New_York"}. + +The @samp{tz} database includes a wide variety of locations ranging +from @samp{Arctic/Longyearbyen} to @samp{Antarctica/South_Pole}, but +if you are at sea and have your own private time zone, or if you are +using a non-@acronym{GNU} host that does not support the @samp{tz} +database, you may need to use a @acronym{POSIX} rule instead. Simple +@acronym{POSIX} rules like @samp{UTC0} specify a time zone without +daylight saving time; other rules can specify simple daylight saving +regimes. @xref{TZ Variable,, Specifying the Time Zone with @code{TZ}, +libc, The GNU C Library}. + @node Authors of get_date @section Authors of @code{get_date} diff --git a/lib/getdate.y b/lib/getdate.y index e8cbfeb6b..ec8c19250 100644 --- a/lib/getdate.y +++ b/lib/getdate.y @@ -22,16 +22,13 @@ <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990. Modified by Paul Eggert <eggert@twinsun.com> in August 1999 to do - the right thing about local DST, and in February 2004 to support - nanosecond-resolution time stamps. Unlike previous versions, this - version is reentrant. */ + the right thing about local DST. Also modified by Paul Eggert + <eggert@cs.ucla.edu> in February 2004 to support + nanosecond-resolution time stamps, and in October 2004 to support + TZ strings in dates. */ /* FIXME: Check for arithmetic overflow in all cases, not just - some of them. - - FIXME: The current code uses 'int' to count seconds; it should use - something like 'intmax_t' to support time stamps that don't fit in - 32 bits. */ + some of them. */ #ifdef HAVE_CONFIG_H # include <config.h> @@ -53,7 +50,11 @@ #include <ctype.h> #include <limits.h> -#include <stdlib.h> /* for `free'; used by Bison 1.27 */ +#include <stdlib.h> +#include <string.h> + +#include "setenv.h" +#include "xalloc.h" #if STDC_HEADERS || (! defined isascii && ! HAVE_ISASCII) # define IN_CTYPE_DOMAIN(c) 1 @@ -64,23 +65,16 @@ #define ISSPACE(c) (IN_CTYPE_DOMAIN (c) && isspace (c)) #define ISALPHA(c) (IN_CTYPE_DOMAIN (c) && isalpha (c)) #define ISLOWER(c) (IN_CTYPE_DOMAIN (c) && islower (c)) -#define ISDIGIT_LOCALE(c) (IN_CTYPE_DOMAIN (c) && isdigit (c)) -/* ISDIGIT differs from ISDIGIT_LOCALE, as follows: +/* ISDIGIT differs from isdigit, as follows: - Its arg may be any int or unsigned int; it need not be an unsigned char. - It's guaranteed to evaluate its argument exactly once. - It's typically faster. POSIX says that only '0' through '9' are digits. Prefer ISDIGIT to - ISDIGIT_LOCALE unless it's important to use the locale's definition + isdigit unless it's important to use the locale's definition of `digit' even when the host does not conform to POSIX. */ #define ISDIGIT(c) ((unsigned int) (c) - '0' <= 9) -#include <string.h> - -#if USE_UNLOCKED_IO -# include "unlocked-io.h" -#endif - #if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 8) || __STRICT_ANSI__ # define __attribute__(x) #endif @@ -166,17 +160,17 @@ typedef struct table local_time_zone_table[3]; } parser_control; -#define PC (* (parser_control *) parm) -#define YYLEX_PARAM parm -#define YYPARSE_PARAM parm - -static int yyerror (); -static int yylex (); +union YYSTYPE; +static int yylex (union YYSTYPE *, parser_control *); +static int yyerror (parser_control *, char *); %} -/* We want a reentrant parser. */ -%pure_parser +/* We want a reentrant parser, even if the TZ manipulation and the calls to + localtime and gmtime are not reentrant. */ +%pure-parser +%parse-param { parser_control *pc } +%lex-param { parser_control *pc } /* This grammar has 13 shift/reduce conflicts. */ %expect 13 @@ -209,8 +203,8 @@ spec: timespec: '@' seconds { - PC.seconds = $2; - PC.timespec_seen = true; + pc->seconds = $2; + pc->timespec_seen = true; } ; @@ -221,104 +215,104 @@ items: item: time - { PC.times_seen++; } + { pc->times_seen++; } | local_zone - { PC.local_zones_seen++; } + { pc->local_zones_seen++; } | zone - { PC.zones_seen++; } + { pc->zones_seen++; } | date - { PC.dates_seen++; } + { pc->dates_seen++; } | day - { PC.days_seen++; } + { pc->days_seen++; } | rel - { PC.rels_seen++; } + { pc->rels_seen++; } | number ; time: tUNUMBER tMERIDIAN { - PC.hour = $1.value; - PC.minutes = 0; - PC.seconds.tv_sec = 0; - PC.seconds.tv_nsec = 0; - PC.meridian = $2; + pc->hour = $1.value; + pc->minutes = 0; + pc->seconds.tv_sec = 0; + pc->seconds.tv_nsec = 0; + pc->meridian = $2; } | tUNUMBER ':' tUNUMBER o_merid { - PC.hour = $1.value; - PC.minutes = $3.value; - PC.seconds.tv_sec = 0; - PC.seconds.tv_nsec = 0; - PC.meridian = $4; + pc->hour = $1.value; + pc->minutes = $3.value; + pc->seconds.tv_sec = 0; + pc->seconds.tv_nsec = 0; + pc->meridian = $4; } | tUNUMBER ':' tUNUMBER tSNUMBER { - PC.hour = $1.value; - PC.minutes = $3.value; - PC.seconds.tv_sec = 0; - PC.seconds.tv_nsec = 0; - PC.meridian = MER24; - PC.zones_seen++; - PC.time_zone = $4.value % 100 + ($4.value / 100) * 60; + pc->hour = $1.value; + pc->minutes = $3.value; + pc->seconds.tv_sec = 0; + pc->seconds.tv_nsec = 0; + pc->meridian = MER24; + pc->zones_seen++; + pc->time_zone = $4.value % 100 + ($4.value / 100) * 60; } | tUNUMBER ':' tUNUMBER ':' unsigned_seconds o_merid { - PC.hour = $1.value; - PC.minutes = $3.value; - PC.seconds = $5; - PC.meridian = $6; + pc->hour = $1.value; + pc->minutes = $3.value; + pc->seconds = $5; + pc->meridian = $6; } | tUNUMBER ':' tUNUMBER ':' unsigned_seconds tSNUMBER { - PC.hour = $1.value; - PC.minutes = $3.value; - PC.seconds = $5; - PC.meridian = MER24; - PC.zones_seen++; - PC.time_zone = $6.value % 100 + ($6.value / 100) * 60; + pc->hour = $1.value; + pc->minutes = $3.value; + pc->seconds = $5; + pc->meridian = MER24; + pc->zones_seen++; + pc->time_zone = $6.value % 100 + ($6.value / 100) * 60; } ; local_zone: tLOCAL_ZONE - { PC.local_isdst = $1; } + { pc->local_isdst = $1; } | tLOCAL_ZONE tDST - { PC.local_isdst = $1 < 0 ? 1 : $1 + 1; } + { pc->local_isdst = $1 < 0 ? 1 : $1 + 1; } ; zone: tZONE - { PC.time_zone = $1; } + { pc->time_zone = $1; } | tDAYZONE - { PC.time_zone = $1 + 60; } + { pc->time_zone = $1 + 60; } | tZONE tDST - { PC.time_zone = $1 + 60; } + { pc->time_zone = $1 + 60; } ; day: tDAY { - PC.day_ordinal = 1; - PC.day_number = $1; + pc->day_ordinal = 1; + pc->day_number = $1; } | tDAY ',' { - PC.day_ordinal = 1; - PC.day_number = $1; + pc->day_ordinal = 1; + pc->day_number = $1; } | tUNUMBER tDAY { - PC.day_ordinal = $1.value; - PC.day_number = $2; + pc->day_ordinal = $1.value; + pc->day_number = $2; } ; date: tUNUMBER '/' tUNUMBER { - PC.month = $1.value; - PC.day = $3.value; + pc->month = $1.value; + pc->day = $3.value; } | tUNUMBER '/' tUNUMBER '/' tUNUMBER { @@ -329,119 +323,119 @@ date: you want portability, use the ISO 8601 format. */ if (4 <= $1.digits) { - PC.year = $1; - PC.month = $3.value; - PC.day = $5.value; + pc->year = $1; + pc->month = $3.value; + pc->day = $5.value; } else { - PC.month = $1.value; - PC.day = $3.value; - PC.year = $5; + pc->month = $1.value; + pc->day = $3.value; + pc->year = $5; } } | tUNUMBER tSNUMBER tSNUMBER { /* ISO 8601 format. YYYY-MM-DD. */ - PC.year = $1; - PC.month = -$2.value; - PC.day = -$3.value; + pc->year = $1; + pc->month = -$2.value; + pc->day = -$3.value; } | tUNUMBER tMONTH tSNUMBER { /* e.g. 17-JUN-1992. */ - PC.day = $1.value; - PC.month = $2; - PC.year.value = -$3.value; - PC.year.digits = $3.digits; + pc->day = $1.value; + pc->month = $2; + pc->year.value = -$3.value; + pc->year.digits = $3.digits; } | tMONTH tSNUMBER tSNUMBER { /* e.g. JUN-17-1992. */ - PC.month = $1; - PC.day = -$2.value; - PC.year.value = -$3.value; - PC.year.digits = $3.digits; + pc->month = $1; + pc->day = -$2.value; + pc->year.value = -$3.value; + pc->year.digits = $3.digits; } | tMONTH tUNUMBER { - PC.month = $1; - PC.day = $2.value; + pc->month = $1; + pc->day = $2.value; } | tMONTH tUNUMBER ',' tUNUMBER { - PC.month = $1; - PC.day = $2.value; - PC.year = $4; + pc->month = $1; + pc->day = $2.value; + pc->year = $4; } | tUNUMBER tMONTH { - PC.day = $1.value; - PC.month = $2; + pc->day = $1.value; + pc->month = $2; } | tUNUMBER tMONTH tUNUMBER { - PC.day = $1.value; - PC.month = $2; - PC.year = $3; + pc->day = $1.value; + pc->month = $2; + pc->year = $3; } ; rel: relunit tAGO { - PC.rel_ns = -PC.rel_ns; - PC.rel_seconds = -PC.rel_seconds; - PC.rel_minutes = -PC.rel_minutes; - PC.rel_hour = -PC.rel_hour; - PC.rel_day = -PC.rel_day; - PC.rel_month = -PC.rel_month; - PC.rel_year = -PC.rel_year; + pc->rel_ns = -pc->rel_ns; + pc->rel_seconds = -pc->rel_seconds; + pc->rel_minutes = -pc->rel_minutes; + pc->rel_hour = -pc->rel_hour; + pc->rel_day = -pc->rel_day; + pc->rel_month = -pc->rel_month; + pc->rel_year = -pc->rel_year; } | relunit ; relunit: tUNUMBER tYEAR_UNIT - { PC.rel_year += $1.value * $2; } + { pc->rel_year += $1.value * $2; } | tSNUMBER tYEAR_UNIT - { PC.rel_year += $1.value * $2; } + { pc->rel_year += $1.value * $2; } | tYEAR_UNIT - { PC.rel_year += $1; } + { pc->rel_year += $1; } | tUNUMBER tMONTH_UNIT - { PC.rel_month += $1.value * $2; } + { pc->rel_month += $1.value * $2; } | tSNUMBER tMONTH_UNIT - { PC.rel_month += $1.value * $2; } + { pc->rel_month += $1.value * $2; } | tMONTH_UNIT - { PC.rel_month += $1; } + { pc->rel_month += $1; } | tUNUMBER tDAY_UNIT - { PC.rel_day += $1.value * $2; } + { pc->rel_day += $1.value * $2; } | tSNUMBER tDAY_UNIT - { PC.rel_day += $1.value * $2; } + { pc->rel_day += $1.value * $2; } | tDAY_UNIT - { PC.rel_day += $1; } + { pc->rel_day += $1; } | tUNUMBER tHOUR_UNIT - { PC.rel_hour += $1.value * $2; } + { pc->rel_hour += $1.value * $2; } | tSNUMBER tHOUR_UNIT - { PC.rel_hour += $1.value * $2; } + { pc->rel_hour += $1.value * $2; } | tHOUR_UNIT - { PC.rel_hour += $1; } + { pc->rel_hour += $1; } | tUNUMBER tMINUTE_UNIT - { PC.rel_minutes += $1.value * $2; } + { pc->rel_minutes += $1.value * $2; } | tSNUMBER tMINUTE_UNIT - { PC.rel_minutes += $1.value * $2; } + { pc->rel_minutes += $1.value * $2; } | tMINUTE_UNIT - { PC.rel_minutes += $1; } + { pc->rel_minutes += $1; } | tUNUMBER tSEC_UNIT - { PC.rel_seconds += $1.value * $2; } + { pc->rel_seconds += $1.value * $2; } | tSNUMBER tSEC_UNIT - { PC.rel_seconds += $1.value * $2; } + { pc->rel_seconds += $1.value * $2; } | tSDECIMAL_NUMBER tSEC_UNIT - { PC.rel_seconds += $1.tv_sec * $2; PC.rel_ns += $1.tv_nsec * $2; } + { pc->rel_seconds += $1.tv_sec * $2; pc->rel_ns += $1.tv_nsec * $2; } | tUDECIMAL_NUMBER tSEC_UNIT - { PC.rel_seconds += $1.tv_sec * $2; PC.rel_ns += $1.tv_nsec * $2; } + { pc->rel_seconds += $1.tv_sec * $2; pc->rel_ns += $1.tv_nsec * $2; } | tSEC_UNIT - { PC.rel_seconds += $1; } + { pc->rel_seconds += $1; } ; seconds: signed_seconds | unsigned_seconds; @@ -461,35 +455,35 @@ unsigned_seconds: number: tUNUMBER { - if (PC.dates_seen - && ! PC.rels_seen && (PC.times_seen || 2 < $1.digits)) - PC.year = $1; + if (pc->dates_seen + && ! pc->rels_seen && (pc->times_seen || 2 < $1.digits)) + pc->year = $1; else { if (4 < $1.digits) { - PC.dates_seen++; - PC.day = $1.value % 100; - PC.month = ($1.value / 100) % 100; - PC.year.value = $1.value / 10000; - PC.year.digits = $1.digits - 4; + pc->dates_seen++; + pc->day = $1.value % 100; + pc->month = ($1.value / 100) % 100; + pc->year.value = $1.value / 10000; + pc->year.digits = $1.digits - 4; } else { - PC.times_seen++; + pc->times_seen++; if ($1.digits <= 2) { - PC.hour = $1.value; - PC.minutes = 0; + pc->hour = $1.value; + pc->minutes = 0; } else { - PC.hour = $1.value / 100; - PC.minutes = $1.value % 100; + pc->hour = $1.value / 100; + pc->minutes = $1.value % 100; } - PC.seconds.tv_sec = 0; - PC.seconds.tv_nsec = 0; - PC.meridian = MER24; + pc->seconds.tv_sec = 0; + pc->seconds.tv_nsec = 0; + pc->meridian = MER24; } } } @@ -510,7 +504,7 @@ static table const meridian_table[] = { "A.M.", tMERIDIAN, MERam }, { "PM", tMERIDIAN, MERpm }, { "P.M.", tMERIDIAN, MERpm }, - { 0, 0, 0 } + { NULL, 0, 0 } }; static table const dst_table[] = @@ -544,7 +538,7 @@ static table const month_and_day_table[] = { "THURS", tDAY, 4 }, { "FRIDAY", tDAY, 5 }, { "SATURDAY", tDAY, 6 }, - { 0, 0, 0 } + { NULL, 0, 0 } }; static table const time_units_table[] = @@ -559,7 +553,7 @@ static table const time_units_table[] = { "MIN", tMINUTE_UNIT, 1 }, { "SECOND", tSEC_UNIT, 1 }, { "SEC", tSEC_UNIT, 1 }, - { 0, 0, 0 } + { NULL, 0, 0 } }; /* Assorted relative-time words. */ @@ -585,7 +579,7 @@ static table const relative_time_table[] = { "ELEVENTH", tUNUMBER, 11 }, { "TWELFTH", tUNUMBER, 12 }, { "AGO", tAGO, 1 }, - { 0, 0, 0 } + { NULL, 0, 0 } }; /* The time zone table. This table is necessarily incomplete, as time @@ -645,7 +639,7 @@ static table const time_zone_table[] = { "GST", tZONE, HOUR (10) }, /* Guam Standard */ { "NZST", tZONE, HOUR (12) }, /* New Zealand Standard */ { "NZDT", tDAYZONE, HOUR (12) }, /* New Zealand Daylight */ - { 0, 0, 0 } + { NULL, 0, 0 } }; /* Military time zone table. */ @@ -676,7 +670,7 @@ static table const military_table[] = { "X", tZONE, HOUR (11) }, { "Y", tZONE, HOUR (12) }, { "Z", tZONE, HOUR ( 0) }, - { 0, 0, 0 } + { NULL, 0, 0 } }; @@ -686,16 +680,14 @@ to_hour (long int hours, int meridian) { switch (meridian) { + default: /* Pacify GCC. */ case MER24: return 0 <= hours && hours < 24 ? hours : -1; case MERam: return 0 < hours && hours < 12 ? hours : hours == 12 ? 0 : -1; case MERpm: return 0 < hours && hours < 12 ? hours + 12 : hours == 12 ? 12 : -1; - default: - abort (); } - /* NOTREACHED */ } static long int @@ -728,7 +720,7 @@ lookup_zone (parser_control const *pc, char const *name) if (strcmp (name, tp->name) == 0) return tp; - return 0; + return NULL; } #if ! HAVE_TM_GMTOFF @@ -827,7 +819,7 @@ lookup_word (parser_control const *pc, char *word) if (period_found && (tp = lookup_zone (pc, word))) return tp; - return 0; + return NULL; } static int @@ -993,11 +985,56 @@ yylex (YYSTYPE *lvalp, parser_control *pc) /* Do nothing if the parser reports an error. */ static int -yyerror (char *s ATTRIBUTE_UNUSED) +yyerror (parser_control *pc ATTRIBUTE_UNUSED, char *s ATTRIBUTE_UNUSED) { return 0; } +/* If *TM0 is the old and *TM1 is the new value of a struct tm after + passing it to mktime, return true if it's OK that mktime returned T. + It's not OK if *TM0 has out-of-range members. */ + +static bool +mktime_ok (struct tm const *tm0, struct tm const *tm1, time_t t) +{ + if (t == (time_t) -1) + { + /* Guard against falsely reporting an error when parsing a time + stamp that happens to equal (time_t) -1, on a host that + supports such a time stamp. */ + tm1 = localtime (&t); + if (!tm1) + return false; + } + + return ! ((tm0->tm_sec ^ tm1->tm_sec) + | (tm0->tm_min ^ tm1->tm_min) + | (tm0->tm_hour ^ tm1->tm_hour) + | (tm0->tm_mday ^ tm1->tm_mday) + | (tm0->tm_mon ^ tm1->tm_mon) + | (tm0->tm_year ^ tm1->tm_year)); +} + +/* A reasonable upper bound for the size of ordinary TZ strings. + Use heap allocation if TZ's length exceeds this. */ +enum { TZBUFSIZE = 100 }; + +/* Return a copy of TZ, stored in TZBUF if it fits, and heap-allocated + otherwise. */ +static char * +get_tz (char tzbuf[TZBUFSIZE]) +{ + char *tz = getenv ("TZ"); + if (tz) + { + size_t tzsize = strlen (tz) + 1; + tz = (tzsize <= TZBUFSIZE + ? memcpy (tzbuf, tz, tzsize) + : xmemdup (tz, tzsize)); + } + return tz; +} + /* Parse a date/time string, storing the resulting time value into *RESULT. The string itself is pointed to by P. Return true if successful. P can be an incomplete or relative time specification; if so, use @@ -1012,6 +1049,11 @@ get_date (struct timespec *result, char const *p, struct timespec const *now) struct tm tm0; parser_control pc; struct timespec gettime_buffer; + unsigned char c; + bool tz_was_altered = false; + char *tz0 = NULL; + char tz0buf[TZBUFSIZE]; + bool ok = true; if (! now) { @@ -1027,6 +1069,44 @@ get_date (struct timespec *result, char const *p, struct timespec const *now) if (! tmp) return false; + while (c = *p, ISSPACE (c)) + p++; + + if (strncmp (p, "TZ=\"", 4) == 0) + { + char const *tzbase = p + 4; + size_t tzsize = 1; + char const *s; + + for (s = tzbase; *s; s++, tzsize++) + if (*s == '\\') + { + s++; + if (! (*s == '\\' || *s == '"')) + break; + } + else if (*s == '"') + { + char *z; + char *tz1; + char tz1buf[TZBUFSIZE]; + bool large_tz = TZBUFSIZE < tzsize; + bool setenv_ok; + tz0 = get_tz (tz0buf); + z = tz1 = large_tz ? xmalloc (tzsize) : tz1buf; + for (s = tzbase; *s != '"'; s++) + *z++ = *(s += *s == '\\'); + *z = '\0'; + setenv_ok = setenv ("TZ", tz1, 1) == 0; + if (large_tz) + free (tz1); + if (!setenv_ok) + goto fail; + tz_was_altered = true; + p = s + 1; + } + } + pc.input = p; pc.year.value = tmp->tm_year; pc.year.value += TM_YEAR_BASE; @@ -1059,7 +1139,7 @@ get_date (struct timespec *result, char const *p, struct timespec const *now) pc.local_time_zone_table[0].name = tmp->tm_zone; pc.local_time_zone_table[0].type = tLOCAL_ZONE; pc.local_time_zone_table[0].value = tmp->tm_isdst; - pc.local_time_zone_table[1].name = 0; + pc.local_time_zone_table[1].name = NULL; /* Probe the names used in the next three calendar quarters, looking for a tm_isdst different from the one we already have. */ @@ -1076,7 +1156,7 @@ get_date (struct timespec *result, char const *p, struct timespec const *now) pc.local_time_zone_table[1].name = probe_tm->tm_zone; pc.local_time_zone_table[1].type = tLOCAL_ZONE; pc.local_time_zone_table[1].value = probe_tm->tm_isdst; - pc.local_time_zone_table[2].name = 0; + pc.local_time_zone_table[2].name = NULL; } break; } @@ -1095,10 +1175,10 @@ get_date (struct timespec *result, char const *p, struct timespec const *now) pc.local_time_zone_table[i].type = tLOCAL_ZONE; pc.local_time_zone_table[i].value = i; } - pc.local_time_zone_table[i].name = 0; + pc.local_time_zone_table[i].name = NULL; } #else - pc.local_time_zone_table[0].name = 0; + pc.local_time_zone_table[0].name = NULL; #endif #endif @@ -1110,146 +1190,177 @@ get_date (struct timespec *result, char const *p, struct timespec const *now) daylight times. So if we see that abbreviation, we don't know whether it's daylight time. */ pc.local_time_zone_table[0].value = -1; - pc.local_time_zone_table[1].name = 0; + pc.local_time_zone_table[1].name = NULL; } if (yyparse (&pc) != 0) - return false; + goto fail; if (pc.timespec_seen) - { - *result = pc.seconds; - return true; - } - - if (1 < pc.times_seen || 1 < pc.dates_seen || 1 < pc.days_seen - || 1 < (pc.local_zones_seen + pc.zones_seen) - || (pc.local_zones_seen && 1 < pc.local_isdst)) - return false; - - tm.tm_year = to_year (pc.year) - TM_YEAR_BASE + pc.rel_year; - tm.tm_mon = pc.month - 1 + pc.rel_month; - tm.tm_mday = pc.day + pc.rel_day; - if (pc.times_seen || (pc.rels_seen && ! pc.dates_seen && ! pc.days_seen)) - { - tm.tm_hour = to_hour (pc.hour, pc.meridian); - if (tm.tm_hour < 0) - return false; - tm.tm_min = pc.minutes; - tm.tm_sec = pc.seconds.tv_sec; - } + *result = pc.seconds; else { - tm.tm_hour = tm.tm_min = tm.tm_sec = 0; - pc.seconds.tv_nsec = 0; - } - - /* Let mktime deduce tm_isdst if we have an absolute time stamp, - or if the relative time stamp mentions days, months, or years. */ - if (pc.dates_seen | pc.days_seen | pc.times_seen | pc.rel_day - | pc.rel_month | pc.rel_year) - tm.tm_isdst = -1; + if (1 < pc.times_seen || 1 < pc.dates_seen || 1 < pc.days_seen + || 1 < (pc.local_zones_seen + pc.zones_seen) + || (pc.local_zones_seen && 1 < pc.local_isdst)) + goto fail; + + tm.tm_year = to_year (pc.year) - TM_YEAR_BASE; + tm.tm_mon = pc.month - 1; + tm.tm_mday = pc.day; + if (pc.times_seen || (pc.rels_seen && ! pc.dates_seen && ! pc.days_seen)) + { + tm.tm_hour = to_hour (pc.hour, pc.meridian); + if (tm.tm_hour < 0) + goto fail; + tm.tm_min = pc.minutes; + tm.tm_sec = pc.seconds.tv_sec; + } + else + { + tm.tm_hour = tm.tm_min = tm.tm_sec = 0; + pc.seconds.tv_nsec = 0; + } - /* But if the input explicitly specifies local time with or without - DST, give mktime that information. */ - if (pc.local_zones_seen) - tm.tm_isdst = pc.local_isdst; + /* Let mktime deduce tm_isdst if we have an absolute time stamp. */ + if (pc.dates_seen | pc.days_seen | pc.times_seen) + tm.tm_isdst = -1; - tm0 = tm; + /* But if the input explicitly specifies local time with or without + DST, give mktime that information. */ + if (pc.local_zones_seen) + tm.tm_isdst = pc.local_isdst; - Start = mktime (&tm); + tm0 = tm; - if (Start == (time_t) -1) - { + Start = mktime (&tm); - /* Guard against falsely reporting errors near the time_t boundaries - when parsing times in other time zones. For example, if the min - time_t value is 1970-01-01 00:00:00 UTC and we are 8 hours ahead - of UTC, then the min localtime value is 1970-01-01 08:00:00; if - we apply mktime to 1970-01-01 00:00:00 we will get an error, so - we apply mktime to 1970-01-02 08:00:00 instead and adjust the time - zone by 24 hours to compensate. This algorithm assumes that - there is no DST transition within a day of the time_t boundaries. */ - if (pc.zones_seen) + if (! mktime_ok (&tm0, &tm, Start)) { - tm = tm0; - if (tm.tm_year <= EPOCH_YEAR - TM_YEAR_BASE) - { - tm.tm_mday++; - pc.time_zone += 24 * 60; - } + if (! pc.zones_seen) + goto fail; else { - tm.tm_mday--; - pc.time_zone -= 24 * 60; + /* Guard against falsely reporting errors near the time_t + boundaries when parsing times in other time zones. For + example, suppose the input string "1969-12-31 23:00:00 -0100", + the current time zone is 8 hours ahead of UTC, and the min + time_t value is 1970-01-01 00:00:00 UTC. Then the min + localtime value is 1970-01-01 08:00:00, and mktime will + therefore fail on 1969-12-31 23:00:00. To work around the + problem, set the time zone to 1 hour behind UTC temporarily + by setting TZ="XXX1:00" and try mktime again. */ + + long int time_zone = pc.time_zone; + long int abs_time_zone = time_zone < 0 ? - time_zone : time_zone; + long int abs_time_zone_hour = abs_time_zone / 60; + int abs_time_zone_min = abs_time_zone % 60; + char tz1buf[sizeof "XXX+0:00" + + sizeof pc.time_zone * CHAR_BIT / 3]; + if (!tz_was_altered) + tz0 = get_tz (tz0buf); + sprintf (tz1buf, "XXX%s%ld:%02d", "-" + (time_zone < 0), + abs_time_zone_hour, abs_time_zone_min); + if (setenv ("TZ", tz1buf, 1) != 0) + goto fail; + tz_was_altered = true; + tm = tm0; + Start = mktime (&tm); + if (! mktime_ok (&tm0, &tm, Start)) + goto fail; } - Start = mktime (&tm); } - if (Start == (time_t) -1) - return false; - } - - if (pc.days_seen && ! pc.dates_seen) - { - tm.tm_mday += ((pc.day_number - tm.tm_wday + 7) % 7 - + 7 * (pc.day_ordinal - (0 < pc.day_ordinal))); - tm.tm_isdst = -1; - Start = mktime (&tm); - if (Start == (time_t) -1) - return false; - } + if (pc.days_seen && ! pc.dates_seen) + { + tm.tm_mday += ((pc.day_number - tm.tm_wday + 7) % 7 + + 7 * (pc.day_ordinal - (0 < pc.day_ordinal))); + tm.tm_isdst = -1; + Start = mktime (&tm); + if (Start == (time_t) -1) + goto fail; + } - if (pc.zones_seen) - { - long int delta = pc.time_zone * 60; - time_t t1; + if (pc.zones_seen) + { + long int delta = pc.time_zone * 60; + time_t t1; #ifdef HAVE_TM_GMTOFF - delta -= tm.tm_gmtoff; + delta -= tm.tm_gmtoff; #else - time_t t = Start; - struct tm const *gmt = gmtime (&t); - if (! gmt) - return false; - delta -= tm_diff (&tm, gmt); + time_t t = Start; + struct tm const *gmt = gmtime (&t); + if (! gmt) + goto fail; + delta -= tm_diff (&tm, gmt); #endif - t1 = Start - delta; - if ((Start < t1) != (delta < 0)) - return false; /* time_t overflow */ - Start = t1; + t1 = Start - delta; + if ((Start < t1) != (delta < 0)) + goto fail; /* time_t overflow */ + Start = t1; + } + + /* Add relative date. */ + if (pc.rel_year | pc.rel_month | pc.rel_day) + { + int year = tm.tm_year + pc.rel_year; + int month = tm.tm_mon + pc.rel_month; + int day = tm.tm_mday + pc.rel_day; + if (((year < tm.tm_year) ^ (pc.rel_year < 0)) + | (month < tm.tm_mon) ^ (pc.rel_month < 0) + | (day < tm.tm_mday) ^ (pc.rel_day < 0)) + goto fail; + tm.tm_year = year; + tm.tm_mon = month; + tm.tm_mday = day; + Start = mktime (&tm); + if (Start == (time_t) -1) + goto fail; + } + + /* Add relative hours, minutes, and seconds. On hosts that support + leap seconds, ignore the possibility of leap seconds; e.g., + "+ 10 minutes" adds 600 seconds, even if one of them is a + leap second. Typically this is not what the user wants, but it's + too hard to do it the other way, because the time zone indicator + must be applied before relative times, and if mktime is applied + again the time zone will be lost. */ + { + long int sum_ns = pc.seconds.tv_nsec + pc.rel_ns; + long int normalized_ns = (sum_ns % BILLION + BILLION) % BILLION; + time_t t0 = Start; + long int d1 = 60 * 60 * pc.rel_hour; + time_t t1 = t0 + d1; + long int d2 = 60 * pc.rel_minutes; + time_t t2 = t1 + d2; + long int d3 = pc.rel_seconds; + time_t t3 = t2 + d3; + long int d4 = (sum_ns - normalized_ns) / BILLION; + time_t t4 = t3 + d4; + + if ((d1 / (60 * 60) ^ pc.rel_hour) + | (d2 / 60 ^ pc.rel_minutes) + | ((t1 < t0) ^ (d1 < 0)) + | ((t2 < t1) ^ (d2 < 0)) + | ((t3 < t2) ^ (d3 < 0)) + | ((t4 < t3) ^ (d4 < 0))) + goto fail; + + result->tv_sec = t4; + result->tv_nsec = normalized_ns; + } } - /* Add relative hours, minutes, and seconds. Ignore leap seconds; - i.e. "+ 10 minutes" means 600 seconds, even if one of them is a - leap second. Typically this is not what the user wants, but it's - too hard to do it the other way, because the time zone indicator - must be applied before relative times, and if mktime is applied - again the time zone will be lost. */ - { - long int sum_ns = pc.seconds.tv_nsec + pc.rel_ns; - long int normalized_ns = (sum_ns % BILLION + BILLION) % BILLION; - time_t t0 = Start; - long int d1 = 60 * 60 * pc.rel_hour; - time_t t1 = t0 + d1; - long int d2 = 60 * pc.rel_minutes; - time_t t2 = t1 + d2; - long int d3 = pc.rel_seconds; - time_t t3 = t2 + d3; - long int d4 = (sum_ns - normalized_ns) / BILLION; - time_t t4 = t3 + d4; - - if ((d1 / (60 * 60) ^ pc.rel_hour) - | (d2 / 60 ^ pc.rel_minutes) - | ((t1 < t0) ^ (d1 < 0)) - | ((t2 < t1) ^ (d2 < 0)) - | ((t3 < t2) ^ (d3 < 0)) - | ((t4 < t3) ^ (d4 < 0))) - return false; - - result->tv_sec = t4; - result->tv_nsec = normalized_ns; - return true; - } + goto done; + + fail: + ok = false; + done: + if (tz_was_altered) + ok &= (tz0 ? setenv ("TZ", tz0, 1) : unsetenv ("TZ")) == 0; + if (tz0 != tz0buf) + free (tz0); + return ok; } #if TEST @@ -1264,7 +1375,7 @@ main (int ac, char **av) printf ("Enter date, or blank line to exit.\n\t> "); fflush (stdout); - buff[BUFSIZ - 1] = 0; + buff[BUFSIZ - 1] = '\0'; while (fgets (buff, BUFSIZ - 1, stdin) && buff[0]) { struct timespec d; @@ -1288,4 +1399,4 @@ main (int ac, char **av) } return 0; } -#endif /* defined TEST */ +#endif /* TEST */ diff --git a/lib/getpagesize.h b/lib/getpagesize.h index 80d9eb3be..e63c1c708 100644 --- a/lib/getpagesize.h +++ b/lib/getpagesize.h @@ -1,5 +1,5 @@ /* Emulate getpagesize on systems that lack it. - Copyright (C) 1999, 2000 Free Software Foundation, Inc. + Copyright (C) 1999, 2000, 2004 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -44,7 +44,7 @@ # endif #endif -#ifndef getpagesize +#if !defined getpagesize && HAVE_SYS_PARAM_H # include <sys/param.h> # ifdef EXEC_PAGESIZE # define getpagesize() EXEC_PAGESIZE diff --git a/lib/mktime.c b/lib/mktime.c index a017a51c8..772b4f347 100644 --- a/lib/mktime.c +++ b/lib/mktime.c @@ -495,7 +495,6 @@ not_equal_tm (const struct tm *a, const struct tm *b) | (a->tm_mday ^ b->tm_mday) | (a->tm_mon ^ b->tm_mon) | (a->tm_year ^ b->tm_year) - | (a->tm_mday ^ b->tm_mday) | (a->tm_yday ^ b->tm_yday) | (a->tm_isdst ^ b->tm_isdst)); } diff --git a/m4/getpagesize.m4 b/m4/getpagesize.m4 index f1f4c42d4..a2f818b62 100644 --- a/m4/getpagesize.m4 +++ b/m4/getpagesize.m4 @@ -1,5 +1,5 @@ -# getpagesize.m4 serial 1 -dnl Copyright (C) 2002 Free Software Foundation, Inc. +# getpagesize.m4 serial 2 +dnl Copyright (C) 2002, 2004 Free Software Foundation, Inc. dnl This file is free software, distributed under the terms of the GNU dnl General Public License. As a special exception to the GNU General dnl Public License, this file may be distributed as part of a program @@ -9,7 +9,7 @@ dnl the same distribution terms as the rest of that program. AC_DEFUN([gl_GETPAGESIZE], [ dnl Prerequisites of lib/getpagesize.h. - AC_CHECK_HEADERS_ONCE(unistd.h) + AC_CHECK_HEADERS_ONCE(sys/param.h unistd.h) AC_CHECK_HEADERS(OS.h) AC_CHECK_FUNCS(getpagesize) ]) |