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