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