work on docs
[pspp] / utilities / pspp-dump-sav.c
index ceabd1d57d4b3df973bfdf6129addc1777e1e675..36cb285380f564c93edbc19be87d4f6383ba43bb 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013 Free Software Foundation, Inc.
+   Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 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
@@ -18,6 +18,7 @@
 
 #include <ctype.h>
 #include <errno.h>
+#include <float.h>
 #include <getopt.h>
 #include <inttypes.h>
 #include <limits.h>
@@ -36,6 +37,7 @@
 #include "gl/progname.h"
 #include "gl/version-etc.h"
 #include "gl/xalloc.h"
+#include "gl/xsize.h"
 
 #define ID_MAX_LEN 64
 
@@ -98,7 +100,7 @@ static void read_simple_compressed_data (struct sfm_reader *, int max_cases);
 static void read_zlib_compressed_data (struct sfm_reader *);
 
 static struct text_record *open_text_record (
-  struct sfm_reader *, size_t size);
+  struct sfm_reader *, size_t size, size_t count);
 static void close_text_record (struct text_record *);
 static bool read_variable_to_value_pair (struct text_record *,
                                          char **key, char **value);
@@ -192,7 +194,7 @@ main (int argc, char *argv[])
 
       if (argc - optind > 1)
         printf ("Reading \"%s\":\n", r.file_name);
-      
+
       read_header (&r);
       while ((rec_type = read_int (&r)) != 999)
         {
@@ -234,9 +236,11 @@ main (int argc, char *argv[])
       else if (r.compression == COMP_ZLIB)
         read_zlib_compressed_data (&r);
 
+      free (r.var_widths);
+
       fclose (r.file);
     }
-  
+
   return 0;
 }
 
@@ -332,7 +336,7 @@ read_header (struct sfm_reader *r)
           : "<error>");
   printf ("\t%17s: %"PRId32"\n", "Weight index", weight_index);
   printf ("\t%17s: %"PRId32"\n", "Number of cases", ncases);
-  printf ("\t%17s: %g\n", "Compression bias", r->bias);
+  printf ("\t%17s: %.*g\n", "Compression bias", DBL_DIG + 1, r->bias);
   printf ("\t%17s: %s\n", "Creation date", creation_date);
   printf ("\t%17s: %s\n", "Creation time", creation_time);
   printf ("\t%17s: \"%s\"\n", "File label", file_label);
@@ -378,6 +382,8 @@ format_name (int format)
     case 37: return "CCE";
     case 38: return "EDATE";
     case 39: return "SDATE";
+    case 40: return "MTIME";
+    case 41: return "YMDHMS";
     default: return "invalid";
     }
 }
@@ -397,7 +403,7 @@ read_variable_record (struct sfm_reader *r)
   char name[9];
 
   printf ("%08llx: variable record #%d\n",
-          (long long int) ftello (r->file), r->n_variable_records++);
+          (long long int) ftello (r->file), ++r->n_variable_records);
 
   width = read_int (r);
   has_variable_label = read_int (r);
@@ -443,18 +449,14 @@ read_variable_record (struct sfm_reader *r)
   if (has_variable_label == 1)
     {
       long long int offset = ftello (r->file);
-      size_t len, read_len;
-      char label[255 + 1];
+      enum { MAX_LABEL_LEN = 65536 };
 
-      len = read_int (r);
-
-      /* Read up to 255 bytes of label. */
-      read_len = MIN (sizeof label - 1, len);
+      size_t len = read_int (r);
+      size_t read_len = MIN (MAX_LABEL_LEN, len);
+      char *label = xmalloc (read_len + 1);
       read_string (r, label, read_len + 1);
       printf("\t%08llx Variable label: \"%s\"\n", offset, label);
-
-      /* Skip unread label bytes. */
-      skip_bytes (r, len - read_len);
+      free (label);
 
       /* Skip label padding up to multiple of 4 bytes. */
       skip_bytes (r, ROUND_UP (len, 4) - len);
@@ -476,11 +478,11 @@ read_variable_record (struct sfm_reader *r)
             {
               double low = read_float (r);
               double high = read_float (r);
-              printf (" %g...%g", low, high);
+              printf (" %.*g...%.*g", DBL_DIG + 1, low, DBL_DIG + 1, high);
               missing_value_code = -missing_value_code - 2;
             }
           for (i = 0; i < missing_value_code; i++)
-            printf (" %g", read_float (r));
+            printf (" %.*g", DBL_DIG + 1, read_float (r));
         }
       else if (width > 0)
         {
@@ -509,7 +511,7 @@ print_untyped_value (struct sfm_reader *r, char raw_value[8])
     if (!isprint (raw_value[n_printable]))
       break;
 
-  printf ("%g/\"%.*s\"", value, n_printable, raw_value);
+  printf ("%.*g/\"%.*s\"", DBL_DIG + 1, value, n_printable, raw_value);
 }
 
 /* Reads value labels from sysfile R and inserts them into the
@@ -517,14 +519,14 @@ print_untyped_value (struct sfm_reader *r, char raw_value[8])
 static void
 read_value_label_record (struct sfm_reader *r)
 {
-  int label_cnt, var_cnt;
+  int n_labels, n_vars;
   int i;
 
   printf ("%08llx: value labels record\n", (long long int) ftello (r->file));
 
   /* Read number of labels. */
