1141104Sharti/*- 294589Sobrien * Copyright (c) 1988, 1989, 1990, 1993 394589Sobrien * The Regents of the University of California. All rights reserved. 45814Sjkh * Copyright (c) 1988, 1989 by Adam de Boor 51590Srgrimes * Copyright (c) 1989 by Berkeley Softworks 61590Srgrimes * All rights reserved. 71590Srgrimes * 81590Srgrimes * This code is derived from software contributed to Berkeley by 91590Srgrimes * Adam de Boor. 101590Srgrimes * 111590Srgrimes * Redistribution and use in source and binary forms, with or without 121590Srgrimes * modification, are permitted provided that the following conditions 131590Srgrimes * are met: 141590Srgrimes * 1. Redistributions of source code must retain the above copyright 151590Srgrimes * notice, this list of conditions and the following disclaimer. 161590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 171590Srgrimes * notice, this list of conditions and the following disclaimer in the 181590Srgrimes * documentation and/or other materials provided with the distribution. 191590Srgrimes * 3. All advertising materials mentioning features or use of this software 201590Srgrimes * must display the following acknowledgement: 211590Srgrimes * This product includes software developed by the University of 221590Srgrimes * California, Berkeley and its contributors. 231590Srgrimes * 4. Neither the name of the University nor the names of its contributors 241590Srgrimes * may be used to endorse or promote products derived from this software 251590Srgrimes * without specific prior written permission. 261590Srgrimes * 271590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 281590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 291590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 301590Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 311590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 321590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 331590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 341590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 351590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 361590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 371590Srgrimes * SUCH DAMAGE. 3862833Swsanchez * 3962833Swsanchez * @(#)dir.c 8.2 (Berkeley) 1/2/94 401590Srgrimes */ 411590Srgrimes 4262833Swsanchez#include <sys/cdefs.h> 4394587Sobrien__FBSDID("$FreeBSD$"); 441590Srgrimes 451590Srgrimes/*- 461590Srgrimes * dir.c -- 471590Srgrimes * Directory searching using wildcards and/or normal names... 481590Srgrimes * Used both for source wildcarding in the Makefile and for finding 491590Srgrimes * implicit sources. 501590Srgrimes * 511590Srgrimes * The interface for this module is: 52143979Sharti * Dir_Init Initialize the module. 531590Srgrimes * 54143979Sharti * Dir_HasWildcards Returns TRUE if the name given it needs to 55143979Sharti * be wildcard-expanded. 561590Srgrimes * 57144020Sharti * Path_Expand Given a pattern and a path, return a Lst of names 58143979Sharti * which match the pattern on the search path. 591590Srgrimes * 60144020Sharti * Path_FindFile Searches for a file on a given search path. 61143979Sharti * If it exists, the entire path is returned. 62143979Sharti * Otherwise NULL is returned. 631590Srgrimes * 64201526Sobrien * Dir_FindHereOrAbove Search for a path in the current directory and 65201526Sobrien * then all the directories above it in turn until 66201526Sobrien * the path is found or we reach the root ("/"). 67201526Sobrien * 68143979Sharti * Dir_MTime Return the modification time of a node. The file 69143979Sharti * is searched for along the default search path. 70143979Sharti * The path and mtime fields of the node are filled in. 711590Srgrimes * 72144020Sharti * Path_AddDir Add a directory to a search path. 731590Srgrimes * 74143979Sharti * Dir_MakeFlags Given a search path and a command flag, create 75143979Sharti * a string with each of the directories in the path 76143979Sharti * preceded by the command flag and all of them 77143979Sharti * separated by a space. 781590Srgrimes * 79143979Sharti * Dir_Destroy Destroy an element of a search path. Frees up all 80143979Sharti * things that can be freed for the element as long 81143979Sharti * as the element is no longer referenced by any other 82143979Sharti * search path. 831590Srgrimes * 84143979Sharti * Dir_ClearPath Resets a search path to the empty list. 85143979Sharti * 861590Srgrimes * For debugging: 871590Srgrimes * Dir_PrintDirectories Print stats about the directory cache. 881590Srgrimes */ 891590Srgrimes 90201526Sobrien#include <sys/param.h> 9175975Sru#include <sys/stat.h> 921590Srgrimes#include <dirent.h> 9375975Sru#include <err.h> 94141104Sharti#include <stdio.h> 95141104Sharti#include <stdlib.h> 96141104Sharti#include <string.h> 97141104Sharti 98141104Sharti#include "arch.h" 99141104Sharti#include "dir.h" 100141104Sharti#include "globals.h" 101141104Sharti#include "GNode.h" 102141104Sharti#include "hash.h" 103141104Sharti#include "lst.h" 104141104Sharti#include "str.h" 105141104Sharti#include "targ.h" 106141104Sharti#include "util.h" 1071590Srgrimes 1081590Srgrimes/* 109144020Sharti * A search path consists of a list of Dir structures. A Dir structure 1101590Srgrimes * has in it the name of the directory and a hash table of all the files 1111590Srgrimes * in the directory. This is used to cut down on the number of system 1121590Srgrimes * calls necessary to find implicit dependents and their like. Since 1131590Srgrimes * these searches are made before any actions are taken, we need not 1141590Srgrimes * worry about the directory changing due to creation commands. If this 1151590Srgrimes * hampers the style of some makefiles, they must be changed. 1161590Srgrimes * 1171590Srgrimes * A list of all previously-read directories is kept in the 118144020Sharti * openDirectories list. This list is checked first before a directory 1191590Srgrimes * is opened. 1201590Srgrimes * 1211590Srgrimes * The need for the caching of whole directories is brought about by 1221590Srgrimes * the multi-level transformation code in suff.c, which tends to search 1231590Srgrimes * for far more files than regular make does. In the initial 1241590Srgrimes * implementation, the amount of time spent performing "stat" calls was 1251590Srgrimes * truly astronomical. The problem with hashing at the start is, 1261590Srgrimes * of course, that pmake doesn't then detect changes to these directories 1271590Srgrimes * during the course of the make. Three possibilities suggest themselves: 1281590Srgrimes * 1291590Srgrimes * 1) just use stat to test for a file's existence. As mentioned 1301590Srgrimes * above, this is very inefficient due to the number of checks 1311590Srgrimes * engendered by the multi-level transformation code. 1321590Srgrimes * 2) use readdir() and company to search the directories, keeping 1331590Srgrimes * them open between checks. I have tried this and while it 1341590Srgrimes * didn't slow down the process too much, it could severely 1351590Srgrimes * affect the amount of parallelism available as each directory 1361590Srgrimes * open would take another file descriptor out of play for 1371590Srgrimes * handling I/O for another job. Given that it is only recently 1381590Srgrimes * that UNIX OS's have taken to allowing more than 20 or 32 1391590Srgrimes * file descriptors for a process, this doesn't seem acceptable 1401590Srgrimes * to me. 141143978Sharti * 3) record the mtime of the directory in the Dir structure and 1421590Srgrimes * verify the directory hasn't changed since the contents were 1431590Srgrimes * hashed. This will catch the creation or deletion of files, 1441590Srgrimes * but not the updating of files. However, since it is the 1451590Srgrimes * creation and deletion that is the problem, this could be 1461590Srgrimes * a good thing to do. Unfortunately, if the directory (say ".") 1471590Srgrimes * were fairly large and changed fairly frequently, the constant 1481590Srgrimes * rehashing could seriously degrade performance. It might be 1491590Srgrimes * good in such cases to keep track of the number of rehashes 1501590Srgrimes * and if the number goes over a (small) limit, resort to using 1511590Srgrimes * stat in its place. 1521590Srgrimes * 1531590Srgrimes * An additional thing to consider is that pmake is used primarily 1541590Srgrimes * to create C programs and until recently pcc-based compilers refused 1551590Srgrimes * to allow you to specify where the resulting object file should be 1561590Srgrimes * placed. This forced all objects to be created in the current 1571590Srgrimes * directory. This isn't meant as a full excuse, just an explanation of 1581590Srgrimes * some of the reasons for the caching used here. 1591590Srgrimes * 1601590Srgrimes * One more note: the location of a target's file is only performed 1611590Srgrimes * on the downward traversal of the graph and then only for terminal 1621590Srgrimes * nodes in the graph. This could be construed as wrong in some cases, 1631590Srgrimes * but prevents inadvertent modification of files when the "installed" 1641590Srgrimes * directory for a file is provided in the search path. 1651590Srgrimes * 1661590Srgrimes * Another data structure maintained by this module is an mtime 1671590Srgrimes * cache used when the searching of cached directories fails to find 168144020Sharti * a file. In the past, Path_FindFile would simply perform an access() 1691590Srgrimes * call in such a case to determine if the file could be found using 1701590Srgrimes * just the name given. When this hit, however, all that was gained 1711590Srgrimes * was the knowledge that the file existed. Given that an access() is 1721590Srgrimes * essentially a stat() without the copyout() call, and that the same 1731590Srgrimes * filesystem overhead would have to be incurred in Dir_MTime, it made 1741590Srgrimes * sense to replace the access() with a stat() and record the mtime 1751590Srgrimes * in a cache for when Dir_MTime was actually called. 1761590Srgrimes */ 1771590Srgrimes 178143978Shartitypedef struct Dir { 179143979Sharti char *name; /* Name of directory */ 180143979Sharti int refCount; /* No. of paths with this directory */ 181143979Sharti int hits; /* No. of times a file has been found here */ 182143979Sharti Hash_Table files; /* Hash table of files in directory */ 183144020Sharti TAILQ_ENTRY(Dir) link; /* allDirs link */ 184143978Sharti} Dir; 185143978Sharti 186144020Sharti/* 187144020Sharti * A path is a list of pointers to directories. These directories are 188144020Sharti * reference counted so a directory can be on more than one path. 189144020Sharti */ 190144020Shartistruct PathElement { 191144020Sharti struct Dir *dir; /* pointer to the directory */ 192144020Sharti TAILQ_ENTRY(PathElement) link; /* path link */ 193144020Sharti}; 194143978Sharti 195138916Sharti/* main search path */ 196144020Shartistruct Path dirSearchPath = TAILQ_HEAD_INITIALIZER(dirSearchPath); 1971590Srgrimes 198138916Sharti/* the list of all open directories */ 199144020Shartistatic TAILQ_HEAD(, Dir) openDirectories = 200144020Sharti TAILQ_HEAD_INITIALIZER(openDirectories); 2011590Srgrimes 2021590Srgrimes/* 2031590Srgrimes * Variables for gathering statistics on the efficiency of the hashing 2041590Srgrimes * mechanism. 2051590Srgrimes */ 206138441Shartistatic int hits; /* Found in directory cache */ 207138441Shartistatic int misses; /* Sad, but not evil misses */ 208138441Shartistatic int nearmisses; /* Found under search path */ 209138441Shartistatic int bigmisses; /* Sought by itself */ 2101590Srgrimes 211143978Shartistatic Dir *dot; /* contents of current directory */ 2121590Srgrimes 213144020Sharti/* Results of doing a last-resort stat in Path_FindFile -- 214138441Sharti * if we have to go to the system to find the file, we might as well 215138441Sharti * have its mtime on record. 216138441Sharti * XXX: If this is done way early, there's a chance other rules will 217138441Sharti * have already updated the file, in which case we'll update it again. 218138441Sharti * Generally, there won't be two rules to update a single file, so this 219138441Sharti * should be ok, but... 220138441Sharti */ 221138441Shartistatic Hash_Table mtimes; 2221590Srgrimes 2231590Srgrimes/*- 2241590Srgrimes *----------------------------------------------------------------------- 2251590Srgrimes * Dir_Init -- 2261590Srgrimes * initialize things for this module 2271590Srgrimes * 2281590Srgrimes * Results: 2291590Srgrimes * none 2301590Srgrimes * 2311590Srgrimes * Side Effects: 232120053Sru * none 2331590Srgrimes *----------------------------------------------------------------------- 2341590Srgrimes */ 2351590Srgrimesvoid 236138232ShartiDir_Init(void) 2371590Srgrimes{ 238138232Sharti 239138441Sharti Hash_InitTable(&mtimes, 0); 240120053Sru} 2418874Srgrimes 242120053Sru/*- 243120053Sru *----------------------------------------------------------------------- 244120053Sru * Dir_InitDot -- 245120053Sru * initialize the "." directory 246120053Sru * 247120053Sru * Results: 248120053Sru * none 249120053Sru * 250120053Sru * Side Effects: 251120053Sru * some directories may be opened. 252120053Sru *----------------------------------------------------------------------- 253120053Sru */ 254120053Sruvoid 255138232ShartiDir_InitDot(void) 256120053Sru{ 257138222Sharti 258144020Sharti dot = Path_AddDir(NULL, "."); 259144020Sharti if (dot == NULL) 260138441Sharti err(1, "cannot open current directory"); 2611590Srgrimes 262138441Sharti /* 263138441Sharti * We always need to have dot around, so we increment its 264138441Sharti * reference count to make sure it's not destroyed. 265138441Sharti */ 266138441Sharti dot->refCount += 1; 2671590Srgrimes} 2681590Srgrimes 2691590Srgrimes/*- 2701590Srgrimes *----------------------------------------------------------------------- 2711590Srgrimes * Dir_HasWildcards -- 272104696Sjmallett * See if the given name has any wildcard characters in it. 2731590Srgrimes * 2741590Srgrimes * Results: 2751590Srgrimes * returns TRUE if the word should be expanded, FALSE otherwise 2761590Srgrimes * 2771590Srgrimes * Side Effects: 2781590Srgrimes * none 2791590Srgrimes *----------------------------------------------------------------------- 2801590Srgrimes */ 2811590SrgrimesBoolean 282138455ShartiDir_HasWildcards(const char *name) 2831590Srgrimes{ 284138455Sharti const char *cp; 285138441Sharti int wild = 0, brace = 0, bracket = 0; 2868874Srgrimes 287138441Sharti for (cp = name; *cp; cp++) { 288138441Sharti switch (*cp) { 289138441Sharti case '{': 290138441Sharti brace++; 291138441Sharti wild = 1; 292138441Sharti break; 293138441Sharti case '}': 294138441Sharti brace--; 295138441Sharti break; 296138441Sharti case '[': 297138441Sharti bracket++; 298138441Sharti wild = 1; 299138441Sharti break; 300138441Sharti case ']': 301138441Sharti bracket--; 302138441Sharti break; 303138441Sharti case '?': 304138441Sharti case '*': 305138441Sharti wild = 1; 306138441Sharti break; 307138441Sharti default: 308138441Sharti break; 309138441Sharti } 3101590Srgrimes } 311138441Sharti return (wild && bracket == 0 && brace == 0); 3121590Srgrimes} 3131590Srgrimes 3141590Srgrimes/*- 3151590Srgrimes *----------------------------------------------------------------------- 3161590Srgrimes * DirMatchFiles -- 317143979Sharti * Given a pattern and a Dir structure, see if any files 3181590Srgrimes * match the pattern and add their names to the 'expansions' list if 3191590Srgrimes * any do. This is incomplete -- it doesn't take care of patterns like 3201590Srgrimes * src / *src / *.c properly (just *.c on any of the directories), but it 3211590Srgrimes * will do for now. 3221590Srgrimes * 3231590Srgrimes * Results: 3241590Srgrimes * Always returns 0 3251590Srgrimes * 3261590Srgrimes * Side Effects: 3271590Srgrimes * File names are added to the expansions lst. The directory will be 3281590Srgrimes * fully hashed when this is done. 3291590Srgrimes *----------------------------------------------------------------------- 3301590Srgrimes */ 3311590Srgrimesstatic int 332143978ShartiDirMatchFiles(const char *pattern, const Dir *p, Lst *expansions) 3331590Srgrimes{ 334143979Sharti Hash_Search search; /* Index into the directory's table */ 335143979Sharti Hash_Entry *entry; /* Current entry in the table */ 336143979Sharti Boolean isDot; /* TRUE if the directory being searched is . */ 3378874Srgrimes 338138441Sharti isDot = (*p->name == '.' && p->name[1] == '\0'); 3398874Srgrimes 340138441Sharti for (entry = Hash_EnumFirst(&p->files, &search); 341138441Sharti entry != NULL; 342138441Sharti entry = Hash_EnumNext(&search)) { 343138441Sharti /* 344138441Sharti * See if the file matches the given pattern. Note we follow 345138441Sharti * the UNIX convention that dot files will only be found if 346138441Sharti * the pattern begins with a dot (note also that as a side 347138441Sharti * effect of the hashing scheme, .* won't match . or .. 348138441Sharti * since they aren't hashed). 349138441Sharti */ 350138441Sharti if (Str_Match(entry->name, pattern) && 351138441Sharti ((entry->name[0] != '.') || 352138441Sharti (pattern[0] == '.'))) { 353138441Sharti Lst_AtEnd(expansions, (isDot ? estrdup(entry->name) : 354138441Sharti str_concat(p->name, entry->name, STR_ADDSLASH))); 355138441Sharti } 3561590Srgrimes } 357138441Sharti return (0); 3581590Srgrimes} 3591590Srgrimes 3601590Srgrimes/*- 3611590Srgrimes *----------------------------------------------------------------------- 3621590Srgrimes * DirExpandCurly -- 3631590Srgrimes * Expand curly braces like the C shell. Does this recursively. 3641590Srgrimes * Note the special case: if after the piece of the curly brace is 3651590Srgrimes * done there are no wildcard characters in the result, the result is 366104696Sjmallett * placed on the list WITHOUT CHECKING FOR ITS EXISTENCE. The 367104696Sjmallett * given arguments are the entire word to expand, the first curly 368104696Sjmallett * brace in the word, the search path, and the list to store the 369104696Sjmallett * expansions in. 3701590Srgrimes * 3711590Srgrimes * Results: 3721590Srgrimes * None. 3731590Srgrimes * 3741590Srgrimes * Side Effects: 3751590Srgrimes * The given list is filled with the expansions... 3761590Srgrimes * 3771590Srgrimes *----------------------------------------------------------------------- 3781590Srgrimes */ 3791590Srgrimesstatic void 380144020ShartiDirExpandCurly(const char *word, const char *brace, struct Path *path, 381144020Sharti Lst *expansions) 3821590Srgrimes{ 383138455Sharti const char *end; /* Character after the closing brace */ 384138455Sharti const char *cp; /* Current position in brace clause */ 385138455Sharti const char *start; /* Start of current piece of brace clause */ 386138441Sharti int bracelevel; /* Number of braces we've seen. If we see a right brace 387138441Sharti * when this is 0, we've hit the end of the clause. */ 388138441Sharti char *file; /* Current expansion */ 389143979Sharti int otherLen; /* The length of the other pieces of the expansion 390138441Sharti * (chars before and after the clause in 'word') */ 391138441Sharti char *cp2; /* Pointer for checking for wildcards in 392138441Sharti * expansion before calling Dir_Expand */ 3931590Srgrimes 394138441Sharti start = brace + 1; 3951590Srgrimes 3961590Srgrimes /* 397138441Sharti * Find the end of the brace clause first, being wary of nested brace 398138441Sharti * clauses. 3991590Srgrimes */ 400138441Sharti for (end = start, bracelevel = 0; *end != '\0'; end++) { 401138441Sharti if (*end == '{') 402138441Sharti bracelevel++; 403138441Sharti else if ((*end == '}') && (bracelevel-- == 0)) 404138441Sharti break; 4051590Srgrimes } 406138441Sharti if (*end == '\0') { 407138441Sharti Error("Unterminated {} clause \"%s\"", start); 408138441Sharti return; 409138441Sharti } else 410138441Sharti end++; 4111590Srgrimes 412138441Sharti otherLen = brace - word + strlen(end); 413138441Sharti 414138441Sharti for (cp = start; cp < end; cp++) { 415138441Sharti /* 416138441Sharti * Find the end of this piece of the clause. 417138441Sharti */ 418138441Sharti bracelevel = 0; 419138441Sharti while (*cp != ',') { 420138441Sharti if (*cp == '{') 421138441Sharti bracelevel++; 422138441Sharti else if ((*cp == '}') && (bracelevel-- <= 0)) 423138441Sharti break; 424138441Sharti cp++; 425138441Sharti } 426138441Sharti /* 427138441Sharti * Allocate room for the combination and install the 428138441Sharti * three pieces. 429138441Sharti */ 430138441Sharti file = emalloc(otherLen + cp - start + 1); 431138441Sharti if (brace != word) 432138441Sharti strncpy(file, word, brace - word); 433138441Sharti if (cp != start) 434138441Sharti strncpy(&file[brace - word], start, cp - start); 435138441Sharti strcpy(&file[(brace - word) + (cp - start)], end); 436138441Sharti 437138441Sharti /* 438138441Sharti * See if the result has any wildcards in it. If we find one, 439138441Sharti * call Dir_Expand right away, telling it to place the result 440138441Sharti * on our list of expansions. 441138441Sharti */ 442138441Sharti for (cp2 = file; *cp2 != '\0'; cp2++) { 443138441Sharti switch (*cp2) { 444138441Sharti case '*': 445138441Sharti case '?': 446138441Sharti case '{': 447138441Sharti case '[': 448144020Sharti Path_Expand(file, path, expansions); 449138441Sharti goto next; 450138441Sharti default: 451138441Sharti break; 452138441Sharti } 453138441Sharti } 454138441Sharti if (*cp2 == '\0') { 455138441Sharti /* 456138441Sharti * Hit the end w/o finding any wildcards, so stick 457138441Sharti * the expansion on the end of the list. 458138441Sharti */ 459138441Sharti Lst_AtEnd(expansions, file); 460138441Sharti } else { 461138441Sharti next: 462138441Sharti free(file); 463138441Sharti } 464138441Sharti start = cp + 1; 4651590Srgrimes } 4661590Srgrimes} 4671590Srgrimes 4681590Srgrimes/*- 4691590Srgrimes *----------------------------------------------------------------------- 4701590Srgrimes * DirExpandInt -- 4711590Srgrimes * Internal expand routine. Passes through the directories in the 4721590Srgrimes * path one by one, calling DirMatchFiles for each. NOTE: This still 473104696Sjmallett * doesn't handle patterns in directories... Works given a word to 474104696Sjmallett * expand, a path to look in, and a list to store expansions in. 4751590Srgrimes * 4761590Srgrimes * Results: 4771590Srgrimes * None. 4781590Srgrimes * 4791590Srgrimes * Side Effects: 4801590Srgrimes * Things are added to the expansions list. 4811590Srgrimes * 4821590Srgrimes *----------------------------------------------------------------------- 4831590Srgrimes */ 4841590Srgrimesstatic void 485144020ShartiDirExpandInt(const char *word, const struct Path *path, Lst *expansions) 4861590Srgrimes{ 487144020Sharti struct PathElement *pe; 4881590Srgrimes 489144020Sharti TAILQ_FOREACH(pe, path, link) 490144020Sharti DirMatchFiles(word, pe->dir, expansions); 4911590Srgrimes} 4921590Srgrimes 4931590Srgrimes/*- 4941590Srgrimes *----------------------------------------------------------------------- 4951590Srgrimes * Dir_Expand -- 4961590Srgrimes * Expand the given word into a list of words by globbing it looking 4971590Srgrimes * in the directories on the given search path. 4981590Srgrimes * 4991590Srgrimes * Results: 5001590Srgrimes * A list of words consisting of the files which exist along the search 501104696Sjmallett * path matching the given pattern is placed in expansions. 5021590Srgrimes * 5031590Srgrimes * Side Effects: 5041590Srgrimes * Directories may be opened. Who knows? 5051590Srgrimes *----------------------------------------------------------------------- 5061590Srgrimes */ 5071590Srgrimesvoid 508144020ShartiPath_Expand(char *word, struct Path *path, Lst *expansions) 5091590Srgrimes{ 510142209Sharti LstNode *ln; 511138441Sharti char *cp; 5121590Srgrimes 513138441Sharti DEBUGF(DIR, ("expanding \"%s\"...", word)); 5148874Srgrimes 515138441Sharti cp = strchr(word, '{'); 516138441Sharti if (cp != NULL) 5171590Srgrimes DirExpandCurly(word, cp, path, expansions); 518138441Sharti else { 519138441Sharti cp = strchr(word, '/'); 520138441Sharti if (cp != NULL) { 521138441Sharti /* 522138441Sharti * The thing has a directory component -- find the 523138441Sharti * first wildcard in the string. 524138441Sharti */ 525138441Sharti for (cp = word; *cp != '\0'; cp++) { 526138441Sharti if (*cp == '?' || *cp == '[' || 527138441Sharti *cp == '*' || *cp == '{') { 528138441Sharti break; 529138441Sharti } 530138441Sharti } 531138441Sharti if (*cp == '{') { 532138441Sharti /* 533138441Sharti * This one will be fun. 534138441Sharti */ 535138441Sharti DirExpandCurly(word, cp, path, expansions); 536138441Sharti return; 537138441Sharti } else if (*cp != '\0') { 538138441Sharti /* 539138441Sharti * Back up to the start of the component 540138441Sharti */ 541138441Sharti char *dirpath; 5421590Srgrimes 543138441Sharti while (cp > word && *cp != '/') 544138441Sharti cp--; 545138441Sharti if (cp != word) { 546138441Sharti char sc; 547138441Sharti 548138441Sharti /* 549138441Sharti * If the glob isn't in the first 550138441Sharti * component, try and find all the 551138441Sharti * components up to the one with a 552138441Sharti * wildcard. 553138441Sharti */ 554138441Sharti sc = cp[1]; 555138441Sharti cp[1] = '\0'; 556144020Sharti dirpath = Path_FindFile(word, path); 557138441Sharti cp[1] = sc; 558138441Sharti /* 559138441Sharti * dirpath is null if can't find the 560138441Sharti * leading component 561144020Sharti * XXX: Path_FindFile won't find internal 562138441Sharti * components. i.e. if the path contains 563138441Sharti * ../Etc/Object and we're looking for 564138441Sharti * Etc, * it won't be found. Ah well. 565138441Sharti * Probably not important. 566138441Sharti */ 567138441Sharti if (dirpath != NULL) { 568138441Sharti char *dp = 569138441Sharti &dirpath[strlen(dirpath) 570138441Sharti - 1]; 571144020Sharti struct Path tp = 572144020Sharti TAILQ_HEAD_INITIALIZER(tp); 573138441Sharti 574138441Sharti if (*dp == '/') 575138441Sharti *dp = '\0'; 576144020Sharti Path_AddDir(&tp, dirpath); 577138916Sharti DirExpandInt(cp + 1, &tp, 578138441Sharti expansions); 579144020Sharti Path_Clear(&tp); 580138441Sharti } 581138441Sharti } else { 582138441Sharti /* 583138441Sharti * Start the search from the local 584138441Sharti * directory 585138441Sharti */ 586138441Sharti DirExpandInt(word, path, expansions); 587138441Sharti } 588138441Sharti } else { 589138441Sharti /* 590138441Sharti * Return the file -- this should never happen. 591138441Sharti */ 592138441Sharti DirExpandInt(word, path, expansions); 593138441Sharti } 5941590Srgrimes } else { 595138441Sharti /* 596138441Sharti * First the files in dot 597138441Sharti */ 598138441Sharti DirMatchFiles(word, dot, expansions); 599138441Sharti 600138441Sharti /* 601138441Sharti * Then the files in every other directory on the path. 602138441Sharti */ 603138441Sharti DirExpandInt(word, path, expansions); 6041590Srgrimes } 6051590Srgrimes } 606138441Sharti if (DEBUG(DIR)) { 607142209Sharti LST_FOREACH(ln, expansions) 608142209Sharti DEBUGF(DIR, ("%s ", (const char *)Lst_Datum(ln))); 609138441Sharti DEBUGF(DIR, ("\n")); 610138441Sharti } 6111590Srgrimes} 6121590Srgrimes 613144020Sharti/** 614144020Sharti * Path_FindFile 6151590Srgrimes * Find the file with the given name along the given search path. 6161590Srgrimes * 6171590Srgrimes * Results: 6181590Srgrimes * The path to the file or NULL. This path is guaranteed to be in a 6191590Srgrimes * different part of memory than name and so may be safely free'd. 6201590Srgrimes * 6211590Srgrimes * Side Effects: 6221590Srgrimes * If the file is found in a directory which is not on the path 6231590Srgrimes * already (either 'name' is absolute or it is a relative path 6241590Srgrimes * [ dir1/.../dirn/file ] which exists below one of the directories 6251590Srgrimes * already on the search path), its directory is added to the end 6261590Srgrimes * of the path on the assumption that there will be more files in 6271590Srgrimes * that directory later on. Sometimes this is true. Sometimes not. 6281590Srgrimes */ 6291590Srgrimeschar * 630144020ShartiPath_FindFile(char *name, struct Path *path) 6311590Srgrimes{ 632138441Sharti char *p1; /* pointer into p->name */ 633138441Sharti char *p2; /* pointer into name */ 634138441Sharti char *file; /* the current filename to check */ 635144020Sharti const struct PathElement *pe; /* current path member */ 636138441Sharti char *cp; /* final component of the name */ 637138441Sharti Boolean hasSlash; /* true if 'name' contains a / */ 638138441Sharti struct stat stb; /* Buffer for stat, if necessary */ 639138441Sharti Hash_Entry *entry; /* Entry for mtimes table */ 6408874Srgrimes 641138441Sharti /* 642138441Sharti * Find the final component of the name and note whether it has a 643138441Sharti * slash in it (the name, I mean) 644138441Sharti */ 645138441Sharti cp = strrchr(name, '/'); 646138441Sharti if (cp != NULL) { 647138441Sharti hasSlash = TRUE; 648138441Sharti cp += 1; 649138441Sharti } else { 650138441Sharti hasSlash = FALSE; 651138441Sharti cp = name; 652138441Sharti } 6538874Srgrimes 654138441Sharti DEBUGF(DIR, ("Searching for %s...", name)); 655138441Sharti /* 656138441Sharti * No matter what, we always look for the file in the current directory 657138441Sharti * before anywhere else and we *do not* add the ./ to it if it exists. 658138441Sharti * This is so there are no conflicts between what the user specifies 659138441Sharti * (fish.c) and what pmake finds (./fish.c). 660138441Sharti */ 661138441Sharti if ((!hasSlash || (cp - name == 2 && *name == '.')) && 662138441Sharti (Hash_FindEntry(&dot->files, cp) != NULL)) { 663138441Sharti DEBUGF(DIR, ("in '.'\n")); 664138441Sharti hits += 1; 665138441Sharti dot->hits += 1; 666138441Sharti return (estrdup(name)); 667138441Sharti } 6688874Srgrimes 669138441Sharti /* 670138441Sharti * We look through all the directories on the path seeking one which 671138441Sharti * contains the final component of the given name and whose final 672138441Sharti * component(s) match the name's initial component(s). If such a beast 673138441Sharti * is found, we concatenate the directory name and the final component 674138441Sharti * and return the resulting string. If we don't find any such thing, 675138441Sharti * we go on to phase two... 676138441Sharti */ 677144020Sharti TAILQ_FOREACH(pe, path, link) { 678144020Sharti DEBUGF(DIR, ("%s...", pe->dir->name)); 679144020Sharti if (Hash_FindEntry(&pe->dir->files, cp) != NULL) { 680138441Sharti DEBUGF(DIR, ("here...")); 681138441Sharti if (hasSlash) { 682138441Sharti /* 683138441Sharti * If the name had a slash, its initial 684138441Sharti * components and p's final components must 685138441Sharti * match. This is false if a mismatch is 686138441Sharti * encountered before all of the initial 687138441Sharti * components have been checked (p2 > name at 688138441Sharti * the end of the loop), or we matched only 689138441Sharti * part of one of the components of p 690138441Sharti * along with all the rest of them (*p1 != '/'). 691138441Sharti */ 692144020Sharti p1 = pe->dir->name + strlen(pe->dir->name) - 1; 693138441Sharti p2 = cp - 2; 694144020Sharti while (p2 >= name && p1 >= pe->dir->name && 695138441Sharti *p1 == *p2) { 696138441Sharti p1 -= 1; p2 -= 1; 697138441Sharti } 698144020Sharti if (p2 >= name || (p1 >= pe->dir->name && 699138441Sharti *p1 != '/')) { 700138441Sharti DEBUGF(DIR, ("component mismatch -- " 701138441Sharti "continuing...")); 702138441Sharti continue; 703138441Sharti } 704138441Sharti } 705144020Sharti file = str_concat(pe->dir->name, cp, STR_ADDSLASH); 706138441Sharti DEBUGF(DIR, ("returning %s\n", file)); 707144020Sharti pe->dir->hits += 1; 708138441Sharti hits += 1; 709138441Sharti return (file); 710138441Sharti } else if (hasSlash) { 711138441Sharti /* 712138441Sharti * If the file has a leading path component and that 713138441Sharti * component exactly matches the entire name of the 714138441Sharti * current search directory, we assume the file 715138441Sharti * doesn't exist and return NULL. 716138441Sharti */ 717144020Sharti for (p1 = pe->dir->name, p2 = name; *p1 && *p1 == *p2; 718138441Sharti p1++, p2++) 719138441Sharti continue; 720138441Sharti if (*p1 == '\0' && p2 == cp - 1) { 721138441Sharti if (*cp == '\0' || ISDOT(cp) || ISDOTDOT(cp)) { 722138441Sharti DEBUGF(DIR, ("returning %s\n", name)); 723138441Sharti return (estrdup(name)); 724138441Sharti } else { 725138441Sharti DEBUGF(DIR, ("must be here but isn't --" 726138441Sharti " returning NULL\n")); 727138441Sharti return (NULL); 728138441Sharti } 729138441Sharti } 7301590Srgrimes } 7311590Srgrimes } 7328874Srgrimes 733138441Sharti /* 734138441Sharti * We didn't find the file on any existing members of the directory. 735138441Sharti * If the name doesn't contain a slash, that means it doesn't exist. 736138441Sharti * If it *does* contain a slash, however, there is still hope: it 737138441Sharti * could be in a subdirectory of one of the members of the search 738138441Sharti * path. (eg. /usr/include and sys/types.h. The above search would 739138441Sharti * fail to turn up types.h in /usr/include, but it *is* in 740138441Sharti * /usr/include/sys/types.h) If we find such a beast, we assume there 741138441Sharti * will be more (what else can we assume?) and add all but the last 742138441Sharti * component of the resulting name onto the search path (at the 743138441Sharti * end). This phase is only performed if the file is *not* absolute. 744138441Sharti */ 745138441Sharti if (!hasSlash) { 746138441Sharti DEBUGF(DIR, ("failed.\n")); 747138441Sharti misses += 1; 748138441Sharti return (NULL); 749138441Sharti } 7508874Srgrimes 751138441Sharti if (*name != '/') { 752138441Sharti Boolean checkedDot = FALSE; 7538874Srgrimes 754138441Sharti DEBUGF(DIR, ("failed. Trying subdirectories...")); 755144020Sharti TAILQ_FOREACH(pe, path, link) { 756144020Sharti if (pe->dir != dot) { 757144020Sharti file = str_concat(pe->dir->name, 758144020Sharti name, STR_ADDSLASH); 759138441Sharti } else { 760138441Sharti /* 761138441Sharti * Checking in dot -- DON'T put a leading ./ 762138441Sharti * on the thing. 763138441Sharti */ 764138441Sharti file = estrdup(name); 765138441Sharti checkedDot = TRUE; 766138441Sharti } 767138441Sharti DEBUGF(DIR, ("checking %s...", file)); 7688874Srgrimes 769138441Sharti if (stat(file, &stb) == 0) { 770138441Sharti DEBUGF(DIR, ("got it.\n")); 7718874Srgrimes 772138441Sharti /* 773138441Sharti * We've found another directory to search. We 774138441Sharti * know there's a slash in 'file' because we put 775138441Sharti * one there. We nuke it after finding it and 776144020Sharti * call Path_AddDir to add this new directory 777138441Sharti * onto the existing search path. Once that's 778138441Sharti * done, we restore the slash and triumphantly 779138441Sharti * return the file name, knowing that should a 780138441Sharti * file in this directory every be referenced 781138441Sharti * again in such a manner, we will find it 782138441Sharti * without having to do numerous numbers of 783138441Sharti * access calls. Hurrah! 784138441Sharti */ 785138441Sharti cp = strrchr(file, '/'); 786138441Sharti *cp = '\0'; 787144020Sharti Path_AddDir(path, file); 788138441Sharti *cp = '/'; 7898874Srgrimes 790138441Sharti /* 791138441Sharti * Save the modification time so if 792138441Sharti * it's needed, we don't have to fetch it again. 793138441Sharti */ 794138441Sharti DEBUGF(DIR, ("Caching %s for %s\n", 795138441Sharti Targ_FmtTime(stb.st_mtime), file)); 796138441Sharti entry = Hash_CreateEntry(&mtimes, file, 797138441Sharti (Boolean *)NULL); 798138441Sharti Hash_SetValue(entry, 799138441Sharti (void *)(long)stb.st_mtime); 800138441Sharti nearmisses += 1; 801138441Sharti return (file); 802138441Sharti } else { 803138441Sharti free(file); 804138441Sharti } 805138441Sharti } 8068874Srgrimes 807138441Sharti DEBUGF(DIR, ("failed. ")); 8081590Srgrimes 809138441Sharti if (checkedDot) { 810138441Sharti /* 811138441Sharti * Already checked by the given name, since . was in 812138441Sharti * the path, so no point in proceeding... 813138441Sharti */ 814138441Sharti DEBUGF(DIR, ("Checked . already, returning NULL\n")); 815138441Sharti return (NULL); 816138441Sharti } 8171590Srgrimes } 8188874Srgrimes 819138441Sharti /* 820138441Sharti * Didn't find it that way, either. Sigh. Phase 3. Add its directory 821138441Sharti * onto the search path in any case, just in case, then look for the 822138441Sharti * thing in the hash table. If we find it, grand. We return a new 823138441Sharti * copy of the name. Otherwise we sadly return a NULL pointer. Sigh. 824138441Sharti * Note that if the directory holding the file doesn't exist, this will 825138441Sharti * do an extra search of the final directory on the path. Unless 826138441Sharti * something weird happens, this search won't succeed and life will 827138441Sharti * be groovy. 828138441Sharti * 829138441Sharti * Sigh. We cannot add the directory onto the search path because 830138441Sharti * of this amusing case: 831138441Sharti * $(INSTALLDIR)/$(FILE): $(FILE) 832138441Sharti * 833138441Sharti * $(FILE) exists in $(INSTALLDIR) but not in the current one. 834138441Sharti * When searching for $(FILE), we will find it in $(INSTALLDIR) 835138441Sharti * b/c we added it here. This is not good... 836138441Sharti */ 837138441Sharti DEBUGF(DIR, ("Looking for \"%s\"...", name)); 8388874Srgrimes 839138441Sharti bigmisses += 1; 840138441Sharti entry = Hash_FindEntry(&mtimes, name); 841138441Sharti if (entry != NULL) { 842138441Sharti DEBUGF(DIR, ("got it (in mtime cache)\n")); 843138441Sharti return (estrdup(name)); 844138441Sharti } else if (stat (name, &stb) == 0) { 845138441Sharti entry = Hash_CreateEntry(&mtimes, name, (Boolean *)NULL); 846138441Sharti DEBUGF(DIR, ("Caching %s for %s\n", 847138441Sharti Targ_FmtTime(stb.st_mtime), name)); 848138441Sharti Hash_SetValue(entry, (void *)(long)stb.st_mtime); 849138441Sharti return (estrdup(name)); 850138441Sharti } else { 851138441Sharti DEBUGF(DIR, ("failed. Returning NULL\n")); 852138441Sharti return (NULL); 853138441Sharti } 8541590Srgrimes} 8551590Srgrimes 8561590Srgrimes/*- 8571590Srgrimes *----------------------------------------------------------------------- 858201526Sobrien * Dir_FindHereOrAbove -- 859201526Sobrien * search for a path starting at a given directory and then working 860201526Sobrien * our way up towards the root. 861201526Sobrien * 862201526Sobrien * Input: 863201526Sobrien * here starting directory 864201526Sobrien * search_path the path we are looking for 865201526Sobrien * result the result of a successful search is placed here 866201526Sobrien * rlen the length of the result buffer 867201526Sobrien * (typically MAXPATHLEN + 1) 868201526Sobrien * 869201526Sobrien * Results: 870201526Sobrien * 0 on failure, 1 on success [in which case the found path is put 871201526Sobrien * in the result buffer]. 872201526Sobrien * 873201526Sobrien * Side Effects: 874201526Sobrien *----------------------------------------------------------------------- 875201526Sobrien */ 876201526Sobrienint 877201526SobrienDir_FindHereOrAbove(char *here, char *search_path, char *result, int rlen) 878201526Sobrien{ 879201526Sobrien struct stat st; 880201526Sobrien char dirbase[MAXPATHLEN + 1], *db_end; 881201526Sobrien char try[MAXPATHLEN + 1], *try_end; 882201526Sobrien 883201526Sobrien /* copy out our starting point */ 884201526Sobrien snprintf(dirbase, sizeof(dirbase), "%s", here); 885201526Sobrien db_end = dirbase + strlen(dirbase); 886201526Sobrien 887201526Sobrien /* loop until we determine a result */ 888201526Sobrien while (1) { 889201526Sobrien /* try and stat(2) it ... */ 890201526Sobrien snprintf(try, sizeof(try), "%s/%s", dirbase, search_path); 891201526Sobrien if (stat(try, &st) != -1) { 892201526Sobrien /* 893201526Sobrien * Success! If we found a file, chop off 894201526Sobrien * the filename so we return a directory. 895201526Sobrien */ 896201526Sobrien if ((st.st_mode & S_IFMT) != S_IFDIR) { 897201526Sobrien try_end = try + strlen(try); 898201526Sobrien while (try_end > try && *try_end != '/') 899201526Sobrien try_end--; 900201526Sobrien if (try_end > try) 901201526Sobrien *try_end = 0; /* chop! */ 902201526Sobrien } 903201526Sobrien 904201526Sobrien /* 905201526Sobrien * Done! 906201526Sobrien */ 907201526Sobrien snprintf(result, rlen, "%s", try); 908201526Sobrien return(1); 909201526Sobrien } 910201526Sobrien 911201526Sobrien /* 912201526Sobrien * Nope, we didn't find it. If we used up dirbase we've 913201526Sobrien * reached the root and failed. 914201526Sobrien */ 915201526Sobrien if (db_end == dirbase) 916201526Sobrien break; /* Failed! */ 917201526Sobrien 918201526Sobrien /* 919201526Sobrien * truncate dirbase from the end to move up a dir 920201526Sobrien */ 921201526Sobrien while (db_end > dirbase && *db_end != '/') 922201526Sobrien db_end--; 923201526Sobrien *db_end = 0; /* chop! */ 924201526Sobrien 925201526Sobrien } /* while (1) */ 926201526Sobrien 927201526Sobrien /* 928201526Sobrien * We failed... 929201526Sobrien */ 930201526Sobrien return(0); 931201526Sobrien} 932201526Sobrien 933201526Sobrien/*- 934201526Sobrien *----------------------------------------------------------------------- 9351590Srgrimes * Dir_MTime -- 9361590Srgrimes * Find the modification time of the file described by gn along the 9371590Srgrimes * search path dirSearchPath. 9388874Srgrimes * 9391590Srgrimes * Results: 9401590Srgrimes * The modification time or 0 if it doesn't exist 9411590Srgrimes * 9421590Srgrimes * Side Effects: 9431590Srgrimes * The modification time is placed in the node's mtime slot. 944199419Sobrien * If the node didn't have a path entry before, and Path_FindFile 9451590Srgrimes * found one for it, the full name is placed in the path slot. 9461590Srgrimes *----------------------------------------------------------------------- 9471590Srgrimes */ 9481590Srgrimesint 949138232ShartiDir_MTime(GNode *gn) 9501590Srgrimes{ 951143979Sharti char *fullName; /* the full pathname of name */ 952138441Sharti struct stat stb; /* buffer for finding the mod time */ 953138441Sharti Hash_Entry *entry; 9548874Srgrimes 955138441Sharti if (gn->type & OP_ARCHV) 956138441Sharti return (Arch_MTime(gn)); 9578874Srgrimes 958143979Sharti else if (gn->path == NULL) 959144020Sharti fullName = Path_FindFile(gn->name, &dirSearchPath); 960138441Sharti else 961138441Sharti fullName = gn->path; 9621590Srgrimes 963138441Sharti if (fullName == NULL) 964138441Sharti fullName = estrdup(gn->name); 965138441Sharti 966138441Sharti entry = Hash_FindEntry(&mtimes, fullName); 967138441Sharti if (entry != NULL) { 968138441Sharti /* 969138441Sharti * Only do this once -- the second time folks are checking to 970138441Sharti * see if the file was actually updated, so we need to 971138441Sharti * actually go to the filesystem. 972138441Sharti */ 973138441Sharti DEBUGF(DIR, ("Using cached time %s for %s\n", 974138441Sharti Targ_FmtTime((time_t)(long)Hash_GetValue(entry)), 975138441Sharti fullName)); 976138441Sharti stb.st_mtime = (time_t)(long)Hash_GetValue(entry); 977138441Sharti Hash_DeleteEntry(&mtimes, entry); 978138441Sharti } else if (stat(fullName, &stb) < 0) { 979138441Sharti if (gn->type & OP_MEMBER) { 980138441Sharti if (fullName != gn->path) 981138441Sharti free(fullName); 982138441Sharti return (Arch_MemMTime(gn)); 983138441Sharti } else { 984138441Sharti stb.st_mtime = 0; 985138441Sharti } 9861590Srgrimes } 987138441Sharti if (fullName && gn->path == (char *)NULL) 988138441Sharti gn->path = fullName; 9898874Srgrimes 990138441Sharti gn->mtime = stb.st_mtime; 991138441Sharti return (gn->mtime); 9921590Srgrimes} 9931590Srgrimes 9941590Srgrimes/*- 9951590Srgrimes *----------------------------------------------------------------------- 996144020Sharti * Path_AddDir -- 997143685Sharti * Add the given name to the end of the given path. 9981590Srgrimes * 9991590Srgrimes * Results: 10001590Srgrimes * none 10011590Srgrimes * 10021590Srgrimes * Side Effects: 10038874Srgrimes * A structure is added to the list and the directory is 10041590Srgrimes * read and hashed. 10051590Srgrimes *----------------------------------------------------------------------- 10061590Srgrimes */ 1007144020Shartistruct Dir * 1008144020ShartiPath_AddDir(struct Path *path, const char *name) 10091590Srgrimes{ 1010144020Sharti Dir *d; /* pointer to new Path structure */ 1011144020Sharti DIR *dir; /* for reading directory */ 1012144020Sharti struct PathElement *pe; 1013138441Sharti struct dirent *dp; /* entry in directory */ 10148874Srgrimes 1015144020Sharti /* check whether we know this directory */ 1016144020Sharti TAILQ_FOREACH(d, &openDirectories, link) { 1017144020Sharti if (strcmp(d->name, name) == 0) { 1018144020Sharti /* Found it. */ 1019144020Sharti if (path == NULL) 1020144020Sharti return (d); 1021144020Sharti 1022144020Sharti /* Check whether its already on the path. */ 1023144020Sharti TAILQ_FOREACH(pe, path, link) { 1024144020Sharti if (pe->dir == d) 1025144020Sharti return (d); 1026144020Sharti } 1027144020Sharti /* Add it to the path */ 1028144020Sharti d->refCount += 1; 1029144020Sharti pe = emalloc(sizeof(*pe)); 1030144020Sharti pe->dir = d; 1031144020Sharti TAILQ_INSERT_TAIL(path, pe, link); 1032144020Sharti return (d); 1033138441Sharti } 1034144020Sharti } 10358874Srgrimes 1036144020Sharti DEBUGF(DIR, ("Caching %s...", name)); 10378874Srgrimes 1038144020Sharti if ((dir = opendir(name)) == NULL) { 1039144020Sharti DEBUGF(DIR, (" cannot open\n")); 1040144020Sharti return (NULL); 1041144020Sharti } 1042144020Sharti 1043144020Sharti d = emalloc(sizeof(*d)); 1044144020Sharti d->name = estrdup(name); 1045144020Sharti d->hits = 0; 1046144020Sharti d->refCount = 1; 1047144020Sharti Hash_InitTable(&d->files, -1); 1048144020Sharti 1049144020Sharti while ((dp = readdir(dir)) != NULL) { 1050103503Sjmallett#if defined(sun) && defined(d_ino) /* d_ino is a sunos4 #define for d_fileno */ 1051144020Sharti /* 1052144020Sharti * The sun directory library doesn't check for 1053144020Sharti * a 0 inode (0-inode slots just take up space), 1054144020Sharti * so we have to do it ourselves. 1055144020Sharti */ 1056144020Sharti if (dp->d_fileno == 0) 1057144020Sharti continue; 105818730Ssteve#endif /* sun && d_ino */ 105970358Swill 1060144020Sharti /* Skip the '.' and '..' entries by checking 1061144020Sharti * for them specifically instead of assuming 1062144020Sharti * readdir() reuturns them in that order when 1063144020Sharti * first going through a directory. This is 1064144020Sharti * needed for XFS over NFS filesystems since 1065144020Sharti * SGI does not guarantee that these are the 1066144020Sharti * first two entries returned from readdir(). 1067144020Sharti */ 1068144020Sharti if (ISDOT(dp->d_name) || ISDOTDOT(dp->d_name)) 1069144020Sharti continue; 107070358Swill 1071144020Sharti Hash_CreateEntry(&d->files, dp->d_name, (Boolean *)NULL); 10721590Srgrimes } 1073144020Sharti closedir(dir); 1074144020Sharti 1075144020Sharti if (path != NULL) { 1076144020Sharti /* Add it to the path */ 1077144020Sharti d->refCount += 1; 1078144020Sharti pe = emalloc(sizeof(*pe)); 1079144020Sharti pe->dir = d; 1080144020Sharti TAILQ_INSERT_TAIL(path, pe, link); 1081144020Sharti } 1082144020Sharti 1083144020Sharti /* Add to list of all directories */ 1084144020Sharti TAILQ_INSERT_TAIL(&openDirectories, d, link); 1085144020Sharti 1086144020Sharti DEBUGF(DIR, ("done\n")); 1087144020Sharti 1088144020Sharti return (d); 10891590Srgrimes} 10901590Srgrimes 1091144020Sharti/** 1092144020Sharti * Path_Duplicate 1093144020Sharti * Duplicate a path. Ups the reference count for the directories. 10941590Srgrimes */ 1095144020Shartivoid 1096144020ShartiPath_Duplicate(struct Path *dst, const struct Path *src) 10971590Srgrimes{ 1098144020Sharti struct PathElement *ped, *pes; 10991590Srgrimes 1100144020Sharti TAILQ_FOREACH(pes, src, link) { 1101144020Sharti ped = emalloc(sizeof(*ped)); 1102144020Sharti ped->dir = pes->dir; 1103144020Sharti ped->dir->refCount++; 1104144020Sharti TAILQ_INSERT_TAIL(dst, ped, link); 1105144020Sharti } 11061590Srgrimes} 11071590Srgrimes 1108144020Sharti/** 1109144020Sharti * Path_MakeFlags 11101590Srgrimes * Make a string by taking all the directories in the given search 11111590Srgrimes * path and preceding them by the given flag. Used by the suffix 11121590Srgrimes * module to create variables for compilers based on suffix search 11131590Srgrimes * paths. 11141590Srgrimes * 11151590Srgrimes * Results: 11161590Srgrimes * The string mentioned above. Note that there is no space between 11171590Srgrimes * the given flag and each directory. The empty string is returned if 11181590Srgrimes * Things don't go well. 11191590Srgrimes */ 11201590Srgrimeschar * 1121144020ShartiPath_MakeFlags(const char *flag, const struct Path *path) 11221590Srgrimes{ 1123138441Sharti char *str; /* the string which will be returned */ 1124138441Sharti char *tstr; /* the current directory preceded by 'flag' */ 1125138547Sharti char *nstr; 1126144020Sharti const struct PathElement *pe; 11278874Srgrimes 1128138441Sharti str = estrdup(""); 11298874Srgrimes 1130144020Sharti TAILQ_FOREACH(pe, path, link) { 1131144020Sharti tstr = str_concat(flag, pe->dir->name, 0); 1132138564Sharti nstr = str_concat(str, tstr, STR_ADDSPACE); 1133138564Sharti free(str); 1134138564Sharti free(tstr); 1135138564Sharti str = nstr; 11361590Srgrimes } 11378874Srgrimes 1138138441Sharti return (str); 11391590Srgrimes} 11401590Srgrimes 1141144020Sharti/** 1142144020Sharti * Path_Clear 11431590Srgrimes * 1144144020Sharti * Destroy a path. This decrements the reference counts of all 1145144020Sharti * directories of this path and, if a reference count goes 0, 1146144020Sharti * destroys the directory object. 11471590Srgrimes */ 11481590Srgrimesvoid 1149144020ShartiPath_Clear(struct Path *path) 11501590Srgrimes{ 1151144020Sharti struct PathElement *pe; 1152138264Sharti 1153144020Sharti while ((pe = TAILQ_FIRST(path)) != NULL) { 1154144020Sharti pe->dir->refCount--; 1155144020Sharti TAILQ_REMOVE(path, pe, link); 1156144020Sharti if (pe->dir->refCount == 0) { 1157144020Sharti TAILQ_REMOVE(&openDirectories, pe->dir, link); 1158144020Sharti Hash_DeleteTable(&pe->dir->files); 1159144020Sharti free(pe->dir->name); 1160144020Sharti free(pe->dir); 1161144020Sharti } 1162144020Sharti free(pe); 1163138441Sharti } 11641590Srgrimes} 11651590Srgrimes 1166144020Sharti/** 1167144020Sharti * Path_Concat 11681590Srgrimes * 11691590Srgrimes * Concatenate two paths, adding the second to the end of the first. 1170144020Sharti * Make sure to avoid duplicates. 11711590Srgrimes * 11721590Srgrimes * Side Effects: 11731590Srgrimes * Reference counts for added dirs are upped. 11741590Srgrimes */ 11751590Srgrimesvoid 1176144020ShartiPath_Concat(struct Path *path1, const struct Path *path2) 11771590Srgrimes{ 1178144020Sharti struct PathElement *p1, *p2; 11791590Srgrimes 1180144020Sharti TAILQ_FOREACH(p2, path2, link) { 1181144020Sharti TAILQ_FOREACH(p1, path1, link) { 1182144020Sharti if (p1->dir == p2->dir) 1183144020Sharti break; 1184138441Sharti } 1185144020Sharti if (p1 == NULL) { 1186144020Sharti p1 = emalloc(sizeof(*p1)); 1187144020Sharti p1->dir = p2->dir; 1188144020Sharti p1->dir->refCount++; 1189144020Sharti TAILQ_INSERT_TAIL(path1, p1, link); 1190144020Sharti } 11911590Srgrimes } 11921590Srgrimes} 11931590Srgrimes 11941590Srgrimes/********** DEBUG INFO **********/ 11951590Srgrimesvoid 1196104696SjmallettDir_PrintDirectories(void) 11971590Srgrimes{ 1198144020Sharti const Dir *d; 11998874Srgrimes 1200138441Sharti printf("#*** Directory Cache:\n"); 1201138441Sharti printf("# Stats: %d hits %d misses %d near misses %d losers (%d%%)\n", 1202138441Sharti hits, misses, nearmisses, bigmisses, 1203138441Sharti (hits + bigmisses + nearmisses ? 1204138441Sharti hits * 100 / (hits + bigmisses + nearmisses) : 0)); 1205138441Sharti printf("# %-20s referenced\thits\n", "directory"); 1206144020Sharti TAILQ_FOREACH(d, &openDirectories, link) 1207144020Sharti printf("# %-20s %10d\t%4d\n", d->name, d->refCount, d->hits); 12081590Srgrimes} 12091590Srgrimes 12101590Srgrimesvoid 1211144020ShartiPath_Print(const struct Path *path) 12121590Srgrimes{ 1213144020Sharti const struct PathElement *p; 1214138232Sharti 1215144020Sharti TAILQ_FOREACH(p, path, link) 1216144020Sharti printf("%s ", p->dir->name); 12171590Srgrimes} 1218