Annotation of src/usr.bin/make/dir.c, Revision 1.11
1.11 ! christos 1: /* $NetBSD: dir.c,v 1.10 1996/02/04 22:20:38 christos Exp $ */
1.8 christos 2:
1.1 cgd 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
1.8 christos 42: #if 0
43: static char sccsid[] = "@(#)dir.c 5.6 (Berkeley) 12/28/90";
44: #else
1.11 ! christos 45: static char rcsid[] = "$NetBSD: dir.c,v 1.10 1996/02/04 22:20:38 christos Exp $";
1.8 christos 46: #endif
1.1 cgd 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: *
1.6 jtc 58: * Dir_End Cleanup the module.
59: *
1.1 cgd 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>
1.5 cgd 94: #include <dirent.h>
1.1 cgd 95: #include <sys/stat.h>
96: #include "make.h"
97: #include "hash.h"
1.5 cgd 98: #include "dir.h"
1.1 cgd 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 Hash_Table mtimes; /* Results of doing a last-resort stat in
185: * Dir_FindFile -- if we have to go to the
186: * system to find the file, we might as well
187: * have its mtime on record. XXX: If this is done
188: * way early, there's a chance other rules will
189: * have already updated the file, in which case
190: * we'll update it again. Generally, there won't
191: * be two rules to update a single file, so this
192: * should be ok, but... */
193:
194:
1.6 jtc 195: static int DirFindName __P((ClientData, ClientData));
1.5 cgd 196: static int DirMatchFiles __P((char *, Path *, Lst));
197: static void DirExpandCurly __P((char *, char *, Lst, Lst));
198: static void DirExpandInt __P((char *, Lst, Lst));
1.6 jtc 199: static int DirPrintWord __P((ClientData, ClientData));
200: static int DirPrintDir __P((ClientData, ClientData));
1.5 cgd 201:
1.1 cgd 202: /*-
203: *-----------------------------------------------------------------------
204: * Dir_Init --
205: * initialize things for this module
206: *
207: * Results:
208: * none
209: *
210: * Side Effects:
211: * some directories may be opened.
212: *-----------------------------------------------------------------------
213: */
214: void
215: Dir_Init ()
216: {
217: dirSearchPath = Lst_Init (FALSE);
218: openDirectories = Lst_Init (FALSE);
219: Hash_InitTable(&mtimes, 0);
220:
221: /*
222: * Since the Path structure is placed on both openDirectories and
223: * the path we give Dir_AddDir (which in this case is openDirectories),
224: * we need to remove "." from openDirectories and what better time to
225: * do it than when we have to fetch the thing anyway?
226: */
227: Dir_AddDir (openDirectories, ".");
228: dot = (Path *) Lst_DeQueue (openDirectories);
229:
230: /*
231: * We always need to have dot around, so we increment its reference count
232: * to make sure it's not destroyed.
233: */
234: dot->refCount += 1;
235: }
236:
237: /*-
238: *-----------------------------------------------------------------------
1.6 jtc 239: * Dir_End --
240: * cleanup things for this module
241: *
242: * Results:
243: * none
244: *
245: * Side Effects:
246: * none
247: *-----------------------------------------------------------------------
248: */
249: void
250: Dir_End()
251: {
252: dot->refCount -= 1;
253: Dir_Destroy((ClientData) dot);
254: Dir_ClearPath(dirSearchPath);
255: Lst_Destroy(dirSearchPath, NOFREE);
256: Dir_ClearPath(openDirectories);
257: Lst_Destroy(openDirectories, NOFREE);
258: Hash_DeleteTable(&mtimes);
259: }
260:
261: /*-
262: *-----------------------------------------------------------------------
1.1 cgd 263: * DirFindName --
264: * See if the Path structure describes the same directory as the
265: * given one by comparing their names. Called from Dir_AddDir via
266: * Lst_Find when searching the list of open directories.
267: *
268: * Results:
269: * 0 if it is the same. Non-zero otherwise
270: *
271: * Side Effects:
272: * None
273: *-----------------------------------------------------------------------
274: */
275: static int
276: DirFindName (p, dname)
1.6 jtc 277: ClientData p; /* Current name */
278: ClientData dname; /* Desired name */
1.1 cgd 279: {
1.6 jtc 280: return (strcmp (((Path *)p)->name, (char *) dname));
1.1 cgd 281: }
282:
283: /*-
284: *-----------------------------------------------------------------------
285: * Dir_HasWildcards --
286: * see if the given name has any wildcard characters in it
287: *
288: * Results:
289: * returns TRUE if the word should be expanded, FALSE otherwise
290: *
291: * Side Effects:
292: * none
293: *-----------------------------------------------------------------------
294: */
295: Boolean
296: Dir_HasWildcards (name)
297: char *name; /* name to check */
298: {
299: register char *cp;
300:
301: for (cp = name; *cp; cp++) {
302: switch(*cp) {
303: case '{':
304: case '[':
305: case '?':
306: case '*':
307: return (TRUE);
308: }
309: }
310: return (FALSE);
311: }
312:
313: /*-
314: *-----------------------------------------------------------------------
315: * DirMatchFiles --
316: * Given a pattern and a Path structure, see if any files
317: * match the pattern and add their names to the 'expansions' list if
318: * any do. This is incomplete -- it doesn't take care of patterns like
1.5 cgd 319: * src / *src / *.c properly (just *.c on any of the directories), but it
1.1 cgd 320: * will do for now.
321: *
322: * Results:
323: * Always returns 0
324: *
325: * Side Effects:
326: * File names are added to the expansions lst. The directory will be
327: * fully hashed when this is done.
328: *-----------------------------------------------------------------------
329: */
330: static int
331: DirMatchFiles (pattern, p, expansions)
332: char *pattern; /* Pattern to look for */
333: Path *p; /* Directory to search */
334: Lst expansions; /* Place to store the results */
335: {
336: Hash_Search search; /* Index into the directory's table */
337: Hash_Entry *entry; /* Current entry in the table */
338: Boolean isDot; /* TRUE if the directory being searched is . */
339:
340: isDot = (*p->name == '.' && p->name[1] == '\0');
341:
342: for (entry = Hash_EnumFirst(&p->files, &search);
343: entry != (Hash_Entry *)NULL;
344: entry = Hash_EnumNext(&search))
345: {
346: /*
347: * See if the file matches the given pattern. Note we follow the UNIX
348: * convention that dot files will only be found if the pattern
349: * begins with a dot (note also that as a side effect of the hashing
350: * scheme, .* won't match . or .. since they aren't hashed).
351: */
352: if (Str_Match(entry->name, pattern) &&
353: ((entry->name[0] != '.') ||
354: (pattern[0] == '.')))
355: {
356: (void)Lst_AtEnd(expansions,
1.11 ! christos 357: (isDot ? estrdup(entry->name) :
1.1 cgd 358: str_concat(p->name, entry->name,
359: STR_ADDSLASH)));
360: }
361: }
362: return (0);
363: }
364:
365: /*-
366: *-----------------------------------------------------------------------
367: * DirExpandCurly --
368: * Expand curly braces like the C shell. Does this recursively.
369: * Note the special case: if after the piece of the curly brace is
370: * done there are no wildcard characters in the result, the result is
371: * placed on the list WITHOUT CHECKING FOR ITS EXISTENCE.
372: *
373: * Results:
374: * None.
375: *
376: * Side Effects:
377: * The given list is filled with the expansions...
378: *
379: *-----------------------------------------------------------------------
380: */
381: static void
382: DirExpandCurly(word, brace, path, expansions)
383: char *word; /* Entire word to expand */
384: char *brace; /* First curly brace in it */
385: Lst path; /* Search path to use */
386: Lst expansions; /* Place to store the expansions */
387: {
388: char *end; /* Character after the closing brace */
389: char *cp; /* Current position in brace clause */
390: char *start; /* Start of current piece of brace clause */
391: int bracelevel; /* Number of braces we've seen. If we see a
392: * right brace when this is 0, we've hit the
393: * end of the clause. */
394: char *file; /* Current expansion */
395: int otherLen; /* The length of the other pieces of the
396: * expansion (chars before and after the
397: * clause in 'word') */
398: char *cp2; /* Pointer for checking for wildcards in
399: * expansion before calling Dir_Expand */
400:
401: start = brace+1;
402:
403: /*
404: * Find the end of the brace clause first, being wary of nested brace
405: * clauses.
406: */
407: for (end = start, bracelevel = 0; *end != '\0'; end++) {
408: if (*end == '{') {
409: bracelevel++;
410: } else if ((*end == '}') && (bracelevel-- == 0)) {
411: break;
412: }
413: }
414: if (*end == '\0') {
415: Error("Unterminated {} clause \"%s\"", start);
416: return;
417: } else {
418: end++;
419: }
420: otherLen = brace - word + strlen(end);
421:
422: for (cp = start; cp < end; cp++) {
423: /*
424: * Find the end of this piece of the clause.
425: */
426: bracelevel = 0;
427: while (*cp != ',') {
428: if (*cp == '{') {
429: bracelevel++;
430: } else if ((*cp == '}') && (bracelevel-- <= 0)) {
431: break;
432: }
433: cp++;
434: }
435: /*
436: * Allocate room for the combination and install the three pieces.
437: */
438: file = emalloc(otherLen + cp - start + 1);
439: if (brace != word) {
440: strncpy(file, word, brace-word);
441: }
442: if (cp != start) {
443: strncpy(&file[brace-word], start, cp-start);
444: }
445: strcpy(&file[(brace-word)+(cp-start)], end);
446:
447: /*
448: * See if the result has any wildcards in it. If we find one, call
449: * Dir_Expand right away, telling it to place the result on our list
450: * of expansions.
451: */
452: for (cp2 = file; *cp2 != '\0'; cp2++) {
453: switch(*cp2) {
454: case '*':
455: case '?':
456: case '{':
457: case '[':
458: Dir_Expand(file, path, expansions);
459: goto next;
460: }
461: }
462: if (*cp2 == '\0') {
463: /*
464: * Hit the end w/o finding any wildcards, so stick the expansion
465: * on the end of the list.
466: */
467: (void)Lst_AtEnd(expansions, file);
468: } else {
469: next:
470: free(file);
471: }
472: start = cp+1;
473: }
474: }
475:
476:
477: /*-
478: *-----------------------------------------------------------------------
479: * DirExpandInt --
480: * Internal expand routine. Passes through the directories in the
481: * path one by one, calling DirMatchFiles for each. NOTE: This still
482: * doesn't handle patterns in directories...
483: *
484: * Results:
485: * None.
486: *
487: * Side Effects:
488: * Things are added to the expansions list.
489: *
490: *-----------------------------------------------------------------------
491: */
492: static void
493: DirExpandInt(word, path, expansions)
494: char *word; /* Word to expand */
495: Lst path; /* Path on which to look */
496: Lst expansions; /* Place to store the result */
497: {
498: LstNode ln; /* Current node */
499: Path *p; /* Directory in the node */
500:
501: if (Lst_Open(path) == SUCCESS) {
502: while ((ln = Lst_Next(path)) != NILLNODE) {
503: p = (Path *)Lst_Datum(ln);
504: DirMatchFiles(word, p, expansions);
505: }
506: Lst_Close(path);
507: }
508: }
509:
510: /*-
511: *-----------------------------------------------------------------------
512: * DirPrintWord --
513: * Print a word in the list of expansions. Callback for Dir_Expand
514: * when DEBUG(DIR), via Lst_ForEach.
515: *
516: * Results:
517: * === 0
518: *
519: * Side Effects:
520: * The passed word is printed, followed by a space.
521: *
522: *-----------------------------------------------------------------------
523: */
524: static int
1.6 jtc 525: DirPrintWord(word, dummy)
526: ClientData word;
527: ClientData dummy;
1.1 cgd 528: {
1.6 jtc 529: printf("%s ", (char *) word);
1.1 cgd 530:
1.6 jtc 531: return(dummy ? 0 : 0);
1.1 cgd 532: }
533:
534: /*-
535: *-----------------------------------------------------------------------
536: * Dir_Expand --
537: * Expand the given word into a list of words by globbing it looking
538: * in the directories on the given search path.
539: *
540: * Results:
541: * A list of words consisting of the files which exist along the search
542: * path matching the given pattern.
543: *
544: * Side Effects:
545: * Directories may be opened. Who knows?
546: *-----------------------------------------------------------------------
547: */
548: void
549: Dir_Expand (word, path, expansions)
550: char *word; /* the word to expand */
551: Lst path; /* the list of directories in which to find
552: * the resulting files */
553: Lst expansions; /* the list on which to place the results */
554: {
555: char *cp;
556:
557: if (DEBUG(DIR)) {
558: printf("expanding \"%s\"...", word);
559: }
560:
1.5 cgd 561: cp = strchr(word, '{');
1.1 cgd 562: if (cp) {
563: DirExpandCurly(word, cp, path, expansions);
564: } else {
1.5 cgd 565: cp = strchr(word, '/');
1.1 cgd 566: if (cp) {
567: /*
568: * The thing has a directory component -- find the first wildcard
569: * in the string.
570: */
571: for (cp = word; *cp; cp++) {
572: if (*cp == '?' || *cp == '[' || *cp == '*' || *cp == '{') {
573: break;
574: }
575: }
576: if (*cp == '{') {
577: /*
578: * This one will be fun.
579: */
580: DirExpandCurly(word, cp, path, expansions);
581: return;
582: } else if (*cp != '\0') {
583: /*
584: * Back up to the start of the component
585: */
586: char *dirpath;
587:
588: while (cp > word && *cp != '/') {
589: cp--;
590: }
591: if (cp != word) {
1.5 cgd 592: char sc;
1.1 cgd 593: /*
594: * If the glob isn't in the first component, try and find
595: * all the components up to the one with a wildcard.
596: */
1.5 cgd 597: sc = cp[1];
598: cp[1] = '\0';
1.1 cgd 599: dirpath = Dir_FindFile(word, path);
1.5 cgd 600: cp[1] = sc;
1.1 cgd 601: /*
602: * dirpath is null if can't find the leading component
603: * XXX: Dir_FindFile won't find internal components.
604: * i.e. if the path contains ../Etc/Object and we're
605: * looking for Etc, it won't be found. Ah well.
606: * Probably not important.
607: */
608: if (dirpath != (char *)NULL) {
1.5 cgd 609: char *dp = &dirpath[strlen(dirpath) - 1];
610: if (*dp == '/')
611: *dp = '\0';
1.1 cgd 612: path = Lst_Init(FALSE);
613: Dir_AddDir(path, dirpath);
614: DirExpandInt(cp+1, path, expansions);
615: Lst_Destroy(path, NOFREE);
616: }
617: } else {
618: /*
619: * Start the search from the local directory
620: */
621: DirExpandInt(word, path, expansions);
622: }
623: } else {
624: /*
625: * Return the file -- this should never happen.
626: */
627: DirExpandInt(word, path, expansions);
628: }
629: } else {
630: /*
631: * First the files in dot
632: */
633: DirMatchFiles(word, dot, expansions);
634:
635: /*
636: * Then the files in every other directory on the path.
637: */
638: DirExpandInt(word, path, expansions);
639: }
640: }
641: if (DEBUG(DIR)) {
1.6 jtc 642: Lst_ForEach(expansions, DirPrintWord, (ClientData) 0);
1.5 cgd 643: fputc('\n', stdout);
1.1 cgd 644: }
645: }
646:
647: /*-
648: *-----------------------------------------------------------------------
649: * Dir_FindFile --
650: * Find the file with the given name along the given search path.
651: *
652: * Results:
653: * The path to the file or NULL. This path is guaranteed to be in a
654: * different part of memory than name and so may be safely free'd.
655: *
656: * Side Effects:
657: * If the file is found in a directory which is not on the path
658: * already (either 'name' is absolute or it is a relative path
659: * [ dir1/.../dirn/file ] which exists below one of the directories
660: * already on the search path), its directory is added to the end
661: * of the path on the assumption that there will be more files in
662: * that directory later on. Sometimes this is true. Sometimes not.
663: *-----------------------------------------------------------------------
664: */
665: char *
666: Dir_FindFile (name, path)
667: char *name; /* the file to find */
668: Lst path; /* the Lst of directories to search */
669: {
670: register char *p1; /* pointer into p->name */
671: register char *p2; /* pointer into name */
672: LstNode ln; /* a list element */
673: register char *file; /* the current filename to check */
674: register Path *p; /* current path member */
675: register char *cp; /* index of first slash, if any */
676: Boolean hasSlash; /* true if 'name' contains a / */
677: struct stat stb; /* Buffer for stat, if necessary */
678: Hash_Entry *entry; /* Entry for mtimes table */
679:
680: /*
681: * Find the final component of the name and note whether it has a
682: * slash in it (the name, I mean)
683: */
1.5 cgd 684: cp = strrchr (name, '/');
1.1 cgd 685: if (cp) {
686: hasSlash = TRUE;
687: cp += 1;
688: } else {
689: hasSlash = FALSE;
690: cp = name;
691: }
692:
693: if (DEBUG(DIR)) {
694: printf("Searching for %s...", name);
695: }
696: /*
697: * No matter what, we always look for the file in the current directory
698: * before anywhere else and we *do not* add the ./ to it if it exists.
699: * This is so there are no conflicts between what the user specifies
700: * (fish.c) and what pmake finds (./fish.c).
701: */
702: if ((!hasSlash || (cp - name == 2 && *name == '.')) &&
703: (Hash_FindEntry (&dot->files, cp) != (Hash_Entry *)NULL)) {
704: if (DEBUG(DIR)) {
705: printf("in '.'\n");
706: }
707: hits += 1;
708: dot->hits += 1;
1.11 ! christos 709: return (estrdup (name));
1.1 cgd 710: }
711:
712: if (Lst_Open (path) == FAILURE) {
713: if (DEBUG(DIR)) {
714: printf("couldn't open path, file not found\n");
715: }
716: misses += 1;
717: return ((char *) NULL);
718: }
719:
720: /*
721: * We look through all the directories on the path seeking one which
722: * contains the final component of the given name and whose final
723: * component(s) match the name's initial component(s). If such a beast
724: * is found, we concatenate the directory name and the final component
725: * and return the resulting string. If we don't find any such thing,
726: * we go on to phase two...
727: */
728: while ((ln = Lst_Next (path)) != NILLNODE) {
729: p = (Path *) Lst_Datum (ln);
730: if (DEBUG(DIR)) {
731: printf("%s...", p->name);
732: }
733: if (Hash_FindEntry (&p->files, cp) != (Hash_Entry *)NULL) {
734: if (DEBUG(DIR)) {
735: printf("here...");
736: }
737: if (hasSlash) {
738: /*
739: * If the name had a slash, its initial components and p's
740: * final components must match. This is false if a mismatch
741: * is encountered before all of the initial components
742: * have been checked (p2 > name at the end of the loop), or
743: * we matched only part of one of the components of p
744: * along with all the rest of them (*p1 != '/').
745: */
746: p1 = p->name + strlen (p->name) - 1;
747: p2 = cp - 2;
1.6 jtc 748: while (p2 >= name && p1 >= p->name && *p1 == *p2) {
1.1 cgd 749: p1 -= 1; p2 -= 1;
750: }
751: if (p2 >= name || (p1 >= p->name && *p1 != '/')) {
752: if (DEBUG(DIR)) {
753: printf("component mismatch -- continuing...");
754: }
755: continue;
756: }
757: }
758: file = str_concat (p->name, cp, STR_ADDSLASH);
759: if (DEBUG(DIR)) {
760: printf("returning %s\n", file);
761: }
762: Lst_Close (path);
763: p->hits += 1;
764: hits += 1;
765: return (file);
766: } else if (hasSlash) {
767: /*
768: * If the file has a leading path component and that component
769: * exactly matches the entire name of the current search
770: * directory, we assume the file doesn't exist and return NULL.
771: */
772: for (p1 = p->name, p2 = name; *p1 && *p1 == *p2; p1++, p2++) {
773: continue;
774: }
775: if (*p1 == '\0' && p2 == cp - 1) {
776: if (DEBUG(DIR)) {
777: printf("must be here but isn't -- returing NULL\n");
778: }
779: Lst_Close (path);
780: return ((char *) NULL);
781: }
782: }
783: }
784:
785: /*
786: * We didn't find the file on any existing members of the directory.
787: * If the name doesn't contain a slash, that means it doesn't exist.
788: * If it *does* contain a slash, however, there is still hope: it
789: * could be in a subdirectory of one of the members of the search
790: * path. (eg. /usr/include and sys/types.h. The above search would
791: * fail to turn up types.h in /usr/include, but it *is* in
792: * /usr/include/sys/types.h) If we find such a beast, we assume there
793: * will be more (what else can we assume?) and add all but the last
794: * component of the resulting name onto the search path (at the
795: * end). This phase is only performed if the file is *not* absolute.
796: */
797: if (!hasSlash) {
798: if (DEBUG(DIR)) {
799: printf("failed.\n");
800: }
801: misses += 1;
802: return ((char *) NULL);
803: }
804:
805: if (*name != '/') {
806: Boolean checkedDot = FALSE;
807:
808: if (DEBUG(DIR)) {
809: printf("failed. Trying subdirectories...");
810: }
811: (void) Lst_Open (path);
812: while ((ln = Lst_Next (path)) != NILLNODE) {
813: p = (Path *) Lst_Datum (ln);
814: if (p != dot) {
815: file = str_concat (p->name, name, STR_ADDSLASH);
816: } else {
817: /*
818: * Checking in dot -- DON'T put a leading ./ on the thing.
819: */
1.11 ! christos 820: file = estrdup(name);
1.1 cgd 821: checkedDot = TRUE;
822: }
823: if (DEBUG(DIR)) {
824: printf("checking %s...", file);
825: }
826:
827:
828: if (stat (file, &stb) == 0) {
829: if (DEBUG(DIR)) {
830: printf("got it.\n");
831: }
832:
833: Lst_Close (path);
834:
835: /*
836: * We've found another directory to search. We know there's
837: * a slash in 'file' because we put one there. We nuke it after
838: * finding it and call Dir_AddDir to add this new directory
839: * onto the existing search path. Once that's done, we restore
840: * the slash and triumphantly return the file name, knowing
841: * that should a file in this directory every be referenced
842: * again in such a manner, we will find it without having to do
843: * numerous numbers of access calls. Hurrah!
844: */
1.5 cgd 845: cp = strrchr (file, '/');
1.1 cgd 846: *cp = '\0';
847: Dir_AddDir (path, file);
848: *cp = '/';
849:
850: /*
851: * Save the modification time so if it's needed, we don't have
852: * to fetch it again.
853: */
854: if (DEBUG(DIR)) {
855: printf("Caching %s for %s\n", Targ_FmtTime(stb.st_mtime),
856: file);
857: }
1.5 cgd 858: entry = Hash_CreateEntry(&mtimes, (char *) file,
1.1 cgd 859: (Boolean *)NULL);
1.7 cgd 860: Hash_SetValue(entry, (long)stb.st_mtime);
1.1 cgd 861: nearmisses += 1;
862: return (file);
863: } else {
864: free (file);
865: }
866: }
867:
868: if (DEBUG(DIR)) {
869: printf("failed. ");
870: }
871: Lst_Close (path);
872:
873: if (checkedDot) {
874: /*
875: * Already checked by the given name, since . was in the path,
876: * so no point in proceeding...
877: */
878: if (DEBUG(DIR)) {
879: printf("Checked . already, returning NULL\n");
880: }
881: return(NULL);
882: }
883: }
884:
885: /*
886: * Didn't find it that way, either. Sigh. Phase 3. Add its directory
887: * onto the search path in any case, just in case, then look for the
888: * thing in the hash table. If we find it, grand. We return a new
889: * copy of the name. Otherwise we sadly return a NULL pointer. Sigh.
890: * Note that if the directory holding the file doesn't exist, this will
891: * do an extra search of the final directory on the path. Unless something
892: * weird happens, this search won't succeed and life will be groovy.
893: *
894: * Sigh. We cannot add the directory onto the search path because
895: * of this amusing case:
896: * $(INSTALLDIR)/$(FILE): $(FILE)
897: *
898: * $(FILE) exists in $(INSTALLDIR) but not in the current one.
899: * When searching for $(FILE), we will find it in $(INSTALLDIR)
900: * b/c we added it here. This is not good...
901: */
902: #ifdef notdef
903: cp[-1] = '\0';
904: Dir_AddDir (path, name);
905: cp[-1] = '/';
906:
907: bigmisses += 1;
908: ln = Lst_Last (path);
909: if (ln == NILLNODE) {
910: return ((char *) NULL);
911: } else {
912: p = (Path *) Lst_Datum (ln);
913: }
914:
915: if (Hash_FindEntry (&p->files, cp) != (Hash_Entry *)NULL) {
1.11 ! christos 916: return (estrdup (name));
1.1 cgd 917: } else {
918: return ((char *) NULL);
919: }
920: #else /* !notdef */
921: if (DEBUG(DIR)) {
922: printf("Looking for \"%s\"...", name);
923: }
924:
925: bigmisses += 1;
926: entry = Hash_FindEntry(&mtimes, name);
927: if (entry != (Hash_Entry *)NULL) {
928: if (DEBUG(DIR)) {
929: printf("got it (in mtime cache)\n");
930: }
1.11 ! christos 931: return(estrdup(name));
1.1 cgd 932: } else if (stat (name, &stb) == 0) {
933: entry = Hash_CreateEntry(&mtimes, name, (Boolean *)NULL);
934: if (DEBUG(DIR)) {
935: printf("Caching %s for %s\n", Targ_FmtTime(stb.st_mtime),
936: name);
937: }
1.7 cgd 938: Hash_SetValue(entry, (long)stb.st_mtime);
1.11 ! christos 939: return (estrdup (name));
1.1 cgd 940: } else {
941: if (DEBUG(DIR)) {
942: printf("failed. Returning NULL\n");
943: }
944: return ((char *)NULL);
945: }
946: #endif /* notdef */
947: }
948:
949: /*-
950: *-----------------------------------------------------------------------
951: * Dir_MTime --
952: * Find the modification time of the file described by gn along the
953: * search path dirSearchPath.
954: *
955: * Results:
956: * The modification time or 0 if it doesn't exist
957: *
958: * Side Effects:
959: * The modification time is placed in the node's mtime slot.
960: * If the node didn't have a path entry before, and Dir_FindFile
961: * found one for it, the full name is placed in the path slot.
962: *-----------------------------------------------------------------------
963: */
964: int
965: Dir_MTime (gn)
966: GNode *gn; /* the file whose modification time is
967: * desired */
968: {
969: char *fullName; /* the full pathname of name */
970: struct stat stb; /* buffer for finding the mod time */
971: Hash_Entry *entry;
972:
973: if (gn->type & OP_ARCHV) {
974: return Arch_MTime (gn);
975: } else if (gn->path == (char *)NULL) {
976: fullName = Dir_FindFile (gn->name, dirSearchPath);
977: } else {
978: fullName = gn->path;
979: }
980:
981: if (fullName == (char *)NULL) {
1.11 ! christos 982: fullName = estrdup(gn->name);
1.1 cgd 983: }
984:
985: entry = Hash_FindEntry(&mtimes, fullName);
986: if (entry != (Hash_Entry *)NULL) {
987: /*
988: * Only do this once -- the second time folks are checking to
989: * see if the file was actually updated, so we need to actually go
990: * to the file system.
991: */
992: if (DEBUG(DIR)) {
993: printf("Using cached time %s for %s\n",
1.7 cgd 994: Targ_FmtTime((time_t)(long)Hash_GetValue(entry)), fullName);
1.1 cgd 995: }
1.7 cgd 996: stb.st_mtime = (time_t)(long)Hash_GetValue(entry);
1.1 cgd 997: Hash_DeleteEntry(&mtimes, entry);
998: } else if (stat (fullName, &stb) < 0) {
999: if (gn->type & OP_MEMBER) {
1.6 jtc 1000: if (fullName != gn->path)
1001: free(fullName);
1.1 cgd 1002: return Arch_MemMTime (gn);
1003: } else {
1004: stb.st_mtime = 0;
1005: }
1006: }
1007: if (fullName && gn->path == (char *)NULL) {
1008: gn->path = fullName;
1009: }
1010:
1011: gn->mtime = stb.st_mtime;
1012: return (gn->mtime);
1013: }
1014:
1015: /*-
1016: *-----------------------------------------------------------------------
1017: * Dir_AddDir --
1018: * Add the given name to the end of the given path. The order of
1019: * the arguments is backwards so ParseDoDependency can do a
1020: * Lst_ForEach of its list of paths...
1021: *
1022: * Results:
1023: * none
1024: *
1025: * Side Effects:
1026: * A structure is added to the list and the directory is
1027: * read and hashed.
1028: *-----------------------------------------------------------------------
1029: */
1030: void
1031: Dir_AddDir (path, name)
1032: Lst path; /* the path to which the directory should be
1033: * added */
1034: char *name; /* the name of the directory to add */
1035: {
1036: LstNode ln; /* node in case Path structure is found */
1037: register Path *p; /* pointer to new Path structure */
1038: DIR *d; /* for reading directory */
1.3 jtc 1039: register struct dirent *dp; /* entry in directory */
1.1 cgd 1040:
1041: ln = Lst_Find (openDirectories, (ClientData)name, DirFindName);
1042: if (ln != NILLNODE) {
1043: p = (Path *)Lst_Datum (ln);
1044: if (Lst_Member(path, (ClientData)p) == NILLNODE) {
1045: p->refCount += 1;
1046: (void)Lst_AtEnd (path, (ClientData)p);
1047: }
1048: } else {
1049: if (DEBUG(DIR)) {
1050: printf("Caching %s...", name);
1051: fflush(stdout);
1052: }
1053:
1054: if ((d = opendir (name)) != (DIR *) NULL) {
1055: p = (Path *) emalloc (sizeof (Path));
1.11 ! christos 1056: p->name = estrdup (name);
1.1 cgd 1057: p->hits = 0;
1058: p->refCount = 1;
1059: Hash_InitTable (&p->files, -1);
1060:
1061: /*
1062: * Skip the first two entries -- these will *always* be . and ..
1063: */
1064: (void)readdir(d);
1065: (void)readdir(d);
1066:
1.3 jtc 1067: while ((dp = readdir (d)) != (struct dirent *) NULL) {
1.10 christos 1068: #if defined(sun) && defined(d_ino) /* d_ino is a sunos4 #define for d_fileno */
1.1 cgd 1069: /*
1070: * The sun directory library doesn't check for a 0 inode
1071: * (0-inode slots just take up space), so we have to do
1072: * it ourselves.
1073: */
1074: if (dp->d_fileno == 0) {
1075: continue;
1076: }
1.10 christos 1077: #endif /* sun && d_ino */
1.1 cgd 1078: (void)Hash_CreateEntry(&p->files, dp->d_name, (Boolean *)NULL);
1079: }
1080: (void) closedir (d);
1081: (void)Lst_AtEnd (openDirectories, (ClientData)p);
1082: (void)Lst_AtEnd (path, (ClientData)p);
1083: }
1084: if (DEBUG(DIR)) {
1085: printf("done\n");
1086: }
1087: }
1088: }
1089:
1090: /*-
1091: *-----------------------------------------------------------------------
1092: * Dir_CopyDir --
1093: * Callback function for duplicating a search path via Lst_Duplicate.
1094: * Ups the reference count for the directory.
1095: *
1096: * Results:
1097: * Returns the Path it was given.
1098: *
1099: * Side Effects:
1100: * The refCount of the path is incremented.
1101: *
1102: *-----------------------------------------------------------------------
1103: */
1104: ClientData
1105: Dir_CopyDir(p)
1.6 jtc 1106: ClientData p;
1.1 cgd 1107: {
1.6 jtc 1108: ((Path *) p)->refCount += 1;
1.1 cgd 1109:
1110: return ((ClientData)p);
1111: }
1112:
1113: /*-
1114: *-----------------------------------------------------------------------
1115: * Dir_MakeFlags --
1116: * Make a string by taking all the directories in the given search
1117: * path and preceding them by the given flag. Used by the suffix
1118: * module to create variables for compilers based on suffix search
1119: * paths.
1120: *
1121: * Results:
1122: * The string mentioned above. Note that there is no space between
1123: * the given flag and each directory. The empty string is returned if
1124: * Things don't go well.
1125: *
1126: * Side Effects:
1127: * None
1128: *-----------------------------------------------------------------------
1129: */
1130: char *
1131: Dir_MakeFlags (flag, path)
1132: char *flag; /* flag which should precede each directory */
1133: Lst path; /* list of directories */
1134: {
1135: char *str; /* the string which will be returned */
1136: char *tstr; /* the current directory preceded by 'flag' */
1137: LstNode ln; /* the node of the current directory */
1138: Path *p; /* the structure describing the current directory */
1139:
1.11 ! christos 1140: str = estrdup ("");
1.1 cgd 1141:
1142: if (Lst_Open (path) == SUCCESS) {
1143: while ((ln = Lst_Next (path)) != NILLNODE) {
1144: p = (Path *) Lst_Datum (ln);
1145: tstr = str_concat (flag, p->name, 0);
1146: str = str_concat (str, tstr, STR_ADDSPACE | STR_DOFREE);
1147: }
1148: Lst_Close (path);
1149: }
1150:
1151: return (str);
1152: }
1153:
1154: /*-
1155: *-----------------------------------------------------------------------
1156: * Dir_Destroy --
1157: * Nuke a directory descriptor, if possible. Callback procedure
1158: * for the suffixes module when destroying a search path.
1159: *
1160: * Results:
1161: * None.
1162: *
1163: * Side Effects:
1164: * If no other path references this directory (refCount == 0),
1165: * the Path and all its data are freed.
1166: *
1167: *-----------------------------------------------------------------------
1168: */
1169: void
1.6 jtc 1170: Dir_Destroy (pp)
1171: ClientData pp; /* The directory descriptor to nuke */
1.1 cgd 1172: {
1.6 jtc 1173: Path *p = (Path *) pp;
1.1 cgd 1174: p->refCount -= 1;
1175:
1176: if (p->refCount == 0) {
1177: LstNode ln;
1178:
1179: ln = Lst_Member (openDirectories, (ClientData)p);
1180: (void) Lst_Remove (openDirectories, ln);
1181:
1182: Hash_DeleteTable (&p->files);
1183: free((Address)p->name);
1184: free((Address)p);
1185: }
1186: }
1187:
1188: /*-
1189: *-----------------------------------------------------------------------
1190: * Dir_ClearPath --
1191: * Clear out all elements of the given search path. This is different
1192: * from destroying the list, notice.
1193: *
1194: * Results:
1195: * None.
1196: *
1197: * Side Effects:
1198: * The path is set to the empty list.
1199: *
1200: *-----------------------------------------------------------------------
1201: */
1202: void
1203: Dir_ClearPath(path)
1204: Lst path; /* Path to clear */
1205: {
1206: Path *p;
1207: while (!Lst_IsEmpty(path)) {
1208: p = (Path *)Lst_DeQueue(path);
1.6 jtc 1209: Dir_Destroy((ClientData) p);
1.1 cgd 1210: }
1211: }
1212:
1213:
1214: /*-
1215: *-----------------------------------------------------------------------
1216: * Dir_Concat --
1217: * Concatenate two paths, adding the second to the end of the first.
1218: * Makes sure to avoid duplicates.
1219: *
1220: * Results:
1221: * None
1222: *
1223: * Side Effects:
1224: * Reference counts for added dirs are upped.
1225: *
1226: *-----------------------------------------------------------------------
1227: */
1228: void
1229: Dir_Concat(path1, path2)
1230: Lst path1; /* Dest */
1231: Lst path2; /* Source */
1232: {
1233: LstNode ln;
1234: Path *p;
1235:
1236: for (ln = Lst_First(path2); ln != NILLNODE; ln = Lst_Succ(ln)) {
1237: p = (Path *)Lst_Datum(ln);
1238: if (Lst_Member(path1, (ClientData)p) == NILLNODE) {
1239: p->refCount += 1;
1240: (void)Lst_AtEnd(path1, (ClientData)p);
1241: }
1242: }
1243: }
1244:
1245: /********** DEBUG INFO **********/
1.5 cgd 1246: void
1.1 cgd 1247: Dir_PrintDirectories()
1248: {
1249: LstNode ln;
1250: Path *p;
1251:
1252: printf ("#*** Directory Cache:\n");
1253: printf ("# Stats: %d hits %d misses %d near misses %d losers (%d%%)\n",
1254: hits, misses, nearmisses, bigmisses,
1255: (hits+bigmisses+nearmisses ?
1256: hits * 100 / (hits + bigmisses + nearmisses) : 0));
1257: printf ("# %-20s referenced\thits\n", "directory");
1258: if (Lst_Open (openDirectories) == SUCCESS) {
1259: while ((ln = Lst_Next (openDirectories)) != NILLNODE) {
1260: p = (Path *) Lst_Datum (ln);
1261: printf ("# %-20s %10d\t%4d\n", p->name, p->refCount, p->hits);
1262: }
1263: Lst_Close (openDirectories);
1264: }
1265: }
1266:
1.6 jtc 1267: static int DirPrintDir (p, dummy)
1268: ClientData p;
1269: ClientData dummy;
1270: {
1271: printf ("%s ", ((Path *) p)->name);
1272: return (dummy ? 0 : 0);
1273: }
1.1 cgd 1274:
1.5 cgd 1275: void
1.1 cgd 1276: Dir_PrintPath (path)
1277: Lst path;
1278: {
1279: Lst_ForEach (path, DirPrintDir, (ClientData)0);
1280: }
CVSweb <webmaster@jp.NetBSD.org>