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