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