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