/[ascend]/trunk/base/generic/utilities/error.c
ViewVC logotype

Contents of /trunk/base/generic/utilities/error.c

Parent Directory Parent Directory | Revision Log Revision Log


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

john.pye@anu.edu.au
ViewVC Help
Powered by ViewVC 1.1.22