1/*	$NetBSD: arch.c,v 1.219 2024/06/02 15:31:25 rillig Exp $	*/
2
3/*
4 * Copyright (c) 1988, 1989, 1990, 1993
5 *	The Regents of the University of California.  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. Neither the name of the University nor the names of its contributors
19 *    may be used to endorse or promote products derived from this software
20 *    without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35/*
36 * Copyright (c) 1989 by Berkeley Softworks
37 * All rights reserved.
38 *
39 * This code is derived from software contributed to Berkeley by
40 * Adam de Boor.
41 *
42 * Redistribution and use in source and binary forms, with or without
43 * modification, are permitted provided that the following conditions
44 * are met:
45 * 1. Redistributions of source code must retain the above copyright
46 *    notice, this list of conditions and the following disclaimer.
47 * 2. Redistributions in binary form must reproduce the above copyright
48 *    notice, this list of conditions and the following disclaimer in the
49 *    documentation and/or other materials provided with the distribution.
50 * 3. All advertising materials mentioning features or use of this software
51 *    must display the following acknowledgement:
52 *	This product includes software developed by the University of
53 *	California, Berkeley and its contributors.
54 * 4. Neither the name of the University nor the names of its contributors
55 *    may be used to endorse or promote products derived from this software
56 *    without specific prior written permission.
57 *
58 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
59 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
60 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
61 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
62 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
63 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
64 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
65 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
66 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
67 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
68 * SUCH DAMAGE.
69 */
70
71/*
72 * Manipulate libraries, archives and their members.
73 *
74 * The first time an archive is referenced, all of its members' headers are
75 * read and cached and the archive closed again.  All cached archives are kept
76 * on a list which is searched each time an archive member is referenced.
77 *
78 * The interface to this module is:
79 *
80 *	Arch_Init	Initialize this module.
81 *
82 *	Arch_End	Clean up this module.
83 *
84 *	Arch_ParseArchive
85 *			Parse an archive specification such as
86 *			"archive.a(member1 member2)".
87 *
88 *	Arch_Touch	Alter the modification time of the archive
89 *			member described by the given node to be
90 *			the time when make was started.
91 *
92 *	Arch_TouchLib	Update the modification time of the library
93 *			described by the given node. This is special
94 *			because it also updates the modification time
95 *			of the library's table of contents.
96 *
97 *	Arch_UpdateMTime
98 *			Find the modification time of a member of
99 *			an archive *in the archive* and place it in the
100 *			member's GNode.
101 *
102 *	Arch_UpdateMemberMTime
103 *			Find the modification time of a member of
104 *			an archive. Called when the member doesn't
105 *			already exist. Looks in the archive for the
106 *			modification time. Returns the modification
107 *			time.
108 *
109 *	Arch_FindLib	Search for a library along a path. The
110 *			library name in the GNode should be in
111 *			-l<name> format.
112 *
113 *	Arch_LibOODate	Decide if a library node is out-of-date.
114 */
115
116#include <sys/types.h>
117#include <sys/stat.h>
118#include <sys/time.h>
119#include <sys/param.h>
120
121#include <ar.h>
122#include <utime.h>
123
124#include "make.h"
125#include "dir.h"
126#include "config.h"
127
128/*	"@(#)arch.c	8.2 (Berkeley) 1/2/94"	*/
129MAKE_RCSID("$NetBSD: arch.c,v 1.219 2024/06/02 15:31:25 rillig Exp $");
130
131typedef struct List ArchList;
132typedef struct ListNode ArchListNode;
133
134static ArchList archives;	/* The archives we've already examined */
135
136typedef struct Arch {
137	char *name;
138	HashTable members;	/* All the members of the archive described
139				 * by <name, struct ar_hdr *> key/value pairs */
140	char *fnametab;		/* Extended name table strings */
141	size_t fnamesize;	/* Size of the string table */
142} Arch;
143
144static FILE *ArchFindMember(const char *, const char *,
145			    struct ar_hdr *, const char *);
146#if defined(__svr4__) || defined(__SVR4) || defined(__ELF__)
147#define SVR4ARCHIVES
148static int ArchSVR4Entry(Arch *, char *, size_t, FILE *);
149#endif
150
151
152#ifdef CLEANUP
153static void
154ArchFree(Arch *a)
155{
156	HashIter hi;
157
158	HashIter_Init(&hi, &a->members);
159	while (HashIter_Next(&hi))
160		free(hi.entry->value);
161
162	free(a->name);
163	free(a->fnametab);
164	HashTable_Done(&a->members);
165	free(a);
166}
167#endif
168
169/* Return "archive(member)". */
170MAKE_ATTR_NOINLINE static char *
171FullName(const char *archive, const char *member)
172{
173	Buffer buf;
174	Buf_Init(&buf);
175	Buf_AddStr(&buf, archive);
176	Buf_AddStr(&buf, "(");
177	Buf_AddStr(&buf, member);
178	Buf_AddStr(&buf, ")");
179	return Buf_DoneData(&buf);
180}
181
182/*
183 * Parse an archive specification such as "archive.a(member1 member2.${EXT})",
184 * adding nodes for the expanded members to gns.  If successful, advance pp
185 * beyond the archive specification and any trailing whitespace.
186 */
187bool
188Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope)
189{
190	char *spec;		/* For modifying some bytes of *pp */
191	const char *cp;		/* Pointer into line */
192	GNode *gn;		/* New node */
193	FStr lib;		/* Library-part of specification */
194	FStr mem;		/* Member-part of specification */
195	char saveChar;		/* Ending delimiter of member-name */
196	bool expandLib;		/* Whether the parsed lib contains
197				 * expressions that need to be expanded */
198
199	spec = *pp;
200	lib = FStr_InitRefer(spec);
201	expandLib = false;
202
203	for (cp = lib.str; *cp != '(' && *cp != '\0';) {
204		if (*cp == '$') {
205			/* Expand nested expressions. */
206			/* XXX: This code can probably be shortened. */
207			const char *nested_p = cp;
208			FStr result;
209			bool isError;
210
211			/* XXX: is expanded twice: once here and once below */
212			result = Var_Parse(&nested_p, scope,
213			    VARE_EVAL_DEFINED);
214			/* TODO: handle errors */
215			isError = result.str == var_Error;
216			FStr_Done(&result);
217			if (isError)
218				return false;
219
220			expandLib = true;
221			cp += nested_p - cp;
222		} else
223			cp++;
224	}
225
226	spec[cp++ - spec] = '\0';
227	if (expandLib)
228		Var_Expand(&lib, scope, VARE_EVAL_DEFINED);
229
230	for (;;) {
231		/*
232		 * First skip to the start of the member's name, mark that
233		 * place and skip to the end of it (either white-space or
234		 * a close paren).
235		 */
236		bool doSubst = false;
237
238		cpp_skip_whitespace(&cp);
239
240		mem = FStr_InitRefer(cp);
241		while (*cp != '\0' && *cp != ')' && !ch_isspace(*cp)) {
242			if (*cp == '$') {
243				/* Expand nested expressions. */
244				/*
245				 * XXX: This code can probably be shortened.
246				 */
247				FStr result;
248				bool isError;
249				const char *nested_p = cp;
250
251				result = Var_Parse(&nested_p, scope,
252				    VARE_EVAL_DEFINED);
253				/* TODO: handle errors */
254				isError = result.str == var_Error;
255				FStr_Done(&result);
256
257				if (isError)
258					return false;
259
260				doSubst = true;
261				cp += nested_p - cp;
262			} else {
263				cp++;
264			}
265		}
266
267		if (*cp == '\0') {
268			Parse_Error(PARSE_FATAL,
269			    "No closing parenthesis "
270			    "in archive specification");
271			return false;
272		}
273
274		if (cp == mem.str)
275			break;
276
277		saveChar = *cp;
278		spec[cp - spec] = '\0';
279
280		/*
281		 * XXX: This should be taken care of intelligently by
282		 * SuffExpandChildren, both for the archive and the member
283		 * portions.
284		 */
285		/*
286		 * If member contains variables, try and substitute for them.
287		 * This slows down archive specs with dynamic sources, since
288		 * they are (non-)substituted three times, but we need to do
289		 * this since SuffExpandChildren calls us, otherwise we could
290		 * assume the substitutions would be taken care of later.
291		 */
292		if (doSubst) {
293			char *fullName;
294			char *p;
295			const char *unexpandedMem = mem.str;
296
297			Var_Expand(&mem, scope, VARE_EVAL_DEFINED);
298
299			/*
300			 * Now form an archive spec and recurse to deal with
301			 * nested variables and multi-word variable values.
302			 */
303			fullName = FullName(lib.str, mem.str);
304			p = fullName;
305
306			if (strcmp(mem.str, unexpandedMem) == 0) {
307				/*
308				 * Must contain dynamic sources, so we can't
309				 * deal with it now. Just create an ARCHV node
310				 * and let SuffExpandChildren handle it.
311				 */
312				gn = Targ_GetNode(fullName);
313				gn->type |= OP_ARCHV;
314				Lst_Append(gns, gn);
315
316			} else if (!Arch_ParseArchive(&p, gns, scope)) {
317				/* Error in nested call. */
318				free(fullName);
319				/* XXX: does unexpandedMemName leak? */
320				return false;
321			}
322			free(fullName);
323			/* XXX: does unexpandedMemName leak? */
324
325		} else if (Dir_HasWildcards(mem.str)) {
326			StringList members = LST_INIT;
327			SearchPath_Expand(&dirSearchPath, mem.str, &members);
328
329			while (!Lst_IsEmpty(&members)) {
330				char *member = Lst_Dequeue(&members);
331				char *fullname = FullName(lib.str, member);
332				free(member);
333
334				gn = Targ_GetNode(fullname);
335				free(fullname);
336
337				gn->type |= OP_ARCHV;
338				Lst_Append(gns, gn);
339			}
340			Lst_Done(&members);
341
342		} else {
343			char *fullname = FullName(lib.str, mem.str);
344			gn = Targ_GetNode(fullname);
345			free(fullname);
346
347			gn->type |= OP_ARCHV;
348			Lst_Append(gns, gn);
349		}
350		FStr_Done(&mem);
351
352		spec[cp - spec] = saveChar;
353	}
354
355	FStr_Done(&lib);
356
357	cp++;			/* skip the ')' */
358	cpp_skip_whitespace(&cp);
359	*pp += cp - *pp;
360	return true;
361}
362
363/*
364 * Locate a member in an archive.
365 *
366 * See ArchFindMember for an almost identical copy of this code.
367 */
368static struct ar_hdr *
369ArchStatMember(const char *archive, const char *member, bool addToCache)
370{
371#define AR_MAX_NAME_LEN (sizeof arh.ar_name - 1)
372	FILE *arch;
373	size_t size;		/* Size of archive member */
374	char magic[SARMAG];
375	ArchListNode *ln;
376	Arch *ar;
377	struct ar_hdr arh;
378	char memName[MAXPATHLEN + 1];
379	/* Current member name while hashing. */
380
381	member = str_basename(member);
382
383	for (ln = archives.first; ln != NULL; ln = ln->next) {
384		const Arch *a = ln->datum;
385		if (strcmp(a->name, archive) == 0)
386			break;
387	}
388
389	if (ln != NULL) {
390		struct ar_hdr *hdr;
391
392		ar = ln->datum;
393		hdr = HashTable_FindValue(&ar->members, member);
394		if (hdr != NULL)
395			return hdr;
396
397		{
398			/* Try truncated name */
399			char copy[AR_MAX_NAME_LEN + 1];
400			size_t len = strlen(member);
401
402			if (len > AR_MAX_NAME_LEN) {
403				snprintf(copy, sizeof copy, "%s", member);
404				hdr = HashTable_FindValue(&ar->members, copy);
405			}
406			return hdr;
407		}
408	}
409
410	if (!addToCache) {
411		/*
412		 * Since the archive is not to be cached, assume there's no
413		 * need to allocate the header, so just declare it static.
414		 */
415		static struct ar_hdr sarh;
416
417		arch = ArchFindMember(archive, member, &sarh, "r");
418		if (arch == NULL)
419			return NULL;
420
421		fclose(arch);
422		return &sarh;
423	}
424
425	arch = fopen(archive, "r");
426	if (arch == NULL)
427		return NULL;
428
429	if (fread(magic, SARMAG, 1, arch) != 1 ||
430	    strncmp(magic, ARMAG, SARMAG) != 0) {
431		(void)fclose(arch);
432		return NULL;
433	}
434
435	ar = bmake_malloc(sizeof *ar);
436	ar->name = bmake_strdup(archive);
437	ar->fnametab = NULL;
438	ar->fnamesize = 0;
439	HashTable_Init(&ar->members);
440	memName[AR_MAX_NAME_LEN] = '\0';
441
442	while (fread(&arh, sizeof arh, 1, arch) == 1) {
443		char *nameend;
444
445		if (strncmp(arh.ar_fmag, ARFMAG, sizeof arh.ar_fmag) != 0)
446			goto bad_archive;
447
448		arh.ar_size[sizeof arh.ar_size - 1] = '\0';
449		size = (size_t)strtol(arh.ar_size, NULL, 10);
450
451		memcpy(memName, arh.ar_name, sizeof arh.ar_name);
452		nameend = memName + AR_MAX_NAME_LEN;
453		while (nameend > memName && *nameend == ' ')
454			nameend--;
455		nameend[1] = '\0';
456
457#ifdef SVR4ARCHIVES
458		/*
459		 * svr4 names are slash-terminated.
460		 * Also svr4 extended the AR format.
461		 */
462		if (memName[0] == '/') {
463			/* svr4 magic mode; handle it */
464			switch (ArchSVR4Entry(ar, memName, size, arch)) {
465			case -1:	/* Invalid data */
466				goto bad_archive;
467			case 0:		/* List of files entry */
468				continue;
469			default:	/* Got the entry */
470				break;
471			}
472		} else {
473			if (nameend[0] == '/')
474				nameend[0] = '\0';
475		}
476#endif
477
478#ifdef AR_EFMT1
479		/*
480		 * BSD 4.4 extended AR format: #1/<namelen>, with name as the
481		 * first <namelen> bytes of the file
482		 */
483		if (strncmp(memName, AR_EFMT1, sizeof AR_EFMT1 - 1) == 0 &&
484		    ch_isdigit(memName[sizeof AR_EFMT1 - 1])) {
485
486			size_t elen = (size_t)atoi(
487			    memName + sizeof AR_EFMT1 - 1);
488
489			if (elen > MAXPATHLEN)
490				goto bad_archive;
491			if (fread(memName, elen, 1, arch) != 1)
492				goto bad_archive;
493			memName[elen] = '\0';
494			if (fseek(arch, -(long)elen, SEEK_CUR) != 0)
495				goto bad_archive;
496			if (DEBUG(ARCH) || DEBUG(MAKE))
497				debug_printf(
498				    "ArchStatMember: "
499				    "Extended format entry for %s\n",
500				    memName);
501		}
502#endif
503
504		{
505			struct ar_hdr *cached_hdr = bmake_malloc(
506			    sizeof *cached_hdr);
507			memcpy(cached_hdr, &arh, sizeof arh);
508			HashTable_Set(&ar->members, memName, cached_hdr);
509		}
510
511		/* Files are padded with newlines to an even-byte boundary. */
512		if (fseek(arch, ((long)size + 1) & ~1, SEEK_CUR) != 0)
513			goto bad_archive;
514	}
515
516	fclose(arch);
517
518	Lst_Append(&archives, ar);
519
520	return HashTable_FindValue(&ar->members, member);
521
522bad_archive:
523	fclose(arch);
524	HashTable_Done(&ar->members);
525	free(ar->fnametab);
526	free(ar);
527	return NULL;
528}
529
530#ifdef SVR4ARCHIVES
531/*
532 * Parse an SVR4 style entry that begins with a slash.
533 * If it is "//", then load the table of filenames.
534 * If it is "/<offset>", then try to substitute the long file name
535 * from offset of a table previously read.
536 * If a table is read, the file pointer is moved to the next archive member.
537 *
538 * Results:
539 *	-1: Bad data in archive
540 *	 0: A table was loaded from the file
541 *	 1: Name was successfully substituted from table
542 *	 2: Name was not successfully substituted from table
543 */
544static int
545ArchSVR4Entry(Arch *ar, char *inout_name, size_t size, FILE *arch)
546{
547#define ARLONGNAMES1 "//"
548#define ARLONGNAMES2 "/ARFILENAMES"
549	size_t entry;
550	char *ptr, *eptr;
551
552	if (strncmp(inout_name, ARLONGNAMES1, sizeof ARLONGNAMES1 - 1) == 0 ||
553	    strncmp(inout_name, ARLONGNAMES2, sizeof ARLONGNAMES2 - 1) == 0) {
554
555		if (ar->fnametab != NULL) {
556			DEBUG0(ARCH,
557			    "Attempted to redefine an SVR4 name table\n");
558			return -1;
559		}
560
561		/*
562		 * This is a table of archive names, so we build one for
563		 * ourselves
564		 */
565		ar->fnametab = bmake_malloc(size);
566		ar->fnamesize = size;
567
568		if (fread(ar->fnametab, size, 1, arch) != 1) {
569			DEBUG0(ARCH, "Reading an SVR4 name table failed\n");
570			return -1;
571		}
572		eptr = ar->fnametab + size;
573		for (entry = 0, ptr = ar->fnametab; ptr < eptr; ptr++)
574			if (*ptr == '/') {
575				entry++;
576				*ptr = '\0';
577			}
578		DEBUG1(ARCH,
579		    "Found svr4 archive name table with %lu entries\n",
580		    (unsigned long)entry);
581		return 0;
582	}
583
584	if (inout_name[1] == ' ' || inout_name[1] == '\0')
585		return 2;
586
587	entry = (size_t)strtol(&inout_name[1], &eptr, 0);
588	if ((*eptr != ' ' && *eptr != '\0') || eptr == &inout_name[1]) {
589		DEBUG1(ARCH, "Could not parse SVR4 name %s\n", inout_name);
590		return 2;
591	}
592	if (entry >= ar->fnamesize) {
593		DEBUG2(ARCH, "SVR4 entry offset %s is greater than %lu\n",
594		    inout_name, (unsigned long)ar->fnamesize);
595		return 2;
596	}
597
598	DEBUG2(ARCH, "Replaced %s with %s\n", inout_name, &ar->fnametab[entry]);
599
600	snprintf(inout_name, MAXPATHLEN + 1, "%s", &ar->fnametab[entry]);
601	return 1;
602}
603#endif
604
605
606static bool
607ArchiveMember_HasName(const struct ar_hdr *hdr,
608		      const char *name, size_t namelen)
609{
610	const size_t ar_name_len = sizeof hdr->ar_name;
611	const char *ar_name = hdr->ar_name;
612
613	if (strncmp(ar_name, name, namelen) != 0)
614		return false;
615
616	if (namelen >= ar_name_len)
617		return namelen == ar_name_len;
618
619	/* hdr->ar_name is space-padded to the right. */
620	if (ar_name[namelen] == ' ')
621		return true;
622
623	/*
624	 * In archives created by GNU binutils 2.27, the member names end
625	 * with a slash.
626	 */
627	if (ar_name[namelen] == '/' && ar_name[namelen + 1] == ' ')
628		return true;
629
630	return false;
631}
632
633/*
634 * Load the header of an archive member.  The mode is "r" for read-only
635 * access, "r+" for read-write access.
636 *
637 * Upon successful return, the archive file is positioned at the start of the
638 * member's struct ar_hdr.  In case of a failure or if the member doesn't
639 * exist, return NULL.
640 *
641 * See ArchStatMember for an almost identical copy of this code.
642 */
643static FILE *
644ArchFindMember(const char *archive, const char *member,
645	       struct ar_hdr *out_arh, const char *mode)
646{
647	FILE *arch;
648	int size;		/* Size of archive member */
649	char magic[SARMAG];
650	size_t len;
651
652	arch = fopen(archive, mode);
653	if (arch == NULL)
654		return NULL;
655
656	if (fread(magic, SARMAG, 1, arch) != 1 ||
657	    strncmp(magic, ARMAG, SARMAG) != 0) {
658		fclose(arch);
659		return NULL;
660	}
661
662	/* Files are archived using their basename, not the entire path. */
663	member = str_basename(member);
664	len = strlen(member);
665
666	while (fread(out_arh, sizeof *out_arh, 1, arch) == 1) {
667
668		if (strncmp(out_arh->ar_fmag, ARFMAG,
669			    sizeof out_arh->ar_fmag) != 0) {
670			fclose(arch);
671			return NULL;
672		}
673
674		DEBUG5(ARCH, "Reading archive %s member %.*s mtime %.*s\n",
675		    archive,
676		    (int)sizeof out_arh->ar_name, out_arh->ar_name,
677		    (int)sizeof out_arh->ar_date, out_arh->ar_date);
678
679		if (ArchiveMember_HasName(out_arh, member, len)) {
680			if (fseek(arch, -(long)sizeof *out_arh, SEEK_CUR) !=
681			    0) {
682				fclose(arch);
683				return NULL;
684			}
685			return arch;
686		}
687
688#ifdef AR_EFMT1
689		/*
690		 * BSD 4.4 extended AR format: #1/<namelen>, with name as the
691		 * first <namelen> bytes of the file
692		 */
693		if (strncmp(out_arh->ar_name, AR_EFMT1, sizeof AR_EFMT1 - 1) ==
694		    0 &&
695		    (ch_isdigit(out_arh->ar_name[sizeof AR_EFMT1 - 1]))) {
696			size_t elen = (size_t)atoi(
697			    &out_arh->ar_name[sizeof AR_EFMT1 - 1]);
698			char ename[MAXPATHLEN + 1];
699
700			if (elen > MAXPATHLEN) {
701				fclose(arch);
702				return NULL;
703			}
704			if (fread(ename, elen, 1, arch) != 1) {
705				fclose(arch);
706				return NULL;
707			}
708			ename[elen] = '\0';
709			if (DEBUG(ARCH) || DEBUG(MAKE))
710				debug_printf(
711				    "ArchFindMember: "
712				    "Extended format entry for %s\n",
713				    ename);
714			if (strncmp(ename, member, len) == 0) {
715				/* Found as extended name */
716				if (fseek(arch,
717				    -(long)(sizeof(struct ar_hdr) - elen),
718				    SEEK_CUR) != 0) {
719					fclose(arch);
720					return NULL;
721				}
722				return arch;
723			}
724			if (fseek(arch, -(long)elen, SEEK_CUR) != 0) {
725				fclose(arch);
726				return NULL;
727			}
728		}
729#endif
730
731		/* Advance to the next member. */
732		out_arh->ar_size[sizeof out_arh->ar_size - 1] = '\0';
733		size = (int)strtol(out_arh->ar_size, NULL, 10);
734		/* Files are padded with newlines to an even-byte boundary. */
735		if (fseek(arch, (size + 1) & ~1L, SEEK_CUR) != 0) {
736			fclose(arch);
737			return NULL;
738		}
739	}
740
741	fclose(arch);
742	return NULL;
743}
744
745/*
746 * Update the ar_date of the member of an archive, on disk but not in the
747 * GNode.  Update the st_mtime of the entire archive as well.  For a library,
748 * it may be required to run ranlib after this.
749 */
750void
751Arch_Touch(GNode *gn)
752{
753	FILE *f;
754	struct ar_hdr arh;
755
756	f = ArchFindMember(GNode_VarArchive(gn), GNode_VarMember(gn), &arh,
757	    "r+");
758	if (f == NULL)
759		return;
760
761	snprintf(arh.ar_date, sizeof arh.ar_date, "%-ld", (unsigned long)now);
762	(void)fwrite(&arh, sizeof arh, 1, f);
763	fclose(f);		/* TODO: handle errors */
764}
765
766/*
767 * Given a node which represents a library, touch the thing, making sure that
768 * the table of contents is also touched.
769 *
770 * Both the modification time of the library and of the RANLIBMAG member are
771 * set to 'now'.
772 */
773/*ARGSUSED*/
774void
775Arch_TouchLib(GNode *gn MAKE_ATTR_UNUSED)
776{
777#ifdef RANLIBMAG
778	FILE *f;
779	struct ar_hdr arh;	/* Header describing table of contents */
780	struct utimbuf times;
781
782	f = ArchFindMember(gn->path, RANLIBMAG, &arh, "r+");
783	if (f == NULL)
784		return;
785
786	snprintf(arh.ar_date, sizeof arh.ar_date, "%-ld", (unsigned long)now);
787	(void)fwrite(&arh, sizeof arh, 1, f);
788	fclose(f);		/* TODO: handle errors */
789
790	times.actime = times.modtime = now;
791	utime(gn->path, &times);	/* TODO: handle errors */
792#endif
793}
794
795/*
796 * Update the mtime of the GNode with the mtime from the archive member on
797 * disk (or in the cache).
798 */
799void
800Arch_UpdateMTime(GNode *gn)
801{
802	struct ar_hdr *arh;
803
804	arh = ArchStatMember(GNode_VarArchive(gn), GNode_VarMember(gn), true);
805	if (arh != NULL)
806		gn->mtime = (time_t)strtol(arh->ar_date, NULL, 10);
807	else
808		gn->mtime = 0;
809}
810
811/*
812 * Given a nonexistent archive member's node, update gn->mtime from its
813 * archived form, if it exists.
814 */
815void
816Arch_UpdateMemberMTime(GNode *gn)
817{
818	GNodeListNode *ln;
819
820	for (ln = gn->parents.first; ln != NULL; ln = ln->next) {
821		GNode *pgn = ln->datum;
822
823		if (pgn->type & OP_ARCHV) {
824			/*
825			 * If the parent is an archive specification and is
826			 * being made and its member's name matches the name
827			 * of the node we were given, record the modification
828			 * time of the parent in the child. We keep searching
829			 * its parents in case some other parent requires this
830			 * child to exist.
831			 */
832			const char *nameStart = strchr(pgn->name, '(') + 1;
833			const char *nameEnd = strchr(nameStart, ')');
834			size_t nameLen = (size_t)(nameEnd - nameStart);
835
836			if (pgn->flags.remake &&
837			    strncmp(nameStart, gn->name, nameLen) == 0) {
838				Arch_UpdateMTime(pgn);
839				gn->mtime = pgn->mtime;
840			}
841		} else if (pgn->flags.remake) {
842			/*
843			 * Something which isn't a library depends on the
844			 * existence of this target, so it needs to exist.
845			 */
846			gn->mtime = 0;
847			break;
848		}
849	}
850}
851
852/*
853 * Search for a library along the given search path.
854 *
855 * The node's 'path' field is set to the found path (including the
856 * actual file name, not -l...). If the system can handle the -L
857 * flag when linking (or we cannot find the library), we assume that
858 * the user has placed the .LIBS variable in the final linking
859 * command (or the linker will know where to find it) and set the
860 * TARGET variable for this node to be the node's name. Otherwise,
861 * we set the TARGET variable to be the full path of the library,
862 * as returned by Dir_FindFile.
863 */
864void
865Arch_FindLib(GNode *gn, SearchPath *path)
866{
867	char *libName = str_concat3("lib", gn->name + 2, ".a");
868	gn->path = Dir_FindFile(libName, path);
869	free(libName);
870
871	Var_Set(gn, TARGET, gn->name);
872}
873
874/* ARGSUSED */
875static bool
876RanlibOODate(const GNode *gn MAKE_ATTR_UNUSED)
877{
878#ifdef RANLIBMAG
879	struct ar_hdr *arh;	/* Header for __.SYMDEF */
880	int tocModTime;		/* The table-of-contents' mod time */
881
882	arh = ArchStatMember(gn->path, RANLIBMAG, false);
883
884	if (arh == NULL) {
885		/* A library without a table of contents is out-of-date. */
886		if (DEBUG(ARCH) || DEBUG(MAKE))
887			debug_printf("no toc...");
888		return true;
889	}
890
891	tocModTime = (int)strtol(arh->ar_date, NULL, 10);
892
893	if (DEBUG(ARCH) || DEBUG(MAKE))
894		debug_printf("%s modified %s...",
895		    RANLIBMAG, Targ_FmtTime(tocModTime));
896	return gn->youngestChild == NULL ||
897	       gn->youngestChild->mtime > tocModTime;
898#else
899	return false;
900#endif
901}
902
903/*
904 * Decide if a node with the OP_LIB attribute is out-of-date.
905 * The library is cached if it hasn't been already.
906 *
907 * There are several ways for a library to be out-of-date that are not
908 * available to ordinary files.  In addition, there are ways that are open to
909 * regular files that are not available to libraries.
910 *
911 * A library that is only used as a source is never considered out-of-date by
912 * itself.  This does not preclude the library's modification time from making
913 * its parent be out-of-date.  A library will be considered out-of-date for
914 * any of these reasons, given that it is a target on a dependency line
915 * somewhere:
916 *
917 *	Its modification time is less than that of one of its sources
918 *	(gn->mtime < gn->youngestChild->mtime).
919 *
920 *	Its modification time is greater than the time at which the make
921 *	began (i.e. it's been modified in the course of the make, probably
922 *	by archiving).
923 *
924 *	The modification time of one of its sources is greater than the one
925 *	of its RANLIBMAG member (i.e. its table of contents is out-of-date).
926 *	We don't compare the archive time vs. TOC time because they can be
927 *	too close. In my opinion we should not bother with the TOC at all
928 *	since this is used by 'ar' rules that affect the data contents of the
929 *	archive, not by ranlib rules, which affect the TOC.
930 */
931bool
932Arch_LibOODate(GNode *gn)
933{
934
935	if (gn->type & OP_PHONY)
936		return true;
937	if (!GNode_IsTarget(gn) && Lst_IsEmpty(&gn->children))
938		return false;
939	if ((!Lst_IsEmpty(&gn->children) && gn->youngestChild == NULL) ||
940		   (gn->mtime > now) ||
941		   (gn->youngestChild != NULL &&
942		    gn->mtime < gn->youngestChild->mtime))
943		return true;
944	return RanlibOODate(gn);
945}
946
947/* Initialize the archives module. */
948void
949Arch_Init(void)
950{
951	Lst_Init(&archives);
952}
953
954/* Clean up the archives module. */
955void
956Arch_End(void)
957{
958#ifdef CLEANUP
959	ArchListNode *ln;
960
961	for (ln = archives.first; ln != NULL; ln = ln->next)
962		ArchFree(ln->datum);
963	Lst_Done(&archives);
964#endif
965}
966
967bool
968Arch_IsLib(GNode *gn)
969{
970	char buf[8];
971	int fd;
972	bool isLib;
973
974	if ((fd = open(gn->path, O_RDONLY)) == -1)
975		return false;
976	isLib = read(fd, buf, sizeof buf) == sizeof buf
977	    && memcmp(buf, "!<arch>\n", sizeof buf) == 0;
978	(void)close(fd);
979	return isLib;
980}
981