VirtualBox

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

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

kmk: Change the directory hash size to prevent /usr/bin from causing 3 rehashings.

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