Test both compressed and uncompressed system files with very long
[pspp] / src / data / file-handle-def.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc.
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <config.h>
18
19 #include "file-handle-def.h"
20
21 #include <assert.h>
22 #include <errno.h>
23 #include <stdlib.h>
24 #include <string.h>
25
26 #include <libpspp/alloc.h>
27 #include <libpspp/compiler.h>
28 #include <libpspp/magic.h>
29 #include <libpspp/message.h>
30 #include <libpspp/str.h>
31 #include <data/file-name.h>
32 #include <data/variable.h>
33 #include <data/scratch-handle.h>
34
35 #include "gettext.h"
36 #define _(msgid) gettext (msgid)
37
38 /* (headers) */
39
40 /* File handle. */
41 struct file_handle
42   {
43     struct file_handle *next;   /* Next in global list. */
44     int open_cnt;               /* 0=not open, otherwise # of openers. */
45     bool deleted;               /* Destroy handle when open_cnt goes to 0? */
46
47     char id[LONG_NAME_LEN + 1]; /* Identifier token; empty string if none. */
48     char *name;                 /* User-friendly identifying name. */
49     const char *type;           /* If open, type of file. */
50     char open_mode[3];          /* "[rw][se]". */
51     void *aux;                  /* Aux data pointer for owner if any. */
52     enum fh_referent referent;  /* What the file handle refers to. */
53
54     /* FH_REF_FILE only. */
55     char *file_name;            /* File name as provided by user. */
56     struct file_identity *identity; /* For checking file identity. */
57     enum fh_mode mode;          /* File mode. */
58
59     /* FH_REF_FILE and FH_REF_INLINE only. */
60     size_t record_width;        /* Length of fixed-format records. */
61     size_t tab_width;           /* Tab width, 0=do not expand tabs. */
62
63     /* FH_REF_SCRATCH only. */
64     struct scratch_handle *sh;  /* Scratch file data. */
65   };
66
67 /* List of all handles. */
68 static struct file_handle *file_handles;
69
70 /* Default file handle for DATA LIST, REREAD, REPEATING DATA
71    commands. */
72 static struct file_handle *default_handle;
73
74 /* The "file" that reads from BEGIN DATA...END DATA. */
75 static struct file_handle *inline_file;
76
77 static struct file_handle *create_handle (const char *id,
78                                           const char *name, enum fh_referent);
79
80 /* File handle initialization routine. */
81 void
82 fh_init (void)
83 {
84   inline_file = create_handle ("INLINE", "INLINE", FH_REF_INLINE);
85   inline_file->record_width = 80;
86   inline_file->tab_width = 8;
87 }
88
89 /* Free HANDLE and remove it from the global list. */
90 static void
91 free_handle (struct file_handle *handle)
92 {
93   /* Remove handle from global list. */
94   if (file_handles == handle)
95     file_handles = handle->next;
96   else
97     {
98       struct file_handle *iter = file_handles;
99       while (iter->next != handle)
100         iter = iter->next;
101       iter->next = handle->next;
102     }
103
104   /* Free data. */
105   free (handle->name);
106   free (handle->file_name);
107   fn_free_identity (handle->identity);
108   scratch_handle_destroy (handle->sh);
109   free (handle);
110 }
111
112 /* Frees all the file handles. */
113 void
114 fh_done (void)
115 {
116   while (file_handles != NULL)
117     free_handle (file_handles);
118 }
119
120 /* Returns the handle with the given ID, or a null pointer if
121    there is none. */
122 struct file_handle *
123 fh_from_id (const char *id)
124 {
125   struct file_handle *iter;
126
127   for (iter = file_handles; iter != NULL; iter = iter->next)
128     if (!iter->deleted && !strcasecmp (id, iter->id))
129       return iter;
130   return NULL;
131 }
132
133 /* Returns the handle for the file named FILE_NAME,
134    or a null pointer if none exists.
135    Different names for the same file (e.g. "x" and "./x") are
136    considered equivalent. */
137 struct file_handle *
138 fh_from_file_name (const char *file_name)
139 {
140   struct file_identity *identity;
141   struct file_handle *iter;
142
143   /* First check for a file with the same identity. */
144   identity = fn_get_identity (file_name);
145   if (identity != NULL)
146     {
147       for (iter = file_handles; iter != NULL; iter = iter->next)
148         if (!iter->deleted
149             && iter->referent == FH_REF_FILE
150             && iter->identity != NULL
151             && !fn_compare_file_identities (identity, iter->identity))
152           {
153             fn_free_identity (identity);
154             return iter;
155           }
156       fn_free_identity (identity);
157     }
158
159   /* Then check for a file with the same name. */
160   for (iter = file_handles; iter != NULL; iter = iter->next)
161     if (!iter->deleted
162         && iter->referent == FH_REF_FILE && !strcmp (file_name, iter->file_name))
163       return iter;
164
165   return NULL;
166 }
167
168 /* Creates a new handle with identifier ID (which may be null)
169    and name HANDLE_NAME that refers to REFERENT.  Links the new
170    handle into the global list.  Returns the new handle.
171
172    The new handle is not fully initialized.  The caller is
173    responsible for completing its initialization. */
174 static struct file_handle *
175 create_handle (const char *id, const char *handle_name,
176                enum fh_referent referent)
177 {
178   struct file_handle *handle = xzalloc (sizeof *handle);
179   assert (id == NULL || fh_from_id (id) == NULL);
180   handle->next = file_handles;
181   handle->open_cnt = 0;
182   handle->deleted = false;
183   str_copy_trunc (handle->id, sizeof handle->id, id != NULL ? id : "");
184   handle->name = xstrdup (handle_name);
185   handle->type = NULL;
186   handle->aux = NULL;
187   handle->referent = referent;
188   file_handles = handle;
189   return handle;
190 }
191
192 /* Returns the unique handle of referent type FH_REF_INLINE,
193    which refers to the "inline file" that represents character
194    data in the command file between BEGIN DATA and END DATA. */
195 struct file_handle *
196 fh_inline_file (void)
197 {
198   return inline_file;
199 }
200
201 /* Creates a new file handle with the given ID, which may be
202    null.  If it is non-null, it must be unique among existing
203    file identifiers.  The new handle is associated with file
204    FILE_NAME and the given PROPERTIES. */
205 struct file_handle *
206 fh_create_file (const char *id, const char *file_name,
207                 const struct fh_properties *properties)
208 {
209   char *handle_name;
210   struct file_handle *handle;
211
212   handle_name = id != NULL ? (char *) id : xasprintf ("\"%s\"", file_name);
213   handle = create_handle (id, handle_name, FH_REF_FILE);
214   if (id == NULL)
215     free (handle_name);
216   handle->file_name = xstrdup (file_name);
217   handle->identity = fn_get_identity (file_name);
218   handle->mode = properties->mode;
219   handle->record_width = properties->record_width;
220   handle->tab_width = properties->tab_width;
221   return handle;
222 }
223
224 /* Creates a new file handle with the given ID, which must be
225    unique among existing file identifiers.  The new handle is
226    associated with a scratch file (initially empty). */
227 struct file_handle *
228 fh_create_scratch (const char *id)
229 {
230   struct file_handle *handle;
231   assert (id != NULL);
232   handle = create_handle (id, id, FH_REF_SCRATCH);
233   handle->sh = NULL;
234   return handle;
235 }
236
237 /* Returns a set of default properties for a file handle. */
238 const struct fh_properties *
239 fh_default_properties (void)
240 {
241   static const struct fh_properties default_properties
242     = {FH_MODE_TEXT, 1024, 4};
243   return &default_properties;
244 }
245
246 /* Deletes FH from the global list of file handles.  Afterward,
247    attempts to search for it will fail.  Unless the file handle
248    is currently open, it will be destroyed; otherwise, it will be
249    destroyed later when it is closed.
250    Normally needed only if a file_handle needs to be re-assigned.
251    Otherwise, just let fh_done() destroy the handle. */
252 void
253 fh_free (struct file_handle *handle)
254 {
255   if (handle == fh_inline_file () || handle == NULL || handle->deleted)
256     return;
257   handle->deleted = true;
258
259   if (handle == default_handle)
260     default_handle = fh_inline_file ();
261
262   if (handle->open_cnt == 0)
263     free_handle (handle);
264 }
265
266 /* Returns an English description of MODE,
267    which is in the format of the MODE argument to fh_open(). */
268 static const char *
269 mode_name (const char *mode)
270 {
271   assert (mode != NULL);
272   assert (mode[0] == 'r' || mode[0] == 'w');
273
274   return mode[0] == 'r' ? "reading" : "writing";
275 }
276
277 /* Tries to open handle H with the given TYPE and MODE.
278
279    H's referent type must be one of the bits in MASK.  The caller
280    must verify this ahead of time; we simply assert it here.
281
282    TYPE is the sort of file, e.g. "system file".  Only one given
283    type of access is allowed on a given file handle at once.
284    If successful, a reference to TYPE is retained, so it should
285    probably be a string literal.
286
287    MODE combines the read or write mode with the sharing mode.
288    The first character is 'r' for read, 'w' for write.  The
289    second character is 's' to permit sharing, 'e' to require
290    exclusive access.
291
292    Returns the address of a void * that the caller can use for
293    data specific to the file handle if successful, or a null
294    pointer on failure.  For exclusive access modes the void *
295    will always be a null pointer at return.  In shared access
296    modes the void * will necessarily be null only if no other
297    sharers are active. */
298 void **
299 fh_open (struct file_handle *h, enum fh_referent mask UNUSED,
300          const char *type, const char *mode)
301 {
302   assert (h != NULL);
303   assert ((fh_get_referent (h) & mask) != 0);
304   assert (type != NULL);
305   assert (mode != NULL);
306   assert (mode[0] == 'r' || mode[0] == 'w');
307   assert (mode[1] == 's' || mode[1] == 'e');
308   assert (mode[2] == '\0');
309
310   if (h->open_cnt != 0)
311     {
312       if (strcmp (h->type, type))
313         {
314           msg (SE, _("Can't open %s as a %s because it is "
315                      "already open as a %s."),
316                fh_get_name (h), type, h->type);
317           return NULL;
318         }
319       else if (strcmp (h->open_mode, mode))
320         {
321           msg (SE, _("Can't open %s as a %s for %s because it is "
322                      "already open for %s."),
323                fh_get_name (h), type, mode_name (mode),
324                mode_name (h->open_mode));
325           return NULL;
326         }
327       else if (h->open_mode[1] == 'e')
328         {
329           msg (SE, _("Can't re-open %s as a %s for %s."),
330                fh_get_name (h), type, mode_name (mode));
331           return NULL;
332         }
333     }
334   else
335     {
336       h->type = type;
337       strcpy (h->open_mode, mode);
338       assert (h->aux == NULL);
339     }
340   h->open_cnt++;
341
342   return &h->aux;
343 }
344
345 /* Closes file handle H, which must have been open for the
346    specified TYPE and MODE of access provided to fh_open().
347    Returns zero if the file is now closed, nonzero if it is still
348    open due to another reference.
349
350    After fh_close() returns zero for a handle, it is unsafe to
351    reference that file handle again in any way, because its
352    storage may have been freed. */
353 int
354 fh_close (struct file_handle *h, const char *type, const char *mode)
355 {
356   assert (h != NULL);
357   assert (h->open_cnt > 0);
358   assert (type != NULL);
359   assert (!strcmp (type, h->type));
360   assert (mode != NULL);
361   assert (!strcmp (mode, h->open_mode));
362
363   if (--h->open_cnt == 0)
364     {
365       h->type = NULL;
366       h->aux = NULL;
367       if (h->deleted)
368         free_handle (h);
369       return 0;
370     }
371   return 1;
372 }
373
374 /* Is the file open?  BEGIN DATA...END DATA uses this to detect
375    whether the inline file is actually in use. */
376 bool
377 fh_is_open (const struct file_handle *handle)
378 {
379   return handle->open_cnt > 0;
380 }
381
382 /* Returns the identifier that may be used in syntax to name the
383    given HANDLE, which takes the form of a PSPP identifier.  If
384    HANDLE has no identifier, returns a null pointer.
385
386    Return value is owned by the file handle.*/
387 const char *
388 fh_get_id (const struct file_handle *handle)
389 {
390   return handle->id[0] != '\0' ? handle->id : NULL;
391 }
392
393 /* Returns a user-friendly string to identify the given HANDLE.
394    If HANDLE was created by referring to a file name, returns the
395    file name, enclosed in double quotes.  Return value is owned
396    by the file handle.
397
398    Useful for printing error messages about use of file handles.  */
399 const char *
400 fh_get_name (const struct file_handle *handle)
401 {
402   return handle->name;
403 }
404
405 /* Returns the type of object that HANDLE refers to. */
406 enum fh_referent
407 fh_get_referent (const struct file_handle *handle)
408 {
409   return handle->referent;
410 }
411
412 /* Returns the name of the file associated with HANDLE. */
413 const char *
414 fh_get_file_name (const struct file_handle *handle)
415 {
416   assert (handle->referent == FH_REF_FILE);
417   return handle->file_name;
418 }
419
420 /* Returns the mode of HANDLE. */
421 enum fh_mode
422 fh_get_mode (const struct file_handle *handle)
423 {
424   assert (handle->referent == FH_REF_FILE);
425   return handle->mode;
426 }
427
428 /* Returns the width of a logical record on HANDLE. */
429 size_t
430 fh_get_record_width (const struct file_handle *handle)
431 {
432   assert (handle->referent & (FH_REF_FILE | FH_REF_INLINE));
433   return handle->record_width;
434 }
435
436 /* Returns the number of characters per tab stop for HANDLE, or
437    zero if tabs are not to be expanded.  Applicable only to
438    FH_MODE_TEXT files. */
439 size_t
440 fh_get_tab_width (const struct file_handle *handle)
441 {
442   assert (handle->referent & (FH_REF_FILE | FH_REF_INLINE));
443   return handle->tab_width;
444 }
445
446 /* Returns the scratch file handle associated with HANDLE.
447    Applicable to only FH_REF_SCRATCH files. */
448 struct scratch_handle *
449 fh_get_scratch_handle (struct file_handle *handle)
450 {
451   assert (handle->referent == FH_REF_SCRATCH);
452   return handle->sh;
453 }
454
455 /* Sets SH to be the scratch file handle associated with HANDLE.
456    Applicable to only FH_REF_SCRATCH files. */
457 void
458 fh_set_scratch_handle (struct file_handle *handle, struct scratch_handle *sh)
459 {
460   assert (handle->referent == FH_REF_SCRATCH);
461   handle->sh = sh;
462 }
463
464 /* Returns the current default handle. */
465 struct file_handle *
466 fh_get_default_handle (void)
467 {
468   return default_handle ? default_handle : fh_inline_file ();
469 }
470
471 /* Sets NEW_DEFAULT_HANDLE as the default handle. */
472 void
473 fh_set_default_handle (struct file_handle *new_default_handle)
474 {
475   assert (new_default_handle == NULL
476           || (new_default_handle->referent & (FH_REF_INLINE | FH_REF_FILE)));
477   default_handle = new_default_handle;
478 }