GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/rcs/date.y Lines: 90 222 40.5 %
Date: 2017-11-07 Branches: 43 170 25.3 %

Line Branch Exec Source
1
%{
2
/*	$OpenBSD: date.y,v 1.14 2016/08/26 09:02:54 guenther 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 <err.h>
19
#include <string.h>
20
#include <time.h>
21
22
#include "rcsprog.h"
23
24
#define YEAR_EPOCH	1970
25
#define YEAR_TMORIGIN	1900
26
#define HOUR(x)		((time_t)(x) * 60)
27
#define SECSPERDAY	(24L * 60L * 60L)
28
29
30
/* An entry in the lexical lookup table */
31
typedef struct _TABLE {
32
	char	*name;
33
	int	type;
34
	time_t	value;
35
} TABLE;
36
37
38
/*  Daylight-savings mode:  on, off, or not yet known. */
39
typedef enum _DSTMODE {
40
	DSTon, DSToff, DSTmaybe
41
} DSTMODE;
42
43
/*  Meridian:  am, pm, or 24-hour style. */
44
typedef enum _MERIDIAN {
45
	MERam, MERpm, MER24
46
} MERIDIAN;
47
48
49
/*
50
 *  Global variables.  We could get rid of most of these by using a good
51
 *  union as the yacc stack.  (This routine was originally written before
52
 *  yacc had the %union construct.)  Maybe someday; right now we only use
53
 *  the %union very rarely.
54
 */
55
static const char	*yyInput;
56
static DSTMODE	yyDSTmode;
57
static time_t	yyDayOrdinal;
58
static time_t	yyDayNumber;
59
static int	yyHaveDate;
60
static int	yyHaveDay;
61
static int	yyHaveRel;
62
static int	yyHaveTime;
63
static int	yyHaveZone;
64
static time_t	yyTimezone;
65
static time_t	yyDay;
66
static time_t	yyHour;
67
static time_t	yyMinutes;
68
static time_t	yyMonth;
69
static time_t	yySeconds;
70
static time_t	yyYear;
71
static MERIDIAN	yyMeridian;
72
static time_t	yyRelMonth;
73
static time_t	yyRelSeconds;
74
75
76
static int	yyerror(const char *);
77
static int	yylex(void);
78
static int	yyparse(void);
79
static int	lookup(char *);
80
81
%}
82
83
%union {
84
	time_t		Number;
85
	enum _MERIDIAN	Meridian;
86
}
87
88
%token	tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT
89
%token	tSEC_UNIT tSNUMBER tUNUMBER tZONE tDST
90
91
%type	<Number>	tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT
92
%type	<Number>	tSEC_UNIT tSNUMBER tUNUMBER tZONE
93
%type	<Meridian>	tMERIDIAN o_merid
94
95
%%
96
97
spec	: /* NULL */
98
	| spec item
99
	;
100
101
item	: time {
102
		yyHaveTime++;
103
	}
104
	| zone {
105
		yyHaveZone++;
106
	}
107
	| date {
108
		yyHaveDate++;
109
	}
110
	| day {
111
		yyHaveDay++;
112
	}
113
	| rel {
114
		yyHaveRel++;
115
	}
116
	| number
117
	;
118
119
time	: tUNUMBER tMERIDIAN {
120
		yyHour = $1;
121
		yyMinutes = 0;
122
		yySeconds = 0;
123
		yyMeridian = $2;
124
	}
125
	| tUNUMBER ':' tUNUMBER o_merid {
126
		yyHour = $1;
127
		yyMinutes = $3;
128
		yySeconds = 0;
129
		yyMeridian = $4;
130
	}
131
	| tUNUMBER ':' tUNUMBER tSNUMBER {
132
		yyHour = $1;
133
		yyMinutes = $3;
134
		yyMeridian = MER24;
135
		yyDSTmode = DSToff;
136
		yyTimezone = - ($4 % 100 + ($4 / 100) * 60);
137
	}
138
	| tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
139
		yyHour = $1;
140
		yyMinutes = $3;
141
		yySeconds = $5;
142
		yyMeridian = $6;
143
	}
144
	| tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER {
145
		yyHour = $1;
146
		yyMinutes = $3;
147
		yySeconds = $5;
148
		yyMeridian = MER24;
149
		yyDSTmode = DSToff;
150
		yyTimezone = - ($6 % 100 + ($6 / 100) * 60);
151
	}
