xref: /netbsd-src/usr.bin/make/dir.c (revision 2a399c6883d870daece976daec6ffa7bb7f934ce)
1 /*	$NetBSD: dir.c,v 1.20 1997/09/28 03:31:02 lukem Exp $	*/
2 
3 /*
4  * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
5  * Copyright (c) 1988, 1989 by Adam de Boor
6  * Copyright (c) 1989 by Berkeley Softworks
7  * All rights reserved.
8  *
9  * This code is derived from software contributed to Berkeley by
10  * Adam de Boor.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  * 3. All advertising materials mentioning features or use of this software
21  *    must display the following acknowledgement:
22  *	This product includes software developed by the University of
23  *	California, Berkeley and its contributors.
24  * 4. Neither the name of the University nor the names of its contributors
25  *    may be used to endorse or promote products derived from this software
26  *    without specific prior written permission.
27  *
28  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38  * SUCH DAMAGE.
39  */
40 
41 #ifdef MAKE_BOOTSTRAP
42 static char rcsid[] = "$NetBSD: dir.c,v 1.20 1997/09/28 03:31:02 lukem Exp $";
43 #else
44 #include <sys/cdefs.h>
45 #ifndef lint
46 #if 0
47 static char sccsid[] = "@(#)dir.c	8.2 (Berkeley) 1/2/94";
48 #else
49 __RCSID("$NetBSD: dir.c,v 1.20 1997/09/28 03:31:02 lukem Exp $");
50 #endif
51 #endif /* not lint */
52 #endif
53 
54 /*-
55  * dir.c --
56  *	Directory searching using wildcards and/or normal names...
57  *	Used both for source wildcarding in the Makefile and for finding
58  *	implicit sources.
59  *
60  * The interface for this module is:
61  *	Dir_Init  	    Initialize the module.
62  *
63  *	Dir_End  	    Cleanup the module.
64  *
65  *	Dir_HasWildcards    Returns TRUE if the name given it needs to
66  *	    	  	    be wildcard-expanded.
67  *
68  *	Dir_Expand	    Given a pattern and a path, return a Lst of names
69  *	    	  	    which match the pattern on the search path.
70  *
71  *	Dir_FindFile	    Searches for a file on a given search path.
72  *	    	  	    If it exists, the entire path is returned.
73  *	    	  	    Otherwise NULL is returned.
74  *
75  *	Dir_MTime 	    Return the modification time of a node. The file
76  *	    	  	    is searched for along the default search path.
77  *	    	  	    The path and mtime fields of the node are filled
78  *	    	  	    in.
79  *
80  *	Dir_AddDir	    Add a directory to a search path.
81  *
82  *	Dir_MakeFlags	    Given a search path and a command flag, create
83  *	    	  	    a string with each of the directories in the path
84  *	    	  	    preceded by the command flag and all of them
85  *	    	  	    separated by a space.
86  *
87  *	Dir_Destroy	    Destroy an element of a search path. Frees up all
88  *	    	  	    things that can be freed for the element as long
89  *	    	  	    as the element is no longer referenced by any other
90  *	    	  	    search path.
91  *	Dir_ClearPath	    Resets a search path to the empty list.
92  *
93  * For debugging:
94  *	Dir_PrintDirectories	Print stats about the directory cache.
95  */
96 
97 #include <stdio.h>
98 #include <sys/types.h>
99 #include <dirent.h>
100 #include <sys/stat.h>
101 #include "make.h"
102 #include "hash.h"
103 #include "dir.h"
104 
105 /*
106  *	A search path consists of a Lst of Path structures. A Path structure
107  *	has in it the name of the directory and a hash table of all the files
108  *	in the directory. This is used to cut down on the number of system
109  *	calls necessary to find implicit dependents and their like. Since
110  *	these searches are made before any actions are taken, we need not
111  *	worry about the directory changing due to creation commands. If this
112  *	hampers the style of some makefiles, they must be changed.
113  *
114  *	A list of all previously-read directories is kept in the
115  *	openDirectories Lst. This list is checked first before a directory
116  *	is opened.
117  *
118  *	The need for the caching of whole directories is brought about by
119  *	the multi-level transformation code in suff.c, which tends to search
120  *	for far more files than regular make does. In the initial
121  *	implementation, the amount of time spent performing "stat" calls was
122  *	truly astronomical. The problem with hashing at the start is,
123  *	of course, that pmake doesn't then detect changes to these directories
124  *	during the course of the make. Three possibilities suggest themselves:
125  *
126  *	    1) just use stat to test for a file's existence. As mentioned
127  *	       above, this is very inefficient due to the number of checks
128  *	       engendered by the multi-level transformation code.
129  *	    2) use readdir() and company to search the directories, keeping
130  *	       them open between checks. I have tried this and while it
131  *	       didn't slow down the process too much, it could severely
132  *	       affect the amount of parallelism available as each directory
133  *	       open would take another file descriptor out of play for
134  *	       handling I/O for another job. Given that it is only recently
135  *	       that UNIX OS's have taken to allowing more than 20 or 32
136  *	       file descriptors for a process, this doesn't seem acceptable
137  *	       to me.
138  *	    3) record the mtime of the directory in the Path structure and
139  *	       verify the directory hasn't changed since the contents were
140  *	       hashed. This will catch the creation or deletion of files,
141  *	       but not the updating of files. However, since it is the
142  *	       creation and deletion that is the problem, this could be
143  *	       a good thing to do. Unfortunately, if the directory (say ".")
144  *	       were fairly large and changed fairly frequently, the constant
145  *	       rehashing could seriously degrade performance. It might be
146  *	       good in such cases to keep track of the number of rehashes
147  *	       and if the number goes over a (small) limit, resort to using
148  *	       stat in its place.
149  *
150  *	An additional thing to consider is that pmake is used primarily
151  *	to create C programs and until recently pcc-based compilers refused
152  *	to allow you to specify where the resulting object file should be
153  *	placed. This forced all objects to be created in the current
154  *	directory. This isn't meant as a full excuse, just an explanation of
155  *	some of the reasons for the caching used here.
156  *
157  *	One more note: the location of a target's file is only performed
158  *	on the downward traversal of the graph and then only for terminal
159  *	nodes in the graph. This could be construed as wrong in some cases,
160  *	but prevents inadvertent modification of files when the "installed"
161  *	directory for a file is provided in the search path.
162  *
163  *	Another data structure maintained by this module is an mtime
164  *	cache used when the searching of cached directories fails to find
165  *	a file. In the past, Dir_FindFile would simply perform an access()
166  *	call in such a case to determine if the file could be found using
167  *	just the name given. When this hit, however, all that was gained
168  *	was the knowledge that the file existed. Given that an access() is
169  *	essentially a stat() without the copyout() call, and that the same
170  *	filesystem overhead would have to be incurred in Dir_MTime, it made
171  *	sense to replace the access() with a stat() and record the mtime
172  *	in a cache for when Dir_MTime was actually called.
173  */
174 
175 Lst          dirSearchPath;	/* main search path */
176 
177 static Lst   openDirectories;	/* the list of all open directories */
178 
179 /*
180  * Variables for gathering statistics on the efficiency of the hashing
181  * mechanism.
182  */
183 static int    hits,	      /* Found in directory cache */
184 	      misses,	      /* Sad, but not evil misses */
185 	      nearmisses,     /* Found under search path */
186 	      bigmisses;      /* Sought by itself */
187 
188 static Path    	  *dot;	    /* contents of current directory */
189 static Path    	  *cur;	    /* contents of current directory, if not dot */
190 static Hash_Table mtimes;   /* Results of doing a last-resort stat in
191 			     * Dir_FindFile -- if we have to go to the
192 			     * system to find the file, we might as well
193 			     * have its mtime on record. XXX: If this is done
194 			     * way early, there's a chance other rules will
195 			     * have already updated the file, in which case
196 			     * we'll update it again. Generally, there won't
197 			     * be two rules to update a single file, so this
198 			     * should be ok, but... */
199 
200 
201 static int DirFindName __P((ClientData, ClientData));
202 static int DirMatchFiles __P((char *, Path *, Lst));
203 static void DirExpandCurly __P((char *, char *, Lst, Lst));
204 static void DirExpandInt __P((char *, Lst, Lst));
205 static int DirPrintWord __P((ClientData, ClientData));
206 static int DirPrintDir __P((ClientData, ClientData));
207 static char *DirLookup __P((Path *, char *, char *, Boolean));
208 static char *DirLookupSubdir __P((Path *, char *));
209 
210 /*-
211  *-----------------------------------------------------------------------
212  * Dir_Init --
213  *	initialize things for this module
214  *
215  * Results:
216  *	none
217  *
218  * Side Effects:
219  *	some directories may be opened.
220  *-----------------------------------------------------------------------
221  */
222 void
223 Dir_Init (cdname)
224     const char *cdname;
225 {
226     dirSearchPath = Lst_Init (FALSE);
227     openDirectories = Lst_Init (FALSE);
228     Hash_InitTable(&mtimes, 0);
229 
230     /*
231      * Since the Path structure is placed on both openDirectories and
232      * the path we give Dir_AddDir (which in this case is openDirectories),
233      * we need to remove "." from openDirectories and what better time to
234      * do it than when we have to fetch the thing anyway?
235      */
236     dot = Dir_AddDir (NULL, ".");
237 
238     /*
239      * We always need to have dot around, so we increment its reference count
240      * to make sure it's not destroyed.
241      */
242     dot->refCount += 1;
243 
244     if (cdname != NULL) {
245 	/*
246 	 * Our build directory is not the same as our source directory.
247 	 * Keep this one around too.
248 	 */
249 	cur = Dir_AddDir (NULL, cdname);
250 	cur->refCount += 1;
251     }
252 }
253 
254 /*-
255  *-----------------------------------------------------------------------
256  * Dir_End --
257  *	cleanup things for this module
258  *
259  * Results:
260  *	none
261  *
262  * Side Effects:
263  *	none
264  *-----------------------------------------------------------------------
265  */
266 void
267 Dir_End()
268 {
269     if (cur) {
270 	cur->refCount -= 1;
271 	Dir_Destroy((ClientData) cur);
272     }
273     dot->refCount -= 1;
274     Dir_Destroy((ClientData) dot);
275     Dir_ClearPath(dirSearchPath);
276     Lst_Destroy(dirSearchPath, NOFREE);
277     Dir_ClearPath(openDirectories);
278     Lst_Destroy(openDirectories, NOFREE);
279     Hash_DeleteTable(&mtimes);
280 }
281 
282 /*-
283  *-----------------------------------------------------------------------
284  * DirFindName --
285  *	See if the Path structure describes the same directory as the
286  *	given one by comparing their names. Called from Dir_AddDir via
287  *	Lst_Find when searching the list of open directories.
288  *
289  * Results:
290  *	0 if it is the same. Non-zero otherwise
291  *
292  * Side Effects:
293  *	None
294  *-----------------------------------------------------------------------
295  */
296 static int
297 DirFindName (p, dname)
298     ClientData    p;	      /* Current name */
299     ClientData	  dname;      /* Desired name */
300 {
301     return (strcmp (((Path *)p)->name, (char *) dname));
302 }
303 
304 /*-
305  *-----------------------------------------------------------------------
306  * Dir_HasWildcards  --
307  *	see if the given name has any wildcard characters in it
308  *	be careful not to expand unmatching brackets or braces.
309  *	XXX: This code is not 100% correct. ([^]] fails etc.)
310  *	I really don't think that make(1) should be expanding
311  *	patterns, because then you have to set a mechanism for
312  *	escaping the expansion!
313  *
314  * Results:
315  *	returns TRUE if the word should be expanded, FALSE otherwise
316  *
317  * Side Effects:
318  *	none
319  *-----------------------------------------------------------------------
320  */
321 Boolean
322 Dir_HasWildcards (name)
323     char          *name;	/* name to check */
324 {
325     register char *cp;
326     int wild = 0, brace = 0, bracket = 0;
327 
328     for (cp = name; *cp; cp++) {
329 	switch(*cp) {
330 	case '{':
331 		brace++;
332 		wild = 1;
333 		break;
334 	case '}':
335 		brace--;
336 		break;
337 	case '[':
338 		bracket++;
339 		wild = 1;
340 		break;
341 	case ']':
342 		bracket--;
343 		break;
344 	case '?':
345 	case '*':
346 		wild = 1;
347 		break;
348 	default:
349 		break;
350 	}
351     }
352     return wild && bracket == 0 && brace == 0;
353 }
354 
355 /*-
356  *-----------------------------------------------------------------------
357  * DirMatchFiles --
358  * 	Given a pattern and a Path structure, see if any files
359  *	match the pattern and add their names to the 'expansions' list if
360  *	any do. This is incomplete -- it doesn't take care of patterns like
361  *	src / *src / *.c properly (just *.c on any of the directories), but it
362  *	will do for now.
363  *
364  * Results:
365  *	Always returns 0
366  *
367  * Side Effects:
368  *	File names are added to the expansions lst. The directory will be
369  *	fully hashed when this is done.
370  *-----------------------------------------------------------------------
371  */
372 static int
373 DirMatchFiles (pattern, p, expansions)
374     char	  *pattern;   	/* Pattern to look for */
375     Path	  *p;         	/* Directory to search */
376     Lst	    	  expansions;	/* Place to store the results */
377 {
378     Hash_Search	  search;   	/* Index into the directory's table */
379     Hash_Entry	  *entry;   	/* Current entry in the table */
380     Boolean 	  isDot;    	/* TRUE if the directory being searched is . */
381 
382     isDot = (*p->name == '.' && p->name[1] == '\0');
383 
384     for (entry = Hash_EnumFirst(&p->files, &search);
385 	 entry != (Hash_Entry *)NULL;
386 	 entry = Hash_EnumNext(&search))
387     {
388 	/*
389 	 * See if the file matches the given pattern. Note we follow the UNIX
390 	 * convention that dot files will only be found if the pattern
391 	 * begins with a dot (note also that as a side effect of the hashing
392 	 * scheme, .* won't match . or .. since they aren't hashed).
393 	 */
394 	if (Str_Match(entry->name, pattern) &&
395 	    ((entry->name[0] != '.') ||
396 	     (pattern[0] == '.')))
397 	{
398 	    (void)Lst_AtEnd(expansions,
399 			    (isDot ? estrdup(entry->name) :
400 			     str_concat(p->name, entry->name,
401 					STR_ADDSLASH)));
402 	}
403     }
404     return (0);
405 }
406 
407 /*-
408  *-----------------------------------------------------------------------
409  * DirExpandCurly --
410  *	Expand curly braces like the C shell. Does this recursively.
411  *	Note the special case: if after the piece of the curly brace is
412  *	done there are no wildcard characters in the result, the result is
413  *	placed on the list WITHOUT CHECKING FOR ITS EXISTENCE.
414  *
415  * Results:
416  *	None.
417  *
418  * Side Effects:
419  *	The given list is filled with the expansions...
420  *
421  *-----------------------------------------------------------------------
422  */
423 static void
424 DirExpandCurly(word, brace, path, expansions)
425     char    	  *word;    	/* Entire word to expand */
426     char    	  *brace;   	/* First curly brace in it */
427     Lst	    	  path;	    	/* Search path to use */
428     Lst	    	  expansions;	/* Place to store the expansions */
429 {
430     char    	  *end;	    	/* Character after the closing brace */
431     char    	  *cp;	    	/* Current position in brace clause */
432     char    	  *start;   	/* Start of current piece of brace clause */
433     int	    	  bracelevel;	/* Number of braces we've seen. If we see a
434 				 * right brace when this is 0, we've hit the
435 				 * end of the clause. */
436     char    	  *file;    	/* Current expansion */
437     int	    	  otherLen; 	/* The length of the other pieces of the
438 				 * expansion (chars before and after the
439 				 * clause in 'word') */
440     char    	  *cp2;	    	/* Pointer for checking for wildcards in
441 				 * expansion before calling Dir_Expand */
442 
443     start = brace+1;
444 
445     /*
446      * Find the end of the brace clause first, being wary of nested brace
447      * clauses.
448      */
449     for (end = start, bracelevel = 0; *end != '\0'; end++) {
450 	if (*end == '{') {
451 	    bracelevel++;
452 	} else if ((*end == '}') && (bracelevel-- == 0)) {
453 	    break;
454 	}
455     }
456     if (*end == '\0') {
457 	Error("Unterminated {} clause \"%s\"", start);
458 	return;
459     } else {
460 	end++;
461     }
462     otherLen = brace - word + strlen(end);
463 
464     for (cp = start; cp < end; cp++) {
465 	/*
466 	 * Find the end of this piece of the clause.
467 	 */
468 	bracelevel = 0;
469 	while (*cp != ',') {
470 	    if (*cp == '{') {
471 		bracelevel++;
472 	    } else if ((*cp == '}') && (bracelevel-- <= 0)) {
473 		break;
474 	    }
475 	    cp++;
476 	}
477 	/*
478 	 * Allocate room for the combination and install the three pieces.
479 	 */
480 	file = emalloc(otherLen + cp - start + 1);
481 	if (brace != word) {
482 	    strncpy(file, word, brace-word);
483 	}
484 	if (cp != start) {
485 	    strncpy(&file[brace-word], start, cp-start);
486 	}
487 	strcpy(&file[(brace-word)+(cp-start)], end);
488 
489 	/*
490 	 * See if the result has any wildcards in it. If we find one, call
491 	 * Dir_Expand right away, telling it to place the result on our list
492 	 * of expansions.
493 	 */
494 	for (cp2 = file; *cp2 != '\0'; cp2++) {
495 	    switch(*cp2) {
496 	    case '*':
497 	    case '?':
498 	    case '{':
499 	    case '[':
500 		Dir_Expand(file, path, expansions);
501 		goto next;
502 	    }
503 	}
504 	if (*cp2 == '\0') {
505 	    /*
506 	     * Hit the end w/o finding any wildcards, so stick the expansion
507 	     * on the end of the list.
508 	     */
509 	    (void)Lst_AtEnd(expansions, file);
510 	} else {
511 	next:
512 	    free(file);
513 	}
514 	start = cp+1;
515     }
516 }
517 
518 
519 /*-
520  *-----------------------------------------------------------------------
521  * DirExpandInt --
522  *	Internal expand routine. Passes through the directories in the
523  *	path one by one, calling DirMatchFiles for each. NOTE: This still
524  *	doesn't handle patterns in directories...
525  *
526  * Results:
527  *	None.
528  *
529  * Side Effects:
530  *	Things are added to the expansions list.
531  *
532  *-----------------------------------------------------------------------
533  */
534 static void
535 DirExpandInt(word, path, expansions)
536     char    	  *word;    	/* Word to expand */
537     Lst	    	  path;	    	/* Path on which to look */
538     Lst	    	  expansions;	/* Place to store the result */
539 {
540     LstNode 	  ln;	    	/* Current node */
541     Path	  *p;	    	/* Directory in the node */
542 
543     if (Lst_Open(path) == SUCCESS) {
544 	while ((ln = Lst_Next(path)) != NILLNODE) {
545 	    p = (Path *)Lst_Datum(ln);
546 	    DirMatchFiles(word, p, expansions);
547 	}
548 	Lst_Close(path);
549     }
550 }
551 
552 /*-
553  *-----------------------------------------------------------------------
554  * DirPrintWord --
555  *	Print a word in the list of expansions. Callback for Dir_Expand
556  *	when DEBUG(DIR), via Lst_ForEach.
557  *
558  * Results:
559  *	=== 0
560  *
561  * Side Effects:
562  *	The passed word is printed, followed by a space.
563  *
564  *-----------------------------------------------------------------------
565  */
566 static int
567 DirPrintWord(word, dummy)
568     ClientData  word;
569     ClientData  dummy;
570 {
571     printf("%s ", (char *) word);
572 
573     return(dummy ? 0 : 0);
574 }
575 
576 /*-
577  *-----------------------------------------------------------------------
578  * Dir_Expand  --
579  *	Expand the given word into a list of words by globbing it looking
580  *	in the directories on the given search path.
581  *
582  * Results:
583  *	A list of words consisting of the files which exist along the search
584  *	path matching the given pattern.
585  *
586  * Side Effects:
587  *	Directories may be opened. Who knows?
588  *-----------------------------------------------------------------------
589  */
590 void
591 Dir_Expand (word, path, expansions)
592     char    *word;      /* the word to expand */
593     Lst     path;   	/* the list of directories in which to find
594 			 * the resulting files */
595     Lst	    expansions;	/* the list on which to place the results */
596 {
597     char    	  *cp;
598 
599     if (DEBUG(DIR)) {
600 	printf("expanding \"%s\"...", word);
601     }
602 
603     cp = strchr(word, '{');
604     if (cp) {
605 	DirExpandCurly(word, cp, path, expansions);
606     } else {
607 	cp = strchr(word, '/');
608 	if (cp) {
609 	    /*
610 	     * The thing has a directory component -- find the first wildcard
611 	     * in the string.
612 	     */
613 	    for (cp = word; *cp; cp++) {
614 		if (*cp == '?' || *cp == '[' || *cp == '*' || *cp == '{') {
615 		    break;
616 		}
617 	    }
618 	    if (*cp == '{') {
619 		/*
620 		 * This one will be fun.
621 		 */
622 		DirExpandCurly(word, cp, path, expansions);
623 		return;
624 	    } else if (*cp != '\0') {
625 		/*
626 		 * Back up to the start of the component
627 		 */
628 		char  *dirpath;
629 
630 		while (cp > word && *cp != '/') {
631 		    cp--;
632 		}
633 		if (cp != word) {
634 		    char sc;
635 		    /*
636 		     * If the glob isn't in the first component, try and find
637 		     * all the components up to the one with a wildcard.
638 		     */
639 		    sc = cp[1];
640 		    cp[1] = '\0';
641 		    dirpath = Dir_FindFile(word, path);
642 		    cp[1] = sc;
643 		    /*
644 		     * dirpath is null if can't find the leading component
645 		     * XXX: Dir_FindFile won't find internal components.
646 		     * i.e. if the path contains ../Etc/Object and we're
647 		     * looking for Etc, it won't be found. Ah well.
648 		     * Probably not important.
649 		     */
650 		    if (dirpath != (char *)NULL) {
651 			char *dp = &dirpath[strlen(dirpath) - 1];
652 			if (*dp == '/')
653 			    *dp = '\0';
654 			path = Lst_Init(FALSE);
655 			(void) Dir_AddDir(path, dirpath);
656 			DirExpandInt(cp+1, path, expansions);
657 			Lst_Destroy(path, NOFREE);
658 		    }
659 		} else {
660 		    /*
661 		     * Start the search from the local directory
662 		     */
663 		    DirExpandInt(word, path, expansions);
664 		}
665 	    } else {
666 		/*
667 		 * Return the file -- this should never happen.
668 		 */
669 		DirExpandInt(word, path, expansions);
670 	    }
671 	} else {
672 	    /*
673 	     * First the files in dot
674 	     */
675 	    DirMatchFiles(word, dot, expansions);
676 
677 	    /*
678 	     * Then the files in every other directory on the path.
679 	     */
680 	    DirExpandInt(word, path, expansions);
681 	}
682     }
683     if (DEBUG(DIR)) {
684 	Lst_ForEach(expansions, DirPrintWord, (ClientData) 0);
685 	fputc('\n', stdout);
686     }
687 }
688 
689 /*-
690  *-----------------------------------------------------------------------
691  * DirLookup  --
692  *	Find if the file with the given name exists in the given path.
693  *
694  * Results:
695  *	The path to the file, the empty string or NULL. If the file is
696  *	the empty string, the search should be terminated.
697  *	This path is guaranteed to be in a
698  *	different part of memory than name and so may be safely free'd.
699  *
700  * Side Effects:
701  *	None.
702  *-----------------------------------------------------------------------
703  */
704 static char *
705 DirLookup(p, name, cp, hasSlash)
706     Path *p;
707     char *name;
708     char *cp;
709     Boolean hasSlash;
710 {
711     char *p1;		/* pointer into p->name */
712     char *p2;		/* pointer into name */
713     char *file;		/* the current filename to check */
714 
715     if (DEBUG(DIR)) {
716 	printf("%s...", p->name);
717     }
718     if (Hash_FindEntry (&p->files, cp) != (Hash_Entry *)NULL) {
719 	if (DEBUG(DIR)) {
720 	    printf("here...");
721 	}
722 	if (hasSlash) {
723 	    /*
724 	     * If the name had a slash, its initial components and p's
725 	     * final components must match. This is false if a mismatch
726 	     * is encountered before all of the initial components
727 	     * have been checked (p2 > name at the end of the loop), or
728 	     * we matched only part of one of the components of p
729 	     * along with all the rest of them (*p1 != '/').
730 	     */
731 	    p1 = p->name + strlen (p->name) - 1;
732 	    p2 = cp - 2;
733 	    while (p2 >= name && p1 >= p->name && *p1 == *p2) {
734 		p1 -= 1; p2 -= 1;
735 	    }
736 	    if (p2 >= name || (p1 >= p->name && *p1 != '/')) {
737 		if (DEBUG(DIR)) {
738 		    printf("component mismatch -- continuing...");
739 		}
740 		return NULL;
741 	    }
742 	}
743 	file = str_concat (p->name, cp, STR_ADDSLASH);
744 	if (DEBUG(DIR)) {
745 	    printf("returning %s\n", file);
746 	}
747 	p->hits += 1;
748 	hits += 1;
749 	return file;
750     } else if (hasSlash) {
751 	/*
752 	 * If the file has a leading path component and that component
753 	 * exactly matches the entire name of the current search
754 	 * directory, we assume the file doesn't exist and return NULL.
755 	 */
756 	for (p1 = p->name, p2 = name; *p1 && *p1 == *p2; p1++, p2++) {
757 	    continue;
758 	}
759 	if (*p1 == '\0' && p2 == cp - 1) {
760 	    if (DEBUG(DIR)) {
761 		printf("must be here but isn't -- returing\n");
762 	    }
763 	    return "";
764 	}
765     }
766     return NULL;
767 }
768 
769 
770 /*-
771  *-----------------------------------------------------------------------
772  * DirLookupSubdir  --
773  *	Find if the file with the given name exists in the given path.
774  *
775  * Results:
776  *	The path to the file or NULL. This path is guaranteed to be in a
777  *	different part of memory than name and so may be safely free'd.
778  *
779  * Side Effects:
780  *	If the file is found, it is added in the modification times hash
781  *	table.
782  *-----------------------------------------------------------------------
783  */
784 static char *
785 DirLookupSubdir(p, name)
786     Path *p;
787     char *name;
788 {
789     struct stat	  stb;		/* Buffer for stat, if necessary */
790     Hash_Entry	 *entry;	/* Entry for mtimes table */
791     char 	 *file;		/* the current filename to check */
792 
793     if (p != dot) {
794 	file = str_concat (p->name, name, STR_ADDSLASH);
795     } else {
796 	/*
797 	 * Checking in dot -- DON'T put a leading ./ on the thing.
798 	 */
799 	file = estrdup(name);
800     }
801 
802     if (DEBUG(DIR)) {
803 	printf("checking %s...", file);
804     }
805 
806     if (stat (file, &stb) == 0) {
807 	if (DEBUG(DIR)) {
808 	    printf("got it.\n");
809 	}
810 
811 	/*
812 	 * Save the modification time so if it's needed, we don't have
813 	 * to fetch it again.
814 	 */
815 	if (DEBUG(DIR)) {
816 	    printf("Caching %s for %s\n", Targ_FmtTime(stb.st_mtime),
817 		    file);
818 	}
819 	entry = Hash_CreateEntry(&mtimes, (char *) file,
820 				 (Boolean *)NULL);
821 	Hash_SetValue(entry, (long)stb.st_mtime);
822 	nearmisses += 1;
823 	return (file);
824     }
825     free (file);
826     return NULL;
827 }
828 
829 /*-
830  *-----------------------------------------------------------------------
831  * Dir_FindFile  --
832  *	Find the file with the given name along the given search path.
833  *
834  * Results:
835  *	The path to the file or NULL. This path is guaranteed to be in a
836  *	different part of memory than name and so may be safely free'd.
837  *
838  * Side Effects:
839  *	If the file is found in a directory which is not on the path
840  *	already (either 'name' is absolute or it is a relative path
841  *	[ dir1/.../dirn/file ] which exists below one of the directories
842  *	already on the search path), its directory is added to the end
843  *	of the path on the assumption that there will be more files in
844  *	that directory later on. Sometimes this is true. Sometimes not.
845  *-----------------------------------------------------------------------
846  */
847 char *
848 Dir_FindFile (name, path)
849     char    	  *name;    /* the file to find */
850     Lst           path;	    /* the Lst of directories to search */
851 {
852     LstNode       ln;	    /* a list element */
853     register char *file;    /* the current filename to check */
854     register Path *p;	    /* current path member */
855     register char *cp;	    /* index of first slash, if any */
856     Boolean	  hasSlash; /* true if 'name' contains a / */
857     struct stat	  stb;	    /* Buffer for stat, if necessary */
858     Hash_Entry	  *entry;   /* Entry for mtimes table */
859 
860     /*
861      * Find the final component of the name and note whether it has a
862      * slash in it (the name, I mean)
863      */
864     cp = strrchr (name, '/');
865     if (cp) {
866 	hasSlash = TRUE;
867 	cp += 1;
868     } else {
869 	hasSlash = FALSE;
870 	cp = name;
871     }
872 
873     if (DEBUG(DIR)) {
874 	printf("Searching for %s...", name);
875     }
876     /*
877      * No matter what, we always look for the file in the current directory
878      * before anywhere else and we *do not* add the ./ to it if it exists.
879      * This is so there are no conflicts between what the user specifies
880      * (fish.c) and what pmake finds (./fish.c).
881      */
882     if ((!hasSlash || (cp - name == 2 && *name == '.'))) {
883 	if (Hash_FindEntry (&dot->files, cp) != (Hash_Entry *)NULL) {
884 	    if (DEBUG(DIR)) {
885 		printf("in '.'\n");
886 	    }
887 	    hits += 1;
888 	    dot->hits += 1;
889 	    return (estrdup (name));
890 	}
891 	if (cur &&
892 	    Hash_FindEntry (&cur->files, cp) != (Hash_Entry *)NULL) {
893 	    if (DEBUG(DIR)) {
894 		printf("in ${.CURDIR} = %s\n", cur->name);
895 	    }
896 	    hits += 1;
897 	    cur->hits += 1;
898 	    return str_concat (cur->name, cp, STR_ADDSLASH);
899 	}
900     }
901 
902     if (Lst_Open (path) == FAILURE) {
903 	if (DEBUG(DIR)) {
904 	    printf("couldn't open path, file not found\n");
905 	}
906 	misses += 1;
907 	return ((char *) NULL);
908     }
909 
910     if (cur && (file = DirLookup(cur, name, cp, hasSlash)) != NULL) {
911 	if (*file)
912 	    return file;
913 	else
914 	    return NULL;
915     }
916 
917     /*
918      * We look through all the directories on the path seeking one which
919      * contains the final component of the given name and whose final
920      * component(s) match the name's initial component(s). If such a beast
921      * is found, we concatenate the directory name and the final component
922      * and return the resulting string. If we don't find any such thing,
923      * we go on to phase two...
924      */
925     while ((ln = Lst_Next (path)) != NILLNODE) {
926 	p = (Path *) Lst_Datum (ln);
927         if ((file = DirLookup(p, name, cp, hasSlash)) != NULL) {
928 	    Lst_Close (path);
929 	    if (*file)
930 		return file;
931 	    else
932 		return NULL;
933 	}
934     }
935 
936     /*
937      * We didn't find the file on any existing members of the directory.
938      * If the name doesn't contain a slash, that means it doesn't exist.
939      * If it *does* contain a slash, however, there is still hope: it
940      * could be in a subdirectory of one of the members of the search
941      * path. (eg. /usr/include and sys/types.h. The above search would
942      * fail to turn up types.h in /usr/include, but it *is* in
943      * /usr/include/sys/types.h) If we find such a beast, we assume there
944      * will be more (what else can we assume?) and add all but the last
945      * component of the resulting name onto the search path (at the
946      * end). This phase is only performed if the file is *not* absolute.
947      */
948     if (!hasSlash) {
949 	if (DEBUG(DIR)) {
950 	    printf("failed.\n");
951 	}
952 	misses += 1;
953 	return ((char *) NULL);
954     }
955 
956     if (*name != '/') {
957 	Boolean	checkedDot = FALSE;
958 
959 	if (DEBUG(DIR)) {
960 	    printf("failed. Trying subdirectories...");
961 	}
962 
963 	if (cur && (file = DirLookupSubdir(cur, name)) != NULL)
964 	    return file;
965 
966 	(void) Lst_Open (path);
967 	while ((ln = Lst_Next (path)) != NILLNODE) {
968 	    p = (Path *) Lst_Datum (ln);
969 	    if (p == dot)
970 		checkedDot = TRUE;
971 	    if ((file = DirLookupSubdir(p, name)) != NULL) {
972 		Lst_Close (path);
973 		return file;
974 	    }
975 	}
976 
977 	if (DEBUG(DIR)) {
978 	    printf("failed. ");
979 	}
980 	Lst_Close (path);
981 
982 	if (checkedDot) {
983 	    /*
984 	     * Already checked by the given name, since . was in the path,
985 	     * so no point in proceeding...
986 	     */
987 	    if (DEBUG(DIR)) {
988 		printf("Checked . already, returning NULL\n");
989 	    }
990 	    return(NULL);
991 	}
992     }
993 
994     /*
995      * Didn't find it that way, either. Sigh. Phase 3. Add its directory
996      * onto the search path in any case, just in case, then look for the
997      * thing in the hash table. If we find it, grand. We return a new
998      * copy of the name. Otherwise we sadly return a NULL pointer. Sigh.
999      * Note that if the directory holding the file doesn't exist, this will
1000      * do an extra search of the final directory on the path. Unless something
1001      * weird happens, this search won't succeed and life will be groovy.
1002      *
1003      * Sigh. We cannot add the directory onto the search path because
1004      * of this amusing case:
1005      * $(INSTALLDIR)/$(FILE): $(FILE)
1006      *
1007      * $(FILE) exists in $(INSTALLDIR) but not in the current one.
1008      * When searching for $(FILE), we will find it in $(INSTALLDIR)
1009      * b/c we added it here. This is not good...
1010      */
1011 #ifdef notdef
1012     cp[-1] = '\0';
1013     (void) Dir_AddDir (path, name);
1014     cp[-1] = '/';
1015 
1016     bigmisses += 1;
1017     ln = Lst_Last (path);
1018     if (ln == NILLNODE) {
1019 	return ((char *) NULL);
1020     } else {
1021 	p = (Path *) Lst_Datum (ln);
1022     }
1023 
1024     if (Hash_FindEntry (&p->files, cp) != (Hash_Entry *)NULL) {
1025 	return (estrdup (name));
1026     } else {
1027 	return ((char *) NULL);
1028     }
1029 #else /* !notdef */
1030     if (DEBUG(DIR)) {
1031 	printf("Looking for \"%s\"...", name);
1032     }
1033 
1034     bigmisses += 1;
1035     entry = Hash_FindEntry(&mtimes, name);
1036     if (entry != (Hash_Entry *)NULL) {
1037 	if (DEBUG(DIR)) {
1038 	    printf("got it (in mtime cache)\n");
1039 	}
1040 	return(estrdup(name));
1041     } else if (stat (name, &stb) == 0) {
1042 	entry = Hash_CreateEntry(&mtimes, name, (Boolean *)NULL);
1043 	if (DEBUG(DIR)) {
1044 	    printf("Caching %s for %s\n", Targ_FmtTime(stb.st_mtime),
1045 		    name);
1046 	}
1047 	Hash_SetValue(entry, (long)stb.st_mtime);
1048 	return (estrdup (name));
1049     } else {
1050 	if (DEBUG(DIR)) {
1051 	    printf("failed. Returning NULL\n");
1052 	}
1053 	return ((char *)NULL);
1054     }
1055 #endif /* notdef */
1056 }
1057 
1058 /*-
1059  *-----------------------------------------------------------------------
1060  * Dir_MTime  --
1061  *	Find the modification time of the file described by gn along the
1062  *	search path dirSearchPath.
1063  *
1064  * Results:
1065  *	The modification time or 0 if it doesn't exist
1066  *
1067  * Side Effects:
1068  *	The modification time is placed in the node's mtime slot.
1069  *	If the node didn't have a path entry before, and Dir_FindFile
1070  *	found one for it, the full name is placed in the path slot.
1071  *-----------------------------------------------------------------------
1072  */
1073 int
1074 Dir_MTime (gn)
1075     GNode         *gn;	      /* the file whose modification time is
1076 			       * desired */
1077 {
1078     char          *fullName;  /* the full pathname of name */
1079     struct stat	  stb;	      /* buffer for finding the mod time */
1080     Hash_Entry	  *entry;
1081 
1082     if (gn->type & OP_ARCHV) {
1083 	return Arch_MTime (gn);
1084     } else if (gn->path == (char *)NULL) {
1085 	if (gn->type & (OP_PHONY|OP_NOPATH))
1086 	    fullName = NULL;
1087 	else
1088 	    fullName = Dir_FindFile (gn->name, dirSearchPath);
1089     } else {
1090 	fullName = gn->path;
1091     }
1092 
1093     if (fullName == (char *)NULL) {
1094 	fullName = estrdup(gn->name);
1095     }
1096 
1097     entry = Hash_FindEntry(&mtimes, fullName);
1098     if (entry != (Hash_Entry *)NULL) {
1099 	/*
1100 	 * Only do this once -- the second time folks are checking to
1101 	 * see if the file was actually updated, so we need to actually go
1102 	 * to the file system.
1103 	 */
1104 	if (DEBUG(DIR)) {
1105 	    printf("Using cached time %s for %s\n",
1106 		    Targ_FmtTime((time_t)(long)Hash_GetValue(entry)), fullName);
1107 	}
1108 	stb.st_mtime = (time_t)(long)Hash_GetValue(entry);
1109 	Hash_DeleteEntry(&mtimes, entry);
1110     } else if (stat (fullName, &stb) < 0) {
1111 	if (gn->type & OP_MEMBER) {
1112 	    if (fullName != gn->path)
1113 		free(fullName);
1114 	    return Arch_MemMTime (gn);
1115 	} else {
1116 	    stb.st_mtime = 0;
1117 	}
1118     }
1119     if (fullName && gn->path == (char *)NULL) {
1120 	gn->path = fullName;
1121     }
1122 
1123     gn->mtime = stb.st_mtime;
1124     return (gn->mtime);
1125 }
1126 
1127 /*-
1128  *-----------------------------------------------------------------------
1129  * Dir_AddDir --
1130  *	Add the given name to the end of the given path. The order of
1131  *	the arguments is backwards so ParseDoDependency can do a
1132  *	Lst_ForEach of its list of paths...
1133  *
1134  * Results:
1135  *	none
1136  *
1137  * Side Effects:
1138  *	A structure is added to the list and the directory is
1139  *	read and hashed.
1140  *-----------------------------------------------------------------------
1141  */
1142 Path *
1143 Dir_AddDir (path, name)
1144     Lst           path;	      /* the path to which the directory should be
1145 			       * added */
1146     const char   *name;	      /* the name of the directory to add */
1147 {
1148     LstNode       ln;	      /* node in case Path structure is found */
1149     register Path *p = NULL;  /* pointer to new Path structure */
1150     DIR     	  *d;	      /* for reading directory */
1151     register struct dirent *dp; /* entry in directory */
1152 
1153     ln = Lst_Find (openDirectories, (ClientData)name, DirFindName);
1154     if (ln != NILLNODE) {
1155 	p = (Path *)Lst_Datum (ln);
1156 	if (Lst_Member(path, (ClientData)p) == NILLNODE) {
1157 	    p->refCount += 1;
1158 	    (void)Lst_AtEnd (path, (ClientData)p);
1159 	}
1160     } else {
1161 	if (DEBUG(DIR)) {
1162 	    printf("Caching %s...", name);
1163 	    fflush(stdout);
1164 	}
1165 
1166 	if ((d = opendir (name)) != (DIR *) NULL) {
1167 	    p = (Path *) emalloc (sizeof (Path));
1168 	    p->name = estrdup (name);
1169 	    p->hits = 0;
1170 	    p->refCount = 1;
1171 	    Hash_InitTable (&p->files, -1);
1172 
1173 	    /*
1174 	     * Skip the first two entries -- these will *always* be . and ..
1175 	     */
1176 	    (void)readdir(d);
1177 	    (void)readdir(d);
1178 
1179 	    while ((dp = readdir (d)) != (struct dirent *) NULL) {
1180 #if defined(sun) && defined(d_ino) /* d_ino is a sunos4 #define for d_fileno */
1181 		/*
1182 		 * The sun directory library doesn't check for a 0 inode
1183 		 * (0-inode slots just take up space), so we have to do
1184 		 * it ourselves.
1185 		 */
1186 		if (dp->d_fileno == 0) {
1187 		    continue;
1188 		}
1189 #endif /* sun && d_ino */
1190 		(void)Hash_CreateEntry(&p->files, dp->d_name, (Boolean *)NULL);
1191 	    }
1192 	    (void) closedir (d);
1193 	    (void)Lst_AtEnd (openDirectories, (ClientData)p);
1194 	    if (path != NULL)
1195 		(void)Lst_AtEnd (path, (ClientData)p);
1196 	}
1197 	if (DEBUG(DIR)) {
1198 	    printf("done\n");
1199 	}
1200     }
1201     return p;
1202 }
1203 
1204 /*-
1205  *-----------------------------------------------------------------------
1206  * Dir_CopyDir --
1207  *	Callback function for duplicating a search path via Lst_Duplicate.
1208  *	Ups the reference count for the directory.
1209  *
1210  * Results:
1211  *	Returns the Path it was given.
1212  *
1213  * Side Effects:
1214  *	The refCount of the path is incremented.
1215  *
1216  *-----------------------------------------------------------------------
1217  */
1218 ClientData
1219 Dir_CopyDir(p)
1220     ClientData p;
1221 {
1222     ((Path *) p)->refCount += 1;
1223 
1224     return ((ClientData)p);
1225 }
1226 
1227 /*-
1228  *-----------------------------------------------------------------------
1229  * Dir_MakeFlags --
1230  *	Make a string by taking all the directories in the given search
1231  *	path and preceding them by the given flag. Used by the suffix
1232  *	module to create variables for compilers based on suffix search
1233  *	paths.
1234  *
1235  * Results:
1236  *	The string mentioned above. Note that there is no space between
1237  *	the given flag and each directory. The empty string is returned if
1238  *	Things don't go well.
1239  *
1240  * Side Effects:
1241  *	None
1242  *-----------------------------------------------------------------------
1243  */
1244 char *
1245 Dir_MakeFlags (flag, path)
1246     char	  *flag;  /* flag which should precede each directory */
1247     Lst	    	  path;	  /* list of directories */
1248 {
1249     char	  *str;	  /* the string which will be returned */
1250     char	  *tstr;  /* the current directory preceded by 'flag' */
1251     LstNode	  ln;	  /* the node of the current directory */
1252     Path	  *p;	  /* the structure describing the current directory */
1253 
1254     str = estrdup ("");
1255 
1256     if (Lst_Open (path) == SUCCESS) {
1257 	while ((ln = Lst_Next (path)) != NILLNODE) {
1258 	    p = (Path *) Lst_Datum (ln);
1259 	    tstr = str_concat (flag, p->name, 0);
1260 	    str = str_concat (str, tstr, STR_ADDSPACE | STR_DOFREE);
1261 	}
1262 	Lst_Close (path);
1263     }
1264 
1265     return (str);
1266 }
1267 
1268 /*-
1269  *-----------------------------------------------------------------------
1270  * Dir_Destroy --
1271  *	Nuke a directory descriptor, if possible. Callback procedure
1272  *	for the suffixes module when destroying a search path.
1273  *
1274  * Results:
1275  *	None.
1276  *
1277  * Side Effects:
1278  *	If no other path references this directory (refCount == 0),
1279  *	the Path and all its data are freed.
1280  *
1281  *-----------------------------------------------------------------------
1282  */
1283 void
1284 Dir_Destroy (pp)
1285     ClientData 	  pp;	    /* The directory descriptor to nuke */
1286 {
1287     Path    	  *p = (Path *) pp;
1288     p->refCount -= 1;
1289 
1290     if (p->refCount == 0) {
1291 	LstNode	ln;
1292 
1293 	ln = Lst_Member (openDirectories, (ClientData)p);
1294 	(void) Lst_Remove (openDirectories, ln);
1295 
1296 	Hash_DeleteTable (&p->files);
1297 	free((Address)p->name);
1298 	free((Address)p);
1299     }
1300 }
1301 
1302 /*-
1303  *-----------------------------------------------------------------------
1304  * Dir_ClearPath --
1305  *	Clear out all elements of the given search path. This is different
1306  *	from destroying the list, notice.
1307  *
1308  * Results:
1309  *	None.
1310  *
1311  * Side Effects:
1312  *	The path is set to the empty list.
1313  *
1314  *-----------------------------------------------------------------------
1315  */
1316 void
1317 Dir_ClearPath(path)
1318     Lst	    path; 	/* Path to clear */
1319 {
1320     Path    *p;
1321     while (!Lst_IsEmpty(path)) {
1322 	p = (Path *)Lst_DeQueue(path);
1323 	Dir_Destroy((ClientData) p);
1324     }
1325 }
1326 
1327 
1328 /*-
1329  *-----------------------------------------------------------------------
1330  * Dir_Concat --
1331  *	Concatenate two paths, adding the second to the end of the first.
1332  *	Makes sure to avoid duplicates.
1333  *
1334  * Results:
1335  *	None
1336  *
1337  * Side Effects:
1338  *	Reference counts for added dirs are upped.
1339  *
1340  *-----------------------------------------------------------------------
1341  */
1342 void
1343 Dir_Concat(path1, path2)
1344     Lst	    path1;  	/* Dest */
1345     Lst	    path2;  	/* Source */
1346 {
1347     LstNode ln;
1348     Path    *p;
1349 
1350     for (ln = Lst_First(path2); ln != NILLNODE; ln = Lst_Succ(ln)) {
1351 	p = (Path *)Lst_Datum(ln);
1352 	if (Lst_Member(path1, (ClientData)p) == NILLNODE) {
1353 	    p->refCount += 1;
1354 	    (void)Lst_AtEnd(path1, (ClientData)p);
1355 	}
1356     }
1357 }
1358 
1359 /********** DEBUG INFO **********/
1360 void
1361 Dir_PrintDirectories()
1362 {
1363     LstNode	ln;
1364     Path	*p;
1365 
1366     printf ("#*** Directory Cache:\n");
1367     printf ("# Stats: %d hits %d misses %d near misses %d losers (%d%%)\n",
1368 	      hits, misses, nearmisses, bigmisses,
1369 	      (hits+bigmisses+nearmisses ?
1370 	       hits * 100 / (hits + bigmisses + nearmisses) : 0));
1371     printf ("# %-20s referenced\thits\n", "directory");
1372     if (Lst_Open (openDirectories) == SUCCESS) {
1373 	while ((ln = Lst_Next (openDirectories)) != NILLNODE) {
1374 	    p = (Path *) Lst_Datum (ln);
1375 	    printf ("# %-20s %10d\t%4d\n", p->name, p->refCount, p->hits);
1376 	}
1377 	Lst_Close (openDirectories);
1378     }
1379 }
1380 
1381 static int DirPrintDir (p, dummy)
1382     ClientData	p;
1383     ClientData	dummy;
1384 {
1385     printf ("%s ", ((Path *) p)->name);
1386     return (dummy ? 0 : 0);
1387 }
1388 
1389 void
1390 Dir_PrintPath (path)
1391     Lst	path;
1392 {
1393     Lst_ForEach (path, DirPrintDir, (ClientData)0);
1394 }
1395