getdate.y revision 94630
169587Sgreen%{ 269587Sgreen/* 369587Sgreen** Originally written by Steven M. Bellovin <smb@research.att.com> while 469587Sgreen** at the University of North Carolina at Chapel Hill. Later tweaked by 569587Sgreen** a couple of people on Usenet. Completely overhauled by Rich $alz 669587Sgreen** <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990; 769587Sgreen** 869587Sgreen** This grammar has 10 shift/reduce conflicts. 969587Sgreen** 1069587Sgreen** This code is in the public domain and has no copyright. 1169587Sgreen*/ 1269587Sgreen/* SUPPRESS 287 on yaccpar_sccsid *//* Unused static variable */ 1369587Sgreen/* SUPPRESS 288 on yyerrlab *//* Label unused */ 1469587Sgreen 1569587Sgreen/* $FreeBSD: head/usr.bin/find/getdate.y 94630 2002-04-14 01:30:20Z obrien $ */ 1669587Sgreen 1769587Sgreen#ifdef HAVE_CONFIG_H 1869587Sgreen#if defined (emacs) || defined (CONFIG_BROKETS) 1969587Sgreen#include <config.h> 2069587Sgreen#else 2169587Sgreen#include "config.h" 2269587Sgreen#endif 2369587Sgreen#endif 2469587Sgreen 2569587Sgreen/* Since the code of getdate.y is not included in the Emacs executable 26106121Sdes itself, there is no need to #define static in this file. Even if 2769587Sgreen the code were included in the Emacs executable, it probably 2869587Sgreen wouldn't do any harm to #undef it here; this will only cause 2969587Sgreen problems if we try to write to a static variable, which I don't 3069587Sgreen think this code needs to do. */ 3169587Sgreen#ifdef emacs 3269587Sgreen#undef static 3369587Sgreen#endif 3469587Sgreen 3576259Sgreen#include <stdio.h> 3669587Sgreen#include <ctype.h> 3769587Sgreen 3876259Sgreen/* The code at the top of get_date which figures out the offset of the 3976259Sgreen current time zone checks various CPP symbols to see if special 4076259Sgreen tricks are need, but defaults to using the gettimeofday system call. 4169587Sgreen Include <sys/time.h> if that will be used. */ 4292555Sdes 4369587Sgreen#if defined(vms) 4469587Sgreen# include <types.h> 4569587Sgreen#else /* defined(vms) */ 4669587Sgreen# include <sys/types.h> 4769587Sgreen/*# include "xtime.h"*/ 4869587Sgreen# include <sys/time.h> 4969587Sgreen# include <sys/timeb.h> 5069587Sgreen#endif /* !defined(vms) */ 5169587Sgreen 5269587Sgreen#if defined (STDC_HEADERS) || defined (USG) 53106121Sdes#include <string.h> 5469587Sgreen#endif 5569587Sgreen 5669587Sgreen/* Some old versions of bison generate parsers that use bcopy. 5769587Sgreen That loses on systems that don't provide the function, so we have 5869587Sgreen to redefine it here. */ 5969587Sgreen#if !defined (HAVE_BCOPY) && defined (HAVE_MEMCPY) && !defined (bcopy) 6069587Sgreen#define bcopy(from, to, len) memcpy ((to), (from), (len)) 6169587Sgreen#endif 6269587Sgreen 6369587Sgreen#if defined (STDC_HEADERS) 6469587Sgreen#include <stdlib.h> 6569587Sgreen#endif 6669587Sgreen 6769587Sgreen/* NOTES on rebuilding getdate.c (particularly for inclusion in CVS 6869587Sgreen releases): 6969587Sgreen 7069587Sgreen We don't want to mess with all the portability hassles of alloca. 7169587Sgreen In particular, most (all?) versions of bison will use alloca in 7276259Sgreen their parser. If bison works on your system (e.g. it should work 7376259Sgreen with gcc), then go ahead and use it, but the more general solution 7469587Sgreen is to use byacc instead of bison, which should generate a portable 7569587Sgreen parser. I played with adding "#define alloca dont_use_alloca", to 7669587Sgreen give an error if the parser generator uses alloca (and thus detect 7769587Sgreen unportable getdate.c's), but that seems to cause as many problems 7869587Sgreen as it solves. */ 7969587Sgreen 8069587Sgreenextern struct tm *gmtime(); 8192555Sdesextern struct tm *localtime(); 8292555Sdes 8392555Sdes#define yyparse getdate_yyparse 8492555Sdes#define yylex getdate_yylex 8576259Sgreen#define yyerror getdate_yyerror 8676259Sgreen 8769587Sgreenstatic int yyparse (); 8876259Sgreenstatic int yylex (); 8976259Sgreenstatic int yyerror (); 9076259Sgreen 9176259Sgreen#define EPOCH 1970 9276259Sgreen#define HOUR(x) ((time_t)(x) * 60) 9376259Sgreen#define SECSPERDAY (24L * 60L * 60L) 9469587Sgreen 9576259Sgreen 9676259Sgreen/* 9792555Sdes** An entry in the lexical lookup table. 9892555Sdes*/ 9969587Sgreentypedef struct _TABLE { 10076259Sgreen char *name; 10169587Sgreen int type; 10269587Sgreen time_t value; 10369587Sgreen} TABLE; 10469587Sgreen 10576259Sgreen 10669587Sgreen/* 10769587Sgreen** Daylight-savings mode: on, off, or not yet known. 10892555Sdes*/ 10969587Sgreentypedef enum _DSTMODE { 11069587Sgreen DSTon, DSToff, DSTmaybe 11169587Sgreen} DSTMODE; 11269587Sgreen 11392555Sdes/* 11492555Sdes** Meridian: am, pm, or 24-hour style. 11592555Sdes*/ 11669587Sgreentypedef enum _MERIDIAN { 11769587Sgreen MERam, MERpm, MER24 11869587Sgreen} MERIDIAN; 11969587Sgreen 12069587Sgreen 12169587Sgreen/* 12269587Sgreen** Global variables. We could get rid of most of these by using a good 12369587Sgreen** union as the yacc stack. (This routine was originally written before 12469587Sgreen** yacc had the %union construct.) Maybe someday; right now we only use 12592555Sdes** the %union very rarely. 12692555Sdes*/ 12769587Sgreenstatic char *yyInput; 12876259Sgreenstatic DSTMODE yyDSTmode; 12976259Sgreenstatic time_t yyDayOrdinal; 13076259Sgreenstatic time_t yyDayNumber; 13176259Sgreenstatic int yyHaveDate; 13276259Sgreenstatic int yyHaveDay; 13369587Sgreenstatic int yyHaveRel; 13469587Sgreenstatic int yyHaveTime; 13569587Sgreenstatic int yyHaveZone; 13669587Sgreenstatic time_t yyTimezone; 13769587Sgreenstatic time_t yyDay; 13869587Sgreenstatic time_t yyHour; 13992555Sdesstatic time_t yyMinutes; 14069587Sgreenstatic time_t yyMonth; 14169587Sgreenstatic time_t yySeconds; 14292555Sdesstatic time_t yyYear; 14376259Sgreenstatic MERIDIAN yyMeridian; 14476259Sgreenstatic time_t yyRelMonth; 14569587Sgreenstatic time_t yyRelSeconds; 14669587Sgreen 14769587Sgreen%} 14869587Sgreen 14969587Sgreen%union { 15069587Sgreen time_t Number; 15169587Sgreen enum _MERIDIAN Meridian; 15276259Sgreen} 15376259Sgreen 15476259Sgreen%token tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT 15592555Sdes%token tSEC_UNIT tSNUMBER tUNUMBER tZONE tDST 15692555Sdes 15769587Sgreen%type <Number> tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT 15869587Sgreen%type <Number> tSEC_UNIT tSNUMBER tUNUMBER tZONE 15969587Sgreen%type <Meridian> tMERIDIAN o_merid 16069587Sgreen 16169587Sgreen%% 16276259Sgreen 16376259Sgreenspec : /* NULL */ 16476259Sgreen | spec item 16569587Sgreen ; 16669587Sgreen 16769587Sgreenitem : time { 16876259Sgreen yyHaveTime++; 16976259Sgreen } 17076259Sgreen | zone { 17176259Sgreen yyHaveZone++; 17276259Sgreen } 17376259Sgreen | date { 17476259Sgreen yyHaveDate++; 17576259Sgreen } 17676259Sgreen | day { 17776259Sgreen yyHaveDay++; 17876259Sgreen } 17976259Sgreen | rel { 18076259Sgreen yyHaveRel++; 18176259Sgreen } 18276259Sgreen | number 18376259Sgreen ; 18476259Sgreen 18576259Sgreentime : tUNUMBER tMERIDIAN { 18676259Sgreen yyHour = $1; 18776259Sgreen yyMinutes = 0; 18876259Sgreen yySeconds = 0; 18976259Sgreen yyMeridian = $2; 19076259Sgreen } 19176259Sgreen | tUNUMBER ':' tUNUMBER o_merid { 19276259Sgreen yyHour = $1; 19376259Sgreen yyMinutes = $3; 19476259Sgreen yySeconds = 0; 19576259Sgreen yyMeridian = $4; 19676259Sgreen } 19776259Sgreen | tUNUMBER ':' tUNUMBER tSNUMBER { 19876259Sgreen yyHour = $1; 19976259Sgreen yyMinutes = $3; 20076259Sgreen yyMeridian = MER24; 20176259Sgreen yyDSTmode = DSToff; 20276259Sgreen yyTimezone = - ($4 % 100 + ($4 / 100) * 60); 20376259Sgreen } 20476259Sgreen | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid { 20576259Sgreen yyHour = $1; 20692555Sdes yyMinutes = $3; 20792555Sdes yySeconds = $5; 20876259Sgreen yyMeridian = $6; 20976259Sgreen } 21076259Sgreen | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER { 21176259Sgreen yyHour = $1; 21276259Sgreen yyMinutes = $3; 21376259Sgreen yySeconds = $5; 21476259Sgreen yyMeridian = MER24; 21576259Sgreen yyDSTmode = DSToff; 21676259Sgreen yyTimezone = - ($6 % 100 + ($6 / 100) * 60); 21776259Sgreen } 21876259Sgreen ; 21976259Sgreen 22076259Sgreenzone : tZONE { 22176259Sgreen yyTimezone = $1; 22276259Sgreen yyDSTmode = DSToff; 22376259Sgreen } 22476259Sgreen | tDAYZONE { 22576259Sgreen yyTimezone = $1; 22676259Sgreen yyDSTmode = DSTon; 22776259Sgreen } 22876259Sgreen | 22992555Sdes tZONE tDST { 23092555Sdes yyTimezone = $1; 23176259Sgreen yyDSTmode = DSTon; 23276259Sgreen } 23376259Sgreen ; 23476259Sgreen 23576259Sgreenday : tDAY { 23676259Sgreen yyDayOrdinal = 1; 23776259Sgreen yyDayNumber = $1; 23876259Sgreen } 23976259Sgreen | tDAY ',' { 24076259Sgreen yyDayOrdinal = 1; 24176259Sgreen yyDayNumber = $1; 24276259Sgreen } 24376259Sgreen | tUNUMBER tDAY { 24476259Sgreen yyDayOrdinal = $1; 24576259Sgreen yyDayNumber = $2; 24676259Sgreen } 24776259Sgreen ; 24876259Sgreen 24976259Sgreendate : tUNUMBER '/' tUNUMBER { 25092555Sdes yyMonth = $1; 25192555Sdes yyDay = $3; 25276259Sgreen } 25376259Sgreen | tUNUMBER '/' tUNUMBER '/' tUNUMBER { 25476259Sgreen if ($1 >= 100) { 25576259Sgreen yyYear = $1; 25676259Sgreen yyMonth = $3; 25776259Sgreen yyDay = $5; 25876259Sgreen } else { 25976259Sgreen yyMonth = $1; 26076259Sgreen yyDay = $3; 26176259Sgreen yyYear = $5; 26276259Sgreen } 26376259Sgreen } 26476259Sgreen | tUNUMBER tSNUMBER tSNUMBER { 26576259Sgreen /* ISO 8601 format. yyyy-mm-dd. */ 26676259Sgreen yyYear = $1; 26776259Sgreen yyMonth = -$2; 26876259Sgreen yyDay = -$3; 26976259Sgreen } 27076259Sgreen | tUNUMBER tMONTH tSNUMBER { 27176259Sgreen /* e.g. 17-JUN-1992. */ 27276259Sgreen yyDay = $1; 27376259Sgreen yyMonth = $2; 27476259Sgreen yyYear = -$3; 27576259Sgreen } 27676259Sgreen | tMONTH tUNUMBER { 27776259Sgreen yyMonth = $1; 27876259Sgreen yyDay = $2; 27976259Sgreen } 28076259Sgreen | tMONTH tUNUMBER ',' tUNUMBER { 28176259Sgreen yyMonth = $1; 28276259Sgreen yyDay = $2; 28376259Sgreen yyYear = $4; 28476259Sgreen } 28576259Sgreen | tUNUMBER tMONTH { 28676259Sgreen yyMonth = $2; 28776259Sgreen yyDay = $1; 28876259Sgreen } 28976259Sgreen | 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