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