152
	;
153
154
zone	: tZONE {
155
		yyTimezone = $1;
156
		yyDSTmode = DSToff;
157
	}
158
	| tDAYZONE {
159
		yyTimezone = $1;
160
		yyDSTmode = DSTon;
161
	}
162
	| tZONE tDST {
163
		yyTimezone = $1;
164
		yyDSTmode = DSTon;
165
	}
166
	;
167
168
day	: tDAY {
169
		yyDayOrdinal = 1;
170
		yyDayNumber = $1;
171
	}
172
	| tDAY ',' {
173
		yyDayOrdinal = 1;
174
		yyDayNumber = $1;
175
	}
176
	| tUNUMBER tDAY {
177
		yyDayOrdinal = $1;
178
		yyDayNumber = $2;
179
	}
180
	;
181
182
date	: tUNUMBER '/' tUNUMBER {
183
		yyMonth = $1;
184
		yyDay = $3;
185
	}
186
	| tUNUMBER '/' tUNUMBER '/' tUNUMBER {
187
		if ($1 >= 100) {
188
			yyYear = $1;
189
			yyMonth = $3;
190
			yyDay = $5;
191
		} else {
192
			yyMonth = $1;
193
			yyDay = $3;
194
			yyYear = $5;
195
		}
196
	}
197
	| tUNUMBER tSNUMBER tSNUMBER {
198
		/* ISO 8601 format.  yyyy-mm-dd.  */
199
		yyYear = $1;
200
		yyMonth = -$2;
201
		yyDay = -$3;
202
	}
203
	| tUNUMBER tMONTH tSNUMBER {
204
		/* e.g. 17-JUN-1992.  */
205
		yyDay = $1;
206
		yyMonth = $2;
207
		yyYear = -$3;
208
	}
209
	| tMONTH tUNUMBER {
210
		yyMonth = $1;
211
		yyDay = $2;
212
	}
213
	| tMONTH tUNUMBER ',' tUNUMBER {
214
		yyMonth = $1;
215
		yyDay = $2;
216
		yyYear = $4;
217
	}
218
	| tUNUMBER tMONTH {
219
		yyMonth = $2;
220
		yyDay = $1;
221
	}
222
	| tUNUMBER tMONTH tUNUMBER {
223
		yyMonth = $2;
224
		yyDay = $1;
225
		yyYear = $3;
226
	}
227
	;
228
229
rel	: relunit tAGO {
230
		yyRelSeconds = -yyRelSeconds;
231
		yyRelMonth = -yyRelMonth;
232
	}
233
	| relunit
234
	;
235
236
relunit	: tUNUMBER tMINUTE_UNIT {
237
		yyRelSeconds += $1 * $2 * 60L;
238
	}
239
	| tSNUMBER tMINUTE_UNIT {
240
		yyRelSeconds += $1 * $2 * 60L;
241
	}
242
	| tMINUTE_UNIT {
243
		yyRelSeconds += $1 * 60L;
244
	}
245
	| tSNUMBER tSEC_UNIT {
246
		yyRelSeconds += $1;
247
	}
248
	| tUNUMBER tSEC_UNIT {
249
		yyRelSeconds += $1;
250
	}
251
	| tSEC_UNIT {
252
		yyRelSeconds++;
253
	}
254
	| tSNUMBER tMONTH_UNIT {
255
		yyRelMonth += $1 * $2;
256
	}
257
	| tUNUMBER tMONTH_UNIT {
258
		yyRelMonth += $1 * $2;
259
	}
260
	| tMONTH_UNIT {
261
		yyRelMonth += $1;
262
	}
263
	;
264
265
number	: tUNUMBER {
266
		if (yyHaveTime && yyHaveDate && !yyHaveRel)
267
			yyYear = $1;
268
		else {
269
			if ($1 > 10000) {
270
				yyHaveDate++;
271
				yyDay= ($1)%100;
272
				yyMonth= ($1/100)%100;
273
				yyYear = $1/10000;
274
			} else {
275
				yyHaveTime++;
276
				if ($1 < 100) {
277
					yyHour = $1;
278
					yyMinutes = 0;
279
				} else {
280
					yyHour = $1 / 100;
281
					yyMinutes = $1 % 100;
282
				}
283
				yySeconds = 0;
284
				yyMeridian = MER24;
285
			}
286
		}
287
	}
