diff options
author | pommicket <pommicket@gmail.com> | 2025-09-14 00:26:39 -0400 |
---|---|---|
committer | pommicket <pommicket@gmail.com> | 2025-09-14 00:26:39 -0400 |
commit | 1406758a5a2e38392e54cb7ba7a00a0f80ee1567 (patch) | |
tree | 4d42477a1b8d8ee3fa7f3251e9e864a9f6a98d2d /pom.c | |
parent | 64c5d7ab942d8f4589f43909497ae07cc69231d0 (diff) |
Merging configurations
Diffstat (limited to 'pom.c')
-rw-r--r-- | pom.c | 220 |
1 files changed, 164 insertions, 56 deletions
@@ -1,3 +1,10 @@ +/* +TODO: +- error_to_string +- conf_copy +- conf_print +- typed get functions +*/ #include "pom.h" #include <stdio.h> // still needed for sprintf, even if POM_NO_STDIO is defined. @@ -717,7 +724,7 @@ parse_line(struct parser *parser) { strip_trailing_accepted_spaces(line); size_t len = strlen(line); if (line[len-1] != ']') { - parser_error(parser, "Missing ] to match ["); + parser_error(parser, "Line starting with [ must end with ]"); return; } line += 1; @@ -732,7 +739,6 @@ parse_line(struct parser *parser) { check_valid_key(parser, current_section); return; } - printf("%s|%s\n",parser->current_section.array,line); size_t equals_idx; for (size_t i = 0; ; i++) { if (line[i] == '=') { @@ -793,7 +799,8 @@ set_error(pom_error **error, pom_error *e) { } } -static void conf_free_list_append(struct main_conf *conf, struct to_free *mem) { +static void +conf_free_list_append(struct main_conf *conf, struct to_free *mem) { mem->next = NULL; if (conf->to_free_tail) { conf->to_free_tail->next = mem; @@ -803,7 +810,8 @@ static void conf_free_list_append(struct main_conf *conf, struct to_free *mem) { } } -static void *conf_calloc(struct main_conf *conf, size_t nmemb, size_t sz) { +static void * +conf_calloc(struct main_conf *conf, size_t nmemb, size_t sz) { if (nmemb > SIZE_MAX / (2*sz)) return NULL; struct to_free *mem = calloc(1, sizeof(struct to_free) + nmemb * sz); if (!mem) return NULL; @@ -811,12 +819,18 @@ static void *conf_calloc(struct main_conf *conf, size_t nmemb, size_t sz) { return &mem->data[0]; } -static void conf_free(struct main_conf *conf) { - if (!conf) return; +static void +conf_free_inner(struct main_conf *conf) { for (struct to_free *next, *f = conf->to_free_head; f; f = next) { next = f->next; free(f); } +} + +static void +conf_free(struct main_conf *conf) { + if (!conf) return; + conf_free_inner(conf); free(conf); } @@ -885,6 +899,66 @@ conf_binary_search_sections(const pom_conf *conf, const char *key, char nxt_char return lo; } +// set up root->sections +static POM__MUST_USE_L bool conf_create_sections(pom_conf *root) POM__MUST_USE_R; +static bool +conf_create_sections(pom_conf *root) { + assert(root->prefix_len == 0); + assert(root->sections == NULL); + struct main_conf *conf = root->main; + size_t sections_count = 0; + size_t items_count = root->items_count; + struct conf_item *items = root->items; + for (size_t i = 0; i < items_count; i++) { + struct conf_item *item = &items[i]; + for (const char *p = item->key; p; ) { + const char *dot = strchr(p, '.'); + if (!dot) break; + sections_count += i == 0 + || strncmp(item->key, items[i-1].key, dot + 1 - item->key) != 0; + p = dot + 1; + } + } + struct conf_section *sections = conf_calloc(conf, sections_count, sizeof *sections); + if (!sections) return false; + root->sections = sections; + root->sections_count = sections_count; + struct conf_section *section = sections; + for (size_t i = 0; i < items_count; i++) { + struct conf_item *item = &items[i]; + for (const char *p = item->key, *dot; p; p = dot + 1) { + dot = strchr(p, '.'); + if (!dot) break; + size_t key_len = dot - item->key; + if (i && strncmp(item->key, items[i-1].key, key_len + 1) == 0) + continue; // section was already created + // create section + char *section_key = conf_calloc(conf, key_len + 1, 1); + if (!section_key) + return false; + section->key = section_key; + memcpy(section_key, item->key, key_len); + section_key[key_len] = 0; + // Note: + (...) is to not include key foo.bar in section(conf, "foo.bar") + size_t i_start = i + (item->key[key_len] == 0); + size_t i_end = conf_binary_search(root, section_key, '.' + 1, NULL); + section->conf.items = items + i_start; + section->conf.items_count = i_end - i_start; + section->conf.prefix_len = strlen(section_key) + 1/* dot */; + section->conf.main = conf; + section++; + } + } + assert(section == sections + sections_count); + for (size_t i = 0; i < sections_count; i++) { + section = §ions[i]; + // set up sub-sections. + section->conf.sections = section; + section->conf.sections_count = conf_binary_search_sections(root, section->key, '.' + 1, NULL) - i; + } + return true; +} + pom_conf * parser_finish(struct parser *parser) { if (parser->out_of_memory || parser->errors.count) { @@ -956,56 +1030,8 @@ parser_finish(struct parser *parser) { root->prefix_len = 0; root->items = items; root->items_count = items_count; - size_t sections_count = 0; - for (size_t i = 0; i < items_count; i++) { - struct conf_item *item = &items[i]; - for (const char *p = item->key; p; ) { - const char *dot = strchr(p, '.'); - if (!dot) break; - sections_count += i == 0 - || strncmp(item->key, items[i-1].key, dot + 1 - item->key) != 0; - p = dot + 1; - } - } - struct conf_section *sections = conf_calloc(conf, sections_count, sizeof *sections); - if (!sections) goto out_of_memory; - root->sections = sections; - root->sections_count = sections_count; - struct conf_section *section = sections; - for (size_t i = 0; i < items_count; i++) { - struct conf_item *item = &items[i]; - for (const char *p = item->key, *dot; p; p = dot + 1) { - dot = strchr(p, '.'); - if (!dot) break; - size_t key_len = dot - item->key; - if (i && strncmp(item->key, items[i-1].key, key_len + 1) == 0) - continue; // section was already created - // create section - char *section_key = conf_calloc(conf, key_len + 1, 1); - if (!section_key) { - conf_free(conf); - return NULL; - } - section->key = section_key; - memcpy(section_key, item->key, key_len); - section_key[key_len] = 0; - // Note: + (...) is to not include key foo.bar in section(conf, "foo.bar") - size_t i_start = i + (item->key[key_len] == 0); - size_t i_end = conf_binary_search(root, section_key, '.' + 1, NULL); - section->conf.items = items + i_start; - section->conf.items_count = i_end - i_start; - section->conf.prefix_len = strlen(section_key) + 1/* dot */; - section->conf.main = conf; - section++; - } - } - assert(section == sections + sections_count); - for (size_t i = 0; i < sections_count; i++) { - section = §ions[i]; - // set up sub-sections. - section->conf.sections = section; - section->conf.sections_count = conf_binary_search_sections(root, section->key, '.' + 1, NULL) - i; - } + if (!conf_create_sections(root)) + goto out_of_memory; return root; } @@ -1304,3 +1330,85 @@ pom_conf_section(const pom_conf *conf, const char *key) { else return &conf->main->empty_section; } + +bool +pom_conf_merge(pom_conf *conf, const pom_conf *other) { + check_conf(conf); + check_conf(other); + assert(conf->prefix_len == 0); + size_t max_items = conf->items_count + other->items_count; + struct conf_item *new_items = conf_calloc(conf->main, max_items, sizeof *new_items); + if (!new_items) + return false; + size_t new_string_data_len = 0; + for (size_t i = 0; i < other->items_count; i++) { + const struct conf_item *item = &other->items[i]; + new_string_data_len += + strlen(item->key) + 1 - other->prefix_len + + strlen(item->value) + 1; + if (i == 0 || item->file != item[-1].file) { + new_string_data_len += strlen(item->file) + 1; + } + } + char *new_string_data = conf_calloc(conf->main, 1, new_string_data_len); + if (!new_string_data) + return false; + char *string = new_string_data; + size_t i_conf = 0; + size_t i_other = 0; + size_t i_new_items = 0; + const char *prev_file_other = NULL, *prev_file_new = NULL; + while (i_conf < conf->items_count || i_other < other->items_count) { + const struct conf_item *item_conf = &conf->items[i_conf]; + const struct conf_item *item_other = &other->items[i_other]; + int cmp = i_conf >= conf->items_count ? 1 + : i_other >= other->items_count ? -1 + : strcmp(item_conf->key + conf->prefix_len, item_other->key + other->prefix_len); + struct conf_item *new_item = &new_items[i_new_items++]; + if (cmp < 0) { + // select item from `conf`. + i_conf++; + new_item->key = item_conf->key; + new_item->value = item_conf->value; + new_item->line = item_conf->line; + new_item->file = item_conf->file; + atomic_init(&new_item->read, atomic_load_explicit(&item_conf->read, memory_order_relaxed)); + } else { + if (cmp == 0) + i_conf++; // skip item with same key in `conf`. + // select item from `other`. + i_other++; + // copy all the fields, since they might have to outlive `other`. + new_item->key = string; + strcpy(string, item_other->key + other->prefix_len); + string += strlen(string) + 1; + new_item->value = string; + strcpy(string, item_other->value); + string += strlen(string) + 1; + new_item->line = item_other->line; + if (item_other->file == prev_file_other) { + new_item->file = prev_file_new; + } else { + // if this item's file is the same as the last one from `other`, + // just use the same pointer. + // this isn't perfect since keys from different files can be interleaved, + // but it's good enough. + new_item->file = string; + prev_file_new = string; + prev_file_other = item_other->file; + strcpy(string, item_other->file); + string += strlen(string) + 1; + } + atomic_init(&new_item->read, atomic_load_explicit(&item_other->read, memory_order_relaxed)); + } + } + assert(string <= new_string_data + new_string_data_len); + pom_conf new_conf = {0}; + new_conf.main = conf->main; + new_conf.items = new_items; + new_conf.items_count = i_new_items; + if (!conf_create_sections(&new_conf)) + return false; + *conf = new_conf; + return true; +} |