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