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