diff options
Diffstat (limited to 'usr/src/lib/libast/common/misc/error.c')
-rw-r--r-- | usr/src/lib/libast/common/misc/error.c | 661 |
1 files changed, 661 insertions, 0 deletions
diff --git a/usr/src/lib/libast/common/misc/error.c b/usr/src/lib/libast/common/misc/error.c new file mode 100644 index 0000000000..89a3378c83 --- /dev/null +++ b/usr/src/lib/libast/common/misc/error.c @@ -0,0 +1,661 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1985-2007 AT&T Knowledge Ventures * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Knowledge Ventures * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler <gsf@research.att.com> * +* David Korn <dgk@research.att.com> * +* Phong Vo <kpv@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped +/* + * Glenn Fowler + * AT&T Research + * + * error and message formatter + * + * level is the error level + * level >= error_info.core!=0 dumps core + * level >= ERROR_FATAL calls error_info.exit + * level < 0 is for debug tracing + * + * NOTE: id && ERROR_NOID && !ERROR_USAGE implies format=id for errmsg() + */ + +#include "lclib.h" + +#include <ctype.h> +#include <ccode.h> +#include <namval.h> +#include <sig.h> +#include <stk.h> +#include <times.h> +#include <regex.h> + +/* + * 2007-03-19 move error_info from _error_info_ to (*_error_infop_) + * to allow future Error_info_t growth + * by 2009 _error_info_ can be static + */ + +#if _BLD_ast && defined(__EXPORT__) +#define extern extern __EXPORT__ +#endif + +extern Error_info_t _error_info_; + +Error_info_t _error_info_ = +{ + 2, exit, write, + 0,0,0,0,0,0,0,0, + 0, /* version */ + 0, /* auxilliary */ + 0,0,0,0,0,0,0, /* top of old context stack */ + 0,0,0,0,0,0,0, /* old empty context */ + 0, /* time */ + translate, + 0 /* catalog */ +}; + +#undef extern + +__EXTERN__(Error_info_t, _error_info_); + +__EXTERN__(Error_info_t*, _error_infop_); + +Error_info_t* _error_infop_ = &_error_info_; + +/* + * these should probably be in error_info + */ + +static struct State_s +{ + char* prefix; + Sfio_t* tty; + unsigned long count; + int breakpoint; + regex_t* match; +} error_state; + +#undef ERROR_CATALOG +#define ERROR_CATALOG (ERROR_LIBRARY<<1) + +#define OPT_BREAK 1 +#define OPT_CATALOG 2 +#define OPT_CORE 3 +#define OPT_COUNT 4 +#define OPT_FD 5 +#define OPT_LIBRARY 6 +#define OPT_MASK 7 +#define OPT_MATCH 8 +#define OPT_PREFIX 9 +#define OPT_SYSTEM 10 +#define OPT_TIME 11 +#define OPT_TRACE 12 + +static const Namval_t options[] = +{ + "break", OPT_BREAK, + "catalog", OPT_CATALOG, + "core", OPT_CORE, + "count", OPT_COUNT, + "debug", OPT_TRACE, + "fd", OPT_FD, + "library", OPT_LIBRARY, + "mask", OPT_MASK, + "match", OPT_MATCH, + "prefix", OPT_PREFIX, + "system", OPT_SYSTEM, + "time", OPT_TIME, + "trace", OPT_TRACE, + 0, 0 +}; + +/* + * called by stropt() to set options + */ + +static int +setopt(void* a, const void* p, register int n, register const char* v) +{ + NoP(a); + if (p) + switch (((Namval_t*)p)->value) + { + case OPT_BREAK: + case OPT_CORE: + if (n) + switch (*v) + { + case 'e': + case 'E': + error_state.breakpoint = ERROR_ERROR; + break; + case 'f': + case 'F': + error_state.breakpoint = ERROR_FATAL; + break; + case 'p': + case 'P': + error_state.breakpoint = ERROR_PANIC; + break; + default: + error_state.breakpoint = strtol(v, NiL, 0); + break; + } + else + error_state.breakpoint = 0; + if (((Namval_t*)p)->value == OPT_CORE) + error_info.core = error_state.breakpoint; + break; + case OPT_CATALOG: + if (n) + error_info.set |= ERROR_CATALOG; + else + error_info.clear |= ERROR_CATALOG; + break; + case OPT_COUNT: + if (n) + error_state.count = strtol(v, NiL, 0); + else + error_state.count = 0; + break; + case OPT_FD: + error_info.fd = n ? strtol(v, NiL, 0) : -1; + break; + case OPT_LIBRARY: + if (n) + error_info.set |= ERROR_LIBRARY; + else + error_info.clear |= ERROR_LIBRARY; + break; + case OPT_MASK: + if (n) + error_info.mask = strtol(v, NiL, 0); + else + error_info.mask = 0; + break; + case OPT_MATCH: + if (error_state.match) + regfree(error_state.match); + if (n) + { + if ((error_state.match || (error_state.match = newof(0, regex_t, 1, 0))) && regcomp(error_state.match, v, REG_EXTENDED|REG_LENIENT)) + { + free(error_state.match); + error_state.match = 0; + } + } + else if (error_state.match) + { + free(error_state.match); + error_state.match = 0; + } + break; + case OPT_PREFIX: + if (n) + error_state.prefix = strdup(v); + else if (error_state.prefix) + { + free(error_state.prefix); + error_state.prefix = 0; + } + break; + case OPT_SYSTEM: + if (n) + error_info.set |= ERROR_SYSTEM; + else + error_info.clear |= ERROR_SYSTEM; + break; + case OPT_TIME: + error_info.time = n ? 1 : 0; + break; + case OPT_TRACE: + if (n) + error_info.trace = -strtol(v, NiL, 0); + else + error_info.trace = 0; + break; + } + return 0; +} + +/* + * print a name with optional delimiter, converting unprintable chars + */ + +static void +print(register Sfio_t* sp, register char* name, char* delim) +{ + if (mbwide()) + sfputr(sp, name, -1); + else + { +#if CC_NATIVE != CC_ASCII + register int c; + register unsigned char* n2a; + register unsigned char* a2n; + register int aa; + register int as; + + n2a = ccmap(CC_NATIVE, CC_ASCII); + a2n = ccmap(CC_ASCII, CC_NATIVE); + aa = n2a['A']; + as = n2a[' ']; + while (c = *name++) + { + c = n2a[c]; + if (c & 0200) + { + c &= 0177; + sfputc(sp, '?'); + } + if (c < as) + { + c += aa - 1; + sfputc(sp, '^'); + } + c = a2n[c]; + sfputc(sp, c); + } +#else + register int c; + + while (c = *name++) + { + if (c & 0200) + { + c &= 0177; + sfputc(sp, '?'); + } + if (c < ' ') + { + c += 'A' - 1; + sfputc(sp, '^'); + } + sfputc(sp, c); + } +#endif + } + if (delim) + sfputr(sp, delim, -1); +} + +/* + * print error context FIFO stack + */ + +#define CONTEXT(f,p) (((f)&ERROR_PUSH)?((Error_context_t*)&(p)->context->context):((Error_context_t*)(p))) + +static void +context(register Sfio_t* sp, register Error_context_t* cp) +{ + if (cp->context) + context(sp, CONTEXT(cp->flags, cp->context)); + if (!(cp->flags & ERROR_SILENT)) + { + if (cp->id) + print(sp, cp->id, NiL); + if (cp->line > ((cp->flags & ERROR_INTERACTIVE) != 0)) + { + if (cp->file) + sfprintf(sp, ": \"%s\", %s %d", cp->file, ERROR_translate(NiL, NiL, ast.id, "line"), cp->line); + else + sfprintf(sp, "[%d]", cp->line); + } + sfputr(sp, ": ", -1); + } +} + +/* + * debugging breakpoint + */ + +extern void +error_break(void) +{ + char* s; + + if (error_state.tty || (error_state.tty = sfopen(NiL, "/dev/tty", "r+"))) + { + sfprintf(error_state.tty, "error breakpoint: "); + if (s = sfgetr(error_state.tty, '\n', 1)) + { + if (streq(s, "q") || streq(s, "quit")) + exit(0); + stropt(s, options, sizeof(*options), setopt, NiL); + } + } +} + +void +error(int level, ...) +{ + va_list ap; + + va_start(ap, level); + errorv(NiL, level, ap); + va_end(ap); +} + +void +errorv(const char* id, int level, va_list ap) +{ + register int n; + int fd; + int flags; + char* s; + char* t; + char* format; + char* library; + const char* catalog; + + int line; + char* file; + +#if !_PACKAGE_astsa + unsigned long d; + struct tms us; +#endif + + if (!error_info.init) + { + error_info.init = 1; + stropt(getenv("ERROR_OPTIONS"), options, sizeof(*options), setopt, NiL); + } + if (level > 0) + { + flags = level & ~ERROR_LEVEL; + level &= ERROR_LEVEL; + } + else + flags = 0; + if ((flags & (ERROR_USAGE|ERROR_NOID)) == ERROR_NOID) + { + format = (char*)id; + id = 0; + } + else + format = 0; + if (id) + { + catalog = (char*)id; + if (!*catalog || *catalog == ':') + { + catalog = 0; + library = 0; + } + else if ((library = strchr(catalog, ':')) && !*++library) + library = 0; + } + else + { + catalog = 0; + library = 0; + } + if (catalog) + id = 0; + else + { + id = (const char*)error_info.id; + catalog = error_info.catalog; + } + 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)))) + { + if (level >= ERROR_FATAL) + (*error_info.exit)(level - 1); + return; + } + if (error_info.trace < 0) + flags |= ERROR_LIBRARY|ERROR_SYSTEM; + flags |= error_info.set | error_info.flags; + flags &= ~error_info.clear; + if (!library) + flags &= ~ERROR_LIBRARY; + fd = (flags & ERROR_OUTPUT) ? va_arg(ap, int) : error_info.fd; + if (error_info.write) + { + long off; + char* bas; + + bas = stkptr(stkstd, 0); + if (off = stktell(stkstd)) + stkfreeze(stkstd, 0); + file = error_info.id; + if (error_state.prefix) + sfprintf(stkstd, "%s: ", error_state.prefix); + if (flags & ERROR_USAGE) + { + if (flags & ERROR_NOID) + sfprintf(stkstd, " "); + else + sfprintf(stkstd, "%s: ", ERROR_translate(NiL, NiL, ast.id, "Usage")); + if (file || opt_info.argv && (file = opt_info.argv[0])) + print(stkstd, file, " "); + } + else + { + if (level && !(flags & ERROR_NOID)) + { + if (error_info.context && level > 0) + context(stkstd, CONTEXT(error_info.flags, error_info.context)); + if (file) + print(stkstd, file, (flags & ERROR_LIBRARY) ? " " : ": "); + if (flags & (ERROR_CATALOG|ERROR_LIBRARY)) + { + sfprintf(stkstd, "["); + if (flags & ERROR_CATALOG) + sfprintf(stkstd, "%s %s%s", + catalog ? catalog : ERROR_translate(NiL, NiL, ast.id, "DEFAULT"), + ERROR_translate(NiL, NiL, ast.id, "catalog"), + (flags & ERROR_LIBRARY) ? ", " : ""); + if (flags & ERROR_LIBRARY) + sfprintf(stkstd, "%s %s", + library, + ERROR_translate(NiL, NiL, ast.id, "library")); + sfprintf(stkstd, "]: "); + } + } + if (level > 0 && error_info.line > ((flags & ERROR_INTERACTIVE) != 0)) + { + if (error_info.file && *error_info.file) + sfprintf(stkstd, "\"%s\", ", error_info.file); + sfprintf(stkstd, "%s %d: ", ERROR_translate(NiL, NiL, ast.id, "line"), error_info.line); + } + } +#if !_PACKAGE_astsa + if (error_info.time) + { + if ((d = times(&us)) < error_info.time || error_info.time == 1) + error_info.time = d; + sfprintf(stkstd, " %05lu.%05lu.%05lu ", d - error_info.time, (unsigned long)us.tms_utime, (unsigned long)us.tms_stime); + } +#endif + switch (level) + { + case 0: + flags &= ~ERROR_SYSTEM; + break; + case ERROR_WARNING: + sfprintf(stkstd, "%s: ", ERROR_translate(NiL, NiL, ast.id, "warning")); + break; + case ERROR_PANIC: + sfprintf(stkstd, "%s: ", ERROR_translate(NiL, NiL, ast.id, "panic")); + break; + default: + if (level < 0) + { + s = ERROR_translate(NiL, NiL, ast.id, "debug"); + if (error_info.trace < -1) + sfprintf(stkstd, "%s%d:%s", s, level, level > -10 ? " " : ""); + else + sfprintf(stkstd, "%s: ", s); + for (n = 0; n < error_info.indent; n++) + { + sfputc(stkstd, ' '); + sfputc(stkstd, ' '); + } + } + break; + } + if (flags & ERROR_SOURCE) + { + /* + * source ([version], file, line) message + */ + + file = va_arg(ap, char*); + line = va_arg(ap, int); + s = ERROR_translate(NiL, NiL, ast.id, "line"); + if (error_info.version) + sfprintf(stkstd, "(%s: \"%s\", %s %d) ", error_info.version, file, s, line); + else + sfprintf(stkstd, "(\"%s\", %s %d) ", file, s, line); + } + if (format || (format = va_arg(ap, char*))) + { + if (!(flags & ERROR_USAGE)) + format = ERROR_translate(NiL, id, catalog, format); + sfvprintf(stkstd, format, ap); + } + if (!(flags & ERROR_PROMPT)) + { + /* + * level&ERROR_OUTPUT on return means message + * already output + */ + + if ((flags & ERROR_SYSTEM) && errno && errno != error_info.last_errno) + { + sfprintf(stkstd, " [%s]", fmterror(errno)); + if (error_info.set & ERROR_SYSTEM) + errno = 0; + error_info.last_errno = (level >= 0) ? 0 : errno; + } + if (error_info.auxilliary && level >= 0) + level = (*error_info.auxilliary)(stkstd, level, flags); + sfputc(stkstd, '\n'); + } + if (level > 0) + { + if ((level & ~ERROR_OUTPUT) > 1) + error_info.errors++; + else + error_info.warnings++; + } + if (level < 0 || !(level & ERROR_OUTPUT)) + { + n = stktell(stkstd); + s = stkptr(stkstd, 0); + if (t = memchr(s, '\f', n)) + { + n -= ++t - s; + s = t; + } +#if HUH_19980401 /* nasty problems if sfgetr() is in effect! */ + sfsync(sfstdin); +#endif + sfsync(sfstdout); + sfsync(sfstderr); + if (fd == sffileno(sfstderr) && error_info.write == write) + { + sfwrite(sfstderr, s, n); + sfsync(sfstderr); + } + else + (*error_info.write)(fd, s, n); + } + else + { + s = 0; + level &= ERROR_LEVEL; + } + stkset(stkstd, bas, off); + } + else + s = 0; + 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)) + { + if (error_info.core) + { +#ifndef SIGABRT +#ifdef SIGQUIT +#define SIGABRT SIGQUIT +#else +#ifdef SIGIOT +#define SIGABRT SIGIOT +#endif +#endif +#endif +#ifdef SIGABRT + signal(SIGABRT, SIG_DFL); + kill(getpid(), SIGABRT); + pause(); +#else + abort(); +#endif + } + else + error_break(); + } + if (level >= ERROR_FATAL) + (*error_info.exit)(level - ERROR_FATAL + 1); +} + +/* + * error_info context control + */ + +#include <error.h> + +static Error_info_t* freecontext; + +Error_info_t* +errorctx(Error_info_t* p, int op, int flags) +{ + if (op & ERROR_POP) + { + if (!(_error_infop_ = p->context)) + _error_infop_ = &_error_info_; + if (op & ERROR_FREE) + { + p->context = freecontext; + freecontext = p; + } + p = _error_infop_; + } + else + { + if (!p) + { + if (p = freecontext) + freecontext = freecontext->context; + else if (!(p = newof(0, Error_info_t, 1, 0))) + return 0; + *p = *_error_infop_; + p->errors = p->flags = p->line = p->warnings = 0; + p->catalog = p->file = 0; + } + if (op & ERROR_PUSH) + { + p->flags = flags; + p->context = _error_infop_; + _error_infop_ = p; + } + p->flags |= ERROR_PUSH; + } + return p; +} |