kit

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

manifest.c (28698B)


      1 #include "manifest.h"
      2 
      3 #include <stdio.h>
      4 #include <stdlib.h>
      5 #include <string.h>
      6 
      7 #define DIST_LINE_MAX 1024u
      8 
      9 #define F_NAME 0x01u
     10 #define F_VERSION 0x02u
     11 #define F_HASH 0x04u
     12 #define F_ID 0x08u
     13 #define F_PATH 0x10u
     14 #define F_KIND 0x20u
     15 #define F_BLAKE2B 0x40u
     16 #define F_ROOT 0x80u
     17 #define F_SIZE 0x100u
     18 
     19 typedef enum { SEC_TOP, SEC_ART, SEC_DEP } Section;
     20 
     21 static int emit(KitWriter* out, const char* s) {
     22   return kit_writer_write(out, s, strlen(s)) == KIT_OK ? DIST_OK : DIST_ERR;
     23 }
     24 
     25 static int emit_kv(KitWriter* out, const char* key, const char* val) {
     26   char line[DIST_LINE_MAX];
     27   snprintf(line, sizeof line, "%s = %s\n", key, val);
     28   return emit(out, line);
     29 }
     30 
     31 static int emit_hex(KitWriter* out, const char* key, const uint8_t* h,
     32                     size_t n) {
     33   char hex[2 * DIST_BLAKE2B_LEN + 1];
     34   dist_hex_encode(hex, h, n);
     35   return emit_kv(out, key, hex);
     36 }
     37 
     38 static int emit_u64(KitWriter* out, const char* key, uint64_t v) {
     39   char num[24];
     40   snprintf(num, sizeof num, "%llu", (unsigned long long)v);
     41   return emit_kv(out, key, num);
     42 }
     43 
     44 int dist_manifest_emit(const DistManifest* m, KitWriter* out) {
     45   size_t i;
     46   if (emit(out, DIST_MANIFEST_MAGIC "\n") != DIST_OK) return DIST_ERR;
     47   if (emit_kv(out, "name", m->name) != DIST_OK) return DIST_ERR;
     48   if (emit_kv(out, "version", m->version) != DIST_OK) return DIST_ERR;
     49   if (m->description[0] &&
     50       emit_kv(out, "description", m->description) != DIST_OK)
     51     return DIST_ERR;
     52   if (emit_kv(out, "hash", DIST_MANIFEST_HASH) != DIST_OK) return DIST_ERR;
     53 
     54   for (i = 0; i < m->n_artifacts; ++i) {
     55     const DistArtifact* a = &m->artifacts[i];
     56     if (emit(out, "\n[artifact]\n") != DIST_OK) return DIST_ERR;
     57     if (emit_u64(out, "id", a->id) != DIST_OK) return DIST_ERR;
     58     if (emit_kv(out, "path", a->path) != DIST_OK) return DIST_ERR;
     59     if (a->target[0] && emit_kv(out, "target", a->target) != DIST_OK)
     60       return DIST_ERR;
     61     if (emit_kv(out, "kind", a->kind) != DIST_OK) return DIST_ERR;
     62     if (emit_u64(out, "size", a->size) != DIST_OK) return DIST_ERR;
     63     if (emit_hex(out, "blake2b", a->blake2b, DIST_BLAKE2B_LEN) != DIST_OK)
     64       return DIST_ERR;
     65     if (emit_hex(out, "root", a->root, DIST_BLAKE2B_LEN) != DIST_OK)
     66       return DIST_ERR;
     67     if (a->entry && emit_kv(out, "entry", "true") != DIST_OK) return DIST_ERR;
     68   }
     69 
     70   for (i = 0; i < m->n_deps; ++i) {
     71     const DistDependency* d = &m->deps[i];
     72     if (emit(out, "\n[dependency]\n") != DIST_OK) return DIST_ERR;
     73     if (emit_kv(out, "name", d->name) != DIST_OK) return DIST_ERR;
     74     if (emit_kv(out, "version", d->version) != DIST_OK) return DIST_ERR;
     75     if (d->has_blake2b &&
     76         emit_hex(out, "blake2b", d->blake2b, DIST_BLAKE2B_LEN) != DIST_OK)
     77       return DIST_ERR;
     78     if (d->has_keyid &&
     79         emit_hex(out, "key", d->keyid, DIST_KEYID_LEN) != DIST_OK)
     80       return DIST_ERR;
     81   }
     82   return DIST_OK;
     83 }
     84 
     85 static char* trim_lead(char* s) {
     86   while (*s == ' ' || *s == '\t') ++s;
     87   return s;
     88 }
     89 
     90 static void trim_trail(char* s) {
     91   size_t n = strlen(s);
     92   while (n && (s[n - 1] == ' ' || s[n - 1] == '\t' || s[n - 1] == '\r' ||
     93                s[n - 1] == '\n'))
     94     s[--n] = '\0';
     95 }
     96 
     97 static int set_err(char* err, size_t cap, const char* msg) {
     98   if (err && cap) snprintf(err, cap, "%s", msg);
     99   return DIST_ERR;
    100 }
    101 
    102 static int copy_field(char* dst, size_t cap, const char* src, char* err,
    103                       size_t errcap) {
    104   if (strlen(src) >= cap) return set_err(err, errcap, "field value too long");
    105   snprintf(dst, cap, "%s", src);
    106   return DIST_OK;
    107 }
    108 
    109 static int kind_valid(const char* k) {
    110   return strcmp(k, "exe") == 0 || strcmp(k, "dso") == 0 ||
    111          strcmp(k, "obj") == 0 || strcmp(k, "wasm") == 0 ||
    112          strcmp(k, "lib") == 0 || strcmp(k, "data") == 0 ||
    113          strcmp(k, "source") == 0;
    114 }
    115 
    116 int dist_manifest_path_valid(const char* p) {
    117   size_t start = 0, i;
    118   if (!p[0] || p[0] == '/') return 0;
    119   for (i = 0;; ++i) {
    120     char c = p[i];
    121     if (c == '\\' || c == ':') return 0;
    122     if (c == '/' || c == '\0') {
    123       size_t n = i - start;
    124       if (n == 0) return 0;
    125       if (n == 1 && p[start] == '.') return 0;
    126       if (n == 2 && p[start] == '.' && p[start + 1] == '.') return 0;
    127       if (c == '\0') return 1;
    128       start = i + 1u;
    129     }
    130   }
    131 }
    132 
    133 static int parse_u64(const char* s, uint64_t* out) {
    134   char* end = NULL;
    135   unsigned long long v;
    136   if (!*s) return DIST_ERR;
    137   v = strtoull(s, &end, 10);
    138   if (!end || *end != '\0') return DIST_ERR;
    139   *out = (uint64_t)v;
    140   return DIST_OK;
    141 }
    142 
    143 static int finalize(Section sec, uint32_t seen, char* err, size_t errcap) {
    144   if (sec == SEC_TOP) {
    145     if ((seen & (F_NAME | F_VERSION | F_HASH)) != (F_NAME | F_VERSION | F_HASH))
    146       return set_err(err, errcap, "missing required top-level field");
    147   } else if (sec == SEC_ART) {
    148     if ((seen & (F_ID | F_PATH | F_KIND | F_BLAKE2B | F_ROOT | F_SIZE)) !=
    149         (F_ID | F_PATH | F_KIND | F_BLAKE2B | F_ROOT | F_SIZE))
    150       return set_err(err, errcap, "missing required [artifact] field");
    151   } else {
    152     if ((seen & (F_NAME | F_VERSION)) != (F_NAME | F_VERSION))
    153       return set_err(err, errcap, "missing required [dependency] field");
    154   }
    155   return DIST_OK;
    156 }
    157 
    158 int dist_manifest_parse(const uint8_t* data, size_t len, DistManifest* m,
    159                         char* err, size_t errcap) {
    160   size_t pos = 0;
    161   int first = 1;
    162   Section sec = SEC_TOP;
    163   uint32_t seen = 0;
    164   DistArtifact* art = NULL;
    165   DistDependency* dep = NULL;
    166 
    167   memset(m, 0, sizeof *m);
    168 
    169   while (pos < len) {
    170     char buf[DIST_LINE_MAX];
    171     size_t end = pos;
    172     size_t n;
    173     char *t, *key, *val, *eq;
    174 
    175     while (end < len && data[end] != '\n') ++end;
    176     n = end - pos;
    177     if (n >= sizeof buf) return set_err(err, errcap, "line too long");
    178     memcpy(buf, data + pos, n);
    179     buf[n] = '\0';
    180     pos = (end < len) ? end + 1 : end;
    181     trim_trail(buf);
    182 
    183     if (first) {
    184       first = 0;
    185       if (strcmp(buf, DIST_MANIFEST_MAGIC) != 0)
    186         return set_err(err, errcap, "bad manifest magic/version");
    187       continue;
    188     }
    189 
    190     t = trim_lead(buf);
    191     if (*t == '\0' || *t == '#') continue;
    192 
    193     if (*t == '[') {
    194       if (finalize(sec, seen, err, errcap) != DIST_OK) return DIST_ERR;
    195       seen = 0;
    196       if (strcmp(t, "[artifact]") == 0) {
    197         if (m->n_artifacts >= DIST_MAX_ARTIFACTS)
    198           return set_err(err, errcap, "too many artifacts");
    199         sec = SEC_ART;
    200         art = &m->artifacts[m->n_artifacts++];
    201       } else if (strcmp(t, "[dependency]") == 0) {
    202         if (m->n_deps >= DIST_MAX_DEPS)
    203           return set_err(err, errcap, "too many dependencies");
    204         sec = SEC_DEP;
    205         dep = &m->deps[m->n_deps++];
    206       } else {
    207         return set_err(err, errcap, "unknown section");
    208       }
    209       continue;
    210     }
    211 
    212     eq = strchr(t, '=');
    213     if (!eq) return set_err(err, errcap, "expected key = value");
    214     *eq = '\0';
    215     key = t;
    216     trim_trail(key);
    217     val = trim_lead(eq + 1);
    218 
    219     if (sec == SEC_TOP) {
    220       if (strcmp(key, "name") == 0) {
    221         if (copy_field(m->name, sizeof m->name, val, err, errcap))
    222           return DIST_ERR;
    223         seen |= F_NAME;
    224       } else if (strcmp(key, "version") == 0) {
    225         if (copy_field(m->version, sizeof m->version, val, err, errcap))
    226           return DIST_ERR;
    227         seen |= F_VERSION;
    228       } else if (strcmp(key, "description") == 0) {
    229         if (copy_field(m->description, sizeof m->description, val, err, errcap))
    230           return DIST_ERR;
    231       } else if (strcmp(key, "hash") == 0) {
    232         if (strcmp(val, DIST_MANIFEST_HASH) != 0)
    233           return set_err(err, errcap, "unsupported hash algorithm");
    234         seen |= F_HASH;
    235       } else {
    236         return set_err(err, errcap, "unknown top-level key");
    237       }
    238     } else if (sec == SEC_ART) {
    239       if (strcmp(key, "id") == 0) {
    240         if (parse_u64(val, &art->id) != DIST_OK)
    241           return set_err(err, errcap, "bad artifact id");
    242         seen |= F_ID;
    243       } else if (strcmp(key, "path") == 0) {
    244         if (!dist_manifest_path_valid(val))
    245           return set_err(err, errcap, "unsafe artifact path");
    246         if (copy_field(art->path, sizeof art->path, val, err, errcap))
    247           return DIST_ERR;
    248         seen |= F_PATH;
    249       } else if (strcmp(key, "target") == 0) {
    250         if (copy_field(art->target, sizeof art->target, val, err, errcap))
    251           return DIST_ERR;
    252       } else if (strcmp(key, "kind") == 0) {
    253         if (!kind_valid(val))
    254           return set_err(err, errcap, "unknown artifact kind");
    255         if (copy_field(art->kind, sizeof art->kind, val, err, errcap))
    256           return DIST_ERR;
    257         seen |= F_KIND;
    258       } else if (strcmp(key, "size") == 0) {
    259         if (parse_u64(val, &art->size) != DIST_OK)
    260           return set_err(err, errcap, "bad artifact size");
    261         seen |= F_SIZE;
    262       } else if (strcmp(key, "blake2b") == 0) {
    263         if (strlen(val) != 2 * DIST_BLAKE2B_LEN ||
    264             dist_hex_decode(art->blake2b, val, DIST_BLAKE2B_LEN) != DIST_OK)
    265           return set_err(err, errcap, "bad artifact blake2b");
    266         seen |= F_BLAKE2B;
    267       } else if (strcmp(key, "root") == 0) {
    268         if (strlen(val) != 2 * DIST_BLAKE2B_LEN ||
    269             dist_hex_decode(art->root, val, DIST_BLAKE2B_LEN) != DIST_OK)
    270           return set_err(err, errcap, "bad artifact root");
    271         seen |= F_ROOT;
    272       } else if (strcmp(key, "entry") == 0) {
    273         art->entry = (strcmp(val, "true") == 0);
    274         if (!art->entry && strcmp(val, "false") != 0)
    275           return set_err(err, errcap, "bad entry value");
    276       } else {
    277         return set_err(err, errcap, "unknown [artifact] key");
    278       }
    279     } else {
    280       if (strcmp(key, "name") == 0) {
    281         if (copy_field(dep->name, sizeof dep->name, val, err, errcap))
    282           return DIST_ERR;
    283         seen |= F_NAME;
    284       } else if (strcmp(key, "version") == 0) {
    285         if (copy_field(dep->version, sizeof dep->version, val, err, errcap))
    286           return DIST_ERR;
    287         seen |= F_VERSION;
    288       } else if (strcmp(key, "blake2b") == 0) {
    289         if (strlen(val) != 2 * DIST_BLAKE2B_LEN ||
    290             dist_hex_decode(dep->blake2b, val, DIST_BLAKE2B_LEN) != DIST_OK)
    291           return set_err(err, errcap, "bad dependency blake2b");
    292         dep->has_blake2b = 1;
    293       } else if (strcmp(key, "key") == 0) {
    294         if (strlen(val) != 2 * DIST_KEYID_LEN ||
    295             dist_hex_decode(dep->keyid, val, DIST_KEYID_LEN) != DIST_OK)
    296           return set_err(err, errcap, "bad dependency key id");
    297         dep->has_keyid = 1;
    298       } else {
    299         return set_err(err, errcap, "unknown [dependency] key");
    300       }
    301     }
    302   }
    303 
    304   if (finalize(sec, seen, err, errcap) != DIST_OK) return DIST_ERR;
    305   {
    306     size_t i, j;
    307     for (i = 0; i < m->n_artifacts; ++i) {
    308       for (j = i + 1u; j < m->n_artifacts; ++j) {
    309         if (m->artifacts[i].id == m->artifacts[j].id)
    310           return set_err(err, errcap, "duplicate artifact id");
    311         if (strcmp(m->artifacts[i].path, m->artifacts[j].path) == 0)
    312           return set_err(err, errcap, "duplicate artifact path");
    313       }
    314     }
    315   }
    316   return DIST_OK;
    317 }
    318 
    319 #define P3_F_NAME 0x00000001u
    320 #define P3_F_VERSION 0x00000002u
    321 #define P3_F_DESCRIPTION 0x00000004u
    322 #define P3_F_HASH 0x00000008u
    323 #define P3_F_TREE_FORMAT 0x00000010u
    324 #define P3_F_BLOB_FORMAT 0x00000020u
    325 #define P3_F_ID 0x00000040u
    326 #define P3_F_OUTPUT_NAME 0x00000080u
    327 #define P3_F_TREE_ID 0x00000100u
    328 #define P3_F_TARGET 0x00000200u
    329 #define P3_F_DEFAULT 0x00000400u
    330 #define P3_F_OUTPUT 0x00000800u
    331 #define P3_F_PATH 0x00001000u
    332 #define P3_F_KIND 0x00002000u
    333 #define P3_F_ENTRY 0x00004000u
    334 #define P3_F_PACKAGE 0x00008000u
    335 #define P3_F_KEY 0x00010000u
    336 
    337 typedef enum {
    338   P3_SEC_TOP,
    339   P3_SEC_OUTPUT,
    340   P3_SEC_ARTIFACT,
    341   P3_SEC_DEPENDENCY
    342 } PackageSection;
    343 
    344 static int field_text_valid(const char* s, int required) {
    345   if (required && !s[0]) return 0;
    346   for (; *s; ++s) {
    347     if (*s == '\n' || *s == '\r') return 0;
    348   }
    349   return 1;
    350 }
    351 
    352 static int parse_bool3(const char* s, int* out) {
    353   if (strcmp(s, "true") == 0) {
    354     *out = 1;
    355     return DIST_OK;
    356   }
    357   if (strcmp(s, "false") == 0) {
    358     *out = 0;
    359     return DIST_OK;
    360   }
    361   return DIST_ERR;
    362 }
    363 
    364 static int parse_u64_dec3(const char* s, uint64_t* out) {
    365   uint64_t v = 0;
    366   if (!*s) return DIST_ERR;
    367   for (; *s; ++s) {
    368     unsigned digit;
    369     if (*s < '0' || *s > '9') return DIST_ERR;
    370     digit = (unsigned)(*s - '0');
    371     if (v > (UINT64_MAX - (uint64_t)digit) / 10u) return DIST_ERR;
    372     v = v * 10u + (uint64_t)digit;
    373   }
    374   *out = v;
    375   return DIST_OK;
    376 }
    377 
    378 static int decode_hash3(uint8_t out[DIST_BLAKE2B_LEN], const char* val,
    379                         const char* err_msg, char* err, size_t errcap) {
    380   if (strlen(val) != 2u * DIST_BLAKE2B_LEN ||
    381       dist_hex_decode(out, val, DIST_BLAKE2B_LEN) != DIST_OK)
    382     return set_err(err, errcap, err_msg);
    383   return DIST_OK;
    384 }
    385 
    386 static int decode_keyid3(uint8_t out[DIST_KEYID_LEN], const char* val,
    387                          char* err, size_t errcap) {
    388   if (strlen(val) != 2u * DIST_KEYID_LEN ||
    389       dist_hex_decode(out, val, DIST_KEYID_LEN) != DIST_OK)
    390     return set_err(err, errcap, "bad dependency key id");
    391   return DIST_OK;
    392 }
    393 
    394 static int seen_once3(uint32_t* seen, uint32_t bit, const char* msg, char* err,
    395                       size_t errcap) {
    396   if (*seen & bit) return set_err(err, errcap, msg);
    397   *seen |= bit;
    398   return DIST_OK;
    399 }
    400 
    401 static int find_output3(const DistPackageManifest* m, uint64_t id) {
    402   size_t i;
    403   for (i = 0; i < m->n_outputs; ++i) {
    404     if (m->outputs[i].id == id) return (int)i;
    405   }
    406   return -1;
    407 }
    408 
    409 static int finalize_package_section3(PackageSection sec, uint32_t seen,
    410                                      char* err, size_t errcap) {
    411   if (sec == P3_SEC_TOP) {
    412     if ((seen & (P3_F_NAME | P3_F_VERSION | P3_F_HASH | P3_F_TREE_FORMAT |
    413                  P3_F_BLOB_FORMAT)) != (P3_F_NAME | P3_F_VERSION | P3_F_HASH |
    414                                         P3_F_TREE_FORMAT | P3_F_BLOB_FORMAT))
    415       return set_err(err, errcap, "missing required top-level field");
    416   } else if (sec == P3_SEC_OUTPUT) {
    417     if ((seen & (P3_F_ID | P3_F_OUTPUT_NAME | P3_F_TREE_ID)) !=
    418         (P3_F_ID | P3_F_OUTPUT_NAME | P3_F_TREE_ID))
    419       return set_err(err, errcap, "missing required [output] field");
    420   } else if (sec == P3_SEC_ARTIFACT) {
    421     if ((seen & (P3_F_OUTPUT | P3_F_PATH | P3_F_KIND)) !=
    422         (P3_F_OUTPUT | P3_F_PATH | P3_F_KIND))
    423       return set_err(err, errcap, "missing required [artifact] field");
    424   } else {
    425     if ((seen & (P3_F_NAME | P3_F_VERSION)) != (P3_F_NAME | P3_F_VERSION))
    426       return set_err(err, errcap, "missing required [dependency] field");
    427   }
    428   return DIST_OK;
    429 }
    430 
    431 int dist_package_manifest_validate(const DistPackageManifest* m, char* err,
    432                                    size_t errcap) {
    433   size_t i, j;
    434   size_t default_outputs = 0;
    435 
    436   if (!field_text_valid(m->name, 1) || !field_text_valid(m->version, 1) ||
    437       !field_text_valid(m->description, 0))
    438     return set_err(err, errcap, "bad package string field");
    439   if (m->n_outputs == 0) return set_err(err, errcap, "missing [output]");
    440   if (m->n_outputs > DIST_MAX_OUTPUTS)
    441     return set_err(err, errcap, "too many outputs");
    442   if (m->n_artifacts > DIST_MAX_ARTIFACTS)
    443     return set_err(err, errcap, "too many artifacts");
    444   if (m->n_deps > DIST_MAX_DEPS)
    445     return set_err(err, errcap, "too many dependencies");
    446 
    447   for (i = 0; i < m->n_outputs; ++i) {
    448     const DistPackageOutput* out = &m->outputs[i];
    449     if (!field_text_valid(out->name, 1) || !field_text_valid(out->target, 0))
    450       return set_err(err, errcap, "bad output string field");
    451     if (out->is_default) ++default_outputs;
    452     for (j = i + 1u; j < m->n_outputs; ++j) {
    453       if (out->id == m->outputs[j].id)
    454         return set_err(err, errcap, "duplicate output id");
    455     }
    456   }
    457   if (default_outputs > 1u)
    458     return set_err(err, errcap, "duplicate default output");
    459 
    460   for (i = 0; i < m->n_artifacts; ++i) {
    461     const DistPackageArtifact* art = &m->artifacts[i];
    462     if (find_output3(m, art->output_id) < 0)
    463       return set_err(err, errcap, "artifact references unknown output");
    464     if (!field_text_valid(art->path, 1) || !dist_manifest_path_valid(art->path))
    465       return set_err(err, errcap, "unsafe artifact path");
    466     if (!kind_valid(art->kind))
    467       return set_err(err, errcap, "unknown artifact kind");
    468     for (j = i + 1u; j < m->n_artifacts; ++j) {
    469       const DistPackageArtifact* other = &m->artifacts[j];
    470       if (art->output_id == other->output_id &&
    471           strcmp(art->path, other->path) == 0)
    472         return set_err(err, errcap, "duplicate artifact path");
    473     }
    474   }
    475 
    476   for (i = 0; i < m->n_deps; ++i) {
    477     const DistPackageDependency* dep = &m->deps[i];
    478     if (!field_text_valid(dep->name, 1) || !field_text_valid(dep->version, 1))
    479       return set_err(err, errcap, "bad dependency string field");
    480   }
    481 
    482   return DIST_OK;
    483 }
    484 
    485 int dist_package_manifest_emit(const DistPackageManifest* m, KitWriter* out) {
    486   size_t i;
    487   char err[128];
    488 
    489   if (dist_package_manifest_validate(m, err, sizeof err) != DIST_OK)
    490     return DIST_ERR;
    491 
    492   if (emit(out, DIST_PACKAGE3_MAGIC "\n") != DIST_OK) return DIST_ERR;
    493   if (emit_kv(out, "name", m->name) != DIST_OK) return DIST_ERR;
    494   if (emit_kv(out, "version", m->version) != DIST_OK) return DIST_ERR;
    495   if (m->description[0] &&
    496       emit_kv(out, "description", m->description) != DIST_OK)
    497     return DIST_ERR;
    498   if (emit_kv(out, "hash", DIST_PACKAGE3_HASH) != DIST_OK) return DIST_ERR;
    499   if (emit_kv(out, "tree", DIST_PACKAGE3_TREE_FORMAT) != DIST_OK)
    500     return DIST_ERR;
    501   if (emit_kv(out, "blob", DIST_PACKAGE3_BLOB_FORMAT) != DIST_OK)
    502     return DIST_ERR;
    503 
    504   for (i = 0; i < m->n_outputs; ++i) {
    505     const DistPackageOutput* pkg_out = &m->outputs[i];
    506     if (emit(out, "\n[output]\n") != DIST_OK) return DIST_ERR;
    507     if (emit_u64(out, "id", pkg_out->id) != DIST_OK) return DIST_ERR;
    508     if (emit_kv(out, "name", pkg_out->name) != DIST_OK) return DIST_ERR;
    509     if (emit_hex(out, "tree", pkg_out->tree, DIST_BLAKE2B_LEN) != DIST_OK)
    510       return DIST_ERR;
    511     if (pkg_out->target[0] &&
    512         emit_kv(out, "target", pkg_out->target) != DIST_OK)
    513       return DIST_ERR;
    514     if (pkg_out->is_default && emit_kv(out, "default", "true") != DIST_OK)
    515       return DIST_ERR;
    516   }
    517 
    518   for (i = 0; i < m->n_artifacts; ++i) {
    519     const DistPackageArtifact* art = &m->artifacts[i];
    520     if (emit(out, "\n[artifact]\n") != DIST_OK) return DIST_ERR;
    521     if (emit_u64(out, "output", art->output_id) != DIST_OK) return DIST_ERR;
    522     if (emit_kv(out, "path", art->path) != DIST_OK) return DIST_ERR;
    523     if (emit_kv(out, "kind", art->kind) != DIST_OK) return DIST_ERR;
    524     if (art->entry && emit_kv(out, "entry", "true") != DIST_OK) return DIST_ERR;
    525   }
    526 
    527   for (i = 0; i < m->n_deps; ++i) {
    528     const DistPackageDependency* dep = &m->deps[i];
    529     if (emit(out, "\n[dependency]\n") != DIST_OK) return DIST_ERR;
    530     if (emit_kv(out, "name", dep->name) != DIST_OK) return DIST_ERR;
    531     if (emit_kv(out, "version", dep->version) != DIST_OK) return DIST_ERR;
    532     if (dep->has_package &&
    533         emit_hex(out, "package", dep->package, DIST_BLAKE2B_LEN) != DIST_OK)
    534       return DIST_ERR;
    535     if (dep->has_keyid &&
    536         emit_hex(out, "key", dep->keyid, DIST_KEYID_LEN) != DIST_OK)
    537       return DIST_ERR;
    538   }
    539 
    540   return DIST_OK;
    541 }
    542 
    543 int dist_package_manifest_parse(const uint8_t* data, size_t len,
    544                                 DistPackageManifest* m, char* err,
    545                                 size_t errcap) {
    546   size_t pos = 0;
    547   int first = 1;
    548   PackageSection sec = P3_SEC_TOP;
    549   uint32_t seen = 0;
    550   DistPackageOutput* pkg_out = NULL;
    551   DistPackageArtifact* art = NULL;
    552   DistPackageDependency* dep = NULL;
    553 
    554   memset(m, 0, sizeof *m);
    555 
    556   while (pos < len) {
    557     char buf[DIST_LINE_MAX];
    558     size_t end = pos;
    559     size_t n;
    560     char *t, *key, *val, *eq;
    561 
    562     while (end < len && data[end] != '\n') ++end;
    563     n = end - pos;
    564     if (n >= sizeof buf) return set_err(err, errcap, "line too long");
    565     memcpy(buf, data + pos, n);
    566     buf[n] = '\0';
    567     pos = (end < len) ? end + 1 : end;
    568     trim_trail(buf);
    569 
    570     if (first) {
    571       first = 0;
    572       if (strcmp(buf, DIST_PACKAGE3_MAGIC) != 0)
    573         return set_err(err, errcap, "bad package manifest magic/version");
    574       continue;
    575     }
    576 
    577     t = trim_lead(buf);
    578     if (*t == '\0' || *t == '#') continue;
    579 
    580     if (*t == '[') {
    581       if (finalize_package_section3(sec, seen, err, errcap) != DIST_OK)
    582         return DIST_ERR;
    583       seen = 0;
    584       if (strcmp(t, "[output]") == 0) {
    585         if (m->n_outputs >= DIST_MAX_OUTPUTS)
    586           return set_err(err, errcap, "too many outputs");
    587         sec = P3_SEC_OUTPUT;
    588         pkg_out = &m->outputs[m->n_outputs++];
    589       } else if (strcmp(t, "[artifact]") == 0) {
    590         if (m->n_artifacts >= DIST_MAX_ARTIFACTS)
    591           return set_err(err, errcap, "too many artifacts");
    592         sec = P3_SEC_ARTIFACT;
    593         art = &m->artifacts[m->n_artifacts++];
    594       } else if (strcmp(t, "[dependency]") == 0) {
    595         if (m->n_deps >= DIST_MAX_DEPS)
    596           return set_err(err, errcap, "too many dependencies");
    597         sec = P3_SEC_DEPENDENCY;
    598         dep = &m->deps[m->n_deps++];
    599       } else {
    600         return set_err(err, errcap, "unknown section");
    601       }
    602       continue;
    603     }
    604 
    605     eq = strchr(t, '=');
    606     if (!eq) return set_err(err, errcap, "expected key = value");
    607     *eq = '\0';
    608     key = t;
    609     trim_trail(key);
    610     val = trim_lead(eq + 1);
    611 
    612     if (sec == P3_SEC_TOP) {
    613       if (strcmp(key, "name") == 0) {
    614         if (seen_once3(&seen, P3_F_NAME, "duplicate top-level key", err,
    615                        errcap) != DIST_OK)
    616           return DIST_ERR;
    617         if (!field_text_valid(val, 1))
    618           return set_err(err, errcap, "bad package name");
    619         if (copy_field(m->name, sizeof m->name, val, err, errcap))
    620           return DIST_ERR;
    621       } else if (strcmp(key, "version") == 0) {
    622         if (seen_once3(&seen, P3_F_VERSION, "duplicate top-level key", err,
    623                        errcap) != DIST_OK)
    624           return DIST_ERR;
    625         if (!field_text_valid(val, 1))
    626           return set_err(err, errcap, "bad package version");
    627         if (copy_field(m->version, sizeof m->version, val, err, errcap))
    628           return DIST_ERR;
    629       } else if (strcmp(key, "description") == 0) {
    630         if (seen_once3(&seen, P3_F_DESCRIPTION, "duplicate top-level key", err,
    631                        errcap) != DIST_OK)
    632           return DIST_ERR;
    633         if (!field_text_valid(val, 0))
    634           return set_err(err, errcap, "bad package description");
    635         if (copy_field(m->description, sizeof m->description, val, err, errcap))
    636           return DIST_ERR;
    637       } else if (strcmp(key, "hash") == 0) {
    638         if (seen_once3(&seen, P3_F_HASH, "duplicate top-level key", err,
    639                        errcap) != DIST_OK)
    640           return DIST_ERR;
    641         if (strcmp(val, DIST_PACKAGE3_HASH) != 0)
    642           return set_err(err, errcap, "unsupported hash algorithm");
    643       } else if (strcmp(key, "tree") == 0) {
    644         if (seen_once3(&seen, P3_F_TREE_FORMAT, "duplicate top-level key", err,
    645                        errcap) != DIST_OK)
    646           return DIST_ERR;
    647         if (strcmp(val, DIST_PACKAGE3_TREE_FORMAT) != 0)
    648           return set_err(err, errcap, "unsupported tree format");
    649       } else if (strcmp(key, "blob") == 0) {
    650         if (seen_once3(&seen, P3_F_BLOB_FORMAT, "duplicate top-level key", err,
    651                        errcap) != DIST_OK)
    652           return DIST_ERR;
    653         if (strcmp(val, DIST_PACKAGE3_BLOB_FORMAT) != 0)
    654           return set_err(err, errcap, "unsupported blob format");
    655       } else {
    656         return set_err(err, errcap, "unknown top-level key");
    657       }
    658     } else if (sec == P3_SEC_OUTPUT) {
    659       if (strcmp(key, "id") == 0) {
    660         if (seen_once3(&seen, P3_F_ID, "duplicate [output] key", err, errcap) !=
    661             DIST_OK)
    662           return DIST_ERR;
    663         if (parse_u64_dec3(val, &pkg_out->id) != DIST_OK)
    664           return set_err(err, errcap, "bad output id");
    665       } else if (strcmp(key, "name") == 0) {
    666         if (seen_once3(&seen, P3_F_OUTPUT_NAME, "duplicate [output] key", err,
    667                        errcap) != DIST_OK)
    668           return DIST_ERR;
    669         if (!field_text_valid(val, 1))
    670           return set_err(err, errcap, "bad output name");
    671         if (copy_field(pkg_out->name, sizeof pkg_out->name, val, err, errcap))
    672           return DIST_ERR;
    673       } else if (strcmp(key, "tree") == 0) {
    674         if (seen_once3(&seen, P3_F_TREE_ID, "duplicate [output] key", err,
    675                        errcap) != DIST_OK)
    676           return DIST_ERR;
    677         if (decode_hash3(pkg_out->tree, val, "bad output tree id", err,
    678                          errcap) != DIST_OK)
    679           return DIST_ERR;
    680       } else if (strcmp(key, "target") == 0) {
    681         if (seen_once3(&seen, P3_F_TARGET, "duplicate [output] key", err,
    682                        errcap) != DIST_OK)
    683           return DIST_ERR;
    684         if (!field_text_valid(val, 0))
    685           return set_err(err, errcap, "bad output target");
    686         if (copy_field(pkg_out->target, sizeof pkg_out->target, val, err,
    687                        errcap))
    688           return DIST_ERR;
    689       } else if (strcmp(key, "default") == 0) {
    690         if (seen_once3(&seen, P3_F_DEFAULT, "duplicate [output] key", err,
    691                        errcap) != DIST_OK)
    692           return DIST_ERR;
    693         if (parse_bool3(val, &pkg_out->is_default) != DIST_OK)
    694           return set_err(err, errcap, "bad default value");
    695       } else {
    696         return set_err(err, errcap, "unknown [output] key");
    697       }
    698     } else if (sec == P3_SEC_ARTIFACT) {
    699       if (strcmp(key, "output") == 0) {
    700         if (seen_once3(&seen, P3_F_OUTPUT, "duplicate [artifact] key", err,
    701                        errcap) != DIST_OK)
    702           return DIST_ERR;
    703         if (parse_u64_dec3(val, &art->output_id) != DIST_OK)
    704           return set_err(err, errcap, "bad artifact output id");
    705       } else if (strcmp(key, "path") == 0) {
    706         if (seen_once3(&seen, P3_F_PATH, "duplicate [artifact] key", err,
    707                        errcap) != DIST_OK)
    708           return DIST_ERR;
    709         if (!dist_manifest_path_valid(val))
    710           return set_err(err, errcap, "unsafe artifact path");
    711         if (copy_field(art->path, sizeof art->path, val, err, errcap))
    712           return DIST_ERR;
    713       } else if (strcmp(key, "kind") == 0) {
    714         if (seen_once3(&seen, P3_F_KIND, "duplicate [artifact] key", err,
    715                        errcap) != DIST_OK)
    716           return DIST_ERR;
    717         if (!kind_valid(val))
    718           return set_err(err, errcap, "unknown artifact kind");
    719         if (copy_field(art->kind, sizeof art->kind, val, err, errcap))
    720           return DIST_ERR;
    721       } else if (strcmp(key, "entry") == 0) {
    722         if (seen_once3(&seen, P3_F_ENTRY, "duplicate [artifact] key", err,
    723                        errcap) != DIST_OK)
    724           return DIST_ERR;
    725         if (parse_bool3(val, &art->entry) != DIST_OK)
    726           return set_err(err, errcap, "bad entry value");
    727       } else {
    728         return set_err(err, errcap, "unknown [artifact] key");
    729       }
    730     } else {
    731       if (strcmp(key, "name") == 0) {
    732         if (seen_once3(&seen, P3_F_NAME, "duplicate [dependency] key", err,
    733                        errcap) != DIST_OK)
    734           return DIST_ERR;
    735         if (!field_text_valid(val, 1))
    736           return set_err(err, errcap, "bad dependency name");
    737         if (copy_field(dep->name, sizeof dep->name, val, err, errcap))
    738           return DIST_ERR;
    739       } else if (strcmp(key, "version") == 0) {
    740         if (seen_once3(&seen, P3_F_VERSION, "duplicate [dependency] key", err,
    741                        errcap) != DIST_OK)
    742           return DIST_ERR;
    743         if (!field_text_valid(val, 1))
    744           return set_err(err, errcap, "bad dependency version");
    745         if (copy_field(dep->version, sizeof dep->version, val, err, errcap))
    746           return DIST_ERR;
    747       } else if (strcmp(key, "package") == 0) {
    748         if (seen_once3(&seen, P3_F_PACKAGE, "duplicate [dependency] key", err,
    749                        errcap) != DIST_OK)
    750           return DIST_ERR;
    751         if (decode_hash3(dep->package, val, "bad dependency package id", err,
    752                          errcap) != DIST_OK)
    753           return DIST_ERR;
    754         dep->has_package = 1;
    755       } else if (strcmp(key, "key") == 0) {
    756         if (seen_once3(&seen, P3_F_KEY, "duplicate [dependency] key", err,
    757                        errcap) != DIST_OK)
    758           return DIST_ERR;
    759         if (decode_keyid3(dep->keyid, val, err, errcap) != DIST_OK)
    760           return DIST_ERR;
    761         dep->has_keyid = 1;
    762       } else {
    763         return set_err(err, errcap, "unknown [dependency] key");
    764       }
    765     }
    766   }
    767 
    768   if (first) return set_err(err, errcap, "bad package manifest magic/version");
    769   if (finalize_package_section3(sec, seen, err, errcap) != DIST_OK)
    770     return DIST_ERR;
    771   return dist_package_manifest_validate(m, err, errcap);
    772 }