add some rudimentary lua support, by having a simple interactive console, and providing access to irc_client_quit
authorTero Marttila <terom@fixme.fi>
Tue, 31 Mar 2009 19:35:51 +0300
changeset 93 42ade8285570
parent 92 99661e5aac91
child 94 05a96b200d7b
add some rudimentary lua support, by having a simple interactive console, and providing access to irc_client_quit
cmake/Modules/FindLua51.cmake
src/CMakeLists.txt
src/console.c
src/console.h
src/error.h
src/lua_console.c
src/lua_console.h
src/lua_objs.c
src/lua_objs.h
src/nexus.c
src/nexus.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cmake/Modules/FindLua51.cmake	Tue Mar 31 19:35:51 2009 +0300
@@ -0,0 +1,23 @@
+# Find lua 5.1
+# Once done, this will define:
+#
+#   Lua51_FOUND
+#   Lua51_INCLUDE_DIRS
+#   Lua51_LIBRARIES
+#
+include (LibFindMacros)
+
+# include dir
+find_path (Lua51_INCLUDE_DIR
+    NAMES "lua5.1/lua.h"
+)
+
+# library
+find_library (Lua51_LIBRARY
+    NAMES "lua5.1"
+)
+
+# set the external vars
+set (Lua51_PROCESS_INCLUDES Lua51_INCLUDE_DIR)
+set (Lua51_PROCESS_LIBS Lua51_LIBRARY)
+libfind_process (Lua51)
--- a/src/CMakeLists.txt	Tue Mar 31 15:41:24 2009 +0300
+++ b/src/CMakeLists.txt	Tue Mar 31 19:35:51 2009 +0300
@@ -3,22 +3,24 @@
 find_package (GnuTLS REQUIRED)
 find_package (LibPQ REQUIRED)
 find_package (Evsql REQUIRED)
+find_package (Lua51 REQUIRED)
 
 # add our include path
-include_directories (${LibEvent_INCLUDE_DIRS} ${GnuTLS_INCLUDE_DIRS} ${Evsql_INCLUDE_DIRS})
+include_directories (${LibEvent_INCLUDE_DIRS} ${GnuTLS_INCLUDE_DIRS} ${Evsql_INCLUDE_DIRS} ${Lua51_INCLUDE_DIRS})
 
 # define our source code modules
 set (CORE_SOURCES error.c log.c)
 set (SOCK_SOURCES sock.c sock_tcp.c sock_gnutls.c sock_test.c line_proto.c)
 set (IRC_SOURCES irc_line.c irc_conn.c irc_net.c irc_chan.c chain.c irc_cmd.c irc_proto.c irc_client.c irc_user.c irc_queue.c)
+set (CONSOLE_SOURCES console.c lua_console.c lua_objs.c)
 
-set (NEXUS_SOURCES nexus.c ${CORE_SOURCES} ${SOCK_SOURCES} ${IRC_SOURCES} signals.c module.c config.c console.c)
+set (NEXUS_SOURCES nexus.c ${CORE_SOURCES} ${SOCK_SOURCES} ${IRC_SOURCES} ${CONSOLE_SOURCES} signals.c module.c config.c)
 set (TEST_SOURCES test.c ${CORE_SOURCES} ${SOCK_SOURCES} ${IRC_SOURCES})
 set (IRC_LOG_SOURCES irc_log.c)
 
 # define our libraries
 set (MODULE_LIBRARIES "dl")
-set (NEXUS_LIBRARIES ${LibEvent_LIBRARIES} ${GnuTLS_LIBRARIES} ${MODULE_LIBRARIES} "readline")
+set (NEXUS_LIBRARIES ${LibEvent_LIBRARIES} ${GnuTLS_LIBRARIES} ${MODULE_LIBRARIES} "readline" ${Lua51_LIBRARIES})
 
 # compiler flags
 set (CFLAGS "-Wall -Wextra -std=gnu99")
