VirtualBox

source: kBuild/trunk/src/gmake/dir.c@ 684

Last change on this file since 684 was 503, checked in by bird, 18 years ago

Untested merge with GNU Make v3.81 (vendor/gnumake/2005-05-16 -> vendor/gnumake/current).

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