src/error.h
author Tero Marttila <terom@fixme.fi>
Thu, 21 May 2009 16:23:50 +0300
branchlua-threads
changeset 207 3fa22abb5421
parent 196 873796250c60
permissions -rw-r--r--
fix lua_nexus_sleep to use EV_TIMEOUT + misc
#ifndef ERROR_H
#define ERROR_H

/**
 * @file
 *
 * Error-handling functions
 */
#include <errno.h>
#include <stdbool.h>

/**
 * The type used for error codes is an explicitly *unsigned* int, meaning that error codes themselves are positive.
 * Negative error codes (as signed ints) also exist in some places, and they are just a negative err_t.
 */
typedef unsigned int err_t;

/**
 * Ways to interpret error_info.extra
 */
enum error_extra_types {
    /** No extra info */
    ERR_EXTRA_NONE      = 0,

    /** libc errno, using strerror() */
    ERR_EXTRA_ERRNO,

    /** libc resolver, using gai_strerror() */
    ERR_EXTRA_GAI,

    /** GnuTLS, using gnutls_strerror() */
    ERR_EXTRA_GNUTLS,

    /** Static error message string */
    ERR_EXTRA_STR,
};

/**
 * List of defined error codes, organized mostly by function name
 */
enum error_code {
    _ERR_INVALID    = 0x000000,
    
    /** stdlib.h functions */
    _ERR_STDLIB     = 0x000100,
    ERR_CALLOC,
    ERR_STRDUP,
    ERR_SIGACTION,
    ERR_ACCESS_READ,
    
    /** DNS resolver */
    _ERR_RESOLVER   = 0x000200,
    ERR_GETADDRINFO,
    ERR_GETADDRINFO_EMPTY, 
    
    /** socket/IO errors */
    _ERR_SOCK       = 0x000300,
    ERR_SOCKET,         ///< socket(2) failed
    ERR_CONNECT,        ///< connect(2) error - either direct or async
    ERR_READ,           ///< read(2) error - will probably show up as an ERR_WRITE as well
    ERR_WRITE,          ///< write(2) error - data was unsent, will probably show up as an ERR_READ as well
    ERR_WRITE_EOF,      ///< write(2) gave EOF - zero bytes written
    ERR_FCNTL,          ///< fcntl(2) failed
    ERR_CLOSE,          ///< close(2) failed, some written data was probably not sent
    ERR_GETSOCKOPT,     ///< getsockopt(2) failed
    ERR_OPEN,           ///< open(2) failed
    ERR_ACCEPT,         ///< accept(2) failed
    ERR_BIND,           ///< bind(2) failed
    ERR_LISTEN,         ///< listen(2) failed

    /** @see sock_gnutls_error_code */
    _ERR_GNUTLS     = 0x000400,

    /** Libevent errors */
    _ERR_LIBEVENT   = 0x000500,
    ERR_EVENT_NEW,
    ERR_EVENT_ADD,
    ERR_EVENT_DEL,

    /** Evsql errors */
    _ERR_EVSQL      = 0x000600,
    ERR_EVSQL_NEW_PQ,
    ERR_EVSQL_QUERY_EXEC,

    /** irc_proto errors */
    _ERR_IRC_LINE   = 0x000700,
    ERR_LINE_TOO_LONG,
    ERR_LINE_INVALID_TOKEN,
    ERR_INVALID_NM,
    ERR_INVALID_NICK_LENGTH,

    /** irc_conn errors */
    _ERR_IRC_CONN   = 0x000800,
    ERR_IRC_CONN_REGISTER_STATE,
    ERR_IRC_CONN_QUIT_STATE,

    /** irc_net errors */
    _ERR_IRC_NET    = 0x000900,
    ERR_IRC_NET_INFO,
    ERR_IRC_NET_STATE,

    /** @see module_error_code */
    _ERR_MODULE     = 0x000a00,

    /** config errors */
    _ERR_CONFIG     = 0x000b00,
    ERR_CONFIG_NAME,        ///< unknown option name
    ERR_CONFIG_TYPE,        ///< invalid value type for parameter
    ERR_CONFIG_REQUIRED,    ///< missing value for required parameter
    ERR_CONFIG_VALUE,       ///< invalid value
    ERR_CONFIG_PARAMS,      ///< invalid number of parameters
    
    /** lua errors */
    _ERR_LUA        = 0x000c00,
    ERR_LUA_MEM,
    ERR_LUA_SYNTAX,
    ERR_LUA_RUN,
    ERR_LUA_ERR,
    ERR_LUA_FILE,

    /** irc_chan errors */
    _ERR_IRC_CHAN   = 0x000d00,
    ERR_IRC_CHAN_STATE,

    /** pcre errors */
    _ERR_PCRE       = 0x000e00,
    ERR_PCRE_COMPILE,       ///< pcre_compile: <error_msg>
    ERR_PCRE_EXEC,          ///< pcre_exec: <error code>

    /** str errors */
    _ERR_STR        = 0x000f00,

    /** Transport errors */
    _ERR_TRANSPORT  = 0x001000,

    /** General errors */
    _ERR_GENERAL    = 0xffff00,
    ERR_MISC,               ///< general error
    ERR_CMD_OPT,            ///< invalid commandline option
    ERR_UNKNOWN,
    ERR_DUP_NAME,           ///< duplicate name
    ERR_EOF,                ///< end of file
    ERR_MEM,                ///< memory allocation error
    ERR_NOT_IMPLEMENTED,    ///< function not implemented
};

/**
 * Table of error descriptions
 */
struct error_desc {
    /** The flat error code */
    err_t code;

    /** The short name */
    const char *name;

    /** How to interpret .extra */
    enum error_extra_types extra_type;
};

/**
 * An error code and associated extra infos
 */
