summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpommicket <pommicket@gmail.com>2025-09-15 14:30:33 -0400
committerpommicket <pommicket@gmail.com>2025-09-15 14:30:33 -0400
commit627278a0cb84bca7751e97f13d9c235abac678f0 (patch)
tree6d674f6ceb0be295c126ba9fcf08179054411e90
parent4f2644f354184d5febc1aa8f37e987eca03f7f25 (diff)
pom_settings
-rw-r--r--README.md2
-rw-r--r--errors.c4
-rw-r--r--examples/read_conf.c5
-rw-r--r--pom.c146
-rw-r--r--pom.h44
-rw-r--r--tests/errors.c2
-rw-r--r--tests/location.c4
-rw-r--r--tests/parsing.c4
8 files changed, 121 insertions, 90 deletions
diff --git a/README.md b/README.md
index 3177389..c808c3d 100644
--- a/README.md
+++ b/README.md
@@ -10,7 +10,7 @@ C parser for [POM configuration language](https://pom.computer)
int main(void) {
pom_error *error;
- pom_conf *conf = pom_load_path("conf.pom", &error);
+ pom_conf *conf = pom_load_path(NULL, "conf.pom", &error);
if (!conf) {
pom_error_print(error);
free(error);
diff --git a/errors.c b/errors.c
index 014bea2..24f51f9 100644
--- a/errors.c
+++ b/errors.c
@@ -1,8 +1,4 @@
#include <inttypes.h>
-#include <assert.h>
-#include <string.h>
-
-static char error_language[32];
enum error_id {
ERROR_HEADER,
diff --git a/examples/read_conf.c b/examples/read_conf.c
index be1f97a..acc3eba 100644
--- a/examples/read_conf.c
+++ b/examples/read_conf.c
@@ -6,8 +6,9 @@
int main(int argc, char **argv) {
pom_error *error;
- //pom_set_error_language("fr");
- pom_conf *conf = pom_load_path(argc >= 2 ? argv[1] : "conf.pom", &error);
+ pom_settings settings = {0};
+ strcpy(settings.error_lang, "fr");
+ pom_conf *conf = pom_load_path(&settings, argc >= 2 ? argv[1] : "conf.pom", &error);
if (!conf) {
pom_error_print(error);
free(error);
diff --git a/pom.c b/pom.c
index 78eb41b..945b729 100644
--- a/pom.c
+++ b/pom.c
@@ -1,7 +1,7 @@
/*
TODO:
- clean up read_conf.c example
-- tests
+- interpretation tests
*/
#include "pom.h"
@@ -88,6 +88,7 @@ struct main_conf {
// can return this from pom_conf_section when the section is mpety
// (so we don't have to store empty sections)
pom_conf empty_section;
+ pom_settings settings;
};
struct pom_item_iter {
@@ -146,6 +147,7 @@ struct parser {
size_t (*read_func)(void *, char *, size_t);
void *userdata;
pom_error *out_of_memory_error;
+ const pom_settings *settings;
struct {
char *array;
size_t capacity;
@@ -193,24 +195,29 @@ struct parser {
#include "errors.c"
-void
-pom_set_error_language(const char *lang) {
- if (lang)
- snprintf(error_language, sizeof error_language-1, "%s", lang);
- else
- strcpy(error_language, "en-US");
+static void *
+pom_calloc(const pom_settings *settings, size_t nmemb, size_t sz) {
+ return settings->calloc(settings->allocator_udata, nmemb, sz);
+}
+static void *
+pom_realloc(const pom_settings *settings, void *ptr, size_t sz) {
+ return settings->realloc(settings->allocator_udata, ptr, sz);
+}
+static void
+pom_free(const pom_settings *settings, void *ptr) {
+ return settings->free(settings->allocator_udata, ptr);
}
-
static const char *
-get_error_message(enum error_id id) {
+get_error_message(const pom_settings *settings, enum error_id id) {
assert(id < ERROR_COUNT);
const char *const *messages = error_messages_en;
+ const char *error_lang = settings->error_lang;
for (size_t i = 0; i < sizeof error_messages / sizeof error_messages[0]; i++) {
const char *lang = error_messages[i].lang;
size_t lang_len = strlen(lang);
- if (strncmp(error_language, lang, lang_len) == 0
- && (error_language[lang_len] == 0 || error_language[lang_len] == '-')) {
+ if (strncmp(error_lang, lang, lang_len) == 0
+ && (error_lang[lang_len] == 0 || error_lang[lang_len] == '-')) {
// language match
messages = error_messages[i].messages;
break;
@@ -240,8 +247,8 @@ fatal_error(const char *fmt, ...) {
// Make an error with no next-error.
static pom_error *
-make_error(const char *file, uint64_t line, enum error_id id, ...) {
- const char *fmt = get_error_message(id);
+make_error(const pom_settings *settings, const char *file, uint64_t line, enum error_id id, ...) {
+ const char *fmt = get_error_message(settings, id);
va_list args, args_copy;
va_start(args, id);
va_copy(args_copy, args);
@@ -253,7 +260,7 @@ make_error(const char *file, uint64_t line, enum error_id id, ...) {
bad_fmt = true;
len = strlen(fmt);
}
- pom_error *err = malloc(sizeof(pom_error) + len * 2 + strlen(file) + 64);
+ pom_error *err = pom_calloc(settings, sizeof(pom_error) + len * 2 + strlen(file) + 64, 1);
if (err) {
char *message = (char *)(err + 1);
if (bad_fmt) {
@@ -271,7 +278,7 @@ make_error(const char *file, uint64_t line, enum error_id id, ...) {
#pragma GCC diagnostic ignored "-Wrestrict"
#endif
sprintf(string, "%s\n%s:%" PRIu64 ": %s\n",
- get_error_message(ERROR_HEADER),
+ get_error_message(settings, ERROR_HEADER),
file, line, message);
#if !__clang__ && __GNUC__ >= 4
#pragma GCC diagnostic pop
@@ -346,7 +353,7 @@ parser_realloc_(struct parser *parser, void *ptr, size_t elem_size, size_t *pcap
void **parray = ptr;
void *array = *parray;
new_capacity = new_capacity * 3 / 2 + 2;
- array = realloc(array, new_capacity * elem_size);
+ array = pom_realloc(parser->settings, array, new_capacity * elem_size);
if (!array) {
parser_out_of_memory(parser);
return false;
@@ -405,7 +412,7 @@ static void
parser_error(struct parser *parser, enum error_id id, ...) {
if (parser->out_of_memory) return;
if (parser->errors.count >= 1000) return; // don't bother at this point.
- const char *fmt = get_error_message(id);
+ const char *fmt = get_error_message(parser->settings, id);
va_list args, args_copy;
va_start(args, id);
va_copy(args_copy, args);
@@ -852,7 +859,7 @@ parse_line(struct parser *parser) {
}
static void
-set_error(pom_error **error, pom_error *e) {
+set_error(const pom_settings *settings, pom_error **error, pom_error *e) {
if (error) {
*error = e;
} else {
@@ -874,7 +881,7 @@ 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) {
if (nmemb > SIZE_MAX / (2*sz)) return NULL;
- struct to_free *mem = calloc(1, sizeof(struct to_free) + nmemb * sz);
+ struct to_free *mem = pom_calloc(&conf->settings, 1, sizeof(struct to_free) + nmemb * sz);
if (!mem) return NULL;
conf_free_list_append(conf, mem);
return &mem->data[0];
@@ -885,9 +892,9 @@ 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);
+ pom_free(&conf->settings, f);
}
- free(conf);
+ pom_free(&conf->settings, conf);
}
static int
@@ -1020,11 +1027,12 @@ parser_finish(struct parser *parser) {
if (parser->out_of_memory || parser->errors.count) {
return NULL;
}
- struct main_conf *conf = calloc(1, sizeof *conf);
+ struct main_conf *conf = pom_calloc(parser->settings, 1, sizeof *conf);
if (!conf) {
parser_out_of_memory(parser);
return NULL;
}
+ conf->settings = *parser->settings;
conf->empty_section.main = conf;
size_t items_count = parser->items.count;
struct conf_item *items = conf_calloc(conf, items_count, sizeof(struct conf_item));
@@ -1090,30 +1098,48 @@ parser_finish(struct parser *parser) {
return root;
}
+static void *libc_calloc(void *udata, size_t n, size_t sz) {
+ return calloc(n, sz);
+}
+
+static void *libc_realloc(void *udata, void *ptr, size_t new_size) {
+ return realloc(ptr, new_size);
+}
+
+static void libc_free(void *udata, void *ptr) {
+ free(ptr);
+}
+
pom_conf *
-pom_load(const char *filename,
+pom_load(const pom_settings *psettings, const char *filename,
size_t (*read_func)(void *userdata, char *buf, size_t len),
void *userdata, pom_error **error) {
if (!filename)
fatal_error("%s called with NULL file name", __func__);
if (!read_func)
fatal_error("%s called with NULL read function", __func__);
+ pom_settings settings_data = {0}, *settings = &settings_data;
+ if (psettings) settings_data = *psettings;
+ if (!settings->calloc) settings->calloc = libc_calloc;
+ if (!settings->realloc) settings->realloc = libc_realloc;
+ if (!settings->free) settings->free = libc_free;
if (error) *error = NULL;
// Start by allocating out-of-memory error, so we can just return
// it if we run out of memory.
- pom_error *out_of_memory = make_error(filename, 1, ERROR_OUT_OF_MEMORY);
+ pom_error *out_of_memory = make_error(settings, filename, 1, ERROR_OUT_OF_MEMORY);
if (!out_of_memory) return NULL;
- char *current_section = calloc(1, 1);
+ char *current_section = pom_calloc(settings, 1, 1);
if (!current_section) {
- set_error(error, out_of_memory);
+ set_error(settings, error, out_of_memory);
return NULL;
}
- struct parser *parser = calloc(1, sizeof *parser);
+ struct parser *parser = pom_calloc(settings, 1, sizeof *parser);
if (!parser) {
- free(current_section);
- set_error(error, out_of_memory);
+ pom_free(settings, current_section);
+ set_error(settings, error, out_of_memory);
return NULL;
}
+ parser->settings = settings;
parser->filename = filename;
parser->out_of_memory_error = out_of_memory;
parser->read_func = read_func;
@@ -1128,14 +1154,14 @@ pom_load(const char *filename,
pom_conf *conf = parser_finish(parser);
if (parser->out_of_memory) {
- set_error(error, out_of_memory);
+ set_error(settings, error, out_of_memory);
} else if (parser->errors.count) {
if (error) {
// shouldn't realistically overflow given that we cut off at 1000 errors
size_t len = (parser->errors.count + 1) * (sizeof(pom_error) + strlen(filename) + 32)
+ parser->error_messages.count * 2 + 64;
// convert parser_errors to pom_error.
- pom_error *errors = malloc(len);
+ pom_error *errors = pom_calloc(settings, 1, len);
if (errors) {
char *messages = (char *)(errors + parser->errors.count);
memcpy(messages,
@@ -1153,7 +1179,7 @@ pom_load(const char *filename,
}
// create string containing all error messages
char *string = strchr(filename, '\0') + 1, *s = string;
- sprintf(s, "%s\n", get_error_message(ERROR_HEADER));
+ sprintf(s, "%s\n", get_error_message(settings, ERROR_HEADER));
s = strchr(s, 0);
for (size_t i = 0; i < parser->errors.count; i++) {
const pom_error *e = &errors[i];
@@ -1166,17 +1192,17 @@ pom_load(const char *filename,
*error = parser->out_of_memory_error;
}
}
- free(parser->errors.array);
- free(parser->error_messages.array);
+ pom_free(settings, parser->errors.array);
+ pom_free(settings, parser->error_messages.array);
}
if (!error || *error != out_of_memory) {
- free(out_of_memory);
+ pom_free(settings, out_of_memory);
}
- free(parser->line.array);
- free(parser->current_section.array);
- free(parser->string_data.array);
- free(parser->items.array);
- free(parser);
+ pom_free(settings, parser->line.array);
+ pom_free(settings, parser->current_section.array);
+ pom_free(settings, parser->string_data.array);
+ pom_free(settings, parser->items.array);
+ pom_free(settings, parser);
return conf;
}
@@ -1194,8 +1220,8 @@ read_string(void *vpstring, char *buf, size_t len) {
}
pom_conf *
-pom_load_string(const char *filename, const char *string, pom_error **error) {
- return pom_load(filename, read_string, &string, error);
+pom_load_string(const pom_settings *settings, const char *filename, const char *string, pom_error **error) {
+ return pom_load(settings, filename, read_string, &string, error);
}
#ifndef POM_NO_STDIO
@@ -1205,16 +1231,16 @@ read_file(void *file, char *buf, size_t len) {
}
pom_conf *
-pom_load_file(const char *filename, FILE *file, pom_error **error) {
+pom_load_file(const pom_settings *settings, const char *filename, FILE *file, pom_error **error) {
if (!filename)
fatal_error("%s called with NULL file name", __func__);
if (!file)
fatal_error("%s called with NULL file", __func__);
- pom_conf *conf = pom_load(filename, read_file, file, error);
+ pom_conf *conf = pom_load(settings, filename, read_file, file, error);
if (ferror(file)) {
if (error) {
- free(*error);
- *error = make_error(filename, 1, ERROR_FILE_READ);
+ pom_free(settings, *error);
+ *error = make_error(settings, filename, 1, ERROR_FILE_READ);
}
pom_conf_free(conf);
conf = NULL;
@@ -1223,18 +1249,18 @@ pom_load_file(const char *filename, FILE *file, pom_error **error) {
}
pom_conf *
-pom_load_path(const char *path, pom_error **error) {
+pom_load_path(const pom_settings *settings, const char *path, pom_error **error) {
if (!path)
fatal_error("%s called with NULL file name", __func__);
FILE *fp = fopen(path, "rb");
if (!fp) {
if (error) {
const char *message = strerror(errno);
- *error = make_error(path, 1, ERROR_CANT_OPEN_FILE, message);
+ *error = make_error(settings, path, 1, ERROR_CANT_OPEN_FILE, message);
}
return NULL;
}
- pom_conf *conf = pom_load_file(path, fp, error);
+ pom_conf *conf = pom_load_file(settings, path, fp, error);
fclose(fp);
return conf;
}
@@ -1257,7 +1283,7 @@ pom_conf_next_item(const pom_conf *conf, pom_item_iter **p_iter) {
check_conf(conf);
if (!p_iter) fatal_error("NULL iter passed to %s", __func__);
if (!*p_iter) {
- *p_iter = calloc(1, sizeof **p_iter);
+ *p_iter = pom_calloc(&conf->main->settings, 1, sizeof **p_iter);
if (!*p_iter) return NULL;
(*p_iter)->conf = conf;
(*p_iter)->conf_item = conf->items;
@@ -1266,7 +1292,7 @@ pom_conf_next_item(const pom_conf *conf, pom_item_iter **p_iter) {
if (iter->conf != conf)
fatal_error("%s being called with inconsistent configurations for a single iterator", __func__);
if (iter->conf_item >= conf->items + conf->items_count) {
- free(iter);
+ pom_free(&conf->main->settings, iter);
*p_iter = NULL;
return NULL;
}
@@ -1283,7 +1309,7 @@ pom_conf_next_unread_key(const pom_conf *conf, pom_unread_key_iter **p_iter) {
check_conf(conf);
if (!p_iter) fatal_error("NULL iter passed to %s", __func__);
if (!*p_iter) {
- *p_iter = malloc(sizeof **p_iter);
+ *p_iter = pom_calloc(&conf->main->settings, 1, sizeof **p_iter);
if (!*p_iter) return NULL;
(*p_iter)->conf = conf;
(*p_iter)->conf_item = conf->items;
@@ -1304,7 +1330,7 @@ pom_conf_next_unread_key(const pom_conf *conf, pom_unread_key_iter **p_iter) {
return key;
}
}
- free(iter);
+ pom_free(&conf->main->settings, iter);
*p_iter = NULL;
return NULL;
}
@@ -1314,7 +1340,7 @@ pom_conf_next_key(const pom_conf *conf, pom_key_iter **p_iter) {
check_conf(conf);
if (!p_iter) fatal_error("NULL iter passed to %s", __func__);
if (!*p_iter) {
- *p_iter = malloc(sizeof **p_iter);
+ *p_iter = pom_calloc(&conf->main->settings, 1, sizeof **p_iter);
if (!*p_iter) return NULL;
(*p_iter)->conf = conf;
(*p_iter)->conf_item = conf->items;
@@ -1328,7 +1354,7 @@ pom_conf_next_key(const pom_conf *conf, pom_key_iter **p_iter) {
size_t first_component_len = strcspn(key, ".");
if (!iter->prev_key || strncmp(iter->prev_key, key, first_component_len) != 0) {
// new first component
- char *first_component = realloc(iter->prev_key, first_component_len + 1);
+ char *first_component = pom_realloc(&conf->main->settings, iter->prev_key, first_component_len + 1);
if (!first_component) return NULL;
iter->prev_key = first_component;
memcpy(first_component, key, first_component_len);
@@ -1337,8 +1363,8 @@ pom_conf_next_key(const pom_conf *conf, pom_key_iter **p_iter) {
return first_component;
}
}
- free(iter->prev_key);
- free(iter);
+ pom_free(&conf->main->settings, iter->prev_key);
+ pom_free(&conf->main->settings, iter);
*p_iter = NULL;
return NULL;
}
@@ -1538,7 +1564,7 @@ pom_conf_print(const pom_conf *conf) {
pom_conf *
pom_conf_copy(const pom_conf *conf) {
check_conf(conf);
- struct main_conf *new_main = calloc(1, sizeof *new_main);
+ struct main_conf *new_main = pom_calloc(&conf->main->settings, 1, sizeof *new_main);
if (!new_main) return NULL;
pom_conf *new_root = conf_calloc(new_main, 1, sizeof *new_root);
if (!new_root) {
@@ -1823,13 +1849,13 @@ pom_conf_get_list(const pom_conf *conf, const char *key) {
size_t max_entries = 1; // upper bound on # of entries
for (const char *p = value_str; *p; p++)
max_entries += *p == ',';
- if (max_entries > SIZE_MAX / 16) {
+ if (max_entries > SIZE_MAX / 4 / sizeof(char *)) {
// too many entries (avoid arithmetic overflow)
return NULL;
}
size_t bytes_needed = (max_entries+1) * sizeof (char *)
+ strlen(value_str) + 1;
- char **list = malloc(bytes_needed);
+ char **list = pom_calloc(&conf->main->settings, 1, bytes_needed);
if (!list) return NULL;
char **entry = list;
char *strings = (char *)(list + max_entries+1);
diff --git a/pom.h b/pom.h
index aa6d3a9..a812f7c 100644
--- a/pom.h
+++ b/pom.h
@@ -6,8 +6,6 @@
/// Of course, you should not free or \ref pom_conf_merge into
/// a configuration while another thread is using it.
///
-/// \ref pom_set_error_language is not thread-safe — see its documentation for more notes.
-///
/// Otherwise, libpom is fully thread-safe
/// provided that C11 atomics are available
/// (`__STDC_VERSION__ >= 201112L && !defined(__STDC_NO_ATOMICS__)`).
@@ -95,18 +93,26 @@ typedef struct pom_item {
uint64_t line;
} pom_item;
-/// Set language for error messages.
-///
-/// This function is **not** thread-safe. Ensure synchronization between calling
-/// this and any functions which can return errors.
-///
-/// If `lang` is `NULL` or unrecognized, the default of `"en-US"` will be used.
-///
-/// Currently supported languages:
-///
-/// - `en-US`
-void
-pom_set_error_language(const char *lang);
+/// Settings for libpom.
+///
+/// Start by initializing this to zero,
+/// then fill out only the fields you need.
+typedef struct pom_settings {
+ /// Data to pass to allocation functions.
+ void *allocator_udata;
+ /// calloc function to use for all purposes, or `NULL` to use libc `calloc`.
+ void *(*calloc)(void *udata, size_t nmemb, size_t sz);
+ /// realloc function to use for all purposes, or `NULL` to use libc `realloc`.
+ void *(*realloc)(void *udata, void *ptr, size_t new_sz);
+ /// free function to use for all purposes, or `NULL` to use libc `free`.
+ void (*free)(void *udata, void *ptr);
+ /// Language for error messages.
+ ///
+ /// If empty, the default of `"en-US"` will be used.
+ char error_lang[16];
+ /// Reserved for future use. Must be set to 0.
+ void *reserved[4];
+} pom_settings;
/// Load a configuration using a `read`-like function.
///
@@ -115,6 +121,8 @@ pom_set_error_language(const char *lang);
/// \ref pom_load_path to load from a path,
/// and \ref pom_load_string to load from a string.
///
+/// `settings` can be `NULL`, in which case the default settings are used.
+///
/// On success, a configuration is returned and `*error` is set to `NULL`
/// if `error` is not `NULL`.
///
@@ -135,7 +143,7 @@ pom_set_error_language(const char *lang);
/// `filename` is only used for errors.
POM__MUST_USE_L
pom_conf *
-pom_load(const char *filename,
+pom_load(const pom_settings *settings, const char *filename,
size_t (*read_func)(void *userdata, char *buf, size_t len),
void *userdata, pom_error **error)
POM__MUST_USE_R;
@@ -153,7 +161,7 @@ POM__MUST_USE_R;
/// `filename` is only used for errors.
POM__MUST_USE_L
pom_conf *
-pom_load_file(const char *filename, FILE *file, pom_error **error)
+pom_load_file(const pom_settings *settings, const char *filename, FILE *file, pom_error **error)
POM__MUST_USE_R;
/// Load configuration from a file path.
@@ -166,7 +174,7 @@ POM__MUST_USE_R;
/// in which case it must be freed with `free`.
POM__MUST_USE_L
pom_conf *
-pom_load_path(const char *path, pom_error **error)
+pom_load_path(const pom_settings *settings, const char *path, pom_error **error)
POM__MUST_USE_R;
#endif
@@ -181,7 +189,7 @@ POM__MUST_USE_R;
/// `filename` is only used for errors.
POM__MUST_USE_L
pom_conf *
-pom_load_string(const char *filename, const char *string, pom_error **error)
+pom_load_string(const pom_settings *settings, const char *filename, const char *string, pom_error **error)
POM__MUST_USE_R;
/// Get the message of this error.
diff --git a/tests/errors.c b/tests/errors.c
index 2bcf14c..d1dec96 100644
--- a/tests/errors.c
+++ b/tests/errors.c
@@ -21,7 +21,7 @@ void test_errors(const char *test_dir) {
char *conf_path = malloc(strlen(errors_dir) + strlen(name) + 30);
sprintf(conf_path, "%s/errors/%s", test_dir, name);
pom_error *error;
- pom_conf *conf = pom_load_path(conf_path, &error);
+ pom_conf *conf = pom_load_path(NULL, conf_path, &error);
if (error) {
free(error);
free(conf_path);
diff --git a/tests/location.c b/tests/location.c
index 5694f78..372d506 100644
--- a/tests/location.c
+++ b/tests/location.c
@@ -24,13 +24,13 @@ void test_location(const char *test_dir) {
(int)(strlen(name) - strlen(".locations.pom")), name);
sprintf(loc_path, "%s/%s", location_dir, name);
pom_error *error;
- pom_conf *conf = pom_load_path(conf_path, &error);
+ pom_conf *conf = pom_load_path(NULL, conf_path, &error);
if (error) {
test_fail("Failed to parse %s\n%s", conf_path,
pom_error_to_string(error));
continue;
}
- pom_conf *loc = pom_load_path(loc_path, &error);
+ pom_conf *loc = pom_load_path(NULL, loc_path, &error);
if (error) {
test_fail("Failed to parse %s\n%s", loc_path,
pom_error_to_string(error));
diff --git a/tests/parsing.c b/tests/parsing.c
index f34107e..c28df0d 100644
--- a/tests/parsing.c
+++ b/tests/parsing.c
@@ -24,13 +24,13 @@ void test_parsing(const char *test_dir) {
(int)(strlen(name) - strlen(".flat.pom")), name);
sprintf(flat_path, "%s/parsing/%s", test_dir, name);
pom_error *error;
- pom_conf *conf = pom_load_path(conf_path, &error);
+ pom_conf *conf = pom_load_path(NULL, conf_path, &error);
if (error) {
test_fail("Failed to parse %s\n%s", conf_path,
pom_error_to_string(error));
continue;
}
- pom_conf *flat = pom_load_path(flat_path, &error);
+ pom_conf *flat = pom_load_path(NULL, flat_path, &error);
if (error) {
test_fail("Failed to parse %s\n%s", flat_path,
pom_error_to_string(error));