1249033Ssjg/* $NetBSD: dir.c,v 1.67 2013/03/05 22:01:43 christos 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 73249033Ssjgstatic char rcsid[] = "$NetBSD: dir.c,v 1.67 2013/03/05 22:01:43 christos 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 80249033Ssjg__RCSID("$NetBSD: dir.c,v 1.67 2013/03/05 22:01:43 christos 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 247236769Sobrien 248236769Sobrienstatic int DirFindName(const void *, const void *); 249236769Sobrienstatic int DirMatchFiles(const char *, Path *, Lst); 250236769Sobrienstatic void DirExpandCurly(const char *, const char *, Lst, Lst); 251236769Sobrienstatic void DirExpandInt(const char *, Lst, Lst); 252236769Sobrienstatic int DirPrintWord(void *, void *); 253236769Sobrienstatic int DirPrintDir(void *, void *); 254236769Sobrienstatic char *DirLookup(Path *, const char *, const char *, Boolean); 255236769Sobrienstatic char *DirLookupSubdir(Path *, const char *); 256236769Sobrienstatic char *DirFindDot(Boolean, const char *, const char *); 257236769Sobrienstatic char *DirLookupAbs(Path *, const char *, const char *); 258236769Sobrien 259236769Sobrien/*- 260236769Sobrien *----------------------------------------------------------------------- 261236769Sobrien * Dir_Init -- 262236769Sobrien * initialize things for this module 263236769Sobrien * 264236769Sobrien * Results: 265236769Sobrien * none 266236769Sobrien * 267236769Sobrien * Side Effects: 268236769Sobrien * some directories may be opened. 269236769Sobrien *----------------------------------------------------------------------- 270236769Sobrien */ 271236769Sobrienvoid 272236769SobrienDir_Init(const char *cdname) 273236769Sobrien{ 274236769Sobrien dirSearchPath = Lst_Init(FALSE); 275236769Sobrien openDirectories = Lst_Init(FALSE); 276236769Sobrien Hash_InitTable(&mtimes, 0); 277236769Sobrien 278236769Sobrien Dir_InitCur(cdname); 279236769Sobrien 280236769Sobrien dotLast = bmake_malloc(sizeof(Path)); 281236769Sobrien dotLast->refCount = 1; 282236769Sobrien dotLast->hits = 0; 283236769Sobrien dotLast->name = bmake_strdup(".DOTLAST"); 284236769Sobrien Hash_InitTable(&dotLast->files, -1); 285236769Sobrien} 286236769Sobrien 287236769Sobrien/* 288236769Sobrien * Called by Dir_Init() and whenever .CURDIR is assigned to. 289236769Sobrien */ 290236769Sobrienvoid 291236769SobrienDir_InitCur(const char *cdname) 292236769Sobrien{ 293236769Sobrien Path *p; 294236769Sobrien 295236769Sobrien if (cdname != NULL) { 296236769Sobrien /* 297236769Sobrien * Our build directory is not the same as our source directory. 298236769Sobrien * Keep this one around too. 299236769Sobrien */ 300236769Sobrien if ((p = Dir_AddDir(NULL, cdname))) { 301236769Sobrien p->refCount += 1; 302236769Sobrien if (cur && cur != p) { 303236769Sobrien /* 304236769Sobrien * We've been here before, cleanup. 305236769Sobrien */ 306236769Sobrien cur->refCount -= 1; 307236769Sobrien Dir_Destroy(cur); 308236769Sobrien } 309236769Sobrien cur = p; 310236769Sobrien } 311236769Sobrien } 312236769Sobrien} 313236769Sobrien 314236769Sobrien/*- 315236769Sobrien *----------------------------------------------------------------------- 316236769Sobrien * Dir_InitDot -- 317236769Sobrien * (re)initialize "dot" (current/object directory) path hash 318236769Sobrien * 319236769Sobrien * Results: 320236769Sobrien * none 321236769Sobrien * 322236769Sobrien * Side Effects: 323236769Sobrien * some directories may be opened. 324236769Sobrien *----------------------------------------------------------------------- 325236769Sobrien */ 326236769Sobrienvoid 327236769SobrienDir_InitDot(void) 328236769Sobrien{ 329236769Sobrien if (dot != NULL) { 330236769Sobrien LstNode ln; 331236769Sobrien 332236769Sobrien /* Remove old entry from openDirectories, but do not destroy. */ 333236769Sobrien ln = Lst_Member(openDirectories, dot); 334236769Sobrien (void)Lst_Remove(openDirectories, ln); 335236769Sobrien } 336236769Sobrien 337236769Sobrien dot = Dir_AddDir(NULL, "."); 338236769Sobrien 339236769Sobrien if (dot == NULL) { 340236769Sobrien Error("Cannot open `.' (%s)", strerror(errno)); 341236769Sobrien exit(1); 342236769Sobrien } 343236769Sobrien 344236769Sobrien /* 345236769Sobrien * We always need to have dot around, so we increment its reference count 346236769Sobrien * to make sure it's not destroyed. 347236769Sobrien */ 348236769Sobrien dot->refCount += 1; 349236769Sobrien Dir_SetPATH(); /* initialize */ 350236769Sobrien} 351236769Sobrien 352236769Sobrien/*- 353236769Sobrien *----------------------------------------------------------------------- 354236769Sobrien * Dir_End -- 355236769Sobrien * cleanup things for this module 356236769Sobrien * 357236769Sobrien * Results: 358236769Sobrien * none 359236769Sobrien * 360236769Sobrien * Side Effects: 361236769Sobrien * none 362236769Sobrien *----------------------------------------------------------------------- 363236769Sobrien */ 364236769Sobrienvoid 365236769SobrienDir_End(void) 366236769Sobrien{ 367236769Sobrien#ifdef CLEANUP 368236769Sobrien if (cur) { 369236769Sobrien cur->refCount -= 1; 370236769Sobrien Dir_Destroy(cur); 371236769Sobrien } 372236769Sobrien dot->refCount -= 1; 373236769Sobrien dotLast->refCount -= 1; 374236769Sobrien Dir_Destroy(dotLast); 375236769Sobrien Dir_Destroy(dot); 376236769Sobrien Dir_ClearPath(dirSearchPath); 377236769Sobrien Lst_Destroy(dirSearchPath, NULL); 378236769Sobrien Dir_ClearPath(openDirectories); 379236769Sobrien Lst_Destroy(openDirectories, NULL); 380236769Sobrien Hash_DeleteTable(&mtimes); 381236769Sobrien#endif 382236769Sobrien} 383236769Sobrien 384236769Sobrien/* 385236769Sobrien * We want ${.PATH} to indicate the order in which we will actually 386236769Sobrien * search, so we rebuild it after any .PATH: target. 387236769Sobrien * This is the simplest way to deal with the effect of .DOTLAST. 388236769Sobrien */ 389236769Sobrienvoid 390236769SobrienDir_SetPATH(void) 391236769Sobrien{ 392236769Sobrien LstNode ln; /* a list element */ 393236769Sobrien Path *p; 394236769Sobrien Boolean hasLastDot = FALSE; /* true we should search dot last */ 395236769Sobrien 396236769Sobrien Var_Delete(".PATH", VAR_GLOBAL); 397236769Sobrien 398236769Sobrien if (Lst_Open(dirSearchPath) == SUCCESS) { 399236769Sobrien if ((ln = Lst_First(dirSearchPath)) != NULL) { 400236769Sobrien p = (Path *)Lst_Datum(ln); 401236769Sobrien if (p == dotLast) { 402236769Sobrien hasLastDot = TRUE; 403236769Sobrien Var_Append(".PATH", dotLast->name, VAR_GLOBAL); 404236769Sobrien } 405236769Sobrien } 406236769Sobrien 407236769Sobrien if (!hasLastDot) { 408236769Sobrien if (dot) 409236769Sobrien Var_Append(".PATH", dot->name, VAR_GLOBAL); 410236769Sobrien if (cur) 411236769Sobrien Var_Append(".PATH", cur->name, VAR_GLOBAL); 412236769Sobrien } 413236769Sobrien 414236769Sobrien while ((ln = Lst_Next(dirSearchPath)) != NULL) { 415236769Sobrien p = (Path *)Lst_Datum(ln); 416236769Sobrien if (p == dotLast) 417236769Sobrien continue; 418236769Sobrien if (p == dot && hasLastDot) 419236769Sobrien continue; 420236769Sobrien Var_Append(".PATH", p->name, VAR_GLOBAL); 421236769Sobrien } 422236769Sobrien 423236769Sobrien if (hasLastDot) { 424236769Sobrien if (dot) 425236769Sobrien Var_Append(".PATH", dot->name, VAR_GLOBAL); 426236769Sobrien if (cur) 427236769Sobrien Var_Append(".PATH", cur->name, VAR_GLOBAL); 428236769Sobrien } 429236769Sobrien Lst_Close(dirSearchPath); 430236769Sobrien } 431236769Sobrien} 432236769Sobrien 433236769Sobrien/*- 434236769Sobrien *----------------------------------------------------------------------- 435236769Sobrien * DirFindName -- 436236769Sobrien * See if the Path structure describes the same directory as the 437236769Sobrien * given one by comparing their names. Called from Dir_AddDir via 438236769Sobrien * Lst_Find when searching the list of open directories. 439236769Sobrien * 440236769Sobrien * Input: 441236769Sobrien * p Current name 442236769Sobrien * dname Desired name 443236769Sobrien * 444236769Sobrien * Results: 445236769Sobrien * 0 if it is the same. Non-zero otherwise 446236769Sobrien * 447236769Sobrien * Side Effects: 448236769Sobrien * None 449236769Sobrien *----------------------------------------------------------------------- 450236769Sobrien */ 451236769Sobrienstatic int 452236769SobrienDirFindName(const void *p, const void *dname) 453236769Sobrien{ 454236769Sobrien return (strcmp(((const Path *)p)->name, dname)); 455236769Sobrien} 456236769Sobrien 457236769Sobrien/*- 458236769Sobrien *----------------------------------------------------------------------- 459236769Sobrien * Dir_HasWildcards -- 460236769Sobrien * see if the given name has any wildcard characters in it 461236769Sobrien * be careful not to expand unmatching brackets or braces. 462236769Sobrien * XXX: This code is not 100% correct. ([^]] fails etc.) 463236769Sobrien * I really don't think that make(1) should be expanding 464236769Sobrien * patterns, because then you have to set a mechanism for 465236769Sobrien * escaping the expansion! 466236769Sobrien * 467236769Sobrien * Input: 468236769Sobrien * name name to check 469236769Sobrien * 470236769Sobrien * Results: 471236769Sobrien * returns TRUE if the word should be expanded, FALSE otherwise 472236769Sobrien * 473236769Sobrien * Side Effects: 474236769Sobrien * none 475236769Sobrien *----------------------------------------------------------------------- 476236769Sobrien */ 477236769SobrienBoolean 478236769SobrienDir_HasWildcards(char *name) 479236769Sobrien{ 480236769Sobrien char *cp; 481236769Sobrien int wild = 0, brace = 0, bracket = 0; 482236769Sobrien 483236769Sobrien for (cp = name; *cp; cp++) { 484236769Sobrien switch(*cp) { 485236769Sobrien case '{': 486236769Sobrien brace++; 487236769Sobrien wild = 1; 488236769Sobrien break; 489236769Sobrien case '}': 490236769Sobrien brace--; 491236769Sobrien break; 492236769Sobrien case '[': 493236769Sobrien bracket++; 494236769Sobrien wild = 1; 495236769Sobrien break; 496236769Sobrien case ']': 497236769Sobrien bracket--; 498236769Sobrien break; 499236769Sobrien case '?': 500236769Sobrien case '*': 501236769Sobrien wild = 1; 502236769Sobrien break; 503236769Sobrien default: 504236769Sobrien break; 505236769Sobrien } 506236769Sobrien } 507236769Sobrien return wild && bracket == 0 && brace == 0; 508236769Sobrien} 509236769Sobrien 510236769Sobrien/*- 511236769Sobrien *----------------------------------------------------------------------- 512236769Sobrien * DirMatchFiles -- 513236769Sobrien * Given a pattern and a Path structure, see if any files 514236769Sobrien * match the pattern and add their names to the 'expansions' list if 515236769Sobrien * any do. This is incomplete -- it doesn't take care of patterns like 516236769Sobrien * src / *src / *.c properly (just *.c on any of the directories), but it 517236769Sobrien * will do for now. 518236769Sobrien * 519236769Sobrien * Input: 520236769Sobrien * pattern Pattern to look for 521236769Sobrien * p Directory to search 522236769Sobrien * expansion Place to store the results 523236769Sobrien * 524236769Sobrien * Results: 525236769Sobrien * Always returns 0 526236769Sobrien * 527236769Sobrien * Side Effects: 528236769Sobrien * File names are added to the expansions lst. The directory will be 529236769Sobrien * fully hashed when this is done. 530236769Sobrien *----------------------------------------------------------------------- 531236769Sobrien */ 532236769Sobrienstatic int 533236769SobrienDirMatchFiles(const char *pattern, Path *p, Lst expansions) 534236769Sobrien{ 535236769Sobrien Hash_Search search; /* Index into the directory's table */ 536236769Sobrien Hash_Entry *entry; /* Current entry in the table */ 537236769Sobrien Boolean isDot; /* TRUE if the directory being searched is . */ 538236769Sobrien 539236769Sobrien isDot = (*p->name == '.' && p->name[1] == '\0'); 540236769Sobrien 541236769Sobrien for (entry = Hash_EnumFirst(&p->files, &search); 542236769Sobrien entry != NULL; 543236769Sobrien entry = Hash_EnumNext(&search)) 544236769Sobrien { 545236769Sobrien /* 546236769Sobrien * See if the file matches the given pattern. Note we follow the UNIX 547236769Sobrien * convention that dot files will only be found if the pattern 548236769Sobrien * begins with a dot (note also that as a side effect of the hashing 549236769Sobrien * scheme, .* won't match . or .. since they aren't hashed). 550236769Sobrien */ 551236769Sobrien if (Str_Match(entry->name, pattern) && 552236769Sobrien ((entry->name[0] != '.') || 553236769Sobrien (pattern[0] == '.'))) 554236769Sobrien { 555236769Sobrien (void)Lst_AtEnd(expansions, 556236769Sobrien (isDot ? bmake_strdup(entry->name) : 557236769Sobrien str_concat(p->name, entry->name, 558236769Sobrien STR_ADDSLASH))); 559236769Sobrien } 560236769Sobrien } 561236769Sobrien return (0); 562236769Sobrien} 563236769Sobrien 564236769Sobrien/*- 565236769Sobrien *----------------------------------------------------------------------- 566236769Sobrien * DirExpandCurly -- 567236769Sobrien * Expand curly braces like the C shell. Does this recursively. 568236769Sobrien * Note the special case: if after the piece of the curly brace is 569236769Sobrien * done there are no wildcard characters in the result, the result is 570236769Sobrien * placed on the list WITHOUT CHECKING FOR ITS EXISTENCE. 571236769Sobrien * 572236769Sobrien * Input: 573236769Sobrien * word Entire word to expand 574236769Sobrien * brace First curly brace in it 575236769Sobrien * path Search path to use 576236769Sobrien * expansions Place to store the expansions 577236769Sobrien * 578236769Sobrien * Results: 579236769Sobrien * None. 580236769Sobrien * 581236769Sobrien * Side Effects: 582236769Sobrien * The given list is filled with the expansions... 583236769Sobrien * 584236769Sobrien *----------------------------------------------------------------------- 585236769Sobrien */ 586236769Sobrienstatic void 587236769SobrienDirExpandCurly(const char *word, const char *brace, Lst path, Lst expansions) 588236769Sobrien{ 589236769Sobrien const char *end; /* Character after the closing brace */ 590236769Sobrien const char *cp; /* Current position in brace clause */ 591236769Sobrien const char *start; /* Start of current piece of brace clause */ 592236769Sobrien int bracelevel; /* Number of braces we've seen. If we see a 593236769Sobrien * right brace when this is 0, we've hit the 594236769Sobrien * end of the clause. */ 595236769Sobrien char *file; /* Current expansion */ 596236769Sobrien int otherLen; /* The length of the other pieces of the 597236769Sobrien * expansion (chars before and after the 598236769Sobrien * clause in 'word') */ 599236769Sobrien char *cp2; /* Pointer for checking for wildcards in 600236769Sobrien * expansion before calling Dir_Expand */ 601236769Sobrien 602236769Sobrien start = brace+1; 603236769Sobrien 604236769Sobrien /* 605236769Sobrien * Find the end of the brace clause first, being wary of nested brace 606236769Sobrien * clauses. 607236769Sobrien */ 608236769Sobrien for (end = start, bracelevel = 0; *end != '\0'; end++) { 609236769Sobrien if (*end == '{') { 610236769Sobrien bracelevel++; 611236769Sobrien } else if ((*end == '}') && (bracelevel-- == 0)) { 612236769Sobrien break; 613236769Sobrien } 614236769Sobrien } 615236769Sobrien if (*end == '\0') { 616236769Sobrien Error("Unterminated {} clause \"%s\"", start); 617236769Sobrien return; 618236769Sobrien } else { 619236769Sobrien end++; 620236769Sobrien } 621236769Sobrien otherLen = brace - word + strlen(end); 622236769Sobrien 623236769Sobrien for (cp = start; cp < end; cp++) { 624236769Sobrien /* 625236769Sobrien * Find the end of this piece of the clause. 626236769Sobrien */ 627236769Sobrien bracelevel = 0; 628236769Sobrien while (*cp != ',') { 629236769Sobrien if (*cp == '{') { 630236769Sobrien bracelevel++; 631236769Sobrien } else if ((*cp == '}') && (bracelevel-- <= 0)) { 632236769Sobrien break; 633236769Sobrien } 634236769Sobrien cp++; 635236769Sobrien } 636236769Sobrien /* 637236769Sobrien * Allocate room for the combination and install the three pieces. 638236769Sobrien */ 639236769Sobrien file = bmake_malloc(otherLen + cp - start + 1); 640236769Sobrien if (brace != word) { 641236769Sobrien strncpy(file, word, brace-word); 642236769Sobrien } 643236769Sobrien if (cp != start) { 644236769Sobrien strncpy(&file[brace-word], start, cp-start); 645236769Sobrien } 646236769Sobrien strcpy(&file[(brace-word)+(cp-start)], end); 647236769Sobrien 648236769Sobrien /* 649236769Sobrien * See if the result has any wildcards in it. If we find one, call 650236769Sobrien * Dir_Expand right away, telling it to place the result on our list 651236769Sobrien * of expansions. 652236769Sobrien */ 653236769Sobrien for (cp2 = file; *cp2 != '\0'; cp2++) { 654236769Sobrien switch(*cp2) { 655236769Sobrien case '*': 656236769Sobrien case '?': 657236769Sobrien case '{': 658236769Sobrien case '[': 659236769Sobrien Dir_Expand(file, path, expansions); 660236769Sobrien goto next; 661236769Sobrien } 662236769Sobrien } 663236769Sobrien if (*cp2 == '\0') { 664236769Sobrien /* 665236769Sobrien * Hit the end w/o finding any wildcards, so stick the expansion 666236769Sobrien * on the end of the list. 667236769Sobrien */ 668236769Sobrien (void)Lst_AtEnd(expansions, file); 669236769Sobrien } else { 670236769Sobrien next: 671236769Sobrien free(file); 672236769Sobrien } 673236769Sobrien start = cp+1; 674236769Sobrien } 675236769Sobrien} 676236769Sobrien 677236769Sobrien 678236769Sobrien/*- 679236769Sobrien *----------------------------------------------------------------------- 680236769Sobrien * DirExpandInt -- 681236769Sobrien * Internal expand routine. Passes through the directories in the 682236769Sobrien * path one by one, calling DirMatchFiles for each. NOTE: This still 683236769Sobrien * doesn't handle patterns in directories... 684236769Sobrien * 685236769Sobrien * Input: 686236769Sobrien * word Word to expand 687236769Sobrien * path Path on which to look 688236769Sobrien * expansions Place to store the result 689236769Sobrien * 690236769Sobrien * Results: 691236769Sobrien * None. 692236769Sobrien * 693236769Sobrien * Side Effects: 694236769Sobrien * Things are added to the expansions list. 695236769Sobrien * 696236769Sobrien *----------------------------------------------------------------------- 697236769Sobrien */ 698236769Sobrienstatic void 699236769SobrienDirExpandInt(const char *word, Lst path, Lst expansions) 700236769Sobrien{ 701236769Sobrien LstNode ln; /* Current node */ 702236769Sobrien Path *p; /* Directory in the node */ 703236769Sobrien 704236769Sobrien if (Lst_Open(path) == SUCCESS) { 705236769Sobrien while ((ln = Lst_Next(path)) != NULL) { 706236769Sobrien p = (Path *)Lst_Datum(ln); 707236769Sobrien DirMatchFiles(word, p, expansions); 708236769Sobrien } 709236769Sobrien Lst_Close(path); 710236769Sobrien } 711236769Sobrien} 712236769Sobrien 713236769Sobrien/*- 714236769Sobrien *----------------------------------------------------------------------- 715236769Sobrien * DirPrintWord -- 716236769Sobrien * Print a word in the list of expansions. Callback for Dir_Expand 717236769Sobrien * when DEBUG(DIR), via Lst_ForEach. 718236769Sobrien * 719236769Sobrien * Results: 720236769Sobrien * === 0 721236769Sobrien * 722236769Sobrien * Side Effects: 723236769Sobrien * The passed word is printed, followed by a space. 724236769Sobrien * 725236769Sobrien *----------------------------------------------------------------------- 726236769Sobrien */ 727236769Sobrienstatic int 728236769SobrienDirPrintWord(void *word, void *dummy) 729236769Sobrien{ 730236769Sobrien fprintf(debug_file, "%s ", (char *)word); 731236769Sobrien 732236769Sobrien return(dummy ? 0 : 0); 733236769Sobrien} 734236769Sobrien 735236769Sobrien/*- 736236769Sobrien *----------------------------------------------------------------------- 737236769Sobrien * Dir_Expand -- 738236769Sobrien * Expand the given word into a list of words by globbing it looking 739236769Sobrien * in the directories on the given search path. 740236769Sobrien * 741236769Sobrien * Input: 742236769Sobrien * word the word to expand 743236769Sobrien * path the list of directories in which to find the 744236769Sobrien * resulting files 745236769Sobrien * expansions the list on which to place the results 746236769Sobrien * 747236769Sobrien * Results: 748236769Sobrien * A list of words consisting of the files which exist along the search 749236769Sobrien * path matching the given pattern. 750236769Sobrien * 751236769Sobrien * Side Effects: 752236769Sobrien * Directories may be opened. Who knows? 753236769Sobrien *----------------------------------------------------------------------- 754236769Sobrien */ 755236769Sobrienvoid 756236769SobrienDir_Expand(const char *word, Lst path, Lst expansions) 757236769Sobrien{ 758236769Sobrien const char *cp; 759236769Sobrien 760236769Sobrien if (DEBUG(DIR)) { 761236769Sobrien fprintf(debug_file, "Expanding \"%s\"... ", word); 762236769Sobrien } 763236769Sobrien 764236769Sobrien cp = strchr(word, '{'); 765236769Sobrien if (cp) { 766236769Sobrien DirExpandCurly(word, cp, path, expansions); 767236769Sobrien } else { 768236769Sobrien cp = strchr(word, '/'); 769236769Sobrien if (cp) { 770236769Sobrien /* 771236769Sobrien * The thing has a directory component -- find the first wildcard 772236769Sobrien * in the string. 773236769Sobrien */ 774236769Sobrien for (cp = word; *cp; cp++) { 775236769Sobrien if (*cp == '?' || *cp == '[' || *cp == '*' || *cp == '{') { 776236769Sobrien break; 777236769Sobrien } 778236769Sobrien } 779236769Sobrien if (*cp == '{') { 780236769Sobrien /* 781236769Sobrien * This one will be fun. 782236769Sobrien */ 783236769Sobrien DirExpandCurly(word, cp, path, expansions); 784236769Sobrien return; 785236769Sobrien } else if (*cp != '\0') { 786236769Sobrien /* 787236769Sobrien * Back up to the start of the component 788236769Sobrien */ 789236769Sobrien char *dirpath; 790236769Sobrien 791236769Sobrien while (cp > word && *cp != '/') { 792236769Sobrien cp--; 793236769Sobrien } 794236769Sobrien if (cp != word) { 795236769Sobrien char sc; 796236769Sobrien /* 797236769Sobrien * If the glob isn't in the first component, try and find 798236769Sobrien * all the components up to the one with a wildcard. 799236769Sobrien */ 800236769Sobrien sc = cp[1]; 801236769Sobrien ((char *)UNCONST(cp))[1] = '\0'; 802236769Sobrien dirpath = Dir_FindFile(word, path); 803236769Sobrien ((char *)UNCONST(cp))[1] = sc; 804236769Sobrien /* 805236769Sobrien * dirpath is null if can't find the leading component 806236769Sobrien * XXX: Dir_FindFile won't find internal components. 807236769Sobrien * i.e. if the path contains ../Etc/Object and we're 808236769Sobrien * looking for Etc, it won't be found. Ah well. 809236769Sobrien * Probably not important. 810236769Sobrien */ 811236769Sobrien if (dirpath != NULL) { 812236769Sobrien char *dp = &dirpath[strlen(dirpath) - 1]; 813236769Sobrien if (*dp == '/') 814236769Sobrien *dp = '\0'; 815236769Sobrien path = Lst_Init(FALSE); 816236769Sobrien (void)Dir_AddDir(path, dirpath); 817236769Sobrien DirExpandInt(cp+1, path, expansions); 818236769Sobrien Lst_Destroy(path, NULL); 819236769Sobrien } 820236769Sobrien } else { 821236769Sobrien /* 822236769Sobrien * Start the search from the local directory 823236769Sobrien */ 824236769Sobrien DirExpandInt(word, path, expansions); 825236769Sobrien } 826236769Sobrien } else { 827236769Sobrien /* 828236769Sobrien * Return the file -- this should never happen. 829236769Sobrien */ 830236769Sobrien DirExpandInt(word, path, expansions); 831236769Sobrien } 832236769Sobrien } else { 833236769Sobrien /* 834236769Sobrien * First the files in dot 835236769Sobrien */ 836236769Sobrien DirMatchFiles(word, dot, expansions); 837236769Sobrien 838236769Sobrien /* 839236769Sobrien * Then the files in every other directory on the path. 840236769Sobrien */ 841236769Sobrien DirExpandInt(word, path, expansions); 842236769Sobrien } 843236769Sobrien } 844236769Sobrien if (DEBUG(DIR)) { 845236769Sobrien Lst_ForEach(expansions, DirPrintWord, NULL); 846236769Sobrien fprintf(debug_file, "\n"); 847236769Sobrien } 848236769Sobrien} 849236769Sobrien 850236769Sobrien/*- 851236769Sobrien *----------------------------------------------------------------------- 852236769Sobrien * DirLookup -- 853236769Sobrien * Find if the file with the given name exists in the given path. 854236769Sobrien * 855236769Sobrien * Results: 856236769Sobrien * The path to the file or NULL. This path is guaranteed to be in a 857236769Sobrien * different part of memory than name and so may be safely free'd. 858236769Sobrien * 859236769Sobrien * Side Effects: 860236769Sobrien * None. 861236769Sobrien *----------------------------------------------------------------------- 862236769Sobrien */ 863236769Sobrienstatic char * 864237578SobrienDirLookup(Path *p, const char *name MAKE_ATTR_UNUSED, const char *cp, 865237578Sobrien Boolean hasSlash MAKE_ATTR_UNUSED) 866236769Sobrien{ 867236769Sobrien char *file; /* the current filename to check */ 868236769Sobrien 869236769Sobrien if (DEBUG(DIR)) { 870236769Sobrien fprintf(debug_file, " %s ...\n", p->name); 871236769Sobrien } 872236769Sobrien 873236769Sobrien if (Hash_FindEntry(&p->files, cp) == NULL) 874236769Sobrien return NULL; 875236769Sobrien 876236769Sobrien file = str_concat(p->name, cp, STR_ADDSLASH); 877236769Sobrien if (DEBUG(DIR)) { 878236769Sobrien fprintf(debug_file, " returning %s\n", file); 879236769Sobrien } 880236769Sobrien p->hits += 1; 881236769Sobrien hits += 1; 882236769Sobrien return file; 883236769Sobrien} 884236769Sobrien 885236769Sobrien 886236769Sobrien/*- 887236769Sobrien *----------------------------------------------------------------------- 888236769Sobrien * DirLookupSubdir -- 889236769Sobrien * Find if the file with the given name exists in the given path. 890236769Sobrien * 891236769Sobrien * Results: 892236769Sobrien * The path to the file or NULL. This path is guaranteed to be in a 893236769Sobrien * different part of memory than name and so may be safely free'd. 894236769Sobrien * 895236769Sobrien * Side Effects: 896236769Sobrien * If the file is found, it is added in the modification times hash 897236769Sobrien * table. 898236769Sobrien *----------------------------------------------------------------------- 899236769Sobrien */ 900236769Sobrienstatic char * 901236769SobrienDirLookupSubdir(Path *p, const char *name) 902236769Sobrien{ 903236769Sobrien struct stat stb; /* Buffer for stat, if necessary */ 904236769Sobrien Hash_Entry *entry; /* Entry for mtimes table */ 905236769Sobrien char *file; /* the current filename to check */ 906236769Sobrien 907236769Sobrien if (p != dot) { 908236769Sobrien file = str_concat(p->name, name, STR_ADDSLASH); 909236769Sobrien } else { 910236769Sobrien /* 911236769Sobrien * Checking in dot -- DON'T put a leading ./ on the thing. 912236769Sobrien */ 913236769Sobrien file = bmake_strdup(name); 914236769Sobrien } 915236769Sobrien 916236769Sobrien if (DEBUG(DIR)) { 917236769Sobrien fprintf(debug_file, "checking %s ...\n", file); 918236769Sobrien } 919236769Sobrien 920236769Sobrien if (stat(file, &stb) == 0) { 921236769Sobrien if (stb.st_mtime == 0) 922236769Sobrien stb.st_mtime = 1; 923236769Sobrien /* 924236769Sobrien * Save the modification time so if it's needed, we don't have 925236769Sobrien * to fetch it again. 926236769Sobrien */ 927236769Sobrien if (DEBUG(DIR)) { 928236769Sobrien fprintf(debug_file, " Caching %s for %s\n", Targ_FmtTime(stb.st_mtime), 929236769Sobrien file); 930236769Sobrien } 931236769Sobrien entry = Hash_CreateEntry(&mtimes, file, NULL); 932236769Sobrien Hash_SetTimeValue(entry, stb.st_mtime); 933236769Sobrien nearmisses += 1; 934236769Sobrien return (file); 935236769Sobrien } 936236769Sobrien free(file); 937236769Sobrien return NULL; 938236769Sobrien} 939236769Sobrien 940236769Sobrien/*- 941236769Sobrien *----------------------------------------------------------------------- 942236769Sobrien * DirLookupAbs -- 943236769Sobrien * Find if the file with the given name exists in the given path. 944236769Sobrien * 945236769Sobrien * Results: 946236769Sobrien * The path to the file, the empty string or NULL. If the file is 947236769Sobrien * the empty string, the search should be terminated. 948236769Sobrien * This path is guaranteed to be in a different part of memory 949236769Sobrien * than name and so may be safely free'd. 950236769Sobrien * 951236769Sobrien * Side Effects: 952236769Sobrien * None. 953236769Sobrien *----------------------------------------------------------------------- 954236769Sobrien */ 955236769Sobrienstatic char * 956236769SobrienDirLookupAbs(Path *p, const char *name, const char *cp) 957236769Sobrien{ 958236769Sobrien char *p1; /* pointer into p->name */ 959236769Sobrien const char *p2; /* pointer into name */ 960236769Sobrien 961236769Sobrien if (DEBUG(DIR)) { 962236769Sobrien fprintf(debug_file, " %s ...\n", p->name); 963236769Sobrien } 964236769Sobrien 965236769Sobrien /* 966236769Sobrien * If the file has a leading path component and that component 967236769Sobrien * exactly matches the entire name of the current search 968236769Sobrien * directory, we can attempt another cache lookup. And if we don't 969236769Sobrien * have a hit, we can safely assume the file does not exist at all. 970236769Sobrien */ 971236769Sobrien for (p1 = p->name, p2 = name; *p1 && *p1 == *p2; p1++, p2++) { 972236769Sobrien continue; 973236769Sobrien } 974236769Sobrien if (*p1 != '\0' || p2 != cp - 1) { 975236769Sobrien return NULL; 976236769Sobrien } 977236769Sobrien 978236769Sobrien if (Hash_FindEntry(&p->files, cp) == NULL) { 979236769Sobrien if (DEBUG(DIR)) { 980236769Sobrien fprintf(debug_file, " must be here but isn't -- returning\n"); 981236769Sobrien } 982236769Sobrien /* Return empty string: terminates search */ 983236769Sobrien return bmake_strdup(""); 984236769Sobrien } 985236769Sobrien 986236769Sobrien p->hits += 1; 987236769Sobrien hits += 1; 988236769Sobrien if (DEBUG(DIR)) { 989236769Sobrien fprintf(debug_file, " returning %s\n", name); 990236769Sobrien } 991236769Sobrien return (bmake_strdup(name)); 992236769Sobrien} 993236769Sobrien 994236769Sobrien/*- 995236769Sobrien *----------------------------------------------------------------------- 996236769Sobrien * DirFindDot -- 997236769Sobrien * Find the file given on "." or curdir 998236769Sobrien * 999236769Sobrien * Results: 1000236769Sobrien * The path to the file or NULL. This path is guaranteed to be in a 1001236769Sobrien * different part of memory than name and so may be safely free'd. 1002236769Sobrien * 1003236769Sobrien * Side Effects: 1004236769Sobrien * Hit counts change 1005236769Sobrien *----------------------------------------------------------------------- 1006236769Sobrien */ 1007236769Sobrienstatic char * 1008237578SobrienDirFindDot(Boolean hasSlash MAKE_ATTR_UNUSED, const char *name, const char *cp) 1009236769Sobrien{ 1010236769Sobrien 1011236769Sobrien if (Hash_FindEntry(&dot->files, cp) != NULL) { 1012236769Sobrien if (DEBUG(DIR)) { 1013236769Sobrien fprintf(debug_file, " in '.'\n"); 1014236769Sobrien } 1015236769Sobrien hits += 1; 1016236769Sobrien dot->hits += 1; 1017236769Sobrien return (bmake_strdup(name)); 1018236769Sobrien } 1019236769Sobrien if (cur && 1020236769Sobrien Hash_FindEntry(&cur->files, cp) != NULL) { 1021236769Sobrien if (DEBUG(DIR)) { 1022236769Sobrien fprintf(debug_file, " in ${.CURDIR} = %s\n", cur->name); 1023236769Sobrien } 1024236769Sobrien hits += 1; 1025236769Sobrien cur->hits += 1; 1026236769Sobrien return str_concat(cur->name, cp, STR_ADDSLASH); 1027236769Sobrien } 1028236769Sobrien 1029236769Sobrien return NULL; 1030236769Sobrien} 1031236769Sobrien 1032236769Sobrien/*- 1033236769Sobrien *----------------------------------------------------------------------- 1034236769Sobrien * Dir_FindFile -- 1035236769Sobrien * Find the file with the given name along the given search path. 1036236769Sobrien * 1037236769Sobrien * Input: 1038236769Sobrien * name the file to find 1039236769Sobrien * path the Lst of directories to search 1040236769Sobrien * 1041236769Sobrien * Results: 1042236769Sobrien * The path to the file or NULL. This path is guaranteed to be in a 1043236769Sobrien * different part of memory than name and so may be safely free'd. 1044236769Sobrien * 1045236769Sobrien * Side Effects: 1046236769Sobrien * If the file is found in a directory which is not on the path 1047236769Sobrien * already (either 'name' is absolute or it is a relative path 1048236769Sobrien * [ dir1/.../dirn/file ] which exists below one of the directories 1049236769Sobrien * already on the search path), its directory is added to the end 1050236769Sobrien * of the path on the assumption that there will be more files in 1051236769Sobrien * that directory later on. Sometimes this is true. Sometimes not. 1052236769Sobrien *----------------------------------------------------------------------- 1053236769Sobrien */ 1054236769Sobrienchar * 1055236769SobrienDir_FindFile(const char *name, Lst path) 1056236769Sobrien{ 1057236769Sobrien LstNode ln; /* a list element */ 1058236769Sobrien char *file; /* the current filename to check */ 1059236769Sobrien Path *p; /* current path member */ 1060236769Sobrien const char *cp; /* Terminal name of file */ 1061236769Sobrien Boolean hasLastDot = FALSE; /* true we should search dot last */ 1062236769Sobrien Boolean hasSlash; /* true if 'name' contains a / */ 1063236769Sobrien struct stat stb; /* Buffer for stat, if necessary */ 1064236769Sobrien Hash_Entry *entry; /* Entry for mtimes table */ 1065236769Sobrien const char *trailing_dot = "."; 1066236769Sobrien 1067236769Sobrien /* 1068236769Sobrien * Find the final component of the name and note whether it has a 1069236769Sobrien * slash in it (the name, I mean) 1070236769Sobrien */ 1071236769Sobrien cp = strrchr(name, '/'); 1072236769Sobrien if (cp) { 1073236769Sobrien hasSlash = TRUE; 1074236769Sobrien cp += 1; 1075236769Sobrien } else { 1076236769Sobrien hasSlash = FALSE; 1077236769Sobrien cp = name; 1078236769Sobrien } 1079236769Sobrien 1080236769Sobrien if (DEBUG(DIR)) { 1081236769Sobrien fprintf(debug_file, "Searching for %s ...", name); 1082236769Sobrien } 1083236769Sobrien 1084236769Sobrien if (Lst_Open(path) == FAILURE) { 1085236769Sobrien if (DEBUG(DIR)) { 1086236769Sobrien fprintf(debug_file, "couldn't open path, file not found\n"); 1087236769Sobrien } 1088236769Sobrien misses += 1; 1089236769Sobrien return NULL; 1090236769Sobrien } 1091236769Sobrien 1092236769Sobrien if ((ln = Lst_First(path)) != NULL) { 1093236769Sobrien p = (Path *)Lst_Datum(ln); 1094236769Sobrien if (p == dotLast) { 1095236769Sobrien hasLastDot = TRUE; 1096236769Sobrien if (DEBUG(DIR)) 1097236769Sobrien fprintf(debug_file, "[dot last]..."); 1098236769Sobrien } 1099236769Sobrien } 1100236769Sobrien if (DEBUG(DIR)) { 1101236769Sobrien fprintf(debug_file, "\n"); 1102236769Sobrien } 1103236769Sobrien 1104236769Sobrien /* 1105236769Sobrien * If there's no leading directory components or if the leading 1106236769Sobrien * directory component is exactly `./', consult the cached contents 1107236769Sobrien * of each of the directories on the search path. 1108236769Sobrien */ 1109236769Sobrien if (!hasSlash || (cp - name == 2 && *name == '.')) { 1110236769Sobrien /* 1111236769Sobrien * We look through all the directories on the path seeking one which 1112236769Sobrien * contains the final component of the given name. If such a beast 1113236769Sobrien * is found, we concatenate the directory name and the final 1114236769Sobrien * component and return the resulting string. If we don't find any 1115236769Sobrien * such thing, we go on to phase two... 1116236769Sobrien * 1117236769Sobrien * No matter what, we always look for the file in the current 1118236769Sobrien * directory before anywhere else (unless we found the magic 1119236769Sobrien * DOTLAST path, in which case we search it last) and we *do not* 1120236769Sobrien * add the ./ to it if it exists. 1121236769Sobrien * This is so there are no conflicts between what the user 1122236769Sobrien * specifies (fish.c) and what pmake finds (./fish.c). 1123236769Sobrien */ 1124236769Sobrien if (!hasLastDot && 1125236769Sobrien (file = DirFindDot(hasSlash, name, cp)) != NULL) { 1126236769Sobrien Lst_Close(path); 1127236769Sobrien return file; 1128236769Sobrien } 1129236769Sobrien 1130236769Sobrien while ((ln = Lst_Next(path)) != NULL) { 1131236769Sobrien p = (Path *)Lst_Datum(ln); 1132236769Sobrien if (p == dotLast) 1133236769Sobrien continue; 1134236769Sobrien if ((file = DirLookup(p, name, cp, hasSlash)) != NULL) { 1135236769Sobrien Lst_Close(path); 1136236769Sobrien return file; 1137236769Sobrien } 1138236769Sobrien } 1139236769Sobrien 1140236769Sobrien if (hasLastDot && 1141236769Sobrien (file = DirFindDot(hasSlash, name, cp)) != NULL) { 1142236769Sobrien Lst_Close(path); 1143236769Sobrien return file; 1144236769Sobrien } 1145236769Sobrien } 1146236769Sobrien Lst_Close(path); 1147236769Sobrien 1148236769Sobrien /* 1149236769Sobrien * We didn't find the file on any directory in the search path. 1150236769Sobrien * If the name doesn't contain a slash, that means it doesn't exist. 1151236769Sobrien * If it *does* contain a slash, however, there is still hope: it 1152236769Sobrien * could be in a subdirectory of one of the members of the search 1153236769Sobrien * path. (eg. /usr/include and sys/types.h. The above search would 1154236769Sobrien * fail to turn up types.h in /usr/include, but it *is* in 1155236769Sobrien * /usr/include/sys/types.h). 1156236769Sobrien * [ This no longer applies: If we find such a beast, we assume there 1157236769Sobrien * will be more (what else can we assume?) and add all but the last 1158236769Sobrien * component of the resulting name onto the search path (at the 1159236769Sobrien * end).] 1160236769Sobrien * This phase is only performed if the file is *not* absolute. 1161236769Sobrien */ 1162236769Sobrien if (!hasSlash) { 1163236769Sobrien if (DEBUG(DIR)) { 1164236769Sobrien fprintf(debug_file, " failed.\n"); 1165236769Sobrien } 1166236769Sobrien misses += 1; 1167236769Sobrien return NULL; 1168236769Sobrien } 1169236769Sobrien 1170236769Sobrien if (*cp == '\0') { 1171236769Sobrien /* we were given a trailing "/" */ 1172236769Sobrien cp = trailing_dot; 1173236769Sobrien } 1174236769Sobrien 1175236769Sobrien if (name[0] != '/') { 1176236769Sobrien Boolean checkedDot = FALSE; 1177236769Sobrien 1178236769Sobrien if (DEBUG(DIR)) { 1179236769Sobrien fprintf(debug_file, " Trying subdirectories...\n"); 1180236769Sobrien } 1181236769Sobrien 1182236769Sobrien if (!hasLastDot) { 1183236769Sobrien if (dot) { 1184236769Sobrien checkedDot = TRUE; 1185236769Sobrien if ((file = DirLookupSubdir(dot, name)) != NULL) 1186236769Sobrien return file; 1187236769Sobrien } 1188236769Sobrien if (cur && (file = DirLookupSubdir(cur, name)) != NULL) 1189236769Sobrien return file; 1190236769Sobrien } 1191236769Sobrien 1192236769Sobrien (void)Lst_Open(path); 1193236769Sobrien while ((ln = Lst_Next(path)) != NULL) { 1194236769Sobrien p = (Path *)Lst_Datum(ln); 1195236769Sobrien if (p == dotLast) 1196236769Sobrien continue; 1197236769Sobrien if (p == dot) { 1198236769Sobrien if (checkedDot) 1199236769Sobrien continue; 1200236769Sobrien checkedDot = TRUE; 1201236769Sobrien } 1202236769Sobrien if ((file = DirLookupSubdir(p, name)) != NULL) { 1203236769Sobrien Lst_Close(path); 1204236769Sobrien return file; 1205236769Sobrien } 1206236769Sobrien } 1207236769Sobrien Lst_Close(path); 1208236769Sobrien 1209236769Sobrien if (hasLastDot) { 1210236769Sobrien if (dot && !checkedDot) { 1211236769Sobrien checkedDot = TRUE; 1212236769Sobrien if ((file = DirLookupSubdir(dot, name)) != NULL) 1213236769Sobrien return file; 1214236769Sobrien } 1215236769Sobrien if (cur && (file = DirLookupSubdir(cur, name)) != NULL) 1216236769Sobrien return file; 1217236769Sobrien } 1218236769Sobrien 1219236769Sobrien if (checkedDot) { 1220236769Sobrien /* 1221236769Sobrien * Already checked by the given name, since . was in the path, 1222236769Sobrien * so no point in proceeding... 1223236769Sobrien */ 1224236769Sobrien if (DEBUG(DIR)) { 1225236769Sobrien fprintf(debug_file, " Checked . already, returning NULL\n"); 1226236769Sobrien } 1227236769Sobrien return NULL; 1228236769Sobrien } 1229236769Sobrien 1230236769Sobrien } else { /* name[0] == '/' */ 1231236769Sobrien 1232236769Sobrien /* 1233236769Sobrien * For absolute names, compare directory path prefix against the 1234236769Sobrien * the directory path of each member on the search path for an exact 1235236769Sobrien * match. If we have an exact match on any member of the search path, 1236236769Sobrien * use the cached contents of that member to lookup the final file 1237236769Sobrien * component. If that lookup fails we can safely assume that the 1238236769Sobrien * file does not exist at all. This is signified by DirLookupAbs() 1239236769Sobrien * returning an empty string. 1240236769Sobrien */ 1241236769Sobrien if (DEBUG(DIR)) { 1242236769Sobrien fprintf(debug_file, " Trying exact path matches...\n"); 1243236769Sobrien } 1244236769Sobrien 1245236769Sobrien if (!hasLastDot && cur && (file = DirLookupAbs(cur, name, cp)) != NULL) 1246236769Sobrien return *file?file:NULL; 1247236769Sobrien 1248236769Sobrien (void)Lst_Open(path); 1249236769Sobrien while ((ln = Lst_Next(path)) != NULL) { 1250236769Sobrien p = (Path *)Lst_Datum(ln); 1251236769Sobrien if (p == dotLast) 1252236769Sobrien continue; 1253236769Sobrien if ((file = DirLookupAbs(p, name, cp)) != NULL) { 1254236769Sobrien Lst_Close(path); 1255236769Sobrien return *file?file:NULL; 1256236769Sobrien } 1257236769Sobrien } 1258236769Sobrien Lst_Close(path); 1259236769Sobrien 1260236769Sobrien if (hasLastDot && cur && (file = DirLookupAbs(cur, name, cp)) != NULL) 1261236769Sobrien return *file?file:NULL; 1262236769Sobrien } 1263236769Sobrien 1264236769Sobrien /* 1265236769Sobrien * Didn't find it that way, either. Sigh. Phase 3. Add its directory 1266236769Sobrien * onto the search path in any case, just in case, then look for the 1267236769Sobrien * thing in the hash table. If we find it, grand. We return a new 1268236769Sobrien * copy of the name. Otherwise we sadly return a NULL pointer. Sigh. 1269236769Sobrien * Note that if the directory holding the file doesn't exist, this will 1270236769Sobrien * do an extra search of the final directory on the path. Unless something 1271236769Sobrien * weird happens, this search won't succeed and life will be groovy. 1272236769Sobrien * 1273236769Sobrien * Sigh. We cannot add the directory onto the search path because 1274236769Sobrien * of this amusing case: 1275236769Sobrien * $(INSTALLDIR)/$(FILE): $(FILE) 1276236769Sobrien * 1277236769Sobrien * $(FILE) exists in $(INSTALLDIR) but not in the current one. 1278236769Sobrien * When searching for $(FILE), we will find it in $(INSTALLDIR) 1279236769Sobrien * b/c we added it here. This is not good... 1280236769Sobrien */ 1281236769Sobrien#ifdef notdef 1282236769Sobrien if (cp == traling_dot) { 1283236769Sobrien cp = strrchr(name, '/'); 1284236769Sobrien cp += 1; 1285236769Sobrien } 1286236769Sobrien cp[-1] = '\0'; 1287236769Sobrien (void)Dir_AddDir(path, name); 1288236769Sobrien cp[-1] = '/'; 1289236769Sobrien 1290236769Sobrien bigmisses += 1; 1291236769Sobrien ln = Lst_Last(path); 1292236769Sobrien if (ln == NULL) { 1293236769Sobrien return NULL; 1294236769Sobrien } else { 1295236769Sobrien p = (Path *)Lst_Datum(ln); 1296236769Sobrien } 1297236769Sobrien 1298236769Sobrien if (Hash_FindEntry(&p->files, cp) != NULL) { 1299236769Sobrien return (bmake_strdup(name)); 1300236769Sobrien } else { 1301236769Sobrien return NULL; 1302236769Sobrien } 1303236769Sobrien#else /* !notdef */ 1304236769Sobrien if (DEBUG(DIR)) { 1305236769Sobrien fprintf(debug_file, " Looking for \"%s\" ...\n", name); 1306236769Sobrien } 1307236769Sobrien 1308236769Sobrien bigmisses += 1; 1309236769Sobrien entry = Hash_FindEntry(&mtimes, name); 1310236769Sobrien if (entry != NULL) { 1311236769Sobrien if (DEBUG(DIR)) { 1312236769Sobrien fprintf(debug_file, " got it (in mtime cache)\n"); 1313236769Sobrien } 1314236769Sobrien return(bmake_strdup(name)); 1315236769Sobrien } else if (stat(name, &stb) == 0) { 1316236769Sobrien if (stb.st_mtime == 0) 1317236769Sobrien stb.st_mtime = 1; 1318236769Sobrien entry = Hash_CreateEntry(&mtimes, name, NULL); 1319236769Sobrien if (DEBUG(DIR)) { 1320236769Sobrien fprintf(debug_file, " Caching %s for %s\n", Targ_FmtTime(stb.st_mtime), 1321236769Sobrien name); 1322236769Sobrien } 1323236769Sobrien Hash_SetTimeValue(entry, stb.st_mtime); 1324236769Sobrien return (bmake_strdup(name)); 1325236769Sobrien } else { 1326236769Sobrien if (DEBUG(DIR)) { 1327236769Sobrien fprintf(debug_file, " failed. Returning NULL\n"); 1328236769Sobrien } 1329236769Sobrien return NULL; 1330236769Sobrien } 1331236769Sobrien#endif /* notdef */ 1332236769Sobrien} 1333236769Sobrien 1334236769Sobrien 1335236769Sobrien/*- 1336236769Sobrien *----------------------------------------------------------------------- 1337236769Sobrien * Dir_FindHereOrAbove -- 1338236769Sobrien * search for a path starting at a given directory and then working 1339236769Sobrien * our way up towards the root. 1340236769Sobrien * 1341236769Sobrien * Input: 1342236769Sobrien * here starting directory 1343236769Sobrien * search_path the path we are looking for 1344236769Sobrien * result the result of a successful search is placed here 1345236769Sobrien * rlen the length of the result buffer 1346236769Sobrien * (typically MAXPATHLEN + 1) 1347236769Sobrien * 1348236769Sobrien * Results: 1349236769Sobrien * 0 on failure, 1 on success [in which case the found path is put 1350236769Sobrien * in the result buffer]. 1351236769Sobrien * 1352236769Sobrien * Side Effects: 1353236769Sobrien *----------------------------------------------------------------------- 1354236769Sobrien */ 1355236769Sobrienint 1356236769SobrienDir_FindHereOrAbove(char *here, char *search_path, char *result, int rlen) { 1357236769Sobrien 1358236769Sobrien struct stat st; 1359236769Sobrien char dirbase[MAXPATHLEN + 1], *db_end; 1360236769Sobrien char try[MAXPATHLEN + 1], *try_end; 1361236769Sobrien 1362236769Sobrien /* copy out our starting point */ 1363236769Sobrien snprintf(dirbase, sizeof(dirbase), "%s", here); 1364236769Sobrien db_end = dirbase + strlen(dirbase); 1365236769Sobrien 1366236769Sobrien /* loop until we determine a result */ 1367236769Sobrien while (1) { 1368236769Sobrien 1369236769Sobrien /* try and stat(2) it ... */ 1370236769Sobrien snprintf(try, sizeof(try), "%s/%s", dirbase, search_path); 1371236769Sobrien if (stat(try, &st) != -1) { 1372236769Sobrien /* 1373236769Sobrien * success! if we found a file, chop off 1374236769Sobrien * the filename so we return a directory. 1375236769Sobrien */ 1376236769Sobrien if ((st.st_mode & S_IFMT) != S_IFDIR) { 1377236769Sobrien try_end = try + strlen(try); 1378236769Sobrien while (try_end > try && *try_end != '/') 1379236769Sobrien try_end--; 1380236769Sobrien if (try_end > try) 1381236769Sobrien *try_end = 0; /* chop! */ 1382236769Sobrien } 1383236769Sobrien 1384236769Sobrien /* 1385236769Sobrien * done! 1386236769Sobrien */ 1387236769Sobrien snprintf(result, rlen, "%s", try); 1388236769Sobrien return(1); 1389236769Sobrien } 1390236769Sobrien 1391236769Sobrien /* 1392236769Sobrien * nope, we didn't find it. if we used up dirbase we've 1393236769Sobrien * reached the root and failed. 1394236769Sobrien */ 1395236769Sobrien if (db_end == dirbase) 1396236769Sobrien break; /* failed! */ 1397236769Sobrien 1398236769Sobrien /* 1399236769Sobrien * truncate dirbase from the end to move up a dir 1400236769Sobrien */ 1401236769Sobrien while (db_end > dirbase && *db_end != '/') 1402236769Sobrien db_end--; 1403236769Sobrien *db_end = 0; /* chop! */ 1404236769Sobrien 1405236769Sobrien } /* while (1) */ 1406236769Sobrien 1407236769Sobrien /* 1408236769Sobrien * we failed... 1409236769Sobrien */ 1410236769Sobrien return(0); 1411236769Sobrien} 1412236769Sobrien 1413236769Sobrien/*- 1414236769Sobrien *----------------------------------------------------------------------- 1415236769Sobrien * Dir_MTime -- 1416236769Sobrien * Find the modification time of the file described by gn along the 1417236769Sobrien * search path dirSearchPath. 1418236769Sobrien * 1419236769Sobrien * Input: 1420236769Sobrien * gn the file whose modification time is desired 1421236769Sobrien * 1422236769Sobrien * Results: 1423236769Sobrien * The modification time or 0 if it doesn't exist 1424236769Sobrien * 1425236769Sobrien * Side Effects: 1426236769Sobrien * The modification time is placed in the node's mtime slot. 1427236769Sobrien * If the node didn't have a path entry before, and Dir_FindFile 1428236769Sobrien * found one for it, the full name is placed in the path slot. 1429236769Sobrien *----------------------------------------------------------------------- 1430236769Sobrien */ 1431236769Sobrienint 1432236769SobrienDir_MTime(GNode *gn, Boolean recheck) 1433236769Sobrien{ 1434236769Sobrien char *fullName; /* the full pathname of name */ 1435236769Sobrien struct stat stb; /* buffer for finding the mod time */ 1436236769Sobrien Hash_Entry *entry; 1437236769Sobrien 1438236769Sobrien if (gn->type & OP_ARCHV) { 1439236769Sobrien return Arch_MTime(gn); 1440236769Sobrien } else if (gn->type & OP_PHONY) { 1441236769Sobrien gn->mtime = 0; 1442236769Sobrien return 0; 1443236769Sobrien } else if (gn->path == NULL) { 1444236769Sobrien if (gn->type & OP_NOPATH) 1445236769Sobrien fullName = NULL; 1446236769Sobrien else { 1447236769Sobrien fullName = Dir_FindFile(gn->name, Suff_FindPath(gn)); 1448236769Sobrien if (fullName == NULL && gn->flags & FROM_DEPEND && 1449236769Sobrien !Lst_IsEmpty(gn->iParents)) { 1450236769Sobrien char *cp; 1451236769Sobrien 1452236769Sobrien cp = strrchr(gn->name, '/'); 1453236769Sobrien if (cp) { 1454236769Sobrien /* 1455236769Sobrien * This is an implied source, and it may have moved, 1456236769Sobrien * see if we can find it via the current .PATH 1457236769Sobrien */ 1458236769Sobrien cp++; 1459236769Sobrien 1460236769Sobrien fullName = Dir_FindFile(cp, Suff_FindPath(gn)); 1461236769Sobrien if (fullName) { 1462236769Sobrien /* 1463236769Sobrien * Put the found file in gn->path 1464236769Sobrien * so that we give that to the compiler. 1465236769Sobrien */ 1466236769Sobrien gn->path = bmake_strdup(fullName); 1467249033Ssjg if (!Job_RunTarget(".STALE", gn->fname)) 1468249033Ssjg fprintf(stdout, 1469249033Ssjg "%s: %s, %d: ignoring stale %s for %s, " 1470249033Ssjg "found %s\n", progname, gn->fname, gn->lineno, 1471249033Ssjg makeDependfile, gn->name, fullName); 1472236769Sobrien } 1473236769Sobrien } 1474236769Sobrien } 1475236769Sobrien if (DEBUG(DIR)) 1476236769Sobrien fprintf(debug_file, "Found '%s' as '%s'\n", 1477236769Sobrien gn->name, fullName ? fullName : "(not found)" ); 1478236769Sobrien } 1479236769Sobrien } else { 1480236769Sobrien fullName = gn->path; 1481236769Sobrien } 1482236769Sobrien 1483236769Sobrien if (fullName == NULL) { 1484236769Sobrien fullName = bmake_strdup(gn->name); 1485236769Sobrien } 1486236769Sobrien 1487236769Sobrien if (!recheck) 1488236769Sobrien entry = Hash_FindEntry(&mtimes, fullName); 1489236769Sobrien else 1490236769Sobrien entry = NULL; 1491236769Sobrien if (entry != NULL) { 1492236769Sobrien if (DEBUG(DIR)) { 1493236769Sobrien fprintf(debug_file, "Using cached time %s for %s\n", 1494236769Sobrien Targ_FmtTime(Hash_GetTimeValue(entry)), fullName); 1495236769Sobrien } 1496236769Sobrien stb.st_mtime = Hash_GetTimeValue(entry); 1497236769Sobrien } else if (stat(fullName, &stb) < 0) { 1498236769Sobrien if (gn->type & OP_MEMBER) { 1499236769Sobrien if (fullName != gn->path) 1500236769Sobrien free(fullName); 1501236769Sobrien return Arch_MemMTime(gn); 1502236769Sobrien } else { 1503236769Sobrien stb.st_mtime = 0; 1504236769Sobrien } 1505236769Sobrien } else { 1506236769Sobrien if (stb.st_mtime == 0) { 1507236769Sobrien /* 1508236769Sobrien * 0 handled specially by the code, if the time is really 0, 1509236769Sobrien * return something else instead 1510236769Sobrien */ 1511236769Sobrien stb.st_mtime = 1; 1512236769Sobrien } 1513236769Sobrien entry = Hash_CreateEntry(&mtimes, fullName, NULL); 1514236769Sobrien Hash_SetTimeValue(entry, stb.st_mtime); 1515236769Sobrien } 1516236769Sobrien 1517236769Sobrien if (fullName && gn->path == NULL) { 1518236769Sobrien gn->path = fullName; 1519236769Sobrien } 1520236769Sobrien 1521236769Sobrien gn->mtime = stb.st_mtime; 1522236769Sobrien return (gn->mtime); 1523236769Sobrien} 1524236769Sobrien 1525236769Sobrien/*- 1526236769Sobrien *----------------------------------------------------------------------- 1527236769Sobrien * Dir_AddDir -- 1528236769Sobrien * Add the given name to the end of the given path. The order of 1529236769Sobrien * the arguments is backwards so ParseDoDependency can do a 1530236769Sobrien * Lst_ForEach of its list of paths... 1531236769Sobrien * 1532236769Sobrien * Input: 1533236769Sobrien * path the path to which the directory should be 1534236769Sobrien * added 1535236769Sobrien * name the name of the directory to add 1536236769Sobrien * 1537236769Sobrien * Results: 1538236769Sobrien * none 1539236769Sobrien * 1540236769Sobrien * Side Effects: 1541236769Sobrien * A structure is added to the list and the directory is 1542236769Sobrien * read and hashed. 1543236769Sobrien *----------------------------------------------------------------------- 1544236769Sobrien */ 1545236769SobrienPath * 1546236769SobrienDir_AddDir(Lst path, const char *name) 1547236769Sobrien{ 1548236769Sobrien LstNode ln = NULL; /* node in case Path structure is found */ 1549236769Sobrien Path *p = NULL; /* pointer to new Path structure */ 1550236769Sobrien DIR *d; /* for reading directory */ 1551236769Sobrien struct dirent *dp; /* entry in directory */ 1552236769Sobrien 1553236769Sobrien if (strcmp(name, ".DOTLAST") == 0) { 1554236769Sobrien ln = Lst_Find(path, name, DirFindName); 1555236769Sobrien if (ln != NULL) 1556236769Sobrien return (Path *)Lst_Datum(ln); 1557236769Sobrien else { 1558236769Sobrien dotLast->refCount += 1; 1559236769Sobrien (void)Lst_AtFront(path, dotLast); 1560236769Sobrien } 1561236769Sobrien } 1562236769Sobrien 1563236769Sobrien if (path) 1564236769Sobrien ln = Lst_Find(openDirectories, name, DirFindName); 1565236769Sobrien if (ln != NULL) { 1566236769Sobrien p = (Path *)Lst_Datum(ln); 1567236769Sobrien if (path && Lst_Member(path, p) == NULL) { 1568236769Sobrien p->refCount += 1; 1569236769Sobrien (void)Lst_AtEnd(path, p); 1570236769Sobrien } 1571236769Sobrien } else { 1572236769Sobrien if (DEBUG(DIR)) { 1573236769Sobrien fprintf(debug_file, "Caching %s ...", name); 1574236769Sobrien } 1575236769Sobrien 1576236769Sobrien if ((d = opendir(name)) != NULL) { 1577236769Sobrien p = bmake_malloc(sizeof(Path)); 1578236769Sobrien p->name = bmake_strdup(name); 1579236769Sobrien p->hits = 0; 1580236769Sobrien p->refCount = 1; 1581236769Sobrien Hash_InitTable(&p->files, -1); 1582236769Sobrien 1583236769Sobrien while ((dp = readdir(d)) != NULL) { 1584236769Sobrien#if defined(sun) && defined(d_ino) /* d_ino is a sunos4 #define for d_fileno */ 1585236769Sobrien /* 1586236769Sobrien * The sun directory library doesn't check for a 0 inode 1587236769Sobrien * (0-inode slots just take up space), so we have to do 1588236769Sobrien * it ourselves. 1589236769Sobrien */ 1590236769Sobrien if (dp->d_fileno == 0) { 1591236769Sobrien continue; 1592236769Sobrien } 1593236769Sobrien#endif /* sun && d_ino */ 1594236769Sobrien (void)Hash_CreateEntry(&p->files, dp->d_name, NULL); 1595236769Sobrien } 1596236769Sobrien (void)closedir(d); 1597236769Sobrien (void)Lst_AtEnd(openDirectories, p); 1598236769Sobrien if (path != NULL) 1599236769Sobrien (void)Lst_AtEnd(path, p); 1600236769Sobrien } 1601236769Sobrien if (DEBUG(DIR)) { 1602236769Sobrien fprintf(debug_file, "done\n"); 1603236769Sobrien } 1604236769Sobrien } 1605236769Sobrien return p; 1606236769Sobrien} 1607236769Sobrien 1608236769Sobrien/*- 1609236769Sobrien *----------------------------------------------------------------------- 1610236769Sobrien * Dir_CopyDir -- 1611236769Sobrien * Callback function for duplicating a search path via Lst_Duplicate. 1612236769Sobrien * Ups the reference count for the directory. 1613236769Sobrien * 1614236769Sobrien * Results: 1615236769Sobrien * Returns the Path it was given. 1616236769Sobrien * 1617236769Sobrien * Side Effects: 1618236769Sobrien * The refCount of the path is incremented. 1619236769Sobrien * 1620236769Sobrien *----------------------------------------------------------------------- 1621236769Sobrien */ 1622236769Sobrienvoid * 1623236769SobrienDir_CopyDir(void *p) 1624236769Sobrien{ 1625236769Sobrien ((Path *)p)->refCount += 1; 1626236769Sobrien 1627236769Sobrien return (p); 1628236769Sobrien} 1629236769Sobrien 1630236769Sobrien/*- 1631236769Sobrien *----------------------------------------------------------------------- 1632236769Sobrien * Dir_MakeFlags -- 1633236769Sobrien * Make a string by taking all the directories in the given search 1634236769Sobrien * path and preceding them by the given flag. Used by the suffix 1635236769Sobrien * module to create variables for compilers based on suffix search 1636236769Sobrien * paths. 1637236769Sobrien * 1638236769Sobrien * Input: 1639236769Sobrien * flag flag which should precede each directory 1640236769Sobrien * path list of directories 1641236769Sobrien * 1642236769Sobrien * Results: 1643236769Sobrien * The string mentioned above. Note that there is no space between 1644236769Sobrien * the given flag and each directory. The empty string is returned if 1645236769Sobrien * Things don't go well. 1646236769Sobrien * 1647236769Sobrien * Side Effects: 1648236769Sobrien * None 1649236769Sobrien *----------------------------------------------------------------------- 1650236769Sobrien */ 1651236769Sobrienchar * 1652236769SobrienDir_MakeFlags(const char *flag, Lst path) 1653236769Sobrien{ 1654236769Sobrien char *str; /* the string which will be returned */ 1655236769Sobrien char *s1, *s2;/* the current directory preceded by 'flag' */ 1656236769Sobrien LstNode ln; /* the node of the current directory */ 1657236769Sobrien Path *p; /* the structure describing the current directory */ 1658236769Sobrien 1659236769Sobrien str = bmake_strdup(""); 1660236769Sobrien 1661236769Sobrien if (Lst_Open(path) == SUCCESS) { 1662236769Sobrien while ((ln = Lst_Next(path)) != NULL) { 1663236769Sobrien p = (Path *)Lst_Datum(ln); 1664236769Sobrien s2 = str_concat(flag, p->name, 0); 1665236769Sobrien str = str_concat(s1 = str, s2, STR_ADDSPACE); 1666236769Sobrien free(s1); 1667236769Sobrien free(s2); 1668236769Sobrien } 1669236769Sobrien Lst_Close(path); 1670236769Sobrien } 1671236769Sobrien 1672236769Sobrien return (str); 1673236769Sobrien} 1674236769Sobrien 1675236769Sobrien/*- 1676236769Sobrien *----------------------------------------------------------------------- 1677236769Sobrien * Dir_Destroy -- 1678236769Sobrien * Nuke a directory descriptor, if possible. Callback procedure 1679236769Sobrien * for the suffixes module when destroying a search path. 1680236769Sobrien * 1681236769Sobrien * Input: 1682236769Sobrien * pp The directory descriptor to nuke 1683236769Sobrien * 1684236769Sobrien * Results: 1685236769Sobrien * None. 1686236769Sobrien * 1687236769Sobrien * Side Effects: 1688236769Sobrien * If no other path references this directory (refCount == 0), 1689236769Sobrien * the Path and all its data are freed. 1690236769Sobrien * 1691236769Sobrien *----------------------------------------------------------------------- 1692236769Sobrien */ 1693236769Sobrienvoid 1694236769SobrienDir_Destroy(void *pp) 1695236769Sobrien{ 1696236769Sobrien Path *p = (Path *)pp; 1697236769Sobrien p->refCount -= 1; 1698236769Sobrien 1699236769Sobrien if (p->refCount == 0) { 1700236769Sobrien LstNode ln; 1701236769Sobrien 1702236769Sobrien ln = Lst_Member(openDirectories, p); 1703236769Sobrien (void)Lst_Remove(openDirectories, ln); 1704236769Sobrien 1705236769Sobrien Hash_DeleteTable(&p->files); 1706236769Sobrien free(p->name); 1707236769Sobrien free(p); 1708236769Sobrien } 1709236769Sobrien} 1710236769Sobrien 1711236769Sobrien/*- 1712236769Sobrien *----------------------------------------------------------------------- 1713236769Sobrien * Dir_ClearPath -- 1714236769Sobrien * Clear out all elements of the given search path. This is different 1715236769Sobrien * from destroying the list, notice. 1716236769Sobrien * 1717236769Sobrien * Input: 1718236769Sobrien * path Path to clear 1719236769Sobrien * 1720236769Sobrien * Results: 1721236769Sobrien * None. 1722236769Sobrien * 1723236769Sobrien * Side Effects: 1724236769Sobrien * The path is set to the empty list. 1725236769Sobrien * 1726236769Sobrien *----------------------------------------------------------------------- 1727236769Sobrien */ 1728236769Sobrienvoid 1729236769SobrienDir_ClearPath(Lst path) 1730236769Sobrien{ 1731236769Sobrien Path *p; 1732236769Sobrien while (!Lst_IsEmpty(path)) { 1733236769Sobrien p = (Path *)Lst_DeQueue(path); 1734236769Sobrien Dir_Destroy(p); 1735236769Sobrien } 1736236769Sobrien} 1737236769Sobrien 1738236769Sobrien 1739236769Sobrien/*- 1740236769Sobrien *----------------------------------------------------------------------- 1741236769Sobrien * Dir_Concat -- 1742236769Sobrien * Concatenate two paths, adding the second to the end of the first. 1743236769Sobrien * Makes sure to avoid duplicates. 1744236769Sobrien * 1745236769Sobrien * Input: 1746236769Sobrien * path1 Dest 1747236769Sobrien * path2 Source 1748236769Sobrien * 1749236769Sobrien * Results: 1750236769Sobrien * None 1751236769Sobrien * 1752236769Sobrien * Side Effects: 1753236769Sobrien * Reference counts for added dirs are upped. 1754236769Sobrien * 1755236769Sobrien *----------------------------------------------------------------------- 1756236769Sobrien */ 1757236769Sobrienvoid 1758236769SobrienDir_Concat(Lst path1, Lst path2) 1759236769Sobrien{ 1760236769Sobrien LstNode ln; 1761236769Sobrien Path *p; 1762236769Sobrien 1763236769Sobrien for (ln = Lst_First(path2); ln != NULL; ln = Lst_Succ(ln)) { 1764236769Sobrien p = (Path *)Lst_Datum(ln); 1765236769Sobrien if (Lst_Member(path1, p) == NULL) { 1766236769Sobrien p->refCount += 1; 1767236769Sobrien (void)Lst_AtEnd(path1, p); 1768236769Sobrien } 1769236769Sobrien } 1770236769Sobrien} 1771236769Sobrien 1772236769Sobrien/********** DEBUG INFO **********/ 1773236769Sobrienvoid 1774236769SobrienDir_PrintDirectories(void) 1775236769Sobrien{ 1776236769Sobrien LstNode ln; 1777236769Sobrien Path *p; 1778236769Sobrien 1779236769Sobrien fprintf(debug_file, "#*** Directory Cache:\n"); 1780236769Sobrien fprintf(debug_file, "# Stats: %d hits %d misses %d near misses %d losers (%d%%)\n", 1781236769Sobrien hits, misses, nearmisses, bigmisses, 1782236769Sobrien (hits+bigmisses+nearmisses ? 1783236769Sobrien hits * 100 / (hits + bigmisses + nearmisses) : 0)); 1784236769Sobrien fprintf(debug_file, "# %-20s referenced\thits\n", "directory"); 1785236769Sobrien if (Lst_Open(openDirectories) == SUCCESS) { 1786236769Sobrien while ((ln = Lst_Next(openDirectories)) != NULL) { 1787236769Sobrien p = (Path *)Lst_Datum(ln); 1788236769Sobrien fprintf(debug_file, "# %-20s %10d\t%4d\n", p->name, p->refCount, p->hits); 1789236769Sobrien } 1790236769Sobrien Lst_Close(openDirectories); 1791236769Sobrien } 1792236769Sobrien} 1793236769Sobrien 1794236769Sobrienstatic int 1795236769SobrienDirPrintDir(void *p, void *dummy) 1796236769Sobrien{ 1797236769Sobrien fprintf(debug_file, "%s ", ((Path *)p)->name); 1798236769Sobrien return (dummy ? 0 : 0); 1799236769Sobrien} 1800236769Sobrien 1801236769Sobrienvoid 1802236769SobrienDir_PrintPath(Lst path) 1803236769Sobrien{ 1804236769Sobrien Lst_ForEach(path, DirPrintDir, NULL); 1805236769Sobrien} 1806