arch.c revision 138916
11590Srgrimes/*
21590Srgrimes * Copyright (c) 1988, 1989, 1990, 1993
31590Srgrimes *	The Regents of the University of California.  All rights reserved.
41590Srgrimes * Copyright (c) 1989 by Berkeley Softworks
51590Srgrimes * All rights reserved.
61590Srgrimes *
71590Srgrimes * This code is derived from software contributed to Berkeley by
81590Srgrimes * Adam de Boor.
91590Srgrimes *
101590Srgrimes * Redistribution and use in source and binary forms, with or without
111590Srgrimes * modification, are permitted provided that the following conditions
121590Srgrimes * are met:
131590Srgrimes * 1. Redistributions of source code must retain the above copyright
141590Srgrimes *    notice, this list of conditions and the following disclaimer.
151590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
161590Srgrimes *    notice, this list of conditions and the following disclaimer in the
171590Srgrimes *    documentation and/or other materials provided with the distribution.
181590Srgrimes * 3. All advertising materials mentioning features or use of this software
191590Srgrimes *    must display the following acknowledgement:
201590Srgrimes *	This product includes software developed by the University of
211590Srgrimes *	California, Berkeley and its contributors.
221590Srgrimes * 4. Neither the name of the University nor the names of its contributors
231590Srgrimes *    may be used to endorse or promote products derived from this software
241590Srgrimes *    without specific prior written permission.
251590Srgrimes *
261590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
271590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
281590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
291590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
301590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
311590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
321590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
331590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
341590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
351590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
361590Srgrimes * SUCH DAMAGE.
3762833Swsanchez *
3862833Swsanchez * @(#)arch.c	8.2 (Berkeley) 1/2/94
391590Srgrimes */
401590Srgrimes
4162833Swsanchez#include <sys/cdefs.h>
4294587Sobrien__FBSDID("$FreeBSD: head/usr.bin/make/arch.c 138916 2004-12-16 16:14:16Z harti $");
431590Srgrimes
441590Srgrimes/*-
451590Srgrimes * arch.c --
461590Srgrimes *	Functions to manipulate libraries, archives and their members.
471590Srgrimes *
481590Srgrimes *	Once again, cacheing/hashing comes into play in the manipulation
491590Srgrimes * of archives. The first time an archive is referenced, all of its members'
501590Srgrimes * headers are read and hashed and the archive closed again. All hashed
511590Srgrimes * archives are kept on a list which is searched each time an archive member
521590Srgrimes * is referenced.
531590Srgrimes *
541590Srgrimes * The interface to this module is:
551590Srgrimes *	Arch_ParseArchive   	Given an archive specification, return a list
561590Srgrimes *	    	  	    	of GNode's, one for each member in the spec.
571590Srgrimes *	    	  	    	FAILURE is returned if the specification is
581590Srgrimes *	    	  	    	invalid for some reason.
591590Srgrimes *
601590Srgrimes *	Arch_Touch	    	Alter the modification time of the archive
611590Srgrimes *	    	  	    	member described by the given node to be
621590Srgrimes *	    	  	    	the current time.
631590Srgrimes *
641590Srgrimes *	Arch_TouchLib	    	Update the modification time of the library
651590Srgrimes *	    	  	    	described by the given node. This is special
661590Srgrimes *	    	  	    	because it also updates the modification time
671590Srgrimes *	    	  	    	of the library's table of contents.
681590Srgrimes *
691590Srgrimes *	Arch_MTime	    	Find the modification time of a member of
701590Srgrimes *	    	  	    	an archive *in the archive*. The time is also
711590Srgrimes *	    	  	    	placed in the member's GNode. Returns the
721590Srgrimes *	    	  	    	modification time.
731590Srgrimes *
741590Srgrimes *	Arch_MemTime	    	Find the modification time of a member of
751590Srgrimes *	    	  	    	an archive. Called when the member doesn't
761590Srgrimes *	    	  	    	already exist. Looks in the archive for the
771590Srgrimes *	    	  	    	modification time. Returns the modification
781590Srgrimes *	    	  	    	time.
791590Srgrimes *
801590Srgrimes *	Arch_FindLib	    	Search for a library along a path. The
811590Srgrimes *	    	  	    	library name in the GNode should be in
821590Srgrimes *	    	  	    	-l<name> format.
831590Srgrimes *
841590Srgrimes *	Arch_LibOODate	    	Special function to decide if a library node
851590Srgrimes *	    	  	    	is out-of-date.
861590Srgrimes *
871590Srgrimes *	Arch_Init 	    	Initialize this module.
885814Sjkh *
895814Sjkh *	Arch_End 	    	Cleanup this module.
901590Srgrimes */
911590Srgrimes
921590Srgrimes#include    <sys/types.h>
931590Srgrimes#include    <sys/stat.h>
941590Srgrimes#include    <sys/time.h>
955814Sjkh#include    <sys/param.h>
961590Srgrimes#include    <ctype.h>
971590Srgrimes#include    <ar.h>
9818730Ssteve#include    <utime.h>
991590Srgrimes#include    <stdio.h>
1005814Sjkh#include    <stdlib.h>
1011590Srgrimes#include    "make.h"
1021590Srgrimes#include    "hash.h"
1031590Srgrimes#include    "dir.h"
1041590Srgrimes#include    "config.h"
1051590Srgrimes
106138916Sharti/* Lst of archives we've already examined */
107138916Shartistatic Lst archives = Lst_Initializer(archives);
1081590Srgrimes
1091590Srgrimestypedef struct Arch {
1101590Srgrimes    char	  *name;      /* Name of archive */
1111590Srgrimes    Hash_Table	  members;    /* All the members of the archive described
1121590Srgrimes			       * by <name, struct ar_hdr *> key/value pairs */
11318730Ssteve    char	  *fnametab;  /* Extended name table strings */
11418730Ssteve    size_t	  fnamesize;  /* Size of the string table */
1151590Srgrimes} Arch;
1161590Srgrimes
11792921Simpstatic void ArchFree(void *);
11892921Simpstatic struct ar_hdr *ArchStatMember(char *, char *, Boolean);
11992921Simpstatic FILE *ArchFindMember(char *, char *, struct ar_hdr *, char *);
12051132Sjulian#if defined(__svr4__) || defined(__SVR4) || defined(__ELF__)
121103503Sjmallett#define	SVR4ARCHIVES
12292921Simpstatic int ArchSVR4Entry(Arch *, char *, size_t, FILE *);
12318730Ssteve#endif
1241590Srgrimes
1251590Srgrimes/*-
1261590Srgrimes *-----------------------------------------------------------------------
1275814Sjkh * ArchFree --
1285814Sjkh *	Free memory used by an archive
1295814Sjkh *
1305814Sjkh * Results:
1315814Sjkh *	None.
1325814Sjkh *
1335814Sjkh * Side Effects:
1345814Sjkh *	None.
1355814Sjkh *
1365814Sjkh *-----------------------------------------------------------------------
1375814Sjkh */
1385814Sjkhstatic void
139104696SjmallettArchFree(void *ap)
1405814Sjkh{
141138264Sharti    Arch *a = ap;
1425814Sjkh    Hash_Search	  search;
1435814Sjkh    Hash_Entry	  *entry;
1448874Srgrimes
1458874Srgrimes    /* Free memory from hash entries */
1465814Sjkh    for (entry = Hash_EnumFirst(&a->members, &search);
14749938Shoek	 entry != NULL;
1485814Sjkh	 entry = Hash_EnumNext(&search))
14969531Swill	free(Hash_GetValue(entry));
1505814Sjkh
1515814Sjkh    free(a->name);
152105826Sjmallett    free(a->fnametab);
1535814Sjkh    Hash_DeleteTable(&a->members);
15469531Swill    free(a);
1555814Sjkh}
1565814Sjkh
1575814Sjkh/*-
1585814Sjkh *-----------------------------------------------------------------------
1591590Srgrimes * Arch_ParseArchive --
1601590Srgrimes *	Parse the archive specification in the given line and find/create
1611590Srgrimes *	the nodes for the specified archive members, placing their nodes
162104696Sjmallett *	on the given list, given the pointer to the start of the
163104696Sjmallett *	specification, a Lst on which to place the nodes, and a context
164104696Sjmallett *	in which to expand variables.
1651590Srgrimes *
1661590Srgrimes * Results:
1671590Srgrimes *	SUCCESS if it was a valid specification. The linePtr is updated
1681590Srgrimes *	to point to the first non-space after the archive spec. The
1691590Srgrimes *	nodes for the members are placed on the given list.
1701590Srgrimes *
1711590Srgrimes * Side Effects:
1721590Srgrimes *	Some nodes may be created. The given list is extended.
1731590Srgrimes *
1741590Srgrimes *-----------------------------------------------------------------------
1751590Srgrimes */
1761590SrgrimesReturnStatus
177138512ShartiArch_ParseArchive(char **linePtr, Lst *nodeLst, GNode *ctxt)
1781590Srgrimes{
17994584Sobrien    char            *cp;	    /* Pointer into line */
1801590Srgrimes    GNode	    *gn;     	    /* New node */
1811590Srgrimes    char	    *libName;  	    /* Library-part of specification */
1821590Srgrimes    char	    *memName;  	    /* Member-part of specification */
18369390Swill    char	    *nameBuf;	    /* temporary place for node name */
1841590Srgrimes    char	    saveChar;  	    /* Ending delimiter of member-name */
1851590Srgrimes    Boolean 	    subLibName;	    /* TRUE if libName should have/had
1861590Srgrimes				     * variable substitution performed on it */
1871590Srgrimes
1881590Srgrimes    libName = *linePtr;
1898874Srgrimes
1901590Srgrimes    subLibName = FALSE;
1911590Srgrimes
1921590Srgrimes    for (cp = libName; *cp != '(' && *cp != '\0'; cp++) {
1931590Srgrimes	if (*cp == '$') {
1941590Srgrimes	    /*
1951590Srgrimes	     * Variable spec, so call the Var module to parse the puppy
1961590Srgrimes	     * so we can safely advance beyond it...
1971590Srgrimes	     */
198138346Sharti	    size_t length;
1991590Srgrimes	    Boolean	freeIt;
2001590Srgrimes	    char	*result;
2018874Srgrimes
202138232Sharti	    result = Var_Parse(cp, ctxt, TRUE, &length, &freeIt);
2031590Srgrimes	    if (result == var_Error) {
204138232Sharti		return (FAILURE);
2051590Srgrimes	    } else {
2061590Srgrimes		subLibName = TRUE;
2071590Srgrimes	    }
2088874Srgrimes
2091590Srgrimes	    if (freeIt) {
2101590Srgrimes		free(result);
2111590Srgrimes	    }
212138232Sharti	    cp += length - 1;
2131590Srgrimes	}
2141590Srgrimes    }
2151590Srgrimes
2161590Srgrimes    *cp++ = '\0';
2171590Srgrimes    if (subLibName) {
2181590Srgrimes	libName = Var_Subst(NULL, libName, ctxt, TRUE);
2191590Srgrimes    }
2201590Srgrimes
2218874Srgrimes
2221590Srgrimes    for (;;) {
2231590Srgrimes	/*
2241590Srgrimes	 * First skip to the start of the member's name, mark that
2251590Srgrimes	 * place and skip to the end of it (either white-space or
2261590Srgrimes	 * a close paren).
2271590Srgrimes	 */
2281590Srgrimes	Boolean	doSubst = FALSE; /* TRUE if need to substitute in memName */
2291590Srgrimes
230138232Sharti	while (*cp != '\0' && *cp != ')' && isspace((unsigned char)*cp)) {
2311590Srgrimes	    cp++;
2321590Srgrimes	}
2331590Srgrimes	memName = cp;
234138232Sharti	while (*cp != '\0' && *cp != ')' && !isspace((unsigned char)*cp)) {
2351590Srgrimes	    if (*cp == '$') {
2361590Srgrimes		/*
2371590Srgrimes		 * Variable spec, so call the Var module to parse the puppy
2381590Srgrimes		 * so we can safely advance beyond it...
2391590Srgrimes		 */
240138346Sharti		size_t length;
2411590Srgrimes		Boolean	freeIt;
2421590Srgrimes		char	*result;
2431590Srgrimes
244138346Sharti		result = Var_Parse(cp, ctxt, TRUE, &length, &freeIt);
2451590Srgrimes		if (result == var_Error) {
246138232Sharti		    return (FAILURE);
2471590Srgrimes		} else {
2481590Srgrimes		    doSubst = TRUE;
2491590Srgrimes		}
2501590Srgrimes
2511590Srgrimes		if (freeIt) {
2521590Srgrimes		    free(result);
2531590Srgrimes		}
2541590Srgrimes		cp += length;
2551590Srgrimes	    } else {
2561590Srgrimes		cp++;
2571590Srgrimes	    }
2581590Srgrimes	}
2591590Srgrimes
2601590Srgrimes	/*
2611590Srgrimes	 * If the specification ends without a closing parenthesis,
2621590Srgrimes	 * chances are there's something wrong (like a missing backslash),
2631590Srgrimes	 * so it's better to return failure than allow such things to happen
2641590Srgrimes	 */
2651590Srgrimes	if (*cp == '\0') {
2661590Srgrimes	    printf("No closing parenthesis in archive specification\n");
2671590Srgrimes	    return (FAILURE);
2681590Srgrimes	}
2691590Srgrimes
2701590Srgrimes	/*
2711590Srgrimes	 * If we didn't move anywhere, we must be done
2721590Srgrimes	 */
2731590Srgrimes	if (cp == memName) {
2741590Srgrimes	    break;
2751590Srgrimes	}
2761590Srgrimes
2771590Srgrimes	saveChar = *cp;
2781590Srgrimes	*cp = '\0';
2791590Srgrimes
2801590Srgrimes	/*
2811590Srgrimes	 * XXX: This should be taken care of intelligently by
2821590Srgrimes	 * SuffExpandChildren, both for the archive and the member portions.
2831590Srgrimes	 */
2841590Srgrimes	/*
2851590Srgrimes	 * If member contains variables, try and substitute for them.
2861590Srgrimes	 * This will slow down archive specs with dynamic sources, of course,
2871590Srgrimes	 * since we'll be (non-)substituting them three times, but them's
2881590Srgrimes	 * the breaks -- we need to do this since SuffExpandChildren calls
2891590Srgrimes	 * us, otherwise we could assume the thing would be taken care of
2901590Srgrimes	 * later.
2911590Srgrimes	 */
2921590Srgrimes	if (doSubst) {
2931590Srgrimes	    char    *buf;
2941590Srgrimes	    char    *sacrifice;
2951590Srgrimes	    char    *oldMemName = memName;
29669390Swill	    size_t   sz;
2978874Srgrimes
2981590Srgrimes	    memName = Var_Subst(NULL, memName, ctxt, TRUE);
2991590Srgrimes
3001590Srgrimes	    /*
3011590Srgrimes	     * Now form an archive spec and recurse to deal with nested
3021590Srgrimes	     * variables and multi-word variable values.... The results
3031590Srgrimes	     * are just placed at the end of the nodeLst we're returning.
3041590Srgrimes	     */
3051590Srgrimes
30669390Swill	    sz = strlen(memName) + strlen(libName) + 3;
30769390Swill	    buf = sacrifice = emalloc(sz);
3081590Srgrimes
30969390Swill	    snprintf(buf, sz, "%s(%s)", libName, memName);
31069390Swill
3111590Srgrimes	    if (strchr(memName, '$') && strcmp(memName, oldMemName) == 0) {
3121590Srgrimes		/*
3131590Srgrimes		 * Must contain dynamic sources, so we can't deal with it now.
3141590Srgrimes		 * Just create an ARCHV node for the thing and let
3151590Srgrimes		 * SuffExpandChildren handle it...
3161590Srgrimes		 */
3171590Srgrimes		gn = Targ_FindNode(buf, TARG_CREATE);
3181590Srgrimes
31969527Swill		if (gn == NULL) {
3201590Srgrimes		    free(buf);
321138232Sharti		    return (FAILURE);
3221590Srgrimes		} else {
3231590Srgrimes		    gn->type |= OP_ARCHV;
324138232Sharti		    Lst_AtEnd(nodeLst, (void *)gn);
3251590Srgrimes		}
326138232Sharti	    } else if (Arch_ParseArchive(&sacrifice, nodeLst, ctxt) != SUCCESS) {
3271590Srgrimes		/*
3281590Srgrimes		 * Error in nested call -- free buffer and return FAILURE
3291590Srgrimes		 * ourselves.
3301590Srgrimes		 */
3311590Srgrimes		free(buf);
332138232Sharti		return (FAILURE);
3331590Srgrimes	    }
3341590Srgrimes	    /*
3351590Srgrimes	     * Free buffer and continue with our work.
3361590Srgrimes	     */
3371590Srgrimes	    free(buf);
3381590Srgrimes	} else if (Dir_HasWildcards(memName)) {
339138916Sharti	    Lst members = Lst_Initializer(members);
3401590Srgrimes	    char  *member;
34169390Swill	    size_t sz = MAXPATHLEN;
34269390Swill	    size_t nsz;
343138196Sharti
34469390Swill	    nameBuf = emalloc(sz);
3451590Srgrimes
346138916Sharti	    Dir_Expand(memName, &dirSearchPath, &members);
347138916Sharti	    while (!Lst_IsEmpty(&members)) {
348138916Sharti		member = Lst_DeQueue(&members);
34969390Swill		nsz = strlen(libName) + strlen(member) + 3;
350138196Sharti		if (nsz > sz) {
351138196Sharti			sz = nsz * 2;
352138196Sharti			nameBuf = erealloc(nameBuf, sz);
353138196Sharti		}
3548874Srgrimes
35569390Swill		snprintf(nameBuf, sz, "%s(%s)", libName, member);
3561590Srgrimes		free(member);
357138232Sharti		gn = Targ_FindNode(nameBuf, TARG_CREATE);
35869527Swill		if (gn == NULL) {
35969390Swill		    free(nameBuf);
360138916Sharti		    /* XXXHB Lst_Destroy(&members) */
3611590Srgrimes		    return (FAILURE);
3621590Srgrimes		} else {
3631590Srgrimes		    /*
3641590Srgrimes		     * We've found the node, but have to make sure the rest of
3651590Srgrimes		     * the world knows it's an archive member, without having
3661590Srgrimes		     * to constantly check for parentheses, so we type the
3671590Srgrimes		     * thing with the OP_ARCHV bit before we place it on the
3681590Srgrimes		     * end of the provided list.
3691590Srgrimes		     */
3701590Srgrimes		    gn->type |= OP_ARCHV;
371138232Sharti		    Lst_AtEnd(nodeLst, (void *)gn);
3721590Srgrimes		}
3731590Srgrimes	    }
37469390Swill	    free(nameBuf);
3751590Srgrimes	} else {
37669390Swill	    size_t sz = strlen(libName) + strlen(memName) + 3;
37769390Swill	    nameBuf = emalloc(sz);
37869390Swill	    snprintf(nameBuf, sz, "%s(%s)", libName, memName);
379138232Sharti	    gn = Targ_FindNode(nameBuf, TARG_CREATE);
38069390Swill	    free(nameBuf);
38169527Swill	    if (gn == NULL) {
3821590Srgrimes		return (FAILURE);
3831590Srgrimes	    } else {
3841590Srgrimes		/*
3851590Srgrimes		 * We've found the node, but have to make sure the rest of the
3861590Srgrimes		 * world knows it's an archive member, without having to
3871590Srgrimes		 * constantly check for parentheses, so we type the thing with
3881590Srgrimes		 * the OP_ARCHV bit before we place it on the end of the
3891590Srgrimes		 * provided list.
3901590Srgrimes		 */
3911590Srgrimes		gn->type |= OP_ARCHV;
392138264Sharti		Lst_AtEnd(nodeLst, gn);
3931590Srgrimes	    }
3941590Srgrimes	}
3951590Srgrimes	if (doSubst) {
3961590Srgrimes	    free(memName);
3971590Srgrimes	}
3988874Srgrimes
3991590Srgrimes	*cp = saveChar;
4001590Srgrimes    }
4011590Srgrimes
4021590Srgrimes    /*
4031590Srgrimes     * If substituted libName, free it now, since we need it no longer.
4041590Srgrimes     */
4051590Srgrimes    if (subLibName) {
4061590Srgrimes	free(libName);
4071590Srgrimes    }
4081590Srgrimes
4091590Srgrimes    /*
4101590Srgrimes     * We promised the pointer would be set up at the next non-space, so
4111590Srgrimes     * we must advance cp there before setting *linePtr... (note that on
4121590Srgrimes     * entrance to the loop, cp is guaranteed to point at a ')')
4131590Srgrimes     */
4141590Srgrimes    do {
4151590Srgrimes	cp++;
416138232Sharti    } while (*cp != '\0' && isspace((unsigned char)*cp));
4171590Srgrimes
4181590Srgrimes    *linePtr = cp;
4191590Srgrimes    return (SUCCESS);
4201590Srgrimes}
4211590Srgrimes
4221590Srgrimes/*-
4231590Srgrimes *-----------------------------------------------------------------------
4241590Srgrimes * ArchFindArchive --
4251590Srgrimes *	See if the given archive is the one we are looking for. Called
426104696Sjmallett *	From ArchStatMember and ArchFindMember via Lst_Find with the
427104696Sjmallett *	current list element and the name we want.
4281590Srgrimes *
4291590Srgrimes * Results:
4301590Srgrimes *	0 if it is, non-zero if it isn't.
4311590Srgrimes *
4321590Srgrimes * Side Effects:
4331590Srgrimes *	None.
4341590Srgrimes *
4351590Srgrimes *-----------------------------------------------------------------------
4361590Srgrimes */
4371590Srgrimesstatic int
438138561ShartiArchFindArchive(const void *ar, const void *archName)
4391590Srgrimes{
440138561Sharti
441138561Sharti	return (strcmp(archName, ((const Arch *)ar)->name));
4421590Srgrimes}
4431590Srgrimes
4441590Srgrimes/*-
4451590Srgrimes *-----------------------------------------------------------------------
4461590Srgrimes * ArchStatMember --
4471590Srgrimes *	Locate a member of an archive, given the path of the archive and
448104696Sjmallett *	the path of the desired member, and a boolean representing whether
449104696Sjmallett *	or not the archive should be hashed (if not already hashed).
4501590Srgrimes *
4511590Srgrimes * Results:
4521590Srgrimes *	A pointer to the current struct ar_hdr structure for the member. Note
4531590Srgrimes *	That no position is returned, so this is not useful for touching
4541590Srgrimes *	archive members. This is mostly because we have no assurances that
4551590Srgrimes *	The archive will remain constant after we read all the headers, so
4561590Srgrimes *	there's not much point in remembering the position...
4571590Srgrimes *
4581590Srgrimes * Side Effects:
4591590Srgrimes *
4601590Srgrimes *-----------------------------------------------------------------------
4611590Srgrimes */
4621590Srgrimesstatic struct ar_hdr *
463138232ShartiArchStatMember(char *archive, char *member, Boolean hash)
4641590Srgrimes{
465138232Sharti#define	AR_MAX_NAME_LEN	    (sizeof(arh.ar_name) - 1)
4661590Srgrimes    FILE *	  arch;	      /* Stream to archive */
4671590Srgrimes    int		  size;       /* Size of archive member */
4681590Srgrimes    char	  *cp;	      /* Useful character pointer */
4691590Srgrimes    char	  magic[SARMAG];
470138512Sharti    LstNode	  *ln;	      /* Lst member containing archive descriptor */
4711590Srgrimes    Arch	  *ar;	      /* Archive descriptor */
4721590Srgrimes    Hash_Entry	  *he;	      /* Entry containing member's description */
4731590Srgrimes    struct ar_hdr arh;        /* archive-member header for reading archive */
474138232Sharti    char	  memName[MAXPATHLEN + 1];
4755814Sjkh    	    	    	    /* Current member name while hashing. */
4761590Srgrimes
4771590Srgrimes    /*
4781590Srgrimes     * Because of space constraints and similar things, files are archived
4791590Srgrimes     * using their final path components, not the entire thing, so we need
4801590Srgrimes     * to point 'member' to the final component, if there is one, to make
4811590Srgrimes     * the comparisons easier...
4821590Srgrimes     */
483138232Sharti    cp = strrchr(member, '/');
48451132Sjulian    if ((cp != NULL) && (strcmp(member, RANLIBMAG) != 0))
4851590Srgrimes	member = cp + 1;
4861590Srgrimes
487138916Sharti    ln = Lst_Find(&archives, archive, ArchFindArchive);
48869527Swill    if (ln != NULL) {
489138264Sharti	ar = Lst_Datum(ln);
4901590Srgrimes
491138232Sharti	he = Hash_FindEntry(&ar->members, member);
4921590Srgrimes
49349938Shoek	if (he != NULL) {
494138232Sharti	    return ((struct ar_hdr *)Hash_GetValue (he));
4951590Srgrimes	} else {
4965814Sjkh	    /* Try truncated name */
497138232Sharti	    char copy[AR_MAX_NAME_LEN + 1];
498138232Sharti	    size_t len = strlen(member);
4995814Sjkh
5005814Sjkh	    if (len > AR_MAX_NAME_LEN) {
5015814Sjkh		len = AR_MAX_NAME_LEN;
5025814Sjkh		strncpy(copy, member, AR_MAX_NAME_LEN);
5035814Sjkh		copy[AR_MAX_NAME_LEN] = '\0';
5045814Sjkh	    }
505138232Sharti	    if ((he = Hash_FindEntry(&ar->members, copy)) != NULL)
506138264Sharti		return (Hash_GetValue(he));
50749938Shoek	    return (NULL);
5081590Srgrimes	}
5091590Srgrimes    }
5101590Srgrimes
5111590Srgrimes    if (!hash) {
5121590Srgrimes	/*
5131590Srgrimes	 * Caller doesn't want the thing hashed, just use ArchFindMember
5141590Srgrimes	 * to read the header for the member out and close down the stream
5151590Srgrimes	 * again. Since the archive is not to be hashed, we assume there's
5161590Srgrimes	 * no need to allocate extra room for the header we're returning,
5171590Srgrimes	 * so just declare it static.
5181590Srgrimes	 */
5191590Srgrimes	 static struct ar_hdr	sarh;
5201590Srgrimes
5211590Srgrimes	 arch = ArchFindMember(archive, member, &sarh, "r");
5221590Srgrimes
52349938Shoek	if (arch == NULL) {
52449938Shoek	    return (NULL);
5251590Srgrimes	} else {
5261590Srgrimes	    fclose(arch);
5271590Srgrimes	    return (&sarh);
5281590Srgrimes	}
5291590Srgrimes    }
5301590Srgrimes
5311590Srgrimes    /*
5321590Srgrimes     * We don't have this archive on the list yet, so we want to find out
5331590Srgrimes     * everything that's in it and cache it so we can get at it quickly.
5341590Srgrimes     */
535138232Sharti    arch = fopen(archive, "r");
53649938Shoek    if (arch == NULL) {
53749938Shoek	return (NULL);
5381590Srgrimes    }
5398874Srgrimes
5401590Srgrimes    /*
5411590Srgrimes     * We use the ARMAG string to make sure this is an archive we
5421590Srgrimes     * can handle...
5431590Srgrimes     */
544138232Sharti    if ((fread(magic, SARMAG, 1, arch) != 1) ||
545138232Sharti    	(strncmp(magic, ARMAG, SARMAG) != 0)) {
546138232Sharti	    fclose(arch);
54749938Shoek	    return (NULL);
5481590Srgrimes    }
5491590Srgrimes
550138264Sharti    ar = emalloc(sizeof(Arch));
551138232Sharti    ar->name = estrdup(archive);
55218730Ssteve    ar->fnametab = NULL;
55318730Ssteve    ar->fnamesize = 0;
554138232Sharti    Hash_InitTable(&ar->members, -1);
5551590Srgrimes    memName[AR_MAX_NAME_LEN] = '\0';
5568874Srgrimes
557138264Sharti    while (fread(&arh, sizeof(struct ar_hdr), 1, arch) == 1) {
558138232Sharti	if (strncmp(arh.ar_fmag, ARFMAG, sizeof(arh.ar_fmag)) != 0) {
55918730Ssteve	    /*
56018730Ssteve	     * The header is bogus, so the archive is bad
56118730Ssteve	     * and there's no way we can recover...
56218730Ssteve	     */
56318730Ssteve	    goto badarch;
5641590Srgrimes	} else {
56518730Ssteve	    /*
56618730Ssteve	     * We need to advance the stream's pointer to the start of the
56718730Ssteve	     * next header. Files are padded with newlines to an even-byte
56818730Ssteve	     * boundary, so we need to extract the size of the file from the
56918730Ssteve	     * 'size' field of the header and round it up during the seek.
57018730Ssteve	     */
571138232Sharti	    arh.ar_size[sizeof(arh.ar_size) - 1] = '\0';
572138232Sharti	    size = (int)strtol(arh.ar_size, NULL, 10);
57318730Ssteve
574138232Sharti	    strncpy(memName, arh.ar_name, sizeof(arh.ar_name));
5751590Srgrimes	    for (cp = &memName[AR_MAX_NAME_LEN]; *cp == ' '; cp--) {
5761590Srgrimes		continue;
5771590Srgrimes	    }
5781590Srgrimes	    cp[1] = '\0';
5791590Srgrimes
58018730Ssteve#ifdef SVR4ARCHIVES
58118730Ssteve	    /*
58218730Ssteve	     * svr4 names are slash terminated. Also svr4 extended AR format.
58318730Ssteve	     */
58418730Ssteve	    if (memName[0] == '/') {
58518730Ssteve		/*
58618730Ssteve		 * svr4 magic mode; handle it
58718730Ssteve		 */
58818730Ssteve		switch (ArchSVR4Entry(ar, memName, size, arch)) {
58918730Ssteve		case -1:  /* Invalid data */
59018730Ssteve		    goto badarch;
59118730Ssteve		case 0:	  /* List of files entry */
59218730Ssteve		    continue;
59318730Ssteve		default:  /* Got the entry */
59418730Ssteve		    break;
59518730Ssteve		}
59618730Ssteve	    }
59718730Ssteve	    else {
59818730Ssteve		if (cp[0] == '/')
59918730Ssteve		    cp[0] = '\0';
60018730Ssteve	    }
60118730Ssteve#endif
60218730Ssteve
6035814Sjkh#ifdef AR_EFMT1
6045814Sjkh	    /*
6055814Sjkh	     * BSD 4.4 extended AR format: #1/<namelen>, with name as the
6065814Sjkh	     * first <namelen> bytes of the file
6075814Sjkh	     */
6085814Sjkh	    if (strncmp(memName, AR_EFMT1, sizeof(AR_EFMT1) - 1) == 0 &&
6095814Sjkh		isdigit(memName[sizeof(AR_EFMT1) - 1])) {
6105814Sjkh
6115814Sjkh		unsigned int elen = atoi(&memName[sizeof(AR_EFMT1)-1]);
6125814Sjkh
61318730Ssteve		if (elen > MAXPATHLEN)
61418730Ssteve			goto badarch;
615138232Sharti		if (fread(memName, elen, 1, arch) != 1)
61618730Ssteve			goto badarch;
6175814Sjkh		memName[elen] = '\0';
618138232Sharti		fseek(arch, -elen, SEEK_CUR);
619103508Sjmallett		/* XXX Multiple levels may be asked for, make this conditional
620103508Sjmallett		 * on one, and use DEBUGF.
621103508Sjmallett		 */
6225814Sjkh		if (DEBUG(ARCH) || DEBUG(MAKE)) {
623103508Sjmallett		    fprintf(stderr, "ArchStat: Extended format entry for %s\n", memName);
6245814Sjkh		}
6255814Sjkh	    }
6265814Sjkh#endif
6275814Sjkh
628138232Sharti	    he = Hash_CreateEntry(&ar->members, memName, NULL);
629138264Sharti	    Hash_SetValue(he, emalloc(sizeof(struct ar_hdr)));
630138264Sharti	    memcpy(Hash_GetValue(he), &arh, sizeof(struct ar_hdr));
6311590Srgrimes	}
632138232Sharti	fseek(arch, (size + 1) & ~1, SEEK_CUR);
6331590Srgrimes    }
6341590Srgrimes
635138232Sharti    fclose(arch);
6361590Srgrimes
637138916Sharti    Lst_AtEnd(&archives, ar);
6381590Srgrimes
6391590Srgrimes    /*
6401590Srgrimes     * Now that the archive has been read and cached, we can look into
6411590Srgrimes     * the hash table to find the desired member's header.
6421590Srgrimes     */
643138232Sharti    he = Hash_FindEntry(&ar->members, member);
6441590Srgrimes
64549938Shoek    if (he != NULL) {
646138264Sharti	return (Hash_GetValue (he));
6471590Srgrimes    } else {
64849938Shoek	return (NULL);
6491590Srgrimes    }
65018730Ssteve
65118730Sstevebadarch:
652138232Sharti    fclose(arch);
653138232Sharti    Hash_DeleteTable(&ar->members);
654105826Sjmallett    free(ar->fnametab);
655138232Sharti    free(ar);
65649938Shoek    return (NULL);
6571590Srgrimes}
6581590Srgrimes
65918730Ssteve#ifdef SVR4ARCHIVES
6601590Srgrimes/*-
6611590Srgrimes *-----------------------------------------------------------------------
66218730Ssteve * ArchSVR4Entry --
66318730Ssteve *	Parse an SVR4 style entry that begins with a slash.
66418730Ssteve *	If it is "//", then load the table of filenames
66518730Ssteve *	If it is "/<offset>", then try to substitute the long file name
66618730Ssteve *	from offset of a table previously read.
66718730Ssteve *
66818730Ssteve * Results:
66918730Ssteve *	-1: Bad data in archive
67018730Ssteve *	 0: A table was loaded from the file
67118730Ssteve *	 1: Name was successfully substituted from table
67218730Ssteve *	 2: Name was not successfully substituted from table
67318730Ssteve *
67418730Ssteve * Side Effects:
67518730Ssteve *	If a table is read, the file pointer is moved to the next archive
67618730Ssteve *	member
67718730Ssteve *
67818730Ssteve *-----------------------------------------------------------------------
67918730Ssteve */
68018730Sstevestatic int
681104696SjmallettArchSVR4Entry(Arch *ar, char *name, size_t size, FILE *arch)
68218730Ssteve{
683103503Sjmallett#define	ARLONGNAMES1 "//"
684103503Sjmallett#define	ARLONGNAMES2 "/ARFILENAMES"
68518730Ssteve    size_t entry;
68618730Ssteve    char *ptr, *eptr;
68718730Ssteve
68818730Ssteve    if (strncmp(name, ARLONGNAMES1, sizeof(ARLONGNAMES1) - 1) == 0 ||
68918730Ssteve	strncmp(name, ARLONGNAMES2, sizeof(ARLONGNAMES2) - 1) == 0) {
69018730Ssteve
69118730Ssteve	if (ar->fnametab != NULL) {
692103545Sjmallett	    DEBUGF(ARCH, ("Attempted to redefine an SVR4 name table\n"));
693138232Sharti	    return (-1);
69418730Ssteve	}
69518730Ssteve
69618730Ssteve	/*
69718730Ssteve	 * This is a table of archive names, so we build one for
69818730Ssteve	 * ourselves
69918730Ssteve	 */
70018730Ssteve	ar->fnametab = emalloc(size);
70118730Ssteve	ar->fnamesize = size;
70218730Ssteve
70318730Ssteve	if (fread(ar->fnametab, size, 1, arch) != 1) {
704103545Sjmallett	    DEBUGF(ARCH, ("Reading an SVR4 name table failed\n"));
705138232Sharti	    return (-1);
70618730Ssteve	}
70718730Ssteve	eptr = ar->fnametab + size;
70818730Ssteve	for (entry = 0, ptr = ar->fnametab; ptr < eptr; ptr++)
70918730Ssteve	    switch (*ptr) {
71018730Ssteve	    case '/':
71118730Ssteve		entry++;
71218730Ssteve		*ptr = '\0';
71318730Ssteve		break;
71418730Ssteve
71518730Ssteve	    case '\n':
71618730Ssteve		break;
71718730Ssteve
71818730Ssteve	    default:
71918730Ssteve		break;
72018730Ssteve	    }
721103545Sjmallett	DEBUGF(ARCH, ("Found svr4 archive name table with %zu entries\n", entry));
722138232Sharti	return (0);
72318730Ssteve    }
72418730Ssteve
72518730Ssteve    if (name[1] == ' ' || name[1] == '\0')
726138232Sharti	return (2);
72718730Ssteve
728138232Sharti    entry = (size_t)strtol(&name[1], &eptr, 0);
72918730Ssteve    if ((*eptr != ' ' && *eptr != '\0') || eptr == &name[1]) {
730103545Sjmallett	DEBUGF(ARCH, ("Could not parse SVR4 name %s\n", name));
731138232Sharti	return (2);
73218730Ssteve    }
73318730Ssteve    if (entry >= ar->fnamesize) {
734103545Sjmallett	DEBUGF(ARCH, ("SVR4 entry offset %s is greater than %zu\n",
735103545Sjmallett	       name, ar->fnamesize));
736138232Sharti	return (2);
73718730Ssteve    }
73818730Ssteve
739103545Sjmallett    DEBUGF(ARCH, ("Replaced %s with %s\n", name, &ar->fnametab[entry]));
74018730Ssteve
741138232Sharti    strncpy(name, &ar->fnametab[entry], MAXPATHLEN);
74298501Sjmallett    name[MAXPATHLEN] = '\0';
743138232Sharti    return (1);
74418730Ssteve}
74518730Ssteve#endif
74618730Ssteve
74718730Ssteve
74818730Ssteve/*-
74918730Ssteve *-----------------------------------------------------------------------
7501590Srgrimes * ArchFindMember --
7511590Srgrimes *	Locate a member of an archive, given the path of the archive and
7521590Srgrimes *	the path of the desired member. If the archive is to be modified,
753104696Sjmallett *	the mode should be "r+", if not, it should be "r".  arhPtr is a
754104696Sjmallett *	poitner to the header structure to fill in.
7551590Srgrimes *
7561590Srgrimes * Results:
7571590Srgrimes *	An FILE *, opened for reading and writing, positioned at the
7581590Srgrimes *	start of the member's struct ar_hdr, or NULL if the member was
7591590Srgrimes *	nonexistent. The current struct ar_hdr for member.
7601590Srgrimes *
7611590Srgrimes * Side Effects:
7621590Srgrimes *	The passed struct ar_hdr structure is filled in.
7631590Srgrimes *
7641590Srgrimes *-----------------------------------------------------------------------
7651590Srgrimes */
7661590Srgrimesstatic FILE *
767138232ShartiArchFindMember(char *archive, char *member, struct ar_hdr *arhPtr, char *mode)
7681590Srgrimes{
7691590Srgrimes    FILE *	  arch;	      /* Stream to archive */
7701590Srgrimes    int		  size;       /* Size of archive member */
7711590Srgrimes    char	  *cp;	      /* Useful character pointer */
7721590Srgrimes    char	  magic[SARMAG];
77398138Sjmallett    size_t	  len, tlen;
7741590Srgrimes
775138232Sharti    arch = fopen(archive, mode);
77649938Shoek    if (arch == NULL) {
77749938Shoek	return (NULL);
7781590Srgrimes    }
7798874Srgrimes
7801590Srgrimes    /*
7811590Srgrimes     * We use the ARMAG string to make sure this is an archive we
7821590Srgrimes     * can handle...
7831590Srgrimes     */
784138232Sharti    if ((fread(magic, SARMAG, 1, arch) != 1) ||
785138232Sharti    	(strncmp(magic, ARMAG, SARMAG) != 0)) {
786138232Sharti	    fclose(arch);
78749938Shoek	    return (NULL);
7881590Srgrimes    }
7891590Srgrimes
7901590Srgrimes    /*
7911590Srgrimes     * Because of space constraints and similar things, files are archived
7921590Srgrimes     * using their final path components, not the entire thing, so we need
7931590Srgrimes     * to point 'member' to the final component, if there is one, to make
7941590Srgrimes     * the comparisons easier...
7951590Srgrimes     */
796138232Sharti    cp = strrchr(member, '/');
79752109Sjulian    if ((cp != NULL) && (strcmp(member, RANLIBMAG) != 0)) {
7981590Srgrimes	member = cp + 1;
7991590Srgrimes    }
800138232Sharti    len = tlen = strlen(member);
801138232Sharti    if (len > sizeof(arhPtr->ar_name)) {
802138232Sharti	tlen = sizeof(arhPtr->ar_name);
8031590Srgrimes    }
8048874Srgrimes
805138264Sharti    while (fread(arhPtr, sizeof(struct ar_hdr), 1, arch) == 1) {
806138232Sharti	if (strncmp(arhPtr->ar_fmag, ARFMAG, sizeof(arhPtr->ar_fmag) ) != 0) {
8071590Srgrimes	     /*
8081590Srgrimes	      * The header is bogus, so the archive is bad
8091590Srgrimes	      * and there's no way we can recover...
8101590Srgrimes	      */
811138232Sharti	     fclose(arch);
81249938Shoek	     return (NULL);
813138232Sharti	} else if (strncmp(member, arhPtr->ar_name, tlen) == 0) {
8141590Srgrimes	    /*
8151590Srgrimes	     * If the member's name doesn't take up the entire 'name' field,
8161590Srgrimes	     * we have to be careful of matching prefixes. Names are space-
8171590Srgrimes	     * padded to the right, so if the character in 'name' at the end
8181590Srgrimes	     * of the matched string is anything but a space, this isn't the
8191590Srgrimes	     * member we sought.
8201590Srgrimes	     */
8215814Sjkh	    if (tlen != sizeof(arhPtr->ar_name) && arhPtr->ar_name[tlen] != ' '){
8225814Sjkh		goto skip;
8231590Srgrimes	    } else {
8241590Srgrimes		/*
8251590Srgrimes		 * To make life easier, we reposition the file at the start
8261590Srgrimes		 * of the header we just read before we return the stream.
8271590Srgrimes		 * In a more general situation, it might be better to leave
8281590Srgrimes		 * the file at the actual member, rather than its header, but
8291590Srgrimes		 * not here...
8301590Srgrimes		 */
831138232Sharti		fseek(arch, -sizeof(struct ar_hdr), SEEK_CUR);
8321590Srgrimes		return (arch);
8331590Srgrimes	    }
8345814Sjkh	} else
8355814Sjkh#ifdef AR_EFMT1
8365814Sjkh		/*
8375814Sjkh		 * BSD 4.4 extended AR format: #1/<namelen>, with name as the
8385814Sjkh		 * first <namelen> bytes of the file
8395814Sjkh		 */
8405814Sjkh	    if (strncmp(arhPtr->ar_name, AR_EFMT1,
8415814Sjkh					sizeof(AR_EFMT1) - 1) == 0 &&
8425814Sjkh		isdigit(arhPtr->ar_name[sizeof(AR_EFMT1) - 1])) {
8435814Sjkh
8445814Sjkh		unsigned int elen = atoi(&arhPtr->ar_name[sizeof(AR_EFMT1)-1]);
8455814Sjkh		char ename[MAXPATHLEN];
8465814Sjkh
8475814Sjkh		if (elen > MAXPATHLEN) {
848138232Sharti			fclose(arch);
8495814Sjkh			return NULL;
8505814Sjkh		}
851138232Sharti		if (fread(ename, elen, 1, arch) != 1) {
852138232Sharti			fclose(arch);
8535814Sjkh			return NULL;
8545814Sjkh		}
8555814Sjkh		ename[elen] = '\0';
856103508Sjmallett		/*
857103508Sjmallett		 * XXX choose one.
858103508Sjmallett		 */
8595814Sjkh		if (DEBUG(ARCH) || DEBUG(MAKE)) {
8605814Sjkh		    printf("ArchFind: Extended format entry for %s\n", ename);
8615814Sjkh		}
8625814Sjkh		if (strncmp(ename, member, len) == 0) {
8635814Sjkh			/* Found as extended name */
864138232Sharti			fseek(arch, -sizeof(struct ar_hdr) - elen, SEEK_CUR);
8655814Sjkh			return (arch);
8665814Sjkh		}
867138232Sharti		fseek(arch, -elen, SEEK_CUR);
8685814Sjkh		goto skip;
8695814Sjkh	} else
8705814Sjkh#endif
8715814Sjkh	{
8725814Sjkhskip:
8731590Srgrimes	    /*
8741590Srgrimes	     * This isn't the member we're after, so we need to advance the
8751590Srgrimes	     * stream's pointer to the start of the next header. Files are
8761590Srgrimes	     * padded with newlines to an even-byte boundary, so we need to
8771590Srgrimes	     * extract the size of the file from the 'size' field of the
8781590Srgrimes	     * header and round it up during the seek.
8791590Srgrimes	     */
880138232Sharti	    arhPtr->ar_size[sizeof(arhPtr->ar_size) - 1] = '\0';
881138232Sharti	    size = (int)strtol(arhPtr->ar_size, NULL, 10);
882138232Sharti	    fseek(arch, (size + 1) & ~1, SEEK_CUR);
8831590Srgrimes	}
8841590Srgrimes    }
8851590Srgrimes
8861590Srgrimes    /*
8871590Srgrimes     * We've looked everywhere, but the member is not to be found. Close the
8881590Srgrimes     * archive and return NULL -- an error.
8891590Srgrimes     */
890138232Sharti    fclose(arch);
89149938Shoek    return (NULL);
8921590Srgrimes}
8931590Srgrimes
8941590Srgrimes/*-
8951590Srgrimes *-----------------------------------------------------------------------
8961590Srgrimes * Arch_Touch --
8971590Srgrimes *	Touch a member of an archive.
8981590Srgrimes *
8991590Srgrimes * Results:
9001590Srgrimes *	The 'time' field of the member's header is updated.
9011590Srgrimes *
9021590Srgrimes * Side Effects:
9031590Srgrimes *	The modification time of the entire archive is also changed.
9041590Srgrimes *	For a library, this could necessitate the re-ranlib'ing of the
9051590Srgrimes *	whole thing.
9061590Srgrimes *
9071590Srgrimes *-----------------------------------------------------------------------
9081590Srgrimes */
9091590Srgrimesvoid
910138232ShartiArch_Touch(GNode *gn)
9111590Srgrimes{
9121590Srgrimes    FILE *	  arch;	  /* Stream open to archive, positioned properly */
9131590Srgrimes    struct ar_hdr arh;	  /* Current header describing member */
9145814Sjkh    char *p1, *p2;
9151590Srgrimes
916138232Sharti    arch = ArchFindMember(Var_Value(ARCHIVE, gn, &p1),
917138232Sharti			  Var_Value(TARGET, gn, &p2),
9181590Srgrimes			  &arh, "r+");
919105826Sjmallett    free(p1);
920105826Sjmallett    free(p2);
921138232Sharti    snprintf(arh.ar_date, sizeof(arh.ar_date), "%-12ld", (long)now);
9221590Srgrimes
92349938Shoek    if (arch != NULL) {
924138264Sharti	fwrite(&arh, sizeof(struct ar_hdr), 1, arch);
925138232Sharti	fclose(arch);
9261590Srgrimes    }
9271590Srgrimes}
9281590Srgrimes
9291590Srgrimes/*-
9301590Srgrimes *-----------------------------------------------------------------------
9311590Srgrimes * Arch_TouchLib --
9321590Srgrimes *	Given a node which represents a library, touch the thing, making
9331590Srgrimes *	sure that the table of contents also is touched.
9341590Srgrimes *
9351590Srgrimes * Results:
9361590Srgrimes *	None.
9371590Srgrimes *
9381590Srgrimes * Side Effects:
9391590Srgrimes *	Both the modification time of the library and of the RANLIBMAG
9401590Srgrimes *	member are set to 'now'.
9411590Srgrimes *
9421590Srgrimes *-----------------------------------------------------------------------
9431590Srgrimes */
9441590Srgrimesvoid
945138232ShartiArch_TouchLib(GNode *gn)
9461590Srgrimes{
94718730Ssteve#ifdef RANLIBMAG
9481590Srgrimes    FILE *	    arch;	/* Stream open to archive */
9491590Srgrimes    struct ar_hdr   arh;      	/* Header describing table of contents */
95018730Ssteve    struct utimbuf  times;	/* Times for utime() call */
9511590Srgrimes
952138232Sharti    arch = ArchFindMember(gn->path, RANLIBMAG, &arh, "r+");
95369390Swill    snprintf(arh.ar_date, sizeof(arh.ar_date), "%-12ld", (long) now);
9541590Srgrimes
95549938Shoek    if (arch != NULL) {
956138264Sharti	fwrite(&arh, sizeof(struct ar_hdr), 1, arch);
957138232Sharti	fclose(arch);
9581590Srgrimes
95918730Ssteve	times.actime = times.modtime = now;
96018730Ssteve	utime(gn->path, &times);
9611590Srgrimes    }
96218730Ssteve#endif
9631590Srgrimes}
9641590Srgrimes
9651590Srgrimes/*-
9661590Srgrimes *-----------------------------------------------------------------------
9671590Srgrimes * Arch_MTime --
968104696Sjmallett *	Return the modification time of a member of an archive, given its
969104696Sjmallett *	name.
9701590Srgrimes *
9711590Srgrimes * Results:
972138232Sharti *	The modification time(seconds).
9731590Srgrimes *
9741590Srgrimes * Side Effects:
9751590Srgrimes *	The mtime field of the given node is filled in with the value
9761590Srgrimes *	returned by the function.
9771590Srgrimes *
9781590Srgrimes *-----------------------------------------------------------------------
9791590Srgrimes */
9801590Srgrimesint
981104696SjmallettArch_MTime(GNode *gn)
9821590Srgrimes{
9831590Srgrimes    struct ar_hdr *arhPtr;    /* Header of desired member */
9841590Srgrimes    int		  modTime;    /* Modification time as an integer */
9855814Sjkh    char *p1, *p2;
9861590Srgrimes
987138232Sharti    arhPtr = ArchStatMember(Var_Value(ARCHIVE, gn, &p1),
988138232Sharti			     Var_Value(TARGET, gn, &p2),
9891590Srgrimes			     TRUE);
990105826Sjmallett    free(p1);
991105826Sjmallett    free(p2);
9925814Sjkh
99349938Shoek    if (arhPtr != NULL) {
994138232Sharti	modTime = (int)strtol(arhPtr->ar_date, NULL, 10);
9951590Srgrimes    } else {
9961590Srgrimes	modTime = 0;
9971590Srgrimes    }
9981590Srgrimes
9991590Srgrimes    gn->mtime = modTime;
10001590Srgrimes    return (modTime);
10011590Srgrimes}
10021590Srgrimes
10031590Srgrimes/*-
10041590Srgrimes *-----------------------------------------------------------------------
10051590Srgrimes * Arch_MemMTime --
10061590Srgrimes *	Given a non-existent archive member's node, get its modification
10071590Srgrimes *	time from its archived form, if it exists.
10081590Srgrimes *
10091590Srgrimes * Results:
10101590Srgrimes *	The modification time.
10111590Srgrimes *
10121590Srgrimes * Side Effects:
10131590Srgrimes *	The mtime field is filled in.
10141590Srgrimes *
10151590Srgrimes *-----------------------------------------------------------------------
10161590Srgrimes */
10171590Srgrimesint
1018138232ShartiArch_MemMTime(GNode *gn)
10191590Srgrimes{
1020138512Sharti    LstNode 	  *ln;
10211590Srgrimes    GNode   	  *pgn;
10221590Srgrimes    char    	  *nameStart,
10231590Srgrimes		  *nameEnd;
10241590Srgrimes
1025138916Sharti    for (ln = Lst_First(&gn->parents); ln != NULL; ln = Lst_Succ(ln)) {
1026138264Sharti	pgn = Lst_Datum(ln);
10271590Srgrimes
10281590Srgrimes	if (pgn->type & OP_ARCHV) {
10291590Srgrimes	    /*
10301590Srgrimes	     * If the parent is an archive specification and is being made
10311590Srgrimes	     * and its member's name matches the name of the node we were
10321590Srgrimes	     * given, record the modification time of the parent in the
10331590Srgrimes	     * child. We keep searching its parents in case some other
10341590Srgrimes	     * parent requires this child to exist...
10351590Srgrimes	     */
1036138232Sharti	    nameStart = strchr(pgn->name, '(') + 1;
1037138232Sharti	    nameEnd = strchr(nameStart, ')');
10381590Srgrimes
10391590Srgrimes	    if (pgn->make &&
10401590Srgrimes		strncmp(nameStart, gn->name, nameEnd - nameStart) == 0) {
10411590Srgrimes				     gn->mtime = Arch_MTime(pgn);
10421590Srgrimes	    }
10431590Srgrimes	} else if (pgn->make) {
10441590Srgrimes	    /*
10451590Srgrimes	     * Something which isn't a library depends on the existence of
10461590Srgrimes	     * this target, so it needs to exist.
10471590Srgrimes	     */
10481590Srgrimes	    gn->mtime = 0;
10491590Srgrimes	    break;
10501590Srgrimes	}
10511590Srgrimes    }
10521590Srgrimes    return (gn->mtime);
10531590Srgrimes}
10541590Srgrimes
10551590Srgrimes/*-
10561590Srgrimes *-----------------------------------------------------------------------
10571590Srgrimes * Arch_FindLib --
1058104696Sjmallett *	Search for a named library along the given search path.
10591590Srgrimes *
10601590Srgrimes * Results:
10611590Srgrimes *	None.
10621590Srgrimes *
10631590Srgrimes * Side Effects:
10641590Srgrimes *	The node's 'path' field is set to the found path (including the
10651590Srgrimes *	actual file name, not -l...). If the system can handle the -L
10661590Srgrimes *	flag when linking (or we cannot find the library), we assume that
10671590Srgrimes *	the user has placed the .LIBRARIES variable in the final linking
10681590Srgrimes *	command (or the linker will know where to find it) and set the
10691590Srgrimes *	TARGET variable for this node to be the node's name. Otherwise,
10701590Srgrimes *	we set the TARGET variable to be the full path of the library,
10711590Srgrimes *	as returned by Dir_FindFile.
10721590Srgrimes *
10731590Srgrimes *-----------------------------------------------------------------------
10741590Srgrimes */
10751590Srgrimesvoid
1076138512ShartiArch_FindLib(GNode *gn, Lst *path)
10771590Srgrimes{
10781590Srgrimes    char	    *libName;   /* file name for archive */
107969390Swill    size_t	    sz;
10801590Srgrimes
108170080Sdes    sz = strlen(gn->name) + 4;
1082138264Sharti    libName = emalloc(sz);
108369390Swill    snprintf(libName, sz, "lib%s.a", &gn->name[2]);
10841590Srgrimes
1085138232Sharti    gn->path = Dir_FindFile(libName, path);
10861590Srgrimes
1087138232Sharti    free(libName);
10881590Srgrimes
10891590Srgrimes#ifdef LIBRARIES
1090138232Sharti    Var_Set(TARGET, gn->name, gn);
10911590Srgrimes#else
1092138232Sharti    Var_Set(TARGET, gn->path == NULL ? gn->name : gn->path, gn);
109318730Ssteve#endif /* LIBRARIES */
10941590Srgrimes}
10951590Srgrimes
10961590Srgrimes/*-
10971590Srgrimes *-----------------------------------------------------------------------
10981590Srgrimes * Arch_LibOODate --
10991590Srgrimes *	Decide if a node with the OP_LIB attribute is out-of-date. Called
1100104696Sjmallett *	from Make_OODate to make its life easier, with the library's
1101104696Sjmallett *	graph node.
11021590Srgrimes *
11031590Srgrimes *	There are several ways for a library to be out-of-date that are
11041590Srgrimes *	not available to ordinary files. In addition, there are ways
11051590Srgrimes *	that are open to regular files that are not available to
11061590Srgrimes *	libraries. A library that is only used as a source is never
11071590Srgrimes *	considered out-of-date by itself. This does not preclude the
11081590Srgrimes *	library's modification time from making its parent be out-of-date.
11091590Srgrimes *	A library will be considered out-of-date for any of these reasons,
11101590Srgrimes *	given that it is a target on a dependency line somewhere:
11111590Srgrimes *	    Its modification time is less than that of one of its
11121590Srgrimes *	    	  sources (gn->mtime < gn->cmtime).
11131590Srgrimes *	    Its modification time is greater than the time at which the
11141590Srgrimes *	    	  make began (i.e. it's been modified in the course
11151590Srgrimes *	    	  of the make, probably by archiving).
11165814Sjkh *	    The modification time of one of its sources is greater than
11175814Sjkh *		  the one of its RANLIBMAG member (i.e. its table of contents
11185814Sjkh *	    	  is out-of-date). We don't compare of the archive time
11195814Sjkh *		  vs. TOC time because they can be too close. In my
11205814Sjkh *		  opinion we should not bother with the TOC at all since
11215814Sjkh *		  this is used by 'ar' rules that affect the data contents
11225814Sjkh *		  of the archive, not by ranlib rules, which affect the
11238874Srgrimes *		  TOC.
11241590Srgrimes *
11251590Srgrimes * Results:
11261590Srgrimes *	TRUE if the library is out-of-date. FALSE otherwise.
11271590Srgrimes *
11281590Srgrimes * Side Effects:
11291590Srgrimes *	The library will be hashed if it hasn't been already.
11301590Srgrimes *
11311590Srgrimes *-----------------------------------------------------------------------
11321590Srgrimes */
11331590SrgrimesBoolean
1134138232ShartiArch_LibOODate(GNode *gn)
11351590Srgrimes{
11361590Srgrimes    Boolean 	  oodate;
11378874Srgrimes
1138138916Sharti    if (OP_NOP(gn->type) && Lst_IsEmpty(&gn->children)) {
11391590Srgrimes	oodate = FALSE;
11401590Srgrimes    } else if ((gn->mtime > now) || (gn->mtime < gn->cmtime)) {
11411590Srgrimes	oodate = TRUE;
11421590Srgrimes    } else {
114318730Ssteve#ifdef RANLIBMAG
11441590Srgrimes	struct ar_hdr  	*arhPtr;    /* Header for __.SYMDEF */
11451590Srgrimes	int 	  	modTimeTOC; /* The table-of-contents's mod time */
11461590Srgrimes
1147138232Sharti	arhPtr = ArchStatMember(gn->path, RANLIBMAG, FALSE);
11481590Srgrimes
114949938Shoek	if (arhPtr != NULL) {
1150138232Sharti	    modTimeTOC = (int)strtol(arhPtr->ar_date, NULL, 10);
11511590Srgrimes
1152103508Sjmallett	    /* XXX choose one. */
11531590Srgrimes	    if (DEBUG(ARCH) || DEBUG(MAKE)) {
11541590Srgrimes		printf("%s modified %s...", RANLIBMAG, Targ_FmtTime(modTimeTOC));
11551590Srgrimes	    }
11565814Sjkh	    oodate = (gn->cmtime > modTimeTOC);
11571590Srgrimes	} else {
11581590Srgrimes	    /*
11591590Srgrimes	     * A library w/o a table of contents is out-of-date
11601590Srgrimes	     */
11611590Srgrimes	    if (DEBUG(ARCH) || DEBUG(MAKE)) {
11621590Srgrimes		printf("No t.o.c....");
11631590Srgrimes	    }
11641590Srgrimes	    oodate = TRUE;
11651590Srgrimes	}
116618730Ssteve#else
116740392Sjdp	oodate = (gn->mtime == 0); /* out-of-date if not present */
116818730Ssteve#endif
11691590Srgrimes    }
11701590Srgrimes    return (oodate);
11711590Srgrimes}
11721590Srgrimes
11731590Srgrimes/*-
11741590Srgrimes *-----------------------------------------------------------------------
11751590Srgrimes * Arch_Init --
11761590Srgrimes *	Initialize things for this module.
11771590Srgrimes *
11781590Srgrimes * Results:
11791590Srgrimes *	None.
11801590Srgrimes *
11811590Srgrimes *-----------------------------------------------------------------------
11821590Srgrimes */
11831590Srgrimesvoid
1184138232ShartiArch_Init(void)
11851590Srgrimes{
11861590Srgrimes}
11875814Sjkh
11885814Sjkh/*-
11895814Sjkh *-----------------------------------------------------------------------
11905814Sjkh * Arch_End --
11915814Sjkh *	Cleanup things for this module.
11925814Sjkh *
11935814Sjkh * Results:
11945814Sjkh *	None.
11955814Sjkh *
11965814Sjkh * Side Effects:
11975814Sjkh *	The 'archives' list is freed
11985814Sjkh *
11995814Sjkh *-----------------------------------------------------------------------
12005814Sjkh */
12015814Sjkhvoid
1202138232ShartiArch_End(void)
12035814Sjkh{
1204138264Sharti
1205138916Sharti    Lst_Destroy(&archives, ArchFree);
12065814Sjkh}
1207