|
|
|
/*
|
|
|
|
* Error and warning reporting and related functions.
|
|
|
|
*
|
|
|
|
* Copyright (C) 2001 Peter Johnson
|
|
|
|
*
|
|
|
|
* This file is part of YASM.
|
|
|
|
*
|
|
|
|
* YASM is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* YASM is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
*/
|
|
|
|
#include "util.h"
|
|
|
|
/*@unused@*/ RCSID("$IdPath$");
|
|
|
|
|
|
|
|
#include <ctype.h>
|
|
|
|
|
|
|
|
#ifdef STDC_HEADERS
|
|
|
|
# include <stdarg.h>
|
|
|
|
# include <assert.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef gettext_noop
|
|
|
|
#define N_(String) gettext_noop(String)
|
|
|
|
#else
|
|
|
|
#define N_(String) (String)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "globals.h"
|
|
|
|
#include "errwarn.h"
|
|
|
|
|
|
|
|
|
|
|
|
/* Total error count for entire assembler run.
|
|
|
|
* Assembler should exit with EXIT_FAILURE if this is >= 0 on finish. */
|
|
|
|
static unsigned int error_count = 0;
|
|
|
|
|
|
|
|
/* Total warning count for entire assembler run.
|
|
|
|
* Should not affect exit value of assembler. */
|
|
|
|
static unsigned int warning_count = 0;
|
|
|
|
|
|
|
|
/* See errwarn.h for constants that match up to these strings.
|
|
|
|
* When adding a string here, keep errwarn.h in sync! */
|
|
|
|
|
|
|
|
/* Fatal error messages. Match up with fatal_num enum in errwarn.h. */
|
|
|
|
/*@-observertrans@*/
|
|
|
|
static const char *fatal_msgs[] = {
|
|
|
|
N_("unknown"),
|
|
|
|
N_("out of memory")
|
|
|
|
};
|
|
|
|
/*@=observertrans@*/
|
|
|
|
|
|
|
|
typedef /*@reldef@*/ STAILQ_HEAD(errwarnhead_s, errwarn_s) errwarnhead;
|
|
|
|
static /*@only@*/ /*@null@*/ errwarnhead *errwarns = (errwarnhead *)NULL;
|
|
|
|
|
|
|
|
typedef struct errwarn_s {
|
|
|
|
/*@reldef@*/ STAILQ_ENTRY(errwarn_s) link;
|
|
|
|
|
|
|
|
enum { WE_ERROR, WE_WARNING } type;
|
|
|
|
|
|
|
|
/*@dependent@*/ const char *filename;
|
|
|
|
unsigned long line;
|
|
|
|
/* FIXME: This should not be a fixed size. But we don't have vasprintf()
|
|
|
|
* right now. */
|
|
|
|
char msg[1024];
|
|
|
|
} errwarn;
|
|
|
|
|
|
|
|
/* Line number of the previous error. Set and checked by Error(). */
|
|
|
|
static unsigned long previous_error_line = 0;
|
|
|
|
|
|
|
|
/* Is the last error a parser error? Error() lets other errors override parser
|
|
|
|
* errors. Set by yyerror(), read and reset by Error(). */
|
|
|
|
static int previous_error_parser = 0;
|
|
|
|
|
|
|
|
/* Line number of the previous warning. Set and checked by Warning(). */
|
|
|
|
static unsigned long previous_warning_line = 0;
|
|
|
|
|
|
|
|
/* Static buffer for use by conv_unprint(). */
|
|
|
|
static char unprint[5];
|
|
|
|
|
|
|
|
/* Convert a possibly unprintable character into a printable string, using
|
|
|
|
* standard cat(1) convention for unprintable characters. */
|
|
|
|
char *
|
|
|
|
conv_unprint(char 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Parser error handler. Moves error into our error handling system. */
|
|
|
|
void
|
|
|
|
ParserError(const char *s)
|
|
|
|
{
|
|
|
|
Error("%s %s", _("parser error:"), s);
|
|
|
|
previous_error_parser = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Report an internal error. Essentially a fatal error with trace info.
|
|
|
|
* Exit immediately because it's essentially an assert() trap. */
|
|
|
|
void
|
|
|
|
InternalError_(const char *file, unsigned int line, const char *message)
|
|
|
|
{
|
|
|
|
fprintf(stderr, _("INTERNAL ERROR at %s, line %d: %s\n"), file, line,
|
|
|
|
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. */
|
|
|
|
void
|
|
|
|
Fatal(fatal_num num)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s %s\n", _("FATAL:"), gettext(fatal_msgs[num]));
|
|
|
|
#ifdef HAVE_ABORT
|
|
|
|
abort();
|
|
|
|
#else
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Register an error. Uses argtypes as described above to specify the
|
|
|
|
* argument types. Does not print the error, only stores it for
|
|
|
|
* OutputAllErrorWarning() to print. */
|
|
|
|
void
|
|
|
|
Error(const char *fmt, ...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
errwarn *we;
|
|
|
|
|
|
|
|
if ((previous_error_line == line_number) && !previous_error_parser)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!errwarns) {
|
|
|
|
errwarns = xmalloc(sizeof(errwarnhead));
|
|
|
|
STAILQ_INIT(errwarns);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (previous_error_parser) {
|
|
|
|
/* overwrite last (parser) error */
|
|
|
|
we = STAILQ_LAST(errwarns, errwarn_s, link);
|
|
|
|
} else {
|
|
|
|
we = xmalloc(sizeof(errwarn));
|
|
|
|
|
|
|
|
we->type = WE_ERROR;
|
|
|
|
we->filename = in_filename;
|
|
|
|
we->line = line_number;
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(we != NULL);
|
|
|
|
|
|
|
|
va_start(ap, fmt);
|
|
|
|
vsprintf(we->msg, fmt, ap);
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
/*@-branchstate@*/
|
|
|
|
if (!previous_error_parser)
|
|
|
|
STAILQ_INSERT_TAIL(errwarns, we, link);
|
|
|
|
/*@=branchstate@*/
|
|
|
|
|
|
|
|
previous_error_line = line_number;
|
|
|
|
previous_error_parser = 0;
|
|
|
|
|
|
|
|
error_count++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Register a warning. Uses argtypes as described above to specify the
|
|
|
|
* argument types. Does not print the warning, only stores it for
|
|
|
|
* OutputAllErrorWarning() to print. */
|
|
|
|
void
|
|
|
|
Warning(const char *fmt, ...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
errwarn *we;
|
|
|
|
|
|
|
|
if (previous_warning_line == line_number)
|
|
|
|
return;
|
|
|
|
|
|
|
|
previous_warning_line = line_number;
|
|
|
|
|
|
|
|
we = xmalloc(sizeof(errwarn));
|
|
|
|
|
|
|
|
we->type = WE_WARNING;
|
|
|
|
we->filename = in_filename;
|
|
|
|
we->line = line_number;
|
|
|
|
va_start(ap, fmt);
|
|
|
|
vsprintf(we->msg, fmt, ap);
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
if (!errwarns) {
|
|
|
|
errwarns = xmalloc(sizeof(errwarnhead));
|
|
|
|
STAILQ_INIT(errwarns);
|
|
|
|
}
|
|
|
|
STAILQ_INSERT_TAIL(errwarns, we, link);
|
|
|
|
warning_count++;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
ErrorNow(const char *fmt, ...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
va_start(ap, fmt);
|
|
|
|
vfprintf(stderr, fmt, ap);
|
|
|
|
va_end(ap);
|
|
|
|
fprintf(stderr, "\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
WarningNow(const char *fmt, ...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
fprintf(stderr, "%s ", _("warning:"));
|
|
|
|
va_start(ap, fmt);
|
|
|
|
vfprintf(stderr, fmt, ap);
|
|
|
|
va_end(ap);
|
|
|
|
fprintf(stderr, "\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
ErrorAt(const char *filename, unsigned long line, const char *fmt, ...)
|
|
|
|
{
|
|
|
|
/* XXX: Should insert into list instead of printing immediately */
|
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
fprintf(stderr, "%s:%lu: ", filename?filename:"(NULL)", line);
|
|
|
|
va_start(ap, fmt);
|
|
|
|
vfprintf(stderr, fmt, ap);
|
|
|
|
va_end(ap);
|
|
|
|
fprintf(stderr, "\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
WarningAt(const char *filename, unsigned long line, const char *fmt, ...)
|
|
|
|
{
|
|
|
|
/* XXX: Should insert into list instead of printing immediately */
|
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
fprintf(stderr, "%s:%lu: %s ", filename?filename:"NULL", line,
|
|
|
|
_("warning:"));
|
|
|
|
va_start(ap, fmt);
|
|
|
|
vfprintf(stderr, fmt, ap);
|
|
|
|
va_end(ap);
|
|
|
|
fprintf(stderr, "\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Output all previously stored errors and warnings to stderr. */
|
|
|
|
unsigned int
|
|
|
|
OutputAllErrorWarning(void)
|
|
|
|
{
|
|
|
|
errwarn *we, *we2;
|
|
|
|
|
|
|
|
/* If errwarns hasn't been initialized, there are no messages. */
|
|
|
|
if (!errwarns)
|
|
|
|
return error_count;
|
|
|
|
|
|
|
|
/* Output error and warning messages. */
|
|
|
|
STAILQ_FOREACH(we, errwarns, link) {
|
|
|
|
if (we->type == WE_ERROR)
|
|
|
|
fprintf(stderr, "%s:%lu: %s\n", we->filename, we->line, we->msg);
|
|
|
|
else
|
|
|
|
fprintf(stderr, "%s:%lu: %s %s\n", we->filename, we->line,
|
|
|
|
_("warning:"), we->msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Delete messages. */
|
|
|
|
we = STAILQ_FIRST(errwarns);
|
|
|
|
while (we) {
|
|
|
|
we2 = STAILQ_NEXT(we, link);
|
|
|
|
xfree(we);
|
|
|
|
we = we2;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Return the total error count up to this point. */
|
|
|
|
return error_count;
|
|
|
|
}
|