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