getdate.y revision 94792
1%{
2/*
3**  Originally written by Steven M. Bellovin <smb@research.att.com> while
4**  at the University of North Carolina at Chapel Hill.  Later tweaked by
5**  a couple of people on Usenet.  Completely overhauled by Rich $alz
6**  <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990;
7**
8**  This grammar has 10 shift/reduce conflicts.
9**
10**  This code is in the public domain and has no copyright.
11*/
12/* SUPPRESS 287 on yaccpar_sccsid *//* Unused static variable */
13/* SUPPRESS 288 on yyerrlab *//* Label unused */
14
15/* $FreeBSD: head/usr.bin/find/getdate.y 94792 2002-04-15 19:27:41Z obrien $ */
16
17#include <stdio.h>
18#include <ctype.h>
19
20/* The code at the top of get_date which figures out the offset of the
21   current time zone checks various CPP symbols to see if special
22   tricks are need, but defaults to using the gettimeofday system call.
23   Include <sys/time.h> if that will be used.  */
24
25#if	defined(vms)
26# include <types.h>
27#else /* defined(vms) */
28# include <sys/types.h>
29# include <sys/time.h>
30# include <sys/timeb.h>
31#endif	/* !defined(vms) */
32
33#if defined (__STDC__) || defined (USG)
34#include <string.h>
35#endif
36
37/* Some old versions of bison generate parsers that use bcopy.
38   That loses on systems that don't provide the function, so we have
39   to redefine it here.  */
40#if !defined (HAVE_BCOPY) && defined (HAVE_MEMCPY) && !defined (bcopy)
41#define bcopy(from, to, len) memcpy ((to), (from), (len))
42#endif
43
44#if defined (__STDC__)
45#include <stdlib.h>
46#endif
47
48/* NOTES on rebuilding getdate.c (particularly for inclusion in CVS
49   releases):
50
51   We don't want to mess with all the portability hassles of alloca.
52   In particular, most (all?) versions of bison will use alloca in
53   their parser.  If bison works on your system (e.g. it should work
54   with gcc), then go ahead and use it, but the more general solution
55   is to use byacc instead of bison, which should generate a portable
56   parser.  I played with adding "#define alloca dont_use_alloca", to
57   give an error if the parser generator uses alloca (and thus detect
58   unportable getdate.c's), but that seems to cause as many problems
59   as it solves.  */
60
61extern struct tm	*gmtime();
62extern struct tm	*localtime();
63
64#define yyparse getdate_yyparse
65#define yylex getdate_yylex
66#define yyerror getdate_yyerror
67
68static int yyparse ();
69static int yylex ();
70static int yyerror ();
71
72#define EPOCH		1970
73#define HOUR(x)		((time_t)(x) * 60)
74#define SECSPERDAY	(24L * 60L * 60L)
75
76
77/*
78**  An entry in the lexical lookup table.
79*/
80typedef struct _TABLE {
81    char	*name;
82    int		type;
83    time_t	value;
84} TABLE;
85
86
87/*
88**  Daylight-savings mode:  on, off, or not yet known.
89*/
90typedef enum _DSTMODE {
91    DSTon, DSToff, DSTmaybe
92} DSTMODE;
93
94/*
95**  Meridian:  am, pm, or 24-hour style.
96*/
97typedef enum _MERIDIAN {
98    MERam, MERpm, MER24
99} MERIDIAN;
100
101
102/*
103**  Global variables.  We could get rid of most of these by using a good
104**  union as the yacc stack.  (This routine was originally written before
105**  yacc had the %union construct.)  Maybe someday; right now we only use
106**  the %union very rarely.
107*/
108static char	*yyInput;
109static DSTMODE	yyDSTmode;
110static time_t	yyDayOrdinal;
111static time_t	yyDayNumber;
112static int	yyHaveDate;
113static int	yyHaveDay;
114static int	yyHaveRel;
115static int	yyHaveTime;
116static int	yyHaveZone;
117static time_t	yyTimezone;
118static time_t	yyDay;
119static time_t	yyHour;
120static time_t	yyMinutes;
121static time_t	yyMonth;
122static time_t	yySeconds;
123static time_t	yyYear;
124static MERIDIAN	yyMeridian;
125static time_t	yyRelMonth;
126static time_t	yyRelSeconds;
127
128%}
129
130%union {
131    time_t		Number;
132    enum _MERIDIAN	Meridian;
133}
134
135%token	tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT
136%token	tSEC_UNIT tSNUMBER tUNUMBER tZONE tDST
137
138%type	<Number>	tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT
139%type	<Number>	tSEC_UNIT tSNUMBER tUNUMBER tZONE
140%type	<Meridian>	tMERIDIAN o_merid
141
142%%
143
144spec	: /* NULL */
145	| spec item
146	;
147
148item	: time {
149	    yyHaveTime++;
150	}
151	| zone {
152	    yyHaveZone++;
153	}
154	| date {
155	    yyHaveDate++;
156	}
157	| day {
158	    yyHaveDay++;
159	}
160	| rel {
161	    yyHaveRel++;
162	}
163	| number
164	;
165
166time	: tUNUMBER tMERIDIAN {
167	    yyHour = $1;
168	    yyMinutes = 0;
169	    yySeconds = 0;
170	    yyMeridian = $2;
171	}
172	| tUNUMBER ':' tUNUMBER o_merid {
173	    yyHour = $1;
174	    yyMinutes = $3;
175	    yySeconds = 0;
176	    yyMeridian = $4;
177	}
178	| tUNUMBER ':' tUNUMBER tSNUMBER {
179	    yyHour = $1;
180	    yyMinutes = $3;
181	    yyMeridian = MER24;
182	    yyDSTmode = DSToff;
183	    yyTimezone = - ($4 % 100 + ($4 / 100) * 60);
184	}
185	| tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
186	    yyHour = $1;
187	    yyMinutes = $3;
188	    yySeconds = $5;
189	    yyMeridian = $6;
190	}
191	| tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER {
192	    yyHour = $1;
193	    yyMinutes = $3;
194	    yySeconds = $5;
195	    yyMeridian = MER24;
196	    yyDSTmode = DSToff;
197	    yyTimezone = - ($6 % 100 + ($6 / 100) * 60);
198	}
199	;
200
201zone	: tZONE {
202	    yyTimezone = $1;
203	    yyDSTmode = DSToff;
204	}
205	| tDAYZONE {
206	    yyTimezone = $1;
207	    yyDSTmode = DSTon;
208	}
209	|
210	  tZONE tDST {
211	    yyTimezone = $1;
212	    yyDSTmode = DSTon;
213	}
214	;
215
216day	: tDAY {
217	    yyDayOrdinal = 1;
218	    yyDayNumber = $1;
219	}
220	| tDAY ',' {
221	    yyDayOrdinal = 1;
222	    yyDayNumber = $1;
223	}
224	| tUNUMBER tDAY {
225	    yyDayOrdinal = $1;
226	    yyDayNumber = $2;
227	}
228	;
229
230date	: tUNUMBER '/' tUNUMBER {
231	    yyMonth = $1;
232	    yyDay = $3;
233	}
234	| tUNUMBER '/' tUNUMBER '/' tUNUMBER {
235	    if ($1 >= 100) {
236		yyYear = $1;
237		yyMonth = $3;
238		yyDay = $5;
239	    } else {
240		yyMonth = $1;
241		yyDay = $3;
242		yyYear = $5;
243	    }
244	}
245	| tUNUMBER tSNUMBER tSNUMBER {
246	    /* ISO 8601 format.  yyyy-mm-dd.  */
247	    yyYear = $1;
248	    yyMonth = -$2;
249	    yyDay = -$3;
250	}
251	| tUNUMBER tMONTH tSNUMBER {
252	    /* e.g. 17-JUN-1992.  */
253	    yyDay = $1;
254	    yyMonth = $2;
255	    yyYear = -$3;
256	}
257	| tMONTH tUNUMBER {
258	    yyMonth = $1;
259	    yyDay = $2;
260	}
261	| tMONTH tUNUMBER ',' tUNUMBER {
262	    yyMonth = $1;
263	    yyDay = $2;
264	    yyYear = $4;
265	}
266	| tUNUMBER tMONTH {
267	    yyMonth = $2;
268	    yyDay = $1;
269	}
270	| tUNUMBER tMONTH tUNUMBER {
271	    yyMonth = $2;
272	    yyDay = $1;
273	    yyYear = $3;
274	}
275	;
276
277rel	: relunit tAGO {
278	    yyRelSeconds = -yyRelSeconds;
279	    yyRelMonth = -yyRelMonth;
280	}
281	| relunit
282	;
283
284relunit	: tUNUMBER tMINUTE_UNIT {
285	    yyRelSeconds += $1 * $2 * 60L;
286	}
287	| tSNUMBER tMINUTE_UNIT {
288	    yyRelSeconds += $1 * $2 * 60L;
289	}
290	| tMINUTE_UNIT {
291	    yyRelSeconds += $1 * 60L;
292	}
293	| tSNUMBER tSEC_UNIT {
294	    yyRelSeconds += $1;
295	}
296	| tUNUMBER tSEC_UNIT {
297	    yyRelSeconds += $1;
298	}
299	| tSEC_UNIT {
300	    yyRelSeconds++;
301	}
302	| tSNUMBER tMONTH_UNIT {
303	    yyRelMonth += $1 * $2;
304	}
305	| tUNUMBER tMONTH_UNIT {
306	    yyRelMonth += $1 * $2;
307	}
308	| tMONTH_UNIT {
309	    yyRelMonth += $1;
310	}
311	;
312
313number	: tUNUMBER {
314	    if (yyHaveTime && yyHaveDate && !yyHaveRel)
315		yyYear = $1;
316	    else {
317		if($1>10000) {
318		    yyHaveDate++;
319		    yyDay= ($1)%100;
320		    yyMonth= ($1/100)%100;
321		    yyYear = $1/10000;
322		}
323		else {
324		    yyHaveTime++;
325		    if ($1 < 100) {
326			yyHour = $1;
327			yyMinutes = 0;
328		    }
329		    else {
330		    	yyHour = $1 / 100;
331		    	yyMinutes = $1 % 100;
332		    }
333		    yySeconds = 0;
334		    yyMeridian = MER24;
335	        }
336	    }
337	}
338	;
339
340o_merid	: /* NULL */ {
341	    $$ = MER24;
342	}
343	| tMERIDIAN {
344	    $$ = $1;
345	}
346	;
347
348%%
349
350/* Month and day table. */
351static TABLE const MonthDayTable[] = {
352    { "january",	tMONTH,  1 },
353    { "february",	tMONTH,  2 },
354    { "march",		tMONTH,  3 },
355    { "april",		tMONTH,  4 },
356    { "may",		tMONTH,  5 },
357    { "june",		tMONTH,  6 },
358    { "july",		tMONTH,  7 },
359    { "august",		tMONTH,  8 },
360    { "september",	tMONTH,  9 },
361    { "sept",		tMONTH,  9 },
362    { "october",	tMONTH, 10 },
363    { "november",	tMONTH, 11 },
364    { "december",	tMONTH, 12 },
365    { "sunday",		tDAY, 0 },
366    { "monday",		tDAY, 1 },
367    { "tuesday",	tDAY, 2 },
368    { "tues",		tDAY, 2 },
369    { "wednesday",	tDAY, 3 },
370    { "wednes",		tDAY, 3 },
371    { "thursday",	tDAY, 4 },
372    { "thur",		tDAY, 4 },
373    { "thurs",		tDAY, 4 },
374    { "friday",		tDAY, 5 },
375    { "saturday",	tDAY, 6 },
376    { NULL }
377};
378
379/* Time units table. */
380static TABLE const UnitsTable[] = {
381    { "year",		tMONTH_UNIT,	12 },
382    { "month",		tMONTH_UNIT,	1 },
383    { "fortnight",	tMINUTE_UNIT,	14 * 24 * 60 },
384    { "week",		tMINUTE_UNIT,	7 * 24 * 60 },
385    { "day",		tMINUTE_UNIT,	1 * 24 * 60 },
386    { "hour",		tMINUTE_UNIT,	60 },
387    { "minute",		tMINUTE_UNIT,	1 },
388    { "min",		tMINUTE_UNIT,	1 },
389    { "second",		tSEC_UNIT,	1 },
390    { "sec",		tSEC_UNIT,	1 },
391    { NULL }
392};
393
394/* Assorted relative-time words. */
395static TABLE const OtherTable[] = {
396    { "tomorrow",	tMINUTE_UNIT,	1 * 24 * 60 },
397    { "yesterday",	tMINUTE_UNIT,	-1 * 24 * 60 },
398    { "today",		tMINUTE_UNIT,	0 },
399    { "now",		tMINUTE_UNIT,	0 },
400    { "last",		tUNUMBER,	-1 },
401    { "this",		tMINUTE_UNIT,	0 },
402    { "next",		tUNUMBER,	2 },
403    { "first",		tUNUMBER,	1 },
404/*  { "second",		tUNUMBER,	2 }, */
405    { "third",		tUNUMBER,	3 },
406    { "fourth",		tUNUMBER,	4 },
407    { "fifth",		tUNUMBER,	5 },
408    { "sixth",		tUNUMBER,	6 },
409    { "seventh",	tUNUMBER,	7 },
410    { "eighth",		tUNUMBER,	8 },
411    { "ninth",		tUNUMBER,	9 },
412    { "tenth",		tUNUMBER,	10 },
413    { "eleventh",	tUNUMBER,	11 },
414    { "twelfth",	tUNUMBER,	12 },
415    { "ago",		tAGO,	1 },
416    { NULL }
417};
418
419/* The timezone table. */
420/* Some of these are commented out because a time_t can't store a float. */
421static TABLE const TimezoneTable[] = {
422    { "gmt",	tZONE,     HOUR( 0) },	/* Greenwich Mean */
423    { "ut",	tZONE,     HOUR( 0) },	/* Universal (Coordinated) */
424    { "utc",	tZONE,     HOUR( 0) },
425    { "wet",	tZONE,     HOUR( 0) },	/* Western European */
426    { "bst",	tDAYZONE,  HOUR( 0) },	/* British Summer */
427    { "wat",	tZONE,     HOUR( 1) },	/* West Africa */
428    { "at",	tZONE,     HOUR( 2) },	/* Azores */
429#if	0
430    /* For completeness.  BST is also British Summer, and GST is
431     * also Guam Standard. */
432    { "bst",	tZONE,     HOUR( 3) },	/* Brazil Standard */
433    { "gst",	tZONE,     HOUR( 3) },	/* Greenland Standard */
434#endif
435#if 0
436    { "nft",	tZONE,     HOUR(3.5) },	/* Newfoundland */
437    { "nst",	tZONE,     HOUR(3.5) },	/* Newfoundland Standard */
438    { "ndt",	tDAYZONE,  HOUR(3.5) },	/* Newfoundland Daylight */
439#endif
440    { "ast",	tZONE,     HOUR( 4) },	/* Atlantic Standard */
441    { "adt",	tDAYZONE,  HOUR( 4) },	/* Atlantic Daylight */
442    { "est",	tZONE,     HOUR( 5) },	/* Eastern Standard */
443    { "edt",	tDAYZONE,  HOUR( 5) },	/* Eastern Daylight */
444    { "cst",	tZONE,     HOUR( 6) },	/* Central Standard */
445    { "cdt",	tDAYZONE,  HOUR( 6) },	/* Central Daylight */
446    { "mst",	tZONE,     HOUR( 7) },	/* Mountain Standard */
447    { "mdt",	tDAYZONE,  HOUR( 7) },	/* Mountain Daylight */
448    { "pst",	tZONE,     HOUR( 8) },	/* Pacific Standard */
449    { "pdt",	tDAYZONE,  HOUR( 8) },	/* Pacific Daylight */
450    { "yst",	tZONE,     HOUR( 9) },	/* Yukon Standard */
451    { "ydt",	tDAYZONE,  HOUR( 9) },	/* Yukon Daylight */
452    { "hst",	tZONE,     HOUR(10) },	/* Hawaii Standard */
453    { "hdt",	tDAYZONE,  HOUR(10) },	/* Hawaii Daylight */
454    { "cat",	tZONE,     HOUR(10) },	/* Central Alaska */
455    { "ahst",	tZONE,     HOUR(10) },	/* Alaska-Hawaii Standard */
456    { "nt",	tZONE,     HOUR(11) },	/* Nome */
457    { "idlw",	tZONE,     HOUR(12) },	/* International Date Line West */
458    { "cet",	tZONE,     -HOUR(1) },	/* Central European */
459    { "met",	tZONE,     -HOUR(1) },	/* Middle European */
460    { "mewt",	tZONE,     -HOUR(1) },	/* Middle European Winter */
461    { "mest",	tDAYZONE,  -HOUR(1) },	/* Middle European Summer */
462    { "swt",	tZONE,     -HOUR(1) },	/* Swedish Winter */
463    { "sst",	tDAYZONE,  -HOUR(1) },	/* Swedish Summer */
464    { "fwt",	tZONE,     -HOUR(1) },	/* French Winter */
465    { "fst",	tDAYZONE,  -HOUR(1) },	/* French Summer */
466    { "eet",	tZONE,     -HOUR(2) },	/* Eastern Europe, USSR Zone 1 */
467    { "bt",	tZONE,     -HOUR(3) },	/* Baghdad, USSR Zone 2 */
468#if 0
469    { "it",	tZONE,     -HOUR(3.5) },/* Iran */
470#endif
471    { "zp4",	tZONE,     -HOUR(4) },	/* USSR Zone 3 */
472    { "zp5",	tZONE,     -HOUR(5) },	/* USSR Zone 4 */
473#if 0
474    { "ist",	tZONE,     -HOUR(5.5) },/* Indian Standard */
475#endif
476    { "zp6",	tZONE,     -HOUR(6) },	/* USSR Zone 5 */
477#if	0
478    /* For completeness.  NST is also Newfoundland Stanard, and SST is
479     * also Swedish Summer. */
480    { "nst",	tZONE,     -HOUR(6.5) },/* North Sumatra */
481    { "sst",	tZONE,     -HOUR(7) },	/* South Sumatra, USSR Zone 6 */
482#endif	/* 0 */
483    { "wast",	tZONE,     -HOUR(7) },	/* West Australian Standard */
484    { "wadt",	tDAYZONE,  -HOUR(7) },	/* West Australian Daylight */
485#if 0
486    { "jt",	tZONE,     -HOUR(7.5) },/* Java (3pm in Cronusland!) */
487#endif
488    { "cct",	tZONE,     -HOUR(8) },	/* China Coast, USSR Zone 7 */
489    { "jst",	tZONE,     -HOUR(9) },	/* Japan Standard, USSR Zone 8 */
490#if 0
491    { "cast",	tZONE,     -HOUR(9.5) },/* Central Australian Standard */
492    { "cadt",	tDAYZONE,  -HOUR(9.5) },/* Central Australian Daylight */
493#endif
494    { "east",	tZONE,     -HOUR(10) },	/* Eastern Australian Standard */
495    { "eadt",	tDAYZONE,  -HOUR(10) },	/* Eastern Australian Daylight */
496    { "gst",	tZONE,     -HOUR(10) },	/* Guam Standard, USSR Zone 9 */
497    { "nzt",	tZONE,     -HOUR(12) },	/* New Zealand */
498    { "nzst",	tZONE,     -HOUR(12) },	/* New Zealand Standard */
499    { "nzdt",	tDAYZONE,  -HOUR(12) },	/* New Zealand Daylight */
500    { "idle",	tZONE,     -HOUR(12) },	/* International Date Line East */
501    {  NULL  }
502};
503
504/* Military timezone table. */
505static TABLE const MilitaryTable[] = {
506    { "a",	tZONE,	HOUR(  1) },
507    { "b",	tZONE,	HOUR(  2) },
508    { "c",	tZONE,	HOUR(  3) },
509    { "d",	tZONE,	HOUR(  4) },
510    { "e",	tZONE,	HOUR(  5) },
511    { "f",	tZONE,	HOUR(  6) },
512    { "g",	tZONE,	HOUR(  7) },
513    { "h",	tZONE,	HOUR(  8) },
514    { "i",	tZONE,	HOUR(  9) },
515    { "k",	tZONE,	HOUR( 10) },
516    { "l",	tZONE,	HOUR( 11) },
517    { "m",	tZONE,	HOUR( 12) },
518    { "n",	tZONE,	HOUR(- 1) },
519    { "o",	tZONE,	HOUR(- 2) },
520    { "p",	tZONE,	HOUR(- 3) },
521    { "q",	tZONE,	HOUR(- 4) },
522    { "r",	tZONE,	HOUR(- 5) },
523    { "s",	tZONE,	HOUR(- 6) },
524    { "t",	tZONE,	HOUR(- 7) },
525    { "u",	tZONE,	HOUR(- 8) },
526    { "v",	tZONE,	HOUR(- 9) },
527    { "w",	tZONE,	HOUR(-10) },
528    { "x",	tZONE,	HOUR(-11) },
529    { "y",	tZONE,	HOUR(-12) },
530    { "z",	tZONE,	HOUR(  0) },
531    { NULL }
532};
533
534
535
536
537/* ARGSUSED */
538static int
539yyerror(s)
540    char	*s __unused;
541{
542  return 0;
543}
544
545
546static time_t
547ToSeconds(Hours, Minutes, Seconds, Meridian)
548    time_t	Hours;
549    time_t	Minutes;
550    time_t	Seconds;
551    MERIDIAN	Meridian;
552{
553    if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59)
554	return -1;
555    switch (Meridian) {
556    case MER24:
557	if (Hours < 0 || Hours > 23)
558	    return -1;
559	return (Hours * 60L + Minutes) * 60L + Seconds;
560    case MERam:
561	if (Hours < 1 || Hours > 12)
562	    return -1;
563	if (Hours == 12)
564	    Hours = 0;
565	return (Hours * 60L + Minutes) * 60L + Seconds;
566    case MERpm:
567	if (Hours < 1 || Hours > 12)
568	    return -1;
569	if (Hours == 12)
570	    Hours = 0;
571	return ((Hours + 12) * 60L + Minutes) * 60L + Seconds;
572    default:
573	abort ();
574    }
575    /* NOTREACHED */
576}
577
578
579/* Year is either
580   * A negative number, which means to use its absolute value (why?)
581   * A number from 0 to 99, which means a year from 1900 to 1999, or
582   * The actual year (>=100).  */
583static time_t
584Convert(Month, Day, Year, Hours, Minutes, Seconds, Meridian, DSTmode)
585    time_t	Month;
586    time_t	Day;
587    time_t	Year;
588    time_t	Hours;
589    time_t	Minutes;
590    time_t	Seconds;
591    MERIDIAN	Meridian;
592    DSTMODE	DSTmode;
593{
594    static int DaysInMonth[12] = {
595	31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
596    };
597    time_t	tod;
598    time_t	Julian;
599    int		i;
600
601    if (Year < 0)
602	Year = -Year;
603    if (Year < 69)
604	Year += 2000;
605    else if (Year < 100)
606	Year += 1900;
607    DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0)
608		    ? 29 : 28;
609    /* Checking for 2038 bogusly assumes that time_t is 32 bits.  But
610       I'm too lazy to try to check for time_t overflow in another way.  */
611    if (Year < EPOCH || Year > 2038
612     || Month < 1 || Month > 12
613     /* Lint fluff:  "conversion from long may lose accuracy" */
614     || Day < 1 || Day > DaysInMonth[(int)--Month])
615	return -1;
616
617    for (Julian = Day - 1, i = 0; i < Month; i++)
618	Julian += DaysInMonth[i];
619    for (i = EPOCH; i < Year; i++)
620	Julian += 365 + (i % 4 == 0);
621    Julian *= SECSPERDAY;
622    Julian += yyTimezone * 60L;
623    if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0)
624	return -1;
625    Julian += tod;
626    if (DSTmode == DSTon
627     || (DSTmode == DSTmaybe && localtime(&Julian)->tm_isdst))
628	Julian -= 60 * 60;
629    return Julian;
630}
631
632
633static time_t
634DSTcorrect(Start, Future)
635    time_t	Start;
636    time_t	Future;
637{
638    time_t	StartDay;
639    time_t	FutureDay;
640
641    StartDay = (localtime(&Start)->tm_hour + 1) % 24;
642    FutureDay = (localtime(&Future)->tm_hour + 1) % 24;
643    return (Future - Start) + (StartDay - FutureDay) * 60L * 60L;
644}
645
646
647static time_t
648RelativeDate(Start, DayOrdinal, DayNumber)
649    time_t	Start;
650    time_t	DayOrdinal;
651    time_t	DayNumber;
652{
653    struct tm	*tm;
654    time_t	now;
655
656    now = Start;
657    tm = localtime(&now);
658    now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7);
659    now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1);
660    return DSTcorrect(Start, now);
661}
662
663
664static time_t
665RelativeMonth(Start, RelMonth)
666    time_t	Start;
667    time_t	RelMonth;
668{
669    struct tm	*tm;
670    time_t	Month;
671    time_t	Year;
672
673    if (RelMonth == 0)
674	return 0;
675    tm = localtime(&Start);
676    Month = 12 * (tm->tm_year + 1900) + tm->tm_mon + RelMonth;
677    Year = Month / 12;
678    Month = Month % 12 + 1;
679    return DSTcorrect(Start,
680	    Convert(Month, (time_t)tm->tm_mday, Year,
681		(time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec,
682		MER24, DSTmaybe));
683}
684
685
686static int
687LookupWord(buff)
688    char		*buff;
689{
690    register char	*p;
691    register char	*q;
692    register const TABLE	*tp;
693    int			i;
694    int			abbrev;
695
696    /* Make it lowercase. */
697    for (p = buff; *p; p++)
698	if (isupper(*p))
699	    *p = tolower(*p);
700
701    if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) {
702	yylval.Meridian = MERam;
703	return tMERIDIAN;
704    }
705    if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) {
706	yylval.Meridian = MERpm;
707	return tMERIDIAN;
708    }
709
710    /* See if we have an abbreviation for a month. */
711    if (strlen(buff) == 3)
712	abbrev = 1;
713    else if (strlen(buff) == 4 && buff[3] == '.') {
714	abbrev = 1;
715	buff[3] = '\0';
716    }
717    else
718	abbrev = 0;
719
720    for (tp = MonthDayTable; tp->name; tp++) {
721	if (abbrev) {
722	    if (strncmp(buff, tp->name, 3) == 0) {
723		yylval.Number = tp->value;
724		return tp->type;
725	    }
726	}
727	else if (strcmp(buff, tp->name) == 0) {
728	    yylval.Number = tp->value;
729	    return tp->type;
730	}
731    }
732
733    for (tp = TimezoneTable; tp->name; tp++)
734	if (strcmp(buff, tp->name) == 0) {
735	    yylval.Number = tp->value;
736	    return tp->type;
737	}
738
739    if (strcmp(buff, "dst") == 0)
740	return tDST;
741
742    for (tp = UnitsTable; tp->name; tp++)
743	if (strcmp(buff, tp->name) == 0) {
744	    yylval.Number = tp->value;
745	    return tp->type;
746	}
747
748    /* Strip off any plural and try the units table again. */
749    i = strlen(buff) - 1;
750    if (buff[i] == 's') {
751	buff[i] = '\0';
752	for (tp = UnitsTable; tp->name; tp++)
753	    if (strcmp(buff, tp->name) == 0) {
754		yylval.Number = tp->value;
755		return tp->type;
756	    }
757	buff[i] = 's';		/* Put back for "this" in OtherTable. */
758    }
759
760    for (tp = OtherTable; tp->name; tp++)
761	if (strcmp(buff, tp->name) == 0) {
762	    yylval.Number = tp->value;
763	    return tp->type;
764	}
765
766    /* Military timezones. */
767    if (buff[1] == '\0' && isalpha(*buff)) {
768	for (tp = MilitaryTable; tp->name; tp++)
769	    if (strcmp(buff, tp->name) == 0) {
770		yylval.Number = tp->value;
771		return tp->type;
772	    }
773    }
774
775    /* Drop out any periods and try the timezone table again. */
776    for (i = 0, p = q = buff; *q; q++)
777	if (*q != '.')
778	    *p++ = *q;
779	else
780	    i++;
781    *p = '\0';
782    if (i)
783	for (tp = TimezoneTable; tp->name; tp++)
784	    if (strcmp(buff, tp->name) == 0) {
785		yylval.Number = tp->value;
786		return tp->type;
787	    }
788
789    return tID;
790}
791
792
793static int
794yylex()
795{
796    register char	c;
797    register char	*p;
798    char		buff[20];
799    int			Count;
800    int			sign;
801
802    for ( ; ; ) {
803	while (isspace(*yyInput))
804	    yyInput++;
805
806	if (isdigit(c = *yyInput) || c == '-' || c == '+') {
807	    if (c == '-' || c == '+') {
808		sign = c == '-' ? -1 : 1;
809		if (!isdigit(*++yyInput))
810		    /* skip the '-' sign */
811		    continue;
812	    }
813	    else
814		sign = 0;
815	    for (yylval.Number = 0; isdigit(c = *yyInput++); )
816		yylval.Number = 10 * yylval.Number + c - '0';
817	    yyInput--;
818	    if (sign < 0)
819		yylval.Number = -yylval.Number;
820	    return sign ? tSNUMBER : tUNUMBER;
821	}
822	if (isalpha(c)) {
823	    for (p = buff; isalpha(c = *yyInput++) || c == '.'; )
824		if (p < &buff[sizeof buff - 1])
825		    *p++ = c;
826	    *p = '\0';
827	    yyInput--;
828	    return LookupWord(buff);
829	}
830	if (c != '(')
831	    return *yyInput++;
832	Count = 0;
833	do {
834	    c = *yyInput++;
835	    if (c == '\0')
836		return c;
837	    if (c == '(')
838		Count++;
839	    else if (c == ')')
840		Count--;
841	} while (Count > 0);
842    }
843}
844
845#define TM_YEAR_ORIGIN 1900
846
847/* Yield A - B, measured in seconds.  */
848static long
849difftm (a, b)
850     struct tm *a, *b;
851{
852  int ay = a->tm_year + (TM_YEAR_ORIGIN - 1);
853  int by = b->tm_year + (TM_YEAR_ORIGIN - 1);
854  int days = (
855	      /* difference in day of year */
856	      a->tm_yday - b->tm_yday
857	      /* + intervening leap days */
858	      +  ((ay >> 2) - (by >> 2))
859	      -  (ay/100 - by/100)
860	      +  ((ay/100 >> 2) - (by/100 >> 2))
861	      /* + difference in years * 365 */
862	      +  (long)(ay-by) * 365
863	      );
864  return (60*(60*(24*days + (a->tm_hour - b->tm_hour))
865	      + (a->tm_min - b->tm_min))
866	  + (a->tm_sec - b->tm_sec));
867}
868
869time_t
870get_date(p, now)
871    char		*p;
872    struct timeb	*now;
873{
874    struct tm		*tm, gmt;
875    struct timeb	ftz;
876    time_t		Start;
877    time_t		tod;
878    time_t nowtime;
879
880    yyInput = p;
881    if (now == NULL) {
882	struct tm *gmt_ptr;
883
884        now = &ftz;
885	(void)time (&nowtime);
886
887	gmt_ptr = gmtime (&nowtime);
888	if (gmt_ptr != NULL)
889	{
890	    /* Make a copy, in case localtime modifies *tm (I think
891	       that comment now applies to *gmt_ptr, but I am too
892	       lazy to dig into how gmtime and locatime allocate the
893	       structures they return pointers to).  */
894	    gmt = *gmt_ptr;
895	}
896
897	if (! (tm = localtime (&nowtime)))
898	    return -1;
899
900	if (gmt_ptr != NULL)
901	    ftz.timezone = difftm (&gmt, tm) / 60;
902	else
903	    /* We are on a system like VMS, where the system clock is
904	       in local time and the system has no concept of timezones.
905	       Hopefully we can fake this out (for the case in which the
906	       user specifies no timezone) by just saying the timezone
907	       is zero.  */
908	    ftz.timezone = 0;
909
910	if(tm->tm_isdst)
911	    ftz.timezone += 60;
912    }
913    else
914    {
915	nowtime = now->time;
916    }
917
918    tm = localtime(&nowtime);
919    yyYear = tm->tm_year + 1900;
920    yyMonth = tm->tm_mon + 1;
921    yyDay = tm->tm_mday;
922    yyTimezone = now->timezone;
923    yyDSTmode = DSTmaybe;
924    yyHour = 0;
925    yyMinutes = 0;
926    yySeconds = 0;
927    yyMeridian = MER24;
928    yyRelSeconds = 0;
929    yyRelMonth = 0;
930    yyHaveDate = 0;
931    yyHaveDay = 0;
932    yyHaveRel = 0;
933    yyHaveTime = 0;
934    yyHaveZone = 0;
935
936    if (yyparse()
937     || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1)
938	return -1;
939
940    if (yyHaveDate || yyHaveTime || yyHaveDay) {
941	Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds,
942		    yyMeridian, yyDSTmode);
943	if (Start < 0)
944	    return -1;
945    }
946    else {
947	Start = nowtime;
948	if (!yyHaveRel)
949	    Start -= ((tm->tm_hour * 60L + tm->tm_min) * 60L) + tm->tm_sec;
950    }
951
952    Start += yyRelSeconds;
953    Start += RelativeMonth(Start, yyRelMonth);
954
955    if (yyHaveDay && !yyHaveDate) {
956	tod = RelativeDate(Start, yyDayOrdinal, yyDayNumber);
957	Start += tod;
958    }
959
960    /* Have to do *something* with a legitimate -1 so it's distinguishable
961     * from the error return value.  (Alternately could set errno on error.) */
962    return Start == -1 ? 0 : Start;
963}
964
965
966#if	defined(TEST)
967
968/* ARGSUSED */
969int
970main(ac, av)
971    int		ac;
972    char	*av[];
973{
974    char	buff[128];
975    time_t	d;
976
977    (void)printf("Enter date, or blank line to exit.\n\t> ");
978    (void)fflush(stdout);
979    while (gets(buff) && buff[0]) {
980	d = get_date(buff, (struct timeb *)NULL);
981	if (d == -1)
982	    (void)printf("Bad format - couldn't convert.\n");
983	else
984	    (void)printf("%s", ctime(&d));
985	(void)printf("\t> ");
986	(void)fflush(stdout);
987    }
988    exit(0);
989    /* NOTREACHED */
990}
991#endif	/* defined(TEST) */
992