// -*- related-file-name: "../include/lcdf/error.hh" -*-
/*
 * error.{cc,hh} -- flexible classes for error reporting
 * Eddie Kohler
 *
 * Copyright (c) 1999-2000 Massachusetts Institute of Technology
 * Copyright (c) 2001-2008 Eddie Kohler
 * Copyright (c) 2008 Meraki, Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, subject to the conditions
 * listed in the Click LICENSE file. These conditions include: you must
 * preserve this copyright notice, and you cannot mention the copyright
 * holders in advertising related to the Software without their permission.
 * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
 * notice is a summary of the Click LICENSE file; the license in that file is
 * legally binding.
 */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <lcdf/error.hh>
#include <lcdf/straccum.hh>
#include <lcdf/hashmap.hh>
#include <errno.h>
#include <ctype.h>
#include <algorithm>
#ifndef __KERNEL__
# include <stdlib.h>
# if HAVE_UNISTD_H
#  include <unistd.h>
# endif
#endif

/** @file error.hh
 * @brief Flexible error handling classes.
 */

struct ErrorHandler::Conversion {
    String name;
    ConversionFunction hook;
    Conversion *next;
};
static ErrorHandler::Conversion *error_items;

const char ErrorHandler::e_abort[] = "<-999>";
const char ErrorHandler::e_fatal[] = "<-1>";
const char ErrorHandler::e_emergency[] = "<0>";
const char ErrorHandler::e_alert[] = "<1>";
const char ErrorHandler::e_critical[] = "<2>";
const char ErrorHandler::e_error[] = "<3>";
const char ErrorHandler::e_warning[] = "<4>";
const char ErrorHandler::e_warning_annotated[] = "<4>warning: ";
const char ErrorHandler::e_notice[] = "<5>";
const char ErrorHandler::e_info[] = "<6>";
const char ErrorHandler::e_debug[] = "<7>";

const int ErrorHandler::ok_result = 0;
const int ErrorHandler::error_result = -EINVAL;

ErrorHandler *ErrorHandler::the_default_handler = 0;
ErrorHandler *ErrorHandler::the_silent_handler = 0;


// ANNOTATION MANAGEMENT

static const char *
parse_level(const char *begin, const char *end, int *result)
{
    int x = 0;
    const char *s = begin;

    bool negative = false;
    if (s != end && *s == '-') {
	negative = true;
	++s;
    } else if (s != end && *s == '+')
	++s;

    const char *digits = s;
    for (; s != end && *s >= '0' && *s <= '9'; ++s)
	x = x * 10 + *s - '0';

    if (s != end && *s == '.')
	for (++s; s != end && *s >= '0' && *s <= '9'; ++s)
	    /* nada */;

    if (s == digits || (s == digits + 1 && s[-1] == '.'))
	return begin;
    if (result)
	*result = (negative ? -x : x);
    return s;
}

String
ErrorHandler::make_anno(const char *name, const String &value)
{
    StringAccum sa;
    sa.reserve(value.length() + 10);

    // level annotation requires special handling
    if (name[0] == '<' && name[1] == '>' && name[2] == 0) {
	if (parse_level(value.begin(), value.end(), 0) == value.end()) {
	    sa << '<' << value << '>';
	    return sa.take_string();
	} else
	    return String();
    }

    sa << '{' << name << ':';
    const char *last = value.begin(), *end = value.end();
    for (const char *s = value.begin(); s != end; ++s)
	if (*s == '\\' || *s == '}') {
	    sa.append(last, s);
	    sa << '\\' << *s;
	    last = s + 1;
	} else if (*s == '\n') {
	    sa.append(last, s);
	    sa << '\\' << 'n';
	    last = s + 1;
	}
    sa.append(last, end);
    sa << '}';
    return sa.take_string();
}

