summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--ChangeLog17
-rw-r--r--bootstrap.conf4
-rw-r--r--src/Makefile.am5
-rw-r--r--src/maketime.c449
-rw-r--r--src/maketime.h28
-rw-r--r--src/partime.c894
-rw-r--r--src/partime.h66
-rw-r--r--src/patch.c4
-rw-r--r--src/util.c40
-rw-r--r--src/util.h2
-rw-r--r--tests/preserve-mode-and-timestamp6
12 files changed, 50 insertions, 1466 deletions
diff --git a/.gitignore b/.gitignore
index 5caf381..4c2012b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -27,3 +27,4 @@
Makefile
Makefile.in
build-aux
+doc
diff --git a/ChangeLog b/ChangeLog
index af05730..e4a5783 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,20 @@
+2010-04-21 Andreas Gruenbacher <agruen@suse.de>
+
+ * src/Makefile.am (patch_SOURCES): Remove maketime.c, maketime.h,
+ partime.c, and partime.h.
+ (patch_LDFLAGS): Add LIB_CLOCK_GETTIME for clock_gettime().
+ * src/util.c: Replace maketime.h and partime.h with getdate.h.
+ (fetchname): Replace str2time() with get_date(). Sanitize the test
+ for epoch timestamps.
+ * src/patch.c (main): When --set-utc is given, set the TZ environment
+ variable to UTC so that get_date() will default to UTC timestamps.
+ * tests/preserve-mode-and-timestamp: Switching to get_date() revealed
+ that str2time() was ignoring timezones. In order to test --set-utc
+ now that get_date() fixes this, we must use timestamps that do not
+ include timestamps here.
+ * bootstrap.conf: Add the clock-time, getdate, gettime, and setenv
+ modules.
+
2010-04-20 Andreas Gruenbacher <agruen@suse.de>
* src/pch.h (pch_timestamp): Return timestamp as struct timespec
diff --git a/bootstrap.conf b/bootstrap.conf
index ae97aa9..bf624f3 100644
--- a/bootstrap.conf
+++ b/bootstrap.conf
@@ -20,13 +20,16 @@
gnulib_modules="
argmatch
backupfile
+clock-time
diffseq
dirname
dup2
exitfail
extensions
full-write
+getdate
getopt-gnu
+gettime
git-version-gen
hash
malloc
@@ -36,6 +39,7 @@ quotearg
realloc
rename
rmdir
+setenv
ssize_t
stdbool
stdlib
diff --git a/src/Makefile.am b/src/Makefile.am
index 80e8bc2..b404950 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -22,10 +22,6 @@ patch_SOURCES = \
common.h \
inp.c \
inp.h \
- maketime.c \
- maketime.h \
- partime.c \
- partime.h \
patch.c \
pch.c \
pch.h \
@@ -35,6 +31,7 @@ patch_SOURCES = \
version.h
AM_CPPFLAGS = -I$(top_builddir)/lib -I$(top_srcdir)/lib
+patch_LDFLAGS = $(LIB_CLOCK_GETTIME)
patch_LDADD = $(top_builddir)/lib/libpatch.a
if ENABLE_MERGE
diff --git a/src/maketime.c b/src/maketime.c
deleted file mode 100644
index 2d095c7..0000000
--- a/src/maketime.c
+++ /dev/null
@@ -1,449 +0,0 @@
-/* Convert struct partime into time_t. */
-
-/* Copyright (C) 1992, 1993, 1994, 1995, 1997, 2003, 2006 Paul Eggert
- Distributed under license by the Free Software Foundation, Inc.
- Copyright (C) 2010 Free Software Foundation, Inc..
-
- This file is also part of RCS.
-
- 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
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program 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 General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>. */
-
-/* For maximum portability, use only localtime and gmtime.
- Make no assumptions about the time_t epoch or the range of time_t values.
- Avoid mktime because it's not universal and because there's no easy,
- portable way for mktime to yield the inverse of gmtime. */
-
-#if HAVE_CONFIG_H
-# include <config.h>
-#endif
-
-#include <maketime.h>
-
-#include <limits.h>
-#include <partime.h>
-#include <stdlib.h>
-#include <time.h>
-
-/* Suppose A1 + B1 = SUM1, using 2's complement arithmetic ignoring overflow.
- Suppose A, B and SUM have the same respective signs as A1, B1, and SUM1.
- Then this yields nonzero if overflow occurred during the addition.
- Overflow occurs if A and B have the same sign, but A and SUM differ in sign.
- Use `^' to test whether signs differ, and `< 0' to isolate the sign. */
-#define overflow_sum_sign(a, b, sum) ((~((a) ^ (b)) & ((a) ^ (sum))) < 0)
-
-/* Quotient and remainder when dividing A by B,
- truncating towards minus infinity, where B is positive. */
-#define DIV(a, b) ((a) / (b) - ((a) % (b) < 0))
-#define MOD(a, b) ((a) % (b) + (b) * ((a) % (b) < 0))
-
-/* Number of days in 400 consecutive Gregorian years. */
-#define Y400_DAYS (365 * 400L + 100 - 4 + 1)
-
-/* Number of years to add to tm_year to get Gregorian year. */
-#define TM_YEAR_ORIGIN 1900
-
-static int
-isleap (int y)
-{
- return (y & 3) == 0 && (y % 100 != 0 || y % 400 == 0);
-}
-
-/* days in year before start of months 0-12 */
-static int const month_yday[] =
-{
- 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365
-};
-
-/* Yield the number of days in TM's month. */
-static int
-month_days (struct tm const *tm)
-{
- int m = tm->tm_mon;
- return (month_yday[m + 1] - month_yday[m]
- + (m == 1 && isleap (tm->tm_year + TM_YEAR_ORIGIN)));
-}
-
-/* Convert UNIXTIME to struct tm form.
- Use gmtime if available and if !LOCALZONE, localtime otherwise. */
-struct tm *
-time2tm (time_t unixtime, int localzone)
-{
- struct tm *tm;
-#ifdef TZ_is_unset
- static char const *TZ;
- if (!TZ && !(TZ = getenv ("TZ")))
- TZ_is_unset ("The TZ environment variable is not set; please set it to your timezone");
-#endif
- if (localzone || !(tm = gmtime (&unixtime)))
- tm = localtime (&unixtime);
- return tm;
-}
-
-/* Yield A - B, measured in seconds. */
-time_t
-difftm (struct tm const *a, struct tm const *b)
-{
- int ay = a->tm_year + (TM_YEAR_ORIGIN - 1);
- int by = b->tm_year + (TM_YEAR_ORIGIN - 1);
- int ac = DIV (ay, 100);
- int bc = DIV (by, 100);
- int difference_in_day_of_year = a->tm_yday - b->tm_yday;
- int intervening_leap_days = (((ay >> 2) - (by >> 2))
- - (ac - bc)
- + ((ac >> 2) - (bc >> 2)));
- time_t difference_in_years = ay - by;
- time_t difference_in_days
- = (difference_in_years * 365
- + (intervening_leap_days + difference_in_day_of_year));
- return (((((difference_in_days * 24
- + (a->tm_hour - b->tm_hour))
- * 60)
- + (a->tm_min - b->tm_min))
- * 60)
- + (a->tm_sec - b->tm_sec));
-}
-
-/* Adjust time T by adding SECONDS.
- The absolute value of SECONDS cannot exceed 59 * INT_MAX,
- and also cannot exceed one month's worth of seconds;
- this is enough to handle any POSIX or real-life daylight-saving offset.
- Adjust only T's year, mon, mday, hour, min and sec members;
- plus adjust wday if it is defined. */
-void
-adjzone (register struct tm *t, long seconds)
-{
- int days = 0;
-
- /* This code can be off by a second if SECONDS is not a multiple of 60,
- if T is local time, and if a leap second happens during this minute.
- But this bug has never occurred, and most likely will not ever occur.
- Liberia, the last country for which SECONDS % 60 was nonzero,
- switched to UTC in May 1972; the first leap second was in June 1972. */
- int leap_second = t->tm_sec == 60;
- long sec = seconds + (t->tm_sec - leap_second);
- if (sec < 0)
- {
- if ((t->tm_min -= (59 - sec) / 60) < 0
- && (t->tm_hour -= (59 - t->tm_min) / 60) < 0)
- {
- days = - ((23 - t->tm_hour) / 24);
- if ((t->tm_mday += days) <= 0)
- {
- if (--t->tm_mon < 0)
- {
- --t->tm_year;
- t->tm_mon = 11;
- }
- t->tm_mday += month_days (t);
- }
- }
- }
- else
- {
- if (60 <= (t->tm_min += sec / 60)
- && (24 <= (t->tm_hour += t->tm_min / 60)))
- {
- days = t->tm_hour / 24;
- if (month_days (t) < (t->tm_mday += days))
- {
- if (11 < ++t->tm_mon)
- {
- ++t->tm_year;
- t->tm_mon = 0;
- }
- t->tm_mday = 1;
- }
- }
- }
- if (TM_DEFINED (t->tm_wday))
- t->tm_wday = MOD (t->tm_wday + days, 7);
- t->tm_hour = MOD (t->tm_hour, 24);
- t->tm_min = MOD (t->tm_min, 60);
- t->tm_sec = (int) MOD (sec, 60) + leap_second;
-}
-
-/* Convert TM to time_t, using localtime if LOCALZONE and gmtime otherwise.
- Use only TM's year, mon, mday, hour, min, and sec members.
- Ignore TM's old tm_yday and tm_wday, but fill in their correct values.
- Yield -1 on failure (e.g. a member out of range).
- POSIX 1003.1 doesn't allow leap seconds, but some implementations
- have them anyway, so allow them if localtime/gmtime does. */
-time_t
-tm2time (struct tm *tm, int localzone)
-{
- /* Cache the most recent t,tm pairs; 1 for gmtime, 1 for localtime. */
- static time_t t_cache[2];
- static struct tm tm_cache[2];
-
- time_t d, gt;
- struct tm const *gtm;
- /* The maximum number of iterations should be enough to handle any
- combinations of leap seconds, time zone rule changes, and solar time.
- 4 is probably enough; we use a bigger number just to be safe. */
- int remaining_tries = 8;
-
- /* Avoid subscript errors. */
- if (12 <= (unsigned) tm->tm_mon)
- return -1;
-
- tm->tm_yday = month_yday[tm->tm_mon] + tm->tm_mday
- - (tm->tm_mon < 2 || !isleap (tm->tm_year + TM_YEAR_ORIGIN));
-
- /* Make a first guess. */
- gt = t_cache[localzone];
- gtm = gt ? &tm_cache[localzone] : time2tm (gt, localzone);
-
- /* Repeatedly use the error from the guess to improve the guess. */
- while ((d = difftm (tm, gtm)) != 0)
- {
- if (--remaining_tries == 0)
- return -1;
- gt += d;
- gtm = time2tm (gt, localzone);
- }
-
- /* Check that the guess actually matches;
- overflow can cause difftm to yield 0 even on differing times,
- or tm may have members out of range (e.g. bad leap seconds). */
-#define TM_DIFFER(a,b) \
- ( \
- ((a)->tm_year ^ (b)->tm_year) | \
- ((a)->tm_mon ^ (b)->tm_mon) | \
- ((a)->tm_mday ^ (b)->tm_mday) | \
- ((a)->tm_hour ^ (b)->tm_hour) | \
- ((a)->tm_min ^ (b)->tm_min) | \
- ((a)->tm_sec ^ (b)->tm_sec) \
- )
- if (TM_DIFFER (tm, gtm))
- {
- /* If gt is a leap second, try gt+1; if it is one greater than
- a leap second, try gt-1; otherwise, it doesn't matter.
- Leap seconds always fall at month end. */
- int yd = tm->tm_year - gtm->tm_year;
- gt += yd + (yd ? 0 : tm->tm_mon - gtm->tm_mon);
- gtm = time2tm (gt, localzone);
- if (TM_DIFFER (tm, gtm))
- return -1;
- }
- t_cache[localzone] = gt;
- tm_cache[localzone] = *gtm;
-
- tm->tm_wday = gtm->tm_wday;
- return gt;
-}
-
-/* Check *PT and convert it to time_t.
- If it is incompletely specified, use DEFAULT_TIME to fill it out.
- Use localtime if PT->zone is the special value TM_LOCAL_ZONE.
- Yield -1 on failure.
- ISO 8601 day-of-year and week numbers are not yet supported. */
-static time_t
-maketime (struct partime const *pt, time_t default_time)
-{
- int localzone, wday, year;
- struct tm tm;
- struct tm *tm0 = 0;
- time_t r;
- int use_ordinal_day;
-
- tm0 = 0; /* Keep gcc -Wall happy. */
- localzone = pt->zone == TM_LOCAL_ZONE;
-
- tm = pt->tm;
- year = tm.tm_year;
- wday = tm.tm_wday;
- use_ordinal_day = (!TM_DEFINED (tm.tm_mday)
- && TM_DEFINED (wday) && TM_DEFINED (pt->wday_ordinal));
-
- if (use_ordinal_day || TM_DEFINED (pt->ymodulus) || !TM_DEFINED (year))
- {
- /* Get tm corresponding to default time. */
- tm0 = time2tm (default_time, localzone);
- if (!localzone)
- adjzone (tm0, pt->zone);
- }
-
- if (use_ordinal_day)
- tm.tm_mday = (tm0->tm_mday
- + ((wday - tm0->tm_wday + 7) % 7
- + 7 * (pt->wday_ordinal - (pt->wday_ordinal != 0))));
-
- if (TM_DEFINED (pt->ymodulus))
- {
- /* Yield a year closest to the default that has the given modulus. */
- int year0 = tm0->tm_year + TM_YEAR_ORIGIN;
- int y0 = MOD (year0, pt->ymodulus);
- int d = 2 * (year - y0);
- year += (((year0 - y0) / pt->ymodulus
- + (pt->ymodulus < d ? -1 : d < -pt->ymodulus))
- * pt->ymodulus);
- }
- else if (!TM_DEFINED (year))
- {
- /* Set default year, month, day from current time. */
- year = tm0->tm_year + TM_YEAR_ORIGIN;
- if (!TM_DEFINED (tm.tm_mon))
- {
- tm.tm_mon = tm0->tm_mon;
- if (!TM_DEFINED (tm.tm_mday))
- tm.tm_mday = tm0->tm_mday;
- }
- }
-
- /* Set remaining default fields to be their minimum values. */
- if (!TM_DEFINED (tm.tm_mon))
- tm.tm_mon = 0;
- if (!TM_DEFINED (tm.tm_mday))
- tm.tm_mday = 1;
- if (!TM_DEFINED (tm.tm_hour))
- tm.tm_hour = 0;
- if (!TM_DEFINED (tm.tm_min))
- tm.tm_min = 0;
- if (!TM_DEFINED (tm.tm_sec))
- tm.tm_sec = 0;
-
- tm.tm_year = year - TM_YEAR_ORIGIN;
- if (tm.tm_year != year - TM_YEAR_ORIGIN)
- return -1;
-
- if (!localzone)
- {
- adjzone (&tm, -pt->zone);
- wday = tm.tm_wday;
- }
-
- /* Convert and fill in the rest of the tm. */
- r = tm2time (&tm, localzone);
- if (r == -1)
- return r;
-
- /* Check weekday. */
- if (TM_DEFINED (wday) && wday != tm.tm_wday)
- return -1;
-
- /* Add relative time, except for seconds.
- We handle seconds separately, at the end,
- so that leap seconds are handled properly. */
- if (pt->tmr.tm_year | pt->tmr.tm_mon | pt->tmr.tm_mday
- | pt->tmr.tm_hour | pt->tmr.tm_min)
- {
- int years = tm.tm_year + pt->tmr.tm_year;
- int mons = tm.tm_mon + pt->tmr.tm_mon;
- int mdays = tm.tm_mday + pt->tmr.tm_mday;
- int hours = tm.tm_hour + pt->tmr.tm_hour;
- int mins = tm.tm_min + pt->tmr.tm_min;
-
- int carried_hours = DIV (mins, 60);
- int hours1 = hours + carried_hours;
- int carried_days = DIV (hours1, 24);
- int mdays1 = mdays + carried_days;
-
- int mon0 = MOD (mons, 12);
- int carried_years0 = DIV (mons, 12);
- int year0 = years + carried_years0;
- int yday0 = (month_yday[mon0]
- - (mon0 < 2 || !isleap (year0 + TM_YEAR_ORIGIN)));
-
- int yday1 = yday0 + mdays1;
- int carried_years1 = DIV (yday1, Y400_DAYS) * 400;
- int year1 = year0 + carried_years1;
- int yday2 = MOD (yday1, Y400_DAYS);
- int leap;
-
- if (overflow_sum_sign (tm.tm_year, pt->tmr.tm_year, years)
- | overflow_sum_sign (tm.tm_mon, pt->tmr.tm_mon, mons)
- | overflow_sum_sign (tm.tm_mday, pt->tmr.tm_mday, mdays)
- | overflow_sum_sign (tm.tm_hour, pt->tmr.tm_hour, hours)
- | overflow_sum_sign (tm.tm_min, pt->tmr.tm_min, mins)
- | overflow_sum_sign (hours, carried_hours, hours1)
- | overflow_sum_sign (mdays, carried_days, mdays1)
- | overflow_sum_sign (years, carried_years0, year0)
- | overflow_sum_sign (yday0, mdays1, yday1)
- | overflow_sum_sign (year0, carried_years1, year1))
- return -1;
-
- for (;;)
- {
- int days_per_year = 365 + (leap = isleap (year1 + TM_YEAR_ORIGIN));
- if (yday2 < days_per_year)
- break;
- yday2 -= days_per_year;
- year1++;
- }
-
- tm.tm_year = year1;
-
- {
- int mon;
- for (mon = 11;
- (tm.tm_mday = (yday2 - month_yday[mon] + (mon < 2 || !leap))) <= 0;
- mon--)
- /* do nothing */ ;
- tm.tm_mon = mon;
- }
-
- tm.tm_hour = MOD (hours1, 24);
- tm.tm_min = MOD (mins, 60);
-
- r = tm2time (&tm, localzone);
- if (r == -1)
- return r;
- }
-
- /* Add the seconds' part of relative time. */
- {
- time_t rs = r + pt->tmr.tm_sec;
- if ((pt->tmr.tm_sec < 0) != (rs < r))
- return -1;
- return rs;
- }
-}
-
-/* Parse a free-format date in *SOURCE, yielding a Unix format time.
- Update *SOURCE to point to the first character after the date.
- If *SOURCE is missing some information, take defaults from
- DEFAULT_TIME and DEFAULT_ZONE. *SOURCE may even be the empty
- string or an immediately invalid string, in which case the default
- time and zone is used.
- Return (time_t) -1 if the time is invalid or cannot be represented. */
-time_t
-str2time (char const **source, time_t default_time, long default_zone)
-{
- struct partime pt;
-
- *source = partime (*source, &pt);
- if (pt.zone == TM_UNDEFINED_ZONE)
- pt.zone = default_zone;
- return maketime (&pt, default_time);
-}
-
-#ifdef TEST
-#include <stdio.h>
-int
-main (int argc, char **argv)
-{
- time_t default_time = time (0);
- long default_zone = argv[1] ? atol (argv[1]) : TM_LOCAL_ZONE;
- char buf[1000];
- while (fgets (buf, sizeof (buf), stdin))
- {
- char const *p = buf;
- time_t t = str2time (&p, default_time, default_zone);
- printf ("`%.*s' -> %s",
- (int) (p - buf - (p[0] == '\0' && p[-1] == '\n')), buf,
- asctime ((argv[1] ? gmtime : localtime) (&t)));
- }
- return 0;
-}
-#endif
diff --git a/src/maketime.h b/src/maketime.h
deleted file mode 100644
index 77a2abe..0000000
--- a/src/maketime.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/* Yield time_t from struct partime yielded by partime. */
-
-/* Copyright (C) 1993, 1994, 1995, 2003, 2006 Paul Eggert
- Distributed under license by the Free Software Foundation, Inc.
- Copyright (C) 2010 Free Software Foundation, Inc..
-
- This file is also part of RCS.
-
- 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
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program 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 General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>. */
-
-#include <time.h>
-
-struct tm *time2tm (time_t, int);
-time_t difftm (struct tm const *, struct tm const *);
-time_t str2time (char const **, time_t, long);
-time_t tm2time (struct tm *, int);
-void adjzone (struct tm *, long);
diff --git a/src/partime.c b/src/partime.c
deleted file mode 100644
index 1528844..0000000
--- a/src/partime.c
+++ /dev/null
@@ -1,894 +0,0 @@
-/* Parse a string, yielding a struct partime that describes it. */
-
-/* Copyright (C) 1993, 1994, 1995, 1997, 2002, 2003, 2006 Paul Eggert
- Distributed under license by the Free Software Foundation, Inc.
- Copyright (C) 2009, 2010 Free Software Foundation, Inc.
-
- This file is also part of RCS.
-
- 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
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program 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 General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>. */
-
-#if HAVE_CONFIG_H
-# include <config.h>
-#endif
-
-#include <partime.h>
-
-#include <limits.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <time.h>
-
-#include <ctype.h>
-#if STDC_HEADERS
-# define CTYPE_DOMAIN(c) 1
-#else
-# define CTYPE_DOMAIN(c) ((unsigned) (c) <= 0177)
-#endif
-#define ISALNUM(c) (CTYPE_DOMAIN (c) && isalnum (c))
-#define ISALPHA(c) (CTYPE_DOMAIN (c) && isalpha (c))
-#define ISSPACE(c) (CTYPE_DOMAIN (c) && isspace (c))
-#define ISUPPER(c) (CTYPE_DOMAIN (c) && isupper (c))
-#define ISDIGIT(c) ((unsigned) (c) - '0' <= 9)
-
-
-/* Lookup tables for names of months, weekdays, time zones. */
-
-#define NAME_LENGTH_MAXIMUM 4
-
-struct name_val
- {
- char name[NAME_LENGTH_MAXIMUM];
- int val;
- };
-
-
-static char const *parse_pattern_letter (char const *, int, struct partime *);
-
-
-static struct name_val const month_names[] =
-{
- {"jan", 0},
- {"feb", 1},
- {"mar", 2},
- {"apr", 3},
- {"may", 4},
- {"jun", 5},
- {"jul", 6},
- {"aug", 7},
- {"sep", 8},
- {"oct", 9},
- {"nov", 10},
- {"dec", 11},
- {"", TM_UNDEFINED}
-};
-
-static struct name_val const weekday_names[] =
-{
- {"sun", 0},
- {"mon", 1},
- {"tue", 2},
- {"wed", 3},
- {"thu", 4},
- {"fri", 5},
- {"sat", 6},
- {"", TM_UNDEFINED}
-};
-
-#define RELATIVE_CONS(member, multiplier) \
- (offsetof (struct tm, member) + (multiplier) * sizeof (struct tm))
-#define RELATIVE_OFFSET(c) ((c) % sizeof (struct tm))
-#define RELATIVE_MULTIPLIER(c) ((c) / sizeof (struct tm))
-static struct name_val const relative_units[] =
-{
- {"year", RELATIVE_CONS (tm_year, 1) },
- {"mont", RELATIVE_CONS (tm_mon , 1) },
- {"fort", RELATIVE_CONS (tm_mday, 14) },
- {"week", RELATIVE_CONS (tm_mday, 7) },
- {"day" , RELATIVE_CONS (tm_mday, 1) },
- {"hour", RELATIVE_CONS (tm_hour, 1) },
- {"min" , RELATIVE_CONS (tm_min , 1) },
- {"sec" , RELATIVE_CONS (tm_sec , 1) },
- {"", TM_UNDEFINED}
-};
-
-static struct name_val const ago[] =
-{
- {"ago", 0},
- {"", TM_UNDEFINED}
-};
-
-static struct name_val const dst_names[] =
-{
- {"dst", 1},
- {"", 0}
-};
-
-#define hr60nonnegative(t) ((t)/100 * 60 + (t)%100)
-#define hr60(t) ((t) < 0 ? - hr60nonnegative (-(t)) : hr60nonnegative (t))
-#define zs(t, s) {s, hr60 (t)}
-#define zd(t, s, d) zs (t, s), zs ((t) + 100, d)
-
-static struct name_val const zone_names[] =
-{
- zs (-1000, "hst"), /* Hawaii */
- zd (-1000, "hast", "hadt"), /* Hawaii-Aleutian */
- zd (- 900, "akst", "akdt"), /* Alaska */
- zd (- 800, "pst" , "pdt" ), /* Pacific */
- zd (- 700, "mst" , "mdt" ), /* Mountain */
- zd (- 600, "cst" , "cdt" ), /* Central */
- zd (- 500, "est" , "edt" ), /* Eastern */
- zd (- 400, "ast" , "adt" ), /* Atlantic */
- zd (- 330, "nst" , "ndt" ), /* Newfoundland */
- zs ( 000, "utc" ), /* Coordinated Universal */
- zs ( 000, "uct" ), /* " */
- zs ( 000, "cut" ), /* " */
- zs ( 000, "ut"), /* Universal */
- zs ( 000, "z"), /* Zulu (required by ISO 8601) */
- zd ( 000, "gmt" , "bst" ), /* Greenwich Mean, British Summer */
- zd ( 000, "wet" , "west"), /* Western European */
- zd ( 100, "cet" , "cest"), /* Central European */
- zd ( 100, "met" , "mest"), /* Middle European (bug in old tz versions) */
- zd ( 100, "mez" , "mesz"), /* Mittel-Europaeische Zeit */
- zd ( 200, "eet" , "eest"), /* Eastern European */
- zs ( 530, "ist" ), /* India */
- zd ( 900, "jst" , "jdt" ), /* Japan */
- zd ( 900, "kst" , "kdt" ), /* Korea */
- zd ( 1200, "nzst", "nzdt"), /* New Zealand */
- {"lt", 1},
-#if 0
- /* The following names are duplicates or are not well attested.
- It's not worth keeping a complete list, since alphabetic time zone names
- are deprecated and there are lots more where these came from. */
- zs (-1100, "sst" ), /* Samoan */
- zd (- 900, "yst" , "ydt" ), /* Yukon - name is no longer used */
- zd (- 500, "ast" , "adt" ), /* Acre */
- zd (- 400, "wst" , "wdt" ), /* Western Brazil */
- zd (- 400, "cst" , "cdt" ), /* Chile */
- zd (- 200, "fst" , "fdt" ), /* Fernando de Noronha */
- zs ( 000, "wat" ), /* West African */
- zs ( 100, "cat" ), /* Central African */
- zs ( 200, "sat" ), /* South African */
- zd ( 200, "ist" , "idt" ), /* Israel */
- zs ( 300, "eat" ), /* East African */
- zd ( 300, "msk" , "msd" ), /* Moscow */
- zd ( 330, "ist" , "idt" ), /* Iran */
- zs ( 800, "hkt" ), /* Hong Kong */
- zs ( 800, "sgt" ), /* Singapore */
- zd ( 800, "cst" , "cdt" ), /* China */
- zd ( 800, "wst" , "wst" ), /* Western Australia */
- zd ( 930, "cst" , "cst" ), /* Central Australia */
- zs ( 1000, "gst" ), /* Guam */
- zd ( 1000, "est" , "est" ), /* Eastern Australia */
-#endif
- {"", -1}
-};
-
-/* Look for a prefix of S in TABLE, returning val for first matching entry. */
-static int
-lookup (char const *s, struct name_val const table[])
-{
- int j;
- char buf[NAME_LENGTH_MAXIMUM];
-
- for (j = 0; j < NAME_LENGTH_MAXIMUM; j++)
- {
- unsigned char c = *s;
- if (! ISALPHA (c))
- {
- buf[j] = '\0';
- break;
- }
- buf[j] = ISUPPER (c) ? tolower (c) : c;
- s++;
- s += *s == '.';
- }
-
- for (;; table++)
- for (j = 0; ; j++)
- if (j == NAME_LENGTH_MAXIMUM || ! table[0].name[j])
- return table[0].val;
- else if (buf[j] != table[0].name[j])
- break;
-}
-
-
-/* Set *T to ``undefined'' values. */
-static void
-undefine (struct partime *t)
-{
- t->tm.tm_sec = t->tm.tm_min = t->tm.tm_hour = t->tm.tm_mday = t->tm.tm_mon
- = t->tm.tm_year = t->tm.tm_wday = t->tm.tm_yday
- = t->wday_ordinal = t->ymodulus = t->yweek
- = TM_UNDEFINED;
- t->tmr.tm_sec = t->tmr.tm_min = t->tmr.tm_hour =
- t->tmr.tm_mday = t->tmr.tm_mon = t->tmr.tm_year = 0;
- t->zone = TM_UNDEFINED_ZONE;
-}
-
-/* Patterns to look for in a time string.
- Order is important: we look for the first matching pattern
- whose values do not contradict values that we already know about.
- See `parse_pattern_letter' below for the meaning of the pattern codes. */
-static char const time_patterns[] =
-{
- /* Traditional patterns come first,
- to prevent an ISO 8601 format from misinterpreting their prefixes. */
-
- /* RFC 822, extended */
- 'E', '_', 'N', '_', 'y', '$', 0,
- 'x', 0,
-
- /* traditional */
- '4', '_', 'M', '_', 'D', '_', 'h', '_', 'm', '_', 's', '$', 0,
- 'R', '_', 'M', '_', 'D', '_', 'h', '_', 'm', '_', 's', '$', 0,
- 'E', '_', 'N', 0,
- 'N', '_', 'E', '_', 'y', ';', 0,
- 'N', '_', 'E', ';', 0,
- 'N', 0,
- 't', ':', 'm', ':', 's', '_', 'A', 0,
- 't', ':', 'm', '_', 'A', 0,
- 't', '_', 'A', 0,
-
- /* traditional get_date */
- 'i', '_', 'x', 0,
- 'Y', '/', 'n', '/', 'E', ';', 0,
- 'n', '/', 'E', '/', 'y', ';', 0,
- 'n', '/', 'E', ';', 0,
- 'u', 0,
-
- /* ISO 8601:1988 formats, generalized a bit. */
- 'y', '-', 'M', '-', 'D', '$', 0,
- '4', 'M', 'D', '$', 0,
- 'Y', '-', 'M', '$', 0,
- 'R', 'M', 'D', '$', 0,
- '-', 'R', '=', 'M', '$', 0,
- '-', 'R', '$', 0,
- '-', '-', 'M', '=', 'D', '$', 0,
- 'M', '=', 'D', 'T', 0,
- '-', '-', 'M', '$', 0,
- '-', '-', '-', 'D', '$', 0,
- 'D', 'T', 0,
- 'Y', '-', 'd', '$', 0,
- '4', 'd', '$', 0,
- 'R', '=', 'd', '$', 0,
- '-', 'd', '$', 0,
- 'd', 'T', 0,
- 'y', '-', 'W', '-', 'X', 0,
- 'y', 'W', 'X', 0,
- 'y', '=', 'W', 0,
- '-', 'r', '-', 'W', '-', 'X', 0,
- 'r', '-', 'W', '-', 'X', 'T', 0,
- '-', 'r', 'W', 'X', 0,
- 'r', 'W', 'X', 'T', 0,
- '-', 'W', '=', 'X', 0,
- 'W', '=', 'X', 'T', 0,
- '-', 'W', 0,
- '-', 'w', '-', 'X', 0,
- 'w', '-', 'X', 'T', 0,
- '-', '-', '-', 'X', '$', 0,
- 'X', 'T', 0,
- '4', '$', 0,
- 'T', 0,
- 'h', ':', 'm', ':', 's', '$', 0,
- 'h', 'm', 's', '$', 0,
- 'h', ':', 'L', '$', 0,
- 'h', 'L', '$', 0,
- 'H', '$', 0,
- '-', 'm', ':', 's', '$', 0,
- '-', 'm', 's', '$', 0,
- '-', 'L', '$', 0,
- '-', '-', 's', '$', 0,
- 'Y', 0,
- 'Z', 0,
-
- 0
-};
-
-/* Parse an initial prefix of STR according to *PATTERNS, setting *T.
- Return the first character after the prefix, or 0 if it couldn't be parsed.
- *PATTERNS is a character array containing one pattern string after another;
- it is terminated by an empty string.
- If success, set *PATTERNS to the next pattern to try.
- Set *PATTERNS to 0 if we know there are no more patterns to try;
- if *PATTERNS is initially 0, give up immediately. */
-static char const *
-parse_prefix (char const *str, char const **patterns, struct partime *t)
-{
- char const *pat = *patterns;
- unsigned char c;
-
- if (! pat)
- return 0;
-
- /* Remove initial noise. */
- while (! ISALNUM (c = *str) && c != '-' && c != '+')
- {
- if (! c)
- {
- undefine (t);
- *patterns = 0;
- return str;
- }
-
- str++;
- }
-
- /* Try a pattern until one succeeds. */
- while (*pat)
- {
- char const *s = str;
- undefine (t);
-
- do
- {
- if (! (c = *pat++))
- {
- *patterns = pat;
- return s;
- }
- }
- while ((s = parse_pattern_letter (s, c, t)) != 0);
-
- while (*pat++)
- /* do nothing */ ;
- }
-
- return 0;
-}
-
-/* Parse an initial prefix of S of length DIGITS; it must be a number.
- Store the parsed number into *RES.
- Return the first character after the prefix, or 0 if it wasn't parsed. */
-static char const *
-parse_fixed (char const *s, int digits, int *res)
-{
- int n = 0;
- char const *lim = s + digits;
- while (s < lim)
- {
- unsigned d = *s++ - '0';
- if (9 < d)
- return 0;
- n = 10 * n + d;
- }
- *res = n;
- return s;
-}
-
-/* Parse a possibly empty initial prefix of S.
- Store the parsed number into *RES.
- Return the first character after the prefix. */
-static char const *
-parse_varying (char const *s, int *res)
-{
- int n = 0;
- for (;;)
- {
- unsigned d = *s - '0';
- if (9 < d)
- break;
- s++;
- n = 10 * n + d;
- }
- *res = n;
- return s;
-}
-
-/* Parse an initial prefix of S of length DIGITS;
- it must be a number in the range LO through HI.
- Store the parsed number into *RES.
- Return the first character after the prefix, or 0 if it wasn't parsed. */
-static char const *
-parse_ranged (char const *s, int digits, int lo, int hi, int *res)
-{
- s = parse_fixed (s, digits, res);
- return s && lo <= *res && *res <= hi ? s : 0;
-}
-
-/* Parse an initial prefix of S of length DIGITS;
- it must be a number in the range LO through HI
- and it may be followed by a fraction to be computed using RESOLUTION.
- Store the parsed number into *RES; store the fraction times RESOLUTION,
- rounded to the nearest integer, into *FRES.
- Return the first character after the prefix, or 0 if it wasn't parsed. */
-static char const *
-parse_decimal (char const *s, int digits, int lo, int hi, int resolution,
- int *res, int *fres)
-{
- s = parse_fixed (s, digits, res);
- if (s && lo <= *res && *res <= hi)
- {
- int f = 0;
- if ((s[0] == ',' || s[0] == '.') && ISDIGIT (s[1]))
- {
- char const *s1 = ++s;
- int num10 = 0, denom10 = 10, product;
- while (ISDIGIT (*++s))
- {
- int d = denom10 * 10;
- if (d / 10 != denom10)
- return 0; /* overflow */
- denom10 = d;
- }
- s = parse_fixed (s1, (int) (s - s1), &num10);
- product = num10 * resolution;
- f = (product + (denom10 >> 1)) / denom10;
- f -= f & (product % denom10 == denom10 >> 1); /* round to even */
- if (f < 0 || product/resolution != num10)
- return 0; /* overflow */
- }
- *fres = f;
- return s;
- }
- return 0;
-}
-
-/* Parse an initial prefix of S; it must denote a time zone.
- Set *ZONE to the number of seconds east of GMT,
- or to TM_LOCAL_ZONE if it is the local time zone.
- Return the first character after the prefix, or 0 if it wasn't parsed. */
-char *
-parzone (s, zone)
- char const *s;
- long *zone;
-{
- char const *s1;
- char sign;
- int hh, mm, ss;
- int minutes_east_of_UTC;
- int trailing_DST;
- long offset, z;
-
- /* The formats are LT, n, n DST, nDST, no, o
- where n is a time zone name
- and o is a time zone offset of the form [-+]hh[:mm[:ss]]. */
- switch (*s)
- {
- case '-':
- case '+':
- z = 0;
- break;
-
- default:
- minutes_east_of_UTC = lookup (s, zone_names);
- if (minutes_east_of_UTC == -1)
- return 0;
-
- /* Don't bother to check rest of spelling,
- but look for an embedded "DST". */
- trailing_DST = 0;
- while (ISALPHA ((unsigned char) *s))
- {
- if ((*s == 'D' || *s == 'd') && lookup (s, dst_names))
- trailing_DST = 1;
- s++;
- s += *s == '.';
- }
-
- /* Don't modify LT. */
- if (minutes_east_of_UTC == 1)
- {
- *zone = TM_LOCAL_ZONE;
- return (char *) s;
- }
-
- z = minutes_east_of_UTC * 60L;
- s1 = s;
-
- /* Look for trailing "DST" or " DST". */
- while (ISSPACE ((unsigned char) *s))
- s++;
- if (lookup (s, dst_names))
- {
- while (ISALPHA ((unsigned char) *s))
- {
- s++;
- s += *s == '.';
- }
- trailing_DST = 1;
- }
-
- if (trailing_DST)
- {
- *zone = z + 60*60;
- return (char *) s;
- }
-
- s = s1;
-
- switch (*s)
- {
- case '-':
- case '+':
- break;
-
- default:
- *zone = z;
- return (char *) s;
- }
-
- break;
- }
-
- sign = *s++;
-
- if (! (s = parse_ranged (s, 2, 0, 23, &hh)))
- return 0;
- mm = ss = 0;
- if (*s == ':')
- s++;
- if (ISDIGIT (*s))
- {
- if (! (s = parse_ranged (s, 2, 0, 59, &mm)))
- return 0;
- if (*s == ':' && s[-3] == ':' && ISDIGIT (s[1])
- && ! (s = parse_ranged (s + 1, 2, 0, 59, &ss)))
- return 0;
- }
- if (ISDIGIT (*s))
- return 0;
- offset = (hh * 60 + mm) * 60L + ss;
- *zone = z + (sign == '-' ? -offset : offset);
- /* ?? Are fractions allowed here? If so, they're not implemented. */
- return (char *) s;
-}
-
-/* Parse an initial prefix of S, matching the pattern whose code is C.
- Set *T accordingly.
- Return the first character after the prefix, or 0 if it wasn't parsed. */
-static char const *
-parse_pattern_letter (char const *s, int c, struct partime *t)
-{
- char const *s0 = s;
-
- switch (c)
- {
- case '$': /* The next character must be a non-digit. */
- if (ISDIGIT (*s))
- return 0;
- break;
-
- case '-':
- case '/':
- case ':':
- /* These characters stand for themselves. */
- if (*s++ != c)
- return 0;
- break;
-
- case '4': /* 4-digit year */
- s = parse_fixed (s, 4, &t->tm.tm_year);
- break;
-
- case ';': /* The next character must be a non-digit, and cannot be ':'. */
- if (ISDIGIT (*s) || *s == ':')
- return 0;
- break;
-
- case '=': /* optional '-' */
- s += *s == '-';
- break;
-
- case 'A': /* AM or PM */
- /* This matches the regular expression [AaPp]\.?([Mm]\.?)?.
- It must not be followed by a letter or digit;
- otherwise it would match prefixes of strings like "PST". */
- switch (*s)
- {
- case 'A':
- case 'a':
- if (t->tm.tm_hour == 12)
- t->tm.tm_hour = 0;
- break;
-
- case 'P':
- case 'p':
- if (t->tm.tm_hour != 12)
- t->tm.tm_hour += 12;
- break;
-
- default:
- return 0;
- }
- s++;
- s += *s == '.';
- switch (*s)
- {
- case 'M':
- case 'm':
- s++;
- s += *s == '.';
- break;
- }
- if (ISALNUM ((unsigned char) *s))
- return 0;
- break;
-
- case 'D': /* day of month [01-31] */
- s = parse_ranged (s, 2, 1, 31, &t->tm.tm_mday);
- break;
-
- case 'd': /* day of year [001-366] */
- s = parse_ranged (s, 3, 1, 366, &t->tm.tm_yday);
- t->tm.tm_yday--;
- break;
-
- case 'E': /* traditional day of month [1-9, 01-31] */
- s = parse_ranged (s, (ISDIGIT (s[0]) && ISDIGIT (s[1])) + 1, 1, 31,
- &t->tm.tm_mday);
- break;
-
- case 'h': /* hour [00-23] */
- s = parse_ranged (s, 2, 0, 23, &t->tm.tm_hour);
- break;
-
- case 'H': /* hour [00-23 followed by optional fraction] */
- {
- int frac;
- s = parse_decimal (s, 2, 0, 23, 60 * 60, &t->tm.tm_hour, &frac);
- t->tm.tm_min = frac / 60;
- t->tm.tm_sec = frac % 60;
- }
- break;
-
- case 'i': /* ordinal day number, e.g. "3rd" */
- s = parse_varying (s, &t->wday_ordinal);
- if (s == s0)
- return 0;
- while (ISALPHA ((unsigned char) *s))
- s++;
- break;
-
- case 'L': /* minute [00-59 followed by optional fraction] */
- s = parse_decimal (s, 2, 0, 59, 60, &t->tm.tm_min, &t->tm.tm_sec);
- break;
-
- case 'm': /* minute [00-59] */
- s = parse_ranged (s, 2, 0, 59, &t->tm.tm_min);
- break;
-
- case 'M': /* month [01-12] */
- s = parse_ranged (s, 2, 1, 12, &t->tm.tm_mon);
- t->tm.tm_mon--;
- break;
-
- case 'n': /* traditional month [1-9, 01-12] */
- s = parse_ranged (s, (ISDIGIT (s[0]) && ISDIGIT (s[1])) + 1, 1, 12,
- &t->tm.tm_mon);
- t->tm.tm_mon--;
- break;
-
- case 'N': /* month name [e.g. "Jan"] */
- if (! TM_DEFINED (t->tm.tm_mon = lookup (s, month_names)))
- return 0;
- /* Don't bother to check rest of spelling. */
- while (ISALPHA ((unsigned char) *s))
- s++;
- break;
-
- case 'r': /* year % 10 (remainder in origin-0 decade) [0-9] */
- s = parse_fixed (s, 1, &t->tm.tm_year);
- t->ymodulus = 10;
- break;
-
- case_R:
- case 'R': /* year % 100 (remainder in origin-0 century) [00-99] */
- s = parse_fixed (s, 2, &t->tm.tm_year);
- t->ymodulus = 100;
- break;
-
- case 's': /* second [00-60 followed by optional fraction] */
- {
- int frac;
- s = parse_decimal (s, 2, 0, 60, 1, &t->tm.tm_sec, &frac);
- }
- break;
-
- case 'T': /* 'T' or 't' */
- switch (*s++)
- {
- case 'T':
- case 't':
- break;
- default:
- return 0;
- }
- break;
-
- case 't': /* traditional hour [1-9 or 01-12] */
- s = parse_ranged (s, (ISDIGIT (s[0]) && ISDIGIT (s[1])) + 1, 1, 12,
- &t->tm.tm_hour);
- break;
-
- case 'u': /* relative unit */
- {
- int i;
- int n;
- int negative = 0;
- switch (*s)
- {
- case '-': negative = 1;
- /* Fall through. */
- case '+': s++;
- }
- if (ISDIGIT (*s))
- s = parse_varying (s, &n);
- else if (s == s0)
- n = 1;
- else
- return 0;
- if (negative)
- n = -n;
- while (! ISALNUM ((unsigned char) *s) && *s)
- s++;
- i = lookup (s, relative_units);
- if (!TM_DEFINED (i))
- return 0;
- * (int *) ((char *) &t->tmr + RELATIVE_OFFSET (i))
- += n * RELATIVE_MULTIPLIER (i);
- while (ISALPHA ((unsigned char) *s))
- s++;
- while (! ISALNUM ((unsigned char) *s) && *s)
- s++;
- if (TM_DEFINED (lookup (s, ago)))
- {
- t->tmr.tm_sec = - t->tmr.tm_sec;
- t->tmr.tm_min = - t->tmr.tm_min;
- t->tmr.tm_hour = - t->tmr.tm_hour;
- t->tmr.tm_mday = - t->tmr.tm_mday;
- t->tmr.tm_mon = - t->tmr.tm_mon;
- t->tmr.tm_year = - t->tmr.tm_year;
- while (ISALPHA ((unsigned char) *s))
- s++;
- }
- break;
- }
-
- case 'w': /* 'W' or 'w' only (stands for current week) */
- switch (*s++)
- {
- case 'W':
- case 'w':
- break;
- default:
- return 0;
- }
- break;
-
- case 'W': /* 'W' or 'w', followed by a week of year [00-53] */
- switch (*s++)
- {
- case 'W':
- case 'w':
- break;
- default:
- return 0;
- }
- s = parse_ranged (s, 2, 0, 53, &t->yweek);
- break;
-
- case 'X': /* weekday (1=Mon ... 7=Sun) [1-7] */
- s = parse_ranged (s, 1, 1, 7, &t->tm.tm_wday);
- t->tm.tm_wday--;
- break;
-
- case 'x': /* weekday name [e.g. "Sun"] */
- if (! TM_DEFINED (t->tm.tm_wday = lookup (s, weekday_names)))
- return 0;
- /* Don't bother to check rest of spelling. */
- while (ISALPHA ((unsigned char) *s))
- s++;
- break;
-
- case 'y': /* either R or Y */
- if (ISDIGIT (s[0]) && ISDIGIT (s[1]) && ! ISDIGIT (s[2]))
- goto case_R;
- /* fall into */
- case 'Y': /* year in full [4 or more digits] */
- s = parse_varying (s, &t->tm.tm_year);
- if (s - s0 < 4)
- return 0;
- break;
-
- case 'Z': /* time zone */
- s = parzone (s, &t->zone);
- break;
-
- case '_': /* possibly empty sequence of non-alphanumerics */
- while (! ISALNUM ((unsigned char) *s) && *s)
- s++;
- break;
-
- default: /* bad pattern */
- return 0;
- }
-
- return s;
-}
-
-/* If there is no conflict, merge into *T the additional information in *U
- and return 0. Otherwise do nothing and return -1. */
-static int
-merge_partime (struct partime *t, struct partime const *u)
-{
-# define conflict(a,b) ((a) != (b) && TM_DEFINED (a) && TM_DEFINED (b))
- if (conflict (t->tm.tm_sec, u->tm.tm_sec)
- || conflict (t->tm.tm_min, u->tm.tm_min)
- || conflict (t->tm.tm_hour, u->tm.tm_hour)
- || conflict (t->tm.tm_mday, u->tm.tm_mday)
- || conflict (t->tm.tm_mon, u->tm.tm_mon)
- || conflict (t->tm.tm_year, u->tm.tm_year)
- || conflict (t->tm.tm_wday, u->tm.tm_wday)
- || conflict (t->tm.tm_yday, u->tm.tm_yday)
- || conflict (t->ymodulus, u->ymodulus)
- || conflict (t->yweek, u->yweek)
- || (t->zone != u->zone
- && t->zone != TM_UNDEFINED_ZONE
- && u->zone != TM_UNDEFINED_ZONE))
- return -1;
-# undef conflict
-# define merge_(a,b) if (TM_DEFINED (b)) (a) = (b);
- merge_ (t->tm.tm_sec, u->tm.tm_sec)
- merge_ (t->tm.tm_min, u->tm.tm_min)
- merge_ (t->tm.tm_hour, u->tm.tm_hour)
- merge_ (t->tm.tm_mday, u->tm.tm_mday)
- merge_ (t->tm.tm_mon, u->tm.tm_mon)
- merge_ (t->tm.tm_year, u->tm.tm_year)
- merge_ (t->tm.tm_wday, u->tm.tm_wday)
- merge_ (t->tm.tm_yday, u->tm.tm_yday)
- merge_ (t->ymodulus, u->ymodulus)
- merge_ (t->yweek, u->yweek)
-# undef merge_
- t->tmr.tm_sec += u->tmr.tm_sec;
- t->tmr.tm_min += u->tmr.tm_min;
- t->tmr.tm_hour += u->tmr.tm_hour;
- t->tmr.tm_mday += u->tmr.tm_mday;
- t->tmr.tm_mon += u->tmr.tm_mon;
- t->tmr.tm_year += u->tmr.tm_year;
- if (u->zone != TM_UNDEFINED_ZONE)
- t->zone = u->zone;
- return 0;
-}
-
-/* Parse a date/time prefix of S, putting the parsed result into *T.
- Return the first character after the prefix.
- The prefix may contain no useful information;
- in that case, *T will contain only undefined values. */
-char *
-partime (s, t)
- char const *s;
- struct partime *t;
-{
- struct partime p;
-
- undefine (t);
-
- while (*s)
- {
- char const *patterns = time_patterns;
- char const *s1;
-
- do
- {
- if (! (s1 = parse_prefix (s, &patterns, &p)))
- return (char *) s;
- }
- while (merge_partime (t, &p) != 0);
-
- s = s1;
- }
-
- return (char *) s;
-}
diff --git a/src/partime.h b/src/partime.h
deleted file mode 100644
index bf87f3f..0000000
--- a/src/partime.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/* Parse a string, yielding a struct partime that describes it. */
-
-/* Copyright (C) 1993, 1994, 1995, 1997, 2003, 2006 Paul Eggert
- Distributed under license by the Free Software Foundation, Inc.
- Copyright (C) 2010 Free Software Foundation, Inc..
-
- This file is also part of RCS.
-
- 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
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program 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 General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>. */
-
-#include <limits.h>
-#include <time.h>
-
-#define TM_UNDEFINED (-1)
-#define TM_DEFINED(x) (0 <= (x))
-
-#define TM_LOCAL_ZONE LONG_MIN
-#define TM_UNDEFINED_ZONE (LONG_MIN + 1)
-
-struct partime
- {
- /* This structure describes the parsed time.
- Only the following tm_* members are used:
- sec, min, hour, mday, mon, year, wday, yday.
- If ! TM_DEFINED (value), the parser never found the value.
- The tm_year field is the actual year, not the year - 1900;
- but see ymodulus below. */
- struct tm tm;
-
- /* Like tm, but values are relative to the value in tm,
- and values are initialized to 0 rather than to TM_UNDEFINED.
- Only the following tm_* members are used:
- sec, min, hour, mday, mon, year. */
- struct tm tmr;
-
- /* If TM_DEFINED (wday_ordinal),
- then day number (e.g. 3 in "3rd Sunday"). */
- int wday_ordinal;
-
- /* If TM_DEFINED (ymodulus),
- then tm.tm_year is actually modulo ymodulus. */
- int ymodulus;
-
- /* Week of year, ISO 8601 style.
- If ! TM_DEFINED (yweek), the parser never found yweek.
- Weeks start on Mondays.
- Week 1 includes Jan 4. */
- int yweek;
-
- /* Seconds east of UTC; or TM_LOCAL_ZONE or TM_UNDEFINED_ZONE. */
- long zone;
- };
-
-char *partime (char const *, struct partime *);
-char *parzone (char const *, long *);
diff --git a/src/patch.c b/src/patch.c
index 2e03d1f..01f9fbf 100644
--- a/src/patch.c
+++ b/src/patch.c
@@ -148,6 +148,10 @@ main (int argc, char **argv)
Argv = argv;
get_some_switches();
+ /* Make get_date() assume that context diff headers use UTC. */
+ if (set_utc)
+ setenv ("TZ", "UTC", 1);
+
if (make_backups | backup_if_mismatch)
backup_type = get_version (version_control_context, version_control);
diff --git a/src/util.c b/src/util.c
index 001dcbd..19b593f 100644
--- a/src/util.c
+++ b/src/util.c
@@ -29,8 +29,7 @@
#include <util.h>
#include <xalloc.h>
-#include <maketime.h>
-#include <partime.h>
+#include <getdate.h>
#include <signal.h>
#if !defined SIGCHLD && defined SIGCLD
@@ -1102,12 +1101,12 @@ removedirs (char *filename)
}
}
-static time_t initial_time;
+static struct timespec initial_time;
void
init_time (void)
{
- time (&initial_time);
+ gettime (&initial_time);
}
/* Make filenames more reasonable. */
@@ -1120,7 +1119,9 @@ fetchname (char *at, int strip_leading, char **ptimestr,
char *timestr = NULL;
register char *t;
int sleading = strip_leading;
- time_t stamp = (time_t) -1;
+ struct timespec stamp;
+
+ stamp.tv_sec = -1;
while (ISSPACE ((unsigned char) *at))
at++;
@@ -1150,7 +1151,7 @@ fetchname (char *at, int strip_leading, char **ptimestr,
continue;
if (*u == '\n')
- stamp = (time_t) -1;
+ stamp.tv_sec = -1;
else
{
if (! pstamp)
@@ -1168,25 +1169,23 @@ fetchname (char *at, int strip_leading, char **ptimestr,
}
if (set_time | set_utc)
- stamp = str2time (&u, initial_time,
- set_utc ? 0L : TM_LOCAL_ZONE);
+ get_date (&stamp, u, &initial_time);
else
{
/* The head says the file is nonexistent if the
timestamp is the epoch; but the listed time is
local time, not UTC, and POSIX.1 allows local
time offset anywhere in the range -25:00 <
- offset < +26:00. Match any time in that range
- by assuming local time is -25:00 and then
- matching any ``local'' time T in the range 0 <
- T < 25+26 hours. */
- stamp = str2time (&u, initial_time, -25L * 60 * 60);
- if (0 < stamp && stamp < (25 + 26) * 60L * 60)
- stamp = 0;
+ offset < +26:00. Match any time in that range. */
+ const struct timespec lower = { -25L * 60 * 60 },
+ upper = { 26L * 60 * 60 };
+ if (get_date (&stamp, u, &initial_time)
+ && timespec_cmp (stamp, lower) > 0
+ && timespec_cmp (stamp, upper) < 0) {
+ stamp.tv_sec = 0;
+ stamp.tv_nsec = 0;
+ }
}
-
- if (*u && ! ISSPACE ((unsigned char) *u))
- stamp = (time_t) -1;
}
*t = '\0';
@@ -1225,10 +1224,7 @@ fetchname (char *at, int strip_leading, char **ptimestr,
}
if (pstamp)
- {
- pstamp->tv_sec = stamp;
- pstamp->tv_nsec = 0;
- }
+ *pstamp = stamp;
if (ptimestr)
*ptimestr = timestr;
return savestr (name);
diff --git a/src/util.h b/src/util.h
index 128f5ff..fbeb1d3 100644
--- a/src/util.h
+++ b/src/util.h
@@ -33,6 +33,8 @@ struct utimbuf
};
#endif
+#include <timespec.h>
+
/* An upper bound on the print length of a signed decimal line number.
Add one for the sign. */
#define LINENUM_LENGTH_BOUND (sizeof (lin) * CHAR_BIT / 3 + 1)
diff --git a/tests/preserve-mode-and-timestamp b/tests/preserve-mode-and-timestamp
index 7019675..43988bb 100644
--- a/tests/preserve-mode-and-timestamp
+++ b/tests/preserve-mode-and-timestamp
@@ -13,8 +13,8 @@ use_tmpdir
# ==============================================================
-timestamp1="2009-03-13 00:00:00 +0000"
-timestamp2="2009-03-14 00:00:00 +0000"
+timestamp1="2009-03-13 00:00:00"
+timestamp2="2009-03-14 00:00:00"
cat > f.diff <<EOF
--- a/f $timestamp1
+++ b/f $timestamp2
@@ -33,7 +33,7 @@ EOF
# touch -d "$timestamp1" f
# Some non-GNU versions of touch don't support the -d option
-sed_expr='s,\(....\)-\(..\)-\(..\) \(..\):\(..\):\(..\) +\(....\),\1\2\3\4\5.\6,'
+sed_expr='s,\(....\)-\(..\)-\(..\) \(..\):\(..\):\(..\),\1\2\3\4\5.\6,'
timestamp1_touch=`echo "$timestamp1" | sed -e "$sed_expr"`
TZ=UTC touch -t $timestamp1_touch f