[BACK]Return to date.y CVS log [TXT][DIR] Up to [local] / src / usr.bin / cvs

Annotation of src/usr.bin/cvs/date.y, Revision 1.16

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