VirtualBox

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

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

cosmetics.

  • Property svn:eol-style set to native
File size: 31.3 KB
Line 
1/* Directory hashing for GNU Make.
2Copyright (C) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997,
31998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software
4Foundation, Inc.
5This file is part of GNU Make.
6
7GNU Make is free software; you can redistribute it and/or modify it under the
8terms of the GNU General Public License as published by the Free Software
9Foundation; either version 2, or (at your option) any later version.
10
11GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
12WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
13A PARTICULAR PURPOSE. See the GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License along with
16GNU Make; see the file COPYING. If not, write to the Free Software
17Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. */
18
19#include "make.h"
20#include "hash.h"
21
22#ifdef HAVE_DIRENT_H
23# include <dirent.h>
24# define NAMLEN(dirent) strlen((dirent)->d_name)
25# ifdef VMS
26char *vmsify (char *name, int type);
27# endif
28#else
29# define dirent direct
30# define NAMLEN(dirent) (dirent)->d_namlen
31# ifdef HAVE_SYS_NDIR_H
32# include <sys/ndir.h>
33# endif
34# ifdef HAVE_SYS_DIR_H
35# include <sys/dir.h>
36# endif
37# ifdef HAVE_NDIR_H
38# include <ndir.h>
39# endif
40# ifdef HAVE_VMSDIR_H
41# include "vmsdir.h"
42# endif /* HAVE_VMSDIR_H */
43#endif
44/* bird: FreeBSD + smbfs -> readdir() + EBADF */
45#ifdef __FreeBSD__
46# include <sys/mount.h>
47#endif
48/* bird: end */
49
50/* In GNU systems, <dirent.h> defines this macro for us. */
51#ifdef _D_NAMLEN
52# undef NAMLEN
53# define NAMLEN(d) _D_NAMLEN(d)
54#endif
55
56#if (defined (POSIX) || defined (VMS) || defined (WINDOWS32)) && !defined (__GNU_LIBRARY__)
57/* Posix does not require that the d_ino field be present, and some
58 systems do not provide it. */
59# define REAL_DIR_ENTRY(dp) 1
60# define FAKE_DIR_ENTRY(dp)
61#else
62# define REAL_DIR_ENTRY(dp) (dp->d_ino != 0)
63# define FAKE_DIR_ENTRY(dp) (dp->d_ino = 1)
64#endif /* POSIX */
65
66
67#ifdef __MSDOS__
68#include <ctype.h>
69#include <fcntl.h>
70
71/* If it's MSDOS that doesn't have _USE_LFN, disable LFN support. */
72#ifndef _USE_LFN
73#define _USE_LFN 0
74#endif
75
76static const char *
77dosify (const char *filename)
78{
79 static char dos_filename[14];
80 char *df;
81 int i;
82
83 if (filename == 0 || _USE_LFN)
84 return filename;
85
86 /* FIXME: what about filenames which violate
87 8+3 constraints, like "config.h.in", or ".emacs"? */
88 if (strpbrk (filename, "\"*+,;<=>?[\\]|") != 0)
89 return filename;
90
91 df = dos_filename;
92
93 /* First, transform the name part. */
94 for (i = 0; *filename != '\0' && i < 8 && *filename != '.'; ++i)
95 *df++ = tolower ((unsigned char)*filename++);
96
97 /* Now skip to the next dot. */
98 while (*filename != '\0' && *filename != '.')
99 ++filename;
100 if (*filename != '\0')
101 {
102 *df++ = *filename++;
103 for (i = 0; *filename != '\0' && i < 3 && *filename != '.'; ++i)
104 *df++ = tolower ((unsigned char)*filename++);
105 }
106
107 /* Look for more dots. */
108 while (*filename != '\0' && *filename != '.')
109 ++filename;
110 if (*filename == '.')
111 return filename;
112 *df = 0;
113 return dos_filename;
114}
115#endif /* __MSDOS__ */
116
117#ifdef WINDOWS32
118#include "pathstuff.h"
119#endif
120
121#ifdef _AMIGA
122#include <ctype.h>
123#endif
124
125#ifdef HAVE_CASE_INSENSITIVE_FS
126static const char *
127downcase (const char *filename)
128{
129 static PATH_VAR (new_filename);
130 char *df;
131 int i;
132
133 if (filename == 0)
134 return 0;
135
136 df = new_filename;
137
138 /* First, transform the name part. */
139 while (*filename != '\0')
140 {
141 *df++ = tolower ((unsigned char)*filename);
142 ++filename;
143 }
144
145 *df = 0;
146
147 return new_filename;
148}
149#endif /* HAVE_CASE_INSENSITIVE_FS */
150
151#ifdef VMS
152
153static int
154vms_hash (char *name)
155{
156 int h = 0;
157 int g;
158
159 while (*name)
160 {
161 unsigned char uc = *name;
162#ifdef HAVE_CASE_INSENSITIVE_FS
163 h = (h << 4) + (isupper (uc) ? tolower (uc) : uc);
164#else
165 h = (h << 4) + uc;
166#endif
167 name++;
168 g = h & 0xf0000000;
169 if (g)
170 {
171 h = h ^ (g >> 24);
172 h = h ^ g;
173 }
174 }
175 return h;
176}
177
178/* fake stat entry for a directory */
179static int
180vmsstat_dir (char *name, struct stat *st)
181{
182 char *s;
183 int h;
184 DIR *dir;
185
186 dir = opendir (name);
187 if (dir == 0)
188 return -1;
189 closedir (dir);
190 s = strchr (name, ':'); /* find device */
191 if (s)
192 {
193 *s++ = 0;
194 st->st_dev = (char *)vms_hash (name);
195 h = vms_hash (s);
196 *(s-1) = ':';
197 }
198 else
199 {
200 st->st_dev = 0;
201 s = name;
202 h = vms_hash (s);
203 }
204
205 st->st_ino[0] = h & 0xff;
206 st->st_ino[1] = h & 0xff00;
207 st->st_ino[2] = h >> 16;
208
209 return 0;
210}
211#endif /* VMS */
212
213
214/* Hash table of directories. */
215
216#ifndef DIRECTORY_BUCKETS
217#ifdef KMK
218# define DIRECTORY_BUCKETS 4096
219# else
220#define DIRECTORY_BUCKETS 199
221# endif
222#endif
223
224struct directory_contents
225 {
226 dev_t dev; /* Device and inode numbers of this dir. */
227#ifdef WINDOWS32
228 /* Inode means nothing on WINDOWS32. Even file key information is
229 * unreliable because it is random per file open and undefined for remote
230 * filesystems. The most unique attribute I can come up with is the fully
231 * qualified name of the directory. Beware though, this is also
232 * unreliable. I'm open to suggestion on a better way to emulate inode. */
233 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 hash_init (&dc->dirfiles, DIRFILE_BUCKETS,
568 dirfile_hash_1, dirfile_hash_2, dirfile_hash_cmp);
569 /* Keep track of how many directories are open. */
570 ++open_directories;
571 if (open_directories == MAX_OPEN_DIRECTORIES)
572 /* We have too many directories open already.
573 Read the entire directory and then close it. */
574 dir_contents_file_exists_p (dc, 0);
575 }
576 }
577
578 /* Point the name-hashed entry for DIR at its contents data. */
579 dir->contents = dc;
580 }
581 }
582
583 return dir;
584}
585
586
587/* Return 1 if the name FILENAME is entered in DIR's hash table.
588 FILENAME must contain no slashes. */
589
590static int
591dir_contents_file_exists_p (struct directory_contents *dir,
592 const char *filename)
593{
594 unsigned int hash;
595 struct dirfile *df;
596 struct dirent *d;
597#ifdef WINDOWS32
598 struct stat st;
599 int rehash = 0;
600#endif
601
602 if (dir == 0 || dir->dirfiles.ht_vec == 0)
603 /* The directory could not be stat'd or opened. */
604 return 0;
605
606#ifdef __MSDOS__
607 filename = dosify (filename);
608#endif
609
610#ifdef HAVE_CASE_INSENSITIVE_FS
611 filename = downcase (filename);
612#endif
613
614#ifdef __EMX__
615 if (filename != 0)
616 _fnlwr (filename); /* lower case for FAT drives */
617#endif
618
619#ifdef VMS
620 filename = vmsify (filename,0);
621#endif
622
623 hash = 0;
624 if (filename != 0)
625 {
626 struct dirfile dirfile_key;
627
628 if (*filename == '\0')
629 {
630 /* Checking if the directory exists. */
631 return 1;
632 }
633 dirfile_key.name = filename;
634 dirfile_key.length = strlen (filename);
635 df = hash_find_item (&dir->dirfiles, &dirfile_key);
636 if (df)
637 return !df->impossible;
638 }
639
640 /* The file was not found in the hashed list.
641 Try to read the directory further. */
642
643 if (dir->dirstream == 0)
644 {
645#ifdef WINDOWS32
646 /*
647 * Check to see if directory has changed since last read. FAT
648 * filesystems force a rehash always as mtime does not change
649 * on directories (ugh!).
650 */
651 if (dir->path_key)
652 {
653 if ((dir->fs_flags & FS_FAT) != 0)
654 {
655 dir->mtime = time ((time_t *) 0);
656 rehash = 1;
657 }
658 else if (stat (dir->path_key, &st) == 0 && st.st_mtime > dir->mtime)
659 {
660 /* reset date stamp to show most recent re-process. */
661 dir->mtime = st.st_mtime;
662 rehash = 1;
663 }
664
665 /* If it has been already read in, all done. */
666 if (!rehash)
667 return 0;
668
669 /* make sure directory can still be opened; if not return. */
670 dir->dirstream = opendir (dir->path_key);
671 if (!dir->dirstream)
672 return 0;
673 }
674 else
675#endif
676 /* The directory has been all read in. */
677 return 0;
678 }
679
680 while (1)
681 {
682 /* Enter the file in the hash table. */
683 unsigned int len;
684 struct dirfile dirfile_key;
685 struct dirfile **dirfile_slot;
686
687 ENULLLOOP (d, readdir (dir->dirstream));
688 if (d == 0)
689 {
690/* bird: Workaround for smbfs mounts returning EBADF at the end of the search.
691 To exactly determin the cause here, I should probably do some smbfs
692 tracing, but for now just ignoring the EBADF on seems to work.
693 (The smb server is 64-bit vista, btw.) */
694#if defined (__FreeBSD__)
695 struct statfs stfs;
696 int saved_errno = errno;
697 errno = 0;
698 if (saved_errno == EBADF
699 && !fstatfs (dirfd (dir->dirstream), &stfs)
700 && !(stfs.f_flags & MNT_LOCAL)
701 && !strcmp(stfs.f_fstypename, "smbfs"))
702 {
703 /*fprintf (stderr, "EBADF on remote fs! dirfd=%d errno=%d\n",
704 dirfd (dir->dirstream), errno);*/
705 saved_errno = 0;
706 }
707 errno = saved_errno;
708#endif
709/* bird: end */
710 if (errno)
711 fatal (NILF, "INTERNAL: readdir(%p): %s (filename=%s)\n", dir, strerror (errno), filename);
712 break;
713 }
714
715#if defined(VMS) && defined(HAVE_DIRENT_H)
716 /* In VMS we get file versions too, which have to be stripped off */
717 {
718 char *p = strrchr (d->d_name, ';');
719 if (p)
720 *p = '\0';
721 }
722#endif
723 if (!REAL_DIR_ENTRY (d))
724 continue;
725
726 len = NAMLEN (d);
727 dirfile_key.name = d->d_name;
728 dirfile_key.length = len;
729 dirfile_slot = (struct dirfile **) hash_find_slot (&dir->dirfiles, &dirfile_key);
730#ifdef WINDOWS32
731 /*
732 * If re-reading a directory, don't cache files that have
733 * already been discovered.
734 */
735 if (! rehash || HASH_VACANT (*dirfile_slot))
736#endif
737 {
738 df = xmalloc (sizeof (struct dirfile));
739 df->name = strcache_add_len (d->d_name, len);
740 df->length = len;
741 df->impossible = 0;
742 hash_insert_at (&dir->dirfiles, df, dirfile_slot);
743 }
744 /* Check if the name matches the one we're searching for. */
745 if (filename != 0 && strieq (d->d_name, filename))
746 return 1;
747 }
748
749 /* If the directory has been completely read in,
750 close the stream and reset the pointer to nil. */
751 if (d == 0)
752 {
753 --open_directories;
754 closedir (dir->dirstream);
755 dir->dirstream = 0;
756 }
757 return 0;
758}
759
760/* Return 1 if the name FILENAME in directory DIRNAME
761 is entered in the dir hash table.
762 FILENAME must contain no slashes. */
763
764int
765dir_file_exists_p (const char *dirname, const char *filename)
766{
767 return dir_contents_file_exists_p (find_directory (dirname)->contents,
768 filename);
769}
770
771
772/* Return 1 if the file named NAME exists. */
773
774int
775file_exists_p (const char *name)
776{
777 const char *dirend;
778 const char *dirname;
779 const char *slash;
780
781#ifndef NO_ARCHIVES
782 if (ar_name (name))
783 return ar_member_date (name) != (time_t) -1;
784#endif
785
786#ifdef VMS
787 dirend = strrchr (name, ']');
788 if (dirend == 0)
789 dirend = strrchr (name, ':');
790 if (dirend == 0)
791 return dir_file_exists_p ("[]", name);
792#else /* !VMS */
793 dirend = strrchr (name, '/');
794#ifdef HAVE_DOS_PATHS
795 /* Forward and backslashes might be mixed. We need the rightmost one. */
796 {
797 const char *bslash = strrchr(name, '\\');
798 if (!dirend || bslash > dirend)
799 dirend = bslash;
800 /* The case of "d:file". */
801 if (!dirend && name[0] && name[1] == ':')
802 dirend = name + 1;
803 }
804#endif /* HAVE_DOS_PATHS */
805 if (dirend == 0)
806#ifndef _AMIGA
807 return dir_file_exists_p (".", name);
808#else /* !VMS && !AMIGA */
809 return dir_file_exists_p ("", name);
810#endif /* AMIGA */
811#endif /* VMS */
812
813 slash = dirend;
814 if (dirend == name)
815 dirname = "/";
816 else
817 {
818 char *p;
819#ifdef HAVE_DOS_PATHS
820 /* d:/ and d: are *very* different... */
821 if (dirend < name + 3 && name[1] == ':' &&
822 (*dirend == '/' || *dirend == '\\' || *dirend == ':'))
823 dirend++;
824#endif
825 p = alloca (dirend - name + 1);
826 memcpy (p, name, dirend - name);
827 p[dirend - name] = '\0';
828 dirname = p;
829 }
830 return dir_file_exists_p (dirname, slash + 1);
831}
832
833
834/* Mark FILENAME as `impossible' for `file_impossible_p'.
835 This means an attempt has been made to search for FILENAME
836 as an intermediate file, and it has failed. */
837
838void
839file_impossible (const char *filename)
840{
841 const char *dirend;
842 const char *p = filename;
843 struct directory *dir;
844 struct dirfile *new;
845
846#ifdef VMS
847 dirend = strrchr (p, ']');
848 if (dirend == 0)
849 dirend = strrchr (p, ':');
850 dirend++;
851 if (dirend == (char *)1)
852 dir = find_directory ("[]");
853#else
854 dirend = strrchr (p, '/');
855# ifdef HAVE_DOS_PATHS
856 /* Forward and backslashes might be mixed. We need the rightmost one. */
857 {
858 const char *bslash = strrchr(p, '\\');
859 if (!dirend || bslash > dirend)
860 dirend = bslash;
861 /* The case of "d:file". */
862 if (!dirend && p[0] && p[1] == ':')
863 dirend = p + 1;
864 }
865# endif /* HAVE_DOS_PATHS */
866 if (dirend == 0)
867# ifdef _AMIGA
868 dir = find_directory ("");
869# else /* !VMS && !AMIGA */
870 dir = find_directory (".");
871# endif /* AMIGA */
872#endif /* VMS */
873 else
874 {
875 const char *dirname;
876 const char *slash = dirend;
877 if (dirend == p)
878 dirname = "/";
879 else
880 {
881 char *cp;
882#ifdef HAVE_DOS_PATHS
883 /* d:/ and d: are *very* different... */
884 if (dirend < p + 3 && p[1] == ':' &&
885 (*dirend == '/' || *dirend == '\\' || *dirend == ':'))
886 dirend++;
887#endif
888 cp = alloca (dirend - p + 1);
889 memcpy (cp, p, dirend - p);
890 cp[dirend - p] = '\0';
891 dirname = cp;
892 }
893 dir = find_directory (dirname);
894 filename = p = slash + 1;
895 }
896
897 if (dir->contents == 0)
898 {
899 /* The directory could not be stat'd. We allocate a contents
900 structure for it, but leave it out of the contents hash table. */
901 dir->contents = xmalloc (sizeof (struct directory_contents));
902 memset (dir->contents, '\0', sizeof (struct directory_contents));
903 }
904
905 if (dir->contents->dirfiles.ht_vec == 0)
906 {
907 hash_init (&dir->contents->dirfiles, DIRFILE_BUCKETS,
908 dirfile_hash_1, dirfile_hash_2, dirfile_hash_cmp);
909 }
910
911 /* Make a new entry and put it in the table. */
912
913 new = xmalloc (sizeof (struct dirfile));
914 new->length = strlen (filename);
915 new->name = strcache_add_len (filename, new->length);
916 new->impossible = 1;
917 hash_insert (&dir->contents->dirfiles, new);
918}
919
920
921/* Return nonzero if FILENAME has been marked impossible. */
922
923int
924file_impossible_p (const char *filename)
925{
926 const char *dirend;
927 const char *p = filename;
928 struct directory_contents *dir;
929 struct dirfile *dirfile;
930 struct dirfile dirfile_key;
931
932#ifdef VMS
933 dirend = strrchr (filename, ']');
934 if (dirend == 0)
935 dir = find_directory ("[]")->contents;
936#else
937 dirend = strrchr (filename, '/');
938#ifdef HAVE_DOS_PATHS
939 /* Forward and backslashes might be mixed. We need the rightmost one. */
940 {
941 const char *bslash = strrchr(filename, '\\');
942 if (!dirend || bslash > dirend)
943 dirend = bslash;
944 /* The case of "d:file". */
945 if (!dirend && filename[0] && filename[1] == ':')
946 dirend = filename + 1;
947 }
948#endif /* HAVE_DOS_PATHS */
949 if (dirend == 0)
950#ifdef _AMIGA
951 dir = find_directory ("")->contents;
952#else /* !VMS && !AMIGA */
953 dir = find_directory (".")->contents;
954#endif /* AMIGA */
955#endif /* VMS */
956 else
957 {
958 const char *dirname;
959 const char *slash = dirend;
960 if (dirend == filename)
961 dirname = "/";
962 else
963 {
964 char *cp;
965#ifdef HAVE_DOS_PATHS
966 /* d:/ and d: are *very* different... */
967 if (dirend < filename + 3 && filename[1] == ':' &&
968 (*dirend == '/' || *dirend == '\\' || *dirend == ':'))
969 dirend++;
970#endif
971 cp = alloca (dirend - filename + 1);
972 memcpy (cp, p, dirend - p);
973 cp[dirend - p] = '\0';
974 dirname = cp;
975 }
976 dir = find_directory (dirname)->contents;
977 p = filename = slash + 1;
978 }
979
980 if (dir == 0 || dir->dirfiles.ht_vec == 0)
981 /* There are no files entered for this directory. */
982 return 0;
983
984#ifdef __MSDOS__
985 filename = dosify (p);
986#endif
987#ifdef HAVE_CASE_INSENSITIVE_FS
988 filename = downcase (p);
989#endif
990#ifdef VMS
991 filename = vmsify (p, 1);
992#endif
993
994 dirfile_key.name = filename;
995 dirfile_key.length = strlen (filename);
996 dirfile = hash_find_item (&dir->dirfiles, &dirfile_key);
997 if (dirfile)
998 return dirfile->impossible;
999
1000 return 0;
1001}
1002
1003
1004/* Return the already allocated name in the
1005 directory hash table that matches DIR. */
1006
1007const char *
1008dir_name (const char *dir)
1009{
1010 return find_directory (dir)->name;
1011}
1012
1013
1014/* Print the data base of directories. */
1015
1016void
1017print_dir_data_base (void)
1018{
1019 unsigned int files;
1020 unsigned int impossible;
1021 struct directory **dir_slot;
1022 struct directory **dir_end;
1023
1024 puts (_("\n# Directories\n"));
1025
1026 files = impossible = 0;
1027
1028 dir_slot = (struct directory **) directories.ht_vec;
1029 dir_end = dir_slot + directories.ht_size;
1030 for ( ; dir_slot < dir_end; dir_slot++)
1031 {
1032 struct directory *dir = *dir_slot;
1033 if (! HASH_VACANT (dir))
1034 {
1035 if (dir->contents == 0)
1036 printf (_("# %s: could not be stat'd.\n"), dir->name);
1037 else if (dir->contents->dirfiles.ht_vec == 0)
1038 {
1039#ifdef WINDOWS32
1040 printf (_("# %s (key %s, mtime %d): could not be opened.\n"),
1041 dir->name, dir->contents->path_key,dir->contents->mtime);
1042#else /* WINDOWS32 */
1043#ifdef VMS
1044 printf (_("# %s (device %d, inode [%d,%d,%d]): could not be opened.\n"),
1045 dir->name, dir->contents->dev,
1046 dir->contents->ino[0], dir->contents->ino[1],
1047 dir->contents->ino[2]);
1048#else
1049 printf (_("# %s (device %ld, inode %ld): could not be opened.\n"),
1050 dir->name, (long int) dir->contents->dev,
1051 (long int) dir->contents->ino);
1052#endif
1053#endif /* WINDOWS32 */
1054 }
1055 else
1056 {
1057 unsigned int f = 0;
1058 unsigned int im = 0;
1059 struct dirfile **files_slot;
1060 struct dirfile **files_end;
1061
1062 files_slot = (struct dirfile **) dir->contents->dirfiles.ht_vec;
1063 files_end = files_slot + dir->contents->dirfiles.ht_size;
1064 for ( ; files_slot < files_end; files_slot++)
1065 {
1066 struct dirfile *df = *files_slot;
1067 if (! HASH_VACANT (df))
1068 {
1069 if (df->impossible)
1070 ++im;
1071 else
1072 ++f;
1073 }
1074 }
1075#ifdef WINDOWS32
1076 printf (_("# %s (key %s, mtime %d): "),
1077 dir->name, dir->contents->path_key, dir->contents->mtime);
1078#else /* WINDOWS32 */
1079#ifdef VMS
1080 printf (_("# %s (device %d, inode [%d,%d,%d]): "),
1081 dir->name, dir->contents->dev,
1082 dir->contents->ino[0], dir->contents->ino[1],
1083 dir->contents->ino[2]);
1084#else
1085 printf (_("# %s (device %ld, inode %ld): "),
1086 dir->name,
1087 (long)dir->contents->dev, (long)dir->contents->ino);
1088#endif
1089#endif /* WINDOWS32 */
1090 if (f == 0)
1091 fputs (_("No"), stdout);
1092 else
1093 printf ("%u", f);
1094 fputs (_(" files, "), stdout);
1095 if (im == 0)
1096 fputs (_("no"), stdout);
1097 else
1098 printf ("%u", im);
1099 fputs (_(" impossibilities"), stdout);
1100 if (dir->contents->dirstream == 0)
1101 puts (".");
1102 else
1103 puts (_(" so far."));
1104 files += f;
1105 impossible += im;
1106
1107#ifdef KMK
1108 fputs ("# ", stdout);
1109 hash_print_stats (&dir->contents->dirfiles, stdout);
1110 fputs ("\n", stdout);
1111#endif
1112 }
1113 }
1114 }
1115
1116 fputs ("\n# ", stdout);
1117 if (files == 0)
1118 fputs (_("No"), stdout);
1119 else
1120 printf ("%u", files);
1121 fputs (_(" files, "), stdout);
1122 if (impossible == 0)
1123 fputs (_("no"), stdout);
1124 else
1125 printf ("%u", impossible);
1126 printf (_(" impossibilities in %lu directories.\n"), directories.ht_fill);
1127#ifdef KMK
1128 fputs ("# directories: ", stdout);
1129 hash_print_stats (&directories, stdout);
1130 fputs ("\n# directory_contents: ", stdout);
1131 hash_print_stats (&directory_contents, stdout);
1132 fputs ("\n", stdout);
1133#endif
1134}
1135
1136
1137/* Hooks for globbing. */
1138
1139#include <glob.h>
1140
1141/* Structure describing state of iterating through a directory hash table. */
1142
1143struct dirstream
1144 {
1145 struct directory_contents *contents; /* The directory being read. */
1146 struct dirfile **dirfile_slot; /* Current slot in table. */
1147 };
1148
1149/* Forward declarations. */
1150static __ptr_t open_dirstream (const char *);
1151static struct dirent *read_dirstream (__ptr_t);
1152
1153static __ptr_t
1154open_dirstream (const char *directory)
1155{
1156 struct dirstream *new;
1157 struct directory *dir = find_directory (directory);
1158
1159 if (dir->contents == 0 || dir->contents->dirfiles.ht_vec == 0)
1160 /* DIR->contents is nil if the directory could not be stat'd.
1161 DIR->contents->dirfiles is nil if it could not be opened. */
1162 return 0;
1163
1164 /* Read all the contents of the directory now. There is no benefit
1165 in being lazy, since glob will want to see every file anyway. */
1166
1167 dir_contents_file_exists_p (dir->contents, 0);
1168
1169 new = xmalloc (sizeof (struct dirstream));
1170 new->contents = dir->contents;
1171 new->dirfile_slot = (struct dirfile **) new->contents->dirfiles.ht_vec;
1172
1173 return (__ptr_t) new;
1174}
1175
1176static struct dirent *
1177read_dirstream (__ptr_t stream)
1178{
1179 static char *buf;
1180 static unsigned int bufsz;
1181
1182 struct dirstream *const ds = (struct dirstream *) stream;
1183 struct directory_contents *dc = ds->contents;
1184 struct dirfile **dirfile_end = (struct dirfile **) dc->dirfiles.ht_vec + dc->dirfiles.ht_size;
1185
1186 while (ds->dirfile_slot < dirfile_end)
1187 {
1188 struct dirfile *df = *ds->dirfile_slot++;
1189 if (! HASH_VACANT (df) && !df->impossible)
1190 {
1191 /* The glob interface wants a `struct dirent', so mock one up. */
1192 struct dirent *d;
1193 unsigned int len = df->length + 1;
1194 unsigned int sz = sizeof (*d) - sizeof (d->d_name) + len;
1195 if (sz > bufsz)
1196 {
1197 bufsz *= 2;
1198 if (sz > bufsz)
1199 bufsz = sz;
1200 buf = xrealloc (buf, bufsz);
1201 }
1202 d = (struct dirent *) buf;
1203#ifdef __MINGW32__
1204# if __MINGW32_MAJOR_VERSION < 3 || (__MINGW32_MAJOR_VERSION == 3 && \
1205 __MINGW32_MINOR_VERSION == 0)
1206 d->d_name = xmalloc(len);
1207# endif
1208#endif
1209 FAKE_DIR_ENTRY (d);
1210#ifdef _DIRENT_HAVE_D_NAMLEN
1211 d->d_namlen = len - 1;
1212#endif
1213#ifdef _DIRENT_HAVE_D_TYPE
1214 d->d_type = DT_UNKNOWN;
1215#endif
1216 memcpy (d->d_name, df->name, len);
1217 return d;
1218 }
1219 }
1220
1221 return 0;
1222}
1223
1224static void
1225ansi_free (void *p)
1226{
1227 if (p)
1228 free(p);
1229}
1230
1231/* On 64 bit ReliantUNIX (5.44 and above) in LFS mode, stat() is actually a
1232 * macro for stat64(). If stat is a macro, make a local wrapper function to
1233 * invoke it.
1234 */
1235#ifndef stat
1236# ifndef VMS
1237int stat (const char *path, struct stat *sbuf);
1238# endif
1239# define local_stat stat
1240#else
1241static int
1242local_stat (const char *path, struct stat *buf)
1243{
1244 int e;
1245
1246 EINTRLOOP (e, stat (path, buf));
1247 return e;
1248}
1249#endif
1250
1251void
1252dir_setup_glob (glob_t *gl)
1253{
1254 gl->gl_opendir = open_dirstream;
1255 gl->gl_readdir = read_dirstream;
1256 gl->gl_closedir = ansi_free;
1257 gl->gl_stat = local_stat;
1258#ifdef __EMX__ /* The FreeBSD implemenation actually uses gl_lstat!! */
1259 gl->gl_lstat = local_stat;
1260#endif
1261 /* We don't bother setting gl_lstat, since glob never calls it.
1262 The slot is only there for compatibility with 4.4 BSD. */
1263}
1264
1265void
1266hash_init_directories (void)
1267{
1268 hash_init (&directories, DIRECTORY_BUCKETS,
1269 directory_hash_1, directory_hash_2, directory_hash_cmp);
1270 hash_init (&directory_contents, DIRECTORY_BUCKETS,
1271 directory_contents_hash_1, directory_contents_hash_2,
1272 directory_contents_hash_cmp);
1273}
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