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