Adopt use of gnulib for portability.
[pspp-builds.git] / src / data-in.c
index eca83d0b44a96764d18b69171a15644284ad4bee..879ad10bd4fc3442a0be3935eb7ba5b0c70875a4 100644 (file)
 
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
-   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
-   02111-1307, USA. */
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA. */
 
 #include <config.h>
 #include "data-in.h"
-#include <assert.h>
+#include "error.h"
 #include <math.h>
 #include <ctype.h>
 #include <stdarg.h>
 #include <stddef.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <stdbool.h>
 #include "error.h"
-#include "getline.h"
-#include "julcal/julcal.h"
+#include "getl.h"
+#include "calendar.h"
 #include "lexer.h"
 #include "magic.h"
 #include "misc.h"
 #include "settings.h"
 #include "str.h"
 #include "var.h"
-\f
-#include "debug-print.h"
 
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
+#include "debug-print.h"
 \f
 /* Specialized error routine. */
 
 static void dls_error (const struct data_in *, const char *format, ...)
-     __attribute__ ((format (printf, 2, 3)));
+     PRINTF_FORMAT (2, 3);
 
 static void
-dls_error (const struct data_in *i, const char *format, ...)
+vdls_error (const struct data_in *i, const char *format, va_list args)
 {
-  char buf[1024];
+  struct error e;
+  struct string title;
 
   if (i->flags & DI_IGNORE_ERROR)
     return;
 
-  {
-    va_list args;
-
-    va_start (args, format);
-    snprintf (buf, 1024, format, args);
-    va_end (args);
-  }
-  
-  {
-    struct error e;
-    struct string title;
-
-    ds_init (NULL, &title, 64);
-    if (!getl_reading_script)
-      ds_concat (&title, _("data-file error: "));
-    if (i->f1 == i->f2)
-      ds_printf (&title, _("(column %d"), i->f1);
-    else
-      ds_printf (&title, _("(columns %d-%d"), i->f1, i->f2);
-    ds_printf (&title, _(", field type %s) "), fmt_to_string (&i->format));
+  ds_init (&title, 64);
+  if (!getl_reading_script)
+    ds_puts (&title, _("data-file error: "));
+  if (i->f1 == i->f2)
+    ds_printf (&title, _("(column %d"), i->f1);
+  else
+    ds_printf (&title, _("(columns %d-%d"), i->f1, i->f2);
+  ds_printf (&title, _(", field type %s) "), fmt_to_string (&i->format));
     
-    e.class = DE;
-    err_location (&e.where);
-    e.title = ds_value (&title);
-    e.text = buf;
+  e.class = DE;
+  err_location (&e.where);
+  e.title = ds_c_str (&title);
 
-    err_vmsg (&e);
+  err_vmsg (&e, format, args);
 
-    ds_destroy (&title);
-  }
+  ds_destroy (&title);
 }
 
+static void
+dls_error (const struct data_in *i, const char *format, ...) 
+{
+  va_list args;
+
+  va_start (args, format);
+  vdls_error (i, format, args);
+  va_end (args);
+}
+\f
+/* Parsing utility functions. */
+
 /* Excludes leading and trailing whitespace from I by adjusting
    pointers. */
 static void
@@ -98,25 +100,34 @@ trim_whitespace (struct data_in *i)
 
 /* Returns nonzero if we're not at the end of the string being
    parsed. */
-static inline int
+static inline bool
 have_char (struct data_in *i)
 {
   return i->s < i->e;
 }
+
+/* If implied decimal places are enabled, apply them to
+   I->v->f. */
+static void
+apply_implied_decimals (struct data_in *i) 
+{
+  if ((i->flags & DI_IMPLIED_DECIMALS) && i->format.d > 0)
+    i->v->f /= pow (10., i->format.d);
+}
 \f
 /* Format parsers. */ 
 
-static int parse_int (struct data_in *i, long *result);
+static bool parse_int (struct data_in *i, long *result);
 
 /* This function is based on strtod() from the GNU C library. */
-static int
+static bool
 parse_numeric (struct data_in *i)
 {
-  short int sign;              /* +1 or -1. */
+  int sign;                     /* +1 or -1. */
   double num;                  /* The number so far.  */
 
-  int got_dot;                 /* Found a decimal point.  */
-  int got_digit;               /* Count of digits.  */
+  bool got_dot;                        /* Found a decimal point.  */
+  size_t digit_cnt;            /* Count of digits.  */
 
   int decimal;                 /* Decimal point character. */
   int grouping;                        /* Grouping character. */
@@ -140,28 +151,30 @@ parse_numeric (struct data_in *i)
       if (*i->s == '-' || *i->s == '+')
        i->s++;
     }
+  else
+    sign = 1;
   
   if (type != FMT_DOT)
     {
-      decimal = set_decimal;
-      grouping = set_grouping;
+      decimal = get_decimal();
+      grouping = get_grouping();
     }
   else
     {
-      decimal = set_grouping;
-      grouping = set_decimal;
+      decimal = get_grouping();
+      grouping = get_decimal();
     }
 
   i->v->f = SYSMIS;
   num = 0.0;
-  got_dot = 0;
-  got_digit = 0;
+  got_dot = false;
+  digit_cnt = 0;
   exponent = 0;
   for (; have_char (i); i->s++)
     {
       if (isdigit (*i->s))
        {
-         got_digit++;
+         digit_cnt++;
 
          /* Make sure that multiplication by 10 will not overflow.  */
          if (num > DBL_MAX * 0.1)
@@ -183,25 +196,25 @@ parse_numeric (struct data_in *i)
        }
       else if (!got_dot && *i->s == decimal)
        /* Record that we have found the decimal point.  */
-       got_dot = 1;
+       got_dot = true;
       else if ((type != FMT_COMMA && type != FMT_DOT) || *i->s != grouping)
        /* Any other character terminates the number.  */
        break;
     }
 
