mirror of
https://github.com/profanity-im/profanity.git
synced 2024-12-04 14:46:46 -05:00
Free plugins commands on quit
This commit is contained in:
parent
606a860bdc
commit
71879a3f64
@ -123,6 +123,7 @@ unittest_sources = \
|
||||
tests/unittests/test_cmd_join.c tests/unittests/test_cmd_join.h \
|
||||
tests/unittests/test_cmd_roster.c tests/unittests/test_cmd_roster.h \
|
||||
tests/unittests/test_cmd_disconnect.c tests/unittests/test_cmd_disconnect.h \
|
||||
tests/unittests/test_callbacks.c tests/unittests/test_callbacks.h \
|
||||
tests/unittests/unittests.c
|
||||
|
||||
functionaltest_sources = \
|
||||
|
@ -3809,7 +3809,7 @@ cmd_form(ProfWin *window, const char *const command, gchar **args)
|
||||
} else {
|
||||
mucconfwin_form_help(confwin);
|
||||
|
||||
const gchar **help_text = NULL;
|
||||
gchar **help_text = NULL;
|
||||
Command *command = cmd_get("/form");
|
||||
|
||||
if (command) {
|
||||
|
@ -39,11 +39,11 @@
|
||||
|
||||
// Command help strings
|
||||
typedef struct cmd_help_t {
|
||||
const gchar *tags[20];
|
||||
const gchar *synopsis[50];
|
||||
const gchar *desc;
|
||||
const gchar *args[128][2];
|
||||
const gchar *examples[20];
|
||||
gchar *tags[20];
|
||||
gchar *synopsis[50];
|
||||
gchar *desc;
|
||||
gchar *args[128][2];
|
||||
gchar *examples[20];
|
||||
} CommandHelp;
|
||||
|
||||
/*
|
||||
|
@ -107,18 +107,21 @@ api_cons_bad_cmd_usage(const char *const cmd)
|
||||
|
||||
void
|
||||
api_register_command(const char *const plugin_name, const char *command_name, int min_args, int max_args,
|
||||
const char **synopsis, const char *description, const char *arguments[][2], const char **examples, void *callback,
|
||||
void(*callback_exec)(PluginCommand *command, gchar **args))
|
||||
const char **synopsis, const char *description, const char *arguments[][2], const char **examples,
|
||||
void *callback, void(*callback_exec)(PluginCommand *command, gchar **args), void(*callback_destroy)(void *callback))
|
||||
{
|
||||
PluginCommand *command = malloc(sizeof(PluginCommand));
|
||||
command->command_name = command_name;
|
||||
command->command_name = strdup(command_name);
|
||||
command->min_args = min_args;
|
||||
command->max_args = max_args;
|
||||
command->callback = callback;
|
||||
command->callback_exec = callback_exec;
|
||||
command->callback_destroy = callback_destroy;
|
||||
|
||||
CommandHelp *help = malloc(sizeof(CommandHelp));
|
||||
|
||||
help->tags[0] = NULL;
|
||||
|
||||
int i = 0;
|
||||
for (i = 0; synopsis[i] != NULL; i++) {
|
||||
help->synopsis[i] = strdup(synopsis[i]);
|
||||
@ -140,16 +143,17 @@ api_register_command(const char *const plugin_name, const char *command_name, in
|
||||
|
||||
command->help = help;
|
||||
|
||||
callbacks_add_command(command);
|
||||
callbacks_add_command(plugin_name, command);
|
||||
}
|
||||
|
||||
void
|
||||
api_register_timed(const char *const plugin_name, void *callback, int interval_seconds,
|
||||
void (*callback_exec)(PluginTimedFunction *timed_function))
|
||||
void (*callback_exec)(PluginTimedFunction *timed_function), void(*callback_destroy)(void *callback))
|
||||
{
|
||||
PluginTimedFunction *timed_function = malloc(sizeof(PluginTimedFunction));
|
||||
timed_function->callback = callback;
|
||||
timed_function->callback_exec = callback_exec;
|
||||
timed_function->callback_destroy = callback_destroy;
|
||||
timed_function->interval_seconds = interval_seconds;
|
||||
timed_function->timer = g_timer_new();
|
||||
|
||||
|
@ -52,9 +52,9 @@ char** api_get_current_occupants(void);
|
||||
|
||||
void api_register_command(const char *const plugin_name, const char *command_name, int min_args, int max_args,
|
||||
const char **synopsis, const char *description, const char *arguments[][2], const char **examples,
|
||||
void *callback, void(*callback_func)(PluginCommand *command, gchar **args));
|
||||
void *callback, void(*callback_func)(PluginCommand *command, gchar **args), void(*callback_destroy)(void *callback));
|
||||
void api_register_timed(const char *const plugin_name, void *callback, int interval_seconds,
|
||||
void (*callback_func)(PluginTimedFunction *timed_function));
|
||||
void (*callback_func)(PluginTimedFunction *timed_function), void(*callback_destroy)(void *callback));
|
||||
|
||||
void api_completer_add(const char *const plugin_name, const char *key, char **items);
|
||||
void api_completer_remove(const char *key, char **items);
|
||||
@ -70,8 +70,8 @@ void api_win_create(
|
||||
const char *const plugin_name,
|
||||
const char *tag,
|
||||
void *callback,
|
||||
void(*destroy)(void *callback),
|
||||
void(*callback_func)(PluginWindowCallback *window_callback, char *tag, char *line));
|
||||
void(*callback_func)(PluginWindowCallback *window_callback, char *tag, char *line),
|
||||
void(*destroy)(void *callback));
|
||||
int api_win_focus(const char *tag);
|
||||
int api_win_show(const char *tag, const char *line);
|
||||
int api_win_show_themed(const char *tag, const char *const group, const char *const key, const char *const def, const char *line);
|
||||
|
@ -90,7 +90,9 @@ c_api_register_command(const char *filename, const char *command_name, int min_a
|
||||
CommandWrapper *wrapper = malloc(sizeof(CommandWrapper));
|
||||
wrapper->func = callback;
|
||||
api_register_command(plugin_name, command_name, min_args, max_args, synopsis,
|
||||
description, arguments, examples, wrapper, c_command_callback);
|
||||
description, arguments, examples, wrapper, c_command_callback, free);
|
||||
|
||||
free(plugin_name);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -101,7 +103,9 @@ c_api_register_timed(const char *filename, void(*callback)(void), int interval_s
|
||||
|
||||
TimedWrapper *wrapper = malloc(sizeof(TimedWrapper));
|
||||
wrapper->func = callback;
|
||||
api_register_timed(plugin_name, wrapper, interval_seconds, c_timed_callback);
|
||||
api_register_timed(plugin_name, wrapper, interval_seconds, c_timed_callback, free);
|
||||
|
||||
free(plugin_name);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -111,6 +115,8 @@ c_api_completer_add(const char *filename, const char *key, char **items)
|
||||
log_debug("Autocomplete add %s for %s", key, plugin_name);
|
||||
|
||||
api_completer_add(plugin_name, key, items);
|
||||
|
||||
free(plugin_name);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -205,7 +211,9 @@ c_api_win_create(const char *filename, char *tag, void(*callback)(char *tag, cha
|
||||
|
||||
WindowWrapper *wrapper = malloc(sizeof(WindowWrapper));
|
||||
wrapper->func = callback;
|
||||
api_win_create(plugin_name, tag, wrapper, free, c_window_callback);
|
||||
api_win_create(plugin_name, tag, wrapper, c_window_callback, free);
|
||||
|
||||
free(plugin_name);
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -44,7 +44,7 @@
|
||||
|
||||
#include "ui/ui.h"
|
||||
|
||||
static GSList *p_commands = NULL;
|
||||
static GHashTable *p_commands = NULL;
|
||||
static GSList *p_timed_functions = NULL;
|
||||
static GHashTable *p_window_callbacks = NULL;
|
||||
|
||||
@ -57,22 +57,88 @@ _free_window_callback(PluginWindowCallback *window_callback)
|
||||
free(window_callback);
|
||||
}
|
||||
|
||||
void
|
||||
callbacks_init(void)
|
||||
//typedef struct cmd_help_t {
|
||||
// const gchar *tags[20];
|
||||
// const gchar *synopsis[50];
|
||||
// const gchar *desc;
|
||||
// const gchar *args[128][2];
|
||||
// const gchar *examples[20];
|
||||
//} CommandHelp;
|
||||
|
||||
static void
|
||||
_free_command_help(CommandHelp *help)
|
||||
{
|
||||
p_window_callbacks = g_hash_table_new_full(g_str_hash, g_str_equal, free, (GDestroyNotify)_free_window_callback);
|
||||
int i = 0;
|
||||
while (help->tags[i] != NULL) {
|
||||
free(help->tags[i++]);
|
||||
}
|
||||
|
||||
i = 0;
|
||||
while (help->synopsis[i] != NULL) {
|
||||
free(help->synopsis[i++]);
|
||||
}
|
||||
|
||||
free(help->desc);
|
||||
|
||||
i = 0;
|
||||
while (help->args[i] != NULL && help->args[i][0] != NULL) {
|
||||
free(help->args[i][0]);
|
||||
free(help->args[i][1]);
|
||||
i++;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
while (help->examples[i] != NULL) {
|
||||
free(help->examples[i++]);
|
||||
}
|
||||
|
||||
free(help);
|
||||
}
|
||||
|
||||
static void
|
||||
_free_command(PluginCommand *command)
|
||||
{
|
||||
if (command->callback_destroy) {
|
||||
command->callback_destroy(command->callback);
|
||||
}
|
||||
free(command->command_name);
|
||||
|
||||
_free_command_help(command->help);
|
||||
free(command);
|
||||
}
|
||||
|
||||
static void
|
||||
_free_command_hash(GHashTable *command_hash)
|
||||
{
|
||||
g_hash_table_destroy(command_hash);
|
||||
}
|
||||
|
||||
void
|
||||
callbacks_init(void)
|
||||
{
|
||||
p_commands = g_hash_table_new_full(g_str_hash, g_str_equal, free, (GDestroyNotify)_free_command_hash);
|
||||
p_window_callbacks = g_hash_table_new_full(g_str_hash, g_str_equal, free, (GDestroyNotify)_free_window_callback);
|
||||
}
|
||||
|
||||
// TODO move to plugin destroy functions
|
||||
void
|
||||
callbacks_close(void)
|
||||
{
|
||||
g_hash_table_destroy(p_commands);
|
||||
g_hash_table_destroy(p_window_callbacks);
|
||||
}
|
||||
|
||||
void
|
||||
callbacks_add_command(PluginCommand *command)
|
||||
callbacks_add_command(const char *const plugin_name, PluginCommand *command)
|
||||
{
|
||||
p_commands = g_slist_append(p_commands, command);
|
||||
GHashTable *command_hash = g_hash_table_lookup(p_commands, plugin_name);
|
||||
if (command_hash) {
|
||||
g_hash_table_insert(command_hash, strdup(command->command_name), command);
|
||||
} else {
|
||||
command_hash = g_hash_table_new_full(g_str_hash, g_str_equal, free, (GDestroyNotify)_free_command);
|
||||
g_hash_table_insert(command_hash, strdup(command->command_name), command);
|
||||
g_hash_table_insert(p_commands, strdup(plugin_name), command_hash);
|
||||
}
|
||||
cmd_ac_add(command->command_name);
|
||||
cmd_ac_add_help(&command->command_name[1]);
|
||||
}
|
||||
@ -104,25 +170,32 @@ plugins_run_command(const char * const input)
|
||||
{
|
||||
gchar **split = g_strsplit(input, " ", -1);
|
||||
|
||||
GSList *p_command = p_commands;
|
||||
while (p_command) {
|
||||
PluginCommand *command = p_command->data;
|
||||
if (g_strcmp0(split[0], command->command_name) == 0) {
|
||||
GList *command_hashes = g_hash_table_get_values(p_commands);
|
||||
GList *curr_hash = command_hashes;
|
||||
while (curr_hash) {
|
||||
GHashTable *command_hash = curr_hash->data;
|
||||
|
||||
PluginCommand *command = g_hash_table_lookup(command_hash, split[0]);
|
||||
if (command) {
|
||||
gboolean result;
|
||||
gchar **args = parse_args_with_freetext(input, command->min_args, command->max_args, &result);
|
||||
if (result == FALSE) {
|
||||
ui_invalid_command_usage(command->command_name, NULL);
|
||||
g_strfreev(split);
|
||||
g_list_free(command_hashes);
|
||||
return TRUE;
|
||||
} else {
|
||||
command->callback_exec(command, args);
|
||||
g_strfreev(split);
|
||||
g_strfreev(args);
|
||||
g_list_free(command_hashes);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
p_command = g_slist_next(p_command);
|
||||
|
||||
curr_hash = g_list_next(curr_hash);
|
||||
}
|
||||
|
||||
g_strfreev(split);
|
||||
return FALSE;
|
||||
}
|
||||
@ -130,16 +203,22 @@ plugins_run_command(const char * const input)
|
||||
CommandHelp*
|
||||
plugins_get_help(const char *const cmd)
|
||||
{
|
||||
GSList *curr = p_commands;
|
||||
while (curr) {
|
||||
PluginCommand *command = curr->data;
|
||||
if (g_strcmp0(cmd, command->command_name) == 0) {
|
||||
GList *command_hashes = g_hash_table_get_values(p_commands);
|
||||
GList *curr_hash = command_hashes;
|
||||
while (curr_hash) {
|
||||
GHashTable *command_hash = curr_hash->data;
|
||||
|
||||
PluginCommand *command = g_hash_table_lookup(command_hash, cmd);
|
||||
if (command) {
|
||||
g_list_free(command_hashes);
|
||||
return command->help;
|
||||
}
|
||||
|
||||
curr = g_slist_next(curr);
|
||||
curr_hash = g_list_next(curr_hash);
|
||||
}
|
||||
|
||||
g_list_free(command_hashes);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -167,12 +246,22 @@ plugins_get_command_names(void)
|
||||
{
|
||||
GList *result = NULL;
|
||||
|
||||
GSList *curr = p_commands;
|
||||
while (curr) {
|
||||
PluginCommand *command = curr->data;
|
||||
result = g_list_append(result, (char*)command->command_name);
|
||||
curr = g_slist_next(curr);
|
||||
GList *command_hashes = g_hash_table_get_values(p_commands);
|
||||
GList *curr_hash = command_hashes;
|
||||
while (curr_hash) {
|
||||
GHashTable *command_hash = curr_hash->data;
|
||||
GList *commands = g_hash_table_get_keys(command_hash);
|
||||
GList *curr = commands;
|
||||
while (curr) {
|
||||
char *command = curr->data;
|
||||
result = g_list_append(result, command);
|
||||
curr = g_list_next(curr);
|
||||
}
|
||||
g_list_free(commands);
|
||||
curr_hash = g_list_next(curr_hash);
|
||||
}
|
||||
|
||||
g_list_free(command_hashes);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -40,17 +40,19 @@
|
||||
#include "command/cmd_defs.h"
|
||||
|
||||
typedef struct p_command {
|
||||
const char *command_name;
|
||||
char *command_name;
|
||||
int min_args;
|
||||
int max_args;
|
||||
CommandHelp *help;
|
||||
void *callback;
|
||||
void (*callback_exec)(struct p_command *command, gchar **args);
|
||||
void (*callback_destroy)(void *callback);
|
||||
} PluginCommand;
|
||||
|
||||
typedef struct p_timed_function {
|
||||
void *callback;
|
||||
void (*callback_exec)(struct p_timed_function *timed_function);
|
||||
void (*callback_destroy)(void *callback);
|
||||
int interval_seconds;
|
||||
GTimer *timer;
|
||||
} PluginTimedFunction;
|
||||
@ -64,7 +66,7 @@ typedef struct p_window_input_callback {
|
||||
void callbacks_init(void);
|
||||
void callbacks_close(void);
|
||||
|
||||
void callbacks_add_command(PluginCommand *command);
|
||||
void callbacks_add_command(const char *const plugin, PluginCommand *command);
|
||||
void callbacks_add_timed(PluginTimedFunction *timed_function);
|
||||
void callbacks_add_window_handler(const char *tag, PluginWindowCallback *window_callback);
|
||||
void * callbacks_get_window_handler(const char *tag);
|
||||
|
@ -166,19 +166,18 @@ python_api_register_command(PyObject *self, PyObject *args)
|
||||
|
||||
allow_python_threads();
|
||||
api_register_command(plugin_name, command_name, min_args, max_args, c_synopsis,
|
||||
description, c_arguments, c_examples, p_callback, python_command_callback);
|
||||
description, c_arguments, c_examples, p_callback, python_command_callback, NULL);
|
||||
disable_python_threads();
|
||||
}
|
||||
|
||||
free(plugin_name);
|
||||
|
||||
return Py_BuildValue("");
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
python_api_register_timed(PyObject *self, PyObject *args)
|
||||
{
|
||||
char *plugin_name = _python_plugin_name();
|
||||
log_debug("Register timed for %s", plugin_name);
|
||||
|
||||
PyObject *p_callback = NULL;
|
||||
int interval_seconds = 0;
|
||||
|
||||
@ -186,12 +185,17 @@ python_api_register_timed(PyObject *self, PyObject *args)
|
||||
return Py_BuildValue("");
|
||||
}
|
||||
|
||||
char *plugin_name = _python_plugin_name();
|
||||
log_debug("Register timed for %s", plugin_name);
|
||||
|
||||
if (p_callback && PyCallable_Check(p_callback)) {
|
||||
allow_python_threads();
|
||||
api_register_timed(plugin_name, p_callback, interval_seconds, python_timed_callback);
|
||||
api_register_timed(plugin_name, p_callback, interval_seconds, python_timed_callback, NULL);
|
||||
disable_python_threads();
|
||||
}
|
||||
|
||||
free(plugin_name);
|
||||
|
||||
return Py_BuildValue("");
|
||||
}
|
||||
|
||||
@ -223,6 +227,8 @@ python_api_completer_add(PyObject *self, PyObject *args)
|
||||
api_completer_add(plugin_name, key, c_items);
|
||||
disable_python_threads();
|
||||
|
||||
free(plugin_name);
|
||||
|
||||
return Py_BuildValue("");
|
||||
}
|
||||
|
||||
@ -456,19 +462,21 @@ python_api_win_create(PyObject *self, PyObject *args)
|
||||
char *tag = NULL;
|
||||
PyObject *p_callback = NULL;
|
||||
|
||||
char *plugin_name = _python_plugin_name();
|
||||
log_debug("Win create %s for %s", tag, plugin_name);
|
||||
|
||||
if (!PyArg_ParseTuple(args, "sO", &tag, &p_callback)) {
|
||||
return Py_BuildValue("");
|
||||
}
|
||||
|
||||
char *plugin_name = _python_plugin_name();
|
||||
log_debug("Win create %s for %s", tag, plugin_name);
|
||||
|
||||
if (p_callback && PyCallable_Check(p_callback)) {
|
||||
allow_python_threads();
|
||||
api_win_create(plugin_name, tag, p_callback, NULL, python_window_callback);
|
||||
api_win_create(plugin_name, tag, p_callback, python_window_callback, NULL);
|
||||
disable_python_threads();
|
||||
}
|
||||
|
||||
free(plugin_name);
|
||||
|
||||
return Py_BuildValue("");
|
||||
}
|
||||
|
||||
|
@ -1230,7 +1230,7 @@ ui_handle_room_config_submit_result_error(const char *const roomjid, const char
|
||||
}
|
||||
|
||||
void
|
||||
ui_show_lines(ProfWin *window, const gchar** lines)
|
||||
ui_show_lines(ProfWin *window, gchar** lines)
|
||||
{
|
||||
if (lines) {
|
||||
int i;
|
||||
|
@ -110,7 +110,7 @@ void ui_goodbye_title(void);
|
||||
void ui_handle_room_configuration_form_error(const char *const roomjid, const char *const message);
|
||||
void ui_handle_room_config_submit_result(const char *const roomjid);
|
||||
void ui_handle_room_config_submit_result_error(const char *const roomjid, const char *const message);
|
||||
void ui_show_lines(ProfWin *window, const gchar** lines);
|
||||
void ui_show_lines(ProfWin *window, gchar** lines);
|
||||
void ui_redraw_all_room_rosters(void);
|
||||
void ui_show_all_room_rosters(void);
|
||||
void ui_hide_all_room_rosters(void);
|
||||
|
32
tests/unittests/test_callbacks.c
Normal file
32
tests/unittests/test_callbacks.c
Normal file
@ -0,0 +1,32 @@
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <setjmp.h>
|
||||
#include <cmocka.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <glib.h>
|
||||
|
||||
#include "plugins/callbacks.h"
|
||||
#include "plugins/plugins.h"
|
||||
|
||||
void returns_no_commands(void **state)
|
||||
{
|
||||
callbacks_init();
|
||||
GList *commands = plugins_get_command_names();
|
||||
|
||||
assert_true(commands == NULL);
|
||||
}
|
||||
|
||||
void returns_commands(void **state)
|
||||
{
|
||||
callbacks_init();
|
||||
PluginCommand *command = malloc(sizeof(PluginCommand));
|
||||
command->command_name = strdup("something");
|
||||
callbacks_add_command("Cool plugin", command);
|
||||
|
||||
GList *commands = plugins_get_command_names();
|
||||
assert_true(g_list_length(commands) == 1);
|
||||
|
||||
char *name = commands->data;
|
||||
assert_string_equal(name, "something");
|
||||
}
|
2
tests/unittests/test_callbacks.h
Normal file
2
tests/unittests/test_callbacks.h
Normal file
@ -0,0 +1,2 @@
|
||||
void returns_no_commands(void **state);
|
||||
void returns_commands(void **state);
|
@ -265,7 +265,7 @@ void mucconfwin_show_form(ProfMucConfWin *confwin) {}
|
||||
void mucconfwin_show_form_field(ProfMucConfWin *confwin, DataForm *form, char *tag) {}
|
||||
void mucconfwin_form_help(ProfMucConfWin *confwin) {}
|
||||
void mucconfwin_field_help(ProfMucConfWin *confwin, char *tag) {}
|
||||
void ui_show_lines(ProfWin *window, const gchar** lines) {}
|
||||
void ui_show_lines(ProfWin *window, gchar** lines) {}
|
||||
void ui_redraw_all_room_rosters(void) {}
|
||||
void ui_show_all_room_rosters(void) {}
|
||||
void ui_hide_all_room_rosters(void) {}
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include "test_cmd_roster.h"
|
||||
#include "test_cmd_disconnect.h"
|
||||
#include "test_form.h"
|
||||
#include "test_callbacks.h"
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
const UnitTest all_tests[] = {
|
||||
@ -602,6 +603,9 @@ int main(int argc, char* argv[]) {
|
||||
|
||||
unit_test(prof_partial_occurrences_tests),
|
||||
unit_test(prof_whole_occurrences_tests),
|
||||
|
||||
unit_test(returns_no_commands),
|
||||
unit_test(returns_commands),
|
||||
};
|
||||
|
||||
return run_tests(all_tests);
|
||||
|
Loading…
Reference in New Issue
Block a user