3ff35e8e85c47db1f10925c3d57a8988521b8d91
[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 <assert.h>
23 #include <errno.h>
24 #include <stdlib.h>
25 #include "alloc.h"
26 #include "filename.h"
27 #include "command.h"
28 #include "hash.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 static struct hsh_table *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 = hsh_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   hsh_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 = hsh_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       hsh_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 = hsh_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 /* Hashes the name of file handle H. */
287 static unsigned
288 hash_file_handle (const void *handle_, void *param unused)
289 {
290   const struct file_handle *handle = handle_;
291
292   return hsh_hash_string (handle->name);
293 }
294
295 /* Compares names of file handles A and B. */
296 static int
297 cmp_file_handle (const void *a_, const void *b_, void *foo unused)
298 {
299   const struct file_handle *a = a_;
300   const struct file_handle *b = b_;
301
302   return strcmp (a->name, b->name);
303 }
304
305 /* Initialize the hash of file handles; inserts the "inline file"
306    inline_file. */
307 void
308 fh_init_files (void)
309 {
310   /* Create hash. */
311   files = hsh_create (4, cmp_file_handle, hash_file_handle, NULL, NULL);
312
313   /* Insert inline file. */
314   inline_file = xmalloc (sizeof *inline_file);
315   init_file_handle (inline_file);
316   inline_file->name = "INLINE";
317   inline_file->where.filename
318     = inline_file->fn = inline_file->norm_fn = (char *) _("<Inline File>");
319   inline_file->where.line_number = 0;
320   hsh_force_insert (files, inline_file);
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_file_handle (void)
328 {
329   struct file_handle *handle;
330
331   if (token == T_ID)
332     handle = fh_get_handle_by_name (tokid);
333   else if (token == T_STRING)
334     handle = fh_get_handle_by_filename (ds_value (&tokstr));
335   else
336     {
337       lex_error (_("expecting a file name or handle"));
338       return NULL;
339     }
340
341   if (!handle)
342     return NULL;
343   lex_get ();
344
345   return handle;
346 }
347
348 /* Returns the (normalized) filename associated with file handle H. */
349 char *
350 fh_handle_filename (struct file_handle * h)
351 {
352   return h->norm_fn;
353 }
354
355 /* Returns the width of a logical record on file handle H. */
356 size_t
357 fh_record_width (struct file_handle *h)
358 {
359   if (h == inline_file)
360     return 80;
361   else if (h->recform == FH_RF_FIXED)
362     return h->lrecl;
363   else
364     return 1024;
365 }
366
367 /*
368    Local variables:
369    mode: c
370    End:
371 */