GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/cvs/date.y Lines: 0 215 0.0 %
Date: 2017-11-13 Branches: 0 164 0.0 %

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