[BACK]Return to parsedate.y CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / lib / libutil

Annotation of src/lib/libutil/parsedate.y, Revision 1.16.6.4

1.1       christos    1: %{
                      2: /*
                      3: **  Originally written by Steven M. Bellovin <smb@research.att.com> while
                      4: **  at the University of North Carolina at Chapel Hill.  Later tweaked by
                      5: **  a couple of people on Usenet.  Completely overhauled by Rich $alz
                      6: **  <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990;
                      7: **
                      8: **  This grammar has 10 shift/reduce conflicts.
                      9: **
                     10: **  This code is in the public domain and has no copyright.
                     11: */
                     12: /* SUPPRESS 287 on yaccpar_sccsid *//* Unused static variable */
                     13: /* SUPPRESS 288 on yyerrlab *//* Label unused */
                     14:
1.15      yamt       15: #include <sys/cdefs.h>
                     16: #ifdef __RCSID
1.16.6.4! snj        17: __RCSID("$NetBSD: parsedate.y,v 1.16.6.3 2014/10/11 16:51:23 snj Exp $");
1.15      yamt       18: #endif
                     19:
1.1       christos   20: #include <stdio.h>
                     21: #include <ctype.h>
1.14      apb        22: #include <errno.h>
1.1       christos   23: #include <string.h>
                     24: #include <time.h>
                     25: #include <util.h>
1.3       drochner   26: #include <stdlib.h>
1.1       christos   27:
                     28: /* NOTES on rebuilding parsedate.c (particularly for inclusion in CVS
                     29:    releases):
                     30:
                     31:    We don't want to mess with all the portability hassles of alloca.
                     32:    In particular, most (all?) versions of bison will use alloca in
                     33:    their parser.  If bison works on your system (e.g. it should work
                     34:    with gcc), then go ahead and use it, but the more general solution
                     35:    is to use byacc instead of bison, which should generate a portable
                     36:    parser.  I played with adding "#define alloca dont_use_alloca", to
                     37:    give an error if the parser generator uses alloca (and thus detect
                     38:    unportable parsedate.c's), but that seems to cause as many problems
                     39:    as it solves.  */
                     40:
                     41: #define EPOCH          1970
                     42: #define HOUR(x)                ((time_t)(x) * 60)
                     43: #define SECSPERDAY     (24L * 60L * 60L)
                     44:
1.16.6.4! snj        45: #define USE_LOCAL_TIME 99999 /* special case for Convert() and yyTimezone */
1.1       christos   46:
                     47: /*
                     48: **  An entry in the lexical lookup table.
                     49: */
                     50: typedef struct _TABLE {
                     51:     const char *name;
                     52:     int                type;
                     53:     time_t     value;
                     54: } TABLE;
                     55:
                     56:
                     57: /*
                     58: **  Daylight-savings mode:  on, off, or not yet known.
                     59: */
                     60: typedef enum _DSTMODE {
                     61:     DSTon, DSToff, DSTmaybe
                     62: } DSTMODE;
                     63:
                     64: /*
                     65: **  Meridian:  am, pm, or 24-hour style.
                     66: */
                     67: typedef enum _MERIDIAN {
                     68:     MERam, MERpm, MER24
                     69: } MERIDIAN;
                     70:
                     71:
1.9       christos   72: struct dateinfo {
1.16.6.1  snj        73:        DSTMODE yyDSTmode;      /* DST on/off/maybe */
1.9       christos   74:        time_t  yyDayOrdinal;
                     75:        time_t  yyDayNumber;
                     76:        int     yyHaveDate;
1.16.6.1  snj        77:        int     yyHaveFullYear; /* if true, year is not abbreviated. */
                     78:                                /* if false, need to call AdjustYear(). */
1.9       christos   79:        int     yyHaveDay;
                     80:        int     yyHaveRel;
                     81:        int     yyHaveTime;
                     82:        int     yyHaveZone;
1.16.6.1  snj        83:        time_t  yyTimezone;     /* Timezone as minutes ahead/east of UTC */
                     84:        time_t  yyDay;          /* Day of month [1-31] */
                     85:        time_t  yyHour;         /* Hour of day [0-24] or [1-12] */
                     86:        time_t  yyMinutes;      /* Minute of hour [0-59] */
                     87:        time_t  yyMonth;        /* Month of year [1-12] */
                     88:        time_t  yySeconds;      /* Second of minute [0-60] */
                     89:        time_t  yyYear;         /* Year, see also yyHaveFullYear */
                     90:        MERIDIAN yyMeridian;    /* Interpret yyHour as AM/PM/24 hour clock */
1.9       christos   91:        time_t  yyRelMonth;
                     92:        time_t  yyRelSeconds;
                     93: };
1.1       christos   94: %}
                     95:
                     96: %union {
                     97:     time_t             Number;
                     98:     enum _MERIDIAN     Meridian;
                     99: }
                    100:
                    101: %token tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT
1.5       tron      102: %token tSEC_UNIT tSNUMBER tUNUMBER tZONE tDST AT_SIGN
1.1       christos  103:
                    104: %type  <Number>        tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT
                    105: %type  <Number>        tSEC_UNIT tSNUMBER tUNUMBER tZONE
                    106: %type  <Meridian>      tMERIDIAN o_merid
                    107:
1.9       christos  108: %parse-param   { struct dateinfo *param }
                    109: %parse-param   { const char **yyInput }
                    110: %lex-param     { const char **yyInput }
                    111: %pure-parser
                    112:
1.1       christos  113: %%
                    114:
                    115: spec   : /* NULL */
                    116:        | spec item
                    117:        ;
                    118:
                    119: item   : time {
1.9       christos  120:            param->yyHaveTime++;
1.1       christos  121:        }
1.16.6.3  snj       122:        | time_numericzone {
                    123:            param->yyHaveTime++;
                    124:            param->yyHaveZone++;
                    125:        }
