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,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,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 |
/* CONSOLE_DEBUG("TREE = %p",TREE); */ |
121 |
/* CONSOLE_DEBUG("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 |
static int writecount = 0; |
237 |
|
238 |
asc_assert(TREE==NULL); /* else recursive calls will occur */ |
239 |
|
240 |
if(++writecount > 50){ |
241 |
CONSOLE_DEBUG("TOO MUCH WRITING"); |
242 |
return 0; |
243 |
} |
244 |
|
245 |
if(t->err){ |
246 |
res += error_reporter(t->err->sev, t->err->filename, t->err->line, t->err->func, t->err->msg); |
247 |
}else{ |
248 |
/* CONSOLE_DEBUG("TREE HAS NO TOP-LEVEL ERROR"); */ |
249 |
} |
250 |
|
251 |
if(t->head){ |
252 |
res += error_reporter_tree_write(t->head); |
253 |
} |
254 |
if(t->next){ |
255 |
res += error_reporter_tree_write(t->next); |
256 |
} |
257 |
return res; |
258 |
} |
259 |
|
260 |
#else /* ERROR_REPORTER_TREE_ACTIVE */ |
261 |
int error_reporter_tree_start(){ |
262 |
ERROR_REPORTER_HERE(ASC_PROG_WARNING,"Error reporter 'tree' turned off at compile time"); |
263 |
return 0; |
264 |
} |
265 |
int error_reporter_tree_end(){return 0;} |
266 |
void error_reporter_tree_clear(){} |
267 |
int error_reporter_tree_has_error(){ |
268 |
ERROR_REPORTER_HERE(ASC_PROG_WARNING,"Attempt to check 'tree_has_error' when 'tree' turned off at compile time"); |
269 |
return 0; |
270 |
} |
271 |
#endif /* ERROR_REPORTER_TREE_ACTIVE */ |
272 |
|
273 |
/*-------------------------- |
274 |
REALLY WHERE THE REPORTING HAPPENS |
275 |
*/ |
276 |
|
277 |
int |
278 |
va_error_reporter( |
279 |
const error_severity_t sev |
280 |
, const char *errfile |
281 |
, const int errline |
282 |
, const char *errfunc |
283 |
, const char *fmt |
284 |
, const va_list args |
285 |
){ |
286 |
int res; |
287 |
|
288 |
#ifdef ERROR_REPORTER_TREE_ACTIVE |
289 |
error_reporter_tree_t *t; |
290 |
if(sev != ASC_PROG_FATAL){ |
291 |
if(TREECURRENT){ |
292 |
/* add the error to the tree, don't output anything now */ |
293 |
t = error_reporter_tree_new(); |
294 |
t->err = error_reporter_meta_new(); |
295 |
res = vsnprintf(t->err->msg,ERROR_REPORTER_MAX_MSG,fmt,args); |
296 |
t->err->filename = errfile; |
297 |
t->err->func = errfunc; |
298 |
t->err->line = errline; |
299 |
t->err->sev = sev; |
300 |
if(!TREECURRENT->head){ |
301 |
TREECURRENT->head = TREECURRENT->tail = t; |
302 |
}else{ |
303 |
TREECURRENT->tail->next = t; |
304 |
TREECURRENT->tail = t; |
305 |
} |
306 |
/* CONSOLE_DEBUG("Message (%d chars) added to tree",res); */ |
307 |
return res; |
308 |
}else if(TREE){ |
309 |
/* flush the tree before outputting current message */ |
310 |
/* CONSOLE_DEBUG("WRITING OUT TREE CONTENTS"); */ |
311 |
t = TREE; |
312 |
TREE = NULL; |
313 |
error_reporter_tree_write(t); |
314 |
CONSOLE_DEBUG("DONE WRITING TREE"); |
315 |
TREECURRENT = t; |
316 |
error_reporter_tree_clear(); |
317 |
/* CONSOLE_DEBUG("DONE FREEING TREE"); |
318 |
CONSOLE_DEBUG("TREE = %p",TREE); |
319 |
CONSOLE_DEBUG("TREECURRENT = %p",TREECURRENT); */ |
320 |
} |
321 |
} |
322 |
#endif |
323 |
|
324 |
if(g_error_reporter_callback==NULL){ |
325 |
/* fprintf(stderr,"CALLING VFPRINTF\n"); */ |
326 |
res = error_reporter_default_callback(sev,errfile,errline,errfunc,fmt,args); |
327 |
}else{ |
328 |
/* fprintf(stderr,"CALLING G_ERROR_REPORTER_CALLBACK\n"); */ |
329 |
res = g_error_reporter_callback(sev,errfile,errline,errfunc,fmt,args); |
330 |
} |
331 |
|
332 |
return res; |
333 |
} |
334 |
|
335 |
/*---------------------------- |
336 |
DROP-IN replacements for stdio.h / ascPrint.h |
337 |
*/ |
338 |
|
339 |
int vfprintf_error_reporter(FILE *file, const char *fmt, const va_list args){ |
340 |
char *msg; |
341 |
int len; |
342 |
int res; |
343 |
if(file==stderr){ |
344 |
if(g_error_reporter_cache.iscaching){ |
345 |
msg = g_error_reporter_cache.msg; |
346 |
len = strlen(msg); |
347 |
res = vsnprintf(msg+len,ERROR_REPORTER_MAX_MSG-len,fmt,args); |
348 |
if(len+res+1>=ERROR_REPORTER_MAX_MSG){ |
349 |
snprintf(msg+ERROR_REPORTER_MAX_MSG-16,15,"... (truncated)"); |
350 |
ASC_FPRINTF(stderr,"TRUNCATED MESSAGE, FULL MESSAGE FOLLOWS:\n----------START----------\n"); |
351 |
ASC_VFPRINTF(stderr,fmt,args); |
352 |
ASC_FPRINTF(stderr,"\n-----------END----------\n"); |
353 |
} |
354 |
}else{ |
355 |
/* Not caching: output all in one go as a ASC_PROG_NOTE */ |
356 |
res = va_error_reporter(ASC_PROG_NOTE,NULL,0,NULL,fmt,args); |
357 |
} |
358 |
}else{ |
359 |
res = ASC_VFPRINTF(file,fmt,args); |
360 |
} |
361 |
return res; |
362 |
} |
363 |
|
364 |
/** |
365 |
This function performs caching of the error text if the flag is set |
366 |
*/ |
367 |
int |
368 |
fprintf_error_reporter(FILE *file, const char *fmt, ...){ |
369 |
va_list args; |
370 |
int res; |
371 |
|
372 |
va_start(args,fmt); |
373 |
res = vfprintf_error_reporter(file,fmt,args); |
374 |
va_end(args); |
375 |
|
376 |
return res; |
377 |
} |
378 |
|
379 |
int |
380 |
fputc_error_reporter(int c, FILE *file){ |
381 |
if(file!=stderr){ |
382 |
return ASC_FPUTC(c,file); |
383 |
}else if(fprintf_error_reporter(file,"%c",c) == 1){ |
384 |
return c; |
385 |
}else{ |
386 |
return EOF; |
387 |
} |
388 |
} |
389 |
|
390 |
int |
391 |
fflush_error_reporter(FILE *file){ |
392 |
if(file!=stderr){ |
393 |
return ASC_FFLUSH(file); |
394 |
}else{ |
395 |
return error_reporter_end_flush(); |
396 |
} |
397 |
} |
398 |
|
399 |
/*---------------------------- |
400 |
CACHING of multiple-FPRINTF error messages |
401 |
*/ |
402 |
|
403 |
int |
404 |
error_reporter_start(const error_severity_t sev, const char *filename, const int line, const char *func){ |
405 |
|
406 |
if(g_error_reporter_cache.iscaching){ |
407 |
error_reporter_end_flush(); |
408 |
} |
409 |
g_error_reporter_cache.iscaching = 1; |
410 |
*(g_error_reporter_cache.msg) = '\0'; |
411 |
g_error_reporter_cache.sev = sev; |
412 |
g_error_reporter_cache.filename = filename; |
413 |
g_error_reporter_cache.line = line; |
414 |
g_error_reporter_cache.func = func; |
415 |
|
416 |
return 1; |
417 |
} |
418 |
|
419 |
int |
420 |
error_reporter_end_flush(){ |
421 |
if(g_error_reporter_cache.iscaching){ |
422 |
error_reporter( |
423 |
g_error_reporter_cache.sev |
424 |
,g_error_reporter_cache.filename |
425 |
,g_error_reporter_cache.line |
426 |
,g_error_reporter_cache.func |
427 |
,g_error_reporter_cache.msg |
428 |
); |
429 |
}else{ |
430 |
/* CONSOLE_DEBUG("IGNORING REPEATED CALL TO error_reporter_end_flush()"); */ |
431 |
} |
432 |
g_error_reporter_cache.iscaching = 0; |
433 |
|
434 |
return 0; /* output must be compatible with fflush */ |
435 |
} |
436 |
|
437 |
/*-------------------------- |
438 |
REPORT the error |
439 |
*/ |
440 |
int |
441 |
error_reporter( |
442 |
const error_severity_t sev |
443 |
, const char *errfile |
444 |
, const int errline |
445 |
, const char *errfunc |
446 |
, const char *fmt |
447 |
, ... |
448 |
){ |
449 |
int res; |
450 |
va_list args; |
451 |
|
452 |
va_start(args,fmt); |
453 |
res = va_error_reporter(sev,errfile,errline,errfunc,fmt,args); |
454 |
va_end(args); |
455 |
|
456 |
return res; |
457 |
} |
458 |
|
459 |
/*------------------------- |
460 |
SET the callback function |
461 |
*/ |
462 |
void |
463 |
error_reporter_set_callback( |
464 |
const error_reporter_callback_t new_callback |
465 |
){ |
466 |
g_error_reporter_callback = new_callback; |
467 |
} |
468 |
|
469 |
/*------------------------- |
470 |
OPTIONAL code for systems not supporting variadic macros. |
471 |
You know, your system probably does support variadic macros, it's just |
472 |
a question of checking what your particular syntax is... |
473 |
*/ |
474 |
|
475 |
#ifdef NO_VARIADIC_MACROS |
476 |
/* Following are only required on compilers without variadic macros: */ |
477 |
|
478 |
int error_reporter_note_no_line(const char *fmt,...){ |
479 |
int res; |
480 |
va_list args; |
481 |
|
482 |
va_start(args,fmt); |
483 |
res = va_error_reporter(ASC_PROG_NOTE,"unknown-file",0,NULL,fmt,args); |
484 |
va_end(args); |
485 |
|
486 |
return res; |
487 |
} |
488 |
|
489 |
/** |
490 |
Error reporter 'here' function for compilers not supporting |
491 |
variadic macros. |
492 |
*/ |
493 |
ASC_DLLSPEC(int) error_reporter_here(const error_severity_t sev, const char *fmt,...){ |
494 |
int res; |
495 |
va_list args; |
496 |
|
497 |
va_start(args,fmt); |
498 |
res = va_error_reporter(sev,"unknown-file",0,NULL,fmt,args); |
499 |
va_end(args); |
500 |
|
501 |
return res; |
502 |
} |
503 |
|
504 |
|
505 |
/** |
506 |
Error reporter 'no line' function for compilers not supporting |
507 |
variadic macros. |
508 |
*/ |
509 |
int error_reporter_noline(const error_severity_t sev, const char *fmt,...){ |
510 |
int res; |
511 |
va_list args; |
512 |
|
513 |
va_start(args,fmt); |
514 |
res = va_error_reporter(sev,NULL,0,NULL,fmt,args); |
515 |
va_end(args); |
516 |
|
517 |
return res; |
518 |
} |
519 |
|
520 |
int console_debug(const char *fmt,...){ |
521 |
int res; |
522 |
va_list args; |
523 |
|
524 |
va_start(args,fmt); |
525 |
res = Asc_VFPrintf(ASCERR,fmt,args); |
526 |
va_end(args); |
527 |
|
528 |
return res; |
529 |
} |
530 |
|
531 |
#endif /* NO_VARIADIC_MACROS */ |