arch.c revision 1.153
1/* $NetBSD: arch.c,v 1.153 2020/11/02 19:07:09 rillig Exp $ */ 2 3/* 4 * Copyright (c) 1988, 1989, 1990, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * Adam de Boor. 9 * 10 * Redistribution and use in source and binary forms, 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 * 3. Neither the name of the University nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35/* 36 * Copyright (c) 1989 by Berkeley Softworks 37 * All rights reserved. 38 * 39 * This code is derived from software contributed to Berkeley by 40 * Adam de Boor. 41 * 42 * Redistribution and use in source and binary forms, with or without 43 * modification, are permitted provided that the following conditions 44 * are met: 45 * 1. Redistributions of source code must retain the above copyright 46 * notice, this list of conditions and the following disclaimer. 47 * 2. Redistributions in binary form must reproduce the above copyright 48 * notice, this list of conditions and the following disclaimer in the 49 * documentation and/or other materials provided with the distribution. 50 * 3. All advertising materials mentioning features or use of this software 51 * must display the following acknowledgement: 52 * This product includes software developed by the University of 53 * California, Berkeley and its contributors. 54 * 4. Neither the name of the University nor the names of its contributors 55 * may be used to endorse or promote products derived from this software 56 * without specific prior written permission. 57 * 58 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 59 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 60 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 61 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 62 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 63 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 64 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 65 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 66 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 67 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 68 * SUCH DAMAGE. 69 */ 70 71/*- 72 * arch.c -- 73 * Functions to manipulate libraries, archives and their members. 74 * 75 * Once again, cacheing/hashing comes into play in the manipulation 76 * of archives. The first time an archive is referenced, all of its members' 77 * headers are read and hashed and the archive closed again. All hashed 78 * archives are kept on a list which is searched each time an archive member 79 * is referenced. 80 * 81 * The interface to this module is: 82 * Arch_ParseArchive 83 * Given an archive specification, return a list 84 * of GNode's, one for each member in the spec. 85 * FALSE is returned if the specification is 86 * invalid for some reason. 87 * 88 * Arch_Touch Alter the modification time of the archive 89 * member described by the given node to be 90 * the current time. 91 * 92 * Arch_TouchLib Update the modification time of the library 93 * described by the given node. This is special 94 * because it also updates the modification time 95 * of the library's table of contents. 96 * 97 * Arch_MTime Find the modification time of a member of 98 * an archive *in the archive*. The time is also 99 * placed in the member's GNode. Returns the 100 * modification time. 101 * 102 * Arch_MemTime Find the modification time of a member of 103 * an archive. Called when the member doesn't 104 * already exist. Looks in the archive for the 105 * modification time. Returns the modification 106 * time. 107 * 108 * Arch_FindLib Search for a library along a path. The 109 * library name in the GNode should be in 110 * -l<name> format. 111 * 112 * Arch_LibOODate Special function to decide if a library node 113 * is out-of-date. 114 * 115 * Arch_Init Initialize this module. 116 * 117 * Arch_End Clean up this module. 118 */ 119 120#include <sys/types.h> 121#include <sys/stat.h> 122#include <sys/time.h> 123#include <sys/param.h> 124 125#include <ar.h> 126#include <utime.h> 127 128#include "make.h" 129#include "dir.h" 130#include "config.h" 131 132/* "@(#)arch.c 8.2 (Berkeley) 1/2/94" */ 133MAKE_RCSID("$NetBSD: arch.c,v 1.153 2020/11/02 19:07:09 rillig Exp $"); 134 135#ifdef TARGET_MACHINE 136#undef MAKE_MACHINE 137#define MAKE_MACHINE TARGET_MACHINE 138#endif 139#ifdef TARGET_MACHINE_ARCH 140#undef MAKE_MACHINE_ARCH 141#define MAKE_MACHINE_ARCH TARGET_MACHINE_ARCH 142#endif 143 144typedef struct List ArchList; 145typedef struct ListNode ArchListNode; 146 147static ArchList *archives; /* The archives we've already examined */ 148 149typedef struct Arch { 150 char *name; /* Name of archive */ 151 HashTable members; /* All the members of the archive described 152 * by <name, struct ar_hdr *> key/value pairs */ 153 char *fnametab; /* Extended name table strings */ 154 size_t fnamesize; /* Size of the string table */ 155} Arch; 156 157static FILE *ArchFindMember(const char *, const char *, 158 struct ar_hdr *, const char *); 159#if defined(__svr4__) || defined(__SVR4) || defined(__ELF__) 160#define SVR4ARCHIVES 161static int ArchSVR4Entry(Arch *, char *, size_t, FILE *); 162#endif 163 164#ifdef CLEANUP 165static void 166ArchFree(void *ap) 167{ 168 Arch *a = ap; 169 HashIter hi; 170 171 /* Free memory from hash entries */ 172 HashIter_Init(&hi, &a->members); 173 while (HashIter_Next(&hi) != NULL) 174 free(hi.entry->value); 175 176 free(a->name); 177 free(a->fnametab); 178 HashTable_Done(&a->members); 179 free(a); 180} 181#endif 182 183 184/*- 185 *----------------------------------------------------------------------- 186 * Arch_ParseArchive -- 187 * Parse the archive specification in the given line and find/create 188 * the nodes for the specified archive members, placing their nodes 189 * on the given list. 190 * 191 * Input: 192 * pp Pointer to start of specification, updated 193 * nodeLst Lst on which to place the nodes 194 * ctxt Context in which to expand variables 195 * 196 * Results: 197 * TRUE if it was a valid specification. The pp is updated 198 * to point to the first non-space after the archive spec. The 199 * nodes for the members are placed on the given list. 200 *----------------------------------------------------------------------- 201 */ 202Boolean 203Arch_ParseArchive(char **pp, GNodeList *nodeLst, GNode *ctxt) 204{ 205 char *cp; /* Pointer into line */ 206 GNode *gn; /* New node */ 207 char *libName; /* Library-part of specification */ 208 char *libName_freeIt = NULL; 209 char *memName; /* Member-part of specification */ 210 char saveChar; /* Ending delimiter of member-name */ 211 Boolean subLibName; /* TRUE if libName should have/had 212 * variable substitution performed on it */ 213 214 libName = *pp; 215 216 subLibName = FALSE; 217 218 for (cp = libName; *cp != '(' && *cp != '\0';) { 219 if (*cp == '$') { 220 /* 221 * Variable spec, so call the Var module to parse the puppy 222 * so we can safely advance beyond it... 223 */ 224 const char *nested_p = cp; 225 void *result_freeIt; 226 const char *result; 227 Boolean isError; 228 229 /* XXX: is expanded twice: once here and once below */ 230 (void)Var_Parse(&nested_p, ctxt, VARE_UNDEFERR|VARE_WANTRES, 231 &result, &result_freeIt); 232 /* TODO: handle errors */ 233 isError = result == var_Error; 234 free(result_freeIt); 235 if (isError) 236 return FALSE; 237 238 subLibName = TRUE; 239 cp += nested_p - cp; 240 } else 241 cp++; 242 } 243 244 *cp++ = '\0'; 245 if (subLibName) { 246 (void)Var_Subst(libName, ctxt, VARE_UNDEFERR|VARE_WANTRES, &libName); 247 /* TODO: handle errors */ 248 libName_freeIt = libName; 249 } 250 251 252 for (;;) { 253 /* 254 * First skip to the start of the member's name, mark that 255 * place and skip to the end of it (either white-space or 256 * a close paren). 257 */ 258 Boolean doSubst = FALSE; /* TRUE if need to substitute in memName */ 259 260 pp_skip_whitespace(&cp); 261 262 memName = cp; 263 while (*cp != '\0' && *cp != ')' && !ch_isspace(*cp)) { 264 if (*cp == '$') { 265 /* 266 * Variable spec, so call the Var module to parse the puppy 267 * so we can safely advance beyond it... 268 */ 269 void *freeIt; 270 const char *result; 271 Boolean isError; 272 const char *nested_p = cp; 273 274 (void)Var_Parse(&nested_p, ctxt, VARE_UNDEFERR|VARE_WANTRES, 275 &result, &freeIt); 276 /* TODO: handle errors */ 277 isError = result == var_Error; 278 free(freeIt); 279 280 if (isError) 281 return FALSE; 282 283 doSubst = TRUE; 284 cp += nested_p - cp; 285 } else { 286 cp++; 287 } 288 } 289 290 /* 291 * If the specification ends without a closing parenthesis, 292 * chances are there's something wrong (like a missing backslash), 293 * so it's better to return failure than allow such things to happen 294 */ 295 if (*cp == '\0') { 296 printf("No closing parenthesis in archive specification\n"); 297 return FALSE; 298 } 299 300 /* 301 * If we didn't move anywhere, we must be done 302 */ 303 if (cp == memName) { 304 break; 305 } 306 307 saveChar = *cp; 308 *cp = '\0'; 309 310 /* 311 * XXX: This should be taken care of intelligently by 312 * SuffExpandChildren, both for the archive and the member portions. 313 */ 314 /* 315 * If member contains variables, try and substitute for them. 316 * This will slow down archive specs with dynamic sources, of course, 317 * since we'll be (non-)substituting them three times, but them's 318 * the breaks -- we need to do this since SuffExpandChildren calls 319 * us, otherwise we could assume the thing would be taken care of 320 * later. 321 */ 322 if (doSubst) { 323 char *buf; 324 char *sacrifice; 325 char *oldMemName = memName; 326 327 (void)Var_Subst(memName, ctxt, VARE_UNDEFERR|VARE_WANTRES, 328 &memName); 329 /* TODO: handle errors */ 330 331 /* 332 * Now form an archive spec and recurse to deal with nested 333 * variables and multi-word variable values.... The results 334 * are just placed at the end of the nodeLst we're returning. 335 */ 336 buf = sacrifice = str_concat4(libName, "(", memName, ")"); 337 338 if (strchr(memName, '$') && strcmp(memName, oldMemName) == 0) { 339 /* 340 * Must contain dynamic sources, so we can't deal with it now. 341 * Just create an ARCHV node for the thing and let 342 * SuffExpandChildren handle it... 343 */ 344 gn = Targ_GetNode(buf); 345 gn->type |= OP_ARCHV; 346 Lst_Append(nodeLst, gn); 347 348 } else if (!Arch_ParseArchive(&sacrifice, nodeLst, ctxt)) { 349 /* Error in nested call. */ 350 free(buf); 351 return FALSE; 352 } 353 free(buf); 354 355 } else if (Dir_HasWildcards(memName)) { 356 StringList *members = Lst_New(); 357 Dir_Expand(memName, dirSearchPath, members); 358 359 while (!Lst_IsEmpty(members)) { 360 char *member = Lst_Dequeue(members); 361 char *fullname = str_concat4(libName, "(", member, ")"); 362 free(member); 363 364 gn = Targ_GetNode(fullname); 365 free(fullname); 366 367 gn->type |= OP_ARCHV; 368 Lst_Append(nodeLst, gn); 369 } 370 Lst_Free(members); 371 372 } else { 373 char *fullname = str_concat4(libName, "(", memName, ")"); 374 gn = Targ_GetNode(fullname); 375 free(fullname); 376 377 /* 378 * We've found the node, but have to make sure the rest of the 379 * world knows it's an archive member, without having to 380 * constantly check for parentheses, so we type the thing with 381 * the OP_ARCHV bit before we place it on the end of the 382 * provided list. 383 */ 384 gn->type |= OP_ARCHV; 385 Lst_Append(nodeLst, gn); 386 } 387 if (doSubst) { 388 free(memName); 389 } 390 391 *cp = saveChar; 392 } 393 394 free(libName_freeIt); 395 396 cp++; /* skip the ')' */ 397 /* We promised that pp would be set up at the next non-space. */ 398 pp_skip_whitespace(&cp); 399 *pp = cp; 400 return TRUE; 401} 402 403/* Locate a member of an archive, given the path of the archive and the path 404 * of the desired member. 405 * 406 * Input: 407 * archive Path to the archive 408 * member Name of member; only its basename is used. 409 * hash TRUE if archive should be hashed if not already so. 410 * 411 * Results: 412 * The ar_hdr for the member. 413 */ 414static struct ar_hdr * 415ArchStatMember(const char *archive, const char *member, Boolean hash) 416{ 417#define AR_MAX_NAME_LEN (sizeof(arh.ar_name) - 1) 418 FILE *arch; /* Stream to archive */ 419 size_t size; /* Size of archive member */ 420 char magic[SARMAG]; 421 ArchListNode *ln; 422 Arch *ar; /* Archive descriptor */ 423 struct ar_hdr arh; /* archive-member header for reading archive */ 424 char memName[MAXPATHLEN + 1]; 425 /* Current member name while hashing. */ 426 427 /* 428 * Because of space constraints and similar things, files are archived 429 * using their basename, not the entire path. 430 */ 431 const char *lastSlash = strrchr(member, '/'); 432 if (lastSlash != NULL) 433 member = lastSlash + 1; 434 435 for (ln = archives->first; ln != NULL; ln = ln->next) { 436 const Arch *a = ln->datum; 437 if (strcmp(a->name, archive) == 0) 438 break; 439 } 440 441 if (ln != NULL) { 442 struct ar_hdr *hdr; 443 444 ar = ln->datum; 445 hdr = HashTable_FindValue(&ar->members, member); 446 if (hdr != NULL) 447 return hdr; 448 449 { 450 /* Try truncated name */ 451 char copy[AR_MAX_NAME_LEN + 1]; 452 size_t len = strlen(member); 453 454 if (len > AR_MAX_NAME_LEN) { 455 len = AR_MAX_NAME_LEN; 456 snprintf(copy, sizeof copy, "%s", member); 457 } 458 hdr = HashTable_FindValue(&ar->members, copy); 459 return hdr; 460 } 461 } 462 463 if (!hash) { 464 /* 465 * Caller doesn't want the thing hashed, just use ArchFindMember 466 * to read the header for the member out and close down the stream 467 * again. Since the archive is not to be hashed, we assume there's 468 * no need to allocate extra room for the header we're returning, 469 * so just declare it static. 470 */ 471 static struct ar_hdr sarh; 472 473 arch = ArchFindMember(archive, member, &sarh, "r"); 474 if (arch == NULL) 475 return NULL; 476 477 fclose(arch); 478 return &sarh; 479 } 480 481 /* 482 * We don't have this archive on the list yet, so we want to find out 483 * everything that's in it and cache it so we can get at it quickly. 484 */ 485 arch = fopen(archive, "r"); 486 if (arch == NULL) 487 return NULL; 488 489 /* 490 * We use the ARMAG string to make sure this is an archive we 491 * can handle... 492 */ 493 if ((fread(magic, SARMAG, 1, arch) != 1) || 494 (strncmp(magic, ARMAG, SARMAG) != 0)) { 495 fclose(arch); 496 return NULL; 497 } 498 499 ar = bmake_malloc(sizeof(Arch)); 500 ar->name = bmake_strdup(archive); 501 ar->fnametab = NULL; 502 ar->fnamesize = 0; 503 HashTable_Init(&ar->members); 504 memName[AR_MAX_NAME_LEN] = '\0'; 505 506 while (fread((char *)&arh, sizeof(struct ar_hdr), 1, arch) == 1) { 507 if (strncmp(arh.ar_fmag, ARFMAG, sizeof(arh.ar_fmag)) != 0) { 508 /* 509 * The header is bogus, so the archive is bad 510 * and there's no way we can recover... 511 */ 512 goto badarch; 513 } else { 514 char *nameend; 515 516 /* 517 * We need to advance the stream's pointer to the start of the 518 * next header. Files are padded with newlines to an even-byte 519 * boundary, so we need to extract the size of the file from the 520 * 'size' field of the header and round it up during the seek. 521 */ 522 arh.ar_size[sizeof(arh.ar_size) - 1] = '\0'; 523 size = (size_t)strtol(arh.ar_size, NULL, 10); 524 525 memcpy(memName, arh.ar_name, sizeof(arh.ar_name)); 526 nameend = memName + AR_MAX_NAME_LEN; 527 while (*nameend == ' ') { 528 nameend--; 529 } 530 nameend[1] = '\0'; 531 532#ifdef SVR4ARCHIVES 533 /* 534 * svr4 names are slash terminated. Also svr4 extended AR format. 535 */ 536 if (memName[0] == '/') { 537 /* 538 * svr4 magic mode; handle it 539 */ 540 switch (ArchSVR4Entry(ar, memName, size, arch)) { 541 case -1: /* Invalid data */ 542 goto badarch; 543 case 0: /* List of files entry */ 544 continue; 545 default: /* Got the entry */ 546 break; 547 } 548 } else { 549 if (nameend[0] == '/') 550 nameend[0] = '\0'; 551 } 552#endif 553 554#ifdef AR_EFMT1 555 /* 556 * BSD 4.4 extended AR format: #1/<namelen>, with name as the 557 * first <namelen> bytes of the file 558 */ 559 if (strncmp(memName, AR_EFMT1, sizeof(AR_EFMT1) - 1) == 0 && 560 ch_isdigit(memName[sizeof(AR_EFMT1) - 1])) { 561 562 int elen = atoi(&memName[sizeof(AR_EFMT1) - 1]); 563 564 if ((unsigned int)elen > MAXPATHLEN) 565 goto badarch; 566 if (fread(memName, (size_t)elen, 1, arch) != 1) 567 goto badarch; 568 memName[elen] = '\0'; 569 if (fseek(arch, -elen, SEEK_CUR) != 0) 570 goto badarch; 571 if (DEBUG(ARCH) || DEBUG(MAKE)) { 572 debug_printf("ArchStat: Extended format entry for %s\n", 573 memName); 574 } 575 } 576#endif 577 578 { 579 HashEntry *he; 580 he = HashTable_CreateEntry(&ar->members, memName, NULL); 581 HashEntry_Set(he, bmake_malloc(sizeof(struct ar_hdr))); 582 memcpy(HashEntry_Get(he), &arh, sizeof(struct ar_hdr)); 583 } 584 } 585 if (fseek(arch, ((long)size + 1) & ~1, SEEK_CUR) != 0) 586 goto badarch; 587 } 588 589 fclose(arch); 590 591 Lst_Append(archives, ar); 592 593 /* 594 * Now that the archive has been read and cached, we can look into 595 * the hash table to find the desired member's header. 596 */ 597 return HashTable_FindValue(&ar->members, member); 598 599badarch: 600 fclose(arch); 601 HashTable_Done(&ar->members); 602 free(ar->fnametab); 603 free(ar); 604 return NULL; 605} 606 607#ifdef SVR4ARCHIVES 608/*- 609 *----------------------------------------------------------------------- 610 * ArchSVR4Entry -- 611 * Parse an SVR4 style entry that begins with a slash. 612 * If it is "//", then load the table of filenames 613 * If it is "/<offset>", then try to substitute the long file name 614 * from offset of a table previously read. 615 * If a table is read, the file pointer is moved to the next archive 616 * member. 617 * 618 * Results: 619 * -1: Bad data in archive 620 * 0: A table was loaded from the file 621 * 1: Name was successfully substituted from table 622 * 2: Name was not successfully substituted from table 623 *----------------------------------------------------------------------- 624 */ 625static int 626ArchSVR4Entry(Arch *ar, char *name, size_t size, FILE *arch) 627{ 628#define ARLONGNAMES1 "//" 629#define ARLONGNAMES2 "/ARFILENAMES" 630 size_t entry; 631 char *ptr, *eptr; 632 633 if (strncmp(name, ARLONGNAMES1, sizeof(ARLONGNAMES1) - 1) == 0 || 634 strncmp(name, ARLONGNAMES2, sizeof(ARLONGNAMES2) - 1) == 0) { 635 636 if (ar->fnametab != NULL) { 637 DEBUG0(ARCH, "Attempted to redefine an SVR4 name table\n"); 638 return -1; 639 } 640 641 /* 642 * This is a table of archive names, so we build one for 643 * ourselves 644 */ 645 ar->fnametab = bmake_malloc(size); 646 ar->fnamesize = size; 647 648 if (fread(ar->fnametab, size, 1, arch) != 1) { 649 DEBUG0(ARCH, "Reading an SVR4 name table failed\n"); 650 return -1; 651 } 652 eptr = ar->fnametab + size; 653 for (entry = 0, ptr = ar->fnametab; ptr < eptr; ptr++) 654 if (*ptr == '/') { 655 entry++; 656 *ptr = '\0'; 657 } 658 DEBUG1(ARCH, "Found svr4 archive name table with %lu entries\n", 659 (unsigned long)entry); 660 return 0; 661 } 662 663 if (name[1] == ' ' || name[1] == '\0') 664 return 2; 665 666 entry = (size_t)strtol(&name[1], &eptr, 0); 667 if ((*eptr != ' ' && *eptr != '\0') || eptr == &name[1]) { 668 DEBUG1(ARCH, "Could not parse SVR4 name %s\n", name); 669 return 2; 670 } 671 if (entry >= ar->fnamesize) { 672 DEBUG2(ARCH, "SVR4 entry offset %s is greater than %lu\n", 673 name, (unsigned long)ar->fnamesize); 674 return 2; 675 } 676 677 DEBUG2(ARCH, "Replaced %s with %s\n", name, &ar->fnametab[entry]); 678 679 snprintf(name, MAXPATHLEN + 1, "%s", &ar->fnametab[entry]); 680 return 1; 681} 682#endif 683 684 685/*- 686 *----------------------------------------------------------------------- 687 * ArchFindMember -- 688 * Locate a member of an archive, given the path of the archive and 689 * the path of the desired member. If the archive is to be modified, 690 * the mode should be "r+", if not, it should be "r". 691 * The passed struct ar_hdr structure is filled in. 692 * 693 * Input: 694 * archive Path to the archive 695 * member Name of member. If it is a path, only the last 696 * component is used. 697 * out_arh Archive header to be filled in 698 * mode The mode for opening the stream 699 * 700 * Results: 701 * An FILE *, opened for reading and writing, positioned at the 702 * start of the member's struct ar_hdr, or NULL if the member was 703 * nonexistent. The current struct ar_hdr for member. 704 *----------------------------------------------------------------------- 705 */ 706static FILE * 707ArchFindMember(const char *archive, const char *member, struct ar_hdr *out_arh, 708 const char *mode) 709{ 710 FILE *arch; /* Stream to archive */ 711 int size; /* Size of archive member */ 712 char magic[SARMAG]; 713 size_t len, tlen; 714 const char *lastSlash; 715 716 arch = fopen(archive, mode); 717 if (arch == NULL) 718 return NULL; 719 720 /* 721 * We use the ARMAG string to make sure this is an archive we 722 * can handle... 723 */ 724 if ((fread(magic, SARMAG, 1, arch) != 1) || 725 (strncmp(magic, ARMAG, SARMAG) != 0)) { 726 fclose(arch); 727 return NULL; 728 } 729 730 /* 731 * Because of space constraints and similar things, files are archived 732 * using their basename, not the entire path. 733 */ 734 lastSlash = strrchr(member, '/'); 735 if (lastSlash != NULL) 736 member = lastSlash + 1; 737 738 len = tlen = strlen(member); 739 if (len > sizeof(out_arh->ar_name)) { 740 tlen = sizeof(out_arh->ar_name); 741 } 742 743 while (fread((char *)out_arh, sizeof(struct ar_hdr), 1, arch) == 1) { 744 745 if (strncmp(out_arh->ar_fmag, ARFMAG, sizeof(out_arh->ar_fmag)) != 0) { 746 /* 747 * The header is bogus, so the archive is bad 748 * and there's no way we can recover... 749 */ 750 fclose(arch); 751 return NULL; 752 } 753 754 if (strncmp(member, out_arh->ar_name, tlen) == 0) { 755 /* 756 * If the member's name doesn't take up the entire 'name' field, 757 * we have to be careful of matching prefixes. Names are space- 758 * padded to the right, so if the character in 'name' at the end 759 * of the matched string is anything but a space, this isn't the 760 * member we sought. 761 */ 762 if (tlen != sizeof out_arh->ar_name && 763 out_arh->ar_name[tlen] != ' ') 764 goto skip; 765 766 /* 767 * To make life easier, we reposition the file at the start 768 * of the header we just read before we return the stream. 769 * In a more general situation, it might be better to leave 770 * the file at the actual member, rather than its header, but 771 * not here... 772 */ 773 if (fseek(arch, -(long)sizeof(struct ar_hdr), SEEK_CUR) != 0) { 774 fclose(arch); 775 return NULL; 776 } 777 return arch; 778 } 779 780#ifdef AR_EFMT1 781 /* 782 * BSD 4.4 extended AR format: #1/<namelen>, with name as the 783 * first <namelen> bytes of the file 784 */ 785 if (strncmp(out_arh->ar_name, AR_EFMT1, sizeof(AR_EFMT1) - 1) == 0 && 786 ch_isdigit(out_arh->ar_name[sizeof(AR_EFMT1) - 1])) 787 { 788 int elen = atoi(&out_arh->ar_name[sizeof(AR_EFMT1) - 1]); 789 char ename[MAXPATHLEN + 1]; 790 791 if ((unsigned int)elen > MAXPATHLEN) { 792 fclose(arch); 793 return NULL; 794 } 795 if (fread(ename, (size_t)elen, 1, arch) != 1) { 796 fclose(arch); 797 return NULL; 798 } 799 ename[elen] = '\0'; 800 if (DEBUG(ARCH) || DEBUG(MAKE)) { 801 debug_printf("ArchFind: Extended format entry for %s\n", ename); 802 } 803 if (strncmp(ename, member, len) == 0) { 804 /* Found as extended name */ 805 if (fseek(arch, -(long)sizeof(struct ar_hdr) - elen, 806 SEEK_CUR) != 0) { 807 fclose(arch); 808 return NULL; 809 } 810 return arch; 811 } 812 if (fseek(arch, -elen, SEEK_CUR) != 0) { 813 fclose(arch); 814 return NULL; 815 } 816 } 817#endif 818 819skip: 820 /* 821 * This isn't the member we're after, so we need to advance the 822 * stream's pointer to the start of the next header. Files are 823 * padded with newlines to an even-byte boundary, so we need to 824 * extract the size of the file from the 'size' field of the 825 * header and round it up during the seek. 826 */ 827 out_arh->ar_size[sizeof(out_arh->ar_size) - 1] = '\0'; 828 size = (int)strtol(out_arh->ar_size, NULL, 10); 829 if (fseek(arch, (size + 1) & ~1, SEEK_CUR) != 0) { 830 fclose(arch); 831 return NULL; 832 } 833 } 834 835 /* 836 * We've looked everywhere, but the member is not to be found. Close the 837 * archive and return NULL -- an error. 838 */ 839 fclose(arch); 840 return NULL; 841} 842 843/*- 844 *----------------------------------------------------------------------- 845 * Arch_Touch -- 846 * Touch a member of an archive. 847 * The modification time of the entire archive is also changed. 848 * For a library, this could necessitate the re-ranlib'ing of the 849 * whole thing. 850 * 851 * Input: 852 * gn Node of member to touch 853 * 854 * Results: 855 * The 'time' field of the member's header is updated. 856 *----------------------------------------------------------------------- 857 */ 858void 859Arch_Touch(GNode *gn) 860{ 861 FILE *arch; /* Stream open to archive, positioned properly */ 862 struct ar_hdr arh; /* Current header describing member */ 863 864 arch = ArchFindMember(GNode_VarArchive(gn), GNode_VarMember(gn), 865 &arh, "r+"); 866 867 snprintf(arh.ar_date, sizeof(arh.ar_date), "%-12ld", (long)now); 868 869 if (arch != NULL) { 870 (void)fwrite((char *)&arh, sizeof(struct ar_hdr), 1, arch); 871 fclose(arch); 872 } 873} 874 875/* Given a node which represents a library, touch the thing, making sure that 876 * the table of contents also is touched. 877 * 878 * Both the modification time of the library and of the RANLIBMAG member are 879 * set to 'now'. 880 * 881 * Input: 882 * gn The node of the library to touch 883 */ 884void 885Arch_TouchLib(GNode *gn) 886{ 887#ifdef RANLIBMAG 888 FILE * arch; /* Stream open to archive */ 889 struct ar_hdr arh; /* Header describing table of contents */ 890 struct utimbuf times; /* Times for utime() call */ 891 892 arch = ArchFindMember(gn->path, RANLIBMAG, &arh, "r+"); 893 snprintf(arh.ar_date, sizeof(arh.ar_date), "%-12ld", (long) now); 894 895 if (arch != NULL) { 896 (void)fwrite((char *)&arh, sizeof(struct ar_hdr), 1, arch); 897 fclose(arch); 898 899 times.actime = times.modtime = now; 900 utime(gn->path, ×); 901 } 902#else 903 (void)gn; 904#endif 905} 906 907/* Return the modification time of a member of an archive. The mtime field 908 * of the given node is filled in with the value returned by the function. 909 * 910 * Input: 911 * gn Node describing archive member 912 */ 913time_t 914Arch_MTime(GNode *gn) 915{ 916 struct ar_hdr *arh; /* Header of desired member */ 917 time_t modTime; /* Modification time as an integer */ 918 919 arh = ArchStatMember(GNode_VarArchive(gn), GNode_VarMember(gn), TRUE); 920 if (arh != NULL) { 921 modTime = (time_t)strtol(arh->ar_date, NULL, 10); 922 } else { 923 modTime = 0; 924 } 925 926 gn->mtime = modTime; 927 return modTime; 928} 929 930/* Given a non-existent archive member's node, get its modification time from 931 * its archived form, if it exists. gn->mtime is filled in as well. */ 932time_t 933Arch_MemMTime(GNode *gn) 934{ 935 GNodeListNode *ln; 936 937 for (ln = gn->parents->first; ln != NULL; ln = ln->next) { 938 GNode *pgn = ln->datum; 939 940 if (pgn->type & OP_ARCHV) { 941 /* 942 * If the parent is an archive specification and is being made 943 * and its member's name matches the name of the node we were 944 * given, record the modification time of the parent in the 945 * child. We keep searching its parents in case some other 946 * parent requires this child to exist... 947 */ 948 const char *nameStart = strchr(pgn->name, '(') + 1; 949 const char *nameEnd = strchr(nameStart, ')'); 950 size_t nameLen = (size_t)(nameEnd - nameStart); 951 952 if ((pgn->flags & REMAKE) && 953 strncmp(nameStart, gn->name, nameLen) == 0) { 954 gn->mtime = Arch_MTime(pgn); 955 } 956 } else if (pgn->flags & REMAKE) { 957 /* 958 * Something which isn't a library depends on the existence of 959 * this target, so it needs to exist. 960 */ 961 gn->mtime = 0; 962 break; 963 } 964 } 965 966 return gn->mtime; 967} 968 969/* Search for a library along the given search path. 970 * 971 * The node's 'path' field is set to the found path (including the 972 * actual file name, not -l...). If the system can handle the -L 973 * flag when linking (or we cannot find the library), we assume that 974 * the user has placed the .LIBS variable in the final linking 975 * command (or the linker will know where to find it) and set the 976 * TARGET variable for this node to be the node's name. Otherwise, 977 * we set the TARGET variable to be the full path of the library, 978 * as returned by Dir_FindFile. 979 * 980 * Input: 981 * gn Node of library to find 982 */ 983void 984Arch_FindLib(GNode *gn, SearchPath *path) 985{ 986 char *libName = str_concat3("lib", gn->name + 2, ".a"); 987 gn->path = Dir_FindFile(libName, path); 988 free(libName); 989 990#ifdef LIBRARIES 991 Var_Set(TARGET, gn->name, gn); 992#else 993 Var_Set(TARGET, gn->path == NULL ? gn->name : gn->path, gn); 994#endif 995} 996 997/* Decide if a node with the OP_LIB attribute is out-of-date. Called from 998 * Make_OODate to make its life easier. 999 * The library will be hashed if it hasn't been already. 1000 * 1001 * There are several ways for a library to be out-of-date that are 1002 * not available to ordinary files. In addition, there are ways 1003 * that are open to regular files that are not available to 1004 * libraries. A library that is only used as a source is never 1005 * considered out-of-date by itself. This does not preclude the 1006 * library's modification time from making its parent be out-of-date. 1007 * A library will be considered out-of-date for any of these reasons, 1008 * given that it is a target on a dependency line somewhere: 1009 * 1010 * Its modification time is less than that of one of its sources 1011 * (gn->mtime < gn->youngestChild->mtime). 1012 * 1013 * Its modification time is greater than the time at which the make 1014 * began (i.e. it's been modified in the course of the make, probably 1015 * by archiving). 1016 * 1017 * The modification time of one of its sources is greater than the one 1018 * of its RANLIBMAG member (i.e. its table of contents is out-of-date). 1019 * We don't compare of the archive time vs. TOC time because they can be 1020 * too close. In my opinion we should not bother with the TOC at all 1021 * since this is used by 'ar' rules that affect the data contents of the 1022 * archive, not by ranlib rules, which affect the TOC. 1023 * 1024 * Input: 1025 * gn The library's graph node 1026 * 1027 * Results: 1028 * TRUE if the library is out-of-date. FALSE otherwise. 1029 */ 1030Boolean 1031Arch_LibOODate(GNode *gn) 1032{ 1033 Boolean oodate; 1034 1035 if (gn->type & OP_PHONY) { 1036 oodate = TRUE; 1037 } else if (!GNode_IsTarget(gn) && Lst_IsEmpty(gn->children)) { 1038 oodate = FALSE; 1039 } else if ((!Lst_IsEmpty(gn->children) && gn->youngestChild == NULL) || 1040 (gn->mtime > now) || 1041 (gn->youngestChild != NULL && 1042 gn->mtime < gn->youngestChild->mtime)) { 1043 oodate = TRUE; 1044 } else { 1045#ifdef RANLIBMAG 1046 struct ar_hdr *arh; /* Header for __.SYMDEF */ 1047 int modTimeTOC; /* The table-of-contents's mod time */ 1048 1049 arh = ArchStatMember(gn->path, RANLIBMAG, FALSE); 1050 1051 if (arh != NULL) { 1052 modTimeTOC = (int)strtol(arh->ar_date, NULL, 10); 1053 1054 if (DEBUG(ARCH) || DEBUG(MAKE)) { 1055 debug_printf("%s modified %s...", RANLIBMAG, Targ_FmtTime(modTimeTOC)); 1056 } 1057 oodate = (gn->youngestChild == NULL || gn->youngestChild->mtime > modTimeTOC); 1058 } else { 1059 /* 1060 * A library w/o a table of contents is out-of-date 1061 */ 1062 if (DEBUG(ARCH) || DEBUG(MAKE)) { 1063 debug_printf("No t.o.c...."); 1064 } 1065 oodate = TRUE; 1066 } 1067#else 1068 oodate = FALSE; 1069#endif 1070 } 1071 return oodate; 1072} 1073 1074/* Initialize the archives module. */ 1075void 1076Arch_Init(void) 1077{ 1078 archives = Lst_New(); 1079} 1080 1081/* Clean up the archives module. */ 1082void 1083Arch_End(void) 1084{ 1085#ifdef CLEANUP 1086 Lst_Destroy(archives, ArchFree); 1087#endif 1088} 1089 1090Boolean 1091Arch_IsLib(GNode *gn) 1092{ 1093 static const char armag[] = "!<arch>\n"; 1094 char buf[sizeof armag - 1]; 1095 int fd; 1096 1097 if ((fd = open(gn->path, O_RDONLY)) == -1) 1098 return FALSE; 1099 1100 if (read(fd, buf, sizeof buf) != sizeof buf) { 1101 (void)close(fd); 1102 return FALSE; 1103 } 1104 1105 (void)close(fd); 1106 1107 return memcmp(buf, armag, sizeof buf) == 0; 1108} 1109