ar.c (26212B)
1 #include <kit/archive.h> 2 #include <kit/core.h> 3 #include <kit/object.h> 4 5 #include "driver.h" 6 7 /* `kit ar` — POSIX ar archive front-end. 8 * 9 * Supported operations (mutually exclusive): 10 * kit ar r archive.a in.o... replace listed members in 11 * place; preserve unlisted; 12 * append new ones; warn when 13 * creating the archive. 14 * kit ar c archive.a in.o... overwrite the archive with 15 * exactly the listed members. 16 * kit ar {rc|cr} archive.a in.o... `r` semantics, but quiet on 17 * archive creation. 18 * kit ar t archive.a list member names 19 * kit ar x archive.a [members...] extract members to cwd 20 * kit ar p archive.a [members...] print members to stdout 21 * 22 * Modifiers (combine with above): 23 * s emit a System V `/` symbol-index member at the head of the 24 * archive. Only valid combined with r/c (i.e. rs, cs, rcs). 25 * Each member is opened with kit_obj_open and its globally- 26 * defined symbols (KIT_SB_GLOBAL with section != NONE) are 27 * indexed; non-object members contribute no symbols. 28 * u update only if member is newer. Accepted as a compatibility 29 * modifier and currently treated as a no-op. 30 * v verbose. With t, list each member as "<size>\t<name>" instead 31 * of name-only. With x, print "x - <name>" per extracted member. 32 * With p, prepend a "<archive>(<name>):\n" header to every member 33 * (not just when there are 0 or >=2 filters). With r/c, print 34 * "a - <name>" for newly added members and "r - <name>" for 35 * replacements (r mode only). 36 * 37 * Reproducibility: SOURCE_DATE_EPOCH (when set to a positive integer) is 38 * written to ar_date for every member on write. Long member names are 39 * routed through a `//` extended-name table. 40 * 41 * Not yet implemented: d (delete), q (quick append), and standalone `s` 42 * without r/c. Encountering any of those yields a usage error with exit 43 * code 2. */ 44 45 #define AR_TOOL "ar" 46 47 static void ar_usage(void) { 48 driver_errf(AR_TOOL, "%.*s", 49 KIT_SLICE_ARG(KIT_SLICE_LIT( 50 "usage: kit ar {rc|r|c|t|x|p}[s] archive.a [members...]\n" 51 " kit ar --help for full operation reference"))); 52 } 53 54 void driver_help_ar(void) { 55 driver_printf( 56 "%.*s", 57 KIT_SLICE_ARG(KIT_SLICE_LIT( 58 "kit ar — POSIX `ar` archive front-end\n" 59 "\n" 60 "USAGE\n" 61 " kit ar MODE archive.a [members...]\n" 62 "\n" 63 "MODE is a string of one or more letters; exactly one operation " 64 "must\n" 65 "be selected and any number of modifiers may follow. The archive\n" 66 "path and (optional) member list follow the mode. Both `rcs` and\n" 67 "the conventional `-rcs` spelling are accepted.\n" 68 "\n" 69 "OPERATIONS (mutually exclusive)\n" 70 " r Replace listed members in place; preserve unlisted; " 71 "append\n" 72 " new ones. Warns when the archive must be created.\n" 73 " c Create / overwrite the archive with exactly the listed\n" 74 " members. Suppresses the create warning.\n" 75 " rc, cr `r` semantics with the create warning suppressed.\n" 76 " t List member names.\n" 77 " x Extract members to the current working directory. With " 78 "no\n" 79 " member list, extract everything.\n" 80 " p Print members to stdout. With no list, print all " 81 "members;\n" 82 " with two or more members each is preceded by a header.\n" 83 "\n" 84 "MODIFIERS\n" 85 " s Emit a System-V `/` symbol-index member at the head of\n" 86 " the archive. Valid only combined with r and/or c (rs, " 87 "cs,\n" 88 " rcs, crs). Globally-defined symbols (KIT_SB_GLOBAL " 89 "with\n" 90 " a defined section) of each object member are indexed; " 91 "non-\n" 92 " object members contribute no symbols.\n" 93 " u Update-if-newer compatibility modifier; accepted as a\n" 94 " no-op.\n" 95 " v Verbose. With t list <size>\\t<name>; with x/r/c print " 96 "one\n" 97 " line per affected member (\"x - name\", \"a - name\", or\n" 98 " \"r - name\" for replacements); with p force the " 99 "per-member\n" 100 " \"archive(name):\" header even on a single match.\n" 101 "\n" 102 "REPRODUCIBILITY\n" 103 " When SOURCE_DATE_EPOCH is set to a positive integer, that value " 104 "is\n" 105 " written to ar_date for every member on write. Long member names\n" 106 " are routed through a `//` extended-name table.\n" 107 "\n" 108 "NOT YET IMPLEMENTED\n" 109 " d (delete), q (quick append), and standalone `s` without r/c.\n" 110 " Encountering any of those yields a usage error with exit code 2.\n" 111 " Refresh an existing archive index with `kit ranlib ARCHIVE.a`.\n" 112 "\n" 113 "INPUT / OUTPUT\n" 114 " Archive and member names are file operands; archives are not read\n" 115 " from stdin. Operations t/x read the archive, x writes into the\n" 116 " current directory, and p writes selected member bytes to stdout.\n" 117 " Use -- between MODE and a leading-dash archive path.\n" 118 "\n" 119 "EXAMPLES\n" 120 " kit ar rcs libfoo.a a.o b.o c.o\n" 121 " kit ranlib libfoo.a\n" 122 " kit ar t libfoo.a\n" 123 " kit ar x libfoo.a a.o\n" 124 " kit ar p libfoo.a a.o > a.o.copy\n" 125 "\n" 126 "GETTING HELP\n" 127 " -h, --help Show this help and exit\n" 128 "\n" 129 "EXIT CODES\n" 130 " 0 success 1 archive I/O error 2 bad " 131 "usage\n"))); 132 } 133 134 /* Return 1 iff `name` matches any of the names in argv[start..argc) — or if 135 * there are no filters, in which case every member matches. */ 136 static int ar_name_selected(KitSlice name, int argc, char** argv, int start) { 137 int i; 138 if (start >= argc) return 1; 139 for (i = start; i < argc; ++i) { 140 const char* base = driver_basename(argv[i]); 141 if (kit_slice_eq_cstr(name, base)) return 1; 142 } 143 return 0; 144 } 145 146 static void ar_set_input_member(KitArInput* out, const char* name, 147 const KitFileData* fd) { 148 out->name.s = name; 149 out->name.len = driver_strlen(name); 150 out->bytes.data = fd->data; 151 out->bytes.len = fd->size; 152 } 153 154 typedef struct ArNameSlot { 155 const char* name; 156 size_t len; 157 uint32_t head_plus_one; 158 uint32_t tail_plus_one; 159 uint32_t hash; 160 } ArNameSlot; 161 162 typedef struct ArNameIndex { 163 DriverEnv* env; 164 ArNameSlot* slots; 165 uint32_t* next_plus_one; 166 size_t cap; 167 size_t next_cap; 168 } ArNameIndex; 169 170 static void ar_name_index_fini(ArNameIndex* idx); 171 172 static uint32_t ar_name_hash(const char* s, size_t len) { 173 uint32_t h = 2166136261u; 174 size_t i; 175 for (i = 0; i < len; ++i) { 176 h ^= (uint8_t)s[i]; 177 h *= 16777619u; 178 } 179 return h ? h : 1u; 180 } 181 182 static int ar_name_eq(const ArNameSlot* slot, const char* name, size_t len, 183 uint32_t hash) { 184 size_t i; 185 if (!slot->name || slot->hash != hash || slot->len != len) return 0; 186 for (i = 0; i < len; ++i) 187 if (slot->name[i] != name[i]) return 0; 188 return 1; 189 } 190 191 static int ar_name_index_init(ArNameIndex* idx, DriverEnv* env, 192 size_t max_entries) { 193 size_t cap = 16; 194 idx->env = env; 195 idx->slots = NULL; 196 idx->next_plus_one = NULL; 197 idx->cap = 0; 198 idx->next_cap = 0; 199 if (max_entries == 0) return 1; 200 while (cap < max_entries * 2u) cap *= 2u; 201 idx->slots = (ArNameSlot*)driver_alloc_zeroed(env, cap * sizeof(*idx->slots)); 202 idx->next_plus_one = 203 (uint32_t*)driver_alloc_zeroed(env, max_entries * sizeof(*idx->next_plus_one)); 204 if (!idx->slots || !idx->next_plus_one) { 205 ar_name_index_fini(idx); 206 return 0; 207 } 208 idx->cap = cap; 209 idx->next_cap = max_entries; 210 return 1; 211 } 212 213 static void ar_name_index_fini(ArNameIndex* idx) { 214 if (idx->slots) 215 driver_free(idx->env, idx->slots, idx->cap * sizeof(*idx->slots)); 216 if (idx->next_plus_one) 217 driver_free(idx->env, idx->next_plus_one, 218 idx->next_cap * sizeof(*idx->next_plus_one)); 219 idx->env = NULL; 220 idx->slots = NULL; 221 idx->next_plus_one = NULL; 222 idx->cap = 0; 223 idx->next_cap = 0; 224 } 225 226 static int ar_name_index_take(ArNameIndex* idx, const char* name, size_t len, 227 uint32_t* out_index) { 228 uint32_t hash; 229 size_t mask; 230 size_t pos; 231 if (!idx->slots) return 0; 232 hash = ar_name_hash(name, len); 233 mask = idx->cap - 1u; 234 pos = hash & mask; 235 for (;;) { 236 const ArNameSlot* slot = &idx->slots[pos]; 237 if (!slot->name) return 0; 238 if (ar_name_eq(slot, name, len, hash)) { 239 ArNameSlot* mut = &idx->slots[pos]; 240 uint32_t index; 241 if (!mut->head_plus_one) return 0; 242 index = mut->head_plus_one - 1u; 243 mut->head_plus_one = idx->next_plus_one[index]; 244 if (!mut->head_plus_one) mut->tail_plus_one = 0; 245 *out_index = index; 246 return 1; 247 } 248 pos = (pos + 1u) & mask; 249 } 250 } 251 252 static void ar_name_index_insert_old(ArNameIndex* idx, const char* name, 253 size_t len, uint32_t member_index) { 254 uint32_t hash = ar_name_hash(name, len); 255 size_t mask = idx->cap - 1u; 256 size_t pos = hash & mask; 257 if (member_index >= idx->next_cap) return; 258 for (;;) { 259 ArNameSlot* slot = &idx->slots[pos]; 260 if (!slot->name) { 261 slot->name = name; 262 slot->len = len; 263 slot->hash = hash; 264 slot->head_plus_one = member_index + 1u; 265 slot->tail_plus_one = member_index + 1u; 266 return; 267 } 268 if (ar_name_eq(slot, name, len, hash)) { 269 if (slot->tail_plus_one) { 270 idx->next_plus_one[slot->tail_plus_one - 1u] = member_index + 1u; 271 slot->tail_plus_one = member_index + 1u; 272 } else { 273 slot->head_plus_one = member_index + 1u; 274 slot->tail_plus_one = member_index + 1u; 275 } 276 return; 277 } 278 pos = (pos + 1u) & mask; 279 } 280 } 281 282 /* Open the archive bytes via file_io. Caller releases fd via ctx. */ 283 static int ar_open_for_read(DriverEnv* env, const char* path, 284 KitContext* ctx_out, KitFileData* fd_out, 285 KitSlice* input_out) { 286 *ctx_out = driver_env_to_context(env); 287 if (ctx_out->file_io->read_all(ctx_out->file_io->user, path, fd_out) != 288 KIT_OK) { 289 driver_errf(AR_TOOL, "failed to read: %.*s", 290 KIT_SLICE_ARG(kit_slice_cstr(path))); 291 return 0; 292 } 293 (void)path; 294 input_out->data = fd_out->data; 295 input_out->len = fd_out->size; 296 return 1; 297 } 298 299 static int ar_do_list(DriverEnv* env, const char* archive_path, int verbose) { 300 KitContext ctx; 301 KitFileData fd = {0}; 302 KitSlice input; 303 KitWriter* out; 304 int rc; 305 306 if (!ar_open_for_read(env, archive_path, &ctx, &fd, &input)) return 1; 307 308 out = driver_stdout_writer(env); 309 if (!out) { 310 driver_errf(AR_TOOL, "out of memory"); 311 ctx.file_io->release(ctx.file_io->user, &fd); 312 return 1; 313 } 314 315 if (verbose) { 316 KitArIter* it = NULL; 317 KitArMember m; 318 if (kit_ar_iter_new(&ctx, &input, &it) != KIT_OK) { 319 driver_errf(AR_TOOL, "not an archive: %.*s", 320 KIT_SLICE_ARG(kit_slice_cstr(archive_path))); 321 kit_writer_close(out); 322 ctx.file_io->release(ctx.file_io->user, &fd); 323 return 1; 324 } 325 for (;;) { 326 KitIterResult r = kit_ar_iter_next(it, &m); 327 if (r != KIT_ITER_ITEM) break; 328 driver_printf("%zu\t%.*s\n", m.size, KIT_SLICE_ARG(m.name)); 329 } 330 kit_ar_iter_free(it); 331 rc = kit_writer_status(out) == KIT_OK ? 0 : 1; 332 } else { 333 rc = kit_ar_list(&input, out) == KIT_OK ? 0 : 1; 334 if (rc) 335 driver_errf(AR_TOOL, "failed to read archive: %.*s", 336 KIT_SLICE_ARG(kit_slice_cstr(archive_path))); 337 } 338 kit_writer_close(out); 339 ctx.file_io->release(ctx.file_io->user, &fd); 340 return rc; 341 } 342 343 static int ar_do_extract(DriverEnv* env, const char* archive_path, int argc, 344 char** argv, int start, int verbose) { 345 KitContext ctx; 346 KitFileData fd = {0}; 347 KitSlice input; 348 KitArIter* it = NULL; 349 KitArMember m; 350 int rc = 0; 351 352 if (!ar_open_for_read(env, archive_path, &ctx, &fd, &input)) return 1; 353 if (kit_ar_iter_new(&ctx, &input, &it) != KIT_OK) { 354 driver_errf(AR_TOOL, "not an archive: %.*s", 355 KIT_SLICE_ARG(kit_slice_cstr(archive_path))); 356 ctx.file_io->release(ctx.file_io->user, &fd); 357 return 1; 358 } 359 360 for (;;) { 361 KitWriter* out; 362 KitIterResult r = kit_ar_iter_next(it, &m); 363 if (r != KIT_ITER_ITEM) break; 364 if (!ar_name_selected(m.name, argc, argv, start)) continue; 365 366 if (ctx.file_io->open_writer(ctx.file_io->user, m.name.s, &out) != KIT_OK) { 367 driver_errf(AR_TOOL, "failed to open: %.*s", KIT_SLICE_ARG(m.name)); 368 rc = 1; 369 continue; 370 } 371 if (m.size) kit_writer_write(out, m.data, m.size); 372 if (kit_writer_status(out) != KIT_OK) { 373 driver_errf(AR_TOOL, "write failed: %.*s", KIT_SLICE_ARG(m.name)); 374 rc = 1; 375 } 376 kit_writer_close(out); 377 if (verbose) driver_printf("x - %.*s\n", KIT_SLICE_ARG(m.name)); 378 } 379 380 kit_ar_iter_free(it); 381 ctx.file_io->release(ctx.file_io->user, &fd); 382 return rc; 383 } 384 385 static int ar_do_print(DriverEnv* env, const char* archive_path, int argc, 386 char** argv, int start, int verbose) { 387 KitContext ctx; 388 KitFileData fd = {0}; 389 KitSlice input; 390 KitArIter* it = NULL; 391 KitArMember m; 392 KitWriter* out; 393 /* prefix when 0 or >=2 filters, or when verbose forces it */ 394 int header = verbose || (argc - start) != 1; 395 int rc = 0; 396 397 if (!ar_open_for_read(env, archive_path, &ctx, &fd, &input)) return 1; 398 if (kit_ar_iter_new(&ctx, &input, &it) != KIT_OK) { 399 driver_errf(AR_TOOL, "not an archive: %.*s", 400 KIT_SLICE_ARG(kit_slice_cstr(archive_path))); 401 ctx.file_io->release(ctx.file_io->user, &fd); 402 return 1; 403 } 404 405 out = driver_stdout_writer(env); 406 if (!out) { 407 driver_errf(AR_TOOL, "out of memory"); 408 kit_ar_iter_free(it); 409 ctx.file_io->release(ctx.file_io->user, &fd); 410 return 1; 411 } 412 413 for (;;) { 414 KitIterResult r = kit_ar_iter_next(it, &m); 415 if (r != KIT_ITER_ITEM) break; 416 if (!ar_name_selected(m.name, argc, argv, start)) continue; 417 if (header) 418 driver_printf("%.*s(%.*s):\n", 419 KIT_SLICE_ARG(kit_slice_cstr(archive_path)), 420 KIT_SLICE_ARG(m.name)); 421 if (m.size) kit_writer_write(out, m.data, m.size); 422 } 423 if (kit_writer_status(out) != KIT_OK) rc = 1; 424 425 kit_ar_iter_free(it); 426 kit_writer_close(out); 427 ctx.file_io->release(ctx.file_io->user, &fd); 428 return rc; 429 } 430 431 /* Write the archive at `archive_path` from the given members. 432 * 433 * Modes: 434 * has_r=0, has_c=1 — `c`: overwrite, no warning. 435 * has_r=1, has_c=0 — `r`: replace listed members in place; preserve 436 * unlisted; append new ones; warn "creating 437 * archive" when the archive does not yet exist. 438 * has_r=1, has_c=1 — `rc`/`cr`: same as `r` but suppress the create warning. 439 */ 440 static int ar_do_write(DriverEnv* env, const char* archive_path, int nmembers, 441 char** member_paths, int has_r, int has_c, int has_s, 442 int has_v) { 443 uint32_t nnew = nmembers > 0 ? (uint32_t)nmembers : 0u; 444 KitArInput* members = NULL; 445 KitFileData* new_fds = NULL; 446 size_t members_cap = 0; 447 uint32_t nm = 0; /* count of entries in `members` */ 448 KitContext ctx = driver_env_to_context(env); 449 KitWriter* out = NULL; 450 KitArWriteOptions opts = {0}; 451 KitFileData old_fd = {0}; 452 int have_old = 0; 453 int rc = 0; 454 uint32_t i; 455 /* Old-member name storage. kit_ar_iter_next returns KitArMember.name 456 * pointing into the iterator's single internal _namebuf, which is 457 * overwritten on each next(); the seeded members[] outlives iteration, 458 * so each name must be copied into our own backing buffer. */ 459 char* old_name_storage = NULL; 460 size_t old_name_bytes = 0; 461 /* Per-member symbol storage (only used when has_s). msyms is the array 462 * passed to kit_ar_write; sym_allocs holds the single per-member blob 463 * (from kit_obj_global_syms) backing each msyms[i].names + name bytes, freed 464 * via kit_obj_global_syms_free which recovers the size from the block. */ 465 KitArMemberSymbols* msyms = NULL; 466 void** sym_allocs = NULL; 467 ArNameIndex name_index = {0}; 468 469 opts.epoch = driver_epoch_from_env(); 470 opts.long_names = 1; 471 472 /* `r`: read existing archive (if any) and seed `members` with it. */ 473 if (has_r) { 474 KitSlice input; 475 KitArIter* it = NULL; 476 KitArMember m; 477 uint32_t nold = 0; 478 479 if (ctx.file_io->read_all(ctx.file_io->user, archive_path, &old_fd) == 480 KIT_OK) { 481 have_old = 1; 482 input.data = old_fd.data; 483 input.len = old_fd.size; 484 if (kit_ar_iter_new(&ctx, &input, &it) != KIT_OK) { 485 driver_errf(AR_TOOL, "not an archive: %.*s", 486 KIT_SLICE_ARG(kit_slice_cstr(archive_path))); 487 rc = 1; 488 goto done; 489 } 490 /* Count first so we can size the array exactly, and total the 491 * name bytes so we can stash a stable copy of each name. */ 492 for (;;) { 493 KitIterResult r = kit_ar_iter_next(it, &m); 494 if (r != KIT_ITER_ITEM) break; 495 nold++; 496 old_name_bytes += m.name.len + 1; 497 } 498 kit_ar_iter_free(it); 499 it = NULL; 500 } else if (!has_c) { 501 /* POSIX: warn (not an error) when `r` creates a new archive. */ 502 driver_errf(AR_TOOL, "creating %.*s", 503 KIT_SLICE_ARG(kit_slice_cstr(archive_path))); 504 } 505 506 members_cap = (size_t)nold + (size_t)nnew; 507 if (members_cap > 0) { 508 members = 509 (KitArInput*)driver_alloc_zeroed(env, members_cap * sizeof(*members)); 510 if (!members) { 511 driver_errf(AR_TOOL, "out of memory"); 512 rc = 1; 513 goto done; 514 } 515 } 516 517 if (have_old) { 518 size_t cursor = 0; 519 if (old_name_bytes > 0) { 520 old_name_storage = (char*)driver_alloc_zeroed(env, old_name_bytes); 521 if (!old_name_storage) { 522 driver_errf(AR_TOOL, "out of memory"); 523 rc = 1; 524 goto done; 525 } 526 } 527 if (kit_ar_iter_new(&ctx, &input, &it) != KIT_OK) { 528 driver_errf(AR_TOOL, "iter re-open failed"); 529 rc = 1; 530 goto done; 531 } 532 while (nm < nold) { 533 KitIterResult r = kit_ar_iter_next(it, &m); 534 char* dst; 535 size_t j; 536 if (r != KIT_ITER_ITEM) break; 537 dst = old_name_storage + cursor; 538 for (j = 0; j < m.name.len; ++j) *dst++ = m.name.s[j]; 539 *dst++ = '\0'; 540 members[nm].name.s = old_name_storage + cursor; 541 members[nm].name.len = m.name.len; 542 members[nm].bytes.data = m.data; 543 members[nm].bytes.len = m.size; 544 cursor = (size_t)(dst - old_name_storage); 545 nm++; 546 } 547 kit_ar_iter_free(it); 548 } 549 if (nm > 0) { 550 if (!ar_name_index_init(&name_index, env, nm)) { 551 driver_errf(AR_TOOL, "out of memory"); 552 rc = 1; 553 goto done; 554 } 555 for (i = 0; i < nm; ++i) 556 ar_name_index_insert_old(&name_index, members[i].name.s, 557 members[i].name.len, i); 558 } 559 } else { 560 /* `c`: overwrite. */ 561 members_cap = (size_t)nnew; 562 if (members_cap > 0) { 563 members = 564 (KitArInput*)driver_alloc_zeroed(env, members_cap * sizeof(*members)); 565 if (!members) { 566 driver_errf(AR_TOOL, "out of memory"); 567 rc = 1; 568 goto done; 569 } 570 } 571 } 572 573 if (nnew > 0) { 574 new_fds = 575 (KitFileData*)driver_alloc_zeroed(env, (size_t)nnew * sizeof(*new_fds)); 576 if (!new_fds) { 577 driver_errf(AR_TOOL, "out of memory"); 578 rc = 1; 579 goto done; 580 } 581 for (i = 0; i < nnew; ++i) { 582 const char* path = member_paths[i]; 583 const char* base = driver_basename(path); 584 size_t base_len = driver_strlen(base); 585 if (ctx.file_io->read_all(ctx.file_io->user, path, &new_fds[i]) != 586 KIT_OK) { 587 driver_errf(AR_TOOL, "failed to read: %.*s", 588 KIT_SLICE_ARG(kit_slice_cstr(path))); 589 rc = 1; 590 goto done; 591 } 592 if (has_r) { 593 /* Replace existing slot if a member with the same basename 594 * is already present; otherwise append. */ 595 uint32_t j = 0; 596 int replaced = 0; 597 if (ar_name_index_take(&name_index, base, base_len, &j)) { 598 members[j].name.s = base; 599 members[j].name.len = base_len; 600 members[j].bytes.data = new_fds[i].data; 601 members[j].bytes.len = new_fds[i].size; 602 replaced = 1; 603 } 604 if (!replaced) { 605 members[nm].name.s = base; 606 members[nm].name.len = base_len; 607 members[nm].bytes.data = new_fds[i].data; 608 members[nm].bytes.len = new_fds[i].size; 609 nm++; 610 } 611 if (has_v) 612 driver_printf("%c - %.*s\n", replaced ? 'r' : 'a', 613 KIT_SLICE_ARG(kit_slice_cstr(base))); 614 } else { 615 ar_set_input_member(&members[nm], base, &new_fds[i]); 616 nm++; 617 if (has_v) 618 driver_printf("a - %.*s\n", KIT_SLICE_ARG(kit_slice_cstr(base))); 619 } 620 } 621 } 622 623 if (has_s && nm > 0) { 624 msyms = (KitArMemberSymbols*)driver_alloc_zeroed( 625 env, (size_t)nm * sizeof(*msyms)); 626 sym_allocs = 627 (void**)driver_alloc_zeroed(env, (size_t)nm * sizeof(*sym_allocs)); 628 if (!msyms || !sym_allocs) { 629 driver_errf(AR_TOOL, "out of memory"); 630 rc = 1; 631 goto done; 632 } 633 for (i = 0; i < nm; ++i) { 634 /* libkit owns the "which symbols a linker indexes" policy + packing; the 635 * returned block carries its own free size (kit_obj_global_syms_free). */ 636 if (kit_obj_global_syms(&ctx, &members[i].bytes, &msyms[i], 637 &sym_allocs[i]) != KIT_OK) { 638 driver_errf(AR_TOOL, "out of memory"); 639 rc = 1; 640 goto done; 641 } 642 } 643 opts.symbol_index = 1; 644 opts.member_symbols = msyms; 645 } 646 647 if (ctx.file_io->open_writer(ctx.file_io->user, archive_path, &out) != 648 KIT_OK) { 649 driver_errf(AR_TOOL, "failed to open: %.*s", 650 KIT_SLICE_ARG(kit_slice_cstr(archive_path))); 651 rc = 1; 652 } else { 653 rc = kit_ar_write(out, members, nm, &opts) == KIT_OK ? 0 : 1; 654 if (rc == 0 && kit_writer_status(out) != KIT_OK) rc = 1; 655 kit_writer_close(out); 656 } 657 658 done: 659 ar_name_index_fini(&name_index); 660 if (sym_allocs) { 661 for (i = 0; i < nm; ++i) { 662 if (sym_allocs[i]) kit_obj_global_syms_free(&ctx, sym_allocs[i]); 663 } 664 driver_free(env, sym_allocs, (size_t)nm * sizeof(*sym_allocs)); 665 } 666 if (msyms) driver_free(env, msyms, (size_t)nm * sizeof(*msyms)); 667 if (new_fds) { 668 for (i = 0; i < nnew; ++i) { 669 if (new_fds[i].data) ctx.file_io->release(ctx.file_io->user, &new_fds[i]); 670 } 671 driver_free(env, new_fds, (size_t)nnew * sizeof(*new_fds)); 672 } 673 if (members) driver_free(env, members, members_cap * sizeof(*members)); 674 if (old_name_storage) driver_free(env, old_name_storage, old_name_bytes); 675 if (have_old) ctx.file_io->release(ctx.file_io->user, &old_fd); 676 return rc; 677 } 678 679 int driver_ar(int argc, char** argv) { 680 DriverEnv env; 681 const char* mode; 682 const char* archive_path; 683 int do_write = 0; 684 int do_list = 0; 685 int do_extract = 0; 686 int do_print = 0; 687 int has_r = 0; 688 int has_c = 0; 689 int has_s = 0; 690 int has_v = 0; 691 int argi = 1; 692 int member_start; 693 int i; 694 int rc; 695 696 if (argc < 2 || driver_argv_wants_help(argc, argv, 1)) { 697 driver_help_ar(); 698 return 0; 699 } 700 701 if (driver_streq(argv[argi], "--")) ++argi; 702 if (argi >= argc) { 703 ar_usage(); 704 return 2; 705 } 706 707 mode = argv[argi++]; 708 if (mode[0] == '-' && mode[1] != '\0') ++mode; 709 if (argi < argc && driver_streq(argv[argi], "--")) ++argi; 710 if (argi >= argc) { 711 ar_usage(); 712 return 2; 713 } 714 archive_path = argv[argi++]; 715 member_start = argi; 716 717 for (i = 0; mode[i]; ++i) { 718 switch (mode[i]) { 719 case 'r': 720 do_write = 1; 721 has_r = 1; 722 break; 723 case 'c': 724 do_write = 1; 725 has_c = 1; 726 break; 727 case 's': 728 has_s = 1; 729 break; 730 case 'v': 731 has_v = 1; 732 break; 733 case 'u': 734 break; 735 case 't': 736 do_list = 1; 737 break; 738 case 'x': 739 do_extract = 1; 740 break; 741 case 'p': 742 do_print = 1; 743 break; 744 case 'd': 745 case 'q': 746 driver_errf(AR_TOOL, "operation not implemented: %c", mode[i]); 747 return 2; 748 default: 749 driver_errf(AR_TOOL, "unrecognized mode letter: %c", mode[i]); 750 return 2; 751 } 752 } 753 if (has_s && !do_write) { 754 driver_errf(AR_TOOL, "s requires r or c"); 755 return 2; 756 } 757 758 { 759 int kinds = !!do_write + !!do_list + !!do_extract + !!do_print; 760 if (kinds == 0) { 761 driver_errf(AR_TOOL, "no operation in mode: %.*s", 762 KIT_SLICE_ARG(kit_slice_cstr(mode))); 763 ar_usage(); 764 return 2; 765 } 766 if (kinds > 1) { 767 driver_errf(AR_TOOL, "conflicting operations: %.*s", 768 KIT_SLICE_ARG(kit_slice_cstr(mode))); 769 return 2; 770 } 771 } 772 773 driver_env_init(&env); 774 775 if (do_list) { 776 if (member_start != argc) { 777 driver_errf(AR_TOOL, "t takes no member arguments"); 778 driver_env_fini(&env); 779 return 2; 780 } 781 rc = ar_do_list(&env, archive_path, has_v); 782 } else if (do_extract) { 783 rc = ar_do_extract(&env, archive_path, argc, argv, member_start, has_v); 784 } else if (do_print) { 785 rc = ar_do_print(&env, archive_path, argc, argv, member_start, has_v); 786 } else { 787 rc = ar_do_write(&env, archive_path, argc - member_start, 788 argv + member_start, has_r, has_c, has_s, has_v); 789 } 790 791 driver_env_fini(&env); 792 return rc; 793 }