xref: /freebsd/contrib/bmake/arch.c (revision 36d6566e5985030fd2f1100bd9c1387bbe0bd290)
1 /*	$NetBSD: arch.c,v 1.151 2020/10/31 18:41:07 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  * arch.c --
73  *	Functions to manipulate libraries, archives and their members.
74  *
75  *	Once again, cacheing/hashing comes into play in the manipulation
76  * of archives. The first time an archive is referenced, all of its members'
77  * headers are read and hashed and the archive closed again. All hashed
78  * archives are kept on a list which is searched each time an archive member
79  * is referenced.
80  *
81  * The interface to this module is:
82  *	Arch_ParseArchive
83  *			Given an archive specification, return a list
84  *			of GNode's, one for each member in the spec.
85  *			FALSE is returned if the specification is
86  *			invalid for some reason.
87  *
88  *	Arch_Touch	Alter the modification time of the archive
89  *			member described by the given node to be
90  *			the current time.
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_MTime	Find the modification time of a member of
98  *			an archive *in the archive*. The time is also
99  *			placed in the member's GNode. Returns the
100  *			modification time.
101  *
102  *	Arch_MemTime	Find the modification time of a member of
103  *			an archive. Called when the member doesn't
104  *			already exist. Looks in the archive for the
105  *			modification time. Returns the modification
106  *			time.
107  *
108  *	Arch_FindLib	Search for a library along a path. The
109  *			library name in the GNode should be in
110  *			-l<name> format.
111  *
112  *	Arch_LibOODate	Special function to decide if a library node
113  *			is out-of-date.
114  *
115  *	Arch_Init	Initialize this module.
116  *
117  *	Arch_End	Clean up this module.
118  */
119 
120 #ifdef HAVE_CONFIG_H
121 # include "config.h"
122 #endif
123 #include <sys/types.h>
124 #include <sys/stat.h>
125 #include <sys/time.h>
126 #include <sys/param.h>
127 #ifdef HAVE_AR_H
128 #include <ar.h>
129 #else
130 struct ar_hdr {
131         char ar_name[16];               /* name */
132         char ar_date[12];               /* modification time */
133         char ar_uid[6];                 /* user id */
134         char ar_gid[6];                 /* group id */
135         char ar_mode[8];                /* octal file permissions */
136         char ar_size[10];               /* size in bytes */
137 #ifndef ARFMAG
138 #define ARFMAG  "`\n"
139 #endif
140         char ar_fmag[2];                /* consistency check */
141 };
142 #endif
143 #if defined(HAVE_RANLIB_H) && !(defined(__ELF__) || defined(NO_RANLIB))
144 #include <ranlib.h>
145 #endif
146 #ifdef HAVE_UTIME_H
147 #include <utime.h>
148 #endif
149 
150 #include "make.h"
151 #include "dir.h"
152 
153 /*	"@(#)arch.c	8.2 (Berkeley) 1/2/94"	*/
154 MAKE_RCSID("$NetBSD: arch.c,v 1.151 2020/10/31 18:41:07 rillig Exp $");
155 
156 #ifdef TARGET_MACHINE
157 #undef MAKE_MACHINE
158 #define MAKE_MACHINE TARGET_MACHINE
159 #endif
160 #ifdef TARGET_MACHINE_ARCH
161 #undef MAKE_MACHINE_ARCH
162 #define MAKE_MACHINE_ARCH TARGET_MACHINE_ARCH
163 #endif
164 
165 typedef struct List ArchList;
166 typedef struct ListNode ArchListNode;
167 
168 static ArchList *archives;	/* The archives we've already examined */
169 
170 typedef struct Arch {
171     char *name;			/* Name of archive */
172     HashTable members;		/* All the members of the archive described
173 				 * by <name, struct ar_hdr *> key/value pairs */
174     char *fnametab;		/* Extended name table strings */
175     size_t fnamesize;		/* Size of the string table */
176 } Arch;
177 
178 static FILE *ArchFindMember(const char *, const char *,
179 			    struct ar_hdr *, const char *);
180 #if defined(__svr4__) || defined(__SVR4) || defined(__ELF__)
181 #define SVR4ARCHIVES
182 static int ArchSVR4Entry(Arch *, char *, size_t, FILE *);
183 #endif
184 
185 
186 #if defined(_AIX)
187 # define AR_NAME _ar_name.ar_name
188 # define AR_FMAG _ar_name.ar_fmag
189 # define SARMAG  SAIAMAG
190 # define ARMAG   AIAMAG
191 # define ARFMAG  AIAFMAG
192 #endif
193 #ifndef  AR_NAME
194 # define AR_NAME ar_name
195 #endif
196 #ifndef  AR_DATE
197 # define AR_DATE ar_date
198 #endif
199 #ifndef  AR_SIZE
200 # define AR_SIZE ar_size
201 #endif
202 #ifndef  AR_FMAG
203 # define AR_FMAG ar_fmag
204 #endif
205 #ifndef ARMAG
206 # define ARMAG	"!<arch>\n"
207 #endif
208 #ifndef SARMAG
209 # define SARMAG	8
210 #endif
211 
212 
213 #ifdef CLEANUP
214 static void
215 ArchFree(void *ap)
216 {
217     Arch *a = ap;
218     HashIter hi;
219 
220     /* Free memory from hash entries */
221     HashIter_Init(&hi, &a->members);
222     while (HashIter_Next(&hi) != NULL)
223 	free(hi.entry->value);
224 
225     free(a->name);
226     free(a->fnametab);
227     HashTable_Done(&a->members);
228     free(a);
229 }
230 #endif
231 
232 
233 /*-
234  *-----------------------------------------------------------------------
235  * Arch_ParseArchive --
236  *	Parse the archive specification in the given line and find/create
237  *	the nodes for the specified archive members, placing their nodes
238  *	on the given list.
239  *
240  * Input:
241  *	linePtr		Pointer to start of specification
242  *	nodeLst		Lst on which to place the nodes
243  *	ctxt		Context in which to expand variables
244  *
245  * Results:
246  *	TRUE if it was a valid specification. The linePtr is updated
247  *	to point to the first non-space after the archive spec. The
248  *	nodes for the members are placed on the given list.
249  *-----------------------------------------------------------------------
250  */
251 Boolean
252 Arch_ParseArchive(char **linePtr, GNodeList *nodeLst, GNode *ctxt)
253 {
254     char *cp;			/* Pointer into line */
255     GNode *gn;			/* New node */
256     char *libName;		/* Library-part of specification */
257     char *memName;		/* Member-part of specification */
258     char saveChar;		/* Ending delimiter of member-name */
259     Boolean subLibName;		/* TRUE if libName should have/had
260 				 * variable substitution performed on it */
261 
262     libName = *linePtr;
263 
264     subLibName = FALSE;
265 
266     for (cp = libName; *cp != '(' && *cp != '\0';) {
267 	if (*cp == '$') {
268 	    /*
269 	     * Variable spec, so call the Var module to parse the puppy
270 	     * so we can safely advance beyond it...
271 	     */
272 	    const char *nested_p = cp;
273 	    void *result_freeIt;
274 	    const char *result;
275 	    Boolean isError;
276 
277 	    (void)Var_Parse(&nested_p, ctxt, VARE_UNDEFERR|VARE_WANTRES,
278 			    &result, &result_freeIt);
279 	    /* TODO: handle errors */
280 	    isError = result == var_Error;
281 	    free(result_freeIt);
282 	    if (isError)
283 		return FALSE;
284 
285 	    subLibName = TRUE;
286 	    cp += nested_p - cp;
287 	} else
288 	    cp++;
289     }
290 
291     *cp++ = '\0';
292     if (subLibName) {
293 	(void)Var_Subst(libName, ctxt, VARE_UNDEFERR|VARE_WANTRES, &libName);
294 	/* TODO: handle errors */
295     }
296 
297 
298     for (;;) {
299 	/*
300 	 * First skip to the start of the member's name, mark that
301 	 * place and skip to the end of it (either white-space or
302 	 * a close paren).
303 	 */
304 	Boolean doSubst = FALSE; /* TRUE if need to substitute in memName */
305 
306 	pp_skip_whitespace(&cp);
307 
308 	memName = cp;
309 	while (*cp != '\0' && *cp != ')' && !ch_isspace(*cp)) {
310 	    if (*cp == '$') {
311 		/*
312 		 * Variable spec, so call the Var module to parse the puppy
313 		 * so we can safely advance beyond it...
314 		 */
315 		void *freeIt;
316 		const char *result;
317 		Boolean isError;
318 		const char *nested_p = cp;
319 
320 		(void)Var_Parse(&nested_p, ctxt, VARE_UNDEFERR|VARE_WANTRES,
321 				&result, &freeIt);
322 		/* TODO: handle errors */
323 		isError = result == var_Error;
324 		free(freeIt);
325 
326 		if (isError)
327 		    return FALSE;
328 
329 		doSubst = TRUE;
330 		cp += nested_p - cp;
331 	    } else {
332 		cp++;
333 	    }
334 	}
335 
336 	/*
337 	 * If the specification ends without a closing parenthesis,
338 	 * chances are there's something wrong (like a missing backslash),
339 	 * so it's better to return failure than allow such things to happen
340 	 */
341 	if (*cp == '\0') {
342 	    printf("No closing parenthesis in archive specification\n");
343 	    return FALSE;
344 	}
345 
346 	/*
347 	 * If we didn't move anywhere, we must be done
348 	 */
349 	if (cp == memName) {
350 	    break;
351 	}
352 
353 	saveChar = *cp;
354 	*cp = '\0';
355 
356 	/*
357 	 * XXX: This should be taken care of intelligently by
358 	 * SuffExpandChildren, both for the archive and the member portions.
359 	 */
360 	/*
361 	 * If member contains variables, try and substitute for them.
362 	 * This will slow down archive specs with dynamic sources, of course,
363 	 * since we'll be (non-)substituting them three times, but them's
364 	 * the breaks -- we need to do this since SuffExpandChildren calls
365 	 * us, otherwise we could assume the thing would be taken care of
366 	 * later.
367 	 */
368 	if (doSubst) {
369 	    char *buf;
370 	    char *sacrifice;
371 	    char *oldMemName = memName;
372 
373 	    (void)Var_Subst(memName, ctxt, VARE_UNDEFERR|VARE_WANTRES,
374 			    &memName);
375 	    /* TODO: handle errors */
376 
377 	    /*
378 	     * Now form an archive spec and recurse to deal with nested
379 	     * variables and multi-word variable values.... The results
380 	     * are just placed at the end of the nodeLst we're returning.
381 	     */
382 	    buf = sacrifice = str_concat4(libName, "(", memName, ")");
383 
384 	    if (strchr(memName, '$') && strcmp(memName, oldMemName) == 0) {
385 		/*
386 		 * Must contain dynamic sources, so we can't deal with it now.
387 		 * Just create an ARCHV node for the thing and let
388 		 * SuffExpandChildren handle it...
389 		 */
390 		gn = Targ_GetNode(buf);
391 		gn->type |= OP_ARCHV;
392 		Lst_Append(nodeLst, gn);
393 
394 	    } else if (!Arch_ParseArchive(&sacrifice, nodeLst, ctxt)) {
395 		/* Error in nested call. */
396 		free(buf);
397 		return FALSE;
398 	    }
399 	    free(buf);
400 
401 	} else if (Dir_HasWildcards(memName)) {
402 	    StringList *members = Lst_New();
403 	    Dir_Expand(memName, dirSearchPath, members);
404 
405 	    while (!Lst_IsEmpty(members)) {
406 		char *member = Lst_Dequeue(members);
407 		char *fullname = str_concat4(libName, "(", member, ")");
408 		free(member);
409 
410 		gn = Targ_GetNode(fullname);
411 		free(fullname);
412 
413 		gn->type |= OP_ARCHV;
414 		Lst_Append(nodeLst, gn);
415 	    }
416 	    Lst_Free(members);
417 
418 	} else {
419 	    char *fullname = str_concat4(libName, "(", memName, ")");
420 	    gn = Targ_GetNode(fullname);
421 	    free(fullname);
422 
423 	    /*
424 	     * We've found the node, but have to make sure the rest of the
425 	     * world knows it's an archive member, without having to
426 	     * constantly check for parentheses, so we type the thing with
427 	     * the OP_ARCHV bit before we place it on the end of the
428 	     * provided list.
429 	     */
430 	    gn->type |= OP_ARCHV;
431 	    Lst_Append(nodeLst, gn);
432 	}
433 	if (doSubst) {
434 	    free(memName);
435 	}
436 
437 	*cp = saveChar;
438     }
439 
440     /*
441      * If substituted libName, free it now, since we need it no longer.
442      */
443     if (subLibName) {
444 	free(libName);
445     }
446 
447     cp++;			/* skip the ')' */
448     /* We promised that linePtr would be set up at the next non-space. */
449     pp_skip_whitespace(&cp);
450     *linePtr = cp;
451     return TRUE;
452 }
453 
454 /* Locate a member of an archive, given the path of the archive and the path
455  * of the desired member.
456  *
457  * Input:
458  *	archive		Path to the archive
459  *	member		Name of member; only its basename is used.
460  *	hash		TRUE if archive should be hashed if not already so.
461  *
462  * Results:
463  *	The ar_hdr for the member.
464  */
465 static struct ar_hdr *
466 ArchStatMember(const char *archive, const char *member, Boolean hash)
467 {
468 #define AR_MAX_NAME_LEN (sizeof(arh.AR_NAME) - 1)
469     FILE *arch;			/* Stream to archive */
470     size_t size;		/* Size of archive member */
471     char magic[SARMAG];
472     ArchListNode *ln;
473     Arch *ar;			/* Archive descriptor */
474     struct ar_hdr arh;		/* archive-member header for reading archive */
475     char memName[MAXPATHLEN + 1];
476 				/* Current member name while hashing. */
477 
478     /*
479      * Because of space constraints and similar things, files are archived
480      * using their basename, not the entire path.
481      */
482     const char *lastSlash = strrchr(member, '/');
483     if (lastSlash != NULL)
484 	member = lastSlash + 1;
485 
486     for (ln = archives->first; ln != NULL; ln = ln->next) {
487 	const Arch *archPtr = ln->datum;
488 	if (strcmp(archPtr->name, archive) == 0)
489 	    break;
490     }
491 
492     if (ln != NULL) {
493 	struct ar_hdr *hdr;
494 
495 	ar = ln->datum;
496 	hdr = HashTable_FindValue(&ar->members, member);
497 	if (hdr != NULL)
498 	    return hdr;
499 
500 	{
501 	    /* Try truncated name */
502 	    char copy[AR_MAX_NAME_LEN + 1];
503 	    size_t len = strlen(member);
504 
505 	    if (len > AR_MAX_NAME_LEN) {
506 		len = AR_MAX_NAME_LEN;
507 		snprintf(copy, sizeof copy, "%s", member);
508 	    }
509 	    hdr = HashTable_FindValue(&ar->members, copy);
510 	    return hdr;
511 	}
512     }
513 
514     if (!hash) {
515 	/*
516 	 * Caller doesn't want the thing hashed, just use ArchFindMember
517 	 * to read the header for the member out and close down the stream
518 	 * again. Since the archive is not to be hashed, we assume there's
519 	 * no need to allocate extra room for the header we're returning,
520 	 * so just declare it static.
521 	 */
522 	static struct ar_hdr sarh;
523 
524 	arch = ArchFindMember(archive, member, &sarh, "r");
525 	if (arch == NULL)
526 	    return NULL;
527 
528 	fclose(arch);
529 	return &sarh;
530     }
531 
532     /*
533      * We don't have this archive on the list yet, so we want to find out
534      * everything that's in it and cache it so we can get at it quickly.
535      */
536     arch = fopen(archive, "r");
537     if (arch == NULL)
538 	return NULL;
539 
540     /*
541      * We use the ARMAG string to make sure this is an archive we
542      * can handle...
543      */
544     if ((fread(magic, SARMAG, 1, arch) != 1) ||
545 	(strncmp(magic, ARMAG, SARMAG) != 0)) {
546 	fclose(arch);
547 	return NULL;
548     }
549 
550     ar = bmake_malloc(sizeof(Arch));
551     ar->name = bmake_strdup(archive);
552     ar->fnametab = NULL;
553     ar->fnamesize = 0;
554     HashTable_Init(&ar->members);
555     memName[AR_MAX_NAME_LEN] = '\0';
556 
557     while (fread((char *)&arh, sizeof(struct ar_hdr), 1, arch) == 1) {
558 	if (strncmp(arh.AR_FMAG, ARFMAG, sizeof(arh.AR_FMAG)) != 0) {
559 	    /*
560 	     * The header is bogus, so the archive is bad
561 	     * and there's no way we can recover...
562 	     */
563 	    goto badarch;
564 	} else {
565 	    char *nameend;
566 
567 	    /*
568 	     * We need to advance the stream's pointer to the start of the
569 	     * next header. Files are padded with newlines to an even-byte
570 	     * boundary, so we need to extract the size of the file from the
571 	     * 'size' field of the header and round it up during the seek.
572 	     */
573 	    arh.AR_SIZE[sizeof(arh.AR_SIZE) - 1] = '\0';
574 	    size = (size_t)strtol(arh.ar_size, NULL, 10);
575 
576 	    memcpy(memName, arh.AR_NAME, sizeof(arh.AR_NAME));
577 	    nameend = memName + AR_MAX_NAME_LEN;
578 	    while (*nameend == ' ') {
579 		nameend--;
580 	    }
581 	    nameend[1] = '\0';
582 
583 #ifdef SVR4ARCHIVES
584 	    /*
585 	     * svr4 names are slash terminated. Also svr4 extended AR format.
586 	     */
587 	    if (memName[0] == '/') {
588 		/*
589 		 * svr4 magic mode; handle it
590 		 */
591 		switch (ArchSVR4Entry(ar, memName, size, arch)) {
592 		case -1:	/* Invalid data */
593 		    goto badarch;
594 		case 0:		/* List of files entry */
595 		    continue;
596 		default:	/* Got the entry */
597 		    break;
598 		}
599 	    } else {
600 		if (nameend[0] == '/')
601 		    nameend[0] = '\0';
602 	    }
603 #endif
604 
605 #ifdef AR_EFMT1
606 	    /*
607 	     * BSD 4.4 extended AR format: #1/<namelen>, with name as the
608 	     * first <namelen> bytes of the file
609 	     */
610 	    if (strncmp(memName, AR_EFMT1, sizeof(AR_EFMT1) - 1) == 0 &&
611 		ch_isdigit(memName[sizeof(AR_EFMT1) - 1])) {
612 
613 		int elen = atoi(&memName[sizeof(AR_EFMT1) - 1]);
614 
615 		if ((unsigned int)elen > MAXPATHLEN)
616 		    goto badarch;
617 		if (fread(memName, (size_t)elen, 1, arch) != 1)
618 		    goto badarch;
619 		memName[elen] = '\0';
620 		if (fseek(arch, -elen, SEEK_CUR) != 0)
621 		    goto badarch;
622 		if (DEBUG(ARCH) || DEBUG(MAKE)) {
623 		    debug_printf("ArchStat: Extended format entry for %s\n",
624 				 memName);
625 		}
626 	    }
627 #endif
628 
629 	    {
630 		HashEntry *he;
631 		he = HashTable_CreateEntry(&ar->members, memName, NULL);
632 		HashEntry_Set(he, bmake_malloc(sizeof(struct ar_hdr)));
633 		memcpy(HashEntry_Get(he), &arh, sizeof(struct ar_hdr));
634 	    }
635 	}
636 	if (fseek(arch, ((long)size + 1) & ~1, SEEK_CUR) != 0)
637 	    goto badarch;
638     }
639 
640     fclose(arch);
641 
642     Lst_Append(archives, ar);
643 
644     /*
645      * Now that the archive has been read and cached, we can look into
646      * the hash table to find the desired member's header.
647      */
648     return HashTable_FindValue(&ar->members, member);
649 
650 badarch:
651     fclose(arch);
652     HashTable_Done(&ar->members);
653     free(ar->fnametab);
654     free(ar);
655     return NULL;
656 }
657 
658 #ifdef SVR4ARCHIVES
659 /*-
660  *-----------------------------------------------------------------------
661  * ArchSVR4Entry --
662  *	Parse an SVR4 style entry that begins with a slash.
663  *	If it is "//", then load the table of filenames
664  *	If it is "/<offset>", then try to substitute the long file name
665  *	from offset of a table previously read.
666  *	If a table is read, the file pointer is moved to the next archive
667  *	member.
668  *
669  * Results:
670  *	-1: Bad data in archive
671  *	 0: A table was loaded from the file
672  *	 1: Name was successfully substituted from table
673  *	 2: Name was not successfully substituted from table
674  *-----------------------------------------------------------------------
675  */
676 static int
677 ArchSVR4Entry(Arch *ar, char *name, size_t size, FILE *arch)
678 {
679 #define ARLONGNAMES1 "//"
680 #define ARLONGNAMES2 "/ARFILENAMES"
681     size_t entry;
682     char *ptr, *eptr;
683 
684     if (strncmp(name, ARLONGNAMES1, sizeof(ARLONGNAMES1) - 1) == 0 ||
685 	strncmp(name, ARLONGNAMES2, sizeof(ARLONGNAMES2) - 1) == 0) {
686 
687 	if (ar->fnametab != NULL) {
688 	    DEBUG0(ARCH, "Attempted to redefine an SVR4 name table\n");
689 	    return -1;
690 	}
691 
692 	/*
693 	 * This is a table of archive names, so we build one for
694 	 * ourselves
695 	 */
696 	ar->fnametab = bmake_malloc(size);
697 	ar->fnamesize = size;
698 
699 	if (fread(ar->fnametab, size, 1, arch) != 1) {
700 	    DEBUG0(ARCH, "Reading an SVR4 name table failed\n");
701 	    return -1;
702 	}
703 	eptr = ar->fnametab + size;
704 	for (entry = 0, ptr = ar->fnametab; ptr < eptr; ptr++)
705 	    if (*ptr == '/') {
706 		entry++;
707 		*ptr = '\0';
708 	    }
709 	DEBUG1(ARCH, "Found svr4 archive name table with %lu entries\n",
710 	       (unsigned long)entry);
711 	return 0;
712     }
713 
714     if (name[1] == ' ' || name[1] == '\0')
715 	return 2;
716 
717     entry = (size_t)strtol(&name[1], &eptr, 0);
718     if ((*eptr != ' ' && *eptr != '\0') || eptr == &name[1]) {
719 	DEBUG1(ARCH, "Could not parse SVR4 name %s\n", name);
720 	return 2;
721     }
722     if (entry >= ar->fnamesize) {
723 	DEBUG2(ARCH, "SVR4 entry offset %s is greater than %lu\n",
724 	       name, (unsigned long)ar->fnamesize);
725 	return 2;
726     }
727 
728     DEBUG2(ARCH, "Replaced %s with %s\n", name, &ar->fnametab[entry]);
729 
730     snprintf(name, MAXPATHLEN + 1, "%s", &ar->fnametab[entry]);
731     return 1;
732 }
733 #endif
734 
735 
736 /*-
737  *-----------------------------------------------------------------------
738  * ArchFindMember --
739  *	Locate a member of an archive, given the path of the archive and
740  *	the path of the desired member. If the archive is to be modified,
741  *	the mode should be "r+", if not, it should be "r".
742  *	The passed struct ar_hdr structure is filled in.
743  *
744  * Input:
745  *	archive		Path to the archive
746  *	member		Name of member. If it is a path, only the last
747  *			component is used.
748  *	arhPtr		Pointer to header structure to be filled in
749  *	mode		The mode for opening the stream
750  *
751  * Results:
752  *	An FILE *, opened for reading and writing, positioned at the
753  *	start of the member's struct ar_hdr, or NULL if the member was
754  *	nonexistent. The current struct ar_hdr for member.
755  *-----------------------------------------------------------------------
756  */
757 static FILE *
758 ArchFindMember(const char *archive, const char *member, struct ar_hdr *arhPtr,
759 	       const char *mode)
760 {
761     FILE *arch;			/* Stream to archive */
762     int size;			/* Size of archive member */
763     char magic[SARMAG];
764     size_t len, tlen;
765     const char *lastSlash;
766 
767     arch = fopen(archive, mode);
768     if (arch == NULL)
769 	return NULL;
770 
771     /*
772      * We use the ARMAG string to make sure this is an archive we
773      * can handle...
774      */
775     if ((fread(magic, SARMAG, 1, arch) != 1) ||
776 	(strncmp(magic, ARMAG, SARMAG) != 0)) {
777 	fclose(arch);
778 	return NULL;
779     }
780 
781     /*
782      * Because of space constraints and similar things, files are archived
783      * using their basename, not the entire path.
784      */
785     lastSlash = strrchr(member, '/');
786     if (lastSlash != NULL)
787 	member = lastSlash + 1;
788 
789     len = tlen = strlen(member);
790     if (len > sizeof(arhPtr->AR_NAME)) {
791 	tlen = sizeof(arhPtr->AR_NAME);
792     }
793 
794     while (fread((char *)arhPtr, sizeof(struct ar_hdr), 1, arch) == 1) {
795 
796 	if (strncmp(arhPtr->AR_FMAG, ARFMAG, sizeof(arhPtr->AR_FMAG)) != 0) {
797 	    /*
798 	     * The header is bogus, so the archive is bad
799 	     * and there's no way we can recover...
800 	     */
801 	    fclose(arch);
802 	    return NULL;
803 	}
804 
805 	if (strncmp(member, arhPtr->AR_NAME, tlen) == 0) {
806 	    /*
807 	     * If the member's name doesn't take up the entire 'name' field,
808 	     * we have to be careful of matching prefixes. Names are space-
809 	     * padded to the right, so if the character in 'name' at the end
810 	     * of the matched string is anything but a space, this isn't the
811 	     * member we sought.
812 	     */
813 	    if (tlen != sizeof arhPtr->AR_NAME && arhPtr->AR_NAME[tlen] != ' ')
814 		goto skip;
815 
816 	    /*
817 	     * To make life easier, we reposition the file at the start
818 	     * of the header we just read before we return the stream.
819 	     * In a more general situation, it might be better to leave
820 	     * the file at the actual member, rather than its header, but
821 	     * not here...
822 	     */
823 	    if (fseek(arch, -(long)sizeof(struct ar_hdr), SEEK_CUR) != 0) {
824 		fclose(arch);
825 		return NULL;
826 	    }
827 	    return arch;
828 	}
829 
830 #ifdef AR_EFMT1
831 	/*
832 	 * BSD 4.4 extended AR format: #1/<namelen>, with name as the
833 	 * first <namelen> bytes of the file
834 	 */
835 	if (strncmp(arhPtr->AR_NAME, AR_EFMT1, sizeof(AR_EFMT1) - 1) == 0 &&
836 	    ch_isdigit(arhPtr->AR_NAME[sizeof(AR_EFMT1) - 1]))
837 	{
838 	    int elen = atoi(&arhPtr->AR_NAME[sizeof(AR_EFMT1) - 1]);
839 	    char ename[MAXPATHLEN + 1];
840 
841 	    if ((unsigned int)elen > MAXPATHLEN) {
842 		fclose(arch);
843 		return NULL;
844 	    }
845 	    if (fread(ename, (size_t)elen, 1, arch) != 1) {
846 		fclose(arch);
847 		return NULL;
848 	    }
849 	    ename[elen] = '\0';
850 	    if (DEBUG(ARCH) || DEBUG(MAKE)) {
851 		debug_printf("ArchFind: Extended format entry for %s\n", ename);
852 	    }
853 	    if (strncmp(ename, member, len) == 0) {
854 		/* Found as extended name */
855 		if (fseek(arch, -(long)sizeof(struct ar_hdr) - elen,
856 			  SEEK_CUR) != 0) {
857 		    fclose(arch);
858 		    return NULL;
859 		}
860 		return arch;
861 	    }
862 	    if (fseek(arch, -elen, SEEK_CUR) != 0) {
863 		fclose(arch);
864 		return NULL;
865 	    }
866 	}
867 #endif
868 
869 skip:
870 	/*
871 	 * This isn't the member we're after, so we need to advance the
872 	 * stream's pointer to the start of the next header. Files are
873 	 * padded with newlines to an even-byte boundary, so we need to
874 	 * extract the size of the file from the 'size' field of the
875 	 * header and round it up during the seek.
876 	 */
877 	arhPtr->ar_size[sizeof(arhPtr->ar_size) - 1] = '\0';
878 	size = (int)strtol(arhPtr->ar_size, NULL, 10);
879 	if (fseek(arch, (size + 1) & ~1, SEEK_CUR) != 0) {
880 	    fclose(arch);
881 	    return NULL;
882 	}
883     }
884 
885     /*
886      * We've looked everywhere, but the member is not to be found. Close the
887      * archive and return NULL -- an error.
888      */
889     fclose(arch);
890     return NULL;
891 }
892 
893 /*-
894  *-----------------------------------------------------------------------
895  * Arch_Touch --
896  *	Touch a member of an archive.
897  *	The modification time of the entire archive is also changed.
898  *	For a library, this could necessitate the re-ranlib'ing of the
899  *	whole thing.
900  *
901  * Input:
902  *	gn		Node of member to touch
903  *
904  * Results:
905  *	The 'time' field of the member's header is updated.
906  *-----------------------------------------------------------------------
907  */
908 void
909 Arch_Touch(GNode *gn)
910 {
911     FILE *arch;		/* Stream open to archive, positioned properly */
912     struct ar_hdr arh;	/* Current header describing member */
913 
914     arch = ArchFindMember(GNode_VarArchive(gn), GNode_VarMember(gn),
915 			  &arh, "r+");
916 
917     snprintf(arh.AR_DATE, sizeof(arh.AR_DATE), "%-12ld", (long)now);
918 
919     if (arch != NULL) {
920 	(void)fwrite((char *)&arh, sizeof(struct ar_hdr), 1, arch);
921 	fclose(arch);
922     }
923 }
924 
925 /* Given a node which represents a library, touch the thing, making sure that
926  * the table of contents also is touched.
927  *
928  * Both the modification time of the library and of the RANLIBMAG member are
929  * set to 'now'.
930  *
931  * Input:
932  *	gn		The node of the library to touch
933  */
934 void
935 Arch_TouchLib(GNode *gn)
936 {
937 #ifdef RANLIBMAG
938     FILE *	    arch;	/* Stream open to archive */
939     struct ar_hdr   arh;	/* Header describing table of contents */
940     struct utimbuf  times;	/* Times for utime() call */
941 
942     arch = ArchFindMember(gn->path, RANLIBMAG, &arh, "r+");
943     snprintf(arh.AR_DATE, sizeof(arh.AR_DATE), "%-12ld", (long) now);
944 
945     if (arch != NULL) {
946 	(void)fwrite((char *)&arh, sizeof(struct ar_hdr), 1, arch);
947 	fclose(arch);
948 
949 	times.actime = times.modtime = now;
950 	utime(gn->path, &times);
951     }
952 #else
953     (void)gn;
954 #endif
955 }
956 
957 /* Return the modification time of a member of an archive. The mtime field
958  * of the given node is filled in with the value returned by the function.
959  *
960  * Input:
961  *	gn		Node describing archive member
962  */
963 time_t
964 Arch_MTime(GNode *gn)
965 {
966     struct ar_hdr *arhPtr;	/* Header of desired member */
967     time_t modTime;		/* Modification time as an integer */
968 
969     arhPtr = ArchStatMember(GNode_VarArchive(gn), GNode_VarMember(gn), TRUE);
970     if (arhPtr != NULL) {
971 	modTime = (time_t)strtol(arhPtr->AR_DATE, NULL, 10);
972     } else {
973 	modTime = 0;
974     }
975 
976     gn->mtime = modTime;
977     return modTime;
978 }
979 
980 /* Given a non-existent archive member's node, get its modification time from
981  * its archived form, if it exists. gn->mtime is filled in as well. */
982 time_t
983 Arch_MemMTime(GNode *gn)
984 {
985     GNodeListNode *ln;
986 
987     for (ln = gn->parents->first; ln != NULL; ln = ln->next) {
988 	GNode *pgn = ln->datum;
989 
990 	if (pgn->type & OP_ARCHV) {
991 	    /*
992 	     * If the parent is an archive specification and is being made
993 	     * and its member's name matches the name of the node we were
994 	     * given, record the modification time of the parent in the
995 	     * child. We keep searching its parents in case some other
996 	     * parent requires this child to exist...
997 	     */
998 	    const char *nameStart = strchr(pgn->name, '(') + 1;
999 	    const char *nameEnd = strchr(nameStart, ')');
1000 	    size_t nameLen = (size_t)(nameEnd - nameStart);
1001 
1002 	    if ((pgn->flags & REMAKE) &&
1003 		strncmp(nameStart, gn->name, nameLen) == 0) {
1004 		gn->mtime = Arch_MTime(pgn);
1005 	    }
1006 	} else if (pgn->flags & REMAKE) {
1007 	    /*
1008 	     * Something which isn't a library depends on the existence of
1009 	     * this target, so it needs to exist.
1010 	     */
1011 	    gn->mtime = 0;
1012 	    break;
1013 	}
1014     }
1015 
1016     return gn->mtime;
1017 }
1018 
1019 /* Search for a library along the given search path.
1020  *
1021  * The node's 'path' field is set to the found path (including the
1022  * actual file name, not -l...). If the system can handle the -L
1023  * flag when linking (or we cannot find the library), we assume that
1024  * the user has placed the .LIBS variable in the final linking
1025  * command (or the linker will know where to find it) and set the
1026  * TARGET variable for this node to be the node's name. Otherwise,
1027  * we set the TARGET variable to be the full path of the library,
1028  * as returned by Dir_FindFile.
1029  *
1030  * Input:
1031  *	gn		Node of library to find
1032  */
1033 void
1034 Arch_FindLib(GNode *gn, SearchPath *path)
1035 {
1036     char *libName = str_concat3("lib", gn->name + 2, ".a");
1037     gn->path = Dir_FindFile(libName, path);
1038     free(libName);
1039 
1040 #ifdef LIBRARIES
1041     Var_Set(TARGET, gn->name, gn);
1042 #else
1043     Var_Set(TARGET, gn->path == NULL ? gn->name : gn->path, gn);
1044 #endif
1045 }
1046 
1047 /* Decide if a node with the OP_LIB attribute is out-of-date. Called from
1048  * Make_OODate to make its life easier.
1049  * The library will be hashed if it hasn't been already.
1050  *
1051  * There are several ways for a library to be out-of-date that are
1052  * not available to ordinary files. In addition, there are ways
1053  * that are open to regular files that are not available to
1054  * libraries. A library that is only used as a source is never
1055  * considered out-of-date by itself. This does not preclude the
1056  * library's modification time from making its parent be out-of-date.
1057  * A library will be considered out-of-date for any of these reasons,
1058  * given that it is a target on a dependency line somewhere:
1059  *
1060  *	Its modification time is less than that of one of its sources
1061  *	(gn->mtime < gn->youngestChild->mtime).
1062  *
1063  *	Its modification time is greater than the time at which the make
1064  *	began (i.e. it's been modified in the course of the make, probably
1065  *	by archiving).
1066  *
1067  *	The modification time of one of its sources is greater than the one
1068  *	of its RANLIBMAG member (i.e. its table of contents is out-of-date).
1069  *	We don't compare of the archive time vs. TOC time because they can be
1070  *	too close. In my opinion we should not bother with the TOC at all
1071  *	since this is used by 'ar' rules that affect the data contents of the
1072  *	archive, not by ranlib rules, which affect the TOC.
1073  *
1074  * Input:
1075  *	gn		The library's graph node
1076  *
1077  * Results:
1078  *	TRUE if the library is out-of-date. FALSE otherwise.
1079  */
1080 Boolean
1081 Arch_LibOODate(GNode *gn)
1082 {
1083     Boolean oodate;
1084 
1085     if (gn->type & OP_PHONY) {
1086 	oodate = TRUE;
1087     } else if (!GNode_IsTarget(gn) && Lst_IsEmpty(gn->children)) {
1088 	oodate = FALSE;
1089     } else if ((!Lst_IsEmpty(gn->children) && gn->youngestChild == NULL) ||
1090 	       (gn->mtime > now) ||
1091 	       (gn->youngestChild != NULL &&
1092 		gn->mtime < gn->youngestChild->mtime)) {
1093 	oodate = TRUE;
1094     } else {
1095 #ifdef RANLIBMAG
1096 	struct ar_hdr *arhPtr;	/* Header for __.SYMDEF */
1097 	int modTimeTOC;		/* The table-of-contents's mod time */
1098 
1099 	arhPtr = ArchStatMember(gn->path, RANLIBMAG, FALSE);
1100 
1101 	if (arhPtr != NULL) {
1102 	    modTimeTOC = (int)strtol(arhPtr->AR_DATE, NULL, 10);
1103 
1104 	    if (DEBUG(ARCH) || DEBUG(MAKE)) {
1105 		debug_printf("%s modified %s...", RANLIBMAG, Targ_FmtTime(modTimeTOC));
1106 	    }
1107 	    oodate = (gn->youngestChild == NULL || gn->youngestChild->mtime > modTimeTOC);
1108 	} else {
1109 	    /*
1110 	     * A library w/o a table of contents is out-of-date
1111 	     */
1112 	    if (DEBUG(ARCH) || DEBUG(MAKE)) {
1113 		debug_printf("No t.o.c....");
1114 	    }
1115 	    oodate = TRUE;
1116 	}
1117 #else
1118 	oodate = FALSE;
1119 #endif
1120     }
1121     return oodate;
1122 }
1123 
1124 /* Initialize the archives module. */
1125 void
1126 Arch_Init(void)
1127 {
1128     archives = Lst_New();
1129 }
1130 
1131 /* Clean up the archives module. */
1132 void
1133 Arch_End(void)
1134 {
1135 #ifdef CLEANUP
1136     Lst_Destroy(archives, ArchFree);
1137 #endif
1138 }
1139 
1140 Boolean
1141 Arch_IsLib(GNode *gn)
1142 {
1143     static const char armag[] = "!<arch>\n";
1144     char buf[sizeof armag - 1];
1145     int fd;
1146 
1147     if ((fd = open(gn->path, O_RDONLY)) == -1)
1148 	return FALSE;
1149 
1150     if (read(fd, buf, sizeof buf) != sizeof buf) {
1151 	(void)close(fd);
1152 	return FALSE;
1153     }
1154 
1155     (void)close(fd);
1156 
1157     return memcmp(buf, armag, sizeof buf) == 0;
1158 }
1159