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 "file-handle-def.h"
26 #include <libpspp/compiler.h>
27 #include <libpspp/hmap.h>
28 #include <libpspp/i18n.h>
29 #include <libpspp/ll.h>
30 #include <libpspp/message.h>
31 #include <libpspp/str.h>
32 #include <libpspp/hash-functions.h>
33 #include <data/file-name.h>
34 #include <data/variable.h>
35 #include <data/scratch-handle.h>
40 #define _(msgid) gettext (msgid)
45 struct ll ll; /* Element in global list. */
46 size_t ref_cnt; /* Number of references. */
47 char *id; /* Identifier token, NULL if none. */
48 char *name; /* User-friendly identifying name. */
49 enum fh_referent referent; /* What the file handle refers to. */
51 /* FH_REF_FILE only. */
52 char *file_name; /* File name as provided by user. */
53 enum fh_mode mode; /* File mode. */
54 const char *encoding; /* File encoding. */
56 /* FH_REF_FILE and FH_REF_INLINE only. */
57 size_t record_width; /* Length of fixed-format records. */
58 size_t tab_width; /* Tab width, 0=do not expand tabs. */
60 /* FH_REF_SCRATCH only. */
61 struct scratch_handle *sh; /* Scratch file data. */
64 static struct file_handle *
65 file_handle_from_ll (struct ll *ll)
67 return ll_data (ll, struct file_handle, ll);
70 /* List of all named handles. */
71 static struct ll_list named_handles;
73 /* Default file handle for DATA LIST, REREAD, REPEATING DATA
75 static struct file_handle *default_handle;
77 /* The "file" that reads from BEGIN DATA...END DATA. */
78 static struct file_handle *inline_file;
80 static struct file_handle *create_handle (const char *id,
81 char *name, enum fh_referent);
82 static void free_handle (struct file_handle *);
83 static void unname_handle (struct file_handle *);
85 /* Hash table of all active locks. */
86 static struct hmap locks = HMAP_INITIALIZER (locks);
88 /* File handle initialization routine. */
92 ll_init (&named_handles);
93 inline_file = create_handle ("INLINE", xstrdup ("INLINE"), FH_REF_INLINE);
94 inline_file->record_width = 80;
95 inline_file->tab_width = 8;
98 /* Removes all named file handles from the global list. */
102 while (!ll_is_empty (&named_handles))
103 unname_handle (file_handle_from_ll (ll_head (&named_handles)));
106 /* Free HANDLE and remove it from the global list. */
108 free_handle (struct file_handle *handle)
110 /* Remove handle from global list. */
111 if (handle->id != NULL)
112 ll_remove (&handle->ll);
117 free (handle->file_name);
118 scratch_handle_destroy (handle->sh);
122 /* Make HANDLE unnamed, so that it can no longer be referenced by
123 name. The caller must hold a reference to HANDLE, which is
124 not affected by this function. */
126 unname_handle (struct file_handle *handle)
128 assert (handle->id != NULL);
131 ll_remove (&handle->ll);
133 /* Drop the reference held by the named_handles table. */
137 /* Increments HANDLE's reference count and returns HANDLE. */
139 fh_ref (struct file_handle *handle)
141 assert (handle->ref_cnt > 0);
146 /* Decrements HANDLE's reference count.
147 If the reference count drops to 0, HANDLE is destroyed. */
149 fh_unref (struct file_handle *handle)
153 assert (handle->ref_cnt > 0);
154 if (--handle->ref_cnt == 0)
155 free_handle (handle);
159 /* Make HANDLE unnamed, so that it can no longer be referenced by
160 name. The caller must hold a reference to HANDLE, which is
161 not affected by this function.
163 This function ignores a null pointer as input. It has no
164 effect on the inline handle, which is always named INLINE.*/
166 fh_unname (struct file_handle *handle)
168 assert (handle->ref_cnt > 1);
169 if (handle != fh_inline_file () && handle->id != NULL)
170 unname_handle (handle);
173 /* Returns the handle with the given ID, or a null pointer if
176 fh_from_id (const char *id)
178 struct file_handle *handle;
180 ll_for_each (handle, struct file_handle, ll, &named_handles)
181 if (!strcasecmp (id, handle->id))
190 /* Creates a new handle with identifier ID (which may be null)
191 and name HANDLE_NAME that refers to REFERENT. Links the new
192 handle into the global list. Returns the new handle.
194 The new handle is not fully initialized. The caller is
195 responsible for completing its initialization. */
196 static struct file_handle *
197 create_handle (const char *id, char *handle_name, enum fh_referent referent)
199 struct file_handle *handle = xzalloc (sizeof *handle);
202 handle->id = id != NULL ? xstrdup (id) : NULL;
203 handle->name = handle_name;
204 handle->referent = referent;
208 assert (fh_from_id (id) == NULL);
209 ll_push_tail (&named_handles, &handle->ll);
216 /* Returns the unique handle of referent type FH_REF_INLINE,
217 which refers to the "inline file" that represents character
218 data in the command file between BEGIN DATA and END DATA. */
220 fh_inline_file (void)
222 fh_ref (inline_file);
226 /* Creates and returns a new file handle with the given ID, which
227 may be null. If it is non-null, it must be unique among
228 existing file identifiers. The new handle is associated with
229 file FILE_NAME and the given PROPERTIES. */
231 fh_create_file (const char *id, const char *file_name,
232 const struct fh_properties *properties)
235 struct file_handle *handle;
237 handle_name = id != NULL ? xstrdup (id) : xasprintf ("`%s'", file_name);
238 handle = create_handle (id, handle_name, FH_REF_FILE);
239 handle->file_name = xstrdup (file_name);
240 handle->mode = properties->mode;
241 handle->record_width = properties->record_width;
242 handle->tab_width = properties->tab_width;
243 handle->encoding = properties->encoding;
247 /* Creates a new file handle with the given ID, which must be
248 unique among existing file identifiers. The new handle is
249 associated with a scratch file (initially empty). */
251 fh_create_scratch (const char *id)
253 struct file_handle *handle;
254 handle = create_handle (id, xstrdup (id), FH_REF_SCRATCH);
259 /* Returns a set of default properties for a file handle. */
260 const struct fh_properties *
261 fh_default_properties (void)
263 static const struct fh_properties default_properties
264 = {FH_MODE_TEXT, 1024, 4, C_ENCODING};
265 return &default_properties;
268 /* Returns the identifier that may be used in syntax to name the
269 given HANDLE, which takes the form of a PSPP identifier. If
270 HANDLE has no identifier, returns a null pointer.
272 Return value is owned by the file handle.*/
274 fh_get_id (const struct file_handle *handle)
279 /* Returns a user-friendly string to identify the given HANDLE.
280 If HANDLE was created by referring to a file name, returns the
281 file name, enclosed in double quotes. Return value is owned
284 Useful for printing error messages about use of file handles. */
286 fh_get_name (const struct file_handle *handle)
291 /* Returns the type of object that HANDLE refers to. */
293 fh_get_referent (const struct file_handle *handle)
295 return handle->referent;
298 /* Returns the name of the file associated with HANDLE. */
300 fh_get_file_name (const struct file_handle *handle)
302 assert (handle->referent == FH_REF_FILE);
303 return handle->file_name;
306 /* Returns the mode of HANDLE. */
308 fh_get_mode (const struct file_handle *handle)
310 assert (handle->referent == FH_REF_FILE);
314 /* Returns the width of a logical record on HANDLE. */
316 fh_get_record_width (const struct file_handle *handle)
318 assert (handle->referent & (FH_REF_FILE | FH_REF_INLINE));
319 return handle->record_width;
322 /* Returns the number of characters per tab stop for HANDLE, or
323 zero if tabs are not to be expanded. Applicable only to
324 FH_MODE_TEXT files. */
326 fh_get_tab_width (const struct file_handle *handle)
328 assert (handle->referent & (FH_REF_FILE | FH_REF_INLINE));
329 return handle->tab_width;
332 /* Returns the encoding of characters read from HANDLE. */
334 fh_get_legacy_encoding (const struct file_handle *handle)
336 assert (handle->referent & (FH_REF_FILE | FH_REF_INLINE));
337 return (handle->referent == FH_REF_FILE ? handle->encoding : C_ENCODING);
340 /* Returns the scratch file handle associated with HANDLE.
341 Applicable to only FH_REF_SCRATCH files. */
342 struct scratch_handle *
343 fh_get_scratch_handle (const struct file_handle *handle)
345 assert (handle->referent == FH_REF_SCRATCH);
349 /* Sets SH to be the scratch file handle associated with HANDLE.
350 Applicable to only FH_REF_SCRATCH files. */
352 fh_set_scratch_handle (struct file_handle *handle, struct scratch_handle *sh)
354 assert (handle->referent == FH_REF_SCRATCH);
358 /* Returns the current default handle. */
360 fh_get_default_handle (void)
362 return default_handle ? fh_ref (default_handle) : fh_inline_file ();
365 /* Sets NEW_DEFAULT_HANDLE as the default handle. */
367 fh_set_default_handle (struct file_handle *new_default_handle)
369 assert (new_default_handle == NULL
370 || (new_default_handle->referent & (FH_REF_INLINE | FH_REF_FILE)));
371 if (default_handle != NULL)
372 fh_unref (default_handle);
373 default_handle = new_default_handle;
374 if (default_handle != NULL)
375 fh_ref (default_handle);
378 /* Information about a file handle's readers or writers. */
381 struct hmap_node node; /* hmap_node member. */
384 enum fh_referent referent; /* Type of underlying file. */
387 struct file_identity *file; /* FH_REF_FILE only. */
388 unsigned int unique_id; /* FH_REF_SCRATCH only. */
391 enum fh_access access; /* Type of file access. */
393 /* Number of openers. */
396 /* Applicable only when open_cnt > 0. */
397 bool exclusive; /* No other openers allowed? */
398 const char *type; /* Human-readable type of file. */
399 void *aux; /* Owner's auxiliary data. */
403 static void make_key (struct fh_lock *, const struct file_handle *,
405 static void free_key (struct fh_lock *);
406 static int compare_fh_locks (const struct fh_lock *a, const struct fh_lock *b);
407 static unsigned int hash_fh_lock (const struct fh_lock *lock);
409 /* Tries to lock handle H for the given kind of ACCESS and TYPE
410 of file. Returns a pointer to a struct fh_lock if successful,
411 otherwise a null pointer.
413 H's referent type must be one of the bits in MASK. The caller
414 must verify this ahead of time; we simply assert it here.
416 TYPE is the sort of file, e.g. "system file". Only one type
417 of access is allowed on a given file at a time for reading,
418 and similarly for writing. If successful, a reference to TYPE
419 is retained, so it should probably be a string literal.
421 TYPE should be marked with N_() in the caller: that is, the
422 caller should not translate it with gettext, but fh_lock will
425 ACCESS specifies whether the lock is for reading or writing.
426 EXCLUSIVE is true to require exclusive access, false to allow
427 sharing with other accessors. Exclusive read access precludes
428 other readers, but not writers; exclusive write access
429 precludes other writers, but not readers. A sharable read or
430 write lock precludes reader or writers, respectively, of a
433 A lock may be associated with auxiliary data. See
434 fh_lock_get_aux and fh_lock_set_aux for more details. */
436 fh_lock (struct file_handle *h, enum fh_referent mask UNUSED,
437 const char *type, enum fh_access access, bool exclusive)
439 struct fh_lock *key = NULL;
441 struct fh_lock *lock = NULL;
442 bool found_lock = false;
444 assert ((fh_get_referent (h) & mask) != 0);
445 assert (access == FH_ACC_READ || access == FH_ACC_WRITE);
447 key = xmalloc (sizeof *key);
449 make_key (key, h, access);
452 key->exclusive = exclusive;
456 hash = hash_fh_lock (key);
458 HMAP_FOR_EACH_WITH_HASH (lock, struct fh_lock, node, hash, &locks)
460 if ( 0 == compare_fh_locks (lock, key))
469 if (strcmp (lock->type, type))
471 if (access == FH_ACC_READ)
472 msg (SE, _("Can't read from %s as a %s because it is "
473 "already being read as a %s."),
474 fh_get_name (h), gettext (type), gettext (lock->type));
476 msg (SE, _("Can't write to %s as a %s because it is "
477 "already being written as a %s."),
478 fh_get_name (h), gettext (type), gettext (lock->type));
481 else if (exclusive || lock->exclusive)
483 msg (SE, _("Can't re-open %s as a %s."),
484 fh_get_name (h), gettext (type));
495 hmap_insert (&locks, &key->node, hash);
497 HMAP_FOR_EACH_WITH_HASH (lock, struct fh_lock, node, hash, &locks)
499 if ( 0 == compare_fh_locks (lock, key))
511 /* Releases LOCK that was acquired with fh_lock.
512 Returns true if LOCK is still locked, because other clients
515 Returns false if LOCK has now been destroyed. In this case
516 the caller must ensure that any auxiliary data associated with
517 LOCK is destroyed, to avoid a memory leak. The caller must
518 obtain a pointer to the auxiliary data, e.g. via
519 fh_lock_get_aux *before* calling fh_unlock (because it yields
520 undefined behavior to call fh_lock_get_aux on a destroyed
523 fh_unlock (struct fh_lock *lock)
527 assert (lock->open_cnt > 0);
528 if (--lock->open_cnt == 0)
530 hmap_delete (&locks, &lock->node);
539 /* Returns auxiliary data for LOCK.
541 Auxiliary data is shared by every client that holds LOCK (for
542 an exclusive lock, this is a single client). To avoid leaks,
543 auxiliary data must be released before LOCK is destroyed. */
545 fh_lock_get_aux (const struct fh_lock *lock)
550 /* Sets the auxiliary data for LOCK to AUX. */
552 fh_lock_set_aux (struct fh_lock *lock, void *aux)
557 /* Returns true if HANDLE is locked for the given type of ACCESS,
560 fh_is_locked (const struct file_handle *handle, enum fh_access access)
563 const struct fh_lock *k = NULL;
564 bool is_locked = false;
567 make_key (&key, handle, access);
569 hash = hash_fh_lock (&key);
572 HMAP_FOR_EACH_WITH_HASH (k, struct fh_lock, node, hash, &locks)
574 if ( 0 == compare_fh_locks (k, &key))
586 /* Initializes the key fields in LOCK for looking up or inserting
587 handle H for the given kind of ACCESS. */
589 make_key (struct fh_lock *lock, const struct file_handle *h,
590 enum fh_access access)
592 lock->referent = fh_get_referent (h);
593 lock->access = access;
594 if (lock->referent == FH_REF_FILE)
595 lock->u.file = fn_get_identity (fh_get_file_name (h));
596 else if (lock->referent == FH_REF_SCRATCH)
598 struct scratch_handle *sh = fh_get_scratch_handle (h);
599 lock->u.unique_id = sh != NULL ? sh->unique_id : 0;
603 /* Frees the key fields in LOCK. */
605 free_key (struct fh_lock *lock)
607 if (lock->referent == FH_REF_FILE)
608 fn_free_identity (lock->u.file);
611 /* Compares the key fields in struct fh_lock objects A and B and
612 returns a strcmp()-type result. */
614 compare_fh_locks (const struct fh_lock *a, const struct fh_lock *b)
616 if (a->referent != b->referent)
617 return a->referent < b->referent ? -1 : 1;
618 else if (a->access != b->access)
619 return a->access < b->access ? -1 : 1;
620 else if (a->referent == FH_REF_FILE)
621 return fn_compare_file_identities (a->u.file, b->u.file);
622 else if (a->referent == FH_REF_SCRATCH)
623 return (a->u.unique_id < b->u.unique_id ? -1
624 : a->u.unique_id > b->u.unique_id);
629 /* Returns a hash value for LOCK. */
631 hash_fh_lock (const struct fh_lock *lock)
634 if (lock->referent == FH_REF_FILE)
635 basis = fn_hash_identity (lock->u.file);
636 else if (lock->referent == FH_REF_SCRATCH)
637 basis = lock->u.unique_id;
640 return hash_int ((lock->referent << 3) | lock->access, basis);