summaryrefslogtreecommitdiff
path: root/pom.c
diff options
context:
space:
mode:
Diffstat (limited to 'pom.c')
-rw-r--r--pom.c131
1 files changed, 110 insertions, 21 deletions
diff --git a/pom.c b/pom.c
index 5a4433f..01d9efb 100644
--- a/pom.c
+++ b/pom.c
@@ -1,7 +1,5 @@
/*
TODO:
-- error_to_string
-- conf_copy
- typed get functions
*/
#include "pom.h"
@@ -76,7 +74,7 @@ struct pom_conf {
struct conf_section {
const char *key;
- struct pom_conf conf;
+ pom_conf conf;
};
// holds the "root" of a configuration
@@ -85,7 +83,7 @@ struct main_conf {
struct to_free *to_free_head, *to_free_tail;
// can return this from pom_conf_section when the section is mpety
// (so we don't have to store empty sections)
- struct pom_conf empty_section;
+ pom_conf empty_section;
};
struct pom_item_iter {
@@ -284,6 +282,28 @@ parser_out_of_memory(struct parser *parser) {
parser->out_of_memory = true;
}
+char *
+pom_error_to_string(pom_error *error) {
+ // first, calculate # of bytes we have to allocate for the string
+ size_t bytes_required = 16;
+ for (const pom_error *e = error; e; e = e->next)
+ bytes_required += strlen(e->file) + strlen(e->message) + 32;
+ char *string = malloc(bytes_required);
+ if (!string) return NULL;
+ if (!error) {
+ strcpy(string, "No error.");
+ return string;
+ }
+ char *s = string;
+ strcpy(s, "Error:\n");
+ s = strchr(s, 0);
+ for (const pom_error *e = error; e; e = e->next) {
+ sprintf(s, "%s:%" PRIu64 ": %s\n", e->file, e->line, e->message);
+ s = strchr(s, 0);
+ }
+ return string;
+}
+
static POM__MUST_USE_L bool parser_realloc_(struct parser *parser, void *ptr, size_t elem_size, size_t *pcapacity, size_t new_capacity) POM__MUST_USE_R;
static bool
parser_realloc_(struct parser *parser, void *ptr, size_t elem_size, size_t *pcapacity, size_t new_capacity) {
@@ -819,17 +839,12 @@ conf_calloc(struct main_conf *conf, size_t nmemb, size_t sz) {
}
static void
-conf_free_inner(struct main_conf *conf) {
+conf_free(struct main_conf *conf) {
+ if (!conf) return;
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);
}
@@ -1330,6 +1345,12 @@ pom_conf_section(const pom_conf *conf, const char *key) {
return &conf->main->empty_section;
}
+#if HAVE_ATOMICS
+#define copy_atomic(dest, src) atomic_init(dest, atomic_load_explicit(src, memory_order_relaxed))
+#else
+#define copy_atomic(dest, src) *(dest) = *(src)
+#endif
+
bool
pom_conf_merge(pom_conf *conf, const pom_conf *other) {
check_conf(conf);
@@ -1371,7 +1392,7 @@ pom_conf_merge(pom_conf *conf, const pom_conf *other) {
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));
+ copy_atomic(&new_item->read, &item_conf->read);
} else {
if (cmp == 0)
i_conf++; // skip item with same key in `conf`.
@@ -1380,25 +1401,25 @@ pom_conf_merge(pom_conf *conf, const pom_conf *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;
+ string = strchr(string, 0) + 1;
new_item->value = string;
strcpy(string, item_other->value);
- string += strlen(string) + 1;
+ string = strchr(string, 0) + 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,
+ // this isn't perfect de-duplication,
+ /// since keys from different files can be interleaved,
// but it's good enough.
- new_item->file = string;
- prev_file_new = string;
+ new_item->file = prev_file_new;
+ } else {
+ new_item->file = prev_file_new = string;
prev_file_other = item_other->file;
strcpy(string, item_other->file);
- string += strlen(string) + 1;
+ string = strchr(string, 0) + 1;
}
- atomic_init(&new_item->read, atomic_load_explicit(&item_other->read, memory_order_relaxed));
+ copy_atomic(&new_item->read, &item_other->read);
}
}
assert(string <= new_string_data + new_string_data_len);
@@ -1439,3 +1460,71 @@ pom_conf_print(const pom_conf *conf) {
printf("\"\n");
}
}
+
+pom_conf *
+pom_conf_copy(const pom_conf *conf) {
+ check_conf(conf);
+ struct main_conf *new_main = calloc(1, sizeof *new_main);
+ if (!new_main) return NULL;
+ pom_conf *new_root = conf_calloc(new_main, 1, sizeof *new_root);
+ if (!new_root) {
+ conf_free(new_main);
+ return NULL;
+ }
+ new_root->main = new_main;
+ const size_t prefix_len = conf->prefix_len;
+ // calculate # of bytes we have to allocate for copies of string data
+ size_t string_bytes_required = 0;
+ for (size_t i = 0; i < conf->items_count; i++) {
+ const struct conf_item *item = &conf->items[i];
+ if (i == 0 || item->file != item[-1].file) {
+ string_bytes_required += strlen(item->file) + 1;
+ }
+ string_bytes_required += strlen(item->key) + 1 - prefix_len
+ + strlen(item->value) + 1;
+ }
+ // allocate new items array + string data all at once
+ struct conf_item *new_items = conf_calloc(new_main,
+ string_bytes_required + conf->items_count * sizeof *conf->items,
+ 1);
+ if (!new_items) {
+ conf_free(new_main);
+ return NULL;
+ }
+ new_root->items = new_items;
+ new_root->items_count = conf->items_count;
+ char *new_string_data = (char *)(new_items + conf->items_count);
+ char *string = new_string_data;
+ const char *prev_filename = NULL, *prev_filename_copy = NULL;
+ for (size_t i = 0; i < conf->items_count; i++) {
+ const struct conf_item *item = &conf->items[i];
+ struct conf_item *new_item = &new_items[i];
+ if (item->file == prev_filename) {
+ // if this item's file is the same as the last one,
+ // just use the same pointer.
+ // this isn't perfect de-duplication,
+ // since keys from different files can be interleaved,
+ // but it's good enough.
+ new_item->file = prev_filename_copy;
+ } else {
+ new_item->file = prev_filename_copy = string;
+ prev_filename = item->file;
+ strcpy(string, item->file);
+ string = strchr(string, 0) + 1;
+ }
+ new_item->line = item->line;
+ new_item->key = string;
+ strcpy(string, item->key + conf->prefix_len);
+ string = strchr(string, 0) + 1;
+ new_item->value = string;
+ strcpy(string, item->value);
+ string = strchr(string, 0) + 1;
+ copy_atomic(&new_item->read, &item->read);
+ }
+ assert(string <= new_string_data + string_bytes_required);
+ if (!conf_create_sections(new_root)) {
+ conf_free(new_main);
+ return NULL;
+ }
+ return new_root;
+}