02111-1307, USA. */
#include <config.h>
+#include "file-handle.h"
#include <assert.h>
#include <errno.h>
#include <stdlib.h>
#include "alloc.h"
-#include "avl.h"
#include "filename.h"
-#include "file-handle.h"
#include "command.h"
#include "lexer.h"
#include "getline.h"
#include "var.h"
/* (headers) */
-#undef DEBUGGING
-/*#define DEBUGGING 1*/
-#include "debug-print.h"
-
-avl_tree *files;
+/* File handle private data. */
+struct private_file_handle
+ {
+ 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. */
+ };
+
+/* Linked list of file handles. */
+struct file_handle_list
+ {
+ struct file_handle *handle;
+ struct file_handle_list *next;
+ };
+
+static struct file_handle_list *file_handles;
struct file_handle *inline_file;
-static void init_file_handle (struct file_handle * handle);
+static struct file_handle *create_file_handle (const char *handle_name,
+ const char *filename);
/* (specification)
"FILE HANDLE" (fh_):
/* (declarations) */
/* (functions) */
+static struct file_handle *
+get_handle_with_name (const char *handle_name)
+{
+ struct file_handle_list *iter;
+
+ for (iter = file_handles; iter != NULL; iter = iter->next)
+ if (!strcmp (handle_name, iter->handle->private->name))
+ return iter->handle;
+ return NULL;
+}
+
+static struct file_handle *
+get_handle_for_filename (const char *filename)
+{
+ struct file_identity *identity;
+ struct file_handle_list *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))
+ {
+ fn_free_identity (identity);
+ return iter->handle;
+ }
+ 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;
+
+ return NULL;
+}
+
int
cmd_file_handle (void)
{
char handle_name[9];
- char *handle_name_p = handle_name;
struct cmd_file_handle cmd;
- struct file_handle *fp;
+ struct file_handle *handle;
- lex_get ();
if (!lex_force_id ())
return CMD_FAILURE;
strcpy (handle_name, tokid);
- fp = NULL;
- if (files)
- fp = avl_find (files, &handle_name_p);
- if (fp)
+ handle = get_handle_with_name (handle_name);
+ if (handle != NULL)
{
- msg (SE, _("File handle %s had already been defined to refer to "
- "file %s. It is not possible to redefine a file "
- "handle within a session."),
- tokid, fp->fn);
+ msg (SE, _("File handle %s already refers to "
+ "file %s. File handle cannot be redefined within a "
+ "session."),
+ tokid, handle->private->filename);
return CMD_FAILURE;
}
goto lossage;
}
- fp = xmalloc (sizeof *fp);
- init_file_handle (fp);
-
- switch (cmd.recform)
+ handle = create_file_handle (handle_name, cmd.s_name);
+ switch (cmd.mode)
{
- case FH_FIXED:
+ case FH_CHARACTER:
+ handle->private->mode = MODE_TEXT;
+ break;
+ case FH_IMAGE:
+ handle->private->mode = MODE_BINARY;
if (cmd.n_lrecl == NOT_LONG)
{
- msg (SE, _("Fixed length records were specified on /RECFORM, but "
- "record length was not specified on /LRECL. 80-character "
- "records will be assumed."));
- cmd.n_lrecl = 80;
+ 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;
}
else if (cmd.n_lrecl < 1)
{
msg (SE, _("Record length (%ld) must be at least one byte. "
- "80-character records will be assumed."), cmd.n_lrecl);
- cmd.n_lrecl = 80;
+ "1-character records will be assumed."), cmd.n_lrecl);
+ handle->private->length = 1;
}
- fp->recform = FH_RF_FIXED;
- fp->lrecl = cmd.n_lrecl;
- break;
- case FH_VARIABLE:
- fp->recform = FH_RF_VARIABLE;
- break;
- case FH_SPANNED:
- msg (SE, _("/RECFORM SPANNED is not implemented, as the author doesn't "
- "know what it is supposed to do. Send the author a note."));
+ else
+ handle->private->length = cmd.n_lrecl;
break;
default:
assert (0);
}
- switch (cmd.mode)
- {
- case FH_CHARACTER:
- fp->mode = FH_MD_CHARACTER;
- break;
- case FH_IMAGE:
- msg (SE, _("/MODE IMAGE is not implemented, as the author doesn't know "
- "what it is supposed to do. Send the author a note."));
- break;
- case FH_BINARY:
- fp->mode = FH_MD_BINARY;
- break;
- case FH_MULTIPUNCH:
- msg (SE, _("/MODE MULTIPUNCH is not implemented. If you care, "
- "complain."));
- break;
- case FH__360:
- msg (SE, _("/MODE 360 is not implemented. If you care, complain."));
- break;
- default:
- assert (0);
- }
-
- fp->name = xstrdup (handle_name);
- fp->norm_fn = fn_normalize (cmd.s_name);
- fp->where.filename = fp->fn = cmd.s_name;
- avl_force_insert (files, fp);
-
return CMD_SUCCESS;
lossage:
\f
/* File handle functions. */
-/* Sets up some fields in H; caller should fill in
- H->{NAME,NORM_FN,FN}. */
-static void
-init_file_handle (struct file_handle *h)
-{
- h->recform = FH_RF_VARIABLE;
- h->mode = FH_MD_CHARACTER;
- h->ext = NULL;
- h->class = NULL;
-}
-
-/* Returns the handle corresponding to FILENAME. Creates the handle
- if no handle exists for that file. All filenames are normalized
- first, so different filenames referring to the same file will
- return the same file handle. */
-struct file_handle *
-fh_get_handle_by_filename (const char *filename)
+/* 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)
{
- struct file_handle f, *fp;
- char *fn;
- char *name;
- int len;
-
- /* Get filename. */
- fn = fn_normalize (filename);
- len = strlen (fn);
-
- /* Create handle name with invalid identifier character to prevent
- conflicts with handles created with FILE HANDLE. */
- name = xmalloc (len + 2);
- name[0] = '*';
- strcpy (&name[1], fn);
-
- f.name = name;
- fp = avl_find (files, &f);
- if (!fp)
- {
- fp = xmalloc (sizeof *fp);
- init_file_handle (fp);
- fp->name = name;
- fp->norm_fn = fn;
- fp->where.filename = fp->fn = xstrdup (filename);
- avl_force_insert (files, fp);
- }
- else
- {
- free (fn);
- free (name);
- }
- return fp;
-}
-
-/* Returns the handle with identifier NAME, if it exists; otherwise
- reports error to user and returns NULL. */
-struct file_handle *
-fh_get_handle_by_name (const char name[9])
-{
- struct file_handle f, *fp;
- f.name = (char *) name;
- fp = avl_find (files, &f);
-
- if (!fp)
- msg (SE, _("File handle `%s' has not been previously declared on "
- "FILE HANDLE."), name);
- return fp;
-}
-
-/* Returns the identifier of file HANDLE. If HANDLE was created by
- referring to a filename (i.e., DATA LIST FILE='yyy' instead of FILE
- HANDLE XXX='yyy'), returns the filename, enclosed in double quotes.
- Return value is in a static buffer.
-
- Useful for printing error messages about use of file handles. */
-const char *
-fh_handle_name (struct file_handle *h)
-{
- static char *buf = NULL;
-
- if (buf)
- {
- free (buf);
- buf = NULL;
- }
- if (!h)
- return NULL;
-
- if (h->name[0] == '*')
- {
- int len = strlen (h->fn);
+ 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;
- buf = xmalloc (len + 3);
- strcpy (&buf[1], h->fn);
- buf[0] = buf[len + 1] = '"';
- buf[len + 2] = 0;
- return buf;
- }
- return h->name;
+ return handle;
}
/* Closes the stdio FILE associated with handle H. Frees internal
if (h == NULL)
return;
- debug_printf (("Closing %s%s.\n", fh_handle_name (h),
- h->class == NULL ? " (already closed)" : ""));
-
- if (h->class)
+ if (h->class != NULL)
h->class->close (h);
h->class = NULL;
h->ext = NULL;
}
-/* Compares names of file handles A and B. */
-static int
-cmp_file_handle (const void *a, const void *b, void *foo unused)
-{
- return strcmp (((struct file_handle *) a)->name,
- ((struct file_handle *) b)->name);
-}
-
-/* Initialize the AVL tree of file handles; inserts the "inline file"
+/* Initialize the hash of file handles; inserts the "inline file"
inline_file. */
void
fh_init_files (void)
{
- /* Create AVL tree. */
- files = avl_create (NULL, cmp_file_handle, NULL);
-
- /* Insert inline file. */
- inline_file = xmalloc (sizeof *inline_file);
- init_file_handle (inline_file);
- inline_file->name = "INLINE";
- inline_file->where.filename
- = inline_file->fn = inline_file->norm_fn = (char *) _("<Inline File>");
- inline_file->where.line_number = 0;
- avl_force_insert (files, inline_file);
+ if (inline_file == NULL)
+ {
+ inline_file = create_file_handle ("INLINE", _("<Inline File>"));
+ inline_file->private->length = 80;
+ }
}
/* Parses a file handle name, which may be a filename as a string or
{
struct file_handle *handle;
- if (token == T_ID)
- handle = fh_get_handle_by_name (tokid);
- else if (token == T_STRING)
- handle = fh_get_handle_by_filename (ds_value (&tokstr));
- else
+ if (token != T_ID && token != T_STRING)
{
- lex_error (_("expecting a file name or handle"));
+ lex_error (_("expecting a file name or handle name"));
return NULL;
}
- if (!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_value (&tokstr));
+ if (handle == NULL)
+ {
+ char *filename = ds_value (&tokstr);
+ char *handle_name = xmalloc (strlen (filename) + 3);
+ sprintf (handle_name, "\"%s\"", filename);
+ handle = create_file_handle (handle_name, filename);
+ free (handle_name);
+ }
+
lex_get ();
return handle;
}
-/* Returns the (normalized) filename associated with file handle H. */
-char *
-fh_handle_filename (struct file_handle * h)
+/* 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)
+{
+ return handle->private->name;
+}
+
+/* Returns the name of the file associated with HANDLE. */
+const char *
+handle_get_filename (const struct file_handle *handle)
+{
+ return handle->private->filename;
+}
+
+/* Returns the mode of HANDLE. */
+enum file_handle_mode
+handle_get_mode (const struct file_handle *handle)
{
- return h->norm_fn;
+ assert (handle != NULL);
+ return handle->private->mode;
}
-/* Returns the width of a logical record on file handle H. */
+/* Returns the width of a logical record on HANDLE. */
size_t
-fh_record_width (struct file_handle *h)
+handle_get_record_width (const struct file_handle *handle)
{
- if (h == inline_file)
- return 80;
- else if (h->recform == FH_RF_FIXED)
- return h->lrecl;
- else
- return 1024;
+ assert (handle != NULL);
+ return handle->private->length;
}
/*