Files
bluelib/core/error.c

1030 lines
24 KiB
C
Raw Normal View History

2025-07-28 22:16:26 +01:00
#include "error.h"
#include <blue/core/stringstream.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static void default_error_reporter(
const struct b_error *error, enum b_error_report_flags flags);
static void free_error(struct b_error *error);
static void do_free_error(struct b_error *error);
static const struct b_error_vendor builtin_vendor;
/* TODO protect these with locks */
static struct b_queue free_errors = B_QUEUE_INIT;
static struct b_queue allocated_errors = B_QUEUE_INIT;
static b_error_report_function error_reporter = default_error_reporter;
static enum b_error_report_flags error_reporter_flags = B_ERROR_REPORT_DEFAULT;
static void error_cleanup(void)
{
struct b_queue_entry *entry = b_queue_pop_back(&allocated_errors);
while (entry) {
struct b_error *error = b_unbox(struct b_error, entry, err_entry);
do_free_error(error);
free(error);
entry = b_queue_pop_back(&allocated_errors);
}
entry = b_queue_pop_back(&free_errors);
while (entry) {
struct b_error *error = b_unbox(struct b_error, entry, err_entry);
free(error);
entry = b_queue_pop_back(&free_errors);
}
}
const struct b_error_vendor *b_error_vendor_get_builtin(void)
{
return &builtin_vendor;
}
const struct b_error_definition *b_error_vendor_get_error_definition(
const struct b_error_vendor *vendor, b_error_status_code code)
{
const struct b_error_definition *error_def = NULL;
if (vendor->v_status_get_definition) {
error_def = vendor->v_status_get_definition(vendor, code);
}
if (error_def || !vendor->v_error_definitions) {
return error_def;
}
size_t nr_errors = vendor->v_error_definitions_length / sizeof *error_def;
if (code >= 0 && code < nr_errors) {
error_def = &vendor->v_error_definitions[code];
}
if (error_def && !error_def->err_name) {
return NULL;
}
return error_def;
}
const char *b_error_vendor_get_status_code_name(
const struct b_error_vendor *vendor, b_error_status_code code)
{
const struct b_error_definition *error_def
= b_error_vendor_get_error_definition(vendor, code);
return error_def ? error_def->err_name : NULL;
}
const char *b_error_vendor_get_status_code_description(
const struct b_error_vendor *vendor, b_error_status_code code)
{
const struct b_error_definition *error_def
= b_error_vendor_get_error_definition(vendor, code);
return error_def ? error_def->err_message : NULL;
}
const struct b_error_msg *b_error_vendor_get_msg(
const struct b_error_vendor *vendor, b_error_msg_id msg_id)
{
const struct b_error_msg *msg = NULL;
if (vendor->v_msg_get_definition) {
msg = vendor->v_msg_get_definition(vendor, msg_id);
}
if (msg || !vendor->v_msg) {
return msg;
}
size_t nr_msg = vendor->v_msg_length / sizeof *msg;
if (msg_id < nr_msg) {
msg = &vendor->v_msg[msg_id];
}
if (msg && !msg->msg_message) {
return NULL;
}
return msg;
}
static struct b_error *allocate_error(void)
{
if (b_queue_empty(&allocated_errors) && b_queue_empty(&free_errors)) {
atexit(error_cleanup);
}
struct b_queue_entry *entry = b_queue_pop_back(&free_errors);
if (entry) {
struct b_error *error = b_unbox(struct b_error, entry, err_entry);
memset(error, 0x0, sizeof *error);
b_queue_push_back(&allocated_errors, entry);
return error;
}
struct b_error *error = malloc(sizeof *error);
if (!error) {
return NULL;
}
memset(error, 0x0, sizeof *error);
b_queue_push_back(&allocated_errors, &error->err_entry);
return error;
}
static void do_free_error(struct b_error *error)
{
struct b_queue_entry *entry = b_queue_pop_back(&error->err_submsg);
while (entry) {
struct b_error_submsg *msg
= b_unbox(struct b_error_submsg, entry, msg_entry);
if (msg->msg_content) {
free(msg->msg_content);
}
free(msg);
entry = b_queue_pop_back(&error->err_submsg);
}
entry = b_queue_pop_back(&error->err_stack);
while (entry) {
struct b_error_stack_frame *frame
= b_unbox(struct b_error_stack_frame, entry, f_entry);
free(frame);
entry = b_queue_pop_back(&error->err_stack);
}
size_t nr_param = sizeof error->err_params / sizeof error->err_params[0];
for (size_t i = 0; i < nr_param; i++) {
const struct b_error_template_parameter_definition *param_def
= error->err_params[i].__param_def;
if (!param_def) {
continue;
}
if (param_def->param_type == B_ERROR_TEMPLATE_PARAM_STRING) {
free((void *)error->err_params[i].param_value);
}
}
if (error->err_description) {
free(error->err_description);
}
if (error->err_caused_by) {
free_error(error->err_caused_by);
}
}
static void free_error(struct b_error *error)
{
b_queue_delete(&allocated_errors, &error->err_entry);
do_free_error(error);
b_queue_push_back(&free_errors, &error->err_entry);
}
struct b_error *z__b_error_create_string(
const b_error_vendor *vendor, b_error_status_code code,
struct b_error *caused_by, const char *file, unsigned int line,
const char *func, const char *description, va_list arg)
{
struct b_error *error = allocate_error();
if (!error) {
return NULL;
}
error->err_vendor = vendor;
error->err_code = code;
error->err_caused_by = caused_by;
if (description) {
b_stringstream desc;
b_stringstream_begin_dynamic(&desc);
b_stringstream_addvf(&desc, description, arg);
error->err_description = b_stringstream_end(&desc);
}
return z__b_error_propagate(error, file, line, func);
}
struct b_error *z__b_error_create_msg(
const struct b_error_vendor *vendor, b_error_status_code code,
struct b_error *caused_by, const char *file, unsigned int line,
const char *func, b_error_msg_id msg_id,
const b_error_template_parameter params[])
{
const struct b_error_definition *error_def
= b_error_vendor_get_error_definition(vendor, code);
if (!error_def) {
fprintf(stderr, "Undefined %s error %lu\n", vendor->v_name, code);
abort();
}
const struct b_error_msg *msg = b_error_vendor_get_msg(vendor, msg_id);
if (!msg) {
fprintf(stderr, "Undefined %s error msg %lu\n", vendor->v_name,
msg_id);
abort();
}
struct b_error *error = allocate_error();
if (!error) {
return NULL;
}
error->err_vendor = vendor;
error->err_code = code;
error->err_msg = msg;
error->err_caused_by = caused_by;
for (size_t i = 0; params[i].param_name; i++) {
const struct b_error_template_parameter *param = &params[i];
const struct b_error_template_parameter_definition *param_def
= b_error_msg_get_template_parameter(msg, param->param_name);
if (!param_def) {
continue;
}
memcpy(&error->err_params[i], param, sizeof *param);
error->err_params[i].__param_def = param_def;
if (param_def->param_type == B_ERROR_TEMPLATE_PARAM_STRING) {
const char *s
= (const char *)error->err_params[i].param_value;
size_t len = strlen(s);
char *s2 = malloc(len + 1);
if (!s2) {
error->err_params[i].param_value = 0;
continue;
}
memcpy(s2, s, len);
s2[len] = 0;
error->err_params[i].param_value = (uintptr_t)s2;
}
}
return z__b_error_propagate(error, file, line, func);
}
struct b_error *z__b_error_create_template(
const struct b_error_vendor *vendor, b_error_status_code code,
struct b_error *caused_by, const char *file, unsigned int line,
const char *func, const b_error_template_parameter params[])
{
const struct b_error_definition *error_def
= b_error_vendor_get_error_definition(vendor, code);
if (!error_def) {
fprintf(stderr, "Undefined %s error %lu\n", vendor->v_name, code);
return NULL;
}
struct b_error *error = allocate_error();
if (!error) {
return NULL;
}
error->err_vendor = vendor;
error->err_code = code;
error->err_caused_by = caused_by;
for (size_t i = 0; params[i].param_name; i++) {
const struct b_error_template_parameter *param = &params[i];
const struct b_error_template_parameter_definition *param_def
= b_error_definition_get_template_parameter(
error_def, param->param_name);
if (!param_def) {
continue;
}
memcpy(&error->err_params[i], param, sizeof *param);
error->err_params[i].__param_def = param_def;
if (param_def->param_type == B_ERROR_TEMPLATE_PARAM_STRING) {
const char *s
= (const char *)error->err_params[i].param_value;
size_t len = strlen(s);
char *s2 = malloc(len + 1);
if (!s2) {
error->err_params[i].param_value = 0;
continue;
}
memcpy(s2, s, len);
s2[len] = 0;
error->err_params[i].param_value = (uintptr_t)s2;
}
}
return z__b_error_propagate(error, file, line, func);
}
struct b_error *z__b_error_propagate(
struct b_error *error, const char *file, unsigned int line,
const char *function)
{
if (!error) {
return error;
}
if (!file && !function && line == 0) {
return error;
}
struct b_error_stack_frame *frame = malloc(sizeof *frame);
if (!frame) {
return error;
}
frame->f_file = file;
frame->f_line_number = line;
frame->f_function = function;
b_queue_push_back(&error->err_stack, &frame->f_entry);
return error;
}
struct b_error *z__b_error_caused_by(struct b_error *error, struct b_error *caused_by)
{
error->err_caused_by = caused_by;
return error;
}
struct b_error *z__b_error_caused_by_b_status(
struct b_error *error, enum b_status status)
{
error->err_caused_by = z__b_error_create(
&builtin_vendor, status, NULL, NULL, 0, NULL, NULL);
return error;
}
enum b_status b_error_add_submsg_string(
struct b_error *error, b_error_submsg_type type, const char *msg, ...)
{
struct b_error_submsg *submsg = malloc(sizeof *submsg);
if (!submsg) {
return B_ERR_NO_MEMORY;
}
char buf[512];
va_list arg;
va_start(arg, msg);
size_t len = vsnprintf(buf, sizeof buf, msg, arg);
va_end(arg);
if (len >= sizeof buf) {
len = sizeof buf - 1;
}
char *content = malloc(len + 1);
if (!content) {
free(submsg);
return B_ERR_NO_MEMORY;
}
memcpy(content, buf, len);
content[len] = 0;
submsg->msg_type = type;
submsg->msg_content = content;
b_queue_push_back(&error->err_submsg, &submsg->msg_entry);
return B_SUCCESS;
}
enum b_status z__b_error_add_submsg_template(
struct b_error *error, enum b_error_submsg_type type,
b_error_msg_id msg_id, struct b_error_template_parameter params[])
{
const struct b_error_msg *msg
= b_error_vendor_get_msg(error->err_vendor, msg_id);
if (!msg) {
fprintf(stderr, "Undefined %s error message %lu\n",
error->err_vendor->v_name, msg_id);
abort();
}
struct b_error_submsg *submsg = malloc(sizeof *submsg);
if (!submsg) {
return B_ERR_NO_MEMORY;
}
submsg->msg_type = type;
submsg->msg_msg = msg;
for (size_t i = 0; params[i].param_name; i++) {
const struct b_error_template_parameter *param = &params[i];
const struct b_error_template_parameter_definition *param_def
= b_error_msg_get_template_parameter(msg, param->param_name);
if (!param_def) {
continue;
}
memcpy(&submsg->msg_params[i], param, sizeof *param);
submsg->msg_params[i].__param_def = param_def;
if (param_def->param_type == B_ERROR_TEMPLATE_PARAM_STRING) {
const char *s
= (const char *)submsg->msg_params[i].param_value;
size_t len = strlen(s);
char *s2 = malloc(len + 1);
if (!s2) {
submsg->msg_params[i].param_value = 0;
continue;
}
memcpy(s2, s, len);
s2[len] = 0;
submsg->msg_params[i].param_value = (uintptr_t)s2;
}
}
b_queue_push_back(&error->err_submsg, &submsg->msg_entry);
return B_SUCCESS;
}
void b_error_release(struct b_error *error)
{
b_queue_delete(&allocated_errors, &error->err_entry);
b_queue_push_back(&free_errors, &error->err_entry);
2025-07-28 22:16:26 +01:00
}
void z__b_error_throw(
struct b_error *error, const char *file, unsigned int line, const char *func)
{
if (!error) {
return;
}
error = z__b_error_propagate(error, file, line, func);
error_reporter(error, error_reporter_flags);
free_error(error);
}
b_error_status_code b_error_get_status_code(const struct b_error *error)
{
return error ? error->err_code : 0;
}
const struct b_error_vendor *b_error_get_vendor(const struct b_error *error)
{
return error->err_vendor;
}
const struct b_error_definition *b_error_get_definition(const struct b_error *error)
{
return error->err_def;
}
const struct b_error_template_parameter *b_error_get_template_parameter(
const struct b_error *error, const char *param_name)
{
const size_t nr_params
= sizeof error->err_params / sizeof error->err_params[0];
for (size_t i = 0; i < nr_params; i++) {
if (!error->err_params[i].param_name) {
break;
}
if (!strcmp(error->err_params[i].param_name, param_name)) {
return &error->err_params[i];
}
}
return NULL;
}
const struct b_error_template_parameter *b_error_get_template_parameters(
const struct b_error *error)
{
return error->err_params;
}
const char *b_error_get_description(const struct b_error *error)
{
return error->err_description;
}
const struct b_error_msg *b_error_get_msg(const b_error *error)
{
return error->err_msg;
}
const struct b_error_submsg *b_error_get_first_submsg(const struct b_error *error)
{
struct b_queue_entry *entry = b_queue_first(&error->err_submsg);
if (!entry) {
return NULL;
}
return b_unbox(struct b_error_submsg, entry, msg_entry);
}
const struct b_error_submsg *b_error_get_next_submsg(
const struct b_error *error, const struct b_error_submsg *msg)
{
struct b_queue_entry *entry = b_queue_next(&msg->msg_entry);
if (!entry) {
return NULL;
}
return b_unbox(struct b_error_submsg, entry, msg_entry);
}
const struct b_error_stack_frame *b_error_get_first_stack_frame(
const struct b_error *error)
{
struct b_queue_entry *entry = b_queue_first(&error->err_stack);
if (!entry) {
return NULL;
}
return b_unbox(struct b_error_stack_frame, entry, f_entry);
}
const struct b_error_stack_frame *b_error_get_next_stack_frame(
const struct b_error *error, const struct b_error_stack_frame *frame)
{
struct b_queue_entry *entry = b_queue_next(&frame->f_entry);
if (!entry) {
return NULL;
}
return b_unbox(struct b_error_stack_frame, entry, f_entry);
}
const b_error *b_error_get_caused_by(const struct b_error *error)
{
return error->err_caused_by;
}
enum b_error_submsg_type b_error_submsg_get_type(const struct b_error_submsg *msg)
{
return msg->msg_type;
}
const char *b_error_submsg_get_content(const struct b_error_submsg *msg)
{
return msg->msg_content;
}
const struct b_error_msg *b_error_submsg_get_msg(const b_error_submsg *msg)
{
return msg->msg_msg;
}
const struct b_error_template_parameter *b_error_submsg_get_template_parameters(
const struct b_error_submsg *msg)
{
return msg->msg_params;
}
const char *b_error_stack_frame_get_filepath(const struct b_error_stack_frame *frame)
{
return frame->f_file;
}
unsigned int b_error_stack_frame_get_line_number(
const struct b_error_stack_frame *frame)
{
return frame->f_line_number;
}
const char *b_error_stack_frame_get_function_name(
const struct b_error_stack_frame *frame)
{
return frame->f_function;
}
const struct b_error_template_parameter_definition *b_error_definition_get_template_parameter(
const struct b_error_definition *error_def, const char *param_name)
{
size_t nr_param
= sizeof error_def->err_params / sizeof error_def->err_params[0];
for (size_t i = 0; i < nr_param; i++) {
if (!error_def->err_params[i].param_name) {
break;
}
if (!strcmp(error_def->err_params[i].param_name, param_name)) {
return &error_def->err_params[i];
}
}
return NULL;
}
const char *b_error_msg_get_content(const struct b_error_msg *msg)
{
return msg->msg_message;
}
const struct b_error_template_parameter_definition *b_error_msg_get_template_parameter(
const struct b_error_msg *msg, const char *param_name)
{
size_t nr_param = sizeof msg->msg_params / sizeof msg->msg_params[0];
for (size_t i = 0; i < nr_param; i++) {
if (!msg->msg_params[i].param_name) {
break;
}
if (!strcmp(msg->msg_params[i].param_name, param_name)) {
return &msg->msg_params[i];
}
}
return NULL;
}
void b_set_error_report_function(
b_error_report_function func, enum b_error_report_flags flags)
{
if (!func) {
func = default_error_reporter;
}
if (!flags) {
flags = B_ERROR_REPORT_DEFAULT;
}
error_reporter = func;
error_reporter_flags = flags;
}
static const struct b_error_definition builtin_errors[] = {
B_ERROR_DEFINITION(B_SUCCESS, "SUCCESS", "Success"),
B_ERROR_DEFINITION(B_ERR_NO_MEMORY, "NO_MEMORY", "Out of memory"),
B_ERROR_DEFINITION(
B_ERR_OUT_OF_BOUNDS, "OUT_OF_BOUNDS", "Argument out of bounds"),
B_ERROR_DEFINITION(
B_ERR_INVALID_ARGUMENT, "INVALID_ARGUMENT", "Invalid argument"),
B_ERROR_DEFINITION(
B_ERR_NAME_EXISTS, "NAME_EXISTS", "Name already exists"),
B_ERROR_DEFINITION(
B_ERR_NOT_SUPPORTED, "NOT_SUPPORTED", "Operation not supported"),
B_ERROR_DEFINITION(B_ERR_BAD_STATE, "BAD_STATE", "Bad state"),
B_ERROR_DEFINITION(B_ERR_NO_ENTRY, "NO_ENTRY", "Name does not exist"),
B_ERROR_DEFINITION(B_ERR_NO_DATA, "NO_DATA", "No data available"),
B_ERROR_DEFINITION(B_ERR_NO_SPACE, "NO_DATA", "No space available"),
B_ERROR_DEFINITION(
B_ERR_UNKNOWN_FUNCTION, "UNKNOWN_FUNCTION", "Unknown function"),
B_ERROR_DEFINITION(B_ERR_BAD_FORMAT, "BAD_FORMAT", "Bad format"),
B_ERROR_DEFINITION(B_ERR_IO_FAILURE, "IO_FAILURE", "I/O failure"),
B_ERROR_DEFINITION(
B_ERR_IS_DIRECTORY, "IS_DIRECTORY", "Item is a directory"),
B_ERROR_DEFINITION(
B_ERR_NOT_DIRECTORY, "NOT_DIRECTORY", "Item is not a directory"),
B_ERROR_DEFINITION(
B_ERR_PERMISSION_DENIED, "PERMISSION_DENIED",
"Permission denied"),
B_ERROR_DEFINITION(B_ERR_BUSY, "BUSY", "Resource busy or locked"),
B_ERROR_DEFINITION(
B_ERR_COMPRESSION_FAILURE, "COMPRESSION_FAILURE",
"Compression failure"),
B_ERROR_DEFINITION(
B_ERR_TYPE_REGISTRATION_FAILURE, "TYPE_REGISTRATION_FAILURE",
"Type registration failure"),
B_ERROR_DEFINITION(
B_ERR_CLASS_INIT_FAILURE, "CLASS_INIT_FAILURE",
"Class initialisation failure"),
2025-07-28 22:16:26 +01:00
};
static const struct b_error_vendor builtin_vendor = {
.v_name = "Blue",
.v_error_definitions = builtin_errors,
.v_error_definitions_length = sizeof builtin_errors,
};
static void get_error_id(
const struct b_error_vendor *vendor, const struct b_error *error,
char *out, size_t max)
{
const char *vendor_name = NULL;
const char *error_name = NULL;
b_error_status_code code = b_error_get_status_code(error);
if (vendor) {
vendor_name = vendor->v_name;
error_name = b_error_vendor_get_status_code_name(vendor, code);
}
b_stringstream id;
b_stringstream_begin(&id, out, max);
if (vendor_name) {
b_stringstream_add(&id, vendor_name);
}
if (error_name) {
if (vendor_name) {
b_stringstream_add(&id, ".");
}
b_stringstream_add(&id, error_name);
} else {
if (vendor_name) {
b_stringstream_add(&id, "#");
}
b_stringstream_addf(&id, "%ld", code);
}
}
static void print_template_parameter(
const struct b_error_template_parameter *params, size_t nr_params,
const char *param_name)
{
const struct b_error_template_parameter *param = NULL;
for (size_t i = 0; i < nr_params; i++) {
if (!params[i].param_name) {
break;
}
if (!strcmp(params[i].param_name, param_name)) {
param = &params[i];
break;
}
}
if (!param) {
return;
}
const char *format = param->__param_def->param_format;
switch (param->__param_def->param_type) {
case B_ERROR_TEMPLATE_PARAM_STRING: {
const char *s = (const char *)param->param_value;
fprintf(stderr, format, s);
break;
}
case B_ERROR_TEMPLATE_PARAM_CHAR: {
char v = (char)param->param_value;
fprintf(stderr, format, v);
break;
}
case B_ERROR_TEMPLATE_PARAM_INT: {
int v = (int)param->param_value;
fprintf(stderr, format, v);
break;
}
case B_ERROR_TEMPLATE_PARAM_UINT: {
unsigned int v = (unsigned int)param->param_value;
fprintf(stderr, format, v);
break;
}
case B_ERROR_TEMPLATE_PARAM_LONG: {
long v = (long)param->param_value;
fprintf(stderr, format, v);
break;
}
case B_ERROR_TEMPLATE_PARAM_ULONG: {
unsigned long v = (unsigned long)param->param_value;
fprintf(stderr, format, v);
break;
}
case B_ERROR_TEMPLATE_PARAM_LONGLONG: {
long long v = (long long)param->param_value;
fprintf(stderr, format, v);
break;
}
case B_ERROR_TEMPLATE_PARAM_ULONGLONG: {
unsigned long long v = (unsigned long long)param->param_value;
fprintf(stderr, format, v);
break;
}
case B_ERROR_TEMPLATE_PARAM_PTR: {
const void *p = (const void *)param->param_value;
fprintf(stderr, format, p);
break;
}
case B_ERROR_TEMPLATE_PARAM_INTPTR: {
intptr_t v = (intptr_t)param->param_value;
fprintf(stderr, format, v);
break;
}
case B_ERROR_TEMPLATE_PARAM_UINTPTR: {
uintptr_t v = (uintptr_t)param->param_value;
fprintf(stderr, format, v);
break;
}
default:
return;
}
}
static void print_content(
const struct b_error_template_parameter *params, size_t nr_params,
const char *s)
{
char modifier = 0;
char buf[128];
size_t buf_len = 0;
size_t i;
for (i = 0; s[i];) {
char c = s[i];
if (c != '@') {
fputc(c, stderr);
i++;
continue;
}
i++;
modifier = s[i];
if (!modifier) {
break;
}
if (modifier != '[' && modifier != '{') {
i++;
}
if (s[i] == '@') {
fputc(c, stderr);
break;
}
bool template_param;
char end = 0;
switch (s[i]) {
case '{':
template_param = false;
end = '}';
break;
case '[':
template_param = true;
end = ']';
break;
default:
return;
}
buf_len = 0;
i++;
while (s[i] && s[i] != end) {
buf[buf_len++] = s[i];
buf[buf_len] = 0;
i++;
}
if (template_param) {
print_template_parameter(params, nr_params, buf);
} else {
fprintf(stderr, "%s", buf);
}
if (s[i] != 0) {
i++;
}
}
}
static void print_submsg(const struct b_error *error)
{
const struct b_error_definition *error_def = b_error_get_definition(error);
const struct b_error_submsg *msg = b_error_get_first_submsg(error);
while (msg) {
enum b_error_submsg_type type = b_error_submsg_get_type(msg);
const char *content = b_error_submsg_get_content(msg);
fprintf(stderr, " > ");
print_content(
msg->msg_params,
sizeof msg->msg_params / sizeof msg->msg_params[0],
content);
fprintf(stderr, "\n");
msg = b_error_get_next_submsg(error, msg);
}
}
const char *get_short_filepath(const char *path)
{
size_t len = strlen(path);
unsigned int sep_count = 0;
for (size_t i = len - 1; i > 0; i--) {
if (path[i] != '/' && path[i] != '\\') {
continue;
}
sep_count++;
if (sep_count == 2) {
return path + i + 1;
}
}
return path;
}
static void print_stack_trace(const struct b_error *error)
{
const struct b_error_stack_frame *frame
= b_error_get_first_stack_frame(error);
while (frame) {
const char *file, *func;
unsigned int line;
file = b_error_stack_frame_get_filepath(frame);
line = b_error_stack_frame_get_line_number(frame);
func = b_error_stack_frame_get_function_name(frame);
file = get_short_filepath(file);
fprintf(stderr, " at %s() (%s:%u)\n", func, file, line);
frame = b_error_get_next_stack_frame(error, frame);
}
}
static void report_error(
const b_error *error, b_error_report_flags flags, bool caused_by)
{
const b_error_vendor *vendor = b_error_get_vendor(error);
char error_id[128];
get_error_id(vendor, error, error_id, sizeof error_id);
const struct b_error_definition *error_def = b_error_get_definition(error);
b_error_status_code code = b_error_get_status_code(error);
const char *description = b_error_get_description(error);
if (!description && vendor) {
description = b_error_vendor_get_status_code_description(
vendor, code);
}
if (caused_by) {
fprintf(stderr, " -> caused by ERROR ");
} else {
fprintf(stderr, "==> ERROR ");
}
if (flags & B_ERROR_REPORT_STATUS) {
fprintf(stderr, "%s", error_id);
}
if (flags & B_ERROR_REPORT_DESCRIPTION) {
if (error->err_msg) {
fprintf(stderr, ": ");
print_content(
error->err_params,
sizeof error->err_params
/ sizeof error->err_params[0],
error->err_msg->msg_message);
} else if (description) {
fprintf(stderr, ": ");
print_content(
error->err_params,
sizeof error->err_params
/ sizeof error->err_params[0],
description);
}
}
fprintf(stderr, "\n");
if (flags & B_ERROR_REPORT_SUBMSG) {
print_submsg(error);
}
if (flags & B_ERROR_REPORT_STACK_TRACE) {
print_stack_trace(error);
}
const struct b_error *cause = b_error_get_caused_by(error);
if (cause && (flags & B_ERROR_REPORT_CAUSE)) {
report_error(cause, flags, true);
}
}
static void default_error_reporter(
const struct b_error *error, enum b_error_report_flags flags)
{
report_error(error, flags, false);
}