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