reapack

Package manager for REAPER
Log | Files | Refs | Submodules | README | LICENSE

commit 2156cf690ae00e165bd063811432055005b79912
parent 14ccc6444ec29e73469e98b7477e2a30198b9751
Author: cfillion <cfillion@users.noreply.github.com>
Date:   Sat, 12 Dec 2015 20:19:17 -0500

save downloaded files under a temporary name first

Diffstat:
Msrc/path.cpp | 15+++++++++++++++
Msrc/path.hpp | 2++
Msrc/transaction.cpp | 63+++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
Msrc/transaction.hpp | 13++++++++++---
Mtest/path.cpp | 17+++++++++++++++++
5 files changed, 101 insertions(+), 9 deletions(-)

diff --git a/src/path.cpp b/src/path.cpp @@ -20,6 +20,11 @@ void Path::append(const string &part) m_parts.push_back(part); } +void Path::clear() +{ + m_parts.clear(); +} + string Path::basename() const { if(m_parts.empty()) @@ -74,3 +79,13 @@ Path Path::operator+(const Path &o) const return path; } + +string &Path::operator[](const size_t index) +{ + auto it = m_parts.begin(); + + for(size_t i = 0; i < index; i++) + it++; + + return *it; +} diff --git a/src/path.hpp b/src/path.hpp @@ -8,6 +8,7 @@ class Path { public: void prepend(const std::string &part); void append(const std::string &part); + void clear(); bool empty() const { return m_parts.empty(); } size_t size() const { return m_parts.size(); } @@ -20,6 +21,7 @@ public: bool operator!=(const Path &) const; Path operator+(const std::string &) const; Path operator+(const Path &) const; + std::string &operator[](const size_t index); private: std::string join(const bool) const; diff --git a/src/transaction.cpp b/src/transaction.cpp @@ -2,6 +2,7 @@ #include "errors.hpp" +#include <cstdio> #include <fstream> #include <reaper_plugin_functions.h> @@ -12,6 +13,7 @@ Transaction::Transaction(Registry *reg, const Path &root) : m_registry(reg), m_root(root), m_isCancelled(false) { m_dbPath = m_root + "ReaPack"; + RecursiveCreateDirectory(m_dbPath.join().c_str(), 0); } @@ -116,6 +118,10 @@ void Transaction::run() void Transaction::cancel() { m_isCancelled = true; + + for(PackageTransaction *tr : m_transactions) + tr->cancel(); + m_queue.abort(); } @@ -147,6 +153,11 @@ void Transaction::finish() if(!m_queue.idle()) return; + if(!m_isCancelled) { + for(PackageTransaction *tr : m_transactions) + tr->apply(); + } + m_onFinish(); } @@ -155,6 +166,11 @@ void Transaction::addError(const string &message, const string &title) m_errors.push_back({message, title}); } +Path Transaction::prefixPath(const Path &input) const +{ + return m_root + input; +} + bool PackageTransaction::isInstalled(Version *ver, const Path &root) { // TODO @@ -163,7 +179,7 @@ bool PackageTransaction::isInstalled(Version *ver, const Path &root) } PackageTransaction::PackageTransaction(Transaction *transaction) - : m_transaction(transaction), m_remaining(0) + : m_transaction(transaction), m_isCancelled(false) { } @@ -190,10 +206,19 @@ void PackageTransaction::saveSource(Download *dl, Source *src) { m_remaining.erase(remove(m_remaining.begin(), m_remaining.end(), dl)); - const Path path = installPath(src); + if(m_isCancelled) + return; + + const Path targetPath = src->targetPath(); + Path tmpPath = targetPath; + tmpPath[tmpPath.size() - 1] += ".new"; + + m_files.push({tmpPath, targetPath}); + + const Path path = m_transaction->prefixPath(tmpPath); if(!m_transaction->saveFile(dl, path)) { - abort(); + cancel(); return; } } @@ -206,13 +231,39 @@ void PackageTransaction::finish() m_onFinish(); } -void PackageTransaction::abort() +void PackageTransaction::cancel() { + m_isCancelled = true; + for(Download *dl : m_remaining) dl->abort(); + + rollback(); +} + +void PackageTransaction::apply() +{ + while(!m_files.empty()) { + const PathPair paths = m_files.front(); + m_files.pop(); + + const string tempPath = m_transaction->prefixPath(paths.first).join(); + const string targetPath = m_transaction->prefixPath(paths.second).join(); + + if(rename(tempPath.c_str(), targetPath.c_str())) + m_transaction->addError(strerror(errno), targetPath); + } } -Path PackageTransaction::installPath(Source *src) const +void PackageTransaction::rollback() { - return m_transaction->m_root + src->targetPath(); + while(!m_files.empty()) { + const PathPair paths = m_files.front(); + m_files.pop(); + + const string tempPath = m_transaction->prefixPath(paths.first).join(); + + if(remove(tempPath.c_str())) + m_transaction->addError(strerror(errno), tempPath); + } } diff --git a/src/transaction.hpp b/src/transaction.hpp @@ -8,6 +8,7 @@ #include "remote.hpp" #include <boost/signals2.hpp> +#include <queue> class PackageTransaction; @@ -53,6 +54,7 @@ private: void saveDatabase(Download *); bool saveFile(Download *, const Path &); void addError(const std::string &msg, const std::string &title); + Path prefixPath(const Path &) const; Registry *m_registry; @@ -85,16 +87,21 @@ public: void onFinish(const Callback &callback) { m_onFinish.connect(callback); } void install(Version *ver); - void abort(); + void apply(); + void cancel(); private: - void saveSource(Download *, Source *); - Path installPath(Source *) const; + typedef std::pair<Path, Path> PathPair; void finish(); + void rollback(); + + void saveSource(Download *, Source *); Transaction *m_transaction; + bool m_isCancelled; std::vector<Download *> m_remaining; + std::queue<PathPair> m_files; Signal m_onFinish; }; diff --git a/test/path.cpp b/test/path.cpp @@ -78,3 +78,20 @@ TEST_CASE("empty components", M) { REQUIRE(a.size() == 0); } + +TEST_CASE("clear path", M) { + Path a; + a.append("test"); + + CHECK(a.size() == 1); + a.clear(); + REQUIRE(a.size() == 0); +} + +TEST_CASE("modify path", M) { + Path a; + a.append("hello"); + + a[0] = "world"; + REQUIRE(a.join() == "world"); +}