const char *
ErrorHandler::skip_anno(const String &str, const char *begin, const char *end,
			String *name_result, String *value_result, bool raw)
{
    String name, value;
    const char *s = begin;

    if (s + 3 <= end && *s == '<') {
	const char *x = parse_level(s + 1, end, 0);
	if (x != s + 1 && x != end && *x == '>') {
	    name = String::make_stable("<>", 2);
	    if (str)
		value = str.substring(begin + 1, x);
	    begin = x + 1;
	}

    } else if (s + 2 <= end && *s == '{' && s[1] == '}')
	begin = s + 2;

    else if (s + 3 <= end && *s == '{' && str) {
	for (++s; s != end && isalnum((unsigned char) *s); ++s)
	    /* nada */;
	if (s == end || s == begin + 1 || (*s != '}' && *s != ':'))
	    /* not an annotation */;
	else if (*s == '}' && str) {
	    name = str.substring(begin + 1, s);
	    begin = s + 1;
	} else if (*s == '}') {
	    name = String::make_stable("{}", 2);
	    begin = s + 1;
	} else if (str) {
	    const char *x, *last = s + 1;
	    StringAccum sa;
	    for (x = s + 1; x != end && *x != '\n' && *x != '}'; ++x)
		if (*x == '\\' && x + 1 != end && x[1] != '\n') {
		    if (!raw) {
			sa.append(last, x);
			sa << (x[1] == 'n' ? '\n' : x[1]);
			last = x + 2;
		    }
		    ++x;
		}
	    if (x != end && *x == '}') {
		name = str.substring(begin + 1, s);
		if (sa) {
		    sa.append(last, x);
		    value = sa.take_string();
		} else
		    value = str.substring(s + 1, x);
		begin = x + 1;
	    }
	} else {
	    const char *x;
	    for (x = s + 1; x != end && *x != '\n' && *x != '}'; ++x)
		if (*x == '\\' && x + 1 != end && x[1] != '\n')
		    ++x;
	    if (x != end && *x == '}') {
		name = String::make_stable("{}", 2);
		begin = x + 1;
	    }
	}
    }

    if (name_result)
	*name_result = name;
    if (value_result)
	*value_result = value;
    return begin;
}

const char *
ErrorHandler::parse_anno(const String &str, const char *begin, const char *end,
			 ...)
{
    const char *names[8];
    void *values[8];
    int nanno = 0;

    va_list val;
    va_start(val, end);
    while (const char *n = va_arg(val, const char *)) {
	assert(nanno < 8);
	names[nanno] = n;
	if (n[0] == '#')
	    values[nanno] = va_arg(val, int *);
	else
	    values[nanno] = va_arg(val, String *);
	++nanno;
    }

    String name, value;
    while (1) {
	begin = skip_anno(str, begin, end, &name, &value, false);
	if (!name)
	    break;
	for (int i = 0; i < nanno; ++i)
	    if (names[i][0] == '#') {
		if (name == (names[i] + 1))
		    parse_level(value.begin(), value.end(), (int *) values[i]);
	    } else {
		if (name == names[i])
		    *(String *) values[i] = value;
	    }
    }

    return begin;
}

String
ErrorHandler::combine_anno(const String &text, const String &anno)
{
    if (!anno)
	return text;

    String names[8], values[8];
    int nanno = 0;
    const char *abegin = anno.begin();
    while (abegin != anno.end()) {
	assert(nanno < 8);
	abegin = skip_anno(anno, abegin, anno.end(), &names[nanno], &values[nanno], true);
	if (names[nanno])
	    ++nanno;
	else
	    break;
    }

    const char *last = text.begin(), *s = last;
    String name;
    StringAccum sa;
    while (s != text.end()) {
	const char *line = s;
	uint32_t mask = (1U << nanno) - 1;
	while (1) {
	    s = skip_anno(text, s, text.end(), &name, 0, false);
	    if (!name)
		break;
	    for (int i = 0; i < nanno; ++i)
		if (name == names[i])
		    mask &= ~(1U << i);
	}

	if (mask) {
	    sa.append(last, line);
	    for (int i = 0; i < nanno; ++i)
		if (mask & (1U << i)) {
		    if (names[i].equals("<>", 2))
			sa << '<' << values[i] << '>';
		    else
			sa << '{' << names[i] << ':' << values[i] << '}';
		}
	    last = line;
	}
	if (abegin != anno.end()) {
	    sa.append(last, s);
	    sa.append(abegin, anno.end());
	    last = s;
	}

	while (s != text.end() && *s != '\n')
	    ++s;
	if (s != text.end())
	    ++s;
    }

    if (sa) {
	sa.append(last, text.end());
	return sa.take_string();
    } else
	return text;
}

