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