initial error code new-lib-errors
authorTero Marttila <terom@fixme.fi>
Wed, 27 May 2009 23:07:00 +0300
branchnew-lib-errors
changeset 216 a10ba529ae39
parent 215 85863b89e38b
child 217 7728d6ec3abf
initial error code
src/error.c
src/error.h
src/lib/error.c
src/lib/error.h
src/lib/str.c
src/lib/str.h
src/log.c
src/log.h
src/str.c
src/str.h
--- 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 <string.h>
-#include <stdio.h>
-
-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;
-    }
-}
-
--- 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 <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
--- /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);
+}
+
--- /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 */
--- /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 <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdbool.h>
+
+#include <assert.h>
+
+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;
+}
+
--- /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 <sys/types.h>
+#include <stdarg.h>
+#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
--- 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;
--- 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__)
--- 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 <ctype.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdbool.h>
-
-#include <assert.h>
-
-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;
-}
--- 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 <sys/types.h>
-#include <stdarg.h>
-#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