Actually implement the new procedure code and adapt all of its clients
[pspp-builds.git] / src / data / por-file-reader.c
index c4fec4b0ed72d30a2608c9b6af49340d35d40384..2c99bde63d7b5625c093a1ace939a2c7efbe956d 100644 (file)
@@ -1,6 +1,5 @@
 /* PSPP - computes sample statistics.
    Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc.
-   Written by Ben Pfaff <blp@gnu.org>.
    Code for parsing floating-point numbers adapted from GNU C
    library.
 
 
 #include <config.h>
 #include "por-file-reader.h"
-#include <libpspp/message.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
+
 #include <ctype.h>
 #include <errno.h>
 #include <math.h>
 #include <setjmp.h>
-#include <libpspp/alloc.h>
+#include <stdarg.h>
 #include <stdbool.h>
-#include "case.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <data/casereader-provider.h>
+#include <data/casereader.h>
+#include <data/dictionary.h>
+#include <data/file-handle-def.h>
+#include <data/format.h>
+#include <data/missing-values.h>
+#include <data/value-labels.h>
+#include <data/variable.h>
+#include <libpspp/alloc.h>
 #include <libpspp/compiler.h>
-#include "dictionary.h"
-#include "file-handle-def.h"
-#include "format.h"
 #include <libpspp/hash.h>
 #include <libpspp/magic.h>
+#include <libpspp/message.h>
 #include <libpspp/misc.h>
 #include <libpspp/pool.h>
 #include <libpspp/str.h>
-#include "value-labels.h"
-#include "variable.h"
 
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
 
-#include <libpspp/debug-print.h>
-
 /* portable_to_local[PORTABLE] translates the given portable
    character into the local character set. */
 static const char portable_to_local[256] =
@@ -73,10 +74,12 @@ struct pfm_reader
     int var_cnt;                /* Number of variables. */
     int weight_index;          /* 0-based index of weight variable, or -1. */
     int *widths;                /* Variable widths, 0 for numeric. */
-    int value_cnt;             /* Number of `value's per case. */
+    size_t value_cnt;          /* Number of `value's per case. */
     bool ok;                    /* Set false on I/O error. */
   };
 
+static struct casereader_class por_file_casereader_class;
+
 static void
 error (struct pfm_reader *r, const char *msg,...)
      PRINTF_FORMAT (2, 3)
@@ -87,34 +90,36 @@ error (struct pfm_reader *r, const char *msg,...)
 static void
 error (struct pfm_reader *r, const char *msg, ...)
 {
-  struct error e;
-  const char *filename;
-  char *title;
+  struct msg m;
+  struct string text;
   va_list args;
 
-  e.class = ME;
-  e.where.filename = NULL;
-  e.where.line_number = 0;
-  filename = fh_get_filename (r->fh);
-  e.title = title = pool_alloc (r->pool, strlen (filename) + 80);
-  sprintf (title, _("portable file %s corrupt at offset %ld: "),
-           filename, ftell (r->file));
-
+  ds_init_empty (&text);
+  ds_put_format (&text, _("portable file %s corrupt at offset %ld: "),
+                 fh_get_file_name (r->fh), ftell (r->file));
   va_start (args, msg);
-  err_vmsg (&e, msg, args);
+  ds_put_vformat (&text, msg, args);
   va_end (args);
 
+  m.category = MSG_GENERAL;
+  m.severity = MSG_ERROR;
+  m.where.file_name = NULL;
+  m.where.line_number = 0;
+  m.text = ds_cstr (&text);
+  
+  msg_emit (&m);
+
   r->ok = false;
 
   longjmp (r->bail_out, 1);
 }
 
 /* Closes portable file reader R, after we're done with it. */
-void
-pfm_close_reader (struct pfm_reader *r)
+static void
+por_file_casereader_destroy (struct casereader *reader UNUSED, void *r_)
 {
-  if (r != NULL)
-    pool_destroy (r->pool);
+  struct pfm_reader *r = r_;
+  pool_destroy (r->pool);
 }
 
 /* Read a single character into cur_char.  */
@@ -156,7 +161,7 @@ void dump_dictionary (struct dictionary *);
 /* Reads the dictionary from file with handle H, and returns it in a
    dictionary structure.  This dictionary may be modified in order to
    rename, reorder, and delete variables, etc. */
