VirtualBox

source: kBuild/vendor/gnumake/2008-10-28/dir.c

Last change on this file was 1989, checked in by bird, 16 years ago

Load gnumake-2008-10-28-CVS into vendor/gnumake/current.

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