VirtualBox

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

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

kmk: Use allocation caches for the tiny structs in dir.c (three caches / structs).

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