dir.c revision 1.258 1 /* $NetBSD: dir.c,v 1.258 2021/01/23 11:06:04 rillig Exp $ */
2
3 /*
4 * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * Adam de Boor.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35 /*
36 * Copyright (c) 1988, 1989 by Adam de Boor
37 * Copyright (c) 1989 by Berkeley Softworks
38 * All rights reserved.
39 *
40 * This code is derived from software contributed to Berkeley by
41 * Adam de Boor.
42 *
43 * Redistribution and use in source and binary forms, with or without
44 * modification, are permitted provided that the following conditions
45 * are met:
46 * 1. Redistributions of source code must retain the above copyright
47 * notice, this list of conditions and the following disclaimer.
48 * 2. Redistributions in binary form must reproduce the above copyright
49 * notice, this list of conditions and the following disclaimer in the
50 * documentation and/or other materials provided with the distribution.
51 * 3. All advertising materials mentioning features or use of this software
52 * must display the following acknowledgement:
53 * This product includes software developed by the University of
54 * California, Berkeley and its contributors.
55 * 4. Neither the name of the University nor the names of its contributors
56 * may be used to endorse or promote products derived from this software
57 * without specific prior written permission.
58 *
59 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
60 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
61 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
62 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
63 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
64 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
65 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
66 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
67 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
68 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
69 * SUCH DAMAGE.
70 */
71
72 /*
73 * Directory searching using wildcards and/or normal names.
74 * Used both for source wildcarding in the makefile and for finding
75 * implicit sources.
76 *
77 * The interface for this module is:
78 * Dir_Init Initialize the module.
79 *
80 * Dir_InitCur Set the cur CachedDir.
81 *
82 * Dir_InitDot Set the dot CachedDir.
83 *
84 * Dir_End Clean up the module.
85 *
86 * Dir_SetPATH Set ${.PATH} to reflect state of dirSearchPath.
87 *
88 * Dir_HasWildcards
89 * Returns TRUE if the name given it needs to
90 * be wildcard-expanded.
91 *
92 * SearchPath_Expand
93 * Expand a filename pattern to find all matching files
94 * from the search path.
95 *
96 * Dir_FindFile Searches for a file on a given search path.
97 * If it exists, the entire path is returned.
98 * Otherwise NULL is returned.
99 *
100 * Dir_FindHereOrAbove
101 * Search for a path in the current directory and
102 * then all the directories above it in turn until
103 * the path is found or we reach the root ("/").
104 *
105 * Dir_UpdateMTime
106 * Update the modification time and path of a node with
107 * data from the file corresponding to the node.
108 *
109 * Dir_AddDir Add a directory to a search path.
110 *
111 * SearchPath_ToFlags
112 * Given a search path and a command flag, create
113 * a string with each of the directories in the path
114 * preceded by the command flag and all of them
115 * separated by a space.
116 *
117 * Dir_Destroy Destroy an element of a search path. Frees up all
118 * things that can be freed for the element as long
119 * as the element is no longer referenced by any other
120 * search path.
121 *
122 * SearchPath_Clear
123 * Resets a search path to the empty list.
124 *
125 * For debugging:
126 * Dir_PrintDirectories
127 * Print stats about the directory cache.
128 */
129
130 #include <sys/types.h>
131 #include <sys/stat.h>
132
133 #include <dirent.h>
134 #include <errno.h>
135
136 #include "make.h"
137 #include "dir.h"
138 #include "job.h"
139
140 /* "@(#)dir.c 8.2 (Berkeley) 1/2/94" */
141 MAKE_RCSID("$NetBSD: dir.c,v 1.258 2021/01/23 11:06:04 rillig Exp $");
142
143 /*
144 * A search path is a list of CachedDir structures. A CachedDir has in it the
145 * name of the directory and the names of all the files in the directory.
146 * This is used to cut down on the number of system calls necessary to find
147 * implicit dependents and their like. Since these searches are made before
148 * any actions are taken, we need not worry about the directory changing due
149 * to creation commands. If this hampers the style of some makefiles, they
150 * must be changed.
151 *
152 * All previously-read directories are kept in openDirs, which is checked
153 * first before a directory is opened.
154 *
155 * The need for the caching of whole directories is brought about by the
156 * multi-level transformation code in suff.c, which tends to search for far
157 * more files than regular make does. In the initial implementation, the
158 * amount of time spent performing "stat" calls was truly astronomical.
159 * The problem with caching at the start is, of course, that pmake doesn't
160 * then detect changes to these directories during the course of the make.
161 * Three possibilities suggest themselves:
162 *
163 * 1) just use stat to test for a file's existence. As mentioned above,
164 * this is very inefficient due to the number of checks engendered by
165 * the multi-level transformation code.
166 *
167 * 2) use readdir() and company to search the directories, keeping them
168 * open between checks. I have tried this and while it didn't slow down
169 * the process too much, it could severely affect the amount of
170 * parallelism available as each directory open would take another file
171 * descriptor out of play for handling I/O for another job. Given that
172 * it is only recently (as of 1993 or earlier) that UNIX OS's have taken
173 * to allowing more than 20 or 32 file descriptors for a process, this
174 * doesn't seem acceptable to me.
175 *
176 * 3) record the mtime of the directory in the CachedDir structure and
177 * verify the directory hasn't changed since the contents were cached.
178 * This will catch the creation or deletion of files, but not the
179 * updating of files. However, since it is the creation and deletion
180 * that is the problem, this could be a good thing to do. Unfortunately,
181 * if the directory (say ".") were fairly large and changed fairly
182 * frequently, the constant reloading could seriously degrade
183 * performance. It might be good in such cases to keep track of the
184 * number of reloadings and if the number goes over a (small) limit,
185 * resort to using stat in its place.
186 *
187 * An additional thing to consider is that pmake is used primarily to create
188 * C programs and until recently (as of 1993 or earlier) pcc-based compilers
189 * refused to allow you to specify where the resulting object file should be
190 * placed. This forced all objects to be created in the current directory.
191 * This isn't meant as a full excuse, just an explanation of some of the
192 * reasons for the caching used here.
193 *
194 * One more note: the location of a target's file is only performed on the
195 * downward traversal of the graph and then only for terminal nodes in the
196 * graph. This could be construed as wrong in some cases, but prevents
197 * inadvertent modification of files when the "installed" directory for a
198 * file is provided in the search path.
199 *
200 * Another data structure maintained by this module is an mtime cache used
201 * when the searching of cached directories fails to find a file. In the past,
202 * Dir_FindFile would simply perform an access() call in such a case to
203 * determine if the file could be found using just the name given. When this
204 * hit, however, all that was gained was the knowledge that the file existed.
205 * Given that an access() is essentially a stat() without the copyout() call,
206 * and that the same filesystem overhead would have to be incurred in
207 * Dir_MTime, it made sense to replace the access() with a stat() and record
208 * the mtime in a cache for when Dir_UpdateMTime was actually called.
209 */
210
211
212 /* A cache for the filenames in a directory. */
213 struct CachedDir {
214 /*
215 * Name of directory, either absolute or relative to the current
216 * directory. The name is not normalized in any way, that is, "."
217 * and "./." are different.
218 *
219 * Not sure what happens when .CURDIR is assigned a new value; see
220 * Parse_DoVar.
221 */
222 char *name;
223
224 /*
225 * The number of SearchPaths that refer to this directory.
226 * Plus the number of global variables that refer to this directory.
227 * References from openDirs do not count though.
228 */
229 int refCount;
230
231 /* The number of times a file in this directory has been found. */
232 int hits;
233
234 /* The names of the directory entries. */
235 HashSet files;
236 };
237
238 typedef List CachedDirList;
239 typedef ListNode CachedDirListNode;
240
241 typedef ListNode SearchPathNode;
242
243 /* A list of cached directories, with fast lookup by directory name. */
244 typedef struct OpenDirs {
245 CachedDirList list;
246 HashTable /* of CachedDirListNode */ table;
247 } OpenDirs;
248
249 typedef enum CachedStatsFlags {
250 CST_NONE = 0,
251 CST_LSTAT = 1 << 0, /* call lstat(2) instead of stat(2) */
252 CST_UPDATE = 1 << 1 /* ignore existing cached entry */
253 } CachedStatsFlags;
254
255
256 SearchPath dirSearchPath = LST_INIT; /* main search path */
257
258 static OpenDirs openDirs; /* all cached directories */
259
260 /*
261 * Variables for gathering statistics on the efficiency of the caching
262 * mechanism.
263 */
264 static int hits; /* Found in directory cache */
265 static int misses; /* Sad, but not evil misses */
266 static int nearmisses; /* Found under search path */
267 static int bigmisses; /* Sought by itself */
268
269 /* The cached contents of ".", the relative current directory. */
270 static CachedDir *dot = NULL;
271 /* The cached contents of the absolute current directory. */
272 static CachedDir *cur = NULL;
273 /* A fake path entry indicating we need to look for '.' last. */
274 static CachedDir *dotLast = NULL;
275
276 /*
277 * Results of doing a last-resort stat in Dir_FindFile -- if we have to go to
278 * the system to find the file, we might as well have its mtime on record.
279 *
280 * XXX: If this is done way early, there's a chance other rules will have
281 * already updated the file, in which case we'll update it again. Generally,
282 * there won't be two rules to update a single file, so this should be ok,
283 * but...
284 */
285 static HashTable mtimes;
286
287 static HashTable lmtimes; /* same as mtimes but for lstat */
288
289
290 static void OpenDirs_Remove(OpenDirs *, const char *);
291
292
293 static CachedDir *
294 CachedDir_New(const char *name)
295 {
296 CachedDir *dir = bmake_malloc(sizeof *dir);
297
298 dir->name = bmake_strdup(name);
299 dir->refCount = 0;
300 dir->hits = 0;
301 HashSet_Init(&dir->files);
302
303 #ifdef DEBUG_REFCNT
304 DEBUG2(DIR, "CachedDir %p new for \"%s\"\n", dir, dir->name);
305 #endif
306
307 return dir;
308 }
309
310 static CachedDir *
311 CachedDir_Ref(CachedDir *dir)
312 {
313 dir->refCount++;
314
315 #ifdef DEBUG_REFCNT
316 DEBUG3(DIR, "CachedDir %p ++ %d for \"%s\"\n",
317 dir, dir->refCount, dir->name);
318 #endif
319
320 return dir;
321 }
322
323 static void
324 CachedDir_Unref(CachedDir *dir)
325 {
326 dir->refCount--;
327
328 #ifdef DEBUG_REFCNT
329 DEBUG3(DIR, "CachedDir %p -- %d for \"%s\"\n",
330 dir, dir->refCount, dir->name);
331 #endif
332
333 if (dir->refCount > 0)
334 return;
335
336 #ifdef DEBUG_REFCNT
337 DEBUG2(DIR, "CachedDir %p free for \"%s\"\n", dir, dir->name);
338 #endif
339
340 OpenDirs_Remove(&openDirs, dir->name);
341
342 free(dir->name);
343 HashSet_Done(&dir->files);
344 free(dir);
345 }
346
347 /* Update the value of the CachedDir variable, updating the reference counts. */
348 static void
349 CachedDir_Assign(CachedDir **var, CachedDir *dir)
350 {
351 CachedDir *prev;
352
353 prev = *var;
354 *var = dir;
355 if (dir != NULL)
356 CachedDir_Ref(dir);
357 if (prev != NULL)
358 CachedDir_Unref(prev);
359 }
360
361 static void
362 OpenDirs_Init(OpenDirs *odirs)
363 {
364 Lst_Init(&odirs->list);
365 HashTable_Init(&odirs->table);
366 }
367
368 #ifdef CLEANUP
369 static void
370 OpenDirs_Done(OpenDirs *odirs)
371 {
372 CachedDirListNode *ln = odirs->list.first;
373 DEBUG1(DIR, "OpenDirs_Done: %u entries to remove\n",
374 odirs->table.numEntries);
375 while (ln != NULL) {
376 CachedDirListNode *next = ln->next;
377 CachedDir *dir = ln->datum;
378 DEBUG2(DIR, "OpenDirs_Done: refCount %d for \"%s\"\n",
379 dir->refCount, dir->name);
380 CachedDir_Unref(dir); /* removes the dir from odirs->list */
381 ln = next;
382 }
383 Lst_Done(&odirs->list);
384 HashTable_Done(&odirs->table);
385 }
386 #endif
387
388 static CachedDir *
389 OpenDirs_Find(OpenDirs *odirs, const char *name)
390 {
391 CachedDirListNode *ln = HashTable_FindValue(&odirs->table, name);
392 return ln != NULL ? ln->datum : NULL;
393 }
394
395 static void
396 OpenDirs_Add(OpenDirs *odirs, CachedDir *cdir)
397 {
398 if (HashTable_FindEntry(&odirs->table, cdir->name) != NULL)
399 return;
400 Lst_Append(&odirs->list, cdir);
401 HashTable_Set(&odirs->table, cdir->name, odirs->list.last);
402 }
403
404 static void
405 OpenDirs_Remove(OpenDirs *odirs, const char *name)
406 {
407 HashEntry *he = HashTable_FindEntry(&odirs->table, name);
408 CachedDirListNode *ln;
409 if (he == NULL)
410 return;
411 ln = HashEntry_Get(he);
412 HashTable_DeleteEntry(&odirs->table, he);
413 Lst_Remove(&odirs->list, ln);
414 }
415
416 /*
417 * Returns 0 and the result of stat(2) or lstat(2) in *out_cst,
418 * or -1 on error.
419 */
420 static int
421 cached_stats(const char *pathname, struct cached_stat *out_cst,
422 CachedStatsFlags flags)
423 {
424 HashTable *tbl = flags & CST_LSTAT ? &lmtimes : &mtimes;
425 struct stat sys_st;
426 struct cached_stat *cst;
427 int rc;
428
429 if (pathname == NULL || pathname[0] == '\0')
430 return -1; /* This can happen in meta mode. */
431
432 cst = HashTable_FindValue(tbl, pathname);
433 if (cst != NULL && !(flags & CST_UPDATE)) {
434 *out_cst = *cst;
435 DEBUG2(DIR, "Using cached time %s for %s\n",
436 Targ_FmtTime(cst->cst_mtime), pathname);
437 return 0;
438 }
439
440 rc = (flags & CST_LSTAT ? lstat : stat)(pathname, &sys_st);
441 if (rc == -1)
442 return -1; /* don't cache negative lookups */
443
444 if (sys_st.st_mtime == 0)
445 sys_st.st_mtime = 1; /* avoid confusion with missing file */
446
447 if (cst == NULL) {
448 cst = bmake_malloc(sizeof *cst);
449 HashTable_Set(tbl, pathname, cst);
450 }
451
452 cst->cst_mtime = sys_st.st_mtime;
453 cst->cst_mode = sys_st.st_mode;
454
455 *out_cst = *cst;
456 DEBUG2(DIR, " Caching %s for %s\n",
457 Targ_FmtTime(sys_st.st_mtime), pathname);
458
459 return 0;
460 }
461
462 int
463 cached_stat(const char *pathname, struct cached_stat *cst)
464 {
465 return cached_stats(pathname, cst, CST_NONE);
466 }
467
468 int
469 cached_lstat(const char *pathname, struct cached_stat *cst)
470 {
471 return cached_stats(pathname, cst, CST_LSTAT);
472 }
473
474 /* Initialize the directories module. */
475 void
476 Dir_Init(void)
477 {
478 OpenDirs_Init(&openDirs);
479 HashTable_Init(&mtimes);
480 HashTable_Init(&lmtimes);
481 CachedDir_Assign(&dotLast, CachedDir_New(".DOTLAST"));
482 }
483
484 /*
485 * Called by Dir_InitDir and whenever .CURDIR is assigned to.
486 */
487 void
488 Dir_InitCur(const char *cdname)
489 {
490 CachedDir *dir;
491
492 if (cdname == NULL)
493 return;
494
495 /*
496 * Our build directory is not the same as our source directory.
497 * Keep this one around too.
498 */
499 dir = Dir_AddDir(NULL, cdname);
500 if (dir == NULL)
501 return;
502
503 CachedDir_Assign(&cur, dir);
504 }
505
506 /*
507 * (Re)initialize "dot" (current/object directory) path hash.
508 * Some directories may be cached.
509 */
510 void
511 Dir_InitDot(void)
512 {
513 CachedDir *dir;
514
515 dir = Dir_AddDir(NULL, ".");
516 if (dir == NULL) {
517 Error("Cannot open `.' (%s)", strerror(errno));
518 exit(2); /* Not 1 so -q can distinguish error */
519 }
520
521 CachedDir_Assign(&dot, dir);
522
523 Dir_SetPATH(); /* initialize */
524 }
525
526 /* Clean up the directories module. */
527 void
528 Dir_End(void)
529 {
530 #ifdef CLEANUP
531 CachedDir_Assign(&cur, NULL);
532 CachedDir_Assign(&dot, NULL);
533 CachedDir_Assign(&dotLast, NULL);
534 SearchPath_Clear(&dirSearchPath);
535 OpenDirs_Done(&openDirs);
536 HashTable_Done(&mtimes);
537 HashTable_Done(&lmtimes);
538 #endif
539 }
540
541 /*
542 * We want ${.PATH} to indicate the order in which we will actually
543 * search, so we rebuild it after any .PATH: target.
544 * This is the simplest way to deal with the effect of .DOTLAST.
545 */
546 void
547 Dir_SetPATH(void)
548 {
549 CachedDirListNode *ln;
550 Boolean seenDotLast = FALSE; /* true if we should search '.' last */
551
552 Var_Delete(".PATH", VAR_GLOBAL);
553
554 if ((ln = dirSearchPath.first) != NULL) {
555 CachedDir *dir = ln->datum;
556 if (dir == dotLast) {
557 seenDotLast = TRUE;
558 Var_Append(".PATH", dotLast->name, VAR_GLOBAL);
559 }
560 }
561
562 if (!seenDotLast) {
563 if (dot != NULL)
564 Var_Append(".PATH", dot->name, VAR_GLOBAL);
565 if (cur != NULL)
566 Var_Append(".PATH", cur->name, VAR_GLOBAL);
567 }
568
569 for (ln = dirSearchPath.first; ln != NULL; ln = ln->next) {
570 CachedDir *dir = ln->datum;
571 if (dir == dotLast)
572 continue;
573 if (dir == dot && seenDotLast)
574 continue;
575 Var_Append(".PATH", dir->name, VAR_GLOBAL);
576 }
577
578 if (seenDotLast) {
579 if (dot != NULL)
580 Var_Append(".PATH", dot->name, VAR_GLOBAL);
581 if (cur != NULL)
582 Var_Append(".PATH", cur->name, VAR_GLOBAL);
583 }
584 }
585
586 /*
587 * See if the given name has any wildcard characters in it and all braces and
588 * brackets are properly balanced.
589 *
590 * XXX: This code is not 100% correct ([^]] fails etc.). I really don't think
591 * that make(1) should be expanding patterns, because then you have to set a
592 * mechanism for escaping the expansion!
593 *
594 * Return TRUE if the word should be expanded, FALSE otherwise.
595 */
596 Boolean
597 Dir_HasWildcards(const char *name)
598 {
599 const char *p;
600 Boolean wild = FALSE;
601 int braces = 0, brackets = 0;
602
603 for (p = name; *p != '\0'; p++) {
604 switch (*p) {
605 case '{':
606 braces++;
607 wild = TRUE;
608 break;
609 case '}':
610 braces--;
611 break;
612 case '[':
613 brackets++;
614 wild = TRUE;
615 break;
616 case ']':
617 brackets--;
618 break;
619 case '?':
620 case '*':
621 wild = TRUE;
622 break;
623 default:
624 break;
625 }
626 }
627 return wild && brackets == 0 && braces == 0;
628 }
629
630 /*
631 * See if any files match the pattern and add their names to the 'expansions'
632 * list if they do.
633 *
634 * This is incomplete -- wildcards are only expanded in the final path
635 * component, but not in directories like src/lib*c/file*.c, but it
636 * will do for now (now being 1993 until at least 2020). To expand these,
637 * use the ':sh' variable modifier such as in ${:!echo src/lib*c/file*.c!}.
638 *
639 * Input:
640 * pattern Pattern to look for
641 * dir Directory to search
642 * expansion Place to store the results
643 */
644 static void
645 DirMatchFiles(const char *pattern, CachedDir *dir, StringList *expansions)
646 {
647 const char *dirName = dir->name;
648 Boolean isDot = dirName[0] == '.' && dirName[1] == '\0';
649 HashIter hi;
650
651 /*
652 * XXX: Iterating over all hash entries is inefficient. If the
653 * pattern is a plain string without any wildcards, a direct lookup
654 * is faster.
655 */
656
657 HashIter_InitSet(&hi, &dir->files);
658 while (HashIter_Next(&hi) != NULL) {
659 const char *base = hi.entry->key;
660
661 if (!Str_Match(base, pattern))
662 continue;
663
664 /*
665 * Follow the UNIX convention that dot files are only found
666 * if the pattern begins with a dot. The pattern '.*' does
667 * not match '.' or '..' since these are not included in the
668 * directory cache.
669 *
670 * This means that the pattern '[a-z.]*' does not find
671 * '.file', which is consistent with bash, NetBSD sh and csh.
672 */
673 if (base[0] == '.' && pattern[0] != '.')
674 continue;
675
676 {
677 char *fullName = isDot
678 ? bmake_strdup(base)
679 : str_concat3(dirName, "/", base);
680 Lst_Append(expansions, fullName);
681 }
682 }
683 }
684
685 /*
686 * Find the next closing brace in the string, taking nested braces into
687 * account.
688 */
689 static const char *
690 closing_brace(const char *p)
691 {
692 int nest = 0;
693 while (*p != '\0') {
694 if (*p == '}' && nest == 0)
695 break;
696 if (*p == '{')
697 nest++;
698 if (*p == '}')
699 nest--;
700 p++;
701 }
702 return p;
703 }
704
705 /*
706 * Find the next closing brace or comma in the string, taking nested braces
707 * into account.
708 */
709 static const char *
710 separator_comma(const char *p)
711 {
712 int nest = 0;
713 while (*p != '\0') {
714 if ((*p == '}' || *p == ',') && nest == 0)
715 break;
716 if (*p == '{')
717 nest++;
718 if (*p == '}')
719 nest--;
720 p++;
721 }
722 return p;
723 }
724
725 static Boolean
726 contains_wildcard(const char *p)
727 {
728 for (; *p != '\0'; p++) {
729 switch (*p) {
730 case '*':
731 case '?':
732 case '{':
733 case '[':
734 return TRUE;
735 }
736 }
737 return FALSE;
738 }
739
740 static char *
741 concat3(const char *a, size_t a_len, const char *b, size_t b_len,
742 const char *c, size_t c_len)
743 {
744 size_t s_len = a_len + b_len + c_len;
745 char *s = bmake_malloc(s_len + 1);
746 memcpy(s, a, a_len);
747 memcpy(s + a_len, b, b_len);
748 memcpy(s + a_len + b_len, c, c_len);
749 s[s_len] = '\0';
750 return s;
751 }
752
753 /*
754 * Expand curly braces like the C shell. Brace expansion by itself is purely
755 * textual, the expansions are not looked up in the file system. But if an
756 * expanded word contains wildcard characters, it is expanded further,
757 * matching only the actually existing files.
758 *
759 * Example: "{a{b,c}}" expands to "ab" and "ac".
760 * Example: "{a}" expands to "a".
761 * Example: "{a,*.c}" expands to "a" and all "*.c" files that exist.
762 *
763 * Input:
764 * word Entire word to expand
765 * brace First curly brace in it
766 * path Search path to use
767 * expansions Place to store the expansions
768 */
769 static void
770 DirExpandCurly(const char *word, const char *brace, SearchPath *path,
771 StringList *expansions)
772 {
773 const char *prefix, *middle, *piece, *middle_end, *suffix;
774 size_t prefix_len, suffix_len;
775
776 /* Split the word into prefix '{' middle '}' suffix. */
777
778 middle = brace + 1;
779 middle_end = closing_brace(middle);
780 if (*middle_end == '\0') {
781 Error("Unterminated {} clause \"%s\"", middle);
782 return;
783 }
784
785 prefix = word;
786 prefix_len = (size_t)(brace - prefix);
787 suffix = middle_end + 1;
788 suffix_len = strlen(suffix);
789
790 /* Split the middle into pieces, separated by commas. */
791
792 piece = middle;
793 while (piece < middle_end + 1) {
794 const char *piece_end = separator_comma(piece);
795 size_t piece_len = (size_t)(piece_end - piece);
796
797 char *file = concat3(prefix, prefix_len, piece, piece_len,
798 suffix, suffix_len);
799
800 if (contains_wildcard(file)) {
801 SearchPath_Expand(path, file, expansions);
802 free(file);
803 } else {
804 Lst_Append(expansions, file);
805 }
806
807 /* skip over the comma or closing brace */
808 piece = piece_end + 1;
809 }
810 }
811
812
813 /* Expand the word in each of the directories from the path. */
814 static void
815 DirExpandPath(const char *word, SearchPath *path, StringList *expansions)
816 {
817 SearchPathNode *ln;
818 for (ln = path->first; ln != NULL; ln = ln->next) {
819 CachedDir *dir = ln->datum;
820 DirMatchFiles(word, dir, expansions);
821 }
822 }
823
824 static void
825 PrintExpansions(StringList *expansions)
826 {
827 const char *sep = "";
828 StringListNode *ln;
829 for (ln = expansions->first; ln != NULL; ln = ln->next) {
830 const char *word = ln->datum;
831 debug_printf("%s%s", sep, word);
832 sep = " ";
833 }
834 debug_printf("\n");
835 }
836
837 /*
838 * Expand the given pattern into a list of existing filenames by globbing it,
839 * looking in each directory from the search path.
840 *
841 * Input:
842 * path the directories in which to find the files
843 * pattern the pattern to expand
844 * expansions the list on which to place the results
845 */
846 void
847 SearchPath_Expand(SearchPath *path, const char *pattern, StringList *expansions)
848 {
849 const char *brace, *slash, *wildcard, *wildcardComponent;
850
851 assert(path != NULL);
852 assert(expansions != NULL);
853
854 DEBUG1(DIR, "Expanding \"%s\"... ", pattern);
855
856 brace = strchr(pattern, '{');
857 if (brace != NULL) {
858 DirExpandCurly(pattern, brace, path, expansions);
859 goto done;
860 }
861
862 /* At this point, the pattern does not contain '{'. */
863
864 slash = strchr(pattern, '/');
865 if (slash == NULL) {
866 /* The pattern has no directory component. */
867
868 /* First the files in dot. */
869 DirMatchFiles(pattern, dot, expansions);
870 /* Then the files in every other directory on the path. */
871 DirExpandPath(pattern, path, expansions);
872 goto done;
873 }
874
875 /* At this point, the pattern has a directory component. */
876
877 /* Find the first wildcard in the pattern. */
878 for (wildcard = pattern; *wildcard != '\0'; wildcard++)
879 if (*wildcard == '?' || *wildcard == '[' || *wildcard == '*')
880 break;
881
882 if (*wildcard == '\0') {
883 /*
884 * No directory component and no wildcard at all -- this
885 * should never happen as in such a simple case there is no
886 * need to expand anything.
887 */
888 DirExpandPath(pattern, path, expansions);
889 goto done;
890 }
891
892 /* Back up to the start of the component containing the wildcard. */
893 /* XXX: This handles '///' and '/' differently. */
894 wildcardComponent = wildcard;
895 while (wildcardComponent > pattern && *wildcardComponent != '/')
896 wildcardComponent--;
897
898 if (wildcardComponent == pattern) {
899 /* The first component contains the wildcard. */
900 /* Start the search from the local directory */
901 DirExpandPath(pattern, path, expansions);
902 goto done;
903 }
904
905 {
906 char *prefix = bmake_strsedup(pattern, wildcardComponent + 1);
907 /*
908 * The wildcard isn't in the first component.
909 * Find all the components up to the one with the wildcard.
910 */
911 /*
912 * XXX: Check the "the directory is added to the path" part.
913 * It is probably surprising that the directory before a
914 * wildcard gets added to the path.
915 */
916 /*
917 * XXX: Only the first match of the prefix in the path is
918 * taken, any others are ignored. The expectation may be
919 * that the pattern is expanded in the whole path.
920 */
921 char *dirpath = Dir_FindFile(prefix, path);
922 free(prefix);
923
924 /*
925 * dirpath is null if can't find the leading component
926 * XXX: Dir_FindFile won't find internal components.
927 * i.e. if the path contains ../Etc/Object and we're
928 * looking for Etc, it won't be found. Ah well.
929 * Probably not important.
930 * XXX: Check whether the above comment is still true.
931 */
932 if (dirpath != NULL) {
933 SearchPath *partPath;
934
935 char *end = &dirpath[strlen(dirpath) - 1];
936 /* XXX: What about multiple trailing slashes? */
937 if (*end == '/')
938 *end = '\0';
939
940 partPath = SearchPath_New();
941 (void)Dir_AddDir(partPath, dirpath);
942 DirExpandPath(wildcardComponent + 1, partPath, expansions);
943 SearchPath_Free(partPath);
944 }
945 }
946
947 done:
948 if (DEBUG(DIR))
949 PrintExpansions(expansions);
950 }
951
952 /*
953 * Find if the file with the given name exists in the given path.
954 * Return the freshly allocated path to the file, or NULL.
955 */
956 static char *
957 DirLookup(CachedDir *dir, const char *base)
958 {
959 char *file; /* the current filename to check */
960
961 DEBUG1(DIR, " %s ...\n", dir->name);
962
963 if (!HashSet_Contains(&dir->files, base))
964 return NULL;
965
966 file = str_concat3(dir->name, "/", base);
967 DEBUG1(DIR, " returning %s\n", file);
968 dir->hits++;
969 hits++;
970 return file;
971 }
972
973
974 /*
975 * Find if the file with the given name exists in the given directory.
976 * Return the freshly allocated path to the file, or NULL.
977 */
978 static char *
979 DirLookupSubdir(CachedDir *dir, const char *name)
980 {
981 struct cached_stat cst;
982 char *file = dir == dot ? bmake_strdup(name)
983 : str_concat3(dir->name, "/", name);
984
985 DEBUG1(DIR, "checking %s ...\n", file);
986
987 if (cached_stat(file, &cst) == 0) {
988 nearmisses++;
989 return file;
990 }
991 free(file);
992 return NULL;
993 }
994
995 /*
996 * Find if the file with the given name exists in the given path.
997 * Return the freshly allocated path to the file, the empty string, or NULL.
998 * Returning the empty string means that the search should be terminated.
999 */
1000 static char *
1001 DirLookupAbs(CachedDir *dir, const char *name, const char *cp)
1002 {
1003 const char *dnp; /* pointer into dir->name */
1004 const char *np; /* pointer into name */
1005
1006 DEBUG1(DIR, " %s ...\n", dir->name);
1007
1008 /*
1009 * If the file has a leading path component and that component
1010 * exactly matches the entire name of the current search
1011 * directory, we can attempt another cache lookup. And if we don't
1012 * have a hit, we can safely assume the file does not exist at all.
1013 */
1014 for (dnp = dir->name, np = name;
1015 *dnp != '\0' && *dnp == *np; dnp++, np++)
1016 continue;
1017 if (*dnp != '\0' || np != cp - 1)
1018 return NULL;
1019
1020 if (!HashSet_Contains(&dir->files, cp)) {
1021 DEBUG0(DIR, " must be here but isn't -- returning\n");
1022 return bmake_strdup(""); /* to terminate the search */
1023 }
1024
1025 dir->hits++;
1026 hits++;
1027 DEBUG1(DIR, " returning %s\n", name);
1028 return bmake_strdup(name);
1029 }
1030
1031 /*
1032 * Find the file given on "." or curdir.
1033 * Return the freshly allocated path to the file, or NULL.
1034 */
1035 static char *
1036 DirFindDot(const char *name, const char *base)
1037 {
1038
1039 if (HashSet_Contains(&dot->files, base)) {
1040 DEBUG0(DIR, " in '.'\n");
1041 hits++;
1042 dot->hits++;
1043 return bmake_strdup(name);
1044 }
1045
1046 if (cur != NULL && HashSet_Contains(&cur->files, base)) {
1047 DEBUG1(DIR, " in ${.CURDIR} = %s\n", cur->name);
1048 hits++;
1049 cur->hits++;
1050 return str_concat3(cur->name, "/", base);
1051 }
1052
1053 return NULL;
1054 }
1055
1056 /*
1057 * Find the file with the given name along the given search path.
1058 *
1059 * If the file is found in a directory that is not on the path
1060 * already (either 'name' is absolute or it is a relative path
1061 * [ dir1/.../dirn/file ] which exists below one of the directories
1062 * already on the search path), its directory is added to the end
1063 * of the path, on the assumption that there will be more files in
1064 * that directory later on. Sometimes this is true. Sometimes not.
1065 *
1066 * Input:
1067 * name the file to find
1068 * path the directories to search, or NULL
1069 *
1070 * Results:
1071 * The freshly allocated path to the file, or NULL.
1072 */
1073 char *
1074 Dir_FindFile(const char *name, SearchPath *path)
1075 {
1076 char *file; /* the current filename to check */
1077 Boolean seenDotLast = FALSE; /* true if we should search dot last */
1078 struct cached_stat cst; /* Buffer for stat, if necessary */
1079 const char *trailing_dot = ".";
1080 const char *base = str_basename(name);
1081
1082 DEBUG1(DIR, "Searching for %s ...", name);
1083
1084 if (path == NULL) {
1085 DEBUG0(DIR, "couldn't open path, file not found\n");
1086 misses++;
1087 return NULL;
1088 }
1089
1090 if (path->first != NULL) {
1091 CachedDir *dir = path->first->datum;
1092 if (dir == dotLast) {
1093 seenDotLast = TRUE;
1094 DEBUG0(DIR, "[dot last]...");
1095 }
1096 }
1097 DEBUG0(DIR, "\n");
1098
1099 /*
1100 * If there's no leading directory components or if the leading
1101 * directory component is exactly `./', consult the cached contents
1102 * of each of the directories on the search path.
1103 */
1104 if (base == name || (base - name == 2 && *name == '.')) {
1105 SearchPathNode *ln;
1106
1107 /*
1108 * We look through all the directories on the path seeking one
1109 * which contains the final component of the given name. If
1110 * such a beast is found, we concatenate the directory name
1111 * and the final component and return the resulting string.
1112 * If we don't find any such thing, we go on to phase two.
1113 *
1114 * No matter what, we always look for the file in the current
1115 * directory before anywhere else (unless we found the magic
1116 * DOTLAST path, in which case we search it last) and we *do
1117 * not* add the ./ to it if it exists.
1118 * This is so there are no conflicts between what the user
1119 * specifies (fish.c) and what pmake finds (./fish.c).
1120 */
1121 if (!seenDotLast && (file = DirFindDot(name, base)) != NULL)
1122 return file;
1123
1124 for (ln = path->first; ln != NULL; ln = ln->next) {
1125 CachedDir *dir = ln->datum;
1126 if (dir == dotLast)
1127 continue;
1128 if ((file = DirLookup(dir, base)) != NULL)
1129 return file;
1130 }
1131
1132 if (seenDotLast && (file = DirFindDot(name, base)) != NULL)
1133 return file;
1134 }
1135
1136 /*
1137 * We didn't find the file on any directory in the search path.
1138 * If the name doesn't contain a slash, that means it doesn't exist.
1139 * If it *does* contain a slash, however, there is still hope: it
1140 * could be in a subdirectory of one of the members of the search
1141 * path. (eg. /usr/include and sys/types.h. The above search would
1142 * fail to turn up types.h in /usr/include, but it *is* in
1143 * /usr/include/sys/types.h).
1144 * [ This no longer applies: If we find such a beast, we assume there
1145 * will be more (what else can we assume?) and add all but the last
1146 * component of the resulting name onto the search path (at the
1147 * end).]
1148 * This phase is only performed if the file is *not* absolute.
1149 */
1150 if (base == name) {
1151 DEBUG0(DIR, " failed.\n");
1152 misses++;
1153 return NULL;
1154 }
1155
1156 if (*base == '\0') {
1157 /* we were given a trailing "/" */
1158 base = trailing_dot;
1159 }
1160
1161 if (name[0] != '/') {
1162 SearchPathNode *ln;
1163 Boolean checkedDot = FALSE;
1164
1165 DEBUG0(DIR, " Trying subdirectories...\n");
1166
1167 if (!seenDotLast) {
1168 if (dot != NULL) {
1169 checkedDot = TRUE;
1170 if ((file = DirLookupSubdir(dot, name)) != NULL)
1171 return file;
1172 }
1173 if (cur != NULL &&
1174 (file = DirLookupSubdir(cur, name)) != NULL)
1175 return file;
1176 }
1177
1178 for (ln = path->first; ln != NULL; ln = ln->next) {
1179 CachedDir *dir = ln->datum;
1180 if (dir == dotLast)
1181 continue;
1182 if (dir == dot) {
1183 if (checkedDot)
1184 continue;
1185 checkedDot = TRUE;
1186 }
1187 if ((file = DirLookupSubdir(dir, name)) != NULL)
1188 return file;
1189 }
1190
1191 if (seenDotLast) {
1192 if (dot != NULL && !checkedDot) {
1193 checkedDot = TRUE;
1194 if ((file = DirLookupSubdir(dot, name)) != NULL)
1195 return file;
1196 }
1197 if (cur != NULL &&
1198 (file = DirLookupSubdir(cur, name)) != NULL)
1199 return file;
1200 }
1201
1202 if (checkedDot) {
1203 /*
1204 * Already checked by the given name, since . was in
1205 * the path, so no point in proceeding.
1206 */
1207 DEBUG0(DIR, " Checked . already, returning NULL\n");
1208 return NULL;
1209 }
1210
1211 } else { /* name[0] == '/' */
1212 SearchPathNode *ln;
1213
1214 /*
1215 * For absolute names, compare directory path prefix against
1216 * the the directory path of each member on the search path
1217 * for an exact match. If we have an exact match on any member
1218 * of the search path, use the cached contents of that member
1219 * to lookup the final file component. If that lookup fails we
1220 * can safely assume that the file does not exist at all.
1221 * This is signified by DirLookupAbs() returning an empty
1222 * string.
1223 */
1224 DEBUG0(DIR, " Trying exact path matches...\n");
1225
1226 if (!seenDotLast && cur != NULL &&
1227 ((file = DirLookupAbs(cur, name, base)) != NULL)) {
1228 if (file[0] == '\0') {
1229 free(file);
1230 return NULL;
1231 }
1232 return file;
1233 }
1234
1235 for (ln = path->first; ln != NULL; ln = ln->next) {
1236 CachedDir *dir = ln->datum;
1237 if (dir == dotLast)
1238 continue;
1239 if ((file = DirLookupAbs(dir, name, base)) != NULL) {
1240 if (file[0] == '\0') {
1241 free(file);
1242 return NULL;
1243 }
1244 return file;
1245 }
1246 }
1247
1248 if (seenDotLast && cur != NULL &&
1249 ((file = DirLookupAbs(cur, name, base)) != NULL)) {
1250 if (file[0] == '\0') {
1251 free(file);
1252 return NULL;
1253 }
1254 return file;
1255 }
1256 }
1257
1258 /*
1259 * Didn't find it that way, either. Sigh. Phase 3. Add its directory
1260 * onto the search path in any case, just in case, then look for the
1261 * thing in the hash table. If we find it, grand. We return a new
1262 * copy of the name. Otherwise we sadly return a NULL pointer. Sigh.
1263 * Note that if the directory holding the file doesn't exist, this
1264 * will do an extra search of the final directory on the path. Unless
1265 * something weird happens, this search won't succeed and life will
1266 * be groovy.
1267 *
1268 * Sigh. We cannot add the directory onto the search path because
1269 * of this amusing case:
1270 * $(INSTALLDIR)/$(FILE): $(FILE)
1271 *
1272 * $(FILE) exists in $(INSTALLDIR) but not in the current one.
1273 * When searching for $(FILE), we will find it in $(INSTALLDIR)
1274 * b/c we added it here. This is not good...
1275 */
1276 #if 0
1277 {
1278 CachedDir *dir;
1279 char *prefix;
1280
1281 if (base == trailing_dot) {
1282 base = strrchr(name, '/');
1283 base++;
1284 }
1285 prefix = bmake_strsedup(name, base - 1);
1286 (void)Dir_AddDir(path, prefix);
1287 free(prefix);
1288
1289 bigmisses++;
1290 if (path->last == NULL)
1291 return NULL;
1292
1293 dir = path->last->datum;
1294 if (HashSet_Contains(&dir->files, base))
1295 return bmake_strdup(name);
1296 return NULL;
1297 }
1298 #else
1299 DEBUG1(DIR, " Looking for \"%s\" ...\n", name);
1300
1301 bigmisses++;
1302 if (cached_stat(name, &cst) == 0) {
1303 return bmake_strdup(name);
1304 }
1305
1306 DEBUG0(DIR, " failed. Returning NULL\n");
1307 return NULL;
1308 #endif
1309 }
1310
1311
1312 /*
1313 * Search for a path starting at a given directory and then working our way
1314 * up towards the root.
1315 *
1316 * Input:
1317 * here starting directory
1318 * search_path the relative path we are looking for
1319 *
1320 * Results:
1321 * The found path, or NULL.
1322 */
1323 char *
1324 Dir_FindHereOrAbove(const char *here, const char *search_path)
1325 {
1326 struct cached_stat cst;
1327 char *dirbase, *dirbase_end;
1328 char *try, *try_end;
1329
1330 /* copy out our starting point */
1331 dirbase = bmake_strdup(here);
1332 dirbase_end = dirbase + strlen(dirbase);
1333
1334 /* loop until we determine a result */
1335 for (;;) {
1336
1337 /* try and stat(2) it ... */
1338 try = str_concat3(dirbase, "/", search_path);
1339 if (cached_stat(try, &cst) != -1) {
1340 /*
1341 * success! if we found a file, chop off
1342 * the filename so we return a directory.
1343 */
1344 if ((cst.cst_mode & S_IFMT) != S_IFDIR) {
1345 try_end = try + strlen(try);
1346 while (try_end > try && *try_end != '/')
1347 try_end--;
1348 if (try_end > try)
1349 *try_end = '\0'; /* chop! */
1350 }
1351
1352 free(dirbase);
1353 return try;
1354 }
1355 free(try);
1356
1357 /*
1358 * nope, we didn't find it. if we used up dirbase we've
1359 * reached the root and failed.
1360 */
1361 if (dirbase_end == dirbase)
1362 break; /* failed! */
1363
1364 /*
1365 * truncate dirbase from the end to move up a dir
1366 */
1367 while (dirbase_end > dirbase && *dirbase_end != '/')
1368 dirbase_end--;
1369 *dirbase_end = '\0'; /* chop! */
1370 }
1371
1372 free(dirbase);
1373 return NULL;
1374 }
1375
1376 /*
1377 * This is an implied source, and it may have moved,
1378 * see if we can find it via the current .PATH
1379 */
1380 static char *
1381 ResolveMovedDepends(GNode *gn)
1382 {
1383 char *fullName;
1384
1385 const char *base = str_basename(gn->name);
1386 if (base == gn->name)
1387 return NULL;
1388
1389 fullName = Dir_FindFile(base, Suff_FindPath(gn));
1390 if (fullName == NULL)
1391 return NULL;
1392
1393 /*
1394 * Put the found file in gn->path so that we give that to the compiler.
1395 */
1396 /*
1397 * XXX: Better just reset gn->path to NULL; updating it is already done
1398 * by Dir_UpdateMTime.
1399 */
1400 gn->path = bmake_strdup(fullName);
1401 if (!Job_RunTarget(".STALE", gn->fname))
1402 fprintf(stdout, /* XXX: Why stdout? */
1403 "%s: %s, %d: ignoring stale %s for %s, found %s\n",
1404 progname, gn->fname, gn->lineno,
1405 makeDependfile, gn->name, fullName);
1406
1407 return fullName;
1408 }
1409
1410 static char *
1411 ResolveFullName(GNode *gn)
1412 {
1413 char *fullName;
1414
1415 fullName = gn->path;
1416 if (fullName == NULL && !(gn->type & OP_NOPATH)) {
1417
1418 fullName = Dir_FindFile(gn->name, Suff_FindPath(gn));
1419
1420 if (fullName == NULL && gn->flags & FROM_DEPEND &&
1421 !Lst_IsEmpty(&gn->implicitParents))
1422 fullName = ResolveMovedDepends(gn);
1423
1424 DEBUG2(DIR, "Found '%s' as '%s'\n",
1425 gn->name, fullName != NULL ? fullName : "(not found)");
1426 }
1427
1428 if (fullName == NULL)
1429 fullName = bmake_strdup(gn->name);
1430
1431 /* XXX: Is every piece of memory freed as it should? */
1432
1433 return fullName;
1434 }
1435
1436 /*
1437 * Search gn along dirSearchPath and store its modification time in gn->mtime.
1438 * If no file is found, store 0 instead.
1439 *
1440 * The found file is stored in gn->path, unless the node already had a path.
1441 */
1442 void
1443 Dir_UpdateMTime(GNode *gn, Boolean recheck)
1444 {
1445 char *fullName;
1446 struct cached_stat cst;
1447
1448 if (gn->type & OP_ARCHV) {
1449 Arch_UpdateMTime(gn);
1450 return;
1451 }
1452
1453 if (gn->type & OP_PHONY) {
1454 gn->mtime = 0;
1455 return;
1456 }
1457
1458 fullName = ResolveFullName(gn);
1459
1460 if (cached_stats(fullName, &cst, recheck ? CST_UPDATE : CST_NONE) < 0) {
1461 if (gn->type & OP_MEMBER) {
1462 if (fullName != gn->path)
1463 free(fullName);
1464 Arch_UpdateMemberMTime(gn);
1465 return;
1466 }
1467
1468 cst.cst_mtime = 0;
1469 }
1470
1471 if (fullName != NULL && gn->path == NULL)
1472 gn->path = fullName;
1473 /* XXX: else free(fullName)? */
1474
1475 gn->mtime = cst.cst_mtime;
1476 }
1477
1478 /*
1479 * Read the directory and add it to the cache in openDirs.
1480 * If a path is given, add the directory to that path as well.
1481 */
1482 static CachedDir *
1483 CacheNewDir(const char *name, SearchPath *path)
1484 {
1485 CachedDir *dir = NULL;
1486 DIR *d;
1487 struct dirent *dp;
1488
1489 if ((d = opendir(name)) == NULL) {
1490 DEBUG1(DIR, "Caching %s ... not found\n", name);
1491 return dir;
1492 }
1493
1494 DEBUG1(DIR, "Caching %s ...\n", name);
1495
1496 dir = CachedDir_New(name);
1497
1498 while ((dp = readdir(d)) != NULL) {
1499
1500 #if defined(sun) && defined(d_ino) /* d_ino is a sunos4 #define for d_fileno */
1501 /*
1502 * The sun directory library doesn't check for a 0 inode
1503 * (0-inode slots just take up space), so we have to do
1504 * it ourselves.
1505 */
1506 if (dp->d_fileno == 0)
1507 continue;
1508 #endif /* sun && d_ino */
1509
1510 (void)HashSet_Add(&dir->files, dp->d_name);
1511 }
1512 (void)closedir(d);
1513
1514 OpenDirs_Add(&openDirs, dir);
1515 if (path != NULL)
1516 Lst_Append(path, CachedDir_Ref(dir));
1517
1518 DEBUG1(DIR, "Caching %s done\n", name);
1519 return dir;
1520 }
1521
1522 /*
1523 * Read the list of filenames in the directory and store the result
1524 * in openDirs.
1525 *
1526 * If a path is given, append the directory to that path.
1527 *
1528 * Input:
1529 * path The path to which the directory should be
1530 * added, or NULL to only add the directory to openDirs
1531 * name The name of the directory to add.
1532 * The name is not normalized in any way.
1533 * Output:
1534 * result If no path is given and the directory exists, the
1535 * returned CachedDir has a reference count of 0. It
1536 * must either be assigned to a variable using
1537 * CachedDir_Assign or be appended to a SearchPath using
1538 * Lst_Append and CachedDir_Ref.
1539 */
1540 CachedDir *
1541 Dir_AddDir(SearchPath *path, const char *name)
1542 {
1543
1544 if (path != NULL && strcmp(name, ".DOTLAST") == 0) {
1545 SearchPathNode *ln;
1546
1547 /* XXX: Linear search gets slow with thousands of entries. */
1548 for (ln = path->first; ln != NULL; ln = ln->next) {
1549 CachedDir *pathDir = ln->datum;
1550 if (strcmp(pathDir->name, name) == 0)
1551 return pathDir;
1552 }
1553
1554 Lst_Prepend(path, CachedDir_Ref(dotLast));
1555 }
1556
1557 if (path != NULL) {
1558 /* XXX: Why is OpenDirs only checked if path != NULL? */
1559 CachedDir *dir = OpenDirs_Find(&openDirs, name);
1560 if (dir != NULL) {
1561 if (Lst_FindDatum(path, dir) == NULL)
1562 Lst_Append(path, CachedDir_Ref(dir));
1563 return dir;
1564 }
1565 }
1566
1567 return CacheNewDir(name, path);
1568 }
1569
1570 /*
1571 * Return a copy of dirSearchPath, incrementing the reference counts for
1572 * the contained directories.
1573 */
1574 SearchPath *
1575 Dir_CopyDirSearchPath(void)
1576 {
1577 SearchPath *path = SearchPath_New();
1578 SearchPathNode *ln;
1579 for (ln = dirSearchPath.first; ln != NULL; ln = ln->next) {
1580 CachedDir *dir = ln->datum;
1581 Lst_Append(path, CachedDir_Ref(dir));
1582 }
1583 return path;
1584 }
1585
1586 /*
1587 * Make a string by taking all the directories in the given search path and
1588 * preceding them by the given flag. Used by the suffix module to create
1589 * variables for compilers based on suffix search paths.
1590 *
1591 * Input:
1592 * flag flag which should precede each directory
1593 * path list of directories
1594 *
1595 * Results:
1596 * The string mentioned above. Note that there is no space between the
1597 * given flag and each directory. The empty string is returned if things
1598 * don't go well.
1599 */
1600 char *
1601 SearchPath_ToFlags(const char *flag, SearchPath *path)
1602 {
1603 Buffer buf;
1604 SearchPathNode *ln;
1605
1606 Buf_Init(&buf);
1607
1608 if (path != NULL) {
1609 for (ln = path->first; ln != NULL; ln = ln->next) {
1610 CachedDir *dir = ln->datum;
1611 Buf_AddStr(&buf, " ");
1612 Buf_AddStr(&buf, flag);
1613 Buf_AddStr(&buf, dir->name);
1614 }
1615 }
1616
1617 return Buf_Destroy(&buf, FALSE);
1618 }
1619
1620 /* Free the search path and all directories mentioned in it. */
1621 void
1622 SearchPath_Free(SearchPath *path)
1623 {
1624 SearchPathNode *ln;
1625
1626 for (ln = path->first; ln != NULL; ln = ln->next) {
1627 CachedDir *dir = ln->datum;
1628 CachedDir_Unref(dir);
1629 }
1630 Lst_Free(path);
1631 }
1632
1633 /*
1634 * Clear out all elements from the given search path.
1635 * The path is set to the empty list but is not destroyed.
1636 */
1637 void
1638 SearchPath_Clear(SearchPath *path)
1639 {
1640 while (!Lst_IsEmpty(path)) {
1641 CachedDir *dir = Lst_Dequeue(path);
1642 CachedDir_Unref(dir);
1643 }
1644 }
1645
1646
1647 /*
1648 * Concatenate two paths, adding the second to the end of the first,
1649 * skipping duplicates.
1650 */
1651 void
1652 SearchPath_AddAll(SearchPath *dst, SearchPath *src)
1653 {
1654 SearchPathNode *ln;
1655
1656 for (ln = src->first; ln != NULL; ln = ln->next) {
1657 CachedDir *dir = ln->datum;
1658 if (Lst_FindDatum(dst, dir) == NULL)
1659 Lst_Append(dst, CachedDir_Ref(dir));
1660 }
1661 }
1662
1663 static int
1664 percentage(int num, int den)
1665 {
1666 return den != 0 ? num * 100 / den : 0;
1667 }
1668
1669 /********** DEBUG INFO **********/
1670 void
1671 Dir_PrintDirectories(void)
1672 {
1673 CachedDirListNode *ln;
1674
1675 debug_printf("#*** Directory Cache:\n");
1676 debug_printf(
1677 "# Stats: %d hits %d misses %d near misses %d losers (%d%%)\n",
1678 hits, misses, nearmisses, bigmisses,
1679 percentage(hits, hits + bigmisses + nearmisses));
1680 debug_printf("# refs hits directory\n");
1681
1682 for (ln = openDirs.list.first; ln != NULL; ln = ln->next) {
1683 CachedDir *dir = ln->datum;
1684 debug_printf("# %4d %4d %s\n",
1685 dir->refCount, dir->hits, dir->name);
1686 }
1687 }
1688
1689 void
1690 SearchPath_Print(SearchPath *path)
1691 {
1692 SearchPathNode *ln;
1693
1694 for (ln = path->first; ln != NULL; ln = ln->next) {
1695 const CachedDir *dir = ln->datum;
1696 debug_printf("%s ", dir->name);
1697 }
1698 }
1699