1.1       christos  126:        | zone {
1.9       christos  127:            param->yyHaveZone++;
1.1       christos  128:        }
                    129:        | date {
1.9       christos  130:            param->yyHaveDate++;
1.1       christos  131:        }
                    132:        | day {
1.9       christos  133:            param->yyHaveDay++;
1.1       christos  134:        }
                    135:        | rel {
1.9       christos  136:            param->yyHaveRel++;
1.1       christos  137:        }
                    138:        | cvsstamp {
1.9       christos  139:            param->yyHaveTime++;
                    140:            param->yyHaveDate++;
                    141:            param->yyHaveZone++;
1.1       christos  142:        }
1.5       tron      143:        | epochdate {
1.9       christos  144:            param->yyHaveTime++;
                    145:            param->yyHaveDate++;
                    146:            param->yyHaveZone++;
1.5       tron      147:        }
1.1       christos  148:        | number
                    149:        ;
                    150:
                    151: cvsstamp: tUNUMBER '.' tUNUMBER '.' tUNUMBER '.' tUNUMBER '.' tUNUMBER '.' tUNUMBER {
1.9       christos  152:            param->yyYear = $1;
                    153:            if (param->yyYear < 100) param->yyYear += 1900;
1.16.6.1  snj       154:            param->yyHaveFullYear = 1;
1.9       christos  155:            param->yyMonth = $3;
                    156:            param->yyDay = $5;
                    157:            param->yyHour = $7;
                    158:            param->yyMinutes = $9;
                    159:            param->yySeconds = $11;
                    160:            param->yyDSTmode = DSToff;
                    161:            param->yyTimezone = 0;
1.1       christos  162:        }
                    163:        ;
                    164:
1.14      apb       165: epochdate: AT_SIGN at_number {
                    166:             time_t    when = $<Number>2;
1.5       tron      167:             struct tm tmbuf;
                    168:             if (gmtime_r(&when, &tmbuf) != NULL) {
1.9       christos  169:                param->yyYear = tmbuf.tm_year + 1900;
                    170:                param->yyMonth = tmbuf.tm_mon + 1;
                    171:                param->yyDay = tmbuf.tm_mday;
                    172:
                    173:                param->yyHour = tmbuf.tm_hour;
                    174:                param->yyMinutes = tmbuf.tm_min;
                    175:                param->yySeconds = tmbuf.tm_sec;
1.5       tron      176:            } else {
1.9       christos  177:                param->yyYear = EPOCH;
                    178:                param->yyMonth = 1;
                    179:                param->yyDay = 1;
                    180:
                    181:                param->yyHour = 0;
                    182:                param->yyMinutes = 0;
                    183:                param->yySeconds = 0;
1.5       tron      184:            }
1.16.6.1  snj       185:            param->yyHaveFullYear = 1;
1.9       christos  186:            param->yyDSTmode = DSToff;
                    187:            param->yyTimezone = 0;
1.5       tron      188:        }
                    189:        ;
                    190:
1.14      apb       191: at_number : tUNUMBER | tSNUMBER ;
                    192:
1.1       christos  193: time   : tUNUMBER tMERIDIAN {
1.9       christos  194:            param->yyHour = $1;
                    195:            param->yyMinutes = 0;
                    196:            param->yySeconds = 0;
                    197:            param->yyMeridian = $2;
1.1       christos  198:        }
                    199:        | tUNUMBER ':' tUNUMBER o_merid {
1.9       christos  200:            param->yyHour = $1;
                    201:            param->yyMinutes = $3;
                    202:            param->yySeconds = 0;
                    203:            param->yyMeridian = $4;
1.1       christos  204:        }
                    205:        | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
1.9       christos  206:            param->yyHour = $1;
                    207:            param->yyMinutes = $3;
                    208:            param->yySeconds = $5;
                    209:            param->yyMeridian = $6;
