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