some of spbot and lib compiles new-lib-errors
authorTero Marttila <terom@fixme.fi>
Thu, 28 May 2009 00:35:02 +0300
branchnew-lib-errors
changeset 218 5229a5d098b2
parent 217 7728d6ec3abf
child 219 cefec18b8268
some of spbot and lib compiles
src/console.c
src/console.h
src/lib/console.c
src/lib/console.h
src/lib/error.c
src/lib/error.h
src/lib/errors.c
src/lib/errors.h
src/lib/log.c
src/lib/str.c
src/lib/str.h
src/lua_config.c
src/lua_config.h
src/lua_console.c
src/lua_console.h
src/lua_thread.c
src/lua_thread.h
src/nexus_lua.c
src/nexus_lua.h
src/spbot/config.h
src/spbot/lua_config.c
src/spbot/lua_config.h
src/spbot/lua_console.c
src/spbot/lua_console.h
src/spbot/lua_thread.c
src/spbot/lua_thread.h
src/spbot/module.c
src/spbot/module.h
src/spbot/nexus.c
src/spbot/nexus_lua.c
src/spbot/nexus_lua.h
src/spbot/signals.c
src/spbot/signals.h
--- a/src/console.c	Wed May 27 23:57:48 2009 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,277 +0,0 @@
-#include "console.h"
-#include "log.h"
-
-#include <stdlib.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <readline/readline.h>
-#include <readline/history.h>
-
-#include <signal.h>
-#include <assert.h>
-
-struct console {
-    /** Configuration */
-    struct console_config config;
-
-    /** Input event */
-    struct event *ev;
-
-    /** Callback functions */
-    const struct console_callbacks *callbacks;
-
-    /** Callback context argument */
-    void *cb_arg;
-
-    /** Old SIGINT handler */
-    struct sigaction old_sigint;
-
-    /** Already initialized? */
-    bool initialized;
-
-    /** Set as log output function? */
-    bool log_output;
-
-    /** Prompt displayed? */
-    bool have_prompt;
-
-    /** Waiting for line to be processed */
-    bool waiting;
-};
-
-/** The global console state */
-static struct console _console;
-
-/**
- * Our stdin input handler
- */
-static void console_input (int fd, short what, void *arg)
-{
-    struct console *console = arg;
-
-    (void) fd;
-    (void) what;
-
-    if (console->waiting)
-        // can't feed readline input while it's disabled
-        return log_warn("discrding input while waiting");
-
-    else
-        // tell readline to process it
-        rl_callback_read_char();
-}
-
-/**
- * Our readline line handler
- */
-static void console_line (char *line)
-{
-    struct console *console = &_console;
-    enum console_line_status status = CONSOLE_CONTINUE;
-    
-    // special-case EOF
-    if (!line) {
-        // prettify
-        rl_crlf();
-        rl_on_new_line();
-
-        if (console->callbacks->on_eof)
-            console->callbacks->on_eof(console->cb_arg);
-        
-        return;
-    }
-
-    // update state for console_print during processing
-    console->have_prompt = false;
-
-    // invoke the console callback
-    if (console->callbacks && console->callbacks->on_line)
-        status = console->callbacks->on_line(line, console->cb_arg);
-    
-    // add to history mechanism
-    add_history(line);
-
-    // release the line
-    free(line);
-    
-    switch (status) {
-        case CONSOLE_CONTINUE:
-            // the prompt will be displayed again
-            console->have_prompt = true;
-
-            break;
-
-        case CONSOLE_WAIT:
-            // deactivate our read event
-            if (event_del(console->ev))
-                log_warn("unable to deactivate console read event");
-
-            // remove the readline stuff to stop it from displaying the prompt
-            rl_callback_handler_remove();
-
-            // ignore input
-            console->waiting = true;
-
-            break;
-    }
-}
-static void on_sigint (int sig)
-{
-    struct console *console = &_console;
-
-    (void) sig;
-
-    if (console->waiting) {
-        // notify user
-        if (console->callbacks->on_interrupt)
-            console->callbacks->on_interrupt(console->cb_arg);
-
-    } else {
-        // interrupt the input line
-        // XXX: is this the right function to call?
-        rl_free_line_state();
-
-        // redisplay on new line
-        rl_crlf();
-        rl_callback_handler_install(console->config.prompt, console_line);
-    }
-}
-
-err_t console_init (struct console **console_ptr, struct event_base *ev_base, const struct console_config *config,
-        const struct console_callbacks *callbacks, void *cb_arg, error_t *err)
-{
-    struct console *console = &_console;
-
-    // check it's not already initialized
-    assert(!console->initialized);
-
-    // store
-    console->config = *config;
-
-    // store callbacks?
-    if (callbacks)
-        console_set_callbacks(console, callbacks, cb_arg);
-
-    // setup the input event
-    if ((console->ev = event_new(ev_base, STDIN_FILENO, EV_READ | EV_PERSIST, &console_input, console)) == NULL)
-        JUMP_SET_ERROR(err, ERR_EVENT_NEW);
-    
-    // set our SIGINT handler
-    struct sigaction sigact;
-    
-    memset(&sigact, 0, sizeof(sigact));
-    sigact.sa_handler = on_sigint;
-    sigaction(SIGINT, &sigact, &console->old_sigint);
-
-    // setup state for initial readline prompt
-    console->have_prompt = true;
-
-    // setup readline
-    rl_callback_handler_install(config->prompt, console_line);
-    
-    // mark it as initialized
-    console->initialized = true;
-
-    // enable input
-    if (event_add(console->ev, NULL))
-        JUMP_SET_ERROR(err, ERR_EVENT_ADD);
-
-    // ok
-    *console_ptr = console;
-
-    return SUCCESS;
-
-error:
-    console_destroy(console);
-
-    return ERROR_CODE(err);
-}
-
-void console_set_callbacks (struct console *console, const struct console_callbacks *callbacks, void *cb_arg)
-{
-    console->callbacks = callbacks;
-    console->cb_arg = cb_arg;
-}
-
-void console_continue (struct console *console)
-{
-    if (!console->waiting)
-        return;
-
-    // re-enable input
-    console->waiting = false;
-
-    if (event_add(console->ev, NULL))
-        log_fatal("unable to re-enable console read event");
-    
-    // the prompt will be displayed again
-    console->have_prompt = true;
-
-    // re-setup readline with prompt
-    rl_callback_handler_install(console->config.prompt, console_line);
-}
-
-err_t console_print (struct console *console, const char *line)
-{
-    if (console->have_prompt)
-        // don't interrupt current input line
-        rl_crlf();
-
-    // output the line
-    if (printf("%s\n", line) < 0)
-        return _ERR_GENERAL;
-    
-    if (console->have_prompt) {
-        // restore input
-        rl_on_new_line();
-        rl_redisplay();
-    }
-
-    // ok
-    return SUCCESS;
-}
-
-/**
- * Our log_output_func implementation
- */
-static void console_log_output_func (const char *line, void *_console)
-{
-    struct console *console = _console;
-
-    console_print(console, line);
-}
-
-void console_set_log_output (struct console *console)
-{
-    // replace old one
-    log_set_func(console_log_output_func, console);
-
-    // mark
-    console->log_output = true;
-}
-
-void console_destroy (struct console *console)
-{
-    if (console->log_output)
-        // unset ourselves as the log handler
-        log_set_func(NULL, NULL);
-
-    // remove the input event
-    if (console->ev)
-        event_free(console->ev); 
-    
-    console->ev = NULL;
-
-    // de-init rl?
-    if (console->initialized)
-        rl_callback_handler_remove();
-    
-    console->initialized = false;
-
-    // restore signal handler
-    sigaction(SIGINT, &console->old_sigint, NULL);
-
-    // remove stored stuff
-    console->callbacks = console->cb_arg = NULL;
-}
-
--- a/src/console.h	Wed May 27 23:57:48 2009 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,114 +0,0 @@
-#ifndef CONSOLE_H
-#define CONSOLE_H
-
-/**
- * @file
- *
- * An interactive line-based console interface for runtime configuration.
- *
- * This uses the GNU readline library to implement the console, and hence, the console uses global state, and there
- * can only be one console per process - not that it should matter, since the console requires a tty.
- *
- * XXX: the log module interferes with this
- */
-#include "error.h"
-#include <event2/event.h>
-#include <stdbool.h>
-
-/**
- * The console state.
- */
-struct console;
-
-/**
- * Return codes for console_callbacks::on_line
- */
-enum console_line_status {
-    /** 
-     * OK, line was processed, display prompt for next input line 
-     */
-    CONSOLE_CONTINUE,
-
-    /**
-     * Line is still executing, do not prompt for next line .
-     *
-     * Call console_continue() once the line was handled.
-     */
-    CONSOLE_WAIT,
-};
-
-/**
- * Callbacks for event-based actions
- */
-struct console_callbacks {
-    /**
-     * A line was read from the console.
-     *
-     * The return code defines how execution continues.
-     */
-    enum console_line_status (*on_line) (const char *line, void *arg);
-
-    /**
-     * EOF was read on the console.
-     *
-     * Note that for interactive consoles, EOF isn't actually EOF - there might be multiple EOFs...
-     */
-    void (*on_eof) (void *arg);
-
-    /**
-     * An input interrupt (SIGINT) was recieved while in the wait state. This can be used to abort execution and
-     * call console_continue quickly.
-     */
-    void (*on_interrupt) (void *arg);
-};
-
-/**
- * Configuration info for console operation
- */
-struct console_config {
-    /** The prompt to use when displaying lines */
-    const char *prompt;
-};
-
-/**
- * Initialize the console, setting up the TTY and input handler.
- *
- * @param console_ptr returned new console struct
- * @param ev_base the libevent base to use
- * @param config configuration things for the console
- * @param callbacks optional callbacks, can be updated later
- * @param cb_arg option callback argument, can be updated later
- * @param err returned error info
- */
-err_t console_init (struct console **console_ptr, struct event_base *ev_base, const struct console_config *config,
-        const struct console_callbacks *callbacks, void *cb_arg, error_t *err);
-
-/**
- * Replace the current callbacks with the given new ones.
- */
-void console_set_callbacks (struct console *console, const struct console_callbacks *callbacks, void *cb_arg);
-
-/**
- * Continue reading input after a CONSOLE_WAIT return code from console_callbacks::on_line.
- *
- * This does nothing if the console is not currently in the waiting state (i.e. last console_callbacks::on_line
- * returned CONSOLE_WAIT).
- */
-void console_continue (struct console *console);
-
-/**
- * Output a full line (without included newline) on the console, trying not to interfere with the input too much.
- */
-err_t console_print (struct console *console, const char *line);
-
-/**
- * Install this console as the log output handler
- */
-void console_set_log_output (struct console *console);
-
-/**
- * Deinitialize the console, restoring the TTY and releasing resources
- */
-void console_destroy (struct console *console);
-
-#endif /* CONSOLE_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/console.c	Thu May 28 00:35:02 2009 +0300
@@ -0,0 +1,277 @@
+#include "console.h"
+#include "log.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <readline/readline.h>
+#include <readline/history.h>
+
+#include <signal.h>
+#include <assert.h>
+
+struct console {
+    /** Configuration */
+    struct console_config config;
+
+    /** Input event */
+    struct event *ev;
+
+    /** Callback functions */
+    const struct console_callbacks *callbacks;
+
+    /** Callback context argument */
+    void *cb_arg;
+
+    /** Old SIGINT handler */
+    struct sigaction old_sigint;
+
+    /** Already initialized? */
+    bool initialized;
+
+    /** Set as log output function? */
+    bool log_output;
+
+    /** Prompt displayed? */
+    bool have_prompt;
+
+    /** Waiting for line to be processed */
+    bool waiting;
+};
+
+/** The global console state */
+static struct console _console;
+
+/**
+ * Our stdin input handler
+ */
+static void console_input (int fd, short what, void *arg)
+{
+    struct console *console = arg;
+
+    (void) fd;
+    (void) what;
+
+    if (console->waiting)
+        // can't feed readline input while it's disabled
+        return log_warn("discrding input while waiting");
+
+    else
+        // tell readline to process it
+        rl_callback_read_char();
+}
+
+/**
+ * Our readline line handler
+ */
+static void console_line (char *line)
+{
+    struct console *console = &_console;
+    enum console_line_status status = CONSOLE_CONTINUE;
+    
+    // special-case EOF
+    if (!line) {
+        // prettify
+        rl_crlf();
+        rl_on_new_line();
+
+        if (console->callbacks->on_eof)
+            console->callbacks->on_eof(console->cb_arg);
+        
+        return;
+    }
+
+    // update state for console_print during processing
+    console->have_prompt = false;
+
+    // invoke the console callback
+    if (console->callbacks && console->callbacks->on_line)
+        status = console->callbacks->on_line(line, console->cb_arg);
+    
+    // add to history mechanism
+    add_history(line);
+
+    // release the line
+    free(line);
+    
+    switch (status) {
+        case CONSOLE_CONTINUE:
+            // the prompt will be displayed again
+            console->have_prompt = true;
+
+            break;
+
+        case CONSOLE_WAIT:
+            // deactivate our read event
+            if (event_del(console->ev))
+                log_warn("unable to deactivate console read event");
+
+            // remove the readline stuff to stop it from displaying the prompt
+            rl_callback_handler_remove();
+
+            // ignore input
+            console->waiting = true;
+
+            break;
+    }
+}
+static void on_sigint (int sig)
+{
+    struct console *console = &_console;
+
+    (void) sig;
+
+    if (console->waiting) {
+        // notify user
+        if (console->callbacks->on_interrupt)
+            console->callbacks->on_interrupt(console->cb_arg);
+
+    } else {
+        // interrupt the input line
+        // XXX: is this the right function to call?
+        rl_free_line_state();
+
+        // redisplay on new line
+        rl_crlf();
+        rl_callback_handler_install(console->config.prompt, console_line);
+    }
+}
+
+err_t console_init (struct console **console_ptr, struct event_base *ev_base, const struct console_config *config,
+        const struct console_callbacks *callbacks, void *cb_arg, error_t *err)
+{
+    struct console *console = &_console;
+
+    // check it's not already initialized
+    assert(!console->initialized);
+
+    // store
+    console->config = *config;
+
+    // store callbacks?
+    if (callbacks)
+        console_set_callbacks(console, callbacks, cb_arg);
+
+    // setup the input event
+    if ((console->ev = event_new(ev_base, STDIN_FILENO, EV_READ | EV_PERSIST, &console_input, console)) == NULL)
+        JUMP_SET_ERROR(err, ERR_EVENT_NEW);
+    
+    // set our SIGINT handler
+    struct sigaction sigact;
+    
+    memset(&sigact, 0, sizeof(sigact));
+    sigact.sa_handler = on_sigint;
+    sigaction(SIGINT, &sigact, &console->old_sigint);
+
+    // setup state for initial readline prompt
+    console->have_prompt = true;
+
+    // setup readline
+    rl_callback_handler_install(config->prompt, console_line);
+    
+    // mark it as initialized
+    console->initialized = true;
+
+    // enable input
+    if (event_add(console->ev, NULL))
+        JUMP_SET_ERROR(err, ERR_EVENT_ADD);
+
+    // ok
+    *console_ptr = console;
+
+    return SUCCESS;
+
+error:
+    console_destroy(console);
+
+    return ERROR_CODE(err);
+}
+
+void console_set_callbacks (struct console *console, const struct console_callbacks *callbacks, void *cb_arg)
+{
+    console->callbacks = callbacks;
+    console->cb_arg = cb_arg;
+}
+
+void console_continue (struct console *console)
+{
+    if (!console->waiting)
+        return;
+
+    // re-enable input
+    console->waiting = false;
+
+    if (event_add(console->ev, NULL))
+        log_fatal("unable to re-enable console read event");
+    
+    // the prompt will be displayed again
+    console->have_prompt = true;
+
+    // re-setup readline with prompt
+    rl_callback_handler_install(console->config.prompt, console_line);
+}
+
+err_t console_print (struct console *console, const char *line)
+{
+    if (console->have_prompt)
+        // don't interrupt current input line
+        rl_crlf();
+
+    // output the line
+    if (printf("%s\n", line) < 0)
+        return _ERR_GENERAL;
+    
+    if (console->have_prompt) {
+        // restore input
+        rl_on_new_line();
+        rl_redisplay();
+    }
+
+    // ok
+    return SUCCESS;
+}
+
+/**
+ * Our log_output_func implementation
+ */
+static void console_log_output_func (const char *line, void *_console)
+{
+    struct console *console = _console;
+
+    console_print(console, line);
+}
+
+void console_set_log_output (struct console *console)
+{
+    // replace old one
+    log_set_func(console_log_output_func, console);
+
+    // mark
+    console->log_output = true;
+}
+
+void console_destroy (struct console *console)
+{
+    if (console->log_output)
+        // unset ourselves as the log handler
+        log_set_func(NULL, NULL);
+
+    // remove the input event
+    if (console->ev)
+        event_free(console->ev); 
+    
+    console->ev = NULL;
+
+    // de-init rl?
+    if (console->initialized)
+        rl_callback_handler_remove();
+    
+    console->initialized = false;
+
+    // restore signal handler
+    sigaction(SIGINT, &console->old_sigint, NULL);
+
+    // remove stored stuff
+    console->callbacks = console->cb_arg = NULL;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/console.h	Thu May 28 00:35:02 2009 +0300
@@ -0,0 +1,113 @@
+#ifndef LIBQMSK_CONSOLE_H
+#define LIBQMSK_CONSOLE_H
+
+/**
+ * @file
+ *
+ * An interactive line-based console interface for runtime configuration.
+ *
+ * This uses the GNU readline library to implement the console, and hence, the console uses global state, and there
+ * can only be one console per process - not that it should matter, since the console requires a tty.
+ *
+ * XXX: the log module interferes with this
+ */
+#include "error.h"
+#include <event2/event.h>
+
+/**
+ * The console state.
+ */
+struct console;
+
+/**
+ * Return codes for console_callbacks::on_line
+ */
+enum console_line_status {
+    /** 
+     * OK, line was processed, display prompt for next input line 
+     */
+    CONSOLE_CONTINUE,
+
+    /**
+     * Line is still executing, do not prompt for next line .
+     *
+     * Call console_continue() once the line was handled.
+     */
+    CONSOLE_WAIT,
+};
+
+/**
+ * Callbacks for event-based actions
+ */
+struct console_callbacks {
+    /**
+     * A line was read from the console.
+     *
+     * The return code defines how execution continues.
+     */
+    enum console_line_status (*on_line) (const char *line, void *arg);
+
+    /**
+     * EOF was read on the console.
+     *
+     * Note that for interactive consoles, EOF isn't actually EOF - there might be multiple EOFs...
+     */
+    void (*on_eof) (void *arg);
+
+    /**
+     * An input interrupt (SIGINT) was recieved while in the wait state. This can be used to abort execution and
+     * call console_continue quickly.
+     */
+    void (*on_interrupt) (void *arg);
+};
+
+/**
+ * Configuration info for console operation
+ */
+struct console_config {
+    /** The prompt to use when displaying lines */
+    const char *prompt;
+};
+
+/**
+ * Initialize the console, setting up the TTY and input handler.
+ *
+ * @param console_ptr returned new console struct
+ * @param ev_base the libevent base to use
+ * @param config configuration things for the console
+ * @param callbacks optional callbacks, can be updated later
+ * @param cb_arg option callback argument, can be updated later
+ * @param err returned error info
+ */
+err_t console_init (struct console **console_ptr, struct event_base *ev_base, const struct console_config *config,
+        const struct console_callbacks *callbacks, void *cb_arg, error_t *err);
+
+/**
+ * Replace the current callbacks with the given new ones.
+ */
+void console_set_callbacks (struct console *console, const struct console_callbacks *callbacks, void *cb_arg);
+
+/**
+ * Continue reading input after a CONSOLE_WAIT return code from console_callbacks::on_line.
+ *
+ * This does nothing if the console is not currently in the waiting state (i.e. last console_callbacks::on_line
+ * returned CONSOLE_WAIT).
+ */
+void console_continue (struct console *console);
+
+/**
+ * Output a full line (without included newline) on the console, trying not to interfere with the input too much.
+ */
+err_t console_print (struct console *console, const char *line);
+
+/**
+ * Install this console as the log output handler
+ */
+void console_set_log_output (struct console *console);
+
+/**
+ * Deinitialize the console, restoring the TTY and releasing resources
+ */
+void console_destroy (struct console *console);
+
+#endif /* LIBQMSK_CONSOLE_H */
--- a/src/lib/error.c	Wed May 27 23:57:48 2009 +0300
+++ b/src/lib/error.c	Thu May 28 00:35:02 2009 +0300
@@ -1,13 +1,9 @@
 #include "error.h"
 #include "log.h"
+#include "str.h"
 
-const struct error_list general_errors = ERROR_LIST("general",
-    ERROR_TYPE(         ERR_MEM,                "memory allocation error"           ),
-    ERROR_TYPE_STRING(  ERR_NOT_IMPLEMENTED,    "function not implmented"           ),
-    ERROR_TYPE_STRING(  ERR_MISC,               "miscellaneous error"               ),
-    ERROR_TYPE_STRING(  ERR_CMD_OPT,            "invalid command line option"       ),
-    ERROR_TYPE(         ERR_UNKNOWN,            "unknown error"                     )
-);
+#include <string.h>
+#include <stdlib.h>
 
 const struct error_type* error_lookup_code (const struct error_list *list, err_t code)
 {
@@ -26,8 +22,9 @@
 
 const struct error_type* error_lookup (const error_t *err)
 {
-    struct error_item *item;
-    struct error_type *type = NULL, *list;
+    const struct error_list *list = NULL;
+    const struct error_item *item;
+    const struct error_type *type;
 
     // traverse stack
     for (item = err->cur; item && item >= err->stack; item--) {
@@ -51,9 +48,9 @@
     return type;
 }
 
-const char* error_name (const struct error_list *list, err_t code);
+const char* error_name (const struct error_list *list, err_t code)
 {
-    struct error_type *type;
+    const struct error_type *type;
     
     // just do a lookup
     if ((type = error_lookup_code(list, code)))
@@ -71,8 +68,9 @@
     char *buf_ptr = buf; 
     size_t buf_size = sizeof(buf);
 
-    struct error_item *item;
-    struct error_type *type;
+    const struct error_list *list = NULL;
+    const struct error_item *item;
+    const struct error_type *type;
 
     // traverse stack
     for (item = err->cur; item && item >= err->stack; item--) {
@@ -92,7 +90,7 @@
         } 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", 
+            buf_ptr += str_advance(&buf_size, NULL, str_append_fmt(buf_ptr, buf_size, "[%s] %s%s", 
                 list->name, type->name, item == err->stack ? "" : ": "
             ));
 
@@ -106,13 +104,13 @@
         // 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
+            type->extra_type ? type->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)
+            err->extra_type->msg_func(err->extra_type, &err->extra_value)
         ));
 
     }
