1 |
#include <string.h> |
2 |
|
3 |
#include "error.h" |
4 |
|
5 |
#define ERROR_REPORTER_TREE_ACTIVE |
6 |
|
7 |
#ifdef ERROR_REPORTER_TREE_ACTIVE |
8 |
# include "ascMalloc.h" |
9 |
# include "ascPanic.h" |
10 |
#endif |
11 |
|
12 |
/** |
13 |
Global variable which stores the pointer to the callback |
14 |
function being used. |
15 |
*/ |
16 |
static error_reporter_callback_t g_error_reporter_callback; |
17 |
|
18 |
/** |
19 |
Global variable which holds cached error info for |
20 |
later output |
21 |
*/ |
22 |
static error_reporter_meta_t g_error_reporter_cache; |
23 |
|
24 |
#ifdef ERROR_REPORTER_TREE_ACTIVE |
25 |
static error_reporter_meta_t *error_reporter_meta_new(){ |
26 |
error_reporter_meta_t *e; |
27 |
e = ASC_NEW(error_reporter_meta_t); |
28 |
e->sev = ASC_USER_SUCCESS; |
29 |
e->iscaching = 0; |
30 |
e->filename = NULL; |
31 |
e->func = NULL; |
32 |
e->line = 0; |
33 |
e->msg[0] = '\0'; |
34 |
return e; |
35 |
} |
36 |
#endif /* ERROR_REPORTER_TREE_ACTIVE */ |
37 |
|
38 |
/** |
39 |
XTERM colour codes used to distinguish between errors of different types. |
40 |
*/ |
41 |
# define ERR_RED "31;1" |
42 |
# define ERR_GRN "32;2" |
43 |
# define ERR_BLU "34;1" |
44 |
# define ERR_BRN "33;1" |
45 |
# define ERR_BOLD "1" |
46 |
|
47 |
/** |
48 |
Default error reporter. To use this error reporter, set |
49 |
the callback pointer to NULL. |
50 |
*/ |
51 |
int error_reporter_default_callback(ERROR_REPORTER_CALLBACK_ARGS){ |
52 |
char *sevmsg=""; |
53 |
char *color=NULL; |
54 |
char *endtxt="\n"; |
55 |
int res=0; |
56 |
switch(sev){ |
57 |
case ASC_PROG_FATAL: color=ERR_RED; sevmsg = "PROGRAM FATAL ERROR: "; break; |
58 |
case ASC_PROG_ERROR: |
59 |
color=ERR_RED; sevmsg = "PROGRAM ERROR: "; |
60 |
break; |
61 |
case ASC_PROG_WARNING: color=ERR_BOLD;sevmsg = "PROGRAM WARNING: "; break; |
62 |
case ASC_PROG_NOTE: color=ERR_GRN; endtxt=""; break; /* default, keep unembellished for now */ |
63 |
case ASC_USER_ERROR: color=ERR_RED; sevmsg = "ERROR: "; break; |
64 |
case ASC_USER_WARNING: color=ERR_BRN; sevmsg = "WARNING: "; break; |
65 |
case ASC_USER_NOTE: sevmsg = "NOTE: "; break; |
66 |
case ASC_USER_SUCCESS: color=ERR_GRN; sevmsg = "SUCCESS: "; break; |
67 |
} |
68 |
|
69 |
color_on(ASCERR,color); |
70 |
res = ASC_FPRINTF(ASCERR,"%s",sevmsg); |
71 |
color_off(ASCERR); |
72 |
|
73 |
if(filename!=NULL){ |
74 |
res += ASC_FPRINTF(ASCERR,"%s:",filename); |
75 |
} |
76 |
if(line!=0){ |
77 |
res += ASC_FPRINTF(ASCERR,"%d:",line); |
78 |
} |
79 |
if(funcname!=NULL){ |
80 |
res += ASC_FPRINTF(ASCERR,"%s:",funcname); |
81 |
} |
82 |
if ((filename!=NULL) || (line!=0) || (funcname!=NULL)){ |
83 |
res += ASC_FPRINTF(ASCERR," "); |
84 |
} |
85 |
|
86 |
res += ASC_VFPRINTF(ASCERR,fmt,args); |
87 |
res += ASC_FPRINTF(ASCERR,"%s",endtxt); |
88 |
|
89 |
return res; |
90 |
} |
91 |
|
92 |
/*--------------------------------------------------------------------------- |
93 |
ERROR REPORTER TREE (BRANCHABLE ERROR STACK) FUNCTIONALITY |
94 |
*/ |
95 |
|
96 |
#ifdef ERROR_REPORTER_TREE_ACTIVE |
97 |
|
98 |
static error_reporter_tree_t *g_error_reporter_tree = NULL; |
99 |
static error_reporter_tree_t *g_error_reporter_tree_current = NULL; |
100 |
|
101 |
# define TREECURRENT g_error_reporter_tree_current |
102 |
# define TREE g_error_reporter_tree |
103 |
|
104 |
static int error_reporter_tree_write(error_reporter_tree_t *t); |
105 |
static void error_reporter_tree_free(error_reporter_tree_t *t); |
106 |
|
107 |
static error_reporter_tree_t *error_reporter_tree_new(){ |
108 |
error_reporter_tree_t *tnew = ASC_NEW(error_reporter_tree_t); |
109 |
tnew->next = NULL; |
110 |
tnew->head = NULL; |
111 |
tnew->tail = NULL; |
112 |
tnew->err = NULL; |
113 |
return tnew; |
114 |
} |
115 |
|
116 |
int error_reporter_tree_start(){ |
117 |
error_reporter_tree_t *tnew; |
118 |
tnew = error_reporter_tree_new(); |
119 |
|
120 |
fprintf(stderr,"TREE = %p",TREE); |
121 |
fprintf(stderr,"TREECURRENT = %p",TREECURRENT); |
122 |
|
123 |
#if 0 |
124 |
if(TREE != NULL && TREECURRENT == NULL){ |
125 |
CONSOLE_DEBUG("CALLED WITH NULL TREECURRENT BUT NON-NULL TREE"); |
126 |
error_reporter_tree_write(TREE); |
127 |
error_reporter_tree_free(TREE); |
128 |
TREE = NULL; |
129 |
} |
130 |
#endif |
131 |
|
132 |
if(TREE == NULL){ |
133 |
/* CONSOLE_DEBUG("CREATING ROOT"); */ |
134 |
/* we're creating the root */ |
135 |
tnew->parent = NULL; |
136 |
TREE = tnew; |
137 |
TREECURRENT = tnew; |
138 |
/* CONSOLE_DEBUG("TREECURRENT = %p",TREECURRENT); */ |
139 |
}else{ |
140 |
asc_assert(TREECURRENT != NULL); |
141 |
/* CONSOLE_DEBUG("CREATING SUBTREE"); */ |
142 |
if(TREECURRENT->head == NULL){ |
143 |
/* if the current tree has no elements, add it as the head */ |
144 |
TREECURRENT->head = tnew; |
145 |
}else{ |
146 |
/* link the new tree to the last in the child list */ |
147 |
TREECURRENT->tail->next = tnew; |
148 |
} |
149 |
/* update the tail of the list */ |
150 |
TREECURRENT->tail = tnew; |
151 |
|
152 |
/* now switch the context to the sub-tree */ |
153 |
tnew->parent = TREECURRENT; |
154 |
/* CONSOLE_DEBUG("SET TREECURRENT TO %p",TREECURRENT); */ |
155 |
TREECURRENT = tnew; |
156 |
} |
157 |
return 0; |
158 |
} |
159 |
|
160 |
int error_reporter_tree_end(){ |
161 |
CONSOLE_DEBUG("TREE END"); |
162 |
if(!TREECURRENT){ |
163 |
ERROR_REPORTER_HERE(ASC_PROG_ERR,"'end' without TREECURRENT set"); |
164 |
return 1; |
165 |
} |
166 |
TREECURRENT = TREECURRENT->parent; |
167 |
/* CONSOLE_DEBUG("SET TREECURRENT TO %p",TREECURRENT); */ |
168 |
return 0; |
169 |
} |
170 |
|
171 |
static void error_reporter_tree_free(error_reporter_tree_t *t){ |
172 |
if(t->head){ |
173 |
error_reporter_tree_free(t->head); |
174 |
} |
175 |
if(t->next){ |
176 |
error_reporter_tree_free(t->next); |
177 |
} |
178 |
if(t->err)ASC_FREE(t->err); |
179 |
ASC_FREE(t); |
180 |
} |
181 |
|
182 |
void error_reporter_tree_clear(){ |
183 |
/* recursively free anything beneath the TREECURRENT node, return to the parent context */ |
184 |
error_reporter_tree_t *t; |
185 |
if(!TREECURRENT){ |
186 |
/* CONSOLE_DEBUG("NOTHING TO CLEAR!"); */ |
187 |
return; |
188 |
} |
189 |
if(TREECURRENT->parent){ |
190 |
t = TREECURRENT->parent; |
191 |
}else{ |
192 |
TREE = NULL; |
193 |
t = NULL; |
194 |
} |
195 |
error_reporter_tree_free(TREECURRENT); |
196 |
TREECURRENT = t; |
197 |
} |
198 |
|
199 |
static int error_reporter_tree_match_sev(error_reporter_tree_t *t, unsigned match){ |
200 |
if(t->err && (t->err->sev & match)){ |
201 |
/* CONSOLE_DEBUG("SEVERITY MATCH FOR t = %p",t); */ |
202 |
return 1; |
203 |
} |
204 |
if(t->next && error_reporter_tree_match_sev(t->next, match)){ |
205 |
/* CONSOLE_DEBUG("SEVERITY MATCH IN 'next' FOR t = %p",t); */ |
206 |
return 1; |
207 |
} |
208 |
if(t->head && error_reporter_tree_match_sev(t->head, match)){ |
209 |
/* CONSOLE_DEBUG("SEVERITTY MATCH IN 'head' FOR t = %p",t); */ |
210 |
return 1; |
211 |
} |
212 |
/* CONSOLE_DEBUG("NO MATCH FOR t = %p",t); */ |
213 |
return 0; |
214 |
} |
215 |
|
216 |
/** |
217 |
traverse the tree, looking for ASC_PROG_ERR, ASC_USER_ERROR, or ASC_PROG_FATAL |
218 |
@return 1 if errors found |
219 |
*/ |
220 |
int error_reporter_tree_has_error(){ |
221 |
int res; |
222 |
if(TREECURRENT){ |
223 |
res = error_reporter_tree_match_sev(TREECURRENT,ASC_ERR_ERR); |
224 |
if(res){ |
225 |
/* CONSOLE_DEBUG("ERROR(S) FOUND IN TREECURRENT %p",TREECURRENT); */ |
226 |
} |
227 |
return res; |
228 |
}else{ |
229 |
CONSOLE_DEBUG("NO TREE FOUND"); |
230 |
return 0; |
231 |
} |
232 |
} |
233 |
|
234 |
static int error_reporter_tree_write(error_reporter_tree_t *t){ |
235 |
int res = 0; |
236 |
|
237 |
asc_assert(TREE==NULL); /* else recursive calls will occur */ |
238 |
|
239 |
if(t->err){ |
240 |
res += error_reporter(t->err->sev, t->err->filename, t->err->line, t->err->func, t->err->msg); |
241 |
}else{ |
242 |
/* CONSOLE_DEBUG("TREE HAS NO TOP-LEVEL ERROR"); */ |
243 |
} |
244 |
|
245 |
if(t->head){ |
246 |
res += error_reporter_tree_write(t->head); |
247 |
} |
248 |
if(t->next){ |
249 |
res += error_reporter_tree_write(t->next); |
250 |
} |
251 |
return res; |
252 |
} |
253 |
|
254 |
#else /* ERROR_REPORTER_TREE_ACTIVE */ |
255 |
int error_reporter_tree_start(){ |
256 |
ERROR_REPORTER_HERE(ASC_PROG_WARNING,"Error reporter 'tree' turned off at compile time"); |
257 |
return 0; |
258 |
} |
259 |
int error_reporter_tree_end(){return 0;} |
260 |
void error_reporter_tree_clear(){} |
261 |
int error_reporter_tree_has_error(){ |
262 |
ERROR_REPORTER_HERE(ASC_PROG_WARNING,"Attempt to check 'tree_has_error' when 'tree' turned off at compile time"); |
263 |
return 0; |
264 |
} |
265 |
#endif /* ERROR_REPORTER_TREE_ACTIVE */ |
266 |
|
267 |
/*-------------------------- |
268 |
REALLY WHERE THE REPORTING HAPPENS |
269 |
*/ |
270 |
|
271 |
int |
272 |
va_error_reporter( |
273 |
const error_severity_t sev |
274 |
, const char *errfile |
275 |
, const int errline |
276 |
, const char *errfunc |
277 |
, const char *fmt |
278 |
, va_list args |
279 |
){ |
280 |
int res; |
281 |
|
282 |
#ifdef ERROR_REPORTER_TREE_ACTIVE |
283 |
error_reporter_tree_t *t; |
284 |
if(sev != ASC_PROG_FATAL){ |
285 |
if(TREECURRENT){ |
286 |
/* add the error to the tree, don't output anything now */ |
287 |
t = error_reporter_tree_new(); |
288 |
t->err = error_reporter_meta_new(); |
289 |
res = vsnprintf(t->err->msg,ERROR_REPORTER_MAX_MSG,fmt,args); |
290 |
t->err->filename = errfile; |
291 |
t->err->func = errfunc; |
292 |
t->err->line = errline; |
293 |
t->err->sev = sev; |
294 |
if(!TREECURRENT->head){ |
295 |
TREECURRENT->head = TREECURRENT->tail = t; |
296 |
}else{ |
297 |
TREECURRENT->tail->next = t; |
298 |
TREECURRENT->tail = t; |
299 |
} |
300 |
/* CONSOLE_DEBUG("Message (%d chars) added to tree",res); */ |
301 |
return res; |
302 |
}else if(TREE){ |
303 |
/* flush the tree before outputting current message */ |
304 |
/* CONSOLE_DEBUG("WRITING OUT TREE CONTENTS"); */ |
305 |
t = TREE; |
306 |
TREE = NULL; |
307 |
error_reporter_tree_write(t); |
308 |
CONSOLE_DEBUG("DONE WRITING TREE"); |
309 |
TREECURRENT = t; |
310 |
error_reporter_tree_clear(); |
311 |
/* CONSOLE_DEBUG("DONE FREEING TREE"); |
312 |
CONSOLE_DEBUG("TREE = %p",TREE); |
313 |
CONSOLE_DEBUG("TREECURRENT = %p",TREECURRENT); */ |
314 |
} |
315 |
} |
316 |
#endif |
317 |
|
318 |
if(g_error_reporter_callback==NULL){ |
319 |
/* fprintf(stderr,"CALLING VFPRINTF\n"); */ |
320 |
res = error_reporter_default_callback(sev,errfile,errline,errfunc,fmt,args); |
321 |
}else{ |
322 |
/* fprintf(stderr,"CALLING G_ERROR_REPORTER_CALLBACK\n"); */ |
323 |
res = g_error_reporter_callback(sev,errfile,errline,errfunc,fmt,args); |
324 |
} |
325 |
|
326 |
return res; |
327 |
} |
328 |
|
329 |
/*---------------------------- |
330 |
DROP-IN replacements for stdio.h / ascPrint.h |
331 |
*/ |
332 |
|
333 |
int vfprintf_error_reporter(FILE *file, const char *fmt, va_list args){ |
334 |
char *msg; |
335 |
int len; |
336 |
int res; |
337 |
if(file==stderr){ |
338 |
if(g_error_reporter_cache.iscaching){ |
339 |
msg = g_error_reporter_cache.msg; |
340 |
len = strlen(msg); |
341 |
res = vsnprintf(msg+len,ERROR_REPORTER_MAX_MSG-len,fmt,args); |
342 |
if(len+res+1>=ERROR_REPORTER_MAX_MSG){ |
343 |
snprintf(msg+ERROR_REPORTER_MAX_MSG-16,15,"... (truncated)"); |
344 |
ASC_FPRINTF(stderr,"TRUNCATED MESSAGE, FULL MESSAGE FOLLOWS:\n----------START----------\n"); |
345 |
ASC_VFPRINTF(stderr,fmt,args); |
346 |
ASC_FPRINTF(stderr,"\n-----------END----------\n"); |
347 |
} |
348 |
}else{ |
349 |
/* Not caching: output all in one go as a ASC_PROG_NOTE */ |
350 |
res = va_error_reporter(ASC_PROG_NOTE,NULL,0,NULL,fmt,args); |
351 |
} |
352 |
}else{ |
353 |
res = ASC_VFPRINTF(file,fmt,args); |
354 |
} |
355 |
return res; |
356 |
} |
357 |
|
358 |
/** |
359 |
This function performs caching of the error text if the flag is set |
360 |
*/ |
361 |
int |
362 |
fprintf_error_reporter(FILE *file, const char *fmt, ...){ |
363 |
va_list args; |
364 |
int res; |
365 |
|
366 |
va_start(args,fmt); |
367 |
res = vfprintf_error_reporter(file,fmt,args); |
368 |
va_end(args); |
369 |
|
370 |
return res; |
371 |
} |
372 |
|
373 |
int |
374 |
fputc_error_reporter(int c, FILE *file){ |
375 |
if(file!=stderr){ |
376 |
return ASC_FPUTC(c,file); |
377 |
}else if(fprintf_error_reporter(file,"%c",c) == 1){ |
378 |
return c; |
379 |
}else{ |
380 |
return EOF; |
381 |
} |
382 |
} |
383 |
|
384 |
int |
385 |
fflush_error_reporter(FILE *file){ |
386 |
if(file!=stderr){ |
387 |
return ASC_FFLUSH(file); |
388 |
}else{ |
389 |
return error_reporter_end_flush(); |
390 |
} |
391 |
} |
392 |
|
393 |
/*---------------------------- |
394 |
CACHING of multiple-FPRINTF error messages |
395 |
*/ |
396 |
|
397 |
int |
398 |
error_reporter_start(const error_severity_t sev, const char *filename, const int line, const char *func){ |
399 |
|
400 |
if(g_error_reporter_cache.iscaching){ |
401 |
error_reporter_end_flush(); |
402 |
} |
403 |
g_error_reporter_cache.iscaching = 1; |
404 |
*(g_error_reporter_cache.msg) = '\0'; |
405 |
g_error_reporter_cache.sev = sev; |
406 |
g_error_reporter_cache.filename = filename; |
407 |
g_error_reporter_cache.line = line; |
408 |
g_error_reporter_cache.func = func; |
409 |
|
410 |
return 1; |
411 |
} |
412 |
|
413 |
int |
414 |
error_reporter_end_flush(){ |
415 |
if(g_error_reporter_cache.iscaching){ |
416 |
error_reporter( |
417 |
g_error_reporter_cache.sev |
418 |
,g_error_reporter_cache.filename |
419 |
,g_error_reporter_cache.line |
420 |
,g_error_reporter_cache.func |
421 |
,g_error_reporter_cache.msg |
422 |
); |
423 |
}else{ |
424 |
/* CONSOLE_DEBUG("IGNORING REPEATED CALL TO error_reporter_end_flush()"); */ |
425 |
} |
426 |
g_error_reporter_cache.iscaching = 0; |
427 |
|
428 |
return 0; /* output must be compatible with fflush */ |
429 |
} |
430 |
|
431 |
/*-------------------------- |
432 |
REPORT the error |
433 |
*/ |
434 |
int |
435 |
error_reporter( |
436 |
const error_severity_t sev |
437 |
, const char *errfile |
438 |
, const int errline |
439 |
, const char *errfunc |
440 |
, const char *fmt |
441 |
, ... |
442 |
){ |
443 |
int res; |
444 |
va_list args; |
445 |
|
446 |
va_start(args,fmt); |
447 |
res = va_error_reporter(sev,errfile,errline,errfunc,fmt,args); |
448 |
va_end(args); |
449 |
|
450 |
return res; |
451 |
} |
452 |
|
453 |
/*------------------------- |
454 |
SET the callback function |
455 |
*/ |
456 |
void |
457 |
error_reporter_set_callback( |
458 |
const error_reporter_callback_t new_callback |
459 |
){ |
460 |
g_error_reporter_callback = new_callback; |
461 |
} |
462 |
|
463 |
/*------------------------- |
464 |
OPTIONAL code for systems not supporting variadic macros. |
465 |
You know, your system probably does support variadic macros, it's just |
466 |
a question of checking what your particular syntax is... |
467 |
*/ |
468 |
|
469 |
#ifdef NO_VARIADIC_MACROS |
470 |
/* Following are only required on compilers without variadic macros: */ |
471 |
|
472 |
int error_reporter_note_no_line(const char *fmt,...){ |
473 |
int res; |
474 |
va_list args; |
475 |
|
476 |
va_start(args,fmt); |
477 |
res = va_error_reporter(ASC_PROG_NOTE,"unknown-file",0,NULL,fmt,args); |
478 |
va_end(args); |
479 |
|
480 |
return res; |
481 |
} |
482 |
|
483 |
/** |
484 |
Error reporter 'here' function for compilers not supporting |
485 |
variadic macros. |
486 |
*/ |
487 |
ASC_DLLSPEC int error_reporter_here(const error_severity_t sev, const char *fmt,...){ |
488 |
int res; |
489 |
va_list args; |
490 |
|
491 |
va_start(args,fmt); |
492 |
res = va_error_reporter(sev,"unknown-file",0,NULL,fmt,args); |
493 |
va_end(args); |
494 |
|
495 |
return res; |
496 |
} |
497 |
|
498 |
|
499 |
/** |
500 |
Error reporter 'no line' function for compilers not supporting |
501 |
variadic macros. |
502 |
*/ |
503 |
int error_reporter_noline(const error_severity_t sev, const char *fmt,...){ |
504 |
int res; |
505 |
va_list args; |
506 |
|
507 |
va_start(args,fmt); |
508 |
res = va_error_reporter(sev,NULL,0,NULL,fmt,args); |
509 |
va_end(args); |
510 |
|
511 |
return res; |
512 |
} |
513 |
|
514 |
int console_debug(const char *fmt,...){ |
515 |
int res; |
516 |
va_list args; |
517 |
|
518 |
va_start(args,fmt); |
519 |
res = Asc_VFPrintf(ASCERR,fmt,args); |
520 |
va_end(args); |
521 |
|
522 |
return res; |
523 |
} |
524 |
|
525 |
#endif /* NO_VARIADIC_MACROS */ |