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