1*22619282SSimon J. Gerraty /* $NetBSD: dir.c,v 1.295 2024/07/07 07:50:57 rillig Exp $ */ 23955d011SMarcel Moolenaar 33955d011SMarcel Moolenaar /* 43955d011SMarcel Moolenaar * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. 53955d011SMarcel Moolenaar * All rights reserved. 63955d011SMarcel Moolenaar * 73955d011SMarcel Moolenaar * This code is derived from software contributed to Berkeley by 83955d011SMarcel Moolenaar * Adam de Boor. 93955d011SMarcel Moolenaar * 103955d011SMarcel Moolenaar * Redistribution and use in source and binary forms, with or without 113955d011SMarcel Moolenaar * modification, are permitted provided that the following conditions 123955d011SMarcel Moolenaar * are met: 133955d011SMarcel Moolenaar * 1. Redistributions of source code must retain the above copyright 143955d011SMarcel Moolenaar * notice, this list of conditions and the following disclaimer. 153955d011SMarcel Moolenaar * 2. Redistributions in binary form must reproduce the above copyright 163955d011SMarcel Moolenaar * notice, this list of conditions and the following disclaimer in the 173955d011SMarcel Moolenaar * documentation and/or other materials provided with the distribution. 183955d011SMarcel Moolenaar * 3. Neither the name of the University nor the names of its contributors 193955d011SMarcel Moolenaar * may be used to endorse or promote products derived from this software 203955d011SMarcel Moolenaar * without specific prior written permission. 213955d011SMarcel Moolenaar * 223955d011SMarcel Moolenaar * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 233955d011SMarcel Moolenaar * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 243955d011SMarcel Moolenaar * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 253955d011SMarcel Moolenaar * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 263955d011SMarcel Moolenaar * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 273955d011SMarcel Moolenaar * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 283955d011SMarcel Moolenaar * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 293955d011SMarcel Moolenaar * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 303955d011SMarcel Moolenaar * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 313955d011SMarcel Moolenaar * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 323955d011SMarcel Moolenaar * SUCH DAMAGE. 333955d011SMarcel Moolenaar */ 343955d011SMarcel Moolenaar 353955d011SMarcel Moolenaar /* 363955d011SMarcel Moolenaar * Copyright (c) 1988, 1989 by Adam de Boor 373955d011SMarcel Moolenaar * Copyright (c) 1989 by Berkeley Softworks 383955d011SMarcel Moolenaar * All rights reserved. 393955d011SMarcel Moolenaar * 403955d011SMarcel Moolenaar * This code is derived from software contributed to Berkeley by 413955d011SMarcel Moolenaar * Adam de Boor. 423955d011SMarcel Moolenaar * 433955d011SMarcel Moolenaar * Redistribution and use in source and binary forms, with or without 443955d011SMarcel Moolenaar * modification, are permitted provided that the following conditions 453955d011SMarcel Moolenaar * are met: 463955d011SMarcel Moolenaar * 1. Redistributions of source code must retain the above copyright 473955d011SMarcel Moolenaar * notice, this list of conditions and the following disclaimer. 483955d011SMarcel Moolenaar * 2. Redistributions in binary form must reproduce the above copyright 493955d011SMarcel Moolenaar * notice, this list of conditions and the following disclaimer in the 503955d011SMarcel Moolenaar * documentation and/or other materials provided with the distribution. 513955d011SMarcel Moolenaar * 3. All advertising materials mentioning features or use of this software 523955d011SMarcel Moolenaar * must display the following acknowledgement: 533955d011SMarcel Moolenaar * This product includes software developed by the University of 543955d011SMarcel Moolenaar * California, Berkeley and its contributors. 553955d011SMarcel Moolenaar * 4. Neither the name of the University nor the names of its contributors 563955d011SMarcel Moolenaar * may be used to endorse or promote products derived from this software 573955d011SMarcel Moolenaar * without specific prior written permission. 583955d011SMarcel Moolenaar * 593955d011SMarcel Moolenaar * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 603955d011SMarcel Moolenaar * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 613955d011SMarcel Moolenaar * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 623955d011SMarcel Moolenaar * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 633955d011SMarcel Moolenaar * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 643955d011SMarcel Moolenaar * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 653955d011SMarcel Moolenaar * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 663955d011SMarcel Moolenaar * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 673955d011SMarcel Moolenaar * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 683955d011SMarcel Moolenaar * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 693955d011SMarcel Moolenaar * SUCH DAMAGE. 703955d011SMarcel Moolenaar */ 713955d011SMarcel Moolenaar 7206b9b3e0SSimon J. Gerraty /* 7306b9b3e0SSimon J. Gerraty * Directory searching using wildcards and/or normal names. 74956e45f6SSimon J. Gerraty * Used both for source wildcarding in the makefile and for finding 753955d011SMarcel Moolenaar * implicit sources. 763955d011SMarcel Moolenaar * 773955d011SMarcel Moolenaar * The interface for this module is: 783955d011SMarcel Moolenaar * Dir_Init Initialize the module. 793955d011SMarcel Moolenaar * 80956e45f6SSimon J. Gerraty * Dir_InitCur Set the cur CachedDir. 813955d011SMarcel Moolenaar * 82956e45f6SSimon J. Gerraty * Dir_InitDot Set the dot CachedDir. 833955d011SMarcel Moolenaar * 843955d011SMarcel Moolenaar * Dir_End Clean up the module. 853955d011SMarcel Moolenaar * 86d5e0a182SSimon J. Gerraty * Dir_SetPATH Set ${.PATH} to reflect the state of dirSearchPath. 873955d011SMarcel Moolenaar * 88956e45f6SSimon J. Gerraty * Dir_HasWildcards 89b0c40a00SSimon J. Gerraty * Returns true if the name given it needs to 903955d011SMarcel Moolenaar * be wildcard-expanded. 913955d011SMarcel Moolenaar * 92dba7b0efSSimon J. Gerraty * SearchPath_Expand 93dba7b0efSSimon J. Gerraty * Expand a filename pattern to find all matching files 94dba7b0efSSimon J. Gerraty * from the search path. 953955d011SMarcel Moolenaar * 963955d011SMarcel Moolenaar * Dir_FindFile Searches for a file on a given search path. 97d5e0a182SSimon J. Gerraty * If it exists, returns the entire path, otherwise NULL. 983955d011SMarcel Moolenaar * 99956e45f6SSimon J. Gerraty * Dir_FindHereOrAbove 100d5e0a182SSimon J. Gerraty * Search for a path in the current directory and then 101d5e0a182SSimon J. Gerraty * all the directories above it in turn, until the path 102d5e0a182SSimon J. Gerraty * is found or the root directory ("/") is reached. 1033955d011SMarcel Moolenaar * 104e2eeea75SSimon J. Gerraty * Dir_UpdateMTime 105e2eeea75SSimon J. Gerraty * Update the modification time and path of a node with 106e2eeea75SSimon J. Gerraty * data from the file corresponding to the node. 1073955d011SMarcel Moolenaar * 108dba7b0efSSimon J. Gerraty * SearchPath_Add Add a directory to a search path. 1093955d011SMarcel Moolenaar * 11006b9b3e0SSimon J. Gerraty * SearchPath_ToFlags 11106b9b3e0SSimon J. Gerraty * Given a search path and a command flag, create 1123955d011SMarcel Moolenaar * a string with each of the directories in the path 1133955d011SMarcel Moolenaar * preceded by the command flag and all of them 1143955d011SMarcel Moolenaar * separated by a space. 1153955d011SMarcel Moolenaar * 11606b9b3e0SSimon J. Gerraty * SearchPath_Clear 11706b9b3e0SSimon J. Gerraty * Resets a search path to the empty list. 1183955d011SMarcel Moolenaar * 1193955d011SMarcel Moolenaar * For debugging: 120956e45f6SSimon J. Gerraty * Dir_PrintDirectories 121956e45f6SSimon J. Gerraty * Print stats about the directory cache. 1223955d011SMarcel Moolenaar */ 1233955d011SMarcel Moolenaar 1243955d011SMarcel Moolenaar #include <sys/types.h> 1253955d011SMarcel Moolenaar #include <sys/stat.h> 1263955d011SMarcel Moolenaar 1273955d011SMarcel Moolenaar #include <dirent.h> 1283955d011SMarcel Moolenaar #include <errno.h> 1293955d011SMarcel Moolenaar 1303955d011SMarcel Moolenaar #include "make.h" 1313955d011SMarcel Moolenaar #include "dir.h" 1321748de26SSimon J. Gerraty #include "job.h" 1333955d011SMarcel Moolenaar 134956e45f6SSimon J. Gerraty /* "@(#)dir.c 8.2 (Berkeley) 1/2/94" */ 135*22619282SSimon J. Gerraty MAKE_RCSID("$NetBSD: dir.c,v 1.295 2024/07/07 07:50:57 rillig Exp $"); 1362c3632d1SSimon J. Gerraty 13706b9b3e0SSimon J. Gerraty /* 13806b9b3e0SSimon J. Gerraty * A search path is a list of CachedDir structures. A CachedDir has in it the 139956e45f6SSimon J. Gerraty * name of the directory and the names of all the files in the directory. 140956e45f6SSimon J. Gerraty * This is used to cut down on the number of system calls necessary to find 141956e45f6SSimon J. Gerraty * implicit dependents and their like. Since these searches are made before 142956e45f6SSimon J. Gerraty * any actions are taken, we need not worry about the directory changing due 143956e45f6SSimon J. Gerraty * to creation commands. If this hampers the style of some makefiles, they 144956e45f6SSimon J. Gerraty * must be changed. 1453955d011SMarcel Moolenaar * 146956e45f6SSimon J. Gerraty * All previously-read directories are kept in openDirs, which is checked 147956e45f6SSimon J. Gerraty * first before a directory is opened. 1483955d011SMarcel Moolenaar * 149d5e0a182SSimon J. Gerraty * This cache is used by the multi-level transformation code in suff.c, which 150d5e0a182SSimon J. Gerraty * tends to search for far more files than in regular explicit targets. After 151d5e0a182SSimon J. Gerraty * a directory has been cached, any later changes to that directory are not 152d5e0a182SSimon J. Gerraty * reflected in the cache. To keep the cache up to date, there are several 153d5e0a182SSimon J. Gerraty * ideas: 1543955d011SMarcel Moolenaar * 155956e45f6SSimon J. Gerraty * 1) just use stat to test for a file's existence. As mentioned above, 156d5e0a182SSimon J. Gerraty * this is very inefficient due to the number of checks performed by 157956e45f6SSimon J. Gerraty * the multi-level transformation code. 158956e45f6SSimon J. Gerraty * 159d5e0a182SSimon J. Gerraty * 2) use readdir() to search the directories, keeping them open between 160d5e0a182SSimon J. Gerraty * checks. Around 1993 or earlier, this didn't slow down the process too 161d5e0a182SSimon J. Gerraty * much, but it consumed one file descriptor per open directory, which 162d5e0a182SSimon J. Gerraty * was critical on the then-current operating systems, as many limited 163d5e0a182SSimon J. Gerraty * the number of open file descriptors to 20 or 32. 1643955d011SMarcel Moolenaar * 165956e45f6SSimon J. Gerraty * 3) record the mtime of the directory in the CachedDir structure and 166956e45f6SSimon J. Gerraty * verify the directory hasn't changed since the contents were cached. 167956e45f6SSimon J. Gerraty * This will catch the creation or deletion of files, but not the 168956e45f6SSimon J. Gerraty * updating of files. However, since it is the creation and deletion 169956e45f6SSimon J. Gerraty * that is the problem, this could be a good thing to do. Unfortunately, 170956e45f6SSimon J. Gerraty * if the directory (say ".") were fairly large and changed fairly 171956e45f6SSimon J. Gerraty * frequently, the constant reloading could seriously degrade 172956e45f6SSimon J. Gerraty * performance. It might be good in such cases to keep track of the 173956e45f6SSimon J. Gerraty * number of reloadings and if the number goes over a (small) limit, 174956e45f6SSimon J. Gerraty * resort to using stat in its place. 1753955d011SMarcel Moolenaar * 176d5e0a182SSimon J. Gerraty * An additional thing to consider is that make is used primarily to create 177d5e0a182SSimon J. Gerraty * C programs and until recently (as of 1993 or earlier), pcc-based compilers 178d5e0a182SSimon J. Gerraty * didn't have an option to specify where the resulting object file should be 179e2eeea75SSimon J. Gerraty * placed. This forced all objects to be created in the current directory. 180e2eeea75SSimon J. Gerraty * This isn't meant as a full excuse, just an explanation of some of the 181e2eeea75SSimon J. Gerraty * reasons for the caching used here. 1823955d011SMarcel Moolenaar * 183956e45f6SSimon J. Gerraty * One more note: the location of a target's file is only performed on the 184956e45f6SSimon J. Gerraty * downward traversal of the graph and then only for terminal nodes in the 185956e45f6SSimon J. Gerraty * graph. This could be construed as wrong in some cases, but prevents 186956e45f6SSimon J. Gerraty * inadvertent modification of files when the "installed" directory for a 187956e45f6SSimon J. Gerraty * file is provided in the search path. 188956e45f6SSimon J. Gerraty * 189956e45f6SSimon J. Gerraty * Another data structure maintained by this module is an mtime cache used 190956e45f6SSimon J. Gerraty * when the searching of cached directories fails to find a file. In the past, 191956e45f6SSimon J. Gerraty * Dir_FindFile would simply perform an access() call in such a case to 192956e45f6SSimon J. Gerraty * determine if the file could be found using just the name given. When this 193956e45f6SSimon J. Gerraty * hit, however, all that was gained was the knowledge that the file existed. 194956e45f6SSimon J. Gerraty * Given that an access() is essentially a stat() without the copyout() call, 195956e45f6SSimon J. Gerraty * and that the same filesystem overhead would have to be incurred in 196956e45f6SSimon J. Gerraty * Dir_MTime, it made sense to replace the access() with a stat() and record 197e2eeea75SSimon J. Gerraty * the mtime in a cache for when Dir_UpdateMTime was actually called. 1983955d011SMarcel Moolenaar */ 1993955d011SMarcel Moolenaar 20006b9b3e0SSimon J. Gerraty 20106b9b3e0SSimon J. Gerraty /* A cache for the filenames in a directory. */ 20206b9b3e0SSimon J. Gerraty struct CachedDir { 20306b9b3e0SSimon J. Gerraty /* 204d5e0a182SSimon J. Gerraty * Name of the directory, either absolute or relative to the current 20506b9b3e0SSimon J. Gerraty * directory. The name is not normalized in any way, that is, "." 20606b9b3e0SSimon J. Gerraty * and "./." are different. 20706b9b3e0SSimon J. Gerraty * 20806b9b3e0SSimon J. Gerraty * Not sure what happens when .CURDIR is assigned a new value; see 209b0c40a00SSimon J. Gerraty * Parse_Var. 21006b9b3e0SSimon J. Gerraty */ 21106b9b3e0SSimon J. Gerraty char *name; 21206b9b3e0SSimon J. Gerraty 21306b9b3e0SSimon J. Gerraty /* 21406b9b3e0SSimon J. Gerraty * The number of SearchPaths that refer to this directory. 21506b9b3e0SSimon J. Gerraty * Plus the number of global variables that refer to this directory. 21606b9b3e0SSimon J. Gerraty * References from openDirs do not count though. 21706b9b3e0SSimon J. Gerraty */ 21806b9b3e0SSimon J. Gerraty int refCount; 21906b9b3e0SSimon J. Gerraty 22006b9b3e0SSimon J. Gerraty /* The number of times a file in this directory has been found. */ 22106b9b3e0SSimon J. Gerraty int hits; 22206b9b3e0SSimon J. Gerraty 22306b9b3e0SSimon J. Gerraty /* The names of the directory entries. */ 22406b9b3e0SSimon J. Gerraty HashSet files; 22506b9b3e0SSimon J. Gerraty }; 22606b9b3e0SSimon J. Gerraty 227956e45f6SSimon J. Gerraty typedef List CachedDirList; 228956e45f6SSimon J. Gerraty typedef ListNode CachedDirListNode; 2293955d011SMarcel Moolenaar 230956e45f6SSimon J. Gerraty /* A list of cached directories, with fast lookup by directory name. */ 231956e45f6SSimon J. Gerraty typedef struct OpenDirs { 23206b9b3e0SSimon J. Gerraty CachedDirList list; 233956e45f6SSimon J. Gerraty HashTable /* of CachedDirListNode */ table; 234956e45f6SSimon J. Gerraty } OpenDirs; 235956e45f6SSimon J. Gerraty 23606b9b3e0SSimon J. Gerraty 237dba7b0efSSimon J. Gerraty SearchPath dirSearchPath = { LST_INIT }; /* main search path */ 23806b9b3e0SSimon J. Gerraty 23906b9b3e0SSimon J. Gerraty static OpenDirs openDirs; /* all cached directories */ 24006b9b3e0SSimon J. Gerraty 24106b9b3e0SSimon J. Gerraty /* 24206b9b3e0SSimon J. Gerraty * Variables for gathering statistics on the efficiency of the caching 24306b9b3e0SSimon J. Gerraty * mechanism. 24406b9b3e0SSimon J. Gerraty */ 24506b9b3e0SSimon J. Gerraty static int hits; /* Found in directory cache */ 24606b9b3e0SSimon J. Gerraty static int misses; /* Sad, but not evil misses */ 24706b9b3e0SSimon J. Gerraty static int nearmisses; /* Found under search path */ 24806b9b3e0SSimon J. Gerraty static int bigmisses; /* Sought by itself */ 24906b9b3e0SSimon J. Gerraty 25006b9b3e0SSimon J. Gerraty /* The cached contents of ".", the relative current directory. */ 25106b9b3e0SSimon J. Gerraty static CachedDir *dot = NULL; 25206b9b3e0SSimon J. Gerraty /* The cached contents of the absolute current directory. */ 25306b9b3e0SSimon J. Gerraty static CachedDir *cur = NULL; 25406b9b3e0SSimon J. Gerraty /* A fake path entry indicating we need to look for '.' last. */ 25506b9b3e0SSimon J. Gerraty static CachedDir *dotLast = NULL; 25606b9b3e0SSimon J. Gerraty 25706b9b3e0SSimon J. Gerraty /* 25806b9b3e0SSimon J. Gerraty * Results of doing a last-resort stat in Dir_FindFile -- if we have to go to 25906b9b3e0SSimon J. Gerraty * the system to find the file, we might as well have its mtime on record. 26006b9b3e0SSimon J. Gerraty * 26106b9b3e0SSimon J. Gerraty * XXX: If this is done way early, there's a chance other rules will have 26206b9b3e0SSimon J. Gerraty * already updated the file, in which case we'll update it again. Generally, 263d5e0a182SSimon J. Gerraty * there won't be two rules to update a single file, so this should be ok. 26406b9b3e0SSimon J. Gerraty */ 26506b9b3e0SSimon J. Gerraty static HashTable mtimes; 26606b9b3e0SSimon J. Gerraty 26706b9b3e0SSimon J. Gerraty static HashTable lmtimes; /* same as mtimes but for lstat */ 26806b9b3e0SSimon J. Gerraty 26906b9b3e0SSimon J. Gerraty 27006b9b3e0SSimon J. Gerraty static void OpenDirs_Remove(OpenDirs *, const char *); 27106b9b3e0SSimon J. Gerraty 27206b9b3e0SSimon J. Gerraty 27306b9b3e0SSimon J. Gerraty static CachedDir * 27406b9b3e0SSimon J. Gerraty CachedDir_New(const char *name) 27506b9b3e0SSimon J. Gerraty { 27606b9b3e0SSimon J. Gerraty CachedDir *dir = bmake_malloc(sizeof *dir); 27706b9b3e0SSimon J. Gerraty 27806b9b3e0SSimon J. Gerraty dir->name = bmake_strdup(name); 27906b9b3e0SSimon J. Gerraty dir->refCount = 0; 28006b9b3e0SSimon J. Gerraty dir->hits = 0; 28106b9b3e0SSimon J. Gerraty HashSet_Init(&dir->files); 28206b9b3e0SSimon J. Gerraty 28306b9b3e0SSimon J. Gerraty #ifdef DEBUG_REFCNT 28406b9b3e0SSimon J. Gerraty DEBUG2(DIR, "CachedDir %p new for \"%s\"\n", dir, dir->name); 28506b9b3e0SSimon J. Gerraty #endif 28606b9b3e0SSimon J. Gerraty 28706b9b3e0SSimon J. Gerraty return dir; 28806b9b3e0SSimon J. Gerraty } 28906b9b3e0SSimon J. Gerraty 29006b9b3e0SSimon J. Gerraty static CachedDir * 29106b9b3e0SSimon J. Gerraty CachedDir_Ref(CachedDir *dir) 29206b9b3e0SSimon J. Gerraty { 29306b9b3e0SSimon J. Gerraty dir->refCount++; 29406b9b3e0SSimon J. Gerraty 29506b9b3e0SSimon J. Gerraty #ifdef DEBUG_REFCNT 29606b9b3e0SSimon J. Gerraty DEBUG3(DIR, "CachedDir %p ++ %d for \"%s\"\n", 29706b9b3e0SSimon J. Gerraty dir, dir->refCount, dir->name); 29806b9b3e0SSimon J. Gerraty #endif 29906b9b3e0SSimon J. Gerraty 30006b9b3e0SSimon J. Gerraty return dir; 30106b9b3e0SSimon J. Gerraty } 30206b9b3e0SSimon J. Gerraty 30306b9b3e0SSimon J. Gerraty static void 30406b9b3e0SSimon J. Gerraty CachedDir_Unref(CachedDir *dir) 30506b9b3e0SSimon J. Gerraty { 30606b9b3e0SSimon J. Gerraty dir->refCount--; 30706b9b3e0SSimon J. Gerraty 30806b9b3e0SSimon J. Gerraty #ifdef DEBUG_REFCNT 30906b9b3e0SSimon J. Gerraty DEBUG3(DIR, "CachedDir %p -- %d for \"%s\"\n", 31006b9b3e0SSimon J. Gerraty dir, dir->refCount, dir->name); 31106b9b3e0SSimon J. Gerraty #endif 31206b9b3e0SSimon J. Gerraty 31306b9b3e0SSimon J. Gerraty if (dir->refCount > 0) 31406b9b3e0SSimon J. Gerraty return; 31506b9b3e0SSimon J. Gerraty 31606b9b3e0SSimon J. Gerraty #ifdef DEBUG_REFCNT 31706b9b3e0SSimon J. Gerraty DEBUG2(DIR, "CachedDir %p free for \"%s\"\n", dir, dir->name); 31806b9b3e0SSimon J. Gerraty #endif 31906b9b3e0SSimon J. Gerraty 32006b9b3e0SSimon J. Gerraty OpenDirs_Remove(&openDirs, dir->name); 32106b9b3e0SSimon J. Gerraty 32206b9b3e0SSimon J. Gerraty free(dir->name); 32306b9b3e0SSimon J. Gerraty HashSet_Done(&dir->files); 32406b9b3e0SSimon J. Gerraty free(dir); 32506b9b3e0SSimon J. Gerraty } 32606b9b3e0SSimon J. Gerraty 327d5e0a182SSimon J. Gerraty /* Update the value of 'var', updating the reference counts. */ 32806b9b3e0SSimon J. Gerraty static void 32906b9b3e0SSimon J. Gerraty CachedDir_Assign(CachedDir **var, CachedDir *dir) 33006b9b3e0SSimon J. Gerraty { 33106b9b3e0SSimon J. Gerraty CachedDir *prev; 33206b9b3e0SSimon J. Gerraty 33306b9b3e0SSimon J. Gerraty prev = *var; 33406b9b3e0SSimon J. Gerraty *var = dir; 33506b9b3e0SSimon J. Gerraty if (dir != NULL) 33606b9b3e0SSimon J. Gerraty CachedDir_Ref(dir); 33706b9b3e0SSimon J. Gerraty if (prev != NULL) 33806b9b3e0SSimon J. Gerraty CachedDir_Unref(prev); 33906b9b3e0SSimon J. Gerraty } 34006b9b3e0SSimon J. Gerraty 341956e45f6SSimon J. Gerraty static void 342956e45f6SSimon J. Gerraty OpenDirs_Init(OpenDirs *odirs) 343956e45f6SSimon J. Gerraty { 34406b9b3e0SSimon J. Gerraty Lst_Init(&odirs->list); 345956e45f6SSimon J. Gerraty HashTable_Init(&odirs->table); 346956e45f6SSimon J. Gerraty } 347956e45f6SSimon J. Gerraty 348956e45f6SSimon J. Gerraty #ifdef CLEANUP 349956e45f6SSimon J. Gerraty static void 350956e45f6SSimon J. Gerraty OpenDirs_Done(OpenDirs *odirs) 351956e45f6SSimon J. Gerraty { 35206b9b3e0SSimon J. Gerraty CachedDirListNode *ln = odirs->list.first; 35306b9b3e0SSimon J. Gerraty DEBUG1(DIR, "OpenDirs_Done: %u entries to remove\n", 35406b9b3e0SSimon J. Gerraty odirs->table.numEntries); 355956e45f6SSimon J. Gerraty while (ln != NULL) { 356956e45f6SSimon J. Gerraty CachedDirListNode *next = ln->next; 357956e45f6SSimon J. Gerraty CachedDir *dir = ln->datum; 35806b9b3e0SSimon J. Gerraty DEBUG2(DIR, "OpenDirs_Done: refCount %d for \"%s\"\n", 35906b9b3e0SSimon J. Gerraty dir->refCount, dir->name); 36006b9b3e0SSimon J. Gerraty CachedDir_Unref(dir); /* removes the dir from odirs->list */ 361956e45f6SSimon J. Gerraty ln = next; 362956e45f6SSimon J. Gerraty } 36306b9b3e0SSimon J. Gerraty Lst_Done(&odirs->list); 364956e45f6SSimon J. Gerraty HashTable_Done(&odirs->table); 365956e45f6SSimon J. Gerraty } 366956e45f6SSimon J. Gerraty #endif 367956e45f6SSimon J. Gerraty 368956e45f6SSimon J. Gerraty static CachedDir * 369956e45f6SSimon J. Gerraty OpenDirs_Find(OpenDirs *odirs, const char *name) 370956e45f6SSimon J. Gerraty { 371956e45f6SSimon J. Gerraty CachedDirListNode *ln = HashTable_FindValue(&odirs->table, name); 372956e45f6SSimon J. Gerraty return ln != NULL ? ln->datum : NULL; 373956e45f6SSimon J. Gerraty } 374956e45f6SSimon J. Gerraty 375956e45f6SSimon J. Gerraty static void 376956e45f6SSimon J. Gerraty OpenDirs_Add(OpenDirs *odirs, CachedDir *cdir) 377956e45f6SSimon J. Gerraty { 378e2eeea75SSimon J. Gerraty if (HashTable_FindEntry(&odirs->table, cdir->name) != NULL) 379956e45f6SSimon J. Gerraty return; 38006b9b3e0SSimon J. Gerraty Lst_Append(&odirs->list, cdir); 38106b9b3e0SSimon J. Gerraty HashTable_Set(&odirs->table, cdir->name, odirs->list.last); 382956e45f6SSimon J. Gerraty } 383956e45f6SSimon J. Gerraty 384956e45f6SSimon J. Gerraty static void 385956e45f6SSimon J. Gerraty OpenDirs_Remove(OpenDirs *odirs, const char *name) 386956e45f6SSimon J. Gerraty { 387956e45f6SSimon J. Gerraty HashEntry *he = HashTable_FindEntry(&odirs->table, name); 388956e45f6SSimon J. Gerraty CachedDirListNode *ln; 389956e45f6SSimon J. Gerraty if (he == NULL) 390956e45f6SSimon J. Gerraty return; 391956e45f6SSimon J. Gerraty ln = HashEntry_Get(he); 392956e45f6SSimon J. Gerraty HashTable_DeleteEntry(&odirs->table, he); 39306b9b3e0SSimon J. Gerraty Lst_Remove(&odirs->list, ln); 394956e45f6SSimon J. Gerraty } 395956e45f6SSimon J. Gerraty 3963955d011SMarcel Moolenaar /* 39706b9b3e0SSimon J. Gerraty * Returns 0 and the result of stat(2) or lstat(2) in *out_cst, 39806b9b3e0SSimon J. Gerraty * or -1 on error. 3993955d011SMarcel Moolenaar */ 40039ac7ef4SSimon J. Gerraty static int 401e2eeea75SSimon J. Gerraty cached_stats(const char *pathname, struct cached_stat *out_cst, 40212904384SSimon J. Gerraty bool useLstat, bool forceRefresh) 40339ac7ef4SSimon J. Gerraty { 40412904384SSimon J. Gerraty HashTable *tbl = useLstat ? &lmtimes : &mtimes; 4052c3632d1SSimon J. Gerraty struct stat sys_st; 406e2eeea75SSimon J. Gerraty struct cached_stat *cst; 40739ac7ef4SSimon J. Gerraty int rc; 40839ac7ef4SSimon J. Gerraty 409e2eeea75SSimon J. Gerraty if (pathname == NULL || pathname[0] == '\0') 410e2eeea75SSimon J. Gerraty return -1; /* This can happen in meta mode. */ 41139ac7ef4SSimon J. Gerraty 412e2eeea75SSimon J. Gerraty cst = HashTable_FindValue(tbl, pathname); 41312904384SSimon J. Gerraty if (cst != NULL && !forceRefresh) { 414e2eeea75SSimon J. Gerraty *out_cst = *cst; 41506b9b3e0SSimon J. Gerraty DEBUG2(DIR, "Using cached time %s for %s\n", 416e2eeea75SSimon J. Gerraty Targ_FmtTime(cst->cst_mtime), pathname); 41739ac7ef4SSimon J. Gerraty return 0; 41839ac7ef4SSimon J. Gerraty } 41939ac7ef4SSimon J. Gerraty 42012904384SSimon J. Gerraty rc = (useLstat ? lstat : stat)(pathname, &sys_st); 42139ac7ef4SSimon J. Gerraty if (rc == -1) 422e2eeea75SSimon J. Gerraty return -1; /* don't cache negative lookups */ 42339ac7ef4SSimon J. Gerraty 4242c3632d1SSimon J. Gerraty if (sys_st.st_mtime == 0) 4252c3632d1SSimon J. Gerraty sys_st.st_mtime = 1; /* avoid confusion with missing file */ 42639ac7ef4SSimon J. Gerraty 427e2eeea75SSimon J. Gerraty if (cst == NULL) { 428e2eeea75SSimon J. Gerraty cst = bmake_malloc(sizeof *cst); 429e2eeea75SSimon J. Gerraty HashTable_Set(tbl, pathname, cst); 430e2eeea75SSimon J. Gerraty } 4312c3632d1SSimon J. Gerraty 432e2eeea75SSimon J. Gerraty cst->cst_mtime = sys_st.st_mtime; 433e2eeea75SSimon J. Gerraty cst->cst_mode = sys_st.st_mode; 434e2eeea75SSimon J. Gerraty 435e2eeea75SSimon J. Gerraty *out_cst = *cst; 43606b9b3e0SSimon J. Gerraty DEBUG2(DIR, " Caching %s for %s\n", 4372c3632d1SSimon J. Gerraty Targ_FmtTime(sys_st.st_mtime), pathname); 43839ac7ef4SSimon J. Gerraty 43939ac7ef4SSimon J. Gerraty return 0; 44039ac7ef4SSimon J. Gerraty } 44139ac7ef4SSimon J. Gerraty 44239ac7ef4SSimon J. Gerraty int 443e2eeea75SSimon J. Gerraty cached_stat(const char *pathname, struct cached_stat *cst) 44439ac7ef4SSimon J. Gerraty { 44512904384SSimon J. Gerraty return cached_stats(pathname, cst, false, false); 44639ac7ef4SSimon J. Gerraty } 44739ac7ef4SSimon J. Gerraty 44839ac7ef4SSimon J. Gerraty int 449e2eeea75SSimon J. Gerraty cached_lstat(const char *pathname, struct cached_stat *cst) 45039ac7ef4SSimon J. Gerraty { 45112904384SSimon J. Gerraty return cached_stats(pathname, cst, true, false); 45239ac7ef4SSimon J. Gerraty } 45339ac7ef4SSimon J. Gerraty 454956e45f6SSimon J. Gerraty /* Initialize the directories module. */ 4553955d011SMarcel Moolenaar void 4562c3632d1SSimon J. Gerraty Dir_Init(void) 4573955d011SMarcel Moolenaar { 458956e45f6SSimon J. Gerraty OpenDirs_Init(&openDirs); 459956e45f6SSimon J. Gerraty HashTable_Init(&mtimes); 460956e45f6SSimon J. Gerraty HashTable_Init(&lmtimes); 46106b9b3e0SSimon J. Gerraty CachedDir_Assign(&dotLast, CachedDir_New(".DOTLAST")); 4623955d011SMarcel Moolenaar } 4633955d011SMarcel Moolenaar 464d5e0a182SSimon J. Gerraty /* Called by Dir_InitDir and whenever .CURDIR is assigned to. */ 4653955d011SMarcel Moolenaar void 466dba7b0efSSimon J. Gerraty Dir_InitCur(const char *newCurdir) 4673955d011SMarcel Moolenaar { 468956e45f6SSimon J. Gerraty CachedDir *dir; 4693955d011SMarcel Moolenaar 470dba7b0efSSimon J. Gerraty if (newCurdir == NULL) 471e2eeea75SSimon J. Gerraty return; 472e2eeea75SSimon J. Gerraty 4733955d011SMarcel Moolenaar /* 474d5e0a182SSimon J. Gerraty * The build directory is not the same as the source directory. 4753955d011SMarcel Moolenaar * Keep this one around too. 4763955d011SMarcel Moolenaar */ 477dba7b0efSSimon J. Gerraty dir = SearchPath_Add(NULL, newCurdir); 478e2eeea75SSimon J. Gerraty if (dir == NULL) 479e2eeea75SSimon J. Gerraty return; 480e2eeea75SSimon J. Gerraty 48106b9b3e0SSimon J. Gerraty CachedDir_Assign(&cur, dir); 48206b9b3e0SSimon J. Gerraty } 483e2eeea75SSimon J. Gerraty 4843955d011SMarcel Moolenaar /* 485d5e0a182SSimon J. Gerraty * (Re)initialize "dot" (the current/object directory). 48606b9b3e0SSimon J. Gerraty * Some directories may be cached. 4873955d011SMarcel Moolenaar */ 4883955d011SMarcel Moolenaar void 4893955d011SMarcel Moolenaar Dir_InitDot(void) 4903955d011SMarcel Moolenaar { 49106b9b3e0SSimon J. Gerraty CachedDir *dir; 4923955d011SMarcel Moolenaar 493dba7b0efSSimon J. Gerraty dir = SearchPath_Add(NULL, "."); 49406b9b3e0SSimon J. Gerraty if (dir == NULL) { 4953955d011SMarcel Moolenaar Error("Cannot open `.' (%s)", strerror(errno)); 49606b9b3e0SSimon J. Gerraty exit(2); /* Not 1 so -q can distinguish error */ 4973955d011SMarcel Moolenaar } 4983955d011SMarcel Moolenaar 49906b9b3e0SSimon J. Gerraty CachedDir_Assign(&dot, dir); 50006b9b3e0SSimon J. Gerraty 5013955d011SMarcel Moolenaar Dir_SetPATH(); /* initialize */ 5023955d011SMarcel Moolenaar } 5033955d011SMarcel Moolenaar 5048d5c8e21SSimon J. Gerraty #ifdef CLEANUP 5058d5c8e21SSimon J. Gerraty static void 5068d5c8e21SSimon J. Gerraty FreeCachedTable(HashTable *tbl) 5078d5c8e21SSimon J. Gerraty { 5088d5c8e21SSimon J. Gerraty HashIter hi; 5098d5c8e21SSimon J. Gerraty HashIter_Init(&hi, tbl); 5108d5c8e21SSimon J. Gerraty while (HashIter_Next(&hi)) 5118d5c8e21SSimon J. Gerraty free(hi.entry->value); 5128d5c8e21SSimon J. Gerraty HashTable_Done(tbl); 5138d5c8e21SSimon J. Gerraty } 5148d5c8e21SSimon J. Gerraty 515956e45f6SSimon J. Gerraty /* Clean up the directories module. */ 5163955d011SMarcel Moolenaar void 5173955d011SMarcel Moolenaar Dir_End(void) 5183955d011SMarcel Moolenaar { 51906b9b3e0SSimon J. Gerraty CachedDir_Assign(&cur, NULL); 52006b9b3e0SSimon J. Gerraty CachedDir_Assign(&dot, NULL); 52106b9b3e0SSimon J. Gerraty CachedDir_Assign(&dotLast, NULL); 52206b9b3e0SSimon J. Gerraty SearchPath_Clear(&dirSearchPath); 523956e45f6SSimon J. Gerraty OpenDirs_Done(&openDirs); 5248d5c8e21SSimon J. Gerraty FreeCachedTable(&mtimes); 5258d5c8e21SSimon J. Gerraty FreeCachedTable(&lmtimes); 5263955d011SMarcel Moolenaar } 527*22619282SSimon J. Gerraty #endif 5283955d011SMarcel Moolenaar 5293955d011SMarcel Moolenaar /* 5303955d011SMarcel Moolenaar * We want ${.PATH} to indicate the order in which we will actually 5313955d011SMarcel Moolenaar * search, so we rebuild it after any .PATH: target. 5323955d011SMarcel Moolenaar * This is the simplest way to deal with the effect of .DOTLAST. 5333955d011SMarcel Moolenaar */ 5343955d011SMarcel Moolenaar void 5353955d011SMarcel Moolenaar Dir_SetPATH(void) 5363955d011SMarcel Moolenaar { 537956e45f6SSimon J. Gerraty CachedDirListNode *ln; 538b0c40a00SSimon J. Gerraty bool seenDotLast = false; /* true if we should search '.' last */ 5393955d011SMarcel Moolenaar 540dba7b0efSSimon J. Gerraty Global_Delete(".PATH"); 5413955d011SMarcel Moolenaar 542dba7b0efSSimon J. Gerraty if ((ln = dirSearchPath.dirs.first) != NULL) { 543956e45f6SSimon J. Gerraty CachedDir *dir = ln->datum; 544956e45f6SSimon J. Gerraty if (dir == dotLast) { 545b0c40a00SSimon J. Gerraty seenDotLast = true; 546dba7b0efSSimon J. Gerraty Global_Append(".PATH", dotLast->name); 5473955d011SMarcel Moolenaar } 5483955d011SMarcel Moolenaar } 5493955d011SMarcel Moolenaar 55006b9b3e0SSimon J. Gerraty if (!seenDotLast) { 55106b9b3e0SSimon J. Gerraty if (dot != NULL) 552dba7b0efSSimon J. Gerraty Global_Append(".PATH", dot->name); 55306b9b3e0SSimon J. Gerraty if (cur != NULL) 554dba7b0efSSimon J. Gerraty Global_Append(".PATH", cur->name); 5553955d011SMarcel Moolenaar } 5563955d011SMarcel Moolenaar 557dba7b0efSSimon J. Gerraty for (ln = dirSearchPath.dirs.first; ln != NULL; ln = ln->next) { 558956e45f6SSimon J. Gerraty CachedDir *dir = ln->datum; 559956e45f6SSimon J. Gerraty if (dir == dotLast) 5603955d011SMarcel Moolenaar continue; 56106b9b3e0SSimon J. Gerraty if (dir == dot && seenDotLast) 5623955d011SMarcel Moolenaar continue; 563dba7b0efSSimon J. Gerraty Global_Append(".PATH", dir->name); 5643955d011SMarcel Moolenaar } 5653955d011SMarcel Moolenaar 56606b9b3e0SSimon J. Gerraty if (seenDotLast) { 56706b9b3e0SSimon J. Gerraty if (dot != NULL) 568dba7b0efSSimon J. Gerraty Global_Append(".PATH", dot->name); 56906b9b3e0SSimon J. Gerraty if (cur != NULL) 570dba7b0efSSimon J. Gerraty Global_Append(".PATH", cur->name); 5713955d011SMarcel Moolenaar } 5723955d011SMarcel Moolenaar } 5733955d011SMarcel Moolenaar 5744fde40d9SSimon J. Gerraty 5754fde40d9SSimon J. Gerraty void 5764fde40d9SSimon J. Gerraty Dir_SetSYSPATH(void) 5774fde40d9SSimon J. Gerraty { 5784fde40d9SSimon J. Gerraty CachedDirListNode *ln; 5799d3df31eSSimon J. Gerraty SearchPath *path = Lst_IsEmpty(&sysIncPath->dirs) 5809d3df31eSSimon J. Gerraty ? defSysIncPath : sysIncPath; 5814fde40d9SSimon J. Gerraty 5824fde40d9SSimon J. Gerraty Var_ReadOnly(".SYSPATH", false); 5834fde40d9SSimon J. Gerraty Global_Delete(".SYSPATH"); 5849d3df31eSSimon J. Gerraty for (ln = path->dirs.first; ln != NULL; ln = ln->next) { 5854fde40d9SSimon J. Gerraty CachedDir *dir = ln->datum; 5864fde40d9SSimon J. Gerraty Global_Append(".SYSPATH", dir->name); 5874fde40d9SSimon J. Gerraty } 5884fde40d9SSimon J. Gerraty Var_ReadOnly(".SYSPATH", true); 5894fde40d9SSimon J. Gerraty } 5904fde40d9SSimon J. Gerraty 59106b9b3e0SSimon J. Gerraty /* 59206b9b3e0SSimon J. Gerraty * See if the given name has any wildcard characters in it and all braces and 593956e45f6SSimon J. Gerraty * brackets are properly balanced. 5942c3632d1SSimon J. Gerraty * 5952c3632d1SSimon J. Gerraty * XXX: This code is not 100% correct ([^]] fails etc.). I really don't think 5962c3632d1SSimon J. Gerraty * that make(1) should be expanding patterns, because then you have to set a 5972c3632d1SSimon J. Gerraty * mechanism for escaping the expansion! 5983955d011SMarcel Moolenaar */ 599b0c40a00SSimon J. Gerraty bool 6002c3632d1SSimon J. Gerraty Dir_HasWildcards(const char *name) 6013955d011SMarcel Moolenaar { 602956e45f6SSimon J. Gerraty const char *p; 603b0c40a00SSimon J. Gerraty bool wild = false; 6042c3632d1SSimon J. Gerraty int braces = 0, brackets = 0; 6053955d011SMarcel Moolenaar 606956e45f6SSimon J. Gerraty for (p = name; *p != '\0'; p++) { 607956e45f6SSimon J. Gerraty switch (*p) { 6083955d011SMarcel Moolenaar case '{': 6092c3632d1SSimon J. Gerraty braces++; 610b0c40a00SSimon J. Gerraty wild = true; 6113955d011SMarcel Moolenaar break; 6123955d011SMarcel Moolenaar case '}': 6132c3632d1SSimon J. Gerraty braces--; 6143955d011SMarcel Moolenaar break; 6153955d011SMarcel Moolenaar case '[': 6162c3632d1SSimon J. Gerraty brackets++; 617b0c40a00SSimon J. Gerraty wild = true; 6183955d011SMarcel Moolenaar break; 6193955d011SMarcel Moolenaar case ']': 6202c3632d1SSimon J. Gerraty brackets--; 6213955d011SMarcel Moolenaar break; 6223955d011SMarcel Moolenaar case '?': 6233955d011SMarcel Moolenaar case '*': 624b0c40a00SSimon J. Gerraty wild = true; 6253955d011SMarcel Moolenaar break; 6263955d011SMarcel Moolenaar default: 6273955d011SMarcel Moolenaar break; 6283955d011SMarcel Moolenaar } 6293955d011SMarcel Moolenaar } 6302c3632d1SSimon J. Gerraty return wild && brackets == 0 && braces == 0; 6313955d011SMarcel Moolenaar } 6323955d011SMarcel Moolenaar 63306b9b3e0SSimon J. Gerraty /* 634d5e0a182SSimon J. Gerraty * See if any files as seen from 'dir' match 'pattern', and add their names 635d5e0a182SSimon J. Gerraty * to 'expansions' if they do. 636956e45f6SSimon J. Gerraty * 637d5e0a182SSimon J. Gerraty * Wildcards are only expanded in the final path component, but not in 638d5e0a182SSimon J. Gerraty * directories like src/lib*c/file*.c. To expand these wildcards, 639dba7b0efSSimon J. Gerraty * delegate the work to the shell, using the '!=' variable assignment 640dba7b0efSSimon J. Gerraty * operator, the ':sh' variable modifier or the ':!...!' variable modifier, 641dba7b0efSSimon J. Gerraty * such as in ${:!echo src/lib*c/file*.c!}. 6423955d011SMarcel Moolenaar */ 6432c3632d1SSimon J. Gerraty static void 644956e45f6SSimon J. Gerraty DirMatchFiles(const char *pattern, CachedDir *dir, StringList *expansions) 6453955d011SMarcel Moolenaar { 646956e45f6SSimon J. Gerraty const char *dirName = dir->name; 647b0c40a00SSimon J. Gerraty bool isDot = dirName[0] == '.' && dirName[1] == '\0'; 648956e45f6SSimon J. Gerraty HashIter hi; 6493955d011SMarcel Moolenaar 65006b9b3e0SSimon J. Gerraty /* 65106b9b3e0SSimon J. Gerraty * XXX: Iterating over all hash entries is inefficient. If the 65206b9b3e0SSimon J. Gerraty * pattern is a plain string without any wildcards, a direct lookup 65306b9b3e0SSimon J. Gerraty * is faster. 65406b9b3e0SSimon J. Gerraty */ 655e2eeea75SSimon J. Gerraty 65606b9b3e0SSimon J. Gerraty HashIter_InitSet(&hi, &dir->files); 6578d5c8e21SSimon J. Gerraty while (HashIter_Next(&hi)) { 658956e45f6SSimon J. Gerraty const char *base = hi.entry->key; 659148ee845SSimon J. Gerraty StrMatchResult res = Str_Match(base, pattern); 660148ee845SSimon J. Gerraty /* TODO: handle errors from res.error */ 6613955d011SMarcel Moolenaar 662148ee845SSimon J. Gerraty if (!res.matched) 663956e45f6SSimon J. Gerraty continue; 664956e45f6SSimon J. Gerraty 6653955d011SMarcel Moolenaar /* 66606b9b3e0SSimon J. Gerraty * Follow the UNIX convention that dot files are only found 66706b9b3e0SSimon J. Gerraty * if the pattern begins with a dot. The pattern '.*' does 66806b9b3e0SSimon J. Gerraty * not match '.' or '..' since these are not included in the 66906b9b3e0SSimon J. Gerraty * directory cache. 670956e45f6SSimon J. Gerraty * 67106b9b3e0SSimon J. Gerraty * This means that the pattern '[a-z.]*' does not find 672dba7b0efSSimon J. Gerraty * '.file', which is consistent with NetBSD sh, NetBSD ksh, 673dba7b0efSSimon J. Gerraty * bash, dash, csh and probably many other shells as well. 6743955d011SMarcel Moolenaar */ 675956e45f6SSimon J. Gerraty if (base[0] == '.' && pattern[0] != '.') 676956e45f6SSimon J. Gerraty continue; 677956e45f6SSimon J. Gerraty 6783955d011SMarcel Moolenaar { 679956e45f6SSimon J. Gerraty char *fullName = isDot 680956e45f6SSimon J. Gerraty ? bmake_strdup(base) 681956e45f6SSimon J. Gerraty : str_concat3(dirName, "/", base); 682956e45f6SSimon J. Gerraty Lst_Append(expansions, fullName); 6833955d011SMarcel Moolenaar } 6843955d011SMarcel Moolenaar } 6852c3632d1SSimon J. Gerraty } 6862c3632d1SSimon J. Gerraty 687d5e0a182SSimon J. Gerraty /* Find the next closing brace in 'p', taking nested braces into account. */ 6882c3632d1SSimon J. Gerraty static const char * 6892c3632d1SSimon J. Gerraty closing_brace(const char *p) 6902c3632d1SSimon J. Gerraty { 691d5e0a182SSimon J. Gerraty int depth = 0; 6922c3632d1SSimon J. Gerraty while (*p != '\0') { 693d5e0a182SSimon J. Gerraty if (*p == '}' && depth == 0) 6942c3632d1SSimon J. Gerraty break; 6952c3632d1SSimon J. Gerraty if (*p == '{') 696d5e0a182SSimon J. Gerraty depth++; 6972c3632d1SSimon J. Gerraty if (*p == '}') 698d5e0a182SSimon J. Gerraty depth--; 6992c3632d1SSimon J. Gerraty p++; 7002c3632d1SSimon J. Gerraty } 7012c3632d1SSimon J. Gerraty return p; 7022c3632d1SSimon J. Gerraty } 7032c3632d1SSimon J. Gerraty 70406b9b3e0SSimon J. Gerraty /* 70506b9b3e0SSimon J. Gerraty * Find the next closing brace or comma in the string, taking nested braces 70606b9b3e0SSimon J. Gerraty * into account. 70706b9b3e0SSimon J. Gerraty */ 7082c3632d1SSimon J. Gerraty static const char * 7092c3632d1SSimon J. Gerraty separator_comma(const char *p) 7102c3632d1SSimon J. Gerraty { 711d5e0a182SSimon J. Gerraty int depth = 0; 7122c3632d1SSimon J. Gerraty while (*p != '\0') { 713d5e0a182SSimon J. Gerraty if ((*p == '}' || *p == ',') && depth == 0) 7142c3632d1SSimon J. Gerraty break; 7152c3632d1SSimon J. Gerraty if (*p == '{') 716d5e0a182SSimon J. Gerraty depth++; 7172c3632d1SSimon J. Gerraty if (*p == '}') 718d5e0a182SSimon J. Gerraty depth--; 7192c3632d1SSimon J. Gerraty p++; 7202c3632d1SSimon J. Gerraty } 7212c3632d1SSimon J. Gerraty return p; 7222c3632d1SSimon J. Gerraty } 7232c3632d1SSimon J. Gerraty 724b0c40a00SSimon J. Gerraty static bool 7252c3632d1SSimon J. Gerraty contains_wildcard(const char *p) 7262c3632d1SSimon J. Gerraty { 7272c3632d1SSimon J. Gerraty for (; *p != '\0'; p++) { 7282c3632d1SSimon J. Gerraty switch (*p) { 7292c3632d1SSimon J. Gerraty case '*': 7302c3632d1SSimon J. Gerraty case '?': 7312c3632d1SSimon J. Gerraty case '{': 7322c3632d1SSimon J. Gerraty case '[': 733b0c40a00SSimon J. Gerraty return true; 7342c3632d1SSimon J. Gerraty } 7352c3632d1SSimon J. Gerraty } 736b0c40a00SSimon J. Gerraty return false; 7372c3632d1SSimon J. Gerraty } 7382c3632d1SSimon J. Gerraty 7392c3632d1SSimon J. Gerraty static char * 7402c3632d1SSimon J. Gerraty concat3(const char *a, size_t a_len, const char *b, size_t b_len, 7412c3632d1SSimon J. Gerraty const char *c, size_t c_len) 7422c3632d1SSimon J. Gerraty { 7432c3632d1SSimon J. Gerraty size_t s_len = a_len + b_len + c_len; 7442c3632d1SSimon J. Gerraty char *s = bmake_malloc(s_len + 1); 7452c3632d1SSimon J. Gerraty memcpy(s, a, a_len); 7462c3632d1SSimon J. Gerraty memcpy(s + a_len, b, b_len); 7472c3632d1SSimon J. Gerraty memcpy(s + a_len + b_len, c, c_len); 7482c3632d1SSimon J. Gerraty s[s_len] = '\0'; 7492c3632d1SSimon J. Gerraty return s; 7503955d011SMarcel Moolenaar } 7513955d011SMarcel Moolenaar 75206b9b3e0SSimon J. Gerraty /* 75306b9b3e0SSimon J. Gerraty * Expand curly braces like the C shell. Brace expansion by itself is purely 754956e45f6SSimon J. Gerraty * textual, the expansions are not looked up in the file system. But if an 755956e45f6SSimon J. Gerraty * expanded word contains wildcard characters, it is expanded further, 756956e45f6SSimon J. Gerraty * matching only the actually existing files. 757956e45f6SSimon J. Gerraty * 758956e45f6SSimon J. Gerraty * Example: "{a{b,c}}" expands to "ab" and "ac". 759956e45f6SSimon J. Gerraty * Example: "{a}" expands to "a". 760956e45f6SSimon J. Gerraty * Example: "{a,*.c}" expands to "a" and all "*.c" files that exist. 7613955d011SMarcel Moolenaar * 7623955d011SMarcel Moolenaar * Input: 7633955d011SMarcel Moolenaar * word Entire word to expand 7643955d011SMarcel Moolenaar * brace First curly brace in it 7653955d011SMarcel Moolenaar * path Search path to use 7663955d011SMarcel Moolenaar * expansions Place to store the expansions 7673955d011SMarcel Moolenaar */ 7683955d011SMarcel Moolenaar static void 769956e45f6SSimon J. Gerraty DirExpandCurly(const char *word, const char *brace, SearchPath *path, 770956e45f6SSimon J. Gerraty StringList *expansions) 7713955d011SMarcel Moolenaar { 7722c3632d1SSimon J. Gerraty const char *prefix, *middle, *piece, *middle_end, *suffix; 7732c3632d1SSimon J. Gerraty size_t prefix_len, suffix_len; 7743955d011SMarcel Moolenaar 775d5e0a182SSimon J. Gerraty /* Split the word into prefix, '{', middle, '}' and suffix. */ 7763955d011SMarcel Moolenaar 7772c3632d1SSimon J. Gerraty middle = brace + 1; 7782c3632d1SSimon J. Gerraty middle_end = closing_brace(middle); 7792c3632d1SSimon J. Gerraty if (*middle_end == '\0') { 7802c3632d1SSimon J. Gerraty Error("Unterminated {} clause \"%s\"", middle); 7813955d011SMarcel Moolenaar return; 7823955d011SMarcel Moolenaar } 7833955d011SMarcel Moolenaar 7842c3632d1SSimon J. Gerraty prefix = word; 7852c3632d1SSimon J. Gerraty prefix_len = (size_t)(brace - prefix); 7862c3632d1SSimon J. Gerraty suffix = middle_end + 1; 7872c3632d1SSimon J. Gerraty suffix_len = strlen(suffix); 7883955d011SMarcel Moolenaar 7892c3632d1SSimon J. Gerraty /* Split the middle into pieces, separated by commas. */ 7902c3632d1SSimon J. Gerraty 7912c3632d1SSimon J. Gerraty piece = middle; 7922c3632d1SSimon J. Gerraty while (piece < middle_end + 1) { 7932c3632d1SSimon J. Gerraty const char *piece_end = separator_comma(piece); 7942c3632d1SSimon J. Gerraty size_t piece_len = (size_t)(piece_end - piece); 7952c3632d1SSimon J. Gerraty 7962c3632d1SSimon J. Gerraty char *file = concat3(prefix, prefix_len, piece, piece_len, 7972c3632d1SSimon J. Gerraty suffix, suffix_len); 7982c3632d1SSimon J. Gerraty 7992c3632d1SSimon J. Gerraty if (contains_wildcard(file)) { 800dba7b0efSSimon J. Gerraty SearchPath_Expand(path, file, expansions); 8013955d011SMarcel Moolenaar free(file); 8022c3632d1SSimon J. Gerraty } else { 8032c3632d1SSimon J. Gerraty Lst_Append(expansions, file); 8043955d011SMarcel Moolenaar } 8052c3632d1SSimon J. Gerraty 80606b9b3e0SSimon J. Gerraty /* skip over the comma or closing brace */ 80706b9b3e0SSimon J. Gerraty piece = piece_end + 1; 8083955d011SMarcel Moolenaar } 8093955d011SMarcel Moolenaar } 8103955d011SMarcel Moolenaar 8113955d011SMarcel Moolenaar 812d5e0a182SSimon J. Gerraty /* Expand 'pattern' in each of the directories from 'path'. */ 8133955d011SMarcel Moolenaar static void 814148ee845SSimon J. Gerraty DirExpandPath(const char *pattern, SearchPath *path, StringList *expansions) 8153955d011SMarcel Moolenaar { 816d5e0a182SSimon J. Gerraty CachedDirListNode *ln; 817dba7b0efSSimon J. Gerraty for (ln = path->dirs.first; ln != NULL; ln = ln->next) { 818956e45f6SSimon J. Gerraty CachedDir *dir = ln->datum; 819148ee845SSimon J. Gerraty DirMatchFiles(pattern, dir, expansions); 8203955d011SMarcel Moolenaar } 8213955d011SMarcel Moolenaar } 8223955d011SMarcel Moolenaar 823956e45f6SSimon J. Gerraty static void 824956e45f6SSimon J. Gerraty PrintExpansions(StringList *expansions) 8253955d011SMarcel Moolenaar { 826956e45f6SSimon J. Gerraty const char *sep = ""; 827956e45f6SSimon J. Gerraty StringListNode *ln; 828956e45f6SSimon J. Gerraty for (ln = expansions->first; ln != NULL; ln = ln->next) { 829956e45f6SSimon J. Gerraty const char *word = ln->datum; 830956e45f6SSimon J. Gerraty debug_printf("%s%s", sep, word); 831956e45f6SSimon J. Gerraty sep = " "; 832956e45f6SSimon J. Gerraty } 833956e45f6SSimon J. Gerraty debug_printf("\n"); 8343955d011SMarcel Moolenaar } 8353955d011SMarcel Moolenaar 83606b9b3e0SSimon J. Gerraty /* 83706b9b3e0SSimon J. Gerraty * The wildcard isn't in the first component. 83806b9b3e0SSimon J. Gerraty * Find all the components up to the one with the wildcard. 83906b9b3e0SSimon J. Gerraty */ 840dba7b0efSSimon J. Gerraty static void 841dba7b0efSSimon J. Gerraty SearchPath_ExpandMiddle(SearchPath *path, const char *pattern, 842dba7b0efSSimon J. Gerraty const char *wildcardComponent, StringList *expansions) 843dba7b0efSSimon J. Gerraty { 844dba7b0efSSimon J. Gerraty char *prefix, *dirpath, *end; 845dba7b0efSSimon J. Gerraty SearchPath *partPath; 846dba7b0efSSimon J. Gerraty 847dba7b0efSSimon J. Gerraty prefix = bmake_strsedup(pattern, wildcardComponent + 1); 84806b9b3e0SSimon J. Gerraty /* 84906b9b3e0SSimon J. Gerraty * XXX: Only the first match of the prefix in the path is 85006b9b3e0SSimon J. Gerraty * taken, any others are ignored. The expectation may be 85106b9b3e0SSimon J. Gerraty * that the pattern is expanded in the whole path. 8523955d011SMarcel Moolenaar */ 853dba7b0efSSimon J. Gerraty dirpath = Dir_FindFile(prefix, path); 854956e45f6SSimon J. Gerraty free(prefix); 85506b9b3e0SSimon J. Gerraty 8563955d011SMarcel Moolenaar /* 8573955d011SMarcel Moolenaar * dirpath is null if can't find the leading component 858dba7b0efSSimon J. Gerraty * 859dba7b0efSSimon J. Gerraty * XXX: Dir_FindFile won't find internal components. i.e. if the 860dba7b0efSSimon J. Gerraty * path contains ../Etc/Object and we're looking for Etc, it won't 861dba7b0efSSimon J. Gerraty * be found. Ah well. Probably not important. 862dba7b0efSSimon J. Gerraty * 863d5e0a182SSimon J. Gerraty * TODO: Check whether the above comment is still true. 8643955d011SMarcel Moolenaar */ 865dba7b0efSSimon J. Gerraty if (dirpath == NULL) 866dba7b0efSSimon J. Gerraty return; 8673955d011SMarcel Moolenaar 868dba7b0efSSimon J. Gerraty end = &dirpath[strlen(dirpath) - 1]; 86906b9b3e0SSimon J. Gerraty /* XXX: What about multiple trailing slashes? */ 87006b9b3e0SSimon J. Gerraty if (*end == '/') 87106b9b3e0SSimon J. Gerraty *end = '\0'; 87206b9b3e0SSimon J. Gerraty 87306b9b3e0SSimon J. Gerraty partPath = SearchPath_New(); 874dba7b0efSSimon J. Gerraty (void)SearchPath_Add(partPath, dirpath); 875dba7b0efSSimon J. Gerraty DirExpandPath(wildcardComponent + 1, partPath, expansions); 87606b9b3e0SSimon J. Gerraty SearchPath_Free(partPath); 8778d5c8e21SSimon J. Gerraty free(dirpath); 8783955d011SMarcel Moolenaar } 879dba7b0efSSimon J. Gerraty 880dba7b0efSSimon J. Gerraty /* 881dba7b0efSSimon J. Gerraty * Expand the given pattern into a list of existing filenames by globbing it, 882dba7b0efSSimon J. Gerraty * looking in each directory from the search path. 883dba7b0efSSimon J. Gerraty * 884dba7b0efSSimon J. Gerraty * Input: 885dba7b0efSSimon J. Gerraty * path the directories in which to find the files 886dba7b0efSSimon J. Gerraty * pattern the pattern to expand 887dba7b0efSSimon J. Gerraty * expansions the list on which to place the results 888dba7b0efSSimon J. Gerraty */ 889dba7b0efSSimon J. Gerraty void 890dba7b0efSSimon J. Gerraty SearchPath_Expand(SearchPath *path, const char *pattern, StringList *expansions) 891dba7b0efSSimon J. Gerraty { 892dba7b0efSSimon J. Gerraty const char *brace, *slash, *wildcard, *wildcardComponent; 893dba7b0efSSimon J. Gerraty 894dba7b0efSSimon J. Gerraty assert(path != NULL); 895dba7b0efSSimon J. Gerraty assert(expansions != NULL); 896dba7b0efSSimon J. Gerraty 897dba7b0efSSimon J. Gerraty DEBUG1(DIR, "Expanding \"%s\"... ", pattern); 898dba7b0efSSimon J. Gerraty 899dba7b0efSSimon J. Gerraty brace = strchr(pattern, '{'); 900dba7b0efSSimon J. Gerraty if (brace != NULL) { 901dba7b0efSSimon J. Gerraty DirExpandCurly(pattern, brace, path, expansions); 902dba7b0efSSimon J. Gerraty goto done; 903dba7b0efSSimon J. Gerraty } 904dba7b0efSSimon J. Gerraty 905dba7b0efSSimon J. Gerraty slash = strchr(pattern, '/'); 906dba7b0efSSimon J. Gerraty if (slash == NULL) { 907dba7b0efSSimon J. Gerraty DirMatchFiles(pattern, dot, expansions); 908dba7b0efSSimon J. Gerraty DirExpandPath(pattern, path, expansions); 909dba7b0efSSimon J. Gerraty goto done; 910dba7b0efSSimon J. Gerraty } 911dba7b0efSSimon J. Gerraty 912dba7b0efSSimon J. Gerraty /* At this point, the pattern has a directory component. */ 913dba7b0efSSimon J. Gerraty 914dba7b0efSSimon J. Gerraty /* Find the first wildcard in the pattern. */ 915dba7b0efSSimon J. Gerraty for (wildcard = pattern; *wildcard != '\0'; wildcard++) 916dba7b0efSSimon J. Gerraty if (*wildcard == '?' || *wildcard == '[' || *wildcard == '*') 917dba7b0efSSimon J. Gerraty break; 918dba7b0efSSimon J. Gerraty 919dba7b0efSSimon J. Gerraty if (*wildcard == '\0') { 920dba7b0efSSimon J. Gerraty /* 921dba7b0efSSimon J. Gerraty * No directory component and no wildcard at all -- this 922dba7b0efSSimon J. Gerraty * should never happen as in such a simple case there is no 923dba7b0efSSimon J. Gerraty * need to expand anything. 924dba7b0efSSimon J. Gerraty */ 925dba7b0efSSimon J. Gerraty DirExpandPath(pattern, path, expansions); 926dba7b0efSSimon J. Gerraty goto done; 927dba7b0efSSimon J. Gerraty } 928dba7b0efSSimon J. Gerraty 929dba7b0efSSimon J. Gerraty /* Back up to the start of the component containing the wildcard. */ 930dba7b0efSSimon J. Gerraty /* XXX: This handles '///' and '/' differently. */ 931dba7b0efSSimon J. Gerraty wildcardComponent = wildcard; 932dba7b0efSSimon J. Gerraty while (wildcardComponent > pattern && *wildcardComponent != '/') 933dba7b0efSSimon J. Gerraty wildcardComponent--; 934dba7b0efSSimon J. Gerraty 935dba7b0efSSimon J. Gerraty if (wildcardComponent == pattern) { 936dba7b0efSSimon J. Gerraty /* The first component contains the wildcard. */ 937dba7b0efSSimon J. Gerraty /* Start the search from the local directory */ 938dba7b0efSSimon J. Gerraty DirExpandPath(pattern, path, expansions); 939dba7b0efSSimon J. Gerraty } else { 940dba7b0efSSimon J. Gerraty SearchPath_ExpandMiddle(path, pattern, wildcardComponent, 941dba7b0efSSimon J. Gerraty expansions); 9423955d011SMarcel Moolenaar } 94306b9b3e0SSimon J. Gerraty 94406b9b3e0SSimon J. Gerraty done: 945956e45f6SSimon J. Gerraty if (DEBUG(DIR)) 946956e45f6SSimon J. Gerraty PrintExpansions(expansions); 9473955d011SMarcel Moolenaar } 9483955d011SMarcel Moolenaar 94906b9b3e0SSimon J. Gerraty /* 950d5e0a182SSimon J. Gerraty * Find if 'base' exists in 'dir'. 95106b9b3e0SSimon J. Gerraty * Return the freshly allocated path to the file, or NULL. 95206b9b3e0SSimon J. Gerraty */ 9533955d011SMarcel Moolenaar static char * 954956e45f6SSimon J. Gerraty DirLookup(CachedDir *dir, const char *base) 9553955d011SMarcel Moolenaar { 956d5e0a182SSimon J. Gerraty char *file; 9573955d011SMarcel Moolenaar 95806b9b3e0SSimon J. Gerraty DEBUG1(DIR, " %s ...\n", dir->name); 9593955d011SMarcel Moolenaar 96006b9b3e0SSimon J. Gerraty if (!HashSet_Contains(&dir->files, base)) 9613955d011SMarcel Moolenaar return NULL; 9623955d011SMarcel Moolenaar 963956e45f6SSimon J. Gerraty file = str_concat3(dir->name, "/", base); 96406b9b3e0SSimon J. Gerraty DEBUG1(DIR, " returning %s\n", file); 965956e45f6SSimon J. Gerraty dir->hits++; 966956e45f6SSimon J. Gerraty hits++; 9673955d011SMarcel Moolenaar return file; 9683955d011SMarcel Moolenaar } 9693955d011SMarcel Moolenaar 9703955d011SMarcel Moolenaar 97106b9b3e0SSimon J. Gerraty /* 972d5e0a182SSimon J. Gerraty * Find if 'name' exists in 'dir'. 97306b9b3e0SSimon J. Gerraty * Return the freshly allocated path to the file, or NULL. 97406b9b3e0SSimon J. Gerraty */ 9753955d011SMarcel Moolenaar static char * 976956e45f6SSimon J. Gerraty DirLookupSubdir(CachedDir *dir, const char *name) 9773955d011SMarcel Moolenaar { 978e2eeea75SSimon J. Gerraty struct cached_stat cst; 9799f45a3c8SSimon J. Gerraty char *file = dir == dot 9809f45a3c8SSimon J. Gerraty ? bmake_strdup(name) 981956e45f6SSimon J. Gerraty : str_concat3(dir->name, "/", name); 9823955d011SMarcel Moolenaar 98306b9b3e0SSimon J. Gerraty DEBUG1(DIR, "checking %s ...\n", file); 9843955d011SMarcel Moolenaar 985e2eeea75SSimon J. Gerraty if (cached_stat(file, &cst) == 0) { 986956e45f6SSimon J. Gerraty nearmisses++; 9873841c287SSimon J. Gerraty return file; 9883955d011SMarcel Moolenaar } 9893955d011SMarcel Moolenaar free(file); 9903955d011SMarcel Moolenaar return NULL; 9913955d011SMarcel Moolenaar } 9923955d011SMarcel Moolenaar 99306b9b3e0SSimon J. Gerraty /* 994d5e0a182SSimon J. Gerraty * Find if 'name' (which has basename 'base') exists in 'dir'. 995d5e0a182SSimon J. Gerraty * Return the freshly allocated path to the file, an empty string, or NULL. 996d5e0a182SSimon J. Gerraty * Returning an empty string means that the search should be terminated. 9973955d011SMarcel Moolenaar */ 9983955d011SMarcel Moolenaar static char * 999d5e0a182SSimon J. Gerraty DirLookupAbs(CachedDir *dir, const char *name, const char *base) 10003955d011SMarcel Moolenaar { 1001956e45f6SSimon J. Gerraty const char *dnp; /* pointer into dir->name */ 1002956e45f6SSimon J. Gerraty const char *np; /* pointer into name */ 10033955d011SMarcel Moolenaar 100406b9b3e0SSimon J. Gerraty DEBUG1(DIR, " %s ...\n", dir->name); 10053955d011SMarcel Moolenaar 10063955d011SMarcel Moolenaar /* 10073955d011SMarcel Moolenaar * If the file has a leading path component and that component 10083955d011SMarcel Moolenaar * exactly matches the entire name of the current search 10093955d011SMarcel Moolenaar * directory, we can attempt another cache lookup. And if we don't 10103955d011SMarcel Moolenaar * have a hit, we can safely assume the file does not exist at all. 10113955d011SMarcel Moolenaar */ 101206b9b3e0SSimon J. Gerraty for (dnp = dir->name, np = name; 101306b9b3e0SSimon J. Gerraty *dnp != '\0' && *dnp == *np; dnp++, np++) 10143955d011SMarcel Moolenaar continue; 1015d5e0a182SSimon J. Gerraty if (*dnp != '\0' || np != base - 1) 10163955d011SMarcel Moolenaar return NULL; 10173955d011SMarcel Moolenaar 1018d5e0a182SSimon J. Gerraty if (!HashSet_Contains(&dir->files, base)) { 101906b9b3e0SSimon J. Gerraty DEBUG0(DIR, " must be here but isn't -- returning\n"); 1020956e45f6SSimon J. Gerraty return bmake_strdup(""); /* to terminate the search */ 10213955d011SMarcel Moolenaar } 10223955d011SMarcel Moolenaar 1023956e45f6SSimon J. Gerraty dir->hits++; 1024956e45f6SSimon J. Gerraty hits++; 102506b9b3e0SSimon J. Gerraty DEBUG1(DIR, " returning %s\n", name); 10263841c287SSimon J. Gerraty return bmake_strdup(name); 10273955d011SMarcel Moolenaar } 10283955d011SMarcel Moolenaar 102906b9b3e0SSimon J. Gerraty /* 1030954401e6SSimon J. Gerraty * Find the given file in "." or curdir. 103106b9b3e0SSimon J. Gerraty * Return the freshly allocated path to the file, or NULL. 103206b9b3e0SSimon J. Gerraty */ 10333955d011SMarcel Moolenaar static char * 1034956e45f6SSimon J. Gerraty DirFindDot(const char *name, const char *base) 10353955d011SMarcel Moolenaar { 10363955d011SMarcel Moolenaar 103706b9b3e0SSimon J. Gerraty if (HashSet_Contains(&dot->files, base)) { 103806b9b3e0SSimon J. Gerraty DEBUG0(DIR, " in '.'\n"); 1039956e45f6SSimon J. Gerraty hits++; 1040956e45f6SSimon J. Gerraty dot->hits++; 10413841c287SSimon J. Gerraty return bmake_strdup(name); 10423955d011SMarcel Moolenaar } 1043956e45f6SSimon J. Gerraty 104406b9b3e0SSimon J. Gerraty if (cur != NULL && HashSet_Contains(&cur->files, base)) { 104506b9b3e0SSimon J. Gerraty DEBUG1(DIR, " in ${.CURDIR} = %s\n", cur->name); 1046956e45f6SSimon J. Gerraty hits++; 1047956e45f6SSimon J. Gerraty cur->hits++; 1048956e45f6SSimon J. Gerraty return str_concat3(cur->name, "/", base); 10493955d011SMarcel Moolenaar } 10503955d011SMarcel Moolenaar 10513955d011SMarcel Moolenaar return NULL; 10523955d011SMarcel Moolenaar } 10533955d011SMarcel Moolenaar 1054b0c40a00SSimon J. Gerraty static bool 1055b0c40a00SSimon J. Gerraty FindFileRelative(SearchPath *path, bool seenDotLast, 1056dba7b0efSSimon J. Gerraty const char *name, char **out_file) 1057dba7b0efSSimon J. Gerraty { 1058d5e0a182SSimon J. Gerraty CachedDirListNode *ln; 1059b0c40a00SSimon J. Gerraty bool checkedDot = false; 1060dba7b0efSSimon J. Gerraty char *file; 1061dba7b0efSSimon J. Gerraty 1062dba7b0efSSimon J. Gerraty DEBUG0(DIR, " Trying subdirectories...\n"); 1063dba7b0efSSimon J. Gerraty 1064dba7b0efSSimon J. Gerraty if (!seenDotLast) { 1065dba7b0efSSimon J. Gerraty if (dot != NULL) { 1066b0c40a00SSimon J. Gerraty checkedDot = true; 1067dba7b0efSSimon J. Gerraty if ((file = DirLookupSubdir(dot, name)) != NULL) 1068d5e0a182SSimon J. Gerraty goto done; 1069dba7b0efSSimon J. Gerraty } 1070dba7b0efSSimon J. Gerraty if (cur != NULL && 1071dba7b0efSSimon J. Gerraty (file = DirLookupSubdir(cur, name)) != NULL) 1072d5e0a182SSimon J. Gerraty goto done; 1073dba7b0efSSimon J. Gerraty } 1074dba7b0efSSimon J. Gerraty 1075dba7b0efSSimon J. Gerraty for (ln = path->dirs.first; ln != NULL; ln = ln->next) { 1076dba7b0efSSimon J. Gerraty CachedDir *dir = ln->datum; 1077dba7b0efSSimon J. Gerraty if (dir == dotLast) 1078dba7b0efSSimon J. Gerraty continue; 1079dba7b0efSSimon J. Gerraty if (dir == dot) { 1080dba7b0efSSimon J. Gerraty if (checkedDot) 1081dba7b0efSSimon J. Gerraty continue; 1082b0c40a00SSimon J. Gerraty checkedDot = true; 1083dba7b0efSSimon J. Gerraty } 1084dba7b0efSSimon J. Gerraty if ((file = DirLookupSubdir(dir, name)) != NULL) 1085d5e0a182SSimon J. Gerraty goto done; 1086dba7b0efSSimon J. Gerraty } 1087dba7b0efSSimon J. Gerraty 1088dba7b0efSSimon J. Gerraty if (seenDotLast) { 1089dba7b0efSSimon J. Gerraty if (dot != NULL && !checkedDot) { 1090b0c40a00SSimon J. Gerraty checkedDot = true; 1091dba7b0efSSimon J. Gerraty if ((file = DirLookupSubdir(dot, name)) != NULL) 1092d5e0a182SSimon J. Gerraty goto done; 1093dba7b0efSSimon J. Gerraty } 1094dba7b0efSSimon J. Gerraty if (cur != NULL && 1095dba7b0efSSimon J. Gerraty (file = DirLookupSubdir(cur, name)) != NULL) 1096d5e0a182SSimon J. Gerraty goto done; 1097dba7b0efSSimon J. Gerraty } 1098dba7b0efSSimon J. Gerraty 1099dba7b0efSSimon J. Gerraty if (checkedDot) { 1100dba7b0efSSimon J. Gerraty /* 1101dba7b0efSSimon J. Gerraty * Already checked by the given name, since . was in 1102dba7b0efSSimon J. Gerraty * the path, so no point in proceeding. 1103dba7b0efSSimon J. Gerraty */ 1104dba7b0efSSimon J. Gerraty DEBUG0(DIR, " Checked . already, returning NULL\n"); 1105dba7b0efSSimon J. Gerraty file = NULL; 1106d5e0a182SSimon J. Gerraty goto done; 1107dba7b0efSSimon J. Gerraty } 1108dba7b0efSSimon J. Gerraty 1109b0c40a00SSimon J. Gerraty return false; 1110dba7b0efSSimon J. Gerraty 1111d5e0a182SSimon J. Gerraty done: 1112dba7b0efSSimon J. Gerraty *out_file = file; 1113b0c40a00SSimon J. Gerraty return true; 1114dba7b0efSSimon J. Gerraty } 1115dba7b0efSSimon J. Gerraty 1116b0c40a00SSimon J. Gerraty static bool 111712904384SSimon J. Gerraty FindFileAbsolute(SearchPath *path, bool seenDotLast, 111812904384SSimon J. Gerraty const char *name, const char *base, char **out_file) 1119dba7b0efSSimon J. Gerraty { 1120dba7b0efSSimon J. Gerraty char *file; 1121d5e0a182SSimon J. Gerraty CachedDirListNode *ln; 1122dba7b0efSSimon J. Gerraty 1123dba7b0efSSimon J. Gerraty DEBUG0(DIR, " Trying exact path matches...\n"); 1124dba7b0efSSimon J. Gerraty 1125dba7b0efSSimon J. Gerraty if (!seenDotLast && cur != NULL && 1126dba7b0efSSimon J. Gerraty ((file = DirLookupAbs(cur, name, base)) != NULL)) 1127dba7b0efSSimon J. Gerraty goto found; 1128dba7b0efSSimon J. Gerraty 1129dba7b0efSSimon J. Gerraty for (ln = path->dirs.first; ln != NULL; ln = ln->next) { 1130dba7b0efSSimon J. Gerraty CachedDir *dir = ln->datum; 1131dba7b0efSSimon J. Gerraty if (dir == dotLast) 1132dba7b0efSSimon J. Gerraty continue; 1133dba7b0efSSimon J. Gerraty if ((file = DirLookupAbs(dir, name, base)) != NULL) 1134dba7b0efSSimon J. Gerraty goto found; 1135dba7b0efSSimon J. Gerraty } 1136dba7b0efSSimon J. Gerraty 1137dba7b0efSSimon J. Gerraty if (seenDotLast && cur != NULL && 1138dba7b0efSSimon J. Gerraty ((file = DirLookupAbs(cur, name, base)) != NULL)) 1139dba7b0efSSimon J. Gerraty goto found; 1140dba7b0efSSimon J. Gerraty 1141b0c40a00SSimon J. Gerraty return false; 1142dba7b0efSSimon J. Gerraty 1143dba7b0efSSimon J. Gerraty found: 1144dba7b0efSSimon J. Gerraty if (file[0] == '\0') { 1145dba7b0efSSimon J. Gerraty free(file); 1146dba7b0efSSimon J. Gerraty file = NULL; 1147dba7b0efSSimon J. Gerraty } 1148dba7b0efSSimon J. Gerraty *out_file = file; 1149b0c40a00SSimon J. Gerraty return true; 1150dba7b0efSSimon J. Gerraty } 1151dba7b0efSSimon J. Gerraty 115206b9b3e0SSimon J. Gerraty /* 115306b9b3e0SSimon J. Gerraty * Find the file with the given name along the given search path. 11543955d011SMarcel Moolenaar * 1155956e45f6SSimon J. Gerraty * Input: 1156956e45f6SSimon J. Gerraty * name the file to find 1157956e45f6SSimon J. Gerraty * path the directories to search, or NULL 11589d3df31eSSimon J. Gerraty * isinclude if true, do not search .CURDIR at all 1159956e45f6SSimon J. Gerraty * 1160956e45f6SSimon J. Gerraty * Results: 1161956e45f6SSimon J. Gerraty * The freshly allocated path to the file, or NULL. 11623955d011SMarcel Moolenaar */ 11639d3df31eSSimon J. Gerraty static char * 11649d3df31eSSimon J. Gerraty FindFile(const char *name, SearchPath *path, bool isinclude) 11653955d011SMarcel Moolenaar { 11663955d011SMarcel Moolenaar char *file; /* the current filename to check */ 11679d3df31eSSimon J. Gerraty bool seenDotLast = isinclude; /* true if we should search dot last */ 1168d5e0a182SSimon J. Gerraty struct cached_stat cst; 11693955d011SMarcel Moolenaar const char *trailing_dot = "."; 117006b9b3e0SSimon J. Gerraty const char *base = str_basename(name); 11713955d011SMarcel Moolenaar 117206b9b3e0SSimon J. Gerraty DEBUG1(DIR, "Searching for %s ...", name); 11733955d011SMarcel Moolenaar 11742c3632d1SSimon J. Gerraty if (path == NULL) { 117506b9b3e0SSimon J. Gerraty DEBUG0(DIR, "couldn't open path, file not found\n"); 1176956e45f6SSimon J. Gerraty misses++; 11773955d011SMarcel Moolenaar return NULL; 11783955d011SMarcel Moolenaar } 11793955d011SMarcel Moolenaar 11809d3df31eSSimon J. Gerraty if (!seenDotLast && path->dirs.first != NULL) { 1181dba7b0efSSimon J. Gerraty CachedDir *dir = path->dirs.first->datum; 1182956e45f6SSimon J. Gerraty if (dir == dotLast) { 1183b0c40a00SSimon J. Gerraty seenDotLast = true; 118406b9b3e0SSimon J. Gerraty DEBUG0(DIR, "[dot last]..."); 11853955d011SMarcel Moolenaar } 11863955d011SMarcel Moolenaar } 118706b9b3e0SSimon J. Gerraty DEBUG0(DIR, "\n"); 11883955d011SMarcel Moolenaar 11893955d011SMarcel Moolenaar /* 11903955d011SMarcel Moolenaar * If there's no leading directory components or if the leading 11913955d011SMarcel Moolenaar * directory component is exactly `./', consult the cached contents 11923955d011SMarcel Moolenaar * of each of the directories on the search path. 11933955d011SMarcel Moolenaar */ 119406b9b3e0SSimon J. Gerraty if (base == name || (base - name == 2 && *name == '.')) { 1195d5e0a182SSimon J. Gerraty CachedDirListNode *ln; 119606b9b3e0SSimon J. Gerraty 11973955d011SMarcel Moolenaar /* 1198d5e0a182SSimon J. Gerraty * Look through all the directories on the path seeking one 119906b9b3e0SSimon J. Gerraty * which contains the final component of the given name. If 1200d5e0a182SSimon J. Gerraty * such a file is found, return its pathname. 1201d5e0a182SSimon J. Gerraty * If there is no such file, go on to phase two. 12023955d011SMarcel Moolenaar * 1203d5e0a182SSimon J. Gerraty * No matter what, always look for the file in the current 1204d5e0a182SSimon J. Gerraty * directory before anywhere else (unless the path contains 1205d5e0a182SSimon J. Gerraty * the magic '.DOTLAST', in which case search it last). 12063955d011SMarcel Moolenaar * This is so there are no conflicts between what the user 1207d5e0a182SSimon J. Gerraty * specifies (fish.c) and what make finds (./fish.c). 12083955d011SMarcel Moolenaar */ 120906b9b3e0SSimon J. Gerraty if (!seenDotLast && (file = DirFindDot(name, base)) != NULL) 12103955d011SMarcel Moolenaar return file; 12113955d011SMarcel Moolenaar 1212dba7b0efSSimon J. Gerraty for (ln = path->dirs.first; ln != NULL; ln = ln->next) { 1213956e45f6SSimon J. Gerraty CachedDir *dir = ln->datum; 1214956e45f6SSimon J. Gerraty if (dir == dotLast) 12153955d011SMarcel Moolenaar continue; 1216956e45f6SSimon J. Gerraty if ((file = DirLookup(dir, base)) != NULL) 12173955d011SMarcel Moolenaar return file; 12183955d011SMarcel Moolenaar } 12193955d011SMarcel Moolenaar 122006b9b3e0SSimon J. Gerraty if (seenDotLast && (file = DirFindDot(name, base)) != NULL) 12213955d011SMarcel Moolenaar return file; 12223955d011SMarcel Moolenaar } 12233955d011SMarcel Moolenaar 122406b9b3e0SSimon J. Gerraty if (base == name) { 122506b9b3e0SSimon J. Gerraty DEBUG0(DIR, " failed.\n"); 1226956e45f6SSimon J. Gerraty misses++; 12273955d011SMarcel Moolenaar return NULL; 12283955d011SMarcel Moolenaar } 12293955d011SMarcel Moolenaar 1230d5e0a182SSimon J. Gerraty if (*base == '\0') 1231d5e0a182SSimon J. Gerraty base = trailing_dot; /* we were given a trailing "/" */ 12323955d011SMarcel Moolenaar 12333955d011SMarcel Moolenaar if (name[0] != '/') { 1234dba7b0efSSimon J. Gerraty if (FindFileRelative(path, seenDotLast, name, &file)) 12353955d011SMarcel Moolenaar return file; 1236dba7b0efSSimon J. Gerraty } else { 1237dba7b0efSSimon J. Gerraty if (FindFileAbsolute(path, seenDotLast, name, base, &file)) 12383955d011SMarcel Moolenaar return file; 12393955d011SMarcel Moolenaar } 12403955d011SMarcel Moolenaar 12413955d011SMarcel Moolenaar /* 1242d5e0a182SSimon J. Gerraty * We cannot add the directory onto the search path because 12433955d011SMarcel Moolenaar * of this amusing case: 12443955d011SMarcel Moolenaar * $(INSTALLDIR)/$(FILE): $(FILE) 12453955d011SMarcel Moolenaar * 12463955d011SMarcel Moolenaar * $(FILE) exists in $(INSTALLDIR) but not in the current one. 12473955d011SMarcel Moolenaar * When searching for $(FILE), we will find it in $(INSTALLDIR) 12483955d011SMarcel Moolenaar * b/c we added it here. This is not good... 12493955d011SMarcel Moolenaar */ 125006b9b3e0SSimon J. Gerraty 125106b9b3e0SSimon J. Gerraty DEBUG1(DIR, " Looking for \"%s\" ...\n", name); 12523955d011SMarcel Moolenaar 1253956e45f6SSimon J. Gerraty bigmisses++; 1254d5e0a182SSimon J. Gerraty if (cached_stat(name, &cst) == 0) 12553841c287SSimon J. Gerraty return bmake_strdup(name); 12562eae894cSSimon J. Gerraty 125706b9b3e0SSimon J. Gerraty DEBUG0(DIR, " failed. Returning NULL\n"); 12583955d011SMarcel Moolenaar return NULL; 12593955d011SMarcel Moolenaar } 12603955d011SMarcel Moolenaar 12619d3df31eSSimon J. Gerraty /* 12629d3df31eSSimon J. Gerraty * Find the file with the given name along the given search path. 12639d3df31eSSimon J. Gerraty * 12649d3df31eSSimon J. Gerraty * Input: 12659d3df31eSSimon J. Gerraty * name the file to find 12669d3df31eSSimon J. Gerraty * path the directories to search, or NULL 12679d3df31eSSimon J. Gerraty * 12689d3df31eSSimon J. Gerraty * Results: 12699d3df31eSSimon J. Gerraty * The freshly allocated path to the file, or NULL. 12709d3df31eSSimon J. Gerraty */ 12719d3df31eSSimon J. Gerraty char * 12729d3df31eSSimon J. Gerraty Dir_FindFile(const char *name, SearchPath *path) 12739d3df31eSSimon J. Gerraty { 12749d3df31eSSimon J. Gerraty return FindFile(name, path, false); 12759d3df31eSSimon J. Gerraty } 12769d3df31eSSimon J. Gerraty 12779d3df31eSSimon J. Gerraty /* 12789d3df31eSSimon J. Gerraty * Find the include file with the given name along the given search path. 12799d3df31eSSimon J. Gerraty * 12809d3df31eSSimon J. Gerraty * Input: 12819d3df31eSSimon J. Gerraty * name the file to find 12829d3df31eSSimon J. Gerraty * path the directories to search, or NULL 12839d3df31eSSimon J. Gerraty * 12849d3df31eSSimon J. Gerraty * Results: 12859d3df31eSSimon J. Gerraty * The freshly allocated path to the file, or NULL. 12869d3df31eSSimon J. Gerraty */ 12879d3df31eSSimon J. Gerraty char * 12889d3df31eSSimon J. Gerraty Dir_FindInclude(const char *name, SearchPath *path) 12899d3df31eSSimon J. Gerraty { 12909d3df31eSSimon J. Gerraty return FindFile(name, path, true); 12919d3df31eSSimon J. Gerraty } 12929d3df31eSSimon J. Gerraty 12933955d011SMarcel Moolenaar 129406b9b3e0SSimon J. Gerraty /* 1295d5e0a182SSimon J. Gerraty * Search for 'needle' starting at the directory 'here' and then working our 1296d5e0a182SSimon J. Gerraty * way up towards the root directory. Return the allocated path, or NULL. 12973955d011SMarcel Moolenaar */ 1298956e45f6SSimon J. Gerraty char * 1299d5e0a182SSimon J. Gerraty Dir_FindHereOrAbove(const char *here, const char *needle) 13002c3632d1SSimon J. Gerraty { 1301e2eeea75SSimon J. Gerraty struct cached_stat cst; 1302956e45f6SSimon J. Gerraty char *dirbase, *dirbase_end; 1303956e45f6SSimon J. Gerraty char *try, *try_end; 13043955d011SMarcel Moolenaar 1305956e45f6SSimon J. Gerraty dirbase = bmake_strdup(here); 13062c3632d1SSimon J. Gerraty dirbase_end = dirbase + strlen(dirbase); 13073955d011SMarcel Moolenaar 1308956e45f6SSimon J. Gerraty for (;;) { 1309d5e0a182SSimon J. Gerraty try = str_concat3(dirbase, "/", needle); 1310e2eeea75SSimon J. Gerraty if (cached_stat(try, &cst) != -1) { 1311e2eeea75SSimon J. Gerraty if ((cst.cst_mode & S_IFMT) != S_IFDIR) { 1312d5e0a182SSimon J. Gerraty /* 1313d5e0a182SSimon J. Gerraty * Chop off the filename, to return a 1314d5e0a182SSimon J. Gerraty * directory. 1315d5e0a182SSimon J. Gerraty */ 13163955d011SMarcel Moolenaar try_end = try + strlen(try); 13173955d011SMarcel Moolenaar while (try_end > try && *try_end != '/') 13183955d011SMarcel Moolenaar try_end--; 13193955d011SMarcel Moolenaar if (try_end > try) 13202c3632d1SSimon J. Gerraty *try_end = '\0'; /* chop! */ 13213955d011SMarcel Moolenaar } 13223955d011SMarcel Moolenaar 1323956e45f6SSimon J. Gerraty free(dirbase); 1324956e45f6SSimon J. Gerraty return try; 13253955d011SMarcel Moolenaar } 1326956e45f6SSimon J. Gerraty free(try); 13273955d011SMarcel Moolenaar 13282c3632d1SSimon J. Gerraty if (dirbase_end == dirbase) 13293955d011SMarcel Moolenaar break; /* failed! */ 13303955d011SMarcel Moolenaar 1331d5e0a182SSimon J. Gerraty /* Truncate dirbase from the end to move up a dir. */ 13322c3632d1SSimon J. Gerraty while (dirbase_end > dirbase && *dirbase_end != '/') 13332c3632d1SSimon J. Gerraty dirbase_end--; 13342c3632d1SSimon J. Gerraty *dirbase_end = '\0'; /* chop! */ 1335956e45f6SSimon J. Gerraty } 13363955d011SMarcel Moolenaar 1337956e45f6SSimon J. Gerraty free(dirbase); 1338956e45f6SSimon J. Gerraty return NULL; 13393955d011SMarcel Moolenaar } 13403955d011SMarcel Moolenaar 134106b9b3e0SSimon J. Gerraty /* 134206b9b3e0SSimon J. Gerraty * This is an implied source, and it may have moved, 134306b9b3e0SSimon J. Gerraty * see if we can find it via the current .PATH 134406b9b3e0SSimon J. Gerraty */ 134506b9b3e0SSimon J. Gerraty static char * 134606b9b3e0SSimon J. Gerraty ResolveMovedDepends(GNode *gn) 134706b9b3e0SSimon J. Gerraty { 134806b9b3e0SSimon J. Gerraty char *fullName; 134906b9b3e0SSimon J. Gerraty 135006b9b3e0SSimon J. Gerraty const char *base = str_basename(gn->name); 135106b9b3e0SSimon J. Gerraty if (base == gn->name) 135206b9b3e0SSimon J. Gerraty return NULL; 135306b9b3e0SSimon J. Gerraty 135406b9b3e0SSimon J. Gerraty fullName = Dir_FindFile(base, Suff_FindPath(gn)); 135506b9b3e0SSimon J. Gerraty if (fullName == NULL) 135606b9b3e0SSimon J. Gerraty return NULL; 135706b9b3e0SSimon J. Gerraty 135806b9b3e0SSimon J. Gerraty /* 135906b9b3e0SSimon J. Gerraty * Put the found file in gn->path so that we give that to the compiler. 136006b9b3e0SSimon J. Gerraty */ 136106b9b3e0SSimon J. Gerraty /* 136206b9b3e0SSimon J. Gerraty * XXX: Better just reset gn->path to NULL; updating it is already done 136306b9b3e0SSimon J. Gerraty * by Dir_UpdateMTime. 136406b9b3e0SSimon J. Gerraty */ 136506b9b3e0SSimon J. Gerraty gn->path = bmake_strdup(fullName); 136606b9b3e0SSimon J. Gerraty if (!Job_RunTarget(".STALE", gn->fname)) 136706b9b3e0SSimon J. Gerraty fprintf(stdout, /* XXX: Why stdout? */ 13689f45a3c8SSimon J. Gerraty "%s: %s, %u: ignoring stale %s for %s, found %s\n", 136906b9b3e0SSimon J. Gerraty progname, gn->fname, gn->lineno, 137006b9b3e0SSimon J. Gerraty makeDependfile, gn->name, fullName); 137106b9b3e0SSimon J. Gerraty 137206b9b3e0SSimon J. Gerraty return fullName; 137306b9b3e0SSimon J. Gerraty } 137406b9b3e0SSimon J. Gerraty 137506b9b3e0SSimon J. Gerraty static char * 137606b9b3e0SSimon J. Gerraty ResolveFullName(GNode *gn) 137706b9b3e0SSimon J. Gerraty { 137806b9b3e0SSimon J. Gerraty char *fullName; 137906b9b3e0SSimon J. Gerraty 138006b9b3e0SSimon J. Gerraty fullName = gn->path; 138106b9b3e0SSimon J. Gerraty if (fullName == NULL && !(gn->type & OP_NOPATH)) { 138206b9b3e0SSimon J. Gerraty 138306b9b3e0SSimon J. Gerraty fullName = Dir_FindFile(gn->name, Suff_FindPath(gn)); 138406b9b3e0SSimon J. Gerraty 138512904384SSimon J. Gerraty if (fullName == NULL && gn->flags.fromDepend && 138606b9b3e0SSimon J. Gerraty !Lst_IsEmpty(&gn->implicitParents)) 138706b9b3e0SSimon J. Gerraty fullName = ResolveMovedDepends(gn); 138806b9b3e0SSimon J. Gerraty 138906b9b3e0SSimon J. Gerraty DEBUG2(DIR, "Found '%s' as '%s'\n", 139006b9b3e0SSimon J. Gerraty gn->name, fullName != NULL ? fullName : "(not found)"); 139106b9b3e0SSimon J. Gerraty } 139206b9b3e0SSimon J. Gerraty 139306b9b3e0SSimon J. Gerraty if (fullName == NULL) 139406b9b3e0SSimon J. Gerraty fullName = bmake_strdup(gn->name); 139506b9b3e0SSimon J. Gerraty 139606b9b3e0SSimon J. Gerraty /* XXX: Is every piece of memory freed as it should? */ 139706b9b3e0SSimon J. Gerraty 139806b9b3e0SSimon J. Gerraty return fullName; 139906b9b3e0SSimon J. Gerraty } 140006b9b3e0SSimon J. Gerraty 140106b9b3e0SSimon J. Gerraty /* 1402d5e0a182SSimon J. Gerraty * Search 'gn' along 'dirSearchPath' and store its modification time in 1403d5e0a182SSimon J. Gerraty * 'gn->mtime'. If no file is found, store 0 instead. 14043955d011SMarcel Moolenaar * 1405d5e0a182SSimon J. Gerraty * The found file is stored in 'gn->path', unless the node already had a path. 140606b9b3e0SSimon J. Gerraty */ 1407e2eeea75SSimon J. Gerraty void 140812904384SSimon J. Gerraty Dir_UpdateMTime(GNode *gn, bool forceRefresh) 14093955d011SMarcel Moolenaar { 1410e2eeea75SSimon J. Gerraty char *fullName; 1411e2eeea75SSimon J. Gerraty struct cached_stat cst; 14123955d011SMarcel Moolenaar 14133955d011SMarcel Moolenaar if (gn->type & OP_ARCHV) { 1414e2eeea75SSimon J. Gerraty Arch_UpdateMTime(gn); 1415e2eeea75SSimon J. Gerraty return; 1416e2eeea75SSimon J. Gerraty } 1417e2eeea75SSimon J. Gerraty 1418e2eeea75SSimon J. Gerraty if (gn->type & OP_PHONY) { 14193955d011SMarcel Moolenaar gn->mtime = 0; 1420e2eeea75SSimon J. Gerraty return; 1421e2eeea75SSimon J. Gerraty } 1422e2eeea75SSimon J. Gerraty 142306b9b3e0SSimon J. Gerraty fullName = ResolveFullName(gn); 14243955d011SMarcel Moolenaar 142512904384SSimon J. Gerraty if (cached_stats(fullName, &cst, false, forceRefresh) < 0) { 14263955d011SMarcel Moolenaar if (gn->type & OP_MEMBER) { 14273955d011SMarcel Moolenaar if (fullName != gn->path) 14283955d011SMarcel Moolenaar free(fullName); 1429e2eeea75SSimon J. Gerraty Arch_UpdateMemberMTime(gn); 1430e2eeea75SSimon J. Gerraty return; 14313955d011SMarcel Moolenaar } 1432e2eeea75SSimon J. Gerraty 1433e2eeea75SSimon J. Gerraty cst.cst_mtime = 0; 14343955d011SMarcel Moolenaar } 14353955d011SMarcel Moolenaar 1436956e45f6SSimon J. Gerraty if (fullName != NULL && gn->path == NULL) 14373955d011SMarcel Moolenaar gn->path = fullName; 143806b9b3e0SSimon J. Gerraty /* XXX: else free(fullName)? */ 14393955d011SMarcel Moolenaar 1440e2eeea75SSimon J. Gerraty gn->mtime = cst.cst_mtime; 14413955d011SMarcel Moolenaar } 14423955d011SMarcel Moolenaar 144306b9b3e0SSimon J. Gerraty /* 144406b9b3e0SSimon J. Gerraty * Read the directory and add it to the cache in openDirs. 144506b9b3e0SSimon J. Gerraty * If a path is given, add the directory to that path as well. 144606b9b3e0SSimon J. Gerraty */ 144706b9b3e0SSimon J. Gerraty static CachedDir * 144806b9b3e0SSimon J. Gerraty CacheNewDir(const char *name, SearchPath *path) 144906b9b3e0SSimon J. Gerraty { 145006b9b3e0SSimon J. Gerraty CachedDir *dir = NULL; 145106b9b3e0SSimon J. Gerraty DIR *d; 145206b9b3e0SSimon J. Gerraty struct dirent *dp; 145306b9b3e0SSimon J. Gerraty 145406b9b3e0SSimon J. Gerraty if ((d = opendir(name)) == NULL) { 145506b9b3e0SSimon J. Gerraty DEBUG1(DIR, "Caching %s ... not found\n", name); 145606b9b3e0SSimon J. Gerraty return dir; 145706b9b3e0SSimon J. Gerraty } 145806b9b3e0SSimon J. Gerraty 145906b9b3e0SSimon J. Gerraty DEBUG1(DIR, "Caching %s ...\n", name); 146006b9b3e0SSimon J. Gerraty 146106b9b3e0SSimon J. Gerraty dir = CachedDir_New(name); 146206b9b3e0SSimon J. Gerraty 146306b9b3e0SSimon J. Gerraty while ((dp = readdir(d)) != NULL) { 146406b9b3e0SSimon J. Gerraty 146506b9b3e0SSimon J. Gerraty #if defined(sun) && defined(d_ino) /* d_ino is a sunos4 #define for d_fileno */ 146606b9b3e0SSimon J. Gerraty /* 146706b9b3e0SSimon J. Gerraty * The sun directory library doesn't check for a 0 inode 146806b9b3e0SSimon J. Gerraty * (0-inode slots just take up space), so we have to do 146906b9b3e0SSimon J. Gerraty * it ourselves. 147006b9b3e0SSimon J. Gerraty */ 147106b9b3e0SSimon J. Gerraty if (dp->d_fileno == 0) 147206b9b3e0SSimon J. Gerraty continue; 147306b9b3e0SSimon J. Gerraty #endif /* sun && d_ino */ 147406b9b3e0SSimon J. Gerraty 147506b9b3e0SSimon J. Gerraty (void)HashSet_Add(&dir->files, dp->d_name); 147606b9b3e0SSimon J. Gerraty } 147706b9b3e0SSimon J. Gerraty (void)closedir(d); 147806b9b3e0SSimon J. Gerraty 147906b9b3e0SSimon J. Gerraty OpenDirs_Add(&openDirs, dir); 148006b9b3e0SSimon J. Gerraty if (path != NULL) 1481dba7b0efSSimon J. Gerraty Lst_Append(&path->dirs, CachedDir_Ref(dir)); 148206b9b3e0SSimon J. Gerraty 148306b9b3e0SSimon J. Gerraty DEBUG1(DIR, "Caching %s done\n", name); 148406b9b3e0SSimon J. Gerraty return dir; 148506b9b3e0SSimon J. Gerraty } 148606b9b3e0SSimon J. Gerraty 148706b9b3e0SSimon J. Gerraty /* 1488d5e0a182SSimon J. Gerraty * Read the list of filenames in the directory 'name' and store the result 1489d5e0a182SSimon J. Gerraty * in 'openDirs'. 1490956e45f6SSimon J. Gerraty * 1491d5e0a182SSimon J. Gerraty * If a search path is given, append the directory to that path. 14923955d011SMarcel Moolenaar * 14933955d011SMarcel Moolenaar * Input: 1494956e45f6SSimon J. Gerraty * path The path to which the directory should be 1495d5e0a182SSimon J. Gerraty * added, or NULL to only add the directory to openDirs. 1496956e45f6SSimon J. Gerraty * name The name of the directory to add. 1497956e45f6SSimon J. Gerraty * The name is not normalized in any way. 149806b9b3e0SSimon J. Gerraty * Output: 149906b9b3e0SSimon J. Gerraty * result If no path is given and the directory exists, the 150006b9b3e0SSimon J. Gerraty * returned CachedDir has a reference count of 0. It 150106b9b3e0SSimon J. Gerraty * must either be assigned to a variable using 150206b9b3e0SSimon J. Gerraty * CachedDir_Assign or be appended to a SearchPath using 150306b9b3e0SSimon J. Gerraty * Lst_Append and CachedDir_Ref. 15043955d011SMarcel Moolenaar */ 1505956e45f6SSimon J. Gerraty CachedDir * 1506dba7b0efSSimon J. Gerraty SearchPath_Add(SearchPath *path, const char *name) 15073955d011SMarcel Moolenaar { 15083955d011SMarcel Moolenaar 15092c3632d1SSimon J. Gerraty if (path != NULL && strcmp(name, ".DOTLAST") == 0) { 1510d5e0a182SSimon J. Gerraty CachedDirListNode *ln; 1511956e45f6SSimon J. Gerraty 1512e2eeea75SSimon J. Gerraty /* XXX: Linear search gets slow with thousands of entries. */ 1513dba7b0efSSimon J. Gerraty for (ln = path->dirs.first; ln != NULL; ln = ln->next) { 1514956e45f6SSimon J. Gerraty CachedDir *pathDir = ln->datum; 1515956e45f6SSimon J. Gerraty if (strcmp(pathDir->name, name) == 0) 1516956e45f6SSimon J. Gerraty return pathDir; 1517956e45f6SSimon J. Gerraty } 15182c3632d1SSimon J. Gerraty 1519dba7b0efSSimon J. Gerraty Lst_Prepend(&path->dirs, CachedDir_Ref(dotLast)); 15203955d011SMarcel Moolenaar } 15213955d011SMarcel Moolenaar 152206b9b3e0SSimon J. Gerraty if (path != NULL) { 152306b9b3e0SSimon J. Gerraty /* XXX: Why is OpenDirs only checked if path != NULL? */ 152406b9b3e0SSimon J. Gerraty CachedDir *dir = OpenDirs_Find(&openDirs, name); 1525956e45f6SSimon J. Gerraty if (dir != NULL) { 1526dba7b0efSSimon J. Gerraty if (Lst_FindDatum(&path->dirs, dir) == NULL) 1527dba7b0efSSimon J. Gerraty Lst_Append(&path->dirs, CachedDir_Ref(dir)); 1528956e45f6SSimon J. Gerraty return dir; 15293955d011SMarcel Moolenaar } 153006b9b3e0SSimon J. Gerraty } 15313955d011SMarcel Moolenaar 153206b9b3e0SSimon J. Gerraty return CacheNewDir(name, path); 153306b9b3e0SSimon J. Gerraty } 15342c3632d1SSimon J. Gerraty 15353955d011SMarcel Moolenaar /* 153606b9b3e0SSimon J. Gerraty * Return a copy of dirSearchPath, incrementing the reference counts for 153706b9b3e0SSimon J. Gerraty * the contained directories. 15383955d011SMarcel Moolenaar */ 1539956e45f6SSimon J. Gerraty SearchPath * 1540956e45f6SSimon J. Gerraty Dir_CopyDirSearchPath(void) 15413955d011SMarcel Moolenaar { 154206b9b3e0SSimon J. Gerraty SearchPath *path = SearchPath_New(); 1543d5e0a182SSimon J. Gerraty CachedDirListNode *ln; 1544dba7b0efSSimon J. Gerraty for (ln = dirSearchPath.dirs.first; ln != NULL; ln = ln->next) { 1545956e45f6SSimon J. Gerraty CachedDir *dir = ln->datum; 1546dba7b0efSSimon J. Gerraty Lst_Append(&path->dirs, CachedDir_Ref(dir)); 1547956e45f6SSimon J. Gerraty } 1548956e45f6SSimon J. Gerraty return path; 15493955d011SMarcel Moolenaar } 15503955d011SMarcel Moolenaar 155106b9b3e0SSimon J. Gerraty /* 155206b9b3e0SSimon J. Gerraty * Make a string by taking all the directories in the given search path and 155306b9b3e0SSimon J. Gerraty * preceding them by the given flag. Used by the suffix module to create 1554d5e0a182SSimon J. Gerraty * variables for compilers based on suffix search paths. Note that there is no 1555d5e0a182SSimon J. Gerraty * space between the given flag and each directory. 15563955d011SMarcel Moolenaar */ 15573955d011SMarcel Moolenaar char * 1558dba7b0efSSimon J. Gerraty SearchPath_ToFlags(SearchPath *path, const char *flag) 15593955d011SMarcel Moolenaar { 15602c3632d1SSimon J. Gerraty Buffer buf; 1561d5e0a182SSimon J. Gerraty CachedDirListNode *ln; 15623955d011SMarcel Moolenaar 1563e2eeea75SSimon J. Gerraty Buf_Init(&buf); 15643955d011SMarcel Moolenaar 15652c3632d1SSimon J. Gerraty if (path != NULL) { 1566dba7b0efSSimon J. Gerraty for (ln = path->dirs.first; ln != NULL; ln = ln->next) { 1567956e45f6SSimon J. Gerraty CachedDir *dir = ln->datum; 15682c3632d1SSimon J. Gerraty Buf_AddStr(&buf, " "); 15692c3632d1SSimon J. Gerraty Buf_AddStr(&buf, flag); 1570956e45f6SSimon J. Gerraty Buf_AddStr(&buf, dir->name); 15713955d011SMarcel Moolenaar } 15723955d011SMarcel Moolenaar } 15733955d011SMarcel Moolenaar 1574dba7b0efSSimon J. Gerraty return Buf_DoneData(&buf); 15753955d011SMarcel Moolenaar } 15763955d011SMarcel Moolenaar 157706b9b3e0SSimon J. Gerraty /* Free the search path and all directories mentioned in it. */ 157806b9b3e0SSimon J. Gerraty void 157906b9b3e0SSimon J. Gerraty SearchPath_Free(SearchPath *path) 158006b9b3e0SSimon J. Gerraty { 1581d5e0a182SSimon J. Gerraty CachedDirListNode *ln; 158206b9b3e0SSimon J. Gerraty 1583dba7b0efSSimon J. Gerraty for (ln = path->dirs.first; ln != NULL; ln = ln->next) { 158406b9b3e0SSimon J. Gerraty CachedDir *dir = ln->datum; 158506b9b3e0SSimon J. Gerraty CachedDir_Unref(dir); 158606b9b3e0SSimon J. Gerraty } 1587dba7b0efSSimon J. Gerraty Lst_Done(&path->dirs); 1588dba7b0efSSimon J. Gerraty free(path); 158906b9b3e0SSimon J. Gerraty } 159006b9b3e0SSimon J. Gerraty 159106b9b3e0SSimon J. Gerraty /* 159206b9b3e0SSimon J. Gerraty * Clear out all elements from the given search path. 159306b9b3e0SSimon J. Gerraty * The path is set to the empty list but is not destroyed. 15943955d011SMarcel Moolenaar */ 15953955d011SMarcel Moolenaar void 159606b9b3e0SSimon J. Gerraty SearchPath_Clear(SearchPath *path) 15973955d011SMarcel Moolenaar { 1598dba7b0efSSimon J. Gerraty while (!Lst_IsEmpty(&path->dirs)) { 1599dba7b0efSSimon J. Gerraty CachedDir *dir = Lst_Dequeue(&path->dirs); 160006b9b3e0SSimon J. Gerraty CachedDir_Unref(dir); 16013955d011SMarcel Moolenaar } 16023955d011SMarcel Moolenaar } 16033955d011SMarcel Moolenaar 16043955d011SMarcel Moolenaar 160506b9b3e0SSimon J. Gerraty /* 160606b9b3e0SSimon J. Gerraty * Concatenate two paths, adding the second to the end of the first, 160706b9b3e0SSimon J. Gerraty * skipping duplicates. 160806b9b3e0SSimon J. Gerraty */ 16093955d011SMarcel Moolenaar void 161006b9b3e0SSimon J. Gerraty SearchPath_AddAll(SearchPath *dst, SearchPath *src) 16113955d011SMarcel Moolenaar { 1612d5e0a182SSimon J. Gerraty CachedDirListNode *ln; 16133955d011SMarcel Moolenaar 1614dba7b0efSSimon J. Gerraty for (ln = src->dirs.first; ln != NULL; ln = ln->next) { 1615956e45f6SSimon J. Gerraty CachedDir *dir = ln->datum; 1616dba7b0efSSimon J. Gerraty if (Lst_FindDatum(&dst->dirs, dir) == NULL) 1617dba7b0efSSimon J. Gerraty Lst_Append(&dst->dirs, CachedDir_Ref(dir)); 16183955d011SMarcel Moolenaar } 16193955d011SMarcel Moolenaar } 16203955d011SMarcel Moolenaar 16212c3632d1SSimon J. Gerraty static int 16222c3632d1SSimon J. Gerraty percentage(int num, int den) 16232c3632d1SSimon J. Gerraty { 16242c3632d1SSimon J. Gerraty return den != 0 ? num * 100 / den : 0; 16252c3632d1SSimon J. Gerraty } 16262c3632d1SSimon J. Gerraty 16273955d011SMarcel Moolenaar void 16283955d011SMarcel Moolenaar Dir_PrintDirectories(void) 16293955d011SMarcel Moolenaar { 1630956e45f6SSimon J. Gerraty CachedDirListNode *ln; 16313955d011SMarcel Moolenaar 1632956e45f6SSimon J. Gerraty debug_printf("#*** Directory Cache:\n"); 163306b9b3e0SSimon J. Gerraty debug_printf( 163406b9b3e0SSimon J. Gerraty "# Stats: %d hits %d misses %d near misses %d losers (%d%%)\n", 16353955d011SMarcel Moolenaar hits, misses, nearmisses, bigmisses, 16362c3632d1SSimon J. Gerraty percentage(hits, hits + bigmisses + nearmisses)); 163706b9b3e0SSimon J. Gerraty debug_printf("# refs hits directory\n"); 16382c3632d1SSimon J. Gerraty 163906b9b3e0SSimon J. Gerraty for (ln = openDirs.list.first; ln != NULL; ln = ln->next) { 1640956e45f6SSimon J. Gerraty CachedDir *dir = ln->datum; 164106b9b3e0SSimon J. Gerraty debug_printf("# %4d %4d %s\n", 164206b9b3e0SSimon J. Gerraty dir->refCount, dir->hits, dir->name); 16433955d011SMarcel Moolenaar } 16443955d011SMarcel Moolenaar } 16453955d011SMarcel Moolenaar 16463955d011SMarcel Moolenaar void 1647dba7b0efSSimon J. Gerraty SearchPath_Print(const SearchPath *path) 16483955d011SMarcel Moolenaar { 1649d5e0a182SSimon J. Gerraty CachedDirListNode *ln; 165006b9b3e0SSimon J. Gerraty 1651dba7b0efSSimon J. Gerraty for (ln = path->dirs.first; ln != NULL; ln = ln->next) { 165206b9b3e0SSimon J. Gerraty const CachedDir *dir = ln->datum; 1653956e45f6SSimon J. Gerraty debug_printf("%s ", dir->name); 1654956e45f6SSimon J. Gerraty } 16553955d011SMarcel Moolenaar } 1656