Merge commit 'origin/stable'
[pspp-builds.git] / src / data / sys-file-reader.c
index 0f583a12a5005fe360abd8662847039742b9afbb..f63a122fe83b96776b632cef57b51e49c83c4bd1 100644 (file)
@@ -88,6 +88,7 @@ struct sfm_reader
     double bias;               /* Compression bias, usually 100.0. */
     uint8_t opcodes[8];         /* Current block of opcodes. */
     size_t opcode_idx;          /* Next opcode to interpret, 8 if none left. */
+    bool corruption_warning;    /* Warned about possible corruption? */
   };
 
 static const struct casereader_class sys_file_casereader_class;
@@ -270,6 +271,7 @@ sfm_open_reader (struct file_handle *fh, struct dictionary **dict,
   r->oct_cnt = 0;
   r->has_long_var_names = false;
   r->opcode_idx = sizeof r->opcodes;
+  r->corruption_warning = false;
 
   /* TRANSLATORS: this fragment will be interpolated into
      messages in fh_lock() that identify types of files. */
@@ -505,9 +507,21 @@ read_header (struct sfm_reader *r, struct dictionary *dict,
   read_bytes (r, raw_bias, sizeof raw_bias);
   if (float_identify (100.0, raw_bias, sizeof raw_bias, &r->float_format) == 0)
     {
-      sys_warn (r, _("Compression bias is not the usual "
-                     "value of 100, or system file uses unrecognized "
-                     "floating-point format."));
+      uint8_t zero_bias[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+
+      if (memcmp (raw_bias, zero_bias, 8))
+        sys_warn (r, _("Compression bias is not the usual "
+                       "value of 100, or system file uses unrecognized "
+                       "floating-point format."));
+      else
+        {
+          /* Some software is known to write all-zeros to this
+             field.  Such software also writes floating-point
+             numbers in the format that we expect by default
+             (it seems that all software most likely does, in
+             reality), so don't warn in this case. */
+        }
+
       if (r->integer_format == INTEGER_MSB_FIRST)
         r->float_format = FLOAT_IEEE_DOUBLE_BE;
       else
@@ -577,7 +591,7 @@ read_variable_record (struct sfm_reader *r, struct dictionary *dict,
 
   /* Create variable. */
   if (width < 0 || width > 255)
-    sys_error (r, _("Bad variable width %d."), width);
+    sys_error (r, _("Bad width %d for variable %s."), width, name);
   var = dict_create_var (dict, name, width);
   if (var == NULL)
     sys_error (r,
@@ -919,7 +933,7 @@ read_machine_integer_info (struct sfm_reader *r, size_t size, size_t count,
     NOT_REACHED ();
   if (integer_representation != expected_integer_format)
     {
-      static const char *const endian[] = {N_("little-endian"), N_("big-endian")};
+      static const char *const endian[] = {N_("Little Endian"), N_("Big Endian")};
       sys_warn (r, _("Integer format indicated by system file (%s) "
                      "differs from expected (%s)."),
                 gettext (endian[integer_representation == 1]),
@@ -981,11 +995,16 @@ read_machine_float_info (struct sfm_reader *r, size_t size, size_t count)
                size, count);
 
   if (sysmis != SYSMIS)
-    sys_warn (r, _("File specifies unexpected value %g as SYSMIS."), sysmis);
+    sys_warn (r, _("File specifies unexpected value %g as %s."),
+              sysmis, "SYSMIS");
+
   if (highest != HIGHEST)
-    sys_warn (r, _("File specifies unexpected value %g as HIGHEST."), highest);
+    sys_warn (r, _("File specifies unexpected value %g as %s."),
+              highest, "HIGHEST");
+
   if (lowest != LOWEST)
-    sys_warn (r, _("File specifies unexpected value %g as LOWEST."), lowest);
+    sys_warn (r, _("File specifies unexpected value %g as %s."),
+              lowest, "LOWEST");
 }
 
 /* Read record type 7, subtype 11, which specifies how variables
@@ -1693,7 +1712,14 @@ read_compressed_number (struct sfm_reader *r, double *d)
       break;
 
     case 254:
-      sys_error (r, _("Compressed data is corrupt."));
+      float_convert (r->float_format, "        ", FLOAT_NATIVE_DOUBLE, d);
+      if (!r->corruption_warning)
+        {
+          r->corruption_warning = true;
+          sys_warn (r, _("Possible compressed data corruption: "
+                         "compressed spaces appear in numeric field."));
+        }
+      break;
 
     case 255:
       *d = SYSMIS;
@@ -1714,7 +1740,8 @@ read_compressed_number (struct sfm_reader *r, double *d)
 static bool
 read_compressed_string (struct sfm_reader *r, uint8_t *dst)
 {
-  switch (read_opcode (r))
+  int opcode = read_opcode (r);
+  switch (opcode)
     {
     case -1:
     case 252:
@@ -1729,7 +1756,25 @@ read_compressed_string (struct sfm_reader *r, uint8_t *dst)
       break;
 
     default:
-      sys_error (r, _("Compressed data is corrupt."));
+      {
+        double value = opcode - r->bias;
+        float_convert (FLOAT_NATIVE_DOUBLE, &value, r->float_format, dst);
+        if (value == 0.0)
+          {
+            /* This has actually been seen "in the wild".  The submitter of the
+               file that showed that the contents decoded as spaces, but they
+               were at the end of the field so it's possible that the null
+               bytes just acted as null terminators. */
+          }
+        else if (!r->corruption_warning)
+          {
+            r->corruption_warning = true;
+            sys_warn (r, _("Possible compressed data corruption: "
+                           "string contains compressed integer (opcode %d)"),
+                      opcode);
+          }
+      }
+      break;
     }
 
   return true;