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