Add scratch file handles.
[pspp-builds.git] / src / file-handle.q
index 3a688a7f5cf86f9d79f36ee2f7859d9acc05fa87..c13be76beda44a320979200adb2418a6f04a6044 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>.
 
    This program is free software; you can redistribute it and/or
@@ -14,8 +14,8 @@
 
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
-   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
-   02111-1307, USA. */
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA. */
 
 #include <config.h>
 #include "file-handle.h"
 #include "filename.h"
 #include "command.h"
 #include "lexer.h"
-#include "getline.h"
+#include "getl.h"
 #include "error.h"
 #include "magic.h"
+#include "str.h"
 #include "var.h"
 #include "linked-list.h"
+#include "file-handle-def.h"
 
-/* (headers) */
-
-/* File handle. */
-struct file_handle 
-  {
-    struct file_handle *next;   /* Next in global list. */
-    char *name;                 /* File handle identifier. */
-    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. */
-    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. */
-  };
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
 
-static struct file_handle *file_handles;
+/* (headers) */
 
-static struct file_handle *create_file_handle (const char *handle_name,
-                                               const char *filename);
 
 /* (specification)
    "FILE HANDLE" (fh_):
      name=string;
      lrecl=integer;
      tabwidth=integer "x>=0" "%s must be nonnegative";
-     mode=mode:!character/image.
+     mode=mode:!character/image/scratch.
 */
 /* (declarations) */
 /* (functions) */
 
-static struct file_handle *
-get_handle_with_name (const char *handle_name) 
-{
-  struct file_handle *iter;
-
-  for (iter = file_handles; iter != NULL; iter = iter->next)
-    if (!strcmp (handle_name, iter->name))
-      return iter;
-  return NULL;
-}
-
-static struct file_handle *
-get_handle_for_filename (const char *filename)
-{
-  struct file_identity *identity;
-  struct file_handle *iter;
-      
-  /* First check for a file with the same identity. */
-  identity = fn_get_identity (filename);
-  if (identity != NULL) 
-    {
-      for (iter = file_handles; iter != NULL; iter = iter->next)
-        if (iter->identity != NULL
-            && !fn_compare_file_identities (identity, iter->identity))
-          {
-            fn_free_identity (identity);
-            return iter; 
-          }
-      fn_free_identity (identity);
-    }
-
-  /* Then check for a file with the same name. */
-  for (iter = file_handles; iter != NULL; iter = iter->next)
-    if (!strcmp (filename, iter->filename))
-      return iter; 
-
-  return NULL;
-}
-
 int
 cmd_file_handle (void)
 {
-  char handle_name[9];
+  char handle_name[LONG_NAME_LEN + 1];
+  struct fh_properties properties = *fh_default_properties ();
 
   struct cmd_file_handle cmd;
   struct file_handle *handle;
 
   if (!lex_force_id ())
     return CMD_FAILURE;
-  strcpy (handle_name, tokid);
+  str_copy_trunc (handle_name, sizeof handle_name, tokid);
 
-  handle = get_handle_with_name (handle_name);
+  handle = fh_from_name (handle_name);
   if (handle != NULL)
     {
-      msg (SE, _("File handle %s already refers to "
-                "file %s.  File handle cannot be redefined within a "
-                 "session."),
-          tokid, handle->filename);
+      msg (SE, _("File handle %s is already defined.  "
+                 "Use CLOSE FILE HANDLE before redefining a file handle."),
+          handle_name);
       return CMD_FAILURE;
     }
 
@@ -135,300 +79,136 @@ cmd_file_handle (void)
   if (!parse_file_handle (&cmd))
     return CMD_FAILURE;
 
-  if (token != '.')
-    {
-      lex_error (_("expecting end of command"));
-      goto lossage;
-    }
+  if (lex_end_of_command () != CMD_SUCCESS)
+    goto lossage;
 
-  if (cmd.s_name == NULL)
+  if (cmd.s_name == NULL && cmd.mode != FH_SCRATCH)
     {
-      msg (SE, _("The FILE HANDLE required subcommand NAME "
-                "is not present."));
+      lex_sbc_missing ("NAME");
       goto lossage;
     }
 
-  handle = create_file_handle (handle_name, cmd.s_name);
   switch (cmd.mode)
     {
     case FH_CHARACTER:
-      handle->mode = MODE_TEXT;
+      properties.mode = FH_MODE_TEXT;
       if (cmd.sbc_tabwidth)
-        handle->tab_width = cmd.n_tabwidth[0];
-      else
-        handle->tab_width = 4;
+        properties.tab_width = cmd.n_tabwidth[0];
       break;
     case FH_IMAGE:
-      handle->mode = MODE_BINARY;
+      properties.mode = FH_MODE_BINARY;
       if (cmd.n_lrecl[0] == NOT_LONG)
-       {
-         msg (SE, _("Fixed-length records were specified on /RECFORM, but "
-                     "record length was not specified on /LRECL.  "
-                     "Assuming 1024-character records."));
-          handle->length = 1024;
-       }
+        msg (SE, _("Fixed-length records were specified on /RECFORM, but "
+                   "record length was not specified on /LRECL.  "
+                   "Assuming %d-character records."),
+             properties.record_width);
       else if (cmd.n_lrecl[0] < 1)
-       {
-         msg (SE, _("Record length (%ld) must be at least one byte.  "
-                    "1-character records will be assumed."), cmd.n_lrecl);
-          handle->length = 1;
-       }
+        msg (SE, _("Record length (%ld) must be at least one byte.  "
+                   "Assuming %d-character records."),
+             cmd.n_lrecl[0], properties.record_width);
       else
-        handle->length = cmd.n_lrecl[0];
+        properties.record_width = cmd.n_lrecl[0];
       break;
     default:
       assert (0);
     }
 
+  if (cmd.mode != FH_SCRATCH)
+    fh_create_file (handle_name, cmd.s_name, &properties);
+  else
+    fh_create_scratch (handle_name);
+
+  free_file_handle (&cmd);
   return CMD_SUCCESS;
 
  lossage:
   free_file_handle (&cmd);
   return CMD_FAILURE;
 }