-  if (!got_digit)
+  if (!digit_cnt)
     {
       if (got_dot)
        {
          i->v->f = SYSMIS;
-         return 1;
+         return true;
        }
-      goto noconv;
+      dls_error (i, _("Field does not form a valid floating-point constant."));
+      i->v->f = SYSMIS;
+      return false;
     }
   
-  if (have_char (i)
-      && (tolower (*i->s) == 'e' || tolower (*i->s) == 'd'
-         || (type == FMT_E && (*i->s == '+' || *i->s == '-'))))
+  if (have_char (i) && strchr ("eEdD-+", *i->s))
     {
       /* Get the exponent specified after the `e' or `E'.  */
       long exp;
@@ -209,11 +222,14 @@ parse_numeric (struct data_in *i)
       if (isalpha (*i->s))
        i->s++;
       if (!parse_int (i, &exp))
-       goto noconv;
+        {
+          i->v->f = SYSMIS;
+          return false;
+        }
 
       exponent += exp;
     }
-  else if (!got_dot)
+  else if (!got_dot && (i->flags & DI_IMPLIED_DECIMALS))
     exponent -= i->format.d;
 
   if (type == FMT_PCT && have_char (i) && *i->s == '%')
@@ -222,52 +238,43 @@ parse_numeric (struct data_in *i)
     {
       dls_error (i, _("Field contents followed by garbage."));
       i->v->f = SYSMIS;
-      return 0;
+      return false;
     }
 
   if (num == 0.0)
     {
       i->v->f = 0.0;
-      return 1;
+      return true;
     }
 
   /* Multiply NUM by 10 to the EXPONENT power, checking for overflow
      and underflow.  */
-
   if (exponent < 0)
     {
-      if (-exponent + got_digit > -(DBL_MIN_10_EXP) + 5
-         || num < DBL_MIN * pow (10.0, (double) -exponent))
-       goto underflow;
+      if (-exponent + digit_cnt > -(DBL_MIN_10_EXP) + 5
+         || num < DBL_MIN * pow (10.0, (double) -exponent)) 
+        {
+          dls_error (i, _("Underflow in floating-point constant."));
+          i->v->f = 0.0;
+          return false;
+        }
+
       num *= pow (10.0, (double) exponent);
     }
   else if (exponent > 0)
     {
       if (num > DBL_MAX * pow (10.0, (double) -exponent))
-       goto overflow;
+        {
+          dls_error (i, _("Overflow in floating-point constant."));
+          i->v->f = SYSMIS;
+          return false;
+        }
+      
       num *= pow (10.0, (double) exponent);
     }
 
-  i->v->f = sign * num;
-  return 1;
-
-overflow:
-  /* Return an overflow error.  */
-  dls_error (i, _("Overflow in floating-point constant."));
-  i->v->f = SYSMIS;
-  return 0;
-
-underflow:
-  /* Return an underflow error.  */
-  dls_error (i, _("Underflow in floating-point constant."));
-  i->v->f = 0.0;
-  return 0;
-
-noconv:
-  /* There was no number.  */
-  dls_error (i, _("Field does not form a valid floating-point constant."));
-  i->v->f = SYSMIS;
-  return 0;
+  i->v->f = sign > 0 ? num : -num;
+  return true;
 }
 
 /* Returns the integer value of hex digit C. */
@@ -281,7 +288,7 @@ hexit_value (int c)
   return cp - s;
 }
 
-static inline int
+static inline bool
 parse_N (struct data_in *i)
 {
   const unsigned char *cp;
@@ -292,18 +299,17 @@ parse_N (struct data_in *i)
       if (!isdigit (*cp))
        {
          dls_error (i, _("All characters in field must be digits."));
-         return 0;
+         return false;
        }
 
       i->v->f = i->v->f * 10.0 + *cp - '0';
     }
 
-  if (i->format.d)
-    i->v->f /= pow (10.0, i->format.d);
-  return 1;
+  apply_implied_decimals (i);
+  return true;
 }
 
-static inline int
+static inline bool
 parse_PIBHEX (struct data_in *i)
 {
   double n;
@@ -317,17 +323,17 @@ parse_PIBHEX (struct data_in *i)
       if (!isxdigit (*cp))
        {
          dls_error (i, _("Unrecognized character in field."));
-         return 0;
+         return false;
        }
 
       n = n * 16.0 + hexit_value (*cp);
     }
   
   i->v->f = n;
-  return 1;
+  return true;
 }
 
-static inline int
+static inline bool
 parse_RBHEX (struct data_in *i)
 {
   /* Validate input. */
@@ -335,7 +341,7 @@ parse_RBHEX (struct data_in *i)
   if ((i->e - i->s) % 2)
     {
       dls_error (i, _("Field must have even length."));
-      return 0;
+      return false;
     }
   
   {
@@ -345,7 +351,7 @@ parse_RBHEX (struct data_in *i)
       if (!isxdigit (*cp))
        {
          dls_error (i, _("Field must contain only hex digits."));
-         return 0;
+         return false;
        }
   }
   
@@ -367,24 +373,26 @@ parse_RBHEX (struct data_in *i)
     i->v->f = u.d;
   }
   
-  return 1;
+  return true;
 }
 
