#include #include #include #include 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); } } 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; b_tty_printf(b_stdtty_err, 0, format, s); break; } case B_ERROR_TEMPLATE_PARAM_CHAR: { char v = (char)param->param_value; b_tty_printf(b_stdtty_err, 0, format, v); break; } case B_ERROR_TEMPLATE_PARAM_INT: { int v = (int)param->param_value; b_tty_printf(b_stdtty_err, 0, format, v); break; } case B_ERROR_TEMPLATE_PARAM_UINT: { unsigned int v = (unsigned int)param->param_value; b_tty_printf(b_stdtty_err, 0, format, v); break; } case B_ERROR_TEMPLATE_PARAM_LONG: { long v = (long)param->param_value; b_tty_printf(b_stdtty_err, 0, format, v); break; } case B_ERROR_TEMPLATE_PARAM_ULONG: { unsigned long v = (unsigned long)param->param_value; b_tty_printf(b_stdtty_err, 0, format, v); break; } case B_ERROR_TEMPLATE_PARAM_LONGLONG: { long long v = (long long)param->param_value; b_tty_printf(b_stdtty_err, 0, format, v); break; } case B_ERROR_TEMPLATE_PARAM_ULONGLONG: { unsigned long long v = (unsigned long long)param->param_value; b_tty_printf(b_stdtty_err, 0, format, v); break; } case B_ERROR_TEMPLATE_PARAM_PTR: { const void *p = (const void *)param->param_value; b_tty_printf(b_stdtty_err, 0, format, p); break; } case B_ERROR_TEMPLATE_PARAM_INTPTR: { intptr_t v = (intptr_t)param->param_value; b_tty_printf(b_stdtty_err, 0, format, v); break; } case B_ERROR_TEMPLATE_PARAM_UINTPTR: { uintptr_t v = (uintptr_t)param->param_value; b_tty_printf(b_stdtty_err, 0, 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 != '@') { b_tty_putc(b_stdtty_err, 0, c); i++; continue; } i++; modifier = s[i]; if (!modifier) { break; } if (modifier != '[' && modifier != '{') { i++; } if (s[i] == '@') { b_tty_putc(b_stdtty_err, 0, c); break; } bool template_param; char end = 0; switch (s[i]) { case '{': template_param = false; end = '}'; break; case '[': template_param = true; end = ']'; break; default: return; } switch (modifier) { case 'i': b_tty_printf(b_stdtty_err, 0, "[cyan]"); break; case 'w': b_tty_printf(b_stdtty_err, 0, "[yellow]"); break; case 'e': b_tty_printf(b_stdtty_err, 0, "[red]"); break; default: break; } 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 { b_tty_printf(b_stdtty_err, 0, "%s", buf); } b_tty_printf(b_stdtty_err, 0, "[reset]"); 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 *submsg = b_error_get_first_submsg(error); while (submsg) { enum b_error_submsg_type type = b_error_submsg_get_type(submsg); const char *content = b_error_submsg_get_content(submsg); const struct b_error_msg *msg = b_error_submsg_get_msg(submsg); const struct b_error_template_parameter *params = b_error_submsg_get_template_parameters(submsg); b_tty_printf(b_stdtty_err, 0, " "); switch (type) { case B_ERROR_SUBMSG_ERROR: b_tty_printf( b_stdtty_err, 0, "[bright_red,bold]>[reset] "); break; case B_ERROR_SUBMSG_WARNING: b_tty_printf( b_stdtty_err, 0, "[bright_yellow,bold]>[reset] "); break; case B_ERROR_SUBMSG_INFO: b_tty_printf( b_stdtty_err, 0, "[bright_cyan,bold]>[reset] "); break; default: break; } if (msg) { print_content( params, B_ERROR_TEMPLATE_PARAMETER_MAX, b_error_msg_get_content(msg)); } else if (content) { print_content( params, B_ERROR_TEMPLATE_PARAMETER_MAX, content); } b_tty_printf(b_stdtty_err, 0, "\n"); submsg = b_error_get_next_submsg(error, submsg); } } 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); b_tty_printf( b_stdtty_err, 0, " [dark_grey]at %s() (%s:%u)[reset]\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); const struct b_error_template_parameter *params = b_error_get_template_parameters(error); if (!description && vendor) { description = b_error_vendor_get_status_code_description( vendor, code); } if (caused_by) { b_tty_printf( b_stdtty_err, 0, " [green]->[reset] caused by [bright_red,bold]ERROR "); } else { b_tty_printf(b_stdtty_err, 0, "[bright_red,bold]==> ERROR "); } if (flags & B_ERROR_REPORT_STATUS) { b_tty_printf(b_stdtty_err, 0, "%s", error_id); } b_tty_printf(b_stdtty_err, 0, "[reset]"); if (flags & B_ERROR_REPORT_DESCRIPTION) { const struct b_error_msg *msg = b_error_get_msg(error); if (msg) { b_tty_printf(b_stdtty_err, 0, ": "); print_content( params, B_ERROR_TEMPLATE_PARAMETER_MAX, b_error_msg_get_content(msg)); } else if (description) { b_tty_printf(b_stdtty_err, 0, ": "); print_content( params, B_ERROR_TEMPLATE_PARAMETER_MAX, description); } } b_tty_printf(b_stdtty_err, 0, "\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); } } void b_enhanced_error_reporter( const struct b_error *error, b_error_report_flags flags) { report_error(error, flags, false); }