Change license from GPLv2+ to GPLv3+.
[pspp-builds.git] / src / data / por-file-reader.c
index 2fa02dedd3c8c5400ea1822e8f04484809ea76a1..f9eb28b620d91d58e88573793f80ba0474e90014 100644 (file)
@@ -1,48 +1,47 @@
-/* PSPP - computes sample statistics.
+/* PSPP - a program for statistical analysis.
    Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc.
-   Code for parsing floating-point numbers adapted from GNU C
-   library.
 
-   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 the Free Software Foundation; either version 2 of the
-   License, or (at your option) any later version.
+   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
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
 
-   This program is distributed in the hope that it will be useful, but
-   WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   General Public License for more details.
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
 
    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., 51 Franklin Street, Fifth Floor, Boston, MA
-   02110-1301, USA. */
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
 
 #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 "missing-values.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)
@@ -71,10 +70,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)
@@ -101,7 +102,7 @@ error (struct pfm_reader *r, const char *msg, ...)
   m.where.file_name = NULL;
   m.where.line_number = 0;
   m.text = ds_cstr (&text);
-  
+
   msg_emit (&m);
 
   r->ok = false;
@@ -110,11 +111,11 @@ error (struct pfm_reader *r, const char *msg, ...)
 }
 
 /* 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.  */
@@ -126,10 +127,10 @@ advance (struct pfm_reader *r)
   while ((c = getc (r->file)) == '\r' || c == '\n')
     continue;
   if (c == EOF)
-    error (r, _("unexpected end of file")); 
+    error (r, _("unexpected end of file"));
 
   if (r->trans != NULL)
-    c = r->trans[c]; 
+    c = r->trans[c];
   r->cc = c;
 }
 
@@ -156,7 +157,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)
 {
@@ -190,7 +191,7 @@ pfm_open_reader (struct file_handle *fh, struct dictionary **dict,
            fh_get_file_name (r->fh), strerror (errno));
       goto error;
     }
-  
+
   /* Read header, version, date info, product id, variables. */
   read_header (r);
   read_version_data (r, info);
@@ -204,10 +205,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;
@@ -216,7 +219,7 @@ pfm_open_reader (struct file_handle *fh, struct dictionary **dict,
 /* Returns the value of base-30 digit C,
    or -1 if C is not a base-30 digit. */
 static int
-base_30_value (unsigned char c) 
+base_30_value (unsigned char c)
 {
   static const char base_30_digits[] = "0123456789ABCDEFGHIJKLMNOPQRST";
   const char *p = strchr (base_30_digits, c);
@@ -282,7 +285,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 == '-')
@@ -326,7 +329,7 @@ read_float (struct pfm_reader *r)
 
   return negative ? -num : num;
 }
-  
+
 /* Read an integer and return its value. */
 static int
 read_int (struct pfm_reader *r)
@@ -345,7 +348,7 @@ read_string (struct pfm_reader *r, char *buf)
   int n = read_int (r);
   if (n < 0 || n > 255)
     error (r, _("Bad string length %d."), n);
