Move main.c to lua.c
authorMyhailo Danylenko <isbear@ukrpost.net>
Sun, 04 Apr 2010 16:43:49 +0300
changeset 103 2cb96eae301a
parent 102 f3d9d9e67ee4
child 104 626ff4f83519
Move main.c to lua.c
CMakeLists.txt
lua.c
main.c
--- a/CMakeLists.txt	Fri Apr 02 20:00:20 2010 +0300
+++ b/CMakeLists.txt	Sun Apr 04 16:43:49 2010 +0300
@@ -38,7 +38,7 @@
 				 ${MCABBER_LIBRARY_DIRS})
 
 ## Define targets
-add_library(lua MODULE main.c util.c)
+add_library(lua MODULE lua.c util.c)
 
 ## Set up compiler
 configure_file(config.h.in config.h ESCAPE_QUOTES)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lua.c	Sun Apr 04 16:43:49 2010 +0300
@@ -0,0 +1,1791 @@
+
+/* Copyright 2009 Myhailo Danylenko
+
+This file is part of mcabber-lua.
+
+mcabber-lua is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#include <glib.h>
+#include <gmodule.h>   // g_module_*
+#include <lua.h>
+#include <lauxlib.h>
+#include <lualib.h>
+#include <stdio.h>
+#include <stdlib.h>    // getenv
+#include <string.h>    // strcmp
+
+#include <mcabber/logprint.h>    // scr_log_print
+#include <mcabber/screen.h>      // scr_Beep, scr_WriteIncomingMessage
+#include <mcabber/hbuf.h>        // HBB_PREFIX_INFO
+#include <mcabber/commands.h>    // process_command, cmd_add, cmd_del
+#include <mcabber/xmpp.h>        // xmpp_getstatus, xmpp_getstatusmsg, lconnection
+#include <mcabber/xmpp_helper.h> // xmpp_add_feature, xmpp_del_feature
+#include <mcabber/roster.h>      // imstatus2char, foreach_buddy, buddy_*, current_buddy, BUDDATA, ROSTER_TYPE_*
+#include <mcabber/utils.h>       // from_utf8, jidtodisp
+#include <mcabber/hooks.h>       // hk_add_handler, hk_del_handler
+#include <mcabber/settings.h>    // settings_set, settings_del, settings_get
+#include <mcabber/compl.h>       // compl_new_category, compl_add_category_word, compl_del_category_word
+#include <mcabber/events.h>      // evs_*
+#include <mcabber/modules.h>     // module_info_t
+
+#include "config.h"
+#include "util.h"
+
+// module description
+static void mlua_init   (void);
+static void mlua_uninit (void);
+
+#ifdef LLM_LOG_HANDLER
+#define DESCRIPTION ( \
+	"Lua scripting interface\n" \
+	"Recognizes options lua_init_file, lua_hook_function and lua_lm_debug\n" \
+	"Provides command /lua" )
+#else
+#define DESCRIPTION ( \
+	"Lua scripting interface\n" \
+	"Recognizes options lua_init_file and lua_hook_function\n" \
+	"Provides command /lua" )
+#endif
+
+static module_info_t info_lua_experimental = {
+	.branch      = "experimental",
+	.api         = 12,
+	.version     = PROJECT_VERSION,
+	.description = DESCRIPTION,
+	.requires    = NULL,
+	.init        = mlua_init,
+	.uninit      = mlua_uninit,
+	.next        = NULL,
+};
+
+module_info_t info_lua = {
+	.branch      = "dev",
+	.api         = 11,
+	.version     = PROJECT_VERSION,
+	.description = DESCRIPTION,
+	.requires    = NULL,
+	.init        = mlua_init,
+	.uninit      = mlua_uninit,
+	.next        = &info_lua_experimental,
+};
+
+// global lua state object, necessary for uninitialization function
+static lua_State *lua = NULL;
+
+// caller sould g_free result
+static char *mcabber_config_filename (const char *file)
+{
+	const char *home = getenv ("HOME");
+	if (!home)
+		return NULL;
+	return g_strconcat (home, "/.mcabber/", file ? file : "", NULL);
+}
+
+/// print
+/// Prints its arguments to log with default priority.
+/// A: something, ...
+static int lua_global_print (lua_State *L)
+{
+	int         top = lua_gettop (L);
+	int         i;
+	luaL_Buffer B;
+	luaL_buffinit (L, &B);
+	for (i = 1; i <= top; i++) {
+		int type = lua_type (L, i);
+		if (i > 1)
+			luaL_addchar (&B, '\t');
+		if (type == LUA_TSTRING) {
+			size_t len;
+			const char *str = lua_tolstring (L, i, &len);
+			luaL_addlstring (&B, str, len);
+		} else if (type == LUA_TNUMBER)
+			luaL_addstring (&B, lua_tostring (L, i)); // XXX: modifies
+		else if (type == LUA_TBOOLEAN) {
+			if (lua_toboolean (L, i))
+				 luaL_addstring (&B, "true");
+			else
+				luaL_addstring (&B, "false");
+		} else if (type == LUA_TNIL)
+			luaL_addstring (&B, "nil");
+		else {
+			char xbuf[9];
+			luaL_addstring (&B, luaL_typename (L, i));
+			luaL_addstring (&B, ": 0x");
+			snprintf (&xbuf[0], 9, "%08x", (int) lua_topointer (L, i));
+			luaL_addlstring (&B, xbuf, 8); // XXX
+		}
+	}
+	luaL_pushresult (&B);
+
+	scr_log_print (LPRINT_LOGNORM | LPRINT_NOTUTF8, lua_tostring (L, -1));
+	return 0;
+}
+
+/// dopath
+/// Loads lua file from default location.
+/// XXX: g_filename_from_utf8?
+/// A: string (filename, without ".lua")
+/// R: string (error message, optional)
+static int lua_global_dopath (lua_State *L)
+{
+	const char *name = luaL_checkstring (L, 1);
+	size_t      size = lua_objlen (L, 1);
+	char       *path;
+	int         ret = 0;
+	if (size > 4 && !strncmp (name + size - 4, ".lua", 4))
+		path = mcabber_config_filename (name);
+	else {
+		char *fname = g_strconcat (name, ".lua", NULL);
+		path = mcabber_config_filename (fname);
+		g_free (fname);
+	}
+
+	if ((ret = luaL_loadfile (L, path)))
+		scr_log_print (LPRINT_LOGNORM, "lua: Unable to compile file %s: %s", path, lua_tostring (L, -1));
+	else if ((ret = lua_pcall (L, 0, LUA_MULTRET, 0)))
+		scr_log_print (LPRINT_LOGNORM, "lua: Runtime error in file %s: %s", path, lua_tostring (L, -1));
+	g_free (path);
+
+	if (ret)
+		return 1;
+	else
+		return 0;
+}
+
+/// yes or no ansvers
+/// G:
+static const string2enum_t lua_yesno[] = {
+	{ "1",       1 },
+	{ "0",       0 },
+	{ "enable",  1 },
+	{ "disable", 0 },
+	{ "true",    1 },
+	{ "false",   0 },
+	{ "on",      1 },
+	{ "off",     0 },
+	{ "yes",     1 },
+	{ "no",      0 },
+	{ NULL,     -1 },
+};
+
+/// main.yesno
+/// According to yes or no ansvers returns true or false.
+/// If ansver is not recognized, returns nil.
+/// A: anything (string expected)
+/// R: boolean or nil
+static int lua_main_yesno (lua_State *L)
+{
+	int type = lua_type (L, 1);
+	if (type == LUA_TSTRING) {
+		int ret = luaL_checkenum (L, 1, lua_yesno);
+		if (ret == -1)
+			lua_pushnil (L);
+		else
+			lua_pushboolean (L, ret);
+	} else if (type == LUA_TNUMBER)
+		lua_pushboolean (L, lua_tointeger (L, 1));
+	else if (type != LUA_TBOOLEAN)
+		lua_pushnil (L);
+	return 1;
+}
+
+/// log print type
+/// G:
+static const string2enum_t lua_lprint[] = {
+	{ "normal",  LPRINT_NORMAL  },
+	{ "log",     LPRINT_LOG     },
+	{ "debug",   LPRINT_DEBUG   },
+	{ "notutf0", LPRINT_NOTUTF8 },
+	{ NULL,      0              },
+};
+
+/// roster type
+/// G:
+static const string2enum_t lua_roster_type[] = {
+	{ "user",    ROSTER_TYPE_USER    },
+	{ "group",   ROSTER_TYPE_GROUP   },
+	{ "agent",   ROSTER_TYPE_AGENT   },
+	{ "room",    ROSTER_TYPE_ROOM    },
+	{ "special", ROSTER_TYPE_SPECIAL },
+	{ NULL,      0                   },
+};
+
+/// main.log
+/// Prints message to log.
+/// Note: most likely you need notutf8 flag enabled.
+/// A: log print type, message, message...
+static int lua_main_log (lua_State *L)
+{
+	int type = luaL_checkenum_multi (L, 1, lua_lprint);
+	lua_concat (L, lua_gettop (L) - 1);
+	scr_log_print (type, lua_tostring (L, -1));
+	return 0;
+}
+
+// expects table on top
+static void lua_options_callback (char *key, char *value, lua_State *L)
+{
+	char *loc = from_utf8 (key);
+	lua_pushstring (L, loc);
+	g_free (loc);
+	loc = from_utf8 (value);
+	lua_pushstring (L, loc);
+	g_free (loc);
+	lua_settable (L, -3);
+}
+
+/// main.option
+/// Sets or gets value of mcabber option.
+/// You can specify nil as a value to delete option.
+/// If you omit option name, it returns hash table of all options.
+/// A: string (option name, optional), string (value, optional)
+/// R: string (value, optional)
+static int lua_main_option (lua_State *L)
+{
+	int top = lua_gettop (L);
+	if (top > 0) {
+		char *name = to_utf8 (luaL_checkstring (L, 1));
+		if (top > 1) { // Set
+			if (lua_type (L, 2) == LUA_TNIL) // Unset
+				settings_del (SETTINGS_TYPE_OPTION, name);
+			else { // Set
+				char *value = to_utf8 (luaL_checkstring (L, 2));
+				settings_set (SETTINGS_TYPE_OPTION, name, value);
+				g_free (value);
+			}
+			g_free (name);
+			return 0;
+		} else { // Get
+			char *value = from_utf8 (settings_get (SETTINGS_TYPE_OPTION, name));
+			if (value) {
+				lua_pushstring (L, value);
+				g_free (value);
+			} else
+				lua_pushnil (L);
+			g_free (name);
+			return 1;
+		}
+	} else { // List
+		lua_newtable (L);
+		settings_foreach (SETTINGS_TYPE_OPTION, (void (*)(char *key, char *val, void *ud)) lua_options_callback, L);
+		return 1;
+	}
+}
+
+/// main.alias
+/// Sets or gets alias.
+/// You can specify nil as a command to delete alias.
+/// If you omit alias name, it will return hash table of all aliases.
+/// A: string (alias name, optional), string (command, optional)
+/// R: string (command, optional)
+static int lua_main_alias (lua_State *L)
+{
+	int top = lua_gettop (L);
+	if (top > 0) {
+		char *name = to_utf8 (luaL_checkstring (L, 1));
+		if (top > 1) { // Set
+			if (lua_type (L, 2) == LUA_TNIL) { // Unset
+				settings_del (SETTINGS_TYPE_ALIAS, name);
+				compl_del_category_word (COMPL_CMD, name);
+			} else { // Set
+				char *value = to_utf8 (luaL_checkstring (L, 2));
+				if (!settings_get (SETTINGS_TYPE_ALIAS, name))
+					compl_add_category_word (COMPL_CMD, name);
+				settings_set (SETTINGS_TYPE_ALIAS, name, value);
+				g_free (value);
+			}
+			g_free (name);
+			return 0;
+		} else { // Get
+			char *value = from_utf8 (settings_get (SETTINGS_TYPE_ALIAS, name));
+			if (value) {
+				lua_pushstring (L, value);
+				g_free (value);
+			} else
+				lua_pushnil (L);
+			g_free (name);
+			return 1;
+		}
+	} else { // List
+		lua_newtable (L);
+		settings_foreach (SETTINGS_TYPE_ALIAS, (void (*)(char *key, char *val, void *ud)) lua_options_callback, L);
+		return 1;
+	}
+}
+
+/// main.bind
+/// Sets or gets binding.
+/// You can specify nil as a command to unbind key.
+/// If you omit keycode, it will return hash table of all bindings.
+/// A: string (keycode, optional), string (command, optional)
+/// R: string (command, optional)
+static int lua_main_binding (lua_State *L)
+{
+	int top = lua_gettop (L);
+	if (top > 0) {
+		// just to be sure...
+		char *name = to_utf8 (luaL_checkstring (L, 1));
+		if (top > 1) { // Set
+			if (lua_type (L, 2) == LUA_TNIL) // Unset
+				settings_del (SETTINGS_TYPE_BINDING, name);
+			else { // Set
+				char *value = to_utf8 (luaL_checkstring (L, 2));
+				settings_set (SETTINGS_TYPE_BINDING, name, value);
+				g_free (value);
+			}
+			g_free (name);
+			return 0;
+		} else { // Get
+			char *value = from_utf8 (settings_get (SETTINGS_TYPE_BINDING, name));
+			if (value) {
+				lua_pushstring (L, value);
+				g_free (value);
+			} else
+				lua_pushnil (L);
+			g_free (name);
+			return 1;
+		}
+	} else { // List
+		lua_newtable (L);
+		settings_foreach (SETTINGS_TYPE_BINDING, (void (*)(char *key, char *val, void *ud)) lua_options_callback, L);
+		return 1;
+	}
+}
+
+/// main.fileoption
+/// Gets option, expanding it as filename.
+/// A: string (option name)
+/// R: string (expanded option value) or nil
+static int lua_main_fileoption (lua_State *L)
+{
+	char *fname = expand_filename (settings_opt_get (luaL_checkstring (L, 1)));
+	if (fname) {
+		lua_pushstring (L, fname);
+		g_free (fname);
+	} else
+		lua_pushnil (L);
+	return 1;
+}
+
+#ifdef LLM_CONNECTION_ENABLE
+/// main.connection
+/// Returns lightuserdata of mcabber's loudmouth connection.
+/// This can be very useful with lua-loudmouth, and not much otherwise.
+/// R: lightuserdata or nil
+static int lua_main_connection (lua_State *L)
+{
+	if (xmpp_is_online ())
+		lua_pushlightuserdata (L, lconnection);
+	else
+		lua_pushnil (L);
+	return 1;
+}
+#endif
+
+/// main.print_info
+/// Prints a system message to buddy's window.
+/// A: string (jid), string (message)
+static int lua_main_print_info (lua_State *L)
+{
+	char *jid  = to_utf8 (luaL_checkstring (L, 1));
+	char *to   = jidtodisp (jid);
+	char *mesg = to_utf8 (luaL_checkstring (L, 2));
+	scr_write_incoming_message (to, mesg, 0, HBB_PREFIX_INFO, 0);
+	g_free (mesg);
+	g_free (to);
+	g_free (jid);
+	return 0;
+}
+
+/// main.beep
+/// Beeps with system speaker.
+static int lua_main_beep (lua_State *L)
+{
+	scr_beep ();
+	return 0;
+}
+
+/// main.run
+/// Runs specified mcabber command.
+/// A: string
+static int lua_main_run (lua_State *L)
+{
+	process_command (luaL_checkstring (L, 1), TRUE);
+	return 0;
+}
+
+/// main.status
+/// Returns your current status.
+/// R: string (status letter), string (status message)
+static int lua_main_status (lua_State *L)
+{
+	char *sm = from_utf8 (xmpp_getstatusmsg ());
+	lua_pushlstring (L, &imstatus2char[xmpp_getstatus ()], 1);
+	lua_pushstring (L, sm);
+	g_free (sm);
+	return 2;
+}
+
+// expects table on top
+static void lua_rosterlist_callback (gpointer buddy, lua_State *L)
+{
+	char *jid = from_utf8 (buddy_getjid (buddy));
+	lua_pushnumber (L, lua_objlen (L, -1) + 1);
+	lua_pushstring (L, jid);
+	lua_settable (L, -3);
+	g_free (jid);
+}
+
+/// main.roster
+/// Returns array of jids of buddies in roster.
+/// R: table
+static int lua_main_roster (lua_State *L)
+{
+	lua_newtable (L);
+	foreach_buddy (ROSTER_TYPE_USER|ROSTER_TYPE_AGENT|ROSTER_TYPE_ROOM, (void (*) (gpointer buddy, void *data)) lua_rosterlist_callback, L);
+	return 1;
+}
+
+/// main.current_buddy
+/// Returns jid of current selected buddy or sets current buddy to buddy with specified jid.
+/// A: string (optional)
+/// R: string (optional)
+static int lua_main_current_buddy (lua_State *L)
+{
+	if (lua_gettop (L) > 0) { // Set
+		// XXX: we need not convert to utf, RS works on jids/names in locale charset,
+		// but will jidtodisp always correctly work on such tings?
+		char *jid = jidtodisp (luaL_checkstring (L, 1));
+		scr_roster_search (jid);
+		g_free (jid);
+		return 0;
+	} else { // Get
+		char *jid = from_utf8 (buddy_getjid (BUDDATA (current_buddy)));
+		lua_pushstring (L, jid);
+		g_free (jid);
+		return 1;
+	}
+}
+
+/// main.full_jid
+/// Returns full jid (with current resource) of specified buddy (or current, if not specified).
+/// Note, that if there are no resources online, it will return just what it got.
+/// A: string (jid, optional)
+/// R: string (jid)
+static int lua_main_full_jid (lua_State *L)
+{
+	GList  *buddy;
+	GSList *resources;
+	GSList *resource;
+	if (lua_gettop (L) > 0) {
+		char *jid = from_utf8 (luaL_checkstring (L, 1));
+		buddy = buddy_search_jid (jid);
+		g_free (jid);
+	} else
+		buddy = current_buddy;
+	if (!buddy)
+		return 0;
+	resources = buddy_getresources (BUDDATA (buddy));
+	if (!resources) {
+		char *loc = from_utf8 (buddy_getjid (BUDDATA (buddy)));
+		lua_pushstring (L, loc);
+		g_free (loc);
+	} else {
+		char *jid = from_utf8 (buddy_getjid (BUDDATA (buddy)));
+		char *res = from_utf8 (g_slist_last (resources)->data);
+		lua_pushfstring (L, "%s%c%s", jid, JID_RESOURCE_SEPARATOR, res);
+		for (resource = resources; resource; resource = g_slist_next (resource))
+			g_free (resource->data);
+		g_slist_free (resources);
+		g_free (jid);
+		g_free (res);
+	}
+	return 1;
+}
+
+typedef struct {
+	lua_State *L;
+	gpointer   buddy;
+} lua_state_and_buddy_t; // :)
+
+/// resources table
+/// Hash table with resource name as keys and another hash tables as values.
+/// Inner tables contain resource-specific information: priority, status and message.
+static void lua_buddy_resources_callback (gpointer resource, lua_state_and_buddy_t *d)
+{
+	char *loc = from_utf8 (resource);
+	lua_pushstring (d->L, loc);
+	g_free (loc);
+	lua_createtable (d->L, 0, 3);
+	lua_pushstring (d->L, "priority");
+	lua_pushnumber (d->L, buddy_getresourceprio (d->buddy, resource));
+	lua_settable   (d->L, -3);
+	lua_pushstring  (d->L, "status");
+	lua_pushlstring (d->L, &imstatus2char[buddy_getstatus (d->buddy, resource)], 1);
+	lua_settable    (d->L, -3);
+	lua_pushstring (d->L, "message");
+	loc = from_utf8 (buddy_getstatusmsg (d->buddy, resource));
+	lua_pushstring (d->L, loc);
+	g_free (loc);
+	lua_settable   (d->L, -3);
+	lua_settable (d->L, -3);
+	g_free (resource);
+}
+
+/// main.buddy_info
+/// Returns a hash table with information on specified buddy.
+/// Table contains fields type, name, onserver and resources (which points to resources table).
+/// A: string (jid)
+/// R: table
+static int lua_main_buddy_info (lua_State *L)
+{
+	char                  *loc   = to_utf8 (luaL_checkstring (L, 1));
+	char                  *jid   = jidtodisp (loc);
+	GSList                *buddy = roster_find (jid, jidsearch, ROSTER_TYPE_USER|ROSTER_TYPE_AGENT|ROSTER_TYPE_ROOM);
+	GSList                *resources;
+	lua_state_and_buddy_t  snb;
+	g_free (jid);
+	g_free (loc);
+
+	if (!buddy) {
+		lua_pushnil (L);
+		return 1;
+	}
+
+	lua_createtable (L, 0, 3);
+	lua_pushstring (L, "type");
+	luaL_pushenum  (L, buddy_gettype (BUDDATA (buddy)), lua_roster_type);
+	lua_settable   (L, -3);
+	lua_pushstring (L, "name");
+	loc = from_utf8 (buddy_getname (BUDDATA (buddy)));
+	lua_pushstring (L, loc);
+	g_free (loc);
+	lua_settable   (L, -3);
+	lua_pushstring  (L, "onserver");
+	lua_pushboolean (L, buddy_getonserverflag (BUDDATA (buddy)));
+	lua_settable    (L, -3);
+	lua_pushstring  (L, "resources");
+	lua_createtable (L, 0, 0);
+	snb.L     = L;
+	snb.buddy = BUDDATA (buddy);
+	resources = buddy_getresources (BUDDATA (buddy));
+	g_slist_foreach (buddy_getresources (BUDDATA (buddy)), (GFunc) lua_buddy_resources_callback, &snb);
+	g_slist_free (resources);
+	lua_settable (L, -3);
+	
+	return 1;
+}
+
+// XMPP DISCO FEATURES
+
+GSList *lua_added_features = NULL;
+
+/// main.add_feature
+/// Adds xmlns to disco#info features list.
+/// A: string (xmlns)
+static int lua_main_add_feature (lua_State *L)
+{
+	char *xmlns = to_utf8 (luaL_checkstring (L, 1));
+	xmpp_add_feature (xmlns);
+	lua_added_features = g_slist_prepend (lua_added_features, xmlns);
+	return 0;
+}
+
+/// main.del_feature
+/// Removes xmlns from disco#info features list.
+/// A: stirng (xmlns)
+static int lua_main_del_feature (lua_State *L)
+{
+	char   *xmlns = to_utf8 (luaL_checkstring (L, 1));
+	GSList *el    = g_slist_find_custom (lua_added_features, xmlns, (GCompareFunc) strcmp);
+	xmpp_del_feature (xmlns);
+	if (el) {
+		g_free (el->data);
+		lua_added_features = g_slist_delete_link (lua_added_features, el);
+	}
+	return 0;
+}
+
+// MCABBER EVENTS
+
+/// event context
+/// Enum, indicating what exactly caused event function firing.
+/// G:
+static const string2enum_t lua_event_context[] = {
+	{ "timeout", EVS_CONTEXT_TIMEOUT  },
+	{ "cancel",  EVS_CONTEXT_CANCEL   },
+	{ "reject",  EVS_CONTEXT_REJECT   },
+	{ "accept",  EVS_CONTEXT_ACCEPT   },
+	{ NULL,      0                    },
+};
+
+typedef struct {
+	lua_State *L;
+	int        reference;
+	int        evid;
+} lua_event_callback_t;
+
+static GSList *lua_events = NULL;
+
+static void lua_event_callback_destroy_notify (gpointer udata)
+{
+	lua_event_callback_t *cb = udata;
+
+	luaL_unref (cb -> L, LUA_REGISTRYINDEX, cb->reference);
+	luaL_unref (cb -> L, LUA_REGISTRYINDEX, cb->evid);
+	luaL_free  (cb -> L, cb);
+}
+
+/// event function
+/// Function to be called, when some event state change occurs
+/// A: event context, string (event args)
+/// R: boolean (if event shoud be preserved)
+static gboolean lua_event_callback (guint context, const gchar *arg, gpointer userdata)
+{
+	lua_event_callback_t *cb = userdata;
+
+	lua_rawgeti (cb->L, LUA_REGISTRYINDEX, cb->reference);
+	luaL_pushenum (cb->L, context, lua_event_context);
+	lua_pushstring (cb -> L, arg);
+	if (lua_pcall (cb->L, 2, 1, 0)) {
+		scr_log_print (LPRINT_LOGNORM, "lua: Event callback execution error: %s", lua_tostring (cb->L, -1));
+		lua_pop (cb->L, 1);
+	}
+
+	if (lua_toboolean (cb -> L, -1))
+		return TRUE;
+	else {
+		lua_events = g_slist_remove (lua_events, cb); // XXX
+		return FALSE;
+	}
+}
+
+/// main.event
+/// Creates new event. If called without arguments, returns event id list.
+/// A: event function (optional), string (event id), string (description, optional), integer (expiration timeout, optional)
+/// R: string (event id) or nothing (creation error) or table (list of event names)
+static int lua_main_event (lua_State *L)
+{
+	int top = lua_gettop (L);
+	if (top > 0) { // Create
+		lua_event_callback_t *cb;
+		const char           *evid    = NULL;
+		int                   timeout = 0;
+		const char           *desc    = NULL;
+		luaL_argcheck (L, lua_type (L, 1) == LUA_TFUNCTION, 1, "event function expected");
+
+		if (top > 1) {
+			evid = luaL_checkstring (L, 2);
+			if (top > 2) {
+				timeout = luaL_checkinteger (L, 3);
+				if (top > 2) {
+					desc = luaL_checkstring (L, 4);
+					lua_pop (L, 3);
+				} else
+					lua_pop (L, 2);
+			} else
+				lua_pop (L, 1);
+		}
+
+		lua_pushvalue (L, 1); // XXX
+		cb              = luaL_malloc (L, sizeof (lua_event_callback_t));
+		cb -> L         = L;
+		cb -> reference = luaL_ref (L, LUA_REGISTRYINDEX);
+		cb -> evid      = LUA_NOREF;
+		lua_events      = g_slist_prepend (lua_events, cb);
+
+		evid = evs_new (desc, evid, timeout, lua_event_callback, cb, lua_event_callback_destroy_notify);
+		if (!evid) {
+			lua_events = g_slist_remove (lua_events, cb); // XXX
+			return 0;
+		}
+
+		lua_pushstring (L, evid);
+		lua_pushvalue (L, -1);
+		cb -> evid = luaL_ref (L, LUA_REGISTRYINDEX); // XXX
+		return 1;
+
+	} else { // List
+		GSList *events = evs_geteventslist ();
+		GSList *event;
+
+		lua_newtable (L);
+		for (event = events; event; event = g_slist_next (event)) {
+			lua_pushstring (L, event->data);
+			luaL_ref (L, -2);
+		}
+		g_slist_free (events);
+
+		return 1;
+	}
+}
+
+// MCABBER COMMANDS
+
+/// completion type
+/// Built-it completion types can be specified as string, instead of id.
+/// G:
+static string2enum_t lua_completion_type[] = { // not const,  we need to modify yesno
+	{ "cmd",       COMPL_CMD       },
+	{ "jid",       COMPL_JID       },
+	{ "urljid",    COMPL_URLJID    },
+	{ "name",      COMPL_NAME      },
+	{ "status",    COMPL_STATUS    },
+	{ "filename",  COMPL_FILENAME  },
+	{ "roster",    COMPL_ROSTER    },
+	{ "buffer",    COMPL_BUFFER    },
+	{ "group",     COMPL_GROUP     },
+	{ "groupname", COMPL_GROUPNAME },
+	{ "multiline", COMPL_MULTILINE },
+	{ "room",      COMPL_ROOM      },
+	{ "resource",  COMPL_RESOURCE  },
+	{ "auth",      COMPL_AUTH      },
+	{ "request",   COMPL_REQUEST   },
+	{ "events",    COMPL_EVENTS    },
+	{ "eventsid",  COMPL_EVENTSID  },
+	{ "pgp",       COMPL_PGP       },
+	{ "color",     COMPL_COLOR     },
+	{ "otr",       COMPL_OTR       },
+	{ "ortpolicy", COMPL_OTRPOLICY },
+	{ "yesno",     0               },
+	{ NULL,        0               },
+};
+#define MLUA_YESNO_POS ( 21 )
+
+typedef struct {
+	int        reference;
+	int        parse_args;
+	lua_State *L;
+} lua_command_callback_t;
+
+static GSList *lua_added_commands = NULL;
+
+static GSList *lua_added_categories = NULL;
+
+// returns true if string contains errors - unclosed quotes or unvalued option
+/// command arguments table
+/// It can parse barewords (with escapes), double-quoted strings (with escapes), single-quoted strings (without escapes), options and arguments.
+/// Arguments are separated only by whitespace, so, consequential quoted strings or barewords are one argument.
+/// This strings are equal:
+/// * ab\ cd\'e\\f\"
+/// * "ab cd'e\\f\""
+/// * 'ab cd'\''e\f"'
+/// * ab" cd'"'e\f"'
+/// Returned table have option names as keys, option values as values, and arguments as sequential members. -- option is supported.
+/// Example: "-t jid -m 9 -- -aa bb cc" will result in { t = 'jid', m = 9, '-aa', 'bb', 'cc' }
+/// Implementation notes:
+/// * All options should be before any arguments. First non-option argument disables options recognizing.
+/// * EOL is a cutting edge, that can cut much earlier, than you expect. Non-closed quoted strings lose leading quote and option without value loses its leading minus.
+/// * Escape character just before EOL is preserved.
+static int luaL_pushargs (lua_State *L, const char *args)
+{
+	const char  *p       = args;
+	luaL_Buffer  buf;
+	int          option  = 0;
+	int          options = 1;
+
+	lua_newtable (L);
+	luaL_buffinit (L, &buf);
+	while (*p) {
+		if (*p == ' ') {
+			++p;
+			continue;
+		}
+		if (*p == '"') { // soft quote
+			const char *start = ++p;
+			while (*p) {
+				if (*p == '\\') { // escape symbol
+					luaL_addlstring (&buf, start, p - start);
+					start = ++p;
+					if (*p) // skip symbol
+						++p;
+					else // add last \ in line
+						luaL_addchar (&buf, '\\');
+				} else if (*p == '"') // quotation end
+					break;
+				else
+					++p;
+			}
+			luaL_addlstring (&buf, start, p - start); // XXX: eats quote on eol
+			if (*p)
+				++p;
+			else
+				return 1;
+		} else if (*p == '\'') { // no-escape quote
+			const char *start = ++p;
+			while (*p && *p != '\'')
+				p++;
+			luaL_addlstring (&buf, start, p - start); // XXX: eats quote on eol
+			if (*p)
+				++p;
+			else
+				return 1;
+		} else { // bareword
+			const char *start = p;
+			while (*p) {
+				if (*p == '\\') {
+					luaL_addlstring (&buf, start, p - start);
+					start = ++p;
+					if (*p) // skip symbol
+						++p;
+					else // add last \ in line
+						luaL_addchar (&buf, '\\');
+				} else if (*p == ' ' || *p == '\'' || *p == '"')
+					break;
+				else
+					++p;
+			}
+			luaL_addlstring (&buf, start, p - start);
+		}
+
+		if ((!*p) || *p == ' ') {
+			const char *result;
+			luaL_pushresult (&buf);
+
+			result = lua_tostring (L, -1);
+			if (options && !option && *result == '-') { // option
+				if (*(result+1) == '-' && !*(result+2)) { // end of options
+					lua_pop (L, 1);
+					options = 0;
+				} else { // option name
+					lua_pushstring (L, result + 1);
+					lua_remove (L, -2);
+					option = 1;
+				}
+			} else if (option) { // opion value
+				lua_settable (L, -3);
+				option = 0;
+			} else { // argument
+				options = 0;
+				luaL_ref (L, -2);
+			}
+
+			luaL_buffinit (L, &buf);
+		}
+	}
+
+	if (option) {
+		luaL_ref (L, -2); // XXX: eats minus on eol
+		return 1;
+	}
+
+	return 0;
+}
+
+/// command function
+/// Function to handle newly registered command.
+/// Argument type passed depends on how command is registered.
+/// A: string (arguments) or command arguments table
+static void lua_main_command_handler (char *args, lua_command_callback_t *cb)
+{
+	lua_rawgeti (cb->L, LUA_REGISTRYINDEX, cb->reference);
+
+	if (cb->parse_args)
+		luaL_pushargs (cb->L, args);
+	else
+		lua_pushstring (cb->L, args);
+
+	if (lua_pcall (cb->L, 1, 0, 0)) {
+		scr_log_print (LPRINT_LOGNORM, "lua: Command execution error: %s", lua_tostring (cb->L, -1));
+		lua_pop (cb->L, 1);
+	}
+}
+
+/// main.parse_args
+/// Function to parse command argument string to command arguments table.
+/// A: string
+/// R: table
+static int lua_main_parse_args (lua_State *L)
+{
+	luaL_pushargs (L, luaL_checkstring (L, 1));
+	return 1;
+}
+
+/// main.add_category
+/// Adds completion category.
+/// A: table (values are used as words for completion, optional)
+/// R: integer (category id, in fact completion type) or nil
+static int lua_main_add_category (lua_State *L)
+{
+	guint cid;
+
+	if (lua_gettop (L) > 0) {
+		luaL_argcheck (L, lua_type (L, 1) == LUA_TTABLE, 1, "table expected");
+		cid = compl_new_category ();
+		if (cid) {
+			lua_pushnil (L);
+			while (lua_next (L, 1)) {
+				char *word = to_utf8 (luaL_checkstring (L, -1));
+				if (word) {
+					compl_add_category_word (cid, word);
+					g_free (word);
+				}
+				lua_pop (L, 1);
+			}
+		}
+	} else
+		cid = compl_new_category ();
+
+	if (cid) {
+		lua_added_categories = g_slist_prepend (lua_added_categories, (gpointer) cid);
+		lua_pushinteger (L, cid);
+	} else
+		lua_pushnil (L);
+
+	return 1;
+}
+
+/// main.del_category
+/// Removes completion category.
+/// A: integer (category id)
+static int lua_main_del_category (lua_State *L)
+{
+	guint cid = luaL_checkinteger (L, 1);
+	compl_del_category (cid);
+	lua_added_categories = g_slist_remove (lua_added_categories, (gpointer) cid);
+	return 0;
+}
+
+/// main.add_completion
+/// Adds word to a completion list.
+/// A: integer (completion group id), string (word)
+static int lua_main_add_completion (lua_State *L)
+{
+	guint  cid  = luaL_checkinteger (L, 1);
+	char  *word = to_utf8 (luaL_checkstring (L, 2)); // XXX
+	compl_add_category_word (cid, word);
+	g_free (word);
+	return 0;
+}
+
+/// main.del_completion
+/// Removes word from a completion list.
+/// A: integer (completion group id), string (word)
+static int lua_main_del_completion (lua_State *L)
+{
+	guint  cid  = luaL_checkinteger (L, 1);
+	char  *word = to_utf8 (luaL_checkstring (L, 2)); // XXX
+	compl_del_category_word (cid, word);
+	g_free (word);
+	return 0;
+}
+
+/// main.command
+/// Associates or breaks association between mcabber command name and lua function.
+/// To unregister command omit function argument.
+/// If you specify a third argument, table (even empty), function will return completion group id or nothing.
+/// You can also specify a string name (see completion type) instead of table, for non-builtin, you can just pass integer id.
+/// Note, that for now there are no way to unregister completion group, so, resources can be exausted easily.
+/// Also note, that it ignores keys in a completion table.
+/// A: string (command name), command function (optional), boolean (parse args flag, optional), table (completions, optional)/completion type (or integer comletion group id, optional)
+/// R: completion type (integer completion group id or string for builtin types, optional)
+static int lua_main_command (lua_State *L)
+{
+	const char             *name = luaL_checkstring (L, 1); // XXX: to_utf8? looks like no :/
+	lua_command_callback_t *cb;
+	int                     top  = lua_gettop (L);
+	if (top > 1) { // Register
+		guint cid = 0;
+		int parse = 0;
+		luaL_argcheck (L, lua_isfunction (L, 2), 2, "function expected");
+
+		if (top > 2) { // parse flag provided
+			parse = lua_toboolean (L, 3);
+
+			if (top > 3) { // Completions provided
+				if (lua_type (L, 4) == LUA_TTABLE) {
+					cid = compl_new_category ();
+					if (cid) {
+						lua_added_categories = g_slist_prepend (lua_added_categories, (gpointer) cid);
+						lua_pushnil (L);
+						while (lua_next (L, 4)) {
+							char *word = to_utf8 (luaL_checkstring (L, -1));
+							compl_add_category_word (cid, word);
+							lua_pop (L, 1);
+							g_free (word);
+						}
+					}
+				} else
+					cid = luaL_checkenum (L, 4, lua_completion_type);
+			}
+		}
+
+		cb = luaL_malloc (L, sizeof (lua_command_callback_t));
+		lua_pushvalue (L, 2);
+		cb->reference  = luaL_ref (L, LUA_REGISTRYINDEX);
+		cb->parse_args = parse;
+		cb->L          = L;
+		cmd_add (name, "", cid, 0, (void (*) (char *p)) lua_main_command_handler, cb);
+
+		lua_added_commands = g_slist_prepend (lua_added_commands, g_strdup (name));
+
+		if (cid) {
+			luaL_pushenum (L, cid, lua_completion_type);
+			return 1;
+		}
+	} else { // Unregister
+		GSList *el = g_slist_find_custom (lua_added_commands, name, (GCompareFunc) g_strcmp0);
+
+		cb = cmd_del (name);
+		if (cb) {
+			luaL_unref (cb->L, LUA_REGISTRYINDEX, cb->reference);
+			luaL_free (cb->L, cb);
+		}
+
+		if (el) {
+			g_free (el->data);
+			lua_added_commands = g_slist_delete_link (lua_added_commands, el);
+		}
+	}
+	return 0;
+}
+
+// TIMER
+
+typedef struct {
+	int        reference;
+	guint      source;
+	lua_State *L;
+} lua_timer_callback_t;
+
+static GSList *lua_timers = NULL;
+
+static void lua_timer_callback_destroy (lua_timer_callback_t *cb)
+{
+	luaL_unref (cb->L, LUA_REGISTRYINDEX, cb->reference);
+	lua_timers = g_slist_remove (lua_timers, (gpointer) cb->source);
+	luaL_free (cb->L, cb);
+}
+
+/// timer function
+/// Function, that will be called periodically until it returns false.
+/// R: boolean
+static gboolean lua_timer_callback (lua_timer_callback_t *cb)
+{
+	int ret;
+	lua_rawgeti (cb->L, LUA_REGISTRYINDEX, cb->reference);
+	if (lua_pcall (cb->L, 0, 1, 0)) {
+		scr_log_print (LPRINT_LOGNORM, "lua: Timer callback execution error: %s", lua_tostring (cb->L, -1));
+		lua_pop (cb->L, 1);
+		return FALSE;
+	}
+	ret = lua_toboolean (cb->L, -1);
+	lua_pop (cb->L, 1);
+	return ret;
+}
+
+/// main.timer
+/// Creates new timer function, that will be called periodically.
+/// A: integer (interval, seconds), timer function
+static int lua_main_timer (lua_State *L)
+{
+	int                   interval = luaL_checkint (L, 1);
+	guint                 source;
+	lua_timer_callback_t *cb;
+	luaL_argcheck (L, lua_isfunction (L, 2), 2, "function expected");
+
+	cb = luaL_malloc (L, sizeof (lua_timer_callback_t));
+	cb->reference = luaL_ref (L, LUA_REGISTRYINDEX);
+	cb->L         = L;
+
+	source = g_timeout_add_seconds_full (MLUA_SOURCE_PRIORITY, interval, (GSourceFunc) lua_timer_callback, cb, (GDestroyNotify) lua_timer_callback_destroy);
+	cb->source = source;
+	lua_timers = g_slist_prepend (lua_timers, (gpointer) source);
+
+	return 0;
+}
+
+// BACKGROUND PIPE READING
+
+typedef struct {
+	lua_State *L;
+	guint      source;
+	FILE      *fd;
+	int        reference;
+} lua_bgread_callback_t;
+
+static GSList *lua_bgreads = NULL;
+
+static gchar lua_bgread_buffer[MLUA_BGREAD_BUFFER];
+
+static void lua_bgread_callback_destroy (lua_bgread_callback_t *cb)
+{
+	luaL_unref (cb->L, LUA_REGISTRYINDEX, cb->reference);
+	pclose (cb->fd); // Not necessary?
+	lua_bgreads = g_slist_remove (lua_bgreads, (gpointer) cb->source);
+	luaL_free (cb->L, cb);
+}
+
+/// background reading function
+/// Function, that processes output from pipe in asynchroneous way.
+/// A: string (data) or nil (eof)
+/// R: boolean (false if reading should be terminated)
+static gboolean lua_bgread_callback (GIOChannel *source, GIOCondition condition, lua_bgread_callback_t *cb)
+{
+	int ret = TRUE;
+
+	if (condition | G_IO_IN) { // data
+		while (TRUE) {
+			gsize read = 0;
+			g_io_channel_read_chars (source, lua_bgread_buffer, MLUA_BGREAD_BUFFER, &read, NULL);
+			if (!read) // exhausted
+				break;
+
+			lua_rawgeti (cb->L, LUA_REGISTRYINDEX, cb->reference);
+			lua_pushlstring (cb->L, lua_bgread_buffer, read);
+			if (lua_pcall (cb->L, 1, 1, 0)) {
+				scr_log_print (LPRINT_LOGNORM, "lua: Bgread callback execution error: %s", lua_tostring (cb->L, -1));
+				lua_pop (cb->L, 1);
+				return FALSE;
+			}
+			ret = lua_toboolean (cb->L, -1);
+			lua_pop (cb->L, 1);
+			if (!ret) // enough
+				return FALSE;
+		}
+	}
+
+	if (condition & ~G_IO_IN) { // err or hup
+		lua_rawgeti (cb->L, LUA_REGISTRYINDEX, cb->reference);
+		lua_pushnil (cb->L);
+		if (lua_pcall (cb->L, 1, 1, 0)) {
+			scr_log_print (LPRINT_LOGNORM, "lua: Bgread callback execution error: %s", lua_tostring (cb->L, -1));
+			lua_pop (cb->L, 1);
+			return FALSE;
+		}
+		ret = lua_toboolean (cb->L, -1);
+		lua_pop (cb->L, 1);
+	}
+
+	return ret;
+}
+
+/// main.bgread
+/// Runs specified command and passes its output to a given function.
+/// A: string (command), background reading function
+static int lua_main_bgread (lua_State *L)
+{
+	const char            *command = luaL_checkstring (L, 1);
+	lua_bgread_callback_t *cb;
+	FILE                  *fd;
+	GIOChannel            *channel;
+	const char            *charset = NULL;
+	guint                  source;
+	luaL_argcheck (L, lua_isfunction (L, 2), 2, "function expected");
+
+	fd = popen (command, "r");
+	if (!fd) {
+		lua_pushstring (L, "Error opening pipe");
+		lua_error (L);
+	}
+
+	channel = g_io_channel_unix_new (fileno (fd));
+	// We, most likely, need this,
+	// But we cannot use this,
+	// It will block.
+	//if (!g_get_charset (&charset))
+	g_io_channel_set_encoding (channel, charset, NULL);
+	g_io_channel_set_buffered (channel, FALSE);
+	g_io_channel_set_flags (channel, G_IO_FLAG_NONBLOCK, NULL);
+	g_io_channel_set_close_on_unref (channel, TRUE);
+
+	cb = luaL_malloc (L, sizeof (lua_bgread_callback_t));
+	cb->reference = luaL_ref (L, LUA_REGISTRYINDEX);
+	cb->L         = L;
+	cb->fd        = fd;
+
+	source = g_io_add_watch_full (channel, MLUA_SOURCE_PRIORITY, G_IO_IN|G_IO_HUP|G_IO_ERR, (GIOFunc) lua_bgread_callback, cb, (GDestroyNotify) lua_bgread_callback_destroy);
+	cb->source = source;
+	lua_bgreads = g_slist_prepend (lua_bgreads, (gpointer) source);
+
+	// unref?
+
+	return 0;
+}
+
+// HOOK HANDLING
+
+typedef struct {
+	lua_State *L;       // lua environment for handler use
+	int        nameref; // reference to hook name string
+	int        cbref;   // reference to hook handler function
+	int        selfref; // self-reference to object
+	guint      hid;     // hook id for object destruction
+} lua_hook_t;
+
+/// hook handler result
+/// What to do with hook processing afterwards
+/// G:
+static const string2enum_t lua_hook_handler_result[] = {
+	{ "proceed", HOOK_HANDLER_RESULT_ALLOW_MORE_HANDLERS       },
+	{ "stop",    HOOK_HANDLER_RESULT_NO_MORE_HANDLER           },
+	{ "drop",    HOOK_HANDLER_RESULT_NO_MORE_HANDLER_DROP_DATA },
+	{ NULL,      HOOK_HANDLER_RESULT_ALLOW_MORE_HANDLERS       },
+};
+
+/// hook handler priority
+/// Feel free to specify just a number instead of some preset value.
+/// G:
+static const string2enum_t lua_hook_handler_priority[] = {
+	{ "high",      G_PRIORITY_HIGH         },
+	{ "default",   G_PRIORITY_DEFAULT      },
+	{ "idle-high", G_PRIORITY_HIGH_IDLE    },
+	{ "idle",      G_PRIORITY_DEFAULT_IDLE },
+	{ "low",       G_PRIORITY_LOW          },
+	{ NULL,        G_PRIORITY_DEFAULT      },
+};
+
+/// hook function
+/// Function to be called, when hook will be processsed.
+/// XXX: we can provide object as argument, but is this necessary?
+/// A: table (arguments hash)
+/// R: hook handler result
+static guint lua_hook_cb (const gchar *hookid, hk_arg_t *args, gpointer data)
+{
+	lua_hook_t *cb  = data;
+	hk_arg_t   *arg = args;
+	guint       ret = HOOK_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
+	lua_State  *L   = cb -> L;
+
+	if (cb -> cbref == LUA_NOREF)
+		return HOOK_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
+
+	lua_rawgeti (L, LUA_REGISTRYINDEX, cb -> cbref);
+	if (!lua_isfunction (L, -1)) {
+		lua_pop (L, 1);
+		return HOOK_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
+	}
+
+	lua_newtable (L);
+	lua_pushliteral (L, "hook");
+	lua_pushstring  (L, hookid);
+	lua_settable (L, -3);
+	while (arg->name != NULL) {
+		char *name  = from_utf8 (arg->name);
+		char *value = from_utf8 (arg->value);
+		lua_pushstring (L, name);
+		lua_pushstring (L, value);
+		lua_settable (L, -3);
+		g_free (name);
+		g_free (value);
+		arg++;
+	}
+
+	if (lua_pcall (L, 1, 1, 0)) {
+		scr_log_print (LPRINT_NORMAL, "lua: Error in hook handler: %s", lua_tostring (L, -1));
+		lua_pop (L, 1);
+	} else {
+		switch (lua_type (L, -1)) {
+		case LUA_TSTRING:
+		case LUA_TNUMBER:
+			ret = luaL_checkenum (L, -1, lua_hook_handler_result);
+			break;
+		default:
+			ret = HOOK_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
+			break;
+		}
+		lua_pop (L, 1);
+	}
+
+	return ret;
+}
+
+/// main.hook
+/// Installs hook handler, returns an object, that you need to keep until
+/// hook handling is no more needed.
+/// A: string (hook name), hook function, integer (priority, optional)
+/// R: userdata (hook object)
+static int lua_main_hook (lua_State *L)
+{
+	const char   *hook_name = luaL_checkstring (L, 1);
+	int           priority  = G_PRIORITY_DEFAULT;
+	lua_hook_t   *cb;
+
+	luaL_argcheck (L, lua_isfunction (L, 2), 2, "function expected");
+
+	if (lua_gettop (L) > 2)
+		priority = luaL_checkenum (L, 3, lua_hook_handler_priority);
+
+	cb = lua_newuserdata (L, sizeof (lua_hook_t));
+	luaL_getmetatable (L, "mcabber.hook");
+	lua_setmetatable (L, -2);
+
+	lua_pushvalue (L, -1);
+	cb -> selfref = luaL_ref (L, LUA_REGISTRYINDEX);
+	lua_pushvalue (L, 1);
+	cb -> nameref = luaL_ref (L, LUA_REGISTRYINDEX);
+	lua_pushvalue (L, 2);
+	cb -> cbref   = luaL_ref (L, LUA_REGISTRYINDEX);
+	cb -> L       = L;
+	cb -> hid     = hk_add_handler (lua_hook_cb, hook_name, priority, cb);
+
+	return 1;
+}
+
+static void lua_mcabber_unregister_hook (lua_State *L, lua_hook_t *cb)
+{
+	const char *name;
+
+	if (!cb -> hid || cb -> nameref == LUA_NOREF)
+		return;
+
+	lua_rawgeti (L, LUA_REGISTRYINDEX, cb -> nameref);
+	name = lua_tostring (L, -1);
+	if (name) {
+		hk_del_handler (name, cb -> hid);
+		cb -> hid = 0;
+	}
+
+	lua_pop (L, 1);
+}
+
+/// hook:del
+/// Unregisters given hook handler from mcabber. Object will be destroyed later.
+static int lua_mcabber_hook_del (lua_State *L)
+{
+	lua_hook_t *cb = luaL_checkudata (L, 1, "mcabber.hook");
+	luaL_argcheck (L, cb != NULL, 1, "mcabber hook object expected");
+	lua_mcabber_unregister_hook (L, cb);
+	if (cb -> selfref != LUA_NOREF) {
+		luaL_unref (L, LUA_REGISTRYINDEX, cb -> selfref);
+		cb -> selfref = LUA_NOREF;
+	}
+	return 0;
+}
+
+static int lua_mcabber_hook_gc (lua_State *L)
+{
+	lua_hook_t *cb = luaL_checkudata (L, 1, "mcabber.hook");
+	luaL_argcheck (L, cb != NULL, 1, "mcabber hook object expected");
+	lua_mcabber_unregister_hook (L, cb);
+	if (cb -> nameref != LUA_NOREF)
+		luaL_unref (L, LUA_REGISTRYINDEX, cb -> nameref);
+	if (cb -> cbref != LUA_NOREF)
+		luaL_unref (L, LUA_REGISTRYINDEX, cb -> cbref);
+	return 0;
+}
+
+static const luaL_Reg lua_mcabber_hook_reg_m[] = {
+	{ "del",  lua_mcabber_hook_del },
+	{ "__gc", lua_mcabber_hook_gc  },
+	{ NULL,   NULL                 },
+};
+
+static void lua_hook_init (lua_State *L)
+{
+	luaL_newmetatable (L, "mcabber.hook");
+	lua_pushvalue (L, -1);
+	lua_setfield (L, -2, "__index");
+	luaL_register (L, NULL, lua_mcabber_hook_reg_m);
+	lua_pop (L, 1);
+}
+
+#if 0
+// OPTION GUARDS
+
+GSList *lua_installed_guards = NULL;
+
+typedef struct {
+	lua_State *L;       // lua environment for handler use
+	int        nameref; // reference to key name string
+	int        cbref;   // reference to guard function
+//	int        objref;  // self_reference to object
+	guint      hid;     // hook id for object destruction
+} lua_guard_t;
+
+/// guard function
+/// Function to be called, when option changes it's value.
+/// Old option value is still accessible through main.option.
+/// A: string (key), string (new value)
+/// R: string (value to save in hash table)
+static gchar *lua_guard_cb (const gchar *key, const gchar *value)
+{
+	lua_guard_t *cb = ;// FIXME
+	lua_State   *L  = cb -> L;
+
+	if (cb -> cbref == LUA_NOREF)
+		return g_strdup (value);
+
+	lua_rawgeti (L, LUA_REGISTRYINDEX, cb -> cbref);
+	if (!lua_isfunction (L, -1)) {
+		lua_pop (L, 1);
+		return g_strdup (value);
+	}
+
+	lua_pushstring (L, key);
+	lua_psuhstring (L, value);
+
+	if (lua_pcall (L, 2, 1, 0)) {
+		scr_log_print (LPRINT_NORMAL, "lua: Error in hook handler: %s", lua_tostring (L, -1));
+		lua_pop (L, 1);
+		return g_strdup (value);
+	}
+
+	return g_strdup (lua_tostring (L, -1));
+}
+
+/// main.guard
+/// Installs option guard for given option. Returns guard object, that
+/// should be kept around as long, as guard is needed.
+/// A: string (option name), guard function
+/// R: userdata (guard object)
+static int lua_main_guard (lua_State *L)
+{
+	const char   *name      = luaL_checkstring (L, 1);
+	int           priority  = G_PRIORITY_DEFAULT;
+	lua_guard_t   *cb;
+
+	luaL_argcheck (L, lua_isfunction (L, 2), 2, "function expected");
+
+	cb = lua_newuserdata (L, sizeof (lua_guard_t));
+	luaL_getmetatable (L, "mcabber.guard");
+	lua_setmetatable (L, -2);
+
+	lua_pushvalue (L, 1);
+	cb -> nameref = luaL_ref (L, LUA_REGISTRYINDEX);
+	lua_pushvalue (L, 2);
+	cb -> cbref   = luaL_ref (L, LUA_REGISTRYINDEX);
+	cb -> L       = L;
+
+	settings_set_guard (name, lua_guard_cb)
+
+	return 1;
+}
+
+static void lua_mcabber_unregister_guard (lua_State *L, lua_guard_t *cb)
+{
+	const char *name;
+
+	if (cb -> nameref == LUA_NOREF)
+		return;
+
+	lua_rawgeti (L, LUA_REGISTRYINDEX, cb -> nameref);
+	name = lua_tostring (L, -1);
+	if (name) {
+		settings_del_guard (name);
+		luaL_unref (L, LUA_REGISTRYINDEX, cb -> nameref);
+		cb -> nameref = LUA_NOREF;
+	}
+
+	lua_pop (L, 1);
+}
+
+/// guard:del
+/// Unregisters given option guard from mcabber. Object will be destroyed later.
+static int lua_mcabber_guard_del (lua_State *L)
+{
+	lua_guard_t *cb = luaL_checkudata (L, 1, "mcabber.guard");
+	luaL_argcheck (L, cb != NULL, 1, "mcabber guard object expected");
+	lua_mcabber_unregister_guard (L, cb);
+	return 0;
+}
+
+static int lua_mcabber_guard_gc (lua_State *L)
+{
+	lua_hook_t *cb = luaL_checkudata (L, 1, "mcabber.guard");
+	luaL_argcheck (L, cb != NULL, 1, "mcabber guard object expected");
+	lua_mcabber_unregister_guard (L, cb);
+	if (cb -> cbref != LUA_NOREF)
+		luaL_unref (L, LUA_REGISTRYINDEX, cb -> cbref);
+	return 0;
+}
+
+static const luaL_Reg lua_mcabber_hook_reg_m[] = {
+	{ "del",  lua_mcabber_guard_del },
+	{ "__gc", lua_mcabber_guard_gc  },
+	{ NULL,   NULL                 },
+};
+
+static void lua_guard_init (lua_State *L)
+{
+	luaL_newmetatable (L, "mcabber.guard");
+	lua_pushvalue (L, -1);
+	lua_setfield (L, -2, "__index");
+	luaL_register (L, NULL, lua_mcabber_guard_reg_m);
+	lua_pop (L, 1);
+}
+
+static void lua_guard_uninit (lua_State *L)
+{
+}
+#endif
+
+// MAIN INITIALIZATION CODE
+
+#ifdef LLM_LOG_HANDLER
+// FIXME: this should not be here
+guint lua_lm_log_handler_id;
+
+void lua_lm_log_handler (const gchar *domain, GLogLevelFlags log_level, const gchar *message, gpointer ignore)
+{
+	if (settings_opt_get_int ("lua_lm_debug"))
+		scr_log_print (LPRINT_LOGNORM, "%s: %s", domain, message);
+}
+#endif
+
+static void do_lua(char *arg, lua_State *L)
+{
+	if (luaL_loadbuffer (L, arg, strlen (arg), "line")) {
+		scr_log_print (LPRINT_LOGNORM, "lua: Compilation error: %s", lua_tostring (L, -1));
+		lua_pop (L, 1);
+		return;
+	}
+ 
+	if (lua_pcall (L, 0, 0, 0)) {
+		scr_log_print (LPRINT_NORMAL, "lua: Runtime error: %s", lua_tostring(L, -1));
+		lua_pop (L, 1);
+		return;
+	}
+}
+
+static void *lua_alloc (void *ud, void *ptr, size_t osize, size_t nsize) {
+	if (nsize == 0) {
+		g_free (ptr);
+		return NULL;
+	} else
+		return g_realloc (ptr, nsize);
+}
+
+#define reg(NAME)                   \
+	{ #NAME, lua_main_##NAME },
+static const luaL_Reg lua_reg_main[] = {
+	reg ( yesno          ) 
+#ifdef LLM_CONNECTION_ENABLE
+	reg ( connection     ) 
+#endif
+	reg ( log            ) 
+	reg ( option         ) 
+	reg ( alias          ) 
+	reg ( binding        ) 
+	reg ( fileoption     ) 
+	reg ( add_feature    ) 
+	reg ( del_feature    ) 
+	reg ( event          )
+	reg ( parse_args     ) 
+	reg ( add_category   ) 
+	reg ( del_category   ) 
+	reg ( add_completion ) 
+	reg ( del_completion ) 
+	reg ( command        ) 
+	reg ( print_info     ) 
+	reg ( beep           ) 
+	reg ( run            ) 
+	reg ( status         ) 
+	reg ( roster         ) 
+	reg ( current_buddy  ) 
+	reg ( full_jid       ) 
+	reg ( buddy_info     ) 
+	reg ( timer          ) 
+	reg ( alias          ) 
+	reg ( binding        ) 
+	reg ( fileoption     ) 
+	reg ( add_feature    ) 
+	reg ( del_feature    ) 
+	reg ( event          )
+	reg ( parse_args     ) 
+	reg ( add_category   ) 
+	reg ( del_category   ) 
+	reg ( add_completion ) 
+	reg ( del_completion ) 
+	reg ( command        ) 
+	reg ( print_info     ) 
+	reg ( beep           ) 
+	reg ( run            ) 
+	reg ( status         ) 
+	reg ( roster         ) 
+	reg ( current_buddy  ) 
+	reg ( full_jid       ) 
+	reg ( buddy_info     ) 
+	reg ( timer          ) 
+	reg ( bgread         ) 
+	reg ( hook           )
+	{ NULL, NULL },
+};
+#undef reg
+
+const gchar *g_module_check_init (GModule *module)
+{
+	lua = lua_newstate (lua_alloc, NULL);
+	if (!lua)
+		return "Lua initialization error";
+	else
+		return NULL;
+}
+
+void g_module_unload (GModule *module)
+{
+	if (lua) {
+		lua_close (lua);
+		lua = NULL;
+	}
+}
+
+static void mlua_init (void)
+{
+	luaL_openlibs (lua);
+
+	luaL_register (lua, "main", lua_reg_main);
+	lua_pop (lua, 1); // XXX
+	lua_register (lua, "dopath", lua_global_dopath);
+	lua_register (lua, "print",  lua_global_print );
+
+	{
+		int cid = compl_new_category ();
+
+		if (cid) {
+			const string2enum_t *word = lua_yesno;
+			lua_completion_type[MLUA_YESNO_POS].value = cid;
+			lua_added_categories = g_slist_prepend (lua_added_categories, (gpointer) cid);
+			while (word->string) {
+				compl_add_category_word (cid, word->string);
+				++word;
+			}
+		}
+	}
+
+	cmd_add ("lua", "Evaluate lua string", 0, 0, (void (*) (char *p)) do_lua, lua);
+
+#ifdef LLM_LOG_HANDLER
+	// FIXME: this should not be here.
+	lua_lm_log_handler_id = g_log_set_handler ("lua-lm", G_LOG_LEVEL_MASK, (GLogFunc) lua_lm_log_handler, NULL);
+#endif
+
+	lua_hook_init (lua);
+
+	{
+		char *initfile = expand_filename (settings_opt_get ("lua_init_filename"));
+		
+		if (!initfile)
+			scr_log_print (LPRINT_LOGNORM, "lua: Cannot determine config file name");
+		else {
+			if (luaL_loadfile(lua, initfile)) {
+				scr_log_print (LPRINT_LOGNORM, "lua: Unable to compile rc file: %s", lua_tostring (lua, -1));
+				lua_pop (lua, 1);
+			} else if (lua_pcall (lua, 0, LUA_MULTRET, 0)) {
+				scr_log_print (LPRINT_LOGNORM, "lua: Runtime error in rc file: %s", lua_tostring(lua, -1));
+				lua_pop (lua, 1);
+			} else
+				scr_log_print (LPRINT_LOGNORM, "lua: Loaded %s", initfile);
+			g_free (initfile);
+		}
+	}
+
+	{
+		hk_arg_t args[] = {
+			{ NULL, NULL },
+		};
+		hk_run_handlers("hook-lua-start", args);
+	}
+}
+
+static void lua_events_cancel (gpointer data, gpointer ignore)
+{
+	lua_event_callback_t *cb = data;
+	const char *evid;
+	if (cb->evid == LUA_NOREF)
+		return;
+	lua_rawgeti (cb->L, LUA_REGISTRYINDEX, cb->evid);
+	evid = lua_tostring (cb ->L, -1);
+	evs_callback (evid, EVS_CONTEXT_CANCEL, "Module unloading");
+}
+
+static void lua_events_destroy (gpointer data, gpointer ignore)
+{
+	lua_event_callback_t *cb = data;
+	const char *evid;
+	if (cb->evid == LUA_NOREF)
+		return;
+	lua_rawgeti (cb->L, LUA_REGISTRYINDEX, cb->evid);
+	evid = lua_tostring (cb ->L, -1);
+	evs_del (evid);
+}
+
+static void lua_bgreads_destroy (guint source, gpointer ignore)
+{
+	g_source_remove (source);
+}
+
+static void lua_timers_destroy (guint source, gpointer ignore)
+{
+	g_source_remove (source);
+}
+
+static void lua_features_destroy (char *xmlns, gpointer ignore)
+{
+	xmpp_del_feature (xmlns);
+	g_free (xmlns);
+}
+
+static void lua_commands_destroy (char *name, gpointer ignore)
+{
+	lua_command_callback_t *cb = cmd_del (name);
+	if (cb) {
+		luaL_unref (cb->L, LUA_REGISTRYINDEX, cb->reference);
+		luaL_free (cb->L, cb);
+	}
+	g_free (name);
+}
+
+static void lua_categories_destroy (guint id, gpointer ignore)
+{
+	compl_del_category (id);
+}
+
+static void mlua_uninit (void)
+{
+	if (lua) {
+		hk_arg_t args[] = {
+			{ NULL, NULL },
+		};
+		hk_run_handlers ("hook-lua-quit", args);
+
+		// hook handlers will be unregistered upon objects destruction
+
+		g_slist_foreach (lua_bgreads, (GFunc) lua_bgreads_destroy, NULL);
+		g_slist_free (lua_bgreads);
+		lua_bgreads = NULL;
+
+		g_slist_foreach (lua_timers, (GFunc) lua_timers_destroy, NULL);
+		g_slist_free (lua_timers);
+		lua_timers = NULL;
+
+		g_slist_foreach (lua_events, (GFunc) lua_events_cancel, NULL);
+		g_slist_foreach (lua_events, (GFunc) lua_events_destroy, NULL);
+		g_slist_free (lua_events);
+		lua_events = NULL;
+
+		g_slist_foreach (lua_added_features, (GFunc) lua_features_destroy, NULL);
+		g_slist_free (lua_added_features);
+		lua_added_features = NULL;
+
+		g_slist_foreach (lua_added_commands, (GFunc) lua_commands_destroy, NULL);
+		g_slist_free (lua_added_commands);
+		lua_added_commands = NULL;
+
+		g_slist_foreach (lua_added_categories, (GFunc) lua_categories_destroy, NULL);
+		g_slist_free (lua_added_categories);
+		lua_added_categories = NULL;
+
+		cmd_del ("lua");
+
+		lua_close (lua);
+		lua = NULL;
+
+#ifdef LLM_LOG_HANDLER
+		// FIXME: shouldn't be here
+		g_log_remove_handler ("lua-lm", lua_lm_log_handler_id);
+#endif
+	}
+}
+
--- a/main.c	Fri Apr 02 20:00:20 2010 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1791 +0,0 @@
-
-/* Copyright 2009 Myhailo Danylenko
-
-This file is part of mcabber-lua.
-
-mcabber-lua is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 2 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program.  If not, see <http://www.gnu.org/licenses/>. */
-
-#include <glib.h>
-#include <gmodule.h>   // g_module_*
-#include <lua.h>
-#include <lauxlib.h>
-#include <lualib.h>
-#include <stdio.h>
-#include <stdlib.h>    // getenv
-#include <string.h>    // strcmp
-
-#include <mcabber/logprint.h>    // scr_log_print
-#include <mcabber/screen.h>      // scr_Beep, scr_WriteIncomingMessage
-#include <mcabber/hbuf.h>        // HBB_PREFIX_INFO
-#include <mcabber/commands.h>    // process_command, cmd_add, cmd_del
-#include <mcabber/xmpp.h>        // xmpp_getstatus, xmpp_getstatusmsg, lconnection
-#include <mcabber/xmpp_helper.h> // xmpp_add_feature, xmpp_del_feature
-#include <mcabber/roster.h>      // imstatus2char, foreach_buddy, buddy_*, current_buddy, BUDDATA, ROSTER_TYPE_*
-#include <mcabber/utils.h>       // from_utf8, jidtodisp
-#include <mcabber/hooks.h>       // hk_add_handler, hk_del_handler
-#include <mcabber/settings.h>    // settings_set, settings_del, settings_get
-#include <mcabber/compl.h>       // compl_new_category, compl_add_category_word, compl_del_category_word
-#include <mcabber/events.h>      // evs_*
-#include <mcabber/modules.h>     // module_info_t
-
-#include "config.h"
-#include "util.h"
-
-// module description
-static void mlua_init   (void);
-static void mlua_uninit (void);
-
-#ifdef LLM_LOG_HANDLER
-#define DESCRIPTION ( \
-	"Lua scripting interface\n" \
-	"Recognizes options lua_init_file, lua_hook_function and lua_lm_debug\n" \
-	"Provides command /lua" )
-#else
-#define DESCRIPTION ( \
-	"Lua scripting interface\n" \
-	"Recognizes options lua_init_file and lua_hook_function\n" \
-	"Provides command /lua" )
-#endif
-
-static module_info_t info_lua_experimental = {
-	.branch      = "experimental",
-	.api         = 12,
-	.version     = PROJECT_VERSION,
-	.description = DESCRIPTION,
-	.requires    = NULL,
-	.init        = mlua_init,
-	.uninit      = mlua_uninit,
-	.next        = NULL,
-};
-
-module_info_t info_lua = {
-	.branch      = "dev",
-	.api         = 11,
-	.version     = PROJECT_VERSION,
-	.description = DESCRIPTION,
-	.requires    = NULL,
-	.init        = mlua_init,
-	.uninit      = mlua_uninit,
-	.next        = &info_lua_experimental,
-};
-
-// global lua state object, necessary for uninitialization function
-static lua_State *lua = NULL;
-
-// caller sould g_free result
-static char *mcabber_config_filename (const char *file)
-{
-	const char *home = getenv ("HOME");
-	if (!home)
-		return NULL;
-	return g_strconcat (home, "/.mcabber/", file ? file : "", NULL);
-}
-
-/// print
-/// Prints its arguments to log with default priority.
-/// A: something, ...
-static int lua_global_print (lua_State *L)
-{
-	int         top = lua_gettop (L);
-	int         i;
-	luaL_Buffer B;
-	luaL_buffinit (L, &B);
-	for (i = 1; i <= top; i++) {
-		int type = lua_type (L, i);
-		if (i > 1)
-			luaL_addchar (&B, '\t');
-		if (type == LUA_TSTRING) {
-			size_t len;
-			const char *str = lua_tolstring (L, i, &len);
-			luaL_addlstring (&B, str, len);
-		} else if (type == LUA_TNUMBER)
-			luaL_addstring (&B, lua_tostring (L, i)); // XXX: modifies
-		else if (type == LUA_TBOOLEAN) {
-			if (lua_toboolean (L, i))
-				 luaL_addstring (&B, "true");
-			else
-				luaL_addstring (&B, "false");
-		} else if (type == LUA_TNIL)
-			luaL_addstring (&B, "nil");
-		else {
-			char xbuf[9];
-			luaL_addstring (&B, luaL_typename (L, i));
-			luaL_addstring (&B, ": 0x");
-			snprintf (&xbuf[0], 9, "%08x", (int) lua_topointer (L, i));
-			luaL_addlstring (&B, xbuf, 8); // XXX
-		}
-	}
-	luaL_pushresult (&B);
-
-	scr_log_print (LPRINT_LOGNORM | LPRINT_NOTUTF8, lua_tostring (L, -1));
-	return 0;
-}
-
-/// dopath
-/// Loads lua file from default location.
-/// XXX: g_filename_from_utf8?
-/// A: string (filename, without ".lua")
-/// R: string (error message, optional)
-static int lua_global_dopath (lua_State *L)
-{
-	const char *name = luaL_checkstring (L, 1);
-	size_t      size = lua_objlen (L, 1);
-	char       *path;
-	int         ret = 0;
-	if (size > 4 && !strncmp (name + size - 4, ".lua", 4))
-		path = mcabber_config_filename (name);
-	else {
-		char *fname = g_strconcat (name, ".lua", NULL);
-		path = mcabber_config_filename (fname);
-		g_free (fname);
-	}
-
-	if ((ret = luaL_loadfile (L, path)))
-		scr_log_print (LPRINT_LOGNORM, "lua: Unable to compile file %s: %s", path, lua_tostring (L, -1));
-	else if ((ret = lua_pcall (L, 0, LUA_MULTRET, 0)))
-		scr_log_print (LPRINT_LOGNORM, "lua: Runtime error in file %s: %s", path, lua_tostring (L, -1));
-	g_free (path);
-
-	if (ret)
-		return 1;
-	else
-		return 0;
-}
-
-/// yes or no ansvers
-/// G:
-static const string2enum_t lua_yesno[] = {
-	{ "1",       1 },
-	{ "0",       0 },
-	{ "enable",  1 },
-	{ "disable", 0 },
-	{ "true",    1 },
-	{ "false",   0 },
-	{ "on",      1 },
-	{ "off",     0 },
-	{ "yes",     1 },
-	{ "no",      0 },
-	{ NULL,     -1 },
-};
-
-/// main.yesno
-/// According to yes or no ansvers returns true or false.
-/// If ansver is not recognized, returns nil.
-/// A: anything (string expected)
-/// R: boolean or nil
-static int lua_main_yesno (lua_State *L)
-{
-	int type = lua_type (L, 1);
-	if (type == LUA_TSTRING) {
-		int ret = luaL_checkenum (L, 1, lua_yesno);
-		if (ret == -1)
-			lua_pushnil (L);
-		else
-			lua_pushboolean (L, ret);
-	} else if (type == LUA_TNUMBER)
-		lua_pushboolean (L, lua_tointeger (L, 1));
-	else if (type != LUA_TBOOLEAN)
-		lua_pushnil (L);
-	return 1;
-}
-
-/// log print type
-/// G:
-static const string2enum_t lua_lprint[] = {
-	{ "normal",  LPRINT_NORMAL  },
-	{ "log",     LPRINT_LOG     },
-	{ "debug",   LPRINT_DEBUG   },
-	{ "notutf0", LPRINT_NOTUTF8 },
-	{ NULL,      0              },
-};
-
-/// roster type
-/// G:
-static const string2enum_t lua_roster_type[] = {
-	{ "user",    ROSTER_TYPE_USER    },
-	{ "group",   ROSTER_TYPE_GROUP   },
-	{ "agent",   ROSTER_TYPE_AGENT   },
-	{ "room",    ROSTER_TYPE_ROOM    },
-	{ "special", ROSTER_TYPE_SPECIAL },
-	{ NULL,      0                   },
-};
-
-/// main.log
-/// Prints message to log.
-/// Note: most likely you need notutf8 flag enabled.
-/// A: log print type, message, message...
-static int lua_main_log (lua_State *L)
-{
-	int type = luaL_checkenum_multi (L, 1, lua_lprint);
-	lua_concat (L, lua_gettop (L) - 1);
-	scr_log_print (type, lua_tostring (L, -1));
-	return 0;
-}
-
-// expects table on top
-static void lua_options_callback (char *key, char *value, lua_State *L)
-{
-	char *loc = from_utf8 (key);
-	lua_pushstring (L, loc);
-	g_free (loc);
-	loc = from_utf8 (value);
-	lua_pushstring (L, loc);
-	g_free (loc);
-	lua_settable (L, -3);
-}
-
-/// main.option
-/// Sets or gets value of mcabber option.
-/// You can specify nil as a value to delete option.
-/// If you omit option name, it returns hash table of all options.
-/// A: string (option name, optional), string (value, optional)
-/// R: string (value, optional)
-static int lua_main_option (lua_State *L)
-{
-	int top = lua_gettop (L);
-	if (top > 0) {
-		char *name = to_utf8 (luaL_checkstring (L, 1));
-		if (top > 1) { // Set
-			if (lua_type (L, 2) == LUA_TNIL) // Unset
-				settings_del (SETTINGS_TYPE_OPTION, name);
-			else { // Set
-				char *value = to_utf8 (luaL_checkstring (L, 2));
-				settings_set (SETTINGS_TYPE_OPTION, name, value);
-				g_free (value);
-			}
-			g_free (name);
-			return 0;
-		} else { // Get
-			char *value = from_utf8 (settings_get (SETTINGS_TYPE_OPTION, name));
-			if (value) {
-				lua_pushstring (L, value);
-				g_free (value);
-			} else
-				lua_pushnil (L);
-			g_free (name);
-			return 1;
-		}
-	} else { // List
-		lua_newtable (L);
-		settings_foreach (SETTINGS_TYPE_OPTION, (void (*)(char *key, char *val, void *ud)) lua_options_callback, L);
-		return 1;
-	}
-}
-
-/// main.alias
-/// Sets or gets alias.
-/// You can specify nil as a command to delete alias.
-/// If you omit alias name, it will return hash table of all aliases.
-/// A: string (alias name, optional), string (command, optional)
-/// R: string (command, optional)
-static int lua_main_alias (lua_State *L)
-{
-	int top = lua_gettop (L);
-	if (top > 0) {
-		char *name = to_utf8 (luaL_checkstring (L, 1));
-		if (top > 1) { // Set
-			if (lua_type (L, 2) == LUA_TNIL) { // Unset
-				settings_del (SETTINGS_TYPE_ALIAS, name);
-				compl_del_category_word (COMPL_CMD, name);
-			} else { // Set
-				char *value = to_utf8 (luaL_checkstring (L, 2));
-				if (!settings_get (SETTINGS_TYPE_ALIAS, name))
-					compl_add_category_word (COMPL_CMD, name);
-				settings_set (SETTINGS_TYPE_ALIAS, name, value);
-				g_free (value);
-			}
-			g_free (name);
-			return 0;
-		} else { // Get
-			char *value = from_utf8 (settings_get (SETTINGS_TYPE_ALIAS, name));
-			if (value) {
-				lua_pushstring (L, value);
-				g_free (value);
-			} else
-				lua_pushnil (L);
-			g_free (name);
-			return 1;
-		}
-	} else { // List
-		lua_newtable (L);
-		settings_foreach (SETTINGS_TYPE_ALIAS, (void (*)(char *key, char *val, void *ud)) lua_options_callback, L);
-		return 1;
-	}
-}
-
-/// main.bind
-/// Sets or gets binding.
-/// You can specify nil as a command to unbind key.
-/// If you omit keycode, it will return hash table of all bindings.
-/// A: string (keycode, optional), string (command, optional)
-/// R: string (command, optional)
-static int lua_main_binding (lua_State *L)
-{
-	int top = lua_gettop (L);
-	if (top > 0) {
-		// just to be sure...
-		char *name = to_utf8 (luaL_checkstring (L, 1));
-		if (top > 1) { // Set
-			if (lua_type (L, 2) == LUA_TNIL) // Unset
-				settings_del (SETTINGS_TYPE_BINDING, name);
-			else { // Set
-				char *value = to_utf8 (luaL_checkstring (L, 2));
-				settings_set (SETTINGS_TYPE_BINDING, name, value);
-				g_free (value);
-			}
-			g_free (name);
-			return 0;
-		} else { // Get
-			char *value = from_utf8 (settings_get (SETTINGS_TYPE_BINDING, name));
-			if (value) {
-				lua_pushstring (L, value);
-				g_free (value);
-			} else
-				lua_pushnil (L);
-			g_free (name);
-			return 1;
-		}
-	} else { // List
-		lua_newtable (L);
-		settings_foreach (SETTINGS_TYPE_BINDING, (void (*)(char *key, char *val, void *ud)) lua_options_callback, L);
-		return 1;
-	}
-}
-
-/// main.fileoption
-/// Gets option, expanding it as filename.
-/// A: string (option name)
-/// R: string (expanded option value) or nil
-static int lua_main_fileoption (lua_State *L)
-{
-	char *fname = expand_filename (settings_opt_get (luaL_checkstring (L, 1)));
-	if (fname) {
-		lua_pushstring (L, fname);
-		g_free (fname);
-	} else
-		lua_pushnil (L);
-	return 1;
-}
-
-#ifdef LLM_CONNECTION_ENABLE
-/// main.connection
-/// Returns lightuserdata of mcabber's loudmouth connection.
-/// This can be very useful with lua-loudmouth, and not much otherwise.
-/// R: lightuserdata or nil
-static int lua_main_connection (lua_State *L)
-{
-	if (xmpp_is_online ())
-		lua_pushlightuserdata (L, lconnection);
-	else
-		lua_pushnil (L);
-	return 1;
-}
-#endif
-
-/// main.print_info
-/// Prints a system message to buddy's window.
-/// A: string (jid), string (message)
-static int lua_main_print_info (lua_State *L)
-{
-	char *jid  = to_utf8 (luaL_checkstring (L, 1));
-	char *to   = jidtodisp (jid);
-	char *mesg = to_utf8 (luaL_checkstring (L, 2));
-	scr_write_incoming_message (to, mesg, 0, HBB_PREFIX_INFO, 0);
-	g_free (mesg);
-	g_free (to);
-	g_free (jid);
-	return 0;
-}
-
-/// main.beep
-/// Beeps with system speaker.
-static int lua_main_beep (lua_State *L)
-{
-	scr_beep ();
-	return 0;
-}
-
-/// main.run
-/// Runs specified mcabber command.
-/// A: string
-static int lua_main_run (lua_State *L)
-{
-	process_command (luaL_checkstring (L, 1), TRUE);
-	return 0;
-}
-
-/// main.status
-/// Returns your current status.
-/// R: string (status letter), string (status message)
-static int lua_main_status (lua_State *L)
-{
-	char *sm = from_utf8 (xmpp_getstatusmsg ());
-	lua_pushlstring (L, &imstatus2char[xmpp_getstatus ()], 1);
-	lua_pushstring (L, sm);
-	g_free (sm);
-	return 2;
-}
-
-// expects table on top
-static void lua_rosterlist_callback (gpointer buddy, lua_State *L)
-{
-	char *jid = from_utf8 (buddy_getjid (buddy));
-	lua_pushnumber (L, lua_objlen (L, -1) + 1);
-	lua_pushstring (L, jid);
-	lua_settable (L, -3);
-	g_free (jid);
-}
-
-/// main.roster
-/// Returns array of jids of buddies in roster.
-/// R: table
-static int lua_main_roster (lua_State *L)
-{
-	lua_newtable (L);
-	foreach_buddy (ROSTER_TYPE_USER|ROSTER_TYPE_AGENT|ROSTER_TYPE_ROOM, (void (*) (gpointer buddy, void *data)) lua_rosterlist_callback, L);
-	return 1;
-}
-
-/// main.current_buddy
-/// Returns jid of current selected buddy or sets current buddy to buddy with specified jid.
-/// A: string (optional)
-/// R: string (optional)
-static int lua_main_current_buddy (lua_State *L)
-{
-	if (lua_gettop (L) > 0) { // Set
-		// XXX: we need not convert to utf, RS works on jids/names in locale charset,
-		// but will jidtodisp always correctly work on such tings?
-		char *jid = jidtodisp (luaL_checkstring (L, 1));
-		scr_roster_search (jid);
-		g_free (jid);
-		return 0;
-	} else { // Get
-		char *jid = from_utf8 (buddy_getjid (BUDDATA (current_buddy)));
-		lua_pushstring (L, jid);
-		g_free (jid);
-		return 1;
-	}
-}
-
-/// main.full_jid
-/// Returns full jid (with current resource) of specified buddy (or current, if not specified).
-/// Note, that if there are no resources online, it will return just what it got.
-/// A: string (jid, optional)
-/// R: string (jid)
-static int lua_main_full_jid (lua_State *L)
-{
-	GList  *buddy;
-	GSList *resources;
-	GSList *resource;
-	if (lua_gettop (L) > 0) {
-		char *jid = from_utf8 (luaL_checkstring (L, 1));
-		buddy = buddy_search_jid (jid);
-		g_free (jid);
-	} else
-		buddy = current_buddy;
-	if (!buddy)
-		return 0;
-	resources = buddy_getresources (BUDDATA (buddy));
-	if (!resources) {
-		char *loc = from_utf8 (buddy_getjid (BUDDATA (buddy)));
-		lua_pushstring (L, loc);
-		g_free (loc);
-	} else {
-		char *jid = from_utf8 (buddy_getjid (BUDDATA (buddy)));
-		char *res = from_utf8 (g_slist_last (resources)->data);
-		lua_pushfstring (L, "%s%c%s", jid, JID_RESOURCE_SEPARATOR, res);
-		for (resource = resources; resource; resource = g_slist_next (resource))
-			g_free (resource->data);
-		g_slist_free (resources);
-		g_free (jid);
-		g_free (res);
-	}
-	return 1;
-}
-
-typedef struct {
-	lua_State *L;
-	gpointer   buddy;
-} lua_state_and_buddy_t; // :)
-
-/// resources table
-/// Hash table with resource name as keys and another hash tables as values.
-/// Inner tables contain resource-specific information: priority, status and message.
-static void lua_buddy_resources_callback (gpointer resource, lua_state_and_buddy_t *d)
-{
-	char *loc = from_utf8 (resource);
-	lua_pushstring (d->L, loc);
-	g_free (loc);
-	lua_createtable (d->L, 0, 3);
-	lua_pushstring (d->L, "priority");
-	lua_pushnumber (d->L, buddy_getresourceprio (d->buddy, resource));
-	lua_settable   (d->L, -3);
-	lua_pushstring  (d->L, "status");
-	lua_pushlstring (d->L, &imstatus2char[buddy_getstatus (d->buddy, resource)], 1);
-	lua_settable    (d->L, -3);
-	lua_pushstring (d->L, "message");
-	loc = from_utf8 (buddy_getstatusmsg (d->buddy, resource));
-	lua_pushstring (d->L, loc);
-	g_free (loc);
-	lua_settable   (d->L, -3);
-	lua_settable (d->L, -3);
-	g_free (resource);
-}
-
-/// main.buddy_info
-/// Returns a hash table with information on specified buddy.
-/// Table contains fields type, name, onserver and resources (which points to resources table).
-/// A: string (jid)
-/// R: table
-static int lua_main_buddy_info (lua_State *L)
-{
-	char                  *loc   = to_utf8 (luaL_checkstring (L, 1));
-	char                  *jid   = jidtodisp (loc);
-	GSList                *buddy = roster_find (jid, jidsearch, ROSTER_TYPE_USER|ROSTER_TYPE_AGENT|ROSTER_TYPE_ROOM);
-	GSList                *resources;
-	lua_state_and_buddy_t  snb;
-	g_free (jid);
-	g_free (loc);
-
-	if (!buddy) {
-		lua_pushnil (L);
-		return 1;
-	}
-
-	lua_createtable (L, 0, 3);
-	lua_pushstring (L, "type");
-	luaL_pushenum  (L, buddy_gettype (BUDDATA (buddy)), lua_roster_type);
-	lua_settable   (L, -3);
-	lua_pushstring (L, "name");
-	loc = from_utf8 (buddy_getname (BUDDATA (buddy)));
-	lua_pushstring (L, loc);
-	g_free (loc);
-	lua_settable   (L, -3);
-	lua_pushstring  (L, "onserver");
-	lua_pushboolean (L, buddy_getonserverflag (BUDDATA (buddy)));
-	lua_settable    (L, -3);
-	lua_pushstring  (L, "resources");
-	lua_createtable (L, 0, 0);
-	snb.L     = L;
-	snb.buddy = BUDDATA (buddy);
-	resources = buddy_getresources (BUDDATA (buddy));
-	g_slist_foreach (buddy_getresources (BUDDATA (buddy)), (GFunc) lua_buddy_resources_callback, &snb);
-	g_slist_free (resources);
-	lua_settable (L, -3);
-	
-	return 1;
-}
-
-// XMPP DISCO FEATURES
-
-GSList *lua_added_features = NULL;
-
-/// main.add_feature
-/// Adds xmlns to disco#info features list.
-/// A: string (xmlns)
-static int lua_main_add_feature (lua_State *L)
-{
-	char *xmlns = to_utf8 (luaL_checkstring (L, 1));
-	xmpp_add_feature (xmlns);
-	lua_added_features = g_slist_prepend (lua_added_features, xmlns);
-	return 0;
-}
-
-/// main.del_feature
-/// Removes xmlns from disco#info features list.
-/// A: stirng (xmlns)
-static int lua_main_del_feature (lua_State *L)
-{
-	char   *xmlns = to_utf8 (luaL_checkstring (L, 1));
-	GSList *el    = g_slist_find_custom (lua_added_features, xmlns, (GCompareFunc) strcmp);
-	xmpp_del_feature (xmlns);
-	if (el) {
-		g_free (el->data);
-		lua_added_features = g_slist_delete_link (lua_added_features, el);
-	}
-	return 0;
-}
-
-// MCABBER EVENTS
-
-/// event context
-/// Enum, indicating what exactly caused event function firing.
-/// G:
-static const string2enum_t lua_event_context[] = {
-	{ "timeout", EVS_CONTEXT_TIMEOUT  },
-	{ "cancel",  EVS_CONTEXT_CANCEL   },
-	{ "reject",  EVS_CONTEXT_REJECT   },
-	{ "accept",  EVS_CONTEXT_ACCEPT   },
-	{ NULL,      0                    },
-};
-
-typedef struct {
-	lua_State *L;
-	int        reference;
-	int        evid;
-} lua_event_callback_t;
-
-static GSList *lua_events = NULL;
-
-static void lua_event_callback_destroy_notify (gpointer udata)
-{
-	lua_event_callback_t *cb = udata;
-
-	luaL_unref (cb -> L, LUA_REGISTRYINDEX, cb->reference);
-	luaL_unref (cb -> L, LUA_REGISTRYINDEX, cb->evid);
-	luaL_free  (cb -> L, cb);
-}
-
-/// event function
-/// Function to be called, when some event state change occurs
-/// A: event context, string (event args)
-/// R: boolean (if event shoud be preserved)
-static gboolean lua_event_callback (guint context, const gchar *arg, gpointer userdata)
-{
-	lua_event_callback_t *cb = userdata;
-
-	lua_rawgeti (cb->L, LUA_REGISTRYINDEX, cb->reference);
-	luaL_pushenum (cb->L, context, lua_event_context);
-	lua_pushstring (cb -> L, arg);
-	if (lua_pcall (cb->L, 2, 1, 0)) {
-		scr_log_print (LPRINT_LOGNORM, "lua: Event callback execution error: %s", lua_tostring (cb->L, -1));
-		lua_pop (cb->L, 1);
-	}
-
-	if (lua_toboolean (cb -> L, -1))
-		return TRUE;
-	else {
-		lua_events = g_slist_remove (lua_events, cb); // XXX
-		return FALSE;
-	}
-}
-
-/// main.event
-/// Creates new event. If called without arguments, returns event id list.
-/// A: event function (optional), string (event id), string (description, optional), integer (expiration timeout, optional)
-/// R: string (event id) or nothing (creation error) or table (list of event names)
-static int lua_main_event (lua_State *L)
-{
-	int top = lua_gettop (L);
-	if (top > 0) { // Create
-		lua_event_callback_t *cb;
-		const char           *evid    = NULL;
-		int                   timeout = 0;
-		const char           *desc    = NULL;
-		luaL_argcheck (L, lua_type (L, 1) == LUA_TFUNCTION, 1, "event function expected");
-
-		if (top > 1) {
-			evid = luaL_checkstring (L, 2);
-			if (top > 2) {
-				timeout = luaL_checkinteger (L, 3);
-				if (top > 2) {
-					desc = luaL_checkstring (L, 4);
-					lua_pop (L, 3);
-				} else
-					lua_pop (L, 2);
-			} else
-				lua_pop (L, 1);
-		}
-
-		lua_pushvalue (L, 1); // XXX
-		cb              = luaL_malloc (L, sizeof (lua_event_callback_t));
-		cb -> L         = L;
-		cb -> reference = luaL_ref (L, LUA_REGISTRYINDEX);
-		cb -> evid      = LUA_NOREF;
-		lua_events      = g_slist_prepend (lua_events, cb);
-
-		evid = evs_new (desc, evid, timeout, lua_event_callback, cb, lua_event_callback_destroy_notify);
-		if (!evid) {
-			lua_events = g_slist_remove (lua_events, cb); // XXX
-			return 0;
-		}
-
-		lua_pushstring (L, evid);
-		lua_pushvalue (L, -1);
-		cb -> evid = luaL_ref (L, LUA_REGISTRYINDEX); // XXX
-		return 1;
-
-	} else { // List
-		GSList *events = evs_geteventslist ();
-		GSList *event;
-
-		lua_newtable (L);
-		for (event = events; event; event = g_slist_next (event)) {
-			lua_pushstring (L, event->data);
-			luaL_ref (L, -2);
-		}
-		g_slist_free (events);
-
-		return 1;
-	}
-}
-
-// MCABBER COMMANDS
-
-/// completion type
-/// Built-it completion types can be specified as string, instead of id.
-/// G:
-static string2enum_t lua_completion_type[] = { // not const,  we need to modify yesno
-	{ "cmd",       COMPL_CMD       },
-	{ "jid",       COMPL_JID       },
-	{ "urljid",    COMPL_URLJID    },
-	{ "name",      COMPL_NAME      },
-	{ "status",    COMPL_STATUS    },
-	{ "filename",  COMPL_FILENAME  },
-	{ "roster",    COMPL_ROSTER    },
-	{ "buffer",    COMPL_BUFFER    },
-	{ "group",     COMPL_GROUP     },
-	{ "groupname", COMPL_GROUPNAME },
-	{ "multiline", COMPL_MULTILINE },
-	{ "room",      COMPL_ROOM      },
-	{ "resource",  COMPL_RESOURCE  },
-	{ "auth",      COMPL_AUTH      },
-	{ "request",   COMPL_REQUEST   },
-	{ "events",    COMPL_EVENTS    },
-	{ "eventsid",  COMPL_EVENTSID  },
-	{ "pgp",       COMPL_PGP       },
-	{ "color",     COMPL_COLOR     },
-	{ "otr",       COMPL_OTR       },
-	{ "ortpolicy", COMPL_OTRPOLICY },
-	{ "yesno",     0               },
-	{ NULL,        0               },
-};
-#define MLUA_YESNO_POS ( 21 )
-
-typedef struct {
-	int        reference;
-	int        parse_args;
-	lua_State *L;
-} lua_command_callback_t;
-
-static GSList *lua_added_commands = NULL;
-
-static GSList *lua_added_categories = NULL;
-
-// returns true if string contains errors - unclosed quotes or unvalued option
-/// command arguments table
-/// It can parse barewords (with escapes), double-quoted strings (with escapes), single-quoted strings (without escapes), options and arguments.
-/// Arguments are separated only by whitespace, so, consequential quoted strings or barewords are one argument.
-/// This strings are equal:
-/// * ab\ cd\'e\\f\"
-/// * "ab cd'e\\f\""
-/// * 'ab cd'\''e\f"'
-/// * ab" cd'"'e\f"'
-/// Returned table have option names as keys, option values as values, and arguments as sequential members. -- option is supported.
-/// Example: "-t jid -m 9 -- -aa bb cc" will result in { t = 'jid', m = 9, '-aa', 'bb', 'cc' }
-/// Implementation notes:
-/// * All options should be before any arguments. First non-option argument disables options recognizing.
-/// * EOL is a cutting edge, that can cut much earlier, than you expect. Non-closed quoted strings lose leading quote and option without value loses its leading minus.
-/// * Escape character just before EOL is preserved.
-static int luaL_pushargs (lua_State *L, const char *args)
-{
-	const char  *p       = args;
-	luaL_Buffer  buf;
-	int          option  = 0;
-	int          options = 1;
-
-	lua_newtable (L);
-	luaL_buffinit (L, &buf);
-	while (*p) {
-		if (*p == ' ') {
-			++p;
-			continue;
-		}
-		if (*p == '"') { // soft quote
-			const char *start = ++p;
-			while (*p) {
-				if (*p == '\\') { // escape symbol
-					luaL_addlstring (&buf, start, p - start);
-					start = ++p;
-					if (*p) // skip symbol
-						++p;
-					else // add last \ in line
-						luaL_addchar (&buf, '\\');
-				} else if (*p == '"') // quotation end
-					break;
-				else
-					++p;
-			}
-			luaL_addlstring (&buf, start, p - start); // XXX: eats quote on eol
-			if (*p)
-				++p;
-			else
-				return 1;
-		} else if (*p == '\'') { // no-escape quote
-			const char *start = ++p;
-			while (*p && *p != '\'')
-				p++;
-			luaL_addlstring (&buf, start, p - start); // XXX: eats quote on eol
-			if (*p)
-				++p;
-			else
-				return 1;
-		} else { // bareword
-			const char *start = p;
-			while (*p) {
-				if (*p == '\\') {
-					luaL_addlstring (&buf, start, p - start);
-					start = ++p;
-					if (*p) // skip symbol
-						++p;
-					else // add last \ in line
-						luaL_addchar (&buf, '\\');
-				} else if (*p == ' ' || *p == '\'' || *p == '"')
-					break;
-				else
-					++p;
-			}
-			luaL_addlstring (&buf, start, p - start);
-		}
-
-		if ((!*p) || *p == ' ') {
-			const char *result;
-			luaL_pushresult (&buf);
-
-			result = lua_tostring (L, -1);
-			if (options && !option && *result == '-') { // option
-				if (*(result+1) == '-' && !*(result+2)) { // end of options
-					lua_pop (L, 1);
-					options = 0;
-				} else { // option name
-					lua_pushstring (L, result + 1);
-					lua_remove (L, -2);
-					option = 1;
-				}
-			} else if (option) { // opion value
-				lua_settable (L, -3);
-				option = 0;
-			} else { // argument
-				options = 0;
-				luaL_ref (L, -2);
-			}
-
-			luaL_buffinit (L, &buf);
-		}
-	}
-
-	if (option) {
-		luaL_ref (L, -2); // XXX: eats minus on eol
-		return 1;
-	}
-
-	return 0;
-}
-
-/// command function
-/// Function to handle newly registered command.
-/// Argument type passed depends on how command is registered.
-/// A: string (arguments) or command arguments table
-static void lua_main_command_handler (char *args, lua_command_callback_t *cb)
-{
-	lua_rawgeti (cb->L, LUA_REGISTRYINDEX, cb->reference);
-
-	if (cb->parse_args)
-		luaL_pushargs (cb->L, args);
-	else
-		lua_pushstring (cb->L, args);
-
-	if (lua_pcall (cb->L, 1, 0, 0)) {
-		scr_log_print (LPRINT_LOGNORM, "lua: Command execution error: %s", lua_tostring (cb->L, -1));
-		lua_pop (cb->L, 1);
-	}
-}
-
-/// main.parse_args
-/// Function to parse command argument string to command arguments table.
-/// A: string
-/// R: table
-static int lua_main_parse_args (lua_State *L)
-{
-	luaL_pushargs (L, luaL_checkstring (L, 1));
-	return 1;
-}
-
-/// main.add_category
-/// Adds completion category.
-/// A: table (values are used as words for completion, optional)
-/// R: integer (category id, in fact completion type) or nil
-static int lua_main_add_category (lua_State *L)
-{
-	guint cid;
-
-	if (lua_gettop (L) > 0) {
-		luaL_argcheck (L, lua_type (L, 1) == LUA_TTABLE, 1, "table expected");
-		cid = compl_new_category ();
-		if (cid) {
-			lua_pushnil (L);
-			while (lua_next (L, 1)) {
-				char *word = to_utf8 (luaL_checkstring (L, -1));
-				if (word) {
-					compl_add_category_word (cid, word);
-					g_free (word);
-				}
-				lua_pop (L, 1);
-			}
-		}
-	} else
-		cid = compl_new_category ();
-
-	if (cid) {
-		lua_added_categories = g_slist_prepend (lua_added_categories, (gpointer) cid);
-		lua_pushinteger (L, cid);
-	} else
-		lua_pushnil (L);
-
-	return 1;
-}
-
-/// main.del_category
-/// Removes completion category.
-/// A: integer (category id)
-static int lua_main_del_category (lua_State *L)
-{
-	guint cid = luaL_checkinteger (L, 1);
-	compl_del_category (cid);
-	lua_added_categories = g_slist_remove (lua_added_categories, (gpointer) cid);
-	return 0;
-}
-
-/// main.add_completion
-/// Adds word to a completion list.
-/// A: integer (completion group id), string (word)
-static int lua_main_add_completion (lua_State *L)
-{
-	guint  cid  = luaL_checkinteger (L, 1);
-	char  *word = to_utf8 (luaL_checkstring (L, 2)); // XXX
-	compl_add_category_word (cid, word);
-	g_free (word);
-	return 0;
-}
-
-/// main.del_completion
-/// Removes word from a completion list.
-/// A: integer (completion group id), string (word)
-static int lua_main_del_completion (lua_State *L)
-{
-	guint  cid  = luaL_checkinteger (L, 1);
-	char  *word = to_utf8 (luaL_checkstring (L, 2)); // XXX
-	compl_del_category_word (cid, word);
-	g_free (word);
-	return 0;
-}
-
-/// main.command
-/// Associates or breaks association between mcabber command name and lua function.
-/// To unregister command omit function argument.
-/// If you specify a third argument, table (even empty), function will return completion group id or nothing.
-/// You can also specify a string name (see completion type) instead of table, for non-builtin, you can just pass integer id.
-/// Note, that for now there are no way to unregister completion group, so, resources can be exausted easily.
-/// Also note, that it ignores keys in a completion table.
-/// A: string (command name), command function (optional), boolean (parse args flag, optional), table (completions, optional)/completion type (or integer comletion group id, optional)
-/// R: completion type (integer completion group id or string for builtin types, optional)
-static int lua_main_command (lua_State *L)
-{
-	const char             *name = luaL_checkstring (L, 1); // XXX: to_utf8? looks like no :/
-	lua_command_callback_t *cb;
-	int                     top  = lua_gettop (L);
-	if (top > 1) { // Register
-		guint cid = 0;
-		int parse = 0;
-		luaL_argcheck (L, lua_isfunction (L, 2), 2, "function expected");
-
-		if (top > 2) { // parse flag provided
-			parse = lua_toboolean (L, 3);
-
-			if (top > 3) { // Completions provided
-				if (lua_type (L, 4) == LUA_TTABLE) {
-					cid = compl_new_category ();
-					if (cid) {
-						lua_added_categories = g_slist_prepend (lua_added_categories, (gpointer) cid);
-						lua_pushnil (L);
-						while (lua_next (L, 4)) {
-							char *word = to_utf8 (luaL_checkstring (L, -1));
-							compl_add_category_word (cid, word);
-							lua_pop (L, 1);
-							g_free (word);
-						}
-					}
-				} else
-					cid = luaL_checkenum (L, 4, lua_completion_type);
-			}
-		}
-
-		cb = luaL_malloc (L, sizeof (lua_command_callback_t));
-		lua_pushvalue (L, 2);
-		cb->reference  = luaL_ref (L, LUA_REGISTRYINDEX);
-		cb->parse_args = parse;
-		cb->L          = L;
-		cmd_add (name, "", cid, 0, (void (*) (char *p)) lua_main_command_handler, cb);
-
-		lua_added_commands = g_slist_prepend (lua_added_commands, g_strdup (name));
-
-		if (cid) {
-			luaL_pushenum (L, cid, lua_completion_type);
-			return 1;
-		}
-	} else { // Unregister
-		GSList *el = g_slist_find_custom (lua_added_commands, name, (GCompareFunc) g_strcmp0);
-
-		cb = cmd_del (name);
-		if (cb) {
-			luaL_unref (cb->L, LUA_REGISTRYINDEX, cb->reference);
-			luaL_free (cb->L, cb);
-		}
-
-		if (el) {
-			g_free (el->data);
-			lua_added_commands = g_slist_delete_link (lua_added_commands, el);
-		}
-	}
-	return 0;
-}
-
-// TIMER
-
-typedef struct {
-	int        reference;
-	guint      source;
-	lua_State *L;
-} lua_timer_callback_t;
-
-static GSList *lua_timers = NULL;
-
-static void lua_timer_callback_destroy (lua_timer_callback_t *cb)
-{
-	luaL_unref (cb->L, LUA_REGISTRYINDEX, cb->reference);
-	lua_timers = g_slist_remove (lua_timers, (gpointer) cb->source);
-	luaL_free (cb->L, cb);
-}
-
-/// timer function
-/// Function, that will be called periodically until it returns false.
-/// R: boolean
-static gboolean lua_timer_callback (lua_timer_callback_t *cb)
-{
-	int ret;
-	lua_rawgeti (cb->L, LUA_REGISTRYINDEX, cb->reference);
-	if (lua_pcall (cb->L, 0, 1, 0)) {
-		scr_log_print (LPRINT_LOGNORM, "lua: Timer callback execution error: %s", lua_tostring (cb->L, -1));
-		lua_pop (cb->L, 1);
-		return FALSE;
-	}
-	ret = lua_toboolean (cb->L, -1);
-	lua_pop (cb->L, 1);
-	return ret;
-}
-
-/// main.timer
-/// Creates new timer function, that will be called periodically.
-/// A: integer (interval, seconds), timer function
-static int lua_main_timer (lua_State *L)
-{
-	int                   interval = luaL_checkint (L, 1);
-	guint                 source;
-	lua_timer_callback_t *cb;
-	luaL_argcheck (L, lua_isfunction (L, 2), 2, "function expected");
-
-	cb = luaL_malloc (L, sizeof (lua_timer_callback_t));
-	cb->reference = luaL_ref (L, LUA_REGISTRYINDEX);
-	cb->L         = L;
-
-	source = g_timeout_add_seconds_full (MLUA_SOURCE_PRIORITY, interval, (GSourceFunc) lua_timer_callback, cb, (GDestroyNotify) lua_timer_callback_destroy);
-	cb->source = source;
-	lua_timers = g_slist_prepend (lua_timers, (gpointer) source);
-
-	return 0;
-}
-
-// BACKGROUND PIPE READING
-
-typedef struct {
-	lua_State *L;
-	guint      source;
-	FILE      *fd;
-	int        reference;
-} lua_bgread_callback_t;
-
-static GSList *lua_bgreads = NULL;
-
-static gchar lua_bgread_buffer[MLUA_BGREAD_BUFFER];
-
-static void lua_bgread_callback_destroy (lua_bgread_callback_t *cb)
-{
-	luaL_unref (cb->L, LUA_REGISTRYINDEX, cb->reference);
-	pclose (cb->fd); // Not necessary?
-	lua_bgreads = g_slist_remove (lua_bgreads, (gpointer) cb->source);
-	luaL_free (cb->L, cb);
-}
-
-/// background reading function
-/// Function, that processes output from pipe in asynchroneous way.
-/// A: string (data) or nil (eof)
-/// R: boolean (false if reading should be terminated)
-static gboolean lua_bgread_callback (GIOChannel *source, GIOCondition condition, lua_bgread_callback_t *cb)
-{
-	int ret = TRUE;
-
-	if (condition | G_IO_IN) { // data
-		while (TRUE) {
-			gsize read = 0;
-			g_io_channel_read_chars (source, lua_bgread_buffer, MLUA_BGREAD_BUFFER, &read, NULL);
-			if (!read) // exhausted
-				break;
-
-			lua_rawgeti (cb->L, LUA_REGISTRYINDEX, cb->reference);
-			lua_pushlstring (cb->L, lua_bgread_buffer, read);
-			if (lua_pcall (cb->L, 1, 1, 0)) {
-				scr_log_print (LPRINT_LOGNORM, "lua: Bgread callback execution error: %s", lua_tostring (cb->L, -1));
-				lua_pop (cb->L, 1);
-				return FALSE;
-			}
-			ret = lua_toboolean (cb->L, -1);
-			lua_pop (cb->L, 1);
-			if (!ret) // enough
-				return FALSE;
-		}
-	}
-
-	if (condition & ~G_IO_IN) { // err or hup
-		lua_rawgeti (cb->L, LUA_REGISTRYINDEX, cb->reference);
-		lua_pushnil (cb->L);
-		if (lua_pcall (cb->L, 1, 1, 0)) {
-			scr_log_print (LPRINT_LOGNORM, "lua: Bgread callback execution error: %s", lua_tostring (cb->L, -1));
-			lua_pop (cb->L, 1);
-			return FALSE;
-		}
-		ret = lua_toboolean (cb->L, -1);
-		lua_pop (cb->L, 1);
-	}
-
-	return ret;
-}
-
-/// main.bgread
-/// Runs specified command and passes its output to a given function.
-/// A: string (command), background reading function
-static int lua_main_bgread (lua_State *L)
-{
-	const char            *command = luaL_checkstring (L, 1);
-	lua_bgread_callback_t *cb;
-	FILE                  *fd;
-	GIOChannel            *channel;
-	const char            *charset = NULL;
-	guint                  source;
-	luaL_argcheck (L, lua_isfunction (L, 2), 2, "function expected");
-
-	fd = popen (command, "r");
-	if (!fd) {
-		lua_pushstring (L, "Error opening pipe");
-		lua_error (L);
-	}
-
-	channel = g_io_channel_unix_new (fileno (fd));
-	// We, most likely, need this,
-	// But we cannot use this,
-	// It will block.
-	//if (!g_get_charset (&charset))
-	g_io_channel_set_encoding (channel, charset, NULL);
-	g_io_channel_set_buffered (channel, FALSE);
-	g_io_channel_set_flags (channel, G_IO_FLAG_NONBLOCK, NULL);
-	g_io_channel_set_close_on_unref (channel, TRUE);
-
-	cb = luaL_malloc (L, sizeof (lua_bgread_callback_t));
-	cb->reference = luaL_ref (L, LUA_REGISTRYINDEX);
-	cb->L         = L;
-	cb->fd        = fd;
-
-	source = g_io_add_watch_full (channel, MLUA_SOURCE_PRIORITY, G_IO_IN|G_IO_HUP|G_IO_ERR, (GIOFunc) lua_bgread_callback, cb, (GDestroyNotify) lua_bgread_callback_destroy);
-	cb->source = source;
-	lua_bgreads = g_slist_prepend (lua_bgreads, (gpointer) source);
-
-	// unref?
-
-	return 0;
-}
-
-// HOOK HANDLING
-
-typedef struct {
-	lua_State *L;       // lua environment for handler use
-	int        nameref; // reference to hook name string
-	int        cbref;   // reference to hook handler function
-	int        selfref; // self-reference to object
-	guint      hid;     // hook id for object destruction
-} lua_hook_t;
-
-/// hook handler result
-/// What to do with hook processing afterwards
-/// G:
-static const string2enum_t lua_hook_handler_result[] = {
-	{ "proceed", HOOK_HANDLER_RESULT_ALLOW_MORE_HANDLERS       },
-	{ "stop",    HOOK_HANDLER_RESULT_NO_MORE_HANDLER           },
-	{ "drop",    HOOK_HANDLER_RESULT_NO_MORE_HANDLER_DROP_DATA },
-	{ NULL,      HOOK_HANDLER_RESULT_ALLOW_MORE_HANDLERS       },
-};
-
-/// hook handler priority
-/// Feel free to specify just a number instead of some preset value.
-/// G:
-static const string2enum_t lua_hook_handler_priority[] = {
-	{ "high",      G_PRIORITY_HIGH         },
-	{ "default",   G_PRIORITY_DEFAULT      },
-	{ "idle-high", G_PRIORITY_HIGH_IDLE    },
-	{ "idle",      G_PRIORITY_DEFAULT_IDLE },
-	{ "low",       G_PRIORITY_LOW          },
-	{ NULL,        G_PRIORITY_DEFAULT      },
-};
-
-/// hook function
-/// Function to be called, when hook will be processsed.
-/// XXX: we can provide object as argument, but is this necessary?
-/// A: table (arguments hash)
-/// R: hook handler result
-static guint lua_hook_cb (const gchar *hookid, hk_arg_t *args, gpointer data)
-{
-	lua_hook_t *cb  = data;
-	hk_arg_t   *arg = args;
-	guint       ret = HOOK_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
-	lua_State  *L   = cb -> L;
-
-	if (cb -> cbref == LUA_NOREF)
-		return HOOK_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
-
-	lua_rawgeti (L, LUA_REGISTRYINDEX, cb -> cbref);
-	if (!lua_isfunction (L, -1)) {
-		lua_pop (L, 1);
-		return HOOK_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
-	}
-
-	lua_newtable (L);
-	lua_pushliteral (L, "hook");
-	lua_pushstring  (L, hookid);
-	lua_settable (L, -3);
-	while (arg->name != NULL) {
-		char *name  = from_utf8 (arg->name);
-		char *value = from_utf8 (arg->value);
-		lua_pushstring (L, name);
-		lua_pushstring (L, value);
-		lua_settable (L, -3);
-		g_free (name);
-		g_free (value);
-		arg++;
-	}
-
-	if (lua_pcall (L, 1, 1, 0)) {
-		scr_log_print (LPRINT_NORMAL, "lua: Error in hook handler: %s", lua_tostring (L, -1));
-		lua_pop (L, 1);
-	} else {
-		switch (lua_type (L, -1)) {
-		case LUA_TSTRING:
-		case LUA_TNUMBER:
-			ret = luaL_checkenum (L, -1, lua_hook_handler_result);
-			break;
-		default:
-			ret = HOOK_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
-			break;
-		}
-		lua_pop (L, 1);
-	}
-
-	return ret;
-}
-
-/// main.hook
-/// Installs hook handler, returns an object, that you need to keep until
-/// hook handling is no more needed.
-/// A: string (hook name), hook function, integer (priority, optional)
-/// R: userdata (hook object)
-static int lua_main_hook (lua_State *L)
-{
-	const char   *hook_name = luaL_checkstring (L, 1);
-	int           priority  = G_PRIORITY_DEFAULT;
-	lua_hook_t   *cb;
-
-	luaL_argcheck (L, lua_isfunction (L, 2), 2, "function expected");
-
-	if (lua_gettop (L) > 2)
-		priority = luaL_checkenum (L, 3, lua_hook_handler_priority);
-
-	cb = lua_newuserdata (L, sizeof (lua_hook_t));
-	luaL_getmetatable (L, "mcabber.hook");
-	lua_setmetatable (L, -2);
-
-	lua_pushvalue (L, -1);
-	cb -> selfref = luaL_ref (L, LUA_REGISTRYINDEX);
-	lua_pushvalue (L, 1);
-	cb -> nameref = luaL_ref (L, LUA_REGISTRYINDEX);
-	lua_pushvalue (L, 2);
-	cb -> cbref   = luaL_ref (L, LUA_REGISTRYINDEX);
-	cb -> L       = L;
-	cb -> hid     = hk_add_handler (lua_hook_cb, hook_name, priority, cb);
-
-	return 1;
-}
-
-static void lua_mcabber_unregister_hook (lua_State *L, lua_hook_t *cb)
-{
-	const char *name;
-
-	if (!cb -> hid || cb -> nameref == LUA_NOREF)
-		return;
-
-	lua_rawgeti (L, LUA_REGISTRYINDEX, cb -> nameref);
-	name = lua_tostring (L, -1);
-	if (name) {
-		hk_del_handler (name, cb -> hid);
-		cb -> hid = 0;
-	}
-
-	lua_pop (L, 1);
-}
-
-/// hook:del
-/// Unregisters given hook handler from mcabber. Object will be destroyed later.
-static int lua_mcabber_hook_del (lua_State *L)
-{
-	lua_hook_t *cb = luaL_checkudata (L, 1, "mcabber.hook");
-	luaL_argcheck (L, cb != NULL, 1, "mcabber hook object expected");
-	lua_mcabber_unregister_hook (L, cb);
-	if (cb -> selfref != LUA_NOREF) {
-		luaL_unref (L, LUA_REGISTRYINDEX, cb -> selfref);
-		cb -> selfref = LUA_NOREF;
-	}
-	return 0;
-}
-
-static int lua_mcabber_hook_gc (lua_State *L)
-{
-	lua_hook_t *cb = luaL_checkudata (L, 1, "mcabber.hook");
-	luaL_argcheck (L, cb != NULL, 1, "mcabber hook object expected");
-	lua_mcabber_unregister_hook (L, cb);
-	if (cb -> nameref != LUA_NOREF)
-		luaL_unref (L, LUA_REGISTRYINDEX, cb -> nameref);
-	if (cb -> cbref != LUA_NOREF)
-		luaL_unref (L, LUA_REGISTRYINDEX, cb -> cbref);
-	return 0;
-}
-
-static const luaL_Reg lua_mcabber_hook_reg_m[] = {
-	{ "del",  lua_mcabber_hook_del },
-	{ "__gc", lua_mcabber_hook_gc  },
-	{ NULL,   NULL                 },
-};
-
-static void lua_hook_init (lua_State *L)
-{
-	luaL_newmetatable (L, "mcabber.hook");
-	lua_pushvalue (L, -1);
-	lua_setfield (L, -2, "__index");
-	luaL_register (L, NULL, lua_mcabber_hook_reg_m);
-	lua_pop (L, 1);
-}
-
-#if 0
-// OPTION GUARDS
-
-GSList *lua_installed_guards = NULL;
-
-typedef struct {
-	lua_State *L;       // lua environment for handler use
-	int        nameref; // reference to key name string
-	int        cbref;   // reference to guard function
-//	int        objref;  // self_reference to object
-	guint      hid;     // hook id for object destruction
-} lua_guard_t;
-
-/// guard function
-/// Function to be called, when option changes it's value.
-/// Old option value is still accessible through main.option.
-/// A: string (key), string (new value)
-/// R: string (value to save in hash table)
-static gchar *lua_guard_cb (const gchar *key, const gchar *value)
-{
-	lua_guard_t *cb = ;// FIXME
-	lua_State   *L  = cb -> L;
-
-	if (cb -> cbref == LUA_NOREF)
-		return g_strdup (value);
-
-	lua_rawgeti (L, LUA_REGISTRYINDEX, cb -> cbref);
-	if (!lua_isfunction (L, -1)) {
-		lua_pop (L, 1);
-		return g_strdup (value);
-	}
-
-	lua_pushstring (L, key);
-	lua_psuhstring (L, value);
-
-	if (lua_pcall (L, 2, 1, 0)) {
-		scr_log_print (LPRINT_NORMAL, "lua: Error in hook handler: %s", lua_tostring (L, -1));
-		lua_pop (L, 1);
-		return g_strdup (value);
-	}
-
-	return g_strdup (lua_tostring (L, -1));
-}
-
-/// main.guard
-/// Installs option guard for given option. Returns guard object, that
-/// should be kept around as long, as guard is needed.
-/// A: string (option name), guard function
-/// R: userdata (guard object)
-static int lua_main_guard (lua_State *L)
-{
-	const char   *name      = luaL_checkstring (L, 1);
-	int           priority  = G_PRIORITY_DEFAULT;
-	lua_guard_t   *cb;
-
-	luaL_argcheck (L, lua_isfunction (L, 2), 2, "function expected");
-
-	cb = lua_newuserdata (L, sizeof (lua_guard_t));
-	luaL_getmetatable (L, "mcabber.guard");
-	lua_setmetatable (L, -2);
-
-	lua_pushvalue (L, 1);
-	cb -> nameref = luaL_ref (L, LUA_REGISTRYINDEX);
-	lua_pushvalue (L, 2);
-	cb -> cbref   = luaL_ref (L, LUA_REGISTRYINDEX);
-	cb -> L       = L;
-
-	settings_set_guard (name, lua_guard_cb)
-
-	return 1;
-}
-
-static void lua_mcabber_unregister_guard (lua_State *L, lua_guard_t *cb)
-{
-	const char *name;
-
-	if (cb -> nameref == LUA_NOREF)
-		return;
-
-	lua_rawgeti (L, LUA_REGISTRYINDEX, cb -> nameref);
-	name = lua_tostring (L, -1);
-	if (name) {
-		settings_del_guard (name);
-		luaL_unref (L, LUA_REGISTRYINDEX, cb -> nameref);
-		cb -> nameref = LUA_NOREF;
-	}
-
-	lua_pop (L, 1);
-}
-
-/// guard:del
-/// Unregisters given option guard from mcabber. Object will be destroyed later.
-static int lua_mcabber_guard_del (lua_State *L)
-{
-	lua_guard_t *cb = luaL_checkudata (L, 1, "mcabber.guard");
-	luaL_argcheck (L, cb != NULL, 1, "mcabber guard object expected");
-	lua_mcabber_unregister_guard (L, cb);
-	return 0;
-}
-
-static int lua_mcabber_guard_gc (lua_State *L)
-{
-	lua_hook_t *cb = luaL_checkudata (L, 1, "mcabber.guard");
-	luaL_argcheck (L, cb != NULL, 1, "mcabber guard object expected");
-	lua_mcabber_unregister_guard (L, cb);
-	if (cb -> cbref != LUA_NOREF)
-		luaL_unref (L, LUA_REGISTRYINDEX, cb -> cbref);
-	return 0;
-}
-
-static const luaL_Reg lua_mcabber_hook_reg_m[] = {
-	{ "del",  lua_mcabber_guard_del },
-	{ "__gc", lua_mcabber_guard_gc  },
-	{ NULL,   NULL                 },
-};
-
-static void lua_guard_init (lua_State *L)
-{
-	luaL_newmetatable (L, "mcabber.guard");
-	lua_pushvalue (L, -1);
-	lua_setfield (L, -2, "__index");
-	luaL_register (L, NULL, lua_mcabber_guard_reg_m);
-	lua_pop (L, 1);
-}
-
-static void lua_guard_uninit (lua_State *L)
-{
-}
-#endif
-
-// MAIN INITIALIZATION CODE
-
-#ifdef LLM_LOG_HANDLER
-// FIXME: this should not be here
-guint lua_lm_log_handler_id;
-
-void lua_lm_log_handler (const gchar *domain, GLogLevelFlags log_level, const gchar *message, gpointer ignore)
-{
-	if (settings_opt_get_int ("lua_lm_debug"))
-		scr_log_print (LPRINT_LOGNORM, "%s: %s", domain, message);
-}
-#endif
-
-static void do_lua(char *arg, lua_State *L)
-{
-	if (luaL_loadbuffer (L, arg, strlen (arg), "line")) {
-		scr_log_print (LPRINT_LOGNORM, "lua: Compilation error: %s", lua_tostring (L, -1));
-		lua_pop (L, 1);
-		return;
-	}
- 
-	if (lua_pcall (L, 0, 0, 0)) {
-		scr_log_print (LPRINT_NORMAL, "lua: Runtime error: %s", lua_tostring(L, -1));
-		lua_pop (L, 1);
-		return;
-	}
-}
-
-static void *lua_alloc (void *ud, void *ptr, size_t osize, size_t nsize) {
-	if (nsize == 0) {
-		g_free (ptr);
-		return NULL;
-	} else
-		return g_realloc (ptr, nsize);
-}
-
-#define reg(NAME)                   \
-	{ #NAME, lua_main_##NAME },
-static const luaL_Reg lua_reg_main[] = {
-	reg ( yesno          ) 
-#ifdef LLM_CONNECTION_ENABLE
-	reg ( connection     ) 
-#endif
-	reg ( log            ) 
-	reg ( option         ) 
-	reg ( alias          ) 
-	reg ( binding        ) 
-	reg ( fileoption     ) 
-	reg ( add_feature    ) 
-	reg ( del_feature    ) 
-	reg ( event          )
-	reg ( parse_args     ) 
-	reg ( add_category   ) 
-	reg ( del_category   ) 
-	reg ( add_completion ) 
-	reg ( del_completion ) 
-	reg ( command        ) 
-	reg ( print_info     ) 
-	reg ( beep           ) 
-	reg ( run            ) 
-	reg ( status         ) 
-	reg ( roster         ) 
-	reg ( current_buddy  ) 
-	reg ( full_jid       ) 
-	reg ( buddy_info     ) 
-	reg ( timer          ) 
-	reg ( alias          ) 
-	reg ( binding        ) 
-	reg ( fileoption     ) 
-	reg ( add_feature    ) 
-	reg ( del_feature    ) 
-	reg ( event          )
-	reg ( parse_args     ) 
-	reg ( add_category   ) 
-	reg ( del_category   ) 
-	reg ( add_completion ) 
-	reg ( del_completion ) 
-	reg ( command        ) 
-	reg ( print_info     ) 
-	reg ( beep           ) 
-	reg ( run            ) 
-	reg ( status         ) 
-	reg ( roster         ) 
-	reg ( current_buddy  ) 
-	reg ( full_jid       ) 
-	reg ( buddy_info     ) 
-	reg ( timer          ) 
-	reg ( bgread         ) 
-	reg ( hook           )
-	{ NULL, NULL },
-};
-#undef reg
-
-const gchar *g_module_check_init (GModule *module)
-{
-	lua = lua_newstate (lua_alloc, NULL);
-	if (!lua)
-		return "Lua initialization error";
-	else
-		return NULL;
-}
-
-void g_module_unload (GModule *module)
-{
-	if (lua) {
-		lua_close (lua);
-		lua = NULL;
-	}
-}
-
-static void mlua_init (void)
-{
-	luaL_openlibs (lua);
-
-	luaL_register (lua, "main", lua_reg_main);
-	lua_pop (lua, 1); // XXX
-	lua_register (lua, "dopath", lua_global_dopath);
-	lua_register (lua, "print",  lua_global_print );
-
-	{
-		int cid = compl_new_category ();
-
-		if (cid) {
-			const string2enum_t *word = lua_yesno;
-			lua_completion_type[MLUA_YESNO_POS].value = cid;
-			lua_added_categories = g_slist_prepend (lua_added_categories, (gpointer) cid);
-			while (word->string) {
-				compl_add_category_word (cid, word->string);
-				++word;
-			}
-		}
-	}
-
-	cmd_add ("lua", "Evaluate lua string", 0, 0, (void (*) (char *p)) do_lua, lua);
-
-#ifdef LLM_LOG_HANDLER
-	// FIXME: this should not be here.
-	lua_lm_log_handler_id = g_log_set_handler ("lua-lm", G_LOG_LEVEL_MASK, (GLogFunc) lua_lm_log_handler, NULL);
-#endif
-
-	lua_hook_init (lua);
-
-	{
-		char *initfile = expand_filename (settings_opt_get ("lua_init_filename"));
-		
-		if (!initfile)
-			scr_log_print (LPRINT_LOGNORM, "lua: Cannot determine config file name");
-		else {
-			if (luaL_loadfile(lua, initfile)) {
-				scr_log_print (LPRINT_LOGNORM, "lua: Unable to compile rc file: %s", lua_tostring (lua, -1));
-				lua_pop (lua, 1);
-			} else if (lua_pcall (lua, 0, LUA_MULTRET, 0)) {
-				scr_log_print (LPRINT_LOGNORM, "lua: Runtime error in rc file: %s", lua_tostring(lua, -1));
-				lua_pop (lua, 1);
-			} else
-				scr_log_print (LPRINT_LOGNORM, "lua: Loaded %s", initfile);
-			g_free (initfile);
-		}
-	}
-
-	{
-		hk_arg_t args[] = {
-			{ NULL, NULL },
-		};
-		hk_run_handlers("hook-lua-start", args);
-	}
-}
-
-static void lua_events_cancel (gpointer data, gpointer ignore)
-{
-	lua_event_callback_t *cb = data;
-	const char *evid;
-	if (cb->evid == LUA_NOREF)
-		return;
-	lua_rawgeti (cb->L, LUA_REGISTRYINDEX, cb->evid);
-	evid = lua_tostring (cb ->L, -1);
-	evs_callback (evid, EVS_CONTEXT_CANCEL, "Module unloading");
-}
-
-static void lua_events_destroy (gpointer data, gpointer ignore)
-{
-	lua_event_callback_t *cb = data;
-	const char *evid;
-	if (cb->evid == LUA_NOREF)
-		return;
-	lua_rawgeti (cb->L, LUA_REGISTRYINDEX, cb->evid);
-	evid = lua_tostring (cb ->L, -1);
-	evs_del (evid);
-}
-
-static void lua_bgreads_destroy (guint source, gpointer ignore)
-{
-	g_source_remove (source);
-}
-
-static void lua_timers_destroy (guint source, gpointer ignore)
-{
-	g_source_remove (source);
-}
-
-static void lua_features_destroy (char *xmlns, gpointer ignore)
-{
-	xmpp_del_feature (xmlns);
-	g_free (xmlns);
-}
-
-static void lua_commands_destroy (char *name, gpointer ignore)
-{
-	lua_command_callback_t *cb = cmd_del (name);
-	if (cb) {
-		luaL_unref (cb->L, LUA_REGISTRYINDEX, cb->reference);
-		luaL_free (cb->L, cb);
-	}
-	g_free (name);
-}
-
-static void lua_categories_destroy (guint id, gpointer ignore)
-{
-	compl_del_category (id);
-}
-
-static void mlua_uninit (void)
-{
-	if (lua) {
-		hk_arg_t args[] = {
-			{ NULL, NULL },
-		};
-		hk_run_handlers ("hook-lua-quit", args);
-
-		// hook handlers will be unregistered upon objects destruction
-
-		g_slist_foreach (lua_bgreads, (GFunc) lua_bgreads_destroy, NULL);
-		g_slist_free (lua_bgreads);
-		lua_bgreads = NULL;
-
-		g_slist_foreach (lua_timers, (GFunc) lua_timers_destroy, NULL);
-		g_slist_free (lua_timers);
-		lua_timers = NULL;
-
-		g_slist_foreach (lua_events, (GFunc) lua_events_cancel, NULL);
-		g_slist_foreach (lua_events, (GFunc) lua_events_destroy, NULL);
-		g_slist_free (lua_events);
-		lua_events = NULL;
-
-		g_slist_foreach (lua_added_features, (GFunc) lua_features_destroy, NULL);
-		g_slist_free (lua_added_features);
-		lua_added_features = NULL;
-
-		g_slist_foreach (lua_added_commands, (GFunc) lua_commands_destroy, NULL);
-		g_slist_free (lua_added_commands);
-		lua_added_commands = NULL;
-
-		g_slist_foreach (lua_added_categories, (GFunc) lua_categories_destroy, NULL);
-		g_slist_free (lua_added_categories);
-		lua_added_categories = NULL;
-
-		cmd_del ("lua");
-
-		lua_close (lua);
-		lua = NULL;
-
-#ifdef LLM_LOG_HANDLER
-		// FIXME: shouldn't be here
-		g_log_remove_handler ("lua-lm", lua_lm_log_handler_id);
-#endif
-	}
-}
-