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