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