parsedate.y revision 1.32 1 1.1 christos %{
2 1.1 christos /*
3 1.1 christos ** Originally written by Steven M. Bellovin <smb (at) research.att.com> while
4 1.1 christos ** at the University of North Carolina at Chapel Hill. Later tweaked by
5 1.1 christos ** a couple of people on Usenet. Completely overhauled by Rich $alz
6 1.1 christos ** <rsalz (at) bbn.com> and Jim Berets <jberets (at) bbn.com> in August, 1990;
7 1.1 christos **
8 1.1 christos ** This grammar has 10 shift/reduce conflicts.
9 1.1 christos **
10 1.1 christos ** This code is in the public domain and has no copyright.
11 1.1 christos */
12 1.1 christos /* SUPPRESS 287 on yaccpar_sccsid *//* Unused static variable */
13 1.1 christos /* SUPPRESS 288 on yyerrlab *//* Label unused */
14 1.1 christos
15 1.15 yamt #include <sys/cdefs.h>
16 1.15 yamt #ifdef __RCSID
17 1.32 kre __RCSID("$NetBSD: parsedate.y,v 1.32 2017/03/22 18:17:42 kre Exp $");
18 1.15 yamt #endif
19 1.15 yamt
20 1.1 christos #include <stdio.h>
21 1.1 christos #include <ctype.h>
22 1.14 apb #include <errno.h>
23 1.1 christos #include <string.h>
24 1.1 christos #include <time.h>
25 1.1 christos #include <util.h>
26 1.3 drochner #include <stdlib.h>
27 1.1 christos
28 1.1 christos /* NOTES on rebuilding parsedate.c (particularly for inclusion in CVS
29 1.1 christos releases):
30 1.1 christos
31 1.1 christos We don't want to mess with all the portability hassles of alloca.
32 1.1 christos In particular, most (all?) versions of bison will use alloca in
33 1.1 christos their parser. If bison works on your system (e.g. it should work
34 1.1 christos with gcc), then go ahead and use it, but the more general solution
35 1.1 christos is to use byacc instead of bison, which should generate a portable
36 1.1 christos parser. I played with adding "#define alloca dont_use_alloca", to
37 1.1 christos give an error if the parser generator uses alloca (and thus detect
38 1.1 christos unportable parsedate.c's), but that seems to cause as many problems
39 1.1 christos as it solves. */
40 1.1 christos
41 1.1 christos #define EPOCH 1970
42 1.21 christos #define HOUR(x) ((time_t)((x) * 60))
43 1.1 christos #define SECSPERDAY (24L * 60L * 60L)
44 1.1 christos
45 1.28 kre #define MAXREL 16 /* hours mins secs days weeks months years - maybe twice each ...*/
46 1.28 kre
47 1.20 apb #define USE_LOCAL_TIME 99999 /* special case for Convert() and yyTimezone */
48 1.1 christos
49 1.1 christos /*
50 1.1 christos ** An entry in the lexical lookup table.
51 1.1 christos */
52 1.1 christos typedef struct _TABLE {
53 1.1 christos const char *name;
54 1.1 christos int type;
55 1.1 christos time_t value;
56 1.1 christos } TABLE;
57 1.1 christos
58 1.1 christos
59 1.1 christos /*
60 1.1 christos ** Daylight-savings mode: on, off, or not yet known.
61 1.1 christos */
62 1.1 christos typedef enum _DSTMODE {
63 1.1 christos DSTon, DSToff, DSTmaybe
64 1.1 christos } DSTMODE;
65 1.1 christos
66 1.1 christos /*
67 1.31 kre ** Meridian: am, pm, or 24-hour style (plus "noon" and "midnight").
68 1.1 christos */
69 1.1 christos typedef enum _MERIDIAN {
70 1.31 kre MERam, MERpm, MER24, MER_NOON, MER_MN
71 1.1 christos } MERIDIAN;
72 1.1 christos
73 1.1 christos
74 1.9 christos struct dateinfo {
75 1.17 apb DSTMODE yyDSTmode; /* DST on/off/maybe */
76 1.9 christos time_t yyDayOrdinal;
77 1.9 christos time_t yyDayNumber;
78 1.9 christos int yyHaveDate;
79 1.17 apb int yyHaveFullYear; /* if true, year is not abbreviated. */
80 1.17 apb /* if false, need to call AdjustYear(). */
81 1.9 christos int yyHaveDay;
82 1.9 christos int yyHaveRel;
83 1.9 christos int yyHaveTime;
84 1.9 christos int yyHaveZone;
85 1.17 apb time_t yyTimezone; /* Timezone as minutes ahead/east of UTC */
86 1.17 apb time_t yyDay; /* Day of month [1-31] */
87 1.17 apb time_t yyHour; /* Hour of day [0-24] or [1-12] */
88 1.17 apb time_t yyMinutes; /* Minute of hour [0-59] */
89 1.17 apb time_t yyMonth; /* Month of year [1-12] */
90 1.17 apb time_t yySeconds; /* Second of minute [0-60] */
91 1.17 apb time_t yyYear; /* Year, see also yyHaveFullYear */
92 1.17 apb MERIDIAN yyMeridian; /* Interpret yyHour as AM/PM/24 hour clock */
93 1.28 kre struct {
94 1.28 kre time_t yyRelVal;
95 1.28 kre int yyRelMonth;
96 1.28 kre } yyRel[MAXREL];
97 1.9 christos };
98 1.1 christos %}
99 1.1 christos
100 1.1 christos %union {
101 1.1 christos time_t Number;
102 1.1 christos enum _MERIDIAN Meridian;
103 1.1 christos }
104 1.1 christos
105 1.1 christos %token tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT
106 1.23 christos %token tSEC_UNIT tSNUMBER tUNUMBER tZONE tDST AT_SIGN tTIME
107 1.1 christos
108 1.1 christos %type <Number> tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT
109 1.23 christos %type <Number> tSEC_UNIT tSNUMBER tUNUMBER tZONE tTIME
110 1.25 dholland %type <Meridian> tMERIDIAN
111 1.25 dholland
112 1.25 dholland %type <Number> at_number
113 1.25 dholland %type <Meridian> o_merid
114 1.1 christos
115 1.9 christos %parse-param { struct dateinfo *param }
116 1.9 christos %parse-param { const char **yyInput }
117 1.9 christos %lex-param { const char **yyInput }
118 1.9 christos %pure-parser
119 1.9 christos
120 1.1 christos %%
121 1.1 christos
122 1.25 dholland spec:
123 1.25 dholland /* empty */
124 1.1 christos | spec item
125 1.25 dholland ;
126 1.1 christos
127 1.25 dholland item:
128 1.25 dholland time { param->yyHaveTime++; }
129 1.25 dholland | time_numericzone { param->yyHaveTime++; param->yyHaveZone++; }
130 1.25 dholland | zone { param->yyHaveZone++; }
131 1.25 dholland | date { param->yyHaveDate++; }
132 1.25 dholland | day { param->yyHaveDay++; }
133 1.25 dholland | rel { param->yyHaveRel++; }
134 1.25 dholland | cvsstamp { param->yyHaveTime++; param->yyHaveDate++;
135 1.25 dholland param->yyHaveZone++; }
136 1.25 dholland | epochdate { param->yyHaveTime++; param->yyHaveDate++;
137 1.25 dholland param->yyHaveZone++; }
138 1.1 christos | number
139 1.25 dholland ;
140 1.1 christos
141 1.25 dholland cvsstamp:
142 1.25 dholland tUNUMBER '.' tUNUMBER '.' tUNUMBER '.'
143 1.25 dholland tUNUMBER '.' tUNUMBER '.' tUNUMBER {
144 1.25 dholland param->yyYear = $1;
145 1.25 dholland if (param->yyYear < 100) {
146 1.25 dholland param->yyYear += 1900;
147 1.25 dholland }
148 1.25 dholland param->yyHaveFullYear = 1;
149 1.25 dholland param->yyMonth = $3;
150 1.25 dholland param->yyDay = $5;
151 1.25 dholland param->yyHour = $7;
152 1.25 dholland param->yyMinutes = $9;
153 1.25 dholland param->yySeconds = $11;
154 1.25 dholland param->yyDSTmode = DSToff;
155 1.25 dholland param->yyTimezone = 0;
156 1.25 dholland }
157 1.25 dholland ;
158 1.25 dholland
159 1.25 dholland epochdate:
160 1.25 dholland AT_SIGN at_number {
161 1.25 dholland time_t when = $2;
162 1.25 dholland struct tm tmbuf;
163 1.25 dholland
164 1.25 dholland if (gmtime_r(&when, &tmbuf) != NULL) {
165 1.25 dholland param->yyYear = tmbuf.tm_year + 1900;
166 1.25 dholland param->yyMonth = tmbuf.tm_mon + 1;
167 1.25 dholland param->yyDay = tmbuf.tm_mday;
168 1.25 dholland
169 1.25 dholland param->yyHour = tmbuf.tm_hour;
170 1.25 dholland param->yyMinutes = tmbuf.tm_min;
171 1.25 dholland param->yySeconds = tmbuf.tm_sec;
172 1.25 dholland } else {
173 1.25 dholland param->yyYear = EPOCH;
174 1.25 dholland param->yyMonth = 1;
175 1.25 dholland param->yyDay = 1;
176 1.1 christos
177 1.25 dholland param->yyHour = 0;
178 1.25 dholland param->yyMinutes = 0;
179 1.25 dholland param->yySeconds = 0;
180 1.25 dholland }
181 1.25 dholland param->yyHaveFullYear = 1;
182 1.25 dholland param->yyDSTmode = DSToff;
183 1.25 dholland param->yyTimezone = 0;
184 1.25 dholland }
185 1.25 dholland ;
186 1.25 dholland
187 1.25 dholland at_number:
188 1.25 dholland tUNUMBER
189 1.25 dholland | tSNUMBER
190 1.25 dholland ;
191 1.25 dholland
192 1.25 dholland time:
193 1.25 dholland tUNUMBER tMERIDIAN {
194 1.9 christos param->yyMinutes = 0;
195 1.9 christos param->yySeconds = 0;
196 1.31 kre if ($2 == MER_NOON || $2 == MER_MN) {
197 1.31 kre if ($1 == 12) {
198 1.31 kre switch ($2) {
199 1.31 kre case MER_NOON: param->yyHour = 12; break;
200 1.31 kre case MER_MN : param->yyHour = 0; break;
201 1.31 kre default: /* impossible */; break;
202 1.31 kre }
203 1.31 kre param->yyMeridian = MER24;
204 1.31 kre } else
205 1.31 kre YYREJECT;
206 1.31 kre } else {
207 1.31 kre param->yyHour = $1;
208 1.31 kre param->yyMeridian = $2;
209 1.31 kre }
210 1.25 dholland }
211 1.1 christos | tUNUMBER ':' tUNUMBER o_merid {
212 1.25 dholland param->yyMinutes = $3;
213 1.25 dholland param->yySeconds = 0;
214 1.31 kre if ($4 == MER_NOON || $4 == MER_MN) {
215 1.31 kre if ($1 == 12 && $3 == 0) {
216 1.31 kre switch ($4) {
217 1.31 kre case MER_NOON: param->yyHour = 12; break;
218 1.31 kre case MER_MN : param->yyHour = 0; break;
219 1.31 kre default: /* impossible */; break;
220 1.31 kre }
221 1.31 kre param->yyMeridian = MER24;
222 1.31 kre } else
223 1.31 kre YYREJECT;
224 1.31 kre } else {
225 1.31 kre param->yyHour = $1;
226 1.31 kre param->yyMeridian = $4;
227 1.31 kre }
228 1.25 dholland }
229 1.19 apb | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
230 1.25 dholland param->yyMinutes = $3;
231 1.25 dholland param->yySeconds = $5;
232 1.31 kre if ($6 == MER_NOON || $6 == MER_MN) {
233 1.31 kre if ($1 == 12 && $3 == 0 && $5 == 0) {
234 1.31 kre switch ($6) {
235 1.31 kre case MER_NOON: param->yyHour = 12; break;
236 1.31 kre case MER_MN : param->yyHour = 0; break;
237 1.31 kre default: /* impossible */; break;
238 1.31 kre }
239 1.31 kre param->yyMeridian = MER24;
240 1.31 kre } else
241 1.31 kre YYREJECT;
242 1.31 kre } else {
243 1.31 kre param->yyHour = $1;
244 1.31 kre param->yyMeridian = $6;
245 1.31 kre }
246 1.25 dholland }
247 1.19 apb | tUNUMBER ':' tUNUMBER ':' tUNUMBER '.' tUNUMBER {
248 1.25 dholland param->yyHour = $1;
249 1.25 dholland param->yyMinutes = $3;
250 1.25 dholland param->yySeconds = $5;
251 1.25 dholland param->yyMeridian = MER24;
252 1.25 dholland /* XXX: Do nothing with millis */
253 1.25 dholland }
254 1.23 christos | tTIME {
255 1.25 dholland param->yyHour = $1;
256 1.25 dholland param->yyMinutes = 0;
257 1.25 dholland param->yySeconds = 0;
258 1.25 dholland param->yyMeridian = MER24;
259 1.25 dholland /* Tues midnight --> Weds 00:00, midnight Tues -> Tues 00:00 */
260 1.25 dholland if ($1 == 0 && param->yyHaveDay)
261 1.25 dholland param->yyDayNumber++;
262 1.31 kre }
263 1.31 kre | tUNUMBER tTIME {
264 1.31 kre if ($1 == 12 && ($2 == 0 || $2 == 12)) {
265 1.31 kre param->yyHour = $2;
266 1.31 kre param->yyMinutes = 0;
267 1.31 kre param->yySeconds = 0;
268 1.31 kre param->yyMeridian = MER24;
269 1.31 kre } else
270 1.31 kre YYREJECT;
271 1.31 kre }
272 1.25 dholland ;
273 1.25 dholland
274 1.25 dholland time_numericzone:
275 1.25 dholland tUNUMBER ':' tUNUMBER tSNUMBER {
276 1.25 dholland param->yyHour = $1;
277 1.25 dholland param->yyMinutes = $3;
278 1.25 dholland param->yyMeridian = MER24;
279 1.25 dholland param->yyDSTmode = DSToff;
280 1.25 dholland param->yyTimezone = - ($4 % 100 + ($4 / 100) * 60);
281 1.25 dholland }
282 1.19 apb | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER {
283 1.25 dholland param->yyHour = $1;
284 1.25 dholland param->yyMinutes = $3;
285 1.25 dholland param->yySeconds = $5;
286 1.25 dholland param->yyMeridian = MER24;
287 1.25 dholland param->yyDSTmode = DSToff;
288 1.25 dholland param->yyTimezone = - ($6 % 100 + ($6 / 100) * 60);
289 1.25 dholland }
290 1.25 dholland ;
291 1.25 dholland
292 1.25 dholland zone:
293 1.25 dholland tZONE { param->yyTimezone = $1; param->yyDSTmode = DSToff; }
294 1.25 dholland | tDAYZONE { param->yyTimezone = $1; param->yyDSTmode = DSTon; }
295 1.25 dholland | tZONE tDST { param->yyTimezone = $1; param->yyDSTmode = DSTon; }
296 1.25 dholland ;
297 1.25 dholland
298 1.25 dholland day:
299 1.25 dholland tDAY { param->yyDayOrdinal = 1; param->yyDayNumber = $1; }
300 1.25 dholland | tDAY ',' { param->yyDayOrdinal = 1; param->yyDayNumber = $1; }
301 1.25 dholland | tUNUMBER tDAY { param->yyDayOrdinal = $1; param->yyDayNumber = $2; }
302 1.25 dholland ;
303 1.1 christos
304 1.25 dholland date:
305 1.25 dholland tUNUMBER '/' tUNUMBER {
306 1.9 christos param->yyMonth = $1;
307 1.9 christos param->yyDay = $3;
308 1.25 dholland }
309 1.25 dholland | tUNUMBER '/' tUNUMBER '/' tUNUMBER {
310 1.25 dholland if ($1 >= 100) {
311 1.25 dholland param->yyYear = $1;
312 1.25 dholland param->yyMonth = $3;
313 1.25 dholland param->yyDay = $5;
314 1.25 dholland } else {
315 1.25 dholland param->yyMonth = $1;
316 1.25 dholland param->yyDay = $3;
317 1.25 dholland param->yyYear = $5;
318 1.25 dholland }
319 1.25 dholland }
320 1.1 christos | tUNUMBER tSNUMBER tSNUMBER {
321 1.25 dholland /* ISO 8601 format. yyyy-mm-dd. */
322 1.25 dholland param->yyYear = $1;
323 1.25 dholland param->yyHaveFullYear = 1;
324 1.25 dholland param->yyMonth = -$2;
325 1.25 dholland param->yyDay = -$3;
326 1.25 dholland }
327 1.1 christos | tUNUMBER tMONTH tSNUMBER {
328 1.25 dholland /* e.g. 17-JUN-1992. */
329 1.25 dholland param->yyDay = $1;
330 1.25 dholland param->yyMonth = $2;
331 1.25 dholland param->yyYear = -$3;
332 1.25 dholland }
333 1.1 christos | tMONTH tUNUMBER {
334 1.25 dholland param->yyMonth = $1;
335 1.25 dholland param->yyDay = $2;
336 1.25 dholland }
337 1.1 christos | tMONTH tUNUMBER ',' tUNUMBER {
338 1.25 dholland param->yyMonth = $1;
339 1.25 dholland param->yyDay = $2;
340 1.25 dholland param->yyYear = $4;
341 1.25 dholland }
342 1.1 christos | tUNUMBER tMONTH {
343 1.25 dholland param->yyMonth = $2;
344 1.25 dholland param->yyDay = $1;
345 1.25 dholland }
346 1.1 christos | tUNUMBER tMONTH tUNUMBER {
347 1.25 dholland param->yyMonth = $2;
348 1.25 dholland if ($1 < 35) {
349 1.25 dholland param->yyDay = $1;
350 1.25 dholland param->yyYear = $3;
351 1.25 dholland } else {
352 1.25 dholland param->yyDay = $3;
353 1.25 dholland param->yyYear = $1;
354 1.25 dholland }
355 1.25 dholland }
356 1.25 dholland ;
357 1.1 christos
358 1.25 dholland rel:
359 1.25 dholland relunit
360 1.25 dholland | relunit tAGO {
361 1.28 kre param->yyRel[param->yyHaveRel].yyRelVal =
362 1.28 kre -param->yyRel[param->yyHaveRel].yyRelVal;
363 1.25 dholland }
364 1.25 dholland ;
365 1.25 dholland
366 1.25 dholland relunit:
367 1.28 kre tUNUMBER tMINUTE_UNIT { RelVal(param, $1 * $2 * 60L, 0); }
368 1.28 kre | tSNUMBER tMINUTE_UNIT { RelVal(param, $1 * $2 * 60L, 0); }
369 1.28 kre | tMINUTE_UNIT { RelVal(param, $1 * 60L, 0); }
370 1.28 kre | tSNUMBER tSEC_UNIT { RelVal(param, $1, 0); }
371 1.28 kre | tUNUMBER tSEC_UNIT { RelVal(param, $1, 0); }
372 1.28 kre | tSEC_UNIT { RelVal(param, 1L, 0); }
373 1.28 kre | tSNUMBER tMONTH_UNIT { RelVal(param, $1 * $2, 1); }
374 1.28 kre | tUNUMBER tMONTH_UNIT { RelVal(param, $1 * $2, 1); }
375 1.28 kre | tMONTH_UNIT { RelVal(param, $1, 1); }
376 1.25 dholland ;
377 1.25 dholland
378 1.25 dholland number:
379 1.25 dholland tUNUMBER {
380 1.25 dholland if (param->yyHaveTime && param->yyHaveDate &&
381 1.25 dholland !param->yyHaveRel) {
382 1.25 dholland param->yyYear = $1;
383 1.25 dholland } else {
384 1.25 dholland if ($1 > 10000) {
385 1.25 dholland param->yyHaveDate++;
386 1.25 dholland param->yyDay = ($1)%100;
387 1.25 dholland param->yyMonth = ($1/100)%100;
388 1.25 dholland param->yyYear = $1/10000;
389 1.25 dholland }
390 1.25 dholland else {
391 1.25 dholland param->yyHaveTime++;
392 1.25 dholland if ($1 < 100) {
393 1.25 dholland param->yyHour = $1;
394 1.25 dholland param->yyMinutes = 0;
395 1.25 dholland }
396 1.25 dholland else {
397 1.25 dholland param->yyHour = $1 / 100;
398 1.25 dholland param->yyMinutes = $1 % 100;
399 1.25 dholland }
400 1.25 dholland param->yySeconds = 0;
401 1.25 dholland param->yyMeridian = MER24;
402 1.25 dholland }
403 1.1 christos }
404 1.1 christos }
405 1.25 dholland ;
406 1.1 christos
407 1.25 dholland o_merid:
408 1.25 dholland /* empty */ { $$ = MER24; }
409 1.25 dholland | tMERIDIAN { $$ = $1; }
410 1.31 kre | tTIME { $$ = $1 == 0 ? MER_MN : MER_NOON; }
411 1.25 dholland ;
412 1.1 christos
413 1.1 christos %%
414 1.1 christos
415 1.28 kre static short DaysInMonth[12] = {
416 1.28 kre 31, 28, 31, 30, 31, 30,
417 1.28 kre 31, 31, 30, 31, 30, 31
418 1.28 kre };
419 1.28 kre
420 1.28 kre /*
421 1.28 kre * works with tm.tm_year (ie: rel to 1900)
422 1.28 kre */
423 1.28 kre #define isleap(yr) (((yr) & 3) == 0 && (((yr) % 100) != 0 || \
424 1.28 kre ((1900+(yr)) % 400) == 0))
425 1.28 kre
426 1.1 christos /* Month and day table. */
427 1.12 joerg static const TABLE MonthDayTable[] = {
428 1.1 christos { "january", tMONTH, 1 },
429 1.1 christos { "february", tMONTH, 2 },
430 1.1 christos { "march", tMONTH, 3 },
431 1.1 christos { "april", tMONTH, 4 },
432 1.1 christos { "may", tMONTH, 5 },
433 1.1 christos { "june", tMONTH, 6 },
434 1.1 christos { "july", tMONTH, 7 },
435 1.1 christos { "august", tMONTH, 8 },
436 1.1 christos { "september", tMONTH, 9 },
437 1.1 christos { "sept", tMONTH, 9 },
438 1.1 christos { "october", tMONTH, 10 },
439 1.1 christos { "november", tMONTH, 11 },
440 1.1 christos { "december", tMONTH, 12 },
441 1.1 christos { "sunday", tDAY, 0 },
442 1.21 christos { "su", tDAY, 0 },
443 1.1 christos { "monday", tDAY, 1 },
444 1.21 christos { "mo", tDAY, 1 },
445 1.1 christos { "tuesday", tDAY, 2 },
446 1.1 christos { "tues", tDAY, 2 },
447 1.21 christos { "tu", tDAY, 2 },
448 1.1 christos { "wednesday", tDAY, 3 },
449 1.1 christos { "wednes", tDAY, 3 },
450 1.21 christos { "weds", tDAY, 3 },
451 1.21 christos { "we", tDAY, 3 },
452 1.1 christos { "thursday", tDAY, 4 },
453 1.21 christos { "thurs", tDAY, 4 },
454 1.1 christos { "thur", tDAY, 4 },
455 1.21 christos { "th", tDAY, 4 },
456 1.1 christos { "friday", tDAY, 5 },
457 1.21 christos { "fr", tDAY, 5 },
458 1.1 christos { "saturday", tDAY, 6 },
459 1.21 christos { "sa", tDAY, 6 },
460 1.1 christos { NULL, 0, 0 }
461 1.1 christos };
462 1.1 christos
463 1.1 christos /* Time units table. */
464 1.12 joerg static const TABLE UnitsTable[] = {
465 1.1 christos { "year", tMONTH_UNIT, 12 },
466 1.1 christos { "month", tMONTH_UNIT, 1 },
467 1.1 christos { "fortnight", tMINUTE_UNIT, 14 * 24 * 60 },
468 1.1 christos { "week", tMINUTE_UNIT, 7 * 24 * 60 },
469 1.1 christos { "day", tMINUTE_UNIT, 1 * 24 * 60 },
470 1.1 christos { "hour", tMINUTE_UNIT, 60 },
471 1.1 christos { "minute", tMINUTE_UNIT, 1 },
472 1.1 christos { "min", tMINUTE_UNIT, 1 },
473 1.1 christos { "second", tSEC_UNIT, 1 },
474 1.1 christos { "sec", tSEC_UNIT, 1 },
475 1.1 christos { NULL, 0, 0 }
476 1.1 christos };
477 1.1 christos
478 1.1 christos /* Assorted relative-time words. */
479 1.12 joerg static const TABLE OtherTable[] = {
480 1.1 christos { "tomorrow", tMINUTE_UNIT, 1 * 24 * 60 },
481 1.1 christos { "yesterday", tMINUTE_UNIT, -1 * 24 * 60 },
482 1.1 christos { "today", tMINUTE_UNIT, 0 },
483 1.1 christos { "now", tMINUTE_UNIT, 0 },
484 1.1 christos { "last", tUNUMBER, -1 },
485 1.1 christos { "this", tMINUTE_UNIT, 0 },
486 1.1 christos { "next", tUNUMBER, 2 },
487 1.1 christos { "first", tUNUMBER, 1 },
488 1.7 christos { "one", tUNUMBER, 1 },
489 1.1 christos /* { "second", tUNUMBER, 2 }, */
490 1.7 christos { "two", tUNUMBER, 2 },
491 1.1 christos { "third", tUNUMBER, 3 },
492 1.7 christos { "three", tUNUMBER, 3 },
493 1.1 christos { "fourth", tUNUMBER, 4 },
494 1.7 christos { "four", tUNUMBER, 4 },
495 1.1 christos { "fifth", tUNUMBER, 5 },
496 1.7 christos { "five", tUNUMBER, 5 },
497 1.1 christos { "sixth", tUNUMBER, 6 },
498 1.7 christos { "six", tUNUMBER, 6 },
499 1.1 christos { "seventh", tUNUMBER, 7 },
500 1.7 christos { "seven", tUNUMBER, 7 },
501 1.1 christos { "eighth", tUNUMBER, 8 },
502 1.7 christos { "eight", tUNUMBER, 8 },
503 1.1 christos { "ninth", tUNUMBER, 9 },
504 1.7 christos { "nine", tUNUMBER, 9 },
505 1.1 christos { "tenth", tUNUMBER, 10 },
506 1.7 christos { "ten", tUNUMBER, 10 },
507 1.1 christos { "eleventh", tUNUMBER, 11 },
508 1.7 christos { "eleven", tUNUMBER, 11 },
509 1.1 christos { "twelfth", tUNUMBER, 12 },
510 1.7 christos { "twelve", tUNUMBER, 12 },
511 1.1 christos { "ago", tAGO, 1 },
512 1.1 christos { NULL, 0, 0 }
513 1.1 christos };
514 1.1 christos
515 1.1 christos /* The timezone table. */
516 1.1 christos /* Some of these are commented out because a time_t can't store a float. */
517 1.12 joerg static const TABLE TimezoneTable[] = {
518 1.1 christos { "gmt", tZONE, HOUR( 0) }, /* Greenwich Mean */
519 1.1 christos { "ut", tZONE, HOUR( 0) }, /* Universal (Coordinated) */
520 1.1 christos { "utc", tZONE, HOUR( 0) },
521 1.1 christos { "wet", tZONE, HOUR( 0) }, /* Western European */
522 1.1 christos { "bst", tDAYZONE, HOUR( 0) }, /* British Summer */
523 1.1 christos { "wat", tZONE, HOUR( 1) }, /* West Africa */
524 1.1 christos { "at", tZONE, HOUR( 2) }, /* Azores */
525 1.1 christos #if 0
526 1.1 christos /* For completeness. BST is also British Summer, and GST is
527 1.1 christos * also Guam Standard. */
528 1.1 christos { "bst", tZONE, HOUR( 3) }, /* Brazil Standard */
529 1.1 christos { "gst", tZONE, HOUR( 3) }, /* Greenland Standard */
530 1.1 christos #endif
531 1.1 christos { "nft", tZONE, HOUR(3.5) }, /* Newfoundland */
532 1.1 christos { "nst", tZONE, HOUR(3.5) }, /* Newfoundland Standard */
533 1.1 christos { "ndt", tDAYZONE, HOUR(3.5) }, /* Newfoundland Daylight */
534 1.1 christos { "ast", tZONE, HOUR( 4) }, /* Atlantic Standard */
535 1.1 christos { "adt", tDAYZONE, HOUR( 4) }, /* Atlantic Daylight */
536 1.1 christos { "est", tZONE, HOUR( 5) }, /* Eastern Standard */
537 1.1 christos { "edt", tDAYZONE, HOUR( 5) }, /* Eastern Daylight */
538 1.1 christos { "cst", tZONE, HOUR( 6) }, /* Central Standard */
539 1.1 christos { "cdt", tDAYZONE, HOUR( 6) }, /* Central Daylight */
540 1.1 christos { "mst", tZONE, HOUR( 7) }, /* Mountain Standard */
541 1.1 christos { "mdt", tDAYZONE, HOUR( 7) }, /* Mountain Daylight */
542 1.1 christos { "pst", tZONE, HOUR( 8) }, /* Pacific Standard */
543 1.1 christos { "pdt", tDAYZONE, HOUR( 8) }, /* Pacific Daylight */
544 1.1 christos { "yst", tZONE, HOUR( 9) }, /* Yukon Standard */
545 1.1 christos { "ydt", tDAYZONE, HOUR( 9) }, /* Yukon Daylight */
546 1.1 christos { "hst", tZONE, HOUR(10) }, /* Hawaii Standard */
547 1.1 christos { "hdt", tDAYZONE, HOUR(10) }, /* Hawaii Daylight */
548 1.1 christos { "cat", tZONE, HOUR(10) }, /* Central Alaska */
549 1.1 christos { "ahst", tZONE, HOUR(10) }, /* Alaska-Hawaii Standard */
550 1.1 christos { "nt", tZONE, HOUR(11) }, /* Nome */
551 1.1 christos { "idlw", tZONE, HOUR(12) }, /* International Date Line West */
552 1.1 christos { "cet", tZONE, -HOUR(1) }, /* Central European */
553 1.1 christos { "met", tZONE, -HOUR(1) }, /* Middle European */
554 1.1 christos { "mewt", tZONE, -HOUR(1) }, /* Middle European Winter */
555 1.1 christos { "mest", tDAYZONE, -HOUR(1) }, /* Middle European Summer */
556 1.1 christos { "swt", tZONE, -HOUR(1) }, /* Swedish Winter */
557 1.1 christos { "sst", tDAYZONE, -HOUR(1) }, /* Swedish Summer */
558 1.1 christos { "fwt", tZONE, -HOUR(1) }, /* French Winter */
559 1.1 christos { "fst", tDAYZONE, -HOUR(1) }, /* French Summer */
560 1.1 christos { "eet", tZONE, -HOUR(2) }, /* Eastern Europe, USSR Zone 1 */
561 1.1 christos { "bt", tZONE, -HOUR(3) }, /* Baghdad, USSR Zone 2 */
562 1.1 christos { "it", tZONE, -HOUR(3.5) },/* Iran */
563 1.1 christos { "zp4", tZONE, -HOUR(4) }, /* USSR Zone 3 */
564 1.1 christos { "zp5", tZONE, -HOUR(5) }, /* USSR Zone 4 */
565 1.1 christos { "ist", tZONE, -HOUR(5.5) },/* Indian Standard */
566 1.1 christos { "zp6", tZONE, -HOUR(6) }, /* USSR Zone 5 */
567 1.1 christos #if 0
568 1.1 christos /* For completeness. NST is also Newfoundland Stanard, and SST is
569 1.1 christos * also Swedish Summer. */
570 1.1 christos { "nst", tZONE, -HOUR(6.5) },/* North Sumatra */
571 1.1 christos { "sst", tZONE, -HOUR(7) }, /* South Sumatra, USSR Zone 6 */
572 1.1 christos #endif /* 0 */
573 1.21 christos { "ict", tZONE, -HOUR(7) }, /* Indo China Time (Thai) */
574 1.21 christos #if 0 /* this one looks to be bogus */
575 1.1 christos { "jt", tZONE, -HOUR(7.5) },/* Java (3pm in Cronusland!) */
576 1.1 christos #endif
577 1.22 christos { "wast", tZONE, -HOUR(8) }, /* West Australian Standard */
578 1.22 christos { "awst", tZONE, -HOUR(8) }, /* West Australian Standard */
579 1.22 christos { "wadt", tDAYZONE, -HOUR(8) }, /* West Australian Daylight */
580 1.22 christos { "awdt", tDAYZONE, -HOUR(8) }, /* West Australian Daylight */
581 1.1 christos { "cct", tZONE, -HOUR(8) }, /* China Coast, USSR Zone 7 */
582 1.21 christos { "sgt", tZONE, -HOUR(8) }, /* Singapore */
583 1.21 christos { "hkt", tZONE, -HOUR(8) }, /* Hong Kong */
584 1.1 christos { "jst", tZONE, -HOUR(9) }, /* Japan Standard, USSR Zone 8 */
585 1.1 christos { "cast", tZONE, -HOUR(9.5) },/* Central Australian Standard */
586 1.21 christos { "acst", tZONE, -HOUR(9.5) },/* Central Australian Standard */
587 1.1 christos { "cadt", tDAYZONE, -HOUR(9.5) },/* Central Australian Daylight */
588 1.21 christos { "acdt", tDAYZONE, -HOUR(9.5) },/* Central Australian Daylight */
589 1.1 christos { "east", tZONE, -HOUR(10) }, /* Eastern Australian Standard */
590 1.21 christos { "aest", tZONE, -HOUR(10) }, /* Eastern Australian Standard */
591 1.1 christos { "eadt", tDAYZONE, -HOUR(10) }, /* Eastern Australian Daylight */
592 1.21 christos { "aedt", tDAYZONE, -HOUR(10) }, /* Eastern Australian Daylight */
593 1.1 christos { "gst", tZONE, -HOUR(10) }, /* Guam Standard, USSR Zone 9 */
594 1.1 christos { "nzt", tZONE, -HOUR(12) }, /* New Zealand */
595 1.1 christos { "nzst", tZONE, -HOUR(12) }, /* New Zealand Standard */
596 1.1 christos { "nzdt", tDAYZONE, -HOUR(12) }, /* New Zealand Daylight */
597 1.1 christos { "idle", tZONE, -HOUR(12) }, /* International Date Line East */
598 1.1 christos { NULL, 0, 0 }
599 1.1 christos };
600 1.1 christos
601 1.1 christos /* Military timezone table. */
602 1.12 joerg static const TABLE MilitaryTable[] = {
603 1.1 christos { "a", tZONE, HOUR( 1) },
604 1.1 christos { "b", tZONE, HOUR( 2) },
605 1.1 christos { "c", tZONE, HOUR( 3) },
606 1.1 christos { "d", tZONE, HOUR( 4) },
607 1.1 christos { "e", tZONE, HOUR( 5) },
608 1.1 christos { "f", tZONE, HOUR( 6) },
609 1.1 christos { "g", tZONE, HOUR( 7) },
610 1.1 christos { "h", tZONE, HOUR( 8) },
611 1.1 christos { "i", tZONE, HOUR( 9) },
612 1.1 christos { "k", tZONE, HOUR( 10) },
613 1.1 christos { "l", tZONE, HOUR( 11) },
614 1.1 christos { "m", tZONE, HOUR( 12) },
615 1.1 christos { "n", tZONE, HOUR(- 1) },
616 1.1 christos { "o", tZONE, HOUR(- 2) },
617 1.1 christos { "p", tZONE, HOUR(- 3) },
618 1.1 christos { "q", tZONE, HOUR(- 4) },
619 1.1 christos { "r", tZONE, HOUR(- 5) },
620 1.1 christos { "s", tZONE, HOUR(- 6) },
621 1.1 christos { "t", tZONE, HOUR(- 7) },
622 1.1 christos { "u", tZONE, HOUR(- 8) },
623 1.1 christos { "v", tZONE, HOUR(- 9) },
624 1.1 christos { "w", tZONE, HOUR(-10) },
625 1.1 christos { "x", tZONE, HOUR(-11) },
626 1.1 christos { "y", tZONE, HOUR(-12) },
627 1.1 christos { "z", tZONE, HOUR( 0) },
628 1.1 christos { NULL, 0, 0 }
629 1.1 christos };
630 1.1 christos
631 1.23 christos static const TABLE TimeNames[] = {
632 1.23 christos { "midnight", tTIME, 0 },
633 1.23 christos { "mn", tTIME, 0 },
634 1.23 christos { "noon", tTIME, 12 },
635 1.28 kre { "midday", tTIME, 12 },
636 1.23 christos { NULL, 0, 0 }
637 1.23 christos };
638 1.23 christos
639 1.1 christos
640 1.1 christos
642 1.1 christos /* ARGSUSED */
643 1.9 christos static int
644 1.1 christos yyerror(struct dateinfo *param, const char **inp, const char *s __unused)
645 1.1 christos {
646 1.1 christos return 0;
647 1.1 christos }
648 1.28 kre
649 1.28 kre /*
650 1.28 kre * Save a relative value, if it fits
651 1.28 kre */
652 1.28 kre static void
653 1.28 kre RelVal(struct dateinfo *param, time_t v, int type)
654 1.28 kre {
655 1.28 kre int i;
656 1.28 kre
657 1.28 kre if ((i = param->yyHaveRel) >= MAXREL)
658 1.28 kre return;
659 1.28 kre param->yyRel[i].yyRelMonth = type;
660 1.28 kre param->yyRel[i].yyRelVal = v;
661 1.28 kre }
662 1.32 kre
663 1.32 kre /*
664 1.17 apb * Adjust year from a value that might be abbreviated, to a full value.
665 1.17 apb * e.g. convert 70 to 1970.
666 1.17 apb * Input Year is either:
667 1.17 apb * - A negative number, which means to use its absolute value (why?)
668 1.17 apb * - A number from 0 to 99, which means a year from 1900 to 1999, or
669 1.32 kre * - The actual year (>=100).
670 1.32 kre * Returns the full year.
671 1.17 apb */
672 1.17 apb static time_t
673 1.17 apb AdjustYear(time_t Year)
674 1.17 apb {
675 1.17 apb /* XXX Y2K */
676 1.17 apb if (Year < 0)
677 1.17 apb Year = -Year;
678 1.17 apb if (Year < 70)
679 1.17 apb Year += 2000;
680 1.17 apb else if (Year < 100)
681 1.17 apb Year += 1900;
682 1.17 apb return Year;
683 1.17 apb }
684 1.1 christos
685 1.1 christos static time_t
686 1.11 apb Convert(
687 1.11 apb time_t Month, /* month of year [1-12] */
688 1.17 apb time_t Day, /* day of month [1-31] */
689 1.11 apb time_t Year, /* year, not abbreviated in any way */
690 1.11 apb time_t Hours, /* Hour of day [0-24] */
691 1.11 apb time_t Minutes, /* Minute of hour [0-59] */
692 1.20 apb time_t Seconds, /* Second of minute [0-60] */
693 1.20 apb time_t Timezone, /* Timezone as minutes east of UTC,
694 1.11 apb * or USE_LOCAL_TIME special case */
695 1.11 apb MERIDIAN Meridian, /* Hours are am/pm/24 hour clock */
696 1.1 christos DSTMODE DSTmode /* DST on/off/maybe */
697 1.1 christos )
698 1.13 apb {
699 1.23 christos struct tm tm = {.tm_sec = 0};
700 1.13 apb struct tm otm;
701 1.1 christos time_t result;
702 1.11 apb
703 1.11 apb tm.tm_sec = Seconds;
704 1.30 kre tm.tm_min = Minutes;
705 1.30 kre tm.tm_hour = ((Hours == 12 && Meridian != MER24) ? 0 : Hours) +
706 1.30 kre (Meridian == MERpm ? 12 : 0);
707 1.11 apb
708 1.11 apb tm.tm_mday = Day;
709 1.11 apb tm.tm_mon = Month - 1;
710 1.20 apb tm.tm_year = Year - 1900;
711 1.21 christos if (Timezone == USE_LOCAL_TIME) {
712 1.21 christos switch (DSTmode) {
713 1.21 christos case DSTon: tm.tm_isdst = 1; break;
714 1.21 christos case DSToff: tm.tm_isdst = 0; break;
715 1.21 christos default: tm.tm_isdst = -1; break;
716 1.24 christos }
717 1.20 apb otm = tm;
718 1.20 apb result = mktime(&tm);
719 1.20 apb } else {
720 1.21 christos /* We rely on mktime_z(NULL, ...) working in UTC */
721 1.24 christos tm.tm_isdst = 0; /* hence cannot be summer time */
722 1.21 christos otm = tm;
723 1.20 apb errno = 0;
724 1.21 christos result = mktime_z(NULL, &tm);
725 1.21 christos if (result != -1 || errno == 0) {
726 1.21 christos result += Timezone * 60;
727 1.21 christos if (DSTmode == DSTon) /* if specified sumer time */
728 1.21 christos result -= 3600; /* UTC is 1 hour earlier XXX */
729 1.20 apb }
730 1.20 apb }
731 1.20 apb
732 1.20 apb #if PARSEDATE_DEBUG
733 1.20 apb fprintf(stderr, "%s(M=%jd D=%jd Y=%jd H=%jd M=%jd S=%jd Z=%jd"
734 1.20 apb " mer=%d DST=%d)",
735 1.20 apb __func__,
736 1.20 apb (intmax_t)Month, (intmax_t)Day, (intmax_t)Year,
737 1.20 apb (intmax_t)Hours, (intmax_t)Minutes, (intmax_t)Seconds,
738 1.20 apb (intmax_t)Timezone, (int)Meridian, (int)DSTmode);
739 1.20 apb fprintf(stderr, " -> %jd", (intmax_t)result);
740 1.20 apb fprintf(stderr, " %s", ctime(&result));
741 1.20 apb #endif
742 1.23 christos
743 1.23 christos #define TM_NE(fld) (otm.tm_ ## fld != tm.tm_ ## fld)
744 1.23 christos if (TM_NE(year) || TM_NE(mon) || TM_NE(mday) ||
745 1.23 christos TM_NE(hour) || TM_NE(min) || TM_NE(sec)) {
746 1.23 christos /* mktime() "corrected" our tm, so it must have been invalid */
747 1.23 christos result = -1;
748 1.23 christos errno = EAGAIN;
749 1.23 christos }
750 1.23 christos #undef TM_NE
751 1.13 apb
752 1.1 christos return result;
753 1.1 christos }
754 1.1 christos
755 1.1 christos
756 1.1 christos static time_t
757 1.1 christos DSTcorrect(
758 1.1 christos time_t Start,
759 1.1 christos time_t Future
760 1.1 christos )
761 1.1 christos {
762 1.1 christos time_t StartDay;
763 1.26 dholland time_t FutureDay;
764 1.6 christos struct tm tm;
765 1.26 dholland
766 1.6 christos if (localtime_r(&Start, &tm) == NULL)
767 1.26 dholland return -1;
768 1.6 christos StartDay = (tm.tm_hour + 1) % 24;
769 1.26 dholland
770 1.6 christos if (localtime_r(&Future, &tm) == NULL)
771 1.26 dholland return -1;
772 1.1 christos FutureDay = (tm.tm_hour + 1) % 24;
773 1.1 christos
774 1.1 christos return (Future - Start) + (StartDay - FutureDay) * 60L * 60L;
775 1.1 christos }
776 1.1 christos
777 1.1 christos
778 1.1 christos static time_t
779 1.1 christos RelativeDate(
780 1.1 christos time_t Start,
781 1.1 christos time_t DayOrdinal,
782 1.1 christos time_t DayNumber
783 1.1 christos )
784 1.26 dholland {
785 1.1 christos struct tm tm;
786 1.1 christos time_t now;
787 1.1 christos
788 1.26 dholland now = Start;
789 1.21 christos if (localtime_r(&now, &tm) == NULL)
790 1.26 dholland return -1;
791 1.1 christos now += SECSPERDAY * ((DayNumber - tm.tm_wday + 7) % 7);
792 1.1 christos now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1);
793 1.1 christos return DSTcorrect(Start, now);
794 1.1 christos }
795 1.1 christos
796 1.1 christos
797 1.1 christos static time_t
798 1.1 christos RelativeMonth(
799 1.9 christos time_t Start,
800 1.9 christos time_t RelMonth,
801 1.1 christos time_t Timezone
802 1.1 christos )
803 1.26 dholland {
804 1.1 christos struct tm tm;
805 1.27 dholland time_t Month;
806 1.28 kre time_t Then;
807 1.1 christos int Day;
808 1.1 christos
809 1.1 christos if (RelMonth == 0)
810 1.27 dholland return 0;
811 1.27 dholland /*
812 1.27 dholland * It doesn't matter what timezone we use to do this computation,
813 1.27 dholland * as long as we use the same one to reassemble the time that we
814 1.27 dholland * used to disassemble it. So always use localtime and mktime. In
815 1.27 dholland * particular, don't use Convert() to reassemble, because it will
816 1.27 dholland * not only reassemble with the wrong timezone but it will also
817 1.27 dholland * fail if we do e.g. three months from March 31 yielding July 1.
818 1.27 dholland */
819 1.27 dholland (void)Timezone;
820 1.26 dholland
821 1.6 christos if (localtime_r(&Start, &tm) == NULL)
822 1.27 dholland return -1;
823 1.26 dholland
824 1.27 dholland Month = 12 * (tm.tm_year + 1900) + tm.tm_mon + RelMonth;
825 1.27 dholland tm.tm_year = (Month / 12) - 1900;
826 1.28 kre tm.tm_mon = Month % 12;
827 1.28 kre if (tm.tm_mday > (Day = DaysInMonth[tm.tm_mon] +
828 1.28 kre ((tm.tm_mon==1) ? isleap(tm.tm_year) : 0)))
829 1.27 dholland tm.tm_mday = Day;
830 1.27 dholland errno = 0;
831 1.27 dholland Then = mktime(&tm);
832 1.27 dholland if (Then == -1 && errno != 0)
833 1.27 dholland return -1;
834 1.1 christos return DSTcorrect(Start, Then);
835 1.1 christos }
836 1.1 christos
837 1.1 christos
838 1.9 christos static int
839 1.1 christos LookupWord(YYSTYPE *yylval, char *buff)
840 1.1 christos {
841 1.1 christos register char *p;
842 1.1 christos register char *q;
843 1.1 christos register const TABLE *tp;
844 1.1 christos int i;
845 1.1 christos int abbrev;
846 1.1 christos
847 1.1 christos /* Make it lowercase. */
848 1.1 christos for (p = buff; *p; p++)
849 1.1 christos if (isupper((unsigned char)*p))
850 1.1 christos *p = tolower((unsigned char)*p);
851 1.1 christos
852 1.9 christos if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) {
853 1.1 christos yylval->Meridian = MERam;
854 1.1 christos return tMERIDIAN;
855 1.1 christos }
856 1.9 christos if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) {
857 1.1 christos yylval->Meridian = MERpm;
858 1.1 christos return tMERIDIAN;
859 1.1 christos }
860 1.1 christos
861 1.1 christos /* See if we have an abbreviation for a month. */
862 1.1 christos if (strlen(buff) == 3)
863 1.1 christos abbrev = 1;
864 1.1 christos else if (strlen(buff) == 4 && buff[3] == '.') {
865 1.1 christos abbrev = 1;
866 1.1 christos buff[3] = '\0';
867 1.1 christos }
868 1.1 christos else
869 1.1 christos abbrev = 0;
870 1.1 christos
871 1.1 christos for (tp = MonthDayTable; tp->name; tp++) {
872 1.1 christos if (abbrev) {
873 1.9 christos if (strncmp(buff, tp->name, 3) == 0) {
874 1.1 christos yylval->Number = tp->value;
875 1.1 christos return tp->type;
876 1.1 christos }
877 1.1 christos }
878 1.9 christos else if (strcmp(buff, tp->name) == 0) {
879 1.1 christos yylval->Number = tp->value;
880 1.1 christos return tp->type;
881 1.1 christos }
882 1.1 christos }
883 1.1 christos
884 1.1 christos for (tp = TimezoneTable; tp->name; tp++)
885 1.9 christos if (strcmp(buff, tp->name) == 0) {
886 1.1 christos yylval->Number = tp->value;
887 1.1 christos return tp->type;
888 1.1 christos }
889 1.1 christos
890 1.1 christos if (strcmp(buff, "dst") == 0)
891 1.1 christos return tDST;
892 1.23 christos
893 1.23 christos for (tp = TimeNames; tp->name; tp++)
894 1.23 christos if (strcmp(buff, tp->name) == 0) {
895 1.23 christos yylval->Number = tp->value;
896 1.23 christos return tp->type;
897 1.23 christos }
898 1.1 christos
899 1.1 christos for (tp = UnitsTable; tp->name; tp++)
900 1.9 christos if (strcmp(buff, tp->name) == 0) {
901 1.1 christos yylval->Number = tp->value;
902 1.1 christos return tp->type;
903 1.1 christos }
904 1.1 christos
905 1.1 christos /* Strip off any plural and try the units table again. */
906 1.1 christos i = strlen(buff) - 1;
907 1.1 christos if (buff[i] == 's') {
908 1.1 christos buff[i] = '\0';
909 1.1 christos for (tp = UnitsTable; tp->name; tp++)
910 1.9 christos if (strcmp(buff, tp->name) == 0) {
911 1.1 christos yylval->Number = tp->value;
912 1.1 christos return tp->type;
913 1.1 christos }
914 1.1 christos buff[i] = 's'; /* Put back for "this" in OtherTable. */
915 1.1 christos }
916 1.1 christos
917 1.1 christos for (tp = OtherTable; tp->name; tp++)
918 1.9 christos if (strcmp(buff, tp->name) == 0) {
919 1.1 christos yylval->Number = tp->value;
920 1.1 christos return tp->type;
921 1.1 christos }
922 1.1 christos
923 1.1 christos /* Military timezones. */
924 1.1 christos if (buff[1] == '\0' && isalpha((unsigned char)*buff)) {
925 1.1 christos for (tp = MilitaryTable; tp->name; tp++)
926 1.9 christos if (strcmp(buff, tp->name) == 0) {
927 1.1 christos yylval->Number = tp->value;
928 1.1 christos return tp->type;
929 1.1 christos }
930 1.1 christos }
931 1.1 christos
932 1.1 christos /* Drop out any periods and try the timezone table again. */
933 1.1 christos for (i = 0, p = q = buff; *q; q++)
934 1.1 christos if (*q != '.')
935 1.1 christos *p++ = *q;
936 1.1 christos else
937 1.1 christos i++;
938 1.1 christos *p = '\0';
939 1.1 christos if (i)
940 1.1 christos for (tp = TimezoneTable; tp->name; tp++)
941 1.9 christos if (strcmp(buff, tp->name) == 0) {
942 1.1 christos yylval->Number = tp->value;
943 1.1 christos return tp->type;
944 1.1 christos }
945 1.1 christos
946 1.1 christos return tID;
947 1.1 christos }
948 1.1 christos
949 1.1 christos
950 1.9 christos static int
951 1.1 christos yylex(YYSTYPE *yylval, const char **yyInput)
952 1.1 christos {
953 1.1 christos register char c;
954 1.1 christos register char *p;
955 1.1 christos char buff[20];
956 1.1 christos int Count;
957 1.9 christos int sign;
958 1.1 christos const char *inp = *yyInput;
959 1.1 christos
960 1.9 christos for ( ; ; ) {
961 1.9 christos while (isspace((unsigned char)*inp))
962 1.1 christos inp++;
963 1.9 christos
964 1.1 christos if (isdigit((unsigned char)(c = *inp)) || c == '-' || c == '+') {
965 1.1 christos if (c == '-' || c == '+') {
966 1.9 christos sign = c == '-' ? -1 : 1;
967 1.1 christos if (!isdigit((unsigned char)*++inp))
968 1.1 christos /* skip the '-' sign */
969 1.1 christos continue;
970 1.1 christos }
971 1.1 christos else
972 1.9 christos sign = 0;
973 1.9 christos for (yylval->Number = 0; isdigit((unsigned char)(c = *inp++)); )
974 1.1 christos yylval->Number = 10 * yylval->Number + c - '0';
975 1.9 christos if (sign < 0)
976 1.9 christos yylval->Number = -yylval->Number;
977 1.1 christos *yyInput = --inp;
978 1.1 christos return sign ? tSNUMBER : tUNUMBER;
979 1.1 christos }
980 1.9 christos if (isalpha((unsigned char)c)) {
981 1.1 christos for (p = buff; isalpha((unsigned char)(c = *inp++)) || c == '.'; )
982 1.1 christos if (p < &buff[sizeof buff - 1])
983 1.1 christos *p++ = c;
984 1.9 christos *p = '\0';
985 1.9 christos *yyInput = --inp;
986 1.1 christos return LookupWord(yylval, buff);
987 1.5 tron }
988 1.9 christos if (c == '@') {
989 1.5 tron *yyInput = ++inp;
990 1.5 tron return AT_SIGN;
991 1.9 christos }
992 1.9 christos if (c != '(') {
993 1.9 christos *yyInput = ++inp;
994 1.9 christos return c;
995 1.1 christos }
996 1.1 christos Count = 0;
997 1.9 christos do {
998 1.1 christos c = *inp++;
999 1.1 christos if (c == '\0')
1000 1.1 christos return c;
1001 1.1 christos if (c == '(')
1002 1.1 christos Count++;
1003 1.1 christos else if (c == ')')
1004 1.1 christos Count--;
1005 1.1 christos } while (Count > 0);
1006 1.1 christos }
1007 1.1 christos }
1008 1.1 christos
1009 1.1 christos #define TM_YEAR_ORIGIN 1900
1010 1.1 christos
1011 1.1 christos time_t
1012 1.1 christos parsedate(const char *p, const time_t *now, const int *zone)
1013 1.20 apb {
1014 1.1 christos struct tm local, *tm;
1015 1.1 christos time_t nowt;
1016 1.1 christos int zonet;
1017 1.6 christos time_t Start;
1018 1.9 christos time_t tod, rm;
1019 1.14 apb struct dateinfo param;
1020 1.28 kre int saved_errno;
1021 1.14 apb int i;
1022 1.14 apb
1023 1.14 apb saved_errno = errno;
1024 1.1 christos errno = 0;
1025 1.20 apb
1026 1.1 christos if (now == NULL) {
1027 1.20 apb now = &nowt;
1028 1.20 apb (void)time(&nowt);
1029 1.20 apb }
1030 1.1 christos if (zone == NULL) {
1031 1.20 apb zone = &zonet;
1032 1.1 christos zonet = USE_LOCAL_TIME;
1033 1.1 christos if ((tm = localtime_r(now, &local)) == NULL)
1034 1.1 christos return -1;
1035 1.20 apb } else {
1036 1.20 apb /*
1037 1.20 apb * Should use the specified zone, not localtime.
1038 1.20 apb * Fake it using gmtime and arithmetic.
1039 1.20 apb * This is good enough because we use only the year/month/day,
1040 1.20 apb * not other fields of struct tm.
1041 1.20 apb */
1042 1.20 apb time_t fake = *now + (*zone * 60);
1043 1.1 christos if ((tm = gmtime_r(&fake, &local)) == NULL)
1044 1.1 christos return -1;
1045 1.9 christos }
1046 1.9 christos param.yyYear = tm->tm_year + 1900;
1047 1.9 christos param.yyMonth = tm->tm_mon + 1;
1048 1.9 christos param.yyDay = tm->tm_mday;
1049 1.9 christos param.yyTimezone = *zone;
1050 1.9 christos param.yyDSTmode = DSTmaybe;
1051 1.9 christos param.yyHour = 0;
1052 1.9 christos param.yyMinutes = 0;
1053 1.9 christos param.yySeconds = 0;
1054 1.9 christos param.yyMeridian = MER24;
1055 1.17 apb param.yyHaveDate = 0;
1056 1.9 christos param.yyHaveFullYear = 0;
1057 1.9 christos param.yyHaveDay = 0;
1058 1.9 christos param.yyHaveRel = 0;
1059 1.9 christos param.yyHaveTime = 0;
1060 1.9 christos param.yyHaveZone = 0;
1061 1.9 christos
1062 1.14 apb if (yyparse(¶m, &p) || param.yyHaveTime > 1 || param.yyHaveZone > 1 ||
1063 1.14 apb param.yyHaveDate > 1 || param.yyHaveDay > 1) {
1064 1.9 christos errno = EINVAL;
1065 1.14 apb return -1;
1066 1.9 christos }
1067 1.9 christos
1068 1.17 apb if (param.yyHaveDate || param.yyHaveTime || param.yyHaveDay) {
1069 1.17 apb if (! param.yyHaveFullYear) {
1070 1.17 apb param.yyYear = AdjustYear(param.yyYear);
1071 1.17 apb param.yyHaveFullYear = 1;
1072 1.21 christos }
1073 1.9 christos errno = 0;
1074 1.9 christos Start = Convert(param.yyMonth, param.yyDay, param.yyYear, param.yyHour,
1075 1.9 christos param.yyMinutes, param.yySeconds, param.yyTimezone,
1076 1.14 apb param.yyMeridian, param.yyDSTmode);
1077 1.1 christos if (Start == -1 && errno != 0)
1078 1.1 christos return -1;
1079 1.1 christos }
1080 1.1 christos else {
1081 1.9 christos Start = *now;
1082 1.1 christos if (!param.yyHaveRel)
1083 1.1 christos Start -= ((tm->tm_hour * 60L + tm->tm_min) * 60L) + tm->tm_sec;
1084 1.1 christos }
1085 1.28 kre
1086 1.28 kre if (param.yyHaveRel > MAXREL) {
1087 1.6 christos errno = EINVAL;
1088 1.28 kre return -1;
1089 1.28 kre }
1090 1.28 kre for (i = 0; i < param.yyHaveRel; i++) {
1091 1.28 kre if (param.yyRel[i].yyRelMonth) {
1092 1.28 kre errno = 0;
1093 1.28 kre rm = RelativeMonth(Start, param.yyRel[i].yyRelVal, param.yyTimezone);
1094 1.28 kre if (rm == -1 && errno != 0)
1095 1.28 kre return -1;
1096 1.28 kre Start += rm;
1097 1.28 kre } else
1098 1.28 kre Start += param.yyRel[i].yyRelVal;
1099 1.1 christos }
1100 1.9 christos
1101 1.21 christos if (param.yyHaveDay && !param.yyHaveDate) {
1102 1.9 christos errno = 0;
1103 1.21 christos tod = RelativeDate(Start, param.yyDayOrdinal, param.yyDayNumber);
1104 1.21 christos if (tod == -1 && errno != 0)
1105 1.1 christos return -1;
1106 1.1 christos Start += tod;
1107 1.1 christos }
1108 1.21 christos
1109 1.6 christos errno = saved_errno;
1110 1.1 christos return Start;
1111 1.1 christos }
1112 1.1 christos
1113 1.1 christos
1114 1.1 christos #if defined(TEST)
1115 1.1 christos
1116 1.1 christos /* ARGSUSED */
1117 1.14 apb int
1118 1.1 christos main(int ac, char *av[])
1119 1.1 christos {
1120 1.1 christos char buff[128];
1121 1.1 christos time_t d;
1122 1.1 christos
1123 1.1 christos (void)printf("Enter date, or blank line to exit.\n\t> ");
1124 1.14 apb (void)fflush(stdout);
1125 1.14 apb while (fgets(buff, sizeof(buff), stdin) && buff[0] != '\n') {
1126 1.1 christos errno = 0;
1127 1.14 apb d = parsedate(buff, NULL, NULL);
1128 1.14 apb if (d == -1 && errno != 0)
1129 1.14 apb (void)printf("Bad format - couldn't convert: %s\n",
1130 1.1 christos strerror(errno));
1131 1.14 apb else
1132 1.1 christos (void)printf("%jd\t%s", (intmax_t)d, ctime(&d));
1133 1.1 christos (void)printf("\t> ");
1134 1.1 christos (void)fflush(stdout);
1135 1.1 christos }
1136 1.1 christos exit(0);
1137 1.1 christos /* NOTREACHED */
1138 1.1 christos }
1139 #endif /* defined(TEST) */
1140