1.1       christos  210:        }
1.16.6.3  snj       211:        | tUNUMBER ':' tUNUMBER ':' tUNUMBER '.' tUNUMBER {
1.9       christos  212:            param->yyHour = $1;
                    213:            param->yyMinutes = $3;
                    214:            param->yySeconds = $5;
                    215:            param->yyMeridian = MER24;
1.16.6.3  snj       216: /* XXX: Do nothing with millis */
                    217:        }
                    218:        ;
                    219:
                    220: time_numericzone : tUNUMBER ':' tUNUMBER tSNUMBER {
                    221:            param->yyHour = $1;
                    222:            param->yyMinutes = $3;
                    223:            param->yyMeridian = MER24;
1.9       christos  224:            param->yyDSTmode = DSToff;
1.16.6.3  snj       225:            param->yyTimezone = - ($4 % 100 + ($4 / 100) * 60);
1.1       christos  226:        }
1.16.6.3  snj       227:        | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER {
1.9       christos  228:            param->yyHour = $1;
                    229:            param->yyMinutes = $3;
                    230:            param->yySeconds = $5;
                    231:            param->yyMeridian = MER24;
1.16.6.3  snj       232:            param->yyDSTmode = DSToff;
                    233:            param->yyTimezone = - ($6 % 100 + ($6 / 100) * 60);
1.7       christos  234:        }
1.1       christos  235:        ;
                    236:
                    237: zone   : tZONE {
1.9       christos  238:            param->yyTimezone = $1;
                    239:            param->yyDSTmode = DSToff;
1.1       christos  240:        }
                    241:        | tDAYZONE {
1.9       christos  242:            param->yyTimezone = $1;
                    243:            param->yyDSTmode = DSTon;
1.1       christos  244:        }
                    245:        |
                    246:          tZONE tDST {
1.9       christos  247:            param->yyTimezone = $1;
                    248:            param->yyDSTmode = DSTon;
1.1       christos  249:        }
                    250:        ;
                    251:
                    252: day    : tDAY {
1.9       christos  253:            param->yyDayOrdinal = 1;
                    254:            param->yyDayNumber = $1;
1.1       christos  255:        }
                    256:        | tDAY ',' {
1.9       christos  257:            param->yyDayOrdinal = 1;
                    258:            param->yyDayNumber = $1;
1.1       christos  259:        }
                    260:        | tUNUMBER tDAY {
1.9       christos  261:            param->yyDayOrdinal = $1;
                    262:            param->yyDayNumber = $2;
1.1       christos  263:        }
                    264:        ;
                    265:
                    266: date   : tUNUMBER '/' tUNUMBER {
1.9       christos  267:            param->yyMonth = $1;
                    268:            param->yyDay = $3;
1.1       christos  269:        }
                    270:        | tUNUMBER '/' tUNUMBER '/' tUNUMBER {
                    271:            if ($1 >= 100) {
1.9       christos  272:                param->yyYear = $1;
                    273:                param->yyMonth = $3;
                    274:                param->yyDay = $5;
1.1       christos  275:            } else {
1.9       christos  276:                param->yyMonth = $1;
                    277:                param->yyDay = $3;
                    278:                param->yyYear = $5;
1.1       christos  279:            }
                    280:        }
                    281:        | tUNUMBER tSNUMBER tSNUMBER {
                    282:            /* ISO 8601 format.  yyyy-mm-dd.  */
1.9       christos  283:            param->yyYear = $1;
1.16.6.1  snj       284:            param->yyHaveFullYear = 1;
1.9       christos  285:            param->yyMonth = -$2;
                    286:            param->yyDay = -$3;
1.1       christos  287:        }
                    288:        | tUNUMBER tMONTH tSNUMBER {
                    289:            /* e.g. 17-JUN-1992.  */
1.9       christos  290:            param->yyDay = $1;
                    291:            param->yyMonth = $2;
                    292:            param->yyYear = -$3;
1.1       christos  293:        }
                    294:        | tMONTH tUNUMBER {
1.9       christos  295:            param->yyMonth = $1;
                    296:            param->yyDay = $2;
1.1       christos  297:        }
                    298:        | tMONTH tUNUMBER ',' tUNUMBER {
1.9       christos  299:            param->yyMonth = $1;
                    300:            param->yyDay = $2;
                    301:            param->yyYear = $4;
1.1       christos  302:        }
                    303:        | tUNUMBER tMONTH {
1.9       christos  304:            param->yyMonth = $2;
                    305:            param->yyDay = $1;
1.1       christos  306:        }
                    307:        | tUNUMBER tMONTH tUNUMBER {
1.9       christos  308:            param->yyMonth = $2;
                    309:            param->yyDay = $1;
                    310:            param->yyYear = $3;
1.1       christos  311:        }
                    312:        ;
                    313:
                    314: rel    : relunit tAGO {
1.9       christos  315:            param->yyRelSeconds = -param->yyRelSeconds;
                    316:            param->yyRelMonth = -param->yyRelMonth;
1.1       christos  317:        }
                    318:        | relunit
                    319:        ;
                    320:
                    321: relunit        : tUNUMBER tMINUTE_UNIT {
1.9       christos  322:            param->yyRelSeconds += $1 * $2 * 60L;
1.1       christos  323:        }
                    324:        | tSNUMBER tMINUTE_UNIT {
1.9       christos  325:            param->yyRelSeconds += $1 * $2 * 60L;
1.1       christos  326:        }
                    327:        | tMINUTE_UNIT {
1.9       christos  328:            param->yyRelSeconds += $1 * 60L;
1.1       christos  329:        }
                    330:        | tSNUMBER tSEC_UNIT {
1.9       christos  331:            param->yyRelSeconds += $1;
1.1       christos  332:        }
                    333:        | tUNUMBER tSEC_UNIT {
1.9       christos  334:            param->yyRelSeconds += $1;
1.1       christos  335:        }
                    336:        | tSEC_UNIT {
1.9       christos  337:            param->yyRelSeconds++;
1.1       christos  338:        }
                    339:        | tSNUMBER tMONTH_UNIT {
1.9       christos  340:            param->yyRelMonth += $1 * $2;
1.1       christos  341:        }
                    342:        | tUNUMBER tMONTH_UNIT {
1.9       christos  343:            param->yyRelMonth += $1 * $2;
1.1       christos  344:        }
                    345:        | tMONTH_UNIT {
1.9       christos  346:            param->yyRelMonth += $1;
1.1       christos  347:        }
                    348:        ;
                    349:
                    350: number : tUNUMBER {
1.9       christos  351:            if (param->yyHaveTime && param->yyHaveDate && !param->yyHaveRel)
                    352:                param->yyYear = $1;
1.1       christos  353:            else {
                    354:                if($1>10000) {
1.9       christos  355:                    param->yyHaveDate++;
                    356:                    param->yyDay= ($1)%100;
                    357:                    param->yyMonth= ($1/100)%100;
                    358:                    param->yyYear = $1/10000;
1.1       christos  359:                }
                    360:                else {
1.9       christos  361:                    param->yyHaveTime++;
1.1       christos  362:                    if ($1 < 100) {
1.9       christos  363:                        param->yyHour = $1;
                    364:                        param->yyMinutes = 0;
1.1       christos  365:                    }
                    366:                    else {
1.9       christos  367:                        param->yyHour = $1 / 100;
                    368:                        param->yyMinutes = $1 % 100;
1.1       christos  369:                    }
1.9       christos  370:                    param->yySeconds = 0;
                    371:                    param->yyMeridian = MER24;
1.1       christos  372:                }
                    373:            }
                    374:        }
                    375:        ;
                    376:
                    377: o_merid        : /* NULL */ {
                    378:            $$ = MER24;
                    379:        }
                    380:        | tMERIDIAN {
                    381:            $$ = $1;
                    382:        }
                    383:        ;
                    384:
                    385: %%
                    386:
                    387: /* Month and day table. */
1.12      joerg     388: static const TABLE MonthDayTable[] = {
1.1       christos  389:     { "january",       tMONTH,  1 },
                    390:     { "february",      tMONTH,  2 },
                    391:     { "march",         tMONTH,  3 },
                    392:     { "april",         tMONTH,  4 },
                    393:     { "may",           tMONTH,  5 },
                    394:     { "june",          tMONTH,  6 },
                    395:     { "july",          tMONTH,  7 },
                    396:     { "august",                tMONTH,  8 },
                    397:     { "september",     tMONTH,  9 },
                    398:     { "sept",          tMONTH,  9 },
                    399:     { "october",       tMONTH, 10 },
                    400:     { "november",      tMONTH, 11 },
                    401:     { "december",      tMONTH, 12 },
                    402:     { "sunday",                tDAY, 0 },
                    403:     { "monday",                tDAY, 1 },
                    404:     { "tuesday",       tDAY, 2 },
                    405:     { "tues",          tDAY, 2 },
                    406:     { "wednesday",     tDAY, 3 },
                    407:     { "wednes",                tDAY, 3 },
                    408:     { "thursday",      tDAY, 4 },
                    409:     { "thur",          tDAY, 4 },
                    410:     { "thurs",         tDAY, 4 },
                    411:     { "friday",                tDAY, 5 },
                    412:     { "saturday",      tDAY, 6 },
                    413:     { NULL,            0,    0 }
                    414: };
                    415:
                    416: /* Time units table. */
