1 /* ------------------------------------------------------------------------
2 @NAME       : error.c
3 @DESCRIPTION: Anything relating to reporting or recording errors and
4               warnings.
5 @GLOBALS    : errclass_names
6               err_actions
7               err_handlers
8               errclass_counts
9               error_buf
10 @CALLS      :
11 @CREATED    : 1996/08/28, Greg Ward
12 @MODIFIED   :
13 @VERSION    : $Id: error.c 640 1999-11-29 01:13:10Z greg $
14 @COPYRIGHT  : Copyright (c) 1996-99 by Gregory P. Ward.  All rights reserved.
15 
16               This file is part of the btparse library.  This library is
17               free software; you can redistribute it and/or modify it under
18               the terms of the GNU Library General Public License as
19               published by the Free Software Foundation; either version 2
20               of the License, or (at your option) any later version.
21 -------------------------------------------------------------------------- */
22 
23 #include "bt_config.h"
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <stdarg.h>
27 #include <string.h>
28 #include "btparse.h"
29 #include "error.h"
30 #include "my_dmalloc.h"
31 
32 
33 #define NUM_ERRCLASSES ((int) BTERR_INTERNAL + 1)
34 
35 
36 static char *errclass_names[NUM_ERRCLASSES] =
37 {
38    NULL,                        /* BTERR_NOTIFY */
39    "warning",                   /* BTERR_CONTENT */
40    "warning",                   /* BTERR_LEXWARN */
41    "warning",                   /* BTERR_USAGEWARN */
42    "error",                     /* BTERR_LEXERR */
43    "syntax error",              /* BTERR_SYNTAX */
44    "fatal error",               /* BTERR_USAGEERR */
45    "internal error"             /* BTERR_INTERNAL */
46 };
47 
48 static bt_erraction err_actions[NUM_ERRCLASSES] =
49 {
50    BTACT_NONE,                  /* BTERR_NOTIFY */
51    BTACT_NONE,                  /* BTERR_CONTENT */
52    BTACT_NONE,                  /* BTERR_LEXWARN */
53    BTACT_NONE,                  /* BTERR_USAGEWARN */
54    BTACT_NONE,                  /* BTERR_LEXERR */
55    BTACT_NONE,                  /* BTERR_SYNTAX */
56    BTACT_CRASH,                 /* BTERR_USAGEERR */
57    BTACT_ABORT                  /* BTERR_INTERNAL */
58 };
59 
60 void print_error (bt_error *err);
61 
62 static bt_err_handler err_handlers[NUM_ERRCLASSES] =
63 {
64    print_error,
65    print_error,
66    print_error,
67    print_error,
68    print_error,
69    print_error,
70    print_error,
71    print_error
72 };
73 
74 static int errclass_counts[NUM_ERRCLASSES] = { 0, 0, 0, 0, 0, 0, 0, 0 };
75 static char error_buf[MAX_ERROR+1];
76 
77 
78 /* ----------------------------------------------------------------------
79  * Error-handling functions.
80  */
81 
print_error(bt_error * err)82 void print_error (bt_error *err)
83 {
84    char *  name;
85    boolean something_printed;
86 
87    something_printed = FALSE;
88 
89    if (err->filename)
90    {
91       fprintf (stderr, err->filename);
92       something_printed = TRUE;
93    }
94    if (err->line > 0)                   /* going to print a line number? */
95    {
96       if (something_printed)
97          fprintf (stderr, ", ");
98       fprintf (stderr, "line %d", err->line);
99       something_printed = TRUE;
100    }
101    if (err->item_desc && err->item > 0) /* going to print an item number? */
102    {
103       if (something_printed)
104          fprintf (stderr, ", ");
105       fprintf (stderr, "%s %d", err->item_desc, err->item);
106       something_printed = TRUE;
107    }
108 
109    name = errclass_names[(int) err->class];
110    if (name)
111    {
112       if (something_printed)
113          fprintf (stderr, ", ");
114       fprintf (stderr, name);
115       something_printed = TRUE;
116    }
117 
118    if (something_printed)
119       fprintf (stderr, ": ");
120 
121    fprintf (stderr, "%s\n", err->message);
122 
123 } /* print_error() */
124 
125 
126 
127 /* ----------------------------------------------------------------------
128  * Error-reporting functions: these are called anywhere in the library
129  * when we encounter an error.
130  */
131 
132 void
report_error(bt_errclass class,char * filename,int line,char * item_desc,int item,char * fmt,va_list arglist)133 report_error (bt_errclass class,
134               char *      filename,
135               int         line,
136               char *      item_desc,
137               int         item,
138               char *      fmt,
139               va_list     arglist)
140 {
141    bt_error  err;
142 #if !HAVE_VSNPRINTF
143    int       msg_len;
144 #endif
145 
146    err.class = class;
147    err.filename = filename;
148    err.line = line;
149    err.item_desc = item_desc;
150    err.item = item;
151 
152    errclass_counts[(int) class]++;
153 
154 
155    /*
156     * Blech -- we're writing to a static buffer because there's no easy
157     * way to know how long the error message is going to be.  (Short of
158     * reimplementing printf(), or maybe printf()'ing to a dummy file
159     * and using the return value -- ugh!)  The GNU C library conveniently
160     * supplies vsnprintf(), which neatly solves this problem by truncating
161     * the output string if it gets too long.  (I could check for this
162     * truncation if I wanted to, but I don't think it's necessary given the
163     * ample size of the message buffer.)  For non-GNU systems, though,
164     * we're stuck with using vsprintf()'s return value.  This can't be
165     * trusted on all systems -- thus there's a check for it in configure.
166     * Also, this won't necessarily trigger the internal_error() if we
167     * do overflow; it's conceivable that vsprintf() itself would crash.
168     * At least doing it this way we avoid the possibility of vsprintf()
169     * silently corrupting some memory, and crashing unpredictably at some
170     * later point.
171     */
172 
173 #if HAVE_VSNPRINTF
174    vsnprintf (error_buf, MAX_ERROR, fmt, arglist);
175 #else
176    msg_len = vsprintf (error_buf, fmt, arglist);
177    if (msg_len > MAX_ERROR)
178       internal_error ("static error message buffer overflowed");
179 #endif
180 
181    err.message = error_buf;
182    if (err_handlers[class])
183       (*err_handlers[class]) (&err);
184 
185    switch (err_actions[class])
186    {
187       case BTACT_NONE: return;
188       case BTACT_CRASH: exit (1);
189       case BTACT_ABORT: abort ();
190       default: internal_error ("invalid error action %d for class %d (%s)",
191                                (int) err_actions[class],
192                                (int) class, errclass_names[class]);
193    }
194 
195 } /* report_error() */
196 
197 
198 GEN_ERRFUNC (general_error,
199              (bt_errclass class,
200               char *      filename,
201               int         line,
202               char *      item_desc,
203               int         item,
204               char *      fmt,
205               ...),
206              class, filename, line, item_desc, item, fmt)
207 
208 GEN_ERRFUNC (error,
209              (bt_errclass class,
210               char *      filename,
211               int         line,
212               char *      fmt,
213               ...),
214              class, filename, line, NULL, -1, fmt)
215 
216 GEN_ERRFUNC (ast_error,
217              (bt_errclass class,
218               AST *       ast,
219               char *      fmt,
220               ...),
221              class, ast->filename, ast->line, NULL, -1, fmt)
222 
223 GEN_ERRFUNC (notify,
224              (char * fmt, ...),
225              BTERR_NOTIFY, NULL, -1, NULL, -1, fmt)
226 
227 GEN_ERRFUNC (usage_warning,
228              (char * fmt, ...),
229              BTERR_USAGEWARN, NULL, -1, NULL, -1, fmt)
230 
231 GEN_ERRFUNC (usage_error,
232              (char * fmt, ...),
233              BTERR_USAGEERR, NULL, -1, NULL, -1, fmt)
234 
235 GEN_ERRFUNC (internal_error,
236              (char * fmt, ...),
237              BTERR_INTERNAL, NULL, -1, NULL, -1, fmt)
238 
239 
240 /* ======================================================================
241  * Functions to be used outside of the library
242  */
243 
244 /* ------------------------------------------------------------------------
245 @NAME       : bt_reset_error_counts()
246 @INPUT      :
247 @OUTPUT     :
248 @RETURNS    :
249 @DESCRIPTION: Resets all the error counters to zero.
250 @GLOBALS    :
251 @CALLS      :
252 @CREATED    : 1997/01/08, GPW
253 @MODIFIED   :
254 -------------------------------------------------------------------------- */
bt_reset_error_counts(void)255 void bt_reset_error_counts (void)
256 {
257    int  i;
258 
259    for (i = 0; i < NUM_ERRCLASSES; i++)
260       errclass_counts[i] = 0;
261 }
262 
263 
264 /* ------------------------------------------------------------------------
265 @NAME       : bt_get_error_count()
266 @INPUT      : errclass
267 @OUTPUT     :
268 @RETURNS    :
269 @DESCRIPTION: Returns number of errors seen in the specified class.
270 @GLOBALS    : errclass_counts
271 @CALLS      :
272 @CREATED    :
273 @MODIFIED   :
274 -------------------------------------------------------------------------- */
bt_get_error_count(bt_errclass errclass)275 int bt_get_error_count (bt_errclass errclass)
276 {
277    return errclass_counts[errclass];
278 }
279 
280 
281 /* ------------------------------------------------------------------------
282 @NAME       : bt_get_error_counts()
283 @INPUT      : counts - pointer to an array big enough to hold all the counts
284                        if NULL, the array will be allocated for you (and you
285                        must free() it when done with it)
286 @OUTPUT     :
287 @RETURNS    : counts - either the passed-in pointer, or the newly-
288                        allocated array if you pass in NULL
289 @DESCRIPTION: Returns a newly-allocated array with the number of errors
290               in each error class, indexed by the members of the
291               eclass_t enum.
292 @GLOBALS    : errclass_counts
293 @CALLS      :
294 @CREATED    : 1997/01/06, GPW
295 @MODIFIED   :
296 -------------------------------------------------------------------------- */
bt_get_error_counts(int * counts)297 int *bt_get_error_counts (int *counts)
298 {
299    int    i;
300 
301    if (counts == NULL)
302       counts = (int *) malloc (sizeof (int) * NUM_ERRCLASSES);
303    for (i = 0; i < NUM_ERRCLASSES; i++)
304       counts[i] = errclass_counts[i];
305 
306    return counts;
307 }
308 
309 
310 /* ------------------------------------------------------------------------
311 @NAME       : bt_error_status
312 @INPUT      : saved_counts - an array of error counts as returned by
313                              bt_get_error_counts, or NULL not to compare
314                              to a previous checkpoint
315 @OUTPUT     :
316 @RETURNS    :
317 @DESCRIPTION: Computes a bitmap where a bit is set for each error class
318               that has more errors now than it used to have (or, if
319               saved_counts is NULL, the bit is set of there are have been
320               any errors in the corresponding error class).
321 
322               Eg. "x & (1<<E_SYNTAX)" (where x is returned by bt_error_status)
323               is true if there have been any syntax errors.
324 @GLOBALS    :
325 @CALLS      :
326 @CREATED    :
327 @MODIFIED   :
328 -------------------------------------------------------------------------- */
bt_error_status(int * saved_counts)329 ushort bt_error_status (int *saved_counts)
330 {
331    int     i;
332    ushort  status;
333 
334    status = 0;
335 
336    if (saved_counts)
337    {
338       for (i = 0; i < NUM_ERRCLASSES; i++)
339          status |= ( (errclass_counts[i] > saved_counts[i]) << i);
340    }
341    else
342    {
343       for (i = 0; i < NUM_ERRCLASSES; i++)
344          status |= ( (errclass_counts[i] > 0) << i);
345    }
346 
347    return status;
348 } /* bt_error_status () */
349