/* * fy-diag.c - diagnostics * * Copyright (c) 2019 Pantelis Antoniou * * SPDX-License-Identifier: MIT */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #include "fy-diag.h" #include "fy-parse.h" #include "fy-input.h" static const char *error_type_txt[] = { [FYET_DEBUG] = "debug", [FYET_INFO] = "info", [FYET_NOTICE] = "notice", [FYET_WARNING] = "warning", [FYET_ERROR] = "error", }; const char *fy_error_type_to_string(enum fy_error_type type) { if ((unsigned int)type >= FYET_MAX) return ""; return error_type_txt[type]; } enum fy_error_type fy_string_to_error_type(const char *str) { unsigned int i; int level; if (!str) return FYET_MAX; if (isdigit((unsigned char)*str)) { level = atoi(str); if (level >= 0 && level < FYET_MAX) return (enum fy_error_type)level; } for (i = 0; i < FYET_MAX; i++) { if (!strcmp(str, error_type_txt[i])) return (enum fy_error_type)i; } return FYET_MAX; } static const char *error_module_txt[] = { [FYEM_UNKNOWN] = "unknown", [FYEM_ATOM] = "atom", [FYEM_SCAN] = "scan", [FYEM_PARSE] = "parse", [FYEM_DOC] = "doc", [FYEM_BUILD] = "build", [FYEM_INTERNAL] = "internal", [FYEM_SYSTEM] = "system", [FYEM_EMIT] = "emit", [FYEM_TYPESET] = "typeset", [FYEM_DECODE] = "decode", [FYEM_ENCODE] = "encode", [FYEM_REFLECTION] = "reflection", }; const char *fy_error_module_to_string(enum fy_error_module module) { if ((unsigned int)module >= FYEM_MAX) return ""; return error_module_txt[module]; } enum fy_error_module fy_string_to_error_module(const char *str) { unsigned int i; if (!str) return FYEM_MAX; for (i = 0; i < FYEM_MAX; i++) { if (!strcmp(str, error_module_txt[i])) return (enum fy_error_module)i; } return FYEM_MAX; } static const char *fy_error_level_str(enum fy_error_type level) { static const char *txt[] = { [FYET_DEBUG] = "DBG", [FYET_INFO] = "INF", [FYET_NOTICE] = "NOT", [FYET_WARNING] = "WRN", [FYET_ERROR] = "ERR", }; if ((unsigned int)level >= FYET_MAX) return "*unknown*"; return txt[level]; } static const char *fy_error_module_str(enum fy_error_module module) { static const char *txt[] = { [FYEM_UNKNOWN] = "UNKWN", [FYEM_ATOM] = "ATOM ", [FYEM_SCAN] = "SCAN ", [FYEM_PARSE] = "PARSE", [FYEM_DOC] = "DOC ", [FYEM_BUILD] = "BUILD", [FYEM_INTERNAL] = "INTRL", [FYEM_SYSTEM] = "SYSTM", [FYEM_EMIT] = "EMIT ", [FYEM_TYPESET] = "TYPES", [FYEM_DECODE] = "DEC ", [FYEM_ENCODE] = "ENC ", [FYEM_REFLECTION] = "RFLCT", }; if ((unsigned int)module >= FYEM_MAX) return "*unknown*"; return txt[module]; } /* really concervative options */ static const struct fy_diag_term_info default_diag_term_info_template = { .rows = 25, .columns = 80 }; static const struct fy_diag_cfg default_diag_cfg_template = { .fp = NULL, /* must be overriden */ .level = FYET_INFO, .module_mask = (1U << FYEM_MAX) - 1, /* all modules */ .show_source = false, .show_position = false, .show_type = true, .show_module = false, .colorize = false, /* can be overriden */ .source_width = 50, .position_width = 10, .type_width = 5, .module_width = 6, }; void fy_diag_cfg_default(struct fy_diag_cfg *cfg) { if (!cfg) return; *cfg = default_diag_cfg_template; cfg->fp = stderr; cfg->colorize = isatty(fileno(stderr)) == 1; } void fy_diag_cfg_from_parser_flags(struct fy_diag_cfg *cfg FY_UNUSED, enum fy_parse_cfg_flags pflags FY_UNUSED) { /* nothing */ } static bool fy_diag_isatty(struct fy_diag *diag) { return diag && diag->cfg.fp && isatty(fileno(diag->cfg.fp)); } static void fy_diag_update_term_info(struct fy_diag *diag) { int fd, rows, columns, ret; /* start by setting things to the default */ diag->term_info = default_diag_term_info_template; fd = diag->cfg.fp && isatty(fileno(diag->cfg.fp)) ? fileno(diag->cfg.fp) : -1; if (fd == -1) goto out; rows = columns = 0; ret = fy_term_query_size(fd, &rows, &columns); if (ret != 0) goto out; if (rows > 0 && columns > 0) { diag->term_info.rows = rows; diag->term_info.columns = columns; } out: diag->terminal_probed = true; } void fy_diag_errorp_free(struct fy_diag_errorp *errp) { if (errp->space) free(errp->space); fy_token_unref(errp->e.fyt); free(errp); } struct fy_diag *fy_diag_create(const struct fy_diag_cfg *cfg) { struct fy_diag *diag; diag = malloc(sizeof(*diag)); if (!diag) return NULL; memset(diag, 0, sizeof(*diag)); if (!cfg) fy_diag_cfg_default(&diag->cfg); else diag->cfg = *cfg; diag->on_error = false; diag->refs = 1; diag->terminal_probed = false; if (!fy_diag_isatty(diag)) fy_diag_update_term_info(diag); fy_diag_errorp_list_init(&diag->errors); return diag; } void fy_diag_destroy(struct fy_diag *diag) { struct fy_diag_errorp *errp; if (!diag) return; diag->destroyed = true; /* free everything */ while ((errp = fy_diag_errorp_list_pop(&diag->errors)) != NULL) fy_diag_errorp_free(errp); fy_diag_unref(diag); } bool fy_diag_got_error(struct fy_diag *diag) { return diag && diag->on_error; } void fy_diag_set_error(struct fy_diag *diag, bool on_error) { if (diag) diag->on_error = on_error; } void fy_diag_reset_error(struct fy_diag *diag) { struct fy_diag_errorp *errp; if (!diag) return; diag->on_error = false; while ((errp = fy_diag_errorp_list_pop(&diag->errors)) != NULL) fy_diag_errorp_free(errp); } void fy_diag_set_collect_errors(struct fy_diag *diag, bool collect_errors) { struct fy_diag_errorp *errp; if (!diag || diag->destroyed) return; diag->collect_errors = collect_errors; /* clear collected errors on disable */ if (!diag->collect_errors) { while ((errp = fy_diag_errorp_list_pop(&diag->errors)) != NULL) fy_diag_errorp_free(errp); } } bool fy_diag_get_collect_errors(struct fy_diag *diag) { if (!diag || diag->destroyed) return false; return diag->collect_errors; } struct fy_diag_error *fy_diag_errors_iterate(struct fy_diag *diag, void **prevp) { struct fy_diag_errorp *errp; if (!diag || !prevp) return NULL; if (!*prevp) errp = fy_diag_errorp_list_head(&diag->errors); else { errp = *prevp; errp = fy_diag_errorp_next(&diag->errors, errp); } if (!errp) return NULL; *prevp = errp; return &errp->e; } void fy_diag_free(struct fy_diag *diag) { if (!diag) return; free(diag); } const struct fy_diag_cfg *fy_diag_get_cfg(struct fy_diag *diag) { if (!diag) return NULL; return &diag->cfg; } void fy_diag_set_cfg(struct fy_diag *diag, const struct fy_diag_cfg *cfg) { if (!diag) return; if (!cfg) fy_diag_cfg_default(&diag->cfg); else diag->cfg = *cfg; fy_diag_update_term_info(diag); } void fy_diag_set_level(struct fy_diag *diag, enum fy_error_type level) { if (!diag || (unsigned int)level >= FYET_MAX) return; diag->cfg.level = level; } void fy_diag_set_colorize(struct fy_diag *diag, bool colorize) { if (!diag) return; diag->cfg.colorize = colorize; } struct fy_diag *fy_diag_ref(struct fy_diag *diag) { if (!diag) return NULL; assert(diag->refs + 1 > 0); diag->refs++; return diag; } void fy_diag_unref(struct fy_diag *diag) { if (!diag) return; assert(diag->refs > 0); if (diag->refs == 1) fy_diag_free(diag); else diag->refs--; } ssize_t fy_diag_write(struct fy_diag *diag, const void *buf, size_t count) { size_t ret; if (!diag || !buf) return -1; /* no more output */ if (diag->destroyed) return 0; ret = 0; if (diag->cfg.fp) { ret = fwrite(buf, 1, count, diag->cfg.fp); } else if (diag->cfg.output_fn) { diag->cfg.output_fn(diag, diag->cfg.user, buf, count); ret = count; } return ret == count ? (ssize_t)count : -1; } int fy_diag_vprintf(struct fy_diag *diag, const char *fmt, va_list ap) { char *buf; int rc; if (!diag || !fmt) return -1; /* no more output */ if (diag->destroyed) return 0; if (diag->cfg.fp) return vfprintf(diag->cfg.fp, fmt, ap); if (diag->cfg.output_fn) { rc = vasprintf(&buf, fmt, ap); if (rc < 0) return rc; diag->cfg.output_fn(diag, diag->cfg.user, buf, (size_t)rc); free(buf); return rc; } return -1; } int fy_diag_printf(struct fy_diag *diag, const char *fmt, ...) { va_list ap; int rc; va_start(ap, fmt); rc = fy_diag_vprintf(diag, fmt, ap); va_end(ap); return rc; } int fy_vdiag(struct fy_diag *diag, const struct fy_diag_ctx *fydc, const char *fmt, va_list ap) { char *msg = NULL; char *source = NULL, *position = NULL, *typestr = NULL, *modulestr = NULL; const char *file_stripped = NULL; const char *color_start = NULL, *color_end = NULL; enum fy_error_type level; int rc; if (!diag || !fydc || !fmt) return -1; level = fydc->level; /* turn errors into debugs while not reset */ if (level >= FYET_ERROR && diag->on_error) level = FYET_DEBUG; if (!fy_diag_log_level_is_enabled(diag, level, fydc->module)) { rc = 0; goto out; } msg = fy_vsprintfa(fmt, ap); fy_strip_trailing_nl(msg); /* source part */ if (diag->cfg.show_source) { if (fydc->source_file) { file_stripped = strrchr(fydc->source_file, '/'); if (!file_stripped) file_stripped = fydc->source_file; else file_stripped++; } else file_stripped = ""; source = fy_sprintfa("%s:%d @%s()%s", file_stripped, fydc->source_line, fydc->source_func, " "); } /* position part */ if (diag->cfg.show_position && fydc->line >= 0 && fydc->column >= 0) position = fy_sprintfa("<%3d:%2d>%s", fydc->line, fydc->column, ": "); /* type part */ if (diag->cfg.show_type) typestr = fy_sprintfa("[%s]%s", fy_error_level_str(level), ": "); /* module part */ if (diag->cfg.show_module) modulestr = fy_sprintfa("<%s>%s", fy_error_module_str(fydc->module), ": "); if (diag->cfg.colorize) { switch (level) { case FYET_DEBUG: color_start = "\x1b[37m"; /* normal white */ break; case FYET_INFO: color_start = "\x1b[37;1m"; /* bright white */ break; case FYET_NOTICE: color_start = "\x1b[34;1m"; /* bright blue */ break; case FYET_WARNING: color_start = "\x1b[33;1m"; /* bright yellow */ break; case FYET_ERROR: color_start = "\x1b[31;1m"; /* bright red */ break; default: /* handles FYET_MAX */ break; } if (color_start) color_end = "\x1b[0m"; } rc = fy_diag_printf(diag, "%s" "%*s" "%*s" "%*s" "%*s" "%s" "%s\n", color_start ? color_start : "", source ? diag->cfg.source_width : 0, source ? source : "", position ? diag->cfg.position_width : 0, position ? position : "", typestr ? diag->cfg.type_width : 0, typestr ? typestr : "", modulestr ? diag->cfg.module_width : 0, modulestr ? modulestr : "", msg, color_end ? color_end : ""); if (rc > 0) rc++; out: /* if it's the first error we're generating set the * on_error flag until the top caller clears it */ if (!diag->on_error && fydc->level >= FYET_ERROR) diag->on_error = true; return rc; } int fy_diagf(struct fy_diag *diag, const struct fy_diag_ctx *fydc, const char *fmt, ...) { va_list ap; int rc; va_start(ap, fmt); rc = fy_vdiag(diag, fydc, fmt, ap); va_end(ap); return rc; } static void fy_diag_get_error_colors(struct fy_diag *diag, enum fy_error_type type, const char **start, const char **end, const char **white) { if (!diag->cfg.colorize) { *start = *end = *white = ""; return; } switch (type) { case FYET_DEBUG: *start = "\x1b[37m"; /* normal white */ break; case FYET_INFO: *start = "\x1b[37;1m"; /* bright white */ break; case FYET_NOTICE: *start = "\x1b[34;1m"; /* bright blue */ break; case FYET_WARNING: *start = "\x1b[33;1m"; /* bright yellow */ break; case FYET_ERROR: *start = "\x1b[31;1m"; /* bright red */ break; default: *start = "\x1b[0m"; /* catch-all reset */ break; } *end = "\x1b[0m"; *white = "\x1b[37;1m"; } void fy_diag_error_atom_display(struct fy_diag *diag, enum fy_error_type type, struct fy_atom *atom) { const struct fy_raw_line *l, *ln; struct fy_raw_line l_tmp; struct fy_atom_raw_line_iter iter; int content_start_col, content_end_col, content_width; int pass, cols, min_col, max_col, max_line_count, max_line_col8, max_width; int start_col, end_col; const char *color_start, *color_end, *white; bool first_line, last_line; const char *display; int display_len, line_shift; char first_mark; char *rowbuf = NULL, *rbs = NULL, *rbe = NULL; const char *s, *e; int col8, c, w; int tab8_len, tilde_start, tilde_width, tilde_width_m1; size_t rowbufsz; (void)end_col; if (!diag || !atom) return; fy_diag_get_error_colors(diag, type, &color_start, &color_end, &white); /* pacify compilers that think these are uninitialized */ cols = 0; rowbufsz = 0; /* two passes, first one collects extents */ start_col = -1; end_col = -1; min_col = -1; max_col = -1; max_line_count = -1; max_line_col8 = -1; line_shift = -1; for (pass = 0; pass < 2; pass++) { /* on the start of the second pass */ if (pass > 0) { cols = 0; /* if it's probed, use what's there */ if (diag->terminal_probed && diag->term_info.columns > 0) cols = diag->term_info.columns; /* heuristic, avoid probing terminal size if maximum column is less than 80 * columns. This is faster and avoid problems with terminals... */ if (!cols && max_line_col8 < 80) cols = 80; /* no choice but to probe */ if (!cols) { /* only need the terminal width when outputting an error */ if (!diag->terminal_probed && fy_diag_isatty(diag)) fy_diag_update_term_info(diag); cols = diag->term_info.columns; } /* worse case utf8 + 2 color sequences + zero terminated */ rowbufsz = cols * 4 + 2 * 16 + 1; rowbuf = alloca(rowbufsz); rbe = rowbuf + rowbufsz; /* if the maximum column number is less than the terminal * width everything fits, and we're fine */ if (max_line_col8 < cols) { line_shift = 0; } else { max_width = max_col - min_col; /* try to center */ line_shift = min_col + (max_width - cols) / 2; /* the start of the content must always be included */ if (start_col < line_shift) line_shift = start_col; } } fy_atom_raw_line_iter_start(atom, &iter); l = fy_atom_raw_line_iter_next(&iter); for (; l != NULL; l = ln) { /* save it */ l_tmp = *l; l = &l_tmp; /* get the next too */ ln = fy_atom_raw_line_iter_next(&iter); first_line = l->lineno <= 1; last_line = ln == NULL; content_start_col = l->content_start_col8; content_end_col = l->content_end_col8; content_width = content_end_col - content_start_col; if (pass == 0) { if (min_col < 0 || content_start_col < min_col) min_col = content_start_col; if (max_col < 0 || content_end_col > max_col) max_col = content_end_col; if (max_line_count < 0 || (int)l->line_count > max_line_count) max_line_count = (int)l->line_count; if (first_line) start_col = content_start_col; if (last_line) end_col = content_end_col; /* optimize by using the content end as a starting point */ s = l->content_start + l->content_len; e = l->line_start + l->line_count; /* guard against something stupid */ if (s > e) s = e; col8 = l->content_end_col8; while ((c = fy_utf8_get(s, (e - s), &w)) >= 0) { s += w; if (fy_is_tab(c)) col8 += 8 - (col8 % 8); else col8++; } /* update the max column number of the lines */ if (max_line_col8 < 0 || col8 > max_line_col8) max_line_col8 = col8; continue; } /* output pass */ /* the defaults if everything fits */ first_mark = first_line ? '^' : '~'; tab8_len = 0; /* find the starting point */ s = l->line_start; e = s + l->line_len; col8 = 0; while (col8 < line_shift && (c = fy_utf8_get(s, (e - s), &w)) >= 0) { if (fy_is_tab(c)) col8 += 8 - (col8 % 8); else col8++; s += w; } if (col8 > line_shift) tab8_len = col8 - line_shift; /* the remaining of the tab */ else tab8_len = 0; /* start filling the row buffer */ assert(rowbuf); rbs = rowbuf; rbe = rowbuf + rowbufsz; /* remaining tabs */ while (tab8_len > 0) { *rbs++ = ' '; tab8_len--; } /* go forward until end of line or cols */ while (col8 < (line_shift + cols) && (c = fy_utf8_get(s, (e - s), &w)) >= 0 && rbs < rbe) { if (fy_is_tab(c)) { s++; tab8_len = 8 - (col8 % 8); col8 += tab8_len; while (tab8_len > 0 && rbs < rbe) { *rbs++ = ' '; tab8_len--; } } else { while (w > 0 && rbs < rbe) { *rbs++ = *s++; w--; } col8++; } } display = rowbuf; display_len = (int)(rbs - rowbuf); tilde_start = content_start_col - line_shift; tilde_width = content_width; if (tilde_start + tilde_width > cols) tilde_width = cols - tilde_start; if ((size_t)tilde_width >= rowbufsz) tilde_width = (int)(rowbufsz - 1); /* guard */ tilde_width_m1 = tilde_width > 0 ? (tilde_width - 1) : 0; /* output the line */ fy_diag_write(diag, display, display_len); /* set the tildes */ assert((int)rowbufsz > tilde_width_m1 + 1); memset(rowbuf, '~', tilde_width_m1); rowbuf[tilde_width_m1] = '\0'; fy_diag_printf(diag, "\n%*s%s%c%.*s%s\n", tilde_start, "", color_start, first_mark, tilde_width_m1, rowbuf, color_end); } fy_atom_raw_line_iter_finish(&iter); } } void fy_diag_error_token_display(struct fy_diag *diag, enum fy_error_type type, struct fy_token *fyt) { struct fy_atom style_handle; if (!diag || !fyt) return; fy_token_get_style_atom(fyt, &style_handle); fy_diag_error_atom_display(diag, type, &style_handle); fy_input_unref(style_handle.fyi); } void fy_diag_vreport(struct fy_diag *diag, const struct fy_diag_report_ctx *fydrc, const char *fmt, va_list ap) { const char *name, *color_start = NULL, *color_end = NULL, *white = NULL; char *msg_str = NULL, *name_str = NULL; const struct fy_mark *start_mark; int line, column; struct fy_diag_errorp *errp; struct fy_diag_error *err; size_t spacesz, msgsz, filesz; char *s; if (!diag || !fydrc || !fmt || !fydrc->fyt) return; start_mark = fy_token_start_mark(fydrc->fyt); if (fydrc->has_override) { name = fydrc->override_file; line = fydrc->override_line; column = fydrc->override_column; } else { name = fy_input_get_filename(fy_token_get_input(fydrc->fyt)); line = start_mark->line + 1; column = start_mark->column + 1; } /* it will strip trailing newlines */ msg_str = fy_vsprintfa(fmt, ap); fy_strip_trailing_nl(msg_str); /* get the colors */ fy_diag_get_error_colors(diag, fydrc->type, &color_start, &color_end, &white); if (name || (line > 0 && column > 0)) name_str = (line > 0 && column > 0) ? fy_sprintfa("%s%s:%d:%d: ", white, name, line, column) : fy_sprintfa("%s%s: ", white, name); if (!diag->collect_errors) { fy_diag_printf(diag, "%s" "%s%s: %s" "%s\n", name_str ? name_str : "", color_start, fy_error_type_to_string(fydrc->type), color_end, msg_str); fy_diag_error_token_display(diag, fydrc->type, fydrc->fyt); fy_token_unref(fydrc->fyt); } else if ((errp = malloc(sizeof(*errp))) != NULL) { msgsz = strlen(msg_str) + 1; filesz = strlen(name) + 1; spacesz = msgsz + filesz; errp->space = malloc(spacesz); if (!errp->space) { free(errp); goto out; } s = errp->space; err = &errp->e; memset(err, 0, sizeof(*err)); err->type = fydrc->type; err->module = fydrc->module; err->fyt = fydrc->fyt; err->msg = s; memcpy(s, msg_str, msgsz); s += msgsz; err->file = s; memcpy(s, name, filesz); s += filesz; err->line = line; err->column = column; fy_diag_errorp_list_add_tail(&diag->errors, errp); } out: if (!diag->on_error && fydrc->type == FYET_ERROR) diag->on_error = true; } void fy_diag_report(struct fy_diag *diag, const struct fy_diag_report_ctx *fydrc, const char *fmt, ...) { va_list ap; va_start(ap, fmt); fy_diag_vreport(diag, fydrc, fmt, ap); va_end(ap); } void fy_diag_token_vreport(struct fy_diag *diag, struct fy_token *fyt, enum fy_error_type type, const char *fmt, va_list ap) { struct fy_diag_report_ctx drc; bool save_on_error; if (!fyt || !diag) return; save_on_error = diag->on_error; diag->on_error = false; memset(&drc, 0, sizeof(drc)); drc.type = type; drc.module = FYEM_UNKNOWN; drc.fyt = fyt; fy_diag_vreport(diag, &drc, fmt, ap); diag->on_error = save_on_error; } void fy_diag_token_report(struct fy_diag *diag, struct fy_token *fyt, enum fy_error_type type, const char *fmt, ...) { va_list ap; va_start(ap, fmt); fy_diag_token_vreport(diag, fyt, type, fmt, ap); va_end(ap); } void fy_diag_token_override_vreport(struct fy_diag *diag, struct fy_token *fyt, enum fy_error_type type, const char *file, int line, int column, const char *fmt, va_list ap) { struct fy_diag_report_ctx drc; bool save_on_error; if (!fyt || !diag) return; save_on_error = diag->on_error; diag->on_error = false; memset(&drc, 0, sizeof(drc)); drc.type = type; drc.module = FYEM_UNKNOWN; drc.fyt = fyt; drc.has_override = true; drc.override_file = file; drc.override_line = line; drc.override_column = column; fy_diag_vreport(diag, &drc, fmt, ap); diag->on_error = save_on_error; } void fy_diag_token_override_report(struct fy_diag *diag, struct fy_token *fyt, enum fy_error_type type, const char *file, int line, int column, const char *fmt, ...) { va_list ap; va_start(ap, fmt); fy_diag_token_override_vreport(diag, fyt, type, file, line, column, fmt, ap); va_end(ap); } void fy_diag_node_vreport(struct fy_diag *diag, struct fy_node *fyn, enum fy_error_type type, const char *fmt, va_list ap) { struct fy_diag_report_ctx drc; bool save_on_error; if (!fyn || !diag) return; save_on_error = diag->on_error; diag->on_error = false; memset(&drc, 0, sizeof(drc)); drc.type = type; drc.module = FYEM_UNKNOWN; drc.fyt = fy_node_token(fyn); fy_diag_vreport(diag, &drc, fmt, ap); diag->on_error = save_on_error; } void fy_diag_node_report(struct fy_diag *diag, struct fy_node *fyn, enum fy_error_type type, const char *fmt, ...) { va_list ap; va_start(ap, fmt); fy_diag_node_vreport(diag, fyn, type, fmt, ap); va_end(ap); } void fy_diag_node_override_vreport(struct fy_diag *diag, struct fy_node *fyn, enum fy_error_type type, const char *file, int line, int column, const char *fmt, va_list ap) { struct fy_diag_report_ctx drc; bool save_on_error; if (!fyn || !diag) return; save_on_error = diag->on_error; diag->on_error = false; memset(&drc, 0, sizeof(drc)); drc.type = type; drc.module = FYEM_UNKNOWN; drc.fyt = fy_node_token(fyn); drc.has_override = true; drc.override_file = file; drc.override_line = line; drc.override_column = column; fy_diag_vreport(diag, &drc, fmt, ap); diag->on_error = save_on_error; } void fy_diag_node_override_report(struct fy_diag *diag, struct fy_node *fyn, enum fy_error_type type, const char *file, int line, int column, const char *fmt, ...) { va_list ap; va_start(ap, fmt); fy_diag_node_override_vreport(diag, fyn, type, file, line, column, fmt, ap); va_end(ap); } void fy_node_vreport(struct fy_node *fyn, enum fy_error_type type, const char *fmt, va_list ap) { if (!fyn || !fyn->fyd) return; fy_diag_node_vreport(fyn->fyd->diag, fyn, type, fmt, ap); } void fy_node_report(struct fy_node *fyn, enum fy_error_type type, const char *fmt, ...) { va_list ap; va_start(ap, fmt); fy_node_vreport(fyn, type, fmt, ap); va_end(ap); } void fy_node_override_vreport(struct fy_node *fyn, enum fy_error_type type, const char *file, int line, int column, const char *fmt, va_list ap) { if (!fyn || !fyn->fyd) return; fy_diag_node_override_vreport(fyn->fyd->diag, fyn, type, file, line, column, fmt, ap); } void fy_node_override_report(struct fy_node *fyn, enum fy_error_type type, const char *file, int line, int column, const char *fmt, ...) { va_list ap; va_start(ap, fmt); fy_node_override_vreport(fyn, type, file, line, column, fmt, ap); va_end(ap); } void fy_diag_event_vreport(struct fy_diag *diag, struct fy_event *fye, enum fy_event_part fyep, enum fy_error_type type, const char *fmt, va_list ap) { struct fy_diag_report_ctx drc; struct fy_token *fyt; bool save_on_error; if (!fye || !diag) return; save_on_error = diag->on_error; diag->on_error = false; memset(&drc, 0, sizeof(drc)); drc.type = type; drc.module = FYEM_UNKNOWN; drc.fyt = NULL; switch (fyep) { case FYEP_VALUE: fyt = fy_event_get_token(fye); break; case FYEP_TAG: fyt = fy_event_get_tag_token(fye); break; case FYEP_ANCHOR: fyt = fy_event_get_anchor_token(fye); break; default: fyt = NULL; break; } drc.fyt = fy_token_ref(fyt); fy_diag_vreport(diag, &drc, fmt, ap); diag->on_error = save_on_error; } void fy_diag_event_report(struct fy_diag *diag, struct fy_event *fye, enum fy_event_part fyep, enum fy_error_type type, const char *fmt, ...) { va_list ap; va_start(ap, fmt); fy_diag_event_vreport(diag, fye, fyep, type, fmt, ap); va_end(ap); } void fy_event_vreport(struct fy_parser *fyp, struct fy_event *fye, enum fy_event_part fyep, enum fy_error_type type, const char *fmt, va_list ap) { if (fye) fy_diag_event_vreport(fyp->diag, fye, fyep, type, fmt, ap); else fy_parser_vlog(fyp, type, fmt, ap); } void fy_event_report(struct fy_parser *fyp, struct fy_event *fye, enum fy_event_part fyep, enum fy_error_type type, const char *fmt, ...) { va_list ap; va_start(ap, fmt); fy_event_vreport(fyp, fye, fyep, type, fmt, ap); va_end(ap); }