1.12      joerg     417: static const TABLE UnitsTable[] = {
1.1       christos  418:     { "year",          tMONTH_UNIT,    12 },
                    419:     { "month",         tMONTH_UNIT,    1 },
                    420:     { "fortnight",     tMINUTE_UNIT,   14 * 24 * 60 },
                    421:     { "week",          tMINUTE_UNIT,   7 * 24 * 60 },
                    422:     { "day",           tMINUTE_UNIT,   1 * 24 * 60 },
                    423:     { "hour",          tMINUTE_UNIT,   60 },
                    424:     { "minute",                tMINUTE_UNIT,   1 },
                    425:     { "min",           tMINUTE_UNIT,   1 },
                    426:     { "second",                tSEC_UNIT,      1 },
                    427:     { "sec",           tSEC_UNIT,      1 },
                    428:     { NULL,            0,              0 }
                    429: };
                    430:
                    431: /* Assorted relative-time words. */
1.12      joerg     432: static const TABLE OtherTable[] = {
1.1       christos  433:     { "tomorrow",      tMINUTE_UNIT,   1 * 24 * 60 },
                    434:     { "yesterday",     tMINUTE_UNIT,   -1 * 24 * 60 },
                    435:     { "today",         tMINUTE_UNIT,   0 },
                    436:     { "now",           tMINUTE_UNIT,   0 },
                    437:     { "last",          tUNUMBER,       -1 },
                    438:     { "this",          tMINUTE_UNIT,   0 },
                    439:     { "next",          tUNUMBER,       2 },
                    440:     { "first",         tUNUMBER,       1 },
1.7       christos  441:     { "one",           tUNUMBER,       1 },
1.1       christos  442: /*  { "second",                tUNUMBER,       2 }, */
1.7       christos  443:     { "two",           tUNUMBER,       2 },
1.1       christos  444:     { "third",         tUNUMBER,       3 },
1.7       christos  445:     { "three",         tUNUMBER,       3 },
1.1       christos  446:     { "fourth",                tUNUMBER,       4 },
1.7       christos  447:     { "four",          tUNUMBER,       4 },
1.1       christos  448:     { "fifth",         tUNUMBER,       5 },
1.7       christos  449:     { "five",          tUNUMBER,       5 },
1.1       christos  450:     { "sixth",         tUNUMBER,       6 },
1.7       christos  451:     { "six",           tUNUMBER,       6 },
1.1       christos  452:     { "seventh",       tUNUMBER,       7 },
1.7       christos  453:     { "seven",         tUNUMBER,       7 },
1.1       christos  454:     { "eighth",                tUNUMBER,       8 },
1.7       christos  455:     { "eight",         tUNUMBER,       8 },
1.1       christos  456:     { "ninth",         tUNUMBER,       9 },
1.7       christos  457:     { "nine",          tUNUMBER,       9 },
1.1       christos  458:     { "tenth",         tUNUMBER,       10 },
1.7       christos  459:     { "ten",           tUNUMBER,       10 },
1.1       christos  460:     { "eleventh",      tUNUMBER,       11 },
1.7       christos  461:     { "eleven",                tUNUMBER,       11 },
1.1       christos  462:     { "twelfth",       tUNUMBER,       12 },
1.7       christos  463:     { "twelve",                tUNUMBER,       12 },
1.1       christos  464:     { "ago",           tAGO,   1 },
                    465:     { NULL,            0,      0 }
                    466: };
                    467:
                    468: /* The timezone table. */
                    469: /* Some of these are commented out because a time_t can't store a float. */
