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