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