288
	;
289
290
o_merid	: /* NULL */ {
291
		$$ = MER24;
292
	}
293
	| tMERIDIAN {
294
		$$ = $1;
295
	}
296
	;
297
298
%%
299
300
/* Month and day table. */
301
static TABLE const MonthDayTable[] = {
302
	{ "january",	tMONTH,	1 },
303
	{ "february",	tMONTH,	2 },
304
	{ "march",	tMONTH,	3 },
305
	{ "april",	tMONTH,	4 },
306
	{ "may",	tMONTH,	5 },
307
	{ "june",	tMONTH,	6 },
308
	{ "july",	tMONTH,	7 },
309
	{ "august",	tMONTH,	8 },
310
	{ "september",	tMONTH,	9 },
311
	{ "sept",	tMONTH,	9 },
312
	{ "october",	tMONTH,	10 },
313
	{ "november",	tMONTH,	11 },
314
	{ "december",	tMONTH,	12 },
315
	{ "sunday",	tDAY,	0 },
316
	{ "monday",	tDAY,	1 },
317
	{ "tuesday",	tDAY,	2 },
318
	{ "tues",	tDAY,	2 },
319
	{ "wednesday",	tDAY,	3 },
320
	{ "wednes",	tDAY,	3 },
321
	{ "thursday",	tDAY,	4 },
322
	{ "thur",	tDAY,	4 },
323
	{ "thurs",	tDAY,	4 },
324
	{ "friday",	tDAY,	5 },
325
	{ "saturday",	tDAY,	6 },
326
	{ NULL }
327
};
328
329
/* Time units table. */
330
static TABLE const UnitsTable[] = {
331
	{ "year",	tMONTH_UNIT,	12 },
332
	{ "month",	tMONTH_UNIT,	1 },
333
	{ "fortnight",	tMINUTE_UNIT,	14 * 24 * 60 },
334
	{ "week",	tMINUTE_UNIT,	7 * 24 * 60 },
335
	{ "day",	tMINUTE_UNIT,	1 * 24 * 60 },
336
	{ "hour",	tMINUTE_UNIT,	60 },
337
	{ "minute",	tMINUTE_UNIT,	1 },
338
	{ "min",	tMINUTE_UNIT,	1 },
339
	{ "second",	tSEC_UNIT,	1 },
340
	{ "sec",	tSEC_UNIT,	1 },
341
	{ NULL }
342
};
343
344
/* Assorted relative-time words. */
345
static TABLE const OtherTable[] = {
346
	{ "tomorrow",	tMINUTE_UNIT,	1 * 24 * 60 },
347
	{ "yesterday",	tMINUTE_UNIT,	-1 * 24 * 60 },
348
	{ "today",	tMINUTE_UNIT,	0 },
349
	{ "now",	tMINUTE_UNIT,	0 },
350
	{ "last",	tUNUMBER,	-1 },
351
	{ "this",	tMINUTE_UNIT,	0 },
352
	{ "next",	tUNUMBER,	2 },
353
	{ "first",	tUNUMBER,	1 },
354
/*  { "second",		tUNUMBER,	2 }, */
355
	{ "third",	tUNUMBER,	3 },
356
	{ "fourth",	tUNUMBER,	4 },
357
	{ "fifth",	tUNUMBER,	5 },
358
	{ "sixth",	tUNUMBER,	6 },
359
	{ "seventh",	tUNUMBER,	7 },
360
	{ "eighth",	tUNUMBER,	8 },
361
	{ "ninth",	tUNUMBER,	9 },
362
	{ "tenth",	tUNUMBER,	10 },
363
	{ "eleventh",	tUNUMBER,	11 },
364
	{ "twelfth",	tUNUMBER,	12 },
365
	{ "ago",	tAGO,	1 },
366
	{ NULL }
367
};
368
369
/* The timezone table. */
370
/* Some of these are commented out because a time_t can't store a float. */
371
static TABLE const TimezoneTable[] = {
372
	{ "gmt",	tZONE,     HOUR( 0) },	/* Greenwich Mean */
373
	{ "ut",		tZONE,     HOUR( 0) },	/* Universal (Coordinated) */
374
	{ "utc",	tZONE,     HOUR( 0) },
375
	{ "wet",	tZONE,     HOUR( 0) },	/* Western European */
376
	{ "bst",	tDAYZONE,  HOUR( 0) },	/* British Summer */
377
	{ "wat",	tZONE,     HOUR( 1) },	/* West Africa */
378
	{ "at",		tZONE,     HOUR( 2) },	/* Azores */
379
#if	0
380
	/* For completeness.  BST is also British Summer, and GST is
381
	 * also Guam Standard. */
382
	{ "bst",	tZONE,     HOUR( 3) },	/* Brazil Standard */
383
	{ "gst",	tZONE,     HOUR( 3) },	/* Greenland Standard */
384
#endif
385
#if 0
386
	{ "nft",	tZONE,     HOUR(3.5) },	/* Newfoundland */
387
	{ "nst",	tZONE,     HOUR(3.5) },	/* Newfoundland Standard */
388
	{ "ndt",	tDAYZONE,  HOUR(3.5) },	/* Newfoundland Daylight */
389
#endif
390
	{ "ast",	tZONE,     HOUR( 4) },	/* Atlantic Standard */
391
	{ "adt",	tDAYZONE,  HOUR( 4) },	/* Atlantic Daylight */
392
	{ "est",	tZONE,     HOUR( 5) },	/* Eastern Standard */
393
	{ "edt",	tDAYZONE,  HOUR( 5) },	/* Eastern Daylight */
394
	{ "cst",	tZONE,     HOUR( 6) },	/* Central Standard */
395
	{ "cdt",	tDAYZONE,  HOUR( 6) },	/* Central Daylight */
396
	{ "mst",	tZONE,     HOUR( 7) },	/* Mountain Standard */
397
	{ "mdt",	tDAYZONE,  HOUR( 7) },	/* Mountain Daylight */
398
	{ "pst",	tZONE,     HOUR( 8) },	/* Pacific Standard */
399
	{ "pdt",	tDAYZONE,  HOUR( 8) },	/* Pacific Daylight */
400
	{ "yst",	tZONE,     HOUR( 9) },	/* Yukon Standard */
401
	{ "ydt",	tDAYZONE,  HOUR( 9) },	/* Yukon Daylight */
402
	{ "hst",	tZONE,     HOUR(10) },	/* Hawaii Standard */
403
	{ "hdt",	tDAYZONE,  HOUR(10) },	/* Hawaii Daylight */
404
	{ "cat",	tZONE,     HOUR(10) },	/* Central Alaska */
405
	{ "ahst",	tZONE,     HOUR(10) },	/* Alaska-Hawaii Standard */
406
	{ "nt",		tZONE,     HOUR(11) },	/* Nome */
407
	{ "idlw",	tZONE,     HOUR(12) },	/* International Date Line West */
408
	{ "cet",	tZONE,     -HOUR(1) },	/* Central European */
409
	{ "met",	tZONE,     -HOUR(1) },	/* Middle European */
410
	{ "mewt",	tZONE,     -HOUR(1) },	/* Middle European Winter */
411
	{ "mest",	tDAYZONE,  -HOUR(1) },	/* Middle European Summer */
412
	{ "swt",	tZONE,     -HOUR(1) },	/* Swedish Winter */
413
	{ "sst",	tDAYZONE,  -HOUR(1) },	/* Swedish Summer */
414
	{ "fwt",	tZONE,     -HOUR(1) },	/* French Winter */
415
	{ "fst",	tDAYZONE,  -HOUR(1) },	/* French Summer */
416
	{ "eet",	tZONE,     -HOUR(2) },	/* Eastern Europe, USSR Zone 1 */
417
	{ "bt",		tZONE,     -HOUR(3) },	/* Baghdad, USSR Zone 2 */
418
#if 0
419
	{ "it",		tZONE,     -HOUR(3.5) },/* Iran */
420
#endif
421
	{ "zp4",	tZONE,     -HOUR(4) },	/* USSR Zone 3 */
422
	{ "zp5",	tZONE,     -HOUR(5) },	/* USSR Zone 4 */
423
#if 0
424
	{ "ist",	tZONE,     -HOUR(5.5) },/* Indian Standard */
425
#endif
426
	{ "zp6",	tZONE,     -HOUR(6) },	/* USSR Zone 5 */
427
#if	0
428
	/* For completeness.  NST is also Newfoundland Stanard, and SST is
429
	 * also Swedish Summer. */
430
	{ "nst",	tZONE,     -HOUR(6.5) },/* North Sumatra */
431
	{ "sst",	tZONE,     -HOUR(7) },	/* South Sumatra, USSR Zone 6 */
432
#endif	/* 0 */
433
	{ "wast",	tZONE,     -HOUR(7) },	/* West Australian Standard */
434
	{ "wadt",	tDAYZONE,  -HOUR(7) },	/* West Australian Daylight */
435
#if 0
436
	{ "jt",		tZONE,     -HOUR(7.5) },/* Java (3pm in Cronusland!) */
437
#endif
438
	{ "cct",	tZONE,     -HOUR(8) },	/* China Coast, USSR Zone 7 */
439
	{ "jst",	tZONE,     -HOUR(9) },	/* Japan Standard, USSR Zone 8 */
440
#if 0
441
	{ "cast",	tZONE,     -HOUR(9.5) },/* Central Australian Standard */
442
	{ "cadt",	tDAYZONE,  -HOUR(9.5) },/* Central Australian Daylight */
443
#endif
444
	{ "east",	tZONE,     -HOUR(10) },	/* Eastern Australian Standard */
445
	{ "eadt",	tDAYZONE,  -HOUR(10) },	/* Eastern Australian Daylight */
446
	{ "gst",	tZONE,     -HOUR(10) },	/* Guam Standard, USSR Zone 9 */
447
	{ "nzt",	tZONE,     -HOUR(12) },	/* New Zealand */
448
	{ "nzst",	tZONE,     -HOUR(12) },	/* New Zealand Standard */
449
	{ "nzdt",	tDAYZONE,  -HOUR(12) },	/* New Zealand Daylight */
450
	{ "idle",	tZONE,     -HOUR(12) },	/* International Date Line East */
451
	{  NULL  }
452
};
453
454
/* Military timezone table. */
455
static TABLE const MilitaryTable[] = {
456
	{ "a",	tZONE,	HOUR(  1) },
457
	{ "b",	tZONE,	HOUR(  2) },
458
	{ "c",	tZONE,	HOUR(  3) },
459
	{ "d",	tZONE,	HOUR(  4) },
460
	{ "e",	tZONE,	HOUR(  5) },
461
	{ "f",	tZONE,	HOUR(  6) },
462
	{ "g",	tZONE,	HOUR(  7) },
463
	{ "h",	tZONE,	HOUR(  8) },
464
	{ "i",	tZONE,	HOUR(  9) },
465
	{ "k",	tZONE,	HOUR( 10) },
466
	{ "l",	tZONE,	HOUR( 11) },
467
	{ "m",	tZONE,	HOUR( 12) },
468
	{ "n",	tZONE,	HOUR(- 1) },
469
	{ "o",	tZONE,	HOUR(- 2) },
470
	{ "p",	tZONE,	HOUR(- 3) },
471
	{ "q",	tZONE,	HOUR(- 4) },
472
	{ "r",	tZONE,	HOUR(- 5) },
473
	{ "s",	tZONE,	HOUR(- 6) },
474
	{ "t",	tZONE,	HOUR(- 7) },
475
	{ "u",	tZONE,	HOUR(- 8) },
476
	{ "v",	tZONE,	HOUR(- 9) },
477
	{ "w",	tZONE,	HOUR(-10) },
478
	{ "x",	tZONE,	HOUR(-11) },
479
	{ "y",	tZONE,	HOUR(-12) },
480
	{ "z",	tZONE,	HOUR(  0) },
481
	{ NULL }
482
};
483
484
485
static int
486
yyerror(const char *s)
487
{
488
	char *str;
489
490
	if (isspace(yyInput[0]) || !isprint(yyInput[0]))
491
		(void)xasprintf(&str,
492
		    "%s: unexpected char 0x%02x in date string", s, yyInput[0]);
493
	else
494
		(void)xasprintf(&str, "%s: unexpected %s in date string",
495
		    s, yyInput);
496
497
	warnx("%s", str);
498
	free(str);
499
	return (0);
500
}
501
502
503
static time_t
504
ToSeconds(time_t Hours, time_t Minutes, time_t	Seconds, MERIDIAN Meridian)
505
{
506
8
	if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59)
