1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 1997-9, 2000, 2006, 2009 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. */
52 enum legacy_encoding encoding;/* File encoding. */
54 /* FH_REF_FILE and FH_REF_INLINE only. */
55 size_t record_width; /* Length of fixed-format records. */
56 size_t tab_width; /* Tab width, 0=do not expand tabs. */
58 /* FH_REF_SCRATCH only. */
59 struct scratch_handle *sh; /* Scratch file data. */
62 static struct file_handle *
63 file_handle_from_ll (struct ll *ll)
65 return ll_data (ll, struct file_handle, ll);
68 /* List of all named handles. */
69 static struct ll_list named_handles;
71 /* Default file handle for DATA LIST, REREAD, REPEATING DATA
73 static struct file_handle *default_handle;
75 /* The "file" that reads from BEGIN DATA...END DATA. */
76 static struct file_handle *inline_file;
78 static struct file_handle *create_handle (const char *id,
79 char *name, enum fh_referent);
80 static void free_handle (struct file_handle *);
81 static void unname_handle (struct file_handle *);
83 /* File handle initialization routine. */
87 ll_init (&named_handles);
88 inline_file = create_handle ("INLINE", xstrdup ("INLINE"), FH_REF_INLINE);
89 inline_file->record_width = 80;
90 inline_file->tab_width = 8;
93 /* Removes all named file handles from the global list. */
97 while (!ll_is_empty (&named_handles))
98 unname_handle (file_handle_from_ll (ll_head (&named_handles)));
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 ll_remove (&handle->ll);
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 ll_remove (&handle->ll);
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 ll_for_each (handle, struct file_handle, ll, &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 ll_push_tail (&named_handles, &handle->ll);
211 /* Returns the unique handle of referent type FH_REF_INLINE,
212 which refers to the "inline file" that represents character
213 data in the command file between BEGIN DATA and END DATA. */
215 fh_inline_file (void)
217 fh_ref (inline_file);
221 /* Creates and returns a new file handle with the given ID, which
222 may be null. If it is non-null, it must be unique among
223 existing file identifiers. The new handle is associated with
224 file FILE_NAME and the given PROPERTIES. */
226 fh_create_file (const char *id, const char *file_name,
227 const struct fh_properties *properties)
230 struct file_handle *handle;
232 handle_name = id != NULL ? xstrdup (id) : xasprintf ("\"%s\"", file_name);
233 handle = create_handle (id, handle_name, FH_REF_FILE);
234 handle->file_name = xstrdup (file_name);
235 handle->mode = properties->mode;
236 handle->record_width = properties->record_width;
237 handle->tab_width = properties->tab_width;
238 handle->encoding = properties->encoding;
242 /* Creates a new file handle with the given ID, which must be
243 unique among existing file identifiers. The new handle is
244 associated with a scratch file (initially empty). */
246 fh_create_scratch (const char *id)
248 struct file_handle *handle;
249 handle = create_handle (id, xstrdup (id), FH_REF_SCRATCH);
254 /* Returns a set of default properties for a file handle. */
255 const struct fh_properties *
256 fh_default_properties (void)
258 static const struct fh_properties default_properties
259 = {FH_MODE_TEXT, 1024, 4, LEGACY_NATIVE};
260 return &default_properties;
263 /* Returns the identifier that may be used in syntax to name the
264 given HANDLE, which takes the form of a PSPP identifier. If
265 HANDLE has no identifier, returns a null pointer.
267 Return value is owned by the file handle.*/
269 fh_get_id (const struct file_handle *handle)
274 /* Returns a user-friendly string to identify the given HANDLE.
275 If HANDLE was created by referring to a file name, returns the
276 file name, enclosed in double quotes. Return value is owned
279 Useful for printing error messages about use of file handles. */
281 fh_get_name (const struct file_handle *handle)
286 /* Returns the type of object that HANDLE refers to. */
288 fh_get_referent (const struct file_handle *handle)
290 return handle->referent;
293 /* Returns the name of the file associated with HANDLE. */
295 fh_get_file_name (const struct file_handle *handle)
297 assert (handle->referent == FH_REF_FILE);
298 return handle->file_name;
301 /* Returns the mode of HANDLE. */
303 fh_get_mode (const struct file_handle *handle)
305 assert (handle->referent == FH_REF_FILE);
309 /* Returns the width of a logical record on HANDLE. */
311 fh_get_record_width (const struct file_handle *handle)
313 assert (handle->referent & (FH_REF_FILE | FH_REF_INLINE));
314 return handle->record_width;
317 /* Returns the number of characters per tab stop for HANDLE, or
318 zero if tabs are not to be expanded. Applicable only to
319 FH_MODE_TEXT files. */
321 fh_get_tab_width (const struct file_handle *handle)
323 assert (handle->referent & (FH_REF_FILE | FH_REF_INLINE));
324 return handle->tab_width;
327 /* Returns the encoding of characters read from HANDLE. */
329 fh_get_legacy_encoding (const struct file_handle *handle)
331 assert (handle->referent & (FH_REF_FILE | FH_REF_INLINE));
332 return (handle->referent == FH_REF_FILE ? handle->encoding : LEGACY_NATIVE);
335 /* Returns the scratch file handle associated with HANDLE.
336 Applicable to only FH_REF_SCRATCH files. */
337 struct scratch_handle *
338 fh_get_scratch_handle (const struct file_handle *handle)
340 assert (handle->referent == FH_REF_SCRATCH);
344 /* Sets SH to be the scratch file handle associated with HANDLE.
345 Applicable to only FH_REF_SCRATCH files. */
347 fh_set_scratch_handle (struct file_handle *handle, struct scratch_handle *sh)
349 assert (handle->referent == FH_REF_SCRATCH);
353 /* Returns the current default handle. */
355 fh_get_default_handle (void)
357 return default_handle ? fh_ref (default_handle) : fh_inline_file ();
360 /* Sets NEW_DEFAULT_HANDLE as the default handle. */
362 fh_set_default_handle (struct file_handle *new_default_handle)
364 assert (new_default_handle == NULL
365 || (new_default_handle->referent & (FH_REF_INLINE | FH_REF_FILE)));
366 if (default_handle != NULL)
367 fh_unref (default_handle);
368 default_handle = new_default_handle;
369 if (default_handle != NULL)
370 fh_ref (default_handle);
373 /* Information about a file handle's readers or writers. */
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_SCRATCH 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. */
395 /* Hash table of all active locks. */
396 static struct hsh_table *locks;
398 static void make_key (struct fh_lock *, const struct file_handle *,
400 static void free_key (struct fh_lock *);
401 static int compare_fh_locks (const void *, const void *, const void *);
402 static unsigned int hash_fh_lock (const void *, const void *);
404 /* Tries to lock handle H for the given kind of ACCESS and TYPE
405 of file. Returns a pointer to a struct fh_lock if successful,
406 otherwise a null pointer.
408 H's referent type must be one of the bits in MASK. The caller
409 must verify this ahead of time; we simply assert it here.
411 TYPE is the sort of file, e.g. "system file". Only one type
412 of access is allowed on a given file at a time for reading,
413 and similarly for writing. If successful, a reference to TYPE
414 is retained, so it should probably be a string literal.
416 TYPE should be marked with N_() in the caller: that is, the
417 caller should not translate it with gettext, but fh_lock will
420 ACCESS specifies whether the lock is for reading or writing.
421 EXCLUSIVE is true to require exclusive access, false to allow
422 sharing with other accessors. Exclusive read access precludes
423 other readers, but not writers; exclusive write access
424 precludes other writers, but not readers. A sharable read or
425 write lock precludes reader or writers, respectively, of a
428 A lock may be associated with auxiliary data. See
429 fh_lock_get_aux and fh_lock_set_aux for more details. */
431 fh_lock (struct file_handle *h, enum fh_referent mask UNUSED,
432 const char *type, enum fh_access access, bool exclusive)
434 struct fh_lock key, *lock;
437 assert ((fh_get_referent (h) & mask) != 0);
438 assert (access == FH_ACC_READ || access == FH_ACC_WRITE);
441 locks = hsh_create (0, compare_fh_locks, hash_fh_lock, NULL, NULL);
443 make_key (&key, h, access);
444 lockp = hsh_probe (locks, &key);
447 lock = *lockp = xmalloc (sizeof *lock);
450 lock->exclusive = exclusive;
459 if (strcmp (lock->type, type))
461 if (access == FH_ACC_READ)
462 msg (SE, _("Can't read from %s as a %s because it is "
463 "already being read as a %s."),
464 fh_get_name (h), gettext (type), gettext (lock->type));
466 msg (SE, _("Can't write to %s as a %s because it is "
467 "already being written as a %s."),
468 fh_get_name (h), gettext (type), gettext (lock->type));
471 else if (exclusive || lock->exclusive)
473 msg (SE, _("Can't re-open %s as a %s."),
474 fh_get_name (h), gettext (type));
483 /* Releases LOCK that was acquired with fh_lock.
484 Returns true if LOCK is still locked, because other clients
487 Returns false if LOCK has now been destroyed. In this case
488 the caller must ensure that any auxiliary data associated with
489 LOCK is destroyed, to avoid a memory leak. The caller must
490 obtain a pointer to the auxiliary data, e.g. via
491 fh_lock_get_aux *before* calling fh_unlock (because it yields
492 undefined behavior to call fh_lock_get_aux on a destroyed
495 fh_unlock (struct fh_lock *lock)
499 assert (lock->open_cnt > 0);
500 if (--lock->open_cnt == 0)
502 hsh_delete (locks, lock);
511 /* Returns auxiliary data for LOCK.
513 Auxiliary data is shared by every client that holds LOCK (for
514 an exclusive lock, this is a single client). To avoid leaks,
515 auxiliary data must be released before LOCK is destroyed. */
517 fh_lock_get_aux (const struct fh_lock *lock)
522 /* Sets the auxiliary data for LOCK to AUX. */
524 fh_lock_set_aux (struct fh_lock *lock, void *aux)
529 /* Returns true if HANDLE is locked for the given type of ACCESS,
532 fh_is_locked (const struct file_handle *handle, enum fh_access access)
537 make_key (&key, handle, access);
538 is_locked = hsh_find (locks, &key) != NULL;
544 /* Initializes the key fields in LOCK for looking up or inserting
545 handle H for the given kind of ACCESS. */
547 make_key (struct fh_lock *lock, const struct file_handle *h,
548 enum fh_access access)
550 lock->referent = fh_get_referent (h);
551 lock->access = access;
552 if (lock->referent == FH_REF_FILE)
553 lock->u.file = fn_get_identity (fh_get_file_name (h));
554 else if (lock->referent == FH_REF_SCRATCH)
556 struct scratch_handle *sh = fh_get_scratch_handle (h);
557 lock->u.unique_id = sh != NULL ? sh->unique_id : 0;
561 /* Frees the key fields in LOCK. */
563 free_key (struct fh_lock *lock)
565 if (lock->referent == FH_REF_FILE)
566 fn_free_identity (lock->u.file);
569 /* Compares the key fields in struct fh_lock objects A and B and
570 returns a strcmp()-type result. */
572 compare_fh_locks (const void *a_, const void *b_, const void *aux UNUSED)
574 const struct fh_lock *a = a_;
575 const struct fh_lock *b = b_;
577 if (a->referent != b->referent)
578 return a->referent < b->referent ? -1 : 1;
579 else if (a->access != b->access)
580 return a->access < b->access ? -1 : 1;
581 else if (a->referent == FH_REF_FILE)
582 return fn_compare_file_identities (a->u.file, b->u.file);
583 else if (a->referent == FH_REF_SCRATCH)
584 return (a->u.unique_id < b->u.unique_id ? -1
585 : a->u.unique_id > b->u.unique_id);
590 /* Returns a hash value for LOCK. */
592 hash_fh_lock (const void *lock_, const void *aux UNUSED)
594 const struct fh_lock *lock = lock_;
596 if (lock->referent == FH_REF_FILE)
597 basis = fn_hash_identity (lock->u.file);
598 else if (lock->referent == FH_REF_SCRATCH)
599 basis = lock->u.unique_id;
602 return hash_int ((lock->referent << 3) | lock->access, basis);