-  
+
   while (n-- > 0)
     {
       *buf++ = r->cc;
@@ -357,7 +360,7 @@ read_string (struct pfm_reader *r, char *buf)
 /* Reads a string and returns a copy of it allocated from R's
    pool. */
 static char *
-read_pool_string (struct pfm_reader *r) 
+read_pool_string (struct pfm_reader *r)
 {
   char string[256];
   read_string (r, string);
@@ -374,7 +377,7 @@ read_header (struct pfm_reader *r)
   /* Read and ignore vanity splash strings. */
   for (i = 0; i < 200; i++)
     advance (r);
-  
+
   /* Skip the first 64 characters of the translation table.
      We don't care about these.  They are probably all set to
      '0', marking them as untranslatable, and that would screw
@@ -385,7 +388,7 @@ read_header (struct pfm_reader *r)
   /* Read the rest of the translation table. */
   trans = pool_malloc (r->pool, 256);
   memset (trans, 0, 256);
-  for (; i < 256; i++) 
+  for (; i < 256; i++)
     {
       unsigned char c;
 
@@ -399,11 +402,11 @@ read_header (struct pfm_reader *r)
   /* Set up the translation table, then read the first
      translated character. */
   r->trans = trans;
-  advance (r); 
+  advance (r);
 
   /* Skip and verify signature. */
-  for (i = 0; i < 8; i++) 
-    if (!match (r, "SPSSPORT"[i])) 
+  for (i = 0; i < 8; i++)
+    if (!match (r, "SPSSPORT"[i]))
       {
         msg (SE, _("%s: Not a portable file."), fh_get_file_name (r->fh));
         longjmp (r->bail_out, 1);
@@ -421,7 +424,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,18 +433,18 @@ 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) 
+  if (info != NULL)
     {
       /* Date. */
-      for (i = 0; i < 8; i++) 
+      for (i = 0; i < 8; i++)
         {
           static const int map[] = {6, 7, 8, 9, 3, 4, 0, 1};
-          info->creation_date[map[i]] = date[i]; 
+          info->creation_date[map[i]] = date[i];
         }
       info->creation_date[2] = info->creation_date[5] = ' ';
       info->creation_date[10] = 0;
@@ -502,10 +505,10 @@ read_variables (struct pfm_reader *r, struct dictionary *dict)
 {
   char *weight_name = NULL;
   int i;
-  
+
   if (!match (r, '4'))
     error (r, _("Expected variable count record."));
-  
+
   r->var_cnt = read_int (r);
   if (r->var_cnt <= 0 || r->var_cnt == NOT_INT)
     error (r, _("Invalid number of variables %d."), r->var_cnt);
@@ -517,10 +520,10 @@ read_variables (struct pfm_reader *r, struct dictionary *dict)
   if (match (r, '6'))
     {
       weight_name = read_pool_string (r);
-      if (strlen (weight_name) > SHORT_NAME_LEN) 
+      if (strlen (weight_name) > SHORT_NAME_LEN)
         error (r, _("Weight variable name (%s) truncated."), weight_name);
     }
-  
+
   for (i = 0; i < r->var_cnt; i++)
     {
       int width;
@@ -548,7 +551,7 @@ 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)
@@ -561,7 +564,7 @@ read_variables (struct pfm_reader *r, struct dictionary *dict)
 
       /* Range missing values. */
       mv_init (&miss, var_get_width (v));
-      if (match (r, 'B')) 
+      if (match (r, 'B'))
         {
           double x = read_float (r);
           double y = read_float (r);
@@ -573,15 +576,15 @@ read_variables (struct pfm_reader *r, struct dictionary *dict)
         mv_add_num_range (&miss, LOWEST, read_float (r));
 
       /* Single missing values. */
-      while (match (r, '8')) 
+      while (match (r, '8'))
         {
           union value value = parse_value (r, v);
-          mv_add_value (&miss, &value); 
+          mv_add_value (&miss, &value);
         }
 
       var_set_missing_values (v, &miss);
 
-      if (match (r, 'C')) 
+      if (match (r, 'C'))
         {
           char label[256];
           read_string (r, label);
@@ -589,7 +592,7 @@ read_variables (struct pfm_reader *r, struct dictionary *dict)
         }
     }
 
-  if (weight_name != NULL) 
+  if (weight_name != NULL)
     {
       struct variable *weight_var = dict_lookup_var (dict, weight_name);
       if (weight_var == NULL)
@@ -605,12 +608,12 @@ static union value
 parse_value (struct pfm_reader *r, struct variable *vv)
 {
   union value v;
-  
-  if (var_is_alpha (vv)) 
+
+  if (var_is_alpha (vv))
     {
       char string[256];
       read_string (r, string);
-      buf_copy_str_rpad (v.s, 8, string); 
+      buf_copy_str_rpad (v.s, 8, string);
     }
   else
     v.f = read_float (r);
@@ -677,25 +680,34 @@ read_value_label (struct pfm_reader *r, struct dictionary *dict)
 }
 
 /* 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;
-  
+    {
+      casereader_force_error (reader);
+      case_destroy (c);
+      return false;
+    }
+
   /* Check for end of file. */
   if (r->cc == 'Z')
-    return false;
+    {
+      case_destroy (c);
+      return false;
+    }
 
   idx = 0;
-  for (i = 0; i < r->var_cnt; i++) 
+  for (i = 0; i < r->var_cnt; i++)
     {
       int width = r->widths[i];
-      
+
       if (width == 0)
         {
           case_data_rw_idx (c, idx)->f = read_float (r);
@@ -709,22 +721,14 @@ pfm_read_case (struct pfm_reader *r, struct ccase *c)
           idx += DIV_RND_UP (width, MAX_SHORT_STRING);
         }
     }
-  
-  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;
+  return true;
 }
 
 /* Returns true if FILE is an SPSS portable file,
    false otherwise. */
 bool
-pfm_detect (FILE *file) 
+pfm_detect (FILE *file)
 {
   unsigned char header[464];
   char trans[256];
@@ -737,21 +741,29 @@ pfm_detect (FILE *file)
       int c = getc (file);
       if (c == EOF || raw_cnt++ > 512)
         return false;
-      else if (c != '\n' && c != '\r') 
+      else if (c != '\n' && c != '\r')
         header[cooked_cnt++] = c;
     }
 
   memset (trans, 0, 256);
-  for (i = 64; i < 256; i++) 
+  for (i = 64; i < 256; i++)
     {
       unsigned char c = header[i + 200];
       if (trans[c] == 0)
         trans[c] = portable_to_local[i];
     }
 
-  for (i = 0; i < 8; i++) 
-    if (trans[header[i + 456]] != "SPSSPORT"[i]) 
-      return false; 
+  for (i = 0; i < 8; i++)
+    if (trans[header[i + 456]] != "SPSSPORT"[i])
+      return false;
 
   return true;
 }
+
+static struct casereader_class por_file_casereader_class =
+  {
+    por_file_casereader_read,
+    por_file_casereader_destroy,
+    NULL,
+    NULL,
+  };