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