~zulcss/samba/server-dailies-3.4

« back to all changes in this revision

Viewing changes to source3/modules/getdate.y

  • Committer: Chuck Short
  • Date: 2010-09-28 20:38:39 UTC
  • Revision ID: zulcss@ubuntu.com-20100928203839-pgjulytsi9ue63x1
Initial version

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
%{
 
2
/* Parse a string into an internal time stamp.
 
3
   Copyright (C) 1999, 2000, 2002 Free Software Foundation, Inc.
 
4
 
 
5
   This program is free software; you can redistribute it and/or modify
 
6
   it under the terms of the GNU General Public License as published by
 
7
   the Free Software Foundation; either version 2, or (at your option)
 
8
   any later version.
 
9
 
 
10
   This program is distributed in the hope that it will be useful,
 
11
   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
13
   GNU General Public License for more details.
 
14
 
 
15
   You should have received a copy of the GNU General Public License
 
16
   along with this program; if not, see <http://www.gnu.org/licenses/>.  */
 
17
 
 
18
/* Originally written by Steven M. Bellovin <smb@research.att.com> while
 
19
   at the University of North Carolina at Chapel Hill.  Later tweaked by
 
20
   a couple of people on Usenet.  Completely overhauled by Rich $alz
 
21
   <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990.
 
22
 
 
23
   Modified by Paul Eggert <eggert@twinsun.com> in August 1999 to do
 
24
   the right thing about local DST.  Unlike previous versions, this
 
25
   version is reentrant.  */
 
26
 
 
27
#ifdef HAVE_CONFIG_H
 
28
# include <config.h>
 
29
# ifdef HAVE_ALLOCA_H
 
30
#  include <alloca.h>
 
31
# endif
 
32
#endif
 
33
 
 
34
/* Since the code of getdate.y is not included in the Emacs executable
 
35
   itself, there is no need to #define static in this file.  Even if
 
36
   the code were included in the Emacs executable, it probably
 
37
   wouldn't do any harm to #undef it here; this will only cause
 
38
   problems if we try to write to a static variable, which I don't
 
39
   think this code needs to do.  */
 
40
#ifdef emacs
 
41
# undef static
 
42
#endif
 
43
 
 
44
#include <ctype.h>
 
45
#include <string.h>
 
46
 
 
47
#if HAVE_STDLIB_H
 
48
# include <stdlib.h> /* for `free'; used by Bison 1.27 */
 
49
#endif
 
50
 
 
51
#if STDC_HEADERS || (! defined isascii && ! HAVE_ISASCII)
 
52
# define IN_CTYPE_DOMAIN(c) 1
 
53
#else
 
54
# define IN_CTYPE_DOMAIN(c) isascii (c)
 
55
#endif
 
56
 
 
57
#define ISSPACE(c) (IN_CTYPE_DOMAIN (c) && isspace (c))
 
58
#define ISALPHA(c) (IN_CTYPE_DOMAIN (c) && isalpha (c))
 
59
#define ISLOWER(c) (IN_CTYPE_DOMAIN (c) && islower (c))
 
60
#define ISDIGIT_LOCALE(c) (IN_CTYPE_DOMAIN (c) && isdigit (c))
 
61
 
 
62
/* ISDIGIT differs from ISDIGIT_LOCALE, as follows:
 
63
   - Its arg may be any int or unsigned int; it need not be an unsigned char.
 
64
   - It's guaranteed to evaluate its argument exactly once.
 
65
   - It's typically faster.
 
66
   POSIX says that only '0' through '9' are digits.  Prefer ISDIGIT to
 
67
   ISDIGIT_LOCALE unless it's important to use the locale's definition
 
68
   of `digit' even when the host does not conform to POSIX.  */
 
69
#define ISDIGIT(c) ((unsigned) (c) - '0' <= 9)
 
70
 
 
71
#if STDC_HEADERS || HAVE_STRING_H
 
72
# include <string.h>
 
73
#endif
 
74
 
 
75
#if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 8) || __STRICT_ANSI__
 
76
# define __attribute__(x)
 
77
#endif
 
78
 
 
79
#ifndef ATTRIBUTE_UNUSED
 
80
# define ATTRIBUTE_UNUSED __attribute__ ((__unused__))
 
81
#endif
 
82
 
 
83
#define EPOCH_YEAR 1970
 
84
#define TM_YEAR_BASE 1900
 
85
 
 
86
#define HOUR(x) ((x) * 60)
 
87
 
 
88
/* An integer value, and the number of digits in its textual
 
89
   representation.  */
 
90
typedef struct
 
91
{
 
92
  int value;
 
93
  int digits;
 
94
} textint;
 
95
 
 
96
/* An entry in the lexical lookup table.  */
 
97
typedef struct
 
98
{
 
99
  char const *name;
 
100
  int type;
 
101
  int value;
 
102
} table;
 
103
 
 
104
/* Meridian: am, pm, or 24-hour style.  */
 
105
enum { MERam, MERpm, MER24 };
 
106
 
 
107
/* Information passed to and from the parser.  */
 
108
typedef struct
 
109
{
 
110
  /* The input string remaining to be parsed. */
 
111
  const char *input;
 
112
 
 
113
  /* N, if this is the Nth Tuesday.  */
 
114
  int day_ordinal;
 
115
 
 
116
  /* Day of week; Sunday is 0.  */
 
117
  int day_number;
 
118
 
 
119
  /* tm_isdst flag for the local zone.  */
 
120
  int local_isdst;
 
121
 
 
122
  /* Time zone, in minutes east of UTC.  */
 
123
  int time_zone;
 
124
 
 
125
  /* Style used for time.  */
 
126
  int meridian;
 
127
 
 
128
  /* Gregorian year, month, day, hour, minutes, and seconds.  */
 
129
  textint year;
 
130
  int month;
 
131
  int day;
 
132
  int hour;
 
133
  int minutes;
 
134
  int seconds;
 
135
 
 
136
  /* Relative year, month, day, hour, minutes, and seconds.  */
 
137
  int rel_year;
 
138
  int rel_month;
 
139
  int rel_day;
 
140
  int rel_hour;
 
141
  int rel_minutes;
 
142
  int rel_seconds;
 
143
 
 
144
  /* Counts of nonterminals of various flavors parsed so far.  */
 
145
  int dates_seen;
 
146
  int days_seen;
 
147
  int local_zones_seen;
 
148
  int rels_seen;
 
149
  int times_seen;
 
150
  int zones_seen;
 
151
 
 
152
  /* Table of local time zone abbrevations, terminated by a null entry.  */
 
153
  table local_time_zone_table[3];
 
154
} parser_control;
 
155
 
 
156
#define PC (* (parser_control *) parm)
 
157
#define YYLEX_PARAM parm
 
158
#define YYPARSE_PARAM parm
 
159
 
 
160
%}
 
