por-file-reader: Tolerate short lines when detecting portable files.
[pspp-builds.git] / src / data / por-file-reader.c
index e82e136167b9a06c006be699dd2929a03ddaf4b0..823361b09593aacc7eb33dae402f1fb31e798bdf 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, 2009 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
 #include <data/casereader.h>
 #include <data/dictionary.h>
 #include <data/file-handle-def.h>
+#include <data/file-name.h>
 #include <data/format.h>
 #include <data/missing-values.h>
+#include <data/short-names.h>
 #include <data/value-labels.h>
 #include <data/variable.h>
-#include <libpspp/alloc.h>
 #include <libpspp/compiler.h>
 #include <libpspp/hash.h>
-#include <libpspp/magic.h>
 #include <libpspp/message.h>
 #include <libpspp/misc.h>
 #include <libpspp/pool.h>
 #include <libpspp/str.h>
 
+#include "xalloc.h"
+
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
+#define N_(msgid) (msgid)
 
 /* portable_to_local[PORTABLE] translates the given portable
    character into the local character set. */
@@ -64,6 +67,7 @@ struct pfm_reader
     jmp_buf bail_out;           /* longjmp() target for error handling. */
 
     struct file_handle *fh;     /* File handle. */
+    struct fh_lock *lock;       /* Read lock for file. */
     FILE *file;                        /* File stream. */
     int line_length;            /* Number of characters so far on this line. */
     char cc;                   /* Current character. */
@@ -75,7 +79,7 @@ struct pfm_reader
     bool ok;                    /* Set false on I/O error. */
   };
 
-static struct casereader_class por_file_casereader_class;
+static const struct casereader_class por_file_casereader_class;
 
 static void
 error (struct pfm_reader *r, const char *msg,...)
@@ -136,12 +140,42 @@ warning (struct pfm_reader *r, const char *msg, ...)
   msg_emit (&m);
 }
 
+/* Close and destroy R.
+   Returns false if an error was detected on R, true otherwise. */
+static bool
+close_reader (struct pfm_reader *r)
+{
+  bool ok;
+  if (r == NULL)
+    return true;
+
+  if (r->file)
+    {
+      if (fn_close (fh_get_file_name (r->fh), r->file) == EOF)
+        {
+          msg (ME, _("Error closing portable file \"%s\": %s."),
+               fh_get_file_name (r->fh), strerror (errno));
+          r->ok = false;
+        }
+      r->file = NULL;
+    }
+
+  fh_unlock (r->lock);
+  fh_unref (r->fh);
+
+  ok = r->ok;
+  pool_destroy (r->pool);
+
+  return ok;
+}
+
 /* Closes portable file reader R, after we're done with it. */
 static void
-por_file_casereader_destroy (struct casereader *reader UNUSED, void *r_)
+por_file_casereader_destroy (struct casereader *reader, void *r_)
 {
   struct pfm_reader *r = r_;
-  pool_destroy (r->pool);
+  if (!close_reader (r))
+    casereader_force_error (reader);
 }
 
 /* Read a single character into cur_char.  */
