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 }