X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Fdata%2Ffile-handle-def.c;h=075f7b07901c79150e84b1b27014ca7202270d53;hb=cb08510bbbab7646bc1031427243489024d22a3b;hp=6ed3f8f9f95205158cbf2a33227e5dbec6a81529;hpb=b5c82cc9aabe7e641011130240ae1b2e84348e23;p=pspp diff --git a/src/data/file-handle-def.c b/src/data/file-handle-def.c index 6ed3f8f9f9..075f7b0790 100644 --- a/src/data/file-handle-def.c +++ b/src/data/file-handle-def.c @@ -1,5 +1,5 @@ /* PSPP - a program for statistical analysis. - Copyright (C) 1997-9, 2000, 2006, 2009 Free Software Foundation, Inc. + Copyright (C) 1997-9, 2000, 2006, 2009, 2010, 2011, 2012, 2013 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -16,31 +16,40 @@ #include -#include "file-handle-def.h" +#include "data/file-handle-def.h" #include #include #include #include -#include -#include -#include -#include -#include -#include -#include -#include +#include "data/dataset.h" +#include "data/variable.h" +#include "libpspp/cast.h" +#include "libpspp/compiler.h" +#include "libpspp/hash-functions.h" +#include "libpspp/hmap.h" +#include "libpspp/i18n.h" +#include "libpspp/message.h" +#include "libpspp/str.h" -#include "xalloc.h" +#include + +#include "gl/dirname.h" +#include "gl/xalloc.h" #include "gettext.h" #define _(msgid) gettext (msgid) +#if defined _WIN32 || defined __WIN32__ +#define WIN32_LEAN_AND_MEAN /* avoid including junk */ +#include +#endif + /* File handle. */ struct file_handle { - struct ll ll; /* Element in global list. */ + struct hmap_node name_node; /* Element in named_handles hmap. */ size_t ref_cnt; /* Number of references. */ char *id; /* Identifier token, NULL if none. */ char *name; /* User-friendly identifying name. */ @@ -48,25 +57,22 @@ struct file_handle /* FH_REF_FILE only. */ char *file_name; /* File name as provided by user. */ + char *file_name_encoding; /* The character encoding of file_name, + This is NOT the encoding of the file contents! */ enum fh_mode mode; /* File mode. */ - const char *encoding; /* File encoding. */ + enum fh_line_ends line_ends; /* Line ends for text files. */ /* FH_REF_FILE and FH_REF_INLINE only. */ size_t record_width; /* Length of fixed-format records. */ size_t tab_width; /* Tab width, 0=do not expand tabs. */ + char *encoding; /* Charset for contents. */ - /* FH_REF_SCRATCH only. */ - struct scratch_handle *sh; /* Scratch file data. */ + /* FH_REF_DATASET only. */ + struct dataset *ds; /* Dataset. */ }; -static struct file_handle * -file_handle_from_ll (struct ll *ll) -{ - return ll_data (ll, struct file_handle, ll); -} - -/* List of all named handles. */ -static struct ll_list named_handles; +/* All "struct file_handle"s with nonnull 'id' member. */ +static struct hmap named_handles = HMAP_INITIALIZER (named_handles); /* Default file handle for DATA LIST, REREAD, REPEATING DATA commands. */ @@ -76,16 +82,26 @@ static struct file_handle *default_handle; static struct file_handle *inline_file; static struct file_handle *create_handle (const char *id, - char *name, enum fh_referent); + char *name, enum fh_referent, + const char *encoding); static void free_handle (struct file_handle *); static void unname_handle (struct file_handle *); +/* Hash table of all active locks. */ +static struct hmap locks = HMAP_INITIALIZER (locks); + +static struct file_identity *fh_get_identity (const struct file_handle *); +static void fh_free_identity (struct file_identity *); +static int fh_compare_file_identities (const struct file_identity *, + const struct file_identity *); +static unsigned int fh_hash_identity (const struct file_identity *); + /* File handle initialization routine. */ void fh_init (void) { - ll_init (&named_handles); - inline_file = create_handle ("INLINE", xstrdup ("INLINE"), FH_REF_INLINE); + inline_file = create_handle ("INLINE", xstrdup ("INLINE"), FH_REF_INLINE, + "Auto"); inline_file->record_width = 80; inline_file->tab_width = 8; } @@ -94,23 +110,32 @@ fh_init (void) void fh_done (void) { - while (!ll_is_empty (&named_handles)) - unname_handle (file_handle_from_ll (ll_head (&named_handles))); + struct file_handle *handle, *next; + + HMAP_FOR_EACH_SAFE (handle, next, + struct file_handle, name_node, &named_handles) + unname_handle (handle); + + free_handle (inline_file); } /* Free HANDLE and remove it from the global list. */ static void free_handle (struct file_handle *handle) { + if (handle == NULL) + return; + /* Remove handle from global list. */ if (handle->id != NULL) - ll_remove (&handle->ll); + hmap_delete (&named_handles, &handle->name_node); /* Free data. */ free (handle->id); free (handle->name); free (handle->file_name); - scratch_handle_destroy (handle->sh); + free (handle->file_name_encoding); + free (handle->encoding); free (handle); } @@ -123,7 +148,7 @@ unname_handle (struct file_handle *handle) assert (handle->id != NULL); free (handle->id); handle->id = NULL; - ll_remove (&handle->ll); + hmap_delete (&named_handles, &handle->name_node); /* Drop the reference held by the named_handles table. */ fh_unref (handle); @@ -133,6 +158,8 @@ unname_handle (struct file_handle *handle) struct file_handle * fh_ref (struct file_handle *handle) { + if (handle == fh_inline_file ()) + return handle; assert (handle->ref_cnt > 0); handle->ref_cnt++; return handle; @@ -145,6 +172,8 @@ fh_unref (struct file_handle *handle) { if (handle != NULL) { + if (handle == fh_inline_file ()) + return; assert (handle->ref_cnt > 0); if (--handle->ref_cnt == 0) free_handle (handle); @@ -172,11 +201,11 @@ fh_from_id (const char *id) { struct file_handle *handle; - ll_for_each (handle, struct file_handle, ll, &named_handles) - if (!strcasecmp (id, handle->id)) + HMAP_FOR_EACH_WITH_HASH (handle, struct file_handle, name_node, + utf8_hash_case_string (id, 0), &named_handles) + if (!utf8_strcasecmp (id, handle->id)) { - handle->ref_cnt++; - return handle; + return fh_ref (handle); } return NULL; @@ -189,20 +218,21 @@ fh_from_id (const char *id) The new handle is not fully initialized. The caller is responsible for completing its initialization. */ static struct file_handle * -create_handle (const char *id, char *handle_name, enum fh_referent referent) +create_handle (const char *id, char *handle_name, enum fh_referent referent, + const char *encoding) { struct file_handle *handle = xzalloc (sizeof *handle); handle->ref_cnt = 1; - handle->id = id != NULL ? xstrdup (id) : NULL; + handle->id = xstrdup_if_nonnull (id); handle->name = handle_name; handle->referent = referent; + handle->encoding = xstrdup (encoding); if (id != NULL) { - assert (fh_from_id (id) == NULL); - ll_push_tail (&named_handles, &handle->ll); - handle->ref_cnt++; + hmap_insert (&named_handles, &handle->name_node, + utf8_hash_case_string (handle->id, 0)); } return handle; @@ -214,40 +244,46 @@ create_handle (const char *id, char *handle_name, enum fh_referent referent) struct file_handle * fh_inline_file (void) { - fh_ref (inline_file); return inline_file; } -/* Creates and returns a new file handle with the given ID, which - may be null. If it is non-null, it must be unique among - existing file identifiers. The new handle is associated with - file FILE_NAME and the given PROPERTIES. */ +/* Creates and returns a new file handle with the given ID, which may be null. + If it is non-null, it must be a UTF-8 encoded string that is unique among + existing file identifiers. The new handle is associated with file FILE_NAME + and the given PROPERTIES. */ struct file_handle * -fh_create_file (const char *id, const char *file_name, +fh_create_file (const char *id, const char *file_name, const char *file_name_encoding, const struct fh_properties *properties) { char *handle_name; struct file_handle *handle; - handle_name = id != NULL ? xstrdup (id) : xasprintf ("\"%s\"", file_name); - handle = create_handle (id, handle_name, FH_REF_FILE); + handle_name = id != NULL ? xstrdup (id) : xasprintf ("`%s'", file_name); + handle = create_handle (id, handle_name, FH_REF_FILE, properties->encoding); handle->file_name = xstrdup (file_name); + handle->file_name_encoding = xstrdup_if_nonnull (file_name_encoding); handle->mode = properties->mode; + handle->line_ends = properties->line_ends; handle->record_width = properties->record_width; handle->tab_width = properties->tab_width; - handle->encoding = properties->encoding; return handle; } /* Creates a new file handle with the given ID, which must be unique among existing file identifiers. The new handle is - associated with a scratch file (initially empty). */ + associated with a dataset file (initially empty). */ struct file_handle * -fh_create_scratch (const char *id) +fh_create_dataset (struct dataset *ds) { + const char *name; struct file_handle *handle; - handle = create_handle (id, xstrdup (id), FH_REF_SCRATCH); - handle->sh = NULL; + + name = dataset_name (ds); + if (name[0] == '\0') + name = _("active dataset"); + + handle = create_handle (NULL, xstrdup (name), FH_REF_DATASET, C_ENCODING); + handle->ds = ds; return handle; } @@ -255,8 +291,14 @@ fh_create_scratch (const char *id) const struct fh_properties * fh_default_properties (void) { +#if defined _WIN32 || defined __WIN32__ +#define DEFAULT_LINE_ENDS FH_END_CRLF +#else +#define DEFAULT_LINE_ENDS FH_END_LF +#endif + static const struct fh_properties default_properties - = {FH_MODE_TEXT, 1024, 4, LEGACY_NATIVE}; + = {FH_MODE_TEXT, DEFAULT_LINE_ENDS, 1024, 4, (char *) "Auto"}; return &default_properties; } @@ -298,6 +340,16 @@ fh_get_file_name (const struct file_handle *handle) return handle->file_name; } + +/* Returns the character encoding of the name of the file associated with HANDLE. */ +const char * +fh_get_file_name_encoding (const struct file_handle *handle) +{ + assert (handle->referent == FH_REF_FILE); + return handle->file_name_encoding; +} + + /* Returns the mode of HANDLE. */ enum fh_mode fh_get_mode (const struct file_handle *handle) @@ -306,6 +358,15 @@ fh_get_mode (const struct file_handle *handle) return handle->mode; } +/* Returns the line ends of HANDLE, which must be a handle associated with a + file. */ +enum fh_line_ends +fh_get_line_ends (const struct file_handle *handle) +{ + assert (handle->referent == FH_REF_FILE); + return handle->line_ends; +} + /* Returns the width of a logical record on HANDLE. */ size_t fh_get_record_width (const struct file_handle *handle) @@ -326,35 +387,25 @@ fh_get_tab_width (const struct file_handle *handle) /* Returns the encoding of characters read from HANDLE. */ const char * -fh_get_legacy_encoding (const struct file_handle *handle) +fh_get_encoding (const struct file_handle *handle) { - assert (handle->referent & (FH_REF_FILE | FH_REF_INLINE)); - return (handle->referent == FH_REF_FILE ? handle->encoding : LEGACY_NATIVE); + return handle->encoding; } -/* Returns the scratch file handle associated with HANDLE. - Applicable to only FH_REF_SCRATCH files. */ -struct scratch_handle * -fh_get_scratch_handle (const struct file_handle *handle) +/* Returns the dataset handle associated with HANDLE. + Applicable to only FH_REF_DATASET files. */ +struct dataset * +fh_get_dataset (const struct file_handle *handle) { - assert (handle->referent == FH_REF_SCRATCH); - return handle->sh; -} - -/* Sets SH to be the scratch file handle associated with HANDLE. - Applicable to only FH_REF_SCRATCH files. */ -void -fh_set_scratch_handle (struct file_handle *handle, struct scratch_handle *sh) -{ - assert (handle->referent == FH_REF_SCRATCH); - handle->sh = sh; + assert (handle->referent == FH_REF_DATASET); + return handle->ds; } /* Returns the current default handle. */ struct file_handle * fh_get_default_handle (void) { - return default_handle ? fh_ref (default_handle) : fh_inline_file (); + return default_handle ? default_handle : fh_inline_file (); } /* Sets NEW_DEFAULT_HANDLE as the default handle. */ @@ -363,22 +414,24 @@ fh_set_default_handle (struct file_handle *new_default_handle) { assert (new_default_handle == NULL || (new_default_handle->referent & (FH_REF_INLINE | FH_REF_FILE))); - if (default_handle != NULL) + if (default_handle != NULL && default_handle != inline_file) fh_unref (default_handle); default_handle = new_default_handle; if (default_handle != NULL) - fh_ref (default_handle); + default_handle = fh_ref (default_handle); } /* Information about a file handle's readers or writers. */ struct fh_lock { + struct hmap_node node; /* hmap_node member. */ + /* Hash key. */ enum fh_referent referent; /* Type of underlying file. */ union { struct file_identity *file; /* FH_REF_FILE only. */ - unsigned int unique_id; /* FH_REF_SCRATCH only. */ + unsigned int unique_id; /* FH_REF_DATASET only. */ } u; enum fh_access access; /* Type of file access. */ @@ -392,14 +445,12 @@ struct fh_lock void *aux; /* Owner's auxiliary data. */ }; -/* Hash table of all active locks. */ -static struct hsh_table *locks; static void make_key (struct fh_lock *, const struct file_handle *, enum fh_access); static void free_key (struct fh_lock *); -static int compare_fh_locks (const void *, const void *, const void *); -static unsigned int hash_fh_lock (const void *, const void *); +static int compare_fh_locks (const struct fh_lock *a, const struct fh_lock *b); +static unsigned int hash_fh_lock (const struct fh_lock *lock); /* Tries to lock handle H for the given kind of ACCESS and TYPE of file. Returns a pointer to a struct fh_lock if successful, @@ -431,31 +482,36 @@ struct fh_lock * fh_lock (struct file_handle *h, enum fh_referent mask UNUSED, const char *type, enum fh_access access, bool exclusive) { - struct fh_lock key, *lock; - void **lockp; + struct fh_lock *key = NULL; + size_t hash ; + struct fh_lock *lock = NULL; + bool found_lock = false; assert ((fh_get_referent (h) & mask) != 0); assert (access == FH_ACC_READ || access == FH_ACC_WRITE); - if (locks == NULL) - locks = hsh_create (0, compare_fh_locks, hash_fh_lock, NULL, NULL); + key = xmalloc (sizeof *key); + + make_key (key, h, access); - make_key (&key, h, access); - lockp = hsh_probe (locks, &key); - if (*lockp == NULL) + key->open_cnt = 1; + key->exclusive = exclusive; + key->type = type; + key->aux = NULL; + + hash = hash_fh_lock (key); + + HMAP_FOR_EACH_WITH_HASH (lock, struct fh_lock, node, hash, &locks) { - lock = *lockp = xmalloc (sizeof *lock); - *lock = key; - lock->open_cnt = 1; - lock->exclusive = exclusive; - lock->type = type; - lock->aux = NULL; + if (0 == compare_fh_locks (lock, key)) + { + found_lock = true; + break; + } } - else - { - free_key (&key); - lock = *lockp; + if (found_lock) + { if (strcmp (lock->type, type)) { if (access == FH_ACC_READ) @@ -475,9 +531,27 @@ fh_lock (struct file_handle *h, enum fh_referent mask UNUSED, return NULL; } lock->open_cnt++; + + free_key (key); + free (key); + + return lock; + } + + hmap_insert (&locks, &key->node, hash); + found_lock = false; + HMAP_FOR_EACH_WITH_HASH (lock, struct fh_lock, node, hash, &locks) + { + if (0 == compare_fh_locks (lock, key)) + { + found_lock = true; + break; + } } - return lock; + assert (found_lock); + + return key; } /* Releases LOCK that was acquired with fh_lock. @@ -499,7 +573,7 @@ fh_unlock (struct fh_lock *lock) assert (lock->open_cnt > 0); if (--lock->open_cnt == 0) { - hsh_delete (locks, lock); + hmap_delete (&locks, &lock->node); free_key (lock); free (lock); return false; @@ -532,10 +606,24 @@ bool fh_is_locked (const struct file_handle *handle, enum fh_access access) { struct fh_lock key; - bool is_locked; + const struct fh_lock *k = NULL; + bool is_locked = false; + size_t hash ; make_key (&key, handle, access); - is_locked = hsh_find (locks, &key) != NULL; + + hash = hash_fh_lock (&key); + + + HMAP_FOR_EACH_WITH_HASH (k, struct fh_lock, node, hash, &locks) + { + if (0 == compare_fh_locks (k, &key)) + { + is_locked = true; + break; + } + } + free_key (&key); return is_locked; @@ -550,12 +638,9 @@ make_key (struct fh_lock *lock, const struct file_handle *h, lock->referent = fh_get_referent (h); lock->access = access; if (lock->referent == FH_REF_FILE) - lock->u.file = fn_get_identity (fh_get_file_name (h)); - else if (lock->referent == FH_REF_SCRATCH) - { - struct scratch_handle *sh = fh_get_scratch_handle (h); - lock->u.unique_id = sh != NULL ? sh->unique_id : 0; - } + lock->u.file = fh_get_identity (h); + else if (lock->referent == FH_REF_DATASET) + lock->u.unique_id = dataset_seqno (fh_get_dataset (h)); } /* Frees the key fields in LOCK. */ @@ -563,24 +648,21 @@ static void free_key (struct fh_lock *lock) { if (lock->referent == FH_REF_FILE) - fn_free_identity (lock->u.file); + fh_free_identity (lock->u.file); } /* Compares the key fields in struct fh_lock objects A and B and returns a strcmp()-type result. */ static int -compare_fh_locks (const void *a_, const void *b_, const void *aux UNUSED) +compare_fh_locks (const struct fh_lock *a, const struct fh_lock *b) { - const struct fh_lock *a = a_; - const struct fh_lock *b = b_; - if (a->referent != b->referent) return a->referent < b->referent ? -1 : 1; else if (a->access != b->access) return a->access < b->access ? -1 : 1; else if (a->referent == FH_REF_FILE) - return fn_compare_file_identities (a->u.file, b->u.file); - else if (a->referent == FH_REF_SCRATCH) + return fh_compare_file_identities (a->u.file, b->u.file); + else if (a->referent == FH_REF_DATASET) return (a->u.unique_id < b->u.unique_id ? -1 : a->u.unique_id > b->u.unique_id); else @@ -589,15 +671,153 @@ compare_fh_locks (const void *a_, const void *b_, const void *aux UNUSED) /* Returns a hash value for LOCK. */ static unsigned int -hash_fh_lock (const void *lock_, const void *aux UNUSED) +hash_fh_lock (const struct fh_lock *lock) { - const struct fh_lock *lock = lock_; unsigned int basis; if (lock->referent == FH_REF_FILE) - basis = fn_hash_identity (lock->u.file); - else if (lock->referent == FH_REF_SCRATCH) + basis = fh_hash_identity (lock->u.file); + else if (lock->referent == FH_REF_DATASET) basis = lock->u.unique_id; else basis = 0; return hash_int ((lock->referent << 3) | lock->access, basis); } + + + + + + +/* A file's identity: + + - For a file that exists, this is its device and inode. + + - For a file that does not exist, but which has a directory + name that exists, this is the device and inode of the + directory, plus the file's base name. + + - For a file that does not exist and has a nonexistent + directory, this is the file name. + + Windows doesn't have inode numbers, so we just use the name + there. */ +struct file_identity +{ + unsigned long long device; /* Device number. */ + unsigned long long inode; /* Inode number. */ + char *name; /* File name, where needed, otherwise NULL. */ +}; + +/* Returns a pointer to a dynamically allocated structure whose + value can be used to tell whether two files are actually the + same file. The caller is responsible for freeing the structure with + fh_free_identity() when finished. */ +static struct file_identity * +fh_get_identity (const struct file_handle *fh) +{ + struct file_identity *identity = xmalloc (sizeof *identity); + + const char *file_name = fh_get_file_name (fh); + +#if !(defined _WIN32 || defined __WIN32__) + struct stat s; + if (lstat (file_name, &s) == 0) + { + identity->device = s.st_dev; + identity->inode = s.st_ino; + identity->name = NULL; + } + else + { + char *dir = dir_name (file_name); + if (last_component (file_name) != NULL && stat (dir, &s) == 0) + { + identity->device = s.st_dev; + identity->inode = s.st_ino; + identity->name = base_name (file_name); + } + else + { + identity->device = 0; + identity->inode = 0; + identity->name = xstrdup (file_name); + } + free (dir); + } +#else /* Windows */ + bool ok = false; + HANDLE h = CreateFile (file_name, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL); + if (h != INVALID_HANDLE_VALUE) + { + BY_HANDLE_FILE_INFORMATION fi; + ok = GetFileInformationByHandle (h, &fi); + if (ok) + { + identity->device = fi.dwVolumeSerialNumber; + identity->inode = fi.nFileIndexHigh; + identity->inode <<= (sizeof fi.nFileIndexLow) * CHAR_BIT; + identity->inode |= fi.nFileIndexLow; + identity->name = 0; + } + CloseHandle (h); + } + + if (!ok) + { + identity->device = 0; + identity->inode = 0; + + size_t bufsize; + size_t pathlen = 255; + char *cname = NULL; + do + { + bufsize = pathlen; + cname = xrealloc (cname, bufsize); + pathlen = GetFullPathName (file_name, bufsize, cname, NULL); + } + while (pathlen > bufsize); + identity->name = xstrdup (cname); + free (cname); + str_lowercase (identity->name); + } +#endif /* Windows */ + + return identity; +} + +/* Frees IDENTITY obtained from fh_get_identity(). */ +void +fh_free_identity (struct file_identity *identity) +{ + if (identity != NULL) + { + free (identity->name); + free (identity); + } +} + +/* Compares A and B, returning a strcmp()-type result. */ +int +fh_compare_file_identities (const struct file_identity *a, + const struct file_identity *b) +{ + if (a->device != b->device) + return a->device < b->device ? -1 : 1; + else if (a->inode != b->inode) + return a->inode < b->inode ? -1 : 1; + else if (a->name != NULL) + return b->name != NULL ? strcmp (a->name, b->name) : 1; + else + return b->name != NULL ? -1 : 0; +} + +/* Returns a hash value for IDENTITY. */ +unsigned int +fh_hash_identity (const struct file_identity *identity) +{ + unsigned int hash = hash_int (identity->device, identity->inode); + if (identity->name != NULL) + hash = hash_string (identity->name, hash); + return hash; +}