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