mirror of
https://github.com/profanity-im/profanity.git
synced 2025-01-03 14:57:42 -05:00
Add /plugins install command
This commit is contained in:
parent
5f393a6d9f
commit
0991699ae6
@ -57,6 +57,8 @@
|
|||||||
#include "pgp/gpg.h"
|
#include "pgp/gpg.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static char* _complete_filepath(const char *const input, char *const startstr);
|
||||||
|
|
||||||
static char* _sub_autocomplete(ProfWin *window, const char *const input);
|
static char* _sub_autocomplete(ProfWin *window, const char *const input);
|
||||||
static char* _notify_autocomplete(ProfWin *window, const char *const input);
|
static char* _notify_autocomplete(ProfWin *window, const char *const input);
|
||||||
static char* _theme_autocomplete(ProfWin *window, const char *const input);
|
static char* _theme_autocomplete(ProfWin *window, const char *const input);
|
||||||
@ -187,7 +189,7 @@ static Autocomplete plugins_ac;
|
|||||||
static Autocomplete plugins_load_ac;
|
static Autocomplete plugins_load_ac;
|
||||||
static Autocomplete plugins_unload_ac;
|
static Autocomplete plugins_unload_ac;
|
||||||
static Autocomplete plugins_reload_ac;
|
static Autocomplete plugins_reload_ac;
|
||||||
static Autocomplete sendfile_ac;
|
static Autocomplete filepath_ac;
|
||||||
static Autocomplete blocked_ac;
|
static Autocomplete blocked_ac;
|
||||||
static Autocomplete tray_ac;
|
static Autocomplete tray_ac;
|
||||||
static Autocomplete presence_ac;
|
static Autocomplete presence_ac;
|
||||||
@ -705,11 +707,12 @@ cmd_ac_init(void)
|
|||||||
autocomplete_add(autoping_ac, "timeout");
|
autocomplete_add(autoping_ac, "timeout");
|
||||||
|
|
||||||
plugins_ac = autocomplete_new();
|
plugins_ac = autocomplete_new();
|
||||||
|
autocomplete_add(plugins_ac, "install");
|
||||||
autocomplete_add(plugins_ac, "load");
|
autocomplete_add(plugins_ac, "load");
|
||||||
autocomplete_add(plugins_ac, "unload");
|
autocomplete_add(plugins_ac, "unload");
|
||||||
autocomplete_add(plugins_ac, "reload");
|
autocomplete_add(plugins_ac, "reload");
|
||||||
|
|
||||||
sendfile_ac = autocomplete_new();
|
filepath_ac = autocomplete_new();
|
||||||
|
|
||||||
blocked_ac = autocomplete_new();
|
blocked_ac = autocomplete_new();
|
||||||
autocomplete_add(blocked_ac, "add");
|
autocomplete_add(blocked_ac, "add");
|
||||||
@ -903,7 +906,7 @@ cmd_ac_reset(ProfWin *window)
|
|||||||
autocomplete_reset(notify_mention_ac);
|
autocomplete_reset(notify_mention_ac);
|
||||||
autocomplete_reset(notify_trigger_ac);
|
autocomplete_reset(notify_trigger_ac);
|
||||||
autocomplete_reset(sub_ac);
|
autocomplete_reset(sub_ac);
|
||||||
autocomplete_reset(sendfile_ac);
|
autocomplete_reset(filepath_ac);
|
||||||
|
|
||||||
autocomplete_reset(who_room_ac);
|
autocomplete_reset(who_room_ac);
|
||||||
autocomplete_reset(who_roster_ac);
|
autocomplete_reset(who_roster_ac);
|
||||||
@ -1111,7 +1114,7 @@ cmd_ac_uninit(void)
|
|||||||
autocomplete_free(plugins_load_ac);
|
autocomplete_free(plugins_load_ac);
|
||||||
autocomplete_free(plugins_unload_ac);
|
autocomplete_free(plugins_unload_ac);
|
||||||
autocomplete_free(plugins_reload_ac);
|
autocomplete_free(plugins_reload_ac);
|
||||||
autocomplete_free(sendfile_ac);
|
autocomplete_free(filepath_ac);
|
||||||
autocomplete_free(blocked_ac);
|
autocomplete_free(blocked_ac);
|
||||||
autocomplete_free(tray_ac);
|
autocomplete_free(tray_ac);
|
||||||
autocomplete_free(presence_ac);
|
autocomplete_free(presence_ac);
|
||||||
@ -1890,6 +1893,10 @@ _plugins_autocomplete(ProfWin *window, const char *const input)
|
|||||||
{
|
{
|
||||||
char *result = NULL;
|
char *result = NULL;
|
||||||
|
|
||||||
|
if (strncmp(input, "/plugins install ", 17) == 0) {
|
||||||
|
return _complete_filepath(input, "/plugins install");
|
||||||
|
}
|
||||||
|
|
||||||
if (strncmp(input, "/plugins load ", 14) == 0) {
|
if (strncmp(input, "/plugins load ", 14) == 0) {
|
||||||
if (plugins_load_ac == NULL) {
|
if (plugins_load_ac == NULL) {
|
||||||
plugins_load_ac = autocomplete_new();
|
plugins_load_ac = autocomplete_new();
|
||||||
@ -2719,107 +2726,9 @@ _close_autocomplete(ProfWin *window, const char *const input)
|
|||||||
static char*
|
static char*
|
||||||
_sendfile_autocomplete(ProfWin *window, const char *const input)
|
_sendfile_autocomplete(ProfWin *window, const char *const input)
|
||||||
{
|
{
|
||||||
static char* last_directory = NULL;
|
return _complete_filepath(input, "/sendfile");
|
||||||
|
|
||||||
unsigned int output_off = 0;
|
|
||||||
|
|
||||||
char *result = NULL;
|
|
||||||
char *tmp;
|
|
||||||
|
|
||||||
// strip command
|
|
||||||
char *inpcp = (char*)input + 9;
|
|
||||||
while (*inpcp == ' ') {
|
|
||||||
inpcp++;
|
|
||||||
}
|
|
||||||
|
|
||||||
inpcp = strdup(inpcp);
|
|
||||||
|
|
||||||
// strip quotes
|
|
||||||
if (*inpcp == '"') {
|
|
||||||
tmp = strchr(inpcp+1, '"');
|
|
||||||
if (tmp) {
|
|
||||||
*tmp = '\0';
|
|
||||||
}
|
|
||||||
tmp = strdup(inpcp+1);
|
|
||||||
free(inpcp);
|
|
||||||
inpcp = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
// expand ~ to $HOME
|
|
||||||
if (inpcp[0] == '~' && inpcp[1] == '/') {
|
|
||||||
if (asprintf(&tmp, "%s/%sfoo", getenv("HOME"), inpcp+2) == -1) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
output_off = strlen(getenv("HOME"))+1;
|
|
||||||
} else {
|
|
||||||
if (asprintf(&tmp, "%sfoo", inpcp) == -1) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
free(inpcp);
|
|
||||||
inpcp = tmp;
|
|
||||||
|
|
||||||
char* inpcp2 = strdup(inpcp);
|
|
||||||
char* foofile = strdup(basename(inpcp2));
|
|
||||||
char* directory = strdup(dirname(inpcp));
|
|
||||||
free(inpcp);
|
|
||||||
free(inpcp2);
|
|
||||||
|
|
||||||
if (!last_directory || strcmp(last_directory, directory) != 0) {
|
|
||||||
free(last_directory);
|
|
||||||
last_directory = directory;
|
|
||||||
autocomplete_reset(sendfile_ac);
|
|
||||||
|
|
||||||
struct dirent *dir;
|
|
||||||
|
|
||||||
DIR *d = opendir(directory);
|
|
||||||
if (d) {
|
|
||||||
while ((dir = readdir(d)) != NULL) {
|
|
||||||
if (strcmp(dir->d_name, ".") == 0) {
|
|
||||||
continue;
|
|
||||||
} else if (strcmp(dir->d_name, "..") == 0) {
|
|
||||||
continue;
|
|
||||||
} else if (*(dir->d_name) == '.' && *foofile != '.') {
|
|
||||||
// only show hidden files on explicit request
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
char * acstring;
|
|
||||||
if (output_off) {
|
|
||||||
if (asprintf(&tmp, "%s/%s", directory, dir->d_name) == -1) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
if (asprintf(&acstring, "~/%s", tmp+output_off) == -1) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
free(tmp);
|
|
||||||
} else if (strcmp(directory, "/") == 0) {
|
|
||||||
if (asprintf(&acstring, "/%s", dir->d_name) == -1) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (asprintf(&acstring, "%s/%s", directory, dir->d_name) == -1) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
autocomplete_add(sendfile_ac, acstring);
|
|
||||||
free(acstring);
|
|
||||||
}
|
|
||||||
closedir(d);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
free(foofile);
|
|
||||||
free(directory);
|
|
||||||
}
|
|
||||||
|
|
||||||
result = autocomplete_param_with_ac(input, "/sendfile", sendfile_ac, TRUE);
|
|
||||||
if (result) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static char*
|
static char*
|
||||||
_subject_autocomplete(ProfWin *window, const char *const input)
|
_subject_autocomplete(ProfWin *window, const char *const input)
|
||||||
{
|
{
|
||||||
@ -3019,3 +2928,106 @@ _presence_autocomplete(ProfWin *window, const char *const input)
|
|||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char*
|
||||||
|
_complete_filepath(const char *const input, char *const startstr)
|
||||||
|
{
|
||||||
|
static char* last_directory = NULL;
|
||||||
|
|
||||||
|
unsigned int output_off = 0;
|
||||||
|
|
||||||
|
char *result = NULL;
|
||||||
|
char *tmp;
|
||||||
|
|
||||||
|
// strip command
|
||||||
|
char *inpcp = (char*)input + strlen(startstr);
|
||||||
|
while (*inpcp == ' ') {
|
||||||
|
inpcp++;
|
||||||
|
}
|
||||||
|
|
||||||
|
inpcp = strdup(inpcp);
|
||||||
|
|
||||||
|
// strip quotes
|
||||||
|
if (*inpcp == '"') {
|
||||||
|
tmp = strchr(inpcp+1, '"');
|
||||||
|
if (tmp) {
|
||||||
|
*tmp = '\0';
|
||||||
|
}
|
||||||
|
tmp = strdup(inpcp+1);
|
||||||
|
free(inpcp);
|
||||||
|
inpcp = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// expand ~ to $HOME
|
||||||
|
if (inpcp[0] == '~' && inpcp[1] == '/') {
|
||||||
|
if (asprintf(&tmp, "%s/%sfoo", getenv("HOME"), inpcp+2) == -1) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
output_off = strlen(getenv("HOME"))+1;
|
||||||
|
} else {
|
||||||
|
if (asprintf(&tmp, "%sfoo", inpcp) == -1) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(inpcp);
|
||||||
|
inpcp = tmp;
|
||||||
|
|
||||||
|
char* inpcp2 = strdup(inpcp);
|
||||||
|
char* foofile = strdup(basename(inpcp2));
|
||||||
|
char* directory = strdup(dirname(inpcp));
|
||||||
|
free(inpcp);
|
||||||
|
free(inpcp2);
|
||||||
|
|
||||||
|
if (!last_directory || strcmp(last_directory, directory) != 0) {
|
||||||
|
free(last_directory);
|
||||||
|
last_directory = directory;
|
||||||
|
autocomplete_reset(filepath_ac);
|
||||||
|
|
||||||
|
struct dirent *dir;
|
||||||
|
|
||||||
|
DIR *d = opendir(directory);
|
||||||
|
if (d) {
|
||||||
|
while ((dir = readdir(d)) != NULL) {
|
||||||
|
if (strcmp(dir->d_name, ".") == 0) {
|
||||||
|
continue;
|
||||||
|
} else if (strcmp(dir->d_name, "..") == 0) {
|
||||||
|
continue;
|
||||||
|
} else if (*(dir->d_name) == '.' && *foofile != '.') {
|
||||||
|
// only show hidden files on explicit request
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
char * acstring;
|
||||||
|
if (output_off) {
|
||||||
|
if (asprintf(&tmp, "%s/%s", directory, dir->d_name) == -1) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (asprintf(&acstring, "~/%s", tmp+output_off) == -1) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
free(tmp);
|
||||||
|
} else if (strcmp(directory, "/") == 0) {
|
||||||
|
if (asprintf(&acstring, "/%s", dir->d_name) == -1) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (asprintf(&acstring, "%s/%s", directory, dir->d_name) == -1) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
autocomplete_add(filepath_ac, acstring);
|
||||||
|
free(acstring);
|
||||||
|
}
|
||||||
|
closedir(d);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
free(foofile);
|
||||||
|
free(directory);
|
||||||
|
}
|
||||||
|
|
||||||
|
result = autocomplete_param_with_ac(input, startstr, filepath_ac, TRUE);
|
||||||
|
if (result) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
@ -1979,16 +1979,19 @@ static struct cmd_t command_defs[] =
|
|||||||
CMD_NOTAGS
|
CMD_NOTAGS
|
||||||
CMD_SYN(
|
CMD_SYN(
|
||||||
"/plugins",
|
"/plugins",
|
||||||
|
"/plugins install <path>",
|
||||||
"/plugins unload <plugin>",
|
"/plugins unload <plugin>",
|
||||||
"/plugins load <plugin>",
|
"/plugins load <plugin>",
|
||||||
"/plugins reload [<plugin>]")
|
"/plugins reload [<plugin>]")
|
||||||
CMD_DESC(
|
CMD_DESC(
|
||||||
"Manage plugins. Passing no arguments lists currently loaded plugins.")
|
"Manage plugins. Passing no arguments lists currently loaded plugins.")
|
||||||
CMD_ARGS(
|
CMD_ARGS(
|
||||||
{ "load <plugin>", "Load a plugin." },
|
{ "install <file>", "Install file to plugins directory, and load or reload the plugin." },
|
||||||
{ "unload <plugin>", "Unload a plugin." },
|
{ "load <plugin>", "Load a plugin that already exists in the plugin directory." },
|
||||||
|
{ "unload <plugin>", "Unload a loaded plugin." },
|
||||||
{ "reload [<plugin>]", "Reload a plugin, passing no argument will reload all plugins" })
|
{ "reload [<plugin>]", "Reload a plugin, passing no argument will reload all plugins" })
|
||||||
CMD_EXAMPLES(
|
CMD_EXAMPLES(
|
||||||
|
"/plugin install /home/steveharris/Downloads/metal.py",
|
||||||
"/plugin load browser.py",
|
"/plugin load browser.py",
|
||||||
"/plugin unload say.py",
|
"/plugin unload say.py",
|
||||||
"/plugin reload wikipedia.py")
|
"/plugin reload wikipedia.py")
|
||||||
|
@ -6025,7 +6025,50 @@ cmd_xa(ProfWin *window, const char *const command, gchar **args)
|
|||||||
gboolean
|
gboolean
|
||||||
cmd_plugins(ProfWin *window, const char *const command, gchar **args)
|
cmd_plugins(ProfWin *window, const char *const command, gchar **args)
|
||||||
{
|
{
|
||||||
if (g_strcmp0(args[0], "load") == 0) {
|
if (g_strcmp0(args[0], "install") == 0) {
|
||||||
|
char *filename = args[1];
|
||||||
|
if (filename == NULL) {
|
||||||
|
cons_bad_cmd_usage(command);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// expand ~ to $HOME
|
||||||
|
if (filename[0] == '~' && filename[1] == '/') {
|
||||||
|
if (asprintf(&filename, "%s/%s", getenv("HOME"), filename+2) == -1) {
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
filename = strdup(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (access(filename, R_OK) != 0) {
|
||||||
|
cons_show("File not found: %s", filename);
|
||||||
|
free(filename);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_regular_file(filename)) {
|
||||||
|
cons_show("Not a file: %s", filename);
|
||||||
|
free(filename);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!g_str_has_suffix(filename, ".py") && !g_str_has_suffix(filename, ".so")) {
|
||||||
|
cons_show("Plugins must have one of the following extensions: '.py' '.so'");
|
||||||
|
free(filename);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *plugin_name = basename(filename);
|
||||||
|
gboolean result = plugins_install(plugin_name, filename);
|
||||||
|
if (result) {
|
||||||
|
cons_show("Plugin installed, use '/plugin load %s' to enable the plugin.", plugin_name);
|
||||||
|
} else {
|
||||||
|
cons_show("Failed to install plugin: %s", plugin_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
} else if (g_strcmp0(args[0], "load") == 0) {
|
||||||
if (args[1] == NULL) {
|
if (args[1] == NULL) {
|
||||||
cons_bad_cmd_usage(command);
|
cons_bad_cmd_usage(command);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
26
src/common.c
26
src/common.c
@ -38,6 +38,7 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
@ -155,6 +156,31 @@ mkdir_recursive(const char *dir)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
copy_file(const char *const sourcepath, const char *const targetpath)
|
||||||
|
{
|
||||||
|
int ch;
|
||||||
|
FILE *source = fopen(sourcepath, "rb");
|
||||||
|
if (source == NULL) {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE *target = fopen(targetpath, "wb");
|
||||||
|
if (target == NULL) {
|
||||||
|
fclose(source);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
while((ch = fgetc(source)) != EOF) {
|
||||||
|
fputc(ch, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(source);
|
||||||
|
fclose(target);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
char*
|
char*
|
||||||
str_replace(const char *string, const char *substr,
|
str_replace(const char *string, const char *substr,
|
||||||
const char *replacement)
|
const char *replacement)
|
||||||
|
@ -102,6 +102,7 @@ gboolean p_hash_table_contains(GHashTable *hash_table, gconstpointer key);
|
|||||||
|
|
||||||
gboolean create_dir(char *name);
|
gboolean create_dir(char *name);
|
||||||
gboolean mkdir_recursive(const char *dir);
|
gboolean mkdir_recursive(const char *dir);
|
||||||
|
gboolean copy_file(const char *const src, const char *const target);
|
||||||
char* str_replace(const char *string, const char *substr, const char *replacement);
|
char* str_replace(const char *string, const char *substr, const char *replacement);
|
||||||
int str_contains(const char str[], int size, char ch);
|
int str_contains(const char str[], int size, char ch);
|
||||||
gboolean strtoi_range(char *str, int *saveptr, int min, int max, char **err_msg);
|
gboolean strtoi_range(char *str, int *saveptr, int min, int max, char **err_msg);
|
||||||
|
@ -62,6 +62,8 @@
|
|||||||
|
|
||||||
static GHashTable *plugins;
|
static GHashTable *plugins;
|
||||||
|
|
||||||
|
static gchar* _get_plugins_dir(void);
|
||||||
|
|
||||||
void
|
void
|
||||||
plugins_init(void)
|
plugins_init(void)
|
||||||
{
|
{
|
||||||
@ -128,6 +130,30 @@ plugins_init(void)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
plugins_install(const char *const plugin_name, const char *const filename)
|
||||||
|
{
|
||||||
|
char *plugins_dir = _get_plugins_dir();
|
||||||
|
GString *target_path = g_string_new(plugins_dir);
|
||||||
|
free(plugins_dir);
|
||||||
|
g_string_append(target_path, "/");
|
||||||
|
g_string_append(target_path, plugin_name);
|
||||||
|
|
||||||
|
ProfPlugin *plugin = g_hash_table_lookup(plugins, plugin_name);
|
||||||
|
if (plugin) {
|
||||||
|
plugins_unload(plugin_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean result = copy_file(filename, target_path->str);
|
||||||
|
g_string_free(target_path, TRUE);
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
result = plugins_load(plugin_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
gboolean
|
gboolean
|
||||||
plugins_load(const char *const name)
|
plugins_load(const char *const name)
|
||||||
{
|
{
|
||||||
|
@ -105,6 +105,7 @@ char* plugins_autocomplete(const char *const input);
|
|||||||
void plugins_reset_autocomplete(void);
|
void plugins_reset_autocomplete(void);
|
||||||
void plugins_shutdown(void);
|
void plugins_shutdown(void);
|
||||||
|
|
||||||
|
gboolean plugins_install(const char *const plugin_name, const char *const filename);
|
||||||
gboolean plugins_load(const char *const name);
|
gboolean plugins_load(const char *const name);
|
||||||
gboolean plugins_unload(const char *const name);
|
gboolean plugins_unload(const char *const name);
|
||||||
gboolean plugins_reload(const char *const name);
|
gboolean plugins_reload(const char *const name);
|
||||||
|
Loading…
Reference in New Issue
Block a user