kit

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

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 }