-\f
-/* File handle functions. */
 
-/* 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. */
-static struct file_handle *
-create_file_handle (const char *handle_name, const char *filename)
+int
+cmd_close_file_handle (void) 
 {
   struct file_handle *handle;
 
-  /* Create and initialize file handle. */
-  handle = xmalloc (sizeof *handle);
-  handle->next = file_handles;
-  handle->name = xstrdup (handle_name);
-  handle->filename = xstrdup (filename);
-  handle->identity = fn_get_identity (filename);
-  handle->where.filename = handle->filename;
-  handle->where.line_number = 0;
-  handle->mode = MODE_TEXT;
-  handle->length = 1024;
-  handle->tab_width = 4;
-  handle->open_cnt = 0;
-  handle->type = NULL;
-  handle->aux = NULL;
-  file_handles = handle;
+  if (!lex_force_id ())
+    return CMD_FAILURE;
+  handle = fh_from_name (tokid);
+  if (handle == NULL)
+    return CMD_FAILURE;
 
-  return handle;
-}
+  fh_free (handle);
 
-static void
-destroy_file_handle(void *fh_, void *aux UNUSED)
-{
-  struct file_handle *fh = fh_;
-  free (fh->name);
-  free (fh->filename);
-  fn_free_identity (fh->identity);
-  free (fh);
+  return CMD_SUCCESS;
 }
 
+/* Returns the name for REFERENT. */
 static const char *
