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