Implemented long variable names a la spss V12.
[pspp-builds.git] / src / file-handle.q
index dc1e9383c60abfa16260cae8d38694ef7f5ff908..3a688a7f5cf86f9d79f36ee2f7859d9acc05fa87 100644 (file)
@@ -19,7 +19,7 @@
 
 #include <config.h>
 #include "file-handle.h"
-#include <assert.h>
+#include "error.h"
 #include <errno.h>
 #include <stdlib.h>
 #include "alloc.h"
 #include "error.h"
 #include "magic.h"
 #include "var.h"
+#include "linked-list.h"
+
 /* (headers) */
 
-/* File handle private data. */
-struct private_file_handle 
+/* 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. */
 
-/* Linked list of file handles. */
-struct file_handle_list 
-  {
-    struct file_handle *handle;
-    struct file_handle_list *next;
+    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. */
   };
 
-static struct file_handle_list *file_handles;
-struct file_handle *inline_file;
+static struct file_handle *file_handles;
 
 static struct file_handle *create_file_handle (const char *handle_name,
                                                const char *filename);
@@ -59,9 +60,9 @@ static struct file_handle *create_file_handle (const char *handle_name,
 /* (specification)
    "FILE HANDLE" (fh_):
      name=string;
-     recform=recform:fixed/!variable/spanned;
      lrecl=integer;
-     mode=mode:!character/image/binary/multipunch/_360.
+     tabwidth=integer "x>=0" "%s must be nonnegative";
+     mode=mode:!character/image.
 */
 /* (declarations) */
 /* (functions) */
@@ -69,11 +70,11 @@ static struct file_handle *create_file_handle (const char *handle_name,
 static struct file_handle *
 get_handle_with_name (const char *handle_name) 
 {
-  struct file_handle_list *iter;
+  struct file_handle *iter;
 
   for (iter = file_handles; iter != NULL; iter = iter->next)
-    if (!strcmp (handle_name, iter->handle->private->name))
-      return iter->handle;
+    if (!strcmp (handle_name, iter->name))
+      return iter;
   return NULL;
 }
 
@@ -81,27 +82,26 @@ static struct file_handle *
 get_handle_for_filename (const char *filename)
 {
   struct file_identity *identity;
-  struct file_handle_list *iter;
+  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->handle->private->identity != NULL
-            && !fn_compare_file_identities (identity,
-                                         iter->handle->private->identity)) 
+        if (iter->identity != NULL
+            && !fn_compare_file_identities (identity, iter->identity))
           {
             fn_free_identity (identity);
-            return iter->handle
+            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->handle->private->filename))
-      return iter->handle
+    if (!strcmp (filename, iter->filename))
+      return iter; 
 
   return NULL;
 }
@@ -124,7 +124,7 @@ cmd_file_handle (void)
       msg (SE, _("File handle %s already refers to "
                 "file %s.  File handle cannot be redefined within a "
                  "session."),
-          tokid, handle->private->filename);
+          tokid, handle->filename);
       return CMD_FAILURE;
     }
 
@@ -152,25 +152,29 @@ cmd_file_handle (void)
   switch (cmd.mode)
     {
     case FH_CHARACTER:
-      handle->private->mode = MODE_TEXT;
+      handle->mode = MODE_TEXT;
+      if (cmd.sbc_tabwidth)
+        handle->tab_width = cmd.n_tabwidth[0];
+      else
+        handle->tab_width = 4;
       break;
     case FH_IMAGE:
-      handle->private->mode = MODE_BINARY;
-      if (cmd.n_lrecl == NOT_LONG)
+      handle->mode = 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->private->length = 1024;
+          handle->length = 1024;
        }
-      else if (cmd.n_lrecl < 1)
+      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->private->length = 1;
+          handle->length = 1;
        }
       else
-        handle->private->length = cmd.n_lrecl;
+        handle->length = cmd.n_lrecl[0];
       break;
     default:
       assert (0);
@@ -192,62 +196,142 @@ static struct file_handle *
 create_file_handle (const char *handle_name, const char *filename)
 {
   struct file_handle *handle;
-  struct file_handle_list *list;
 
   /* Create and initialize file handle. */
   handle = xmalloc (sizeof *handle);
-  handle->private = xmalloc (sizeof *handle->private);
-  handle->private->name = xstrdup (handle_name);
-  handle->private->filename = xstrdup (filename);
-  handle->private->identity = fn_get_identity (filename);
-  handle->private->where.filename = handle->private->filename;
-  handle->private->where.line_number = 0;
-  handle->private->mode = MODE_TEXT;
-  handle->private->length = 1024;
-  handle->ext = NULL;
-  handle->class = NULL;
-
-  /* Add file handle to global list. */
-  list = xmalloc (sizeof *list);
-  list->handle = handle;
-  list->next = file_handles;
-  file_handles = list;
+  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;
 
   return handle;
 }
 
-/* Closes the stdio FILE associated with handle H.  Frees internal
-   buffers associated with that file.  Does *not* destroy the file
-   handle H.  (File handles are permanent during a session.)  */
-void
-fh_close_handle (struct file_handle *h)
+static void
+destroy_file_handle(void *fh_, void *aux UNUSED)
 {
-  if (h == NULL)
-    return;
+  struct file_handle *fh = fh_;
+  free (fh->name);
+  free (fh->filename);
+  fn_free_identity (fh->identity);
+  free (fh);
+}
+
+static const char *
+mode_name (const char *mode) 
+{
+  assert (mode != NULL);
+  assert (mode[0] == 'r' || mode[0] == 'w');
+
+  return mode[0] == 'r' ? "reading" : "writing";
+}
+
+
+/* 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) 
+{
+  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');
+
+  if (h->open_cnt != 0) 
+    {
+      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);
+          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));
+          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));
+          return NULL;
+        }
+    }
+  else 
+    {
+      h->type = type;
+      strcpy (h->open_mode, mode);
+      assert (h->aux == NULL);
+    }
+  h->open_cnt++;
 
-  if (h->class != NULL)
-    h->class->close (h);
-  h->class = NULL;
-  h->ext = NULL;
+  return &h->aux;
 }
 
-/* Initialize the hash of file handles; inserts the "inline file"
-   inline_file. */
-void
-fh_init_files (void)
+/* 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)
 {
-  if (inline_file == NULL) 
+  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) 
     {
-      inline_file = create_file_handle ("INLINE", _("<Inline File>"));
-      inline_file->private->length = 80; 
+      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_file_handle (void)
+fh_parse (void)
 {
   struct file_handle *handle;
 
@@ -262,18 +346,20 @@ fh_parse_file_handle (void)
   if (token == T_ID) 
     handle = get_handle_with_name (tokid);
   if (handle == NULL)
-    handle = get_handle_for_filename (ds_value (&tokstr));
+    handle = get_handle_for_filename (ds_c_str (&tokstr));
   if (handle == NULL) 
     {
-      char *filename = ds_value (&tokstr);
+      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;
 }
 
@@ -286,14 +372,16 @@ fh_parse_file_handle (void)
 const char *
 handle_get_name (const struct file_handle *handle)
 {
-  return handle->private->name;
+  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) 
 {
-  return handle->private->filename;
+  assert (handle != NULL);
+  return handle->filename;
 }
 
 /* Returns the mode of HANDLE. */
@@ -301,17 +389,46 @@ enum file_handle_mode
 handle_get_mode (const struct file_handle *handle) 
 {
   assert (handle != NULL);
-  return handle->private->mode;
+  return handle->mode;
 }
 
-/* Returns the width of a logical record on HANDLE. */
+/* 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->private->length;
+  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