@@ -143,7 +141,7 @@
         // initial position
         return (err->cur = err->stack);
 
-    else if (err->cur < err->stack + ERR_DEPTH_MAX)
+    else if (err->cur < err->stack + ERROR_DEPTH_MAX)
         return (err->cur++);
 
     else
@@ -199,7 +197,7 @@
 
 void error_copy (error_t *dst, const error_t *src)
 {
-    struct error_item *item;
+    const struct error_item *item;
 
     // reset
     error_reset(dst);
@@ -228,12 +226,15 @@
     va_end(vargs);
 }
 
-void _error_abort (const char *file, const char *line, const char *func, const char *fmt, ...)
+void _error_abort (const char *file, int 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);
+
+    // then remember to die
+    abort();
 }
 
--- a/src/lib/error.h	Wed May 27 23:57:48 2009 +0300
+++ b/src/lib/error.h	Thu May 28 00:35:02 2009 +0300
@@ -12,6 +12,7 @@
  * then be used to trace the error down.
  */
 #include <stdbool.h>
+#include <errno.h>
 
 /**
  * The type used to represent a scalar error code, there are only unique per level.
@@ -112,7 +113,7 @@
  * Helper macro to define an error_list
  */
 #define ERROR_LIST(name, ...) \
-    { (name), __VA_ARGS__, ERROR_TYPE_END }
+    { (name), { __VA_ARGS__, ERROR_TYPE_END } }
 
 /**
  * Maximum number of nesting levels supported for errors
@@ -185,7 +186,7 @@
 /**
  * Evaluates to the current top of the error stack, 
  */