-static inline int
+static inline bool
 parse_Z (struct data_in *i)
 {
   char buf[64];
+  bool got_dot = false;
 
   /* Warn user that we suck. */
   {
-    static int warned;
+    static bool warned;
 
     if (!warned)
       {
-       msg (MW, _("Quality of zoned decimal (Z) input format code is "
-                  "suspect.  Check your results three times, report bugs "
-                  "to author."));
-       warned = 1;
+       msg (MW, 
+            _("Quality of zoned decimal (Z) input format code is "
+              "suspect.  Check your results three times. Report bugs "
+               "to %s."),PACKAGE_BUGREPORT);
+       warned = true;
       }
   }
 
@@ -395,14 +403,14 @@ parse_Z (struct data_in *i)
     {
       dls_error (i, _("Zoned decimal field contains fewer than 2 "
                      "characters."));
-      return 0;
+      return false;
     }
 
   /* Copy sign into buf[0]. */
   if ((i->e[-1] & 0xc0) != 0xc0)
     {
       dls_error (i, _("Bad sign byte in zoned decimal number."));
-      return 0;
+      return false;
     }
   buf[0] = (i->e[-1] ^ (i->e[-1] >> 1)) & 0x10 ? '-' : '+';
 
@@ -412,14 +420,17 @@ parse_Z (struct data_in *i)
     char *dp;
 
     for (sp = i->s, dp = buf + 1; sp < i->e - 1; sp++, dp++)
-      if (*sp == '.')
-       *dp = '.';
+      if (*sp == '.') 
+        {
+          *dp = '.';
+          got_dot = true;
+        }
       else if ((*sp & 0xf0) == 0xf0 && (*sp & 0xf) < 10)
        *dp = (*sp & 0xf) + '0';
       else
        {
          dls_error (i, _("Format error in zoned decimal number."));
-         return 0;
+         return false;
        }
 
     *dp = '\0';
@@ -433,17 +444,22 @@ parse_Z (struct data_in *i)
     if ((unsigned char *) tail != i->e)
       {
        dls_error (i, _("Error in syntax of zoned decimal number."));
-       return 0;
+       return false;
       }
   }
-  
-  return 1;
+
+  if (!got_dot)
+    apply_implied_decimals (i);
+
+  return true;
 }
 
-static inline int
+static inline bool
 parse_IB (struct data_in *i)
 {
+#ifndef WORDS_BIGENDIAN
   char buf[64];
+#endif
   const char *p;
 
   unsigned char xor;
@@ -454,7 +470,7 @@ parse_IB (struct data_in *i)
   p = i->s;
 #else
   memcpy (buf, i->s, i->e - i->s);
-  mm_reverse (buf, i->e - i->s);
+  buf_reverse (buf, i->e - i->s);
   p = buf;
 #endif
 
@@ -478,13 +494,12 @@ parse_IB (struct data_in *i)
   if (p[0] & 0x80)
     i->v->f = -(i->v->f + 1.0);
 
-  if (i->format.d)
-    i->v->f /= pow (10.0, i->format.d);
+  apply_implied_decimals (i);
 
-  return 1;
+  return true;
 }
 
-static inline int
+static inline bool
 parse_PIB (struct data_in *i)
 {
   int j;
@@ -498,13 +513,12 @@ parse_PIB (struct data_in *i)
     i->v->f = i->v->f * 256.0 + i->s[j];
 #endif
 
-  if (i->format.d)
-    i->v->f /= pow (10.0, i->format.d);
+  apply_implied_decimals (i);
 
-  return 1;
+  return true;
 }
 
-static inline int
+static inline bool
 parse_P (struct data_in *i)
 {
   const unsigned char *cp;
@@ -519,13 +533,12 @@ parse_P (struct data_in *i)
   if ((*cp ^ (*cp >> 1)) & 0x10)
       i->v->f = -i->v->f;
 
-  if (i->format.d)
-    i->v->f /= pow (10.0, i->format.d);
+  apply_implied_decimals (i);
 
-  return 1;
+  return true;
 }
 
-static inline int
+static inline bool
 parse_PK (struct data_in *i)
 {
   const unsigned char *cp;
@@ -537,13 +550,12 @@ parse_PK (struct data_in *i)
       i->v->f = i->v->f * 10 + (*cp & 15);
     }
 
-  if (i->format.d)
-    i->v->f /= pow (10.0, i->format.d);
+  apply_implied_decimals (i);
 
-  return 1;
+  return true;
 }
 
-static inline int
+static inline bool
 parse_RB (struct data_in *i)
 {
   union
@@ -557,10 +569,10 @@ parse_RB (struct data_in *i)
   memcpy (u.c, i->s, min ((int) sizeof (u.c), i->e - i->s));
   i->v->f = u.d;
 
-  return 1;
+  return true;
 }
 
-static inline int
+static inline bool
 parse_A (struct data_in *i)
 {
   ptrdiff_t len = i->e - i->s;
@@ -573,10 +585,10 @@ parse_A (struct data_in *i)
       memset (i->v->s + len, ' ', i->format.w - len);
     }
 
-  return 1;
+  return true;
 }
 
-static inline int
+static inline bool
 parse_AHEX (struct data_in *i)
 {
   /* Validate input. */
@@ -584,7 +596,7 @@ parse_AHEX (struct data_in *i)
   if ((i->e - i->s) % 2)
     {
       dls_error (i, _("Field must have even length."));
-      return 0;
+      return false;
     }
 
   {
@@ -594,7 +606,7 @@ parse_AHEX (struct data_in *i)
       if (!isxdigit (*cp))
        {
          dls_error (i, _("Field must contain only hex digits."));
-         return 0;
+         return false;
        }
   }
   
@@ -607,7 +619,7 @@ parse_AHEX (struct data_in *i)
     memset (i->v->s + (i->e - i->s) / 2, ' ', (i->format.w - (i->e - i->s)) / 2);
   }
   
-  return 1;
+  return true;
 }
 \f
 /* Date & time format components. */
@@ -620,30 +632,30 @@ skip_whitespace (struct data_in *i)
     i->s++;
 }
 
-static inline int
+static inline bool
 parse_leader (struct data_in *i)
 {
   skip_whitespace (i);
-  return 1;
+  return true;
 }
 
-static inline int
+static inline bool
 force_have_char (struct data_in *i)
 {
   if (have_char (i))
-    return 1;
+    return true;
 
   dls_error (i, _("Unexpected end of field."));
-  return 0;
+  return false;
 }
 
