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