mirror of https://github.com/yasm/yasm.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
401 lines
10 KiB
401 lines
10 KiB
/* |
|
* Error and warning reporting and related functions. |
|
* |
|
* Copyright (C) 2001 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. |
|
*/ |
|
#define YASM_LIB_INTERNAL |
|
#include "util.h" |
|
/*@unused@*/ RCSID("$Id$"); |
|
|
|
#include <ctype.h> |
|
|
|
#ifdef STDC_HEADERS |
|
# include <stdarg.h> |
|
#endif |
|
|
|
#include "coretype.h" |
|
|
|
#include "linemgr.h" |
|
#include "errwarn.h" |
|
|
|
|
|
#define MSG_MAXSIZE 1024 |
|
|
|
/* 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; |
|
|
|
/* Enabled warnings. See errwarn.h for a list. */ |
|
static unsigned long warn_class_enabled; |
|
|
|
/* Total error count */ |
|
static unsigned int error_count; |
|
|
|
/* Total warning count */ |
|
static unsigned int warning_count; |
|
|
|
typedef /*@reldef@*/ SLIST_HEAD(errwarndatahead_s, errwarn_data) |
|
errwarndatahead; |
|
static /*@only@*/ /*@null@*/ errwarndatahead errwarns; |
|
|
|
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 displine; |
|
|
|
/* FIXME: This should not be a fixed size. But we don't have vasprintf() |
|
* right now. */ |
|
char msg[MSG_MAXSIZE]; |
|
} errwarn_data; |
|
|
|
/* Last inserted error/warning. Used to speed up insertions. */ |
|
static /*@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<<YASM_WARN_GENERAL) | (1UL<<YASM_WARN_UNREC_CHAR) | |
|
(1UL<<YASM_WARN_PREPROC) | (0UL<<YASM_WARN_ORPHAN_LABEL); |
|
|
|
error_count = 0; |
|
warning_count = 0; |
|
SLIST_INIT(&errwarns); |
|
previous_we = NULL; |
|
} |
|
|
|
void |
|
yasm_errwarn_cleanup(void) |
|
{ |
|
errwarn_data *we; |
|
|
|
/* Delete all error/warnings */ |
|
while (!SLIST_EMPTY(&errwarns)) { |
|
we = SLIST_FIRST(&errwarns); |
|
|
|
SLIST_REMOVE_HEAD(&errwarns, link); |
|
yasm_xfree(we); |
|
} |
|
} |
|
|
|
/* Convert a possibly unprintable character into a printable string, using |
|
* standard cat(1) convention for unprintable characters. |
|
*/ |
|
char * |
|
yasm__conv_unprint(int ch) |
|
{ |
|
int pos = 0; |
|
|
|
if (((ch & ~0x7F) != 0) /*!isascii(ch)*/ && !isprint(ch)) { |
|
unprint[pos++] = 'M'; |
|
unprint[pos++] = '-'; |
|
ch &= toascii(ch); |
|
} |
|
if (iscntrl(ch)) { |
|
unprint[pos++] = '^'; |
|
unprint[pos++] = (ch == '\177') ? '?' : ch | 0100; |
|
} else |
|
unprint[pos++] = ch; |
|
unprint[pos] = '\0'; |
|
|
|
return unprint; |
|
} |
|
|
|
/* Report an internal error. Essentially a fatal error with trace info. |
|
* Exit immediately because it's essentially an assert() trap. |
|
*/ |
|
static void |
|
def_internal_error_(const char *file, unsigned int line, const char *message) |
|
{ |
|
fprintf(stderr, |
|
yasm_gettext_hook(N_("INTERNAL ERROR at %s, line %u: %s\n")), |
|
file, line, yasm_gettext_hook(message)); |
|
#ifdef HAVE_ABORT |
|
abort(); |
|
#else |
|
exit(EXIT_FAILURE); |
|
#endif |
|
} |
|
|
|
/* Report a fatal error. These are unrecoverable (such as running out of |
|
* memory), so just exit immediately. |
|
*/ |
|
static void |
|
def_fatal(const char *fmt, va_list va) |
|
{ |
|
fprintf(stderr, "%s: ", yasm_gettext_hook(N_("FATAL"))); |
|
vfprintf(stderr, yasm_gettext_hook(fmt), va); |
|
fputc('\n', stderr); |
|
exit(EXIT_FAILURE); |
|
} |
|
|
|
/* Create an errwarn structure in the correct linked list location. |
|
* If replace_parser_error is nonzero, overwrites the last error if its |
|
* type is WE_PARSERERROR. |
|
*/ |
|
static errwarn_data * |
|
errwarn_data_new(unsigned long line, unsigned long displine, |
|
int replace_parser_error) |
|
{ |
|
errwarn_data *first, *next, *ins_we, *we; |
|
enum { INS_NONE, INS_HEAD, INS_AFTER } action = INS_NONE; |
|
|
|
/* Find the entry with either line=line or the last one with line<line. |
|
* Start with the last entry added to speed the search. |
|
*/ |
|
ins_we = previous_we; |
|
first = SLIST_FIRST(&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->displine = displine; |
|
|
|
if (action == INS_HEAD) |
|
SLIST_INSERT_HEAD(&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 */ |
|
previous_we = we; |
|
|
|
return we; |
|
} |
|
|
|
/* Register an error at line line, displaying line displine. Does not print |
|
* the error, only stores it for output_all() to print. |
|
*/ |
|
void |
|
yasm__error_va_at(unsigned long line, unsigned long displine, const char *fmt, |
|
va_list va) |
|
{ |
|
errwarn_data *we = errwarn_data_new(line, displine, 1); |
|
|
|
we->type = WE_ERROR; |
|
|
|
#ifdef HAVE_VSNPRINTF |
|
vsnprintf(we->msg, MSG_MAXSIZE, yasm_gettext_hook(fmt), va); |
|
#else |
|
vsprintf(we->msg, yasm_gettext_hook(fmt), va); |
|
#endif |
|
|
|
error_count++; |
|
} |
|
|
|
/* Register an warning at line line, displaying line displine. Does not print |
|
* the warning, only stores it for output_all() to print. |
|
*/ |
|
void |
|
yasm__warning_va_at(yasm_warn_class num, unsigned long line, |
|
unsigned long displine, const char *fmt, va_list va) |
|
{ |
|
errwarn_data *we; |
|
|
|
if (!(warn_class_enabled & (1UL<<num))) |
|
return; /* warning is part of disabled class */ |
|
|
|
we = errwarn_data_new(line, displine, 0); |
|
|
|
we->type = WE_WARNING; |
|
|
|
#ifdef HAVE_VSNPRINTF |
|
vsnprintf(we->msg, MSG_MAXSIZE, yasm_gettext_hook(fmt), va); |
|
#else |
|
vsprintf(we->msg, yasm_gettext_hook(fmt), va); |
|
#endif |
|
|
|
warning_count++; |
|
} |
|
|
|
/* Register an error at line line. Does not print the error, only stores it |
|
* for output_all() to print. |
|
*/ |
|
void |
|
yasm__error(unsigned long line, const char *fmt, ...) |
|
{ |
|
va_list va; |
|
va_start(va, fmt); |
|
yasm__error_va_at(line, line, fmt, va); |
|
va_end(va); |
|
} |
|
|
|
/* Register an error at line line, displaying line displine. Does not print |
|
* the error, only stores it for output_all() to print. |
|
*/ |
|
void |
|
yasm__error_at(unsigned long line, unsigned long displine, const char *fmt, |
|
...) |
|
{ |
|
va_list va; |
|
va_start(va, fmt); |
|
yasm__error_va_at(line, displine, fmt, va); |
|
va_end(va); |
|
} |
|
|
|
/* Register an warning at line line. Does not print the warning, only stores |
|
* it for output_all() to print. |
|
*/ |
|
void |
|
yasm__warning(yasm_warn_class num, unsigned long line, const char *fmt, ...) |
|
{ |
|
va_list va; |
|
va_start(va, fmt); |
|
yasm__warning_va_at(num, line, line, fmt, va); |
|
va_end(va); |
|
} |
|
|
|
/* Register an warning at line line, displaying line displine. Does not print |
|
* the warning, only stores it for output_all() to print. |
|
*/ |
|
void |
|
yasm__warning_at(yasm_warn_class num, unsigned long line, |
|
unsigned long displine, const char *fmt, ...) |
|
{ |
|
va_list va; |
|
va_start(va, fmt); |
|
yasm__warning_va_at(num, line, line, fmt, va); |
|
va_end(va); |
|
} |
|
|
|
/* Parser error handler. Moves YACC-style error into our error handling |
|
* system. |
|
*/ |
|
void |
|
yasm__parser_error(unsigned long line, const char *s) |
|
{ |
|
yasm__error(line, N_("parser error: %s"), s); |
|
previous_we->type = WE_PARSERERROR; |
|
} |
|
|
|
void |
|
yasm_warn_enable(yasm_warn_class num) |
|
{ |
|
warn_class_enabled |= (1UL<<num); |
|
} |
|
|
|
void |
|
yasm_warn_disable(yasm_warn_class num) |
|
{ |
|
warn_class_enabled &= ~(1UL<<num); |
|
} |
|
|
|
void |
|
yasm_warn_disable_all(void) |
|
{ |
|
warn_class_enabled = 0; |
|
} |
|
|
|
unsigned int |
|
yasm_get_num_errors(int warning_as_error) |
|
{ |
|
if (warning_as_error) |
|
return error_count+warning_count; |
|
else |
|
return error_count; |
|
} |
|
|
|
void |
|
yasm_errwarn_output_all(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; |
|
unsigned long 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"))); |
|
warning_as_error = 2; |
|
} |
|
|
|
/* Output error/warnings. */ |
|
SLIST_FOREACH(we, &errwarns, link) { |
|
/* Output error/warning */ |
|
yasm_linemap_lookup(lm, we->displine, &filename, &line); |
|
if (we->type == WE_ERROR) |
|
print_error(filename, line, we->msg); |
|
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); |
|
}
|
|
|