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