@@ -27,7 +27,7 @@ E.g. | |||
DEPRECATED void foo(); | |||
*/ | |||
#if defined(__GNUC__) || defined(__clang__) | |||
#define DEPRECATED __attribute__ ((deprecated)) | |||
#define DEPRECATED __attribute__((deprecated)) | |||
#elif defined(_MSC_VER) | |||
#define DEPRECATED __declspec(deprecated) | |||
#endif | |||
@@ -168,6 +168,10 @@ Can be subclassed to throw/catch specific custom exceptions. | |||
*/ | |||
struct Exception : std::exception { | |||
std::string msg; | |||
// Attribute index 1 refers to `Exception*` argument so use 2. | |||
__attribute__((format(printf, 2, 3))) | |||
Exception(const char* format, ...); | |||
Exception(const std::string& msg) : msg(msg) {} | |||
const char* what() const noexcept override { | |||
return msg.c_str(); | |||
@@ -35,7 +35,7 @@ void destroy(); | |||
/** Do not use this function directly. Use the macros above. | |||
Thread-safe, meaning messages cannot overlap each other in the log. | |||
*/ | |||
__attribute__ ((format(printf, 5, 6))) | |||
__attribute__((format(printf, 5, 6))) | |||
void log(Level level, const char* filename, int line, const char* func, const char* format, ...); | |||
/** Returns whether the current log file failed to end properly, due to a possible crash. | |||
Must be called *before* init(). | |||
@@ -100,7 +100,7 @@ | |||
#undef PRIVATE | |||
#define PRIVATE __attribute__ ((warning ("Using private function or symbol"))) | |||
#define PRIVATE __attribute__((warning ("Using private function or symbol"))) | |||
namespace rack { | |||
@@ -1,4 +1,5 @@ | |||
#pragma once | |||
#include <stdarg.h> | |||
#include <vector> | |||
#include <common.hpp> | |||
@@ -15,7 +16,9 @@ namespace string { | |||
/** Converts a `printf()` format string and optional arguments into a std::string. | |||
Remember that "%s" must reference a `char *`, so use `.c_str()` for `std::string`s, otherwise you might get binary garbage. | |||
*/ | |||
__attribute__((format(printf, 1, 2))) | |||
std::string f(const char* format, ...); | |||
std::string fV(const char* format, va_list args); | |||
/** Replaces all characters to lowercase letters */ | |||
std::string lowercase(const std::string& s); | |||
/** Replaces all characters to uppercase letters */ | |||
@@ -737,7 +737,7 @@ void ModuleWidget::pasteClipboardAction() { | |||
void ModuleWidget::load(std::string filename) { | |||
FILE* file = std::fopen(filename.c_str(), "r"); | |||
if (!file) | |||
throw Exception(string::f("Could not load patch file %s", filename.c_str())); | |||
throw Exception("Could not load patch file %s", filename.c_str()); | |||
DEFER({std::fclose(file);}); | |||
INFO("Loading preset %s", filename.c_str()); | |||
@@ -745,7 +745,7 @@ void ModuleWidget::load(std::string filename) { | |||
json_error_t error; | |||
json_t* moduleJ = json_loadf(file, 0, &error); | |||
if (!moduleJ) | |||
throw Exception(string::f("File is not a valid patch file. JSON parsing error at %s %d:%d %s", error.source, error.line, error.column, error.text)); | |||
throw Exception("File is not a valid patch file. JSON parsing error at %s %d:%d %s", error.source, error.line, error.column, error.text); | |||
DEFER({json_decref(moduleJ);}); | |||
// Don't use IDs from JSON | |||
@@ -21,6 +21,14 @@ const std::string API_URL = "https://api.vcvrack.com"; | |||
const std::string API_VERSION = "2"; | |||
Exception::Exception(const char* format, ...) { | |||
va_list args; | |||
va_start(args, format); | |||
msg = string::fV(format, args); | |||
va_end(args); | |||
} | |||
} // namespace rack | |||
@@ -48,7 +48,7 @@ static void* loadLibrary(std::string libraryPath) { | |||
SetErrorMode(0); | |||
if (!handle) { | |||
int error = GetLastError(); | |||
throw Exception(string::f("Failed to load library %s: code %d", libraryPath.c_str(), error)); | |||
throw Exception("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. | |||
@@ -59,7 +59,7 @@ static void* loadLibrary(std::string libraryPath) { | |||
// 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())); | |||
throw Exception("Failed to load library %s: %s", libraryPath.c_str(), dlerror()); | |||
#endif | |||
return handle; | |||
} | |||
@@ -80,7 +80,7 @@ static InitCallback loadPluginCallback(Plugin* plugin) { | |||
// Check file existence | |||
if (!system::isFile(libraryPath)) | |||
throw Exception(string::f("Plugin binary not found at %s", libraryPath.c_str())); | |||
throw Exception("Plugin binary not found at %s", libraryPath.c_str()); | |||
// Load dynamic/shared library | |||
plugin->handle = loadLibrary(libraryPath); | |||
@@ -93,7 +93,7 @@ static InitCallback loadPluginCallback(Plugin* plugin) { | |||
initCallback = (InitCallback) dlsym(plugin->handle, "init"); | |||
#endif | |||
if (!initCallback) | |||
throw Exception(string::f("Failed to read init() symbol in %s", libraryPath.c_str())); | |||
throw Exception("Failed to read init() symbol in %s", libraryPath.c_str()); | |||
return initCallback; | |||
} | |||
@@ -124,13 +124,13 @@ static Plugin* loadPlugin(std::string path) { | |||
std::string manifestFilename = (path == "") ? asset::system("Core.json") : system::join(path, "plugin.json"); | |||
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("Manifest file %s does not exist", manifestFilename.c_str()); | |||
DEFER({std::fclose(file);}); | |||
json_error_t error; | |||
json_t* rootJ = json_loadf(file, 0, &error); | |||
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("JSON parsing error at %s %d:%d %s", manifestFilename.c_str(), error.line, error.column, error.text); | |||
DEFER({json_decref(rootJ);}); | |||
// Call init callback | |||
@@ -149,7 +149,7 @@ static Plugin* loadPlugin(std::string path) { | |||
// Reject plugin if slug already exists | |||
Plugin* oldPlugin = getPlugin(plugin->slug); | |||
if (oldPlugin) | |||
throw Exception(string::f("Plugin %s is already loaded, not attempting to load it again", plugin->slug.c_str())); | |||
throw Exception("Plugin %s is already loaded, not attempting to load it again", plugin->slug.c_str()); | |||
plugins.push_back(plugin); | |||
INFO("Loaded plugin %s v%s from %s", plugin->slug.c_str(), plugin->version.c_str(), plugin->path.c_str()); | |||
@@ -288,7 +288,7 @@ Model* modelFromJson(json_t* moduleJ) { | |||
// Get Model | |||
Model* model = getModel(pluginSlug, modelSlug); | |||
if (!model) | |||
throw Exception(string::f("Could not find module \"%s\" of plugin \"%s\"", modelSlug.c_str(), pluginSlug.c_str())); | |||
throw Exception("Could not find module \"%s\" of plugin \"%s\"", modelSlug.c_str(), pluginSlug.c_str()); | |||
return model; | |||
} | |||
@@ -19,7 +19,7 @@ void Model::fromJson(json_t* rootJ) { | |||
if (nameJ) | |||
name = json_string_value(nameJ); | |||
if (name == "") | |||
throw Exception(string::f("No module name for slug %s", slug.c_str())); | |||
throw Exception("No module name for slug %s", slug.c_str()); | |||
json_t* descriptionJ = json_object_get(rootJ, "description"); | |||
if (descriptionJ) | |||
@@ -39,14 +39,14 @@ void Plugin::fromJson(json_t* rootJ) { | |||
if (slug == "") | |||
throw Exception("No plugin slug"); | |||
if (!isSlugValid(slug)) | |||
throw Exception(string::f("Plugin slug \"%s\" is invalid", slug.c_str())); | |||
throw Exception("Plugin slug \"%s\" is invalid", slug.c_str()); | |||
// version | |||
json_t* versionJ = json_object_get(rootJ, "version"); | |||
if (versionJ) | |||
version = json_string_value(versionJ); | |||
if (!string::startsWith(version, ABI_VERSION + ".")) | |||
throw Exception(string::f("Plugin version %s does not match Rack ABI version %s", version.c_str(), ABI_VERSION.c_str())); | |||
throw Exception("Plugin version %s does not match Rack ABI version %s", version.c_str(), ABI_VERSION.c_str()); | |||
if (version == "") | |||
throw Exception("No plugin version"); | |||
@@ -120,26 +120,26 @@ void Plugin::fromJson(json_t* rootJ) { | |||
// Get model slug | |||
json_t* modelSlugJ = json_object_get(moduleJ, "slug"); | |||
if (!modelSlugJ) { | |||
throw Exception(string::f("No slug found for module entry %d", moduleId)); | |||
throw Exception("No slug found for module entry %" PRId64, moduleId); | |||
} | |||
std::string modelSlug = json_string_value(modelSlugJ); | |||
// Check model slug | |||
if (!isSlugValid(modelSlug)) { | |||
throw Exception(string::f("Module slug \"%s\" is invalid", modelSlug.c_str())); | |||
throw Exception("Module slug \"%s\" is invalid", modelSlug.c_str()); | |||
} | |||
// Get model | |||
Model* model = getModel(modelSlug); | |||
if (!model) { | |||
throw Exception(string::f("Manifest contains module %s but it is not defined in the plugin", modelSlug.c_str())); | |||
throw Exception("Manifest contains module %s but it is not defined in the plugin", modelSlug.c_str()); | |||
} | |||
model->fromJson(moduleJ); | |||
} | |||
} | |||
else { | |||
WARN("No modules in plugin %s", slug.c_str()); | |||
WARN("No modules in plugin manifest %s", slug.c_str()); | |||
} | |||
// Remove models without names | |||
@@ -32,7 +32,7 @@ struct RtAudioDevice : audio::Device { | |||
rtAudio = new RtAudio(api); | |||
rtAudio->showWarnings(false); | |||
if (!rtAudio) { | |||
throw Exception(string::f("Failed to create RtAudio driver %d", api)); | |||
throw Exception("Failed to create RtAudio driver %d", api); | |||
} | |||
rtAudio->showWarnings(false); | |||
@@ -40,7 +40,7 @@ struct RtAudioDevice : audio::Device { | |||
deviceInfo = rtAudio->getDeviceInfo(deviceId); | |||
} | |||
catch (RtAudioError& e) { | |||
throw Exception(string::f("Failed to query RtAudio device: %s", e.what())); | |||
throw Exception("Failed to query RtAudio device: %s", e.what()); | |||
} | |||
this->deviceId = deviceId; | |||
@@ -61,7 +61,7 @@ struct RtAudioDevice : audio::Device { | |||
void openStream() { | |||
// Open new device | |||
if (deviceInfo.outputChannels == 0 && deviceInfo.inputChannels == 0) { | |||
throw Exception(string::f("RtAudio device %d has 0 inputs and 0 outputs", deviceId)); | |||
throw Exception("RtAudio device %d has 0 inputs and 0 outputs", deviceId); | |||
} | |||
inputParameters = RtAudio::StreamParameters(); | |||
@@ -103,7 +103,7 @@ struct RtAudioDevice : audio::Device { | |||
&rtAudioCallback, this, &options, NULL); | |||
} | |||
catch (RtAudioError& e) { | |||
throw Exception(string::f("Failed to open RtAudio stream: %s", e.what())); | |||
throw Exception("Failed to open RtAudio stream: %s", e.what()); | |||
} | |||
INFO("Starting RtAudio stream %d", deviceId); | |||
@@ -111,7 +111,7 @@ struct RtAudioDevice : audio::Device { | |||
rtAudio->startStream(); | |||
} | |||
catch (RtAudioError& e) { | |||
throw Exception(string::f("Failed to start RtAudio stream: %s", e.what())); | |||
throw Exception("Failed to start RtAudio stream: %s", e.what()); | |||
} | |||
// Update sample rate to actual value | |||
@@ -127,7 +127,7 @@ struct RtAudioDevice : audio::Device { | |||
rtAudio->stopStream(); | |||
} | |||
catch (RtAudioError& e) { | |||
throw Exception(string::f("Failed to stop RtAudio stream %s", e.what())); | |||
throw Exception("Failed to stop RtAudio stream %s", e.what()); | |||
} | |||
} | |||
if (rtAudio->isStreamOpen()) { | |||
@@ -136,7 +136,7 @@ struct RtAudioDevice : audio::Device { | |||
rtAudio->closeStream(); | |||
} | |||
catch (RtAudioError& e) { | |||
throw Exception(string::f("Failed to close RtAudio stream %s", e.what())); | |||
throw Exception("Failed to close RtAudio stream %s", e.what()); | |||
} | |||
} | |||
INFO("Closed RtAudio stream"); | |||
@@ -35,7 +35,7 @@ struct RtMidiInputDevice : midi::InputDevice { | |||
rtMidiIn = new RtMidiIn((RtMidi::Api) driverId, "VCV Rack"); | |||
} | |||
catch (RtMidiError& e) { | |||
throw Exception(string::f("Failed to create RtMidi input driver %d: %s", driverId, e.what())); | |||
throw Exception("Failed to create RtMidi input driver %d: %s", driverId, e.what()); | |||
} | |||
rtMidiIn->setErrorCallback(rtMidiErrorCallback); | |||
rtMidiIn->ignoreTypes(false, false, false); | |||
@@ -45,14 +45,14 @@ struct RtMidiInputDevice : midi::InputDevice { | |||
name = rtMidiIn->getPortName(deviceId); | |||
} | |||
catch (RtMidiError& e) { | |||
throw Exception(string::f("Failed to get RtMidi input device name: %s", e.what())); | |||
throw Exception("Failed to get RtMidi input device name: %s", e.what()); | |||
} | |||
try { | |||
rtMidiIn->openPort(deviceId, "VCV Rack input"); | |||
} | |||
catch (RtMidiError& e) { | |||
throw Exception(string::f("Failed to open RtMidi input device: %s", e.what())); | |||
throw Exception("Failed to open RtMidi input device: %s", e.what()); | |||
} | |||
} | |||
@@ -105,7 +105,7 @@ struct RtMidiOutputDevice : midi::OutputDevice { | |||
rtMidiOut = new RtMidiOut((RtMidi::Api) driverId, "VCV Rack"); | |||
} | |||
catch (RtMidiError& e) { | |||
throw Exception(string::f("Failed to create RtMidi output driver %d: %s", driverId, e.what())); | |||
throw Exception("Failed to create RtMidi output driver %d: %s", driverId, e.what()); | |||
} | |||
rtMidiOut->setErrorCallback(rtMidiErrorCallback); | |||
@@ -113,14 +113,14 @@ struct RtMidiOutputDevice : midi::OutputDevice { | |||
name = rtMidiOut->getPortName(deviceId); | |||
} | |||
catch (RtMidiError& e) { | |||
throw Exception(string::f("Failed to get RtMidi output device name: %s", e.what())); | |||
throw Exception("Failed to get RtMidi output device name: %s", e.what()); | |||
} | |||
try { | |||
rtMidiOut->openPort(deviceId, "VCV Rack output"); | |||
} | |||
catch (RtMidiError& e) { | |||
throw Exception(string::f("Failed to get RtMidi output device name: %s", e.what())); | |||
throw Exception("Failed to get RtMidi output device name: %s", e.what()); | |||
} | |||
startThread(); | |||
@@ -211,7 +211,7 @@ struct RtMidiDriver : midi::Driver { | |||
rtMidiIn = new RtMidiIn((RtMidi::Api) driverId); | |||
} | |||
catch (RtMidiError& e) { | |||
throw Exception(string::f("Failed to create RtMidi input driver %d: %s", driverId, e.what())); | |||
throw Exception("Failed to create RtMidi input driver %d: %s", driverId, e.what()); | |||
} | |||
rtMidiIn->setErrorCallback(rtMidiErrorCallback); | |||
@@ -219,7 +219,7 @@ struct RtMidiDriver : midi::Driver { | |||
rtMidiOut = new RtMidiOut((RtMidi::Api) driverId); | |||
} | |||
catch (RtMidiError& e) { | |||
throw Exception(string::f("Failed to create RtMidi output driver %d: %s", driverId, e.what())); | |||
throw Exception("Failed to create RtMidi output driver %d: %s", driverId, e.what()); | |||
} | |||
rtMidiOut->setErrorCallback(rtMidiErrorCallback); | |||
} | |||
@@ -250,7 +250,7 @@ struct RtMidiDriver : midi::Driver { | |||
count = rtMidiIn->getPortCount(); | |||
} | |||
catch (RtMidiError& e) { | |||
throw Exception(string::f("Failed to get RtMidi input device count: %s", e.what())); | |||
throw Exception("Failed to get RtMidi input device count: %s", e.what()); | |||
} | |||
std::vector<int> deviceIds; | |||
@@ -266,7 +266,7 @@ struct RtMidiDriver : midi::Driver { | |||
return rtMidiIn->getPortName(deviceId); | |||
} | |||
catch (RtMidiError& e) { | |||
throw Exception(string::f("Failed to get RtMidi input device name: %s", e.what())); | |||
throw Exception("Failed to get RtMidi input device name: %s", e.what()); | |||
} | |||
} | |||
@@ -279,7 +279,7 @@ struct RtMidiDriver : midi::Driver { | |||
inputDevices[deviceId] = device = new RtMidiInputDevice(driverId, deviceId); | |||
} | |||
catch (RtMidiError& e) { | |||
throw Exception(string::f("Failed to create RtMidi input device: %s", e.what())); | |||
throw Exception("Failed to create RtMidi input device: %s", e.what()); | |||
} | |||
} | |||
@@ -301,7 +301,7 @@ struct RtMidiDriver : midi::Driver { | |||
delete device; | |||
} | |||
catch (RtMidiError& e) { | |||
throw Exception(string::f("Failed to delete RtMidi input device: %s", e.what())); | |||
throw Exception("Failed to delete RtMidi input device: %s", e.what()); | |||
} | |||
} | |||
} | |||
@@ -313,7 +313,7 @@ struct RtMidiDriver : midi::Driver { | |||
count = rtMidiOut->getPortCount(); | |||
} | |||
catch (RtMidiError& e) { | |||
throw Exception(string::f("Failed to get RtMidi output device count: %s", e.what())); | |||
throw Exception("Failed to get RtMidi output device count: %s", e.what()); | |||
} | |||
std::vector<int> deviceIds; | |||
@@ -329,7 +329,7 @@ struct RtMidiDriver : midi::Driver { | |||
return rtMidiOut->getPortName(deviceId); | |||
} | |||
catch (RtMidiError& e) { | |||
throw Exception(string::f("Failed to get RtMidi output device count: %s", e.what())); | |||
throw Exception("Failed to get RtMidi output device count: %s", e.what()); | |||
} | |||
} | |||
@@ -342,7 +342,7 @@ struct RtMidiDriver : midi::Driver { | |||
outputDevices[deviceId] = device = new RtMidiOutputDevice(driverId, deviceId); | |||
} | |||
catch (RtMidiError& e) { | |||
throw Exception(string::f("Failed to create RtMidi output device: %s", e.what())); | |||
throw Exception("Failed to create RtMidi output device: %s", e.what()); | |||
} | |||
} | |||
@@ -364,7 +364,7 @@ struct RtMidiDriver : midi::Driver { | |||
delete device; | |||
} | |||
catch (RtMidiError& e) { | |||
throw Exception(string::f("Failed to delete RtMidi output device: %s", e.what())); | |||
throw Exception("Failed to delete RtMidi output device: %s", e.what()); | |||
} | |||
} | |||
} | |||
@@ -356,7 +356,7 @@ void load(const std::string& path) { | |||
json_error_t error; | |||
json_t* rootJ = json_loadf(file, 0, &error); | |||
if (!rootJ) | |||
throw Exception(string::f("Settings file has invalid JSON at %d:%d %s", error.line, error.column, error.text)); | |||
throw Exception("Settings file has invalid JSON at %d:%d %s", error.line, error.column, error.text); | |||
fromJson(rootJ); | |||
json_decref(rootJ); | |||
@@ -1,6 +1,7 @@ | |||
#include <cctype> // for tolower and toupper | |||
#include <algorithm> // for transform and equal | |||
#include <libgen.h> // for dirname and basename | |||
#include <stdarg.h> | |||
#if defined ARCH_WIN | |||
#include <windows.h> // for MultiByteToWideChar | |||
@@ -16,17 +17,24 @@ namespace string { | |||
std::string f(const char* format, ...) { | |||
va_list args; | |||
va_start(args, format); | |||
std::string s = fV(format, args); | |||
va_end(args); | |||
return s; | |||
} | |||
std::string fV(const char* format, va_list args) { | |||
// va_lists cannot be reused but we need it twice, so clone args. | |||
va_list args2; | |||
va_copy(args2, args); | |||
// Compute size of required buffer | |||
int size = vsnprintf(NULL, 0, format, args); | |||
va_end(args); | |||
if (size < 0) | |||
return ""; | |||
// Create buffer | |||
std::string s; | |||
s.resize(size); | |||
va_start(args, format); | |||
vsnprintf(&s[0], size + 1, format, args); | |||
va_end(args); | |||
vsnprintf(&s[0], size + 1, format, args2); | |||
return s; | |||
} | |||
@@ -303,7 +303,7 @@ void archiveFolder(const std::string& archivePath, const std::string& folderPath | |||
assert(0 <= compressionLevel && compressionLevel <= 19); | |||
r = archive_write_set_filter_option(a, NULL, "compression-level", std::to_string(compressionLevel).c_str()); | |||
if (r < ARCHIVE_OK) | |||
throw Exception(string::f("Archiver could not set filter option: %s", archive_error_string(a))); | |||
throw Exception("Archiver could not set filter option: %s", archive_error_string(a)); | |||
#if defined ARCH_WIN | |||
r = archive_write_open_filename_w(a, string::U8toU16(archivePath).c_str()); | |||
@@ -311,7 +311,7 @@ void archiveFolder(const std::string& archivePath, const std::string& folderPath | |||
r = archive_write_open_filename(a, archivePath.c_str()); | |||
#endif | |||
if (r < ARCHIVE_OK) | |||
throw Exception(string::f("Archiver could not open archive %s for writing: %s", archivePath.c_str(), archive_error_string(a))); | |||
throw Exception("Archiver could not open archive %s for writing: %s", archivePath.c_str(), archive_error_string(a)); | |||
DEFER({archive_write_close(a);}); | |||
// Open folder for reading | |||
@@ -323,7 +323,7 @@ void archiveFolder(const std::string& archivePath, const std::string& folderPath | |||
r = archive_read_disk_open(disk, folderPath.c_str()); | |||
#endif | |||
if (r < ARCHIVE_OK) | |||
throw Exception(string::f("Archiver could not open folder %s for reading: %s", folderPath.c_str(), archive_error_string(a))); | |||
throw Exception("Archiver could not open folder %s for reading: %s", folderPath.c_str(), archive_error_string(a)); | |||
DEFER({archive_read_close(a);}); | |||
// Iterate folder | |||
@@ -335,7 +335,7 @@ void archiveFolder(const std::string& archivePath, const std::string& folderPath | |||
if (r == ARCHIVE_EOF) | |||
break; | |||
if (r < ARCHIVE_OK) | |||
throw Exception(string::f("Archiver could not get next entry from archive: %s", archive_error_string(disk))); | |||
throw Exception("Archiver could not get next entry from archive: %s", archive_error_string(disk)); | |||
// Recurse dirs | |||
archive_read_disk_descend(disk); | |||
@@ -358,7 +358,7 @@ void archiveFolder(const std::string& archivePath, const std::string& folderPath | |||
// Write file to archive | |||
r = archive_write_header(a, entry); | |||
if (r < ARCHIVE_OK) | |||
throw Exception(string::f("Archiver could not write entry to archive: %s", archive_error_string(a))); | |||
throw Exception("Archiver could not write entry to archive: %s", archive_error_string(a)); | |||
// Manually copy data | |||
#if defined ARCH_WIN | |||
@@ -394,7 +394,7 @@ void unarchiveToFolder(const std::string& archivePath, const std::string& folder | |||
r = archive_read_open_filename(a, archivePath.c_str(), 1 << 14); | |||
#endif | |||
if (r < ARCHIVE_OK) | |||
throw Exception(string::f("Unarchiver could not open archive %s: %s", archivePath.c_str(), archive_error_string(a))); | |||
throw Exception("Unarchiver could not open archive %s: %s", archivePath.c_str(), archive_error_string(a)); | |||
DEFER({archive_read_close(a);}); | |||
// Open folder for writing | |||
@@ -412,12 +412,12 @@ void unarchiveToFolder(const std::string& archivePath, const std::string& folder | |||
if (r == ARCHIVE_EOF) | |||
break; | |||
if (r < ARCHIVE_OK) | |||
throw Exception(string::f("Unarchiver could not read entry from archive: %s", archive_error_string(a))); | |||
throw Exception("Unarchiver could not read entry from archive: %s", archive_error_string(a)); | |||
// Convert relative pathname to absolute based on folderPath | |||
std::string entryPath = archive_entry_pathname(entry); | |||
if (!fs::u8path(entryPath).is_relative()) | |||
throw Exception(string::f("Unarchiver does not support absolute tar paths: %s", entryPath.c_str())); | |||
throw Exception("Unarchiver does not support absolute tar paths: %s", entryPath.c_str()); | |||
entryPath = (fs::u8path(folderPath) / fs::u8path(entryPath)).generic_u8string(); | |||
#if defined ARCH_WIN | |||
archive_entry_copy_pathname_w(entry, string::U8toU16(entryPath).c_str()); | |||
@@ -428,7 +428,7 @@ void unarchiveToFolder(const std::string& archivePath, const std::string& folder | |||
// Write entry to disk | |||
r = archive_write_header(disk, entry); | |||
if (r < ARCHIVE_OK) | |||
throw Exception(string::f("Unarchiver could not write file to folder: %s", archive_error_string(disk))); | |||
throw Exception("Unarchiver could not write file to folder: %s", archive_error_string(disk)); | |||
// Copy data to file | |||
for (;;) { | |||
@@ -440,18 +440,18 @@ void unarchiveToFolder(const std::string& archivePath, const std::string& folder | |||
if (r == ARCHIVE_EOF) | |||
break; | |||
if (r < ARCHIVE_OK) | |||
throw Exception(string::f("Unarchiver could not read data from archive", archive_error_string(a))); | |||
throw Exception("Unarchiver could not read data from archive: %s", archive_error_string(a)); | |||
// Write data to file | |||
r = archive_write_data_block(disk, buf, size, offset); | |||
if (r < ARCHIVE_OK) | |||
throw Exception(string::f("Unarchiver could not write data to file", archive_error_string(disk))); | |||
throw Exception("Unarchiver could not write data to file: %s", archive_error_string(disk)); | |||
} | |||
// Close file | |||
r = archive_write_finish_entry(disk); | |||
if (r < ARCHIVE_OK) | |||
throw Exception(string::f("Unarchiver could not close file", archive_error_string(disk))); | |||
throw Exception("Unarchiver could not close file: %s", archive_error_string(disk)); | |||
} | |||
} | |||