VirtualBox

source: kBuild/vendor/gnumake/2005-05-16/dir.c@ 1989

Last change on this file since 1989 was 280, checked in by bird, 20 years ago

Current make snaphot, 2005-05-16.

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