-mode_name (const char *mode
+referent_name (enum fh_referent referent
 {
-  assert (mode != NULL);
-  assert (mode[0] == 'r' || mode[0] == 'w');
-
-  return mode[0] == 'r' ? "reading" : "writing";
+  switch (referent) 
+    {
+    case FH_REF_FILE:
+      return _("file");
+    case FH_REF_INLINE:
+      return _("inline file");
+    case FH_REF_SCRATCH:
+      return _("scratch file");
+    default:
+      abort ();
+    }
 }
 
-
-/* Tries to open FILE with the given TYPE and MODE.
-
-   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.
-
-   MODE combines the read or write mode with the sharing mode.
-   The first character is 'r' for read, 'w' for write.  The
-   second character is 's' to permit sharing, 'e' to require
-   exclusive access.
-
-   Returns the address of a void * that the caller can use for
-   data specific to the file handle if successful, or a null
-   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. */
-void **
-fh_open (struct file_handle *h, const char *type, const char *mode) 
+/* Parses a file handle name, which may be a filename as a string
+   or a file handle name as an identifier.  The allowed types of
+   file handle are restricted to those in REFERENT_MASK.  Returns
+   the file handle when successful, a null pointer on failure. */
+struct file_handle *
+fh_parse (enum fh_referent referent_mask)
 {
-  assert (h != NULL);
-  assert (type != NULL);
-  assert (mode != NULL);
-  assert (mode[0] == 'r' || mode[0] == 'w');
-  assert (mode[1] == 's' || mode[1] == 'e');
-  assert (mode[2] == '\0');
+  struct file_handle *handle;
 
-  if (h->open_cnt != 0) 
+  if (lex_match_id ("INLINE")) 
+    handle = fh_inline_file ();
+  else 
     {
-      if (strcmp (h->type, type)) 
+      if (token != T_ID && token != T_STRING)
         {
-          msg (SE, _("Can't open %s as a %s because it is "
-                     "already open as a %s"),
-               handle_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 "
-                     "already open for %s"),
-               handle_get_name (h), type,
-               mode_name (mode), mode_name (h->open_mode));
+          lex_error (_("expecting a file name or handle name"));
           return NULL;
         }
-      else if (h->open_mode[1] == 'e')
+
+      handle = NULL;
+      if (token == T_ID) 
+        handle = fh_from_name (tokid);
+      if (handle == NULL) 
+        handle = fh_from_filename (ds_c_str (&tokstr)); 
+      if (handle == NULL)
         {
-          msg (SE, _("Can't re-open %s as a %s for %s"),
-               handle_get_name (h), type, mode_name (mode));
-          return NULL;
+          if (token != T_ID || tokid[0] != '#' || get_syntax () != ENHANCED) 
+            {
+              char *filename = ds_c_str (&tokstr);
+              char *handle_name = xasprintf ("\"%s\"", filename);
+              handle = fh_create_file (handle_name, filename,
+                                       fh_default_properties ());
+              free (handle_name);
+            }
+          else
+            handle = fh_create_scratch (tokid);
         }
+      lex_get ();
     }
-  else 
-    {
-      h->type = type;
-      strcpy (h->open_mode, mode);
-      assert (h->aux == NULL);
-    }
-  h->open_cnt++;
-
-  return &h->aux;
-}
-
-/* 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. */
-int
-fh_close (struct file_handle *h, const char *type, const char *mode)
-{
-  assert (h != NULL);
-  assert (h->open_cnt > 0);
-  assert (type != NULL);
-  assert (!strcmp (type, h->type));
-  assert (mode != NULL);
-  assert (!strcmp (mode, h->open_mode));
-
-  h->open_cnt--;
-  if (h->open_cnt == 0) 
-    {
-      h->type = NULL;
-      h->aux = NULL;
-    }
-  return h->open_cnt;
-}
-
-
-static struct linked_list *handle_list;
 
-
-/* Parses a file handle name, which may be a filename as a string or
-   a file handle name as an identifier.  Returns the file handle or
-   NULL on failure. */
-struct file_handle *
-fh_parse (void)
-{
-  struct file_handle *handle;
-
-  if (token != T_ID && token != T_STRING)
+  if (!(fh_get_referent (handle) & referent_mask)) 
     {
-      lex_error (_("expecting a file name or handle name"));
+      msg (SE, _("Handle for %s not allowed here."),
+           referent_name (fh_get_referent (handle)));
       return NULL;
     }
 
-  /* Check for named handles first, then go by filename. */
-  handle = NULL;
-  if (token == T_ID) 
-    handle = get_handle_with_name (tokid);
-  if (handle == NULL)
-    handle = get_handle_for_filename (ds_c_str (&tokstr));
-  if (handle == NULL) 
-    {
-      char *filename = ds_c_str (&tokstr);
-      char *handle_name = xmalloc (strlen (filename) + 3);
-      sprintf (handle_name, "\"%s\"", filename);
-      handle = create_file_handle (handle_name, filename);
-      ll_push_front(handle_list, handle);
-      free (handle_name);
-    }
-
-  lex_get ();
-
-
   return handle;
 }
 
-/* Returns the identifier of file HANDLE.  If HANDLE was created
-   by referring to a filename instead of a handle name, returns
-   the filename, enclosed in double quotes.  Return value is
-   owned by the file handle. 
-
-   Useful for printing error messages about use of file handles.  */
-const char *
-handle_get_name (const struct file_handle *handle)
-{
-  assert (handle != NULL);
-  return handle->name;
-}
-
-/* Returns the name of the file associated with HANDLE. */
-const char *
-handle_get_filename (const struct file_handle *handle) 
-{
-  assert (handle != NULL);
-  return handle->filename;
-}
-
-/* Returns the mode of HANDLE. */
-enum file_handle_mode
-handle_get_mode (const struct file_handle *handle) 
-{
-  assert (handle != NULL);
-  return handle->mode;
-}
-
-/* Returns the width of a logical record on HANDLE.  Applicable
-   only to MODE_BINARY files.  */
-size_t
-handle_get_record_width (const struct file_handle *handle)
-{
-  assert (handle != NULL);
-  return handle->length;
-}
-
-/* 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. */
-size_t
-handle_get_tab_width (const struct file_handle *handle) 
-{
-  assert (handle != NULL);
-  return handle->tab_width;
-}
-
-
-void 
-fh_init(void)
-{
-  handle_list = ll_create(destroy_file_handle,0);
-}
-
-void 
-fh_done(void)
-{
-  if ( handle_list )  
-  {
-    ll_destroy(handle_list);
-    handle_list = 0;
-  }
-}
-
-
 /*
    Local variables:
    mode: c