1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 1997-9, 2000, 2006, 2009, 2010, 2011 Free Software Foundation, Inc.
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.
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.
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/>. */
19 #include "data/file-handle-def.h"
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"
36 #include "gl/xalloc.h"
39 #define _(msgid) gettext (msgid)
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. */
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. */
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. */
59 /* FH_REF_DATASET only. */
60 struct dataset *ds; /* Dataset. */
63 /* All "struct file_handle"s with nonnull 'id' member. */
64 static struct hmap named_handles = HMAP_INITIALIZER (named_handles);
66 /* Default file handle for DATA LIST, REREAD, REPEATING DATA
68 static struct file_handle *default_handle;
70 /* The "file" that reads from BEGIN DATA...END DATA. */
71 static struct file_handle *inline_file;
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 *);
78 /* Hash table of all active locks. */
79 static struct hmap locks = HMAP_INITIALIZER (locks);
81 /* File handle initialization routine. */
85 inline_file = create_handle ("INLINE", xstrdup ("INLINE"), FH_REF_INLINE);
86 inline_file->record_width = 80;
87 inline_file->tab_width = 8;
90 /* Removes all named file handles from the global list. */
94 struct file_handle *handle, *next;
96 HMAP_FOR_EACH_SAFE (handle, next,
97 struct file_handle, name_node, &named_handles)
98 unname_handle (handle);
101 /* Free HANDLE and remove it from the global list. */
103 free_handle (struct file_handle *handle)
105 /* Remove handle from global list. */
106 if (handle->id != NULL)
107 hmap_delete (&named_handles, &handle->name_node);
112 free (handle->file_name);
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. */
120 unname_handle (struct file_handle *handle)
122 assert (handle->id != NULL);
125 hmap_delete (&named_handles, &handle->name_node);
127 /* Drop the reference held by the named_handles table. */
131 /* Increments HANDLE's reference count and returns HANDLE. */
133 fh_ref (struct file_handle *handle)
135 assert (handle->ref_cnt > 0);
140 /* Decrements HANDLE's reference count.
141 If the reference count drops to 0, HANDLE is destroyed. */
143 fh_unref (struct file_handle *handle)
147 assert (handle->ref_cnt > 0);
148 if (--handle->ref_cnt == 0)
149 free_handle (handle);
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.
157 This function ignores a null pointer as input. It has no
158 effect on the inline handle, which is always named INLINE.*/
160 fh_unname (struct file_handle *handle)
162 assert (handle->ref_cnt > 1);
163 if (handle != fh_inline_file () && handle->id != NULL)
164 unname_handle (handle);
167 /* Returns the handle with the given ID, or a null pointer if
170 fh_from_id (const char *id)
172 struct file_handle *handle;
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))
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.
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)
194 struct file_handle *handle = xzalloc (sizeof *handle);
197 handle->id = id != NULL ? xstrdup (id) : NULL;
198 handle->name = handle_name;
199 handle->referent = referent;
203 assert (fh_from_id (id) == NULL);
204 hmap_insert (&named_handles, &handle->name_node,
205 hash_case_string (handle->id, 0));
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. */
216 fh_inline_file (void)
218 fh_ref (inline_file);
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. */
227 fh_create_file (const char *id, const char *file_name,
228 const struct fh_properties *properties)
231 struct file_handle *handle;
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;
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). */
247 fh_create_dataset (struct dataset *ds)
250 struct file_handle *handle;
252 name = dataset_name (ds);
254 name = _("active dataset");
256 handle = create_handle (NULL, xstrdup (name), FH_REF_DATASET);
261 /* Returns a set of default properties for a file handle. */
262 const struct fh_properties *
263 fh_default_properties (void)
265 static const struct fh_properties default_properties
266 = {FH_MODE_TEXT, 1024, 4, C_ENCODING};
267 return &default_properties;
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.
274 Return value is owned by the file handle.*/
276 fh_get_id (const struct file_handle *handle)
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
286 Useful for printing error messages about use of file handles. */
288 fh_get_name (const struct file_handle *handle)
293 /* Returns the type of object that HANDLE refers to. */
295 fh_get_referent (const struct file_handle *handle)
297 return handle->referent;
300 /* Returns the name of the file associated with HANDLE. */
302 fh_get_file_name (const struct file_handle *handle)
304 assert (handle->referent == FH_REF_FILE);
305 return handle->file_name;
308 /* Returns the mode of HANDLE. */
310 fh_get_mode (const struct file_handle *handle)
312 assert (handle->referent == FH_REF_FILE);
316 /* Returns the width of a logical record on HANDLE. */
318 fh_get_record_width (const struct file_handle *handle)
320 assert (handle->referent & (FH_REF_FILE | FH_REF_INLINE));
321 return handle->record_width;
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. */
328 fh_get_tab_width (const struct file_handle *handle)
330 assert (handle->referent & (FH_REF_FILE | FH_REF_INLINE));
331 return handle->tab_width;
334 /* Returns the encoding of characters read from HANDLE. */
336 fh_get_legacy_encoding (const struct file_handle *handle)
338 assert (handle->referent & (FH_REF_FILE | FH_REF_INLINE));
339 return (handle->referent == FH_REF_FILE ? handle->encoding : C_ENCODING);
342 /* Returns the dataset handle associated with HANDLE.
343 Applicable to only FH_REF_DATASET files. */
345 fh_get_dataset (const struct file_handle *handle)
347 assert (handle->referent == FH_REF_DATASET);
351 /* Returns the current default handle. */
353 fh_get_default_handle (void)
355 return default_handle ? fh_ref (default_handle) : fh_inline_file ();
358 /* Sets NEW_DEFAULT_HANDLE as the default handle. */
360 fh_set_default_handle (struct file_handle *new_default_handle)
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);
371 /* Information about a file handle's readers or writers. */
374 struct hmap_node node; /* hmap_node member. */
377 enum fh_referent referent; /* Type of underlying file. */
380 struct file_identity *file; /* FH_REF_FILE only. */
381 unsigned int unique_id; /* FH_REF_DATASET only. */
384 enum fh_access access; /* Type of file access. */
386 /* Number of openers. */
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. */
396 static void make_key (struct fh_lock *, const struct file_handle *,
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);
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.
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.
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.
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
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
426 A lock may be associated with auxiliary data. See
427 fh_lock_get_aux and fh_lock_set_aux for more details. */
429 fh_lock (struct file_handle *h, enum fh_referent mask UNUSED,
430 const char *type, enum fh_access access, bool exclusive)
432 struct fh_lock *key = NULL;
434 struct fh_lock *lock = NULL;
435 bool found_lock = false;
437 assert ((fh_get_referent (h) & mask) != 0);
438 assert (access == FH_ACC_READ || access == FH_ACC_WRITE);
440 key = xmalloc (sizeof *key);
442 make_key (key, h, access);
445 key->exclusive = exclusive;
449 hash = hash_fh_lock (key);
451 HMAP_FOR_EACH_WITH_HASH (lock, struct fh_lock, node, hash, &locks)
453 if ( 0 == compare_fh_locks (lock, key))
462 if (strcmp (lock->type, type))
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));
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));
474 else if (exclusive || lock->exclusive)
476 msg (SE, _("Can't re-open %s as a %s."),
477 fh_get_name (h), gettext (type));
488 hmap_insert (&locks, &key->node, hash);
490 HMAP_FOR_EACH_WITH_HASH (lock, struct fh_lock, node, hash, &locks)
492 if ( 0 == compare_fh_locks (lock, key))
504 /* Releases LOCK that was acquired with fh_lock.
505 Returns true if LOCK is still locked, because other clients
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
516 fh_unlock (struct fh_lock *lock)
520 assert (lock->open_cnt > 0);
521 if (--lock->open_cnt == 0)
523 hmap_delete (&locks, &lock->node);
532 /* Returns auxiliary data for LOCK.
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. */
538 fh_lock_get_aux (const struct fh_lock *lock)
543 /* Sets the auxiliary data for LOCK to AUX. */
545 fh_lock_set_aux (struct fh_lock *lock, void *aux)
550 /* Returns true if HANDLE is locked for the given type of ACCESS,
553 fh_is_locked (const struct file_handle *handle, enum fh_access access)
556 const struct fh_lock *k = NULL;
557 bool is_locked = false;
560 make_key (&key, handle, access);
562 hash = hash_fh_lock (&key);
565 HMAP_FOR_EACH_WITH_HASH (k, struct fh_lock, node, hash, &locks)
567 if ( 0 == compare_fh_locks (k, &key))
579 /* Initializes the key fields in LOCK for looking up or inserting
580 handle H for the given kind of ACCESS. */
582 make_key (struct fh_lock *lock, const struct file_handle *h,
583 enum fh_access access)
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));
593 /* Frees the key fields in LOCK. */
595 free_key (struct fh_lock *lock)
597 if (lock->referent == FH_REF_FILE)
598 fn_free_identity (lock->u.file);
601 /* Compares the key fields in struct fh_lock objects A and B and
602 returns a strcmp()-type result. */
604 compare_fh_locks (const struct fh_lock *a, const struct fh_lock *b)
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);
619 /* Returns a hash value for LOCK. */
621 hash_fh_lock (const struct fh_lock *lock)
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;
630 return hash_int ((lock->referent << 3) | lock->access, basis);