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