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