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=9c853e5e983d7968bf427e0a01765680c1cec0e0;hpb=504ee72af2e3f4df358ca45b8acb847affdb85cb;p=pspp diff --git a/src/data/file-handle-def.c b/src/data/file-handle-def.c index 9c853e5e98..075f7b0790 100644 --- a/src/data/file-handle-def.c +++ b/src/data/file-handle-def.c @@ -24,7 +24,6 @@ #include #include "data/dataset.h" -#include "data/file-name.h" #include "data/variable.h" #include "libpspp/cast.h" #include "libpspp/compiler.h" @@ -34,11 +33,19 @@ #include "libpspp/message.h" #include "libpspp/str.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 { @@ -50,6 +57,8 @@ 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. */ enum fh_line_ends line_ends; /* Line ends for text files. */ @@ -81,6 +90,12 @@ 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) @@ -100,12 +115,17 @@ fh_done (void) 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) hmap_delete (&named_handles, &handle->name_node); @@ -114,6 +134,7 @@ free_handle (struct file_handle *handle) free (handle->id); free (handle->name); free (handle->file_name); + free (handle->file_name_encoding); free (handle->encoding); free (handle); } @@ -137,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; @@ -149,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); @@ -199,7 +224,7 @@ create_handle (const char *id, char *handle_name, enum fh_referent referent, 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); @@ -227,7 +252,7 @@ fh_inline_file (void) 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; @@ -236,6 +261,7 @@ fh_create_file (const char *id, const char *file_name, 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; @@ -314,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) @@ -382,7 +418,7 @@ fh_set_default_handle (struct file_handle *new_default_handle) 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. */ @@ -467,14 +503,14 @@ fh_lock (struct file_handle *h, enum fh_referent mask UNUSED, HMAP_FOR_EACH_WITH_HASH (lock, struct fh_lock, node, hash, &locks) { - if ( 0 == compare_fh_locks (lock, key)) + if (0 == compare_fh_locks (lock, key)) { found_lock = true; break; } } - if ( found_lock ) + if (found_lock) { if (strcmp (lock->type, type)) { @@ -495,7 +531,7 @@ fh_lock (struct file_handle *h, enum fh_referent mask UNUSED, return NULL; } lock->open_cnt++; - + free_key (key); free (key); @@ -506,7 +542,7 @@ fh_lock (struct file_handle *h, enum fh_referent mask UNUSED, found_lock = false; HMAP_FOR_EACH_WITH_HASH (lock, struct fh_lock, node, hash, &locks) { - if ( 0 == compare_fh_locks (lock, key)) + if (0 == compare_fh_locks (lock, key)) { found_lock = true; break; @@ -581,7 +617,7 @@ fh_is_locked (const struct file_handle *handle, enum fh_access access) HMAP_FOR_EACH_WITH_HASH (k, struct fh_lock, node, hash, &locks) { - if ( 0 == compare_fh_locks (k, &key)) + if (0 == compare_fh_locks (k, &key)) { is_locked = true; break; @@ -602,7 +638,7 @@ 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)); + lock->u.file = fh_get_identity (h); else if (lock->referent == FH_REF_DATASET) lock->u.unique_id = dataset_seqno (fh_get_dataset (h)); } @@ -612,7 +648,7 @@ 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 @@ -625,7 +661,7 @@ compare_fh_locks (const struct fh_lock *a, const struct fh_lock *b) 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); + 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); @@ -639,10 +675,149 @@ hash_fh_lock (const struct fh_lock *lock) { unsigned int basis; if (lock->referent == FH_REF_FILE) - basis = fn_hash_identity (lock->u.file); + 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; +}