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