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