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 "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"
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_SCRATCH only. */
60 struct scratch_handle *sh; /* Scratch file data. */
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);
113 scratch_handle_destroy (handle->sh);
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. */
121 unname_handle (struct file_handle *handle)
123 assert (handle->id != NULL);
126 hmap_delete (&named_handles, &handle->name_node);
128 /* Drop the reference held by the named_handles table. */
132 /* Increments HANDLE's reference count and returns HANDLE. */
134 fh_ref (struct file_handle *handle)
136 assert (handle->ref_cnt > 0);
141 /* Decrements HANDLE's reference count.
142 If the reference count drops to 0, HANDLE is destroyed. */
144 fh_unref (struct file_handle *handle)
148 assert (handle->ref_cnt > 0);
149 if (--handle->ref_cnt == 0)
150 free_handle (handle);
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.
158 This function ignores a null pointer as input. It has no
159 effect on the inline handle, which is always named INLINE.*/
161 fh_unname (struct file_handle *handle)
163 assert (handle->ref_cnt > 1);
164 if (handle != fh_inline_file () && handle->id != NULL)
165 unname_handle (handle);
168 /* Returns the handle with the given ID, or a null pointer if
171 fh_from_id (const char *id)
173 struct file_handle *handle;
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))
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.
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)
195 struct file_handle *handle = xzalloc (sizeof *handle);
198 handle->id = id != NULL ? xstrdup (id) : NULL;
199 handle->name = handle_name;
200 handle->referent = referent;
204 assert (fh_from_id (id) == NULL);
205 hmap_insert (&named_handles, &handle->name_node,
206 hash_case_string (handle->id, 0));
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. */
217 fh_inline_file (void)
219 fh_ref (inline_file);
223 /* Creates and returns a new file handle with the given ID, which
224 may be null. If it is non-null, it must be unique among
225 existing file identifiers. The new handle is associated with
226 file FILE_NAME and the given PROPERTIES. */
228 fh_create_file (const char *id, const char *file_name,
229 const struct fh_properties *properties)
232 struct file_handle *handle;
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;
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). */
248 fh_create_scratch (const char *id)
250 struct file_handle *handle;
251 handle = create_handle (id, xstrdup (id), FH_REF_SCRATCH);
256 /* Returns a set of default properties for a file handle. */
257 const struct fh_properties *
258 fh_default_properties (void)
260 static const struct fh_properties default_properties
261 = {FH_MODE_TEXT, 1024, 4, C_ENCODING};
262 return &default_properties;
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.
269 Return value is owned by the file handle.*/
271 fh_get_id (const struct file_handle *handle)
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
281 Useful for printing error messages about use of file handles. */
283 fh_get_name (const struct file_handle *handle)
288 /* Returns the type of object that HANDLE refers to. */
290 fh_get_referent (const struct file_handle *handle)
292 return handle->referent;
295 /* Returns the name of the file associated with HANDLE. */
297 fh_get_file_name (const struct file_handle *handle)
299 assert (handle->referent == FH_REF_FILE);
300 return handle->file_name;
303 /* Returns the mode of HANDLE. */
305 fh_get_mode (const struct file_handle *handle)
307 assert (handle->referent == FH_REF_FILE);
311 /* Returns the width of a logical record on HANDLE. */
313 fh_get_record_width (const struct file_handle *handle)
315 assert (handle->referent & (FH_REF_FILE | FH_REF_INLINE));
316 return handle->record_width;
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. */
323 fh_get_tab_width (const struct file_handle *handle)
325 assert (handle->referent & (FH_REF_FILE | FH_REF_INLINE));
326 return handle->tab_width;
329 /* Returns the encoding of characters read from HANDLE. */
331 fh_get_legacy_encoding (const struct file_handle *handle)
333 assert (handle->referent & (FH_REF_FILE | FH_REF_INLINE));
334 return (handle->referent == FH_REF_FILE ? handle->encoding : C_ENCODING);
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)
342 assert (handle->referent == FH_REF_SCRATCH);
346 /* Sets SH to be the scratch file handle associated with HANDLE.
347 Applicable to only FH_REF_SCRATCH files. */
349 fh_set_scratch_handle (struct file_handle *handle, struct scratch_handle *sh)
351 assert (handle->referent == FH_REF_SCRATCH);
355 /* Returns the current default handle. */
357 fh_get_default_handle (void)
359 return default_handle ? fh_ref (default_handle) : fh_inline_file ();
362 /* Sets NEW_DEFAULT_HANDLE as the default handle. */
364 fh_set_default_handle (struct file_handle *new_default_handle)
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);
375 /* Information about a file handle's readers or writers. */
378 struct hmap_node node; /* hmap_node member. */
381 enum fh_referent referent; /* Type of underlying file. */
384 struct file_identity *file; /* FH_REF_FILE only. */
385 unsigned int unique_id; /* FH_REF_SCRATCH only. */
388 enum fh_access access; /* Type of file access. */
390 /* Number of openers. */
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. */
400 static void make_key (struct fh_lock *, const struct file_handle *,
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);
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.
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.
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.
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
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
430 A lock may be associated with auxiliary data. See
431 fh_lock_get_aux and fh_lock_set_aux for more details. */
433 fh_lock (struct file_handle *h, enum fh_referent mask UNUSED,
434 const char *type, enum fh_access access, bool exclusive)
436 struct fh_lock *key = NULL;
438 struct fh_lock *lock = NULL;
439 bool found_lock = false;
441 assert ((fh_get_referent (h) & mask) != 0);
442 assert (access == FH_ACC_READ || access == FH_ACC_WRITE);
444 key = xmalloc (sizeof *key);
446 make_key (key, h, access);
449 key->exclusive = exclusive;
453 hash = hash_fh_lock (key);
455 HMAP_FOR_EACH_WITH_HASH (lock, struct fh_lock, node, hash, &locks)
457 if ( 0 == compare_fh_locks (lock, key))
466 if (strcmp (lock->type, type))
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));
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));
478 else if (exclusive || lock->exclusive)
480 msg (SE, _("Can't re-open %s as a %s."),
481 fh_get_name (h), gettext (type));
492 hmap_insert (&locks, &key->node, hash);
494 HMAP_FOR_EACH_WITH_HASH (lock, struct fh_lock, node, hash, &locks)
496 if ( 0 == compare_fh_locks (lock, key))
508 /* Releases LOCK that was acquired with fh_lock.
509 Returns true if LOCK is still locked, because other clients
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
520 fh_unlock (struct fh_lock *lock)
524 assert (lock->open_cnt > 0);
525 if (--lock->open_cnt == 0)
527 hmap_delete (&locks, &lock->node);
536 /* Returns auxiliary data for LOCK.
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. */
542 fh_lock_get_aux (const struct fh_lock *lock)
547 /* Sets the auxiliary data for LOCK to AUX. */
549 fh_lock_set_aux (struct fh_lock *lock, void *aux)
554 /* Returns true if HANDLE is locked for the given type of ACCESS,
557 fh_is_locked (const struct file_handle *handle, enum fh_access access)
560 const struct fh_lock *k = NULL;
561 bool is_locked = false;
564 make_key (&key, handle, access);
566 hash = hash_fh_lock (&key);
569 HMAP_FOR_EACH_WITH_HASH (k, struct fh_lock, node, hash, &locks)
571 if ( 0 == compare_fh_locks (k, &key))
583 /* Initializes the key fields in LOCK for looking up or inserting
584 handle H for the given kind of ACCESS. */
586 make_key (struct fh_lock *lock, const struct file_handle *h,
587 enum fh_access access)
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)
595 struct scratch_handle *sh = fh_get_scratch_handle (h);
596 lock->u.unique_id = sh != NULL ? sh->unique_id : 0;
600 /* Frees the key fields in LOCK. */
602 free_key (struct fh_lock *lock)
604 if (lock->referent == FH_REF_FILE)
605 fn_free_identity (lock->u.file);
608 /* Compares the key fields in struct fh_lock objects A and B and
609 returns a strcmp()-type result. */
611 compare_fh_locks (const struct fh_lock *a, const struct fh_lock *b)
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);
626 /* Returns a hash value for LOCK. */
628 hash_fh_lock (const struct fh_lock *lock)
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;
637 return hash_int ((lock->referent << 3) | lock->access, basis);