161
 
 
162
/* We want a reentrant parser.  */
 
163
%pure_parser
 
164
 
 
165
/* This grammar has 13 shift/reduce conflicts. */
 
166
%expect 13
 
167
 
 
168
%union
 
169
{
 
170
  int intval;
 
171
  textint textintval;
 
172
}
 
173
 
 
174
%{
 
175
 
 
176
static int yyerror(const char *);
 
177
static int yylex(YYSTYPE *, parser_control *);
 
178
 
 
179
%}
 
180
 
 
181
%token tAGO tDST
 
182
 
 
183
%token <intval> tDAY tDAY_UNIT tDAYZONE tHOUR_UNIT tLOCAL_ZONE tMERIDIAN
 
184
%token <intval> tMINUTE_UNIT tMONTH tMONTH_UNIT tSEC_UNIT tYEAR_UNIT tZONE
 
185
 
 
186
%token <textintval> tSNUMBER tUNUMBER
 
187
 
 
188
%type <intval> o_merid
 
189
 
 
190
%%
 
191
 
 
192
spec:
 
193
    /* empty */
 
194
  | spec item
 
195
  ;
 
196
 
 
197
item:
 
198
    time
 
199
      { PC.times_seen++; }
 
200
  | local_zone
 
201
      { PC.local_zones_seen++; }
 
202
  | zone
 
203
      { PC.zones_seen++; }
 
204
  | date
 
205
      { PC.dates_seen++; }
 
206
  | day
 
207
      { PC.days_seen++; }
 
208
  | rel
 
209
      { PC.rels_seen++; }
 
210
  | number
 
211
  ;
 
212
 
 
213
time:
 
214
    tUNUMBER tMERIDIAN
 
215
      {
 
216
        PC.hour = $1.value;
 
217
        PC.minutes = 0;
 
218
        PC.seconds = 0;
 
219
        PC.meridian = $2;
 
220
      }
 
221
  | tUNUMBER ':' tUNUMBER o_merid
 
222
      {
 
223
        PC.hour = $1.value;
 
224
        PC.minutes = $3.value;
 
225
        PC.seconds = 0;
 
226
        PC.meridian = $4;
 
227
      }
 
228
  | tUNUMBER ':' tUNUMBER tSNUMBER
 
229
      {
 
230
        PC.hour = $1.value;
 
231
        PC.minutes = $3.value;
 
232
        PC.meridian = MER24;
 
233
        PC.zones_seen++;
 
234
        PC.time_zone = $4.value % 100 + ($4.value / 100) * 60;
 
235
      }
 
236
  | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid
 
237
      {
 
238
        PC.hour = $1.value;
 
239
        PC.minutes = $3.value;
 
240
        PC.seconds = $5.value;
 
241
        PC.meridian = $6;
 
242
      }
 
243
  | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER
 
244
      {
 
245
        PC.hour = $1.value;
 
246
        PC.minutes = $3.value;
 
247
        PC.seconds = $5.value;
 
248
        PC.meridian = MER24;
 
249
        PC.zones_seen++;
 
250
        PC.time_zone = $6.value % 100 + ($6.value / 100) * 60;
 
251
      }
 
252
  ;
 
253
 
 
254
local_zone:
 
255
    tLOCAL_ZONE
 
256
      { PC.local_isdst = $1; }
 
257
  | tLOCAL_ZONE tDST
 
258
      { PC.local_isdst = $1 < 0 ? 1 : $1 + 1; }
 
259
  ;
 
260
 
 
261
zone:
 
262
    tZONE
 
263
      { PC.time_zone = $1; }
 
264
  | tDAYZONE
 
265
      { PC.time_zone = $1 + 60; }
 
266
  | tZONE tDST
 
267
      { PC.time_zone = $1 + 60; }
 
268
  ;
 
269
 
 
270
day:
 
271
    tDAY
 
272
      {
 
273
        PC.day_ordinal = 1;
 
274
        PC.day_number = $1;
 
275
      }
 
276
  | tDAY ','
 
277
      {
 
278
        PC.day_ordinal = 1;
 
279
        PC.day_number = $1;
 
280
      }
 
281
  | tUNUMBER tDAY
 
282
      {
 
283
        PC.day_ordinal = $1.value;
 
284
        PC.day_number = $2;
 
285
      }
 
286
  ;
 
287
 
 
288
date:
 
289
    tUNUMBER '/' tUNUMBER
 
290
      {
 
291
        PC.month = $1.value;
 
292
        PC.day = $3.value;
 
293
      }
 
294
  | tUNUMBER '/' tUNUMBER '/' tUNUMBER
 
295
      {
 
296
        /* Interpret as YYYY/MM/DD if the first value has 4 or more digits,
 
297
           otherwise as MM/DD/YY.
 
298
           The goal in recognizing YYYY/MM/DD is solely to support legacy
 
299
           machine-generated dates like those in an RCS log listing.  If
 
300
           you want portability, use the ISO 8601 format.  */
 
301
        if (4 <= $1.digits)
 
302
          {
 
303
            PC.year = $1;
 
304
            PC.month = $3.value;
 
305
            PC.day = $5.value;
 
306
          }
 
307
        else
 
308
          {
 
309
            PC.month = $1.value;
 
310
            PC.day = $3.value;
 
311
            PC.year = $5;
 
312
          }
 
313
      }
 
314
  | tUNUMBER tSNUMBER tSNUMBER
 
315
      {
 
316
        /* ISO 8601 format.  YYYY-MM-DD.  */
 
317
        PC.year = $1;
 
318
        PC.month = -$2.value;
 
319
        PC.day = -$3.value;
 
320
      }
 
