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