Implemented long variable names a la spss V12.
[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., 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     char open_mode[3];          /* "[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->aux = NULL;
214   file_handles = handle;
215
216   return handle;
217 }
218
219 static void
220 destroy_file_handle(void *fh_, void *aux UNUSED)
221 {
222   struct file_handle *fh = fh_;
223   free (fh->name);
224   free (fh->filename);
225   fn_free_identity (fh->identity);
226   free (fh);
227 }
228
229 static const char *
230 mode_name (const char *mode) 
231 {
232   assert (mode != NULL);
233   assert (mode[0] == 'r' || mode[0] == 'w');
234
235   return mode[0] == 'r' ? "reading" : "writing";
236 }
237
238
239 /* Tries to open FILE with the given TYPE and MODE.
240
241    TYPE is the sort of file, e.g. "system file".  Only one given
242    type of access is allowed on a given file handle at once.
243
244    MODE combines the read or write mode with the sharing mode.
245    The first character is 'r' for read, 'w' for write.  The
246    second character is 's' to permit sharing, 'e' to require
247    exclusive access.
248
249    Returns the address of a void * that the caller can use for
250    data specific to the file handle if successful, or a null
251    pointer on failure.  For exclusive access modes the void *
252    will always be a null pointer at return.  In shared access
253    modes the void * will necessarily be null only if no other
254    sharers are active.
255
256    If successful, a reference to type is retained, so it should
257    probably be a string literal. */
258 void **
259 fh_open (struct file_handle *h, const char *type, const char *mode) 
260 {
261   assert (h != NULL);
262   assert (type != NULL);
263   assert (mode != NULL);
264   assert (mode[0] == 'r' || mode[0] == 'w');
265   assert (mode[1] == 's' || mode[1] == 'e');
266   assert (mode[2] == '\0');
267
268   if (h->open_cnt != 0) 
269     {
270       if (strcmp (h->type, type)) 
271         {
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           return NULL; 
276         }
277       else if (strcmp (h->open_mode, mode)) 
278         {
279           msg (SE, _("Can't open %s as a %s for %s because it is "
280                      "already open for %s"),
281                handle_get_name (h), type,
282                mode_name (mode), mode_name (h->open_mode));
283           return NULL;
284         }
285       else if (h->open_mode[1] == 'e')
286         {
287           msg (SE, _("Can't re-open %s as a %s for %s"),
288                handle_get_name (h), type, mode_name (mode));
289           return NULL;
290         }
291     }
292   else 
293     {
294       h->type = type;
295       strcpy (h->open_mode, mode);
296       assert (h->aux == NULL);
297     }
298   h->open_cnt++;
299
300   return &h->aux;
301 }
302
303 /* Closes file handle H, which must have been open for the
304    specified TYPE and MODE of access provided to fh_open().
305    Returns zero if the file is now closed, nonzero if it is still
306    open due to another reference. */
307 int
308 fh_close (struct file_handle *h, const char *type, const char *mode)
309 {
310   assert (h != NULL);
311   assert (h->open_cnt > 0);
312   assert (type != NULL);
313   assert (!strcmp (type, h->type));
314   assert (mode != NULL);
315   assert (!strcmp (mode, h->open_mode));
316
317   h->open_cnt--;
318   if (h->open_cnt == 0) 
319     {
320       h->type = NULL;
321       h->aux = NULL;
322     }
323   return h->open_cnt;
324 }
325
326
327 static struct linked_list *handle_list;
328
329
330 /* Parses a file handle name, which may be a filename as a string or
331    a file handle name as an identifier.  Returns the file handle or
332    NULL on failure. */
333 struct file_handle *
334 fh_parse (void)
335 {
336   struct file_handle *handle;
337
338   if (token != T_ID && token != T_STRING)
339     {
340       lex_error (_("expecting a file name or handle name"));
341       return NULL;
342     }
343
344   /* Check for named handles first, then go by filename. */
345   handle = NULL;
346   if (token == T_ID) 
347     handle = get_handle_with_name (tokid);
348   if (handle == NULL)
349     handle = get_handle_for_filename (ds_c_str (&tokstr));
350   if (handle == NULL) 
351     {
352       char *filename = ds_c_str (&tokstr);
353       char *handle_name = xmalloc (strlen (filename) + 3);
354       sprintf (handle_name, "\"%s\"", filename);
355       handle = create_file_handle (handle_name, filename);
356       ll_push_front(handle_list, handle);
357       free (handle_name);
358     }
359
360   lex_get ();
361
362
363   return handle;
364 }
365
366 /* Returns the identifier of file HANDLE.  If HANDLE was created
367    by referring to a filename instead of a handle name, returns
368    the filename, enclosed in double quotes.  Return value is
369    owned by the file handle. 
370
371    Useful for printing error messages about use of file handles.  */
372 const char *
373 handle_get_name (const struct file_handle *handle)
374 {
375   assert (handle != NULL);
376   return handle->name;
377 }
378
379 /* Returns the name of the file associated with HANDLE. */
380 const char *
381 handle_get_filename (const struct file_handle *handle) 
382 {
383   assert (handle != NULL);
384   return handle->filename;
385 }
386
387 /* Returns the mode of HANDLE. */
388 enum file_handle_mode
389 handle_get_mode (const struct file_handle *handle) 
390 {
391   assert (handle != NULL);
392   return handle->mode;
393 }
394
395 /* Returns the width of a logical record on HANDLE.  Applicable
396    only to MODE_BINARY files.  */
397 size_t
398 handle_get_record_width (const struct file_handle *handle)
399 {
400   assert (handle != NULL);
401   return handle->length;
402 }
403
404 /* Returns the number of characters per tab stop for HANDLE, or
405    zero if tabs are not to be expanded.  Applicable only to
406    MODE_TEXT files. */
407 size_t
408 handle_get_tab_width (const struct file_handle *handle) 
409 {
410   assert (handle != NULL);
411   return handle->tab_width;
412 }
413
414
415 void 
416 fh_init(void)
417 {
418   handle_list = ll_create(destroy_file_handle,0);
419 }
420
421 void 
422 fh_done(void)
423 {
424   if ( handle_list )  
425   {
426     ll_destroy(handle_list);
427     handle_list = 0;
428   }
429 }
430
431
432 /*
433    Local variables:
434    mode: c
435    End:
436 */