Add scratch file handles.
[pspp-builds.git] / src / pfm-read.c
index 0d5fbcc98000079c4739d8ec6d84ac89b5d6f5ea..f398747584ae9b7e48c5ffeee25bebfb66581fc1 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - computes sample statistics.
-   Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc.
    Written by Ben Pfaff <blp@gnu.org>.
    Code for parsing floating-point numbers adapted from GNU C
    library.
 #include <math.h>
 #include <setjmp.h>
 #include "alloc.h"
-#include "bool.h"
+#include <stdbool.h>
 #include "case.h"
 #include "dictionary.h"
 #include "file-handle.h"
 #include "format.h"
-#include "getline.h"
+#include "getl.h"
 #include "hash.h"
 #include "magic.h"
 #include "misc.h"
 #include "value-labels.h"
 #include "var.h"
 
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
 #include "debug-print.h"
 
+/* portable_to_local[PORTABLE] translates the given portable
+   character into the local character set. */
+static const char portable_to_local[256] =
+  {
+    "                                                                "
+    "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz ."
+    "<(+|&[]!$*);^-/|,%_>?`:$@'=\"      ~-   0123456789   -() {}\\     "
+    "                                                                "
+  };
+
 /* Portable file reader. */
 struct pfm_reader
   {
@@ -56,8 +69,7 @@ struct pfm_reader
     struct file_handle *fh;     /* File handle. */
     FILE *file;                        /* File stream. */
     char cc;                   /* Current character. */
-    unsigned char *trans;      /* 256-byte character set translation table. */
-
+    char *trans;                /* 256-byte character set translation table. */
     int var_cnt;                /* Number of variables. */
     int weight_index;          /* 0-based index of weight variable, or -1. */
     int *widths;                /* Variable widths, 0 for numeric. */
@@ -80,7 +92,7 @@ error (struct pfm_reader *r, const char *msg, ...)
 
   e.class = ME;
   getl_location (&e.where.filename, &e.where.line_number);
-  filename = handle_get_filename (r->fh);
+  filename = fh_get_filename (r->fh);
   e.title = title = pool_alloc (r->pool, strlen (filename) + 80);
   sprintf (title, _("portable file %s corrupt at offset %ld: "),
            filename, ftell (r->file));
@@ -147,7 +159,7 @@ pfm_open_reader (struct file_handle *fh, struct dictionary **dict,
   struct pfm_reader *volatile r = NULL;
 
   *dict = dict_create ();
-  if (!fh_open (fh, "portable file", "rs"))
+  if (!fh_open (fh, FH_REF_FILE, "portable file", "rs"))
     goto error;
 
   /* Create and initialize reader. */
@@ -157,7 +169,7 @@ pfm_open_reader (struct file_handle *fh, struct dictionary **dict,
   if (setjmp (r->bail_out))
     goto error;
   r->fh = fh;
-  r->file = pool_fopen (r->pool, handle_get_filename (r->fh), "rb");
+  r->file = pool_fopen (r->pool, fh_get_filename (r->fh), "rb");
   r->weight_index = -1;
   r->trans = NULL;
   r->var_cnt = 0;
@@ -169,7 +181,7 @@ pfm_open_reader (struct file_handle *fh, struct dictionary **dict,
     {
       msg (ME, _("An error occurred while opening \"%s\" for reading "
                  "as a portable file: %s."),
-           handle_get_filename (r->fh), strerror (errno));
+           fh_get_filename (r->fh), strerror (errno));
       err_cond_fail ();
       goto error;
     }
@@ -339,7 +351,7 @@ read_string (struct pfm_reader *r, char *buf)
 
 /* Reads a string and returns a copy of it allocated from R's
    pool. */
-static unsigned char *
+static char *
 read_pool_string (struct pfm_reader *r) 
 {
   char string[256];
@@ -351,17 +363,7 @@ read_pool_string (struct pfm_reader *r)
 static void
 read_header (struct pfm_reader *r)
 {
-  /* portable_to_local[PORTABLE] translates the given portable
-     character into the local character set. */
-  static const unsigned char portable_to_local[256] =
-    {
-      "                                                                "
-      "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz ."
-      "<(+|&[]!$*);^-/|,%_>?`:$@'=\"      ~-   0123456789   -() {}\\     "
-      "                                                                "
-    };
-
-  unsigned char *trans;
+  char *trans;
   int i;
 
   /* Read and ignore vanity splash strings. */
@@ -398,7 +400,7 @@ read_header (struct pfm_reader *r)
   for (i = 0; i < 8; i++) 
     if (!match (r, "SPSSPORT"[i])) 
       {
-        msg (SE, _("%s: Not a portable file."), handle_get_filename (r->fh));
+        msg (SE, _("%s: Not a portable file."), fh_get_filename (r->fh));
         longjmp (r->bail_out, 1);
       }
 }
@@ -408,6 +410,7 @@ read_header (struct pfm_reader *r)
 static void
 read_version_data (struct pfm_reader *r, struct pfm_read_info *info)
 {
+  static char empty_string[] = "";
   char *date, *time, *product, *author, *subproduct;
   int i;
 
@@ -416,10 +419,9 @@ read_version_data (struct pfm_reader *r, struct pfm_read_info *info)
     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) : (unsigned char *) "";
-  author = match (r, '2') ? read_pool_string (r) : (unsigned char *) "";
-  subproduct
-    = match (r, '3') ? read_pool_string (r) : (unsigned char *) "";
+  product = match (r, '1') ? read_pool_string (r) : empty_string;
+  author = match (r, '2') ? read_pool_string (r) : empty_string;
+  subproduct = match (r, '3') ? read_pool_string (r) : empty_string;
 
   /* Validate file. */
   if (strlen (date) != 8)
@@ -490,7 +492,7 @@ read_variables (struct pfm_reader *r, struct dictionary *dict)
   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);
-  r->widths = pool_alloc (r->pool, sizeof *r->widths * r->var_cnt);
+  r->widths = pool_nalloc (r->pool, r->var_cnt, sizeof *r->widths);
 
   /* Purpose of this value is unknown.  It is typically 161. */
   read_int (r);
@@ -523,7 +525,7 @@ read_variables (struct pfm_reader *r, struct dictionary *dict)
         fmt[j] = read_int (r);
 
       if (!var_is_valid_name (name, false) || *name == '#' || *name == '$')
-        error (r, _("position %d: Invalid variable name `%s'."), name);
+        error (r, _("position %d: Invalid variable name `%s'."), i, name);
       str_uppercase (name);
 
       if (width < 0 || width > 255)
@@ -537,45 +539,23 @@ read_variables (struct pfm_reader *r, struct dictionary *dict)
       convert_format (r, &fmt[3], &v->write, v);
 
       /* Range missing values. */
-      if (match (r, 'B'))
-       {
-         v->miss_type = MISSING_RANGE;
-          v->missing[0] = parse_value (r, v);
-          v->missing[1] = parse_value (r, v);
-       }
+      if (match (r, 'B')) 
+        {
+          double x = read_float (r);
+          double y = read_float (r);
+          mv_add_num_range (&v->miss, x, y);
+        }
       else if (match (r, 'A'))
-       {
-         v->miss_type = MISSING_HIGH;
-          v->missing[0] = parse_value (r, v);
-       }
+        mv_add_num_range (&v->miss, read_float (r), HIGHEST);
       else if (match (r, '9'))
-       {
-         v->miss_type = MISSING_LOW;
-          v->missing[0] = parse_value (r, v);
-       }
+        mv_add_num_range (&v->miss, LOWEST, read_float (r));
 
       /* Single missing values. */
-      while (match (r, '8'))
-       {
-         static const int map_next[MISSING_COUNT] =
-           {
-             MISSING_1, MISSING_2, MISSING_3, -1,
-             MISSING_RANGE_1, MISSING_LOW_1, MISSING_HIGH_1,
-             -1, -1, -1,
-           };
-
-         static const int map_ofs[MISSING_COUNT] = 
-           {
-             -1, 0, 1, 2, -1, -1, -1, 2, 1, 1,
-           };
-
-         v->miss_type = map_next[v->miss_type];
-         if (v->miss_type == -1)
-           error (r, _("Bad missing values for %s."), v->name);
-         
-         assert (map_ofs[v->miss_type] != -1);
-          v->missing[map_ofs[v->miss_type]] = parse_value (r, v);
-       }
+      while (match (r, '8')) 
+        {
+          union value value = parse_value (r, v);
+          mv_add_value (&v->miss, &value); 
+        }
 
       if (match (r, 'C')) 
         {
@@ -628,7 +608,7 @@ read_value_label (struct pfm_reader *r, struct dictionary *dict)
   int i;
 
   nv = read_int (r);
-  v = pool_alloc (r->pool, sizeof *v * nv);
+  v = pool_nalloc (r->pool, nv, sizeof *v);
   for (i = 0; i < nv; i++)
     {
       char name[256];
@@ -707,3 +687,38 @@ pfm_read_case (struct pfm_reader *r, struct ccase *c)
   
   return true;
 }
+
+/* Returns true if FILE is an SPSS portable file,
+   false otherwise. */
+bool
+pfm_detect (FILE *file) 
+{
+  unsigned char header[464];
+  char trans[256];
+  int cooked_cnt, raw_cnt;
+  int i;
+
+  cooked_cnt = raw_cnt = 0;
+  while (cooked_cnt < sizeof header)
+    {
+      int c = getc (file);
+      if (c == EOF || raw_cnt++ > 512)
+        return false;
+      else if (c != '\n' && c != '\r') 
+        header[cooked_cnt++] = c;
+    }
+
+  memset (trans, 0, 256);
+  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; 
+
+  return true;
+}