1321964Ssjg/* $NetBSD: dir.c,v 1.71 2017/04/16 21:14:47 riastradh Exp $ */ 2236769Sobrien 3236769Sobrien/* 4236769Sobrien * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. 5236769Sobrien * All rights reserved. 6236769Sobrien * 7236769Sobrien * This code is derived from software contributed to Berkeley by 8236769Sobrien * Adam de Boor. 9236769Sobrien * 10236769Sobrien * Redistribution and use in source and binary forms, with or without 11236769Sobrien * modification, are permitted provided that the following conditions 12236769Sobrien * are met: 13236769Sobrien * 1. Redistributions of source code must retain the above copyright 14236769Sobrien * notice, this list of conditions and the following disclaimer. 15236769Sobrien * 2. Redistributions in binary form must reproduce the above copyright 16236769Sobrien * notice, this list of conditions and the following disclaimer in the 17236769Sobrien * documentation and/or other materials provided with the distribution. 18236769Sobrien * 3. Neither the name of the University nor the names of its contributors 19236769Sobrien * may be used to endorse or promote products derived from this software 20236769Sobrien * without specific prior written permission. 21236769Sobrien * 22236769Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23236769Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24236769Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25236769Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26236769Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27236769Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28236769Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29236769Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30236769Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31236769Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32236769Sobrien * SUCH DAMAGE. 33236769Sobrien */ 34236769Sobrien 35236769Sobrien/* 36236769Sobrien * Copyright (c) 1988, 1989 by Adam de Boor 37236769Sobrien * Copyright (c) 1989 by Berkeley Softworks 38236769Sobrien * All rights reserved. 39236769Sobrien * 40236769Sobrien * This code is derived from software contributed to Berkeley by 41236769Sobrien * Adam de Boor. 42236769Sobrien * 43236769Sobrien * Redistribution and use in source and binary forms, with or without 44236769Sobrien * modification, are permitted provided that the following conditions 45236769Sobrien * are met: 46236769Sobrien * 1. Redistributions of source code must retain the above copyright 47236769Sobrien * notice, this list of conditions and the following disclaimer. 48236769Sobrien * 2. Redistributions in binary form must reproduce the above copyright 49236769Sobrien * notice, this list of conditions and the following disclaimer in the 50236769Sobrien * documentation and/or other materials provided with the distribution. 51236769Sobrien * 3. All advertising materials mentioning features or use of this software 52236769Sobrien * must display the following acknowledgement: 53236769Sobrien * This product includes software developed by the University of 54236769Sobrien * California, Berkeley and its contributors. 55236769Sobrien * 4. Neither the name of the University nor the names of its contributors 56236769Sobrien * may be used to endorse or promote products derived from this software 57236769Sobrien * without specific prior written permission. 58236769Sobrien * 59236769Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 60236769Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 61236769Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 62236769Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 63236769Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 64236769Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 65236769Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 66236769Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 67236769Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 68236769Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 69236769Sobrien * SUCH DAMAGE. 70236769Sobrien */ 71236769Sobrien 72236769Sobrien#ifndef MAKE_NATIVE 73321964Ssjgstatic char rcsid[] = "$NetBSD: dir.c,v 1.71 2017/04/16 21:14:47 riastradh Exp $"; 74236769Sobrien#else 75236769Sobrien#include <sys/cdefs.h> 76236769Sobrien#ifndef lint 77236769Sobrien#if 0 78236769Sobrienstatic char sccsid[] = "@(#)dir.c 8.2 (Berkeley) 1/2/94"; 79236769Sobrien#else 80321964Ssjg__RCSID("$NetBSD: dir.c,v 1.71 2017/04/16 21:14:47 riastradh Exp $"); 81236769Sobrien#endif 82236769Sobrien#endif /* not lint */ 83236769Sobrien#endif 84236769Sobrien 85236769Sobrien/*- 86236769Sobrien * dir.c -- 87236769Sobrien * Directory searching using wildcards and/or normal names... 88236769Sobrien * Used both for source wildcarding in the Makefile and for finding 89236769Sobrien * implicit sources. 90236769Sobrien * 91236769Sobrien * The interface for this module is: 92236769Sobrien * Dir_Init Initialize the module. 93236769Sobrien * 94236769Sobrien * Dir_InitCur Set the cur Path. 95236769Sobrien * 96236769Sobrien * Dir_InitDot Set the dot Path. 97236769Sobrien * 98236769Sobrien * Dir_End Cleanup the module. 99236769Sobrien * 100236769Sobrien * Dir_SetPATH Set ${.PATH} to reflect state of dirSearchPath. 101236769Sobrien * 102236769Sobrien * Dir_HasWildcards Returns TRUE if the name given it needs to 103236769Sobrien * be wildcard-expanded. 104236769Sobrien * 105236769Sobrien * Dir_Expand Given a pattern and a path, return a Lst of names 106236769Sobrien * which match the pattern on the search path. 107236769Sobrien * 108236769Sobrien * Dir_FindFile Searches for a file on a given search path. 109236769Sobrien * If it exists, the entire path is returned. 110236769Sobrien * Otherwise NULL is returned. 111236769Sobrien * 112236769Sobrien * Dir_FindHereOrAbove Search for a path in the current directory and 113236769Sobrien * then all the directories above it in turn until 114236769Sobrien * the path is found or we reach the root ("/"). 115236769Sobrien * 116236769Sobrien * Dir_MTime Return the modification time of a node. The file 117236769Sobrien * is searched for along the default search path. 118236769Sobrien * The path and mtime fields of the node are filled 119236769Sobrien * in. 120236769Sobrien * 121236769Sobrien * Dir_AddDir Add a directory to a search path. 122236769Sobrien * 123236769Sobrien * Dir_MakeFlags Given a search path and a command flag, create 124236769Sobrien * a string with each of the directories in the path 125236769Sobrien * preceded by the command flag and all of them 126236769Sobrien * separated by a space. 127236769Sobrien * 128236769Sobrien * Dir_Destroy Destroy an element of a search path. Frees up all 129236769Sobrien * things that can be freed for the element as long 130236769Sobrien * as the element is no longer referenced by any other 131236769Sobrien * search path. 132236769Sobrien * Dir_ClearPath Resets a search path to the empty list. 133236769Sobrien * 134236769Sobrien * For debugging: 135236769Sobrien * Dir_PrintDirectories Print stats about the directory cache. 136236769Sobrien */ 137236769Sobrien 138236769Sobrien#include <sys/types.h> 139236769Sobrien#include <sys/stat.h> 140236769Sobrien 141236769Sobrien#include <dirent.h> 142236769Sobrien#include <errno.h> 143236769Sobrien#include <stdio.h> 144236769Sobrien 145236769Sobrien#include "make.h" 146236769Sobrien#include "hash.h" 147236769Sobrien#include "dir.h" 148249033Ssjg#include "job.h" 149236769Sobrien 150236769Sobrien/* 151236769Sobrien * A search path consists of a Lst of Path structures. A Path structure 152236769Sobrien * has in it the name of the directory and a hash table of all the files 153236769Sobrien * in the directory. This is used to cut down on the number of system 154236769Sobrien * calls necessary to find implicit dependents and their like. Since 155236769Sobrien * these searches are made before any actions are taken, we need not 156236769Sobrien * worry about the directory changing due to creation commands. If this 157236769Sobrien * hampers the style of some makefiles, they must be changed. 158236769Sobrien * 159236769Sobrien * A list of all previously-read directories is kept in the 160236769Sobrien * openDirectories Lst. This list is checked first before a directory 161236769Sobrien * is opened. 162236769Sobrien * 163236769Sobrien * The need for the caching of whole directories is brought about by 164236769Sobrien * the multi-level transformation code in suff.c, which tends to search 165236769Sobrien * for far more files than regular make does. In the initial 166236769Sobrien * implementation, the amount of time spent performing "stat" calls was 167236769Sobrien * truly astronomical. The problem with hashing at the start is, 168236769Sobrien * of course, that pmake doesn't then detect changes to these directories 169236769Sobrien * during the course of the make. Three possibilities suggest themselves: 170236769Sobrien * 171236769Sobrien * 1) just use stat to test for a file's existence. As mentioned 172236769Sobrien * above, this is very inefficient due to the number of checks 173236769Sobrien * engendered by the multi-level transformation code. 174236769Sobrien * 2) use readdir() and company to search the directories, keeping 175236769Sobrien * them open between checks. I have tried this and while it 176236769Sobrien * didn't slow down the process too much, it could severely 177236769Sobrien * affect the amount of parallelism available as each directory 178236769Sobrien * open would take another file descriptor out of play for 179236769Sobrien * handling I/O for another job. Given that it is only recently 180236769Sobrien * that UNIX OS's have taken to allowing more than 20 or 32 181236769Sobrien * file descriptors for a process, this doesn't seem acceptable 182236769Sobrien * to me. 183236769Sobrien * 3) record the mtime of the directory in the Path structure and 184236769Sobrien * verify the directory hasn't changed since the contents were 185236769Sobrien * hashed. This will catch the creation or deletion of files, 186236769Sobrien * but not the updating of files. However, since it is the 187236769Sobrien * creation and deletion that is the problem, this could be 188236769Sobrien * a good thing to do. Unfortunately, if the directory (say ".") 189236769Sobrien * were fairly large and changed fairly frequently, the constant 190236769Sobrien * rehashing could seriously degrade performance. It might be 191236769Sobrien * good in such cases to keep track of the number of rehashes 192236769Sobrien * and if the number goes over a (small) limit, resort to using 193236769Sobrien * stat in its place. 194236769Sobrien * 195236769Sobrien * An additional thing to consider is that pmake is used primarily 196236769Sobrien * to create C programs and until recently pcc-based compilers refused 197236769Sobrien * to allow you to specify where the resulting object file should be 198236769Sobrien * placed. This forced all objects to be created in the current 199236769Sobrien * directory. This isn't meant as a full excuse, just an explanation of 200236769Sobrien * some of the reasons for the caching used here. 201236769Sobrien * 202236769Sobrien * One more note: the location of a target's file is only performed 203236769Sobrien * on the downward traversal of the graph and then only for terminal 204236769Sobrien * nodes in the graph. This could be construed as wrong in some cases, 205236769Sobrien * but prevents inadvertent modification of files when the "installed" 206236769Sobrien * directory for a file is provided in the search path. 207236769Sobrien * 208236769Sobrien * Another data structure maintained by this module is an mtime 209236769Sobrien * cache used when the searching of cached directories fails to find 210236769Sobrien * a file. In the past, Dir_FindFile would simply perform an access() 211236769Sobrien * call in such a case to determine if the file could be found using 212236769Sobrien * just the name given. When this hit, however, all that was gained 213236769Sobrien * was the knowledge that the file existed. Given that an access() is 214236769Sobrien * essentially a stat() without the copyout() call, and that the same 215236769Sobrien * filesystem overhead would have to be incurred in Dir_MTime, it made 216236769Sobrien * sense to replace the access() with a stat() and record the mtime 217236769Sobrien * in a cache for when Dir_MTime was actually called. 218236769Sobrien */ 219236769Sobrien 220236769SobrienLst dirSearchPath; /* main search path */ 221236769Sobrien 222236769Sobrienstatic Lst openDirectories; /* the list of all open directories */ 223236769Sobrien 224236769Sobrien/* 225236769Sobrien * Variables for gathering statistics on the efficiency of the hashing 226236769Sobrien * mechanism. 227236769Sobrien */ 228236769Sobrienstatic int hits, /* Found in directory cache */ 229236769Sobrien misses, /* Sad, but not evil misses */ 230236769Sobrien nearmisses, /* Found under search path */ 231236769Sobrien bigmisses; /* Sought by itself */ 232236769Sobrien 233236769Sobrienstatic Path *dot; /* contents of current directory */ 234236769Sobrienstatic Path *cur; /* contents of current directory, if not dot */ 235236769Sobrienstatic Path *dotLast; /* a fake path entry indicating we need to 236236769Sobrien * look for . last */ 237236769Sobrienstatic Hash_Table mtimes; /* Results of doing a last-resort stat in 238236769Sobrien * Dir_FindFile -- if we have to go to the 239236769Sobrien * system to find the file, we might as well 240236769Sobrien * have its mtime on record. XXX: If this is done 241236769Sobrien * way early, there's a chance other rules will 242236769Sobrien * have already updated the file, in which case 243236769Sobrien * we'll update it again. Generally, there won't 244236769Sobrien * be two rules to update a single file, so this 245236769Sobrien * should be ok, but... */ 246236769Sobrien 247321964Ssjgstatic Hash_Table lmtimes; /* same as mtimes but for lstat */ 248236769Sobrien 249236769Sobrienstatic int DirFindName(const void *, const void *); 250236769Sobrienstatic int DirMatchFiles(const char *, Path *, Lst); 251236769Sobrienstatic void DirExpandCurly(const char *, const char *, Lst, Lst); 252236769Sobrienstatic void DirExpandInt(const char *, Lst, Lst); 253236769Sobrienstatic int DirPrintWord(void *, void *); 254236769Sobrienstatic int DirPrintDir(void *, void *); 255236769Sobrienstatic char *DirLookup(Path *, const char *, const char *, Boolean); 256236769Sobrienstatic char *DirLookupSubdir(Path *, const char *); 257236769Sobrienstatic char *DirFindDot(Boolean, const char *, const char *); 258236769Sobrienstatic char *DirLookupAbs(Path *, const char *, const char *); 259236769Sobrien 260321964Ssjg 261321964Ssjg/* 262321964Ssjg * We use stat(2) a lot, cache the results 263321964Ssjg * mtime and mode are all we care about. 264321964Ssjg */ 265321964Ssjgstruct cache_st { 266321964Ssjg time_t mtime; 267321964Ssjg mode_t mode; 268321964Ssjg}; 269321964Ssjg 270321964Ssjg/* minimize changes below */ 271321964Ssjgstatic time_t 272321964SsjgHash_GetTimeValue(Hash_Entry *entry) 273321964Ssjg{ 274321964Ssjg struct cache_st *cst; 275321964Ssjg 276321964Ssjg cst = entry->clientPtr; 277321964Ssjg return cst->mtime; 278321964Ssjg} 279321964Ssjg 280321964Ssjg#define CST_LSTAT 1 281321964Ssjg#define CST_UPDATE 2 282321964Ssjg 283321964Ssjgstatic int 284321964Ssjgcached_stats(Hash_Table *htp, const char *pathname, struct stat *st, int flags) 285321964Ssjg{ 286321964Ssjg Hash_Entry *entry; 287321964Ssjg struct cache_st *cst; 288321964Ssjg int rc; 289321964Ssjg 290321964Ssjg if (!pathname || !pathname[0]) 291321964Ssjg return -1; 292321964Ssjg 293321964Ssjg entry = Hash_FindEntry(htp, pathname); 294321964Ssjg 295321964Ssjg if (entry && (flags & CST_UPDATE) == 0) { 296321964Ssjg cst = entry->clientPtr; 297321964Ssjg 298321964Ssjg memset(st, 0, sizeof(*st)); 299321964Ssjg st->st_mtime = cst->mtime; 300321964Ssjg st->st_mode = cst->mode; 301321964Ssjg return 0; 302321964Ssjg } 303321964Ssjg 304321964Ssjg rc = (flags & CST_LSTAT) ? lstat(pathname, st) : stat(pathname, st); 305321964Ssjg if (rc == -1) 306321964Ssjg return -1; 307321964Ssjg 308321964Ssjg if (st->st_mtime == 0) 309321964Ssjg st->st_mtime = 1; /* avoid confusion with missing file */ 310321964Ssjg 311321964Ssjg if (!entry) 312321964Ssjg entry = Hash_CreateEntry(htp, pathname, NULL); 313321964Ssjg if (!entry->clientPtr) 314321964Ssjg entry->clientPtr = bmake_malloc(sizeof(*cst)); 315321964Ssjg cst = entry->clientPtr; 316321964Ssjg cst->mtime = st->st_mtime; 317321964Ssjg cst->mode = st->st_mode; 318321964Ssjg 319321964Ssjg return 0; 320321964Ssjg} 321321964Ssjg 322321964Ssjgint 323321964Ssjgcached_stat(const char *pathname, void *st) 324321964Ssjg{ 325321964Ssjg return cached_stats(&mtimes, pathname, st, 0); 326321964Ssjg} 327321964Ssjg 328321964Ssjgint 329321964Ssjgcached_lstat(const char *pathname, void *st) 330321964Ssjg{ 331321964Ssjg return cached_stats(&lmtimes, pathname, st, CST_LSTAT); 332321964Ssjg} 333321964Ssjg 334236769Sobrien/*- 335236769Sobrien *----------------------------------------------------------------------- 336236769Sobrien * Dir_Init -- 337236769Sobrien * initialize things for this module 338236769Sobrien * 339236769Sobrien * Results: 340236769Sobrien * none 341236769Sobrien * 342236769Sobrien * Side Effects: 343236769Sobrien * some directories may be opened. 344236769Sobrien *----------------------------------------------------------------------- 345236769Sobrien */ 346236769Sobrienvoid 347236769SobrienDir_Init(const char *cdname) 348236769Sobrien{ 349321964Ssjg if (!cdname) { 350321964Ssjg dirSearchPath = Lst_Init(FALSE); 351321964Ssjg openDirectories = Lst_Init(FALSE); 352321964Ssjg Hash_InitTable(&mtimes, 0); 353321964Ssjg Hash_InitTable(&lmtimes, 0); 354321964Ssjg return; 355321964Ssjg } 356236769Sobrien Dir_InitCur(cdname); 357236769Sobrien 358236769Sobrien dotLast = bmake_malloc(sizeof(Path)); 359236769Sobrien dotLast->refCount = 1; 360236769Sobrien dotLast->hits = 0; 361236769Sobrien dotLast->name = bmake_strdup(".DOTLAST"); 362236769Sobrien Hash_InitTable(&dotLast->files, -1); 363236769Sobrien} 364236769Sobrien 365236769Sobrien/* 366236769Sobrien * Called by Dir_Init() and whenever .CURDIR is assigned to. 367236769Sobrien */ 368236769Sobrienvoid 369236769SobrienDir_InitCur(const char *cdname) 370236769Sobrien{ 371236769Sobrien Path *p; 372236769Sobrien 373236769Sobrien if (cdname != NULL) { 374236769Sobrien /* 375236769Sobrien * Our build directory is not the same as our source directory. 376236769Sobrien * Keep this one around too. 377236769Sobrien */ 378236769Sobrien if ((p = Dir_AddDir(NULL, cdname))) { 379236769Sobrien p->refCount += 1; 380236769Sobrien if (cur && cur != p) { 381236769Sobrien /* 382236769Sobrien * We've been here before, cleanup. 383236769Sobrien */ 384236769Sobrien cur->refCount -= 1; 385236769Sobrien Dir_Destroy(cur); 386236769Sobrien } 387236769Sobrien cur = p; 388236769Sobrien } 389236769Sobrien } 390236769Sobrien} 391236769Sobrien 392236769Sobrien/*- 393236769Sobrien *----------------------------------------------------------------------- 394236769Sobrien * Dir_InitDot -- 395236769Sobrien * (re)initialize "dot" (current/object directory) path hash 396236769Sobrien * 397236769Sobrien * Results: 398236769Sobrien * none 399236769Sobrien * 400236769Sobrien * Side Effects: 401236769Sobrien * some directories may be opened. 402236769Sobrien *----------------------------------------------------------------------- 403236769Sobrien */ 404236769Sobrienvoid 405236769SobrienDir_InitDot(void) 406236769Sobrien{ 407236769Sobrien if (dot != NULL) { 408236769Sobrien LstNode ln; 409236769Sobrien 410236769Sobrien /* Remove old entry from openDirectories, but do not destroy. */ 411236769Sobrien ln = Lst_Member(openDirectories, dot); 412236769Sobrien (void)Lst_Remove(openDirectories, ln); 413236769Sobrien } 414236769Sobrien 415236769Sobrien dot = Dir_AddDir(NULL, "."); 416236769Sobrien 417236769Sobrien if (dot == NULL) { 418236769Sobrien Error("Cannot open `.' (%s)", strerror(errno)); 419236769Sobrien exit(1); 420236769Sobrien } 421236769Sobrien 422236769Sobrien /* 423236769Sobrien * We always need to have dot around, so we increment its reference count 424236769Sobrien * to make sure it's not destroyed. 425236769Sobrien */ 426236769Sobrien dot->refCount += 1; 427236769Sobrien Dir_SetPATH(); /* initialize */ 428236769Sobrien} 429236769Sobrien 430236769Sobrien/*- 431236769Sobrien *----------------------------------------------------------------------- 432236769Sobrien * Dir_End -- 433236769Sobrien * cleanup things for this module 434236769Sobrien * 435236769Sobrien * Results: 436236769Sobrien * none 437236769Sobrien * 438236769Sobrien * Side Effects: 439236769Sobrien * none 440236769Sobrien *----------------------------------------------------------------------- 441236769Sobrien */ 442236769Sobrienvoid 443236769SobrienDir_End(void) 444236769Sobrien{ 445236769Sobrien#ifdef CLEANUP 446236769Sobrien if (cur) { 447236769Sobrien cur->refCount -= 1; 448236769Sobrien Dir_Destroy(cur); 449236769Sobrien } 450236769Sobrien dot->refCount -= 1; 451236769Sobrien dotLast->refCount -= 1; 452236769Sobrien Dir_Destroy(dotLast); 453236769Sobrien Dir_Destroy(dot); 454236769Sobrien Dir_ClearPath(dirSearchPath); 455236769Sobrien Lst_Destroy(dirSearchPath, NULL); 456236769Sobrien Dir_ClearPath(openDirectories); 457236769Sobrien Lst_Destroy(openDirectories, NULL); 458236769Sobrien Hash_DeleteTable(&mtimes); 459236769Sobrien#endif 460236769Sobrien} 461236769Sobrien 462236769Sobrien/* 463236769Sobrien * We want ${.PATH} to indicate the order in which we will actually 464236769Sobrien * search, so we rebuild it after any .PATH: target. 465236769Sobrien * This is the simplest way to deal with the effect of .DOTLAST. 466236769Sobrien */ 467236769Sobrienvoid 468236769SobrienDir_SetPATH(void) 469236769Sobrien{ 470236769Sobrien LstNode ln; /* a list element */ 471236769Sobrien Path *p; 472236769Sobrien Boolean hasLastDot = FALSE; /* true we should search dot last */ 473236769Sobrien 474236769Sobrien Var_Delete(".PATH", VAR_GLOBAL); 475236769Sobrien 476236769Sobrien if (Lst_Open(dirSearchPath) == SUCCESS) { 477236769Sobrien if ((ln = Lst_First(dirSearchPath)) != NULL) { 478236769Sobrien p = (Path *)Lst_Datum(ln); 479236769Sobrien if (p == dotLast) { 480236769Sobrien hasLastDot = TRUE; 481236769Sobrien Var_Append(".PATH", dotLast->name, VAR_GLOBAL); 482236769Sobrien } 483236769Sobrien } 484236769Sobrien 485236769Sobrien if (!hasLastDot) { 486236769Sobrien if (dot) 487236769Sobrien Var_Append(".PATH", dot->name, VAR_GLOBAL); 488236769Sobrien if (cur) 489236769Sobrien Var_Append(".PATH", cur->name, VAR_GLOBAL); 490236769Sobrien } 491236769Sobrien 492236769Sobrien while ((ln = Lst_Next(dirSearchPath)) != NULL) { 493236769Sobrien p = (Path *)Lst_Datum(ln); 494236769Sobrien if (p == dotLast) 495236769Sobrien continue; 496236769Sobrien if (p == dot && hasLastDot) 497236769Sobrien continue; 498236769Sobrien Var_Append(".PATH", p->name, VAR_GLOBAL); 499236769Sobrien } 500236769Sobrien 501236769Sobrien if (hasLastDot) { 502236769Sobrien if (dot) 503236769Sobrien Var_Append(".PATH", dot->name, VAR_GLOBAL); 504236769Sobrien if (cur) 505236769Sobrien Var_Append(".PATH", cur->name, VAR_GLOBAL); 506236769Sobrien } 507236769Sobrien Lst_Close(dirSearchPath); 508236769Sobrien } 509236769Sobrien} 510236769Sobrien 511236769Sobrien/*- 512236769Sobrien *----------------------------------------------------------------------- 513236769Sobrien * DirFindName -- 514236769Sobrien * See if the Path structure describes the same directory as the 515236769Sobrien * given one by comparing their names. Called from Dir_AddDir via 516236769Sobrien * Lst_Find when searching the list of open directories. 517236769Sobrien * 518236769Sobrien * Input: 519236769Sobrien * p Current name 520236769Sobrien * dname Desired name 521236769Sobrien * 522236769Sobrien * Results: 523236769Sobrien * 0 if it is the same. Non-zero otherwise 524236769Sobrien * 525236769Sobrien * Side Effects: 526236769Sobrien * None 527236769Sobrien *----------------------------------------------------------------------- 528236769Sobrien */ 529236769Sobrienstatic int 530236769SobrienDirFindName(const void *p, const void *dname) 531236769Sobrien{ 532236769Sobrien return (strcmp(((const Path *)p)->name, dname)); 533236769Sobrien} 534236769Sobrien 535236769Sobrien/*- 536236769Sobrien *----------------------------------------------------------------------- 537236769Sobrien * Dir_HasWildcards -- 538236769Sobrien * see if the given name has any wildcard characters in it 539236769Sobrien * be careful not to expand unmatching brackets or braces. 540236769Sobrien * XXX: This code is not 100% correct. ([^]] fails etc.) 541236769Sobrien * I really don't think that make(1) should be expanding 542236769Sobrien * patterns, because then you have to set a mechanism for 543236769Sobrien * escaping the expansion! 544236769Sobrien * 545236769Sobrien * Input: 546236769Sobrien * name name to check 547236769Sobrien * 548236769Sobrien * Results: 549236769Sobrien * returns TRUE if the word should be expanded, FALSE otherwise 550236769Sobrien * 551236769Sobrien * Side Effects: 552236769Sobrien * none 553236769Sobrien *----------------------------------------------------------------------- 554236769Sobrien */ 555236769SobrienBoolean 556236769SobrienDir_HasWildcards(char *name) 557236769Sobrien{ 558236769Sobrien char *cp; 559236769Sobrien int wild = 0, brace = 0, bracket = 0; 560236769Sobrien 561236769Sobrien for (cp = name; *cp; cp++) { 562236769Sobrien switch(*cp) { 563236769Sobrien case '{': 564236769Sobrien brace++; 565236769Sobrien wild = 1; 566236769Sobrien break; 567236769Sobrien case '}': 568236769Sobrien brace--; 569236769Sobrien break; 570236769Sobrien case '[': 571236769Sobrien bracket++; 572236769Sobrien wild = 1; 573236769Sobrien break; 574236769Sobrien case ']': 575236769Sobrien bracket--; 576236769Sobrien break; 577236769Sobrien case '?': 578236769Sobrien case '*': 579236769Sobrien wild = 1; 580236769Sobrien break; 581236769Sobrien default: 582236769Sobrien break; 583236769Sobrien } 584236769Sobrien } 585236769Sobrien return wild && bracket == 0 && brace == 0; 586236769Sobrien} 587236769Sobrien 588236769Sobrien/*- 589236769Sobrien *----------------------------------------------------------------------- 590236769Sobrien * DirMatchFiles -- 591236769Sobrien * Given a pattern and a Path structure, see if any files 592236769Sobrien * match the pattern and add their names to the 'expansions' list if 593236769Sobrien * any do. This is incomplete -- it doesn't take care of patterns like 594236769Sobrien * src / *src / *.c properly (just *.c on any of the directories), but it 595236769Sobrien * will do for now. 596236769Sobrien * 597236769Sobrien * Input: 598236769Sobrien * pattern Pattern to look for 599236769Sobrien * p Directory to search 600236769Sobrien * expansion Place to store the results 601236769Sobrien * 602236769Sobrien * Results: 603236769Sobrien * Always returns 0 604236769Sobrien * 605236769Sobrien * Side Effects: 606236769Sobrien * File names are added to the expansions lst. The directory will be 607236769Sobrien * fully hashed when this is done. 608236769Sobrien *----------------------------------------------------------------------- 609236769Sobrien */ 610236769Sobrienstatic int 611236769SobrienDirMatchFiles(const char *pattern, Path *p, Lst expansions) 612236769Sobrien{ 613236769Sobrien Hash_Search search; /* Index into the directory's table */ 614236769Sobrien Hash_Entry *entry; /* Current entry in the table */ 615236769Sobrien Boolean isDot; /* TRUE if the directory being searched is . */ 616236769Sobrien 617236769Sobrien isDot = (*p->name == '.' && p->name[1] == '\0'); 618236769Sobrien 619236769Sobrien for (entry = Hash_EnumFirst(&p->files, &search); 620236769Sobrien entry != NULL; 621236769Sobrien entry = Hash_EnumNext(&search)) 622236769Sobrien { 623236769Sobrien /* 624236769Sobrien * See if the file matches the given pattern. Note we follow the UNIX 625236769Sobrien * convention that dot files will only be found if the pattern 626236769Sobrien * begins with a dot (note also that as a side effect of the hashing 627236769Sobrien * scheme, .* won't match . or .. since they aren't hashed). 628236769Sobrien */ 629236769Sobrien if (Str_Match(entry->name, pattern) && 630236769Sobrien ((entry->name[0] != '.') || 631236769Sobrien (pattern[0] == '.'))) 632236769Sobrien { 633236769Sobrien (void)Lst_AtEnd(expansions, 634236769Sobrien (isDot ? bmake_strdup(entry->name) : 635236769Sobrien str_concat(p->name, entry->name, 636236769Sobrien STR_ADDSLASH))); 637236769Sobrien } 638236769Sobrien } 639236769Sobrien return (0); 640236769Sobrien} 641236769Sobrien 642236769Sobrien/*- 643236769Sobrien *----------------------------------------------------------------------- 644236769Sobrien * DirExpandCurly -- 645236769Sobrien * Expand curly braces like the C shell. Does this recursively. 646236769Sobrien * Note the special case: if after the piece of the curly brace is 647236769Sobrien * done there are no wildcard characters in the result, the result is 648236769Sobrien * placed on the list WITHOUT CHECKING FOR ITS EXISTENCE. 649236769Sobrien * 650236769Sobrien * Input: 651236769Sobrien * word Entire word to expand 652236769Sobrien * brace First curly brace in it 653236769Sobrien * path Search path to use 654236769Sobrien * expansions Place to store the expansions 655236769Sobrien * 656236769Sobrien * Results: 657236769Sobrien * None. 658236769Sobrien * 659236769Sobrien * Side Effects: 660236769Sobrien * The given list is filled with the expansions... 661236769Sobrien * 662236769Sobrien *----------------------------------------------------------------------- 663236769Sobrien */ 664236769Sobrienstatic void 665236769SobrienDirExpandCurly(const char *word, const char *brace, Lst path, Lst expansions) 666236769Sobrien{ 667236769Sobrien const char *end; /* Character after the closing brace */ 668236769Sobrien const char *cp; /* Current position in brace clause */ 669236769Sobrien const char *start; /* Start of current piece of brace clause */ 670236769Sobrien int bracelevel; /* Number of braces we've seen. If we see a 671236769Sobrien * right brace when this is 0, we've hit the 672236769Sobrien * end of the clause. */ 673236769Sobrien char *file; /* Current expansion */ 674236769Sobrien int otherLen; /* The length of the other pieces of the 675236769Sobrien * expansion (chars before and after the 676236769Sobrien * clause in 'word') */ 677236769Sobrien char *cp2; /* Pointer for checking for wildcards in 678236769Sobrien * expansion before calling Dir_Expand */ 679236769Sobrien 680236769Sobrien start = brace+1; 681236769Sobrien 682236769Sobrien /* 683236769Sobrien * Find the end of the brace clause first, being wary of nested brace 684236769Sobrien * clauses. 685236769Sobrien */ 686236769Sobrien for (end = start, bracelevel = 0; *end != '\0'; end++) { 687236769Sobrien if (*end == '{') { 688236769Sobrien bracelevel++; 689236769Sobrien } else if ((*end == '}') && (bracelevel-- == 0)) { 690236769Sobrien break; 691236769Sobrien } 692236769Sobrien } 693236769Sobrien if (*end == '\0') { 694236769Sobrien Error("Unterminated {} clause \"%s\"", start); 695236769Sobrien return; 696236769Sobrien } else { 697236769Sobrien end++; 698236769Sobrien } 699236769Sobrien otherLen = brace - word + strlen(end); 700236769Sobrien 701236769Sobrien for (cp = start; cp < end; cp++) { 702236769Sobrien /* 703236769Sobrien * Find the end of this piece of the clause. 704236769Sobrien */ 705236769Sobrien bracelevel = 0; 706236769Sobrien while (*cp != ',') { 707236769Sobrien if (*cp == '{') { 708236769Sobrien bracelevel++; 709236769Sobrien } else if ((*cp == '}') && (bracelevel-- <= 0)) { 710236769Sobrien break; 711236769Sobrien } 712236769Sobrien cp++; 713236769Sobrien } 714236769Sobrien /* 715236769Sobrien * Allocate room for the combination and install the three pieces. 716236769Sobrien */ 717236769Sobrien file = bmake_malloc(otherLen + cp - start + 1); 718236769Sobrien if (brace != word) { 719236769Sobrien strncpy(file, word, brace-word); 720236769Sobrien } 721236769Sobrien if (cp != start) { 722236769Sobrien strncpy(&file[brace-word], start, cp-start); 723236769Sobrien } 724236769Sobrien strcpy(&file[(brace-word)+(cp-start)], end); 725236769Sobrien 726236769Sobrien /* 727236769Sobrien * See if the result has any wildcards in it. If we find one, call 728236769Sobrien * Dir_Expand right away, telling it to place the result on our list 729236769Sobrien * of expansions. 730236769Sobrien */ 731236769Sobrien for (cp2 = file; *cp2 != '\0'; cp2++) { 732236769Sobrien switch(*cp2) { 733236769Sobrien case '*': 734236769Sobrien case '?': 735236769Sobrien case '{': 736236769Sobrien case '[': 737236769Sobrien Dir_Expand(file, path, expansions); 738236769Sobrien goto next; 739236769Sobrien } 740236769Sobrien } 741236769Sobrien if (*cp2 == '\0') { 742236769Sobrien /* 743236769Sobrien * Hit the end w/o finding any wildcards, so stick the expansion 744236769Sobrien * on the end of the list. 745236769Sobrien */ 746236769Sobrien (void)Lst_AtEnd(expansions, file); 747236769Sobrien } else { 748236769Sobrien next: 749236769Sobrien free(file); 750236769Sobrien } 751236769Sobrien start = cp+1; 752236769Sobrien } 753236769Sobrien} 754236769Sobrien 755236769Sobrien 756236769Sobrien/*- 757236769Sobrien *----------------------------------------------------------------------- 758236769Sobrien * DirExpandInt -- 759236769Sobrien * Internal expand routine. Passes through the directories in the 760236769Sobrien * path one by one, calling DirMatchFiles for each. NOTE: This still 761236769Sobrien * doesn't handle patterns in directories... 762236769Sobrien * 763236769Sobrien * Input: 764236769Sobrien * word Word to expand 765236769Sobrien * path Path on which to look 766236769Sobrien * expansions Place to store the result 767236769Sobrien * 768236769Sobrien * Results: 769236769Sobrien * None. 770236769Sobrien * 771236769Sobrien * Side Effects: 772236769Sobrien * Things are added to the expansions list. 773236769Sobrien * 774236769Sobrien *----------------------------------------------------------------------- 775236769Sobrien */ 776236769Sobrienstatic void 777236769SobrienDirExpandInt(const char *word, Lst path, Lst expansions) 778236769Sobrien{ 779236769Sobrien LstNode ln; /* Current node */ 780236769Sobrien Path *p; /* Directory in the node */ 781236769Sobrien 782236769Sobrien if (Lst_Open(path) == SUCCESS) { 783236769Sobrien while ((ln = Lst_Next(path)) != NULL) { 784236769Sobrien p = (Path *)Lst_Datum(ln); 785236769Sobrien DirMatchFiles(word, p, expansions); 786236769Sobrien } 787236769Sobrien Lst_Close(path); 788236769Sobrien } 789236769Sobrien} 790236769Sobrien 791236769Sobrien/*- 792236769Sobrien *----------------------------------------------------------------------- 793236769Sobrien * DirPrintWord -- 794236769Sobrien * Print a word in the list of expansions. Callback for Dir_Expand 795236769Sobrien * when DEBUG(DIR), via Lst_ForEach. 796236769Sobrien * 797236769Sobrien * Results: 798236769Sobrien * === 0 799236769Sobrien * 800236769Sobrien * Side Effects: 801236769Sobrien * The passed word is printed, followed by a space. 802236769Sobrien * 803236769Sobrien *----------------------------------------------------------------------- 804236769Sobrien */ 805236769Sobrienstatic int 806321964SsjgDirPrintWord(void *word, void *dummy MAKE_ATTR_UNUSED) 807236769Sobrien{ 808236769Sobrien fprintf(debug_file, "%s ", (char *)word); 809236769Sobrien 810321964Ssjg return 0; 811236769Sobrien} 812236769Sobrien 813236769Sobrien/*- 814236769Sobrien *----------------------------------------------------------------------- 815236769Sobrien * Dir_Expand -- 816236769Sobrien * Expand the given word into a list of words by globbing it looking 817236769Sobrien * in the directories on the given search path. 818236769Sobrien * 819236769Sobrien * Input: 820236769Sobrien * word the word to expand 821236769Sobrien * path the list of directories in which to find the 822236769Sobrien * resulting files 823236769Sobrien * expansions the list on which to place the results 824236769Sobrien * 825236769Sobrien * Results: 826236769Sobrien * A list of words consisting of the files which exist along the search 827236769Sobrien * path matching the given pattern. 828236769Sobrien * 829236769Sobrien * Side Effects: 830236769Sobrien * Directories may be opened. Who knows? 831236769Sobrien *----------------------------------------------------------------------- 832236769Sobrien */ 833236769Sobrienvoid 834236769SobrienDir_Expand(const char *word, Lst path, Lst expansions) 835236769Sobrien{ 836236769Sobrien const char *cp; 837236769Sobrien 838236769Sobrien if (DEBUG(DIR)) { 839236769Sobrien fprintf(debug_file, "Expanding \"%s\"... ", word); 840236769Sobrien } 841236769Sobrien 842236769Sobrien cp = strchr(word, '{'); 843236769Sobrien if (cp) { 844236769Sobrien DirExpandCurly(word, cp, path, expansions); 845236769Sobrien } else { 846236769Sobrien cp = strchr(word, '/'); 847236769Sobrien if (cp) { 848236769Sobrien /* 849236769Sobrien * The thing has a directory component -- find the first wildcard 850236769Sobrien * in the string. 851236769Sobrien */ 852236769Sobrien for (cp = word; *cp; cp++) { 853236769Sobrien if (*cp == '?' || *cp == '[' || *cp == '*' || *cp == '{') { 854236769Sobrien break; 855236769Sobrien } 856236769Sobrien } 857236769Sobrien if (*cp == '{') { 858236769Sobrien /* 859236769Sobrien * This one will be fun. 860236769Sobrien */ 861236769Sobrien DirExpandCurly(word, cp, path, expansions); 862236769Sobrien return; 863236769Sobrien } else if (*cp != '\0') { 864236769Sobrien /* 865236769Sobrien * Back up to the start of the component 866236769Sobrien */ 867236769Sobrien char *dirpath; 868236769Sobrien 869236769Sobrien while (cp > word && *cp != '/') { 870236769Sobrien cp--; 871236769Sobrien } 872236769Sobrien if (cp != word) { 873236769Sobrien char sc; 874236769Sobrien /* 875236769Sobrien * If the glob isn't in the first component, try and find 876236769Sobrien * all the components up to the one with a wildcard. 877236769Sobrien */ 878236769Sobrien sc = cp[1]; 879236769Sobrien ((char *)UNCONST(cp))[1] = '\0'; 880236769Sobrien dirpath = Dir_FindFile(word, path); 881236769Sobrien ((char *)UNCONST(cp))[1] = sc; 882236769Sobrien /* 883236769Sobrien * dirpath is null if can't find the leading component 884236769Sobrien * XXX: Dir_FindFile won't find internal components. 885236769Sobrien * i.e. if the path contains ../Etc/Object and we're 886236769Sobrien * looking for Etc, it won't be found. Ah well. 887236769Sobrien * Probably not important. 888236769Sobrien */ 889236769Sobrien if (dirpath != NULL) { 890236769Sobrien char *dp = &dirpath[strlen(dirpath) - 1]; 891236769Sobrien if (*dp == '/') 892236769Sobrien *dp = '\0'; 893236769Sobrien path = Lst_Init(FALSE); 894236769Sobrien (void)Dir_AddDir(path, dirpath); 895236769Sobrien DirExpandInt(cp+1, path, expansions); 896236769Sobrien Lst_Destroy(path, NULL); 897236769Sobrien } 898236769Sobrien } else { 899236769Sobrien /* 900236769Sobrien * Start the search from the local directory 901236769Sobrien */ 902236769Sobrien DirExpandInt(word, path, expansions); 903236769Sobrien } 904236769Sobrien } else { 905236769Sobrien /* 906236769Sobrien * Return the file -- this should never happen. 907236769Sobrien */ 908236769Sobrien DirExpandInt(word, path, expansions); 909236769Sobrien } 910236769Sobrien } else { 911236769Sobrien /* 912236769Sobrien * First the files in dot 913236769Sobrien */ 914236769Sobrien DirMatchFiles(word, dot, expansions); 915236769Sobrien 916236769Sobrien /* 917236769Sobrien * Then the files in every other directory on the path. 918236769Sobrien */ 919236769Sobrien DirExpandInt(word, path, expansions); 920236769Sobrien } 921236769Sobrien } 922236769Sobrien if (DEBUG(DIR)) { 923236769Sobrien Lst_ForEach(expansions, DirPrintWord, NULL); 924236769Sobrien fprintf(debug_file, "\n"); 925236769Sobrien } 926236769Sobrien} 927236769Sobrien 928236769Sobrien/*- 929236769Sobrien *----------------------------------------------------------------------- 930236769Sobrien * DirLookup -- 931236769Sobrien * Find if the file with the given name exists in the given path. 932236769Sobrien * 933236769Sobrien * Results: 934236769Sobrien * The path to the file or NULL. This path is guaranteed to be in a 935236769Sobrien * different part of memory than name and so may be safely free'd. 936236769Sobrien * 937236769Sobrien * Side Effects: 938236769Sobrien * None. 939236769Sobrien *----------------------------------------------------------------------- 940236769Sobrien */ 941236769Sobrienstatic char * 942237578SobrienDirLookup(Path *p, const char *name MAKE_ATTR_UNUSED, const char *cp, 943237578Sobrien Boolean hasSlash MAKE_ATTR_UNUSED) 944236769Sobrien{ 945236769Sobrien char *file; /* the current filename to check */ 946236769Sobrien 947236769Sobrien if (DEBUG(DIR)) { 948236769Sobrien fprintf(debug_file, " %s ...\n", p->name); 949236769Sobrien } 950236769Sobrien 951236769Sobrien if (Hash_FindEntry(&p->files, cp) == NULL) 952236769Sobrien return NULL; 953236769Sobrien 954236769Sobrien file = str_concat(p->name, cp, STR_ADDSLASH); 955236769Sobrien if (DEBUG(DIR)) { 956236769Sobrien fprintf(debug_file, " returning %s\n", file); 957236769Sobrien } 958236769Sobrien p->hits += 1; 959236769Sobrien hits += 1; 960236769Sobrien return file; 961236769Sobrien} 962236769Sobrien 963236769Sobrien 964236769Sobrien/*- 965236769Sobrien *----------------------------------------------------------------------- 966236769Sobrien * DirLookupSubdir -- 967236769Sobrien * Find if the file with the given name exists in the given path. 968236769Sobrien * 969236769Sobrien * Results: 970236769Sobrien * The path to the file or NULL. This path is guaranteed to be in a 971236769Sobrien * different part of memory than name and so may be safely free'd. 972236769Sobrien * 973236769Sobrien * Side Effects: 974236769Sobrien * If the file is found, it is added in the modification times hash 975236769Sobrien * table. 976236769Sobrien *----------------------------------------------------------------------- 977236769Sobrien */ 978236769Sobrienstatic char * 979236769SobrienDirLookupSubdir(Path *p, const char *name) 980236769Sobrien{ 981236769Sobrien struct stat stb; /* Buffer for stat, if necessary */ 982236769Sobrien char *file; /* the current filename to check */ 983236769Sobrien 984236769Sobrien if (p != dot) { 985236769Sobrien file = str_concat(p->name, name, STR_ADDSLASH); 986236769Sobrien } else { 987236769Sobrien /* 988236769Sobrien * Checking in dot -- DON'T put a leading ./ on the thing. 989236769Sobrien */ 990236769Sobrien file = bmake_strdup(name); 991236769Sobrien } 992236769Sobrien 993236769Sobrien if (DEBUG(DIR)) { 994236769Sobrien fprintf(debug_file, "checking %s ...\n", file); 995236769Sobrien } 996236769Sobrien 997321964Ssjg if (cached_stat(file, &stb) == 0) { 998236769Sobrien /* 999236769Sobrien * Save the modification time so if it's needed, we don't have 1000236769Sobrien * to fetch it again. 1001236769Sobrien */ 1002236769Sobrien if (DEBUG(DIR)) { 1003236769Sobrien fprintf(debug_file, " Caching %s for %s\n", Targ_FmtTime(stb.st_mtime), 1004236769Sobrien file); 1005236769Sobrien } 1006236769Sobrien nearmisses += 1; 1007236769Sobrien return (file); 1008236769Sobrien } 1009236769Sobrien free(file); 1010236769Sobrien return NULL; 1011236769Sobrien} 1012236769Sobrien 1013236769Sobrien/*- 1014236769Sobrien *----------------------------------------------------------------------- 1015236769Sobrien * DirLookupAbs -- 1016236769Sobrien * Find if the file with the given name exists in the given path. 1017236769Sobrien * 1018236769Sobrien * Results: 1019236769Sobrien * The path to the file, the empty string or NULL. If the file is 1020236769Sobrien * the empty string, the search should be terminated. 1021236769Sobrien * This path is guaranteed to be in a different part of memory 1022236769Sobrien * than name and so may be safely free'd. 1023236769Sobrien * 1024236769Sobrien * Side Effects: 1025236769Sobrien * None. 1026236769Sobrien *----------------------------------------------------------------------- 1027236769Sobrien */ 1028236769Sobrienstatic char * 1029236769SobrienDirLookupAbs(Path *p, const char *name, const char *cp) 1030236769Sobrien{ 1031236769Sobrien char *p1; /* pointer into p->name */ 1032236769Sobrien const char *p2; /* pointer into name */ 1033236769Sobrien 1034236769Sobrien if (DEBUG(DIR)) { 1035236769Sobrien fprintf(debug_file, " %s ...\n", p->name); 1036236769Sobrien } 1037236769Sobrien 1038236769Sobrien /* 1039236769Sobrien * If the file has a leading path component and that component 1040236769Sobrien * exactly matches the entire name of the current search 1041236769Sobrien * directory, we can attempt another cache lookup. And if we don't 1042236769Sobrien * have a hit, we can safely assume the file does not exist at all. 1043236769Sobrien */ 1044236769Sobrien for (p1 = p->name, p2 = name; *p1 && *p1 == *p2; p1++, p2++) { 1045236769Sobrien continue; 1046236769Sobrien } 1047236769Sobrien if (*p1 != '\0' || p2 != cp - 1) { 1048236769Sobrien return NULL; 1049236769Sobrien } 1050236769Sobrien 1051236769Sobrien if (Hash_FindEntry(&p->files, cp) == NULL) { 1052236769Sobrien if (DEBUG(DIR)) { 1053236769Sobrien fprintf(debug_file, " must be here but isn't -- returning\n"); 1054236769Sobrien } 1055236769Sobrien /* Return empty string: terminates search */ 1056236769Sobrien return bmake_strdup(""); 1057236769Sobrien } 1058236769Sobrien 1059236769Sobrien p->hits += 1; 1060236769Sobrien hits += 1; 1061236769Sobrien if (DEBUG(DIR)) { 1062236769Sobrien fprintf(debug_file, " returning %s\n", name); 1063236769Sobrien } 1064236769Sobrien return (bmake_strdup(name)); 1065236769Sobrien} 1066236769Sobrien 1067236769Sobrien/*- 1068236769Sobrien *----------------------------------------------------------------------- 1069236769Sobrien * DirFindDot -- 1070236769Sobrien * Find the file given on "." or curdir 1071236769Sobrien * 1072236769Sobrien * Results: 1073236769Sobrien * The path to the file or NULL. This path is guaranteed to be in a 1074236769Sobrien * different part of memory than name and so may be safely free'd. 1075236769Sobrien * 1076236769Sobrien * Side Effects: 1077236769Sobrien * Hit counts change 1078236769Sobrien *----------------------------------------------------------------------- 1079236769Sobrien */ 1080236769Sobrienstatic char * 1081237578SobrienDirFindDot(Boolean hasSlash MAKE_ATTR_UNUSED, const char *name, const char *cp) 1082236769Sobrien{ 1083236769Sobrien 1084236769Sobrien if (Hash_FindEntry(&dot->files, cp) != NULL) { 1085236769Sobrien if (DEBUG(DIR)) { 1086236769Sobrien fprintf(debug_file, " in '.'\n"); 1087236769Sobrien } 1088236769Sobrien hits += 1; 1089236769Sobrien dot->hits += 1; 1090236769Sobrien return (bmake_strdup(name)); 1091236769Sobrien } 1092236769Sobrien if (cur && 1093236769Sobrien Hash_FindEntry(&cur->files, cp) != NULL) { 1094236769Sobrien if (DEBUG(DIR)) { 1095236769Sobrien fprintf(debug_file, " in ${.CURDIR} = %s\n", cur->name); 1096236769Sobrien } 1097236769Sobrien hits += 1; 1098236769Sobrien cur->hits += 1; 1099236769Sobrien return str_concat(cur->name, cp, STR_ADDSLASH); 1100236769Sobrien } 1101236769Sobrien 1102236769Sobrien return NULL; 1103236769Sobrien} 1104236769Sobrien 1105236769Sobrien/*- 1106236769Sobrien *----------------------------------------------------------------------- 1107236769Sobrien * Dir_FindFile -- 1108236769Sobrien * Find the file with the given name along the given search path. 1109236769Sobrien * 1110236769Sobrien * Input: 1111236769Sobrien * name the file to find 1112236769Sobrien * path the Lst of directories to search 1113236769Sobrien * 1114236769Sobrien * Results: 1115236769Sobrien * The path to the file or NULL. This path is guaranteed to be in a 1116236769Sobrien * different part of memory than name and so may be safely free'd. 1117236769Sobrien * 1118236769Sobrien * Side Effects: 1119236769Sobrien * If the file is found in a directory which is not on the path 1120236769Sobrien * already (either 'name' is absolute or it is a relative path 1121236769Sobrien * [ dir1/.../dirn/file ] which exists below one of the directories 1122236769Sobrien * already on the search path), its directory is added to the end 1123236769Sobrien * of the path on the assumption that there will be more files in 1124236769Sobrien * that directory later on. Sometimes this is true. Sometimes not. 1125236769Sobrien *----------------------------------------------------------------------- 1126236769Sobrien */ 1127236769Sobrienchar * 1128236769SobrienDir_FindFile(const char *name, Lst path) 1129236769Sobrien{ 1130236769Sobrien LstNode ln; /* a list element */ 1131236769Sobrien char *file; /* the current filename to check */ 1132236769Sobrien Path *p; /* current path member */ 1133236769Sobrien const char *cp; /* Terminal name of file */ 1134236769Sobrien Boolean hasLastDot = FALSE; /* true we should search dot last */ 1135236769Sobrien Boolean hasSlash; /* true if 'name' contains a / */ 1136236769Sobrien struct stat stb; /* Buffer for stat, if necessary */ 1137236769Sobrien Hash_Entry *entry; /* Entry for mtimes table */ 1138236769Sobrien const char *trailing_dot = "."; 1139236769Sobrien 1140236769Sobrien /* 1141236769Sobrien * Find the final component of the name and note whether it has a 1142236769Sobrien * slash in it (the name, I mean) 1143236769Sobrien */ 1144236769Sobrien cp = strrchr(name, '/'); 1145236769Sobrien if (cp) { 1146236769Sobrien hasSlash = TRUE; 1147236769Sobrien cp += 1; 1148236769Sobrien } else { 1149236769Sobrien hasSlash = FALSE; 1150236769Sobrien cp = name; 1151236769Sobrien } 1152236769Sobrien 1153236769Sobrien if (DEBUG(DIR)) { 1154236769Sobrien fprintf(debug_file, "Searching for %s ...", name); 1155236769Sobrien } 1156236769Sobrien 1157236769Sobrien if (Lst_Open(path) == FAILURE) { 1158236769Sobrien if (DEBUG(DIR)) { 1159236769Sobrien fprintf(debug_file, "couldn't open path, file not found\n"); 1160236769Sobrien } 1161236769Sobrien misses += 1; 1162236769Sobrien return NULL; 1163236769Sobrien } 1164236769Sobrien 1165236769Sobrien if ((ln = Lst_First(path)) != NULL) { 1166236769Sobrien p = (Path *)Lst_Datum(ln); 1167236769Sobrien if (p == dotLast) { 1168236769Sobrien hasLastDot = TRUE; 1169236769Sobrien if (DEBUG(DIR)) 1170236769Sobrien fprintf(debug_file, "[dot last]..."); 1171236769Sobrien } 1172236769Sobrien } 1173236769Sobrien if (DEBUG(DIR)) { 1174236769Sobrien fprintf(debug_file, "\n"); 1175236769Sobrien } 1176236769Sobrien 1177236769Sobrien /* 1178236769Sobrien * If there's no leading directory components or if the leading 1179236769Sobrien * directory component is exactly `./', consult the cached contents 1180236769Sobrien * of each of the directories on the search path. 1181236769Sobrien */ 1182236769Sobrien if (!hasSlash || (cp - name == 2 && *name == '.')) { 1183236769Sobrien /* 1184236769Sobrien * We look through all the directories on the path seeking one which 1185236769Sobrien * contains the final component of the given name. If such a beast 1186236769Sobrien * is found, we concatenate the directory name and the final 1187236769Sobrien * component and return the resulting string. If we don't find any 1188236769Sobrien * such thing, we go on to phase two... 1189236769Sobrien * 1190236769Sobrien * No matter what, we always look for the file in the current 1191236769Sobrien * directory before anywhere else (unless we found the magic 1192236769Sobrien * DOTLAST path, in which case we search it last) and we *do not* 1193236769Sobrien * add the ./ to it if it exists. 1194236769Sobrien * This is so there are no conflicts between what the user 1195236769Sobrien * specifies (fish.c) and what pmake finds (./fish.c). 1196236769Sobrien */ 1197236769Sobrien if (!hasLastDot && 1198236769Sobrien (file = DirFindDot(hasSlash, name, cp)) != NULL) { 1199236769Sobrien Lst_Close(path); 1200236769Sobrien return file; 1201236769Sobrien } 1202236769Sobrien 1203236769Sobrien while ((ln = Lst_Next(path)) != NULL) { 1204236769Sobrien p = (Path *)Lst_Datum(ln); 1205236769Sobrien if (p == dotLast) 1206236769Sobrien continue; 1207236769Sobrien if ((file = DirLookup(p, name, cp, hasSlash)) != NULL) { 1208236769Sobrien Lst_Close(path); 1209236769Sobrien return file; 1210236769Sobrien } 1211236769Sobrien } 1212236769Sobrien 1213236769Sobrien if (hasLastDot && 1214236769Sobrien (file = DirFindDot(hasSlash, name, cp)) != NULL) { 1215236769Sobrien Lst_Close(path); 1216236769Sobrien return file; 1217236769Sobrien } 1218236769Sobrien } 1219236769Sobrien Lst_Close(path); 1220236769Sobrien 1221236769Sobrien /* 1222236769Sobrien * We didn't find the file on any directory in the search path. 1223236769Sobrien * If the name doesn't contain a slash, that means it doesn't exist. 1224236769Sobrien * If it *does* contain a slash, however, there is still hope: it 1225236769Sobrien * could be in a subdirectory of one of the members of the search 1226236769Sobrien * path. (eg. /usr/include and sys/types.h. The above search would 1227236769Sobrien * fail to turn up types.h in /usr/include, but it *is* in 1228236769Sobrien * /usr/include/sys/types.h). 1229236769Sobrien * [ This no longer applies: If we find such a beast, we assume there 1230236769Sobrien * will be more (what else can we assume?) and add all but the last 1231236769Sobrien * component of the resulting name onto the search path (at the 1232236769Sobrien * end).] 1233236769Sobrien * This phase is only performed if the file is *not* absolute. 1234236769Sobrien */ 1235236769Sobrien if (!hasSlash) { 1236236769Sobrien if (DEBUG(DIR)) { 1237236769Sobrien fprintf(debug_file, " failed.\n"); 1238236769Sobrien } 1239236769Sobrien misses += 1; 1240236769Sobrien return NULL; 1241236769Sobrien } 1242236769Sobrien 1243236769Sobrien if (*cp == '\0') { 1244236769Sobrien /* we were given a trailing "/" */ 1245236769Sobrien cp = trailing_dot; 1246236769Sobrien } 1247236769Sobrien 1248236769Sobrien if (name[0] != '/') { 1249236769Sobrien Boolean checkedDot = FALSE; 1250236769Sobrien 1251236769Sobrien if (DEBUG(DIR)) { 1252236769Sobrien fprintf(debug_file, " Trying subdirectories...\n"); 1253236769Sobrien } 1254236769Sobrien 1255236769Sobrien if (!hasLastDot) { 1256236769Sobrien if (dot) { 1257236769Sobrien checkedDot = TRUE; 1258236769Sobrien if ((file = DirLookupSubdir(dot, name)) != NULL) 1259236769Sobrien return file; 1260236769Sobrien } 1261236769Sobrien if (cur && (file = DirLookupSubdir(cur, name)) != NULL) 1262236769Sobrien return file; 1263236769Sobrien } 1264236769Sobrien 1265236769Sobrien (void)Lst_Open(path); 1266236769Sobrien while ((ln = Lst_Next(path)) != NULL) { 1267236769Sobrien p = (Path *)Lst_Datum(ln); 1268236769Sobrien if (p == dotLast) 1269236769Sobrien continue; 1270236769Sobrien if (p == dot) { 1271236769Sobrien if (checkedDot) 1272236769Sobrien continue; 1273236769Sobrien checkedDot = TRUE; 1274236769Sobrien } 1275236769Sobrien if ((file = DirLookupSubdir(p, name)) != NULL) { 1276236769Sobrien Lst_Close(path); 1277236769Sobrien return file; 1278236769Sobrien } 1279236769Sobrien } 1280236769Sobrien Lst_Close(path); 1281236769Sobrien 1282236769Sobrien if (hasLastDot) { 1283236769Sobrien if (dot && !checkedDot) { 1284236769Sobrien checkedDot = TRUE; 1285236769Sobrien if ((file = DirLookupSubdir(dot, name)) != NULL) 1286236769Sobrien return file; 1287236769Sobrien } 1288236769Sobrien if (cur && (file = DirLookupSubdir(cur, name)) != NULL) 1289236769Sobrien return file; 1290236769Sobrien } 1291236769Sobrien 1292236769Sobrien if (checkedDot) { 1293236769Sobrien /* 1294236769Sobrien * Already checked by the given name, since . was in the path, 1295236769Sobrien * so no point in proceeding... 1296236769Sobrien */ 1297236769Sobrien if (DEBUG(DIR)) { 1298236769Sobrien fprintf(debug_file, " Checked . already, returning NULL\n"); 1299236769Sobrien } 1300236769Sobrien return NULL; 1301236769Sobrien } 1302236769Sobrien 1303236769Sobrien } else { /* name[0] == '/' */ 1304236769Sobrien 1305236769Sobrien /* 1306236769Sobrien * For absolute names, compare directory path prefix against the 1307236769Sobrien * the directory path of each member on the search path for an exact 1308236769Sobrien * match. If we have an exact match on any member of the search path, 1309236769Sobrien * use the cached contents of that member to lookup the final file 1310236769Sobrien * component. If that lookup fails we can safely assume that the 1311236769Sobrien * file does not exist at all. This is signified by DirLookupAbs() 1312236769Sobrien * returning an empty string. 1313236769Sobrien */ 1314236769Sobrien if (DEBUG(DIR)) { 1315236769Sobrien fprintf(debug_file, " Trying exact path matches...\n"); 1316236769Sobrien } 1317236769Sobrien 1318321964Ssjg if (!hasLastDot && cur && ((file = DirLookupAbs(cur, name, cp)) 1319321964Ssjg != NULL)) { 1320321964Ssjg if (file[0] == '\0') { 1321321964Ssjg free(file); 1322321964Ssjg return NULL; 1323321964Ssjg } 1324321964Ssjg return file; 1325321964Ssjg } 1326236769Sobrien 1327236769Sobrien (void)Lst_Open(path); 1328236769Sobrien while ((ln = Lst_Next(path)) != NULL) { 1329236769Sobrien p = (Path *)Lst_Datum(ln); 1330236769Sobrien if (p == dotLast) 1331236769Sobrien continue; 1332236769Sobrien if ((file = DirLookupAbs(p, name, cp)) != NULL) { 1333236769Sobrien Lst_Close(path); 1334321964Ssjg if (file[0] == '\0') { 1335321964Ssjg free(file); 1336321964Ssjg return NULL; 1337321964Ssjg } 1338321964Ssjg return file; 1339236769Sobrien } 1340236769Sobrien } 1341236769Sobrien Lst_Close(path); 1342236769Sobrien 1343321964Ssjg if (hasLastDot && cur && ((file = DirLookupAbs(cur, name, cp)) 1344321964Ssjg != NULL)) { 1345321964Ssjg if (file[0] == '\0') { 1346321964Ssjg free(file); 1347321964Ssjg return NULL; 1348321964Ssjg } 1349321964Ssjg return file; 1350321964Ssjg } 1351236769Sobrien } 1352236769Sobrien 1353236769Sobrien /* 1354236769Sobrien * Didn't find it that way, either. Sigh. Phase 3. Add its directory 1355236769Sobrien * onto the search path in any case, just in case, then look for the 1356236769Sobrien * thing in the hash table. If we find it, grand. We return a new 1357236769Sobrien * copy of the name. Otherwise we sadly return a NULL pointer. Sigh. 1358236769Sobrien * Note that if the directory holding the file doesn't exist, this will 1359236769Sobrien * do an extra search of the final directory on the path. Unless something 1360236769Sobrien * weird happens, this search won't succeed and life will be groovy. 1361236769Sobrien * 1362236769Sobrien * Sigh. We cannot add the directory onto the search path because 1363236769Sobrien * of this amusing case: 1364236769Sobrien * $(INSTALLDIR)/$(FILE): $(FILE) 1365236769Sobrien * 1366236769Sobrien * $(FILE) exists in $(INSTALLDIR) but not in the current one. 1367236769Sobrien * When searching for $(FILE), we will find it in $(INSTALLDIR) 1368236769Sobrien * b/c we added it here. This is not good... 1369236769Sobrien */ 1370236769Sobrien#ifdef notdef 1371236769Sobrien if (cp == traling_dot) { 1372236769Sobrien cp = strrchr(name, '/'); 1373236769Sobrien cp += 1; 1374236769Sobrien } 1375236769Sobrien cp[-1] = '\0'; 1376236769Sobrien (void)Dir_AddDir(path, name); 1377236769Sobrien cp[-1] = '/'; 1378236769Sobrien 1379236769Sobrien bigmisses += 1; 1380236769Sobrien ln = Lst_Last(path); 1381236769Sobrien if (ln == NULL) { 1382236769Sobrien return NULL; 1383236769Sobrien } else { 1384236769Sobrien p = (Path *)Lst_Datum(ln); 1385236769Sobrien } 1386236769Sobrien 1387236769Sobrien if (Hash_FindEntry(&p->files, cp) != NULL) { 1388236769Sobrien return (bmake_strdup(name)); 1389236769Sobrien } else { 1390236769Sobrien return NULL; 1391236769Sobrien } 1392236769Sobrien#else /* !notdef */ 1393236769Sobrien if (DEBUG(DIR)) { 1394236769Sobrien fprintf(debug_file, " Looking for \"%s\" ...\n", name); 1395236769Sobrien } 1396236769Sobrien 1397236769Sobrien bigmisses += 1; 1398236769Sobrien entry = Hash_FindEntry(&mtimes, name); 1399236769Sobrien if (entry != NULL) { 1400236769Sobrien if (DEBUG(DIR)) { 1401236769Sobrien fprintf(debug_file, " got it (in mtime cache)\n"); 1402236769Sobrien } 1403236769Sobrien return(bmake_strdup(name)); 1404321964Ssjg } else if (cached_stat(name, &stb) == 0) { 1405236769Sobrien if (DEBUG(DIR)) { 1406236769Sobrien fprintf(debug_file, " Caching %s for %s\n", Targ_FmtTime(stb.st_mtime), 1407236769Sobrien name); 1408236769Sobrien } 1409236769Sobrien return (bmake_strdup(name)); 1410236769Sobrien } else { 1411236769Sobrien if (DEBUG(DIR)) { 1412236769Sobrien fprintf(debug_file, " failed. Returning NULL\n"); 1413236769Sobrien } 1414236769Sobrien return NULL; 1415236769Sobrien } 1416236769Sobrien#endif /* notdef */ 1417236769Sobrien} 1418236769Sobrien 1419236769Sobrien 1420236769Sobrien/*- 1421236769Sobrien *----------------------------------------------------------------------- 1422236769Sobrien * Dir_FindHereOrAbove -- 1423236769Sobrien * search for a path starting at a given directory and then working 1424236769Sobrien * our way up towards the root. 1425236769Sobrien * 1426236769Sobrien * Input: 1427236769Sobrien * here starting directory 1428236769Sobrien * search_path the path we are looking for 1429236769Sobrien * result the result of a successful search is placed here 1430236769Sobrien * rlen the length of the result buffer 1431236769Sobrien * (typically MAXPATHLEN + 1) 1432236769Sobrien * 1433236769Sobrien * Results: 1434236769Sobrien * 0 on failure, 1 on success [in which case the found path is put 1435236769Sobrien * in the result buffer]. 1436236769Sobrien * 1437236769Sobrien * Side Effects: 1438236769Sobrien *----------------------------------------------------------------------- 1439236769Sobrien */ 1440236769Sobrienint 1441236769SobrienDir_FindHereOrAbove(char *here, char *search_path, char *result, int rlen) { 1442236769Sobrien 1443236769Sobrien struct stat st; 1444236769Sobrien char dirbase[MAXPATHLEN + 1], *db_end; 1445236769Sobrien char try[MAXPATHLEN + 1], *try_end; 1446236769Sobrien 1447236769Sobrien /* copy out our starting point */ 1448236769Sobrien snprintf(dirbase, sizeof(dirbase), "%s", here); 1449236769Sobrien db_end = dirbase + strlen(dirbase); 1450236769Sobrien 1451236769Sobrien /* loop until we determine a result */ 1452236769Sobrien while (1) { 1453236769Sobrien 1454236769Sobrien /* try and stat(2) it ... */ 1455236769Sobrien snprintf(try, sizeof(try), "%s/%s", dirbase, search_path); 1456321964Ssjg if (cached_stat(try, &st) != -1) { 1457236769Sobrien /* 1458236769Sobrien * success! if we found a file, chop off 1459236769Sobrien * the filename so we return a directory. 1460236769Sobrien */ 1461236769Sobrien if ((st.st_mode & S_IFMT) != S_IFDIR) { 1462236769Sobrien try_end = try + strlen(try); 1463236769Sobrien while (try_end > try && *try_end != '/') 1464236769Sobrien try_end--; 1465236769Sobrien if (try_end > try) 1466236769Sobrien *try_end = 0; /* chop! */ 1467236769Sobrien } 1468236769Sobrien 1469236769Sobrien /* 1470236769Sobrien * done! 1471236769Sobrien */ 1472236769Sobrien snprintf(result, rlen, "%s", try); 1473236769Sobrien return(1); 1474236769Sobrien } 1475236769Sobrien 1476236769Sobrien /* 1477236769Sobrien * nope, we didn't find it. if we used up dirbase we've 1478236769Sobrien * reached the root and failed. 1479236769Sobrien */ 1480236769Sobrien if (db_end == dirbase) 1481236769Sobrien break; /* failed! */ 1482236769Sobrien 1483236769Sobrien /* 1484236769Sobrien * truncate dirbase from the end to move up a dir 1485236769Sobrien */ 1486236769Sobrien while (db_end > dirbase && *db_end != '/') 1487236769Sobrien db_end--; 1488236769Sobrien *db_end = 0; /* chop! */ 1489236769Sobrien 1490236769Sobrien } /* while (1) */ 1491236769Sobrien 1492236769Sobrien /* 1493236769Sobrien * we failed... 1494236769Sobrien */ 1495236769Sobrien return(0); 1496236769Sobrien} 1497236769Sobrien 1498236769Sobrien/*- 1499236769Sobrien *----------------------------------------------------------------------- 1500236769Sobrien * Dir_MTime -- 1501236769Sobrien * Find the modification time of the file described by gn along the 1502236769Sobrien * search path dirSearchPath. 1503236769Sobrien * 1504236769Sobrien * Input: 1505236769Sobrien * gn the file whose modification time is desired 1506236769Sobrien * 1507236769Sobrien * Results: 1508236769Sobrien * The modification time or 0 if it doesn't exist 1509236769Sobrien * 1510236769Sobrien * Side Effects: 1511236769Sobrien * The modification time is placed in the node's mtime slot. 1512236769Sobrien * If the node didn't have a path entry before, and Dir_FindFile 1513236769Sobrien * found one for it, the full name is placed in the path slot. 1514236769Sobrien *----------------------------------------------------------------------- 1515236769Sobrien */ 1516236769Sobrienint 1517236769SobrienDir_MTime(GNode *gn, Boolean recheck) 1518236769Sobrien{ 1519236769Sobrien char *fullName; /* the full pathname of name */ 1520236769Sobrien struct stat stb; /* buffer for finding the mod time */ 1521236769Sobrien Hash_Entry *entry; 1522236769Sobrien 1523236769Sobrien if (gn->type & OP_ARCHV) { 1524236769Sobrien return Arch_MTime(gn); 1525236769Sobrien } else if (gn->type & OP_PHONY) { 1526236769Sobrien gn->mtime = 0; 1527236769Sobrien return 0; 1528236769Sobrien } else if (gn->path == NULL) { 1529236769Sobrien if (gn->type & OP_NOPATH) 1530236769Sobrien fullName = NULL; 1531236769Sobrien else { 1532236769Sobrien fullName = Dir_FindFile(gn->name, Suff_FindPath(gn)); 1533236769Sobrien if (fullName == NULL && gn->flags & FROM_DEPEND && 1534236769Sobrien !Lst_IsEmpty(gn->iParents)) { 1535236769Sobrien char *cp; 1536236769Sobrien 1537236769Sobrien cp = strrchr(gn->name, '/'); 1538236769Sobrien if (cp) { 1539236769Sobrien /* 1540236769Sobrien * This is an implied source, and it may have moved, 1541236769Sobrien * see if we can find it via the current .PATH 1542236769Sobrien */ 1543236769Sobrien cp++; 1544236769Sobrien 1545236769Sobrien fullName = Dir_FindFile(cp, Suff_FindPath(gn)); 1546236769Sobrien if (fullName) { 1547236769Sobrien /* 1548236769Sobrien * Put the found file in gn->path 1549236769Sobrien * so that we give that to the compiler. 1550236769Sobrien */ 1551236769Sobrien gn->path = bmake_strdup(fullName); 1552249033Ssjg if (!Job_RunTarget(".STALE", gn->fname)) 1553249033Ssjg fprintf(stdout, 1554249033Ssjg "%s: %s, %d: ignoring stale %s for %s, " 1555249033Ssjg "found %s\n", progname, gn->fname, gn->lineno, 1556249033Ssjg makeDependfile, gn->name, fullName); 1557236769Sobrien } 1558236769Sobrien } 1559236769Sobrien } 1560236769Sobrien if (DEBUG(DIR)) 1561236769Sobrien fprintf(debug_file, "Found '%s' as '%s'\n", 1562236769Sobrien gn->name, fullName ? fullName : "(not found)" ); 1563236769Sobrien } 1564236769Sobrien } else { 1565236769Sobrien fullName = gn->path; 1566236769Sobrien } 1567236769Sobrien 1568236769Sobrien if (fullName == NULL) { 1569236769Sobrien fullName = bmake_strdup(gn->name); 1570236769Sobrien } 1571236769Sobrien 1572236769Sobrien if (!recheck) 1573236769Sobrien entry = Hash_FindEntry(&mtimes, fullName); 1574236769Sobrien else 1575236769Sobrien entry = NULL; 1576236769Sobrien if (entry != NULL) { 1577321964Ssjg stb.st_mtime = Hash_GetTimeValue(entry); 1578236769Sobrien if (DEBUG(DIR)) { 1579236769Sobrien fprintf(debug_file, "Using cached time %s for %s\n", 1580321964Ssjg Targ_FmtTime(stb.st_mtime), fullName); 1581236769Sobrien } 1582321964Ssjg } else if (cached_stats(&mtimes, fullName, &stb, recheck ? CST_UPDATE : 0) < 0) { 1583236769Sobrien if (gn->type & OP_MEMBER) { 1584236769Sobrien if (fullName != gn->path) 1585236769Sobrien free(fullName); 1586236769Sobrien return Arch_MemMTime(gn); 1587236769Sobrien } else { 1588236769Sobrien stb.st_mtime = 0; 1589236769Sobrien } 1590236769Sobrien } 1591321964Ssjg 1592236769Sobrien if (fullName && gn->path == NULL) { 1593236769Sobrien gn->path = fullName; 1594236769Sobrien } 1595236769Sobrien 1596236769Sobrien gn->mtime = stb.st_mtime; 1597236769Sobrien return (gn->mtime); 1598236769Sobrien} 1599236769Sobrien 1600236769Sobrien/*- 1601236769Sobrien *----------------------------------------------------------------------- 1602236769Sobrien * Dir_AddDir -- 1603236769Sobrien * Add the given name to the end of the given path. The order of 1604236769Sobrien * the arguments is backwards so ParseDoDependency can do a 1605236769Sobrien * Lst_ForEach of its list of paths... 1606236769Sobrien * 1607236769Sobrien * Input: 1608236769Sobrien * path the path to which the directory should be 1609236769Sobrien * added 1610236769Sobrien * name the name of the directory to add 1611236769Sobrien * 1612236769Sobrien * Results: 1613236769Sobrien * none 1614236769Sobrien * 1615236769Sobrien * Side Effects: 1616236769Sobrien * A structure is added to the list and the directory is 1617236769Sobrien * read and hashed. 1618236769Sobrien *----------------------------------------------------------------------- 1619236769Sobrien */ 1620236769SobrienPath * 1621236769SobrienDir_AddDir(Lst path, const char *name) 1622236769Sobrien{ 1623236769Sobrien LstNode ln = NULL; /* node in case Path structure is found */ 1624236769Sobrien Path *p = NULL; /* pointer to new Path structure */ 1625236769Sobrien DIR *d; /* for reading directory */ 1626236769Sobrien struct dirent *dp; /* entry in directory */ 1627236769Sobrien 1628236769Sobrien if (strcmp(name, ".DOTLAST") == 0) { 1629236769Sobrien ln = Lst_Find(path, name, DirFindName); 1630236769Sobrien if (ln != NULL) 1631236769Sobrien return (Path *)Lst_Datum(ln); 1632236769Sobrien else { 1633236769Sobrien dotLast->refCount += 1; 1634236769Sobrien (void)Lst_AtFront(path, dotLast); 1635236769Sobrien } 1636236769Sobrien } 1637236769Sobrien 1638236769Sobrien if (path) 1639236769Sobrien ln = Lst_Find(openDirectories, name, DirFindName); 1640236769Sobrien if (ln != NULL) { 1641236769Sobrien p = (Path *)Lst_Datum(ln); 1642236769Sobrien if (path && Lst_Member(path, p) == NULL) { 1643236769Sobrien p->refCount += 1; 1644236769Sobrien (void)Lst_AtEnd(path, p); 1645236769Sobrien } 1646236769Sobrien } else { 1647236769Sobrien if (DEBUG(DIR)) { 1648236769Sobrien fprintf(debug_file, "Caching %s ...", name); 1649236769Sobrien } 1650236769Sobrien 1651236769Sobrien if ((d = opendir(name)) != NULL) { 1652236769Sobrien p = bmake_malloc(sizeof(Path)); 1653236769Sobrien p->name = bmake_strdup(name); 1654236769Sobrien p->hits = 0; 1655236769Sobrien p->refCount = 1; 1656236769Sobrien Hash_InitTable(&p->files, -1); 1657236769Sobrien 1658236769Sobrien while ((dp = readdir(d)) != NULL) { 1659236769Sobrien#if defined(sun) && defined(d_ino) /* d_ino is a sunos4 #define for d_fileno */ 1660236769Sobrien /* 1661236769Sobrien * The sun directory library doesn't check for a 0 inode 1662236769Sobrien * (0-inode slots just take up space), so we have to do 1663236769Sobrien * it ourselves. 1664236769Sobrien */ 1665236769Sobrien if (dp->d_fileno == 0) { 1666236769Sobrien continue; 1667236769Sobrien } 1668236769Sobrien#endif /* sun && d_ino */ 1669236769Sobrien (void)Hash_CreateEntry(&p->files, dp->d_name, NULL); 1670236769Sobrien } 1671236769Sobrien (void)closedir(d); 1672236769Sobrien (void)Lst_AtEnd(openDirectories, p); 1673236769Sobrien if (path != NULL) 1674236769Sobrien (void)Lst_AtEnd(path, p); 1675236769Sobrien } 1676236769Sobrien if (DEBUG(DIR)) { 1677236769Sobrien fprintf(debug_file, "done\n"); 1678236769Sobrien } 1679236769Sobrien } 1680236769Sobrien return p; 1681236769Sobrien} 1682236769Sobrien 1683236769Sobrien/*- 1684236769Sobrien *----------------------------------------------------------------------- 1685236769Sobrien * Dir_CopyDir -- 1686236769Sobrien * Callback function for duplicating a search path via Lst_Duplicate. 1687236769Sobrien * Ups the reference count for the directory. 1688236769Sobrien * 1689236769Sobrien * Results: 1690236769Sobrien * Returns the Path it was given. 1691236769Sobrien * 1692236769Sobrien * Side Effects: 1693236769Sobrien * The refCount of the path is incremented. 1694236769Sobrien * 1695236769Sobrien *----------------------------------------------------------------------- 1696236769Sobrien */ 1697236769Sobrienvoid * 1698236769SobrienDir_CopyDir(void *p) 1699236769Sobrien{ 1700236769Sobrien ((Path *)p)->refCount += 1; 1701236769Sobrien 1702236769Sobrien return (p); 1703236769Sobrien} 1704236769Sobrien 1705236769Sobrien/*- 1706236769Sobrien *----------------------------------------------------------------------- 1707236769Sobrien * Dir_MakeFlags -- 1708236769Sobrien * Make a string by taking all the directories in the given search 1709236769Sobrien * path and preceding them by the given flag. Used by the suffix 1710236769Sobrien * module to create variables for compilers based on suffix search 1711236769Sobrien * paths. 1712236769Sobrien * 1713236769Sobrien * Input: 1714236769Sobrien * flag flag which should precede each directory 1715236769Sobrien * path list of directories 1716236769Sobrien * 1717236769Sobrien * Results: 1718236769Sobrien * The string mentioned above. Note that there is no space between 1719236769Sobrien * the given flag and each directory. The empty string is returned if 1720236769Sobrien * Things don't go well. 1721236769Sobrien * 1722236769Sobrien * Side Effects: 1723236769Sobrien * None 1724236769Sobrien *----------------------------------------------------------------------- 1725236769Sobrien */ 1726236769Sobrienchar * 1727236769SobrienDir_MakeFlags(const char *flag, Lst path) 1728236769Sobrien{ 1729236769Sobrien char *str; /* the string which will be returned */ 1730236769Sobrien char *s1, *s2;/* the current directory preceded by 'flag' */ 1731236769Sobrien LstNode ln; /* the node of the current directory */ 1732236769Sobrien Path *p; /* the structure describing the current directory */ 1733236769Sobrien 1734236769Sobrien str = bmake_strdup(""); 1735236769Sobrien 1736236769Sobrien if (Lst_Open(path) == SUCCESS) { 1737236769Sobrien while ((ln = Lst_Next(path)) != NULL) { 1738236769Sobrien p = (Path *)Lst_Datum(ln); 1739236769Sobrien s2 = str_concat(flag, p->name, 0); 1740236769Sobrien str = str_concat(s1 = str, s2, STR_ADDSPACE); 1741236769Sobrien free(s1); 1742236769Sobrien free(s2); 1743236769Sobrien } 1744236769Sobrien Lst_Close(path); 1745236769Sobrien } 1746236769Sobrien 1747236769Sobrien return (str); 1748236769Sobrien} 1749236769Sobrien 1750236769Sobrien/*- 1751236769Sobrien *----------------------------------------------------------------------- 1752236769Sobrien * Dir_Destroy -- 1753236769Sobrien * Nuke a directory descriptor, if possible. Callback procedure 1754236769Sobrien * for the suffixes module when destroying a search path. 1755236769Sobrien * 1756236769Sobrien * Input: 1757236769Sobrien * pp The directory descriptor to nuke 1758236769Sobrien * 1759236769Sobrien * Results: 1760236769Sobrien * None. 1761236769Sobrien * 1762236769Sobrien * Side Effects: 1763236769Sobrien * If no other path references this directory (refCount == 0), 1764236769Sobrien * the Path and all its data are freed. 1765236769Sobrien * 1766236769Sobrien *----------------------------------------------------------------------- 1767236769Sobrien */ 1768236769Sobrienvoid 1769236769SobrienDir_Destroy(void *pp) 1770236769Sobrien{ 1771236769Sobrien Path *p = (Path *)pp; 1772236769Sobrien p->refCount -= 1; 1773236769Sobrien 1774236769Sobrien if (p->refCount == 0) { 1775236769Sobrien LstNode ln; 1776236769Sobrien 1777236769Sobrien ln = Lst_Member(openDirectories, p); 1778236769Sobrien (void)Lst_Remove(openDirectories, ln); 1779236769Sobrien 1780236769Sobrien Hash_DeleteTable(&p->files); 1781236769Sobrien free(p->name); 1782236769Sobrien free(p); 1783236769Sobrien } 1784236769Sobrien} 1785236769Sobrien 1786236769Sobrien/*- 1787236769Sobrien *----------------------------------------------------------------------- 1788236769Sobrien * Dir_ClearPath -- 1789236769Sobrien * Clear out all elements of the given search path. This is different 1790236769Sobrien * from destroying the list, notice. 1791236769Sobrien * 1792236769Sobrien * Input: 1793236769Sobrien * path Path to clear 1794236769Sobrien * 1795236769Sobrien * Results: 1796236769Sobrien * None. 1797236769Sobrien * 1798236769Sobrien * Side Effects: 1799236769Sobrien * The path is set to the empty list. 1800236769Sobrien * 1801236769Sobrien *----------------------------------------------------------------------- 1802236769Sobrien */ 1803236769Sobrienvoid 1804236769SobrienDir_ClearPath(Lst path) 1805236769Sobrien{ 1806236769Sobrien Path *p; 1807236769Sobrien while (!Lst_IsEmpty(path)) { 1808236769Sobrien p = (Path *)Lst_DeQueue(path); 1809236769Sobrien Dir_Destroy(p); 1810236769Sobrien } 1811236769Sobrien} 1812236769Sobrien 1813236769Sobrien 1814236769Sobrien/*- 1815236769Sobrien *----------------------------------------------------------------------- 1816236769Sobrien * Dir_Concat -- 1817236769Sobrien * Concatenate two paths, adding the second to the end of the first. 1818236769Sobrien * Makes sure to avoid duplicates. 1819236769Sobrien * 1820236769Sobrien * Input: 1821236769Sobrien * path1 Dest 1822236769Sobrien * path2 Source 1823236769Sobrien * 1824236769Sobrien * Results: 1825236769Sobrien * None 1826236769Sobrien * 1827236769Sobrien * Side Effects: 1828236769Sobrien * Reference counts for added dirs are upped. 1829236769Sobrien * 1830236769Sobrien *----------------------------------------------------------------------- 1831236769Sobrien */ 1832236769Sobrienvoid 1833236769SobrienDir_Concat(Lst path1, Lst path2) 1834236769Sobrien{ 1835236769Sobrien LstNode ln; 1836236769Sobrien Path *p; 1837236769Sobrien 1838236769Sobrien for (ln = Lst_First(path2); ln != NULL; ln = Lst_Succ(ln)) { 1839236769Sobrien p = (Path *)Lst_Datum(ln); 1840236769Sobrien if (Lst_Member(path1, p) == NULL) { 1841236769Sobrien p->refCount += 1; 1842236769Sobrien (void)Lst_AtEnd(path1, p); 1843236769Sobrien } 1844236769Sobrien } 1845236769Sobrien} 1846236769Sobrien 1847236769Sobrien/********** DEBUG INFO **********/ 1848236769Sobrienvoid 1849236769SobrienDir_PrintDirectories(void) 1850236769Sobrien{ 1851236769Sobrien LstNode ln; 1852236769Sobrien Path *p; 1853236769Sobrien 1854236769Sobrien fprintf(debug_file, "#*** Directory Cache:\n"); 1855236769Sobrien fprintf(debug_file, "# Stats: %d hits %d misses %d near misses %d losers (%d%%)\n", 1856236769Sobrien hits, misses, nearmisses, bigmisses, 1857236769Sobrien (hits+bigmisses+nearmisses ? 1858236769Sobrien hits * 100 / (hits + bigmisses + nearmisses) : 0)); 1859236769Sobrien fprintf(debug_file, "# %-20s referenced\thits\n", "directory"); 1860236769Sobrien if (Lst_Open(openDirectories) == SUCCESS) { 1861236769Sobrien while ((ln = Lst_Next(openDirectories)) != NULL) { 1862236769Sobrien p = (Path *)Lst_Datum(ln); 1863236769Sobrien fprintf(debug_file, "# %-20s %10d\t%4d\n", p->name, p->refCount, p->hits); 1864236769Sobrien } 1865236769Sobrien Lst_Close(openDirectories); 1866236769Sobrien } 1867236769Sobrien} 1868236769Sobrien 1869236769Sobrienstatic int 1870321964SsjgDirPrintDir(void *p, void *dummy MAKE_ATTR_UNUSED) 1871236769Sobrien{ 1872236769Sobrien fprintf(debug_file, "%s ", ((Path *)p)->name); 1873321964Ssjg return 0; 1874236769Sobrien} 1875236769Sobrien 1876236769Sobrienvoid 1877236769SobrienDir_PrintPath(Lst path) 1878236769Sobrien{ 1879236769Sobrien Lst_ForEach(path, DirPrintDir, NULL); 1880236769Sobrien} 1881