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