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