src/lib/error.c
author Tero Marttila <terom@fixme.fi>
Wed, 27 May 2009 23:57:48 +0300
branchnew-lib-errors
changeset 217 7728d6ec3abf
parent 216 a10ba529ae39
child 218 5229a5d098b2
permissions -rw-r--r--
nexus.c compiles
#include "error.h"
#include "log.h"

const struct error_list general_errors = ERROR_LIST("general",
    ERROR_TYPE(         ERR_MEM,                "memory allocation error"           ),
    ERROR_TYPE_STRING(  ERR_NOT_IMPLEMENTED,    "function not implmented"           ),
    ERROR_TYPE_STRING(  ERR_MISC,               "miscellaneous error"               ),
    ERROR_TYPE_STRING(  ERR_CMD_OPT,            "invalid command line option"       ),
    ERROR_TYPE(         ERR_UNKNOWN,            "unknown error"                     )
);

const struct error_type* error_lookup_code (const struct error_list *list, err_t code)
{
    const struct error_type *type;

    // find matching code
    for (type = list->list; type->code; type++) {
        if (type->code == code)
            // found
            return type;
    }
    
    // not found
    return NULL;
}

const struct error_type* error_lookup (const error_t *err)
{
    struct error_item *item;
    struct error_type *type = NULL, *list;

    // traverse stack
    for (item = err->cur; item && item >= err->stack; item--) {
        if (!list)
            // initial type
            list = item->list;

        // verify list/type
        if (!list)
            log_warn("Uknown type");

        else if (!(type = error_lookup_code(list, item->code)))
            log_warn("Uknown code %#04x for list %s", item->code, list->name);

        else
            // expected list for next item
            list = type->sublist;
    }

    // return what we found
    return type;
}

const char* error_name (const struct error_list *list, err_t code);
{
    struct error_type *type;
    
    // just do a lookup
    if ((type = error_lookup_code(list, code)))
        return "[unknown]";

    else
        return type->name;
}

const char* error_msg (const error_t *err)
{
    // XXX: bad to have a single buffer
    static char buf[ERROR_MSG_MAX];

    char *buf_ptr = buf; 
    size_t buf_size = sizeof(buf);

    struct error_item *item;
    struct error_type *type;

    // traverse stack
    for (item = err->cur; item && item >= err->stack; item--) {
        if (!list)
            // initial lookup table
            list = item->list;

        // verify list/type
        if (!list) {
            // no type info at all
            buf_ptr += str_advance(&buf_size, NULL, str_append_fmt(buf_ptr, buf_size, "[%#04x]", item->code));

        } else if (!(type = error_lookup_code(list, item->code))) {
            // couldn't find code in list
            buf_ptr += str_advance(&buf_size, NULL, str_append_fmt(buf_ptr, buf_size, "[%s:%#02x]", list->name, item->code));

        } else {
            // found code's type
            // delimit using colons, except at the end
            buf_ptr += str_advance(&buf_size, NULL, str_append(buf_ptr, buf_size, "[%s] %s%s", 
                list->name, type->name, item == err->stack ? "" : ": "
            ));

            // expected list for next item
            list = type->sublist;
        }
    }
    
    // add info for extra
    if (err->extra_type != type->extra_type) {
        // type mismatch
        buf_ptr += str_advance(&buf_size, NULL, str_append_fmt(buf_ptr, buf_size, ": [error_extra type mismatch: %s <=> %s",
            err->extra_type ? err->extra_type->name : NULL,
            item->extra_type ? item->extra_type->name : NULL
        ));

    } else if (err->extra_type) {
        // add extra info
        buf_ptr += str_advance(&buf_size, NULL, str_append_fmt(buf_ptr, buf_size, ": %s", 
            err->extra_type->msg_func(item->extra_type, &item->extra_value)
        ));

    }
    
    // ok, return message
    return buf;
}

bool error_cmp_eq (const error_t *a, const error_t *b)
{
    // XXX: implement
    return true;
}

void error_reset (error_t *err)
{
    memset(err, 0, sizeof(*err));
}

/**
 * Advance error stack to next position, and return pointer.
 *
 * Returns NULL if the stack runs out of space.
 */
struct error_item* error_next (error_t *err)
{
    if (!err->cur)
        // initial position
        return (err->cur = err->stack);

    else if (err->cur < err->stack + ERR_DEPTH_MAX)
        return (err->cur++);

    else
        return NULL;
}

void error_set (error_t *err, const struct error_list *list, err_t code)
{
    struct error_item *item;

    // empty
    error_reset(err);

    // next
    if (!(item = error_next(err)))
        return;

    // store
    item->code = code;
    item->list = list;
}

void error_set_extra (error_t *err, const struct error_list *list, err_t code, const struct error_extra_type *type, union error_extra extra)
{
    struct error_item *item;

    // empty
    error_reset(err);

    // next
    if (!(item = error_next(err)))
        return;

    // store
    item->code = code;
    item->list = list;
    err->extra_type = type;
    err->extra_value = extra;
}

void error_push (error_t *err, const struct error_list *list, err_t code)
{
    struct error_item *item;

    // next
    if (!(item = error_next(err)))
        return;

    // store
    item->code = code;
    item->list = list;
}

void error_copy (error_t *dst, const error_t *src)
{
    struct error_item *item;

    // reset
    error_reset(dst);

    // copy each item
    for (item = src->stack; src->cur && item <= src->cur; item++)
        // push it on separately
        error_push(dst, item->list, item->code);

    // store extra
    dst->extra_type = src->extra_type;
    dst->extra_value = src->extra_value;
}

/*
 * Weird trickery to call log_msg_va2 with the fmt's in the right order (prefix).
 *
 * XXX: log should provide similar functionality
 */
void _error_abort_ (const char *fmt2, va_list fmtargs2, const char *fmt1, ...)
{
    va_list vargs;

    va_start(vargs, fmt1);
    _log_msg_va2(LOG_FATAL, "error_abort", fmt1, vargs, fmt2, fmtargs2);
    va_end(vargs);
}

void _error_abort (const char *file, const char *line, const char *func, const char *fmt, ...)
{
    va_list vargs;

    va_start(vargs, fmt);
    _error_abort_(fmt, vargs, "%s:%d[%s]", file, line, func);
    va_end(vargs);
}