321
  | tUNUMBER tMONTH tSNUMBER
 
322
      {
 
323
        /* e.g. 17-JUN-1992.  */
 
324
        PC.day = $1.value;
 
325
        PC.month = $2;
 
326
        PC.year.value = -$3.value;
 
327
        PC.year.digits = $3.digits;
 
328
      }
 
329
  | tMONTH tUNUMBER
 
330
      {
 
331
        PC.month = $1;
 
332
        PC.day = $2.value;
 
333
      }
 
334
  | tMONTH tUNUMBER ',' tUNUMBER
 
335
      {
 
336
        PC.month = $1;
 
337
        PC.day = $2.value;
 
338
        PC.year = $4;
 
339
      }
 
340
  | tUNUMBER tMONTH
 
341
      {
 
342
        PC.day = $1.value;
 
343
        PC.month = $2;
 
344
      }
 
345
  | tUNUMBER tMONTH tUNUMBER
 
346
      {
 
347
        PC.day = $1.value;
 
348
        PC.month = $2;
 
349
        PC.year = $3;
 
350
      }
 
351
  ;
 
352
 
 
353
rel:
 
354
    relunit tAGO
 
355
      {
 
356
        PC.rel_seconds = -PC.rel_seconds;
 
357
        PC.rel_minutes = -PC.rel_minutes;
 
358
        PC.rel_hour = -PC.rel_hour;
 
359
        PC.rel_day = -PC.rel_day;
 
360
        PC.rel_month = -PC.rel_month;
 
361
        PC.rel_year = -PC.rel_year;
 
362
      }
 
363
  | relunit
 
364
  ;
 
365
 
 
366
relunit:
 
367
    tUNUMBER tYEAR_UNIT
 
368
      { PC.rel_year += $1.value * $2; }
 
369
  | tSNUMBER tYEAR_UNIT
 
370
      { PC.rel_year += $1.value * $2; }
 
371
  | tYEAR_UNIT
 
372
      { PC.rel_year += $1; }
 
373
  | tUNUMBER tMONTH_UNIT
 
374
      { PC.rel_month += $1.value * $2; }
 
375
  | tSNUMBER tMONTH_UNIT
 
376
      { PC.rel_month += $1.value * $2; }
 
377
  | tMONTH_UNIT
 
378
      { PC.rel_month += $1; }
 
379
  | tUNUMBER tDAY_UNIT
 
380
      { PC.rel_day += $1.value * $2; }
 
381
  | tSNUMBER tDAY_UNIT
 
382
      { PC.rel_day += $1.value * $2; }
 
383
  | tDAY_UNIT
 
384
      { PC.rel_day += $1; }
 
385
  | tUNUMBER tHOUR_UNIT
 
386
      { PC.rel_hour += $1.value * $2; }
 
387
  | tSNUMBER tHOUR_UNIT
 
388
      { PC.rel_hour += $1.value * $2; }
 
389
  | tHOUR_UNIT
 
390
      { PC.rel_hour += $1; }
 
391
  | tUNUMBER tMINUTE_UNIT
 
392
      { PC.rel_minutes += $1.value * $2; }
 
393
  | tSNUMBER tMINUTE_UNIT
 
394
      { PC.rel_minutes += $1.value * $2; }
 
395
  | tMINUTE_UNIT
 
396
      { PC.rel_minutes += $1; }
 
397
  | tUNUMBER tSEC_UNIT
 
398
      { PC.rel_seconds += $1.value * $2; }
 
399
  | tSNUMBER tSEC_UNIT
 
400
      { PC.rel_seconds += $1.value * $2; }
 
401
  | tSEC_UNIT
 
402
      { PC.rel_seconds += $1; }
 
403
  ;
 
404
 
 
405
number:
 
406
    tUNUMBER
 
407
      {
 
408
        if (PC.dates_seen
 
409
            && ! PC.rels_seen && (PC.times_seen || 2 < $1.digits))
 
410
          PC.year = $1;
 
411
        else
 
412
          {
 
413
            if (4 < $1.digits)
 
414
              {
 
415
                PC.dates_seen++;
 
416
                PC.day = $1.value % 100;
 
417
                PC.month = ($1.value / 100) % 100;
 
418
                PC.year.value = $1.value / 10000;
 
419
                PC.year.digits = $1.digits - 4;
 
420
              }
 
421
            else
 
422
              {
 
423
                PC.times_seen++;
 
424
                if ($1.digits <= 2)
 
425
                  {
 
426
                    PC.hour = $1.value;
 
427
                    PC.minutes = 0;
 
428
                  }
 
429
                else
 
430
                  {
 
431
                    PC.hour = $1.value / 100;
 
432
                    PC.minutes = $1.value % 100;
 
433
                  }
 
434
                PC.seconds = 0;
 
435
                PC.meridian = MER24;
 
436
              }
 
437
          }
 
438
      }
 
439
  ;
 
440
 
 
441
o_merid:
 
442
    /* empty */
 
443
      { $$ = MER24; }
 
444
  | tMERIDIAN
 
445
      { $$ = $1; }
 
446
  ;
 
447
 
 
448
%%
 
449
 
 
450
/* Include this file down here because bison inserts code above which
 
451
   may define-away `const'.  We want the prototype for get_date to have
 
452
   the same signature as the function definition.  */
 
453
#include "modules/getdate.h"
 
454
 
 
455
#ifndef gmtime
 
456
struct tm *gmtime (const time_t *);
 
457
#endif
 
458
#ifndef localtime
 
459
struct tm *localtime (const time_t *);
 
460
#endif
 
461
#ifndef mktime
 
462
time_t mktime (struct tm *);
 
463
#endif
 
464
 
 
465
static table const meridian_table[] =
 
466
{
 
467
  { "AM",   tMERIDIAN, MERam },
 
468
  { "A.M.", tMERIDIAN, MERam },
 
469
  { "PM",   tMERIDIAN, MERpm },
 
470
  { "P.M.", tMERIDIAN, MERpm },
 
471
  { 0, 0, 0 }
 
472
};
 
473
 
 
474
static table const dst_table[] =
 
475
{
 
476
  { "DST", tDST, 0 }
 
477
};
 
478
 
 
479
static table const month_and_day_table[] =
 
