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;
8 ** This grammar has 13 shift/reduce conflicts.
10 ** This code is in the public domain and has no copyright.
15 # ifdef FORCE_ALLOCA_H
20 /* Since the code of getdate.y is not included in the Emacs executable
21 itself, there is no need to #define static in this file. Even if
22 the code were included in the Emacs executable, it probably
23 wouldn't do any harm to #undef it here; this will only cause
24 problems if we try to write to a static variable, which I don't
25 think this code needs to do. */
34 #if defined (STDC_HEADERS) || (!defined (isascii) && !defined (HAVE_ISASCII))
35 # define IN_CTYPE_DOMAIN(c) 1
37 # define IN_CTYPE_DOMAIN(c) isascii(c)
40 #define ISSPACE(c) (IN_CTYPE_DOMAIN (c) && isspace (c))
41 #define ISALPHA(c) (IN_CTYPE_DOMAIN (c) && isalpha (c))
42 #define ISUPPER(c) (IN_CTYPE_DOMAIN (c) && isupper (c))
43 #define ISDIGIT_LOCALE(c) (IN_CTYPE_DOMAIN (c) && isdigit (c))
45 /* ISDIGIT differs from ISDIGIT_LOCALE, as follows:
46 - Its arg may be any int or unsigned int; it need not be an unsigned char.
47 - It's guaranteed to evaluate its argument exactly once.
48 - It's typically faster.
49 Posix 1003.2-1992 section 2.5.2.1 page 50 lines 1556-1558 says that
50 only '0' through '9' are digits. Prefer ISDIGIT to ISDIGIT_LOCALE unless
51 it's important to use the locale's definition of `digit' even when the
52 host does not conform to Posix. */
53 #define ISDIGIT(c) ((unsigned) (c) - '0' <= 9)
57 #if defined (STDC_HEADERS)
61 /* Some old versions of bison generate parsers that use bcopy.
62 That loses on systems that don't provide the function, so we have
63 to redefine it here. */
64 #if !defined (HAVE_BCOPY) && defined (HAVE_MEMCPY) && !defined (bcopy)
65 # define bcopy(from, to, len) memcpy ((to), (from), (len))
68 /* Remap normal yacc parser interface names (yyparse, yylex, yyerror, etc),
69 as well as gratuitiously global symbol names, so we can have multiple
70 yacc generated parsers in the same program. Note that these are only
71 the variables produced by yacc. If other parser generators (bison,
72 byacc, etc) produce additional global names that conflict at link time,
73 then those parser generators need to be fixed instead of adding those
74 names to this list. */
76 #define yymaxdepth gd_maxdepth
77 #define yyparse gd_parse
79 #define yyerror gd_error
80 #define yylval gd_lval
81 #define yychar gd_char
82 #define yydebug gd_debug
83 #define yypact gd_pact
90 #define yyexca gd_exca
91 #define yyerrflag gd_errflag
92 #define yynerrs gd_nerrs
97 #define yystate gd_state
100 #define yy_yyv gd_yyv
102 #define yylloc gd_lloc
103 #define yyreds gd_reds /* With YYDEBUG defined */
104 #define yytoks gd_toks /* With YYDEBUG defined */
105 #define yylhs gd_yylhs
106 #define yylen gd_yylen
107 #define yydefred gd_yydefred
108 #define yydgoto gd_yydgoto
109 #define yysindex gd_yysindex
110 #define yyrindex gd_yyrindex
111 #define yygindex gd_yygindex
112 #define yytable gd_yytable
113 #define yycheck gd_yycheck
115 static int yylex (void);
116 static int yyerror (const char *s);
119 #define HOUR(x) ((x) * 60)
121 #define MAX_BUFF_LEN 128 /* size of buffer to read the date into */
124 ** An entry in the lexical lookup table.
126 typedef struct _TABLE {
134 ** Meridian: am, pm, or 24-hour style.
136 typedef enum _MERIDIAN {
142 ** Global variables. We could get rid of most of these by using a good
143 ** union as the yacc stack. (This routine was originally written before
144 ** yacc had the %union construct.) Maybe someday; right now we only use
145 ** the %union very rarely.
147 static const char *yyInput;
148 static int yyDayOrdinal;
149 static int yyDayNumber;
150 static int yyHaveDate;
151 static int yyHaveDay;
152 static int yyHaveRel;
153 static int yyHaveTime;
154 static int yyHaveZone;
155 static int yyTimezone;
158 static int yyMinutes;
160 static int yySeconds;
162 static MERIDIAN yyMeridian;
164 static int yyRelHour;
165 static int yyRelMinutes;
166 static int yyRelMonth;
167 static int yyRelSeconds;
168 static int yyRelYear;
174 enum _MERIDIAN Meridian;
177 %token tAGO tDAY tDAY_UNIT tDAYZONE tDST tHOUR_UNIT tID
178 %token tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT
179 %token tSEC_UNIT tSNUMBER tUNUMBER tYEAR_UNIT tZONE
181 %type <Number> tDAY tDAY_UNIT tDAYZONE tHOUR_UNIT tMINUTE_UNIT
182 %type <Number> tMONTH tMONTH_UNIT
183 %type <Number> tSEC_UNIT tSNUMBER tUNUMBER tYEAR_UNIT tZONE
184 %type <Meridian> tMERIDIAN o_merid
210 time : tUNUMBER tMERIDIAN {
216 | tUNUMBER ':' tUNUMBER o_merid {
222 | tUNUMBER ':' tUNUMBER tSNUMBER {
228 ? -$4 % 100 + (-$4 / 100) * 60
229 : - ($4 % 100 + ($4 / 100) * 60));
231 | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
237 | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER {
244 ? -$6 % 100 + (-$6 / 100) * 60
245 : - ($6 % 100 + ($6 / 100) * 60));
253 yyTimezone = $1 - 60;
257 yyTimezone = $1 - 60;
275 date : tUNUMBER '/' tUNUMBER {
279 | tUNUMBER '/' tUNUMBER '/' tUNUMBER {
280 /* Interpret as YYYY/MM/DD if $1 >= 1000, otherwise as MM/DD/YY.
281 The goal in recognizing YYYY/MM/DD is solely to support legacy
282 machine-generated dates like those in an RCS log listing. If
283 you want portability, use the ISO 8601 format. */
297 | tUNUMBER tSNUMBER tSNUMBER {
298 /* ISO 8601 format. yyyy-mm-dd. */
303 | tUNUMBER tMONTH tSNUMBER {
304 /* e.g. 17-JUN-1992. */
313 | tMONTH tUNUMBER ',' tUNUMBER {
322 | tUNUMBER tMONTH tUNUMBER {
330 yyRelSeconds = -yyRelSeconds;
331 yyRelMinutes = -yyRelMinutes;
332 yyRelHour = -yyRelHour;
333 yyRelDay = -yyRelDay;
334 yyRelMonth = -yyRelMonth;
335 yyRelYear = -yyRelYear;
340 relunit : tUNUMBER tYEAR_UNIT {
341 yyRelYear += $1 * $2;
343 | tSNUMBER tYEAR_UNIT {
344 yyRelYear += $1 * $2;
349 | tUNUMBER tMONTH_UNIT {
350 yyRelMonth += $1 * $2;
352 | tSNUMBER tMONTH_UNIT {
353 yyRelMonth += $1 * $2;
358 | tUNUMBER tDAY_UNIT {
361 | tSNUMBER tDAY_UNIT {
367 | tUNUMBER tHOUR_UNIT {
368 yyRelHour += $1 * $2;
370 | tSNUMBER tHOUR_UNIT {
371 yyRelHour += $1 * $2;
376 | tUNUMBER tMINUTE_UNIT {
377 yyRelMinutes += $1 * $2;
379 | tSNUMBER tMINUTE_UNIT {
380 yyRelMinutes += $1 * $2;
385 | tUNUMBER tSEC_UNIT {
386 yyRelSeconds += $1 * $2;
388 | tSNUMBER tSEC_UNIT {
389 yyRelSeconds += $1 * $2;
398 if (yyHaveTime && yyHaveDate && !yyHaveRel)
406 yyMonth= ($1/100)%100;
420 yyMinutes = $1 % 100;
441 /* Month and day table. */
442 static TABLE const MonthDayTable[] = {
443 { "january", tMONTH, 1 },
444 { "february", tMONTH, 2 },
445 { "march", tMONTH, 3 },
446 { "april", tMONTH, 4 },
447 { "may", tMONTH, 5 },
448 { "june", tMONTH, 6 },
449 { "july", tMONTH, 7 },
450 { "august", tMONTH, 8 },
451 { "september", tMONTH, 9 },
452 { "sept", tMONTH, 9 },
453 { "october", tMONTH, 10 },
454 { "november", tMONTH, 11 },
455 { "december", tMONTH, 12 },
456 { "sunday", tDAY, 0 },
457 { "monday", tDAY, 1 },
458 { "tuesday", tDAY, 2 },
460 { "wednesday", tDAY, 3 },
461 { "wednes", tDAY, 3 },
462 { "thursday", tDAY, 4 },
464 { "thurs", tDAY, 4 },
465 { "friday", tDAY, 5 },
466 { "saturday", tDAY, 6 },
470 /* Time units table. */
471 static TABLE const UnitsTable[] = {
472 { "year", tYEAR_UNIT, 1 },
473 { "month", tMONTH_UNIT, 1 },
474 { "fortnight", tDAY_UNIT, 14 },
475 { "week", tDAY_UNIT, 7 },
476 { "day", tDAY_UNIT, 1 },
477 { "hour", tHOUR_UNIT, 1 },
478 { "minute", tMINUTE_UNIT, 1 },
479 { "min", tMINUTE_UNIT, 1 },
480 { "second", tSEC_UNIT, 1 },
481 { "sec", tSEC_UNIT, 1 },
485 /* Assorted relative-time words. */
486 static TABLE const OtherTable[] = {
487 { "tomorrow", tMINUTE_UNIT, 1 * 24 * 60 },
488 { "yesterday", tMINUTE_UNIT, -1 * 24 * 60 },
489 { "today", tMINUTE_UNIT, 0 },
490 { "now", tMINUTE_UNIT, 0 },
491 { "last", tUNUMBER, -1 },
492 { "this", tMINUTE_UNIT, 0 },
493 { "next", tUNUMBER, 2 },
494 { "first", tUNUMBER, 1 },
495 /* { "second", tUNUMBER, 2 }, */
496 { "third", tUNUMBER, 3 },
497 { "fourth", tUNUMBER, 4 },
498 { "fifth", tUNUMBER, 5 },
499 { "sixth", tUNUMBER, 6 },
500 { "seventh", tUNUMBER, 7 },
501 { "eighth", tUNUMBER, 8 },
502 { "ninth", tUNUMBER, 9 },
503 { "tenth", tUNUMBER, 10 },
504 { "eleventh", tUNUMBER, 11 },
505 { "twelfth", tUNUMBER, 12 },
510 /* The timezone table. */
511 static TABLE const TimezoneTable[] = {
512 { "gmt", tZONE, HOUR ( 0) }, /* Greenwich Mean */
513 { "ut", tZONE, HOUR ( 0) }, /* Universal (Coordinated) */
514 { "utc", tZONE, HOUR ( 0) },
515 { "wet", tZONE, HOUR ( 0) }, /* Western European */
516 { "bst", tDAYZONE, HOUR ( 0) }, /* British Summer */
517 { "wat", tZONE, HOUR ( 1) }, /* West Africa */
518 { "at", tZONE, HOUR ( 2) }, /* Azores */
519 { "ast", tZONE, HOUR ( 4) }, /* Atlantic Standard */
520 { "adt", tDAYZONE, HOUR ( 4) }, /* Atlantic Daylight */
521 { "est", tZONE, HOUR ( 5) }, /* Eastern Standard */
522 { "edt", tDAYZONE, HOUR ( 5) }, /* Eastern Daylight */
523 { "cst", tZONE, HOUR ( 6) }, /* Central Standard */
524 { "cdt", tDAYZONE, HOUR ( 6) }, /* Central Daylight */
525 { "mst", tZONE, HOUR ( 7) }, /* Mountain Standard */
526 { "mdt", tDAYZONE, HOUR ( 7) }, /* Mountain Daylight */
527 { "pst", tZONE, HOUR ( 8) }, /* Pacific Standard */
528 { "pdt", tDAYZONE, HOUR ( 8) }, /* Pacific Daylight */
529 { "yst", tZONE, HOUR ( 9) }, /* Yukon Standard */
530 { "ydt", tDAYZONE, HOUR ( 9) }, /* Yukon Daylight */
531 { "hst", tZONE, HOUR (10) }, /* Hawaii Standard */
532 { "hdt", tDAYZONE, HOUR (10) }, /* Hawaii Daylight */
533 { "cat", tZONE, HOUR (10) }, /* Central Alaska */
534 { "ahst", tZONE, HOUR (10) }, /* Alaska-Hawaii Standard */
535 { "nt", tZONE, HOUR (11) }, /* Nome */
536 { "idlw", tZONE, HOUR (12) }, /* International Date Line West */
537 { "cet", tZONE, -HOUR (1) }, /* Central European */
538 { "met", tZONE, -HOUR (1) }, /* Middle European */
539 { "mewt", tZONE, -HOUR (1) }, /* Middle European Winter */
540 { "mest", tDAYZONE, -HOUR (1) }, /* Middle European Summer */
541 { "mesz", tDAYZONE, -HOUR (1) }, /* Middle European Summer */
542 { "swt", tZONE, -HOUR (1) }, /* Swedish Winter */
543 { "sst", tDAYZONE, -HOUR (1) }, /* Swedish Summer */
544 { "fwt", tZONE, -HOUR (1) }, /* French Winter */
545 { "fst", tDAYZONE, -HOUR (1) }, /* French Summer */
546 { "eet", tZONE, -HOUR (2) }, /* Eastern Europe, USSR Zone 1 */
547 { "bt", tZONE, -HOUR (3) }, /* Baghdad, USSR Zone 2 */
548 { "zp4", tZONE, -HOUR (4) }, /* USSR Zone 3 */
549 { "zp5", tZONE, -HOUR (5) }, /* USSR Zone 4 */
550 { "zp6", tZONE, -HOUR (6) }, /* USSR Zone 5 */
551 { "wast", tZONE, -HOUR (7) }, /* West Australian Standard */
552 { "wadt", tDAYZONE, -HOUR (7) }, /* West Australian Daylight */
553 { "cct", tZONE, -HOUR (8) }, /* China Coast, USSR Zone 7 */
554 { "jst", tZONE, -HOUR (9) }, /* Japan Standard, USSR Zone 8 */
555 { "east", tZONE, -HOUR (10) }, /* Eastern Australian Standard */
556 { "eadt", tDAYZONE, -HOUR (10) }, /* Eastern Australian Daylight */
557 { "gst", tZONE, -HOUR (10) }, /* Guam Standard, USSR Zone 9 */
558 { "nzt", tZONE, -HOUR (12) }, /* New Zealand */
559 { "nzst", tZONE, -HOUR (12) }, /* New Zealand Standard */
560 { "nzdt", tDAYZONE, -HOUR (12) }, /* New Zealand Daylight */
561 { "idle", tZONE, -HOUR (12) }, /* International Date Line East */
565 /* Military timezone table. */
566 static TABLE const MilitaryTable[] = {
567 { "a", tZONE, HOUR ( 1) },
568 { "b", tZONE, HOUR ( 2) },
569 { "c", tZONE, HOUR ( 3) },
570 { "d", tZONE, HOUR ( 4) },
571 { "e", tZONE, HOUR ( 5) },
572 { "f", tZONE, HOUR ( 6) },
573 { "g", tZONE, HOUR ( 7) },
574 { "h", tZONE, HOUR ( 8) },
575 { "i", tZONE, HOUR ( 9) },
576 { "k", tZONE, HOUR ( 10) },
577 { "l", tZONE, HOUR ( 11) },
578 { "m", tZONE, HOUR ( 12) },
579 { "n", tZONE, HOUR (- 1) },
580 { "o", tZONE, HOUR (- 2) },
581 { "p", tZONE, HOUR (- 3) },
582 { "q", tZONE, HOUR (- 4) },
583 { "r", tZONE, HOUR (- 5) },
584 { "s", tZONE, HOUR (- 6) },
585 { "t", tZONE, HOUR (- 7) },
586 { "u", tZONE, HOUR (- 8) },
587 { "v", tZONE, HOUR (- 9) },
588 { "w", tZONE, HOUR (-10) },
589 { "x", tZONE, HOUR (-11) },
590 { "y", tZONE, HOUR (-12) },
591 { "z", tZONE, HOUR ( 0) },
598 static int yyerror (unused const char *s)
603 static int ToHour (int Hours, MERIDIAN Meridian)
608 if (Hours < 0 || Hours > 23)
612 if (Hours < 1 || Hours > 12)
618 if (Hours < 1 || Hours > 12)
629 static int ToYear (int Year)
634 /* XPG4 suggests that years 00-68 map to 2000-2068, and
635 years 69-99 map to 1969-1999. */
644 static int LookupWord (char *buff)
648 register const TABLE *tp;
652 /* Make it lowercase. */
653 for (p = buff; *p; p++)
657 if (strcmp (buff, "am") == 0 || strcmp (buff, "a.m.") == 0)
659 yylval.Meridian = MERam;
662 if (strcmp (buff, "pm") == 0 || strcmp (buff, "p.m.") == 0)
664 yylval.Meridian = MERpm;
668 /* See if we have an abbreviation for a month. */
669 if (strlen (buff) == 3)
671 else if (strlen (buff) == 4 && buff[3] == '.')
679 for (tp = MonthDayTable; tp->name; tp++)
683 if (strncmp (buff, tp->name, 3) == 0)
685 yylval.Number = tp->value;
689 else if (strcmp (buff, tp->name) == 0)
691 yylval.Number = tp->value;
696 for (tp = TimezoneTable; tp->name; tp++)
697 if (strcmp (buff, tp->name) == 0)
699 yylval.Number = tp->value;
703 if (strcmp (buff, "dst") == 0)
706 for (tp = UnitsTable; tp->name; tp++)
707 if (strcmp (buff, tp->name) == 0)
709 yylval.Number = tp->value;
713 /* Strip off any plural and try the units table again. */
714 i = strlen (buff) - 1;
718 for (tp = UnitsTable; tp->name; tp++)
719 if (strcmp (buff, tp->name) == 0)
721 yylval.Number = tp->value;
724 buff[i] = 's'; /* Put back for "this" in OtherTable. */
727 for (tp = OtherTable; tp->name; tp++)
728 if (strcmp (buff, tp->name) == 0)
730 yylval.Number = tp->value;
734 /* Military timezones. */
735 if (buff[1] == '\0' && ISALPHA (*buff))
737 for (tp = MilitaryTable; tp->name; tp++)
738 if (strcmp (buff, tp->name) == 0)
740 yylval.Number = tp->value;
745 /* Drop out any periods and try the timezone table again. */
746 for (i = 0, p = q = buff; *q; q++)
753 for (tp = TimezoneTable; tp->name; tp++)
754 if (strcmp (buff, tp->name) == 0)
756 yylval.Number = tp->value;
774 while (ISSPACE (*yyInput))
777 if (ISDIGIT (c = *yyInput) || c == '-' || c == '+')
779 if (c == '-' || c == '+')
781 sign = c == '-' ? -1 : 1;
782 if (!ISDIGIT (*++yyInput))
783 /* skip the '-' sign */
788 for (yylval.Number = 0; ISDIGIT (c = *yyInput++);)
789 yylval.Number = 10 * yylval.Number + c - '0';
792 yylval.Number = -yylval.Number;
793 return sign ? tSNUMBER : tUNUMBER;
797 for (p = buff; (c = *yyInput++, ISALPHA (c)) || c == '.';)
798 if (p < &buff[sizeof buff - 1])
802 return LookupWord (buff);
821 #define TM_YEAR_ORIGIN 1900
823 /* Yield A - B, measured in seconds. */
824 static long difftm (struct tm *a, struct tm *b)
826 int ay = a->tm_year + (TM_YEAR_ORIGIN - 1);
827 int by = b->tm_year + (TM_YEAR_ORIGIN - 1);
829 /* difference in day of year */
830 a->tm_yday - b->tm_yday
831 /* + intervening leap days */
832 + ((ay >> 2) - (by >> 2))
833 - (ay / 100 - by / 100)
834 + ((ay / 100 >> 2) - (by / 100 >> 2))
835 /* + difference in years * 365 */
836 + (long) (ay - by) * 365
838 return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
839 + (a->tm_min - b->tm_min))
840 + (a->tm_sec - b->tm_sec));
843 time_t get_date (const char *p, const time_t *now)
845 struct tm tm, tm0, *tmp;
849 Start = now ? *now : time ((time_t *) NULL);
850 tmp = localtime (&Start);
851 yyYear = tmp->tm_year + TM_YEAR_ORIGIN;
852 yyMonth = tmp->tm_mon + 1;
853 yyDay = tmp->tm_mday;
854 yyHour = tmp->tm_hour;
855 yyMinutes = tmp->tm_min;
856 yySeconds = tmp->tm_sec;
871 || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1)
874 tm.tm_year = ToYear (yyYear) - TM_YEAR_ORIGIN + yyRelYear;
875 tm.tm_mon = yyMonth - 1 + yyRelMonth;
876 tm.tm_mday = yyDay + yyRelDay;
877 if (yyHaveTime || (yyHaveRel && !yyHaveDate && !yyHaveDay))
879 tm.tm_hour = ToHour (yyHour, yyMeridian);
882 tm.tm_min = yyMinutes;
883 tm.tm_sec = yySeconds;
887 tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
889 tm.tm_hour += yyRelHour;
890 tm.tm_min += yyRelMinutes;
891 tm.tm_sec += yyRelSeconds;
895 Start = mktime (&tm);
897 if (Start == (time_t) -1)
900 /* Guard against falsely reporting errors near the time_t boundaries
901 when parsing times in other time zones. For example, if the min
902 time_t value is 1970-01-01 00:00:00 UTC and we are 8 hours ahead
903 of UTC, then the min localtime value is 1970-01-01 08:00:00; if
904 we apply mktime to 1970-01-01 00:00:00 we will get an error, so
905 we apply mktime to 1970-01-02 08:00:00 instead and adjust the time
906 zone by 24 hours to compensate. This algorithm assumes that
907 there is no DST transition within a day of the time_t boundaries. */
911 if (tm.tm_year <= EPOCH - TM_YEAR_ORIGIN)
914 yyTimezone -= 24 * 60;
919 yyTimezone += 24 * 60;
921 Start = mktime (&tm);
924 if (Start == (time_t) -1)
928 if (yyHaveDay && !yyHaveDate)
930 tm.tm_mday += ((yyDayNumber - tm.tm_wday + 7) % 7
931 + 7 * (yyDayOrdinal - (0 < yyDayOrdinal)));
932 Start = mktime (&tm);
933 if (Start == (time_t) -1)
939 long delta = yyTimezone * 60L + difftm (&tm, gmtime (&Start));
940 if ((Start + delta < Start) != (delta < 0))
941 return -1; /* time_t overflow */
956 char buff[MAX_BUFF_LEN + 1];
959 (void) printf ("Enter date, or blank line to exit.\n\t> ");
960 (void) fflush (stdout);
962 buff[MAX_BUFF_LEN] = 0;
963 while (fgets (buff, MAX_BUFF_LEN, stdin) && buff[0])
965 d = get_date (buff, (time_t *) NULL);
967 (void) printf ("Bad format - couldn't convert.\n");
969 (void) printf ("%s", ctime (&d));
970 (void) printf ("\t> ");
971 (void) fflush (stdout);
976 #endif /* defined (TEST) */