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