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