diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index 7f854249..d26ff43f 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -6214,49 +6214,77 @@ cmd_xa(ProfWin *window, const char *const command, gchar **args) gboolean cmd_plugins_install(ProfWin *window, const char *const command, gchar **args) { - char *filename = args[1]; - if (filename == NULL) { + char *path = args[1]; + if (path == 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) { + if (path[0] == '~' && path[1] == '/') { + if (asprintf(&path, "%s/%s", getenv("HOME"), path+2) == -1) { return TRUE; } } else { - filename = strdup(filename); + path = strdup(path); } - if (access(filename, R_OK) != 0) { - cons_show("File not found: %s", filename); - free(filename); + if (access(path, R_OK) != 0) { + cons_show("File not found: %s", path); + free(path); return TRUE; } - if (!is_regular_file(filename)) { - cons_show("Not a file: %s", filename); - free(filename); + if (is_regular_file(path)) { + if (!g_str_has_suffix(path, ".py") && !g_str_has_suffix(path, ".so")) { + cons_show("Plugins must have one of the following extensions: '.py' '.so'"); + free(path); + return TRUE; + } + + gchar *plugin_name = g_path_get_basename(path); + gboolean result = plugins_install(plugin_name, path); + if (result) { + cons_show("Plugin installed: %s", plugin_name); + } else { + cons_show("Failed to install plugin: %s", plugin_name); + } + g_free(plugin_name); + + free(path); 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); + if (is_dir(path)) { + PluginsInstallResult* result = plugins_install_all(path); + if (result->installed || result->failed) { + if (result->installed) { + cons_show(""); + cons_show("Installed plugins:"); + GSList *curr = result->installed; + while (curr) { + cons_show(" %s", curr->data); + curr = g_slist_next(curr); + } + } + if (result->failed) { + cons_show(""); + cons_show("Failed installs:"); + GSList *curr = result->failed; + while (curr) { + cons_show(" %s", curr->data); + curr = g_slist_next(curr); + } + } + } else { + cons_show("No plugins found in: %s", path); + } + free(path); + plugins_free_install_result(result); return TRUE; } - gchar *plugin_name = g_path_get_basename(filename); - gboolean result = plugins_install(plugin_name, filename); - if (result) { - cons_show("Plugin installed: %s", plugin_name); - } else { - cons_show("Failed to install plugin: %s", plugin_name); - } - g_free(plugin_name); - - free(filename); + cons_show("Argument must be a file or directory."); return TRUE; } diff --git a/src/common.c b/src/common.c index ef7129cd..898f62fa 100644 --- a/src/common.c +++ b/src/common.c @@ -536,3 +536,45 @@ prof_occurrences(const char *const needle, const char *const haystack, int offse return *result; } +int +is_regular_file(const char *path) +{ + struct stat st; + stat(path, &st); + return S_ISREG(st.st_mode); +} + +int +is_dir(const char *path) +{ + struct stat st; + stat(path, &st); + return S_ISDIR(st.st_mode); +} + +void +get_file_paths_recursive(const char *path, GSList **contents) +{ + if (!is_dir(path)) { + return; + } + + GDir* directory = g_dir_open(path, 0, NULL); + const gchar *entry = g_dir_read_name(directory); + while (entry) { + GString *full = g_string_new(path); + if (!g_str_has_suffix(full->str, "/")) { + g_string_append(full, "/"); + } + g_string_append(full, entry); + + if (is_dir(full->str)) { + get_file_paths_recursive(full->str, contents); + } else if (is_regular_file(full->str)) { + *contents = g_slist_append(*contents, full->str); + } + + g_string_free(full, FALSE); + entry = g_dir_read_name(directory); + } +} diff --git a/src/common.h b/src/common.h index c2317390..8c42ae52 100644 --- a/src/common.h +++ b/src/common.h @@ -123,4 +123,8 @@ gboolean is_notify_enabled(void); GSList* prof_occurrences(const char *const needle, const char *const haystack, int offset, gboolean whole_word, GSList **result); +int is_regular_file(const char *path); +int is_dir(const char *path); +void get_file_paths_recursive(const char *directory, GSList **contents); + #endif diff --git a/src/plugins/plugins.c b/src/plugins/plugins.c index 4896fedb..92d605e8 100644 --- a/src/plugins/plugins.c +++ b/src/plugins/plugins.c @@ -129,6 +129,43 @@ plugins_init(void) return; } +void +plugins_free_install_result(PluginsInstallResult *result) +{ + if (!result) { + return; + } + g_slist_free_full(result->installed, free); + g_slist_free_full(result->failed, free); +} + +PluginsInstallResult* +plugins_install_all(const char *const path) +{ + PluginsInstallResult *result = malloc(sizeof(PluginsInstallResult)); + result->installed = NULL; + result->failed = NULL; + GSList *contents = NULL; + get_file_paths_recursive(path, &contents); + + GSList *curr = contents; + while (curr) { + if (g_str_has_suffix(curr->data, ".py") || g_str_has_suffix(curr->data, ".so")) { + gchar *plugin_name = g_path_get_basename(curr->data); + if (plugins_install(plugin_name, curr->data)) { + result->installed = g_slist_append(result->installed, strdup(curr->data)); + } else { + result->failed = g_slist_append(result->failed, strdup(curr->data)); + } + } + curr = g_slist_next(curr); + } + + g_slist_free_full(contents, g_free); + + return result; +} + gboolean plugins_install(const char *const plugin_name, const char *const filename) { diff --git a/src/plugins/plugins.h b/src/plugins/plugins.h index 502af806..af659757 100644 --- a/src/plugins/plugins.h +++ b/src/plugins/plugins.h @@ -42,6 +42,11 @@ typedef enum { LANG_C } lang_t; +typedef struct prof_plugins_install_t { + GSList *installed; + GSList *failed; +} PluginsInstallResult; + typedef struct prof_plugin_t { char *name; lang_t lang; @@ -107,7 +112,10 @@ char* plugins_autocomplete(const char *const input); void plugins_reset_autocomplete(void); void plugins_shutdown(void); +void plugins_free_install_result(PluginsInstallResult *result); + gboolean plugins_install(const char *const plugin_name, const char *const filename); +PluginsInstallResult* plugins_install_all(const char *const path); gboolean plugins_load(const char *const name); GSList* plugins_load_all(void); gboolean plugins_unload(const char *const name); diff --git a/src/tools/http_upload.c b/src/tools/http_upload.c index c1ac9fdf..6e5dd27c 100644 --- a/src/tools/http_upload.c +++ b/src/tools/http_upload.c @@ -52,6 +52,7 @@ #include "config/preferences.h" #include "ui/ui.h" #include "ui/window.h" +#include "common.h" #define FALLBACK_MIMETYPE "application/octet-stream" #define FALLBACK_CONTENTTYPE_HEADER "Content-Type: application/octet-stream" @@ -330,10 +331,3 @@ off_t file_size(const char* const filename) stat(filename, &st); return st.st_size; } - -int is_regular_file(const char *filename) -{ - struct stat st; - stat(filename, &st); - return S_ISREG(st.st_mode); -} diff --git a/src/tools/http_upload.h b/src/tools/http_upload.h index 15281fa5..ae8f8223 100644 --- a/src/tools/http_upload.h +++ b/src/tools/http_upload.h @@ -62,6 +62,5 @@ void* http_file_put(void *userdata); char* file_mime_type(const char* const file_name); off_t file_size(const char* const file_name); -int is_regular_file(const char *filename); #endif diff --git a/tests/unittests/tools/stub_http_upload.c b/tests/unittests/tools/stub_http_upload.c index c0b4e979..508d3f8b 100644 --- a/tests/unittests/tools/stub_http_upload.c +++ b/tests/unittests/tools/stub_http_upload.c @@ -24,6 +24,5 @@ void* http_file_put(void *userdata) {} char* file_mime_type(const char* const file_name) {} off_t file_size(const char* const file_name) {} -int is_regular_file(const char *filename) {} #endif