-static int
+static bool
 parse_int (struct data_in *i, long *result)
 {
-  int negative = 0;
+  bool negative = false;
   
   if (!force_have_char (i))
-    return 0;
+    return false;
 
   if (*i->s == '+')
     {
@@ -652,7 +664,7 @@ parse_int (struct data_in *i, long *result)
     }
   else if (*i->s == '-')
     {
-      negative = 1;
+      negative = true;
       i->s++;
       force_have_char (i);
     }
@@ -660,7 +672,7 @@ parse_int (struct data_in *i, long *result)
   if (!isdigit (*i->s))
     {
       dls_error (i, _("Digit expected in field."));
-      return 0;
+      return false;
     }
 
   *result = 0;
@@ -673,254 +685,177 @@ parse_int (struct data_in *i, long *result)
 
   if (negative)
     *result = -*result;
-  return 1;
+  return true;
 }
 
-static int
+static bool
 parse_day (struct data_in *i, long *day)
 {
   if (!parse_int (i, day))
-    return 0;
+    return false;
   if (*day >= 1 && *day <= 31)
-    return 1;
+    return true;
 
   dls_error (i, _("Day (%ld) must be between 1 and 31."), *day);
-  return 0;
+  return false;
 }
 
-static int
+static bool
 parse_day_count (struct data_in *i, long *day_count)
 {
   return parse_int (i, day_count);
 }
 
-static int
+static bool
 parse_date_delimiter (struct data_in *i)
 {
-  int delim = 0;
+  bool delim = false;
 
   while (have_char (i)
         && (*i->s == '-' || *i->s == '/' || isspace (*i->s)
             || *i->s == '.' || *i->s == ','))
     {
-      delim = 1;
+      delim = true;
       i->s++;
     }
   if (delim)
-    return 1;
+    return true;
 
   dls_error (i, _("Delimiter expected between fields in date."));
-  return 0;
+  return false;
 }
 
-/* Formats NUMBER as Roman numerals in ROMAN, or as Arabic numerals if
-   the Roman expansion would be too long. */
-static void
-to_roman (int number, char roman[32])
-{
-  int save_number = number;
-
-  struct roman_digit
-    {
-      int value;               /* Value corresponding to this digit. */
-      char name;               /* Digit name. */
-    };
-
-  static const struct roman_digit roman_tab[7] =
+/* Association between a name and a value. */
+struct enum_name
   {
-    {1000, 'M'},
-    {500, 'D'},
-    {100, 'C'},
-    {50, 'L'},
-    {10, 'X'},
-    {5, 'V'},
-    {1, 'I'},
+    const char *name;           /* Name. */
+    bool can_abbreviate;        /* True if name may be abbreviated. */
+    int value;                  /* Value associated with name. */
   };
 
-  char *cp = roman;
-
-  int i, j;
-
-  assert (32 >= INT_DIGITS + 1);
-  if (number == 0)
-    goto arabic;
-
-  if (number < 0)
+/* Reads a name from I and sets *OUTPUT to the value associated
+   with that name.  Returns true if successful, false otherwise. */
+static bool
+parse_enum (struct data_in *i, const char *what,
+            const struct enum_name *enum_names,
+            long *output) 
+{
+  const char *name;
+  size_t length;
+  const struct enum_name *ep;
+
+  /* Consume alphabetic characters. */
+  name = i->s;
+  length = 0;
+  while (have_char (i) && isalpha (*i->s)) 
     {
-      *cp++ = '-';
-      number = -number;
+      length++;
+      i->s++; 
     }
-
-  for (i = 0; i < 7; i++)
+  if (length == 0) 
     {
-      int digit = roman_tab[i].value;
-      while (number >= digit)
-       {
-         number -= digit;
-         if (cp > &roman[30])
-           goto arabic;
-         *cp++ = roman_tab[i].name;
-       }
-
-      for (j = i + 1; j < 7; j++)
-       {
-         if (i == 4 && j == 5) /* VX is not a shortened form of V. */
-           break;
-
-         digit = roman_tab[i].value - roman_tab[j].value;
-         while (number >= digit)
-           {
-             number -= digit;
-             if (cp > &roman[29])
-               goto arabic;
-             *cp++ = roman_tab[j].name;
-             *cp++ = roman_tab[i].name;
-           }
-       }
+      dls_error (i, _("Parse error at `%c' expecting %s."), *i->s, what);
+      return false;
     }
-  *cp = 0;
-  return;
 
-arabic:
-  sprintf (roman, "%d", save_number);
-}
-
-/* Returns true if C is a (lowercase) roman numeral. */
-#define CHAR_IS_ROMAN(C)                               \
-       ((C) == 'x' || (C) == 'v' || (C) == 'i')
+  for (ep = enum_names; ep->name != NULL; ep++)
+    if ((ep->can_abbreviate
+         && lex_id_match_len (ep->name, strlen (ep->name), name, length))
+        || (!ep->can_abbreviate && length == strlen (ep->name)
+            && !buf_compare_case (name, ep->name, length)))
+      {
+        *output = ep->value;
+        return true;
+      }
 
-/* Returns the value of a single (lowercase) roman numeral. */
-#define ROMAN_VALUE(C)                         \
-       ((C) == 'x' ? 10 : ((C) == 'v' ? 5 : 1))
+  dls_error (i, _("Unknown %s `%.*s'."), what, (int) length, name);
+  return false;
+}
 
