1 /* 2 * Error and warning reporting and related functions. 3 * 4 * Copyright (C) 2001-2007 Peter Johnson 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS'' 16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE 19 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 * POSSIBILITY OF SUCH DAMAGE. 26 */ 27 #include "util.h" 28 29 #include <ctype.h> 30 #include <stdarg.h> 31 32 #include "coretype.h" 33 34 #include "linemap.h" 35 #include "errwarn.h" 36 37 38 #define MSG_MAXSIZE 1024 39 40 #if !defined(HAVE_TOASCII) || defined(lint) 41 # define toascii(c) ((c) & 0x7F) 42 #endif 43 44 /* Default handlers for replacable functions */ 45 static /*@exits@*/ void def_internal_error_ 46 (const char *file, unsigned int line, const char *message); 47 static /*@exits@*/ void def_fatal(const char *message, va_list va); 48 static const char *def_gettext_hook(const char *msgid); 49 50 /* Storage for errwarn's "extern" functions */ 51 /*@exits@*/ void (*yasm_internal_error_) 52 (const char *file, unsigned int line, const char *message) 53 = def_internal_error_; 54 /*@exits@*/ void (*yasm_fatal) (const char *message, va_list va) = def_fatal; 55 const char * (*yasm_gettext_hook) (const char *msgid) = def_gettext_hook; 56 57 /* Error indicator */ 58 /* yasm_eclass is not static so that yasm_error_occurred macro can access it */ 59 yasm_error_class yasm_eclass; 60 static /*@only@*/ /*@null@*/ char *yasm_estr; 61 static unsigned long yasm_exrefline; 62 static /*@only@*/ /*@null@*/ char *yasm_exrefstr; 63 64 /* Warning indicator */ 65 typedef struct warn { 66 /*@reldef@*/ STAILQ_ENTRY(warn) link; 67 68 yasm_warn_class wclass; 69 /*@owned@*/ /*@null@*/ char *wstr; 70 } warn; 71 static STAILQ_HEAD(warn_head, warn) yasm_warns; 72 73 /* Enabled warnings. See errwarn.h for a list. */ 74 static unsigned long warn_class_enabled; 75 76 typedef struct errwarn_data { 77 /*@reldef@*/ SLIST_ENTRY(errwarn_data) link; 78 79 enum { WE_UNKNOWN, WE_ERROR, WE_WARNING, WE_PARSERERROR } type; 80 81 unsigned long line; 82 unsigned long xrefline; 83 /*@owned@*/ char *msg; 84 /*@owned@*/ char *xrefmsg; 85 } errwarn_data; 86 87 struct yasm_errwarns { 88 /*@reldef@*/ SLIST_HEAD(errwarn_head, errwarn_data) errwarns; 89 90 /* Total error count */ 91 unsigned int ecount; 92 93 /* Total warning count */ 94 unsigned int wcount; 95 96 /* Last inserted error/warning. Used to speed up insertions. */ 97 /*@null@*/ errwarn_data *previous_we; 98 }; 99 100 /* Static buffer for use by conv_unprint(). */ 101 static char unprint[5]; 102 103 104 static const char * 105 def_gettext_hook(const char *msgid) 106 { 107 return msgid; 108 } 109 110 void 111 yasm_errwarn_initialize(void) 112 { 113 /* Default enabled warnings. See errwarn.h for a list. */ 114 warn_class_enabled = 115 (1UL<<YASM_WARN_GENERAL) | (1UL<<YASM_WARN_UNREC_CHAR) | 116 (1UL<<YASM_WARN_PREPROC) | (0UL<<YASM_WARN_ORPHAN_LABEL) | 117 (1UL<<YASM_WARN_UNINIT_CONTENTS) | (0UL<<YASM_WARN_SIZE_OVERRIDE) | 118 (1UL<<YASM_WARN_IMPLICIT_SIZE_OVERRIDE); 119 120 yasm_eclass = YASM_ERROR_NONE; 121 yasm_estr = NULL; 122 yasm_exrefline = 0; 123 yasm_exrefstr = NULL; 124 125 STAILQ_INIT(&yasm_warns); 126 } 127 128 void 129 yasm_errwarn_cleanup(void) 130 { 131 yasm_error_clear(); 132 yasm_warn_clear(); 133 } 134 135 /* Convert a possibly unprintable character into a printable string, using 136 * standard cat(1) convention for unprintable characters. 137 */ 138 char * 139 yasm__conv_unprint(int ch) 140 { 141 int pos = 0; 142 143 if (((ch & ~0x7F) != 0) /*!isascii(ch)*/ && !isprint(ch)) { 144 unprint[pos++] = 'M'; 145 unprint[pos++] = '-'; 146 ch &= toascii(ch); 147 } 148 if (iscntrl(ch)) { 149 unprint[pos++] = '^'; 150 unprint[pos++] = (ch == '\177') ? '?' : ch | 0100; 151 } else 152 unprint[pos++] = ch; 153 unprint[pos] = '\0'; 154 155 return unprint; 156 } 157 158 /* Report an internal error. Essentially a fatal error with trace info. 159 * Exit immediately because it's essentially an assert() trap. 160 */ 161 static void 162 def_internal_error_(const char *file, unsigned int line, const char *message) 163 { 164 fprintf(stderr, 165 yasm_gettext_hook(N_("INTERNAL ERROR at %s, line %u: %s\n")), 166 file, line, yasm_gettext_hook(message)); 167 #ifdef HAVE_ABORT 168 abort(); 169 #else 170 exit(EXIT_FAILURE); 171 #endif 172 } 173 174 /* Report a fatal error. These are unrecoverable (such as running out of 175 * memory), so just exit immediately. 176 */ 177 static void 178 def_fatal(const char *fmt, va_list va) 179 { 180 fprintf(stderr, "%s: ", yasm_gettext_hook(N_("FATAL"))); 181 vfprintf(stderr, yasm_gettext_hook(fmt), va); 182 fputc('\n', stderr); 183 exit(EXIT_FAILURE); 184 } 185 186 /* Create an errwarn structure in the correct linked list location. 187 * If replace_parser_error is nonzero, overwrites the last error if its 188 * type is WE_PARSERERROR. 189 */ 190 static errwarn_data * 191 errwarn_data_new(yasm_errwarns *errwarns, unsigned long line, 192 int replace_parser_error) 193 { 194 errwarn_data *first, *next, *ins_we, *we; 195 enum { INS_NONE, INS_HEAD, INS_AFTER } action = INS_NONE; 196 197 /* Find the entry with either line=line or the last one with line<line. 198 * Start with the last entry added to speed the search. 199 */ 200 ins_we = errwarns->previous_we; 201 first = SLIST_FIRST(&errwarns->errwarns); 202 if (!ins_we || !first) 203 action = INS_HEAD; 204 while (action == INS_NONE) { 205 next = SLIST_NEXT(ins_we, link); 206 if (line < ins_we->line) { 207 if (ins_we == first) 208 action = INS_HEAD; 209 else 210 ins_we = first; 211 } else if (!next) 212 action = INS_AFTER; 213 else if (line >= ins_we->line && line < next->line) 214 action = INS_AFTER; 215 else 216 ins_we = next; 217 } 218 219 if (replace_parser_error && ins_we && ins_we->type == WE_PARSERERROR) { 220 /* overwrite last error */ 221 we = ins_we; 222 } else { 223 /* add a new error */ 224 we = yasm_xmalloc(sizeof(errwarn_data)); 225 226 we->type = WE_UNKNOWN; 227 we->line = line; 228 we->xrefline = 0; 229 we->msg = NULL; 230 we->xrefmsg = NULL; 231 232 if (action == INS_HEAD) 233 SLIST_INSERT_HEAD(&errwarns->errwarns, we, link); 234 else if (action == INS_AFTER) { 235 assert(ins_we != NULL); 236 SLIST_INSERT_AFTER(ins_we, we, link); 237 } else 238 yasm_internal_error(N_("Unexpected errwarn insert action")); 239 } 240 241 /* Remember previous err/warn */ 242 errwarns->previous_we = we; 243 244 return we; 245 } 246 247 void 248 yasm_error_clear(void) 249 { 250 if (yasm_estr) 251 yasm_xfree(yasm_estr); 252 if (yasm_exrefstr) 253 yasm_xfree(yasm_exrefstr); 254 yasm_eclass = YASM_ERROR_NONE; 255 yasm_estr = NULL; 256 yasm_exrefline = 0; 257 yasm_exrefstr = NULL; 258 } 259 260 int 261 yasm_error_matches(yasm_error_class eclass) 262 { 263 if (yasm_eclass == YASM_ERROR_NONE) 264 return eclass == YASM_ERROR_NONE; 265 if (yasm_eclass == YASM_ERROR_GENERAL) 266 return eclass == YASM_ERROR_GENERAL; 267 return (yasm_eclass & eclass) == eclass; 268 } 269 270 void 271 yasm_error_set_va(yasm_error_class eclass, const char *format, va_list va) 272 { 273 if (yasm_eclass != YASM_ERROR_NONE) 274 return; 275 276 yasm_eclass = eclass; 277 yasm_estr = yasm_xmalloc(MSG_MAXSIZE+1); 278 #ifdef HAVE_VSNPRINTF 279 vsnprintf(yasm_estr, MSG_MAXSIZE, yasm_gettext_hook(format), va); 280 #else 281 vsprintf(yasm_estr, yasm_gettext_hook(format), va); 282 #endif 283 } 284 285 void 286 yasm_error_set(yasm_error_class eclass, const char *format, ...) 287 { 288 va_list va; 289 va_start(va, format); 290 yasm_error_set_va(eclass, format, va); 291 va_end(va); 292 } 293 294 void 295 yasm_error_set_xref_va(unsigned long xrefline, const char *format, va_list va) 296 { 297 if (yasm_eclass != YASM_ERROR_NONE) 298 return; 299 300 yasm_exrefline = xrefline; 301 302 yasm_exrefstr = yasm_xmalloc(MSG_MAXSIZE+1); 303 #ifdef HAVE_VSNPRINTF 304 vsnprintf(yasm_exrefstr, MSG_MAXSIZE, yasm_gettext_hook(format), va); 305 #else 306 vsprintf(yasm_exrefstr, yasm_gettext_hook(format), va); 307 #endif 308 } 309 310 void 311 yasm_error_set_xref(unsigned long xrefline, const char *format, ...) 312 { 313 va_list va; 314 va_start(va, format); 315 yasm_error_set_xref_va(xrefline, format, va); 316 va_end(va); 317 } 318 319 void 320 yasm_error_fetch(yasm_error_class *eclass, char **str, unsigned long *xrefline, 321 char **xrefstr) 322 { 323 *eclass = yasm_eclass; 324 *str = yasm_estr; 325 *xrefline = yasm_exrefline; 326 *xrefstr = yasm_exrefstr; 327 yasm_eclass = YASM_ERROR_NONE; 328 yasm_estr = NULL; 329 yasm_exrefline = 0; 330 yasm_exrefstr = NULL; 331 } 332 333 void yasm_warn_clear(void) 334 { 335 /* Delete all error/warnings */ 336 while (!STAILQ_EMPTY(&yasm_warns)) { 337 warn *w = STAILQ_FIRST(&yasm_warns); 338 339 if (w->wstr) 340 yasm_xfree(w->wstr); 341 342 STAILQ_REMOVE_HEAD(&yasm_warns, link); 343 yasm_xfree(w); 344 } 345 } 346 347 yasm_warn_class 348 yasm_warn_occurred(void) 349 { 350 if (STAILQ_EMPTY(&yasm_warns)) 351 return YASM_WARN_NONE; 352 return STAILQ_FIRST(&yasm_warns)->wclass; 353 } 354 355 void 356 yasm_warn_set_va(yasm_warn_class wclass, const char *format, va_list va) 357 { 358 warn *w; 359 360 if (!(warn_class_enabled & (1UL<<wclass))) 361 return; /* warning is part of disabled class */ 362 363 w = yasm_xmalloc(sizeof(warn)); 364 w->wclass = wclass; 365 w->wstr = yasm_xmalloc(MSG_MAXSIZE+1); 366 #ifdef HAVE_VSNPRINTF 367 vsnprintf(w->wstr, MSG_MAXSIZE, yasm_gettext_hook(format), va); 368 #else 369 vsprintf(w->wstr, yasm_gettext_hook(format), va); 370 #endif 371 STAILQ_INSERT_TAIL(&yasm_warns, w, link); 372 } 373 374 void 375 yasm_warn_set(yasm_warn_class wclass, const char *format, ...) 376 { 377 va_list va; 378 va_start(va, format); 379 yasm_warn_set_va(wclass, format, va); 380 va_end(va); 381 } 382 383 void 384 yasm_warn_fetch(yasm_warn_class *wclass, char **str) 385 { 386 warn *w = STAILQ_FIRST(&yasm_warns); 387 388 if (!w) { 389 *wclass = YASM_WARN_NONE; 390 *str = NULL; 391 return; 392 } 393 394 *wclass = w->wclass; 395 *str = w->wstr; 396 397 STAILQ_REMOVE_HEAD(&yasm_warns, link); 398 yasm_xfree(w); 399 } 400 401 void 402 yasm_warn_enable(yasm_warn_class num) 403 { 404 warn_class_enabled |= (1UL<<num); 405 } 406 407 void 408 yasm_warn_disable(yasm_warn_class num) 409 { 410 warn_class_enabled &= ~(1UL<<num); 411 } 412 413 void 414 yasm_warn_disable_all(void) 415 { 416 warn_class_enabled = 0; 417 } 418 419 yasm_errwarns * 420 yasm_errwarns_create(void) 421 { 422 yasm_errwarns *errwarns = yasm_xmalloc(sizeof(yasm_errwarns)); 423 SLIST_INIT(&errwarns->errwarns); 424 errwarns->ecount = 0; 425 errwarns->wcount = 0; 426 errwarns->previous_we = NULL; 427 return errwarns; 428 } 429 430 void 431 yasm_errwarns_destroy(yasm_errwarns *errwarns) 432 { 433 errwarn_data *we; 434 435 /* Delete all error/warnings */ 436 while (!SLIST_EMPTY(&errwarns->errwarns)) { 437 we = SLIST_FIRST(&errwarns->errwarns); 438 if (we->msg) 439 yasm_xfree(we->msg); 440 if (we->xrefmsg) 441 yasm_xfree(we->xrefmsg); 442 443 SLIST_REMOVE_HEAD(&errwarns->errwarns, link); 444 yasm_xfree(we); 445 } 446 447 yasm_xfree(errwarns); 448 } 449 450 void 451 yasm_errwarn_propagate(yasm_errwarns *errwarns, unsigned long line) 452 { 453 if (yasm_eclass != YASM_ERROR_NONE) { 454 errwarn_data *we = errwarn_data_new(errwarns, line, 1); 455 yasm_error_class eclass; 456 457 yasm_error_fetch(&eclass, &we->msg, &we->xrefline, &we->xrefmsg); 458 if (eclass != YASM_ERROR_GENERAL 459 && (eclass & YASM_ERROR_PARSE) == YASM_ERROR_PARSE) 460 we->type = WE_PARSERERROR; 461 else 462 we->type = WE_ERROR; 463 errwarns->ecount++; 464 } 465 466 while (!STAILQ_EMPTY(&yasm_warns)) { 467 errwarn_data *we = errwarn_data_new(errwarns, line, 0); 468 yasm_warn_class wclass; 469 470 yasm_warn_fetch(&wclass, &we->msg); 471 we->type = WE_WARNING; 472 errwarns->wcount++; 473 } 474 } 475 476 unsigned int 477 yasm_errwarns_num_errors(yasm_errwarns *errwarns, int warning_as_error) 478 { 479 if (warning_as_error) 480 return errwarns->ecount+errwarns->wcount; 481 else 482 return errwarns->ecount; 483 } 484 485 void 486 yasm_errwarns_output_all(yasm_errwarns *errwarns, yasm_linemap *lm, 487 int warning_as_error, 488 yasm_print_error_func print_error, 489 yasm_print_warning_func print_warning) 490 { 491 errwarn_data *we; 492 const char *filename, *xref_filename; 493 unsigned long line, xref_line; 494 495 /* If we're treating warnings as errors, tell the user about it. */ 496 if (warning_as_error && warning_as_error != 2) { 497 print_error("", 0, 498 yasm_gettext_hook(N_("warnings being treated as errors")), 499 NULL, 0, NULL); 500 warning_as_error = 2; 501 } 502 503 /* Output error/warnings. */ 504 SLIST_FOREACH(we, &errwarns->errwarns, link) { 505 /* Output error/warning */ 506 yasm_linemap_lookup(lm, we->line, &filename, &line); 507 if (we->xrefline) 508 yasm_linemap_lookup(lm, we->xrefline, &xref_filename, &xref_line); 509 else { 510 xref_filename = NULL; 511 xref_line = 0; 512 } 513 if (we->type == WE_ERROR || we->type == WE_PARSERERROR) 514 print_error(filename, line, we->msg, xref_filename, xref_line, 515 we->xrefmsg); 516 else 517 print_warning(filename, line, we->msg); 518 } 519 } 520 521 void 522 yasm__fatal(const char *message, ...) 523 { 524 va_list va; 525 va_start(va, message); 526 yasm_fatal(message, va); 527 /*@notreached@*/ 528 va_end(va); 529 } 530