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