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