/* * Error and warning reporting and related functions. * * Copyright (C) 2001-2007 Peter Johnson * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include "util.h" #include #include #include #include "coretype.h" #include "linemap.h" #include "errwarn.h" #define MSG_MAXSIZE 1024 #if !defined(HAVE_TOASCII) || defined(lint) # define toascii(c) ((c) & 0x7F) #endif /* Default handlers for replacable functions */ static /*@exits@*/ void def_internal_error_ (const char *file, unsigned int line, const char *message); static /*@exits@*/ void def_fatal(const char *message, va_list va); static const char *def_gettext_hook(const char *msgid); /* Storage for errwarn's "extern" functions */ /*@exits@*/ void (*yasm_internal_error_) (const char *file, unsigned int line, const char *message) = def_internal_error_; /*@exits@*/ void (*yasm_fatal) (const char *message, va_list va) = def_fatal; const char * (*yasm_gettext_hook) (const char *msgid) = def_gettext_hook; /* Error indicator */ /* yasm_eclass is not static so that yasm_error_occurred macro can access it */ yasm_error_class yasm_eclass; static /*@only@*/ /*@null@*/ char *yasm_estr; static unsigned long yasm_exrefline; static /*@only@*/ /*@null@*/ char *yasm_exrefstr; /* Warning indicator */ typedef struct warn { /*@reldef@*/ STAILQ_ENTRY(warn) link; yasm_warn_class wclass; /*@owned@*/ /*@null@*/ char *wstr; } warn; static STAILQ_HEAD(warn_head, warn) yasm_warns; /* Enabled warnings. See errwarn.h for a list. */ static unsigned long warn_class_enabled; typedef struct errwarn_data { /*@reldef@*/ SLIST_ENTRY(errwarn_data) link; enum { WE_UNKNOWN, WE_ERROR, WE_WARNING, WE_PARSERERROR } type; unsigned long line; unsigned long xrefline; /*@owned@*/ char *msg; /*@owned@*/ char *xrefmsg; } errwarn_data; struct yasm_errwarns { /*@reldef@*/ SLIST_HEAD(errwarn_head, errwarn_data) errwarns; /* Total error count */ unsigned int ecount; /* Total warning count */ unsigned int wcount; /* Last inserted error/warning. Used to speed up insertions. */ /*@null@*/ errwarn_data *previous_we; }; /* Static buffer for use by conv_unprint(). */ static char unprint[5]; static const char * def_gettext_hook(const char *msgid) { return msgid; } void yasm_errwarn_initialize(void) { /* Default enabled warnings. See errwarn.h for a list. */ warn_class_enabled = (1UL<previous_we; first = SLIST_FIRST(&errwarns->errwarns); if (!ins_we || !first) action = INS_HEAD; while (action == INS_NONE) { next = SLIST_NEXT(ins_we, link); if (line < ins_we->line) { if (ins_we == first) action = INS_HEAD; else ins_we = first; } else if (!next) action = INS_AFTER; else if (line >= ins_we->line && line < next->line) action = INS_AFTER; else ins_we = next; } if (replace_parser_error && ins_we && ins_we->type == WE_PARSERERROR) { /* overwrite last error */ we = ins_we; } else { /* add a new error */ we = yasm_xmalloc(sizeof(errwarn_data)); we->type = WE_UNKNOWN; we->line = line; we->xrefline = 0; we->msg = NULL; we->xrefmsg = NULL; if (action == INS_HEAD) SLIST_INSERT_HEAD(&errwarns->errwarns, we, link); else if (action == INS_AFTER) { assert(ins_we != NULL); SLIST_INSERT_AFTER(ins_we, we, link); } else yasm_internal_error(N_("Unexpected errwarn insert action")); } /* Remember previous err/warn */ errwarns->previous_we = we; return we; } void yasm_error_clear(void) { if (yasm_estr) yasm_xfree(yasm_estr); if (yasm_exrefstr) yasm_xfree(yasm_exrefstr); yasm_eclass = YASM_ERROR_NONE; yasm_estr = NULL; yasm_exrefline = 0; yasm_exrefstr = NULL; } int yasm_error_matches(yasm_error_class eclass) { if (yasm_eclass == YASM_ERROR_NONE) return eclass == YASM_ERROR_NONE; if (yasm_eclass == YASM_ERROR_GENERAL) return eclass == YASM_ERROR_GENERAL; return (yasm_eclass & eclass) == eclass; } void yasm_error_set_va(yasm_error_class eclass, const char *format, va_list va) { if (yasm_eclass != YASM_ERROR_NONE) return; yasm_eclass = eclass; yasm_estr = yasm_xmalloc(MSG_MAXSIZE+1); #ifdef HAVE_VSNPRINTF vsnprintf(yasm_estr, MSG_MAXSIZE, yasm_gettext_hook(format), va); #else vsprintf(yasm_estr, yasm_gettext_hook(format), va); #endif } void yasm_error_set(yasm_error_class eclass, const char *format, ...) { va_list va; va_start(va, format); yasm_error_set_va(eclass, format, va); va_end(va); } void yasm_error_set_xref_va(unsigned long xrefline, const char *format, va_list va) { if (yasm_eclass != YASM_ERROR_NONE) return; yasm_exrefline = xrefline; yasm_exrefstr = yasm_xmalloc(MSG_MAXSIZE+1); #ifdef HAVE_VSNPRINTF vsnprintf(yasm_exrefstr, MSG_MAXSIZE, yasm_gettext_hook(format), va); #else vsprintf(yasm_exrefstr, yasm_gettext_hook(format), va); #endif } void yasm_error_set_xref(unsigned long xrefline, const char *format, ...) { va_list va; va_start(va, format); yasm_error_set_xref_va(xrefline, format, va); va_end(va); } void yasm_error_fetch(yasm_error_class *eclass, char **str, unsigned long *xrefline, char **xrefstr) { *eclass = yasm_eclass; *str = yasm_estr; *xrefline = yasm_exrefline; *xrefstr = yasm_exrefstr; yasm_eclass = YASM_ERROR_NONE; yasm_estr = NULL; yasm_exrefline = 0; yasm_exrefstr = NULL; } void yasm_warn_clear(void) { /* Delete all error/warnings */ while (!STAILQ_EMPTY(&yasm_warns)) { warn *w = STAILQ_FIRST(&yasm_warns); if (w->wstr) yasm_xfree(w->wstr); STAILQ_REMOVE_HEAD(&yasm_warns, link); yasm_xfree(w); } } yasm_warn_class yasm_warn_occurred(void) { if (STAILQ_EMPTY(&yasm_warns)) return YASM_WARN_NONE; return STAILQ_FIRST(&yasm_warns)->wclass; } void yasm_warn_set_va(yasm_warn_class wclass, const char *format, va_list va) { warn *w; if (!(warn_class_enabled & (1UL<wclass = wclass; w->wstr = yasm_xmalloc(MSG_MAXSIZE+1); #ifdef HAVE_VSNPRINTF vsnprintf(w->wstr, MSG_MAXSIZE, yasm_gettext_hook(format), va); #else vsprintf(w->wstr, yasm_gettext_hook(format), va); #endif STAILQ_INSERT_TAIL(&yasm_warns, w, link); } void yasm_warn_set(yasm_warn_class wclass, const char *format, ...) { va_list va; va_start(va, format); yasm_warn_set_va(wclass, format, va); va_end(va); } void yasm_warn_fetch(yasm_warn_class *wclass, char **str) { warn *w = STAILQ_FIRST(&yasm_warns); if (!w) { *wclass = YASM_WARN_NONE; *str = NULL; return; } *wclass = w->wclass; *str = w->wstr; STAILQ_REMOVE_HEAD(&yasm_warns, link); yasm_xfree(w); } void yasm_warn_enable(yasm_warn_class num) { warn_class_enabled |= (1UL<errwarns); errwarns->ecount = 0; errwarns->wcount = 0; errwarns->previous_we = NULL; return errwarns; } void yasm_errwarns_destroy(yasm_errwarns *errwarns) { errwarn_data *we; /* Delete all error/warnings */ while (!SLIST_EMPTY(&errwarns->errwarns)) { we = SLIST_FIRST(&errwarns->errwarns); if (we->msg) yasm_xfree(we->msg); if (we->xrefmsg) yasm_xfree(we->xrefmsg); SLIST_REMOVE_HEAD(&errwarns->errwarns, link); yasm_xfree(we); } yasm_xfree(errwarns); } void yasm_errwarn_propagate(yasm_errwarns *errwarns, unsigned long line) { if (yasm_eclass != YASM_ERROR_NONE) { errwarn_data *we = errwarn_data_new(errwarns, line, 1); yasm_error_class eclass; yasm_error_fetch(&eclass, &we->msg, &we->xrefline, &we->xrefmsg); if (eclass != YASM_ERROR_GENERAL && (eclass & YASM_ERROR_PARSE) == YASM_ERROR_PARSE) we->type = WE_PARSERERROR; else we->type = WE_ERROR; errwarns->ecount++; } while (!STAILQ_EMPTY(&yasm_warns)) { errwarn_data *we = errwarn_data_new(errwarns, line, 0); yasm_warn_class wclass; yasm_warn_fetch(&wclass, &we->msg); we->type = WE_WARNING; errwarns->wcount++; } } unsigned int yasm_errwarns_num_errors(yasm_errwarns *errwarns, int warning_as_error) { if (warning_as_error) return errwarns->ecount+errwarns->wcount; else return errwarns->ecount; } void yasm_errwarns_output_all(yasm_errwarns *errwarns, yasm_linemap *lm, int warning_as_error, yasm_print_error_func print_error, yasm_print_warning_func print_warning) { errwarn_data *we; const char *filename, *xref_filename; unsigned long line, xref_line; /* If we're treating warnings as errors, tell the user about it. */ if (warning_as_error && warning_as_error != 2) { print_error("", 0, yasm_gettext_hook(N_("warnings being treated as errors")), NULL, 0, NULL); warning_as_error = 2; } /* Output error/warnings. */ SLIST_FOREACH(we, &errwarns->errwarns, link) { /* Output error/warning */ yasm_linemap_lookup(lm, we->line, &filename, &line); if (we->xrefline) yasm_linemap_lookup(lm, we->xrefline, &xref_filename, &xref_line); else { xref_filename = NULL; xref_line = 0; } if (we->type == WE_ERROR || we->type == WE_PARSERERROR) print_error(filename, line, we->msg, xref_filename, xref_line, we->xrefmsg); else print_warning(filename, line, we->msg); } } void yasm__fatal(const char *message, ...) { va_list va; va_start(va, message); yasm_fatal(message, va); /*@notreached@*/ va_end(va); } void yasm__fatal_missing_input_file(const char *message, const char *filename) { fprintf(stderr, "%s: %s %s (%s)\n", yasm_gettext_hook(N_("FATAL")), yasm_gettext_hook(message), strerror(errno), yasm_gettext_hook(filename)); exit(EXIT_FAILURE); }