reapack

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

commit 43b1524218588d7ad4c1501e471e4b3e66d87165
parent f0e2cbb7ab6450742fbfa8b8e2cb7a735b1e321b
Author: cfillion <cfillion@users.noreply.github.com>
Date:   Mon, 11 Sep 2017 04:35:12 -0400

(28500861794ee0a4b1c533ac0bbcaf989394f7c6 done better)

Diffstat:
Msrc/archive.cpp | 38+++++++++++++++++++++-----------------
Msrc/errors.hpp | 15++-------------
Msrc/package.cpp | 12++++++++----
Asrc/string.cpp | 35+++++++++++++++++++++++++++++++++++
Asrc/string.hpp | 30++++++++++++++++++++++++++++++
Msrc/task.cpp | 3++-
Msrc/transaction.cpp | 2+-
Msrc/version.cpp | 6+++---
Atest/string.cpp | 13+++++++++++++
9 files changed, 115 insertions(+), 39 deletions(-)

diff --git a/src/archive.cpp b/src/archive.cpp @@ -79,7 +79,8 @@ void Archive::import(const string &path) stringstream toc; if(const int err = state.m_reader->extractFile(ARCHIVE_TOC, toc)) - throw reapack_error("Cannot locate the table of contents (%d)", err); + throw reapack_error(String::format( + "Cannot locate the table of contents (%d)", err)); // starting import, do not abort process (eg. by throwing) at this point if(!(state.m_tx = g_reapack->setupTransaction())) @@ -101,7 +102,8 @@ void Archive::import(const string &path) state.importPackage(data); break; default: - throw reapack_error("Unknown token '%s' (skipping)", line.substr(0, 4).c_str()); + throw reapack_error(String::format("Unknown token '%s' (skipping)", + line.substr(0, 4).c_str())); } } catch(const reapack_error &e) { @@ -119,8 +121,8 @@ void ImportArchive::importRemote(const string &data) Remote remote = Remote::fromString(data); if(const int err = m_reader->extractFile(Index::pathFor(remote.name()))) { - throw reapack_error("Failed to extract index of %s (%d)", - remote.name().c_str(), err); + throw reapack_error(String::format("Failed to extract index of %s (%d)", + remote.name().c_str(), err)); } const Remote &original = m_remotes->get(remote.name()); @@ -152,10 +154,10 @@ void ImportArchive::importPackage(const string &data) const Version *ver = pkg ? pkg->findVersion(versionName) : nullptr; if(!ver) { - throw reapack_error( + throw reapack_error(String::format( "%s/%s/%s v%s cannot be found or is incompatible with your operating system.", m_lastIndex->name().c_str(), categoryName.c_str(), - packageName.c_str(), versionName.c_str()); + packageName.c_str(), versionName.c_str())); } m_tx->install(ver, pinned, m_reader); @@ -186,8 +188,10 @@ int ArchiveReader::extractFile(const Path &path) if(FS::open(stream, path)) return extractFile(path, stream); - else - throw reapack_error("%s: %s", path.join().c_str(), FS::lastError()); + else { + throw reapack_error(String::format("%s: %s", + path.join().c_str(), FS::lastError())); + } } int ArchiveReader::extractFile(const Path &path, ostream &stream) noexcept @@ -234,9 +238,8 @@ bool FileExtractor::run() stream.close(); if(error) { - char msg[64]; - snprintf(msg, sizeof(msg), "Failed to extract file (%d)", error); - setError({msg, m_path.target().join()}); + setError({String::format("Failed to extract file (%d)", error), + m_path.target().join()}); return false; } @@ -268,8 +271,10 @@ int ArchiveWriter::addFile(const Path &path) if(FS::open(stream, path)) return addFile(path, stream); - else - throw reapack_error("%s: %s", path.join().c_str(), FS::lastError()); + else { + throw reapack_error(String::format("%s: %s", + path.join().c_str(), FS::lastError())); + } } int ArchiveWriter::addFile(const Path &path, istream &stream) noexcept @@ -307,7 +312,8 @@ bool FileCompressor::run() { ifstream stream; if(!FS::open(stream, m_path)) { - setError({string("Could not open file for export (") + FS::lastError() + ')', + setError({ + String::format("Could not open file for export (%s)", FS::lastError()), m_path.join()}); return false; } @@ -316,9 +322,7 @@ bool FileCompressor::run() stream.close(); if(error) { - char msg[64]; - snprintf(msg, sizeof(msg), "Failed to compress file (%d)", error); - setError({msg, m_path.join()}); + setError({String::format("Failed to compress file (%d)", error), m_path.join()}); return false; } diff --git a/src/errors.hpp b/src/errors.hpp @@ -19,23 +19,12 @@ #define REAPACK_ERRORS_HPP #include <stdexcept> -#include <string> + +#include "string.hpp" class reapack_error : public std::runtime_error { public: using runtime_error::runtime_error; - - template<typename... Args> reapack_error(const char *fmt, Args&&... args) - : runtime_error(format(fmt, std::forward<Args>(args)...)) {} - -private: - template<typename... Args> std::string format(const char *fmt, Args&&... args) - { - const int size = snprintf(nullptr, 0, fmt, args...); - std::string buf(size, 0); - snprintf(&buf[0], size + 1, fmt, args...); - return buf; - } }; struct ErrorInfo { diff --git a/src/package.cpp b/src/package.cpp @@ -91,8 +91,10 @@ Package::Package(const Type type, const string &name, const Category *cat) { if(m_name.empty()) throw reapack_error("empty package name"); - else if(m_name.find_first_of("/\\") != string::npos) - throw reapack_error("invalid package name '%s'", m_name.c_str()); + else if(m_name.find_first_of("/\\") != string::npos) { + throw reapack_error( + String::format("invalid package name '%s'", m_name.c_str())); + } } Package::~Package() @@ -112,8 +114,10 @@ bool Package::addVersion(const Version *ver) throw reapack_error("version belongs to another package"); else if(ver->sources().empty()) return false; - else if(m_versions.count(ver)) - throw reapack_error("duplicate version '%s'", ver->fullName().c_str()); + else if(m_versions.count(ver)) { + throw reapack_error(String::format("duplicate version '%s'", + ver->fullName().c_str())); + } m_versions.insert(ver); diff --git a/src/string.cpp b/src/string.cpp @@ -0,0 +1,35 @@ +/* ReaPack: Package manager for REAPER + * Copyright (C) 2015-2017 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 "string.hpp" + +std::string String::format(const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + const int size = vsnprintf(nullptr, 0, fmt, args); + va_end(args); + + std::string buf(size, 0); + + va_start(args, fmt); + vsnprintf(&buf[0], size + 1, fmt, args); + va_end(args); + + return buf; +} diff --git a/src/string.hpp b/src/string.hpp @@ -0,0 +1,30 @@ +/* ReaPack: Package manager for REAPER + * Copyright (C) 2015-2017 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_STRING_HPP +#define REAPACK_STRING_HPP + +#include <string> + +namespace String { +#ifdef __GNUC__ + __attribute__((format(printf, 1, 2))) +#endif + std::string format(const char *fmt, ...); +} + +#endif diff --git a/src/task.cpp b/src/task.cpp @@ -107,7 +107,8 @@ void InstallTask::commit() for(const TempPath &paths : m_newFiles) { if(!FS::rename(paths)) { - tx()->receipt()->addError({string("Cannot rename to target: ") + FS::lastError(), + tx()->receipt()->addError({ + String::format("Cannot rename to target: %s", FS::lastError()), paths.target().join()}); // it's a bit late to rollback here as some files might already have been diff --git a/src/transaction.cpp b/src/transaction.cpp @@ -169,7 +169,7 @@ IndexPtr Transaction::loadIndex(const Remote &remote) } catch(const reapack_error &e) { m_receipt.addError({ - string("Couldn't load repository: ") + e.what(), remote.name()}); + String::format("Could not load repository: %s", e.what()), remote.name()}); return nullptr; } } diff --git a/src/version.cpp b/src/version.cpp @@ -102,7 +102,7 @@ void VersionName::parse(const string &str) if(first >= 'a' || first >= 'z') { if(segments.empty()) // got leading letters - throw reapack_error("invalid version name '%s'", str.c_str()); + throw reapack_error(String::format("invalid version name '%s'", str.c_str())); segments.push_back(match); letters++; @@ -112,13 +112,13 @@ void VersionName::parse(const string &str) segments.push_back(boost::lexical_cast<Numeric>(match)); } catch(const boost::bad_lexical_cast &) { - throw reapack_error("version segment overflow in '%s'", str.c_str()); + throw reapack_error(String::format("version segment overflow in '%s'", str.c_str())); } } } if(segments.empty()) // version doesn't have any numbers - throw reapack_error("invalid version name '%s'", str.c_str()); + throw reapack_error(String::format("invalid version name '%s'", str.c_str())); m_string = str; swap(m_segments, segments); diff --git a/test/string.cpp b/test/string.cpp @@ -0,0 +1,13 @@ +#include "helper.hpp" + +#include <string.hpp> + +static constexpr const char *M = "[string]"; + +using namespace std; + +TEST_CASE("string format", M) { + const string &formatted = String::format("%d%% Hello %s!", 100, "World"); + CHECK(formatted.size() == 17); + REQUIRE(formatted == "100% Hello World!"); +}