-static int
+static bool
 parse_month (struct data_in *i, long *month)
 {
+  static const struct enum_name month_names[] = 
+    {
+      {"january", true, 1},
+      {"february", true, 2},
+      {"march", true, 3},
+      {"april", true, 4},
+      {"may", true, 5},
+      {"june", true, 6},
+      {"july", true, 7},
+      {"august", true, 8},
+      {"september", true, 9},
+      {"october", true, 10},
+      {"november", true, 11},
+      {"december", true, 12},
+
+      {"i", false, 1},
+      {"ii", false, 2},
+      {"iii", false, 3},
+      {"iv", false, 4},
+      {"iiii", false, 4},
+      {"v", false, 5},
+      {"vi", false, 6},
+      {"vii", false, 7},
+      {"viii", false, 8},
+      {"ix", false, 9},
+      {"viiii", false, 9},
+      {"x", false, 10},
+      {"xi", false, 11},
+      {"xii", false, 12},
+
+      {NULL, false, 0},
+    };
+
   if (!force_have_char (i))
-    return 0;
+    return false;
   
   if (isdigit (*i->s))
     {
       if (!parse_int (i, month))
-       return 0;
+       return false;
       if (*month >= 1 && *month <= 12)
-       return 1;
+       return true;
       
       dls_error (i, _("Month (%ld) must be between 1 and 12."), *month);
-      return 0;
-    }
-
-  if (CHAR_IS_ROMAN (tolower (*i->s)))
-    {
-      int last = ROMAN_VALUE (tolower (*i->s));
-
-      *month = 0;
-      for (;;)
-       {
-         int value;
-
-         i->s++;
-         if (!have_char || !CHAR_IS_ROMAN (tolower (*i->s)))
-           {
-             if (last != INT_MAX)
-               *month += last;
-             break;
-           }
-
-         value = ROMAN_VALUE (tolower (*i->s));
-         if (last == INT_MAX)
-           *month += value;
-         else if (value > last)
-           {
-             *month += value - last;
-             last = INT_MAX;
-           }
-         else
-           {
-             *month += last;
-             last = value;
-           }
-       }
-
-      if (*month < 1 || *month > 12)
-       {
-         char buf[32];
-
-         to_roman (*month, buf);
-         dls_error (i, _("Month (%s) must be between I and XII."), buf);
-         return 0;
-       }
-      
-      return 1;
+      return false;
     }
-  
-  {
-    static const char *months[12] =
-      {
-       "january", "february", "march", "april", "may", "june",
-       "july", "august", "september", "october", "november", "december",
-      };
-
-    char month_buf[32];
-    char *mp;
-
-    int j;
-
-    for (mp = month_buf;
-        have_char (i) && isalpha (*i->s) && mp < &month_buf[31];
-        i->s++)
-      *mp++ = tolower (*i->s);
-    *mp = '\0';
-
-    if (have_char (i) && isalpha (*i->s))
-      {
-       dls_error (i, _("Month name (%s...) is too long."), month_buf);
-       return 0;
-      }
-
-    for (j = 0; j < 12; j++)
-      if (lex_id_match (months[j], month_buf))
-       {
-         *month = j + 1;
-         return 1;
-       }
-
-    dls_error (i, _("Bad month name (%s)."), month_buf);
-    return 0;
-  }
+  else 
+    return parse_enum (i, _("month"), month_names, month);
 }
 
-static int
+static bool
 parse_year (struct data_in *i, long *year)
 {
   if (!parse_int (i, year))
-    return 0;
+    return false;
   
   if (*year >= 0 && *year <= 199)
     *year += 1900;
   if (*year >= 1582 || *year <= 19999)
-    return 1;
+    return true;
 
   dls_error (i, _("Year (%ld) must be between 1582 and 19999."), *year);
-  return 0;
+  return false;
 }
 
-static int
+static bool
 parse_trailer (struct data_in *i)
 {
   skip_whitespace (i);
   if (!have_char (i))
-    return 1;
+    return true;
   
   dls_error (i, _("Trailing garbage \"%s\" following date."), i->s);
-  return 0;
+  return false;
 }
 
-static int
+static bool
 parse_julian (struct data_in *i, long *julian)
 {
   if (!parse_int (i, julian))
-    return 0;
+    return false;
    
   {
     int day = *julian % 1000;
@@ -928,7 +863,7 @@ parse_julian (struct data_in *i, long *julian)
     if (day < 1 || day > 366)
       {
        dls_error (i, _("Julian day (%d) must be between 1 and 366."), day);
-       return 0;
+       return false;
       }
   }
   
@@ -940,52 +875,52 @@ parse_julian (struct data_in *i, long *julian)
     else if (year < 1582 || year > 19999)
       {
        dls_error (i, _("Year (%d) must be between 1582 and 19999."), year);
-       return 0;
+       return false;
       }
   }
 
-  return 1;
+  return true;
 }
 
-static int
+static bool
 parse_quarter (struct data_in *i, long *quarter)
 {
   if (!parse_int (i, quarter))
-    return 0;
+    return false;
   if (*quarter >= 1 && *quarter <= 4)
-    return 1;
+    return true;
 
   dls_error (i, _("Quarter (%ld) must be between 1 and 4."), *quarter);
-  return 0;
+  return false;
 }
 
-static int
+static bool
 parse_q_delimiter (struct data_in *i)
 {
   skip_whitespace (i);
   if (!have_char (i) || tolower (*i->s) != 'q')
     {
       dls_error (i, _("`Q' expected between quarter and year."));
-      return 0;
+      return false;
     }
   i->s++;
   skip_whitespace (i);
-  return 1;
+  return true;
 }
 
-static int
+static bool
 parse_week (struct data_in *i, long *week)
 {
   if (!parse_int (i, week))
-    return 0;
+    return false;
   if (*week >= 1 && *week <= 53)
-    return 1;
+    return true;
 
   dls_error (i, _("Week (%ld) must be between 1 and 53."), *week);
-  return 0;
+  return false;
 }
 
-static int
+static bool
 parse_wk_delimiter (struct data_in *i)
 {
   skip_whitespace (i);
@@ -993,60 +928,59 @@ parse_wk_delimiter (struct data_in *i)
       || tolower (i->s[0]) != 'w' || tolower (i->s[1]) != 'k')
     {
       dls_error (i, _("`WK' expected between week and year."));
-      return 0;
+      return false;
     }
   i->s += 2;
   skip_whitespace (i);
-  return 1;
+  return true;
 }
 
