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