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:
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;
}