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 *
GETTEXT(const char * msgid)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
yyerror(s)593 yyerror(s)
594 char *s;
595 {
596 return 0;
597 }
598
599
600 static time_t
ToSeconds(Hours,Minutes,Seconds,Meridian)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
Convert(Month,Day,Year,Hours,Minutes,Seconds,Meridian,DSTmode)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
DSTcorrect(Start,Future)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
RelativeDate(Start,DayOrdinal,DayNumber)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
RelativeMonth(Start,RelMonth)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
LookupWord(buff)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
yylex()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
difftm(a,b)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
get_date(p)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 */
main(ac,av)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