Add isnanf module.
[pspp] / lib / getdate.y
index 591c7f0fcae1cbfc2c79b5a336c7e00f9f9e3aff..695fd59c47e47d850c2cecbdb6d08d82f4b36400 100644 (file)
@@ -1,8 +1,8 @@
 %{
 /* Parse a string into an internal time stamp.
 
-   Copyright (C) 1999, 2000, 2002, 2003, 2004, 2005, 2006, 2007 Free Software
-   Foundation, Inc.
+   Copyright (C) 1999, 2000, 2002, 2003, 2004, 2005, 2006, 2007, 2008
+   Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -66,7 +66,6 @@
 #include <stdlib.h>
 #include <string.h>
 
-#include "setenv.h"
 #include "xalloc.h"
 
 
@@ -208,6 +207,70 @@ static int yylex (union YYSTYPE *, parser_control *);
 static int yyerror (parser_control const *, char const *);
 static long int time_zone_hhmm (textint, long int);
 
+/* Extract into *PC any date and time info from a string of digits
+   of the form e.g., YYYYMMDD, YYMMDD, HHMM, HH (and sometimes YYY,
+   YYYY, ...).  */
+static void
+digits_to_date_time (parser_control *pc, textint text_int)
+{
+  if (pc->dates_seen && ! pc->year.digits
+      && ! pc->rels_seen && (pc->times_seen || 2 < text_int.digits))
+    pc->year = text_int;
+  else
+    {
+      if (4 < text_int.digits)
+       {
+         pc->dates_seen++;
+         pc->day = text_int.value % 100;
+         pc->month = (text_int.value / 100) % 100;
+         pc->year.value = text_int.value / 10000;
+         pc->year.digits = text_int.digits - 4;
+       }
+      else
+       {
+         pc->times_seen++;
+         if (text_int.digits <= 2)
+           {
+             pc->hour = text_int.value;
+             pc->minutes = 0;
+           }
+         else
+           {
+             pc->hour = text_int.value / 100;
+             pc->minutes = text_int.value % 100;
+           }
+         pc->seconds.tv_sec = 0;
+         pc->seconds.tv_nsec = 0;
+         pc->meridian = MER24;
+       }
+    }
+}
+
+/* Increment PC->rel by FACTOR * REL (FACTOR is 1 or -1).  */
+static void
+apply_relative_time (parser_control *pc, relative_time rel, int factor)
+{
+  pc->rel.ns += factor * rel.ns;
+  pc->rel.seconds += factor * rel.seconds;
+  pc->rel.minutes += factor * rel.minutes;
+  pc->rel.hour += factor * rel.hour;
+  pc->rel.day += factor * rel.day;
+  pc->rel.month += factor * rel.month;
+  pc->rel.year += factor * rel.year;
+  pc->rels_seen = true;
+}
+
+/* Set PC-> hour, minutes, seconds and nanoseconds members from arguments.  */
+static void
+set_hhmmss (parser_control *pc, long int hour, long int minutes,
+           time_t sec, long int nsec)
+{
+  pc->hour = hour;
+  pc->minutes = minutes;
+  pc->seconds.tv_sec = sec;
+  pc->seconds.tv_nsec = nsec;
+}
+
 %}
 
 /* We want a reentrant parser, even if the TZ manipulation and the calls to
@@ -275,49 +338,36 @@ item:
   | day
       { pc->days_seen++; }
   | rel
-      { pc->rels_seen = true; }
   | number
+  | hybrid
   ;
 
 time:
     tUNUMBER tMERIDIAN
       {
-       pc->hour = $1.value;
-       pc->minutes = 0;
-       pc->seconds.tv_sec = 0;
-       pc->seconds.tv_nsec = 0;
+       set_hhmmss (pc, $1.value, 0, 0, 0);
        pc->meridian = $2;
       }
   | tUNUMBER ':' tUNUMBER o_merid
       {
-       pc->hour = $1.value;
-       pc->minutes = $3.value;
-       pc->seconds.tv_sec = 0;
-       pc->seconds.tv_nsec = 0;
+       set_hhmmss (pc, $1.value, $3.value, 0, 0);
        pc->meridian = $4;
       }
   | tUNUMBER ':' tUNUMBER tSNUMBER o_colon_minutes
       {
-       pc->hour = $1.value;
-       pc->minutes = $3.value;
-       pc->seconds.tv_sec = 0;
-       pc->seconds.tv_nsec = 0;
+       set_hhmmss (pc, $1.value, $3.value, 0, 0);
        pc->meridian = MER24;
        pc->zones_seen++;
        pc->time_zone = time_zone_hhmm ($4, $5);
       }
   | tUNUMBER ':' tUNUMBER ':' unsigned_seconds o_merid
       {
-       pc->hour = $1.value;
-       pc->minutes = $3.value;
-       pc->seconds = $5;
+       set_hhmmss (pc, $1.value, $3.value, $5.tv_sec, $5.tv_nsec);
        pc->meridian = $6;
       }
   | tUNUMBER ':' tUNUMBER ':' unsigned_seconds tSNUMBER o_colon_minutes
       {
-       pc->hour = $1.value;
-       pc->minutes = $3.value;
-       pc->seconds = $5;
+       set_hhmmss (pc, $1.value, $3.value, $5.tv_sec, $5.tv_nsec);
        pc->meridian = MER24;
        pc->zones_seen++;
        pc->time_zone = time_zone_hhmm ($6, $7);
@@ -342,14 +392,7 @@ zone:
       { pc->time_zone = $1; }
   | tZONE relunit_snumber
       { pc->time_zone = $1;
-       pc->rel.ns += $2.ns;
-       pc->rel.seconds += $2.seconds;
-       pc->rel.minutes += $2.minutes;
-       pc->rel.hour += $2.hour;
-       pc->rel.day += $2.day;
-       pc->rel.month += $2.month;
-       pc->rel.year += $2.year;
-        pc->rels_seen = true; }
+       apply_relative_time (pc, $2, 1); }
   | tZONE tSNUMBER o_colon_minutes
       { pc->time_zone = $1 + time_zone_hhmm ($2, $3); }
   | tDAYZONE
@@ -456,25 +499,9 @@ date:
 
 rel:
     relunit tAGO
-      {
-       pc->rel.ns -= $1.ns;
-       pc->rel.seconds -= $1.seconds;
-       pc->rel.minutes -= $1.minutes;
-       pc->rel.hour -= $1.hour;
-       pc->rel.day -= $1.day;
-       pc->rel.month -= $1.month;
-       pc->rel.year -= $1.year;
-      }
+      { apply_relative_time (pc, $1, -1); }
   | relunit
-      {
-       pc->rel.ns += $1.ns;
-       pc->rel.seconds += $1.seconds;
-       pc->rel.minutes += $1.minutes;
-       pc->rel.hour += $1.hour;
-       pc->rel.day += $1.day;
-       pc->rel.month += $1.month;
-       pc->rel.year += $1.year;
-      }
+      { apply_relative_time (pc, $1, 1); }
   ;
 
 relunit:
@@ -552,38 +579,16 @@ unsigned_seconds:
 
 number:
     tUNUMBER
+      { digits_to_date_time (pc, $1); }
+  ;
+
+hybrid:
+    tUNUMBER relunit_snumber
       {
-       if (pc->dates_seen && ! pc->year.digits
-           && ! pc->rels_seen && (pc->times_seen || 2 < $1.digits))
-         pc->year = $1;
-       else
-         {
-           if (4 < $1.digits)
-             {
-               pc->dates_seen++;
-               pc->day = $1.value % 100;
-               pc->month = ($1.value / 100) % 100;
-               pc->year.value = $1.value / 10000;
-               pc->year.digits = $1.digits - 4;
-             }
-           else
-             {
-               pc->times_seen++;
-               if ($1.digits <= 2)
-                 {
-                   pc->hour = $1.value;
-                   pc->minutes = 0;
-                 }
-               else
-                 {
-                   pc->hour = $1.value / 100;
-                   pc->minutes = $1.value % 100;
-                 }
-               pc->seconds.tv_sec = 0;
-               pc->seconds.tv_nsec = 0;
-               pc->meridian = MER24;
-             }
-         }
+       /* Hybrid all-digit and relative offset, so that we accept e.g.,
+          "YYYYMMDD +N days" as well as "YYYYMMDD N days".  */
+       digits_to_date_time (pc, $1);
+       apply_relative_time (pc, $2, 1);
       }
   ;
 