507
		return (-1);
508
509

4
	switch (Meridian) {
510
	case MER24:
511
4
		if (Hours < 0 || Hours > 23)
512
			return (-1);
513
4
		return (Hours * 60L + Minutes) * 60L + Seconds;
514
	case MERam:
515
		if (Hours < 1 || Hours > 12)
516
			return (-1);
517
		if (Hours == 12)
518
			Hours = 0;
519
		return (Hours * 60L + Minutes) * 60L + Seconds;
520
	case MERpm:
521
		if (Hours < 1 || Hours > 12)
522
			return (-1);
523
		if (Hours == 12)
524
			Hours = 0;
525
		return ((Hours + 12) * 60L + Minutes) * 60L + Seconds;
526
	default:
527
		return (-1);
528
	}
529
	/* NOTREACHED */
530
4
}
531
532
533
/* Year is either
534
 * A negative number, which means to use its absolute value (why?)
535
 * A number from 0 to 99, which means a year from 1900 to 1999, or
536
 * The actual year (>=100).
537
 */
538
static time_t
539
Convert(time_t Month, time_t Day, time_t Year, time_t Hours, time_t Minutes,
540
    time_t Seconds, MERIDIAN Meridian, DSTMODE DSTmode)
541
{
542
	static int DaysInMonth[12] = {
543
		31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
544
	};
545
	time_t	tod;
546
8
	time_t	julian;
547
	int	i;
548
549
4
	if (Year < 0)
550
		Year = -Year;
551
4
	if (Year < 69)
552
		Year += 2000;
553
4
	else if (Year < 100) {
554
		Year += 1900;
555
		if (Year < YEAR_EPOCH)
556
			Year += 100;
557
	}
558

8
	DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0)
