diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | ChangeLog | 17 | ||||
-rw-r--r-- | bootstrap.conf | 4 | ||||
-rw-r--r-- | src/Makefile.am | 5 | ||||
-rw-r--r-- | src/maketime.c | 449 | ||||
-rw-r--r-- | src/maketime.h | 28 | ||||
-rw-r--r-- | src/partime.c | 894 | ||||
-rw-r--r-- | src/partime.h | 66 | ||||
-rw-r--r-- | src/patch.c | 4 | ||||
-rw-r--r-- | src/util.c | 40 | ||||
-rw-r--r-- | src/util.h | 2 | ||||
-rw-r--r-- | tests/preserve-mode-and-timestamp | 6 |
12 files changed, 50 insertions, 1466 deletions
@@ -27,3 +27,4 @@ Makefile Makefile.in build-aux +doc @@ -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); @@ -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); @@ -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 |