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