559
	    ? 29 : 28;
560
	/* XXX Sloppily check for 2038 if time_t is 32 bits */
561
8
	if (Year < YEAR_EPOCH ||
562
12
	    (sizeof(time_t) == sizeof(int) && Year > 2038) ||
563
8
	    Month < 1 || Month > 12 ||
564
	    /* Lint fluff:  "conversion from long may lose accuracy" */
565
8
	     Day < 1 || Day > DaysInMonth[(int)--Month])
566
		return (-1);
567
568
8
	for (julian = Day - 1, i = 0; i < Month; i++)
569
		julian += DaysInMonth[i];
570
571
326
	for (i = YEAR_EPOCH; i < Year; i++)
572
159
		julian += 365 + (i % 4 == 0);
573
4
	julian *= SECSPERDAY;
574
4
	julian += yyTimezone * 60L;
575
576
4
	if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0)
577
		return (-1);
578
4
	julian += tod;
579

4
	if ((DSTmode == DSTon) ||
580
4
	    (DSTmode == DSTmaybe && localtime(&julian)->tm_isdst))
581
	julian -= 60 * 60;
582
4
	return (julian);
583
4
}
584
585
586
static time_t
587
DSTcorrect(time_t Start, time_t Future)
588
{
589
	time_t	StartDay;
590
	time_t	FutureDay;
591
592
	StartDay = (localtime(&Start)->tm_hour + 1) % 24;
593
	FutureDay = (localtime(&Future)->tm_hour + 1) % 24;
594
	return (Future - Start) + (StartDay - FutureDay) * 60L * 60L;
595
}
596
597
598
static time_t
599
RelativeDate(time_t Start, time_t DayOrdinal, time_t DayNumber)
600
{
601
	struct tm	*tm;
602
	time_t	now;
603
604
	now = Start;
605
	tm = localtime(&now);
606
	now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7);
