# HG changeset patch # User Myhailo Danylenko # Date 1269983541 -10800 # Node ID b00f9ce82016c50db6b8bc790ea8cda546b6abd2 # Parent 270ce1cfc823047fce97298f83d7bac76616421e Update hooks interface according to upstream diff -r 270ce1cfc823 -r b00f9ce82016 main.c --- a/main.c Wed Mar 31 00:09:47 2010 +0300 +++ b/main.c Wed Mar 31 00:12:21 2010 +0300 @@ -25,7 +25,7 @@ #include // getenv #include // strcmp -#include // scr_LogPrint +#include // scr_log_print #include // scr_Beep, scr_WriteIncomingMessage #include // HBB_PREFIX_INFO #include // process_command, cmd_add, cmd_del @@ -60,7 +60,7 @@ static module_info_t info_lua_experimental = { .branch = "experimental", - .api = 5, + .api = 10, .version = PROJECT_VERSION, .description = DESCRIPTION, .requires = NULL, @@ -71,7 +71,7 @@ module_info_t info_lua = { .branch = "dev", - .api = 4, + .api = 11, .version = PROJECT_VERSION, .description = DESCRIPTION, .requires = NULL, @@ -128,7 +128,7 @@ } luaL_pushresult (&B); - scr_LogPrint (LPRINT_LOGNORM | LPRINT_NOTUTF8, lua_tostring (L, -1)); + scr_log_print (LPRINT_LOGNORM | LPRINT_NOTUTF8, lua_tostring (L, -1)); return 0; } @@ -151,10 +151,10 @@ g_free (fname); } - if (ret = luaL_loadfile (L, path)) - scr_LogPrint (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_LogPrint (LPRINT_LOGNORM, "lua: Runtime error in file %s: %s", path, lua_tostring (L, -1)); + 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) @@ -229,7 +229,7 @@ { int type = luaL_checkenum_multi (L, 1, lua_lprint); lua_concat (L, lua_gettop (L) - 1); - scr_LogPrint (type, lua_tostring (L, -1)); + scr_log_print (type, lua_tostring (L, -1)); return 0; } @@ -398,7 +398,7 @@ char *jid = to_utf8 (luaL_checkstring (L, 1)); char *to = jidtodisp (jid); char *mesg = to_utf8 (luaL_checkstring (L, 2)); - scr_WriteIncomingMessage (to, mesg, 0, HBB_PREFIX_INFO, 0); + scr_write_incoming_message (to, mesg, 0, HBB_PREFIX_INFO, 0); g_free (mesg); g_free (to); g_free (jid); @@ -409,7 +409,7 @@ /// Beeps with system speaker. static int lua_main_beep (lua_State *L) { - scr_Beep (); + scr_beep (); return 0; } @@ -464,7 +464,7 @@ // 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_RosterSearch (jid); + scr_roster_search (jid); g_free (jid); return 0; } else { // Get @@ -656,7 +656,7 @@ luaL_pushenum (cb->L, context, lua_event_context); lua_pushstring (cb -> L, arg); if (lua_pcall (cb->L, 2, 1, 0)) { - scr_LogPrint (LPRINT_LOGNORM, "lua: Event callback execution error: %s", lua_tostring (cb->L, -1)); + scr_log_print (LPRINT_LOGNORM, "lua: Event callback execution error: %s", lua_tostring (cb->L, -1)); lua_pop (cb->L, 1); } @@ -894,7 +894,7 @@ lua_pushstring (cb->L, args); if (lua_pcall (cb->L, 1, 0, 0)) { - scr_LogPrint (LPRINT_LOGNORM, "lua: Command execution error: %s", lua_tostring (cb->L, -1)); + scr_log_print (LPRINT_LOGNORM, "lua: Command execution error: %s", lua_tostring (cb->L, -1)); lua_pop (cb->L, 1); } } @@ -963,6 +963,7 @@ char *word = to_utf8 (luaL_checkstring (L, 2)); // XXX compl_add_category_word (cid, word); g_free (word); + return 0; } /// main.del_completion @@ -974,6 +975,7 @@ char *word = to_utf8 (luaL_checkstring (L, 2)); // XXX compl_del_category_word (cid, word); g_free (word); + return 0; } /// main.command @@ -1071,7 +1073,7 @@ int ret; lua_rawgeti (cb->L, LUA_REGISTRYINDEX, cb->reference); if (lua_pcall (cb->L, 0, 1, 0)) { - scr_LogPrint (LPRINT_LOGNORM, "lua: Timer callback execution error: %s", lua_tostring (cb->L, -1)); + scr_log_print (LPRINT_LOGNORM, "lua: Timer callback execution error: %s", lua_tostring (cb->L, -1)); lua_pop (cb->L, 1); return FALSE; } @@ -1140,7 +1142,7 @@ 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_LogPrint (LPRINT_LOGNORM, "lua: Bgread callback execution error: %s", lua_tostring (cb->L, -1)); + scr_log_print (LPRINT_LOGNORM, "lua: Bgread callback execution error: %s", lua_tostring (cb->L, -1)); lua_pop (cb->L, 1); return FALSE; } @@ -1155,7 +1157,7 @@ lua_rawgeti (cb->L, LUA_REGISTRYINDEX, cb->reference); lua_pushnil (cb->L); if (lua_pcall (cb->L, 1, 1, 0)) { - scr_LogPrint (LPRINT_LOGNORM, "lua: Bgread callback execution error: %s", lua_tostring (cb->L, -1)); + scr_log_print (LPRINT_LOGNORM, "lua: Bgread callback execution error: %s", lua_tostring (cb->L, -1)); lua_pop (cb->L, 1); return FALSE; } @@ -1209,46 +1211,63 @@ return 0; } -// MAIN INITIALIZATION CODE +// HOOK HANDLING -#ifdef LLM_LOG_HANDLER -// FIXME: this should not be here -guint lua_lm_log_handler_id; +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; -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_LogPrint (LPRINT_LOGNORM, "%s: %s", domain, message); -} -#endif +/// 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 }, +}; -static void do_lua(char *arg, lua_State *L) +/// 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) { - if (luaL_loadbuffer (L, arg, strlen (arg), "line")) { - scr_LogPrint (LPRINT_LOGNORM, "lua: Compilation error: %s", lua_tostring (L, -1)); - lua_pop (L, 1); - return; - } - - if (lua_pcall (L, 0, 0, 0)) { - scr_LogPrint (LPRINT_NORMAL, "lua: Runtime error: %s", lua_tostring(L, -1)); - lua_pop (L, 1); - return; - } -} + lua_hook_t *cb = data; + hk_arg_t *arg = args; + guint ret = HOOK_HANDLER_RESULT_ALLOW_MORE_HANDLERS; + lua_State *L = cb -> L; -static void lua_hook (guint32 hookid, hk_arg_t *args, lua_State *L) -{ - hk_arg_t *arg = args; - const char *hook = settings_opt_get ("lua_hook_function"); - if (!hook) - return; - lua_getglobal (L, hook); + 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; + 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); @@ -1259,10 +1278,272 @@ g_free (value); arg++; } - if (lua_pcall (L, 1, 0, 0)) { - scr_LogPrint (LPRINT_NORMAL, "lua: Error in hook handler: %s", lua_tostring (L, -1)); + + 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) { @@ -1303,7 +1584,29 @@ 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 @@ -1355,32 +1658,31 @@ 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_LogPrint (LPRINT_LOGNORM, "lua: Cannot determine config file name"); + scr_log_print (LPRINT_LOGNORM, "lua: Cannot determine config file name"); else { if (luaL_loadfile(lua, initfile)) { - scr_LogPrint (LPRINT_LOGNORM, "lua: Unable to compile rc file: %s", lua_tostring (lua, -1)); + 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_LogPrint (LPRINT_LOGNORM, "lua: Runtime error in rc file: %s", lua_tostring(lua, -1)); + scr_log_print (LPRINT_LOGNORM, "lua: Runtime error in rc file: %s", lua_tostring(lua, -1)); lua_pop (lua, 1); } else - scr_LogPrint (LPRINT_LOGNORM, "lua: Loaded %s", initfile); + scr_log_print (LPRINT_LOGNORM, "lua: Loaded %s", initfile); g_free (initfile); } } - hk_add_handler ((hk_handler_t) lua_hook, ~((guint32)0), lua); - { hk_arg_t args[] = { - { "hook", "hook-start" }, - { NULL, NULL }, + { NULL, NULL }, }; - lua_hook (0, args, lua); + hk_run_handlers("hook-lua-start", args); } } @@ -1441,12 +1743,11 @@ { if (lua) { hk_arg_t args[] = { - { "hook", "hook-quit" }, - { NULL, NULL }, + { NULL, NULL }, }; - lua_hook (0, args, lua); + hk_run_handlers ("hook-lua-quit", args); - hk_del_handler ((hk_handler_t) lua_hook, lua); + // hook handlers will be unregistered upon objects destruction g_slist_foreach (lua_bgreads, (GFunc) lua_bgreads_destroy, NULL); g_slist_free (lua_bgreads);