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