1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 1997-9, 2000, 2006, 2009, 2010 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 "file-handle-def.h"
26 #include <libpspp/compiler.h>
27 #include <libpspp/hmap.h>
28 #include <libpspp/ll.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>
39 #define _(msgid) gettext (msgid)
44 struct ll ll; /* Element in global list. */
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 static struct file_handle *
64 file_handle_from_ll (struct ll *ll)
66 return ll_data (ll, struct file_handle, ll);
69 /* List of all named handles. */
70 static struct ll_list named_handles;
72 /* Default file handle for DATA LIST, REREAD, REPEATING DATA
74 static struct file_handle *default_handle;
76 /* The "file" that reads from BEGIN DATA...END DATA. */
77 static struct file_handle *inline_file;
79 static struct file_handle *create_handle (const char *id,
80 char *name, enum fh_referent);
81 static void free_handle (struct file_handle *);
82 static void unname_handle (struct file_handle *);
84 /* Hash table of all active locks. */
85 static struct hmap locks = HMAP_INITIALIZER (locks);
87 /* File handle initialization routine. */
91 ll_init (&named_handles);
92 inline_file = create_handle ("INLINE", xstrdup ("INLINE"), FH_REF_INLINE);
93 inline_file->record_width = 80;
94 inline_file->tab_width = 8;
97 /* Removes all named file handles from the global list. */
101 while (!ll_is_empty (&named_handles))
102 unname_handle (file_handle_from_ll (ll_head (&named_handles)));
105 /* Free HANDLE and remove it from the global list. */
107 free_handle (struct file_handle *handle)
109 /* Remove handle from global list. */
110 if (handle->id != NULL)
111 ll_remove (&handle->ll);
116 free (handle->file_name);
117 scratch_handle_destroy (handle->sh);
121 /* Make HANDLE unnamed, so that it can no longer be referenced by
122 name. The caller must hold a reference to HANDLE, which is
123 not affected by this function. */
125 unname_handle (struct file_handle *handle)
127 assert (handle->id != NULL);
130 ll_remove (&handle->ll);
132 /* Drop the reference held by the named_handles table. */
136 /* Increments HANDLE's reference count and returns HANDLE. */
138 fh_ref (struct file_handle *handle)
140 assert (handle->ref_cnt > 0);
145 /* Decrements HANDLE's reference count.
146 If the reference count drops to 0, HANDLE is destroyed. */
148 fh_unref (struct file_handle *handle)
152 assert (handle->ref_cnt > 0);
153 if (--handle->ref_cnt == 0)
154 free_handle (handle);
158 /* Make HANDLE unnamed, so that it can no longer be referenced by
159 name. The caller must hold a reference to HANDLE, which is
160 not affected by this function.
162 This function ignores a null pointer as input. It has no
163 effect on the inline handle, which is always named INLINE.*/
165 fh_unname (struct file_handle *handle)
167 assert (handle->ref_cnt > 1);
168 if (handle != fh_inline_file () && handle->id != NULL)
169 unname_handle (handle);
172 /* Returns the handle with the given ID, or a null pointer if
175 fh_from_id (const char *id)
177 struct file_handle *handle;
179 ll_for_each (handle, struct file_handle, ll, &named_handles)
180 if (!strcasecmp (id, handle->id))
189 /* Creates a new handle with identifier ID (which may be null)
190 and name HANDLE_NAME that refers to REFERENT. Links the new
191 handle into the global list. Returns the new handle.
193 The new handle is not fully initialized. The caller is
194 responsible for completing its initialization. */
195 static struct file_handle *
196 create_handle (const char *id, char *handle_name, enum fh_referent referent)
198 struct file_handle *handle = xzalloc (sizeof *handle);
201 handle->id = id != NULL ? xstrdup (id) : NULL;
202 handle->name = handle_name;
203 handle->referent = referent;
207 assert (fh_from_id (id) == NULL);
208 ll_push_tail (&named_handles, &handle->ll);
215 /* Returns the unique handle of referent type FH_REF_INLINE,
216 which refers to the "inline file" that represents character
217 data in the command file between BEGIN DATA and END DATA. */
219 fh_inline_file (void)
221 fh_ref (inline_file);
225 /* Creates and returns a new file handle with the given ID, which
226 may be null. If it is non-null, it must be unique among
227 existing file identifiers. The new handle is associated with
228 file FILE_NAME and the given PROPERTIES. */
230 fh_create_file (const char *id, const char *file_name,
231 const struct fh_properties *properties)
234 struct file_handle *handle;
236 handle_name = id != NULL ? xstrdup (id) : xasprintf ("`%s'", file_name);
237 handle = create_handle (id, handle_name, FH_REF_FILE);
238 handle->file_name = xstrdup (file_name);
239 handle->mode = properties->mode;
240 handle->record_width = properties->record_width;
241 handle->tab_width = properties->tab_width;
242 handle->encoding = properties->encoding;
246 /* Creates a new file handle with the given ID, which must be
247 unique among existing file identifiers. The new handle is
248 associated with a scratch file (initially empty). */
250 fh_create_scratch (const char *id)
252 struct file_handle *handle;
253 handle = create_handle (id, xstrdup (id), FH_REF_SCRATCH);
258 /* Returns a set of default properties for a file handle. */
259 const struct fh_properties *
260 fh_default_properties (void)
262 static const struct fh_properties default_properties
263 = {FH_MODE_TEXT, 1024, 4, LEGACY_NATIVE};
264 return &default_properties;
267 /* Returns the identifier that may be used in syntax to name the
268 given HANDLE, which takes the form of a PSPP identifier. If
269 HANDLE has no identifier, returns a null pointer.
271 Return value is owned by the file handle.*/
273 fh_get_id (const struct file_handle *handle)
278 /* Returns a user-friendly string to identify the given HANDLE.
279 If HANDLE was created by referring to a file name, returns the
280 file name, enclosed in double quotes. Return value is owned
283 Useful for printing error messages about use of file handles. */
285 fh_get_name (const struct file_handle *handle)
290 /* Returns the type of object that HANDLE refers to. */
292 fh_get_referent (const struct file_handle *handle)
294 return handle->referent;
297 /* Returns the name of the file associated with HANDLE. */
299 fh_get_file_name (const struct file_handle *handle)
301 assert (handle->referent == FH_REF_FILE);
302 return handle->file_name;
305 /* Returns the mode of HANDLE. */
307 fh_get_mode (const struct file_handle *handle)
309 assert (handle->referent == FH_REF_FILE);
313 /* Returns the width of a logical record on HANDLE. */
315 fh_get_record_width (const struct file_handle *handle)
317 assert (handle->referent & (FH_REF_FILE | FH_REF_INLINE));
318 return handle->record_width;
321 /* Returns the number of characters per tab stop for HANDLE, or
322 zero if tabs are not to be expanded. Applicable only to
323 FH_MODE_TEXT files. */
325 fh_get_tab_width (const struct file_handle *handle)
327 assert (handle->referent & (FH_REF_FILE | FH_REF_INLINE));
328 return handle->tab_width;
331 /* Returns the encoding of characters read from HANDLE. */
333 fh_get_legacy_encoding (const struct file_handle *handle)
335 assert (handle->referent & (FH_REF_FILE | FH_REF_INLINE));
336 return (handle->referent == FH_REF_FILE ? handle->encoding : LEGACY_NATIVE);
339 /* Returns the scratch file handle associated with HANDLE.
340 Applicable to only FH_REF_SCRATCH files. */
341 struct scratch_handle *
342 fh_get_scratch_handle (const struct file_handle *handle)
344 assert (handle->referent == FH_REF_SCRATCH);
348 /* Sets SH to be the scratch file handle associated with HANDLE.
349 Applicable to only FH_REF_SCRATCH files. */
351 fh_set_scratch_handle (struct file_handle *handle, struct scratch_handle *sh)
353 assert (handle->referent == FH_REF_SCRATCH);
357 /* Returns the current default handle. */
359 fh_get_default_handle (void)
361 return default_handle ? fh_ref (default_handle) : fh_inline_file ();
364 /* Sets NEW_DEFAULT_HANDLE as the default handle. */
366 fh_set_default_handle (struct file_handle *new_default_handle)
368 assert (new_default_handle == NULL
369 || (new_default_handle->referent & (FH_REF_INLINE | FH_REF_FILE)));
370 if (default_handle != NULL)
371 fh_unref (default_handle);
372 default_handle = new_default_handle;
373 if (default_handle != NULL)
374 fh_ref (default_handle);
377 /* Information about a file handle's readers or writers. */
380 struct hmap_node node; /* hmap_node member. */
383 enum fh_referent referent; /* Type of underlying file. */
386 struct file_identity *file; /* FH_REF_FILE only. */
387 unsigned int unique_id; /* FH_REF_SCRATCH only. */
390 enum fh_access access; /* Type of file access. */
392 /* Number of openers. */
395 /* Applicable only when open_cnt > 0. */
396 bool exclusive; /* No other openers allowed? */
397 const char *type; /* Human-readable type of file. */
398 void *aux; /* Owner's auxiliary data. */
402 static void make_key (struct fh_lock *, const struct file_handle *,
404 static void free_key (struct fh_lock *);
405 static int compare_fh_locks (const struct fh_lock *a, const struct fh_lock *b);
406 static unsigned int hash_fh_lock (const struct fh_lock *lock);
408 /* Tries to lock handle H for the given kind of ACCESS and TYPE
409 of file. Returns a pointer to a struct fh_lock if successful,
410 otherwise a null pointer.
412 H's referent type must be one of the bits in MASK. The caller
413 must verify this ahead of time; we simply assert it here.
415 TYPE is the sort of file, e.g. "system file". Only one type
416 of access is allowed on a given file at a time for reading,
417 and similarly for writing. If successful, a reference to TYPE
418 is retained, so it should probably be a string literal.
420 TYPE should be marked with N_() in the caller: that is, the
421 caller should not translate it with gettext, but fh_lock will
424 ACCESS specifies whether the lock is for reading or writing.
425 EXCLUSIVE is true to require exclusive access, false to allow
426 sharing with other accessors. Exclusive read access precludes
427 other readers, but not writers; exclusive write access
428 precludes other writers, but not readers. A sharable read or
429 write lock precludes reader or writers, respectively, of a
432 A lock may be associated with auxiliary data. See
433 fh_lock_get_aux and fh_lock_set_aux for more details. */
435 fh_lock (struct file_handle *h, enum fh_referent mask UNUSED,
436 const char *type, enum fh_access access, bool exclusive)
438 struct fh_lock *key = NULL;
440 struct fh_lock *lock = NULL;
441 bool found_lock = false;
443 assert ((fh_get_referent (h) & mask) != 0);
444 assert (access == FH_ACC_READ || access == FH_ACC_WRITE);
446 key = xmalloc (sizeof *key);
448 make_key (key, h, access);
451 key->exclusive = exclusive;
455 hash = hash_fh_lock (key);
457 HMAP_FOR_EACH_WITH_HASH (lock, struct fh_lock, node, hash, &locks)
459 if ( 0 == compare_fh_locks (lock, key))
468 if (strcmp (lock->type, type))
470 if (access == FH_ACC_READ)
471 msg (SE, _("Can't read from %s as a %s because it is "
472 "already being read as a %s."),
473 fh_get_name (h), gettext (type), gettext (lock->type));
475 msg (SE, _("Can't write to %s as a %s because it is "
476 "already being written as a %s."),
477 fh_get_name (h), gettext (type), gettext (lock->type));
480 else if (exclusive || lock->exclusive)
482 msg (SE, _("Can't re-open %s as a %s."),
483 fh_get_name (h), gettext (type));
494 hmap_insert (&locks, &key->node, hash);
496 HMAP_FOR_EACH_WITH_HASH (lock, struct fh_lock, node, hash, &locks)
498 if ( 0 == compare_fh_locks (lock, key))
510 /* Releases LOCK that was acquired with fh_lock.
511 Returns true if LOCK is still locked, because other clients
514 Returns false if LOCK has now been destroyed. In this case
515 the caller must ensure that any auxiliary data associated with
516 LOCK is destroyed, to avoid a memory leak. The caller must
517 obtain a pointer to the auxiliary data, e.g. via
518 fh_lock_get_aux *before* calling fh_unlock (because it yields
519 undefined behavior to call fh_lock_get_aux on a destroyed
522 fh_unlock (struct fh_lock *lock)
526 assert (lock->open_cnt > 0);
527 if (--lock->open_cnt == 0)
529 hmap_delete (&locks, &lock->node);
538 /* Returns auxiliary data for LOCK.
540 Auxiliary data is shared by every client that holds LOCK (for
541 an exclusive lock, this is a single client). To avoid leaks,
542 auxiliary data must be released before LOCK is destroyed. */
544 fh_lock_get_aux (const struct fh_lock *lock)
549 /* Sets the auxiliary data for LOCK to AUX. */
551 fh_lock_set_aux (struct fh_lock *lock, void *aux)
556 /* Returns true if HANDLE is locked for the given type of ACCESS,
559 fh_is_locked (const struct file_handle *handle, enum fh_access access)
562 const struct fh_lock *k = NULL;
563 bool is_locked = false;
566 make_key (&key, handle, access);
568 hash = hash_fh_lock (&key);
571 HMAP_FOR_EACH_WITH_HASH (k, struct fh_lock, node, hash, &locks)
573 if ( 0 == compare_fh_locks (k, &key))
585 /* Initializes the key fields in LOCK for looking up or inserting
586 handle H for the given kind of ACCESS. */
588 make_key (struct fh_lock *lock, const struct file_handle *h,
589 enum fh_access access)
591 lock->referent = fh_get_referent (h);
592 lock->access = access;
593 if (lock->referent == FH_REF_FILE)
594 lock->u.file = fn_get_identity (fh_get_file_name (h));
595 else if (lock->referent == FH_REF_SCRATCH)
597 struct scratch_handle *sh = fh_get_scratch_handle (h);
598 lock->u.unique_id = sh != NULL ? sh->unique_id : 0;
602 /* Frees the key fields in LOCK. */
604 free_key (struct fh_lock *lock)
606 if (lock->referent == FH_REF_FILE)
607 fn_free_identity (lock->u.file);
610 /* Compares the key fields in struct fh_lock objects A and B and
611 returns a strcmp()-type result. */
613 compare_fh_locks (const struct fh_lock *a, const struct fh_lock *b)
615 if (a->referent != b->referent)
616 return a->referent < b->referent ? -1 : 1;
617 else if (a->access != b->access)
618 return a->access < b->access ? -1 : 1;
619 else if (a->referent == FH_REF_FILE)
620 return fn_compare_file_identities (a->u.file, b->u.file);
621 else if (a->referent == FH_REF_SCRATCH)
622 return (a->u.unique_id < b->u.unique_id ? -1
623 : a->u.unique_id > b->u.unique_id);
628 /* Returns a hash value for LOCK. */
630 hash_fh_lock (const struct fh_lock *lock)
633 if (lock->referent == FH_REF_FILE)
634 basis = fn_hash_identity (lock->u.file);
635 else if (lock->referent == FH_REF_SCRATCH)
636 basis = lock->u.unique_id;
639 return hash_int ((lock->referent << 3) | lock->access, basis);