gui: Fix behavior of print preview in psppire-output-window.
[pspp] / 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 "data/dataset.h"
27 #include "data/file-name.h"
28 #include "data/variable.h"
29 #include "libpspp/compiler.h"
30 #include "libpspp/hmap.h"
31 #include "libpspp/i18n.h"
32 #include "libpspp/message.h"
33 #include "libpspp/str.h"
34 #include "libpspp/hash-functions.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_DATASET only. */
60     struct dataset *ds;         /* Dataset. */
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   free (handle);
114 }
115
116 /* Make HANDLE unnamed, so that it can no longer be referenced by
117    name.  The caller must hold a reference to HANDLE, which is
118    not affected by this function. */
119 static void
120 unname_handle (struct file_handle *handle)
121 {
122   assert (handle->id != NULL);
123   free (handle->id);
124   handle->id = NULL;
125   hmap_delete (&named_handles, &handle->name_node);
126
127   /* Drop the reference held by the named_handles table. */
128   fh_unref (handle);
129 }
130
131 /* Increments HANDLE's reference count and returns HANDLE. */
132 struct file_handle *
133 fh_ref (struct file_handle *handle)
134 {
135   assert (handle->ref_cnt > 0);
136   handle->ref_cnt++;
137   return handle;
138 }
139
140 /* Decrements HANDLE's reference count.
141    If the reference count drops to 0, HANDLE is destroyed. */
142 void
143 fh_unref (struct file_handle *handle)
144 {
145   if (handle != NULL)
146     {
147       assert (handle->ref_cnt > 0);
148       if (--handle->ref_cnt == 0)
149         free_handle (handle);
150     }
151 }
152
153 /* Make HANDLE unnamed, so that it can no longer be referenced by
154    name.  The caller must hold a reference to HANDLE, which is
155    not affected by this function.
156
157    This function ignores a null pointer as input.  It has no
158    effect on the inline handle, which is always named INLINE.*/
159 void
160 fh_unname (struct file_handle *handle)
161 {
162   assert (handle->ref_cnt > 1);
163   if (handle != fh_inline_file () && handle->id != NULL)
164     unname_handle (handle);
165 }
166
167 /* Returns the handle with the given ID, or a null pointer if
168    there is none. */
169 struct file_handle *
170 fh_from_id (const char *id)
171 {
172   struct file_handle *handle;
173
174   HMAP_FOR_EACH_WITH_HASH (handle, struct file_handle, name_node,
175                            hash_case_string (id, 0), &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       hmap_insert (&named_handles, &handle->name_node,
205                    hash_case_string (handle->id, 0));
206       handle->ref_cnt++;
207     }
208
209   return handle;
210 }
211
212 /* Returns the unique handle of referent type FH_REF_INLINE,
213    which refers to the "inline file" that represents character
214    data in the command file between BEGIN DATA and END DATA. */
215 struct file_handle *
216 fh_inline_file (void)
217 {
218   fh_ref (inline_file);
219   return inline_file;
220 }
221
222 /* Creates and returns a new file handle with the given ID, which may be null.
223    If it is non-null, it must be a UTF-8 encoded string that is unique among
224    existing file identifiers.  The new handle is associated with file FILE_NAME
225    and the given PROPERTIES. */
226 struct file_handle *
227 fh_create_file (const char *id, const char *file_name,
228                 const struct fh_properties *properties)
229 {
230   char *handle_name;
231   struct file_handle *handle;
232
233   handle_name = id != NULL ? xstrdup (id) : xasprintf ("`%s'", file_name);
234   handle = create_handle (id, handle_name, FH_REF_FILE);
235   handle->file_name = xstrdup (file_name);
236   handle->mode = properties->mode;
237   handle->record_width = properties->record_width;
238   handle->tab_width = properties->tab_width;
239   handle->encoding = properties->encoding;
240   return handle;
241 }
242
243 /* Creates a new file handle with the given ID, which must be
244    unique among existing file identifiers.  The new handle is
245    associated with a dataset file (initially empty). */
246 struct file_handle *
247 fh_create_dataset (struct dataset *ds)
248 {
249   const char *name;
250   struct file_handle *handle;
251
252   name = dataset_name (ds);
253   if (name[0] == '\0')
254     name = _("active dataset");
255
256   handle = create_handle (NULL, xstrdup (name), FH_REF_DATASET);
257   handle->ds = ds;
258   return handle;
259 }
260
261 /* Returns a set of default properties for a file handle. */
262 const struct fh_properties *
263 fh_default_properties (void)
264 {
265   static const struct fh_properties default_properties
266     = {FH_MODE_TEXT, 1024, 4, C_ENCODING};
267   return &default_properties;
268 }
269
270 /* Returns the identifier that may be used in syntax to name the
271    given HANDLE, which takes the form of a PSPP identifier.  If
272    HANDLE has no identifier, returns a null pointer.
273
274    Return value is owned by the file handle.*/
275 const char *
276 fh_get_id (const struct file_handle *handle)
277 {
278   return handle->id;
279 }
280
281 /* Returns a user-friendly string to identify the given HANDLE.
282    If HANDLE was created by referring to a file name, returns the
283    file name, enclosed in double quotes.  Return value is owned
284    by the file handle.
285
286    Useful for printing error messages about use of file handles.  */
287 const char *
288 fh_get_name (const struct file_handle *handle)
289 {
290   return handle->name;
291 }
292
293 /* Returns the type of object that HANDLE refers to. */
294 enum fh_referent
295 fh_get_referent (const struct file_handle *handle)
296 {
297   return handle->referent;
298 }
299
300 /* Returns the name of the file associated with HANDLE. */
301 const char *
302 fh_get_file_name (const struct file_handle *handle)
303 {
304   assert (handle->referent == FH_REF_FILE);
305   return handle->file_name;
306 }
307
308 /* Returns the mode of HANDLE. */
309 enum fh_mode
310 fh_get_mode (const struct file_handle *handle)
311 {
312   assert (handle->referent == FH_REF_FILE);
313   return handle->mode;
314 }
315
316 /* Returns the width of a logical record on HANDLE. */
317 size_t
318 fh_get_record_width (const struct file_handle *handle)
319 {
320   assert (handle->referent & (FH_REF_FILE | FH_REF_INLINE));
321   return handle->record_width;
322 }
323
324 /* Returns the number of characters per tab stop for HANDLE, or
325    zero if tabs are not to be expanded.  Applicable only to
326    FH_MODE_TEXT files. */
327 size_t
328 fh_get_tab_width (const struct file_handle *handle)
329 {
330   assert (handle->referent & (FH_REF_FILE | FH_REF_INLINE));
331   return handle->tab_width;
332 }
333
334 /* Returns the encoding of characters read from HANDLE. */
335 const char *
336 fh_get_legacy_encoding (const struct file_handle *handle)
337 {
338   assert (handle->referent & (FH_REF_FILE | FH_REF_INLINE));
339   return (handle->referent == FH_REF_FILE ? handle->encoding : C_ENCODING);
340 }
341
342 /* Returns the dataset handle associated with HANDLE.
343    Applicable to only FH_REF_DATASET files. */
344 struct dataset *
345 fh_get_dataset (const struct file_handle *handle)
346 {
347   assert (handle->referent == FH_REF_DATASET);
348   return handle->ds;
349 }
350
351 /* Returns the current default handle. */
352 struct file_handle *
353 fh_get_default_handle (void)
354 {
355   return default_handle ? fh_ref (default_handle) : fh_inline_file ();
356 }
357
358 /* Sets NEW_DEFAULT_HANDLE as the default handle. */
359 void
360 fh_set_default_handle (struct file_handle *new_default_handle)
361 {
362   assert (new_default_handle == NULL
363           || (new_default_handle->referent & (FH_REF_INLINE | FH_REF_FILE)));
364   if (default_handle != NULL)
365     fh_unref (default_handle);
366   default_handle = new_default_handle;
367   if (default_handle != NULL)
368     fh_ref (default_handle);
369 }
370 \f
371 /* Information about a file handle's readers or writers. */
372 struct fh_lock
373   {
374     struct hmap_node node;      /* hmap_node member. */
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_DATASET 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
396 static void make_key (struct fh_lock *, const struct file_handle *,
397                       enum fh_access);
398 static void free_key (struct fh_lock *);
399 static int compare_fh_locks (const struct fh_lock *a, const struct fh_lock *b);
400 static unsigned int hash_fh_lock (const struct fh_lock *lock);
401
402 /* Tries to lock handle H for the given kind of ACCESS and TYPE
403    of file.  Returns a pointer to a struct fh_lock if successful,
404    otherwise a null pointer.
405
406    H's referent type must be one of the bits in MASK.  The caller
407    must verify this ahead of time; we simply assert it here.
408
409    TYPE is the sort of file, e.g. "system file".  Only one type
410    of access is allowed on a given file at a time for reading,
411    and similarly for writing.  If successful, a reference to TYPE
412    is retained, so it should probably be a string literal.
413
414    TYPE should be marked with N_() in the caller: that is, the
415    caller should not translate it with gettext, but fh_lock will
416    do so.
417
418    ACCESS specifies whether the lock is for reading or writing.
419    EXCLUSIVE is true to require exclusive access, false to allow
420    sharing with other accessors.  Exclusive read access precludes
421    other readers, but not writers; exclusive write access
422    precludes other writers, but not readers.  A sharable read or
423    write lock precludes reader or writers, respectively, of a
424    different TYPE.
425
426    A lock may be associated with auxiliary data.  See
427    fh_lock_get_aux and fh_lock_set_aux for more details. */
428 struct fh_lock *
429 fh_lock (struct file_handle *h, enum fh_referent mask UNUSED,
430          const char *type, enum fh_access access, bool exclusive)
431 {
432   struct fh_lock *key = NULL;
433   size_t hash ;
434   struct fh_lock *lock = NULL;
435   bool found_lock = false;
436
437   assert ((fh_get_referent (h) & mask) != 0);
438   assert (access == FH_ACC_READ || access == FH_ACC_WRITE);
439
440   key = xmalloc (sizeof *key);
441
442   make_key (key, h, access);
443
444   key->open_cnt = 1;
445   key->exclusive = exclusive;
446   key->type = type;
447   key->aux = NULL;
448
449   hash = hash_fh_lock (key);
450
451   HMAP_FOR_EACH_WITH_HASH (lock, struct fh_lock, node, hash, &locks)
452     {
453       if ( 0 == compare_fh_locks (lock, key))
454         {
455           found_lock = true;
456           break;
457         }
458     }
459
460   if ( found_lock )
461     {
462       if (strcmp (lock->type, type))
463         {
464           if (access == FH_ACC_READ)
465             msg (SE, _("Can't read from %s as a %s because it is "
466                        "already being read as a %s."),
467                  fh_get_name (h), gettext (type), gettext (lock->type));
468           else
469             msg (SE, _("Can't write to %s as a %s because it is "
470                        "already being written as a %s."),
471                  fh_get_name (h), gettext (type), gettext (lock->type));
472           return NULL;
473         }
474       else if (exclusive || lock->exclusive)
475         {
476           msg (SE, _("Can't re-open %s as a %s."),
477                fh_get_name (h), gettext (type));
478           return NULL;
479         }
480       lock->open_cnt++;
481       
482       free_key (key);
483       free (key);
484
485       return lock;
486     }
487
488   hmap_insert (&locks, &key->node, hash);
489   found_lock = false;
490   HMAP_FOR_EACH_WITH_HASH (lock, struct fh_lock, node, hash, &locks)
491     {
492       if ( 0 == compare_fh_locks (lock, key))
493         {
494           found_lock = true;
495           break;
496         }
497     }
498
499   assert (found_lock);
500
501   return key;
502 }
503
504 /* Releases LOCK that was acquired with fh_lock.
505    Returns true if LOCK is still locked, because other clients
506    also had it locked.
507
508    Returns false if LOCK has now been destroyed.  In this case
509    the caller must ensure that any auxiliary data associated with
510    LOCK is destroyed, to avoid a memory leak.  The caller must
511    obtain a pointer to the auxiliary data, e.g. via
512    fh_lock_get_aux *before* calling fh_unlock (because it yields
513    undefined behavior to call fh_lock_get_aux on a destroyed
514    lock).  */
515 bool
516 fh_unlock (struct fh_lock *lock)
517 {
518   if (lock != NULL)
519     {
520       assert (lock->open_cnt > 0);
521       if (--lock->open_cnt == 0)
522         {
523           hmap_delete (&locks, &lock->node);
524           free_key (lock);
525           free (lock);
526           return false;
527         }
528     }
529   return true;
530 }
531
532 /* Returns auxiliary data for LOCK.
533
534    Auxiliary data is shared by every client that holds LOCK (for
535    an exclusive lock, this is a single client).  To avoid leaks,
536    auxiliary data must be released before LOCK is destroyed. */
537 void *
538 fh_lock_get_aux (const struct fh_lock *lock)
539 {
540   return lock->aux;
541 }
542
543 /* Sets the auxiliary data for LOCK to AUX. */
544 void
545 fh_lock_set_aux (struct fh_lock *lock, void *aux)
546 {
547   lock->aux = aux;
548 }
549
550 /* Returns true if HANDLE is locked for the given type of ACCESS,
551    false otherwise. */
552 bool
553 fh_is_locked (const struct file_handle *handle, enum fh_access access)
554 {
555   struct fh_lock key;
556   const struct fh_lock *k = NULL;
557   bool is_locked = false;
558   size_t hash ;
559
560   make_key (&key, handle, access);
561
562   hash = hash_fh_lock (&key);
563
564
565   HMAP_FOR_EACH_WITH_HASH (k, struct fh_lock, node, hash, &locks)
566     {
567       if ( 0 == compare_fh_locks (k, &key))
568         {
569           is_locked = true;
570           break;
571         }
572     }
573
574   free_key (&key);
575
576   return is_locked;
577 }
578
579 /* Initializes the key fields in LOCK for looking up or inserting
580    handle H for the given kind of ACCESS. */
581 static void
582 make_key (struct fh_lock *lock, const struct file_handle *h,
583           enum fh_access access)
584 {
585   lock->referent = fh_get_referent (h);
586   lock->access = access;
587   if (lock->referent == FH_REF_FILE)
588     lock->u.file = fn_get_identity (fh_get_file_name (h));
589   else if (lock->referent == FH_REF_DATASET)
590     lock->u.unique_id = dataset_seqno (fh_get_dataset (h));
591 }
592
593 /* Frees the key fields in LOCK. */
594 static void
595 free_key (struct fh_lock *lock)
596 {
597   if (lock->referent == FH_REF_FILE)
598     fn_free_identity (lock->u.file);
599 }
600
601 /* Compares the key fields in struct fh_lock objects A and B and
602    returns a strcmp()-type result. */
603 static int
604 compare_fh_locks (const struct fh_lock *a, const struct fh_lock *b)
605 {
606   if (a->referent != b->referent)
607     return a->referent < b->referent ? -1 : 1;
608   else if (a->access != b->access)
609     return a->access < b->access ? -1 : 1;
610   else if (a->referent == FH_REF_FILE)
611     return fn_compare_file_identities (a->u.file, b->u.file);
612   else if (a->referent == FH_REF_DATASET)
613     return (a->u.unique_id < b->u.unique_id ? -1
614             : a->u.unique_id > b->u.unique_id);
615   else
616     return 0;
617 }
618
619 /* Returns a hash value for LOCK. */
620 static unsigned int
621 hash_fh_lock (const struct fh_lock *lock)
622 {
623   unsigned int basis;
624   if (lock->referent == FH_REF_FILE)
625     basis = fn_hash_identity (lock->u.file);
626   else if (lock->referent == FH_REF_DATASET)
627     basis = lock->u.unique_id;
628   else
629     basis = 0;
630   return hash_int ((lock->referent << 3) | lock->access, basis);
631 }