X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Ffile-handle.q;h=99e5d0d7ce8e0d5e8e21f69a00a15ffbb782a50d;hb=e582516e80e5a04f10a651515a35b616911cf4d6;hp=84aff6c98fe772d605f77640fb1bb7eb4661ca32;hpb=fcb9e49b2a2d57af7c001ae5d2eda9ac443ba36b;p=pspp-builds.git diff --git a/src/file-handle.q b/src/file-handle.q index 84aff6c9..99e5d0d7 100644 --- a/src/file-handle.q +++ b/src/file-handle.q @@ -18,61 +18,113 @@ 02111-1307, USA. */ #include -#include +#include "file-handle.h" +#include "error.h" #include #include #include "alloc.h" -#include "avl.h" #include "filename.h" -#include "file-handle.h" #include "command.h" #include "lexer.h" #include "getline.h" #include "error.h" #include "magic.h" #include "var.h" -/* (headers) */ +#include "linked-list.h" -#include "debug-print.h" - -avl_tree *files; -struct file_handle *inline_file; +/* (headers) */ -static void init_file_handle (struct file_handle * 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. */ + + 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 *file_handles; + +static struct file_handle *create_file_handle (const char *handle_name, + const char *filename); /* (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) */ +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_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->filename); return CMD_FAILURE; } @@ -96,67 +148,38 @@ cmd_file_handle (void) 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: - if (cmd.n_lrecl == NOT_LONG) + case FH_CHARACTER: + 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->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. 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->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. " - "80-character records will be assumed."), cmd.n_lrecl); - cmd.n_lrecl = 80; + "1-character records will be assumed."), cmd.n_lrecl); + handle->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->length = cmd.n_lrecl[0]; 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: @@ -166,193 +189,245 @@ cmd_file_handle (void) /* 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) +/* 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) { - h->recform = FH_RF_VARIABLE; - h->mode = FH_MD_CHARACTER; - h->ext = NULL; - h->class = NULL; + 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; + + return handle; } -/* 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) +static void +destroy_file_handle(void *fh_, void *aux UNUSED) { - 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; + struct file_handle *fh = fh_; + free (fh->name); + free (fh->filename); + fn_free_identity (fh->identity); + free (fh); } -/* 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]) +static const char * +mode_name (const char *mode) { - 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; + assert (mode != NULL); + assert (mode[0] == 'r' || mode[0] == 'w'); + + return mode[0] == 'r' ? "reading" : "writing"; } -/* 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; +/* Tries to open FILE with the given TYPE and MODE. - if (buf) - { - free (buf); - buf = NULL; - } - if (!h) - return NULL; + 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 (h->name[0] == '*') - { - int len = strlen (h->fn); + 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. - buf = xmalloc (len + 3); - strcpy (&buf[1], h->fn); - buf[0] = buf[len + 1] = '"'; - buf[len + 2] = 0; - return buf; - } - return h->name; -} + 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. -/* 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) + 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) { - if (h == NULL) - return; - - debug_printf (("Closing %s%s.\n", fh_handle_name (h), - h->class == NULL ? " (already closed)" : "")); + 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) - h->class->close (h); - h->class = NULL; - h->ext = NULL; + return &h->aux; } -/* Compares names of file handles A and B. */ -static int -cmp_file_handle (const void *a, const void *b, void *foo unused) +/* 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) { - return strcmp (((struct file_handle *) a)->name, - ((struct file_handle *) b)->name); + 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; } -/* Initialize the AVL tree 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->where.line_number = 0; - avl_force_insert (files, inline_file); -} + +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; - 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_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 (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) +{ + 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) { - return h->norm_fn; + assert (handle != NULL); + return handle->mode; } -/* Returns the width of a logical record on file handle H. */ +/* Returns the width of a logical record on HANDLE. Applicable + only to MODE_BINARY files. */ 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->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) +{ + assert(handle_list); + + ll_destroy(handle_list); + handle_list = 0; +} + + /* Local variables: mode: c