-  label_cnt = read_int (r);
-  for (i = 0; i < label_cnt; i++)
+  n_labels = read_int (r);
+  for (i = 0; i < n_labels; i++)
     {
       char raw_value[8];
       unsigned char label_len;
@@ -557,8 +559,8 @@ read_value_label_record (struct sfm_reader *r)
   /* Read number of variables associated with value label from type 4
      record. */
   printf ("\t%08llx: apply to variables", (long long int) ftello (r->file));
-  var_cnt = read_int (r);
-  for (i = 0; i < var_cnt; i++)
+  n_vars = read_int (r);
+  for (i = 0; i < n_vars; i++)
     printf (" #%d", read_int (r));
   putchar ('\n');
 }
@@ -605,12 +607,6 @@ read_extension_record (struct sfm_reader *r)
       read_machine_float_info (r, size, count);
       return;
 
-    case 5:
-      /* Variable sets information.  We don't use these yet.
-         They only apply to GUIs; see VARSETS on the APPLY
-         DICTIONARY command in SPSS documentation. */
-      break;
-
     case 6:
       /* DATE variable information.  We don't use it yet, but we
          should. */
@@ -718,20 +714,20 @@ read_machine_float_info (struct sfm_reader *r, size_t size, size_t count)
     sys_error (r, "Bad size (%zu) or count (%zu) on extension 4.",
                size, count);
 
-  printf ("\tsysmis: %g (%a)\n", sysmis, sysmis);
+  printf ("\tsysmis: %.*g (%a)\n", DBL_DIG + 1, sysmis, sysmis);
   if (sysmis != SYSMIS)
-    sys_warn (r, "File specifies unexpected value %g (%a) as %s.",
-              sysmis, sysmis, "SYSMIS");
+    sys_warn (r, "File specifies unexpected value %.*g (%a) as %s.",
+              DBL_DIG + 1, sysmis, sysmis, "SYSMIS");
 
-  printf ("\thighest: %g (%a)\n", highest, highest);
+  printf ("\thighest: %.*g (%a)\n", DBL_DIG + 1, highest, highest);
   if (highest != HIGHEST)
-    sys_warn (r, "File specifies unexpected value %g (%a) as %s.",
-              highest, highest, "HIGHEST");
+    sys_warn (r, "File specifies unexpected value %.*g (%a) as %s.",
+              DBL_DIG + 1, highest, highest, "HIGHEST");
 
-  printf ("\tlowest: %g (%a)\n", lowest, lowest);
+  printf ("\tlowest: %.*g (%a)\n", DBL_DIG + 1, lowest, lowest);
   if (lowest != LOWEST && lowest != SYSMIS)
-    sys_warn (r, "File specifies unexpected value %g (%a) as %s.",
-              lowest, lowest, "LOWEST");
+    sys_warn (r, "File specifies unexpected value %.*g (%a) as %s.",
+              DBL_DIG + 1, lowest, lowest, "LOWEST");
 }
 
 static void
