/* vim:set et ts=4 sts=4: * * ibus-libpinyin - Intelligent Pinyin engine based on libpinyin for IBus * * Copyright (c) 2010 Peng Wu * * This program 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, 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include "lua-plugin.h" #ifndef lua_objlen #define lua_objlen lua_rawlen #endif #if LUA_VERSION_NUM >= 502 /* ugly hack for lua 5.2 */ #define lua_open luaL_newstate #endif #define IBUS_ENGINE_PLUGIN_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), IBUS_TYPE_ENGINE_PLUGIN, IBusEnginePluginPrivate)) struct _IBusEnginePluginPrivate{ lua_State * L; GArray * lua_commands; /* Array of lua_command_t. */ }; G_DEFINE_TYPE (IBusEnginePlugin, ibus_engine_plugin, G_TYPE_OBJECT); static void lua_command_clone(lua_command_t * command, lua_command_t * new_command){ new_command->command_name = g_strdup(command->command_name); new_command->lua_function_name = g_strdup(command->lua_function_name); new_command->description = g_strdup(command->description); new_command->leading = g_strdup(command->leading); new_command->help = g_strdup(command->help); } static void lua_command_reclaim(lua_command_t * command){ g_free((gpointer)command->command_name); g_free((gpointer)command->lua_function_name); g_free((gpointer)command->description); g_free((gpointer)command->leading); g_free((gpointer)command->help); } static int lua_plugin_init(IBusEnginePluginPrivate * plugin){ g_assert(NULL == plugin->L); /* initialize Lua */ plugin->L = lua_open(); /* enable libs in sandbox */ lua_plugin_openlibs(plugin->L); g_assert ( NULL == plugin->lua_commands ); plugin->lua_commands = g_array_new(TRUE, TRUE, sizeof(lua_command_t)); return 0; } static int lua_plugin_fini(IBusEnginePluginPrivate * plugin){ size_t i; lua_command_t * command; if ( plugin->lua_commands ){ for ( i = 0; i < plugin->lua_commands->len; ++i){ command = &g_array_index(plugin->lua_commands, lua_command_t, i); lua_command_reclaim(command); } g_array_free(plugin->lua_commands, TRUE); plugin->lua_commands = NULL; } lua_close(plugin->L); plugin->L = NULL; return 0; } static void ibus_engine_plugin_finalize (GObject *gobject) { IBusEnginePlugin *self = IBUS_ENGINE_PLUGIN (gobject); /* do some cleaning here. */ lua_plugin_fini(self->priv); /* Chain up to the parent class */ G_OBJECT_CLASS (ibus_engine_plugin_parent_class)->dispose(gobject); } static void ibus_engine_plugin_class_init (IBusEnginePluginClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->finalize = ibus_engine_plugin_finalize; g_type_class_add_private (klass, sizeof (IBusEnginePluginPrivate)); } static void ibus_engine_plugin_init (IBusEnginePlugin *self) { IBusEnginePluginPrivate *priv; self->priv = priv = IBUS_ENGINE_PLUGIN_GET_PRIVATE (self); memset(priv, 0, sizeof(IBusEnginePluginPrivate)); lua_plugin_init(priv); lua_plugin_store_plugin(priv->L, self); } IBusEnginePlugin * ibus_engine_plugin_new(){ IBusEnginePlugin * plugin; plugin = (IBusEnginePlugin *) g_object_new (IBUS_TYPE_ENGINE_PLUGIN, NULL); return plugin; } static void l_message (const char *pname, const char *msg) { if (pname) fprintf(stderr, "%s: ", pname); fprintf(stderr, "%s\n", msg); fflush(stderr); } static int report (lua_State *L, int status) { if (status && !lua_isnil(L, -1)) { const char *msg = lua_tostring(L, -1); if (msg == NULL) msg = "(error object is not a string)"; l_message(NULL, msg); lua_pop(L, 1); } return status; } int ibus_engine_plugin_load_lua_script(IBusEnginePlugin * plugin, const char * filename){ IBusEnginePluginPrivate * priv = IBUS_ENGINE_PLUGIN_GET_PRIVATE(plugin); int status = luaL_dofile(priv->L, filename); return report(priv->L, status); } static gint compare_command(gconstpointer a, gconstpointer b){ lua_command_t * ca = (lua_command_t *) a; lua_command_t * cb = (lua_command_t *) b; return strcmp(ca->command_name, cb->command_name); } gboolean ibus_engine_plugin_add_command(IBusEnginePlugin * plugin, lua_command_t * command){ IBusEnginePluginPrivate * priv = IBUS_ENGINE_PLUGIN_GET_PRIVATE(plugin); GArray * lua_commands = priv->lua_commands; if ( ibus_engine_plugin_lookup_command( plugin, command->command_name) ) return FALSE; lua_command_t new_command; lua_command_clone(command, &new_command); g_array_append_val(lua_commands, new_command); /* Note: need to improve speed here? */ g_array_sort(lua_commands, compare_command); return TRUE; } const lua_command_t * ibus_engine_plugin_lookup_command(IBusEnginePlugin * plugin, const char * command_name){ IBusEnginePluginPrivate * priv = IBUS_ENGINE_PLUGIN_GET_PRIVATE(plugin); GArray * lua_commands = priv->lua_commands; lua_command_t lookup_command = {.command_name = command_name, }; lua_command_t * result = bsearch(&lookup_command, lua_commands->data, lua_commands->len, sizeof(lua_command_t), compare_command); return result; } const GArray * ibus_engine_plugin_get_available_commands(IBusEnginePlugin * plugin){ IBusEnginePluginPrivate * priv = IBUS_ENGINE_PLUGIN_GET_PRIVATE(plugin); return priv->lua_commands; } int ibus_engine_plugin_call(IBusEnginePlugin * plugin, const char * lua_function_name, const char * argument /*optional, maybe NULL.*/){ IBusEnginePluginPrivate * priv = IBUS_ENGINE_PLUGIN_GET_PRIVATE(plugin); int type; int result; lua_State * L = priv->L; /* In google pinyin, argument can't be NULL, but empty string is acceptable. */ if (NULL == argument) argument = ""; /* check whether lua_function_name exists. */ lua_getglobal(L, lua_function_name); type = lua_type(L, -1); if ( LUA_TFUNCTION != type ) return 0; lua_pushstring(L, argument); result = lua_pcall(L, 1, 1, 0); if (result) return 0; type = lua_type(L, -1); if ( LUA_TTABLE == type ){ return lua_objlen(L, -1); } else if (LUA_TNUMBER == type || LUA_TBOOLEAN == type || LUA_TSTRING == type){ return 1; } return 0; } /** * get a candidate from lua return value. */ static const lua_command_candidate_t * ibus_engine_plugin_get_candidate(lua_State * L){ const char * suggest, * help, * content = NULL; lua_command_candidate_t * candidate = malloc(sizeof(lua_command_candidate_t)); memset(candidate, 0, sizeof(lua_command_candidate_t)); int type = lua_type(L, -1); if ( LUA_TTABLE == type ){ lua_pushliteral(L, "suggest"); lua_gettable(L, -2); lua_pushliteral(L, "help"); lua_gettable(L, -3); suggest = lua_tostring(L, -2); help = lua_tostring(L, -1); candidate->suggest = g_strdup(suggest); candidate->help = g_strdup(help); lua_pop(L, 2); } else if (LUA_TNUMBER == type || LUA_TBOOLEAN == type || LUA_TSTRING == type) { content = lua_tostring(L, -1); candidate->content = g_strdup(content); } return candidate; } /** * retrieve the retval string value. (value has been copied.) */ const lua_command_candidate_t * ibus_engine_plugin_get_retval(IBusEnginePlugin * plugin){ IBusEnginePluginPrivate * priv = IBUS_ENGINE_PLUGIN_GET_PRIVATE(plugin); lua_command_candidate_t * result = NULL; int type; lua_State * L = priv->L; type = lua_type(L ,-1); if ( LUA_TNUMBER == type || LUA_TBOOLEAN == type || LUA_TSTRING == type) { result = malloc(sizeof(lua_command_candidate_t)); memset(result, 0, sizeof(lua_command_candidate_t)); result->content = g_strdup(lua_tostring(L, -1)); lua_pop(L, 1); } else if( LUA_TTABLE == type ){ lua_pushinteger(L, 1); lua_gettable(L, -2); result = (lua_command_candidate_t *)ibus_engine_plugin_get_candidate(L); lua_pop(L, 2); } return (const lua_command_candidate_t *)result; } /** * retrieve the array of string values. (string values have been copied.) */ GArray * ibus_engine_plugin_get_retvals(IBusEnginePlugin * plugin){ IBusEnginePluginPrivate * priv = IBUS_ENGINE_PLUGIN_GET_PRIVATE(plugin); lua_State * L = priv->L; int elem_num; int type; GArray * result = NULL; int i; const lua_command_candidate_t * candidate = NULL; type = lua_type(L, -1); if ( LUA_TTABLE != type ) return result; result = g_array_new(TRUE, TRUE, sizeof(lua_command_candidate_t *)); elem_num = lua_objlen(L, -1); for ( i = 0; i < elem_num; ++i ){ lua_pushinteger(L, i + 1); lua_gettable(L, -2); candidate = ibus_engine_plugin_get_candidate(L); lua_pop(L, 1); g_array_append_val(result, candidate); } lua_pop(L, 1); return result; } void ibus_engine_plugin_free_candidate(lua_command_candidate_t * candidate){ g_free((gpointer)candidate->content); g_free((gpointer)candidate->suggest); g_free((gpointer)candidate->help); }