summaryrefslogtreecommitdiff
path: root/pom.c
diff options
context:
space:
mode:
authorpommicket <pommicket@gmail.com>2025-09-14 00:26:39 -0400
committerpommicket <pommicket@gmail.com>2025-09-14 00:26:39 -0400
commit1406758a5a2e38392e54cb7ba7a00a0f80ee1567 (patch)
tree4d42477a1b8d8ee3fa7f3251e9e864a9f6a98d2d /pom.c
parent64c5d7ab942d8f4589f43909497ae07cc69231d0 (diff)
Merging configurations
Diffstat (limited to 'pom.c')
-rw-r--r--pom.c220
1 files changed, 164 insertions, 56 deletions
diff --git a/pom.c b/pom.c
index 5846226..7fc754f 100644
--- a/pom.c
+++ b/pom.c
@@ -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 = &sections[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 = &sections[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;
+}