file-handle-def: New function fh_equal().
[pspp] / src / data / file-handle-def.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 1997-9, 2000, 2006, 2009, 2010, 2011, 2012, 2013 Free Software Foundation, Inc.
3
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.
8
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.
13
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/>. */
16
17 #include <config.h>
18
19 #include "data/file-handle-def.h"
20
21 #include <assert.h>
22 #include <errno.h>
23 #include <stdlib.h>
24 #include <string.h>
25
26 #include "data/dataset.h"
27 #include "data/variable.h"
28 #include "libpspp/assertion.h"
29 #include "libpspp/cast.h"
30 #include "libpspp/compiler.h"
31 #include "libpspp/hash-functions.h"
32 #include "libpspp/hmap.h"
33 #include "libpspp/i18n.h"
34 #include "libpspp/message.h"
35 #include "libpspp/str.h"
36
37 #include <sys/stat.h>
38
39 #include "gl/dirname.h"
40 #include "gl/xalloc.h"
41
42 #include "gettext.h"
43 #define _(msgid) gettext (msgid)
44
45 #if defined _WIN32 || defined __WIN32__
46 #define WIN32_LEAN_AND_MEAN  /* avoid including junk */
47 #include <windows.h>
48 #endif
49
50 /* File handle. */
51 struct file_handle
52   {
53     struct hmap_node name_node; /* Element in named_handles hmap. */
54     size_t ref_cnt;             /* Number of references. */
55     char *id;                   /* Identifier token, NULL if none. */
56     char *name;                 /* User-friendly identifying name. */
57     enum fh_referent referent;  /* What the file handle refers to. */
58
59     /* FH_REF_FILE only. */
60     char *file_name;            /* File name as provided by user. */
61     char *file_name_encoding;   /* The character encoding of file_name,
62                                    This is NOT the encoding of the file contents! */
63     enum fh_mode mode;          /* File mode. */
64     enum fh_line_ends line_ends; /* Line ends for text files. */
65
66     /* FH_REF_FILE and FH_REF_INLINE only. */
67     size_t record_width;        /* Length of fixed-format records. */
68     size_t tab_width;           /* Tab width, 0=do not expand tabs. */
69     char *encoding;             /* Charset for contents. */
70
71     /* FH_REF_DATASET only. */
72     struct dataset *ds;         /* Dataset. */
73   };
74
75 /* All "struct file_handle"s with nonnull 'id' member. */
76 static struct hmap named_handles = HMAP_INITIALIZER (named_handles);
77
78 /* Default file handle for DATA LIST, REREAD, REPEATING DATA
79    commands. */
80 static struct file_handle *default_handle;
81
82 /* The "file" that reads from BEGIN DATA...END DATA. */
83 static struct file_handle *inline_file;
84
85 static struct file_handle *create_handle (const char *id,
86                                           char *name, enum fh_referent,
87                                           const char *encoding);
88 static void free_handle (struct file_handle *);
89 static void unname_handle (struct file_handle *);
90
91 /* Hash table of all active locks. */
92 static struct hmap locks = HMAP_INITIALIZER (locks);
93
94 static struct file_identity *fh_get_identity (const struct file_handle *);
95 static void fh_free_identity (struct file_identity *);
96 static int fh_compare_file_identities (const struct file_identity *,
97                                 const struct file_identity *);
98 static unsigned int fh_hash_identity (const struct file_identity *);
99
100 /* File handle initialization routine. */
101 void
102 fh_init (void)
103 {
104   inline_file = create_handle ("INLINE", xstrdup ("INLINE"), FH_REF_INLINE,
105                                "Auto");
106   inline_file->record_width = 80;
107   inline_file->tab_width = 8;
108 }
109
110 /* Removes all named file handles from the global list. */
111 void
112 fh_done (void)
113 {
114   struct file_handle *handle, *next;
115
116   HMAP_FOR_EACH_SAFE (handle, next,
117                       struct file_handle, name_node, &named_handles)
118     unname_handle (handle);
119
120   free_handle (inline_file);
121 }
122
123 /* Free HANDLE and remove it from the global list. */
124 static void
125 free_handle (struct file_handle *handle)
126 {
127   if (handle == NULL)
128     return;
129
130   /* Remove handle from global list. */
131   if (handle->id != NULL)
132     hmap_delete (&named_handles, &handle->name_node);
133
134   /* Free data. */
135   free (handle->id);
136   free (handle->name);
137   free (handle->file_name);
138   free (handle->file_name_encoding);
139   free (handle->encoding);
140   free (handle);
141 }
142
143 /* Make HANDLE unnamed, so that it can no longer be referenced by
144    name.  The caller must hold a reference to HANDLE, which is
145    not affected by this function. */
146 static void
147 unname_handle (struct file_handle *handle)
148 {
149   assert (handle->id != NULL);
150   free (handle->id);
151   handle->id = NULL;
152   hmap_delete (&named_handles, &handle->name_node);
153
154   /* Drop the reference held by the named_handles table. */
155   fh_unref (handle);
156 }
157
158 /* Increments HANDLE's reference count and returns HANDLE. */
159 struct file_handle *
160 fh_ref (struct file_handle *handle)
161 {
162   if (handle == fh_inline_file ())
163     return handle;
164   assert (handle->ref_cnt > 0);
165   handle->ref_cnt++;
166   return handle;
167 }
168
169 /* Decrements HANDLE's reference count.
170    If the reference count drops to 0, HANDLE is destroyed. */
171 void
172 fh_unref (struct file_handle *handle)
173 {
174   if (handle != NULL)
175     {
176       if (handle == fh_inline_file ())
177         return;
178       assert (handle->ref_cnt > 0);
179       if (--handle->ref_cnt == 0)
180         free_handle (handle);
181     }
182 }
183
184 /* Make HANDLE unnamed, so that it can no longer be referenced by
185    name.  The caller must hold a reference to HANDLE, which is
186    not affected by this function.
187
188    This function ignores a null pointer as input.  It has no
189    effect on the inline handle, which is always named INLINE.*/
190 void
191 fh_unname (struct file_handle *handle)
192 {
193   assert (handle->ref_cnt > 1);
194   if (handle != fh_inline_file () && handle->id != NULL)
195     unname_handle (handle);
196 }
197
198 /* Returns the handle with the given ID, or a null pointer if
199    there is none. */
200 struct file_handle *
201 fh_from_id (const char *id)
202 {
203   struct file_handle *handle;
204
205   HMAP_FOR_EACH_WITH_HASH (handle, struct file_handle, name_node,
206                            utf8_hash_case_string (id, 0), &named_handles)
207     if (!utf8_strcasecmp (id, handle->id))
208       {
209         return fh_ref (handle);
210       }
211
212   return NULL;
213 }
214
215 /* Creates a new handle with identifier ID (which may be null)
216    and name HANDLE_NAME that refers to REFERENT.  Links the new
217    handle into the global list.  Returns the new handle.
218
219    The new handle is not fully initialized.  The caller is
220    responsible for completing its initialization. */
221 static struct file_handle *
222 create_handle (const char *id, char *handle_name, enum fh_referent referent,
223                const char *encoding)
224 {
225   struct file_handle *handle = XZALLOC (struct file_handle);
226
227   handle->ref_cnt = 1;
228   handle->id = xstrdup_if_nonnull (id);
229   handle->name = handle_name;
230   handle->referent = referent;
231   handle->encoding = xstrdup (encoding);
232
233   if (id != NULL)
234     {
235       hmap_insert (&named_handles, &handle->name_node,
236                    utf8_hash_case_string (handle->id, 0));
237     }
238
239   return handle;
240 }
241
242 /* Returns the unique handle of referent type FH_REF_INLINE,
243    which refers to the "inline file" that represents character
244    data in the command file between BEGIN DATA and END DATA. */
245 struct file_handle *
246 fh_inline_file (void)
247 {
248   return inline_file;
249 }
250
251 /* Creates and returns a new file handle with the given ID, which may be null.
252    If it is non-null, it must be a UTF-8 encoded string that is unique among
253    existing file identifiers.  The new handle is associated with file FILE_NAME
254    and the given PROPERTIES. */
255 struct file_handle *
256 fh_create_file (const char *id, const char *file_name, const char *file_name_encoding,
257                 const struct fh_properties *properties)
258 {
259   char *handle_name;
260   struct file_handle *handle;
261
262   handle_name = id != NULL ? xstrdup (id) : xasprintf ("`%s'", file_name);
263   handle = create_handle (id, handle_name, FH_REF_FILE, properties->encoding);
264   handle->file_name = xstrdup (file_name);
265   handle->file_name_encoding = xstrdup_if_nonnull (file_name_encoding);
266   handle->mode = properties->mode;
267   handle->line_ends = properties->line_ends;
268   handle->record_width = properties->record_width;
269   handle->tab_width = properties->tab_width;
270   return handle;
271 }
272
273 /* Creates a new file handle with the given ID, which must be
274    unique among existing file identifiers.  The new handle is
275    associated with a dataset file (initially empty). */
276 struct file_handle *
277 fh_create_dataset (struct dataset *ds)
278 {
279   const char *name;
280   struct file_handle *handle;
281
282   name = dataset_name (ds);
283   if (name[0] == '\0')
284     name = _("active dataset");
285
286   handle = create_handle (NULL, xstrdup (name), FH_REF_DATASET, C_ENCODING);
287   handle->ds = ds;
288   return handle;
289 }
290
291 /* Returns a set of default properties for a file handle. */
292 const struct fh_properties *
293 fh_default_properties (void)
294 {
295 #if defined _WIN32 || defined __WIN32__
296 #define DEFAULT_LINE_ENDS FH_END_CRLF
297 #else
298 #define DEFAULT_LINE_ENDS FH_END_LF
299 #endif
300
301   static const struct fh_properties default_properties
302     = {FH_MODE_TEXT, DEFAULT_LINE_ENDS, 1024, 4, (char *) "Auto"};
303   return &default_properties;
304 }
305
306 /* Returns the identifier that may be used in syntax to name the
307    given HANDLE, which takes the form of a PSPP identifier.  If
308    HANDLE has no identifier, returns a null pointer.
309
310    Return value is owned by the file handle.*/
311 const char *
312 fh_get_id (const struct file_handle *handle)
313 {
314   return handle->id;
315 }
316
317 /* Returns a user-friendly string to identify the given HANDLE.
318    If HANDLE was created by referring to a file name, returns the
319    file name, enclosed in double quotes.  Return value is owned
320    by the file handle.
321
322    Useful for printing error messages about use of file handles.  */
323 const char *
324 fh_get_name (const struct file_handle *handle)
325 {
326   return handle->name;
327 }
328
329 /* Returns the type of object that HANDLE refers to. */
330 enum fh_referent
331 fh_get_referent (const struct file_handle *handle)
332 {
333   return handle->referent;
334 }
335
336 /* Returns the name of the file associated with HANDLE. */
337 const char *
338 fh_get_file_name (const struct file_handle *handle)
339 {
340   assert (handle->referent == FH_REF_FILE);
341   return handle->file_name;
342 }
343
344
345 /* Returns the character encoding of the name of the file associated with HANDLE. */
346 const char *
347 fh_get_file_name_encoding (const struct file_handle *handle)
348 {
349   assert (handle->referent == FH_REF_FILE);
350   return handle->file_name_encoding;
351 }
352
353
354 /* Returns the mode of HANDLE. */
355 enum fh_mode
356 fh_get_mode (const struct file_handle *handle)
357 {
358   assert (handle->referent == FH_REF_FILE);
359   return handle->mode;
360 }
361
362 /* Returns the line ends of HANDLE, which must be a handle associated with a
363    file. */
364 enum fh_line_ends
365 fh_get_line_ends (const struct file_handle *handle)
366 {
367   assert (handle->referent == FH_REF_FILE);
368   return handle->line_ends;
369 }
370
371 /* Returns the width of a logical record on HANDLE. */
372 size_t
373 fh_get_record_width (const struct file_handle *handle)
374 {
375   assert (handle->referent & (FH_REF_FILE | FH_REF_INLINE));
376   return handle->record_width;
377 }
378
379 /* Returns the number of characters per tab stop for HANDLE, or
380    zero if tabs are not to be expanded.  Applicable only to
381    FH_MODE_TEXT files. */
382 size_t
383 fh_get_tab_width (const struct file_handle *handle)
384 {
385   assert (handle->referent & (FH_REF_FILE | FH_REF_INLINE));
386   return handle->tab_width;
387 }
388
389 /* Returns the encoding of characters read from HANDLE. */
390 const char *
391 fh_get_encoding (const struct file_handle *handle)
392 {
393   return handle->encoding;
394 }
395
396 /* Returns true if A and B refer to the same file or dataset, false
397    otherwise. */
398 bool
399 fh_equal (const struct file_handle *a, const struct file_handle *b)
400 {
401   if (a->referent != b->referent)
402     return false;
403
404   switch (a->referent)
405     {
406     case FH_REF_FILE:
407       {
408         struct file_identity *a_id = fh_get_identity (a);
409         struct file_identity *b_id = fh_get_identity (b);
410
411         int cmp = fh_compare_file_identities (a_id, b_id);
412
413         fh_free_identity (a_id);
414         fh_free_identity (b_id);
415
416         return cmp == 0;
417       }
418
419     case FH_REF_INLINE:
420       return true;
421
422     case FH_REF_DATASET:
423       return a->ds == b->ds;
424
425     default:
426       NOT_REACHED ();
427     }
428 }
429
430 /* Returns the dataset handle associated with HANDLE.
431    Applicable to only FH_REF_DATASET files. */
432 struct dataset *
433 fh_get_dataset (const struct file_handle *handle)
434 {
435   assert (handle->referent == FH_REF_DATASET);
436   return handle->ds;
437 }
438
439 /* Returns the current default handle. */
440 struct file_handle *
441 fh_get_default_handle (void)
442 {
443   return default_handle ? default_handle : fh_inline_file ();
444 }
445
446 /* Sets NEW_DEFAULT_HANDLE as the default handle. */
447 void
448 fh_set_default_handle (struct file_handle *new_default_handle)
449 {
450   assert (new_default_handle == NULL
451           || (new_default_handle->referent & (FH_REF_INLINE | FH_REF_FILE)));
452   if (default_handle != NULL && default_handle != inline_file)
453     fh_unref (default_handle);
454   default_handle = new_default_handle;
455   if (default_handle != NULL)
456     default_handle = fh_ref (default_handle);
457 }
458 \f
459 /* Information about a file handle's readers or writers. */
460 struct fh_lock
461   {
462     struct hmap_node node;      /* hmap_node member. */
463
464     /* Hash key. */
465     enum fh_referent referent;  /* Type of underlying file. */
466     union
467       {
468         struct file_identity *file; /* FH_REF_FILE only. */
469         unsigned int unique_id;    /* FH_REF_DATASET only. */
470       }
471     u;
472     enum fh_access access;      /* Type of file access. */
473
474     /* Number of openers. */
475     size_t open_cnt;
476
477     /* Applicable only when open_cnt > 0. */
478     bool exclusive;             /* No other openers allowed? */
479     const char *type;           /* Human-readable type of file. */
480     void *aux;                  /* Owner's auxiliary data. */
481   };
482
483
484 static void make_key (struct fh_lock *, const struct file_handle *,
485                       enum fh_access);
486 static void free_key (struct fh_lock *);
487 static int compare_fh_locks (const struct fh_lock *a, const struct fh_lock *b);
488 static unsigned int hash_fh_lock (const struct fh_lock *lock);
489
490 /* Tries to lock handle H for the given kind of ACCESS and TYPE
491    of file.  Returns a pointer to a struct fh_lock if successful,
492    otherwise a null pointer.
493
494    H's referent type must be one of the bits in MASK.  The caller
495    must verify this ahead of time; we simply assert it here.
496
497    TYPE is the sort of file, e.g. "system file".  Only one type
498    of access is allowed on a given file at a time for reading,
499    and similarly for writing.  If successful, a reference to TYPE
500    is retained, so it should probably be a string literal.
501
502    TYPE should be marked with N_() in the caller: that is, the
503    caller should not translate it with gettext, but fh_lock will
504    do so.
505
506    ACCESS specifies whether the lock is for reading or writing.
507    EXCLUSIVE is true to require exclusive access, false to allow
508    sharing with other accessors.  Exclusive read access precludes
509    other readers, but not writers; exclusive write access
510    precludes other writers, but not readers.  A sharable read or
511    write lock precludes reader or writers, respectively, of a
512    different TYPE.
513
514    A lock may be associated with auxiliary data.  See
515    fh_lock_get_aux and fh_lock_set_aux for more details. */
516 struct fh_lock *
517 fh_lock (struct file_handle *h, enum fh_referent mask UNUSED,
518          const char *type, enum fh_access access, bool exclusive)
519 {
520   struct fh_lock *key = NULL;
521   size_t hash ;
522   struct fh_lock *lock = NULL;
523   bool found_lock = false;
524
525   assert ((fh_get_referent (h) & mask) != 0);
526   assert (access == FH_ACC_READ || access == FH_ACC_WRITE);
527
528   key = xmalloc (sizeof *key);
529
530   make_key (key, h, access);
531
532   key->open_cnt = 1;
533   key->exclusive = exclusive;
534   key->type = type;
535   key->aux = NULL;
536
537   hash = hash_fh_lock (key);
538
539   HMAP_FOR_EACH_WITH_HASH (lock, struct fh_lock, node, hash, &locks)
540     {
541       if (0 == compare_fh_locks (lock, key))
542         {
543           found_lock = true;
544           break;
545         }
546     }
547
548   if (found_lock)
549     {
550       if (strcmp (lock->type, type))
551         {
552           if (access == FH_ACC_READ)
553             msg (SE, _("Can't read from %s as a %s because it is "
554                        "already being read as a %s."),
555                  fh_get_name (h), gettext (type), gettext (lock->type));
556           else
557             msg (SE, _("Can't write to %s as a %s because it is "
558                        "already being written as a %s."),
559                  fh_get_name (h), gettext (type), gettext (lock->type));
560           return NULL;
561         }
562       else if (exclusive || lock->exclusive)
563         {
564           msg (SE, _("Can't re-open %s as a %s."),
565                fh_get_name (h), gettext (type));
566           return NULL;
567         }
568       lock->open_cnt++;
569
570       free_key (key);
571       free (key);
572
573       return lock;
574     }
575
576   hmap_insert (&locks, &key->node, hash);
577   found_lock = false;
578   HMAP_FOR_EACH_WITH_HASH (lock, struct fh_lock, node, hash, &locks)
579     {
580       if (0 == compare_fh_locks (lock, key))
581         {
582           found_lock = true;
583           break;
584         }
585     }
586
587   assert (found_lock);
588
589   return key;
590 }
591
592 /* Releases LOCK that was acquired with fh_lock.
593    Returns true if LOCK is still locked, because other clients
594    also had it locked.
595
596    Returns false if LOCK has now been destroyed.  In this case
597    the caller must ensure that any auxiliary data associated with
598    LOCK is destroyed, to avoid a memory leak.  The caller must
599    obtain a pointer to the auxiliary data, e.g. via
600    fh_lock_get_aux *before* calling fh_unlock (because it yields
601    undefined behavior to call fh_lock_get_aux on a destroyed
602    lock).  */
603 bool
604 fh_unlock (struct fh_lock *lock)
605 {
606   if (lock != NULL)
607     {
608       assert (lock->open_cnt > 0);
609       if (--lock->open_cnt == 0)
610         {
611           hmap_delete (&locks, &lock->node);
612           free_key (lock);
613           free (lock);
614           return false;
615         }
616     }
617   return true;
618 }
619
620 /* Returns auxiliary data for LOCK.
621
622    Auxiliary data is shared by every client that holds LOCK (for
623    an exclusive lock, this is a single client).  To avoid leaks,
624    auxiliary data must be released before LOCK is destroyed. */
625 void *
626 fh_lock_get_aux (const struct fh_lock *lock)
627 {
628   return lock->aux;
629 }
630
631 /* Sets the auxiliary data for LOCK to AUX. */
632 void
633 fh_lock_set_aux (struct fh_lock *lock, void *aux)
634 {
635   lock->aux = aux;
636 }
637
638 /* Returns true if HANDLE is locked for the given type of ACCESS,
639    false otherwise. */
640 bool
641 fh_is_locked (const struct file_handle *handle, enum fh_access access)
642 {
643   struct fh_lock key;
644   const struct fh_lock *k = NULL;
645   bool is_locked = false;
646   size_t hash ;
647
648   make_key (&key, handle, access);
649
650   hash = hash_fh_lock (&key);
651
652
653   HMAP_FOR_EACH_WITH_HASH (k, struct fh_lock, node, hash, &locks)
654     {
655       if (0 == compare_fh_locks (k, &key))
656         {
657           is_locked = true;
658           break;
659         }
660     }
661
662   free_key (&key);
663
664   return is_locked;
665 }
666
667 /* Initializes the key fields in LOCK for looking up or inserting
668    handle H for the given kind of ACCESS. */
669 static void
670 make_key (struct fh_lock *lock, const struct file_handle *h,
671           enum fh_access access)
672 {
673   lock->referent = fh_get_referent (h);
674   lock->access = access;
675   if (lock->referent == FH_REF_FILE)
676     lock->u.file = fh_get_identity (h);
677   else if (lock->referent == FH_REF_DATASET)
678     lock->u.unique_id = dataset_seqno (fh_get_dataset (h));
679 }
680
681 /* Frees the key fields in LOCK. */
682 static void
683 free_key (struct fh_lock *lock)
684 {
685   if (lock->referent == FH_REF_FILE)
686     fh_free_identity (lock->u.file);
687 }
688
689 /* Compares the key fields in struct fh_lock objects A and B and
690    returns a strcmp()-type result. */
691 static int
692 compare_fh_locks (const struct fh_lock *a, const struct fh_lock *b)
693 {
694   if (a->referent != b->referent)
695     return a->referent < b->referent ? -1 : 1;
696   else if (a->access != b->access)
697     return a->access < b->access ? -1 : 1;
698   else if (a->referent == FH_REF_FILE)
699     return fh_compare_file_identities (a->u.file, b->u.file);
700   else if (a->referent == FH_REF_DATASET)
701     return (a->u.unique_id < b->u.unique_id ? -1
702             : a->u.unique_id > b->u.unique_id);
703   else
704     return 0;
705 }
706
707 /* Returns a hash value for LOCK. */
708 static unsigned int
709 hash_fh_lock (const struct fh_lock *lock)
710 {
711   unsigned int basis;
712   if (lock->referent == FH_REF_FILE)
713     basis = fh_hash_identity (lock->u.file);
714   else if (lock->referent == FH_REF_DATASET)
715     basis = lock->u.unique_id;
716   else
717     basis = 0;
718   return hash_int ((lock->referent << 3) | lock->access, basis);
719 }
720
721 \f
722
723
724
725
726 /* A file's identity:
727
728    - For a file that exists, this is its device and inode.
729
730    - For a file that does not exist, but which has a directory
731      name that exists, this is the device and inode of the
732      directory, plus the file's base name.
733
734    - For a file that does not exist and has a nonexistent
735      directory, this is the file name.
736
737    Windows doesn't have inode numbers, so we just use the name
738    there. */
739 struct file_identity
740 {
741   unsigned long long device;               /* Device number. */
742   unsigned long long inode;                /* Inode number. */
743   char *name;                 /* File name, where needed, otherwise NULL. */
744 };
745
746 /* Returns a pointer to a dynamically allocated structure whose
747    value can be used to tell whether two files are actually the
748    same file.  The caller is responsible for freeing the structure with
749    fh_free_identity() when finished. */
750 static struct file_identity *
751 fh_get_identity (const struct file_handle *fh)
752 {
753   struct file_identity *identity = xmalloc (sizeof *identity);
754
755   const char *file_name = fh_get_file_name (fh);
756
757 #if !(defined _WIN32 || defined __WIN32__)
758   struct stat s;
759   if (lstat (file_name, &s) == 0)
760     {
761       identity->device = s.st_dev;
762       identity->inode = s.st_ino;
763       identity->name = NULL;
764     }
765   else
766     {
767       char *dir = dir_name (file_name);
768       if (last_component (file_name) != NULL && stat (dir, &s) == 0)
769         {
770           identity->device = s.st_dev;
771           identity->inode = s.st_ino;
772           identity->name = base_name (file_name);
773         }
774       else
775         {
776           identity->device = 0;
777           identity->inode = 0;
778           identity->name = xstrdup (file_name);
779         }
780       free (dir);
781     }
782 #else /* Windows */
783   bool ok = false;
784   HANDLE h = CreateFile (file_name, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);
785   if (h != INVALID_HANDLE_VALUE)
786   {
787     BY_HANDLE_FILE_INFORMATION fi;
788     ok = GetFileInformationByHandle (h, &fi);
789     if (ok)
790       {
791         identity->device = fi.dwVolumeSerialNumber;
792         identity->inode = fi.nFileIndexHigh;
793         identity->inode <<= (sizeof fi.nFileIndexLow) * CHAR_BIT;
794         identity->inode |= fi.nFileIndexLow;
795         identity->name = 0;
796       }
797     CloseHandle (h);
798   }
799
800   if (!ok)
801     {
802       identity->device = 0;
803       identity->inode = 0;
804
805       size_t bufsize;
806       size_t pathlen = 255;
807       char *cname = NULL;
808       do
809       {
810         bufsize = pathlen;
811         cname = xrealloc (cname, bufsize);
812         pathlen = GetFullPathName (file_name, bufsize, cname, NULL);
813       }
814       while (pathlen > bufsize);
815       identity->name = xstrdup (cname);
816       free (cname);
817       str_lowercase (identity->name);
818     }
819 #endif /* Windows */
820
821   return identity;
822 }
823
824 /* Frees IDENTITY obtained from fh_get_identity(). */
825 void
826 fh_free_identity (struct file_identity *identity)
827 {
828   if (identity != NULL)
829     {
830       free (identity->name);
831       free (identity);
832     }
833 }
834
835 /* Compares A and B, returning a strcmp()-type result. */
836 int
837 fh_compare_file_identities (const struct file_identity *a,
838                             const struct file_identity *b)
839 {
840   if (a->device != b->device)
841     return a->device < b->device ? -1 : 1;
842   else if (a->inode != b->inode)
843     return a->inode < b->inode ? -1 : 1;
844   else if (a->name != NULL)
845     return b->name != NULL ? strcmp (a->name, b->name) : 1;
846   else
847     return b->name != NULL ? -1 : 0;
848 }
849
850 /* Returns a hash value for IDENTITY. */
851 unsigned int
852 fh_hash_identity (const struct file_identity *identity)
853 {
854   unsigned int hash = hash_int (identity->device, identity->inode);
855   if (identity->name != NULL)
856     hash = hash_string (identity->name, hash);
857   return hash;
858 }