/[ascend]/branches/relerrorlist/ascend/utilities/error.c
ViewVC logotype

Contents of /branches/relerrorlist/ascend/utilities/error.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3217 - (show annotations) (download) (as text)
Sat Oct 28 07:01:00 2017 UTC (12 months, 3 weeks ago) by jpye
File MIME type: text/x-csrc
File size: 16638 byte(s)
fixing va_copy errors and snprintf warnings from gcc 7.2.0

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 <ascend/general/ascMalloc.h>
9 # include <ascend/general/panic.h>
10 #endif
11
12 //#define ERROR_DEBUG
13 #ifdef ERROR_DEBUG
14 # define MSG CONSOLE_DEBUG
15 # define TREE_PRINT error_reporter_tree_print
16 #else
17 # define MSG(ARGS...) ((void)0)
18 # define TREE_PRINT(ARGS...) ((void)0)
19 #endif
20
21
22 /**
23 Global variable which stores the pointer to the callback
24 function being used.
25 */
26 static error_reporter_callback_t g_error_reporter_callback;
27
28 /**
29 Global variable which enables caching of *individual* error messages,
30 as used with error_reporter_start() and error_reporter_end_flush().
31 This is not the same as the caching performed by error_reporter_tree_start()
32 which holds/records multiple error messages depending on the iscaching
33 setting.
34 */
35 static error_reporter_meta_t g_error_reporter_cache;
36
37 /**
38 Default error reporter. This error reporter is used whenever the callback
39 pointer is NULL, but can be replaced with another callback (eg for GUI
40 reporting) using error_reporter_set_callback()
41 */
42 int error_reporter_default_callback(ERROR_REPORTER_CALLBACK_ARGS){
43 char *sevmsg="";
44 enum ConsoleColor color = 0;
45 char *endtxt="\n";
46 int res=0;
47 switch(sev){
48 case ASC_PROG_FATAL: color=ASC_FG_BRIGHTRED; sevmsg = "PROGRAM FATAL ERROR: "; break;
49 case ASC_PROG_ERROR: color=ASC_FG_RED; sevmsg = "PROGRAM ERROR: "; break;
50 case ASC_PROG_WARNING: color=ASC_FG_BROWN;sevmsg = "PROGRAM WARNING: "; break;
51 case ASC_PROG_NOTE: color=ASC_FG_BRIGHTGREEN; endtxt=""; break; /* default, keep unembellished for now */
52 case ASC_USER_ERROR: color=ASC_FG_BRIGHTRED; sevmsg = "ERROR: "; break;
53 case ASC_USER_WARNING: color=ASC_FG_BROWN; sevmsg = "WARNING: "; break;
54 case ASC_USER_NOTE: sevmsg = "NOTE: "; break;
55 case ASC_USER_SUCCESS: color=ASC_FG_BRIGHTGREEN; sevmsg = "SUCCESS: "; break;
56 }
57 color_on(ASCERR,color);
58 res = ASC_FPRINTF(ASCERR,"%s",sevmsg);
59 color_off(ASCERR);
60 if(filename!=NULL){
61 //MSG("filename = '%s'",filename);
62 res += ASC_FPRINTF(ASCERR,"%s:",filename);
63 }
64 if(line!=0)res += ASC_FPRINTF(ASCERR,"%d:",line);
65 if(funcname!=NULL){
66 //MSG("funcname = '%s'",funcname);
67 res += ASC_FPRINTF(ASCERR,"%s:",funcname);
68 }else{
69 //MSG("funcname NULL");
70 }
71 if ((filename!=NULL) || (line!=0) || (funcname!=NULL))res += ASC_FPRINTF(ASCERR," ");
72 res += ASC_VFPRINTF(ASCERR,fmt,*args);
73 res += ASC_FPRINTF(ASCERR,"%s",endtxt);
74 return res;
75 }
76
77 /*---------------------------------------------------------------------------
78 ERROR REPORTER TREE (BRANCHABLE ERROR STACK) FUNCTIONALITY
79 */
80
81 #ifdef ERROR_REPORTER_TREE_ACTIVE
82
83 static error_reporter_tree_t *g_error_reporter_tree_current = NULL;
84
85 static error_reporter_meta_t *error_reporter_meta_new(){
86 error_reporter_meta_t *e;
87 e = ASC_NEW(error_reporter_meta_t);
88 e->sev = ASC_USER_SUCCESS;
89 e->iscaching = 0;
90 e->filename = NULL;
91 e->func = NULL;
92 e->line = 0;
93 e->msg[0] = '\0';
94 return e;
95 }
96
97 # define CURRENT g_error_reporter_tree_current
98
99 #ifdef ERROR_DEBUG
100 static void error_reporter_tree_print(error_reporter_tree_t *t1);
101 #endif
102
103 static int error_reporter_tree_write(error_reporter_tree_t *t);
104 static void error_reporter_tree_free(error_reporter_tree_t *t);
105
106 /// deprecated function for testing purpose only
107 error_reporter_tree_t *error_reporter_get_tree_current(){
108 return CURRENT;
109 }
110
111 static error_reporter_tree_t *error_reporter_tree_new(int iscaching){
112 error_reporter_tree_t *tnew = ASC_NEW(error_reporter_tree_t);
113 tnew->iscaching = iscaching;
114 tnew->next = NULL;
115 tnew->prev = NULL;
116 tnew->head = NULL;
117 tnew->tail = NULL;
118 tnew->err = NULL;
119 tnew->parent = NULL;
120 TREE_PRINT(tnew);
121 return tnew;
122 }
123
124 static int error_reporter_tree_has_caching_parent(error_reporter_tree_t *t){
125 assert(t);
126 if(NULL == t->parent)return 0;
127 error_reporter_tree_t *t1 = t->parent;
128 int iscaching = 0;
129 while(t1){
130 iscaching = iscaching || t1->iscaching;
131 assert(t1->parent != t1);
132 t1 = t1->parent;
133 }
134 return iscaching;
135 }
136
137 #ifdef ERROR_DEBUG
138 static void error_reporter_tree_print1(error_reporter_tree_t *t,int level){
139 if(t->head){
140 assert(t->err == NULL);
141 MSG("%*s+%p%s (head=%p,tail=%p)",2+2*level,"",t,t==CURRENT?" (CURRENT)":"",t->head,t->tail);
142 error_reporter_tree_print1(t->head,level+1);
143 }else if(t->err){
144 assert(t->err != NULL);
145 MSG("%*s-%p %s:%d: %s (parent=%p,next=%p)",2+2*level,"",t,t->err->filename, t->err->line, t->err->msg,t->parent,t->next);
146 }else{
147 MSG("%*s-%p EMPTY NODE",2+2*level,"",t);
148 }
149
150 if(t->next){
151 error_reporter_tree_print1(t->next,level);
152 }
153 }
154
155 static void error_reporter_tree_print(error_reporter_tree_t *t1){
156 if(!t1){
157 MSG("null tree");
158 }else{
159 MSG("finding top of tree, starting at %p",t1);
160 while(t1->parent){
161 t1 = t1->parent;
162 }
163 MSG("top of tree is %p",t1);
164 error_reporter_tree_print1(t1,0);
165 }
166 }
167 #endif
168
169 error_reporter_tree_t *error_reporter_tree_start(int iscaching){
170 error_reporter_tree_t *tnew = error_reporter_tree_new(iscaching);
171 if(CURRENT == NULL){
172 MSG("creating tree (caching=%d)",iscaching);
173 CURRENT = tnew;
174 }else{
175 MSG("creating sub-tree (caching=%d)",iscaching);
176 if(CURRENT->head == NULL){
177 MSG("adding at head");
178 CURRENT->head = tnew;
179 }else{
180 MSG("adding at tail");
181 /* link the new tree to the last in the child list */
182 CURRENT->tail->next = tnew;
183 tnew->prev = CURRENT->tail;
184 }
185 /* update the tail of the list */
186 CURRENT->tail = tnew;
187 /* now switch the context to the sub-tree */
188 tnew->parent = CURRENT;
189 CURRENT = tnew;
190 }
191 return CURRENT;
192 }
193
194 int error_reporter_tree_end(error_reporter_tree_t *tree){
195 assert(CURRENT);
196 assert(tree==CURRENT);
197 if(CURRENT->iscaching){
198 if(error_reporter_tree_has_caching_parent(CURRENT)){
199 MSG("no output; caching parent");
200 }else{
201 MSG("outputting now, no caching parent");
202 error_reporter_tree_write(CURRENT);
203 }
204 }
205 if(CURRENT->parent){
206 MSG("ending sub-tree, NOT freeing structures");
207 CURRENT = CURRENT->parent;
208 }else{
209 MSG("ending top tree, freeing structures");
210 TREE_PRINT(CURRENT);
211 error_reporter_tree_free(CURRENT);
212 CURRENT = NULL;
213 }
214 return 0;
215 }
216
217 /** recursive routine to deallocate error_reporter_tree_t structures. */
218 static void error_reporter_tree_free(error_reporter_tree_t *t){
219 assert(t);
220 error_reporter_tree_t *n=NULL;
221 if(t->head){
222 assert(t->err == NULL);
223 MSG("freeing sub-nodes");
224 error_reporter_tree_free(t->head);
225 MSG("done freeing sub-nodes");
226 }
227 if(t->err){
228 MSG("freeing error metadata ('%s')",t->err->msg);
229 assert(t->head == NULL);
230 assert(t->tail == NULL);
231 ASC_FREE(t->err);
232 }
233 if(t->next)n = t->next;
234
235 MSG("freeing node %p%s",t,t==CURRENT?" (CURRENT)":"");
236 ASC_FREE(t);
237
238 if(n){
239 MSG("freeing next node %p",n);
240 error_reporter_tree_free(n);
241 }
242 }
243
244 /** clear all errors nested within the current tree -- they won't be visible
245 to any parent trees after this; they never happened. Must be called INSTEAD OF
246 error_reporter_tree_end(). */
247 void error_reporter_tree_end_clear(error_reporter_tree_t *tree){
248 assert(CURRENT);
249 assert(tree==CURRENT);
250 error_reporter_tree_t *t1 = NULL, *tp, *tn;
251 if(CURRENT->parent){
252 MSG("ending/clearing sub-tree %p",CURRENT);
253 TREE_PRINT(CURRENT);
254 t1 = CURRENT->parent;
255 tn = CURRENT->next;
256 tp = CURRENT->prev;
257 assert(tn==NULL); /* actually, we can't imagine a case where tn != NULL */
258 if(tn){
259 MSG("%p->prev = %p",tn,tp);
260 tn->prev = tp;
261 }
262 if(tp){
263 MSG("%p->next = %p",tp,tn);
264 tp->next = tn;
265 }
266 if(t1->head == CURRENT){
267 MSG("fixing head");
268 if(tp)t1->head = tp;
269 else if(tn)t1->head = tn;
270 else t1->head = NULL;
271 }
272 if(t1->tail == CURRENT){
273 MSG("fixing tail");
274 if(tp)t1->tail = tp;
275 else t1->tail = NULL;
276 }
277 MSG("after pruning");
278 TREE_PRINT(t1);
279 }else{
280 MSG("ending/clearing top tree");
281 }
282 error_reporter_tree_free(CURRENT);
283 CURRENT = t1;
284 MSG("completed tree_end_clear, new current:");
285 TREE_PRINT(CURRENT);
286 }
287
288 static int error_reporter_tree_match_sev(error_reporter_tree_t *t, unsigned match){
289 assert(t);
290 if(t->err && (t->err->sev & match)){
291 /* MSG("SEVERITY MATCH FOR t = %p",t); */
292 return 1;
293 }
294 if(t->next && error_reporter_tree_match_sev(t->next, match)){
295 /* MSG("SEVERITY MATCH IN 'next' FOR t = %p",t); */
296 return 1;
297 }
298 if(t->head && error_reporter_tree_match_sev(t->head, match)){
299 /* MSG("SEVERITTY MATCH IN 'head' FOR t = %p",t); */
300 return 1;
301 }
302 /* MSG("NO MATCH FOR t = %p",t); */
303 return 0;
304 }
305
306 /**
307 traverse the tree, looking for ASC_PROG_ERR, ASC_USER_ERROR, or ASC_PROG_FATAL
308 @return 1 if errors found
309 */
310 int error_reporter_tree_has_error(error_reporter_tree_t *tree){
311 int res;
312 assert(CURRENT);
313 assert(tree==CURRENT);
314 res = error_reporter_tree_match_sev(CURRENT,ASC_ERR_ERR);
315 if(res){
316 MSG("ERROR(S) FOUND IN CURRENT %p",CURRENT);
317 }
318 return res;
319 }
320
321 static int error_reporter_tree_write(error_reporter_tree_t *t){
322 int res = 0;
323 assert(t != NULL);
324
325 if(t->err){
326 // an a simple node -- just an error
327 assert(t->head == NULL);
328 assert(t->tail == NULL);
329 MSG("WRITING MSG FROM CACHE");
330 error_reporter_callback_t cb = g_error_reporter_callback ?
331 g_error_reporter_callback : error_reporter_default_callback;
332 res += (*cb)(t->err->sev,t->err->filename,t->err->line,t->err->func,t->err->msg,NULL);
333 }else{
334 // a parent node -- traverse the sub-nodes
335 assert(t->head);
336 assert(t->tail);
337 error_reporter_tree_t *t1 = t->head;
338 while(t1){
339 res += error_reporter_tree_write(t1);
340 t1 = t1->next;
341 }
342 }
343 return res;
344 }
345
346 #else /* ERROR_REPORTER_TREE_ACTIVE */
347 error_reporter_tree_t *error_reporter_tree_start(int iscaching){
348 (void)iscaching;
349 ERROR_REPORTER_HERE(ASC_PROG_WARNING,"Error reporter 'tree' turned off at compile time");
350 return NULL;
351 }
352 int error_reporter_tree_end(error_reporter_tree_t *tree){
353 assert(tree==NULL);
354 return 0;
355 }
356 void error_reporter_tree_end_clear(error_reporter_tree_t *tree){
357 assert(tree==NULL);
358 }
359 int error_reporter_tree_has_error(error_reporter_tree_t *tree){
360 assert(tree==NULL);
361 ERROR_REPORTER_HERE(ASC_PROG_FATAL,"Attempt to check 'tree_has_error' when 'tree' turned off at compile time");
362 return 0;
363 }
364 #endif /* ERROR_REPORTER_TREE_ACTIVE */
365
366 /*--------------------------
367 REALLY WHERE THE REPORTING HAPPENS
368 */
369
370 int
371 va_error_reporter(
372 const error_severity_t sev
373 , const char *errfile
374 , const int errline
375 , const char *errfunc
376 , const char *fmt
377 , va_list *args
378 ){
379 int res = 0;
380 va_list args2;
381
382 error_reporter_callback_t cb = g_error_reporter_callback;
383 if(cb == NULL){
384 //MSG("using default error reporter callback, errfunc = '%s'",errfunc);
385 cb = error_reporter_default_callback;
386 }
387
388 #ifdef ERROR_REPORTER_TREE_ACTIVE
389 error_reporter_tree_t *t;
390 if(sev != ASC_PROG_FATAL){
391 if(CURRENT){
392 va_copy(args2,*args);
393
394 MSG("adding to tree");
395 /* add the error to the tree, don't output anything now */
396 t = error_reporter_tree_new(0);
397 t->err = error_reporter_meta_new();
398 vsnprintf(t->err->msg,ERROR_REPORTER_MAX_MSG,fmt,*args);
399 MSG("t->err->msg = \"%s\"",t->err->msg);
400 t->err->filename = errfile;
401 t->err->func = errfunc;
402 t->err->line = errline;
403 t->err->sev = sev;
404 if(!CURRENT->head){
405 // current tree is empty
406 CURRENT->head = CURRENT->tail = t;
407 t->parent = CURRENT;
408 }else{
409 CURRENT->tail->next = t;
410 t->prev = CURRENT->tail;
411 CURRENT->tail = t;
412 t->parent = CURRENT;
413 }
414 MSG("message '%s' added to tree",t->err->msg);
415 //MSG("message has errfunc '%s'",t->err->func);
416
417 if(CURRENT->iscaching || error_reporter_tree_has_caching_parent(CURRENT)){
418 MSG("caching; no output");
419 return res;
420 }else{
421 MSG("non-caching; outputting again directly");
422 res = (*cb)(sev,errfile,errline,errfunc,fmt,&args2);
423 MSG("res = %d",res);
424 return res;
425 }
426 }else{
427 MSG("no error tree; direct output");
428 return (*cb)(sev,errfile,errline,errfunc,fmt,args);
429 }
430 }
431 #endif
432
433 MSG("fatal error message format = '%s'",fmt);
434 return (*cb)(sev,errfile,errline,errfunc,fmt,args);
435 }
436
437 /*----------------------------
438 DROP-IN replacements for stdio.h / ascPrint.h
439 */
440
441 int vfprintf_error_reporter(FILE *file, const char *fmt, va_list *args){
442 char *msg;
443 int len;
444 int res;
445 if(file==stderr){
446 if(g_error_reporter_cache.iscaching){
447 msg = g_error_reporter_cache.msg;
448 len = strlen(msg);
449 res = vsnprintf(msg+len,ERROR_REPORTER_MAX_MSG-len,fmt,*args);
450 //MSG("Appended \"%s\" to message",msg+len);
451 if(len+res+1>=ERROR_REPORTER_MAX_MSG){
452 SNPRINTF(msg+ERROR_REPORTER_MAX_MSG-16,16,"... (truncated)");
453 ASC_FPRINTF(stderr,"TRUNCATED MESSAGE, FULL MESSAGE FOLLOWS:\n----------START----------\n");
454 ASC_VFPRINTF(stderr,fmt,*args);
455 ASC_FPRINTF(stderr,"\n-----------END----------\n");
456 }
457 }else{
458 MSG("Reporting msg directly, fmt = \"%s\"",fmt);
459 /* Not caching: output all in one go as a ASC_PROG_NOTE */
460 res = va_error_reporter(ASC_PROG_NOTE,NULL,0,NULL,fmt,args);
461 }
462 }else{
463 MSG("Printing directly, fmt = \"%s\"",fmt);
464 res = ASC_VFPRINTF(file,fmt,*args);
465 }
466 return res;
467 }
468
469 /**
470 This function performs caching of the error text if the flag is set
471 */
472 int
473 fprintf_error_reporter(FILE *file, const char *fmt, ...){
474 va_list args;
475 int res;
476
477 va_start(args,fmt);
478 res = vfprintf_error_reporter(file,fmt,&args);
479 va_end(args);
480
481 return res;
482 }
483
484 int
485 fputc_error_reporter(int c, FILE *file){
486 if(file!=stderr){
487 return ASC_FPUTC(c,file);
488 }else if(fprintf_error_reporter(file,"%c",c) == 1){
489 return c;
490 }else{
491 return EOF;
492 }
493 }
494
495 int
496 fflush_error_reporter(FILE *file){
497 if(file!=stderr){
498 return ASC_FFLUSH(file);
499 }else{
500 return error_reporter_end_flush();
501 }
502 }
503
504 /*----------------------------
505 CACHING of multiple-FPRINTF error messages
506 */
507
508 int error_reporter_start(const error_severity_t sev, const char *filename
509 , const int line, const char *func
510 ){
511 if(g_error_reporter_cache.iscaching){
512 MSG("call to error_reporter_start before expected error_reporter_end_flush");
513 error_reporter_end_flush();
514 }
515 g_error_reporter_cache.iscaching = 1;
516 *(g_error_reporter_cache.msg) = '\0';
517 g_error_reporter_cache.sev = sev;
518 g_error_reporter_cache.filename = filename;
519 g_error_reporter_cache.line = line;
520 g_error_reporter_cache.func = func;
521
522 return 1;
523 }
524
525 /**
526 This function will flush the output of a multi-line error message
527 (as written with multiple calls to FPRINTF(stderr,...))
528 */
529 int error_reporter_end_flush(){
530 if(g_error_reporter_cache.iscaching){
531 error_reporter(
532 g_error_reporter_cache.sev
533 ,g_error_reporter_cache.filename
534 ,g_error_reporter_cache.line
535 ,g_error_reporter_cache.func
536 ,g_error_reporter_cache.msg
537 );
538 }else{
539 /* MSG("IGNORING REPEATED CALL TO error_reporter_end_flush()"); */
540 }
541 g_error_reporter_cache.iscaching = 0;
542
543 return 0; /* output must be compatible with fflush */
544 }
545
546 /*--------------------------
547 REPORT the error
548 */
549 int
550 error_reporter(
551 const error_severity_t sev
552 , const char *errfile
553 , const int errline
554 , const char *errfunc
555 , const char *fmt
556 , ...
557 ){
558 int res;
559 va_list args;
560
561 va_start(args,fmt);
562 res = va_error_reporter(sev,errfile,errline,errfunc,fmt,&args);
563 va_end(args);
564
565 return res;
566 }
567
568 /*-------------------------
569 SET the callback function
570 */
571 void error_reporter_set_callback(
572 const error_reporter_callback_t new_callback
573 ){
574 g_error_reporter_callback = new_callback;
575 }
576
577 /*-------------------------
578 OPTIONAL code for systems not supporting variadic macros.
579 Your system probably does support variadic macros, it's just
580 a question of checking what your particular syntax is, and possibly
581 adding some stuff in error.h.
582 */
583
584 #ifdef NO_VARIADIC_MACROS
585 /** error reporter for compilers not supporting variadic macros */
586 int error_reporter_note_no_line(const char *fmt,...){
587 int res;
588 va_list args;
589
590 va_start(args,fmt);
591 res = va_error_reporter(ASC_PROG_NOTE,"unknown-file",0,NULL,fmt,&args);
592 va_end(args);
593
594 return res;
595 }
596
597 /** error reporter 'here' for compilers not supporting variadic macros */
598 ASC_DLLSPEC int error_reporter_here(const error_severity_t sev, const char *fmt,...){
599 int res;
600 va_list args;
601
602 va_start(args,fmt);
603 res = va_error_reporter(sev,"unknown-file",0,NULL,fmt,&args);
604 va_end(args);
605
606 return res;
607 }
608
609 /** error reporter for compilers not supporting variadic macros */
610 int error_reporter_noline(const error_severity_t sev, const char *fmt,...){
611 int res;
612 va_list args;
613
614 va_start(args,fmt);
615 res = va_error_reporter(sev,NULL,0,NULL,fmt,&args);
616 va_end(args);
617
618 return res;
619 }
620
621 /** console debugging for compilers not supporting variadic macros */
622 int console_debug(const char *fmt,...){
623 int res;
624 va_list args;
625
626 va_start(args,fmt);
627 res = Asc_VFPrintf(ASCERR,fmt,args);
628 va_end(args);
629
630 return res;
631 }
632 #endif /* NO_VARIADIC_MACROS */

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