lua: Make better use of GLib

- Use GPtrArray everywhere
- Remove unessary NULL checks
- Make use of g_clear macros
- Use g_str_has_suffix
This commit is contained in:
Patrick Griffis 2016-04-04 08:51:03 -04:00
parent 2fe52bbf58
commit 4abcd53044

View File

@ -59,10 +59,6 @@ static hexchat_plugin *ph;
#define luaL_setfuncs(L, r, n) luaL_register(L, NULL, r)
#endif
#define ARRAY_RESIZE(A, N) ((A) = g_realloc((A), (N) * sizeof(*(A))))
#define ARRAY_GROW(A, N) ((N)++, ARRAY_RESIZE(A, N))
#define ARRAY_SHRINK(A, N) ((N)--, ARRAY_RESIZE(A, N))
typedef struct
{
hexchat_hook *hook;
@ -79,12 +75,10 @@ typedef struct
hexchat_plugin *handle;
char *filename;
lua_State *state;
GPtrArray *hooks;
GPtrArray *unload_hooks;
int traceback;
int status;
hook_info **hooks;
size_t num_hooks;
hook_info **unload_hooks;
size_t num_unload_hooks;
}
script_info;
@ -245,14 +239,13 @@ static int api_hexchat_strip(lua_State *L)
static void register_hook(hook_info *hook)
{
script_info *info = get_info(hook->state);
ARRAY_GROW(info->hooks, info->num_hooks);
info->hooks[info->num_hooks - 1] = hook;
g_ptr_array_add(info->hooks, hook);
}
static void free_hook(hook_info *hook)
{
lua_State *L = hook->state;
luaL_unref(L, LUA_REGISTRYINDEX, hook->ref);
if (hook->state)
luaL_unref(hook->state, LUA_REGISTRYINDEX, hook->ref);
if(hook->hook)
hexchat_unhook(ph, hook->hook);
g_free(hook);
@ -261,27 +254,13 @@ static void free_hook(hook_info *hook)
static int unregister_hook(hook_info *hook)
{
script_info *info = get_info(hook->state);
size_t i;
for(i = 0; i < info->num_hooks; i++)
if(info->hooks[i] == hook)
{
size_t j;
free_hook(hook);
for(j = i; j < info->num_hooks - 1; j++)
info->hooks[j] = info->hooks[j + 1];
ARRAY_SHRINK(info->hooks, info->num_hooks);
return 1;
}
for(i = 0; i < info->num_unload_hooks; i++)
if(info->unload_hooks[i] == hook)
{
size_t j;
free_hook(hook);
for(j = i; j < info->num_unload_hooks - 1; j++)
info->unload_hooks[j] = info->unload_hooks[j + 1];
ARRAY_SHRINK(info->unload_hooks, info->num_unload_hooks);
return 1;
}
if (g_ptr_array_remove_fast(info->hooks, hook))
return 1;
if (g_ptr_array_remove_fast(info->unload_hooks, hook))
return 1;
return 0;
}
@ -651,8 +630,8 @@ static int api_hexchat_hook_unload(lua_State *L)
luaL_newmetatable(L, "hook");
lua_setmetatable(L, -2);
script = get_info(info->state);
ARRAY_GROW(script->unload_hooks, script->num_unload_hooks);
script->unload_hooks[script->num_unload_hooks - 1] = info;
g_ptr_array_add(script->unload_hooks, info);
return 1;
}
@ -1207,8 +1186,7 @@ static void patch_clibs(lua_State *L)
lua_pop(L, 1);
}
script_info **scripts = NULL;
size_t num_scripts = 0;
static GPtrArray *scripts;
static char *expand_buffer = NULL;
static char const *expand_path(char const *path)
@ -1220,8 +1198,7 @@ static char const *expand_path(char const *path)
{
if(!path[1] || path[1] == '/')
{
if(expand_buffer)
g_free(expand_buffer);
g_free(expand_buffer);
expand_buffer = g_build_filename(g_get_home_dir(), path + 1, NULL);
return expand_buffer;
}
@ -1239,8 +1216,8 @@ static char const *expand_path(char const *path)
slash_pos = strchr(path, '/');
if(!slash_pos)
return pw->pw_dir;
if(expand_buffer)
g_free(expand_buffer);
g_free(expand_buffer);
expand_buffer = g_strconcat(pw->pw_dir, slash_pos, NULL);
return expand_buffer;
}
@ -1253,18 +1230,15 @@ static char const *expand_path(char const *path)
else
#endif
{
if(expand_buffer)
g_free(expand_buffer);
g_free(expand_buffer);
expand_buffer = g_build_filename(hexchat_get_info(ph, "configdir"), "addons", path, NULL);
return expand_buffer;
}
}
static int is_lua_file(char const *file)
static inline int is_lua_file(char const *file)
{
char const *ext1 = ".lua";
char const *ext2 = ".luac";
return (strlen(file) >= strlen(ext1) && !strcmp(file + strlen(file) - strlen(ext1), ext1)) || (strlen(file) >= strlen(ext2) && !strcmp(file + strlen(file) - strlen(ext2), ext2));
return g_str_has_suffix(file, ".lua") || g_str_has_suffix(file, ".luac");
}
static void prepare_state(lua_State *L, script_info *info)
@ -1288,20 +1262,64 @@ static void prepare_state(lua_State *L, script_info *info)
lua_pop(L, 1);
}
struct unload_userdata {
lua_State *L;
int base;
};
static void run_unload_hook(hook_info *hook, struct unload_userdata *ud)
{
lua_State *L = ud->L;
int base = ud->base;
lua_rawgeti(L, LUA_REGISTRYINDEX, hook->ref);
if(lua_pcall(L, 0, 0, base))
{
char const *error = lua_tostring(L, -1);
lua_pop(L, 2);
hexchat_printf(ph, "Lua error in unload hook: %s", error ? error : "(non-string error)");
}
}
static void destroy_script(script_info *info)
{
if (info)
{
if (info->state)
{
struct unload_userdata data = {info->state, lua_gettop(info->state)};
lua_rawgeti(info->state, LUA_REGISTRYINDEX, info->traceback);
g_ptr_array_foreach(info->unload_hooks, (GFunc)run_unload_hook, &data);
}
g_clear_pointer(&info->hooks, g_ptr_array_unref);
g_clear_pointer(&info->unload_hooks, g_ptr_array_unref);
g_clear_pointer(&info->state, lua_close);
if (info->handle)
hexchat_plugingui_remove(ph, info->handle);
g_free(info->filename);
g_free(info->name);
g_free(info->description);
g_free(info->version);
g_free(info);
}
}
static script_info *create_script(char const *file)
{
int base;
char *filename_fs;
lua_State *L;
script_info *info = g_new0(script_info, 1);
info->hooks = g_ptr_array_new_with_free_func((GDestroyNotify)free_hook);
info->unload_hooks = g_ptr_array_new_with_free_func((GDestroyNotify)free_hook);
info->filename = g_strdup(expand_path(file));
L = luaL_newstate();
info->state = L;
if(!L)
{
hexchat_print(ph, "\00304Could not allocate memory for the script");
g_free(info->filename);
g_free(info);
destroy_script(info);
return NULL;
}
prepare_state(L, info);
@ -1311,18 +1329,14 @@ static script_info *create_script(char const *file)
if(!filename_fs)
{
hexchat_printf(ph, "Invalid filename: %s", info->filename);
lua_close(L);
g_free(info->filename);
g_free(info);
destroy_script(info);
return NULL;
}
if(luaL_loadfile(L, filename_fs))
{
g_free(filename_fs);
hexchat_printf(ph, "Lua syntax error: %s", luaL_optstring(L, -1, ""));
lua_close(L);
g_free(info->filename);
g_free(info);
destroy_script(info);
return NULL;
}
g_free(filename_fs);
@ -1330,127 +1344,80 @@ static script_info *create_script(char const *file)
if(lua_pcall(L, 0, 0, base))
{
char const *error = lua_tostring(L, -1);
size_t i;
hexchat_printf(ph, "Lua error: %s", error ? error : "(non-string error)");
for(i = 0; i < info->num_hooks; i++)
free_hook(info->hooks[i]);
for(i = 0; i < info->num_unload_hooks; i++)
free_hook(info->unload_hooks[i]);
lua_close(L);
if(info->name)
{
g_free(info->name);
g_free(info->description);
g_free(info->version);
hexchat_plugingui_remove(ph, info->handle);
}
g_free(info->filename);
g_free(info);
return 0;
destroy_script(info);
return NULL;
}
lua_pop(L, 1);
if(!info->name)
{
size_t i;
hexchat_printf(ph, "Lua script didn't register with hexchat.register");
for(i = 0; i < info->num_hooks; i++)
free_hook(info->hooks[i]);
for(i = 0; i < info->num_unload_hooks; i++)
free_hook(info->unload_hooks[i]);
lua_close(L);
g_free(info->filename);
g_free(info);
return 0;
destroy_script(info);
return NULL;
}
return info;
}
static void destroy_script(script_info *info)
{
lua_State *L;
size_t i;
int base;
for(i = 0; i < info->num_hooks; i++)
free_hook(info->hooks[i]);
L = info->state;
lua_rawgeti(L, LUA_REGISTRYINDEX, info->traceback);
base = lua_gettop(L);
for(i = 0; i < info->num_unload_hooks; i++)
{
hook_info *hook = info->unload_hooks[i];
lua_rawgeti(L, LUA_REGISTRYINDEX, hook->ref);
if(lua_pcall(L, 0, 0, base))
{
char const *error = lua_tostring(L, -1);
lua_pop(L, 2);
hexchat_printf(ph, "Lua error in unload hook: %s", error ? error : "(non-string error)");
}
free_hook(hook);
}
lua_close(L);
g_free(info->filename);
g_free(info->name);
g_free(info->description);
g_free(info->version);
hexchat_plugingui_remove(ph, info->handle);
g_free(info);
}
static void load_script(char const *file)
{
script_info *info = create_script(file);
if(info)
{
ARRAY_GROW(scripts, num_scripts);
scripts[num_scripts - 1] = info;
g_ptr_array_add(scripts, info);
check_deferred(info);
}
}
static script_info *get_script_by_file(char const *filename)
{
char const *expanded = expand_path(filename);
guint i;
for(i = 0; i < scripts->len; i++)
{
script_info *script = scripts->pdata[i];
if(!strcmp(script->filename, expanded))
{
return script;
}
}
return NULL;
}
static int unload_script(char const *filename)
{
size_t i;
char const *expanded = expand_path(filename);
for(i = 0; i < num_scripts; i++)
if(!strcmp(scripts[i]->filename, expanded))
{
if(scripts[i]->status & STATUS_ACTIVE)
scripts[i]->status |= STATUS_DEFERRED_UNLOAD;
else
{
size_t j;
destroy_script(scripts[i]);
for(j = i; j < num_scripts - 1; j++)
scripts[j] = scripts[j + 1];
ARRAY_SHRINK(scripts, num_scripts);
}
return 1;
}
return 0;
script_info *script = get_script_by_file(filename);
if (!script)
return 0;
if(script->status & STATUS_ACTIVE)
script->status |= STATUS_DEFERRED_UNLOAD;
else
g_ptr_array_remove_fast(scripts, script);
return 1;
}
static int reload_script(char const *filename)
{
size_t i;
char const *expanded = expand_path(filename);
for(i = 0; i < num_scripts; i++)
if(!strcmp(scripts[i]->filename, expanded))
{
if(scripts[i]->status & STATUS_ACTIVE)
scripts[i]->status |= STATUS_DEFERRED_RELOAD;
else
{
size_t j;
destroy_script(scripts[i]);
for(j = i; j < num_scripts - 1; j++)
scripts[j] = scripts[j + 1];
ARRAY_SHRINK(scripts, num_scripts);
load_script(filename);
}
return 1;
}
return 0;
script_info *script = get_script_by_file(filename);
if (!script)
return 0;
if(script->status & STATUS_ACTIVE)
script->status |= STATUS_DEFERRED_RELOAD;
else
{
char *filename = g_strdup(script->filename);
g_ptr_array_remove_fast(scripts, script);
load_script(filename);
g_free(filename);
}
return 1;
}
static void autoload_scripts(void)
@ -1494,30 +1461,11 @@ static void destroy_interpreter(void)
{
if(interp)
{
lua_State *L;
size_t i;
int base;
for(i = 0; i < interp->num_hooks; i++)
free_hook(interp->hooks[i]);
L = interp->state;
lua_rawgeti(L, LUA_REGISTRYINDEX, interp->traceback);
base = lua_gettop(L);
for(i = 0; i < interp->num_unload_hooks; i++)
{
hook_info *hook = interp->unload_hooks[i];
lua_rawgeti(L, LUA_REGISTRYINDEX, hook->ref);
if(lua_pcall(L, 0, 0, base))
{
char const *error = lua_tostring(L, -1);
lua_pop(L, 2);
hexchat_printf(ph, "Lua error in unload hook: %s", error ? error : "(non-string error)");
}
free_hook(hook);
}
lua_close(L);
g_free(interp);
interp = NULL;
lua_rawgeti(interp->state, LUA_REGISTRYINDEX, interp->traceback);
g_clear_pointer(&interp->hooks, g_ptr_array_unref);
g_clear_pointer(&interp->unload_hooks, g_ptr_array_unref);
g_clear_pointer(&interp->state, lua_close);
g_clear_pointer(&interp, g_free);
}
}
@ -1610,16 +1558,7 @@ void check_deferred(script_info *info)
info->status &= ~STATUS_ACTIVE;
if(info->status & STATUS_DEFERRED_UNLOAD)
{
size_t i;
for(i = 0; i < num_scripts; i++)
if(scripts[i] == info)
{
size_t j;
destroy_script(info);
for(j = i; j < num_scripts - 1; j++)
scripts[j] = scripts[j + 1];
ARRAY_SHRINK(scripts, num_scripts);
}
g_ptr_array_remove_fast(scripts, info);
}
else if(info->status & STATUS_DEFERRED_RELOAD)
{
@ -1630,20 +1569,10 @@ void check_deferred(script_info *info)
}
else
{
size_t i;
for(i = 0; i < num_scripts; i++)
if(scripts[i] == info)
{
size_t j;
char *filename = g_strdup(info->filename);
destroy_script(info);
for(j = i; j < num_scripts - 1; j++)
scripts[j] = scripts[j + 1];
ARRAY_SHRINK(scripts, num_scripts);
load_script(filename);
g_free(filename);
break;
}
char *filename = g_strdup(info->filename);
g_ptr_array_remove_fast(scripts, info);
load_script(filename);
g_free(filename);
}
}
}
@ -1671,18 +1600,15 @@ static int command_lua(char *word[], char *word_eol[], void *userdata)
}
else if(!strcmp(word[2], "inject"))
{
char const *expanded = expand_path(word[3]);
size_t i;
int found = 0;
for(i = 0; i < num_scripts; i++)
if(!strcmp(scripts[i]->filename, expanded))
{
inject_string(scripts[i], word_eol[4]);
found = 1;
break;
}
if(!found)
script_info *script = get_script_by_file(word[3]);
if (script)
{
inject_string(script, word_eol[4]);
}
else
{
hexchat_printf(ph, "Could not find a script by the name '%s'", word[3]);
}
}
else if(!strcmp(word[2], "reset"))
{
@ -1696,9 +1622,12 @@ static int command_lua(char *word[], char *word_eol[], void *userdata)
}
else if(!strcmp(word[2], "list"))
{
size_t i;
for(i = 0; i < num_scripts; i++)
hexchat_printf(ph, "%s %s: %s (%s)", scripts[i]->name, scripts[i]->version, scripts[i]->description, scripts[i]->filename);
guint i;
for(i = 0; i < scripts->len; i++)
{
script_info *info = scripts->pdata[i];
hexchat_printf(ph, "%s %s: %s (%s)", info->name, info->version, info->description, info->filename);
}
if(interp)
hexchat_printf(ph, "%s %s", interp->name, plugin_version);
}
@ -1731,6 +1660,7 @@ G_MODULE_EXPORT int hexchat_plugin_init(hexchat_plugin *plugin_handle, char **na
hexchat_printf(ph, "%s version %s loaded.\n", plugin_name, plugin_version);
scripts = g_ptr_array_new_with_free_func((GDestroyNotify)destroy_script);
create_interpreter();
if(!arg)
@ -1740,28 +1670,26 @@ G_MODULE_EXPORT int hexchat_plugin_init(hexchat_plugin *plugin_handle, char **na
G_MODULE_EXPORT int hexchat_plugin_deinit(hexchat_plugin *plugin_handle)
{
size_t i;
int found = 0;
for(i = 0; i < num_scripts; i++)
if(scripts[i]->status & STATUS_ACTIVE)
guint i;
gboolean active = FALSE;
for(i = 0; i < scripts->len; i++)
{
if(((script_info*)scripts->pdata[i])->status & STATUS_ACTIVE)
{
found = 1;
active = TRUE;
break;
}
if(!found && interp && interp->status & STATUS_ACTIVE)
found = 1;
if(found)
}
if(!active && interp && interp->status & STATUS_ACTIVE)
active = TRUE;
if(active)
{
hexchat_print(ph, "\00304Cannot unload the lua plugin while there are active states");
return 0;
}
destroy_interpreter();
for(i = 0; i < num_scripts; i++)
destroy_script(scripts[i]);
num_scripts = 0;
ARRAY_RESIZE(scripts, num_scripts);
if(expand_buffer)
g_free(expand_buffer);
g_clear_pointer(&scripts, g_ptr_array_unref);
g_clear_pointer(&expand_buffer, g_free);
return 1;
}