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