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