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