1.12      joerg     470: static const TABLE TimezoneTable[] = {
1.1       christos  471:     { "gmt",   tZONE,     HOUR( 0) },  /* Greenwich Mean */
                    472:     { "ut",    tZONE,     HOUR( 0) },  /* Universal (Coordinated) */
                    473:     { "utc",   tZONE,     HOUR( 0) },
                    474:     { "wet",   tZONE,     HOUR( 0) },  /* Western European */
                    475:     { "bst",   tDAYZONE,  HOUR( 0) },  /* British Summer */
                    476:     { "wat",   tZONE,     HOUR( 1) },  /* West Africa */
                    477:     { "at",    tZONE,     HOUR( 2) },  /* Azores */
                    478: #if    0
                    479:     /* For completeness.  BST is also British Summer, and GST is
                    480:      * also Guam Standard. */
                    481:     { "bst",   tZONE,     HOUR( 3) },  /* Brazil Standard */
                    482:     { "gst",   tZONE,     HOUR( 3) },  /* Greenland Standard */
                    483: #endif
                    484: #if 0
                    485:     { "nft",   tZONE,     HOUR(3.5) }, /* Newfoundland */
                    486:     { "nst",   tZONE,     HOUR(3.5) }, /* Newfoundland Standard */
                    487:     { "ndt",   tDAYZONE,  HOUR(3.5) }, /* Newfoundland Daylight */
                    488: #endif
                    489:     { "ast",   tZONE,     HOUR( 4) },  /* Atlantic Standard */
                    490:     { "adt",   tDAYZONE,  HOUR( 4) },  /* Atlantic Daylight */
                    491:     { "est",   tZONE,     HOUR( 5) },  /* Eastern Standard */
                    492:     { "edt",   tDAYZONE,  HOUR( 5) },  /* Eastern Daylight */
                    493:     { "cst",   tZONE,     HOUR( 6) },  /* Central Standard */
                    494:     { "cdt",   tDAYZONE,  HOUR( 6) },  /* Central Daylight */
                    495:     { "mst",   tZONE,     HOUR( 7) },  /* Mountain Standard */
                    496:     { "mdt",   tDAYZONE,  HOUR( 7) },  /* Mountain Daylight */
                    497:     { "pst",   tZONE,     HOUR( 8) },  /* Pacific Standard */
                    498:     { "pdt",   tDAYZONE,  HOUR( 8) },  /* Pacific Daylight */
                    499:     { "yst",   tZONE,     HOUR( 9) },  /* Yukon Standard */
                    500:     { "ydt",   tDAYZONE,  HOUR( 9) },  /* Yukon Daylight */
                    501:     { "hst",   tZONE,     HOUR(10) },  /* Hawaii Standard */
                    502:     { "hdt",   tDAYZONE,  HOUR(10) },  /* Hawaii Daylight */
                    503:     { "cat",   tZONE,     HOUR(10) },  /* Central Alaska */
                    504:     { "ahst",  tZONE,     HOUR(10) },  /* Alaska-Hawaii Standard */
                    505:     { "nt",    tZONE,     HOUR(11) },  /* Nome */
                    506:     { "idlw",  tZONE,     HOUR(12) },  /* International Date Line West */
                    507:     { "cet",   tZONE,     -HOUR(1) },  /* Central European */
                    508:     { "met",   tZONE,     -HOUR(1) },  /* Middle European */
                    509:     { "mewt",  tZONE,     -HOUR(1) },  /* Middle European Winter */
                    510:     { "mest",  tDAYZONE,  -HOUR(1) },  /* Middle European Summer */
                    511:     { "swt",   tZONE,     -HOUR(1) },  /* Swedish Winter */
                    512:     { "sst",   tDAYZONE,  -HOUR(1) },  /* Swedish Summer */
                    513:     { "fwt",   tZONE,     -HOUR(1) },  /* French Winter */
                    514:     { "fst",   tDAYZONE,  -HOUR(1) },  /* French Summer */
                    515:     { "eet",   tZONE,     -HOUR(2) },  /* Eastern Europe, USSR Zone 1 */
                    516:     { "bt",    tZONE,     -HOUR(3) },  /* Baghdad, USSR Zone 2 */
                    517: #if 0
                    518:     { "it",    tZONE,     -HOUR(3.5) },/* Iran */
                    519: #endif
                    520:     { "zp4",   tZONE,     -HOUR(4) },  /* USSR Zone 3 */
                    521:     { "zp5",   tZONE,     -HOUR(5) },  /* USSR Zone 4 */
                    522: #if 0
                    523:     { "ist",   tZONE,     -HOUR(5.5) },/* Indian Standard */
                    524: #endif
                    525:     { "zp6",   tZONE,     -HOUR(6) },  /* USSR Zone 5 */
                    526: #if    0
                    527:     /* For completeness.  NST is also Newfoundland Stanard, and SST is
                    528:      * also Swedish Summer. */
                    529:     { "nst",   tZONE,     -HOUR(6.5) },/* North Sumatra */
                    530:     { "sst",   tZONE,     -HOUR(7) },  /* South Sumatra, USSR Zone 6 */
                    531: #endif /* 0 */
                    532:     { "wast",  tZONE,     -HOUR(7) },  /* West Australian Standard */
                    533:     { "wadt",  tDAYZONE,  -HOUR(7) },  /* West Australian Daylight */
                    534: #if 0
                    535:     { "jt",    tZONE,     -HOUR(7.5) },/* Java (3pm in Cronusland!) */
                    536: #endif
                    537:     { "cct",   tZONE,     -HOUR(8) },  /* China Coast, USSR Zone 7 */
                    538:     { "jst",   tZONE,     -HOUR(9) },  /* Japan Standard, USSR Zone 8 */
                    539: #if 0
                    540:     { "cast",  tZONE,     -HOUR(9.5) },/* Central Australian Standard */
                    541:     { "cadt",  tDAYZONE,  -HOUR(9.5) },/* Central Australian Daylight */
                    542: #endif
                    543:     { "east",  tZONE,     -HOUR(10) }, /* Eastern Australian Standard */
                    544:     { "eadt",  tDAYZONE,  -HOUR(10) }, /* Eastern Australian Daylight */
                    545:     { "gst",   tZONE,     -HOUR(10) }, /* Guam Standard, USSR Zone 9 */
                    546:     { "nzt",   tZONE,     -HOUR(12) }, /* New Zealand */
                    547:     { "nzst",  tZONE,     -HOUR(12) }, /* New Zealand Standard */
                    548:     { "nzdt",  tDAYZONE,  -HOUR(12) }, /* New Zealand Daylight */
                    549:     { "idle",  tZONE,     -HOUR(12) }, /* International Date Line East */
                    550:     {  NULL,   0,          0 }
                    551: };
                    552:
                    553: /* Military timezone table. */
1.12      joerg     554: static const TABLE MilitaryTable[] = {
1.1       christos  555:     { "a",     tZONE,  HOUR(  1) },
                    556:     { "b",     tZONE,  HOUR(  2) },
                    557:     { "c",     tZONE,  HOUR(  3) },
                    558:     { "d",     tZONE,  HOUR(  4) },
                    559:     { "e",     tZONE,  HOUR(  5) },
                    560:     { "f",     tZONE,  HOUR(  6) },
                    561:     { "g",     tZONE,  HOUR(  7) },
                    562:     { "h",     tZONE,  HOUR(  8) },
                    563:     { "i",     tZONE,  HOUR(  9) },
                    564:     { "k",     tZONE,  HOUR( 10) },
                    565:     { "l",     tZONE,  HOUR( 11) },
                    566:     { "m",     tZONE,  HOUR( 12) },
                    567:     { "n",     tZONE,  HOUR(- 1) },
                    568:     { "o",     tZONE,  HOUR(- 2) },
                    569:     { "p",     tZONE,  HOUR(- 3) },
                    570:     { "q",     tZONE,  HOUR(- 4) },
                    571:     { "r",     tZONE,  HOUR(- 5) },
                    572:     { "s",     tZONE,  HOUR(- 6) },
                    573:     { "t",     tZONE,  HOUR(- 7) },
                    574:     { "u",     tZONE,  HOUR(- 8) },
                    575:     { "v",     tZONE,  HOUR(- 9) },
                    576:     { "w",     tZONE,  HOUR(-10) },
                    577:     { "x",     tZONE,  HOUR(-11) },
                    578:     { "y",     tZONE,  HOUR(-12) },
                    579:     { "z",     tZONE,  HOUR(  0) },
                    580:     { NULL,    0,      0 }
                    581: };
                    582:
                    583: 
                    584:
                    585:
                    586: /* ARGSUSED */
                    587: static int
