kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

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 }