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