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