summaryrefslogtreecommitdiff
path: root/usr/src/lib/libast/common/misc/error.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/libast/common/misc/error.c')
-rw-r--r--usr/src/lib/libast/common/misc/error.c661
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;
+}