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