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