1 /*
2 the DASM macro assembler (aka small systems cross assembler)
3
4 Copyright (c) 1988-2002 by Matthew Dillon.
5 Copyright (c) 1995 by Olaf "Rhialto" Seibert.
6 Copyright (c) 2003-2008 by Andrew Davie.
7 Copyright (c) 2008 by Peter H. Froehlich.
8
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License along
20 with this program; if not, write to the Free Software Foundation, Inc.,
21 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 */
23
24 /**
25 * @file
26 */
27
28 #include "errors.h"
29
30 #include "asm.h"
31 #include "util.h"
32 #include "version.h"
33
34 #include <assert.h>
35 #include <stdarg.h>
36 #include <stdio.h>
37
38 /*
39 TODO: I simply replaced "error" with the current level in
40 all messages, not sure that works on Windows? GNU is fine,
41 it doesn't require "error" in the message...
42 */
43
44 /*
45 TODO: the original asmerr() would set bStopAtEnd (what is
46 now nof_fatals>0) if the error had the "fatal" flag set in
47 the struct that described it; if would *abort* on a true
48 argument; in my previous reading of the code, I had assumed
49 that a true argument meant simply "fatal" and not abort. To
50 be verified again and addressed, maybe there are a few fatals
51 that should really be panics...
52 */
53
54 static error_format_t F_error_format = ERRORFORMAT_DEFAULT;
55 static error_level_t F_error_level = ERRORLEVEL_DEFAULT;
56 static size_t nof_fatals = 0;
57 static size_t nof_errors = 0;
58 static size_t nof_warnings = 0;
59 char source_location_buffer[SOURCE_LOCATION_LENGTH];
60
61 static const char *level_names[] =
62 {
63 [ERRORLEVEL_DEBUG] = "debug",
64 [ERRORLEVEL_INFO] = "info",
65 [ERRORLEVEL_NOTICE] = "notice",
66 [ERRORLEVEL_WARNING] = "warning",
67 [ERRORLEVEL_ERROR] = "error",
68 [ERRORLEVEL_FATAL] = "fatal",
69 [ERRORLEVEL_PANIC] = "***panic***",
70 };
71
valid_error_format(int format)72 bool valid_error_format(int format)
73 {
74 return (ERRORFORMAT_MIN <= format && format <= ERRORFORMAT_MAX);
75 }
76
set_error_format(error_format_t format)77 void set_error_format(error_format_t format)
78 {
79 assert(valid_error_format(format));
80 F_error_format = format;
81 }
82
valid_error_level(int level)83 bool valid_error_level(int level)
84 {
85 return (ERRORLEVEL_MIN <= level && level <= ERRORLEVEL_MAX);
86 }
87
set_error_level(error_level_t level)88 void set_error_level(error_level_t level)
89 {
90 assert(valid_error_level(level));
91 F_error_level = level;
92 }
93
94 /**
95 * @brief Display this error level?
96 */
visible_error_level(error_level_t level)97 static bool visible_error_level(error_level_t level)
98 {
99 return (level >= F_error_level);
100 }
101
number_of_fatals(void)102 size_t number_of_fatals(void)
103 {
104 return nof_fatals;
105 }
106
number_of_errors(void)107 size_t number_of_errors(void)
108 {
109 return nof_errors;
110 }
111
number_of_warnings(void)112 size_t number_of_warnings(void)
113 {
114 return nof_warnings;
115 }
116
117 /* Super low-level panic for disasters *inside* the errors module! */
internal_panic(const char * message)118 static void internal_panic(const char *message)
119 {
120 fprintf(
121 stderr,
122 "\n%s: FATAL INTERNAL PANIC (errors.c): %s\n\n",
123 getprogname(),
124 message
125 );
126 exit(EXIT_FAILURE);
127 }
128
129 /**
130 * @brief Print final error message to all relevant streams.
131 * @note We always print to stderr; we print to the listing
132 * file if we have one. Messages to the listing file get a
133 * leading "*" just like Matt's version did years ago; at
134 * one point I thought that the "*" starts a comment, but I
135 * can't confirm that in the code (only ";" seems to be a
136 * comment), so the motivation must have been different.
137 * We only get here after all the other filters checked that
138 * we should really print, so we don't check anything else
139 * about the error message.
140 */
print_error_message(const char * message)141 static void print_error_message(const char *message)
142 {
143 assert(message != NULL);
144 assert(strlen(message) > 0);
145
146 fprintf(stderr, "%s\n", message);
147
148 if (FI_listfile != NULL) {
149 fprintf(FI_listfile, "*%s\n", message);
150 }
151 else {
152 /* sanity check: if there was no FILE* there should be no name */
153 assert(F_listfile == NULL);
154 }
155 }
156
157 /**
158 * @brief Sane wrapper for vsnprintf().
159 * @note See sane_snprintf() for details.
160 */
161 static size_t
sane_vsnprintf(char * restrict str,size_t size,const char * restrict fmt,va_list ap)162 sane_vsnprintf(/*@out@*/ char *restrict str, size_t size, const char *restrict fmt, va_list ap)
163 {
164 int res;
165 assert(str != NULL);
166 assert(size > 0);
167 assert(fmt != NULL);
168
169 res = vsnprintf(str, size, fmt, ap);
170 if (res < 0) {
171 internal_panic("sane_vsnprintf() failed!");
172 }
173 /* res >= 0 here so cast to size_t is okay */
174 return (size_t) res;
175 }
176
177 static size_t
178 sane_snprintf(/*@out@*/ char *restrict str, size_t size, const char *restrict fmt, ...)
179 __attribute__((format(printf, 3, 4)));
180
181 /**
182 * @brief Sane wrapper for snprintf().
183 * @note The interface for snprintf() is a little retarded since
184 * the return type is int instead of size_t. Due to it's stdio.h
185 * heritage, returning something negative on error is expected.
186 * But we're using it to format strings, so we don't care about
187 * those errors in detail (if there ever are any, not even sure).
188 * We handle potential errors here and return a size_t suitable
189 * for overflow checking.
190 */
191 static size_t
sane_snprintf(char * restrict str,size_t size,const char * restrict fmt,...)192 sane_snprintf(/*@out@*/ char *restrict str, size_t size, const char *restrict fmt, ...)
193 {
194 size_t res;
195 va_list ap;
196
197 va_start(ap, fmt);
198
199 res = sane_vsnprintf(str, size, fmt, ap);
200
201 va_end(ap);
202 return res;
203 }
204
205 /**
206 * @brief Format the level part of the error message.
207 * @note Follows strlcat() conventions.
208 */
append_level(char * buffer,error_level_t level,size_t size)209 static size_t append_level(char *buffer, error_level_t level, size_t size)
210 {
211 char name[1024];
212 size_t res = 0;
213 assert(valid_error_level(level));
214 res = sane_snprintf(name, sizeof(name), "%s: ", level_names[level]);
215 if (res >= sizeof(name)) {
216 internal_panic("Buffer overflow in append_level()!");
217 }
218 return strlcat(buffer, name, size);
219 }
220
221 /**
222 * @brief Format the location part of the error message.
223 * @note Follows strlcat() conventions.
224 */
append_location(char * buffer,const INCFILE * file,size_t size)225 static size_t append_location(char *buffer, /*@null@*/ const INCFILE *file, size_t size)
226 {
227 char location[1024];
228 size_t res = 0;
229
230 /* clear buffer */
231 location[0] = '\0';
232
233 switch (F_error_format) {
234 case ERRORFORMAT_WOE:
235 /*
236 Error format for MS VisualStudio and relatives:
237 "file (line): error: string"
238 */
239 if (file != NULL) {
240 res = sane_snprintf(
241 location, sizeof(location), "%s (%lu): ",
242 file->name, file->lineno
243 );
244 }
245 break;
246 case ERRORFORMAT_DILLON:
247 /*
248 Matthew Dillon's original format. Note that
249 Matt's 2.16 uses this instead:
250 "line %4ld %-10s %s\n" (terminal)
251 */
252 if (file != NULL) {
253 res = sane_snprintf(
254 location, sizeof(location), "line %7lu %-10s ",
255 file->lineno, file->name
256 );
257 }
258 break;
259 case ERRORFORMAT_GNU:
260 /*
261 GNU format error messages, from their coding
262 standards: "source-file-name:lineno: message"
263 */
264 if (file != NULL) {
265 res = sane_snprintf(
266 location, sizeof(location), "%s:%lu: ",
267 file->name, file->lineno
268 );
269 }
270 else {
271 res = sane_snprintf(
272 location, sizeof(location), "%s: ",
273 getprogname()
274 );
275 }
276 break;
277 default:
278 internal_panic("Invalid error format in append_location()!");
279 break;
280 }
281 if (res >= sizeof(location)) {
282 internal_panic("Buffer overflow in append_location()!");
283 }
284 return strlcat(buffer, location, size);
285 }
286
287 /**
288 * @brief Format the information part of the error message.
289 * @note Follows strlcat() conventions.
290 */
291 static size_t
append_information(char * buffer,const char * fmt,va_list ap,size_t size)292 append_information(char *buffer, const char *fmt, va_list ap, size_t size)
293 {
294 char information[1024];
295 size_t res = 0;
296 res = sane_vsnprintf(information, sizeof(information), fmt, ap);
297 if (res >= sizeof(information)) {
298 internal_panic("Buffer overflow in append_information()!");
299 }
300 return strlcat(buffer, information, size);
301 }
302
vanotify(error_level_t level,const char * fmt,va_list ap)303 static void vanotify(error_level_t level, const char *fmt, va_list ap)
304 {
305 /* buffer for formatting error message into */
306 char buffer[1024];
307 /* include file we're in right now (if any) */
308 INCFILE *file = pIncfile;
309 /* holds the return value from strlcat */
310 size_t res;
311
312 assert(valid_error_level(level));
313
314 if (!visible_error_level(level)) {
315 /* condition not severe enough */
316 return;
317 }
318
319 /* find the file we're in (if any) */
320 /* TODO: how does this work exactly? */
321 while (file != NULL && (file->flags & INF_MACRO) != 0) {
322 file = file->next;
323 }
324
325 /* clear buffer */
326 buffer[0] = '\0';
327
328 /* append the various pieces of the message */
329 res = append_location(buffer, file, sizeof(buffer));
330 if (res > sizeof(buffer)) {
331 internal_panic("Buffer overflow in vanotify()!");
332 }
333 res = append_level(buffer, level, sizeof(buffer));
334 if (res > sizeof(buffer)) {
335 internal_panic("Buffer overflow in vanotify()!");
336 }
337 res = append_information(buffer, fmt, ap, sizeof(buffer));
338 if (res > sizeof(buffer)) {
339 internal_panic("Buffer overflow in vanotify()!");
340 }
341
342 /* print the message */
343 print_error_message(buffer);
344
345 /* maintain statistics about warnings and errors */
346 /* TODO: count everything < PANIC? */
347 if (level == ERRORLEVEL_WARNING)
348 {
349 nof_warnings += 1;
350 }
351 if (level == ERRORLEVEL_ERROR)
352 {
353 nof_errors += 1;
354 }
355
356 /* fatal and higher errors lead to (eventual) termination */
357 if (level >= ERRORLEVEL_FATAL)
358 {
359 nof_fatals += 1; /* stop after current pass */
360 }
361 if (level == ERRORLEVEL_PANIC)
362 {
363 exit(EXIT_FAILURE); /* stop right now! */
364 }
365 }
366
367 /* avoid code replication through macros, sweet [phf] */
368 #define IMPLEMENT_FMT(level) \
369 va_list ap; \
370 va_start(ap, fmt); \
371 vanotify(level, fmt, ap); \
372 va_end(ap)
373 #define DEFINE_FMT(name, level) \
374 void name(const char *fmt, ...) \
375 { \
376 IMPLEMENT_FMT(level); \
377 }
378
notify_fmt(error_level_t level,const char * fmt,...)379 void notify_fmt(error_level_t level, const char *fmt, ...)
380 {
381 IMPLEMENT_FMT(level);
382 }
383
384 DEFINE_FMT(debug_fmt, ERRORLEVEL_DEBUG)
385 DEFINE_FMT(info_fmt, ERRORLEVEL_INFO)
386 DEFINE_FMT(notice_fmt, ERRORLEVEL_NOTICE)
387 DEFINE_FMT(warning_fmt, ERRORLEVEL_WARNING)
388 DEFINE_FMT(error_fmt, ERRORLEVEL_ERROR)
389 DEFINE_FMT(fatal_fmt, ERRORLEVEL_FATAL)
390 DEFINE_FMT(panic_fmt, ERRORLEVEL_PANIC)
391
392 /* vim: set tabstop=4 softtabstop=4 expandtab shiftwidth=4 autoindent: */
393