minix/lib/libutil/parsedate.y
Ben Gras 0c3983b25a update/fix manpage support
. add bsd-style MLINKS to minix man set, restoring aliases
	  (e.g. man add64 -> int64)
	. update daily cron script to run makewhatis and restore makewhatis
	  in man Makefile (makedb), restores functionality of man -k
	. netbsd imports of man, mdocml, makewhatis, libutil, apropos
	. update man.conf with manpage locations, restoring man [-s] <section>
	. throws out some obsolete manpages
2011-09-28 15:24:15 +00:00

1061 lines
26 KiB
Plaintext
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

%{
/*
** Originally written by Steven M. Bellovin <smb@research.att.com> while
** at the University of North Carolina at Chapel Hill. Later tweaked by
** a couple of people on Usenet. Completely overhauled by Rich $alz
** <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990;
**
** This grammar has 10 shift/reduce conflicts.
**
** This code is in the public domain and has no copyright.
*/
/* SUPPRESS 287 on yaccpar_sccsid *//* Unused static variable */
/* SUPPRESS 288 on yyerrlab *//* Label unused */
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <time.h>
#include <util.h>
#include <stdlib.h>
/* NOTES on rebuilding parsedate.c (particularly for inclusion in CVS
releases):
We don't want to mess with all the portability hassles of alloca.
In particular, most (all?) versions of bison will use alloca in
their parser. If bison works on your system (e.g. it should work
with gcc), then go ahead and use it, but the more general solution
is to use byacc instead of bison, which should generate a portable
parser. I played with adding "#define alloca dont_use_alloca", to
give an error if the parser generator uses alloca (and thus detect
unportable parsedate.c's), but that seems to cause as many problems
as it solves. */
#define EPOCH 1970
#define HOUR(x) ((time_t)(x) * 60)
#define SECSPERDAY (24L * 60L * 60L)
/*
** An entry in the lexical lookup table.
*/
typedef struct _TABLE {
const char *name;
int type;
time_t value;
} TABLE;
/*
** Daylight-savings mode: on, off, or not yet known.
*/
typedef enum _DSTMODE {
DSTon, DSToff, DSTmaybe
} DSTMODE;
/*
** Meridian: am, pm, or 24-hour style.
*/
typedef enum _MERIDIAN {
MERam, MERpm, MER24
} MERIDIAN;
struct dateinfo {
DSTMODE yyDSTmode;
time_t yyDayOrdinal;
time_t yyDayNumber;
int yyHaveDate;
int yyHaveDay;
int yyHaveRel;
int yyHaveTime;
int yyHaveZone;
time_t yyTimezone;
time_t yyDay;
time_t yyHour;
time_t yyMinutes;
time_t yyMonth;
time_t yySeconds;
time_t yyYear;
MERIDIAN yyMeridian;
time_t yyRelMonth;
time_t yyRelSeconds;
};
%}
%union {
time_t Number;
enum _MERIDIAN Meridian;
}
%token tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT
%token tSEC_UNIT tSNUMBER tUNUMBER tZONE tDST AT_SIGN
%type <Number> tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT
%type <Number> tSEC_UNIT tSNUMBER tUNUMBER tZONE
%type <Meridian> tMERIDIAN o_merid
%parse-param { struct dateinfo *param }
%parse-param { const char **yyInput }
%lex-param { const char **yyInput }
%pure-parser
%%
spec : /* NULL */
| spec item
;
item : time {
param->yyHaveTime++;
}
| zone {
param->yyHaveZone++;
}
| date {
param->yyHaveDate++;
}
| day {
param->yyHaveDay++;
}
| rel {
param->yyHaveRel++;
}
| cvsstamp {
param->yyHaveTime++;
param->yyHaveDate++;
param->yyHaveZone++;
}
| epochdate {
param->yyHaveTime++;
param->yyHaveDate++;
param->yyHaveZone++;
}
| number
;
cvsstamp: tUNUMBER '.' tUNUMBER '.' tUNUMBER '.' tUNUMBER '.' tUNUMBER '.' tUNUMBER {
param->yyYear = $1;
if (param->yyYear < 100) param->yyYear += 1900;
param->yyMonth = $3;
param->yyDay = $5;
param->yyHour = $7;
param->yyMinutes = $9;
param->yySeconds = $11;
param->yyDSTmode = DSToff;
param->yyTimezone = 0;
}
;
epochdate: AT_SIGN tUNUMBER {
time_t when = $2;
struct tm tmbuf;
if (gmtime_r(&when, &tmbuf) != NULL) {
param->yyYear = tmbuf.tm_year + 1900;
param->yyMonth = tmbuf.tm_mon + 1;
param->yyDay = tmbuf.tm_mday;
param->yyHour = tmbuf.tm_hour;
param->yyMinutes = tmbuf.tm_min;
param->yySeconds = tmbuf.tm_sec;
} else {
param->yyYear = EPOCH;
param->yyMonth = 1;
param->yyDay = 1;
param->yyHour = 0;
param->yyMinutes = 0;
param->yySeconds = 0;
}
param->yyDSTmode = DSToff;
param->yyTimezone = 0;
}
;
time : tUNUMBER tMERIDIAN {
param->yyHour = $1;
param->yyMinutes = 0;
param->yySeconds = 0;
param->yyMeridian = $2;
}
| tUNUMBER ':' tUNUMBER o_merid {
param->yyHour = $1;
param->yyMinutes = $3;
param->yySeconds = 0;
param->yyMeridian = $4;
}
| tUNUMBER ':' tUNUMBER tSNUMBER {
param->yyHour = $1;
param->yyMinutes = $3;
param->yyMeridian = MER24;
param->yyDSTmode = DSToff;
param->yyTimezone = - ($4 % 100 + ($4 / 100) * 60);
}
| tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
param->yyHour = $1;
param->yyMinutes = $3;
param->yySeconds = $5;
param->yyMeridian = $6;
}
| tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER {
param->yyHour = $1;
param->yyMinutes = $3;
param->yySeconds = $5;
param->yyMeridian = MER24;
param->yyDSTmode = DSToff;
param->yyTimezone = - ($6 % 100 + ($6 / 100) * 60);
}
| tUNUMBER ':' tUNUMBER ':' tUNUMBER '.' tUNUMBER {
param->yyHour = $1;
param->yyMinutes = $3;
param->yySeconds = $5;
param->yyMeridian = MER24;
param->yyDSTmode = DSToff;
/* XXX: Do nothing with millis */
/* param->yyTimezone = ($7 % 100 + ($7 / 100) * 60); */
}
;
zone : tZONE {
param->yyTimezone = $1;
param->yyDSTmode = DSToff;
}
| tDAYZONE {
param->yyTimezone = $1;
param->yyDSTmode = DSTon;
}
|
tZONE tDST {
param->yyTimezone = $1;
param->yyDSTmode = DSTon;
}
;
day : tDAY {
param->yyDayOrdinal = 1;
param->yyDayNumber = $1;
}
| tDAY ',' {
param->yyDayOrdinal = 1;
param->yyDayNumber = $1;
}
| tUNUMBER tDAY {
param->yyDayOrdinal = $1;
param->yyDayNumber = $2;
}
;
date : tUNUMBER '/' tUNUMBER {
param->yyMonth = $1;
param->yyDay = $3;
}
| tUNUMBER '/' tUNUMBER '/' tUNUMBER {
if ($1 >= 100) {
param->yyYear = $1;
param->yyMonth = $3;
param->yyDay = $5;
} else {
param->yyMonth = $1;
param->yyDay = $3;
param->yyYear = $5;
}
}
| tUNUMBER tSNUMBER tSNUMBER {
/* ISO 8601 format. yyyy-mm-dd. */
param->yyYear = $1;
param->yyMonth = -$2;
param->yyDay = -$3;
}
| tUNUMBER tMONTH tSNUMBER {
/* e.g. 17-JUN-1992. */
param->yyDay = $1;
param->yyMonth = $2;
param->yyYear = -$3;
}
| tMONTH tUNUMBER {
param->yyMonth = $1;
param->yyDay = $2;
}
| tMONTH tUNUMBER ',' tUNUMBER {
param->yyMonth = $1;
param->yyDay = $2;
param->yyYear = $4;
}
| tUNUMBER tMONTH {
param->yyMonth = $2;
param->yyDay = $1;
}
| tUNUMBER tMONTH tUNUMBER {
param->yyMonth = $2;
param->yyDay = $1;
param->yyYear = $3;
}
;
rel : relunit tAGO {
param->yyRelSeconds = -param->yyRelSeconds;
param->yyRelMonth = -param->yyRelMonth;
}
| relunit
;
relunit : tUNUMBER tMINUTE_UNIT {
param->yyRelSeconds += $1 * $2 * 60L;
}
| tSNUMBER tMINUTE_UNIT {
param->yyRelSeconds += $1 * $2 * 60L;
}
| tMINUTE_UNIT {
param->yyRelSeconds += $1 * 60L;
}
| tSNUMBER tSEC_UNIT {
param->yyRelSeconds += $1;
}
| tUNUMBER tSEC_UNIT {
param->yyRelSeconds += $1;
}
| tSEC_UNIT {
param->yyRelSeconds++;
}
| tSNUMBER tMONTH_UNIT {
param->yyRelMonth += $1 * $2;
}
| tUNUMBER tMONTH_UNIT {
param->yyRelMonth += $1 * $2;
}
| tMONTH_UNIT {
param->yyRelMonth += $1;
}
;
number : tUNUMBER {
if (param->yyHaveTime && param->yyHaveDate && !param->yyHaveRel)
param->yyYear = $1;
else {
if($1>10000) {
param->yyHaveDate++;
param->yyDay= ($1)%100;
param->yyMonth= ($1/100)%100;
param->yyYear = $1/10000;
}
else {
param->yyHaveTime++;
if ($1 < 100) {
param->yyHour = $1;
param->yyMinutes = 0;
}
else {
param->yyHour = $1 / 100;
param->yyMinutes = $1 % 100;
}
param->yySeconds = 0;
param->yyMeridian = MER24;
}
}
}
;
o_merid : /* NULL */ {
$$ = MER24;
}
| tMERIDIAN {
$$ = $1;
}
;
%%
/* Month and day table. */
static const TABLE const MonthDayTable[] = {
{ "january", tMONTH, 1 },
{ "february", tMONTH, 2 },
{ "march", tMONTH, 3 },
{ "april", tMONTH, 4 },
{ "may", tMONTH, 5 },
{ "june", tMONTH, 6 },
{ "july", tMONTH, 7 },
{ "august", tMONTH, 8 },
{ "september", tMONTH, 9 },
{ "sept", tMONTH, 9 },
{ "october", tMONTH, 10 },
{ "november", tMONTH, 11 },
{ "december", tMONTH, 12 },
{ "sunday", tDAY, 0 },
{ "monday", tDAY, 1 },
{ "tuesday", tDAY, 2 },
{ "tues", tDAY, 2 },
{ "wednesday", tDAY, 3 },
{ "wednes", tDAY, 3 },
{ "thursday", tDAY, 4 },
{ "thur", tDAY, 4 },
{ "thurs", tDAY, 4 },
{ "friday", tDAY, 5 },
{ "saturday", tDAY, 6 },
{ NULL, 0, 0 }
};
/* Time units table. */
static const TABLE const UnitsTable[] = {
{ "year", tMONTH_UNIT, 12 },
{ "month", tMONTH_UNIT, 1 },
{ "fortnight", tMINUTE_UNIT, 14 * 24 * 60 },
{ "week", tMINUTE_UNIT, 7 * 24 * 60 },
{ "day", tMINUTE_UNIT, 1 * 24 * 60 },
{ "hour", tMINUTE_UNIT, 60 },
{ "minute", tMINUTE_UNIT, 1 },
{ "min", tMINUTE_UNIT, 1 },
{ "second", tSEC_UNIT, 1 },
{ "sec", tSEC_UNIT, 1 },
{ NULL, 0, 0 }
};
/* Assorted relative-time words. */
static const TABLE const OtherTable[] = {
{ "tomorrow", tMINUTE_UNIT, 1 * 24 * 60 },
{ "yesterday", tMINUTE_UNIT, -1 * 24 * 60 },
{ "today", tMINUTE_UNIT, 0 },
{ "now", tMINUTE_UNIT, 0 },
{ "last", tUNUMBER, -1 },
{ "this", tMINUTE_UNIT, 0 },
{ "next", tUNUMBER, 2 },
{ "first", tUNUMBER, 1 },
{ "one", tUNUMBER, 1 },
/* { "second", tUNUMBER, 2 }, */
{ "two", tUNUMBER, 2 },
{ "third", tUNUMBER, 3 },
{ "three", tUNUMBER, 3 },
{ "fourth", tUNUMBER, 4 },
{ "four", tUNUMBER, 4 },
{ "fifth", tUNUMBER, 5 },
{ "five", tUNUMBER, 5 },
{ "sixth", tUNUMBER, 6 },
{ "six", tUNUMBER, 6 },
{ "seventh", tUNUMBER, 7 },
{ "seven", tUNUMBER, 7 },
{ "eighth", tUNUMBER, 8 },
{ "eight", tUNUMBER, 8 },
{ "ninth", tUNUMBER, 9 },
{ "nine", tUNUMBER, 9 },
{ "tenth", tUNUMBER, 10 },
{ "ten", tUNUMBER, 10 },
{ "eleventh", tUNUMBER, 11 },
{ "eleven", tUNUMBER, 11 },
{ "twelfth", tUNUMBER, 12 },
{ "twelve", tUNUMBER, 12 },
{ "ago", tAGO, 1 },
{ NULL, 0, 0 }
};
/* The timezone table. */
/* Some of these are commented out because a time_t can't store a float. */
static const TABLE const TimezoneTable[] = {
{ "gmt", tZONE, HOUR( 0) }, /* Greenwich Mean */
{ "ut", tZONE, HOUR( 0) }, /* Universal (Coordinated) */
{ "utc", tZONE, HOUR( 0) },
{ "wet", tZONE, HOUR( 0) }, /* Western European */
{ "bst", tDAYZONE, HOUR( 0) }, /* British Summer */
{ "wat", tZONE, HOUR( 1) }, /* West Africa */
{ "at", tZONE, HOUR( 2) }, /* Azores */
#if 0
/* For completeness. BST is also British Summer, and GST is
* also Guam Standard. */
{ "bst", tZONE, HOUR( 3) }, /* Brazil Standard */
{ "gst", tZONE, HOUR( 3) }, /* Greenland Standard */
#endif
#if 0
{ "nft", tZONE, HOUR(3.5) }, /* Newfoundland */
{ "nst", tZONE, HOUR(3.5) }, /* Newfoundland Standard */
{ "ndt", tDAYZONE, HOUR(3.5) }, /* Newfoundland Daylight */
#endif
{ "ast", tZONE, HOUR( 4) }, /* Atlantic Standard */
{ "adt", tDAYZONE, HOUR( 4) }, /* Atlantic Daylight */
{ "est", tZONE, HOUR( 5) }, /* Eastern Standard */
{ "edt", tDAYZONE, HOUR( 5) }, /* Eastern Daylight */
{ "cst", tZONE, HOUR( 6) }, /* Central Standard */
{ "cdt", tDAYZONE, HOUR( 6) }, /* Central Daylight */
{ "mst", tZONE, HOUR( 7) }, /* Mountain Standard */
{ "mdt", tDAYZONE, HOUR( 7) }, /* Mountain Daylight */
{ "pst", tZONE, HOUR( 8) }, /* Pacific Standard */
{ "pdt", tDAYZONE, HOUR( 8) }, /* Pacific Daylight */
{ "yst", tZONE, HOUR( 9) }, /* Yukon Standard */
{ "ydt", tDAYZONE, HOUR( 9) }, /* Yukon Daylight */
{ "hst", tZONE, HOUR(10) }, /* Hawaii Standard */
{ "hdt", tDAYZONE, HOUR(10) }, /* Hawaii Daylight */
{ "cat", tZONE, HOUR(10) }, /* Central Alaska */
{ "ahst", tZONE, HOUR(10) }, /* Alaska-Hawaii Standard */
{ "nt", tZONE, HOUR(11) }, /* Nome */
{ "idlw", tZONE, HOUR(12) }, /* International Date Line West */
{ "cet", tZONE, -HOUR(1) }, /* Central European */
{ "met", tZONE, -HOUR(1) }, /* Middle European */
{ "mewt", tZONE, -HOUR(1) }, /* Middle European Winter */
{ "mest", tDAYZONE, -HOUR(1) }, /* Middle European Summer */
{ "swt", tZONE, -HOUR(1) }, /* Swedish Winter */
{ "sst", tDAYZONE, -HOUR(1) }, /* Swedish Summer */
{ "fwt", tZONE, -HOUR(1) }, /* French Winter */
{ "fst", tDAYZONE, -HOUR(1) }, /* French Summer */
{ "eet", tZONE, -HOUR(2) }, /* Eastern Europe, USSR Zone 1 */
{ "bt", tZONE, -HOUR(3) }, /* Baghdad, USSR Zone 2 */
#if 0
{ "it", tZONE, -HOUR(3.5) },/* Iran */
#endif
{ "zp4", tZONE, -HOUR(4) }, /* USSR Zone 3 */
{ "zp5", tZONE, -HOUR(5) }, /* USSR Zone 4 */
#if 0
{ "ist", tZONE, -HOUR(5.5) },/* Indian Standard */
#endif
{ "zp6", tZONE, -HOUR(6) }, /* USSR Zone 5 */
#if 0
/* For completeness. NST is also Newfoundland Stanard, and SST is
* also Swedish Summer. */
{ "nst", tZONE, -HOUR(6.5) },/* North Sumatra */
{ "sst", tZONE, -HOUR(7) }, /* South Sumatra, USSR Zone 6 */
#endif /* 0 */
{ "wast", tZONE, -HOUR(7) }, /* West Australian Standard */
{ "wadt", tDAYZONE, -HOUR(7) }, /* West Australian Daylight */
#if 0
{ "jt", tZONE, -HOUR(7.5) },/* Java (3pm in Cronusland!) */
#endif
{ "cct", tZONE, -HOUR(8) }, /* China Coast, USSR Zone 7 */
{ "jst", tZONE, -HOUR(9) }, /* Japan Standard, USSR Zone 8 */
#if 0
{ "cast", tZONE, -HOUR(9.5) },/* Central Australian Standard */
{ "cadt", tDAYZONE, -HOUR(9.5) },/* Central Australian Daylight */
#endif
{ "east", tZONE, -HOUR(10) }, /* Eastern Australian Standard */
{ "eadt", tDAYZONE, -HOUR(10) }, /* Eastern Australian Daylight */
{ "gst", tZONE, -HOUR(10) }, /* Guam Standard, USSR Zone 9 */
{ "nzt", tZONE, -HOUR(12) }, /* New Zealand */
{ "nzst", tZONE, -HOUR(12) }, /* New Zealand Standard */
{ "nzdt", tDAYZONE, -HOUR(12) }, /* New Zealand Daylight */
{ "idle", tZONE, -HOUR(12) }, /* International Date Line East */
{ NULL, 0, 0 }
};
/* Military timezone table. */
static const TABLE const MilitaryTable[] = {
{ "a", tZONE, HOUR( 1) },
{ "b", tZONE, HOUR( 2) },
{ "c", tZONE, HOUR( 3) },
{ "d", tZONE, HOUR( 4) },
{ "e", tZONE, HOUR( 5) },
{ "f", tZONE, HOUR( 6) },
{ "g", tZONE, HOUR( 7) },
{ "h", tZONE, HOUR( 8) },
{ "i", tZONE, HOUR( 9) },
{ "k", tZONE, HOUR( 10) },
{ "l", tZONE, HOUR( 11) },
{ "m", tZONE, HOUR( 12) },
{ "n", tZONE, HOUR(- 1) },
{ "o", tZONE, HOUR(- 2) },
{ "p", tZONE, HOUR(- 3) },
{ "q", tZONE, HOUR(- 4) },
{ "r", tZONE, HOUR(- 5) },
{ "s", tZONE, HOUR(- 6) },
{ "t", tZONE, HOUR(- 7) },
{ "u", tZONE, HOUR(- 8) },
{ "v", tZONE, HOUR(- 9) },
{ "w", tZONE, HOUR(-10) },
{ "x", tZONE, HOUR(-11) },
{ "y", tZONE, HOUR(-12) },
{ "z", tZONE, HOUR( 0) },
{ NULL, 0, 0 }
};
/* ARGSUSED */
static int
yyerror(struct dateinfo *param, const char **inp, const char *s __unused)
{
return 0;
}
static time_t
ToSeconds(
time_t Hours,
time_t Minutes,
time_t Seconds,
MERIDIAN Meridian
)
{
if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59)
return -1;
switch (Meridian) {
case MER24:
if (Hours < 0 || Hours > 23)
return -1;
return (Hours * 60L + Minutes) * 60L + Seconds;
case MERam:
if (Hours < 1 || Hours > 12)
return -1;
if (Hours == 12)
Hours = 0;
return (Hours * 60L + Minutes) * 60L + Seconds;
case MERpm:
if (Hours < 1 || Hours > 12)
return -1;
if (Hours == 12)
Hours = 0;
return ((Hours + 12) * 60L + Minutes) * 60L + Seconds;
default:
abort ();
}
/* NOTREACHED */
}
static int
isLeap(int year)
{
return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
}
/* Year is either
* A negative number, which means to use its absolute value (why?)
* A number from 0 to 99, which means a year from 1900 to 1999, or
* The actual year (>=100). */
static time_t
Convert(
time_t Month,
time_t Day,
time_t Year,
time_t Hours,
time_t Minutes,
time_t Seconds,
time_t Timezone,
MERIDIAN Meridian,
DSTMODE DSTmode
)
{
static int DaysInMonth[12] = {
31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
time_t tod;
time_t Julian, oJulian;
int i;
/* XXX Y2K */
if (Year < 0)
Year = -Year;
if (Year < 70)
Year += 2000;
else if (Year < 100)
Year += 1900;
DaysInMonth[1] = isLeap(Year) ? 29 : 28;
if (Year < EPOCH || Month < 1 || Month > 12
/* Lint fluff: "conversion from long may lose accuracy" */
|| Day < 1 || Day > DaysInMonth[(int)--Month])
/* FIXME:
* It would be nice to set a global error string here.
* "February 30 is not a valid date" is much more informative than
* "Can't parse date/time: 100 months" when the user input was
* "100 months" and addition resolved that to February 30, for
* example. See rcs2-7 in src/sanity.sh for more. */
return -1;
for (Julian = Day - 1, i = 0; i < Month; i++)
Julian += DaysInMonth[i];
oJulian = Julian;
for (i = EPOCH; i < Year; i++) {
Julian += 365 + isLeap(i);
if (oJulian > Julian)
return -1;
oJulian = Julian;
}
Julian *= SECSPERDAY;
if (oJulian > Julian)
return -1;
oJulian = Julian;
Julian += Timezone * 60L;
if (Timezone > 0 && oJulian > Julian)
return -1;
oJulian = Julian;
if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0)
return -1;
Julian += tod;
if (oJulian > Julian)
return -1;
if (DSTmode == DSTon || (DSTmode == DSTmaybe)) {
struct tm *tm;
if ((tm = localtime(&Julian)) == NULL)
return -1;
if (tm->tm_isdst)
Julian -= 60 * 60;
}
return Julian;
}
static time_t
DSTcorrect(
time_t Start,
time_t Future
)
{
time_t StartDay;
time_t FutureDay;
struct tm *tm;
if ((tm = localtime(&Start)) == NULL)
return -1;
StartDay = (tm->tm_hour + 1) % 24;
if ((tm = localtime(&Future)) == NULL)
return -1;
FutureDay = (tm->tm_hour + 1) % 24;
return (Future - Start) + (StartDay - FutureDay) * 60L * 60L;
}
static time_t
RelativeDate(
time_t Start,
time_t DayOrdinal,
time_t DayNumber
)
{
struct tm *tm;
time_t now;
now = Start;
tm = localtime(&now);
now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7);
now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1);
return DSTcorrect(Start, now);
}
static time_t
RelativeMonth(
time_t Start,
time_t RelMonth,
time_t Timezone
)
{
struct tm *tm;
time_t Month;
time_t Year;
if (RelMonth == 0)
return 0;
tm = localtime(&Start);
if (tm == NULL)
return -1;
Month = 12 * (tm->tm_year + 1900) + tm->tm_mon + RelMonth;
Year = Month / 12;
Month = Month % 12 + 1;
return DSTcorrect(Start,
Convert(Month, (time_t)tm->tm_mday, Year,
(time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec,
Timezone, MER24, DSTmaybe));
}
static int
LookupWord(YYSTYPE *yylval, char *buff)
{
register char *p;
register char *q;
register const TABLE *tp;
int i;
int abbrev;
/* Make it lowercase. */
for (p = buff; *p; p++)
if (isupper((unsigned char)*p))
*p = tolower((unsigned char)*p);
if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) {
yylval->Meridian = MERam;
return tMERIDIAN;
}
if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) {
yylval->Meridian = MERpm;
return tMERIDIAN;
}
/* See if we have an abbreviation for a month. */
if (strlen(buff) == 3)
abbrev = 1;
else if (strlen(buff) == 4 && buff[3] == '.') {
abbrev = 1;
buff[3] = '\0';
}
else
abbrev = 0;
for (tp = MonthDayTable; tp->name; tp++) {
if (abbrev) {
if (strncmp(buff, tp->name, 3) == 0) {
yylval->Number = tp->value;
return tp->type;
}
}
else if (strcmp(buff, tp->name) == 0) {
yylval->Number = tp->value;
return tp->type;
}
}
for (tp = TimezoneTable; tp->name; tp++)
if (strcmp(buff, tp->name) == 0) {
yylval->Number = tp->value;
return tp->type;
}
if (strcmp(buff, "dst") == 0)
return tDST;
for (tp = UnitsTable; tp->name; tp++)
if (strcmp(buff, tp->name) == 0) {
yylval->Number = tp->value;
return tp->type;
}
/* Strip off any plural and try the units table again. */
i = strlen(buff) - 1;
if (buff[i] == 's') {
buff[i] = '\0';
for (tp = UnitsTable; tp->name; tp++)
if (strcmp(buff, tp->name) == 0) {
yylval->Number = tp->value;
return tp->type;
}
buff[i] = 's'; /* Put back for "this" in OtherTable. */
}
for (tp = OtherTable; tp->name; tp++)
if (strcmp(buff, tp->name) == 0) {
yylval->Number = tp->value;
return tp->type;
}
/* Military timezones. */
if (buff[1] == '\0' && isalpha((unsigned char)*buff)) {
for (tp = MilitaryTable; tp->name; tp++)
if (strcmp(buff, tp->name) == 0) {
yylval->Number = tp->value;
return tp->type;
}
}
/* Drop out any periods and try the timezone table again. */
for (i = 0, p = q = buff; *q; q++)
if (*q != '.')
*p++ = *q;
else
i++;
*p = '\0';
if (i)
for (tp = TimezoneTable; tp->name; tp++)
if (strcmp(buff, tp->name) == 0) {
yylval->Number = tp->value;
return tp->type;
}
return tID;
}
static int
yylex(YYSTYPE *yylval, const char **yyInput)
{
register char c;
register char *p;
char buff[20];
int Count;
int sign;
const char *inp = *yyInput;
for ( ; ; ) {
while (isspace((unsigned char)*inp))
inp++;
if (isdigit((unsigned char)(c = *inp)) || c == '-' || c == '+') {
if (c == '-' || c == '+') {
sign = c == '-' ? -1 : 1;
if (!isdigit((unsigned char)*++inp))
/* skip the '-' sign */
continue;
}
else
sign = 0;
for (yylval->Number = 0; isdigit((unsigned char)(c = *inp++)); )
yylval->Number = 10 * yylval->Number + c - '0';
if (sign < 0)
yylval->Number = -yylval->Number;
*yyInput = --inp;
return sign ? tSNUMBER : tUNUMBER;
}
if (isalpha((unsigned char)c)) {
for (p = buff; isalpha((unsigned char)(c = *inp++)) || c == '.'; )
if (p < &buff[sizeof buff - 1])
*p++ = c;
*p = '\0';
*yyInput = --inp;
return LookupWord(yylval, buff);
}
if (c == '@') {
*yyInput = ++inp;
return AT_SIGN;
}
if (c != '(') {
*yyInput = ++inp;
return c;
}
Count = 0;
do {
c = *inp++;
if (c == '\0')
return c;
if (c == '(')
Count++;
else if (c == ')')
Count--;
} while (Count > 0);
}
}
#define TM_YEAR_ORIGIN 1900
/* Yield A - B, measured in seconds. */
static time_t
difftm (struct tm *a, struct tm *b)
{
int ay = a->tm_year + (TM_YEAR_ORIGIN - 1);
int by = b->tm_year + (TM_YEAR_ORIGIN - 1);
int days = (
/* difference in day of year */
a->tm_yday - b->tm_yday
/* + intervening leap days */
+ ((ay >> 2) - (by >> 2))
- (ay/100 - by/100)
+ ((ay/100 >> 2) - (by/100 >> 2))
/* + difference in years * 365 */
+ (long)(ay-by) * 365
);
return ((time_t)60*(60*(24*days + (a->tm_hour - b->tm_hour))
+ (a->tm_min - b->tm_min))
+ (a->tm_sec - b->tm_sec));
}
time_t
parsedate(const char *p, const time_t *now, const int *zone)
{
struct tm gmt, local, *gmt_ptr, *tm;
time_t nowt;
int zonet;
time_t Start;
time_t tod, rm;
struct dateinfo param;
if (now == NULL || zone == NULL) {
now = &nowt;
zone = &zonet;
(void)time(&nowt);
gmt_ptr = gmtime_r(now, &gmt);
if ((tm = localtime_r(now, &local)) == NULL)
return -1;
if (gmt_ptr != NULL)
zonet = difftm(&gmt, &local) / 60;
else
/* We are on a system like VMS, where the system clock is
in local time and the system has no concept of timezones.
Hopefully we can fake this out (for the case in which the
user specifies no timezone) by just saying the timezone
is zero. */
zonet = 0;
if (local.tm_isdst)
zonet += 60;
} else {
if ((tm = localtime_r(now, &local)) == NULL)
return -1;
}
param.yyYear = tm->tm_year + 1900;
param.yyMonth = tm->tm_mon + 1;
param.yyDay = tm->tm_mday;
param.yyTimezone = *zone;
param.yyDSTmode = DSTmaybe;
param.yyHour = 0;
param.yyMinutes = 0;
param.yySeconds = 0;
param.yyMeridian = MER24;
param.yyRelSeconds = 0;
param.yyRelMonth = 0;
param.yyHaveDate = 0;
param.yyHaveDay = 0;
param.yyHaveRel = 0;
param.yyHaveTime = 0;
param.yyHaveZone = 0;
if (yyparse(&param, &p) || param.yyHaveTime > 1 || param.yyHaveZone > 1 ||
param.yyHaveDate > 1 || param.yyHaveDay > 1)
return -1;
if (param.yyHaveDate || param.yyHaveTime || param.yyHaveDay) {
Start = Convert(param.yyMonth, param.yyDay, param.yyYear, param.yyHour,
param.yyMinutes, param.yySeconds, param.yyTimezone,
param.yyMeridian, param.yyDSTmode);
if (Start < 0)
return -1;
}
else {
Start = *now;
if (!param.yyHaveRel)
Start -= ((tm->tm_hour * 60L + tm->tm_min) * 60L) + tm->tm_sec;
}
Start += param.yyRelSeconds;
rm = RelativeMonth(Start, param.yyRelMonth, param.yyTimezone);
if (rm == -1)
return -1;
Start += rm;
if (param.yyHaveDay && !param.yyHaveDate) {
tod = RelativeDate(Start, param.yyDayOrdinal, param.yyDayNumber);
Start += tod;
}
return Start;
}
#if defined(TEST)
/* ARGSUSED */
int
main(ac, av)
int ac;
char *av[];
{
char buff[128];
time_t d;
(void)printf("Enter date, or blank line to exit.\n\t> ");
(void)fflush(stdout);
while (gets(buff) && buff[0]) {
d = parsedate(buff, NULL, NULL);
if (d == -1)
(void)printf("Bad format - couldn't convert.\n");
else
(void)printf("%s", ctime(&d));
(void)printf("\t> ");
(void)fflush(stdout);
}
exit(0);
/* NOTREACHED */
}
#endif /* defined(TEST) */