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