607
	now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1);
608
	return DSTcorrect(Start, now);
609
}
610
611
612
static time_t
613
RelativeMonth(time_t Start, time_t RelMonth)
614
{
615
	struct tm	*tm;
616
	time_t	Month;
617
	time_t	Year;
618
619
4
	if (RelMonth == 0)
620
4
		return (0);
621
	tm = localtime(&Start);
622
	Month = 12 * (tm->tm_year + 1900) + tm->tm_mon + RelMonth;
623
	Year = Month / 12;
624
	Month = Month % 12 + 1;
625
	return DSTcorrect(Start,
626
	    Convert(Month, (time_t)tm->tm_mday, Year,
627
	    (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec,
628
	    MER24, DSTmaybe));
629
4
}
630
631
632
static int
633
lookup(char *buff)
634
{
635
	size_t		len;
636
	char		*p, *q;
637
	int		i, abbrev;
638
	const TABLE	*tp;
639
640
	/* Make it lowercase. */
641
	for (p = buff; *p; p++)
642
		if (isupper(*p))
643
			*p = tolower(*p);
644
645
	if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) {
646
		yylval.Meridian = MERam;
647
		return (tMERIDIAN);
648
	}
649
	if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) {
650
		yylval.Meridian = MERpm;
651
		return (tMERIDIAN);
652
	}