String
ErrorHandler::clean_landmark(const String &landmark, bool with_colon)
{
    const char *end = landmark.end();
    while (end != landmark.begin() && isspace((unsigned char) end[-1]))
	--end;
    if (end != landmark.begin() && end[-1] == ':')
	--end;
    if (end == landmark.begin())
	return String();
    else if (with_colon)
	return landmark.substring(landmark.begin(), end) + ": ";
    else
	return landmark.substring(landmark.begin(), end);
}


// FORMATTING

#define NUMBUF_SIZE	128
#define ErrH		ErrorHandler

static char *
do_number(unsigned long num, char *after_last, int base, int flags)
{
    const char *digits =
	((flags & ErrH::cf_uppercase) ? "0123456789ABCDEF" : "0123456789abcdef");
    char *pos = after_last;
    while (num) {
	*--pos = digits[num % base];
	num /= base;
    }
    if (pos == after_last)
	*--pos = '0';
    return pos;
}

static char *
do_number_flags(char *pos, char *after_last, int base, int flags,
		int precision, int field_width)
{
    // remove cf_alternate_form for zero results in base 16
    if ((flags & ErrH::cf_alternate_form) && base == 16 && *pos == '0')
	flags &= ~ErrH::cf_alternate_form;

    // account for zero padding
    if (precision >= 0)
	while (after_last - pos < precision)
	    *--pos = '0';
    else if (flags & ErrH::cf_zero_pad) {
	if ((flags & ErrH::cf_alternate_form) && base == 16)
	    field_width -= 2;
	if ((flags & ErrH::cf_negative)
	    || (flags & (ErrH::cf_plus_positive | ErrH::cf_space_positive)))
	    field_width--;
	while (after_last - pos < field_width)
	    *--pos = '0';
    }

    // alternate forms
    if ((flags & ErrH::cf_alternate_form) && base == 8 && pos[1] != '0')
	*--pos = '0';
    else if ((flags & ErrH::cf_alternate_form) && base == 16) {
	*--pos = ((flags & ErrH::cf_uppercase) ? 'X' : 'x');
	*--pos = '0';
    }

    // sign
    if (flags & ErrH::cf_negative)
	*--pos = '-';
    else if (flags & ErrH::cf_plus_positive)
	*--pos = '+';
    else if (flags & ErrH::cf_space_positive)
	*--pos = ' ';

    return pos;
}

