xref: /freebsd/contrib/libarchive/libarchive/archive_getdate.c (revision b9128a37faafede823eb456aa65a11ac69997284)
1fd082e96SMartin Matuska /*
2fd082e96SMartin Matuska  * This code is in the public domain and has no copyright.
3fd082e96SMartin Matuska  *
4fd082e96SMartin Matuska  * This is a plain C recursive-descent translation of an old
5fd082e96SMartin Matuska  * public-domain YACC grammar that has been used for parsing dates in
6fd082e96SMartin Matuska  * very many open-source projects.
7fd082e96SMartin Matuska  *
8fd082e96SMartin Matuska  * Since the original authors were generous enough to donate their
9fd082e96SMartin Matuska  * work to the public domain, I feel compelled to match their
10fd082e96SMartin Matuska  * generosity.
11fd082e96SMartin Matuska  *
12fd082e96SMartin Matuska  * Tim Kientzle, February 2009.
13fd082e96SMartin Matuska  */
14fd082e96SMartin Matuska 
15fd082e96SMartin Matuska /*
16fd082e96SMartin Matuska  * Header comment from original getdate.y:
17fd082e96SMartin Matuska  */
18fd082e96SMartin Matuska 
19fd082e96SMartin Matuska /*
20fd082e96SMartin Matuska **  Originally written by Steven M. Bellovin <smb@research.att.com> while
21fd082e96SMartin Matuska **  at the University of North Carolina at Chapel Hill.  Later tweaked by
22fd082e96SMartin Matuska **  a couple of people on Usenet.  Completely overhauled by Rich $alz
23fd082e96SMartin Matuska **  <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990;
24fd082e96SMartin Matuska **
25fd082e96SMartin Matuska **  This grammar has 10 shift/reduce conflicts.
26fd082e96SMartin Matuska **
27fd082e96SMartin Matuska **  This code is in the public domain and has no copyright.
28fd082e96SMartin Matuska */
29fd082e96SMartin Matuska 
30f9762417SMartin Matuska #include "archive_platform.h"
31fd082e96SMartin Matuska 
32fd082e96SMartin Matuska #include <ctype.h>
33fd082e96SMartin Matuska #include <stdio.h>
34fd082e96SMartin Matuska #include <stdlib.h>
35fd082e96SMartin Matuska #include <string.h>
36fd082e96SMartin Matuska #include <time.h>
37fd082e96SMartin Matuska 
38cdf63a70SMartin Matuska #define __LIBARCHIVE_BUILD 1
39cdf63a70SMartin Matuska #include "archive_getdate.h"
40fd082e96SMartin Matuska 
41fd082e96SMartin Matuska /* Basic time units. */
42fd082e96SMartin Matuska #define	EPOCH		1970
43fd082e96SMartin Matuska #define	MINUTE		(60L)
44fd082e96SMartin Matuska #define	HOUR		(60L * MINUTE)
45fd082e96SMartin Matuska #define	DAY		(24L * HOUR)
46fd082e96SMartin Matuska 
47fd082e96SMartin Matuska /* Daylight-savings mode:  on, off, or not yet known. */
48fd082e96SMartin Matuska enum DSTMODE { DSTon, DSToff, DSTmaybe };
49fd082e96SMartin Matuska /* Meridian:  am or pm. */
50fd082e96SMartin Matuska enum { tAM, tPM };
51fd082e96SMartin Matuska /* Token types returned by nexttoken() */
52fd082e96SMartin Matuska enum { tAGO = 260, tDAY, tDAYZONE, tAMPM, tMONTH, tMONTH_UNIT, tSEC_UNIT,
53fd082e96SMartin Matuska        tUNUMBER, tZONE, tDST };
54fd082e96SMartin Matuska struct token { int token; time_t value; };
55fd082e96SMartin Matuska 
56fd082e96SMartin Matuska /*
57fd082e96SMartin Matuska  * Parser state.
58fd082e96SMartin Matuska  */
59fd082e96SMartin Matuska struct gdstate {
60fd082e96SMartin Matuska 	struct token *tokenp; /* Pointer to next token. */
61fd082e96SMartin Matuska 	/* HaveXxxx counts how many of this kind of phrase we've seen;
62fd082e96SMartin Matuska 	 * it's a fatal error to have more than one time, zone, day,
63fd082e96SMartin Matuska 	 * or date phrase. */
64fd082e96SMartin Matuska 	int	HaveYear;
65fd082e96SMartin Matuska 	int	HaveMonth;
66fd082e96SMartin Matuska 	int	HaveDay;
67fd082e96SMartin Matuska 	int	HaveWeekDay; /* Day of week */
68fd082e96SMartin Matuska 	int	HaveTime; /* Hour/minute/second */
69fd082e96SMartin Matuska 	int	HaveZone; /* timezone and/or DST info */
70fd082e96SMartin Matuska 	int	HaveRel; /* time offset; we can have more than one */
71fd082e96SMartin Matuska 	/* Absolute time values. */
72fd082e96SMartin Matuska 	time_t	Timezone;  /* Seconds offset from GMT */
73fd082e96SMartin Matuska 	time_t	Day;
74fd082e96SMartin Matuska 	time_t	Hour;
75fd082e96SMartin Matuska 	time_t	Minutes;
76fd082e96SMartin Matuska 	time_t	Month;
77fd082e96SMartin Matuska 	time_t	Seconds;
78fd082e96SMartin Matuska 	time_t	Year;
79fd082e96SMartin Matuska 	/* DST selection */
80fd082e96SMartin Matuska 	enum DSTMODE	DSTmode;
81fd082e96SMartin Matuska 	/* Day of week accounting, e.g., "3rd Tuesday" */
82fd082e96SMartin Matuska 	time_t	DayOrdinal; /* "3" in "3rd Tuesday" */
83fd082e96SMartin Matuska 	time_t	DayNumber; /* "Tuesday" in "3rd Tuesday" */
84fd082e96SMartin Matuska 	/* Relative time values: hour/day/week offsets are measured in
85fd082e96SMartin Matuska 	 * seconds, month/year are counted in months. */
86fd082e96SMartin Matuska 	time_t	RelMonth;
87fd082e96SMartin Matuska 	time_t	RelSeconds;
88fd082e96SMartin Matuska };
89fd082e96SMartin Matuska 
90fd082e96SMartin Matuska /*
91fd082e96SMartin Matuska  * A series of functions that recognize certain common time phrases.
92fd082e96SMartin Matuska  * Each function returns 1 if it managed to make sense of some of the
93fd082e96SMartin Matuska  * tokens, zero otherwise.
94fd082e96SMartin Matuska  */
95fd082e96SMartin Matuska 
96fd082e96SMartin Matuska /*
97fd082e96SMartin Matuska  *  hour:minute or hour:minute:second with optional AM, PM, or numeric
98fd082e96SMartin Matuska  *  timezone offset
99fd082e96SMartin Matuska  */
100fd082e96SMartin Matuska static int
101fd082e96SMartin Matuska timephrase(struct gdstate *gds)
102fd082e96SMartin Matuska {
103fd082e96SMartin Matuska 	if (gds->tokenp[0].token == tUNUMBER
104fd082e96SMartin Matuska 	    && gds->tokenp[1].token == ':'
105fd082e96SMartin Matuska 	    && gds->tokenp[2].token == tUNUMBER
106fd082e96SMartin Matuska 	    && gds->tokenp[3].token == ':'
107fd082e96SMartin Matuska 	    && gds->tokenp[4].token == tUNUMBER) {
108fd082e96SMartin Matuska 		/* "12:14:18" or "22:08:07" */
109fd082e96SMartin Matuska 		++gds->HaveTime;
110fd082e96SMartin Matuska 		gds->Hour = gds->tokenp[0].value;
111fd082e96SMartin Matuska 		gds->Minutes = gds->tokenp[2].value;
112fd082e96SMartin Matuska 		gds->Seconds = gds->tokenp[4].value;
113fd082e96SMartin Matuska 		gds->tokenp += 5;
114fd082e96SMartin Matuska 	}
115fd082e96SMartin Matuska 	else if (gds->tokenp[0].token == tUNUMBER
116fd082e96SMartin Matuska 	    && gds->tokenp[1].token == ':'
117fd082e96SMartin Matuska 	    && gds->tokenp[2].token == tUNUMBER) {
118fd082e96SMartin Matuska 		/* "12:14" or "22:08" */
119fd082e96SMartin Matuska 		++gds->HaveTime;
120fd082e96SMartin Matuska 		gds->Hour = gds->tokenp[0].value;
121fd082e96SMartin Matuska 		gds->Minutes = gds->tokenp[2].value;
122fd082e96SMartin Matuska 		gds->Seconds = 0;
123fd082e96SMartin Matuska 		gds->tokenp += 3;
124fd082e96SMartin Matuska 	}
125fd082e96SMartin Matuska 	else if (gds->tokenp[0].token == tUNUMBER
126fd082e96SMartin Matuska 	    && gds->tokenp[1].token == tAMPM) {
127fd082e96SMartin Matuska 		/* "7" is a time if it's followed by "am" or "pm" */
128fd082e96SMartin Matuska 		++gds->HaveTime;
129fd082e96SMartin Matuska 		gds->Hour = gds->tokenp[0].value;
130fd082e96SMartin Matuska 		gds->Minutes = gds->Seconds = 0;
131fd082e96SMartin Matuska 		/* We'll handle the AM/PM below. */
132fd082e96SMartin Matuska 		gds->tokenp += 1;
133fd082e96SMartin Matuska 	} else {
134fd082e96SMartin Matuska 		/* We can't handle this. */
135fd082e96SMartin Matuska 		return 0;
136fd082e96SMartin Matuska 	}
137fd082e96SMartin Matuska 
138fd082e96SMartin Matuska 	if (gds->tokenp[0].token == tAMPM) {
139fd082e96SMartin Matuska 		/* "7:12pm", "12:20:13am" */
140fd082e96SMartin Matuska 		if (gds->Hour == 12)
141fd082e96SMartin Matuska 			gds->Hour = 0;
142fd082e96SMartin Matuska 		if (gds->tokenp[0].value == tPM)
143fd082e96SMartin Matuska 			gds->Hour += 12;
144fd082e96SMartin Matuska 		gds->tokenp += 1;
145fd082e96SMartin Matuska 	}
146fd082e96SMartin Matuska 	if (gds->tokenp[0].token == '+'
147fd082e96SMartin Matuska 	    && gds->tokenp[1].token == tUNUMBER) {
148fd082e96SMartin Matuska 		/* "7:14+0700" */
149fd082e96SMartin Matuska 		gds->HaveZone++;
150fd082e96SMartin Matuska 		gds->DSTmode = DSToff;
151fd082e96SMartin Matuska 		gds->Timezone = - ((gds->tokenp[1].value / 100) * HOUR
152fd082e96SMartin Matuska 		    + (gds->tokenp[1].value % 100) * MINUTE);
153fd082e96SMartin Matuska 		gds->tokenp += 2;
154fd082e96SMartin Matuska 	}
155fd082e96SMartin Matuska 	if (gds->tokenp[0].token == '-'
156fd082e96SMartin Matuska 	    && gds->tokenp[1].token == tUNUMBER) {
157fd082e96SMartin Matuska 		/* "19:14:12-0530" */
158fd082e96SMartin Matuska 		gds->HaveZone++;
159fd082e96SMartin Matuska 		gds->DSTmode = DSToff;
160fd082e96SMartin Matuska 		gds->Timezone = + ((gds->tokenp[1].value / 100) * HOUR
161fd082e96SMartin Matuska 		    + (gds->tokenp[1].value % 100) * MINUTE);
162fd082e96SMartin Matuska 		gds->tokenp += 2;
163fd082e96SMartin Matuska 	}
164fd082e96SMartin Matuska 	return 1;
165fd082e96SMartin Matuska }
166fd082e96SMartin Matuska 
167fd082e96SMartin Matuska /*
168fd082e96SMartin Matuska  * Timezone name, possibly including DST.
169fd082e96SMartin Matuska  */
170fd082e96SMartin Matuska static int
171fd082e96SMartin Matuska zonephrase(struct gdstate *gds)
172fd082e96SMartin Matuska {
173fd082e96SMartin Matuska 	if (gds->tokenp[0].token == tZONE
174fd082e96SMartin Matuska 	    && gds->tokenp[1].token == tDST) {
175fd082e96SMartin Matuska 		gds->HaveZone++;
176fd082e96SMartin Matuska 		gds->Timezone = gds->tokenp[0].value;
177fd082e96SMartin Matuska 		gds->DSTmode = DSTon;
178fd082e96SMartin Matuska 		gds->tokenp += 1;
179fd082e96SMartin Matuska 		return 1;
180fd082e96SMartin Matuska 	}
181fd082e96SMartin Matuska 
182fd082e96SMartin Matuska 	if (gds->tokenp[0].token == tZONE) {
183fd082e96SMartin Matuska 		gds->HaveZone++;
184fd082e96SMartin Matuska 		gds->Timezone = gds->tokenp[0].value;
185fd082e96SMartin Matuska 		gds->DSTmode = DSToff;
186fd082e96SMartin Matuska 		gds->tokenp += 1;
187fd082e96SMartin Matuska 		return 1;
188fd082e96SMartin Matuska 	}
189fd082e96SMartin Matuska 
190fd082e96SMartin Matuska 	if (gds->tokenp[0].token == tDAYZONE) {
191fd082e96SMartin Matuska 		gds->HaveZone++;
192fd082e96SMartin Matuska 		gds->Timezone = gds->tokenp[0].value;
193fd082e96SMartin Matuska 		gds->DSTmode = DSTon;
194fd082e96SMartin Matuska 		gds->tokenp += 1;
195fd082e96SMartin Matuska 		return 1;
196fd082e96SMartin Matuska 	}
197fd082e96SMartin Matuska 	return 0;
198fd082e96SMartin Matuska }
199fd082e96SMartin Matuska 
200fd082e96SMartin Matuska /*
201fd082e96SMartin Matuska  * Year/month/day in various combinations.
202fd082e96SMartin Matuska  */
203fd082e96SMartin Matuska static int
204fd082e96SMartin Matuska datephrase(struct gdstate *gds)
205fd082e96SMartin Matuska {
206fd082e96SMartin Matuska 	if (gds->tokenp[0].token == tUNUMBER
207fd082e96SMartin Matuska 	    && gds->tokenp[1].token == '/'
208fd082e96SMartin Matuska 	    && gds->tokenp[2].token == tUNUMBER
209fd082e96SMartin Matuska 	    && gds->tokenp[3].token == '/'
210fd082e96SMartin Matuska 	    && gds->tokenp[4].token == tUNUMBER) {
211fd082e96SMartin Matuska 		gds->HaveYear++;
212fd082e96SMartin Matuska 		gds->HaveMonth++;
213fd082e96SMartin Matuska 		gds->HaveDay++;
214fd082e96SMartin Matuska 		if (gds->tokenp[0].value >= 13) {
215fd082e96SMartin Matuska 			/* First number is big:  2004/01/29, 99/02/17 */
216fd082e96SMartin Matuska 			gds->Year = gds->tokenp[0].value;
217fd082e96SMartin Matuska 			gds->Month = gds->tokenp[2].value;
218fd082e96SMartin Matuska 			gds->Day = gds->tokenp[4].value;
219fd082e96SMartin Matuska 		} else if ((gds->tokenp[4].value >= 13)
220fd082e96SMartin Matuska 		    || (gds->tokenp[2].value >= 13)) {
221fd082e96SMartin Matuska 			/* Last number is big:  01/07/98 */
222fd082e96SMartin Matuska 			/* Middle number is big:  01/29/04 */
223fd082e96SMartin Matuska 			gds->Month = gds->tokenp[0].value;
224fd082e96SMartin Matuska 			gds->Day = gds->tokenp[2].value;
225fd082e96SMartin Matuska 			gds->Year = gds->tokenp[4].value;
226fd082e96SMartin Matuska 		} else {
227fd082e96SMartin Matuska 			/* No significant clues: 02/03/04 */
228fd082e96SMartin Matuska 			gds->Month = gds->tokenp[0].value;
229fd082e96SMartin Matuska 			gds->Day = gds->tokenp[2].value;
230fd082e96SMartin Matuska 			gds->Year = gds->tokenp[4].value;
231fd082e96SMartin Matuska 		}
232fd082e96SMartin Matuska 		gds->tokenp += 5;
233fd082e96SMartin Matuska 		return 1;
234fd082e96SMartin Matuska 	}
235fd082e96SMartin Matuska 
236fd082e96SMartin Matuska 	if (gds->tokenp[0].token == tUNUMBER
237fd082e96SMartin Matuska 	    && gds->tokenp[1].token == '/'
238fd082e96SMartin Matuska 	    && gds->tokenp[2].token == tUNUMBER) {
239fd082e96SMartin Matuska 		/* "1/15" */
240fd082e96SMartin Matuska 		gds->HaveMonth++;
241fd082e96SMartin Matuska 		gds->HaveDay++;
242fd082e96SMartin Matuska 		gds->Month = gds->tokenp[0].value;
243fd082e96SMartin Matuska 		gds->Day = gds->tokenp[2].value;
244fd082e96SMartin Matuska 		gds->tokenp += 3;
245fd082e96SMartin Matuska 		return 1;
246fd082e96SMartin Matuska 	}
247fd082e96SMartin Matuska 
248fd082e96SMartin Matuska 	if (gds->tokenp[0].token == tUNUMBER
249fd082e96SMartin Matuska 	    && gds->tokenp[1].token == '-'
250fd082e96SMartin Matuska 	    && gds->tokenp[2].token == tUNUMBER
251fd082e96SMartin Matuska 	    && gds->tokenp[3].token == '-'
252fd082e96SMartin Matuska 	    && gds->tokenp[4].token == tUNUMBER) {
253fd082e96SMartin Matuska 		/* ISO 8601 format.  yyyy-mm-dd.  */
254fd082e96SMartin Matuska 		gds->HaveYear++;
255fd082e96SMartin Matuska 		gds->HaveMonth++;
256fd082e96SMartin Matuska 		gds->HaveDay++;
257fd082e96SMartin Matuska 		gds->Year = gds->tokenp[0].value;
258fd082e96SMartin Matuska 		gds->Month = gds->tokenp[2].value;
259fd082e96SMartin Matuska 		gds->Day = gds->tokenp[4].value;
260fd082e96SMartin Matuska 		gds->tokenp += 5;
261fd082e96SMartin Matuska 		return 1;
262fd082e96SMartin Matuska 	}
263fd082e96SMartin Matuska 
264fd082e96SMartin Matuska 	if (gds->tokenp[0].token == tUNUMBER
265fd082e96SMartin Matuska 	    && gds->tokenp[1].token == '-'
266fd082e96SMartin Matuska 	    && gds->tokenp[2].token == tMONTH
267fd082e96SMartin Matuska 	    && gds->tokenp[3].token == '-'
268fd082e96SMartin Matuska 	    && gds->tokenp[4].token == tUNUMBER) {
269fd082e96SMartin Matuska 		gds->HaveYear++;
270fd082e96SMartin Matuska 		gds->HaveMonth++;
271fd082e96SMartin Matuska 		gds->HaveDay++;
272fd082e96SMartin Matuska 		if (gds->tokenp[0].value > 31) {
273fd082e96SMartin Matuska 			/* e.g. 1992-Jun-17 */
274fd082e96SMartin Matuska 			gds->Year = gds->tokenp[0].value;
275fd082e96SMartin Matuska 			gds->Month = gds->tokenp[2].value;
276fd082e96SMartin Matuska 			gds->Day = gds->tokenp[4].value;
277fd082e96SMartin Matuska 		} else {
278fd082e96SMartin Matuska 			/* e.g. 17-JUN-1992.  */
279fd082e96SMartin Matuska 			gds->Day = gds->tokenp[0].value;
280fd082e96SMartin Matuska 			gds->Month = gds->tokenp[2].value;
281fd082e96SMartin Matuska 			gds->Year = gds->tokenp[4].value;
282fd082e96SMartin Matuska 		}
283fd082e96SMartin Matuska 		gds->tokenp += 5;
284fd082e96SMartin Matuska 		return 1;
285fd082e96SMartin Matuska 	}
286fd082e96SMartin Matuska 
287fd082e96SMartin Matuska 	if (gds->tokenp[0].token == tMONTH
288fd082e96SMartin Matuska 	    && gds->tokenp[1].token == tUNUMBER
289fd082e96SMartin Matuska 	    && gds->tokenp[2].token == ','
290fd082e96SMartin Matuska 	    && gds->tokenp[3].token == tUNUMBER) {
291fd082e96SMartin Matuska 		/* "June 17, 2001" */
292fd082e96SMartin Matuska 		gds->HaveYear++;
293fd082e96SMartin Matuska 		gds->HaveMonth++;
294fd082e96SMartin Matuska 		gds->HaveDay++;
295fd082e96SMartin Matuska 		gds->Month = gds->tokenp[0].value;
296fd082e96SMartin Matuska 		gds->Day = gds->tokenp[1].value;
297fd082e96SMartin Matuska 		gds->Year = gds->tokenp[3].value;
298fd082e96SMartin Matuska 		gds->tokenp += 4;
299fd082e96SMartin Matuska 		return 1;
300fd082e96SMartin Matuska 	}
301fd082e96SMartin Matuska 
302fd082e96SMartin Matuska 	if (gds->tokenp[0].token == tMONTH
303fd082e96SMartin Matuska 	    && gds->tokenp[1].token == tUNUMBER) {
304fd082e96SMartin Matuska 		/* "May 3" */
305fd082e96SMartin Matuska 		gds->HaveMonth++;
306fd082e96SMartin Matuska 		gds->HaveDay++;
307fd082e96SMartin Matuska 		gds->Month = gds->tokenp[0].value;
308fd082e96SMartin Matuska 		gds->Day = gds->tokenp[1].value;
309fd082e96SMartin Matuska 		gds->tokenp += 2;
310fd082e96SMartin Matuska 		return 1;
311fd082e96SMartin Matuska 	}
312fd082e96SMartin Matuska 
313fd082e96SMartin Matuska 	if (gds->tokenp[0].token == tUNUMBER
314fd082e96SMartin Matuska 	    && gds->tokenp[1].token == tMONTH
315fd082e96SMartin Matuska 	    && gds->tokenp[2].token == tUNUMBER) {
316fd082e96SMartin Matuska 		/* "12 Sept 1997" */
317fd082e96SMartin Matuska 		gds->HaveYear++;
318fd082e96SMartin Matuska 		gds->HaveMonth++;
319fd082e96SMartin Matuska 		gds->HaveDay++;
320fd082e96SMartin Matuska 		gds->Day = gds->tokenp[0].value;
321fd082e96SMartin Matuska 		gds->Month = gds->tokenp[1].value;
322fd082e96SMartin Matuska 		gds->Year = gds->tokenp[2].value;
323fd082e96SMartin Matuska 		gds->tokenp += 3;
324fd082e96SMartin Matuska 		return 1;
325fd082e96SMartin Matuska 	}
326fd082e96SMartin Matuska 
327fd082e96SMartin Matuska 	if (gds->tokenp[0].token == tUNUMBER
328fd082e96SMartin Matuska 	    && gds->tokenp[1].token == tMONTH) {
329fd082e96SMartin Matuska 		/* "12 Sept" */
330fd082e96SMartin Matuska 		gds->HaveMonth++;
331fd082e96SMartin Matuska 		gds->HaveDay++;
332fd082e96SMartin Matuska 		gds->Day = gds->tokenp[0].value;
333fd082e96SMartin Matuska 		gds->Month = gds->tokenp[1].value;
334fd082e96SMartin Matuska 		gds->tokenp += 2;
335fd082e96SMartin Matuska 		return 1;
336fd082e96SMartin Matuska 	}
337fd082e96SMartin Matuska 
338fd082e96SMartin Matuska 	return 0;
339fd082e96SMartin Matuska }
340fd082e96SMartin Matuska 
341fd082e96SMartin Matuska /*
342fd082e96SMartin Matuska  * Relative time phrase: "tomorrow", "yesterday", "+1 hour", etc.
343fd082e96SMartin Matuska  */
344fd082e96SMartin Matuska static int
345fd082e96SMartin Matuska relunitphrase(struct gdstate *gds)
346fd082e96SMartin Matuska {
347fd082e96SMartin Matuska 	if (gds->tokenp[0].token == '-'
348fd082e96SMartin Matuska 	    && gds->tokenp[1].token == tUNUMBER
349fd082e96SMartin Matuska 	    && gds->tokenp[2].token == tSEC_UNIT) {
350fd082e96SMartin Matuska 		/* "-3 hours" */
351fd082e96SMartin Matuska 		gds->HaveRel++;
352fd082e96SMartin Matuska 		gds->RelSeconds -= gds->tokenp[1].value * gds->tokenp[2].value;
353fd082e96SMartin Matuska 		gds->tokenp += 3;
354fd082e96SMartin Matuska 		return 1;
355fd082e96SMartin Matuska 	}
356fd082e96SMartin Matuska 	if (gds->tokenp[0].token == '+'
357fd082e96SMartin Matuska 	    && gds->tokenp[1].token == tUNUMBER
358fd082e96SMartin Matuska 	    && gds->tokenp[2].token == tSEC_UNIT) {
359fd082e96SMartin Matuska 		/* "+1 minute" */
360fd082e96SMartin Matuska 		gds->HaveRel++;
361fd082e96SMartin Matuska 		gds->RelSeconds += gds->tokenp[1].value * gds->tokenp[2].value;
362fd082e96SMartin Matuska 		gds->tokenp += 3;
363fd082e96SMartin Matuska 		return 1;
364fd082e96SMartin Matuska 	}
365fd082e96SMartin Matuska 	if (gds->tokenp[0].token == tUNUMBER
366fd082e96SMartin Matuska 	    && gds->tokenp[1].token == tSEC_UNIT) {
367fd082e96SMartin Matuska 		/* "1 day" */
368fd082e96SMartin Matuska 		gds->HaveRel++;
3693fd25813STim Kientzle 		gds->RelSeconds += gds->tokenp[0].value * gds->tokenp[1].value;
3703fd25813STim Kientzle 		gds->tokenp += 2;
371fd082e96SMartin Matuska 		return 1;
372fd082e96SMartin Matuska 	}
373fd082e96SMartin Matuska 	if (gds->tokenp[0].token == '-'
374fd082e96SMartin Matuska 	    && gds->tokenp[1].token == tUNUMBER
375fd082e96SMartin Matuska 	    && gds->tokenp[2].token == tMONTH_UNIT) {
376fd082e96SMartin Matuska 		/* "-3 months" */
377fd082e96SMartin Matuska 		gds->HaveRel++;
378fd082e96SMartin Matuska 		gds->RelMonth -= gds->tokenp[1].value * gds->tokenp[2].value;
379fd082e96SMartin Matuska 		gds->tokenp += 3;
380fd082e96SMartin Matuska 		return 1;
381fd082e96SMartin Matuska 	}
382fd082e96SMartin Matuska 	if (gds->tokenp[0].token == '+'
383fd082e96SMartin Matuska 	    && gds->tokenp[1].token == tUNUMBER
384fd082e96SMartin Matuska 	    && gds->tokenp[2].token == tMONTH_UNIT) {
385fd082e96SMartin Matuska 		/* "+5 years" */
386fd082e96SMartin Matuska 		gds->HaveRel++;
387fd082e96SMartin Matuska 		gds->RelMonth += gds->tokenp[1].value * gds->tokenp[2].value;
388fd082e96SMartin Matuska 		gds->tokenp += 3;
389fd082e96SMartin Matuska 		return 1;
390fd082e96SMartin Matuska 	}
391fd082e96SMartin Matuska 	if (gds->tokenp[0].token == tUNUMBER
392fd082e96SMartin Matuska 	    && gds->tokenp[1].token == tMONTH_UNIT) {
393fd082e96SMartin Matuska 		/* "2 years" */
394fd082e96SMartin Matuska 		gds->HaveRel++;
395fd082e96SMartin Matuska 		gds->RelMonth += gds->tokenp[0].value * gds->tokenp[1].value;
396fd082e96SMartin Matuska 		gds->tokenp += 2;
397fd082e96SMartin Matuska 		return 1;
398fd082e96SMartin Matuska 	}
399fd082e96SMartin Matuska 	if (gds->tokenp[0].token == tSEC_UNIT) {
400fd082e96SMartin Matuska 		/* "now", "tomorrow" */
401fd082e96SMartin Matuska 		gds->HaveRel++;
402fd082e96SMartin Matuska 		gds->RelSeconds += gds->tokenp[0].value;
4033fd25813STim Kientzle 		gds->tokenp += 1;
404fd082e96SMartin Matuska 		return 1;
405fd082e96SMartin Matuska 	}
406fd082e96SMartin Matuska 	if (gds->tokenp[0].token == tMONTH_UNIT) {
407fd082e96SMartin Matuska 		/* "month" */
408fd082e96SMartin Matuska 		gds->HaveRel++;
409fd082e96SMartin Matuska 		gds->RelMonth += gds->tokenp[0].value;
410fd082e96SMartin Matuska 		gds->tokenp += 1;
411fd082e96SMartin Matuska 		return 1;
412fd082e96SMartin Matuska 	}
413fd082e96SMartin Matuska 	return 0;
414fd082e96SMartin Matuska }
415fd082e96SMartin Matuska 
416fd082e96SMartin Matuska /*
417fd082e96SMartin Matuska  * Day of the week specification.
418fd082e96SMartin Matuska  */
419fd082e96SMartin Matuska static int
420fd082e96SMartin Matuska dayphrase(struct gdstate *gds)
421fd082e96SMartin Matuska {
422fd082e96SMartin Matuska 	if (gds->tokenp[0].token == tDAY) {
423fd082e96SMartin Matuska 		/* "tues", "wednesday," */
424fd082e96SMartin Matuska 		gds->HaveWeekDay++;
425fd082e96SMartin Matuska 		gds->DayOrdinal = 1;
426fd082e96SMartin Matuska 		gds->DayNumber = gds->tokenp[0].value;
427fd082e96SMartin Matuska 		gds->tokenp += 1;
428fd082e96SMartin Matuska 		if (gds->tokenp[0].token == ',')
429fd082e96SMartin Matuska 			gds->tokenp += 1;
430fd082e96SMartin Matuska 		return 1;
431fd082e96SMartin Matuska 	}
432fd082e96SMartin Matuska 	if (gds->tokenp[0].token == tUNUMBER
433fd082e96SMartin Matuska 		&& gds->tokenp[1].token == tDAY) {
434fd082e96SMartin Matuska 		/* "second tues" "3 wed" */
435fd082e96SMartin Matuska 		gds->HaveWeekDay++;
436fd082e96SMartin Matuska 		gds->DayOrdinal = gds->tokenp[0].value;
437fd082e96SMartin Matuska 		gds->DayNumber = gds->tokenp[1].value;
438fd082e96SMartin Matuska 		gds->tokenp += 2;
439fd082e96SMartin Matuska 		return 1;
440fd082e96SMartin Matuska 	}
441fd082e96SMartin Matuska 	return 0;
442fd082e96SMartin Matuska }
443fd082e96SMartin Matuska 
444fd082e96SMartin Matuska /*
445fd082e96SMartin Matuska  * Try to match a phrase using one of the above functions.
446fd082e96SMartin Matuska  * This layer also deals with a couple of generic issues.
447fd082e96SMartin Matuska  */
448fd082e96SMartin Matuska static int
449fd082e96SMartin Matuska phrase(struct gdstate *gds)
450fd082e96SMartin Matuska {
451fd082e96SMartin Matuska 	if (timephrase(gds))
452fd082e96SMartin Matuska 		return 1;
453fd082e96SMartin Matuska 	if (zonephrase(gds))
454fd082e96SMartin Matuska 		return 1;
455fd082e96SMartin Matuska 	if (datephrase(gds))
456fd082e96SMartin Matuska 		return 1;
457fd082e96SMartin Matuska 	if (dayphrase(gds))
458fd082e96SMartin Matuska 		return 1;
459fd082e96SMartin Matuska 	if (relunitphrase(gds)) {
460fd082e96SMartin Matuska 		if (gds->tokenp[0].token == tAGO) {
461fd082e96SMartin Matuska 			gds->RelSeconds = -gds->RelSeconds;
462fd082e96SMartin Matuska 			gds->RelMonth = -gds->RelMonth;
463fd082e96SMartin Matuska 			gds->tokenp += 1;
464fd082e96SMartin Matuska 		}
465fd082e96SMartin Matuska 		return 1;
466fd082e96SMartin Matuska 	}
467fd082e96SMartin Matuska 
468fd082e96SMartin Matuska 	/* Bare numbers sometimes have meaning. */
469fd082e96SMartin Matuska 	if (gds->tokenp[0].token == tUNUMBER) {
470fd082e96SMartin Matuska 		if (gds->HaveTime && !gds->HaveYear && !gds->HaveRel) {
471fd082e96SMartin Matuska 			gds->HaveYear++;
472fd082e96SMartin Matuska 			gds->Year = gds->tokenp[0].value;
473fd082e96SMartin Matuska 			gds->tokenp += 1;
474fd082e96SMartin Matuska 			return 1;
475fd082e96SMartin Matuska 		}
476fd082e96SMartin Matuska 
477fd082e96SMartin Matuska 		if(gds->tokenp[0].value > 10000) {
478fd082e96SMartin Matuska 			/* "20040301" */
479fd082e96SMartin Matuska 			gds->HaveYear++;
480fd082e96SMartin Matuska 			gds->HaveMonth++;
481fd082e96SMartin Matuska 			gds->HaveDay++;
482fd082e96SMartin Matuska 			gds->Day= (gds->tokenp[0].value)%100;
483fd082e96SMartin Matuska 			gds->Month= (gds->tokenp[0].value/100)%100;
484fd082e96SMartin Matuska 			gds->Year = gds->tokenp[0].value/10000;
485fd082e96SMartin Matuska 			gds->tokenp += 1;
486fd082e96SMartin Matuska 			return 1;
487fd082e96SMartin Matuska 		}
488fd082e96SMartin Matuska 
489fd082e96SMartin Matuska 		if (gds->tokenp[0].value < 24) {
490fd082e96SMartin Matuska 			gds->HaveTime++;
491fd082e96SMartin Matuska 			gds->Hour = gds->tokenp[0].value;
492fd082e96SMartin Matuska 			gds->Minutes = 0;
493fd082e96SMartin Matuska 			gds->Seconds = 0;
494fd082e96SMartin Matuska 			gds->tokenp += 1;
495fd082e96SMartin Matuska 			return 1;
496fd082e96SMartin Matuska 		}
497fd082e96SMartin Matuska 
498fd082e96SMartin Matuska 		if ((gds->tokenp[0].value / 100 < 24)
499fd082e96SMartin Matuska 		    && (gds->tokenp[0].value % 100 < 60)) {
500fd082e96SMartin Matuska 			/* "513" is same as "5:13" */
501fd082e96SMartin Matuska 			gds->Hour = gds->tokenp[0].value / 100;
502fd082e96SMartin Matuska 			gds->Minutes = gds->tokenp[0].value % 100;
503fd082e96SMartin Matuska 			gds->Seconds = 0;
504fd082e96SMartin Matuska 			gds->tokenp += 1;
505fd082e96SMartin Matuska 			return 1;
506fd082e96SMartin Matuska 		}
507fd082e96SMartin Matuska 	}
508fd082e96SMartin Matuska 
509fd082e96SMartin Matuska 	return 0;
510fd082e96SMartin Matuska }
511fd082e96SMartin Matuska 
512fd082e96SMartin Matuska /*
513fd082e96SMartin Matuska  * A dictionary of time words.
514fd082e96SMartin Matuska  */
515fd082e96SMartin Matuska static struct LEXICON {
516fd082e96SMartin Matuska 	size_t		abbrev;
517fd082e96SMartin Matuska 	const char	*name;
518fd082e96SMartin Matuska 	int		type;
519fd082e96SMartin Matuska 	time_t		value;
520fd082e96SMartin Matuska } const TimeWords[] = {
521fd082e96SMartin Matuska 	/* am/pm */
522fd082e96SMartin Matuska 	{ 0, "am",		tAMPM,	tAM },
523fd082e96SMartin Matuska 	{ 0, "pm",		tAMPM,	tPM },
524fd082e96SMartin Matuska 
525fd082e96SMartin Matuska 	/* Month names. */
526fd082e96SMartin Matuska 	{ 3, "january",		tMONTH,  1 },
527fd082e96SMartin Matuska 	{ 3, "february",	tMONTH,  2 },
528fd082e96SMartin Matuska 	{ 3, "march",		tMONTH,  3 },
529fd082e96SMartin Matuska 	{ 3, "april",		tMONTH,  4 },
530fd082e96SMartin Matuska 	{ 3, "may",		tMONTH,  5 },
531fd082e96SMartin Matuska 	{ 3, "june",		tMONTH,  6 },
532fd082e96SMartin Matuska 	{ 3, "july",		tMONTH,  7 },
533fd082e96SMartin Matuska 	{ 3, "august",		tMONTH,  8 },
534fd082e96SMartin Matuska 	{ 3, "september",	tMONTH,  9 },
535fd082e96SMartin Matuska 	{ 3, "october",		tMONTH, 10 },
536fd082e96SMartin Matuska 	{ 3, "november",	tMONTH, 11 },
537fd082e96SMartin Matuska 	{ 3, "december",	tMONTH, 12 },
538fd082e96SMartin Matuska 
539fd082e96SMartin Matuska 	/* Days of the week. */
540fd082e96SMartin Matuska 	{ 2, "sunday",		tDAY, 0 },
541fd082e96SMartin Matuska 	{ 3, "monday",		tDAY, 1 },
542fd082e96SMartin Matuska 	{ 2, "tuesday",		tDAY, 2 },
543fd082e96SMartin Matuska 	{ 3, "wednesday",	tDAY, 3 },
544fd082e96SMartin Matuska 	{ 2, "thursday",	tDAY, 4 },
545fd082e96SMartin Matuska 	{ 2, "friday",		tDAY, 5 },
546fd082e96SMartin Matuska 	{ 2, "saturday",	tDAY, 6 },
547fd082e96SMartin Matuska 
548fd082e96SMartin Matuska 	/* Timezones: Offsets are in seconds. */
549fd082e96SMartin Matuska 	{ 0, "gmt",  tZONE,     0*HOUR }, /* Greenwich Mean */
550fd082e96SMartin Matuska 	{ 0, "ut",   tZONE,     0*HOUR }, /* Universal (Coordinated) */
551fd082e96SMartin Matuska 	{ 0, "utc",  tZONE,     0*HOUR },
552fd082e96SMartin Matuska 	{ 0, "wet",  tZONE,     0*HOUR }, /* Western European */
553fd082e96SMartin Matuska 	{ 0, "bst",  tDAYZONE,  0*HOUR }, /* British Summer */
554fd082e96SMartin Matuska 	{ 0, "wat",  tZONE,     1*HOUR }, /* West Africa */
555fd082e96SMartin Matuska 	{ 0, "at",   tZONE,     2*HOUR }, /* Azores */
556fd082e96SMartin Matuska 	/* { 0, "bst", tZONE, 3*HOUR }, */ /* Brazil Standard: Conflict */
557fd082e96SMartin Matuska 	/* { 0, "gst", tZONE, 3*HOUR }, */ /* Greenland Standard: Conflict*/
558fd082e96SMartin Matuska 	{ 0, "nft",  tZONE,     3*HOUR+30*MINUTE }, /* Newfoundland */
559fd082e96SMartin Matuska 	{ 0, "nst",  tZONE,     3*HOUR+30*MINUTE }, /* Newfoundland Standard */
560fd082e96SMartin Matuska 	{ 0, "ndt",  tDAYZONE,  3*HOUR+30*MINUTE }, /* Newfoundland Daylight */
561fd082e96SMartin Matuska 	{ 0, "ast",  tZONE,     4*HOUR }, /* Atlantic Standard */
562fd082e96SMartin Matuska 	{ 0, "adt",  tDAYZONE,  4*HOUR }, /* Atlantic Daylight */
563fd082e96SMartin Matuska 	{ 0, "est",  tZONE,     5*HOUR }, /* Eastern Standard */
564fd082e96SMartin Matuska 	{ 0, "edt",  tDAYZONE,  5*HOUR }, /* Eastern Daylight */
565fd082e96SMartin Matuska 	{ 0, "cst",  tZONE,     6*HOUR }, /* Central Standard */
566fd082e96SMartin Matuska 	{ 0, "cdt",  tDAYZONE,  6*HOUR }, /* Central Daylight */
567fd082e96SMartin Matuska 	{ 0, "mst",  tZONE,     7*HOUR }, /* Mountain Standard */
568fd082e96SMartin Matuska 	{ 0, "mdt",  tDAYZONE,  7*HOUR }, /* Mountain Daylight */
569fd082e96SMartin Matuska 	{ 0, "pst",  tZONE,     8*HOUR }, /* Pacific Standard */
570fd082e96SMartin Matuska 	{ 0, "pdt",  tDAYZONE,  8*HOUR }, /* Pacific Daylight */
571fd082e96SMartin Matuska 	{ 0, "yst",  tZONE,     9*HOUR }, /* Yukon Standard */
572fd082e96SMartin Matuska 	{ 0, "ydt",  tDAYZONE,  9*HOUR }, /* Yukon Daylight */
573fd082e96SMartin Matuska 	{ 0, "hst",  tZONE,     10*HOUR }, /* Hawaii Standard */
574fd082e96SMartin Matuska 	{ 0, "hdt",  tDAYZONE,  10*HOUR }, /* Hawaii Daylight */
575fd082e96SMartin Matuska 	{ 0, "cat",  tZONE,     10*HOUR }, /* Central Alaska */
576fd082e96SMartin Matuska 	{ 0, "ahst", tZONE,     10*HOUR }, /* Alaska-Hawaii Standard */
577fd082e96SMartin Matuska 	{ 0, "nt",   tZONE,     11*HOUR }, /* Nome */
578fd082e96SMartin Matuska 	{ 0, "idlw", tZONE,     12*HOUR }, /* Intl Date Line West */
579fd082e96SMartin Matuska 	{ 0, "cet",  tZONE,     -1*HOUR }, /* Central European */
580fd082e96SMartin Matuska 	{ 0, "met",  tZONE,     -1*HOUR }, /* Middle European */
581fd082e96SMartin Matuska 	{ 0, "mewt", tZONE,     -1*HOUR }, /* Middle European Winter */
582fd082e96SMartin Matuska 	{ 0, "mest", tDAYZONE,  -1*HOUR }, /* Middle European Summer */
583fd082e96SMartin Matuska 	{ 0, "swt",  tZONE,     -1*HOUR }, /* Swedish Winter */
584fd082e96SMartin Matuska 	{ 0, "sst",  tDAYZONE,  -1*HOUR }, /* Swedish Summer */
585fd082e96SMartin Matuska 	{ 0, "fwt",  tZONE,     -1*HOUR }, /* French Winter */
586fd082e96SMartin Matuska 	{ 0, "fst",  tDAYZONE,  -1*HOUR }, /* French Summer */
587fd082e96SMartin Matuska 	{ 0, "eet",  tZONE,     -2*HOUR }, /* Eastern Eur, USSR Zone 1 */
588fd082e96SMartin Matuska 	{ 0, "bt",   tZONE,     -3*HOUR }, /* Baghdad, USSR Zone 2 */
589fd082e96SMartin Matuska 	{ 0, "it",   tZONE,     -3*HOUR-30*MINUTE },/* Iran */
590fd082e96SMartin Matuska 	{ 0, "zp4",  tZONE,     -4*HOUR }, /* USSR Zone 3 */
591fd082e96SMartin Matuska 	{ 0, "zp5",  tZONE,     -5*HOUR }, /* USSR Zone 4 */
592fd082e96SMartin Matuska 	{ 0, "ist",  tZONE,     -5*HOUR-30*MINUTE },/* Indian Standard */
593fd082e96SMartin Matuska 	{ 0, "zp6",  tZONE,     -6*HOUR }, /* USSR Zone 5 */
594fd082e96SMartin Matuska 	/* { 0, "nst",  tZONE, -6.5*HOUR }, */ /* North Sumatra: Conflict */
595fd082e96SMartin Matuska 	/* { 0, "sst", tZONE, -7*HOUR }, */ /* So Sumatra, USSR 6: Conflict */
596fd082e96SMartin Matuska 	{ 0, "wast", tZONE,     -7*HOUR }, /* West Australian Standard */
597fd082e96SMartin Matuska 	{ 0, "wadt", tDAYZONE,  -7*HOUR }, /* West Australian Daylight */
598fd082e96SMartin Matuska 	{ 0, "jt",   tZONE,     -7*HOUR-30*MINUTE },/* Java (3pm in Cronusland!)*/
599fd082e96SMartin Matuska 	{ 0, "cct",  tZONE,     -8*HOUR }, /* China Coast, USSR Zone 7 */
600fd082e96SMartin Matuska 	{ 0, "jst",  tZONE,     -9*HOUR }, /* Japan Std, USSR Zone 8 */
601fd082e96SMartin Matuska 	{ 0, "cast", tZONE,     -9*HOUR-30*MINUTE },/* Ctrl Australian Std */
602fd082e96SMartin Matuska 	{ 0, "cadt", tDAYZONE,  -9*HOUR-30*MINUTE },/* Ctrl Australian Daylt */
603fd082e96SMartin Matuska 	{ 0, "east", tZONE,     -10*HOUR }, /* Eastern Australian Std */
604fd082e96SMartin Matuska 	{ 0, "eadt", tDAYZONE,  -10*HOUR }, /* Eastern Australian Daylt */
605fd082e96SMartin Matuska 	{ 0, "gst",  tZONE,     -10*HOUR }, /* Guam Std, USSR Zone 9 */
606fd082e96SMartin Matuska 	{ 0, "nzt",  tZONE,     -12*HOUR }, /* New Zealand */
607fd082e96SMartin Matuska 	{ 0, "nzst", tZONE,     -12*HOUR }, /* New Zealand Standard */
608fd082e96SMartin Matuska 	{ 0, "nzdt", tDAYZONE,  -12*HOUR }, /* New Zealand Daylight */
609fd082e96SMartin Matuska 	{ 0, "idle", tZONE,     -12*HOUR }, /* Intl Date Line East */
610fd082e96SMartin Matuska 
611fd082e96SMartin Matuska 	{ 0, "dst",  tDST,		0 },
612fd082e96SMartin Matuska 
613fd082e96SMartin Matuska 	/* Time units. */
614fd082e96SMartin Matuska 	{ 4, "years",		tMONTH_UNIT,	12 },
615fd082e96SMartin Matuska 	{ 5, "months",		tMONTH_UNIT,	1 },
616fd082e96SMartin Matuska 	{ 9, "fortnights",	tSEC_UNIT,	14 * DAY },
617fd082e96SMartin Matuska 	{ 4, "weeks",		tSEC_UNIT,	7 * DAY },
618fd082e96SMartin Matuska 	{ 3, "days",		tSEC_UNIT,	DAY },
619fd082e96SMartin Matuska 	{ 4, "hours",		tSEC_UNIT,	HOUR },
620fd082e96SMartin Matuska 	{ 3, "minutes",		tSEC_UNIT,	MINUTE },
621fd082e96SMartin Matuska 	{ 3, "seconds",		tSEC_UNIT,	1 },
622fd082e96SMartin Matuska 
623fd082e96SMartin Matuska 	/* Relative-time words. */
624fd082e96SMartin Matuska 	{ 0, "tomorrow",	tSEC_UNIT,	DAY },
625fd082e96SMartin Matuska 	{ 0, "yesterday",	tSEC_UNIT,	-DAY },
626fd082e96SMartin Matuska 	{ 0, "today",		tSEC_UNIT,	0 },
627fd082e96SMartin Matuska 	{ 0, "now",		tSEC_UNIT,	0 },
628fd082e96SMartin Matuska 	{ 0, "last",		tUNUMBER,	-1 },
629fd082e96SMartin Matuska 	{ 0, "this",		tSEC_UNIT,	0 },
630fd082e96SMartin Matuska 	{ 0, "next",		tUNUMBER,	2 },
631fd082e96SMartin Matuska 	{ 0, "first",		tUNUMBER,	1 },
632fd082e96SMartin Matuska 	{ 0, "1st",		tUNUMBER,	1 },
633fd082e96SMartin Matuska /*	{ 0, "second",		tUNUMBER,	2 }, */
634fd082e96SMartin Matuska 	{ 0, "2nd",		tUNUMBER,	2 },
635fd082e96SMartin Matuska 	{ 0, "third",		tUNUMBER,	3 },
636fd082e96SMartin Matuska 	{ 0, "3rd",		tUNUMBER,	3 },
637fd082e96SMartin Matuska 	{ 0, "fourth",		tUNUMBER,	4 },
638fd082e96SMartin Matuska 	{ 0, "4th",		tUNUMBER,	4 },
639fd082e96SMartin Matuska 	{ 0, "fifth",		tUNUMBER,	5 },
640fd082e96SMartin Matuska 	{ 0, "5th",		tUNUMBER,	5 },
641fd082e96SMartin Matuska 	{ 0, "sixth",		tUNUMBER,	6 },
642fd082e96SMartin Matuska 	{ 0, "seventh",		tUNUMBER,	7 },
643fd082e96SMartin Matuska 	{ 0, "eighth",		tUNUMBER,	8 },
644fd082e96SMartin Matuska 	{ 0, "ninth",		tUNUMBER,	9 },
645fd082e96SMartin Matuska 	{ 0, "tenth",		tUNUMBER,	10 },
646fd082e96SMartin Matuska 	{ 0, "eleventh",	tUNUMBER,	11 },
647fd082e96SMartin Matuska 	{ 0, "twelfth",		tUNUMBER,	12 },
648fd082e96SMartin Matuska 	{ 0, "ago",		tAGO,		1 },
649fd082e96SMartin Matuska 
650fd082e96SMartin Matuska 	/* Military timezones. */
651fd082e96SMartin Matuska 	{ 0, "a",	tZONE,	1*HOUR },
652fd082e96SMartin Matuska 	{ 0, "b",	tZONE,	2*HOUR },
653fd082e96SMartin Matuska 	{ 0, "c",	tZONE,	3*HOUR },
654fd082e96SMartin Matuska 	{ 0, "d",	tZONE,	4*HOUR },
655fd082e96SMartin Matuska 	{ 0, "e",	tZONE,	5*HOUR },
656fd082e96SMartin Matuska 	{ 0, "f",	tZONE,	6*HOUR },
657fd082e96SMartin Matuska 	{ 0, "g",	tZONE,	7*HOUR },
658fd082e96SMartin Matuska 	{ 0, "h",	tZONE,	8*HOUR },
659fd082e96SMartin Matuska 	{ 0, "i",	tZONE,	9*HOUR },
660fd082e96SMartin Matuska 	{ 0, "k",	tZONE,	10*HOUR },
661fd082e96SMartin Matuska 	{ 0, "l",	tZONE,	11*HOUR },
662fd082e96SMartin Matuska 	{ 0, "m",	tZONE,	12*HOUR },
663fd082e96SMartin Matuska 	{ 0, "n",	tZONE,	-1*HOUR },
664fd082e96SMartin Matuska 	{ 0, "o",	tZONE,	-2*HOUR },
665fd082e96SMartin Matuska 	{ 0, "p",	tZONE,	-3*HOUR },
666fd082e96SMartin Matuska 	{ 0, "q",	tZONE,	-4*HOUR },
667fd082e96SMartin Matuska 	{ 0, "r",	tZONE,	-5*HOUR },
668fd082e96SMartin Matuska 	{ 0, "s",	tZONE,	-6*HOUR },
669fd082e96SMartin Matuska 	{ 0, "t",	tZONE,	-7*HOUR },
670fd082e96SMartin Matuska 	{ 0, "u",	tZONE,	-8*HOUR },
671fd082e96SMartin Matuska 	{ 0, "v",	tZONE,	-9*HOUR },
672fd082e96SMartin Matuska 	{ 0, "w",	tZONE,	-10*HOUR },
673fd082e96SMartin Matuska 	{ 0, "x",	tZONE,	-11*HOUR },
674fd082e96SMartin Matuska 	{ 0, "y",	tZONE,	-12*HOUR },
675fd082e96SMartin Matuska 	{ 0, "z",	tZONE,	0*HOUR },
676fd082e96SMartin Matuska 
677fd082e96SMartin Matuska 	/* End of table. */
678fd082e96SMartin Matuska 	{ 0, NULL,	0,	0 }
679fd082e96SMartin Matuska };
680fd082e96SMartin Matuska 
681fd082e96SMartin Matuska /*
682fd082e96SMartin Matuska  * Year is either:
683fd082e96SMartin Matuska  *  = A number from 0 to 99, which means a year from 1970 to 2069, or
684fd082e96SMartin Matuska  *  = The actual year (>=100).
685fd082e96SMartin Matuska  */
686fd082e96SMartin Matuska static time_t
687fd082e96SMartin Matuska Convert(time_t Month, time_t Day, time_t Year,
688fd082e96SMartin Matuska 	time_t Hours, time_t Minutes, time_t Seconds,
689fd082e96SMartin Matuska 	time_t Timezone, enum DSTMODE DSTmode)
690fd082e96SMartin Matuska {
691a8fc61d5SMartin Matuska 	signed char DaysInMonth[12] = {
692fd082e96SMartin Matuska 		31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
693fd082e96SMartin Matuska 	};
694fd082e96SMartin Matuska 	time_t		Julian;
695fd082e96SMartin Matuska 	int		i;
696f9762417SMartin Matuska 	struct tm	*ltime;
697*e64fe029SMartin Matuska #if defined(HAVE_LOCALTIME_R) || defined(HAVE_LOCALTIME_S)
698f9762417SMartin Matuska 	struct tm	tmbuf;
699f9762417SMartin Matuska #endif
700fd082e96SMartin Matuska 
701fd082e96SMartin Matuska 	if (Year < 69)
702fd082e96SMartin Matuska 		Year += 2000;
703fd082e96SMartin Matuska 	else if (Year < 100)
704fd082e96SMartin Matuska 		Year += 1900;
705fd082e96SMartin Matuska 	DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0)
706fd082e96SMartin Matuska 	    ? 29 : 28;
707fd082e96SMartin Matuska 	/* Checking for 2038 bogusly assumes that time_t is 32 bits.  But
708fd082e96SMartin Matuska 	   I'm too lazy to try to check for time_t overflow in another way.  */
709833a452eSMartin Matuska 	if (Year < EPOCH || Year >= 2038
710fd082e96SMartin Matuska 	    || Month < 1 || Month > 12
711fd082e96SMartin Matuska 	    /* Lint fluff:  "conversion from long may lose accuracy" */
712fd082e96SMartin Matuska 	    || Day < 1 || Day > DaysInMonth[(int)--Month]
713fd082e96SMartin Matuska 	    || Hours < 0 || Hours > 23
714fd082e96SMartin Matuska 	    || Minutes < 0 || Minutes > 59
715fd082e96SMartin Matuska 	    || Seconds < 0 || Seconds > 59)
716fd082e96SMartin Matuska 		return -1;
717fd082e96SMartin Matuska 
718fd082e96SMartin Matuska 	Julian = Day - 1;
719fd082e96SMartin Matuska 	for (i = 0; i < Month; i++)
720fd082e96SMartin Matuska 		Julian += DaysInMonth[i];
721fd082e96SMartin Matuska 	for (i = EPOCH; i < Year; i++)
722fd082e96SMartin Matuska 		Julian += 365 + (i % 4 == 0);
723fd082e96SMartin Matuska 	Julian *= DAY;
724fd082e96SMartin Matuska 	Julian += Timezone;
725fd082e96SMartin Matuska 	Julian += Hours * HOUR + Minutes * MINUTE + Seconds;
726*e64fe029SMartin Matuska #if defined(HAVE_LOCALTIME_S)
727*e64fe029SMartin Matuska 	ltime = localtime_s(&tmbuf, &Julian) ? NULL : &tmbuf;
728*e64fe029SMartin Matuska #elif defined(HAVE_LOCALTIME_R)
729f9762417SMartin Matuska 	ltime = localtime_r(&Julian, &tmbuf);
730f9762417SMartin Matuska #else
731f9762417SMartin Matuska 	ltime = localtime(&Julian);
732f9762417SMartin Matuska #endif
733fd082e96SMartin Matuska 	if (DSTmode == DSTon
734f9762417SMartin Matuska 	    || (DSTmode == DSTmaybe && ltime->tm_isdst))
735fd082e96SMartin Matuska 		Julian -= HOUR;
736fd082e96SMartin Matuska 	return Julian;
737fd082e96SMartin Matuska }
738fd082e96SMartin Matuska 
739fd082e96SMartin Matuska static time_t
740fd082e96SMartin Matuska DSTcorrect(time_t Start, time_t Future)
741fd082e96SMartin Matuska {
742fd082e96SMartin Matuska 	time_t		StartDay;
743fd082e96SMartin Matuska 	time_t		FutureDay;
744f9762417SMartin Matuska 	struct tm	*ltime;
745*e64fe029SMartin Matuska #if defined(HAVE_LOCALTIME_R) || defined(HAVE_LOCALTIME_S)
746f9762417SMartin Matuska 	struct tm	tmbuf;
747f9762417SMartin Matuska #endif
748*e64fe029SMartin Matuska #if defined(HAVE_LOCALTIME_S)
749*e64fe029SMartin Matuska 	ltime = localtime_s(&tmbuf, &Start) ? NULL : &tmbuf;
750*e64fe029SMartin Matuska #elif defined(HAVE_LOCALTIME_R)
751f9762417SMartin Matuska 	ltime = localtime_r(&Start, &tmbuf);
752f9762417SMartin Matuska #else
753f9762417SMartin Matuska 	ltime = localtime(&Start);
754f9762417SMartin Matuska #endif
755f9762417SMartin Matuska 	StartDay = (ltime->tm_hour + 1) % 24;
756*e64fe029SMartin Matuska #if defined(HAVE_LOCALTIME_S)
757*e64fe029SMartin Matuska 	ltime = localtime_s(&tmbuf, &Future) ? NULL : &tmbuf;
758*e64fe029SMartin Matuska #elif defined(HAVE_LOCALTIME_R)
759f9762417SMartin Matuska 	ltime = localtime_r(&Future, &tmbuf);
760f9762417SMartin Matuska #else
761f9762417SMartin Matuska 	ltime = localtime(&Future);
762f9762417SMartin Matuska #endif
763f9762417SMartin Matuska 	FutureDay = (ltime->tm_hour + 1) % 24;
764fd082e96SMartin Matuska 	return (Future - Start) + (StartDay - FutureDay) * HOUR;
765fd082e96SMartin Matuska }
766fd082e96SMartin Matuska 
767fd082e96SMartin Matuska 
768fd082e96SMartin Matuska static time_t
769fd082e96SMartin Matuska RelativeDate(time_t Start, time_t zone, int dstmode,
770fd082e96SMartin Matuska     time_t DayOrdinal, time_t DayNumber)
771fd082e96SMartin Matuska {
772fd082e96SMartin Matuska 	struct tm	*tm;
773fd082e96SMartin Matuska 	time_t	t, now;
774*e64fe029SMartin Matuska #if defined(HAVE_GMTIME_R) || defined(HAVE_GMTIME_S)
775f9762417SMartin Matuska 	struct tm	tmbuf;
776f9762417SMartin Matuska #endif
777fd082e96SMartin Matuska 
778fd082e96SMartin Matuska 	t = Start - zone;
779*e64fe029SMartin Matuska #if defined(HAVE_GMTIME_S)
780*e64fe029SMartin Matuska 	tm = gmtime_s(&tmbuf, &t) ? NULL : &tmbuf;
781*e64fe029SMartin Matuska #elif defined(HAVE_GMTIME_R)
782f9762417SMartin Matuska 	tm = gmtime_r(&t, &tmbuf);
783f9762417SMartin Matuska #else
784fd082e96SMartin Matuska 	tm = gmtime(&t);
785f9762417SMartin Matuska #endif
786fd082e96SMartin Matuska 	now = Start;
787fd082e96SMartin Matuska 	now += DAY * ((DayNumber - tm->tm_wday + 7) % 7);
788fd082e96SMartin Matuska 	now += 7 * DAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1);
789fd082e96SMartin Matuska 	if (dstmode == DSTmaybe)
790fd082e96SMartin Matuska 		return DSTcorrect(Start, now);
791fd082e96SMartin Matuska 	return now - Start;
792fd082e96SMartin Matuska }
793fd082e96SMartin Matuska 
794fd082e96SMartin Matuska 
795fd082e96SMartin Matuska static time_t
796fd082e96SMartin Matuska RelativeMonth(time_t Start, time_t Timezone, time_t RelMonth)
797fd082e96SMartin Matuska {
798fd082e96SMartin Matuska 	struct tm	*tm;
799fd082e96SMartin Matuska 	time_t	Month;
800fd082e96SMartin Matuska 	time_t	Year;
801*e64fe029SMartin Matuska #if defined(HAVE_LOCALTIME_R) || defined(HAVE_LOCALTIME_S)
802f9762417SMartin Matuska 	struct tm	tmbuf;
803f9762417SMartin Matuska #endif
804fd082e96SMartin Matuska 
805fd082e96SMartin Matuska 	if (RelMonth == 0)
806fd082e96SMartin Matuska 		return 0;
807*e64fe029SMartin Matuska #if defined(HAVE_LOCALTIME_S)
808*e64fe029SMartin Matuska 	tm = localtime_s(&tmbuf, &Start) ? NULL : &tmbuf;
809*e64fe029SMartin Matuska #elif defined(HAVE_LOCALTIME_R)
810f9762417SMartin Matuska 	tm = localtime_r(&Start, &tmbuf);
811f9762417SMartin Matuska #else
812fd082e96SMartin Matuska 	tm = localtime(&Start);
813f9762417SMartin Matuska #endif
814fd082e96SMartin Matuska 	Month = 12 * (tm->tm_year + 1900) + tm->tm_mon + RelMonth;
815fd082e96SMartin Matuska 	Year = Month / 12;
816fd082e96SMartin Matuska 	Month = Month % 12 + 1;
817fd082e96SMartin Matuska 	return DSTcorrect(Start,
818fd082e96SMartin Matuska 	    Convert(Month, (time_t)tm->tm_mday, Year,
819fd082e96SMartin Matuska 		(time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec,
820fd082e96SMartin Matuska 		Timezone, DSTmaybe));
821fd082e96SMartin Matuska }
822fd082e96SMartin Matuska 
823fd082e96SMartin Matuska /*
824fd082e96SMartin Matuska  * Tokenizer.
825fd082e96SMartin Matuska  */
826fd082e96SMartin Matuska static int
827cdf63a70SMartin Matuska nexttoken(const char **in, time_t *value)
828fd082e96SMartin Matuska {
829fd082e96SMartin Matuska 	char	c;
830fd082e96SMartin Matuska 	char	buff[64];
831fd082e96SMartin Matuska 
832fd082e96SMartin Matuska 	for ( ; ; ) {
833fd082e96SMartin Matuska 		while (isspace((unsigned char)**in))
834fd082e96SMartin Matuska 			++*in;
835fd082e96SMartin Matuska 
836fd082e96SMartin Matuska 		/* Skip parenthesized comments. */
837fd082e96SMartin Matuska 		if (**in == '(') {
838fd082e96SMartin Matuska 			int Count = 0;
839fd082e96SMartin Matuska 			do {
840fd082e96SMartin Matuska 				c = *(*in)++;
841fd082e96SMartin Matuska 				if (c == '\0')
842fd082e96SMartin Matuska 					return c;
843fd082e96SMartin Matuska 				if (c == '(')
844fd082e96SMartin Matuska 					Count++;
845fd082e96SMartin Matuska 				else if (c == ')')
846fd082e96SMartin Matuska 					Count--;
847fd082e96SMartin Matuska 			} while (Count > 0);
848fd082e96SMartin Matuska 			continue;
849fd082e96SMartin Matuska 		}
850fd082e96SMartin Matuska 
851fd082e96SMartin Matuska 		/* Try the next token in the word table first. */
852fd082e96SMartin Matuska 		/* This allows us to match "2nd", for example. */
853fd082e96SMartin Matuska 		{
854cdf63a70SMartin Matuska 			const char *src = *in;
855fd082e96SMartin Matuska 			const struct LEXICON *tp;
856fd082e96SMartin Matuska 			unsigned i = 0;
857fd082e96SMartin Matuska 
858fd082e96SMartin Matuska 			/* Force to lowercase and strip '.' characters. */
859fd082e96SMartin Matuska 			while (*src != '\0'
860fd082e96SMartin Matuska 			    && (isalnum((unsigned char)*src) || *src == '.')
861fd082e96SMartin Matuska 			    && i < sizeof(buff)-1) {
862fd082e96SMartin Matuska 				if (*src != '.') {
863fd082e96SMartin Matuska 					if (isupper((unsigned char)*src))
864fd082e96SMartin Matuska 						buff[i++] = tolower((unsigned char)*src);
865fd082e96SMartin Matuska 					else
866fd082e96SMartin Matuska 						buff[i++] = *src;
867fd082e96SMartin Matuska 				}
868fd082e96SMartin Matuska 				src++;
869fd082e96SMartin Matuska 			}
870fd082e96SMartin Matuska 			buff[i] = '\0';
871fd082e96SMartin Matuska 
872fd082e96SMartin Matuska 			/*
873fd082e96SMartin Matuska 			 * Find the first match.  If the word can be
874fd082e96SMartin Matuska 			 * abbreviated, make sure we match at least
875fd082e96SMartin Matuska 			 * the minimum abbreviation.
876fd082e96SMartin Matuska 			 */
877fd082e96SMartin Matuska 			for (tp = TimeWords; tp->name; tp++) {
878fd082e96SMartin Matuska 				size_t abbrev = tp->abbrev;
879fd082e96SMartin Matuska 				if (abbrev == 0)
880fd082e96SMartin Matuska 					abbrev = strlen(tp->name);
881fd082e96SMartin Matuska 				if (strlen(buff) >= abbrev
882fd082e96SMartin Matuska 				    && strncmp(tp->name, buff, strlen(buff))
883fd082e96SMartin Matuska 				    	== 0) {
884fd082e96SMartin Matuska 					/* Skip over token. */
885fd082e96SMartin Matuska 					*in = src;
886fd082e96SMartin Matuska 					/* Return the match. */
887fd082e96SMartin Matuska 					*value = tp->value;
888fd082e96SMartin Matuska 					return tp->type;
889fd082e96SMartin Matuska 				}
890fd082e96SMartin Matuska 			}
891fd082e96SMartin Matuska 		}
892fd082e96SMartin Matuska 
893fd082e96SMartin Matuska 		/*
894fd082e96SMartin Matuska 		 * Not in the word table, maybe it's a number.  Note:
895fd082e96SMartin Matuska 		 * Because '-' and '+' have other special meanings, I
896fd082e96SMartin Matuska 		 * don't deal with signed numbers here.
897fd082e96SMartin Matuska 		 */
898fd082e96SMartin Matuska 		if (isdigit((unsigned char)(c = **in))) {
899fd082e96SMartin Matuska 			for (*value = 0; isdigit((unsigned char)(c = *(*in)++)); )
900fd082e96SMartin Matuska 				*value = 10 * *value + c - '0';
901fd082e96SMartin Matuska 			(*in)--;
902fd082e96SMartin Matuska 			return (tUNUMBER);
903fd082e96SMartin Matuska 		}
904fd082e96SMartin Matuska 
905fd082e96SMartin Matuska 		return *(*in)++;
906fd082e96SMartin Matuska 	}
907fd082e96SMartin Matuska }
908fd082e96SMartin Matuska 
909fd082e96SMartin Matuska #define	TM_YEAR_ORIGIN 1900
910fd082e96SMartin Matuska 
911fd082e96SMartin Matuska /* Yield A - B, measured in seconds.  */
912fd082e96SMartin Matuska static long
913fd082e96SMartin Matuska difftm (struct tm *a, struct tm *b)
914fd082e96SMartin Matuska {
915fd082e96SMartin Matuska 	int ay = a->tm_year + (TM_YEAR_ORIGIN - 1);
916fd082e96SMartin Matuska 	int by = b->tm_year + (TM_YEAR_ORIGIN - 1);
917fd082e96SMartin Matuska 	int days = (
918fd082e96SMartin Matuska 		/* difference in day of year */
919fd082e96SMartin Matuska 		a->tm_yday - b->tm_yday
920fd082e96SMartin Matuska 		/* + intervening leap days */
921fd082e96SMartin Matuska 		+  ((ay >> 2) - (by >> 2))
922fd082e96SMartin Matuska 		-  (ay/100 - by/100)
923fd082e96SMartin Matuska 		+  ((ay/100 >> 2) - (by/100 >> 2))
924fd082e96SMartin Matuska 		/* + difference in years * 365 */
925fd082e96SMartin Matuska 		+  (long)(ay-by) * 365
926fd082e96SMartin Matuska 		);
927fd082e96SMartin Matuska 	return (days * DAY + (a->tm_hour - b->tm_hour) * HOUR
928fd082e96SMartin Matuska 	    + (a->tm_min - b->tm_min) * MINUTE
929fd082e96SMartin Matuska 	    + (a->tm_sec - b->tm_sec));
930fd082e96SMartin Matuska }
931fd082e96SMartin Matuska 
932fd082e96SMartin Matuska /*
933fd082e96SMartin Matuska  *
934fd082e96SMartin Matuska  * The public function.
935fd082e96SMartin Matuska  *
936fd082e96SMartin Matuska  * TODO: tokens[] array should be dynamically sized.
937fd082e96SMartin Matuska  */
938fd082e96SMartin Matuska time_t
939cdf63a70SMartin Matuska __archive_get_date(time_t now, const char *p)
940fd082e96SMartin Matuska {
941fd082e96SMartin Matuska 	struct token	tokens[256];
942fd082e96SMartin Matuska 	struct gdstate	_gds;
943fd082e96SMartin Matuska 	struct token	*lasttoken;
944fd082e96SMartin Matuska 	struct gdstate	*gds;
945fd082e96SMartin Matuska 	struct tm	local, *tm;
946fd082e96SMartin Matuska 	struct tm	gmt, *gmt_ptr;
947fd082e96SMartin Matuska 	time_t		Start;
948fd082e96SMartin Matuska 	time_t		tod;
949fd082e96SMartin Matuska 	long		tzone;
950fd082e96SMartin Matuska 
951fd082e96SMartin Matuska 	/* Clear out the parsed token array. */
952fd082e96SMartin Matuska 	memset(tokens, 0, sizeof(tokens));
953fd082e96SMartin Matuska 	/* Initialize the parser state. */
954fd082e96SMartin Matuska 	memset(&_gds, 0, sizeof(_gds));
955fd082e96SMartin Matuska 	gds = &_gds;
956fd082e96SMartin Matuska 
957fd082e96SMartin Matuska 	/* Look up the current time. */
958*e64fe029SMartin Matuska #if defined(HAVE_LOCALTIME_S)
959*e64fe029SMartin Matuska 	tm = localtime_s(&local, &now) ? NULL : &local;
960*e64fe029SMartin Matuska #elif defined(HAVE_LOCALTIME_R)
961f9762417SMartin Matuska 	tm = localtime_r(&now, &local);
962f9762417SMartin Matuska #else
963fd082e96SMartin Matuska 	memset(&local, 0, sizeof(local));
964fd082e96SMartin Matuska 	tm = localtime(&now);
965f9762417SMartin Matuska #endif
966fd082e96SMartin Matuska 	if (tm == NULL)
967fd082e96SMartin Matuska 		return -1;
968*e64fe029SMartin Matuska #if !defined(HAVE_LOCALTIME_R) && !defined(HAVE_LOCALTIME_S)
969fd082e96SMartin Matuska 	local = *tm;
970f9762417SMartin Matuska #endif
971fd082e96SMartin Matuska 
972fd082e96SMartin Matuska 	/* Look up UTC if we can and use that to determine the current
973fd082e96SMartin Matuska 	 * timezone offset. */
974*e64fe029SMartin Matuska #if defined(HAVE_GMTIME_S)
975*e64fe029SMartin Matuska 	gmt_ptr = gmtime_s(&gmt, &now) ? NULL : &gmt;
976*e64fe029SMartin Matuska #elif defined(HAVE_GMTIME_R)
977f9762417SMartin Matuska 	gmt_ptr = gmtime_r(&now, &gmt);
978f9762417SMartin Matuska #else
979fd082e96SMartin Matuska 	memset(&gmt, 0, sizeof(gmt));
980fd082e96SMartin Matuska 	gmt_ptr = gmtime(&now);
981fd082e96SMartin Matuska 	if (gmt_ptr != NULL) {
982fd082e96SMartin Matuska 		/* Copy, in case localtime and gmtime use the same buffer. */
983fd082e96SMartin Matuska 		gmt = *gmt_ptr;
984fd082e96SMartin Matuska 	}
985f9762417SMartin Matuska #endif
986fd082e96SMartin Matuska 	if (gmt_ptr != NULL)
987fd082e96SMartin Matuska 		tzone = difftm (&gmt, &local);
988fd082e96SMartin Matuska 	else
989fd082e96SMartin Matuska 		/* This system doesn't understand timezones; fake it. */
990fd082e96SMartin Matuska 		tzone = 0;
991fd082e96SMartin Matuska 	if(local.tm_isdst)
992fd082e96SMartin Matuska 		tzone += HOUR;
993fd082e96SMartin Matuska 
994fd082e96SMartin Matuska 	/* Tokenize the input string. */
995fd082e96SMartin Matuska 	lasttoken = tokens;
996fd082e96SMartin Matuska 	while ((lasttoken->token = nexttoken(&p, &lasttoken->value)) != 0) {
997fd082e96SMartin Matuska 		++lasttoken;
998fd082e96SMartin Matuska 		if (lasttoken > tokens + 255)
999fd082e96SMartin Matuska 			return -1;
1000fd082e96SMartin Matuska 	}
1001fd082e96SMartin Matuska 	gds->tokenp = tokens;
1002fd082e96SMartin Matuska 
1003fd082e96SMartin Matuska 	/* Match phrases until we run out of input tokens. */
1004fd082e96SMartin Matuska 	while (gds->tokenp < lasttoken) {
1005fd082e96SMartin Matuska 		if (!phrase(gds))
1006fd082e96SMartin Matuska 			return -1;
1007fd082e96SMartin Matuska 	}
1008fd082e96SMartin Matuska 
1009fd082e96SMartin Matuska 	/* Use current local timezone if none was specified. */
1010fd082e96SMartin Matuska 	if (!gds->HaveZone) {
1011fd082e96SMartin Matuska 		gds->Timezone = tzone;
1012fd082e96SMartin Matuska 		gds->DSTmode = DSTmaybe;
1013fd082e96SMartin Matuska 	}
1014fd082e96SMartin Matuska 
1015fd082e96SMartin Matuska 	/* If a timezone was specified, use that for generating the default
1016fd082e96SMartin Matuska 	 * time components instead of the local timezone. */
1017fd082e96SMartin Matuska 	if (gds->HaveZone && gmt_ptr != NULL) {
1018fd082e96SMartin Matuska 		now -= gds->Timezone;
1019*e64fe029SMartin Matuska #if defined(HAVE_GMTIME_S)
1020*e64fe029SMartin Matuska 		gmt_ptr = gmtime_s(&gmt, &now) ? NULL : &gmt;
1021*e64fe029SMartin Matuska #elif defined(HAVE_GMTIME_R)
1022f9762417SMartin Matuska 		gmt_ptr = gmtime_r(&now, &gmt);
1023f9762417SMartin Matuska #else
1024fd082e96SMartin Matuska 		gmt_ptr = gmtime(&now);
1025f9762417SMartin Matuska #endif
1026fd082e96SMartin Matuska 		if (gmt_ptr != NULL)
1027fd082e96SMartin Matuska 			local = *gmt_ptr;
1028fd082e96SMartin Matuska 		now += gds->Timezone;
1029fd082e96SMartin Matuska 	}
1030fd082e96SMartin Matuska 
1031fd082e96SMartin Matuska 	if (!gds->HaveYear)
1032fd082e96SMartin Matuska 		gds->Year = local.tm_year + 1900;
1033fd082e96SMartin Matuska 	if (!gds->HaveMonth)
1034fd082e96SMartin Matuska 		gds->Month = local.tm_mon + 1;
1035fd082e96SMartin Matuska 	if (!gds->HaveDay)
1036fd082e96SMartin Matuska 		gds->Day = local.tm_mday;
1037fd082e96SMartin Matuska 	/* Note: No default for hour/min/sec; a specifier that just
1038fd082e96SMartin Matuska 	 * gives date always refers to 00:00 on that date. */
1039fd082e96SMartin Matuska 
1040fd082e96SMartin Matuska 	/* If we saw more than one time, timezone, weekday, year, month,
1041fd082e96SMartin Matuska 	 * or day, then give up. */
1042fd082e96SMartin Matuska 	if (gds->HaveTime > 1 || gds->HaveZone > 1 || gds->HaveWeekDay > 1
1043fd082e96SMartin Matuska 	    || gds->HaveYear > 1 || gds->HaveMonth > 1 || gds->HaveDay > 1)
1044fd082e96SMartin Matuska 		return -1;
1045fd082e96SMartin Matuska 
1046fd082e96SMartin Matuska 	/* Compute an absolute time based on whatever absolute information
1047fd082e96SMartin Matuska 	 * we collected. */
1048fd082e96SMartin Matuska 	if (gds->HaveYear || gds->HaveMonth || gds->HaveDay
1049fd082e96SMartin Matuska 	    || gds->HaveTime || gds->HaveWeekDay) {
1050fd082e96SMartin Matuska 		Start = Convert(gds->Month, gds->Day, gds->Year,
1051fd082e96SMartin Matuska 		    gds->Hour, gds->Minutes, gds->Seconds,
1052fd082e96SMartin Matuska 		    gds->Timezone, gds->DSTmode);
1053fd082e96SMartin Matuska 		if (Start < 0)
1054fd082e96SMartin Matuska 			return -1;
1055fd082e96SMartin Matuska 	} else {
1056fd082e96SMartin Matuska 		Start = now;
1057fd082e96SMartin Matuska 		if (!gds->HaveRel)
1058fd082e96SMartin Matuska 			Start -= local.tm_hour * HOUR + local.tm_min * MINUTE
1059fd082e96SMartin Matuska 			    + local.tm_sec;
1060fd082e96SMartin Matuska 	}
1061fd082e96SMartin Matuska 
1062fd082e96SMartin Matuska 	/* Add the relative offset. */
1063fd082e96SMartin Matuska 	Start += gds->RelSeconds;
1064fd082e96SMartin Matuska 	Start += RelativeMonth(Start, gds->Timezone, gds->RelMonth);
1065fd082e96SMartin Matuska 
1066fd082e96SMartin Matuska 	/* Adjust for day-of-week offsets. */
1067fd082e96SMartin Matuska 	if (gds->HaveWeekDay
1068fd082e96SMartin Matuska 	    && !(gds->HaveYear || gds->HaveMonth || gds->HaveDay)) {
1069fd082e96SMartin Matuska 		tod = RelativeDate(Start, gds->Timezone,
1070fd082e96SMartin Matuska 		    gds->DSTmode, gds->DayOrdinal, gds->DayNumber);
1071fd082e96SMartin Matuska 		Start += tod;
1072fd082e96SMartin Matuska 	}
1073fd082e96SMartin Matuska 
1074fd082e96SMartin Matuska 	/* -1 is an error indicator, so return 0 instead of -1 if
1075fd082e96SMartin Matuska 	 * that's the actual time. */
1076fd082e96SMartin Matuska 	return Start == -1 ? 0 : Start;
1077fd082e96SMartin Matuska }
1078fd082e96SMartin Matuska 
1079fd082e96SMartin Matuska 
1080fd082e96SMartin Matuska #if	defined(TEST)
1081fd082e96SMartin Matuska 
1082fd082e96SMartin Matuska /* ARGSUSED */
1083fd082e96SMartin Matuska int
1084fd082e96SMartin Matuska main(int argc, char **argv)
1085fd082e96SMartin Matuska {
1086fd082e96SMartin Matuska     time_t	d;
10873fd25813STim Kientzle     time_t	now = time(NULL);
1088fd082e96SMartin Matuska 
1089fd082e96SMartin Matuska     while (*++argv != NULL) {
1090fd082e96SMartin Matuska 	    (void)printf("Input: %s\n", *argv);
10913fd25813STim Kientzle 	    d = get_date(now, *argv);
1092fd082e96SMartin Matuska 	    if (d == -1)
1093fd082e96SMartin Matuska 		    (void)printf("Bad format - couldn't convert.\n");
1094fd082e96SMartin Matuska 	    else
1095fd082e96SMartin Matuska 		    (void)printf("Output: %s\n", ctime(&d));
1096fd082e96SMartin Matuska     }
1097fd082e96SMartin Matuska     exit(0);
1098fd082e96SMartin Matuska     /* NOTREACHED */
1099fd082e96SMartin Matuska }
1100fd082e96SMartin Matuska #endif	/* defined(TEST) */
1101