Browse Source

Add system::unarchiveToFolder() and archiveFolder(). Begin using `std::experimental::filesystem`.

tags/v2.0.0
Andrew Belt 4 years ago
parent
commit
0ded01c228
4 changed files with 172 additions and 10 deletions
  1. +9
    -4
      include/system.hpp
  2. +6
    -3
      src/patch.cpp
  3. +6
    -3
      src/plugin.cpp
  4. +151
    -0
      src/system.cpp

+ 9
- 4
include/system.hpp View File

@@ -2,11 +2,16 @@
#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 {
@@ -64,11 +69,11 @@ 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(); std::string getOperatingSystemInfo();
/** Unzips a ZIP file to a folder.
The folder must exist.
Returns 0 if successful.
/** Extracts an archive into a folder.
Currently supports the "ustar zstd" format (.tar.zstd)
*/ */
int unzipToFolder(const std::string& zipPath, const std::string& dir);
void unarchiveToFolder(const filesystem::path& archivePath, const filesystem::path& folderPath);
void archiveFolder(const filesystem::path& archivePath, const filesystem::path& folderPath);




} // namespace system } // namespace system


+ 6
- 3
src/patch.cpp View File

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


// TODO Archive autosave folder
system::archiveFolder(path, asset::autosavePath);
} }