String
ErrorHandler::vxformat(int default_flags, const char *s, va_list val)
{
    StringAccum msg;

    char numbuf[NUMBUF_SIZE];	// for numerics
    numbuf[NUMBUF_SIZE-1] = 0;

    String strstore;		// to ensure temporaries aren't destroyed

    // declare and initialize these here to make gcc shut up about possible
    // use before initialization
    int flags = 0;
    int field_width = -1;
    int precision = -1;
    int width_flag = 0;
    int base = 10;
    while (1) {

	const char *pct = strchr(s, '%');
	if (!pct) {
	    if (*s)
		msg << s;
	    break;
	}
	if (pct != s) {
	    msg.append(s, pct - s);
	    s = pct;
	}

	// parse flags
	flags = default_flags;
    flags:
	switch (*++s) {
	case '#': flags |= cf_alternate_form; goto flags;
	case '0': flags |= cf_zero_pad; goto flags;
	case '-': flags |= cf_left_just; goto flags;
	case ' ': flags |= cf_space_positive; goto flags;
	case '+': flags |= cf_plus_positive; goto flags;
	case '\'': flags |= cf_singlequote; goto flags;
	case '_': flags &= ~cf_utf8; goto flags;
	}

	// parse field width
	field_width = -1;
	if (*s == '*') {
	    field_width = va_arg(val, int);
	    if (field_width < 0) {
		field_width = -field_width;
		flags |= cf_left_just;
	    }
	    s++;
	} else if (*s >= '0' && *s <= '9')
	    for (field_width = 0; *s >= '0' && *s <= '9'; s++)
		field_width = 10*field_width + *s - '0';

	// parse precision
	precision = -1;
	if (*s == '.') {
	    s++;
	    precision = 0;
	    if (*s == '*') {
		precision = va_arg(val, int);
		s++;
	    } else if (*s >= '0' && *s <= '9')
		for (; *s >= '0' && *s <= '9'; s++)
		    precision = 10*precision + *s - '0';
	}

	// parse width flags
	width_flag = 0;
    width_flags:
	switch (*s) {
	case 'h': case 'l':
	    if (width_flag == *s)
		width_flag = *s + 'A' - 'a';
	    else if (width_flag)
		break;
	    else
		width_flag = *s;
	    s++;
	    goto width_flags;
	case 'z':
	    if (width_flag)
		break;
	    width_flag = *s++;
	    break;
	case '^':
	    if (!isdigit((unsigned char) s[1]) || width_flag)
		break;
	    for (s++; isdigit((unsigned char) *s); s++)
		width_flag = width_flag * 10 + *s - '0';
	    width_flag = -width_flag;
	    break;
	}

	// conversion character
	// after switch, data lies between `s1' and `s2'
	const char *s1 = 0, *s2 = 0;
	base = 10;
	switch (*s++) {

	case 's': {
	    s1 = va_arg(val, const char *);
	    if (!s1)
		s1 = "(null)";

	    // transform string, fetch length
	    int len;
	    if (flags & cf_alternate_form) {
		strstore = String(s1).printable();
		len = strstore.length();
	    } else
		len = strlen(s1);

	    // adjust length for precision
	    if (precision >= 0 && precision < len)
		len = precision;

	    // quote characters that look like annotations, readjusting length
	    if (flags & (cf_singlequote | cf_alternate_form)) {
		if (!(flags & cf_alternate_form))
		    strstore = String(s1, len);

		// check first line, considering trailing part of 'msg'
		const char *mbegin = msg.end();
		while (mbegin != msg.begin() && mbegin[-1] != '\n')
		    --mbegin;
		if (skip_anno(strstore.begin(), strstore.end()) != strstore.begin()
		    && skip_anno(mbegin, msg.end()) == msg.end()) {
		    strstore = String::make_stable("{}", 2) + strstore;
		    len += 2;
		}

		// check subsequent lines
		const char *s = std::find(strstore.begin(), strstore.end(), '\n');
		while (s != strstore.end() && s + 1 != strstore.end()) {
		    size_t nextpos = (s + 1) - strstore.begin();
		    if (skip_anno(s + 1, strstore.end()) != s + 1) {
			strstore = strstore.substring(strstore.begin(), s + 1)
			    + String::make_stable("{}", 2)
			    + strstore.substring(s + 1, strstore.end());
			len += 2;
		    }
		    s = std::find(strstore.begin() + nextpos, strstore.end(), '\n');
		}
	    }

	    // obtain begin and end pointers
	    if (flags & (cf_singlequote | cf_alternate_form))
		s1 = strstore.begin();
	    s2 = s1 + len;
	    break;
	}

	case 'c': {
	    int c = va_arg(val, int);
	    // check for extension of 'signed char' to 'int'
	    if (c < 0)
		c += 256;
	    // assume ASCII
	    if (c == '\n')
		strcpy(numbuf, "\\n");
	    else if (c == '\t')
		strcpy(numbuf, "\\t");
	    else if (c == '\r')
		strcpy(numbuf, "\\r");
	    else if (c == '\0')
		strcpy(numbuf, "\\0");
	    else if (c < 0 || c >= 256)
		strcpy(numbuf, "(bad char)");
	    else if (c < 32 || c >= 0177)
		sprintf(numbuf, "\\%03o", c);
	    else
		sprintf(numbuf, "%c", c);
	    s1 = numbuf;
	    s2 = strchr(numbuf, 0);
	    break;
	}

	case '%': {
	    numbuf[0] = '%';
	    s1 = numbuf;
	    s2 = s1 + 1;
	    break;
	}

	case '<':
	    s1 = (flags & cf_utf8 ? "\342\200\230" : "\'");
	    s2 = s1 + strlen(s1);
	    break;

	case '>':
	case ',':
	    s1 = (flags & cf_utf8 ? "\342\200\231" : "\'");
	    s2 = s1 + strlen(s1);
	    break;

	case 'd':
	case 'i':
	    flags |= cf_signed;
	case 'u':
	number: {
	    // protect numbuf from overflow
	    if (field_width > NUMBUF_SIZE)
		field_width = NUMBUF_SIZE;
	    if (precision > NUMBUF_SIZE - 4)
		precision = NUMBUF_SIZE - 4;

	    s2 = numbuf + NUMBUF_SIZE;

	    unsigned long num;
	    switch (width_flag) {
	    case 'H':
	    case -8:
		num = (unsigned char) va_arg(val, int);
		if ((flags & cf_signed) && (signed char) num < 0)
		    num = -(signed char) num, flags |= cf_negative;
		break;
	    case 'h':
	    case -16:
		num = (unsigned short) va_arg(val, int);
		if ((flags & cf_signed) && (short) num < 0)
		    num = -(short) num, flags |= cf_negative;
		break;
	    case 0:
	    case -32:
#if SIZEOF_LONG == 4
	    case 'l':
#endif
#if SIZEOF_SIZE_T == 4
	    case 'z':
#endif
		num = va_arg(val, unsigned);
		if ((flags & cf_signed) && (int) num < 0)
		    num = -(int) num, flags |= cf_negative;
		break;
#if HAVE_INT64_TYPES
# if SIZEOF_LONG == 8
	    case 'l':
# endif
# if SIZEOF_LONG_LONG == 8
	    case 'L':
# endif
# if SIZEOF_SIZE_T == 8
	    case 'z':
# endif
	    case -64: {
		uint64_t qnum = va_arg(val, uint64_t);
		if ((flags & cf_signed) && (int64_t)qnum < 0)
		    qnum = -(int64_t) qnum, flags |= cf_negative;
		StringAccum sa;
		sa.append_numeric(static_cast<String::uint_large_t>(qnum), base, (flags & cf_uppercase));
		s1 = s2 - sa.length();
		memcpy(const_cast<char*>(s1), sa.data(), s2 - s1);
		goto got_number;
	    }
#endif
	    default:
		goto error;
	    }
	    s1 = do_number(num, (char *)s2, base, flags);

#if HAVE_INT64_TYPES
	got_number:
#endif
	    s1 = do_number_flags((char *)s1, (char *)s2, base, flags,
				 precision, field_width);
	    break;
	}

	case 'o':
	    base = 8;
	    goto number;

	case 'X':
	    flags |= cf_uppercase;
	case 'x':
	    base = 16;
	    goto number;

	case 'p': {
	    void *v = va_arg(val, void *);
	    s2 = numbuf + NUMBUF_SIZE;
	    s1 = do_number((unsigned long)v, (char *)s2, 16, flags);
	    s1 = do_number_flags((char *)s1, (char *)s2, 16, flags | cf_alternate_form,
				 precision, field_width);
	    break;
	}

#ifndef __KERNEL__
	case 'e': case 'f': case 'g':
	case 'E': case 'F': case 'G': {
	    char format[80], *f = format, new_numbuf[NUMBUF_SIZE];
	    *f++ = '%';
	    if (flags & cf_alternate_form)
		*f++ = '#';
	    if (precision >= 0)
		f += sprintf(f, ".%d", precision);
	    *f++ = s[-1];
	    *f++ = 0;

	    int len = sprintf(new_numbuf, format, va_arg(val, double));

	    s2 = numbuf + NUMBUF_SIZE;
	    s1 = s2 - len;
	    memcpy((char *)s1, new_numbuf, len); // note: no terminating \0
	    s1 = do_number_flags((char *)s1, (char *)s2, 10, flags & ~cf_alternate_form, -1, field_width);
	    break;
	}
#endif

	case '{': {
	    const char *rbrace = strchr(s, '}');
	    if (!rbrace || rbrace == s)
		assert(0 /* Bad %{ in error */);
	    String name(s, rbrace - s);
	    s = rbrace + 1;
	    for (Conversion *item = error_items; item; item = item->next)
		if (item->name == name) {
		    strstore = item->hook(flags, VA_LIST_REF(val));
		    s1 = strstore.begin();
		    s2 = strstore.end();
		    goto got_result;
		}
	    goto error;
	}

	error:
	default:
	    assert(0 /* Bad % in error */);
	    break;

	}

	// add result of conversion
    got_result:
	int slen = s2 - s1;
	if (slen > field_width)
	    field_width = slen;
	char *dest = msg.extend(field_width);
	if (flags & cf_left_just) {
	    memcpy(dest, s1, slen);
	    memset(dest + slen, ' ', field_width - slen);
	} else {
	    memcpy(dest + field_width - slen, s1, slen);
	    memset(dest, (flags & cf_zero_pad ? '0' : ' '), field_width - slen);
	}
    }

    return msg.take_string();
}

