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