checkin of 0.3.0
[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 <assert.h>
22 #include <errno.h>
23 #include <stdlib.h>
24 #include "alloc.h"
25 #include "avl.h"
26 #include "filename.h"
27 #include "file-handle.h"
28 #include "command.h"
29 #include "lexer.h"
30 #include "getline.h"
31 #include "error.h"
32 #include "magic.h"
33 #include "var.h"
34 /* (headers) */
35
36 #undef DEBUGGING
37 /*#define DEBUGGING 1*/
38 #include "debug-print.h"
39
40 avl_tree *files;
41 struct file_handle *inline_file;
42
43 static void init_file_handle (struct file_handle * handle);
44
45 /* (specification)
46    "FILE HANDLE" (fh_):
47      name=string;
48      recform=recform:fixed/!variable/spanned;
49      lrecl=integer;
50      mode=mode:!character/image/binary/multipunch/_360.
51 */
52 /* (declarations) */
53 /* (functions) */
54
55 int
56 cmd_file_handle (void)
57 {
58   char handle_name[9];
59   char *handle_name_p = handle_name;
60
61   struct cmd_file_handle cmd;
62   struct file_handle *fp;
63
64   lex_get ();
65   if (!lex_force_id ())
66     return CMD_FAILURE;
67   strcpy (handle_name, tokid);
68
69   fp = NULL;
70   if (files)
71     fp = avl_find (files, &handle_name_p);
72   if (fp)
73     {
74       msg (SE, _("File handle %s had already been defined to refer to "
75                  "file %s.  It is not possible to redefine a file "
76                  "handle within a session."),
77            tokid, fp->fn);
78       return CMD_FAILURE;
79     }
80
81   lex_get ();
82   if (!lex_force_match ('/'))
83     return CMD_FAILURE;
84
85   if (!parse_file_handle (&cmd))
86     return CMD_FAILURE;
87
88   if (token != '.')
89     {
90       lex_error (_("expecting end of command"));
91       goto lossage;
92     }
93
94   if (cmd.s_name == NULL)
95     {
96       msg (SE, _("The FILE HANDLE required subcommand NAME "
97                  "is not present."));
98       goto lossage;
99     }
100
101   fp = xmalloc (sizeof *fp);
102   init_file_handle (fp);
103
104   switch (cmd.recform)
105     {
106     case FH_FIXED:
107       if (cmd.n_lrecl == NOT_LONG)
108         {
109           msg (SE, _("Fixed length records were specified on /RECFORM, but "
110                "record length was not specified on /LRECL.  80-character "
111                "records will be assumed."));
112           cmd.n_lrecl = 80;
113         }
114       else if (cmd.n_lrecl < 1)
115         {
116           msg (SE, _("Record length (%ld) must be at least one byte.  "
117                      "80-character records will be assumed."), cmd.n_lrecl);
118           cmd.n_lrecl = 80;
119         }
120       fp->recform = FH_RF_FIXED;
121       fp->lrecl = cmd.n_lrecl;
122       break;
123     case FH_VARIABLE:
124       fp->recform = FH_RF_VARIABLE;
125       break;
126     case FH_SPANNED:
127       msg (SE, _("/RECFORM SPANNED is not implemented, as the author doesn't "
128            "know what it is supposed to do.  Send the author a note."));
129       break;
130     default:
131       assert (0);
132     }
133
134   switch (cmd.mode)
135     {
136     case FH_CHARACTER:
137       fp->mode = FH_MD_CHARACTER;
138       break;
139     case FH_IMAGE:
140       msg (SE, _("/MODE IMAGE is not implemented, as the author doesn't know "
141            "what it is supposed to do.  Send the author a note."));
142       break;
143     case FH_BINARY:
144       fp->mode = FH_MD_BINARY;
145       break;
146     case FH_MULTIPUNCH:
147       msg (SE, _("/MODE MULTIPUNCH is not implemented.  If you care, "
148                  "complain."));
149       break;
150     case FH__360:
151       msg (SE, _("/MODE 360 is not implemented.  If you care, complain."));
152       break;
153     default:
154       assert (0);
155     }
156
157   fp->name = xstrdup (handle_name);
158   fp->norm_fn = fn_normalize (cmd.s_name);
159   fp->where.filename = fp->fn = cmd.s_name;
160   avl_force_insert (files, fp);
161
162   return CMD_SUCCESS;
163
164  lossage:
165   free_file_handle (&cmd);
166   return CMD_FAILURE;
167 }
168 \f
169 /* File handle functions. */
170
171 /* Sets up some fields in H; caller should fill in
172    H->{NAME,NORM_FN,FN}. */
173 static void
174 init_file_handle (struct file_handle *h)
175 {
176   h->recform = FH_RF_VARIABLE;
177   h->mode = FH_MD_CHARACTER;
178   h->ext = NULL;
179   h->class = NULL;
180 }
181
182 /* Returns the handle corresponding to FILENAME.  Creates the handle
183    if no handle exists for that file.  All filenames are normalized
184    first, so different filenames referring to the same file will
185    return the same file handle. */
186 struct file_handle *
187 fh_get_handle_by_filename (const char *filename)
188 {
189   struct file_handle f, *fp;
190   char *fn;
191   char *name;
192   int len;
193
194   /* Get filename. */
195   fn = fn_normalize (filename);
196   len = strlen (fn);
197
198   /* Create handle name with invalid identifier character to prevent
199      conflicts with handles created with FILE HANDLE. */
200   name = xmalloc (len + 2);
201   name[0] = '*';
202   strcpy (&name[1], fn);
203
204   f.name = name;
205   fp = avl_find (files, &f);
206   if (!fp)
207     {
208       fp = xmalloc (sizeof *fp);
209       init_file_handle (fp);
210       fp->name = name;
211       fp->norm_fn = fn;
212       fp->where.filename = fp->fn = xstrdup (filename);
213       avl_force_insert (files, fp);
214     }
215   else
216     {
217       free (fn);
218       free (name);
219     }
220   return fp;
221 }
222
223 /* Returns the handle with identifier NAME, if it exists; otherwise
224    reports error to user and returns NULL. */
225 struct file_handle *
226 fh_get_handle_by_name (const char name[9])
227 {
228   struct file_handle f, *fp;
229   f.name = (char *) name;
230   fp = avl_find (files, &f);
231
232   if (!fp)
233     msg (SE, _("File handle `%s' has not been previously declared on "
234          "FILE HANDLE."), name);
235   return fp;
236 }
237
238 /* Returns the identifier of file HANDLE.  If HANDLE was created by
239    referring to a filename (i.e., DATA LIST FILE='yyy' instead of FILE
240    HANDLE XXX='yyy'), returns the filename, enclosed in double quotes.
241    Return value is in a static buffer.
242
243    Useful for printing error messages about use of file handles.  */
244 const char *
245 fh_handle_name (struct file_handle *h)
246 {
247   static char *buf = NULL;
248
249   if (buf)
250     {
251       free (buf);
252       buf = NULL;
253     }
254   if (!h)
255     return NULL;
256
257   if (h->name[0] == '*')
258     {
259       int len = strlen (h->fn);
260
261       buf = xmalloc (len + 3);
262       strcpy (&buf[1], h->fn);
263       buf[0] = buf[len + 1] = '"';
264       buf[len + 2] = 0;
265       return buf;
266     }
267   return h->name;
268 }
269
270 /* Closes the stdio FILE associated with handle H.  Frees internal
271    buffers associated with that file.  Does *not* destroy the file
272    handle H.  (File handles are permanent during a session.)  */
273 void
274 fh_close_handle (struct file_handle *h)
275 {
276   if (h == NULL)
277     return;
278
279   debug_printf (("Closing %s%s.\n", fh_handle_name (h),
280                  h->class == NULL ? " (already closed)" : ""));
281
282   if (h->class)
283     h->class->close (h);
284   h->class = NULL;
285   h->ext = NULL;
286 }
287
288 /* Compares names of file handles A and B. */
289 static int
290 cmp_file_handle (const void *a, const void *b, void *foo unused)
291 {
292   return strcmp (((struct file_handle *) a)->name,
293                  ((struct file_handle *) b)->name);
294 }
295
296 /* Initialize the AVL tree of file handles; inserts the "inline file"
297    inline_file. */
298 void
299 fh_init_files (void)
300 {
301   /* Create AVL tree. */
302   files = avl_create (NULL, cmp_file_handle, NULL);
303
304   /* Insert inline file. */
305   inline_file = xmalloc (sizeof *inline_file);
306   init_file_handle (inline_file);
307   inline_file->name = "INLINE";
308   inline_file->where.filename
309     = inline_file->fn = inline_file->norm_fn = (char *) _("<Inline File>");
310   inline_file->where.line_number = 0;
311   avl_force_insert (files, inline_file);
312 }
313
314 /* Parses a file handle name, which may be a filename as a string or
315    a file handle name as an identifier.  Returns the file handle or
316    NULL on failure. */
317 struct file_handle *
318 fh_parse_file_handle (void)
319 {
320   struct file_handle *handle;
321
322   if (token == T_ID)
323     handle = fh_get_handle_by_name (tokid);
324   else if (token == T_STRING)
325     handle = fh_get_handle_by_filename (ds_value (&tokstr));
326   else
327     {
328       lex_error (_("expecting a file name or handle"));
329       return NULL;
330     }
331
332   if (!handle)
333     return NULL;
334   lex_get ();
335
336   return handle;
337 }
338
339 /* Returns the (normalized) filename associated with file handle H. */
340 char *
341 fh_handle_filename (struct file_handle * h)
342 {
343   return h->norm_fn;
344 }
345
346 /* Returns the width of a logical record on file handle H. */
347 size_t
348 fh_record_width (struct file_handle *h)
349 {
350   if (h == inline_file)
351     return 80;
352   else if (h->recform == FH_RF_FIXED)
353     return h->lrecl;
354   else
355     return 1024;
356 }
357
358 /*
359    Local variables:
360    mode: c
361    End:
362 */