-struct pfm_reader *
+struct casereader *
 pfm_open_reader (struct file_handle *fh, struct dictionary **dict,
                  struct pfm_read_info *info)
 {
@@ -174,7 +179,7 @@ pfm_open_reader (struct file_handle *fh, struct dictionary **dict,
   if (setjmp (r->bail_out))
     goto error;
   r->fh = fh;
-  r->file = pool_fopen (r->pool, fh_get_filename (r->fh), "rb");
+  r->file = pool_fopen (r->pool, fh_get_file_name (r->fh), "rb");
   r->weight_index = -1;
   r->trans = NULL;
   r->var_cnt = 0;
@@ -187,7 +192,7 @@ pfm_open_reader (struct file_handle *fh, struct dictionary **dict,
     {
       msg (ME, _("An error occurred while opening \"%s\" for reading "
                  "as a portable file: %s."),
-           fh_get_filename (r->fh), strerror (errno));
+           fh_get_file_name (r->fh), strerror (errno));
       goto error;
     }
   
@@ -204,10 +209,12 @@ pfm_open_reader (struct file_handle *fh, struct dictionary **dict,
   if (!match (r, 'F'))
     error (r, _("Data record expected."));
 
-  return r;
+  r->value_cnt = dict_get_next_value_idx (*dict);
+  return casereader_create_sequential (NULL, r->value_cnt, CASENUMBER_MAX,
+                                       &por_file_casereader_class, r);
 
  error:
-  pfm_close_reader (r);
+  pool_destroy (r->pool);
   dict_destroy (*dict);
   *dict = NULL;
   return NULL;
@@ -282,7 +289,7 @@ read_float (struct pfm_reader *r)
 
   /* Check that we had some digits. */
   if (!got_digit)
-    error (r, "Number expected.");
+    error (r, _("Number expected."));
 
   /* Get exponent if any. */
   if (r->cc == '+' || r->cc == '-')
@@ -405,7 +412,7 @@ read_header (struct pfm_reader *r)
   for (i = 0; i < 8; i++) 
     if (!match (r, "SPSSPORT"[i])) 
       {
-        msg (SE, _("%s: Not a portable file."), fh_get_filename (r->fh));
+        msg (SE, _("%s: Not a portable file."), fh_get_file_name (r->fh));
         longjmp (r->bail_out, 1);
       }
 }
@@ -421,7 +428,7 @@ read_version_data (struct pfm_reader *r, struct pfm_read_info *info)
 
   /* Read file. */
   if (!match (r, 'A'))
-    error (r, "Unrecognized version code `%c'.", r->cc);
+    error (r, _("Unrecognized version code `%c'."), r->cc);
   date = read_pool_string (r);
   time = read_pool_string (r);
   product = match (r, '1') ? read_pool_string (r) : empty_string;
@@ -430,9 +437,9 @@ read_version_data (struct pfm_reader *r, struct pfm_read_info *info)
 
   /* Validate file. */
   if (strlen (date) != 8)
-    error (r, _("Bad date string length %d."), strlen (date));
+    error (r, _("Bad date string length %d."), (int) strlen (date));
   if (strlen (time) != 6)
-    error (r, _("Bad time string length %d."), strlen (time));
+    error (r, _("Bad time string length %d."), (int) strlen (time));
 
   /* Save file info. */
   if (info != NULL) 
@@ -464,22 +471,34 @@ read_version_data (struct pfm_reader *r, struct pfm_read_info *info)
 /* Translates a format specification read from portable file R as
    the three integers INTS into a normal format specifier FORMAT,
    checking that the format is appropriate for variable V. */
-static void
+static struct fmt_spec
 convert_format (struct pfm_reader *r, const int portable_format[3],
-                struct fmt_spec *format, struct variable *v)
+                struct variable *v)
 {
-  format->type = translate_fmt (portable_format[0]);
-  if (format->type == -1)
+  struct fmt_spec format;
+  bool ok;
+
+  if (!fmt_from_io (portable_format[0], &format.type))
     error (r, _("%s: Bad format specifier byte (%d)."),
-           v->name, portable_format[0]);
-  format->w = portable_format[1];
-  format->d = portable_format[2];
-
-  if (!check_output_specifier (format, false)
-      || !check_specifier_width (format, v->width, false))
-    error (r, _("%s variable %s has invalid format specifier %s."),
-           v->type == NUMERIC ? _("Numeric") : _("String"),
-           v->name, fmt_to_string (format));
+           var_get_name (v), portable_format[0]);
+  format.w = portable_format[1];
+  format.d = portable_format[2];
+
+  msg_disable ();
+  ok = (fmt_check_output (&format)
+        && fmt_check_width_compat (&format, var_get_width (v)));
+  msg_enable ();
+
+  if (!ok)
+    {
+      char fmt_string[FMT_STRING_LEN_MAX + 1];
+      error (r, _("%s variable %s has invalid format specifier %s."),
+             var_is_numeric (v) ? _("Numeric") : _("String"),
+             var_get_name (v), fmt_to_string (&format, fmt_string));
+      format = fmt_default_for_width (var_get_width (v));
+    }
+
+  return format;
 }
 
 static union value parse_value (struct pfm_reader *, struct variable *);
@@ -515,6 +534,8 @@ read_variables (struct pfm_reader *r, struct dictionary *dict)
       char name[256];
       int fmt[6];
       struct variable *v;
+      struct missing_values miss;
+      struct fmt_spec print, write;
       int j;
 
       if (!match (r, '7'))
@@ -534,39 +555,44 @@ read_variables (struct pfm_reader *r, struct dictionary *dict)
       str_uppercase (name);
 
       if (width < 0 || width > 255)
-       error (r, "Bad width %d for variable %s.", width, name);
+       error (r, _("Bad width %d for variable %s."), width, name);
 
       v = dict_create_var (dict, name, width);
       if (v == NULL)
        error (r, _("Duplicate variable name %s."), name);
 
