|
|
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.