diff options
author | David Robillard <d@drobilla.net> | 2018-01-21 00:41:34 +0100 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2018-01-21 00:56:50 +0100 |
commit | a99b72e4adbc4c28fadc08d29299d99405f72db9 (patch) | |
tree | fb21f2cea8f5db1dc187cdbdd01f43e73bbddfff | |
parent | 329f498d901f9be9c0c820749850f5277a17df5d (diff) | |
download | ingen-a99b72e4adbc4c28fadc08d29299d99405f72db9.tar.gz ingen-a99b72e4adbc4c28fadc08d29299d99405f72db9.tar.bz2 ingen-a99b72e4adbc4c28fadc08d29299d99405f72db9.zip |
Add FilePath class and remove use of glib path utilities
-rw-r--r-- | ingen/Configuration.hpp | 15 | ||||
-rw-r--r-- | ingen/FilePath.hpp | 126 | ||||
-rw-r--r-- | ingen/Node.hpp | 3 | ||||
-rw-r--r-- | ingen/Parser.hpp | 11 | ||||
-rw-r--r-- | ingen/URI.hpp | 6 | ||||
-rw-r--r-- | ingen/filesystem.hpp | 84 | ||||
-rw-r--r-- | ingen/runtime_paths.hpp | 13 | ||||
-rw-r--r-- | src/Configuration.cpp | 41 | ||||
-rw-r--r-- | src/FilePath.cpp | 242 | ||||
-rw-r--r-- | src/Parser.cpp | 56 | ||||
-rw-r--r-- | src/Serialiser.cpp | 77 | ||||
-rw-r--r-- | src/URI.cpp | 8 | ||||
-rw-r--r-- | src/World.cpp | 18 | ||||
-rw-r--r-- | src/gui/LoadGraphWindow.cpp | 8 | ||||
-rw-r--r-- | src/gui/ThreadedLoader.cpp | 21 | ||||
-rw-r--r-- | src/gui/ThreadedLoader.hpp | 9 | ||||
-rw-r--r-- | src/runtime_paths.cpp | 92 | ||||
-rw-r--r-- | src/server/LV2Block.cpp | 25 | ||||
-rw-r--r-- | src/server/LV2Block.hpp | 4 | ||||
-rw-r--r-- | src/server/events/CreateBlock.cpp | 3 | ||||
-rw-r--r-- | src/server/events/Delta.cpp | 14 | ||||
-rw-r--r-- | src/wscript | 1 | ||||
-rw-r--r-- | tests/ingen_test.cpp | 23 | ||||
-rw-r--r-- | tests/test_utils.hpp | 40 | ||||
-rw-r--r-- | tests/tst_FilePath.cpp | 103 | ||||
-rw-r--r-- | wscript | 8 |
26 files changed, 837 insertions, 214 deletions
diff --git a/ingen/Configuration.hpp b/ingen/Configuration.hpp index ae5e4909..67104422 100644 --- a/ingen/Configuration.hpp +++ b/ingen/Configuration.hpp @@ -30,6 +30,7 @@ namespace Ingen { +class FilePath; class Forge; class URIMap; @@ -81,7 +82,7 @@ public: void parse(int argc, char** argv) throw (OptionError); /** Load a specific file. */ - bool load(const std::string& path); + bool load(const FilePath& path); /** Save configuration to a file. * @@ -98,10 +99,10 @@ public: * * @return The absolute path of the saved configuration file. */ - std::string save(URIMap& uri_map, - const std::string& app, - const std::string& filename, - unsigned scopes) throw (FileError); + FilePath save(URIMap& uri_map, + const std::string& app, + const FilePath& filename, + unsigned scopes) throw (FileError); /** Load files from the standard configuration directories for the app. * @@ -109,8 +110,8 @@ public: * will be loaded before the user's, e.g. ~/.config/appname/filename, * so the user options will override the system options. */ - std::list<std::string> load_default(const std::string& app, - const std::string& filename); + std::list<FilePath> load_default(const std::string& app, + const FilePath& filename); const Atom& option(const std::string& long_name) const; bool set(const std::string& long_name, const Atom& value); diff --git a/ingen/FilePath.hpp b/ingen/FilePath.hpp new file mode 100644 index 00000000..7ad341e0 --- /dev/null +++ b/ingen/FilePath.hpp @@ -0,0 +1,126 @@ +/* + This file is part of Ingen. + Copyright 2018 David Robillard <http://drobilla.net/> + + Ingen is free software: you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free + Software Foundation, either version 3 of the License, or any later version. + + Ingen 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 Affero General Public License for details. + + You should have received a copy of the GNU Affero General Public License + along with Ingen. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef INGEN_FILE_PATH_HPP +#define INGEN_FILE_PATH_HPP + +#include <iosfwd> +#include <type_traits> +#include <utility> + +#include <boost/utility/string_view.hpp> + +#if defined(_WIN32) && !defined(__CYGWIN__) +#define USE_WINDOWS_FILE_PATHS 1 +#endif + +namespace Ingen { + +/** A path to a file. + * + * This is a minimal subset of the std::filesystem::path interface in C++17. + * Support for Windows paths is only partial and there is no support for + * character encoding conversion at all. + */ +class FilePath +{ +public: +#ifdef USE_WINDOWS_FILE_PATHS + typedef wchar_t value_type; + static constexpr value_type preferred_separator = L'\\'; +#else + typedef char value_type; + static constexpr value_type preferred_separator = '/'; +#endif + + typedef std::basic_string<value_type> string_type; + + FilePath() noexcept = default; + FilePath(const FilePath&) = default; + + FilePath(FilePath&& path) noexcept + : _str(std::move(path._str)) + { + path.clear(); + } + + FilePath(string_type&& str) : _str(std::move(str)) {} + FilePath(const string_type& str) : _str(str) {} + FilePath(const value_type* str) : _str(str) {} + FilePath(const boost::basic_string_view<value_type>& sv) : _str(sv) {} + + ~FilePath() = default; + + FilePath& operator=(const FilePath& path) = default; + FilePath& operator=(FilePath&& path) noexcept; + FilePath& operator=(string_type&& str); + + FilePath& operator/=(const FilePath& path); + + FilePath& operator+=(const FilePath& path); + FilePath& operator+=(const string_type& str); + FilePath& operator+=(const value_type* str); + FilePath& operator+=(value_type chr); + FilePath& operator+=(boost::basic_string_view<value_type> sv); + + void clear() noexcept { _str.clear(); } + + const string_type& native() const noexcept { return _str; } + const string_type& string() const noexcept { return _str; } + const value_type* c_str() const noexcept { return _str.c_str(); } + + operator string_type() const { return _str; } + + FilePath root_name() const; + FilePath root_directory() const; + FilePath root_path() const; + FilePath relative_path() const; + FilePath parent_path() const; + FilePath filename() const; + FilePath stem() const; + FilePath extension() const; + + bool empty() const noexcept { return _str.empty(); } + + bool is_absolute() const; + bool is_relative() const { return !is_absolute(); } + +private: + std::size_t find_first_sep() const; + std::size_t find_last_sep() const; + + string_type _str; +}; + +bool operator==(const FilePath& lhs, const FilePath& rhs) noexcept; +bool operator!=(const FilePath& lhs, const FilePath& rhs) noexcept; +bool operator<(const FilePath& lhs, const FilePath& rhs) noexcept; +bool operator<=(const FilePath& lhs, const FilePath& rhs) noexcept; +bool operator>(const FilePath& lhs, const FilePath& rhs) noexcept; +bool operator>=(const FilePath& lhs, const FilePath& rhs) noexcept; + +FilePath operator/(const FilePath& lhs, const FilePath& rhs); + +template <typename Char, typename Traits> +std::basic_ostream<Char, Traits>& +operator<<(std::basic_ostream<Char, Traits>& os, const FilePath& path) +{ + return os << path.string(); +} + +} // namespace Ingen + +#endif // INGEN_FILE_PATH_HPP diff --git a/ingen/Node.hpp b/ingen/Node.hpp index e023e9e2..ca78aa3d 100644 --- a/ingen/Node.hpp +++ b/ingen/Node.hpp @@ -36,6 +36,7 @@ class Symbol; namespace Ingen { class Arc; +class FilePath; class Store; class URIs; @@ -74,7 +75,7 @@ public: // Plugin blocks only virtual LilvInstance* instance() { return nullptr; } - virtual bool save_state(const std::string& dir) const { return false; } + virtual bool save_state(const FilePath& dir) const { return false; } // All objects virtual GraphType graph_type() const = 0; diff --git a/ingen/Parser.hpp b/ingen/Parser.hpp index ac9d8320..96e21c51 100644 --- a/ingen/Parser.hpp +++ b/ingen/Parser.hpp @@ -23,6 +23,7 @@ #include <boost/optional/optional.hpp> +#include "ingen/FilePath.hpp" #include "ingen/Properties.hpp" #include "ingen/URI.hpp" #include "ingen/ingen.h" @@ -50,7 +51,7 @@ public: /** Record of a resource listed in a bundle manifest. */ struct ResourceRecord { - inline ResourceRecord(URI u, std::string f) + inline ResourceRecord(URI u, FilePath f) : uri(std::move(u)), filename(std::move(f)) {} @@ -58,8 +59,8 @@ public: return uri < r.uri; } - URI uri; ///< URI of resource (e.g. a Graph) - std::string filename; ///< Path of describing file (seeAlso) + URI uri; ///< URI of resource (e.g. a Graph) + FilePath filename; ///< Path of describing file (seeAlso) }; /** Find all resources of a given type listed in a manifest file. */ @@ -79,7 +80,7 @@ public: virtual bool parse_file( World* world, Interface* target, - const std::string& path, + const FilePath& path, boost::optional<Raul::Path> parent = boost::optional<Raul::Path>(), boost::optional<Raul::Symbol> symbol = boost::optional<Raul::Symbol>(), boost::optional<Properties> data = boost::optional<Properties>()); @@ -88,7 +89,7 @@ public: World* world, Interface* target, const std::string& str, - const std::string& base_uri, + const URI& base_uri, boost::optional<Raul::Path> parent = boost::optional<Raul::Path>(), boost::optional<Raul::Symbol> symbol = boost::optional<Raul::Symbol>(), boost::optional<Properties> data = boost::optional<Properties>()); diff --git a/ingen/URI.hpp b/ingen/URI.hpp index 6b2adff4..30aeb7cc 100644 --- a/ingen/URI.hpp +++ b/ingen/URI.hpp @@ -22,6 +22,7 @@ #include <boost/utility/string_view.hpp> +#include "ingen/FilePath.hpp" #include "ingen/ingen.h" #include "serd/serd.h" #include "sord/sordmm.hpp" @@ -38,6 +39,7 @@ public: explicit URI(const char* str); URI(const std::string& str, const URI& base); explicit URI(const Sord::Node& node); + explicit URI(const FilePath& path); URI(const URI& uri); URI& operator=(const URI& uri); @@ -53,6 +55,10 @@ public: size_t length() const { return _node.n_bytes; } const char* c_str() const { return (const char*)_node.buf; } + FilePath file_path() const { + return scheme() == "file" ? FilePath(path()) : FilePath(); + } + operator std::string() const { return string(); } const char* begin() const { return (const char*)_node.buf; } diff --git a/ingen/filesystem.hpp b/ingen/filesystem.hpp new file mode 100644 index 00000000..19fa74b2 --- /dev/null +++ b/ingen/filesystem.hpp @@ -0,0 +1,84 @@ +/* + This file is part of Ingen. + Copyright 2007-2017 David Robillard <http://drobilla.net/> + + Ingen is free software: you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free + Software Foundation, either version 3 of the License, or any later version. + + Ingen 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 Affero General Public License for details. + + You should have received a copy of the GNU Affero General Public License + along with Ingen. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef INGEN_FILESYSTEM_HPP +#define INGEN_FILESYSTEM_HPP + +#define _BSD_SOURCE 1 +#define _DEFAULT_SOURCE 1 + +#include <cerrno> +#include <climits> +#include <cstdlib> +#include <memory> +#include <vector> + +#include <sys/stat.h> +#include <sys/types.h> + +#ifdef _WIN32 +# include <windows.h> +# include <io.h> +# define F_OK 0 +# define mkdir(path, flags) _mkdir(path) +#endif + +#include "ingen/FilePath.hpp" + +/* A minimal subset of the std::filesystem API from C++17. */ + +namespace Ingen { +namespace filesystem { + +inline bool exists(const FilePath& path) +{ + return !access(path.c_str(), F_OK); +} + +inline bool is_directory(const FilePath& path) +{ + struct stat info; + stat(path.c_str(), &info); + return S_ISDIR(info.st_mode); +} + +inline bool create_directories(const FilePath& path) +{ + std::vector<FilePath> paths; + for (FilePath p = path; p != path.root_directory(); p = p.parent_path()) { + paths.emplace_back(p); + } + + for (auto p = paths.rbegin(); p != paths.rend(); ++p) { + if (mkdir(p->c_str(), 0755) && errno != EEXIST) { + return false; + } + } + + return true; +} + +inline FilePath current_path() +{ + std::unique_ptr<char> cpath(realpath(".", NULL)); + const FilePath path(cpath.get()); + return path; +} + +} // namespace filesystem +} // namespace Ingen + +#endif // INGEN_FILESYSTEM_HPP diff --git a/ingen/runtime_paths.hpp b/ingen/runtime_paths.hpp index 747bc9b7..1a8bc2c2 100644 --- a/ingen/runtime_paths.hpp +++ b/ingen/runtime_paths.hpp @@ -18,17 +18,24 @@ #define INGEN_RUNTIME_PATHS_HPP #include <string> +#include <vector> #include "ingen/ingen.h" +#include "ingen/FilePath.hpp" namespace Ingen { +extern const char search_path_separator; + INGEN_API void set_bundle_path(const char* path); INGEN_API void set_bundle_path_from_code(void* function); -INGEN_API std::string bundle_file_path(const std::string& name); -INGEN_API std::string data_file_path(const std::string& name); -INGEN_API std::string module_path(const std::string& name, std::string dir=""); +INGEN_API FilePath bundle_file_path(const std::string& name); +INGEN_API FilePath data_file_path(const std::string& name); +INGEN_API FilePath ingen_module_path(const std::string& name, FilePath dir={}); + +INGEN_API FilePath user_config_dir(); +INGEN_API std::vector<FilePath> system_config_dirs(); } // namespace Ingen diff --git a/src/Configuration.cpp b/src/Configuration.cpp index bdbf07bc..ec9cbf0f 100644 --- a/src/Configuration.cpp +++ b/src/Configuration.cpp @@ -20,14 +20,13 @@ #include <iostream> #include <thread> -#include <glibmm/fileutils.h> -#include <glibmm/miscutils.h> - #include "ingen/Configuration.hpp" #include "ingen/Forge.hpp" #include "ingen/Log.hpp" #include "ingen/URIMap.hpp" +#include "ingen/filesystem.hpp" #include "ingen/ingen.h" +#include "ingen/runtime_paths.hpp" #include "sord/sordmm.hpp" #include "sratom/sratom.h" @@ -226,9 +225,9 @@ Configuration::parse(int argc, char** argv) throw (Configuration::OptionError) } bool -Configuration::load(const std::string& path) +Configuration::load(const FilePath& path) { - if (!Glib::file_test(path, Glib::FILE_TEST_EXISTS)) { + if (!filesystem::exists(path)) { return false; } @@ -261,23 +260,22 @@ Configuration::load(const std::string& path) return true; } -std::string +FilePath Configuration::save(URIMap& uri_map, const std::string& app, - const std::string& filename, + const FilePath& filename, unsigned scopes) throw (FileError) { // Save to file if it is absolute, otherwise save to user config dir - std::string path = filename; - if (!Glib::path_is_absolute(path)) { - path = Glib::build_filename( - Glib::build_filename(Glib::get_user_config_dir(), app), filename); + FilePath path = filename; + if (!path.is_absolute()) { + path = FilePath(user_config_dir()) / app / filename; } // Create parent directories if necessary - const std::string dir = Glib::path_get_dirname(path); - if (g_mkdir_with_parents(dir.c_str(), 0755) < 0) { + const FilePath dir = path.parent_path(); + if (!filesystem::create_directories(dir)) { throw FileError((fmt("Error creating directory %1% (%2%)") % dir % strerror(errno)).str()); } @@ -343,23 +341,20 @@ Configuration::save(URIMap& uri_map, return path; } -std::list<std::string> -Configuration::load_default(const std::string& app, - const std::string& filename) +std::list<FilePath> +Configuration::load_default(const std::string& app, const FilePath& filename) { - std::list<std::string> loaded; + std::list<FilePath> loaded; - const std::vector<std::string> dirs = Glib::get_system_config_dirs(); - for (auto d : dirs) { - const std::string path = Glib::build_filename( - Glib::build_filename(d, app), filename); + const std::vector<FilePath> dirs = system_config_dirs(); + for (const auto& d : dirs) { + const FilePath path = d / app / filename; if (load(path)) { loaded.push_back(path); } } - const std::string path = Glib::build_filename( - Glib::build_filename(Glib::get_user_config_dir(), app), filename); + const FilePath path = user_config_dir() / app / filename; if (load(path)) { loaded.push_back(path); } diff --git a/src/FilePath.cpp b/src/FilePath.cpp new file mode 100644 index 00000000..557fabc9 --- /dev/null +++ b/src/FilePath.cpp @@ -0,0 +1,242 @@ +/* + This file is part of Ingen. + Copyright 2018 David Robillard <http://drobilla.net/> + + Ingen is free software: you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free + Software Foundation, either version 3 of the License, or any later version. + + Ingen 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 Affero General Public License for details. + + You should have received a copy of the GNU Affero General Public License + along with Ingen. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "ingen/FilePath.hpp" + +namespace Ingen { + +template <typename Char> +static bool +is_sep(const Char chr) +{ +#ifdef USE_WINDOWS_FILE_PATHS + return chr == L'/' || chr == preferred_separator; +#else + return chr == '/'; +#endif +} + +FilePath& +FilePath::operator=(FilePath&& path) noexcept +{ + _str = std::move(path._str); + path.clear(); + return *this; +} + +FilePath& +FilePath::operator=(string_type&& str) +{ + return *this = FilePath(std::move(str)); +} + +FilePath& +FilePath::operator/=(const FilePath& path) +{ + const FilePath::string_type str = path.string(); + if (!_str.empty() && !is_sep(_str.back()) && !str.empty() && + !is_sep(str.front())) { + _str += preferred_separator; + } + + _str += str; + return *this; +} + +FilePath& +FilePath::operator+=(const FilePath& path) +{ + return operator+=(path.native()); +} + +FilePath& +FilePath::operator+=(const string_type& str) +{ + _str += str; + return *this; +} + +FilePath& +FilePath::operator+=(const value_type* str) +{ + _str += str; + return *this; +} + +FilePath& +FilePath::operator+=(value_type chr) +{ + _str += chr; + return *this; +} + +FilePath& +FilePath::operator+=(boost::basic_string_view<value_type> sv) +{ + _str.append(sv.data(), sv.size()); + return *this; +} + +FilePath +FilePath::root_name() const +{ +#ifdef USE_WINDOWS_FILE_PATHS + if (_str.length() >= 2 && _str[0] >= 'A' && _str[0] <= 'Z' && + _str[1] == ':') { + return FilePath(_str.substr(0, 2)); + } +#endif + + return FilePath(); +} + +FilePath +FilePath::root_directory() const +{ +#ifdef USE_WINDOWS_FILE_PATHS + const auto name = root_name().string(); + return name.empty() ? Path() : Path(name + preferred_separator); +#endif + + return _str[0] == '/' ? FilePath("/") : FilePath(); +} + +FilePath +FilePath::root_path() const +{ +#ifdef USE_WINDOWS_FILE_PATHS + const auto name = root_name(); + return name.empty() ? FilePath() : name / root_directory(); +#endif + return root_directory(); +} + +FilePath +FilePath::relative_path() const +{ + const auto root = root_path(); + return root.empty() ? FilePath() + : FilePath(_str.substr(root.string().length())); +} + +FilePath +FilePath::parent_path() const +{ + if (empty() || *this == root_path()) { + return *this; + } + + const auto first_sep = find_first_sep(); + const auto last_sep = find_last_sep(); + return ((last_sep == std::string::npos || last_sep == first_sep) + ? root_path() + : FilePath(_str.substr(0, last_sep))); +} + +FilePath +FilePath::filename() const +{ + return ((empty() || *this == root_path()) + ? FilePath() + : FilePath(_str.substr(find_last_sep() + 1))); +} + +FilePath +FilePath::stem() const +{ + const auto name = filename(); + const auto dot = name.string().find('.'); + return ((dot == std::string::npos) ? name + : FilePath(name.string().substr(0, dot))); +} + +FilePath +FilePath::extension() const +{ + const auto name = filename().string(); + const auto dot = name.find('.'); + return ((dot == std::string::npos) ? FilePath() + : FilePath(name.substr(dot, dot))); +} + +bool +FilePath::is_absolute() const +{ +#ifdef USE_WINDOWS_FILE_PATHS + return !root_name().empty(); +#else + return !root_directory().empty(); +#endif +} + +std::size_t +FilePath::find_first_sep() const +{ + const auto i = std::find_if(_str.begin(), _str.end(), is_sep<value_type>); + return i == _str.end() ? std::string::npos : (i - _str.begin()); +} + +std::size_t +FilePath::find_last_sep() const +{ + const auto i = std::find_if(_str.rbegin(), _str.rend(), is_sep<value_type>); + return (i == _str.rend() ? std::string::npos + : (_str.length() - 1 - (i - _str.rbegin()))); +} + +bool +operator==(const FilePath& lhs, const FilePath& rhs) noexcept +{ + return lhs.string() == rhs.string(); +} + +bool +operator!=(const FilePath& lhs, const FilePath& rhs) noexcept +{ + return !(lhs == rhs); +} + +bool +operator<(const FilePath& lhs, const FilePath& rhs) noexcept +{ + return lhs.string().compare(rhs.string()) < 0; +} + +bool +operator<=(const FilePath& lhs, const FilePath& rhs) noexcept +{ + return !(rhs < lhs); +} + +bool +operator>(const FilePath& lhs, const FilePath& rhs) noexcept +{ + return rhs < lhs; +} + +bool +operator>=(const FilePath& lhs, const FilePath& rhs) noexcept +{ + return !(lhs < rhs); +} + +FilePath +operator/(const FilePath& lhs, const FilePath& rhs) +{ + return FilePath(lhs) /= rhs; +} + +} // namespace Ingen diff --git a/src/Parser.cpp b/src/Parser.cpp index e0ec0d35..27f7d753 100644 --- a/src/Parser.cpp +++ b/src/Parser.cpp @@ -19,10 +19,6 @@ #include <string> #include <utility> -#include <glibmm/convert.h> -#include <glibmm/fileutils.h> -#include <glibmm/miscutils.h> - #include "ingen/Atom.hpp" #include "ingen/AtomForgeSink.hpp" #include "ingen/Forge.hpp" @@ -32,6 +28,7 @@ #include "ingen/URIMap.hpp" #include "ingen/URIs.hpp" #include "ingen/World.hpp" +#include "ingen/filesystem.hpp" #include "ingen/paths.hpp" #include "lv2/lv2plug.in/ns/ext/atom/atom.h" #include "serd/serd.h" @@ -105,14 +102,6 @@ relative_uri(const std::string& base, const std::string& uri, bool leading_slash return ret; } -static std::string -get_basename(const std::string& uri) -{ - std::string ret = Glib::path_get_basename(uri); - ret = ret.substr(0, ret.find_last_of('.')); - return ret; -} - static bool skip_property(Ingen::URIs& uris, const Sord::Node& predicate) { @@ -418,9 +407,11 @@ parse_graph(Ingen::World* world, // For each block in this graph for (Sord::Iter n = model.find(subject, ingen_block, nil); !n.end(); ++n) { - Sord::Node node = n.get_object(); + Sord::Node node = n.get_object(); + URI node_uri = URI(node.to_string()); + assert(!node_uri.path().empty() && node_uri.path() != "/"); const Raul::Path block_path = graph_path.child( - Raul::Symbol(get_basename(node.to_string()))); + Raul::Symbol(FilePath(node_uri.path()).stem().string())); // Parse and create block parse_block(world, target, model, base_uri, node, block_path, @@ -630,30 +621,23 @@ parse(Ingen::World* world, bool Parser::parse_file(Ingen::World* world, Ingen::Interface* target, - const std::string& path, + const FilePath& path, boost::optional<Raul::Path> parent, boost::optional<Raul::Symbol> symbol, boost::optional<Properties> data) { // Get absolute file path - std::string file_path = path; - if (!Glib::path_is_absolute(file_path)) { - file_path = Glib::build_filename(Glib::get_current_dir(), file_path); + FilePath file_path = path; + if (!file_path.is_absolute()) { + file_path = filesystem::current_path() / file_path; } // Find file to use as manifest - const bool is_bundle = Glib::file_test(file_path, Glib::FILE_TEST_IS_DIR); - const std::string manifest_path = (is_bundle - ? Glib::build_filename(file_path, "manifest.ttl") - : file_path); - - URI manifest_uri; - try { - manifest_uri = URI(Glib::filename_to_uri(manifest_path)); - } catch (const Glib::ConvertError& e) { - world->log().error(fmt("URI conversion error (%1%)\n") % e.what()); - return false; - } + const bool is_bundle = filesystem::is_directory(file_path); + const FilePath manifest_path = + (is_bundle ? file_path / "manifest.ttl" : file_path); + + URI manifest_uri(manifest_path); // Find graphs in manifest const std::set<ResourceRecord> resources = find_resources( @@ -669,7 +653,7 @@ Parser::parse_file(Ingen::World* world, In this case, choose the one with the file URI. */ URI uri; for (const ResourceRecord& r : resources) { - if (r.uri == Glib::filename_to_uri(manifest_path)) { + if (r.uri == URI(manifest_path)) { uri = r.uri; file_path = r.filename; break; @@ -688,10 +672,10 @@ Parser::parse_file(Ingen::World* world, } // Initialise parsing environment - const std::string file_uri = Glib::filename_to_uri(file_path); - const uint8_t* uri_c_str = (const uint8_t*)uri.c_str(); - SerdNode base_node = serd_node_from_string(SERD_URI, uri_c_str); - SerdEnv* env = serd_env_new(&base_node); + const URI file_uri = URI(file_path); + const uint8_t* uri_c_str = (const uint8_t*)uri.c_str(); + SerdNode base_node = serd_node_from_string(SERD_URI, uri_c_str); + SerdEnv* env = serd_env_new(&base_node); // Load graph into model Sord::Model model(*world->rdf_world(), uri.string(), SORD_SPO|SORD_PSO, false); @@ -726,7 +710,7 @@ boost::optional<URI> Parser::parse_string(Ingen::World* world, Ingen::Interface* target, const std::string& str, - const std::string& base_uri, + const URI& base_uri, boost::optional<Raul::Path> parent, boost::optional<Raul::Symbol> symbol, boost::optional<Properties> data) diff --git a/src/Serialiser.cpp b/src/Serialiser.cpp index 2f976d53..86913aa4 100644 --- a/src/Serialiser.cpp +++ b/src/Serialiser.cpp @@ -22,14 +22,8 @@ #include <string> #include <utility> -#include <glib.h> -#include <glib/gstdio.h> -#include <glibmm/convert.h> -#include <glibmm/fileutils.h> -#include <glibmm/miscutils.h> -#include <glibmm/module.h> - #include "ingen/Arc.hpp" +#include "ingen/FilePath.hpp" #include "ingen/Forge.hpp" #include "ingen/Interface.hpp" #include "ingen/Log.hpp" @@ -41,6 +35,8 @@ #include "ingen/URIMap.hpp" #include "ingen/URIs.hpp" #include "ingen/World.hpp" +#include "ingen/filesystem.hpp" +#include "ingen/runtime_paths.hpp" #include "lv2/lv2plug.in/ns/ext/state/state.h" #include "lv2/lv2plug.in/ns/extensions/ui/ui.h" #include "raul/Path.hpp" @@ -65,7 +61,8 @@ struct Serialiser::Impl { enum class Mode { TO_FILE, TO_STRING }; - void start_to_file(const Raul::Path& root, const std::string& filename); + void start_to_file(const Raul::Path& root, + const FilePath& filename); std::set<const Resource*> serialise_graph(SPtr<const Node> graph, const Sord::Node& graph_id); @@ -85,10 +82,10 @@ struct Serialiser::Impl { Sord::Node path_rdf_node(const Raul::Path& path); - void write_manifest(const std::string& bundle_path, - SPtr<const Node> graph); + void write_manifest(const FilePath& bundle_path, + SPtr<const Node> graph); - void write_plugins(const std::string& bundle_path, + void write_plugins(const FilePath& bundle_path, const std::set<const Resource*> plugins); void serialise_arc(const Sord::Node& parent, @@ -116,11 +113,11 @@ Serialiser::~Serialiser() } void -Serialiser::Impl::write_manifest(const std::string& bundle_path, - SPtr<const Node> graph) +Serialiser::Impl::write_manifest(const FilePath& bundle_path, + SPtr<const Node> graph) { - const string manifest_path(Glib::build_filename(bundle_path, "manifest.ttl")); - const string binary_path(Glib::Module::build_path("", "ingen_lv2")); + const FilePath manifest_path(bundle_path / "manifest.ttl"); + const FilePath binary_path(ingen_module_path("lv2")); start_to_file(Raul::Path("/"), manifest_path); @@ -147,10 +144,10 @@ Serialiser::Impl::write_manifest(const std::string& bundle_path, } void -Serialiser::Impl::write_plugins(const std::string& bundle_path, +Serialiser::Impl::write_plugins(const FilePath& bundle_path, const std::set<const Resource*> plugins) { - const string plugins_path(Glib::build_filename(bundle_path, "plugins.ttl")); + const FilePath plugins_path(bundle_path / "plugins.ttl"); start_to_file(Raul::Path("/"), plugins_path); @@ -187,27 +184,22 @@ Serialiser::write_bundle(SPtr<const Node> graph, const URI& uri) void Serialiser::Impl::write_bundle(SPtr<const Node> graph, const URI& uri) { - std::string path = Glib::filename_from_uri(uri.string()); - if (Glib::file_test(path, Glib::FILE_TEST_EXISTS) - && !Glib::file_test(path, Glib::FILE_TEST_IS_DIR)) { - path = Glib::path_get_dirname(path); + FilePath path(uri.path()); + if (filesystem::exists(path) && !filesystem::is_directory(path)) { + path = path.parent_path(); } _world.log().info(fmt("Writing bundle %1%\n") % path); + filesystem::create_directories(path); - if (path.back() != '/') { - path.append("/"); - } - - g_mkdir_with_parents(path.c_str(), 0744); - - const string main_file = Glib::build_filename(path, "main.ttl"); + const FilePath main_file = path / "main.ttl"; const Raul::Path old_root_path = _root_path; start_to_file(graph->path(), main_file); std::set<const Resource*> plugins = serialise_graph( - graph, Sord::URI(_model->world(), main_file, _base_uri.string())); + graph, + Sord::URI(_model->world(), main_file.string(), _base_uri.string())); finish(); write_manifest(path, graph); @@ -221,20 +213,13 @@ Serialiser::Impl::write_bundle(SPtr<const Node> graph, const URI& uri) * This must be called before any serializing methods. */ void -Serialiser::Impl::start_to_file(const Raul::Path& root, const string& filename) +Serialiser::Impl::start_to_file(const Raul::Path& root, + const FilePath& filename) { - // Set Base URI - assert(filename.find(":") == string::npos || filename.substr(0, 5) == "file:"); - if (filename.find(":") == string::npos) { - _base_uri = URI("file://" + filename); - } else { - _base_uri = URI(filename); - } - - // Find graph basename to use as symbol / fallback name - _basename = Glib::path_get_basename(filename); + _base_uri = URI(filename); + _basename = filename.stem().string(); if (_basename == "main.ttl") { - _basename = Glib::path_get_basename(Glib::path_get_dirname(filename)); + _basename = filename.filename().parent_path().stem().string(); } _model = new Sord::Model(*_world.rdf_world(), _base_uri.string()); @@ -449,14 +434,14 @@ Serialiser::Impl::serialise_block(SPtr<const Node> block, serialise_properties(block_id, props); if (_base_uri.scheme() == "file") { - const std::string base = Glib::filename_from_uri(_base_uri.string()); - const std::string graph_dir = Glib::path_get_dirname(base); - const std::string state_dir = Glib::build_filename(graph_dir, block->symbol()); - const std::string state_file = Glib::build_filename(state_dir, "state.ttl"); + const FilePath base_path = _base_uri.file_path(); + const FilePath graph_dir = base_path.parent_path(); + const FilePath state_dir = graph_dir / block->symbol(); + const FilePath state_file = state_dir / "state.ttl"; if (block->save_state(state_dir)) { _model->add_statement(block_id, Sord::URI(_model->world(), uris.state_state), - Sord::URI(_model->world(), Glib::filename_to_uri(state_file))); + Sord::URI(_model->world(), URI(state_file))); } } diff --git a/src/URI.cpp b/src/URI.cpp index cc7dde46..a9c12223 100644 --- a/src/URI.cpp +++ b/src/URI.cpp @@ -16,6 +16,7 @@ #include <cassert> +#include "ingen/FilePath.hpp" #include "ingen/URI.hpp" namespace Ingen { @@ -47,6 +48,13 @@ URI::URI(const Sord::Node& node) assert(node.type() == Sord::Node::URI); } +URI::URI(const FilePath& path) + : _node(serd_node_new_file_uri((const uint8_t*)path.c_str(), + NULL, + &_uri, + true)) +{} + URI::URI(const URI& uri) : _node(serd_node_new_uri(&uri._uri, NULL, &_uri)) {} diff --git a/src/World.cpp b/src/World.cpp index 7834c3df..25e13873 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -17,8 +17,6 @@ #include <map> #include <string> -#include <glibmm/fileutils.h> -#include <glibmm/miscutils.h> #include <glibmm/module.h> #include "ingen/Configuration.hpp" @@ -34,6 +32,7 @@ #include "ingen/URIMap.hpp" #include "ingen/URIs.hpp" #include "ingen/World.hpp" +#include "ingen/filesystem.hpp" #include "ingen/ingen.h" #include "ingen/runtime_paths.hpp" #include "lilv/lilv.h" @@ -63,14 +62,13 @@ ingen_load_module(Log& log, const string& name) Glib::Module* module = nullptr; // Search INGEN_MODULE_PATH first - bool module_path_found; - string module_path = Glib::getenv("INGEN_MODULE_PATH", module_path_found); - if (module_path_found) { + const char* const module_path = getenv("INGEN_MODULE_PATH"); + if (module_path) { string dir; std::istringstream iss(module_path); - while (getline(iss, dir, G_SEARCHPATH_SEPARATOR)) { - string filename = Ingen::module_path(name, dir); - if (Glib::file_test(filename, Glib::FILE_TEST_EXISTS)) { + while (getline(iss, dir, search_path_separator)) { + FilePath filename = Ingen::ingen_module_path(name, FilePath(dir)); + if (filesystem::exists(filename)) { module = new Glib::Module(filename); if (*module) { return module; @@ -82,11 +80,11 @@ ingen_load_module(Log& log, const string& name) } // Try default directory if not found - module = new Glib::Module(Ingen::module_path(name)); + module = new Glib::Module(Ingen::ingen_module_path(name)); if (*module) { return module; - } else if (!module_path_found) { + } else if (!module_path) { log.error(fmt("Unable to find %1% (%2%)\n") % name % Glib::Module::get_last_error()); return nullptr; diff --git a/src/gui/LoadGraphWindow.cpp b/src/gui/LoadGraphWindow.cpp index 10ddf436..b02ca510 100644 --- a/src/gui/LoadGraphWindow.cpp +++ b/src/gui/LoadGraphWindow.cpp @@ -88,9 +88,9 @@ LoadGraphWindow::LoadGraphWindow(BaseObjectType* cobject, property_select_multiple() = true; // Add global examples directory to "shortcut folders" (bookmarks) - const std::string examples_dir = Ingen::data_file_path("graphs"); + const FilePath examples_dir = Ingen::data_file_path("graphs"); if (Glib::file_test(examples_dir, Glib::FILE_TEST_IS_DIR)) { - add_shortcut_folder(examples_dir); + add_shortcut_folder(examples_dir.string()); } } @@ -175,7 +175,7 @@ LoadGraphWindow::ok_clicked() } _app->loader()->load_graph( - true, get_filename(), parent, symbol, _initial_data); + true, FilePath(get_filename()), parent, symbol, _initial_data); } else { std::list<Glib::ustring> uri_list = get_filenames(); @@ -194,7 +194,7 @@ LoadGraphWindow::ok_clicked() symbol = avoid_symbol_clash(symbol); _app->loader()->load_graph( - false, u, _graph->path(), symbol, _initial_data); + false, FilePath(URI(u).path()), _graph->path(), symbol, _initial_data); } } diff --git a/src/gui/ThreadedLoader.cpp b/src/gui/ThreadedLoader.cpp index 891502f7..7a80fa6e 100644 --- a/src/gui/ThreadedLoader.cpp +++ b/src/gui/ThreadedLoader.cpp @@ -46,7 +46,9 @@ ThreadedLoader::~ThreadedLoader() { _exit_flag = true; _sem.post(); - _thread.join(); + if (_thread.joinable()) { + _thread.join(); + } } SPtr<Parser> @@ -59,23 +61,22 @@ void ThreadedLoader::run() { while (_sem.wait() && !_exit_flag) { - _mutex.lock(); + std::lock_guard<std::mutex> lock(_mutex); while (!_events.empty()) { _events.front()(); _events.pop_front(); } - _mutex.unlock(); } } void ThreadedLoader::load_graph(bool merge, - const Glib::ustring& document_uri, + const FilePath& file_path, optional<Raul::Path> engine_parent, optional<Raul::Symbol> engine_symbol, optional<Properties> engine_data) { - _mutex.lock(); + std::lock_guard<std::mutex> lock(_mutex); Glib::ustring engine_base = ""; if (engine_parent) { @@ -88,17 +89,16 @@ ThreadedLoader::load_graph(bool merge, _events.push_back(sigc::hide_return( sigc::bind(sigc::mem_fun(this, &ThreadedLoader::load_graph_event), - document_uri, + file_path, engine_parent, engine_symbol, engine_data))); - _mutex.unlock(); _sem.post(); } void -ThreadedLoader::load_graph_event(const Glib::ustring& document_uri, +ThreadedLoader::load_graph_event(const FilePath& file_path, optional<Raul::Path> engine_parent, optional<Raul::Symbol> engine_symbol, optional<Properties> engine_data) @@ -107,7 +107,7 @@ ThreadedLoader::load_graph_event(const Glib::ustring& document_uri, _app.world()->parser()->parse_file(_app.world(), _app.world()->interface().get(), - document_uri, + file_path, engine_parent, engine_symbol, engine_data); @@ -116,14 +116,13 @@ ThreadedLoader::load_graph_event(const Glib::ustring& document_uri, void ThreadedLoader::save_graph(SPtr<const Client::GraphModel> model, const URI& uri) { - _mutex.lock(); + std::lock_guard<std::mutex> lock(_mutex); _events.push_back(sigc::hide_return( sigc::bind(sigc::mem_fun(this, &ThreadedLoader::save_graph_event), model, uri))); - _mutex.unlock(); _sem.post(); } diff --git a/src/gui/ThreadedLoader.hpp b/src/gui/ThreadedLoader.hpp index 64968230..79ef6466 100644 --- a/src/gui/ThreadedLoader.hpp +++ b/src/gui/ThreadedLoader.hpp @@ -21,11 +21,12 @@ #include <cassert> #include <list> +#include <mutex> #include <string> #include <boost/optional.hpp> -#include <glibmm/thread.h> +#include "ingen/FilePath.hpp" #include "ingen/Interface.hpp" #include "ingen/Parser.hpp" #include "ingen/Serialiser.hpp" @@ -57,7 +58,7 @@ public: ~ThreadedLoader(); void load_graph(bool merge, - const Glib::ustring& document_uri, + const FilePath& file_path, boost::optional<Raul::Path> engine_parent, boost::optional<Raul::Symbol> engine_symbol, boost::optional<Properties> engine_data); @@ -67,7 +68,7 @@ public: SPtr<Parser> parser(); private: - void load_graph_event(const Glib::ustring& document_uri, + void load_graph_event(const FilePath& file_path, boost::optional<Raul::Path> engine_parent, boost::optional<Raul::Symbol> engine_symbol, boost::optional<Properties> engine_data); @@ -83,7 +84,7 @@ private: App& _app; Raul::Semaphore _sem; SPtr<Interface> _engine; - Glib::Mutex _mutex; + std::mutex _mutex; std::list<Closure> _events; bool _exit_flag; std::thread _thread; diff --git a/src/runtime_paths.cpp b/src/runtime_paths.cpp index bc3bf302..8dbe5c0c 100644 --- a/src/runtime_paths.cpp +++ b/src/runtime_paths.cpp @@ -16,20 +16,34 @@ #include <climits> #include <cstdlib> +#include <cstdlib> +#include <sstream> #include <string> #include <dlfcn.h> -#include <glibmm/module.h> -#include <glibmm/miscutils.h> - #include "ingen/runtime_paths.hpp" +#include "ingen/FilePath.hpp" #include "ingen_config.h" namespace Ingen { -static std::string bundle_path; +static FilePath bundle_path; + +#if defined(__APPLE__) +const char search_path_separator = ':'; +static const char* const library_prefix = "lib"; +static const char* const library_suffix = ".dylib"; +#elif defined(_WIN32) && !defined(__CYGWIN__) +const char search_path_separator = ';'; +static const char* const library_prefix = ""; +static const char* const library_suffix = ".dll"; +#else +const char search_path_separator = ':'; +static const char* const library_prefix = "lib"; +static const char* const library_suffix = ".so"; +#endif /** Must be called once at startup, and passed a pointer to a function * that lives in the 'top level' of the bundle (e.g. the executable). @@ -48,59 +62,85 @@ set_bundle_path_from_code(void* function) const char* bin_loc = dli.dli_fname; #endif - std::string bundle = bin_loc; - bundle = bundle.substr(0, bundle.find_last_of(G_DIR_SEPARATOR)); - bundle_path = bundle; + bundle_path = FilePath(bin_loc).parent_path(); } void set_bundle_path(const char* path) { - bundle_path = path; + bundle_path = FilePath(path); } /** Return the absolute path of a file in an Ingen LV2 bundle */ -std::string +FilePath bundle_file_path(const std::string& name) { - return Glib::build_filename(bundle_path, name); + return bundle_path / name; } /** Return the absolute path of a 'resource' file. */ -std::string +FilePath data_file_path(const std::string& name) { #ifdef BUNDLE - return Glib::build_filename(bundle_path, Glib::build_path(INGEN_DATA_DIR, name)); + return bundle_path / INGEN_DATA_DIR / name; #else - return Glib::build_filename(INGEN_DATA_DIR, name); + return FilePath(INGEN_DATA_DIR) / name; #endif } /** Return the absolute path of a module (dynamically loaded shared library). */ -std::string -module_path(const std::string& name, std::string dir) +FilePath +ingen_module_path(const std::string& name, FilePath dir) { - std::string ret; - if (dir == "") { + FilePath ret; + if (dir.empty()) { #ifdef BUNDLE - dir = Glib::build_path(bundle_path, INGEN_MODULE_DIR); + dir = FilePath(bundle_path) / INGEN_MODULE_DIR; #else - dir = INGEN_MODULE_DIR; + dir = FilePath(INGEN_MODULE_DIR); #endif } - ret = Glib::Module::build_path(dir, std::string("ingen_") + name); + return dir / + (std::string(library_prefix) + "ingen_" + name + library_suffix); +} -#ifdef __APPLE__ - // MacPorts glib doesnt seem to do portable path building correctly... - if (ret.substr(ret.length() - 3) == ".so") - ret = ret.substr(0, ret.length() - 2).append("dylib"); -#endif - return ret; +FilePath +user_config_dir() +{ + const char* const xdg_config_home = getenv("XDG_CONFIG_HOME"); + const char* const home = getenv("HOME"); + + if (xdg_config_home) { + return FilePath(xdg_config_home); + } else if (home) { + return FilePath(home) / ".config"; + } + + return FilePath(); +} + +std::vector<FilePath> +system_config_dirs() +{ + const char* const xdg_config_dirs = getenv("XDG_CONFIG_DIRS"); + + std::vector<FilePath> paths; + if (xdg_config_dirs) { + std::istringstream ss(xdg_config_dirs); + std::string entry; + while (std::getline(ss, entry, search_path_separator)) { + paths.emplace_back(entry); + } + } else { + paths.emplace_back("/etc/xdg"); + } + + return paths; } } // namespace Ingen diff --git a/src/server/LV2Block.cpp b/src/server/LV2Block.cpp index 53bed410..f4792f39 100644 --- a/src/server/LV2Block.cpp +++ b/src/server/LV2Block.cpp @@ -18,9 +18,6 @@ #include <cmath> #include <cstdint> -#include <glibmm/miscutils.h> -#include <glibmm/convert.h> - #include "lv2/lv2plug.in/ns/ext/morph/morph.h" #include "lv2/lv2plug.in/ns/ext/presets/presets.h" #include "lv2/lv2plug.in/ns/ext/options/options.h" @@ -30,7 +27,9 @@ #include "raul/Maid.hpp" #include "raul/Array.hpp" +#include "ingen/FilePath.hpp" #include "ingen/Log.hpp" +#include "ingen/URI.hpp" #include "ingen/URIMap.hpp" #include "ingen/URIs.hpp" #include "ingen/World.hpp" @@ -460,7 +459,7 @@ LV2Block::instantiate(BufferFactory& bufs, const LilvState* state) } bool -LV2Block::save_state(const std::string& dir) const +LV2Block::save_state(const FilePath& dir) const { World* world = _lv2_plugin->world(); LilvWorld* lworld = world->lilv_world(); @@ -623,11 +622,11 @@ LV2Block::load_preset(const URI& uri) } LilvState* -LV2Block::load_state(World* world, const std::string& path) +LV2Block::load_state(World* world, const FilePath& path) { - LilvWorld* lworld = world->lilv_world(); - const std::string uri = Glib::filename_to_uri(path); - LilvNode* subject = lilv_new_uri(lworld, uri.c_str()); + LilvWorld* lworld = world->lilv_world(); + const URI uri = URI(path); + LilvNode* subject = lilv_new_uri(lworld, uri.c_str()); LilvState* state = lilv_state_new_from_file( lworld, @@ -685,9 +684,9 @@ LV2Block::save_preset(const URI& uri, LV2_URID_Map* lmap = &world->uri_map().urid_map_feature()->urid_map; LV2_URID_Unmap* lunmap = &world->uri_map().urid_unmap_feature()->urid_unmap; - const std::string path = Glib::filename_from_uri(uri.string()); - const std::string dirname = Glib::path_get_dirname(path); - const std::string basename = Glib::path_get_basename(path); + const FilePath path = FilePath(uri.path()); + const FilePath dirname = path.parent_path(); + const FilePath basename = path.stem(); LilvState* state = lilv_state_new_from_instance( _lv2_plugin->lilv_plugin(), instance(0), lmap, @@ -715,8 +714,8 @@ LV2Block::save_preset(const URI& uri, preset.set_property(_uris.lv2_appliesTo, world->forge().make_urid(_lv2_plugin->uri())); - LilvNode* lbundle = lilv_new_uri( - lworld, Glib::filename_to_uri(dirname + "/").c_str()); + const std::string bundle_uri = URI(dirname).string() + '/'; + LilvNode* lbundle = lilv_new_uri(lworld, bundle_uri.c_str()); lilv_world_load_bundle(lworld, lbundle); lilv_node_free(lbundle); diff --git a/src/server/LV2Block.hpp b/src/server/LV2Block.hpp index eec852a2..f3a59550 100644 --- a/src/server/LV2Block.hpp +++ b/src/server/LV2Block.hpp @@ -51,7 +51,7 @@ public: bool instantiate(BufferFactory& bufs, const LilvState* state); LilvInstance* instance() { return instance(0); } - bool save_state(const std::string& dir) const; + bool save_state(const FilePath& dir) const; BlockImpl* duplicate(Engine& engine, const Raul::Symbol& symbol, @@ -80,7 +80,7 @@ public: BufferRef buf, SampleCount offset); - static LilvState* load_state(World* world, const std::string& path); + static LilvState* load_state(World* world, const FilePath& path); protected: struct Instance : public Raul::Noncopyable { diff --git a/src/server/events/CreateBlock.cpp b/src/server/events/CreateBlock.cpp index fc0c0f5e..d678bea3 100644 --- a/src/server/events/CreateBlock.cpp +++ b/src/server/events/CreateBlock.cpp @@ -119,7 +119,8 @@ CreateBlock::pre_process(PreProcessContext& ctx) LilvState* state = nullptr; auto s = _properties.find(uris.state_state); if (s != _properties.end() && s->second.type() == uris.forge.Path) { - state = LV2Block::load_state(_engine.world(), s->second.ptr<char>()); + state = LV2Block::load_state( + _engine.world(), FilePath(s->second.ptr<char>())); } // Instantiate plugin diff --git a/src/server/events/Delta.cpp b/src/server/events/Delta.cpp index 56cc22aa..b23ae884 100644 --- a/src/server/events/Delta.cpp +++ b/src/server/events/Delta.cpp @@ -17,8 +17,6 @@ #include <vector> #include <thread> -#include <glibmm/convert.h> - #include "ingen/Log.hpp" #include "ingen/Store.hpp" #include "ingen/URIs.hpp" @@ -366,15 +364,17 @@ Delta::pre_process(PreProcessContext& ctx) _status = Status::BAD_VALUE_TYPE; } } else if (key == uris.pset_preset) { - std::string uri_str; + URI uri; if (uris.forge.is_uri(value)) { - uri_str = uris.forge.str(value, false); + const std::string uri_str = uris.forge.str(value, false); + if (URI::is_valid(uri_str)) { + uri = URI(uri_str); + } } else if (value.type() == uris.forge.Path) { - uri_str = Glib::filename_to_uri(value.ptr<char>()); + uri = URI(FilePath(value.ptr<char>())); } - if (URI::is_valid(uri_str)) { - const URI uri(uri_str); + if (!uri.empty()) { op = SpecialType::PRESET; if ((_state = block->load_preset(uri))) { lilv_state_emit_port_values( diff --git a/src/wscript b/src/wscript index 5429ba26..82055ac9 100644 --- a/src/wscript +++ b/src/wscript @@ -8,6 +8,7 @@ def build(bld): 'ClashAvoider.cpp', 'ColorContext.cpp', 'Configuration.cpp', + 'FilePath.cpp', 'Forge.cpp', 'LV2Features.cpp', 'Log.cpp', diff --git a/tests/ingen_test.cpp b/tests/ingen_test.cpp index ab7cbc7d..beac1a7f 100644 --- a/tests/ingen_test.cpp +++ b/tests/ingen_test.cpp @@ -22,11 +22,6 @@ #include <boost/optional.hpp> -#include <glibmm/convert.h> -#include <glibmm/miscutils.h> -#include <glibmm/thread.h> -#include <glibmm/timer.h> - #include "raul/Path.hpp" #include "serd/serd.h" @@ -48,6 +43,7 @@ #include "ingen/URIMap.hpp" #include "ingen/World.hpp" #include "ingen/client/ThreadedSigClientInterface.hpp" +#include "ingen/filesystem.hpp" #include "ingen/runtime_paths.hpp" #include "ingen/types.hpp" @@ -71,7 +67,6 @@ ingen_try(bool cond, const char* msg) int main(int argc, char** argv) { - Glib::thread_init(); set_bundle_path_from_code((void*)&ingen_try); // Create world @@ -100,7 +95,7 @@ main(int argc, char** argv) } const std::string start_graph = real_start_graph; - const std::string cmds_file_path = (const char*)execute.get_body(); + const FilePath cmds_file_path = (const char*)execute.get_body(); free(real_start_graph); // Load modules @@ -188,10 +183,10 @@ main(int argc, char** argv) // Save resulting graph auto r = world->store()->find(Raul::Path("/")); - const std::string base = Glib::path_get_basename(cmds_file_path); + const std::string base = cmds_file_path.stem(); const std::string out_name = base.substr(0, base.find('.')) + ".out.ingen"; - const std::string out_path = Glib::build_filename(Glib::get_current_dir(), out_name); - world->serialiser()->write_bundle(r->second, URI(Glib::filename_to_uri(out_path))); + const FilePath out_path = filesystem::current_path() / out_name; + world->serialiser()->write_bundle(r->second, URI(out_path)); // Undo every event (should result in a graph identical to the original) for (int i = 0; i < n_events; ++i) { @@ -202,8 +197,8 @@ main(int argc, char** argv) // Save completely undone graph r = world->store()->find(Raul::Path("/")); const std::string undo_name = base.substr(0, base.find('.')) + ".undo.ingen"; - const std::string undo_path = Glib::build_filename(Glib::get_current_dir(), undo_name); - world->serialiser()->write_bundle(r->second, URI(Glib::filename_to_uri(undo_path))); + const FilePath undo_path = filesystem::current_path() / undo_name; + world->serialiser()->write_bundle(r->second, URI(undo_path)); // Redo every event (should result in a graph identical to the pre-undo output) for (int i = 0; i < n_events; ++i) { @@ -214,8 +209,8 @@ main(int argc, char** argv) // Save completely redone graph r = world->store()->find(Raul::Path("/")); const std::string redo_name = base.substr(0, base.find('.')) + ".redo.ingen"; - const std::string redo_path = Glib::build_filename(Glib::get_current_dir(), redo_name); - world->serialiser()->write_bundle(r->second, URI(Glib::filename_to_uri(redo_path))); + const FilePath redo_path = filesystem::current_path() / redo_name; + world->serialiser()->write_bundle(r->second, URI(redo_path)); serd_env_free(env); sratom_free(sratom); diff --git a/tests/test_utils.hpp b/tests/test_utils.hpp new file mode 100644 index 00000000..a0cc53ac --- /dev/null +++ b/tests/test_utils.hpp @@ -0,0 +1,40 @@ +/* + This file is part of Ingen. + Copyright 2018 David Robillard <http://drobilla.net/> + + Ingen is free software: you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free + Software Foundation, either version 3 of the License, or any later version. + + Ingen 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 Affero General Public License for details. + + You should have received a copy of the GNU Affero General Public License + along with Ingen. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <iostream> + +#include <boost/format.hpp> + +typedef boost::basic_format<char> fmt; + +#define EXPECT_TRUE(value) \ + if (!(value)) { \ + std::cerr << (fmt("error: %1%:%2%: !%3%\n") % __FILE__ % \ + __LINE__ % (#value)); \ + } + +#define EXPECT_FALSE(value) \ + if ((value)) { \ + std::cerr << (fmt("error: %1%:%2%: !%3%\n") % __FILE__ % \ + __LINE__ % (#value)); \ + } + +#define EXPECT_EQ(value, expected) \ + if (!((value) == (expected))) { \ + std::cerr << (fmt("error: %1%:%2%: %3% != %4%\n") % __FILE__ % \ + __LINE__ % (#value) % (#expected)); \ + std::cerr << "note: actual value: " << value << std::endl; \ + } diff --git a/tests/tst_FilePath.cpp b/tests/tst_FilePath.cpp new file mode 100644 index 00000000..d3cf1efa --- /dev/null +++ b/tests/tst_FilePath.cpp @@ -0,0 +1,103 @@ +/* + This file is part of Ingen. + Copyright 2018 David Robillard <http://drobilla.net/> + + Ingen is free software: you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free + Software Foundation, either version 3 of the License, or any later version. + + Ingen 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 Affero General Public License for details. + + You should have received a copy of the GNU Affero General Public License + along with Ingen. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <boost/utility/string_view.hpp> + +#include "ingen/FilePath.hpp" +#include "test_utils.hpp" + +using Ingen::FilePath; + +int +main(int, char**) +{ + EXPECT_EQ(FilePath("/").parent_path(), FilePath("/")); + + EXPECT_TRUE(FilePath("/abs").is_absolute()) + EXPECT_FALSE(FilePath("/abs").is_relative()) + EXPECT_EQ(FilePath("/abs").root_name(), FilePath()); + EXPECT_EQ(FilePath("/abs").root_directory(), FilePath("/")); + EXPECT_EQ(FilePath("/abs").root_path(), FilePath("/")); + EXPECT_EQ(FilePath("/abs").relative_path(), FilePath("abs")); + EXPECT_EQ(FilePath("/abs").parent_path(), FilePath("/")); + EXPECT_EQ(FilePath("/abs").filename(), FilePath("abs")); + EXPECT_EQ(FilePath("/abs").stem(), FilePath("abs")); + EXPECT_EQ(FilePath("/abs").extension(), FilePath()); + + EXPECT_FALSE(FilePath("rel").is_absolute()) + EXPECT_TRUE(FilePath("rel").is_relative()) + EXPECT_EQ(FilePath("rel").root_name(), FilePath()); + EXPECT_EQ(FilePath("rel").root_directory(), FilePath()); + EXPECT_EQ(FilePath("rel").root_path(), FilePath()); + EXPECT_EQ(FilePath("rel").relative_path(), FilePath()); + EXPECT_EQ(FilePath("rel").parent_path(), FilePath()); + EXPECT_EQ(FilePath("rel").filename(), "rel"); + EXPECT_EQ(FilePath("rel").stem(), "rel"); + EXPECT_EQ(FilePath("rel").extension(), FilePath()); + + EXPECT_FALSE(FilePath("file.txt").is_absolute()) + EXPECT_TRUE(FilePath("file.txt").is_relative()) + EXPECT_EQ(FilePath("file.txt").filename(), "file.txt"); + EXPECT_EQ(FilePath("file.txt").stem(), "file"); + EXPECT_EQ(FilePath("file.txt").extension(), ".txt"); + + EXPECT_TRUE(FilePath("/abs/file.txt").is_absolute()) + EXPECT_FALSE(FilePath("/abs/file.txt").is_relative()) + EXPECT_EQ(FilePath("/abs/file.txt").filename(), "file.txt"); + EXPECT_EQ(FilePath("/abs/file.txt").stem(), "file"); + EXPECT_EQ(FilePath("/abs/file.txt").extension(), ".txt"); + + EXPECT_FALSE(FilePath("rel/file.txt").is_absolute()) + EXPECT_TRUE(FilePath("rel/file.txt").is_relative()) + EXPECT_EQ(FilePath("rel/file.txt").filename(), "file.txt"); + EXPECT_EQ(FilePath("rel/file.txt").stem(), "file"); + EXPECT_EQ(FilePath("rel/file.txt").extension(), ".txt"); + + FilePath path("/x"); + EXPECT_EQ(path, "/x"); + path = std::move(std::string("/a")); + EXPECT_EQ(path, "/a"); + + path /= FilePath("b"); + EXPECT_EQ(path, "/a/b"); + + path += FilePath("ar"); + EXPECT_EQ(path, "/a/bar"); + + path += std::string("/c"); + EXPECT_EQ(path, "/a/bar/c"); + + path += "a"; + EXPECT_EQ(path, "/a/bar/ca"); + + path += 'r'; + EXPECT_EQ(path, "/a/bar/car"); + + path += boost::string_view("/d"); + EXPECT_EQ(path, "/a/bar/car/d"); + + const FilePath apple("apple"); + const FilePath zebra("zebra"); + EXPECT_TRUE(apple == apple); + EXPECT_TRUE(apple != zebra); + EXPECT_TRUE(apple < zebra); + EXPECT_TRUE(apple <= zebra); + EXPECT_TRUE(apple <= apple); + EXPECT_TRUE(zebra > apple); + EXPECT_TRUE(zebra >= apple); + EXPECT_TRUE(zebra >= zebra); + return 0; +} @@ -205,6 +205,8 @@ def configure(conf): autowaf.display_msg(conf, "Socket interface", conf.is_defined('HAVE_SOCKET')) print('') +unit_tests = ['tst_FilePath'] + def build(bld): opts = Options.options opts.datadir = opts.datadir or bld.env.PREFIX + 'share' @@ -240,7 +242,7 @@ def build(bld): # Test program if bld.env.BUILD_TESTS: - for i in ['ingen_test', 'ingen_bench']: + for i in ['ingen_test', 'ingen_bench'] + unit_tests: obj = bld(features = 'cxx cxxprogram', source = 'tests/%s.cpp' % i, target = 'tests/%s' % i, @@ -345,6 +347,10 @@ def test(ctx): autowaf.pre_test(ctx, APPNAME, dirs=['.', 'src', 'tests']) + with autowaf.begin_tests(ctx, APPNAME, 'unit'): + for i in unit_tests: + autowaf.run_test(ctx, APPNAME, 'tests/' + i) + with autowaf.begin_tests(ctx, APPNAME, 'system'): empty = ctx.path.find_node('tests/empty.ingen') empty_path = os.path.join(empty.abspath(), 'main.ttl') |