480
{
 
481
  { "JANUARY",  tMONTH,  1 },
 
482
  { "FEBRUARY", tMONTH,  2 },
 
483
  { "MARCH",    tMONTH,  3 },
 
484
  { "APRIL",    tMONTH,  4 },
 
485
  { "MAY",      tMONTH,  5 },
 
486
  { "JUNE",     tMONTH,  6 },
 
487
  { "JULY",     tMONTH,  7 },
 
488
  { "AUGUST",   tMONTH,  8 },
 
489
  { "SEPTEMBER",tMONTH,  9 },
 
490
  { "SEPT",     tMONTH,  9 },
 
491
  { "OCTOBER",  tMONTH, 10 },
 
492
  { "NOVEMBER", tMONTH, 11 },
 
493
  { "DECEMBER", tMONTH, 12 },
 
494
  { "SUNDAY",   tDAY,    0 },
 
495
  { "MONDAY",   tDAY,    1 },
 
496
  { "TUESDAY",  tDAY,    2 },
 
497
  { "TUES",     tDAY,    2 },
 
498
  { "WEDNESDAY",tDAY,    3 },
 
499
  { "WEDNES",   tDAY,    3 },
 
500
  { "THURSDAY", tDAY,    4 },
 
501
  { "THUR",     tDAY,    4 },
 
502
  { "THURS",    tDAY,    4 },
 
503
  { "FRIDAY",   tDAY,    5 },
 
504
  { "SATURDAY", tDAY,    6 },
 
505
  { 0, 0, 0 }
 
506
};
 
507
 
 
508
static table const time_units_table[] =
 
509
{
 
510
  { "YEAR",     tYEAR_UNIT,      1 },
 
511
  { "MONTH",    tMONTH_UNIT,     1 },
 
512
  { "FORTNIGHT",tDAY_UNIT,      14 },
 
513
  { "WEEK",     tDAY_UNIT,       7 },
 
514
  { "DAY",      tDAY_UNIT,       1 },
 
515
  { "HOUR",     tHOUR_UNIT,      1 },
 
516
  { "MINUTE",   tMINUTE_UNIT,    1 },
 
517
  { "MIN",      tMINUTE_UNIT,    1 },
 
518
  { "SECOND",   tSEC_UNIT,       1 },
 
519
  { "SEC",      tSEC_UNIT,       1 },
 
520
  { 0, 0, 0 }
 
521
};
 
522
 
 
523
/* Assorted relative-time words. */
 
524
static table const relative_time_table[] =
 
525
{
 
526
  { "TOMORROW", tMINUTE_UNIT,   24 * 60 },
 
527
  { "YESTERDAY",tMINUTE_UNIT,   - (24 * 60) },
 
528
  { "TODAY",    tMINUTE_UNIT,    0 },
 
529
  { "NOW",      tMINUTE_UNIT,    0 },
 
530
  { "LAST",     tUNUMBER,       -1 },
 
531
  { "THIS",     tUNUMBER,        0 },
 
532
  { "NEXT",     tUNUMBER,        1 },
 
533
  { "FIRST",    tUNUMBER,        1 },
 
534
/*{ "SECOND",   tUNUMBER,        2 }, */
 
535
  { "THIRD",    tUNUMBER,        3 },
 
536
  { "FOURTH",   tUNUMBER,        4 },
 
537
  { "FIFTH",    tUNUMBER,        5 },
 
538
  { "SIXTH",    tUNUMBER,        6 },
 
539
  { "SEVENTH",  tUNUMBER,        7 },
 
540
  { "EIGHTH",   tUNUMBER,        8 },
 
541
  { "NINTH",    tUNUMBER,        9 },
 
542
  { "TENTH",    tUNUMBER,       10 },
 
543
  { "ELEVENTH", tUNUMBER,       11 },
 
544
  { "TWELFTH",  tUNUMBER,       12 },
 
545
  { "AGO",      tAGO,            1 },
 
546
  { 0, 0, 0 }
 
547
};
 
548
 
 
549
/* The time zone table.  This table is necessarily incomplete, as time
 
550
   zone abbreviations are ambiguous; e.g. Australians interpret "EST"
 
551
   as Eastern time in Australia, not as US Eastern Standard Time.
 
552
   You cannot rely on getdate to handle arbitrary time zone
 
553
   abbreviations; use numeric abbreviations like `-0500' instead.  */
 
554
static table const time_zone_table[] =
 