-static int
+static bool
 parse_time_delimiter (struct data_in *i)
 {
-  int delim = 0;
+  bool delim = false;
 
-  while (have_char (i)
-        && (*i->s == ':' || *i->s == '.' || isspace (*i->s)))
+  while (have_char (i) && (*i->s == ':' || *i->s == '.' || isspace (*i->s)))
     {
-      delim = 1;
+      delim = true;
       i->s++;
     }
 
   if (delim)
-    return 1;
+    return true;
   
   dls_error (i, _("Delimiter expected between fields in time."));
-  return 0;
+  return false;
 }
 
-static int
+static bool
 parse_hour (struct data_in *i, long *hour)
 {
   if (!parse_int (i, hour))
-    return 0;
+    return false;
   if (*hour >= 0)
-    return 1;
+    return true;
   
   dls_error (i, _("Hour (%ld) must be positive."), *hour);
-  return 0;
+  return false;
 }
 
-static int
+static bool
 parse_minute (struct data_in *i, long *minute)
 {
   if (!parse_int (i, minute))
-    return 0;
+    return false;
   if (*minute >= 0 && *minute <= 59)
-    return 1;
+    return true;
   
   dls_error (i, _("Minute (%ld) must be between 0 and 59."), *minute);
-  return 0;
+  return false;
 }
 
-static int
+static bool
 parse_opt_second (struct data_in *i, double *second)
 {
-  int delim = 0;
+  bool delim = false;
 
   char buf[64];
   char *cp;
@@ -1054,14 +988,14 @@ parse_opt_second (struct data_in *i, double *second)
   while (have_char (i)
         && (*i->s == ':' || *i->s == '.' || isspace (*i->s)))
     {
-      delim = 1;
+      delim = true;
       i->s++;
     }
   
   if (!delim || !isdigit (*i->s))
     {
       *second = 0.0;
-      return 1;
+      return true;
     }
 
   cp = buf;
@@ -1075,96 +1009,66 @@ parse_opt_second (struct data_in *i, double *second)
   
   *second = strtod (buf, NULL);
 
-  return 1;
+  return true;
 }
 
-static int
+static bool
 parse_hour24 (struct data_in *i, long *hour24)
 {
   if (!parse_int (i, hour24))
-    return 0;
+    return false;
   if (*hour24 >= 0 && *hour24 <= 23)
-    return 1;
+    return true;
   
   dls_error (i, _("Hour (%ld) must be between 0 and 23."), *hour24);
-  return 0;
+  return false;
 }
 
      
-static int
-parse_weekday (struct data_in *i, int *weekday)
+static bool
+parse_weekday (struct data_in *i, long *weekday)
 {
-  /* PORTME */
-  #define TUPLE(A,B)                           \
-         (((A) << 8) + (B))
-
-  if (i->s + 1 >= i->e)
-    {
-      dls_error (i, _("Day of the week expected in date value."));
-      return 0;
-    }
-
-  switch (TUPLE (tolower (i->s[0]), tolower (i->s[1])))
+  static const struct enum_name weekday_names[] = 
     {
-    case TUPLE ('s', 'u'):
-      *weekday = 1;
-      break;
-
-    case TUPLE ('m', 'o'):
-      *weekday = 2;
-      break;
-
-    case TUPLE ('t', 'u'):
-      *weekday = 3;
-      break;
-
-    case TUPLE ('w', 'e'):
-      *weekday = 4;
-      break;
-
-    case TUPLE ('t', 'h'):
-      *weekday = 5;
-      break;
-
-    case TUPLE ('f', 'r'):
-      *weekday = 6;
-      break;
-
-    case TUPLE ('s', 'a'):
-      *weekday = 7;
-      break;
-
-    default:
-      dls_error (i, _("Day of the week expected in date value."));
-      return 0;
-    }
-
-  while (have_char (i) && isalpha (*i->s))
-    i->s++;
-
-  return 1;
+      {"sunday", true, 1},
+      {"su", true, 1},
+      {"monday", true, 2},
+      {"mo", true, 2},
+      {"tuesday", true, 3},
+      {"tu", true, 3},
+      {"wednesday", true, 4},
+      {"we", true, 4},
+      {"thursday", true, 5},
+      {"th", true, 5},
+      {"friday", true, 6},
+      {"fr", true, 6},
+      {"saturday", true, 7},
+      {"sa", true, 7},
+      
+      {NULL, false, 0},
+    };
 
-  #undef TUPLE
+  return parse_enum (i, _("weekday"), weekday_names, weekday);
 }
 
-static int
+static bool
 parse_spaces (struct data_in *i)
 {
   skip_whitespace (i);
-  return 1;
+  return true;
 }
 
-static int
+static bool
 parse_sign (struct data_in *i, int *sign)
 {
   if (!force_have_char (i))
-    return 0;
+    return false;
 
   switch (*i->s)
     {
     case '-':
       i->s++;
-      *sign = 1;
+      *sign = -1;
       break;
 
     case '+':
@@ -1172,202 +1076,177 @@ parse_sign (struct data_in *i, int *sign)
       /* fall through */
 
     default:
-      *sign = 0;
+      *sign = 1;
       break;
     }
 
-  return 1;
+  return true;
 }
 \f
 /* Date & time formats. */
 
-static int
-valid_date (struct data_in *i)
+static void
+calendar_error (void *i_, const char *format, ...) 
 {
-  if (i->v->f == SYSMIS)
+  struct data_in *i = i_;
+  va_list args;
+
+  va_start (args, format);
+  vdls_error (i, format, args);
+  va_end (args);
+}
+
+static bool
+ymd_to_ofs (struct data_in *i, int year, int month, int day, double *ofs) 
+{
+  *ofs = calendar_gregorian_to_offset (year, month, day, calendar_error, i);
+  return *ofs != SYSMIS;
+}
+
+static bool
+ymd_to_date (struct data_in *i, int year, int month, int day, double *date) 
+{
+  if (ymd_to_ofs (i, year, month, day, date)) 
     {
-      dls_error (i, _("Date is not in valid range between "
-                  "15 Oct 1582 and 31 Dec 19999."));
-      return 0;
+      *date *= 60. * 60. * 24.;
+      return true; 
     }
   else
-    return 1;
+    return false;
 }
 
