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