555
{
 
556
  { "GMT",      tZONE,     HOUR ( 0) }, /* Greenwich Mean */
 
557
  { "UT",       tZONE,     HOUR ( 0) }, /* Universal (Coordinated) */
 
558
  { "UTC",      tZONE,     HOUR ( 0) },
 
559
  { "WET",      tZONE,     HOUR ( 0) }, /* Western European */
 
560
  { "WEST",     tDAYZONE,  HOUR ( 0) }, /* Western European Summer */
 
561
  { "BST",      tDAYZONE,  HOUR ( 0) }, /* British Summer */
 
562
  { "ART",      tZONE,    -HOUR ( 3) }, /* Argentina */
 
563
  { "BRT",      tZONE,    -HOUR ( 3) }, /* Brazil */
 
564
  { "BRST",     tDAYZONE, -HOUR ( 3) }, /* Brazil Summer */
 
565
  { "NST",      tZONE,   -(HOUR ( 3) + 30) },   /* Newfoundland Standard */
 
566
  { "NDT",      tDAYZONE,-(HOUR ( 3) + 30) },   /* Newfoundland Daylight */
 
567
  { "AST",      tZONE,    -HOUR ( 4) }, /* Atlantic Standard */
 
568
  { "ADT",      tDAYZONE, -HOUR ( 4) }, /* Atlantic Daylight */
 
569
  { "CLT",      tZONE,    -HOUR ( 4) }, /* Chile */
 
570
  { "CLST",     tDAYZONE, -HOUR ( 4) }, /* Chile Summer */
 
571
  { "EST",      tZONE,    -HOUR ( 5) }, /* Eastern Standard */
 
572
  { "EDT",      tDAYZONE, -HOUR ( 5) }, /* Eastern Daylight */
 
573
  { "CST",      tZONE,    -HOUR ( 6) }, /* Central Standard */
 
574
  { "CDT",      tDAYZONE, -HOUR ( 6) }, /* Central Daylight */
 
575
  { "MST",      tZONE,    -HOUR ( 7) }, /* Mountain Standard */
 
576
  { "MDT",      tDAYZONE, -HOUR ( 7) }, /* Mountain Daylight */
 
577
  { "PST",      tZONE,    -HOUR ( 8) }, /* Pacific Standard */
 
578
  { "PDT",      tDAYZONE, -HOUR ( 8) }, /* Pacific Daylight */
 
579
  { "AKST",     tZONE,    -HOUR ( 9) }, /* Alaska Standard */
 
580
  { "AKDT",     tDAYZONE, -HOUR ( 9) }, /* Alaska Daylight */
 
581
  { "HST",      tZONE,    -HOUR (10) }, /* Hawaii Standard */
 
582
  { "HAST",     tZONE,    -HOUR (10) }, /* Hawaii-Aleutian Standard */
 
583
  { "HADT",     tDAYZONE, -HOUR (10) }, /* Hawaii-Aleutian Daylight */
 
584
  { "SST",      tZONE,    -HOUR (12) }, /* Samoa Standard */
 
585
  { "WAT",      tZONE,     HOUR ( 1) }, /* West Africa */
 
586
  { "CET",      tZONE,     HOUR ( 1) }, /* Central European */
 
587
  { "CEST",     tDAYZONE,  HOUR ( 1) }, /* Central European Summer */
 
588
  { "MET",      tZONE,     HOUR ( 1) }, /* Middle European */
 
589
  { "MEZ",      tZONE,     HOUR ( 1) }, /* Middle European */
 
590
  { "MEST",     tDAYZONE,  HOUR ( 1) }, /* Middle European Summer */
 
591
  { "MESZ",     tDAYZONE,  HOUR ( 1) }, /* Middle European Summer */
 
592
  { "EET",      tZONE,     HOUR ( 2) }, /* Eastern European */
 
593
  { "EEST",     tDAYZONE,  HOUR ( 2) }, /* Eastern European Summer */
 
594
  { "CAT",      tZONE,     HOUR ( 2) }, /* Central Africa */
 
595
  { "SAST",     tZONE,     HOUR ( 2) }, /* South Africa Standard */
 
596
  { "EAT",      tZONE,     HOUR ( 3) }, /* East Africa */
 
597
  { "MSK",      tZONE,     HOUR ( 3) }, /* Moscow */
 
598
  { "MSD",      tDAYZONE,  HOUR ( 3) }, /* Moscow Daylight */
 
599
  { "IST",      tZONE,    (HOUR ( 5) + 30) },   /* India Standard */
 
600
  { "SGT",      tZONE,     HOUR ( 8) }, /* Singapore */
 
601
  { "KST",      tZONE,     HOUR ( 9) }, /* Korea Standard */
 
602
  { "JST",      tZONE,     HOUR ( 9) }, /* Japan Standard */
 
603
  { "GST",      tZONE,     HOUR (10) }, /* Guam Standard */
 
604
  { "NZST",     tZONE,     HOUR (12) }, /* New Zealand Standard */
 
605
  { "NZDT",     tDAYZONE,  HOUR (12) }, /* New Zealand Daylight */
 
606
  { 0, 0, 0  }
 
607
};
 
608
 
 
609
/* Military time zone table. */
 
610
static table const military_table[] =
 
611
{
 
612
  { "A", tZONE, -HOUR ( 1) },
 
613
  { "B", tZONE, -HOUR ( 2) },
 
614
  { "C", tZONE, -HOUR ( 3) },
 
615
  { "D", tZONE, -HOUR ( 4) },
 
616
  { "E", tZONE, -HOUR ( 5) },
 
617
  { "F", tZONE, -HOUR ( 6) },
 
618
  { "G", tZONE, -HOUR ( 7) },
 
619
  { "H", tZONE, -HOUR ( 8) },
 
620
  { "I", tZONE, -HOUR ( 9) },
 
621
  { "K", tZONE, -HOUR (10) },
 
622
  { "L", tZONE, -HOUR (11) },
 
623
  { "M", tZONE, -HOUR (12) },
 
624
  { "N", tZONE,  HOUR ( 1) },
 
625
  { "O", tZONE,  HOUR ( 2) },
 
626
  { "P", tZONE,  HOUR ( 3) },
 
627
  { "Q", tZONE,  HOUR ( 4) },
 
628
  { "R", tZONE,  HOUR ( 5) },
 
629
  { "S", tZONE,  HOUR ( 6) },
 
630
  { "T", tZONE,  HOUR ( 7) },
 
631
  { "U", tZONE,  HOUR ( 8) },
 
632
  { "V", tZONE,  HOUR ( 9) },
 
633
  { "W", tZONE,  HOUR (10) },
 
634
  { "X", tZONE,  HOUR (11) },
 
635
  { "Y", tZONE,  HOUR (12) },
 
636
  { "Z", tZONE,  HOUR ( 0) },
 
637
  { 0, 0, 0 }
 
638
};
 
639
 
 
640
 
 
641
 
 
642
static int
 
643
to_hour (int hours, int meridian)
 
644
{
 
645
  switch (meridian)
 
646
    {
 
647
    case MER24:
 
648
      return 0 <= hours && hours < 24 ? hours : -1;
 
649
    case MERam:
 
650
      return 0 < hours && hours < 12 ? hours : hours == 12 ? 0 : -1;
 
651
    case MERpm:
 
652
      return 0 < hours && hours < 12 ? hours + 12 : hours == 12 ? 12 : -1;
 
653
    default:
 
654
      abort ();
 
655
    }
 
656
  /* NOTREACHED */
 
657
    return 0;
 
658
}
 
659
 
 
660
static int
 
661
to_year (textint textyear)
 
662
{
 
663
  int year = textyear.value;
 
664
 
 
665
  if (year < 0)
 
666
    year = -year;
 
667
 
 
668
  /* XPG4 suggests that years 00-68 map to 2000-2068, and
 
669
     years 69-99 map to 1969-1999.  */
 
670
  if (textyear.digits == 2)
 
671
    year += year < 69 ? 2000 : 1900;
 
672
 
 
673
  return year;
 
674
}
 
