.
[make.git] / dir.c
blobd87cb1c7da20631ac8eee022a4d6e7295a44189f
1 /* Directory hashing for GNU Make.
2 Copyright (C) 1988, 89, 91, 92, 93, 94, 95 Free Software Foundation, Inc.
3 This file is part of GNU Make.
5 GNU Make is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2, or (at your option)
8 any later version.
10 GNU Make is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with GNU Make; see the file COPYING. If not, write to
17 the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
19 #include "make.h"
21 #if defined (POSIX) || defined (HAVE_DIRENT_H) || defined (__GNU_LIBRARY__)
22 #include <dirent.h>
23 #ifndef __GNU_LIBRARY__
24 #define D_NAMLEN(d) strlen((d)->d_name)
25 #else /* GNU C library. */
26 #define D_NAMLEN(d) ((d)->d_namlen)
27 #endif /* Not GNU C library. */
28 #else /* Not POSIX or HAVE_DIRENT_H. */
29 #define direct dirent
30 #define D_NAMLEN(d) ((d)->d_namlen)
31 #ifdef HAVE_SYS_NDIR_H
32 #include <sys/ndir.h>
33 #endif /* HAVE_SYS_NDIR_H */
34 #ifdef HAVE_SYS_DIR_H
35 #include <sys/dir.h>
36 #endif /* HAVE_SYS_DIR_H */
37 #ifdef HAVE_NDIR_H
38 #include <ndir.h>
39 #endif /* HAVE_NDIR_H */
40 #endif /* POSIX or HAVE_DIRENT_H or __GNU_LIBRARY__. */
42 #if defined (POSIX) && !defined (__GNU_LIBRARY__)
43 /* Posix does not require that the d_ino field be present, and some
44 systems do not provide it. */
45 #define REAL_DIR_ENTRY(dp) 1
46 #else
47 #define REAL_DIR_ENTRY(dp) (dp->d_ino != 0)
48 #endif /* POSIX */
50 #ifdef __MSDOS__
51 #include <ctype.h>
53 static char *
54 dosify (filename)
55 char *filename;
57 static char dos_filename[14];
58 char *df;
59 int i;
61 if (filename == 0)
62 return 0;
64 if (strpbrk (filename, "\"*+,;<=>?[\\]|") != 0)
65 return filename;
67 df = dos_filename;
69 /* First, transform the name part. */
70 for (i = 0; *filename != '\0' && i < 8 && *filename != '.'; ++i)
71 *df++ = tolower (*filename++);
73 /* Now skip to the next dot. */
74 while (*filename != '\0' && *filename != '.')
75 ++filename;
76 if (*filename != '\0')
78 *df++ = *filename++;
79 for (i = 0; *filename != '\0' && i < 3 && *filename != '.'; ++i)
80 *df++ = tolower (*filename++);
83 /* Look for more dots. */
84 while (*filename != '\0' && *filename != '.')
85 ++filename;
86 if (*filename == '.')
87 return filename;
88 *df = 0;
89 return dos_filename;
91 #endif
93 /* Hash table of directories. */
95 #ifndef DIRECTORY_BUCKETS
96 #define DIRECTORY_BUCKETS 199
97 #endif
99 struct directory_contents
101 struct directory_contents *next;
103 int dev, ino; /* Device and inode numbers of this dir. */
105 struct dirfile **files; /* Files in this directory. */
106 DIR *dirstream; /* Stream reading this directory. */
109 /* Table of directory contents hashed by device and inode number. */
110 static struct directory_contents *directories_contents[DIRECTORY_BUCKETS];
112 struct directory
114 struct directory *next;
116 char *name; /* Name of the directory. */
118 /* The directory's contents. This data may be shared by several
119 entries in the hash table, which refer to the same directory
120 (identified uniquely by `dev' and `ino') under different names. */
121 struct directory_contents *contents;
124 /* Table of directories hashed by name. */
125 static struct directory *directories[DIRECTORY_BUCKETS];
128 /* Never have more than this many directories open at once. */
130 #define MAX_OPEN_DIRECTORIES 10
132 static unsigned int open_directories = 0;
135 /* Hash table of files in each directory. */
137 struct dirfile
139 struct dirfile *next;
140 char *name; /* Name of the file. */
141 char impossible; /* This file is impossible. */
144 #ifndef DIRFILE_BUCKETS
145 #define DIRFILE_BUCKETS 107
146 #endif
148 static int dir_contents_file_exists_p ();
150 /* Find the directory named NAME and return its `struct directory'. */
152 static struct directory *
153 find_directory (name)
154 register char *name;
156 register unsigned int hash = 0;
157 register char *p;
158 register struct directory *dir;
160 for (p = name; *p != '\0'; ++p)
161 HASH (hash, *p);
162 hash %= DIRECTORY_BUCKETS;
164 for (dir = directories[hash]; dir != 0; dir = dir->next)
165 if (streq (dir->name, name))
166 break;
168 if (dir == 0)
170 struct stat st;
172 /* The directory was not found. Create a new entry for it. */
174 dir = (struct directory *) xmalloc (sizeof (struct directory));
175 dir->next = directories[hash];
176 directories[hash] = dir;
177 dir->name = savestring (name, p - name);
179 /* The directory is not in the name hash table.
180 Find its device and inode numbers, and look it up by them. */
182 if (safe_stat (name, &st) < 0)
183 /* Couldn't stat the directory. Mark this by
184 setting the `contents' member to a nil pointer. */
185 dir->contents = 0;
186 else
188 /* Search the contents hash table; device and inode are the key. */
190 struct directory_contents *dc;
192 hash = ((unsigned int) st.st_dev << 16) | (unsigned int) st.st_ino;
193 hash %= DIRECTORY_BUCKETS;
195 for (dc = directories_contents[hash]; dc != 0; dc = dc->next)
196 if (dc->dev == st.st_dev && dc->ino == st.st_ino)
197 break;
199 if (dc == 0)
201 /* Nope; this really is a directory we haven't seen before. */
203 dc = (struct directory_contents *)
204 xmalloc (sizeof (struct directory_contents));
206 /* Enter it in the contents hash table. */
207 dc->dev = st.st_dev;
208 dc->ino = st.st_ino;
209 dc->next = directories_contents[hash];
210 directories_contents[hash] = dc;
212 dc->dirstream = opendir (name);
213 if (dc->dirstream == 0)
214 /* Couldn't open the directory. Mark this by
215 setting the `files' member to a nil pointer. */
216 dc->files = 0;
217 else
219 /* Allocate an array of buckets for files and zero it. */
220 dc->files = (struct dirfile **)
221 xmalloc (sizeof (struct dirfile *) * DIRFILE_BUCKETS);
222 bzero ((char *) dc->files,
223 sizeof (struct dirfile *) * DIRFILE_BUCKETS);
225 /* Keep track of how many directories are open. */
226 ++open_directories;
227 if (open_directories == MAX_OPEN_DIRECTORIES)
228 /* We have too many directories open already.
229 Read the entire directory and then close it. */
230 (void) dir_contents_file_exists_p (dc, (char *) 0);
234 /* Point the name-hashed entry for DIR at its contents data. */
235 dir->contents = dc;
239 return dir;
242 /* Return 1 if the name FILENAME is entered in DIR's hash table.
243 FILENAME must contain no slashes. */
245 static int
246 dir_contents_file_exists_p (dir, filename)
247 register struct directory_contents *dir;
248 register char *filename;
250 register unsigned int hash;
251 register char *p;
252 register struct dirfile *df;
253 register struct dirent *d;
255 if (dir == 0 || dir->files == 0)
256 /* The directory could not be stat'd or opened. */
257 return 0;
259 #ifdef __MSDOS__
260 filename = dosify (filename);
261 #endif
263 hash = 0;
264 if (filename != 0)
266 if (*filename == '\0')
267 /* Checking if the directory exists. */
268 return 1;
270 for (p = filename; *p != '\0'; ++p)
271 HASH (hash, *p);
272 hash %= DIRFILE_BUCKETS;
274 /* Search the list of hashed files. */
276 for (df = dir->files[hash]; df != 0; df = df->next)
277 if (streq (df->name, filename))
278 return !df->impossible;
281 /* The file was not found in the hashed list.
282 Try to read the directory further. */
284 if (dir->dirstream == 0)
285 /* The directory has been all read in. */
286 return 0;
288 while ((d = readdir (dir->dirstream)) != 0)
290 /* Enter the file in the hash table. */
291 register unsigned int newhash = 0;
292 unsigned int len;
293 register unsigned int i;
295 if (!REAL_DIR_ENTRY (d))
296 continue;
298 len = D_NAMLEN (d);
299 while (d->d_name[len - 1] == '\0')
300 --len;
302 for (i = 0; i < len; ++i)
303 HASH (newhash, d->d_name[i]);
304 newhash %= DIRFILE_BUCKETS;
306 df = (struct dirfile *) xmalloc (sizeof (struct dirfile));
307 df->next = dir->files[newhash];
308 dir->files[newhash] = df;
309 df->name = savestring (d->d_name, len);
310 df->impossible = 0;
312 /* Check if the name matches the one we're searching for. */
313 if (filename != 0
314 && newhash == hash && streq (d->d_name, filename))
315 return 1;
318 /* If the directory has been completely read in,
319 close the stream and reset the pointer to nil. */
320 if (d == 0)
322 --open_directories;
323 closedir (dir->dirstream);
324 dir->dirstream = 0;
327 return 0;
330 /* Return 1 if the name FILENAME in directory DIRNAME
331 is entered in the dir hash table.
332 FILENAME must contain no slashes. */
335 dir_file_exists_p (dirname, filename)
336 register char *dirname;
337 register char *filename;
339 return dir_contents_file_exists_p (find_directory (dirname)->contents,
340 filename);
343 /* Return 1 if the file named NAME exists. */
346 file_exists_p (name)
347 register char *name;
349 char *dirend;
350 char *dirname;
352 #ifndef NO_ARCHIVES
353 if (ar_name (name))
354 return ar_member_date (name) != (time_t) -1;
355 #endif
357 dirend = rindex (name, '/');
358 if (dirend == 0)
359 return dir_file_exists_p (".", name);
361 dirname = (char *) alloca (dirend - name + 1);
362 bcopy (name, dirname, dirend - name);
363 dirname[dirend - name] = '\0';
364 return dir_file_exists_p (dirname, dirend + 1);
367 /* Mark FILENAME as `impossible' for `file_impossible_p'.
368 This means an attempt has been made to search for FILENAME
369 as an intermediate file, and it has failed. */
371 void
372 file_impossible (filename)
373 register char *filename;
375 char *dirend;
376 register char *p = filename;
377 register unsigned int hash;
378 register struct directory *dir;
379 register struct dirfile *new;
381 dirend = rindex (p, '/');
382 if (dirend == 0)
383 dir = find_directory (".");
384 else
386 char *dirname = (char *) alloca (dirend - p + 1);
387 bcopy (p, dirname, dirend - p);
388 dirname[dirend - p] = '\0';
389 dir = find_directory (dirname);
390 filename = p = dirend + 1;
393 for (hash = 0; *p != '\0'; ++p)
394 HASH (hash, *p);
395 hash %= DIRFILE_BUCKETS;
397 if (dir->contents == 0)
399 /* The directory could not be stat'd. We allocate a contents
400 structure for it, but leave it out of the contents hash table. */
401 dir->contents = (struct directory_contents *)
402 xmalloc (sizeof (struct directory_contents));
403 dir->contents->dev = dir->contents->ino = 0;
404 dir->contents->files = 0;
405 dir->contents->dirstream = 0;
408 if (dir->contents->files == 0)
410 /* The directory was not opened; we must allocate the hash buckets. */
411 dir->contents->files = (struct dirfile **)
412 xmalloc (sizeof (struct dirfile) * DIRFILE_BUCKETS);
413 bzero ((char *) dir->contents->files,
414 sizeof (struct dirfile) * DIRFILE_BUCKETS);
417 /* Make a new entry and put it in the table. */
419 new = (struct dirfile *) xmalloc (sizeof (struct dirfile));
420 new->next = dir->contents->files[hash];
421 dir->contents->files[hash] = new;
422 new->name = savestring (filename, strlen (filename));
423 new->impossible = 1;
426 /* Return nonzero if FILENAME has been marked impossible. */
429 file_impossible_p (filename)
430 char *filename;
432 char *dirend;
433 register char *p = filename;
434 register unsigned int hash;
435 register struct directory_contents *dir;
436 register struct dirfile *next;
438 dirend = rindex (filename, '/');
439 if (dirend == 0)
440 dir = find_directory (".")->contents;
441 else
443 char *dirname = (char *) alloca (dirend - filename + 1);
444 bcopy (p, dirname, dirend - p);
445 dirname[dirend - p] = '\0';
446 dir = find_directory (dirname)->contents;
447 p = filename = dirend + 1;
450 if (dir == 0 || dir->files == 0)
451 /* There are no files entered for this directory. */
452 return 0;
454 #ifdef __MSDOS__
455 p = filename = dosify (p);
456 #endif
458 for (hash = 0; *p != '\0'; ++p)
459 HASH (hash, *p);
460 hash %= DIRFILE_BUCKETS;
462 for (next = dir->files[hash]; next != 0; next = next->next)
463 if (streq (filename, next->name))
464 return next->impossible;
466 return 0;
469 /* Return the already allocated name in the
470 directory hash table that matches DIR. */
472 char *
473 dir_name (dir)
474 char *dir;
476 return find_directory (dir)->name;
479 /* Print the data base of directories. */
481 void
482 print_dir_data_base ()
484 register unsigned int i, dirs, files, impossible;
485 register struct directory *dir;
487 puts ("\n# Directories\n");
489 dirs = files = impossible = 0;
490 for (i = 0; i < DIRECTORY_BUCKETS; ++i)
491 for (dir = directories[i]; dir != 0; dir = dir->next)
493 ++dirs;
494 if (dir->contents == 0)
495 printf ("# %s: could not be stat'd.\n", dir->name);
496 else if (dir->contents->files == 0)
497 printf ("# %s (device %d, inode %d): could not be opened.\n",
498 dir->name, dir->contents->dev, dir->contents->ino);
499 else
501 register unsigned int f = 0, im = 0;
502 register unsigned int j;
503 register struct dirfile *df;
504 for (j = 0; j < DIRFILE_BUCKETS; ++j)
505 for (df = dir->contents->files[j]; df != 0; df = df->next)
506 if (df->impossible)
507 ++im;
508 else
509 ++f;
510 printf ("# %s (device %d, inode %d): ",
511 dir->name, dir->contents->dev, dir->contents->ino);
512 if (f == 0)
513 fputs ("No", stdout);
514 else
515 printf ("%u", f);
516 fputs (" files, ", stdout);
517 if (im == 0)
518 fputs ("no", stdout);
519 else
520 printf ("%u", im);
521 fputs (" impossibilities", stdout);
522 if (dir->contents->dirstream == 0)
523 puts (".");
524 else
525 puts (" so far.");
526 files += f;
527 impossible += im;
531 fputs ("\n# ", stdout);
532 if (files == 0)
533 fputs ("No", stdout);
534 else
535 printf ("%u", files);
536 fputs (" files, ", stdout);
537 if (impossible == 0)
538 fputs ("no", stdout);
539 else
540 printf ("%u", impossible);
541 printf (" impossibilities in %u directories.\n", dirs);
544 /* Hooks for globbing. */
546 #include <glob.h>
548 /* Structure describing state of iterating through a directory hash table. */
550 struct dirstream
552 struct directory_contents *contents; /* The directory being read. */
554 unsigned int bucket; /* Current hash bucket. */
555 struct dirfile *elt; /* Current elt in bucket. */
558 /* Forward declarations. */
559 static __ptr_t open_dirstream __P ((const char *));
560 static const char *read_dirstream __P ((__ptr_t));
562 static __ptr_t
563 open_dirstream (directory)
564 const char *directory;
566 struct dirstream *new;
567 struct directory *dir = find_directory (directory);
569 if (dir->contents == 0 || dir->contents->files == 0)
570 /* DIR->contents is nil if the directory could not be stat'd.
571 DIR->contents->files is nil if it could not be opened. */
572 return 0;
574 /* Read all the contents of the directory now. There is no benefit
575 in being lazy, since glob will want to see every file anyway. */
577 (void) dir_contents_file_exists_p (dir->contents, (char *) 0);
579 new = (struct dirstream *) xmalloc (sizeof (struct dirstream));
580 new->contents = dir->contents;
581 new->bucket = 0;
582 new->elt = new->contents->files[0];
584 return (__ptr_t) new;
587 static const char *
588 read_dirstream (stream)
589 __ptr_t stream;
591 struct dirstream *const ds = (struct dirstream *) stream;
592 register struct dirfile *df;
594 while (ds->bucket < DIRFILE_BUCKETS)
596 while ((df = ds->elt) != 0)
598 ds->elt = df->next;
599 if (!df->impossible)
600 return df->name;
602 if (++ds->bucket == DIRFILE_BUCKETS)
603 break;
604 ds->elt = ds->contents->files[ds->bucket];
607 return 0;
610 void
611 init_dir ()
613 __glob_opendir_hook = open_dirstream;
614 __glob_readdir_hook = read_dirstream;
615 __glob_closedir_hook = (void (*) __P ((__ptr_t stream))) free;