String
ErrorHandler::xformat(int default_flags, const char *fmt, ...)
{
    va_list val;
    va_start(val, fmt);
    String s = vxformat(default_flags, fmt, val);
    va_end(val);
    return s;
}

String
ErrorHandler::xformat(const char *fmt, ...)
{
    va_list val;
    va_start(val, fmt);
    String s = vxformat(0, fmt, val);
    va_end(val);
    return s;
}

String
ErrorHandler::format(const char *fmt, ...)
{
    va_list val;
    va_start(val, fmt);
    String s = vformat(fmt, val);
    va_end(val);
    return s;
}


// ERROR MESSAGE SHORTHAND

void
ErrorHandler::debug(const char *fmt, ...)
{
    va_list val;
    va_start(val, fmt);
    vxmessage(String::make_stable(e_debug, 3), fmt, val);
    va_end(val);
}

void
ErrorHandler::message(const char *fmt, ...)
{
    va_list val;
    va_start(val, fmt);
    vxmessage(String::make_stable(e_info, 3), fmt, val);
    va_end(val);
}

int
ErrorHandler::warning(const char *fmt, ...)
{
    va_list val;
    va_start(val, fmt);
    int r = vxmessage(String::make_stable(e_warning_annotated, 12), fmt, val);
    va_end(val);
    return r;
}

