/* $NetBSD: dir.c,v 1.297 2025/06/12 18:51:05 rillig Exp $ */12/*3* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.4* All rights reserved.5*6* This code is derived from software contributed to Berkeley by7* Adam de Boor.8*9* Redistribution and use in source and binary forms, with or without10* modification, are permitted provided that the following conditions11* are met:12* 1. Redistributions of source code must retain the above copyright13* notice, this list of conditions and the following disclaimer.14* 2. Redistributions in binary form must reproduce the above copyright15* notice, this list of conditions and the following disclaimer in the16* documentation and/or other materials provided with the distribution.17* 3. Neither the name of the University nor the names of its contributors18* may be used to endorse or promote products derived from this software19* without specific prior written permission.20*21* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND22* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE23* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE24* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE25* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL26* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS27* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)28* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT29* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY30* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF31* SUCH DAMAGE.32*/3334/*35* Copyright (c) 1988, 1989 by Adam de Boor36* Copyright (c) 1989 by Berkeley Softworks37* All rights reserved.38*39* This code is derived from software contributed to Berkeley by40* Adam de Boor.41*42* Redistribution and use in source and binary forms, with or without43* modification, are permitted provided that the following conditions44* are met:45* 1. Redistributions of source code must retain the above copyright46* notice, this list of conditions and the following disclaimer.47* 2. Redistributions in binary form must reproduce the above copyright48* notice, this list of conditions and the following disclaimer in the49* documentation and/or other materials provided with the distribution.50* 3. All advertising materials mentioning features or use of this software51* must display the following acknowledgement:52* This product includes software developed by the University of53* California, Berkeley and its contributors.54* 4. Neither the name of the University nor the names of its contributors55* may be used to endorse or promote products derived from this software56* without specific prior written permission.57*58* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND59* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE60* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE61* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE62* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL63* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS64* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)65* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT66* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY67* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF68* SUCH DAMAGE.69*/7071/*72* Directory searching using wildcards and/or normal names.73* Used both for source wildcarding in the makefile and for finding74* implicit sources.75*76* The interface for this module is:77* Dir_Init Initialize the module.78*79* Dir_InitCur Set the cur CachedDir.80*81* Dir_InitDot Set the dot CachedDir.82*83* Dir_End Clean up the module.84*85* Dir_SetPATH Set ${.PATH} to reflect the state of dirSearchPath.86*87* Dir_HasWildcards88* Returns true if the name given it needs to89* be wildcard-expanded.90*91* SearchPath_Expand92* Expand a filename pattern to find all matching files93* from the search path.94*95* Dir_FindFile Searches for a file on a given search path.96* If it exists, returns the entire path, otherwise NULL.97*98* Dir_FindHereOrAbove99* Search for a path in the current directory and then100* all the directories above it in turn, until the path101* is found or the root directory ("/") is reached.102*103* Dir_UpdateMTime104* Update the modification time and path of a node with105* data from the file corresponding to the node.106*107* SearchPath_Add Add a directory to a search path.108*109* SearchPath_ToFlags110* Given a search path and a command flag, create111* a string with each of the directories in the path112* preceded by the command flag and all of them113* separated by a space.114*115* SearchPath_Clear116* Resets a search path to the empty list.117*118* For debugging:119* Dir_PrintDirectories120* Print stats about the directory cache.121*/122123#include <sys/types.h>124#include <sys/stat.h>125126#include <dirent.h>127#include <errno.h>128129#include "make.h"130#include "dir.h"131#include "job.h"132133/* "@(#)dir.c 8.2 (Berkeley) 1/2/94" */134MAKE_RCSID("$NetBSD: dir.c,v 1.297 2025/06/12 18:51:05 rillig Exp $");135136/*137* A search path is a list of CachedDir structures. A CachedDir has in it the138* name of the directory and the names of all the files in the directory.139* This is used to cut down on the number of system calls necessary to find140* implicit dependents and their like. Since these searches are made before141* any actions are taken, we need not worry about the directory changing due142* to creation commands. If this hampers the style of some makefiles, they143* must be changed.144*145* All previously-read directories are kept in openDirs, which is checked146* first before a directory is opened.147*148* This cache is used by the multi-level transformation code in suff.c, which149* tends to search for far more files than in regular explicit targets. After150* a directory has been cached, any later changes to that directory are not151* reflected in the cache. To keep the cache up to date, there are several152* ideas:153*154* 1) just use stat to test for a file's existence. As mentioned above,155* this is very inefficient due to the number of checks performed by156* the multi-level transformation code.157*158* 2) use readdir() to search the directories, keeping them open between159* checks. Around 1993 or earlier, this didn't slow down the process too160* much, but it consumed one file descriptor per open directory, which161* was critical on the then-current operating systems, as many limited162* the number of open file descriptors to 20 or 32.163*164* 3) record the mtime of the directory in the CachedDir structure and165* verify the directory hasn't changed since the contents were cached.166* This will catch the creation or deletion of files, but not the167* updating of files. However, since it is the creation and deletion168* that is the problem, this could be a good thing to do. Unfortunately,169* if the directory (say ".") were fairly large and changed fairly170* frequently, the constant reloading could seriously degrade171* performance. It might be good in such cases to keep track of the172* number of reloadings and if the number goes over a (small) limit,173* resort to using stat in its place.174*175* An additional thing to consider is that make is used primarily to create176* C programs and until recently (as of 1993 or earlier), pcc-based compilers177* didn't have an option to specify where the resulting object file should be178* placed. This forced all objects to be created in the current directory.179* This isn't meant as a full excuse, just an explanation of some of the180* reasons for the caching used here.181*182* One more note: the location of a target's file is only performed on the183* downward traversal of the graph and then only for terminal nodes in the184* graph. This could be construed as wrong in some cases, but prevents185* inadvertent modification of files when the "installed" directory for a186* file is provided in the search path.187*188* Another data structure maintained by this module is an mtime cache used189* when the searching of cached directories fails to find a file. In the past,190* Dir_FindFile would simply perform an access() call in such a case to191* determine if the file could be found using just the name given. When this192* hit, however, all that was gained was the knowledge that the file existed.193* Given that an access() is essentially a stat() without the copyout() call,194* and that the same filesystem overhead would have to be incurred in195* Dir_MTime, it made sense to replace the access() with a stat() and record196* the mtime in a cache for when Dir_UpdateMTime was actually called.197*/198199200/* A cache for the filenames in a directory. */201struct CachedDir {202/*203* Name of the directory, either absolute or relative to the current204* directory. The name is not normalized in any way, that is, "."205* and "./." are different.206*207* Not sure what happens when .CURDIR is assigned a new value; see208* Parse_Var.209*/210char *name;211212/*213* The number of SearchPaths that refer to this directory.214* Plus the number of global variables that refer to this directory.215* References from openDirs do not count though.216*/217int refCount;218219/* The number of times a file in this directory has been found. */220int hits;221222/* The names of the directory entries. */223HashSet files;224};225226typedef List CachedDirList;227typedef ListNode CachedDirListNode;228229/* A list of cached directories, with fast lookup by directory name. */230typedef struct OpenDirs {231CachedDirList list;232HashTable /* of CachedDirListNode */ table;233} OpenDirs;234235236SearchPath dirSearchPath = { LST_INIT }; /* main search path */237238static OpenDirs openDirs; /* all cached directories */239240/*241* Variables for gathering statistics on the efficiency of the caching242* mechanism.243*/244static int hits; /* Found in directory cache */245static int misses; /* Sad, but not evil misses */246static int nearmisses; /* Found under search path */247static int bigmisses; /* Sought by itself */248249/* The cached contents of ".", the relative current directory. */250static CachedDir *dot = NULL;251/* The cached contents of the absolute current directory. */252static CachedDir *cur = NULL;253/* A fake path entry indicating we need to look for '.' last. */254static CachedDir *dotLast = NULL;255256/*257* Results of doing a last-resort stat in Dir_FindFile -- if we have to go to258* the system to find the file, we might as well have its mtime on record.259*260* XXX: If this is done way early, there's a chance other rules will have261* already updated the file, in which case we'll update it again. Generally,262* there won't be two rules to update a single file, so this should be ok.263*/264static HashTable mtimes;265266static HashTable lmtimes; /* same as mtimes but for lstat */267268269static void OpenDirs_Remove(OpenDirs *, const char *);270271272static CachedDir *273CachedDir_New(const char *name)274{275CachedDir *dir = bmake_malloc(sizeof *dir);276277dir->name = bmake_strdup(name);278dir->refCount = 0;279dir->hits = 0;280HashSet_Init(&dir->files);281282#ifdef DEBUG_REFCNT283DEBUG2(DIR, "CachedDir %p new for \"%s\"\n", dir, dir->name);284#endif285286return dir;287}288289static CachedDir *290CachedDir_Ref(CachedDir *dir)291{292dir->refCount++;293294#ifdef DEBUG_REFCNT295DEBUG3(DIR, "CachedDir %p ++ %d for \"%s\"\n",296dir, dir->refCount, dir->name);297#endif298299return dir;300}301302static void303CachedDir_Unref(CachedDir *dir)304{305dir->refCount--;306307#ifdef DEBUG_REFCNT308DEBUG3(DIR, "CachedDir %p -- %d for \"%s\"\n",309dir, dir->refCount, dir->name);310#endif311312if (dir->refCount > 0)313return;314315#ifdef DEBUG_REFCNT316DEBUG2(DIR, "CachedDir %p free for \"%s\"\n", dir, dir->name);317#endif318319OpenDirs_Remove(&openDirs, dir->name);320321free(dir->name);322HashSet_Done(&dir->files);323free(dir);324}325326/* Update the value of 'var', updating the reference counts. */327static void328CachedDir_Assign(CachedDir **var, CachedDir *dir)329{330CachedDir *prev;331332prev = *var;333*var = dir;334if (dir != NULL)335CachedDir_Ref(dir);336if (prev != NULL)337CachedDir_Unref(prev);338}339340static void341OpenDirs_Init(OpenDirs *odirs)342{343Lst_Init(&odirs->list);344HashTable_Init(&odirs->table);345}346347#ifdef CLEANUP348static void349OpenDirs_Done(OpenDirs *odirs)350{351CachedDirListNode *ln = odirs->list.first;352DEBUG1(DIR, "OpenDirs_Done: %u entries to remove\n",353odirs->table.numEntries);354while (ln != NULL) {355CachedDirListNode *next = ln->next;356CachedDir *dir = ln->datum;357DEBUG2(DIR, "OpenDirs_Done: refCount %d for \"%s\"\n",358dir->refCount, dir->name);359CachedDir_Unref(dir); /* removes the dir from odirs->list */360ln = next;361}362Lst_Done(&odirs->list);363HashTable_Done(&odirs->table);364}365#endif366367static CachedDir *368OpenDirs_Find(OpenDirs *odirs, const char *name)369{370CachedDirListNode *ln = HashTable_FindValue(&odirs->table, name);371return ln != NULL ? ln->datum : NULL;372}373374static void375OpenDirs_Add(OpenDirs *odirs, CachedDir *cdir)376{377if (HashTable_FindEntry(&odirs->table, cdir->name) != NULL)378return;379Lst_Append(&odirs->list, cdir);380HashTable_Set(&odirs->table, cdir->name, odirs->list.last);381}382383static void384OpenDirs_Remove(OpenDirs *odirs, const char *name)385{386HashEntry *he = HashTable_FindEntry(&odirs->table, name);387CachedDirListNode *ln;388if (he == NULL)389return;390ln = HashEntry_Get(he);391HashTable_DeleteEntry(&odirs->table, he);392Lst_Remove(&odirs->list, ln);393}394395/*396* Returns 0 and the result of stat(2) or lstat(2) in *out_cst,397* or -1 on error.398*/399static int400cached_stats(const char *pathname, struct cached_stat *out_cst,401bool useLstat, bool forceRefresh)402{403HashTable *tbl = useLstat ? &lmtimes : &mtimes;404struct stat sys_st;405struct cached_stat *cst;406int rc;407408if (pathname == NULL || pathname[0] == '\0')409return -1; /* This can happen in meta mode. */410411cst = HashTable_FindValue(tbl, pathname);412if (cst != NULL && !forceRefresh) {413*out_cst = *cst;414DEBUG2(DIR, "Using cached time %s for %s\n",415Targ_FmtTime(cst->cst_mtime), pathname);416return 0;417}418419rc = (useLstat ? lstat : stat)(pathname, &sys_st);420if (rc == -1)421return -1; /* don't cache negative lookups */422423if (sys_st.st_mtime == 0)424sys_st.st_mtime = 1; /* avoid confusion with missing file */425426if (cst == NULL) {427cst = bmake_malloc(sizeof *cst);428HashTable_Set(tbl, pathname, cst);429}430431cst->cst_mtime = sys_st.st_mtime;432cst->cst_mode = sys_st.st_mode;433434*out_cst = *cst;435DEBUG2(DIR, " Caching %s for %s\n",436Targ_FmtTime(sys_st.st_mtime), pathname);437438return 0;439}440441int442cached_stat(const char *pathname, struct cached_stat *cst)443{444return cached_stats(pathname, cst, false, false);445}446447int448cached_lstat(const char *pathname, struct cached_stat *cst)449{450return cached_stats(pathname, cst, true, false);451}452453/* Initialize the directories module. */454void455Dir_Init(void)456{457OpenDirs_Init(&openDirs);458HashTable_Init(&mtimes);459HashTable_Init(&lmtimes);460CachedDir_Assign(&dotLast, CachedDir_New(".DOTLAST"));461}462463/* Called by Dir_InitDir and whenever .CURDIR is assigned to. */464void465Dir_InitCur(const char *newCurdir)466{467CachedDir *dir;468469if (newCurdir == NULL)470return;471472/*473* The build directory is not the same as the source directory.474* Keep this one around too.475*/476dir = SearchPath_Add(NULL, newCurdir);477if (dir == NULL)478return;479480CachedDir_Assign(&cur, dir);481}482483/*484* (Re)initialize "dot" (the current/object directory).485* Some directories may be cached.486*/487void488Dir_InitDot(void)489{490CachedDir *dir;491492dir = SearchPath_Add(NULL, ".");493if (dir == NULL) {494Error("Cannot open \".\": %s", strerror(errno));495exit(2); /* Not 1 so -q can distinguish error */496}497498CachedDir_Assign(&dot, dir);499500Dir_SetPATH(); /* initialize */501}502503#ifdef CLEANUP504static void505FreeCachedTable(HashTable *tbl)506{507HashIter hi;508HashIter_Init(&hi, tbl);509while (HashIter_Next(&hi))510free(hi.entry->value);511HashTable_Done(tbl);512}513514/* Clean up the directories module. */515void516Dir_End(void)517{518CachedDir_Assign(&cur, NULL);519CachedDir_Assign(&dot, NULL);520CachedDir_Assign(&dotLast, NULL);521SearchPath_Clear(&dirSearchPath);522OpenDirs_Done(&openDirs);523FreeCachedTable(&mtimes);524FreeCachedTable(&lmtimes);525}526#endif527528/*529* We want ${.PATH} to indicate the order in which we will actually530* search, so we rebuild it after any .PATH: target.531* This is the simplest way to deal with the effect of .DOTLAST.532*/533void534Dir_SetPATH(void)535{536CachedDirListNode *ln;537bool seenDotLast = false; /* true if we should search '.' last */538539Global_Delete(".PATH");540541if ((ln = dirSearchPath.dirs.first) != NULL) {542CachedDir *dir = ln->datum;543if (dir == dotLast) {544seenDotLast = true;545Global_Append(".PATH", dotLast->name);546}547}548549if (!seenDotLast) {550if (dot != NULL)551Global_Append(".PATH", dot->name);552if (cur != NULL)553Global_Append(".PATH", cur->name);554}555556for (ln = dirSearchPath.dirs.first; ln != NULL; ln = ln->next) {557CachedDir *dir = ln->datum;558if (dir == dotLast)559continue;560if (dir == dot && seenDotLast)561continue;562Global_Append(".PATH", dir->name);563}564565if (seenDotLast) {566if (dot != NULL)567Global_Append(".PATH", dot->name);568if (cur != NULL)569Global_Append(".PATH", cur->name);570}571}572573574void575Dir_SetSYSPATH(void)576{577CachedDirListNode *ln;578SearchPath *path = Lst_IsEmpty(&sysIncPath->dirs)579? defSysIncPath : sysIncPath;580581Var_ReadOnly(".SYSPATH", false);582Global_Delete(".SYSPATH");583for (ln = path->dirs.first; ln != NULL; ln = ln->next) {584CachedDir *dir = ln->datum;585Global_Append(".SYSPATH", dir->name);586}587Var_ReadOnly(".SYSPATH", true);588}589590/*591* See if the given name has any wildcard characters in it and all braces and592* brackets are properly balanced.593*594* XXX: This code is not 100% correct ([^]] fails etc.). I really don't think595* that make(1) should be expanding patterns, because then you have to set a596* mechanism for escaping the expansion!597*/598bool599Dir_HasWildcards(const char *name)600{601const char *p;602bool wild = false;603int braces = 0, brackets = 0;604605for (p = name; *p != '\0'; p++) {606switch (*p) {607case '{':608braces++;609wild = true;610break;611case '}':612braces--;613break;614case '[':615brackets++;616wild = true;617break;618case ']':619brackets--;620break;621case '?':622case '*':623wild = true;624break;625default:626break;627}628}629return wild && brackets == 0 && braces == 0;630}631632/*633* See if any files as seen from 'dir' match 'pattern', and add their names634* to 'expansions' if they do.635*636* Wildcards are only expanded in the final path component, but not in637* directories like src/lib*c/file*.c. To expand these wildcards,638* delegate the work to the shell, using the '!=' variable assignment639* operator, the ':sh' variable modifier or the ':!...!' variable modifier,640* such as in ${:!echo src/lib*c/file*.c!}.641*/642static void643DirMatchFiles(const char *pattern, CachedDir *dir, StringList *expansions)644{645const char *dirName = dir->name;646bool isDot = dirName[0] == '.' && dirName[1] == '\0';647HashIter hi;648649/*650* XXX: Iterating over all hash entries is inefficient. If the651* pattern is a plain string without any wildcards, a direct lookup652* is faster.653*/654655HashIter_InitSet(&hi, &dir->files);656while (HashIter_Next(&hi)) {657const char *base = hi.entry->key;658StrMatchResult res = Str_Match(base, pattern);659/* TODO: handle errors from res.error */660661if (!res.matched)662continue;663664/*665* Follow the UNIX convention that dot files are only found666* if the pattern begins with a dot. The pattern '.*' does667* not match '.' or '..' since these are not included in the668* directory cache.669*670* This means that the pattern '[a-z.]*' does not find671* '.file', which is consistent with NetBSD sh, NetBSD ksh,672* bash, dash, csh and probably many other shells as well.673*/674if (base[0] == '.' && pattern[0] != '.')675continue;676677{678char *fullName = isDot679? bmake_strdup(base)680: str_concat3(dirName, "/", base);681Lst_Append(expansions, fullName);682}683}684}685686/* Find the next closing brace in 'p', taking nested braces into account. */687static const char *688closing_brace(const char *p)689{690int depth = 0;691while (*p != '\0') {692if (*p == '}' && depth == 0)693break;694if (*p == '{')695depth++;696if (*p == '}')697depth--;698p++;699}700return p;701}702703/*704* Find the next closing brace or comma in the string, taking nested braces705* into account.706*/707static const char *708separator_comma(const char *p)709{710int depth = 0;711while (*p != '\0') {712if ((*p == '}' || *p == ',') && depth == 0)713break;714if (*p == '{')715depth++;716if (*p == '}')717depth--;718p++;719}720return p;721}722723static bool724contains_wildcard(const char *p)725{726for (; *p != '\0'; p++) {727switch (*p) {728case '*':729case '?':730case '{':731case '[':732return true;733}734}735return false;736}737738static char *739concat3(const char *a, size_t a_len, const char *b, size_t b_len,740const char *c, size_t c_len)741{742size_t s_len = a_len + b_len + c_len;743char *s = bmake_malloc(s_len + 1);744memcpy(s, a, a_len);745memcpy(s + a_len, b, b_len);746memcpy(s + a_len + b_len, c, c_len);747s[s_len] = '\0';748return s;749}750751/*752* Expand curly braces like the C shell. Brace expansion by itself is purely753* textual, the expansions are not looked up in the file system. But if an754* expanded word contains wildcard characters, it is expanded further,755* matching only the actually existing files.756*757* Example: "{a{b,c}}" expands to "ab" and "ac".758* Example: "{a}" expands to "a".759* Example: "{a,*.c}" expands to "a" and all "*.c" files that exist.760*761* Input:762* word Entire word to expand763* brace First curly brace in it764* path Search path to use765* expansions Place to store the expansions766*/767static void768DirExpandCurly(const char *word, const char *brace, SearchPath *path,769StringList *expansions)770{771const char *prefix, *middle, *piece, *middle_end, *suffix;772size_t prefix_len, suffix_len;773774/* Split the word into prefix, '{', middle, '}' and suffix. */775776middle = brace + 1;777middle_end = closing_brace(middle);778if (*middle_end == '\0') {779Error("Unterminated {} clause \"%s\"", middle);780return;781}782783prefix = word;784prefix_len = (size_t)(brace - prefix);785suffix = middle_end + 1;786suffix_len = strlen(suffix);787788/* Split the middle into pieces, separated by commas. */789790piece = middle;791while (piece < middle_end + 1) {792const char *piece_end = separator_comma(piece);793size_t piece_len = (size_t)(piece_end - piece);794795char *file = concat3(prefix, prefix_len, piece, piece_len,796suffix, suffix_len);797798if (contains_wildcard(file)) {799SearchPath_Expand(path, file, expansions);800free(file);801} else {802Lst_Append(expansions, file);803}804805/* skip over the comma or closing brace */806piece = piece_end + 1;807}808}809810811/* Expand 'pattern' in each of the directories from 'path'. */812static void813DirExpandPath(const char *pattern, SearchPath *path, StringList *expansions)814{815CachedDirListNode *ln;816for (ln = path->dirs.first; ln != NULL; ln = ln->next) {817CachedDir *dir = ln->datum;818DirMatchFiles(pattern, dir, expansions);819}820}821822static void823PrintExpansions(StringList *expansions)824{825const char *sep = "";826StringListNode *ln;827for (ln = expansions->first; ln != NULL; ln = ln->next) {828const char *word = ln->datum;829debug_printf("%s%s", sep, word);830sep = " ";831}832debug_printf("\n");833}834835/*836* The wildcard isn't in the first component.837* Find all the components up to the one with the wildcard.838*/839static void840SearchPath_ExpandMiddle(SearchPath *path, const char *pattern,841const char *wildcardComponent, StringList *expansions)842{843char *prefix, *dirpath, *end;844SearchPath *partPath;845846prefix = bmake_strsedup(pattern, wildcardComponent + 1);847/*848* XXX: Only the first match of the prefix in the path is849* taken, any others are ignored. The expectation may be850* that the pattern is expanded in the whole path.851*/852dirpath = Dir_FindFile(prefix, path);853free(prefix);854855/*856* dirpath is null if can't find the leading component857*858* XXX: Dir_FindFile won't find internal components. i.e. if the859* path contains ../Etc/Object and we're looking for Etc, it won't860* be found. Ah well. Probably not important.861*862* TODO: Check whether the above comment is still true.863*/864if (dirpath == NULL)865return;866867end = &dirpath[strlen(dirpath) - 1];868/* XXX: What about multiple trailing slashes? */869if (*end == '/')870*end = '\0';871872partPath = SearchPath_New();873(void)SearchPath_Add(partPath, dirpath);874DirExpandPath(wildcardComponent + 1, partPath, expansions);875SearchPath_Free(partPath);876free(dirpath);877}878879/*880* Expand the given pattern into a list of existing filenames by globbing it,881* looking in each directory from the search path.882*883* Input:884* path the directories in which to find the files885* pattern the pattern to expand886* expansions the list on which to place the results887*/888void889SearchPath_Expand(SearchPath *path, const char *pattern, StringList *expansions)890{891const char *brace, *slash, *wildcard, *wildcardComponent;892893assert(path != NULL);894assert(expansions != NULL);895896DEBUG1(DIR, "Expanding \"%s\"... ", pattern);897898brace = strchr(pattern, '{');899if (brace != NULL) {900DirExpandCurly(pattern, brace, path, expansions);901goto done;902}903904slash = strchr(pattern, '/');905if (slash == NULL) {906DirMatchFiles(pattern, dot, expansions);907DirExpandPath(pattern, path, expansions);908goto done;909}910911/* At this point, the pattern has a directory component. */912913/* Find the first wildcard in the pattern. */914for (wildcard = pattern; *wildcard != '\0'; wildcard++)915if (*wildcard == '?' || *wildcard == '[' || *wildcard == '*')916break;917918if (*wildcard == '\0') {919/*920* No directory component and no wildcard at all -- this921* should never happen as in such a simple case there is no922* need to expand anything.923*/924DirExpandPath(pattern, path, expansions);925goto done;926}927928/* Back up to the start of the component containing the wildcard. */929/* XXX: This handles '///' and '/' differently. */930wildcardComponent = wildcard;931while (wildcardComponent > pattern && *wildcardComponent != '/')932wildcardComponent--;933934if (wildcardComponent == pattern) {935/* The first component contains the wildcard. */936/* Start the search from the local directory */937DirExpandPath(pattern, path, expansions);938} else {939SearchPath_ExpandMiddle(path, pattern, wildcardComponent,940expansions);941}942943done:944if (DEBUG(DIR))945PrintExpansions(expansions);946}947948/*949* Find if 'base' exists in 'dir'.950* Return the freshly allocated path to the file, or NULL.951*/952static char *953DirLookup(CachedDir *dir, const char *base)954{955char *file;956957DEBUG1(DIR, " %s ...\n", dir->name);958959if (!HashSet_Contains(&dir->files, base))960return NULL;961962file = str_concat3(dir->name, "/", base);963DEBUG1(DIR, " returning %s\n", file);964dir->hits++;965hits++;966return file;967}968969970/*971* Find if 'name' exists in 'dir'.972* Return the freshly allocated path to the file, or NULL.973*/974static char *975DirLookupSubdir(CachedDir *dir, const char *name)976{977struct cached_stat cst;978char *file = dir == dot979? bmake_strdup(name)980: str_concat3(dir->name, "/", name);981982DEBUG1(DIR, "checking %s ...\n", file);983984if (cached_stat(file, &cst) == 0) {985nearmisses++;986return file;987}988free(file);989return NULL;990}991992/*993* Find if 'name' (which has basename 'base') exists in 'dir'.994* Return the freshly allocated path to the file, an empty string, or NULL.995* Returning an empty string means that the search should be terminated.996*/997static char *998DirLookupAbs(CachedDir *dir, const char *name, const char *base)999{1000const char *dnp; /* pointer into dir->name */1001const char *np; /* pointer into name */10021003DEBUG1(DIR, " %s ...\n", dir->name);10041005/*1006* If the file has a leading path component and that component1007* exactly matches the entire name of the current search1008* directory, we can attempt another cache lookup. And if we don't1009* have a hit, we can safely assume the file does not exist at all.1010*/1011for (dnp = dir->name, np = name;1012*dnp != '\0' && *dnp == *np; dnp++, np++)1013continue;1014if (*dnp != '\0' || np != base - 1)1015return NULL;10161017if (!HashSet_Contains(&dir->files, base)) {1018DEBUG0(DIR, " must be here but isn't -- returning\n");1019return bmake_strdup(""); /* to terminate the search */1020}10211022dir->hits++;1023hits++;1024DEBUG1(DIR, " returning %s\n", name);1025return bmake_strdup(name);1026}10271028/*1029* Find the given file in "." or curdir.1030* Return the freshly allocated path to the file, or NULL.1031*/1032static char *1033DirFindDot(const char *name, const char *base)1034{10351036if (HashSet_Contains(&dot->files, base)) {1037DEBUG0(DIR, " in '.'\n");1038hits++;1039dot->hits++;1040return bmake_strdup(name);1041}10421043if (cur != NULL && HashSet_Contains(&cur->files, base)) {1044DEBUG1(DIR, " in ${.CURDIR} = %s\n", cur->name);1045hits++;1046cur->hits++;1047return str_concat3(cur->name, "/", base);1048}10491050return NULL;1051}10521053static bool1054FindFileRelative(SearchPath *path, bool seenDotLast,1055const char *name, char **out_file)1056{1057CachedDirListNode *ln;1058bool checkedDot = false;1059char *file;10601061DEBUG0(DIR, " Trying subdirectories...\n");10621063if (!seenDotLast) {1064if (dot != NULL) {1065checkedDot = true;1066if ((file = DirLookupSubdir(dot, name)) != NULL)1067goto done;1068}1069if (cur != NULL &&1070(file = DirLookupSubdir(cur, name)) != NULL)1071goto done;1072}10731074for (ln = path->dirs.first; ln != NULL; ln = ln->next) {1075CachedDir *dir = ln->datum;1076if (dir == dotLast)1077continue;1078if (dir == dot) {1079if (checkedDot)1080continue;1081checkedDot = true;1082}1083if ((file = DirLookupSubdir(dir, name)) != NULL)1084goto done;1085}10861087if (seenDotLast) {1088if (dot != NULL && !checkedDot) {1089checkedDot = true;1090if ((file = DirLookupSubdir(dot, name)) != NULL)1091goto done;1092}1093if (cur != NULL &&1094(file = DirLookupSubdir(cur, name)) != NULL)1095goto done;1096}10971098if (checkedDot) {1099/*1100* Already checked by the given name, since . was in1101* the path, so no point in proceeding.1102*/1103DEBUG0(DIR, " Checked . already, returning NULL\n");1104file = NULL;1105goto done;1106}11071108return false;11091110done:1111*out_file = file;1112return true;1113}11141115static bool1116FindFileAbsolute(SearchPath *path, bool seenDotLast,1117const char *name, const char *base, char **out_file)1118{1119char *file;1120CachedDirListNode *ln;11211122DEBUG0(DIR, " Trying exact path matches...\n");11231124if (!seenDotLast && cur != NULL &&1125((file = DirLookupAbs(cur, name, base)) != NULL))1126goto found;11271128for (ln = path->dirs.first; ln != NULL; ln = ln->next) {1129CachedDir *dir = ln->datum;1130if (dir == dotLast)1131continue;1132if ((file = DirLookupAbs(dir, name, base)) != NULL)1133goto found;1134}11351136if (seenDotLast && cur != NULL &&1137((file = DirLookupAbs(cur, name, base)) != NULL))1138goto found;11391140return false;11411142found:1143if (file[0] == '\0') {1144free(file);1145file = NULL;1146}1147*out_file = file;1148return true;1149}11501151/*1152* Find the file with the given name along the given search path.1153*1154* Input:1155* name the file to find1156* path the directories to search, or NULL1157* isinclude if true, do not search .CURDIR at all1158*1159* Results:1160* The freshly allocated path to the file, or NULL.1161*/1162static char *1163FindFile(const char *name, SearchPath *path, bool isinclude)1164{1165char *file; /* the current filename to check */1166bool seenDotLast = isinclude; /* true if we should search dot last */1167struct cached_stat cst;1168const char *trailing_dot = ".";1169const char *base = str_basename(name);11701171DEBUG1(DIR, "Searching for %s ...", name);11721173if (path == NULL) {1174DEBUG0(DIR, "couldn't open path, file not found\n");1175misses++;1176return NULL;1177}11781179if (!seenDotLast && path->dirs.first != NULL) {1180CachedDir *dir = path->dirs.first->datum;1181if (dir == dotLast) {1182seenDotLast = true;1183DEBUG0(DIR, "[dot last]...");1184}1185}1186DEBUG0(DIR, "\n");11871188/*1189* If there's no leading directory components or if the leading1190* directory component is exactly `./', consult the cached contents1191* of each of the directories on the search path.1192*/1193if (base == name || (base - name == 2 && *name == '.')) {1194CachedDirListNode *ln;11951196/*1197* Look through all the directories on the path seeking one1198* which contains the final component of the given name. If1199* such a file is found, return its pathname.1200* If there is no such file, go on to phase two.1201*1202* No matter what, always look for the file in the current1203* directory before anywhere else (unless the path contains1204* the magic '.DOTLAST', in which case search it last).1205* This is so there are no conflicts between what the user1206* specifies (fish.c) and what make finds (./fish.c).1207*/1208if (!seenDotLast && (file = DirFindDot(name, base)) != NULL)1209return file;12101211for (ln = path->dirs.first; ln != NULL; ln = ln->next) {1212CachedDir *dir = ln->datum;1213if (dir == dotLast)1214continue;1215if ((file = DirLookup(dir, base)) != NULL)1216return file;1217}12181219if (seenDotLast && (file = DirFindDot(name, base)) != NULL)1220return file;1221}12221223if (base == name) {1224DEBUG0(DIR, " failed.\n");1225misses++;1226return NULL;1227}12281229if (*base == '\0')1230base = trailing_dot; /* we were given a trailing "/" */12311232if (name[0] != '/') {1233if (FindFileRelative(path, seenDotLast, name, &file))1234return file;1235} else {1236if (FindFileAbsolute(path, seenDotLast, name, base, &file))1237return file;1238}12391240/*1241* We cannot add the directory onto the search path because1242* of this amusing case:1243* $(INSTALLDIR)/$(FILE): $(FILE)1244*1245* $(FILE) exists in $(INSTALLDIR) but not in the current one.1246* When searching for $(FILE), we will find it in $(INSTALLDIR)1247* b/c we added it here. This is not good...1248*/12491250DEBUG1(DIR, " Looking for \"%s\" ...\n", name);12511252bigmisses++;1253if (cached_stat(name, &cst) == 0)1254return bmake_strdup(name);12551256DEBUG0(DIR, " failed. Returning NULL\n");1257return NULL;1258}12591260/*1261* Find the file with the given name along the given search path.1262*1263* Input:1264* name the file to find1265* path the directories to search, or NULL1266*1267* Results:1268* The freshly allocated path to the file, or NULL.1269*/1270char *1271Dir_FindFile(const char *name, SearchPath *path)1272{1273return FindFile(name, path, false);1274}12751276/*1277* Find the include file with the given name along the given search path.1278*1279* Input:1280* name the file to find1281* path the directories to search, or NULL1282*1283* Results:1284* The freshly allocated path to the file, or NULL.1285*/1286char *1287Dir_FindInclude(const char *name, SearchPath *path)1288{1289return FindFile(name, path, true);1290}129112921293/*1294* Search for 'needle' starting at the directory 'here' and then working our1295* way up towards the root directory. Return the allocated path, or NULL.1296*/1297char *1298Dir_FindHereOrAbove(const char *here, const char *needle)1299{1300struct cached_stat cst;1301char *dirbase, *dirbase_end;1302char *try, *try_end;13031304dirbase = bmake_strdup(here);1305dirbase_end = dirbase + strlen(dirbase);13061307for (;;) {1308try = str_concat3(dirbase, "/", needle);1309if (cached_stat(try, &cst) != -1) {1310if ((cst.cst_mode & S_IFMT) != S_IFDIR) {1311/*1312* Chop off the filename, to return a1313* directory.1314*/1315try_end = try + strlen(try);1316while (try_end > try && *try_end != '/')1317try_end--;1318if (try_end > try)1319*try_end = '\0'; /* chop! */1320}13211322free(dirbase);1323return try;1324}1325free(try);13261327if (dirbase_end == dirbase)1328break; /* failed! */13291330/* Truncate dirbase from the end to move up a dir. */1331while (dirbase_end > dirbase && *dirbase_end != '/')1332dirbase_end--;1333*dirbase_end = '\0'; /* chop! */1334}13351336free(dirbase);1337return NULL;1338}13391340/*1341* This is an implied source, and it may have moved,1342* see if we can find it via the current .PATH1343*/1344static char *1345ResolveMovedDepends(GNode *gn)1346{1347char *fullName;13481349const char *base = str_basename(gn->name);1350if (base == gn->name)1351return NULL;13521353fullName = Dir_FindFile(base, Suff_FindPath(gn));1354if (fullName == NULL)1355return NULL;13561357/*1358* Put the found file in gn->path so that we give that to the compiler.1359*/1360/*1361* XXX: Better just reset gn->path to NULL; updating it is already done1362* by Dir_UpdateMTime.1363*/1364gn->path = bmake_strdup(fullName);1365if (!Job_RunTarget(".STALE", gn->fname))1366fprintf(stdout, /* XXX: Why stdout? */1367"%s: %s:%u: ignoring stale %s for %s, found %s\n",1368progname, gn->fname, gn->lineno,1369makeDependfile, gn->name, fullName);13701371return fullName;1372}13731374static char *1375ResolveFullName(GNode *gn)1376{1377char *fullName;13781379fullName = gn->path;1380if (fullName == NULL && !(gn->type & OP_NOPATH)) {13811382fullName = Dir_FindFile(gn->name, Suff_FindPath(gn));13831384if (fullName == NULL && gn->flags.fromDepend &&1385!Lst_IsEmpty(&gn->implicitParents))1386fullName = ResolveMovedDepends(gn);13871388DEBUG2(DIR, "Found '%s' as '%s'\n",1389gn->name, fullName != NULL ? fullName : "(not found)");1390}13911392if (fullName == NULL)1393fullName = bmake_strdup(gn->name);13941395/* XXX: Is every piece of memory freed as it should? */13961397return fullName;1398}13991400/*1401* Search 'gn' along 'dirSearchPath' and store its modification time in1402* 'gn->mtime'. If no file is found, store 0 instead.1403*1404* The found file is stored in 'gn->path', unless the node already had a path.1405*/1406void1407Dir_UpdateMTime(GNode *gn, bool forceRefresh)1408{1409char *fullName;1410struct cached_stat cst;14111412if (gn->type & OP_ARCHV) {1413Arch_UpdateMTime(gn);1414return;1415}14161417if (gn->type & OP_PHONY) {1418gn->mtime = 0;1419return;1420}14211422fullName = ResolveFullName(gn);14231424if (cached_stats(fullName, &cst, false, forceRefresh) < 0) {1425if (gn->type & OP_MEMBER) {1426if (fullName != gn->path)1427free(fullName);1428Arch_UpdateMemberMTime(gn);1429return;1430}14311432cst.cst_mtime = 0;1433}14341435if (fullName != NULL && gn->path == NULL)1436gn->path = fullName;1437/* XXX: else free(fullName)? */14381439gn->mtime = cst.cst_mtime;1440}14411442/*1443* Read the directory and add it to the cache in openDirs.1444* If a path is given, add the directory to that path as well.1445*/1446static CachedDir *1447CacheNewDir(const char *name, SearchPath *path)1448{1449CachedDir *dir = NULL;1450DIR *d;1451struct dirent *dp;14521453if ((d = opendir(name)) == NULL) {1454DEBUG1(DIR, "Caching %s ... not found\n", name);1455return dir;1456}14571458DEBUG1(DIR, "Caching %s ...\n", name);14591460dir = CachedDir_New(name);14611462while ((dp = readdir(d)) != NULL) {14631464#if defined(sun) && defined(d_ino) /* d_ino is a sunos4 #define for d_fileno */1465/*1466* The sun directory library doesn't check for a 0 inode1467* (0-inode slots just take up space), so we have to do1468* it ourselves.1469*/1470if (dp->d_fileno == 0)1471continue;1472#endif /* sun && d_ino */14731474(void)HashSet_Add(&dir->files, dp->d_name);1475}1476(void)closedir(d);14771478OpenDirs_Add(&openDirs, dir);1479if (path != NULL)1480Lst_Append(&path->dirs, CachedDir_Ref(dir));14811482DEBUG1(DIR, "Caching %s done\n", name);1483return dir;1484}14851486/*1487* Read the list of filenames in the directory 'name' and store the result1488* in 'openDirs'.1489*1490* If a search path is given, append the directory to that path.1491*1492* Input:1493* path The path to which the directory should be1494* added, or NULL to only add the directory to openDirs.1495* name The name of the directory to add.1496* The name is not normalized in any way.1497* Output:1498* result If no path is given and the directory exists, the1499* returned CachedDir has a reference count of 0. It1500* must either be assigned to a variable using1501* CachedDir_Assign or be appended to a SearchPath using1502* Lst_Append and CachedDir_Ref.1503*/1504CachedDir *1505SearchPath_Add(SearchPath *path, const char *name)1506{15071508if (path != NULL && strcmp(name, ".DOTLAST") == 0) {1509CachedDirListNode *ln;15101511/* XXX: Linear search gets slow with thousands of entries. */1512for (ln = path->dirs.first; ln != NULL; ln = ln->next) {1513CachedDir *pathDir = ln->datum;1514if (strcmp(pathDir->name, name) == 0)1515return pathDir;1516}15171518Lst_Prepend(&path->dirs, CachedDir_Ref(dotLast));1519}15201521if (path != NULL) {1522/* XXX: Why is OpenDirs only checked if path != NULL? */1523CachedDir *dir = OpenDirs_Find(&openDirs, name);1524if (dir != NULL) {1525if (Lst_FindDatum(&path->dirs, dir) == NULL)1526Lst_Append(&path->dirs, CachedDir_Ref(dir));1527return dir;1528}1529}15301531return CacheNewDir(name, path);1532}15331534/*1535* Return a copy of dirSearchPath, incrementing the reference counts for1536* the contained directories.1537*/1538SearchPath *1539Dir_CopyDirSearchPath(void)1540{1541SearchPath *path = SearchPath_New();1542CachedDirListNode *ln;1543for (ln = dirSearchPath.dirs.first; ln != NULL; ln = ln->next) {1544CachedDir *dir = ln->datum;1545Lst_Append(&path->dirs, CachedDir_Ref(dir));1546}1547return path;1548}15491550/*1551* Make a string by taking all the directories in the given search path and1552* preceding them by the given flag. Used by the suffix module to create1553* variables for compilers based on suffix search paths. Note that there is no1554* space between the given flag and each directory.1555*/1556char *1557SearchPath_ToFlags(SearchPath *path, const char *flag)1558{1559Buffer buf;1560CachedDirListNode *ln;15611562Buf_Init(&buf);15631564if (path != NULL) {1565for (ln = path->dirs.first; ln != NULL; ln = ln->next) {1566CachedDir *dir = ln->datum;1567Buf_AddStr(&buf, " ");1568Buf_AddStr(&buf, flag);1569Buf_AddStr(&buf, dir->name);1570}1571}15721573return Buf_DoneData(&buf);1574}15751576/* Free the search path and all directories mentioned in it. */1577void1578SearchPath_Free(SearchPath *path)1579{1580CachedDirListNode *ln;15811582for (ln = path->dirs.first; ln != NULL; ln = ln->next) {1583CachedDir *dir = ln->datum;1584CachedDir_Unref(dir);1585}1586Lst_Done(&path->dirs);1587free(path);1588}15891590/*1591* Clear out all elements from the given search path.1592* The path is set to the empty list but is not destroyed.1593*/1594void1595SearchPath_Clear(SearchPath *path)1596{1597while (!Lst_IsEmpty(&path->dirs)) {1598CachedDir *dir = Lst_Dequeue(&path->dirs);1599CachedDir_Unref(dir);1600}1601}160216031604/*1605* Concatenate two paths, adding the second to the end of the first,1606* skipping duplicates.1607*/1608void1609SearchPath_AddAll(SearchPath *dst, SearchPath *src)1610{1611CachedDirListNode *ln;16121613for (ln = src->dirs.first; ln != NULL; ln = ln->next) {1614CachedDir *dir = ln->datum;1615if (Lst_FindDatum(&dst->dirs, dir) == NULL)1616Lst_Append(&dst->dirs, CachedDir_Ref(dir));1617}1618}16191620static int1621percentage(int num, int den)1622{1623return den != 0 ? num * 100 / den : 0;1624}16251626void1627Dir_PrintDirectories(void)1628{1629CachedDirListNode *ln;16301631debug_printf("#*** Directory Cache:\n");1632debug_printf(1633"# Stats: %d hits %d misses %d near misses %d losers (%d%%)\n",1634hits, misses, nearmisses, bigmisses,1635percentage(hits, hits + bigmisses + nearmisses));1636debug_printf("# refs hits directory\n");16371638for (ln = openDirs.list.first; ln != NULL; ln = ln->next) {1639CachedDir *dir = ln->datum;1640debug_printf("# %4d %4d %s\n",1641dir->refCount, dir->hits, dir->name);1642}1643}16441645void1646SearchPath_Print(const SearchPath *path)1647{1648CachedDirListNode *ln;16491650for (ln = path->dirs.first; ln != NULL; ln = ln->next) {1651const CachedDir *dir = ln->datum;1652debug_printf("%s ", dir->name);1653}1654}165516561657