--- a/lua.c Sun Apr 11 21:54:55 2010 +0300
+++ b/lua.c Mon Apr 12 00:29:44 2010 +0300
@@ -62,7 +62,7 @@
static module_info_t info_lua_experimental = {
.branch = "experimental",
- .api = 25,
+ .api = 27,
.version = PROJECT_VERSION,
.description = DESCRIPTION,
.requires = NULL,
@@ -802,12 +802,16 @@
#define MLUA_YESNO_POS ( 21 )
typedef struct {
- int reference;
+ int cbref;
int parse_args;
+#ifdef HAVE_CMD_ID
+ gpointer cmid;
+#else
+ int nameref;
+#endif
+ int selfref;
lua_State *L;
-} lua_command_callback_t;
-
-static GSList *lua_added_commands = NULL;
+} lua_command_t;
static GSList *lua_added_categories = NULL;
@@ -925,9 +929,9 @@
/// 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)
+static void lua_main_command_handler (char *args, lua_command_t *cb)
{
- lua_rawgeti (cb->L, LUA_REGISTRYINDEX, cb->reference);
+ lua_rawgeti (cb->L, LUA_REGISTRYINDEX, cb->cbref);
if (cb->parse_args)
luaL_pushargs (cb->L, args);
@@ -1020,75 +1024,123 @@
}
/// 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.
+/// Associates mcabber command name and lua function.
+/// As a completion 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)
+/// A: string (command name), command function, boolean (parse args flag, optional), table (completions, optional)/completion type (or integer comletion group id, optional)
+/// R: userdata (command object)
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 && !(top == 2 && lua_isnil (L, 2))) { // Register
- guint cid = 0;
- int parse = 0;
- luaL_argcheck (L, lua_isfunction (L, 2), 2, "function expected");
+ const char *name = luaL_checkstring (L, 1); // XXX: to_utf8? looks like no :/
+ lua_command_t *cb;
+ int top = lua_gettop (L);
+ 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 > 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);
- }
+ 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);
+ }
+ } else
+ cid = luaL_checkenum (L, 4, lua_completion_type);
}
}
+
+ cb = lua_newuserdata (L, sizeof (lua_command_t));
+ luaL_getmetatable (L, "mcabber.command");
+ lua_setmetatable (L, -2);
+ lua_pushvalue (L, 2);
+ cb -> cbref = luaL_ref (L, LUA_REGISTRYINDEX);
+ lua_pushvalue (L, -1);
+ cb -> selfref = luaL_ref (L, LUA_REGISTRYINDEX);
+ cb -> parse_args = parse;
+ cb -> L = L;
+#ifdef HAVE_CMD_ID
+ cb -> cmid = cmd_add (name, "", cid, 0, (void (*) (char *p)) lua_main_command_handler, cb);
+#else
+ lua_pushvalue (L, 1);
+ cb -> nameref = luaL_ref (L, LUA_REGISTRYINDEX);
+ cmd_add (name, "", cid, 0, (void (*) (char *p)) lua_main_command_handler, cb);
+#endif
+
+ return 1;
+}
+
+static void lua_mcabber_unregister_command (lua_State *L, lua_command_t *cb)
+{
+#ifdef HAVE_CMD_ID
+ if (cb -> cmid)
+ cmd_del (cb -> cmid);
+#else
+ const char *name;
+
+ if (cb -> nameref == LUA_NOREF)
+ return;
+
+ lua_rawgeti (L, LUA_REGISTRYINDEX, cb -> nameref);
+ name = lua_tostring (L, -1);
+ cmd_del (name);
+ lua_pop (L, 1);
+#endif
+}
+
+/// command:del
+/// Unregisters given command from mcabber. Object will be destroyed later.
+static int lua_mcabber_command_del (lua_State *L)
+{
+ lua_command_t *cb = luaL_checkudata (L, 1, "mcabber.command");
+ luaL_argcheck (L, cb != NULL, 1, "mcabber command object expected");
+ lua_mcabber_unregister_command (L, cb);
+ if (cb -> selfref != LUA_NOREF) {
+ luaL_unref (L, LUA_REGISTRYINDEX, cb -> selfref);
+ cb -> selfref = LUA_NOREF;
+ }
return 0;
}
+static int lua_mcabber_command_gc (lua_State *L)
+{
+ lua_command_t *cb = luaL_checkudata (L, 1, "mcabber.command");
+ luaL_argcheck (L, cb != NULL, 1, "mcabber command object expected");
+ lua_mcabber_unregister_command (L, cb);
+#ifndef HAVE_CMD_ID
+ if (cb -> nameref != LUA_NOREF)
+ luaL_unref (L, LUA_REGISTRYINDEX, cb -> nameref);
+#endif
+ if (cb -> cbref != LUA_NOREF)
+ luaL_unref (L, LUA_REGISTRYINDEX, cb -> cbref);
+ return 0;
+}
+
+static const luaL_Reg lua_mcabber_command_reg_m[] = {
+ { "del", lua_mcabber_command_del },
+ { "__gc", lua_mcabber_command_gc },
+ { NULL, NULL },
+};
+
+static void lua_command_init (lua_State *L)
+{
+ luaL_newmetatable (L, "mcabber.command");
+ lua_pushvalue (L, -1);
+ lua_setfield (L, -2, "__index");
+ luaL_register (L, NULL, lua_mcabber_command_reg_m);
+ lua_pop (L, 1);
+}
+
// TIMER
typedef struct {
@@ -1700,7 +1752,8 @@
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);
+ lua_hook_init (lua);
+ lua_command_init (lua);
{
char *initfile = expand_filename (settings_opt_get ("lua_init_filename"));
@@ -1766,16 +1819,6 @@
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);
@@ -1789,7 +1832,7 @@
};
hk_run_handlers ("hook-lua-quit", args);
- // hook handlers will be unregistered upon objects destruction
+ // hook handlers and commands will be unregistered upon objects destruction
g_slist_foreach (lua_bgreads, (GFunc) lua_bgreads_destroy, NULL);
g_slist_free (lua_bgreads);
@@ -1808,19 +1851,15 @@
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;
+ cmd_del ("lua");
+
+ lua_close (lua);
+ lua = 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);