1/* $OpenBSD: dir.c,v 1.69 2023/09/04 11:35:11 espie Exp $ */ 2/* $NetBSD: dir.c,v 1.14 1997/03/29 16:51:26 christos Exp $ */ 3 4/* 5 * Copyright (c) 1999 Marc Espie. 6 * 7 * Extensive code changes for the OpenBSD project. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE OPENBSD PROJECT AND CONTRIBUTORS 19 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OPENBSD 22 * PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30/* 31 * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. 32 * Copyright (c) 1988, 1989 by Adam de Boor 33 * Copyright (c) 1989 by Berkeley Softworks 34 * All rights reserved. 35 * 36 * This code is derived from software contributed to Berkeley by 37 * Adam de Boor. 38 * 39 * Redistribution and use in source and binary forms, with or without 40 * modification, are permitted provided that the following conditions 41 * are met: 42 * 1. Redistributions of source code must retain the above copyright 43 * notice, this list of conditions and the following disclaimer. 44 * 2. Redistributions in binary form must reproduce the above copyright 45 * notice, this list of conditions and the following disclaimer in the 46 * documentation and/or other materials provided with the distribution. 47 * 3. Neither the name of the University nor the names of its contributors 48 * may be used to endorse or promote products derived from this software 49 * without specific prior written permission. 50 * 51 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 52 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 53 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 54 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 55 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 56 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 57 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 58 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 59 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 60 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 61 * SUCH DAMAGE. 62 */ 63 64#include <sys/stat.h> 65#include <dirent.h> 66#include <limits.h> 67#include <stddef.h> 68#include <stdint.h> 69#include <stdio.h> 70#include <stdlib.h> 71#include <string.h> 72#include <ohash.h> 73#include "defines.h" 74#include "dir.h" 75#include "lst.h" 76#include "memory.h" 77#include "buf.h" 78#include "gnode.h" 79#include "arch.h" 80#include "targ.h" 81#include "error.h" 82#include "str.h" 83#include "timestamp.h" 84 85 86/* A search path consists of a Lst of PathEntry structures. A Path 87 * structure has in it the name of the directory and a hash table of all 88 * the files in the directory. This is used to cut down on the number of 89 * system calls necessary to find implicit dependents and their like. 90 * Since these searches are made before any actions are taken, we need not 91 * worry about the directory changing due to creation commands. If this 92 * hampers the style of some makefiles, they must be changed. 93 * 94 * A list of all previously-read directories is kept in the 95 * knownDirectories cache. 96 * 97 * The need for the caching of whole directories is brought about by 98 * the multi-level transformation code in suff.c, which tends to search 99 * for far more files than regular make does. In the initial 100 * implementation, the amount of time spent performing "stat" calls was 101 * truly astronomical. The problem with hashing at the start is, 102 * of course, that pmake doesn't then detect changes to these directories 103 * during the course of the make. Three possibilities suggest themselves: 104 * 105 * 1) just use stat to test for a file's existence. As mentioned 106 * above, this is very inefficient due to the number of checks 107 * engendered by the multi-level transformation code. 108 * 2) use readdir() and company to search the directories, keeping 109 * them open between checks. I have tried this and while it 110 * didn't slow down the process too much, it could severely 111 * affect the amount of parallelism available as each directory 112 * open would take another file descriptor out of play for 113 * handling I/O for another job. Given that it is only recently 114 * that UNIX OS's have taken to allowing more than 20 or 32 115 * file descriptors for a process, this doesn't seem acceptable 116 * to me. 117 * 3) record the mtime of the directory in the PathEntry structure and 118 * verify the directory hasn't changed since the contents were 119 * hashed. This will catch the creation or deletion of files, 120 * but not the updating of files. However, since it is the 121 * creation and deletion that is the problem, this could be 122 * a good thing to do. Unfortunately, if the directory (say ".") 123 * were fairly large and changed fairly frequently, the constant 124 * rehashing could seriously degrade performance. It might be 125 * good in such cases to keep track of the number of rehashes 126 * and if the number goes over a (small) limit, resort to using 127 * stat in its place. 128 * 129 * An additional thing to consider is that pmake is used primarily 130 * to create C programs and until recently pcc-based compilers refused 131 * to allow you to specify where the resulting object file should be 132 * placed. This forced all objects to be created in the current 133 * directory. This isn't meant as a full excuse, just an explanation of 134 * some of the reasons for the caching used here. 135 * 136 * One more note: the location of a target's file is only performed 137 * on the downward traversal of the graph and then only for terminal 138 * nodes in the graph. This could be construed as wrong in some cases, 139 * but prevents inadvertent modification of files when the "installed" 140 * directory for a file is provided in the search path. 141 * 142 * Another data structure maintained by this module is an mtime 143 * cache used when the searching of cached directories fails to find 144 * a file. In the past, Dir_FindFile would simply perform an access() 145 * call in such a case to determine if the file could be found using 146 * just the name given. When this hit, however, all that was gained 147 * was the knowledge that the file existed. Given that an access() is 148 * essentially a stat() without the copyout() call, and that the same 149 * filesystem overhead would have to be incurred in Dir_MTime, it made 150 * sense to replace the access() with a stat() and record the mtime 151 * in a cache for when Dir_MTime was actually called. */ 152 153 154/* several data structures exist to handle caching of directory stuff. 155 * 156 * There is a global hash of directory names (knownDirectories), and each 157 * read directory is kept there as one PathEntry instance. Such a structure 158 * only contains the file names. 159 * 160 * There is a global hash of timestamps (modification times), so care must 161 * be taken of giving the right file names to that structure. 162 * 163 * XXX A set of similar structure should exist at the Target level to properly 164 * take care of VPATH issues. 165 */ 166 167 168/* each directory is cached into a PathEntry structure. */ 169struct PathEntry { 170 int refCount; /* ref-counted, can participate to 171 * several paths */ 172 struct ohash files; /* hash of name of files in the directory */ 173 char name[1]; /* directory name */ 174}; 175 176/* PathEntry kept on knownDirectories */ 177static struct ohash_info dir_info = { 178 offsetof(struct PathEntry, name), NULL, hash_calloc, hash_free, 179 element_alloc 180}; 181 182static struct ohash knownDirectories; /* cache all open directories */ 183 184 185/* file names kept in a path entry */ 186static struct ohash_info file_info = { 187 0, NULL, hash_calloc, hash_free, element_alloc 188}; 189 190 191/* Global structure used to cache mtimes. XXX We don't cache an mtime 192 * before a caller actually looks up for the given time, because of the 193 * possibility a caller might update the file and invalidate the cache 194 * entry, and we don't look up in this cache except as a last resort. 195 */ 196struct file_stamp { 197 struct timespec mtime; /* time stamp... */ 198 char name[1]; /* ...for that file. */ 199}; 200 201static struct ohash mtimes; 202 203 204static struct ohash_info stamp_info = { 205 offsetof(struct file_stamp, name), NULL, hash_calloc, hash_free, 206 element_alloc 207}; 208 209 210 211static LIST theDefaultPath; /* main search path */ 212Lst defaultPath= &theDefaultPath; 213struct PathEntry *dot; /* contents of current directory */ 214 215 216 217/* add_file(path, name): add a file name to a path hash structure. */ 218static void add_file(struct PathEntry *, const char *); 219/* n = find_file_hashi(p, name, end, hv): retrieve name in a path hash 220 * structure. */ 221static char *find_file_hashi(struct PathEntry *, const char *, const char *, 222 uint32_t); 223 224/* stamp = find_stampi(name, end): look for (name, end) in the global 225 * cache. */ 226static struct file_stamp *find_stampi(const char *, const char *); 227/* record_stamp(name, timestamp): record timestamp for name in the global 228 * cache. */ 229static void record_stamp(const char *, struct timespec); 230 231static bool read_directory(struct PathEntry *); 232/* p = DirReaddiri(name, end): read an actual directory, caching results 233 * as we go. */ 234static struct PathEntry *create_PathEntry(const char *, const char *); 235/* Debugging: show a dir name in a path. */ 236static void DirPrintDir(void *); 237 238/*** 239 *** timestamp handling 240 ***/ 241 242static void 243record_stamp(const char *file, struct timespec t) 244{ 245 unsigned int slot; 246 const char *end = NULL; 247 struct file_stamp *n; 248 249 slot = ohash_qlookupi(&mtimes, file, &end); 250 n = ohash_find(&mtimes, slot); 251 if (n) 252 n->mtime = t; 253 else { 254 n = ohash_create_entry(&stamp_info, file, &end); 255 n->mtime = t; 256 ohash_insert(&mtimes, slot, n); 257 } 258} 259 260static struct file_stamp * 261find_stampi(const char *file, const char *efile) 262{ 263 return ohash_find(&mtimes, ohash_qlookupi(&mtimes, file, &efile)); 264} 265 266/*** 267 *** PathEntry handling 268 ***/ 269 270static void 271add_file(struct PathEntry *p, const char *file) 272{ 273 unsigned int slot; 274 const char *end = NULL; 275 char *n; 276 struct ohash *h = &p->files; 277 278 slot = ohash_qlookupi(h, file, &end); 279 n = ohash_find(h, slot); 280 if (n == NULL) { 281 n = ohash_create_entry(&file_info, file, &end); 282 ohash_insert(h, slot, n); 283 } 284} 285 286static char * 287find_file_hashi(struct PathEntry *p, const char *file, const char *efile, 288 uint32_t hv) 289{ 290 struct ohash *h = &p->files; 291 292 return ohash_find(h, ohash_lookup_interval(h, file, efile, hv)); 293} 294 295static bool 296read_directory(struct PathEntry *p) 297{ 298 DIR *d; 299 struct dirent *dp; 300 301 if (DEBUG(DIR)) { 302 printf("Caching %s...", p->name); 303 fflush(stdout); 304 } 305 306 if ((d = opendir(p->name)) == NULL) 307 return false; 308 309 ohash_init(&p->files, 4, &file_info); 310 311 while ((dp = readdir(d)) != NULL) { 312 if (dp->d_name[0] == '.' && 313 (dp->d_name[1] == '\0' || 314 (dp->d_name[1] == '.' && dp->d_name[2] == '\0'))) 315 continue; 316 add_file(p, dp->d_name); 317 } 318 (void)closedir(d); 319 if (DEBUG(DIR)) 320 printf("done\n"); 321 return true; 322} 323 324/* Read a directory, either from the disk, or from the cache. */ 325static struct PathEntry * 326create_PathEntry(const char *name, const char *ename) 327{ 328 struct PathEntry *p; 329 unsigned int slot; 330 331 slot = ohash_qlookupi(&knownDirectories, name, &ename); 332 p = ohash_find(&knownDirectories, slot); 333 334 if (p == NULL) { 335 p = ohash_create_entry(&dir_info, name, &ename); 336 p->refCount = 0; 337 if (!read_directory(p)) { 338 free(p); 339 return NULL; 340 } 341 ohash_insert(&knownDirectories, slot, p); 342 } 343 p->refCount++; 344 return p; 345} 346 347char * 348PathEntry_name(struct PathEntry *p) 349{ 350 return p->name; 351} 352 353/* Side Effects: cache the current directory */ 354void 355Dir_Init(void) 356{ 357 char *dotname = "."; 358 359 Static_Lst_Init(defaultPath); 360 ohash_init(&knownDirectories, 4, &dir_info); 361 ohash_init(&mtimes, 4, &stamp_info); 362 363 364 dot = create_PathEntry(dotname, dotname+1); 365 366 if (!dot) 367 Fatal("Can't access current directory"); 368} 369 370/*- 371 *----------------------------------------------------------------------- 372 * Dir_MatchFilesi -- 373 * Given a pattern and a PathEntry structure, see if any files 374 * match the pattern and add their names to the 'expansions' list if 375 * any do. This is incomplete -- it doesn't take care of patterns like 376 * src / *src / *.c properly (just *.c on any of the directories), but it 377 * will do for now. 378 *----------------------------------------------------------------------- 379 */ 380void 381Dir_MatchFilesi(const char *word, const char *eword, struct PathEntry *p, 382 Lst expansions) 383{ 384 unsigned int search; /* Index into the directory's table */ 385 const char *entry; /* Current entry in the table */ 386 387 for (entry = ohash_first(&p->files, &search); entry != NULL; 388 entry = ohash_next(&p->files, &search)) { 389 /* See if the file matches the given pattern. We follow the UNIX 390 * convention that dot files will only be found if the pattern 391 * begins with a dot (the hashing scheme doesn't hash . or .., 392 * so they won't match `.*'. */ 393 if (*word != '.' && *entry == '.') 394 continue; 395 if (Str_Matchi(entry, strchr(entry, '\0'), word, eword)) 396 Lst_AtEnd(expansions, 397 p == dot ? estrdup(entry) : 398 Str_concat(p->name, entry, '/')); 399 } 400} 401 402/*- 403 * Side Effects: 404 * If the file is found in a directory which is not on the path 405 * already (either 'name' is absolute or it is a relative path 406 * [ dir1/.../dirn/file ] which exists below one of the directories 407 * already on the search path), its directory is added to the end 408 * of the path on the assumption that there will be more files in 409 * that directory later on. 410 */ 411char * 412Dir_FindFileComplexi(const char *name, const char *ename, Lst path, 413 bool checkCurdirFirst) 414{ 415 struct PathEntry *p; /* current path member */ 416 char *p1; /* pointer into p->name */ 417 const char *p2; /* pointer into name */ 418 LstNode ln; /* a list element */ 419 char *file; /* the current filename to check */ 420 char *temp; /* index into file */ 421 const char *basename; 422 bool hasSlash; 423 struct stat stb;/* Buffer for stat, if necessary */ 424 struct file_stamp *entry; 425 /* Entry for mtimes table */ 426 uint32_t hv; /* hash value for last component in file name */ 427 char *q; /* Str_dupi(name, ename) */ 428 429 /* Find the final component of the name and note whether name has a 430 * slash in it */ 431 basename = Str_rchri(name, ename, '/'); 432 if (basename) { 433 hasSlash = true; 434 basename++; 435 } else { 436 hasSlash = false; 437 basename = name; 438 } 439 440 hv = ohash_interval(basename, &ename); 441 442 if (DEBUG(DIR)) 443 printf("Searching for %s...", name); 444 /* Unless checkCurDirFirst is false, we always look for 445 * the file in the current directory before anywhere else 446 * and we always return exactly what the caller specified. */ 447 if (checkCurdirFirst && 448 (!hasSlash || (basename - name == 2 && *name == '.')) && 449 find_file_hashi(dot, basename, ename, hv) != NULL) { 450 if (DEBUG(DIR)) 451 printf("in '.'\n"); 452 return Str_dupi(name, ename); 453 } 454 455 /* Then, we look through all the directories on path, seeking one 456 * containing the final component of name and whose final 457 * component(s) match name's initial component(s). 458 * If found, we concatenate the directory name and the 459 * final component and return the resulting string. */ 460 for (ln = Lst_First(path); ln != NULL; ln = Lst_Adv(ln)) { 461 p = Lst_Datum(ln); 462 if (DEBUG(DIR)) 463 printf("%s...", p->name); 464 if (find_file_hashi(p, basename, ename, hv) != NULL) { 465 if (DEBUG(DIR)) 466 printf("here..."); 467 if (hasSlash) { 468 /* If the name had a slash, its initial 469 * components and p's final components must 470 * match. This is false if a mismatch is 471 * encountered before all of the initial 472 * components have been checked (p2 > name at 473 * the end of the loop), or we matched only 474 * part of one of the components of p along 475 * with all the rest of them (*p1 != '/'). */ 476 p1 = p->name + strlen(p->name) - 1; 477 p2 = basename - 2; 478 while (p2 >= name && p1 >= p->name && 479 *p1 == *p2) { 480 p1--; 481 p2--; 482 } 483 if (p2 >= name || 484 (p1 >= p->name && *p1 != '/')) { 485 if (DEBUG(DIR)) 486 printf("component mismatch -- continuing..."); 487 continue; 488 } 489 } 490 file = Str_concati(p->name, strchr(p->name, '\0'), basename, 491 ename, '/'); 492 if (DEBUG(DIR)) 493 printf("returning %s\n", file); 494 return file; 495 } else if (hasSlash) { 496 /* If the file has a leading path component and that 497 * component exactly matches the entire name of the 498 * current search directory, we assume the file 499 * doesn't exist and return NULL. */ 500 for (p1 = p->name, p2 = name; *p1 && *p1 == *p2; 501 p1++, p2++) 502 continue; 503 if (*p1 == '\0' && p2 == basename - 1) { 504 if (DEBUG(DIR)) 505 printf("has to be here but isn't -- returning NULL\n"); 506 return NULL; 507 } 508 } 509 } 510 511 /* We didn't find the file on any existing member of the path. 512 * If the name doesn't contain a slash, end of story. 513 * If it does contain a slash, however, it could be in a subdirectory 514 * of one of the members of the search path. (eg., for path=/usr/include 515 * and name=sys/types.h, the above search fails to turn up types.h 516 * in /usr/include, even though /usr/include/sys/types.h exists). 517 * 518 * We only perform this look-up for non-absolute file names. 519 * 520 * Whenever we score a hit, we assume there will be more matches from 521 * that directory, and append all but the last component of the 522 * resulting name onto the search path. */ 523 if (!hasSlash) { 524 if (DEBUG(DIR)) 525 printf("failed.\n"); 526 return NULL; 527 } 528 529 if (*name != '/') { 530 bool checkedDot = false; 531 532 if (DEBUG(DIR)) 533 printf("failed. Trying subdirectories..."); 534 for (ln = Lst_First(path); ln != NULL; ln = Lst_Adv(ln)) { 535 p = Lst_Datum(ln); 536 if (p != dot) 537 file = Str_concati(p->name, 538 strchr(p->name, '\0'), name, ename, '/'); 539 else { 540 /* Checking in dot -- DON'T put a leading 541 * ./ on the thing. */ 542 file = Str_dupi(name, ename); 543 checkedDot = true; 544 } 545 if (DEBUG(DIR)) 546 printf("checking %s...", file); 547 548 if (stat(file, &stb) == 0) { 549 struct timespec mtime; 550 551 ts_set_from_stat(stb, mtime); 552 if (DEBUG(DIR)) 553 printf("got it.\n"); 554 555 /* We've found another directory to search. 556 * We know there is a slash in 'file'. We 557 * call Dir_AddDiri to add the new directory 558 * onto the existing search path. Once that's 559 * done, we return the file name, knowing that 560 * should a file in this directory ever be 561 * referenced again in such a manner, we will 562 * find it without having to do numerous 563 * access calls. */ 564 temp = strrchr(file, '/'); 565 Dir_AddDiri(path, file, temp); 566 567 /* Save the modification time so if it's 568 * needed, we don't have to fetch it again. */ 569 if (DEBUG(DIR)) 570 printf("Caching %s for %s\n", 571 time_to_string(&mtime), file); 572 record_stamp(file, mtime); 573 return file; 574 } else 575 free(file); 576 } 577 578 if (DEBUG(DIR)) 579 printf("failed. "); 580 581 if (checkedDot) { 582 /* Already checked by the given name, since . was in 583 * the path, so no point in proceeding... */ 584 if (DEBUG(DIR)) 585 printf("Checked . already, returning NULL\n"); 586 return NULL; 587 } 588 } 589 590 /* Didn't find it that way, either. Last resort: look for the file 591 * in the global mtime cache, then on the disk. 592 * If this doesn't succeed, we finally return a NULL pointer. 593 * 594 * We cannot add this directory onto the search path because 595 * of this amusing case: 596 * $(INSTALLDIR)/$(FILE): $(FILE) 597 * 598 * $(FILE) exists in $(INSTALLDIR) but not in the current one. 599 * When searching for $(FILE), we will find it in $(INSTALLDIR) 600 * b/c we added it here. This is not good... */ 601 q = Str_dupi(name, ename); 602 if (DEBUG(DIR)) 603 printf("Looking for \"%s\"...", q); 604 605 entry = find_stampi(name, ename); 606 if (entry != NULL) { 607 if (DEBUG(DIR)) 608 printf("got it (in mtime cache)\n"); 609 return q; 610 } else if (stat(q, &stb) == 0) { 611 struct timespec mtime; 612 613 ts_set_from_stat(stb, mtime); 614 if (DEBUG(DIR)) 615 printf("Caching %s for %s\n", time_to_string(&mtime), 616 q); 617 record_stamp(q, mtime); 618 return q; 619 } else { 620 if (DEBUG(DIR)) 621 printf("failed. Returning NULL\n"); 622 free(q); 623 return NULL; 624 } 625} 626 627void 628Dir_AddDiri(Lst path, const char *name, const char *ename) 629{ 630 struct PathEntry *p; 631 632 p = create_PathEntry(name, ename); 633 if (p == NULL) 634 return; 635 if (p->refCount == 1) 636 Lst_AtEnd(path, p); 637 else if (!Lst_AddNew(path, p)) 638 return; 639} 640 641void * 642Dir_CopyDir(void *p) 643{ 644 struct PathEntry *q = p; 645 q->refCount++; 646 return p; 647} 648 649void 650Dir_Destroy(void *pp) 651{ 652 struct PathEntry *p = pp; 653 654 if (--p->refCount == 0) { 655 ohash_remove(&knownDirectories, 656 ohash_qlookup(&knownDirectories, p->name)); 657 free_hash(&p->files); 658 free(p); 659 } 660} 661 662/*- 663 *----------------------------------------------------------------------- 664 * Dir_Concat -- 665 * Concatenate two paths, adding the second to the end of the first. 666 * Makes sure to avoid duplicates. 667 * 668 * Side Effects: 669 * Reference counts for added dirs are upped. 670 *----------------------------------------------------------------------- 671 */ 672void 673Dir_Concat(Lst path1, Lst path2) 674{ 675 LstNode ln; 676 struct PathEntry *p; 677 678 for (ln = Lst_First(path2); ln != NULL; ln = Lst_Adv(ln)) { 679 p = Lst_Datum(ln); 680 if (Lst_AddNew(path1, p)) 681 p->refCount++; 682 } 683} 684 685static void 686DirPrintDir(void *p) 687{ 688 const struct PathEntry *q = p; 689 printf("%s ", q->name); 690} 691 692void 693Dir_PrintPath(Lst path) 694{ 695 Lst_Every(path, DirPrintDir); 696} 697 698struct timespec 699Dir_MTime(GNode *gn) 700{ 701 char *fullName; 702 struct stat stb; 703 struct file_stamp *entry; 704 unsigned int slot; 705 struct timespec mtime; 706 707 if (gn->type & OP_PHONY) 708 return gn->mtime; 709 710 if (gn->type & OP_ARCHV) 711 return Arch_MTime(gn); 712 713 if (gn->path == NULL) { 714 fullName = Dir_FindFile(gn->name, defaultPath); 715 if (fullName == NULL) 716 fullName = estrdup(gn->name); 717 } else 718 fullName = gn->path; 719 720 slot = ohash_qlookup(&mtimes, fullName); 721 entry = ohash_find(&mtimes, slot); 722 if (entry != NULL) { 723 /* Only do this once -- the second time folks are checking to 724 * see if the file was actually updated, so we need to 725 * actually go to the file system. */ 726 if (DEBUG(DIR)) 727 printf("Using cached time %s for %s\n", 728 time_to_string(&entry->mtime), fullName); 729 mtime = entry->mtime; 730 free(entry); 731 ohash_remove(&mtimes, slot); 732 } else if (stat(fullName, &stb) == 0) 733 ts_set_from_stat(stb, mtime); 734 else { 735 if (gn->type & OP_MEMBER) { 736 if (fullName != gn->path) 737 free(fullName); 738 return Arch_MemMTime(gn); 739 } else 740 ts_set_out_of_date(mtime); 741 } 742 if (fullName && gn->path == NULL) 743 gn->path = fullName; 744 745 gn->mtime = mtime; 746 return gn->mtime; 747} 748 749