~vcs-imports/samba/main

« back to all changes in this revision

Viewing changes to source/modules/getdate.y

  • Committer: jerry
  • Date: 2006-07-14 21:48:39 UTC
  • Revision ID: vcs-imports@canonical.com-20060714214839-586d8c489a8fcead
gutting trunk to move to svn:externals

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