VirtualBox

source: kBuild/branches/FREEBSD/src/kmk/dir.c@ 10

Last change on this file since 10 was 10, checked in by (none), 23 years ago

This commit was manufactured by cvs2svn to create branch 'FREEBSD'.

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette