/usr/src/lib/libast/common/misc/error.c
C | 659 lines | 556 code | 39 blank | 64 comment | 131 complexity | 67ae7b529cf996fb22130da833f3055d MD5 | raw file
Possible License(s): GPL-3.0, LGPL-2.1, MPL-2.0-no-copyleft-exception, AGPL-3.0, BSD-3-Clause, LGPL-2.0, 0BSD, BSD-2-Clause, BSD-3-Clause-No-Nuclear-License-2014, AGPL-1.0, GPL-2.0
- /***********************************************************************
- * *
- * This software is part of the ast package *
- * Copyright (c) 1985-2010 AT&T Intellectual Property *
- * and is licensed under the *
- * Common Public License, Version 1.0 *
- * by AT&T Intellectual Property *
- * *
- * 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
- */
- 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;
- }