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