Annotation of src/usr.bin/make/dir.c, Revision 1.1
1.1 ! deraadt 1: /* $NetBSD: dir.c,v 1.8 1995/06/14 15:19:07 christos Exp $ */
! 2:
! 3: /*
! 4: * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
! 5: * Copyright (c) 1988, 1989 by Adam de Boor
! 6: * Copyright (c) 1989 by Berkeley Softworks
! 7: * All rights reserved.
! 8: *
! 9: * This code is derived from software contributed to Berkeley by
! 10: * Adam de Boor.
! 11: *
! 12: * Redistribution and use in source and binary forms, with or without
! 13: * modification, are permitted provided that the following conditions
! 14: * are met:
! 15: * 1. Redistributions of source code must retain the above copyright
! 16: * notice, this list of conditions and the following disclaimer.
! 17: * 2. Redistributions in binary form must reproduce the above copyright
! 18: * notice, this list of conditions and the following disclaimer in the
! 19: * documentation and/or other materials provided with the distribution.
! 20: * 3. All advertising materials mentioning features or use of this software
! 21: * must display the following acknowledgement:
! 22: * This product includes software developed by the University of
! 23: * California, Berkeley and its contributors.
! 24: * 4. Neither the name of the University nor the names of its contributors
! 25: * may be used to endorse or promote products derived from this software
! 26: * without specific prior written permission.
! 27: *
! 28: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
! 29: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
! 30: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
! 31: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
! 32: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
! 33: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
! 34: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
! 35: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
! 36: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
! 37: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
! 38: * SUCH DAMAGE.
! 39: */
! 40:
! 41: #ifndef lint
! 42: #if 0
! 43: static char sccsid[] = "@(#)dir.c 5.6 (Berkeley) 12/28/90";
! 44: #else
! 45: static char rcsid[] = "$NetBSD: dir.c,v 1.8 1995/06/14 15:19:07 christos Exp $";
! 46: #endif
! 47: #endif /* not lint */
! 48:
! 49: /*-
! 50: * dir.c --
! 51: * Directory searching using wildcards and/or normal names...
! 52: * Used both for source wildcarding in the Makefile and for finding
! 53: * implicit sources.
! 54: *
! 55: * The interface for this module is:
! 56: * Dir_Init Initialize the module.
! 57: *
! 58: * Dir_End Cleanup the module.
! 59: *
! 60: * Dir_HasWildcards Returns TRUE if the name given it needs to
! 61: * be wildcard-expanded.
! 62: *
! 63: * Dir_Expand Given a pattern and a path, return a Lst of names
! 64: * which match the pattern on the search path.
! 65: *
! 66: * Dir_FindFile Searches for a file on a given search path.
! 67: * If it exists, the entire path is returned.
! 68: * Otherwise NULL is returned.
! 69: *
! 70: * Dir_MTime Return the modification time of a node. The file
! 71: * is searched for along the default search path.
! 72: * The path and mtime fields of the node are filled
! 73: * in.
! 74: *
! 75: * Dir_AddDir Add a directory to a search path.
! 76: *
! 77: * Dir_MakeFlags Given a search path and a command flag, create
! 78: * a string with each of the directories in the path
! 79: * preceded by the command flag and all of them
! 80: * separated by a space.
! 81: *
! 82: * Dir_Destroy Destroy an element of a search path. Frees up all
! 83: * things that can be freed for the element as long
! 84: * as the element is no longer referenced by any other
! 85: * search path.
! 86: * Dir_ClearPath Resets a search path to the empty list.
! 87: *
! 88: * For debugging:
! 89: * Dir_PrintDirectories Print stats about the directory cache.
! 90: */
! 91:
! 92: #include <stdio.h>
! 93: #include <sys/types.h>
! 94: #include <dirent.h>
! 95: #include <sys/stat.h>
! 96: #include "make.h"
! 97: #include "hash.h"
! 98: #include "dir.h"
! 99:
! 100: /*
! 101: * A search path consists of a Lst of Path structures. A Path structure
! 102: * has in it the name of the directory and a hash table of all the files
! 103: * in the directory. This is used to cut down on the number of system
! 104: * calls necessary to find implicit dependents and their like. Since
! 105: * these searches are made before any actions are taken, we need not
! 106: * worry about the directory changing due to creation commands. If this
! 107: * hampers the style of some makefiles, they must be changed.
! 108: *
! 109: * A list of all previously-read directories is kept in the
! 110: * openDirectories Lst. This list is checked first before a directory
! 111: * is opened.
! 112: *
! 113: * The need for the caching of whole directories is brought about by
! 114: * the multi-level transformation code in suff.c, which tends to search
! 115: * for far more files than regular make does. In the initial
! 116: * implementation, the amount of time spent performing "stat" calls was
! 117: * truly astronomical. The problem with hashing at the start is,
! 118: * of course, that pmake doesn't then detect changes to these directories
! 119: * during the course of the make. Three possibilities suggest themselves:
! 120: *
! 121: * 1) just use stat to test for a file's existence. As mentioned
! 122: * above, this is very inefficient due to the number of checks
! 123: * engendered by the multi-level transformation code.
! 124: * 2) use readdir() and company to search the directories, keeping
! 125: * them open between checks. I have tried this and while it
! 126: * didn't slow down the process too much, it could severely
! 127: * affect the amount of parallelism available as each directory
! 128: * open would take another file descriptor out of play for
! 129: * handling I/O for another job. Given that it is only recently
! 130: * that UNIX OS's have taken to allowing more than 20 or 32
! 131: * file descriptors for a process, this doesn't seem acceptable
! 132: * to me.
! 133: * 3) record the mtime of the directory in the Path structure and
! 134: * verify the directory hasn't changed since the contents were
! 135: * hashed. This will catch the creation or deletion of files,
! 136: * but not the updating of files. However, since it is the
! 137: * creation and deletion that is the problem, this could be
! 138: * a good thing to do. Unfortunately, if the directory (say ".")
! 139: * were fairly large and changed fairly frequently, the constant
! 140: * rehashing could seriously degrade performance. It might be
! 141: * good in such cases to keep track of the number of rehashes
! 142: * and if the number goes over a (small) limit, resort to using
! 143: * stat in its place.
! 144: *
! 145: * An additional thing to consider is that pmake is used primarily
! 146: * to create C programs and until recently pcc-based compilers refused
! 147: * to allow you to specify where the resulting object file should be
! 148: * placed. This forced all objects to be created in the current
! 149: * directory. This isn't meant as a full excuse, just an explanation of
! 150: * some of the reasons for the caching used here.
! 151: *
! 152: * One more note: the location of a target's file is only performed
! 153: * on the downward traversal of the graph and then only for terminal
! 154: * nodes in the graph. This could be construed as wrong in some cases,
! 155: * but prevents inadvertent modification of files when the "installed"
! 156: * directory for a file is provided in the search path.
! 157: *
! 158: * Another data structure maintained by this module is an mtime
! 159: * cache used when the searching of cached directories fails to find
! 160: * a file. In the past, Dir_FindFile would simply perform an access()
! 161: * call in such a case to determine if the file could be found using
! 162: * just the name given. When this hit, however, all that was gained
! 163: * was the knowledge that the file existed. Given that an access() is
! 164: * essentially a stat() without the copyout() call, and that the same
! 165: * filesystem overhead would have to be incurred in Dir_MTime, it made
! 166: * sense to replace the access() with a stat() and record the mtime
! 167: * in a cache for when Dir_MTime was actually called.
! 168: */
! 169:
! 170: Lst dirSearchPath; /* main search path */
! 171:
! 172: static Lst openDirectories; /* the list of all open directories */
! 173:
! 174: /*
! 175: * Variables for gathering statistics on the efficiency of the hashing
! 176: * mechanism.
! 177: */
! 178: static int hits, /* Found in directory cache */
! 179: misses, /* Sad, but not evil misses */
! 180: nearmisses, /* Found under search path */
! 181: bigmisses; /* Sought by itself */
! 182:
! 183: static Path *dot; /* contents of current directory */
! 184: static 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:
! 195: static int DirFindName __P((ClientData, ClientData));
! 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));
! 199: static int DirPrintWord __P((ClientData, ClientData));
! 200: static int DirPrintDir __P((ClientData, ClientData));
! 201:
! 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: *-----------------------------------------------------------------------
! 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: *-----------------------------------------------------------------------
! 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)
! 277: ClientData p; /* Current name */
! 278: ClientData dname; /* Desired name */
! 279: {
! 280: return (strcmp (((Path *)p)->name, (char *) dname));
! 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
! 319: * src / *src / *.c properly (just *.c on any of the directories), but it
! 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,
! 357: (isDot ? strdup(entry->name) :
! 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
! 525: DirPrintWord(word, dummy)
! 526: ClientData word;
! 527: ClientData dummy;
! 528: {
! 529: printf("%s ", (char *) word);
! 530:
! 531: return(dummy ? 0 : 0);
! 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:
! 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, (ClientData) 0);
! 643: fputc('\n', stdout);
! 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: */
! 684: cp = strrchr (name, '/');
! 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;
! 709: return (strdup (name));
! 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;
! 748: while (p2 >= name && p1 >= p->name && *p1 == *p2) {
! 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: */
! 820: file = strdup(name);
! 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: */
! 845: cp = strrchr (file, '/');
! 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: }
! 858: entry = Hash_CreateEntry(&mtimes, (char *) file,
! 859: (Boolean *)NULL);
! 860: Hash_SetValue(entry, (long)stb.st_mtime);
! 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) {
! 916: return (strdup (name));
! 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: }
! 931: return(strdup(name));
! 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: }
! 938: Hash_SetValue(entry, (long)stb.st_mtime);
! 939: return (strdup (name));
! 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) {
! 982: fullName = strdup(gn->name);
! 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",
! 994: Targ_FmtTime((time_t)(long)Hash_GetValue(entry)), fullName);
! 995: }
! 996: stb.st_mtime = (time_t)(long)Hash_GetValue(entry);
! 997: Hash_DeleteEntry(&mtimes, entry);
! 998: } else if (stat (fullName, &stb) < 0) {
! 999: if (gn->type & OP_MEMBER) {
! 1000: if (fullName != gn->path)
! 1001: free(fullName);
! 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 */
! 1039: register struct dirent *dp; /* entry in directory */
! 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));
! 1056: p->name = strdup (name);
! 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:
! 1067: while ((dp = readdir (d)) != (struct dirent *) NULL) {
! 1068: #ifdef sun
! 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: }
! 1077: #endif /* sun */
! 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)
! 1106: ClientData p;
! 1107: {
! 1108: ((Path *) p)->refCount += 1;
! 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:
! 1140: str = strdup ("");
! 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
! 1170: Dir_Destroy (pp)
! 1171: ClientData pp; /* The directory descriptor to nuke */
! 1172: {
! 1173: Path *p = (Path *) pp;
! 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);
! 1209: Dir_Destroy((ClientData) p);
! 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 **********/
! 1246: void
! 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:
! 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: }
! 1274:
! 1275: void
! 1276: Dir_PrintPath (path)
! 1277: Lst path;
! 1278: {
! 1279: Lst_ForEach (path, DirPrintDir, (ClientData)0);
! 1280: }