1045 lines
24 KiB
C
1045 lines
24 KiB
C
#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);
|
|
}
|
|
}
|
|
|
|
bool b_result_is(
|
|
struct b_error *err, const b_error_vendor *vendor, b_error_status_code code)
|
|
{
|
|
if (!err) {
|
|
return false;
|
|
}
|
|
|
|
if (err->err_vendor != vendor) {
|
|
return false;
|
|
}
|
|
|
|
return err->err_code == code;
|
|
}
|
|
|
|
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_create();
|
|
b_stream_write_vfmt(desc, NULL, description, arg);
|
|
error->err_description = b_stringstream_steal(desc);
|
|
b_stringstream_unref(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 = ¶ms[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 = ¶ms[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 = ¶ms[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);
|
|
}
|
|
|
|
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"),
|
|
};
|
|
|
|
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_create_with_buffer(out, max);
|
|
|
|
if (vendor_name) {
|
|
b_stream_write_string(id, vendor_name, NULL);
|
|
}
|
|
|
|
if (error_name) {
|
|
if (vendor_name) {
|
|
b_stream_write_string(id, ".", NULL);
|
|
}
|
|
|
|
b_stream_write_string(id, error_name, NULL);
|
|
} else {
|
|
if (vendor_name) {
|
|
b_stream_write_string(id, "#", NULL);
|
|
}
|
|
|
|
b_stream_write_fmt(id, NULL, "%ld", code);
|
|
}
|
|
|
|
b_stringstream_unref(id);
|
|
}
|
|
|
|
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 = ¶ms[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);
|
|
}
|