1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 1997-9, 2000, 2006 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/hash.h>
28 #include <libpspp/ll.h>
29 #include <libpspp/message.h>
30 #include <libpspp/str.h>
31 #include <data/file-name.h>
32 #include <data/variable.h>
33 #include <data/scratch-handle.h>
38 #define _(msgid) gettext (msgid)
43 struct ll ll; /* Element in global list. */
44 size_t ref_cnt; /* Number of references. */
45 char *id; /* Identifier token, NULL if none. */
46 char *name; /* User-friendly identifying name. */
47 enum fh_referent referent; /* What the file handle refers to. */
49 /* FH_REF_FILE only. */
50 char *file_name; /* File name as provided by user. */
51 enum fh_mode mode; /* File mode. */
53 /* FH_REF_FILE and FH_REF_INLINE only. */
54 size_t record_width; /* Length of fixed-format records. */
55 size_t tab_width; /* Tab width, 0=do not expand tabs. */
57 /* FH_REF_SCRATCH only. */
58 struct scratch_handle *sh; /* Scratch file data. */
61 static struct file_handle *
62 file_handle_from_ll (struct ll *ll)
64 return ll_data (ll, struct file_handle, ll);
67 /* List of all named handles. */
68 static struct ll_list named_handles;
70 /* Default file handle for DATA LIST, REREAD, REPEATING DATA
72 static struct file_handle *default_handle;
74 /* The "file" that reads from BEGIN DATA...END DATA. */
75 static struct file_handle *inline_file;
77 static struct file_handle *create_handle (const char *id,
78 char *name, enum fh_referent);
79 static void free_handle (struct file_handle *);
80 static void unname_handle (struct file_handle *);
82 /* File handle initialization routine. */
86 ll_init (&named_handles);
87 inline_file = create_handle ("INLINE", xstrdup ("INLINE"), FH_REF_INLINE);
88 inline_file->record_width = 80;
89 inline_file->tab_width = 8;
92 /* Removes all named file handles from the global list. */
96 while (!ll_is_empty (&named_handles))
97 unname_handle (file_handle_from_ll (ll_head (&named_handles)));
100 /* Free HANDLE and remove it from the global list. */
102 free_handle (struct file_handle *handle)
104 /* Remove handle from global list. */
105 if (handle->id != NULL)
106 ll_remove (&handle->ll);
111 free (handle->file_name);
112 scratch_handle_destroy (handle->sh);
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 ll_remove (&handle->ll);
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 ll_for_each (handle, struct file_handle, ll, &named_handles)
175 if (!strcasecmp (id, handle->id))
184 /* Creates a new handle with identifier ID (which may be null)
185 and name HANDLE_NAME that refers to REFERENT. Links the new
186 handle into the global list. Returns the new handle.
188 The new handle is not fully initialized. The caller is
189 responsible for completing its initialization. */
190 static struct file_handle *
191 create_handle (const char *id, char *handle_name, enum fh_referent referent)
193 struct file_handle *handle = xzalloc (sizeof *handle);
196 handle->id = id != NULL ? xstrdup (id) : NULL;
197 handle->name = handle_name;
198 handle->referent = referent;
202 assert (fh_from_id (id) == NULL);
203 ll_push_tail (&named_handles, &handle->ll);
210 /* Returns the unique handle of referent type FH_REF_INLINE,
211 which refers to the "inline file" that represents character
212 data in the command file between BEGIN DATA and END DATA. */
214 fh_inline_file (void)
216 fh_ref (inline_file);
220 /* Creates and returns a new file handle with the given ID, which
221 may be null. If it is non-null, it must be unique among
222 existing file identifiers. The new handle is associated with
223 file FILE_NAME and the given PROPERTIES. */
225 fh_create_file (const char *id, const char *file_name,
226 const struct fh_properties *properties)
229 struct file_handle *handle;
231 handle_name = id != NULL ? xstrdup (id) : xasprintf ("\"%s\"", file_name);
232 handle = create_handle (id, handle_name, FH_REF_FILE);
233 handle->file_name = xstrdup (file_name);
234 handle->mode = properties->mode;
235 handle->record_width = properties->record_width;
236 handle->tab_width = properties->tab_width;
240 /* Creates a new file handle with the given ID, which must be
241 unique among existing file identifiers. The new handle is
242 associated with a scratch file (initially empty). */
244 fh_create_scratch (const char *id)
246 struct file_handle *handle;
247 handle = create_handle (id, xstrdup (id), FH_REF_SCRATCH);
252 /* Returns a set of default properties for a file handle. */
253 const struct fh_properties *
254 fh_default_properties (void)
256 static const struct fh_properties default_properties
257 = {FH_MODE_TEXT, 1024, 4};
258 return &default_properties;
261 /* Returns the identifier that may be used in syntax to name the
262 given HANDLE, which takes the form of a PSPP identifier. If
263 HANDLE has no identifier, returns a null pointer.
265 Return value is owned by the file handle.*/
267 fh_get_id (const struct file_handle *handle)
272 /* Returns a user-friendly string to identify the given HANDLE.
273 If HANDLE was created by referring to a file name, returns the
274 file name, enclosed in double quotes. Return value is owned
277 Useful for printing error messages about use of file handles. */
279 fh_get_name (const struct file_handle *handle)
284 /* Returns the type of object that HANDLE refers to. */
286 fh_get_referent (const struct file_handle *handle)
288 return handle->referent;
291 /* Returns the name of the file associated with HANDLE. */
293 fh_get_file_name (const struct file_handle *handle)
295 assert (handle->referent == FH_REF_FILE);
296 return handle->file_name;
299 /* Returns the mode of HANDLE. */
301 fh_get_mode (const struct file_handle *handle)
303 assert (handle->referent == FH_REF_FILE);
307 /* Returns the width of a logical record on HANDLE. */
309 fh_get_record_width (const struct file_handle *handle)
311 assert (handle->referent & (FH_REF_FILE | FH_REF_INLINE));
312 return handle->record_width;
315 /* Returns the number of characters per tab stop for HANDLE, or
316 zero if tabs are not to be expanded. Applicable only to
317 FH_MODE_TEXT files. */
319 fh_get_tab_width (const struct file_handle *handle)
321 assert (handle->referent & (FH_REF_FILE | FH_REF_INLINE));
322 return handle->tab_width;
325 /* Returns the scratch file handle associated with HANDLE.
326 Applicable to only FH_REF_SCRATCH files. */
327 struct scratch_handle *
328 fh_get_scratch_handle (const struct file_handle *handle)
330 assert (handle->referent == FH_REF_SCRATCH);
334 /* Sets SH to be the scratch file handle associated with HANDLE.
335 Applicable to only FH_REF_SCRATCH files. */
337 fh_set_scratch_handle (struct file_handle *handle, struct scratch_handle *sh)
339 assert (handle->referent == FH_REF_SCRATCH);
343 /* Returns the current default handle. */
345 fh_get_default_handle (void)
347 return default_handle ? fh_ref (default_handle) : fh_inline_file ();
350 /* Sets NEW_DEFAULT_HANDLE as the default handle. */
352 fh_set_default_handle (struct file_handle *new_default_handle)
354 assert (new_default_handle == NULL
355 || (new_default_handle->referent & (FH_REF_INLINE | FH_REF_FILE)));
356 if (default_handle != NULL)
357 fh_unref (default_handle);
358 default_handle = new_default_handle;
359 if (default_handle != NULL)
360 fh_ref (default_handle);
363 /* Information about a file handle's readers or writers. */
367 enum fh_referent referent; /* Type of underlying file. */
370 struct file_identity *file; /* FH_REF_FILE only. */
371 unsigned int unique_id; /* FH_REF_SCRATCH only. */
374 enum fh_access access; /* Type of file access. */
376 /* Number of openers. */
379 /* Applicable only when open_cnt > 0. */
380 bool exclusive; /* No other openers allowed? */
381 const char *type; /* Human-readable type of file. */
382 void *aux; /* Owner's auxiliary data. */
385 /* Hash table of all active locks. */
386 static struct hsh_table *locks;
388 static void make_key (struct fh_lock *, const struct file_handle *,
390 static void free_key (struct fh_lock *);
391 static int compare_fh_locks (const void *, const void *, const void *);
392 static unsigned int hash_fh_lock (const void *, const void *);
394 /* Tries to lock handle H for the given kind of ACCESS and TYPE
395 of file. Returns a pointer to a struct fh_lock if successful,
396 otherwise a null pointer.
398 H's referent type must be one of the bits in MASK. The caller
399 must verify this ahead of time; we simply assert it here.
401 TYPE is the sort of file, e.g. "system file". Only one type
402 of access is allowed on a given file at a time for reading,
403 and similarly for writing. If successful, a reference to TYPE
404 is retained, so it should probably be a string literal.
406 TYPE should be marked with N_() in the caller: that is, the
407 caller should not translate it with gettext, but fh_lock will
410 ACCESS specifies whether the lock is for reading or writing.
411 EXCLUSIVE is true to require exclusive access, false to allow
412 sharing with other accessors. Exclusive read access precludes
413 other readers, but not writers; exclusive write access
414 precludes other writers, but not readers. A sharable read or
415 write lock precludes reader or writers, respectively, of a
418 A lock may be associated with auxiliary data. See
419 fh_lock_get_aux and fh_lock_set_aux for more details. */
421 fh_lock (struct file_handle *h, enum fh_referent mask UNUSED,
422 const char *type, enum fh_access access, bool exclusive)
424 struct fh_lock key, *lock;
427 assert ((fh_get_referent (h) & mask) != 0);
428 assert (access == FH_ACC_READ || access == FH_ACC_WRITE);
431 locks = hsh_create (0, compare_fh_locks, hash_fh_lock, NULL, NULL);
433 make_key (&key, h, access);
434 lockp = hsh_probe (locks, &key);
437 lock = *lockp = xmalloc (sizeof *lock);
440 lock->exclusive = exclusive;
449 if (strcmp (lock->type, type))
451 if (access == FH_ACC_READ)
452 msg (SE, _("Can't read from %s as a %s because it is "
453 "already being read as a %s."),
454 fh_get_name (h), gettext (type), gettext (lock->type));
456 msg (SE, _("Can't write to %s as a %s because it is "
457 "already being written as a %s."),
458 fh_get_name (h), gettext (type), gettext (lock->type));
461 else if (exclusive || lock->exclusive)
463 msg (SE, _("Can't re-open %s as a %s."),
464 fh_get_name (h), gettext (type));
473 /* Releases LOCK that was acquired with fh_lock.
474 Returns true if LOCK is still locked, because other clients
477 Returns false if LOCK has now been destroyed. In this case
478 the caller must ensure that any auxiliary data associated with
479 LOCK is destroyed, to avoid a memory leak. The caller must
480 obtain a pointer to the auxiliary data, e.g. via
481 fh_lock_get_aux *before* calling fh_unlock (because it yields
482 undefined behavior to call fh_lock_get_aux on a destroyed
485 fh_unlock (struct fh_lock *lock)
489 assert (lock->open_cnt > 0);
490 if (--lock->open_cnt == 0)
492 hsh_delete (locks, lock);
501 /* Returns auxiliary data for LOCK.
503 Auxiliary data is shared by every client that holds LOCK (for
504 an exclusive lock, this is a single client). To avoid leaks,
505 auxiliary data must be released before LOCK is destroyed. */
507 fh_lock_get_aux (const struct fh_lock *lock)
512 /* Sets the auxiliary data for LOCK to AUX. */
514 fh_lock_set_aux (struct fh_lock *lock, void *aux)
519 /* Returns true if HANDLE is locked for the given type of ACCESS,
522 fh_is_locked (const struct file_handle *handle, enum fh_access access)
527 make_key (&key, handle, access);
528 is_locked = hsh_find (locks, &key) != NULL;
534 /* Initializes the key fields in LOCK for looking up or inserting
535 handle H for the given kind of ACCESS. */
537 make_key (struct fh_lock *lock, const struct file_handle *h,
538 enum fh_access access)
540 lock->referent = fh_get_referent (h);
541 lock->access = access;
542 if (lock->referent == FH_REF_FILE)
543 lock->u.file = fn_get_identity (fh_get_file_name (h));
544 else if (lock->referent == FH_REF_SCRATCH)
546 struct scratch_handle *sh = fh_get_scratch_handle (h);
547 lock->u.unique_id = sh != NULL ? sh->unique_id : 0;
551 /* Frees the key fields in LOCK. */
553 free_key (struct fh_lock *lock)
555 if (lock->referent == FH_REF_FILE)
556 fn_free_identity (lock->u.file);
559 /* Compares the key fields in struct fh_lock objects A and B and
560 returns a strcmp()-type result. */
562 compare_fh_locks (const void *a_, const void *b_, const void *aux UNUSED)
564 const struct fh_lock *a = a_;
565 const struct fh_lock *b = b_;
567 if (a->referent != b->referent)
568 return a->referent < b->referent ? -1 : 1;
569 else if (a->access != b->access)
570 return a->access < b->access ? -1 : 1;
571 else if (a->referent == FH_REF_FILE)
572 return fn_compare_file_identities (a->u.file, b->u.file);
573 else if (a->referent == FH_REF_SCRATCH)
574 return (a->u.unique_id < b->u.unique_id ? -1
575 : a->u.unique_id > b->u.unique_id);
580 /* Returns a hash value for LOCK. */
582 hash_fh_lock (const void *lock_, const void *aux UNUSED)
584 const struct fh_lock *lock = lock_;
585 unsigned int hash = hsh_hash_int ((lock->referent << 3) | lock->access);
586 if (lock->referent == FH_REF_FILE)
587 hash ^= fn_hash_identity (lock->u.file);
588 else if (lock->referent == FH_REF_SCRATCH)
589 hash ^= hsh_hash_int (lock->u.unique_id);