int
ErrorHandler::error(const char *fmt, ...)
{
    va_list val;
    va_start(val, fmt);
    int r = vxmessage(String::make_stable(e_error, 3), fmt, val);
    va_end(val);
    return r;
}

int
ErrorHandler::fatal(const char *fmt, ...)
{
    va_list val;
    va_start(val, fmt);
    int r = vxmessage(String::make_stable(e_fatal, 4), fmt, val);
    va_end(val);
    return r;
}

void
ErrorHandler::ldebug(const String &landmark, const char *fmt, ...)
{
    va_list val;
    va_start(val, fmt);
    String l = make_landmark_anno(landmark);
    vxmessage(String::make_stable(e_debug, 3) + l, fmt, val);
    va_end(val);
}

void
ErrorHandler::lmessage(const String &landmark, const char *fmt, ...)
{
    va_list val;
    va_start(val, fmt);
    String l = make_landmark_anno(landmark);
    vxmessage(String::make_stable(e_info, 3) + l, fmt, val);
    va_end(val);
}

int
ErrorHandler::lwarning(const String &landmark, const char *fmt, ...)
{
    va_list val;
    va_start(val, fmt);
    String l = make_landmark_anno(landmark);
    int r = vxmessage(l + String::make_stable(e_warning_annotated, 12), fmt, val);
    va_end(val);
    return r;
}

