Browse Source

Move string::absolute, directory, filename, filenameBase, and filenameExtension to system::getAbsolute, getDirectory, getFilename, getStem, and getExtension. Reimplement most system:: functions using std::experimental::filesystem. Add system::doesExist, getFileSize, and getTempDir.

tags/v2.0.0
Andrew Belt 4 years ago
parent
commit
3fbd0f77a9
13 changed files with 431 additions and 529 deletions
  1. +0
    -6
      include/common.hpp
  2. +1
    -25
      include/string.hpp
  3. +87
    -34
      include/system.hpp
  4. +1
    -1
      src/app/MenuBar.cpp
  5. +20
    -28
      src/app/ModuleWidget.cpp
  6. +1
    -1
      src/app/Scene.cpp
  7. +0
    -7
      src/common.cpp
  8. +20
    -30
      src/patch.cpp
  9. +35
    -49
      src/plugin.cpp
  10. +0
    -49
      src/string.cpp
  11. +263
    -297
      src/system.cpp
  12. +2
    -1
      src/updater.cpp
  13. +1
    -1
      src/window.cpp

+ 0
- 6
include/common.hpp View File

@@ -205,18 +205,12 @@ static_assert(sizeof(wchar_t) == 2);


// Windows C standard functions are ASCII-8 instead of UTF-8, so redirect these functions to wrappers which convert to UTF-8 // Windows C standard functions are ASCII-8 instead of UTF-8, so redirect these functions to wrappers which convert to UTF-8
#define fopen fopen_u8 #define fopen fopen_u8
#define remove remove_u8
#define rename rename_u8


extern "C" { extern "C" {
FILE* fopen_u8(const char* filename, const char* mode); FILE* fopen_u8(const char* filename, const char* mode);
int remove_u8(const char* path);
int rename_u8(const char* oldname, const char* newname);
} }


namespace std { namespace std {
using ::fopen_u8; using ::fopen_u8;
using ::remove_u8;
using ::rename_u8;
} }
#endif #endif

+ 1
- 25
include/string.hpp View File

@@ -28,31 +28,6 @@ std::string ellipsizePrefix(const std::string& s, size_t len);
bool startsWith(const std::string& str, const std::string& prefix); bool startsWith(const std::string& str, const std::string& prefix);
bool endsWith(const std::string& str, const std::string& suffix); bool endsWith(const std::string& str, const std::string& suffix);


/** Extracts the directory of the path.
Example: directory("dir/file.txt") // "dir"
Calls POSIX dirname().
*/
std::string directory(const std::string& path);
/** Extracts the filename of the path.
Example: directory("dir/file.txt") // "file.txt"
Calls POSIX basename().
*/
std::string filename(const std::string& path);
/** Extracts the portion of a filename without the extension.
Example: filenameBase("file.txt") // "file"
Note: Only works on filenames. Call filename(path) to get the filename of the path.
*/
std::string filenameBase(const std::string& filename);
/** Extracts the extension of a filename.
Example: filenameExtension("file.txt") // "txt"
Note: Only works on filenames. Call filename(path) to get the filename of the path.
*/
std::string filenameExtension(const std::string& filename);
/** Returns the canonicalized absolute path pointed to by `path`, following symlinks.
Returns "" if the symbol is not found.
*/
std::string absolutePath(const std::string& path);