-static int
+static bool
 parse_DATE (struct data_in *i)
 {
   long day, month, year;
 
-  if (!parse_leader (i)
-      || !parse_day (i, &day)
-      || !parse_date_delimiter (i)
-      || !parse_month (i, &month)
-      || !parse_date_delimiter (i)
-      || !parse_year (i, &year)
-      || !parse_trailer (i))
-    return 0;
-
-  i->v->f = calendar_to_julian (year, month, day);
-  if (!valid_date (i))
-    return 0;
-  i->v->f *= 60. * 60. * 24.;
-
-  return 1;
+  return (parse_leader (i)
+          && parse_day (i, &day)
+          && parse_date_delimiter (i)
+          && parse_month (i, &month)
+          && parse_date_delimiter (i)
+          && parse_year (i, &year)
+          && parse_trailer (i)
+          && ymd_to_date (i, year, month, day, &i->v->f));
 }
 
-static int
+static bool
 parse_ADATE (struct data_in *i)
 {
   long month, day, year;
 
-  if (!parse_leader (i)
-      || !parse_month (i, &month)
-      || !parse_date_delimiter (i)
-      || !parse_day (i, &day)
-      || !parse_date_delimiter (i)
-      || !parse_year (i, &year)
-      || !parse_trailer (i))
-    return 0;
-
-  i->v->f = calendar_to_julian (year, month, day);
-  if (!valid_date (i))
-    return 0;
-  i->v->f *= 60. * 60. * 24.;
-
-  return 1;
+  return (parse_leader (i)
+          && parse_month (i, &month)
+          && parse_date_delimiter (i)
+          && parse_day (i, &day)
+          && parse_date_delimiter (i)
+          && parse_year (i, &year)
+          && parse_trailer (i)
+          && ymd_to_date (i, year, month, day, &i->v->f));
 }
 
-static int
+static bool
 parse_EDATE (struct data_in *i)
 {
   long month, day, year;
 
-  if (!parse_leader (i)
-      || !parse_day (i, &day)
-      || !parse_date_delimiter (i)
-      || !parse_month (i, &month)
-      || !parse_date_delimiter (i)
-      || !parse_year (i, &year)
-      || !parse_trailer (i))
-    return 0;
-
-  i->v->f = calendar_to_julian (year, month, day);
-  if (!valid_date (i))
-    return 0;
-  i->v->f *= 60. * 60. * 24.;
-
-  return 1;
+  return (parse_leader (i)
+          && parse_day (i, &day)
+          && parse_date_delimiter (i)
+          && parse_month (i, &month)
+          && parse_date_delimiter (i)
+          && parse_year (i, &year)
+          && parse_trailer (i)
+          && ymd_to_date (i, year, month, day, &i->v->f));
 }
 
-static int
+static bool
 parse_SDATE (struct data_in *i)
 {
   long month, day, year;
 
-  if (!parse_leader (i)
-      || !parse_year (i, &year)
-      || !parse_date_delimiter (i)
-      || !parse_month (i, &month)
-      || !parse_date_delimiter (i)
-      || !parse_day (i, &day)
-      || !parse_trailer (i))
-    return 0;
-
-  i->v->f = calendar_to_julian (year, month, day);
-  if (!valid_date (i))
-    return 0;
-  i->v->f *= 60. * 60. * 24.;
-
-  return 1;
+  return (parse_leader (i)
+          && parse_year (i, &year)
+          && parse_date_delimiter (i)
+          && parse_month (i, &month)
+          && parse_date_delimiter (i)
+          && parse_day (i, &day)
+          && parse_trailer (i)
+          && ymd_to_date (i, year, month, day, &i->v->f));
 }
 
-static int
+static bool
 parse_JDATE (struct data_in *i)
 {
   long julian;
+  double ofs;
   
   if (!parse_leader (i)
       || !parse_julian (i, &julian)
-      || !parse_trailer (i))
-    return 0;
-
-  if (julian / 1000 == 1582)
-    i->v->f = calendar_to_julian (1583, 1, 1) - 365;
-  else
-    i->v->f = calendar_to_julian (julian / 1000, 1, 1);
+      || !parse_trailer (i)
+      || !ymd_to_ofs (i, julian / 1000, 1, 1, &ofs))
+    return false;
 
-  if (valid_date (i))
-    {
-      i->v->f = (i->v->f + julian % 1000 - 1) * 60. * 60. * 24.;
-      if (i->v->f < 0.)
-       i->v->f = SYSMIS;
-    }
-
-  return valid_date (i);
+  i->v->f = (ofs + julian % 1000 - 1) * 60. * 60. * 24.;
+  return true;
 }
 
-static int
+static bool
 parse_QYR (struct data_in *i)
 {
   long quarter, year;
 
-  if (!parse_leader (i)
-      || !parse_quarter (i, &quarter)
-      || !parse_q_delimiter (i)
-      || !parse_year (i, &year)
-      || !parse_trailer (i))
-    return 0;
-
-  i->v->f = calendar_to_julian (year, (quarter - 1) * 3 + 1, 1);
-  if (!valid_date (i))
-    return 0;
-  i->v->f *= 60. * 60. * 24.;
-
-  return 1;
+  return (parse_leader (i)
+          && parse_quarter (i, &quarter)
+          && parse_q_delimiter (i)
+          && parse_year (i, &year)
+          && parse_trailer (i)
+          && ymd_to_date (i, year, (quarter - 1) * 3 + 1, 1, &i->v->f));
 }
 
