1 /**	$MirOS: src/usr.bin/make/dir.c,v 1.4 2007/06/21 14:17:07 tg Exp $ */
2 /*	$OpenPackages$ */
3 /*	$OpenBSD: dir.c,v 1.45 2007/01/18 17:49:51 espie Exp $ */
4 /*	$NetBSD: dir.c,v 1.14 1997/03/29 16:51:26 christos Exp $	*/
5 
6 /*
7  * Copyright (c) 1999 Marc Espie.
8  *
9  * Extensive code changes for the OpenBSD project.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE OPENBSD PROJECT AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OPENBSD
24  * PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 /*
33  * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
34  * Copyright (c) 1988, 1989 by Adam de Boor
35  * Copyright (c) 1989 by Berkeley Softworks
36  * All rights reserved.
37  *
38  * This code is derived from software contributed to Berkeley by
39  * Adam de Boor.
40  *
41  * Redistribution and use in source and binary forms, with or without
42  * modification, are permitted provided that the following conditions
43  * are met:
44  * 1. Redistributions of source code must retain the above copyright
45  *    notice, this list of conditions and the following disclaimer.
46  * 2. Redistributions in binary form must reproduce the above copyright
47  *    notice, this list of conditions and the following disclaimer in the
48  *    documentation and/or other materials provided with the distribution.
49  * 3. Neither the name of the University nor the names of its contributors
50  *    may be used to endorse or promote products derived from this software
51  *    without specific prior written permission.
52  *
53  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
54  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
55  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
56  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
57  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
58  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
59  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
60  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
61  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
62  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
63  * SUCH DAMAGE.
64  */
65 
66 #include <sys/param.h>
67 #include <sys/stat.h>
68 #include <dirent.h>
69 #include <limits.h>
70 #include <stddef.h>
71 #include <stdio.h>
72 #include <stdint.h>
73 #include <stdlib.h>
74 #include <string.h>
75 #include "config.h"
76 #include "defines.h"
77 #include "ohash.h"
78 #include "dir.h"
79 #include "lst.h"
80 #include "memory.h"
81 #include "buf.h"
82 #include "gnode.h"
83 #include "arch.h"
84 #include "targ.h"
85 #include "error.h"
86 #include "str.h"
87 #include "timestamp.h"
88 
89 __RCSID("$MirOS: src/usr.bin/make/dir.c,v 1.4 2007/06/21 14:17:07 tg Exp $");
90 
91 typedef struct Path_ {
92     int 	  refCount;	/* Number of paths with this directory */
93 #ifdef DEBUG_DIRECTORY_CACHE
94     int 	  hits; 	/* the number of times a file in this
95 				 * directory has been found */
96 #endif
97     struct ohash   files;	/* Hash table of files in directory */
98     char	  name[1];	/* Name of directory */
99 } Path;
100 
101 /*	A search path consists of a Lst of Path structures. A Path structure
102  *	has in it the name of the directory and a hash table of all the files
103  *	in the directory. This is used to cut down on the number of system
104  *	calls necessary to find implicit dependents and their like. Since
105  *	these searches are made before any actions are taken, we need not
106  *	worry about the directory changing due to creation commands. If this
107  *	hampers the style of some makefiles, they must be changed.
108  *
109  *	A list of all previously-read directories is kept in the
110  *	openDirectories cache.
111  *
112  *	The need for the caching of whole directories is brought about by
113  *	the multi-level transformation code in suff.c, which tends to search
114  *	for far more files than regular make does. In the initial
115  *	implementation, the amount of time spent performing "stat" calls was
116  *	truly astronomical. The problem with hashing at the start is,
117  *	of course, that pmake doesn't then detect changes to these directories
118  *	during the course of the make. Three possibilities suggest themselves:
119  *
120  *	    1) just use stat to test for a file's existence. As mentioned
121  *	       above, this is very inefficient due to the number of checks
122  *	       engendered by the multi-level transformation code.
123  *	    2) use readdir() and company to search the directories, keeping
124  *	       them open between checks. I have tried this and while it
125  *	       didn't slow down the process too much, it could severely
126  *	       affect the amount of parallelism available as each directory
127  *	       open would take another file descriptor out of play for
128  *	       handling I/O for another job. Given that it is only recently
129  *	       that UNIX OS's have taken to allowing more than 20 or 32
130  *	       file descriptors for a process, this doesn't seem acceptable
131  *	       to me.
132  *	    3) record the mtime of the directory in the Path structure and
133  *	       verify the directory hasn't changed since the contents were
134  *	       hashed. This will catch the creation or deletion of files,
135  *	       but not the updating of files. However, since it is the
136  *	       creation and deletion that is the problem, this could be
137  *	       a good thing to do. Unfortunately, if the directory (say ".")
138  *	       were fairly large and changed fairly frequently, the constant
139  *	       rehashing could seriously degrade performance. It might be
140  *	       good in such cases to keep track of the number of rehashes
141  *	       and if the number goes over a (small) limit, resort to using
142  *	       stat in its place.
143  *
144  *	An additional thing to consider is that pmake is used primarily
145  *	to create C programs and until recently pcc-based compilers refused
146  *	to allow you to specify where the resulting object file should be
147  *	placed. This forced all objects to be created in the current
148  *	directory. This isn't meant as a full excuse, just an explanation of
149  *	some of the reasons for the caching used here.
150  *
151  *	One more note: the location of a target's file is only performed
152  *	on the downward traversal of the graph and then only for terminal
153  *	nodes in the graph. This could be construed as wrong in some cases,
154  *	but prevents inadvertent modification of files when the "installed"
155  *	directory for a file is provided in the search path.
156  *
157  *	Another data structure maintained by this module is an mtime
158  *	cache used when the searching of cached directories fails to find
159  *	a file. In the past, Dir_FindFile would simply perform an access()
160  *	call in such a case to determine if the file could be found using
161  *	just the name given. When this hit, however, all that was gained
162  *	was the knowledge that the file existed. Given that an access() is
163  *	essentially a stat() without the copyout() call, and that the same
164  *	filesystem overhead would have to be incurred in Dir_MTime, it made
165  *	sense to replace the access() with a stat() and record the mtime
166  *	in a cache for when Dir_MTime was actually called.  */
167 
168 static LIST   thedirSearchPath;		/* main search path */
169 Lst	      dirSearchPath= &thedirSearchPath;
170 
171 #ifdef DEBUG_DIRECTORY_CACHE
172 /* Variables for gathering statistics on the efficiency of the hashing
173  * mechanism.  */
174 static int    hits,			/* Found in directory cache */
175 	      misses,			/* Sad, but not evil misses */
176 	      nearmisses,		/* Found under search path */
177 	      bigmisses;		/* Sought by itself */
178 #endif
179 
180 static Path	  *dot; 		/* contents of current directory */
181 
182 struct file_stamp {
183 	TIMESTAMP mtime;		/* time stamp... */
184 	char name[1];			/* ...for that file.  */
185 };
186 
187 static struct ohash   openDirectories;	/* cache all open directories */
188 
189 /* Global structure used to cache mtimes.  XXX We don't cache an mtime
190  * before a caller actually looks up for the given time, because of the
191  * possibility a caller might update the file and invalidate the cache
192  * entry, and we don't look up in this cache except as a last resort.
193  */
194 static struct ohash mtimes;
195 
196 
197 /* There are three distinct hash structures:
198  * - to collate files's last modification times (global mtimes)
199  * - to collate file names (in each Path structure)
200  * - to collate known directories (global openDirectories).  */
201 static struct ohash_info stamp_info = { offsetof(struct file_stamp, name),
202     NULL, hash_alloc, hash_free, element_alloc };
203 
204 static struct ohash_info file_info = { 0,
205     NULL, hash_alloc, hash_free, element_alloc };
206 
207 static struct ohash_info dir_info = { offsetof(Path, name),
208     NULL, hash_alloc, hash_free, element_alloc };
209 
210 /* add_file(path, name): add a file name to a path hash structure. */
211 static void add_file(Path *, const char *);
212 /* n = find_file_hashi(p, name, end, hv): retrieve name in a path hash
213  * 	structure. */
214 static char *find_file_hashi(Path *, const char *, const char *, uint32_t);
215 
216 /* stamp = find_stampi(name, end): look for (name, end) in the global
217  *	cache. */
218 static struct file_stamp *find_stampi(const char *, const char *);
219 /* record_stamp(name, timestamp): record timestamp for name in the global
220  * 	cache. */
221 static void record_stamp(const char *, TIMESTAMP);
222 
223 /* free_hash(o): free a ohash structure, where each element can be free'd. */
224 static void free_hash(struct ohash *);
225 
226 /* p = DirReaddiri(name, end): read an actual directory, caching results
227  * 	as we go.  */
228 static Path *DirReaddiri(const char *, const char *);
229 /* Handles wildcard expansion on a given directory. */
230 static void DirMatchFilesi(const char *, const char *, Path *, Lst);
231 /* Handles simple wildcard expansion on a path. */
232 static void PathMatchFilesi(const char *, const char *, Lst, Lst);
233 /* Handles wildcards expansion except for curly braces. */
234 static void DirExpandWildi(const char *, const char *, Lst, Lst);
235 #define DirExpandWild(s, l1, l2) DirExpandWildi(s, strchr(s, '\0'), l1, l2)
236 /* Handles wildcard expansion including curly braces. */
237 static void DirExpandCurlyi(const char *, const char *, Lst, Lst);
238 
239 /* Debugging: show each word in an expansion list. */
240 static void DirPrintWord(void *);
241 /* Debugging: show a dir name in a path. */
242 static void DirPrintDir(void *);
243 
244 static void
record_stamp(const char * file,TIMESTAMP t)245 record_stamp(const char *file, TIMESTAMP t)
246 {
247     unsigned int	slot;
248     const char		*end = NULL;
249     struct file_stamp	*n;
250 
251     slot = ohash_qlookupi(&mtimes, file, &end);
252     n = ohash_find(&mtimes, slot);
253     if (n)
254 	n->mtime = t;
255     else {
256 	n = ohash_create_entry(&stamp_info, file, &end);
257 	n->mtime = t;
258 	ohash_insert(&mtimes, slot, n);
259     }
260 }
261 
262 static struct file_stamp *
find_stampi(const char * file,const char * efile)263 find_stampi(const char *file, const char *efile)
264 {
265     return ohash_find(&mtimes, ohash_qlookupi(&mtimes, file, &efile));
266 }
267 
268 static void
add_file(Path * p,const char * file)269 add_file(Path *p, const char *file)
270 {
271     unsigned int	slot;
272     const char		*end = NULL;
273     char		*n;
274     struct ohash 	*h = &p->files;
275 
276     slot = ohash_qlookupi(h, file, &end);
277     n = ohash_find(h, slot);
278     if (n == NULL) {
279 	n = ohash_create_entry(&file_info, file, &end);
280 	ohash_insert(h, slot, n);
281     }
282 }
283 
284 static char *
find_file_hashi(Path * p,const char * file,const char * efile,uint32_t hv)285 find_file_hashi(Path *p, const char *file, const char *efile, uint32_t hv)
286 {
287     struct ohash 	*h = &p->files;
288 
289     return ohash_find(h, ohash_lookup_interval(h, file, efile, hv));
290 }
291 
292 static void
free_hash(struct ohash * h)293 free_hash(struct ohash *h)
294 {
295     void		*e;
296     unsigned int	i;
297 
298     for (e = ohash_first(h, &i); e != NULL; e = ohash_next(h, &i))
299 	free(e);
300     ohash_delete(h);
301 }
302 
303 
304 /* Side Effects: cache the current directory */
305 void
Dir_Init(void)306 Dir_Init(void)
307 {
308     const char *dotname = ".";
309 
310     Static_Lst_Init(dirSearchPath);
311     ohash_init(&openDirectories, 4, &dir_info);
312     ohash_init(&mtimes, 4, &stamp_info);
313 
314 
315     dot = DirReaddiri(dotname, dotname+1);
316 
317     if (!dot)
318     	Fatal("Can't access current directory");
319 
320     /* We always need to have dot around, so we increment its reference count
321      * to make sure it won't be destroyed.  */
322     dot->refCount++;
323 }
324 
325 #ifdef CLEANUP
326 void
Dir_End(void)327 Dir_End(void)
328 {
329     struct Path *p;
330     unsigned int i;
331 
332     dot->refCount--;
333     Dir_Destroy(dot);
334     Lst_Destroy(dirSearchPath, Dir_Destroy);
335     for (p = ohash_first(&openDirectories, &i); p != NULL;
336 	p = ohash_next(&openDirectories, &i))
337 	    Dir_Destroy(p);
338     ohash_delete(&openDirectories);
339     free_hash(&mtimes);
340 }
341 #endif
342 
343 
344 /* XXX: This code is not 100% correct ([^]] fails) */
345 bool
Dir_HasWildcardsi(const char * name,const char * ename)346 Dir_HasWildcardsi(const char *name, const char *ename)
347 {
348     const char		*cp;
349     bool		wild = false;
350     unsigned long	brace = 0, bracket = 0;
351 
352     for (cp = name; cp != ename; cp++) {
353 	switch (*cp) {
354 	case '{':
355 	    brace++;
356 	    wild = true;
357 	    break;
358 	case '}':
359 	    if (brace == 0)
360 		return false;
361 	    brace--;
362 	    break;
363 	case '[':
364 	    bracket++;
365 	    wild = true;
366 	    break;
367 	case ']':
368 	    if (bracket == 0)
369 		return false;
370 	    bracket--;
371 	    break;
372 	case '?':
373 	case '*':
374 	    wild = true;
375 	    break;
376 	default:
377 	    break;
378 	}
379     }
380     return wild && bracket == 0 && brace == 0;
381 }
382 
383 /*-
384  *-----------------------------------------------------------------------
385  * DirMatchFilesi --
386  *	Given a pattern and a Path structure, see if any files
387  *	match the pattern and add their names to the 'expansions' list if
388  *	any do. This is incomplete -- it doesn't take care of patterns like
389  *	src / *src / *.c properly (just *.c on any of the directories), but it
390  *	will do for now.
391  *-----------------------------------------------------------------------
392  */
393 static void
DirMatchFilesi(const char * word,const char * eword,Path * p,Lst expansions)394 DirMatchFilesi(const char *word, const char *eword, Path *p, Lst expansions)
395 {
396     unsigned int	search; 	/* Index into the directory's table */
397     const char		*entry; 	/* Current entry in the table */
398     bool		isDot;		/* Is the directory "." ? */
399 
400     isDot = p->name[0] == '.' && p->name[1] == '\0';
401 
402     for (entry = ohash_first(&p->files, &search); entry != NULL;
403 	 entry = ohash_next(&p->files, &search)) {
404 	/* See if the file matches the given pattern. We follow the UNIX
405 	 * convention that dot files will only be found if the pattern
406 	 * begins with a dot (the hashing scheme doesn't hash . or ..,
407 	 * so they won't match `.*'.  */
408 	if (*word != '.' && *entry == '.')
409 	    continue;
410 	if (Str_Matchi(entry, strchr(entry, '\0'), word, eword))
411 	    Lst_AtEnd(expansions,
412 		isDot ? estrdup(entry) : Str_concat(p->name, entry, '/'));
413     }
414 }
415 
416 /*-
417  *-----------------------------------------------------------------------
418  * PathMatchFilesi --
419  *	Traverse directories in the path, calling DirMatchFiles for each.
420  *	NOTE: This doesn't handle patterns in directories.
421  *-----------------------------------------------------------------------
422  */
423 static void
PathMatchFilesi(const char * word,const char * eword,Lst path,Lst expansions)424 PathMatchFilesi(const char *word, const char *eword, Lst path, Lst expansions)
425 {
426     LstNode	ln;		/* Current node */
427 
428     for (ln = Lst_First(path); ln != NULL; ln = Lst_Adv(ln))
429 	DirMatchFilesi(word, eword, (Path *)Lst_Datum(ln), expansions);
430 }
431 
432 static void
DirPrintWord(void * word)433 DirPrintWord(void *word)
434 {
435     printf("%s ", (char *)word);
436 }
437 
438 /*-
439  *-----------------------------------------------------------------------
440  * DirExpandWildi:
441  *	Expand all wild cards in a fully qualified name, except for
442  *	curly braces.
443  * Side-effect:
444  *	Will hash any directory in which a file is found, and add it to
445  *	the path, on the assumption that future lookups will find files
446  *	there as well.
447  *-----------------------------------------------------------------------
448  */
449 static void
DirExpandWildi(const char * word,const char * eword,Lst path,Lst expansions)450 DirExpandWildi(const char *word, const char *eword, Lst path, Lst expansions)
451 {
452     const char	*cp;
453     const char	*slash; 	/* keep track of first slash before wildcard */
454 
455     slash = memchr(word, '/', eword - word);
456     if (slash == NULL) {
457 	/* First the files in dot.  */
458 	DirMatchFilesi(word, eword, dot, expansions);
459 
460 	/* Then the files in every other directory on the path.  */
461 	PathMatchFilesi(word, eword, path, expansions);
462 	return;
463     }
464     /* The thing has a directory component -- find the first wildcard
465      * in the string.  */
466     slash = word;
467     for (cp = word; cp != eword; cp++) {
468 	if (*cp == '/')
469 	    slash = cp;
470 	if (*cp == '?' || *cp == '[' || *cp == '*') {
471 
472 	    if (slash != word) {
473 		char	*dirpath;
474 
475 		/* If the glob isn't in the first component, try and find
476 		 * all the components up to the one with a wildcard.  */
477 		dirpath = Dir_FindFilei(word, slash+1, path);
478 		/* dirpath is null if we can't find the leading component
479 		 * XXX: Dir_FindFile won't find internal components.
480 		 * i.e. if the path contains ../Etc/Object and we're
481 		 * looking for Etc, it won't be found. */
482 		if (dirpath != NULL) {
483 		    char *dp;
484 		    LIST temp;
485 
486 		    dp = strchr(dirpath, '\0');
487 		    while (dp > dirpath && dp[-1] == '/')
488 		    	dp--;
489 
490 		    Lst_Init(&temp);
491 		    Dir_AddDiri(&temp, dirpath, dp);
492 		    PathMatchFilesi(slash+1, eword, &temp, expansions);
493 		    Lst_Destroy(&temp, NOFREE);
494 		}
495 	    } else
496 		/* Start the search from the local directory.  */
497 		PathMatchFilesi(word, eword, path, expansions);
498 	    return;
499 	}
500     }
501     /* Return the file -- this should never happen.  */
502     PathMatchFilesi(word, eword, path, expansions);
503 }
504 
505 /*-
506  *-----------------------------------------------------------------------
507  * DirExpandCurly --
508  *	Expand curly braces like the C shell, and other wildcards as per
509  *	Str_Match.
510  *	XXX: if curly expansion yields a result with
511  *	no wildcards, the result is placed on the list WITHOUT CHECKING
512  *	FOR ITS EXISTENCE.
513  *-----------------------------------------------------------------------
514  */
515 static void
DirExpandCurlyi(const char * word,const char * eword,Lst path,Lst expansions)516 DirExpandCurlyi(const char *word, const char *eword, Lst path, Lst expansions)
517 {
518     const char	*cp2;		/* Pointer for checking for wildcards in
519 				 * expansion before calling Dir_Expand */
520     LIST	curled; 	/* Queue of words to expand */
521     char	*toexpand;	/* Current word to expand */
522     bool	dowild; 	/* Wildcard left after curlies ? */
523 
524     /* Determine once and for all if there is something else going on */
525     dowild = false;
526     for (cp2 = word; cp2 != eword; cp2++)
527 	if (*cp2 == '*' || *cp2 == '?' || *cp2 == '[') {
528 		dowild = true;
529 		break;
530 	}
531 
532     /* Prime queue with copy of initial word */
533     Lst_Init(&curled);
534     Lst_EnQueue(&curled, Str_dupi(word, eword));
535     while ((toexpand = (char *)Lst_DeQueue(&curled)) != NULL) {
536 	const char	*brace;
537 	const char	*start; /* Start of current chunk of brace clause */
538 	const char	*end;	/* Character after the closing brace */
539 	int		bracelevel;
540 				/* Keep track of nested braces. If we hit
541 				 * the right brace with bracelevel == 0,
542 				 * this is the end of the clause. */
543 	size_t		endLen;
544 				/* The length of the ending non-curlied
545 				 * part of the current expansion */
546 
547 	/* End case: no curly left to expand */
548 	brace = strchr(toexpand, '{');
549 	if (brace == NULL) {
550 	    if (dowild) {
551 		DirExpandWild(toexpand, path, expansions);
552 		free(toexpand);
553 	    } else
554 		Lst_AtEnd(expansions, toexpand);
555 	    continue;
556 	}
557 
558 	start = brace+1;
559 
560 	/* Find the end of the brace clause first, being wary of nested brace
561 	 * clauses.  */
562 	for (end = start, bracelevel = 0;; end++) {
563 	    if (*end == '{')
564 		bracelevel++;
565 	    else if (*end == '\0') {
566 		Error("Unterminated {} clause \"%s\"", start);
567 		return;
568 	    } else if (*end == '}' && bracelevel-- == 0)
569 		break;
570 	}
571 	end++;
572 	endLen = strlen(end);
573 
574 	for (;;) {
575 	    char	*file;	/* To hold current expansion */
576 	    const char	*cp;	/* Current position in brace clause */
577 
578 	    /* Find the end of the current expansion */
579 	    for (bracelevel = 0, cp = start;
580 	    	bracelevel != 0 || (*cp != '}' && *cp != ','); cp++) {
581 		if (*cp == '{')
582 		    bracelevel++;
583 		else if (*cp == '}')
584 		    bracelevel--;
585 	    }
586 
587 	    /* Build the current combination and enqueue it.  */
588 	    file = emalloc((brace - toexpand) + (cp - start) + endLen + 1);
589 	    if (brace != toexpand)
590 	    	memcpy(file, toexpand, brace-toexpand);
591 	    if (cp != start)
592 	    	memcpy(file+(brace-toexpand), start, cp-start);
593 	    memcpy(file+(brace-toexpand)+(cp-start), end, endLen + 1);
594 	    Lst_EnQueue(&curled, file);
595 	    if (*cp == '}')
596 	    	break;
597 	    start = cp+1;
598 	}
599 	free(toexpand);
600     }
601 }
602 
603 /* Side effects:
604  * 	Dir_Expandi will hash directories that were not yet visited */
605 void
Dir_Expandi(const char * word,const char * eword,Lst path,Lst expansions)606 Dir_Expandi(const char *word, const char *eword, Lst path, Lst expansions)
607 {
608     const char	*cp;
609 
610     if (DEBUG(DIR)) {
611     	char *s = Str_dupi(word, eword);
612 	printf("expanding \"%s\"...", s);
613 	free(s);
614     }
615 
616     cp = memchr(word, '{', eword - word);
617     if (cp)
618 	DirExpandCurlyi(word, eword, path, expansions);
619     else
620 	DirExpandWildi(word, eword, path, expansions);
621 
622     if (DEBUG(DIR)) {
623 	Lst_Every(expansions, DirPrintWord);
624 	fputc('\n', stdout);
625     }
626 }
627 
628 
629 /*-
630  * Side Effects:
631  *	If the file is found in a directory which is not on the path
632  *	already (either 'name' is absolute or it is a relative path
633  *	[ dir1/.../dirn/file ] which exists below one of the directories
634  *	already on the search path), its directory is added to the end
635  *	of the path on the assumption that there will be more files in
636  *	that directory later on.
637  */
638 char *
Dir_FindFileComplexi(const char * name,const char * ename,Lst path,bool checkCurdirFirst)639 Dir_FindFileComplexi(const char *name, const char *ename, Lst path,
640     bool checkCurdirFirst)
641 {
642     Path		*p;	/* current path member */
643     char		*p1;	/* pointer into p->name */
644     const char		*p2;	/* pointer into name */
645     LstNode		ln;	/* a list element */
646     char		*file;	/* the current filename to check */
647     char		*temp;	/* index into file */
648     const char		*cp;	/* index of first slash, if any */
649     bool		hasSlash;
650     struct stat 	stb;	/* Buffer for stat, if necessary */
651     struct file_stamp	*entry; /* Entry for mtimes table */
652     uint32_t		hv;	/* hash value for last component in file name */
653     char		*q;	/* Str_dupi(name, ename) */
654 
655     /* Find the final component of the name and note whether name has a
656      * slash in it */
657     cp = Str_rchri(name, ename, '/');
658     if (cp) {
659 	hasSlash = true;
660 	cp++;
661     } else {
662 	hasSlash = false;
663 	cp = name;
664     }
665 
666     hv = ohash_interval(cp, &ename);
667 
668     if (DEBUG(DIR))
669 	printf("Searching for %s...", name);
670     /* Unless checkCurDirFirst is false, we always look for
671      * the file in the current directory before anywhere else
672      * and we always return exactly what the caller specified. */
673     if (checkCurdirFirst && (!hasSlash || (cp - name == 2 && *name == '.')) &&
674 	find_file_hashi(dot, cp, ename, hv) != NULL) {
675 	    if (DEBUG(DIR))
676 		printf("in '.'\n");
677 #ifdef DEBUG_DIRECTORY_CACHE
678 	    hits++;
679 	    dot->hits++;
680 #endif
681 	    return Str_dupi(name, ename);
682     }
683 
684     /* Then, we look through all the directories on path, seeking one
685      * containing the final component of name and whose final
686      * component(s) match name's initial component(s).
687      * If found, we concatenate the directory name and the
688      * final component and return the resulting string.  */
689     for (ln = Lst_First(path); ln != NULL; ln = Lst_Adv(ln)) {
690 	p = (Path *)Lst_Datum(ln);
691 	if (DEBUG(DIR))
692 	    printf("%s...", p->name);
693 	if (find_file_hashi(p, cp, ename, hv) != NULL) {
694 	    if (DEBUG(DIR))
695 		printf("here...");
696 	    if (hasSlash) {
697 		/* If the name had a slash, its initial components and p's
698 		 * final components must match. This is false if a mismatch
699 		 * is encountered before all of the initial components
700 		 * have been checked (p2 > name at the end of the loop), or
701 		 * we matched only part of one of the components of p
702 		 * along with all the rest of them (*p1 != '/').  */
703 		p1 = p->name + strlen(p->name) - 1;
704 		p2 = cp - 2;
705 		while (p2 >= name && p1 >= p->name && *p1 == *p2) {
706 		    p1--;
707 		    p2--;
708 		}
709 		if (p2 >= name || (p1 >= p->name && *p1 != '/')) {
710 		    if (DEBUG(DIR))
711 			printf("component mismatch -- continuing...");
712 		    continue;
713 		}
714 	    }
715 	    file = Str_concati(p->name, strchr(p->name, '\0'), cp, ename, '/');
716 	    if (DEBUG(DIR))
717 		printf("returning %s\n", file);
718 #ifdef DEBUG_DIRECTORY_CACHE
719 	    p->hits++;
720 	    hits++;
721 #endif
722 	    return file;
723 	} else if (hasSlash) {
724 	    /* If the file has a leading path component and that component
725 	     * exactly matches the entire name of the current search
726 	     * directory, we assume the file doesn't exist and return NULL.  */
727 	    for (p1 = p->name, p2 = name; *p1 && *p1 == *p2; p1++, p2++)
728 		continue;
729 	    if (*p1 == '\0' && p2 == cp - 1) {
730 		if (DEBUG(DIR))
731 		    printf("has to be here but isn't -- returning NULL\n");
732 		return NULL;
733 	    }
734 	}
735     }
736 
737     /* We didn't find the file on any existing member of the path.
738      * If the name doesn't contain a slash, end of story.
739      * If it does contain a slash, however, it could be in a subdirectory
740      * of one of the members of the search path. (eg., for path=/usr/include
741      * and name=sys/types.h, the above search fails to turn up types.h
742      * in /usr/include, even though /usr/include/sys/types.h exists).
743      *
744      * We only perform this look-up for non-absolute file names.
745      *
746      * Whenever we score a hit, we assume there will be more matches from
747      * that directory, and append all but the last component of the
748      * resulting name onto the search path. */
749     if (!hasSlash) {
750 	if (DEBUG(DIR))
751 	    printf("failed.\n");
752 #ifdef DEBUG_DIRECTORY_CACHE
753 	misses++;
754 #endif
755 	return NULL;
756     }
757 
758     if (*name != '/') {
759 	bool checkedDot = false;
760 
761 	if (DEBUG(DIR))
762 	    printf("failed. Trying subdirectories...");
763 	for (ln = Lst_First(path); ln != NULL; ln = Lst_Adv(ln)) {
764 	    p = (Path *)Lst_Datum(ln);
765 	    if (p != dot)
766 		file = Str_concati(p->name, strchr(p->name, '\0'), name, ename, '/');
767 	    else {
768 		/* Checking in dot -- DON'T put a leading ./ on the thing.  */
769 		file = Str_dupi(name, ename);
770 		checkedDot = true;
771 	    }
772 	    if (DEBUG(DIR))
773 		printf("checking %s...", file);
774 
775 	    if (stat(file, &stb) == 0) {
776 		TIMESTAMP mtime;
777 
778 		ts_set_from_stat(stb, mtime);
779 		if (DEBUG(DIR))
780 		    printf("got it.\n");
781 
782 		/* We've found another directory to search. We know there
783 		 * is a slash in 'file'. We call Dir_AddDiri to add the
784 		 * new directory onto the existing search path. Once
785 		 * that's done, we return the file name, knowing that
786 		 * should a file in this directory ever be referenced again
787 		 * in such a manner, we will find it without having to do
788 		 * numerous access calls.  */
789 		temp = strrchr(file, '/');
790 		Dir_AddDiri(path, file, temp);
791 
792 		/* Save the modification time so if it's needed, we don't have
793 		 * to fetch it again.  */
794 		if (DEBUG(DIR))
795 		    printf("Caching %s for %s\n", Targ_FmtTime(mtime),
796 			    file);
797 		record_stamp(file, mtime);
798 #ifdef DEBUG_DIRECTORY_CACHE
799 		nearmisses++;
800 #endif
801 		return file;
802 	    } else
803 		free(file);
804 	}
805 
806 	if (DEBUG(DIR))
807 	    printf("failed. ");
808 
809 	if (checkedDot) {
810 	    /* Already checked by the given name, since . was in the path,
811 	     * so no point in proceeding...  */
812 	    if (DEBUG(DIR))
813 		printf("Checked . already, returning NULL\n");
814 	    return NULL;
815 	}
816     }
817 
818     /* Didn't find it that way, either. Last resort: look for the file
819      * in the global mtime cache, then on the disk.
820      * If this doesn't succeed, we finally return a NULL pointer.
821      *
822      * We cannot add this directory onto the search path because
823      * of this amusing case:
824      * $(INSTALLDIR)/$(FILE): $(FILE)
825      *
826      * $(FILE) exists in $(INSTALLDIR) but not in the current one.
827      * When searching for $(FILE), we will find it in $(INSTALLDIR)
828      * b/c we added it here. This is not good...  */
829     q = Str_dupi(name, ename);
830     if (DEBUG(DIR))
831 	printf("Looking for \"%s\"...", q);
832 
833 #ifdef DEBUG_DIRECTORY_CACHE
834     bigmisses++;
835 #endif
836     entry = find_stampi(name, ename);
837     if (entry != NULL) {
838 	if (DEBUG(DIR))
839 	    printf("got it (in mtime cache)\n");
840 	return q;
841     } else if (stat(q, &stb) == 0) {
842 	TIMESTAMP mtime;
843 
844 	ts_set_from_stat(stb, mtime);
845 	if (DEBUG(DIR))
846 	    printf("Caching %s for %s\n", Targ_FmtTime(mtime),
847 		    q);
848 	record_stamp(q, mtime);
849 	return q;
850     } else {
851 	if (DEBUG(DIR))
852 	    printf("failed. Returning NULL\n");
853 	free(q);
854 	return NULL;
855     }
856 }
857 
858 /* Read a directory, either from the disk, or from the cache.  */
859 static Path *
DirReaddiri(const char * name,const char * ename)860 DirReaddiri(const char *name, const char *ename)
861 {
862     Path		*p;	/* pointer to new Path structure */
863     DIR 		*d;	/* for reading directory */
864     struct dirent	*dp;	/* entry in directory */
865     unsigned int	slot;
866 
867     slot = ohash_qlookupi(&openDirectories, name, &ename);
868     p = ohash_find(&openDirectories, slot);
869 
870     if (p != NULL)
871 	return p;
872 
873     p = ohash_create_entry(&dir_info, name, &ename);
874 #ifdef DEBUG_DIRECTORY_CACHE
875     p->hits = 0;
876 #endif
877     p->refCount = 0;
878     ohash_init(&p->files, 4, &file_info);
879 
880     if (DEBUG(DIR)) {
881 	printf("Caching %s...", p->name);
882 	fflush(stdout);
883     }
884 
885     if ((d = opendir(p->name)) == NULL)
886 	return NULL;
887 
888     while ((dp = readdir(d)) != NULL) {
889 	if (dp->d_name[0] == '.' &&
890 	    (dp->d_name[1] == '\0' ||
891 	    	(dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
892 		continue;
893 	add_file(p, dp->d_name);
894     }
895     (void)closedir(d);
896     if (DEBUG(DIR))
897 	printf("done\n");
898 
899     ohash_insert(&openDirectories, slot, p);
900     return p;
901 }
902 
903 /*-
904  *-----------------------------------------------------------------------
905  * Dir_AddDiri --
906  *	Add the given name to the end of the given path. The order of
907  *	the arguments is backwards so ParseDoDependency can do a
908  *	Lst_ForEach of its list of paths...
909  *
910  * Side Effects:
911  *	A structure is added to the list and the directory is
912  *	read and hashed.
913  *-----------------------------------------------------------------------
914  */
915 
916 void
Dir_AddDiri(Lst path,const char * name,const char * ename)917 Dir_AddDiri(Lst path, const char *name, const char *ename)
918 {
919     Path	*p;	/* pointer to new Path structure */
920 
921     p = DirReaddiri(name, ename);
922     if (p == NULL)
923 	return;
924     if (p->refCount == 0)
925 	Lst_AtEnd(path, p);
926     else if (!Lst_AddNew(path, p))
927 	return;
928     p->refCount++;
929 }
930 
931 /*-
932  *-----------------------------------------------------------------------
933  * Dir_CopyDir --
934  *	Callback function for duplicating a search path via Lst_Duplicate.
935  *	Ups the reference count for the directory.
936  *
937  * Results:
938  *	Returns the Path it was given.
939  *
940  * Side Effects:
941  *	The refCount of the path is incremented.
942  *-----------------------------------------------------------------------
943  */
944 void *
Dir_CopyDir(void * p)945 Dir_CopyDir(void *p)
946 {
947     ((Path *)p)->refCount++;
948     return p;
949 }
950 
951 /*-
952  *-----------------------------------------------------------------------
953  * Dir_MakeFlags --
954  *	Make a string by taking all the directories in the given search
955  *	path and preceding them by the given flag. Used by the suffix
956  *	module to create variables for compilers based on suffix search
957  *	paths.
958  *
959  * Results:
960  *	The string mentioned above. Note that there is no space between
961  *	the given flag and each directory. The empty string is returned if
962  *	Things don't go well.
963  *-----------------------------------------------------------------------
964  */
965 char *
Dir_MakeFlags(const char * flag,Lst path)966 Dir_MakeFlags(const char *flag, Lst path)
967 {
968     LstNode	  ln;	  /* the node of the current directory */
969     BUFFER	  buf;
970 
971     Buf_Init(&buf, 0);
972 
973     for (ln = Lst_First(path); ln != NULL; ln = Lst_Adv(ln)) {
974 	    Buf_AddString(&buf, flag);
975 	    Buf_AddString(&buf, ((Path *)Lst_Datum(ln))->name);
976 	    Buf_AddSpace(&buf);
977     }
978 
979     return Buf_Retrieve(&buf);
980 }
981 
982 /*-
983  *-----------------------------------------------------------------------
984  * Dir_Destroy --
985  *	Nuke a directory descriptor, if possible. Callback procedure
986  *	for the suffixes module when destroying a search path.
987  *
988  * Side Effects:
989  *	If no other path references this directory (refCount == 0),
990  *	the Path and all its data are freed.
991  *-----------------------------------------------------------------------
992  */
993 void
Dir_Destroy(void * pp)994 Dir_Destroy(void *pp)
995 {
996     Path	*p = (Path *)pp;
997 
998     if (--p->refCount == 0) {
999 	ohash_remove(&openDirectories, ohash_qlookup(&openDirectories, p->name));
1000 	free_hash(&p->files);
1001 	free(p);
1002     }
1003 }
1004 
1005 /*-
1006  *-----------------------------------------------------------------------
1007  * Dir_Concat --
1008  *	Concatenate two paths, adding the second to the end of the first.
1009  *	Makes sure to avoid duplicates.
1010  *
1011  * Side Effects:
1012  *	Reference counts for added dirs are upped.
1013  *-----------------------------------------------------------------------
1014  */
1015 void
Dir_Concat(Lst path1,Lst path2)1016 Dir_Concat(Lst path1, Lst path2)
1017 {
1018     LstNode	ln;
1019     Path	*p;
1020 
1021     for (ln = Lst_First(path2); ln != NULL; ln = Lst_Adv(ln)) {
1022 	p = (Path *)Lst_Datum(ln);
1023 	if (Lst_AddNew(path1, p))
1024 	    p->refCount++;
1025     }
1026 }
1027 
1028 #ifdef DEBUG_DIRECTORY_CACHE
1029 void
Dir_PrintDirectories(void)1030 Dir_PrintDirectories(void)
1031 {
1032     Path		*p;
1033     unsigned int	i;
1034 
1035     printf("#*** Directory Cache:\n");
1036     printf("# Stats: %d hits %d misses %d near misses %d losers (%d%%)\n",
1037 	      hits, misses, nearmisses, bigmisses,
1038 	      (hits+bigmisses+nearmisses ?
1039 	       hits * 100 / (hits + bigmisses + nearmisses) : 0));
1040     printf("# %-20s referenced\thits\n", "directory");
1041     for (p = ohash_first(&openDirectories, &i); p != NULL;
1042 	p = ohash_next(&openDirectories, &i))
1043 	    printf("# %-20s %10d\t%4d\n", p->name, p->refCount, p->hits);
1044 }
1045 #endif
1046 
1047 static void
DirPrintDir(void * p)1048 DirPrintDir(void *p)
1049 {
1050     printf("%s ", ((Path *)p)->name);
1051 }
1052 
1053 void
Dir_PrintPath(Lst path)1054 Dir_PrintPath(Lst path)
1055 {
1056     Lst_Every(path, DirPrintDir);
1057 }
1058 
1059 TIMESTAMP
Dir_MTime(GNode * gn)1060 Dir_MTime(GNode *gn)
1061 {
1062     char	  *fullName;  /* the full pathname of name */
1063     struct stat   stb;	      /* buffer for finding the mod time */
1064     struct file_stamp
1065 		  *entry;
1066     unsigned int  slot;
1067     TIMESTAMP	  mtime;
1068 
1069     if (gn->type & OP_ARCHV)
1070 	return Arch_MTime(gn);
1071 
1072     if (gn->path == NULL) {
1073 	fullName = Dir_FindFile(gn->name, dirSearchPath);
1074 	if (fullName == NULL)
1075 	    fullName = estrdup(gn->name);
1076     } else
1077 	fullName = gn->path;
1078 
1079     slot = ohash_qlookup(&mtimes, fullName);
1080     entry = ohash_find(&mtimes, slot);
1081     if (entry != NULL) {
1082 	/* Only do this once -- the second time folks are checking to
1083 	 * see if the file was actually updated, so we need to actually go
1084 	 * to the file system.	*/
1085 	if (DEBUG(DIR))
1086 	    printf("Using cached time %s for %s\n",
1087 		    Targ_FmtTime(entry->mtime), fullName);
1088 	mtime = entry->mtime;
1089 	free(entry);
1090 	ohash_remove(&mtimes, slot);
1091     } else if (stat(fullName, &stb) == 0)
1092 	ts_set_from_stat(stb, mtime);
1093     else {
1094 	if (gn->type & OP_MEMBER) {
1095 	    if (fullName != gn->path)
1096 		free(fullName);
1097 	    return Arch_MemMTime(gn);
1098 	} else
1099 	    ts_set_out_of_date(mtime);
1100     }
1101     if (fullName && gn->path == NULL)
1102 	gn->path = fullName;
1103 
1104     gn->mtime = mtime;
1105     return gn->mtime;
1106 }
1107