Adopt use of gnulib for portability.
[pspp-builds.git] / src / sfm-read.c
index c036739b596bccc469afe24b3385254d2618c24c..0c07150eac3097b2c15bc3f9c98c922d4bee6443 100644 (file)
@@ -33,7 +33,7 @@
 #include "file-handle.h"
 #include "filename.h"
 #include "format.h"
-#include "getline.h"
+#include "getl.h"
 #include "hash.h"
 #include "magic.h"
 #include "misc.h"
@@ -41,6 +41,9 @@
 #include "str.h"
 #include "var.h"
 
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
 #include "debug-print.h"
 
 /* System file reader. */
@@ -59,7 +62,6 @@ struct sfm_reader
 
     /* Variables. */
     struct sfm_var *vars;       /* Variables. */
-    size_t var_cnt;             /* Number of variables. */
 
     /* File's special constants. */
     flt64 sysmis;
@@ -94,7 +96,7 @@ bswap (unsigned char *a, unsigned char *b)
   *b = t;
 }
 
-/* bswap_int32(): Reverse the byte order of 32-bit integer *X. */
+/* Reverse the byte order of 32-bit integer *X. */
 static inline void
 bswap_int32 (int32 *x_)
 {
@@ -157,6 +159,8 @@ sfm_close_reader (struct sfm_reader *r)
 \f
 /* Dictionary reader. */
 
+static void buf_unread(struct sfm_reader *r, size_t byte_cnt);
+
 static void *buf_read (struct sfm_reader *, void *buf, size_t byte_cnt,
                        size_t min_alloc);
 
@@ -219,7 +223,6 @@ sfm_open_reader (struct file_handle *fh, struct dictionary **dict,
   r->weight_idx = -1;
 
   r->vars = NULL;
-  r->var_cnt = 0;
 
   r->sysmis = -FLT64_MAX;
   r->highest = FLT64_MAX;
@@ -242,10 +245,19 @@ sfm_open_reader (struct file_handle *fh, struct dictionary **dict,
   if (!read_header (r, *dict, info) || !read_variables (r, *dict, &var_by_idx))
     goto error;
 
+
   /* Handle weighting. */
   if (r->weight_idx != -1)
     {
-      struct variable *weight_var = var_by_idx[r->weight_idx];
+      struct variable *weight_var;
+
+      if (r->weight_idx < 0 || r->weight_idx >= r->value_cnt)
+       lose ((ME, _("%s: Index of weighting variable (%d) is not between 0 "
+                    "and number of elements per case (%d)."),
+              handle_get_filename (r->fh), r->weight_idx, r->value_cnt));
+
+
+      weight_var = var_by_idx[r->weight_idx];
 
       if (weight_var == NULL)
        lose ((ME,
@@ -333,7 +345,7 @@ sfm_open_reader (struct file_handle *fh, struct dictionary **dict,
                {
                  const int  n_vars = data.count / 3 ;
                  int i;
-                 if ( data.count % 3 ) 
+                 if ( data.count % 3 || n_vars > dict_get_var_cnt(*dict) 
                    {
                      msg (MW, _("%s: Invalid subrecord length. "
                                 "Record: 7; Subrecord: 11"), 
@@ -341,7 +353,7 @@ sfm_open_reader (struct file_handle *fh, struct dictionary **dict,
                      skip = 1;
                    }
 
-                 for ( i = 0 ; i < n_vars ; ++i ) 
+                 for ( i = 0 ; i < min(n_vars, dict_get_var_cnt(*dict)) ; ++i ) 
                    {
                      struct
                      {
@@ -412,6 +424,16 @@ sfm_open_reader (struct file_handle *fh, struct dictionary **dict,
                           break;
                         }
 
+                      /* Identify any duplicates. */
+                     if ( compare_var_names(short_name, long_name, 0) &&
+                         NULL != dict_lookup_var (*dict, long_name))
+                        {
+                         lose ((ME, _("%s: Duplicate long variable name `%s' "
+                                      "within system file."),
+                                handle_get_filename (r->fh), long_name));
+                          break;
+                        }
+
                       /* Set long name.
                          Renaming a variable may clear the short
                          name, but we want to retain it, so
@@ -459,8 +481,8 @@ sfm_open_reader (struct file_handle *fh, struct dictionary **dict,
          }
 
        default:
-         lose ((ME, _("%s: Unrecognized record type %d."),
-                 handle_get_filename (r->fh), rec_type));
+         corrupt_msg(MW, _("%s: Unrecognized record type %d."),
+                 handle_get_filename (r->fh), rec_type);
        }
     }
 
@@ -643,22 +665,18 @@ read_header (struct sfm_reader *r,
       bswap_flt64 (&hdr.bias);
     }
 
+
   /* Copy basic info and verify correctness. */
   r->value_cnt = hdr.case_size;
-  if (r->value_cnt <= 0
-      || r->value_cnt > (INT_MAX / (int) sizeof (union value) / 2))
-    lose ((ME, _("%s: Number of elements per case (%d) is not between 1 "
-                 "and %d."),
-           handle_get_filename (r->fh), r->value_cnt,
-           INT_MAX / sizeof (union value) / 2));
+
+  /* If value count is rediculous, then force it to -1 (a sentinel value) */
+  if ( r->value_cnt < 0 || 
+       r->value_cnt > (INT_MAX / (int) sizeof (union value) / 2))
+    r->value_cnt = -1;
 
   r->compressed = hdr.compress;
 
   r->weight_idx = hdr.weight_idx - 1;
-  if (hdr.weight_idx < 0 || hdr.weight_idx > r->value_cnt)
-    lose ((ME, _("%s: Index of weighting variable (%d) is not between 0 "
-                 "and number of elements per case (%d)."),
-          handle_get_filename (r->fh), hdr.weight_idx, r->value_cnt));
 
   r->case_cnt = hdr.case_cnt;
   if (r->case_cnt < -1 || r->case_cnt > INT_MAX / 2)
@@ -734,22 +752,31 @@ read_variables (struct sfm_reader *r,
   int long_string_count = 0;   /* # of long string continuation
                                   records still expected. */
   int next_value = 0;          /* Index to next `value' structure. */
-  size_t var_cap = 0;
 
   assert(r);
 
-  /* Allocate variables. */
-  *var_by_idx = xmalloc (sizeof **var_by_idx * r->value_cnt);
+  *var_by_idx = 0;
+
+  /* Pre-allocate variables. */
+  if ( r->value_cnt != -1 ) 
+    {
+      *var_by_idx = xmalloc(r->value_cnt * sizeof (**var_by_idx));
+      r->vars = xmalloc( r->value_cnt * sizeof (*r->vars) );
+    }
+
 
   /* Read in the entry for each variable and use the info to
      initialize the dictionary. */
-  for (i = 0; i < r->value_cnt; i++)
+  for (i = 0; ; ++i)
     {
       struct variable *vv;
-      char name[9];
+      char name[SHORT_NAME_LEN + 1];
       int nv;
       int j;
 
+      if ( r->value_cnt != -1  && i >= r->value_cnt ) 
+       break;
+
       assertive_buf_read (r, &sv, sizeof sv, 0);
 
       if (r->reverse_endian)
@@ -762,10 +789,19 @@ read_variables (struct sfm_reader *r,
          bswap_int32 (&sv.write);
        }
 
+      /* We've come to the end of the variable entries */
       if (sv.rec_type != 2)
-       lose ((ME, _("%s: position %d: Bad record type (%d); "
-                     "the expected value was 2."),
-               handle_get_filename (r->fh), i, sv.rec_type));
+       {
+         buf_unread(r, sizeof sv);
+         r->value_cnt = i;
+         break;
+       }
+
+      if ( -1 == r->value_cnt ) 
+       {
+         *var_by_idx = xrealloc (*var_by_idx, sizeof **var_by_idx * (i + 1));
+         r->vars = xrealloc(r->vars,  (i + 1) * sizeof (*r->vars) );
+       }
 
       /* If there was a long string previously, make sure that the
         continuations are present; otherwise make sure there aren't
@@ -777,6 +813,8 @@ read_variables (struct sfm_reader *r,
                         "proper number of continuation records."),
                    handle_get_filename (r->fh), i));
 
+
+         r->vars[i].width = -1;
          (*var_by_idx)[i] = NULL;
          long_string_count--;
          continue;
@@ -839,11 +877,17 @@ read_variables (struct sfm_reader *r,
        }
       name[j] = 0;
 
+      if ( ! var_is_valid_name(name, false) ) 
+        lose ((ME, _("%s: Invalid variable name `%s' within system file."),
+               handle_get_filename (r->fh), name));
+
       /* Create variable. */
+
       vv = (*var_by_idx)[i] = dict_create_var (dict, name, sv.type);
       if (vv == NULL) 
         lose ((ME, _("%s: Duplicate variable name `%s' within system file."),
                handle_get_filename (r->fh), name));
+
       var_set_short_name (vv, vv->name);
 
       /* Case reading data. */
@@ -944,15 +988,9 @@ read_variables (struct sfm_reader *r,
          || !parse_format_spec (r, sv.write, &vv->write, vv))
        goto error;
 
-      /* Add variable to list. */
-      if (var_cap >= r->var_cnt) 
-        {
-          var_cap = 2 + r->var_cnt * 2;
-          r->vars = xrealloc (r->vars, var_cap * sizeof *r->vars);
-        }
-      r->vars[r->var_cnt].width = vv->width;
-      r->vars[r->var_cnt].fv = vv->fv;
-      r->var_cnt++;
+      r->vars[i].width = vv->width;
+      r->vars[i].fv = vv->fv;
+
     }
 
   /* Some consistency checks. */
@@ -960,10 +998,12 @@ read_variables (struct sfm_reader *r,
     lose ((ME, _("%s: Long string continuation records omitted at end of "
                  "dictionary."),
            handle_get_filename (r->fh)));
+
   if (next_value != r->value_cnt)
-    lose ((ME, _("%s: System file header indicates %d variable positions but "
+    corrupt_msg(MW, _("%s: System file header indicates %d variable positions but "
                  "%d were read from file."),
-           handle_get_filename (r->fh), r->value_cnt, next_value));
+           handle_get_filename (r->fh), r->value_cnt, next_value);
+
 
   return 1;
 
@@ -1036,8 +1076,15 @@ read_value_labels (struct sfm_reader *r,
   if (r->reverse_endian)
     bswap_int32 (&n_labels);
 
+  if ( n_labels >= ((int32) ~0) / sizeof *labels)
+    {    
+      corrupt_msg(MW, _("%s: Invalid number of labels: %d.  Ignoring labels."),
+                 handle_get_filename (r->fh), n_labels);
+      n_labels = 0;
+    }
+
   /* Allocate memory. */
-  labels = xmalloc (n_labels * sizeof *labels);
+  labels = xcalloc (n_labels ,  sizeof *labels);
   for (i = 0; i < n_labels; i++)
     labels[i].label = NULL;
 
@@ -1220,6 +1267,19 @@ buf_read (struct sfm_reader *r, void *buf, size_t byte_cnt, size_t min_alloc)
   return buf;
 }
 
+/* Winds the reader BYTE_CNT bytes back in the reader stream.   */
+void
+buf_unread(struct sfm_reader *r, size_t byte_cnt)
+{
+  assert(byte_cnt > 0);
+
+  if ( 0 != fseek(r->file, -byte_cnt, SEEK_CUR))
+    {
+      msg (ME, _("%s: Seeking system file: %s."),
+          handle_get_filename (r->fh), strerror (errno));
+    }
+}
+
 /* Reads a document record, type 6, from system file R, and sets up
    the documents and n_documents fields in the associated
    dictionary. */
@@ -1296,7 +1356,7 @@ read_compressed_data (struct sfm_reader *r, flt64 *buf)
 
   for (;;)
     {
-      for (; p < p_end; p++)
+      for (; p < p_end; p++){
        switch (*p)
          {
          case 0:
@@ -1350,7 +1410,7 @@ read_compressed_data (struct sfm_reader *r, flt64 *buf)
              goto success;
            break;
          }
-
+      }
       /* We have reached the end of this instruction octet.  Read
         another. */
       if (r->ptr == NULL || r->ptr >= r->end)
@@ -1401,7 +1461,7 @@ sfm_read_case (struct sfm_reader *r, struct ccase *c)
         {
           int i;
           
-          for (i = 0; i < r->var_cnt; i++) 
+          for (i = 0; i < r->value_cnt; i++) 
             if (r->vars[i].width == 0)
               bswap_flt64 (&case_data_rw (c, r->vars[i].fv)->f);
         }
@@ -1413,7 +1473,7 @@ sfm_read_case (struct sfm_reader *r, struct ccase *c)
         {
           int i;
           
-          for (i = 0; i < r->var_cnt; i++) 
+          for (i = 0; i < r->value_cnt; i++) 
             if (r->vars[i].width == 0 && case_num (c, i) == r->sysmis)
               case_data_rw (c, r->vars[i].fv)->f = SYSMIS;
         }
@@ -1441,7 +1501,7 @@ sfm_read_case (struct sfm_reader *r, struct ccase *c)
           return 0;
         }
 
-      for (i = 0; i < r->var_cnt; i++)
+      for (i = 0; i < r->value_cnt; i++)
         {
           struct sfm_var *v = &r->vars[i];
 
@@ -1450,9 +1510,9 @@ sfm_read_case (struct sfm_reader *r, struct ccase *c)
               flt64 f = *bounce_cur++;
               if (r->reverse_endian)
                 bswap_flt64 (&f);
-              case_data_rw (c, i)->f = f == r->sysmis ? SYSMIS : f;
+              case_data_rw (c, v->fv)->f = f == r->sysmis ? SYSMIS : f;
             }
-          else 
+          else if (v->width != -1)
             {
               memcpy (case_data_rw (c, v->fv)->s, bounce_cur, v->width);
               bounce_cur += DIV_RND_UP (v->width, sizeof (flt64));