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