summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpommicket <pommicket@gmail.com>2025-09-14 01:26:00 -0400
committerpommicket <pommicket@gmail.com>2025-09-14 01:26:00 -0400
commit0e220eaa56d5b1947e6bead8e5695446ab102fba (patch)
tree2777f5010c10bdf71bbb38d87eb4f8b8deb35b42
parent1ed5d20169194631b3da49982df82774fcba7cb7 (diff)
simplify error-to-string interface
-rw-r--r--examples/conf.pom4
-rw-r--r--examples/read_conf.c7
-rw-r--r--pom.c66
-rw-r--r--pom.h24
4 files changed, 53 insertions, 48 deletions
diff --git a/examples/conf.pom b/examples/conf.pom
index 0edc83f..ed37ed9 100644
--- a/examples/conf.pom
+++ b/examples/conf.pom
@@ -1,7 +1,7 @@
-[number]
+[number]p
one = 1
two = 2x"\?
-three = "3
+three j= "3
is
the
best"
diff --git a/examples/read_conf.c b/examples/read_conf.c
index 54a1e86..18802e3 100644
--- a/examples/read_conf.c
+++ b/examples/read_conf.c
@@ -8,12 +8,7 @@ int main(int argc, char **argv) {
pom_error *error;
pom_conf *conf = pom_load_path(argc >= 2 ? argv[1] : "conf.pom", &error);
if (!conf) {
- char *string = pom_error_to_string(error);
- for (char *s=string; *s; s++)
- if (*s >= 'a' && *s <= 'z')
- *s += 'A' - 'a';
- printf("%s\n",string);
- free(string);
+ pom_error_print(error);
free(error);
return EXIT_FAILURE;
}
diff --git a/pom.c b/pom.c
index 01d9efb..906e970 100644
--- a/pom.c
+++ b/pom.c
@@ -35,6 +35,9 @@ struct pom_error {
const char *file;
uint64_t line;
const char *message;
+ // only set for first error in error list.
+ // return value of pom_error_to_string
+ const char *string;
};
struct conf_item {
@@ -219,7 +222,7 @@ make_error(const char *file, uint64_t line, const char *fmt, ...) {
bad_fmt = true;
len = strlen(fmt);
}
- pom_error *err = malloc(sizeof(pom_error) + len + 1);
+ pom_error *err = malloc(sizeof(pom_error) + len * 2 + strlen(file) + 48);
if (err) {
char *message = (char *)(err + 1);
if (bad_fmt) {
@@ -230,6 +233,17 @@ make_error(const char *file, uint64_t line, const char *fmt, ...) {
err->file = file;
err->line = line;
err->message = message;
+ char *string = strchr(message, '\0') + 1;
+ // no, clang, string will not overlap with message.
+ #if __GNUC__ >= 4
+ #pragma GCC diagnostic push
+ #pragma GCC diagnostic ignored "-Wrestrict"
+ #endif
+ sprintf(string, "Error:\n%s:%" PRIu64 ": %s\n", file, line, message);
+ #if __GNUC__ >= 4
+ #pragma GCC diagnostic pop
+ #endif
+ err->string = string;
err->next = NULL;
}
va_end(args_copy);
@@ -265,15 +279,8 @@ pom_error_message(const pom_error *error) {
#ifndef POM_NO_STDIO
void
-pom_error_print(const pom_error *error) {
- if (!error) {
- fprintf(stderr, "No error.\n");
- return;
- }
- fprintf(stderr, "Error:\n");
- for (; error; error = pom_error_next(error)) {
- fprintf(stderr, "%s:%" PRIu64 ": %s\n", error->file, error->line, error->message);
- }
+pom_error_print(pom_error *error) {
+ fputs(pom_error_to_string(error), stderr);
}
#endif
@@ -282,26 +289,13 @@ parser_out_of_memory(struct parser *parser) {
parser->out_of_memory = true;
}
-char *
+const 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);
+ if (error) {
+ return error->string;
+ } else {
+ return "No error.\n";
}
- 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;
@@ -1090,8 +1084,9 @@ pom_load(const char *filename,
set_error(error, out_of_memory);
} else if (parser->errors.count) {
if (error) {
- // shouldn't overflow
- size_t len = parser->errors.count * sizeof(pom_error) + parser->error_messages.count + strlen(filename) + 1;
+ // 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 + 16;
// convert parser_errors to pom_error.
pom_error *errors = malloc(len);
if (errors) {
@@ -1107,7 +1102,18 @@ pom_load(const char *filename,
errors[i].line = parser_error->line;
errors[i].message = messages + parser_error->message;
errors[i].next = i == parser->errors.count - 1 ? NULL : &errors[i+1];
+ errors[i].string = NULL;
+ }
+ // create string containing all error messages
+ char *string = strchr(filename, '\0') + 1, *s = string;
+ strcpy(s, "Error:\n");
+ s = strchr(s, 0);
+ for (size_t i = 0; i < parser->errors.count; i++) {
+ const pom_error *e = &errors[i];
+ sprintf(s, "%s:%" PRIu64 ": %s\n", e->file, e->line, e->message);
+ s = strchr(s, 0);
}
+ errors->string = string;
*error = errors;
} else {
*error = parser->out_of_memory_error;
diff --git a/pom.h b/pom.h
index ea38a3d..9adb156 100644
--- a/pom.h
+++ b/pom.h
@@ -90,6 +90,11 @@ typedef struct pom_item {
/// Load a configuration using a `read`-like function.
///
+/// Most of the time, you won't need this function:
+/// use \ref pom_load_file to load from a `FILE *`,
+/// \ref pom_load_path to load from a path,
+/// and \ref pom_load_string to load from a string.
+///
/// On success, a configuration is returned and `*error` is set to `NULL`
/// if `error` is not `NULL`.
///
@@ -99,11 +104,6 @@ typedef struct pom_item {
/// there isn’t even enough memory for an out-of-memory
/// error, `NULL` is returned and `*error` is set to `NULL`.
///
-/// Most of the time, you won't need this function:
-/// use \ref pom_load_file to load from a `FILE *`,
-/// \ref pom_load_path to load from a path,
-/// and \ref pom_load_string to load from a string.
-///
/// `read_func` will be passed the `userdata` pointer passed to this function,
/// a buffer, and the length of that buffer (which will be nonzero).
/// It must fill out the buffer as much as possible,
@@ -121,7 +121,7 @@ pom_load(const char *filename,
POM__MUST_USE_R;
#ifndef POM_NO_STDIO
-/// Load configuration from a `FILE *`, which should be opened in binary mode.
+/// Load configuration from a `FILE *` opened in read-binary mode.
///
/// On success, a configuration is returned and `*error` is set to `NULL`
/// if `error` is not `NULL`.
@@ -187,7 +187,11 @@ pom_error_file(const pom_error *error);
uint64_t
pom_error_line(const pom_error *error);
-/// Get next error in error list.
+/// Get next error in error list, or `NULL` if this is the last error.
+///
+/// You can only call \ref pom_error_message, \ref pom_error_file, and \ref pom_error_line
+/// on the returned error, not \ref pom_error_print or \ref pom_error_to_string.
+/// (This is enforced with `const`-ness.)
const pom_error *
pom_error_next(const pom_error *error);
@@ -196,14 +200,14 @@ pom_error_next(const pom_error *error);
///
/// Includes every error in an error list (see \ref pom_error_next).
void
-pom_error_print(const pom_error *error);
+pom_error_print(pom_error *error);
#endif
-/// Convert error to string. Return value must be `free()`d.
+/// Convert error to string. Return value is valid until `error` is `free()`’d.
///
/// Includes every error in an error list (see \ref pom_error_next).
POM__MUST_USE_L
-char *
+const char *
pom_error_to_string(pom_error *error)
POM__MUST_USE_R;