Adopt use of gnulib for portability.
[pspp-builds.git] / src / file-handle.q
1 /* PSPP - computes sample statistics.
2    Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
3    Written by Ben Pfaff <blp@gnu.org>.
4
5    This program is free software; you can redistribute it and/or
6    modify it under the terms of the GNU General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    License, or (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful, but
11    WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18    02110-1301, USA. */
19
20 #include <config.h>
21 #include "file-handle.h"
22 #include "error.h"
23 #include <errno.h>
24 #include <stdlib.h>
25 #include "alloc.h"
26 #include "filename.h"
27 #include "command.h"
28 #include "lexer.h"
29 #include "getl.h"
30 #include "error.h"
31 #include "magic.h"
32 #include "var.h"
33 #include "linked-list.h"
34
35 #include "gettext.h"
36 #define _(msgid) gettext (msgid)
37
38 /* (headers) */
39
40 /* File handle. */
41 struct file_handle 
42   {
43     struct file_handle *next;   /* Next in global list. */
44     char *name;                 /* File handle identifier. */
45     char *filename;             /* Filename as provided by user. */
46     struct file_identity *identity; /* For checking file identity. */
47     struct file_locator where;  /* Used for reporting error messages. */
48     enum file_handle_mode mode; /* File mode. */
49     size_t length;              /* Length of fixed-format records. */
50     size_t tab_width;           /* Tab width, 0=do not expand tabs. */
51
52     int open_cnt;               /* 0=not open, otherwise # of openers. */
53     const char *type;           /* If open, type of file. */
54     char open_mode[3];          /* "[rw][se]". */
55     void *aux;                  /* Aux data pointer for owner if any. */
56   };
57
58 static struct file_handle *file_handles;
59
60 static struct file_handle *create_file_handle (const char *handle_name,
61                                                const char *filename);
62
63 /* (specification)
64    "FILE HANDLE" (fh_):
65      name=string;
66      lrecl=integer;
67      tabwidth=integer "x>=0" "%s must be nonnegative";
68      mode=mode:!character/image.
69 */
70 /* (declarations) */
71 /* (functions) */
72
73 static struct file_handle *
74 get_handle_with_name (const char *handle_name) 
75 {
76   struct file_handle *iter;
77
78   for (iter = file_handles; iter != NULL; iter = iter->next)
79     if (!strcasecmp (handle_name, iter->name))
80       return iter;
81   return NULL;
82 }
83
84 static struct file_handle *
85 get_handle_for_filename (const char *filename)
86 {
87   struct file_identity *identity;
88   struct file_handle *iter;
89       
90   /* First check for a file with the same identity. */
91   identity = fn_get_identity (filename);
92   if (identity != NULL) 
93     {
94       for (iter = file_handles; iter != NULL; iter = iter->next)
95         if (iter->identity != NULL
96             && !fn_compare_file_identities (identity, iter->identity))
97           {
98             fn_free_identity (identity);
99             return iter; 
100           }
101       fn_free_identity (identity);
102     }
103
104   /* Then check for a file with the same name. */
105   for (iter = file_handles; iter != NULL; iter = iter->next)
106     if (!strcmp (filename, iter->filename))
107       return iter; 
108
109   return NULL;
110 }
111
112 int
113 cmd_file_handle (void)
114 {
115   char handle_name[LONG_NAME_LEN + 1];
116
117   struct cmd_file_handle cmd;
118   struct file_handle *handle;
119
120   if (!lex_force_id ())
121     return CMD_FAILURE;
122   str_copy_trunc (handle_name, sizeof handle_name, tokid);
123
124   handle = get_handle_with_name (handle_name);
125   if (handle != NULL)
126     {
127       msg (SE, _("File handle %s already refers to file %s.  "
128                  "File handles cannot be redefined within a session."),
129            handle_name, handle->filename);
130       return CMD_FAILURE;
131     }
132
133   lex_get ();
134   if (!lex_force_match ('/'))
135     return CMD_FAILURE;
136
137   if (!parse_file_handle (&cmd))
138     return CMD_FAILURE;
139
140   if (token != '.')
141     {
142       lex_error (_("expecting end of command"));
143       goto lossage;
144     }
145
146   if (cmd.s_name == NULL)
147     {
148       msg (SE, _("The FILE HANDLE required subcommand NAME "
149                  "is not present."));
150       goto lossage;
151     }
152
153   handle = create_file_handle (handle_name, cmd.s_name);
154   switch (cmd.mode)
155     {
156     case FH_CHARACTER:
157       handle->mode = MODE_TEXT;
158       if (cmd.sbc_tabwidth)
159         handle->tab_width = cmd.n_tabwidth[0];
160       else
161         handle->tab_width = 4;
162       break;
163     case FH_IMAGE:
164       handle->mode = MODE_BINARY;
165       if (cmd.n_lrecl[0] == NOT_LONG)
166         {
167           msg (SE, _("Fixed-length records were specified on /RECFORM, but "
168                      "record length was not specified on /LRECL.  "
169                      "Assuming 1024-character records."));
170           handle->length = 1024;
171         }
172       else if (cmd.n_lrecl[0] < 1)
173         {
174           msg (SE, _("Record length (%ld) must be at least one byte.  "
175                      "1-character records will be assumed."), cmd.n_lrecl[0]);
176           handle->length = 1;
177         }
178       else
179         handle->length = cmd.n_lrecl[0];
180       break;
181     default:
182       assert (0);
183     }
184
185   return CMD_SUCCESS;
186
187  lossage:
188   free_file_handle (&cmd);
189   return CMD_FAILURE;
190 }
191 \f
192 /* File handle functions. */
193
194 /* Creates and returns a new file handle with the given values
195    and defaults for other values.  Adds the created file handle
196    to the global list. */
197 static struct file_handle *
198 create_file_handle (const char *handle_name, const char *filename)
199 {
200   struct file_handle *handle;
201
202   /* Create and initialize file handle. */
203   handle = xmalloc (sizeof *handle);
204   handle->next = file_handles;
205   handle->name = xstrdup (handle_name);
206   handle->filename = xstrdup (filename);
207   handle->identity = fn_get_identity (filename);
208   handle->where.filename = handle->filename;
209   handle->where.line_number = 0;
210   handle->mode = MODE_TEXT;
211   handle->length = 1024;
212   handle->tab_width = 4;
213   handle->open_cnt = 0;
214   handle->type = NULL;
215   handle->aux = NULL;
216   file_handles = handle;
217
218   return handle;
219 }
220
221 static void
222 destroy_file_handle(void *fh_, void *aux UNUSED)
223 {
224   struct file_handle *fh = fh_;
225   free (fh->name);
226   free (fh->filename);
227   fn_free_identity (fh->identity);
228   free (fh);
229 }
230
231 static const char *
232 mode_name (const char *mode) 
233 {
234   assert (mode != NULL);
235   assert (mode[0] == 'r' || mode[0] == 'w');
236
237   return mode[0] == 'r' ? "reading" : "writing";
238 }
239
240
241 /* Tries to open handle H with the given TYPE and MODE.
242
243    TYPE is the sort of file, e.g. "system file".  Only one given
244    type of access is allowed on a given file handle at once.
245
246    MODE combines the read or write mode with the sharing mode.
247    The first character is 'r' for read, 'w' for write.  The
248    second character is 's' to permit sharing, 'e' to require
249    exclusive access.
250
251    Returns the address of a void * that the caller can use for
252    data specific to the file handle if successful, or a null
253    pointer on failure.  For exclusive access modes the void *
254    will always be a null pointer at return.  In shared access
255    modes the void * will necessarily be null only if no other
256    sharers are active.
257
258    If successful, a reference to type is retained, so it should
259    probably be a string literal. */
260 void **
261 fh_open (struct file_handle *h, const char *type, const char *mode) 
262 {
263   assert (h != NULL);
264   assert (type != NULL);
265   assert (mode != NULL);
266   assert (mode[0] == 'r' || mode[0] == 'w');
267   assert (mode[1] == 's' || mode[1] == 'e');
268   assert (mode[2] == '\0');
269
270   if (h->open_cnt != 0) 
271     {
272       if (strcmp (h->type, type)) 
273         {
274           msg (SE, _("Can't open %s as a %s because it is "
275                      "already open as a %s"),
276                handle_get_name (h), type, h->type);
277           return NULL; 
278         }
279       else if (strcmp (h->open_mode, mode)) 
280         {
281           msg (SE, _("Can't open %s as a %s for %s because it is "
282                      "already open for %s"),
283                handle_get_name (h), type,
284                mode_name (mode), mode_name (h->open_mode));
285           return NULL;
286         }
287       else if (h->open_mode[1] == 'e')
288         {
289           msg (SE, _("Can't re-open %s as a %s for %s"),
290                handle_get_name (h), type, mode_name (mode));
291           return NULL;
292         }
293     }
294   else 
295     {
296       h->type = type;
297       strcpy (h->open_mode, mode);
298       assert (h->aux == NULL);
299     }
300   h->open_cnt++;
301
302   return &h->aux;
303 }
304
305 /* Closes file handle H, which must have been open for the
306    specified TYPE and MODE of access provided to fh_open().
307    Returns zero if the file is now closed, nonzero if it is still
308    open due to another reference. */
309 int
310 fh_close (struct file_handle *h, const char *type, const char *mode)
311 {
312   assert (h != NULL);
313   assert (h->open_cnt > 0);
314   assert (type != NULL);
315   assert (!strcmp (type, h->type));
316   assert (mode != NULL);
317   assert (!strcmp (mode, h->open_mode));
318
319   h->open_cnt--;
320   if (h->open_cnt == 0) 
321     {
322       h->type = NULL;
323       h->aux = NULL;
324     }
325   return h->open_cnt;
326 }
327
328
329 static struct linked_list *handle_list;
330
331
332 /* Parses a file handle name, which may be a filename as a string or
333    a file handle name as an identifier.  Returns the file handle or
334    NULL on failure. */
335 struct file_handle *
336 fh_parse (void)
337 {
338   struct file_handle *handle;
339
340   if (token != T_ID && token != T_STRING)
341     {
342       lex_error (_("expecting a file name or handle name"));
343       return NULL;
344     }
345
346   /* Check for named handles first, then go by filename. */
347   handle = NULL;
348   if (token == T_ID) 
349     handle = get_handle_with_name (tokid);
350   if (handle == NULL)
351     handle = get_handle_for_filename (ds_c_str (&tokstr));
352   if (handle == NULL) 
353     {
354       char *filename = ds_c_str (&tokstr);
355       char *handle_name = xmalloc (strlen (filename) + 3);
356       sprintf (handle_name, "\"%s\"", filename);
357       handle = create_file_handle (handle_name, filename);
358       ll_push_front(handle_list, handle);
359       free (handle_name);
360     }
361
362   lex_get ();
363
364
365   return handle;
366 }
367
368 /* Returns the identifier of file HANDLE.  If HANDLE was created
369    by referring to a filename instead of a handle name, returns
370    the filename, enclosed in double quotes.  Return value is
371    owned by the file handle. 
372
373    Useful for printing error messages about use of file handles.  */
374 const char *
375 handle_get_name (const struct file_handle *handle)
376 {
377   assert (handle != NULL);
378   return handle->name;
379 }
380
381 /* Returns the name of the file associated with HANDLE. */
382 const char *
383 handle_get_filename (const struct file_handle *handle) 
384 {
385   assert (handle != NULL);
386   return handle->filename;
387 }
388
389 /* Returns the mode of HANDLE. */
390 enum file_handle_mode
391 handle_get_mode (const struct file_handle *handle) 
392 {
393   assert (handle != NULL);
394   return handle->mode;
395 }
396
397 /* Returns the width of a logical record on HANDLE. */
398 size_t
399 handle_get_record_width (const struct file_handle *handle)
400 {
401   assert (handle != NULL);
402   return handle->length;
403 }
404
405 /* Returns the number of characters per tab stop for HANDLE, or
406    zero if tabs are not to be expanded.  Applicable only to
407    MODE_TEXT files. */
408 size_t
409 handle_get_tab_width (const struct file_handle *handle) 
410 {
411   assert (handle != NULL);
412   return handle->tab_width;
413 }
414
415
416 void 
417 fh_init(void)
418 {
419   handle_list = ll_create(destroy_file_handle,0);
420 }
421
422 void 
423 fh_done(void)
424 {
425   if ( handle_list )  
426   {
427     ll_destroy(handle_list);
428     handle_list = 0;
429   }
430 }
431
432
433 /*
434    Local variables:
435    mode: c
436    End:
437 */