Added files in src/math
[pspp] / src / file-handle-def.c
index 5dd13e5415f5d692577c05403b8f6efebb8e0425..3be48250e85c3b35dfef883acc4b03a6443e7ba6 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - computes sample statistics.
 /* 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>.
 
    This program is free software; you can redistribute it and/or
    Written by Ben Pfaff <blp@gnu.org>.
 
    This program is free software; you can redistribute it and/or
    02110-1301, USA. */
 
 #include <config.h>
    02110-1301, USA. */
 
 #include <config.h>
-#include "file-handle.h"
+#include "file-handle-def.h"
 #include "error.h"
 #include <errno.h>
 #include <stdlib.h>
 #include <string.h>
 #include "alloc.h"
 #include "error.h"
 #include <errno.h>
 #include <stdlib.h>
 #include <string.h>
 #include "alloc.h"
+#include "file-handle.h"
 #include "filename.h"
 #include "command.h"
 #include "getl.h"
 #include "error.h"
 #include "magic.h"
 #include "var.h"
 #include "filename.h"
 #include "command.h"
 #include "getl.h"
 #include "error.h"
 #include "magic.h"
 #include "var.h"
-#include "file-handle-def.h"
+#include "scratch-handle.h"
 
 #include "gettext.h"
 
 #include "gettext.h"
-
 #define _(msgid) gettext (msgid)
 
 /* (headers) */
 #define _(msgid) gettext (msgid)
 
 /* (headers) */
 struct file_handle 
   {
     struct file_handle *next;   /* Next in global list. */
 struct file_handle 
   {
     struct file_handle *next;   /* Next in global list. */
+    int open_cnt;               /* 0=not open, otherwise # of openers. */
+    bool deleted;               /* Destroy handle when open_cnt goes to 0? */
+
     char *name;                 /* File handle identifier. */
     char *name;                 /* File handle identifier. */
+    const char *type;           /* If open, type of file. */
+    char open_mode[3];          /* "[rw][se]". */
+    void *aux;                  /* Aux data pointer for owner if any. */
+    enum fh_referent referent;  /* What the file handle refers to. */
+
+    /* FH_REF_FILE only. */
     char *filename;            /* Filename as provided by user. */
     struct file_identity *identity; /* For checking file identity. */
     char *filename;            /* Filename as provided by user. */
     struct file_identity *identity; /* For checking file identity. */
-    struct file_locator where; /* Used for reporting error messages. */
-    enum file_handle_mode mode;        /* File mode. */
-    size_t length;             /* Length of fixed-format records. */
+    enum fh_mode mode;         /* File mode. */
+
+    /* FH_REF_FILE and FH_REF_INLINE only. */
+    size_t record_width;        /* Length of fixed-format records. */
     size_t tab_width;           /* Tab width, 0=do not expand tabs. */
 
     size_t tab_width;           /* Tab width, 0=do not expand tabs. */
 
-    int open_cnt;               /* 0=not open, otherwise # of openers. */
-    const char *type;           /* If open, type of file. */
-    char open_mode[3];          /* "[rw][se]". */
-    void *aux;                  /* Aux data pointer for owner if any. */
+    /* FH_REF_SCRATCH only. */
+    struct scratch_handle *sh;  /* Scratch file data. */
   };
 
   };
 
-
+/* List of all handles. */
 static struct file_handle *file_handles;
 
 static struct file_handle *file_handles;
 
+/* Default file handle for DATA LIST, REREAD, REPEATING DATA
+   commands. */
+static struct file_handle *default_handle;
+
+/* The "file" that reads from BEGIN DATA...END DATA. */
+static struct file_handle *inline_file;
+
+static struct file_handle *create_handle (const char *name, enum fh_referent);
+
+/* File handle initialization routine. */
+void 
+fh_init (void)
+{
+  inline_file = create_handle ("INLINE", FH_REF_INLINE);
+  inline_file->record_width = 80;
+  inline_file->tab_width = 8;
+}
+
+/* Free HANDLE and remove it from the global list. */
+static void
+free_handle (struct file_handle *handle) 
+{
+  /* Remove handle from global list. */
+  if (file_handles == handle)
+    file_handles = handle->next;
+  else 
+    {
+      struct file_handle *iter = file_handles;
+      while (iter->next != handle)
+        iter = iter->next;
+      iter->next = handle->next;
+    }
+
+  /* Free data. */
+  free (handle->name);
+  free (handle->filename);
+  fn_free_identity (handle->identity);
+  scratch_handle_destroy (handle->sh);
+  free (handle);
+}
+
+/* Frees all the file handles. */
+void 
+fh_done (void)
+{
+  while (file_handles != NULL) 
+    free_handle (file_handles);
+}
 
 
+/* Returns the handle named HANDLE_NAME, or a null pointer if
+   there is none. */
 struct file_handle *
 struct file_handle *
