VirtualBox

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

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

Workaround for readdir on smbfs drive. (FreeBSD)

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