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