@@ -210,17 +244,14 @@ pfm_open_reader (struct file_handle *fh, struct dictionary **dict,
   struct pfm_reader *volatile r = NULL;
 
   *dict = dict_create ();
-  if (!fh_open (fh, FH_REF_FILE, "portable file", "rs"))
-    goto error;
 
   /* Create and initialize reader. */
   pool = pool_create ();
   r = pool_alloc (pool, sizeof *r);
   r->pool = pool;
-  if (setjmp (r->bail_out))
-    goto error;
-  r->fh = fh;
-  r->file = pool_fopen (r->pool, fh_get_file_name (r->fh), "rb");
+  r->fh = fh_ref (fh);
+  r->lock = NULL;
+  r->file = NULL;
   r->line_length = 0;
   r->weight_index = -1;
   r->trans = NULL;
@@ -228,8 +259,18 @@ pfm_open_reader (struct file_handle *fh, struct dictionary **dict,
   r->widths = NULL;
   r->value_cnt = 0;
   r->ok = true;
+  if (setjmp (r->bail_out))
+    goto error;
+
+  /* Lock file. */
+  /* TRANSLATORS: this fragment will be interpolated into
+     messages in fh_lock() that identify types of files. */
+  r->lock = fh_lock (fh, FH_REF_FILE, N_("portable file"), FH_ACC_READ, false);
+  if (r->lock == NULL)
+    goto error;
 
-  /* Check that file open succeeded, prime reading. */
+  /* Open file. */
+  r->file = fn_open (fh_get_file_name (r->fh), "rb");
   if (r->file == NULL)
     {
       msg (ME, _("An error occurred while opening \"%s\" for reading "
@@ -260,7 +301,7 @@ pfm_open_reader (struct file_handle *fh, struct dictionary **dict,
                                        &por_file_casereader_class, r);
 
  error:
-  pool_destroy (r->pool);
+  close_reader (r);
   dict_destroy (*dict);
   *dict = NULL;
   return NULL;
@@ -468,8 +509,9 @@ 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;
+  static const char empty_string[] = "";
+  char *date, *time;
+  const char *product, *author, *subproduct;
   int i;
 
   /* Read file. */
@@ -483,9 +525,9 @@ read_version_data (struct pfm_reader *r, struct pfm_read_info *info)
 
   /* Validate file. */
   if (strlen (date) != 8)
-    error (r, _("Bad date string length %d."), (int) strlen (date));
+    error (r, _("Bad date string length %zu."), strlen (date));
   if (strlen (time) != 6)
-    error (r, _("Bad time string length %d."), (int) strlen (time));
+    error (r, _("Bad time string length %zu."), strlen (time));
 
   /* Save file info. */
   if (info != NULL)
@@ -579,7 +621,7 @@ read_variables (struct pfm_reader *r, struct dictionary *dict)
     error (r, _("Expected variable count record."));
 
   r->var_cnt = read_int (r);
-  if (r->var_cnt <= 0 || r->var_cnt == NOT_INT)
+  if (r->var_cnt <= 0)
     error (r, _("Invalid number of variables %d."), r->var_cnt);
   r->widths = pool_nalloc (r->pool, r->var_cnt, sizeof *r->widths);
 
@@ -629,8 +671,8 @@ read_variables (struct pfm_reader *r, struct dictionary *dict)
           int i;
           for (i = 1; i < 100000; i++)
             {
-              char try_name[LONG_NAME_LEN + 1];
-              sprintf (try_name, "%.*s_%d", LONG_NAME_LEN - 6, name, i);
+              char try_name[VAR_NAME_LEN + 1];
+              sprintf (try_name, "%.*s_%d", VAR_NAME_LEN - 6, name, i);
               v = dict_create_var (dict, try_name, width);
               if (v != NULL)
                 break;
@@ -652,12 +694,12 @@ read_variables (struct pfm_reader *r, struct dictionary *dict)
         {
           double x = read_float (r);
           double y = read_float (r);
-          mv_add_num_range (&miss, x, y);
+          mv_add_range (&miss, x, y);
         }
       else if (match (r, 'A'))
-        mv_add_num_range (&miss, read_float (r), HIGHEST);
+        mv_add_range (&miss, read_float (r), HIGHEST);
       else if (match (r, '9'))
-        mv_add_num_range (&miss, LOWEST, read_float (r));
+        mv_add_range (&miss, LOWEST, read_float (r));
 
       /* Single missing values. */
       while (match (r, '8'))
@@ -825,17 +867,30 @@ pfm_detect (FILE *file)
 {
   unsigned char header[464];
   char trans[256];
-  int cooked_cnt, raw_cnt;
+  int cooked_cnt, raw_cnt, line_len;
   int i;
 
   cooked_cnt = raw_cnt = 0;
+  line_len = 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;
+      else if (c == '\n')
+        {
+          while (line_len < 80 && cooked_cnt < sizeof header)
+            {
+              header[cooked_cnt++] = ' ';
+              line_len++;
+            }
+          line_len = 0;
+        }
+      else if (c != '\r')
+        {
+          header[cooked_cnt++] = c;
+          line_len++;
+        }
     }
 
   memset (trans, 0, 256);
@@ -853,7 +908,7 @@ pfm_detect (FILE *file)
   return true;
 }
 
-static struct casereader_class por_file_casereader_class =
+static const struct casereader_class por_file_casereader_class =
   {
     por_file_casereader_read,
     por_file_casereader_destroy,