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