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