-static struct error_item* error_top (error_t *err)
+static inline struct error_item* error_top (error_t *err)
 {
     return err->cur ? err->cur : err->stack;
 }
@@ -267,26 +268,17 @@
 /**
  * Abort execution of process with error message
  */
-void _error_abort (const char *file, const char *line, const char *func, const char *fmt, ...);
+void _error_abort (const char *file, int 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));
+#define NOT_REACHED(val) error_abort("%s = %d", #val, (int) (val));
 
-/**
- * General-purpose errors that may be useful and don't belong in any more specific namespace.
+/*
+ * Include some common error codes
  */
-enum general_error_code {
-    ERR_GENERAL_NONE,
-    ERR_MEM,                ///< memory allocation error
-    ERR_NOT_IMPLEMENTED,    ///< function not implmented: <func>
-    ERR_MISC,               ///< miscellaneous error: <error>
-    ERR_CMD_OPT,            ///< invalid command line option: <error> - XXX: replace with something getopt
-    ERR_UNKNOWN,            ///< unknown error
-};
-
-const struct error_list general_errors;
+#include "errors.h"
 
 #endif /* LIBQMSK_ERROR_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/errors.c	Thu May 28 00:35:02 2009 +0300
@@ -0,0 +1,20 @@
+#include "errors.h"
+
+const struct error_list general_errors = ERROR_LIST("general",
+    ERROR_TYPE(         ERR_MEM,                "memory allocation error"           ),
+    ERROR_TYPE_STRING(  ERR_NOT_IMPLEMENTED,    "function not implmented"           ),
+    ERROR_TYPE_STRING(  ERR_MISC,               "miscellaneous error"               ),
+    ERROR_TYPE_STRING(  ERR_CMD_OPT,            "invalid command line option"       ),
+    ERROR_TYPE(         ERR_UNKNOWN,            "unknown error"                     )
+);
+
+const struct error_list libc_errors = ERROR_LIST("libc",
+    ERROR_TYPE_ERRNO(   ERR_SIGACTION,          "sigaction"                         )
+);
+
+const struct error_list libevent_errors = ERROR_LIST("libevent",
+    ERROR_TYPE(         ERR_EVENT_NEW,          "event_new"                         ),
+    ERROR_TYPE(         ERR_EVENT_ADD,          "event_add"                         ),
+    ERROR_TYPE(         ERR_EVENT_DEL,          "event_del"                         )
+);
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/errors.h	Thu May 28 00:35:02 2009 +0300
@@ -0,0 +1,52 @@
+#ifndef LIBQMSK_ERRORS_H
+#define LIBQMSK_ERRORS_H
+
+/**
+ * @file
+ *
+ * Defines some errors codes for general-purpose use, or for common libraries.
+ */
+#include "error.h"
+
+/**
+ * General-purpose errors that may be useful and don't belong in any more specific namespace.
+ */
+enum general_error_code {
+    ERR_GENERAL_NONE,
+    ERR_MEM,                ///< memory allocation error
+    ERR_NOT_IMPLEMENTED,    ///< function not implmented: <func>
+    ERR_MISC,               ///< miscellaneous error: <error>
+    ERR_CMD_OPT,            ///< invalid command line option: <error> - XXX: replace with something getopt
+    ERR_UNKNOWN,            ///< unknown error
+};
+
+const struct error_list general_errors;
+
+/**
+ * Convenience macros
+ */
+#define SET_ERROR_MEM(err_state) \
+    SET_ERROR(err_state, &general_errors, ERR_MEM)
+
+/**
+ * Errors for POSIX libc functions
+ */
+enum libc_error_code {
+    ERR_LIBC_NONE,
+    ERR_SIGACTION,          ///< sigaction: <perror>
+};
+
+const struct error_list libc_errors;
+
+/**
+ * Errors for libevent
+ */
+enum libevent_error_code {
+    ERR_EVENT_NEW,          ///< event_new
+    ERR_EVENT_ADD,          ///< event_add
+    ERR_EVENT_DEL,          ///< event_del
+};
+
+const struct error_list libevent_errors;
+
+#endif /* LIBQMSK_ERRORS_H */
--- a/src/lib/log.c	Wed May 27 23:57:48 2009 +0300
+++ b/src/lib/log.c	Thu May 28 00:35:02 2009 +0300
@@ -5,6 +5,7 @@
 #include <stdio.h>
 #include <stdarg.h>
 #include <string.h>
+#include <stdlib.h>
 
 /**
  * The default log output func
--- a/src/lib/str.c	Wed May 27 23:57:48 2009 +0300
+++ b/src/lib/str.c	Thu May 28 00:35:02 2009 +0300
@@ -185,21 +185,20 @@
 /**
  * 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)
+static err_t str_format_param (char **buf, size_t *buf_size, const char *name, const char *flags, str_format_cb func, void *arg, error_t *err)
 {
     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;
+    if (func(name, &value, &value_len, arg, err))
+        return PUSH_ERROR(err, &str_errors, ERR_STR_FMT_VALUE);
     
     // not found?
     if (!value)
-        return ERR_STR_FMT_NAME;
+        return SET_ERROR(err, &str_errors, ERR_STR_FMT_NAME);
     
     // parse flags
     while ((flag = *flags++)) {
@@ -212,7 +211,7 @@
 
             default:
                 // unknown flag
-                return ERR_STR_FMT_FLAG;
+                return SET_ERROR(err, &str_errors, ERR_STR_FMT_FLAG);
     
         }
     }
--- a/src/lib/str.h	Wed May 27 23:57:48 2009 +0300
+++ b/src/lib/str.h	Thu May 28 00:35:02 2009 +0300
@@ -20,6 +20,7 @@
     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_VALUE,              ///< parameter value
     ERR_STR_FMT_BUF_LEN,            ///< output buffer ran out
 };
 
@@ -104,9 +105,9 @@
  * @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
+ * @param err returned error info
  */
