ext-array.c: Ensure that fseek is called before switching between read and write. 20120616000502/pspp 20120617000503/pspp 20120618000503/pspp 20120619000503/pspp 20120620000508/pspp
authorJohn Darrington <john@darrington.wattle.id.au>
Fri, 15 Jun 2012 18:29:24 +0000 (20:29 +0200)
committerJohn Darrington <john@darrington.wattle.id.au>
Fri, 15 Jun 2012 18:43:00 +0000 (20:43 +0200)
ANSI C requires that a file positioning function (eg seek) occurs before a switch
from reading to writing a stream (or vici-versa).  We had not been doing this.
This had caused problems which manifested themselves on Windows operating systems,
by mysteriously failing to write the stream (and thus truncating the datafile).
This change corrects this.

Thanks to Harry Thijssen and Henry Gong for their very valuable assistance tracking
down the cause of this problem.

Reviewed-by: Ben Pfaff
src/libpspp/ext-array.c

index 57ff8e4a7d6225e3b56c9b71c0760e6e9570f635..e5287fcc75d1637970cd0361ddddca4155d9a752 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2007, 2009, 2010, 2011 Free Software Foundation, Inc.
+   Copyright (C) 2007, 2009, 2010, 2011, 2012 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 "gettext.h"
 #define _(msgid) gettext (msgid)
 
+enum op
+  {
+    OP_WRITE, /* writing */
+    OP_READ   /* reading */
+  };
+
 struct ext_array
   {
     FILE *file;                 /* Underlying file. */
@@ -45,6 +51,9 @@ struct ext_array
        the stream buffer, making the common case of sequential
        access to cases unreasonably slow. */
     off_t position;
+
+    /* The most recent operation performed */
+    enum op op;
   };
 
 /* Creates and returns a new external array. */
@@ -56,6 +65,7 @@ ext_array_create (void)
   if (ea->file == NULL)
     error (0, errno, _("failed to create temporary file"));
   ea->position = 0;
+  ea->op = OP_WRITE;
   return ea;
 }
 
@@ -80,13 +90,12 @@ ext_array_destroy (struct ext_array *ea)
    Returns true if the seek is successful and EA is not
    otherwise tainted, false otherwise. */
 static bool
-do_seek (const struct ext_array *ea_, off_t offset)
+do_seek (const struct ext_array *ea_, off_t offset, enum op op)
 {
   struct ext_array *ea = CONST_CAST (struct ext_array *, ea_);
-
   if (!ext_array_error (ea))
     {
-      if (ea->position == offset)
+      if (ea->position == offset && ea->op == op)
         return true;
       else if (fseeko (ea->file, offset, SEEK_SET) == 0)
         {
@@ -121,6 +130,7 @@ do_read (const struct ext_array *ea_, void *buffer, size_t bytes)
       return false;
     }
   ea->position += bytes;
+  ea->op = OP_READ;
   return true;
 }
 
@@ -138,6 +148,7 @@ do_write (struct ext_array *ea, const void *buffer, size_t bytes)
       return false;
     }
   ea->position += bytes;
+  ea->op = OP_WRITE;
   return true;
 }
 
@@ -146,16 +157,17 @@ do_write (struct ext_array *ea, const void *buffer, size_t bytes)
 bool
 ext_array_read (const struct ext_array *ea, off_t offset, size_t n, void *data)
 {
-  return do_seek (ea, offset) && do_read (ea, data, n);
+  return do_seek (ea, offset, OP_READ) && do_read (ea, data, n);
 }
 
+
 /* Writes the N bytes in DATA to EA at byte offset OFFSET.
    Returns true if successful, false on failure.  */
 bool
 ext_array_write (struct ext_array *ea, off_t offset, size_t n,
                  const void *data)
 {
-  return do_seek (ea, offset) && do_write (ea, data, n);
+  return do_seek (ea, offset, OP_WRITE) && do_write (ea, data, n);
 }
 
 /* Returns true if an error has occurred in I/O on EA,