/* * aegis - project change supervisor * Copyright (C) 1991-1995, 1997-1999, 2002-2008, 2012 Peter Miller * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * * This code is derived from code which is * Copyright (C) 1986 Steven M. Bellovin * Steven Bellovin */ %token AGO %token COLON %token COMMA %token DAY %token DAYZONE %token ID %token JUNK %token MERIDIAN %token MONTH %token MUNIT %token NUMBER %token SLASH %token SUNIT %token UNIT %token ZONE %{ #include #include #include #include #include #include #include #include #include /* * This forward declaration needs to be here so that code generated by * Sun's yacc doesn't puke. I'd move the whole yylex function to here, * but then the the code generated by Berkeley's yacc will puke. Sheesh. */ static int yylex(void); #define daysec (24L * 60L * 60L) #define AM 1 #define PM 2 #define DAYLIGHT 1 #define STANDARD 2 #define MAYBE 3 #define MAX_ID_LENGTH 20 static int timeflag; static int zoneflag; static int dateflag; static int dayflag; static int relflag; static time_t relsec; static time_t relmonth; static int hh; static int mm; static int ss; static int merid; static int day_light_flag; static int dayord; static int dayreq; static int month; static int day; static int year; static int ourzone; static const char *lptr; #define YYSTYPE int extern YYSTYPE yylval; extern int yydebug; static int mdays[12] = { 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; #define epoch 1970 int yyparse(void); /* forward */ /* * NAME * timeconv - convert a time * * SYNOPSIS * time_t timeconv(int hh, int mm, int ss, int mer); * * DESCRIPTION * The timeconv function is used to convert a time * specified in hours minutes and seconds, into seconds past midnight. * * ARGUMENTS * hh hours, range depends on the meridian * mm minutes, 0..59 * ss seconds, 0..59 * mer meridian to use: AM, PM or 24 * * RETURNS * time_t; seconds past midnight; -1 on any error. */ static time_t timeconv(int ahh, int amm, int ass, int mer) { time_t result; /* * perform sanity checks on input */ trace(("timeconv(ahh = %d, amm = %d, ass = %d, mer = %d)\n{\n", ahh, amm, ass, mer)); result = -1; if (amm < 0 || amm > 59 || ass < 0 || ass > 59) goto done; /* * perform range checks depending on the meridian */ switch (mer) { case AM: if (ahh < 1 || ahh > 12) goto done; if (ahh == 12) ahh = 0; break; case PM: if (ahh < 1 || ahh > 12) goto done; if (ahh == 12) ahh = 0; ahh += 12; break; case 24: if (ahh < 0 || ahh > 23) goto done; break; default: goto done; } result = ((ahh * 60L + amm) * 60L + ass); done: trace(("return %ld;\n", (long)result)); trace(("}\n")); return result; } static int is_a_leap_year(int yy) { return (yy % 4 == 0 && (yy % 100 != 0 || yy % 400 == 0)); } /* * NAME * dateconv - convert a date * * SYNOPSIS * time_t dateconv(int mm, int dd, int year, int h, int m, int s, * int mer, int zone, int dayflag); * * DESCRIPTION * The dateconv function may be used to convert a date after the * date string has been taken apart by yyparse. * * ARGUMENTS * mm month number, in the range 1..12 * year year number, in several ranges: * 0..37 means 2000..2037 * 70..99 means 1970..1999 * 1970..2037 mean themselves. * dd day of month, in the range 1..max, where max varies for * each month, as per the catchy jingle (except February, * which is a monster). * h hours since midnight or meridian * m minutes past hour * s seconds past minute * mer meridian, AM or PM. * zone minutes correction for the time zone. * dayflag whether to use daylight savings: STANDARD, DAYLIGHT or MAYBE. * * RETURNS * time_t; the time in seconds past Jan 1 0:00:00 1970 GMT, this will * always be positive or zero; -1 is returned for any error. * * CAVEAT * The date functions only work between 1970 and 2037, * because 0 is Jan 1 00:00:00 1970 GMT * and (2^31-1) is Jan 19 03:14:07 2038 GMT * hence some if the weir magic number below. * * Because -1 is used to represent errors, times before noon Jan 1 1970 * in places east of GMT can't always be represented. */ static time_t dateconv(int amm, int dd, int ayear, int h, int m, int s, int mer, int zone, int adayflag) { time_t result; time_t tod; time_t jdate; int i; /* * make corrections for the year * * If it is 0..99, RFC822 says pick closest century. */ trace(("dateconv(amm = %d, dd = %d, ayear = %d, h = %d, m = %d, " "s = %d, mer = %d, zone = %d, adayflag = %d)\n{\n", amm, dd, ayear, h, m, s, mer, zone, adayflag)); result = -1; if (ayear < 0) ayear = -ayear; if (ayear < 38) ayear += 2000; else if (ayear < 100) ayear += 1900; /* * correct February length once we know the year */ mdays[1] = 28 + is_a_leap_year(ayear); /* * perform some sanity checks on the input */ if ( ayear < epoch || ayear >= 2038 || amm < 1 || amm > 12 || dd < 1 || dd > mdays[--amm] ) goto done; /* * Determine the julian day number of the dd-mm-yy given. * Turn it into seconds, and add in the time zone correction. */ jdate = dd - 1; for (i = 0; i < amm; i++) jdate += mdays[i]; for (i = epoch; i < ayear; i++) jdate += 365 + is_a_leap_year(i); jdate *= daysec; jdate += zone * 60L; /* * Determine the time of day. * that is, seconds from midnight. * Add it into the julian date. */ tod = timeconv(h, m, s, mer); if (tod < 0) goto done; jdate += tod; /* * Perform daylight savings correction if necessary. * (This assumes 1 hour daylite savings, which is probably wrong.) */ if ( adayflag == DAYLIGHT || (adayflag == MAYBE && localtime(&jdate)->tm_isdst) ) jdate += -1 * 60 * 60; /* * there you have it. */ result = jdate; done: trace(("return %ld;\n", (long)result)); trace(("}\n")); return result; } /* * NAME * daylcorr * * SYNOPSIS * time_t daylcorr(time_t future, time_t relative_to); * * DESCRIPTION * The daylcorr function is used to determine the difference in seconds * between two times, taking daylight savings into account. * * ARGUMENTS * future - a later time * relative_to - an earlier time * * RETURNS * time_t; the difference in seconds * * CAVEAT * Assumes daylight savings is alays an integral number of hours. * This is wrong is Saudi Arabia (time zone changes during the day), * and South Australia (half hour DLS). */ static time_t daylcorr(time_t future, time_t relative_to) { int fdayl; int nowdayl; time_t result; trace(("daylcorr(future = %ld, relative_to = %ld)\n{\n", (long)future, (long)relative_to)); nowdayl = (localtime(&relative_to)->tm_hour + 1) % 24; fdayl = (localtime(&future)->tm_hour + 1) % 24; result = ((future - relative_to) + 60L * 60L * (nowdayl - fdayl)); trace(("return %ld;\n", (long)result)); trace(("}\n")); return result; } /* * NAME * dayconv * * SYNOPSIS * time_t dayconv(int ord, int day, time_t relative_to); * * DESCRIPTION * The dayconv function is used to convert a day-of-the-week into * a meaningful time. * * ARGUMENTS * ord - the ord'th day from relative_to * day - which day of the week * relative_to - relative to this * * RETURNS * time_t; time in seconds from epoch */ static time_t dayconv(int ord, int aday, time_t relative_to) { time_t tod; time_t result; trace(("dayconv(ord = %d, aday = %d, relative_to = %ld)\n{\n", ord, aday, (long)relative_to)); tod = relative_to; tod += daysec * ((aday - localtime(&tod)->tm_wday + 7) % 7); tod += 7 * daysec * (ord <= 0 ? ord : ord - 1); result = daylcorr(tod, relative_to); trace(("return %ld;\n", (long)result)); trace(("}\n")); return result; } /* * NAME * monthadd * * SYNOPSIS * time_t monthadd(time_t sdate, time_t relmonth); * * DESCRIPTION * The monthadd function is used to add a given number of * months to a specified time. * * ARGUMENTS * sdate - add the months to this * relmonth - add this many months * * RETURNS * time_t; seconds since the epoch */ static time_t monthadd(time_t sdate, time_t arelmonth) { struct tm *ltime; int amm; int ayear; time_t result; trace(("monthadd(sdate = %ld, arelmonth = %ld)\n{\n", (long)sdate, (long)arelmonth)); if (arelmonth == 0) result = 0; else { ltime = localtime(&sdate); amm = 12 * (ltime->tm_year + 1900) + ltime->tm_mon + arelmonth; ayear = amm / 12; amm = amm % 12 + 1; result = dateconv ( amm, ltime->tm_mday, ayear, ltime->tm_hour, ltime->tm_min, ltime->tm_sec, 24, ourzone, MAYBE ); if (result >= 0) result = daylcorr(result, sdate); } trace(("return %ld;\n", (long)result)); trace(("}\n")); return result; } /* * NAME * date_scan * * SYNOPSIS * time_t date_scan(char *s); * * DESCRIPTION * The date_scan function is used to scan a string and * return a number of seconds since epoch. * * ARGUMENTS * s - string to scan * * RETURNS * time_t; seconds to epoch, -1 on error. * * CAVEAT * it isn't psychic */ time_t date_scan(const char *p) { time_t when; struct tm *lt; time_t result; time_t tod; /* * find time zone info, if not given */ trace(("date_scan(p = \"%s\")\n{\n", p)); lptr = p; /* * initialize things */ when = now(); lt = localtime(&when); year = lt->tm_year + 1900; month = lt->tm_mon + 1; day = lt->tm_mday; relsec = 0; relmonth = 0; timeflag = 0; zoneflag = 0; dateflag = 0; dayflag = 0; relflag = 0; ourzone = 0; day_light_flag = MAYBE; #ifdef HAVE_tm_zone ourzone = -lt->tm_gmtoff/60; #else #ifdef HAVE_GETTIMEOFDAY { struct timeval tv; struct timezone tz; if (0 == gettimeofday(&tv, &tz)) { ourzone = tz.tz_minuteswest; /* the tz_timezone field isn't any use */ } } #endif #endif hh = 0; mm = 0; ss = 0; merid = 24; /* * parse the string */ #ifdef DEBUG yydebug = trace_pretest_; #endif trace(("yyparse()\n{\n")); result = yyparse(); trace(("}\n")); if (result) { result = -1; goto done; } /* * sanity checks */ result = -1; if (timeflag > 1 || zoneflag > 1 || dateflag > 1 || dayflag > 1) goto done; if (dateflag || timeflag || dayflag) { result = dateconv ( month, day, year, hh, mm, ss, merid, ourzone, day_light_flag ); if (result < 0) goto done; } else { result = when; if (!relflag) { result -= ( (lt->tm_hour * 60L + lt->tm_min * 60) + lt->tm_sec ); } } result += relsec; relsec = monthadd(result, relmonth); if (relsec < 0) { result = -1; goto done; } result += relsec; if (dayflag && !dateflag) { tod = dayconv(dayord, dayreq, result); result += tod; } /* * here for all exits */ done: trace(("return %ld;\n", (long)result)); trace(("}\n")); return result; } /* * NAME * date_string - build one * * SYNOPSIS * char *date_string(time_t when); * * DESCRIPTION * The date_string function may be used to construct a * string from a given time in seconds. * * The string will conform to the RFC822 standard, * which states a definite preference for GMT dates. * * ARGUMENTS * when the time to be rendered. * * RETURNS * Pointer to string containing rendered time. * The contents of this string will remain undisturbed * only until the next call to date_string. */ const char * date_string(time_t when) { struct tm *the_time; static char buffer[32]; static const char *weekday_name[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", }; static const char *month_name[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", }; /* * break the given time down into components * (RFC1036 likes GMT, remember) */ trace(("date_string(when = %ld)\n{\n", (long)when)); the_time = gmtime(&when); /* * build the date string */ snprintf ( buffer, sizeof(buffer), "%s,%3d %s %4.4d %2.2d:%2.2d:%2.2d GMT", weekday_name[the_time->tm_wday], the_time->tm_mday, month_name[the_time->tm_mon], the_time->tm_year + 1900, the_time->tm_hour, the_time->tm_min, the_time->tm_sec ); trace(("return \"%s\";\n", buffer)); trace(("}\n")); return buffer; } /* * NAME * yyerror * * SYNOPSIS * void yyerror(char *); * * DESCRIPTION * The yyerror function is invoked by yacc to report * errors, but we just throw it away. * * ARGUMENTS * s - error to report */ static void yyerror(const char *s) { trace(("yyerror(s = \"%s\")\n{\n", s)); (void)s; trace(("}\n")); } /* * NAME * yytrace - follow parser actions * * SYNOPSIS * void yytrace(char *, ...); * * DESCRIPTION * The yytrace function is used to print the various shifts * and reductions, etc, done by the yacc-generated parser. * lines are accumulated and printed whole, * so as to avoid confusing the trace output. * * ARGUMENTS * as for printf * * CAVEAT * only available when DEBUG is defined */ #ifdef DEBUG #define YYDEBUG 1 #define printf yytrace static void yytrace(const char *s, ...) { va_list ap; va_start(ap, s); string_ty *buffer = str_vformat(s, ap); va_end(ap); static char line[1024]; static char *line_p = line; line_p = strendcpy(line_p, buffer->str_text, line + sizeof(line)); str_free(buffer); if (line_p > line && line_p[-1] == '\n') { line_p[-1] = '\0'; trace_printf("%s\n", line); line[0] = '\0'; line_p = line; } } #endif /* DEBUG */ %} %% timedate : /* empty */ | timedate item | error { /* * Mostly, this production is unnecessary, * however it silences warnings about unused * labels, etc. */ return -1; } ; item : TimeSpecification { timeflag++; } | TimeZone { zoneflag++; } | DateSpecification { dateflag++; } | DayOfWeekSpecification { dayflag++; } | RelativeSpecification { relflag++; } | NumberSpecification ; NumberSpecification : NUMBER { if (timeflag && dateflag && !relflag) year = $1; else { timeflag++; hh = $1 / 100; mm = $1 % 100; ss = 0; merid = 24; } } ; TimeSpecification : NUMBER MERIDIAN { hh = $1; mm = 0; ss = 0; merid = $2; } | NUMBER COLON NUMBER { hh = $1; mm = $3; merid = 24; } | NUMBER COLON NUMBER MERIDIAN { hh = $1; mm = $3; merid = $4; } | NUMBER COLON NUMBER NUMBER { hh = $1; mm = $3; merid = 24; day_light_flag = STANDARD; $4 = -$4; ourzone = $4 % 100 + 60 * $4 / 100; } | NUMBER COLON NUMBER COLON NUMBER { hh = $1; mm = $3; ss = $5; merid = 24; } | NUMBER COLON NUMBER COLON NUMBER MERIDIAN { hh = $1; mm = $3; ss = $5; merid = $6; } | NUMBER COLON NUMBER COLON NUMBER NUMBER { hh = $1; mm = $3; ss = $5; merid = 24; day_light_flag = STANDARD; $6 = -$6; ourzone = $6 % 100 + 60 * $6 / 100; } ; TimeZone : ZONE { ourzone = $1; day_light_flag = STANDARD; } | DAYZONE { ourzone = $1; day_light_flag = DAYLIGHT; } ; DayOfWeekSpecification : DAY { dayord = 1; dayreq = $1; } | DAY COMMA { dayord = 1; dayreq = $1; } | NUMBER DAY { dayord = $1; dayreq = $2; } ; DateSpecification : NUMBER SLASH NUMBER { if ($1 > 12 && $3 <= 12) { day = $1; month = $3; } else { month = $1; day = $3; } } | NUMBER SLASH NUMBER SLASH NUMBER { if ($1 > 12 && $3 <= 12) { /* european and Australian */ day = $1; month = $3; } else { month = $1; day = $3; } year = $5; } | MONTH NUMBER { month = $1; day = $2; } | MONTH NUMBER COMMA NUMBER { month = $1; day = $2; year = $4; } | NUMBER MONTH { month = $2; day = $1; } | NUMBER MONTH NUMBER { month = $2; day = $1; year = $3; } ; RelativeSpecification : NUMBER UNIT { relsec += 60L * $1 * $2; } | NUMBER MUNIT { relmonth += $1 * $2; } | NUMBER SUNIT { relsec += $1; } | UNIT { relsec += 60L * $1; } | MUNIT { relmonth += $1; } | SUNIT { relsec++; } | RelativeSpecification AGO { relsec = -relsec; relmonth = -relmonth; } ; %% /* The following needs to be here so Berkeley Yacc doesn't puke. */ /* * NAME * table - list of known names * * SYNOPSIS * table_t table[]; * * DESCRIPTION * The table is used to hold the list of known names. * This includes time zone names and days of the week, etc. * * CAVEAT * It is in English. * It is impossible to have a full list of time zones. */ struct table_t { const char *name; int type; int value; }; #define HRMIN(a, b) ((a) * 60 + (b)) static table_t table[] = { { "a", ZONE, HRMIN(1, 0), }, { "a.c.s.s.t.", DAYZONE, -HRMIN(9, 30), }, { "a.c.s.t.", ZONE, -HRMIN(9, 30), }, { "a.d.t.", DAYZONE, HRMIN(4, 0), }, { "a.e.s.s.t.", DAYZONE, -HRMIN(10, 0), }, { "a.e.s.t.", ZONE, -HRMIN(10, 0), }, { "a.m.", MERIDIAN, AM, }, { "a.s.t.", ZONE, HRMIN(4, 0), }, { "a.w.s.t.", ZONE, -HRMIN(8, 0), }, /* (no daylight time there, I'm told */ { "acsst", DAYZONE, -HRMIN(9, 30), }, /* Australian Central Summer Time */ { "acst", ZONE, -HRMIN(9, 30), }, /* Australian Central Time */ { "adt", DAYZONE, HRMIN(4, 0), }, { "aesst", DAYZONE, -HRMIN(10, 0), }, /* Australian Eastern Summer Time */ { "aest", ZONE, -HRMIN(10, 0), }, /* Australian Eastern Time */ { "ago", AGO, 1, }, { "am", MERIDIAN, AM, }, { "apr", MONTH, 4, }, { "apr.", MONTH, 4, }, { "april", MONTH, 4, }, { "ast", ZONE, HRMIN(4, 0), }, /* Atlantic */ { "aug", MONTH, 8, }, { "aug.", MONTH, 8, }, { "august", MONTH, 8, }, { "awst", ZONE, -HRMIN(8, 0), }, /* Australian Western Time */ { "b", ZONE, HRMIN(2, 0), }, { "b.s.t.", DAYZONE, HRMIN(0, 0), }, { "bst", DAYZONE, HRMIN(0, 0), }, /* British Summer Time*/ { "c", ZONE, HRMIN(3, 0), }, { "c.d.t.", DAYZONE, HRMIN(6, 0), }, { "c.s.t.", ZONE, HRMIN(6, 0), }, { "cdt", DAYZONE, HRMIN(6, 0), }, { "cst", ZONE, HRMIN(6, 0), }, /* Central */ { "d", ZONE, HRMIN(4, 0), }, { "day", UNIT, 1 * 24 * 60, }, { "days", UNIT, 1 * 24 * 60, }, { "dec", MONTH, 12, }, { "dec.", MONTH, 12, }, { "december", MONTH, 12, }, { "e", ZONE, HRMIN(5, 0), }, { "e.d.t.", DAYZONE, HRMIN(5, 0), }, { "e.e.s.t.", DAYZONE, HRMIN(0, 0), }, { "e.e.t.", ZONE, HRMIN(0, 0), }, { "e.s.t.", ZONE, HRMIN(5, 0), }, { "edt", DAYZONE, HRMIN(5, 0), }, { "eest", DAYZONE, HRMIN(0, 0), }, /* European Eastern Summer Time */ { "eet", ZONE, HRMIN(0, 0), }, /* European Eastern Time */ { "eigth", NUMBER, 8, }, { "eleventh", NUMBER, 11, }, { "est", ZONE, HRMIN(5, 0), }, /* Eastern */ { "f", ZONE, HRMIN(6, 0), }, { "feb", MONTH, 2, }, { "feb.", MONTH, 2, }, { "february", MONTH, 2, }, { "fifth", NUMBER, 5, }, { "first", NUMBER, 1, }, { "fortnight", UNIT, 14 * 24 * 60, }, { "fortnights", UNIT, 14 * 24 * 60, }, { "fourth", NUMBER, 4, }, { "fri", DAY, 5, }, { "fri.", DAY, 5, }, { "friday", DAY, 5, }, { "g", ZONE, HRMIN(7, 0), }, { "g.m.t.", ZONE, HRMIN(0, 0), }, { "gmt", ZONE, HRMIN(0, 0), }, { "h", ZONE, HRMIN(8, 0), }, { "h.d.t.", DAYZONE, HRMIN(10, 0), }, { "h.s.t.", ZONE, HRMIN(10, 0), }, { "hdt", DAYZONE, HRMIN(10, 0), }, { "hour", UNIT, 60, }, { "hours", UNIT, 60, }, { "hr", UNIT, 60, }, { "hrs", UNIT, 60, }, { "hst", ZONE, HRMIN(10, 0), }, /* Hawaii */ { "i", ZONE, HRMIN(9, 0), }, { "j.s.t.", ZONE, -HRMIN(9, 0), }, /* Japan Standard Time */ { "jan", MONTH, 1, }, { "jan.", MONTH, 1, }, { "january", MONTH, 1, }, { "jst", ZONE, -HRMIN(9, 0), }, /* Japan Standard Time */ { "jul", MONTH, 7, }, { "jul.", MONTH, 7, }, { "july", MONTH, 7, }, { "jun", MONTH, 6, }, { "jun.", MONTH, 6, }, { "june", MONTH, 6, }, { "k", ZONE, HRMIN(10, 0), }, { "l", ZONE, HRMIN(11, 0), }, { "last", NUMBER, -1, }, { "m", ZONE, HRMIN(12, 0), }, { "m.d.t.", DAYZONE, HRMIN(7, 0), }, { "m.e.s.t.", DAYZONE, -HRMIN(1, 0), }, { "m.e.t.", ZONE, -HRMIN(1, 0), }, { "m.s.t.", ZONE, HRMIN(7, 0), }, { "mar", MONTH, 3, }, { "mar.", MONTH, 3, }, { "march", MONTH, 3, }, { "may", MONTH, 5, }, { "mdt", DAYZONE, HRMIN(7, 0), }, { "mest", DAYZONE, -HRMIN(1, 0), }, /* Middle European Summer Time */ { "met", ZONE, -HRMIN(1, 0), }, /* Middle European Time */ { "min", UNIT, 1, }, { "mins", UNIT, 1, }, { "minute", UNIT, 1, }, { "minutes", UNIT, 1, }, { "mon", DAY, 1, }, { "mon.", DAY, 1, }, { "monday", DAY, 1, }, { "month", MUNIT, 1, }, { "months", MUNIT, 1, }, { "mst", ZONE, HRMIN(7, 0), }, /* Mountain */ { "n", ZONE, -HRMIN(1, 0), }, { "n.s.t.", ZONE, HRMIN(3, 30), }, { "next", NUMBER, 2, }, { "ninth", NUMBER, 9, }, { "nov", MONTH, 11, }, { "nov.", MONTH, 11, }, { "november", MONTH, 11, }, { "now", UNIT, 0, }, { "nst", ZONE, HRMIN(3, 30), }, /* Newfoundland */ { "o", ZONE, -HRMIN(2, 0), }, { "oct", MONTH, 10, }, { "oct.", MONTH, 10, }, { "october", MONTH, 10, }, { "p", ZONE, -HRMIN(3, 0), }, { "p.d.t.", DAYZONE, HRMIN(8, 0), }, { "p.m.", MERIDIAN, PM, }, { "p.s.t.", ZONE, HRMIN(8, 0), }, { "pdt", DAYZONE, HRMIN(8, 0), }, { "pm", MERIDIAN, PM, }, { "pst", ZONE, HRMIN(8, 0), }, /* Pacific */ { "q", ZONE, -HRMIN(4, 0), }, { "r", ZONE, -HRMIN(5, 0), }, { "s", ZONE, -HRMIN(6, 0), }, { "sat", DAY, 6, }, { "sat.", DAY, 6, }, { "saturday", DAY, 6, }, { "sec", SUNIT, 1, }, { "second", SUNIT, 1, }, { "seconds", SUNIT, 1, }, { "secs", SUNIT, 1, }, { "sep", MONTH, 9, }, { "sep.", MONTH, 9, }, { "sept", MONTH, 9, }, { "sept.", MONTH, 9, }, { "september", MONTH, 9, }, { "seventh", NUMBER, 7, }, { "sixth", NUMBER, 6, }, { "sun", DAY, 0, }, { "sun.", DAY, 0, }, { "sunday", DAY, 0, }, { "t", ZONE, -HRMIN(7, 0), }, { "tenth", NUMBER, 10, }, { "third", NUMBER, 3, }, { "this", UNIT, 0, }, { "thu", DAY, 4, }, { "thu.", DAY, 4, }, { "thur", DAY, 4, }, { "thur.", DAY, 4, }, { "thurs", DAY, 4, }, { "thurs.", DAY, 4, }, { "thursday", DAY, 4, }, { "today", UNIT, 0, }, { "tomorrow", UNIT, 1 * 24 * 60, }, { "tue", DAY, 2, }, { "tue.", DAY, 2, }, { "tues", DAY, 2, }, { "tues.", DAY, 2, }, { "tuesday", DAY, 2, }, { "twelfth", NUMBER, 12, }, { "u", ZONE, -HRMIN(8, 0), }, { "u.t.", ZONE, HRMIN(0, 0), }, { "ut", ZONE, HRMIN(0, 0), }, { "v", ZONE, -HRMIN(9, 0), }, { "w", ZONE, -HRMIN(10, 0), }, { "w.e.s.t.", DAYZONE, -HRMIN(2, 0), }, { "w.e.t.", ZONE, -HRMIN(2, 0), }, { "wed", DAY, 3, }, { "wed.", DAY, 3, }, { "wednes", DAY, 3, }, { "wednes.", DAY, 3, }, { "wednesday", DAY, 3, }, { "week", UNIT, 7 * 24 * 60, }, { "weeks", UNIT, 7 * 24 * 60, }, { "west", DAYZONE, -HRMIN(2, 0), }, /* Western European Summer Time */ { "wet", ZONE, -HRMIN(2, 0), }, /* Western European Time */ { "x", ZONE, -HRMIN(11, 0), }, { "y", ZONE, -HRMIN(12, 0), }, { "y.d.t.", DAYZONE, HRMIN(9, 0), }, { "y.s.t.", ZONE, HRMIN(9, 0), }, { "ydt", DAYZONE, HRMIN(9, 0), }, { "year", MUNIT, 12, }, { "years", MUNIT, 12, }, { "yesterday", UNIT, -1*24*60, }, { "yst", ZONE, HRMIN(9, 0), }, /* Yukon */ { "z", ZONE, HRMIN(0, 0), }, }; /* * NAME * lookup - find name * * SYNOPSIS * int lookup(char *id); * * DESCRIPTION * The lookup function is used to find a token corresponding to * a given name. * * ARGUMENTS * id - name to search for. Assumes already downcased. * * RETURNS * int; yacc token, ID if not found. */ static int lookup(char *id) { table_t *tp; int min; int max; int mid; int cmp; int result; /* * binary chop the table */ trace(("lookup(id = \"%s\")\n{\n", id)); result = ID; min = 0; max = SIZEOF(table) - 1; while (min <= max) { mid = (min + max) / 2; tp = table + mid; cmp = strcmp(id, tp->name); if (!cmp) { yylval = tp->value; result = tp->type; break; } if (cmp < 0) max = mid - 1; else min = mid + 1; } trace(("return %d;\n", result)); trace(("}\n")); return result; } /* * NAME * yylex - lexical analyser * * SYNOPSIS * int yylex(void); * * DESCRIPTION * The yylex function is used to scan the input string * and break it into discrete tokens. * * RETURNS * int; the yacc token, 0 means the-end. */ static int yylex(void) { int sign; int c; char *p; char idbuf[MAX_ID_LENGTH]; int pcnt; int token; trace(("yylex()\n{\n")); yylval = 0; for (;;) { /* * get the next input character */ c = *lptr++; /* * action depends on the character */ switch (c) { case 0: token = 0; lptr--; break; case ' ': case '\t': /* * ignore white space */ continue; case ':': token = COLON; break; case ',': token = COMMA; break; case '/': token = SLASH; break; case '.': /* ignore lonely dots */ continue; case '-': if (!isdigit((unsigned char)*lptr)) { /* * ignore lonely '-'s */ continue; } sign = -1; c = *lptr++; goto number; case '+': if (!isdigit((unsigned char)*lptr)) { token = c; break; } sign = 1; c = *lptr++; goto number; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': /* * numbers */ sign = 1; number: for (;;) { yylval = yylval * 10 + c - '0'; c = *lptr++; switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': continue; } break; } yylval *= sign; lptr--; token = NUMBER; break; case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': /* * name */ p = idbuf; for (;;) { if (isupper((unsigned char)c)) c = tolower(c); if (p < idbuf + sizeof(idbuf) - 1) *p++ = c; c = *lptr++; switch (c) { case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': case '.': continue; } break; } *p = 0; lptr--; token = lookup(idbuf); break; case '(': /* * comment */ for (pcnt = 1; pcnt > 0; ) { c = *lptr++; switch (c) { case 0: --lptr; pcnt = 0; break; case '(': pcnt++; break; case ')': pcnt--; break; } } continue; default: /* * unrecognosed */ token = JUNK; break; } break; } trace(("yylval = %d;\n", yylval)); trace(("return %d;\n", token)); trace(("}\n")); return token; } /* vim: set ts=8 sw=4 et : */