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