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