kit

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

archive.c (11299B)


      1 /* POSIX ar archive reader/writer. */
      2 
      3 #include <kit/archive.h>
      4 #include <kit/object.h>
      5 
      6 #include "core/bytes.h"
      7 #include "core/core.h"
      8 #include "core/heap.h"
      9 #include "core/slice.h"
     10 
     11 /* ============================================================
     12  * Write helpers
     13  * ============================================================ */
     14 
     15 static KitStatus wh_bytes(Writer* w, const void* p, size_t n) {
     16   return kit_writer_write(w, p, n);
     17 }
     18 static KitStatus wh_char(Writer* w, char c) {
     19   return kit_writer_write(w, &c, 1);
     20 }
     21 static KitStatus wh_nl(Writer* w) { return wh_char(w, '\n'); }
     22 
     23 static void wh_ar_num(char* dst, int width, u64 v) {
     24   char tmp[20];
     25   int len = 0, i;
     26   if (v == 0) {
     27     tmp[len++] = '0';
     28   } else {
     29     u64 t = v;
     30     while (t) {
     31       tmp[len++] = '0' + (int)(t % 10);
     32       t /= 10;
     33     }
     34   }
     35   for (i = 0; i < len / 2; ++i) {
     36     char x = tmp[i];
     37     tmp[i] = tmp[len - 1 - i];
     38     tmp[len - 1 - i] = x;
     39   }
     40   for (i = 0; i < len && i < width; ++i) dst[i] = tmp[i];
     41   for (; i < width; ++i) dst[i] = ' ';
     42 }
     43 
     44 static KitStatus wh_be32(Writer* w, u32 v) {
     45   u8 b[4];
     46   wr_u32_be(b, v);
     47   return wh_bytes(w, b, 4);
     48 }
     49 
     50 static Slice ar_name_basename(Slice in) {
     51   size_t start = 0, i;
     52   for (i = 0; i < in.len; ++i) {
     53     if (in.s[i] == '/') start = i + 1;
     54   }
     55   return (Slice){.s = in.s + start, .len = in.len - start};
     56 }
     57 
     58 static int ar_name_needs_longtable(const char* name, size_t len) {
     59   size_t i;
     60   if (len > 15) return 1;
     61   for (i = 0; i < len; ++i)
     62     if (name[i] == '/') return 1;
     63   return 0;
     64 }
     65 
     66 static size_t ar_member_padded_size(size_t len) { return 60 + len + (len & 1); }
     67 
     68 static void ar_fill_header(char hdr[60], const char name_field[16],
     69                            uint64_t epoch, uint64_t size) {
     70   size_t j;
     71   for (j = 0; j < 16; ++j) hdr[j] = name_field[j];
     72   for (j = 16; j < 28; ++j) hdr[j] = ' ';
     73   if (epoch)
     74     wh_ar_num(hdr + 16, 12, epoch);
     75   else
     76     hdr[16] = '0';
     77   for (j = 28; j < 34; ++j) hdr[j] = ' ';
     78   hdr[28] = '0';
     79   for (j = 34; j < 40; ++j) hdr[j] = ' ';
     80   hdr[34] = '0';
     81   for (j = 40; j < 48; ++j) hdr[j] = ' ';
     82   hdr[40] = '6';
     83   hdr[41] = '4';
     84   hdr[42] = '4';
     85   wh_ar_num(hdr + 48, 10, size);
     86   hdr[58] = '`';
     87   hdr[59] = '\n';
     88 }
     89 
     90 KitStatus kit_ar_write(KitWriter* out, const KitArInput* members,
     91                        uint32_t nmembers, const KitArWriteOptions* opts) {
     92   static const char magic[] = "!<arch>\n";
     93   static const KitArWriteOptions default_opts = {0, 0, 0, NULL};
     94   uint32_t i;
     95   uint64_t epoch;
     96   int long_names;
     97   int symbol_index;
     98   const KitArMemberSymbols* msyms;
     99   uint64_t longtab_size = 0;
    100   uint32_t nsyms = 0;
    101   uint64_t names_size = 0;
    102   uint64_t index_payload = 0;
    103   uint64_t index_total = 0;
    104   uint64_t longtab_total = 0;
    105   char pad = '\n';
    106 
    107   if (!out) return KIT_INVALID;
    108   if (!members && nmembers) return KIT_INVALID;
    109 
    110   if (!opts) opts = &default_opts;
    111   epoch = opts->epoch;
    112   long_names = opts->long_names;
    113   symbol_index = opts->symbol_index;
    114   msyms = opts->member_symbols;
    115 
    116   if (long_names) {
    117     for (i = 0; i < nmembers; ++i) {
    118       Slice base;
    119       if (!members[i].name.s) return KIT_INVALID;
    120       base = ar_name_basename(members[i].name);
    121       if (ar_name_needs_longtable(base.s, base.len)) {
    122         longtab_size += (uint64_t)base.len + 2;
    123       }
    124     }
    125   } else {
    126     for (i = 0; i < nmembers; ++i) {
    127       if (!members[i].name.s) return KIT_INVALID;
    128     }
    129   }
    130 
    131   if (symbol_index) {
    132     if (msyms) {
    133       for (i = 0; i < nmembers; ++i) {
    134         u32 k;
    135         if (msyms[i].count && !msyms[i].names) return KIT_INVALID;
    136         for (k = 0; k < msyms[i].count; ++k) {
    137           Slice nm = msyms[i].names[k];
    138           if (!nm.s) return KIT_INVALID;
    139           nsyms += 1;
    140           names_size += (uint64_t)nm.len + 1;
    141         }
    142       }
    143     }
    144     index_payload = (uint64_t)4 + (uint64_t)4 * (uint64_t)nsyms + names_size;
    145     index_total = 60 + index_payload + (index_payload & 1);
    146   }
    147   if (longtab_size) {
    148     longtab_total = 60 + longtab_size + (longtab_size & 1);
    149   }
    150 
    151   wh_bytes(out, magic, 8);
    152 
    153   if (symbol_index) {
    154     char hdr[60];
    155     char name_field[16];
    156     size_t j;
    157     uint64_t cur_offset;
    158 
    159     for (j = 0; j < 16; ++j) name_field[j] = ' ';
    160     name_field[0] = '/';
    161     ar_fill_header(hdr, name_field, epoch, index_payload);
    162     wh_bytes(out, hdr, 60);
    163 
    164     wh_be32(out, nsyms);
    165 
    166     cur_offset = (uint64_t)8 + index_total + longtab_total;
    167     if (msyms) {
    168       for (i = 0; i < nmembers; ++i) {
    169         u32 k;
    170         for (k = 0; k < msyms[i].count; ++k) {
    171           wh_be32(out, (u32)cur_offset);
    172         }
    173         cur_offset += ar_member_padded_size(members[i].bytes.len);
    174       }
    175     }
    176 
    177     if (msyms) {
    178       for (i = 0; i < nmembers; ++i) {
    179         u32 k;
    180         for (k = 0; k < msyms[i].count; ++k) {
    181           Slice nm = msyms[i].names[k];
    182           static const char nul = '\0';
    183           wh_bytes(out, nm.s, nm.len);
    184           wh_bytes(out, &nul, 1);
    185         }
    186       }
    187     }
    188 
    189     if (index_payload & 1) wh_bytes(out, &pad, 1);
    190   }
    191 
    192   if (longtab_size) {
    193     char hdr[60];
    194     char name_field[16];
    195     size_t j;
    196     for (j = 0; j < 16; ++j) name_field[j] = ' ';
    197     name_field[0] = '/';
    198     name_field[1] = '/';
    199     ar_fill_header(hdr, name_field, 0, longtab_size);
    200     wh_bytes(out, hdr, 60);
    201     for (i = 0; i < nmembers; ++i) {
    202       Slice base = ar_name_basename(members[i].name);
    203       if (ar_name_needs_longtable(base.s, base.len)) {
    204         wh_bytes(out, base.s, base.len);
    205         wh_bytes(out, "/\n", 2);
    206       }
    207     }
    208     if (longtab_size & 1) wh_bytes(out, &pad, 1);
    209   }
    210 
    211   {
    212     uint64_t longtab_off = 0;
    213     for (i = 0; i < nmembers; ++i) {
    214       const KitArInput* m = &members[i];
    215       Slice base = ar_name_basename(m->name);
    216       char hdr[60];
    217       char name_field[16];
    218       size_t j;
    219 
    220       for (j = 0; j < 16; ++j) name_field[j] = ' ';
    221       if (long_names && ar_name_needs_longtable(base.s, base.len)) {
    222         name_field[0] = '/';
    223         wh_ar_num(name_field + 1, 15, longtab_off);
    224         longtab_off += (uint64_t)base.len + 2;
    225       } else {
    226         size_t emit = base.len > 15 ? 15 : base.len;
    227         for (j = 0; j < emit; ++j) name_field[j] = base.s[j];
    228         name_field[emit] = '/';
    229       }
    230 
    231       ar_fill_header(hdr, name_field, epoch, (uint64_t)m->bytes.len);
    232       wh_bytes(out, hdr, 60);
    233       if (m->bytes.data && m->bytes.len)
    234         wh_bytes(out, m->bytes.data, m->bytes.len);
    235       if (m->bytes.len & 1) wh_bytes(out, &pad, 1);
    236     }
    237   }
    238 
    239   return kit_writer_status(out);
    240 }
    241 
    242 /* ============================================================
    243  * Read (iterator) and list
    244  * ============================================================ */
    245 
    246 #define AR_NAMEBUF 256
    247 
    248 struct KitArIter {
    249   const KitContext* ctx;
    250   const u8* p;
    251   const u8* end;
    252   const u8* longnames;
    253   size_t longnames_len;
    254   char namebuf[AR_NAMEBUF];
    255 };
    256 
    257 static size_t ar_resolve_longname(KitArIter* it, uint64_t off) {
    258   size_t i;
    259   if (!it->longnames) return 0;
    260   if (off >= it->longnames_len) return 0;
    261   for (i = 0; i + 1 < sizeof(it->namebuf); ++i) {
    262     size_t k = (size_t)off + i;
    263     char ch;
    264     if (k >= it->longnames_len) break;
    265     ch = (char)it->longnames[k];
    266     if (ch == '/' || ch == '\n') break;
    267     it->namebuf[i] = ch;
    268   }
    269   it->namebuf[i] = '\0';
    270   return i;
    271 }
    272 
    273 KitStatus kit_ar_iter_new(const KitContext* ctx, const KitSlice* archive,
    274                           KitArIter** out) {
    275   Heap* h;
    276   KitArIter* it;
    277   if (!out) return KIT_INVALID;
    278   if (!ctx || !ctx->heap || !archive) return KIT_INVALID;
    279   if (!archive->data && archive->len) return KIT_INVALID;
    280   if (kit_detect_fmt(archive->data, archive->len) != KIT_BIN_AR)
    281     return KIT_MALFORMED;
    282   h = ctx->heap;
    283   it = (KitArIter*)h->alloc(h, sizeof(*it), _Alignof(KitArIter));
    284   if (!it) return KIT_NOMEM;
    285   it->ctx = ctx;
    286   it->p = archive->data + 8;
    287   it->end = archive->data + archive->len;
    288   it->longnames = NULL;
    289   it->longnames_len = 0;
    290   it->namebuf[0] = '\0';
    291   *out = it;
    292   return KIT_OK;
    293 }
    294 
    295 KitIterResult kit_ar_iter_next(KitArIter* it, KitArMember* out) {
    296   if (!it || !out) return KIT_ITER_ERROR;
    297   for (;;) {
    298     uint64_t size;
    299     size_t avail;
    300     int j;
    301     int namelen;
    302     char name_field[16];
    303 
    304     if (it->p + 60 > it->end) return KIT_ITER_END;
    305 
    306     for (j = 0; j < 16; ++j) name_field[j] = (char)it->p[j];
    307 
    308     size = 0;
    309     for (j = 48; j < 58; ++j) {
    310       char ch = (char)it->p[j];
    311       if (ch < '0' || ch > '9') break;
    312       size = size * 10 + (uint64_t)(unsigned char)(ch - '0');
    313     }
    314 
    315     it->p += 60;
    316     avail = (size_t)(it->end - it->p);
    317     if ((uint64_t)avail < size) return KIT_ITER_END;
    318 
    319     if (name_field[0] == '/' && name_field[1] == '/') {
    320       it->longnames = it->p;
    321       it->longnames_len = (size_t)size;
    322       goto advance;
    323     }
    324     if (name_field[0] == '/' && name_field[1] == ' ') {
    325       goto advance;
    326     }
    327 
    328     if (name_field[0] == '/' && name_field[1] >= '0' && name_field[1] <= '9') {
    329       uint64_t off = 0;
    330       for (j = 1; j < 16; ++j) {
    331         char ch = name_field[j];
    332         if (ch < '0' || ch > '9') break;
    333         off = off * 10 + (uint64_t)(unsigned char)(ch - '0');
    334       }
    335       namelen = (int)ar_resolve_longname(it, off);
    336     } else if (name_field[0] == '#' && name_field[1] == '1' &&
    337                name_field[2] == '/') {
    338       uint64_t nlen = 0;
    339       size_t k;
    340       for (j = 3; j < 16; ++j) {
    341         char ch = name_field[j];
    342         if (ch < '0' || ch > '9') break;
    343         nlen = nlen * 10 + (uint64_t)(unsigned char)(ch - '0');
    344       }
    345       if (nlen > size || nlen + 1 > sizeof(it->namebuf)) return KIT_ITER_ERROR;
    346       namelen = 0;
    347       for (k = 0; k < (size_t)nlen; ++k) {
    348         char ch = (char)it->p[k];
    349         if (ch == '\0') break;
    350         it->namebuf[namelen++] = ch;
    351       }
    352       it->namebuf[namelen] = '\0';
    353       it->p += (size_t)nlen;
    354       size -= nlen;
    355     } else {
    356       namelen = 0;
    357       for (j = 0; j < 16; ++j) {
    358         char ch = name_field[j];
    359         if (ch == '/' || ch == ' ' || ch == '\0') break;
    360         it->namebuf[namelen++] = ch;
    361       }
    362       it->namebuf[namelen] = '\0';
    363     }
    364 
    365     out->name.s = it->namebuf;
    366     out->name.len = namelen;
    367     out->data = it->p;
    368     out->size = (size_t)size;
    369 
    370     it->p += (size_t)size;
    371     if ((size & 1) && it->p < it->end) it->p++;
    372 
    373     if (it->namebuf[0] == '_' && it->namebuf[1] == '_' &&
    374         it->namebuf[2] == '.') {
    375       continue;
    376     }
    377     if (namelen > 0) return KIT_ITER_ITEM;
    378     continue;
    379 
    380   advance:
    381     it->p += (size_t)size;
    382     if ((size & 1) && it->p < it->end) it->p++;
    383   }
    384 }
    385 
    386 void kit_ar_iter_free(KitArIter* it) {
    387   Heap* h;
    388   if (!it) return;
    389   h = it->ctx->heap;
    390   h->free(h, it, sizeof(*it));
    391 }
    392 
    393 KitStatus kit_ar_list(const KitSlice* archive, KitWriter* out) {
    394   /* Iter API requires a context; emulate locally without heap allocation. */
    395   struct KitArIter local;
    396   KitArMember m;
    397 
    398   if (!out) return KIT_INVALID;
    399   if (!archive) return KIT_INVALID;
    400   if (kit_detect_fmt(archive->data, archive->len) != KIT_BIN_AR)
    401     return KIT_MALFORMED;
    402   local.ctx = NULL;
    403   local.p = archive->data + 8;
    404   local.end = archive->data + archive->len;
    405   local.longnames = NULL;
    406   local.longnames_len = 0;
    407   local.namebuf[0] = '\0';
    408 
    409   for (;;) {
    410     KitIterResult r = kit_ar_iter_next(&local, &m);
    411     if (r != KIT_ITER_ITEM) break;
    412     wh_bytes(out, m.name.s, m.name.len);
    413     wh_nl(out);
    414   }
    415 
    416   return kit_writer_status(out);
    417 }