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