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 */
45static /*@exits@*/ void def_internal_error_
46    (const char *file, unsigned int line, const char *message);
47static /*@exits@*/ void def_fatal(const char *message, va_list va);
48static 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;
55const 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 */
59yasm_error_class yasm_eclass;
60static /*@only@*/ /*@null@*/ char *yasm_estr;
61static unsigned long yasm_exrefline;
62static /*@only@*/ /*@null@*/ char *yasm_exrefstr;
63
64/* Warning indicator */
65typedef struct warn {
66    /*@reldef@*/ STAILQ_ENTRY(warn) link;
67
68    yasm_warn_class wclass;
69    /*@owned@*/ /*@null@*/ char *wstr;
70} warn;
71static STAILQ_HEAD(warn_head, warn) yasm_warns;
72
73/* Enabled warnings.  See errwarn.h for a list. */
74static unsigned long warn_class_enabled;
75
76typedef 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
87struct 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(). */
101static char unprint[5];
102
103
104static const char *
105def_gettext_hook(const char *msgid)
106{
107    return msgid;
108}
109
110void
111yasm_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
128void
129yasm_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 */
138char *
139yasm__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 */
161static void
162def_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 */
177static void
178def_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 */
190static errwarn_data *
191errwarn_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
247void
248yasm_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
260int
261yasm_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
270void
271yasm_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
285void
286yasm_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
294void
295yasm_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
310void
311yasm_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
319void
320yasm_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
333void 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
347yasm_warn_class
348yasm_warn_occurred(void)
349{
350    if (STAILQ_EMPTY(&yasm_warns))
351        return YASM_WARN_NONE;
352    return STAILQ_FIRST(&yasm_warns)->wclass;
353}
354
355void
356yasm_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
374void
375yasm_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
383void
384yasm_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
401void
402yasm_warn_enable(yasm_warn_class num)
403{
404    warn_class_enabled |= (1UL<<num);
405}
406
407void
408yasm_warn_disable(yasm_warn_class num)
409{
410    warn_class_enabled &= ~(1UL<<num);
411}
412
413void
414yasm_warn_disable_all(void)
415{
416    warn_class_enabled = 0;
417}
418
419yasm_errwarns *
420yasm_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
430void
431yasm_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
450void
451yasm_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
476unsigned int
477yasm_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
485void
486yasm_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
521void
522yasm__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