1*12904384SSimon J. Gerraty /* $NetBSD: dir.c,v 1.275 2021/11/28 21:46:17 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 * 863955d011SMarcel Moolenaar * Dir_SetPATH Set ${.PATH} to reflect 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. 973955d011SMarcel Moolenaar * If it exists, the entire path is returned. 983955d011SMarcel Moolenaar * Otherwise NULL is returned. 993955d011SMarcel Moolenaar * 100956e45f6SSimon J. Gerraty * Dir_FindHereOrAbove 101956e45f6SSimon J. Gerraty * Search for a path in the current directory and 1023955d011SMarcel Moolenaar * then all the directories above it in turn until 1033955d011SMarcel Moolenaar * the path is found or we reach the root ("/"). 1043955d011SMarcel Moolenaar * 105e2eeea75SSimon J. Gerraty * Dir_UpdateMTime 106e2eeea75SSimon J. Gerraty * Update the modification time and path of a node with 107e2eeea75SSimon J. Gerraty * data from the file corresponding to the node. 1083955d011SMarcel Moolenaar * 109dba7b0efSSimon J. Gerraty * SearchPath_Add Add a directory to a search path. 1103955d011SMarcel Moolenaar * 11106b9b3e0SSimon J. Gerraty * SearchPath_ToFlags 11206b9b3e0SSimon J. Gerraty * Given a search path and a command flag, create 1133955d011SMarcel Moolenaar * a string with each of the directories in the path 1143955d011SMarcel Moolenaar * preceded by the command flag and all of them 1153955d011SMarcel Moolenaar * separated by a space. 1163955d011SMarcel Moolenaar * 1173955d011SMarcel Moolenaar * Dir_Destroy Destroy an element of a search path. Frees up all 1183955d011SMarcel Moolenaar * things that can be freed for the element as long 1193955d011SMarcel Moolenaar * as the element is no longer referenced by any other 1203955d011SMarcel Moolenaar * search path. 121956e45f6SSimon J. Gerraty * 12206b9b3e0SSimon J. Gerraty * SearchPath_Clear 12306b9b3e0SSimon J. Gerraty * Resets a search path to the empty list. 1243955d011SMarcel Moolenaar * 1253955d011SMarcel Moolenaar * For debugging: 126956e45f6SSimon J. Gerraty * Dir_PrintDirectories 127956e45f6SSimon J. Gerraty * Print stats about the directory cache. 1283955d011SMarcel Moolenaar */ 1293955d011SMarcel Moolenaar 1303955d011SMarcel Moolenaar #include <sys/types.h> 1313955d011SMarcel Moolenaar #include <sys/stat.h> 1323955d011SMarcel Moolenaar 1333955d011SMarcel Moolenaar #include <dirent.h> 1343955d011SMarcel Moolenaar #include <errno.h> 1353955d011SMarcel Moolenaar 1363955d011SMarcel Moolenaar #include "make.h" 1373955d011SMarcel Moolenaar #include "dir.h" 1381748de26SSimon J. Gerraty #include "job.h" 1393955d011SMarcel Moolenaar 140956e45f6SSimon J. Gerraty /* "@(#)dir.c 8.2 (Berkeley) 1/2/94" */ 141*12904384SSimon J. Gerraty MAKE_RCSID("$NetBSD: dir.c,v 1.275 2021/11/28 21:46:17 rillig Exp $"); 1422c3632d1SSimon J. Gerraty 14306b9b3e0SSimon J. Gerraty /* 14406b9b3e0SSimon J. Gerraty * A search path is a list of CachedDir structures. A CachedDir has in it the 145956e45f6SSimon J. Gerraty * name of the directory and the names of all the files in the directory. 146956e45f6SSimon J. Gerraty * This is used to cut down on the number of system calls necessary to find 147956e45f6SSimon J. Gerraty * implicit dependents and their like. Since these searches are made before 148956e45f6SSimon J. Gerraty * any actions are taken, we need not worry about the directory changing due 149956e45f6SSimon J. Gerraty * to creation commands. If this hampers the style of some makefiles, they 150956e45f6SSimon J. Gerraty * must be changed. 1513955d011SMarcel Moolenaar * 152956e45f6SSimon J. Gerraty * All previously-read directories are kept in openDirs, which is checked 153956e45f6SSimon J. Gerraty * first before a directory is opened. 1543955d011SMarcel Moolenaar * 155956e45f6SSimon J. Gerraty * The need for the caching of whole directories is brought about by the 156956e45f6SSimon J. Gerraty * multi-level transformation code in suff.c, which tends to search for far 157956e45f6SSimon J. Gerraty * more files than regular make does. In the initial implementation, the 158956e45f6SSimon J. Gerraty * amount of time spent performing "stat" calls was truly astronomical. 159956e45f6SSimon J. Gerraty * The problem with caching at the start is, of course, that pmake doesn't 160956e45f6SSimon J. Gerraty * then detect changes to these directories during the course of the make. 161956e45f6SSimon J. Gerraty * Three possibilities suggest themselves: 1623955d011SMarcel Moolenaar * 163956e45f6SSimon J. Gerraty * 1) just use stat to test for a file's existence. As mentioned above, 164956e45f6SSimon J. Gerraty * this is very inefficient due to the number of checks engendered by 165956e45f6SSimon J. Gerraty * the multi-level transformation code. 166956e45f6SSimon J. Gerraty * 167956e45f6SSimon J. Gerraty * 2) use readdir() and company to search the directories, keeping them 168956e45f6SSimon J. Gerraty * open between checks. I have tried this and while it didn't slow down 169956e45f6SSimon J. Gerraty * the process too much, it could severely affect the amount of 170956e45f6SSimon J. Gerraty * parallelism available as each directory open would take another file 171956e45f6SSimon J. Gerraty * descriptor out of play for handling I/O for another job. Given that 172e2eeea75SSimon J. Gerraty * it is only recently (as of 1993 or earlier) that UNIX OS's have taken 173e2eeea75SSimon J. Gerraty * to allowing more than 20 or 32 file descriptors for a process, this 174e2eeea75SSimon J. Gerraty * doesn't seem acceptable to me. 1753955d011SMarcel Moolenaar * 176956e45f6SSimon J. Gerraty * 3) record the mtime of the directory in the CachedDir structure and 177956e45f6SSimon J. Gerraty * verify the directory hasn't changed since the contents were cached. 178956e45f6SSimon J. Gerraty * This will catch the creation or deletion of files, but not the 179956e45f6SSimon J. Gerraty * updating of files. However, since it is the creation and deletion 180956e45f6SSimon J. Gerraty * that is the problem, this could be a good thing to do. Unfortunately, 181956e45f6SSimon J. Gerraty * if the directory (say ".") were fairly large and changed fairly 182956e45f6SSimon J. Gerraty * frequently, the constant reloading could seriously degrade 183956e45f6SSimon J. Gerraty * performance. It might be good in such cases to keep track of the 184956e45f6SSimon J. Gerraty * number of reloadings and if the number goes over a (small) limit, 185956e45f6SSimon J. Gerraty * resort to using stat in its place. 1863955d011SMarcel Moolenaar * 187956e45f6SSimon J. Gerraty * An additional thing to consider is that pmake is used primarily to create 188e2eeea75SSimon J. Gerraty * C programs and until recently (as of 1993 or earlier) pcc-based compilers 189e2eeea75SSimon J. Gerraty * refused to allow you to specify where the resulting object file should be 190e2eeea75SSimon J. Gerraty * placed. This forced all objects to be created in the current directory. 191e2eeea75SSimon J. Gerraty * This isn't meant as a full excuse, just an explanation of some of the 192e2eeea75SSimon J. Gerraty * reasons for the caching used here. 1933955d011SMarcel Moolenaar * 194956e45f6SSimon J. Gerraty * One more note: the location of a target's file is only performed on the 195956e45f6SSimon J. Gerraty * downward traversal of the graph and then only for terminal nodes in the 196956e45f6SSimon J. Gerraty * graph. This could be construed as wrong in some cases, but prevents 197956e45f6SSimon J. Gerraty * inadvertent modification of files when the "installed" directory for a 198956e45f6SSimon J. Gerraty * file is provided in the search path. 199956e45f6SSimon J. Gerraty * 200956e45f6SSimon J. Gerraty * Another data structure maintained by this module is an mtime cache used 201956e45f6SSimon J. Gerraty * when the searching of cached directories fails to find a file. In the past, 202956e45f6SSimon J. Gerraty * Dir_FindFile would simply perform an access() call in such a case to 203956e45f6SSimon J. Gerraty * determine if the file could be found using just the name given. When this 204956e45f6SSimon J. Gerraty * hit, however, all that was gained was the knowledge that the file existed. 205956e45f6SSimon J. Gerraty * Given that an access() is essentially a stat() without the copyout() call, 206956e45f6SSimon J. Gerraty * and that the same filesystem overhead would have to be incurred in 207956e45f6SSimon J. Gerraty * Dir_MTime, it made sense to replace the access() with a stat() and record 208e2eeea75SSimon J. Gerraty * the mtime in a cache for when Dir_UpdateMTime was actually called. 2093955d011SMarcel Moolenaar */ 2103955d011SMarcel Moolenaar 21106b9b3e0SSimon J. Gerraty 21206b9b3e0SSimon J. Gerraty /* A cache for the filenames in a directory. */ 21306b9b3e0SSimon J. Gerraty struct CachedDir { 21406b9b3e0SSimon J. Gerraty /* 21506b9b3e0SSimon J. Gerraty * Name of directory, either absolute or relative to the current 21606b9b3e0SSimon J. Gerraty * directory. The name is not normalized in any way, that is, "." 21706b9b3e0SSimon J. Gerraty * and "./." are different. 21806b9b3e0SSimon J. Gerraty * 21906b9b3e0SSimon J. Gerraty * Not sure what happens when .CURDIR is assigned a new value; see 220b0c40a00SSimon J. Gerraty * Parse_Var. 22106b9b3e0SSimon J. Gerraty */ 22206b9b3e0SSimon J. Gerraty char *name; 22306b9b3e0SSimon J. Gerraty 22406b9b3e0SSimon J. Gerraty /* 22506b9b3e0SSimon J. Gerraty * The number of SearchPaths that refer to this directory. 22606b9b3e0SSimon J. Gerraty * Plus the number of global variables that refer to this directory. 22706b9b3e0SSimon J. Gerraty * References from openDirs do not count though. 22806b9b3e0SSimon J. Gerraty */ 22906b9b3e0SSimon J. Gerraty int refCount; 23006b9b3e0SSimon J. Gerraty 23106b9b3e0SSimon J. Gerraty /* The number of times a file in this directory has been found. */ 23206b9b3e0SSimon J. Gerraty int hits; 23306b9b3e0SSimon J. Gerraty 23406b9b3e0SSimon J. Gerraty /* The names of the directory entries. */ 23506b9b3e0SSimon J. Gerraty HashSet files; 23606b9b3e0SSimon J. Gerraty }; 23706b9b3e0SSimon J. Gerraty 238956e45f6SSimon J. Gerraty typedef List CachedDirList; 239956e45f6SSimon J. Gerraty typedef ListNode CachedDirListNode; 2403955d011SMarcel Moolenaar 241956e45f6SSimon J. Gerraty typedef ListNode SearchPathNode; 242956e45f6SSimon J. Gerraty 243956e45f6SSimon J. Gerraty /* A list of cached directories, with fast lookup by directory name. */ 244956e45f6SSimon J. Gerraty typedef struct OpenDirs { 24506b9b3e0SSimon J. Gerraty CachedDirList list; 246956e45f6SSimon J. Gerraty HashTable /* of CachedDirListNode */ table; 247956e45f6SSimon J. Gerraty } OpenDirs; 248956e45f6SSimon J. Gerraty 24906b9b3e0SSimon J. Gerraty 250dba7b0efSSimon J. Gerraty SearchPath dirSearchPath = { LST_INIT }; /* main search path */ 25106b9b3e0SSimon J. Gerraty 25206b9b3e0SSimon J. Gerraty static OpenDirs openDirs; /* all cached directories */ 25306b9b3e0SSimon J. Gerraty 25406b9b3e0SSimon J. Gerraty /* 25506b9b3e0SSimon J. Gerraty * Variables for gathering statistics on the efficiency of the caching 25606b9b3e0SSimon J. Gerraty * mechanism. 25706b9b3e0SSimon J. Gerraty */ 25806b9b3e0SSimon J. Gerraty static int hits; /* Found in directory cache */ 25906b9b3e0SSimon J. Gerraty static int misses; /* Sad, but not evil misses */ 26006b9b3e0SSimon J. Gerraty static int nearmisses; /* Found under search path */ 26106b9b3e0SSimon J. Gerraty static int bigmisses; /* Sought by itself */ 26206b9b3e0SSimon J. Gerraty 26306b9b3e0SSimon J. Gerraty /* The cached contents of ".", the relative current directory. */ 26406b9b3e0SSimon J. Gerraty static CachedDir *dot = NULL; 26506b9b3e0SSimon J. Gerraty /* The cached contents of the absolute current directory. */ 26606b9b3e0SSimon J. Gerraty static CachedDir *cur = NULL; 26706b9b3e0SSimon J. Gerraty /* A fake path entry indicating we need to look for '.' last. */ 26806b9b3e0SSimon J. Gerraty static CachedDir *dotLast = NULL; 26906b9b3e0SSimon J. Gerraty 27006b9b3e0SSimon J. Gerraty /* 27106b9b3e0SSimon J. Gerraty * Results of doing a last-resort stat in Dir_FindFile -- if we have to go to 27206b9b3e0SSimon J. Gerraty * the system to find the file, we might as well have its mtime on record. 27306b9b3e0SSimon J. Gerraty * 27406b9b3e0SSimon J. Gerraty * XXX: If this is done way early, there's a chance other rules will have 27506b9b3e0SSimon J. Gerraty * already updated the file, in which case we'll update it again. Generally, 27606b9b3e0SSimon J. Gerraty * there won't be two rules to update a single file, so this should be ok, 27706b9b3e0SSimon J. Gerraty * but... 27806b9b3e0SSimon J. Gerraty */ 27906b9b3e0SSimon J. Gerraty static HashTable mtimes; 28006b9b3e0SSimon J. Gerraty 28106b9b3e0SSimon J. Gerraty static HashTable lmtimes; /* same as mtimes but for lstat */ 28206b9b3e0SSimon J. Gerraty 28306b9b3e0SSimon J. Gerraty 28406b9b3e0SSimon J. Gerraty static void OpenDirs_Remove(OpenDirs *, const char *); 28506b9b3e0SSimon J. Gerraty 28606b9b3e0SSimon J. Gerraty 28706b9b3e0SSimon J. Gerraty static CachedDir * 28806b9b3e0SSimon J. Gerraty CachedDir_New(const char *name) 28906b9b3e0SSimon J. Gerraty { 29006b9b3e0SSimon J. Gerraty CachedDir *dir = bmake_malloc(sizeof *dir); 29106b9b3e0SSimon J. Gerraty 29206b9b3e0SSimon J. Gerraty dir->name = bmake_strdup(name); 29306b9b3e0SSimon J. Gerraty dir->refCount = 0; 29406b9b3e0SSimon J. Gerraty dir->hits = 0; 29506b9b3e0SSimon J. Gerraty HashSet_Init(&dir->files); 29606b9b3e0SSimon J. Gerraty 29706b9b3e0SSimon J. Gerraty #ifdef DEBUG_REFCNT 29806b9b3e0SSimon J. Gerraty DEBUG2(DIR, "CachedDir %p new for \"%s\"\n", dir, dir->name); 29906b9b3e0SSimon J. Gerraty #endif 30006b9b3e0SSimon J. Gerraty 30106b9b3e0SSimon J. Gerraty return dir; 30206b9b3e0SSimon J. Gerraty } 30306b9b3e0SSimon J. Gerraty 30406b9b3e0SSimon J. Gerraty static CachedDir * 30506b9b3e0SSimon J. Gerraty CachedDir_Ref(CachedDir *dir) 30606b9b3e0SSimon J. Gerraty { 30706b9b3e0SSimon J. Gerraty dir->refCount++; 30806b9b3e0SSimon J. Gerraty 30906b9b3e0SSimon J. Gerraty #ifdef DEBUG_REFCNT 31006b9b3e0SSimon J. Gerraty DEBUG3(DIR, "CachedDir %p ++ %d for \"%s\"\n", 31106b9b3e0SSimon J. Gerraty dir, dir->refCount, dir->name); 31206b9b3e0SSimon J. Gerraty #endif 31306b9b3e0SSimon J. Gerraty 31406b9b3e0SSimon J. Gerraty return dir; 31506b9b3e0SSimon J. Gerraty } 31606b9b3e0SSimon J. Gerraty 31706b9b3e0SSimon J. Gerraty static void 31806b9b3e0SSimon J. Gerraty CachedDir_Unref(CachedDir *dir) 31906b9b3e0SSimon J. Gerraty { 32006b9b3e0SSimon J. Gerraty dir->refCount--; 32106b9b3e0SSimon J. Gerraty 32206b9b3e0SSimon J. Gerraty #ifdef DEBUG_REFCNT 32306b9b3e0SSimon J. Gerraty DEBUG3(DIR, "CachedDir %p -- %d for \"%s\"\n", 32406b9b3e0SSimon J. Gerraty dir, dir->refCount, dir->name); 32506b9b3e0SSimon J. Gerraty #endif 32606b9b3e0SSimon J. Gerraty 32706b9b3e0SSimon J. Gerraty if (dir->refCount > 0) 32806b9b3e0SSimon J. Gerraty return; 32906b9b3e0SSimon J. Gerraty 33006b9b3e0SSimon J. Gerraty #ifdef DEBUG_REFCNT 33106b9b3e0SSimon J. Gerraty DEBUG2(DIR, "CachedDir %p free for \"%s\"\n", dir, dir->name); 33206b9b3e0SSimon J. Gerraty #endif 33306b9b3e0SSimon J. Gerraty 33406b9b3e0SSimon J. Gerraty OpenDirs_Remove(&openDirs, dir->name); 33506b9b3e0SSimon J. Gerraty 33606b9b3e0SSimon J. Gerraty free(dir->name); 33706b9b3e0SSimon J. Gerraty HashSet_Done(&dir->files); 33806b9b3e0SSimon J. Gerraty free(dir); 33906b9b3e0SSimon J. Gerraty } 34006b9b3e0SSimon J. Gerraty 34106b9b3e0SSimon J. Gerraty /* Update the value of the CachedDir variable, updating the reference counts. */ 34206b9b3e0SSimon J. Gerraty static void 34306b9b3e0SSimon J. Gerraty CachedDir_Assign(CachedDir **var, CachedDir *dir) 34406b9b3e0SSimon J. Gerraty { 34506b9b3e0SSimon J. Gerraty CachedDir *prev; 34606b9b3e0SSimon J. Gerraty 34706b9b3e0SSimon J. Gerraty prev = *var; 34806b9b3e0SSimon J. Gerraty *var = dir; 34906b9b3e0SSimon J. Gerraty if (dir != NULL) 35006b9b3e0SSimon J. Gerraty CachedDir_Ref(dir); 35106b9b3e0SSimon J. Gerraty if (prev != NULL) 35206b9b3e0SSimon J. Gerraty CachedDir_Unref(prev); 35306b9b3e0SSimon J. Gerraty } 35406b9b3e0SSimon J. Gerraty 355956e45f6SSimon J. Gerraty static void 356956e45f6SSimon J. Gerraty OpenDirs_Init(OpenDirs *odirs) 357956e45f6SSimon J. Gerraty { 35806b9b3e0SSimon J. Gerraty Lst_Init(&odirs->list); 359956e45f6SSimon J. Gerraty HashTable_Init(&odirs->table); 360956e45f6SSimon J. Gerraty } 361956e45f6SSimon J. Gerraty 362956e45f6SSimon J. Gerraty #ifdef CLEANUP 363956e45f6SSimon J. Gerraty static void 364956e45f6SSimon J. Gerraty OpenDirs_Done(OpenDirs *odirs) 365956e45f6SSimon J. Gerraty { 36606b9b3e0SSimon J. Gerraty CachedDirListNode *ln = odirs->list.first; 36706b9b3e0SSimon J. Gerraty DEBUG1(DIR, "OpenDirs_Done: %u entries to remove\n", 36806b9b3e0SSimon J. Gerraty odirs->table.numEntries); 369956e45f6SSimon J. Gerraty while (ln != NULL) { 370956e45f6SSimon J. Gerraty CachedDirListNode *next = ln->next; 371956e45f6SSimon J. Gerraty CachedDir *dir = ln->datum; 37206b9b3e0SSimon J. Gerraty DEBUG2(DIR, "OpenDirs_Done: refCount %d for \"%s\"\n", 37306b9b3e0SSimon J. Gerraty dir->refCount, dir->name); 37406b9b3e0SSimon J. Gerraty CachedDir_Unref(dir); /* removes the dir from odirs->list */ 375956e45f6SSimon J. Gerraty ln = next; 376956e45f6SSimon J. Gerraty } 37706b9b3e0SSimon J. Gerraty Lst_Done(&odirs->list); 378956e45f6SSimon J. Gerraty HashTable_Done(&odirs->table); 379956e45f6SSimon J. Gerraty } 380956e45f6SSimon J. Gerraty #endif 381956e45f6SSimon J. Gerraty 382956e45f6SSimon J. Gerraty static CachedDir * 383956e45f6SSimon J. Gerraty OpenDirs_Find(OpenDirs *odirs, const char *name) 384956e45f6SSimon J. Gerraty { 385956e45f6SSimon J. Gerraty CachedDirListNode *ln = HashTable_FindValue(&odirs->table, name); 386956e45f6SSimon J. Gerraty return ln != NULL ? ln->datum : NULL; 387956e45f6SSimon J. Gerraty } 388956e45f6SSimon J. Gerraty 389956e45f6SSimon J. Gerraty static void 390956e45f6SSimon J. Gerraty OpenDirs_Add(OpenDirs *odirs, CachedDir *cdir) 391956e45f6SSimon J. Gerraty { 392e2eeea75SSimon J. Gerraty if (HashTable_FindEntry(&odirs->table, cdir->name) != NULL) 393956e45f6SSimon J. Gerraty return; 39406b9b3e0SSimon J. Gerraty Lst_Append(&odirs->list, cdir); 39506b9b3e0SSimon J. Gerraty HashTable_Set(&odirs->table, cdir->name, odirs->list.last); 396956e45f6SSimon J. Gerraty } 397956e45f6SSimon J. Gerraty 398956e45f6SSimon J. Gerraty static void 399956e45f6SSimon J. Gerraty OpenDirs_Remove(OpenDirs *odirs, const char *name) 400956e45f6SSimon J. Gerraty { 401956e45f6SSimon J. Gerraty HashEntry *he = HashTable_FindEntry(&odirs->table, name); 402956e45f6SSimon J. Gerraty CachedDirListNode *ln; 403956e45f6SSimon J. Gerraty if (he == NULL) 404956e45f6SSimon J. Gerraty return; 405956e45f6SSimon J. Gerraty ln = HashEntry_Get(he); 406956e45f6SSimon J. Gerraty HashTable_DeleteEntry(&odirs->table, he); 40706b9b3e0SSimon J. Gerraty Lst_Remove(&odirs->list, ln); 408956e45f6SSimon J. Gerraty } 409956e45f6SSimon J. Gerraty 4103955d011SMarcel Moolenaar /* 41106b9b3e0SSimon J. Gerraty * Returns 0 and the result of stat(2) or lstat(2) in *out_cst, 41206b9b3e0SSimon J. Gerraty * or -1 on error. 4133955d011SMarcel Moolenaar */ 41439ac7ef4SSimon J. Gerraty static int 415e2eeea75SSimon J. Gerraty cached_stats(const char *pathname, struct cached_stat *out_cst, 416*12904384SSimon J. Gerraty bool useLstat, bool forceRefresh) 41739ac7ef4SSimon J. Gerraty { 418*12904384SSimon J. Gerraty HashTable *tbl = useLstat ? &lmtimes : &mtimes; 4192c3632d1SSimon J. Gerraty struct stat sys_st; 420e2eeea75SSimon J. Gerraty struct cached_stat *cst; 42139ac7ef4SSimon J. Gerraty int rc; 42239ac7ef4SSimon J. Gerraty 423e2eeea75SSimon J. Gerraty if (pathname == NULL || pathname[0] == '\0') 424e2eeea75SSimon J. Gerraty return -1; /* This can happen in meta mode. */ 42539ac7ef4SSimon J. Gerraty 426e2eeea75SSimon J. Gerraty cst = HashTable_FindValue(tbl, pathname); 427*12904384SSimon J. Gerraty if (cst != NULL && !forceRefresh) { 428e2eeea75SSimon J. Gerraty *out_cst = *cst; 42906b9b3e0SSimon J. Gerraty DEBUG2(DIR, "Using cached time %s for %s\n", 430e2eeea75SSimon J. Gerraty Targ_FmtTime(cst->cst_mtime), pathname); 43139ac7ef4SSimon J. Gerraty return 0; 43239ac7ef4SSimon J. Gerraty } 43339ac7ef4SSimon J. Gerraty 434*12904384SSimon J. Gerraty rc = (useLstat ? lstat : stat)(pathname, &sys_st); 43539ac7ef4SSimon J. Gerraty if (rc == -1) 436e2eeea75SSimon J. Gerraty return -1; /* don't cache negative lookups */ 43739ac7ef4SSimon J. Gerraty 4382c3632d1SSimon J. Gerraty if (sys_st.st_mtime == 0) 4392c3632d1SSimon J. Gerraty sys_st.st_mtime = 1; /* avoid confusion with missing file */ 44039ac7ef4SSimon J. Gerraty 441e2eeea75SSimon J. Gerraty if (cst == NULL) { 442e2eeea75SSimon J. Gerraty cst = bmake_malloc(sizeof *cst); 443e2eeea75SSimon J. Gerraty HashTable_Set(tbl, pathname, cst); 444e2eeea75SSimon J. Gerraty } 4452c3632d1SSimon J. Gerraty 446e2eeea75SSimon J. Gerraty cst->cst_mtime = sys_st.st_mtime; 447e2eeea75SSimon J. Gerraty cst->cst_mode = sys_st.st_mode; 448e2eeea75SSimon J. Gerraty 449e2eeea75SSimon J. Gerraty *out_cst = *cst; 45006b9b3e0SSimon J. Gerraty DEBUG2(DIR, " Caching %s for %s\n", 4512c3632d1SSimon J. Gerraty Targ_FmtTime(sys_st.st_mtime), pathname); 45239ac7ef4SSimon J. Gerraty 45339ac7ef4SSimon J. Gerraty return 0; 45439ac7ef4SSimon J. Gerraty } 45539ac7ef4SSimon J. Gerraty 45639ac7ef4SSimon J. Gerraty int 457e2eeea75SSimon J. Gerraty cached_stat(const char *pathname, struct cached_stat *cst) 45839ac7ef4SSimon J. Gerraty { 459*12904384SSimon J. Gerraty return cached_stats(pathname, cst, false, false); 46039ac7ef4SSimon J. Gerraty } 46139ac7ef4SSimon J. Gerraty 46239ac7ef4SSimon J. Gerraty int 463e2eeea75SSimon J. Gerraty cached_lstat(const char *pathname, struct cached_stat *cst) 46439ac7ef4SSimon J. Gerraty { 465*12904384SSimon J. Gerraty return cached_stats(pathname, cst, true, false); 46639ac7ef4SSimon J. Gerraty } 46739ac7ef4SSimon J. Gerraty 468956e45f6SSimon J. Gerraty /* Initialize the directories module. */ 4693955d011SMarcel Moolenaar void 4702c3632d1SSimon J. Gerraty Dir_Init(void) 4713955d011SMarcel Moolenaar { 472956e45f6SSimon J. Gerraty OpenDirs_Init(&openDirs); 473956e45f6SSimon J. Gerraty HashTable_Init(&mtimes); 474956e45f6SSimon J. Gerraty HashTable_Init(&lmtimes); 47506b9b3e0SSimon J. Gerraty CachedDir_Assign(&dotLast, CachedDir_New(".DOTLAST")); 4763955d011SMarcel Moolenaar } 4773955d011SMarcel Moolenaar 4783955d011SMarcel Moolenaar /* 4792c3632d1SSimon J. Gerraty * Called by Dir_InitDir and whenever .CURDIR is assigned to. 4803955d011SMarcel Moolenaar */ 4813955d011SMarcel Moolenaar void 482dba7b0efSSimon J. Gerraty Dir_InitCur(const char *newCurdir) 4833955d011SMarcel Moolenaar { 484956e45f6SSimon J. Gerraty CachedDir *dir; 4853955d011SMarcel Moolenaar 486dba7b0efSSimon J. Gerraty if (newCurdir == NULL) 487e2eeea75SSimon J. Gerraty return; 488e2eeea75SSimon J. Gerraty 4893955d011SMarcel Moolenaar /* 4903955d011SMarcel Moolenaar * Our build directory is not the same as our source directory. 4913955d011SMarcel Moolenaar * Keep this one around too. 4923955d011SMarcel Moolenaar */ 493dba7b0efSSimon J. Gerraty dir = SearchPath_Add(NULL, newCurdir); 494e2eeea75SSimon J. Gerraty if (dir == NULL) 495e2eeea75SSimon J. Gerraty return; 496e2eeea75SSimon J. Gerraty 49706b9b3e0SSimon J. Gerraty CachedDir_Assign(&cur, dir); 49806b9b3e0SSimon J. Gerraty } 499e2eeea75SSimon J. Gerraty 5003955d011SMarcel Moolenaar /* 50106b9b3e0SSimon J. Gerraty * (Re)initialize "dot" (current/object directory) path hash. 50206b9b3e0SSimon J. Gerraty * Some directories may be cached. 5033955d011SMarcel Moolenaar */ 5043955d011SMarcel Moolenaar void 5053955d011SMarcel Moolenaar Dir_InitDot(void) 5063955d011SMarcel Moolenaar { 50706b9b3e0SSimon J. Gerraty CachedDir *dir; 5083955d011SMarcel Moolenaar 509dba7b0efSSimon J. Gerraty dir = SearchPath_Add(NULL, "."); 51006b9b3e0SSimon J. Gerraty if (dir == NULL) { 5113955d011SMarcel Moolenaar Error("Cannot open `.' (%s)", strerror(errno)); 51206b9b3e0SSimon J. Gerraty exit(2); /* Not 1 so -q can distinguish error */ 5133955d011SMarcel Moolenaar } 5143955d011SMarcel Moolenaar 51506b9b3e0SSimon J. Gerraty CachedDir_Assign(&dot, dir); 51606b9b3e0SSimon J. Gerraty 5173955d011SMarcel Moolenaar Dir_SetPATH(); /* initialize */ 5183955d011SMarcel Moolenaar } 5193955d011SMarcel Moolenaar 520956e45f6SSimon J. Gerraty /* Clean up the directories module. */ 5213955d011SMarcel Moolenaar void 5223955d011SMarcel Moolenaar Dir_End(void) 5233955d011SMarcel Moolenaar { 5243955d011SMarcel Moolenaar #ifdef CLEANUP 52506b9b3e0SSimon J. Gerraty CachedDir_Assign(&cur, NULL); 52606b9b3e0SSimon J. Gerraty CachedDir_Assign(&dot, NULL); 52706b9b3e0SSimon J. Gerraty CachedDir_Assign(&dotLast, NULL); 52806b9b3e0SSimon J. Gerraty SearchPath_Clear(&dirSearchPath); 529956e45f6SSimon J. Gerraty OpenDirs_Done(&openDirs); 530956e45f6SSimon J. Gerraty HashTable_Done(&mtimes); 53106b9b3e0SSimon J. Gerraty HashTable_Done(&lmtimes); 5323955d011SMarcel Moolenaar #endif 5333955d011SMarcel Moolenaar } 5343955d011SMarcel Moolenaar 5353955d011SMarcel Moolenaar /* 5363955d011SMarcel Moolenaar * We want ${.PATH} to indicate the order in which we will actually 5373955d011SMarcel Moolenaar * search, so we rebuild it after any .PATH: target. 5383955d011SMarcel Moolenaar * This is the simplest way to deal with the effect of .DOTLAST. 5393955d011SMarcel Moolenaar */ 5403955d011SMarcel Moolenaar void 5413955d011SMarcel Moolenaar Dir_SetPATH(void) 5423955d011SMarcel Moolenaar { 543956e45f6SSimon J. Gerraty CachedDirListNode *ln; 544b0c40a00SSimon J. Gerraty bool seenDotLast = false; /* true if we should search '.' last */ 5453955d011SMarcel Moolenaar 546dba7b0efSSimon J. Gerraty Global_Delete(".PATH"); 5473955d011SMarcel Moolenaar 548dba7b0efSSimon J. Gerraty if ((ln = dirSearchPath.dirs.first) != NULL) { 549956e45f6SSimon J. Gerraty CachedDir *dir = ln->datum; 550956e45f6SSimon J. Gerraty if (dir == dotLast) { 551b0c40a00SSimon J. Gerraty seenDotLast = true; 552dba7b0efSSimon J. Gerraty Global_Append(".PATH", dotLast->name); 5533955d011SMarcel Moolenaar } 5543955d011SMarcel Moolenaar } 5553955d011SMarcel Moolenaar 55606b9b3e0SSimon J. Gerraty if (!seenDotLast) { 55706b9b3e0SSimon J. Gerraty if (dot != NULL) 558dba7b0efSSimon J. Gerraty Global_Append(".PATH", dot->name); 55906b9b3e0SSimon J. Gerraty if (cur != NULL) 560dba7b0efSSimon J. Gerraty Global_Append(".PATH", cur->name); 5613955d011SMarcel Moolenaar } 5623955d011SMarcel Moolenaar 563dba7b0efSSimon J. Gerraty for (ln = dirSearchPath.dirs.first; ln != NULL; ln = ln->next) { 564956e45f6SSimon J. Gerraty CachedDir *dir = ln->datum; 565956e45f6SSimon J. Gerraty if (dir == dotLast) 5663955d011SMarcel Moolenaar continue; 56706b9b3e0SSimon J. Gerraty if (dir == dot && seenDotLast) 5683955d011SMarcel Moolenaar continue; 569dba7b0efSSimon J. Gerraty Global_Append(".PATH", dir->name); 5703955d011SMarcel Moolenaar } 5713955d011SMarcel Moolenaar 57206b9b3e0SSimon J. Gerraty if (seenDotLast) { 57306b9b3e0SSimon J. Gerraty if (dot != NULL) 574dba7b0efSSimon J. Gerraty Global_Append(".PATH", dot->name); 57506b9b3e0SSimon J. Gerraty if (cur != NULL) 576dba7b0efSSimon J. Gerraty Global_Append(".PATH", cur->name); 5773955d011SMarcel Moolenaar } 5783955d011SMarcel Moolenaar } 5793955d011SMarcel Moolenaar 58006b9b3e0SSimon J. Gerraty /* 58106b9b3e0SSimon J. Gerraty * See if the given name has any wildcard characters in it and all braces and 582956e45f6SSimon J. Gerraty * brackets are properly balanced. 5832c3632d1SSimon J. Gerraty * 5842c3632d1SSimon J. Gerraty * XXX: This code is not 100% correct ([^]] fails etc.). I really don't think 5852c3632d1SSimon J. Gerraty * that make(1) should be expanding patterns, because then you have to set a 5862c3632d1SSimon J. Gerraty * mechanism for escaping the expansion! 5873955d011SMarcel Moolenaar * 588b0c40a00SSimon J. Gerraty * Return true if the word should be expanded, false otherwise. 5893955d011SMarcel Moolenaar */ 590b0c40a00SSimon J. Gerraty bool 5912c3632d1SSimon J. Gerraty Dir_HasWildcards(const char *name) 5923955d011SMarcel Moolenaar { 593956e45f6SSimon J. Gerraty const char *p; 594b0c40a00SSimon J. Gerraty bool wild = false; 5952c3632d1SSimon J. Gerraty int braces = 0, brackets = 0; 5963955d011SMarcel Moolenaar 597956e45f6SSimon J. Gerraty for (p = name; *p != '\0'; p++) { 598956e45f6SSimon J. Gerraty switch (*p) { 5993955d011SMarcel Moolenaar case '{': 6002c3632d1SSimon J. Gerraty braces++; 601b0c40a00SSimon J. Gerraty wild = true; 6023955d011SMarcel Moolenaar break; 6033955d011SMarcel Moolenaar case '}': 6042c3632d1SSimon J. Gerraty braces--; 6053955d011SMarcel Moolenaar break; 6063955d011SMarcel Moolenaar case '[': 6072c3632d1SSimon J. Gerraty brackets++; 608b0c40a00SSimon J. Gerraty wild = true; 6093955d011SMarcel Moolenaar break; 6103955d011SMarcel Moolenaar case ']': 6112c3632d1SSimon J. Gerraty brackets--; 6123955d011SMarcel Moolenaar break; 6133955d011SMarcel Moolenaar case '?': 6143955d011SMarcel Moolenaar case '*': 615b0c40a00SSimon J. Gerraty wild = true; 6163955d011SMarcel Moolenaar break; 6173955d011SMarcel Moolenaar default: 6183955d011SMarcel Moolenaar break; 6193955d011SMarcel Moolenaar } 6203955d011SMarcel Moolenaar } 6212c3632d1SSimon J. Gerraty return wild && brackets == 0 && braces == 0; 6223955d011SMarcel Moolenaar } 6233955d011SMarcel Moolenaar 62406b9b3e0SSimon J. Gerraty /* 62506b9b3e0SSimon J. Gerraty * See if any files match the pattern and add their names to the 'expansions' 626956e45f6SSimon J. Gerraty * list if they do. 627956e45f6SSimon J. Gerraty * 628956e45f6SSimon J. Gerraty * This is incomplete -- wildcards are only expanded in the final path 629956e45f6SSimon J. Gerraty * component, but not in directories like src/lib*c/file*.c, but it 630956e45f6SSimon J. Gerraty * will do for now (now being 1993 until at least 2020). To expand these, 631dba7b0efSSimon J. Gerraty * delegate the work to the shell, using the '!=' variable assignment 632dba7b0efSSimon J. Gerraty * operator, the ':sh' variable modifier or the ':!...!' variable modifier, 633dba7b0efSSimon J. Gerraty * such as in ${:!echo src/lib*c/file*.c!}. 6343955d011SMarcel Moolenaar * 6353955d011SMarcel Moolenaar * Input: 6363955d011SMarcel Moolenaar * pattern Pattern to look for 637956e45f6SSimon J. Gerraty * dir Directory to search 6383955d011SMarcel Moolenaar * expansion Place to store the results 6393955d011SMarcel Moolenaar */ 6402c3632d1SSimon J. Gerraty static void 641956e45f6SSimon J. Gerraty DirMatchFiles(const char *pattern, CachedDir *dir, StringList *expansions) 6423955d011SMarcel Moolenaar { 643956e45f6SSimon J. Gerraty const char *dirName = dir->name; 644b0c40a00SSimon J. Gerraty bool isDot = dirName[0] == '.' && dirName[1] == '\0'; 645956e45f6SSimon J. Gerraty HashIter hi; 6463955d011SMarcel Moolenaar 64706b9b3e0SSimon J. Gerraty /* 64806b9b3e0SSimon J. Gerraty * XXX: Iterating over all hash entries is inefficient. If the 64906b9b3e0SSimon J. Gerraty * pattern is a plain string without any wildcards, a direct lookup 65006b9b3e0SSimon J. Gerraty * is faster. 65106b9b3e0SSimon J. Gerraty */ 652e2eeea75SSimon J. Gerraty 65306b9b3e0SSimon J. Gerraty HashIter_InitSet(&hi, &dir->files); 654956e45f6SSimon J. Gerraty while (HashIter_Next(&hi) != NULL) { 655956e45f6SSimon J. Gerraty const char *base = hi.entry->key; 6563955d011SMarcel Moolenaar 657956e45f6SSimon J. Gerraty if (!Str_Match(base, pattern)) 658956e45f6SSimon J. Gerraty continue; 659956e45f6SSimon J. Gerraty 6603955d011SMarcel Moolenaar /* 66106b9b3e0SSimon J. Gerraty * Follow the UNIX convention that dot files are only found 66206b9b3e0SSimon J. Gerraty * if the pattern begins with a dot. The pattern '.*' does 66306b9b3e0SSimon J. Gerraty * not match '.' or '..' since these are not included in the 66406b9b3e0SSimon J. Gerraty * directory cache. 665956e45f6SSimon J. Gerraty * 66606b9b3e0SSimon J. Gerraty * This means that the pattern '[a-z.]*' does not find 667dba7b0efSSimon J. Gerraty * '.file', which is consistent with NetBSD sh, NetBSD ksh, 668dba7b0efSSimon J. Gerraty * bash, dash, csh and probably many other shells as well. 6693955d011SMarcel Moolenaar */ 670956e45f6SSimon J. Gerraty if (base[0] == '.' && pattern[0] != '.') 671956e45f6SSimon J. Gerraty continue; 672956e45f6SSimon J. Gerraty 6733955d011SMarcel Moolenaar { 674956e45f6SSimon J. Gerraty char *fullName = isDot 675956e45f6SSimon J. Gerraty ? bmake_strdup(base) 676956e45f6SSimon J. Gerraty : str_concat3(dirName, "/", base); 677956e45f6SSimon J. Gerraty Lst_Append(expansions, fullName); 6783955d011SMarcel Moolenaar } 6793955d011SMarcel Moolenaar } 6802c3632d1SSimon J. Gerraty } 6812c3632d1SSimon J. Gerraty 68206b9b3e0SSimon J. Gerraty /* 68306b9b3e0SSimon J. Gerraty * Find the next closing brace in the string, taking nested braces into 68406b9b3e0SSimon J. Gerraty * account. 68506b9b3e0SSimon J. Gerraty */ 6862c3632d1SSimon J. Gerraty static const char * 6872c3632d1SSimon J. Gerraty closing_brace(const char *p) 6882c3632d1SSimon J. Gerraty { 6892c3632d1SSimon J. Gerraty int nest = 0; 6902c3632d1SSimon J. Gerraty while (*p != '\0') { 6912c3632d1SSimon J. Gerraty if (*p == '}' && nest == 0) 6922c3632d1SSimon J. Gerraty break; 6932c3632d1SSimon J. Gerraty if (*p == '{') 6942c3632d1SSimon J. Gerraty nest++; 6952c3632d1SSimon J. Gerraty if (*p == '}') 6962c3632d1SSimon J. Gerraty nest--; 6972c3632d1SSimon J. Gerraty p++; 6982c3632d1SSimon J. Gerraty } 6992c3632d1SSimon J. Gerraty return p; 7002c3632d1SSimon J. Gerraty } 7012c3632d1SSimon J. Gerraty 70206b9b3e0SSimon J. Gerraty /* 70306b9b3e0SSimon J. Gerraty * Find the next closing brace or comma in the string, taking nested braces 70406b9b3e0SSimon J. Gerraty * into account. 70506b9b3e0SSimon J. Gerraty */ 7062c3632d1SSimon J. Gerraty static const char * 7072c3632d1SSimon J. Gerraty separator_comma(const char *p) 7082c3632d1SSimon J. Gerraty { 7092c3632d1SSimon J. Gerraty int nest = 0; 7102c3632d1SSimon J. Gerraty while (*p != '\0') { 7112c3632d1SSimon J. Gerraty if ((*p == '}' || *p == ',') && nest == 0) 7122c3632d1SSimon J. Gerraty break; 7132c3632d1SSimon J. Gerraty if (*p == '{') 7142c3632d1SSimon J. Gerraty nest++; 7152c3632d1SSimon J. Gerraty if (*p == '}') 7162c3632d1SSimon J. Gerraty nest--; 7172c3632d1SSimon J. Gerraty p++; 7182c3632d1SSimon J. Gerraty } 7192c3632d1SSimon J. Gerraty return p; 7202c3632d1SSimon J. Gerraty } 7212c3632d1SSimon J. Gerraty 722b0c40a00SSimon J. Gerraty static bool 7232c3632d1SSimon J. Gerraty contains_wildcard(const char *p) 7242c3632d1SSimon J. Gerraty { 7252c3632d1SSimon J. Gerraty for (; *p != '\0'; p++) { 7262c3632d1SSimon J. Gerraty switch (*p) { 7272c3632d1SSimon J. Gerraty case '*': 7282c3632d1SSimon J. Gerraty case '?': 7292c3632d1SSimon J. Gerraty case '{': 7302c3632d1SSimon J. Gerraty case '[': 731b0c40a00SSimon J. Gerraty return true; 7322c3632d1SSimon J. Gerraty } 7332c3632d1SSimon J. Gerraty } 734b0c40a00SSimon J. Gerraty return false; 7352c3632d1SSimon J. Gerraty } 7362c3632d1SSimon J. Gerraty 7372c3632d1SSimon J. Gerraty static char * 7382c3632d1SSimon J. Gerraty concat3(const char *a, size_t a_len, const char *b, size_t b_len, 7392c3632d1SSimon J. Gerraty const char *c, size_t c_len) 7402c3632d1SSimon J. Gerraty { 7412c3632d1SSimon J. Gerraty size_t s_len = a_len + b_len + c_len; 7422c3632d1SSimon J. Gerraty char *s = bmake_malloc(s_len + 1); 7432c3632d1SSimon J. Gerraty memcpy(s, a, a_len); 7442c3632d1SSimon J. Gerraty memcpy(s + a_len, b, b_len); 7452c3632d1SSimon J. Gerraty memcpy(s + a_len + b_len, c, c_len); 7462c3632d1SSimon J. Gerraty s[s_len] = '\0'; 7472c3632d1SSimon J. Gerraty return s; 7483955d011SMarcel Moolenaar } 7493955d011SMarcel Moolenaar 75006b9b3e0SSimon J. Gerraty /* 75106b9b3e0SSimon J. Gerraty * Expand curly braces like the C shell. Brace expansion by itself is purely 752956e45f6SSimon J. Gerraty * textual, the expansions are not looked up in the file system. But if an 753956e45f6SSimon J. Gerraty * expanded word contains wildcard characters, it is expanded further, 754956e45f6SSimon J. Gerraty * matching only the actually existing files. 755956e45f6SSimon J. Gerraty * 756956e45f6SSimon J. Gerraty * Example: "{a{b,c}}" expands to "ab" and "ac". 757956e45f6SSimon J. Gerraty * Example: "{a}" expands to "a". 758956e45f6SSimon J. Gerraty * Example: "{a,*.c}" expands to "a" and all "*.c" files that exist. 7593955d011SMarcel Moolenaar * 7603955d011SMarcel Moolenaar * Input: 7613955d011SMarcel Moolenaar * word Entire word to expand 7623955d011SMarcel Moolenaar * brace First curly brace in it 7633955d011SMarcel Moolenaar * path Search path to use 7643955d011SMarcel Moolenaar * expansions Place to store the expansions 7653955d011SMarcel Moolenaar */ 7663955d011SMarcel Moolenaar static void 767956e45f6SSimon J. Gerraty DirExpandCurly(const char *word, const char *brace, SearchPath *path, 768956e45f6SSimon J. Gerraty StringList *expansions) 7693955d011SMarcel Moolenaar { 7702c3632d1SSimon J. Gerraty const char *prefix, *middle, *piece, *middle_end, *suffix; 7712c3632d1SSimon J. Gerraty size_t prefix_len, suffix_len; 7723955d011SMarcel Moolenaar 7732c3632d1SSimon J. Gerraty /* Split the word into prefix '{' middle '}' suffix. */ 7743955d011SMarcel Moolenaar 7752c3632d1SSimon J. Gerraty middle = brace + 1; 7762c3632d1SSimon J. Gerraty middle_end = closing_brace(middle); 7772c3632d1SSimon J. Gerraty if (*middle_end == '\0') { 7782c3632d1SSimon J. Gerraty Error("Unterminated {} clause \"%s\"", middle); 7793955d011SMarcel Moolenaar return; 7803955d011SMarcel Moolenaar } 7813955d011SMarcel Moolenaar 7822c3632d1SSimon J. Gerraty prefix = word; 7832c3632d1SSimon J. Gerraty prefix_len = (size_t)(brace - prefix); 7842c3632d1SSimon J. Gerraty suffix = middle_end + 1; 7852c3632d1SSimon J. Gerraty suffix_len = strlen(suffix); 7863955d011SMarcel Moolenaar 7872c3632d1SSimon J. Gerraty /* Split the middle into pieces, separated by commas. */ 7882c3632d1SSimon J. Gerraty 7892c3632d1SSimon J. Gerraty piece = middle; 7902c3632d1SSimon J. Gerraty while (piece < middle_end + 1) { 7912c3632d1SSimon J. Gerraty const char *piece_end = separator_comma(piece); 7922c3632d1SSimon J. Gerraty size_t piece_len = (size_t)(piece_end - piece); 7932c3632d1SSimon J. Gerraty 7942c3632d1SSimon J. Gerraty char *file = concat3(prefix, prefix_len, piece, piece_len, 7952c3632d1SSimon J. Gerraty suffix, suffix_len); 7962c3632d1SSimon J. Gerraty 7972c3632d1SSimon J. Gerraty if (contains_wildcard(file)) { 798dba7b0efSSimon J. Gerraty SearchPath_Expand(path, file, expansions); 7993955d011SMarcel Moolenaar free(file); 8002c3632d1SSimon J. Gerraty } else { 8012c3632d1SSimon J. Gerraty Lst_Append(expansions, file); 8023955d011SMarcel Moolenaar } 8032c3632d1SSimon J. Gerraty 80406b9b3e0SSimon J. Gerraty /* skip over the comma or closing brace */ 80506b9b3e0SSimon J. Gerraty piece = piece_end + 1; 8063955d011SMarcel Moolenaar } 8073955d011SMarcel Moolenaar } 8083955d011SMarcel Moolenaar 8093955d011SMarcel Moolenaar 810956e45f6SSimon J. Gerraty /* Expand the word in each of the directories from the path. */ 8113955d011SMarcel Moolenaar static void 812956e45f6SSimon J. Gerraty DirExpandPath(const char *word, SearchPath *path, StringList *expansions) 8133955d011SMarcel Moolenaar { 814956e45f6SSimon J. Gerraty SearchPathNode *ln; 815dba7b0efSSimon J. Gerraty for (ln = path->dirs.first; ln != NULL; ln = ln->next) { 816956e45f6SSimon J. Gerraty CachedDir *dir = ln->datum; 817956e45f6SSimon J. Gerraty DirMatchFiles(word, dir, expansions); 8183955d011SMarcel Moolenaar } 8193955d011SMarcel Moolenaar } 8203955d011SMarcel Moolenaar 821956e45f6SSimon J. Gerraty static void 822956e45f6SSimon J. Gerraty PrintExpansions(StringList *expansions) 8233955d011SMarcel Moolenaar { 824956e45f6SSimon J. Gerraty const char *sep = ""; 825956e45f6SSimon J. Gerraty StringListNode *ln; 826956e45f6SSimon J. Gerraty for (ln = expansions->first; ln != NULL; ln = ln->next) { 827956e45f6SSimon J. Gerraty const char *word = ln->datum; 828956e45f6SSimon J. Gerraty debug_printf("%s%s", sep, word); 829956e45f6SSimon J. Gerraty sep = " "; 830956e45f6SSimon J. Gerraty } 831956e45f6SSimon J. Gerraty debug_printf("\n"); 8323955d011SMarcel Moolenaar } 8333955d011SMarcel Moolenaar 83406b9b3e0SSimon J. Gerraty /* 83506b9b3e0SSimon J. Gerraty * The wildcard isn't in the first component. 83606b9b3e0SSimon J. Gerraty * Find all the components up to the one with the wildcard. 83706b9b3e0SSimon J. Gerraty */ 838dba7b0efSSimon J. Gerraty static void 839dba7b0efSSimon J. Gerraty SearchPath_ExpandMiddle(SearchPath *path, const char *pattern, 840dba7b0efSSimon J. Gerraty const char *wildcardComponent, StringList *expansions) 841dba7b0efSSimon J. Gerraty { 842dba7b0efSSimon J. Gerraty char *prefix, *dirpath, *end; 843dba7b0efSSimon J. Gerraty SearchPath *partPath; 844dba7b0efSSimon J. Gerraty 845dba7b0efSSimon J. Gerraty prefix = bmake_strsedup(pattern, wildcardComponent + 1); 84606b9b3e0SSimon J. Gerraty /* 84706b9b3e0SSimon J. Gerraty * XXX: Check the "the directory is added to the path" part. 84806b9b3e0SSimon J. Gerraty * It is probably surprising that the directory before a 84906b9b3e0SSimon J. Gerraty * wildcard gets added to the path. 85006b9b3e0SSimon J. Gerraty */ 85106b9b3e0SSimon J. Gerraty /* 85206b9b3e0SSimon J. Gerraty * XXX: Only the first match of the prefix in the path is 85306b9b3e0SSimon J. Gerraty * taken, any others are ignored. The expectation may be 85406b9b3e0SSimon J. Gerraty * that the pattern is expanded in the whole path. 8553955d011SMarcel Moolenaar */ 856dba7b0efSSimon J. Gerraty dirpath = Dir_FindFile(prefix, path); 857956e45f6SSimon J. Gerraty free(prefix); 85806b9b3e0SSimon J. Gerraty 8593955d011SMarcel Moolenaar /* 8603955d011SMarcel Moolenaar * dirpath is null if can't find the leading component 861dba7b0efSSimon J. Gerraty * 862dba7b0efSSimon J. Gerraty * XXX: Dir_FindFile won't find internal components. i.e. if the 863dba7b0efSSimon J. Gerraty * path contains ../Etc/Object and we're looking for Etc, it won't 864dba7b0efSSimon J. Gerraty * be found. Ah well. Probably not important. 865dba7b0efSSimon J. Gerraty * 86606b9b3e0SSimon J. Gerraty * XXX: Check whether the above comment is still true. 8673955d011SMarcel Moolenaar */ 868dba7b0efSSimon J. Gerraty if (dirpath == NULL) 869dba7b0efSSimon J. Gerraty return; 8703955d011SMarcel Moolenaar 871dba7b0efSSimon J. Gerraty end = &dirpath[strlen(dirpath) - 1]; 87206b9b3e0SSimon J. Gerraty /* XXX: What about multiple trailing slashes? */ 87306b9b3e0SSimon J. Gerraty if (*end == '/') 87406b9b3e0SSimon J. Gerraty *end = '\0'; 87506b9b3e0SSimon J. Gerraty 87606b9b3e0SSimon J. Gerraty partPath = SearchPath_New(); 877dba7b0efSSimon J. Gerraty (void)SearchPath_Add(partPath, dirpath); 878dba7b0efSSimon J. Gerraty DirExpandPath(wildcardComponent + 1, partPath, expansions); 87906b9b3e0SSimon J. Gerraty SearchPath_Free(partPath); 8803955d011SMarcel Moolenaar } 881dba7b0efSSimon J. Gerraty 882dba7b0efSSimon J. Gerraty /* 883dba7b0efSSimon J. Gerraty * Expand the given pattern into a list of existing filenames by globbing it, 884dba7b0efSSimon J. Gerraty * looking in each directory from the search path. 885dba7b0efSSimon J. Gerraty * 886dba7b0efSSimon J. Gerraty * Input: 887dba7b0efSSimon J. Gerraty * path the directories in which to find the files 888dba7b0efSSimon J. Gerraty * pattern the pattern to expand 889dba7b0efSSimon J. Gerraty * expansions the list on which to place the results 890dba7b0efSSimon J. Gerraty */ 891dba7b0efSSimon J. Gerraty void 892dba7b0efSSimon J. Gerraty SearchPath_Expand(SearchPath *path, const char *pattern, StringList *expansions) 893dba7b0efSSimon J. Gerraty { 894dba7b0efSSimon J. Gerraty const char *brace, *slash, *wildcard, *wildcardComponent; 895dba7b0efSSimon J. Gerraty 896dba7b0efSSimon J. Gerraty assert(path != NULL); 897dba7b0efSSimon J. Gerraty assert(expansions != NULL); 898dba7b0efSSimon J. Gerraty 899dba7b0efSSimon J. Gerraty DEBUG1(DIR, "Expanding \"%s\"... ", pattern); 900dba7b0efSSimon J. Gerraty 901dba7b0efSSimon J. Gerraty brace = strchr(pattern, '{'); 902dba7b0efSSimon J. Gerraty if (brace != NULL) { 903dba7b0efSSimon J. Gerraty DirExpandCurly(pattern, brace, path, expansions); 904dba7b0efSSimon J. Gerraty goto done; 905dba7b0efSSimon J. Gerraty } 906dba7b0efSSimon J. Gerraty 907dba7b0efSSimon J. Gerraty /* At this point, the pattern does not contain '{'. */ 908dba7b0efSSimon J. Gerraty 909dba7b0efSSimon J. Gerraty slash = strchr(pattern, '/'); 910dba7b0efSSimon J. Gerraty if (slash == NULL) { 911dba7b0efSSimon J. Gerraty /* The pattern has no directory component. */ 912dba7b0efSSimon J. Gerraty 913dba7b0efSSimon J. Gerraty /* First the files in dot. */ 914dba7b0efSSimon J. Gerraty DirMatchFiles(pattern, dot, expansions); 915dba7b0efSSimon J. Gerraty /* Then the files in every other directory on the path. */ 916dba7b0efSSimon J. Gerraty DirExpandPath(pattern, path, expansions); 917dba7b0efSSimon J. Gerraty goto done; 918dba7b0efSSimon J. Gerraty } 919dba7b0efSSimon J. Gerraty 920dba7b0efSSimon J. Gerraty /* At this point, the pattern has a directory component. */ 921dba7b0efSSimon J. Gerraty 922dba7b0efSSimon J. Gerraty /* Find the first wildcard in the pattern. */ 923dba7b0efSSimon J. Gerraty for (wildcard = pattern; *wildcard != '\0'; wildcard++) 924dba7b0efSSimon J. Gerraty if (*wildcard == '?' || *wildcard == '[' || *wildcard == '*') 925dba7b0efSSimon J. Gerraty break; 926dba7b0efSSimon J. Gerraty 927dba7b0efSSimon J. Gerraty if (*wildcard == '\0') { 928dba7b0efSSimon J. Gerraty /* 929dba7b0efSSimon J. Gerraty * No directory component and no wildcard at all -- this 930dba7b0efSSimon J. Gerraty * should never happen as in such a simple case there is no 931dba7b0efSSimon J. Gerraty * need to expand anything. 932dba7b0efSSimon J. Gerraty */ 933dba7b0efSSimon J. Gerraty DirExpandPath(pattern, path, expansions); 934dba7b0efSSimon J. Gerraty goto done; 935dba7b0efSSimon J. Gerraty } 936dba7b0efSSimon J. Gerraty 937dba7b0efSSimon J. Gerraty /* Back up to the start of the component containing the wildcard. */ 938dba7b0efSSimon J. Gerraty /* XXX: This handles '///' and '/' differently. */ 939dba7b0efSSimon J. Gerraty wildcardComponent = wildcard; 940dba7b0efSSimon J. Gerraty while (wildcardComponent > pattern && *wildcardComponent != '/') 941dba7b0efSSimon J. Gerraty wildcardComponent--; 942dba7b0efSSimon J. Gerraty 943dba7b0efSSimon J. Gerraty if (wildcardComponent == pattern) { 944dba7b0efSSimon J. Gerraty /* The first component contains the wildcard. */ 945dba7b0efSSimon J. Gerraty /* Start the search from the local directory */ 946dba7b0efSSimon J. Gerraty DirExpandPath(pattern, path, expansions); 947dba7b0efSSimon J. Gerraty } else { 948dba7b0efSSimon J. Gerraty SearchPath_ExpandMiddle(path, pattern, wildcardComponent, 949dba7b0efSSimon J. Gerraty expansions); 9503955d011SMarcel Moolenaar } 95106b9b3e0SSimon J. Gerraty 95206b9b3e0SSimon J. Gerraty done: 953956e45f6SSimon J. Gerraty if (DEBUG(DIR)) 954956e45f6SSimon J. Gerraty PrintExpansions(expansions); 9553955d011SMarcel Moolenaar } 9563955d011SMarcel Moolenaar 95706b9b3e0SSimon J. Gerraty /* 95806b9b3e0SSimon J. Gerraty * Find if the file with the given name exists in the given path. 95906b9b3e0SSimon J. Gerraty * Return the freshly allocated path to the file, or NULL. 96006b9b3e0SSimon J. Gerraty */ 9613955d011SMarcel Moolenaar static char * 962956e45f6SSimon J. Gerraty DirLookup(CachedDir *dir, const char *base) 9633955d011SMarcel Moolenaar { 9643955d011SMarcel Moolenaar char *file; /* the current filename to check */ 9653955d011SMarcel Moolenaar 96606b9b3e0SSimon J. Gerraty DEBUG1(DIR, " %s ...\n", dir->name); 9673955d011SMarcel Moolenaar 96806b9b3e0SSimon J. Gerraty if (!HashSet_Contains(&dir->files, base)) 9693955d011SMarcel Moolenaar return NULL; 9703955d011SMarcel Moolenaar 971956e45f6SSimon J. Gerraty file = str_concat3(dir->name, "/", base); 97206b9b3e0SSimon J. Gerraty DEBUG1(DIR, " returning %s\n", file); 973956e45f6SSimon J. Gerraty dir->hits++; 974956e45f6SSimon J. Gerraty hits++; 9753955d011SMarcel Moolenaar return file; 9763955d011SMarcel Moolenaar } 9773955d011SMarcel Moolenaar 9783955d011SMarcel Moolenaar 97906b9b3e0SSimon J. Gerraty /* 98006b9b3e0SSimon J. Gerraty * Find if the file with the given name exists in the given directory. 98106b9b3e0SSimon J. Gerraty * Return the freshly allocated path to the file, or NULL. 98206b9b3e0SSimon J. Gerraty */ 9833955d011SMarcel Moolenaar static char * 984956e45f6SSimon J. Gerraty DirLookupSubdir(CachedDir *dir, const char *name) 9853955d011SMarcel Moolenaar { 986e2eeea75SSimon J. Gerraty struct cached_stat cst; 987956e45f6SSimon J. Gerraty char *file = dir == dot ? bmake_strdup(name) 988956e45f6SSimon J. Gerraty : str_concat3(dir->name, "/", name); 9893955d011SMarcel Moolenaar 99006b9b3e0SSimon J. Gerraty DEBUG1(DIR, "checking %s ...\n", file); 9913955d011SMarcel Moolenaar 992e2eeea75SSimon J. Gerraty if (cached_stat(file, &cst) == 0) { 993956e45f6SSimon J. Gerraty nearmisses++; 9943841c287SSimon J. Gerraty return file; 9953955d011SMarcel Moolenaar } 9963955d011SMarcel Moolenaar free(file); 9973955d011SMarcel Moolenaar return NULL; 9983955d011SMarcel Moolenaar } 9993955d011SMarcel Moolenaar 100006b9b3e0SSimon J. Gerraty /* 100106b9b3e0SSimon J. Gerraty * Find if the file with the given name exists in the given path. 1002956e45f6SSimon J. Gerraty * Return the freshly allocated path to the file, the empty string, or NULL. 1003956e45f6SSimon J. Gerraty * Returning the empty string means that the search should be terminated. 10043955d011SMarcel Moolenaar */ 10053955d011SMarcel Moolenaar static char * 1006956e45f6SSimon J. Gerraty DirLookupAbs(CachedDir *dir, const char *name, const char *cp) 10073955d011SMarcel Moolenaar { 1008956e45f6SSimon J. Gerraty const char *dnp; /* pointer into dir->name */ 1009956e45f6SSimon J. Gerraty const char *np; /* pointer into name */ 10103955d011SMarcel Moolenaar 101106b9b3e0SSimon J. Gerraty DEBUG1(DIR, " %s ...\n", dir->name); 10123955d011SMarcel Moolenaar 10133955d011SMarcel Moolenaar /* 10143955d011SMarcel Moolenaar * If the file has a leading path component and that component 10153955d011SMarcel Moolenaar * exactly matches the entire name of the current search 10163955d011SMarcel Moolenaar * directory, we can attempt another cache lookup. And if we don't 10173955d011SMarcel Moolenaar * have a hit, we can safely assume the file does not exist at all. 10183955d011SMarcel Moolenaar */ 101906b9b3e0SSimon J. Gerraty for (dnp = dir->name, np = name; 102006b9b3e0SSimon J. Gerraty *dnp != '\0' && *dnp == *np; dnp++, np++) 10213955d011SMarcel Moolenaar continue; 1022956e45f6SSimon J. Gerraty if (*dnp != '\0' || np != cp - 1) 10233955d011SMarcel Moolenaar return NULL; 10243955d011SMarcel Moolenaar 102506b9b3e0SSimon J. Gerraty if (!HashSet_Contains(&dir->files, cp)) { 102606b9b3e0SSimon J. Gerraty DEBUG0(DIR, " must be here but isn't -- returning\n"); 1027956e45f6SSimon J. Gerraty return bmake_strdup(""); /* to terminate the search */ 10283955d011SMarcel Moolenaar } 10293955d011SMarcel Moolenaar 1030956e45f6SSimon J. Gerraty dir->hits++; 1031956e45f6SSimon J. Gerraty hits++; 103206b9b3e0SSimon J. Gerraty DEBUG1(DIR, " returning %s\n", name); 10333841c287SSimon J. Gerraty return bmake_strdup(name); 10343955d011SMarcel Moolenaar } 10353955d011SMarcel Moolenaar 103606b9b3e0SSimon J. Gerraty /* 103706b9b3e0SSimon J. Gerraty * Find the file given on "." or curdir. 103806b9b3e0SSimon J. Gerraty * Return the freshly allocated path to the file, or NULL. 103906b9b3e0SSimon J. Gerraty */ 10403955d011SMarcel Moolenaar static char * 1041956e45f6SSimon J. Gerraty DirFindDot(const char *name, const char *base) 10423955d011SMarcel Moolenaar { 10433955d011SMarcel Moolenaar 104406b9b3e0SSimon J. Gerraty if (HashSet_Contains(&dot->files, base)) { 104506b9b3e0SSimon J. Gerraty DEBUG0(DIR, " in '.'\n"); 1046956e45f6SSimon J. Gerraty hits++; 1047956e45f6SSimon J. Gerraty dot->hits++; 10483841c287SSimon J. Gerraty return bmake_strdup(name); 10493955d011SMarcel Moolenaar } 1050956e45f6SSimon J. Gerraty 105106b9b3e0SSimon J. Gerraty if (cur != NULL && HashSet_Contains(&cur->files, base)) { 105206b9b3e0SSimon J. Gerraty DEBUG1(DIR, " in ${.CURDIR} = %s\n", cur->name); 1053956e45f6SSimon J. Gerraty hits++; 1054956e45f6SSimon J. Gerraty cur->hits++; 1055956e45f6SSimon J. Gerraty return str_concat3(cur->name, "/", base); 10563955d011SMarcel Moolenaar } 10573955d011SMarcel Moolenaar 10583955d011SMarcel Moolenaar return NULL; 10593955d011SMarcel Moolenaar } 10603955d011SMarcel Moolenaar 1061b0c40a00SSimon J. Gerraty static bool 1062b0c40a00SSimon J. Gerraty FindFileRelative(SearchPath *path, bool seenDotLast, 1063dba7b0efSSimon J. Gerraty const char *name, char **out_file) 1064dba7b0efSSimon J. Gerraty { 1065dba7b0efSSimon J. Gerraty SearchPathNode *ln; 1066b0c40a00SSimon J. Gerraty bool checkedDot = false; 1067dba7b0efSSimon J. Gerraty char *file; 1068dba7b0efSSimon J. Gerraty 1069dba7b0efSSimon J. Gerraty DEBUG0(DIR, " Trying subdirectories...\n"); 1070dba7b0efSSimon J. Gerraty 1071dba7b0efSSimon J. Gerraty if (!seenDotLast) { 1072dba7b0efSSimon J. Gerraty if (dot != NULL) { 1073b0c40a00SSimon J. Gerraty checkedDot = true; 1074dba7b0efSSimon J. Gerraty if ((file = DirLookupSubdir(dot, name)) != NULL) 1075dba7b0efSSimon J. Gerraty goto found; 1076dba7b0efSSimon J. Gerraty } 1077dba7b0efSSimon J. Gerraty if (cur != NULL && 1078dba7b0efSSimon J. Gerraty (file = DirLookupSubdir(cur, name)) != NULL) 1079dba7b0efSSimon J. Gerraty goto found; 1080dba7b0efSSimon J. Gerraty } 1081dba7b0efSSimon J. Gerraty 1082dba7b0efSSimon J. Gerraty for (ln = path->dirs.first; ln != NULL; ln = ln->next) { 1083dba7b0efSSimon J. Gerraty CachedDir *dir = ln->datum; 1084dba7b0efSSimon J. Gerraty if (dir == dotLast) 1085dba7b0efSSimon J. Gerraty continue; 1086dba7b0efSSimon J. Gerraty if (dir == dot) { 1087dba7b0efSSimon J. Gerraty if (checkedDot) 1088dba7b0efSSimon J. Gerraty continue; 1089b0c40a00SSimon J. Gerraty checkedDot = true; 1090dba7b0efSSimon J. Gerraty } 1091dba7b0efSSimon J. Gerraty if ((file = DirLookupSubdir(dir, name)) != NULL) 1092dba7b0efSSimon J. Gerraty goto found; 1093dba7b0efSSimon J. Gerraty } 1094dba7b0efSSimon J. Gerraty 1095dba7b0efSSimon J. Gerraty if (seenDotLast) { 1096dba7b0efSSimon J. Gerraty if (dot != NULL && !checkedDot) { 1097b0c40a00SSimon J. Gerraty checkedDot = true; 1098dba7b0efSSimon J. Gerraty if ((file = DirLookupSubdir(dot, name)) != NULL) 1099dba7b0efSSimon J. Gerraty goto found; 1100dba7b0efSSimon J. Gerraty } 1101dba7b0efSSimon J. Gerraty if (cur != NULL && 1102dba7b0efSSimon J. Gerraty (file = DirLookupSubdir(cur, name)) != NULL) 1103dba7b0efSSimon J. Gerraty goto found; 1104dba7b0efSSimon J. Gerraty } 1105dba7b0efSSimon J. Gerraty 1106dba7b0efSSimon J. Gerraty if (checkedDot) { 1107dba7b0efSSimon J. Gerraty /* 1108dba7b0efSSimon J. Gerraty * Already checked by the given name, since . was in 1109dba7b0efSSimon J. Gerraty * the path, so no point in proceeding. 1110dba7b0efSSimon J. Gerraty */ 1111dba7b0efSSimon J. Gerraty DEBUG0(DIR, " Checked . already, returning NULL\n"); 1112dba7b0efSSimon J. Gerraty file = NULL; 1113dba7b0efSSimon J. Gerraty goto found; 1114dba7b0efSSimon J. Gerraty } 1115dba7b0efSSimon J. Gerraty 1116b0c40a00SSimon J. Gerraty return false; 1117dba7b0efSSimon J. Gerraty 1118dba7b0efSSimon J. Gerraty found: 1119dba7b0efSSimon J. Gerraty *out_file = file; 1120b0c40a00SSimon J. Gerraty return true; 1121dba7b0efSSimon J. Gerraty } 1122dba7b0efSSimon J. Gerraty 1123b0c40a00SSimon J. Gerraty static bool 1124*12904384SSimon J. Gerraty FindFileAbsolute(SearchPath *path, bool seenDotLast, 1125*12904384SSimon J. Gerraty const char *name, const char *base, char **out_file) 1126dba7b0efSSimon J. Gerraty { 1127dba7b0efSSimon J. Gerraty char *file; 1128dba7b0efSSimon J. Gerraty SearchPathNode *ln; 1129dba7b0efSSimon J. Gerraty 1130dba7b0efSSimon J. Gerraty /* 1131dba7b0efSSimon J. Gerraty * For absolute names, compare directory path prefix against 1132dba7b0efSSimon J. Gerraty * the the directory path of each member on the search path 1133dba7b0efSSimon J. Gerraty * for an exact match. If we have an exact match on any member 1134dba7b0efSSimon J. Gerraty * of the search path, use the cached contents of that member 1135dba7b0efSSimon J. Gerraty * to lookup the final file component. If that lookup fails we 1136dba7b0efSSimon J. Gerraty * can safely assume that the file does not exist at all. 1137dba7b0efSSimon J. Gerraty * This is signified by DirLookupAbs() returning an empty 1138dba7b0efSSimon J. Gerraty * string. 1139dba7b0efSSimon J. Gerraty */ 1140dba7b0efSSimon J. Gerraty DEBUG0(DIR, " Trying exact path matches...\n"); 1141dba7b0efSSimon J. Gerraty 1142dba7b0efSSimon J. Gerraty if (!seenDotLast && cur != NULL && 1143dba7b0efSSimon J. Gerraty ((file = DirLookupAbs(cur, name, base)) != NULL)) 1144dba7b0efSSimon J. Gerraty goto found; 1145dba7b0efSSimon J. Gerraty 1146dba7b0efSSimon J. Gerraty for (ln = path->dirs.first; ln != NULL; ln = ln->next) { 1147dba7b0efSSimon J. Gerraty CachedDir *dir = ln->datum; 1148dba7b0efSSimon J. Gerraty if (dir == dotLast) 1149dba7b0efSSimon J. Gerraty continue; 1150dba7b0efSSimon J. Gerraty if ((file = DirLookupAbs(dir, name, base)) != NULL) 1151dba7b0efSSimon J. Gerraty goto found; 1152dba7b0efSSimon J. Gerraty } 1153dba7b0efSSimon J. Gerraty 1154dba7b0efSSimon J. Gerraty if (seenDotLast && cur != NULL && 1155dba7b0efSSimon J. Gerraty ((file = DirLookupAbs(cur, name, base)) != NULL)) 1156dba7b0efSSimon J. Gerraty goto found; 1157dba7b0efSSimon J. Gerraty 1158b0c40a00SSimon J. Gerraty return false; 1159dba7b0efSSimon J. Gerraty 1160dba7b0efSSimon J. Gerraty found: 1161dba7b0efSSimon J. Gerraty if (file[0] == '\0') { 1162dba7b0efSSimon J. Gerraty free(file); 1163dba7b0efSSimon J. Gerraty file = NULL; 1164dba7b0efSSimon J. Gerraty } 1165dba7b0efSSimon J. Gerraty *out_file = file; 1166b0c40a00SSimon J. Gerraty return true; 1167dba7b0efSSimon J. Gerraty } 1168dba7b0efSSimon J. Gerraty 116906b9b3e0SSimon J. Gerraty /* 117006b9b3e0SSimon J. Gerraty * Find the file with the given name along the given search path. 11713955d011SMarcel Moolenaar * 1172956e45f6SSimon J. Gerraty * If the file is found in a directory that is not on the path 11733955d011SMarcel Moolenaar * already (either 'name' is absolute or it is a relative path 11743955d011SMarcel Moolenaar * [ dir1/.../dirn/file ] which exists below one of the directories 11753955d011SMarcel Moolenaar * already on the search path), its directory is added to the end 1176956e45f6SSimon J. Gerraty * of the path, on the assumption that there will be more files in 11773955d011SMarcel Moolenaar * that directory later on. Sometimes this is true. Sometimes not. 1178956e45f6SSimon J. Gerraty * 1179956e45f6SSimon J. Gerraty * Input: 1180956e45f6SSimon J. Gerraty * name the file to find 1181956e45f6SSimon J. Gerraty * path the directories to search, or NULL 1182956e45f6SSimon J. Gerraty * 1183956e45f6SSimon J. Gerraty * Results: 1184956e45f6SSimon J. Gerraty * The freshly allocated path to the file, or NULL. 11853955d011SMarcel Moolenaar */ 11863955d011SMarcel Moolenaar char * 1187956e45f6SSimon J. Gerraty Dir_FindFile(const char *name, SearchPath *path) 11883955d011SMarcel Moolenaar { 11893955d011SMarcel Moolenaar char *file; /* the current filename to check */ 1190b0c40a00SSimon J. Gerraty bool seenDotLast = false; /* true if we should search dot last */ 1191e2eeea75SSimon J. Gerraty struct cached_stat cst; /* Buffer for stat, if necessary */ 11923955d011SMarcel Moolenaar const char *trailing_dot = "."; 119306b9b3e0SSimon J. Gerraty const char *base = str_basename(name); 11943955d011SMarcel Moolenaar 119506b9b3e0SSimon J. Gerraty DEBUG1(DIR, "Searching for %s ...", name); 11963955d011SMarcel Moolenaar 11972c3632d1SSimon J. Gerraty if (path == NULL) { 119806b9b3e0SSimon J. Gerraty DEBUG0(DIR, "couldn't open path, file not found\n"); 1199956e45f6SSimon J. Gerraty misses++; 12003955d011SMarcel Moolenaar return NULL; 12013955d011SMarcel Moolenaar } 12023955d011SMarcel Moolenaar 1203dba7b0efSSimon J. Gerraty if (path->dirs.first != NULL) { 1204dba7b0efSSimon J. Gerraty CachedDir *dir = path->dirs.first->datum; 1205956e45f6SSimon J. Gerraty if (dir == dotLast) { 1206b0c40a00SSimon J. Gerraty seenDotLast = true; 120706b9b3e0SSimon J. Gerraty DEBUG0(DIR, "[dot last]..."); 12083955d011SMarcel Moolenaar } 12093955d011SMarcel Moolenaar } 121006b9b3e0SSimon J. Gerraty DEBUG0(DIR, "\n"); 12113955d011SMarcel Moolenaar 12123955d011SMarcel Moolenaar /* 12133955d011SMarcel Moolenaar * If there's no leading directory components or if the leading 12143955d011SMarcel Moolenaar * directory component is exactly `./', consult the cached contents 12153955d011SMarcel Moolenaar * of each of the directories on the search path. 12163955d011SMarcel Moolenaar */ 121706b9b3e0SSimon J. Gerraty if (base == name || (base - name == 2 && *name == '.')) { 121806b9b3e0SSimon J. Gerraty SearchPathNode *ln; 121906b9b3e0SSimon J. Gerraty 12203955d011SMarcel Moolenaar /* 122106b9b3e0SSimon J. Gerraty * We look through all the directories on the path seeking one 122206b9b3e0SSimon J. Gerraty * which contains the final component of the given name. If 1223dba7b0efSSimon J. Gerraty * such a file is found, we concatenate the directory name 122406b9b3e0SSimon J. Gerraty * and the final component and return the resulting string. 122506b9b3e0SSimon J. Gerraty * If we don't find any such thing, we go on to phase two. 12263955d011SMarcel Moolenaar * 12273955d011SMarcel Moolenaar * No matter what, we always look for the file in the current 12283955d011SMarcel Moolenaar * directory before anywhere else (unless we found the magic 122906b9b3e0SSimon J. Gerraty * DOTLAST path, in which case we search it last) and we *do 123006b9b3e0SSimon J. Gerraty * not* add the ./ to it if it exists. 12313955d011SMarcel Moolenaar * This is so there are no conflicts between what the user 12323955d011SMarcel Moolenaar * specifies (fish.c) and what pmake finds (./fish.c). 12333955d011SMarcel Moolenaar */ 123406b9b3e0SSimon J. Gerraty if (!seenDotLast && (file = DirFindDot(name, base)) != NULL) 12353955d011SMarcel Moolenaar return file; 12363955d011SMarcel Moolenaar 1237dba7b0efSSimon J. Gerraty for (ln = path->dirs.first; ln != NULL; ln = ln->next) { 1238956e45f6SSimon J. Gerraty CachedDir *dir = ln->datum; 1239956e45f6SSimon J. Gerraty if (dir == dotLast) 12403955d011SMarcel Moolenaar continue; 1241956e45f6SSimon J. Gerraty if ((file = DirLookup(dir, base)) != NULL) 12423955d011SMarcel Moolenaar return file; 12433955d011SMarcel Moolenaar } 12443955d011SMarcel Moolenaar 124506b9b3e0SSimon J. Gerraty if (seenDotLast && (file = DirFindDot(name, base)) != NULL) 12463955d011SMarcel Moolenaar return file; 12473955d011SMarcel Moolenaar } 12483955d011SMarcel Moolenaar 12493955d011SMarcel Moolenaar /* 12503955d011SMarcel Moolenaar * We didn't find the file on any directory in the search path. 12513955d011SMarcel Moolenaar * If the name doesn't contain a slash, that means it doesn't exist. 12523955d011SMarcel Moolenaar * If it *does* contain a slash, however, there is still hope: it 12533955d011SMarcel Moolenaar * could be in a subdirectory of one of the members of the search 12543955d011SMarcel Moolenaar * path. (eg. /usr/include and sys/types.h. The above search would 12553955d011SMarcel Moolenaar * fail to turn up types.h in /usr/include, but it *is* in 12563955d011SMarcel Moolenaar * /usr/include/sys/types.h). 1257dba7b0efSSimon J. Gerraty * [ This no longer applies: If we find such a file, we assume there 12583955d011SMarcel Moolenaar * will be more (what else can we assume?) and add all but the last 12593955d011SMarcel Moolenaar * component of the resulting name onto the search path (at the 12603955d011SMarcel Moolenaar * end).] 12613955d011SMarcel Moolenaar * This phase is only performed if the file is *not* absolute. 12623955d011SMarcel Moolenaar */ 126306b9b3e0SSimon J. Gerraty if (base == name) { 126406b9b3e0SSimon J. Gerraty DEBUG0(DIR, " failed.\n"); 1265956e45f6SSimon J. Gerraty misses++; 12663955d011SMarcel Moolenaar return NULL; 12673955d011SMarcel Moolenaar } 12683955d011SMarcel Moolenaar 1269956e45f6SSimon J. Gerraty if (*base == '\0') { 12703955d011SMarcel Moolenaar /* we were given a trailing "/" */ 1271956e45f6SSimon J. Gerraty base = trailing_dot; 12723955d011SMarcel Moolenaar } 12733955d011SMarcel Moolenaar 12743955d011SMarcel Moolenaar if (name[0] != '/') { 1275dba7b0efSSimon J. Gerraty if (FindFileRelative(path, seenDotLast, name, &file)) 12763955d011SMarcel Moolenaar return file; 1277dba7b0efSSimon J. Gerraty } else { 1278dba7b0efSSimon J. Gerraty if (FindFileAbsolute(path, seenDotLast, name, base, &file)) 12793955d011SMarcel Moolenaar return file; 12803955d011SMarcel Moolenaar } 12813955d011SMarcel Moolenaar 12823955d011SMarcel Moolenaar /* 12833955d011SMarcel Moolenaar * Didn't find it that way, either. Sigh. Phase 3. Add its directory 12843955d011SMarcel Moolenaar * onto the search path in any case, just in case, then look for the 12853955d011SMarcel Moolenaar * thing in the hash table. If we find it, grand. We return a new 12863955d011SMarcel Moolenaar * copy of the name. Otherwise we sadly return a NULL pointer. Sigh. 128706b9b3e0SSimon J. Gerraty * Note that if the directory holding the file doesn't exist, this 128806b9b3e0SSimon J. Gerraty * will do an extra search of the final directory on the path. Unless 128906b9b3e0SSimon J. Gerraty * something weird happens, this search won't succeed and life will 129006b9b3e0SSimon J. Gerraty * be groovy. 12913955d011SMarcel Moolenaar * 12923955d011SMarcel Moolenaar * Sigh. We cannot add the directory onto the search path because 12933955d011SMarcel Moolenaar * of this amusing case: 12943955d011SMarcel Moolenaar * $(INSTALLDIR)/$(FILE): $(FILE) 12953955d011SMarcel Moolenaar * 12963955d011SMarcel Moolenaar * $(FILE) exists in $(INSTALLDIR) but not in the current one. 12973955d011SMarcel Moolenaar * When searching for $(FILE), we will find it in $(INSTALLDIR) 12983955d011SMarcel Moolenaar * b/c we added it here. This is not good... 12993955d011SMarcel Moolenaar */ 1300e2eeea75SSimon J. Gerraty #if 0 130106b9b3e0SSimon J. Gerraty { 130206b9b3e0SSimon J. Gerraty CachedDir *dir; 130306b9b3e0SSimon J. Gerraty char *prefix; 130406b9b3e0SSimon J. Gerraty 1305956e45f6SSimon J. Gerraty if (base == trailing_dot) { 1306956e45f6SSimon J. Gerraty base = strrchr(name, '/'); 1307956e45f6SSimon J. Gerraty base++; 13083955d011SMarcel Moolenaar } 130906b9b3e0SSimon J. Gerraty prefix = bmake_strsedup(name, base - 1); 1310dba7b0efSSimon J. Gerraty (void)SearchPath_Add(path, prefix); 131106b9b3e0SSimon J. Gerraty free(prefix); 13123955d011SMarcel Moolenaar 1313956e45f6SSimon J. Gerraty bigmisses++; 131406b9b3e0SSimon J. Gerraty if (path->last == NULL) 13153955d011SMarcel Moolenaar return NULL; 13163955d011SMarcel Moolenaar 131706b9b3e0SSimon J. Gerraty dir = path->last->datum; 131806b9b3e0SSimon J. Gerraty if (HashSet_Contains(&dir->files, base)) 13193841c287SSimon J. Gerraty return bmake_strdup(name); 13203955d011SMarcel Moolenaar return NULL; 13213955d011SMarcel Moolenaar } 1322e2eeea75SSimon J. Gerraty #else 132306b9b3e0SSimon J. Gerraty DEBUG1(DIR, " Looking for \"%s\" ...\n", name); 13243955d011SMarcel Moolenaar 1325956e45f6SSimon J. Gerraty bigmisses++; 1326e2eeea75SSimon J. Gerraty if (cached_stat(name, &cst) == 0) { 13273841c287SSimon J. Gerraty return bmake_strdup(name); 13283955d011SMarcel Moolenaar } 13292eae894cSSimon J. Gerraty 133006b9b3e0SSimon J. Gerraty DEBUG0(DIR, " failed. Returning NULL\n"); 13313955d011SMarcel Moolenaar return NULL; 1332e2eeea75SSimon J. Gerraty #endif 13333955d011SMarcel Moolenaar } 13343955d011SMarcel Moolenaar 13353955d011SMarcel Moolenaar 133606b9b3e0SSimon J. Gerraty /* 133706b9b3e0SSimon J. Gerraty * Search for a path starting at a given directory and then working our way 1338956e45f6SSimon J. Gerraty * up towards the root. 13393955d011SMarcel Moolenaar * 13403955d011SMarcel Moolenaar * Input: 13413955d011SMarcel Moolenaar * here starting directory 1342956e45f6SSimon J. Gerraty * search_path the relative path we are looking for 13433955d011SMarcel Moolenaar * 13443955d011SMarcel Moolenaar * Results: 1345956e45f6SSimon J. Gerraty * The found path, or NULL. 13463955d011SMarcel Moolenaar */ 1347956e45f6SSimon J. Gerraty char * 1348956e45f6SSimon J. Gerraty Dir_FindHereOrAbove(const char *here, const char *search_path) 13492c3632d1SSimon J. Gerraty { 1350e2eeea75SSimon J. Gerraty struct cached_stat cst; 1351956e45f6SSimon J. Gerraty char *dirbase, *dirbase_end; 1352956e45f6SSimon J. Gerraty char *try, *try_end; 13533955d011SMarcel Moolenaar 13543955d011SMarcel Moolenaar /* copy out our starting point */ 1355956e45f6SSimon J. Gerraty dirbase = bmake_strdup(here); 13562c3632d1SSimon J. Gerraty dirbase_end = dirbase + strlen(dirbase); 13573955d011SMarcel Moolenaar 13583955d011SMarcel Moolenaar /* loop until we determine a result */ 1359956e45f6SSimon J. Gerraty for (;;) { 13603955d011SMarcel Moolenaar 13613955d011SMarcel Moolenaar /* try and stat(2) it ... */ 1362956e45f6SSimon J. Gerraty try = str_concat3(dirbase, "/", search_path); 1363e2eeea75SSimon J. Gerraty if (cached_stat(try, &cst) != -1) { 13643955d011SMarcel Moolenaar /* 13653955d011SMarcel Moolenaar * success! if we found a file, chop off 13663955d011SMarcel Moolenaar * the filename so we return a directory. 13673955d011SMarcel Moolenaar */ 1368e2eeea75SSimon J. Gerraty if ((cst.cst_mode & S_IFMT) != S_IFDIR) { 13693955d011SMarcel Moolenaar try_end = try + strlen(try); 13703955d011SMarcel Moolenaar while (try_end > try && *try_end != '/') 13713955d011SMarcel Moolenaar try_end--; 13723955d011SMarcel Moolenaar if (try_end > try) 13732c3632d1SSimon J. Gerraty *try_end = '\0'; /* chop! */ 13743955d011SMarcel Moolenaar } 13753955d011SMarcel Moolenaar 1376956e45f6SSimon J. Gerraty free(dirbase); 1377956e45f6SSimon J. Gerraty return try; 13783955d011SMarcel Moolenaar } 1379956e45f6SSimon J. Gerraty free(try); 13803955d011SMarcel Moolenaar 13813955d011SMarcel Moolenaar /* 13823955d011SMarcel Moolenaar * nope, we didn't find it. if we used up dirbase we've 13833955d011SMarcel Moolenaar * reached the root and failed. 13843955d011SMarcel Moolenaar */ 13852c3632d1SSimon J. Gerraty if (dirbase_end == dirbase) 13863955d011SMarcel Moolenaar break; /* failed! */ 13873955d011SMarcel Moolenaar 13883955d011SMarcel Moolenaar /* 13893955d011SMarcel Moolenaar * truncate dirbase from the end to move up a dir 13903955d011SMarcel Moolenaar */ 13912c3632d1SSimon J. Gerraty while (dirbase_end > dirbase && *dirbase_end != '/') 13922c3632d1SSimon J. Gerraty dirbase_end--; 13932c3632d1SSimon J. Gerraty *dirbase_end = '\0'; /* chop! */ 1394956e45f6SSimon J. Gerraty } 13953955d011SMarcel Moolenaar 1396956e45f6SSimon J. Gerraty free(dirbase); 1397956e45f6SSimon J. Gerraty return NULL; 13983955d011SMarcel Moolenaar } 13993955d011SMarcel Moolenaar 140006b9b3e0SSimon J. Gerraty /* 140106b9b3e0SSimon J. Gerraty * This is an implied source, and it may have moved, 140206b9b3e0SSimon J. Gerraty * see if we can find it via the current .PATH 140306b9b3e0SSimon J. Gerraty */ 140406b9b3e0SSimon J. Gerraty static char * 140506b9b3e0SSimon J. Gerraty ResolveMovedDepends(GNode *gn) 140606b9b3e0SSimon J. Gerraty { 140706b9b3e0SSimon J. Gerraty char *fullName; 140806b9b3e0SSimon J. Gerraty 140906b9b3e0SSimon J. Gerraty const char *base = str_basename(gn->name); 141006b9b3e0SSimon J. Gerraty if (base == gn->name) 141106b9b3e0SSimon J. Gerraty return NULL; 141206b9b3e0SSimon J. Gerraty 141306b9b3e0SSimon J. Gerraty fullName = Dir_FindFile(base, Suff_FindPath(gn)); 141406b9b3e0SSimon J. Gerraty if (fullName == NULL) 141506b9b3e0SSimon J. Gerraty return NULL; 141606b9b3e0SSimon J. Gerraty 141706b9b3e0SSimon J. Gerraty /* 141806b9b3e0SSimon J. Gerraty * Put the found file in gn->path so that we give that to the compiler. 141906b9b3e0SSimon J. Gerraty */ 142006b9b3e0SSimon J. Gerraty /* 142106b9b3e0SSimon J. Gerraty * XXX: Better just reset gn->path to NULL; updating it is already done 142206b9b3e0SSimon J. Gerraty * by Dir_UpdateMTime. 142306b9b3e0SSimon J. Gerraty */ 142406b9b3e0SSimon J. Gerraty gn->path = bmake_strdup(fullName); 142506b9b3e0SSimon J. Gerraty if (!Job_RunTarget(".STALE", gn->fname)) 142606b9b3e0SSimon J. Gerraty fprintf(stdout, /* XXX: Why stdout? */ 142706b9b3e0SSimon J. Gerraty "%s: %s, %d: ignoring stale %s for %s, found %s\n", 142806b9b3e0SSimon J. Gerraty progname, gn->fname, gn->lineno, 142906b9b3e0SSimon J. Gerraty makeDependfile, gn->name, fullName); 143006b9b3e0SSimon J. Gerraty 143106b9b3e0SSimon J. Gerraty return fullName; 143206b9b3e0SSimon J. Gerraty } 143306b9b3e0SSimon J. Gerraty 143406b9b3e0SSimon J. Gerraty static char * 143506b9b3e0SSimon J. Gerraty ResolveFullName(GNode *gn) 143606b9b3e0SSimon J. Gerraty { 143706b9b3e0SSimon J. Gerraty char *fullName; 143806b9b3e0SSimon J. Gerraty 143906b9b3e0SSimon J. Gerraty fullName = gn->path; 144006b9b3e0SSimon J. Gerraty if (fullName == NULL && !(gn->type & OP_NOPATH)) { 144106b9b3e0SSimon J. Gerraty 144206b9b3e0SSimon J. Gerraty fullName = Dir_FindFile(gn->name, Suff_FindPath(gn)); 144306b9b3e0SSimon J. Gerraty 1444*12904384SSimon J. Gerraty if (fullName == NULL && gn->flags.fromDepend && 144506b9b3e0SSimon J. Gerraty !Lst_IsEmpty(&gn->implicitParents)) 144606b9b3e0SSimon J. Gerraty fullName = ResolveMovedDepends(gn); 144706b9b3e0SSimon J. Gerraty 144806b9b3e0SSimon J. Gerraty DEBUG2(DIR, "Found '%s' as '%s'\n", 144906b9b3e0SSimon J. Gerraty gn->name, fullName != NULL ? fullName : "(not found)"); 145006b9b3e0SSimon J. Gerraty } 145106b9b3e0SSimon J. Gerraty 145206b9b3e0SSimon J. Gerraty if (fullName == NULL) 145306b9b3e0SSimon J. Gerraty fullName = bmake_strdup(gn->name); 145406b9b3e0SSimon J. Gerraty 145506b9b3e0SSimon J. Gerraty /* XXX: Is every piece of memory freed as it should? */ 145606b9b3e0SSimon J. Gerraty 145706b9b3e0SSimon J. Gerraty return fullName; 145806b9b3e0SSimon J. Gerraty } 145906b9b3e0SSimon J. Gerraty 146006b9b3e0SSimon J. Gerraty /* 146106b9b3e0SSimon J. Gerraty * Search gn along dirSearchPath and store its modification time in gn->mtime. 1462e2eeea75SSimon J. Gerraty * If no file is found, store 0 instead. 14633955d011SMarcel Moolenaar * 146406b9b3e0SSimon J. Gerraty * The found file is stored in gn->path, unless the node already had a path. 146506b9b3e0SSimon J. Gerraty */ 1466e2eeea75SSimon J. Gerraty void 1467*12904384SSimon J. Gerraty Dir_UpdateMTime(GNode *gn, bool forceRefresh) 14683955d011SMarcel Moolenaar { 1469e2eeea75SSimon J. Gerraty char *fullName; 1470e2eeea75SSimon J. Gerraty struct cached_stat cst; 14713955d011SMarcel Moolenaar 14723955d011SMarcel Moolenaar if (gn->type & OP_ARCHV) { 1473e2eeea75SSimon J. Gerraty Arch_UpdateMTime(gn); 1474e2eeea75SSimon J. Gerraty return; 1475e2eeea75SSimon J. Gerraty } 1476e2eeea75SSimon J. Gerraty 1477e2eeea75SSimon J. Gerraty if (gn->type & OP_PHONY) { 14783955d011SMarcel Moolenaar gn->mtime = 0; 1479e2eeea75SSimon J. Gerraty return; 1480e2eeea75SSimon J. Gerraty } 1481e2eeea75SSimon J. Gerraty 148206b9b3e0SSimon J. Gerraty fullName = ResolveFullName(gn); 14833955d011SMarcel Moolenaar 1484*12904384SSimon J. Gerraty if (cached_stats(fullName, &cst, false, forceRefresh) < 0) { 14853955d011SMarcel Moolenaar if (gn->type & OP_MEMBER) { 14863955d011SMarcel Moolenaar if (fullName != gn->path) 14873955d011SMarcel Moolenaar free(fullName); 1488e2eeea75SSimon J. Gerraty Arch_UpdateMemberMTime(gn); 1489e2eeea75SSimon J. Gerraty return; 14903955d011SMarcel Moolenaar } 1491e2eeea75SSimon J. Gerraty 1492e2eeea75SSimon J. Gerraty cst.cst_mtime = 0; 14933955d011SMarcel Moolenaar } 14943955d011SMarcel Moolenaar 1495956e45f6SSimon J. Gerraty if (fullName != NULL && gn->path == NULL) 14963955d011SMarcel Moolenaar gn->path = fullName; 149706b9b3e0SSimon J. Gerraty /* XXX: else free(fullName)? */ 14983955d011SMarcel Moolenaar 1499e2eeea75SSimon J. Gerraty gn->mtime = cst.cst_mtime; 15003955d011SMarcel Moolenaar } 15013955d011SMarcel Moolenaar 150206b9b3e0SSimon J. Gerraty /* 150306b9b3e0SSimon J. Gerraty * Read the directory and add it to the cache in openDirs. 150406b9b3e0SSimon J. Gerraty * If a path is given, add the directory to that path as well. 150506b9b3e0SSimon J. Gerraty */ 150606b9b3e0SSimon J. Gerraty static CachedDir * 150706b9b3e0SSimon J. Gerraty CacheNewDir(const char *name, SearchPath *path) 150806b9b3e0SSimon J. Gerraty { 150906b9b3e0SSimon J. Gerraty CachedDir *dir = NULL; 151006b9b3e0SSimon J. Gerraty DIR *d; 151106b9b3e0SSimon J. Gerraty struct dirent *dp; 151206b9b3e0SSimon J. Gerraty 151306b9b3e0SSimon J. Gerraty if ((d = opendir(name)) == NULL) { 151406b9b3e0SSimon J. Gerraty DEBUG1(DIR, "Caching %s ... not found\n", name); 151506b9b3e0SSimon J. Gerraty return dir; 151606b9b3e0SSimon J. Gerraty } 151706b9b3e0SSimon J. Gerraty 151806b9b3e0SSimon J. Gerraty DEBUG1(DIR, "Caching %s ...\n", name); 151906b9b3e0SSimon J. Gerraty 152006b9b3e0SSimon J. Gerraty dir = CachedDir_New(name); 152106b9b3e0SSimon J. Gerraty 152206b9b3e0SSimon J. Gerraty while ((dp = readdir(d)) != NULL) { 152306b9b3e0SSimon J. Gerraty 152406b9b3e0SSimon J. Gerraty #if defined(sun) && defined(d_ino) /* d_ino is a sunos4 #define for d_fileno */ 152506b9b3e0SSimon J. Gerraty /* 152606b9b3e0SSimon J. Gerraty * The sun directory library doesn't check for a 0 inode 152706b9b3e0SSimon J. Gerraty * (0-inode slots just take up space), so we have to do 152806b9b3e0SSimon J. Gerraty * it ourselves. 152906b9b3e0SSimon J. Gerraty */ 153006b9b3e0SSimon J. Gerraty if (dp->d_fileno == 0) 153106b9b3e0SSimon J. Gerraty continue; 153206b9b3e0SSimon J. Gerraty #endif /* sun && d_ino */ 153306b9b3e0SSimon J. Gerraty 153406b9b3e0SSimon J. Gerraty (void)HashSet_Add(&dir->files, dp->d_name); 153506b9b3e0SSimon J. Gerraty } 153606b9b3e0SSimon J. Gerraty (void)closedir(d); 153706b9b3e0SSimon J. Gerraty 153806b9b3e0SSimon J. Gerraty OpenDirs_Add(&openDirs, dir); 153906b9b3e0SSimon J. Gerraty if (path != NULL) 1540dba7b0efSSimon J. Gerraty Lst_Append(&path->dirs, CachedDir_Ref(dir)); 154106b9b3e0SSimon J. Gerraty 154206b9b3e0SSimon J. Gerraty DEBUG1(DIR, "Caching %s done\n", name); 154306b9b3e0SSimon J. Gerraty return dir; 154406b9b3e0SSimon J. Gerraty } 154506b9b3e0SSimon J. Gerraty 154606b9b3e0SSimon J. Gerraty /* 154706b9b3e0SSimon J. Gerraty * Read the list of filenames in the directory and store the result 154806b9b3e0SSimon J. Gerraty * in openDirs. 1549956e45f6SSimon J. Gerraty * 1550956e45f6SSimon J. Gerraty * If a path is given, append the directory to that path. 15513955d011SMarcel Moolenaar * 15523955d011SMarcel Moolenaar * Input: 1553956e45f6SSimon J. Gerraty * path The path to which the directory should be 155406b9b3e0SSimon J. Gerraty * added, or NULL to only add the directory to openDirs 1555956e45f6SSimon J. Gerraty * name The name of the directory to add. 1556956e45f6SSimon J. Gerraty * The name is not normalized in any way. 155706b9b3e0SSimon J. Gerraty * Output: 155806b9b3e0SSimon J. Gerraty * result If no path is given and the directory exists, the 155906b9b3e0SSimon J. Gerraty * returned CachedDir has a reference count of 0. It 156006b9b3e0SSimon J. Gerraty * must either be assigned to a variable using 156106b9b3e0SSimon J. Gerraty * CachedDir_Assign or be appended to a SearchPath using 156206b9b3e0SSimon J. Gerraty * Lst_Append and CachedDir_Ref. 15633955d011SMarcel Moolenaar */ 1564956e45f6SSimon J. Gerraty CachedDir * 1565dba7b0efSSimon J. Gerraty SearchPath_Add(SearchPath *path, const char *name) 15663955d011SMarcel Moolenaar { 15673955d011SMarcel Moolenaar 15682c3632d1SSimon J. Gerraty if (path != NULL && strcmp(name, ".DOTLAST") == 0) { 1569956e45f6SSimon J. Gerraty SearchPathNode *ln; 1570956e45f6SSimon J. Gerraty 1571e2eeea75SSimon J. Gerraty /* XXX: Linear search gets slow with thousands of entries. */ 1572dba7b0efSSimon J. Gerraty for (ln = path->dirs.first; ln != NULL; ln = ln->next) { 1573956e45f6SSimon J. Gerraty CachedDir *pathDir = ln->datum; 1574956e45f6SSimon J. Gerraty if (strcmp(pathDir->name, name) == 0) 1575956e45f6SSimon J. Gerraty return pathDir; 1576956e45f6SSimon J. Gerraty } 15772c3632d1SSimon J. Gerraty 1578dba7b0efSSimon J. Gerraty Lst_Prepend(&path->dirs, CachedDir_Ref(dotLast)); 15793955d011SMarcel Moolenaar } 15803955d011SMarcel Moolenaar 158106b9b3e0SSimon J. Gerraty if (path != NULL) { 158206b9b3e0SSimon J. Gerraty /* XXX: Why is OpenDirs only checked if path != NULL? */ 158306b9b3e0SSimon J. Gerraty CachedDir *dir = OpenDirs_Find(&openDirs, name); 1584956e45f6SSimon J. Gerraty if (dir != NULL) { 1585dba7b0efSSimon J. Gerraty if (Lst_FindDatum(&path->dirs, dir) == NULL) 1586dba7b0efSSimon J. Gerraty Lst_Append(&path->dirs, CachedDir_Ref(dir)); 1587956e45f6SSimon J. Gerraty return dir; 15883955d011SMarcel Moolenaar } 158906b9b3e0SSimon J. Gerraty } 15903955d011SMarcel Moolenaar 159106b9b3e0SSimon J. Gerraty return CacheNewDir(name, path); 159206b9b3e0SSimon J. Gerraty } 15932c3632d1SSimon J. Gerraty 15943955d011SMarcel Moolenaar /* 159506b9b3e0SSimon J. Gerraty * Return a copy of dirSearchPath, incrementing the reference counts for 159606b9b3e0SSimon J. Gerraty * the contained directories. 15973955d011SMarcel Moolenaar */ 1598956e45f6SSimon J. Gerraty SearchPath * 1599956e45f6SSimon J. Gerraty Dir_CopyDirSearchPath(void) 16003955d011SMarcel Moolenaar { 160106b9b3e0SSimon J. Gerraty SearchPath *path = SearchPath_New(); 1602956e45f6SSimon J. Gerraty SearchPathNode *ln; 1603dba7b0efSSimon J. Gerraty for (ln = dirSearchPath.dirs.first; ln != NULL; ln = ln->next) { 1604956e45f6SSimon J. Gerraty CachedDir *dir = ln->datum; 1605dba7b0efSSimon J. Gerraty Lst_Append(&path->dirs, CachedDir_Ref(dir)); 1606956e45f6SSimon J. Gerraty } 1607956e45f6SSimon J. Gerraty return path; 16083955d011SMarcel Moolenaar } 16093955d011SMarcel Moolenaar 161006b9b3e0SSimon J. Gerraty /* 161106b9b3e0SSimon J. Gerraty * Make a string by taking all the directories in the given search path and 161206b9b3e0SSimon J. Gerraty * preceding them by the given flag. Used by the suffix module to create 161306b9b3e0SSimon J. Gerraty * variables for compilers based on suffix search paths. 16143955d011SMarcel Moolenaar * 16153955d011SMarcel Moolenaar * Input: 16163955d011SMarcel Moolenaar * flag flag which should precede each directory 16173955d011SMarcel Moolenaar * path list of directories 16183955d011SMarcel Moolenaar * 16193955d011SMarcel Moolenaar * Results: 162006b9b3e0SSimon J. Gerraty * The string mentioned above. Note that there is no space between the 162106b9b3e0SSimon J. Gerraty * given flag and each directory. The empty string is returned if things 162206b9b3e0SSimon J. Gerraty * don't go well. 16233955d011SMarcel Moolenaar */ 16243955d011SMarcel Moolenaar char * 1625dba7b0efSSimon J. Gerraty SearchPath_ToFlags(SearchPath *path, const char *flag) 16263955d011SMarcel Moolenaar { 16272c3632d1SSimon J. Gerraty Buffer buf; 1628956e45f6SSimon J. Gerraty SearchPathNode *ln; 16293955d011SMarcel Moolenaar 1630e2eeea75SSimon J. Gerraty Buf_Init(&buf); 16313955d011SMarcel Moolenaar 16322c3632d1SSimon J. Gerraty if (path != NULL) { 1633dba7b0efSSimon J. Gerraty for (ln = path->dirs.first; ln != NULL; ln = ln->next) { 1634956e45f6SSimon J. Gerraty CachedDir *dir = ln->datum; 16352c3632d1SSimon J. Gerraty Buf_AddStr(&buf, " "); 16362c3632d1SSimon J. Gerraty Buf_AddStr(&buf, flag); 1637956e45f6SSimon J. Gerraty Buf_AddStr(&buf, dir->name); 16383955d011SMarcel Moolenaar } 16393955d011SMarcel Moolenaar } 16403955d011SMarcel Moolenaar 1641dba7b0efSSimon J. Gerraty return Buf_DoneData(&buf); 16423955d011SMarcel Moolenaar } 16433955d011SMarcel Moolenaar 164406b9b3e0SSimon J. Gerraty /* Free the search path and all directories mentioned in it. */ 164506b9b3e0SSimon J. Gerraty void 164606b9b3e0SSimon J. Gerraty SearchPath_Free(SearchPath *path) 164706b9b3e0SSimon J. Gerraty { 164806b9b3e0SSimon J. Gerraty SearchPathNode *ln; 164906b9b3e0SSimon J. Gerraty 1650dba7b0efSSimon J. Gerraty for (ln = path->dirs.first; ln != NULL; ln = ln->next) { 165106b9b3e0SSimon J. Gerraty CachedDir *dir = ln->datum; 165206b9b3e0SSimon J. Gerraty CachedDir_Unref(dir); 165306b9b3e0SSimon J. Gerraty } 1654dba7b0efSSimon J. Gerraty Lst_Done(&path->dirs); 1655dba7b0efSSimon J. Gerraty free(path); 165606b9b3e0SSimon J. Gerraty } 165706b9b3e0SSimon J. Gerraty 165806b9b3e0SSimon J. Gerraty /* 165906b9b3e0SSimon J. Gerraty * Clear out all elements from the given search path. 166006b9b3e0SSimon J. Gerraty * The path is set to the empty list but is not destroyed. 16613955d011SMarcel Moolenaar */ 16623955d011SMarcel Moolenaar void 166306b9b3e0SSimon J. Gerraty SearchPath_Clear(SearchPath *path) 16643955d011SMarcel Moolenaar { 1665dba7b0efSSimon J. Gerraty while (!Lst_IsEmpty(&path->dirs)) { 1666dba7b0efSSimon J. Gerraty CachedDir *dir = Lst_Dequeue(&path->dirs); 166706b9b3e0SSimon J. Gerraty CachedDir_Unref(dir); 16683955d011SMarcel Moolenaar } 16693955d011SMarcel Moolenaar } 16703955d011SMarcel Moolenaar 16713955d011SMarcel Moolenaar 167206b9b3e0SSimon J. Gerraty /* 167306b9b3e0SSimon J. Gerraty * Concatenate two paths, adding the second to the end of the first, 167406b9b3e0SSimon J. Gerraty * skipping duplicates. 167506b9b3e0SSimon J. Gerraty */ 16763955d011SMarcel Moolenaar void 167706b9b3e0SSimon J. Gerraty SearchPath_AddAll(SearchPath *dst, SearchPath *src) 16783955d011SMarcel Moolenaar { 1679956e45f6SSimon J. Gerraty SearchPathNode *ln; 16803955d011SMarcel Moolenaar 1681dba7b0efSSimon J. Gerraty for (ln = src->dirs.first; ln != NULL; ln = ln->next) { 1682956e45f6SSimon J. Gerraty CachedDir *dir = ln->datum; 1683dba7b0efSSimon J. Gerraty if (Lst_FindDatum(&dst->dirs, dir) == NULL) 1684dba7b0efSSimon J. Gerraty Lst_Append(&dst->dirs, CachedDir_Ref(dir)); 16853955d011SMarcel Moolenaar } 16863955d011SMarcel Moolenaar } 16873955d011SMarcel Moolenaar 16882c3632d1SSimon J. Gerraty static int 16892c3632d1SSimon J. Gerraty percentage(int num, int den) 16902c3632d1SSimon J. Gerraty { 16912c3632d1SSimon J. Gerraty return den != 0 ? num * 100 / den : 0; 16922c3632d1SSimon J. Gerraty } 16932c3632d1SSimon J. Gerraty 16943955d011SMarcel Moolenaar /********** DEBUG INFO **********/ 16953955d011SMarcel Moolenaar void 16963955d011SMarcel Moolenaar Dir_PrintDirectories(void) 16973955d011SMarcel Moolenaar { 1698956e45f6SSimon J. Gerraty CachedDirListNode *ln; 16993955d011SMarcel Moolenaar 1700956e45f6SSimon J. Gerraty debug_printf("#*** Directory Cache:\n"); 170106b9b3e0SSimon J. Gerraty debug_printf( 170206b9b3e0SSimon J. Gerraty "# Stats: %d hits %d misses %d near misses %d losers (%d%%)\n", 17033955d011SMarcel Moolenaar hits, misses, nearmisses, bigmisses, 17042c3632d1SSimon J. Gerraty percentage(hits, hits + bigmisses + nearmisses)); 170506b9b3e0SSimon J. Gerraty debug_printf("# refs hits directory\n"); 17062c3632d1SSimon J. Gerraty 170706b9b3e0SSimon J. Gerraty for (ln = openDirs.list.first; ln != NULL; ln = ln->next) { 1708956e45f6SSimon J. Gerraty CachedDir *dir = ln->datum; 170906b9b3e0SSimon J. Gerraty debug_printf("# %4d %4d %s\n", 171006b9b3e0SSimon J. Gerraty dir->refCount, dir->hits, dir->name); 17113955d011SMarcel Moolenaar } 17123955d011SMarcel Moolenaar } 17133955d011SMarcel Moolenaar 17143955d011SMarcel Moolenaar void 1715dba7b0efSSimon J. Gerraty SearchPath_Print(const SearchPath *path) 17163955d011SMarcel Moolenaar { 171706b9b3e0SSimon J. Gerraty SearchPathNode *ln; 171806b9b3e0SSimon J. Gerraty 1719dba7b0efSSimon J. Gerraty for (ln = path->dirs.first; ln != NULL; ln = ln->next) { 172006b9b3e0SSimon J. Gerraty const CachedDir *dir = ln->datum; 1721956e45f6SSimon J. Gerraty debug_printf("%s ", dir->name); 1722956e45f6SSimon J. Gerraty } 17233955d011SMarcel Moolenaar } 1724