#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. */
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. */
+ const 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);
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;
}
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;
}
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;
}
switch (cmd.mode)
{
case FH_CHARACTER:
- handle->private->mode = MODE_TEXT;
+ handle->mode = MODE_TEXT;
if (cmd.sbc_tabwidth)
- handle->private->tab_width = cmd.n_tabwidth;
+ handle->tab_width = cmd.n_tabwidth[0];
else
- handle->private->tab_width = 4;
+ 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);
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->private->tab_width = 4;
- 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;
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;
}
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. */
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. Applicable
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
handle_get_tab_width (const struct file_handle *handle)
{
assert (handle != NULL);
- return handle->private->tab_width;
+ return handle->tab_width;
}
+
+void
+fh_init(void)
+{
+ handle_list = ll_create(destroy_file_handle,0);
+}
+
+void
+fh_done(void)
+{
+ assert(handle_list);
+
+ ll_destroy(handle_list);
+ handle_list = 0;
+}
+
+
/*
Local variables:
mode: c