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