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