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