reapack

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

commit 492e40e9b069b751deec9e6b0cba5ddb88a5d472
parent 9c774968966ef15b7bfa8bc33b666a4c254f7dd1
Author: cfillion <cfillion@users.noreply.github.com>
Date:   Thu,  3 Mar 2016 01:43:38 -0500

preparation work for downloading indexes independently of a transaction

Diffstat:
Asrc/filesystem.cpp | 138+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/filesystem.hpp | 36++++++++++++++++++++++++++++++++++++
Msrc/index.cpp | 33+++++++++++++++++++--------------
Msrc/index.hpp | 3+++
Msrc/task.cpp | 83++++++++-----------------------------------------------------------------------
Msrc/task.hpp | 4----
Msrc/transaction.cpp | 23++++++++++++-----------
7 files changed, 216 insertions(+), 104 deletions(-)

diff --git a/src/filesystem.cpp b/src/filesystem.cpp @@ -0,0 +1,138 @@ +/* ReaPack: Package manager for REAPER + * Copyright (C) 2015-2016 Christian Fillion + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "filesystem.hpp" + +#include "encoding.hpp" +#include "path.hpp" + +#include <cerrno> +#include <cstdio> +#include <fstream> +#include <sys/stat.h> + +#ifdef _WIN32 +#include <windows.h> +#endif + +using namespace std; + +FILE *FS::open(const Path &path) +{ +#ifdef _WIN32 + FILE *file = nullptr; + _wfopen_s(&file, make_autostring(path.join()).c_str(), L"rb"); + return file; +#else + return fopen(path.join().c_str(), "rb"); +#endif +} + +bool FS::write(const Path &path, const string &contents) +{ + ofstream file(make_autostring(path.join()), ios_base::binary); + + if(!file) + return false; + + file << contents; + file.close(); + + return true; +} + +bool FS::rename(const Path &from, const Path &to) +{ + const string &fullFrom = Path::prefixRoot(from).join(); + const string &fullTo = Path::prefixRoot(to).join(); + +#ifdef _WIN32 + return !_wrename(make_autostring(fullFrom).c_str(), + make_autostring(fullTo).c_str()); +#else + return !::rename(fullFrom.c_str(), fullTo.c_str()); +#endif +} + +bool FS::remove(const Path &path) +{ + const auto_string &fullPath = + make_autostring(Path::prefixRoot(path).join()); + +#ifdef _WIN32 + if(GetFileAttributes(fullPath.c_str()) == INVALID_FILE_ATTRIBUTES + && GetLastError() == ERROR_FILE_NOT_FOUND) + return true; + + if(GetFileAttributes(fullPath.c_str()) & FILE_ATTRIBUTE_DIRECTORY) + return RemoveDirectory(fullPath.c_str()) != 0; + else if(!_wremove(fullPath.c_str())) + return true; + + // So the file cannot be removed (probably because it's in use)? + // Then let's move it somewhere else. And delete it on next startup. + // Windows is so great! + + Path workaroundPath; + workaroundPath.prepend(Path::CACHE_DIRNAME); + workaroundPath.append("old_" + path.last() + ".tmp"); + + return rename(path, workaroundPath); +#else + return !::remove(fullPath.c_str()); +#endif +} + +bool FS::removeRecursive(const Path &file) +{ + if(!remove(file)) + return false; + + Path dir = file; + + // remove empty directories, but not top-level ones that were created by REAPER + while(dir.size() > 2) { + dir.removeLast(); + + if(!remove(dir)) + break; + } + + return true; +} + +bool FS::mtime(const Path &path, time_t *time) +{ + struct stat st; + +#ifdef _WIN32 + if(_wstat(make_autostring(path.join()).c_str() &st)) + return false; +#else + if(stat(path.join().c_str(), &st)) + return false; +#endif + + *time = st.st_mtime; + + return true; +} + +string FS::lastError() +{ + return strerror(errno); +} diff --git a/src/filesystem.hpp b/src/filesystem.hpp @@ -0,0 +1,36 @@ +/* ReaPack: Package manager for REAPER + * Copyright (C) 2015-2016 Christian Fillion + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef REAPACK_FILESYSTEM_HPP +#define REAPACK_FILESYSTEM_HPP + +#include <string> + +class Path; + +namespace FS { + FILE *open(const Path &); + bool write(const Path &, const std::string &); + bool rename(const Path &, const Path &); + bool remove(const Path &); + bool removeRecursive(const Path &); + bool mtime(const Path &, time_t *); + + std::string lastError(); +}; + +#endif diff --git a/src/index.cpp b/src/index.cpp @@ -17,28 +17,19 @@ #include "index.hpp" +#include "download.hpp" #include "encoding.hpp" #include "errors.hpp" +#include "filesystem.hpp" #include "path.hpp" +#include "remote.hpp" #include <boost/algorithm/string/predicate.hpp> -#include <cerrno> #include <WDL/tinyxml/tinyxml.h> using namespace std; -static FILE *OpenFile(const char *path) -{ -#ifdef _WIN32 - FILE *file = nullptr; - _wfopen_s(&file, make_autostring(path).c_str(), L"rb"); - return file; -#else - return fopen(path, "rb"); -#endif -} - Path RemoteIndex::pathFor(const string &name) { return Path::prefixCache(name + ".xml"); @@ -56,10 +47,10 @@ const RemoteIndex *RemoteIndex::load(const string &name) { TiXmlDocument doc; - FILE *file = OpenFile(pathFor(name).join().c_str()); + FILE *file = FS::open(pathFor(name)); if(!file) - throw reapack_error(strerror(errno)); + throw reapack_error(FS::lastError().c_str()); const bool success = doc.LoadFile(file); fclose(file); @@ -87,6 +78,20 @@ const RemoteIndex *RemoteIndex::load(const string &name) } } +Download *RemoteIndex::fetch(const Remote &remote, const bool stale) +{ + time_t mtime = 0, now = time(nullptr); + + if(FS::mtime(pathFor(remote.name()), &mtime)) { + const time_t threshold = stale ? 2 : (24 * 3600); + + if(mtime > now - threshold) + return nullptr; + } + + return new Download(remote.name() + ".xml", remote.url()); +} + RemoteIndex::RemoteIndex(const string &name) : m_name(name) { diff --git a/src/index.hpp b/src/index.hpp @@ -27,7 +27,9 @@ class Category; typedef std::vector<const Category *> CategoryList; +class Download; class Path; +class Remote; class TiXmlElement; struct Link { std::string name; std::string url; }; @@ -41,6 +43,7 @@ public: static Path pathFor(const std::string &name); static LinkType linkTypeFor(const char *rel); static const RemoteIndex *load(const std::string &name); + static Download *fetch(const Remote &, bool stale = false); RemoteIndex(const std::string &name); ~RemoteIndex(); diff --git a/src/task.cpp b/src/task.cpp @@ -17,17 +17,10 @@ #include "task.hpp" -#include "encoding.hpp" +#include "filesystem.hpp" #include "transaction.hpp" #include "version.hpp" -#include <cerrno> -#include <cstdio> - -#ifdef _WIN32 -#include <windows.h> -#endif - using namespace std; Task::Task(Transaction *transaction) @@ -58,66 +51,6 @@ void Task::rollback() doRollback(); } -bool Task::RenameFile(const Path &from, const Path &to) -{ - const string &fullFrom = Path::prefixRoot(from).join(); - const string &fullTo = Path::prefixRoot(to).join(); - -#ifdef _WIN32 - return !_wrename(make_autostring(fullFrom).c_str(), - make_autostring(fullTo).c_str()); -#else - return !rename(fullFrom.c_str(), fullTo.c_str()); -#endif -} - -bool Task::RemoveFile(const Path &path) -{ - const auto_string &fullPath = - make_autostring(Path::prefixRoot(path).join()); - -#ifdef _WIN32 - if(GetFileAttributes(fullPath.c_str()) == INVALID_FILE_ATTRIBUTES - && GetLastError() == ERROR_FILE_NOT_FOUND) - return true; - - if(GetFileAttributes(fullPath.c_str()) & FILE_ATTRIBUTE_DIRECTORY) - return RemoveDirectory(fullPath.c_str()) != 0; - else if(!_wremove(fullPath.c_str())) - return true; - - // So the file cannot be removed (probably because it's in use)? - // Then let's move it somewhere else. And delete it on next startup. - // Windows is so great! - - Path workaroundPath; - workaroundPath.prepend(Path::CACHE_DIRNAME); - workaroundPath.append("old_" + path.last() + ".tmp"); - - return RenameFile(path, workaroundPath); -#else - return !remove(fullPath.c_str()); -#endif -} - -bool Task::RemoveFileRecursive(const Path &file) -{ - if(!RemoveFile(file)) - return false; - - Path dir = file; - - // remove empty directories, but not top-level ones that were created by REAPER - while(dir.size() > 2) { - dir.removeLast(); - - if(!RemoveFile(dir)) - break; - } - - return true; -} - InstallTask::InstallTask(const Version *ver, const set<Path> &oldFiles, Transaction *t) : Task(t), m_version(ver), m_oldFiles(move(oldFiles)) @@ -168,15 +101,15 @@ void InstallTask::saveSource(Download *dl, const Source *src) bool InstallTask::doCommit() { for(const Path &path : m_oldFiles) - RemoveFile(path); + FS::remove(path); for(const PathGroup &paths : m_newFiles) { #ifdef _WIN32 - RemoveFile(paths.target); + FS::remove(paths.target); #endif - if(!RenameFile(paths.temp, paths.target)) { - transaction()->addError(strerror(errno), paths.target.join()); + if(!FS::rename(paths.temp, paths.target)) { + transaction()->addError(FS::lastError(), paths.target.join()); // it's a bit late to rollback here as some files might already have been // overwritten. at least we can delete the temporary files @@ -191,7 +124,7 @@ bool InstallTask::doCommit() void InstallTask::doRollback() { for(const PathGroup &paths : m_newFiles) - RemoveFileRecursive(paths.temp); + FS::removeRecursive(paths.temp); m_newFiles.clear(); } @@ -204,10 +137,10 @@ RemoveTask::RemoveTask(const vector<Path> &files, Transaction *t) bool RemoveTask::doCommit() { for(const Path &path : m_files) { - if(RemoveFileRecursive(path)) + if(FS::removeRecursive(path)) m_removedFiles.insert(path); else - transaction()->addError(strerror(errno), path.join()); + transaction()->addError(FS::lastError(), path.join()); } return true; diff --git a/src/task.hpp b/src/task.hpp @@ -45,10 +45,6 @@ public: void rollback(); protected: - static bool RenameFile(const Path &, const Path &); - static bool RemoveFile(const Path &); - static bool RemoveFileRecursive(const Path &); - Transaction *transaction() const { return m_transaction; } virtual void doStart() = 0; diff --git a/src/transaction.cpp b/src/transaction.cpp @@ -19,12 +19,12 @@ #include "encoding.hpp" #include "errors.hpp" +#include "filesystem.hpp" #include "index.hpp" #include "remote.hpp" #include "task.hpp" #include <boost/algorithm/string.hpp> -#include <fstream> #include <reaper_plugin_functions.h> @@ -96,9 +96,16 @@ void Transaction::fetchIndex(const Remote &remote, const IndexCallback &cb) if(m_remotes.count(name) > 1) return; - Download *dl = new Download(name, remote.url()); - m_downloadQueue.push(dl); + Download *dl = RemoteIndex::fetch(remote, true); + + if(!dl) { + // the index was last downloaded less than a few seconds ago + cb(); + finish(); + return; + } + m_downloadQueue.push(dl); dl->onFinish(bind(&Transaction::saveIndex, this, dl, name)); } @@ -263,17 +270,11 @@ bool Transaction::saveFile(Download *dl, const Path &path) RecursiveCreateDirectory(path.dirname().c_str(), 0); - const string strPath = path.join(); - ofstream file(make_autostring(strPath), ios_base::binary); - - if(!file) { - addError(strerror(errno), strPath); + if(!FS::write(path, dl->contents())) { + addError(FS::lastError(), path.join()); return false; } - file << dl->contents(); - file.close(); - return true; }