1 /* $NetBSD: dir.c,v 1.20 1997/09/28 03:31:02 lukem Exp $ */ 2 3 /* 4 * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. 5 * Copyright (c) 1988, 1989 by Adam de Boor 6 * Copyright (c) 1989 by Berkeley Softworks 7 * All rights reserved. 8 * 9 * This code is derived from software contributed to Berkeley by 10 * Adam de Boor. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 3. All advertising materials mentioning features or use of this software 21 * must display the following acknowledgement: 22 * This product includes software developed by the University of 23 * California, Berkeley and its contributors. 24 * 4. Neither the name of the University nor the names of its contributors 25 * may be used to endorse or promote products derived from this software 26 * without specific prior written permission. 27 * 28 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 29 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 31 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 32 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 36 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 37 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 38 * SUCH DAMAGE. 39 */ 40 41 #ifdef MAKE_BOOTSTRAP 42 static char rcsid[] = "$NetBSD: dir.c,v 1.20 1997/09/28 03:31:02 lukem Exp $"; 43 #else 44 #include <sys/cdefs.h> 45 #ifndef lint 46 #if 0 47 static char sccsid[] = "@(#)dir.c 8.2 (Berkeley) 1/2/94"; 48 #else 49 __RCSID("$NetBSD: dir.c,v 1.20 1997/09/28 03:31:02 lukem Exp $"); 50 #endif 51 #endif /* not lint */ 52 #endif 53 54 /*- 55 * dir.c -- 56 * Directory searching using wildcards and/or normal names... 57 * Used both for source wildcarding in the Makefile and for finding 58 * implicit sources. 59 * 60 * The interface for this module is: 61 * Dir_Init Initialize the module. 62 * 63 * Dir_End Cleanup the module. 64 * 65 * Dir_HasWildcards Returns TRUE if the name given it needs to 66 * be wildcard-expanded. 67 * 68 * Dir_Expand Given a pattern and a path, return a Lst of names 69 * which match the pattern on the search path. 70 * 71 * Dir_FindFile Searches for a file on a given search path. 72 * If it exists, the entire path is returned. 73 * Otherwise NULL is returned. 74 * 75 * Dir_MTime Return the modification time of a node. The file 76 * is searched for along the default search path. 77 * The path and mtime fields of the node are filled 78 * in. 79 * 80 * Dir_AddDir Add a directory to a search path. 81 * 82 * Dir_MakeFlags Given a search path and a command flag, create 83 * a string with each of the directories in the path 84 * preceded by the command flag and all of them 85 * separated by a space. 86 * 87 * Dir_Destroy Destroy an element of a search path. Frees up all 88 * things that can be freed for the element as long 89 * as the element is no longer referenced by any other 90 * search path. 91 * Dir_ClearPath Resets a search path to the empty list. 92 * 93 * For debugging: 94 * Dir_PrintDirectories Print stats about the directory cache. 95 */ 96 97 #include <stdio.h> 98 #include <sys/types.h> 99 #include <dirent.h> 100 #include <sys/stat.h> 101 #include "make.h" 102 #include "hash.h" 103 #include "dir.h" 104 105 /* 106 * A search path consists of a Lst of Path structures. A Path structure 107 * has in it the name of the directory and a hash table of all the files 108 * in the directory. This is used to cut down on the number of system 109 * calls necessary to find implicit dependents and their like. Since 110 * these searches are made before any actions are taken, we need not 111 * worry about the directory changing due to creation commands. If this 112 * hampers the style of some makefiles, they must be changed. 113 * 114 * A list of all previously-read directories is kept in the 115 * openDirectories Lst. This list is checked first before a directory 116 * is opened. 117 * 118 * The need for the caching of whole directories is brought about by 119 * the multi-level transformation code in suff.c, which tends to search 120 * for far more files than regular make does. In the initial 121 * implementation, the amount of time spent performing "stat" calls was 122 * truly astronomical. The problem with hashing at the start is, 123 * of course, that pmake doesn't then detect changes to these directories 124 * during the course of the make. Three possibilities suggest themselves: 125 * 126 * 1) just use stat to test for a file's existence. As mentioned 127 * above, this is very inefficient due to the number of checks 128 * engendered by the multi-level transformation code. 129 * 2) use readdir() and company to search the directories, keeping 130 * them open between checks. I have tried this and while it 131 * didn't slow down the process too much, it could severely 132 * affect the amount of parallelism available as each directory 133 * open would take another file descriptor out of play for 134 * handling I/O for another job. Given that it is only recently 135 * that UNIX OS's have taken to allowing more than 20 or 32 136 * file descriptors for a process, this doesn't seem acceptable 137 * to me. 138 * 3) record the mtime of the directory in the Path structure and 139 * verify the directory hasn't changed since the contents were 140 * hashed. This will catch the creation or deletion of files, 141 * but not the updating of files. However, since it is the 142 * creation and deletion that is the problem, this could be 143 * a good thing to do. Unfortunately, if the directory (say ".") 144 * were fairly large and changed fairly frequently, the constant 145 * rehashing could seriously degrade performance. It might be 146 * good in such cases to keep track of the number of rehashes 147 * and if the number goes over a (small) limit, resort to using 148 * stat in its place. 149 * 150 * An additional thing to consider is that pmake is used primarily 151 * to create C programs and until recently pcc-based compilers refused 152 * to allow you to specify where the resulting object file should be 153 * placed. This forced all objects to be created in the current 154 * directory. This isn't meant as a full excuse, just an explanation of 155 * some of the reasons for the caching used here. 156 * 157 * One more note: the location of a target's file is only performed 158 * on the downward traversal of the graph and then only for terminal 159 * nodes in the graph. This could be construed as wrong in some cases, 160 * but prevents inadvertent modification of files when the "installed" 161 * directory for a file is provided in the search path. 162 * 163 * Another data structure maintained by this module is an mtime 164 * cache used when the searching of cached directories fails to find 165 * a file. In the past, Dir_FindFile would simply perform an access() 166 * call in such a case to determine if the file could be found using 167 * just the name given. When this hit, however, all that was gained 168 * was the knowledge that the file existed. Given that an access() is 169 * essentially a stat() without the copyout() call, and that the same 170 * filesystem overhead would have to be incurred in Dir_MTime, it made 171 * sense to replace the access() with a stat() and record the mtime 172 * in a cache for when Dir_MTime was actually called. 173 */ 174 175 Lst dirSearchPath; /* main search path */ 176 177 static Lst openDirectories; /* the list of all open directories */ 178 179 /* 180 * Variables for gathering statistics on the efficiency of the hashing 181 * mechanism. 182 */ 183 static int hits, /* Found in directory cache */ 184 misses, /* Sad, but not evil misses */ 185 nearmisses, /* Found under search path */ 186 bigmisses; /* Sought by itself */ 187 188 static Path *dot; /* contents of current directory */ 189 static Path *cur; /* contents of current directory, if not dot */ 190 static Hash_Table mtimes; /* Results of doing a last-resort stat in 191 * Dir_FindFile -- if we have to go to the 192 * system to find the file, we might as well 193 * have its mtime on record. XXX: If this is done 194 * way early, there's a chance other rules will 195 * have already updated the file, in which case 196 * we'll update it again. Generally, there won't 197 * be two rules to update a single file, so this 198 * should be ok, but... */ 199 200 201 static int DirFindName __P((ClientData, ClientData)); 202 static int DirMatchFiles __P((char *, Path *, Lst)); 203 static void DirExpandCurly __P((char *, char *, Lst, Lst)); 204 static void DirExpandInt __P((char *, Lst, Lst)); 205 static int DirPrintWord __P((ClientData, ClientData)); 206 static int DirPrintDir __P((ClientData, ClientData)); 207 static char *DirLookup __P((Path *, char *, char *, Boolean)); 208 static char *DirLookupSubdir __P((Path *, char *)); 209 210 /*- 211 *----------------------------------------------------------------------- 212 * Dir_Init -- 213 * initialize things for this module 214 * 215 * Results: 216 * none 217 * 218 * Side Effects: 219 * some directories may be opened. 220 *----------------------------------------------------------------------- 221 */ 222 void 223 Dir_Init (cdname) 224 const char *cdname; 225 { 226 dirSearchPath = Lst_Init (FALSE); 227 openDirectories = Lst_Init (FALSE); 228 Hash_InitTable(&mtimes, 0); 229 230 /* 231 * Since the Path structure is placed on both openDirectories and 232 * the path we give Dir_AddDir (which in this case is openDirectories), 233 * we need to remove "." from openDirectories and what better time to 234 * do it than when we have to fetch the thing anyway? 235 */ 236 dot = Dir_AddDir (NULL, "."); 237 238 /* 239 * We always need to have dot around, so we increment its reference count 240 * to make sure it's not destroyed. 241 */ 242 dot->refCount += 1; 243 244 if (cdname != NULL) { 245 /* 246 * Our build directory is not the same as our source directory. 247 * Keep this one around too. 248 */ 249 cur = Dir_AddDir (NULL, cdname); 250 cur->refCount += 1; 251 } 252 } 253 254 /*- 255 *----------------------------------------------------------------------- 256 * Dir_End -- 257 * cleanup things for this module 258 * 259 * Results: 260 * none 261 * 262 * Side Effects: 263 * none 264 *----------------------------------------------------------------------- 265 */ 266 void 267 Dir_End() 268 { 269 if (cur) { 270 cur->refCount -= 1; 271 Dir_Destroy((ClientData) cur); 272 } 273 dot->refCount -= 1; 274 Dir_Destroy((ClientData) dot); 275 Dir_ClearPath(dirSearchPath); 276 Lst_Destroy(dirSearchPath, NOFREE); 277 Dir_ClearPath(openDirectories); 278 Lst_Destroy(openDirectories, NOFREE); 279 Hash_DeleteTable(&mtimes); 280 } 281 282 /*- 283 *----------------------------------------------------------------------- 284 * DirFindName -- 285 * See if the Path structure describes the same directory as the 286 * given one by comparing their names. Called from Dir_AddDir via 287 * Lst_Find when searching the list of open directories. 288 * 289 * Results: 290 * 0 if it is the same. Non-zero otherwise 291 * 292 * Side Effects: 293 * None 294 *----------------------------------------------------------------------- 295 */ 296 static int 297 DirFindName (p, dname) 298 ClientData p; /* Current name */ 299 ClientData dname; /* Desired name */ 300 { 301 return (strcmp (((Path *)p)->name, (char *) dname)); 302 } 303 304 /*- 305 *----------------------------------------------------------------------- 306 * Dir_HasWildcards -- 307 * see if the given name has any wildcard characters in it 308 * be careful not to expand unmatching brackets or braces. 309 * XXX: This code is not 100% correct. ([^]] fails etc.) 310 * I really don't think that make(1) should be expanding 311 * patterns, because then you have to set a mechanism for 312 * escaping the expansion! 313 * 314 * Results: 315 * returns TRUE if the word should be expanded, FALSE otherwise 316 * 317 * Side Effects: 318 * none 319 *----------------------------------------------------------------------- 320 */ 321 Boolean 322 Dir_HasWildcards (name) 323 char *name; /* name to check */ 324 { 325 register char *cp; 326 int wild = 0, brace = 0, bracket = 0; 327 328 for (cp = name; *cp; cp++) { 329 switch(*cp) { 330 case '{': 331 brace++; 332 wild = 1; 333 break; 334 case '}': 335 brace--; 336 break; 337 case '[': 338 bracket++; 339 wild = 1; 340 break; 341 case ']': 342 bracket--; 343 break; 344 case '?': 345 case '*': 346 wild = 1; 347 break; 348 default: 349 break; 350 } 351 } 352 return wild && bracket == 0 && brace == 0; 353 } 354 355 /*- 356 *----------------------------------------------------------------------- 357 * DirMatchFiles -- 358 * Given a pattern and a Path structure, see if any files 359 * match the pattern and add their names to the 'expansions' list if 360 * any do. This is incomplete -- it doesn't take care of patterns like 361 * src / *src / *.c properly (just *.c on any of the directories), but it 362 * will do for now. 363 * 364 * Results: 365 * Always returns 0 366 * 367 * Side Effects: 368 * File names are added to the expansions lst. The directory will be 369 * fully hashed when this is done. 370 *----------------------------------------------------------------------- 371 */ 372 static int 373 DirMatchFiles (pattern, p, expansions) 374 char *pattern; /* Pattern to look for */ 375 Path *p; /* Directory to search */ 376 Lst expansions; /* Place to store the results */ 377 { 378 Hash_Search search; /* Index into the directory's table */ 379 Hash_Entry *entry; /* Current entry in the table */ 380 Boolean isDot; /* TRUE if the directory being searched is . */ 381 382 isDot = (*p->name == '.' && p->name[1] == '\0'); 383 384 for (entry = Hash_EnumFirst(&p->files, &search); 385 entry != (Hash_Entry *)NULL; 386 entry = Hash_EnumNext(&search)) 387 { 388 /* 389 * See if the file matches the given pattern. Note we follow the UNIX 390 * convention that dot files will only be found if the pattern 391 * begins with a dot (note also that as a side effect of the hashing 392 * scheme, .* won't match . or .. since they aren't hashed). 393 */ 394 if (Str_Match(entry->name, pattern) && 395 ((entry->name[0] != '.') || 396 (pattern[0] == '.'))) 397 { 398 (void)Lst_AtEnd(expansions, 399 (isDot ? estrdup(entry->name) : 400 str_concat(p->name, entry->name, 401 STR_ADDSLASH))); 402 } 403 } 404 return (0); 405 } 406 407 /*- 408 *----------------------------------------------------------------------- 409 * DirExpandCurly -- 410 * Expand curly braces like the C shell. Does this recursively. 411 * Note the special case: if after the piece of the curly brace is 412 * done there are no wildcard characters in the result, the result is 413 * placed on the list WITHOUT CHECKING FOR ITS EXISTENCE. 414 * 415 * Results: 416 * None. 417 * 418 * Side Effects: 419 * The given list is filled with the expansions... 420 * 421 *----------------------------------------------------------------------- 422 */ 423 static void 424 DirExpandCurly(word, brace, path, expansions) 425 char *word; /* Entire word to expand */ 426 char *brace; /* First curly brace in it */ 427 Lst path; /* Search path to use */ 428 Lst expansions; /* Place to store the expansions */ 429 { 430 char *end; /* Character after the closing brace */ 431 char *cp; /* Current position in brace clause */ 432 char *start; /* Start of current piece of brace clause */ 433 int bracelevel; /* Number of braces we've seen. If we see a 434 * right brace when this is 0, we've hit the 435 * end of the clause. */ 436 char *file; /* Current expansion */ 437 int otherLen; /* The length of the other pieces of the 438 * expansion (chars before and after the 439 * clause in 'word') */ 440 char *cp2; /* Pointer for checking for wildcards in 441 * expansion before calling Dir_Expand */ 442 443 start = brace+1; 444 445 /* 446 * Find the end of the brace clause first, being wary of nested brace 447 * clauses. 448 */ 449 for (end = start, bracelevel = 0; *end != '\0'; end++) { 450 if (*end == '{') { 451 bracelevel++; 452 } else if ((*end == '}') && (bracelevel-- == 0)) { 453 break; 454 } 455 } 456 if (*end == '\0') { 457 Error("Unterminated {} clause \"%s\"", start); 458 return; 459 } else { 460 end++; 461 } 462 otherLen = brace - word + strlen(end); 463 464 for (cp = start; cp < end; cp++) { 465 /* 466 * Find the end of this piece of the clause. 467 */ 468 bracelevel = 0; 469 while (*cp != ',') { 470 if (*cp == '{') { 471 bracelevel++; 472 } else if ((*cp == '}') && (bracelevel-- <= 0)) { 473 break; 474 } 475 cp++; 476 } 477 /* 478 * Allocate room for the combination and install the three pieces. 479 */ 480 file = emalloc(otherLen + cp - start + 1); 481 if (brace != word) { 482 strncpy(file, word, brace-word); 483 } 484 if (cp != start) { 485 strncpy(&file[brace-word], start, cp-start); 486 } 487 strcpy(&file[(brace-word)+(cp-start)], end); 488 489 /* 490 * See if the result has any wildcards in it. If we find one, call 491 * Dir_Expand right away, telling it to place the result on our list 492 * of expansions. 493 */ 494 for (cp2 = file; *cp2 != '\0'; cp2++) { 495 switch(*cp2) { 496 case '*': 497 case '?': 498 case '{': 499 case '[': 500 Dir_Expand(file, path, expansions); 501 goto next; 502 } 503 } 504 if (*cp2 == '\0') { 505 /* 506 * Hit the end w/o finding any wildcards, so stick the expansion 507 * on the end of the list. 508 */ 509 (void)Lst_AtEnd(expansions, file); 510 } else { 511 next: 512 free(file); 513 } 514 start = cp+1; 515 } 516 } 517 518 519 /*- 520 *----------------------------------------------------------------------- 521 * DirExpandInt -- 522 * Internal expand routine. Passes through the directories in the 523 * path one by one, calling DirMatchFiles for each. NOTE: This still 524 * doesn't handle patterns in directories... 525 * 526 * Results: 527 * None. 528 * 529 * Side Effects: 530 * Things are added to the expansions list. 531 * 532 *----------------------------------------------------------------------- 533 */ 534 static void 535 DirExpandInt(word, path, expansions) 536 char *word; /* Word to expand */ 537 Lst path; /* Path on which to look */ 538 Lst expansions; /* Place to store the result */ 539 { 540 LstNode ln; /* Current node */ 541 Path *p; /* Directory in the node */ 542 543 if (Lst_Open(path) == SUCCESS) { 544 while ((ln = Lst_Next(path)) != NILLNODE) { 545 p = (Path *)Lst_Datum(ln); 546 DirMatchFiles(word, p, expansions); 547 } 548 Lst_Close(path); 549 } 550 } 551 552 /*- 553 *----------------------------------------------------------------------- 554 * DirPrintWord -- 555 * Print a word in the list of expansions. Callback for Dir_Expand 556 * when DEBUG(DIR), via Lst_ForEach. 557 * 558 * Results: 559 * === 0 560 * 561 * Side Effects: 562 * The passed word is printed, followed by a space. 563 * 564 *----------------------------------------------------------------------- 565 */ 566 static int 567 DirPrintWord(word, dummy) 568 ClientData word; 569 ClientData dummy; 570 { 571 printf("%s ", (char *) word); 572 573 return(dummy ? 0 : 0); 574 } 575 576 /*- 577 *----------------------------------------------------------------------- 578 * Dir_Expand -- 579 * Expand the given word into a list of words by globbing it looking 580 * in the directories on the given search path. 581 * 582 * Results: 583 * A list of words consisting of the files which exist along the search 584 * path matching the given pattern. 585 * 586 * Side Effects: 587 * Directories may be opened. Who knows? 588 *----------------------------------------------------------------------- 589 */ 590 void 591 Dir_Expand (word, path, expansions) 592 char *word; /* the word to expand */ 593 Lst path; /* the list of directories in which to find 594 * the resulting files */ 595 Lst expansions; /* the list on which to place the results */ 596 { 597 char *cp; 598 599 if (DEBUG(DIR)) { 600 printf("expanding \"%s\"...", word); 601 } 602 603 cp = strchr(word, '{'); 604 if (cp) { 605 DirExpandCurly(word, cp, path, expansions); 606 } else { 607 cp = strchr(word, '/'); 608 if (cp) { 609 /* 610 * The thing has a directory component -- find the first wildcard 611 * in the string. 612 */ 613 for (cp = word; *cp; cp++) { 614 if (*cp == '?' || *cp == '[' || *cp == '*' || *cp == '{') { 615 break; 616 } 617 } 618 if (*cp == '{') { 619 /* 620 * This one will be fun. 621 */ 622 DirExpandCurly(word, cp, path, expansions); 623 return; 624 } else if (*cp != '\0') { 625 /* 626 * Back up to the start of the component 627 */ 628 char *dirpath; 629 630 while (cp > word && *cp != '/') { 631 cp--; 632 } 633 if (cp != word) { 634 char sc; 635 /* 636 * If the glob isn't in the first component, try and find 637 * all the components up to the one with a wildcard. 638 */ 639 sc = cp[1]; 640 cp[1] = '\0'; 641 dirpath = Dir_FindFile(word, path); 642 cp[1] = sc; 643 /* 644 * dirpath is null if can't find the leading component 645 * XXX: Dir_FindFile won't find internal components. 646 * i.e. if the path contains ../Etc/Object and we're 647 * looking for Etc, it won't be found. Ah well. 648 * Probably not important. 649 */ 650 if (dirpath != (char *)NULL) { 651 char *dp = &dirpath[strlen(dirpath) - 1]; 652 if (*dp == '/') 653 *dp = '\0'; 654 path = Lst_Init(FALSE); 655 (void) Dir_AddDir(path, dirpath); 656 DirExpandInt(cp+1, path, expansions); 657 Lst_Destroy(path, NOFREE); 658 } 659 } else { 660 /* 661 * Start the search from the local directory 662 */ 663 DirExpandInt(word, path, expansions); 664 } 665 } else { 666 /* 667 * Return the file -- this should never happen. 668 */ 669 DirExpandInt(word, path, expansions); 670 } 671 } else { 672 /* 673 * First the files in dot 674 */ 675 DirMatchFiles(word, dot, expansions); 676 677 /* 678 * Then the files in every other directory on the path. 679 */ 680 DirExpandInt(word, path, expansions); 681 } 682 } 683 if (DEBUG(DIR)) { 684 Lst_ForEach(expansions, DirPrintWord, (ClientData) 0); 685 fputc('\n', stdout); 686 } 687 } 688 689 /*- 690 *----------------------------------------------------------------------- 691 * DirLookup -- 692 * Find if the file with the given name exists in the given path. 693 * 694 * Results: 695 * The path to the file, the empty string or NULL. If the file is 696 * the empty string, the search should be terminated. 697 * This path is guaranteed to be in a 698 * different part of memory than name and so may be safely free'd. 699 * 700 * Side Effects: 701 * None. 702 *----------------------------------------------------------------------- 703 */ 704 static char * 705 DirLookup(p, name, cp, hasSlash) 706 Path *p; 707 char *name; 708 char *cp; 709 Boolean hasSlash; 710 { 711 char *p1; /* pointer into p->name */ 712 char *p2; /* pointer into name */ 713 char *file; /* the current filename to check */ 714 715 if (DEBUG(DIR)) { 716 printf("%s...", p->name); 717 } 718 if (Hash_FindEntry (&p->files, cp) != (Hash_Entry *)NULL) { 719 if (DEBUG(DIR)) { 720 printf("here..."); 721 } 722 if (hasSlash) { 723 /* 724 * If the name had a slash, its initial components and p's 725 * final components must match. This is false if a mismatch 726 * is encountered before all of the initial components 727 * have been checked (p2 > name at the end of the loop), or 728 * we matched only part of one of the components of p 729 * along with all the rest of them (*p1 != '/'). 730 */ 731 p1 = p->name + strlen (p->name) - 1; 732 p2 = cp - 2; 733 while (p2 >= name && p1 >= p->name && *p1 == *p2) { 734 p1 -= 1; p2 -= 1; 735 } 736 if (p2 >= name || (p1 >= p->name && *p1 != '/')) { 737 if (DEBUG(DIR)) { 738 printf("component mismatch -- continuing..."); 739 } 740 return NULL; 741 } 742 } 743 file = str_concat (p->name, cp, STR_ADDSLASH); 744 if (DEBUG(DIR)) { 745 printf("returning %s\n", file); 746 } 747 p->hits += 1; 748 hits += 1; 749 return file; 750 } else if (hasSlash) { 751 /* 752 * If the file has a leading path component and that component 753 * exactly matches the entire name of the current search 754 * directory, we assume the file doesn't exist and return NULL. 755 */ 756 for (p1 = p->name, p2 = name; *p1 && *p1 == *p2; p1++, p2++) { 757 continue; 758 } 759 if (*p1 == '\0' && p2 == cp - 1) { 760 if (DEBUG(DIR)) { 761 printf("must be here but isn't -- returing\n"); 762 } 763 return ""; 764 } 765 } 766 return NULL; 767 } 768 769 770 /*- 771 *----------------------------------------------------------------------- 772 * DirLookupSubdir -- 773 * Find if the file with the given name exists in the given path. 774 * 775 * Results: 776 * The path to the file or NULL. This path is guaranteed to be in a 777 * different part of memory than name and so may be safely free'd. 778 * 779 * Side Effects: 780 * If the file is found, it is added in the modification times hash 781 * table. 782 *----------------------------------------------------------------------- 783 */ 784 static char * 785 DirLookupSubdir(p, name) 786 Path *p; 787 char *name; 788 { 789 struct stat stb; /* Buffer for stat, if necessary */ 790 Hash_Entry *entry; /* Entry for mtimes table */ 791 char *file; /* the current filename to check */ 792 793 if (p != dot) { 794 file = str_concat (p->name, name, STR_ADDSLASH); 795 } else { 796 /* 797 * Checking in dot -- DON'T put a leading ./ on the thing. 798 */ 799 file = estrdup(name); 800 } 801 802 if (DEBUG(DIR)) { 803 printf("checking %s...", file); 804 } 805 806 if (stat (file, &stb) == 0) { 807 if (DEBUG(DIR)) { 808 printf("got it.\n"); 809 } 810 811 /* 812 * Save the modification time so if it's needed, we don't have 813 * to fetch it again. 814 */ 815 if (DEBUG(DIR)) { 816 printf("Caching %s for %s\n", Targ_FmtTime(stb.st_mtime), 817 file); 818 } 819 entry = Hash_CreateEntry(&mtimes, (char *) file, 820 (Boolean *)NULL); 821 Hash_SetValue(entry, (long)stb.st_mtime); 822 nearmisses += 1; 823 return (file); 824 } 825 free (file); 826 return NULL; 827 } 828 829 /*- 830 *----------------------------------------------------------------------- 831 * Dir_FindFile -- 832 * Find the file with the given name along the given search path. 833 * 834 * Results: 835 * The path to the file or NULL. This path is guaranteed to be in a 836 * different part of memory than name and so may be safely free'd. 837 * 838 * Side Effects: 839 * If the file is found in a directory which is not on the path 840 * already (either 'name' is absolute or it is a relative path 841 * [ dir1/.../dirn/file ] which exists below one of the directories 842 * already on the search path), its directory is added to the end 843 * of the path on the assumption that there will be more files in 844 * that directory later on. Sometimes this is true. Sometimes not. 845 *----------------------------------------------------------------------- 846 */ 847 char * 848 Dir_FindFile (name, path) 849 char *name; /* the file to find */ 850 Lst path; /* the Lst of directories to search */ 851 { 852 LstNode ln; /* a list element */ 853 register char *file; /* the current filename to check */ 854 register Path *p; /* current path member */ 855 register char *cp; /* index of first slash, if any */ 856 Boolean hasSlash; /* true if 'name' contains a / */ 857 struct stat stb; /* Buffer for stat, if necessary */ 858 Hash_Entry *entry; /* Entry for mtimes table */ 859 860 /* 861 * Find the final component of the name and note whether it has a 862 * slash in it (the name, I mean) 863 */ 864 cp = strrchr (name, '/'); 865 if (cp) { 866 hasSlash = TRUE; 867 cp += 1; 868 } else { 869 hasSlash = FALSE; 870 cp = name; 871 } 872 873 if (DEBUG(DIR)) { 874 printf("Searching for %s...", name); 875 } 876 /* 877 * No matter what, we always look for the file in the current directory 878 * before anywhere else and we *do not* add the ./ to it if it exists. 879 * This is so there are no conflicts between what the user specifies 880 * (fish.c) and what pmake finds (./fish.c). 881 */ 882 if ((!hasSlash || (cp - name == 2 && *name == '.'))) { 883 if (Hash_FindEntry (&dot->files, cp) != (Hash_Entry *)NULL) { 884 if (DEBUG(DIR)) { 885 printf("in '.'\n"); 886 } 887 hits += 1; 888 dot->hits += 1; 889 return (estrdup (name)); 890 } 891 if (cur && 892 Hash_FindEntry (&cur->files, cp) != (Hash_Entry *)NULL) { 893 if (DEBUG(DIR)) { 894 printf("in ${.CURDIR} = %s\n", cur->name); 895 } 896 hits += 1; 897 cur->hits += 1; 898 return str_concat (cur->name, cp, STR_ADDSLASH); 899 } 900 } 901 902 if (Lst_Open (path) == FAILURE) { 903 if (DEBUG(DIR)) { 904 printf("couldn't open path, file not found\n"); 905 } 906 misses += 1; 907 return ((char *) NULL); 908 } 909 910 if (cur && (file = DirLookup(cur, name, cp, hasSlash)) != NULL) { 911 if (*file) 912 return file; 913 else 914 return NULL; 915 } 916 917 /* 918 * We look through all the directories on the path seeking one which 919 * contains the final component of the given name and whose final 920 * component(s) match the name's initial component(s). If such a beast 921 * is found, we concatenate the directory name and the final component 922 * and return the resulting string. If we don't find any such thing, 923 * we go on to phase two... 924 */ 925 while ((ln = Lst_Next (path)) != NILLNODE) { 926 p = (Path *) Lst_Datum (ln); 927 if ((file = DirLookup(p, name, cp, hasSlash)) != NULL) { 928 Lst_Close (path); 929 if (*file) 930 return file; 931 else 932 return NULL; 933 } 934 } 935 936 /* 937 * We didn't find the file on any existing members of the directory. 938 * If the name doesn't contain a slash, that means it doesn't exist. 939 * If it *does* contain a slash, however, there is still hope: it 940 * could be in a subdirectory of one of the members of the search 941 * path. (eg. /usr/include and sys/types.h. The above search would 942 * fail to turn up types.h in /usr/include, but it *is* in 943 * /usr/include/sys/types.h) If we find such a beast, we assume there 944 * will be more (what else can we assume?) and add all but the last 945 * component of the resulting name onto the search path (at the 946 * end). This phase is only performed if the file is *not* absolute. 947 */ 948 if (!hasSlash) { 949 if (DEBUG(DIR)) { 950 printf("failed.\n"); 951 } 952 misses += 1; 953 return ((char *) NULL); 954 } 955 956 if (*name != '/') { 957 Boolean checkedDot = FALSE; 958 959 if (DEBUG(DIR)) { 960 printf("failed. Trying subdirectories..."); 961 } 962 963 if (cur && (file = DirLookupSubdir(cur, name)) != NULL) 964 return file; 965 966 (void) Lst_Open (path); 967 while ((ln = Lst_Next (path)) != NILLNODE) { 968 p = (Path *) Lst_Datum (ln); 969 if (p == dot) 970 checkedDot = TRUE; 971 if ((file = DirLookupSubdir(p, name)) != NULL) { 972 Lst_Close (path); 973 return file; 974 } 975 } 976 977 if (DEBUG(DIR)) { 978 printf("failed. "); 979 } 980 Lst_Close (path); 981 982 if (checkedDot) { 983 /* 984 * Already checked by the given name, since . was in the path, 985 * so no point in proceeding... 986 */ 987 if (DEBUG(DIR)) { 988 printf("Checked . already, returning NULL\n"); 989 } 990 return(NULL); 991 } 992 } 993 994 /* 995 * Didn't find it that way, either. Sigh. Phase 3. Add its directory 996 * onto the search path in any case, just in case, then look for the 997 * thing in the hash table. If we find it, grand. We return a new 998 * copy of the name. Otherwise we sadly return a NULL pointer. Sigh. 999 * Note that if the directory holding the file doesn't exist, this will 1000 * do an extra search of the final directory on the path. Unless something 1001 * weird happens, this search won't succeed and life will be groovy. 1002 * 1003 * Sigh. We cannot add the directory onto the search path because 1004 * of this amusing case: 1005 * $(INSTALLDIR)/$(FILE): $(FILE) 1006 * 1007 * $(FILE) exists in $(INSTALLDIR) but not in the current one. 1008 * When searching for $(FILE), we will find it in $(INSTALLDIR) 1009 * b/c we added it here. This is not good... 1010 */ 1011 #ifdef notdef 1012 cp[-1] = '\0'; 1013 (void) Dir_AddDir (path, name); 1014 cp[-1] = '/'; 1015 1016 bigmisses += 1; 1017 ln = Lst_Last (path); 1018 if (ln == NILLNODE) { 1019 return ((char *) NULL); 1020 } else { 1021 p = (Path *) Lst_Datum (ln); 1022 } 1023 1024 if (Hash_FindEntry (&p->files, cp) != (Hash_Entry *)NULL) { 1025 return (estrdup (name)); 1026 } else { 1027 return ((char *) NULL); 1028 } 1029 #else /* !notdef */ 1030 if (DEBUG(DIR)) { 1031 printf("Looking for \"%s\"...", name); 1032 } 1033 1034 bigmisses += 1; 1035 entry = Hash_FindEntry(&mtimes, name); 1036 if (entry != (Hash_Entry *)NULL) { 1037 if (DEBUG(DIR)) { 1038 printf("got it (in mtime cache)\n"); 1039 } 1040 return(estrdup(name)); 1041 } else if (stat (name, &stb) == 0) { 1042 entry = Hash_CreateEntry(&mtimes, name, (Boolean *)NULL); 1043 if (DEBUG(DIR)) { 1044 printf("Caching %s for %s\n", Targ_FmtTime(stb.st_mtime), 1045 name); 1046 } 1047 Hash_SetValue(entry, (long)stb.st_mtime); 1048 return (estrdup (name)); 1049 } else { 1050 if (DEBUG(DIR)) { 1051 printf("failed. Returning NULL\n"); 1052 } 1053 return ((char *)NULL); 1054 } 1055 #endif /* notdef */ 1056 } 1057 1058 /*- 1059 *----------------------------------------------------------------------- 1060 * Dir_MTime -- 1061 * Find the modification time of the file described by gn along the 1062 * search path dirSearchPath. 1063 * 1064 * Results: 1065 * The modification time or 0 if it doesn't exist 1066 * 1067 * Side Effects: 1068 * The modification time is placed in the node's mtime slot. 1069 * If the node didn't have a path entry before, and Dir_FindFile 1070 * found one for it, the full name is placed in the path slot. 1071 *----------------------------------------------------------------------- 1072 */ 1073 int 1074 Dir_MTime (gn) 1075 GNode *gn; /* the file whose modification time is 1076 * desired */ 1077 { 1078 char *fullName; /* the full pathname of name */ 1079 struct stat stb; /* buffer for finding the mod time */ 1080 Hash_Entry *entry; 1081 1082 if (gn->type & OP_ARCHV) { 1083 return Arch_MTime (gn); 1084 } else if (gn->path == (char *)NULL) { 1085 if (gn->type & (OP_PHONY|OP_NOPATH)) 1086 fullName = NULL; 1087 else 1088 fullName = Dir_FindFile (gn->name, dirSearchPath); 1089 } else { 1090 fullName = gn->path; 1091 } 1092 1093 if (fullName == (char *)NULL) { 1094 fullName = estrdup(gn->name); 1095 } 1096 1097 entry = Hash_FindEntry(&mtimes, fullName); 1098 if (entry != (Hash_Entry *)NULL) { 1099 /* 1100 * Only do this once -- the second time folks are checking to 1101 * see if the file was actually updated, so we need to actually go 1102 * to the file system. 1103 */ 1104 if (DEBUG(DIR)) { 1105 printf("Using cached time %s for %s\n", 1106 Targ_FmtTime((time_t)(long)Hash_GetValue(entry)), fullName); 1107 } 1108 stb.st_mtime = (time_t)(long)Hash_GetValue(entry); 1109 Hash_DeleteEntry(&mtimes, entry); 1110 } else if (stat (fullName, &stb) < 0) { 1111 if (gn->type & OP_MEMBER) { 1112 if (fullName != gn->path) 1113 free(fullName); 1114 return Arch_MemMTime (gn); 1115 } else { 1116 stb.st_mtime = 0; 1117 } 1118 } 1119 if (fullName && gn->path == (char *)NULL) { 1120 gn->path = fullName; 1121 } 1122 1123 gn->mtime = stb.st_mtime; 1124 return (gn->mtime); 1125 } 1126 1127 /*- 1128 *----------------------------------------------------------------------- 1129 * Dir_AddDir -- 1130 * Add the given name to the end of the given path. The order of 1131 * the arguments is backwards so ParseDoDependency can do a 1132 * Lst_ForEach of its list of paths... 1133 * 1134 * Results: 1135 * none 1136 * 1137 * Side Effects: 1138 * A structure is added to the list and the directory is 1139 * read and hashed. 1140 *----------------------------------------------------------------------- 1141 */ 1142 Path * 1143 Dir_AddDir (path, name) 1144 Lst path; /* the path to which the directory should be 1145 * added */ 1146 const char *name; /* the name of the directory to add */ 1147 { 1148 LstNode ln; /* node in case Path structure is found */ 1149 register Path *p = NULL; /* pointer to new Path structure */ 1150 DIR *d; /* for reading directory */ 1151 register struct dirent *dp; /* entry in directory */ 1152 1153 ln = Lst_Find (openDirectories, (ClientData)name, DirFindName); 1154 if (ln != NILLNODE) { 1155 p = (Path *)Lst_Datum (ln); 1156 if (Lst_Member(path, (ClientData)p) == NILLNODE) { 1157 p->refCount += 1; 1158 (void)Lst_AtEnd (path, (ClientData)p); 1159 } 1160 } else { 1161 if (DEBUG(DIR)) { 1162 printf("Caching %s...", name); 1163 fflush(stdout); 1164 } 1165 1166 if ((d = opendir (name)) != (DIR *) NULL) { 1167 p = (Path *) emalloc (sizeof (Path)); 1168 p->name = estrdup (name); 1169 p->hits = 0; 1170 p->refCount = 1; 1171 Hash_InitTable (&p->files, -1); 1172 1173 /* 1174 * Skip the first two entries -- these will *always* be . and .. 1175 */ 1176 (void)readdir(d); 1177 (void)readdir(d); 1178 1179 while ((dp = readdir (d)) != (struct dirent *) NULL) { 1180 #if defined(sun) && defined(d_ino) /* d_ino is a sunos4 #define for d_fileno */ 1181 /* 1182 * The sun directory library doesn't check for a 0 inode 1183 * (0-inode slots just take up space), so we have to do 1184 * it ourselves. 1185 */ 1186 if (dp->d_fileno == 0) { 1187 continue; 1188 } 1189 #endif /* sun && d_ino */ 1190 (void)Hash_CreateEntry(&p->files, dp->d_name, (Boolean *)NULL); 1191 } 1192 (void) closedir (d); 1193 (void)Lst_AtEnd (openDirectories, (ClientData)p); 1194 if (path != NULL) 1195 (void)Lst_AtEnd (path, (ClientData)p); 1196 } 1197 if (DEBUG(DIR)) { 1198 printf("done\n"); 1199 } 1200 } 1201 return p; 1202 } 1203 1204 /*- 1205 *----------------------------------------------------------------------- 1206 * Dir_CopyDir -- 1207 * Callback function for duplicating a search path via Lst_Duplicate. 1208 * Ups the reference count for the directory. 1209 * 1210 * Results: 1211 * Returns the Path it was given. 1212 * 1213 * Side Effects: 1214 * The refCount of the path is incremented. 1215 * 1216 *----------------------------------------------------------------------- 1217 */ 1218 ClientData 1219 Dir_CopyDir(p) 1220 ClientData p; 1221 { 1222 ((Path *) p)->refCount += 1; 1223 1224 return ((ClientData)p); 1225 } 1226 1227 /*- 1228 *----------------------------------------------------------------------- 1229 * Dir_MakeFlags -- 1230 * Make a string by taking all the directories in the given search 1231 * path and preceding them by the given flag. Used by the suffix 1232 * module to create variables for compilers based on suffix search 1233 * paths. 1234 * 1235 * Results: 1236 * The string mentioned above. Note that there is no space between 1237 * the given flag and each directory. The empty string is returned if 1238 * Things don't go well. 1239 * 1240 * Side Effects: 1241 * None 1242 *----------------------------------------------------------------------- 1243 */ 1244 char * 1245 Dir_MakeFlags (flag, path) 1246 char *flag; /* flag which should precede each directory */ 1247 Lst path; /* list of directories */ 1248 { 1249 char *str; /* the string which will be returned */ 1250 char *tstr; /* the current directory preceded by 'flag' */ 1251 LstNode ln; /* the node of the current directory */ 1252 Path *p; /* the structure describing the current directory */ 1253 1254 str = estrdup (""); 1255 1256 if (Lst_Open (path) == SUCCESS) { 1257 while ((ln = Lst_Next (path)) != NILLNODE) { 1258 p = (Path *) Lst_Datum (ln); 1259 tstr = str_concat (flag, p->name, 0); 1260 str = str_concat (str, tstr, STR_ADDSPACE | STR_DOFREE); 1261 } 1262 Lst_Close (path); 1263 } 1264 1265 return (str); 1266 } 1267 1268 /*- 1269 *----------------------------------------------------------------------- 1270 * Dir_Destroy -- 1271 * Nuke a directory descriptor, if possible. Callback procedure 1272 * for the suffixes module when destroying a search path. 1273 * 1274 * Results: 1275 * None. 1276 * 1277 * Side Effects: 1278 * If no other path references this directory (refCount == 0), 1279 * the Path and all its data are freed. 1280 * 1281 *----------------------------------------------------------------------- 1282 */ 1283 void 1284 Dir_Destroy (pp) 1285 ClientData pp; /* The directory descriptor to nuke */ 1286 { 1287 Path *p = (Path *) pp; 1288 p->refCount -= 1; 1289 1290 if (p->refCount == 0) { 1291 LstNode ln; 1292 1293 ln = Lst_Member (openDirectories, (ClientData)p); 1294 (void) Lst_Remove (openDirectories, ln); 1295 1296 Hash_DeleteTable (&p->files); 1297 free((Address)p->name); 1298 free((Address)p); 1299 } 1300 } 1301 1302 /*- 1303 *----------------------------------------------------------------------- 1304 * Dir_ClearPath -- 1305 * Clear out all elements of the given search path. This is different 1306 * from destroying the list, notice. 1307 * 1308 * Results: 1309 * None. 1310 * 1311 * Side Effects: 1312 * The path is set to the empty list. 1313 * 1314 *----------------------------------------------------------------------- 1315 */ 1316 void 1317 Dir_ClearPath(path) 1318 Lst path; /* Path to clear */ 1319 { 1320 Path *p; 1321 while (!Lst_IsEmpty(path)) { 1322 p = (Path *)Lst_DeQueue(path); 1323 Dir_Destroy((ClientData) p); 1324 } 1325 } 1326 1327 1328 /*- 1329 *----------------------------------------------------------------------- 1330 * Dir_Concat -- 1331 * Concatenate two paths, adding the second to the end of the first. 1332 * Makes sure to avoid duplicates. 1333 * 1334 * Results: 1335 * None 1336 * 1337 * Side Effects: 1338 * Reference counts for added dirs are upped. 1339 * 1340 *----------------------------------------------------------------------- 1341 */ 1342 void 1343 Dir_Concat(path1, path2) 1344 Lst path1; /* Dest */ 1345 Lst path2; /* Source */ 1346 { 1347 LstNode ln; 1348 Path *p; 1349 1350 for (ln = Lst_First(path2); ln != NILLNODE; ln = Lst_Succ(ln)) { 1351 p = (Path *)Lst_Datum(ln); 1352 if (Lst_Member(path1, (ClientData)p) == NILLNODE) { 1353 p->refCount += 1; 1354 (void)Lst_AtEnd(path1, (ClientData)p); 1355 } 1356 } 1357 } 1358 1359 /********** DEBUG INFO **********/ 1360 void 1361 Dir_PrintDirectories() 1362 { 1363 LstNode ln; 1364 Path *p; 1365 1366 printf ("#*** Directory Cache:\n"); 1367 printf ("# Stats: %d hits %d misses %d near misses %d losers (%d%%)\n", 1368 hits, misses, nearmisses, bigmisses, 1369 (hits+bigmisses+nearmisses ? 1370 hits * 100 / (hits + bigmisses + nearmisses) : 0)); 1371 printf ("# %-20s referenced\thits\n", "directory"); 1372 if (Lst_Open (openDirectories) == SUCCESS) { 1373 while ((ln = Lst_Next (openDirectories)) != NILLNODE) { 1374 p = (Path *) Lst_Datum (ln); 1375 printf ("# %-20s %10d\t%4d\n", p->name, p->refCount, p->hits); 1376 } 1377 Lst_Close (openDirectories); 1378 } 1379 } 1380 1381 static int DirPrintDir (p, dummy) 1382 ClientData p; 1383 ClientData dummy; 1384 { 1385 printf ("%s ", ((Path *) p)->name); 1386 return (dummy ? 0 : 0); 1387 } 1388 1389 void 1390 Dir_PrintPath (path) 1391 Lst path; 1392 { 1393 Lst_ForEach (path, DirPrintDir, (ClientData)0); 1394 } 1395