675
 
 
676
static table const *
 
677
lookup_zone (parser_control const *pc, char const *name)
 
678
{
 
679
  table const *tp;
 
680
 
 
681
  /* Try local zone abbreviations first; they're more likely to be right.  */
 
682
  for (tp = pc->local_time_zone_table; tp->name; tp++)
 
683
    if (strcmp (name, tp->name) == 0)
 
684
      return tp;
 
685
 
 
686
  for (tp = time_zone_table; tp->name; tp++)
 
687
    if (strcmp (name, tp->name) == 0)
 
688
      return tp;
 
689
 
 
690
  return 0;
 
691
}
 
692
 
 
693
#if ! HAVE_TM_GMTOFF
 
694
/* Yield the difference between *A and *B,
 
695
   measured in seconds, ignoring leap seconds.
 
696
   The body of this function is taken directly from the GNU C Library;
 
697
   see src/strftime.c.  */
 
698
static int
 
699
tm_diff (struct tm const *a, struct tm const *b)
 
700
{
 
701
  /* Compute intervening leap days correctly even if year is negative.
 
702
     Take care to avoid int overflow in leap day calculations,
 
703
     but it's OK to assume that A and B are close to each other.  */
 
704
  int a4 = (a->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (a->tm_year & 3);
 
705
  int b4 = (b->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (b->tm_year & 3);
 
706
  int a100 = a4 / 25 - (a4 % 25 < 0);
 
707
  int b100 = b4 / 25 - (b4 % 25 < 0);
 
708
  int a400 = a100 >> 2;
 
709
  int b400 = b100 >> 2;
 
710
  int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
 
711
  int years = a->tm_year - b->tm_year;
 
712
  int days = (365 * years + intervening_leap_days
 
713
              + (a->tm_yday - b->tm_yday));
 
714
  return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
 
715
                + (a->tm_min - b->tm_min))
 
716
          + (a->tm_sec - b->tm_sec));
 
717
}
 
718
#endif /* ! HAVE_TM_GMTOFF */
 
719
 
 
720
static table const *
 
721
lookup_word (parser_control const *pc, char *word)
 
722
{
 
723
  char *p;
 
724
  char *q;
 
725
  size_t wordlen;
 
726
  table const *tp;
 
727
  int i;
 
728
  int abbrev;
 
729
 
 
730
  /* Make it uppercase.  */
 
731
  for (p = word; *p; p++)
 
732
    if (ISLOWER ((unsigned char) *p))
 
733
      *p = toupper ((unsigned char) *p);
 
734
 
 
735
  for (tp = meridian_table; tp->name; tp++)
 
736
    if (strcmp (word, tp->name) == 0)
 
737
      return tp;
 
738
 
 
739
  /* See if we have an abbreviation for a month. */
 
740
  wordlen = strlen (word);
 
741
  abbrev = wordlen == 3 || (wordlen == 4 && word[3] == '.');
 
742
 
 
743
  for (tp = month_and_day_table; tp->name; tp++)
 
744
    if ((abbrev ? strncmp (word, tp->name, 3) : strcmp (word, tp->name)) == 0)
 
745
      return tp;
 
746
 
 
747
  if ((tp = lookup_zone (pc, word)))
 
748
    return tp;
 
749
 
 
750
  if (strcmp (word, dst_table[0].name) == 0)
 
751
    return dst_table;
 
752
 
 
753
  for (tp = time_units_table; tp->name; tp++)
 
754
    if (strcmp (word, tp->name) == 0)
 
755
      return tp;
 
756
 
 
757
  /* Strip off any plural and try the units table again. */
 
758
  if (word[wordlen - 1] == 'S')
 
759
    {
 
760
      word[wordlen - 1] = '\0';
 
761
      for (tp = time_units_table; tp->name; tp++)
 
762
        if (strcmp (word, tp->name) == 0)
 
763
          return tp;
 
764
      word[wordlen - 1] = 'S';  /* For "this" in relative_time_table.  */
 
765
    }
 
766
 
 
767
  for (tp = relative_time_table; tp->name; tp++)
 
768
    if (strcmp (word, tp->name) == 0)
 
769
      return tp;
 
770
 
 
771
  /* Military time zones. */
 
772
  if (wordlen == 1)
 
773
    for (tp = military_table; tp->name; tp++)
 
774
      if (word[0] == tp->name[0])
 
775
        return tp;
 
776
 
 
777
  /* Drop out any periods and try the time zone table again. */
 
778
  for (i = 0, p = q = word; (*p = *q); q++)
 
779
    if (*q == '.')
 
780
      i = 1;
 
781
    else
 
782
      p++;
 
783
  if (i && (tp = lookup_zone (pc, word)))
 
784
    return tp;
 
785
 
 
786
  return 0;
 
787
}
 
788
 
 
789
static int
 
790
yylex (YYSTYPE *lvalp, parser_control *pc)
 
791
{
 
792
  unsigned char c;
 
793
  int count;
 
794
 
 
795
  for (;;)
 
796
    {
 
797
      while (c = *pc->input, ISSPACE (c))
 
798
        pc->input++;
 
799
 
 
800
      if (ISDIGIT (c) || c == '-' || c == '+')
 
801
        {
 
802
          char const *p;
 
803
          int sign;
 
804
          int value;
 
805
          if (c == '-' || c == '+')
 
806
            {
 
807
              sign = c == '-' ? -1 : 1;
 
808
              c = *++pc->input;
 
809
              if (! ISDIGIT (c))
 
810
                /* skip the '-' sign */
 
811
                continue;
 
812
            }
 
813
          else
 
814
            sign = 0;
 
815
          p = pc->input;
 
816
          value = 0;
 
817
          do
 
818
            {
 
819
              value = 10 * value + c - '0';
 
820
              c = *++p;
 
821
            }
 
822
          while (ISDIGIT (c));
 
823
          lvalp->textintval.value = sign < 0 ? -value : value;
 
824
          lvalp->textintval.digits = p - pc->input;
 
825
          pc->input = p;
 
826
          return sign ? tSNUMBER : tUNUMBER;
 
827
        }
 
828
 
 
829
      if (ISALPHA (c))
 
830
        {
 
831
          char buff[20];
 
832
          char *p = buff;
 
833
          table const *tp;
 
834
 
 
835
          do
 
836
            {
 
837
              if (p < buff + sizeof buff - 1)
 
838
                *p++ = c;
 
839
              c = *++pc->input;
 
840
            }
 
841
          while (ISALPHA (c) || c == '.');
 
842
 
 
843
          *p = '\0';
 
844
          tp = lookup_word (pc, buff);
 
845
          if (! tp)
 
846
            return '?';
 
847
          lvalp->intval = tp->value;
 
848
          return tp->type;
 
849
        }
 
850
 
 
851
      if (c != '(')
 
852
        return *pc->input++;
 
853
      count = 0;
 
854
      do
 
855
        {
 
856
          c = *pc->input++;
 
857
          if (c == '\0')
 
858
            return c;
 
859
          if (c == '(')
 
860
            count++;
 
861
          else if (c == ')')
 
862
            count--;
 
863
        }
 
864
      while (count > 0);
 
865
    }
 
866
}
 
