kit

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

cfg.c (10570B)


      1 #include "cfg.h"
      2 
      3 #include <stdio.h>
      4 #include <string.h>
      5 
      6 static int set_err(char* err, size_t cap, const char* msg) {
      7   if (err && cap) snprintf(err, cap, "%s", msg);
      8   return BUILD_ERR;
      9 }
     10 
     11 static int write_bytes(KitWriter* out, const void* data, size_t n) {
     12   return out && kit_writer_write(out, data, n) == 0 ? BUILD_OK : BUILD_ERR;
     13 }
     14 
     15 static int write_cstr(KitWriter* out, const char* s) {
     16   return write_bytes(out, s, strlen(s));
     17 }
     18 
     19 static int slice_text_valid(KitSlice s, size_t cap, int allow_space) {
     20   size_t i;
     21   if (s.len >= cap) return 0;
     22   if (s.len && !s.s) return 0;
     23   for (i = 0; i < s.len; ++i) {
     24     unsigned char c = (unsigned char)s.s[i];
     25     if (c == '\0' || c == '\n' || c == '\r') return 0;
     26     if (!allow_space && (c == ' ' || c == '\t')) return 0;
     27   }
     28   return 1;
     29 }
     30 
     31 static int cstr_text_valid(const char* s, size_t cap, int allow_space) {
     32   size_t i;
     33   if (!s) return 0;
     34   for (i = 0; i < cap; ++i) {
     35     unsigned char c = (unsigned char)s[i];
     36     if (c == '\0') return 1;
     37     if (c == '\n' || c == '\r') return 0;
     38     if (!allow_space && (c == ' ' || c == '\t')) return 0;
     39   }
     40   return 0;
     41 }
     42 
     43 static int slice_cstr_cmp(KitSlice a, const char* b) {
     44   size_t i = 0;
     45   for (;;) {
     46     int ac = i < a.len ? (int)(unsigned char)a.s[i] : 0;
     47     int bc = b[i] ? (int)(unsigned char)b[i] : 0;
     48     if (ac != bc) return ac < bc ? -1 : 1;
     49     if (!ac) return 0;
     50     ++i;
     51   }
     52 }
     53 
     54 static int cstr_cmp(const char* a, const char* b) {
     55   int c = strcmp(a, b);
     56   return c < 0 ? -1 : (c > 0 ? 1 : 0);
     57 }
     58 
     59 static void copy_slice(char* dst, size_t cap, KitSlice s) {
     60   (void)cap;
     61   if (s.len) memcpy(dst, s.s, s.len);
     62   dst[s.len] = '\0';
     63 }
     64 
     65 void build_config_init(BuildConfig* cfg, BuildConfigEntry* storage,
     66                        size_t cap) {
     67   if (!cfg) return;
     68   cfg->entries = storage;
     69   cfg->n = 0;
     70   cfg->cap = cap;
     71 }
     72 
     73 void build_argv_init(BuildArgv* argv, char (*storage)[BUILD_VAL_MAX],
     74                      size_t cap) {
     75   if (!argv) return;
     76   argv->args = storage;
     77   argv->n = 0;
     78   argv->cap = cap;
     79 }
     80 
     81 int build_config_set(BuildConfig* cfg, KitSlice key, KitSlice value) {
     82   size_t lo, hi, pos, i;
     83   int cmp = 0;
     84 
     85   if (!cfg || (cfg->cap && !cfg->entries)) return BUILD_ERR;
     86   if (!slice_text_valid(key, BUILD_KEY_MAX, 0) ||
     87       !slice_text_valid(value, BUILD_VAL_MAX, 1) || key.len == 0)
     88     return BUILD_ERR;
     89 
     90   lo = 0;
     91   hi = cfg->n;
     92   while (lo < hi) {
     93     size_t mid = lo + (hi - lo) / 2u;
     94     cmp = slice_cstr_cmp(key, cfg->entries[mid].key);
     95     if (cmp > 0)
     96       lo = mid + 1u;
     97     else
     98       hi = mid;
     99   }
    100   pos = lo;
    101   if (pos < cfg->n && slice_cstr_cmp(key, cfg->entries[pos].key) == 0) {
    102     copy_slice(cfg->entries[pos].value, sizeof cfg->entries[pos].value, value);
    103     return BUILD_OK;
    104   }
    105   if (cfg->n >= cfg->cap) return BUILD_ERR;
    106   for (i = cfg->n; i > pos; --i) cfg->entries[i] = cfg->entries[i - 1u];
    107   copy_slice(cfg->entries[pos].key, sizeof cfg->entries[pos].key, key);
    108   copy_slice(cfg->entries[pos].value, sizeof cfg->entries[pos].value, value);
    109   ++cfg->n;
    110   return BUILD_OK;
    111 }
    112 
    113 int build_argv_set(BuildArgv* out, const KitSlice* args, size_t n) {
    114   size_t i;
    115   if (!out || (out->cap && !out->args)) return BUILD_ERR;
    116   if (n > out->cap) return BUILD_ERR;
    117   if (n && !args) return BUILD_ERR;
    118   out->n = 0;
    119   for (i = 0; i < n; ++i) {
    120     if (!slice_text_valid(args[i], BUILD_VAL_MAX, 1)) return BUILD_ERR;
    121     copy_slice(out->args[out->n], BUILD_VAL_MAX, args[i]);
    122     ++out->n;
    123   }
    124   return BUILD_OK;
    125 }
    126 
    127 int build_config_get(const BuildConfig* cfg, KitSlice key, KitSlice* value,
    128                      int* present) {
    129   size_t lo, hi;
    130   if (!cfg || !present || !slice_text_valid(key, BUILD_KEY_MAX, 0) ||
    131       key.len == 0)
    132     return BUILD_ERR;
    133   lo = 0;
    134   hi = cfg->n;
    135   while (lo < hi) {
    136     size_t mid = lo + (hi - lo) / 2u;
    137     int cmp = slice_cstr_cmp(key, cfg->entries[mid].key);
    138     if (cmp == 0) {
    139       if (value) {
    140         value->s = cfg->entries[mid].value;
    141         value->len = strlen(cfg->entries[mid].value);
    142       }
    143       *present = 1;
    144       return BUILD_OK;
    145     }
    146     if (cmp > 0)
    147       lo = mid + 1u;
    148     else
    149       hi = mid;
    150   }
    151   if (value) *value = KIT_SLICE_NULL;
    152   *present = 0;
    153   return BUILD_OK;
    154 }
    155 
    156 int build_config_overlay(const BuildConfig* base, const KitBuildKV* overrides,
    157                          size_t noverrides, BuildConfig* out) {
    158   size_t i;
    159   if (!base || !out || (base->n && !base->entries)) return BUILD_ERR;
    160   if (noverrides && !overrides) return BUILD_ERR;
    161   out->n = 0;
    162   for (i = 0; i < base->n; ++i) {
    163     if (build_config_set(out, kit_slice_cstr(base->entries[i].key),
    164                          kit_slice_cstr(base->entries[i].value)) != BUILD_OK)
    165       return BUILD_ERR;
    166   }
    167   for (i = 0; i < noverrides; ++i)
    168     if (build_config_set(out, overrides[i].key, overrides[i].value) != BUILD_OK)
    169       return BUILD_ERR;
    170   return BUILD_OK;
    171 }
    172 
    173 int build_config_overlay_map(const BuildConfig* base,
    174                              const BuildConfig* overrides, BuildConfig* out) {
    175   size_t i;
    176   if (!overrides || (overrides->n && !overrides->entries)) return BUILD_ERR;
    177   if (build_config_overlay(base, NULL, 0, out) != BUILD_OK) return BUILD_ERR;
    178   for (i = 0; i < overrides->n; ++i) {
    179     if (build_config_set(out, kit_slice_cstr(overrides->entries[i].key),
    180                          kit_slice_cstr(overrides->entries[i].value)) !=
    181         BUILD_OK)
    182       return BUILD_ERR;
    183   }
    184   return BUILD_OK;
    185 }
    186 
    187 static int config_validate(const BuildConfig* cfg) {
    188   size_t i;
    189   if (!cfg || (cfg->n && !cfg->entries)) return BUILD_ERR;
    190   for (i = 0; i < cfg->n; ++i) {
    191     if (!cstr_text_valid(cfg->entries[i].key, BUILD_KEY_MAX, 0) ||
    192         !cstr_text_valid(cfg->entries[i].value, BUILD_VAL_MAX, 1) ||
    193         cfg->entries[i].key[0] == '\0')
    194       return BUILD_ERR;
    195     if (i > 0 && cstr_cmp(cfg->entries[i - 1u].key, cfg->entries[i].key) >= 0)
    196       return BUILD_ERR;
    197   }
    198   return BUILD_OK;
    199 }
    200 
    201 int build_config_emit(const BuildConfig* cfg, KitWriter* out) {
    202   size_t i;
    203   if (config_validate(cfg) != BUILD_OK) return BUILD_ERR;
    204   if (write_cstr(out, BUILD_CONFIG_MAGIC "\n") != BUILD_OK) return BUILD_ERR;
    205   for (i = 0; i < cfg->n; ++i) {
    206     if (write_cstr(out, cfg->entries[i].key) != BUILD_OK ||
    207         write_cstr(out, " ") != BUILD_OK ||
    208         write_cstr(out, cfg->entries[i].value) != BUILD_OK ||
    209         write_cstr(out, "\n") != BUILD_OK)
    210       return BUILD_ERR;
    211   }
    212   return kit_writer_status(out) == 0 ? BUILD_OK : BUILD_ERR;
    213 }
    214 
    215 int build_argv_emit(const BuildArgv* argv, KitWriter* out) {
    216   size_t i;
    217   if (!argv || (argv->n && !argv->args)) return BUILD_ERR;
    218   if (write_cstr(out, BUILD_ARGV_MAGIC "\n") != BUILD_OK) return BUILD_ERR;
    219   for (i = 0; i < argv->n; ++i) {
    220     if (!cstr_text_valid(argv->args[i], BUILD_VAL_MAX, 1)) return BUILD_ERR;
    221     if (write_cstr(out, argv->args[i]) != BUILD_OK ||
    222         write_cstr(out, "\n") != BUILD_OK)
    223       return BUILD_ERR;
    224   }
    225   return kit_writer_status(out) == 0 ? BUILD_OK : BUILD_ERR;
    226 }
    227 
    228 static int next_line(const uint8_t* data, size_t len, size_t* pos,
    229                      KitSlice* line) {
    230   size_t start = *pos;
    231   size_t end = start;
    232   while (end < len && data[end] != '\n') ++end;
    233   if (end == len) return BUILD_ERR;
    234   line->data = data + start;
    235   line->len = end - start;
    236   *pos = end + 1u;
    237   return BUILD_OK;
    238 }
    239 
    240 int build_config_parse(const uint8_t* data, size_t len, BuildConfig* out,
    241                        char* err, size_t errcap) {
    242   size_t pos = 0;
    243   KitSlice line;
    244   char prev[BUILD_KEY_MAX];
    245   int have_prev = 0;
    246 
    247   if (!data || !out) return set_err(err, errcap, "missing config");
    248   out->n = 0;
    249   if (next_line(data, len, &pos, &line) != BUILD_OK ||
    250       !kit_slice_eq_cstr(line, BUILD_CONFIG_MAGIC))
    251     return set_err(err, errcap, "bad config magic/version");
    252   while (pos < len) {
    253     size_t i;
    254     KitSlice key, value;
    255     if (next_line(data, len, &pos, &line) != BUILD_OK)
    256       return set_err(err, errcap, "unterminated config line");
    257     for (i = 0; i < line.len && line.s[i] != ' '; ++i) {
    258     }
    259     if (i == 0 || i == line.len)
    260       return set_err(err, errcap, "expected config key value");
    261     key.s = line.s;
    262     key.len = i;
    263     value.s = line.s + i + 1u;
    264     value.len = line.len - i - 1u;
    265     if (!slice_text_valid(key, BUILD_KEY_MAX, 0) ||
    266         !slice_text_valid(value, BUILD_VAL_MAX, 1))
    267       return set_err(err, errcap, "invalid config text");
    268     if (have_prev && slice_cstr_cmp(key, prev) <= 0)
    269       return set_err(err, errcap, "non-canonical config ordering");
    270     if (build_config_set(out, key, value) != BUILD_OK)
    271       return set_err(err, errcap, "too many config entries");
    272     copy_slice(prev, sizeof prev, key);
    273     have_prev = 1;
    274   }
    275   return BUILD_OK;
    276 }
    277 
    278 int build_argv_parse(const uint8_t* data, size_t len, BuildArgv* out, char* err,
    279                      size_t errcap) {
    280   size_t pos = 0;
    281   KitSlice line;
    282 
    283   if (!data || !out) return set_err(err, errcap, "missing argv");
    284   out->n = 0;
    285   if (next_line(data, len, &pos, &line) != BUILD_OK ||
    286       !kit_slice_eq_cstr(line, BUILD_ARGV_MAGIC))
    287     return set_err(err, errcap, "bad argv magic/version");
    288   while (pos < len) {
    289     if (next_line(data, len, &pos, &line) != BUILD_OK)
    290       return set_err(err, errcap, "unterminated argv line");
    291     if (!slice_text_valid(line, BUILD_VAL_MAX, 1))
    292       return set_err(err, errcap, "invalid argv text");
    293     if (out->n >= out->cap) return set_err(err, errcap, "too many argv args");
    294     copy_slice(out->args[out->n], BUILD_VAL_MAX, line);
    295     ++out->n;
    296   }
    297   return BUILD_OK;
    298 }
    299 
    300 int build_config_id(KitHeap* heap, const BuildConfig* cfg,
    301                     uint8_t out[BUILD_HASH_LEN]) {
    302   KitWriter* w = NULL;
    303   const uint8_t* bytes;
    304   size_t len;
    305   KitBlobInfo info;
    306   if (!heap || !out || kit_writer_mem(heap, &w) != 0 || !w) return BUILD_ERR;
    307   if (build_config_emit(cfg, w) != BUILD_OK || kit_writer_status(w) != 0) {
    308     kit_writer_close(w);
    309     return BUILD_ERR;
    310   }
    311   bytes = kit_writer_mem_bytes(w, &len);
    312   kit_blob_info(&info, bytes, len);
    313   memcpy(out, info.id, BUILD_HASH_LEN);
    314   kit_writer_close(w);
    315   return BUILD_OK;
    316 }
    317 
    318 int build_argv_id(KitHeap* heap, const BuildArgv* argv,
    319                   uint8_t out[BUILD_HASH_LEN]) {
    320   KitWriter* w = NULL;
    321   const uint8_t* bytes;
    322   size_t len;
    323   KitBlobInfo info;
    324   if (!heap || !out || kit_writer_mem(heap, &w) != 0 || !w) return BUILD_ERR;
    325   if (build_argv_emit(argv, w) != BUILD_OK || kit_writer_status(w) != 0) {
    326     kit_writer_close(w);
    327     return BUILD_ERR;
    328   }
    329   bytes = kit_writer_mem_bytes(w, &len);
    330   kit_blob_info(&info, bytes, len);
    331   memcpy(out, info.id, BUILD_HASH_LEN);
    332   kit_writer_close(w);
    333   return BUILD_OK;
    334 }