# HG changeset patch # User Tero Marttila # Date 1243454820 -10800 # Node ID a10ba529ae39657e1ee12103cf5b360b65f876d7 # Parent 85863b89e38b04ab0a6baa3872c1826218d93c15 initial error code diff -r 85863b89e38b -r a10ba529ae39 src/error.c --- a/src/error.c Sat May 23 00:33:23 2009 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,242 +0,0 @@ - -#include "error.h" - -// for the error_desc tables -#include "sock.h" -#include "ssl_internal.h" -#include "module.h" - -#include -#include - -struct error_desc _core_error_desc[] = { - { ERR_CALLOC, "calloc", ERR_EXTRA_NONE }, - { ERR_STRDUP, "strdup", ERR_EXTRA_NONE }, - { ERR_SIGACTION, "sigaction", ERR_EXTRA_ERRNO }, - { ERR_ACCESS_READ, "access(R_OK)", ERR_EXTRA_ERRNO }, - { ERR_GETADDRINFO, "getaddrinfo", ERR_EXTRA_GAI }, - { ERR_GETADDRINFO_EMPTY, "getaddrinfo: no results", ERR_EXTRA_NONE }, - { ERR_EVENT_NEW, "event_new", ERR_EXTRA_NONE }, - { ERR_EVENT_ADD, "event_add", ERR_EXTRA_NONE }, - { ERR_EVSQL_NEW_PQ, "evsql_new_pq", ERR_EXTRA_NONE }, - { ERR_EVSQL_QUERY_EXEC, "evsql_query_exec", ERR_EXTRA_NONE }, - { ERR_CMD_OPT, "argv", ERR_EXTRA_STR }, - { _ERR_INVALID, NULL, 0 } - -}, _sock_error_desc[] = { - { ERR_SOCKET, "socket", ERR_EXTRA_ERRNO }, - { ERR_CONNECT, "connect", ERR_EXTRA_ERRNO }, - { ERR_READ, "read", ERR_EXTRA_ERRNO }, - { ERR_WRITE, "write", ERR_EXTRA_ERRNO }, - { ERR_WRITE_EOF, "write: EOF", ERR_EXTRA_NONE }, - { ERR_FCNTL, "fcntl", ERR_EXTRA_ERRNO }, - { ERR_CLOSE, "close", ERR_EXTRA_ERRNO }, - { ERR_GETSOCKOPT, "getsockopt", ERR_EXTRA_ERRNO }, - { ERR_OPEN, "open", ERR_EXTRA_ERRNO }, - { ERR_ACCEPT, "accept", ERR_EXTRA_ERRNO }, - { ERR_BIND, "bind", ERR_EXTRA_ERRNO }, - { ERR_LISTEN, "listen", ERR_EXTRA_ERRNO }, - { _ERR_INVALID, NULL, 0 } - -}, _sock_gnutls_error_desc[] = { - { ERR_GNUTLS_CERT_ALLOC_CRED, "gnutls_certificate_allocate_credentials", ERR_EXTRA_GNUTLS }, - { ERR_GNUTLS_GLOBAL_INIT, "gnutls_global_init", ERR_EXTRA_GNUTLS }, - { ERR_GNUTLS_SET_DEFAULT_PRIORITY, "gnutls_set_default_priority", ERR_EXTRA_GNUTLS }, - { ERR_GNUTLS_CRED_SET, "gnutls_credentials_set", ERR_EXTRA_GNUTLS }, - { ERR_GNUTLS_HANDSHAKE, "gnutls_handshake", ERR_EXTRA_GNUTLS }, - { ERR_GNUTLS_RECORD_SEND, "gnutls_record_send", ERR_EXTRA_GNUTLS }, - { ERR_GNUTLS_RECORD_RECV, "gnutls_record_recv", ERR_EXTRA_GNUTLS }, - { ERR_GNUTLS_RECORD_GET_DIRECTION, "gnutls_record_get_direction", ERR_EXTRA_GNUTLS }, - { ERR_GNUTLS_CERT_VERIFY_PEERS2, "gnutls_certificate_verify_peers2", ERR_EXTRA_GNUTLS }, - { ERR_GNUTLS_CERT_VERIFY, "X.509 Certificate verification failed", ERR_EXTRA_STR }, - { ERR_GNUTLS_CERT_SET_X509_TRUST_FILE,"gnutls_certificate_set_x509_trust_file", ERR_EXTRA_GNUTLS }, - { ERR_GNUTLS_CERT_SET_X509_KEY_FILE, "gnutls_certificate_set_x509_key_file", ERR_EXTRA_GNUTLS }, - { _ERR_INVALID, NULL, 0 } - -}, _irc_error_desc[] = { - { ERR_LINE_TOO_LONG, "IRC line is too long", ERR_EXTRA_NONE }, - { ERR_LINE_INVALID_TOKEN, "Illegal token value for IRC line", ERR_EXTRA_NONE }, - { ERR_INVALID_NM, "Invalid nickmask", ERR_EXTRA_NONE }, - { ERR_INVALID_NICK_LENGTH, "Nickname is too long", ERR_EXTRA_NONE }, - - // extra: the name of the invalid field - { ERR_IRC_NET_INFO, "invalid irc_net_info", ERR_EXTRA_STR }, - { ERR_IRC_NET_STATE, "invalid irc_net state for operation", ERR_EXTRA_NONE }, - { ERR_IRC_CHAN_STATE, "invalid irc_chan state for operation", ERR_EXTRA_NONE }, - - { _ERR_INVALID, NULL, 0 } - -}, _config_error_desc[] = { - { ERR_CONFIG_NAME, "unknown config option", ERR_EXTRA_STR }, - { ERR_CONFIG_TYPE, "invalid config type", ERR_EXTRA_NONE }, - { ERR_CONFIG_REQUIRED, "missing required value", ERR_EXTRA_STR }, - { ERR_CONFIG_VALUE, "invalid value", ERR_EXTRA_STR }, - { ERR_CONFIG_PARAMS, "invalid number of paramters", ERR_EXTRA_NONE }, - - { _ERR_INVALID, NULL, 0 } - -}, _module_error_desc[] = { - { ERR_MODULE_NAME, "invalid module name", ERR_EXTRA_NONE }, - { ERR_MODULE_DUP, "module already loaded", ERR_EXTRA_NONE }, - { ERR_MODULE_PATH, "invalid module path", ERR_EXTRA_STR }, - { ERR_MODULE_OPEN, "module dlopen() failed", ERR_EXTRA_STR }, - { ERR_MODULE_SYM, "module dlsym() failed", ERR_EXTRA_STR }, - { ERR_MODULE_INIT_FUNC, "invalid module init func", ERR_EXTRA_STR }, - { ERR_MODULE_CONF, "module_conf", ERR_EXTRA_STR }, - { _ERR_INVALID, NULL, 0 } - -}, _lua_error_desc[] = { - { ERR_LUA_MEM, "lua: out of memory", ERR_EXTRA_STR }, - { ERR_LUA_SYNTAX, "lua: syntax error", ERR_EXTRA_STR }, - { ERR_LUA_RUN, "lua: runtime error", ERR_EXTRA_STR }, - { ERR_LUA_ERR, "lua: error handling error", ERR_EXTRA_STR }, - { ERR_LUA_FILE, "lua: error loading file", ERR_EXTRA_STR }, - { _ERR_INVALID, NULL, 0 } - -}, _pcre_error_desc[] = { - { ERR_PCRE_COMPILE, "pcre_compile", ERR_EXTRA_STR }, - { ERR_PCRE_EXEC, "pcre_exec", ERR_EXTRA_STR }, - { _ERR_INVALID, NULL, 0 } -}, _general_error_desc[] = { - { ERR_MISC, "miscellaneous error", ERR_EXTRA_STR }, - { ERR_CMD_OPT, "invalid command line option", ERR_EXTRA_STR }, - { ERR_DUP_NAME, "duplicate name", ERR_EXTRA_STR }, - { ERR_EOF, "EOF", ERR_EXTRA_NONE }, - { ERR_MEM, "memory allocation error", ERR_EXTRA_NONE }, - { ERR_NOT_IMPLEMENTED, "function not implemented", ERR_EXTRA_NONE }, - { _ERR_INVALID, NULL, 0 } -}; - -/** - * Array of error_desc tables - */ -static struct error_desc* _desc_tables[] = { - _core_error_desc, - _sock_error_desc, - _sock_gnutls_error_desc, - _irc_error_desc, - _config_error_desc, - _module_error_desc, - _lua_error_desc, - _pcre_error_desc, - _general_error_desc, - NULL -}; - -const struct error_desc* error_lookup (err_t code) -{ - struct error_desc **desc_table, *desc = NULL; - - // iterate over each defined error_desc array - for (desc_table = _desc_tables; *desc_table; desc_table++) { - for (desc = *desc_table; desc->code && desc->name; desc++) { - // compare code - if (desc->code == code) - // found - return desc; - } - } - - // not found - return NULL; -} - -const char *error_name (err_t code) -{ - const struct error_desc *desc; - - if (!code) - // no error... - return "success"; - - else if ((desc = error_lookup(code))) - // found an error_desc for it - return desc->name; - - else - // unknown - return "[unknown]"; -} - -const char *error_msg (const struct error_info *err) -{ - static char msg[ERROR_MSG_MAXLEN]; - const struct error_desc *desc; - - // do we have an error_desc for it? - if ((desc = error_lookup(err->code)) == NULL) - // ??? - snprintf(msg, ERROR_MSG_MAXLEN, "[%#.8x]: %#.8x", err->code, err->extra); - - else - // intrepret .extra - switch (desc->extra_type) { - case ERR_EXTRA_NONE: - // no additional info - snprintf(msg, ERROR_MSG_MAXLEN, "%s", desc->name); - break; - - case ERR_EXTRA_ERRNO: - // strerror - snprintf(msg, ERROR_MSG_MAXLEN, "%s: %s", desc->name, strerror(err->extra)); - break; - - case ERR_EXTRA_GAI: - // gai_strerror - snprintf(msg, ERROR_MSG_MAXLEN, "%s: %s", desc->name, gai_strerror(err->extra)); - break; - - case ERR_EXTRA_GNUTLS: - // gnutls_strerror - snprintf(msg, ERROR_MSG_MAXLEN, "%s: %s", desc->name, gnutls_strerror(err->extra)); - break; - - case ERR_EXTRA_STR: - // static error message string - snprintf(msg, ERROR_MSG_MAXLEN, "%s: %s", desc->name, err->extra_str); - break; - - default: - // ??? - snprintf(msg, ERROR_MSG_MAXLEN, "%s: %#.8x", desc->name, err->extra); - break; - } - - // return static pointer - return msg; -} - -bool error_cmp_eq (const error_t *a, const error_t *b) -{ - const struct error_desc *desc; - - // compare the top-level code - if (a->code != b->code) - return false; - - // lookup the extra type - if ((desc = error_lookup(a->code)) == NULL) - // not good... - return false; - - // compare by type - switch (desc->extra_type) { - case ERR_EXTRA_NONE: - return true; - - case ERR_EXTRA_ERRNO: - case ERR_EXTRA_GAI: - case ERR_EXTRA_GNUTLS: - // integer comparison - return (a->extra == b->extra); - - case ERR_EXTRA_STR: - // string comparison - return a->extra_str && b->extra_str && (strcmp(a->extra_str, b->extra_str) == 0); - - default: - // ??? - return false; - } -} - diff -r 85863b89e38b -r a10ba529ae39 src/error.h --- a/src/error.h Sat May 23 00:33:23 2009 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,272 +0,0 @@ -#ifndef ERROR_H -#define ERROR_H - -/** - * @file - * - * Error-handling functions - */ -#include -#include - -/** - * 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: - ERR_PCRE_EXEC, ///< pcre_exec: - - /** 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 -#define NOT_REACHED(val) abort() - -#endif diff -r 85863b89e38b -r a10ba529ae39 src/lib/error.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib/error.c Wed May 27 23:07:00 2009 +0300 @@ -0,0 +1,226 @@ +#include "error.h" +#include "log.h" + +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 error_t *err) +{ + struct error_type *type; + + // just do a lookup + if ((type = error_lookup(err))) + 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; +} + +/** + * 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); +} + diff -r 85863b89e38b -r a10ba529ae39 src/lib/error.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib/error.h Wed May 27 23:07:00 2009 +0300 @@ -0,0 +1,278 @@ +#ifndef LIBQMSK_ERROR_H +#define LIBQMSK_ERROR_H + +/** + * @file + * + * Support for error handling, using explicit function return codes and error info contexts. + * + * This is designed to support multiple "namespaces" of errors, which are entirely independent of eachother. These + * namespaces can then reference one another, to build a 'tree' of useable error codes. Hence, each 'level' of this + * tree has it's own set of error codes, and then error info contexts contain a stack of these error codes, which can + * then be used to trace the error down. + */ + +/** + * The type used to represent a scalar error code, there are only unique per level. + * + * Note that this type is signed to avoid nastyness with negating error codes, but all valid ERR_* codes are *always* + * greater than zero in value. + */ +typedef signed short err_t; + +/** + * Standardized err_t code to mean no error, evaluates as logical false. + */ +static const err_t SUCCESS = 0; + +/** + * Extra information about an error, used to reference error codes in external libraries etc. + */ +union error_extra { + /** (signed) integer code */ + int int_; + + /** Constant string */ + const char *str; +}; + +/** + * Errors can contain 'extra' information to reference things like error codes from external libraries; when these are + * used, a 'type' must be specified to handle these. + */ +struct error_extra_type { + /** Short name */ + const char *name; + + /** Conversion func, must return a static pointer */ + const char * (*msg_func) (const struct error_extra_type *type, const union error_extra *extra); +}; + +/** + * error_extra_type for libc errno values + */ +const struct error_extra_type error_extra_errno; + +/** + * error_extra_type for string error values + */ +const struct error_extra_type error_extra_string; + +/** + * Description of an error code + */ +struct error_type { + /** The actual code */ + err_t code; + + /** The short name of the error */ + const char *name; + + /** The type of any contained extra info */ + const struct error_extra_type *extra_type; + + /** Any linked error_code table for looking up sub-errors */ + const struct error_list *sublist; +}; + +/** + * Helper macros to define error_type's + */ +#define ERROR_TYPE(code, name) \ + { (code), (name), NULL, NULL } + +#define ERROR_TYPE_ERRNO(code, name) \ + { (code), (name), &error_extra_errno, NULL } + +#define ERROR_TYPE_STRING(code, name) \ + { (code), (name), &error_extra_string, NULL } + +#define ERROR_TYPE_CUSTOM(code, name, type) \ + { (code), (name), (type), NULL } + +#define ERROR_TYPE_SUB(code, name, sub) \ + { (code), (name), NULL, (sub) } + +#define ERROR_TYPE_END \ + { 0, NULL, NULL, NULL } + +/** + * List of error types + */ +struct error_list { + /** Name of sublib */ + const char *name; + + /** The list */ + struct error_type list[]; +}; + +/** + * Helper macro to define an error_list + */ +#define ERROR_LIST(name, ...) \ + { (name), __VA_ARGS__, ERROR_TYPE_END } + +/** + * Maximum number of nesting levels supported for errors + */ +#define ERROR_DEPTH_MAX 8 + +/** + * Information about an actual error + */ +typedef struct error_state { + /** The stack of error codes */ + struct error_item { + /** This level's error code */ + err_t code; + + /** Optional table for interpreting error_code, can be used instead of error_code::next */ + const struct error_list *list; + + } stack[ERROR_DEPTH_MAX]; + + /** Current top of stack for accumulating errors, NULL means empty stack */ + struct error_info_level *cur; + + /** Type info for external error info */ + const struct error_extra_type *extra_type; + + /** Value of external error info */ + union error_extra extra_value; + +} error_t; + +/** + * Look up the error_type for the given table and scalar code. + */ +const struct error_type* error_lookup_code (const struct error_list *list, err_t code); + +/** + * Look up the error_type for for the given root table and error_info. + */ +const struct error_type* error_lookup (const error_t *err); + +/** + * Return the error name for the lowest level error in the given state's stack. + */ +const char* error_name (const error_t *err); + +/** + * Maximum length of messages returned by error_msg + */ +#define ERROR_MSG_MAX 1024 + +/** + * Return a pointer to a statically allocated string describing the given full error + */ +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); + + +/** + * Reset the given error state to an empty state + */ +static inline void error_reset (error_t *err) +{ + memset(err, 0, sizeof(*err)); +} + +/** + * Evaluates to the current top of the error stack, + */ +static struct error_item* error_top (const error_t *err) +{ + return err->cur ? err->cur : err->stack; +} + +/** + * Evaluate to the most recent error code pushed, or SUCCESS if none + */ +static inline err_t error_code (error_t *err) +{ + return err->cur ? err->cur->code : SUCCESS; +} + +/** + * Reset the error state, push the given initial err. + */ +void error_set (error_t *err, const struct error_list *list, err_t code); + +/** + * Reset, push err, and set extra. + */ +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); + +/** + * Push an additional level of error info onto the error state's stack. If it doesn't fit, XXX: it is ignored. + */ +void error_push (error_t *err, const struct error_list *list, err_t code); + +/** + * Copy value of error + */ +void error_copy (error_t *dst, const error_t *src); + +/** Evalutes to the lvalue representing the error code at the top of the stack */ +#define ERROR_CODE(err_state) \ + (error_top(err_state)->code) + +/** Push a new error code onto the given state */ +#define PUSH_ERROR(err_state, err_list, err_code) ({ \ + error_push(err_state, err_list, err_code); \ + err_code; }) + +/** Set the error to the given value */ +#define SET_ERROR(err_state, err_list, err_code) ({ \ + error_set(err_state, err_list, err_code); \ + err_code; }) + +/* Helper macro to use error_set_extra */ +#define _ERROR_SET_EXTRA(err_state, err_list, err_code, err_extra_type, _err_extra_field_, err_extra_value) \ + error_set_extra(err_state, err_list, err_code, err_extra_type, ((union error_extra) { ._err_extra_field_ = (err_extra_value)})) + +/** Set the error with extra info as integer */ +#define SET_ERROR_EXTRA(err_state, err_list, err_code, err_extra_type, err_extra_int) ({ \ + _ERROR_SET_EXTRA(err_state, err_list, err_code, err_extra_type, int_, err_extra); \ + err_code; }) + +/** Set the error with extra info as the libc errno */ +#define SET_ERROR_ERRNO(err_state, err_list, err_code) ({ \ + _ERROR_SET_EXTRA(err_state, err_list, err_code, &error_extra_errno, int_, errno); \ + err_code; }) + +/** Set the error with extra info as the given static string */ +#define SET_ERROR_STR(err_state, err_list, err_code, err_str) ({ \ + _ERROR_SET_EXTRA(err_state, err_list, err_code, &error_extra_string, str, err_str); \ + err_code; }) + +/** Set the error with the contents of the given error */ +#define SET_ERROR_COPY(err_state_dst, err_state_src) ({ \ + error_code(err_state_dst); }) + +/** Same as above, but also do a 'goto error' jump */ +#define JUMP_PUSH_ERROR(...) do { PUSH_ERROR(__VA_ARGS__); goto error; } while (0) +#define JUMP_SET_ERROR(...) do { SET_ERROR(__VA_ARGS__); goto error; } while (0) +#define JUMP_SET_ERROR_EXTRA(...) do { SET_ERROR_EXTRA(__VA_ARGS__); goto error; } while (0) +#define JUMP_SET_ERROR_ERRNO(...) do { SET_ERROR_ERRNO(__VA_ARGS__); goto error; } while (0) +#define JUMP_SET_ERROR_STR(...) do { SET_ERROR_STR(__VA_ARGS__); goto error; } while (0) +#define JUMP_SET_ERROR_INFO(...) do { SET_ERROR_INFO(__VA_ARGS__); goto error; } while (0) + + +/** + * Abort execution of process with error message + */ +void _error_abort (const char *file, const char *line, const char *func, const char *fmt, ...); +#define error_abort(...) _error_abort(__FILE__, __LINE__, __func__, __VA_ARGS__); + +/** + * Used to mark unexpcted conditions for switch-default. The given val must be an integer, as passed to switch + */ +#define NOT_REACHED(val) error_abort("%s = %#x", #val, (int) (val)); + +#endif /* LIBQMSK_ERROR_H */ diff -r 85863b89e38b -r a10ba529ae39 src/lib/str.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib/str.c Wed May 27 23:07:00 2009 +0300 @@ -0,0 +1,320 @@ +#include "str.h" + +#include +#include +#include +#include + +#include + +const struct error_list str_errors = ERROR_LIST("str", + ERROR_TYPE( ERR_STR_FMT_TAG, "invalid parameter tag syntax" ), + ERROR_TYPE( ERR_STR_FMT_NAME_LEN, "invalid parameter name length" ), + ERROR_TYPE( ERR_STR_FMT_NAME, "invalid/unknown parameter name" ), + ERROR_TYPE( ERR_STR_FMT_FLAGS_LEN, "invalid paramter flags length" ), + ERROR_TYPE( ERR_STR_FMT_FLAG, "invalid paramter flag" ), + ERROR_TYPE( ERR_STR_FMT_BUF_LEN, "output buffer ran out" ) +); + +size_t str_append_num (char *buf, size_t buf_size, const char *str, ssize_t len) +{ + size_t str_len; + + // copy the bytes + // len will either be >0 or <0 + for (str_len = 0; *str && buf_size > 1 && len != 0; str_len++, buf_size--, len--) + *buf++ = *str++; + + // count the rest + for (; *str && len != 0; str++, len--) + str_len++; + + if (buf_size) + // NUL-terminate + *buf = '\0'; + + // ok + return str_len; + +} + +size_t str_append (char *buf, size_t buf_size, const char *str) +{ + return str_append_num(buf, buf_size, str, -1); +} + +size_t str_append_char (char *buf, size_t buf_size, char c) +{ + if (buf_size > 1) + *buf++ = c; + + if (buf_size > 0) + *buf = '\0'; + + return 1; +} + +size_t str_append_fmt_va (char *buf, size_t buf_size, const char *fmt, va_list vargs) +{ + int ret; + + // do the formatting + ret = vsnprintf(buf, buf_size, fmt, vargs); + + // XXX: not all vsnprintf implementations do this + assert(ret >= 0); + + // ok + return ret; +} + +size_t str_append_fmt (char *buf, size_t buf_size, const char *fmt, ...) +{ + va_list vargs; + int ret; + + va_start(vargs, fmt); + ret = str_append_fmt_va(buf, buf_size, fmt, vargs); + va_end(vargs); + + return ret; +} + +static const struct str_quote_item { + /** The char value to quote */ + char c; + + /** The quoted value */ + const char *out; +} _quote_table[] = { + { '\'', "\\'" }, + { '\\', "\\\\" }, + { '\0', "\\0" }, + { '\r', "\\r" }, + { '\n', "\\n" }, + { 0, NULL } +}; + +size_t str_quote_char (char *buf, size_t buf_size, char c) +{ + const struct str_quote_item *quote_item = _quote_table; + + // special quote? + for (quote_item = _quote_table; quote_item->c || quote_item->out; quote_item++) { + if (quote_item->c == c) + return str_append(buf, buf_size, quote_item->out); + } + + // output directly? + if (isprint(c)) + return str_append_char(buf, buf_size, c); + + else + return str_append_fmt(buf, buf_size, "\\x%02x", c); +} + +size_t str_advance (size_t *data_size, size_t *buf_size, size_t len) +{ + if (data_size) + *data_size += len; + + if (len > *buf_size) + *buf_size = 0; + + else + *buf_size -= len; + + return len; +} + +/** + * Number of bytes reserved by str_quote for the trailing overflow stuff + */ +#define STR_QUOTE_RESERVED 5 + +size_t str_quote (char *buf, size_t buf_size, const char *str, ssize_t str_len) +{ + size_t data_size = 0; + + // NULL? + if (str == NULL) { + return str_append(buf, buf_size, "NULL"); + + } else { + // calc length? + if (str_len < 0) + str_len = strlen(str); + + // quote + buf += str_advance(&data_size, &buf_size, str_append_char(buf, buf_size, '\'')); + + // dump each char + while (str_len--) { + if (buf_size > STR_QUOTE_RESERVED) { + size_t char_size; + + // output and count the chars + char_size = str_quote_char(buf, buf_size, *str++); + + // if there's still enough room left, commit this and continue + if (buf_size > char_size && buf_size - char_size >= STR_QUOTE_RESERVED) + buf += str_advance(&data_size, &buf_size, char_size); + + else + // keep the buf pointer intact, we'll overwrite it now + data_size += char_size; + + } else { + // continue counting the chars, but don't output anything anymore + data_size += str_quote_char(NULL, 0, *str++); + } + } + + // end quote + buf += str_advance(&data_size, &buf_size, str_append_char(buf, buf_size, '\'')); + } + + // overflow -> mark + if (buf_size < STR_QUOTE_RESERVED) + buf += str_advance(NULL, &buf_size, str_append(buf, buf_size, "...")); + + // the total outputted chars + return data_size; +} + +/** + * Output the data for a single parameter + */ +static err_t str_format_param (char **buf, size_t *buf_size, const char *name, const char *flags, str_format_cb func, void *arg) +{ + const char *value; + ssize_t value_len = -1; + char flag; + bool use_quote = false; + err_t err; + + // look it up + if ((err = func(name, &value, &value_len, arg))) + return err; + + // not found? + if (!value) + return ERR_STR_FMT_NAME; + + // parse flags + while ((flag = *flags++)) { + switch (flag) { + case 'r': + // quote + use_quote = true; + + break; + + default: + // unknown flag + return ERR_STR_FMT_FLAG; + + } + } + + // output value + if (use_quote) + *buf += str_advance(NULL, buf_size, str_quote(*buf, *buf_size, value, value_len)); + + else + *buf += str_advance(NULL, buf_size, str_append_num(*buf, *buf_size, value, value_len)); + + // ok + return SUCCESS; +} + +err_t str_format (char *buf, size_t buf_size, const char *format, str_format_cb func, void *arg, error_t *err) +{ + char name_buf[STR_FORMAT_PARAM_MAX + 1], *name_ptr; + size_t name_size; + char flags_buf[STR_FORMAT_FLAGS_MAX + 1], *flags_ptr; + size_t flags_size; + bool in_param = false, in_param_flags = false; + + // iterate over the format string + do { + // check buffer state + if (!buf_size) + return SET_ERROR(err, &str_errors, ERR_STR_FMT_BUF_LEN); + + // inspect this char + switch (*format) { + case '{': + // syntax + if (in_param) + return SET_ERROR(err, &str_errors, ERR_STR_FMT_TAG); + + // init state + in_param = true; + name_ptr = name_buf; + name_size = sizeof(name_buf); + flags_ptr = flags_buf; + flags_size = sizeof(flags_buf); + + *name_ptr = *flags_ptr = '\0'; + + break; + + case '}': + // syntax + if (!in_param) + return SET_ERROR(err, &str_errors, ERR_STR_FMT_TAG); + + // reset state + in_param = false; + in_param_flags = false; + + // output token + if (str_format_param(&buf, &buf_size, name_buf, flags_buf, func, arg, err)) + return error_code(err); + + break; + + case ':': + if (in_param) { + // set state + in_param_flags = true; + + break; + } + + /* Fallthrough */ + + default: + if (in_param && in_param_flags ) { + // add to param flags + flags_ptr += str_advance(NULL, &flags_size, str_append_char(flags_ptr, flags_size, *format)); + + if (!flags_size) + return SET_ERROR(err, &str_errors, ERR_STR_FMT_FLAGS_LEN); + + } else if (in_param) { + // add to param name + name_ptr += str_advance(NULL, &name_size, str_append_char(name_ptr, name_size, *format)); + + if (!name_size) + return SET_ERROR(err, &str_errors, ERR_STR_FMT_NAME_LEN); + + } else { + // add to output + buf += str_advance(NULL, &buf_size, str_append_char(buf, buf_size, *format)); + + } + + break; + } + } while (*format++); + + // syntax + if (in_param) + return SET_ERROR(err, &str_errors, ERR_STR_FMT_TAG); + + // ok + return SUCCESS; +} + diff -r 85863b89e38b -r a10ba529ae39 src/lib/str.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib/str.h Wed May 27 23:07:00 2009 +0300 @@ -0,0 +1,126 @@ +#ifndef STR_H +#define STR_H + +/** + * @file + * + * Miscellaneous string utility functions + */ +#include +#include +#include "error.h" + +/** + * Error codes + */ +enum str_error_code { + ERR_STR_NONE, + ERR_STR_FMT_TAG, ///< invalid parameter tag syntax + ERR_STR_FMT_NAME_LEN, ///< invalid parameter name length + ERR_STR_FMT_NAME, ///< invalid/unknown parameter name + ERR_STR_FMT_FLAGS_LEN, ///< invalid paramter flags length + ERR_STR_FMT_FLAG, ///< invalid paramter flag + ERR_STR_FMT_BUF_LEN, ///< output buffer ran out +}; + +/** + * Error list + */ +const struct error_list str_errors; + +/** + * Writes the given string into the given buffer, reading at most len bytes (or up to NUL if -1), and writing at most + * buf_size bytes, including the terminating NUL. + * + * Returns the number of input bytes that would have been written, not including the terminating NUL. + */ +size_t str_append_num (char *buf, size_t buf_size, const char *str, ssize_t len); + +/** + * Like str_append_num with len = -1. + * + * @see str_append_num() + */ +size_t str_append (char *buf, size_t buf_size, const char *str); + +/** + * Like str_append, but only a single char. + * + * @see str_append() + */ +size_t str_append_char (char *buf, size_t buf_size, char c); + +/** + * Like str_append, but using formatted input instead. + * + * @see str_append() + */ +size_t str_append_fmt (char *buf, size_t buf_size, const char *fmt, ...); + +/** + * Like str_append_fmt, but using a vargs list instead. + * + * @see str_append_fmt() + */ +size_t str_append_fmt_va (char *buf, size_t buf_size, const char *fmt, va_list vargs); + +/** + * Use str_append* to write out the quoted char value to the given buffer. + */ +size_t str_quote_char (char *buf, size_t buf_size, char c); + +/** + * Update data_size to add len (if given), then update buf_size to remove to min(buf_size, len), and return len. + * + * Intended to be used like: + * \code + * buf += str_advance(&data_size, &buf_size, str_append_fmt(buf, buf_size, "...", ...)); + * \endcode + */ +size_t str_advance (size_t *data_size, size_t *buf_size, size_t len); + +/** + * Copy the given \a str into \buf, surrounding it with quotes and escaping any data inside. + * + * At most \a str_len bytes of input will be read, unless given as -1, whereupon str will be read up to the first \0 byte. + * + * At most \a buf_size bytes of output will be written, if the output string was truncated, it will end in '...', and a + * value larger than \a buf_size will be returned. + * + * As a special case, if \a str is NULL, only the string "NULL" will be output. + * + * @param buf the buffer to write the output to + * @param buf_size the size of the given output buffer + * @param str the input string + * @param str_len number of bytes of input to process, or -1 to use strlen() + * @return the total number of bytes that would have been written out, may be more than buf_size + */ +size_t str_quote (char *buf, size_t buf_size, const char *str, ssize_t str_len); + +/** + * Callback function used by str_format to look up a value for a parameter. + * + * @param name the name of the paramter in the format string + * @param value returned pointer to param value + * @param value_len returned param value length, or -1 for strlen + * @param arg the context arg given to str_format + * @return the parameter value, or NULL to error out + */ +typedef err_t (*str_format_cb) (const char *name, const char **value, ssize_t *value_len, void *arg); + +/** + * Maximum length of a parameter name + */ +#define STR_FORMAT_PARAM_MAX 32 + +/** + * Maximum length of a parameter flags section + */ +#define STR_FORMAT_FLAGS_MAX 8 + +/** + * Format an output string based on the given template, filling in parameter values using a callback function. + */ +err_t str_format (char *buf, size_t buf_size, const char *format, str_format_cb func, void *arg, error_t *err); + +#endif diff -r 85863b89e38b -r a10ba529ae39 src/log.c --- a/src/log.c Sat May 23 00:33:23 2009 +0300 +++ b/src/log.c Wed May 27 23:07:00 2009 +0300 @@ -72,7 +72,7 @@ _log_output_ctx.arg = arg; } -void log_output_tag (enum log_level level, const char *tag, const char *func, const char *user_fmt, va_list user_fmtargs, const char *log_fmt, ...) +void log_output_tag_va (enum log_level level, const char *tag, const char *func, const char *user_fmt, va_list user_fmtargs, const char *log_fmt, va_list log_fmtargs) { char buf[LOG_MSG_MAX], *buf_ptr = buf; size_t buf_size = sizeof(buf); @@ -89,16 +89,21 @@ buf_ptr += str_advance(NULL, &buf_size, str_append_fmt_va(buf_ptr, buf_size, user_fmt, user_fmtargs)); // output the suffix - if (log_fmt) { - va_list vargs; - - va_start(vargs, log_fmt); - buf_ptr += str_advance(NULL, &buf_size, str_append_fmt_va(buf_ptr, buf_size, log_fmt, vargs)); - va_end(vargs); - } + if (log_fmt) + buf_ptr += str_advance(NULL, &buf_size, str_append_fmt_va(buf_ptr, buf_size, log_fmt, log_fmtargs)); // send it to the output func _log_output_ctx.func(buf, _log_output_ctx.arg); + +} + +void log_output_tag (enum log_level level, const char *tag, const char *func, const char *user_fmt, va_list user_fmtargs, const char *log_fmt, ...) +{ + va_list vargs; + + va_start(vargs, log_fmt); + log_output_tag_va(level, tag, func, user_fmt, user_fmtargs, log_fmt, vargs); + va_end(vargs); } void _log_msg (enum log_level level, const char *func, const char *format, ...) @@ -111,6 +116,11 @@ va_end(vargs); } +void _log_msg_va2 (enum log_level level, const char *func, const char *fmt1, va_list fmtargs1, const char *fmt2, va_list fmtargs2) +{ + log_output_tag_va(level, log_level_name(level), func, fmt1, fmtargs1, fmt2, fmtargs2); +} + void _log_err (enum log_level level, err_t err, const char *func, const char *format, ...) { va_list vargs; diff -r 85863b89e38b -r a10ba529ae39 src/log.h --- a/src/log.h Sat May 23 00:33:23 2009 +0300 +++ b/src/log.h Wed May 27 23:07:00 2009 +0300 @@ -63,6 +63,11 @@ __attribute__ ((format (printf, 6, 7))); /** + * va_list version of log_output_tag + */ +void log_output_tag_va (enum log_level level, const char *tag, const char *func, const char *user_fmt, va_list user_fmtargs, const char *log_fmt, va_list log_fmtargs); + +/** * Log a message with the given level */ #define log_msg(level, ...) _log_msg(level, __func__, __VA_ARGS__) @@ -70,6 +75,13 @@ __attribute__ ((format (printf, 3, 4))); /** + * Log a message with the given level with the given format and varargs + */ +#define log_msg_va2(level, fmt1, vargs1, fmt2, vargs2) _log_msg_va(level, __func__, fmt1, vargs1, fmt2, vargs2) +void _log_msg_va2 (enum log_level level, const char *func, const char *fmt1, va_list fmtargs1, const char *fmt2, va_list fmtargs2); + + +/** * Shorthand for log_msg */ #define log_debug(...) log_msg(LOG_DEBUG, __VA_ARGS__) diff -r 85863b89e38b -r a10ba529ae39 src/str.c --- a/src/str.c Sat May 23 00:33:23 2009 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,311 +0,0 @@ -#include "str.h" - -#include -#include -#include -#include - -#include - -size_t str_append_num (char *buf, size_t buf_size, const char *str, ssize_t len) -{ - size_t str_len; - - // copy the bytes - // len will either be >0 or <0 - for (str_len = 0; *str && buf_size > 1 && len != 0; str_len++, buf_size--, len--) - *buf++ = *str++; - - // count the rest - for (; *str && len != 0; str++, len--) - str_len++; - - if (buf_size) - // NUL-terminate - *buf = '\0'; - - // ok - return str_len; - -} - -size_t str_append (char *buf, size_t buf_size, const char *str) -{ - return str_append_num(buf, buf_size, str, -1); -} - -size_t str_append_char (char *buf, size_t buf_size, char c) -{ - if (buf_size > 1) - *buf++ = c; - - if (buf_size > 0) - *buf = '\0'; - - return 1; -} - -size_t str_append_fmt_va (char *buf, size_t buf_size, const char *fmt, va_list vargs) -{ - int ret; - - // do the formatting - ret = vsnprintf(buf, buf_size, fmt, vargs); - - // XXX: not all vsnprintf implementations do this - assert(ret >= 0); - - // ok - return ret; -} - -size_t str_append_fmt (char *buf, size_t buf_size, const char *fmt, ...) -{ - va_list vargs; - int ret; - - va_start(vargs, fmt); - ret = str_append_fmt_va(buf, buf_size, fmt, vargs); - va_end(vargs); - - return ret; -} - -static const struct str_quote_item { - /** The char value to quote */ - char c; - - /** The quoted value */ - const char *out; -} _quote_table[] = { - { '\'', "\\'" }, - { '\\', "\\\\" }, - { '\0', "\\0" }, - { '\r', "\\r" }, - { '\n', "\\n" }, - { 0, NULL } -}; - -size_t str_quote_char (char *buf, size_t buf_size, char c) -{ - const struct str_quote_item *quote_item = _quote_table; - - // special quote? - for (quote_item = _quote_table; quote_item->c || quote_item->out; quote_item++) { - if (quote_item->c == c) - return str_append(buf, buf_size, quote_item->out); - } - - // output directly? - if (isprint(c)) - return str_append_char(buf, buf_size, c); - - else - return str_append_fmt(buf, buf_size, "\\x%02x", c); -} - -size_t str_advance (size_t *data_size, size_t *buf_size, size_t len) -{ - if (data_size) - *data_size += len; - - if (len > *buf_size) - *buf_size = 0; - - else - *buf_size -= len; - - return len; -} - -/** - * Number of bytes reserved by str_quote for the trailing overflow stuff - */ -#define STR_QUOTE_RESERVED 5 - -size_t str_quote (char *buf, size_t buf_size, const char *str, ssize_t str_len) -{ - size_t data_size = 0; - - // NULL? - if (str == NULL) { - return str_append(buf, buf_size, "NULL"); - - } else { - // calc length? - if (str_len < 0) - str_len = strlen(str); - - // quote - buf += str_advance(&data_size, &buf_size, str_append_char(buf, buf_size, '\'')); - - // dump each char - while (str_len--) { - if (buf_size > STR_QUOTE_RESERVED) { - size_t char_size; - - // output and count the chars - char_size = str_quote_char(buf, buf_size, *str++); - - // if there's still enough room left, commit this and continue - if (buf_size > char_size && buf_size - char_size >= STR_QUOTE_RESERVED) - buf += str_advance(&data_size, &buf_size, char_size); - - else - // keep the buf pointer intact, we'll overwrite it now - data_size += char_size; - - } else { - // continue counting the chars, but don't output anything anymore - data_size += str_quote_char(NULL, 0, *str++); - } - } - - // end quote - buf += str_advance(&data_size, &buf_size, str_append_char(buf, buf_size, '\'')); - } - - // overflow -> mark - if (buf_size < STR_QUOTE_RESERVED) - buf += str_advance(NULL, &buf_size, str_append(buf, buf_size, "...")); - - // the total outputted chars - return data_size; -} - -/** - * Output the data for a single parameter - */ -static err_t str_format_param (char **buf, size_t *buf_size, const char *name, const char *flags, str_format_cb func, void *arg) -{ - const char *value; - ssize_t value_len = -1; - char flag; - bool use_quote = false; - err_t err; - - // look it up - if ((err = func(name, &value, &value_len, arg))) - return err; - - // not found? - if (!value) - return ERR_STR_FMT_NAME; - - // parse flags - while ((flag = *flags++)) { - switch (flag) { - case 'r': - // quote - use_quote = true; - - break; - - default: - // unknown flag - return ERR_STR_FMT_FLAG; - - } - } - - // output value - if (use_quote) - *buf += str_advance(NULL, buf_size, str_quote(*buf, *buf_size, value, value_len)); - - else - *buf += str_advance(NULL, buf_size, str_append_num(*buf, *buf_size, value, value_len)); - - // ok - return SUCCESS; -} - -err_t str_format (char *buf, size_t buf_size, const char *format, str_format_cb func, void *arg) -{ - char name_buf[STR_FORMAT_PARAM_MAX + 1], *name_ptr; - size_t name_size; - char flags_buf[STR_FORMAT_FLAGS_MAX + 1], *flags_ptr; - size_t flags_size; - bool in_param = false, in_param_flags = false; - err_t err; - - // iterate over the format string - do { - // check buffer state - if (!buf_size) - return ERR_STR_FMT_BUF_LEN; - - // inspect this char - switch (*format) { - case '{': - // syntax - if (in_param) - return ERR_STR_FMT_TAG; - - // init state - in_param = true; - name_ptr = name_buf; - name_size = sizeof(name_buf); - flags_ptr = flags_buf; - flags_size = sizeof(flags_buf); - - *name_ptr = *flags_ptr = '\0'; - - break; - - case '}': - // syntax - if (!in_param) - return ERR_STR_FMT_TAG; - - // reset state - in_param = false; - in_param_flags = false; - - // output token - if ((err = str_format_param(&buf, &buf_size, name_buf, flags_buf, func, arg))) - return err; - - break; - - case ':': - if (in_param) { - // set state - in_param_flags = true; - - break; - } - - /* Fallthrough */ - - default: - if (in_param && in_param_flags ) { - // add to param flags - flags_ptr += str_advance(NULL, &flags_size, str_append_char(flags_ptr, flags_size, *format)); - - if (!flags_size) - return ERR_STR_FMT_FLAGS_LEN; - - } else if (in_param) { - // add to param name - name_ptr += str_advance(NULL, &name_size, str_append_char(name_ptr, name_size, *format)); - - if (!name_size) - return ERR_STR_FMT_NAME_LEN; - - } else { - // add to output - buf += str_advance(NULL, &buf_size, str_append_char(buf, buf_size, *format)); - - } - - break; - } - } while (*format++); - - // syntax - if (in_param) - return ERR_STR_FMT_TAG; - - // ok - return SUCCESS; -} diff -r 85863b89e38b -r a10ba529ae39 src/str.h --- a/src/str.h Sat May 23 00:33:23 2009 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,121 +0,0 @@ -#ifndef STR_H -#define STR_H - -/** - * @file - * - * Miscellaneous string utility functions - */ -#include -#include -#include "error.h" - -/** - * Error codes - */ -enum str_error_code { - _ERR_STR_BEGIN = _ERR_STR, - ERR_STR_FMT_TAG, ///< invalid parameter tag syntax - ERR_STR_FMT_NAME_LEN, ///< invalid parameter name length - ERR_STR_FMT_NAME, ///< invalid/unknown parameter name - ERR_STR_FMT_FLAGS_LEN, ///< invalid paramter flags length - ERR_STR_FMT_FLAG, ///< invalid paramter flag - ERR_STR_FMT_BUF_LEN, ///< output buffer ran out -}; - -/** - * Writes the given string into the given buffer, reading at most len bytes (or up to NUL if -1), and writing at most - * buf_size bytes, including the terminating NUL. - * - * Returns the number of input bytes that would have been written, not including the terminating NUL. - */ -size_t str_append_num (char *buf, size_t buf_size, const char *str, ssize_t len); - -/** - * Like str_append_num with len = -1. - * - * @see str_append_num() - */ -size_t str_append (char *buf, size_t buf_size, const char *str); - -/** - * Like str_append, but only a single char. - * - * @see str_append() - */ -size_t str_append_char (char *buf, size_t buf_size, char c); - -/** - * Like str_append, but using formatted input instead. - * - * @see str_append() - */ -size_t str_append_fmt (char *buf, size_t buf_size, const char *fmt, ...); - -/** - * Like str_append_fmt, but using a vargs list instead. - * - * @see str_append_fmt() - */ -size_t str_append_fmt_va (char *buf, size_t buf_size, const char *fmt, va_list vargs); - -/** - * Use str_append* to write out the quoted char value to the given buffer. - */ -size_t str_quote_char (char *buf, size_t buf_size, char c); - -/** - * Update data_size to add len (if given), then update buf_size to remove to min(buf_size, len), and return len. - * - * Intended to be used like: - * \code - * buf += str_advance(&data_size, &buf_size, str_append_fmt(buf, buf_size, "...", ...)); - * \endcode - */ -size_t str_advance (size_t *data_size, size_t *buf_size, size_t len); - -/** - * Copy the given \a str into \buf, surrounding it with quotes and escaping any data inside. - * - * At most \a str_len bytes of input will be read, unless given as -1, whereupon str will be read up to the first \0 byte. - * - * At most \a buf_size bytes of output will be written, if the output string was truncated, it will end in '...', and a - * value larger than \a buf_size will be returned. - * - * As a special case, if \a str is NULL, only the string "NULL" will be output. - * - * @param buf the buffer to write the output to - * @param buf_size the size of the given output buffer - * @param str the input string - * @param str_len number of bytes of input to process, or -1 to use strlen() - * @return the total number of bytes that would have been written out, may be more than buf_size - */ -size_t str_quote (char *buf, size_t buf_size, const char *str, ssize_t str_len); - -/** - * Callback function used by str_format to look up a value for a parameter. - * - * @param name the name of the paramter in the format string - * @param value returned pointer to param value - * @param value_len returned param value length, or -1 for strlen - * @param arg the context arg given to str_format - * @return the parameter value, or NULL to error out - */ -typedef err_t (*str_format_cb) (const char *name, const char **value, ssize_t *value_len, void *arg); - -/** - * Maximum length of a parameter name - */ -#define STR_FORMAT_PARAM_MAX 32 - -/** - * Maximum length of a parameter flags section - */ -#define STR_FORMAT_FLAGS_MAX 8 - -/** - * Format an output string based on the given template, filling in parameter values using a callback function. - */ -err_t str_format (char *buf, size_t buf_size, const char *format, str_format_cb func, void *arg); - -#endif