-get_handle_with_name (const char *handle_name) 
+fh_from_name (const char *handle_name) 
 {
   struct file_handle *iter;
 
   for (iter = file_handles; iter != NULL; iter = iter->next)
 {
   struct file_handle *iter;
 
   for (iter = file_handles; iter != NULL; iter = iter->next)
-    if (!strcasecmp (handle_name, iter->name))
+    if (!iter->deleted && !strcasecmp (handle_name, iter->name))
       return iter;
   return NULL;
 }
 
       return iter;
   return NULL;
 }
 
+/* Returns the handle for the file named FILENAME,
+   or a null pointer if none exists.
+   Different names for the same file (e.g. "x" and "./x") are
+   considered equivalent. */
 struct file_handle *
 struct file_handle *
-get_handle_for_filename (const char *filename)
+fh_from_filename (const char *filename)
 {
   struct file_identity *identity;
   struct file_handle *iter;
 {
   struct file_identity *identity;
   struct file_handle *iter;
@@ -82,7 +144,9 @@ get_handle_for_filename (const char *filename)
   if (identity != NULL) 
     {
       for (iter = file_handles; iter != NULL; iter = iter->next)
   if (identity != NULL) 
     {
       for (iter = file_handles; iter != NULL; iter = iter->next)
-        if (iter->identity != NULL
+        if (!iter->deleted
+            && iter->referent == FH_REF_FILE
+            && iter->identity != NULL
             && !fn_compare_file_identities (identity, iter->identity))
           {
             fn_free_identity (identity);
             && !fn_compare_file_identities (identity, iter->identity))
           {
             fn_free_identity (identity);
@@ -93,65 +157,103 @@ get_handle_for_filename (const char *filename)
 
   /* Then check for a file with the same name. */
   for (iter = file_handles; iter != NULL; iter = iter->next)
 
   /* Then check for a file with the same name. */
   for (iter = file_handles; iter != NULL; iter = iter->next)
-    if (!strcmp (filename, iter->filename))
+    if (!iter->deleted
+        && iter->referent == FH_REF_FILE && !strcmp (filename, iter->filename))
       return iter; 
 
   return NULL;
 }
 
       return iter; 
 
   return NULL;
 }
 
+/* Creates a new handle with name HANDLE_NAME that refers to
+   REFERENT.  Links the new handle into the global list.  Returns
+   the new handle.
 
 
-/* File handle functions. */
+   The new handle is not fully initialized.  The caller is
+   responsible for completing its initialization. */
+static struct file_handle *
+create_handle (const char *handle_name, enum fh_referent referent) 
+{
+  struct file_handle *handle = xzalloc (sizeof *handle);
+  handle->next = file_handles;
+  handle->open_cnt = 0;
+  handle->deleted = false;
+  handle->name = xstrdup (handle_name);
+  handle->type = NULL;
+  handle->aux = NULL;
+  handle->referent = referent;
+  file_handles = handle;
+  return handle;
+}
 
 
+/* Returns the unique handle of referent type FH_REF_INLINE,
+   which refers to the "inline file" that represents character
+   data in the command file between BEGIN DATA and END DATA. */
 struct file_handle *
 struct file_handle *
-create_file_handle_with_defaults (const char *handle_name, 
-                                 const char *filename)
+fh_inline_file (void) 
 {
 {
-  return create_file_handle (handle_name, filename, 
-                            MODE_TEXT,1024, 4);
+  return inline_file;
 }
 
 }
 
-
-/* Creates and returns a new file handle with the given values
-   and defaults for other values.  Adds the created file handle
-   to the global list. */
+/* Creates a new file handle named HANDLE_NAME, which must not be
+   the name of an existing file handle.  The new handle is
+   associated with file FILENAME and the given PROPERTIES. */
 struct file_handle *
 struct file_handle *
-create_file_handle (const char *handle_name, const char *filename,
-                   enum file_handle_mode mode,
-                   size_t length,
-                   size_t tab_width
-                   )
+fh_create_file (const char *handle_name, const char *filename,
+                const struct fh_properties *properties)
 {
   struct file_handle *handle;
 {
   struct file_handle *handle;
-
-  /* Create and initialize file handle. */
-  handle = xmalloc (sizeof *handle);
-  handle->next = file_handles;
-  handle->name = xstrdup (handle_name);
+  assert (fh_from_name (handle_name) == NULL);
+  handle = create_handle (handle_name, FH_REF_FILE);
   handle->filename = xstrdup (filename);
   handle->identity = fn_get_identity (filename);
   handle->filename = xstrdup (filename);
   handle->identity = fn_get_identity (filename);
-  handle->where.filename = handle->filename;
-  handle->where.line_number = 0;
-  handle->mode = mode;
-  handle->length = length;
-  handle->tab_width = tab_width;
-  handle->open_cnt = 0;
-  handle->type = NULL;
-  handle->aux = NULL;
-  file_handles = handle;
+  handle->mode = properties->mode;
+  handle->record_width = properties->record_width;
+  handle->tab_width = properties->tab_width;
+  return handle;
+}
 
 
+/* Creates a new file handle named HANDLE_NAME, which must not be
+   the name of an existing file handle.  The new handle is
+   associated with a scratch file (initially empty). */
+struct file_handle *
+fh_create_scratch (const char *handle_name) 
+{
+  struct file_handle *handle = create_handle (handle_name, FH_REF_SCRATCH);
+  handle->sh = NULL;
   return handle;
 }
 
   return handle;
 }
 
-void
-destroy_file_handle(void *fh_, void *aux UNUSED)
+/* Returns a set of default properties for a file handle. */
+const struct fh_properties *
+fh_default_properties (void)
 {
 {
-  struct file_handle *fh = fh_;
-  free (fh->name);
-  free (fh->filename);
-  fn_free_identity (fh->identity);
-  free (fh);
+  static const struct fh_properties default_properties
+    = {FH_MODE_TEXT, 1024, 4};
+  return &default_properties;
+}
+
+/* Deletes FH from the global list of file handles.  Afterward,
+   attempts to search for it will fail.  Unless the file handle
+   is currently open, it will be destroyed; otherwise, it will be
+   destroyed later when it is closed.
+   Normally needed only if a file_handle needs to be re-assigned.
+   Otherwise, just let fh_done() destroy the handle. */
+void 
+fh_free (struct file_handle *handle)
+{
+  if (handle == fh_inline_file () || handle == NULL || handle->deleted)
+    return;
+  handle->deleted = true;
+
+  if (handle == default_handle)
+    default_handle = fh_inline_file ();
+
+  if (handle->open_cnt == 0)
+    free_handle (handle);
 }
 
 }
 
+/* Returns an English description of MODE,
+   which is in the format of the MODE argument to fh_open(). */
 static const char *
 mode_name (const char *mode) 
 {
 static const char *
 mode_name (const char *mode) 
 {
@@ -161,11 +263,15 @@ mode_name (const char *mode)
   return mode[0] == 'r' ? "reading" : "writing";
 }
 
   return mode[0] == 'r' ? "reading" : "writing";
 }
 
-
 /* Tries to open handle H with the given TYPE and MODE.
 
 /* Tries to open handle H with the given TYPE and MODE.
 
+   H's referent type must be one of the bits in MASK.  The caller
+   must verify this ahead of time; we simply assert it here.
+
    TYPE is the sort of file, e.g. "system file".  Only one given
    type of access is allowed on a given file handle at once.
    TYPE is the sort of file, e.g. "system file".  Only one given
    type of access is allowed on a given file handle at once.
+   If successful, a reference to TYPE is retained, so it should
+   probably be a string literal.
 
    MODE combines the read or write mode with the sharing mode.
    The first character is 'r' for read, 'w' for write.  The
 
    MODE combines the read or write mode with the sharing mode.
    The first character is 'r' for read, 'w' for write.  The
@@ -177,14 +283,13 @@ mode_name (const char *mode)
    pointer on failure.  For exclusive access modes the void *
    will always be a null pointer at return.  In shared access
    modes the void * will necessarily be null only if no other
    pointer on failure.  For exclusive access modes the void *
    will always be a null pointer at return.  In shared access
    modes the void * will necessarily be null only if no other
-   sharers are active.
-
-   If successful, a reference to type is retained, so it should
-   probably be a string literal. */
+   sharers are active. */
 void **
 void **
-fh_open (struct file_handle *h, const char *type, const char *mode) 
+fh_open (struct file_handle *h, enum fh_referent mask UNUSED,
+         const char *type, const char *mode) 
 {
   assert (h != NULL);
 {
   assert (h != NULL);
+  assert ((fh_get_referent (h) & mask) != 0);
   assert (type != NULL);
   assert (mode != NULL);
   assert (mode[0] == 'r' || mode[0] == 'w');
   assert (type != NULL);
   assert (mode != NULL);
   assert (mode[0] == 'r' || mode[0] == 'w');
@@ -196,22 +301,22 @@ fh_open (struct file_handle *h, const char *type, const char *mode)
       if (strcmp (h->type, type)) 
         {
           msg (SE, _("Can't open %s as a %s because it is "
       if (strcmp (h->type, type)) 
         {
           msg (SE, _("Can't open %s as a %s because it is "
-                     "already open as a %s"),
-               handle_get_name (h), type, h->type);
+                     "already open as a %s."),
+               fh_get_name (h), type, h->type);
           return NULL; 
         }
       else if (strcmp (h->open_mode, mode)) 
         {
           msg (SE, _("Can't open %s as a %s for %s because it is "
           return NULL; 
         }
       else if (strcmp (h->open_mode, mode)) 
         {
           msg (SE, _("Can't open %s as a %s for %s because it is "
-                     "already open for %s"),
-               handle_get_name (h), type,
-               mode_name (mode), mode_name (h->open_mode));
+                     "already open for %s."),
+               fh_get_name (h), type, mode_name (mode),
+               mode_name (h->open_mode));
           return NULL;
         }
       else if (h->open_mode[1] == 'e')
         {
           return NULL;
         }
       else if (h->open_mode[1] == 'e')
         {
-          msg (SE, _("Can't re-open %s as a %s for %s"),
-               handle_get_name (h), type, mode_name (mode));
+          msg (SE, _("Can't re-open %s as a %s for %s."),
+               fh_get_name (h), type, mode_name (mode));
           return NULL;
         }
     }
           return NULL;
         }
     }
@@ -229,7 +334,11 @@ fh_open (struct file_handle *h, const char *type, const char *mode)
 /* Closes file handle H, which must have been open for the
    specified TYPE and MODE of access provided to fh_open().
    Returns zero if the file is now closed, nonzero if it is still
 /* Closes file handle H, which must have been open for the
    specified TYPE and MODE of access provided to fh_open().
    Returns zero if the file is now closed, nonzero if it is still
-   open due to another reference. */
+   open due to another reference.
+
+   After fh_close() returns zero for a handle, it is unsafe to
+   reference that file handle again in any way, because its
+   storage may have been freed. */
 int
 fh_close (struct file_handle *h, const char *type, const char *mode)
 {
 int
 fh_close (struct file_handle *h, const char *type, const char *mode)
 {
@@ -240,17 +349,24 @@ fh_close (struct file_handle *h, const char *type, const char *mode)
   assert (mode != NULL);
   assert (!strcmp (mode, h->open_mode));
 
   assert (mode != NULL);
   assert (!strcmp (mode, h->open_mode));
 
-  h->open_cnt--;
-  if (h->open_cnt == 0) 
+  if (--h->open_cnt == 0) 
     {
       h->type = NULL;
       h->aux = NULL;
     {
       h->type = NULL;
       h->aux = NULL;
+      if (h->deleted)
+        free_handle (h);
+      return 0;
     }
     }
-  return h->open_cnt;
+  return 1;
 }
 
 }
 
-
-
+/* Is the file open?  BEGIN DATA...END DATA uses this to detect
+   whether the inline file is actually in use. */
+bool
+fh_is_open (const struct file_handle *handle) 
+{
+  return handle->open_cnt > 0;
+}
 
 /* Returns the identifier of file HANDLE.  If HANDLE was created
    by referring to a filename instead of a handle name, returns
 
 /* Returns the identifier of file HANDLE.  If HANDLE was created
    by referring to a filename instead of a handle name, returns
@@ -259,43 +375,82 @@ fh_close (struct file_handle *h, const char *type, const char *mode)
 
    Useful for printing error messages about use of file handles.  */
 const char *
 
    Useful for printing error messages about use of file handles.  */
 const char *
-handle_get_name (const struct file_handle *handle)
+fh_get_name (const struct file_handle *handle)
 {
 {
-  assert (handle != NULL);
   return handle->name;
 }
 
   return handle->name;
 }
 
+/* Returns the type of object that HANDLE refers to. */
+enum fh_referent
+fh_get_referent (const struct file_handle *handle) 
+{
+  return handle->referent;
+}
+
 /* Returns the name of the file associated with HANDLE. */
 const char *
 /* Returns the name of the file associated with HANDLE. */
 const char *
-handle_get_filename (const struct file_handle *handle) 
+fh_get_filename (const struct file_handle *handle) 
 {
 {
-  assert (handle != NULL);
+  assert (handle->referent == FH_REF_FILE);
   return handle->filename;
 }
 
 /* Returns the mode of HANDLE. */
   return handle->filename;
 }
 
 /* Returns the mode of HANDLE. */
-enum file_handle_mode
-handle_get_mode (const struct file_handle *handle) 
+enum fh_mode
+fh_get_mode (const struct file_handle *handle) 
 {
 {
-  assert (handle != NULL);
+  assert (handle->referent == FH_REF_FILE);
   return handle->mode;
 }
 
 /* Returns the width of a logical record on HANDLE. */
 size_t
   return handle->mode;
 }
 
 /* Returns the width of a logical record on HANDLE. */
 size_t
-handle_get_record_width (const struct file_handle *handle)
+fh_get_record_width (const struct file_handle *handle)
 {
 {
-  assert (handle != NULL);
-  return handle->length;
+  assert (handle->referent & (FH_REF_FILE | FH_REF_INLINE));
+  return handle->record_width;
 }
 
 /* Returns the number of characters per tab stop for HANDLE, or
    zero if tabs are not to be expanded.  Applicable only to
 }
 
 /* Returns the number of characters per tab stop for HANDLE, or
    zero if tabs are not to be expanded.  Applicable only to
-   MODE_TEXT files. */
+   FH_MODE_TEXT files. */
 size_t
 size_t
-handle_get_tab_width (const struct file_handle *handle) 
+fh_get_tab_width (const struct file_handle *handle) 
 {
 {
-  assert (handle != NULL);
+  assert (handle->referent & (FH_REF_FILE | FH_REF_INLINE));
   return handle->tab_width;
 }
 
   return handle->tab_width;
 }
 
+/* Returns the scratch file handle associated with HANDLE.
+   Applicable to only FH_REF_SCRATCH files. */
+struct scratch_handle *
+fh_get_scratch_handle (struct file_handle *handle) 
+{
+  assert (handle->referent == FH_REF_SCRATCH);
+  return handle->sh;
+}
+
+/* Sets SH to be the scratch file handle associated with HANDLE.
+   Applicable to only FH_REF_SCRATCH files. */
+void
+fh_set_scratch_handle (struct file_handle *handle, struct scratch_handle *sh)
+{
+  assert (handle->referent == FH_REF_SCRATCH);
+  handle->sh = sh;
+}
+
+/* Returns the current default handle. */
+struct file_handle *
+fh_get_default_handle (void) 
+{
+  return default_handle ? default_handle : fh_inline_file ();
+}
+
+/* Sets NEW_DEFAULT_HANDLE as the default handle. */
+void
+fh_set_default_handle (struct file_handle *new_default_handle) 
+{
+  assert (new_default_handle == NULL
+          || (new_default_handle->referent & (FH_REF_INLINE | FH_REF_FILE)));
+  default_handle = new_default_handle;
+}