/** Scores how well a query matches a string. /** Scores how well a query matches a string.
A score of 0 means no match. A score of 0 means no match.
The score is arbitrary and is only meaningful for sorting. The score is arbitrary and is only meaningful for sorting.
@@ -85,5 +60,6 @@ std::string U16toU8(const std::wstring& w);
std::wstring U8toU16(const std::string& s); std::wstring U8toU16(const std::string& s);
#endif #endif



} // namespace string } // namespace string
} // namespace rack } // namespace rack

+ 87
- 34
include/system.hpp View File

@@ -2,54 +2,117 @@
#include <list> #include <list>


#include <common.hpp> #include <common.hpp>
#include <experimental/filesystem>




namespace rack { namespace rack {




// In C++17, this will be `std::filesystem`
namespace filesystem = std::experimental::filesystem;


/** Cross-platform functions for operating systems routines /** Cross-platform functions for operating systems routines
*/ */
namespace system { namespace system {




/** Returns a list of all entries (directories, files, symbols) in a directory.
Sorted alphabetically.
// Filesystem

/** Returns a list of all entries (directories, files, symbolic links, etc) in a directory.
`depth` is the number of directories to recurse. 0 depth does not recurse. -1 depth recurses infinitely.
*/ */
std::list<std::string> getEntries(const std::string& path);
std::list<std::string> getEntriesRecursive(const std::string &path, int depth);
std::list<std::string> getEntries(const std::string& dirPath, int depth = 0);
bool doesExist(const std::string& path);
/** Returns whether the given path is a file. */ /** Returns whether the given path is a file. */
bool isFile(const std::string& path); bool isFile(const std::string& path);
/** Returns whether the given path is a directory. */ /** Returns whether the given path is a directory. */
bool isDirectory(const std::string& path); bool isDirectory(const std::string& path);
/** Moves a file. */
void moveFile(const std::string& srcPath, const std::string& destPath);
/** Copies a file. */
void copyFile(const std::string& srcPath, const std::string& destPath);
uint64_t getFileSize(const std::string& path);
/** Moves a file or folder.
Does not overwrite the destination. If this behavior is needed, use remove() or removeRecursively() before moving.
*/
void rename(const std::string& srcPath, const std::string& destPath);
/** Copies a file or folder recursively. */
void copy(const std::string& srcPath, const std::string& destPath);
/** Creates a directory. /** Creates a directory.
The parent directory must exist. The parent directory must exist.
*/ */
void createDirectory(const std::string& path);
bool createDirectory(const std::string& path);
/** Creates all directories up to the path. /** Creates all directories up to the path.
*/ */
void createDirectories(const std::string& path);
/** Deletes a directory.
The directory must be empty. Fails silently.
bool createDirectories(const std::string& path);
/** Deletes a file or empty directory.
Returns whether the deletion was successful.
*/ */
void removeDirectory(const std::string& path);
/** Deletes a directory if empty and all parent directories that are then empty.
bool remove(const std::string& path);
/** Deletes a file or directory recursively.
Returns the number of files and directories that were deleted.
*/ */
void removeDirectories(const std::string& path);
int removeRecursively(const std::string& path);
std::string getWorkingDirectory(); std::string getWorkingDirectory();
void setWorkingDirectory(const std::string& path); void setWorkingDirectory(const std::string& path);
std::string getTempDir();
std::string getAbsolute(const std::string& path);
/** Extracts the parent directory of the path.
Examples:
getDirectory("/var/tmp/example.txt") // "/var/tmp"
getDirectory("/") // ""
getDirectory("/var/tmp/.") // "/var/tmp"
*/
std::string getDirectory(const std::string& path);
/** Extracts the filename of the path.
Examples:
getFilename("/foo/bar.txt") // "bar.txt"
getFilename("/foo/.bar") // ".bar"
getFilename("/foo/bar/") // "."
getFilename("/foo/.") // "."
getFilename("/foo/..") // ".."
getFilename(".") // "."
getFilename("..") // ".."
getFilename("/") // "/"
*/
std::string getFilename(const std::string& path);
/** Extracts the portion of a filename without the extension.
Examples:
getExtension("/foo/bar.txt") // "bar"
getExtension("/foo/.bar") // ""
getExtension("/foo/foo.bar.baz.tar") // "foo.bar.baz"
*/
std::string getStem(const std::string& path);
/** Extracts the extension of a filename, including the dot.
Examples:
getExtension("/foo/bar.txt") // ".txt"
getExtension("/foo/bar.") // "."
getExtension("/foo/bar") // ""
getExtension("/foo/bar.txt/bar.cc") // ".cc"
getExtension("/foo/bar.txt/bar.") // "."
getExtension("/foo/bar.txt/bar") // ""
getExtension("/foo/.") // ""
getExtension("/foo/..") // ""
getExtension("/foo/.hidden") // ".hidden"
*/
std::string getExtension(const std::string& path);

/** Compresses the contents of a folder (recursively) to an archive.
Currently supports the "ustar zstd" format (.tar.zst)
An equivalent shell command is

tar -cf archivePath --zstd -C folderPath .
*/
void archiveFolder(const std::string& archivePath, const std::string& folderPath);
/** Extracts an archive into a folder.
An equivalent shell command is

tar -xf archivePath --zstd -C folderPath
*/
void unarchiveToFolder(const std::string& archivePath, const std::string& folderPath);


// Threading

/** Returns the number of logical simultaneous multithreading (SMT) (e.g. Intel Hyperthreaded) threads on the CPU. */ /** Returns the number of logical simultaneous multithreading (SMT) (e.g. Intel Hyperthreaded) threads on the CPU. */
int getLogicalCoreCount(); int getLogicalCoreCount();
/** Sets a name of the current thread for debuggers and OS-specific process viewers. */ /** Sets a name of the current thread for debuggers and OS-specific process viewers. */
void setThreadName(const std::string& name); void setThreadName(const std::string& name);

// Querying

/** Returns the caller's human-readable stack trace with "\n"-separated lines. */ /** Returns the caller's human-readable stack trace with "\n"-separated lines. */
std::string getStackTrace(); std::string getStackTrace();
/** Returns the current number of nanoseconds since the epoch. /** Returns the current number of nanoseconds since the epoch.
@@ -57,6 +120,10 @@ The goal of this function is to give the most precise (fine-grained) time availa
The epoch is undefined. Do not use this function to get absolute time, as it is different on each OS. The epoch is undefined. Do not use this function to get absolute time, as it is different on each OS.
*/ */
int64_t getNanoseconds(); int64_t getNanoseconds();
std::string getOperatingSystemInfo();

// Applications

/** Opens a URL, also happens to work with PDFs and folders. /** 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. Shell injection is possible, so make sure the URL is trusted or hard coded.
May block, so open in a new thread. May block, so open in a new thread.
@@ -68,20 +135,6 @@ void openFolder(const std::string& path);
The launched process will continue running if the current process is closed. The launched process will continue running if the current process is closed.
*/ */
void runProcessDetached(const std::string& path); void runProcessDetached(const std::string& path);
std::string getOperatingSystemInfo();
/** Compresses the contents of a folder (recursively) to an archive.
Currently supports the "ustar zstd" format (.tar.zst)
An equivalent shell command is

tar -cf archivePath --zstd -C folderPath .
*/
void archiveFolder(const filesystem::path& archivePath, const filesystem::path& folderPath);
/** Extracts an archive into a folder.
An equivalent shell command is

tar -xf archivePath --zstd -C folderPath
*/
void unarchiveToFolder(const filesystem::path& archivePath, const filesystem::path& folderPath);




} // namespace system } // namespace system


+ 1
- 1
src/app/MenuBar.cpp View File

@@ -105,7 +105,7 @@ struct OpenRecentItem : ui::MenuItem {


for (const std::string& path : settings::recentPatchPaths) { for (const std::string& path : settings::recentPatchPaths) {
OpenPathItem* item = new OpenPathItem; OpenPathItem* item = new OpenPathItem;
item->text = string::filename(path);
item->text = system::getFilename(path);
item->path = path; item->path = path;
menu->addChild(item); menu->addChild(item);
} }


+ 20
- 28
src/app/ModuleWidget.cpp View File

@@ -265,12 +265,11 @@ struct ModulePresetItem : ui::MenuItem {
bool hasPresets = false; bool hasPresets = false;
// Note: This is not cached, so opening this menu each time might have a bit of latency. // Note: This is not cached, so opening this menu each time might have a bit of latency.
for (const std::string& presetPath : system::getEntries(presetDir)) { for (const std::string& presetPath : system::getEntries(presetDir)) {
std::string presetFilename = string::filename(presetPath);
if (string::filenameExtension(presetFilename) != "vcvm")
if (system::getExtension(presetPath) != ".vcvm")
continue; continue;
hasPresets = true; hasPresets = true;


std::string presetName = string::filenameBase(presetFilename);
std::string presetName = system::getStem(presetPath);
// Remove "1_", "42_", "001_", etc at the beginning of preset filenames // Remove "1_", "42_", "001_", etc at the beginning of preset filenames
std::regex r("^\\d*_"); std::regex r("^\\d*_");
presetName = std::regex_replace(presetName, r, ""); presetName = std::regex_replace(presetName, r, "");
@@ -690,25 +689,22 @@ void ModuleWidget::loadDialog() {


// Delete directories if empty // Delete directories if empty
DEFER({ DEFER({
system::removeDirectories(presetDir);
system::remove(presetDir);
system::remove(system::getDirectory(presetDir));
}); });


osdialog_filters* filters = osdialog_filters_parse(PRESET_FILTERS); osdialog_filters* filters = osdialog_filters_parse(PRESET_FILTERS);
DEFER({
osdialog_filters_free(filters);
});
DEFER({osdialog_filters_free(filters);});


char* path = osdialog_file(OSDIALOG_OPEN, presetDir.c_str(), NULL, filters);
if (!path) {
char* pathC = osdialog_file(OSDIALOG_OPEN, presetDir.c_str(), NULL, filters);
if (!pathC) {
// No path selected // No path selected
return; return;
} }
DEFER({
free(path);
});
DEFER({free(pathC);});


try { try {
loadAction(path);
loadAction(pathC);
} }
catch (Exception& e) { catch (Exception& e) {
osdialog_message(OSDIALOG_WARNING, OSDIALOG_OK, e.what()); osdialog_message(OSDIALOG_WARNING, OSDIALOG_OK, e.what());
@@ -749,30 +745,26 @@ void ModuleWidget::saveDialog() {


// Delete directories if empty // Delete directories if empty
DEFER({ DEFER({
system::removeDirectories(presetDir);
// These fail silently if the directories are not empty
system::remove(presetDir);
system::remove(system::getDirectory(presetDir));
}); });


osdialog_filters* filters = osdialog_filters_parse(PRESET_FILTERS); osdialog_filters* filters = osdialog_filters_parse(PRESET_FILTERS);
DEFER({
osdialog_filters_free(filters);
});
DEFER({osdialog_filters_free(filters);});


char* path = osdialog_file(OSDIALOG_SAVE, presetDir.c_str(), "Untitled.vcvm", filters);
if (!path) {
char* pathC = osdialog_file(OSDIALOG_SAVE, presetDir.c_str(), "Untitled.vcvm", filters);
if (!pathC) {
// No path selected // No path selected
return; return;
} }
DEFER({
free(path);
});
DEFER({free(pathC);});


std::string pathStr = path;
std::string extension = string::filenameExtension(string::filename(pathStr));
if (extension == "") {
pathStr += ".vcvm";
}
std::string path = pathC;
if (system::getExtension(path) == "")
path += ".vcvm";


save(pathStr);
save(path);
} }


template <class T, typename F> template <class T, typename F>


+ 1
- 1
src/app/Scene.cpp View File

@@ -182,7 +182,7 @@ void Scene::onHoverKey(const event::HoverKey& e) {
void Scene::onPathDrop(const event::PathDrop& e) { void Scene::onPathDrop(const event::PathDrop& e) {
if (e.paths.size() >= 1) { if (e.paths.size() >= 1) {
const std::string& path = e.paths[0]; const std::string& path = e.paths[0];
if (string::filenameExtension(string::filename(path)) == "vcv") {
if (system::getExtension(path) == ".vcv") {
APP->patch->loadPathDialog(path); APP->patch->loadPathDialog(path);
e.consume(this); e.consume(this);
return; return;


+ 0
- 7
src/common.cpp View File

@@ -31,12 +31,5 @@ FILE* fopen_u8(const char* filename, const char* mode) {
return _wfopen(rack::string::U8toU16(filename).c_str(), rack::string::U8toU16(mode).c_str()); return _wfopen(rack::string::U8toU16(filename).c_str(), rack::string::U8toU16(mode).c_str());
} }


int remove_u8(const char* path) {
return _wremove(rack::string::U8toU16(path).c_str());
}

int rename_u8(const char* oldname, const char* newname) {
return _wrename(rack::string::U8toU16(oldname).c_str(), rack::string::U8toU16(newname).c_str());
}


#endif #endif

+ 20
- 30
src/patch.cpp View File

@@ -62,7 +62,7 @@ void PatchManager::save(std::string path) {
INFO("Saving patch %s", path.c_str()); INFO("Saving patch %s", path.c_str());
saveAutosave(); saveAutosave();


system::archiveFolder(filesystem::u8path(path), asset::autosavePath);
system::archiveFolder(path, asset::autosavePath);
} }




@@ -89,30 +89,26 @@ void PatchManager::saveAsDialog() {
std::string filename; std::string filename;
if (this->path == "") { if (this->path == "") {
dir = asset::user("patches"); dir = asset::user("patches");
system::createDirectory(dir);
system::createDirectories(dir);
} }
else { else {
dir = string::directory(this->path);
filename = string::filename(this->path);
dir = system::getDirectory(this->path);
filename = system::getFilename(this->path);
} }


osdialog_filters* filters = osdialog_filters_parse(PATCH_FILTERS); osdialog_filters* filters = osdialog_filters_parse(PATCH_FILTERS);
DEFER({
osdialog_filters_free(filters);
});
DEFER({osdialog_filters_free(filters);});


char* pathC = osdialog_file(OSDIALOG_SAVE, dir.c_str(), filename.c_str(), filters); char* pathC = osdialog_file(OSDIALOG_SAVE, dir.c_str(), filename.c_str(), filters);
if (!pathC) { if (!pathC) {
// Fail silently
// Cancel silently
return; return;
} }
DEFER({
std::free(pathC);
});
DEFER({std::free(pathC);});


// Append .vcv extension if no extension was given. // Append .vcv extension if no extension was given.
std::string path = pathC; std::string path = pathC;
if (string::filenameExtension(string::filename(path)) == "") {
if (system::getExtension(path) == "") {
path += ".vcv"; path += ".vcv";
} }


@@ -150,9 +146,7 @@ void PatchManager::saveAutosave() {
json_t* rootJ = toJson(); json_t* rootJ = toJson();
if (!rootJ) if (!rootJ)
return; return;
DEFER({
json_decref(rootJ);
});
DEFER({json_decref(rootJ);});


// Write to temporary path and then rename it to the correct path // Write to temporary path and then rename it to the correct path
system::createDirectories(asset::autosavePath); system::createDirectories(asset::autosavePath);
@@ -166,7 +160,8 @@ void PatchManager::saveAutosave() {


json_dumpf(rootJ, file, JSON_INDENT(2) | JSON_REAL_PRECISION(9)); json_dumpf(rootJ, file, JSON_INDENT(2) | JSON_REAL_PRECISION(9));
std::fclose(file); std::fclose(file);
system::moveFile(tmpPath, patchPath);
system::remove(patchPath);
system::rename(tmpPath, patchPath);
} }




@@ -186,17 +181,16 @@ static bool isPatchLegacyPre2(std::string path) {
void PatchManager::load(std::string path) { void PatchManager::load(std::string path) {
INFO("Loading patch %s", path.c_str()); INFO("Loading patch %s", path.c_str());


filesystem::remove_all(asset::autosavePath);
filesystem::create_directories(asset::autosavePath);
system::removeRecursively(asset::autosavePath);
system::createDirectories(asset::autosavePath);


if (isPatchLegacyPre2(path)) { if (isPatchLegacyPre2(path)) {
// Move the .vcv file directly to "patch.json".
filesystem::path autosavePath = filesystem::u8path(asset::autosavePath);
filesystem::copy(filesystem::u8path(path), autosavePath / "patch.json");
// Copy the .vcv file directly to "patch.json".
system::copy(path, asset::autosavePath + "/patch.json");
} }
else { else {
// Extract the .vcv file as a .tar.zst archive. // Extract the .vcv file as a .tar.zst archive.
system::unarchiveToFolder(filesystem::u8path(path), asset::autosavePath);
system::unarchiveToFolder(path, asset::autosavePath);
} }


loadAutosave(); loadAutosave();
@@ -241,7 +235,7 @@ void PatchManager::loadAutosave() {
FILE* file = std::fopen(patchPath.c_str(), "r"); FILE* file = std::fopen(patchPath.c_str(), "r");
if (!file) { if (!file) {
// Exit silently // Exit silently
// TODO Load template
// TODO Load template without causing infinite recursion
return; return;
} }
DEFER({ DEFER({
@@ -255,9 +249,7 @@ void PatchManager::loadAutosave() {
osdialog_message(OSDIALOG_WARNING, OSDIALOG_OK, message.c_str()); osdialog_message(OSDIALOG_WARNING, OSDIALOG_OK, message.c_str());
return; return;
} }
DEFER({
json_decref(rootJ);
});
DEFER({json_decref(rootJ);});


fromJson(rootJ); fromJson(rootJ);
} }
@@ -288,13 +280,11 @@ void PatchManager::loadDialog() {
system::createDirectory(dir); system::createDirectory(dir);
} }
else { else {
dir = string::directory(this->path);
dir = system::getDirectory(this->path);
} }


osdialog_filters* filters = osdialog_filters_parse(PATCH_FILTERS); osdialog_filters* filters = osdialog_filters_parse(PATCH_FILTERS);
DEFER({
osdialog_filters_free(filters);
});
DEFER({osdialog_filters_free(filters);});


char* pathC = osdialog_file(OSDIALOG_OPEN, dir.c_str(), NULL, filters); char* pathC = osdialog_file(OSDIALOG_OPEN, dir.c_str(), NULL, filters);
if (!pathC) { if (!pathC) {


+ 35
- 49
src/plugin.cpp View File

@@ -44,30 +44,27 @@ namespace plugin {
//////////////////// ////////////////////


static void* loadLibrary(std::string libraryPath) { static void* loadLibrary(std::string libraryPath) {
#if defined ARCH_WIN
SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS);
std::wstring libraryFilenameW = string::U8toU16(libraryPath);
HINSTANCE handle = LoadLibraryW(libraryFilenameW.c_str());
SetErrorMode(0);
if (!handle) {
int error = GetLastError();
throw Exception(string::f("Failed to load library %s: code %d", libraryPath.c_str(), error));
}
#else
// Plugin uses -rpath=. so change working directory so it can find libRack.
std::string cwd = system::getWorkingDirectory();
system::setWorkingDirectory(asset::systemDir);
// And then change it back
DEFER({
system::setWorkingDirectory(cwd);
});
// Load library with dlopen
void* handle = dlopen(libraryPath.c_str(), RTLD_NOW | RTLD_LOCAL);
if (!handle) {
throw Exception(string::f("Failed to load library %s: %s", libraryPath.c_str(), dlerror()));
}
#endif
return handle;
#if defined ARCH_WIN
SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS);
std::wstring libraryFilenameW = string::U8toU16(libraryPath);
HINSTANCE handle = LoadLibraryW(libraryFilenameW.c_str());
SetErrorMode(0);
if (!handle) {
int error = GetLastError();
throw Exception(string::f("Failed to load library %s: code %d", libraryPath.c_str(), error));
}
#else
// As of Rack v2.0, plugins are linked with `-rpath=.` so change current directory so it can find libRack.
std::string cwd = system::getWorkingDirectory();
system::setWorkingDirectory(asset::systemDir);
// Change it back when we're finished
DEFER({system::setWorkingDirectory(cwd);});
// Load library with dlopen
void* handle = dlopen(libraryPath.c_str(), RTLD_NOW | RTLD_LOCAL);
if (!handle)
throw Exception(string::f("Failed to load library %s: %s", libraryPath.c_str(), dlerror()));
#endif
return handle;
} }


typedef void (*InitCallback)(Plugin*); typedef void (*InitCallback)(Plugin*);
@@ -85,9 +82,8 @@ static InitCallback loadPluginCallback(Plugin* plugin) {
std::string libraryPath = plugin->path + "/plugin." + libraryExt; std::string libraryPath = plugin->path + "/plugin." + libraryExt;


// Check file existence // Check file existence
if (!system::isFile(libraryPath)) {
throw Exception(string::f("Library %s does not exist", libraryPath.c_str()));
}
if (!system::isFile(libraryPath))
throw Exception(string::f("Plugin binary not found at %s", libraryPath.c_str()));


// Load dynamic/shared library // Load dynamic/shared library
plugin->handle = loadLibrary(libraryPath); plugin->handle = loadLibrary(libraryPath);
@@ -99,9 +95,8 @@ static InitCallback loadPluginCallback(Plugin* plugin) {
#else #else
initCallback = (InitCallback) dlsym(plugin->handle, "init"); initCallback = (InitCallback) dlsym(plugin->handle, "init");
#endif #endif
if (!initCallback) {
if (!initCallback)
throw Exception(string::f("Failed to read init() symbol in %s", libraryPath.c_str())); throw Exception(string::f("Failed to read init() symbol in %s", libraryPath.c_str()));
}


return initCallback; return initCallback;
} }
@@ -130,22 +125,16 @@ static Plugin* loadPlugin(std::string path) {


// Load plugin.json // Load plugin.json
std::string manifestFilename = (path == "") ? asset::system("Core.json") : (path + "/plugin.json"); std::string manifestFilename = (path == "") ? asset::system("Core.json") : (path + "/plugin.json");
FILE* file = fopen(manifestFilename.c_str(), "r");
if (!file) {
FILE* file = std::fopen(manifestFilename.c_str(), "r");
if (!file)
throw Exception(string::f("Manifest file %s does not exist", manifestFilename.c_str())); throw Exception(string::f("Manifest file %s does not exist", manifestFilename.c_str()));
}
DEFER({
fclose(file);
});
DEFER({std::fclose(file);});


json_error_t error; json_error_t error;
json_t* rootJ = json_loadf(file, 0, &error); json_t* rootJ = json_loadf(file, 0, &error);
if (!rootJ) {
if (!rootJ)
throw Exception(string::f("JSON parsing error at %s %d:%d %s", manifestFilename.c_str(), error.line, error.column, error.text)); throw Exception(string::f("JSON parsing error at %s %d:%d %s", manifestFilename.c_str(), error.line, error.column, error.text));
}
DEFER({
json_decref(rootJ);
});
DEFER({json_decref(rootJ);});


// Call init callback // Call init callback
InitCallback initCallback; InitCallback initCallback;
@@ -162,12 +151,11 @@ static Plugin* loadPlugin(std::string path) {


// Reject plugin if slug already exists // Reject plugin if slug already exists
Plugin* oldPlugin = getPlugin(plugin->slug); Plugin* oldPlugin = getPlugin(plugin->slug);
if (oldPlugin) {
if (oldPlugin)
throw Exception(string::f("Plugin %s is already loaded, not attempting to load it again", plugin->slug.c_str())); throw Exception(string::f("Plugin %s is already loaded, not attempting to load it again", plugin->slug.c_str()));
}


INFO("Loaded plugin %s v%s from %s", plugin->slug.c_str(), plugin->version.c_str(), plugin->path.c_str());
plugins.push_back(plugin); plugins.push_back(plugin);
INFO("Loaded plugin %s v%s from %s", plugin->slug.c_str(), plugin->version.c_str(), plugin->path.c_str());
} }
catch (Exception& e) { catch (Exception& e) {
WARN("Could not load plugin %s: %s", path.c_str(), e.what()); WARN("Could not load plugin %s: %s", path.c_str(), e.what());
@@ -193,7 +181,7 @@ static void extractPackages(std::string path) {
std::string message; std::string message;


for (std::string packagePath : system::getEntries(path)) { for (std::string packagePath : system::getEntries(path)) {
if (string::filenameExtension(string::filename(packagePath)) != "zip")
if (system::getExtension(packagePath) != ".zip")
continue; continue;
INFO("Extracting package %s", packagePath.c_str()); INFO("Extracting package %s", packagePath.c_str());
// Extract package // Extract package
@@ -201,14 +189,12 @@ static void extractPackages(std::string path) {
system::unarchiveToFolder(packagePath, path); system::unarchiveToFolder(packagePath, path);
} }
catch (Exception& e) { catch (Exception& e) {
WARN("Package %s failed to extract: %s", packagePath.c_str(), e.what());
message += string::f("Could not extract package %s\n", packagePath.c_str());
WARN("Plugin package %s failed to extract: %s", packagePath.c_str(), e.what());
message += string::f("Could not extract plugin package %s\n", packagePath.c_str());
continue; continue;
} }
// Remove package // Remove package
if (remove(packagePath.c_str())) {
WARN("Could not delete file %s: error %d", packagePath.c_str(), errno);
}
system::remove(packagePath.c_str());
} }
if (!message.empty()) { if (!message.empty()) {
osdialog_message(OSDIALOG_WARNING, OSDIALOG_OK, message.c_str()); osdialog_message(OSDIALOG_WARNING, OSDIALOG_OK, message.c_str());


+ 0
- 49
src/string.cpp View File

@@ -87,55 +87,6 @@ bool endsWith(const std::string& str, const std::string& suffix) {
} }




std::string directory(const std::string& path) {
char* pathDup = strdup(path.c_str());
std::string directory = dirname(pathDup);
free(pathDup);
return directory;
}


std::string filename(const std::string& path) {
char* pathDup = strdup(path.c_str());
std::string filename = basename(pathDup);
free(pathDup);
return filename;
}


std::string filenameBase(const std::string& filename) {
size_t pos = filename.rfind('.');
if (pos == std::string::npos)
return filename;
return std::string(filename, 0, pos);
}


std::string filenameExtension(const std::string& filename) {
size_t pos = filename.rfind('.');
if (pos == std::string::npos)
return "";
return std::string(filename, pos + 1);
}


std::string absolutePath(const std::string& path) {
#if defined ARCH_LIN || defined ARCH_MAC
char buf[PATH_MAX];
char* absPathC = realpath(path.c_str(), buf);
if (absPathC)
return absPathC;
#elif defined ARCH_WIN
std::wstring pathW = U8toU16(path);
wchar_t buf[PATH_MAX];
wchar_t* absPathC = _wfullpath(buf, pathW.c_str(), PATH_MAX);
if (absPathC)
return U16toU8(absPathC);
#endif
return "";
}


float fuzzyScore(const std::string& s, const std::string& query) { float fuzzyScore(const std::string& s, const std::string& query) {
size_t pos = s.find(query); size_t pos = s.find(query);
if (pos == std::string::npos) if (pos == std::string::npos)


+ 263
- 297
src/system.cpp View File

@@ -1,6 +1,7 @@
#include <thread> #include <thread>
#include <regex> #include <regex>
#include <chrono> #include <chrono>
#include <experimental/filesystem>


#include <dirent.h> #include <dirent.h>
#include <sys/stat.h> #include <sys/stat.h>
@@ -33,39 +34,31 @@
#include <string.hpp> #include <string.hpp>




namespace rack {
namespace system {
/*
In C++17, this will be `std::filesystem`
Important: When using `fs::path`, always convert strings to UTF-8 using


fs::path p = fs::u8path(s);


std::list<std::string> getEntries(const std::string& path) {
std::list<std::string> 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);
}
filenames.sort();
return filenames;
}
In fact, it's best to work only with strings to avoid forgetting to decode a string as UTF-8.
The need to do this is a fatal flaw of `fs::path`, but at least `std::filesystem` has some helpful operations.
*/
namespace fs = std::experimental::filesystem;




std::list<std::string> getEntriesRecursive(const std::string &path, int depth) {
std::list<std::string> entries = getEntries(path);
if (depth > 0) {
// Don't iterate using iterators because the list will be growing.
size_t limit = entries.size();
auto it = entries.begin();
for (size_t i = 0; i < limit; i++) {
const std::string &entry = *it++;
if (isDirectory(entry)) {
std::list<std::string> subEntries = getEntriesRecursive(entry, depth - 1);
// Append subEntries to entries
namespace rack {
namespace system {


std::list<std::string> getEntries(const std::string& dirPath, int depth) {
std::list<std::string> entries;
for (auto& entry : fs::directory_iterator(fs::u8path(dirPath))) {
std::string subEntry = entry.path().u8string();
entries.push_back(subEntry);
// Recurse if depth > 0 (limited recursion) or depth < 0 (infinite recursion).
if (depth != 0) {
if (fs::is_directory(entry.path())) {
std::list<std::string> subEntries = getEntries(subEntry, depth - 1);
entries.splice(entries.end(), subEntries); entries.splice(entries.end(), subEntries);
} }
} }
@@ -74,311 +67,113 @@ std::list<std::string> getEntriesRecursive(const std::string &path, int depth) {
} }




bool isFile(const std::string& path) {
struct stat statbuf;
if (stat(path.c_str(), &statbuf))
return false;
return S_ISREG(statbuf.st_mode);
bool doesExist(const std::string& path) {
return fs::exists(fs::u8path(path));
} }




bool isDirectory(const std::string& path) {
struct stat statbuf;
if (stat(path.c_str(), &statbuf))
return false;
return S_ISDIR(statbuf.st_mode);
bool isFile(const std::string& path) {
return fs::is_regular_file(fs::u8path(path));
} }




void moveFile(const std::string& srcPath, const std::string& destPath) {
std::remove(destPath.c_str());
// Whether this overwrites existing files is implementation-defined.
// i.e. Mingw64 fails to overwrite.
// This is why we remove the file above.
std::rename(srcPath.c_str(), destPath.c_str());
bool isDirectory(const std::string& path) {
return fs::is_directory(fs::u8path(path));
} }




void copyFile(const std::string& srcPath, const std::string& destPath) {
// Open source
FILE* source = fopen(srcPath.c_str(), "rb");
if (!source)
return;
DEFER({
fclose(source);
});
// Open destination
FILE* dest = fopen(destPath.c_str(), "wb");
if (!dest)
return;
DEFER({
fclose(dest);
});
// Copy buffer
const int bufferSize = (1 << 15);
char buffer[bufferSize];
while (1) {
size_t size = fread(buffer, 1, bufferSize, source);
if (size == 0)
break;
size = fwrite(buffer, 1, size, dest);
if (size == 0)
break;
}
uint64_t getFileSize(const std::string& path) {
return fs::file_size(fs::u8path(path));
} }




void createDirectory(const std::string& path) {
#if defined ARCH_WIN
std::wstring pathW = string::U8toU16(path);
_wmkdir(pathW.c_str());
#else
mkdir(path.c_str(), 0755);
#endif
void rename(const std::string& srcPath, const std::string& destPath) {
fs::rename(fs::u8path(srcPath), fs::u8path(destPath));
} }




void createDirectories(const std::string& path) {
for (size_t i = 1; i < path.size(); i++) {
char c = path[i];
if (c == '/' || c == '\\')
createDirectory(path.substr(0, i));
}
createDirectory(path);
void copy(const std::string& srcPath, const std::string& destPath) {
fs::copy(fs::u8path(srcPath), fs::u8path(destPath), fs::copy_options::recursive);
} }




void removeDirectory(const std::string& path) {
#if defined ARCH_WIN
std::wstring pathW = string::U8toU16(path);
_wrmdir(pathW.c_str());
#else
rmdir(path.c_str());
#endif
bool createDirectory(const std::string& path) {
return fs::create_directory(fs::u8path(path));
} }




void removeDirectories(const std::string& path) {
removeDirectory(path);
for (size_t i = path.size() - 1; i >= 1; i--) {
char c = path[i];
if (c == '/' || c == '\\')
removeDirectory(path.substr(0, i));
}
bool createDirectories(const std::string& path) {
return fs::create_directories(fs::u8path(path));
} }




std::string getWorkingDirectory() {
#if defined ARCH_WIN
wchar_t buf[4096] = L"";
GetCurrentDirectory(sizeof(buf), buf);
return string::U16toU8(buf);
#else
char buf[4096] = "";
getcwd(buf, sizeof(buf));
return buf;
#endif
bool remove(const std::string& path) {
return fs::remove(fs::u8path(path));
} }




void setWorkingDirectory(const std::string& path) {
#if defined ARCH_WIN
std::wstring pathW = string::U8toU16(path);
SetCurrentDirectory(pathW.c_str());
#else
chdir(path.c_str());
#endif
}

int getLogicalCoreCount() {
return std::thread::hardware_concurrency();
int removeRecursively(const std::string& path) {
return fs::remove_all(fs::u8path(path));
} }




void setThreadName(const std::string& name) {
#if defined ARCH_LIN
pthread_setname_np(pthread_self(), name.c_str());
#elif defined ARCH_WIN
// Unsupported on Windows
#endif
std::string getWorkingDirectory() {
return fs::current_path().u8string();
} }




std::string getStackTrace() {
int stackLen = 128;
void* stack[stackLen];
std::string s;

#if defined ARCH_LIN || defined ARCH_MAC
stackLen = backtrace(stack, stackLen);
char** strings = backtrace_symbols(stack, stackLen);

// Skip the first line because it's this function.
for (int i = 1; i < stackLen; i++) {
s += string::f("%d: ", stackLen - i - 1);
std::string line = strings[i];
#if 0
// Parse line
std::regex r(R"((.*)\((.*)\+(.*)\) (.*))");
std::smatch match;
if (std::regex_search(line, match, r)) {
s += match[1].str();
s += "(";
std::string symbol = match[2].str();
// Demangle symbol
char* symbolD = __cxxabiv1::__cxa_demangle(symbol.c_str(), NULL, NULL, NULL);
if (symbolD) {
symbol = symbolD;
free(symbolD);
}
s += symbol;
s += "+";
s += match[3].str();
s += ")";
}
#else
s += line;
#endif
s += "\n";
}
free(strings);

#elif defined ARCH_WIN
HANDLE process = GetCurrentProcess();
SymInitialize(process, NULL, true);
stackLen = CaptureStackBackTrace(0, stackLen, stack, NULL);

SYMBOL_INFO* symbol = (SYMBOL_INFO*) calloc(sizeof(SYMBOL_INFO) + 256, 1);
symbol->MaxNameLen = 255;
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);

for (int i = 1; i < stackLen; i++) {
SymFromAddr(process, (DWORD64) stack[i], 0, symbol);
s += string::f("%d: %s 0x%0x\n", stackLen - i - 1, symbol->Name, symbol->Address);
}
free(symbol);
#endif

return s;
void setWorkingDirectory(const std::string& path) {
fs::current_path(fs::u8path(path));
} }




int64_t getNanoseconds() {
#if defined ARCH_WIN
LARGE_INTEGER counter;
QueryPerformanceCounter(&counter);
LARGE_INTEGER frequency;
QueryPerformanceFrequency(&frequency);
// TODO Check if this is always an integer factor on all CPUs
int64_t nsPerTick = 1000000000LL / frequency.QuadPart;
int64_t time = counter.QuadPart * nsPerTick;
return time;
#endif
#if defined ARCH_LIN
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
int64_t time = int64_t(ts.tv_sec) * 1000000000LL + ts.tv_nsec;
return time;
#endif
#if defined ARCH_MAC
using clock = std::chrono::high_resolution_clock;
using time_point = std::chrono::time_point<clock>;
time_point now = clock::now();
using duration = std::chrono::duration<int64_t, std::nano>;
duration d = now.time_since_epoch();
return d.count();
#endif
std::string getTempDir() {
return fs::temp_directory_path().u8string();
} }




void openBrowser(const std::string& url) {
#if defined ARCH_LIN
std::string command = "xdg-open \"" + url + "\"";
(void) std::system(command.c_str());
#endif
#if defined ARCH_MAC
std::string command = "open \"" + url + "\"";
std::system(command.c_str());
#endif
#if defined ARCH_WIN
std::wstring urlW = string::U8toU16(url);
ShellExecuteW(NULL, L"open", urlW.c_str(), NULL, NULL, SW_SHOWDEFAULT);
#endif
std::string getAbsolutePath(const std::string& path) {
return fs::absolute(fs::u8path(path)).u8string();
} }




void openFolder(const std::string& path) {
#if defined ARCH_LIN
std::string command = "xdg-open \"" + path + "\"";
(void) std::system(command.c_str());
#endif
#if defined ARCH_MAC
std::string command = "open \"" + path + "\"";
std::system(command.c_str());
#endif
#if defined ARCH_WIN
std::wstring pathW = string::U8toU16(path);
ShellExecuteW(NULL, L"explore", pathW.c_str(), NULL, NULL, SW_SHOWDEFAULT);
#endif
std::string getDirectory(const std::string& path) {
return fs::u8path(path).parent_path().u8string();
} }




void runProcessDetached(const std::string& path) {
#if defined ARCH_WIN
SHELLEXECUTEINFOW shExInfo;
ZeroMemory(&shExInfo, sizeof(shExInfo));
shExInfo.cbSize = sizeof(shExInfo);
shExInfo.lpVerb = L"runas";
std::string getFilename(const std::string& path) {
return fs::u8path(path).filename().u8string();
}


std::wstring pathW = string::U8toU16(path);
shExInfo.lpFile = pathW.c_str();
shExInfo.nShow = SW_SHOW;


if (ShellExecuteExW(&shExInfo)) {
// Do nothing
}
#else
// Not implemented on Linux or Mac
assert(0);
#endif
std::string getStem(const std::string& path) {
return fs::u8path(path).stem().u8string();
} }




std::string getOperatingSystemInfo() {
#if defined ARCH_LIN || defined ARCH_MAC
struct utsname u;
uname(&u);
return string::f("%s %s %s %s", u.sysname, u.release, u.version, u.machine);
#elif defined ARCH_WIN
OSVERSIONINFOW info;
ZeroMemory(&info, sizeof(info));
info.dwOSVersionInfoSize = sizeof(info);
GetVersionExW(&info);
// See https://docs.microsoft.com/en-us/windows/desktop/api/winnt/ns-winnt-_osversioninfoa for a list of Windows version numbers.
return string::f("Windows %u.%u", info.dwMajorVersion, info.dwMinorVersion);
#endif
std::string getExtension(const std::string& path) {
return fs::u8path(path).extension().u8string();
} }




/** Behaves like `std::filesystem::relative()`.
/** Returns `p` in relative path form, relative to `base`
Limitation: `p` must be a descendant of `base`. Doesn't support adding `../` to the return path. Limitation: `p` must be a descendant of `base`. Doesn't support adding `../` to the return path.
*/ */
static filesystem::path getRelativePath(filesystem::path p, filesystem::path base = filesystem::current_path()) {
p = filesystem::absolute(p);
base = filesystem::absolute(base);
std::string pStr = p.generic_u8string();
std::string baseStr = base.generic_u8string();
if (pStr.size() < baseStr.size())
static std::string getRelativePath(std::string path, std::string base) {
path = fs::absolute(fs::u8path(path)).generic_u8string();
base = fs::absolute(fs::u8path(base)).generic_u8string();
if (path.size() < base.size())
throw Exception("getRelativePath() error: path is shorter than base"); throw Exception("getRelativePath() error: path is shorter than base");
if (!std::equal(baseStr.begin(), baseStr.end(), pStr.begin()))
if (!std::equal(base.begin(), base.end(), path.begin()))
throw Exception("getRelativePath() error: path does not begin with base"); throw Exception("getRelativePath() error: path does not begin with base");


// If p == base, this correctly returns "."
return "." + std::string(pStr.begin() + baseStr.size(), pStr.end());
// If path == base, this correctly returns "."
return "." + std::string(path.begin() + base.size(), path.end());
} }




void archiveFolder(const filesystem::path& archivePath, const filesystem::path& folderPath) {
void archiveFolder(const std::string& archivePath, const std::string& folderPath) {
// Based on minitar.c create() in libarchive examples // Based on minitar.c create() in libarchive examples
int r; int r;


@@ -388,24 +183,24 @@ void archiveFolder(const filesystem::path& archivePath, const filesystem::path&
archive_write_set_format_ustar(a); archive_write_set_format_ustar(a);
archive_write_add_filter_zstd(a); archive_write_add_filter_zstd(a);
#if defined ARCH_WIN #if defined ARCH_WIN
r = archive_write_open_filename_w(a, archivePath.generic_wstring().c_str());
r = archive_write_open_filename_w(a, string::U8toU16(archivePath).c_str());
#else #else
r = archive_write_open_filename(a, archivePath.generic_u8string().c_str());
r = archive_write_open_filename(a, archivePath.c_str());
#endif #endif
if (r < ARCHIVE_OK) if (r < ARCHIVE_OK)
throw Exception(string::f("archiveFolder() could not open archive %s for writing: %s", archivePath.generic_u8string().c_str(), archive_error_string(a)));
throw Exception(string::f("archiveFolder() could not open archive %s for writing: %s", archivePath.c_str(), archive_error_string(a)));
DEFER({archive_write_close(a);}); DEFER({archive_write_close(a);});


// Open folder for reading // Open folder for reading
struct archive* disk = archive_read_disk_new(); struct archive* disk = archive_read_disk_new();
DEFER({archive_read_free(disk);}); DEFER({archive_read_free(disk);});
#if defined ARCH_WIN #if defined ARCH_WIN
r = archive_read_disk_open_w(disk, folderPath.generic_wstring().c_str());
r = archive_read_disk_open_w(disk, string::U8toU16(folderPath).c_str());
#else #else
r = archive_read_disk_open(disk, folderPath.generic_u8string().c_str());
r = archive_read_disk_open(disk, folderPath.c_str());
#endif #endif
if (r < ARCHIVE_OK) if (r < ARCHIVE_OK)
throw Exception(string::f("archiveFolder() could not open folder %s for reading: %s", folderPath.generic_u8string().c_str(), archive_error_string(a)));
throw Exception(string::f("archiveFolder() could not open folder %s for reading: %s", folderPath.c_str(), archive_error_string(a)));
DEFER({archive_read_close(a);}); DEFER({archive_read_close(a);});


// Iterate folder // Iterate folder
@@ -423,17 +218,18 @@ void archiveFolder(const filesystem::path& archivePath, const filesystem::path&
archive_read_disk_descend(disk); archive_read_disk_descend(disk);


// Convert absolute path to relative path // Convert absolute path to relative path
filesystem::path entryPath =
std::string entryPath;
#if defined ARCH_WIN #if defined ARCH_WIN
archive_entry_pathname_w(entry);
entryPath = string::U16toU8(archive_entry_pathname_w(entry));
#else #else
archive_entry_pathname(entry);
entryPath = archive_entry_pathname(entry);
#endif #endif
entryPath = getRelativePath(entryPath, folderPath); entryPath = getRelativePath(entryPath, folderPath);
#if defined ARCH_WIN #if defined ARCH_WIN
archive_entry_copy_pathname_w(entry, entryPath.generic_wstring().c_str());
// FIXME This doesn't seem to set UTF-8 paths on Windows.
archive_entry_copy_pathname_w(entry, string::U8toU16(entryPath).c_str());
#else #else
archive_entry_set_pathname(entry, entryPath.generic_u8string().c_str());
archive_entry_set_pathname(entry, entryPath.c_str());
#endif #endif


// Write file to archive // Write file to archive
@@ -443,11 +239,11 @@ void archiveFolder(const filesystem::path& archivePath, const filesystem::path&


// Manually copy data // Manually copy data
#if defined ARCH_WIN #if defined ARCH_WIN
filesystem::path entrySourcePath = archive_entry_sourcepath_w(entry);
std::string entrySourcePath = string::U16toU8(archive_entry_sourcepath_w(entry));
#else #else
filesystem::path entrySourcePath = archive_entry_sourcepath(entry);
std::string entrySourcePath = archive_entry_sourcepath(entry);
#endif #endif
FILE* f = std::fopen(entrySourcePath.generic_u8string().c_str(), "rb");
FILE* f = std::fopen(entrySourcePath.c_str(), "rb");
DEFER({std::fclose(f);}); DEFER({std::fclose(f);});
char buf[1 << 14]; char buf[1 << 14];
ssize_t len; ssize_t len;
@@ -458,7 +254,7 @@ void archiveFolder(const filesystem::path& archivePath, const filesystem::path&
} }




void unarchiveToFolder(const filesystem::path& archivePath, const filesystem::path& folderPath) {
void unarchiveToFolder(const std::string& archivePath, const std::string& folderPath) {
// Based on minitar.c extract() in libarchive examples // Based on minitar.c extract() in libarchive examples
int r; int r;


@@ -470,12 +266,12 @@ void unarchiveToFolder(const filesystem::path& archivePath, const filesystem::pa
archive_read_support_format_tar(a); archive_read_support_format_tar(a);
// archive_read_support_format_all(a); // archive_read_support_format_all(a);
#if defined ARCH_WIN #if defined ARCH_WIN
r = archive_read_open_filename_w(a, archivePath.generic_wstring().c_str(), 1 << 14);
r = archive_read_open_filename_w(a, string::U8toU16(archivePath).c_str(), 1 << 14);
#else #else
r = archive_read_open_filename(a, archivePath.generic_u8string().c_str(), 1 << 14);
r = archive_read_open_filename(a, archivePath.c_str(), 1 << 14);
#endif #endif
if (r < ARCHIVE_OK) if (r < ARCHIVE_OK)
throw Exception(string::f("unzipToFolder() could not open archive %s: %s", archivePath.generic_u8string().c_str(), archive_error_string(a)));
throw Exception(string::f("unzipToFolder() could not open archive %s: %s", archivePath.c_str(), archive_error_string(a)));
DEFER({archive_read_close(a);}); DEFER({archive_read_close(a);});


// Open folder for writing // Open folder for writing
@@ -496,14 +292,14 @@ void unarchiveToFolder(const filesystem::path& archivePath, const filesystem::pa
throw Exception(string::f("unzipToFolder() could not read entry from archive: %s", archive_error_string(a))); throw Exception(string::f("unzipToFolder() could not read entry from archive: %s", archive_error_string(a)));


// Convert relative pathname to absolute based on folderPath // Convert relative pathname to absolute based on folderPath
filesystem::path entryPath = filesystem::u8path(archive_entry_pathname(entry));
if (!entryPath.is_relative())
throw Exception(string::f("unzipToFolder() does not support absolute paths: %s", entryPath.generic_u8string().c_str()));
entryPath = filesystem::absolute(entryPath, folderPath);
std::string entryPath = archive_entry_pathname(entry);
if (!fs::u8path(entryPath).is_relative())
throw Exception(string::f("unzipToFolder() does not support absolute paths: %s", entryPath.c_str()));
entryPath = fs::absolute(fs::u8path(entryPath), fs::u8path(folderPath)).u8string();
#if defined ARCH_WIN #if defined ARCH_WIN
archive_entry_copy_pathname_w(entry, entryPath.generic_wstring().c_str());
archive_entry_copy_pathname_w(entry, string::U8toU16(entryPath).c_str());
#else #else
archive_entry_set_pathname(entry, entryPath.generic_u8string().c_str());
archive_entry_set_pathname(entry, entryPath.c_str());
#endif #endif


// Write entry to disk // Write entry to disk
@@ -537,5 +333,175 @@ void unarchiveToFolder(const filesystem::path& archivePath, const filesystem::pa
} }




int getLogicalCoreCount() {
return std::thread::hardware_concurrency();
}


void setThreadName(const std::string& name) {
#if defined ARCH_LIN
pthread_setname_np(pthread_self(), name.c_str());
#elif defined ARCH_WIN
// Unsupported on Windows
#endif
}


std::string getStackTrace() {
int stackLen = 128;
void* stack[stackLen];
std::string s;

#if defined ARCH_LIN || defined ARCH_MAC
stackLen = backtrace(stack, stackLen);
char** strings = backtrace_symbols(stack, stackLen);

// Skip the first line because it's this function.
for (int i = 1; i < stackLen; i++) {
s += string::f("%d: ", stackLen - i - 1);
std::string line = strings[i];
#if 0
// Parse line
std::regex r(R"((.*)\((.*)\+(.*)\) (.*))");
std::smatch match;
if (std::regex_search(line, match, r)) {
s += match[1].str();
s += "(";
std::string symbol = match[2].str();
// Demangle symbol
char* symbolD = __cxxabiv1::__cxa_demangle(symbol.c_str(), NULL, NULL, NULL);
if (symbolD) {
symbol = symbolD;
free(symbolD);
}
s += symbol;
s += "+";
s += match[3].str();
s += ")";
}
#else
s += line;
#endif
s += "\n";
}
free(strings);

#elif defined ARCH_WIN
HANDLE process = GetCurrentProcess();
SymInitialize(process, NULL, true);
stackLen = CaptureStackBackTrace(0, stackLen, stack, NULL);

SYMBOL_INFO* symbol = (SYMBOL_INFO*) calloc(sizeof(SYMBOL_INFO) + 256, 1);
symbol->MaxNameLen = 255;
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);

for (int i = 1; i < stackLen; i++) {
SymFromAddr(process, (DWORD64) stack[i], 0, symbol);
s += string::f("%d: %s 0x%0x\n", stackLen - i - 1, symbol->Name, symbol->Address);
}
free(symbol);
#endif

return s;
}


int64_t getNanoseconds() {
#if defined ARCH_WIN
LARGE_INTEGER counter;
QueryPerformanceCounter(&counter);
LARGE_INTEGER frequency;
QueryPerformanceFrequency(&frequency);
// TODO Check if this is always an integer factor on all CPUs
int64_t nsPerTick = 1000000000LL / frequency.QuadPart;
int64_t time = counter.QuadPart * nsPerTick;
return time;
#endif
#if defined ARCH_LIN
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
int64_t time = int64_t(ts.tv_sec) * 1000000000LL + ts.tv_nsec;
return time;
#endif
#if defined ARCH_MAC
using clock = std::chrono::high_resolution_clock;
using time_point = std::chrono::time_point<clock>;
time_point now = clock::now();
using duration = std::chrono::duration<int64_t, std::nano>;
duration d = now.time_since_epoch();
return d.count();
#endif
}


std::string getOperatingSystemInfo() {
#if defined ARCH_LIN || defined ARCH_MAC
struct utsname u;
uname(&u);
return string::f("%s %s %s %s", u.sysname, u.release, u.version, u.machine);
#elif defined ARCH_WIN
OSVERSIONINFOW info;
ZeroMemory(&info, sizeof(info));
info.dwOSVersionInfoSize = sizeof(info);
GetVersionExW(&info);
// See https://docs.microsoft.com/en-us/windows/desktop/api/winnt/ns-winnt-_osversioninfoa for a list of Windows version numbers.
return string::f("Windows %u.%u", info.dwMajorVersion, info.dwMinorVersion);
#endif
}


void openBrowser(const std::string& url) {
#if defined ARCH_LIN
std::string command = "xdg-open \"" + url + "\"";
(void) std::system(command.c_str());
#endif
#if defined ARCH_MAC
std::string command = "open \"" + url + "\"";
std::system(command.c_str());
#endif
#if defined ARCH_WIN
std::wstring urlW = string::U8toU16(url);
ShellExecuteW(NULL, L"open", urlW.c_str(), NULL, NULL, SW_SHOWDEFAULT);
#endif
}


void openFolder(const std::string& path) {
#if defined ARCH_LIN
std::string command = "xdg-open \"" + path + "\"";
(void) std::system(command.c_str());
#endif
#if defined ARCH_MAC
std::string command = "open \"" + path + "\"";
std::system(command.c_str());
#endif
#if defined ARCH_WIN
std::wstring pathW = string::U8toU16(path);
ShellExecuteW(NULL, L"explore", pathW.c_str(), NULL, NULL, SW_SHOWDEFAULT);
#endif
}


void runProcessDetached(const std::string& path) {
#if defined ARCH_WIN
SHELLEXECUTEINFOW shExInfo;
ZeroMemory(&shExInfo, sizeof(shExInfo));
shExInfo.cbSize = sizeof(shExInfo);
shExInfo.lpVerb = L"runas";

std::wstring pathW = string::U8toU16(path);
shExInfo.lpFile = pathW.c_str();
shExInfo.nShow = SW_SHOW;

if (ShellExecuteExW(&shExInfo)) {
// Do nothing
}
#else
// Not implemented on Linux or Mac
assert(0);
#endif
}


} // namespace system } // namespace system
} // namespace rack } // namespace rack

+ 2
- 1
src/updater.cpp View File

@@ -64,7 +64,8 @@ void update() {
return; return;


// Download update // Download update
std::string filename = string::filename(network::urlPath(downloadUrl));
// HACK getFilename is only supposed to be used for filesystem paths, not URLs.
std::string filename = system::getFilename(network::urlPath(downloadUrl));
std::string path = asset::user(filename); std::string path = asset::user(filename);
INFO("Downloading update %s to %s", downloadUrl.c_str(), path.c_str()); INFO("Downloading update %s to %s", downloadUrl.c_str(), path.c_str());
network::requestDownload(downloadUrl, path, &progress); network::requestDownload(downloadUrl, path, &progress);


+ 1
- 1
src/window.cpp View File

@@ -374,7 +374,7 @@ void Window::run() {
windowTitle += " - "; windowTitle += " - ";
if (!APP->history->isSaved()) if (!APP->history->isSaved())
windowTitle += "*"; windowTitle += "*";
windowTitle += string::filename(APP->patch->path);
windowTitle += system::getFilename(APP->patch->path);
} }
if (windowTitle != internal->lastWindowTitle) { if (windowTitle != internal->lastWindowTitle) {
glfwSetWindowTitle(win, windowTitle.c_str()); glfwSetWindowTitle(win, windowTitle.c_str());


Loading…
Cancel
Save