31 |
#include "magic.h" |
#include "magic.h" |
32 |
#include "var.h" |
#include "var.h" |
33 |
#include "linked-list.h" |
#include "linked-list.h" |
34 |
|
#include "file-handle-def.h" |
35 |
|
|
36 |
#include "gettext.h" |
#include "gettext.h" |
37 |
#define _(msgid) gettext (msgid) |
#define _(msgid) gettext (msgid) |
38 |
|
|
39 |
/* (headers) */ |
/* (headers) */ |
40 |
|
|
|
/* 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. */ |
|
|
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); |
|
41 |
|
|
42 |
/* (specification) |
/* (specification) |
43 |
"FILE HANDLE" (fh_): |
"FILE HANDLE" (fh_): |
49 |
/* (declarations) */ |
/* (declarations) */ |
50 |
/* (functions) */ |
/* (functions) */ |
51 |
|
|
|
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 (!strcasecmp (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; |
|
|
} |
|
52 |
|
|
53 |
int |
int |
54 |
cmd_file_handle (void) |
cmd_file_handle (void) |
67 |
{ |
{ |
68 |
msg (SE, _("File handle %s already refers to file %s. " |
msg (SE, _("File handle %s already refers to file %s. " |
69 |
"File handles cannot be redefined within a session."), |
"File handles cannot be redefined within a session."), |
70 |
handle_name, handle->filename); |
handle_name, handle_get_filename(handle)); |
71 |
return CMD_FAILURE; |
return CMD_FAILURE; |
72 |
} |
} |
73 |
|
|
91 |
goto lossage; |
goto lossage; |
92 |
} |
} |
93 |
|
|
94 |
handle = create_file_handle (handle_name, cmd.s_name); |
|
95 |
|
enum file_handle_mode mode = MODE_TEXT; |
96 |
|
size_t length = 1024; |
97 |
|
size_t tab_width = 4; |
98 |
|
|
99 |
|
|
100 |
switch (cmd.mode) |
switch (cmd.mode) |
101 |
{ |
{ |
102 |
case FH_CHARACTER: |
case FH_CHARACTER: |
103 |
handle->mode = MODE_TEXT; |
mode = MODE_TEXT; |
104 |
if (cmd.sbc_tabwidth) |
if (cmd.sbc_tabwidth) |
105 |
handle->tab_width = cmd.n_tabwidth[0]; |
tab_width = cmd.n_tabwidth[0]; |
106 |
else |
else |
107 |
handle->tab_width = 4; |
tab_width = 4; |
108 |
break; |
break; |
109 |
case FH_IMAGE: |
case FH_IMAGE: |
110 |
handle->mode = MODE_BINARY; |
mode = MODE_BINARY; |
111 |
if (cmd.n_lrecl[0] == NOT_LONG) |
if (cmd.n_lrecl[0] == NOT_LONG) |
112 |
{ |
{ |
113 |
msg (SE, _("Fixed-length records were specified on /RECFORM, but " |
msg (SE, _("Fixed-length records were specified on /RECFORM, but " |
114 |
"record length was not specified on /LRECL. " |
"record length was not specified on /LRECL. " |
115 |
"Assuming 1024-character records.")); |
"Assuming 1024-character records.")); |
116 |
handle->length = 1024; |
length = 1024; |
117 |
} |
} |
118 |
else if (cmd.n_lrecl[0] < 1) |
else if (cmd.n_lrecl[0] < 1) |
119 |
{ |
{ |
120 |
msg (SE, _("Record length (%ld) must be at least one byte. " |
msg (SE, _("Record length (%ld) must be at least one byte. " |
121 |
"1-character records will be assumed."), cmd.n_lrecl[0]); |
"1-character records will be assumed."), cmd.n_lrecl[0]); |
122 |
handle->length = 1; |
length = 1; |
123 |
} |
} |
124 |
else |
else |
125 |
handle->length = cmd.n_lrecl[0]; |
length = cmd.n_lrecl[0]; |
126 |
break; |
break; |
127 |
default: |
default: |
128 |
assert (0); |
assert (0); |
129 |
} |
} |
130 |
|
|
131 |
|
handle = create_file_handle (handle_name, cmd.s_name, |
132 |
|
mode, length, tab_width); |
133 |
|
|
134 |
|
|
135 |
return CMD_SUCCESS; |
return CMD_SUCCESS; |
136 |
|
|
137 |
lossage: |
lossage: |
138 |
free_file_handle (&cmd); |
free_file_handle (&cmd); |
139 |
return CMD_FAILURE; |
return CMD_FAILURE; |
140 |
} |
} |
|
|
|
|
/* File handle functions. */ |
|
|
|
|
|
/* 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 *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; |
|
|
} |
|
|
|
|
|
static void |
|
|
destroy_file_handle(void *fh_, void *aux UNUSED) |
|
|
{ |
|
|
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"; |
|
|
} |
|
|
|
|
141 |
|
|
|
/* Tries to open handle H 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++; |
|
|
|
|
|
return &h->aux; |
|
|
} |
|
|
|
|
|
/* 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) |
|
|
{ |
|
|
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; |
|
|
} |
|
142 |
|
|
143 |
|
|
144 |
static struct linked_list *handle_list; |
static struct linked_list *handle_list; |
169 |
char *filename = ds_c_str (&tokstr); |
char *filename = ds_c_str (&tokstr); |
170 |
char *handle_name = xmalloc (strlen (filename) + 3); |
char *handle_name = xmalloc (strlen (filename) + 3); |
171 |
sprintf (handle_name, "\"%s\"", filename); |
sprintf (handle_name, "\"%s\"", filename); |
172 |
handle = create_file_handle (handle_name, filename); |
handle = create_file_handle_with_defaults (handle_name, filename); |
173 |
ll_push_front(handle_list, handle); |
ll_push_front(handle_list, handle); |
174 |
free (handle_name); |
free (handle_name); |
175 |
} |
} |
180 |
return handle; |
return handle; |
181 |
} |
} |
182 |
|
|
|
/* 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) |
|
|
{ |
|
|
assert (handle != NULL); |
|
|
return handle->mode; |
|
|
} |
|
|
|
|
|
/* Returns the width of a logical record on HANDLE. */ |
|
|
size_t |
|
|
handle_get_record_width (const struct file_handle *handle) |
|
|
{ |
|
|
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; |
|
|
} |
|
183 |
|
|
184 |
|
|
185 |
void |
void |