void PatchManager::saveDialog() { void PatchManager::saveDialog() {
@@ -141,8 +141,11 @@ void PatchManager::saveAutosave() {
bool PatchManager::load(std::string path) { bool PatchManager::load(std::string path) {
INFO("Loading patch %s", path.c_str()); INFO("Loading patch %s", path.c_str());


// TODO Extract archive to autosave
filesystem::remove_all(asset::autosavePath);
filesystem::create_directories(asset::autosavePath);
system::unarchiveToFolder(path, asset::autosavePath);


loadAutosave();
return true; return true;
} }


@@ -174,7 +177,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 autosave
// TODO Load template
return; return;
} }
DEFER({ DEFER({


+ 6
- 3
src/plugin.cpp View File

@@ -197,8 +197,11 @@ static void extractPackages(std::string path) {
continue; continue;
INFO("Extracting package %s", packagePath.c_str()); INFO("Extracting package %s", packagePath.c_str());
// Extract package // Extract package
if (system::unzipToFolder(packagePath, path)) {
WARN("Package %s failed to extract", packagePath.c_str());
try {
system::unarchiveToFolder(packagePath, path);
}
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()); message += string::f("Could not extract package %s\n", packagePath.c_str());
continue; continue;
} }
@@ -240,7 +243,7 @@ void init() {
std::string fundamentalDir = asset::pluginsPath + "/Fundamental"; std::string fundamentalDir = asset::pluginsPath + "/Fundamental";
if (!settings::devMode && !getPlugin("Fundamental") && system::isFile(fundamentalSrc)) { if (!settings::devMode && !getPlugin("Fundamental") && system::isFile(fundamentalSrc)) {
INFO("Extracting bundled Fundamental package"); INFO("Extracting bundled Fundamental package");
system::unzipToFolder(fundamentalSrc.c_str(), asset::pluginsPath.c_str());
system::unarchiveToFolder(fundamentalSrc.c_str(), asset::pluginsPath.c_str());
loadPlugin(fundamentalDir); loadPlugin(fundamentalDir);
} }




+ 151
- 0
src/system.cpp View File

@@ -28,6 +28,8 @@


#define ZIP_STATIC #define ZIP_STATIC
#include <zip.h> #include <zip.h>
#include <archive.h>
#include <archive_entry.h>


#include <system.hpp> #include <system.hpp>
#include <string.hpp> #include <string.hpp>
@@ -428,5 +430,154 @@ int unzipToFolder(const std::string& zipPath, const std::string& dir) {
} }




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

// Open archive for reading
struct archive* a = archive_read_new();
DEFER({archive_read_free(a);});
archive_read_support_filter_zstd(a);
// archive_read_support_filter_all(a);
archive_read_support_format_tar(a);
// archive_read_support_format_all(a);
// TODO Fix unicode filenames on Windows?
r = archive_read_open_filename(a, archivePath.c_str(), 1 << 14);
if (r != ARCHIVE_OK)
throw Exception(string::f("unzipToFolder() could not open archive %s: %s", archivePath.c_str(), archive_error_string(a)));
DEFER({archive_read_close(a);});

// Open folder for writing
struct archive* disk = archive_write_disk_new();
DEFER({archive_write_free(disk);});
int flags = ARCHIVE_EXTRACT_TIME;
archive_write_disk_set_options(disk, flags);
// archive_write_disk_set_standard_lookup(disk);
DEFER({archive_write_close(disk);});

// Iterate archive
for (;;) {
// Get next entry
struct archive_entry* entry;
r = archive_read_next_header(a, &entry);
if (r == ARCHIVE_EOF)
break;
if (r < ARCHIVE_OK)
throw Exception(string::f("unzipToFolder() could not read entry from archive: %s", archive_error_string(a)));

// Set pathname relative to folderPath
filesystem::path entryPath = archive_entry_pathname(entry);
if (!entryPath.is_relative())
throw Exception(string::f("unzipToFolder() does not support absolute paths: %s", entryPath.c_str()));
entryPath = filesystem::absolute(entryPath, folderPath);
archive_entry_set_pathname(entry, entryPath.c_str());

// Write entry to disk
r = archive_write_header(disk, entry);
if (r < ARCHIVE_OK)
throw Exception(string::f("unzipToFolder() could not write file to folder: %s", archive_error_string(disk)));

// Copy data to file
for (;;) {
const void* buf;
size_t size;
int64_t offset;
// Read data from archive
r = archive_read_data_block(a, &buf, &size, &offset);
if (r == ARCHIVE_EOF)
break;
if (r < ARCHIVE_OK)
throw Exception(string::f("unzipToFolder() could not read data from archive", 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("unzipToFolder() could not write data to file", archive_error_string(disk)));
}

// Close file
r = archive_write_finish_entry(disk);
if (r < ARCHIVE_OK)
throw Exception(string::f("unzipToFolder() could not close file", archive_error_string(disk)));
}
}


/** Behaves like `std::filesystem::relative()`.
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;
std::string baseStr = base;
if (pStr.size() < baseStr.size())
throw Exception("getRelativePath() error: path is shorter than base");
if (!std::equal(baseStr.begin(), baseStr.end(), pStr.begin()))
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());
}


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

// Open archive for writing
struct archive* a = archive_write_new();
DEFER({archive_write_free(a);});
archive_write_set_format_ustar(a);
archive_write_add_filter_zstd(a);
r = archive_write_open_filename(a, archivePath.c_str());
if (r != ARCHIVE_OK)
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);});

// Open folder for reading
struct archive* disk = archive_read_disk_new();
DEFER({archive_read_free(disk);});
r = archive_read_disk_open(disk, folderPath.c_str());
if (r != ARCHIVE_OK)
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);});

// Iterate folder
for (;;) {
struct archive_entry* entry = archive_entry_new();
DEFER({archive_entry_free(entry);});

r = archive_read_next_header2(disk, entry);
if (r == ARCHIVE_EOF)
break;
if (r != ARCHIVE_OK)
throw Exception(string::f("archiveFolder() could not get next entry from archive: %s", archive_error_string(disk)));

// Recurse dirs
archive_read_disk_descend(disk);

// Convert absolute path to relative path
filesystem::path entryPath = archive_entry_pathname(entry);
entryPath = getRelativePath(entryPath, folderPath);
archive_entry_set_pathname(entry, entryPath.c_str());

// Write file to archive
r = archive_write_header(a, entry);
if (r != ARCHIVE_OK)
throw Exception("archiveFolder() could not write entry to archive");

// Manually copy data
FILE* f = std::fopen(archive_entry_sourcepath(entry), "rb");
DEFER({std::fclose(f);});
static char buf[1 << 14];
ssize_t len;
while ((len = std::fread(buf, 1, sizeof(buf), f)) > 0) {
archive_write_data(a, buf, len);
}
}
}


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

Loading…
Cancel
Save