1.9       christos  588: yyerror(struct dateinfo *param, const char **inp, const char *s __unused)
1.1       christos  589: {
                    590:   return 0;
                    591: }
                    592:
                    593:
1.16.6.1  snj       594: /* Adjust year from a value that might be abbreviated, to a full value.
                    595:  * e.g. convert 70 to 1970.
                    596:  * Input Year is either:
                    597:  *  - A negative number, which means to use its absolute value (why?)
                    598:  *  - A number from 0 to 99, which means a year from 1900 to 1999, or
                    599:  *  - The actual year (>=100).
                    600:  * Returns the full year. */
                    601: static time_t
                    602: AdjustYear(time_t Year)
                    603: {
                    604:     /* XXX Y2K */
                    605:     if (Year < 0)
                    606:        Year = -Year;
                    607:     if (Year < 70)
                    608:        Year += 2000;
                    609:     else if (Year < 100)
                    610:        Year += 1900;
                    611:     return Year;
                    612: }
                    613:
1.1       christos  614: static time_t
                    615: Convert(
1.11      apb       616:     time_t     Month,          /* month of year [1-12] */
                    617:     time_t     Day,            /* day of month [1-31] */
1.16.6.1  snj       618:     time_t     Year,           /* year, not abbreviated in any way */
1.11      apb       619:     time_t     Hours,          /* Hour of day [0-24] */
                    620:     time_t     Minutes,        /* Minute of hour [0-59] */
                    621:     time_t     Seconds,        /* Second of minute [0-60] */
1.16.6.4! snj       622:     time_t     Timezone,       /* Timezone as minutes east of UTC,
        !           623:                                 * or USE_LOCAL_TIME special case */
1.11      apb       624:     MERIDIAN   Meridian,       /* Hours are am/pm/24 hour clock */
                    625:     DSTMODE    DSTmode         /* DST on/off/maybe */
1.1       christos  626: )
                    627: {
1.13      apb       628:     struct tm tm = {.tm_sec = 0};
                    629:     time_t result;
1.1       christos  630:
1.11      apb       631:     tm.tm_sec = Seconds;
                    632:     tm.tm_min = Minutes;
                    633:     tm.tm_hour = Hours + (Meridian == MERpm ? 12 : 0);
                    634:     tm.tm_mday = Day;
                    635:     tm.tm_mon = Month - 1;
                    636:     tm.tm_year = Year - 1900;
                    637:     switch (DSTmode) {
                    638:     case DSTon:  tm.tm_isdst = 1; break;
                    639:     case DSToff: tm.tm_isdst = 0; break;
                    640:     default:     tm.tm_isdst = -1; break;
1.6       christos  641:     }
                    642:
1.16.6.4! snj       643:     if (Timezone == USE_LOCAL_TIME) {
        !           644:            result = mktime(&tm);
        !           645:     } else {
        !           646:            /* We rely on mktime_z(NULL, ...) working in UTC */
        !           647:            result = mktime_z(NULL, &tm);
        !           648:            result += Timezone * 60;
        !           649:     }
        !           650:
        !           651: #if PARSEDATE_DEBUG
        !           652:     fprintf(stderr, "%s(M=%jd D=%jd Y=%jd H=%jd M=%jd S=%jd Z=%jd"
        !           653:                    " mer=%d DST=%d)",
        !           654:        __func__,
        !           655:        (intmax_t)Month, (intmax_t)Day, (intmax_t)Year,
        !           656:        (intmax_t)Hours, (intmax_t)Minutes, (intmax_t)Seconds,
        !           657:        (intmax_t)Timezone, (int)Meridian, (int)DSTmode);
        !           658:     fprintf(stderr, " -> %jd", (intmax_t)result);
        !           659:     fprintf(stderr, " %s", ctime(&result));
        !           660: #endif
        !           661:
1.13      apb       662:     return result;
1.1       christos  663: }
                    664:
                    665:
                    666: static time_t
                    667: DSTcorrect(
                    668:     time_t     Start,
                    669:     time_t     Future
                    670: )
                    671: {
                    672:     time_t     StartDay;
                    673:     time_t     FutureDay;
1.6       christos  674:     struct tm  *tm;
                    675:
                    676:     if ((tm = localtime(&Start)) == NULL)
                    677:        return -1;
                    678:     StartDay = (tm->tm_hour + 1) % 24;
                    679:
                    680:     if ((tm = localtime(&Future)) == NULL)
                    681:        return -1;
                    682:     FutureDay = (tm->tm_hour + 1) % 24;
1.1       christos  683:
                    684:     return (Future - Start) + (StartDay - FutureDay) * 60L * 60L;
                    685: }
                    686:
                    687:
                    688: static time_t
                    689: RelativeDate(
                    690:     time_t     Start,
                    691:     time_t     DayOrdinal,
                    692:     time_t     DayNumber
                    693: )
                    694: {
                    695:     struct tm  *tm;
                    696:     time_t     now;
                    697:
                    698:     now = Start;
                    699:     tm = localtime(&now);
                    700:     now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7);
                    701:     now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1);
                    702:     return DSTcorrect(Start, now);
                    703: }
                    704:
                    705:
                    706: static time_t
                    707: RelativeMonth(
                    708:     time_t     Start,
1.9       christos  709:     time_t     RelMonth,
                    710:     time_t     Timezone
1.1       christos  711: )
                    712: {
                    713:     struct tm  *tm;
                    714:     time_t     Month;
                    715:     time_t     Year;
                    716:
                    717:     if (RelMonth == 0)
                    718:        return 0;
                    719:     tm = localtime(&Start);
1.6       christos  720:     if (tm == NULL)
                    721:        return -1;
1.1       christos  722:     Month = 12 * (tm->tm_year + 1900) + tm->tm_mon + RelMonth;
                    723:     Year = Month / 12;
                    724:     Month = Month % 12 + 1;
                    725:     return DSTcorrect(Start,
                    726:            Convert(Month, (time_t)tm->tm_mday, Year,
                    727:                (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec,
1.9       christos  728:                Timezone, MER24, DSTmaybe));
1.1       christos  729: }
                    730:
                    731:
                    732: static int
1.9       christos  733: LookupWord(YYSTYPE *yylval, char *buff)
1.1       christos  734: {
                    735:     register char      *p;
                    736:     register char      *q;
                    737:     register const TABLE       *tp;
                    738:     int                        i;
                    739:     int                        abbrev;
                    740:
                    741:     /* Make it lowercase. */
                    742:     for (p = buff; *p; p++)
                    743:        if (isupper((unsigned char)*p))
                    744:            *p = tolower((unsigned char)*p);
                    745:
                    746:     if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) {
1.9       christos  747:        yylval->Meridian = MERam;
1.1       christos  748:        return tMERIDIAN;
                    749:     }
                    750:     if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) {
1.9       christos  751:        yylval->Meridian = MERpm;
1.1       christos  752:        return tMERIDIAN;
                    753:     }
                    754:
                    755:     /* See if we have an abbreviation for a month. */
                    756:     if (strlen(buff) == 3)
                    757:        abbrev = 1;
                    758:     else if (strlen(buff) == 4 && buff[3] == '.') {
                    759:        abbrev = 1;
                    760:        buff[3] = '\0';
                    761:     }
                    762:     else
                    763:        abbrev = 0;
                    764:
                    765:     for (tp = MonthDayTable; tp->name; tp++) {
                    766:        if (abbrev) {
                    767:            if (strncmp(buff, tp->name, 3) == 0) {
1.9       christos  768:                yylval->Number = tp->value;
1.1       christos  769:                return tp->type;
                    770:            }
                    771:        }
                    772:        else if (strcmp(buff, tp->name) == 0) {
1.9       christos  773:            yylval->Number = tp->value;
1.1       christos  774:            return tp->type;
                    775:        }
                    776:     }
                    777:
                    778:     for (tp = TimezoneTable; tp->name; tp++)
                    779:        if (strcmp(buff, tp->name) == 0) {
1.9       christos  780:            yylval->Number = tp->value;
1.1       christos  781:            return tp->type;
                    782:        }
                    783:
                    784:     if (strcmp(buff, "dst") == 0)
                    785:        return tDST;
                    786:
                    787:     for (tp = UnitsTable; tp->name; tp++)
                    788:        if (strcmp(buff, tp->name) == 0) {
1.9       christos  789:            yylval->Number = tp->value;
1.1       christos  790:            return tp->type;
                    791:        }
                    792:
                    793:     /* Strip off any plural and try the units table again. */
                    794:     i = strlen(buff) - 1;
                    795:     if (buff[i] == 's') {
                    796:        buff[i] = '\0';
                    797:        for (tp = UnitsTable; tp->name; tp++)
                    798:            if (strcmp(buff, tp->name) == 0) {
1.9       christos  799:                yylval->Number = tp->value;
1.1       christos  800:                return tp->type;
                    801:            }
                    802:        buff[i] = 's';          /* Put back for "this" in OtherTable. */
                    803:     }
                    804:
                    805:     for (tp = OtherTable; tp->name; tp++)
                    806:        if (strcmp(buff, tp->name) == 0) {
1.9       christos  807:            yylval->Number = tp->value;
1.1       christos  808:            return tp->type;
                    809:        }
                    810:
                    811:     /* Military timezones. */
                    812:     if (buff[1] == '\0' && isalpha((unsigned char)*buff)) {
                    813:        for (tp = MilitaryTable; tp->name; tp++)
                    814:            if (strcmp(buff, tp->name) == 0) {
1.9       christos  815:                yylval->Number = tp->value;
1.1       christos  816:                return tp->type;
                    817:            }
                    818:     }
                    819:
                    820:     /* Drop out any periods and try the timezone table again. */
                    821:     for (i = 0, p = q = buff; *q; q++)
                    822:        if (*q != '.')
                    823:            *p++ = *q;
                    824:        else
                    825:            i++;
                    826:     *p = '\0';
                    827:     if (i)
                    828:        for (tp = TimezoneTable; tp->name; tp++)
                    829:            if (strcmp(buff, tp->name) == 0) {
1.9       christos  830:                yylval->Number = tp->value;
1.1       christos  831:                return tp->type;
                    832:            }
                    833:
                    834:     return tID;
                    835: }
                    836:
                    837:
                    838: static int
