c4878bbb928b7b6447daaf95b7e474b6682b2b39
[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, 
126            _("%s is not implemented, as the author doesn't know what it is supposed to do.  Send a note to %s.") ,
127            "/RECFORM SPANNED",PACKAGE_BUGREPORT);
128       break;
129     default:
130       assert (0);
131     }
132
133   switch (cmd.mode)
134     {
135     case FH_CHARACTER:
136       fp->mode = FH_MD_CHARACTER;
137       break;
138     case FH_IMAGE:
139       msg (SE, 
140            _("%s is not implemented, as the author doesn't know what it is supposed to do.  Send a note to %s.") ,
141            "/MODE IMAGE",PACKAGE_BUGREPORT);
142       break;
143     case FH_BINARY:
144       fp->mode = FH_MD_BINARY;
145       break;
146     case FH_MULTIPUNCH:
147       msg (SE, _("%s is not implemented.  If you care, complain."),"/MODE MULTIPUNCH");
148       break;
149     case FH__360:
150       msg (SE, _("%s is not implemented.  If you care, complain."),"/MODE 360");
151       break;
152     default:
153       assert (0);
154     }
155
156   fp->name = xstrdup (handle_name);
157   fp->norm_fn = fn_normalize (cmd.s_name);
158   fp->where.filename = fp->fn = cmd.s_name;
159   hsh_force_insert (files, fp);
160
161   return CMD_SUCCESS;
162
163  lossage:
164   free_file_handle (&cmd);
165   return CMD_FAILURE;
166 }
167 \f
168 /* File handle functions. */
169
170 /* Sets up some fields in H; caller should fill in
171    H->{NAME,NORM_FN,FN}. */
172 static void
173 init_file_handle (struct file_handle *h)
174 {
175   h->recform = FH_RF_VARIABLE;
176   h->mode = FH_MD_CHARACTER;
177   h->ext = NULL;
178   h->class = NULL;
179 }
180
181 /* Returns the handle corresponding to FILENAME.  Creates the handle
182    if no handle exists for that file.  All filenames are normalized
183    first, so different filenames referring to the same file will
184    return the same file handle. */
185 struct file_handle *
186 fh_get_handle_by_filename (const char *filename)
187 {
188   struct file_handle f, *fp;
189   char *fn;
190   char *name;
191   int len;
192
193   /* Get filename. */
194   fn = fn_normalize (filename);
195   len = strlen (fn);
196
197   /* Create handle name with invalid identifier character to prevent
198      conflicts with handles created with FILE HANDLE. */
199   name = xmalloc (len + 2);
200   name[0] = '*';
201   strcpy (&name[1], fn);
202
203   f.name = name;
204   fp = hsh_find (files, &f);
205   if (!fp)
206     {
207       fp = xmalloc (sizeof *fp);
208       init_file_handle (fp);
209       fp->name = name;
210       fp->norm_fn = fn;
211       fp->where.filename = fp->fn = xstrdup (filename);
212       hsh_force_insert (files, fp);
213     }
214   else
215     {
216       free (fn);
217       free (name);
218     }
219   return fp;
220 }
221
222 /* Returns the handle with identifier NAME, if it exists; otherwise
223    reports error to user and returns NULL. */
224 struct file_handle *
225 fh_get_handle_by_name (const char name[9])
226 {
227   struct file_handle f, *fp;
228   f.name = (char *) name;
229   fp = hsh_find (files, &f);
230
231   if (!fp)
232     msg (SE, _("File handle `%s' has not been previously declared on "
233          "FILE HANDLE."), name);
234   return fp;
235 }
236
237 /* Returns the identifier of file HANDLE.  If HANDLE was created by
238    referring to a filename (i.e., DATA LIST FILE='yyy' instead of FILE
239    HANDLE XXX='yyy'), returns the filename, enclosed in double quotes.
240    Return value is in a static buffer.
241
242    Useful for printing error messages about use of file handles.  */
243 const char *
244 fh_handle_name (struct file_handle *h)
245 {
246   static char *buf = NULL;
247
248   if (buf)
249     {
250       free (buf);
251       buf = NULL;
252     }
253   if (!h)
254     return NULL;
255
256   if (h->name[0] == '*')
257     {
258       int len = strlen (h->fn);
259
260       buf = xmalloc (len + 3);
261       strcpy (&buf[1], h->fn);
262       buf[0] = buf[len + 1] = '"';
263       buf[len + 2] = 0;
264       return buf;
265     }
266   return h->name;
267 }
268
269 /* Closes the stdio FILE associated with handle H.  Frees internal
270    buffers associated with that file.  Does *not* destroy the file
271    handle H.  (File handles are permanent during a session.)  */
272 void
273 fh_close_handle (struct file_handle *h)
274 {
275   if (h == NULL)
276     return;
277
278   debug_printf (("Closing %s%s.\n", fh_handle_name (h),
279                  h->class == NULL ? " (already closed)" : ""));
280
281   if (h->class)
282     h->class->close (h);
283   h->class = NULL;
284   h->ext = NULL;
285 }
286
287 /* Hashes the name of file handle H. */
288 static unsigned
289 hash_file_handle (const void *handle_, void *param UNUSED)
290 {
291   const struct file_handle *handle = handle_;
292
293   return hsh_hash_string (handle->name);
294 }
295
296 /* Compares names of file handles A and B. */
297 static int
298 cmp_file_handle (const void *a_, const void *b_, void *foo UNUSED)
299 {
300   const struct file_handle *a = a_;
301   const struct file_handle *b = b_;
302
303   return strcmp (a->name, b->name);
304 }
305
306 /* Initialize the hash of file handles; inserts the "inline file"
307    inline_file. */
308 void
309 fh_init_files (void)
310 {
311   /* Create hash. */
312   files = hsh_create (4, cmp_file_handle, hash_file_handle, NULL, NULL);
313
314   /* Insert inline file. */
315   inline_file = xmalloc (sizeof *inline_file);
316   init_file_handle (inline_file);
317   inline_file->name = "INLINE";
318   inline_file->where.filename
319     = inline_file->fn = inline_file->norm_fn = (char *) _("<Inline File>");
320   inline_file->where.line_number = 0;
321   hsh_force_insert (files, inline_file);
322 }
323
324 /* Parses a file handle name, which may be a filename as a string or
325    a file handle name as an identifier.  Returns the file handle or
326    NULL on failure. */
327 struct file_handle *
328 fh_parse_file_handle (void)
329 {
330   struct file_handle *handle;
331
332   if (token == T_ID)
333     handle = fh_get_handle_by_name (tokid);
334   else if (token == T_STRING)
335     handle = fh_get_handle_by_filename (ds_value (&tokstr));
336   else
337     {
338       lex_error (_("expecting a file name or handle"));
339       return NULL;
340     }
341
342   if (!handle)
343     return NULL;
344   lex_get ();
345
346   return handle;
347 }
348
349 /* Returns the (normalized) filename associated with file handle H. */
350 char *
351 fh_handle_filename (struct file_handle * h)
352 {
353   return h->norm_fn;
354 }
355
356 /* Returns the width of a logical record on file handle H. */
357 size_t
358 fh_record_width (struct file_handle *h)
359 {
360   if (h == inline_file)
361     return 80;
362   else if (h->recform == FH_RF_FIXED)
363     return h->lrecl;
364   else
365     return 1024;
366 }
367
368 /*
369    Local variables:
370    mode: c
371    End:
372 */