struct error_info {
    /** 
     * The base error code.
     *
     * This is a signed int because we need to be able to manipulate negative errors codes as well.
     */
    signed int code;
    
    union {
        /** Additional detail info, usually some third-party error code, as defined by the code's ERR_EXTRA_* */
        int extra;

        /** Additional info, stored as a pointer to a static string (note how dangerous this is) */
        const char *extra_str;
    };
};

/**
 * The public names
 */
typedef struct error_info error_t;

/**
 * Translate an err_t into a function name.
 */
const char *error_name (err_t code);

/**
 * Look up the error_desc for the given error code
 */
const struct error_desc* error_lookup (err_t code);

/**
 * Maximum length of error messages returned by error_msg (including NUL byte)
 */
#define ERROR_MSG_MAXLEN 1024

/**
 * Translate an error_info into a message.
 *
 * This is returned as a pointer into a statically allocated buffer. It is not re-entrant.
 */
const char *error_msg (const error_t *err);

/**
 * Compare the given errors for equivalency
 */
bool error_cmp_eq (const error_t *a, const error_t *b);

/** No error, evaulates as logical false */
#define SUCCESS (0)

/** Evaulates to error_info.code as lvalue */
#define ERROR_CODE(err_info_ptr) ((err_info_ptr)->code)

/** Evaulates to error_info.extra as lvalue */
#define ERROR_EXTRA(err_info_ptr) ((err_info_ptr)->extra)

/** Set error_info.code to SUCCESS, evaulates as zero */
#define RESET_ERROR(err_info_ptr) ((err_info_ptr)->code = SUCCESS)

/** Compare error_info.code != 0 */
#define IS_ERROR(err_info_ptr) (!!(err_info_ptr)->code)

/** Compare the err_code/err_extra for an err_info */
#define MATCH_ERROR(err_info_ptr, err_code, err_extra) ((err_info_ptr)->code == (err_code) && (err_info_ptr)->extra == (err_extra))

/** Set error_info.code, but leave err_extra as-is. Evaluates to err_code */
#define SET_ERROR(err_info_ptr, err_code) ((err_info_ptr)->code = (err_code))

/** Set error_info.code/extra. XXX: should evaluate to err_code */
#define _SET_ERROR_EXTRA(err_info_ptr, err_code, err_extra) (err_info_ptr)->code = (err_code); (err_info_ptr)->extra = (err_extra)
#define SET_ERROR_EXTRA(err_info_ptr, err_code, err_extra) do { _SET_ERROR_EXTRA(err_info_ptr, err_code, err_extra); } while (0)

/** Set error_info.code to err_code, and .extra to errno. XXX: should evaulate to err_code */
#define _SET_ERROR_ERRNO(err_info_ptr, err_code) _SET_ERROR_EXTRA(err_info_ptr, err_code, errno);
#define SET_ERROR_ERRNO(err_info_ptr, err_code) SET_ERROR_EXTRA(err_info_ptr, err_code, errno);

/** 
 * Set error_info.code to err_code, and .extra_str to str. The given string pointer should remain valid while the error
 * is being handled down-stack.
 */
#define _SET_ERROR_STR(err_info_ptr, err_code, err_str) (err_info_ptr)->code = (err_code); (err_info_ptr)->extra_str = (err_str)
#define SET_ERROR_STR(err_info_ptr, err_code, err_str) do { _SET_ERROR_STR(err_info_ptr, err_code, err_str); } while(0)

/** Set error_info from another error_info. Evaluates to the new error_info */
#define SET_ERROR_INFO(err_info_ptr, from_ptr) (*err_info_ptr = *from_ptr)

/** Same as above, but also return err_code from func. XXX: use 'return SET_ERROR...' instead */
#define RETURN_SET_ERROR(err_info_ptr, err_code) do { SET_ERROR(err_info_ptr, err_code); return (err_code); } while (0)
#define RETURN_SET_ERROR_EXTRA(err_info_ptr, err_code, err_extra) do { _SET_ERROR_EXTRA(err_info_ptr, err_code, err_extra); return (err_code); } while (0)
#define RETURN_SET_ERROR_ERRNO(err_info_ptr, err_code) do { _SET_ERROR_ERRNO(err_info_ptr, err_code); return (err_code); } while (0)
#define RETURN_SET_ERROR_INFO(err_info_ptr, from_ptr) do { SET_ERROR_INFO(err_info_ptr, from_ptr); return (from_ptr->code); } while (0)
#define RETURN_SET_ERROR_STR(err_info_ptr, err_code, err_str) do { _SET_ERROR_STR(err_info_ptr, err_code, err_str); return (err_code); } while (0)

/** Same as above, but also do a 'goto error' */
#define JUMP_SET_ERROR(err_info_ptr, err_code) do { SET_ERROR(err_info_ptr, err_code); goto error; } while (0)
#define JUMP_SET_ERROR_EXTRA(err_info_ptr, err_code, err_extra) do { _SET_ERROR_EXTRA(err_info_ptr, err_code, err_extra); goto error; } while (0)
#define JUMP_SET_ERROR_ERRNO(err_info_ptr, err_code) do { _SET_ERROR_ERRNO(err_info_ptr, err_code); goto error; } while (0)
#define JUMP_SET_ERROR_INFO(err_info_ptr, from_ptr) do { SET_ERROR_INFO(err_info_ptr, from_ptr); goto error; } while (0)
#define JUMP_SET_ERROR_STR(err_info_ptr, err_code, err_str) do { _SET_ERROR_STR(err_info_ptr, err_code, err_str); goto error; } while (0)

/**
 * Macro used to mark code segments that should never be executed (e.g. switch-default), kind of like assert
 */
#include <stdlib.h>
#define NOT_REACHED(val) abort()

#endif