ar.c (21835B)
1 #include <kit/archive.h> 2 #include <kit/core.h> 3 #include <kit/object.h> 4 5 #include "driver.h" 6 #include "inputs.h" 7 8 /* `kit ar` — POSIX ar archive front-end. 9 * 10 * Supported operations (mutually exclusive): 11 * kit ar r archive.a in.o... replace listed members in 12 * place; preserve unlisted; 13 * append new ones; warn when 14 * creating the archive. 15 * kit ar c archive.a in.o... overwrite the archive with 16 * exactly the listed members. 17 * kit ar {rc|cr} archive.a in.o... `r` semantics, but quiet on 18 * archive creation. 19 * kit ar t archive.a list member names 20 * kit ar x archive.a [members...] extract members to cwd 21 * kit ar p archive.a [members...] print members to stdout 22 * 23 * Modifiers (combine with above): 24 * s emit a System V `/` symbol-index member at the head of the 25 * archive. Only valid combined with r/c (i.e. rs, cs, rcs). 26 * Each member is opened with kit_obj_open and its globally- 27 * defined symbols (KIT_SB_GLOBAL with section != NONE) are 28 * indexed; non-object members contribute no symbols. 29 * u update only if member is newer. Accepted as a compatibility 30 * modifier and currently treated as a no-op. 31 * v verbose. With t, list each member as "<size>\t<name>" instead 32 * of name-only. With x, print "x - <name>" per extracted member. 33 * With p, prepend a "<archive>(<name>):\n" header to every member 34 * (not just when there are 0 or >=2 filters). With r/c, print 35 * "a - <name>" for newly added members and "r - <name>" for 36 * replacements (r mode only). 37 * 38 * Reproducibility: SOURCE_DATE_EPOCH (when set to a positive integer) is 39 * written to ar_date for every member on write. Long member names are 40 * routed through a `//` extended-name table. 41 * 42 * Not yet implemented: d (delete), q (quick append), and standalone `s` 43 * without r/c. Encountering any of those yields a usage error with exit 44 * code 2. */ 45 46 #define AR_TOOL "ar" 47 48 static void ar_usage(void) { 49 driver_errf(AR_TOOL, "%.*s", 50 KIT_SLICE_ARG(KIT_SLICE_LIT( 51 "usage: kit ar {rc|r|c|t|x|p}[s] archive.a [members...]\n" 52 " kit ar --help for full operation reference"))); 53 } 54 55 void driver_help_ar(void) { 56 driver_printf( 57 "%.*s", 58 KIT_SLICE_ARG(KIT_SLICE_LIT( 59 "kit ar — POSIX `ar` archive front-end\n" 60 "\n" 61 "USAGE\n" 62 " kit ar MODE archive.a [members...]\n" 63 "\n" 64 "MODE is a string of one or more letters; exactly one operation " 65 "must\n" 66 "be selected and any number of modifiers may follow. The archive\n" 67 "path and (optional) member list follow the mode.\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 "\n" 112 "EXAMPLES\n" 113 " kit ar rcs libfoo.a a.o b.o c.o\n" 114 " kit ar t libfoo.a\n" 115 " kit ar x libfoo.a a.o\n" 116 " kit ar p libfoo.a a.o > a.o.copy\n" 117 "\n" 118 "GETTING HELP\n" 119 " -h, --help Show this help and exit\n" 120 "\n" 121 "EXIT CODES\n" 122 " 0 success 1 archive I/O error 2 bad " 123 "usage\n"))); 124 } 125 126 /* Parse SOURCE_DATE_EPOCH into a u64; 0 (or unset/invalid) means "no epoch". */ 127 static uint64_t ar_epoch_from_env(void) { 128 const char* s = driver_getenv("SOURCE_DATE_EPOCH"); 129 uint64_t v = 0; 130 if (!s || !*s) return 0; 131 for (; *s; ++s) { 132 if (*s < '0' || *s > '9') return 0; 133 v = v * 10 + (uint64_t)(*s - '0'); 134 } 135 return v; 136 } 137 138 /* Return 1 iff `name` matches any of the names in argv[start..argc) — or if 139 * there are no filters, in which case every member matches. */ 140 static int ar_name_selected(KitSlice name, int argc, char** argv, int start) { 141 int i; 142 if (start >= argc) return 1; 143 for (i = start; i < argc; ++i) { 144 const char* base = driver_basename(argv[i]); 145 if (kit_slice_eq_cstr(name, base)) return 1; 146 } 147 return 0; 148 } 149 150 static void ar_set_input_member(KitArInput* out, const char* name, 151 const KitFileData* fd) { 152 out->name.s = name; 153 out->name.len = driver_strlen(name); 154 out->bytes.data = fd->data; 155 out->bytes.len = fd->size; 156 } 157 158 /* Open the archive bytes via file_io. Caller releases fd via ctx. */ 159 static int ar_open_for_read(DriverEnv* env, const char* path, 160 KitContext* ctx_out, KitFileData* fd_out, 161 KitSlice* input_out) { 162 *ctx_out = driver_env_to_context(env); 163 if (ctx_out->file_io->read_all(ctx_out->file_io->user, path, fd_out) != 164 KIT_OK) { 165 driver_errf(AR_TOOL, "failed to read: %.*s", 166 KIT_SLICE_ARG(kit_slice_cstr(path))); 167 return 0; 168 } 169 (void)path; 170 input_out->data = fd_out->data; 171 input_out->len = fd_out->size; 172 return 1; 173 } 174 175 static int ar_do_list(DriverEnv* env, const char* archive_path, int verbose) { 176 KitContext ctx; 177 KitFileData fd = {0}; 178 KitSlice input; 179 KitWriter* out; 180 int rc; 181 182 if (!ar_open_for_read(env, archive_path, &ctx, &fd, &input)) return 1; 183 184 out = driver_stdout_writer(env); 185 if (!out) { 186 driver_errf(AR_TOOL, "out of memory"); 187 ctx.file_io->release(ctx.file_io->user, &fd); 188 return 1; 189 } 190 191 if (verbose) { 192 KitArIter* it = NULL; 193 KitArMember m; 194 if (kit_ar_iter_new(&ctx, &input, &it) != KIT_OK) { 195 driver_errf(AR_TOOL, "not an archive: %.*s", 196 KIT_SLICE_ARG(kit_slice_cstr(archive_path))); 197 kit_writer_close(out); 198 ctx.file_io->release(ctx.file_io->user, &fd); 199 return 1; 200 } 201 for (;;) { 202 KitIterResult r = kit_ar_iter_next(it, &m); 203 if (r != KIT_ITER_ITEM) break; 204 driver_printf("%zu\t%.*s\n", m.size, KIT_SLICE_ARG(m.name)); 205 } 206 kit_ar_iter_free(it); 207 rc = kit_writer_status(out) == KIT_OK ? 0 : 1; 208 } else { 209 rc = kit_ar_list(&input, out) == KIT_OK ? 0 : 1; 210 if (rc) 211 driver_errf(AR_TOOL, "failed to read archive: %.*s", 212 KIT_SLICE_ARG(kit_slice_cstr(archive_path))); 213 } 214 kit_writer_close(out); 215 ctx.file_io->release(ctx.file_io->user, &fd); 216 return rc; 217 } 218 219 static int ar_do_extract(DriverEnv* env, const char* archive_path, int argc, 220 char** argv, int start, int verbose) { 221 KitContext ctx; 222 KitFileData fd = {0}; 223 KitSlice input; 224 KitArIter* it = NULL; 225 KitArMember m; 226 int rc = 0; 227 228 if (!ar_open_for_read(env, archive_path, &ctx, &fd, &input)) return 1; 229 if (kit_ar_iter_new(&ctx, &input, &it) != KIT_OK) { 230 driver_errf(AR_TOOL, "not an archive: %.*s", 231 KIT_SLICE_ARG(kit_slice_cstr(archive_path))); 232 ctx.file_io->release(ctx.file_io->user, &fd); 233 return 1; 234 } 235 236 for (;;) { 237 KitWriter* out; 238 KitIterResult r = kit_ar_iter_next(it, &m); 239 if (r != KIT_ITER_ITEM) break; 240 if (!ar_name_selected(m.name, argc, argv, start)) continue; 241 242 if (ctx.file_io->open_writer(ctx.file_io->user, m.name.s, &out) != KIT_OK) { 243 driver_errf(AR_TOOL, "failed to open: %.*s", KIT_SLICE_ARG(m.name)); 244 rc = 1; 245 continue; 246 } 247 if (m.size) kit_writer_write(out, m.data, m.size); 248 if (kit_writer_status(out) != KIT_OK) { 249 driver_errf(AR_TOOL, "write failed: %.*s", KIT_SLICE_ARG(m.name)); 250 rc = 1; 251 } 252 kit_writer_close(out); 253 if (verbose) driver_printf("x - %.*s\n", KIT_SLICE_ARG(m.name)); 254 } 255 256 kit_ar_iter_free(it); 257 ctx.file_io->release(ctx.file_io->user, &fd); 258 return rc; 259 } 260 261 static int ar_do_print(DriverEnv* env, const char* archive_path, int argc, 262 char** argv, int start, int verbose) { 263 KitContext ctx; 264 KitFileData fd = {0}; 265 KitSlice input; 266 KitArIter* it = NULL; 267 KitArMember m; 268 KitWriter* out; 269 /* prefix when 0 or >=2 filters, or when verbose forces it */ 270 int header = verbose || (argc - start) != 1; 271 int rc = 0; 272 273 if (!ar_open_for_read(env, archive_path, &ctx, &fd, &input)) return 1; 274 if (kit_ar_iter_new(&ctx, &input, &it) != KIT_OK) { 275 driver_errf(AR_TOOL, "not an archive: %.*s", 276 KIT_SLICE_ARG(kit_slice_cstr(archive_path))); 277 ctx.file_io->release(ctx.file_io->user, &fd); 278 return 1; 279 } 280 281 out = driver_stdout_writer(env); 282 if (!out) { 283 driver_errf(AR_TOOL, "out of memory"); 284 kit_ar_iter_free(it); 285 ctx.file_io->release(ctx.file_io->user, &fd); 286 return 1; 287 } 288 289 for (;;) { 290 KitIterResult r = kit_ar_iter_next(it, &m); 291 if (r != KIT_ITER_ITEM) break; 292 if (!ar_name_selected(m.name, argc, argv, start)) continue; 293 if (header) 294 driver_printf("%.*s(%.*s):\n", 295 KIT_SLICE_ARG(kit_slice_cstr(archive_path)), 296 KIT_SLICE_ARG(m.name)); 297 if (m.size) kit_writer_write(out, m.data, m.size); 298 } 299 if (kit_writer_status(out) != KIT_OK) rc = 1; 300 301 kit_ar_iter_free(it); 302 kit_writer_close(out); 303 ctx.file_io->release(ctx.file_io->user, &fd); 304 return rc; 305 } 306 307 /* Write the archive at `archive_path` from the given members. 308 * 309 * Modes: 310 * has_r=0, has_c=1 — `c`: overwrite, no warning. 311 * has_r=1, has_c=0 — `r`: replace listed members in place; preserve 312 * unlisted; append new ones; warn "creating 313 * archive" when the archive does not yet exist. 314 * has_r=1, has_c=1 — `rc`/`cr`: same as `r` but suppress the create warning. 315 */ 316 static int ar_do_write(DriverEnv* env, const char* archive_path, int nmembers, 317 char** member_paths, int has_r, int has_c, int has_s, 318 int has_v) { 319 uint32_t nnew = nmembers > 0 ? (uint32_t)nmembers : 0u; 320 KitArInput* members = NULL; 321 KitFileData* new_fds = NULL; 322 size_t members_cap = 0; 323 uint32_t nm = 0; /* count of entries in `members` */ 324 KitContext ctx = driver_env_to_context(env); 325 KitWriter* out = NULL; 326 KitArWriteOptions opts = {0}; 327 KitFileData old_fd = {0}; 328 int have_old = 0; 329 int rc = 0; 330 uint32_t i; 331 /* Old-member name storage. kit_ar_iter_next returns KitArMember.name 332 * pointing into the iterator's single internal _namebuf, which is 333 * overwritten on each next(); the seeded members[] outlives iteration, 334 * so each name must be copied into our own backing buffer. */ 335 char* old_name_storage = NULL; 336 size_t old_name_bytes = 0; 337 /* Per-member symbol storage (only used when has_s). msyms is the array 338 * passed to kit_ar_write; sym_allocs holds the single per-member 339 * blob backing each msyms[i].names + name bytes (one allocation per 340 * member, sized in sym_alloc_szs for the matching driver_free). */ 341 KitArMemberSymbols* msyms = NULL; 342 void** sym_allocs = NULL; 343 size_t* sym_alloc_szs = NULL; 344 345 opts.epoch = ar_epoch_from_env(); 346 opts.long_names = 1; 347 348 /* `r`: read existing archive (if any) and seed `members` with it. */ 349 if (has_r) { 350 KitSlice input; 351 KitArIter* it = NULL; 352 KitArMember m; 353 uint32_t nold = 0; 354 355 if (ctx.file_io->read_all(ctx.file_io->user, archive_path, &old_fd) == 356 KIT_OK) { 357 have_old = 1; 358 input.data = old_fd.data; 359 input.len = old_fd.size; 360 if (kit_ar_iter_new(&ctx, &input, &it) != KIT_OK) { 361 driver_errf(AR_TOOL, "not an archive: %.*s", 362 KIT_SLICE_ARG(kit_slice_cstr(archive_path))); 363 rc = 1; 364 goto done; 365 } 366 /* Count first so we can size the array exactly, and total the 367 * name bytes so we can stash a stable copy of each name. */ 368 for (;;) { 369 KitIterResult r = kit_ar_iter_next(it, &m); 370 if (r != KIT_ITER_ITEM) break; 371 nold++; 372 old_name_bytes += m.name.len + 1; 373 } 374 kit_ar_iter_free(it); 375 it = NULL; 376 } else if (!has_c) { 377 /* POSIX: warn (not an error) when `r` creates a new archive. */ 378 driver_errf(AR_TOOL, "creating %.*s", 379 KIT_SLICE_ARG(kit_slice_cstr(archive_path))); 380 } 381 382 members_cap = (size_t)nold + (size_t)nnew; 383 if (members_cap > 0) { 384 members = 385 (KitArInput*)driver_alloc_zeroed(env, members_cap * sizeof(*members)); 386 if (!members) { 387 driver_errf(AR_TOOL, "out of memory"); 388 rc = 1; 389 goto done; 390 } 391 } 392 393 if (have_old) { 394 size_t cursor = 0; 395 if (old_name_bytes > 0) { 396 old_name_storage = (char*)driver_alloc_zeroed(env, old_name_bytes); 397 if (!old_name_storage) { 398 driver_errf(AR_TOOL, "out of memory"); 399 rc = 1; 400 goto done; 401 } 402 } 403 if (kit_ar_iter_new(&ctx, &input, &it) != KIT_OK) { 404 driver_errf(AR_TOOL, "iter re-open failed"); 405 rc = 1; 406 goto done; 407 } 408 while (nm < nold) { 409 KitIterResult r = kit_ar_iter_next(it, &m); 410 char* dst; 411 size_t j; 412 if (r != KIT_ITER_ITEM) break; 413 dst = old_name_storage + cursor; 414 for (j = 0; j < m.name.len; ++j) *dst++ = m.name.s[j]; 415 *dst++ = '\0'; 416 members[nm].name.s = old_name_storage + cursor; 417 members[nm].name.len = m.name.len; 418 members[nm].bytes.data = m.data; 419 members[nm].bytes.len = m.size; 420 cursor = (size_t)(dst - old_name_storage); 421 nm++; 422 } 423 kit_ar_iter_free(it); 424 } 425 } else { 426 /* `c`: overwrite. */ 427 members_cap = (size_t)nnew; 428 if (members_cap > 0) { 429 members = 430 (KitArInput*)driver_alloc_zeroed(env, members_cap * sizeof(*members)); 431 if (!members) { 432 driver_errf(AR_TOOL, "out of memory"); 433 rc = 1; 434 goto done; 435 } 436 } 437 } 438 439 if (nnew > 0) { 440 new_fds = 441 (KitFileData*)driver_alloc_zeroed(env, (size_t)nnew * sizeof(*new_fds)); 442 if (!new_fds) { 443 driver_errf(AR_TOOL, "out of memory"); 444 rc = 1; 445 goto done; 446 } 447 for (i = 0; i < nnew; ++i) { 448 const char* path = member_paths[i]; 449 const char* base = driver_basename(path); 450 if (ctx.file_io->read_all(ctx.file_io->user, path, &new_fds[i]) != 451 KIT_OK) { 452 driver_errf(AR_TOOL, "failed to read: %.*s", 453 KIT_SLICE_ARG(kit_slice_cstr(path))); 454 rc = 1; 455 goto done; 456 } 457 if (has_r) { 458 /* Replace existing slot if a member with the same basename 459 * is already present; otherwise append. */ 460 uint32_t j; 461 int replaced = 0; 462 for (j = 0; j < nm; ++j) { 463 if (driver_streq(members[j].name.s, base)) { 464 ar_set_input_member(&members[j], base, &new_fds[i]); 465 replaced = 1; 466 break; 467 } 468 } 469 if (!replaced) { 470 ar_set_input_member(&members[nm], base, &new_fds[i]); 471 nm++; 472 } 473 if (has_v) 474 driver_printf("%c - %.*s\n", replaced ? 'r' : 'a', 475 KIT_SLICE_ARG(kit_slice_cstr(base))); 476 } else { 477 ar_set_input_member(&members[nm], base, &new_fds[i]); 478 nm++; 479 if (has_v) 480 driver_printf("a - %.*s\n", KIT_SLICE_ARG(kit_slice_cstr(base))); 481 } 482 } 483 } 484 485 if (has_s && nm > 0) { 486 msyms = (KitArMemberSymbols*)driver_alloc_zeroed( 487 env, (size_t)nm * sizeof(*msyms)); 488 sym_allocs = 489 (void**)driver_alloc_zeroed(env, (size_t)nm * sizeof(*sym_allocs)); 490 sym_alloc_szs = 491 (size_t*)driver_alloc_zeroed(env, (size_t)nm * sizeof(*sym_alloc_szs)); 492 if (!msyms || !sym_allocs || !sym_alloc_szs) { 493 driver_errf(AR_TOOL, "out of memory"); 494 rc = 1; 495 goto done; 496 } 497 for (i = 0; i < nm; ++i) { 498 KitSlice in = members[i].bytes; 499 void* blob = NULL; 500 size_t blob_size = 0; 501 const KitSlice* names = NULL; 502 uint32_t count = 0; 503 504 if (driver_collect_obj_global_syms(env, &ctx, AR_TOOL, &in, &blob, 505 &blob_size, &names, &count) != 0) { 506 rc = 1; 507 goto done; 508 } 509 if (count == 0) continue; 510 511 sym_allocs[i] = blob; 512 sym_alloc_szs[i] = blob_size; 513 msyms[i].names = names; 514 msyms[i].count = count; 515 } 516 opts.symbol_index = 1; 517 opts.member_symbols = msyms; 518 } 519 520 if (ctx.file_io->open_writer(ctx.file_io->user, archive_path, &out) != 521 KIT_OK) { 522 driver_errf(AR_TOOL, "failed to open: %.*s", 523 KIT_SLICE_ARG(kit_slice_cstr(archive_path))); 524 rc = 1; 525 } else { 526 rc = kit_ar_write(out, members, nm, &opts) == KIT_OK ? 0 : 1; 527 if (rc == 0 && kit_writer_status(out) != KIT_OK) rc = 1; 528 kit_writer_close(out); 529 } 530 531 done: 532 if (sym_allocs) { 533 for (i = 0; i < nm; ++i) { 534 if (sym_allocs[i]) driver_free(env, sym_allocs[i], sym_alloc_szs[i]); 535 } 536 driver_free(env, sym_allocs, (size_t)nm * sizeof(*sym_allocs)); 537 driver_free(env, sym_alloc_szs, (size_t)nm * sizeof(*sym_alloc_szs)); 538 } 539 if (msyms) driver_free(env, msyms, (size_t)nm * sizeof(*msyms)); 540 if (new_fds) { 541 for (i = 0; i < nnew; ++i) { 542 if (new_fds[i].data) ctx.file_io->release(ctx.file_io->user, &new_fds[i]); 543 } 544 driver_free(env, new_fds, (size_t)nnew * sizeof(*new_fds)); 545 } 546 if (members) driver_free(env, members, members_cap * sizeof(*members)); 547 if (old_name_storage) driver_free(env, old_name_storage, old_name_bytes); 548 if (have_old) ctx.file_io->release(ctx.file_io->user, &old_fd); 549 return rc; 550 } 551 552 int driver_ar(int argc, char** argv) { 553 DriverEnv env; 554 const char* mode; 555 const char* archive_path; 556 int do_write = 0; 557 int do_list = 0; 558 int do_extract = 0; 559 int do_print = 0; 560 int has_r = 0; 561 int has_c = 0; 562 int has_s = 0; 563 int has_v = 0; 564 int i; 565 int rc; 566 567 if (argc < 2 || driver_argv_wants_help(argc, argv, 1)) { 568 driver_help_ar(); 569 return 0; 570 } 571 572 if (argc < 3) { 573 ar_usage(); 574 return 2; 575 } 576 577 mode = argv[1]; 578 archive_path = argv[2]; 579 580 for (i = 0; mode[i]; ++i) { 581 switch (mode[i]) { 582 case 'r': 583 do_write = 1; 584 has_r = 1; 585 break; 586 case 'c': 587 do_write = 1; 588 has_c = 1; 589 break; 590 case 's': 591 has_s = 1; 592 break; 593 case 'v': 594 has_v = 1; 595 break; 596 case 'u': 597 break; 598 case 't': 599 do_list = 1; 600 break; 601 case 'x': 602 do_extract = 1; 603 break; 604 case 'p': 605 do_print = 1; 606 break; 607 case 'd': 608 case 'q': 609 driver_errf(AR_TOOL, "operation not implemented: %c", mode[i]); 610 return 2; 611 default: 612 driver_errf(AR_TOOL, "unrecognized mode letter: %c", mode[i]); 613 return 2; 614 } 615 } 616 if (has_s && !do_write) { 617 driver_errf(AR_TOOL, "s requires r or c"); 618 return 2; 619 } 620 621 { 622 int kinds = !!do_write + !!do_list + !!do_extract + !!do_print; 623 if (kinds == 0) { 624 driver_errf(AR_TOOL, "no operation in mode: %.*s", 625 KIT_SLICE_ARG(kit_slice_cstr(mode))); 626 ar_usage(); 627 return 2; 628 } 629 if (kinds > 1) { 630 driver_errf(AR_TOOL, "conflicting operations: %.*s", 631 KIT_SLICE_ARG(kit_slice_cstr(mode))); 632 return 2; 633 } 634 } 635 636 driver_env_init(&env); 637 638 if (do_list) { 639 if (argc != 3) { 640 driver_errf(AR_TOOL, "t takes no member arguments"); 641 driver_env_fini(&env); 642 return 2; 643 } 644 rc = ar_do_list(&env, archive_path, has_v); 645 } else if (do_extract) { 646 rc = ar_do_extract(&env, archive_path, argc, argv, 3, has_v); 647 } else if (do_print) { 648 rc = ar_do_print(&env, archive_path, argc, argv, 3, has_v); 649 } else { 650 rc = ar_do_write(&env, archive_path, argc - 3, argv + 3, has_r, has_c, 651 has_s, has_v); 652 } 653 654 driver_env_fini(&env); 655 return rc; 656 }