-typedef err_t (*str_format_cb) (const char *name, const char **value, ssize_t *value_len, void *arg);
+typedef err_t (*str_format_cb) (const char *name, const char **value, ssize_t *value_len, void *arg, error_t *err);
 
 /**
  * Maximum length of a parameter name
--- a/src/lua_config.c	Wed May 27 23:57:48 2009 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,16 +0,0 @@
-#include "lua_config.h"
-
-#include <lua5.1/lauxlib.h>
-
-err_t lua_config_load (struct nexus_lua *lua, const char *path, error_t *err)
-{
-    // just use luaL_loadfile and translate the error code
-    if (nexus_lua_error(lua->st, luaL_loadfile(lua->st, path), err))
-        // XXX: pollute the stack
-        return ERROR_CODE(err);
-    
-    // execute it
-    // XXX; error handler with debug info
-    return nexus_lua_error(lua->st, lua_pcall(lua->st, 0, 0, 0), err);
-}
-
--- a/src/lua_config.h	Wed May 27 23:57:48 2009 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,19 +0,0 @@
-#ifndef LUA_CONFIG_H
-#define LUA_CONFIG_H
-
-/**
- * @file
- *
- * Read a lua configuration file into a nexus_lua state
- */
-#include "error.h"
-#include "nexus_lua.h"
-
-/**
- * Load a lua config file at the given path into the nexus's lua state.
- *
- * Path can also be given as NULL to load from stdin.
- */
-err_t lua_config_load (struct nexus_lua *lua, const char *path, error_t *err);
-
-#endif /* LUA_CONFIG_H */
--- a/src/lua_console.c	Wed May 27 23:57:48 2009 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,124 +0,0 @@
-#include "lua_console.h"
-#include "lua_objs.h"
-#include "log.h"
-
-#include <stdlib.h>
-
-#include <lua5.1/lauxlib.h>
-
-struct lua_console {
-    /** The lowlevel line-based console */
-    struct console *console;
-
-    /** Our lua state */
-    struct nexus_lua *lua;
-
-    /** Coroutine state */
-    struct lua_thread thread;
-};
-
-/**
- * Line finished execution
- */
-static void lua_console_on_thread (const error_t *err, void *arg)
-{
-    struct lua_console *lc = arg;
-
-    if (err)
-        // display error message
-        console_print(lc->console, error_msg(err));
-
-    // XXX: display return value?
-    
-    // un-wait console
-    console_continue(lc->console);
-}
-
-/**
- * Got a line to exec from the console
- */
-static enum console_line_status lua_console_on_line (const char *line, void *arg)
-{
-    struct lua_console *lc = arg;
-    error_t err;
-    int ret;
-
-    // ignore empty lines and EOF
-    if (!line || !(*line))
-        return CONSOLE_CONTINUE;
-    
-    // exec it in our thread
-    if ((ret = lua_thread_start(&lc->thread, line, &err)) < 0) {
-        // display error message
-        console_print(lc->console, error_msg(&err));
-
-        return CONSOLE_CONTINUE;
-    }
-
-    // waiting?
-    if (ret)
-        return CONSOLE_WAIT;
-
-    else
-        return CONSOLE_CONTINUE;
-}
-
-static void lua_console_on_eof (void *arg)
-{
-    struct lua_console *lc = arg;
-
-    // exit the process
-    nexus_shutdown(lc->lua->nexus);
-}
-
-static void lua_console_on_interrupt (void *arg)
-{
-    struct lua_console *lc = arg;
-
-    // abort thread so we can _start again
-    lua_thread_abort(&lc->thread);
-
-    // inform user
-    console_print(lc->console, "Execution aborted.");
-    
-    // accept input
-    console_continue(lc->console);
-}
-
-static struct console_callbacks _console_callbacks = {
-    .on_line        = lua_console_on_line,
-    .on_eof         = lua_console_on_eof,
-    .on_interrupt   = lua_console_on_interrupt,
-};
-
-err_t lua_console_create (struct lua_console **lc_ptr, struct console *console, struct nexus_lua *lua, error_t *err)
-{
-    struct lua_console *lc;
-
-    // allocate
-    if ((lc = calloc(1, sizeof(*lc))) == NULL)
-        return SET_ERROR(err, ERR_CALLOC);
-
-    // store
-    lc->console = console;
-    lc->lua = lua;
-
-    // set our console callbacks
-    console_set_callbacks(console, &_console_callbacks, lc);
-
-    // init thread
-    lua_thread_init(&lc->thread, lua, lua_console_on_thread, lc);
-
-    // ok
-    *lc_ptr = lc;
-
-    return SUCCESS;
-}
-
-void lua_console_destroy (struct lua_console *lc)
-{
-    // the console
-    console_destroy(lc->console);
-
-    free(lc);
-}
--- a/src/lua_console.h	Wed May 27 23:57:48 2009 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,31 +0,0 @@
-#ifndef LUA_CONSOLE_H
-#define LUA_CONSOLE_H
-
-/**
- * @file
- *
- * An interactive lua console
- */
-#include "lua_thread.h"
-#include "console.h"
-
-#include <lua5.1/lua.h>
-
-/**
- * The lua console state
- */
-struct lua_console;
-
-/**
- * Create a new lua console based on the given low-level console, operating on the given nexus
- *
- * This overrides the console callbacks.
- */
-err_t lua_console_create (struct lua_console **lc_ptr, struct console *console, struct nexus_lua *lua, error_t *err);
-
-/**
- * Destroy the lua console state
- */
-void lua_console_destroy (struct lua_console *lc);
-
-#endif
--- a/src/lua_thread.c	Wed May 27 23:57:48 2009 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,241 +0,0 @@
-#include "lua_thread.h"
-#include "log.h"
-
-#include <string.h>
-#include <assert.h>
-
-#include <lua5.1/lauxlib.h>
-
-
-void lua_thread_init (struct lua_thread *thread, struct nexus_lua *lua, lua_thread_cb cb_func, void *cb_arg)
-{
-    // zero
-    memset(thread, 0, sizeof(*thread));
-
-    // store
-    thread->lua = lua;
-    thread->cb_func = cb_func;
-    thread->cb_arg = cb_arg;
-}
-
-/**
- * Thread completed, cleanup and call user callback with given error (NULL for success).
- */
-static void lua_thread_done (struct lua_thread *thread, error_t *err)
-{
-    // remove from registry so thread can be GC'd
-    // XXX: unsafe
-    lua_pushthread(thread->L);
-    lua_pushnil(thread->L);
-    lua_settable(thread->L, LUA_REGISTRYINDEX);
-
-    // drop our ref
-    thread->L = NULL;
-
-    // call
-    thread->cb_func(err, thread->cb_arg);
-}
-
-/**
- * Execute the lua_resume with zero arguments. If the thread finished, this returns zero, if it yielded, >0. For
- * errors, returns -err_t.
- */
-static int lua_thread_resume (lua_State *L, struct lua_thread *thread, error_t *err)
-{
-    int ret;
-
-    (void) thread;
-    
-    // invoke it
-    switch ((ret = lua_resume(L, 0))) {
-        case LUA_YIELD:
-            // ok, running, wait
-            return 1;
-
-        case 0:
-            // done
-            return 0;
-
-        default:
-            // let caller handle
-            // XXX: backtrace...
-            return -nexus_lua_error(L, ret, err);
-    }
-}
-
-struct lua_thread_start_ctx {
-    struct lua_thread *thread;
-    const char *chunk;
-};
-
-/**
- * Create a new thread, set it up, and run the initial resume, returning the boolean result of that
- * (true for yielded, false for done).
- */
-static int _lua_thread_start (lua_State *L)
-{
-    struct lua_thread_start_ctx *ctx;
-    lua_State *TL;
-    error_t err;
-    int ret;
-
-    // read the ctx argument off the stack
-    if ((ctx = lua_touserdata(L, 1)) == NULL)
-        return luaL_error(L, "lua_touserdata");
-    else
-        lua_pop(L, 1);
-    
-    // check
-    assert(!ctx->thread->L);
-
-    // create new thread
-    TL = lua_newthread(L);
- 
-    // push the lua_thread as the value
-    lua_pushlightuserdata(L, ctx->thread);
-
-    // store L -> lua_thread mapping in the registry
-    lua_settable(L, LUA_REGISTRYINDEX);
-
-    // load the chunk as a lua function into the thread's state
-    if (nexus_lua_error(TL, luaL_loadstring(TL, ctx->chunk), &err))
-        goto error;
-
-    // initial resume on the loaded chunk
-    if ((ret = lua_thread_resume(TL, ctx->thread, &err)) < 0)
-        goto error;
-
-    // yielded?
-    if (ret) 
-        // store
-        ctx->thread->L = TL;
-    
-    // pop thread to release for GC
-    lua_pop(L, 1);
-
-    return 0;
-        
-error:
-    // pop thread to release for GC
-    lua_pop(L, 1);
-
-    // drop ref
-    ctx->thread->L = NULL;
-
-    // pass on error
-    return luaL_error(L, "%s", error_msg(&err));
-}
-
-int lua_thread_start (struct lua_thread *thread, const char *chunk, error_t *err)
-{
-    struct lua_thread_start_ctx ctx = { thread, chunk };
-
-    // sanity-check
-    assert(!thread->L);
-
-    // use protected mode to setup
-    if (nexus_lua_error(thread->lua->st, lua_cpcall(thread->lua->st, _lua_thread_start, &ctx), err))
-        return -ERROR_CODE(err);
-
-    // return true if yielded, false if done
-    return (thread->L != NULL);
-}
-
-int lua_thread_yield_state (lua_State *L)
-{
-    return lua_yield(L, 0);
-}
-
-static int _lua_thread_resume_state (lua_State *L)
-{
-    struct lua_thread *thread, **thread_ptr;
-
-    // read the ctx argument off the stack
-    if ((thread_ptr = lua_touserdata(L, 1)) == NULL)
-        return luaL_error(L, "lua_touserdata");
-    else
-        lua_pop(L, 1);
-
-    // lookup the thread's context
-    lua_pushthread(L);
-    lua_gettable(L, LUA_REGISTRYINDEX);
-    
-    // not registered?
-    if (lua_isnil(L, -1))
-        return luaL_error(L, "not a registered lua_thread state");
-
-    else if (lua_isboolean(L, -1) && !lua_toboolean(L, -1))
-        return luaL_error(L, "lua_thread was aborted");
-
-    // get it as a pointer
-    if ((thread = lua_touserdata(L, -1)) == NULL)
-        return luaL_error(L, "lua_touserdata");
-    else
-        lua_pop(L, 1);
-
-    // store it
-    *thread_ptr = thread;
-
-    // ok
-    return 0;
-}
-
-void lua_thread_resume_state (lua_State *L)
-{
-    struct lua_thread *thread = NULL;
-    error_t err;
-    int ret;
-
-    // execute in protected mode
-    if (nexus_lua_error(L, lua_cpcall(L, _lua_thread_resume_state, &thread), &err))
-        return log_warn_error(&err, "%p", L);
-
-    // handle the resume
-    if ((ret = lua_thread_resume(L, thread, &err)) < 0)
-        // notify user
-        lua_thread_done(thread, &err);
-
-    // finished?
-    else if (ret == 0)
-        lua_thread_done(thread, NULL);
-
-}
-
-static int _lua_thread_abort (lua_State *L)
-{
-    struct lua_thread *thread;
-
-    // get context arg
-    if ((thread = lua_touserdata(L, -1)) == NULL)
-        luaL_error(L, "lua_thread_resume_state: lua_touserdata");
-    else
-        lua_pop(L, 1);
-
-    // no mechanism to actually abort the underlying wait yet
-    (void) thread;
-
-    // invalidate in registry for later lua_thread_resume_state
-    lua_pushthread(L);
-    lua_pushboolean(L, false);
-    lua_settable(L, LUA_REGISTRYINDEX);
-
-    // not much else we can do
-    return 0;
-}
-
-void lua_thread_abort (struct lua_thread *thread)
-{
-    error_t err;
-
-    if (!thread->L)
-        // no thread executing
-        return;
-    
-    // use protected mode
-    if (nexus_lua_error(thread->L, lua_cpcall(thread->L, _lua_thread_abort, thread), &err))
-        log_warn_error(&err, "_lua_thread_abort");
-
-    // unset
-    thread->L = NULL;
-}
-
--- a/src/lua_thread.h	Wed May 27 23:57:48 2009 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,85 +0,0 @@
-#ifndef LUA_THREAD_H
-#define LUA_THREAD_H
-
-/**
- * @file
- *
- * Running code as lua coroutines for a nexus_lua
- */
-#include "nexus_lua.h"
-#include <lua5.1/lua.h>
-
-/*
- * Forward-declare
- */
-struct lua_thread;
-
-/**
- * Callback for async execution completion. The thread will be cleaned up for re-use before this is called.
- *
- * Called with err == NULL for success, error code otherwise.
- *
- * XXX: also return execution results (as a lua_State arg)?
- */
-typedef void (*lua_thread_cb) (const error_t *err, void *arg);
-
-/**
- * A thread of execution
- */
-struct lua_thread {
-    /** The lua_nexus environment we're running under */
-    struct nexus_lua *lua;
-
-    /** Callback */
-    lua_thread_cb cb_func;
-
-    /** Callback context argument */
-    void *cb_arg;
-
-    /** Real lua thread state */
-    lua_State *L;
-};
-
-/**
- * Initialize the given lua_thread state for execution.
- *
- * You only need to call this once for each lua_thread that you use.
- */
-void lua_thread_init (struct lua_thread *thread, struct nexus_lua *lua, lua_thread_cb cb_func, void *cb_arg);
-
-/**
- * Execute the given chunk in a thread context.
- *
- * This should be called from unprotected mode, and the thread must currently not be active.
- *
- * This will load the chunk, create a new lua thread state off the lua_nexus, and then do the initial 'lua_resume' call
- * on the loaded chunk, to execute up to the first yield or final return.
- *
- * If this process raises an error, it will be returned directly. If the called chunk simply returns without yielding,
- * this returns 0, and the callback is *NOT* called. Otherwise, this returns >0 and the thread's callback function will
- * eventually be called once the thread either errors out or finishes execution.
- */
-int lua_thread_start (struct lua_thread *thread, const char *chunk, error_t *err);
-
-/**
- * Protected-mode function to yield execution of the given lua_State, which must be a luaState created with
- * lua_thread_start.
- *
- * This should be called the same way as lua_yield, except no results are supported yet.
- */
-int lua_thread_yield_state (lua_State *L);
-
-/**
- * Resume execution of the given lua_State, which must be a lua_State created with lua_thread_start.
- */
-void lua_thread_resume_state (lua_State *L);
-
-/**
- * Abort execution of the given thread, if it's running.
- *
- * Currently, there is no mechanism to actually abort a running thread, so this will instead release the lua_thread for
- * new use, and then cause the lua_thread_resume to do nothing. 
- */
-void lua_thread_abort (struct lua_thread *thread);
-
-#endif
--- a/src/nexus_lua.c	Wed May 27 23:57:48 2009 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,117 +0,0 @@
-#include "nexus_lua.h"
-#include "lua_objs.h"
-#include "lua_irc.h"
-
-#include <stdlib.h>
-
-#include <lua5.1/lualib.h>
-#include <lua5.1/lauxlib.h>
-
-/**
- * Initialize the lua state, in protected mode
- */
-int nexus_lua_init (lua_State *L)
-{
-    struct nexus_lua *lua;
-   
-    // read the nexus_lua off the stack
-    if ((lua = lua_touserdata(L, 1)) == NULL)
-        luaL_error(L, "lua_touserdata: NULL");
-
-    // open the standard libraries
-    luaL_openlibs(L);
- 
-    // open our core lua api
-    lua_objs_init(lua);
-
-    // and the irc_* stuff
-    lua_irc_init(lua);
-
-    // ok
-    return 0;
-}
-
-err_t nexus_lua_create (struct nexus_lua **lua_ptr, struct nexus *nexus, error_t *err)
-{
-    struct nexus_lua *lua;
-
-    // alloc
-    if ((lua = calloc(1, sizeof(*lua))) == NULL)
-        return SET_ERROR(err, ERR_CALLOC);
-
-    // store
-    lua->nexus = nexus;
-
-    // create the lua state
-    if ((lua->st = luaL_newstate()) == NULL)
-        JUMP_SET_ERROR(err, ERR_LUA_MEM);
-    
-    // init in protected mode
-    if (nexus_lua_error(lua->st, lua_cpcall(lua->st, &nexus_lua_init, lua), err))
-        goto error;
-
-    // ok
-    *lua_ptr = lua;
-
-    return SUCCESS;
-
-error:
-    nexus_lua_destroy(lua);
-
-    return ERROR_CODE(err);
-}
-
-void nexus_lua_destroy (struct nexus_lua *lua)
-{
-    // close the lua stuff
-    lua_close(lua->st);
-
-    free(lua);
-}
-
-err_t nexus_lua_eval (struct nexus_lua *lua, const char *chunk, error_t *err)
-{
-    int ret;
-    bool loaded = false;
-
-    RESET_ERROR(err);
-
-    // load the line as a lua function
-    if ((ret = luaL_loadstring(lua->st, chunk)))
-        goto error;
-    
-    loaded = true;
-
-    // execute it
-    if ((ret = lua_pcall(lua->st, 0, 0, 0)))
-        goto error;
-
-error:
-    if (ret) {
-        // build the error_info
-        nexus_lua_error(lua->st, ret, err);
-
-        // pop the error message
-        // XXX: err points at GC:able memory
-        lua_pop(lua->st, 1);
-    }
-
-    return ERROR_CODE(err);
-}
-
-err_t nexus_lua_error (lua_State *L, int ret, error_t *err)
-{
-    // XXX: this can raise an erorr itself
-    const char *error = lua_tostring(L, -1);
-
-    switch (ret) {
-        case 0:                 RETURN_SET_ERROR(err, SUCCESS);
-        case LUA_ERRSYNTAX:     RETURN_SET_ERROR_STR(err, ERR_LUA_SYNTAX, error);
-        case LUA_ERRRUN:        RETURN_SET_ERROR_STR(err, ERR_LUA_RUN, error);
-        case LUA_ERRMEM:        RETURN_SET_ERROR_STR(err, ERR_LUA_MEM, error);
-        case LUA_ERRERR:        RETURN_SET_ERROR_STR(err, ERR_LUA_ERR, error);
-        case LUA_ERRFILE:       RETURN_SET_ERROR_STR(err, ERR_LUA_FILE, error);
-        default:                RETURN_SET_ERROR_EXTRA(err, ERR_UNKNOWN, ret);
-    };
-}
-
--- a/src/nexus_lua.h	Wed May 27 23:57:48 2009 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,55 +0,0 @@
-#ifndef NEXUS_LUA_H
-#define NEXUS_LUA_H
-
-/**
- * @file
- *
- * Defines the lua environment for use with nexus
- */
-struct nexus_lua;
-
-#include "nexus.h"
-
-#include <lua5.1/lua.h>
-
-/**
- * The global lua state
- */
-struct nexus_lua {
-    /** The nexus we are operating on */
-    struct nexus *nexus;
-
-    /** The main lua state */
-    lua_State *st;
-};
-
-/**
- * Create a new lua state for nexus
- */
-err_t nexus_lua_create (struct nexus_lua **lua_ptr, struct nexus *nexus, error_t *err);
-
-/**
- * Destroy the lua state
- */
-void nexus_lua_destroy (struct nexus_lua *lua);
-
-/**
- * Parse and execute the given lua chunk in the nexus's lua context.
- *
- * This operation is equally valid for both textual and binary chunks, but this is intended for textual chunks, and
- * hence accepts a NUL-terminated string.
- *
- * This runs the chunk in the main lua state, so this will fail for any coroutine functions.
- */
-err_t nexus_lua_eval (struct nexus_lua *lua, const char *chunk, error_t *err);
-
-/**
- * Handle a Lua error by converting the given error code into a ERR_LUA_* code, inspecting the error object at
- * the top of the stack.
- *
- * Please note that the resulting error_info points into strings inside the lua stack - once you pop the error, the
- * error_info might not be valid anymore after the next GC cycle.
- */
-err_t nexus_lua_error (lua_State *L, int ret, error_t *err);
-
-#endif /* NEXUS_LUA_H */
--- a/src/spbot/config.h	Wed May 27 23:57:48 2009 +0300
+++ b/src/spbot/config.h	Thu May 28 00:35:02 2009 +0300
@@ -1,5 +1,5 @@
-#ifndef CONFIG_H
-#define CONFIG_H
+#ifndef SPBOT_CONFIG_H
+#define SPBOT_CONFIG_H
 
 /**
  * @file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/spbot/lua_config.c	Thu May 28 00:35:02 2009 +0300
@@ -0,0 +1,16 @@
+#include "lua_config.h"
+
+#include <lua5.1/lauxlib.h>
+
+err_t lua_config_load (struct nexus_lua *lua, const char *path, error_t *err)
+{
+    // just use luaL_loadfile and translate the error code
+    if (nexus_lua_error(lua->st, luaL_loadfile(lua->st, path), err))
+        // XXX: pollute the stack
+        return ERROR_CODE(err);
+    
+    // execute it
+    // XXX; error handler with debug info
+    return nexus_lua_error(lua->st, lua_pcall(lua->st, 0, 0, 0), err);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/spbot/lua_config.h	Thu May 28 00:35:02 2009 +0300
@@ -0,0 +1,19 @@
+#ifndef SPBOT_LUA_CONFIG_H
+#define SPBOT_LUA_CONFIG_H
+
+/**
+ * @file
+ *
+ * Read a lua configuration file into a nexus_lua state
+ */
+#include <lib/error.h>
+#include "nexus_lua.h"
+
+/**
+ * Load a lua config file at the given path into the nexus's lua state.
+ *
+ * Path can also be given as NULL to load from stdin.
+ */
+err_t lua_config_load (struct nexus_lua *lua, const char *path, error_t *err);
+
+#endif /* LUA_CONFIG_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/spbot/lua_console.c	Thu May 28 00:35:02 2009 +0300
@@ -0,0 +1,124 @@
+#include "lua_console.h"
+#include "lua_objs.h"
+#include "log.h"
+
+#include <stdlib.h>
+
+#include <lua5.1/lauxlib.h>
+
+struct lua_console {
+    /** The lowlevel line-based console */
+    struct console *console;
+
+    /** Our lua state */
+    struct nexus_lua *lua;
+
+    /** Coroutine state */
+    struct lua_thread thread;
+};
+
+/**
+ * Line finished execution
+ */
+static void lua_console_on_thread (const error_t *err, void *arg)
+{
+    struct lua_console *lc = arg;
+
+    if (err)
+        // display error message
+        console_print(lc->console, error_msg(err));
+
+    // XXX: display return value?
+    
+    // un-wait console
+    console_continue(lc->console);
+}
+
+/**
+ * Got a line to exec from the console
+ */
+static enum console_line_status lua_console_on_line (const char *line, void *arg)
+{
+    struct lua_console *lc = arg;
+    error_t err;
+    int ret;
+
+    // ignore empty lines and EOF
+    if (!line || !(*line))
+        return CONSOLE_CONTINUE;
+    
+    // exec it in our thread
+    if ((ret = lua_thread_start(&lc->thread, line, &err)) < 0) {
+        // display error message
+        console_print(lc->console, error_msg(&err));
+
+        return CONSOLE_CONTINUE;
+    }
+
+    // waiting?
+    if (ret)
+        return CONSOLE_WAIT;
+
+    else
+        return CONSOLE_CONTINUE;
+}
+
+static void lua_console_on_eof (void *arg)
+{
+    struct lua_console *lc = arg;
+
+    // exit the process
+    nexus_shutdown(lc->lua->nexus);
+}
+
+static void lua_console_on_interrupt (void *arg)
+{
+    struct lua_console *lc = arg;
+
+    // abort thread so we can _start again
+    lua_thread_abort(&lc->thread);
+
+    // inform user
+    console_print(lc->console, "Execution aborted.");
+    
+    // accept input
+    console_continue(lc->console);
+}
+
+static struct console_callbacks _console_callbacks = {
+    .on_line        = lua_console_on_line,
+    .on_eof         = lua_console_on_eof,
+    .on_interrupt   = lua_console_on_interrupt,
+};
+
+err_t lua_console_create (struct lua_console **lc_ptr, struct console *console, struct nexus_lua *lua, error_t *err)
+{
+    struct lua_console *lc;
+
+    // allocate
+    if ((lc = calloc(1, sizeof(*lc))) == NULL)
+        return SET_ERROR(err, ERR_CALLOC);
+
+    // store
+    lc->console = console;
+    lc->lua = lua;
+
+    // set our console callbacks
+    console_set_callbacks(console, &_console_callbacks, lc);
+
+    // init thread
+    lua_thread_init(&lc->thread, lua, lua_console_on_thread, lc);
+
+    // ok
+    *lc_ptr = lc;
+
+    return SUCCESS;
+}
+
+void lua_console_destroy (struct lua_console *lc)
+{
+    // the console
+    console_destroy(lc->console);
+
+    free(lc);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/spbot/lua_console.h	Thu May 28 00:35:02 2009 +0300
@@ -0,0 +1,31 @@
+#ifndef SPBOT_LUA_CONSOLE_H
+#define SPBOT_LUA_CONSOLE_H
+
+/**
+ * @file
+ *
+ * An interactive lua console
+ */
+#include "lua_thread.h"
+#include <lib/console.h>
+
+#include <lua5.1/lua.h>
+
+/**
+ * The lua console state
+ */
+struct lua_console;
+
+/**
+ * Create a new lua console based on the given low-level console, operating on the given nexus
+ *
+ * This overrides the console callbacks.
+ */
+err_t lua_console_create (struct lua_console **lc_ptr, struct console *console, struct nexus_lua *lua, error_t *err);
+
+/**
+ * Destroy the lua console state
+ */
+void lua_console_destroy (struct lua_console *lc);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/spbot/lua_thread.c	Thu May 28 00:35:02 2009 +0300
@@ -0,0 +1,241 @@
+#include "lua_thread.h"
+#include <lib/log.h>
+
+#include <string.h>
+#include <assert.h>
+
+#include <lua5.1/lauxlib.h>
+
+
+void lua_thread_init (struct lua_thread *thread, struct nexus_lua *lua, lua_thread_cb cb_func, void *cb_arg)
+{
+    // zero
+    memset(thread, 0, sizeof(*thread));
+
+    // store
+    thread->lua = lua;
+    thread->cb_func = cb_func;
+    thread->cb_arg = cb_arg;
+}
+
+/**
+ * Thread completed, cleanup and call user callback with given error (NULL for success).
+ */
+static void lua_thread_done (struct lua_thread *thread, error_t *err)
+{
+    // remove from registry so thread can be GC'd
+    // XXX: unsafe
+    lua_pushthread(thread->L);
+    lua_pushnil(thread->L);
+    lua_settable(thread->L, LUA_REGISTRYINDEX);
+
+    // drop our ref
+    thread->L = NULL;
+
+    // call
+    thread->cb_func(err, thread->cb_arg);
+}
+
+/**
+ * Execute the lua_resume with zero arguments. If the thread finished, this returns zero, if it yielded, >0. For
+ * errors, returns -err_t.
+ */
+static int lua_thread_resume (lua_State *L, struct lua_thread *thread, error_t *err)
+{
+    int ret;
+
+    (void) thread;
+    
+    // invoke it
+    switch ((ret = lua_resume(L, 0))) {
+        case LUA_YIELD:
+            // ok, running, wait
+            return 1;
+
+        case 0:
+            // done
+            return 0;
+
+        default:
+            // let caller handle
+            // XXX: backtrace...
+            return -nexus_lua_error(L, ret, err);
+    }
+}
+
+struct lua_thread_start_ctx {
+    struct lua_thread *thread;
+    const char *chunk;
+};
+
+/**
+ * Create a new thread, set it up, and run the initial resume, returning the boolean result of that
+ * (true for yielded, false for done).
+ */
+static int _lua_thread_start (lua_State *L)
+{
+    struct lua_thread_start_ctx *ctx;
+    lua_State *TL;
+    error_t err;
+    int ret;
+
+    // read the ctx argument off the stack
+    if ((ctx = lua_touserdata(L, 1)) == NULL)
+        return luaL_error(L, "lua_touserdata");
+    else
+        lua_pop(L, 1);
+    
+    // check
+    assert(!ctx->thread->L);
+
+    // create new thread
+    TL = lua_newthread(L);
+ 
+    // push the lua_thread as the value
+    lua_pushlightuserdata(L, ctx->thread);
+
+    // store L -> lua_thread mapping in the registry
+    lua_settable(L, LUA_REGISTRYINDEX);
+
+    // load the chunk as a lua function into the thread's state
+    if (nexus_lua_error(TL, luaL_loadstring(TL, ctx->chunk), &err))
+        goto error;
+
+    // initial resume on the loaded chunk
+    if ((ret = lua_thread_resume(TL, ctx->thread, &err)) < 0)
+        goto error;
+
+    // yielded?
+    if (ret) 
+        // store
+        ctx->thread->L = TL;
+    
+    // pop thread to release for GC
+    lua_pop(L, 1);
+
+    return 0;
+        
+error:
+    // pop thread to release for GC
+    lua_pop(L, 1);
+
+    // drop ref
+    ctx->thread->L = NULL;
+
+    // pass on error
+    return luaL_error(L, "%s", error_msg(&err));
+}
+
+int lua_thread_start (struct lua_thread *thread, const char *chunk, error_t *err)
+{
+    struct lua_thread_start_ctx ctx = { thread, chunk };
+
+    // sanity-check
+    assert(!thread->L);
+
+    // use protected mode to setup
+    if (nexus_lua_error(thread->lua->st, lua_cpcall(thread->lua->st, _lua_thread_start, &ctx), err))
+        return -ERROR_CODE(err);
+
+    // return true if yielded, false if done
+    return (thread->L != NULL);
+}
+
+int lua_thread_yield_state (lua_State *L)
+{
+    return lua_yield(L, 0);
+}
+
+static int _lua_thread_resume_state (lua_State *L)
+{
+    struct lua_thread *thread, **thread_ptr;
+
+    // read the ctx argument off the stack
+    if ((thread_ptr = lua_touserdata(L, 1)) == NULL)
+        return luaL_error(L, "lua_touserdata");
+    else
+        lua_pop(L, 1);
+
+    // lookup the thread's context
+    lua_pushthread(L);
+    lua_gettable(L, LUA_REGISTRYINDEX);
+    
+    // not registered?
+    if (lua_isnil(L, -1))
+        return luaL_error(L, "not a registered lua_thread state");
+
+    else if (lua_isboolean(L, -1) && !lua_toboolean(L, -1))
+        return luaL_error(L, "lua_thread was aborted");
+
+    // get it as a pointer
+    if ((thread = lua_touserdata(L, -1)) == NULL)
+        return luaL_error(L, "lua_touserdata");
+    else
+        lua_pop(L, 1);
+
+    // store it
+    *thread_ptr = thread;
+
+    // ok
+    return 0;
+}
+
+void lua_thread_resume_state (lua_State *L)
+{
+    struct lua_thread *thread = NULL;
+    error_t err;
+    int ret;
+
+    // execute in protected mode
+    if (nexus_lua_error(L, lua_cpcall(L, _lua_thread_resume_state, &thread), &err))
+        return log_warn_error(&err, "%p", L);
+
+    // handle the resume
+    if ((ret = lua_thread_resume(L, thread, &err)) < 0)
+        // notify user
+        lua_thread_done(thread, &err);
+
+    // finished?
+    else if (ret == 0)
+        lua_thread_done(thread, NULL);
+
+}
+
+static int _lua_thread_abort (lua_State *L)
+{
+    struct lua_thread *thread;
+
+    // get context arg
+    if ((thread = lua_touserdata(L, -1)) == NULL)
+        luaL_error(L, "lua_thread_resume_state: lua_touserdata");
+    else
+        lua_pop(L, 1);
+
+    // no mechanism to actually abort the underlying wait yet
+    (void) thread;
+
+    // invalidate in registry for later lua_thread_resume_state
+    lua_pushthread(L);
+    lua_pushboolean(L, false);
+    lua_settable(L, LUA_REGISTRYINDEX);
+
+    // not much else we can do
+    return 0;
+}
+
+void lua_thread_abort (struct lua_thread *thread)
+{
+    error_t err;
+
+    if (!thread->L)
+        // no thread executing
+        return;
+    
+    // use protected mode
+    if (nexus_lua_error(thread->L, lua_cpcall(thread->L, _lua_thread_abort, thread), &err))
+        log_warn_error(&err, "_lua_thread_abort");
+
+    // unset
+    thread->L = NULL;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/spbot/lua_thread.h	Thu May 28 00:35:02 2009 +0300
@@ -0,0 +1,85 @@
+#ifndef SPBOT_LUA_THREAD_H
+#define SPBOT_LUA_THREAD_H
+
+/**
+ * @file
+ *
+ * Running code as lua coroutines for a nexus_lua
+ */
+#include "nexus_lua.h"
+#include <lua5.1/lua.h>
+
+/*
+ * Forward-declare
+ */
+struct lua_thread;
+
+/**
+ * Callback for async execution completion. The thread will be cleaned up for re-use before this is called.
+ *
+ * Called with err == NULL for success, error code otherwise.
+ *
+ * XXX: also return execution results (as a lua_State arg)?
+ */
+typedef void (*lua_thread_cb) (const error_t *err, void *arg);
+
+/**
+ * A thread of execution
+ */
+struct lua_thread {
+    /** The lua_nexus environment we're running under */
+    struct nexus_lua *lua;
+
+    /** Callback */
+    lua_thread_cb cb_func;
+
+    /** Callback context argument */
+    void *cb_arg;
+
+    /** Real lua thread state */
+    lua_State *L;
+};
+
+/**
+ * Initialize the given lua_thread state for execution.
+ *
+ * You only need to call this once for each lua_thread that you use.
+ */
+void lua_thread_init (struct lua_thread *thread, struct nexus_lua *lua, lua_thread_cb cb_func, void *cb_arg);
+
+/**
+ * Execute the given chunk in a thread context.
+ *
+ * This should be called from unprotected mode, and the thread must currently not be active.
+ *
+ * This will load the chunk, create a new lua thread state off the lua_nexus, and then do the initial 'lua_resume' call
+ * on the loaded chunk, to execute up to the first yield or final return.
+ *
+ * If this process raises an error, it will be returned directly. If the called chunk simply returns without yielding,
+ * this returns 0, and the callback is *NOT* called. Otherwise, this returns >0 and the thread's callback function will
+ * eventually be called once the thread either errors out or finishes execution.
+ */
+int lua_thread_start (struct lua_thread *thread, const char *chunk, error_t *err);
+
+/**
+ * Protected-mode function to yield execution of the given lua_State, which must be a luaState created with
+ * lua_thread_start.
+ *
+ * This should be called the same way as lua_yield, except no results are supported yet.
+ */
+int lua_thread_yield_state (lua_State *L);
+
+/**
+ * Resume execution of the given lua_State, which must be a lua_State created with lua_thread_start.
+ */
+void lua_thread_resume_state (lua_State *L);
+
+/**
+ * Abort execution of the given thread, if it's running.
+ *
+ * Currently, there is no mechanism to actually abort a running thread, so this will instead release the lua_thread for
+ * new use, and then cause the lua_thread_resume to do nothing. 
+ */
+void lua_thread_abort (struct lua_thread *thread);
+
+#endif
--- a/src/spbot/module.c	Wed May 27 23:57:48 2009 +0300
+++ b/src/spbot/module.c	Thu May 28 00:35:02 2009 +0300
@@ -1,5 +1,5 @@
 #include "module.h"
-#include "log.h"
+#include <lib/log.h>
 
 #include <stdlib.h>
 #include <unistd.h>
@@ -8,22 +8,24 @@
 #include <assert.h>
 
 const struct error_list module_errors = ERROR_LIST("module",
-    ERROR_TYPE_STRING(  ERR_MODULE_NAME,        "invalid module name"       ),
-    ERROR_TYPE(         ERR_MODULE_DUP,         "module already loaded"     ),
-    ERROR_TYPE_STRING(  ERR_MODULE_PATH,        "invalid module path"       ),
-    ERROR_TYPE_STRING(  ERR_MODULE_OPEN,        "module dlopen() failed"    ),
-    ERROR_TYPE_STRING(  ERR_MODULE_SYM,         "module dlsym() failed"     ),
-    ERROR_TYPE_STRING(  ERR_MODULE_INIT_FUNC,   "invalid module init func"  ),
-    ERROR_TYPE_STRING(  ERR_MODULE_CONF,        "module_conf"               )
+    ERROR_TYPE_STRING(  ERR_MODULE_NAME,        "invalid module name"                       ),
+    ERROR_TYPE(         ERR_MODULE_DUP,         "module already loaded"                     ),
+    ERROR_TYPE_STRING(  ERR_MODULE_PATH,        "invalid module path"                       ),
+    ERROR_TYPE_STRING(  ERR_MODULE_OPEN,        "module dlopen() failed"                    ),
+    ERROR_TYPE_STRING(  ERR_MODULE_SYM,         "module dlsym() failed"                     ),
+    ERROR_TYPE_STRING(  ERR_MODULE_INIT_FUNC,   "invalid module init func"                  ),
+    ERROR_TYPE_STRING(  ERR_MODULE_CONF,        "module_conf"                               ),
+    ERROR_TYPE(         ERR_MODULE_STATE,       "module in wrong state for operation"       ),
+    ERROR_TYPE(         ERR_MODULE_UNLOAD,      "unable to unload module"                   )
 );
 
-err_t modules_create (struct modules **modules_ptr, struct nexus *nexus)
+err_t modules_create (struct modules **modules_ptr, struct nexus *nexus, error_t *err)
 {
     struct modules *modules;
 
     // alloc
     if ((modules = calloc(1, sizeof(*modules))) == NULL)
-        return ERR_CALLOC;
+        return SET_ERROR_MEM(err);
     
     // init
     TAILQ_INIT(&modules->list);
@@ -85,12 +87,12 @@
 err_t modules_unload (struct modules *modules)
 {
     struct module *module;
-    err_t err;
+    error_t err;
 
     // unload each module in turn
     while ((module = TAILQ_FIRST(&modules->list))) {
-        if ((err = module_unload(module)))
-            log_warn("module_unload(%s) failed: %s", module->info.name, error_name(err));
+        if (module_unload(module, &err))
+            log_warn_error(&err, "module_unload(%s) failed", module->info.name);
     }
 
     // ok
@@ -208,7 +210,7 @@
 
     // alloc
     if ((module = calloc(1, sizeof(*module))) == NULL)
-        return SET_ERROR(err, &module_errors, ERR_CALLOC);
+        return SET_ERROR_MEM(err);
     
     // initialize
     module->info = *_info;
@@ -240,8 +242,8 @@
         JUMP_SET_ERROR_STR(err, &module_errors, ERR_MODULE_OPEN, dlerror());
     
     // load the funcs symbol
-    if ((ERROR_CODE(err) = module_symbol(module, (void **) &module->desc, "module")))
-        JUMP_SET_ERROR_STR(err, &module_errors, ERROR_CODE(err), dlerror());
+    if (module_symbol(module, (void **) &module->desc, "module", err))
+        goto error;
 
     // call the init func
     if ((module->desc->init(modules->nexus, &module->ctx, err)))
@@ -279,11 +281,11 @@
 {
     // sanity-check
     if (!module->desc->config_options)
-        RETURN_SET_ERROR_STR(err, &module_errors, ERR_MODULE_CONF, "module does not have any config options");
+        return SET_ERROR_STR(err, &module_errors, ERR_MODULE_CONF, "module does not have any config options");
 
     // wrong state
     if (module->unloading)
-        RETURN_SET_ERROR_STR(err, &module_errors, ERR_MODULE_CONF, "module is being unloaded");
+        return SET_ERROR_STR(err, &module_errors, ERR_MODULE_CONF, "module is being unloaded");
     
     // parse/apply using the module's stuff
     // XXX: error namespaces?
@@ -308,20 +310,18 @@
 {
     // wrong state
     if (module->unloading)
-        RETURN_SET_ERROR_STR(err, &module_errors, ERR_MODULE_CONF, "module is being unloaded");
+        return SET_ERROR_STR(err, &module_errors, ERR_MODULE_CONF, "module is being unloaded");
     
     // apply with the module's ctx
     // XXX: error namespaces?
     return config_apply_opt(opt, module->ctx, value, err);
 }
 
-err_t module_unload (struct module *module)
+err_t module_unload (struct module *module, error_t *err)
 {
-    err_t err;
-    
     // wrong state
     if (module->unloading)
-        return ERR_MODULE_STATE;
+        return SET_ERROR(err, &module_errors, ERR_MODULE_STATE);
 
     // remove from modules list
     TAILQ_REMOVE(&module->modules->list, module, modules_list);
@@ -332,11 +332,11 @@
     if (module->desc->unload) {
         // invoke the unload func, passing it our reference
         // note that the module might not exist anymore after calling this
-        if ((err = module->desc->unload(module->ctx, module))) {
+        if (module->desc->unload(module->ctx, module, err)) {
             // mark it as "unloaded"
             module_unloaded(module);
-
-            return err;
+            
+            return PUSH_ERROR(err, &module_errors, ERR_MODULE_UNLOAD);
         }
 
     } else {
--- a/src/spbot/module.h	Wed May 27 23:57:48 2009 +0300
+++ b/src/spbot/module.h	Thu May 28 00:35:02 2009 +0300
@@ -1,5 +1,5 @@
-#ifndef MODULE_H
-#define MODULE_H
+#ifndef SPBOT_MODULE_H
+#define SPBOT_MODULE_H
 
 /**
  * @file
@@ -47,6 +47,7 @@
     ERR_MODULE_INIT_FUNC,   ///< invalid module_init_func_t
     ERR_MODULE_CONF,        ///< value error in configuration data
     ERR_MODULE_STATE,       ///< module in wrong state for operation
+    ERR_MODULE_UNLOAD,      ///< unable to unload module
 };
 
 const struct error_list module_errors;
@@ -108,7 +109,7 @@
      * @param module the hanging module reference, that must be passed to module_unloaded()
      * @return error code
      */
-    err_t (*unload) (void *ctx, struct module *module);
+    err_t (*unload) (void *ctx, struct module *module, error_t *err);
 
     /**
      * Destroy the module now. No later chances, the module's code will be unloaded directly after this, which means
@@ -187,7 +188,7 @@
 /**
  * Create a new modules state
  */
-err_t modules_create (struct modules **modules_ptr, struct nexus *nexus);
+err_t modules_create (struct modules **modules_ptr, struct nexus *nexus, error_t *err);
 
 /**
  * Set a search path for finding modules by name. The given string won't be copied.
@@ -271,10 +272,10 @@
  * \p unload function, passing it the primary reference. The module's unload code will then go about shutting down the
  * module, and once that is done, it may module_put() the primary reference, which may then lead to module_destroy().
  *
- * This returns ERR_MODULE_STATE if the module is already being unloaded, or other errors from the module's own unload
- * functionality.
+ * This returns ERR_MODULE_STATE if the module is already being unloaded, or ERR_MODULE_UNLOAD if the module's unload
+ * func fails.
  */
-err_t module_unload (struct module *module);
+err_t module_unload (struct module *module, error_t *err);
 
 /**
  * Used by a module itself to indicate that an module_desc::unload() operation has completed. This will execute a
--- a/src/spbot/nexus.c	Wed May 27 23:57:48 2009 +0300
+++ b/src/spbot/nexus.c	Thu May 28 00:35:02 2009 +0300
@@ -535,7 +535,7 @@
     
 
     // initialize signal handlers
-    if ((ERROR_CODE(&err) = signals_create(&nexus->signals, nexus->ev_base)))
+    if (signals_create(&nexus->signals, nexus->ev_base, &err))
         FATAL("signals_create");
  
     // add our signal handlers
@@ -554,7 +554,7 @@
         FATAL_ERROR(&err, "sock_init");
 
     // modules 
-    if ((ERROR_CODE(&err) = modules_create(&nexus->modules, nexus)))
+    if (modules_create(&nexus->modules, nexus, &err))
         FATAL_ERROR(&err, "modules_create");
     
     // the IRC client
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/spbot/nexus_lua.c	Thu May 28 00:35:02 2009 +0300
@@ -0,0 +1,117 @@
+#include "nexus_lua.h"
+#include "../lua_objs.h"
+#include "../lua_irc.h"
+
+#include <stdlib.h>
+
+#include <lua5.1/lualib.h>
+#include <lua5.1/lauxlib.h>
+
+/**
+ * Initialize the lua state, in protected mode
+ */
+int nexus_lua_init (lua_State *L)
+{
+    struct nexus_lua *lua;
+   
+    // read the nexus_lua off the stack
+    if ((lua = lua_touserdata(L, 1)) == NULL)
+        luaL_error(L, "lua_touserdata: NULL");
+
+    // open the standard libraries
+    luaL_openlibs(L);
+ 
+    // open our core lua api
+    lua_objs_init(lua);
+
+    // and the irc_* stuff
+    lua_irc_init(lua);
+
+    // ok
+    return 0;
+}
+
+err_t nexus_lua_create (struct nexus_lua **lua_ptr, struct nexus *nexus, error_t *err)
+{
+    struct nexus_lua *lua;
+
+    // alloc
+    if ((lua = calloc(1, sizeof(*lua))) == NULL)
+        return SET_ERROR(err, ERR_CALLOC);
+
+    // store
+    lua->nexus = nexus;
+
+    // create the lua state
+    if ((lua->st = luaL_newstate()) == NULL)
+        JUMP_SET_ERROR(err, ERR_LUA_MEM);
+    
+    // init in protected mode
+    if (nexus_lua_error(lua->st, lua_cpcall(lua->st, &nexus_lua_init, lua), err))
+        goto error;
+
+    // ok
+    *lua_ptr = lua;
+
+    return SUCCESS;
+
+error:
+    nexus_lua_destroy(lua);
+
+    return ERROR_CODE(err);
+}
+
+void nexus_lua_destroy (struct nexus_lua *lua)
+{
+    // close the lua stuff
+    lua_close(lua->st);
+
+    free(lua);
+}
+
+err_t nexus_lua_eval (struct nexus_lua *lua, const char *chunk, error_t *err)
+{
+    int ret;
+    bool loaded = false;
+
+    RESET_ERROR(err);
+
+    // load the line as a lua function
+    if ((ret = luaL_loadstring(lua->st, chunk)))
+        goto error;
+    
+    loaded = true;
+
+    // execute it
+    if ((ret = lua_pcall(lua->st, 0, 0, 0)))
+        goto error;
+
+error:
+    if (ret) {
+        // build the error_info
+        nexus_lua_error(lua->st, ret, err);
+
+        // pop the error message
+        // XXX: err points at GC:able memory
+        lua_pop(lua->st, 1);
+    }
+
+    return ERROR_CODE(err);
+}
+
+err_t nexus_lua_error (lua_State *L, int ret, error_t *err)
+{
+    // XXX: this can raise an erorr itself
+    const char *error = lua_tostring(L, -1);
+
+    switch (ret) {
+        case 0:                 RETURN_SET_ERROR(err, SUCCESS);
+        case LUA_ERRSYNTAX:     RETURN_SET_ERROR_STR(err, ERR_LUA_SYNTAX, error);
+        case LUA_ERRRUN:        RETURN_SET_ERROR_STR(err, ERR_LUA_RUN, error);
+        case LUA_ERRMEM:        RETURN_SET_ERROR_STR(err, ERR_LUA_MEM, error);
+        case LUA_ERRERR:        RETURN_SET_ERROR_STR(err, ERR_LUA_ERR, error);
+        case LUA_ERRFILE:       RETURN_SET_ERROR_STR(err, ERR_LUA_FILE, error);
+        default:                RETURN_SET_ERROR_EXTRA(err, ERR_UNKNOWN, ret);
+    };
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/spbot/nexus_lua.h	Thu May 28 00:35:02 2009 +0300
@@ -0,0 +1,55 @@
+#ifndef SPBOT_NEXUS_LUA_H
+#define SPBOT_NEXUS_LUA_H
+
+/**
+ * @file
+ *
+ * Defines the lua environment for use with nexus
+ */
+struct nexus_lua;
+
+#include "nexus.h"
+
+#include <lua5.1/lua.h>
+
+/**
+ * The global lua state
+ */
+struct nexus_lua {
+    /** The nexus we are operating on */
+    struct nexus *nexus;
+
+    /** The main lua state */
+    lua_State *st;
+};
+
+/**
+ * Create a new lua state for nexus
+ */
+err_t nexus_lua_create (struct nexus_lua **lua_ptr, struct nexus *nexus, error_t *err);
+
+/**
+ * Destroy the lua state
+ */
+void nexus_lua_destroy (struct nexus_lua *lua);
+
+/**
+ * Parse and execute the given lua chunk in the nexus's lua context.
+ *
+ * This operation is equally valid for both textual and binary chunks, but this is intended for textual chunks, and
+ * hence accepts a NUL-terminated string.
+ *
+ * This runs the chunk in the main lua state, so this will fail for any coroutine functions.
+ */
+err_t nexus_lua_eval (struct nexus_lua *lua, const char *chunk, error_t *err);
+
+/**
+ * Handle a Lua error by converting the given error code into a ERR_LUA_* code, inspecting the error object at
+ * the top of the stack.
+ *
+ * Please note that the resulting error_info points into strings inside the lua stack - once you pop the error, the
+ * error_info might not be valid anymore after the next GC cycle.
+ */
+err_t nexus_lua_error (lua_State *L, int ret, error_t *err);
+
+#endif /* SPBOT_NEXUS_LUA_H */
--- a/src/spbot/signals.c	Wed May 27 23:57:48 2009 +0300
+++ b/src/spbot/signals.c	Thu May 28 00:35:02 2009 +0300
@@ -1,6 +1,6 @@
 
 #include "signals.h"
-#include "log.h"
+#include <lib/log.h>
 
 #define _GNU_SOURCE
 
@@ -27,7 +27,8 @@
 void signals_loopexit (int signal, short event, void *arg) 
 {
     struct signals *signals = arg;
-
+    
+    (void) signal;
     (void) event;
 
     log_info("caught %s: exiting", /* strsignal(signal) */ "xxx");
@@ -48,12 +49,12 @@
     /* ignore */
 }
 
-err_t signals_create (struct signals **signals_ptr, struct event_base *ev_base)
+err_t signals_create (struct signals **signals_ptr, struct event_base *ev_base, error_t *err)
 {
     struct signals *signals;
 
     if ((signals = calloc(1, sizeof(*signals))) == NULL)
-        return ERR_CALLOC;
+        return SET_ERROR(err, &general_errors, ERR_MEM);
     
     // simple attributes
     signals->ev_base = ev_base;
@@ -83,7 +84,7 @@
     return SUCCESS;
 }
 
-err_t signal_ignore (int signum, struct error_info *err)
+err_t signal_ignore (int signum, error_t *err)
 {
     struct sigaction act = {
         .sa_handler     = SIG_IGN,
@@ -91,7 +92,7 @@
     
     // signall the handler
     if (sigaction(signum, &act, NULL))
-        RETURN_SET_ERROR_ERRNO(err, ERR_SIGACTION);
+        return SET_ERROR_ERRNO(err, &libc_errors, ERR_SIGACTION);
 
     // ok
     return SUCCESS;
@@ -100,10 +101,10 @@
 struct signals *signals_default (struct event_base *ev_base) 
 {
     struct signals *signals = NULL;
-    struct error_info err;
+    error_t err;
     
     // alloc signals
-    if (signals_create(&signals, ev_base))
+    if (signals_create(&signals, ev_base, &err))
         return NULL;
     
     // add the set of default signals
--- a/src/spbot/signals.h	Wed May 27 23:57:48 2009 +0300
+++ b/src/spbot/signals.h	Thu May 28 00:35:02 2009 +0300
@@ -34,7 +34,7 @@
  *
  * Returns NULL on failure
  */
-err_t signals_create (struct signals **signals_ptr, struct event_base *ev_base);
+err_t signals_create (struct signals **signals_ptr, struct event_base *ev_base, error_t *err);
 
 /**
  * Add a signal to be handled by the given signals struct with the given handler.