@@ -742,7 +738,7 @@ read_extra_product_info (struct sfm_reader *r,
   const char *s;
 
   printf ("%08llx: extra product info\n", (long long int) ftello (r->file));
-  text = open_text_record (r, size * count);
+  text = open_text_record (r, size, count);
   s = text_get_all (text);
   print_string (s, strlen (s));
   close_text_record (text);
@@ -756,7 +752,7 @@ read_mrsets (struct sfm_reader *r, size_t size, size_t count)
 
   printf ("%08llx: multiple response sets\n",
           (long long int) ftello (r->file));
-  text = open_text_record (r, size * count);
+  text = open_text_record (r, size, count);
   for (;;)
     {
       const char *name;
@@ -767,6 +763,9 @@ read_mrsets (struct sfm_reader *r, size_t size, size_t count)
       const char *label;
       const char *variables;
 
+      while (text_match (text, '\n'))
+        continue;
+
       name = text_tokenize (text, '=');
       if (name == NULL)
         break;
@@ -801,7 +800,11 @@ read_mrsets (struct sfm_reader *r, size_t size, size_t count)
             }
 
           number = text_tokenize (text, ' ');
-          if (!strcmp (number, "11"))
+          if (!number)
+            sys_warn (r, "Missing label source value "
+                      "following `E' at offset %zu in MRSETS record",
+                      text_pos (text));
+          else if (!strcmp (number, "11"))
             label_from_var_label = true;
           else if (strcmp (number, "1"))
             sys_warn (r, "Unexpected label source value `%s' "
@@ -828,12 +831,6 @@ read_mrsets (struct sfm_reader *r, size_t size, size_t count)
         break;
 
       variables = text_tokenize (text, '\n');
-      if (variables == NULL)
-        {
-          sys_warn (r, "missing variable names following label "
-                    "at offset %zu in mrsets record", text_pos (text));
-          break;
-        }
 
       printf ("\t\"%s\": multiple %s set",
               name, type == MRSET_MC ? "category" : "dichotomy");
@@ -845,7 +842,10 @@ read_mrsets (struct sfm_reader *r, size_t size, size_t count)
         printf (", label \"%s\"", label);
       if (label_from_var_label)
         printf (", label from variable label");
-      printf(", variables \"%s\"\n", variables);
+      if (variables != NULL)
+        printf(", variables \"%s\"\n", variables);
+      else
+        printf(", no variables\n");
     }
   close_text_record (text);
 }
@@ -912,7 +912,7 @@ read_long_var_name_map (struct sfm_reader *r, size_t size, size_t count)
 
   printf ("%08llx: long variable names (short => long)\n",
           (long long int) ftello (r->file));
-  text = open_text_record (r, size * count);
+  text = open_text_record (r, size, count);
   while (read_variable_to_value_pair (text, &var, &long_name))
     printf ("\t%s => %s\n", var, long_name);
   close_text_record (text);
@@ -929,7 +929,7 @@ read_long_string_map (struct sfm_reader *r, size_t size, size_t count)
 
   printf ("%08llx: very long strings (variable => length)\n",
           (long long int) ftello (r->file));
-  text = open_text_record (r, size * count);
+  text = open_text_record (r, size, count);
   while (read_variable_to_value_pair (text, &var, &length_s))
     printf ("\t%s => %d\n", var, atoi (length_s));
   close_text_record (text);
@@ -942,17 +942,17 @@ read_attributes (struct sfm_reader *r, struct text_record *text,
   const char *key;
   int index;
 
-  for (;;) 
+  for (;;)
     {
       key = text_tokenize (text, '(');
       if (key == NULL)
         return true;
-  
+
       for (index = 1; ; index++)
         {
           /* Parse the value. */
           const char *value = text_tokenize (text, '\n');
-          if (value == NULL) 
+          if (value == NULL)
             {
               sys_warn (r, "%s: Error parsing attribute value %s[%d]",
                         variable, key, index);
@@ -972,7 +972,7 @@ read_attributes (struct sfm_reader *r, struct text_record *text,
         }
 
       if (text_match (text, '/'))
-        return true; 
+        return true;
     }
 }
 
@@ -1002,12 +1002,12 @@ read_ncases64 (struct sfm_reader *r, size_t size, size_t count)
 }
 
 static void
-read_datafile_attributes (struct sfm_reader *r, size_t size, size_t count) 
+read_datafile_attributes (struct sfm_reader *r, size_t size, size_t count)
 {
   struct text_record *text;
-  
+
   printf ("%08llx: datafile attributes\n", (long long int) ftello (r->file));
-  text = open_text_record (r, size * count);
+  text = open_text_record (r, size, count);
   read_attributes (r, text, "datafile");
   close_text_record (text);
 }
@@ -1020,6 +1020,8 @@ read_character_encoding (struct sfm_reader *r, size_t size, size_t count)
   read_string (r, encoding, count + 1);
 
   printf ("%08llx: Character Encoding: %s\n", posn, encoding);
+
+  free (encoding);
 }
 
 static void
@@ -1194,17 +1196,17 @@ read_unknown_extension (struct sfm_reader *r, size_t size, size_t count)
 }
 
 static void
