xref: /openbsd/usr.bin/make/arch.c (revision e5dd7070)
1 /*	$OpenBSD: arch.c,v 1.91 2020/01/13 13:54:44 espie Exp $ */
2 /*	$NetBSD: arch.c,v 1.17 1996/11/06 17:58:59 christos Exp $	*/
3 
4 /*
5  * Copyright (c) 1999,2000 Marc Espie.
6  *
7  * Extensive code changes for the OpenBSD project.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE OPENBSD PROJECT AND CONTRIBUTORS
19  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OPENBSD
22  * PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 /*
31  * Copyright (c) 1988, 1989, 1990, 1993
32  *	The Regents of the University of California.  All rights reserved.
33  * Copyright (c) 1989 by Berkeley Softworks
34  * All rights reserved.
35  *
36  * This code is derived from software contributed to Berkeley by
37  * Adam de Boor.
38  *
39  * Redistribution and use in source and binary forms, with or without
40  * modification, are permitted provided that the following conditions
41  * are met:
42  * 1. Redistributions of source code must retain the above copyright
43  *    notice, this list of conditions and the following disclaimer.
44  * 2. Redistributions in binary form must reproduce the above copyright
45  *    notice, this list of conditions and the following disclaimer in the
46  *    documentation and/or other materials provided with the distribution.
47  * 3. Neither the name of the University nor the names of its contributors
48  *    may be used to endorse or promote products derived from this software
49  *    without specific prior written permission.
50  *
51  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
52  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
53  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
54  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
55  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
56  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
57  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
58  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
59  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
60  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
61  * SUCH DAMAGE.
62  */
63 
64 /*
65  *	Once again, cacheing/hashing comes into play in the manipulation
66  * of archives. The first time an archive is referenced, all of its members'
67  * headers are read and hashed and the archive closed again. All hashed
68  * archives are kept in a hash (archives) which is searched each time
69  * an archive member is referenced.
70  *
71  */
72 
73 #include <ar.h>
74 #include <assert.h>
75 #include <ctype.h>
76 #include <fcntl.h>
77 #include <limits.h>
78 #include <stddef.h>
79 #include <stdint.h>
80 #include <stdio.h>
81 #include <stdlib.h>
82 #include <string.h>
83 #include <unistd.h>
84 #include <ohash.h>
85 #include "config.h"
86 #include "defines.h"
87 #include "buf.h"
88 #include "dir.h"
89 #include "direxpand.h"
90 #include "arch.h"
91 #include "var.h"
92 #include "targ.h"
93 #include "memory.h"
94 #include "gnode.h"
95 #include "timestamp.h"
96 #include "lst.h"
97 
98 #ifdef TARGET_MACHINE
99 #undef MACHINE
100 #define MACHINE TARGET_MACHINE
101 #endif
102 #ifdef TARGET_MACHINE_ARCH
103 #undef MACHINE_ARCH
104 #define MACHINE_ARCH TARGET_MACHINE_ARCH
105 #endif
106 #ifdef TARGET_MACHINE_CPU
107 #undef MACHINE_CPU
108 #define MACHINE_CPU TARGET_MACHINE_CPU
109 #endif
110 
111 static struct ohash archives;	/* Archives we've already examined.  */
112 
113 typedef struct Arch_ {
114 	struct ohash members;	/* All the members of this archive, as
115 				 * struct arch_member entries.  */
116 	char name[1];		/* Archive name. */
117 } Arch;
118 
119 /* Used to get to ar's field sizes.  */
120 static struct ar_hdr *dummy;
121 #define AR_NAME_SIZE		(sizeof(dummy->ar_name))
122 #define AR_DATE_SIZE		(sizeof(dummy->ar_date))
123 
124 /* Each archive member is tied to an arch_member structure,
125  * suitable for hashing.  */
126 struct arch_member {
127 	struct timespec mtime;		/* Member modification date.  */
128 	char date[AR_DATE_SIZE+1];	/* Same, before conversion to numeric
129 					 * value.  */
130 	char name[1];			/* Member name.  */
131 };
132 
133 static struct ohash_info members_info = {
134 	offsetof(struct arch_member, name), NULL,
135 	hash_calloc, hash_free, element_alloc
136 };
137 
138 static struct ohash_info arch_info = {
139 	offsetof(Arch, name), NULL, hash_calloc, hash_free, element_alloc
140 };
141 
142 
143 
144 static struct arch_member *new_arch_member(struct ar_hdr *, const char *);
145 static struct timespec mtime_of_member(struct arch_member *);
146 static long field2long(const char *, size_t);
147 static Arch *read_archive(const char *, const char *);
148 
149 static struct timespec ArchMTimeMember(const char *, const char *, bool);
150 static FILE *ArchFindMember(const char *, const char *, struct ar_hdr *, const char *);
151 static void ArchTouch(const char *, const char *);
152 #if defined(__svr4__) || defined(__SVR4) || \
153     (defined(__OpenBSD__) && defined(__ELF__))
154 #define SVR4ARCHIVES
155 #endif
156 static bool parse_archive(Buffer, const char **, Lst, SymTable *);
157 static void add_archive_node(Lst, const char *);
158 
159 struct SVR4namelist {
160 	char *fnametab;		/* Extended name table strings */
161 	size_t fnamesize;	/* Size of the string table */
162 };
163 
164 #ifdef SVR4ARCHIVES
165 static const char *svr4list = "Archive list";
166 
167 static char *ArchSVR4Entry(struct SVR4namelist *, const char *, size_t, FILE *);
168 #endif
169 
170 static struct arch_member *
171 new_arch_member(struct ar_hdr *hdr, const char *name)
172 {
173 	const char *end = NULL;
174 	struct arch_member *n;
175 
176 	n = ohash_create_entry(&members_info, name, &end);
177 	/* XXX ar entries are NOT null terminated.	*/
178 	memcpy(n->date, &(hdr->ar_date), AR_DATE_SIZE);
179 	n->date[AR_DATE_SIZE] = '\0';
180 	/* Don't compute mtime before it is needed. */
181 	ts_set_out_of_date(n->mtime);
182 	return n;
183 }
184 
185 static struct timespec
186 mtime_of_member(struct arch_member *m)
187 {
188 	if (is_out_of_date(m->mtime))
189 		ts_set_from_time_t((time_t) strtoll(m->date, NULL, 10),
190 		    m->mtime);
191 	return m->mtime;
192 }
193 
194 bool
195 Arch_ParseArchive(const char **line, Lst nodes, SymTable *ctxt)
196 {
197 	bool result;
198 	static BUFFER expand;
199 
200 	Buf_Reinit(&expand, MAKE_BSIZE);
201 	result = parse_archive(&expand, line, nodes, ctxt);
202 	return result;
203 }
204 
205 static void
206 add_archive_node(Lst nodes, const char *name)
207 {
208 	GNode *gn;
209 
210 	gn = Targ_FindNode(name, TARG_CREATE);
211 	gn->type |= OP_ARCHV;
212 	Lst_AtEnd(nodes, gn);
213 }
214 
215 static bool
216 parse_archive(Buffer expand, const char **linePtr, Lst nodeLst, SymTable *ctxt)
217 {
218 	const char *cp;    	/* Pointer into line */
219 	const char *lib;	/* Library-part of specification */
220 	const char *elib;
221 	const char *member;    	/* Member-part of specification */
222 	const char *emember;
223 	bool subst_lib;
224 
225 	/* figure out the library name part */
226 	lib = *linePtr;
227 	subst_lib = false;
228 
229 	for (cp = lib; *cp != '(' && *cp != '\0';) {
230 		if (*cp == '$') {
231 			if (!Var_ParseSkip(&cp, ctxt))
232 				return false;
233 			subst_lib = true;
234 		} else
235 			cp++;
236 	}
237 
238 	elib = cp;
239 	if (subst_lib) {
240 		lib = Var_Substi(lib, elib, ctxt, true);
241 		elib = lib + strlen(lib);
242 	}
243 
244 	if (*cp == '\0') {
245 		printf("Unclosed parenthesis in archive specification\n");
246 		return false;
247 	}
248 	cp++;
249 	/* iterate on members, that may be separated by spaces */
250 	for (;;) {
251 		/* First skip to the start of the member's name, mark that
252 		 * place and skip to the end of it (either white-space or
253 		 * a close paren).  */
254 		bool subst_member = false;
255 
256 		while (ISSPACE(*cp))
257 			cp++;
258 		member = cp;
259 		while (*cp != '\0' && *cp != ')' && !ISSPACE(*cp)) {
260 			if (*cp == '$') {
261 				if (!Var_ParseSkip(&cp, ctxt))
262 					return false;
263 				subst_member = true;
264 			} else
265 				cp++;
266 		}
267 
268 		/* If the specification ends without a closing parenthesis,
269 		 * chances are there's something wrong (like a missing
270 		 * backslash), so it's better to return failure than allow such
271 		 * things to happen.  */
272 		if (*cp == '\0' || ISSPACE(*cp)) {
273 			printf("No closing parenthesis in archive specification\n");
274 			return false;
275 		}
276 
277 		/* If we didn't move anywhere, we must be done.  */
278 		if (cp == member)
279 			break;
280 
281 		emember = cp;
282 
283 		/* XXX: This should be taken care of intelligently by
284 		 * SuffExpandChildren, both for the archive and the member
285 		 * portions.  */
286 
287 		/* If member contains variables, try and substitute for them.
288 		 * This will slow down archive specs with dynamic sources, of
289 		 * course, since we'll be (non-)substituting them three times,
290 		 * but them's the breaks -- we need to do this since
291 		 * SuffExpandChildren calls us, otherwise we could assume the
292 		 * thing would be taken care of later.  */
293 		if (subst_member) {
294 			const char *oldMemberName = member;
295 			const char *result;
296 
297 			member = Var_Substi(member, emember, ctxt, true);
298 
299 			/* Now form an archive spec and recurse to deal with
300 			 * nested variables and multi-word variable values....
301 			 * The results are just placed at the end of the
302 			 * nodeLst we're returning.  */
303 			Buf_Addi(expand, lib, elib);
304 			Buf_AddChar(expand, '(');
305 			Buf_AddString(expand, member);
306 			Buf_AddChar(expand, ')');
307 			result = Buf_Retrieve(expand);
308 
309 			if (strchr(member, '$') &&
310 			    memcmp(member, oldMemberName,
311 				emember - oldMemberName) == 0) {
312 				/* Must contain dynamic sources, so we can't
313 				 * deal with it now.  let SuffExpandChildren
314 				 * handle it later  */
315 				add_archive_node(nodeLst, result);
316 			} else if (!Arch_ParseArchive(&result, nodeLst, ctxt))
317 				return false;
318 			Buf_Reset(expand);
319 		} else if (Dir_HasWildcardsi(member, emember)) {
320 			LIST  members;
321 			char  *m;
322 
323 			Lst_Init(&members);
324 
325 			Dir_Expandi(member, emember, defaultPath, &members);
326 			while ((m = Lst_DeQueue(&members)) != NULL) {
327 				Buf_Addi(expand, lib, elib);
328 				Buf_AddChar(expand, '(');
329 				Buf_AddString(expand, m);
330 				Buf_AddChar(expand, ')');
331 				free(m);
332 				add_archive_node(nodeLst, Buf_Retrieve(expand));
333 				Buf_Reset(expand);
334 			}
335 		} else {
336 			Buf_Addi(expand, lib, elib);
337 			Buf_AddChar(expand, '(');
338 			Buf_Addi(expand, member, emember);
339 			Buf_AddChar(expand, ')');
340 			add_archive_node(nodeLst, Buf_Retrieve(expand));
341 			Buf_Reset(expand);
342 		}
343 		if (subst_member)
344 			free((char *)member);
345 
346 	}
347 
348 	if (subst_lib)
349 		free((char *)lib);
350 
351 	/* We promised the pointer would be set up at the next non-space, so
352 	 * we must advance cp there before setting *linePtr... (note that on
353 	 * entrance to the loop, cp is guaranteed to point at a ')') */
354 	do {
355 		cp++;
356 	} while (ISSPACE(*cp));
357 
358 	*linePtr = cp;
359 	return true;
360 }
361 
362 /* Helper function: ar fields are not null terminated.	*/
363 static long
364 field2long(const char *field, size_t length)
365 {
366 	static char enough[32];
367 
368 	assert(length < sizeof(enough));
369 	memcpy(enough, field, length);
370 	enough[length] = '\0';
371 	return strtol(enough, NULL, 10);
372 }
373 
374 static Arch *
375 read_archive(const char *archive, const char *earchive)
376 {
377 	FILE *arch;       /* Stream to archive */
378 	char magic[SARMAG];
379 	Arch *ar;
380 	struct SVR4namelist list;
381 
382 	list.fnametab = NULL;
383 
384 	/* When we encounter an archive for the first time, we read its
385 	 * whole contents, to place it in the cache.  */
386 	arch = fopen(archive, "r");
387 	if (arch == NULL)
388 		return NULL;
389 
390 	/* Make sure this is an archive we can handle.  */
391 	if ((fread(magic, SARMAG, 1, arch) != 1) ||
392 	    (strncmp(magic, ARMAG, SARMAG) != 0)) {
393 		fclose(arch);
394 		return NULL;
395 	}
396 
397 	ar = ohash_create_entry(&arch_info, archive, &earchive);
398 	ohash_init(&ar->members, 8, &members_info);
399 
400 	for (;;) {
401 		size_t n;
402 		struct ar_hdr arHeader;	/* Archive-member header */
403 		off_t size;		/* Size of archive member */
404 		char buffer[PATH_MAX];
405 		char *memberName; 	/* Current member name while hashing. */
406 		char *cp;
407 
408 		memberName = buffer;
409 		n = fread(&arHeader, 1, sizeof(struct ar_hdr), arch);
410 
411 		/*  Whole archive read ok.  */
412 		if (n == 0 && feof(arch)) {
413 			free(list.fnametab);
414 			fclose(arch);
415 			return ar;
416 		}
417 		if (n < sizeof(struct ar_hdr))
418 			break;
419 
420 		if (memcmp(arHeader.ar_fmag, ARFMAG, sizeof(arHeader.ar_fmag))
421 		    != 0) {
422 			/* header is bogus.  */
423 			break;
424 		} else {
425 			/* We need to advance the stream's pointer to the start
426 			 * of the next header.  Records are padded with
427 			 * newlines to an even-byte boundary, so we need to
428 			 * extract the size of the record and round it up
429 			 * during the seek.  */
430 			size = (off_t) field2long(arHeader.ar_size,
431 			    sizeof(arHeader.ar_size));
432 
433 			(void)memcpy(memberName, arHeader.ar_name,
434 			    AR_NAME_SIZE);
435 			/* Find real end of name (strip extranous ' ')  */
436 			for (cp = memberName + AR_NAME_SIZE - 1; *cp == ' ';)
437 				cp--;
438 			cp[1] = '\0';
439 
440 #ifdef SVR4ARCHIVES
441 			/* SVR4 names are slash terminated.  Also svr4 extended
442 			 * AR format.
443 			 */
444 			if (memberName[0] == '/') {
445 				/* SVR4 magic mode.  */
446 				memberName = ArchSVR4Entry(&list, memberName,
447 				    size, arch);
448 				if (memberName == NULL)
449 					/* Invalid data */
450 					break;
451 				else if (memberName == svr4list)
452 					/* List of files entry */
453 					continue;
454 				/* Got the entry.  */
455 				/* XXX this assumes further processing, such as
456 				 * AR_EFMT1, also applies to SVR4ARCHIVES.  */
457 			}
458 			else {
459 				if (cp[0] == '/')
460 					cp[0] = '\0';
461 			}
462 #endif
463 
464 #ifdef AR_EFMT1
465 			/* BSD 4.4 extended AR format: #1/<namelen>, with name
466 			 * as the first <namelen> bytes of the file.  */
467 			if (memcmp(memberName, AR_EFMT1, sizeof(AR_EFMT1) - 1)
468 			    == 0 && ISDIGIT(memberName[sizeof(AR_EFMT1) - 1])) {
469 
470 				int elen = atoi(memberName +
471 				    sizeof(AR_EFMT1)-1);
472 
473 				if (elen <= 0 || elen >= PATH_MAX)
474 					break;
475 				memberName = buffer;
476 				if (fread(memberName, elen, 1, arch) != 1)
477 					break;
478 				memberName[elen] = '\0';
479 				if (fseek(arch, -elen, SEEK_CUR) != 0)
480 					break;
481 				if (DEBUG(ARCH) || DEBUG(MAKE))
482 					printf("ArchStat: Extended format entry for %s\n",
483 					    memberName);
484 			}
485 #endif
486 
487 			ohash_insert(&ar->members,
488 			    ohash_qlookup(&ar->members, memberName),
489 				new_arch_member(&arHeader, memberName));
490 		}
491 		if (fseek(arch, (size + 1) & ~1, SEEK_CUR) != 0)
492 			break;
493 	}
494 
495 	fclose(arch);
496 	ohash_delete(&ar->members);
497 	free(list.fnametab);
498 	free(ar);
499 	return NULL;
500 }
501 
502 /*-
503  *-----------------------------------------------------------------------
504  * ArchMTimeMember --
505  *	Find the modification time of an archive's member, given the
506  *	path to the archive and the path to the desired member.
507  *
508  * Results:
509  *	The archive member's modification time, or OUT_OF_DATE if member
510  *	was not found (convenient, so that missing members are always
511  *	out of date).
512  *
513  * Side Effects:
514  *	Cache the whole archive contents if hash is true.
515  *-----------------------------------------------------------------------
516  */
517 static struct timespec
518 ArchMTimeMember(
519     const char *archive,	/* Path to the archive */
520     const char *member, 	/* Name of member. If it is a path, only the
521 				 * last component is used. */
522     bool hash)       		/* true if archive should be hashed if not
523 				 * already so. */
524 {
525 	FILE *arch;     	/* Stream to archive */
526 	Arch *ar;		/* Archive descriptor */
527 	unsigned int slot;	/* Place of archive in the archives hash */
528 	const char *end = NULL;
529 	const char *cp;
530 	struct timespec result;
531 
532 	ts_set_out_of_date(result);
533 	/* Because of space constraints and similar things, files are archived
534 	 * using their final path components, not the entire thing, so we need
535 	 * to point 'member' to the final component, if there is one, to make
536 	 * the comparisons easier...  */
537 	cp = strrchr(member, '/');
538 	if (cp != NULL)
539 		member = cp + 1;
540 
541 	/* Try to find archive in cache.  */
542 	slot = ohash_qlookupi(&archives, archive, &end);
543 	ar = ohash_find(&archives, slot);
544 
545 	/* If not found, get it now.  */
546 	if (ar == NULL) {
547 		if (!hash) {
548 			/* Quick path:  no need to hash the whole archive, just
549 			 * use ArchFindMember to get the member's header and
550 			 * close the stream again.  */
551 			struct ar_hdr arHeader;
552 
553 			arch = ArchFindMember(archive, member, &arHeader, "r");
554 
555 			if (arch != NULL) {
556 				fclose(arch);
557 				ts_set_from_time_t(
558 				    (time_t)strtol(arHeader.ar_date, NULL, 10),
559 				    result);
560 			}
561 			return result;
562 		}
563 		ar = read_archive(archive, end);
564 		if (ar != NULL)
565 			ohash_insert(&archives, slot, ar);
566 	}
567 
568 	/* If archive was found, get entry we seek.  */
569 	if (ar != NULL) {
570 		struct arch_member *he;
571 		end = NULL;
572 
573 		he = ohash_find(&ar->members, ohash_qlookupi(&ar->members,
574 		    member, &end));
575 		if (he != NULL)
576 			return mtime_of_member(he);
577 		else {
578 			if ((size_t)(end - member) > AR_NAME_SIZE) {
579 				/* Try truncated name.	*/
580 				end = member + AR_NAME_SIZE;
581 				he = ohash_find(&ar->members,
582 				    ohash_qlookupi(&ar->members, member, &end));
583 				if (he != NULL)
584 					return mtime_of_member(he);
585 			}
586 		}
587 	}
588 	return result;
589 }
590 
591 #ifdef SVR4ARCHIVES
592 /*-
593  *-----------------------------------------------------------------------
594  * ArchSVR4Entry --
595  *	Parse an SVR4 style entry that begins with a slash.
596  *	If it is "//", then load the table of filenames
597  *	If it is "/<offset>", then try to substitute the long file name
598  *	from offset of a table previously read.
599  *
600  * Results:
601  *	svr4list: just read a list of names
602  *	NULL:	  error occurred
603  *	extended name
604  *
605  * Side-effect:
606  *	For a list of names, store the list in l.
607  *-----------------------------------------------------------------------
608  */
609 
610 static char *
611 ArchSVR4Entry(struct SVR4namelist *l, const char *name, size_t size, FILE *arch)
612 {
613 #define ARLONGNAMES1 "/"
614 #define ARLONGNAMES2 "ARFILENAMES"
615 	size_t entry;
616 	char *ptr, *eptr;
617 
618 	assert(name[0] == '/');
619 	name++;
620 	/* First comes a table of archive names, to be used by subsequent
621 	 * calls.  */
622 	if (memcmp(name, ARLONGNAMES1, sizeof(ARLONGNAMES1) - 1) == 0 ||
623 	    memcmp(name, ARLONGNAMES2, sizeof(ARLONGNAMES2) - 1) == 0) {
624 
625 		if (l->fnametab != NULL) {
626 			if (DEBUG(ARCH))
627 				printf("Attempted to redefine an SVR4 name table\n");
628 			return NULL;
629 		}
630 
631 		l->fnametab = emalloc(size);
632 		l->fnamesize = size;
633 
634 		if (fread(l->fnametab, size, 1, arch) != 1) {
635 			if (DEBUG(ARCH))
636 				printf("Reading an SVR4 name table failed\n");
637 			return NULL;
638 		}
639 
640 		eptr = l->fnametab + size;
641 		for (entry = 0, ptr = l->fnametab; ptr < eptr; ptr++)
642 			switch (*ptr) {
643 			case '/':
644 				entry++;
645 				*ptr = '\0';
646 				break;
647 
648 			case '\n':
649 				break;
650 
651 			default:
652 				break;
653 			}
654 		if (DEBUG(ARCH))
655 			printf("Found svr4 archive name table with %zu entries\n",
656 			    entry);
657 		return (char *)svr4list;
658 	}
659 	/* Then the names themselves are given as offsets in this table.  */
660 	if (*name == ' ' || *name == '\0')
661 		return NULL;
662 
663 	entry = (size_t) strtol(name, &eptr, 0);
664 	if ((*eptr != ' ' && *eptr != '\0') || eptr == name) {
665 		if (DEBUG(ARCH))
666 			printf("Could not parse SVR4 name /%s\n", name);
667 		return NULL;
668 	}
669 	if (entry >= l->fnamesize) {
670 		if (DEBUG(ARCH))
671 			printf("SVR4 entry offset /%s is greater than %zu\n",
672 			    name, l->fnamesize);
673 		return NULL;
674 	}
675 
676 	if (DEBUG(ARCH))
677 		printf("Replaced /%s with %s\n", name, l->fnametab + entry);
678 
679 	return l->fnametab + entry;
680 }
681 #endif
682 
683 
684 /*-
685  *-----------------------------------------------------------------------
686  * ArchFindMember --
687  *	Locate a member of an archive, given the path of the archive and
688  *	the path of the desired member. If the archive is to be modified,
689  *	the mode should be "r+", if not, it should be "r".
690  *
691  * Results:
692  *	A FILE *, opened for reading and writing, positioned right after
693  *	the member's header, or NULL if the member was nonexistent.
694  *
695  * Side Effects:
696  *	Fill the struct ar_hdr pointed by arHeaderPtr.
697  *-----------------------------------------------------------------------
698  */
699 static FILE *
700 ArchFindMember(
701     const char	  *archive,   /* Path to the archive */
702     const char	  *member,    /* Name of member. If it is a path, only the
703 			       * last component is used. */
704     struct ar_hdr *arHeaderPtr,/* Pointer to header structure to be filled in */
705     const char	  *mode)      /* mode for opening the stream */
706 {
707 	FILE *	  arch;       /* Stream to archive */
708 	char	  *cp;
709 	char	  magic[SARMAG];
710 	size_t	  length;
711 	struct SVR4namelist list;
712 
713 	list.fnametab = NULL;
714 
715 	arch = fopen(archive, mode);
716 	if (arch == NULL)
717 		return NULL;
718 
719 	/* Make sure this is an archive we can handle.  */
720 	if (fread(magic, SARMAG, 1, arch) != 1 ||
721 	    strncmp(magic, ARMAG, SARMAG) != 0) {
722 		fclose(arch);
723 		return NULL;
724 	}
725 
726 	/* Because of space constraints and similar things, files are archived
727 	 * using their final path components, not the entire thing, so we need
728 	 * to point 'member' to the final component, if there is one, to make
729 	 * the comparisons easier...  */
730 	cp = strrchr(member, '/');
731 	if (cp != NULL)
732 		member = cp + 1;
733 
734 	length = strlen(member);
735 	if (length >= AR_NAME_SIZE)
736 		length = AR_NAME_SIZE;
737 
738 	/* Error handling is simpler than for read_archive, since we just
739 	 * look for a given member.  */
740 	while (fread(arHeaderPtr, sizeof(struct ar_hdr), 1, arch) == 1) {
741 		off_t size;       /* Size of archive member */
742 		char *memberName;
743 
744 		if (memcmp(arHeaderPtr->ar_fmag, ARFMAG,
745 		    sizeof(arHeaderPtr->ar_fmag) ) != 0)
746 			 /* The header is bogus, so the archive is bad.  */
747 			 break;
748 
749 		memberName = arHeaderPtr->ar_name;
750 		if (memcmp(member, memberName, length) == 0) {
751 			/* If the member's name doesn't take up the entire
752 			 * 'name' field, we have to be careful of matching
753 			 * prefixes. Names are space- padded to the right, so
754 			 * if the character in 'name' at the end of the matched
755 			 * string is anything but a space, this isn't the
756 			 * member we sought.  */
757 #ifdef SVR4ARCHIVES
758 			if (length < sizeof(arHeaderPtr->ar_name) &&
759 			    memberName[length] == '/')
760 				length++;
761 #endif
762 			if (length == sizeof(arHeaderPtr->ar_name) ||
763 			    memberName[length] == ' ') {
764 				free(list.fnametab);
765 				return arch;
766 			}
767 		}
768 
769 		size = (off_t) field2long(arHeaderPtr->ar_size,
770 		    sizeof(arHeaderPtr->ar_size));
771 
772 #ifdef SVR4ARCHIVES
773 		/* svr4 names are slash terminated. Also svr4 extended AR
774 		 * format.
775 		 */
776 		if (memberName[0] == '/') {
777 			/* svr4 magic mode.  */
778 			memberName = ArchSVR4Entry(&list, arHeaderPtr->ar_name,
779 			    size, arch);
780 			if (memberName == NULL)
781 				/* Invalid data */
782 				break;
783 			else if (memberName == svr4list)
784 				/* List of files entry */
785 				continue;
786 			/* Got the entry.  */
787 			if (strcmp(memberName, member) == 0) {
788 				free(list.fnametab);
789 				return arch;
790 			}
791 		}
792 #endif
793 
794 #ifdef AR_EFMT1
795 		/* BSD 4.4 extended AR format: #1/<namelen>, with name as the
796 		 * first <namelen> bytes of the file.  */
797 		if (memcmp(memberName, AR_EFMT1, sizeof(AR_EFMT1) - 1) == 0 &&
798 		    ISDIGIT(memberName[sizeof(AR_EFMT1) - 1])) {
799 			char ename[PATH_MAX];
800 
801 			int elength = atoi(memberName + sizeof(AR_EFMT1)-1);
802 
803 			if (elength <= 0 || elength >= PATH_MAX)
804 				break;
805 			if (fread(ename, elength, 1, arch) != 1)
806 				break;
807 			if (fseek(arch, -elength, SEEK_CUR) != 0)
808 				break;
809 			ename[elength] = '\0';
810 			if (DEBUG(ARCH) || DEBUG(MAKE))
811 				printf("ArchFind: Extended format entry for %s\n", ename);
812 			/* Found as extended name.	*/
813 			if (strcmp(ename, member) == 0) {
814 				free(list.fnametab);
815 				return arch;
816 			}
817 		}
818 #endif
819 		/* This isn't the member we're after, so we need to advance the
820 		 * stream's pointer to the start of the next header.  */
821 		if (fseek(arch, (size + 1) & ~1, SEEK_CUR) != 0)
822 			break;
823 	}
824 
825 	/* We did not find the member, or we ran into an error while reading
826 	 * the archive.  */
827 #ifdef SVRARCHIVES
828 	free(list.fnametab);
829 #endif
830 	fclose(arch);
831 	return NULL;
832 }
833 
834 static void
835 ArchTouch(const char *archive, const char *member)
836 {
837 	FILE *arch;
838 	struct ar_hdr arHeader;
839 
840 	arch = ArchFindMember(archive, member, &arHeader, "r+");
841 	if (arch != NULL) {
842 		snprintf(arHeader.ar_date, sizeof(arHeader.ar_date),
843 		    "%-12ld", (long) time(NULL));
844 		if (fseek(arch, -sizeof(struct ar_hdr), SEEK_CUR) == 0)
845 			(void)fwrite(&arHeader, sizeof(struct ar_hdr), 1, arch);
846 		fclose(arch);
847 	}
848 }
849 
850 /*
851  * Side Effects:
852  *	The modification time of the entire archive is also changed.
853  *	For a library, this could necessitate the re-ranlib'ing of the
854  *	whole thing.
855  */
856 void
857 Arch_Touch(GNode *gn)
858 {
859 	ArchTouch(Var(ARCHIVE_INDEX, gn), Var(MEMBER_INDEX, gn));
860 }
861 
862 struct timespec
863 Arch_MTime(GNode *gn)
864 {
865 	gn->mtime = ArchMTimeMember(Var(ARCHIVE_INDEX, gn),
866 	     Var(MEMBER_INDEX, gn), true);
867 
868 	return gn->mtime;
869 }
870 
871 struct timespec
872 Arch_MemMTime(GNode *gn)
873 {
874 	LstNode ln;
875 
876 	for (ln = Lst_First(&gn->parents); ln != NULL; ln = Lst_Adv(ln)) {
877 		GNode *pgn;
878 		char *nameStart;
879 		char *nameEnd;
880 
881 		pgn = Lst_Datum(ln);
882 
883 		if (pgn->type & OP_ARCHV) {
884 			/* If the parent is an archive specification and is
885 			 * being built and its member's name matches the name of
886 			 * the node we were given, record the modification time
887 			 * of the parent in the child. We keep searching its
888 			 * parents in case some other parent requires this
889 			 * child to exist...  */
890 			if ((nameStart = strchr(pgn->name, '(') ) != NULL) {
891 				nameStart++;
892 				nameEnd = strchr(nameStart, ')');
893 			} else
894 				nameEnd = NULL;
895 
896 			if (pgn->must_make && nameEnd != NULL &&
897 			    strncmp(nameStart, gn->name, nameEnd - nameStart)
898 			    == 0 && gn->name[nameEnd-nameStart] == '\0')
899 				gn->mtime = Arch_MTime(pgn);
900 		} else if (pgn->must_make) {
901 			/* Something which isn't a library depends on the
902 			 * existence of this target, so it needs to exist.  */
903 			ts_set_out_of_date(gn->mtime);
904 			break;
905 		}
906 	}
907 	return gn->mtime;
908 }
909 
910 void
911 Arch_Init(void)
912 {
913 	ohash_init(&archives, 4, &arch_info);
914 }
915