1 /* $OpenBSD: arch.c,v 1.91 2020/01/13 13:54:44 espie Exp $ */ 2 /* $NetBSD: arch.c,v 1.17 1996/11/06 17:58:59 christos Exp $ */ 3 4 /* 5 * Copyright (c) 1999,2000 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, 1993 32 * The Regents of the University of California. All rights reserved. 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 /* 65 * Once again, cacheing/hashing comes into play in the manipulation 66 * of archives. The first time an archive is referenced, all of its members' 67 * headers are read and hashed and the archive closed again. All hashed 68 * archives are kept in a hash (archives) which is searched each time 69 * an archive member is referenced. 70 * 71 */ 72 73 #include <ar.h> 74 #include <assert.h> 75 #include <ctype.h> 76 #include <fcntl.h> 77 #include <limits.h> 78 #include <stddef.h> 79 #include <stdint.h> 80 #include <stdio.h> 81 #include <stdlib.h> 82 #include <string.h> 83 #include <unistd.h> 84 #include <ohash.h> 85 #include "config.h" 86 #include "defines.h" 87 #include "buf.h" 88 #include "dir.h" 89 #include "direxpand.h" 90 #include "arch.h" 91 #include "var.h" 92 #include "targ.h" 93 #include "memory.h" 94 #include "gnode.h" 95 #include "timestamp.h" 96 #include "lst.h" 97 98 #ifdef TARGET_MACHINE 99 #undef MACHINE 100 #define MACHINE TARGET_MACHINE 101 #endif 102 #ifdef TARGET_MACHINE_ARCH 103 #undef MACHINE_ARCH 104 #define MACHINE_ARCH TARGET_MACHINE_ARCH 105 #endif 106 #ifdef TARGET_MACHINE_CPU 107 #undef MACHINE_CPU 108 #define MACHINE_CPU TARGET_MACHINE_CPU 109 #endif 110 111 static struct ohash archives; /* Archives we've already examined. */ 112 113 typedef struct Arch_ { 114 struct ohash members; /* All the members of this archive, as 115 * struct arch_member entries. */ 116 char name[1]; /* Archive name. */ 117 } Arch; 118 119 /* Used to get to ar's field sizes. */ 120 static struct ar_hdr *dummy; 121 #define AR_NAME_SIZE (sizeof(dummy->ar_name)) 122 #define AR_DATE_SIZE (sizeof(dummy->ar_date)) 123 124 /* Each archive member is tied to an arch_member structure, 125 * suitable for hashing. */ 126 struct arch_member { 127 struct timespec mtime; /* Member modification date. */ 128 char date[AR_DATE_SIZE+1]; /* Same, before conversion to numeric 129 * value. */ 130 char name[1]; /* Member name. */ 131 }; 132 133 static struct ohash_info members_info = { 134 offsetof(struct arch_member, name), NULL, 135 hash_calloc, hash_free, element_alloc 136 }; 137 138 static struct ohash_info arch_info = { 139 offsetof(Arch, name), NULL, hash_calloc, hash_free, element_alloc 140 }; 141 142 143 144 static struct arch_member *new_arch_member(struct ar_hdr *, const char *); 145 static struct timespec mtime_of_member(struct arch_member *); 146 static long field2long(const char *, size_t); 147 static Arch *read_archive(const char *, const char *); 148 149 static struct timespec ArchMTimeMember(const char *, const char *, bool); 150 static FILE *ArchFindMember(const char *, const char *, struct ar_hdr *, const char *); 151 static void ArchTouch(const char *, const char *); 152 #if defined(__svr4__) || defined(__SVR4) || \ 153 (defined(__OpenBSD__) && defined(__ELF__)) 154 #define SVR4ARCHIVES 155 #endif 156 static bool parse_archive(Buffer, const char **, Lst, SymTable *); 157 static void add_archive_node(Lst, const char *); 158 159 struct SVR4namelist { 160 char *fnametab; /* Extended name table strings */ 161 size_t fnamesize; /* Size of the string table */ 162 }; 163 164 #ifdef SVR4ARCHIVES 165 static const char *svr4list = "Archive list"; 166 167 static char *ArchSVR4Entry(struct SVR4namelist *, const char *, size_t, FILE *); 168 #endif 169 170 static struct arch_member * 171 new_arch_member(struct ar_hdr *hdr, const char *name) 172 { 173 const char *end = NULL; 174 struct arch_member *n; 175 176 n = ohash_create_entry(&members_info, name, &end); 177 /* XXX ar entries are NOT null terminated. */ 178 memcpy(n->date, &(hdr->ar_date), AR_DATE_SIZE); 179 n->date[AR_DATE_SIZE] = '\0'; 180 /* Don't compute mtime before it is needed. */ 181 ts_set_out_of_date(n->mtime); 182 return n; 183 } 184 185 static struct timespec 186 mtime_of_member(struct arch_member *m) 187 { 188 if (is_out_of_date(m->mtime)) 189 ts_set_from_time_t((time_t) strtoll(m->date, NULL, 10), 190 m->mtime); 191 return m->mtime; 192 } 193 194 bool 195 Arch_ParseArchive(const char **line, Lst nodes, SymTable *ctxt) 196 { 197 bool result; 198 static BUFFER expand; 199 200 Buf_Reinit(&expand, MAKE_BSIZE); 201 result = parse_archive(&expand, line, nodes, ctxt); 202 return result; 203 } 204 205 static void 206 add_archive_node(Lst nodes, const char *name) 207 { 208 GNode *gn; 209 210 gn = Targ_FindNode(name, TARG_CREATE); 211 gn->type |= OP_ARCHV; 212 Lst_AtEnd(nodes, gn); 213 } 214 215 static bool 216 parse_archive(Buffer expand, const char **linePtr, Lst nodeLst, SymTable *ctxt) 217 { 218 const char *cp; /* Pointer into line */ 219 const char *lib; /* Library-part of specification */ 220 const char *elib; 221 const char *member; /* Member-part of specification */ 222 const char *emember; 223 bool subst_lib; 224 225 /* figure out the library name part */ 226 lib = *linePtr; 227 subst_lib = false; 228 229 for (cp = lib; *cp != '(' && *cp != '\0';) { 230 if (*cp == '$') { 231 if (!Var_ParseSkip(&cp, ctxt)) 232 return false; 233 subst_lib = true; 234 } else 235 cp++; 236 } 237 238 elib = cp; 239 if (subst_lib) { 240 lib = Var_Substi(lib, elib, ctxt, true); 241 elib = lib + strlen(lib); 242 } 243 244 if (*cp == '\0') { 245 printf("Unclosed parenthesis in archive specification\n"); 246 return false; 247 } 248 cp++; 249 /* iterate on members, that may be separated by spaces */ 250 for (;;) { 251 /* First skip to the start of the member's name, mark that 252 * place and skip to the end of it (either white-space or 253 * a close paren). */ 254 bool subst_member = false; 255 256 while (ISSPACE(*cp)) 257 cp++; 258 member = cp; 259 while (*cp != '\0' && *cp != ')' && !ISSPACE(*cp)) { 260 if (*cp == '$') { 261 if (!Var_ParseSkip(&cp, ctxt)) 262 return false; 263 subst_member = true; 264 } else 265 cp++; 266 } 267 268 /* If the specification ends without a closing parenthesis, 269 * chances are there's something wrong (like a missing 270 * backslash), so it's better to return failure than allow such 271 * things to happen. */ 272 if (*cp == '\0' || ISSPACE(*cp)) { 273 printf("No closing parenthesis in archive specification\n"); 274 return false; 275 } 276 277 /* If we didn't move anywhere, we must be done. */ 278 if (cp == member) 279 break; 280 281 emember = cp; 282 283 /* XXX: This should be taken care of intelligently by 284 * SuffExpandChildren, both for the archive and the member 285 * portions. */ 286 287 /* If member contains variables, try and substitute for them. 288 * This will slow down archive specs with dynamic sources, of 289 * course, since we'll be (non-)substituting them three times, 290 * but them's the breaks -- we need to do this since 291 * SuffExpandChildren calls us, otherwise we could assume the 292 * thing would be taken care of later. */ 293 if (subst_member) { 294 const char *oldMemberName = member; 295 const char *result; 296 297 member = Var_Substi(member, emember, ctxt, true); 298 299 /* Now form an archive spec and recurse to deal with 300 * nested variables and multi-word variable values.... 301 * The results are just placed at the end of the 302 * nodeLst we're returning. */ 303 Buf_Addi(expand, lib, elib); 304 Buf_AddChar(expand, '('); 305 Buf_AddString(expand, member); 306 Buf_AddChar(expand, ')'); 307 result = Buf_Retrieve(expand); 308 309 if (strchr(member, '$') && 310 memcmp(member, oldMemberName, 311 emember - oldMemberName) == 0) { 312 /* Must contain dynamic sources, so we can't 313 * deal with it now. let SuffExpandChildren 314 * handle it later */ 315 add_archive_node(nodeLst, result); 316 } else if (!Arch_ParseArchive(&result, nodeLst, ctxt)) 317 return false; 318 Buf_Reset(expand); 319 } else if (Dir_HasWildcardsi(member, emember)) { 320 LIST members; 321 char *m; 322 323 Lst_Init(&members); 324 325 Dir_Expandi(member, emember, defaultPath, &members); 326 while ((m = Lst_DeQueue(&members)) != NULL) { 327 Buf_Addi(expand, lib, elib); 328 Buf_AddChar(expand, '('); 329 Buf_AddString(expand, m); 330 Buf_AddChar(expand, ')'); 331 free(m); 332 add_archive_node(nodeLst, Buf_Retrieve(expand)); 333 Buf_Reset(expand); 334 } 335 } else { 336 Buf_Addi(expand, lib, elib); 337 Buf_AddChar(expand, '('); 338 Buf_Addi(expand, member, emember); 339 Buf_AddChar(expand, ')'); 340 add_archive_node(nodeLst, Buf_Retrieve(expand)); 341 Buf_Reset(expand); 342 } 343 if (subst_member) 344 free((char *)member); 345 346 } 347 348 if (subst_lib) 349 free((char *)lib); 350 351 /* We promised the pointer would be set up at the next non-space, so 352 * we must advance cp there before setting *linePtr... (note that on 353 * entrance to the loop, cp is guaranteed to point at a ')') */ 354 do { 355 cp++; 356 } while (ISSPACE(*cp)); 357 358 *linePtr = cp; 359 return true; 360 } 361 362 /* Helper function: ar fields are not null terminated. */ 363 static long 364 field2long(const char *field, size_t length) 365 { 366 static char enough[32]; 367 368 assert(length < sizeof(enough)); 369 memcpy(enough, field, length); 370 enough[length] = '\0'; 371 return strtol(enough, NULL, 10); 372 } 373 374 static Arch * 375 read_archive(const char *archive, const char *earchive) 376 { 377 FILE *arch; /* Stream to archive */ 378 char magic[SARMAG]; 379 Arch *ar; 380 struct SVR4namelist list; 381 382 list.fnametab = NULL; 383 384 /* When we encounter an archive for the first time, we read its 385 * whole contents, to place it in the cache. */ 386 arch = fopen(archive, "r"); 387 if (arch == NULL) 388 return NULL; 389 390 /* Make sure this is an archive we can handle. */ 391 if ((fread(magic, SARMAG, 1, arch) != 1) || 392 (strncmp(magic, ARMAG, SARMAG) != 0)) { 393 fclose(arch); 394 return NULL; 395 } 396 397 ar = ohash_create_entry(&arch_info, archive, &earchive); 398 ohash_init(&ar->members, 8, &members_info); 399 400 for (;;) { 401 size_t n; 402 struct ar_hdr arHeader; /* Archive-member header */ 403 off_t size; /* Size of archive member */ 404 char buffer[PATH_MAX]; 405 char *memberName; /* Current member name while hashing. */ 406 char *cp; 407 408 memberName = buffer; 409 n = fread(&arHeader, 1, sizeof(struct ar_hdr), arch); 410 411 /* Whole archive read ok. */ 412 if (n == 0 && feof(arch)) { 413 free(list.fnametab); 414 fclose(arch); 415 return ar; 416 } 417 if (n < sizeof(struct ar_hdr)) 418 break; 419 420 if (memcmp(arHeader.ar_fmag, ARFMAG, sizeof(arHeader.ar_fmag)) 421 != 0) { 422 /* header is bogus. */ 423 break; 424 } else { 425 /* We need to advance the stream's pointer to the start 426 * of the next header. Records are padded with 427 * newlines to an even-byte boundary, so we need to 428 * extract the size of the record and round it up 429 * during the seek. */ 430 size = (off_t) field2long(arHeader.ar_size, 431 sizeof(arHeader.ar_size)); 432 433 (void)memcpy(memberName, arHeader.ar_name, 434 AR_NAME_SIZE); 435 /* Find real end of name (strip extranous ' ') */ 436 for (cp = memberName + AR_NAME_SIZE - 1; *cp == ' ';) 437 cp--; 438 cp[1] = '\0'; 439 440 #ifdef SVR4ARCHIVES 441 /* SVR4 names are slash terminated. Also svr4 extended 442 * AR format. 443 */ 444 if (memberName[0] == '/') { 445 /* SVR4 magic mode. */ 446 memberName = ArchSVR4Entry(&list, memberName, 447 size, arch); 448 if (memberName == NULL) 449 /* Invalid data */ 450 break; 451 else if (memberName == svr4list) 452 /* List of files entry */ 453 continue; 454 /* Got the entry. */ 455 /* XXX this assumes further processing, such as 456 * AR_EFMT1, also applies to SVR4ARCHIVES. */ 457 } 458 else { 459 if (cp[0] == '/') 460 cp[0] = '\0'; 461 } 462 #endif 463 464 #ifdef AR_EFMT1 465 /* BSD 4.4 extended AR format: #1/<namelen>, with name 466 * as the first <namelen> bytes of the file. */ 467 if (memcmp(memberName, AR_EFMT1, sizeof(AR_EFMT1) - 1) 468 == 0 && ISDIGIT(memberName[sizeof(AR_EFMT1) - 1])) { 469 470 int elen = atoi(memberName + 471 sizeof(AR_EFMT1)-1); 472 473 if (elen <= 0 || elen >= PATH_MAX) 474 break; 475 memberName = buffer; 476 if (fread(memberName, elen, 1, arch) != 1) 477 break; 478 memberName[elen] = '\0'; 479 if (fseek(arch, -elen, SEEK_CUR) != 0) 480 break; 481 if (DEBUG(ARCH) || DEBUG(MAKE)) 482 printf("ArchStat: Extended format entry for %s\n", 483 memberName); 484 } 485 #endif 486 487 ohash_insert(&ar->members, 488 ohash_qlookup(&ar->members, memberName), 489 new_arch_member(&arHeader, memberName)); 490 } 491 if (fseek(arch, (size + 1) & ~1, SEEK_CUR) != 0) 492 break; 493 } 494 495 fclose(arch); 496 ohash_delete(&ar->members); 497 free(list.fnametab); 498 free(ar); 499 return NULL; 500 } 501 502 /*- 503 *----------------------------------------------------------------------- 504 * ArchMTimeMember -- 505 * Find the modification time of an archive's member, given the 506 * path to the archive and the path to the desired member. 507 * 508 * Results: 509 * The archive member's modification time, or OUT_OF_DATE if member 510 * was not found (convenient, so that missing members are always 511 * out of date). 512 * 513 * Side Effects: 514 * Cache the whole archive contents if hash is true. 515 *----------------------------------------------------------------------- 516 */ 517 static struct timespec 518 ArchMTimeMember( 519 const char *archive, /* Path to the archive */ 520 const char *member, /* Name of member. If it is a path, only the 521 * last component is used. */ 522 bool hash) /* true if archive should be hashed if not 523 * already so. */ 524 { 525 FILE *arch; /* Stream to archive */ 526 Arch *ar; /* Archive descriptor */ 527 unsigned int slot; /* Place of archive in the archives hash */ 528 const char *end = NULL; 529 const char *cp; 530 struct timespec result; 531 532 ts_set_out_of_date(result); 533 /* Because of space constraints and similar things, files are archived 534 * using their final path components, not the entire thing, so we need 535 * to point 'member' to the final component, if there is one, to make 536 * the comparisons easier... */ 537 cp = strrchr(member, '/'); 538 if (cp != NULL) 539 member = cp + 1; 540 541 /* Try to find archive in cache. */ 542 slot = ohash_qlookupi(&archives, archive, &end); 543 ar = ohash_find(&archives, slot); 544 545 /* If not found, get it now. */ 546 if (ar == NULL) { 547 if (!hash) { 548 /* Quick path: no need to hash the whole archive, just 549 * use ArchFindMember to get the member's header and 550 * close the stream again. */ 551 struct ar_hdr arHeader; 552 553 arch = ArchFindMember(archive, member, &arHeader, "r"); 554 555 if (arch != NULL) { 556 fclose(arch); 557 ts_set_from_time_t( 558 (time_t)strtol(arHeader.ar_date, NULL, 10), 559 result); 560 } 561 return result; 562 } 563 ar = read_archive(archive, end); 564 if (ar != NULL) 565 ohash_insert(&archives, slot, ar); 566 } 567 568 /* If archive was found, get entry we seek. */ 569 if (ar != NULL) { 570 struct arch_member *he; 571 end = NULL; 572 573 he = ohash_find(&ar->members, ohash_qlookupi(&ar->members, 574 member, &end)); 575 if (he != NULL) 576 return mtime_of_member(he); 577 else { 578 if ((size_t)(end - member) > AR_NAME_SIZE) { 579 /* Try truncated name. */ 580 end = member + AR_NAME_SIZE; 581 he = ohash_find(&ar->members, 582 ohash_qlookupi(&ar->members, member, &end)); 583 if (he != NULL) 584 return mtime_of_member(he); 585 } 586 } 587 } 588 return result; 589 } 590 591 #ifdef SVR4ARCHIVES 592 /*- 593 *----------------------------------------------------------------------- 594 * ArchSVR4Entry -- 595 * Parse an SVR4 style entry that begins with a slash. 596 * If it is "//", then load the table of filenames 597 * If it is "/<offset>", then try to substitute the long file name 598 * from offset of a table previously read. 599 * 600 * Results: 601 * svr4list: just read a list of names 602 * NULL: error occurred 603 * extended name 604 * 605 * Side-effect: 606 * For a list of names, store the list in l. 607 *----------------------------------------------------------------------- 608 */ 609 610 static char * 611 ArchSVR4Entry(struct SVR4namelist *l, const char *name, size_t size, FILE *arch) 612 { 613 #define ARLONGNAMES1 "/" 614 #define ARLONGNAMES2 "ARFILENAMES" 615 size_t entry; 616 char *ptr, *eptr; 617 618 assert(name[0] == '/'); 619 name++; 620 /* First comes a table of archive names, to be used by subsequent 621 * calls. */ 622 if (memcmp(name, ARLONGNAMES1, sizeof(ARLONGNAMES1) - 1) == 0 || 623 memcmp(name, ARLONGNAMES2, sizeof(ARLONGNAMES2) - 1) == 0) { 624 625 if (l->fnametab != NULL) { 626 if (DEBUG(ARCH)) 627 printf("Attempted to redefine an SVR4 name table\n"); 628 return NULL; 629 } 630 631 l->fnametab = emalloc(size); 632 l->fnamesize = size; 633 634 if (fread(l->fnametab, size, 1, arch) != 1) { 635 if (DEBUG(ARCH)) 636 printf("Reading an SVR4 name table failed\n"); 637 return NULL; 638 } 639 640 eptr = l->fnametab + size; 641 for (entry = 0, ptr = l->fnametab; ptr < eptr; ptr++) 642 switch (*ptr) { 643 case '/': 644 entry++; 645 *ptr = '\0'; 646 break; 647 648 case '\n': 649 break; 650 651 default: 652 break; 653 } 654 if (DEBUG(ARCH)) 655 printf("Found svr4 archive name table with %zu entries\n", 656 entry); 657 return (char *)svr4list; 658 } 659 /* Then the names themselves are given as offsets in this table. */ 660 if (*name == ' ' || *name == '\0') 661 return NULL; 662 663 entry = (size_t) strtol(name, &eptr, 0); 664 if ((*eptr != ' ' && *eptr != '\0') || eptr == name) { 665 if (DEBUG(ARCH)) 666 printf("Could not parse SVR4 name /%s\n", name); 667 return NULL; 668 } 669 if (entry >= l->fnamesize) { 670 if (DEBUG(ARCH)) 671 printf("SVR4 entry offset /%s is greater than %zu\n", 672 name, l->fnamesize); 673 return NULL; 674 } 675 676 if (DEBUG(ARCH)) 677 printf("Replaced /%s with %s\n", name, l->fnametab + entry); 678 679 return l->fnametab + entry; 680 } 681 #endif 682 683 684 /*- 685 *----------------------------------------------------------------------- 686 * ArchFindMember -- 687 * Locate a member of an archive, given the path of the archive and 688 * the path of the desired member. If the archive is to be modified, 689 * the mode should be "r+", if not, it should be "r". 690 * 691 * Results: 692 * A FILE *, opened for reading and writing, positioned right after 693 * the member's header, or NULL if the member was nonexistent. 694 * 695 * Side Effects: 696 * Fill the struct ar_hdr pointed by arHeaderPtr. 697 *----------------------------------------------------------------------- 698 */ 699 static FILE * 700 ArchFindMember( 701 const char *archive, /* Path to the archive */ 702 const char *member, /* Name of member. If it is a path, only the 703 * last component is used. */ 704 struct ar_hdr *arHeaderPtr,/* Pointer to header structure to be filled in */ 705 const char *mode) /* mode for opening the stream */ 706 { 707 FILE * arch; /* Stream to archive */ 708 char *cp; 709 char magic[SARMAG]; 710 size_t length; 711 struct SVR4namelist list; 712 713 list.fnametab = NULL; 714 715 arch = fopen(archive, mode); 716 if (arch == NULL) 717 return NULL; 718 719 /* Make sure this is an archive we can handle. */ 720 if (fread(magic, SARMAG, 1, arch) != 1 || 721 strncmp(magic, ARMAG, SARMAG) != 0) { 722 fclose(arch); 723 return NULL; 724 } 725 726 /* Because of space constraints and similar things, files are archived 727 * using their final path components, not the entire thing, so we need 728 * to point 'member' to the final component, if there is one, to make 729 * the comparisons easier... */ 730 cp = strrchr(member, '/'); 731 if (cp != NULL) 732 member = cp + 1; 733 734 length = strlen(member); 735 if (length >= AR_NAME_SIZE) 736 length = AR_NAME_SIZE; 737 738 /* Error handling is simpler than for read_archive, since we just 739 * look for a given member. */ 740 while (fread(arHeaderPtr, sizeof(struct ar_hdr), 1, arch) == 1) { 741 off_t size; /* Size of archive member */ 742 char *memberName; 743 744 if (memcmp(arHeaderPtr->ar_fmag, ARFMAG, 745 sizeof(arHeaderPtr->ar_fmag) ) != 0) 746 /* The header is bogus, so the archive is bad. */ 747 break; 748 749 memberName = arHeaderPtr->ar_name; 750 if (memcmp(member, memberName, length) == 0) { 751 /* If the member's name doesn't take up the entire 752 * 'name' field, we have to be careful of matching 753 * prefixes. Names are space- padded to the right, so 754 * if the character in 'name' at the end of the matched 755 * string is anything but a space, this isn't the 756 * member we sought. */ 757 #ifdef SVR4ARCHIVES 758 if (length < sizeof(arHeaderPtr->ar_name) && 759 memberName[length] == '/') 760 length++; 761 #endif 762 if (length == sizeof(arHeaderPtr->ar_name) || 763 memberName[length] == ' ') { 764 free(list.fnametab); 765 return arch; 766 } 767 } 768 769 size = (off_t) field2long(arHeaderPtr->ar_size, 770 sizeof(arHeaderPtr->ar_size)); 771 772 #ifdef SVR4ARCHIVES 773 /* svr4 names are slash terminated. Also svr4 extended AR 774 * format. 775 */ 776 if (memberName[0] == '/') { 777 /* svr4 magic mode. */ 778 memberName = ArchSVR4Entry(&list, arHeaderPtr->ar_name, 779 size, arch); 780 if (memberName == NULL) 781 /* Invalid data */ 782 break; 783 else if (memberName == svr4list) 784 /* List of files entry */ 785 continue; 786 /* Got the entry. */ 787 if (strcmp(memberName, member) == 0) { 788 free(list.fnametab); 789 return arch; 790 } 791 } 792 #endif 793 794 #ifdef AR_EFMT1 795 /* BSD 4.4 extended AR format: #1/<namelen>, with name as the 796 * first <namelen> bytes of the file. */ 797 if (memcmp(memberName, AR_EFMT1, sizeof(AR_EFMT1) - 1) == 0 && 798 ISDIGIT(memberName[sizeof(AR_EFMT1) - 1])) { 799 char ename[PATH_MAX]; 800 801 int elength = atoi(memberName + sizeof(AR_EFMT1)-1); 802 803 if (elength <= 0 || elength >= PATH_MAX) 804 break; 805 if (fread(ename, elength, 1, arch) != 1) 806 break; 807 if (fseek(arch, -elength, SEEK_CUR) != 0) 808 break; 809 ename[elength] = '\0'; 810 if (DEBUG(ARCH) || DEBUG(MAKE)) 811 printf("ArchFind: Extended format entry for %s\n", ename); 812 /* Found as extended name. */ 813 if (strcmp(ename, member) == 0) { 814 free(list.fnametab); 815 return arch; 816 } 817 } 818 #endif 819 /* This isn't the member we're after, so we need to advance the 820 * stream's pointer to the start of the next header. */ 821 if (fseek(arch, (size + 1) & ~1, SEEK_CUR) != 0) 822 break; 823 } 824 825 /* We did not find the member, or we ran into an error while reading 826 * the archive. */ 827 #ifdef SVRARCHIVES 828 free(list.fnametab); 829 #endif 830 fclose(arch); 831 return NULL; 832 } 833 834 static void 835 ArchTouch(const char *archive, const char *member) 836 { 837 FILE *arch; 838 struct ar_hdr arHeader; 839 840 arch = ArchFindMember(archive, member, &arHeader, "r+"); 841 if (arch != NULL) { 842 snprintf(arHeader.ar_date, sizeof(arHeader.ar_date), 843 "%-12ld", (long) time(NULL)); 844 if (fseek(arch, -sizeof(struct ar_hdr), SEEK_CUR) == 0) 845 (void)fwrite(&arHeader, sizeof(struct ar_hdr), 1, arch); 846 fclose(arch); 847 } 848 } 849 850 /* 851 * Side Effects: 852 * The modification time of the entire archive is also changed. 853 * For a library, this could necessitate the re-ranlib'ing of the 854 * whole thing. 855 */ 856 void 857 Arch_Touch(GNode *gn) 858 { 859 ArchTouch(Var(ARCHIVE_INDEX, gn), Var(MEMBER_INDEX, gn)); 860 } 861 862 struct timespec 863 Arch_MTime(GNode *gn) 864 { 865 gn->mtime = ArchMTimeMember(Var(ARCHIVE_INDEX, gn), 866 Var(MEMBER_INDEX, gn), true); 867 868 return gn->mtime; 869 } 870 871 struct timespec 872 Arch_MemMTime(GNode *gn) 873 { 874 LstNode ln; 875 876 for (ln = Lst_First(&gn->parents); ln != NULL; ln = Lst_Adv(ln)) { 877 GNode *pgn; 878 char *nameStart; 879 char *nameEnd; 880 881 pgn = Lst_Datum(ln); 882 883 if (pgn->type & OP_ARCHV) { 884 /* If the parent is an archive specification and is 885 * being built and its member's name matches the name of 886 * the node we were given, record the modification time 887 * of the parent in the child. We keep searching its 888 * parents in case some other parent requires this 889 * child to exist... */ 890 if ((nameStart = strchr(pgn->name, '(') ) != NULL) { 891 nameStart++; 892 nameEnd = strchr(nameStart, ')'); 893 } else 894 nameEnd = NULL; 895 896 if (pgn->must_make && nameEnd != NULL && 897 strncmp(nameStart, gn->name, nameEnd - nameStart) 898 == 0 && gn->name[nameEnd-nameStart] == '\0') 899 gn->mtime = Arch_MTime(pgn); 900 } else if (pgn->must_make) { 901 /* Something which isn't a library depends on the 902 * existence of this target, so it needs to exist. */ 903 ts_set_out_of_date(gn->mtime); 904 break; 905 } 906 } 907 return gn->mtime; 908 } 909 910 void 911 Arch_Init(void) 912 { 913 ohash_init(&archives, 4, &arch_info); 914 } 915