1176998Sphk%{
2176998Sphk/*
3176998Sphk**  Originally written by Steven M. Bellovin <smb@research.att.com> while
4176998Sphk**  at the University of North Carolina at Chapel Hill.  Later tweaked by
5176998Sphk**  a couple of people on Usenet.  Completely overhauled by Rich $alz
6176998Sphk**  <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990;
7176998Sphk**
8176998Sphk**  This grammar has 10 shift/reduce conflicts.
9176998Sphk**
10176998Sphk**  This code is in the public domain and has no copyright.
11176998Sphk**
12176998Sphk**  Picked up from CVS and slightly cleaned up by to WARNS=5 level by
13176998Sphk**  Poul-Henning Kamp <phk@FreeBSD.org>
14176998Sphk**
15176998Sphk** $FreeBSD$
16176998Sphk*/
17176998Sphk
18176998Sphk#include <stdio.h>
19176998Sphk#include <stdlib.h>
20176998Sphk#include <ctype.h>
21176998Sphk#include <string.h>
22176998Sphk#include <sys/types.h>
23176998Sphk#include <sys/time.h>
24176998Sphk
25176998Sphk#include "libfifolog.h"
26176998Sphk
27176998Sphk#define yylex getdate_yylex
28176998Sphk#define yyerror getdate_yyerror
29176998Sphk
30176998Sphkstatic int yylex(void);
31176998Sphkstatic int yyerror(const char *);
32176998Sphk
33176998Sphk#define EPOCH		1970
34176998Sphk#define HOUR(x)		((time_t)(x) * 60)
35176998Sphk#define SECSPERDAY	(24L * 60L * 60L)
36176998Sphk
37176998Sphk
38176998Sphk/*
39176998Sphk**  An entry in the lexical lookup table.
40176998Sphk*/
41176998Sphktypedef struct _TABLE {
42176998Sphk    const char	*name;
43176998Sphk    int		type;
44176998Sphk    time_t	value;
45176998Sphk} TABLE;
46176998Sphk
47176998Sphk
48176998Sphk/*
49176998Sphk**  Daylight-savings mode:  on, off, or not yet known.
50176998Sphk*/
51176998Sphktypedef enum _DSTMODE {
52176998Sphk    DSToff, DSTon, DSTmaybe
53176998Sphk} DSTMODE;
54176998Sphk
55176998Sphk/*
56176998Sphk**  Meridian:  am, pm, or 24-hour style.
57176998Sphk*/
58176998Sphktypedef enum _MERIDIAN {
59176998Sphk    MERam, MERpm, MER24
60176998Sphk} MERIDIAN;
61176998Sphk
62176998Sphk
63176998Sphk/*
64176998Sphk**  Global variables.  We could get rid of most of these by using a good
65176998Sphk**  union as the yacc stack.  (This routine was originally written before
66176998Sphk**  yacc had the %union construct.)  Maybe someday; right now we only use
67176998Sphk**  the %union very rarely.
68176998Sphk*/
69176998Sphkstatic char	*yyInput;
70176998Sphkstatic DSTMODE	yyDSTmode;
71176998Sphkstatic time_t	yyDayOrdinal;
72176998Sphkstatic time_t	yyDayNumber;
73176998Sphkstatic int	yyHaveDate;
74176998Sphkstatic int	yyHaveDay;
75176998Sphkstatic int	yyHaveRel;
76176998Sphkstatic int	yyHaveTime;
77176998Sphkstatic int	yyHaveZone;
78176998Sphkstatic time_t	yyTimezone;
79176998Sphkstatic time_t	yyDay;
80176998Sphkstatic time_t	yyHour;
81176998Sphkstatic time_t	yyMinutes;
82176998Sphkstatic time_t	yyMonth;
83176998Sphkstatic time_t	yySeconds;
84176998Sphkstatic time_t	yyYear;
85176998Sphkstatic MERIDIAN	yyMeridian;
86176998Sphkstatic time_t	yyRelMonth;
87176998Sphkstatic time_t	yyRelSeconds;
88176998Sphk
89176998Sphk%}
90176998Sphk
91176998Sphk%union {
92176998Sphk    time_t		Number;
93176998Sphk    enum _MERIDIAN	Meridian;
94176998Sphk}
95176998Sphk
96176998Sphk%token	tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT
97176998Sphk%token	tSEC_UNIT tSNUMBER tUNUMBER tZONE tDST
98176998Sphk
99176998Sphk%type	<Number>	tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT
100176998Sphk%type	<Number>	tSEC_UNIT tSNUMBER tUNUMBER tZONE
101176998Sphk%type	<Meridian>	tMERIDIAN o_merid
102176998Sphk
103176998Sphk%%
104176998Sphk
105176998Sphkspec	: /* NULL */
106176998Sphk	| spec item
107176998Sphk	;
108176998Sphk
109176998Sphkitem	: time {
110176998Sphk	    yyHaveTime++;
111176998Sphk	}
112176998Sphk	| zone {
113176998Sphk	    yyHaveZone++;
114176998Sphk	}
115176998Sphk	| date {
116176998Sphk	    yyHaveDate++;
117176998Sphk	}
118176998Sphk	| day {
119176998Sphk	    yyHaveDay++;
120176998Sphk	}
121176998Sphk	| rel {
122176998Sphk	    yyHaveRel++;
123176998Sphk	}
124176998Sphk	| cvsstamp {
125176998Sphk	    yyHaveTime++;
126176998Sphk	    yyHaveDate++;
127176998Sphk	    yyHaveZone++;
128176998Sphk	}
129176998Sphk	| number
130176998Sphk	;
131176998Sphk
132176998Sphkcvsstamp: tUNUMBER '.' tUNUMBER '.' tUNUMBER '.' tUNUMBER '.' tUNUMBER '.' tUNUMBER {
133176998Sphk	    yyYear = $1;
134176998Sphk	    if (yyYear < 100) yyYear += 1900;
135176998Sphk	    yyMonth = $3;
136176998Sphk	    yyDay = $5;
137176998Sphk	    yyHour = $7;
138176998Sphk	    yyMinutes = $9;
139176998Sphk	    yySeconds = $11;
140176998Sphk	    yyDSTmode = DSToff;
141176998Sphk	    yyTimezone = 0;
142176998Sphk	}
143176998Sphk	;
144176998Sphk
145176998Sphktime	: tUNUMBER tMERIDIAN {
146176998Sphk	    yyHour = $1;
147176998Sphk	    yyMinutes = 0;
148176998Sphk	    yySeconds = 0;
149176998Sphk	    yyMeridian = $2;
150176998Sphk	}
151176998Sphk	| tUNUMBER ':' tUNUMBER o_merid {
152176998Sphk	    yyHour = $1;
153176998Sphk	    yyMinutes = $3;
154176998Sphk	    yySeconds = 0;
155176998Sphk	    yyMeridian = $4;
156176998Sphk	}
157176998Sphk	| tUNUMBER ':' tUNUMBER tSNUMBER {
158176998Sphk	    yyHour = $1;
159176998Sphk	    yyMinutes = $3;
160176998Sphk	    yyMeridian = MER24;
161176998Sphk	    yyDSTmode = DSToff;
162176998Sphk	    yyTimezone = - ($4 % 100 + ($4 / 100) * 60);
163176998Sphk	}
164176998Sphk	| tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
165176998Sphk	    yyHour = $1;
166176998Sphk	    yyMinutes = $3;
167176998Sphk	    yySeconds = $5;
168176998Sphk	    yyMeridian = $6;
169176998Sphk	}
170176998Sphk	| tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER {
171176998Sphk	    yyHour = $1;
172176998Sphk	    yyMinutes = $3;
173176998Sphk	    yySeconds = $5;
174176998Sphk	    yyMeridian = MER24;
175176998Sphk	    yyDSTmode = DSToff;
176176998Sphk	    yyTimezone = - ($6 % 100 + ($6 / 100) * 60);
177176998Sphk	}
178176998Sphk	;
179176998Sphk
180176998Sphkzone	: tZONE {
181176998Sphk	    yyTimezone = $1;
182176998Sphk	    yyDSTmode = DSToff;
183176998Sphk	}
184176998Sphk	| tDAYZONE {
185176998Sphk	    yyTimezone = $1;
186176998Sphk	    yyDSTmode = DSTon;
187176998Sphk	}
188176998Sphk	|
189176998Sphk	  tZONE tDST {
190176998Sphk	    yyTimezone = $1;
191176998Sphk	    yyDSTmode = DSTon;
192176998Sphk	}
193176998Sphk	;
194176998Sphk
195176998Sphkday	: tDAY {
196176998Sphk	    yyDayOrdinal = 1;
197176998Sphk	    yyDayNumber = $1;
198176998Sphk	}
199176998Sphk	| tDAY ',' {
200176998Sphk	    yyDayOrdinal = 1;
201176998Sphk	    yyDayNumber = $1;
202176998Sphk	}
203176998Sphk	| tUNUMBER tDAY {
204176998Sphk	    yyDayOrdinal = $1;
205176998Sphk	    yyDayNumber = $2;
206176998Sphk	}
207176998Sphk	;
208176998Sphk
209176998Sphkdate	: tUNUMBER '/' tUNUMBER {
210176998Sphk	    yyMonth = $1;
211176998Sphk	    yyDay = $3;
212176998Sphk	}
213176998Sphk	| tUNUMBER '/' tUNUMBER '/' tUNUMBER {
214176998Sphk	    if ($1 >= 100) {
215176998Sphk		yyYear = $1;
216176998Sphk		yyMonth = $3;
217176998Sphk		yyDay = $5;
218176998Sphk	    } else {
219176998Sphk		yyMonth = $1;
220176998Sphk		yyDay = $3;
221176998Sphk		yyYear = $5;
222176998Sphk	    }
223176998Sphk	}
224176998Sphk	| tUNUMBER tSNUMBER tSNUMBER {
225176998Sphk	    /* ISO 8601 format.  yyyy-mm-dd.  */
226176998Sphk	    yyYear = $1;
227176998Sphk	    yyMonth = -$2;
228176998Sphk	    yyDay = -$3;
229176998Sphk	}
230176998Sphk	| tUNUMBER tMONTH tSNUMBER {
231176998Sphk	    /* e.g. 17-JUN-1992.  */
232176998Sphk	    yyDay = $1;
233176998Sphk	    yyMonth = $2;
234176998Sphk	    yyYear = -$3;
235176998Sphk	}
236176998Sphk	| tMONTH tUNUMBER {
237176998Sphk	    yyMonth = $1;
238176998Sphk	    yyDay = $2;
239176998Sphk	}
240176998Sphk	| tMONTH tUNUMBER ',' tUNUMBER {
241176998Sphk	    yyMonth = $1;
242176998Sphk	    yyDay = $2;
243176998Sphk	    yyYear = $4;
244176998Sphk	}
245176998Sphk	| tUNUMBER tMONTH {
246176998Sphk	    yyMonth = $2;
247176998Sphk	    yyDay = $1;
248176998Sphk	}
249176998Sphk	| tUNUMBER tMONTH tUNUMBER {
250176998Sphk	    yyMonth = $2;
251176998Sphk	    yyDay = $1;
252176998Sphk	    yyYear = $3;
253176998Sphk	}
254176998Sphk	;
255176998Sphk
256176998Sphkrel	: relunit tAGO {
257176998Sphk	    yyRelSeconds = -yyRelSeconds;
258176998Sphk	    yyRelMonth = -yyRelMonth;
259176998Sphk	}
260176998Sphk	| relunit
261176998Sphk	;
262176998Sphk
263176998Sphkrelunit	: tUNUMBER tMINUTE_UNIT {
264176998Sphk	    yyRelSeconds += $1 * $2 * 60L;
265176998Sphk	}
266176998Sphk	| tSNUMBER tMINUTE_UNIT {
267176998Sphk	    yyRelSeconds += $1 * $2 * 60L;
268176998Sphk	}
269176998Sphk	| tMINUTE_UNIT {
270176998Sphk	    yyRelSeconds += $1 * 60L;
271176998Sphk	}
272176998Sphk	| tSNUMBER tSEC_UNIT {
273176998Sphk	    yyRelSeconds += $1;
274176998Sphk	}
275176998Sphk	| tUNUMBER tSEC_UNIT {
276176998Sphk	    yyRelSeconds += $1;
277176998Sphk	}
278176998Sphk	| tSEC_UNIT {
279176998Sphk	    yyRelSeconds++;
280176998Sphk	}
281176998Sphk	| tSNUMBER tMONTH_UNIT {
282176998Sphk	    yyRelMonth += $1 * $2;
283176998Sphk	}
284176998Sphk	| tUNUMBER tMONTH_UNIT {
285176998Sphk	    yyRelMonth += $1 * $2;
286176998Sphk	}
287176998Sphk	| tMONTH_UNIT {
288176998Sphk	    yyRelMonth += $1;
289176998Sphk	}
290176998Sphk	;
291176998Sphk
292176998Sphknumber	: tUNUMBER {
293176998Sphk	    if (yyHaveTime && yyHaveDate && !yyHaveRel)
294176998Sphk		yyYear = $1;
295176998Sphk	    else {
296176998Sphk		if($1>10000) {
297176998Sphk		    yyHaveDate++;
298176998Sphk		    yyDay= ($1)%100;
299176998Sphk		    yyMonth= ($1/100)%100;
300176998Sphk		    yyYear = $1/10000;
301176998Sphk		}
302176998Sphk		else {
303176998Sphk		    yyHaveTime++;
304176998Sphk		    if ($1 < 100) {
305176998Sphk			yyHour = $1;
306176998Sphk			yyMinutes = 0;
307176998Sphk		    }
308176998Sphk		    else {
309176998Sphk		    	yyHour = $1 / 100;
310176998Sphk		    	yyMinutes = $1 % 100;
311176998Sphk		    }
312176998Sphk		    yySeconds = 0;
313176998Sphk		    yyMeridian = MER24;
314176998Sphk	        }
315176998Sphk	    }
316176998Sphk	}
317176998Sphk	;
318176998Sphk
319176998Sphko_merid	: /* NULL */ {
320176998Sphk	    $$ = MER24;
321176998Sphk	}
322176998Sphk	| tMERIDIAN {
323176998Sphk	    $$ = $1;
324176998Sphk	}
325176998Sphk	;
326176998Sphk
327176998Sphk%%
328176998Sphk
329176998Sphk/* Month and day table. */
330176998Sphkstatic TABLE const MonthDayTable[] = {
331176998Sphk    { "january",	tMONTH,  1 },
332176998Sphk    { "february",	tMONTH,  2 },
333176998Sphk    { "march",		tMONTH,  3 },
334176998Sphk    { "april",		tMONTH,  4 },
335176998Sphk    { "may",		tMONTH,  5 },
336176998Sphk    { "june",		tMONTH,  6 },
337176998Sphk    { "july",		tMONTH,  7 },
338176998Sphk    { "august",		tMONTH,  8 },
339176998Sphk    { "september",	tMONTH,  9 },
340176998Sphk    { "sept",		tMONTH,  9 },
341176998Sphk    { "october",	tMONTH, 10 },
342176998Sphk    { "november",	tMONTH, 11 },
343176998Sphk    { "december",	tMONTH, 12 },
344176998Sphk    { "sunday",		tDAY, 0 },
345176998Sphk    { "monday",		tDAY, 1 },
346176998Sphk    { "tuesday",	tDAY, 2 },
347176998Sphk    { "tues",		tDAY, 2 },
348176998Sphk    { "wednesday",	tDAY, 3 },
349176998Sphk    { "wednes",		tDAY, 3 },
350176998Sphk    { "thursday",	tDAY, 4 },
351176998Sphk    { "thur",		tDAY, 4 },
352176998Sphk    { "thurs",		tDAY, 4 },
353176998Sphk    { "friday",		tDAY, 5 },
354176998Sphk    { "saturday",	tDAY, 6 },
355176998Sphk    { NULL,		0,    0 }
356176998Sphk};
357176998Sphk
358176998Sphk/* Time units table. */
359176998Sphkstatic TABLE const UnitsTable[] = {
360176998Sphk    { "year",		tMONTH_UNIT,	12 },
361176998Sphk    { "month",		tMONTH_UNIT,	1 },
362176998Sphk    { "fortnight",	tMINUTE_UNIT,	14 * 24 * 60 },
363176998Sphk    { "week",		tMINUTE_UNIT,	7 * 24 * 60 },
364176998Sphk    { "day",		tMINUTE_UNIT,	1 * 24 * 60 },
365176998Sphk    { "hour",		tMINUTE_UNIT,	60 },
366176998Sphk    { "minute",		tMINUTE_UNIT,	1 },
367176998Sphk    { "min",		tMINUTE_UNIT,	1 },
368176998Sphk    { "second",		tSEC_UNIT,	1 },
369176998Sphk    { "sec",		tSEC_UNIT,	1 },
370176998Sphk    { NULL,		0,		0 }
371176998Sphk};
372176998Sphk
373176998Sphk/* Assorted relative-time words. */
374176998Sphkstatic TABLE const OtherTable[] = {
375176998Sphk    { "tomorrow",	tMINUTE_UNIT,	1 * 24 * 60 },
376176998Sphk    { "yesterday",	tMINUTE_UNIT,	-1 * 24 * 60 },
377176998Sphk    { "today",		tMINUTE_UNIT,	0 },
378176998Sphk    { "now",		tMINUTE_UNIT,	0 },
379176998Sphk    { "last",		tUNUMBER,	-1 },
380176998Sphk    { "this",		tMINUTE_UNIT,	0 },
381176998Sphk    { "next",		tUNUMBER,	2 },
382176998Sphk    { "first",		tUNUMBER,	1 },
383176998Sphk/*  { "second",		tUNUMBER,	2 }, */
384176998Sphk    { "third",		tUNUMBER,	3 },
385176998Sphk    { "fourth",		tUNUMBER,	4 },
386176998Sphk    { "fifth",		tUNUMBER,	5 },
387176998Sphk    { "sixth",		tUNUMBER,	6 },
388176998Sphk    { "seventh",	tUNUMBER,	7 },
389176998Sphk    { "eighth",		tUNUMBER,	8 },
390176998Sphk    { "ninth",		tUNUMBER,	9 },
391176998Sphk    { "tenth",		tUNUMBER,	10 },
392176998Sphk    { "eleventh",	tUNUMBER,	11 },
393176998Sphk    { "twelfth",	tUNUMBER,	12 },
394176998Sphk    { "ago",		tAGO,		1 },
395176998Sphk    { NULL,		0,		0 }
396176998Sphk};
397176998Sphk
398176998Sphk/* The timezone table. */
399176998Sphk/* Some of these are commented out because a time_t can't store a float. */
400176998Sphkstatic TABLE const TimezoneTable[] = {
401176998Sphk    { "gmt",	tZONE,     HOUR( 0) },	/* Greenwich Mean */
402176998Sphk    { "ut",	tZONE,     HOUR( 0) },	/* Universal (Coordinated) */
403176998Sphk    { "utc",	tZONE,     HOUR( 0) },
404176998Sphk    { "wet",	tZONE,     HOUR( 0) },	/* Western European */
405176998Sphk    { "bst",	tDAYZONE,  HOUR( 0) },	/* British Summer */
406176998Sphk    { "wat",	tZONE,     HOUR( 1) },	/* West Africa */
407176998Sphk    { "at",	tZONE,     HOUR( 2) },	/* Azores */
408176998Sphk#if	0
409176998Sphk    /* For completeness.  BST is also British Summer, and GST is
410176998Sphk     * also Guam Standard. */
411176998Sphk    { "bst",	tZONE,     HOUR( 3) },	/* Brazil Standard */
412176998Sphk    { "gst",	tZONE,     HOUR( 3) },	/* Greenland Standard */
413176998Sphk#endif
414176998Sphk#if 0
415176998Sphk    { "nft",	tZONE,     HOUR(3.5) },	/* Newfoundland */
416176998Sphk    { "nst",	tZONE,     HOUR(3.5) },	/* Newfoundland Standard */
417176998Sphk    { "ndt",	tDAYZONE,  HOUR(3.5) },	/* Newfoundland Daylight */
418176998Sphk#endif
419176998Sphk    { "ast",	tZONE,     HOUR( 4) },	/* Atlantic Standard */
420176998Sphk    { "adt",	tDAYZONE,  HOUR( 4) },	/* Atlantic Daylight */
421176998Sphk    { "est",	tZONE,     HOUR( 5) },	/* Eastern Standard */
422176998Sphk    { "edt",	tDAYZONE,  HOUR( 5) },	/* Eastern Daylight */
423176998Sphk    { "cst",	tZONE,     HOUR( 6) },	/* Central Standard */
424176998Sphk    { "cdt",	tDAYZONE,  HOUR( 6) },	/* Central Daylight */
425176998Sphk    { "mst",	tZONE,     HOUR( 7) },	/* Mountain Standard */
426176998Sphk    { "mdt",	tDAYZONE,  HOUR( 7) },	/* Mountain Daylight */
427176998Sphk    { "pst",	tZONE,     HOUR( 8) },	/* Pacific Standard */
428176998Sphk    { "pdt",	tDAYZONE,  HOUR( 8) },	/* Pacific Daylight */
429176998Sphk    { "yst",	tZONE,     HOUR( 9) },	/* Yukon Standard */
430176998Sphk    { "ydt",	tDAYZONE,  HOUR( 9) },	/* Yukon Daylight */
431176998Sphk    { "hst",	tZONE,     HOUR(10) },	/* Hawaii Standard */
432176998Sphk    { "hdt",	tDAYZONE,  HOUR(10) },	/* Hawaii Daylight */
433176998Sphk    { "cat",	tZONE,     HOUR(10) },	/* Central Alaska */
434176998Sphk    { "ahst",	tZONE,     HOUR(10) },	/* Alaska-Hawaii Standard */
435176998Sphk    { "nt",	tZONE,     HOUR(11) },	/* Nome */
436176998Sphk    { "idlw",	tZONE,     HOUR(12) },	/* International Date Line West */
437176998Sphk    { "cet",	tZONE,     -HOUR(1) },	/* Central European */
438176998Sphk    { "met",	tZONE,     -HOUR(1) },	/* Middle European */
439176998Sphk    { "mewt",	tZONE,     -HOUR(1) },	/* Middle European Winter */
440176998Sphk    { "mest",	tDAYZONE,  -HOUR(1) },	/* Middle European Summer */
441176998Sphk    { "swt",	tZONE,     -HOUR(1) },	/* Swedish Winter */
442176998Sphk    { "sst",	tDAYZONE,  -HOUR(1) },	/* Swedish Summer */
443176998Sphk    { "fwt",	tZONE,     -HOUR(1) },	/* French Winter */
444176998Sphk    { "fst",	tDAYZONE,  -HOUR(1) },	/* French Summer */
445176998Sphk    { "eet",	tZONE,     -HOUR(2) },	/* Eastern Europe, USSR Zone 1 */
446176998Sphk    { "bt",	tZONE,     -HOUR(3) },	/* Baghdad, USSR Zone 2 */
447176998Sphk#if 0
448176998Sphk    { "it",	tZONE,     -HOUR(3.5) },/* Iran */
449176998Sphk#endif
450176998Sphk    { "zp4",	tZONE,     -HOUR(4) },	/* USSR Zone 3 */
451176998Sphk    { "zp5",	tZONE,     -HOUR(5) },	/* USSR Zone 4 */
452176998Sphk#if 0
453176998Sphk    { "ist",	tZONE,     -HOUR(5.5) },/* Indian Standard */
454176998Sphk#endif
455176998Sphk    { "zp6",	tZONE,     -HOUR(6) },	/* USSR Zone 5 */
456176998Sphk#if	0
457176998Sphk    /* For completeness.  NST is also Newfoundland Stanard, and SST is
458176998Sphk     * also Swedish Summer. */
459176998Sphk    { "nst",	tZONE,     -HOUR(6.5) },/* North Sumatra */
460176998Sphk    { "sst",	tZONE,     -HOUR(7) },	/* South Sumatra, USSR Zone 6 */
461176998Sphk#endif	/* 0 */
462176998Sphk    { "wast",	tZONE,     -HOUR(7) },	/* West Australian Standard */
463176998Sphk    { "wadt",	tDAYZONE,  -HOUR(7) },	/* West Australian Daylight */
464176998Sphk#if 0
465176998Sphk    { "jt",	tZONE,     -HOUR(7.5) },/* Java (3pm in Cronusland!) */
466176998Sphk#endif
467176998Sphk    { "cct",	tZONE,     -HOUR(8) },	/* China Coast, USSR Zone 7 */
468176998Sphk    { "jst",	tZONE,     -HOUR(9) },	/* Japan Standard, USSR Zone 8 */
469176998Sphk#if 0
470176998Sphk    { "cast",	tZONE,     -HOUR(9.5) },/* Central Australian Standard */
471176998Sphk    { "cadt",	tDAYZONE,  -HOUR(9.5) },/* Central Australian Daylight */
472176998Sphk#endif
473176998Sphk    { "east",	tZONE,     -HOUR(10) },	/* Eastern Australian Standard */
474176998Sphk    { "eadt",	tDAYZONE,  -HOUR(10) },	/* Eastern Australian Daylight */
475176998Sphk    { "gst",	tZONE,     -HOUR(10) },	/* Guam Standard, USSR Zone 9 */
476176998Sphk    { "nzt",	tZONE,     -HOUR(12) },	/* New Zealand */
477176998Sphk    { "nzst",	tZONE,     -HOUR(12) },	/* New Zealand Standard */
478176998Sphk    { "nzdt",	tDAYZONE,  -HOUR(12) },	/* New Zealand Daylight */
479176998Sphk    { "idle",	tZONE,     -HOUR(12) },	/* International Date Line East */
480176998Sphk    {  NULL,	0,	   0 }
481176998Sphk};
482176998Sphk
483176998Sphk/* Military timezone table. */
484176998Sphkstatic TABLE const MilitaryTable[] = {
485176998Sphk    { "a",	tZONE,	HOUR(  1) },
486176998Sphk    { "b",	tZONE,	HOUR(  2) },
487176998Sphk    { "c",	tZONE,	HOUR(  3) },
488176998Sphk    { "d",	tZONE,	HOUR(  4) },
489176998Sphk    { "e",	tZONE,	HOUR(  5) },
490176998Sphk    { "f",	tZONE,	HOUR(  6) },
491176998Sphk    { "g",	tZONE,	HOUR(  7) },
492176998Sphk    { "h",	tZONE,	HOUR(  8) },
493176998Sphk    { "i",	tZONE,	HOUR(  9) },
494176998Sphk    { "k",	tZONE,	HOUR( 10) },
495176998Sphk    { "l",	tZONE,	HOUR( 11) },
496176998Sphk    { "m",	tZONE,	HOUR( 12) },
497176998Sphk    { "n",	tZONE,	HOUR(- 1) },
498176998Sphk    { "o",	tZONE,	HOUR(- 2) },
499176998Sphk    { "p",	tZONE,	HOUR(- 3) },
500176998Sphk    { "q",	tZONE,	HOUR(- 4) },
501176998Sphk    { "r",	tZONE,	HOUR(- 5) },
502176998Sphk    { "s",	tZONE,	HOUR(- 6) },
503176998Sphk    { "t",	tZONE,	HOUR(- 7) },
504176998Sphk    { "u",	tZONE,	HOUR(- 8) },
505176998Sphk    { "v",	tZONE,	HOUR(- 9) },
506176998Sphk    { "w",	tZONE,	HOUR(-10) },
507176998Sphk    { "x",	tZONE,	HOUR(-11) },
508176998Sphk    { "y",	tZONE,	HOUR(-12) },
509176998Sphk    { "z",	tZONE,	HOUR(  0) },
510176998Sphk    { NULL,	0,  0 }
511176998Sphk};
512176998Sphk
513176998Sphk
514176998Sphk
515176998Sphk
516176998Sphk/* ARGSUSED */
517176998Sphkstatic int
518176998Sphkyyerror(const char *s __unused)
519176998Sphk{
520176998Sphk  return 0;
521176998Sphk}
522176998Sphk
523176998Sphk
524176998Sphkstatic time_t
525176998SphkToSeconds(time_t Hours, time_t Minutes, time_t Seconds, MERIDIAN Meridian)
526176998Sphk{
527176998Sphk    if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59)
528176998Sphk	return -1;
529176998Sphk    switch (Meridian) {
530176998Sphk    case MER24:
531176998Sphk	if (Hours < 0 || Hours > 23)
532176998Sphk	    return -1;
533176998Sphk	return (Hours * 60L + Minutes) * 60L + Seconds;
534176998Sphk    case MERam:
535176998Sphk	if (Hours < 1 || Hours > 12)
536176998Sphk	    return -1;
537176998Sphk	if (Hours == 12)
538176998Sphk	    Hours = 0;
539176998Sphk	return (Hours * 60L + Minutes) * 60L + Seconds;
540176998Sphk    case MERpm:
541176998Sphk	if (Hours < 1 || Hours > 12)
542176998Sphk	    return -1;
543176998Sphk	if (Hours == 12)
544176998Sphk	    Hours = 0;
545176998Sphk	return ((Hours + 12) * 60L + Minutes) * 60L + Seconds;
546176998Sphk    default:
547176998Sphk	abort ();
548176998Sphk    }
549176998Sphk    /* NOTREACHED */
550176998Sphk}
551176998Sphk
552176998Sphk
553176998Sphk/* Year is either
554176998Sphk   * A negative number, which means to use its absolute value (why?)
555176998Sphk   * A number from 0 to 99, which means a year from 1900 to 1999, or
556176998Sphk   * The actual year (>=100).  */
557176998Sphkstatic time_t
558176998SphkConvert(time_t Month, time_t Day, time_t Year,
559176998Sphk    time_t Hours, time_t Minutes, time_t Seconds,
560176998Sphk    MERIDIAN Meridian, DSTMODE DSTmode)
561176998Sphk{
562176998Sphk    static int DaysInMonth[12] = {
563176998Sphk	31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
564176998Sphk    };
565176998Sphk    time_t	tod;
566176998Sphk    time_t	Julian;
567176998Sphk    int		i;
568176998Sphk    struct tm   *ltm;
569176998Sphk
570176998Sphk    if (Year < 0)
571176998Sphk	Year = -Year;
572176998Sphk    if (Year < 69)
573176998Sphk	Year += 2000;
574176998Sphk    else if (Year < 100)
575176998Sphk	Year += 1900;
576176998Sphk    DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0)
577176998Sphk		    ? 29 : 28;
578176998Sphk    /* Checking for 2038 bogusly assumes that time_t is 32 bits.  But
579176998Sphk       I'm too lazy to try to check for time_t overflow in another way.  */
580176998Sphk    if (Year < EPOCH || Year > 2038
581176998Sphk     || Month < 1 || Month > 12
582176998Sphk     /* Lint fluff:  "conversion from long may lose accuracy" */
583176998Sphk     || Day < 1 || Day > DaysInMonth[(int)--Month])
584176998Sphk	/* FIXME:
585176998Sphk	 * It would be nice to set a global error string here.
586176998Sphk	 * "February 30 is not a valid date" is much more informative than
587176998Sphk	 * "Can't parse date/time: 100 months" when the user input was
588176998Sphk	 * "100 months" and addition resolved that to February 30, for
589176998Sphk	 * example.  See rcs2-7 in src/sanity.sh for more. */
590176998Sphk	return -1;
591176998Sphk
592176998Sphk    for (Julian = Day - 1, i = 0; i < Month; i++)
593176998Sphk	Julian += DaysInMonth[i];
594176998Sphk    for (i = EPOCH; i < Year; i++)
595176998Sphk	Julian += 365 + (i % 4 == 0);
596176998Sphk    Julian *= SECSPERDAY;
597176998Sphk    Julian += yyTimezone * 60L;
598176998Sphk    if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0)
599176998Sphk	return -1;
600176998Sphk    Julian += tod;
601176998Sphk    ltm = localtime(&Julian);
602176998Sphkfprintf(stderr, "DST %d TZ %s %d\n", DSTmode, ltm->tm_zone, ltm->tm_isdst);
603176998Sphk    if (DSTmode == DSTon
604176998Sphk     || (DSTmode == DSTmaybe && localtime(&Julian)->tm_isdst))
605176998Sphk	Julian -= 60 * 60;
606176998Sphk    return Julian;
607176998Sphk}
608176998Sphk
609176998Sphk
610176998Sphkstatic time_t
611176998SphkDSTcorrect(time_t Start, time_t Future)
612176998Sphk{
613176998Sphk    time_t	StartDay;
614176998Sphk    time_t	FutureDay;
615176998Sphk
616176998Sphk    StartDay = (localtime(&Start)->tm_hour + 1) % 24;
617176998Sphk    FutureDay = (localtime(&Future)->tm_hour + 1) % 24;
618176998Sphk    return (Future - Start) + (StartDay - FutureDay) * 60L * 60L;
619176998Sphk}
620176998Sphk
621176998Sphk
622176998Sphkstatic time_t
623176998SphkRelativeDate(time_t Start, time_t DayOrdinal, time_t DayNumber)
624176998Sphk{
625176998Sphk    struct tm	*tm;
626176998Sphk    time_t	now;
627176998Sphk
628176998Sphk    now = Start;
629176998Sphk    tm = localtime(&now);
630176998Sphk    now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7);
631176998Sphk    now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1);
632176998Sphk    return DSTcorrect(Start, now);
633176998Sphk}
634176998Sphk
635176998Sphk
636176998Sphkstatic time_t
637176998SphkRelativeMonth(time_t Start, time_t RelMonth)
638176998Sphk{
639176998Sphk    struct tm	*tm;
640176998Sphk    time_t	Month;
641176998Sphk    time_t	Year;
642176998Sphk
643176998Sphk    if (RelMonth == 0)
644176998Sphk	return 0;
645176998Sphk    tm = localtime(&Start);
646176998Sphk    Month = 12 * (tm->tm_year + 1900) + tm->tm_mon + RelMonth;
647176998Sphk    Year = Month / 12;
648176998Sphk    Month = Month % 12 + 1;
649176998Sphk    return DSTcorrect(Start,
650176998Sphk	    Convert(Month, (time_t)tm->tm_mday, Year,
651176998Sphk		(time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec,
652176998Sphk		MER24, DSTmaybe));
653176998Sphk}
654176998Sphk
655176998Sphk
656176998Sphkstatic int
657176998SphkLookupWord(char *buff)
658176998Sphk{
659176998Sphk    char	*p;
660176998Sphk    char	*q;
661176998Sphk    const TABLE	*tp;
662176998Sphk    int			i;
663176998Sphk    int			abbrev;
664176998Sphk
665176998Sphk    /* Make it lowercase. */
666176998Sphk    for (p = buff; *p; p++)
667176998Sphk	if (isupper(*p))
668176998Sphk	    *p = tolower(*p);
669176998Sphk
670176998Sphk    if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) {
671176998Sphk	yylval.Meridian = MERam;
672176998Sphk	return tMERIDIAN;
673176998Sphk    }
674176998Sphk    if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) {
675176998Sphk	yylval.Meridian = MERpm;
676176998Sphk	return tMERIDIAN;
677176998Sphk    }
678176998Sphk
679176998Sphk    /* See if we have an abbreviation for a month. */
680176998Sphk    if (strlen(buff) == 3)
681176998Sphk	abbrev = 1;
682176998Sphk    else if (strlen(buff) == 4 && buff[3] == '.') {
683176998Sphk	abbrev = 1;
684176998Sphk	buff[3] = '\0';
685176998Sphk    }
686176998Sphk    else
687176998Sphk	abbrev = 0;
688176998Sphk
689176998Sphk    for (tp = MonthDayTable; tp->name; tp++) {
690176998Sphk	if (abbrev) {
691176998Sphk	    if (strncmp(buff, tp->name, 3) == 0) {
692176998Sphk		yylval.Number = tp->value;
693176998Sphk		return tp->type;
694176998Sphk	    }
695176998Sphk	}
696176998Sphk	else if (strcmp(buff, tp->name) == 0) {
697176998Sphk	    yylval.Number = tp->value;
698176998Sphk	    return tp->type;
699176998Sphk	}
700176998Sphk    }
701176998Sphk
702176998Sphk    for (tp = TimezoneTable; tp->name; tp++)
703176998Sphk	if (strcmp(buff, tp->name) == 0) {
704176998Sphk	    yylval.Number = tp->value;
705176998Sphk	    return tp->type;
706176998Sphk	}
707176998Sphk
708176998Sphk    if (strcmp(buff, "dst") == 0)
709176998Sphk	return tDST;
710176998Sphk
711176998Sphk    for (tp = UnitsTable; tp->name; tp++)
712176998Sphk	if (strcmp(buff, tp->name) == 0) {
713176998Sphk	    yylval.Number = tp->value;
714176998Sphk	    return tp->type;
715176998Sphk	}
716176998Sphk
717176998Sphk    /* Strip off any plural and try the units table again. */
718176998Sphk    i = strlen(buff) - 1;
719176998Sphk    if (buff[i] == 's') {
720176998Sphk	buff[i] = '\0';
721176998Sphk	for (tp = UnitsTable; tp->name; tp++)
722176998Sphk	    if (strcmp(buff, tp->name) == 0) {
723176998Sphk		yylval.Number = tp->value;
724176998Sphk		return tp->type;
725176998Sphk	    }
726176998Sphk	buff[i] = 's';		/* Put back for "this" in OtherTable. */
727176998Sphk    }
728176998Sphk
729176998Sphk    for (tp = OtherTable; tp->name; tp++)
730176998Sphk	if (strcmp(buff, tp->name) == 0) {
731176998Sphk	    yylval.Number = tp->value;
732176998Sphk	    return tp->type;
733176998Sphk	}
734176998Sphk
735176998Sphk    /* Military timezones. */
736176998Sphk    if (buff[1] == '\0' && isalpha(*buff)) {
737176998Sphk	for (tp = MilitaryTable; tp->name; tp++)
738176998Sphk	    if (strcmp(buff, tp->name) == 0) {
739176998Sphk		yylval.Number = tp->value;
740176998Sphk		return tp->type;
741176998Sphk	    }
742176998Sphk    }
743176998Sphk
744176998Sphk    /* Drop out any periods and try the timezone table again. */
745176998Sphk    for (i = 0, p = q = buff; *q; q++)
746176998Sphk	if (*q != '.')
747176998Sphk	    *p++ = *q;
748176998Sphk	else
749176998Sphk	    i++;
750176998Sphk    *p = '\0';
751176998Sphk    if (i)
752176998Sphk	for (tp = TimezoneTable; tp->name; tp++)
753176998Sphk	    if (strcmp(buff, tp->name) == 0) {
754176998Sphk		yylval.Number = tp->value;
755176998Sphk		return tp->type;
756176998Sphk	    }
757176998Sphk
758176998Sphk    return tID;
759176998Sphk}
760176998Sphk
761176998Sphk
762176998Sphkstatic int
763201227Sedyylex(void)
764176998Sphk{
765176998Sphk    char	c;
766176998Sphk    char	*p;
767176998Sphk    char		buff[20];
768176998Sphk    int			Count;
769176998Sphk    int			sign;
770176998Sphk
771176998Sphk    for ( ; ; ) {
772176998Sphk	while (isspace(*yyInput))
773176998Sphk	    yyInput++;
774176998Sphk
775176998Sphk	if (isdigit(c = *yyInput) || c == '-' || c == '+') {
776176998Sphk	    if (c == '-' || c == '+') {
777176998Sphk		sign = c == '-' ? -1 : 1;
778176998Sphk		if (!isdigit(*++yyInput))
779176998Sphk		    /* skip the '-' sign */
780176998Sphk		    continue;
781176998Sphk	    }
782176998Sphk	    else
783176998Sphk		sign = 0;
784176998Sphk	    for (yylval.Number = 0; isdigit(c = *yyInput++); )
785176998Sphk		yylval.Number = 10 * yylval.Number + c - '0';
786176998Sphk	    yyInput--;
787176998Sphk	    if (sign < 0)
788176998Sphk		yylval.Number = -yylval.Number;
789176998Sphk	    return sign ? tSNUMBER : tUNUMBER;
790176998Sphk	}
791176998Sphk	if (isalpha(c)) {
792176998Sphk	    for (p = buff; isalpha(c = *yyInput++) || c == '.'; )
793176998Sphk		if (p < &buff[sizeof buff - 1])
794176998Sphk		    *p++ = c;
795176998Sphk	    *p = '\0';
796176998Sphk	    yyInput--;
797176998Sphk	    return LookupWord(buff);
798176998Sphk	}
799176998Sphk	if (c != '(')
800176998Sphk	    return *yyInput++;
801176998Sphk	Count = 0;
802176998Sphk	do {
803176998Sphk	    c = *yyInput++;
804176998Sphk	    if (c == '\0')
805176998Sphk		return c;
806176998Sphk	    if (c == '(')
807176998Sphk		Count++;
808176998Sphk	    else if (c == ')')
809176998Sphk		Count--;
810176998Sphk	} while (Count > 0);
811176998Sphk    }
812176998Sphk}
813176998Sphk
814176998Sphk#define TM_YEAR_ORIGIN 1900
815176998Sphk
816176998Sphktime_t
817176998Sphkget_date(char *p)
818176998Sphk{
819176998Sphk    struct tm		*tm, gmt;
820176998Sphk    time_t		Start;
821176998Sphk    time_t		tod;
822176998Sphk    time_t nowtime;
823176998Sphk    struct tm *gmt_ptr;
824176998Sphk
825176998Sphk    yyInput = p;
826176998Sphk
827176998Sphk    (void)time (&nowtime);
828176998Sphk
829176998Sphk    gmt_ptr = gmtime (&nowtime);
830176998Sphk    if (gmt_ptr != NULL)
831176998Sphk    {
832176998Sphk	/* Make a copy, in case localtime modifies *tm (I think
833176998Sphk	   that comment now applies to *gmt_ptr, but I am too
834176998Sphk	   lazy to dig into how gmtime and locatime allocate the
835176998Sphk	   structures they return pointers to).  */
836176998Sphk	gmt = *gmt_ptr;
837176998Sphk    }
838176998Sphk
839176998Sphk    if (! (tm = localtime (&nowtime)))
840176998Sphk	return -1;
841176998Sphk
842176998Sphk    tm = localtime(&nowtime);
843176998Sphk    yyYear = tm->tm_year + 1900;
844176998Sphk    yyMonth = tm->tm_mon + 1;
845176998Sphk    yyDay = tm->tm_mday;
846176998Sphk    yyTimezone = tm->tm_gmtoff;
847176998Sphk    yyDSTmode = DSTmaybe;
848176998Sphk    yyHour = 0;
849176998Sphk    yyMinutes = 0;
850176998Sphk    yySeconds = 0;
851176998Sphk    yyMeridian = MER24;
852176998Sphk    yyRelSeconds = 0;
853176998Sphk    yyRelMonth = 0;
854176998Sphk    yyHaveDate = 0;
855176998Sphk    yyHaveDay = 0;
856176998Sphk    yyHaveRel = 0;
857176998Sphk    yyHaveTime = 0;
858176998Sphk    yyHaveZone = 0;
859176998Sphk
860176998Sphk    if (yyparse()
861176998Sphk     || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1)
862176998Sphk	return -1;
863176998Sphk
864176998Sphk    if (yyHaveDate || yyHaveTime || yyHaveDay) {
865176998Sphk	Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds,
866176998Sphk		    yyMeridian, yyDSTmode);
867176998Sphk	if (Start < 0)
868176998Sphk	    return -1;
869176998Sphk    }
870176998Sphk    else {
871176998Sphk	Start = nowtime;
872176998Sphk	if (!yyHaveRel)
873176998Sphk	    Start -= ((tm->tm_hour * 60L + tm->tm_min) * 60L) + tm->tm_sec;
874176998Sphk    }
875176998Sphk
876176998Sphk    Start += yyRelSeconds;
877176998Sphk    Start += RelativeMonth(Start, yyRelMonth);
878176998Sphk
879176998Sphk    if (yyHaveDay && !yyHaveDate) {
880176998Sphk	tod = RelativeDate(Start, yyDayOrdinal, yyDayNumber);
881176998Sphk	Start += tod;
882176998Sphk    }
883176998Sphk
884176998Sphk    /* Have to do *something* with a legitimate -1 so it's distinguishable
885176998Sphk     * from the error return value.  (Alternately could set errno on error.) */
886176998Sphk    return Start == -1 ? 0 : Start;
887176998Sphk}
888