VirtualBox

source: kBuild/trunk/src/kmk/dir.c@ 1901

Last change on this file since 1901 was 1901, checked in by bird, 16 years ago

kmk: fix for file_cache <-> file_strcache confusion bug introduced earlier tonight.

  • Property svn:eol-style set to native
File size: 35.3 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
379static unsigned long
380directory_hash_1 (const void *key)
381{
382#ifndef CONFIG_WITH_STRCACHE2
383 return_ISTRING_HASH_1 (((const struct directory *) key)->name);
384#else
385 return strcache2_get_ptr_hash (&file_strcache, ((const struct directory *) key)->name);
386#endif
387}
388
389static unsigned long
390directory_hash_2 (const void *key)
391{
392#ifndef CONFIG_WITH_STRCACHE2
393 return_ISTRING_HASH_2 (((const struct directory *) key)->name);
394#else
395 return strcache2_get_hash1 (&file_strcache, ((const struct directory *) key)->name);
396#endif
397}
398
399static int
400directory_hash_cmp (const void *x, const void *y)
401{
402#ifndef CONFIG_WITH_STRCACHE2
403 return_ISTRING_COMPARE (((const struct directory *) x)->name,
404 ((const struct directory *) y)->name);
405#else
406 return ((const struct directory *) x)->name
407 == ((const struct directory *) y)->name ? 0 : -1;
408#endif
409}
410
411/* Table of directories hashed by name. */
412static struct hash_table directories;
413
414#ifdef CONFIG_WITH_ALLOC_CACHES
415/* Allocation cache for directories. */
416struct alloccache directories_cache;
417#endif
418
419/* Never have more than this many directories open at once. */
420
421#define MAX_OPEN_DIRECTORIES 10
422
423static unsigned int open_directories = 0;
424
425
426/* Hash table of files in each directory. */
427
428struct dirfile
429 {
430 const char *name; /* Name of the file. */
431 short length;
432 short impossible; /* This file is impossible. */
433 };
434
435static unsigned long
436dirfile_hash_1 (const void *key)
437{
438#ifndef CONFIG_WITH_STRCACHE2
439 return_ISTRING_HASH_1 (((struct dirfile const *) key)->name);
440#else
441 return strcache2_get_ptr_hash (&file_strcache, ((struct dirfile const *) key)->name);
442#endif
443}
444
445static unsigned long
446dirfile_hash_2 (const void *key)
447{
448#ifndef CONFIG_WITH_STRCACHE2
449 return_ISTRING_HASH_2 (((struct dirfile const *) key)->name);
450#else
451 return strcache2_get_hash1 (&file_strcache, ((struct dirfile const *) key)->name);
452#endif
453}
454
455static int
456dirfile_hash_cmp (const void *xv, const void *yv)
457{
458 const struct dirfile *x = xv;
459 const struct dirfile *y = yv;
460#ifndef CONFIG_WITH_STRCACHE2
461 int result = x->length - y->length;
462 if (result)
463 return result;
464 return_ISTRING_COMPARE (x->name, y->name);
465#else
466 return x->name == y->name ? 0 : -1;
467#endif
468}
469
470#ifndef DIRFILE_BUCKETS
471#define DIRFILE_BUCKETS 107
472#endif
473
474#ifdef CONFIG_WITH_ALLOC_CACHES
475/* Allocation cache for dirfiles. */
476struct alloccache dirfile_cache;
477#endif
478
479
480
481static int dir_contents_file_exists_p (struct directory_contents *dir,
482 const char *filename);
483static struct directory *find_directory (const char *name);
484
485/* Find the directory named NAME and return its `struct directory'. */
486
487static struct directory *
488find_directory (const char *name)
489{
490 const char *p;
491 struct directory *dir;
492 struct directory **dir_slot;
493 struct directory dir_key;
494 int r;
495#ifdef WINDOWS32
496 char* w32_path;
497 char fs_label[BUFSIZ];
498 char fs_type[BUFSIZ];
499 unsigned long fs_serno;
500 unsigned long fs_flags;
501 unsigned long fs_len;
502#endif
503#ifdef VMS
504 if ((*name == '.') && (*(name+1) == 0))
505 name = "[]";
506 else
507 name = vmsify (name,1);
508#endif
509
510#ifndef CONFIG_WITH_STRCACHE2
511 dir_key.name = name;
512#else
513 p = name + strlen (name);
514 dir_key.name = strcache_add_len (name, p - name);
515#endif
516 dir_slot = (struct directory **) hash_find_slot (&directories, &dir_key);
517 dir = *dir_slot;
518
519 if (HASH_VACANT (dir))
520 {
521 struct stat st;
522
523 /* The directory was not found. Create a new entry for it. */
524
525#ifndef CONFIG_WITH_STRCACHE2
526 p = name + strlen (name);
527#endif
528#ifndef CONFIG_WITH_ALLOC_CACHES
529 dir = xmalloc (sizeof (struct directory));
530#else
531 dir = alloccache_alloc (&directories_cache);
532#endif
533#ifndef CONFIG_WITH_STRCACHE2
534 dir->name = strcache_add_len (name, p - name);
535#else
536 dir->name = dir_key.name;
537#endif
538 hash_insert_at (&directories, dir, dir_slot);
539 /* The directory is not in the name hash table.
540 Find its device and inode numbers, and look it up by them. */
541
542#ifdef WINDOWS32
543 /* Remove any trailing '\'. Windows32 stat fails even on valid
544 directories if they end in '\'. */
545 if (p[-1] == '\\')
546 ((char *)p)[-1] = '\0';
547#endif
548
549#ifdef VMS
550 r = vmsstat_dir (name, &st);
551#else
552 EINTRLOOP (r, stat (name, &st));
553#endif
554
555#ifdef WINDOWS32
556 /* Put back the trailing '\'. If we don't, we're permanently
557 truncating the value! */
558 if (p[-1] == '\0')
559 ((char *)p)[-1] = '\\';
560#endif
561
562 if (r < 0)
563 {
564 /* Couldn't stat the directory. Mark this by
565 setting the `contents' member to a nil pointer. */
566 dir->contents = 0;
567 }
568 else
569 {
570 /* Search the contents hash table; device and inode are the key. */
571
572 struct directory_contents *dc;
573 struct directory_contents **dc_slot;
574 struct directory_contents dc_key;
575
576 dc_key.dev = st.st_dev;
577#ifdef WINDOWS32
578# ifndef CONFIG_WITH_STRCACHE2
579 dc_key.path_key = w32_path = w32ify (name, 1);
580# else /* CONFIG_WITH_STRCACHE2 */
581 w32_path = w32ify (name, 1);
582 dc_key.path_key = strcache_add (w32_path);
583# endif /* CONFIG_WITH_STRCACHE2 */
584 dc_key.ctime = st.st_ctime;
585#else
586# ifdef VMS
587 dc_key.ino[0] = st.st_ino[0];
588 dc_key.ino[1] = st.st_ino[1];
589 dc_key.ino[2] = st.st_ino[2];
590# else
591 dc_key.ino = st.st_ino;
592# endif
593#endif
594 dc_slot = (struct directory_contents **) hash_find_slot (&directory_contents, &dc_key);
595 dc = *dc_slot;
596
597 if (HASH_VACANT (dc))
598 {
599 /* Nope; this really is a directory we haven't seen before. */
600
601#ifndef CONFIG_WITH_ALLOC_CACHES
602 dc = (struct directory_contents *)
603 xmalloc (sizeof (struct directory_contents));
604#else
605 dc = (struct directory_contents *)
606 alloccache_alloc (&directory_contents_cache);
607#endif
608
609 /* Enter it in the contents hash table. */
610 dc->dev = st.st_dev;
611#ifdef WINDOWS32
612# ifndef CONFIG_WITH_STRCACHE2
613 dc->path_key = xstrdup (w32_path);
614# else /* CONFIG_WITH_STRCACHE2 */
615 dc->path_key = dc_key.path_key;
616# endif /* CONFIG_WITH_STRCACHE2 */
617
618 dc->ctime = st.st_ctime;
619 dc->mtime = st.st_mtime;
620
621 /*
622 * NTFS is the only WINDOWS32 filesystem that bumps mtime
623 * on a directory when files are added/deleted from
624 * a directory.
625 */
626 w32_path[3] = '\0';
627 if (GetVolumeInformation(w32_path,
628 fs_label, sizeof (fs_label),
629 &fs_serno, &fs_len,
630 &fs_flags, fs_type, sizeof (fs_type)) == FALSE)
631 dc->fs_flags = FS_UNKNOWN;
632 else if (!strcmp(fs_type, "FAT"))
633 dc->fs_flags = FS_FAT;
634 else if (!strcmp(fs_type, "NTFS"))
635 dc->fs_flags = FS_NTFS;
636 else
637 dc->fs_flags = FS_UNKNOWN;
638#else
639# ifdef VMS
640 dc->ino[0] = st.st_ino[0];
641 dc->ino[1] = st.st_ino[1];
642 dc->ino[2] = st.st_ino[2];
643# else
644 dc->ino = st.st_ino;
645# endif
646#endif /* WINDOWS32 */
647 hash_insert_at (&directory_contents, dc, dc_slot);
648 ENULLLOOP (dc->dirstream, opendir (name));
649 if (dc->dirstream == 0)
650 /* Couldn't open the directory. Mark this by setting the
651 `files' member to a nil pointer. */
652 dc->dirfiles.ht_vec = 0;
653 else
654 {
655#ifdef KMK
656 int buckets = st.st_nlink * 2;
657 if (buckets < DIRFILE_BUCKETS)
658 buckets = DIRFILE_BUCKETS;
659 hash_init (&dc->dirfiles, buckets,
660 dirfile_hash_1, dirfile_hash_2, dirfile_hash_cmp);
661#else
662 hash_init (&dc->dirfiles, DIRFILE_BUCKETS,
663 dirfile_hash_1, dirfile_hash_2, dirfile_hash_cmp);
664#endif
665 /* Keep track of how many directories are open. */
666 ++open_directories;
667 if (open_directories == MAX_OPEN_DIRECTORIES)
668 /* We have too many directories open already.
669 Read the entire directory and then close it. */
670 dir_contents_file_exists_p (dc, 0);
671 }
672 }
673
674 /* Point the name-hashed entry for DIR at its contents data. */
675 dir->contents = dc;
676 }
677 }
678
679 return dir;
680}
681
682
683/* Return 1 if the name FILENAME is entered in DIR's hash table.
684 FILENAME must contain no slashes. */
685
686static int
687dir_contents_file_exists_p (struct directory_contents *dir,
688 const char *filename)
689{
690 unsigned int hash;
691 struct dirfile *df;
692 struct dirent *d;
693#ifdef WINDOWS32
694 struct stat st;
695 int rehash = 0;
696#endif
697
698 if (dir == 0 || dir->dirfiles.ht_vec == 0)
699 /* The directory could not be stat'd or opened. */
700 return 0;
701
702#ifdef __MSDOS__
703 filename = dosify (filename);
704#endif
705
706#ifdef HAVE_CASE_INSENSITIVE_FS
707 filename = downcase (filename);
708#endif
709
710#ifdef __EMX__
711 if (filename != 0)
712 _fnlwr (filename); /* lower case for FAT drives */
713#endif
714
715#ifdef VMS
716 filename = vmsify (filename,0);
717#endif
718
719 hash = 0;
720 if (filename != 0)
721 {
722 struct dirfile dirfile_key;
723
724 if (*filename == '\0')
725 {
726 /* Checking if the directory exists. */
727 return 1;
728 }
729#ifndef CONFIG_WITH_STRCACHE2
730 dirfile_key.name = filename;
731 dirfile_key.length = strlen (filename);
732#else /* CONFIG_WITH_STRCACHE2 */
733 dirfile_key.length = strlen (filename);
734 dirfile_key.name = filename
735 = strcache_add_len (filename, dirfile_key.length);
736#endif /* CONFIG_WITH_STRCACHE2 */
737 df = hash_find_item (&dir->dirfiles, &dirfile_key);
738 if (df)
739 return !df->impossible;
740 }
741
742 /* The file was not found in the hashed list.
743 Try to read the directory further. */
744
745 if (dir->dirstream == 0)
746 {
747#ifdef WINDOWS32
748 /*
749 * Check to see if directory has changed since last read. FAT
750 * filesystems force a rehash always as mtime does not change
751 * on directories (ugh!).
752 */
753 if (dir->path_key)
754 {
755 if ((dir->fs_flags & FS_FAT) != 0)
756 {
757 dir->mtime = time ((time_t *) 0);
758 rehash = 1;
759 }
760 else if (stat (dir->path_key, &st) == 0 && st.st_mtime > dir->mtime)
761 {
762 /* reset date stamp to show most recent re-process. */
763 dir->mtime = st.st_mtime;
764 rehash = 1;
765 }
766
767 /* If it has been already read in, all done. */
768 if (!rehash)
769 return 0;
770
771 /* make sure directory can still be opened; if not return. */
772 dir->dirstream = opendir (dir->path_key);
773 if (!dir->dirstream)
774 return 0;
775 }
776 else
777#endif
778 /* The directory has been all read in. */
779 return 0;
780 }
781
782 while (1)
783 {
784 /* Enter the file in the hash table. */
785 unsigned int len;
786 struct dirfile dirfile_key;
787 struct dirfile **dirfile_slot;
788
789 ENULLLOOP (d, readdir (dir->dirstream));
790 if (d == 0)
791 {
792/* bird: Workaround for smbfs mounts returning EBADF at the end of the search.
793 To exactly determin the cause here, I should probably do some smbfs
794 tracing, but for now just ignoring the EBADF on seems to work.
795 (The smb server is 64-bit vista, btw.) */
796#if defined (__FreeBSD__)
797 struct statfs stfs;
798 int saved_errno = errno;
799 errno = 0;
800 if (saved_errno == EBADF
801 && !fstatfs (dirfd (dir->dirstream), &stfs)
802 && !(stfs.f_flags & MNT_LOCAL)
803 && !strcmp(stfs.f_fstypename, "smbfs"))
804 {
805 /*fprintf (stderr, "EBADF on remote fs! dirfd=%d errno=%d\n",
806 dirfd (dir->dirstream), errno);*/
807 saved_errno = 0;
808 }
809 errno = saved_errno;
810#endif
811/* bird: end */
812 if (errno)
813 fatal (NILF, "INTERNAL: readdir(%p): %s (filename=%s)\n", dir, strerror (errno), filename);
814 break;
815 }
816
817#if defined(VMS) && defined(HAVE_DIRENT_H)
818 /* In VMS we get file versions too, which have to be stripped off */
819 {
820 char *p = strrchr (d->d_name, ';');
821 if (p)
822 *p = '\0';
823 }
824#endif
825 if (!REAL_DIR_ENTRY (d))
826 continue;
827
828 len = NAMLEN (d);
829#ifndef CONFIG_WITH_STRCACHE2
830 dirfile_key.name = d->d_name;
831#else
832 dirfile_key.name = strcache_add_len (d->d_name, len);
833#endif
834 dirfile_key.length = len;
835 dirfile_slot = (struct dirfile **) hash_find_slot (&dir->dirfiles, &dirfile_key);
836#ifdef WINDOWS32
837 /*
838 * If re-reading a directory, don't cache files that have
839 * already been discovered.
840 */
841 if (! rehash || HASH_VACANT (*dirfile_slot))
842#endif
843 {
844#ifndef CONFIG_WITH_ALLOC_CACHES
845 df = xmalloc (sizeof (struct dirfile));
846#else
847 df = alloccache_alloc (&dirfile_cache);
848#endif
849#ifndef CONFIG_WITH_STRCACHE2
850 df->name = strcache_add_len (d->d_name, len);
851#else
852 df->name = dirfile_key.name;
853#endif
854 df->length = len;
855 df->impossible = 0;
856 hash_insert_at (&dir->dirfiles, df, dirfile_slot);
857 }
858 /* Check if the name matches the one we're searching for. */
859#ifndef CONFIG_WITH_STRCACHE2
860 if (filename != 0 && strieq (d->d_name, filename))
861#else
862 if (filename != 0 && dirfile_key.name == filename)
863#endif
864 return 1;
865 }
866
867 /* If the directory has been completely read in,
868 close the stream and reset the pointer to nil. */
869 if (d == 0)
870 {
871 --open_directories;
872 closedir (dir->dirstream);
873 dir->dirstream = 0;
874 }
875 return 0;
876}
877
878/* Return 1 if the name FILENAME in directory DIRNAME
879 is entered in the dir hash table.
880 FILENAME must contain no slashes. */
881
882int
883dir_file_exists_p (const char *dirname, const char *filename)
884{
885 return dir_contents_file_exists_p (find_directory (dirname)->contents,
886 filename);
887}
888
889
890/* Return 1 if the file named NAME exists. */
891
892int
893file_exists_p (const char *name)
894{
895 const char *dirend;
896 const char *dirname;
897 const char *slash;
898
899#ifndef NO_ARCHIVES
900 if (ar_name (name))
901 return ar_member_date (name) != (time_t) -1;
902#endif
903
904#ifdef VMS
905 dirend = strrchr (name, ']');
906 if (dirend == 0)
907 dirend = strrchr (name, ':');
908 if (dirend == 0)
909 return dir_file_exists_p ("[]", name);
910#else /* !VMS */
911 dirend = strrchr (name, '/');
912#ifdef HAVE_DOS_PATHS
913 /* Forward and backslashes might be mixed. We need the rightmost one. */
914 {
915 const char *bslash = strrchr(name, '\\');
916 if (!dirend || bslash > dirend)
917 dirend = bslash;
918 /* The case of "d:file". */
919 if (!dirend && name[0] && name[1] == ':')
920 dirend = name + 1;
921 }
922#endif /* HAVE_DOS_PATHS */
923 if (dirend == 0)
924#ifndef _AMIGA
925 return dir_file_exists_p (".", name);
926#else /* !VMS && !AMIGA */
927 return dir_file_exists_p ("", name);
928#endif /* AMIGA */
929#endif /* VMS */
930
931 slash = dirend;
932 if (dirend == name)
933 dirname = "/";
934 else
935 {
936 char *p;
937#ifdef HAVE_DOS_PATHS
938 /* d:/ and d: are *very* different... */
939 if (dirend < name + 3 && name[1] == ':' &&
940 (*dirend == '/' || *dirend == '\\' || *dirend == ':'))
941 dirend++;
942#endif
943 p = alloca (dirend - name + 1);
944 memcpy (p, name, dirend - name);
945 p[dirend - name] = '\0';
946 dirname = p;
947 }
948 return dir_file_exists_p (dirname, slash + 1);
949}
950
951
952/* Mark FILENAME as `impossible' for `file_impossible_p'.
953 This means an attempt has been made to search for FILENAME
954 as an intermediate file, and it has failed. */
955
956void
957file_impossible (const char *filename)
958{
959 const char *dirend;
960 const char *p = filename;
961 struct directory *dir;
962 struct dirfile *new;
963
964#ifdef VMS
965 dirend = strrchr (p, ']');
966 if (dirend == 0)
967 dirend = strrchr (p, ':');
968 dirend++;
969 if (dirend == (char *)1)
970 dir = find_directory ("[]");
971#else
972 dirend = strrchr (p, '/');
973# ifdef HAVE_DOS_PATHS
974 /* Forward and backslashes might be mixed. We need the rightmost one. */
975 {
976 const char *bslash = strrchr(p, '\\');
977 if (!dirend || bslash > dirend)
978 dirend = bslash;
979 /* The case of "d:file". */
980 if (!dirend && p[0] && p[1] == ':')
981 dirend = p + 1;
982 }
983# endif /* HAVE_DOS_PATHS */
984 if (dirend == 0)
985# ifdef _AMIGA
986 dir = find_directory ("");
987# else /* !VMS && !AMIGA */
988 dir = find_directory (".");
989# endif /* AMIGA */
990#endif /* VMS */
991 else
992 {
993 const char *dirname;
994 const char *slash = dirend;
995 if (dirend == p)
996 dirname = "/";
997 else
998 {
999 char *cp;
1000#ifdef HAVE_DOS_PATHS
1001 /* d:/ and d: are *very* different... */
1002 if (dirend < p + 3 && p[1] == ':' &&
1003 (*dirend == '/' || *dirend == '\\' || *dirend == ':'))
1004 dirend++;
1005#endif
1006 cp = alloca (dirend - p + 1);
1007 memcpy (cp, p, dirend - p);
1008 cp[dirend - p] = '\0';
1009 dirname = cp;
1010 }
1011 dir = find_directory (dirname);
1012 filename = p = slash + 1;
1013 }
1014
1015 if (dir->contents == 0)
1016 {
1017 /* The directory could not be stat'd. We allocate a contents
1018 structure for it, but leave it out of the contents hash table. */
1019#ifndef CONFIG_WITH_ALLOC_CACHES
1020 dir->contents = xmalloc (sizeof (struct directory_contents));
1021#else
1022 dir->contents = alloccache_alloc (&directory_contents_cache);
1023#endif
1024 memset (dir->contents, '\0', sizeof (struct directory_contents));
1025
1026 }
1027
1028 if (dir->contents->dirfiles.ht_vec == 0)
1029 {
1030 hash_init (&dir->contents->dirfiles, DIRFILE_BUCKETS,
1031 dirfile_hash_1, dirfile_hash_2, dirfile_hash_cmp);
1032 }
1033
1034 /* Make a new entry and put it in the table. */
1035
1036#ifndef CONFIG_WITH_ALLOC_CACHES
1037 new = xmalloc (sizeof (struct dirfile));
1038#else
1039 new = alloccache_alloc (&dirfile_cache);
1040#endif
1041 new->length = strlen (filename);
1042 new->name = strcache_add_len (filename, new->length);
1043 new->impossible = 1;
1044 hash_insert (&dir->contents->dirfiles, new);
1045}
1046
1047
1048/* Return nonzero if FILENAME has been marked impossible. */
1049
1050int
1051file_impossible_p (const char *filename)
1052{
1053 const char *dirend;
1054 const char *p = filename;
1055 struct directory_contents *dir;
1056 struct dirfile *dirfile;
1057 struct dirfile dirfile_key;
1058
1059#ifdef VMS
1060 dirend = strrchr (filename, ']');
1061 if (dirend == 0)
1062 dir = find_directory ("[]")->contents;
1063#else
1064 dirend = strrchr (filename, '/');
1065#ifdef HAVE_DOS_PATHS
1066 /* Forward and backslashes might be mixed. We need the rightmost one. */
1067 {
1068 const char *bslash = strrchr(filename, '\\');
1069 if (!dirend || bslash > dirend)
1070 dirend = bslash;
1071 /* The case of "d:file". */
1072 if (!dirend && filename[0] && filename[1] == ':')
1073 dirend = filename + 1;
1074 }
1075#endif /* HAVE_DOS_PATHS */
1076 if (dirend == 0)
1077#ifdef _AMIGA
1078 dir = find_directory ("")->contents;
1079#else /* !VMS && !AMIGA */
1080 dir = find_directory (".")->contents;
1081#endif /* AMIGA */
1082#endif /* VMS */
1083 else
1084 {
1085 const char *dirname;
1086 const char *slash = dirend;
1087 if (dirend == filename)
1088 dirname = "/";
1089 else
1090 {
1091 char *cp;
1092#ifdef HAVE_DOS_PATHS
1093 /* d:/ and d: are *very* different... */
1094 if (dirend < filename + 3 && filename[1] == ':' &&
1095 (*dirend == '/' || *dirend == '\\' || *dirend == ':'))
1096 dirend++;
1097#endif
1098 cp = alloca (dirend - filename + 1);
1099 memcpy (cp, p, dirend - p);
1100 cp[dirend - p] = '\0';
1101 dirname = cp;
1102 }
1103 dir = find_directory (dirname)->contents;
1104 p = filename = slash + 1;
1105 }
1106
1107 if (dir == 0 || dir->dirfiles.ht_vec == 0)
1108 /* There are no files entered for this directory. */
1109 return 0;
1110
1111#ifdef __MSDOS__
1112 filename = dosify (p);
1113#endif
1114#ifdef HAVE_CASE_INSENSITIVE_FS
1115 filename = downcase (p);
1116#endif
1117#ifdef VMS
1118 filename = vmsify (p, 1);
1119#endif
1120
1121#ifndef CONFIG_WITH_STRCACHE2
1122 dirfile_key.name = filename;
1123 dirfile_key.length = strlen (filename);
1124#else
1125 dirfile_key.length = strlen (filename);
1126 dirfile_key.name = strcache_add_len (filename, dirfile_key.length);
1127#endif
1128 dirfile = hash_find_item (&dir->dirfiles, &dirfile_key);
1129 if (dirfile)
1130 return dirfile->impossible;
1131
1132 return 0;
1133}
1134
1135
1136/* Return the already allocated name in the
1137 directory hash table that matches DIR. */
1138
1139const char *
1140dir_name (const char *dir)
1141{
1142 return find_directory (dir)->name;
1143}
1144
1145
1146/* Print the data base of directories. */
1147
1148void
1149print_dir_data_base (void)
1150{
1151 unsigned int files;
1152 unsigned int impossible;
1153 struct directory **dir_slot;
1154 struct directory **dir_end;
1155
1156 puts (_("\n# Directories\n"));
1157
1158 files = impossible = 0;
1159
1160 dir_slot = (struct directory **) directories.ht_vec;
1161 dir_end = dir_slot + directories.ht_size;
1162 for ( ; dir_slot < dir_end; dir_slot++)
1163 {
1164 struct directory *dir = *dir_slot;
1165 if (! HASH_VACANT (dir))
1166 {
1167 if (dir->contents == 0)
1168 printf (_("# %s: could not be stat'd.\n"), dir->name);
1169 else if (dir->contents->dirfiles.ht_vec == 0)
1170 {
1171#ifdef WINDOWS32
1172 printf (_("# %s (key %s, mtime %d): could not be opened.\n"),
1173 dir->name, dir->contents->path_key,dir->contents->mtime);
1174#else /* WINDOWS32 */
1175#ifdef VMS
1176 printf (_("# %s (device %d, inode [%d,%d,%d]): could not be opened.\n"),
1177 dir->name, dir->contents->dev,
1178 dir->contents->ino[0], dir->contents->ino[1],
1179 dir->contents->ino[2]);
1180#else
1181 printf (_("# %s (device %ld, inode %ld): could not be opened.\n"),
1182 dir->name, (long int) dir->contents->dev,
1183 (long int) dir->contents->ino);
1184#endif
1185#endif /* WINDOWS32 */
1186 }
1187 else
1188 {
1189 unsigned int f = 0;
1190 unsigned int im = 0;
1191 struct dirfile **files_slot;
1192 struct dirfile **files_end;
1193
1194 files_slot = (struct dirfile **) dir->contents->dirfiles.ht_vec;
1195 files_end = files_slot + dir->contents->dirfiles.ht_size;
1196 for ( ; files_slot < files_end; files_slot++)
1197 {
1198 struct dirfile *df = *files_slot;
1199 if (! HASH_VACANT (df))
1200 {
1201 if (df->impossible)
1202 ++im;
1203 else
1204 ++f;
1205 }
1206 }
1207#ifdef WINDOWS32
1208 printf (_("# %s (key %s, mtime %d): "),
1209 dir->name, dir->contents->path_key, dir->contents->mtime);
1210#else /* WINDOWS32 */
1211#ifdef VMS
1212 printf (_("# %s (device %d, inode [%d,%d,%d]): "),
1213 dir->name, dir->contents->dev,
1214 dir->contents->ino[0], dir->contents->ino[1],
1215 dir->contents->ino[2]);
1216#else
1217 printf (_("# %s (device %ld, inode %ld): "),
1218 dir->name,
1219 (long)dir->contents->dev, (long)dir->contents->ino);
1220#endif
1221#endif /* WINDOWS32 */
1222 if (f == 0)
1223 fputs (_("No"), stdout);
1224 else
1225 printf ("%u", f);
1226 fputs (_(" files, "), stdout);
1227 if (im == 0)
1228 fputs (_("no"), stdout);
1229 else
1230 printf ("%u", im);
1231 fputs (_(" impossibilities"), stdout);
1232 if (dir->contents->dirstream == 0)
1233 puts (".");
1234 else
1235 puts (_(" so far."));
1236 files += f;
1237 impossible += im;
1238
1239#ifdef KMK
1240 fputs ("# ", stdout);
1241 hash_print_stats (&dir->contents->dirfiles, stdout);
1242 fputs ("\n", stdout);
1243#endif
1244 }
1245 }
1246 }
1247
1248 fputs ("\n# ", stdout);
1249 if (files == 0)
1250 fputs (_("No"), stdout);
1251 else
1252 printf ("%u", files);
1253 fputs (_(" files, "), stdout);
1254 if (impossible == 0)
1255 fputs (_("no"), stdout);
1256 else
1257 printf ("%u", impossible);
1258 printf (_(" impossibilities in %lu directories.\n"), directories.ht_fill);
1259#ifdef KMK
1260 fputs ("# directories: ", stdout);
1261 hash_print_stats (&directories, stdout);
1262 fputs ("\n# directory_contents: ", stdout);
1263 hash_print_stats (&directory_contents, stdout);
1264 fputs ("\n", stdout);
1265#endif
1266}
1267
1268
1269/* Hooks for globbing. */
1270
1271#include <glob.h>
1272
1273/* Structure describing state of iterating through a directory hash table. */
1274
1275struct dirstream
1276 {
1277 struct directory_contents *contents; /* The directory being read. */
1278 struct dirfile **dirfile_slot; /* Current slot in table. */
1279 };
1280
1281/* Forward declarations. */
1282static __ptr_t open_dirstream (const char *);
1283static struct dirent *read_dirstream (__ptr_t);
1284
1285static __ptr_t
1286open_dirstream (const char *directory)
1287{
1288 struct dirstream *new;
1289 struct directory *dir = find_directory (directory);
1290
1291 if (dir->contents == 0 || dir->contents->dirfiles.ht_vec == 0)
1292 /* DIR->contents is nil if the directory could not be stat'd.
1293 DIR->contents->dirfiles is nil if it could not be opened. */
1294 return 0;
1295
1296 /* Read all the contents of the directory now. There is no benefit
1297 in being lazy, since glob will want to see every file anyway. */
1298
1299 dir_contents_file_exists_p (dir->contents, 0);
1300
1301 new = xmalloc (sizeof (struct dirstream));
1302 new->contents = dir->contents;
1303 new->dirfile_slot = (struct dirfile **) new->contents->dirfiles.ht_vec;
1304
1305 return (__ptr_t) new;
1306}
1307
1308static struct dirent *
1309read_dirstream (__ptr_t stream)
1310{
1311 static char *buf;
1312 static unsigned int bufsz;
1313
1314 struct dirstream *const ds = (struct dirstream *) stream;
1315 struct directory_contents *dc = ds->contents;
1316 struct dirfile **dirfile_end = (struct dirfile **) dc->dirfiles.ht_vec + dc->dirfiles.ht_size;
1317
1318 while (ds->dirfile_slot < dirfile_end)
1319 {
1320 struct dirfile *df = *ds->dirfile_slot++;
1321 if (! HASH_VACANT (df) && !df->impossible)
1322 {
1323 /* The glob interface wants a `struct dirent', so mock one up. */
1324 struct dirent *d;
1325 unsigned int len = df->length + 1;
1326 unsigned int sz = sizeof (*d) - sizeof (d->d_name) + len;
1327 if (sz > bufsz)
1328 {
1329 bufsz *= 2;
1330 if (sz > bufsz)
1331 bufsz = sz;
1332 buf = xrealloc (buf, bufsz);
1333 }
1334 d = (struct dirent *) buf;
1335#ifdef __MINGW32__
1336# if __MINGW32_MAJOR_VERSION < 3 || (__MINGW32_MAJOR_VERSION == 3 && \
1337 __MINGW32_MINOR_VERSION == 0)
1338 d->d_name = xmalloc(len);
1339# endif
1340#endif
1341 FAKE_DIR_ENTRY (d);
1342#ifdef _DIRENT_HAVE_D_NAMLEN
1343 d->d_namlen = len - 1;
1344#endif
1345#ifdef _DIRENT_HAVE_D_TYPE
1346 d->d_type = DT_UNKNOWN;
1347#endif
1348 memcpy (d->d_name, df->name, len);
1349 return d;
1350 }
1351 }
1352
1353 return 0;
1354}
1355
1356static void
1357ansi_free (void *p)
1358{
1359 if (p)
1360 free(p);
1361}
1362
1363/* On 64 bit ReliantUNIX (5.44 and above) in LFS mode, stat() is actually a
1364 * macro for stat64(). If stat is a macro, make a local wrapper function to
1365 * invoke it.
1366 */
1367#ifndef stat
1368# ifndef VMS
1369int stat (const char *path, struct stat *sbuf);
1370# endif
1371# define local_stat stat
1372#else
1373static int
1374local_stat (const char *path, struct stat *buf)
1375{
1376 int e;
1377
1378 EINTRLOOP (e, stat (path, buf));
1379 return e;
1380}
1381#endif
1382
1383void
1384dir_setup_glob (glob_t *gl)
1385{
1386 gl->gl_opendir = open_dirstream;
1387 gl->gl_readdir = read_dirstream;
1388 gl->gl_closedir = ansi_free;
1389 gl->gl_stat = local_stat;
1390#ifdef __EMX__ /* The FreeBSD implemenation actually uses gl_lstat!! */
1391 gl->gl_lstat = local_stat;
1392#endif
1393 /* We don't bother setting gl_lstat, since glob never calls it.
1394 The slot is only there for compatibility with 4.4 BSD. */
1395}
1396
1397void
1398hash_init_directories (void)
1399{
1400 hash_init (&directories, DIRECTORY_BUCKETS,
1401 directory_hash_1, directory_hash_2, directory_hash_cmp);
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.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette