PageRenderTime 44ms CodeModel.GetById 27ms app.highlight 9ms RepoModel.GetById 1ms app.codeStats 1ms

/contrib/cvs/lib/getdate.y

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