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