Branch data Line data Source code
1 : : %{
2 : : /* Parse a string into an internal time stamp.
3 : :
4 : : Copyright (C) 1999, 2000, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
5 : : 2010 Free Software Foundation, Inc.
6 : :
7 : : This program is free software: you can redistribute it and/or modify
8 : : it under the terms of the GNU General Public License as published by
9 : : the Free Software Foundation; either version 3 of the License, or
10 : : (at your option) any later version.
11 : :
12 : : This program is distributed in the hope that it will be useful,
13 : : but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 : : GNU General Public License for more details.
16 : :
17 : : You should have received a copy of the GNU General Public License
18 : : along with this program. If not, see <http://www.gnu.org/licenses/>. */
19 : :
20 : : /* Originally written by Steven M. Bellovin <smb@research.att.com> while
21 : : at the University of North Carolina at Chapel Hill. Later tweaked by
22 : : a couple of people on Usenet. Completely overhauled by Rich $alz
23 : : <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990.
24 : :
25 : : Modified by Paul Eggert <eggert@twinsun.com> in August 1999 to do
26 : : the right thing about local DST. Also modified by Paul Eggert
27 : : <eggert@cs.ucla.edu> in February 2004 to support
28 : : nanosecond-resolution time stamps, and in October 2004 to support
29 : : TZ strings in dates. */
30 : :
31 : : /* FIXME: Check for arithmetic overflow in all cases, not just
32 : : some of them. */
33 : :
34 : : #include <config.h>
35 : :
36 : : #include "getdate.h"
37 : :
38 : : #include "intprops.h"
39 : : #include "timespec.h"
40 : : #include "verify.h"
41 : :
42 : : /* There's no need to extend the stack, so there's no need to involve
43 : : alloca. */
44 : : #define YYSTACK_USE_ALLOCA 0
45 : :
46 : : /* Tell Bison how much stack space is needed. 20 should be plenty for
47 : : this grammar, which is not right recursive. Beware setting it too
48 : : high, since that might cause problems on machines whose
49 : : implementations have lame stack-overflow checking. */
50 : : #define YYMAXDEPTH 20
51 : : #define YYINITDEPTH YYMAXDEPTH
52 : :
53 : : /* Since the code of getdate.y is not included in the Emacs executable
54 : : itself, there is no need to #define static in this file. Even if
55 : : the code were included in the Emacs executable, it probably
56 : : wouldn't do any harm to #undef it here; this will only cause
57 : : problems if we try to write to a static variable, which I don't
58 : : think this code needs to do. */
59 : : #ifdef emacs
60 : : # undef static
61 : : #endif
62 : :
63 : : #include <c-ctype.h>
64 : : #include <limits.h>
65 : : #include <stdio.h>
66 : : #include <stdlib.h>
67 : : #include <string.h>
68 : :
69 : : #include "xalloc.h"
70 : :
71 : :
72 : : /* ISDIGIT differs from isdigit, as follows:
73 : : - Its arg may be any int or unsigned int; it need not be an unsigned char
74 : : or EOF.
75 : : - It's typically faster.
76 : : POSIX says that only '0' through '9' are digits. Prefer ISDIGIT to
77 : : isdigit unless it's important to use the locale's definition
78 : : of `digit' even when the host does not conform to POSIX. */
79 : : #define ISDIGIT(c) ((unsigned int) (c) - '0' <= 9)
80 : :
81 : : /* Shift A right by B bits portably, by dividing A by 2**B and
82 : : truncating towards minus infinity. A and B should be free of side
83 : : effects, and B should be in the range 0 <= B <= INT_BITS - 2, where
84 : : INT_BITS is the number of useful bits in an int. GNU code can
85 : : assume that INT_BITS is at least 32.
86 : :
87 : : ISO C99 says that A >> B is implementation-defined if A < 0. Some
88 : : implementations (e.g., UNICOS 9.0 on a Cray Y-MP EL) don't shift
89 : : right in the usual way when A < 0, so SHR falls back on division if
90 : : ordinary A >> B doesn't seem to be the usual signed shift. */
91 : : #define SHR(a, b) \
92 : : (-1 >> 1 == -1 \
93 : : ? (a) >> (b) \
94 : : : (a) / (1 << (b)) - ((a) % (1 << (b)) < 0))
95 : :
96 : : #define EPOCH_YEAR 1970
97 : : #define TM_YEAR_BASE 1900
98 : :
99 : : #define HOUR(x) ((x) * 60)
100 : :
101 : : /* long_time_t is a signed integer type that contains all time_t values. */
102 : : verify (TYPE_IS_INTEGER (time_t));
103 : : #if TIME_T_FITS_IN_LONG_INT
104 : : typedef long int long_time_t;
105 : : #else
106 : : typedef time_t long_time_t;
107 : : #endif
108 : :
109 : : /* Lots of this code assumes time_t and time_t-like values fit into
110 : : long_time_t. */
111 : : verify (TYPE_MINIMUM (long_time_t) <= TYPE_MINIMUM (time_t)
112 : : && TYPE_MAXIMUM (time_t) <= TYPE_MAXIMUM (long_time_t));
113 : :
114 : : /* FIXME: It also assumes that signed integer overflow silently wraps around,
115 : : but this is not true any more with recent versions of GCC 4. */
116 : :
117 : : /* An integer value, and the number of digits in its textual
118 : : representation. */
119 : : typedef struct
120 : : {
121 : : bool negative;
122 : : long int value;
123 : : size_t digits;
124 : : } textint;
125 : :
126 : : /* An entry in the lexical lookup table. */
127 : : typedef struct
128 : : {
129 : : char const *name;
130 : : int type;
131 : : int value;
132 : : } table;
133 : :
134 : : /* Meridian: am, pm, or 24-hour style. */
135 : : enum { MERam, MERpm, MER24 };
136 : :
137 : : enum { BILLION = 1000000000, LOG10_BILLION = 9 };
138 : :
139 : : /* Relative times. */
140 : : typedef struct
141 : : {
142 : : /* Relative year, month, day, hour, minutes, seconds, and nanoseconds. */
143 : : long int year;
144 : : long int month;
145 : : long int day;
146 : : long int hour;
147 : : long int minutes;
148 : : long_time_t seconds;
149 : : long int ns;
150 : : } relative_time;
151 : :
152 : : #if HAVE_COMPOUND_LITERALS
153 : : # define RELATIVE_TIME_0 ((relative_time) { 0, 0, 0, 0, 0, 0, 0 })
154 : : #else
155 : : static relative_time const RELATIVE_TIME_0;
156 : : #endif
157 : :
158 : : /* Information passed to and from the parser. */
159 : : typedef struct
160 : : {
161 : : /* The input string remaining to be parsed. */
162 : : const char *input;
163 : :
164 : : /* N, if this is the Nth Tuesday. */
165 : : long int day_ordinal;
166 : :
167 : : /* Day of week; Sunday is 0. */
168 : : int day_number;
169 : :
170 : : /* tm_isdst flag for the local zone. */
171 : : int local_isdst;
172 : :
173 : : /* Time zone, in minutes east of UTC. */
174 : : long int time_zone;
175 : :
176 : : /* Style used for time. */
177 : : int meridian;
178 : :
179 : : /* Gregorian year, month, day, hour, minutes, seconds, and nanoseconds. */
180 : : textint year;
181 : : long int month;
182 : : long int day;
183 : : long int hour;
184 : : long int minutes;
185 : : struct timespec seconds; /* includes nanoseconds */
186 : :
187 : : /* Relative year, month, day, hour, minutes, seconds, and nanoseconds. */
188 : : relative_time rel;
189 : :
190 : : /* Presence or counts of nonterminals of various flavors parsed so far. */
191 : : bool timespec_seen;
192 : : bool rels_seen;
193 : : size_t dates_seen;
194 : : size_t days_seen;
195 : : size_t local_zones_seen;
196 : : size_t dsts_seen;
197 : : size_t times_seen;
198 : : size_t zones_seen;
199 : :
200 : : /* Table of local time zone abbrevations, terminated by a null entry. */
201 : : table local_time_zone_table[3];
202 : : } parser_control;
203 : :
204 : : union YYSTYPE;
205 : : static int yylex (union YYSTYPE *, parser_control *);
206 : : static int yyerror (parser_control const *, char const *);
207 : : static long int time_zone_hhmm (parser_control *, textint, long int);
208 : :
209 : : /* Extract into *PC any date and time info from a string of digits
210 : : of the form e.g., YYYYMMDD, YYMMDD, HHMM, HH (and sometimes YYY,
211 : : YYYY, ...). */
212 : : static void
213 : 0 : digits_to_date_time (parser_control *pc, textint text_int)
214 : : {
215 [ # # ][ # # ]: 0 : if (pc->dates_seen && ! pc->year.digits
[ # # ][ # # ]
[ # # ]
216 : 0 : && ! pc->rels_seen && (pc->times_seen || 2 < text_int.digits))
217 : 0 : pc->year = text_int;
218 : : else
219 : : {
220 [ # # ]: 0 : if (4 < text_int.digits)
221 : : {
222 : 0 : pc->dates_seen++;
223 : 0 : pc->day = text_int.value % 100;
224 : 0 : pc->month = (text_int.value / 100) % 100;
225 : 0 : pc->year.value = text_int.value / 10000;
226 : 0 : pc->year.digits = text_int.digits - 4;
227 : : }
228 : : else
229 : : {
230 : 0 : pc->times_seen++;
231 [ # # ]: 0 : if (text_int.digits <= 2)
232 : : {
233 : 0 : pc->hour = text_int.value;
234 : 0 : pc->minutes = 0;
235 : : }
236 : : else
237 : : {
238 : 0 : pc->hour = text_int.value / 100;
239 : 0 : pc->minutes = text_int.value % 100;
240 : : }
241 : 0 : pc->seconds.tv_sec = 0;
242 : 0 : pc->seconds.tv_nsec = 0;
243 : 0 : pc->meridian = MER24;
244 : : }
245 : : }
246 : 0 : }
247 : :
248 : : /* Increment PC->rel by FACTOR * REL (FACTOR is 1 or -1). */
249 : : static void
250 : 0 : apply_relative_time (parser_control *pc, relative_time rel, int factor)
251 : : {
252 : 0 : pc->rel.ns += factor * rel.ns;
253 : 0 : pc->rel.seconds += factor * rel.seconds;
254 : 0 : pc->rel.minutes += factor * rel.minutes;
255 : 0 : pc->rel.hour += factor * rel.hour;
256 : 0 : pc->rel.day += factor * rel.day;
257 : 0 : pc->rel.month += factor * rel.month;
258 : 0 : pc->rel.year += factor * rel.year;
259 : 0 : pc->rels_seen = true;
260 : 0 : }
261 : :
262 : : /* Set PC-> hour, minutes, seconds and nanoseconds members from arguments. */
263 : : static void
264 : 0 : set_hhmmss (parser_control *pc, long int hour, long int minutes,
265 : : time_t sec, long int nsec)
266 : : {
267 : 0 : pc->hour = hour;
268 : 0 : pc->minutes = minutes;
269 : 0 : pc->seconds.tv_sec = sec;
270 : 0 : pc->seconds.tv_nsec = nsec;
271 : 0 : }
272 : :
273 : : %}
274 : :
275 : : /* We want a reentrant parser, even if the TZ manipulation and the calls to
276 : : localtime and gmtime are not reentrant. */
277 : : %pure-parser
278 : : %parse-param { parser_control *pc }
279 : : %lex-param { parser_control *pc }
280 : :
281 : : /* This grammar has 20 shift/reduce conflicts. */
282 : : %expect 20
283 : :
284 : : %union
285 : : {
286 : : long int intval;
287 : : textint textintval;
288 : : struct timespec timespec;
289 : : relative_time rel;
290 : : }
291 : :
292 : : %token tAGO tDST
293 : :
294 : : %token tYEAR_UNIT tMONTH_UNIT tHOUR_UNIT tMINUTE_UNIT tSEC_UNIT
295 : : %token <intval> tDAY_UNIT tDAY_SHIFT
296 : :
297 : : %token <intval> tDAY tDAYZONE tLOCAL_ZONE tMERIDIAN
298 : : %token <intval> tMONTH tORDINAL tZONE
299 : :
300 : : %token <textintval> tSNUMBER tUNUMBER
301 : : %token <timespec> tSDECIMAL_NUMBER tUDECIMAL_NUMBER
302 : :
303 : : %type <intval> o_colon_minutes o_merid
304 : : %type <timespec> seconds signed_seconds unsigned_seconds
305 : :
306 : : %type <rel> relunit relunit_snumber dayshift
307 : :
308 : : %%
309 : :
310 : : spec:
311 : : timespec
312 : : | items
313 : : ;
314 : :
315 : : timespec:
316 : : '@' seconds
317 : : {
318 : 0 : pc->seconds = $2;
319 : 0 : pc->timespec_seen = true;
320 : : }
321 : 0 : ;
322 : :
323 : : items:
324 : : /* empty */
325 : : | items item
326 : : ;
327 : :
328 : : item:
329 : : time
330 : 0 : { pc->times_seen++; }
331 : 0 : | local_zone
332 : 0 : { pc->local_zones_seen++; }
333 : 0 : | zone
334 : 0 : { pc->zones_seen++; }
335 : 0 : | date
336 : 0 : { pc->dates_seen++; }
337 : 0 : | day
338 : 0 : { pc->days_seen++; }
339 : 0 : | rel
340 : : | number
341 : : | hybrid
342 : : ;
343 : :
344 : : time:
345 : : tUNUMBER tMERIDIAN
346 : : {
347 : 0 : set_hhmmss (pc, $1.value, 0, 0, 0);
348 : 0 : pc->meridian = $2;
349 : : }
350 : 0 : | tUNUMBER ':' tUNUMBER o_merid
351 : : {
352 : 0 : set_hhmmss (pc, $1.value, $3.value, 0, 0);
353 : 0 : pc->meridian = $4;
354 : : }
355 : 0 : | tUNUMBER ':' tUNUMBER tSNUMBER o_colon_minutes
356 : : {
357 : 0 : set_hhmmss (pc, $1.value, $3.value, 0, 0);
358 : 0 : pc->meridian = MER24;
359 : 0 : pc->zones_seen++;
360 : 0 : pc->time_zone = time_zone_hhmm (pc, $4, $5);
361 : : }
362 : 0 : | tUNUMBER ':' tUNUMBER ':' unsigned_seconds o_merid
363 : : {
364 : 0 : set_hhmmss (pc, $1.value, $3.value, $5.tv_sec, $5.tv_nsec);
365 : 0 : pc->meridian = $6;
366 : : }
367 : 0 : | tUNUMBER ':' tUNUMBER ':' unsigned_seconds tSNUMBER o_colon_minutes
368 : : {
369 : 0 : set_hhmmss (pc, $1.value, $3.value, $5.tv_sec, $5.tv_nsec);
370 : 0 : pc->meridian = MER24;
371 : 0 : pc->zones_seen++;
372 : 0 : pc->time_zone = time_zone_hhmm (pc, $6, $7);
373 : : }
374 : 0 : ;
375 : :
376 : : local_zone:
377 : : tLOCAL_ZONE
378 : : {
379 : 0 : pc->local_isdst = $1;
380 : 0 : pc->dsts_seen += (0 < $1);
381 : : }
382 : 0 : | tLOCAL_ZONE tDST
383 : : {
384 : 0 : pc->local_isdst = 1;
385 [ # # ]: 0 : pc->dsts_seen += (0 < $1) + 1;
386 : : }
387 : 0 : ;
388 : :
389 : : zone:
390 : : tZONE
391 : 0 : { pc->time_zone = $1; }
392 : 0 : | tZONE relunit_snumber
393 : 0 : { pc->time_zone = $1;
394 : 0 : apply_relative_time (pc, $2, 1); }
395 : 0 : | tZONE tSNUMBER o_colon_minutes
396 : 0 : { pc->time_zone = $1 + time_zone_hhmm (pc, $2, $3); }
397 : 0 : | tDAYZONE
398 : 0 : { pc->time_zone = $1 + 60; }
399 : 0 : | tZONE tDST
400 : 0 : { pc->time_zone = $1 + 60; }
401 : 0 : ;
402 : :
403 : : day:
404 : : tDAY
405 : : {
406 : 0 : pc->day_ordinal = 0;
407 : 0 : pc->day_number = $1;
408 : : }
409 : 0 : | tDAY ','
410 : : {
411 : 0 : pc->day_ordinal = 0;
412 : 0 : pc->day_number = $1;
413 : : }
414 : 0 : | tORDINAL tDAY
415 : : {
416 : 0 : pc->day_ordinal = $1;
417 : 0 : pc->day_number = $2;
418 : : }
419 : 0 : | tUNUMBER tDAY
420 : : {
421 : 0 : pc->day_ordinal = $1.value;
422 : 0 : pc->day_number = $2;
423 : : }
424 : 0 : ;
425 : :
426 : : date:
427 : : tUNUMBER '/' tUNUMBER
428 : : {
429 : 0 : pc->month = $1.value;
430 : 0 : pc->day = $3.value;
431 : : }
432 : 0 : | tUNUMBER '/' tUNUMBER '/' tUNUMBER
433 : : {
434 : : /* Interpret as YYYY/MM/DD if the first value has 4 or more digits,
435 : : otherwise as MM/DD/YY.
436 : : The goal in recognizing YYYY/MM/DD is solely to support legacy
437 : : machine-generated dates like those in an RCS log listing. If
438 : : you want portability, use the ISO 8601 format. */
439 [ # # ]: 0 : if (4 <= $1.digits)
440 : : {
441 : 0 : pc->year = $1;
442 : 0 : pc->month = $3.value;
443 : 0 : pc->day = $5.value;
444 : : }
445 : : else
446 : : {
447 : 0 : pc->month = $1.value;
448 : 0 : pc->day = $3.value;
449 : 0 : pc->year = $5;
450 : : }
451 : : }
452 : 0 : | tUNUMBER tSNUMBER tSNUMBER
453 : : {
454 : : /* ISO 8601 format. YYYY-MM-DD. */
455 : 0 : pc->year = $1;
456 : 0 : pc->month = -$2.value;
457 : 0 : pc->day = -$3.value;
458 : : }
459 : 0 : | tUNUMBER tMONTH tSNUMBER
460 : : {
461 : : /* e.g. 17-JUN-1992. */
462 : 0 : pc->day = $1.value;
463 : 0 : pc->month = $2;
464 : 0 : pc->year.value = -$3.value;
465 : 0 : pc->year.digits = $3.digits;
466 : : }
467 : 0 : | tMONTH tSNUMBER tSNUMBER
468 : : {
469 : : /* e.g. JUN-17-1992. */
470 : 0 : pc->month = $1;
471 : 0 : pc->day = -$2.value;
472 : 0 : pc->year.value = -$3.value;
473 : 0 : pc->year.digits = $3.digits;
474 : : }
475 : 0 : | tMONTH tUNUMBER
476 : : {
477 : 0 : pc->month = $1;
478 : 0 : pc->day = $2.value;
479 : : }
480 : 0 : | tMONTH tUNUMBER ',' tUNUMBER
481 : : {
482 : 0 : pc->month = $1;
483 : 0 : pc->day = $2.value;
484 : 0 : pc->year = $4;
485 : : }
486 : 0 : | tUNUMBER tMONTH
487 : : {
488 : 0 : pc->day = $1.value;
489 : 0 : pc->month = $2;
490 : : }
491 : 0 : | tUNUMBER tMONTH tUNUMBER
492 : : {
493 : 0 : pc->day = $1.value;
494 : 0 : pc->month = $2;
495 : 0 : pc->year = $3;
496 : : }
497 : 0 : ;
498 : :
499 : : rel:
500 : : relunit tAGO
501 : 0 : { apply_relative_time (pc, $1, -1); }
502 : 0 : | relunit
503 : 0 : { apply_relative_time (pc, $1, 1); }
504 : 0 : | dayshift
505 : 0 : { apply_relative_time (pc, $1, 1); }
506 : 0 : ;
507 : :
508 : : relunit:
509 : : tORDINAL tYEAR_UNIT
510 : 0 : { $$ = RELATIVE_TIME_0; $$.year = $1; }
511 : 0 : | tUNUMBER tYEAR_UNIT
512 : 0 : { $$ = RELATIVE_TIME_0; $$.year = $1.value; }
513 : 0 : | tYEAR_UNIT
514 : 0 : { $$ = RELATIVE_TIME_0; $$.year = 1; }
515 : 0 : | tORDINAL tMONTH_UNIT
516 : 0 : { $$ = RELATIVE_TIME_0; $$.month = $1; }
517 : 0 : | tUNUMBER tMONTH_UNIT
518 : 0 : { $$ = RELATIVE_TIME_0; $$.month = $1.value; }
519 : 0 : | tMONTH_UNIT
520 : 0 : { $$ = RELATIVE_TIME_0; $$.month = 1; }
521 : 0 : | tORDINAL tDAY_UNIT
522 : 0 : { $$ = RELATIVE_TIME_0; $$.day = $1 * $2; }
523 : 0 : | tUNUMBER tDAY_UNIT
524 : 0 : { $$ = RELATIVE_TIME_0; $$.day = $1.value * $2; }
525 : 0 : | tDAY_UNIT
526 : 0 : { $$ = RELATIVE_TIME_0; $$.day = $1; }
527 : 0 : | tORDINAL tHOUR_UNIT
528 : 0 : { $$ = RELATIVE_TIME_0; $$.hour = $1; }
529 : 0 : | tUNUMBER tHOUR_UNIT
530 : 0 : { $$ = RELATIVE_TIME_0; $$.hour = $1.value; }
531 : 0 : | tHOUR_UNIT
532 : 0 : { $$ = RELATIVE_TIME_0; $$.hour = 1; }
533 : 0 : | tORDINAL tMINUTE_UNIT
534 : 0 : { $$ = RELATIVE_TIME_0; $$.minutes = $1; }
535 : 0 : | tUNUMBER tMINUTE_UNIT
536 : 0 : { $$ = RELATIVE_TIME_0; $$.minutes = $1.value; }
537 : 0 : | tMINUTE_UNIT
538 : 0 : { $$ = RELATIVE_TIME_0; $$.minutes = 1; }
539 : 0 : | tORDINAL tSEC_UNIT
540 : 0 : { $$ = RELATIVE_TIME_0; $$.seconds = $1; }
541 : 0 : | tUNUMBER tSEC_UNIT
542 : 0 : { $$ = RELATIVE_TIME_0; $$.seconds = $1.value; }
543 : 0 : | tSDECIMAL_NUMBER tSEC_UNIT
544 : 0 : { $$ = RELATIVE_TIME_0; $$.seconds = $1.tv_sec; $$.ns = $1.tv_nsec; }
545 : 0 : | tUDECIMAL_NUMBER tSEC_UNIT
546 : 0 : { $$ = RELATIVE_TIME_0; $$.seconds = $1.tv_sec; $$.ns = $1.tv_nsec; }
547 : 0 : | tSEC_UNIT
548 : 0 : { $$ = RELATIVE_TIME_0; $$.seconds = 1; }
549 : 0 : | relunit_snumber
550 : : ;
551 : :
552 : : relunit_snumber:
553 : : tSNUMBER tYEAR_UNIT
554 : 0 : { $$ = RELATIVE_TIME_0; $$.year = $1.value; }
555 : 0 : | tSNUMBER tMONTH_UNIT
556 : 0 : { $$ = RELATIVE_TIME_0; $$.month = $1.value; }
557 : 0 : | tSNUMBER tDAY_UNIT
558 : 0 : { $$ = RELATIVE_TIME_0; $$.day = $1.value * $2; }
559 : 0 : | tSNUMBER tHOUR_UNIT
560 : 0 : { $$ = RELATIVE_TIME_0; $$.hour = $1.value; }
561 : 0 : | tSNUMBER tMINUTE_UNIT
562 : 0 : { $$ = RELATIVE_TIME_0; $$.minutes = $1.value; }
563 : 0 : | tSNUMBER tSEC_UNIT
564 : 0 : { $$ = RELATIVE_TIME_0; $$.seconds = $1.value; }
565 : 0 : ;
566 : :
567 : : dayshift:
568 : : tDAY_SHIFT
569 : 0 : { $$ = RELATIVE_TIME_0; $$.day = $1; }
570 : 0 : ;
571 : :
572 : : seconds: signed_seconds | unsigned_seconds;
573 : :
574 : : signed_seconds:
575 : : tSDECIMAL_NUMBER
576 : : | tSNUMBER
577 : 0 : { $$.tv_sec = $1.value; $$.tv_nsec = 0; }
578 : 0 : ;
579 : :
580 : : unsigned_seconds:
581 : : tUDECIMAL_NUMBER
582 : : | tUNUMBER
583 : 0 : { $$.tv_sec = $1.value; $$.tv_nsec = 0; }
584 : 0 : ;
585 : :
586 : : number:
587 : : tUNUMBER
588 : 0 : { digits_to_date_time (pc, $1); }
589 : 0 : ;
590 : :
591 : : hybrid:
592 : : tUNUMBER relunit_snumber
593 : : {
594 : : /* Hybrid all-digit and relative offset, so that we accept e.g.,
595 : : "YYYYMMDD +N days" as well as "YYYYMMDD N days". */
596 : 0 : digits_to_date_time (pc, $1);
597 : 0 : apply_relative_time (pc, $2, 1);
598 : : }
599 : 0 : ;
600 : :
601 : : o_colon_minutes:
602 : : /* empty */
603 : 0 : { $$ = -1; }
604 : 0 : | ':' tUNUMBER
605 : 0 : { $$ = $2.value; }
606 : 0 : ;
607 : :
608 : : o_merid:
609 : : /* empty */
610 : 0 : { $$ = MER24; }
611 : 0 : | tMERIDIAN
612 : 0 : { $$ = $1; }
613 : : ;
614 : :
615 : : %%
616 : :
617 : : static table const meridian_table[] =
618 : : {
619 : : { "AM", tMERIDIAN, MERam },
620 : : { "A.M.", tMERIDIAN, MERam },
621 : : { "PM", tMERIDIAN, MERpm },
622 : : { "P.M.", tMERIDIAN, MERpm },
623 : : { NULL, 0, 0 }
624 : : };
625 : :
626 : : static table const dst_table[] =
627 : : {
628 : : { "DST", tDST, 0 }
629 : : };
630 : :
631 : : static table const month_and_day_table[] =
632 : : {
633 : : { "JANUARY", tMONTH, 1 },
634 : : { "FEBRUARY", tMONTH, 2 },
635 : : { "MARCH", tMONTH, 3 },
636 : : { "APRIL", tMONTH, 4 },
637 : : { "MAY", tMONTH, 5 },
638 : : { "JUNE", tMONTH, 6 },
639 : : { "JULY", tMONTH, 7 },
640 : : { "AUGUST", tMONTH, 8 },
641 : : { "SEPTEMBER",tMONTH, 9 },
642 : : { "SEPT", tMONTH, 9 },
643 : : { "OCTOBER", tMONTH, 10 },
644 : : { "NOVEMBER", tMONTH, 11 },
645 : : { "DECEMBER", tMONTH, 12 },
646 : : { "SUNDAY", tDAY, 0 },
647 : : { "MONDAY", tDAY, 1 },
648 : : { "TUESDAY", tDAY, 2 },
649 : : { "TUES", tDAY, 2 },
650 : : { "WEDNESDAY",tDAY, 3 },
651 : : { "WEDNES", tDAY, 3 },
652 : : { "THURSDAY", tDAY, 4 },
653 : : { "THUR", tDAY, 4 },
654 : : { "THURS", tDAY, 4 },
655 : : { "FRIDAY", tDAY, 5 },
656 : : { "SATURDAY", tDAY, 6 },
657 : : { NULL, 0, 0 }
658 : : };
659 : :
660 : : static table const time_units_table[] =
661 : : {
662 : : { "YEAR", tYEAR_UNIT, 1 },
663 : : { "MONTH", tMONTH_UNIT, 1 },
664 : : { "FORTNIGHT",tDAY_UNIT, 14 },
665 : : { "WEEK", tDAY_UNIT, 7 },
666 : : { "DAY", tDAY_UNIT, 1 },
667 : : { "HOUR", tHOUR_UNIT, 1 },
668 : : { "MINUTE", tMINUTE_UNIT, 1 },
669 : : { "MIN", tMINUTE_UNIT, 1 },
670 : : { "SECOND", tSEC_UNIT, 1 },
671 : : { "SEC", tSEC_UNIT, 1 },
672 : : { NULL, 0, 0 }
673 : : };
674 : :
675 : : /* Assorted relative-time words. */
676 : : static table const relative_time_table[] =
677 : : {
678 : : { "TOMORROW", tDAY_SHIFT, 1 },
679 : : { "YESTERDAY",tDAY_SHIFT, -1 },
680 : : { "TODAY", tDAY_SHIFT, 0 },
681 : : { "NOW", tDAY_SHIFT, 0 },
682 : : { "LAST", tORDINAL, -1 },
683 : : { "THIS", tORDINAL, 0 },
684 : : { "NEXT", tORDINAL, 1 },
685 : : { "FIRST", tORDINAL, 1 },
686 : : /*{ "SECOND", tORDINAL, 2 }, */
687 : : { "THIRD", tORDINAL, 3 },
688 : : { "FOURTH", tORDINAL, 4 },
689 : : { "FIFTH", tORDINAL, 5 },
690 : : { "SIXTH", tORDINAL, 6 },
691 : : { "SEVENTH", tORDINAL, 7 },
692 : : { "EIGHTH", tORDINAL, 8 },
693 : : { "NINTH", tORDINAL, 9 },
694 : : { "TENTH", tORDINAL, 10 },
695 : : { "ELEVENTH", tORDINAL, 11 },
696 : : { "TWELFTH", tORDINAL, 12 },
697 : : { "AGO", tAGO, 1 },
698 : : { NULL, 0, 0 }
699 : : };
700 : :
701 : : /* The universal time zone table. These labels can be used even for
702 : : time stamps that would not otherwise be valid, e.g., GMT time
703 : : stamps in London during summer. */
704 : : static table const universal_time_zone_table[] =
705 : : {
706 : : { "GMT", tZONE, HOUR ( 0) }, /* Greenwich Mean */
707 : : { "UT", tZONE, HOUR ( 0) }, /* Universal (Coordinated) */
708 : : { "UTC", tZONE, HOUR ( 0) },
709 : : { NULL, 0, 0 }
710 : : };
711 : :
712 : : /* The time zone table. This table is necessarily incomplete, as time
713 : : zone abbreviations are ambiguous; e.g. Australians interpret "EST"
714 : : as Eastern time in Australia, not as US Eastern Standard Time.
715 : : You cannot rely on getdate to handle arbitrary time zone
716 : : abbreviations; use numeric abbreviations like `-0500' instead. */
717 : : static table const time_zone_table[] =
718 : : {
719 : : { "WET", tZONE, HOUR ( 0) }, /* Western European */
720 : : { "WEST", tDAYZONE, HOUR ( 0) }, /* Western European Summer */
721 : : { "BST", tDAYZONE, HOUR ( 0) }, /* British Summer */
722 : : { "ART", tZONE, -HOUR ( 3) }, /* Argentina */
723 : : { "BRT", tZONE, -HOUR ( 3) }, /* Brazil */
724 : : { "BRST", tDAYZONE, -HOUR ( 3) }, /* Brazil Summer */
725 : : { "NST", tZONE, -(HOUR ( 3) + 30) }, /* Newfoundland Standard */
726 : : { "NDT", tDAYZONE,-(HOUR ( 3) + 30) }, /* Newfoundland Daylight */
727 : : { "AST", tZONE, -HOUR ( 4) }, /* Atlantic Standard */
728 : : { "ADT", tDAYZONE, -HOUR ( 4) }, /* Atlantic Daylight */
729 : : { "CLT", tZONE, -HOUR ( 4) }, /* Chile */
730 : : { "CLST", tDAYZONE, -HOUR ( 4) }, /* Chile Summer */
731 : : { "EST", tZONE, -HOUR ( 5) }, /* Eastern Standard */
732 : : { "EDT", tDAYZONE, -HOUR ( 5) }, /* Eastern Daylight */
733 : : { "CST", tZONE, -HOUR ( 6) }, /* Central Standard */
734 : : { "CDT", tDAYZONE, -HOUR ( 6) }, /* Central Daylight */
735 : : { "MST", tZONE, -HOUR ( 7) }, /* Mountain Standard */
736 : : { "MDT", tDAYZONE, -HOUR ( 7) }, /* Mountain Daylight */
737 : : { "PST", tZONE, -HOUR ( 8) }, /* Pacific Standard */
738 : : { "PDT", tDAYZONE, -HOUR ( 8) }, /* Pacific Daylight */
739 : : { "AKST", tZONE, -HOUR ( 9) }, /* Alaska Standard */
740 : : { "AKDT", tDAYZONE, -HOUR ( 9) }, /* Alaska Daylight */
741 : : { "HST", tZONE, -HOUR (10) }, /* Hawaii Standard */
742 : : { "HAST", tZONE, -HOUR (10) }, /* Hawaii-Aleutian Standard */
743 : : { "HADT", tDAYZONE, -HOUR (10) }, /* Hawaii-Aleutian Daylight */
744 : : { "SST", tZONE, -HOUR (12) }, /* Samoa Standard */
745 : : { "WAT", tZONE, HOUR ( 1) }, /* West Africa */
746 : : { "CET", tZONE, HOUR ( 1) }, /* Central European */
747 : : { "CEST", tDAYZONE, HOUR ( 1) }, /* Central European Summer */
748 : : { "MET", tZONE, HOUR ( 1) }, /* Middle European */
749 : : { "MEZ", tZONE, HOUR ( 1) }, /* Middle European */
750 : : { "MEST", tDAYZONE, HOUR ( 1) }, /* Middle European Summer */
751 : : { "MESZ", tDAYZONE, HOUR ( 1) }, /* Middle European Summer */
752 : : { "EET", tZONE, HOUR ( 2) }, /* Eastern European */
753 : : { "EEST", tDAYZONE, HOUR ( 2) }, /* Eastern European Summer */
754 : : { "CAT", tZONE, HOUR ( 2) }, /* Central Africa */
755 : : { "SAST", tZONE, HOUR ( 2) }, /* South Africa Standard */
756 : : { "EAT", tZONE, HOUR ( 3) }, /* East Africa */
757 : : { "MSK", tZONE, HOUR ( 3) }, /* Moscow */
758 : : { "MSD", tDAYZONE, HOUR ( 3) }, /* Moscow Daylight */
759 : : { "IST", tZONE, (HOUR ( 5) + 30) }, /* India Standard */
760 : : { "SGT", tZONE, HOUR ( 8) }, /* Singapore */
761 : : { "KST", tZONE, HOUR ( 9) }, /* Korea Standard */
762 : : { "JST", tZONE, HOUR ( 9) }, /* Japan Standard */
763 : : { "GST", tZONE, HOUR (10) }, /* Guam Standard */
764 : : { "NZST", tZONE, HOUR (12) }, /* New Zealand Standard */
765 : : { "NZDT", tDAYZONE, HOUR (12) }, /* New Zealand Daylight */
766 : : { NULL, 0, 0 }
767 : : };
768 : :
769 : : /* Military time zone table. */
770 : : static table const military_table[] =
771 : : {
772 : : { "A", tZONE, -HOUR ( 1) },
773 : : { "B", tZONE, -HOUR ( 2) },
774 : : { "C", tZONE, -HOUR ( 3) },
775 : : { "D", tZONE, -HOUR ( 4) },
776 : : { "E", tZONE, -HOUR ( 5) },
777 : : { "F", tZONE, -HOUR ( 6) },
778 : : { "G", tZONE, -HOUR ( 7) },
779 : : { "H", tZONE, -HOUR ( 8) },
780 : : { "I", tZONE, -HOUR ( 9) },
781 : : { "K", tZONE, -HOUR (10) },
782 : : { "L", tZONE, -HOUR (11) },
783 : : { "M", tZONE, -HOUR (12) },
784 : : { "N", tZONE, HOUR ( 1) },
785 : : { "O", tZONE, HOUR ( 2) },
786 : : { "P", tZONE, HOUR ( 3) },
787 : : { "Q", tZONE, HOUR ( 4) },
788 : : { "R", tZONE, HOUR ( 5) },
789 : : { "S", tZONE, HOUR ( 6) },
790 : : { "T", tZONE, HOUR ( 7) },
791 : : { "U", tZONE, HOUR ( 8) },
792 : : { "V", tZONE, HOUR ( 9) },
793 : : { "W", tZONE, HOUR (10) },
794 : : { "X", tZONE, HOUR (11) },
795 : : { "Y", tZONE, HOUR (12) },
796 : : { "Z", tZONE, HOUR ( 0) },
797 : : { NULL, 0, 0 }
798 : : };
799 : :
800 : :
801 : :
802 : : /* Convert a time zone expressed as HH:MM into an integer count of
803 : : minutes. If MM is negative, then S is of the form HHMM and needs
804 : : to be picked apart; otherwise, S is of the form HH. As specified in
805 : : http://www.opengroup.org/susv3xbd/xbd_chap08.html#tag_08_03, allow
806 : : only valid TZ range, and consider first two digits as hours, if no
807 : : minutes specified. */
808 : :
809 : : static long int
810 : 0 : time_zone_hhmm (parser_control *pc, textint s, long int mm)
811 : : {
812 : : long int n_minutes;
813 : :
814 : : /* If the length of S is 1 or 2 and no minutes are specified,
815 : : interpret it as a number of hours. */
816 [ # # ][ # # ]: 0 : if (s.digits <= 2 && mm < 0)
817 : 0 : s.value *= 100;
818 : :
819 [ # # ]: 0 : if (mm < 0)
820 : 0 : n_minutes = (s.value / 100) * 60 + s.value % 100;
821 : : else
822 [ # # ]: 0 : n_minutes = s.value * 60 + (s.negative ? -mm : mm);
823 : :
824 : : /* If the absolute number of minutes is larger than 24 hours,
825 : : arrange to reject it by incrementing pc->zones_seen. Thus,
826 : : we allow only values in the range UTC-24:00 to UTC+24:00. */
827 [ # # ]: 0 : if (24 * 60 < abs (n_minutes))
828 : 0 : pc->zones_seen++;
829 : :
830 : 0 : return n_minutes;
831 : : }
832 : :
833 : : static int
834 : 0 : to_hour (long int hours, int meridian)
835 : : {
836 [ # # # ]: 0 : switch (meridian)
837 : : {
838 : : default: /* Pacify GCC. */
839 : : case MER24:
840 [ # # ][ # # ]: 0 : return 0 <= hours && hours < 24 ? hours : -1;
841 : : case MERam:
842 [ # # ][ # # ]: 0 : return 0 < hours && hours < 12 ? hours : hours == 12 ? 0 : -1;
[ # # ]
843 : : case MERpm:
844 [ # # ][ # # ]: 0 : return 0 < hours && hours < 12 ? hours + 12 : hours == 12 ? 12 : -1;
[ # # ]
845 : : }
846 : : }
847 : :
848 : : static long int
849 : 0 : to_year (textint textyear)
850 : : {
851 : 0 : long int year = textyear.value;
852 : :
853 [ # # ]: 0 : if (year < 0)
854 : 0 : year = -year;
855 : :
856 : : /* XPG4 suggests that years 00-68 map to 2000-2068, and
857 : : years 69-99 map to 1969-1999. */
858 [ # # ]: 0 : else if (textyear.digits == 2)
859 [ # # ]: 0 : year += year < 69 ? 2000 : 1900;
860 : :
861 : 0 : return year;
862 : : }
863 : :
864 : : static table const *
865 : 0 : lookup_zone (parser_control const *pc, char const *name)
866 : : {
867 : : table const *tp;
868 : :
869 [ # # ]: 0 : for (tp = universal_time_zone_table; tp->name; tp++)
870 [ # # ]: 0 : if (strcmp (name, tp->name) == 0)
871 : 0 : return tp;
872 : :
873 : : /* Try local zone abbreviations before those in time_zone_table, as
874 : : the local ones are more likely to be right. */
875 [ # # ]: 0 : for (tp = pc->local_time_zone_table; tp->name; tp++)
876 [ # # ]: 0 : if (strcmp (name, tp->name) == 0)
877 : 0 : return tp;
878 : :
879 [ # # ]: 0 : for (tp = time_zone_table; tp->name; tp++)
880 [ # # ]: 0 : if (strcmp (name, tp->name) == 0)
881 : 0 : return tp;
882 : :
883 : 0 : return NULL;
884 : : }
885 : :
886 : : #if ! HAVE_TM_GMTOFF
887 : : /* Yield the difference between *A and *B,
888 : : measured in seconds, ignoring leap seconds.
889 : : The body of this function is taken directly from the GNU C Library;
890 : : see src/strftime.c. */
891 : : static long int
892 : : tm_diff (struct tm const *a, struct tm const *b)
893 : : {
894 : : /* Compute intervening leap days correctly even if year is negative.
895 : : Take care to avoid int overflow in leap day calculations. */
896 : : int a4 = SHR (a->tm_year, 2) + SHR (TM_YEAR_BASE, 2) - ! (a->tm_year & 3);
897 : : int b4 = SHR (b->tm_year, 2) + SHR (TM_YEAR_BASE, 2) - ! (b->tm_year & 3);
898 : : int a100 = a4 / 25 - (a4 % 25 < 0);
899 : : int b100 = b4 / 25 - (b4 % 25 < 0);
900 : : int a400 = SHR (a100, 2);
901 : : int b400 = SHR (b100, 2);
902 : : int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
903 : : long int ayear = a->tm_year;
904 : : long int years = ayear - b->tm_year;
905 : : long int days = (365 * years + intervening_leap_days
906 : : + (a->tm_yday - b->tm_yday));
907 : : return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
908 : : + (a->tm_min - b->tm_min))
909 : : + (a->tm_sec - b->tm_sec));
910 : : }
911 : : #endif /* ! HAVE_TM_GMTOFF */
912 : :
913 : : static table const *
914 : 0 : lookup_word (parser_control const *pc, char *word)
915 : : {
916 : : char *p;
917 : : char *q;
918 : : size_t wordlen;
919 : : table const *tp;
920 : : bool period_found;
921 : : bool abbrev;
922 : :
923 : : /* Make it uppercase. */
924 [ # # ]: 0 : for (p = word; *p; p++)
925 : : {
926 : 0 : unsigned char ch = *p;
927 : 0 : *p = c_toupper (ch);
928 : : }
929 : :
930 [ # # ]: 0 : for (tp = meridian_table; tp->name; tp++)
931 [ # # ]: 0 : if (strcmp (word, tp->name) == 0)
932 : 0 : return tp;
933 : :
934 : : /* See if we have an abbreviation for a month. */
935 : 0 : wordlen = strlen (word);
936 [ # # ][ # # ]: 0 : abbrev = wordlen == 3 || (wordlen == 4 && word[3] == '.');
[ # # ]
937 : :
938 [ # # ]: 0 : for (tp = month_and_day_table; tp->name; tp++)
939 [ # # ][ # # ]: 0 : if ((abbrev ? strncmp (word, tp->name, 3) : strcmp (word, tp->name)) == 0)
940 : 0 : return tp;
941 : :
942 [ # # ]: 0 : if ((tp = lookup_zone (pc, word)))
943 : 0 : return tp;
944 : :
945 [ # # ]: 0 : if (strcmp (word, dst_table[0].name) == 0)
946 : 0 : return dst_table;
947 : :
948 [ # # ]: 0 : for (tp = time_units_table; tp->name; tp++)
949 [ # # ]: 0 : if (strcmp (word, tp->name) == 0)
950 : 0 : return tp;
951 : :
952 : : /* Strip off any plural and try the units table again. */
953 [ # # ]: 0 : if (word[wordlen - 1] == 'S')
954 : : {
955 : 0 : word[wordlen - 1] = '\0';
956 [ # # ]: 0 : for (tp = time_units_table; tp->name; tp++)
957 [ # # ]: 0 : if (strcmp (word, tp->name) == 0)
958 : 0 : return tp;
959 : 0 : word[wordlen - 1] = 'S'; /* For "this" in relative_time_table. */
960 : : }
961 : :
962 [ # # ]: 0 : for (tp = relative_time_table; tp->name; tp++)
963 [ # # ]: 0 : if (strcmp (word, tp->name) == 0)
964 : 0 : return tp;
965 : :
966 : : /* Military time zones. */
967 [ # # ]: 0 : if (wordlen == 1)
968 [ # # ]: 0 : for (tp = military_table; tp->name; tp++)
969 [ # # ]: 0 : if (word[0] == tp->name[0])
970 : 0 : return tp;
971 : :
972 : : /* Drop out any periods and try the time zone table again. */
973 [ # # ]: 0 : for (period_found = false, p = q = word; (*p = *q); q++)
974 [ # # ]: 0 : if (*q == '.')
975 : 0 : period_found = true;
976 : : else
977 : 0 : p++;
978 [ # # ][ # # ]: 0 : if (period_found && (tp = lookup_zone (pc, word)))
979 : 0 : return tp;
980 : :
981 : 0 : return NULL;
982 : : }
983 : :
984 : : static int
985 : 0 : yylex (YYSTYPE *lvalp, parser_control *pc)
986 : : {
987 : : unsigned char c;
988 : : size_t count;
989 : :
990 : : for (;;)
991 : : {
992 [ # # ]: 0 : while (c = *pc->input, c_isspace (c))
993 : 0 : pc->input++;
994 : :
995 [ # # ][ # # ]: 0 : if (ISDIGIT (c) || c == '-' || c == '+')
[ # # ]
996 : : {
997 : : char const *p;
998 : : int sign;
999 : : unsigned long int value;
1000 [ # # ][ # # ]: 0 : if (c == '-' || c == '+')
1001 : : {
1002 [ # # ]: 0 : sign = c == '-' ? -1 : 1;
1003 [ # # ]: 0 : while (c = *++pc->input, c_isspace (c))
1004 : 0 : continue;
1005 [ # # ]: 0 : if (! ISDIGIT (c))
1006 : : /* skip the '-' sign */
1007 : 0 : continue;
1008 : : }
1009 : : else
1010 : 0 : sign = 0;
1011 : 0 : p = pc->input;
1012 : 0 : for (value = 0; ; value *= 10)
1013 : : {
1014 : 0 : unsigned long int value1 = value + (c - '0');
1015 [ # # ]: 0 : if (value1 < value)
1016 : 0 : return '?';
1017 : 0 : value = value1;
1018 : 0 : c = *++p;
1019 [ # # ]: 0 : if (! ISDIGIT (c))
1020 : : break;
1021 [ # # ]: 0 : if (ULONG_MAX / 10 < value)
1022 : 0 : return '?';
1023 : 0 : }
1024 [ # # ][ # # ]: 0 : if ((c == '.' || c == ',') && ISDIGIT (p[1]))
[ # # ]
1025 : : {
1026 : : time_t s;
1027 : : int ns;
1028 : : int digits;
1029 : : unsigned long int value1;
1030 : :
1031 : : /* Check for overflow when converting value to time_t. */
1032 [ # # ]: 0 : if (sign < 0)
1033 : : {
1034 : 0 : s = - value;
1035 [ # # ]: 0 : if (0 < s)
1036 : 0 : return '?';
1037 : 0 : value1 = -s;
1038 : : }
1039 : : else
1040 : : {
1041 : 0 : s = value;
1042 [ # # ]: 0 : if (s < 0)
1043 : 0 : return '?';
1044 : 0 : value1 = s;
1045 : : }
1046 [ # # ]: 0 : if (value != value1)
1047 : 0 : return '?';
1048 : :
1049 : : /* Accumulate fraction, to ns precision. */
1050 : 0 : p++;
1051 : 0 : ns = *p++ - '0';
1052 [ # # ]: 0 : for (digits = 2; digits <= LOG10_BILLION; digits++)
1053 : : {
1054 : 0 : ns *= 10;
1055 [ # # ]: 0 : if (ISDIGIT (*p))
1056 : 0 : ns += *p++ - '0';
1057 : : }
1058 : :
1059 : : /* Skip excess digits, truncating toward -Infinity. */
1060 [ # # ]: 0 : if (sign < 0)
1061 [ # # ]: 0 : for (; ISDIGIT (*p); p++)
1062 [ # # ]: 0 : if (*p != '0')
1063 : : {
1064 : 0 : ns++;
1065 : 0 : break;
1066 : : }
1067 [ # # ]: 0 : while (ISDIGIT (*p))
1068 : 0 : p++;
1069 : :
1070 : : /* Adjust to the timespec convention, which is that
1071 : : tv_nsec is always a positive offset even if tv_sec is
1072 : : negative. */
1073 [ # # ][ # # ]: 0 : if (sign < 0 && ns)
1074 : : {
1075 : 0 : s--;
1076 [ # # ]: 0 : if (! (s < 0))
1077 : 0 : return '?';
1078 : 0 : ns = BILLION - ns;
1079 : : }
1080 : :
1081 : 0 : lvalp->timespec.tv_sec = s;
1082 : 0 : lvalp->timespec.tv_nsec = ns;
1083 : 0 : pc->input = p;
1084 [ # # ]: 0 : return sign ? tSDECIMAL_NUMBER : tUDECIMAL_NUMBER;
1085 : : }
1086 : : else
1087 : : {
1088 : 0 : lvalp->textintval.negative = sign < 0;
1089 [ # # ]: 0 : if (sign < 0)
1090 : : {
1091 : 0 : lvalp->textintval.value = - value;
1092 [ # # ]: 0 : if (0 < lvalp->textintval.value)
1093 : 0 : return '?';
1094 : : }
1095 : : else
1096 : : {
1097 : 0 : lvalp->textintval.value = value;
1098 [ # # ]: 0 : if (lvalp->textintval.value < 0)
1099 : 0 : return '?';
1100 : : }
1101 : 0 : lvalp->textintval.digits = p - pc->input;
1102 : 0 : pc->input = p;
1103 [ # # ]: 0 : return sign ? tSNUMBER : tUNUMBER;
1104 : : }
1105 : : }
1106 : :
1107 [ # # ]: 0 : if (c_isalpha (c))
1108 : : {
1109 : : char buff[20];
1110 : 0 : char *p = buff;
1111 : : table const *tp;
1112 : :
1113 : : do
1114 : : {
1115 [ # # ]: 0 : if (p < buff + sizeof buff - 1)
1116 : 0 : *p++ = c;
1117 : 0 : c = *++pc->input;
1118 : : }
1119 [ # # ][ # # ]: 0 : while (c_isalpha (c) || c == '.');
1120 : :
1121 : 0 : *p = '\0';
1122 : 0 : tp = lookup_word (pc, buff);
1123 [ # # ]: 0 : if (! tp)
1124 : 0 : return '?';
1125 : 0 : lvalp->intval = tp->value;
1126 : 0 : return tp->type;
1127 : : }
1128 : :
1129 [ # # ]: 0 : if (c != '(')
1130 : 0 : return *pc->input++;
1131 : 0 : count = 0;
1132 : : do
1133 : : {
1134 : 0 : c = *pc->input++;
1135 [ # # ]: 0 : if (c == '\0')
1136 : 0 : return c;
1137 [ # # ]: 0 : if (c == '(')
1138 : 0 : count++;
1139 [ # # ]: 0 : else if (c == ')')
1140 : 0 : count--;
1141 : : }
1142 [ # # ]: 0 : while (count != 0);
1143 : 0 : }
1144 : : }
1145 : :
1146 : : /* Do nothing if the parser reports an error. */
1147 : : static int
1148 : 0 : yyerror (parser_control const *pc _GL_UNUSED,
1149 : : char const *s _GL_UNUSED)
1150 : : {
1151 : 0 : return 0;
1152 : : }
1153 : :
1154 : : /* If *TM0 is the old and *TM1 is the new value of a struct tm after
1155 : : passing it to mktime, return true if it's OK that mktime returned T.
1156 : : It's not OK if *TM0 has out-of-range members. */
1157 : :
1158 : : static bool
1159 : 0 : mktime_ok (struct tm const *tm0, struct tm const *tm1, time_t t)
1160 : : {
1161 [ # # ]: 0 : if (t == (time_t) -1)
1162 : : {
1163 : : /* Guard against falsely reporting an error when parsing a time
1164 : : stamp that happens to equal (time_t) -1, on a host that
1165 : : supports such a time stamp. */
1166 : 0 : tm1 = localtime (&t);
1167 [ # # ]: 0 : if (!tm1)
1168 : 0 : return false;
1169 : : }
1170 : :
1171 : 0 : return ! ((tm0->tm_sec ^ tm1->tm_sec)
1172 : 0 : | (tm0->tm_min ^ tm1->tm_min)
1173 : 0 : | (tm0->tm_hour ^ tm1->tm_hour)
1174 : 0 : | (tm0->tm_mday ^ tm1->tm_mday)
1175 : 0 : | (tm0->tm_mon ^ tm1->tm_mon)
1176 : 0 : | (tm0->tm_year ^ tm1->tm_year));
1177 : : }
1178 : :
1179 : : /* A reasonable upper bound for the size of ordinary TZ strings.
1180 : : Use heap allocation if TZ's length exceeds this. */
1181 : : enum { TZBUFSIZE = 100 };
1182 : :
1183 : : /* Return a copy of TZ, stored in TZBUF if it fits, and heap-allocated
1184 : : otherwise. */
1185 : : static char *
1186 : 0 : get_tz (char tzbuf[TZBUFSIZE])
1187 : : {
1188 : 0 : char *tz = getenv ("TZ");
1189 [ # # ]: 0 : if (tz)
1190 : : {
1191 : 0 : size_t tzsize = strlen (tz) + 1;
1192 [ # # ]: 0 : tz = (tzsize <= TZBUFSIZE
1193 : 0 : ? memcpy (tzbuf, tz, tzsize)
1194 : 0 : : xmemdup (tz, tzsize));
1195 : : }
1196 : 0 : return tz;
1197 : : }
1198 : :
1199 : : /* Parse a date/time string, storing the resulting time value into *RESULT.
1200 : : The string itself is pointed to by P. Return true if successful.
1201 : : P can be an incomplete or relative time specification; if so, use
1202 : : *NOW as the basis for the returned time. */
1203 : : bool
1204 : 0 : get_date (struct timespec *result, char const *p, struct timespec const *now)
1205 : : {
1206 : : time_t Start;
1207 : : long int Start_ns;
1208 : : struct tm const *tmp;
1209 : : struct tm tm;
1210 : : struct tm tm0;
1211 : : parser_control pc;
1212 : : struct timespec gettime_buffer;
1213 : : unsigned char c;
1214 : 0 : bool tz_was_altered = false;
1215 : 0 : char *tz0 = NULL;
1216 : : char tz0buf[TZBUFSIZE];
1217 : 0 : bool ok = true;
1218 : :
1219 [ # # ]: 0 : if (! now)
1220 : : {
1221 : 0 : gettime (&gettime_buffer);
1222 : 0 : now = &gettime_buffer;
1223 : : }
1224 : :
1225 : 0 : Start = now->tv_sec;
1226 : 0 : Start_ns = now->tv_nsec;
1227 : :
1228 : 0 : tmp = localtime (&now->tv_sec);
1229 [ # # ]: 0 : if (! tmp)
1230 : 0 : return false;
1231 : :
1232 [ # # ]: 0 : while (c = *p, c_isspace (c))
1233 : 0 : p++;
1234 : :
1235 [ # # ]: 0 : if (strncmp (p, "TZ=\"", 4) == 0)
1236 : : {
1237 : 0 : char const *tzbase = p + 4;
1238 : 0 : size_t tzsize = 1;
1239 : : char const *s;
1240 : :
1241 [ # # ]: 0 : for (s = tzbase; *s; s++, tzsize++)
1242 [ # # ]: 0 : if (*s == '\\')
1243 : : {
1244 : 0 : s++;
1245 [ # # ][ # # ]: 0 : if (! (*s == '\\' || *s == '"'))
1246 : 0 : break;
1247 : : }
1248 [ # # ]: 0 : else if (*s == '"')
1249 : : {
1250 : : char *z;
1251 : : char *tz1;
1252 : : char tz1buf[TZBUFSIZE];
1253 : 0 : bool large_tz = TZBUFSIZE < tzsize;
1254 : : bool setenv_ok;
1255 : : /* Free tz0, in case this is the 2nd or subsequent time through. */
1256 : 0 : free (tz0);
1257 : 0 : tz0 = get_tz (tz0buf);
1258 [ # # ]: 0 : z = tz1 = large_tz ? xmalloc (tzsize) : tz1buf;
1259 [ # # ]: 0 : for (s = tzbase; *s != '"'; s++)
1260 : 0 : *z++ = *(s += *s == '\\');
1261 : 0 : *z = '\0';
1262 : 0 : setenv_ok = setenv ("TZ", tz1, 1) == 0;
1263 [ # # ]: 0 : if (large_tz)
1264 : 0 : free (tz1);
1265 [ # # ]: 0 : if (!setenv_ok)
1266 : 0 : goto fail;
1267 : 0 : tz_was_altered = true;
1268 : 0 : p = s + 1;
1269 : : }
1270 : : }
1271 : :
1272 : : /* As documented, be careful to treat the empty string just like
1273 : : a date string of "0". Without this, an empty string would be
1274 : : declared invalid when parsed during a DST transition. */
1275 [ # # ]: 0 : if (*p == '\0')
1276 : 0 : p = "0";
1277 : :
1278 : 0 : pc.input = p;
1279 : 0 : pc.year.value = tmp->tm_year;
1280 : 0 : pc.year.value += TM_YEAR_BASE;
1281 : 0 : pc.year.digits = 0;
1282 : 0 : pc.month = tmp->tm_mon + 1;
1283 : 0 : pc.day = tmp->tm_mday;
1284 : 0 : pc.hour = tmp->tm_hour;
1285 : 0 : pc.minutes = tmp->tm_min;
1286 : 0 : pc.seconds.tv_sec = tmp->tm_sec;
1287 : 0 : pc.seconds.tv_nsec = Start_ns;
1288 : 0 : tm.tm_isdst = tmp->tm_isdst;
1289 : :
1290 : 0 : pc.meridian = MER24;
1291 : 0 : pc.rel = RELATIVE_TIME_0;
1292 : 0 : pc.timespec_seen = false;
1293 : 0 : pc.rels_seen = false;
1294 : 0 : pc.dates_seen = 0;
1295 : 0 : pc.days_seen = 0;
1296 : 0 : pc.times_seen = 0;
1297 : 0 : pc.local_zones_seen = 0;
1298 : 0 : pc.dsts_seen = 0;
1299 : 0 : pc.zones_seen = 0;
1300 : :
1301 : : #if HAVE_STRUCT_TM_TM_ZONE
1302 : 0 : pc.local_time_zone_table[0].name = tmp->tm_zone;
1303 : 0 : pc.local_time_zone_table[0].type = tLOCAL_ZONE;
1304 : 0 : pc.local_time_zone_table[0].value = tmp->tm_isdst;
1305 : 0 : pc.local_time_zone_table[1].name = NULL;
1306 : :
1307 : : /* Probe the names used in the next three calendar quarters, looking
1308 : : for a tm_isdst different from the one we already have. */
1309 : : {
1310 : : int quarter;
1311 [ # # ]: 0 : for (quarter = 1; quarter <= 3; quarter++)
1312 : : {
1313 : 0 : time_t probe = Start + quarter * (90 * 24 * 60 * 60);
1314 : 0 : struct tm const *probe_tm = localtime (&probe);
1315 [ # # # # ]: 0 : if (probe_tm && probe_tm->tm_zone
[ # # ]
1316 : 0 : && probe_tm->tm_isdst != pc.local_time_zone_table[0].value)
1317 : : {
1318 : : {
1319 : 0 : pc.local_time_zone_table[1].name = probe_tm->tm_zone;
1320 : 0 : pc.local_time_zone_table[1].type = tLOCAL_ZONE;
1321 : 0 : pc.local_time_zone_table[1].value = probe_tm->tm_isdst;
1322 : 0 : pc.local_time_zone_table[2].name = NULL;
1323 : : }
1324 : 0 : break;
1325 : : }
1326 : : }
1327 : : }
1328 : : #else
1329 : : #if HAVE_TZNAME
1330 : : {
1331 : : # if !HAVE_DECL_TZNAME
1332 : : extern char *tzname[];
1333 : : # endif
1334 : : int i;
1335 : : for (i = 0; i < 2; i++)
1336 : : {
1337 : : pc.local_time_zone_table[i].name = tzname[i];
1338 : : pc.local_time_zone_table[i].type = tLOCAL_ZONE;
1339 : : pc.local_time_zone_table[i].value = i;
1340 : : }
1341 : : pc.local_time_zone_table[i].name = NULL;
1342 : : }
1343 : : #else
1344 : : pc.local_time_zone_table[0].name = NULL;
1345 : : #endif
1346 : : #endif
1347 : :
1348 [ # # ][ # # ]: 0 : if (pc.local_time_zone_table[0].name && pc.local_time_zone_table[1].name
[ # # ]
1349 : 0 : && ! strcmp (pc.local_time_zone_table[0].name,
1350 : : pc.local_time_zone_table[1].name))
1351 : : {
1352 : : /* This locale uses the same abbrevation for standard and
1353 : : daylight times. So if we see that abbreviation, we don't
1354 : : know whether it's daylight time. */
1355 : 0 : pc.local_time_zone_table[0].value = -1;
1356 : 0 : pc.local_time_zone_table[1].name = NULL;
1357 : : }
1358 : :
1359 [ # # ]: 0 : if (yyparse (&pc) != 0)
1360 : 0 : goto fail;
1361 : :
1362 [ # # ]: 0 : if (pc.timespec_seen)
1363 : 0 : *result = pc.seconds;
1364 : : else
1365 : : {
1366 [ # # ]: 0 : if (1 < (pc.times_seen | pc.dates_seen | pc.days_seen | pc.dsts_seen
1367 : 0 : | (pc.local_zones_seen + pc.zones_seen)))
1368 : 0 : goto fail;
1369 : :
1370 : 0 : tm.tm_year = to_year (pc.year) - TM_YEAR_BASE;
1371 : 0 : tm.tm_mon = pc.month - 1;
1372 : 0 : tm.tm_mday = pc.day;
1373 [ # # # # ]: 0 : if (pc.times_seen || (pc.rels_seen && ! pc.dates_seen && ! pc.days_seen))
[ # # ][ # # ]
1374 : : {
1375 : 0 : tm.tm_hour = to_hour (pc.hour, pc.meridian);
1376 [ # # ]: 0 : if (tm.tm_hour < 0)
1377 : 0 : goto fail;
1378 : 0 : tm.tm_min = pc.minutes;
1379 : 0 : tm.tm_sec = pc.seconds.tv_sec;
1380 : : }
1381 : : else
1382 : : {
1383 : 0 : tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
1384 : 0 : pc.seconds.tv_nsec = 0;
1385 : : }
1386 : :
1387 : : /* Let mktime deduce tm_isdst if we have an absolute time stamp. */
1388 [ # # ]: 0 : if (pc.dates_seen | pc.days_seen | pc.times_seen)
1389 : 0 : tm.tm_isdst = -1;
1390 : :
1391 : : /* But if the input explicitly specifies local time with or without
1392 : : DST, give mktime that information. */
1393 [ # # ]: 0 : if (pc.local_zones_seen)
1394 : 0 : tm.tm_isdst = pc.local_isdst;
1395 : :
1396 : 0 : tm0 = tm;
1397 : :
1398 : 0 : Start = mktime (&tm);
1399 : :
1400 [ # # ]: 0 : if (! mktime_ok (&tm0, &tm, Start))
1401 : : {
1402 [ # # ]: 0 : if (! pc.zones_seen)
1403 : 0 : goto fail;
1404 : : else
1405 : : {
1406 : : /* Guard against falsely reporting errors near the time_t
1407 : : boundaries when parsing times in other time zones. For
1408 : : example, suppose the input string "1969-12-31 23:00:00 -0100",
1409 : : the current time zone is 8 hours ahead of UTC, and the min
1410 : : time_t value is 1970-01-01 00:00:00 UTC. Then the min
1411 : : localtime value is 1970-01-01 08:00:00, and mktime will
1412 : : therefore fail on 1969-12-31 23:00:00. To work around the
1413 : : problem, set the time zone to 1 hour behind UTC temporarily
1414 : : by setting TZ="XXX1:00" and try mktime again. */
1415 : :
1416 : 0 : long int time_zone = pc.time_zone;
1417 : 0 : long int abs_time_zone = time_zone < 0 ? - time_zone : time_zone;
1418 : 0 : long int abs_time_zone_hour = abs_time_zone / 60;
1419 : 0 : int abs_time_zone_min = abs_time_zone % 60;
1420 : : char tz1buf[sizeof "XXX+0:00"
1421 : : + sizeof pc.time_zone * CHAR_BIT / 3];
1422 [ # # ]: 0 : if (!tz_was_altered)
1423 : 0 : tz0 = get_tz (tz0buf);
1424 [ # # ]: 0 : sprintf (tz1buf, "XXX%s%ld:%02d", "-" + (time_zone < 0),
1425 : : abs_time_zone_hour, abs_time_zone_min);
1426 [ # # ]: 0 : if (setenv ("TZ", tz1buf, 1) != 0)
1427 : 0 : goto fail;
1428 : 0 : tz_was_altered = true;
1429 : 0 : tm = tm0;
1430 : 0 : Start = mktime (&tm);
1431 [ # # ]: 0 : if (! mktime_ok (&tm0, &tm, Start))
1432 : 0 : goto fail;
1433 : : }
1434 : : }
1435 : :
1436 [ # # ][ # # ]: 0 : if (pc.days_seen && ! pc.dates_seen)
1437 : : {
1438 : 0 : tm.tm_mday += ((pc.day_number - tm.tm_wday + 7) % 7
1439 [ # # ][ # # ]: 0 : + 7 * (pc.day_ordinal
1440 : : - (0 < pc.day_ordinal
1441 : 0 : && tm.tm_wday != pc.day_number)));
1442 : 0 : tm.tm_isdst = -1;
1443 : 0 : Start = mktime (&tm);
1444 [ # # ]: 0 : if (Start == (time_t) -1)
1445 : 0 : goto fail;
1446 : : }
1447 : :
1448 : : /* Add relative date. */
1449 [ # # ]: 0 : if (pc.rel.year | pc.rel.month | pc.rel.day)
1450 : : {
1451 : 0 : int year = tm.tm_year + pc.rel.year;
1452 : 0 : int month = tm.tm_mon + pc.rel.month;
1453 : 0 : int day = tm.tm_mday + pc.rel.day;
1454 [ # # ]: 0 : if (((year < tm.tm_year) ^ (pc.rel.year < 0))
1455 : 0 : | ((month < tm.tm_mon) ^ (pc.rel.month < 0))
1456 : 0 : | ((day < tm.tm_mday) ^ (pc.rel.day < 0)))
1457 : 0 : goto fail;
1458 : 0 : tm.tm_year = year;
1459 : 0 : tm.tm_mon = month;
1460 : 0 : tm.tm_mday = day;
1461 : 0 : tm.tm_hour = tm0.tm_hour;
1462 : 0 : tm.tm_min = tm0.tm_min;
1463 : 0 : tm.tm_sec = tm0.tm_sec;
1464 : 0 : tm.tm_isdst = tm0.tm_isdst;
1465 : 0 : Start = mktime (&tm);
1466 [ # # ]: 0 : if (Start == (time_t) -1)
1467 : 0 : goto fail;
1468 : : }
1469 : :
1470 : : /* The only "output" of this if-block is an updated Start value,
1471 : : so this block must follow others that clobber Start. */
1472 [ # # ]: 0 : if (pc.zones_seen)
1473 : : {
1474 : 0 : long int delta = pc.time_zone * 60;
1475 : : time_t t1;
1476 : : #ifdef HAVE_TM_GMTOFF
1477 : 0 : delta -= tm.tm_gmtoff;
1478 : : #else
1479 : : time_t t = Start;
1480 : : struct tm const *gmt = gmtime (&t);
1481 : : if (! gmt)
1482 : : goto fail;
1483 : : delta -= tm_diff (&tm, gmt);
1484 : : #endif
1485 : 0 : t1 = Start - delta;
1486 [ # # ]: 0 : if ((Start < t1) != (delta < 0))
1487 : 0 : goto fail; /* time_t overflow */
1488 : 0 : Start = t1;
1489 : : }
1490 : :
1491 : : /* Add relative hours, minutes, and seconds. On hosts that support
1492 : : leap seconds, ignore the possibility of leap seconds; e.g.,
1493 : : "+ 10 minutes" adds 600 seconds, even if one of them is a
1494 : : leap second. Typically this is not what the user wants, but it's
1495 : : too hard to do it the other way, because the time zone indicator
1496 : : must be applied before relative times, and if mktime is applied
1497 : : again the time zone will be lost. */
1498 : : {
1499 : 0 : long int sum_ns = pc.seconds.tv_nsec + pc.rel.ns;
1500 : 0 : long int normalized_ns = (sum_ns % BILLION + BILLION) % BILLION;
1501 : 0 : time_t t0 = Start;
1502 : 0 : long int d1 = 60 * 60 * pc.rel.hour;
1503 : 0 : time_t t1 = t0 + d1;
1504 : 0 : long int d2 = 60 * pc.rel.minutes;
1505 : 0 : time_t t2 = t1 + d2;
1506 : 0 : long_time_t d3 = pc.rel.seconds;
1507 : 0 : long_time_t t3 = t2 + d3;
1508 : 0 : long int d4 = (sum_ns - normalized_ns) / BILLION;
1509 : 0 : long_time_t t4 = t3 + d4;
1510 : 0 : time_t t5 = t4;
1511 : :
1512 [ # # ]: 0 : if ((d1 / (60 * 60) ^ pc.rel.hour)
1513 : 0 : | (d2 / 60 ^ pc.rel.minutes)
1514 : 0 : | ((t1 < t0) ^ (d1 < 0))
1515 : 0 : | ((t2 < t1) ^ (d2 < 0))
1516 : 0 : | ((t3 < t2) ^ (d3 < 0))
1517 : 0 : | ((t4 < t3) ^ (d4 < 0))
1518 : 0 : | (t5 != t4))
1519 : 0 : goto fail;
1520 : :
1521 : 0 : result->tv_sec = t5;
1522 : 0 : result->tv_nsec = normalized_ns;
1523 : : }
1524 : : }
1525 : :
1526 : 0 : goto done;
1527 : :
1528 : : fail:
1529 : 0 : ok = false;
1530 : : done:
1531 [ # # ]: 0 : if (tz_was_altered)
1532 [ # # ]: 0 : ok &= (tz0 ? setenv ("TZ", tz0, 1) : unsetenv ("TZ")) == 0;
1533 [ # # ]: 0 : if (tz0 != tz0buf)
1534 : 0 : free (tz0);
1535 : 0 : return ok;
1536 : : }
1537 : :
1538 : : #if TEST
1539 : :
1540 : : int
1541 : : main (int ac, char **av)
1542 : : {
1543 : : char buff[BUFSIZ];
1544 : :
1545 : : printf ("Enter date, or blank line to exit.\n\t> ");
1546 : : fflush (stdout);
1547 : :
1548 : : buff[BUFSIZ - 1] = '\0';
1549 : : while (fgets (buff, BUFSIZ - 1, stdin) && buff[0])
1550 : : {
1551 : : struct timespec d;
1552 : : struct tm const *tm;
1553 : : if (! get_date (&d, buff, NULL))
1554 : : printf ("Bad format - couldn't convert.\n");
1555 : : else if (! (tm = localtime (&d.tv_sec)))
1556 : : {
1557 : : long int sec = d.tv_sec;
1558 : : printf ("localtime (%ld) failed\n", sec);
1559 : : }
1560 : : else
1561 : : {
1562 : : int ns = d.tv_nsec;
1563 : : printf ("%04ld-%02d-%02d %02d:%02d:%02d.%09d\n",
1564 : : tm->tm_year + 1900L, tm->tm_mon + 1, tm->tm_mday,
1565 : : tm->tm_hour, tm->tm_min, tm->tm_sec, ns);
1566 : : }
1567 : : printf ("\t> ");
1568 : : fflush (stdout);
1569 : : }
1570 : : return 0;
1571 : : }
1572 : : #endif /* TEST */
|