source: examples/erfsplit/getdate.y @ 5dfa670

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

poink

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