1 /* $NetBSD: dir.c,v 1.76 2020/07/03 08:13:23 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 #ifndef MAKE_NATIVE 73 static char rcsid[] = "$NetBSD: dir.c,v 1.76 2020/07/03 08:13:23 rillig Exp $"; 74 #else 75 #include <sys/cdefs.h> 76 #ifndef lint 77 #if 0 78 static char sccsid[] = "@(#)dir.c 8.2 (Berkeley) 1/2/94"; 79 #else 80 __RCSID("$NetBSD: dir.c,v 1.76 2020/07/03 08:13:23 rillig Exp $"); 81 #endif 82 #endif /* not lint */ 83 #endif 84 85 /*- 86 * dir.c -- 87 * Directory searching using wildcards and/or normal names... 88 * Used both for source wildcarding in the Makefile and for finding 89 * implicit sources. 90 * 91 * The interface for this module is: 92 * Dir_Init Initialize the module. 93 * 94 * Dir_InitCur Set the cur Path. 95 * 96 * Dir_InitDot Set the dot Path. 97 * 98 * Dir_End Cleanup the module. 99 * 100 * Dir_SetPATH Set ${.PATH} to reflect state of dirSearchPath. 101 * 102 * Dir_HasWildcards Returns TRUE if the name given it needs to 103 * be wildcard-expanded. 104 * 105 * Dir_Expand Given a pattern and a path, return a Lst of names 106 * which match the pattern on the search path. 107 * 108 * Dir_FindFile Searches for a file on a given search path. 109 * If it exists, the entire path is returned. 110 * Otherwise NULL is returned. 111 * 112 * Dir_FindHereOrAbove Search for a path in the current directory and 113 * then all the directories above it in turn until 114 * the path is found or we reach the root ("/"). 115 * 116 * Dir_MTime Return the modification time of a node. The file 117 * is searched for along the default search path. 118 * The path and mtime fields of the node are filled 119 * in. 120 * 121 * Dir_AddDir Add a directory to a search path. 122 * 123 * Dir_MakeFlags Given a search path and a command flag, create 124 * a string with each of the directories in the path 125 * preceded by the command flag and all of them 126 * separated by a space. 127 * 128 * Dir_Destroy Destroy an element of a search path. Frees up all 129 * things that can be freed for the element as long 130 * as the element is no longer referenced by any other 131 * search path. 132 * Dir_ClearPath Resets a search path to the empty list. 133 * 134 * For debugging: 135 * Dir_PrintDirectories Print stats about the directory cache. 136 */ 137 138 #include <sys/types.h> 139 #include <sys/stat.h> 140 141 #include <dirent.h> 142 #include <errno.h> 143 #include <stdio.h> 144 145 #include "make.h" 146 #include "hash.h" 147 #include "dir.h" 148 #include "job.h" 149 150 /* 151 * A search path consists of a Lst of Path structures. A Path structure 152 * has in it the name of the directory and a hash table of all the files 153 * in the directory. This is used to cut down on the number of system 154 * calls necessary to find implicit dependents and their like. Since 155 * these searches are made before any actions are taken, we need not 156 * worry about the directory changing due to creation commands. If this 157 * hampers the style of some makefiles, they must be changed. 158 * 159 * A list of all previously-read directories is kept in the 160 * openDirectories Lst. This list is checked first before a directory 161 * is opened. 162 * 163 * The need for the caching of whole directories is brought about by 164 * the multi-level transformation code in suff.c, which tends to search 165 * for far more files than regular make does. In the initial 166 * implementation, the amount of time spent performing "stat" calls was 167 * truly astronomical. The problem with hashing at the start is, 168 * of course, that pmake doesn't then detect changes to these directories 169 * during the course of the make. Three possibilities suggest themselves: 170 * 171 * 1) just use stat to test for a file's existence. As mentioned 172 * above, this is very inefficient due to the number of checks 173 * engendered by the multi-level transformation code. 174 * 2) use readdir() and company to search the directories, keeping 175 * them open between checks. I have tried this and while it 176 * didn't slow down the process too much, it could severely 177 * affect the amount of parallelism available as each directory 178 * open would take another file descriptor out of play for 179 * handling I/O for another job. Given that it is only recently 180 * that UNIX OS's have taken to allowing more than 20 or 32 181 * file descriptors for a process, this doesn't seem acceptable 182 * to me. 183 * 3) record the mtime of the directory in the Path structure and 184 * verify the directory hasn't changed since the contents were 185 * hashed. This will catch the creation or deletion of files, 186 * but not the updating of files. However, since it is the 187 * creation and deletion that is the problem, this could be 188 * a good thing to do. Unfortunately, if the directory (say ".") 189 * were fairly large and changed fairly frequently, the constant 190 * rehashing could seriously degrade performance. It might be 191 * good in such cases to keep track of the number of rehashes 192 * and if the number goes over a (small) limit, resort to using 193 * stat in its place. 194 * 195 * An additional thing to consider is that pmake is used primarily 196 * to create C programs and until recently pcc-based compilers refused 197 * to allow you to specify where the resulting object file should be 198 * placed. This forced all objects to be created in the current 199 * directory. This isn't meant as a full excuse, just an explanation of 200 * some of the reasons for the caching used here. 201 * 202 * One more note: the location of a target's file is only performed 203 * on the downward traversal of the graph and then only for terminal 204 * nodes in the graph. This could be construed as wrong in some cases, 205 * but prevents inadvertent modification of files when the "installed" 206 * directory for a file is provided in the search path. 207 * 208 * Another data structure maintained by this module is an mtime 209 * cache used when the searching of cached directories fails to find 210 * a file. In the past, Dir_FindFile would simply perform an access() 211 * call in such a case to determine if the file could be found using 212 * just the name given. When this hit, however, all that was gained 213 * was the knowledge that the file existed. Given that an access() is 214 * essentially a stat() without the copyout() call, and that the same 215 * filesystem overhead would have to be incurred in Dir_MTime, it made 216 * sense to replace the access() with a stat() and record the mtime 217 * in a cache for when Dir_MTime was actually called. 218 */ 219 220 Lst dirSearchPath; /* main search path */ 221 222 static Lst openDirectories; /* the list of all open directories */ 223 224 /* 225 * Variables for gathering statistics on the efficiency of the hashing 226 * mechanism. 227 */ 228 static int hits, /* Found in directory cache */ 229 misses, /* Sad, but not evil misses */ 230 nearmisses, /* Found under search path */ 231 bigmisses; /* Sought by itself */ 232 233 static Path *dot; /* contents of current directory */ 234 static Path *cur; /* contents of current directory, if not dot */ 235 static Path *dotLast; /* a fake path entry indicating we need to 236 * look for . last */ 237 static Hash_Table mtimes; /* Results of doing a last-resort stat in 238 * Dir_FindFile -- if we have to go to the 239 * system to find the file, we might as well 240 * have its mtime on record. XXX: If this is done 241 * way early, there's a chance other rules will 242 * have already updated the file, in which case 243 * we'll update it again. Generally, there won't 244 * be two rules to update a single file, so this 245 * should be ok, but... */ 246 247 static Hash_Table lmtimes; /* same as mtimes but for lstat */ 248 249 static int DirFindName(const void *, const void *); 250 static int DirMatchFiles(const char *, Path *, Lst); 251 static void DirExpandCurly(const char *, const char *, Lst, Lst); 252 static void DirExpandInt(const char *, Lst, Lst); 253 static int DirPrintWord(void *, void *); 254 static int DirPrintDir(void *, void *); 255 static char *DirLookup(Path *, const char *, const char *, Boolean); 256 static char *DirLookupSubdir(Path *, const char *); 257 static char *DirFindDot(Boolean, const char *, const char *); 258 static char *DirLookupAbs(Path *, const char *, const char *); 259 260 261 /* 262 * We use stat(2) a lot, cache the results 263 * mtime and mode are all we care about. 264 */ 265 struct cache_st { 266 time_t lmtime; /* lstat */ 267 time_t mtime; /* stat */ 268 mode_t mode; 269 }; 270 271 /* minimize changes below */ 272 #define CST_LSTAT 1 273 #define CST_UPDATE 2 274 275 static int 276 cached_stats(Hash_Table *htp, const char *pathname, struct stat *st, int flags) 277 { 278 Hash_Entry *entry; 279 struct cache_st *cst; 280 int rc; 281 282 if (!pathname || !pathname[0]) 283 return -1; 284 285 entry = Hash_FindEntry(htp, pathname); 286 287 if (entry && (flags & CST_UPDATE) == 0) { 288 cst = entry->clientPtr; 289 290 memset(st, 0, sizeof(*st)); 291 st->st_mode = cst->mode; 292 st->st_mtime = (flags & CST_LSTAT) ? cst->lmtime : cst->mtime; 293 if (st->st_mtime) { 294 if (DEBUG(DIR)) { 295 fprintf(debug_file, "Using cached time %s for %s\n", 296 Targ_FmtTime(st->st_mtime), pathname); 297 } 298 return 0; 299 } 300 } 301 302 rc = (flags & CST_LSTAT) ? lstat(pathname, st) : stat(pathname, st); 303 if (rc == -1) 304 return -1; 305 306 if (st->st_mtime == 0) 307 st->st_mtime = 1; /* avoid confusion with missing file */ 308 309 if (!entry) 310 entry = Hash_CreateEntry(htp, pathname, NULL); 311 if (!entry->clientPtr) { 312 entry->clientPtr = bmake_malloc(sizeof(*cst)); 313 memset(entry->clientPtr, 0, sizeof(*cst)); 314 } 315 cst = entry->clientPtr; 316 if ((flags & CST_LSTAT)) { 317 cst->lmtime = st->st_mtime; 318 } else { 319 cst->mtime = st->st_mtime; 320 } 321 cst->mode = st->st_mode; 322 if (DEBUG(DIR)) { 323 fprintf(debug_file, " Caching %s for %s\n", 324 Targ_FmtTime(st->st_mtime), pathname); 325 } 326 327 return 0; 328 } 329 330 int 331 cached_stat(const char *pathname, void *st) 332 { 333 return cached_stats(&mtimes, pathname, st, 0); 334 } 335 336 int 337 cached_lstat(const char *pathname, void *st) 338 { 339 return cached_stats(&lmtimes, pathname, st, CST_LSTAT); 340 } 341 342 /*- 343 *----------------------------------------------------------------------- 344 * Dir_Init -- 345 * initialize things for this module 346 * 347 * Results: 348 * none 349 * 350 * Side Effects: 351 * some directories may be opened. 352 *----------------------------------------------------------------------- 353 */ 354 void 355 Dir_Init(const char *cdname) 356 { 357 if (!cdname) { 358 dirSearchPath = Lst_Init(FALSE); 359 openDirectories = Lst_Init(FALSE); 360 Hash_InitTable(&mtimes, 0); 361 Hash_InitTable(&lmtimes, 0); 362 return; 363 } 364 Dir_InitCur(cdname); 365 366 dotLast = bmake_malloc(sizeof(Path)); 367 dotLast->refCount = 1; 368 dotLast->hits = 0; 369 dotLast->name = bmake_strdup(".DOTLAST"); 370 Hash_InitTable(&dotLast->files, -1); 371 } 372 373 /* 374 * Called by Dir_Init() and whenever .CURDIR is assigned to. 375 */ 376 void 377 Dir_InitCur(const char *cdname) 378 { 379 Path *p; 380 381 if (cdname != NULL) { 382 /* 383 * Our build directory is not the same as our source directory. 384 * Keep this one around too. 385 */ 386 if ((p = Dir_AddDir(NULL, cdname))) { 387 p->refCount += 1; 388 if (cur && cur != p) { 389 /* 390 * We've been here before, cleanup. 391 */ 392 cur->refCount -= 1; 393 Dir_Destroy(cur); 394 } 395 cur = p; 396 } 397 } 398 } 399 400 /*- 401 *----------------------------------------------------------------------- 402 * Dir_InitDot -- 403 * (re)initialize "dot" (current/object directory) path hash 404 * 405 * Results: 406 * none 407 * 408 * Side Effects: 409 * some directories may be opened. 410 *----------------------------------------------------------------------- 411 */ 412 void 413 Dir_InitDot(void) 414 { 415 if (dot != NULL) { 416 LstNode ln; 417 418 /* Remove old entry from openDirectories, but do not destroy. */ 419 ln = Lst_Member(openDirectories, dot); 420 (void)Lst_Remove(openDirectories, ln); 421 } 422 423 dot = Dir_AddDir(NULL, "."); 424 425 if (dot == NULL) { 426 Error("Cannot open `.' (%s)", strerror(errno)); 427 exit(1); 428 } 429 430 /* 431 * We always need to have dot around, so we increment its reference count 432 * to make sure it's not destroyed. 433 */ 434 dot->refCount += 1; 435 Dir_SetPATH(); /* initialize */ 436 } 437 438 /*- 439 *----------------------------------------------------------------------- 440 * Dir_End -- 441 * cleanup things for this module 442 * 443 * Results: 444 * none 445 * 446 * Side Effects: 447 * none 448 *----------------------------------------------------------------------- 449 */ 450 void 451 Dir_End(void) 452 { 453 #ifdef CLEANUP 454 if (cur) { 455 cur->refCount -= 1; 456 Dir_Destroy(cur); 457 } 458 dot->refCount -= 1; 459 dotLast->refCount -= 1; 460 Dir_Destroy(dotLast); 461 Dir_Destroy(dot); 462 Dir_ClearPath(dirSearchPath); 463 Lst_Destroy(dirSearchPath, NULL); 464 Dir_ClearPath(openDirectories); 465 Lst_Destroy(openDirectories, NULL); 466 Hash_DeleteTable(&mtimes); 467 #endif 468 } 469 470 /* 471 * We want ${.PATH} to indicate the order in which we will actually 472 * search, so we rebuild it after any .PATH: target. 473 * This is the simplest way to deal with the effect of .DOTLAST. 474 */ 475 void 476 Dir_SetPATH(void) 477 { 478 LstNode ln; /* a list element */ 479 Path *p; 480 Boolean hasLastDot = FALSE; /* true we should search dot last */ 481 482 Var_Delete(".PATH", VAR_GLOBAL); 483 484 if (Lst_Open(dirSearchPath) == SUCCESS) { 485 if ((ln = Lst_First(dirSearchPath)) != NULL) { 486 p = (Path *)Lst_Datum(ln); 487 if (p == dotLast) { 488 hasLastDot = TRUE; 489 Var_Append(".PATH", dotLast->name, VAR_GLOBAL); 490 } 491 } 492 493 if (!hasLastDot) { 494 if (dot) 495 Var_Append(".PATH", dot->name, VAR_GLOBAL); 496 if (cur) 497 Var_Append(".PATH", cur->name, VAR_GLOBAL); 498 } 499 500 while ((ln = Lst_Next(dirSearchPath)) != NULL) { 501 p = (Path *)Lst_Datum(ln); 502 if (p == dotLast) 503 continue; 504 if (p == dot && hasLastDot) 505 continue; 506 Var_Append(".PATH", p->name, VAR_GLOBAL); 507 } 508 509 if (hasLastDot) { 510 if (dot) 511 Var_Append(".PATH", dot->name, VAR_GLOBAL); 512 if (cur) 513 Var_Append(".PATH", cur->name, VAR_GLOBAL); 514 } 515 Lst_Close(dirSearchPath); 516 } 517 } 518 519 /*- 520 *----------------------------------------------------------------------- 521 * DirFindName -- 522 * See if the Path structure describes the same directory as the 523 * given one by comparing their names. Called from Dir_AddDir via 524 * Lst_Find when searching the list of open directories. 525 * 526 * Input: 527 * p Current name 528 * dname Desired name 529 * 530 * Results: 531 * 0 if it is the same. Non-zero otherwise 532 * 533 * Side Effects: 534 * None 535 *----------------------------------------------------------------------- 536 */ 537 static int 538 DirFindName(const void *p, const void *dname) 539 { 540 return strcmp(((const Path *)p)->name, dname); 541 } 542 543 /*- 544 *----------------------------------------------------------------------- 545 * Dir_HasWildcards -- 546 * see if the given name has any wildcard characters in it 547 * be careful not to expand unmatching brackets or braces. 548 * XXX: This code is not 100% correct. ([^]] fails etc.) 549 * I really don't think that make(1) should be expanding 550 * patterns, because then you have to set a mechanism for 551 * escaping the expansion! 552 * 553 * Input: 554 * name name to check 555 * 556 * Results: 557 * returns TRUE if the word should be expanded, FALSE otherwise 558 * 559 * Side Effects: 560 * none 561 *----------------------------------------------------------------------- 562 */ 563 Boolean 564 Dir_HasWildcards(char *name) 565 { 566 char *cp; 567 int wild = 0, brace = 0, bracket = 0; 568 569 for (cp = name; *cp; cp++) { 570 switch(*cp) { 571 case '{': 572 brace++; 573 wild = 1; 574 break; 575 case '}': 576 brace--; 577 break; 578 case '[': 579 bracket++; 580 wild = 1; 581 break; 582 case ']': 583 bracket--; 584 break; 585 case '?': 586 case '*': 587 wild = 1; 588 break; 589 default: 590 break; 591 } 592 } 593 return wild && bracket == 0 && brace == 0; 594 } 595 596 /*- 597 *----------------------------------------------------------------------- 598 * DirMatchFiles -- 599 * Given a pattern and a Path structure, see if any files 600 * match the pattern and add their names to the 'expansions' list if 601 * any do. This is incomplete -- it doesn't take care of patterns like 602 * src / *src / *.c properly (just *.c on any of the directories), but it 603 * will do for now. 604 * 605 * Input: 606 * pattern Pattern to look for 607 * p Directory to search 608 * expansion Place to store the results 609 * 610 * Results: 611 * Always returns 0 612 * 613 * Side Effects: 614 * File names are added to the expansions lst. The directory will be 615 * fully hashed when this is done. 616 *----------------------------------------------------------------------- 617 */ 618 static int 619 DirMatchFiles(const char *pattern, Path *p, Lst expansions) 620 { 621 Hash_Search search; /* Index into the directory's table */ 622 Hash_Entry *entry; /* Current entry in the table */ 623 Boolean isDot; /* TRUE if the directory being searched is . */ 624 625 isDot = (*p->name == '.' && p->name[1] == '\0'); 626 627 for (entry = Hash_EnumFirst(&p->files, &search); 628 entry != NULL; 629 entry = Hash_EnumNext(&search)) 630 { 631 /* 632 * See if the file matches the given pattern. Note we follow the UNIX 633 * convention that dot files will only be found if the pattern 634 * begins with a dot (note also that as a side effect of the hashing 635 * scheme, .* won't match . or .. since they aren't hashed). 636 */ 637 if (Str_Match(entry->name, pattern) && 638 ((entry->name[0] != '.') || 639 (pattern[0] == '.'))) 640 { 641 (void)Lst_AtEnd(expansions, 642 (isDot ? bmake_strdup(entry->name) : 643 str_concat(p->name, entry->name, 644 STR_ADDSLASH))); 645 } 646 } 647 return 0; 648 } 649 650 /*- 651 *----------------------------------------------------------------------- 652 * DirExpandCurly -- 653 * Expand curly braces like the C shell. Does this recursively. 654 * Note the special case: if after the piece of the curly brace is 655 * done there are no wildcard characters in the result, the result is 656 * placed on the list WITHOUT CHECKING FOR ITS EXISTENCE. 657 * 658 * Input: 659 * word Entire word to expand 660 * brace First curly brace in it 661 * path Search path to use 662 * expansions Place to store the expansions 663 * 664 * Results: 665 * None. 666 * 667 * Side Effects: 668 * The given list is filled with the expansions... 669 * 670 *----------------------------------------------------------------------- 671 */ 672 static void 673 DirExpandCurly(const char *word, const char *brace, Lst path, Lst expansions) 674 { 675 const char *end; /* Character after the closing brace */ 676 const char *cp; /* Current position in brace clause */ 677 const char *start; /* Start of current piece of brace clause */ 678 int bracelevel; /* Number of braces we've seen. If we see a 679 * right brace when this is 0, we've hit the 680 * end of the clause. */ 681 char *file; /* Current expansion */ 682 int otherLen; /* The length of the other pieces of the 683 * expansion (chars before and after the 684 * clause in 'word') */ 685 char *cp2; /* Pointer for checking for wildcards in 686 * expansion before calling Dir_Expand */ 687 688 start = brace+1; 689 690 /* 691 * Find the end of the brace clause first, being wary of nested brace 692 * clauses. 693 */ 694 for (end = start, bracelevel = 0; *end != '\0'; end++) { 695 if (*end == '{') { 696 bracelevel++; 697 } else if ((*end == '}') && (bracelevel-- == 0)) { 698 break; 699 } 700 } 701 if (*end == '\0') { 702 Error("Unterminated {} clause \"%s\"", start); 703 return; 704 } else { 705 end++; 706 } 707 otherLen = brace - word + strlen(end); 708 709 for (cp = start; cp < end; cp++) { 710 /* 711 * Find the end of this piece of the clause. 712 */ 713 bracelevel = 0; 714 while (*cp != ',') { 715 if (*cp == '{') { 716 bracelevel++; 717 } else if ((*cp == '}') && (bracelevel-- <= 0)) { 718 break; 719 } 720 cp++; 721 } 722 /* 723 * Allocate room for the combination and install the three pieces. 724 */ 725 file = bmake_malloc(otherLen + cp - start + 1); 726 if (brace != word) { 727 strncpy(file, word, brace-word); 728 } 729 if (cp != start) { 730 strncpy(&file[brace-word], start, cp-start); 731 } 732 strcpy(&file[(brace-word)+(cp-start)], end); 733 734 /* 735 * See if the result has any wildcards in it. If we find one, call 736 * Dir_Expand right away, telling it to place the result on our list 737 * of expansions. 738 */ 739 for (cp2 = file; *cp2 != '\0'; cp2++) { 740 switch(*cp2) { 741 case '*': 742 case '?': 743 case '{': 744 case '[': 745 Dir_Expand(file, path, expansions); 746 goto next; 747 } 748 } 749 if (*cp2 == '\0') { 750 /* 751 * Hit the end w/o finding any wildcards, so stick the expansion 752 * on the end of the list. 753 */ 754 (void)Lst_AtEnd(expansions, file); 755 } else { 756 next: 757 free(file); 758 } 759 start = cp+1; 760 } 761 } 762 763 764 /*- 765 *----------------------------------------------------------------------- 766 * DirExpandInt -- 767 * Internal expand routine. Passes through the directories in the 768 * path one by one, calling DirMatchFiles for each. NOTE: This still 769 * doesn't handle patterns in directories... 770 * 771 * Input: 772 * word Word to expand 773 * path Path on which to look 774 * expansions Place to store the result 775 * 776 * Results: 777 * None. 778 * 779 * Side Effects: 780 * Things are added to the expansions list. 781 * 782 *----------------------------------------------------------------------- 783 */ 784 static void 785 DirExpandInt(const char *word, Lst path, Lst expansions) 786 { 787 LstNode ln; /* Current node */ 788 Path *p; /* Directory in the node */ 789 790 if (Lst_Open(path) == SUCCESS) { 791 while ((ln = Lst_Next(path)) != NULL) { 792 p = (Path *)Lst_Datum(ln); 793 DirMatchFiles(word, p, expansions); 794 } 795 Lst_Close(path); 796 } 797 } 798 799 /*- 800 *----------------------------------------------------------------------- 801 * DirPrintWord -- 802 * Print a word in the list of expansions. Callback for Dir_Expand 803 * when DEBUG(DIR), via Lst_ForEach. 804 * 805 * Results: 806 * === 0 807 * 808 * Side Effects: 809 * The passed word is printed, followed by a space. 810 * 811 *----------------------------------------------------------------------- 812 */ 813 static int 814 DirPrintWord(void *word, void *dummy MAKE_ATTR_UNUSED) 815 { 816 fprintf(debug_file, "%s ", (char *)word); 817 818 return 0; 819 } 820 821 /*- 822 *----------------------------------------------------------------------- 823 * Dir_Expand -- 824 * Expand the given word into a list of words by globbing it looking 825 * in the directories on the given search path. 826 * 827 * Input: 828 * word the word to expand 829 * path the list of directories in which to find the 830 * resulting files 831 * expansions the list on which to place the results 832 * 833 * Results: 834 * A list of words consisting of the files which exist along the search 835 * path matching the given pattern. 836 * 837 * Side Effects: 838 * Directories may be opened. Who knows? 839 *----------------------------------------------------------------------- 840 */ 841 void 842 Dir_Expand(const char *word, Lst path, Lst expansions) 843 { 844 const char *cp; 845 846 if (DEBUG(DIR)) { 847 fprintf(debug_file, "Expanding \"%s\"... ", word); 848 } 849 850 cp = strchr(word, '{'); 851 if (cp) { 852 DirExpandCurly(word, cp, path, expansions); 853 } else { 854 cp = strchr(word, '/'); 855 if (cp) { 856 /* 857 * The thing has a directory component -- find the first wildcard 858 * in the string. 859 */ 860 for (cp = word; *cp; cp++) { 861 if (*cp == '?' || *cp == '[' || *cp == '*' || *cp == '{') { 862 break; 863 } 864 } 865 if (*cp == '{') { 866 /* 867 * This one will be fun. 868 */ 869 DirExpandCurly(word, cp, path, expansions); 870 return; 871 } else if (*cp != '\0') { 872 /* 873 * Back up to the start of the component 874 */ 875 char *dirpath; 876 877 while (cp > word && *cp != '/') { 878 cp--; 879 } 880 if (cp != word) { 881 char sc; 882 /* 883 * If the glob isn't in the first component, try and find 884 * all the components up to the one with a wildcard. 885 */ 886 sc = cp[1]; 887 ((char *)UNCONST(cp))[1] = '\0'; 888 dirpath = Dir_FindFile(word, path); 889 ((char *)UNCONST(cp))[1] = sc; 890 /* 891 * dirpath is null if can't find the leading component 892 * XXX: Dir_FindFile won't find internal components. 893 * i.e. if the path contains ../Etc/Object and we're 894 * looking for Etc, it won't be found. Ah well. 895 * Probably not important. 896 */ 897 if (dirpath != NULL) { 898 char *dp = &dirpath[strlen(dirpath) - 1]; 899 if (*dp == '/') 900 *dp = '\0'; 901 path = Lst_Init(FALSE); 902 (void)Dir_AddDir(path, dirpath); 903 DirExpandInt(cp+1, path, expansions); 904 Lst_Destroy(path, NULL); 905 } 906 } else { 907 /* 908 * Start the search from the local directory 909 */ 910 DirExpandInt(word, path, expansions); 911 } 912 } else { 913 /* 914 * Return the file -- this should never happen. 915 */ 916 DirExpandInt(word, path, expansions); 917 } 918 } else { 919 /* 920 * First the files in dot 921 */ 922 DirMatchFiles(word, dot, expansions); 923 924 /* 925 * Then the files in every other directory on the path. 926 */ 927 DirExpandInt(word, path, expansions); 928 } 929 } 930 if (DEBUG(DIR)) { 931 Lst_ForEach(expansions, DirPrintWord, NULL); 932 fprintf(debug_file, "\n"); 933 } 934 } 935 936 /*- 937 *----------------------------------------------------------------------- 938 * DirLookup -- 939 * Find if the file with the given name exists in the given path. 940 * 941 * Results: 942 * The path to the file or NULL. This path is guaranteed to be in a 943 * different part of memory than name and so may be safely free'd. 944 * 945 * Side Effects: 946 * None. 947 *----------------------------------------------------------------------- 948 */ 949 static char * 950 DirLookup(Path *p, const char *name MAKE_ATTR_UNUSED, const char *cp, 951 Boolean hasSlash MAKE_ATTR_UNUSED) 952 { 953 char *file; /* the current filename to check */ 954 955 if (DEBUG(DIR)) { 956 fprintf(debug_file, " %s ...\n", p->name); 957 } 958 959 if (Hash_FindEntry(&p->files, cp) == NULL) 960 return NULL; 961 962 file = str_concat(p->name, cp, STR_ADDSLASH); 963 if (DEBUG(DIR)) { 964 fprintf(debug_file, " returning %s\n", file); 965 } 966 p->hits += 1; 967 hits += 1; 968 return file; 969 } 970 971 972 /*- 973 *----------------------------------------------------------------------- 974 * DirLookupSubdir -- 975 * Find if the file with the given name exists in the given path. 976 * 977 * Results: 978 * The path to the file or NULL. This path is guaranteed to be in a 979 * different part of memory than name and so may be safely free'd. 980 * 981 * Side Effects: 982 * If the file is found, it is added in the modification times hash 983 * table. 984 *----------------------------------------------------------------------- 985 */ 986 static char * 987 DirLookupSubdir(Path *p, const char *name) 988 { 989 struct stat stb; /* Buffer for stat, if necessary */ 990 char *file; /* the current filename to check */ 991 992 if (p != dot) { 993 file = str_concat(p->name, name, STR_ADDSLASH); 994 } else { 995 /* 996 * Checking in dot -- DON'T put a leading ./ on the thing. 997 */ 998 file = bmake_strdup(name); 999 } 1000 1001 if (DEBUG(DIR)) { 1002 fprintf(debug_file, "checking %s ...\n", file); 1003 } 1004 1005 if (cached_stat(file, &stb) == 0) { 1006 nearmisses += 1; 1007 return file; 1008 } 1009 free(file); 1010 return NULL; 1011 } 1012 1013 /*- 1014 *----------------------------------------------------------------------- 1015 * DirLookupAbs -- 1016 * Find if the file with the given name exists in the given path. 1017 * 1018 * Results: 1019 * The path to the file, the empty string or NULL. If the file is 1020 * the empty string, the search should be terminated. 1021 * This path is guaranteed to be in a different part of memory 1022 * than name and so may be safely free'd. 1023 * 1024 * Side Effects: 1025 * None. 1026 *----------------------------------------------------------------------- 1027 */ 1028 static char * 1029 DirLookupAbs(Path *p, const char *name, const char *cp) 1030 { 1031 char *p1; /* pointer into p->name */ 1032 const char *p2; /* pointer into name */ 1033 1034 if (DEBUG(DIR)) { 1035 fprintf(debug_file, " %s ...\n", p->name); 1036 } 1037 1038 /* 1039 * If the file has a leading path component and that component 1040 * exactly matches the entire name of the current search 1041 * directory, we can attempt another cache lookup. And if we don't 1042 * have a hit, we can safely assume the file does not exist at all. 1043 */ 1044 for (p1 = p->name, p2 = name; *p1 && *p1 == *p2; p1++, p2++) { 1045 continue; 1046 } 1047 if (*p1 != '\0' || p2 != cp - 1) { 1048 return NULL; 1049 } 1050 1051 if (Hash_FindEntry(&p->files, cp) == NULL) { 1052 if (DEBUG(DIR)) { 1053 fprintf(debug_file, " must be here but isn't -- returning\n"); 1054 } 1055 /* Return empty string: terminates search */ 1056 return bmake_strdup(""); 1057 } 1058 1059 p->hits += 1; 1060 hits += 1; 1061 if (DEBUG(DIR)) { 1062 fprintf(debug_file, " returning %s\n", name); 1063 } 1064 return bmake_strdup(name); 1065 } 1066 1067 /*- 1068 *----------------------------------------------------------------------- 1069 * DirFindDot -- 1070 * Find the file given on "." or curdir 1071 * 1072 * Results: 1073 * The path to the file or NULL. This path is guaranteed to be in a 1074 * different part of memory than name and so may be safely free'd. 1075 * 1076 * Side Effects: 1077 * Hit counts change 1078 *----------------------------------------------------------------------- 1079 */ 1080 static char * 1081 DirFindDot(Boolean hasSlash MAKE_ATTR_UNUSED, const char *name, const char *cp) 1082 { 1083 1084 if (Hash_FindEntry(&dot->files, cp) != NULL) { 1085 if (DEBUG(DIR)) { 1086 fprintf(debug_file, " in '.'\n"); 1087 } 1088 hits += 1; 1089 dot->hits += 1; 1090 return bmake_strdup(name); 1091 } 1092 if (cur && 1093 Hash_FindEntry(&cur->files, cp) != NULL) { 1094 if (DEBUG(DIR)) { 1095 fprintf(debug_file, " in ${.CURDIR} = %s\n", cur->name); 1096 } 1097 hits += 1; 1098 cur->hits += 1; 1099 return str_concat(cur->name, cp, STR_ADDSLASH); 1100 } 1101 1102 return NULL; 1103 } 1104 1105 /*- 1106 *----------------------------------------------------------------------- 1107 * Dir_FindFile -- 1108 * Find the file with the given name along the given search path. 1109 * 1110 * Input: 1111 * name the file to find 1112 * path the Lst of directories to search 1113 * 1114 * Results: 1115 * The path to the file or NULL. This path is guaranteed to be in a 1116 * different part of memory than name and so may be safely free'd. 1117 * 1118 * Side Effects: 1119 * If the file is found in a directory which is not on the path 1120 * already (either 'name' is absolute or it is a relative path 1121 * [ dir1/.../dirn/file ] which exists below one of the directories 1122 * already on the search path), its directory is added to the end 1123 * of the path on the assumption that there will be more files in 1124 * that directory later on. Sometimes this is true. Sometimes not. 1125 *----------------------------------------------------------------------- 1126 */ 1127 char * 1128 Dir_FindFile(const char *name, Lst path) 1129 { 1130 LstNode ln; /* a list element */ 1131 char *file; /* the current filename to check */ 1132 Path *p; /* current path member */ 1133 const char *cp; /* Terminal name of file */ 1134 Boolean hasLastDot = FALSE; /* true we should search dot last */ 1135 Boolean hasSlash; /* true if 'name' contains a / */ 1136 struct stat stb; /* Buffer for stat, if necessary */ 1137 const char *trailing_dot = "."; 1138 1139 /* 1140 * Find the final component of the name and note whether it has a 1141 * slash in it (the name, I mean) 1142 */ 1143 cp = strrchr(name, '/'); 1144 if (cp) { 1145 hasSlash = TRUE; 1146 cp += 1; 1147 } else { 1148 hasSlash = FALSE; 1149 cp = name; 1150 } 1151 1152 if (DEBUG(DIR)) { 1153 fprintf(debug_file, "Searching for %s ...", name); 1154 } 1155 1156 if (Lst_Open(path) == FAILURE) { 1157 if (DEBUG(DIR)) { 1158 fprintf(debug_file, "couldn't open path, file not found\n"); 1159 } 1160 misses += 1; 1161 return NULL; 1162 } 1163 1164 if ((ln = Lst_First(path)) != NULL) { 1165 p = (Path *)Lst_Datum(ln); 1166 if (p == dotLast) { 1167 hasLastDot = TRUE; 1168 if (DEBUG(DIR)) 1169 fprintf(debug_file, "[dot last]..."); 1170 } 1171 } 1172 if (DEBUG(DIR)) { 1173 fprintf(debug_file, "\n"); 1174 } 1175 1176 /* 1177 * If there's no leading directory components or if the leading 1178 * directory component is exactly `./', consult the cached contents 1179 * of each of the directories on the search path. 1180 */ 1181 if (!hasSlash || (cp - name == 2 && *name == '.')) { 1182 /* 1183 * We look through all the directories on the path seeking one which 1184 * contains the final component of the given name. If such a beast 1185 * is found, we concatenate the directory name and the final 1186 * component and return the resulting string. If we don't find any 1187 * such thing, we go on to phase two... 1188 * 1189 * No matter what, we always look for the file in the current 1190 * directory before anywhere else (unless we found the magic 1191 * DOTLAST path, in which case we search it last) and we *do not* 1192 * add the ./ to it if it exists. 1193 * This is so there are no conflicts between what the user 1194 * specifies (fish.c) and what pmake finds (./fish.c). 1195 */ 1196 if (!hasLastDot && 1197 (file = DirFindDot(hasSlash, name, cp)) != NULL) { 1198 Lst_Close(path); 1199 return file; 1200 } 1201 1202 while ((ln = Lst_Next(path)) != NULL) { 1203 p = (Path *)Lst_Datum(ln); 1204 if (p == dotLast) 1205 continue; 1206 if ((file = DirLookup(p, name, cp, hasSlash)) != NULL) { 1207 Lst_Close(path); 1208 return file; 1209 } 1210 } 1211 1212 if (hasLastDot && 1213 (file = DirFindDot(hasSlash, name, cp)) != NULL) { 1214 Lst_Close(path); 1215 return file; 1216 } 1217 } 1218 Lst_Close(path); 1219 1220 /* 1221 * We didn't find the file on any directory in the search path. 1222 * If the name doesn't contain a slash, that means it doesn't exist. 1223 * If it *does* contain a slash, however, there is still hope: it 1224 * could be in a subdirectory of one of the members of the search 1225 * path. (eg. /usr/include and sys/types.h. The above search would 1226 * fail to turn up types.h in /usr/include, but it *is* in 1227 * /usr/include/sys/types.h). 1228 * [ This no longer applies: If we find such a beast, we assume there 1229 * will be more (what else can we assume?) and add all but the last 1230 * component of the resulting name onto the search path (at the 1231 * end).] 1232 * This phase is only performed if the file is *not* absolute. 1233 */ 1234 if (!hasSlash) { 1235 if (DEBUG(DIR)) { 1236 fprintf(debug_file, " failed.\n"); 1237 } 1238 misses += 1; 1239 return NULL; 1240 } 1241 1242 if (*cp == '\0') { 1243 /* we were given a trailing "/" */ 1244 cp = trailing_dot; 1245 } 1246 1247 if (name[0] != '/') { 1248 Boolean checkedDot = FALSE; 1249 1250 if (DEBUG(DIR)) { 1251 fprintf(debug_file, " Trying subdirectories...\n"); 1252 } 1253 1254 if (!hasLastDot) { 1255 if (dot) { 1256 checkedDot = TRUE; 1257 if ((file = DirLookupSubdir(dot, name)) != NULL) 1258 return file; 1259 } 1260 if (cur && (file = DirLookupSubdir(cur, name)) != NULL) 1261 return file; 1262 } 1263 1264 (void)Lst_Open(path); 1265 while ((ln = Lst_Next(path)) != NULL) { 1266 p = (Path *)Lst_Datum(ln); 1267 if (p == dotLast) 1268 continue; 1269 if (p == dot) { 1270 if (checkedDot) 1271 continue; 1272 checkedDot = TRUE; 1273 } 1274 if ((file = DirLookupSubdir(p, name)) != NULL) { 1275 Lst_Close(path); 1276 return file; 1277 } 1278 } 1279 Lst_Close(path); 1280 1281 if (hasLastDot) { 1282 if (dot && !checkedDot) { 1283 checkedDot = TRUE; 1284 if ((file = DirLookupSubdir(dot, name)) != NULL) 1285 return file; 1286 } 1287 if (cur && (file = DirLookupSubdir(cur, name)) != NULL) 1288 return file; 1289 } 1290 1291 if (checkedDot) { 1292 /* 1293 * Already checked by the given name, since . was in the path, 1294 * so no point in proceeding... 1295 */ 1296 if (DEBUG(DIR)) { 1297 fprintf(debug_file, " Checked . already, returning NULL\n"); 1298 } 1299 return NULL; 1300 } 1301 1302 } else { /* name[0] == '/' */ 1303 1304 /* 1305 * For absolute names, compare directory path prefix against the 1306 * the directory path of each member on the search path for an exact 1307 * match. If we have an exact match on any member of the search path, 1308 * use the cached contents of that member to lookup the final file 1309 * component. If that lookup fails we can safely assume that the 1310 * file does not exist at all. This is signified by DirLookupAbs() 1311 * returning an empty string. 1312 */ 1313 if (DEBUG(DIR)) { 1314 fprintf(debug_file, " Trying exact path matches...\n"); 1315 } 1316 1317 if (!hasLastDot && cur && ((file = DirLookupAbs(cur, name, cp)) 1318 != NULL)) { 1319 if (file[0] == '\0') { 1320 free(file); 1321 return NULL; 1322 } 1323 return file; 1324 } 1325 1326 (void)Lst_Open(path); 1327 while ((ln = Lst_Next(path)) != NULL) { 1328 p = (Path *)Lst_Datum(ln); 1329 if (p == dotLast) 1330 continue; 1331 if ((file = DirLookupAbs(p, name, cp)) != NULL) { 1332 Lst_Close(path); 1333 if (file[0] == '\0') { 1334 free(file); 1335 return NULL; 1336 } 1337 return file; 1338 } 1339 } 1340 Lst_Close(path); 1341 1342 if (hasLastDot && cur && ((file = DirLookupAbs(cur, name, cp)) 1343 != NULL)) { 1344 if (file[0] == '\0') { 1345 free(file); 1346 return NULL; 1347 } 1348 return file; 1349 } 1350 } 1351 1352 /* 1353 * Didn't find it that way, either. Sigh. Phase 3. Add its directory 1354 * onto the search path in any case, just in case, then look for the 1355 * thing in the hash table. If we find it, grand. We return a new 1356 * copy of the name. Otherwise we sadly return a NULL pointer. Sigh. 1357 * Note that if the directory holding the file doesn't exist, this will 1358 * do an extra search of the final directory on the path. Unless something 1359 * weird happens, this search won't succeed and life will be groovy. 1360 * 1361 * Sigh. We cannot add the directory onto the search path because 1362 * of this amusing case: 1363 * $(INSTALLDIR)/$(FILE): $(FILE) 1364 * 1365 * $(FILE) exists in $(INSTALLDIR) but not in the current one. 1366 * When searching for $(FILE), we will find it in $(INSTALLDIR) 1367 * b/c we added it here. This is not good... 1368 */ 1369 #ifdef notdef 1370 if (cp == traling_dot) { 1371 cp = strrchr(name, '/'); 1372 cp += 1; 1373 } 1374 cp[-1] = '\0'; 1375 (void)Dir_AddDir(path, name); 1376 cp[-1] = '/'; 1377 1378 bigmisses += 1; 1379 ln = Lst_Last(path); 1380 if (ln == NULL) { 1381 return NULL; 1382 } else { 1383 p = (Path *)Lst_Datum(ln); 1384 } 1385 1386 if (Hash_FindEntry(&p->files, cp) != NULL) { 1387 return bmake_strdup(name); 1388 } else { 1389 return NULL; 1390 } 1391 #else /* !notdef */ 1392 if (DEBUG(DIR)) { 1393 fprintf(debug_file, " Looking for \"%s\" ...\n", name); 1394 } 1395 1396 bigmisses += 1; 1397 if (cached_stat(name, &stb) == 0) { 1398 return bmake_strdup(name); 1399 } 1400 1401 if (DEBUG(DIR)) { 1402 fprintf(debug_file, " failed. Returning NULL\n"); 1403 } 1404 return NULL; 1405 #endif /* notdef */ 1406 } 1407 1408 1409 /*- 1410 *----------------------------------------------------------------------- 1411 * Dir_FindHereOrAbove -- 1412 * search for a path starting at a given directory and then working 1413 * our way up towards the root. 1414 * 1415 * Input: 1416 * here starting directory 1417 * search_path the path we are looking for 1418 * result the result of a successful search is placed here 1419 * rlen the length of the result buffer 1420 * (typically MAXPATHLEN + 1) 1421 * 1422 * Results: 1423 * 0 on failure, 1 on success [in which case the found path is put 1424 * in the result buffer]. 1425 * 1426 * Side Effects: 1427 *----------------------------------------------------------------------- 1428 */ 1429 int 1430 Dir_FindHereOrAbove(char *here, char *search_path, char *result, int rlen) { 1431 1432 struct stat st; 1433 char dirbase[MAXPATHLEN + 1], *db_end; 1434 char try[MAXPATHLEN + 1], *try_end; 1435 1436 /* copy out our starting point */ 1437 snprintf(dirbase, sizeof(dirbase), "%s", here); 1438 db_end = dirbase + strlen(dirbase); 1439 1440 /* loop until we determine a result */ 1441 while (1) { 1442 1443 /* try and stat(2) it ... */ 1444 snprintf(try, sizeof(try), "%s/%s", dirbase, search_path); 1445 if (cached_stat(try, &st) != -1) { 1446 /* 1447 * success! if we found a file, chop off 1448 * the filename so we return a directory. 1449 */ 1450 if ((st.st_mode & S_IFMT) != S_IFDIR) { 1451 try_end = try + strlen(try); 1452 while (try_end > try && *try_end != '/') 1453 try_end--; 1454 if (try_end > try) 1455 *try_end = 0; /* chop! */ 1456 } 1457 1458 /* 1459 * done! 1460 */ 1461 snprintf(result, rlen, "%s", try); 1462 return 1; 1463 } 1464 1465 /* 1466 * nope, we didn't find it. if we used up dirbase we've 1467 * reached the root and failed. 1468 */ 1469 if (db_end == dirbase) 1470 break; /* failed! */ 1471 1472 /* 1473 * truncate dirbase from the end to move up a dir 1474 */ 1475 while (db_end > dirbase && *db_end != '/') 1476 db_end--; 1477 *db_end = 0; /* chop! */ 1478 1479 } /* while (1) */ 1480 1481 /* 1482 * we failed... 1483 */ 1484 return 0; 1485 } 1486 1487 /*- 1488 *----------------------------------------------------------------------- 1489 * Dir_MTime -- 1490 * Find the modification time of the file described by gn along the 1491 * search path dirSearchPath. 1492 * 1493 * Input: 1494 * gn the file whose modification time is desired 1495 * 1496 * Results: 1497 * The modification time or 0 if it doesn't exist 1498 * 1499 * Side Effects: 1500 * The modification time is placed in the node's mtime slot. 1501 * If the node didn't have a path entry before, and Dir_FindFile 1502 * found one for it, the full name is placed in the path slot. 1503 *----------------------------------------------------------------------- 1504 */ 1505 int 1506 Dir_MTime(GNode *gn, Boolean recheck) 1507 { 1508 char *fullName; /* the full pathname of name */ 1509 struct stat stb; /* buffer for finding the mod time */ 1510 1511 if (gn->type & OP_ARCHV) { 1512 return Arch_MTime(gn); 1513 } else if (gn->type & OP_PHONY) { 1514 gn->mtime = 0; 1515 return 0; 1516 } else if (gn->path == NULL) { 1517 if (gn->type & OP_NOPATH) 1518 fullName = NULL; 1519 else { 1520 fullName = Dir_FindFile(gn->name, Suff_FindPath(gn)); 1521 if (fullName == NULL && gn->flags & FROM_DEPEND && 1522 !Lst_IsEmpty(gn->iParents)) { 1523 char *cp; 1524 1525 cp = strrchr(gn->name, '/'); 1526 if (cp) { 1527 /* 1528 * This is an implied source, and it may have moved, 1529 * see if we can find it via the current .PATH 1530 */ 1531 cp++; 1532 1533 fullName = Dir_FindFile(cp, Suff_FindPath(gn)); 1534 if (fullName) { 1535 /* 1536 * Put the found file in gn->path 1537 * so that we give that to the compiler. 1538 */ 1539 gn->path = bmake_strdup(fullName); 1540 if (!Job_RunTarget(".STALE", gn->fname)) 1541 fprintf(stdout, 1542 "%s: %s, %d: ignoring stale %s for %s, " 1543 "found %s\n", progname, gn->fname, gn->lineno, 1544 makeDependfile, gn->name, fullName); 1545 } 1546 } 1547 } 1548 if (DEBUG(DIR)) 1549 fprintf(debug_file, "Found '%s' as '%s'\n", 1550 gn->name, fullName ? fullName : "(not found)" ); 1551 } 1552 } else { 1553 fullName = gn->path; 1554 } 1555 1556 if (fullName == NULL) { 1557 fullName = bmake_strdup(gn->name); 1558 } 1559 1560 if (cached_stats(&mtimes, fullName, &stb, recheck ? CST_UPDATE : 0) < 0) { 1561 if (gn->type & OP_MEMBER) { 1562 if (fullName != gn->path) 1563 free(fullName); 1564 return Arch_MemMTime(gn); 1565 } else { 1566 stb.st_mtime = 0; 1567 } 1568 } 1569 1570 if (fullName && gn->path == NULL) { 1571 gn->path = fullName; 1572 } 1573 1574 gn->mtime = stb.st_mtime; 1575 return gn->mtime; 1576 } 1577 1578 /*- 1579 *----------------------------------------------------------------------- 1580 * Dir_AddDir -- 1581 * Add the given name to the end of the given path. The order of 1582 * the arguments is backwards so ParseDoDependency can do a 1583 * Lst_ForEach of its list of paths... 1584 * 1585 * Input: 1586 * path the path to which the directory should be 1587 * added 1588 * name the name of the directory to add 1589 * 1590 * Results: 1591 * none 1592 * 1593 * Side Effects: 1594 * A structure is added to the list and the directory is 1595 * read and hashed. 1596 *----------------------------------------------------------------------- 1597 */ 1598 Path * 1599 Dir_AddDir(Lst path, const char *name) 1600 { 1601 LstNode ln = NULL; /* node in case Path structure is found */ 1602 Path *p = NULL; /* pointer to new Path structure */ 1603 DIR *d; /* for reading directory */ 1604 struct dirent *dp; /* entry in directory */ 1605 1606 if (strcmp(name, ".DOTLAST") == 0) { 1607 ln = Lst_Find(path, name, DirFindName); 1608 if (ln != NULL) 1609 return (Path *)Lst_Datum(ln); 1610 else { 1611 dotLast->refCount += 1; 1612 (void)Lst_AtFront(path, dotLast); 1613 } 1614 } 1615 1616 if (path) 1617 ln = Lst_Find(openDirectories, name, DirFindName); 1618 if (ln != NULL) { 1619 p = (Path *)Lst_Datum(ln); 1620 if (path && Lst_Member(path, p) == NULL) { 1621 p->refCount += 1; 1622 (void)Lst_AtEnd(path, p); 1623 } 1624 } else { 1625 if (DEBUG(DIR)) { 1626 fprintf(debug_file, "Caching %s ...", name); 1627 } 1628 1629 if ((d = opendir(name)) != NULL) { 1630 p = bmake_malloc(sizeof(Path)); 1631 p->name = bmake_strdup(name); 1632 p->hits = 0; 1633 p->refCount = 1; 1634 Hash_InitTable(&p->files, -1); 1635 1636 while ((dp = readdir(d)) != NULL) { 1637 #if defined(sun) && defined(d_ino) /* d_ino is a sunos4 #define for d_fileno */ 1638 /* 1639 * The sun directory library doesn't check for a 0 inode 1640 * (0-inode slots just take up space), so we have to do 1641 * it ourselves. 1642 */ 1643 if (dp->d_fileno == 0) { 1644 continue; 1645 } 1646 #endif /* sun && d_ino */ 1647 (void)Hash_CreateEntry(&p->files, dp->d_name, NULL); 1648 } 1649 (void)closedir(d); 1650 (void)Lst_AtEnd(openDirectories, p); 1651 if (path != NULL) 1652 (void)Lst_AtEnd(path, p); 1653 } 1654 if (DEBUG(DIR)) { 1655 fprintf(debug_file, "done\n"); 1656 } 1657 } 1658 return p; 1659 } 1660 1661 /*- 1662 *----------------------------------------------------------------------- 1663 * Dir_CopyDir -- 1664 * Callback function for duplicating a search path via Lst_Duplicate. 1665 * Ups the reference count for the directory. 1666 * 1667 * Results: 1668 * Returns the Path it was given. 1669 * 1670 * Side Effects: 1671 * The refCount of the path is incremented. 1672 * 1673 *----------------------------------------------------------------------- 1674 */ 1675 void * 1676 Dir_CopyDir(void *p) 1677 { 1678 ((Path *)p)->refCount += 1; 1679 1680 return p; 1681 } 1682 1683 /*- 1684 *----------------------------------------------------------------------- 1685 * Dir_MakeFlags -- 1686 * Make a string by taking all the directories in the given search 1687 * path and preceding them by the given flag. Used by the suffix 1688 * module to create variables for compilers based on suffix search 1689 * paths. 1690 * 1691 * Input: 1692 * flag flag which should precede each directory 1693 * path list of directories 1694 * 1695 * Results: 1696 * The string mentioned above. Note that there is no space between 1697 * the given flag and each directory. The empty string is returned if 1698 * Things don't go well. 1699 * 1700 * Side Effects: 1701 * None 1702 *----------------------------------------------------------------------- 1703 */ 1704 char * 1705 Dir_MakeFlags(const char *flag, Lst path) 1706 { 1707 char *str; /* the string which will be returned */ 1708 char *s1, *s2;/* the current directory preceded by 'flag' */ 1709 LstNode ln; /* the node of the current directory */ 1710 Path *p; /* the structure describing the current directory */ 1711 1712 str = bmake_strdup(""); 1713 1714 if (Lst_Open(path) == SUCCESS) { 1715 while ((ln = Lst_Next(path)) != NULL) { 1716 p = (Path *)Lst_Datum(ln); 1717 s2 = str_concat(flag, p->name, 0); 1718 str = str_concat(s1 = str, s2, STR_ADDSPACE); 1719 free(s1); 1720 free(s2); 1721 } 1722 Lst_Close(path); 1723 } 1724 1725 return str; 1726 } 1727 1728 /*- 1729 *----------------------------------------------------------------------- 1730 * Dir_Destroy -- 1731 * Nuke a directory descriptor, if possible. Callback procedure 1732 * for the suffixes module when destroying a search path. 1733 * 1734 * Input: 1735 * pp The directory descriptor to nuke 1736 * 1737 * Results: 1738 * None. 1739 * 1740 * Side Effects: 1741 * If no other path references this directory (refCount == 0), 1742 * the Path and all its data are freed. 1743 * 1744 *----------------------------------------------------------------------- 1745 */ 1746 void 1747 Dir_Destroy(void *pp) 1748 { 1749 Path *p = (Path *)pp; 1750 p->refCount -= 1; 1751 1752 if (p->refCount == 0) { 1753 LstNode ln; 1754 1755 ln = Lst_Member(openDirectories, p); 1756 (void)Lst_Remove(openDirectories, ln); 1757 1758 Hash_DeleteTable(&p->files); 1759 free(p->name); 1760 free(p); 1761 } 1762 } 1763 1764 /*- 1765 *----------------------------------------------------------------------- 1766 * Dir_ClearPath -- 1767 * Clear out all elements of the given search path. This is different 1768 * from destroying the list, notice. 1769 * 1770 * Input: 1771 * path Path to clear 1772 * 1773 * Results: 1774 * None. 1775 * 1776 * Side Effects: 1777 * The path is set to the empty list. 1778 * 1779 *----------------------------------------------------------------------- 1780 */ 1781 void 1782 Dir_ClearPath(Lst path) 1783 { 1784 Path *p; 1785 while (!Lst_IsEmpty(path)) { 1786 p = (Path *)Lst_DeQueue(path); 1787 Dir_Destroy(p); 1788 } 1789 } 1790 1791 1792 /*- 1793 *----------------------------------------------------------------------- 1794 * Dir_Concat -- 1795 * Concatenate two paths, adding the second to the end of the first. 1796 * Makes sure to avoid duplicates. 1797 * 1798 * Input: 1799 * path1 Dest 1800 * path2 Source 1801 * 1802 * Results: 1803 * None 1804 * 1805 * Side Effects: 1806 * Reference counts for added dirs are upped. 1807 * 1808 *----------------------------------------------------------------------- 1809 */ 1810 void 1811 Dir_Concat(Lst path1, Lst path2) 1812 { 1813 LstNode ln; 1814 Path *p; 1815 1816 for (ln = Lst_First(path2); ln != NULL; ln = Lst_Succ(ln)) { 1817 p = (Path *)Lst_Datum(ln); 1818 if (Lst_Member(path1, p) == NULL) { 1819 p->refCount += 1; 1820 (void)Lst_AtEnd(path1, p); 1821 } 1822 } 1823 } 1824 1825 /********** DEBUG INFO **********/ 1826 void 1827 Dir_PrintDirectories(void) 1828 { 1829 LstNode ln; 1830 Path *p; 1831 1832 fprintf(debug_file, "#*** Directory Cache:\n"); 1833 fprintf(debug_file, "# Stats: %d hits %d misses %d near misses %d losers (%d%%)\n", 1834 hits, misses, nearmisses, bigmisses, 1835 (hits+bigmisses+nearmisses ? 1836 hits * 100 / (hits + bigmisses + nearmisses) : 0)); 1837 fprintf(debug_file, "# %-20s referenced\thits\n", "directory"); 1838 if (Lst_Open(openDirectories) == SUCCESS) { 1839 while ((ln = Lst_Next(openDirectories)) != NULL) { 1840 p = (Path *)Lst_Datum(ln); 1841 fprintf(debug_file, "# %-20s %10d\t%4d\n", p->name, p->refCount, p->hits); 1842 } 1843 Lst_Close(openDirectories); 1844 } 1845 } 1846 1847 static int 1848 DirPrintDir(void *p, void *dummy MAKE_ATTR_UNUSED) 1849 { 1850 fprintf(debug_file, "%s ", ((Path *)p)->name); 1851 return 0; 1852 } 1853 1854 void 1855 Dir_PrintPath(Lst path) 1856 { 1857 Lst_ForEach(path, DirPrintDir, NULL); 1858 } 1859