653
654
	len = strlen(buff);
655
	/* See if we have an abbreviation for a month. */
656
	if (len == 3)
657
		abbrev = 1;
658
	else if (len == 4 && buff[3] == '.') {
659
		abbrev = 1;
660
		buff[3] = '\0';
661
		--len;
662
	} else
663
		abbrev = 0;
664
665
	for (tp = MonthDayTable; tp->name; tp++) {
666
		if (abbrev) {
667
			if (strncmp(buff, tp->name, 3) == 0) {
668
				yylval.Number = tp->value;
669
				return (tp->type);
670
			}
671
		} else if (strcmp(buff, tp->name) == 0) {
672
			yylval.Number = tp->value;
673
			return (tp->type);
674
		}
675
	}
676
677
	for (tp = TimezoneTable; tp->name; tp++)
678
		if (strcmp(buff, tp->name) == 0) {
679
			yylval.Number = tp->value;
680
			return (tp->type);
681
		}
682
683
	if (strcmp(buff, "dst") == 0)
684
		return (tDST);
685
686
	for (tp = UnitsTable; tp->name; tp++)
687
		if (strcmp(buff, tp->name) == 0) {
688
			yylval.Number = tp->value;
689
			return (tp->type);
690
		}
691
692
	/* Strip off any plural and try the units table again. */
693
	if (len != 0 && buff[len - 1] == 's') {
694
		buff[len - 1] = '\0';
695
		for (tp = UnitsTable; tp->name; tp++)
696
			if (strcmp(buff, tp->name) == 0) {
697
				yylval.Number = tp->value;
698
				return (tp->type);
699
			}
700
		buff[len - 1] = 's';	/* Put back for "this" in OtherTable. */
701
	}
702
703
	for (tp = OtherTable; tp->name; tp++)
704
		if (strcmp(buff, tp->name) == 0) {
705
			yylval.Number = tp->value;
706
			return (tp->type);
707
		}
708
709
	/* Military timezones. */
710
	if (len == 1 && isalpha(*buff)) {
711
		for (tp = MilitaryTable; tp->name; tp++)
712
			if (strcmp(buff, tp->name) == 0) {
713
				yylval.Number = tp->value;
714
				return (tp->type);
715
			}
716
	}
717
718
	/* Drop out any periods and try the timezone table again. */
719
	for (i = 0, p = q = buff; *q; q++)
720
		if (*q != '.')
721
			*p++ = *q;
722
		else
723
			i++;
724
	*p = '\0';
725
	if (i)
726
		for (tp = TimezoneTable; tp->name; tp++)
727
			if (strcmp(buff, tp->name) == 0) {
728
				yylval.Number = tp->value;
729
				return (tp->type);
730
			}
731
732
	return (tID);
733
}
734
735
736
static int
737
yylex(void)
738
{
739
80
	char	c, *p, buff[20];
740
	int	count, sign;
741
742
40
	for (;;) {
743
88
		while (isspace(*yyInput))
744
4
			yyInput++;
745
746

80
		if (isdigit(c = *yyInput) || c == '-' || c == '+') {
747

48
			if (c == '-' || c == '+') {
748
12
				sign = c == '-' ? -1 : 1;
749
12
				if (!isdigit(*++yyInput))
750
					/* skip the '-' sign */
751
					continue;
752
			}
753
			else
754
				sign = 0;
755
756
156
			for (yylval.Number = 0; isdigit(c = *yyInput++); )
757
64
				yylval.Number = 10 * yylval.Number + c - '0';
758
28
			yyInput--;
759
28
			if (sign < 0)
760
8
				yylval.Number = -yylval.Number;
761
28
			return sign ? tSNUMBER : tUNUMBER;
762
		}
763
764
12
		if (isalpha(c)) {
765
			for (p = buff; isalpha(c = *yyInput++) || c == '.'; )
766
				if (p < &buff[sizeof buff - 1])
767
					*p++ = c;
768
			*p = '\0';
769
			yyInput--;
770
			return lookup(buff);
771
		}
772
12
		if (c != '(')
773
12
			return *yyInput++;
774
775
		count = 0;
776
		do {
777
			c = *yyInput++;
778
			if (c == '\0')
779
				return (c);
780
			if (c == '(')
781
				count++;
782
			else if (c == ')')
783
				count--;
784
		} while (count > 0);
785
	}
786
40
}
787
788
/* Yield A - B, measured in seconds.  */
789
static long
790
difftm(struct tm *a, struct tm *b)
791
{
792
8
	int ay = a->tm_year + (YEAR_TMORIGIN - 1);
793
4
	int by = b->tm_year + (YEAR_TMORIGIN - 1);
794
4
	int days = (
795
	    /* difference in day of year */
796
8
	    a->tm_yday - b->tm_yday
797
	    /* + intervening leap days */
798
4
	    +  ((ay >> 2) - (by >> 2))
799
4
	    -  (ay/100 - by/100)
800
4
	    +  ((ay/100 >> 2) - (by/100 >> 2))
801
	    /* + difference in years * 365 */
802
4
	    +  (long)(ay-by) * 365);
803
12
	return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
804
8
	    + (a->tm_min - b->tm_min)) + (a->tm_sec - b->tm_sec));
