1133fd31286367695eecf4c815d7eeabf96de2f8
[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 /* (headers) */
34
35 /* File handle private data. */
36 struct private_file_handle 
37   {
38     char *name;                 /* File handle identifier. */
39     char *filename;             /* Filename as provided by user. */
40     struct file_identity *identity; /* For checking file identity. */
41     struct file_locator where;  /* Used for reporting error messages. */
42     enum file_handle_mode mode; /* File mode. */
43     size_t length;              /* Length of fixed-format records. */
44   };
45
46 /* Linked list of file handles. */
47 struct file_handle_list 
48   {
49     struct file_handle *handle;
50     struct file_handle_list *next;
51   };
52
53 static struct file_handle_list *file_handles;
54 struct file_handle *inline_file;
55
56 static struct file_handle *create_file_handle (const char *handle_name,
57                                                const char *filename);
58
59 /* (specification)
60    "FILE HANDLE" (fh_):
61      name=string;
62      recform=recform:fixed/!variable/spanned;
63      lrecl=integer;
64      mode=mode:!character/image/binary/multipunch/_360.
65 */
66 /* (declarations) */
67 /* (functions) */
68
69 static struct file_handle *
70 get_handle_with_name (const char *handle_name) 
71 {
72   struct file_handle_list *iter;
73
74   for (iter = file_handles; iter != NULL; iter = iter->next)
75     if (!strcmp (handle_name, iter->handle->private->name))
76       return iter->handle;
77   return NULL;
78 }
79
80 static struct file_handle *
81 get_handle_for_filename (const char *filename)
82 {
83   struct file_identity *identity;
84   struct file_handle_list *iter;
85       
86   /* First check for a file with the same identity. */
87   identity = fn_get_identity (filename);
88   if (identity != NULL) 
89     {
90       for (iter = file_handles; iter != NULL; iter = iter->next)
91         if (iter->handle->private->identity != NULL
92             && !fn_compare_file_identities (identity,
93                                          iter->handle->private->identity)) 
94           {
95             fn_free_identity (identity);
96             return iter->handle; 
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->handle->private->filename))
104       return iter->handle; 
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->private->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->private->mode = MODE_TEXT;
156       break;
157     case FH_IMAGE:
158       handle->private->mode = MODE_BINARY;
159       if (cmd.n_lrecl == NOT_LONG)
160         {
161           msg (SE, _("Fixed-length records were specified on /RECFORM, but "
162                      "record length was not specified on /LRECL.  "
163                      "Assuming 1024-character records."));
164           handle->private->length = 1024;
165         }
166       else if (cmd.n_lrecl < 1)
167         {
168           msg (SE, _("Record length (%ld) must be at least one byte.  "
169                      "1-character records will be assumed."), cmd.n_lrecl);
170           handle->private->length = 1;
171         }
172       else
173         handle->private->length = cmd.n_lrecl;
174       break;
175     default:
176       assert (0);
177     }
178
179   return CMD_SUCCESS;
180
181  lossage:
182   free_file_handle (&cmd);
183   return CMD_FAILURE;
184 }
185 \f
186 /* File handle functions. */
187
188 /* Creates and returns a new file handle with the given values
189    and defaults for other values.  Adds the created file handle
190    to the global list. */
191 static struct file_handle *
192 create_file_handle (const char *handle_name, const char *filename)
193 {
194   struct file_handle *handle;
195   struct file_handle_list *list;
196
197   /* Create and initialize file handle. */
198   handle = xmalloc (sizeof *handle);
199   handle->private = xmalloc (sizeof *handle->private);
200   handle->private->name = xstrdup (handle_name);
201   handle->private->filename = xstrdup (filename);
202   handle->private->identity = fn_get_identity (filename);
203   handle->private->where.filename = handle->private->filename;
204   handle->private->where.line_number = 0;
205   handle->private->mode = MODE_TEXT;
206   handle->private->length = 1024;
207   handle->ext = NULL;
208   handle->class = NULL;
209
210   /* Add file handle to global list. */
211   list = xmalloc (sizeof *list);
212   list->handle = handle;
213   list->next = file_handles;
214   file_handles = list;
215
216   return handle;
217 }
218
219 /* Closes the stdio FILE associated with handle H.  Frees internal
220    buffers associated with that file.  Does *not* destroy the file
221    handle H.  (File handles are permanent during a session.)  */
222 void
223 fh_close_handle (struct file_handle *h)
224 {
225   if (h == NULL)
226     return;
227
228   if (h->class != NULL)
229     h->class->close (h);
230   h->class = NULL;
231   h->ext = NULL;
232 }
233
234 /* Initialize the hash of file handles; inserts the "inline file"
235    inline_file. */
236 void
237 fh_init_files (void)
238 {
239   if (inline_file == NULL) 
240     {
241       inline_file = create_file_handle ("INLINE", _("<Inline File>"));
242       inline_file->private->length = 80; 
243     }
244 }
245
246 /* Parses a file handle name, which may be a filename as a string or
247    a file handle name as an identifier.  Returns the file handle or
248    NULL on failure. */
249 struct file_handle *
250 fh_parse_file_handle (void)
251 {
252   struct file_handle *handle;
253
254   if (token != T_ID && token != T_STRING)
255     {
256       lex_error (_("expecting a file name or handle name"));
257       return NULL;
258     }
259
260   /* Check for named handles first, then go by filename. */
261   handle = NULL;
262   if (token == T_ID) 
263     handle = get_handle_with_name (tokid);
264   if (handle == NULL)
265     handle = get_handle_for_filename (ds_value (&tokstr));
266   if (handle == NULL) 
267     {
268       char *filename = ds_value (&tokstr);
269       char *handle_name = xmalloc (strlen (filename) + 3);
270       sprintf (handle_name, "\"%s\"", filename);
271       handle = create_file_handle (handle_name, filename);
272       free (handle_name);
273     }
274
275   lex_get ();
276
277   return handle;
278 }
279
280 /* Returns the identifier of file HANDLE.  If HANDLE was created
281    by referring to a filename instead of a handle name, returns
282    the filename, enclosed in double quotes.  Return value is
283    owned by the file handle. 
284
285    Useful for printing error messages about use of file handles.  */
286 const char *
287 handle_get_name (const struct file_handle *handle)
288 {
289   return handle->private->name;
290 }
291
292 /* Returns the name of the file associated with HANDLE. */
293 const char *
294 handle_get_filename (const struct file_handle *handle) 
295 {
296   return handle->private->filename;
297 }
298
299 /* Returns the mode of HANDLE. */
300 enum file_handle_mode
301 handle_get_mode (const struct file_handle *handle) 
302 {
303   assert (handle != NULL);
304   return handle->private->mode;
305 }
306
307 /* Returns the width of a logical record on HANDLE. */
308 size_t
309 handle_get_record_width (const struct file_handle *handle)
310 {
311   assert (handle != NULL);
312   return handle->private->length;
313 }
314
315 /*
316    Local variables:
317    mode: c
318    End:
319 */