@@ -1297,7 +1302,7 @@ get_date (struct timespec *result, char const *p, struct timespec const *now)
 #else
 #if HAVE_TZNAME
   {
-# ifndef tzname
+# if !HAVE_DECL_TZNAME
     extern char *tzname[];
 # endif
     int i;
@@ -1412,25 +1417,6 @@ get_date (struct timespec *result, char const *p, struct timespec const *now)
            goto fail;
        }
 
-      if (pc.zones_seen)
-       {
-         long int delta = pc.time_zone * 60;
-         time_t t1;
-#ifdef HAVE_TM_GMTOFF
-         delta -= tm.tm_gmtoff;
-#else
-         time_t t = Start;
-         struct tm const *gmt = gmtime (&t);
-         if (! gmt)
-           goto fail;
-         delta -= tm_diff (&tm, gmt);
-#endif
-         t1 = Start - delta;
-         if ((Start < t1) != (delta < 0))
-           goto fail;  /* time_t overflow */
-         Start = t1;
-       }
-
       /* Add relative date.  */
       if (pc.rel.year | pc.rel.month | pc.rel.day)
        {
@@ -1453,6 +1439,27 @@ get_date (struct timespec *result, char const *p, struct timespec const *now)
            goto fail;
        }
 
+      /* The only "output" of this if-block is an updated Start value,
+        so this block must follow others that clobber Start.  */
+      if (pc.zones_seen)
+       {
+         long int delta = pc.time_zone * 60;
+         time_t t1;
+#ifdef HAVE_TM_GMTOFF
+         delta -= tm.tm_gmtoff;
+#else
+         time_t t = Start;
+         struct tm const *gmt = gmtime (&t);
+         if (! gmt)
+           goto fail;
+         delta -= tm_diff (&tm, gmt);
+#endif
+         t1 = Start - delta;
+         if ((Start < t1) != (delta < 0))
+           goto fail;  /* time_t overflow */
+         Start = t1;
+       }
+
       /* Add relative hours, minutes, and seconds.  On hosts that support
         leap seconds, ignore the possibility of leap seconds; e.g.,
         "+ 10 minutes" adds 600 seconds, even if one of them is a