Fix assertion for proper Huffman merge pattern: 0 == 1 modulo 1.
[pspp] / 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. */
36 struct file_handle 
37   {
38     struct file_handle *next;   /* Next in global list. */
39     char *name;                 /* File handle identifier. */
40     char *filename;             /* Filename as provided by user. */
41     struct file_identity *identity; /* For checking file identity. */
42     struct file_locator where;  /* Used for reporting error messages. */
43     enum file_handle_mode mode; /* File mode. */
44     size_t length;              /* Length of fixed-format records. */
45     size_t tab_width;           /* Tab width, 0=do not expand tabs. */
46
47     int open_cnt;               /* 0=not open, otherwise # of openers. */
48     const char *type;           /* If open, type of file. */
49     const char *open_mode;      /* "[rw][se]". */
50     void *aux;                  /* Aux data pointer for owner if any. */
51   };
52
53 static struct file_handle *file_handles;
54
55 static struct file_handle *create_file_handle (const char *handle_name,
56                                                const char *filename);
57
58 /* (specification)
59    "FILE HANDLE" (fh_):
60      name=string;
61      lrecl=integer;
62      tabwidth=integer "x>=0" "%s must be nonnegative";
63      mode=mode:!character/image.
64 */
65 /* (declarations) */
66 /* (functions) */
67
68 static struct file_handle *
69 get_handle_with_name (const char *handle_name) 
70 {
71   struct file_handle *iter;
72
73   for (iter = file_handles; iter != NULL; iter = iter->next)
74     if (!strcmp (handle_name, iter->name))
75       return iter;
76   return NULL;
77 }
78
79 static struct file_handle *
80 get_handle_for_filename (const char *filename)
81 {
82   struct file_identity *identity;
83   struct file_handle *iter;
84       
85   /* First check for a file with the same identity. */
86   identity = fn_get_identity (filename);
87   if (identity != NULL) 
88     {
89       for (iter = file_handles; iter != NULL; iter = iter->next)
90         if (iter->identity != NULL
91             && !fn_compare_file_identities (identity, iter->identity))
92           {
93             fn_free_identity (identity);
94             return iter; 
95           }
96       fn_free_identity (identity);
97     }
98
99   /* Then check for a file with the same name. */
100   for (iter = file_handles; iter != NULL; iter = iter->next)
101     if (!strcmp (filename, iter->filename))
102       return iter; 
103
104   return NULL;
105 }
106
107 int
108 cmd_file_handle (void)
109 {
110   char handle_name[9];
111
112   struct cmd_file_handle cmd;
113   struct file_handle *handle;
114
115   if (!lex_force_id ())
116     return CMD_FAILURE;
117   strcpy (handle_name, tokid);
118
119   handle = get_handle_with_name (handle_name);
120   if (handle != NULL)
121     {
122       msg (SE, _("File handle %s already refers to "
123                  "file %s.  File handle cannot be redefined within a "
124                  "session."),
125            tokid, handle->filename);
126       return CMD_FAILURE;
127     }
128
129   lex_get ();
130   if (!lex_force_match ('/'))
131     return CMD_FAILURE;
132
133   if (!parse_file_handle (&cmd))
134     return CMD_FAILURE;
135
136   if (token != '.')
137     {
138       lex_error (_("expecting end of command"));
139       goto lossage;
140     }
141
142   if (cmd.s_name == NULL)
143     {
144       msg (SE, _("The FILE HANDLE required subcommand NAME "
145                  "is not present."));
146       goto lossage;
147     }
148
149   handle = create_file_handle (handle_name, cmd.s_name);
150   switch (cmd.mode)
151     {
152     case FH_CHARACTER:
153       handle->mode = MODE_TEXT;
154       if (cmd.sbc_tabwidth)
155         handle->tab_width = cmd.n_tabwidth[0];
156       else
157         handle->tab_width = 4;
158       break;
159     case FH_IMAGE:
160       handle->mode = MODE_BINARY;
161       if (cmd.n_lrecl[0] == NOT_LONG)
162         {
163           msg (SE, _("Fixed-length records were specified on /RECFORM, but "
164                      "record length was not specified on /LRECL.  "
165                      "Assuming 1024-character records."));
166           handle->length = 1024;
167         }
168       else if (cmd.n_lrecl[0] < 1)
169         {
170           msg (SE, _("Record length (%ld) must be at least one byte.  "
171                      "1-character records will be assumed."), cmd.n_lrecl);
172           handle->length = 1;
173         }
174       else
175         handle->length = cmd.n_lrecl[0];
176       break;
177     default:
178       assert (0);
179     }
180
181   return CMD_SUCCESS;
182
183  lossage:
184   free_file_handle (&cmd);
185   return CMD_FAILURE;
186 }
187 \f
188 /* File handle functions. */
189
190 /* Creates and returns a new file handle with the given values
191    and defaults for other values.  Adds the created file handle
192    to the global list. */
193 static struct file_handle *
194 create_file_handle (const char *handle_name, const char *filename)
195 {
196   struct file_handle *handle;
197
198   /* Create and initialize file handle. */
199   handle = xmalloc (sizeof *handle);
200   handle->next = file_handles;
201   handle->name = xstrdup (handle_name);
202   handle->filename = xstrdup (filename);
203   handle->identity = fn_get_identity (filename);
204   handle->where.filename = handle->filename;
205   handle->where.line_number = 0;
206   handle->mode = MODE_TEXT;
207   handle->length = 1024;
208   handle->tab_width = 4;
209   handle->open_cnt = 0;
210   handle->type = NULL;
211   handle->open_mode = NULL;
212   handle->aux = NULL;
213   file_handles = handle;
214
215   return handle;
216 }
217
218 static const char *
219 mode_name (const char *mode) 
220 {
221   assert (mode != NULL);
222   assert (mode[0] == 'r' || mode[0] == 'w');
223
224   return mode[0] == 'r' ? "reading" : "writing";
225 }
226
227
228 /* Tries to open FILE with the given TYPE and MODE.
229
230    TYPE is the sort of file, e.g. "system file".  Only one given
231    type of access is allowed on a given file handle at once.
232
233    MODE combines the read or write mode with the sharing mode.
234    The first character is 'r' for read, 'w' for write.  The
235    second character is 's' to permit sharing, 'e' to require
236    exclusive access.
237
238    Returns the address of a void * that the caller can use for
239    data specific to the file handle if successful, or a null
240    pointer on failure.  For exclusive access modes the void *
241    will always be a null pointer at return.  In shared access
242    modes the void * will necessarily be null only if no other
243    sharers are active.
244
245    If successful, references to type and mode are retained, so
246    they should probably be string literals. */
247 void **
248 fh_open (struct file_handle *h, const char *type, const char *mode) 
249 {
250   assert (h != NULL);
251   assert (type != NULL);
252   assert (mode != NULL);
253   assert (mode[0] == 'r' || mode[0] == 'w');
254   assert (mode[1] == 's' || mode[1] == 'e');
255   assert (mode[2] == '\0');
256
257   if (h->open_cnt != 0) 
258     {
259       if (strcmp (h->type, type))
260         msg (SE, _("Can't open %s as a %s because it is "
261                    "already open as a %s"),
262              handle_get_name (h), type, h->type);
263       else if (strcmp (h->open_mode, mode))
264         msg (SE, _("Can't open %s as a %s for %s because it is "
265                    "already open for %s"),
266              handle_get_name (h), type,
267              mode_name (mode), mode_name (h->open_mode));
268       else if (h->open_mode[1] == 'e')
269         msg (SE, _("Can't re-open %s as a %s for %s"),
270              handle_get_name (h), type, mode_name (mode));
271     }
272   else 
273     {
274       h->type = type;
275       h->open_mode = mode;
276       assert (h->aux == NULL);
277     }
278   h->open_cnt++;
279
280   return &h->aux;
281 }
282
283 /* Closes file handle H, which must have been open for the
284    specified TYPE and MODE of access provided to fh_open().
285    Returns zero if the file is now closed, nonzero if it is still
286    open due to another reference. */
287 int
288 fh_close (struct file_handle *h, const char *type, const char *mode)
289 {
290   assert (h != NULL);
291   assert (h->open_cnt > 0);
292   assert (type != NULL);
293   assert (!strcmp (type, h->type));
294   assert (mode != NULL);
295   assert (!strcmp (mode, h->open_mode));
296
297   h->open_cnt--;
298   if (h->open_cnt == 0) 
299     {
300       h->type = NULL;
301       h->open_mode = NULL;
302       h->aux = NULL;
303     }
304   return h->open_cnt;
305 }
306
307 /* Parses a file handle name, which may be a filename as a string or
308    a file handle name as an identifier.  Returns the file handle or
309    NULL on failure. */
310 struct file_handle *
311 fh_parse (void)
312 {
313   struct file_handle *handle;
314
315   if (token != T_ID && token != T_STRING)
316     {
317       lex_error (_("expecting a file name or handle name"));
318       return NULL;
319     }
320
321   /* Check for named handles first, then go by filename. */
322   handle = NULL;
323   if (token == T_ID) 
324     handle = get_handle_with_name (tokid);
325   if (handle == NULL)
326     handle = get_handle_for_filename (ds_c_str (&tokstr));
327   if (handle == NULL) 
328     {
329       char *filename = ds_c_str (&tokstr);
330       char *handle_name = xmalloc (strlen (filename) + 3);
331       sprintf (handle_name, "\"%s\"", filename);
332       handle = create_file_handle (handle_name, filename);
333       free (handle_name);
334     }
335
336   lex_get ();
337
338   return handle;
339 }
340
341 /* Returns the identifier of file HANDLE.  If HANDLE was created
342    by referring to a filename instead of a handle name, returns
343    the filename, enclosed in double quotes.  Return value is
344    owned by the file handle. 
345
346    Useful for printing error messages about use of file handles.  */
347 const char *
348 handle_get_name (const struct file_handle *handle)
349 {
350   assert (handle != NULL);
351   return handle->name;
352 }
353
354 /* Returns the name of the file associated with HANDLE. */
355 const char *
356 handle_get_filename (const struct file_handle *handle) 
357 {
358   assert (handle != NULL);
359   return handle->filename;
360 }
361
362 /* Returns the mode of HANDLE. */
363 enum file_handle_mode
364 handle_get_mode (const struct file_handle *handle) 
365 {
366   assert (handle != NULL);
367   return handle->mode;
368 }
369
370 /* Returns the width of a logical record on HANDLE.  Applicable
371    only to MODE_BINARY files.  */
372 size_t
373 handle_get_record_width (const struct file_handle *handle)
374 {
375   assert (handle != NULL);
376   return handle->length;
377 }
378
379 /* Returns the number of characters per tab stop for HANDLE, or
380    zero if tabs are not to be expanded.  Applicable only to
381    MODE_TEXT files. */
382 size_t
383 handle_get_tab_width (const struct file_handle *handle) 
384 {
385   assert (handle != NULL);
386   return handle->tab_width;
387 }
388
389 /*
390    Local variables:
391    mode: c
392    End:
393 */