Fix problems with uniqueness of short names in system files with very
[pspp-builds.git] / src / data / sys-file-reader.c
index 148afd09c0bb3237ab43b30ed92d77599c1924dc..9a0c054c1889c23b2f63a1a5010effa61b77f2fc 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2006, 2007 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
@@ -77,6 +77,7 @@ struct sfm_reader
     int flt64_cnt;             /* Number of 8-byte units per case. */
     struct sfm_var *vars;       /* Variables. */
     size_t var_cnt;             /* Number of variables. */
+    int32_t case_cnt;           /* Number of cases */
     bool has_long_var_names;    /* File has a long variable name map */
     bool has_vls;               /* File has one or more very long strings? */
 
@@ -278,7 +279,7 @@ sfm_open_reader (struct file_handle *fh, struct dictionary **dict,
             name, but we want to retain it, so re-set it
             explicitly. */
          dict_rename_var (*dict, var, long_name);
-         var_set_short_name (var, short_name);
+         var_set_short_name (var, 0, short_name);
        }
 
       r->has_long_var_names = true;
@@ -308,7 +309,9 @@ sfm_open_reader (struct file_handle *fh, struct dictionary **dict,
 
   pool_free (r->pool, var_by_value_idx);
   r->value_cnt = dict_get_next_value_idx (*dict);
-  return casereader_create_sequential (NULL, r->value_cnt, CASENUMBER_MAX,
+  return casereader_create_sequential
+    (NULL, r->value_cnt,
+     r->case_cnt == -1 ? CASENUMBER_MAX: r->case_cnt,
                                        &sys_file_casereader_class, r);
 }
 
@@ -381,7 +384,6 @@ read_header (struct sfm_reader *r, struct dictionary *dict,
   char rec_type[5];
   char eye_catcher[61];
   uint8_t raw_layout_code[4];
-  int case_cnt;
   uint8_t raw_bias[8];
   char creation_date[10];
   char creation_time[9];
@@ -412,9 +414,10 @@ read_header (struct sfm_reader *r, struct dictionary *dict,
 
   *weight_idx = read_int32 (r);
 
-  case_cnt = read_int32 (r);
-  if (case_cnt < -1 || case_cnt > INT_MAX / 2)
-    case_cnt = -1;
+  r->case_cnt = read_int32 (r);
+  if ( r->case_cnt > INT_MAX / 2)
+    r->case_cnt = -1;
+
 
   /* Identify floating-point format and obtain compression bias. */
   read_bytes (r, raw_bias, sizeof raw_bias);
@@ -453,7 +456,7 @@ read_header (struct sfm_reader *r, struct dictionary *dict,
       info->integer_format = r->integer_format;
       info->float_format = r->float_format;
       info->compressed = r->compressed;
-      info->case_cnt = case_cnt;
+      info->case_cnt = r->case_cnt;
 
       product = ss_cstr (eye_catcher);
       ss_match_string (&product, ss_cstr ("@(#) SPSS DATA FILE"));
@@ -505,8 +508,8 @@ read_variable_record (struct sfm_reader *r, struct dictionary *dict,
                _("Duplicate variable name `%s' within system file."),
                name);
 
-  /* Set the short name the same as the long name */
-  var_set_short_name (var, var_get_name (var));
+  /* Set the short name the same as the long name. */
+  var_set_short_name (var, 0, var_get_name (var));
 
   /* Get variable label, if any. */
   if (has_variable_label != 0 && has_variable_label != 1)
@@ -901,8 +904,9 @@ read_long_var_name_map (struct sfm_reader *r, size_t size, size_t count,
   while (read_variable_to_value_map (r, dict, map, &var, &long_name,
                                      &warning_cnt))
     {
-      char short_name[SHORT_NAME_LEN + 1];
-      strcpy (short_name, var_get_short_name (var));
+      char **short_names;
+      size_t short_name_cnt;
+      size_t i;
 
       /* Validate long name. */
       if (!var_is_valid_name (long_name, false))
@@ -914,7 +918,7 @@ read_long_var_name_map (struct sfm_reader *r, size_t size, size_t count,
         }
 
       /* Identify any duplicates. */
-      if (strcasecmp (short_name, long_name)
+      if (strcasecmp (var_get_short_name (var, 0), long_name)
           && dict_lookup_var (dict, long_name) != NULL)
         {
           sys_warn (r, _("Duplicate long variable name `%s' "
@@ -922,11 +926,26 @@ read_long_var_name_map (struct sfm_reader *r, size_t size, size_t count,
           continue;
         }
 
-      /* Set long name.  Renaming a variable may clear the short
-         name, but we want to retain it, so re-set it
-         explicitly. */
+      /* Renaming a variable may clear its short names, but we
+         want to retain them, so we save them and re-set them
+         afterward. */
+      short_name_cnt = var_get_short_name_cnt (var);
+      short_names = xnmalloc (short_name_cnt, sizeof *short_names);
+      for (i = 0; i < short_name_cnt; i++) 
+        {
+          const char *s = var_get_short_name (var, i);
+          short_names[i] = s != NULL ? xstrdup (s) : NULL;
+        }
+
+      /* Set long name. */
       dict_rename_var (dict, var, long_name);
-      var_set_short_name (var, short_name);
+
+      /* Restore short names. */
+      for (i = 0; i < short_name_cnt; i++) 
+        {
+          var_set_short_name (var, i, short_names[i]);
+          free (short_names[i]);
+        }
     }
   close_variable_to_value_map (r, map);
   r->has_long_var_names = true;
@@ -1122,6 +1141,10 @@ read_value_labels (struct sfm_reader *r,
 
 static void partial_record (struct sfm_reader *r)
      NO_RETURN;
+
+static void read_error (struct casereader *, const struct sfm_reader *);
+
+
 static bool read_case_number (struct sfm_reader *, double *);
 static bool read_case_string (struct sfm_reader *, char *, size_t);
 static int read_opcode (struct sfm_reader *);
@@ -1154,6 +1177,8 @@ sys_file_casereader_read (struct casereader *reader, void *r_,
                            sizeof (union value) * r->flt64_cnt))
         {
           case_destroy (c);
+         if ( r->case_cnt != -1 )
+           read_error (reader, r);
           return false;
         }
 
@@ -1229,6 +1254,8 @@ sys_file_casereader_read (struct casereader *reader, void *r_,
       case_destroy (c);
       if (i != 0)
         partial_record (r);
+      if ( r->case_cnt != -1 )
+       read_error (reader, r);
       return false;
     }
 }
@@ -1240,6 +1267,13 @@ partial_record (struct sfm_reader *r)
   sys_error (r, _("File ends in partial case."));
 }
 
+static void
+read_error (struct casereader *r, const struct sfm_reader *sfm)
+{
+  msg (ME, _("Error reading case from file %s"), fh_get_name (sfm->fh));
+  casereader_force_error (r);
+}
+
 /* Reads a number from R and stores its value in *D.
    If R is compressed, reads a compressed number;
    otherwise, reads a number in the regular way.
@@ -1465,7 +1499,7 @@ lookup_var_by_short_name (struct dictionary *d, const char *short_name)
 
   /* First try looking up by full name.  This often succeeds. */
   var = dict_lookup_var (d, short_name);
-  if (var != NULL && !strcasecmp (var_get_short_name (var), short_name))
+  if (var != NULL && !strcasecmp (var_get_short_name (var, 0), short_name))
     return var;
 
   /* Iterate through the whole dictionary as a fallback. */
@@ -1473,7 +1507,7 @@ lookup_var_by_short_name (struct dictionary *d, const char *short_name)
   for (i = 0; i < var_cnt; i++)
     {
       var = dict_get_var (d, i);
-      if (!strcasecmp (var_get_short_name (var), short_name))
+      if (!strcasecmp (var_get_short_name (var, 0), short_name))
         return var;
     }