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