1 /* $NetBSD: dir.c,v 1.294 2024/05/31 05:50:11 rillig Exp $ */ 2 3 /* 4 * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * Adam de Boor. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. Neither the name of the University nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 /* 36 * Copyright (c) 1988, 1989 by Adam de Boor 37 * Copyright (c) 1989 by Berkeley Softworks 38 * All rights reserved. 39 * 40 * This code is derived from software contributed to Berkeley by 41 * Adam de Boor. 42 * 43 * Redistribution and use in source and binary forms, with or without 44 * modification, are permitted provided that the following conditions 45 * are met: 46 * 1. Redistributions of source code must retain the above copyright 47 * notice, this list of conditions and the following disclaimer. 48 * 2. Redistributions in binary form must reproduce the above copyright 49 * notice, this list of conditions and the following disclaimer in the 50 * documentation and/or other materials provided with the distribution. 51 * 3. All advertising materials mentioning features or use of this software 52 * must display the following acknowledgement: 53 * This product includes software developed by the University of 54 * California, Berkeley and its contributors. 55 * 4. Neither the name of the University nor the names of its contributors 56 * may be used to endorse or promote products derived from this software 57 * without specific prior written permission. 58 * 59 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 60 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 61 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 62 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 63 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 64 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 65 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 66 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 67 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 68 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 69 * SUCH DAMAGE. 70 */ 71 72 /* 73 * Directory searching using wildcards and/or normal names. 74 * Used both for source wildcarding in the makefile and for finding 75 * implicit sources. 76 * 77 * The interface for this module is: 78 * Dir_Init Initialize the module. 79 * 80 * Dir_InitCur Set the cur CachedDir. 81 * 82 * Dir_InitDot Set the dot CachedDir. 83 * 84 * Dir_End Clean up the module. 85 * 86 * Dir_SetPATH Set ${.PATH} to reflect the state of dirSearchPath. 87 * 88 * Dir_HasWildcards 89 * Returns true if the name given it needs to 90 * be wildcard-expanded. 91 * 92 * SearchPath_Expand 93 * Expand a filename pattern to find all matching files 94 * from the search path. 95 * 96 * Dir_FindFile Searches for a file on a given search path. 97 * If it exists, returns the entire path, otherwise NULL. 98 * 99 * Dir_FindHereOrAbove 100 * Search for a path in the current directory and then 101 * all the directories above it in turn, until the path 102 * is found or the root directory ("/") is reached. 103 * 104 * Dir_UpdateMTime 105 * Update the modification time and path of a node with 106 * data from the file corresponding to the node. 107 * 108 * SearchPath_Add Add a directory to a search path. 109 * 110 * SearchPath_ToFlags 111 * Given a search path and a command flag, create 112 * a string with each of the directories in the path 113 * preceded by the command flag and all of them 114 * separated by a space. 115 * 116 * SearchPath_Clear 117 * Resets a search path to the empty list. 118 * 119 * For debugging: 120 * Dir_PrintDirectories 121 * Print stats about the directory cache. 122 */ 123 124 #include <sys/types.h> 125 #include <sys/stat.h> 126 127 #include <dirent.h> 128 #include <errno.h> 129 130 #include "make.h" 131 #include "dir.h" 132 #include "job.h" 133 134 /* "@(#)dir.c 8.2 (Berkeley) 1/2/94" */ 135 MAKE_RCSID("$NetBSD: dir.c,v 1.294 2024/05/31 05:50:11 rillig Exp $"); 136 137 /* 138 * A search path is a list of CachedDir structures. A CachedDir has in it the 139 * name of the directory and the names of all the files in the directory. 140 * This is used to cut down on the number of system calls necessary to find 141 * implicit dependents and their like. Since these searches are made before 142 * any actions are taken, we need not worry about the directory changing due 143 * to creation commands. If this hampers the style of some makefiles, they 144 * must be changed. 145 * 146 * All previously-read directories are kept in openDirs, which is checked 147 * first before a directory is opened. 148 * 149 * This cache is used by the multi-level transformation code in suff.c, which 150 * tends to search for far more files than in regular explicit targets. After 151 * a directory has been cached, any later changes to that directory are not 152 * reflected in the cache. To keep the cache up to date, there are several 153 * ideas: 154 * 155 * 1) just use stat to test for a file's existence. As mentioned above, 156 * this is very inefficient due to the number of checks performed by 157 * the multi-level transformation code. 158 * 159 * 2) use readdir() to search the directories, keeping them open between 160 * checks. Around 1993 or earlier, this didn't slow down the process too 161 * much, but it consumed one file descriptor per open directory, which 162 * was critical on the then-current operating systems, as many limited 163 * the number of open file descriptors to 20 or 32. 164 * 165 * 3) record the mtime of the directory in the CachedDir structure and 166 * verify the directory hasn't changed since the contents were cached. 167 * This will catch the creation or deletion of files, but not the 168 * updating of files. However, since it is the creation and deletion 169 * that is the problem, this could be a good thing to do. Unfortunately, 170 * if the directory (say ".") were fairly large and changed fairly 171 * frequently, the constant reloading could seriously degrade 172 * performance. It might be good in such cases to keep track of the 173 * number of reloadings and if the number goes over a (small) limit, 174 * resort to using stat in its place. 175 * 176 * An additional thing to consider is that make is used primarily to create 177 * C programs and until recently (as of 1993 or earlier), pcc-based compilers 178 * didn't have an option to specify where the resulting object file should be 179 * placed. This forced all objects to be created in the current directory. 180 * This isn't meant as a full excuse, just an explanation of some of the 181 * reasons for the caching used here. 182 * 183 * One more note: the location of a target's file is only performed on the 184 * downward traversal of the graph and then only for terminal nodes in the 185 * graph. This could be construed as wrong in some cases, but prevents 186 * inadvertent modification of files when the "installed" directory for a 187 * file is provided in the search path. 188 * 189 * Another data structure maintained by this module is an mtime cache used 190 * when the searching of cached directories fails to find a file. In the past, 191 * Dir_FindFile would simply perform an access() call in such a case to 192 * determine if the file could be found using just the name given. When this 193 * hit, however, all that was gained was the knowledge that the file existed. 194 * Given that an access() is essentially a stat() without the copyout() call, 195 * and that the same filesystem overhead would have to be incurred in 196 * Dir_MTime, it made sense to replace the access() with a stat() and record 197 * the mtime in a cache for when Dir_UpdateMTime was actually called. 198 */ 199 200 201 /* A cache for the filenames in a directory. */ 202 struct CachedDir { 203 /* 204 * Name of the directory, either absolute or relative to the current 205 * directory. The name is not normalized in any way, that is, "." 206 * and "./." are different. 207 * 208 * Not sure what happens when .CURDIR is assigned a new value; see 209 * Parse_Var. 210 */ 211 char *name; 212 213 /* 214 * The number of SearchPaths that refer to this directory. 215 * Plus the number of global variables that refer to this directory. 216 * References from openDirs do not count though. 217 */ 218 int refCount; 219 220 /* The number of times a file in this directory has been found. */ 221 int hits; 222 223 /* The names of the directory entries. */ 224 HashSet files; 225 }; 226 227 typedef List CachedDirList; 228 typedef ListNode CachedDirListNode; 229 230 /* A list of cached directories, with fast lookup by directory name. */ 231 typedef struct OpenDirs { 232 CachedDirList list; 233 HashTable /* of CachedDirListNode */ table; 234 } OpenDirs; 235 236 237 SearchPath dirSearchPath = { LST_INIT }; /* main search path */ 238 239 static OpenDirs openDirs; /* all cached directories */ 240 241 /* 242 * Variables for gathering statistics on the efficiency of the caching 243 * mechanism. 244 */ 245 static int hits; /* Found in directory cache */ 246 static int misses; /* Sad, but not evil misses */ 247 static int nearmisses; /* Found under search path */ 248 static int bigmisses; /* Sought by itself */ 249 250 /* The cached contents of ".", the relative current directory. */ 251 static CachedDir *dot = NULL; 252 /* The cached contents of the absolute current directory. */ 253 static CachedDir *cur = NULL; 254 /* A fake path entry indicating we need to look for '.' last. */ 255 static CachedDir *dotLast = NULL; 256 257 /* 258 * Results of doing a last-resort stat in Dir_FindFile -- if we have to go to 259 * the system to find the file, we might as well have its mtime on record. 260 * 261 * XXX: If this is done way early, there's a chance other rules will have 262 * already updated the file, in which case we'll update it again. Generally, 263 * there won't be two rules to update a single file, so this should be ok. 264 */ 265 static HashTable mtimes; 266 267 static HashTable lmtimes; /* same as mtimes but for lstat */ 268 269 270 static void OpenDirs_Remove(OpenDirs *, const char *); 271 272 273 static CachedDir * 274 CachedDir_New(const char *name) 275 { 276 CachedDir *dir = bmake_malloc(sizeof *dir); 277 278 dir->name = bmake_strdup(name); 279 dir->refCount = 0; 280 dir->hits = 0; 281 HashSet_Init(&dir->files); 282 283 #ifdef DEBUG_REFCNT 284 DEBUG2(DIR, "CachedDir %p new for \"%s\"\n", dir, dir->name); 285 #endif 286 287 return dir; 288 } 289 290 static CachedDir * 291 CachedDir_Ref(CachedDir *dir) 292 { 293 dir->refCount++; 294 295 #ifdef DEBUG_REFCNT 296 DEBUG3(DIR, "CachedDir %p ++ %d for \"%s\"\n", 297 dir, dir->refCount, dir->name); 298 #endif 299 300 return dir; 301 } 302 303 static void 304 CachedDir_Unref(CachedDir *dir) 305 { 306 dir->refCount--; 307 308 #ifdef DEBUG_REFCNT 309 DEBUG3(DIR, "CachedDir %p -- %d for \"%s\"\n", 310 dir, dir->refCount, dir->name); 311 #endif 312 313 if (dir->refCount > 0) 314 return; 315 316 #ifdef DEBUG_REFCNT 317 DEBUG2(DIR, "CachedDir %p free for \"%s\"\n", dir, dir->name); 318 #endif 319 320 OpenDirs_Remove(&openDirs, dir->name); 321 322 free(dir->name); 323 HashSet_Done(&dir->files); 324 free(dir); 325 } 326 327 /* Update the value of 'var', updating the reference counts. */ 328 static void 329 CachedDir_Assign(CachedDir **var, CachedDir *dir) 330 { 331 CachedDir *prev; 332 333 prev = *var; 334 *var = dir; 335 if (dir != NULL) 336 CachedDir_Ref(dir); 337 if (prev != NULL) 338 CachedDir_Unref(prev); 339 } 340 341 static void 342 OpenDirs_Init(OpenDirs *odirs) 343 { 344 Lst_Init(&odirs->list); 345 HashTable_Init(&odirs->table); 346 } 347 348 #ifdef CLEANUP 349 static void 350 OpenDirs_Done(OpenDirs *odirs) 351 { 352 CachedDirListNode *ln = odirs->list.first; 353 DEBUG1(DIR, "OpenDirs_Done: %u entries to remove\n", 354 odirs->table.numEntries); 355 while (ln != NULL) { 356 CachedDirListNode *next = ln->next; 357 CachedDir *dir = ln->datum; 358 DEBUG2(DIR, "OpenDirs_Done: refCount %d for \"%s\"\n", 359 dir->refCount, dir->name); 360 CachedDir_Unref(dir); /* removes the dir from odirs->list */ 361 ln = next; 362 } 363 Lst_Done(&odirs->list); 364 HashTable_Done(&odirs->table); 365 } 366 #endif 367 368 static CachedDir * 369 OpenDirs_Find(OpenDirs *odirs, const char *name) 370 { 371 CachedDirListNode *ln = HashTable_FindValue(&odirs->table, name); 372 return ln != NULL ? ln->datum : NULL; 373 } 374 375 static void 376 OpenDirs_Add(OpenDirs *odirs, CachedDir *cdir) 377 { 378 if (HashTable_FindEntry(&odirs->table, cdir->name) != NULL) 379 return; 380 Lst_Append(&odirs->list, cdir); 381 HashTable_Set(&odirs->table, cdir->name, odirs->list.last); 382 } 383 384 static void 385 OpenDirs_Remove(OpenDirs *odirs, const char *name) 386 { 387 HashEntry *he = HashTable_FindEntry(&odirs->table, name); 388 CachedDirListNode *ln; 389 if (he == NULL) 390 return; 391 ln = HashEntry_Get(he); 392 HashTable_DeleteEntry(&odirs->table, he); 393 Lst_Remove(&odirs->list, ln); 394 } 395 396 /* 397 * Returns 0 and the result of stat(2) or lstat(2) in *out_cst, 398 * or -1 on error. 399 */ 400 static int 401 cached_stats(const char *pathname, struct cached_stat *out_cst, 402 bool useLstat, bool forceRefresh) 403 { 404 HashTable *tbl = useLstat ? &lmtimes : &mtimes; 405 struct stat sys_st; 406 struct cached_stat *cst; 407 int rc; 408 409 if (pathname == NULL || pathname[0] == '\0') 410 return -1; /* This can happen in meta mode. */ 411 412 cst = HashTable_FindValue(tbl, pathname); 413 if (cst != NULL && !forceRefresh) { 414 *out_cst = *cst; 415 DEBUG2(DIR, "Using cached time %s for %s\n", 416 Targ_FmtTime(cst->cst_mtime), pathname); 417 return 0; 418 } 419 420 rc = (useLstat ? lstat : stat)(pathname, &sys_st); 421 if (rc == -1) 422 return -1; /* don't cache negative lookups */ 423 424 if (sys_st.st_mtime == 0) 425 sys_st.st_mtime = 1; /* avoid confusion with missing file */ 426 427 if (cst == NULL) { 428 cst = bmake_malloc(sizeof *cst); 429 HashTable_Set(tbl, pathname, cst); 430 } 431 432 cst->cst_mtime = sys_st.st_mtime; 433 cst->cst_mode = sys_st.st_mode; 434 435 *out_cst = *cst; 436 DEBUG2(DIR, " Caching %s for %s\n", 437 Targ_FmtTime(sys_st.st_mtime), pathname); 438 439 return 0; 440 } 441 442 int 443 cached_stat(const char *pathname, struct cached_stat *cst) 444 { 445 return cached_stats(pathname, cst, false, false); 446 } 447 448 int 449 cached_lstat(const char *pathname, struct cached_stat *cst) 450 { 451 return cached_stats(pathname, cst, true, false); 452 } 453 454 /* Initialize the directories module. */ 455 void 456 Dir_Init(void) 457 { 458 OpenDirs_Init(&openDirs); 459 HashTable_Init(&mtimes); 460 HashTable_Init(&lmtimes); 461 CachedDir_Assign(&dotLast, CachedDir_New(".DOTLAST")); 462 } 463 464 /* Called by Dir_InitDir and whenever .CURDIR is assigned to. */ 465 void 466 Dir_InitCur(const char *newCurdir) 467 { 468 CachedDir *dir; 469 470 if (newCurdir == NULL) 471 return; 472 473 /* 474 * The build directory is not the same as the source directory. 475 * Keep this one around too. 476 */ 477 dir = SearchPath_Add(NULL, newCurdir); 478 if (dir == NULL) 479 return; 480 481 CachedDir_Assign(&cur, dir); 482 } 483 484 /* 485 * (Re)initialize "dot" (the current/object directory). 486 * Some directories may be cached. 487 */ 488 void 489 Dir_InitDot(void) 490 { 491 CachedDir *dir; 492 493 dir = SearchPath_Add(NULL, "."); 494 if (dir == NULL) { 495 Error("Cannot open `.' (%s)", strerror(errno)); 496 exit(2); /* Not 1 so -q can distinguish error */ 497 } 498 499 CachedDir_Assign(&dot, dir); 500 501 Dir_SetPATH(); /* initialize */ 502 } 503 504 #ifdef CLEANUP 505 static void 506 FreeCachedTable(HashTable *tbl) 507 { 508 HashIter hi; 509 HashIter_Init(&hi, tbl); 510 while (HashIter_Next(&hi)) 511 free(hi.entry->value); 512 HashTable_Done(tbl); 513 } 514 #endif 515 516 /* Clean up the directories module. */ 517 void 518 Dir_End(void) 519 { 520 #ifdef CLEANUP 521 CachedDir_Assign(&cur, NULL); 522 CachedDir_Assign(&dot, NULL); 523 CachedDir_Assign(&dotLast, NULL); 524 SearchPath_Clear(&dirSearchPath); 525 OpenDirs_Done(&openDirs); 526 FreeCachedTable(&mtimes); 527 FreeCachedTable(&lmtimes); 528 #endif 529 } 530 531 /* 532 * We want ${.PATH} to indicate the order in which we will actually 533 * search, so we rebuild it after any .PATH: target. 534 * This is the simplest way to deal with the effect of .DOTLAST. 535 */ 536 void 537 Dir_SetPATH(void) 538 { 539 CachedDirListNode *ln; 540 bool seenDotLast = false; /* true if we should search '.' last */ 541 542 Global_Delete(".PATH"); 543 544 if ((ln = dirSearchPath.dirs.first) != NULL) { 545 CachedDir *dir = ln->datum; 546 if (dir == dotLast) { 547 seenDotLast = true; 548 Global_Append(".PATH", dotLast->name); 549 } 550 } 551 552 if (!seenDotLast) { 553 if (dot != NULL) 554 Global_Append(".PATH", dot->name); 555 if (cur != NULL) 556 Global_Append(".PATH", cur->name); 557 } 558 559 for (ln = dirSearchPath.dirs.first; ln != NULL; ln = ln->next) { 560 CachedDir *dir = ln->datum; 561 if (dir == dotLast) 562 continue; 563 if (dir == dot && seenDotLast) 564 continue; 565 Global_Append(".PATH", dir->name); 566 } 567 568 if (seenDotLast) { 569 if (dot != NULL) 570 Global_Append(".PATH", dot->name); 571 if (cur != NULL) 572 Global_Append(".PATH", cur->name); 573 } 574 } 575 576 577 void 578 Dir_SetSYSPATH(void) 579 { 580 CachedDirListNode *ln; 581 SearchPath *path = Lst_IsEmpty(&sysIncPath->dirs) 582 ? defSysIncPath : sysIncPath; 583 584 Var_ReadOnly(".SYSPATH", false); 585 Global_Delete(".SYSPATH"); 586 for (ln = path->dirs.first; ln != NULL; ln = ln->next) { 587 CachedDir *dir = ln->datum; 588 Global_Append(".SYSPATH", dir->name); 589 } 590 Var_ReadOnly(".SYSPATH", true); 591 } 592 593 /* 594 * See if the given name has any wildcard characters in it and all braces and 595 * brackets are properly balanced. 596 * 597 * XXX: This code is not 100% correct ([^]] fails etc.). I really don't think 598 * that make(1) should be expanding patterns, because then you have to set a 599 * mechanism for escaping the expansion! 600 */ 601 bool 602 Dir_HasWildcards(const char *name) 603 { 604 const char *p; 605 bool wild = false; 606 int braces = 0, brackets = 0; 607 608 for (p = name; *p != '\0'; p++) { 609 switch (*p) { 610 case '{': 611 braces++; 612 wild = true; 613 break; 614 case '}': 615 braces--; 616 break; 617 case '[': 618 brackets++; 619 wild = true; 620 break; 621 case ']': 622 brackets--; 623 break; 624 case '?': 625 case '*': 626 wild = true; 627 break; 628 default: 629 break; 630 } 631 } 632 return wild && brackets == 0 && braces == 0; 633 } 634 635 /* 636 * See if any files as seen from 'dir' match 'pattern', and add their names 637 * to 'expansions' if they do. 638 * 639 * Wildcards are only expanded in the final path component, but not in 640 * directories like src/lib*c/file*.c. To expand these wildcards, 641 * delegate the work to the shell, using the '!=' variable assignment 642 * operator, the ':sh' variable modifier or the ':!...!' variable modifier, 643 * such as in ${:!echo src/lib*c/file*.c!}. 644 */ 645 static void 646 DirMatchFiles(const char *pattern, CachedDir *dir, StringList *expansions) 647 { 648 const char *dirName = dir->name; 649 bool isDot = dirName[0] == '.' && dirName[1] == '\0'; 650 HashIter hi; 651 652 /* 653 * XXX: Iterating over all hash entries is inefficient. If the 654 * pattern is a plain string without any wildcards, a direct lookup 655 * is faster. 656 */ 657 658 HashIter_InitSet(&hi, &dir->files); 659 while (HashIter_Next(&hi)) { 660 const char *base = hi.entry->key; 661 StrMatchResult res = Str_Match(base, pattern); 662 /* TODO: handle errors from res.error */ 663 664 if (!res.matched) 665 continue; 666 667 /* 668 * Follow the UNIX convention that dot files are only found 669 * if the pattern begins with a dot. The pattern '.*' does 670 * not match '.' or '..' since these are not included in the 671 * directory cache. 672 * 673 * This means that the pattern '[a-z.]*' does not find 674 * '.file', which is consistent with NetBSD sh, NetBSD ksh, 675 * bash, dash, csh and probably many other shells as well. 676 */ 677 if (base[0] == '.' && pattern[0] != '.') 678 continue; 679 680 { 681 char *fullName = isDot 682 ? bmake_strdup(base) 683 : str_concat3(dirName, "/", base); 684 Lst_Append(expansions, fullName); 685 } 686 } 687 } 688 689 /* Find the next closing brace in 'p', taking nested braces into account. */ 690 static const char * 691 closing_brace(const char *p) 692 { 693 int depth = 0; 694 while (*p != '\0') { 695 if (*p == '}' && depth == 0) 696 break; 697 if (*p == '{') 698 depth++; 699 if (*p == '}') 700 depth--; 701 p++; 702 } 703 return p; 704 } 705 706 /* 707 * Find the next closing brace or comma in the string, taking nested braces 708 * into account. 709 */ 710 static const char * 711 separator_comma(const char *p) 712 { 713 int depth = 0; 714 while (*p != '\0') { 715 if ((*p == '}' || *p == ',') && depth == 0) 716 break; 717 if (*p == '{') 718 depth++; 719 if (*p == '}') 720 depth--; 721 p++; 722 } 723 return p; 724 } 725 726 static bool 727 contains_wildcard(const char *p) 728 { 729 for (; *p != '\0'; p++) { 730 switch (*p) { 731 case '*': 732 case '?': 733 case '{': 734 case '[': 735 return true; 736 } 737 } 738 return false; 739 } 740 741 static char * 742 concat3(const char *a, size_t a_len, const char *b, size_t b_len, 743 const char *c, size_t c_len) 744 { 745 size_t s_len = a_len + b_len + c_len; 746 char *s = bmake_malloc(s_len + 1); 747 memcpy(s, a, a_len); 748 memcpy(s + a_len, b, b_len); 749 memcpy(s + a_len + b_len, c, c_len); 750 s[s_len] = '\0'; 751 return s; 752 } 753 754 /* 755 * Expand curly braces like the C shell. Brace expansion by itself is purely 756 * textual, the expansions are not looked up in the file system. But if an 757 * expanded word contains wildcard characters, it is expanded further, 758 * matching only the actually existing files. 759 * 760 * Example: "{a{b,c}}" expands to "ab" and "ac". 761 * Example: "{a}" expands to "a". 762 * Example: "{a,*.c}" expands to "a" and all "*.c" files that exist. 763 * 764 * Input: 765 * word Entire word to expand 766 * brace First curly brace in it 767 * path Search path to use 768 * expansions Place to store the expansions 769 */ 770 static void 771 DirExpandCurly(const char *word, const char *brace, SearchPath *path, 772 StringList *expansions) 773 { 774 const char *prefix, *middle, *piece, *middle_end, *suffix; 775 size_t prefix_len, suffix_len; 776 777 /* Split the word into prefix, '{', middle, '}' and suffix. */ 778 779 middle = brace + 1; 780 middle_end = closing_brace(middle); 781 if (*middle_end == '\0') { 782 Error("Unterminated {} clause \"%s\"", middle); 783 return; 784 } 785 786 prefix = word; 787 prefix_len = (size_t)(brace - prefix); 788 suffix = middle_end + 1; 789 suffix_len = strlen(suffix); 790 791 /* Split the middle into pieces, separated by commas. */ 792 793 piece = middle; 794 while (piece < middle_end + 1) { 795 const char *piece_end = separator_comma(piece); 796 size_t piece_len = (size_t)(piece_end - piece); 797 798 char *file = concat3(prefix, prefix_len, piece, piece_len, 799 suffix, suffix_len); 800 801 if (contains_wildcard(file)) { 802 SearchPath_Expand(path, file, expansions); 803 free(file); 804 } else { 805 Lst_Append(expansions, file); 806 } 807 808 /* skip over the comma or closing brace */ 809 piece = piece_end + 1; 810 } 811 } 812 813 814 /* Expand 'pattern' in each of the directories from 'path'. */ 815 static void 816 DirExpandPath(const char *pattern, SearchPath *path, StringList *expansions) 817 { 818 CachedDirListNode *ln; 819 for (ln = path->dirs.first; ln != NULL; ln = ln->next) { 820 CachedDir *dir = ln->datum; 821 DirMatchFiles(pattern, dir, expansions); 822 } 823 } 824 825 static void 826 PrintExpansions(StringList *expansions) 827 { 828 const char *sep = ""; 829 StringListNode *ln; 830 for (ln = expansions->first; ln != NULL; ln = ln->next) { 831 const char *word = ln->datum; 832 debug_printf("%s%s", sep, word); 833 sep = " "; 834 } 835 debug_printf("\n"); 836 } 837 838 /* 839 * The wildcard isn't in the first component. 840 * Find all the components up to the one with the wildcard. 841 */ 842 static void 843 SearchPath_ExpandMiddle(SearchPath *path, const char *pattern, 844 const char *wildcardComponent, StringList *expansions) 845 { 846 char *prefix, *dirpath, *end; 847 SearchPath *partPath; 848 849 prefix = bmake_strsedup(pattern, wildcardComponent + 1); 850 /* 851 * XXX: Only the first match of the prefix in the path is 852 * taken, any others are ignored. The expectation may be 853 * that the pattern is expanded in the whole path. 854 */ 855 dirpath = Dir_FindFile(prefix, path); 856 free(prefix); 857 858 /* 859 * dirpath is null if can't find the leading component 860 * 861 * XXX: Dir_FindFile won't find internal components. i.e. if the 862 * path contains ../Etc/Object and we're looking for Etc, it won't 863 * be found. Ah well. Probably not important. 864 * 865 * TODO: Check whether the above comment is still true. 866 */ 867 if (dirpath == NULL) 868 return; 869 870 end = &dirpath[strlen(dirpath) - 1]; 871 /* XXX: What about multiple trailing slashes? */ 872 if (*end == '/') 873 *end = '\0'; 874 875 partPath = SearchPath_New(); 876 (void)SearchPath_Add(partPath, dirpath); 877 DirExpandPath(wildcardComponent + 1, partPath, expansions); 878 SearchPath_Free(partPath); 879 free(dirpath); 880 } 881 882 /* 883 * Expand the given pattern into a list of existing filenames by globbing it, 884 * looking in each directory from the search path. 885 * 886 * Input: 887 * path the directories in which to find the files 888 * pattern the pattern to expand 889 * expansions the list on which to place the results 890 */ 891 void 892 SearchPath_Expand(SearchPath *path, const char *pattern, StringList *expansions) 893 { 894 const char *brace, *slash, *wildcard, *wildcardComponent; 895 896 assert(path != NULL); 897 assert(expansions != NULL); 898 899 DEBUG1(DIR, "Expanding \"%s\"... ", pattern); 900 901 brace = strchr(pattern, '{'); 902 if (brace != NULL) { 903 DirExpandCurly(pattern, brace, path, expansions); 904 goto done; 905 } 906 907 slash = strchr(pattern, '/'); 908 if (slash == NULL) { 909 DirMatchFiles(pattern, dot, expansions); 910 DirExpandPath(pattern, path, expansions); 911 goto done; 912 } 913 914 /* At this point, the pattern has a directory component. */ 915 916 /* Find the first wildcard in the pattern. */ 917 for (wildcard = pattern; *wildcard != '\0'; wildcard++) 918 if (*wildcard == '?' || *wildcard == '[' || *wildcard == '*') 919 break; 920 921 if (*wildcard == '\0') { 922 /* 923 * No directory component and no wildcard at all -- this 924 * should never happen as in such a simple case there is no 925 * need to expand anything. 926 */ 927 DirExpandPath(pattern, path, expansions); 928 goto done; 929 } 930 931 /* Back up to the start of the component containing the wildcard. */ 932 /* XXX: This handles '///' and '/' differently. */ 933 wildcardComponent = wildcard; 934 while (wildcardComponent > pattern && *wildcardComponent != '/') 935 wildcardComponent--; 936 937 if (wildcardComponent == pattern) { 938 /* The first component contains the wildcard. */ 939 /* Start the search from the local directory */ 940 DirExpandPath(pattern, path, expansions); 941 } else { 942 SearchPath_ExpandMiddle(path, pattern, wildcardComponent, 943 expansions); 944 } 945 946 done: 947 if (DEBUG(DIR)) 948 PrintExpansions(expansions); 949 } 950 951 /* 952 * Find if 'base' exists in 'dir'. 953 * Return the freshly allocated path to the file, or NULL. 954 */ 955 static char * 956 DirLookup(CachedDir *dir, const char *base) 957 { 958 char *file; 959 960 DEBUG1(DIR, " %s ...\n", dir->name); 961 962 if (!HashSet_Contains(&dir->files, base)) 963 return NULL; 964 965 file = str_concat3(dir->name, "/", base); 966 DEBUG1(DIR, " returning %s\n", file); 967 dir->hits++; 968 hits++; 969 return file; 970 } 971 972 973 /* 974 * Find if 'name' exists in 'dir'. 975 * Return the freshly allocated path to the file, or NULL. 976 */ 977 static char * 978 DirLookupSubdir(CachedDir *dir, const char *name) 979 { 980 struct cached_stat cst; 981 char *file = dir == dot 982 ? bmake_strdup(name) 983 : str_concat3(dir->name, "/", name); 984 985 DEBUG1(DIR, "checking %s ...\n", file); 986 987 if (cached_stat(file, &cst) == 0) { 988 nearmisses++; 989 return file; 990 } 991 free(file); 992 return NULL; 993 } 994 995 /* 996 * Find if 'name' (which has basename 'base') exists in 'dir'. 997 * Return the freshly allocated path to the file, an empty string, or NULL. 998 * Returning an empty string means that the search should be terminated. 999 */ 1000 static char * 1001 DirLookupAbs(CachedDir *dir, const char *name, const char *base) 1002 { 1003 const char *dnp; /* pointer into dir->name */ 1004 const char *np; /* pointer into name */ 1005 1006 DEBUG1(DIR, " %s ...\n", dir->name); 1007 1008 /* 1009 * If the file has a leading path component and that component 1010 * exactly matches the entire name of the current search 1011 * directory, we can attempt another cache lookup. And if we don't 1012 * have a hit, we can safely assume the file does not exist at all. 1013 */ 1014 for (dnp = dir->name, np = name; 1015 *dnp != '\0' && *dnp == *np; dnp++, np++) 1016 continue; 1017 if (*dnp != '\0' || np != base - 1) 1018 return NULL; 1019 1020 if (!HashSet_Contains(&dir->files, base)) { 1021 DEBUG0(DIR, " must be here but isn't -- returning\n"); 1022 return bmake_strdup(""); /* to terminate the search */ 1023 } 1024 1025 dir->hits++; 1026 hits++; 1027 DEBUG1(DIR, " returning %s\n", name); 1028 return bmake_strdup(name); 1029 } 1030 1031 /* 1032 * Find the given file in "." or curdir. 1033 * Return the freshly allocated path to the file, or NULL. 1034 */ 1035 static char * 1036 DirFindDot(const char *name, const char *base) 1037 { 1038 1039 if (HashSet_Contains(&dot->files, base)) { 1040 DEBUG0(DIR, " in '.'\n"); 1041 hits++; 1042 dot->hits++; 1043 return bmake_strdup(name); 1044 } 1045 1046 if (cur != NULL && HashSet_Contains(&cur->files, base)) { 1047 DEBUG1(DIR, " in ${.CURDIR} = %s\n", cur->name); 1048 hits++; 1049 cur->hits++; 1050 return str_concat3(cur->name, "/", base); 1051 } 1052 1053 return NULL; 1054 } 1055 1056 static bool 1057 FindFileRelative(SearchPath *path, bool seenDotLast, 1058 const char *name, char **out_file) 1059 { 1060 CachedDirListNode *ln; 1061 bool checkedDot = false; 1062 char *file; 1063 1064 DEBUG0(DIR, " Trying subdirectories...\n"); 1065 1066 if (!seenDotLast) { 1067 if (dot != NULL) { 1068 checkedDot = true; 1069 if ((file = DirLookupSubdir(dot, name)) != NULL) 1070 goto done; 1071 } 1072 if (cur != NULL && 1073 (file = DirLookupSubdir(cur, name)) != NULL) 1074 goto done; 1075 } 1076 1077 for (ln = path->dirs.first; ln != NULL; ln = ln->next) { 1078 CachedDir *dir = ln->datum; 1079 if (dir == dotLast) 1080 continue; 1081 if (dir == dot) { 1082 if (checkedDot) 1083 continue; 1084 checkedDot = true; 1085 } 1086 if ((file = DirLookupSubdir(dir, name)) != NULL) 1087 goto done; 1088 } 1089 1090 if (seenDotLast) { 1091 if (dot != NULL && !checkedDot) { 1092 checkedDot = true; 1093 if ((file = DirLookupSubdir(dot, name)) != NULL) 1094 goto done; 1095 } 1096 if (cur != NULL && 1097 (file = DirLookupSubdir(cur, name)) != NULL) 1098 goto done; 1099 } 1100 1101 if (checkedDot) { 1102 /* 1103 * Already checked by the given name, since . was in 1104 * the path, so no point in proceeding. 1105 */ 1106 DEBUG0(DIR, " Checked . already, returning NULL\n"); 1107 file = NULL; 1108 goto done; 1109 } 1110 1111 return false; 1112 1113 done: 1114 *out_file = file; 1115 return true; 1116 } 1117 1118 static bool 1119 FindFileAbsolute(SearchPath *path, bool seenDotLast, 1120 const char *name, const char *base, char **out_file) 1121 { 1122 char *file; 1123 CachedDirListNode *ln; 1124 1125 DEBUG0(DIR, " Trying exact path matches...\n"); 1126 1127 if (!seenDotLast && cur != NULL && 1128 ((file = DirLookupAbs(cur, name, base)) != NULL)) 1129 goto found; 1130 1131 for (ln = path->dirs.first; ln != NULL; ln = ln->next) { 1132 CachedDir *dir = ln->datum; 1133 if (dir == dotLast) 1134 continue; 1135 if ((file = DirLookupAbs(dir, name, base)) != NULL) 1136 goto found; 1137 } 1138 1139 if (seenDotLast && cur != NULL && 1140 ((file = DirLookupAbs(cur, name, base)) != NULL)) 1141 goto found; 1142 1143 return false; 1144 1145 found: 1146 if (file[0] == '\0') { 1147 free(file); 1148 file = NULL; 1149 } 1150 *out_file = file; 1151 return true; 1152 } 1153 1154 /* 1155 * Find the file with the given name along the given search path. 1156 * 1157 * Input: 1158 * name the file to find 1159 * path the directories to search, or NULL 1160 * isinclude if true, do not search .CURDIR at all 1161 * 1162 * Results: 1163 * The freshly allocated path to the file, or NULL. 1164 */ 1165 static char * 1166 FindFile(const char *name, SearchPath *path, bool isinclude) 1167 { 1168 char *file; /* the current filename to check */ 1169 bool seenDotLast = isinclude; /* true if we should search dot last */ 1170 struct cached_stat cst; 1171 const char *trailing_dot = "."; 1172 const char *base = str_basename(name); 1173 1174 DEBUG1(DIR, "Searching for %s ...", name); 1175 1176 if (path == NULL) { 1177 DEBUG0(DIR, "couldn't open path, file not found\n"); 1178 misses++; 1179 return NULL; 1180 } 1181 1182 if (!seenDotLast && path->dirs.first != NULL) { 1183 CachedDir *dir = path->dirs.first->datum; 1184 if (dir == dotLast) { 1185 seenDotLast = true; 1186 DEBUG0(DIR, "[dot last]..."); 1187 } 1188 } 1189 DEBUG0(DIR, "\n"); 1190 1191 /* 1192 * If there's no leading directory components or if the leading 1193 * directory component is exactly `./', consult the cached contents 1194 * of each of the directories on the search path. 1195 */ 1196 if (base == name || (base - name == 2 && *name == '.')) { 1197 CachedDirListNode *ln; 1198 1199 /* 1200 * Look through all the directories on the path seeking one 1201 * which contains the final component of the given name. If 1202 * such a file is found, return its pathname. 1203 * If there is no such file, go on to phase two. 1204 * 1205 * No matter what, always look for the file in the current 1206 * directory before anywhere else (unless the path contains 1207 * the magic '.DOTLAST', in which case search it last). 1208 * This is so there are no conflicts between what the user 1209 * specifies (fish.c) and what make finds (./fish.c). 1210 */ 1211 if (!seenDotLast && (file = DirFindDot(name, base)) != NULL) 1212 return file; 1213 1214 for (ln = path->dirs.first; ln != NULL; ln = ln->next) { 1215 CachedDir *dir = ln->datum; 1216 if (dir == dotLast) 1217 continue; 1218 if ((file = DirLookup(dir, base)) != NULL) 1219 return file; 1220 } 1221 1222 if (seenDotLast && (file = DirFindDot(name, base)) != NULL) 1223 return file; 1224 } 1225 1226 if (base == name) { 1227 DEBUG0(DIR, " failed.\n"); 1228 misses++; 1229 return NULL; 1230 } 1231 1232 if (*base == '\0') 1233 base = trailing_dot; /* we were given a trailing "/" */ 1234 1235 if (name[0] != '/') { 1236 if (FindFileRelative(path, seenDotLast, name, &file)) 1237 return file; 1238 } else { 1239 if (FindFileAbsolute(path, seenDotLast, name, base, &file)) 1240 return file; 1241 } 1242 1243 /* 1244 * We cannot add the directory onto the search path because 1245 * of this amusing case: 1246 * $(INSTALLDIR)/$(FILE): $(FILE) 1247 * 1248 * $(FILE) exists in $(INSTALLDIR) but not in the current one. 1249 * When searching for $(FILE), we will find it in $(INSTALLDIR) 1250 * b/c we added it here. This is not good... 1251 */ 1252 1253 DEBUG1(DIR, " Looking for \"%s\" ...\n", name); 1254 1255 bigmisses++; 1256 if (cached_stat(name, &cst) == 0) 1257 return bmake_strdup(name); 1258 1259 DEBUG0(DIR, " failed. Returning NULL\n"); 1260 return NULL; 1261 } 1262 1263 /* 1264 * Find the file with the given name along the given search path. 1265 * 1266 * Input: 1267 * name the file to find 1268 * path the directories to search, or NULL 1269 * 1270 * Results: 1271 * The freshly allocated path to the file, or NULL. 1272 */ 1273 char * 1274 Dir_FindFile(const char *name, SearchPath *path) 1275 { 1276 return FindFile(name, path, false); 1277 } 1278 1279 /* 1280 * Find the include file with the given name along the given search path. 1281 * 1282 * Input: 1283 * name the file to find 1284 * path the directories to search, or NULL 1285 * 1286 * Results: 1287 * The freshly allocated path to the file, or NULL. 1288 */ 1289 char * 1290 Dir_FindInclude(const char *name, SearchPath *path) 1291 { 1292 return FindFile(name, path, true); 1293 } 1294 1295 1296 /* 1297 * Search for 'needle' starting at the directory 'here' and then working our 1298 * way up towards the root directory. Return the allocated path, or NULL. 1299 */ 1300 char * 1301 Dir_FindHereOrAbove(const char *here, const char *needle) 1302 { 1303 struct cached_stat cst; 1304 char *dirbase, *dirbase_end; 1305 char *try, *try_end; 1306 1307 dirbase = bmake_strdup(here); 1308 dirbase_end = dirbase + strlen(dirbase); 1309 1310 for (;;) { 1311 try = str_concat3(dirbase, "/", needle); 1312 if (cached_stat(try, &cst) != -1) { 1313 if ((cst.cst_mode & S_IFMT) != S_IFDIR) { 1314 /* 1315 * Chop off the filename, to return a 1316 * directory. 1317 */ 1318 try_end = try + strlen(try); 1319 while (try_end > try && *try_end != '/') 1320 try_end--; 1321 if (try_end > try) 1322 *try_end = '\0'; /* chop! */ 1323 } 1324 1325 free(dirbase); 1326 return try; 1327 } 1328 free(try); 1329 1330 if (dirbase_end == dirbase) 1331 break; /* failed! */ 1332 1333 /* Truncate dirbase from the end to move up a dir. */ 1334 while (dirbase_end > dirbase && *dirbase_end != '/') 1335 dirbase_end--; 1336 *dirbase_end = '\0'; /* chop! */ 1337 } 1338 1339 free(dirbase); 1340 return NULL; 1341 } 1342 1343 /* 1344 * This is an implied source, and it may have moved, 1345 * see if we can find it via the current .PATH 1346 */ 1347 static char * 1348 ResolveMovedDepends(GNode *gn) 1349 { 1350 char *fullName; 1351 1352 const char *base = str_basename(gn->name); 1353 if (base == gn->name) 1354 return NULL; 1355 1356 fullName = Dir_FindFile(base, Suff_FindPath(gn)); 1357 if (fullName == NULL) 1358 return NULL; 1359 1360 /* 1361 * Put the found file in gn->path so that we give that to the compiler. 1362 */ 1363 /* 1364 * XXX: Better just reset gn->path to NULL; updating it is already done 1365 * by Dir_UpdateMTime. 1366 */ 1367 gn->path = bmake_strdup(fullName); 1368 if (!Job_RunTarget(".STALE", gn->fname)) 1369 fprintf(stdout, /* XXX: Why stdout? */ 1370 "%s: %s, %u: ignoring stale %s for %s, found %s\n", 1371 progname, gn->fname, gn->lineno, 1372 makeDependfile, gn->name, fullName); 1373 1374 return fullName; 1375 } 1376 1377 static char * 1378 ResolveFullName(GNode *gn) 1379 { 1380 char *fullName; 1381 1382 fullName = gn->path; 1383 if (fullName == NULL && !(gn->type & OP_NOPATH)) { 1384 1385 fullName = Dir_FindFile(gn->name, Suff_FindPath(gn)); 1386 1387 if (fullName == NULL && gn->flags.fromDepend && 1388 !Lst_IsEmpty(&gn->implicitParents)) 1389 fullName = ResolveMovedDepends(gn); 1390 1391 DEBUG2(DIR, "Found '%s' as '%s'\n", 1392 gn->name, fullName != NULL ? fullName : "(not found)"); 1393 } 1394 1395 if (fullName == NULL) 1396 fullName = bmake_strdup(gn->name); 1397 1398 /* XXX: Is every piece of memory freed as it should? */ 1399 1400 return fullName; 1401 } 1402 1403 /* 1404 * Search 'gn' along 'dirSearchPath' and store its modification time in 1405 * 'gn->mtime'. If no file is found, store 0 instead. 1406 * 1407 * The found file is stored in 'gn->path', unless the node already had a path. 1408 */ 1409 void 1410 Dir_UpdateMTime(GNode *gn, bool forceRefresh) 1411 { 1412 char *fullName; 1413 struct cached_stat cst; 1414 1415 if (gn->type & OP_ARCHV) { 1416 Arch_UpdateMTime(gn); 1417 return; 1418 } 1419 1420 if (gn->type & OP_PHONY) { 1421 gn->mtime = 0; 1422 return; 1423 } 1424 1425 fullName = ResolveFullName(gn); 1426 1427 if (cached_stats(fullName, &cst, false, forceRefresh) < 0) { 1428 if (gn->type & OP_MEMBER) { 1429 if (fullName != gn->path) 1430 free(fullName); 1431 Arch_UpdateMemberMTime(gn); 1432 return; 1433 } 1434 1435 cst.cst_mtime = 0; 1436 } 1437 1438 if (fullName != NULL && gn->path == NULL) 1439 gn->path = fullName; 1440 /* XXX: else free(fullName)? */ 1441 1442 gn->mtime = cst.cst_mtime; 1443 } 1444 1445 /* 1446 * Read the directory and add it to the cache in openDirs. 1447 * If a path is given, add the directory to that path as well. 1448 */ 1449 static CachedDir * 1450 CacheNewDir(const char *name, SearchPath *path) 1451 { 1452 CachedDir *dir = NULL; 1453 DIR *d; 1454 struct dirent *dp; 1455 1456 if ((d = opendir(name)) == NULL) { 1457 DEBUG1(DIR, "Caching %s ... not found\n", name); 1458 return dir; 1459 } 1460 1461 DEBUG1(DIR, "Caching %s ...\n", name); 1462 1463 dir = CachedDir_New(name); 1464 1465 while ((dp = readdir(d)) != NULL) { 1466 1467 #if defined(sun) && defined(d_ino) /* d_ino is a sunos4 #define for d_fileno */ 1468 /* 1469 * The sun directory library doesn't check for a 0 inode 1470 * (0-inode slots just take up space), so we have to do 1471 * it ourselves. 1472 */ 1473 if (dp->d_fileno == 0) 1474 continue; 1475 #endif /* sun && d_ino */ 1476 1477 (void)HashSet_Add(&dir->files, dp->d_name); 1478 } 1479 (void)closedir(d); 1480 1481 OpenDirs_Add(&openDirs, dir); 1482 if (path != NULL) 1483 Lst_Append(&path->dirs, CachedDir_Ref(dir)); 1484 1485 DEBUG1(DIR, "Caching %s done\n", name); 1486 return dir; 1487 } 1488 1489 /* 1490 * Read the list of filenames in the directory 'name' and store the result 1491 * in 'openDirs'. 1492 * 1493 * If a search path is given, append the directory to that path. 1494 * 1495 * Input: 1496 * path The path to which the directory should be 1497 * added, or NULL to only add the directory to openDirs. 1498 * name The name of the directory to add. 1499 * The name is not normalized in any way. 1500 * Output: 1501 * result If no path is given and the directory exists, the 1502 * returned CachedDir has a reference count of 0. It 1503 * must either be assigned to a variable using 1504 * CachedDir_Assign or be appended to a SearchPath using 1505 * Lst_Append and CachedDir_Ref. 1506 */ 1507 CachedDir * 1508 SearchPath_Add(SearchPath *path, const char *name) 1509 { 1510 1511 if (path != NULL && strcmp(name, ".DOTLAST") == 0) { 1512 CachedDirListNode *ln; 1513 1514 /* XXX: Linear search gets slow with thousands of entries. */ 1515 for (ln = path->dirs.first; ln != NULL; ln = ln->next) { 1516 CachedDir *pathDir = ln->datum; 1517 if (strcmp(pathDir->name, name) == 0) 1518 return pathDir; 1519 } 1520 1521 Lst_Prepend(&path->dirs, CachedDir_Ref(dotLast)); 1522 } 1523 1524 if (path != NULL) { 1525 /* XXX: Why is OpenDirs only checked if path != NULL? */ 1526 CachedDir *dir = OpenDirs_Find(&openDirs, name); 1527 if (dir != NULL) { 1528 if (Lst_FindDatum(&path->dirs, dir) == NULL) 1529 Lst_Append(&path->dirs, CachedDir_Ref(dir)); 1530 return dir; 1531 } 1532 } 1533 1534 return CacheNewDir(name, path); 1535 } 1536 1537 /* 1538 * Return a copy of dirSearchPath, incrementing the reference counts for 1539 * the contained directories. 1540 */ 1541 SearchPath * 1542 Dir_CopyDirSearchPath(void) 1543 { 1544 SearchPath *path = SearchPath_New(); 1545 CachedDirListNode *ln; 1546 for (ln = dirSearchPath.dirs.first; ln != NULL; ln = ln->next) { 1547 CachedDir *dir = ln->datum; 1548 Lst_Append(&path->dirs, CachedDir_Ref(dir)); 1549 } 1550 return path; 1551 } 1552 1553 /* 1554 * Make a string by taking all the directories in the given search path and 1555 * preceding them by the given flag. Used by the suffix module to create 1556 * variables for compilers based on suffix search paths. Note that there is no 1557 * space between the given flag and each directory. 1558 */ 1559 char * 1560 SearchPath_ToFlags(SearchPath *path, const char *flag) 1561 { 1562 Buffer buf; 1563 CachedDirListNode *ln; 1564 1565 Buf_Init(&buf); 1566 1567 if (path != NULL) { 1568 for (ln = path->dirs.first; ln != NULL; ln = ln->next) { 1569 CachedDir *dir = ln->datum; 1570 Buf_AddStr(&buf, " "); 1571 Buf_AddStr(&buf, flag); 1572 Buf_AddStr(&buf, dir->name); 1573 } 1574 } 1575 1576 return Buf_DoneData(&buf); 1577 } 1578 1579 /* Free the search path and all directories mentioned in it. */ 1580 void 1581 SearchPath_Free(SearchPath *path) 1582 { 1583 CachedDirListNode *ln; 1584 1585 for (ln = path->dirs.first; ln != NULL; ln = ln->next) { 1586 CachedDir *dir = ln->datum; 1587 CachedDir_Unref(dir); 1588 } 1589 Lst_Done(&path->dirs); 1590 free(path); 1591 } 1592 1593 /* 1594 * Clear out all elements from the given search path. 1595 * The path is set to the empty list but is not destroyed. 1596 */ 1597 void 1598 SearchPath_Clear(SearchPath *path) 1599 { 1600 while (!Lst_IsEmpty(&path->dirs)) { 1601 CachedDir *dir = Lst_Dequeue(&path->dirs); 1602 CachedDir_Unref(dir); 1603 } 1604 } 1605 1606 1607 /* 1608 * Concatenate two paths, adding the second to the end of the first, 1609 * skipping duplicates. 1610 */ 1611 void 1612 SearchPath_AddAll(SearchPath *dst, SearchPath *src) 1613 { 1614 CachedDirListNode *ln; 1615 1616 for (ln = src->dirs.first; ln != NULL; ln = ln->next) { 1617 CachedDir *dir = ln->datum; 1618 if (Lst_FindDatum(&dst->dirs, dir) == NULL) 1619 Lst_Append(&dst->dirs, CachedDir_Ref(dir)); 1620 } 1621 } 1622 1623 static int 1624 percentage(int num, int den) 1625 { 1626 return den != 0 ? num * 100 / den : 0; 1627 } 1628 1629 void 1630 Dir_PrintDirectories(void) 1631 { 1632 CachedDirListNode *ln; 1633 1634 debug_printf("#*** Directory Cache:\n"); 1635 debug_printf( 1636 "# Stats: %d hits %d misses %d near misses %d losers (%d%%)\n", 1637 hits, misses, nearmisses, bigmisses, 1638 percentage(hits, hits + bigmisses + nearmisses)); 1639 debug_printf("# refs hits directory\n"); 1640 1641 for (ln = openDirs.list.first; ln != NULL; ln = ln->next) { 1642 CachedDir *dir = ln->datum; 1643 debug_printf("# %4d %4d %s\n", 1644 dir->refCount, dir->hits, dir->name); 1645 } 1646 } 1647 1648 void 1649 SearchPath_Print(const SearchPath *path) 1650 { 1651 CachedDirListNode *ln; 1652 1653 for (ln = path->dirs.first; ln != NULL; ln = ln->next) { 1654 const CachedDir *dir = ln->datum; 1655 debug_printf("%s ", dir->name); 1656 } 1657 } 1658