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