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