-static int
+static bool
 parse_MOYR (struct data_in *i)
 {
   long month, year;
 
-  if (!parse_leader (i)
-      || !parse_month (i, &month)
-      || !parse_date_delimiter (i)
-      || !parse_year (i, &year)
-      || !parse_trailer (i))
-    return 0;
-
-  i->v->f = calendar_to_julian (year, month, 1);
-  if (!valid_date (i))
-    return 0;
-  i->v->f *= 60. * 60. * 24.;
-
-  return 1;
+  return (parse_leader (i)
+          && parse_month (i, &month)
+          && parse_date_delimiter (i)
+          && parse_year (i, &year)
+          && parse_trailer (i)
+          && ymd_to_date (i, year, month, 1, &i->v->f));
 }
 
-static int
+static bool
 parse_WKYR (struct data_in *i)
 {
   long week, year;
+  double ofs;
 
   if (!parse_leader (i)
       || !parse_week (i, &week)
       || !parse_wk_delimiter (i)
       || !parse_year (i, &year)
       || !parse_trailer (i))
-    return 0;
+    return false;
 
-  i->v->f = calendar_to_julian (year, 1, 1);
-  if (!valid_date (i))
-    return 0;
-  i->v->f = (i->v->f + (week - 1) * 7) * 60. * 60. * 24.;
+  if (year != 1582) 
+    {
+      if (!ymd_to_ofs (i, year, 1, 1, &ofs))
+        return false;
+    }
+  else 
+    {
+      if (ymd_to_ofs (i, 1583, 1, 1, &ofs))
+        return false;
+      ofs -= 365;
+    }
 
-  return 1;
+  i->v->f = (ofs + (week - 1) * 7) * 60. * 60. * 24.;
+  return true;
 }
 
-static int
+static bool
 parse_TIME (struct data_in *i)
 {
   int sign;
@@ -1381,15 +1260,13 @@ parse_TIME (struct data_in *i)
       || !parse_time_delimiter (i)
       || !parse_minute (i, &minute)
       || !parse_opt_second (i, &second))
-    return 0;
+    return false;
 
-  i->v->f = hour * 60. * 60. + minute * 60. + second;
-  if (sign)
-    i->v->f = -i->v->f;
-  return 1;
+  i->v->f = (hour * 60. * 60. + minute * 60. + second) * sign;
+  return true;
 }
 
-static int
+static bool
 parse_DTIME (struct data_in *i)
 {
   int sign;
@@ -1406,18 +1283,16 @@ parse_DTIME (struct data_in *i)
       || !parse_time_delimiter (i)
       || !parse_minute (i, &minute)
       || !parse_opt_second (i, &second))
-    return 0;
+    return false;
 
   i->v->f = (day_count * 60. * 60. * 24.
             + hour * 60. * 60.
             + minute * 60.
-            + second);
-  if (sign)
-    i->v->f = -i->v->f;
-  return 1;
+            + second) * sign;
+  return true;
 }
 
-static int
+static bool
 parse_DATETIME (struct data_in *i)
 {
   long day, month, year;
@@ -1435,35 +1310,29 @@ parse_DATETIME (struct data_in *i)
       || !parse_hour24 (i, &hour24)
       || !parse_time_delimiter (i)
       || !parse_minute (i, &minute)
-      || !parse_opt_second (i, &second))
-    return 0;
+      || !parse_opt_second (i, &second)
+      || !ymd_to_date (i, year, month, day, &i->v->f))
+    return false;
 
-  i->v->f = calendar_to_julian (year, month, day);
-  if (!valid_date (i))
-    return 0;
-  i->v->f = (i->v->f * 60. * 60. * 24.
-            + hour24 * 60. * 60.
-            + minute * 60.
-            + second);
-
-  return 1;
+  i->v->f += hour24 * 60. * 60. + minute * 60. + second;
+  return true;
 }
 
-static int
+static bool
 parse_WKDAY (struct data_in *i)
 {
-  int weekday;
+  long weekday;
 
   if (!parse_leader (i)
       || !parse_weekday (i, &weekday)
       || !parse_trailer (i))
-    return 0;
+    return false;
 
   i->v->f = weekday;
-  return 1;
+  return true;
 }
 
-static int
+static bool
 parse_MONTH (struct data_in *i)
 {
   long month;
@@ -1471,10 +1340,10 @@ parse_MONTH (struct data_in *i)
   if (!parse_leader (i)
       || !parse_month (i, &month)
       || !parse_trailer (i))
-    return 0;
+    return false;
 
   i->v->f = month;
-  return 1;
+  return true;
 }
 \f
 /* Main dispatcher. */
@@ -1488,19 +1357,21 @@ default_result (struct data_in *i)
   if (fmt->cat & FCAT_STRING)
     memset (i->v->s, ' ', i->format.w);
   else
-    i->v->f = set_blanks;
+    i->v->f = get_blanks();
 }
 
-int
+bool
 data_in (struct data_in *i)
 {
   const struct fmt_desc *const fmt = &formats[i->format.type];
 
+  assert (check_input_specifier (&i->format, 0));
+
   /* Check that we've got a string to work with. */
   if (i->e == i->s || i->format.w <= 0)
     {
       default_result (i);
-      return 1;
+      return true;
     }
 
   i->f2 = i->f1 + (i->e - i->s) - 1;
@@ -1526,14 +1397,14 @@ data_in (struct data_in *i)
 
          if (++cp == i->e)
            {
-             i->v->f = set_blanks;
-             return 1;
+             i->v->f = get_blanks();
+             return true;
            }
        }
     }
   
   {
-    static int (*const handlers[FMT_NUMBER_OF_FORMATS])(struct data_in *) = 
+    static bool (*const handlers[FMT_NUMBER_OF_FORMATS])(struct data_in *) = 
       {
        parse_numeric, parse_N, parse_numeric, parse_numeric,
        parse_numeric, parse_numeric, parse_numeric,
@@ -1546,8 +1417,8 @@ data_in (struct data_in *i)
        parse_WKDAY, parse_MONTH,
       };
 
-    int (*handler)(struct data_in *);
-    int success;
+    bool (*handler)(struct data_in *);
+    bool success;
 
     handler = handlers[i->format.type];
     assert (handler != NULL);