source: trunk/src/kmk/dir.c@ 1902

Last change on this file since 1902 was 1902, checked in by bird, 17 years ago

kmk: Moved the strcache hash optimizations into the hash code.

  • Property svn:eol-style set to native
File size: 35.8 KB
Line 
1/* Directory hashing for GNU Make.
2Copyright (C) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997,
31998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software
4Foundation, Inc.
5This file is part of GNU Make.
6
7GNU Make is free software; you can redistribute it and/or modify it under the
8terms of the GNU General Public License as published by the Free Software
9Foundation; either version 2, or (at your option) any later version.
10
11GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
12WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
13A PARTICULAR PURPOSE. See the GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License along with
16GNU Make; see the file COPYING. If not, write to the Free Software
17Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. */
18
19#include "make.h"
20#include "hash.h"
21
22#ifdef HAVE_DIRENT_H
23# include <dirent.h>
24# define NAMLEN(dirent) strlen((dirent)->d_name)
25# ifdef VMS
26char *vmsify (char *name, int type);
27# endif
28#else
29# define dirent direct
30# define NAMLEN(dirent) (dirent)->d_namlen
31# ifdef HAVE_SYS_NDIR_H
32# include <sys/ndir.h>
33# endif
34# ifdef HAVE_SYS_DIR_H
35# include <sys/dir.h>
36# endif
37# ifdef HAVE_NDIR_H
38# include <ndir.h>
39# endif
40# ifdef HAVE_VMSDIR_H
41# include "vmsdir.h"
42# endif /* HAVE_VMSDIR_H */
43#endif
44/* bird: FreeBSD + smbfs -> readdir() + EBADF */
45#ifdef __FreeBSD__
46# include <sys/mount.h>
47#endif
48/* bird: end */
49
50/* In GNU systems, <dirent.h> defines this macro for us. */
51#ifdef _D_NAMLEN
52# undef NAMLEN
53# define NAMLEN(d) _D_NAMLEN(d)
54#endif
55
56#if (defined (POSIX) || defined (VMS) || defined (WINDOWS32)) && !defined (__GNU_LIBRARY__)
57/* Posix does not require that the d_ino field be present, and some
58 systems do not provide it. */
59# define REAL_DIR_ENTRY(dp) 1
60# define FAKE_DIR_ENTRY(dp)
61#else
62# define REAL_DIR_ENTRY(dp) (dp->d_ino != 0)
63# define FAKE_DIR_ENTRY(dp) (dp->d_ino = 1)
64#endif /* POSIX */
65
66
67#ifdef __MSDOS__
68#include <ctype.h>
69#include <fcntl.h>
70
71/* If it's MSDOS that doesn't have _USE_LFN, disable LFN support. */
72#ifndef _USE_LFN
73#define _USE_LFN 0
74#endif
75
76static const char *
77dosify (const char *filename)
78{
79 static char dos_filename[14];
80 char *df;
81 int i;
82
83 if (filename == 0 || _USE_LFN)
84 return filename;
85
86 /* FIXME: what about filenames which violate
87 8+3 constraints, like "config.h.in", or ".emacs"? */
88 if (strpbrk (filename, "\"*+,;<=>?[\\]|") != 0)
89 return filename;
90
91 df = dos_filename;
92
93 /* First, transform the name part. */
94 for (i = 0; *filename != '\0' && i < 8 && *filename != '.'; ++i)
95 *df++ = tolower ((unsigned char)*filename++);
96
97 /* Now skip to the next dot. */
98 while (*filename != '\0' && *filename != '.')
99 ++filename;
100 if (*filename != '\0')
101 {
102 *df++ = *filename++;
103 for (i = 0; *filename != '\0' && i < 3 && *filename != '.'; ++i)
104 *df++ = tolower ((unsigned char)*filename++);
105 }
106
107 /* Look for more dots. */
108 while (*filename != '\0' && *filename != '.')
109 ++filename;
110 if (*filename == '.')
111 return filename;
112 *df = 0;
113 return dos_filename;
114}
115#endif /* __MSDOS__ */
116
117#ifdef WINDOWS32
118#include "pathstuff.h"
119#endif
120
121#ifdef _AMIGA
122#include <ctype.h>
123#endif
124
125#ifdef HAVE_CASE_INSENSITIVE_FS
126static const char *
127downcase (const char *filename)
128{
129 static PATH_VAR (new_filename);
130 char *df;
131 int i;
132
133 if (filename == 0)
134 return 0;
135
136 df = new_filename;
137
138 /* First, transform the name part. */
139 while (*filename != '\0')
140 {
141 *df++ = tolower ((unsigned char)*filename);
142 ++filename;
143 }
144
145 *df = 0;
146
147 return new_filename;
148}
149#endif /* HAVE_CASE_INSENSITIVE_FS */
150
151#ifdef VMS
152
153static int
154vms_hash (char *name)
155{
156 int h = 0;
157 int g;
158
159 while (*name)
160 {
161 unsigned char uc = *name;
162#ifdef HAVE_CASE_INSENSITIVE_FS
163 h = (h << 4) + (isupper (uc) ? tolower (uc) : uc);
164#else
165 h = (h << 4) + uc;
166#endif
167 name++;
168 g = h & 0xf0000000;
169 if (g)
170 {
171 h = h ^ (g >> 24);
172 h = h ^ g;
173 }
174 }
175 return h;
176}
177
178/* fake stat entry for a directory */
179static int
180vmsstat_dir (char *name, struct stat *st)
181{
182 char *s;
183 int h;
184 DIR *dir;
185
186 dir = opendir (name);
187 if (dir == 0)
188 return -1;
189 closedir (dir);
190 s = strchr (name, ':'); /* find device */
191 if (s)
192 {
193 *s++ = 0;
194 st->st_dev = (char *)vms_hash (name);
195 h = vms_hash (s);
196 *(s-1) = ':';
197 }
198 else
199 {
200 st->st_dev = 0;
201 s = name;
202 h = vms_hash (s);
203 }
204
205 st->st_ino[0] = h & 0xff;
206 st->st_ino[1] = h & 0xff00;
207 st->st_ino[2] = h >> 16;
208
209 return 0;
210}
211#endif /* VMS */
212
213
214/* Hash table of directories. */
215
216#ifndef DIRECTORY_BUCKETS
217#ifdef KMK
218# define DIRECTORY_BUCKETS 4096
219# else
220#define DIRECTORY_BUCKETS 199
221# endif
222#endif
223
224struct directory_contents
225 {
226 dev_t dev; /* Device and inode numbers of this dir. */
227#ifdef WINDOWS32
228 /* Inode means nothing on WINDOWS32. Even file key information is
229 * unreliable because it is random per file open and undefined for remote
230 * filesystems. The most unique attribute I can come up with is the fully
231 * qualified name of the directory. Beware though, this is also
232 * unreliable. I'm open to suggestion on a better way to emulate inode. */
233# ifndef CONFIG_WITH_STRCACHE2
234 char *path_key;
235# else
236 char const *path_key; /* strcache'ed */
237# endif
238 int ctime;
239 int mtime; /* controls check for stale directory cache */
240 int fs_flags; /* FS_FAT, FS_NTFS, ... */
241# define FS_FAT 0x1
242# define FS_NTFS 0x2
243# define FS_UNKNOWN 0x4
244#else
245# ifdef VMS
246 ino_t ino[3];
247# else
248 ino_t ino;
249# endif
250#endif /* WINDOWS32 */
251 struct hash_table dirfiles; /* Files in this directory. */
252 DIR *dirstream; /* Stream reading this directory. */
253 };
254
255static unsigned long
256directory_contents_hash_1 (const void *key_0)
257{
258 const struct directory_contents *key = key_0;
259 unsigned long hash;
260
261#ifdef WINDOWS32
262# ifndef CONFIG_WITH_STRCACHE2
263 hash = 0;
264 ISTRING_HASH_1 (key->path_key, hash);
265# else /* CONFIG_WITH_STRCACHE2 */
266 hash = strcache2_get_ptr_hash (&file_strcache, key->path_key);
267# endif /* CONFIG_WITH_STRCACHE2 */
268 hash ^= ((unsigned int) key->dev << 4) ^ (unsigned int) key->ctime;
269#else
270# ifdef VMS
271 hash = (((unsigned int) key->dev << 4)
272 ^ ((unsigned int) key->ino[0]
273 + (unsigned int) key->ino[1]
274 + (unsigned int) key->ino[2]));
275# else
276 hash = ((unsigned int) key->dev << 4) ^ (unsigned int) key->ino;
277# endif
278#endif /* WINDOWS32 */
279 return hash;
280}
281
282static unsigned long
283directory_contents_hash_2 (const void *key_0)
284{
285 const struct directory_contents *key = key_0;
286 unsigned long hash;
287
288#ifdef WINDOWS32
289# ifndef CONFIG_WITH_STRCACHE2
290 hash = 0;
291 ISTRING_HASH_2 (key->path_key, hash);
292# else /* CONFIG_WITH_STRCACHE2 */
293 hash = strcache2_get_hash1 (&file_strcache, key->path_key);
294# endif /* CONFIG_WITH_STRCACHE2 */
295 hash ^= ((unsigned int) key->dev << 4) ^ (unsigned int) ~key->ctime;
296#else
297# ifdef VMS
298 hash = (((unsigned int) key->dev << 4)
299 ^ ~((unsigned int) key->ino[0]
300 + (unsigned int) key->ino[1]
301 + (unsigned int) key->ino[2]));
302# else
303 hash = ((unsigned int) key->dev << 4) ^ (unsigned int) ~key->ino;
304# endif
305#endif /* WINDOWS32 */
306
307 return hash;
308}
309
310/* Sometimes it's OK to use subtraction to get this value:
311 result = X - Y;
312 But, if we're not sure of the type of X and Y they may be too large for an
313 int (on a 64-bit system for example). So, use ?: instead.
314 See Savannah bug #15534.
315
316 NOTE! This macro has side-effects!
317*/
318
319#define MAKECMP(_x,_y) ((_x)<(_y)?-1:((_x)==(_y)?0:1))
320
321static int
322directory_contents_hash_cmp (const void *xv, const void *yv)
323{
324 const struct directory_contents *x = xv;
325 const struct directory_contents *y = yv;
326 int result;
327
328#ifdef WINDOWS32
329# ifndef CONFIG_WITH_STRCACHE2
330 ISTRING_COMPARE (x->path_key, y->path_key, result);
331 if (result)
332 return result;
333# else /* CONFIG_WITH_STRCACHE2 */
334 if (x->path_key != y->path_key)
335 return -1;
336# endif /* CONFIG_WITH_STRCACHE2 */
337 result = MAKECMP(x->ctime, y->ctime);
338 if (result)
339 return result;
340#else
341# ifdef VMS
342 result = MAKECMP(x->ino[0], y->ino[0]);
343 if (result)
344 return result;
345 result = MAKECMP(x->ino[1], y->ino[1]);
346 if (result)
347 return result;
348 result = MAKECMP(x->ino[2], y->ino[2]);
349 if (result)
350 return result;
351# else
352 result = MAKECMP(x->ino, y->ino);
353 if (result)
354 return result;
355# endif
356#endif /* WINDOWS32 */
357
358 return MAKECMP(x->dev, y->dev);
359}
360
361/* Table of directory contents hashed by device and inode number. */
362static struct hash_table directory_contents;
363
364#ifdef CONFIG_WITH_ALLOC_CACHES
365/* Allocation cache for directory contents. */
366struct alloccache directory_contents_cache;
367#endif
368
369struct directory
370 {
371 const char *name; /* Name of the directory. */
372
373 /* The directory's contents. This data may be shared by several
374 entries in the hash table, which refer to the same directory
375 (identified uniquely by `dev' and `ino') under different names. */
376 struct directory_contents *contents;
377 };
378
379#ifndef CONFIG_WITH_STRCACHE2
380static unsigned long
381directory_hash_1 (const void *key)
382{
383 return_ISTRING_HASH_1 (((const struct directory *) key)->name);
384}
385
386static unsigned long
387directory_hash_2 (const void *key)
388{
389 return_ISTRING_HASH_2 (((const struct directory *) key)->name);
390}
391
392static int
393directory_hash_cmp (const void *x, const void *y)
394{
395 return_ISTRING_COMPARE (((const struct directory *) x)->name,
396 ((const struct directory *) y)->name);
397}
398#endif /* !CONFIG_WITH_STRCACHE2 */
399
400/* Table of directories hashed by name. */
401static struct hash_table directories;
402
403#ifdef CONFIG_WITH_ALLOC_CACHES
404/* Allocation cache for directories. */
405struct alloccache directories_cache;
406#endif
407
408/* Never have more than this many directories open at once. */
409
410#define MAX_OPEN_DIRECTORIES 10
411
412static unsigned int open_directories = 0;
413
414
415/* Hash table of files in each directory. */
416
417struct dirfile
418 {
419 const char *name; /* Name of the file. */
420 short length;
421 short impossible; /* This file is impossible. */
422 };
423
424#ifndef CONFIG_WITH_STRCACHE2
425static unsigned long
426dirfile_hash_1 (const void *key)
427{
428 return_ISTRING_HASH_1 (((struct dirfile const *) key)->name);
429}
430
431static unsigned long
432dirfile_hash_2 (const void *key)
433{
434 return_ISTRING_HASH_2 (((struct dirfile const *) key)->name);
435}
436
437static int
438dirfile_hash_cmp (const void *xv, const void *yv)
439{
440 const struct dirfile *x = xv;
441 const struct dirfile *y = yv;
442 int result = x->length - y->length;
443 if (result)
444 return result;
445 return_ISTRING_COMPARE (x->name, y->name);
446}
447#endif /* !CONFIG_WITH_STRCACHE2 */
448
449#ifndef DIRFILE_BUCKETS
450#define DIRFILE_BUCKETS 107
451#endif
452
453#ifdef CONFIG_WITH_ALLOC_CACHES
454/* Allocation cache for dirfiles. */
455struct alloccache dirfile_cache;
456#endif
457
458
459
460static int dir_contents_file_exists_p (struct directory_contents *dir,
461 const char *filename);
462static struct directory *find_directory (const char *name);
463
464/* Find the directory named NAME and return its `struct directory'. */
465
466static struct directory *
467find_directory (const char *name)
468{
469 const char *p;
470 struct directory *dir;
471 struct directory **dir_slot;
472 struct directory dir_key;
473 int r;
474#ifdef WINDOWS32
475 char* w32_path;
476 char fs_label[BUFSIZ];
477 char fs_type[BUFSIZ];
478 unsigned long fs_serno;
479 unsigned long fs_flags;
480 unsigned long fs_len;
481#endif
482#ifdef VMS
483 if ((*name == '.') && (*(name+1) == 0))
484 name = "[]";
485 else
486 name = vmsify (name,1);
487#endif
488
489#ifndef CONFIG_WITH_STRCACHE2
490 dir_key.name = name;
491 dir_slot = (struct directory **) hash_find_slot (&directories, &dir_key);
492#else
493 p = name + strlen (name);
494 dir_key.name = strcache_add_len (name, p - name);
495 dir_slot = (struct directory **) hash_find_slot_strcached (&directories, &dir_key);
496#endif
497 dir = *dir_slot;
498
499 if (HASH_VACANT (dir))
500 {
501 struct stat st;
502
503 /* The directory was not found. Create a new entry for it. */
504
505#ifndef CONFIG_WITH_STRCACHE2
506 p = name + strlen (name);
507#endif
508#ifndef CONFIG_WITH_ALLOC_CACHES
509 dir = xmalloc (sizeof (struct directory));
510#else
511 dir = alloccache_alloc (&directories_cache);
512#endif
513#ifndef CONFIG_WITH_STRCACHE2
514 dir->name = strcache_add_len (name, p - name);
515#else
516 dir->name = dir_key.name;
517#endif
518 hash_insert_at (&directories, dir, dir_slot);
519 /* The directory is not in the name hash table.
520 Find its device and inode numbers, and look it up by them. */
521
522#ifdef WINDOWS32
523 /* Remove any trailing '\'. Windows32 stat fails even on valid
524 directories if they end in '\'. */
525 if (p[-1] == '\\')
526 ((char *)p)[-1] = '\0';
527#endif
528
529#ifdef VMS
530 r = vmsstat_dir (name, &st);
531#else
532 EINTRLOOP (r, stat (name, &st));
533#endif
534
535#ifdef WINDOWS32
536 /* Put back the trailing '\'. If we don't, we're permanently
537 truncating the value! */
538 if (p[-1] == '\0')
539 ((char *)p)[-1] = '\\';
540#endif
541
542 if (r < 0)
543 {
544 /* Couldn't stat the directory. Mark this by
545 setting the `contents' member to a nil pointer. */
546 dir->contents = 0;
547 }
548 else
549 {
550 /* Search the contents hash table; device and inode are the key. */
551
552 struct directory_contents *dc;
553 struct directory_contents **dc_slot;
554 struct directory_contents dc_key;
555
556 dc_key.dev = st.st_dev;
557#ifdef WINDOWS32
558# ifndef CONFIG_WITH_STRCACHE2
559 dc_key.path_key = w32_path = w32ify (name, 1);
560# else /* CONFIG_WITH_STRCACHE2 */
561 w32_path = w32ify (name, 1);
562 dc_key.path_key = strcache_add (w32_path);
563# endif /* CONFIG_WITH_STRCACHE2 */
564 dc_key.ctime = st.st_ctime;
565#else
566# ifdef VMS
567 dc_key.ino[0] = st.st_ino[0];
568 dc_key.ino[1] = st.st_ino[1];
569 dc_key.ino[2] = st.st_ino[2];
570# else
571 dc_key.ino = st.st_ino;
572# endif
573#endif
574 dc_slot = (struct directory_contents **) hash_find_slot (&directory_contents, &dc_key);
575 dc = *dc_slot;
576
577 if (HASH_VACANT (dc))
578 {
579 /* Nope; this really is a directory we haven't seen before. */
580
581#ifndef CONFIG_WITH_ALLOC_CACHES
582 dc = (struct directory_contents *)
583 xmalloc (sizeof (struct directory_contents));
584#else
585 dc = (struct directory_contents *)
586 alloccache_alloc (&directory_contents_cache);
587#endif
588
589 /* Enter it in the contents hash table. */
590 dc->dev = st.st_dev;
591#ifdef WINDOWS32
592# ifndef CONFIG_WITH_STRCACHE2
593 dc->path_key = xstrdup (w32_path);
594# else /* CONFIG_WITH_STRCACHE2 */
595 dc->path_key = dc_key.path_key;
596# endif /* CONFIG_WITH_STRCACHE2 */
597
598 dc->ctime = st.st_ctime;
599 dc->mtime = st.st_mtime;
600
601 /*
602 * NTFS is the only WINDOWS32 filesystem that bumps mtime
603 * on a directory when files are added/deleted from
604 * a directory.
605 */
606 w32_path[3] = '\0';
607 if (GetVolumeInformation(w32_path,
608 fs_label, sizeof (fs_label),
609 &fs_serno, &fs_len,
610 &fs_flags, fs_type, sizeof (fs_type)) == FALSE)
611 dc->fs_flags = FS_UNKNOWN;
612 else if (!strcmp(fs_type, "FAT"))
613 dc->fs_flags = FS_FAT;
614 else if (!strcmp(fs_type, "NTFS"))
615 dc->fs_flags = FS_NTFS;
616 else
617 dc->fs_flags = FS_UNKNOWN;
618#else
619# ifdef VMS
620 dc->ino[0] = st.st_ino[0];
621 dc->ino[1] = st.st_ino[1];
622 dc->ino[2] = st.st_ino[2];
623# else
624 dc->ino = st.st_ino;
625# endif
626#endif /* WINDOWS32 */
627 hash_insert_at (&directory_contents, dc, dc_slot);
628 ENULLLOOP (dc->dirstream, opendir (name));
629 if (dc->dirstream == 0)
630 /* Couldn't open the directory. Mark this by setting the
631 `files' member to a nil pointer. */
632 dc->dirfiles.ht_vec = 0;
633 else
634 {
635#ifdef KMK
636 int buckets = st.st_nlink * 2;
637 if (buckets < DIRFILE_BUCKETS)
638 buckets = DIRFILE_BUCKETS;
639 hash_init_strcached (&dc->dirfiles, buckets, &file_strcache,
640 offsetof (struct dirfile, name));
641#else
642# ifndef CONFIG_WITH_STRCACHE2
643 hash_init (&dc->dirfiles, DIRFILE_BUCKETS,
644 dirfile_hash_1, dirfile_hash_2, dirfile_hash_cmp);
645# else /* CONFIG_WITH_STRCACHE2 */
646 hash_init_strcached (&dc->dirfiles, DIRFILE_BUCKETS,
647 &file_strcache,
648 offsetof (struct dirfile, name));
649# endif /* CONFIG_WITH_STRCACHE2 */
650#endif
651 /* Keep track of how many directories are open. */
652 ++open_directories;
653 if (open_directories == MAX_OPEN_DIRECTORIES)
654 /* We have too many directories open already.
655 Read the entire directory and then close it. */
656 dir_contents_file_exists_p (dc, 0);
657 }
658 }
659
660 /* Point the name-hashed entry for DIR at its contents data. */
661 dir->contents = dc;
662 }
663 }
664
665 return dir;
666}
667
668
669/* Return 1 if the name FILENAME is entered in DIR's hash table.
670 FILENAME must contain no slashes. */
671
672static int
673dir_contents_file_exists_p (struct directory_contents *dir,
674 const char *filename)
675{
676 unsigned int hash;
677 struct dirfile *df;
678 struct dirent *d;
679#ifdef WINDOWS32
680 struct stat st;
681 int rehash = 0;
682#endif
683
684 if (dir == 0 || dir->dirfiles.ht_vec == 0)
685 /* The directory could not be stat'd or opened. */
686 return 0;
687
688#ifdef __MSDOS__
689 filename = dosify (filename);
690#endif
691
692#ifdef HAVE_CASE_INSENSITIVE_FS
693 filename = downcase (filename);
694#endif
695
696#ifdef __EMX__
697 if (filename != 0)
698 _fnlwr (filename); /* lower case for FAT drives */
699#endif
700
701#ifdef VMS
702 filename = vmsify (filename,0);
703#endif
704
705 hash = 0;
706 if (filename != 0)
707 {
708 struct dirfile dirfile_key;
709
710 if (*filename == '\0')
711 {
712 /* Checking if the directory exists. */
713 return 1;
714 }
715#ifndef CONFIG_WITH_STRCACHE2
716 dirfile_key.name = filename;
717 dirfile_key.length = strlen (filename);
718 df = hash_find_item (&dir->dirfiles, &dirfile_key);
719#else /* CONFIG_WITH_STRCACHE2 */
720 dirfile_key.length = strlen (filename);
721 dirfile_key.name = filename
722 = strcache_add_len (filename, dirfile_key.length);
723 df = hash_find_item_strcached (&dir->dirfiles, &dirfile_key);
724#endif /* CONFIG_WITH_STRCACHE2 */
725 if (df)
726 return !df->impossible;
727 }
728
729 /* The file was not found in the hashed list.
730 Try to read the directory further. */
731
732 if (dir->dirstream == 0)
733 {
734#ifdef WINDOWS32
735 /*
736 * Check to see if directory has changed since last read. FAT
737 * filesystems force a rehash always as mtime does not change
738 * on directories (ugh!).
739 */
740 if (dir->path_key)
741 {
742 if ((dir->fs_flags & FS_FAT) != 0)
743 {
744 dir->mtime = time ((time_t *) 0);
745 rehash = 1;
746 }
747 else if (stat (dir->path_key, &st) == 0 && st.st_mtime > dir->mtime)
748 {
749 /* reset date stamp to show most recent re-process. */
750 dir->mtime = st.st_mtime;
751 rehash = 1;
752 }
753
754 /* If it has been already read in, all done. */
755 if (!rehash)
756 return 0;
757
758 /* make sure directory can still be opened; if not return. */
759 dir->dirstream = opendir (dir->path_key);
760 if (!dir->dirstream)
761 return 0;
762 }
763 else
764#endif
765 /* The directory has been all read in. */
766 return 0;
767 }
768
769 while (1)
770 {
771 /* Enter the file in the hash table. */
772 unsigned int len;
773 struct dirfile dirfile_key;
774 struct dirfile **dirfile_slot;
775
776 ENULLLOOP (d, readdir (dir->dirstream));
777 if (d == 0)
778 {
779/* bird: Workaround for smbfs mounts returning EBADF at the end of the search.
780 To exactly determin the cause here, I should probably do some smbfs
781 tracing, but for now just ignoring the EBADF on seems to work.
782 (The smb server is 64-bit vista, btw.) */
783#if defined (__FreeBSD__)
784 struct statfs stfs;
785 int saved_errno = errno;
786 errno = 0;
787 if (saved_errno == EBADF
788 && !fstatfs (dirfd (dir->dirstream), &stfs)
789 && !(stfs.f_flags & MNT_LOCAL)
790 && !strcmp(stfs.f_fstypename, "smbfs"))
791 {
792 /*fprintf (stderr, "EBADF on remote fs! dirfd=%d errno=%d\n",
793 dirfd (dir->dirstream), errno);*/
794 saved_errno = 0;
795 }
796 errno = saved_errno;
797#endif
798/* bird: end */
799 if (errno)
800 fatal (NILF, "INTERNAL: readdir(%p): %s (filename=%s)\n", (void *)dir, strerror (errno), filename);
801 break;
802 }
803
804#if defined(VMS) && defined(HAVE_DIRENT_H)
805 /* In VMS we get file versions too, which have to be stripped off */
806 {
807 char *p = strrchr (d->d_name, ';');
808 if (p)
809 *p = '\0';
810 }
811#endif
812 if (!REAL_DIR_ENTRY (d))
813 continue;
814
815 len = NAMLEN (d);
816#ifndef CONFIG_WITH_STRCACHE2
817 dirfile_key.name = d->d_name;
818 dirfile_key.length = len;
819 dirfile_slot = (struct dirfile **) hash_find_slot (&dir->dirfiles, &dirfile_key);
820#else
821 dirfile_key.name = strcache_add_len (d->d_name, len);
822 dirfile_key.length = len;
823 dirfile_slot = (struct dirfile **) hash_find_slot_strcached (&dir->dirfiles, &dirfile_key);
824#endif
825#ifdef WINDOWS32
826 /*
827 * If re-reading a directory, don't cache files that have
828 * already been discovered.
829 */
830 if (! rehash || HASH_VACANT (*dirfile_slot))
831#endif
832 {
833#ifndef CONFIG_WITH_ALLOC_CACHES
834 df = xmalloc (sizeof (struct dirfile));
835#else
836 df = alloccache_alloc (&dirfile_cache);
837#endif
838#ifndef CONFIG_WITH_STRCACHE2
839 df->name = strcache_add_len (d->d_name, len);
840#else
841 df->name = dirfile_key.name;
842#endif
843 df->length = len;
844 df->impossible = 0;
845 hash_insert_at (&dir->dirfiles, df, dirfile_slot);
846 }
847 /* Check if the name matches the one we're searching for. */
848#ifndef CONFIG_WITH_STRCACHE2
849 if (filename != 0 && strieq (d->d_name, filename))
850#else
851 if (filename != 0 && dirfile_key.name == filename)
852#endif
853 return 1;
854 }
855
856 /* If the directory has been completely read in,
857 close the stream and reset the pointer to nil. */
858 if (d == 0)
859 {
860 --open_directories;
861 closedir (dir->dirstream);
862 dir->dirstream = 0;
863 }
864 return 0;
865}
866
867/* Return 1 if the name FILENAME in directory DIRNAME
868 is entered in the dir hash table.
869 FILENAME must contain no slashes. */
870
871int
872dir_file_exists_p (const char *dirname, const char *filename)
873{
874 return dir_contents_file_exists_p (find_directory (dirname)->contents,
875 filename);
876}
877
878
879/* Return 1 if the file named NAME exists. */
880
881int
882file_exists_p (const char *name)
883{
884 const char *dirend;
885 const char *dirname;
886 const char *slash;
887
888#ifndef NO_ARCHIVES
889 if (ar_name (name))
890 return ar_member_date (name) != (time_t) -1;
891#endif
892
893#ifdef VMS
894 dirend = strrchr (name, ']');
895 if (dirend == 0)
896 dirend = strrchr (name, ':');
897 if (dirend == 0)
898 return dir_file_exists_p ("[]", name);
899#else /* !VMS */
900 dirend = strrchr (name, '/');
901#ifdef HAVE_DOS_PATHS
902 /* Forward and backslashes might be mixed. We need the rightmost one. */
903 {
904 const char *bslash = strrchr(name, '\\');
905 if (!dirend || bslash > dirend)
906 dirend = bslash;
907 /* The case of "d:file". */
908 if (!dirend && name[0] && name[1] == ':')
909 dirend = name + 1;
910 }
911#endif /* HAVE_DOS_PATHS */
912 if (dirend == 0)
913#ifndef _AMIGA
914 return dir_file_exists_p (".", name);
915#else /* !VMS && !AMIGA */
916 return dir_file_exists_p ("", name);
917#endif /* AMIGA */
918#endif /* VMS */
919
920 slash = dirend;
921 if (dirend == name)
922 dirname = "/";
923 else
924 {
925 char *p;
926#ifdef HAVE_DOS_PATHS
927 /* d:/ and d: are *very* different... */
928 if (dirend < name + 3 && name[1] == ':' &&
929 (*dirend == '/' || *dirend == '\\' || *dirend == ':'))
930 dirend++;
931#endif
932 p = alloca (dirend - name + 1);
933 memcpy (p, name, dirend - name);
934 p[dirend - name] = '\0';
935 dirname = p;
936 }
937 return dir_file_exists_p (dirname, slash + 1);
938}
939
940
941/* Mark FILENAME as `impossible' for `file_impossible_p'.
942 This means an attempt has been made to search for FILENAME
943 as an intermediate file, and it has failed. */
944
945void
946file_impossible (const char *filename)
947{
948 const char *dirend;
949 const char *p = filename;
950 struct directory *dir;
951 struct dirfile *new;
952
953#ifdef VMS
954 dirend = strrchr (p, ']');
955 if (dirend == 0)
956 dirend = strrchr (p, ':');
957 dirend++;
958 if (dirend == (char *)1)
959 dir = find_directory ("[]");
960#else
961 dirend = strrchr (p, '/');
962# ifdef HAVE_DOS_PATHS
963 /* Forward and backslashes might be mixed. We need the rightmost one. */
964 {
965 const char *bslash = strrchr(p, '\\');
966 if (!dirend || bslash > dirend)
967 dirend = bslash;
968 /* The case of "d:file". */
969 if (!dirend && p[0] && p[1] == ':')
970 dirend = p + 1;
971 }
972# endif /* HAVE_DOS_PATHS */
973 if (dirend == 0)
974# ifdef _AMIGA
975 dir = find_directory ("");
976# else /* !VMS && !AMIGA */
977 dir = find_directory (".");
978# endif /* AMIGA */
979#endif /* VMS */
980 else
981 {
982 const char *dirname;
983 const char *slash = dirend;
984 if (dirend == p)
985 dirname = "/";
986 else
987 {
988 char *cp;
989#ifdef HAVE_DOS_PATHS
990 /* d:/ and d: are *very* different... */
991 if (dirend < p + 3 && p[1] == ':' &&
992 (*dirend == '/' || *dirend == '\\' || *dirend == ':'))
993 dirend++;
994#endif
995 cp = alloca (dirend - p + 1);
996 memcpy (cp, p, dirend - p);
997 cp[dirend - p] = '\0';
998 dirname = cp;
999 }
1000 dir = find_directory (dirname);
1001 filename = p = slash + 1;
1002 }
1003
1004 if (dir->contents == 0)
1005 {
1006 /* The directory could not be stat'd. We allocate a contents
1007 structure for it, but leave it out of the contents hash table. */
1008#ifndef CONFIG_WITH_ALLOC_CACHES
1009 dir->contents = xmalloc (sizeof (struct directory_contents));
1010#else
1011 dir->contents = alloccache_alloc (&directory_contents_cache);
1012#endif
1013 memset (dir->contents, '\0', sizeof (struct directory_contents));
1014
1015 }
1016
1017 if (dir->contents->dirfiles.ht_vec == 0)
1018 {
1019#ifndef CONFIG_WITH_STRCACHE2
1020 hash_init (&dir->contents->dirfiles, DIRFILE_BUCKETS,
1021 dirfile_hash_1, dirfile_hash_2, dirfile_hash_cmp);
1022#else /* CONFIG_WITH_STRCACHE2 */
1023 hash_init_strcached (&dir->contents->dirfiles, DIRFILE_BUCKETS,
1024 &file_strcache, offsetof (struct dirfile, name));
1025#endif /* CONFIG_WITH_STRCACHE2 */
1026 }
1027
1028 /* Make a new entry and put it in the table. */
1029
1030#ifndef CONFIG_WITH_ALLOC_CACHES
1031 new = xmalloc (sizeof (struct dirfile));
1032#else
1033 new = alloccache_alloc (&dirfile_cache);
1034#endif
1035 new->length = strlen (filename);
1036 new->name = strcache_add_len (filename, new->length);
1037 new->impossible = 1;
1038 hash_insert (&dir->contents->dirfiles, new);
1039}
1040
1041
1042/* Return nonzero if FILENAME has been marked impossible. */
1043
1044int
1045file_impossible_p (const char *filename)
1046{
1047 const char *dirend;
1048 const char *p = filename;
1049 struct directory_contents *dir;
1050 struct dirfile *dirfile;
1051 struct dirfile dirfile_key;
1052
1053#ifdef VMS
1054 dirend = strrchr (filename, ']');
1055 if (dirend == 0)
1056 dir = find_directory ("[]")->contents;
1057#else
1058 dirend = strrchr (filename, '/');
1059#ifdef HAVE_DOS_PATHS
1060 /* Forward and backslashes might be mixed. We need the rightmost one. */
1061 {
1062 const char *bslash = strrchr(filename, '\\');
1063 if (!dirend || bslash > dirend)
1064 dirend = bslash;
1065 /* The case of "d:file". */
1066 if (!dirend && filename[0] && filename[1] == ':')
1067 dirend = filename + 1;
1068 }
1069#endif /* HAVE_DOS_PATHS */
1070 if (dirend == 0)
1071#ifdef _AMIGA
1072 dir = find_directory ("")->contents;
1073#else /* !VMS && !AMIGA */
1074 dir = find_directory (".")->contents;
1075#endif /* AMIGA */
1076#endif /* VMS */
1077 else
1078 {
1079 const char *dirname;
1080 const char *slash = dirend;
1081 if (dirend == filename)
1082 dirname = "/";
1083 else
1084 {
1085 char *cp;
1086#ifdef HAVE_DOS_PATHS
1087 /* d:/ and d: are *very* different... */
1088 if (dirend < filename + 3 && filename[1] == ':' &&
1089 (*dirend == '/' || *dirend == '\\' || *dirend == ':'))
1090 dirend++;
1091#endif
1092 cp = alloca (dirend - filename + 1);
1093 memcpy (cp, p, dirend - p);
1094 cp[dirend - p] = '\0';
1095 dirname = cp;
1096 }
1097 dir = find_directory (dirname)->contents;
1098 p = filename = slash + 1;
1099 }
1100
1101 if (dir == 0 || dir->dirfiles.ht_vec == 0)
1102 /* There are no files entered for this directory. */
1103 return 0;
1104
1105#ifdef __MSDOS__
1106 filename = dosify (p);
1107#endif
1108#ifdef HAVE_CASE_INSENSITIVE_FS
1109 filename = downcase (p);
1110#endif
1111#ifdef VMS
1112 filename = vmsify (p, 1);
1113#endif
1114
1115#ifndef CONFIG_WITH_STRCACHE2
1116 dirfile_key.name = filename;
1117 dirfile_key.length = strlen (filename);
1118 dirfile = hash_find_item (&dir->dirfiles, &dirfile_key);
1119#else
1120 dirfile_key.length = strlen (filename);
1121 dirfile_key.name = strcache_add_len (filename, dirfile_key.length);
1122 dirfile = hash_find_item_strcached (&dir->dirfiles, &dirfile_key);
1123#endif
1124 if (dirfile)
1125 return dirfile->impossible;
1126
1127 return 0;
1128}
1129
1130
1131/* Return the already allocated name in the
1132 directory hash table that matches DIR. */
1133
1134const char *
1135dir_name (const char *dir)
1136{
1137 return find_directory (dir)->name;
1138}
1139
1140
1141/* Print the data base of directories. */
1142
1143void
1144print_dir_data_base (void)
1145{
1146 unsigned int files;
1147 unsigned int impossible;
1148 struct directory **dir_slot;
1149 struct directory **dir_end;
1150
1151 puts (_("\n# Directories\n"));
1152
1153 files = impossible = 0;
1154
1155 dir_slot = (struct directory **) directories.ht_vec;
1156 dir_end = dir_slot + directories.ht_size;
1157 for ( ; dir_slot < dir_end; dir_slot++)
1158 {
1159 struct directory *dir = *dir_slot;
1160 if (! HASH_VACANT (dir))
1161 {
1162 if (dir->contents == 0)
1163 printf (_("# %s: could not be stat'd.\n"), dir->name);
1164 else if (dir->contents->dirfiles.ht_vec == 0)
1165 {
1166#ifdef WINDOWS32
1167 printf (_("# %s (key %s, mtime %d): could not be opened.\n"),
1168 dir->name, dir->contents->path_key,dir->contents->mtime);
1169#else /* WINDOWS32 */
1170#ifdef VMS
1171 printf (_("# %s (device %d, inode [%d,%d,%d]): could not be opened.\n"),
1172 dir->name, dir->contents->dev,
1173 dir->contents->ino[0], dir->contents->ino[1],
1174 dir->contents->ino[2]);
1175#else
1176 printf (_("# %s (device %ld, inode %ld): could not be opened.\n"),
1177 dir->name, (long int) dir->contents->dev,
1178 (long int) dir->contents->ino);
1179#endif
1180#endif /* WINDOWS32 */
1181 }
1182 else
1183 {
1184 unsigned int f = 0;
1185 unsigned int im = 0;
1186 struct dirfile **files_slot;
1187 struct dirfile **files_end;
1188
1189 files_slot = (struct dirfile **) dir->contents->dirfiles.ht_vec;
1190 files_end = files_slot + dir->contents->dirfiles.ht_size;
1191 for ( ; files_slot < files_end; files_slot++)
1192 {
1193 struct dirfile *df = *files_slot;
1194 if (! HASH_VACANT (df))
1195 {
1196 if (df->impossible)
1197 ++im;
1198 else
1199 ++f;
1200 }
1201 }
1202#ifdef WINDOWS32
1203 printf (_("# %s (key %s, mtime %d): "),
1204 dir->name, dir->contents->path_key, dir->contents->mtime);
1205#else /* WINDOWS32 */
1206#ifdef VMS
1207 printf (_("# %s (device %d, inode [%d,%d,%d]): "),
1208 dir->name, dir->contents->dev,
1209 dir->contents->ino[0], dir->contents->ino[1],
1210 dir->contents->ino[2]);
1211#else
1212 printf (_("# %s (device %ld, inode %ld): "),
1213 dir->name,
1214 (long)dir->contents->dev, (long)dir->contents->ino);
1215#endif
1216#endif /* WINDOWS32 */
1217 if (f == 0)
1218 fputs (_("No"), stdout);
1219 else
1220 printf ("%u", f);
1221 fputs (_(" files, "), stdout);
1222 if (im == 0)
1223 fputs (_("no"), stdout);
1224 else
1225 printf ("%u", im);
1226 fputs (_(" impossibilities"), stdout);
1227 if (dir->contents->dirstream == 0)
1228 puts (".");
1229 else
1230 puts (_(" so far."));
1231 files += f;
1232 impossible += im;
1233
1234#ifdef KMK
1235 fputs ("# ", stdout);
1236 hash_print_stats (&dir->contents->dirfiles, stdout);
1237 fputs ("\n", stdout);
1238#endif
1239 }
1240 }
1241 }
1242
1243 fputs ("\n# ", stdout);
1244 if (files == 0)
1245 fputs (_("No"), stdout);
1246 else
1247 printf ("%u", files);
1248 fputs (_(" files, "), stdout);
1249 if (impossible == 0)
1250 fputs (_("no"), stdout);
1251 else
1252 printf ("%u", impossible);
1253 printf (_(" impossibilities in %lu directories.\n"), directories.ht_fill);
1254#ifdef KMK
1255 fputs ("# directories: ", stdout);
1256 hash_print_stats (&directories, stdout);
1257 fputs ("\n# directory_contents: ", stdout);
1258 hash_print_stats (&directory_contents, stdout);
1259 fputs ("\n", stdout);
1260#endif
1261}
1262
1263
1264/* Hooks for globbing. */
1265
1266#include <glob.h>
1267
1268/* Structure describing state of iterating through a directory hash table. */
1269
1270struct dirstream
1271 {
1272 struct directory_contents *contents; /* The directory being read. */
1273 struct dirfile **dirfile_slot; /* Current slot in table. */
1274 };
1275
1276/* Forward declarations. */
1277static __ptr_t open_dirstream (const char *);
1278static struct dirent *read_dirstream (__ptr_t);
1279
1280static __ptr_t
1281open_dirstream (const char *directory)
1282{
1283 struct dirstream *new;
1284 struct directory *dir = find_directory (directory);
1285
1286 if (dir->contents == 0 || dir->contents->dirfiles.ht_vec == 0)
1287 /* DIR->contents is nil if the directory could not be stat'd.
1288 DIR->contents->dirfiles is nil if it could not be opened. */
1289 return 0;
1290
1291 /* Read all the contents of the directory now. There is no benefit
1292 in being lazy, since glob will want to see every file anyway. */
1293
1294 dir_contents_file_exists_p (dir->contents, 0);
1295
1296 new = xmalloc (sizeof (struct dirstream));
1297 new->contents = dir->contents;
1298 new->dirfile_slot = (struct dirfile **) new->contents->dirfiles.ht_vec;
1299
1300 return (__ptr_t) new;
1301}
1302
1303static struct dirent *
1304read_dirstream (__ptr_t stream)
1305{
1306 static char *buf;
1307 static unsigned int bufsz;
1308
1309 struct dirstream *const ds = (struct dirstream *) stream;
1310 struct directory_contents *dc = ds->contents;
1311 struct dirfile **dirfile_end = (struct dirfile **) dc->dirfiles.ht_vec + dc->dirfiles.ht_size;
1312
1313 while (ds->dirfile_slot < dirfile_end)
1314 {
1315 struct dirfile *df = *ds->dirfile_slot++;
1316 if (! HASH_VACANT (df) && !df->impossible)
1317 {
1318 /* The glob interface wants a `struct dirent', so mock one up. */
1319 struct dirent *d;
1320 unsigned int len = df->length + 1;
1321 unsigned int sz = sizeof (*d) - sizeof (d->d_name) + len;
1322 if (sz > bufsz)
1323 {
1324 bufsz *= 2;
1325 if (sz > bufsz)
1326 bufsz = sz;
1327 buf = xrealloc (buf, bufsz);
1328 }
1329 d = (struct dirent *) buf;
1330#ifdef __MINGW32__
1331# if __MINGW32_MAJOR_VERSION < 3 || (__MINGW32_MAJOR_VERSION == 3 && \
1332 __MINGW32_MINOR_VERSION == 0)
1333 d->d_name = xmalloc(len);
1334# endif
1335#endif
1336 FAKE_DIR_ENTRY (d);
1337#ifdef _DIRENT_HAVE_D_NAMLEN
1338 d->d_namlen = len - 1;
1339#endif
1340#ifdef _DIRENT_HAVE_D_TYPE
1341 d->d_type = DT_UNKNOWN;
1342#endif
1343 memcpy (d->d_name, df->name, len);
1344 return d;
1345 }
1346 }
1347
1348 return 0;
1349}
1350
1351static void
1352ansi_free (void *p)
1353{
1354 if (p)
1355 free(p);
1356}
1357
1358/* On 64 bit ReliantUNIX (5.44 and above) in LFS mode, stat() is actually a
1359 * macro for stat64(). If stat is a macro, make a local wrapper function to
1360 * invoke it.
1361 */
1362#ifndef stat
1363# ifndef VMS
1364int stat (const char *path, struct stat *sbuf);
1365# endif
1366# define local_stat stat
1367#else
1368static int
1369local_stat (const char *path, struct stat *buf)
1370{
1371 int e;
1372
1373 EINTRLOOP (e, stat (path, buf));
1374 return e;
1375}
1376#endif
1377
1378void
1379dir_setup_glob (glob_t *gl)
1380{
1381 gl->gl_opendir = open_dirstream;
1382 gl->gl_readdir = read_dirstream;
1383 gl->gl_closedir = ansi_free;
1384 gl->gl_stat = local_stat;
1385#ifdef __EMX__ /* The FreeBSD implemenation actually uses gl_lstat!! */
1386 gl->gl_lstat = local_stat;
1387#endif
1388 /* We don't bother setting gl_lstat, since glob never calls it.
1389 The slot is only there for compatibility with 4.4 BSD. */
1390}
1391
1392void
1393hash_init_directories (void)
1394{
1395#ifndef CONFIG_WITH_STRCACHE2
1396 hash_init (&directories, DIRECTORY_BUCKETS,
1397 directory_hash_1, directory_hash_2, directory_hash_cmp);
1398#else /* */
1399 hash_init_strcached (&directories, DIRECTORY_BUCKETS, &file_strcache,
1400 offsetof (struct directory, name));
1401#endif /* */
1402 hash_init (&directory_contents, DIRECTORY_BUCKETS,
1403 directory_contents_hash_1, directory_contents_hash_2,
1404 directory_contents_hash_cmp);
1405#ifdef CONFIG_WITH_ALLOC_CACHES
1406 alloccache_init (&directories_cache, sizeof (struct directory),
1407 "directories", NULL, NULL);
1408 alloccache_init (&directory_contents_cache, sizeof (struct directory_contents),
1409 "directory_contents", NULL, NULL);
1410 alloccache_init (&dirfile_cache, sizeof (struct dirfile),
1411 "dirfile", NULL, NULL);
1412#endif
1413}
Note: See TracBrowser for help on using the repository browser.