-read_variable_attributes (struct sfm_reader *r, size_t size, size_t count) 
+read_variable_attributes (struct sfm_reader *r, size_t size, size_t count)
 {
   struct text_record *text;
-  
+
   printf ("%08llx: variable attributes\n", (long long int) ftello (r->file));
-  text = open_text_record (r, size * count);
-  for (;;) 
+  text = open_text_record (r, size, count);
+  for (;;)
     {
       const char *variable = text_tokenize (text, ':');
       if (variable == NULL || !read_attributes (r, text, variable))
-        break; 
+        break;
     }
   close_text_record (text);
 }
@@ -1229,7 +1231,7 @@ read_simple_compressed_data (struct sfm_reader *r, int max_cases)
     {
       printf ("%08llx: case %d's uncompressible data begins\n",
               (long long int) ftello (r->file), case_num);
-      for (i = 0; i < r->n_var_widths; )
+      for (i = 0; i < r->n_var_widths;)
         {
           int width = r->var_widths[i];
           char raw_value[8];
@@ -1254,7 +1256,7 @@ read_simple_compressed_data (struct sfm_reader *r, int max_cases)
           switch (opcode)
             {
             default:
-              printf ("%g", opcode - r->bias);
+              printf ("%.*g", DBL_DIG + 1, opcode - r->bias);
               if (width != 0)
                 printf (", but this is a string variable (width=%d)", width);
               printf ("\n");
@@ -1392,18 +1394,23 @@ struct text_record
     size_t pos;                 /* Current position in buffer. */
   };
 
-/* Reads SIZE bytes into a text record for R,
+/* Reads SIZE * COUNT bytes into a text record for R,
    and returns the new text record. */
 static struct text_record *
-open_text_record (struct sfm_reader *r, size_t size)
+open_text_record (struct sfm_reader *r, size_t size, size_t count)
 {
   struct text_record *text = xmalloc (sizeof *text);
-  char *buffer = xmalloc (size + 1);
-  read_bytes (r, buffer, size);
-  buffer[size] = '\0';
+
+  if (size_overflow_p (xsum (1, xtimes (size, count))))
+    sys_error (r, "Extension record too large.");
+
+  size_t n_bytes = size * count;
+  char *buffer = xmalloc (n_bytes + 1);
+  read_bytes (r, buffer, n_bytes);
+  buffer[n_bytes] = '\0';
   text->reader = r;
   text->buffer = buffer;
-  text->size = size;
+  text->size = n_bytes;
   text->pos = 0;
   return text;
 }
@@ -1433,9 +1440,9 @@ text_tokenize (struct text_record *text, int delimiter)
 }
 
 static bool
-text_match (struct text_record *text, int c) 
+text_match (struct text_record *text, int c)
 {
-  if (text->pos < text->size && text->buffer[text->pos] == c) 
+  if (text->pos < text->size && text->buffer[text->pos] == c)
     {
       text->pos++;
       return true;
@@ -1583,10 +1590,10 @@ sys_error (struct sfm_reader *r, const char *format, ...)
    too. */
 static inline bool
 read_bytes_internal (struct sfm_reader *r, bool eof_is_ok,
-                     void *buf, size_t byte_cnt)
+                     void *buf, size_t n_bytes)
 {
-  size_t bytes_read = fread (buf, 1, byte_cnt, r->file);
-  if (bytes_read == byte_cnt)
+  size_t bytes_read = fread (buf, 1, n_bytes, r->file);
+  if (bytes_read == n_bytes)
     return true;
   else if (ferror (r->file))
     sys_error (r, "System error: %s.", strerror (errno));
@@ -1599,9 +1606,9 @@ read_bytes_internal (struct sfm_reader *r, bool eof_is_ok,
 /* Reads BYTE_CNT into BUF.
    Aborts upon I/O error or if end-of-file is encountered. */
 static void
-read_bytes (struct sfm_reader *r, void *buf, size_t byte_cnt)
+read_bytes (struct sfm_reader *r, void *buf, size_t n_bytes)
 {
-  read_bytes_internal (r, false, buf, byte_cnt);
+  read_bytes_internal (r, false, buf, n_bytes);
 }
 
 /* Reads BYTE_CNT bytes into BUF.
@@ -1609,9 +1616,9 @@ read_bytes (struct sfm_reader *r, void *buf, size_t byte_cnt)
    Returns false if an immediate end-of-file is encountered.
    Aborts if an I/O error or a partial read occurs. */
 static bool
-try_read_bytes (struct sfm_reader *r, void *buf, size_t byte_cnt)
+try_read_bytes (struct sfm_reader *r, void *buf, size_t n_bytes)
 {
-  return read_bytes_internal (r, true, buf, byte_cnt);
+  return read_bytes_internal (r, true, buf, n_bytes);
 }
 
 /* Reads a 32-bit signed integer from R and returns its value in