source: examples/erfsplit/getdate.y @ c773929

4.0.1-hotfixescachetimestampsdevelopdpdk-ndagetsilivegetfragoffhelplibtrace4ndag_formatpfringrc-4.0.1rc-4.0.2rc-4.0.3rc-4.0.4ringdecrementfixringperformanceringtimestampfixes
Last change on this file since c773929 was c773929, checked in by Daniel Lawson <dlawson@…>, 16 years ago

added erfsplit into libtrace.

erfsplit builds cleanly, supports date matching and bpf filters

  • Property mode set to 100644
File size: 27.7 KB
Line 
1%{
2/* Parse a string into an internal time stamp.
3   Copyright 1999, 2000 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
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 1003.2-1992 section 2.5.2.1 page 50 lines 1556-1558 says that
67   only '0' through '9' are digits.  Prefer ISDIGIT to ISDIGIT_LOCALE unless
68   it's important to use the locale's definition of `digit' even when the
69   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.  */
91typedef struct
92{
93  int value;
94  int digits;
95} textint;
96
97/* An entry in the lexical lookup table.  */
98typedef struct
99{
100  char const *name;
101  int type;
102  int value;
103} table;
104
105/* Meridian: am, pm, or 24-hour style.  */
106enum { MERam, MERpm, MER24 };
107
108/* Information passed to and from the parser.  */
109typedef 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
161static int yyerror ();
162static 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
189spec:
190    /* empty */
191  | spec item
192  ;
193
194item:
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
210time:
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
251local_zone:
252    tLOCAL_ZONE
253      { PC.local_isdst = $1; }
254  | tLOCAL_ZONE tDST
255      { PC.local_isdst = $1 < 0 ? 1 : $1 + 1; }
256  ;
257
258zone:
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
267day:
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
285date:
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
350rel:
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
363relunit:
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
402number:
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
438o_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 "getdate.h"
451
452#ifndef gmtime
453struct tm *gmtime ();
454#endif
455#ifndef localtime
456struct tm *localtime ();
457#endif
458#ifndef mktime
459time_t mktime ();
460#endif
461
462static 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
471static table const dst_table[] =
472{
473  { "DST", tDST, 0 }
474};
475
476static 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
505static 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. */
521static 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.  */
551static 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. */
607static 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
639static int
640to_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}
655
656static int
657to_year (textint textyear)
658{
659  int year = textyear.value;
660
661  if (year < 0)
662    year = -year;
663
664  /* XPG4 suggests that years 00-68 map to 2000-2068, and
665     years 69-99 map to 1969-1999.  */
666  if (textyear.digits == 2)
667    year += year < 69 ? 2000 : 1900;
668
669  return year;
670}
671
672static table const *
673lookup_zone (parser_control const *pc, char const *name)
674{
675  table const *tp;
676
677  /* Try local zone abbreviations first; they're more likely to be right.  */
678  for (tp = pc->local_time_zone_table; tp->name; tp++)
679    if (strcmp (name, tp->name) == 0)
680      return tp;
681
682  for (tp = time_zone_table; tp->name; tp++)
683    if (strcmp (name, tp->name) == 0)
684      return tp;
685
686  return 0;
687}
688
689#if ! HAVE_TM_GMTOFF
690/* Yield the difference between *A and *B,
691   measured in seconds, ignoring leap seconds.
692   The body of this function is taken directly from the GNU C Library;
693   see src/strftime.c.  */
694static int
695tm_diff (struct tm const *a, struct tm const *b)
696{
697  /* Compute intervening leap days correctly even if year is negative.
698     Take care to avoid int overflow in leap day calculations,
699     but it's OK to assume that A and B are close to each other.  */
700  int a4 = (a->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (a->tm_year & 3);
701  int b4 = (b->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (b->tm_year & 3);
702  int a100 = a4 / 25 - (a4 % 25 < 0);
703  int b100 = b4 / 25 - (b4 % 25 < 0);
704  int a400 = a100 >> 2;
705  int b400 = b100 >> 2;
706  int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
707  int years = a->tm_year - b->tm_year;
708  int days = (365 * years + intervening_leap_days
709              + (a->tm_yday - b->tm_yday));
710  return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
711                + (a->tm_min - b->tm_min))
712          + (a->tm_sec - b->tm_sec));
713}
714#endif /* ! HAVE_TM_GMTOFF */
715
716static table const *
717lookup_word (parser_control const *pc, char *word)
718{
719  char *p;
720  char *q;
721  size_t wordlen;
722  table const *tp;
723  int i;
724  int abbrev;
725
726  /* Make it uppercase.  */
727  for (p = word; *p; p++)
728    if (ISLOWER ((unsigned char) *p))
729      *p = toupper ((unsigned char) *p);
730
731  for (tp = meridian_table; tp->name; tp++)
732    if (strcmp (word, tp->name) == 0)
733      return tp;
734
735  /* See if we have an abbreviation for a month. */
736  wordlen = strlen (word);
737  abbrev = wordlen == 3 || (wordlen == 4 && word[3] == '.');
738
739  for (tp = month_and_day_table; tp->name; tp++)
740    if ((abbrev ? strncmp (word, tp->name, 3) : strcmp (word, tp->name)) == 0)
741      return tp;
742
743  if ((tp = lookup_zone (pc, word)))
744    return tp;
745
746  if (strcmp (word, dst_table[0].name) == 0)
747    return dst_table;
748
749  for (tp = time_units_table; tp->name; tp++)
750    if (strcmp (word, tp->name) == 0)
751      return tp;
752
753  /* Strip off any plural and try the units table again. */
754  if (word[wordlen - 1] == 'S')
755    {
756      word[wordlen - 1] = '\0';
757      for (tp = time_units_table; tp->name; tp++)
758        if (strcmp (word, tp->name) == 0)
759          return tp;
760      word[wordlen - 1] = 'S';  /* For "this" in relative_time_table.  */
761    }
762
763  for (tp = relative_time_table; tp->name; tp++)
764    if (strcmp (word, tp->name) == 0)
765      return tp;
766
767  /* Military time zones. */
768  if (wordlen == 1)
769    for (tp = military_table; tp->name; tp++)
770      if (word[0] == tp->name[0])
771        return tp;
772
773  /* Drop out any periods and try the time zone table again. */
774  for (i = 0, p = q = word; (*p = *q); q++)
775    if (*q == '.')
776      i = 1;
777    else
778      p++;
779  if (i && (tp = lookup_zone (pc, word)))
780    return tp;
781
782  return 0;
783}
784
785static int
786yylex (YYSTYPE *lvalp, parser_control *pc)
787{
788  unsigned char c;
789  int count;
790
791  for (;;)
792    {
793      while (c = *pc->input, ISSPACE (c))
794        pc->input++;
795
796      if (ISDIGIT (c) || c == '-' || c == '+')
797        {
798          char const *p;
799          int sign;
800          int value;
801          if (c == '-' || c == '+')
802            {
803              sign = c == '-' ? -1 : 1;
804              c = *++pc->input;
805              if (! ISDIGIT (c))
806                /* skip the '-' sign */
807                continue;
808            }
809          else
810            sign = 0;
811          p = pc->input;
812          value = 0;
813          do
814            {
815              value = 10 * value + c - '0';
816              c = *++p;
817            }
818          while (ISDIGIT (c));
819          lvalp->textintval.value = sign < 0 ? -value : value;
820          lvalp->textintval.digits = p - pc->input;
821          pc->input = p;
822          return sign ? tSNUMBER : tUNUMBER;
823        }
824
825      if (ISALPHA (c))
826        {
827          char buff[20];
828          char *p = buff;
829          table const *tp;
830
831          do
832            {
833              if (p < buff + sizeof buff - 1)
834                *p++ = c;
835              c = *++pc->input;
836            }
837          while (ISALPHA (c) || c == '.');
838
839          *p = '\0';
840          tp = lookup_word (pc, buff);
841          if (! tp)
842            return '?';
843          lvalp->intval = tp->value;
844          return tp->type;
845        }
846
847      if (c != '(')
848        return *pc->input++;
849      count = 0;
850      do
851        {
852          c = *pc->input++;
853          if (c == '\0')
854            return c;
855          if (c == '(')
856            count++;
857          else if (c == ')')
858            count--;
859        }
860      while (count > 0);
861    }
862}
863
864/* Do nothing if the parser reports an error.  */
865static int
866yyerror (char *s ATTRIBUTE_UNUSED)
867{
868  return 0;
869}
870
871/* Parse a date/time string P.  Return the corresponding time_t value,
872   or (time_t) -1 if there is an error.  P can be an incomplete or
873   relative time specification; if so, use *NOW as the basis for the
874   returned time.  */
875time_t
876get_date (const char *p, const time_t *now)
877{
878  time_t Start = now ? *now : time (0);
879  struct tm *tmp = localtime (&Start);
880  struct tm tm;
881  struct tm tm0;
882  parser_control pc;
883
884  if (! tmp)
885    return -1;
886
887  pc.input = p;
888  pc.year.value = tmp->tm_year + TM_YEAR_BASE;
889  pc.year.digits = 4;
890  pc.month = tmp->tm_mon + 1;
891  pc.day = tmp->tm_mday;
892  pc.hour = tmp->tm_hour;
893  pc.minutes = tmp->tm_min;
894  pc.seconds = tmp->tm_sec;
895  tm.tm_isdst = tmp->tm_isdst;
896
897  pc.meridian = MER24;
898  pc.rel_seconds = 0;
899  pc.rel_minutes = 0;
900  pc.rel_hour = 0;
901  pc.rel_day = 0;
902  pc.rel_month = 0;
903  pc.rel_year = 0;
904  pc.dates_seen = 0;
905  pc.days_seen = 0;
906  pc.rels_seen = 0;
907  pc.times_seen = 0;
908  pc.local_zones_seen = 0;
909  pc.zones_seen = 0;
910
911#if HAVE_TM_ZONE
912  pc.local_time_zone_table[0].name = tmp->tm_zone;
913  pc.local_time_zone_table[0].type = tLOCAL_ZONE;
914  pc.local_time_zone_table[0].value = tmp->tm_isdst;
915  pc.local_time_zone_table[1].name = 0;
916
917  /* Probe the names used in the next three calendar quarters, looking
918     for a tm_isdst different from the one we already have.  */
919  {
920    int quarter;
921    for (quarter = 1; quarter <= 3; quarter++)
922      {
923        time_t probe = Start + quarter * (90 * 24 * 60 * 60);
924        struct tm *probe_tm = localtime (&probe);
925        if (probe_tm && probe_tm->tm_zone
926            && probe_tm->tm_isdst != pc.local_time_zone_table[0].value)
927          {
928              {
929                pc.local_time_zone_table[1].name = probe_tm->tm_zone;
930                pc.local_time_zone_table[1].type = tLOCAL_ZONE;
931                pc.local_time_zone_table[1].value = probe_tm->tm_isdst;
932                pc.local_time_zone_table[2].name = 0;
933              }
934            break;
935          }
936      }
937  }
938#else
939#if HAVE_TZNAME
940  {
941# ifndef tzname
942    extern char *tzname[];
943# endif
944    int i;
945    for (i = 0; i < 2; i++)
946      {
947        pc.local_time_zone_table[i].name = tzname[i];
948        pc.local_time_zone_table[i].type = tLOCAL_ZONE;
949        pc.local_time_zone_table[i].value = i;
950      }
951    pc.local_time_zone_table[i].name = 0;
952  }
953#else
954  pc.local_time_zone_table[0].name = 0;
955#endif
956#endif
957
958  if (pc.local_time_zone_table[0].name && pc.local_time_zone_table[1].name
959      && ! strcmp (pc.local_time_zone_table[0].name,
960                   pc.local_time_zone_table[1].name))
961    {
962      /* This locale uses the same abbrevation for standard and
963         daylight times.  So if we see that abbreviation, we don't
964         know whether it's daylight time.  */
965      pc.local_time_zone_table[0].value = -1;
966      pc.local_time_zone_table[1].name = 0;
967    }
968
969  if (yyparse (&pc) != 0
970      || 1 < pc.times_seen || 1 < pc.dates_seen || 1 < pc.days_seen
971      || 1 < (pc.local_zones_seen + pc.zones_seen)
972      || (pc.local_zones_seen && 1 < pc.local_isdst))
973    return -1;
974
975  tm.tm_year = to_year (pc.year) - TM_YEAR_BASE + pc.rel_year;
976  tm.tm_mon = pc.month - 1 + pc.rel_month;
977  tm.tm_mday = pc.day + pc.rel_day;
978  if (pc.times_seen || (pc.rels_seen && ! pc.dates_seen && ! pc.days_seen))
979    {
980      tm.tm_hour = to_hour (pc.hour, pc.meridian);
981      if (tm.tm_hour < 0)
982        return -1;
983      tm.tm_min = pc.minutes;
984      tm.tm_sec = pc.seconds;
985    }
986  else
987    {
988      tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
989    }
990
991  /* Let mktime deduce tm_isdst if we have an absolute time stamp,
992     or if the relative time stamp mentions days, months, or years.  */
993  if (pc.dates_seen | pc.days_seen | pc.times_seen | pc.rel_day | pc.rel_month | pc.rel_year)
994    tm.tm_isdst = -1;
995
996  /* But if the input explicitly specifies local time with or without
997     DST, give mktime that information.  */
998  if (pc.local_zones_seen)
999    tm.tm_isdst = pc.local_isdst;
1000
1001  tm0 = tm;
1002
1003  Start = mktime (&tm);
1004
1005  if (Start == (time_t) -1)
1006    {
1007
1008      /* Guard against falsely reporting errors near the time_t boundaries
1009         when parsing times in other time zones.  For example, if the min
1010         time_t value is 1970-01-01 00:00:00 UTC and we are 8 hours ahead
1011         of UTC, then the min localtime value is 1970-01-01 08:00:00; if
1012         we apply mktime to 1970-01-01 00:00:00 we will get an error, so
1013         we apply mktime to 1970-01-02 08:00:00 instead and adjust the time
1014         zone by 24 hours to compensate.  This algorithm assumes that
1015         there is no DST transition within a day of the time_t boundaries.  */
1016      if (pc.zones_seen)
1017        {
1018          tm = tm0;
1019          if (tm.tm_year <= EPOCH_YEAR - TM_YEAR_BASE)
1020            {
1021              tm.tm_mday++;
1022              pc.time_zone += 24 * 60;
1023            }
1024          else
1025            {
1026              tm.tm_mday--;
1027              pc.time_zone -= 24 * 60;
1028            }
1029          Start = mktime (&tm);
1030        }
1031
1032      if (Start == (time_t) -1)
1033        return Start;
1034    }
1035
1036  if (pc.days_seen && ! pc.dates_seen)
1037    {
1038      tm.tm_mday += ((pc.day_number - tm.tm_wday + 7) % 7
1039                     + 7 * (pc.day_ordinal - (0 < pc.day_ordinal)));
1040      Start = mktime (&tm);
1041      if (Start == (time_t) -1)
1042        return Start;
1043    }
1044
1045  if (pc.zones_seen)
1046    {
1047      int delta = pc.time_zone * 60;
1048#ifdef HAVE_TM_GMTOFF
1049      delta -= tm.tm_gmtoff;
1050#else
1051      struct tm *gmt = gmtime (&Start);
1052      if (! gmt)
1053        return -1;
1054      delta -= tm_diff (&tm, gmt);
1055#endif
1056      if ((Start < Start - delta) != (delta < 0))
1057        return -1;      /* time_t overflow */
1058      Start -= delta;
1059    }
1060
1061  /* Add relative hours, minutes, and seconds.  Ignore leap seconds;
1062     i.e. "+ 10 minutes" means 600 seconds, even if one of them is a
1063     leap second.  Typically this is not what the user wants, but it's
1064     too hard to do it the other way, because the time zone indicator
1065     must be applied before relative times, and if mktime is applied
1066     again the time zone will be lost.  */
1067  {
1068    time_t t0 = Start;
1069    long d1 = 60 * 60 * (long) pc.rel_hour;
1070    time_t t1 = t0 + d1;
1071    long d2 = 60 * (long) pc.rel_minutes;
1072    time_t t2 = t1 + d2;
1073    int d3 = pc.rel_seconds;
1074    time_t t3 = t2 + d3;
1075    if ((d1 / (60 * 60) ^ pc.rel_hour)
1076        | (d2 / 60 ^ pc.rel_minutes)
1077        | ((t0 + d1 < t0) ^ (d1 < 0))
1078        | ((t1 + d2 < t1) ^ (d2 < 0))
1079        | ((t2 + d3 < t2) ^ (d3 < 0)))
1080      return -1;
1081    Start = t3;
1082  }
1083
1084  return Start;
1085}
1086
1087#if TEST
1088
1089#include <stdio.h>
1090
1091int
1092main (int ac, char **av)
1093{
1094  char buff[BUFSIZ];
1095  time_t d;
1096
1097  printf ("Enter date, or blank line to exit.\n\t> ");
1098  fflush (stdout);
1099
1100  buff[BUFSIZ - 1] = 0;
1101  while (fgets (buff, BUFSIZ - 1, stdin) && buff[0])
1102    {
1103      d = get_date (buff, 0);
1104      if (d == (time_t) -1)
1105        printf ("Bad format - couldn't convert.\n");
1106      else
1107        printf ("%s", ctime (&d));
1108      printf ("\t> ");
1109      fflush (stdout);
1110    }
1111  return 0;
1112}
1113#endif /* defined TEST */
Note: See TracBrowser for help on using the repository browser.