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