1*a8c56be4SSimon J. Gerraty /* $NetBSD: arch.c,v 1.223 2025/06/28 22:39:27 rillig Exp $ */
23955d011SMarcel Moolenaar
33955d011SMarcel Moolenaar /*
43955d011SMarcel Moolenaar * Copyright (c) 1988, 1989, 1990, 1993
53955d011SMarcel Moolenaar * The Regents of the University of California. All rights reserved.
63955d011SMarcel Moolenaar *
73955d011SMarcel Moolenaar * This code is derived from software contributed to Berkeley by
83955d011SMarcel Moolenaar * Adam de Boor.
93955d011SMarcel Moolenaar *
103955d011SMarcel Moolenaar * Redistribution and use in source and binary forms, with or without
113955d011SMarcel Moolenaar * modification, are permitted provided that the following conditions
123955d011SMarcel Moolenaar * are met:
133955d011SMarcel Moolenaar * 1. Redistributions of source code must retain the above copyright
143955d011SMarcel Moolenaar * notice, this list of conditions and the following disclaimer.
153955d011SMarcel Moolenaar * 2. Redistributions in binary form must reproduce the above copyright
163955d011SMarcel Moolenaar * notice, this list of conditions and the following disclaimer in the
173955d011SMarcel Moolenaar * documentation and/or other materials provided with the distribution.
183955d011SMarcel Moolenaar * 3. Neither the name of the University nor the names of its contributors
193955d011SMarcel Moolenaar * may be used to endorse or promote products derived from this software
203955d011SMarcel Moolenaar * without specific prior written permission.
213955d011SMarcel Moolenaar *
223955d011SMarcel Moolenaar * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
233955d011SMarcel Moolenaar * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
243955d011SMarcel Moolenaar * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
253955d011SMarcel Moolenaar * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
263955d011SMarcel Moolenaar * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
273955d011SMarcel Moolenaar * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
283955d011SMarcel Moolenaar * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
293955d011SMarcel Moolenaar * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
303955d011SMarcel Moolenaar * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
313955d011SMarcel Moolenaar * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
323955d011SMarcel Moolenaar * SUCH DAMAGE.
333955d011SMarcel Moolenaar */
343955d011SMarcel Moolenaar
353955d011SMarcel Moolenaar /*
363955d011SMarcel Moolenaar * Copyright (c) 1989 by Berkeley Softworks
373955d011SMarcel Moolenaar * All rights reserved.
383955d011SMarcel Moolenaar *
393955d011SMarcel Moolenaar * This code is derived from software contributed to Berkeley by
403955d011SMarcel Moolenaar * Adam de Boor.
413955d011SMarcel Moolenaar *
423955d011SMarcel Moolenaar * Redistribution and use in source and binary forms, with or without
433955d011SMarcel Moolenaar * modification, are permitted provided that the following conditions
443955d011SMarcel Moolenaar * are met:
453955d011SMarcel Moolenaar * 1. Redistributions of source code must retain the above copyright
463955d011SMarcel Moolenaar * notice, this list of conditions and the following disclaimer.
473955d011SMarcel Moolenaar * 2. Redistributions in binary form must reproduce the above copyright
483955d011SMarcel Moolenaar * notice, this list of conditions and the following disclaimer in the
493955d011SMarcel Moolenaar * documentation and/or other materials provided with the distribution.
503955d011SMarcel Moolenaar * 3. All advertising materials mentioning features or use of this software
513955d011SMarcel Moolenaar * must display the following acknowledgement:
523955d011SMarcel Moolenaar * This product includes software developed by the University of
533955d011SMarcel Moolenaar * California, Berkeley and its contributors.
543955d011SMarcel Moolenaar * 4. Neither the name of the University nor the names of its contributors
553955d011SMarcel Moolenaar * may be used to endorse or promote products derived from this software
563955d011SMarcel Moolenaar * without specific prior written permission.
573955d011SMarcel Moolenaar *
583955d011SMarcel Moolenaar * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
593955d011SMarcel Moolenaar * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
603955d011SMarcel Moolenaar * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
613955d011SMarcel Moolenaar * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
623955d011SMarcel Moolenaar * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
633955d011SMarcel Moolenaar * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
643955d011SMarcel Moolenaar * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
653955d011SMarcel Moolenaar * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
663955d011SMarcel Moolenaar * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
673955d011SMarcel Moolenaar * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
683955d011SMarcel Moolenaar * SUCH DAMAGE.
693955d011SMarcel Moolenaar */
703955d011SMarcel Moolenaar
7106b9b3e0SSimon J. Gerraty /*
7206b9b3e0SSimon J. Gerraty * Manipulate libraries, archives and their members.
733955d011SMarcel Moolenaar *
74e2eeea75SSimon J. Gerraty * The first time an archive is referenced, all of its members' headers are
75e2eeea75SSimon J. Gerraty * read and cached and the archive closed again. All cached archives are kept
76e2eeea75SSimon J. Gerraty * on a list which is searched each time an archive member is referenced.
773955d011SMarcel Moolenaar *
783955d011SMarcel Moolenaar * The interface to this module is:
79e2eeea75SSimon J. Gerraty *
80e2eeea75SSimon J. Gerraty * Arch_Init Initialize this module.
81e2eeea75SSimon J. Gerraty *
82e2eeea75SSimon J. Gerraty * Arch_End Clean up this module.
83e2eeea75SSimon J. Gerraty *
84956e45f6SSimon J. Gerraty * Arch_ParseArchive
85e2eeea75SSimon J. Gerraty * Parse an archive specification such as
86e2eeea75SSimon J. Gerraty * "archive.a(member1 member2)".
873955d011SMarcel Moolenaar *
883955d011SMarcel Moolenaar * Arch_Touch Alter the modification time of the archive
893955d011SMarcel Moolenaar * member described by the given node to be
90e2eeea75SSimon J. Gerraty * the time when make was started.
913955d011SMarcel Moolenaar *
923955d011SMarcel Moolenaar * Arch_TouchLib Update the modification time of the library
933955d011SMarcel Moolenaar * described by the given node. This is special
943955d011SMarcel Moolenaar * because it also updates the modification time
953955d011SMarcel Moolenaar * of the library's table of contents.
963955d011SMarcel Moolenaar *
97e2eeea75SSimon J. Gerraty * Arch_UpdateMTime
98e2eeea75SSimon J. Gerraty * Find the modification time of a member of
99e2eeea75SSimon J. Gerraty * an archive *in the archive* and place it in the
100e2eeea75SSimon J. Gerraty * member's GNode.
1013955d011SMarcel Moolenaar *
102e2eeea75SSimon J. Gerraty * Arch_UpdateMemberMTime
103e2eeea75SSimon J. Gerraty * Find the modification time of a member of
1043955d011SMarcel Moolenaar * an archive. Called when the member doesn't
1053955d011SMarcel Moolenaar * already exist. Looks in the archive for the
1063955d011SMarcel Moolenaar * modification time. Returns the modification
1073955d011SMarcel Moolenaar * time.
1083955d011SMarcel Moolenaar *
1093955d011SMarcel Moolenaar * Arch_FindLib Search for a library along a path. The
1103955d011SMarcel Moolenaar * library name in the GNode should be in
1113955d011SMarcel Moolenaar * -l<name> format.
1123955d011SMarcel Moolenaar *
113e2eeea75SSimon J. Gerraty * Arch_LibOODate Decide if a library node is out-of-date.
1143955d011SMarcel Moolenaar */
1153955d011SMarcel Moolenaar
1163955d011SMarcel Moolenaar #ifdef HAVE_CONFIG_H
1173955d011SMarcel Moolenaar # include "config.h"
1183955d011SMarcel Moolenaar #endif
1193955d011SMarcel Moolenaar #include <sys/types.h>
1203955d011SMarcel Moolenaar #include <sys/stat.h>
1213955d011SMarcel Moolenaar #include <sys/time.h>
1223955d011SMarcel Moolenaar #include <sys/param.h>
1233955d011SMarcel Moolenaar #ifdef HAVE_AR_H
1243955d011SMarcel Moolenaar #include <ar.h>
1253955d011SMarcel Moolenaar #else
1263955d011SMarcel Moolenaar struct ar_hdr {
1273955d011SMarcel Moolenaar char ar_name[16]; /* name */
1283955d011SMarcel Moolenaar char ar_date[12]; /* modification time */
1293955d011SMarcel Moolenaar char ar_uid[6]; /* user id */
1303955d011SMarcel Moolenaar char ar_gid[6]; /* group id */
1313955d011SMarcel Moolenaar char ar_mode[8]; /* octal file permissions */
1323955d011SMarcel Moolenaar char ar_size[10]; /* size in bytes */
1333955d011SMarcel Moolenaar #ifndef ARFMAG
1343955d011SMarcel Moolenaar #define ARFMAG "`\n"
1353955d011SMarcel Moolenaar #endif
1363955d011SMarcel Moolenaar char ar_fmag[2]; /* consistency check */
1373955d011SMarcel Moolenaar };
1383955d011SMarcel Moolenaar #endif
1393955d011SMarcel Moolenaar #if defined(HAVE_RANLIB_H) && !(defined(__ELF__) || defined(NO_RANLIB))
1403955d011SMarcel Moolenaar #include <ranlib.h>
1413955d011SMarcel Moolenaar #endif
1423955d011SMarcel Moolenaar #ifdef HAVE_UTIME_H
1433955d011SMarcel Moolenaar #include <utime.h>
1443955d011SMarcel Moolenaar #endif
1453955d011SMarcel Moolenaar
1463955d011SMarcel Moolenaar #include "make.h"
1473955d011SMarcel Moolenaar #include "dir.h"
1483955d011SMarcel Moolenaar
149956e45f6SSimon J. Gerraty /* "@(#)arch.c 8.2 (Berkeley) 1/2/94" */
150*a8c56be4SSimon J. Gerraty MAKE_RCSID("$NetBSD: arch.c,v 1.223 2025/06/28 22:39:27 rillig Exp $");
1513955d011SMarcel Moolenaar
152956e45f6SSimon J. Gerraty typedef struct List ArchList;
153956e45f6SSimon J. Gerraty typedef struct ListNode ArchListNode;
154956e45f6SSimon J. Gerraty
15506b9b3e0SSimon J. Gerraty static ArchList archives; /* The archives we've already examined */
1563955d011SMarcel Moolenaar
1573955d011SMarcel Moolenaar typedef struct Arch {
158548bfc56SSimon J. Gerraty char *name;
159956e45f6SSimon J. Gerraty HashTable members; /* All the members of the archive described
1603955d011SMarcel Moolenaar * by <name, struct ar_hdr *> key/value pairs */
1613955d011SMarcel Moolenaar char *fnametab; /* Extended name table strings */
1623955d011SMarcel Moolenaar size_t fnamesize; /* Size of the string table */
1633955d011SMarcel Moolenaar } Arch;
1643955d011SMarcel Moolenaar
1652c3632d1SSimon J. Gerraty static FILE *ArchFindMember(const char *, const char *,
1662c3632d1SSimon J. Gerraty struct ar_hdr *, const char *);
1673955d011SMarcel Moolenaar #if defined(__svr4__) || defined(__SVR4) || defined(__ELF__)
1683955d011SMarcel Moolenaar #define SVR4ARCHIVES
1693955d011SMarcel Moolenaar static int ArchSVR4Entry(Arch *, char *, size_t, FILE *);
1703955d011SMarcel Moolenaar #endif
1713955d011SMarcel Moolenaar
1723955d011SMarcel Moolenaar
1733955d011SMarcel Moolenaar #if defined(_AIX)
1743955d011SMarcel Moolenaar # define AR_NAME _ar_name.ar_name
1753955d011SMarcel Moolenaar # define AR_FMAG _ar_name.ar_fmag
1763955d011SMarcel Moolenaar # define SARMAG SAIAMAG
1773955d011SMarcel Moolenaar # define ARMAG AIAMAG
1783955d011SMarcel Moolenaar # define ARFMAG AIAFMAG
1793955d011SMarcel Moolenaar #endif
1803955d011SMarcel Moolenaar #ifndef AR_NAME
1813955d011SMarcel Moolenaar # define AR_NAME ar_name
1823955d011SMarcel Moolenaar #endif
1833955d011SMarcel Moolenaar #ifndef AR_DATE
1843955d011SMarcel Moolenaar # define AR_DATE ar_date
1853955d011SMarcel Moolenaar #endif
1863955d011SMarcel Moolenaar #ifndef AR_SIZE
1873955d011SMarcel Moolenaar # define AR_SIZE ar_size
1883955d011SMarcel Moolenaar #endif
1893955d011SMarcel Moolenaar #ifndef AR_FMAG
1903955d011SMarcel Moolenaar # define AR_FMAG ar_fmag
1913955d011SMarcel Moolenaar #endif
1923955d011SMarcel Moolenaar #ifndef ARMAG
1933955d011SMarcel Moolenaar # define ARMAG "!<arch>\n"
1943955d011SMarcel Moolenaar #endif
1953955d011SMarcel Moolenaar #ifndef SARMAG
1963955d011SMarcel Moolenaar # define SARMAG 8
1973955d011SMarcel Moolenaar #endif
1983955d011SMarcel Moolenaar
1993955d011SMarcel Moolenaar
2003955d011SMarcel Moolenaar #ifdef CLEANUP
2013955d011SMarcel Moolenaar static void
ArchFree(Arch * a)202548bfc56SSimon J. Gerraty ArchFree(Arch *a)
2033955d011SMarcel Moolenaar {
204956e45f6SSimon J. Gerraty HashIter hi;
2053955d011SMarcel Moolenaar
206956e45f6SSimon J. Gerraty HashIter_Init(&hi, &a->members);
2078d5c8e21SSimon J. Gerraty while (HashIter_Next(&hi))
208956e45f6SSimon J. Gerraty free(hi.entry->value);
2093955d011SMarcel Moolenaar
2103955d011SMarcel Moolenaar free(a->name);
2113955d011SMarcel Moolenaar free(a->fnametab);
212956e45f6SSimon J. Gerraty HashTable_Done(&a->members);
2133955d011SMarcel Moolenaar free(a);
2143955d011SMarcel Moolenaar }
2153955d011SMarcel Moolenaar #endif
2163955d011SMarcel Moolenaar
217b0c40a00SSimon J. Gerraty /* Return "archive(member)". */
218548bfc56SSimon J. Gerraty MAKE_ATTR_NOINLINE static char *
FullName(const char * archive,const char * member)219b0c40a00SSimon J. Gerraty FullName(const char *archive, const char *member)
220b0c40a00SSimon J. Gerraty {
221548bfc56SSimon J. Gerraty Buffer buf;
222548bfc56SSimon J. Gerraty Buf_Init(&buf);
223548bfc56SSimon J. Gerraty Buf_AddStr(&buf, archive);
224548bfc56SSimon J. Gerraty Buf_AddStr(&buf, "(");
225548bfc56SSimon J. Gerraty Buf_AddStr(&buf, member);
226548bfc56SSimon J. Gerraty Buf_AddStr(&buf, ")");
227548bfc56SSimon J. Gerraty return Buf_DoneData(&buf);
228b0c40a00SSimon J. Gerraty }
2293955d011SMarcel Moolenaar
230e2eeea75SSimon J. Gerraty /*
231e2eeea75SSimon J. Gerraty * Parse an archive specification such as "archive.a(member1 member2.${EXT})",
232548bfc56SSimon J. Gerraty * adding nodes for the expanded members to gns. If successful, advance pp
233548bfc56SSimon J. Gerraty * beyond the archive specification and any trailing whitespace.
2343955d011SMarcel Moolenaar */
235b0c40a00SSimon J. Gerraty bool
Arch_ParseArchive(char ** pp,GNodeList * gns,GNode * scope)236dba7b0efSSimon J. Gerraty Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope)
2373955d011SMarcel Moolenaar {
2389f45a3c8SSimon J. Gerraty char *spec; /* For modifying some bytes of *pp */
2399f45a3c8SSimon J. Gerraty const char *cp; /* Pointer into line */
2403955d011SMarcel Moolenaar GNode *gn; /* New node */
2419f45a3c8SSimon J. Gerraty FStr lib; /* Library-part of specification */
24212904384SSimon J. Gerraty FStr mem; /* Member-part of specification */
2433955d011SMarcel Moolenaar char saveChar; /* Ending delimiter of member-name */
244d5e0a182SSimon J. Gerraty bool expandLib; /* Whether the parsed lib contains
2459f45a3c8SSimon J. Gerraty * expressions that need to be expanded */
2463955d011SMarcel Moolenaar
2479f45a3c8SSimon J. Gerraty spec = *pp;
2489f45a3c8SSimon J. Gerraty lib = FStr_InitRefer(spec);
2499f45a3c8SSimon J. Gerraty expandLib = false;
2503955d011SMarcel Moolenaar
2519f45a3c8SSimon J. Gerraty for (cp = lib.str; *cp != '(' && *cp != '\0';) {
2523955d011SMarcel Moolenaar if (*cp == '$') {
253d5e0a182SSimon J. Gerraty /* Expand nested expressions. */
25406b9b3e0SSimon J. Gerraty /* XXX: This code can probably be shortened. */
255956e45f6SSimon J. Gerraty const char *nested_p = cp;
25606b9b3e0SSimon J. Gerraty FStr result;
257b0c40a00SSimon J. Gerraty bool isError;
2583955d011SMarcel Moolenaar
259e2eeea75SSimon J. Gerraty /* XXX: is expanded twice: once here and once below */
2608d5c8e21SSimon J. Gerraty result = Var_Parse(&nested_p, scope,
2618d5c8e21SSimon J. Gerraty VARE_EVAL_DEFINED);
262956e45f6SSimon J. Gerraty /* TODO: handle errors */
26306b9b3e0SSimon J. Gerraty isError = result.str == var_Error;
26406b9b3e0SSimon J. Gerraty FStr_Done(&result);
2652c3632d1SSimon J. Gerraty if (isError)
266b0c40a00SSimon J. Gerraty return false;
267be19d90bSSimon J. Gerraty
2689f45a3c8SSimon J. Gerraty expandLib = true;
269956e45f6SSimon J. Gerraty cp += nested_p - cp;
270956e45f6SSimon J. Gerraty } else
271956e45f6SSimon J. Gerraty cp++;
2723955d011SMarcel Moolenaar }
2733955d011SMarcel Moolenaar
2749f45a3c8SSimon J. Gerraty spec[cp++ - spec] = '\0';
2759f45a3c8SSimon J. Gerraty if (expandLib)
2768d5c8e21SSimon J. Gerraty Var_Expand(&lib, scope, VARE_EVAL_DEFINED);
2773955d011SMarcel Moolenaar
2783955d011SMarcel Moolenaar for (;;) {
2793955d011SMarcel Moolenaar /*
2803955d011SMarcel Moolenaar * First skip to the start of the member's name, mark that
2813955d011SMarcel Moolenaar * place and skip to the end of it (either white-space or
2823955d011SMarcel Moolenaar * a close paren).
2833955d011SMarcel Moolenaar */
284b0c40a00SSimon J. Gerraty bool doSubst = false;
2853955d011SMarcel Moolenaar
2869f45a3c8SSimon J. Gerraty cpp_skip_whitespace(&cp);
287956e45f6SSimon J. Gerraty
28812904384SSimon J. Gerraty mem = FStr_InitRefer(cp);
289956e45f6SSimon J. Gerraty while (*cp != '\0' && *cp != ')' && !ch_isspace(*cp)) {
2903955d011SMarcel Moolenaar if (*cp == '$') {
291d5e0a182SSimon J. Gerraty /* Expand nested expressions. */
2929f45a3c8SSimon J. Gerraty /*
2939f45a3c8SSimon J. Gerraty * XXX: This code can probably be shortened.
2949f45a3c8SSimon J. Gerraty */
29506b9b3e0SSimon J. Gerraty FStr result;
296b0c40a00SSimon J. Gerraty bool isError;
297956e45f6SSimon J. Gerraty const char *nested_p = cp;
2983955d011SMarcel Moolenaar
2998c973ee2SSimon J. Gerraty result = Var_Parse(&nested_p, scope,
3008d5c8e21SSimon J. Gerraty VARE_EVAL_DEFINED);
301956e45f6SSimon J. Gerraty /* TODO: handle errors */
30206b9b3e0SSimon J. Gerraty isError = result.str == var_Error;
30306b9b3e0SSimon J. Gerraty FStr_Done(&result);
304be19d90bSSimon J. Gerraty
3052c3632d1SSimon J. Gerraty if (isError)
306b0c40a00SSimon J. Gerraty return false;
3073955d011SMarcel Moolenaar
308b0c40a00SSimon J. Gerraty doSubst = true;
309956e45f6SSimon J. Gerraty cp += nested_p - cp;
3103955d011SMarcel Moolenaar } else {
3113955d011SMarcel Moolenaar cp++;
3123955d011SMarcel Moolenaar }
3133955d011SMarcel Moolenaar }
3143955d011SMarcel Moolenaar
3153955d011SMarcel Moolenaar if (*cp == '\0') {
31606b9b3e0SSimon J. Gerraty Parse_Error(PARSE_FATAL,
317*a8c56be4SSimon J. Gerraty "Missing \")\" in archive specification");
318b0c40a00SSimon J. Gerraty return false;
3193955d011SMarcel Moolenaar }
3203955d011SMarcel Moolenaar
32112904384SSimon J. Gerraty if (cp == mem.str)
3223955d011SMarcel Moolenaar break;
3233955d011SMarcel Moolenaar
3243955d011SMarcel Moolenaar saveChar = *cp;
3259f45a3c8SSimon J. Gerraty spec[cp - spec] = '\0';
3263955d011SMarcel Moolenaar
3273955d011SMarcel Moolenaar /*
3283955d011SMarcel Moolenaar * XXX: This should be taken care of intelligently by
32906b9b3e0SSimon J. Gerraty * SuffExpandChildren, both for the archive and the member
33006b9b3e0SSimon J. Gerraty * portions.
3313955d011SMarcel Moolenaar */
3323955d011SMarcel Moolenaar /*
3333955d011SMarcel Moolenaar * If member contains variables, try and substitute for them.
3344fde40d9SSimon J. Gerraty * This slows down archive specs with dynamic sources, since
3354fde40d9SSimon J. Gerraty * they are (non-)substituted three times, but we need to do
3364fde40d9SSimon J. Gerraty * this since SuffExpandChildren calls us, otherwise we could
3374fde40d9SSimon J. Gerraty * assume the substitutions would be taken care of later.
3383955d011SMarcel Moolenaar */
3393955d011SMarcel Moolenaar if (doSubst) {
34006b9b3e0SSimon J. Gerraty char *fullName;
3419f45a3c8SSimon J. Gerraty char *p;
34212904384SSimon J. Gerraty const char *unexpandedMem = mem.str;
3433955d011SMarcel Moolenaar
3448d5c8e21SSimon J. Gerraty Var_Expand(&mem, scope, VARE_EVAL_DEFINED);
3453955d011SMarcel Moolenaar
3463955d011SMarcel Moolenaar /*
34706b9b3e0SSimon J. Gerraty * Now form an archive spec and recurse to deal with
34806b9b3e0SSimon J. Gerraty * nested variables and multi-word variable values.
3493955d011SMarcel Moolenaar */
3509f45a3c8SSimon J. Gerraty fullName = FullName(lib.str, mem.str);
35106b9b3e0SSimon J. Gerraty p = fullName;
3523955d011SMarcel Moolenaar
35312904384SSimon J. Gerraty if (strcmp(mem.str, unexpandedMem) == 0) {
3543955d011SMarcel Moolenaar /*
35506b9b3e0SSimon J. Gerraty * Must contain dynamic sources, so we can't
35606b9b3e0SSimon J. Gerraty * deal with it now. Just create an ARCHV node
357548bfc56SSimon J. Gerraty * and let SuffExpandChildren handle it.
3583955d011SMarcel Moolenaar */
35906b9b3e0SSimon J. Gerraty gn = Targ_GetNode(fullName);
3603955d011SMarcel Moolenaar gn->type |= OP_ARCHV;
36106b9b3e0SSimon J. Gerraty Lst_Append(gns, gn);
362956e45f6SSimon J. Gerraty
363dba7b0efSSimon J. Gerraty } else if (!Arch_ParseArchive(&p, gns, scope)) {
364956e45f6SSimon J. Gerraty /* Error in nested call. */
36506b9b3e0SSimon J. Gerraty free(fullName);
36606b9b3e0SSimon J. Gerraty /* XXX: does unexpandedMemName leak? */
367b0c40a00SSimon J. Gerraty return false;
3683955d011SMarcel Moolenaar }
36906b9b3e0SSimon J. Gerraty free(fullName);
37006b9b3e0SSimon J. Gerraty /* XXX: does unexpandedMemName leak? */
3713955d011SMarcel Moolenaar
37212904384SSimon J. Gerraty } else if (Dir_HasWildcards(mem.str)) {
37306b9b3e0SSimon J. Gerraty StringList members = LST_INIT;
37412904384SSimon J. Gerraty SearchPath_Expand(&dirSearchPath, mem.str, &members);
375956e45f6SSimon J. Gerraty
37606b9b3e0SSimon J. Gerraty while (!Lst_IsEmpty(&members)) {
37706b9b3e0SSimon J. Gerraty char *member = Lst_Dequeue(&members);
3789f45a3c8SSimon J. Gerraty char *fullname = FullName(lib.str, member);
3793955d011SMarcel Moolenaar free(member);
3802c3632d1SSimon J. Gerraty
381956e45f6SSimon J. Gerraty gn = Targ_GetNode(fullname);
382956e45f6SSimon J. Gerraty free(fullname);
383956e45f6SSimon J. Gerraty
3843955d011SMarcel Moolenaar gn->type |= OP_ARCHV;
38506b9b3e0SSimon J. Gerraty Lst_Append(gns, gn);
3863955d011SMarcel Moolenaar }
38706b9b3e0SSimon J. Gerraty Lst_Done(&members);
3882c3632d1SSimon J. Gerraty
3893955d011SMarcel Moolenaar } else {
3909f45a3c8SSimon J. Gerraty char *fullname = FullName(lib.str, mem.str);
391956e45f6SSimon J. Gerraty gn = Targ_GetNode(fullname);
392956e45f6SSimon J. Gerraty free(fullname);
393956e45f6SSimon J. Gerraty
3943955d011SMarcel Moolenaar gn->type |= OP_ARCHV;
39506b9b3e0SSimon J. Gerraty Lst_Append(gns, gn);
3963955d011SMarcel Moolenaar }
39712904384SSimon J. Gerraty FStr_Done(&mem);
3983955d011SMarcel Moolenaar
3999f45a3c8SSimon J. Gerraty spec[cp - spec] = saveChar;
4003955d011SMarcel Moolenaar }
4013955d011SMarcel Moolenaar
4029f45a3c8SSimon J. Gerraty FStr_Done(&lib);
4033955d011SMarcel Moolenaar
404956e45f6SSimon J. Gerraty cp++; /* skip the ')' */
4059f45a3c8SSimon J. Gerraty cpp_skip_whitespace(&cp);
4069f45a3c8SSimon J. Gerraty *pp += cp - *pp;
407b0c40a00SSimon J. Gerraty return true;
4083955d011SMarcel Moolenaar }
4093955d011SMarcel Moolenaar
41006b9b3e0SSimon J. Gerraty /*
411548bfc56SSimon J. Gerraty * Locate a member in an archive.
412e2eeea75SSimon J. Gerraty *
413e2eeea75SSimon J. Gerraty * See ArchFindMember for an almost identical copy of this code.
4143955d011SMarcel Moolenaar */
4153955d011SMarcel Moolenaar static struct ar_hdr *
ArchStatMember(const char * archive,const char * member,bool addToCache)416b0c40a00SSimon J. Gerraty ArchStatMember(const char *archive, const char *member, bool addToCache)
4173955d011SMarcel Moolenaar {
4186a7405f5SSimon J. Gerraty #define AR_MAX_NAME_LEN (sizeof arh.AR_NAME - 1)
41906b9b3e0SSimon J. Gerraty FILE *arch;
4202c3632d1SSimon J. Gerraty size_t size; /* Size of archive member */
4213955d011SMarcel Moolenaar char magic[SARMAG];
422956e45f6SSimon J. Gerraty ArchListNode *ln;
423548bfc56SSimon J. Gerraty Arch *ar;
424548bfc56SSimon J. Gerraty struct ar_hdr arh;
4253955d011SMarcel Moolenaar char memName[MAXPATHLEN + 1];
4263955d011SMarcel Moolenaar /* Current member name while hashing. */
4273955d011SMarcel Moolenaar
42806b9b3e0SSimon J. Gerraty member = str_basename(member);
429956e45f6SSimon J. Gerraty
43006b9b3e0SSimon J. Gerraty for (ln = archives.first; ln != NULL; ln = ln->next) {
431e2eeea75SSimon J. Gerraty const Arch *a = ln->datum;
432e2eeea75SSimon J. Gerraty if (strcmp(a->name, archive) == 0)
433956e45f6SSimon J. Gerraty break;
4343955d011SMarcel Moolenaar }
4353955d011SMarcel Moolenaar
4363955d011SMarcel Moolenaar if (ln != NULL) {
437956e45f6SSimon J. Gerraty struct ar_hdr *hdr;
4383955d011SMarcel Moolenaar
439956e45f6SSimon J. Gerraty ar = ln->datum;
440956e45f6SSimon J. Gerraty hdr = HashTable_FindValue(&ar->members, member);
441956e45f6SSimon J. Gerraty if (hdr != NULL)
442956e45f6SSimon J. Gerraty return hdr;
4433955d011SMarcel Moolenaar
444956e45f6SSimon J. Gerraty {
4453955d011SMarcel Moolenaar /* Try truncated name */
4463955d011SMarcel Moolenaar char copy[AR_MAX_NAME_LEN + 1];
4473955d011SMarcel Moolenaar size_t len = strlen(member);
4483955d011SMarcel Moolenaar
4493955d011SMarcel Moolenaar if (len > AR_MAX_NAME_LEN) {
4502c3632d1SSimon J. Gerraty snprintf(copy, sizeof copy, "%s", member);
451956e45f6SSimon J. Gerraty hdr = HashTable_FindValue(&ar->members, copy);
452e2eeea75SSimon J. Gerraty }
453956e45f6SSimon J. Gerraty return hdr;
4543955d011SMarcel Moolenaar }
4553955d011SMarcel Moolenaar }
4563955d011SMarcel Moolenaar
457e2eeea75SSimon J. Gerraty if (!addToCache) {
4583955d011SMarcel Moolenaar /*
459548bfc56SSimon J. Gerraty * Since the archive is not to be cached, assume there's no
460548bfc56SSimon J. Gerraty * need to allocate the header, so just declare it static.
4613955d011SMarcel Moolenaar */
4623955d011SMarcel Moolenaar static struct ar_hdr sarh;
4633955d011SMarcel Moolenaar
4643955d011SMarcel Moolenaar arch = ArchFindMember(archive, member, &sarh, "r");
465956e45f6SSimon J. Gerraty if (arch == NULL)
4663955d011SMarcel Moolenaar return NULL;
467956e45f6SSimon J. Gerraty
4683955d011SMarcel Moolenaar fclose(arch);
4693841c287SSimon J. Gerraty return &sarh;
4703955d011SMarcel Moolenaar }
4713955d011SMarcel Moolenaar
4723955d011SMarcel Moolenaar arch = fopen(archive, "r");
473956e45f6SSimon J. Gerraty if (arch == NULL)
4743955d011SMarcel Moolenaar return NULL;
4753955d011SMarcel Moolenaar
476e2eeea75SSimon J. Gerraty if (fread(magic, SARMAG, 1, arch) != 1 ||
477e2eeea75SSimon J. Gerraty strncmp(magic, ARMAG, SARMAG) != 0) {
478e2eeea75SSimon J. Gerraty (void)fclose(arch);
4793955d011SMarcel Moolenaar return NULL;
4803955d011SMarcel Moolenaar }
4813955d011SMarcel Moolenaar
482e2eeea75SSimon J. Gerraty ar = bmake_malloc(sizeof *ar);
4833955d011SMarcel Moolenaar ar->name = bmake_strdup(archive);
4843955d011SMarcel Moolenaar ar->fnametab = NULL;
4853955d011SMarcel Moolenaar ar->fnamesize = 0;
486956e45f6SSimon J. Gerraty HashTable_Init(&ar->members);
4873955d011SMarcel Moolenaar memName[AR_MAX_NAME_LEN] = '\0';
4883955d011SMarcel Moolenaar
489e2eeea75SSimon J. Gerraty while (fread(&arh, sizeof arh, 1, arch) == 1) {
4902c3632d1SSimon J. Gerraty char *nameend;
4912c3632d1SSimon J. Gerraty
492e2eeea75SSimon J. Gerraty if (strncmp(arh.AR_FMAG, ARFMAG, sizeof arh.AR_FMAG) != 0)
493548bfc56SSimon J. Gerraty goto bad_archive;
494e2eeea75SSimon J. Gerraty
495e2eeea75SSimon J. Gerraty arh.AR_SIZE[sizeof arh.AR_SIZE - 1] = '\0';
496e2eeea75SSimon J. Gerraty size = (size_t)strtol(arh.AR_SIZE, NULL, 10);
4973955d011SMarcel Moolenaar
498e2eeea75SSimon J. Gerraty memcpy(memName, arh.AR_NAME, sizeof arh.AR_NAME);
4992c3632d1SSimon J. Gerraty nameend = memName + AR_MAX_NAME_LEN;
500e2eeea75SSimon J. Gerraty while (nameend > memName && *nameend == ' ')
5012c3632d1SSimon J. Gerraty nameend--;
5022c3632d1SSimon J. Gerraty nameend[1] = '\0';
5033955d011SMarcel Moolenaar
5043955d011SMarcel Moolenaar #ifdef SVR4ARCHIVES
5053955d011SMarcel Moolenaar /*
50606b9b3e0SSimon J. Gerraty * svr4 names are slash-terminated.
50706b9b3e0SSimon J. Gerraty * Also svr4 extended the AR format.
5083955d011SMarcel Moolenaar */
5093955d011SMarcel Moolenaar if (memName[0] == '/') {
51006b9b3e0SSimon J. Gerraty /* svr4 magic mode; handle it */
5113955d011SMarcel Moolenaar switch (ArchSVR4Entry(ar, memName, size, arch)) {
5123955d011SMarcel Moolenaar case -1: /* Invalid data */
513548bfc56SSimon J. Gerraty goto bad_archive;
5143955d011SMarcel Moolenaar case 0: /* List of files entry */
5153955d011SMarcel Moolenaar continue;
5163955d011SMarcel Moolenaar default: /* Got the entry */
5173955d011SMarcel Moolenaar break;
5183955d011SMarcel Moolenaar }
519956e45f6SSimon J. Gerraty } else {
5202c3632d1SSimon J. Gerraty if (nameend[0] == '/')
5212c3632d1SSimon J. Gerraty nameend[0] = '\0';
5223955d011SMarcel Moolenaar }
5233955d011SMarcel Moolenaar #endif
5243955d011SMarcel Moolenaar
5253955d011SMarcel Moolenaar #ifdef AR_EFMT1
5263955d011SMarcel Moolenaar /*
5273955d011SMarcel Moolenaar * BSD 4.4 extended AR format: #1/<namelen>, with name as the
5283955d011SMarcel Moolenaar * first <namelen> bytes of the file
5293955d011SMarcel Moolenaar */
530e2eeea75SSimon J. Gerraty if (strncmp(memName, AR_EFMT1, sizeof AR_EFMT1 - 1) == 0 &&
531e2eeea75SSimon J. Gerraty ch_isdigit(memName[sizeof AR_EFMT1 - 1])) {
5323955d011SMarcel Moolenaar
5334fde40d9SSimon J. Gerraty size_t elen = (size_t)atoi(
5344fde40d9SSimon J. Gerraty memName + sizeof AR_EFMT1 - 1);
5353955d011SMarcel Moolenaar
53612904384SSimon J. Gerraty if (elen > MAXPATHLEN)
537548bfc56SSimon J. Gerraty goto bad_archive;
53812904384SSimon J. Gerraty if (fread(memName, elen, 1, arch) != 1)
539548bfc56SSimon J. Gerraty goto bad_archive;
5403955d011SMarcel Moolenaar memName[elen] = '\0';
54112904384SSimon J. Gerraty if (fseek(arch, -(long)elen, SEEK_CUR) != 0)
542548bfc56SSimon J. Gerraty goto bad_archive;
543e2eeea75SSimon J. Gerraty if (DEBUG(ARCH) || DEBUG(MAKE))
54406b9b3e0SSimon J. Gerraty debug_printf(
54506b9b3e0SSimon J. Gerraty "ArchStatMember: "
54606b9b3e0SSimon J. Gerraty "Extended format entry for %s\n",
547956e45f6SSimon J. Gerraty memName);
5483955d011SMarcel Moolenaar }
5493955d011SMarcel Moolenaar #endif
5503955d011SMarcel Moolenaar
551956e45f6SSimon J. Gerraty {
55206b9b3e0SSimon J. Gerraty struct ar_hdr *cached_hdr = bmake_malloc(
55306b9b3e0SSimon J. Gerraty sizeof *cached_hdr);
554e2eeea75SSimon J. Gerraty memcpy(cached_hdr, &arh, sizeof arh);
555e2eeea75SSimon J. Gerraty HashTable_Set(&ar->members, memName, cached_hdr);
556956e45f6SSimon J. Gerraty }
557e2eeea75SSimon J. Gerraty
558548bfc56SSimon J. Gerraty /* Files are padded with newlines to an even-byte boundary. */
5592c3632d1SSimon J. Gerraty if (fseek(arch, ((long)size + 1) & ~1, SEEK_CUR) != 0)
560548bfc56SSimon J. Gerraty goto bad_archive;
5613955d011SMarcel Moolenaar }
5623955d011SMarcel Moolenaar
5633955d011SMarcel Moolenaar fclose(arch);
5643955d011SMarcel Moolenaar
56506b9b3e0SSimon J. Gerraty Lst_Append(&archives, ar);
5663955d011SMarcel Moolenaar
567956e45f6SSimon J. Gerraty return HashTable_FindValue(&ar->members, member);
5683955d011SMarcel Moolenaar
569548bfc56SSimon J. Gerraty bad_archive:
5703955d011SMarcel Moolenaar fclose(arch);
571956e45f6SSimon J. Gerraty HashTable_Done(&ar->members);
5723955d011SMarcel Moolenaar free(ar->fnametab);
5733955d011SMarcel Moolenaar free(ar);
5743955d011SMarcel Moolenaar return NULL;
5753955d011SMarcel Moolenaar }
5763955d011SMarcel Moolenaar
5773955d011SMarcel Moolenaar #ifdef SVR4ARCHIVES
57806b9b3e0SSimon J. Gerraty /*
5793955d011SMarcel Moolenaar * Parse an SVR4 style entry that begins with a slash.
58006b9b3e0SSimon J. Gerraty * If it is "//", then load the table of filenames.
5813955d011SMarcel Moolenaar * If it is "/<offset>", then try to substitute the long file name
5823955d011SMarcel Moolenaar * from offset of a table previously read.
58306b9b3e0SSimon J. Gerraty * If a table is read, the file pointer is moved to the next archive member.
5843955d011SMarcel Moolenaar *
5853955d011SMarcel Moolenaar * Results:
5863955d011SMarcel Moolenaar * -1: Bad data in archive
5873955d011SMarcel Moolenaar * 0: A table was loaded from the file
5883955d011SMarcel Moolenaar * 1: Name was successfully substituted from table
5893955d011SMarcel Moolenaar * 2: Name was not successfully substituted from table
5903955d011SMarcel Moolenaar */
5913955d011SMarcel Moolenaar static int
ArchSVR4Entry(Arch * ar,char * inout_name,size_t size,FILE * arch)592e2eeea75SSimon J. Gerraty ArchSVR4Entry(Arch *ar, char *inout_name, size_t size, FILE *arch)
5933955d011SMarcel Moolenaar {
5943955d011SMarcel Moolenaar #define ARLONGNAMES1 "//"
5953955d011SMarcel Moolenaar #define ARLONGNAMES2 "/ARFILENAMES"
5963955d011SMarcel Moolenaar size_t entry;
5973955d011SMarcel Moolenaar char *ptr, *eptr;
5983955d011SMarcel Moolenaar
599e2eeea75SSimon J. Gerraty if (strncmp(inout_name, ARLONGNAMES1, sizeof ARLONGNAMES1 - 1) == 0 ||
600e2eeea75SSimon J. Gerraty strncmp(inout_name, ARLONGNAMES2, sizeof ARLONGNAMES2 - 1) == 0) {
6013955d011SMarcel Moolenaar
6023955d011SMarcel Moolenaar if (ar->fnametab != NULL) {
60306b9b3e0SSimon J. Gerraty DEBUG0(ARCH,
60406b9b3e0SSimon J. Gerraty "Attempted to redefine an SVR4 name table\n");
6053955d011SMarcel Moolenaar return -1;
6063955d011SMarcel Moolenaar }
6073955d011SMarcel Moolenaar
6083955d011SMarcel Moolenaar /*
6093955d011SMarcel Moolenaar * This is a table of archive names, so we build one for
6103955d011SMarcel Moolenaar * ourselves
6113955d011SMarcel Moolenaar */
6123955d011SMarcel Moolenaar ar->fnametab = bmake_malloc(size);
6133955d011SMarcel Moolenaar ar->fnamesize = size;
6143955d011SMarcel Moolenaar
6153955d011SMarcel Moolenaar if (fread(ar->fnametab, size, 1, arch) != 1) {
616956e45f6SSimon J. Gerraty DEBUG0(ARCH, "Reading an SVR4 name table failed\n");
6173955d011SMarcel Moolenaar return -1;
6183955d011SMarcel Moolenaar }
6193955d011SMarcel Moolenaar eptr = ar->fnametab + size;
6203955d011SMarcel Moolenaar for (entry = 0, ptr = ar->fnametab; ptr < eptr; ptr++)
621956e45f6SSimon J. Gerraty if (*ptr == '/') {
6223955d011SMarcel Moolenaar entry++;
6233955d011SMarcel Moolenaar *ptr = '\0';
6243955d011SMarcel Moolenaar }
6259f45a3c8SSimon J. Gerraty DEBUG1(ARCH,
6269f45a3c8SSimon J. Gerraty "Found svr4 archive name table with %lu entries\n",
627e48f47ddSSimon J. Gerraty (unsigned long)entry);
6283955d011SMarcel Moolenaar return 0;
6293955d011SMarcel Moolenaar }
6303955d011SMarcel Moolenaar
631e2eeea75SSimon J. Gerraty if (inout_name[1] == ' ' || inout_name[1] == '\0')
6323955d011SMarcel Moolenaar return 2;
6333955d011SMarcel Moolenaar
634e2eeea75SSimon J. Gerraty entry = (size_t)strtol(&inout_name[1], &eptr, 0);
635e2eeea75SSimon J. Gerraty if ((*eptr != ' ' && *eptr != '\0') || eptr == &inout_name[1]) {
636e2eeea75SSimon J. Gerraty DEBUG1(ARCH, "Could not parse SVR4 name %s\n", inout_name);
6373955d011SMarcel Moolenaar return 2;
6383955d011SMarcel Moolenaar }
6393955d011SMarcel Moolenaar if (entry >= ar->fnamesize) {
640956e45f6SSimon J. Gerraty DEBUG2(ARCH, "SVR4 entry offset %s is greater than %lu\n",
641e2eeea75SSimon J. Gerraty inout_name, (unsigned long)ar->fnamesize);
6423955d011SMarcel Moolenaar return 2;
6433955d011SMarcel Moolenaar }
6443955d011SMarcel Moolenaar
645e2eeea75SSimon J. Gerraty DEBUG2(ARCH, "Replaced %s with %s\n", inout_name, &ar->fnametab[entry]);
6463955d011SMarcel Moolenaar
647e2eeea75SSimon J. Gerraty snprintf(inout_name, MAXPATHLEN + 1, "%s", &ar->fnametab[entry]);
6483955d011SMarcel Moolenaar return 1;
6493955d011SMarcel Moolenaar }
6503955d011SMarcel Moolenaar #endif
6513955d011SMarcel Moolenaar
6523955d011SMarcel Moolenaar
653b0c40a00SSimon J. Gerraty static bool
ArchiveMember_HasName(const struct ar_hdr * hdr,const char * name,size_t namelen)654e2eeea75SSimon J. Gerraty ArchiveMember_HasName(const struct ar_hdr *hdr,
655e2eeea75SSimon J. Gerraty const char *name, size_t namelen)
656e2eeea75SSimon J. Gerraty {
657e2eeea75SSimon J. Gerraty const size_t ar_name_len = sizeof hdr->AR_NAME;
658e2eeea75SSimon J. Gerraty const char *ar_name = hdr->AR_NAME;
659e2eeea75SSimon J. Gerraty
660e2eeea75SSimon J. Gerraty if (strncmp(ar_name, name, namelen) != 0)
661b0c40a00SSimon J. Gerraty return false;
662e2eeea75SSimon J. Gerraty
663e2eeea75SSimon J. Gerraty if (namelen >= ar_name_len)
664e2eeea75SSimon J. Gerraty return namelen == ar_name_len;
665e2eeea75SSimon J. Gerraty
666e2eeea75SSimon J. Gerraty /* hdr->AR_NAME is space-padded to the right. */
667e2eeea75SSimon J. Gerraty if (ar_name[namelen] == ' ')
668b0c40a00SSimon J. Gerraty return true;
669e2eeea75SSimon J. Gerraty
6709f45a3c8SSimon J. Gerraty /*
6719f45a3c8SSimon J. Gerraty * In archives created by GNU binutils 2.27, the member names end
6729f45a3c8SSimon J. Gerraty * with a slash.
6739f45a3c8SSimon J. Gerraty */
674548bfc56SSimon J. Gerraty if (ar_name[namelen] == '/' && ar_name[namelen + 1] == ' ')
675b0c40a00SSimon J. Gerraty return true;
676e2eeea75SSimon J. Gerraty
677b0c40a00SSimon J. Gerraty return false;
678e2eeea75SSimon J. Gerraty }
679e2eeea75SSimon J. Gerraty
68006b9b3e0SSimon J. Gerraty /*
681548bfc56SSimon J. Gerraty * Load the header of an archive member. The mode is "r" for read-only
682548bfc56SSimon J. Gerraty * access, "r+" for read-write access.
6833955d011SMarcel Moolenaar *
684548bfc56SSimon J. Gerraty * Upon successful return, the archive file is positioned at the start of the
685548bfc56SSimon J. Gerraty * member's struct ar_hdr. In case of a failure or if the member doesn't
686548bfc56SSimon J. Gerraty * exist, return NULL.
687e2eeea75SSimon J. Gerraty *
688e2eeea75SSimon J. Gerraty * See ArchStatMember for an almost identical copy of this code.
6893955d011SMarcel Moolenaar */
6903955d011SMarcel Moolenaar static FILE *
ArchFindMember(const char * archive,const char * member,struct ar_hdr * out_arh,const char * mode)691548bfc56SSimon J. Gerraty ArchFindMember(const char *archive, const char *member,
692548bfc56SSimon J. Gerraty struct ar_hdr *out_arh, const char *mode)
6933955d011SMarcel Moolenaar {
694548bfc56SSimon J. Gerraty FILE *arch;
6953955d011SMarcel Moolenaar int size; /* Size of archive member */
6963955d011SMarcel Moolenaar char magic[SARMAG];
69706b9b3e0SSimon J. Gerraty size_t len;
6983955d011SMarcel Moolenaar
6993955d011SMarcel Moolenaar arch = fopen(archive, mode);
700956e45f6SSimon J. Gerraty if (arch == NULL)
7013955d011SMarcel Moolenaar return NULL;
7023955d011SMarcel Moolenaar
703e2eeea75SSimon J. Gerraty if (fread(magic, SARMAG, 1, arch) != 1 ||
704e2eeea75SSimon J. Gerraty strncmp(magic, ARMAG, SARMAG) != 0) {
7053955d011SMarcel Moolenaar fclose(arch);
7063955d011SMarcel Moolenaar return NULL;
7073955d011SMarcel Moolenaar }
7083955d011SMarcel Moolenaar
709548bfc56SSimon J. Gerraty /* Files are archived using their basename, not the entire path. */
71006b9b3e0SSimon J. Gerraty member = str_basename(member);
71106b9b3e0SSimon J. Gerraty len = strlen(member);
7123955d011SMarcel Moolenaar
713e2eeea75SSimon J. Gerraty while (fread(out_arh, sizeof *out_arh, 1, arch) == 1) {
714956e45f6SSimon J. Gerraty
71506b9b3e0SSimon J. Gerraty if (strncmp(out_arh->AR_FMAG, ARFMAG,
71606b9b3e0SSimon J. Gerraty sizeof out_arh->AR_FMAG) != 0) {
7173955d011SMarcel Moolenaar fclose(arch);
7183955d011SMarcel Moolenaar return NULL;
719956e45f6SSimon J. Gerraty }
720956e45f6SSimon J. Gerraty
721e2eeea75SSimon J. Gerraty DEBUG5(ARCH, "Reading archive %s member %.*s mtime %.*s\n",
722e2eeea75SSimon J. Gerraty archive,
723e2eeea75SSimon J. Gerraty (int)sizeof out_arh->AR_NAME, out_arh->AR_NAME,
724e2eeea75SSimon J. Gerraty (int)sizeof out_arh->ar_date, out_arh->ar_date);
725956e45f6SSimon J. Gerraty
726e2eeea75SSimon J. Gerraty if (ArchiveMember_HasName(out_arh, member, len)) {
72706b9b3e0SSimon J. Gerraty if (fseek(arch, -(long)sizeof *out_arh, SEEK_CUR) !=
72806b9b3e0SSimon J. Gerraty 0) {
729e1cee40dSSimon J. Gerraty fclose(arch);
730e1cee40dSSimon J. Gerraty return NULL;
731e1cee40dSSimon J. Gerraty }
7323841c287SSimon J. Gerraty return arch;
7333955d011SMarcel Moolenaar }
734956e45f6SSimon J. Gerraty
7353955d011SMarcel Moolenaar #ifdef AR_EFMT1
7363955d011SMarcel Moolenaar /*
7373955d011SMarcel Moolenaar * BSD 4.4 extended AR format: #1/<namelen>, with name as the
7383955d011SMarcel Moolenaar * first <namelen> bytes of the file
7393955d011SMarcel Moolenaar */
74006b9b3e0SSimon J. Gerraty if (strncmp(out_arh->AR_NAME, AR_EFMT1, sizeof AR_EFMT1 - 1) ==
74106b9b3e0SSimon J. Gerraty 0 &&
74206b9b3e0SSimon J. Gerraty (ch_isdigit(out_arh->AR_NAME[sizeof AR_EFMT1 - 1]))) {
7434fde40d9SSimon J. Gerraty size_t elen = (size_t)atoi(
74412904384SSimon J. Gerraty &out_arh->AR_NAME[sizeof AR_EFMT1 - 1]);
7453955d011SMarcel Moolenaar char ename[MAXPATHLEN + 1];
7463955d011SMarcel Moolenaar
74712904384SSimon J. Gerraty if (elen > MAXPATHLEN) {
7483955d011SMarcel Moolenaar fclose(arch);
7493955d011SMarcel Moolenaar return NULL;
7503955d011SMarcel Moolenaar }
75112904384SSimon J. Gerraty if (fread(ename, elen, 1, arch) != 1) {
7523955d011SMarcel Moolenaar fclose(arch);
7533955d011SMarcel Moolenaar return NULL;
7543955d011SMarcel Moolenaar }
7553955d011SMarcel Moolenaar ename[elen] = '\0';
756e2eeea75SSimon J. Gerraty if (DEBUG(ARCH) || DEBUG(MAKE))
75706b9b3e0SSimon J. Gerraty debug_printf(
75806b9b3e0SSimon J. Gerraty "ArchFindMember: "
75906b9b3e0SSimon J. Gerraty "Extended format entry for %s\n",
760e2eeea75SSimon J. Gerraty ename);
7613955d011SMarcel Moolenaar if (strncmp(ename, member, len) == 0) {
7623955d011SMarcel Moolenaar /* Found as extended name */
76306b9b3e0SSimon J. Gerraty if (fseek(arch,
76412904384SSimon J. Gerraty -(long)(sizeof(struct ar_hdr) - elen),
765e1cee40dSSimon J. Gerraty SEEK_CUR) != 0) {
766e1cee40dSSimon J. Gerraty fclose(arch);
767e1cee40dSSimon J. Gerraty return NULL;
768e1cee40dSSimon J. Gerraty }
7693841c287SSimon J. Gerraty return arch;
7703955d011SMarcel Moolenaar }
77112904384SSimon J. Gerraty if (fseek(arch, -(long)elen, SEEK_CUR) != 0) {
772e1cee40dSSimon J. Gerraty fclose(arch);
773e1cee40dSSimon J. Gerraty return NULL;
774e1cee40dSSimon J. Gerraty }
775956e45f6SSimon J. Gerraty }
7763955d011SMarcel Moolenaar #endif
777956e45f6SSimon J. Gerraty
778548bfc56SSimon J. Gerraty /* Advance to the next member. */
779e2eeea75SSimon J. Gerraty out_arh->AR_SIZE[sizeof out_arh->AR_SIZE - 1] = '\0';
780e2eeea75SSimon J. Gerraty size = (int)strtol(out_arh->AR_SIZE, NULL, 10);
781548bfc56SSimon J. Gerraty /* Files are padded with newlines to an even-byte boundary. */
78212904384SSimon J. Gerraty if (fseek(arch, (size + 1) & ~1L, SEEK_CUR) != 0) {
783e1cee40dSSimon J. Gerraty fclose(arch);
784e1cee40dSSimon J. Gerraty return NULL;
785e1cee40dSSimon J. Gerraty }
7863955d011SMarcel Moolenaar }
7873955d011SMarcel Moolenaar
7883955d011SMarcel Moolenaar fclose(arch);
7893955d011SMarcel Moolenaar return NULL;
7903955d011SMarcel Moolenaar }
7913955d011SMarcel Moolenaar
79206b9b3e0SSimon J. Gerraty /*
793548bfc56SSimon J. Gerraty * Update the ar_date of the member of an archive, on disk but not in the
794548bfc56SSimon J. Gerraty * GNode. Update the st_mtime of the entire archive as well. For a library,
795548bfc56SSimon J. Gerraty * it may be required to run ranlib after this.
7963955d011SMarcel Moolenaar */
7973955d011SMarcel Moolenaar void
Arch_Touch(GNode * gn)7983955d011SMarcel Moolenaar Arch_Touch(GNode *gn)
7993955d011SMarcel Moolenaar {
800e2eeea75SSimon J. Gerraty FILE *f;
801e2eeea75SSimon J. Gerraty struct ar_hdr arh;
8023955d011SMarcel Moolenaar
80306b9b3e0SSimon J. Gerraty f = ArchFindMember(GNode_VarArchive(gn), GNode_VarMember(gn), &arh,
80406b9b3e0SSimon J. Gerraty "r+");
805e2eeea75SSimon J. Gerraty if (f == NULL)
806e2eeea75SSimon J. Gerraty return;
807be19d90bSSimon J. Gerraty
808e2eeea75SSimon J. Gerraty snprintf(arh.ar_date, sizeof arh.ar_date, "%-ld", (unsigned long)now);
809e2eeea75SSimon J. Gerraty (void)fwrite(&arh, sizeof arh, 1, f);
810e2eeea75SSimon J. Gerraty fclose(f); /* TODO: handle errors */
8113955d011SMarcel Moolenaar }
8123955d011SMarcel Moolenaar
81306b9b3e0SSimon J. Gerraty /*
81406b9b3e0SSimon J. Gerraty * Given a node which represents a library, touch the thing, making sure that
815e2eeea75SSimon J. Gerraty * the table of contents is also touched.
8162c3632d1SSimon J. Gerraty *
8172c3632d1SSimon J. Gerraty * Both the modification time of the library and of the RANLIBMAG member are
81806b9b3e0SSimon J. Gerraty * set to 'now'.
81906b9b3e0SSimon J. Gerraty */
8203955d011SMarcel Moolenaar void
Arch_TouchLib(GNode * gn MAKE_ATTR_UNUSED)821e2eeea75SSimon J. Gerraty Arch_TouchLib(GNode *gn MAKE_ATTR_UNUSED)
8223955d011SMarcel Moolenaar {
8233955d011SMarcel Moolenaar #ifdef RANLIBMAG
824e2eeea75SSimon J. Gerraty FILE *f;
8253955d011SMarcel Moolenaar struct ar_hdr arh; /* Header describing table of contents */
826e2eeea75SSimon J. Gerraty struct utimbuf times;
8273955d011SMarcel Moolenaar
828e2eeea75SSimon J. Gerraty f = ArchFindMember(gn->path, RANLIBMAG, &arh, "r+");
829e2eeea75SSimon J. Gerraty if (f == NULL)
830e2eeea75SSimon J. Gerraty return;
8313955d011SMarcel Moolenaar
832e2eeea75SSimon J. Gerraty snprintf(arh.ar_date, sizeof arh.ar_date, "%-ld", (unsigned long)now);
833e2eeea75SSimon J. Gerraty (void)fwrite(&arh, sizeof arh, 1, f);
834e2eeea75SSimon J. Gerraty fclose(f); /* TODO: handle errors */
8353955d011SMarcel Moolenaar
8363955d011SMarcel Moolenaar times.actime = times.modtime = now;
837e2eeea75SSimon J. Gerraty utime(gn->path, ×); /* TODO: handle errors */
8383955d011SMarcel Moolenaar #endif
8393955d011SMarcel Moolenaar }
8403955d011SMarcel Moolenaar
84106b9b3e0SSimon J. Gerraty /*
84206b9b3e0SSimon J. Gerraty * Update the mtime of the GNode with the mtime from the archive member on
84306b9b3e0SSimon J. Gerraty * disk (or in the cache).
84406b9b3e0SSimon J. Gerraty */
845e2eeea75SSimon J. Gerraty void
Arch_UpdateMTime(GNode * gn)846e2eeea75SSimon J. Gerraty Arch_UpdateMTime(GNode *gn)
8473955d011SMarcel Moolenaar {
848e2eeea75SSimon J. Gerraty struct ar_hdr *arh;
8493955d011SMarcel Moolenaar
850b0c40a00SSimon J. Gerraty arh = ArchStatMember(GNode_VarArchive(gn), GNode_VarMember(gn), true);
851e2eeea75SSimon J. Gerraty if (arh != NULL)
852e2eeea75SSimon J. Gerraty gn->mtime = (time_t)strtol(arh->ar_date, NULL, 10);
853e2eeea75SSimon J. Gerraty else
854e2eeea75SSimon J. Gerraty gn->mtime = 0;
8553955d011SMarcel Moolenaar }
8563955d011SMarcel Moolenaar
85706b9b3e0SSimon J. Gerraty /*
85806b9b3e0SSimon J. Gerraty * Given a nonexistent archive member's node, update gn->mtime from its
85906b9b3e0SSimon J. Gerraty * archived form, if it exists.
86006b9b3e0SSimon J. Gerraty */
861e2eeea75SSimon J. Gerraty void
Arch_UpdateMemberMTime(GNode * gn)862e2eeea75SSimon J. Gerraty Arch_UpdateMemberMTime(GNode *gn)
8633955d011SMarcel Moolenaar {
864956e45f6SSimon J. Gerraty GNodeListNode *ln;
8653955d011SMarcel Moolenaar
86606b9b3e0SSimon J. Gerraty for (ln = gn->parents.first; ln != NULL; ln = ln->next) {
867956e45f6SSimon J. Gerraty GNode *pgn = ln->datum;
8683955d011SMarcel Moolenaar
8693955d011SMarcel Moolenaar if (pgn->type & OP_ARCHV) {
8703955d011SMarcel Moolenaar /*
87106b9b3e0SSimon J. Gerraty * If the parent is an archive specification and is
87206b9b3e0SSimon J. Gerraty * being made and its member's name matches the name
87306b9b3e0SSimon J. Gerraty * of the node we were given, record the modification
87406b9b3e0SSimon J. Gerraty * time of the parent in the child. We keep searching
87506b9b3e0SSimon J. Gerraty * its parents in case some other parent requires this
87606b9b3e0SSimon J. Gerraty * child to exist.
8773955d011SMarcel Moolenaar */
8782c3632d1SSimon J. Gerraty const char *nameStart = strchr(pgn->name, '(') + 1;
8792c3632d1SSimon J. Gerraty const char *nameEnd = strchr(nameStart, ')');
8802c3632d1SSimon J. Gerraty size_t nameLen = (size_t)(nameEnd - nameStart);
8813955d011SMarcel Moolenaar
88212904384SSimon J. Gerraty if (pgn->flags.remake &&
8832c3632d1SSimon J. Gerraty strncmp(nameStart, gn->name, nameLen) == 0) {
884e2eeea75SSimon J. Gerraty Arch_UpdateMTime(pgn);
885e2eeea75SSimon J. Gerraty gn->mtime = pgn->mtime;
8863955d011SMarcel Moolenaar }
88712904384SSimon J. Gerraty } else if (pgn->flags.remake) {
8883955d011SMarcel Moolenaar /*
88906b9b3e0SSimon J. Gerraty * Something which isn't a library depends on the
89006b9b3e0SSimon J. Gerraty * existence of this target, so it needs to exist.
8913955d011SMarcel Moolenaar */
8923955d011SMarcel Moolenaar gn->mtime = 0;
8933955d011SMarcel Moolenaar break;
8943955d011SMarcel Moolenaar }
8953955d011SMarcel Moolenaar }
8963955d011SMarcel Moolenaar }
8973955d011SMarcel Moolenaar
89806b9b3e0SSimon J. Gerraty /*
89906b9b3e0SSimon J. Gerraty * Search for a library along the given search path.
9003955d011SMarcel Moolenaar *
9013955d011SMarcel Moolenaar * The node's 'path' field is set to the found path (including the
9023955d011SMarcel Moolenaar * actual file name, not -l...). If the system can handle the -L
9033955d011SMarcel Moolenaar * flag when linking (or we cannot find the library), we assume that
9042c3632d1SSimon J. Gerraty * the user has placed the .LIBS variable in the final linking
9053955d011SMarcel Moolenaar * command (or the linker will know where to find it) and set the
9063955d011SMarcel Moolenaar * TARGET variable for this node to be the node's name. Otherwise,
9073955d011SMarcel Moolenaar * we set the TARGET variable to be the full path of the library,
9083955d011SMarcel Moolenaar * as returned by Dir_FindFile.
9093955d011SMarcel Moolenaar */
9103955d011SMarcel Moolenaar void
Arch_FindLib(GNode * gn,SearchPath * path)911956e45f6SSimon J. Gerraty Arch_FindLib(GNode *gn, SearchPath *path)
9123955d011SMarcel Moolenaar {
913956e45f6SSimon J. Gerraty char *libName = str_concat3("lib", gn->name + 2, ".a");
9143955d011SMarcel Moolenaar gn->path = Dir_FindFile(libName, path);
9153955d011SMarcel Moolenaar free(libName);
9163955d011SMarcel Moolenaar
917dba7b0efSSimon J. Gerraty Var_Set(gn, TARGET, gn->name);
9183955d011SMarcel Moolenaar }
9193955d011SMarcel Moolenaar
92012904384SSimon J. Gerraty static bool
RanlibOODate(const GNode * gn MAKE_ATTR_UNUSED)92112904384SSimon J. Gerraty RanlibOODate(const GNode *gn MAKE_ATTR_UNUSED)
92212904384SSimon J. Gerraty {
92312904384SSimon J. Gerraty #ifdef RANLIBMAG
92412904384SSimon J. Gerraty struct ar_hdr *arh; /* Header for __.SYMDEF */
92512904384SSimon J. Gerraty int tocModTime; /* The table-of-contents' mod time */
92612904384SSimon J. Gerraty
92712904384SSimon J. Gerraty arh = ArchStatMember(gn->path, RANLIBMAG, false);
92812904384SSimon J. Gerraty
92912904384SSimon J. Gerraty if (arh == NULL) {
93012904384SSimon J. Gerraty /* A library without a table of contents is out-of-date. */
93112904384SSimon J. Gerraty if (DEBUG(ARCH) || DEBUG(MAKE))
93212904384SSimon J. Gerraty debug_printf("no toc...");
93312904384SSimon J. Gerraty return true;
93412904384SSimon J. Gerraty }
93512904384SSimon J. Gerraty
93612904384SSimon J. Gerraty tocModTime = (int)strtol(arh->ar_date, NULL, 10);
93712904384SSimon J. Gerraty
93812904384SSimon J. Gerraty if (DEBUG(ARCH) || DEBUG(MAKE))
93912904384SSimon J. Gerraty debug_printf("%s modified %s...",
94012904384SSimon J. Gerraty RANLIBMAG, Targ_FmtTime(tocModTime));
94112904384SSimon J. Gerraty return gn->youngestChild == NULL ||
94212904384SSimon J. Gerraty gn->youngestChild->mtime > tocModTime;
94312904384SSimon J. Gerraty #else
94412904384SSimon J. Gerraty return false;
94512904384SSimon J. Gerraty #endif
94612904384SSimon J. Gerraty }
94712904384SSimon J. Gerraty
94806b9b3e0SSimon J. Gerraty /*
949548bfc56SSimon J. Gerraty * Decide if a node with the OP_LIB attribute is out-of-date.
950e2eeea75SSimon J. Gerraty * The library is cached if it hasn't been already.
9513955d011SMarcel Moolenaar *
952548bfc56SSimon J. Gerraty * There are several ways for a library to be out-of-date that are not
953548bfc56SSimon J. Gerraty * available to ordinary files. In addition, there are ways that are open to
954548bfc56SSimon J. Gerraty * regular files that are not available to libraries.
955e2eeea75SSimon J. Gerraty *
956548bfc56SSimon J. Gerraty * A library that is only used as a source is never considered out-of-date by
957548bfc56SSimon J. Gerraty * itself. This does not preclude the library's modification time from making
958548bfc56SSimon J. Gerraty * its parent be out-of-date. A library will be considered out-of-date for
959548bfc56SSimon J. Gerraty * any of these reasons, given that it is a target on a dependency line
960548bfc56SSimon J. Gerraty * somewhere:
9612c3632d1SSimon J. Gerraty *
9622c3632d1SSimon J. Gerraty * Its modification time is less than that of one of its sources
963956e45f6SSimon J. Gerraty * (gn->mtime < gn->youngestChild->mtime).
9642c3632d1SSimon J. Gerraty *
9652c3632d1SSimon J. Gerraty * Its modification time is greater than the time at which the make
9662c3632d1SSimon J. Gerraty * began (i.e. it's been modified in the course of the make, probably
9672c3632d1SSimon J. Gerraty * by archiving).
9682c3632d1SSimon J. Gerraty *
9692c3632d1SSimon J. Gerraty * The modification time of one of its sources is greater than the one
9702c3632d1SSimon J. Gerraty * of its RANLIBMAG member (i.e. its table of contents is out-of-date).
971e2eeea75SSimon J. Gerraty * We don't compare the archive time vs. TOC time because they can be
9722c3632d1SSimon J. Gerraty * too close. In my opinion we should not bother with the TOC at all
9732c3632d1SSimon J. Gerraty * since this is used by 'ar' rules that affect the data contents of the
9742c3632d1SSimon J. Gerraty * archive, not by ranlib rules, which affect the TOC.
9753955d011SMarcel Moolenaar */
976b0c40a00SSimon J. Gerraty bool
Arch_LibOODate(GNode * gn)9773955d011SMarcel Moolenaar Arch_LibOODate(GNode *gn)
9783955d011SMarcel Moolenaar {
9793955d011SMarcel Moolenaar
980548bfc56SSimon J. Gerraty if (gn->type & OP_PHONY)
98112904384SSimon J. Gerraty return true;
982548bfc56SSimon J. Gerraty if (!GNode_IsTarget(gn) && Lst_IsEmpty(&gn->children))
98312904384SSimon J. Gerraty return false;
984548bfc56SSimon J. Gerraty if ((!Lst_IsEmpty(&gn->children) && gn->youngestChild == NULL) ||
9853955d011SMarcel Moolenaar (gn->mtime > now) ||
986956e45f6SSimon J. Gerraty (gn->youngestChild != NULL &&
987548bfc56SSimon J. Gerraty gn->mtime < gn->youngestChild->mtime))
98812904384SSimon J. Gerraty return true;
98912904384SSimon J. Gerraty return RanlibOODate(gn);
9903955d011SMarcel Moolenaar }
9913955d011SMarcel Moolenaar
992956e45f6SSimon J. Gerraty /* Initialize the archives module. */
9933955d011SMarcel Moolenaar void
Arch_Init(void)9943955d011SMarcel Moolenaar Arch_Init(void)
9953955d011SMarcel Moolenaar {
99606b9b3e0SSimon J. Gerraty Lst_Init(&archives);
9973955d011SMarcel Moolenaar }
9983955d011SMarcel Moolenaar
99922619282SSimon J. Gerraty #ifdef CLEANUP
1000956e45f6SSimon J. Gerraty /* Clean up the archives module. */
10013955d011SMarcel Moolenaar void
Arch_End(void)10023955d011SMarcel Moolenaar Arch_End(void)
10033955d011SMarcel Moolenaar {
1004548bfc56SSimon J. Gerraty ArchListNode *ln;
1005548bfc56SSimon J. Gerraty
1006548bfc56SSimon J. Gerraty for (ln = archives.first; ln != NULL; ln = ln->next)
1007548bfc56SSimon J. Gerraty ArchFree(ln->datum);
1008548bfc56SSimon J. Gerraty Lst_Done(&archives);
10093955d011SMarcel Moolenaar }
101022619282SSimon J. Gerraty #endif
10113955d011SMarcel Moolenaar
1012b0c40a00SSimon J. Gerraty bool
Arch_IsLib(GNode * gn)10133955d011SMarcel Moolenaar Arch_IsLib(GNode *gn)
10143955d011SMarcel Moolenaar {
1015548bfc56SSimon J. Gerraty char buf[8];
10163955d011SMarcel Moolenaar int fd;
1017548bfc56SSimon J. Gerraty bool isLib;
10183955d011SMarcel Moolenaar
10193955d011SMarcel Moolenaar if ((fd = open(gn->path, O_RDONLY)) == -1)
1020b0c40a00SSimon J. Gerraty return false;
1021548bfc56SSimon J. Gerraty isLib = read(fd, buf, sizeof buf) == sizeof buf
1022548bfc56SSimon J. Gerraty && memcmp(buf, "!<arch>\n", sizeof buf) == 0;
10233955d011SMarcel Moolenaar (void)close(fd);
1024548bfc56SSimon J. Gerraty return isLib;
10253955d011SMarcel Moolenaar }
1026