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