1 /***********************************************************************
3 * This software is part of the ast package *
4 * Copyright (c) 1985-2010 AT&T Intellectual Property *
5 * and is licensed under the *
6 * Common Public License, Version 1.0 *
7 * by AT&T Intellectual Property *
9 * A copy of the License is available at *
10 * http://www.opensource.org/licenses/cpl1.0.txt *
11 * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
13 * Information and Software Systems Research *
17 * Glenn Fowler <gsf@research.att.com> *
18 * David Korn <dgk@research.att.com> *
19 * Phong Vo <kpv@research.att.com> *
21 ***********************************************************************/
27 * error and message formatter
29 * level is the error level
30 * level >= error_info.core!=0 dumps core
31 * level >= ERROR_FATAL calls error_info.exit
32 * level < 0 is for debug tracing
34 * NOTE: id && ERROR_NOID && !ERROR_USAGE implies format=id for errmsg()
48 * 2007-03-19 move error_info from _error_info_ to (*_error_infop_)
49 * to allow future Error_info_t growth
50 * by 2009 _error_info_ can be static
53 #if _BLD_ast && defined(__EXPORT__)
54 #define extern extern __EXPORT__
57 extern Error_info_t _error_info_
;
59 Error_info_t _error_info_
=
65 0,0,0,0,0,0,0, /* top of old context stack */
66 0,0,0,0,0,0,0, /* old empty context */
74 __EXTERN__(Error_info_t
, _error_info_
);
76 __EXTERN__(Error_info_t
*, _error_infop_
);
78 Error_info_t
* _error_infop_
= &_error_info_
;
81 * these should probably be in error_info
94 #define ERROR_CATALOG (ERROR_LIBRARY<<1)
101 #define OPT_LIBRARY 6
105 #define OPT_SYSTEM 10
109 static const Namval_t options
[] =
112 "catalog", OPT_CATALOG
,
117 "library", OPT_LIBRARY
,
120 "prefix", OPT_PREFIX
,
121 "system", OPT_SYSTEM
,
128 * called by stropt() to set options
132 setopt(void* a
, const void* p
, register int n
, register const char* v
)
136 switch (((Namval_t
*)p
)->value
)
145 error_state
.breakpoint
= ERROR_ERROR
;
149 error_state
.breakpoint
= ERROR_FATAL
;
153 error_state
.breakpoint
= ERROR_PANIC
;
156 error_state
.breakpoint
= strtol(v
, NiL
, 0);
160 error_state
.breakpoint
= 0;
161 if (((Namval_t
*)p
)->value
== OPT_CORE
)
162 error_info
.core
= error_state
.breakpoint
;
166 error_info
.set
|= ERROR_CATALOG
;
168 error_info
.clear
|= ERROR_CATALOG
;
172 error_state
.count
= strtol(v
, NiL
, 0);
174 error_state
.count
= 0;
177 error_info
.fd
= n
? strtol(v
, NiL
, 0) : -1;
181 error_info
.set
|= ERROR_LIBRARY
;
183 error_info
.clear
|= ERROR_LIBRARY
;
187 error_info
.mask
= strtol(v
, NiL
, 0);
192 if (error_state
.match
)
193 regfree(error_state
.match
);
196 if ((error_state
.match
|| (error_state
.match
= newof(0, regex_t
, 1, 0))) && regcomp(error_state
.match
, v
, REG_EXTENDED
|REG_LENIENT
))
198 free(error_state
.match
);
199 error_state
.match
= 0;
202 else if (error_state
.match
)
204 free(error_state
.match
);
205 error_state
.match
= 0;
210 error_state
.prefix
= strdup(v
);
211 else if (error_state
.prefix
)
213 free(error_state
.prefix
);
214 error_state
.prefix
= 0;
219 error_info
.set
|= ERROR_SYSTEM
;
221 error_info
.clear
|= ERROR_SYSTEM
;
224 error_info
.time
= n
? 1 : 0;
228 error_info
.trace
= -strtol(v
, NiL
, 0);
230 error_info
.trace
= 0;
237 * print a name with optional delimiter, converting unprintable chars
241 print(register Sfio_t
* sp
, register char* name
, char* delim
)
244 sfputr(sp
, name
, -1);
247 #if CC_NATIVE != CC_ASCII
249 register unsigned char* n2a
;
250 register unsigned char* a2n
;
254 n2a
= ccmap(CC_NATIVE
, CC_ASCII
);
255 a2n
= ccmap(CC_ASCII
, CC_NATIVE
);
294 sfputr(sp
, delim
, -1);
298 * print error context FIFO stack
301 #define CONTEXT(f,p) (((f)&ERROR_PUSH)?((Error_context_t*)&(p)->context->context):((Error_context_t*)(p)))
304 context(register Sfio_t
* sp
, register Error_context_t
* cp
)
307 context(sp
, CONTEXT(cp
->flags
, cp
->context
));
308 if (!(cp
->flags
& ERROR_SILENT
))
311 print(sp
, cp
->id
, NiL
);
312 if (cp
->line
> ((cp
->flags
& ERROR_INTERACTIVE
) != 0))
315 sfprintf(sp
, ": \"%s\", %s %d", cp
->file
, ERROR_translate(NiL
, NiL
, ast
.id
, "line"), cp
->line
);
317 sfprintf(sp
, "[%d]", cp
->line
);
319 sfputr(sp
, ": ", -1);
324 * debugging breakpoint
332 if (error_state
.tty
|| (error_state
.tty
= sfopen(NiL
, "/dev/tty", "r+")))
334 sfprintf(error_state
.tty
, "error breakpoint: ");
335 if (s
= sfgetr(error_state
.tty
, '\n', 1))
337 if (streq(s
, "q") || streq(s
, "quit"))
339 stropt(s
, options
, sizeof(*options
), setopt
, NiL
);
345 error(int level
, ...)
350 errorv(NiL
, level
, ap
);
355 errorv(const char* id
, int level
, va_list ap
)
374 if (!error_info
.init
)
377 stropt(getenv("ERROR_OPTIONS"), options
, sizeof(*options
), setopt
, NiL
);
381 flags
= level
& ~ERROR_LEVEL
;
382 level
&= ERROR_LEVEL
;
386 if ((flags
& (ERROR_USAGE
|ERROR_NOID
)) == ERROR_NOID
)
396 if (!*catalog
|| *catalog
== ':')
401 else if ((library
= strchr(catalog
, ':')) && !*++library
)
413 id
= (const char*)error_info
.id
;
414 catalog
= error_info
.catalog
;
416 if (level
< error_info
.trace
|| (flags
& ERROR_LIBRARY
) && !(((error_info
.set
| error_info
.flags
) ^ error_info
.clear
) & ERROR_LIBRARY
) || level
< 0 && error_info
.mask
&& !(error_info
.mask
& (1<<(-level
- 1))))
418 if (level
>= ERROR_FATAL
)
419 (*error_info
.exit
)(level
- 1);
422 if (error_info
.trace
< 0)
423 flags
|= ERROR_LIBRARY
|ERROR_SYSTEM
;
424 flags
|= error_info
.set
| error_info
.flags
;
425 flags
&= ~error_info
.clear
;
427 flags
&= ~ERROR_LIBRARY
;
428 fd
= (flags
& ERROR_OUTPUT
) ? va_arg(ap
, int) : error_info
.fd
;
429 if (error_info
.write
)
434 bas
= stkptr(stkstd
, 0);
435 if (off
= stktell(stkstd
))
436 stkfreeze(stkstd
, 0);
437 file
= error_info
.id
;
438 if (error_state
.prefix
)
439 sfprintf(stkstd
, "%s: ", error_state
.prefix
);
440 if (flags
& ERROR_USAGE
)
442 if (flags
& ERROR_NOID
)
443 sfprintf(stkstd
, " ");
445 sfprintf(stkstd
, "%s: ", ERROR_translate(NiL
, NiL
, ast
.id
, "Usage"));
446 if (file
|| opt_info
.argv
&& (file
= opt_info
.argv
[0]))
447 print(stkstd
, file
, " ");
451 if (level
&& !(flags
& ERROR_NOID
))
453 if (error_info
.context
&& level
> 0)
454 context(stkstd
, CONTEXT(error_info
.flags
, error_info
.context
));
456 print(stkstd
, file
, (flags
& ERROR_LIBRARY
) ? " " : ": ");
457 if (flags
& (ERROR_CATALOG
|ERROR_LIBRARY
))
459 sfprintf(stkstd
, "[");
460 if (flags
& ERROR_CATALOG
)
461 sfprintf(stkstd
, "%s %s%s",
462 catalog
? catalog
: ERROR_translate(NiL
, NiL
, ast
.id
, "DEFAULT"),
463 ERROR_translate(NiL
, NiL
, ast
.id
, "catalog"),
464 (flags
& ERROR_LIBRARY
) ? ", " : "");
465 if (flags
& ERROR_LIBRARY
)
466 sfprintf(stkstd
, "%s %s",
468 ERROR_translate(NiL
, NiL
, ast
.id
, "library"));
469 sfprintf(stkstd
, "]: ");
472 if (level
> 0 && error_info
.line
> ((flags
& ERROR_INTERACTIVE
) != 0))
474 if (error_info
.file
&& *error_info
.file
)
475 sfprintf(stkstd
, "\"%s\", ", error_info
.file
);
476 sfprintf(stkstd
, "%s %d: ", ERROR_translate(NiL
, NiL
, ast
.id
, "line"), error_info
.line
);
482 if ((d
= times(&us
)) < error_info
.time
|| error_info
.time
== 1)
484 sfprintf(stkstd
, " %05lu.%05lu.%05lu ", d
- error_info
.time
, (unsigned long)us
.tms_utime
, (unsigned long)us
.tms_stime
);
490 flags
&= ~ERROR_SYSTEM
;
493 sfprintf(stkstd
, "%s: ", ERROR_translate(NiL
, NiL
, ast
.id
, "warning"));
496 sfprintf(stkstd
, "%s: ", ERROR_translate(NiL
, NiL
, ast
.id
, "panic"));
501 s
= ERROR_translate(NiL
, NiL
, ast
.id
, "debug");
502 if (error_info
.trace
< -1)
503 sfprintf(stkstd
, "%s%d:%s", s
, level
, level
> -10 ? " " : "");
505 sfprintf(stkstd
, "%s: ", s
);
506 for (n
= 0; n
< error_info
.indent
; n
++)
514 if (flags
& ERROR_SOURCE
)
517 * source ([version], file, line) message
520 file
= va_arg(ap
, char*);
521 line
= va_arg(ap
, int);
522 s
= ERROR_translate(NiL
, NiL
, ast
.id
, "line");
523 if (error_info
.version
)
524 sfprintf(stkstd
, "(%s: \"%s\", %s %d) ", error_info
.version
, file
, s
, line
);
526 sfprintf(stkstd
, "(\"%s\", %s %d) ", file
, s
, line
);
528 if (format
|| (format
= va_arg(ap
, char*)))
530 if (!(flags
& ERROR_USAGE
))
531 format
= ERROR_translate(NiL
, id
, catalog
, format
);
532 sfvprintf(stkstd
, format
, ap
);
534 if (!(flags
& ERROR_PROMPT
))
537 * level&ERROR_OUTPUT on return means message
541 if ((flags
& ERROR_SYSTEM
) && errno
&& errno
!= error_info
.last_errno
)
543 sfprintf(stkstd
, " [%s]", fmterror(errno
));
544 if (error_info
.set
& ERROR_SYSTEM
)
546 error_info
.last_errno
= (level
>= 0) ? 0 : errno
;
548 if (error_info
.auxilliary
&& level
>= 0)
549 level
= (*error_info
.auxilliary
)(stkstd
, level
, flags
);
550 sfputc(stkstd
, '\n');
554 if ((level
& ~ERROR_OUTPUT
) > 1)
557 error_info
.warnings
++;
559 if (level
< 0 || !(level
& ERROR_OUTPUT
))
562 s
= stkptr(stkstd
, 0);
563 if (t
= memchr(s
, '\f', n
))
568 #if HUH_19980401 /* nasty problems if sfgetr() is in effect! */
573 if (fd
== sffileno(sfstderr
) && error_info
.write
== write
)
575 sfwrite(sfstderr
, s
, n
);
579 (*error_info
.write
)(fd
, s
, n
);
584 level
&= ERROR_LEVEL
;
586 stkset(stkstd
, bas
, off
);
590 if (level
>= error_state
.breakpoint
&& error_state
.breakpoint
&& (!error_state
.match
|| !regexec(error_state
.match
, s
? s
: format
, 0, NiL
, 0)) && (!error_state
.count
|| !--error_state
.count
))
596 #define SIGABRT SIGQUIT
599 #define SIGABRT SIGIOT
604 signal(SIGABRT
, SIG_DFL
);
605 kill(getpid(), SIGABRT
);
614 if (level
>= ERROR_FATAL
)
615 (*error_info
.exit
)(level
- ERROR_FATAL
+ 1);
619 * error_info context control
622 static Error_info_t
* freecontext
;
625 errorctx(Error_info_t
* p
, int op
, int flags
)
629 if (!(_error_infop_
= p
->context
))
630 _error_infop_
= &_error_info_
;
633 p
->context
= freecontext
;
643 freecontext
= freecontext
->context
;
644 else if (!(p
= newof(0, Error_info_t
, 1, 0)))
647 p
->errors
= p
->flags
= p
->line
= p
->warnings
= 0;
648 p
->catalog
= p
->file
= 0;
653 p
->context
= _error_infop_
;
656 p
->flags
|= ERROR_PUSH
;