1.9       christos  839: yylex(YYSTYPE *yylval, const char **yyInput)
1.1       christos  840: {
                    841:     register char      c;
                    842:     register char      *p;
                    843:     char               buff[20];
                    844:     int                        Count;
                    845:     int                        sign;
1.9       christos  846:     const char         *inp = *yyInput;
1.1       christos  847:
                    848:     for ( ; ; ) {
1.9       christos  849:        while (isspace((unsigned char)*inp))
                    850:            inp++;
1.1       christos  851:
1.9       christos  852:        if (isdigit((unsigned char)(c = *inp)) || c == '-' || c == '+') {
1.1       christos  853:            if (c == '-' || c == '+') {
                    854:                sign = c == '-' ? -1 : 1;
1.9       christos  855:                if (!isdigit((unsigned char)*++inp))
1.1       christos  856:                    /* skip the '-' sign */
                    857:                    continue;
                    858:            }
                    859:            else
                    860:                sign = 0;
1.9       christos  861:            for (yylval->Number = 0; isdigit((unsigned char)(c = *inp++)); )
                    862:                yylval->Number = 10 * yylval->Number + c - '0';
1.1       christos  863:            if (sign < 0)
1.9       christos  864:                yylval->Number = -yylval->Number;
                    865:            *yyInput = --inp;
1.1       christos  866:            return sign ? tSNUMBER : tUNUMBER;
                    867:        }
                    868:        if (isalpha((unsigned char)c)) {
1.9       christos  869:            for (p = buff; isalpha((unsigned char)(c = *inp++)) || c == '.'; )
1.1       christos  870:                if (p < &buff[sizeof buff - 1])
                    871:                    *p++ = c;
                    872:            *p = '\0';
1.9       christos  873:            *yyInput = --inp;
                    874:            return LookupWord(yylval, buff);
1.1       christos  875:        }
1.5       tron      876:        if (c == '@') {
1.9       christos  877:            *yyInput = ++inp;
1.5       tron      878:            return AT_SIGN;
                    879:        }
1.9       christos  880:        if (c != '(') {
                    881:            *yyInput = ++inp;
                    882:            return c;
                    883:        }
1.1       christos  884:        Count = 0;
                    885:        do {
1.9       christos  886:            c = *inp++;
1.1       christos  887:            if (c == '\0')
                    888:                return c;
                    889:            if (c == '(')
                    890:                Count++;
                    891:            else if (c == ')')
                    892:                Count--;
                    893:        } while (Count > 0);
                    894:     }
                    895: }
                    896:
                    897: #define TM_YEAR_ORIGIN 1900
                    898:
                    899: time_t
                    900: parsedate(const char *p, const time_t *now, const int *zone)
                    901: {
1.16.6.4! snj       902:     struct tm          local, *tm;
1.1       christos  903:     time_t             nowt;
                    904:     int                        zonet;
                    905:     time_t             Start;
1.6       christos  906:     time_t             tod, rm;
1.9       christos  907:     struct dateinfo    param;
1.14      apb       908:     int                        saved_errno;
                    909:
                    910:     saved_errno = errno;
                    911:     errno = 0;
1.1       christos  912:
1.16.6.4! snj       913:     if (now == NULL) {
1.1       christos  914:         now = &nowt;
                    915:        (void)time(&nowt);
1.16.6.4! snj       916:     }
        !           917:     if (zone == NULL) {
        !           918:        zone = &zonet;
        !           919:        zonet = USE_LOCAL_TIME;
1.1       christos  920:        if ((tm = localtime_r(now, &local)) == NULL)
                    921:            return -1;
                    922:     } else {
1.16.6.4! snj       923:        /*
        !           924:         * Should use the specified zone, not localtime.
        !           925:         * Fake it using gmtime and arithmetic.
        !           926:         * This is good enough because we use only the year/month/day,
        !           927:         * not other fields of struct tm.
        !           928:         */
        !           929:        time_t fake = *now + (*zone * 60);
        !           930:        if ((tm = gmtime_r(&fake, &local)) == NULL)
1.1       christos  931:            return -1;
                    932:     }
1.9       christos  933:     param.yyYear = tm->tm_year + 1900;
                    934:     param.yyMonth = tm->tm_mon + 1;
                    935:     param.yyDay = tm->tm_mday;
                    936:     param.yyTimezone = *zone;
                    937:     param.yyDSTmode = DSTmaybe;
                    938:     param.yyHour = 0;
                    939:     param.yyMinutes = 0;
                    940:     param.yySeconds = 0;
                    941:     param.yyMeridian = MER24;
                    942:     param.yyRelSeconds = 0;
                    943:     param.yyRelMonth = 0;
                    944:     param.yyHaveDate = 0;
1.16.6.1  snj       945:     param.yyHaveFullYear = 0;
1.9       christos  946:     param.yyHaveDay = 0;
                    947:     param.yyHaveRel = 0;
                    948:     param.yyHaveTime = 0;
                    949:     param.yyHaveZone = 0;
                    950:
                    951:     if (yyparse(&param, &p) || param.yyHaveTime > 1 || param.yyHaveZone > 1 ||
1.14      apb       952:        param.yyHaveDate > 1 || param.yyHaveDay > 1) {
                    953:        errno = EINVAL;
1.9       christos  954:        return -1;
1.14      apb       955:     }
1.9       christos  956:
                    957:     if (param.yyHaveDate || param.yyHaveTime || param.yyHaveDay) {
1.16.6.1  snj       958:        if (! param.yyHaveFullYear) {
                    959:                param.yyYear = AdjustYear(param.yyYear);
                    960:                param.yyHaveFullYear = 1;
                    961:        }
1.9       christos  962:        Start = Convert(param.yyMonth, param.yyDay, param.yyYear, param.yyHour,
                    963:            param.yyMinutes, param.yySeconds, param.yyTimezone,
                    964:            param.yyMeridian, param.yyDSTmode);
1.14      apb       965:        if (Start == -1 && errno != 0)
1.1       christos  966:            return -1;
                    967:     }
                    968:     else {
                    969:        Start = *now;
1.9       christos  970:        if (!param.yyHaveRel)
1.1       christos  971:            Start -= ((tm->tm_hour * 60L + tm->tm_min) * 60L) + tm->tm_sec;
                    972:     }
                    973:
1.9       christos  974:     Start += param.yyRelSeconds;
                    975:     rm = RelativeMonth(Start, param.yyRelMonth, param.yyTimezone);
1.14      apb       976:     if (rm == -1 && errno != 0)
1.6       christos  977:        return -1;
                    978:     Start += rm;
1.1       christos  979:
1.9       christos  980:     if (param.yyHaveDay && !param.yyHaveDate) {
                    981:        tod = RelativeDate(Start, param.yyDayOrdinal, param.yyDayNumber);
1.1       christos  982:        Start += tod;
                    983:     }
                    984:
1.14      apb       985:     if (errno == 0)
                    986:        errno = saved_errno;
1.6       christos  987:     return Start;
1.1       christos  988: }
                    989:
                    990:
                    991: #if    defined(TEST)
                    992:
                    993: /* ARGSUSED */
                    994: int
1.14      apb       995: main(int ac, char *av[])
1.1       christos  996: {
                    997:     char       buff[128];
                    998:     time_t     d;
                    999:
                   1000:     (void)printf("Enter date, or blank line to exit.\n\t> ");
                   1001:     (void)fflush(stdout);
1.14      apb      1002:     while (fgets(buff, sizeof(buff), stdin) && buff[0] != '\n') {
                   1003:        errno = 0;
1.1       christos 1004:        d = parsedate(buff, NULL, NULL);
1.14      apb      1005:        if (d == -1 && errno != 0)
                   1006:            (void)printf("Bad format - couldn't convert: %s\n",
                   1007:                strerror(errno));
1.1       christos 1008:        else
1.14      apb      1009:            (void)printf("%jd\t%s", (intmax_t)d, ctime(&d));
1.1       christos 1010:        (void)printf("\t> ");
                   1011:        (void)fflush(stdout);
                   1012:     }
                   1013:     exit(0);
                   1014:     /* NOTREACHED */
                   1015: }
                   1016: #endif /* defined(TEST) */

CVSweb <webmaster@jp.NetBSD.org>