+ 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++;