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