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