Lst_AtEnd(archives, (ClientData)ar);
Lst_AtEnd(archives, ar);
static struct ar_hdr *ArchStatMember __P((char *, char *, Boolean));static FILE *ArchFindMember __P((char *, char *, struct ar_hdr *, char *));#if defined(__svr4__) || defined(__SVR4) || \    (defined(__OpenBSD__) && defined(__mips__)) || \    (defined(__OpenBSD__) && defined(__powerpc))#define SVR4ARCHIVESstatic int ArchSVR4Entry __P((Arch *, char *, size_t, FILE *));#endif#ifdef CLEANUP/*- *----------------------------------------------------------------------- * ArchFree -- *	Free memory used by an archive * * Results: *	None. * * Side Effects: *	None. * *----------------------------------------------------------------------- */static voidArchFree(ap)    ClientData ap;{    Arch *a = (Arch *) ap;    Hash_Search	  search;    Hash_Entry	  *entry;    /* Free memory from hash entries */    for (entry = Hash_EnumFirst(&a->members, &search);	 entry != NULL;	 entry = Hash_EnumNext(&search))	free((Address) Hash_GetValue (entry));    free(a->name);    efree(a->fnametab);    Hash_DeleteTable(&a->members);    free((Address) a);}#endif/*- *----------------------------------------------------------------------- * Arch_ParseArchive -- *	Parse the archive specification in the given line and find/create *	the nodes for the specified archive members, placing their nodes *	on the given list. * * Results: *	SUCCESS if it was a valid specification. The linePtr is updated *	to point to the first non-space after the archive spec. The *	nodes for the members are placed on the given list. * * Side Effects: *	Some nodes may be created. The given list is extended. * *----------------------------------------------------------------------- */ReturnStatusArch_ParseArchive (linePtr, nodeLst, ctxt)    char	    **linePtr;      /* Pointer to start of specification */    Lst	    	    nodeLst;   	    /* Lst on which to place the nodes */    GNode   	    *ctxt;  	    /* Context in which to expand variables */{    register char   *cp;	    /* Pointer into line */    GNode	    *gn;     	    /* New node */    char	    *libName;  	    /* Library-part of specification */    char	    *memName;  	    /* Member-part of specification */    char	    nameBuf[MAKE_BSIZE]; /* temporary place for node name */    char	    saveChar;  	    /* Ending delimiter of member-name */    Boolean 	    subLibName;	    /* TRUE if libName should have/had				     * variable substitution performed on it */    libName = *linePtr;    subLibName = FALSE;    for (cp = libName; *cp != '(' && *cp != '\0'; cp++) {	if (*cp == '$') {	    /*	     * Variable spec, so call the Var module to parse the puppy	     * so we can safely advance beyond it...	     */	    size_t 	length;	    Boolean	freeIt;	    char	*result;	    result=Var_Parse(cp, ctxt, TRUE, &length, &freeIt);	    if (result == var_Error) {		return(FAILURE);	    } else {		subLibName = TRUE;	    }	    if (freeIt) {		free(result);	    }	    cp += length-1;	}    }    *cp++ = '\0';    if (subLibName) {	libName = Var_Subst(libName, ctxt, TRUE);    }    for (;;) {	/*	 * First skip to the start of the member's name, mark that	 * place and skip to the end of it (either white-space or	 * a close paren).	 */	Boolean	doSubst = FALSE; /* TRUE if need to substitute in memName */	while (*cp != '\0' && *cp != ')' && isspace (*cp)) {	    cp++;	}	memName = cp;	while (*cp != '\0' && *cp != ')' && !isspace (*cp)) {	    if (*cp == '$') {		/*		 * Variable spec, so call the Var module to parse the puppy		 * so we can safely advance beyond it...		 */		size_t 	length;		Boolean	freeIt;		char	*result;		result=Var_Parse(cp, ctxt, TRUE, &length, &freeIt);		if (result == var_Error) {		    return(FAILURE);		} else {		    doSubst = TRUE;		}		if (freeIt) {		    free(result);		}		cp += length;	    } else {		cp++;	    }	}	/*	 * If the specification ends without a closing parenthesis,	 * chances are there's something wrong (like a missing backslash),	 * so it's better to return failure than allow such things to happen	 */	if (*cp == '\0') {	    printf("No closing parenthesis in archive specification\n");	    return (FAILURE);	}	/*	 * If we didn't move anywhere, we must be done	 */	if (cp == memName) {	    break;	}	saveChar = *cp;	*cp = '\0';	/*	 * XXX: This should be taken care of intelligently by	 * SuffExpandChildren, both for the archive and the member portions.	 */	/*	 * If member contains variables, try and substitute for them.	 * This will slow down archive specs with dynamic sources, of course,	 * since we'll be (non-)substituting them three times, but them's	 * the breaks -- we need to do this since SuffExpandChildren calls	 * us, otherwise we could assume the thing would be taken care of	 * later.	 */	if (doSubst) {	    char    *buf;	    char    *sacrifice;	    char    *oldMemName = memName;	    memName = Var_Subst(memName, ctxt, TRUE);	    /*	     * Now form an archive spec and recurse to deal with nested	     * variables and multi-word variable values.... The results	     * are just placed at the end of the nodeLst we're returning.	     */	    buf = sacrifice = emalloc(strlen(memName)+strlen(libName)+3);	    sprintf(buf, "%s(%s)", libName, memName);	    if (strchr(memName, '$') && strcmp(memName, oldMemName) == 0) {		/*		 * Must contain dynamic sources, so we can't deal with it now.		 * Just create an ARCHV node for the thing and let		 * SuffExpandChildren handle it...		 */		gn = Targ_FindNode(buf, TARG_CREATE);		if (gn == NULL) {		    free(buf);		    return(FAILURE);		} else {		    gn->type |= OP_ARCHV;		    Lst_AtEnd(nodeLst, (ClientData)gn);		}	    } else if (Arch_ParseArchive(&sacrifice, nodeLst, ctxt)!=SUCCESS) {		/*		 * Error in nested call -- free buffer and return FAILURE		 * ourselves.		 */		free(buf);		return(FAILURE);	    }	    /*	     * Free buffer and continue with our work.	     */	    free(buf);	} else if (Dir_HasWildcards(memName)) {	    Lst	  members = Lst_Init();	    char  *member;	    Dir_Expand(memName, dirSearchPath, members);	    while ((member = (char *)Lst_DeQueue(members)) != NULL) {		sprintf(nameBuf, "%s(%s)", libName, member);		free(member);		gn = Targ_FindNode (nameBuf, TARG_CREATE);		if (gn == NULL) {		    return (FAILURE);		} else {		    /*		     * We've found the node, but have to make sure the rest of		     * the world knows it's an archive member, without having		     * to constantly check for parentheses, so we type the		     * thing with the OP_ARCHV bit before we place it on the		     * end of the provided list.		     */		    gn->type |= OP_ARCHV;		    Lst_AtEnd(nodeLst, (ClientData)gn);		}	    }	    Lst_Destroy(members, NOFREE);	} else {	    sprintf(nameBuf, "%s(%s)", libName, memName);	    gn = Targ_FindNode (nameBuf, TARG_CREATE);	    if (gn == NULL) {		return (FAILURE);	    } else {		/*		 * We've found the node, but have to make sure the rest of the		 * world knows it's an archive member, without having to		 * constantly check for parentheses, so we type the thing with		 * the OP_ARCHV bit before we place it on the end of the		 * provided list.		 */		gn->type |= OP_ARCHV;		Lst_AtEnd(nodeLst, (ClientData)gn);	    }	}	if (doSubst) {	    free(memName);	}	*cp = saveChar;    }    /*     * If substituted libName, free it now, since we need it no longer.     */    if (subLibName) {	free(libName);    }    /*     * We promised the pointer would be set up at the next non-space, so     * we must advance cp there before setting *linePtr... (note that on     * entrance to the loop, cp is guaranteed to point at a ')')     */    do {	cp++;    } while (*cp != '\0' && isspace (*cp));    *linePtr = cp;    return (SUCCESS);}/*- *----------------------------------------------------------------------- * ArchFindArchive -- *	See if the given archive is the one we are looking for. Called *	From ArchStatMember and ArchFindMember via Lst_Find. * * Results: *	0 if it is, non-zero if it isn't. * * Side Effects: *	None. * *----------------------------------------------------------------------- */static intArchFindArchive (ar, archName)    ClientData	  ar;	      	  /* Current list element */    ClientData	  archName;  	  /* Name we want */{    return (strcmp ((char *) archName, ((Arch *) ar)->name));}/*- *----------------------------------------------------------------------- * ArchStatMember -- *	Locate a member of an archive, given the path of the archive and *	the path of the desired member. * * Results: *	A pointer to the current struct ar_hdr structure for the member. Note *	That no position is returned, so this is not useful for touching *	archive members. This is mostly because we have no assurances that *	The archive will remain constant after we read all the headers, so *	there's not much point in remembering the position... * * Side Effects: * *----------------------------------------------------------------------- */static struct ar_hdr *ArchStatMember (archive, member, hash)    char	  *archive;   /* Path to the archive */    char	  *member;    /* Name of member. If it is a path, only the			       * last component is used. */    Boolean	  hash;	      /* TRUE if archive should be hashed if not    			       * already so. */{#define AR_MAX_NAME_LEN	    (sizeof(arh.ar_name)-1)    FILE *	  arch;	      /* Stream to archive */    int		  size;       /* Size of archive member */    char	  *cp;	      /* Useful character pointer */    char	  magic[SARMAG];    LstNode	  ln;	      /* Lst member containing archive descriptor */    Arch	  *ar;	      /* Archive descriptor */    Hash_Entry	  *he;	      /* Entry containing member's description */    struct ar_hdr arh;        /* archive-member header for reading archive */    char	  memName[MAXPATHLEN+1];    	    	    	    /* Current member name while hashing. */    /*     * Because of space constraints and similar things, files are archived     * using their final path components, not the entire thing, so we need     * to point 'member' to the final component, if there is one, to make     * the comparisons easier...     */    cp = strrchr (member, '/');    if (cp != NULL)	member = cp + 1;    ln = Lst_Find(archives, ArchFindArchive, (ClientData)archive);    if (ln != NULL) {	ar = (Arch *) Lst_Datum (ln);	he = Hash_FindEntry (&ar->members, member);	if (he != NULL) {	    return ((struct ar_hdr *) Hash_GetValue (he));	} else {	    /* Try truncated name */	    char copy[AR_MAX_NAME_LEN+1];	    int len = strlen (member);	    if (len > AR_MAX_NAME_LEN) {		len = AR_MAX_NAME_LEN;		strncpy(copy, member, AR_MAX_NAME_LEN);		copy[AR_MAX_NAME_LEN] = '\0';	    }	    if ((he = Hash_FindEntry (&ar->members, copy)) != NULL)		return ((struct ar_hdr *) Hash_GetValue (he));	    return (NULL);	}    }    if (!hash) {	/*	 * Caller doesn't want the thing hashed, just use ArchFindMember	 * to read the header for the member out and close down the stream	 * again. Since the archive is not to be hashed, we assume there's	 * no need to allocate extra room for the header we're returning,	 * so just declare it static.	 */	 static struct ar_hdr	sarh;	 arch = ArchFindMember(archive, member, &sarh, "r");	if (arch == NULL) {	    return (NULL);	} else {	    fclose(arch);	    return (&sarh);	}    }    /*     * We don't have this archive on the list yet, so we want to find out     * everything that's in it and cache it so we can get at it quickly.     */    arch = fopen (archive, "r");    if (arch == NULL) {	return (NULL);    }    /*     * We use the ARMAG string to make sure this is an archive we     * can handle...     */    if ((fread (magic, SARMAG, 1, arch) != 1) ||    	(strncmp (magic, ARMAG, SARMAG) != 0)) {	    fclose (arch);	    return (NULL);    }    ar = (Arch *)emalloc (sizeof (Arch));    ar->name = estrdup (archive);    ar->fnametab = NULL;    ar->fnamesize = 0;    Hash_InitTable (&ar->members, -1);    memName[AR_MAX_NAME_LEN] = '\0';    while (fread ((char *)&arh, sizeof (struct ar_hdr), 1, arch) == 1) {	if (strncmp ( arh.ar_fmag, ARFMAG, sizeof (arh.ar_fmag)) != 0) {	    /*	     * The header is bogus, so the archive is bad	     * and there's no way we can recover...	     */	    goto badarch;	} else {	    /*	     * We need to advance the stream's pointer to the start of the	     * next header. Files are padded with newlines to an even-byte	     * boundary, so we need to extract the size of the file from the	     * 'size' field of the header and round it up during the seek.	     */	    arh.ar_size[sizeof(arh.ar_size)-1] = '\0';	    size = (int) strtol(arh.ar_size, NULL, 10);	    (void) strncpy (memName, arh.ar_name, sizeof(arh.ar_name));	    for (cp = &memName[AR_MAX_NAME_LEN]; *cp == ' '; cp--) {		continue;	    }	    cp[1] = '\0';#ifdef SVR4ARCHIVES	    /*	     * svr4 names are slash terminated. Also svr4 extended AR format.	     */	    if (memName[0] == '/') {		/*		 * svr4 magic mode; handle it		 */		switch (ArchSVR4Entry(ar, memName, size, arch)) {		case -1:  /* Invalid data */		    goto badarch;		case 0:	  /* List of files entry */		    continue;		default:  /* Got the entry */		    break;		}	    }	    else {		if (cp[0] == '/')		    cp[0] = '\0';	    }#endif#ifdef AR_EFMT1	    /*	     * BSD 4.4 extended AR format: #1/<namelen>, with name as the	     * first <namelen> bytes of the file	     */	    if (strncmp(memName, AR_EFMT1, sizeof(AR_EFMT1) - 1) == 0 &&		isdigit(memName[sizeof(AR_EFMT1) - 1])) {		unsigned int elen = atoi(&memName[sizeof(AR_EFMT1)-1]);		if (elen > MAXPATHLEN)			goto badarch;		if (fread (memName, elen, 1, arch) != 1)			goto badarch;		memName[elen] = '\0';		fseek (arch, -elen, SEEK_CUR);		if (DEBUG(ARCH) || DEBUG(MAKE)) {		    printf("ArchStat: Extended format entry for %s\n", memName);		}	    }#endif	    he = Hash_CreateEntry (&ar->members, memName, NULL);	    Hash_SetValue (he, (ClientData)emalloc (sizeof (struct ar_hdr)));	    memcpy ((Address)Hash_GetValue (he), (Address)&arh,		sizeof (struct ar_hdr));	}	fseek (arch, (size + 1) & ~1, SEEK_CUR);    }    fclose (arch);    Lst_AtEnd(archives, (ClientData)ar);    /*     * Now that the archive has been read and cached, we can look into     * the hash table to find the desired member's header.     */    he = Hash_FindEntry (&ar->members, member);    if (he != NULL) {	return ((struct ar_hdr *) Hash_GetValue (he));    } else {	return (NULL);    }badarch:    fclose (arch);    Hash_DeleteTable (&ar->members);    efree(ar->fnametab);    free ((Address)ar);    return (NULL);}