-      convert_format (r, &fmt[0], &v->print, v);
-      convert_format (r, &fmt[3], &v->write, v);
+      print = convert_format (r, &fmt[0], v);
+      write = convert_format (r, &fmt[3], v);
+      var_set_print_format (v, &print);
+      var_set_write_format (v, &write);
 
       /* Range missing values. */
+      mv_init (&miss, var_get_width (v));
       if (match (r, 'B')) 
         {
           double x = read_float (r);
           double y = read_float (r);
-          mv_add_num_range (&v->miss, x, y);
+          mv_add_num_range (&miss, x, y);
         }
       else if (match (r, 'A'))
-        mv_add_num_range (&v->miss, read_float (r), HIGHEST);
+        mv_add_num_range (&miss, read_float (r), HIGHEST);
       else if (match (r, '9'))
-        mv_add_num_range (&v->miss, LOWEST, read_float (r));
+        mv_add_num_range (&miss, LOWEST, read_float (r));
 
       /* Single missing values. */
       while (match (r, '8')) 
         {
           union value value = parse_value (r, v);
-          mv_add_value (&v->miss, &value); 
+          mv_add_value (&miss, &value); 
         }
 
+      var_set_missing_values (v, &miss);
+
       if (match (r, 'C')) 
         {
           char label[256];
           read_string (r, label);
-          v->label = xstrdup (label); 
+          var_set_label (v, label);
         }
     }
 
@@ -587,7 +613,7 @@ parse_value (struct pfm_reader *r, struct variable *vv)
 {
   union value v;
   
-  if (vv->type == ALPHA
+  if (var_is_alpha (vv)
     {
       char string[256];
       read_string (r, string);
@@ -623,10 +649,10 @@ read_value_label (struct pfm_reader *r, struct dictionary *dict)
       if (v[i] == NULL)
        error (r, _("Unknown variable %s while parsing value labels."), name);
 
-      if (v[0]->width != v[i]->width)
+      if (var_get_width (v[0]) != var_get_width (v[i]))
        error (r, _("Cannot assign value labels to %s and %s, which "
                    "have different variable types or widths."),
-              v[0]->name, v[i]->name);
+              var_get_name (v[0]), var_get_name (v[i]));
     }
 
   n_labels = read_int (r);
@@ -644,33 +670,42 @@ read_value_label (struct pfm_reader *r, struct dictionary *dict)
        {
          struct variable *var = v[j];
 
-         if (!val_labs_replace (var->val_labs, val, label))
+         if (!var_add_value_label (var, &val, label))
            continue;
 
-         if (var->type == NUMERIC)
+         if (var_is_numeric (var))
            error (r, _("Duplicate label for value %g for variable %s."),
-                  val.f, var->name);
+                  val.f, var_get_name (var));
          else
            error (r, _("Duplicate label for value `%.*s' for variable %s."),
-                  var->width, val.s, var->name);
+                  var_get_width (var), val.s, var_get_name (var));
        }
     }
 }
 
 /* Reads one case from portable file R into C. */
-bool
-pfm_read_case (struct pfm_reader *r, struct ccase *c)
+static bool
+por_file_casereader_read (struct casereader *reader, void *r_, struct ccase *c)
 {
+  struct pfm_reader *r = r_;
   size_t i;
   size_t idx;
 
+  case_create (c, casereader_get_value_cnt (reader));
   setjmp (r->bail_out);
-  if (!r->ok)
-    return false;
+  if (!r->ok) 
+    {
+      casereader_force_error (reader);
+      case_destroy (c);
+      return false; 
+    }
   
   /* Check for end of file. */
-  if (r->cc == 'Z')
-    return false;
+  if (r->cc == 'Z') 
+    {
+      case_destroy (c);
+      return false; 
+    }
 
   idx = 0;
   for (i = 0; i < r->var_cnt; i++) 
@@ -679,14 +714,14 @@ pfm_read_case (struct pfm_reader *r, struct ccase *c)
       
       if (width == 0)
         {
-          case_data_rw (c, idx)->f = read_float (r);
+          case_data_rw_idx (c, idx)->f = read_float (r);
           idx++;
         }
       else
         {
           char string[256];
           read_string (r, string);
-          buf_copy_str_rpad (case_data_rw (c, idx)->s, width, string);
+          buf_copy_str_rpad (case_data_rw_idx (c, idx)->s, width, string);
           idx += DIV_RND_UP (width, MAX_SHORT_STRING);
         }
     }
@@ -694,14 +729,6 @@ pfm_read_case (struct pfm_reader *r, struct ccase *c)
   return true;
 }
 
-/* Returns true if an I/O error has occurred on READER, false
-   otherwise. */
-bool
-pfm_read_error (const struct pfm_reader *reader) 
-{
-  return !reader->ok;
-}
-
 /* Returns true if FILE is an SPSS portable file,
    false otherwise. */
 bool
@@ -736,3 +763,11 @@ pfm_detect (FILE *file)
 
   return true;
 }
+
+static struct casereader_class por_file_casereader_class = 
+  {
+    por_file_casereader_read,
+    por_file_casereader_destroy,
+    NULL,
+    NULL,
+  };