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