From ef64c132870b0a00978309fb8b2f6025fb04e842 Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Fri, 23 Mar 2018 07:13:52 -0400 Subject: [PATCH] Add systemOpenBrowser --- include/util/common.hpp | 34 +++--- src/app/PluginManagerWidget.cpp | 8 +- src/app/RackScene.cpp | 1 - src/main.cpp | 24 ---- src/plugin.cpp | 203 +++++++++++++++++--------------- src/util/logger.cpp | 14 +-- src/util/string.cpp | 4 + src/util/system.cpp | 26 +++- 8 files changed, 165 insertions(+), 149 deletions(-) diff --git a/include/util/common.hpp b/include/util/common.hpp index 2856abdc..055bd13c 100644 --- a/include/util/common.hpp +++ b/include/util/common.hpp @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -15,23 +16,28 @@ // Handy macros //////////////////// -/** Surrounds raw text with quotes +/** Concatenates two literals or two macros Example: - printf("Hello " STRINGIFY(world)) -will expand to - printf("Hello " "world") -and of course the C++ lexer/parser will then concatenate the string literals + #define COUNT 42 + CONCAT(myVariable, COUNT) +expands to + myVariable42 */ -#define STRINGIFY(x) #x -/** Converts a macro to a string literal +#define CONCAT_LITERAL(x, y) x ## y +#define CONCAT(x, y) CONCAT_LITERAL(x, y) + +/** Surrounds raw text with quotes Example: #define NAME "world" printf("Hello " TOSTRING(NAME)) -will expand to +expands to printf("Hello " "world") +and of course the C++ lexer/parser then concatenates the string literals. */ -#define TOSTRING(x) STRINGIFY(x) +#define TOSTRING_LITERAL(x) #x +#define TOSTRING(x) TOSTRING_LITERAL(x) +/** Produces the length of a static array in number of elements */ #define LENGTHOF(arr) (sizeof(arr) / sizeof((arr)[0])) /** Reserve space for `count` enums starting with `name`. @@ -95,10 +101,7 @@ DeferWrapper deferWrapper(F f) { return DeferWrapper(f); } -#define DEFER_1(x, y) x##y -#define DEFER_2(x, y) DEFER_1(x, y) -#define DEFER_3(x) DEFER_2(x, __COUNTER__) -#define defer(code) auto DEFER_3(_defer_) = deferWrapper([&]() code) +#define defer(code) auto CONCAT(x, __COUNTER__) = deferWrapper([&]() code) //////////////////// // Random number generator @@ -129,6 +132,7 @@ std::string uppercase(std::string s); /** Truncates and adds "..." to a string, not exceeding `len` characters */ std::string ellipsize(std::string s, size_t len); bool startsWith(std::string str, std::string prefix); +bool endsWith(std::string str, std::string suffix); std::string extractDirectory(std::string path); std::string extractFilename(std::string path); @@ -139,11 +143,13 @@ std::string extractExtension(std::string path); // system.cpp //////////////////// +std::vector systemListDirectory(std::string path); + /** Opens a URL, also happens to work with PDFs and folders. Shell injection is possible, so make sure the URL is trusted or hard coded. May block, so open in a new thread. */ -void openBrowser(std::string url); +void systemOpenBrowser(std::string url); //////////////////// // Debug logger diff --git a/src/app/PluginManagerWidget.cpp b/src/app/PluginManagerWidget.cpp index 914f0faa..1d3165e7 100644 --- a/src/app/PluginManagerWidget.cpp +++ b/src/app/PluginManagerWidget.cpp @@ -62,7 +62,9 @@ PluginManagerWidget::PluginManagerWidget() { struct RegisterButton : Button { void onAction(EventAction &e) override { - std::thread t(openBrowser, "https://vcvrack.com/"); + std::thread t([&]() { + systemOpenBrowser("https://vcvrack.com/"); + }); t.detach(); } }; @@ -126,7 +128,9 @@ PluginManagerWidget::PluginManagerWidget() { struct ManageButton : Button { void onAction(EventAction &e) override { - std::thread t(openBrowser, "https://vcvrack.com/"); + std::thread t([&]() { + systemOpenBrowser("https://vcvrack.com/"); + }); t.detach(); } }; diff --git a/src/app/RackScene.cpp b/src/app/RackScene.cpp index 01bbf71c..2a87a8f2 100644 --- a/src/app/RackScene.cpp +++ b/src/app/RackScene.cpp @@ -1,7 +1,6 @@ #include "app.hpp" #include "window.hpp" #include "util/request.hpp" -#include "osdialog.h" #include #include diff --git a/src/main.cpp b/src/main.cpp index b2a12a8e..6cba8c13 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -10,35 +10,11 @@ #include -#include - using namespace rack; -std::vector filesystemListDirectory(std::string path) { - std::vector filenames; - DIR *dir = opendir(path.c_str()); - if (dir) { - struct dirent *d; - while ((d = readdir(dir))) { - std::string filename = d->d_name; - if (filename == "." || filename == "..") - continue; - filenames.push_back(path + "/" + filename); - } - closedir(dir); - } - return filenames; -} - int main(int argc, char* argv[]) { - - for (std::string filename : filesystemListDirectory("plugins")) { - debug("%s", filename.c_str()); - } - return 0; - randomInit(); loggerInit(); diff --git a/src/plugin.cpp b/src/plugin.cpp index 0f7725ce..dd028fb0 100644 --- a/src/plugin.cpp +++ b/src/plugin.cpp @@ -1,5 +1,10 @@ -#include +#include "plugin.hpp" +#include "app.hpp" +#include "asset.hpp" +#include "util/request.hpp" +#include "osdialog.h" +#include #include #include #include @@ -13,7 +18,7 @@ #include #include -#if defined(ARCH_WIN) +#if ARCH_WIN #include #include #define mkdir(_dir, _perms) _mkdir(_dir) @@ -22,14 +27,10 @@ #endif #include -#include "plugin.hpp" -#include "app.hpp" -#include "asset.hpp" -#include "util/request.hpp" - namespace rack { + std::list gPlugins; std::string gToken; @@ -73,7 +74,7 @@ static int loadPlugin(std::string path) { warn("Failed to load library %s: %d", libraryFilename.c_str(), error); return -1; } -#elif ARCH_LIN || ARCH_MAC +#else void *handle = dlopen(libraryFilename.c_str(), RTLD_NOW); if (!handle) { warn("Failed to load library %s: %s", libraryFilename.c_str(), dlerror()); @@ -86,7 +87,7 @@ static int loadPlugin(std::string path) { InitCallback initCallback; #if ARCH_WIN initCallback = (InitCallback) GetProcAddress(handle, "init"); -#elif ARCH_LIN || ARCH_MAC +#else initCallback = (InitCallback) dlsym(handle, "init"); #endif if (!initCallback) { @@ -117,80 +118,6 @@ static int loadPlugin(std::string path) { return 0; } -static void loadPlugins(std::string path) { - DIR *dir = opendir(path.c_str()); - if (dir) { - struct dirent *d; - while ((d = readdir(dir))) { - if (d->d_name[0] == '.') - continue; - loadPlugin(path + "/" + d->d_name); - } - closedir(dir); - } -} - -//////////////////// -// plugin helpers -//////////////////// - -static int extractZipHandle(zip_t *za, const char *dir) { - int err = 0; - for (int i = 0; i < zip_get_num_entries(za, 0); i++) { - zip_stat_t zs; - err = zip_stat_index(za, i, 0, &zs); - if (err) - return err; - int nameLen = strlen(zs.name); - - char path[MAXPATHLEN]; - snprintf(path, sizeof(path), "%s/%s", dir, zs.name); - - if (zs.name[nameLen - 1] == '/') { - err = mkdir(path, 0755); - if (err && errno != EEXIST) - return err; - } - else { - zip_file_t *zf = zip_fopen_index(za, i, 0); - if (!zf) - return 1; - - FILE *outFile = fopen(path, "wb"); - if (!outFile) - continue; - - while (1) { - char buffer[4096]; - int len = zip_fread(zf, buffer, sizeof(buffer)); - if (len <= 0) - break; - fwrite(buffer, 1, len, outFile); - } - - err = zip_fclose(zf); - if (err) - return err; - fclose(outFile); - } - } - return 0; -} - -static int extractZip(const char *filename, const char *dir) { - int err = 0; - zip_t *za = zip_open(filename, 0, &err); - if (!za) - return 1; - - if (!err) { - err = extractZipHandle(za, dir); - } - - zip_close(za); - return err; -} - static bool syncPlugin(json_t *pluginJ, bool dryRun) { json_t *slugJ = json_object_get(pluginJ, "slug"); if (!slugJ) @@ -223,11 +150,11 @@ static bool syncPlugin(json_t *pluginJ, bool dryRun) { json_t *downloadsJ = json_object_get(pluginJ, "downloads"); if (downloadsJ) { -#if defined(ARCH_WIN) +#if ARCH_WIN #define DOWNLOADS_ARCH "win" -#elif defined(ARCH_MAC) +#elif ARCH_MAC #define DOWNLOADS_ARCH "mac" -#elif defined(ARCH_LIN) +#elif ARCH_LIN #define DOWNLOADS_ARCH "lin" #endif json_t *archJ = json_object_get(downloadsJ, DOWNLOADS_ARCH); @@ -281,15 +208,6 @@ static bool syncPlugin(json_t *pluginJ, bool dryRun) { } } - // Unzip file - int err = extractZip(zipPath.c_str(), pluginsDir.c_str()); - if (!err) { - // Delete zip - remove(zipPath.c_str()); - // Load plugin - // loadPlugin(pluginPath); - } - downloadName = ""; return true; } @@ -378,6 +296,95 @@ bool pluginSync(bool dryRun) { return available; } +static void loadPlugins(std::string path) { + DIR *dir = opendir(path.c_str()); + if (dir) { + struct dirent *d; + while ((d = readdir(dir))) { + if (d->d_name[0] == '.') + continue; + loadPlugin(path + "/" + d->d_name); + } + closedir(dir); + } +} + +static int extractZipHandle(zip_t *za, const char *dir) { + int err = 0; + for (int i = 0; i < zip_get_num_entries(za, 0); i++) { + zip_stat_t zs; + err = zip_stat_index(za, i, 0, &zs); + if (err) + return err; + int nameLen = strlen(zs.name); + + char path[MAXPATHLEN]; + snprintf(path, sizeof(path), "%s/%s", dir, zs.name); + + if (zs.name[nameLen - 1] == '/') { + err = mkdir(path, 0755); + if (err && errno != EEXIST) + return err; + } + else { + zip_file_t *zf = zip_fopen_index(za, i, 0); + if (!zf) + return 1; + + FILE *outFile = fopen(path, "wb"); + if (!outFile) + continue; + + while (1) { + char buffer[4096]; + int len = zip_fread(zf, buffer, sizeof(buffer)); + if (len <= 0) + break; + fwrite(buffer, 1, len, outFile); + } + + err = zip_fclose(zf); + if (err) + return err; + fclose(outFile); + } + } + return 0; +} + +static int extractZip(const char *filename, const char *dir) { + int err = 0; + zip_t *za = zip_open(filename, 0, &err); + if (!za) + return 1; + + if (!err) { + err = extractZipHandle(za, dir); + } + + zip_close(za); + return err; +} + +static void extractPackages(std::string path) { + std::string message; + + for (std::string packagePath : systemListDirectory(path)) { + if (endsWith(packagePath, ".zip")) { + // Extract package + if (!extractZip(packagePath.c_str(), path.c_str())) { + message += stringf("Could not extract package %s\n", path); + continue; + } + // Remove package + remove(packagePath.c_str()); + } + } + if (!message.empty()) { + osdialog_message(OSDIALOG_WARNING, OSDIALOG_OK, message.c_str()); + } +} + //////////////////// // plugin API //////////////////// @@ -401,6 +408,8 @@ void pluginInit() { // Load plugins from local directory std::string localPlugins = assetLocal("plugins"); mkdir(localPlugins.c_str(), 0755); + info("Unzipping plugins from %s", localPlugins.c_str()); + extractPackages(localPlugins); info("Loading plugins from %s", localPlugins.c_str()); loadPlugins(localPlugins); } @@ -408,10 +417,10 @@ void pluginInit() { void pluginDestroy() { for (Plugin *plugin : gPlugins) { // Free library handle -#if defined(ARCH_WIN) +#if ARCH_WIN if (plugin->handle) FreeLibrary((HINSTANCE)plugin->handle); -#elif defined(ARCH_LIN) || defined(ARCH_MAC) +#else if (plugin->handle) dlclose(plugin->handle); #endif diff --git a/src/util/logger.cpp b/src/util/logger.cpp index cb446c6b..e1e362b2 100644 --- a/src/util/logger.cpp +++ b/src/util/logger.cpp @@ -11,16 +11,16 @@ static auto startTime = std::chrono::high_resolution_clock::now(); void loggerInit() { - #ifdef RELEASE - std::string logFilename = assetLocal("log.txt"); - gLogFile = fopen(logFilename.c_str(), "w"); - #endif +#ifdef RELEASE + std::string logFilename = assetLocal("log.txt"); + logFile = fopen(logFilename.c_str(), "w"); +#endif } void loggerDestroy() { - #ifdef RELEASE - fclose(gLogFile); - #endif +#ifdef RELEASE + fclose(logFile); +#endif } static void printTimestamp() { diff --git a/src/util/string.cpp b/src/util/string.cpp index e2a1b77c..be70a81b 100644 --- a/src/util/string.cpp +++ b/src/util/string.cpp @@ -45,6 +45,10 @@ bool startsWith(std::string str, std::string prefix) { return str.substr(0, prefix.size()) == prefix; } +bool endsWith(std::string str, std::string suffix) { + return str.substr(str.size() - suffix.size(), suffix.size()) == suffix; +} + std::string extractDirectory(std::string path) { char *pathDup = strdup(path.c_str()); std::string directory = dirname(pathDup); diff --git a/src/util/system.cpp b/src/util/system.cpp index dcc63774..340aef8f 100644 --- a/src/util/system.cpp +++ b/src/util/system.cpp @@ -1,18 +1,36 @@ #include "util/common.hpp" +#include + #if ARCH_WIN -#include -#include + #include + #include #endif namespace rack { -void openBrowser(std::string url) { +std::vector systemListDirectory(std::string path) { + std::vector filenames; + DIR *dir = opendir(path.c_str()); + if (dir) { + struct dirent *d; + while ((d = readdir(dir))) { + std::string filename = d->d_name; + if (filename == "." || filename == "..") + continue; + filenames.push_back(path + "/" + filename); + } + closedir(dir); + } + return filenames; +} + +void systemOpenBrowser(std::string url) { #if ARCH_LIN std::string command = "xdg-open " + url; - (void)system(command.c_str()); + (void) system(command.c_str()); #endif #if ARCH_MAC std::string command = "open " + url;