867
 
 
868
/* Do nothing if the parser reports an error.  */
 
869
static int
 
870
yyerror (const char *s ATTRIBUTE_UNUSED)
 
871
{
 
872
  return 0;
 
873
}
 
874
 
 
875
/* Parse a date/time string P.  Return the corresponding time_t value,
 
876
   or (time_t) -1 if there is an error.  P can be an incomplete or
 
877
   relative time specification; if so, use *NOW as the basis for the
 
878
   returned time.  */
 
879
time_t
 
880
get_date (const char *p, const time_t *now)
 
881
{
 
882
  time_t Start = now ? *now : time (0);
 
883
  struct tm *tmp = localtime (&Start);
 
884
  struct tm tm;
 
885
  struct tm tm0;
 
886
  parser_control pc;
 
887
 
 
888
  if (! tmp)
 
889
    return -1;
 
890
 
 
891
  pc.input = p;
 
892
  pc.year.value = tmp->tm_year + TM_YEAR_BASE;
 
893
  pc.year.digits = 4;
 
894
  pc.month = tmp->tm_mon + 1;
 
895
  pc.day = tmp->tm_mday;
 
896
  pc.hour = tmp->tm_hour;
 
897
  pc.minutes = tmp->tm_min;
 
898
  pc.seconds = tmp->tm_sec;
 
899
  tm.tm_isdst = tmp->tm_isdst;
 
900
 
 
901
  pc.meridian = MER24;
 
902
  pc.rel_seconds = 0;
 
903
  pc.rel_minutes = 0;
 
904
  pc.rel_hour = 0;
 
905
  pc.rel_day = 0;
 
906
  pc.rel_month = 0;
 
907
  pc.rel_year = 0;
 
908
  pc.dates_seen = 0;
 
909
  pc.days_seen = 0;
 
910
  pc.rels_seen = 0;
 
911
  pc.times_seen = 0;
 
912
  pc.local_zones_seen = 0;
 
913
  pc.zones_seen = 0;
 
914
 
 
915
#if HAVE_STRUCT_TM_TM_ZONE
 
916
  pc.local_time_zone_table[0].name = tmp->tm_zone;
 
917
  pc.local_time_zone_table[0].type = tLOCAL_ZONE;
 
918
  pc.local_time_zone_table[0].value = tmp->tm_isdst;
 
919
  pc.local_time_zone_table[1].name = 0;
 
920
 
 
921
  /* Probe the names used in the next three calendar quarters, looking
 
922
     for a tm_isdst different from the one we already have.  */
 
923
  {
 
924
    int quarter;
 
925
    for (quarter = 1; quarter <= 3; quarter++)
 
926
      {
 
927
        time_t probe = Start + quarter * (90 * 24 * 60 * 60);
 
928
        struct tm *probe_tm = localtime (&probe);
 
929
        if (probe_tm && probe_tm->tm_zone
 
930
            && probe_tm->tm_isdst != pc.local_time_zone_table[0].value)
 
931
          {
 
932
              {
 
933
                pc.local_time_zone_table[1].name = probe_tm->tm_zone;
 
934
                pc.local_time_zone_table[1].type = tLOCAL_ZONE;
 
935
                pc.local_time_zone_table[1].value = probe_tm->tm_isdst;
 
936
                pc.local_time_zone_table[2].name = 0;
 
937
              }
 
938
            break;
 
939
          }
 
940
      }
 
941
  }
 
942
#else
 
943
#if HAVE_TZNAME
 
944
  {
 
945
# ifndef tzname
 
946
    extern char *tzname[];
 
947
# endif
 
948
    int i;
 
949
    for (i = 0; i < 2; i++)
 
950
      {
 
951
        pc.local_time_zone_table[i].name = tzname[i];
 
952
        pc.local_time_zone_table[i].type = tLOCAL_ZONE;
 
953
        pc.local_time_zone_table[i].value = i;
 
954
      }
 
955
    pc.local_time_zone_table[i].name = 0;
 
956
  }
 
957
#else
 
958
  pc.local_time_zone_table[0].name = 0;
 
959
#endif
 
960
#endif
 
961
 
 
962
  if (pc.local_time_zone_table[0].name && pc.local_time_zone_table[1].name
 
963
      && ! strcmp (pc.local_time_zone_table[0].name,
 
964
                   pc.local_time_zone_table[1].name))
 
965
    {
 
966
      /* This locale uses the same abbrevation for standard and
 
967
         daylight times.  So if we see that abbreviation, we don't
 
968
         know whether it's daylight time.  */
 
969
      pc.local_time_zone_table[0].value = -1;
 
970
      pc.local_time_zone_table[1].name = 0;
 
971
    }
 
972
 
 
973
  if (yyparse (&pc) != 0
 
974
      || 1 < pc.times_seen || 1 < pc.dates_seen || 1 < pc.days_seen
 
975
      || 1 < (pc.local_zones_seen + pc.zones_seen)
 
976
      || (pc.local_zones_seen && 1 < pc.local_isdst))
 
977
    return -1;
 
978
 
 
979
  tm.tm_year = to_year (pc.year) - TM_YEAR_BASE + pc.rel_year;
 
980
  tm.tm_mon = pc.month - 1 + pc.rel_month;
 
981
  tm.tm_mday = pc.day + pc.rel_day;
 
982
  if (pc.times_seen || (pc.rels_seen && ! pc.dates_seen && ! pc.days_seen))
 
983
    {
 
984
      tm.tm_hour = to_hour (pc.hour, pc.meridian);
 
985
      if (tm.tm_hour < 0)
 
986
        return -1;
 
987
      tm.tm_min = pc.minutes;
 
988
      tm.tm_sec = pc.seconds;
 
989
    }
 
990
  else
 
991
    {
 
992
      tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
 
993
    }
 
994
 
 
995
  /* Let mktime deduce tm_isdst if we have an absolute time stamp,
 
996
     or if the relative time stamp mentions days, months, or years.  */
 
997
  if (pc.dates_seen | pc.days_seen | pc.times_seen | pc.rel_day
 
998
      | pc.rel_month | pc.rel_year)
 
999
    tm.tm_isdst = -1;
 
1000
 
 
1001
  /* But if the input explicitly specifies local time with or without
 
1002
     DST, give mktime that information.  */
 
1003
  if (pc.local_zones_seen)
 
1004
    tm.tm_isdst = pc.local_isdst;
 
1005
 
 
1006
  tm0 = tm;
 
1007
 
 
1008
  Start = mktime (&tm);
 
1009
 
 
1010
  if (Start == (time_t) -1)
 
1011
    {
 
1012
 
 
1013
      /* Guard against falsely reporting errors near the time_t boundaries
 
1014
         when parsing times in other time zones.  For example, if the min
 
1015
         time_t value is 1970-01-01 00:00:00 UTC and we are 8 hours ahead
 
1016
         of UTC, then the min localtime value is 1970-01-01 08:00:00; if
 
1017
         we apply mktime to 1970-01-01 00:00:00 we will get an error, so
 
1018
         we apply mktime to 1970-01-02 08:00:00 instead and adjust the time
 
1019
         zone by 24 hours to compensate.  This algorithm assumes that
 
1020
         there is no DST transition within a day of the time_t boundaries.  */
 
1021
      if (pc.zones_seen)
 
1022
        {
 
1023
          tm = tm0;
 
1024
          if (tm.tm_year <= EPOCH_YEAR - TM_YEAR_BASE)
 
1025
            {
 
1026
              tm.tm_mday++;
 
1027
              pc.time_zone += 24 * 60;
 
1028
            }
 
1029
          else
 
1030
            {
 
1031
              tm.tm_mday--;
 
1032
              pc.time_zone -= 24 * 60;
 
1033
            }
 
1034
          Start = mktime (&tm);
 
1035
        }
 
1036
 
 
1037
      if (Start == (time_t) -1)
 
1038
        return Start;
 
1039
    }
 
1040
 
 
1041
  if (pc.days_seen && ! pc.dates_seen)
 
1042
    {
 
1043
      tm.tm_mday += ((pc.day_number - tm.tm_wday + 7) % 7
 
1044
                     + 7 * (pc.day_ordinal - (0 < pc.day_ordinal)));
 
1045
      tm.tm_isdst = -1;
 
1046
      Start = mktime (&tm);
 
1047
      if (Start == (time_t) -1)
 
1048
        return Start;
 
1049
    }
 
1050
 
 
1051
  if (pc.zones_seen)
 
1052
    {
 
1053
      int delta = pc.time_zone * 60;
 
1054
#ifdef HAVE_TM_GMTOFF
 
1055
      delta -= tm.tm_gmtoff;
 
1056
#else
 
1057
      struct tm *gmt = gmtime (&Start);
 
1058
      if (! gmt)
 
1059
        return -1;
 
1060
      delta -= tm_diff (&tm, gmt);
 
1061
#endif
 
1062
      if ((Start < Start - delta) != (delta < 0))
 
1063
        return -1;      /* time_t overflow */
 
1064
      Start -= delta;
 
1065
    }
 
1066
 
 
1067
  /* Add relative hours, minutes, and seconds.  Ignore leap seconds;
 
1068
     i.e. "+ 10 minutes" means 600 seconds, even if one of them is a
 
1069
     leap second.  Typically this is not what the user wants, but it's
 
1070
     too hard to do it the other way, because the time zone indicator
 
1071
     must be applied before relative times, and if mktime is applied
 
1072
     again the time zone will be lost.  */
 
1073
  {
 
1074
    time_t t0 = Start;
 
1075
    long d1 = 60 * 60 * (long) pc.rel_hour;
 
1076
    time_t t1 = t0 + d1;
 
1077
    long d2 = 60 * (long) pc.rel_minutes;
 
1078
    time_t t2 = t1 + d2;
 
1079
    int d3 = pc.rel_seconds;
 
1080
    time_t t3 = t2 + d3;
 
1081
    if ((d1 / (60 * 60) ^ pc.rel_hour)
 
1082
        | (d2 / 60 ^ pc.rel_minutes)
 
1083
        | ((t0 + d1 < t0) ^ (d1 < 0))
 
1084
        | ((t1 + d2 < t1) ^ (d2 < 0))
 
1085
        | ((t2 + d3 < t2) ^ (d3 < 0)))
 
1086
      return -1;
 
1087
    Start = t3;
 
1088
  }
 
1089
 
 
1090
  return Start;
 
1091
}
 
1092
 
 
1093
#if TEST
 
1094
 
 
1095
#include <stdio.h>
 
1096
 
 
1097
int
 
1098
main (int ac, char **av)
 
1099
{
 
1100
  char buff[BUFSIZ];
 
1101
  time_t d;
 
1102
 
 
1103
  printf ("Enter date, or blank line to exit.\n\t> ");
 
1104
  fflush (stdout);
 
1105
 
 
1106
  buff[BUFSIZ - 1] = 0;
 
1107
  while (fgets (buff, BUFSIZ - 1, stdin) && buff[0])
 
1108
    {
 
1109
      d = get_date (buff, 0);
 
1110
      if (d == (time_t) -1)
 
1111
        printf ("Bad format - couldn't convert.\n");
 
1112
      else
 
1113
        printf ("%s", ctime (&d));
 
1114
      printf ("\t> ");
 
1115
      fflush (stdout);
 
1116
    }
 
1117
  return 0;
 
1118
}
 
1119
#endif /* defined TEST */