int
ErrorHandler::lerror(const String &landmark, const char *fmt, ...)
{
    va_list val;
    va_start(val, fmt);
    String l = make_landmark_anno(landmark);
    int r = vxmessage(String::make_stable(e_error, 3) + l, fmt, val);
    va_end(val);
    return r;
}

int
ErrorHandler::lfatal(const String &landmark, const char *fmt, ...)
{
    va_list val;
    va_start(val, fmt);
    String l = make_landmark_anno(landmark);
    int r = vxmessage(String::make_stable(e_fatal, 4) + l, fmt, val);
    va_end(val);
    return r;
}

int
ErrorHandler::xmessage(const String &str)
{
    String xstr = decorate(str);

    int min_level = 1000, xlevel = 1000;
    const char *s = xstr.begin(), *end = xstr.end();
    void *user_data = 0;
    while (s != end) {
	const char *l = parse_anno(xstr, s, end, "#<>", &xlevel,
				   (const char *) 0);
	const char *nl = std::find(l, end, '\n');
	String line = xstr.substring(s, nl);
	s = nl + (nl != end);
	user_data = emit(line, user_data, s != end);
	min_level = (xlevel < min_level ? xlevel : min_level);
    }

    account(min_level);

    return (min_level <= el_warning ? error_result : ok_result);
}

String
ErrorHandler::vformat(const char *fmt, va_list val)
{
    return vxformat(0, fmt, val);
}

String
ErrorHandler::decorate(const String &str)
{
    return str;
}

void *
ErrorHandler::emit(const String &, void *user_data, bool)
{
    return user_data;
}


#ifndef __KERNEL__
//
// FILE ERROR HANDLER
//

FileErrorHandler::FileErrorHandler(FILE *f, const String &context)
    : _f(f), _context(context), _default_flags(0)
{
# if HAVE_UNISTD_H
    if (isatty(fileno(_f))) {
# endif
	char *s = getenv("LANG");
	if (s && (strstr(s, "UTF-8") != 0 || strstr(s, "UTF8") != 0
		  || strstr(s, "utf8") != 0))
	    _default_flags |= cf_utf8;
# if HAVE_UNISTD_H
    }
# endif
}

String
FileErrorHandler::vformat(const char *fmt, va_list val)
{
    return vxformat(_default_flags, fmt, val);
}

void *
FileErrorHandler::emit(const String &str, void *, bool)
{
    String landmark;
    const char *s = parse_anno(str, str.begin(), str.end(),
			       "l", &landmark, (const char *) 0);
    StringAccum sa;
    sa << _context << clean_landmark(landmark, true)
       << str.substring(s, str.end()) << '\n';
    ssize_t result = fwrite(sa.begin(), 1, sa.length(), _f);
    (void) result;
    return 0;
}

void
FileErrorHandler::account(int level)
{
    ErrorHandler::account(level);
    if (level <= el_abort)
	abort();
    else if (level <= el_fatal)
	exit(-level);
}

#endif


//
// STATIC ERROR HANDLERS
//

ErrorHandler::Conversion *
ErrorHandler::add_conversion(const String &name, ConversionFunction function)
{
    if (Conversion *c = new Conversion) {
	c->name = name;
	c->hook = function;
	c->next = error_items;
	error_items = c;
	return c;
    } else
	return 0;
}

int
ErrorHandler::remove_conversion(ErrorHandler::Conversion *conv)
{
    Conversion **pprev = &error_items;
    for (Conversion *c = error_items; c; pprev = &c->next, c = *pprev)
	if (c == conv) {
	    *pprev = c->next;
	    delete c;
	    return 0;
	}
    return -1;
}

