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