VirtualBox

source: kBuild/trunk/src/gmakenew/dir.c@ 944

Last change on this file since 944 was 903, checked in by bird, 18 years ago

Merged with the 2007-05-23 CVS. Added rsort and fixed a couple of windows build issues.

  • 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 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
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 WINDOWS32
461 /* Remove any trailing '\'. Windows32 stat fails even on valid
462 directories if they end in '\'. */
463 if (p[-1] == '\\')
464 ((char *)p)[-1] = '\0';
465#endif
466
467#ifdef VMS
468 r = vmsstat_dir (name, &st);
469#else
470 EINTRLOOP (r, stat (name, &st));
471#endif
472
473#ifdef WINDOWS32
474 /* Put back the trailing '\'. If we don't, we're permanently
475 truncating the value! */
476 if (p[-1] == '\0')
477 ((char *)p)[-1] = '\\';
478#endif
479
480 if (r < 0)
481 {
482 /* Couldn't stat the directory. Mark this by
483 setting the `contents' member to a nil pointer. */
484 dir->contents = 0;
485 }
486 else
487 {
488 /* Search the contents hash table; device and inode are the key. */
489
490 struct directory_contents *dc;
491 struct directory_contents **dc_slot;
492 struct directory_contents dc_key;
493
494 dc_key.dev = st.st_dev;
495#ifdef WINDOWS32
496 dc_key.path_key = w32_path = w32ify (name, 1);
497 dc_key.ctime = st.st_ctime;
498#else
499# ifdef VMS
500 dc_key.ino[0] = st.st_ino[0];
501 dc_key.ino[1] = st.st_ino[1];
502 dc_key.ino[2] = st.st_ino[2];
503# else
504 dc_key.ino = st.st_ino;
505# endif
506#endif
507 dc_slot = (struct directory_contents **) hash_find_slot (&directory_contents, &dc_key);
508 dc = *dc_slot;
509
510 if (HASH_VACANT (dc))
511 {
512 /* Nope; this really is a directory we haven't seen before. */
513
514 dc = (struct directory_contents *)
515 xmalloc (sizeof (struct directory_contents));
516
517 /* Enter it in the contents hash table. */
518 dc->dev = st.st_dev;
519#ifdef WINDOWS32
520 dc->path_key = xstrdup (w32_path);
521 dc->ctime = st.st_ctime;
522 dc->mtime = st.st_mtime;
523
524 /*
525 * NTFS is the only WINDOWS32 filesystem that bumps mtime
526 * on a directory when files are added/deleted from
527 * a directory.
528 */
529 w32_path[3] = '\0';
530 if (GetVolumeInformation(w32_path,
531 fs_label, sizeof (fs_label),
532 &fs_serno, &fs_len,
533 &fs_flags, fs_type, sizeof (fs_type)) == FALSE)
534 dc->fs_flags = FS_UNKNOWN;
535 else if (!strcmp(fs_type, "FAT"))
536 dc->fs_flags = FS_FAT;
537 else if (!strcmp(fs_type, "NTFS"))
538 dc->fs_flags = FS_NTFS;
539 else
540 dc->fs_flags = FS_UNKNOWN;
541#else
542# ifdef VMS
543 dc->ino[0] = st.st_ino[0];
544 dc->ino[1] = st.st_ino[1];
545 dc->ino[2] = st.st_ino[2];
546# else
547 dc->ino = st.st_ino;
548# endif
549#endif /* WINDOWS32 */
550 hash_insert_at (&directory_contents, dc, dc_slot);
551 ENULLLOOP (dc->dirstream, opendir (name));
552 if (dc->dirstream == 0)
553 /* Couldn't open the directory. Mark this by setting the
554 `files' member to a nil pointer. */
555 dc->dirfiles.ht_vec = 0;
556 else
557 {
558 hash_init (&dc->dirfiles, DIRFILE_BUCKETS,
559 dirfile_hash_1, dirfile_hash_2, dirfile_hash_cmp);
560 /* Keep track of how many directories are open. */
561 ++open_directories;
562 if (open_directories == MAX_OPEN_DIRECTORIES)
563 /* We have too many directories open already.
564 Read the entire directory and then close it. */
565 dir_contents_file_exists_p (dc, 0);
566 }
567 }
568
569 /* Point the name-hashed entry for DIR at its contents data. */
570 dir->contents = dc;
571 }
572 }
573
574 return dir;
575}
576
577
578/* Return 1 if the name FILENAME is entered in DIR's hash table.
579 FILENAME must contain no slashes. */
580
581static int
582dir_contents_file_exists_p (struct directory_contents *dir,
583 const char *filename)
584{
585 unsigned int hash;
586 struct dirfile *df;
587 struct dirent *d;
588#ifdef WINDOWS32
589 struct stat st;
590 int rehash = 0;
591#endif
592
593 if (dir == 0 || dir->dirfiles.ht_vec == 0)
594 /* The directory could not be stat'd or opened. */
595 return 0;
596
597#ifdef __MSDOS__
598 filename = dosify (filename);
599#endif
600
601#ifdef HAVE_CASE_INSENSITIVE_FS
602 filename = downcase (filename);
603#endif
604
605#ifdef __EMX__
606 if (filename != 0)
607 _fnlwr (filename); /* lower case for FAT drives */
608#endif
609
610#ifdef VMS
611 filename = vmsify (filename,0);
612#endif
613
614 hash = 0;
615 if (filename != 0)
616 {
617 struct dirfile dirfile_key;
618
619 if (*filename == '\0')
620 {
621 /* Checking if the directory exists. */
622 return 1;
623 }
624 dirfile_key.name = filename;
625 dirfile_key.length = strlen (filename);
626 df = hash_find_item (&dir->dirfiles, &dirfile_key);
627 if (df)
628 return !df->impossible;
629 }
630
631 /* The file was not found in the hashed list.
632 Try to read the directory further. */
633
634 if (dir->dirstream == 0)
635 {
636#ifdef WINDOWS32
637 /*
638 * Check to see if directory has changed since last read. FAT
639 * filesystems force a rehash always as mtime does not change
640 * on directories (ugh!).
641 */
642 if (dir->path_key)
643 {
644 if ((dir->fs_flags & FS_FAT) != 0)
645 {
646 dir->mtime = time ((time_t *) 0);
647 rehash = 1;
648 }
649 else if (stat (dir->path_key, &st) == 0 && st.st_mtime > dir->mtime)
650 {
651 /* reset date stamp to show most recent re-process. */
652 dir->mtime = st.st_mtime;
653 rehash = 1;
654 }
655
656 /* If it has been already read in, all done. */
657 if (!rehash)
658 return 0;
659
660 /* make sure directory can still be opened; if not return. */
661 dir->dirstream = opendir (dir->path_key);
662 if (!dir->dirstream)
663 return 0;
664 }
665 else
666#endif
667 /* The directory has been all read in. */
668 return 0;
669 }
670
671 while (1)
672 {
673 /* Enter the file in the hash table. */
674 unsigned int len;
675 struct dirfile dirfile_key;
676 struct dirfile **dirfile_slot;
677
678 ENULLLOOP (d, readdir (dir->dirstream));
679 if (d == 0)
680 {
681 if (errno)
682 fatal (NILF, "INTERNAL: readdir: %s\n", strerror (errno));
683 break;
684 }
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 = xmalloc (sizeof (struct dirfile));
710 df->name = strcache_add_len (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 return 1;
718 }
719
720 /* If the directory has been completely read in,
721 close the stream and reset the pointer to nil. */
722 if (d == 0)
723 {
724 --open_directories;
725 closedir (dir->dirstream);
726 dir->dirstream = 0;
727 }
728 return 0;
729}
730
731/* Return 1 if the name FILENAME in directory DIRNAME
732 is entered in the dir hash table.
733 FILENAME must contain no slashes. */
734
735int
736dir_file_exists_p (const char *dirname, const char *filename)
737{
738 return dir_contents_file_exists_p (find_directory (dirname)->contents,
739 filename);
740}
741
742
743/* Return 1 if the file named NAME exists. */
744
745int
746file_exists_p (const char *name)
747{
748 const char *dirend;
749 const char *dirname;
750 const char *slash;
751
752#ifndef NO_ARCHIVES
753 if (ar_name (name))
754 return ar_member_date (name) != (time_t) -1;
755#endif
756
757#ifdef VMS
758 dirend = strrchr (name, ']');
759 if (dirend == 0)
760 dirend = strrchr (name, ':');
761 if (dirend == 0)
762 return dir_file_exists_p ("[]", name);
763#else /* !VMS */
764 dirend = strrchr (name, '/');
765#ifdef HAVE_DOS_PATHS
766 /* Forward and backslashes might be mixed. We need the rightmost one. */
767 {
768 const char *bslash = strrchr(name, '\\');
769 if (!dirend || bslash > dirend)
770 dirend = bslash;
771 /* The case of "d:file". */
772 if (!dirend && name[0] && name[1] == ':')
773 dirend = name + 1;
774 }
775#endif /* HAVE_DOS_PATHS */
776 if (dirend == 0)
777#ifndef _AMIGA
778 return dir_file_exists_p (".", name);
779#else /* !VMS && !AMIGA */
780 return dir_file_exists_p ("", name);
781#endif /* AMIGA */
782#endif /* VMS */
783
784 slash = dirend;
785 if (dirend == name)
786 dirname = "/";
787 else
788 {
789 char *p;
790#ifdef HAVE_DOS_PATHS
791 /* d:/ and d: are *very* different... */
792 if (dirend < name + 3 && name[1] == ':' &&
793 (*dirend == '/' || *dirend == '\\' || *dirend == ':'))
794 dirend++;
795#endif
796 p = alloca (dirend - name + 1);
797 memcpy (p, name, dirend - name);
798 p[dirend - name] = '\0';
799 dirname = p;
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 (const char *filename)
811{
812 const char *dirend;
813 const char *p = filename;
814 struct directory *dir;
815 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 const 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 const char *dirname;
847 const char *slash = dirend;
848 if (dirend == p)
849 dirname = "/";
850 else
851 {
852 char *cp;
853#ifdef HAVE_DOS_PATHS
854 /* d:/ and d: are *very* different... */
855 if (dirend < p + 3 && p[1] == ':' &&
856 (*dirend == '/' || *dirend == '\\' || *dirend == ':'))
857 dirend++;
858#endif
859 cp = alloca (dirend - p + 1);
860 memcpy (cp, p, dirend - p);
861 cp[dirend - p] = '\0';
862 dirname = cp;
863 }
864 dir = find_directory (dirname);
865 filename = p = slash + 1;
866 }
867
868 if (dir->contents == 0)
869 {
870 /* The directory could not be stat'd. We allocate a contents
871 structure for it, but leave it out of the contents hash table. */
872 dir->contents = xmalloc (sizeof (struct directory_contents));
873 memset (dir->contents, '\0', sizeof (struct directory_contents));
874 }
875
876 if (dir->contents->dirfiles.ht_vec == 0)
877 {
878 hash_init (&dir->contents->dirfiles, DIRFILE_BUCKETS,
879 dirfile_hash_1, dirfile_hash_2, dirfile_hash_cmp);
880 }
881
882 /* Make a new entry and put it in the table. */
883
884 new = xmalloc (sizeof (struct dirfile));
885 new->length = strlen (filename);
886 new->name = strcache_add_len (filename, new->length);
887 new->impossible = 1;
888 hash_insert (&dir->contents->dirfiles, new);
889}
890
891
892/* Return nonzero if FILENAME has been marked impossible. */
893
894int
895file_impossible_p (const char *filename)
896{
897 const char *dirend;
898 const char *p = filename;
899 struct directory_contents *dir;
900 struct dirfile *dirfile;
901 struct dirfile dirfile_key;
902
903#ifdef VMS
904 dirend = strrchr (filename, ']');
905 if (dirend == 0)
906 dir = find_directory ("[]")->contents;
907#else
908 dirend = strrchr (filename, '/');
909#ifdef HAVE_DOS_PATHS
910 /* Forward and backslashes might be mixed. We need the rightmost one. */
911 {
912 const char *bslash = strrchr(filename, '\\');
913 if (!dirend || bslash > dirend)
914 dirend = bslash;
915 /* The case of "d:file". */
916 if (!dirend && filename[0] && filename[1] == ':')
917 dirend = filename + 1;
918 }
919#endif /* HAVE_DOS_PATHS */
920 if (dirend == 0)
921#ifdef _AMIGA
922 dir = find_directory ("")->contents;
923#else /* !VMS && !AMIGA */
924 dir = find_directory (".")->contents;
925#endif /* AMIGA */
926#endif /* VMS */
927 else
928 {
929 const char *dirname;
930 const char *slash = dirend;
931 if (dirend == filename)
932 dirname = "/";
933 else
934 {
935 char *cp;
936#ifdef HAVE_DOS_PATHS
937 /* d:/ and d: are *very* different... */
938 if (dirend < filename + 3 && filename[1] == ':' &&
939 (*dirend == '/' || *dirend == '\\' || *dirend == ':'))
940 dirend++;
941#endif
942 cp = alloca (dirend - filename + 1);
943 memcpy (cp, p, dirend - p);
944 cp[dirend - p] = '\0';
945 dirname = cp;
946 }
947 dir = find_directory (dirname)->contents;
948 p = filename = slash + 1;
949 }
950
951 if (dir == 0 || dir->dirfiles.ht_vec == 0)
952 /* There are no files entered for this directory. */
953 return 0;
954
955#ifdef __MSDOS__
956 filename = dosify (p);
957#endif
958#ifdef HAVE_CASE_INSENSITIVE_FS
959 filename = downcase (p);
960#endif
961#ifdef VMS
962 filename = vmsify (p, 1);
963#endif
964
965 dirfile_key.name = filename;
966 dirfile_key.length = strlen (filename);
967 dirfile = hash_find_item (&dir->dirfiles, &dirfile_key);
968 if (dirfile)
969 return dirfile->impossible;
970
971 return 0;
972}
973
974
975/* Return the already allocated name in the
976 directory hash table that matches DIR. */
977
978const char *
979dir_name (const char *dir)
980{
981 return find_directory (dir)->name;
982}
983
984
985/* Print the data base of directories. */
986
987void
988print_dir_data_base (void)
989{
990 unsigned int files;
991 unsigned int impossible;
992 struct directory **dir_slot;
993 struct directory **dir_end;
994
995 puts (_("\n# Directories\n"));
996
997 files = impossible = 0;
998
999 dir_slot = (struct directory **) directories.ht_vec;
1000 dir_end = dir_slot + directories.ht_size;
1001 for ( ; dir_slot < dir_end; dir_slot++)
1002 {
1003 struct directory *dir = *dir_slot;
1004 if (! HASH_VACANT (dir))
1005 {
1006 if (dir->contents == 0)
1007 printf (_("# %s: could not be stat'd.\n"), dir->name);
1008 else if (dir->contents->dirfiles.ht_vec == 0)
1009 {
1010#ifdef WINDOWS32
1011 printf (_("# %s (key %s, mtime %d): could not be opened.\n"),
1012 dir->name, dir->contents->path_key,dir->contents->mtime);
1013#else /* WINDOWS32 */
1014#ifdef VMS
1015 printf (_("# %s (device %d, inode [%d,%d,%d]): could not be opened.\n"),
1016 dir->name, dir->contents->dev,
1017 dir->contents->ino[0], dir->contents->ino[1],
1018 dir->contents->ino[2]);
1019#else
1020 printf (_("# %s (device %ld, inode %ld): could not be opened.\n"),
1021 dir->name, (long int) dir->contents->dev,
1022 (long int) dir->contents->ino);
1023#endif
1024#endif /* WINDOWS32 */
1025 }
1026 else
1027 {
1028 unsigned int f = 0;
1029 unsigned int im = 0;
1030 struct dirfile **files_slot;
1031 struct dirfile **files_end;
1032
1033 files_slot = (struct dirfile **) dir->contents->dirfiles.ht_vec;
1034 files_end = files_slot + dir->contents->dirfiles.ht_size;
1035 for ( ; files_slot < files_end; files_slot++)
1036 {
1037 struct dirfile *df = *files_slot;
1038 if (! HASH_VACANT (df))
1039 {
1040 if (df->impossible)
1041 ++im;
1042 else
1043 ++f;
1044 }
1045 }
1046#ifdef WINDOWS32
1047 printf (_("# %s (key %s, mtime %d): "),
1048 dir->name, dir->contents->path_key, dir->contents->mtime);
1049#else /* WINDOWS32 */
1050#ifdef VMS
1051 printf (_("# %s (device %d, inode [%d,%d,%d]): "),
1052 dir->name, dir->contents->dev,
1053 dir->contents->ino[0], dir->contents->ino[1],
1054 dir->contents->ino[2]);
1055#else
1056 printf (_("# %s (device %ld, inode %ld): "),
1057 dir->name,
1058 (long)dir->contents->dev, (long)dir->contents->ino);
1059#endif
1060#endif /* WINDOWS32 */
1061 if (f == 0)
1062 fputs (_("No"), stdout);
1063 else
1064 printf ("%u", f);
1065 fputs (_(" files, "), stdout);
1066 if (im == 0)
1067 fputs (_("no"), stdout);
1068 else
1069 printf ("%u", im);
1070 fputs (_(" impossibilities"), stdout);
1071 if (dir->contents->dirstream == 0)
1072 puts (".");
1073 else
1074 puts (_(" so far."));
1075 files += f;
1076 impossible += im;
1077 }
1078 }
1079 }
1080
1081 fputs ("\n# ", stdout);
1082 if (files == 0)
1083 fputs (_("No"), stdout);
1084 else
1085 printf ("%u", files);
1086 fputs (_(" files, "), stdout);
1087 if (impossible == 0)
1088 fputs (_("no"), stdout);
1089 else
1090 printf ("%u", impossible);
1091 printf (_(" impossibilities in %lu directories.\n"), directories.ht_fill);
1092}
1093
1094
1095/* Hooks for globbing. */
1096
1097#include <glob.h>
1098
1099/* Structure describing state of iterating through a directory hash table. */
1100
1101struct dirstream
1102 {
1103 struct directory_contents *contents; /* The directory being read. */
1104 struct dirfile **dirfile_slot; /* Current slot in table. */
1105 };
1106
1107/* Forward declarations. */
1108static __ptr_t open_dirstream (const char *);
1109static struct dirent *read_dirstream (__ptr_t);
1110
1111static __ptr_t
1112open_dirstream (const char *directory)
1113{
1114 struct dirstream *new;
1115 struct directory *dir = find_directory (directory);
1116
1117 if (dir->contents == 0 || dir->contents->dirfiles.ht_vec == 0)
1118 /* DIR->contents is nil if the directory could not be stat'd.
1119 DIR->contents->dirfiles is nil if it could not be opened. */
1120 return 0;
1121
1122 /* Read all the contents of the directory now. There is no benefit
1123 in being lazy, since glob will want to see every file anyway. */
1124
1125 dir_contents_file_exists_p (dir->contents, 0);
1126
1127 new = xmalloc (sizeof (struct dirstream));
1128 new->contents = dir->contents;
1129 new->dirfile_slot = (struct dirfile **) new->contents->dirfiles.ht_vec;
1130
1131 return (__ptr_t) new;
1132}
1133
1134static struct dirent *
1135read_dirstream (__ptr_t stream)
1136{
1137 static char *buf;
1138 static unsigned int bufsz;
1139
1140 struct dirstream *const ds = (struct dirstream *) stream;
1141 struct directory_contents *dc = ds->contents;
1142 struct dirfile **dirfile_end = (struct dirfile **) dc->dirfiles.ht_vec + dc->dirfiles.ht_size;
1143
1144 while (ds->dirfile_slot < dirfile_end)
1145 {
1146 struct dirfile *df = *ds->dirfile_slot++;
1147 if (! HASH_VACANT (df) && !df->impossible)
1148 {
1149 /* The glob interface wants a `struct dirent', so mock one up. */
1150 struct dirent *d;
1151 unsigned int len = df->length + 1;
1152 unsigned int sz = sizeof (*d) - sizeof (d->d_name) + len;
1153 if (sz > bufsz)
1154 {
1155 bufsz *= 2;
1156 if (sz > bufsz)
1157 bufsz = sz;
1158 buf = xrealloc (buf, bufsz);
1159 }
1160 d = (struct dirent *) buf;
1161#ifdef __MINGW32__
1162# if __MINGW32_MAJOR_VERSION < 3 || (__MINGW32_MAJOR_VERSION == 3 && \
1163 __MINGW32_MINOR_VERSION == 0)
1164 d->d_name = xmalloc(len);
1165# endif
1166#endif
1167 FAKE_DIR_ENTRY (d);
1168#ifdef _DIRENT_HAVE_D_NAMLEN
1169 d->d_namlen = len - 1;
1170#endif
1171#ifdef _DIRENT_HAVE_D_TYPE
1172 d->d_type = DT_UNKNOWN;
1173#endif
1174 memcpy (d->d_name, df->name, len);
1175 return d;
1176 }
1177 }
1178
1179 return 0;
1180}
1181
1182static void
1183ansi_free (void *p)
1184{
1185 if (p)
1186 free(p);
1187}
1188
1189/* On 64 bit ReliantUNIX (5.44 and above) in LFS mode, stat() is actually a
1190 * macro for stat64(). If stat is a macro, make a local wrapper function to
1191 * invoke it.
1192 */
1193#ifndef stat
1194# ifndef VMS
1195int stat (const char *path, struct stat *sbuf);
1196# endif
1197# define local_stat stat
1198#else
1199static int
1200local_stat (const char *path, struct stat *buf)
1201{
1202 int e;
1203
1204 EINTRLOOP (e, stat (path, buf));
1205 return e;
1206}
1207#endif
1208
1209void
1210dir_setup_glob (glob_t *gl)
1211{
1212 gl->gl_opendir = open_dirstream;
1213 gl->gl_readdir = read_dirstream;
1214 gl->gl_closedir = ansi_free;
1215 gl->gl_stat = local_stat;
1216#ifdef __EMX__ /* The FreeBSD implemenation actually uses gl_lstat!! */
1217 gl->gl_lstat = local_stat;
1218#endif
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