ErrorHandler *
ErrorHandler::static_initialize(ErrorHandler *default_handler)
{
    if (!the_silent_handler) {
	the_default_handler = default_handler;
	the_silent_handler = new SilentErrorHandler;
    }
    return default_handler;
}

void
ErrorHandler::static_cleanup()
{
    delete the_default_handler;
    delete the_silent_handler;
    the_default_handler = the_silent_handler = 0;
    while (error_items) {
	Conversion *next = error_items->next;
	delete error_items;
	error_items = next;
    }
}

void
ErrorHandler::set_default_handler(ErrorHandler *errh)
{
    the_default_handler = errh;
}


//
// ERROR VENEER
//

String
ErrorVeneer::vformat(const char *fmt, va_list val)
{
    if (_errh)
	return _errh->vformat(fmt, val);
    else
	return ErrorHandler::vformat(fmt, val);
}

String
ErrorVeneer::decorate(const String &str)
{
    if (_errh)
	return _errh->decorate(str);
    else
	return ErrorHandler::decorate(str);
}

void *
ErrorVeneer::emit(const String &str, void *user_data, bool more)
{
    if (_errh)
	return _errh->emit(str, user_data, more);
    else
	return ErrorHandler::emit(str, user_data, more);
}

void
ErrorVeneer::account(int level)
{
    ErrorHandler::account(level);
    if (_errh)
	_errh->account(level);
}


//
// CONTEXT ERROR HANDLER
//

ContextErrorHandler::ContextErrorHandler(ErrorHandler *errh, const char *fmt,
					 ...)
    : ErrorVeneer(errh), _indent(String::make_stable("  ", 2)),
      _context_printed(false)
{
    va_list val;
    va_start(val, fmt);
    _context = ErrorVeneer::vformat(fmt, val);
    va_end(val);
    if (_context)
	_context = combine_anno(_context, String::make_stable("{context:context}", 17));
}

String
ContextErrorHandler::decorate(const String &str)
{
    String cstr = ErrorVeneer::decorate(str), context_anno;
    const char *cstr_endanno = parse_anno(cstr, cstr.begin(), cstr.end(),
					  "context", &context_anno,
					  (const char *) 0);
    if (context_anno.equals("no", 2))
	return cstr;

    String icstr;
    if (context_anno.equals("noindent", 8))
	icstr = combine_anno(cstr, _context_landmark);
    else
	icstr = combine_anno(cstr, _context_landmark + _indent);

    if (!_context_printed && !context_anno.equals("nocontext", 9)) {
	String astr = combine_anno(combine_anno(_context, _context_landmark),
				   cstr.substring(cstr.begin(), cstr_endanno));
	if (astr && astr.back() != '\n')
	    astr += '\n';
	_context_printed = true;
	return ErrorVeneer::decorate(astr) + icstr;
    } else
	return icstr;
}


//
// PREFIX ERROR HANDLER
//

PrefixErrorHandler::PrefixErrorHandler(ErrorHandler *errh,
				       const String &prefix)
    : ErrorVeneer(errh), _prefix(prefix)
{
}

String
PrefixErrorHandler::decorate(const String &str)
{
    return ErrorVeneer::decorate(combine_anno(str, _prefix));
}


//
// LANDMARK ERROR HANDLER
//

LandmarkErrorHandler::LandmarkErrorHandler(ErrorHandler *errh, const String &landmark)
    : ErrorVeneer(errh), _landmark(make_landmark_anno(landmark))
{
}

String
LandmarkErrorHandler::decorate(const String &str)
{
    return ErrorVeneer::decorate(combine_anno(str, _landmark));
}


//
// BAIL ERROR HANDLER
//

#ifndef __KERNEL__

BailErrorHandler::BailErrorHandler(ErrorHandler *errh, int l)
    : ErrorVeneer(errh), _level(l)
{
}

void
BailErrorHandler::account(int level)
{
    ErrorVeneer::account(level);
    if (level <= _level)
	exit(1);
}

#endif