805
}
806
807
/*
808
 * date_parse()
809
 *
810
 * Returns the number of seconds since the Epoch corresponding to the date.
811
 */
812
time_t
813
date_parse(const char *p)
814
{
815
8
	struct tm	gmt, tm;
816
4
	time_t		Start, tod, nowtime, tz;
817
818
4
	yyInput = p;
819
820

12
	if (time(&nowtime) == -1 || !gmtime_r(&nowtime, &gmt) ||
821
4
	    !localtime_r(&nowtime, &tm))
822
		return -1;
823
824
4
	tz = difftm(&gmt, &tm) / 60;
825
826
4
	if (tm.tm_isdst)
827
		tz += 60;
828
829
4
	yyYear = tm.tm_year + 1900;
830
4
	yyMonth = tm.tm_mon + 1;
831
4
	yyDay = tm.tm_mday;
832
4
	yyTimezone = tz;
833
4
	yyDSTmode = DSTmaybe;
834
4
	yyHour = 0;
835
4
	yyMinutes = 0;
836
4
	yySeconds = 0;
837
4
	yyMeridian = MER24;
838
4
	yyRelSeconds = 0;
839
4
	yyRelMonth = 0;
840
4
	yyHaveDate = 0;
841
4
	yyHaveDay = 0;
842
4
	yyHaveRel = 0;
843
4
	yyHaveTime = 0;
844
4
	yyHaveZone = 0;
845
846
12
	if (yyparse() || yyHaveTime > 1 || yyHaveZone > 1 ||
847
8
	    yyHaveDate > 1 || yyHaveDay > 1)
848
		return (-1);
849
850
4
	if (yyHaveDate || yyHaveTime || yyHaveDay) {
851
8
		Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes,
852
4
		    yySeconds, yyMeridian, yyDSTmode);
853
4
		if (Start < 0)
854
			return (-1);
855
	} else {
856
		Start = nowtime;
857
		if (!yyHaveRel)
858
			Start -= ((tm.tm_hour * 60L + tm.tm_min) * 60L) +
859
			    tm.tm_sec;
860
	}
861
862
4
	Start += yyRelSeconds;
863
4
	Start += RelativeMonth(Start, yyRelMonth);
864
865
4
	if (yyHaveDay && !yyHaveDate) {
866
		tod = RelativeDate(Start, yyDayOrdinal, yyDayNumber);
867
		Start += tod;
868
	}
869
870
4
	return Start;
871
4
}
872
873
#if defined(TEST)
874
/* ARGSUSED */
875
int
876
main(int argc, char **argv)
877
{
878
	char	buff[128];
879
	time_t	d;
880
881
	(void)printf("Enter date, or blank line to exit.\n\t> ");
882
	(void)fflush(stdout);
883
	while (fgets(buff, sizeof(buff), stdin) && buff[0]) {
884
		d = date_parse(buff);
885
		if (d == -1)
886
			(void)printf("Bad format - couldn't convert.\n");
887
		else
888
			(void)printf("%s", ctime(&d));
889
		(void)printf("\t> ");
890
		(void)fflush(stdout);
891
	}
892
893
	return (0);
894
}
895
#endif	/* defined(TEST) */