~ubuntu-branches/debian/experimental/lftp/experimental

« back to all changes in this revision

Viewing changes to lib/parse-datetime.y

  • Committer: Package Import Robot
  • Author(s): Noël Köthe
  • Date: 2015-08-21 16:06:22 UTC
  • mfrom: (1.1.20) (24.1.38 sid)
  • Revision ID: package-import@ubuntu.com-20150821160622-lckdmbiqx16wefgy
Tags: 4.6.4-1
new upstream release 2015-08-21

Show diffs side-by-side

added added

removed removed

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