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