summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--examples/conf.pom7
-rw-r--r--examples/read_conf.c8
-rw-r--r--pom.c109
-rw-r--r--pom.h11
-rwxr-xr-xpre-commit.sh1
5 files changed, 132 insertions, 4 deletions
diff --git a/examples/conf.pom b/examples/conf.pom
index 1054e41..8df17e7 100644
--- a/examples/conf.pom
+++ b/examples/conf.pom
@@ -2,8 +2,13 @@
bar = 10
# testing
-
+Hello = 5
# here's another comment
[jibjabjobdsfajkha]
win = yes
+
+[number]
+ one = 1
+[]
+thing=yup
diff --git a/examples/read_conf.c b/examples/read_conf.c
index ed33e21..966f0a2 100644
--- a/examples/read_conf.c
+++ b/examples/read_conf.c
@@ -1,5 +1,6 @@
#include <stdlib.h>
#include <string.h>
+#include <inttypes.h>
#include "pom.h"
@@ -11,6 +12,13 @@ int main(int argc, char **argv) {
free(error);
return EXIT_FAILURE;
}
+ printf("number.one: %s\n", pom_conf_get(conf, "number.one"));
+ printf("number.tow: %s\n", pom_conf_get_or_default(conf, "number.tow", "(none)"));
+ printf("has thing? %d\n", pom_conf_has(conf, "thing"));
+ const char *file; uint64_t line;
+ if (pom_conf_location(conf, "thing", &file, &line)) {
+ printf("\tthing location: %s:%" PRIu64 "\n", file,line);
+ }
const pom_item *item;
pom_item_iter *iter = NULL;
while ((item = pom_conf_next_item(conf, &iter))) {
diff --git a/pom.c b/pom.c
index 9d4b0e4..b795f3b 100644
--- a/pom.c
+++ b/pom.c
@@ -13,12 +13,18 @@
#else
#define ATTRIBUTE_PRINTF(fmt, args)
#endif
+
#if _MSC_VER >= 1600
#define PRINTF_FORMAT_STRING _Printf_format_string_
#else
#define PRINTF_FORMAT_STRING
#endif
+#if __STDC_VERSION__ >= 201112 && !defined(__STDC_NO_ATOMICS__)
+#define HAVE_ATOMICS 1
+#include <stdatomic.h>
+#endif
+
struct pom_error {
const pom_error *next;
const char *file;
@@ -44,7 +50,7 @@ struct pom_conf {
// to detect when a section is used after a merge
uint64_t version_number;
size_t prefix_len;
- const struct conf_item *items;
+ struct conf_item *items;
size_t items_count;
};
@@ -58,6 +64,11 @@ struct conf_item {
const char *key, *value, *file;
uint64_t line;
const struct pom_conf *section;
+#if HAVE_ATOMICS
+ atomic_bool read;
+#else
+ bool read;
+#endif
};
struct main_conf {
@@ -650,6 +661,13 @@ static void conf_free(struct main_conf *conf) {
free(conf);
}
+static int
+conf_item_cmp_qsort(const void *va, const void *vb) {
+ const struct conf_item *item_a = va;
+ const struct conf_item *item_b = vb;
+ return strcmp(item_a->key, item_b->key);
+}
+
pom_conf *
parser_finish(struct parser *parser) {
if (parser->out_of_memory || parser->errors.count) {
@@ -667,6 +685,7 @@ parser_finish(struct parser *parser) {
parser_out_of_memory(parser);
return NULL;
}
+ strcpy(filename, parser->filename);
// we made room for the to_free header in pom_load.
struct to_free *string_data = (struct to_free *)parser->string_data.array;
// stop this from getting freed
@@ -680,8 +699,14 @@ parser_finish(struct parser *parser) {
conf_item->line = parser_item->line;
conf_item->key = ((char*)string_data->data) + (parser_item->key - sizeof(struct to_free));
conf_item->value = ((char*)string_data->data) + (parser_item->value - sizeof(struct to_free));
+#if HAVE_ATOMICS
+ conf_item->read = ATOMIC_VAR_INIT(false);
+#else
+ conf_item->read = false;
+#endif
conf_item->section = NULL; // TODO
}
+ qsort(conf->items, conf->items_count, sizeof *conf->items, conf_item_cmp_qsort);
pom_conf *root = conf_calloc(conf, 1, sizeof *root);
root->items = conf->items;
root->items_count = conf->items_count;
@@ -825,7 +850,7 @@ pom_conf_free(pom_conf *conf) {
}
static void
-check_conf(const pom_conf *conf, const char *func) {
+check_conf_(const pom_conf *conf, const char *func) {
if (!conf)
fatal_error("NULL configuration passed to %s", func);
if (conf->version_number != conf->main->version_number)
@@ -833,7 +858,7 @@ check_conf(const pom_conf *conf, const char *func) {
"(in %s) after pom_conf_merge was called.\n"
"This is not allowed.", func);
}
-#define check_conf(conf) check_conf(conf, __func__)
+#define check_conf(conf) check_conf_(conf, __func__)
const pom_item *
pom_conf_next_item(const pom_conf *conf, pom_item_iter **p_iter) {
@@ -860,3 +885,81 @@ pom_conf_next_item(const pom_conf *conf, pom_item_iter **p_iter) {
iter->conf_item++;
return &iter->item;
}
+
+static size_t
+conf_binary_search(const pom_conf *conf, const char *key, char nxt_char, bool *found) {
+ size_t lo = 0;
+ size_t hi = conf->items_count;
+ size_t key_len = strlen(key);
+ while (lo < hi) {
+ size_t mid = (lo + hi) / 2;
+ const char *mid_key = conf->items[mid].key + conf->prefix_len;
+ int cmp = memcmp(key, mid_key, key_len);
+ if (cmp == 0)
+ cmp = nxt_char - mid_key[key_len];
+ if (cmp < 0) {
+ hi = mid;
+ } else if (cmp > 0) {
+ lo = mid + 1;
+ } else {
+ if (found) *found = true;
+ return mid;
+ }
+ }
+ if (found) *found = false;
+ return lo;
+}
+
+struct conf_item *
+conf_get_item(const pom_conf *conf, const char *key, const char *func) {
+ check_conf_(conf, func);
+ if (!key)
+ fatal_error("%s called with key = NULL", func);
+ bool found;
+ size_t i = conf_binary_search(conf, key, 0, &found);
+ if (found)
+ return &conf->items[i];
+ else
+ return NULL;
+}
+#define conf_get_item(conf, key) conf_get_item(conf, key, __func__)
+
+const char *
+pom_conf_get(const pom_conf *conf, const char *key) {
+ struct conf_item *item = conf_get_item(conf, key);
+ if (item) {
+ #if HAVE_ATOMICS
+ atomic_store(&item->read, true);
+ #else
+ item->read = true;
+ #endif
+ return item->value;
+ } else {
+ return NULL;
+ }
+}
+
+const char *
+pom_conf_get_or_default(const pom_conf *conf, const char *key, const char *dflt) {
+ const char *value = pom_conf_get(conf, key);
+ return value ? value : dflt;
+}
+
+bool
+pom_conf_has(const pom_conf *conf, const char *key) {
+ return conf_get_item(conf, key) != NULL;
+}
+
+bool
+pom_conf_location(const pom_conf *conf, const char *key, const char **file, uint64_t *line) {
+ struct conf_item *item = conf_get_item(conf, key);
+ if (item) {
+ if (file) *file = item->file;
+ if (line) *line = item->line;
+ return true;
+ } else {
+ if (file) *file = NULL;
+ if (line) *line = 0;
+ return false;
+ }
+}
diff --git a/pom.h b/pom.h
index 50897b8..75fefd1 100644
--- a/pom.h
+++ b/pom.h
@@ -162,6 +162,8 @@ POM__MUST_USE_R;
///
/// This will be a string such as `"Duplicate key: foo.bar"`
///
+/// Error messages may change arbitrarily in future versions.
+///
/// The returned pointer is valid until the error is freed.
///
/// See also \ref pom_error_print and \ref pom_error_to_string, which
@@ -357,6 +359,8 @@ pom_conf_section(const pom_conf *conf, const char *section);
/// you should still call this function repeatedly until you get `NULL`;
/// otherwise memory associated with the iterator may be leaked.
///
+/// The iterator is invalidated if \ref pom_conf_merge is called.
+///
/// The correct usage for this function is:
///
/// ```C
@@ -384,6 +388,8 @@ pom_conf_next_key(const pom_conf *conf, pom_key_iter **iter);
/// you should still call this function repeatedly until you get `NULL`;
/// otherwise memory associated with the iterator may be leaked.
///
+/// The iterator is invalidated if \ref pom_conf_merge is called.
+///
/// The returned pointer is only valid until the next call to \ref pom_conf_next_item.
///
/// The correct usage for this function is:
@@ -413,6 +419,9 @@ POM__MUST_USE_R;
/// Merge keys from `other` into `conf`, preferring keys in `other`. This invalidates
/// all sections made with \ref pom_conf_section.
+///
+/// It also invalidates any iterators from \ref pom_conf_next_item, \ref pom_conf_next_unread_key,
+/// \ref pom_conf_next_key.
void
pom_conf_merge(pom_conf *conf, const pom_conf *other);
@@ -431,6 +440,8 @@ pom_conf_merge(pom_conf *conf, const pom_conf *other);
/// you should still call this function repeatedly until you get `NULL`;
/// otherwise memory associated with the iterator may be leaked.
///
+/// The iterator is invalidated if \ref pom_conf_merge is called.
+///
/// The correct usage for this function is:
///
/// ```C
diff --git a/pre-commit.sh b/pre-commit.sh
index 6fd5ced..2c213bd 100755
--- a/pre-commit.sh
+++ b/pre-commit.sh
@@ -1,5 +1,6 @@
#!/bin/sh
if sed --version | grep -q 'GNU sed'; then
+ # Remove trailing white space
sed -i 's/\s\s*$//' pom.c pom.h
fi
git add -u