Update to more recent GNULIB
[pspp] / src / libpspp / temp-file.c
index 554a6d7d5f74276774057905fe2e71ef318df919..03e6f18e61229e4717e2881e51d4728f2cc5cf5c 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2007, 2009, 2010 Free Software Foundation, Inc.
+   Copyright (C) 2010, 2011 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
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
 
-/* A interface to allow a temporary file to be treated as an
-   array of data. */
+/* Functions for temporary files that honor $TMPDIR. */
 
 #include <config.h>
 
-#include <libpspp/temp-file.h>
+#include "libpspp/temp-file.h"
+#include "libpspp/hmapx.h"
+#include "libpspp/hash-functions.h"
 
-#include <errno.h>
-#include <stdio.h>
 #include <stdlib.h>
 
-#include <libpspp/assertion.h>
-#include <libpspp/cast.h>
+#include "gl/clean-temp.h"
+#include "gl/xvasprintf.h"
 
-#include "error.h"
-#include "xalloc.h"
+/* Creates and returns a new temporary file that will be removed automatically
+   when the process exits.  The file is opened in mode "wb+".  To close the
+   file before the process exits, use close_temp_file() to ensure that it gets
+   deleted early.
 
-#include "gettext.h"
-#define _(msgid) gettext (msgid)
+   Returns NULL if creating the temporary file fails.
 
-struct temp_file
-  {
-    FILE *file;                 /* Underlying file. */
+   This is similar to tmpfile(), except:
 
-    /* Current byte offset in file.  We track this manually,
-       instead of using ftello, because in glibc ftello flushes
-       the stream buffer, making the common case of sequential
-       access to cases unreasonably slow. */
-    off_t position;
-  };
+     - It honors the $TMPDIR environment variable.
 
-/* Creates and returns a new temporary file.  The temporary file
-   will be automatically deleted when the process exits. */
-struct temp_file *
-temp_file_create (void)
+*/
+
+static void cleanup (void);
+
+static struct temp_dir *temp_dir;
+struct hmapx map;
+
+static void
+setup (void)
 {
-  struct temp_file *tf = xmalloc (sizeof *tf);
-  tf->file = tmpfile ();
-  if (tf->file == NULL)
-    error (0, errno, _("failed to create temporary file"));
-  tf->position = 0;
-  return tf;
+  hmapx_init (&map);
+  temp_dir = create_temp_dir ("pspp", NULL, true);
 }
 
-/* Closes and destroys temporary file TF.  Returns true if I/O on
-   TF always succeeded, false if an I/O error occurred at some
-   point. */
-bool
-temp_file_destroy (struct temp_file *tf)
+static void
+initialise (void)
 {
-  bool ok = true;
-  if (tf != NULL)
+  if (temp_dir == NULL)
     {
-      ok = !temp_file_error (tf);
-      if (tf->file != NULL)
-        fclose (tf->file);
-      free (tf);
+      setup ();
+      if (temp_dir == NULL)
+        return ;
+      atexit (cleanup);
     }
-  return ok;
 }
 
-/* Seeks TF's underlying file to the start of `union value'
-   VALUE_IDX within case CASE_IDX.
-   Returns true if the seek is successful and TF is not
-   otherwise tainted, false otherwise. */
-static bool
-do_seek (const struct temp_file *tf_, off_t offset)
+
+const char *
+temp_dir_name (void)
 {
-  struct temp_file *tf = CONST_CAST (struct temp_file *, tf_);
+  initialise ();
 
-  if (!temp_file_error (tf))
-    {
-      if (tf->position == offset)
-        return true;
-      else if (fseeko (tf->file, offset, SEEK_SET) == 0)
-        {
-          tf->position = offset;
-          return true;
-        }
-      else
-        error (0, errno, _("seeking in temporary file"));
-    }
+  if (temp_dir)
+    return temp_dir->dir_name;
 
-  return false;
+  return NULL;
 }
 
-/* Reads BYTES bytes from TF's underlying file into BUFFER.
-   TF must not be tainted upon entry into this function.
-   Returns true if successful, false upon an I/O error (in which
-   case TF is marked tainted). */
-static bool
-do_read (const struct temp_file *tf_, void *buffer, size_t bytes)
+static void
+cleanup (void)
 {
-  struct temp_file *tf = CONST_CAST (struct temp_file *, tf_);
+  struct hmapx_node *node;
+  char *fn;
 
-  assert (!temp_file_error (tf));
-  if (bytes > 0 && fread (buffer, bytes, 1, tf->file) != 1)
-    {
-      if (ferror (tf->file))
-        error (0, errno, _("reading temporary file"));
-      else if (feof (tf->file))
-        error (0, 0, _("unexpected end of file reading temporary file"));
-      else
-        NOT_REACHED ();
-      return false;
-    }
-  tf->position += bytes;
-  return true;
-}
+  cleanup_temp_dir (temp_dir);
 
-/* Writes BYTES bytes from BUFFER into TF's underlying file.
-   TF must not be tainted upon entry into this function.
-   Returns true if successful, false upon an I/O error (in which
-   case TF is marked tainted). */
-static bool
-do_write (struct temp_file *tf, const void *buffer, size_t bytes)
-{
-  assert (!temp_file_error (tf));
-  if (bytes > 0 && fwrite (buffer, bytes, 1, tf->file) != 1)
+  HMAPX_FOR_EACH (fn, node, &map)
     {
-      error (0, errno, _("writing to temporary file"));
-      return false;
+      free (fn);
     }
-  tf->position += bytes;
-  return true;
-}
 
-/* Reads N bytes from TF at byte offset OFFSET into DATA.
-   Returns true if successful, false on failure.  */
-bool
-temp_file_read (const struct temp_file *tf, off_t offset, size_t n, void *data)
-{
-  return do_seek (tf, offset) && do_read (tf, data, n);
+  hmapx_destroy (&map);
 }
 
-/* Writes the N bytes in DATA to TF at byte offset OFFSET.
-   Returns true if successful, false on failure.  */
-bool
-temp_file_write (struct temp_file *tf, off_t offset, size_t n,
-                 const void *data)
+FILE *
+create_temp_file (void)
 {
-  return do_seek (tf, offset) && do_write (tf, data, n);
+  static int idx = 0;
+  char *file_name;
+  FILE *stream;
+
+  initialise ();
+  if (temp_dir == NULL)
+    return NULL;
+
+  file_name = xasprintf ("%s/%d", temp_dir->dir_name, idx++);
+  register_temp_file (temp_dir, file_name);
+  stream = fopen_temp (file_name, "wb+", true);
+  if (stream == NULL)
+    unregister_temp_file (temp_dir, file_name);
+  else
+    setvbuf (stream, NULL, _IOFBF, 65536);
+
+  hmapx_insert (&map, file_name, hash_pointer (stream, 0));
+
+  return stream;
 }
 
-/* Returns true if an error has occurred in I/O on TF,
-   false if no error has been detected. */
-bool
-temp_file_error (const struct temp_file *tf)
+/* Closes and removes a temporary file created by create_temp_file(). */
+void
+close_temp_file (FILE *file)
 {
-  return tf->file == NULL || ferror (tf->file) || feof (tf->file);
+  if (file != NULL)
+    {
+      struct hmapx_node *node = hmapx_first_with_hash (&map, hash_pointer (file, 0));
+      char *fn = node->data;
+      fclose_temp (file);
+      cleanup_temp_file (temp_dir, fn);
+      hmapx_delete (&map, node);
+      free (fn);
+    }
 }