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