#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