Allow output files to overwrite input files (bug #21280). Thanks to
[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/compiler.h>
27 #include <libpspp/hash.h>
28 #include <libpspp/ll.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 "xalloc.h"
36
37 #include "gettext.h"
38 #define _(msgid) gettext (msgid)
39
40 /* File handle. */
41 struct file_handle
42   {
43     struct ll ll;               /* Element in global list. */
44     size_t ref_cnt;             /* Number of references. */
45     char *id;                   /* Identifier token, NULL if none. */
46     char *name;                 /* User-friendly identifying name. */
47     enum fh_referent referent;  /* What the file handle refers to. */
48
49     /* FH_REF_FILE only. */
50     char *file_name;            /* File name as provided by user. */
51     enum fh_mode mode;          /* File mode. */
52
53     /* FH_REF_FILE and FH_REF_INLINE only. */
54     size_t record_width;        /* Length of fixed-format records. */
55     size_t tab_width;           /* Tab width, 0=do not expand tabs. */
56
57     /* FH_REF_SCRATCH only. */
58     struct scratch_handle *sh;  /* Scratch file data. */
59   };
60
61 static struct file_handle *
62 file_handle_from_ll (struct ll *ll)
63 {
64   return ll_data (ll, struct file_handle, ll);
65 }
66
67 /* List of all named handles. */
68 static struct ll_list named_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                                           char *name, enum fh_referent);
79 static void free_handle (struct file_handle *);
80 static void unname_handle (struct file_handle *);
81
82 /* File handle initialization routine. */
83 void
84 fh_init (void)
85 {
86   ll_init (&named_handles);
87   inline_file = create_handle ("INLINE", xstrdup ("INLINE"), FH_REF_INLINE);
88   inline_file->record_width = 80;
89   inline_file->tab_width = 8;
90 }
91
92 /* Removes all named file handles from the global list. */
93 void
94 fh_done (void)
95 {
96   while (!ll_is_empty (&named_handles))
97     unname_handle (file_handle_from_ll (ll_head (&named_handles)));
98 }
99
100 /* Free HANDLE and remove it from the global list. */
101 static void
102 free_handle (struct file_handle *handle)
103 {
104   /* Remove handle from global list. */
105   if (handle->id != NULL)
106     ll_remove (&handle->ll);
107
108   /* Free data. */
109   free (handle->id);
110   free (handle->name);
111   free (handle->file_name);
112   scratch_handle_destroy (handle->sh);
113   free (handle);
114 }
115
116 /* Make HANDLE unnamed, so that it can no longer be referenced by
117    name.  The caller must hold a reference to HANDLE, which is
118    not affected by this function. */
119 static void
120 unname_handle (struct file_handle *handle)
121 {
122   assert (handle->id != NULL);
123   free (handle->id);
124   handle->id = NULL;
125   ll_remove (&handle->ll);
126
127   /* Drop the reference held by the named_handles table. */
128   fh_unref (handle);
129 }
130
131 /* Increments HANDLE's reference count and returns HANDLE. */
132 struct file_handle *
133 fh_ref (struct file_handle *handle)
134 {
135   assert (handle->ref_cnt > 0);
136   handle->ref_cnt++;
137   return handle;
138 }
139
140 /* Decrements HANDLE's reference count.
141    If the reference count drops to 0, HANDLE is destroyed. */
142 void
143 fh_unref (struct file_handle *handle)
144 {
145   if (handle != NULL)
146     {
147       assert (handle->ref_cnt > 0);
148       if (--handle->ref_cnt == 0)
149         free_handle (handle);
150     }
151 }
152
153 /* Make HANDLE unnamed, so that it can no longer be referenced by
154    name.  The caller must hold a reference to HANDLE, which is
155    not affected by this function.
156
157    This function ignores a null pointer as input.  It has no
158    effect on the inline handle, which is always named INLINE.*/
159 void
160 fh_unname (struct file_handle *handle)
161 {
162   assert (handle->ref_cnt > 1);
163   if (handle != fh_inline_file () && handle->id != NULL)
164     unname_handle (handle);
165 }
166
167 /* Returns the handle with the given ID, or a null pointer if
168    there is none. */
169 struct file_handle *
170 fh_from_id (const char *id)
171 {
172   struct file_handle *handle;
173
174   ll_for_each (handle, struct file_handle, ll, &named_handles)
175     if (!strcasecmp (id, handle->id))
176       {
177         handle->ref_cnt++;
178         return handle;
179       }
180
181   return NULL;
182 }
183
184 /* Creates a new handle with identifier ID (which may be null)
185    and name HANDLE_NAME that refers to REFERENT.  Links the new
186    handle into the global list.  Returns the new handle.
187
188    The new handle is not fully initialized.  The caller is
189    responsible for completing its initialization. */
190 static struct file_handle *
191 create_handle (const char *id, char *handle_name, enum fh_referent referent)
192 {
193   struct file_handle *handle = xzalloc (sizeof *handle);
194
195   handle->ref_cnt = 1;
196   handle->id = id != NULL ? xstrdup (id) : NULL;
197   handle->name = handle_name;
198   handle->referent = referent;
199
200   if (id != NULL)
201     {
202       assert (fh_from_id (id) == NULL);
203       ll_push_tail (&named_handles, &handle->ll);
204       handle->ref_cnt++;
205     }
206
207   return handle;
208 }
209
210 /* Returns the unique handle of referent type FH_REF_INLINE,
211    which refers to the "inline file" that represents character
212    data in the command file between BEGIN DATA and END DATA. */
213 struct file_handle *
214 fh_inline_file (void)
215 {
216   fh_ref (inline_file);
217   return inline_file;
218 }
219
220 /* Creates and returns a new file handle with the given ID, which
221    may be null.  If it is non-null, it must be unique among
222    existing file identifiers.  The new handle is associated with
223    file FILE_NAME and the given PROPERTIES. */
224 struct file_handle *
225 fh_create_file (const char *id, const char *file_name,
226                 const struct fh_properties *properties)
227 {
228   char *handle_name;
229   struct file_handle *handle;
230
231   handle_name = id != NULL ? xstrdup (id) : xasprintf ("\"%s\"", file_name);
232   handle = create_handle (id, handle_name, FH_REF_FILE);
233   handle->file_name = xstrdup (file_name);
234   handle->mode = properties->mode;
235   handle->record_width = properties->record_width;
236   handle->tab_width = properties->tab_width;
237   return handle;
238 }
239
240 /* Creates a new file handle with the given ID, which must be
241    unique among existing file identifiers.  The new handle is
242    associated with a scratch file (initially empty). */
243 struct file_handle *
244 fh_create_scratch (const char *id)
245 {
246   struct file_handle *handle;
247   handle = create_handle (id, xstrdup (id), FH_REF_SCRATCH);
248   handle->sh = NULL;
249   return handle;
250 }
251
252 /* Returns a set of default properties for a file handle. */
253 const struct fh_properties *
254 fh_default_properties (void)
255 {
256   static const struct fh_properties default_properties
257     = {FH_MODE_TEXT, 1024, 4};
258   return &default_properties;
259 }
260
261 /* Returns the identifier that may be used in syntax to name the
262    given HANDLE, which takes the form of a PSPP identifier.  If
263    HANDLE has no identifier, returns a null pointer.
264
265    Return value is owned by the file handle.*/
266 const char *
267 fh_get_id (const struct file_handle *handle)
268 {
269   return handle->id;
270 }
271
272 /* Returns a user-friendly string to identify the given HANDLE.
273    If HANDLE was created by referring to a file name, returns the
274    file name, enclosed in double quotes.  Return value is owned
275    by the file handle.
276
277    Useful for printing error messages about use of file handles.  */
278 const char *
279 fh_get_name (const struct file_handle *handle)
280 {
281   return handle->name;
282 }
283
284 /* Returns the type of object that HANDLE refers to. */
285 enum fh_referent
286 fh_get_referent (const struct file_handle *handle)
287 {
288   return handle->referent;
289 }
290
291 /* Returns the name of the file associated with HANDLE. */
292 const char *
293 fh_get_file_name (const struct file_handle *handle)
294 {
295   assert (handle->referent == FH_REF_FILE);
296   return handle->file_name;
297 }
298
299 /* Returns the mode of HANDLE. */
300 enum fh_mode
301 fh_get_mode (const struct file_handle *handle)
302 {
303   assert (handle->referent == FH_REF_FILE);
304   return handle->mode;
305 }
306
307 /* Returns the width of a logical record on HANDLE. */
308 size_t
309 fh_get_record_width (const struct file_handle *handle)
310 {
311   assert (handle->referent & (FH_REF_FILE | FH_REF_INLINE));
312   return handle->record_width;
313 }
314
315 /* Returns the number of characters per tab stop for HANDLE, or
316    zero if tabs are not to be expanded.  Applicable only to
317    FH_MODE_TEXT files. */
318 size_t
319 fh_get_tab_width (const struct file_handle *handle)
320 {
321   assert (handle->referent & (FH_REF_FILE | FH_REF_INLINE));
322   return handle->tab_width;
323 }
324
325 /* Returns the scratch file handle associated with HANDLE.
326    Applicable to only FH_REF_SCRATCH files. */
327 struct scratch_handle *
328 fh_get_scratch_handle (const struct file_handle *handle)
329 {
330   assert (handle->referent == FH_REF_SCRATCH);
331   return handle->sh;
332 }
333
334 /* Sets SH to be the scratch file handle associated with HANDLE.
335    Applicable to only FH_REF_SCRATCH files. */
336 void
337 fh_set_scratch_handle (struct file_handle *handle, struct scratch_handle *sh)
338 {
339   assert (handle->referent == FH_REF_SCRATCH);
340   handle->sh = sh;
341 }
342
343 /* Returns the current default handle. */
344 struct file_handle *
345 fh_get_default_handle (void)
346 {
347   return default_handle ? fh_ref (default_handle) : fh_inline_file ();
348 }
349
350 /* Sets NEW_DEFAULT_HANDLE as the default handle. */
351 void
352 fh_set_default_handle (struct file_handle *new_default_handle)
353 {
354   assert (new_default_handle == NULL
355           || (new_default_handle->referent & (FH_REF_INLINE | FH_REF_FILE)));
356   if (default_handle != NULL)
357     fh_unref (default_handle);
358   default_handle = new_default_handle;
359   if (default_handle != NULL)
360     fh_ref (default_handle);
361 }
362 \f
363 /* Information about a file handle's readers or writers. */
364 struct fh_lock
365   {
366     /* Hash key. */
367     enum fh_referent referent;  /* Type of underlying file. */
368     union
369       {
370         struct file_identity *file; /* FH_REF_FILE only. */
371         unsigned int unique_id;    /* FH_REF_SCRATCH only. */
372       }
373     u;
374     enum fh_access access;      /* Type of file access. */
375
376     /* Number of openers. */
377     size_t open_cnt;
378
379     /* Applicable only when open_cnt > 0. */
380     bool exclusive;             /* No other openers allowed? */
381     const char *type;           /* Human-readable type of file. */
382     void *aux;                  /* Owner's auxiliary data. */
383   };
384
385 /* Hash table of all active locks. */
386 static struct hsh_table *locks;
387
388 static void make_key (struct fh_lock *, const struct file_handle *,
389                       enum fh_access);
390 static void free_key (struct fh_lock *);
391 static int compare_fh_locks (const void *, const void *, const void *);
392 static unsigned int hash_fh_lock (const void *, const void *);
393
394 /* Tries to lock handle H for the given kind of ACCESS and TYPE
395    of file.  Returns a pointer to a struct fh_lock if successful,
396    otherwise a null pointer.
397
398    H's referent type must be one of the bits in MASK.  The caller
399    must verify this ahead of time; we simply assert it here.
400
401    TYPE is the sort of file, e.g. "system file".  Only one type
402    of access is allowed on a given file at a time for reading,
403    and similarly for writing.  If successful, a reference to TYPE
404    is retained, so it should probably be a string literal.
405
406    ACCESS specifies whether the lock is for reading or writing.
407    EXCLUSIVE is true to require exclusive access, false to allow
408    sharing with other accessors.  Exclusive read access precludes
409    other readers, but not writers; exclusive write access
410    precludes other writers, but not readers.  A sharable read or
411    write lock precludes reader or writers, respectively, of a
412    different TYPE.
413
414    A lock may be associated with auxiliary data.  See
415    fh_lock_get_aux and fh_lock_set_aux for more details. */
416 struct fh_lock *
417 fh_lock (struct file_handle *h, enum fh_referent mask UNUSED,
418          const char *type, enum fh_access access, bool exclusive)
419 {
420   struct fh_lock key, *lock;
421   void **lockp;
422
423   assert ((fh_get_referent (h) & mask) != 0);
424   assert (access == FH_ACC_READ || access == FH_ACC_WRITE);
425
426   if (locks == NULL)
427     locks = hsh_create (0, compare_fh_locks, hash_fh_lock, NULL, NULL);
428
429   make_key (&key, h, access);
430   lockp = hsh_probe (locks, &key);
431   if (*lockp == NULL)
432     {
433       lock = *lockp = xmalloc (sizeof *lock);
434       *lock = key;
435       lock->open_cnt = 1;
436       lock->exclusive = exclusive;
437       lock->type = type;
438       lock->aux = NULL;
439     }
440   else
441     {
442       free_key (&key);
443
444       lock = *lockp;
445       if (strcmp (lock->type, type))
446         {
447           if (access == FH_ACC_READ)
448             msg (SE, _("Can't read from %s as a %s because it is "
449                        "already being read as a %s."),
450                  fh_get_name (h), gettext (type), gettext (lock->type));
451           else
452             msg (SE, _("Can't write to %s as a %s because it is "
453                        "already being written as a %s."),
454                  fh_get_name (h), gettext (type), gettext (lock->type));
455           return NULL;
456         }
457       else if (exclusive || lock->exclusive)
458         {
459           msg (SE, _("Can't re-open %s as a %s."),
460                fh_get_name (h), gettext (type));
461           return NULL;
462         }
463       lock->open_cnt++;
464     }
465
466   return lock;
467 }
468
469 /* Releases LOCK that was acquired with fh_lock.
470    Returns true if LOCK is still locked, because other clients
471    also had it locked.
472
473    Returns false if LOCK has now been destroyed.  In this case
474    the caller must ensure that any auxiliary data associated with
475    LOCK is destroyed, to avoid a memory leak.  The caller must
476    obtain a pointer to the auxiliary data, e.g. via
477    fh_lock_get_aux *before* calling fh_unlock (because it yields
478    undefined behavior to call fh_lock_get_aux on a destroyed
479    lock).  */
480 bool
481 fh_unlock (struct fh_lock *lock)
482 {
483   if (lock != NULL)
484     {
485       assert (lock->open_cnt > 0);
486       if (--lock->open_cnt == 0)
487         {
488           hsh_delete (locks, lock);
489           free_key (lock);
490           free (lock);
491           return false;
492         }
493     }
494   return true;
495 }
496
497 /* Returns auxiliary data for LOCK.
498
499    Auxiliary data is shared by every client that holds LOCK (for
500    an exclusive lock, this is a single client).  To avoid leaks,
501    auxiliary data must be released before LOCK is destroyed. */
502 void *
503 fh_lock_get_aux (const struct fh_lock *lock)
504 {
505   return lock->aux;
506 }
507
508 /* Sets the auxiliary data for LOCK to AUX. */
509 void
510 fh_lock_set_aux (struct fh_lock *lock, void *aux)
511 {
512   lock->aux = aux;
513 }
514
515 /* Returns true if HANDLE is locked for the given type of ACCESS,
516    false otherwise. */
517 bool
518 fh_is_locked (const struct file_handle *handle, enum fh_access access)
519 {
520   struct fh_lock key;
521   bool is_locked;
522
523   make_key (&key, handle, access);
524   is_locked = hsh_find (locks, &key) != NULL;
525   free_key (&key);
526
527   return is_locked;
528 }
529
530 /* Initializes the key fields in LOCK for looking up or inserting
531    handle H for the given kind of ACCESS. */
532 static void
533 make_key (struct fh_lock *lock, const struct file_handle *h,
534           enum fh_access access)
535 {
536   lock->referent = fh_get_referent (h);
537   lock->access = access;
538   if (lock->referent == FH_REF_FILE)
539     lock->u.file = fn_get_identity (fh_get_file_name (h));
540   else if (lock->referent == FH_REF_SCRATCH)
541     {
542       struct scratch_handle *sh = fh_get_scratch_handle (h);
543       lock->u.unique_id = sh != NULL ? sh->unique_id : 0;
544     }
545 }
546
547 /* Frees the key fields in LOCK. */
548 static void
549 free_key (struct fh_lock *lock)
550 {
551   if (lock->referent == FH_REF_FILE)
552     fn_free_identity (lock->u.file);
553 }
554
555 /* Compares the key fields in struct fh_lock objects A and B and
556    returns a strcmp()-type result. */
557 static int
558 compare_fh_locks (const void *a_, const void *b_, const void *aux UNUSED)
559 {
560   const struct fh_lock *a = a_;
561   const struct fh_lock *b = b_;
562
563   if (a->referent != b->referent)
564     return a->referent < b->referent ? -1 : 1;
565   else if (a->access != b->access)
566     return a->access < b->access ? -1 : 1;
567   else if (a->referent == FH_REF_FILE)
568     return fn_compare_file_identities (a->u.file, b->u.file);
569   else if (a->referent == FH_REF_SCRATCH)
570     return (a->u.unique_id < b->u.unique_id ? -1
571             : a->u.unique_id > b->u.unique_id);
572   else
573     return 0;
574 }
575
576 /* Returns a hash value for LOCK. */
577 static unsigned int
578 hash_fh_lock (const void *lock_, const void *aux UNUSED)
579 {
580   const struct fh_lock *lock = lock_;
581   unsigned int hash = hsh_hash_int ((lock->referent << 3) | lock->access);
582   if (lock->referent == FH_REF_FILE)
583     hash ^= fn_hash_identity (lock->u.file);
584   else if (lock->referent == FH_REF_SCRATCH)
585     hash ^= hsh_hash_int (lock->u.unique_id);
586   return hash;
587 }