Rev 6 | Blame | Compare with Previous | Last modification | View Log | RSS feed
/*
* Copyright (c) 2002-2006 The TenDRA Project <http://www.tendra.org/>.
* All rights reserved.
*
* 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.
* 3. Neither the name of The TenDRA Project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific, prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 COPYRIGHT HOLDERS OR
* 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.
*
* $Id$
*/
/*
Crown Copyright (c) 1997
This TenDRA(r) Computer Program is subject to Copyright
owned by the United Kingdom Secretary of State for Defence
acting through the Defence Evaluation and Research Agency
(DERA). It is made available to Recipients with a
royalty-free licence for its use, reproduction, transfer
to other parties and amendment for any purpose not excluding
product development provided that any such use et cetera
shall be deemed to be acceptance of the following conditions:-
(1) Its Recipients shall ensure that this Notice is
reproduced upon any copies or amended versions of it;
(2) Any amended version of it shall be clearly marked to
show both the nature of and the organisation responsible
for the relevant amendment or amendments;
(3) Its onward transfer from a recipient to another
party shall be deemed to be that party's acceptance of
these conditions;
(4) DERA gives no warranty or assurance as to its
quality or suitability for any purpose and DERA accepts
no liability whatsoever in relation to any use to which
it may be put.
*/
#include "config.h"
#if FS_STDARG
#include <stdarg.h>
#else
#include <varargs.h>
#endif
#include <limits.h>
#include "system.h"
#include "version.h"
#include "c_types.h"
#include "err_ext.h"
#include "exp_ops.h"
#include "loc_ext.h"
#include "error.h"
#include "catalog.h"
#include "option.h"
#include "tdf.h"
#include "basetype.h"
#include "buffer.h"
#include "capsule.h"
#include "dump.h"
#include "file.h"
#include "lex.h"
#include "literal.h"
#include "preproc.h"
#include "print.h"
#include "save.h"
#include "statement.h"
#include "ustring.h"
#include "xalloc.h"
/*
PROGRAM NAME
These variables give the program name and version number.
*/
CONST char *progname = NULL;
CONST char *progvers = NULL;
/*
SET PROGRAM NAME
This routine sets the program name to the basename of prog and the
program version to vers.
*/
void
set_progname(CONST char *prog, CONST char *vers)
{
error_file = stderr;
if (prog) {
char *s = strrchr(prog, '/');
if (s)prog = s + 1;
s = strrchr(prog, file_sep);
if (s)prog = s + 1;
progname = prog;
} else {
progname = "unknown";
}
progvers = vers;
return;
}
/*
PRINT VERSION NUMBER
This routine prints the program name and version number plus the
versions of C++ and the TDF specification it supports to the print
buffer, returning the result.
*/
string
report_version(int vers)
{
BUFFER *bf = clear_buffer(&print_buff, NIL(FILE));
bfprintf(bf, "%x: Version %x", progname, progvers);
if (vers) {
char buff[20];
char *v = LANGUAGE_VERSION;
sprintf_v(buff, "%.4s-%.2s", v, v + 4);
bfprintf(bf, " (%x %x", LANGUAGE_NAME, buff);
bfprintf(bf, " to TDF %d.%d", TDF_major, TDF_minor);
#ifdef RELEASE
bfprintf(bf, ", Release %x", RELEASE);
#endif
bfprintf(bf, ")");
}
return (bf->start);
}
/*
CURRENT FILE POSITION
This structure gives the current file position. This consists of a
string, giving the file name, an integer, giving the line number, and
a pointer to any previous positions from which this is derived by a
#include directive.
*/
LOCATION crt_loc = NULL_loc;
LOCATION builtin_loc = NULL_loc;
/*
ERROR REPORTING VARIABLES
These variables are used by the error reporting routines. exit_status
gives the overall status of the program - it is EXIT_SUCCESS if no
serious errors have occurred, and EXIT_FAILURE otherwise. A count of
the number of serious errors is kept in number_errors, up to a maximum
of max_errors.
*/
FILE *error_file = NULL;
int exit_status = EXIT_SUCCESS;
unsigned long number_errors = 0;
unsigned long number_warnings = 0;
unsigned long max_errors = 32;
int error_threshold = ERROR_NONE;
int no_error_args = 0;
int verbose = 0;
static int print_short = 0;
static int print_error_loc = 0;
static int print_error_name = 0;
static int print_error_source = 0;
static int print_iso_ref = 1;
static int print_ansi_ref = 0;
/*
PROCESS AN ERROR FORMATTING OPTION
This routine processes the error formatting options given by opt.
This corresponds to the command-line option '-mopt'.
*/
void
error_option(string opt)
{
int out = 1;
character c;
while (c = *(opt++), c != 0) {
switch (c) {
case 'a':
print_ansi_ref = out;
break;
case 'c':
print_error_source = out;
break;
case 'e':
print_error_name = out;
break;
case 'f':
good_fseek = out;
break;
case 'g':
record_location = out;
break;
case 'i':
good_stat = out;
break;
case 'k':
output_spec = out;
break;
case 'l':
print_error_loc = out;
break;
case 'm':
allow_multibyte = out;
break;
case 'p':
preproc_space = out;
break;
case 'q':
print_short = out;
break;
case 'r':
allow_dos_newline = out;
break;
case 's':
print_iso_ref = out;
break;
case 't':
print_type_alias = out;
break;
case 'x':
print_c_style = out;
break;
case '+':
out = 1;
break;
case '-':
out = 0;
break;
case 'o': {
error_file = (out ? stdout : stderr);
break;
}
case 'w': {
OPTION sev = OPTION_WARN;
if (out) {
sev = OPTION_OFF;
}
OPT_CATALOG[OPT_warning].def[0] = sev;
OPT_CATALOG[OPT_warning].def[1] = sev;
break;
}
case 'z': {
OPTION sev = OPTION_ON;
if (out) {
sev = OPTION_WARN;
}
OPT_CATALOG[OPT_error].def[0] = sev;
OPT_CATALOG[OPT_error].def[1] = sev;
break;
}
default : {
/* Unknown output options */
CONST char *err =
"Unknown error formatting option, '%c'";
error(ERROR_WARNING, err,(int)c);
break;
}
}
}
return;
}
/*
ERROR MESSAGE PARAMETERS
These macros are used to parameterise the form of the error messages.
*/
#define HEADER_FATAL "Fatal error"
#define HEADER_SERIOUS "Error"
#define HEADER_WARNING "Warning"
#define HEADER_INTERNAL "Internal error"
#define HEADER_ASSERT "Assertion"
#define PRINT_HEADER(M, L, F)\
print_location((L), (F));\
fputs_v(": ",(F));\
fputs_v((M), (F));\
fputs_v(":\n",(F))
#define PRINT_SOURCE(L, F)\
print_source((L), 1, 0, " ",(F))
#define PRINT_FROM(L, F)\
fputs_v(" (included from ",(F));\
print_location((L), (F));\
fputs_v(")\n",(F))
#define PRINT_START(M, F)\
fputs_v((M), (F));\
fputs_v(": ",(F))
#define MESSAGE_START " "
#define MESSAGE_END ".\n"
#define MESSAGE_TERM "\n"
#define MESSAGE_NAME "[%x]: "
#define MESSAGE_ISO "[ISO %x]: "
#define MESSAGE_ANSI "[ANSI "
#define MESSAGE_ANSI_END "]: "
#define MESSAGE_PRAGMA "[Pragma]: "
#define MESSAGE_PRINTF "[Printf]: "
#define MESSAGE_SYNTAX "[Syntax]: "
#define MESSAGE_TOKEN "[Token]: "
/*
FORWARD DECLARATION
The following forward declaration is necessary.
*/
static void print_error_msg(ERROR, LOCATION *, FILE *);
/*
TERMINATE PROGRAM
This routine is called to terminate the program. It tidies up any
output files and error messages and then exits. fatal is true after
a memory allocation error when the amount of tidying up which can
be done is limited, although some memory is freed to give a little
leeway.
*/
void
term_error(int fatal)
{
if (fatal) {
/* Cope with memory allocation errors */
exit_status = EXIT_FAILURE;
output_capsule = 0;
output_spec = 0;
free_buffer(&token_buff);
}
if (do_dump) {
/* End dump file */
term_dump();
do_dump = 0;
}
if (do_error) {
/* Report errors in dump file */
unsigned long e = number_errors;
unsigned long w = number_warnings;
int sev = (e ? ERROR_SERIOUS : ERROR_WARNING);
do_error = 0;
error(sev, "%lu error(s), %lu warning(s)", e, w);
}
if (output_name[OUTPUT_SPEC]) {
/* Write spec file */
begin_spec();
end_spec();
}
if (output_tdf) {
/* Write capsule */
write_capsule();
}
exit(exit_status);
}
/*
ERROR BREAKPOINT ROUTINE
This routine is intended to aid in debugging. It is called just after
any error message is printed.
*/
static void
error_break(void)
{
return;
}
/*
FIND AN ERROR MESSAGE HEADER
This routine returns the error message header for an error of severity
sev. It also updates the internal flags.
*/
static CONST char *
error_header(int sev)
{
CONST char *msg;
switch (sev) {
case ERROR_FATAL: {
msg = HEADER_FATAL;
exit_status = EXIT_FAILURE;
output_capsule = 0;
number_errors++;
break;
}
case ERROR_INTERNAL: {
msg = HEADER_INTERNAL;
if (error_severity[OPTION_ON] == ERROR_SERIOUS) {
exit_status = EXIT_FAILURE;
output_capsule = 0;
}
number_errors++;
break;
}
default : {
msg = HEADER_SERIOUS;
if (error_severity[OPTION_ON] == ERROR_SERIOUS) {
exit_status = EXIT_FAILURE;
output_capsule = 0;
}
number_errors++;
break;
}
case ERROR_WARNING: {
msg = HEADER_WARNING;
number_warnings++;
break;
}
}
return (msg);
}
/*
PRINT A LOCATION TO A FILE
This routine prints the location loc to the file f.
*/
static void
print_location(LOCATION *loc, FILE *f)
{
BUFFER *bf = clear_buffer(&print_buff, f);
IGNORE print_loc(loc, NIL(LOCATION), bf, 0);
output_buffer(bf, 0);
return;
}
/*
CONVERT AN ISO SECTION NUMBER TO AN ANSI SECTION NUMBER
The ISO C standard was based on the ANSI C standard but the sections
were renumbered. The ISO and ANSI C++ standards are identical. This
routine converts the ISO section number s to the corresponding ANSI
section number, printing it to the buffer bf.
*/
static void
iso_to_ansi(BUFFER *bf, CONST char *s)
{
#if LANGUAGE_C
char c;
unsigned long n = 0;
CONST char *p = "1.";
CONST char *q = s;
while (c = *q,(c >= '0' && c <= '9')) {
n = 10 * n + (unsigned long)(c - '0');
q++;
}
if (n == 0) {
bfprintf(bf, "%x", s);
} else {
switch (n) {
case 1:
n = 2;
break;
case 2:
n = 3;
break;
case 3:
n = 6;
break;
case 4:
n = 7;
break;
default:
p = "";
n -= 3;
break;
}
bfprintf(bf, "%x%lu%x", p, n, q);
}
#else
bfprintf(bf, "%x", s);
#endif
return;
}
/*
PRINT THE START OF AN ERROR MESSAGE
This routine prints the start of an error message of severity sev and
location loc to the file f.
*/
static void
print_error_start(FILE *f, LOCATION *loc, int sev)
{
CONST char *msg = error_header(sev);
if (loc) {
PRINT_HEADER(msg, loc, f);
if (print_error_loc) {
/* Print full error location */
LOCATION floc;
LOCATION *ploc = loc;
for (;;) {
PTR(LOCATION)from;
PTR(POSITION)posn = ploc->posn;
if (IS_NULL_ptr(posn)) {
break;
}
from = DEREF_ptr(posn_from(posn));
if (IS_NULL_ptr(from)) {
break;
}
DEREF_loc(from, floc);
ploc = &floc;
PRINT_FROM(ploc, f);
}
}
if (print_error_source) {
/* Print source line */
PRINT_SOURCE(loc, f);
}
} else {
PRINT_START(msg, f);
}
return;
}
/*
PRINT THE END OF AN ERROR MESSAGE
This routine prints the end of an error message of severity sev to
the file f.
*/
static void
print_error_end(FILE *f, int sev)
{
unsigned long n = number_errors;
if (n >= max_errors && sev != ERROR_FATAL) {
ERROR err = ERR_fail_too_many(n);
print_error_msg(err, &crt_loc, f);
sev = ERROR_FATAL;
}
fputs_v(MESSAGE_TERM, f);
error_break();
if (sev == ERROR_FATAL) {
term_error(0);
}
return;
}
/*
OPTION SEVERITY LEVELS
This table gives the mapping between options and error severity
levels.
*/
int error_severity[] = {
ERROR_NONE, /* OPTION_OFF */
ERROR_WARNING, /* OPTION_WARN */
ERROR_SERIOUS, /* OPTION_ON */
ERROR_WHATEVER /* OPTION_WHATEVER */
};
int default_severity[] = {
ERROR_NONE, /* OPTION_OFF */
ERROR_WARNING, /* OPTION_WARN */
ERROR_SERIOUS, /* OPTION_ON */
ERROR_WHATEVER /* OPTION_WHATEVER */
};
/*
CREATE AN ERROR STRUCTURE
This routine creates a structure for error n in the error catalogue.
Any extra arguments needed by the error should also be given. Because
of restrictions imposed by the way that stdarg is implemented, any
structure arguments need to be explicitly passed by reference. Note
that these arguments are stored in the result in reverse order.
*/
ERROR
make_error(int n, ...) /* VARARGS */
{
int sev;
ERROR e;
OPTION opt;
va_list args;
ERR_DATA *msg;
CONST char *s;
#if FS_STDARG
va_start(args, n);
#else
int n;
va_start(args);
n = va_arg(args, int);
#endif
/* Check severity level */
msg = ERR_CATALOG + n;
if (crt_opt) {
opt = crt_opt[msg->usage];
} else {
/* Can have errors before crt_opt is initialised */
opt = OPT_CATALOG[msg->usage].def[0];
}
sev = error_severity[opt];
if (sev == ERROR_NONE) {
va_end(args);
return (NULL_err);
}
/* Read arguments */
s = msg->signature;
if (s == NULL) {
MAKE_err_simple(sev, n, e);
} else {
unsigned i, m = (unsigned)strlen(s);
if (no_error_args)m = 0;
MAKE_err_simple_args(sev, n, m, e);
for (i = 0; i < m; i++) {
switch (s[i]) {
case ERR_KEY_BASE_TYPE: {
BASE_TYPE arg = va_arg(args, BASE_TYPE);
COPY_btype(err_arg(e, i, BASE_TYPE), arg);
break;
}
case ERR_KEY_CLASS_TYPE: {
CLASS_TYPE arg = va_arg(args, CLASS_TYPE);
COPY_ctype(err_arg(e, i, CLASS_TYPE), arg);
break;
}
case ERR_KEY_CV_SPEC: {
CV_SPEC arg = va_arg(args, CV_SPEC);
COPY_cv(err_arg(e, i, CV_SPEC), arg);
break;
}
case ERR_KEY_ACCESS:
case ERR_KEY_DECL_SPEC: {
DECL_SPEC arg = va_arg(args, DECL_SPEC);
COPY_dspec(err_arg(e, i, DECL_SPEC), arg);
break;
}
case ERR_KEY_FLOAT: {
FLOAT arg = va_arg(args, FLOAT);
COPY_flt(err_arg(e, i, FLOAT), arg);
break;
}
case ERR_KEY_HASHID: {
HASHID arg = va_arg(args, HASHID);
COPY_hashid(err_arg(e, i, HASHID), arg);
break;
}
case ERR_KEY_IDENTIFIER:
case ERR_KEY_LONG_ID: {
IDENTIFIER arg = va_arg(args, IDENTIFIER);
COPY_id(err_arg(e, i, IDENTIFIER), arg);
break;
}
case ERR_KEY_LEX: {
LEX arg = va_arg(args, LEX);
COPY_int(err_arg(e, i, LEX), arg);
break;
}
case ERR_KEY_NAMESPACE: {
NAMESPACE arg = va_arg(args, NAMESPACE);
COPY_nspace(err_arg(e, i, NAMESPACE), arg);
break;
}
case ERR_KEY_NAT: {
NAT arg = va_arg(args, NAT);
COPY_nat(err_arg(e, i, NAT), arg);
break;
}
case ERR_KEY_PPTOKEN_P: {
PPTOKEN_P arg = va_arg(args, PPTOKEN_P);
COPY_pptok(err_arg(e, i, PPTOKEN_P), arg);
break;
}
case ERR_KEY_PTR_LOC: {
PTR_LOC arg = va_arg(args, PTR_LOC);
COPY_ptr(err_arg(e, i, PTR_LOC), arg);
break;
}
case ERR_KEY_QUALIFIER: {
QUALIFIER arg = va_arg(args, QUALIFIER);
COPY_qual(err_arg(e, i, QUALIFIER), arg);
break;
}
case ERR_KEY_STRING: {
STRING arg = va_arg(args, STRING);
COPY_str(err_arg(e, i, STRING), arg);
break;
}
case ERR_KEY_TYPE: {
TYPE arg = va_arg(args, TYPE);
COPY_type(err_arg(e, i, TYPE), arg);
break;
}
case ERR_KEY_cint: {
cint arg = va_arg(args, cint);
COPY_int(err_arg(e, i, cint), arg);
break;
}
case ERR_KEY_cstring: {
cstring arg = va_arg(args, cstring);
string uarg = ustrlit(arg);
COPY_string(err_arg(e, i, string), uarg);
break;
}
case ERR_KEY_string: {
string arg = va_arg(args, string);
COPY_string(err_arg(e, i, string), arg);
break;
}
case ERR_KEY_ulong:
case ERR_KEY_ucint: {
ulong arg = va_arg(args, ulong);
COPY_ulong(err_arg(e, i, ulong), arg);
break;
}
case ERR_KEY_unsigned:
case ERR_KEY_plural: {
unsigned arg = va_arg(args, unsigned);
COPY_unsigned(err_arg(e, i, unsigned), arg);
break;
}
default : {
FAIL(Bad signature);
break;
}
}
}
}
va_end(args);
return (e);
}
/*
PRINT THE BODY OF AN ERROR STRUCTURE
This routine prints the body of the simple error message e to the
buffer bf.
*/
static void
print_error_body(ERROR e, LOCATION *loc, BUFFER *bf)
{
char c;
QUALIFIER qual = qual_none;
/* Extract error information */
int n = DEREF_int(err_simple_number(e));
unsigned sz = DEREF_unsigned(err_simple_size(e));
/* Look up error in catalogue */
ERR_DATA *msg = ERR_CATALOG + n;
CONST char *sig = msg->signature;
CONST char *s = msg->key_STD;
/* Print the error message */
if (s == NULL) return;
while (c = *(s++), c != 0) {
if (c == '%') {
/* Error argument - find number */
unsigned a;
c = *(s++);
if (c >= '0' && c <= '9') {
/* Arguments 0 to 9 */
a = (unsigned)(c - '0');
} else {
if (c != '%')bfputc(bf, '%');
bfputc(bf, c);
continue;
}
/* Find argument type */
if (a < sz) {
c = sig[a];
} else {
c = '?';
}
/* Print appropriate argument */
switch (c) {
case ERR_KEY_ACCESS: {
ACCESS arg = DEREF_dspec(err_arg(e, a, ACCESS));
IGNORE print_access(arg, bf, 0);
break;
}
case ERR_KEY_BASE_TYPE: {
BASE_TYPE arg;
arg = DEREF_btype(err_arg(e, a, BASE_TYPE));
IGNORE print_btype(arg, bf, 0);
break;
}
case ERR_KEY_CLASS_TYPE: {
CLASS_TYPE arg;
arg = DEREF_ctype(err_arg(e, a, CLASS_TYPE));
IGNORE print_ctype(arg, qual_none, 0, bf, 0);
break;
}
case ERR_KEY_CV_SPEC: {
CV_SPEC arg = DEREF_cv(err_arg(e, a, CV_SPEC));
if (!print_cv(arg, bf, 0)) {
bfprintf(bf, "<none>");
}
break;
}
case ERR_KEY_DECL_SPEC: {
DECL_SPEC arg;
arg = DEREF_dspec(err_arg(e, a, DECL_SPEC));
if (!print_dspec(arg, bf, 0)) {
bfprintf(bf, "<none>");
}
break;
}
case ERR_KEY_FLOAT: {
FLOAT arg = DEREF_flt(err_arg(e, a, FLOAT));
IGNORE print_flt(arg, bf, 0);
break;
}
case ERR_KEY_HASHID: {
HASHID arg = DEREF_hashid(err_arg(e, a, HASHID));
IGNORE print_hashid(arg, 1, 1, bf, 0);
break;
}
case ERR_KEY_IDENTIFIER: {
IDENTIFIER arg;
arg = DEREF_id(err_arg(e, a, IDENTIFIER));
IGNORE print_id_short(arg, qual, bf, 0);
qual = qual_none;
break;
}
case ERR_KEY_LEX: {
LEX arg = DEREF_int(err_arg(e, a, LEX));
IGNORE print_lex(arg, bf, 0);
break;
}
case ERR_KEY_LONG_ID: {
LONG_ID arg = DEREF_id(err_arg(e, a, LONG_ID));
IGNORE print_id_long(arg, qual, bf, 0);
qual = qual_none;
break;
}
case ERR_KEY_NAMESPACE: {
NAMESPACE arg;
arg = DEREF_nspace(err_arg(e, a, NAMESPACE));
IGNORE print_nspace(arg, qual_none, 0, bf, 0);
break;
}
case ERR_KEY_NAT: {
NAT arg = DEREF_nat(err_arg(e, a, NAT));
IGNORE print_nat(arg, 0, bf, 0);
break;
}
case ERR_KEY_PPTOKEN_P: {
PPTOKEN_P arg;
arg = DEREF_pptok(err_arg(e, a, PPTOKEN_P));
IGNORE print_pptok(arg, bf, 0);
break;
}
case ERR_KEY_PTR_LOC: {
PTR_LOC arg;
arg = DEREF_ptr(err_arg(e, a, PTR_LOC));
if (!IS_NULL_ptr(arg)) {
LOCATION ploc;
DEREF_loc(arg, ploc);
IGNORE print_loc(&ploc, loc, bf, 0);
} else {
IGNORE print_loc(loc, loc, bf, 0);
}
break;
}
case ERR_KEY_QUALIFIER: {
QUALIFIER arg;
arg = DEREF_qual(err_arg(e, a, QUALIFIER));
qual = arg;
break;
}
case ERR_KEY_STRING: {
STRING arg = DEREF_str(err_arg(e, a, STRING));
IGNORE print_str(arg, bf, 0);
break;
}
case ERR_KEY_TYPE: {
TYPE arg = DEREF_type(err_arg(e, a, TYPE));
IGNORE print_type(arg, bf, 0);
break;
}
case ERR_KEY_cint: {
cint arg = DEREF_int(err_arg(e, a, cint));
unsigned long ca = (unsigned long)arg;
print_char(ca, CHAR_SIMPLE, 0, bf);
break;
}
case ERR_KEY_plural: {
plural arg = DEREF_unsigned(err_arg(e, a, plural));
if (arg != 1)bfputc(bf, 's');
break;
}
case ERR_KEY_cstring:
case ERR_KEY_string: {
string arg =
DEREF_string(err_arg(e, a, string));
if (arg) {
ulong u;
while (u = (ulong)*(arg++), u != 0) {
print_char(u, CHAR_SIMPLE, 0,
bf);
}
}
break;
}
case ERR_KEY_ucint: {
ucint arg = DEREF_ulong(err_arg(e, a, ucint));
if (arg <= (ucint)0xffff) {
print_char(arg, CHAR_UNI4, 0, bf);
} else {
print_char(arg, CHAR_UNI8, 0, bf);
}
break;
}
case ERR_KEY_ulong: {
ulong arg = DEREF_ulong(err_arg(e, a, ulong));
bfprintf(bf, "%lu", arg);
break;
}
case ERR_KEY_unsigned: {
unsigned arg;
arg = DEREF_unsigned(err_arg(e, a, unsigned));
bfprintf(bf, "%u", arg);
break;
}
default : {
bfprintf(bf, "<arg%u>", a);
break;
}
}
} else {
/* Other characters */
bfputc(bf, c);
}
}
return;
}
/*
PRINT AN ERROR STRUCTURE
This routine prints the body of the error message given by e to the
file f.
*/
static void
print_error_msg(ERROR e, LOCATION *loc, FILE *f)
{
if (IS_err_simple(e)) {
/* Print simple error message */
BUFFER *bf;
int n = DEREF_int(err_simple_number(e));
ERR_DATA *msg = ERR_CATALOG + n;
ERR_PROPS props = msg->props;
int sev = DEREF_int(err_severity(e));
if (sev == ERROR_WHATEVER && print_short) return;
bf = clear_buffer(&print_buff, f);
if (loc)bfprintf(bf, MESSAGE_START);
if (print_error_name) {
CONST char *name = msg->name;
if (name)bfprintf(bf, MESSAGE_NAME, name);
}
if (!(props & ERR_PROP_non_iso) && print_iso_ref) {
CONST char *iso;
ERR_DATA *prev = msg;
while (iso = prev->key_ISO, iso == NULL) {
/* Scan back to current section number */
if (prev == ERR_CATALOG)break;
prev--;
}
msg->key_ISO = iso;
if (iso && iso[0]) {
if (print_ansi_ref) {
bfprintf(bf, MESSAGE_ANSI);
iso_to_ansi(bf, iso);
bfprintf(bf, MESSAGE_ANSI_END);
} else {
bfprintf(bf, MESSAGE_ISO, iso);
}
}
}
if (props) {
if (props & ERR_PROP_pragma) {
bfprintf(bf, MESSAGE_PRAGMA);
}
if (props & ERR_PROP_printf) {
bfprintf(bf, MESSAGE_PRINTF);
}
if (props & ERR_PROP_token) {
bfprintf(bf, MESSAGE_TOKEN);
}
if (props & ERR_PROP_syntax) {
bfprintf(bf, MESSAGE_SYNTAX);
}
}
print_error_body(e, loc, bf);
bfprintf(bf, MESSAGE_END);
output_buffer(bf, 1);
} else {
/* Print composite error message */
ERROR e1 = DEREF_err(err_compound_head(e));
ERROR e2 = DEREF_err(err_compound_tail(e));
print_error_msg(e1, loc, f);
print_error_msg(e2, loc, f);
}
return;
}
/*
DESTROY AN ERROR STRUCTURE
This routine destroys the error structure e. If d is false then the
first component of a compound error is not destroyed.
*/
void
destroy_error(ERROR e, int d)
{
if (!IS_NULL_err(e)) {
if (IS_err_simple(e)) {
if (d)DESTROY_err_simple_args(e);
} else {
int sev;
ERROR e1, e2;
DESTROY_err_compound(destroy, sev, e1, e2, e);
if (d)destroy_error(e1, 1);
destroy_error(e2, 1);
UNUSED(sev);
}
}
return;
}
/*
JOIN TWO ERROR STRUCTURES
This routine joins the error structures e1 and e2 into a single compound
error structure.
*/
ERROR
concat_error(ERROR e1, ERROR e2)
{
ERROR e;
int s1, s2;
if (IS_NULL_err(e1)) {
return (e2);
}
if (IS_NULL_err(e2)) {
return (e1);
}
s1 = DEREF_int(err_severity(e1));
s2 = DEREF_int(err_severity(e2));
if (s2 > s1) {
s1 = s2;
}
MAKE_err_compound(s1, e1, e2, e);
return (e);
}
/*
OPTIONALLY JOIN TWO ERROR STRUCTURES
This routine joins the error structures e1 and e2 into a single compound
error structure if e1 represents a serious error. Otherwise e2 is
destroyed and e1 is returned.
*/
ERROR
concat_warning(ERROR e1, ERROR e2)
{
ERROR e;
int s1, s2;
if (IS_NULL_err(e1)) {
return (e2);
}
if (IS_NULL_err(e2)) {
return (e1);
}
s1 = DEREF_int(err_severity(e1));
if (s1 > ERROR_WARNING) {
s2 = DEREF_int(err_severity(e2));
if (s2 > s1) {
s1 = s2;
}
MAKE_err_compound(s1, e1, e2, e);
} else {
destroy_error(e2, 1);
e = e1;
}
return (e);
}
/*
ADD AN ERROR TO A LIST
This routine adds the error e to the end of the list indicated by err.
If err is the null pointer then e is destroyed.
*/
void
add_error(ERROR *err, ERROR e)
{
if (!IS_NULL_err(e)) {
if (err) {
ERROR e1 = *err;
if (IS_NULL_err(e1)) {
*err = e;
} else {
int s1 = DEREF_int(err_severity(e1));
int s2 = DEREF_int(err_severity(e));
if (s2 > s1) {
s1 = s2;
}
MAKE_err_compound(s1, e1, e, *err);
}
} else {
destroy_error(e, 1);
}
}
return;
}
/*
STANDARD ERROR PREFIX
These variables give an error message which is added to the start
of any error before it is printed. The prefix error severity is not
taken into account in the overall error severity.
*/
static ERROR error_prefix = NULL_err;
/*
SET ERROR PREFIX
This routine sets the error prefix to be e, returning the previous
value.
*/
ERROR
set_prefix(ERROR e)
{
ERROR p = error_prefix;
error_prefix = e;
return (p);
}
/*
RESTORE ERROR PREFIX
This routine restores the error prefix to e, destroying the previous
value.
*/
void
restore_prefix(ERROR e)
{
destroy_error(error_prefix, 1);
error_prefix = e;
return;
}
/*
PRINT AN ERROR MESSAGE
This routine prints the error e at location loc.
*/
void
print_error(LOCATION *loc, ERROR e)
{
if (!IS_NULL_err(e)) {
int d = 1;
int sev = DEREF_int(err_severity(e));
if (sev > error_threshold) {
ERROR p = error_prefix;
if (!IS_NULL_err(p)) {
/* Add error prefix */
MAKE_err_compound(sev, p, e, e);
d = 0;
}
if (do_error && dump_error(e, loc, sev, 0)) {
/* Dump error to file */
unsigned long n;
IGNORE error_header(sev);
n = number_errors;
error_break();
if (sev == ERROR_FATAL)term_error(0);
if (n >= max_errors)term_error(0);
} else {
/* Print error to standard error */
FILE *f = error_file;
print_error_start(f, loc, sev);
print_error_msg(e, loc, f);
print_error_end(f, sev);
}
}
destroy_error(e, d);
}
return;
}
/*
CREATE AN INSTALLER ERROR EXPRESSION
This routine creates an install-time error expression for the error
e at the location loc.
*/
EXP
install_error(LOCATION *loc, ERROR e)
{
EXP a = NULL_exp;
if (!IS_NULL_err(e)) {
int sev = DEREF_int(err_severity(e));
if (sev > ERROR_WARNING) {
string s;
BUFFER *bf = clear_buffer(&print_buff, NIL(FILE));
if (loc) {
IGNORE print_loc(loc, NIL(LOCATION), bf, 0);
bfprintf(bf, ": ");
}
print_error_body(e, loc, bf);
bfputc(bf, 0);
s = xustrcpy(bf->start);
MAKE_exp_fail(type_bottom, s, a);
}
destroy_error(e, 1);
}
return (a);
}
/*
PRINT A SIMPLE ERROR
This routine prints a simple error message at the current location of
severity sev given by the printf style string s. Any extra arguments
needed by s should also be given.
*/
void
error(int sev, CONST char *s, ...) /* VARARGS */
{
va_list args;
#if FS_STDARG
va_start(args, s);
#else
int sev;
CONST char *s;
va_start(args);
sev = va_arg(args, int);
s = va_arg(args, CONST char *);
#endif
if (sev > error_threshold) {
FILE *f = error_file;
print_error_start(f, NIL(LOCATION), sev);
vfprintf_v(f, s, args);
fputs_v(MESSAGE_END, f);
print_error_end(f, sev);
}
va_end(args);
return;
}
/*
PRINT A RUNNING COMMENTARY
This routine is used in verbose mode to print a running commentary of
the compilation of the object id.
*/
void
commentary(IDENTIFIER id)
{
if (verbose && !IS_NULL_id(id)) {
BUFFER *bf = clear_buffer(&print_buff, stdout);
print_id_desc++;
IGNORE print_id_long(id, qual_none, bf, 0);
print_id_desc--;
bfprintf(bf, " ;\n");
output_buffer(bf, 1);
}
return;
}
/*
PRINT AN ASSERTION
The routine assertion prints the assertion s which occurred at the
location given by file and line. The routine is_true is used to
check whether the condition of an assertion is false.
*/
#ifdef ASSERTS
void
assertion(CONST char *s, CONST char *file, int line)
{
FILE *f = error_file;
PRINT_HEADER(HEADER_ASSERT, &crt_loc, f);
fprintf_v(f, " %s, %s: line %d.\n\n", s, file, line);
error_break();
abort();
}
int
is_true(int c)
{
return (c);
}
#endif