--- a/src/console.c	Tue Mar 31 15:41:24 2009 +0300
+++ b/src/console.c	Tue Mar 31 19:35:51 2009 +0300
@@ -1,5 +1,6 @@
 #include "console.h"
 
+#include <stdlib.h>
 #include <unistd.h>
 #include <stdio.h>
 #include <readline/readline.h>
@@ -30,8 +31,14 @@
 {
     struct console *console = &_console;
 
+    // XXX: EOF?
+
     // invoke the console callback
-    console->callbacks->on_line(line, console->cb_arg);
+    if (console->callbacks && console->callbacks->on_line)
+        console->callbacks->on_line(line, console->cb_arg);
+
+    // release the line
+    free(line);
 }
 
 err_t console_init (struct console **console_ptr, struct event_base *ev_base, const struct console_config *config,
@@ -42,9 +49,9 @@
     // check it's not already initialized
     assert(!console->initialized);
 
-    // store
-    console->callbacks = callbacks;
-    console->cb_arg = cb_arg;
+    // 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)
@@ -71,6 +78,12 @@
     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_destroy (struct console *console)
 {
     // remove the input event
--- a/src/console.h	Tue Mar 31 15:41:24 2009 +0300
+++ b/src/console.h	Tue Mar 31 19:35:51 2009 +0300
@@ -19,8 +19,12 @@
  * Callbacks for event-based actions
  */
 struct console_callbacks {
-    /** A line was read from the console */
-    void (*on_line) (char *line, void *arg);
+    /**
+     * A line was read from the console. 
+     *
+     * XXX: currently, line might be NULL on EOF, but this is probably a second callback
+     */
+    void (*on_line) (const char *line, void *arg);
 };
 
 /**
@@ -32,7 +36,9 @@
 };
 
 /**
- * The console state...
+ * The console state.
+ *
+ * You may replace the callbacks/cb_arg field with a new one at any time, using console_set_callbacks().
  */
 struct console {
     /** The input event */
@@ -49,12 +55,24 @@
 };
 
 /**
- * Initialize the console, setting up the TTY and input handler
+ * 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, struct error_info *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);
+
+/**
  * Deinitialize the console, restoring the TTY and releasing resources
  */
 void console_destroy (struct console *console);
--- a/src/error.h	Tue Mar 31 15:41:24 2009 +0300
+++ b/src/error.h	Tue Mar 31 19:35:51 2009 +0300
@@ -89,6 +89,11 @@
     /** config errors */
     _ERR_CONFIG     = 0x000b00,
     ERR_CONFIG_NAME,
+    
+    /** lua errors */
+    _ERR_LUA        = 0x000c00,
+    ERR_LUA_MEM,
+    ERR_LUA_SYNTAX,
 
     /** General errors */
     _ERR_GENERAL    = 0xffff00,
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lua_console.c	Tue Mar 31 19:35:51 2009 +0300
@@ -0,0 +1,112 @@
+#include "lua_console.h"
+#include "lua_objs.h"
+#include "log.h"
+
+#include <stdlib.h>
+
+#include <lua5.1/lualib.h>
+#include <lua5.1/lauxlib.h>
+
+static void lua_console_on_line (const char *line, void *arg)
+{
+    struct lua_console *lc = arg;
+    lua_State *L = lc->st;
+    int ret;
+
+    // ignore empty lines and EOF
+    if (!line || !(*line))
+        return;
+    
+    // load the line as a lua function
+    if ((ret = luaL_loadstring(L, line)))
+        goto error;
+
+    // execute it
+    if ((ret = lua_pcall(L, 0, 0, 0)))
+        goto error;
+
+    // XXX: display results?
+    
+error:
+    if (ret) {
+        const char *error = lua_tostring(L, -1);
+
+        switch (ret) {
+            case LUA_ERRSYNTAX:
+                log_error("syntax error: %s", error);
+                break;
+
+            case LUA_ERRRUN:
+                log_error("runtime error: %s", error);
+                break;
+
+            case LUA_ERRMEM:
+                log_error("memory allocation error: %s", error);
+                break;
+            
+            case LUA_ERRERR:
+                log_error("error handling error: %s", error);
+                break;
+
+            default:
+                log_error("unknown error: %s", error);
+                break;
+        };
+
+        lua_pop(L, 1);
+    }
+}
+
+static struct console_callbacks _console_callbacks = {
+    .on_line    = &lua_console_on_line,
+};
+
+err_t lua_console_create (struct lua_console **lc_ptr, struct console *console, struct nexus *nexus, struct error_info *err)
+{
+    struct lua_console *lc;
+
+    // allocate
+    if ((lc = calloc(1, sizeof(*lc))) == NULL)
+        return SET_ERROR(err, ERR_CALLOC);
+
+    // store
+    lc->console = console;
+
+    // set our console callbacks
+    console_set_callbacks(console, &_console_callbacks, lc);
+
+    // create the lua state
+    if ((lc->st = luaL_newstate()) == NULL)
+        JUMP_SET_ERROR(err, ERR_LUA_MEM);
+    
+    // we can then load the core libs
+    // XXX: we don't need all of these
+    // XXX: errors?
+    luaL_openlibs(lc->st);
+
+    // then our own things
+    if ((ERROR_CODE(err) = lua_objs_init(lc->st, nexus)))
+        goto error;
+
+    // ok
+    *lc_ptr = lc;
+
+    return SUCCESS;
+
+error:
+    // destroy
+    lua_console_destroy(lc);
+
+    return ERROR_CODE(err);
+}
+
+void lua_console_destroy (struct lua_console *lc)
+{
+    // close the lua stuff
+    lua_close(lc->st);
+    
+    // and the console
+    console_destroy(lc->console);
+
+    free(lc);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lua_console.h	Tue Mar 31 19:35:51 2009 +0300
@@ -0,0 +1,37 @@
+#ifndef LUA_CONSOLE_H
+#define LUA_CONSOLE_H
+
+/**
+ * @file
+ *
+ * An interactive lua console
+ */
+#include "nexus.h"
+#include "console.h"
+
+#include <lua5.1/lua.h>
+
+/**
+ * The lua console state
+ */
+struct lua_console {
+    /** The lowlevel line-based console */
+    struct console *console;
+
+    /** Our lua-state */
+    lua_State *st;
+};
+
+/**
+ * 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 *nexus, struct error_info *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/lua_objs.c	Tue Mar 31 19:35:51 2009 +0300
@@ -0,0 +1,83 @@
+#include "lua_objs.h"
+
+#include <lua5.1/lua.h>
+#include <lua5.1/lualib.h>
+#include <lua5.1/lauxlib.h>
+
+/**
+ * Our lua wrapper for irc_net
+ */
+struct lua_irc_net {
+    struct irc_net *net;
+};
+
+/**
+ * Wrapper for irc_client
+ */
+struct lua_client {
+    struct irc_client *client;
+};
+
+static int lua_client_quit (lua_State *L)
+{
+    struct lua_client *lua_client;
+    err_t err;
+   
+    // validate the lua_client arg
+    if ((lua_client = luaL_checkudata(L, 1, "evirc.client")) == NULL)
+        return luaL_argerror(L, 1, "`client` expected");
+    
+    // the message
+    const char *message = luaL_checkstring(L, 2);
+
+    // execute
+    if ((err = irc_client_quit(lua_client->client, message)))
+        return luaL_error(L, "irc_client_quit: %s", error_name(err));
+
+    // ok
+    return 0;
+}
+
+static const struct luaL_Reg lua_client_lib[] = {
+    {   "quit",     &lua_client_quit    },
+    {   NULL,       NULL                    }
+};
+
+/**
+ * Initializes a lua_irc_client wrapper for the given client in the give lua state. This registers a set of globals for
+ * 'client' and 'networks'.
+ */
+static err_t lua_client_init (lua_State *L, struct irc_client *client)
+{
+    // allocate the global "client" object
+    // XXX: mem errors?
+    struct lua_client *lua_client = lua_newuserdata(L, sizeof(*client));
+    
+    // push a new metatable to identify the client object
+    luaL_newmetatable(L, "evirc.client");
+
+    // set the metatable __index to itself
+    lua_pushvalue(L, -1);
+    lua_setfield(L, -1, "__index");
+
+    // register the methods to the metatable
+    luaL_register(L, NULL, lua_client_lib);
+
+    // set the client userdata's metatable
+    lua_setmetatable(L, -2);
+
+    // initialize it
+    lua_client->client = client;
+
+    // store it as a global
+    lua_setglobal(L, "client");
+
+    // ok
+    return SUCCESS;
+}
+
+err_t lua_objs_init (lua_State *L, struct nexus *nexus)
+{
+    // init the various bits
+    return lua_client_init(L, nexus->client);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lua_objs.h	Tue Mar 31 19:35:51 2009 +0300
@@ -0,0 +1,18 @@
+#ifndef LUA_OBJS_H
+#define LUA_OBJS_H
+
+/**
+ * @file
+ *
+ * Defines lua functions to access the various objects in a nexus
+ */
+#include "nexus.h"
+
+#include <lua5.1/lua.h>
+
+/**
+ * Registers our lua runtime objects into the given lua state
+ */
+err_t lua_objs_init (lua_State *L, struct nexus *nexus);
+
+#endif /* LUA_OBJS_H */
--- a/src/nexus.c	Tue Mar 31 15:41:24 2009 +0300
+++ b/src/nexus.c	Tue Mar 31 19:35:51 2009 +0300
@@ -236,33 +236,27 @@
     return SUCCESS;
 }
 
-static void on_line (char *line, void *arg)
-{
-    struct nexus *nexus = arg;
-
-    (void) nexus;
-
-    // just dump it
-    log_info("read line from console: '%s'", line);
-}
-
-static struct console_callbacks nexus_console_callbacks = {
-    .on_line        = &on_line,
-};
-
 /**
  * Open the console
  */
 static err_t apply_console (struct nexus *nexus, struct error_info *err)
 {
     struct console_config config = {
-        .prompt     = " > ",
+        .prompt     = "> ",
     };
 
     log_info("initializing the console");
     
-    // just init it
-    return console_init(&nexus->console, nexus->ev_base, &config, &nexus_console_callbacks, nexus, err);
+    // init the console
+    if (console_init(&nexus->console, nexus->ev_base, &config, NULL, NULL, err))
+        return ERROR_CODE(err);
+
+    // create the lua console on top of that
+    if (lua_console_create(&nexus->lua_console, nexus->console, nexus, err))
+        return ERROR_CODE(err);
+
+    // ok
+    return SUCCESS;
 }
 
 /**
@@ -335,8 +329,8 @@
     log_info("Quitting...");
 
     // destroy the console
-    if (ctx->console)
-        console_destroy(ctx->console);
+    if (ctx->lua_console)
+        lua_console_destroy(ctx->lua_console);
 
     // unload the modules
     modules_unload(ctx->modules);
--- a/src/nexus.h	Tue Mar 31 15:41:24 2009 +0300
+++ b/src/nexus.h	Tue Mar 31 19:35:51 2009 +0300
@@ -11,6 +11,7 @@
 #include <event2/event.h>
 #include "signals.h"
 #include "console.h"
+#include "lua_console.h"
 #include "module.h"
 #include "irc_client.h"
 
@@ -27,6 +28,9 @@
     /** Our console */
     struct console *console;
 
+    /** Our lua console */
+    struct lua_console *lua_console;
+
     /** Our loaded modules */
     struct modules *modules;