1 /* Directory hashing for GNU Make.
2 Copyright (C) 1988-2020 Free Software Foundation, Inc.
3 This file is part of GNU Make.
4
5 GNU Make is free software; you can redistribute it and/or modify it under the
6 terms of the GNU General Public License as published by the Free Software
7 Foundation; either version 3 of the License, or (at your option) any later
8 version.
9
10 GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
11 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12 A PARTICULAR PURPOSE. See the GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License along with
15 this program. If not, see <http://www.gnu.org/licenses/>. */
16
17 #include "makeint.h"
18 #include "hash.h"
19 #include "filedef.h"
20 #include "dep.h"
21
22 #ifdef HAVE_DIRENT_H
23 # include <dirent.h>
24 # define NAMLEN(dirent) strlen((dirent)->d_name)
25 #else
26 # define dirent direct
27 # define NAMLEN(dirent) (dirent)->d_namlen
28 # ifdef HAVE_SYS_NDIR_H
29 # include <sys/ndir.h>
30 # endif
31 # ifdef HAVE_SYS_DIR_H
32 # include <sys/dir.h>
33 # endif
34 # ifdef HAVE_NDIR_H
35 # include <ndir.h>
36 # endif
37 #endif
38
39 /* In GNU systems, <dirent.h> defines this macro for us. */
40 #ifdef _D_NAMLEN
41 # undef NAMLEN
42 # define NAMLEN(d) _D_NAMLEN(d)
43 #endif
44
45 #if defined (POSIX) && !defined (__GNU_LIBRARY__)
46 /* Posix does not require that the d_ino field be present, and some
47 systems do not provide it. */
48 # define REAL_DIR_ENTRY(dp) 1
49 # define FAKE_DIR_ENTRY(dp)
50 #else
51 # define REAL_DIR_ENTRY(dp) (dp->d_ino != 0)
52 # define FAKE_DIR_ENTRY(dp) (dp->d_ino = 1)
53 #endif /* POSIX */
54
55 #ifdef __MSDOS__
56 #include <ctype.h>
57 #include <fcntl.h>
58
59 /* If it's MSDOS that doesn't have _USE_LFN, disable LFN support. */
60 #ifndef _USE_LFN
61 #define _USE_LFN 0
62 #endif
63
64 static const char *
dosify(const char * filename)65 dosify (const char *filename)
66 {
67 static char dos_filename[14];
68 char *df;
69 int i;
70
71 if (filename == 0 || _USE_LFN)
72 return filename;
73
74 /* FIXME: what about filenames which violate
75 8+3 constraints, like "config.h.in", or ".emacs"? */
76 if (strpbrk (filename, "\"*+,;<=>?[\\]|") != 0)
77 return filename;
78
79 df = dos_filename;
80
81 /* First, transform the name part. */
82 for (i = 0; i < 8 && ! STOP_SET (*filename, MAP_DOT|MAP_NUL); ++i)
83 *df++ = tolower ((unsigned char)*filename++);
84
85 /* Now skip to the next dot. */
86 while (! STOP_SET (*filename, MAP_DOT|MAP_NUL))
87 ++filename;
88 if (*filename != '\0')
89 {
90 *df++ = *filename++;
91 for (i = 0; i < 3 && ! STOP_SET (*filename, MAP_DOT|MAP_NUL); ++i)
92 *df++ = tolower ((unsigned char)*filename++);
93 }
94
95 /* Look for more dots. */
96 while (! STOP_SET (*filename, MAP_DOT|MAP_NUL))
97 ++filename;
98 if (*filename == '.')
99 return filename;
100 *df = 0;
101 return dos_filename;
102 }
103 #endif /* __MSDOS__ */
104
105 #ifdef WINDOWS32
106 #include "pathstuff.h"
107 #endif
108
109 #ifdef HAVE_CASE_INSENSITIVE_FS
110 static const char *
downcase(const char * filename)111 downcase (const char *filename)
112 {
113 static PATH_VAR (new_filename);
114 char *df;
115
116 if (filename == 0)
117 return 0;
118
119 df = new_filename;
120 while (*filename != '\0')
121 {
122 *df++ = tolower ((unsigned char)*filename);
123 ++filename;
124 }
125
126 *df = 0;
127
128 return new_filename;
129 }
130 #endif /* HAVE_CASE_INSENSITIVE_FS */
131
132 #ifdef VMS
133
134 static char *
downcase_inplace(char * filename)135 downcase_inplace(char *filename)
136 {
137 char *name;
138 name = filename;
139 while (*name != '\0')
140 {
141 *name = tolower ((unsigned char)*name);
142 ++name;
143 }
144 return filename;
145 }
146
147 #ifndef _USE_STD_STAT
148 /* VMS 8.2 fixed the VMS stat output to have unique st_dev and st_ino
149 when _USE_STD_STAT is used on the compile line.
150
151 Prior to _USE_STD_STAT support, the st_dev is a pointer to thread
152 static memory containing the device of the last filename looked up.
153
154 Todo: find out if the ino_t still needs to be faked on a directory.
155 */
156
157 /* Define this if the older VMS_INO_T is needed */
158 #define VMS_INO_T 1
159
160 static int
vms_hash(const char * name)161 vms_hash (const char *name)
162 {
163 int h = 0;
164
165 while (*name)
166 {
167 unsigned char uc = *name;
168 int g;
169 #ifdef HAVE_CASE_INSENSITIVE_FS
170 h = (h << 4) + (isupper (uc) ? tolower (uc) : uc);
171 #else
172 h = (h << 4) + uc;
173 #endif
174 name++;
175 g = h & 0xf0000000;
176 if (g)
177 {
178 h = h ^ (g >> 24);
179 h = h ^ g;
180 }
181 }
182 return h;
183 }
184
185 /* fake stat entry for a directory */
186 static int
vmsstat_dir(const char * name,struct stat * st)187 vmsstat_dir (const char *name, struct stat *st)
188 {
189 char *s;
190 int h;
191 DIR *dir;
192
193 dir = opendir (name);
194 if (dir == 0)
195 return -1;
196 closedir (dir);
197 s = strchr (name, ':'); /* find device */
198 if (s)
199 {
200 /* to keep the compiler happy we said "const char *name", now we cheat */
201 *s++ = 0;
202 st->st_dev = (char *)vms_hash (name);
203 h = vms_hash (s);
204 *(s-1) = ':';
205 }
206 else
207 {
208 st->st_dev = 0;
209 h = vms_hash (name);
210 }
211
212 st->st_ino[0] = h & 0xff;
213 st->st_ino[1] = h & 0xff00;
214 st->st_ino[2] = h >> 16;
215
216 return 0;
217 }
218
219 # define stat(__path, __sbuf) vmsstat_dir (__path, __sbuf)
220
221 #endif /* _USE_STD_STAT */
222 #endif /* VMS */
223
224 /* Hash table of directories. */
225
226 #ifndef DIRECTORY_BUCKETS
227 #define DIRECTORY_BUCKETS 199
228 #endif
229
230 struct directory_contents
231 {
232 dev_t dev; /* Device and inode numbers of this dir. */
233 #ifdef WINDOWS32
234 /* Inode means nothing on WINDOWS32. Even file key information is
235 * unreliable because it is random per file open and undefined for remote
236 * filesystems. The most unique attribute I can come up with is the fully
237 * qualified name of the directory. Beware though, this is also
238 * unreliable. I'm open to suggestion on a better way to emulate inode. */
239 char *path_key;
240 time_t ctime;
241 time_t mtime; /* controls check for stale directory cache */
242 int fs_flags; /* FS_FAT, FS_NTFS, ... */
243 # define FS_FAT 0x1
244 # define FS_NTFS 0x2
245 # define FS_UNKNOWN 0x4
246 #else
247 # ifdef VMS_INO_T
248 ino_t ino[3];
249 # else
250 ino_t ino;
251 # endif
252 #endif /* WINDOWS32 */
253 struct hash_table dirfiles; /* Files in this directory. */
254 DIR *dirstream; /* Stream reading this directory. */
255 };
256
257 static unsigned long
directory_contents_hash_1(const void * key_0)258 directory_contents_hash_1 (const void *key_0)
259 {
260 const struct directory_contents *key = key_0;
261 unsigned long hash;
262
263 #ifdef WINDOWS32
264 hash = 0;
265 ISTRING_HASH_1 (key->path_key, hash);
266 hash ^= ((unsigned int) key->dev << 4) ^ (unsigned int) key->ctime;
267 #else
268 # ifdef VMS_INO_T
269 hash = (((unsigned int) key->dev << 4)
270 ^ ((unsigned int) key->ino[0]
271 + (unsigned int) key->ino[1]
272 + (unsigned int) key->ino[2]));
273 # else
274 hash = ((unsigned int) key->dev << 4) ^ (unsigned int) key->ino;
275 # endif
276 #endif /* WINDOWS32 */
277 return hash;
278 }
279
280 static unsigned long
directory_contents_hash_2(const void * key_0)281 directory_contents_hash_2 (const void *key_0)
282 {
283 const struct directory_contents *key = key_0;
284 unsigned long hash;
285
286 #ifdef WINDOWS32
287 hash = 0;
288 ISTRING_HASH_2 (key->path_key, hash);
289 hash ^= ((unsigned int) key->dev << 4) ^ (unsigned int) ~key->ctime;
290 #else
291 # ifdef VMS_INO_T
292 hash = (((unsigned int) key->dev << 4)
293 ^ ~((unsigned int) key->ino[0]
294 + (unsigned int) key->ino[1]
295 + (unsigned int) key->ino[2]));
296 # else
297 hash = ((unsigned int) key->dev << 4) ^ (unsigned int) ~key->ino;
298 # endif
299 #endif /* WINDOWS32 */
300
301 return hash;
302 }
303
304 /* Sometimes it's OK to use subtraction to get this value:
305 result = X - Y;
306 But, if we're not sure of the type of X and Y they may be too large for an
307 int (on a 64-bit system for example). So, use ?: instead.
308 See Savannah bug #15534.
309
310 NOTE! This macro has side-effects!
311 */
312
313 #define MAKECMP(_x,_y) ((_x)<(_y)?-1:((_x)==(_y)?0:1))
314
315 static int
directory_contents_hash_cmp(const void * xv,const void * yv)316 directory_contents_hash_cmp (const void *xv, const void *yv)
317 {
318 const struct directory_contents *x = xv;
319 const struct directory_contents *y = yv;
320 int result;
321
322 result = MAKECMP(x->ino, y->ino);
323 if (result)
324 return result;
325
326 return MAKECMP(x->dev, y->dev);
327 }
328
329 /* Table of directory contents hashed by device and inode number. */
330 static struct hash_table directory_contents;
331
332 struct directory
333 {
334 const char *name; /* Name of the directory. */
335
336 /* The directory's contents. This data may be shared by several
337 entries in the hash table, which refer to the same directory
338 (identified uniquely by 'dev' and 'ino') under different names. */
339 struct directory_contents *contents;
340 };
341
342 static unsigned long
directory_hash_1(const void * key)343 directory_hash_1 (const void *key)
344 {
345 return_ISTRING_HASH_1 (((const struct directory *) key)->name);
346 }
347
348 static unsigned long
directory_hash_2(const void * key)349 directory_hash_2 (const void *key)
350 {
351 return_ISTRING_HASH_2 (((const struct directory *) key)->name);
352 }
353
354 static int
directory_hash_cmp(const void * x,const void * y)355 directory_hash_cmp (const void *x, const void *y)
356 {
357 return_ISTRING_COMPARE (((const struct directory *) x)->name,
358 ((const struct directory *) y)->name);
359 }
360
361 /* Table of directories hashed by name. */
362 static struct hash_table directories;
363
364 /* Never have more than this many directories open at once. */
365
366 #define MAX_OPEN_DIRECTORIES 10
367
368 static unsigned int open_directories = 0;
369
370
371 /* Hash table of files in each directory. */
372
373 struct dirfile
374 {
375 const char *name; /* Name of the file. */
376 size_t length;
377 short impossible; /* This file is impossible. */
378 unsigned char type;
379 };
380
381 static unsigned long
dirfile_hash_1(const void * key)382 dirfile_hash_1 (const void *key)
383 {
384 return_ISTRING_HASH_1 (((struct dirfile const *) key)->name);
385 }
386
387 static unsigned long
dirfile_hash_2(const void * key)388 dirfile_hash_2 (const void *key)
389 {
390 return_ISTRING_HASH_2 (((struct dirfile const *) key)->name);
391 }
392
393 static int
dirfile_hash_cmp(const void * xv,const void * yv)394 dirfile_hash_cmp (const void *xv, const void *yv)
395 {
396 const struct dirfile *x = xv;
397 const struct dirfile *y = yv;
398 int result = (int) (x->length - y->length);
399 if (result)
400 return result;
401 return_ISTRING_COMPARE (x->name, y->name);
402 }
403
404 #ifndef DIRFILE_BUCKETS
405 #define DIRFILE_BUCKETS 107
406 #endif
407
408 static int dir_contents_file_exists_p (struct directory_contents *dir,
409 const char *filename);
410 static struct directory *find_directory (const char *name);
411
412 /* Find the directory named NAME and return its 'struct directory'. */
413
414 static struct directory *
find_directory(const char * name)415 find_directory (const char *name)
416 {
417 struct directory *dir;
418 struct directory **dir_slot;
419 struct directory dir_key;
420
421 dir_key.name = name;
422 dir_slot = (struct directory **) hash_find_slot (&directories, &dir_key);
423 dir = *dir_slot;
424
425 if (HASH_VACANT (dir))
426 {
427 /* The directory was not found. Create a new entry for it. */
428 const char *p = name + strlen (name);
429 struct stat st;
430 int r;
431
432 dir = xmalloc (sizeof (struct directory));
433 dir->name = strcache_add_len (name, p - name);
434 hash_insert_at (&directories, dir, dir_slot);
435 /* The directory is not in the name hash table.
436 Find its device and inode numbers, and look it up by them. */
437
438 EINTRLOOP (r, stat (name, &st));
439
440 if (r < 0)
441 {
442 /* Couldn't stat the directory. Mark this by
443 setting the 'contents' member to a nil pointer. */
444 dir->contents = 0;
445 }
446 else
447 {
448 /* Search the contents hash table; device and inode are the key. */
449
450 struct directory_contents *dc;
451 struct directory_contents **dc_slot;
452 struct directory_contents dc_key;
453
454 dc_key.dev = st.st_dev;
455 dc_key.ino = st.st_ino;
456 dc_slot = (struct directory_contents **) hash_find_slot (&directory_contents, &dc_key);
457 dc = *dc_slot;
458
459 if (HASH_VACANT (dc))
460 {
461 /* Nope; this really is a directory we haven't seen before. */
462 dc = (struct directory_contents *)
463 xmalloc (sizeof (struct directory_contents));
464
465 /* Enter it in the contents hash table. */
466 dc->dev = st.st_dev;
467 dc->ino = st.st_ino;
468 hash_insert_at (&directory_contents, dc, dc_slot);
469 ENULLLOOP (dc->dirstream, opendir (name));
470 if (dc->dirstream == 0)
471 /* Couldn't open the directory. Mark this by setting the
472 'files' member to a nil pointer. */
473 dc->dirfiles.ht_vec = 0;
474 else
475 {
476 hash_init (&dc->dirfiles, DIRFILE_BUCKETS,
477 dirfile_hash_1, dirfile_hash_2, dirfile_hash_cmp);
478 /* Keep track of how many directories are open. */
479 ++open_directories;
480 if (open_directories == MAX_OPEN_DIRECTORIES)
481 /* We have too many directories open already.
482 Read the entire directory and then close it. */
483 dir_contents_file_exists_p (dc, 0);
484 }
485 }
486
487 /* Point the name-hashed entry for DIR at its contents data. */
488 dir->contents = dc;
489 }
490 }
491
492 return dir;
493 }
494
495 /* Return 1 if the name FILENAME is entered in DIR's hash table.
496 FILENAME must contain no slashes. */
497
498 static int
dir_contents_file_exists_p(struct directory_contents * dir,const char * filename)499 dir_contents_file_exists_p (struct directory_contents *dir,
500 const char *filename)
501 {
502 struct dirfile *df;
503 struct dirent *d;
504
505 if (dir == 0 || dir->dirfiles.ht_vec == 0)
506 /* The directory could not be stat'd or opened. */
507 return 0;
508
509 #ifdef HAVE_CASE_INSENSITIVE_FS
510 filename = downcase (filename);
511 #endif
512
513 if (filename != 0)
514 {
515 struct dirfile dirfile_key;
516
517 if (*filename == '\0')
518 {
519 /* Checking if the directory exists. */
520 return 1;
521 }
522 dirfile_key.name = filename;
523 dirfile_key.length = strlen (filename);
524 df = hash_find_item (&dir->dirfiles, &dirfile_key);
525 if (df)
526 return !df->impossible;
527 }
528
529 /* The file was not found in the hashed list.
530 Try to read the directory further. */
531
532 if (dir->dirstream == 0)
533 {
534 /* The directory has been all read in. */
535 return 0;
536 }
537
538 while (1)
539 {
540 /* Enter the file in the hash table. */
541 size_t len;
542 struct dirfile dirfile_key;
543 struct dirfile **dirfile_slot;
544
545 ENULLLOOP (d, readdir (dir->dirstream));
546 if (d == 0)
547 {
548 if (errno)
549 pfatal_with_name ("INTERNAL: readdir");
550 break;
551 }
552
553 if (!REAL_DIR_ENTRY (d))
554 continue;
555
556 len = NAMLEN (d);
557 dirfile_key.name = d->d_name;
558 dirfile_key.length = len;
559 dirfile_slot = (struct dirfile **) hash_find_slot (&dir->dirfiles, &dirfile_key);
560 {
561 df = xmalloc (sizeof (struct dirfile));
562 #if defined(HAVE_CASE_INSENSITIVE_FS) && defined(VMS)
563 /* TODO: Why is this only needed on VMS? */
564 df->name = strcache_add_len (downcase_inplace (d->d_name), len);
565 #else
566 df->name = strcache_add_len (d->d_name, len);
567 #endif
568 #ifdef HAVE_STRUCT_DIRENT_D_TYPE
569 df->type = d->d_type;
570 #endif
571 df->length = len;
572 df->impossible = 0;
573 hash_insert_at (&dir->dirfiles, df, dirfile_slot);
574 }
575 /* Check if the name matches the one we're searching for. */
576 if (filename != 0 && patheq (d->d_name, filename))
577 return 1;
578 }
579
580 /* If the directory has been completely read in,
581 close the stream and reset the pointer to nil. */
582 if (d == 0)
583 {
584 --open_directories;
585 closedir (dir->dirstream);
586 dir->dirstream = 0;
587 }
588 return 0;
589 }
590
591 /* Return 1 if the name FILENAME in directory DIRNAME
592 is entered in the dir hash table.
593 FILENAME must contain no slashes. */
594
595 int
dir_file_exists_p(const char * dirname,const char * filename)596 dir_file_exists_p (const char *dirname, const char *filename)
597 {
598 return dir_contents_file_exists_p (find_directory (dirname)->contents,
599 filename);
600 }
601
602 /* Return 1 if the file named NAME exists. */
603
604 int
file_exists_p(const char * name)605 file_exists_p (const char *name)
606 {
607 const char *dirend;
608 const char *dirname;
609 const char *slash;
610
611 #ifndef NO_ARCHIVES
612 {
613 time_t member_date;
614 if (ar_name (name))
615 return ar_member_date (name, &member_date);
616 }
617 #endif
618
619 dirend = strrchr (name, '/');
620 if (dirend == 0)
621 return dir_file_exists_p (".", name);
622
623 slash = dirend;
624 if (dirend == name)
625 dirname = "/";
626 else
627 {
628 char *p;
629 p = alloca (dirend - name + 1);
630 memcpy (p, name, dirend - name);
631 p[dirend - name] = '\0';
632 dirname = p;
633 }
634 slash++;
635 return dir_file_exists_p (dirname, slash);
636 }
637
638 /* Mark FILENAME as 'impossible' for 'file_impossible_p'.
639 This means an attempt has been made to search for FILENAME
640 as an intermediate file, and it has failed. */
641
642 void
file_impossible(const char * filename)643 file_impossible (const char *filename)
644 {
645 const char *dirend;
646 const char *p = filename;
647 struct directory *dir;
648 struct dirfile *new;
649
650 dirend = strrchr (p, '/');
651 if (dirend == 0)
652 dir = find_directory (".");
653 else
654 {
655 const char *dirname;
656 const char *slash = dirend;
657 if (dirend == p)
658 dirname = "/";
659 else
660 {
661 char *cp;
662 cp = alloca (dirend - p + 1);
663 memcpy (cp, p, dirend - p);
664 cp[dirend - p] = '\0';
665 dirname = cp;
666 }
667 dir = find_directory (dirname);
668 filename = p = slash + 1;
669 }
670
671 if (dir->contents == 0)
672 /* The directory could not be stat'd. We allocate a contents
673 structure for it, but leave it out of the contents hash table. */
674 dir->contents = xcalloc (sizeof (struct directory_contents));
675
676 if (dir->contents->dirfiles.ht_vec == 0)
677 {
678 hash_init (&dir->contents->dirfiles, DIRFILE_BUCKETS,
679 dirfile_hash_1, dirfile_hash_2, dirfile_hash_cmp);
680 }
681
682 /* Make a new entry and put it in the table. */
683
684 new = xmalloc (sizeof (struct dirfile));
685 new->length = strlen (filename);
686 new->name = strcache_add_len (filename, new->length);
687 new->impossible = 1;
688 hash_insert (&dir->contents->dirfiles, new);
689 }
690
691 /* Return nonzero if FILENAME has been marked impossible. */
692
693 int
file_impossible_p(const char * filename)694 file_impossible_p (const char *filename)
695 {
696 const char *dirend;
697 struct directory_contents *dir;
698 struct dirfile *dirfile;
699 struct dirfile dirfile_key;
700
701 dirend = strrchr (filename, '/');
702 if (dirend == 0)
703 dir = find_directory (".")->contents;
704 else
705 {
706 const char *dirname;
707 const char *slash = dirend;
708 if (dirend == filename)
709 dirname = "/";
710 else
711 {
712 char *cp;
713 cp = alloca (dirend - filename + 1);
714 memcpy (cp, filename, dirend - filename);
715 cp[dirend - filename] = '\0';
716 dirname = cp;
717 }
718 dir = find_directory (dirname)->contents;
719 filename = slash + 1;
720 }
721
722 if (dir == 0 || dir->dirfiles.ht_vec == 0)
723 /* There are no files entered for this directory. */
724 return 0;
725
726 #ifdef HAVE_CASE_INSENSITIVE_FS
727 filename = downcase (filename);
728 #endif
729
730 dirfile_key.name = filename;
731 dirfile_key.length = strlen (filename);
732 dirfile = hash_find_item (&dir->dirfiles, &dirfile_key);
733 if (dirfile)
734 return dirfile->impossible;
735
736 return 0;
737 }
738
739 /* Return the already allocated name in the
740 directory hash table that matches DIR. */
741
742 const char *
dir_name(const char * dir)743 dir_name (const char *dir)
744 {
745 return find_directory (dir)->name;
746 }
747
748 /* Print the data base of directories. */
749
750 void
print_dir_data_base(void)751 print_dir_data_base (void)
752 {
753 unsigned int files;
754 unsigned int impossible;
755 struct directory **dir_slot;
756 struct directory **dir_end;
757
758 puts (_("\n# Directories\n"));
759
760 files = impossible = 0;
761
762 dir_slot = (struct directory **) directories.ht_vec;
763 dir_end = dir_slot + directories.ht_size;
764 for ( ; dir_slot < dir_end; dir_slot++)
765 {
766 struct directory *dir = *dir_slot;
767 if (! HASH_VACANT (dir))
768 {
769 if (dir->contents == 0)
770 printf (_("# %s: could not be stat'd.\n"), dir->name);
771 else if (dir->contents->dirfiles.ht_vec == 0)
772 {
773 printf (_("# %s (device %ld, inode %ld): could not be opened.\n"),
774 dir->name, (long int) dir->contents->dev,
775 (long int) dir->contents->ino);
776 }
777 else
778 {
779 unsigned int f = 0;
780 unsigned int im = 0;
781 struct dirfile **files_slot;
782 struct dirfile **files_end;
783
784 files_slot = (struct dirfile **) dir->contents->dirfiles.ht_vec;
785 files_end = files_slot + dir->contents->dirfiles.ht_size;
786 for ( ; files_slot < files_end; files_slot++)
787 {
788 struct dirfile *df = *files_slot;
789 if (! HASH_VACANT (df))
790 {
791 if (df->impossible)
792 ++im;
793 else
794 ++f;
795 }
796 }
797 printf (_("# %s (device %ld, inode %ld): "),
798 dir->name,
799 (long)dir->contents->dev, (long)dir->contents->ino);
800 if (f == 0)
801 fputs (_("No"), stdout);
802 else
803 printf ("%u", f);
804 fputs (_(" files, "), stdout);
805 if (im == 0)
806 fputs (_("no"), stdout);
807 else
808 printf ("%u", im);
809 fputs (_(" impossibilities"), stdout);
810 if (dir->contents->dirstream == 0)
811 puts (".");
812 else
813 puts (_(" so far."));
814 files += f;
815 impossible += im;
816 }
817 }
818 }
819
820 fputs ("\n# ", stdout);
821 if (files == 0)
822 fputs (_("No"), stdout);
823 else
824 printf ("%u", files);
825 fputs (_(" files, "), stdout);
826 if (impossible == 0)
827 fputs (_("no"), stdout);
828 else
829 printf ("%u", impossible);
830 printf (_(" impossibilities in %lu directories.\n"), directories.ht_fill);
831 }
832
833 /* Hooks for globbing. */
834
835 #include <glob.h>
836
837 /* Structure describing state of iterating through a directory hash table. */
838
839 struct dirstream
840 {
841 struct directory_contents *contents; /* The directory being read. */
842 struct dirfile **dirfile_slot; /* Current slot in table. */
843 };
844
845 /* Forward declarations. */
846 static __ptr_t open_dirstream (const char *);
847 static struct dirent *read_dirstream (__ptr_t);
848
849 static __ptr_t
open_dirstream(const char * directory)850 open_dirstream (const char *directory)
851 {
852 struct dirstream *new;
853 struct directory *dir = find_directory (directory);
854
855 if (dir->contents == 0 || dir->contents->dirfiles.ht_vec == 0)
856 /* DIR->contents is nil if the directory could not be stat'd.
857 DIR->contents->dirfiles is nil if it could not be opened. */
858 return 0;
859
860 /* Read all the contents of the directory now. There is no benefit
861 in being lazy, since glob will want to see every file anyway. */
862
863 dir_contents_file_exists_p (dir->contents, 0);
864
865 new = xmalloc (sizeof (struct dirstream));
866 new->contents = dir->contents;
867 new->dirfile_slot = (struct dirfile **) new->contents->dirfiles.ht_vec;
868
869 return (__ptr_t) new;
870 }
871
872 static struct dirent *
read_dirstream(__ptr_t stream)873 read_dirstream (__ptr_t stream)
874 {
875 static char *buf;
876 static size_t bufsz;
877
878 struct dirstream *const ds = (struct dirstream *) stream;
879 struct directory_contents *dc = ds->contents;
880 struct dirfile **dirfile_end = (struct dirfile **) dc->dirfiles.ht_vec + dc->dirfiles.ht_size;
881
882 while (ds->dirfile_slot < dirfile_end)
883 {
884 struct dirfile *df = *ds->dirfile_slot++;
885 if (! HASH_VACANT (df) && !df->impossible)
886 {
887 /* The glob interface wants a 'struct dirent', so mock one up. */
888 struct dirent *d;
889 size_t len = df->length + 1;
890 size_t sz = sizeof (*d) - sizeof (d->d_name) + len;
891 if (sz > bufsz)
892 {
893 bufsz *= 2;
894 if (sz > bufsz)
895 bufsz = sz;
896 buf = xrealloc (buf, bufsz);
897 }
898 d = (struct dirent *) buf;
899 #ifdef __MINGW32__
900 # if __MINGW32_MAJOR_VERSION < 3 || (__MINGW32_MAJOR_VERSION == 3 && \
901 __MINGW32_MINOR_VERSION == 0)
902 d->d_name = xmalloc (len);
903 # endif
904 #endif
905 FAKE_DIR_ENTRY (d);
906 #ifdef _DIRENT_HAVE_D_NAMLEN
907 d->d_namlen = len - 1;
908 #endif
909 #ifdef HAVE_STRUCT_DIRENT_D_TYPE
910 d->d_type = df->type;
911 #endif
912 memcpy (d->d_name, df->name, len);
913 return d;
914 }
915 }
916
917 return 0;
918 }
919
920 /* On 64 bit ReliantUNIX (5.44 and above) in LFS mode, stat() is actually a
921 * macro for stat64(). If stat is a macro, make a local wrapper function to
922 * invoke it.
923 *
924 * On MS-Windows, stat() "succeeds" for foo/bar/. where foo/bar is a
925 * regular file; fix that here.
926 */
927 #if !defined(stat) && !defined(WINDOWS32) || defined(VMS)
928 # ifndef VMS
929 # ifndef HAVE_SYS_STAT_H
930 int stat (const char *path, struct stat *sbuf);
931 # endif
932 # else
933 /* We are done with the fake stat. Go back to the real stat */
934 # ifdef stat
935 # undef stat
936 # endif
937 # endif
938 # define local_stat stat
939 #else
940 static int
local_stat(const char * path,struct stat * buf)941 local_stat (const char *path, struct stat *buf)
942 {
943 int e;
944 EINTRLOOP (e, stat (path, buf));
945 return e;
946 }
947 #endif
948
949 /* Similarly for lstat. */
950 #if !defined(lstat) && !defined(WINDOWS32) || defined(VMS)
951 # ifndef VMS
952 # ifndef HAVE_SYS_STAT_H
953 int lstat (const char *path, struct stat *sbuf);
954 # endif
955 # else
956 /* We are done with the fake lstat. Go back to the real lstat */
957 # ifdef lstat
958 # undef lstat
959 # endif
960 # endif
961 # define local_lstat lstat
962 #elif defined(WINDOWS32)
963 /* Windows doesn't support lstat(). */
964 # define local_lstat local_stat
965 #else
966 static int
local_lstat(const char * path,struct stat * buf)967 local_lstat (const char *path, struct stat *buf)
968 {
969 int e;
970 EINTRLOOP (e, lstat (path, buf));
971 return e;
972 }
973 #endif
974
975 void
dir_setup_glob(glob_t * gl)976 dir_setup_glob (glob_t *gl)
977 {
978 gl->gl_offs = 0;
979 gl->gl_opendir = open_dirstream;
980 gl->gl_readdir = read_dirstream;
981 gl->gl_closedir = free;
982 gl->gl_lstat = local_lstat;
983 gl->gl_stat = local_stat;
984 }
985
986 void
hash_init_directories(void)987 hash_init_directories (void)
988 {
989 hash_init (&directories, DIRECTORY_BUCKETS,
990 directory_hash_1, directory_hash_2, directory_hash_cmp);
991 hash_init (&directory_contents, DIRECTORY_BUCKETS,
992 directory_contents_hash_1, directory_contents_hash_2,
993 directory_contents_hash_cmp);
994 }
995