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