diff options
Diffstat (limited to 'src/boost/tools/quickbook/src/path.cpp')
-rw-r--r-- | src/boost/tools/quickbook/src/path.cpp | 450 |
1 files changed, 450 insertions, 0 deletions
diff --git a/src/boost/tools/quickbook/src/path.cpp b/src/boost/tools/quickbook/src/path.cpp new file mode 100644 index 000000000..66c27ba31 --- /dev/null +++ b/src/boost/tools/quickbook/src/path.cpp @@ -0,0 +1,450 @@ +/*============================================================================= + Copyright (c) 2002 2004 2006 Joel de Guzman + Copyright (c) 2004 Eric Niebler + Copyright (c) 2005 Thomas Guest + Copyright (c) 2013, 2017 Daniel James + + Use, modification and distribution is subject to the Boost Software + License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ + +#include "path.hpp" +#include <cassert> +#include <boost/filesystem/operations.hpp> +#include <boost/range/algorithm/replace.hpp> +#include "for.hpp" +#include "glob.hpp" +#include "include_paths.hpp" +#include "state.hpp" +#include "utils.hpp" + +#if QUICKBOOK_CYGWIN_PATHS +#include <sys/cygwin.h> +#include <boost/scoped_array.hpp> +#endif + +namespace quickbook +{ + // Not a general purpose normalization function, just + // from paths from the root directory. It strips the excess + // ".." parts from a path like: "x/../../y", leaving "y". + std::vector<fs::path> remove_dots_from_path(fs::path const& path) + { + assert(!path.has_root_directory() && !path.has_root_name()); + + std::vector<fs::path> parts; + + QUICKBOOK_FOR (fs::path const& part, path) { + if (part.empty() || part == ".") { + } + else if (part == "..") { + if (!parts.empty()) parts.pop_back(); + } + else { + parts.push_back(part); + } + } + + return parts; + } + + // The relative path from base to path + fs::path path_difference( + fs::path const& base, fs::path const& path, bool is_file) + { + fs::path absolute_base = fs::absolute(base), + absolute_path = fs::absolute(path); + + // Remove '.', '..' and empty parts from the remaining path + std::vector<fs::path> base_parts = remove_dots_from_path( + absolute_base.relative_path()), + path_parts = remove_dots_from_path( + absolute_path.relative_path()); + + std::vector<fs::path>::iterator base_it = base_parts.begin(), + base_end = base_parts.end(), + path_it = path_parts.begin(), + path_end = path_parts.end(); + + // Build up the two paths in these variables, checking for the first + // difference. + fs::path base_tmp = absolute_base.root_path(), + path_tmp = absolute_path.root_path(); + + fs::path result; + + // If they have different roots then there's no relative path so + // just build an absolute path. + if (!fs::equivalent(base_tmp, path_tmp)) { + result = path_tmp; + } + else { + // Find the point at which the paths differ + for (; base_it != base_end && path_it != path_end; + ++base_it, ++path_it) { + base_tmp /= *base_it; + path_tmp /= *path_it; + if (*base_it != *path_it) { + if (!fs::exists(base_tmp) || !fs::exists(path_tmp) || + !fs::equivalent(base_tmp, path_tmp)) { + break; + } + } + } + + if (is_file && path_it == path_end && + path_it != path_parts.begin()) { + --path_it; + result = ".."; + } + else if (base_it == base_end && path_it == path_end) { + result = "."; + } + + // Build a relative path to that point + for (; base_it != base_end; ++base_it) + result /= ".."; + } + + // Build the rest of our path + for (; path_it != path_end; ++path_it) + result /= *path_it; + + return result; + } + + // Convert a Boost.Filesystem path to a URL. + // + // I'm really not sure about this, as the meaning of root_name and + // root_directory are only clear for windows. + // + // Some info on file URLs at: + // https://en.wikipedia.org/wiki/File_URI_scheme + std::string file_path_to_url_impl(fs::path const& x, bool is_dir) + { + fs::path::const_iterator it = x.begin(), end = x.end(); + if (it == end) { + return is_dir ? "./" : ""; + } + + std::string result; + bool sep = false; + std::string part; + if (x.has_root_name()) { + // Handle network address (e.g. \\example.com) + part = detail::path_to_generic(*it); + if (part.size() >= 2 && part[0] == '/' && part[1] == '/') { + result = "file:" + detail::escape_uri(part); + sep = true; + ++it; + if (it != end && *it == "/") { + result += "/"; + sep = false; + ++it; + } + } + else { + result = "file:///"; + } + + // Handle windows root (e.g. c:) + if (it != end) { + part = detail::path_to_generic(*it); + if (part.size() >= 2 && part[part.size() - 1] == ':') { + result += + detail::escape_uri(part.substr(0, part.size() - 1)); + result += ':'; + sep = false; + ++it; + } + } + } + else if (x.has_root_directory()) { + result = "file://"; + sep = true; + } + else if (*it == ".") { + result = "."; + sep = true; + ++it; + } + + for (; it != end; ++it) { + part = detail::path_to_generic(*it); + if (part == "/") { + result += "/"; + sep = false; + } + else if (part == ".") { + // If the path has a trailing slash, write it out, + // even if is_dir is false. + if (sep) { + result += "/"; + sep = false; + } + } + else { + if (sep) { + result += "/"; + } + result += detail::escape_uri(detail::path_to_generic(*it)); + sep = true; + } + } + + if (is_dir && sep) { + result += "/"; + } + + return result; + } + + std::string file_path_to_url(fs::path const& x) + { + return file_path_to_url_impl(x, false); + } + + std::string dir_path_to_url(fs::path const& x) + { + return file_path_to_url_impl(x, true); + } + + namespace detail + { +#if QUICKBOOK_WIDE_PATHS + std::string command_line_to_utf8(command_line_string const& x) + { + return to_utf8(x); + } +#else + std::string command_line_to_utf8(command_line_string const& x) + { + return x; + } +#endif + +#if QUICKBOOK_WIDE_PATHS + fs::path generic_to_path(quickbook::string_view x) + { + return fs::path(from_utf8(x)); + } + + std::string path_to_generic(fs::path const& x) + { + return to_utf8(x.generic_wstring()); + } +#else + fs::path generic_to_path(quickbook::string_view x) + { + return fs::path(x.begin(), x.end()); + } + + std::string path_to_generic(fs::path const& x) + { + return x.generic_string(); + } +#endif + +#if QUICKBOOK_CYGWIN_PATHS + fs::path command_line_to_path(command_line_string const& path) + { + cygwin_conv_path_t flags = CCP_POSIX_TO_WIN_W | CCP_RELATIVE; + + ssize_t size = cygwin_conv_path(flags, path.c_str(), NULL, 0); + + if (size < 0) + throw conversion_error( + "Error converting cygwin path to windows."); + + boost::scoped_array<char> result(new char[size]); + void* ptr = result.get(); + + if (cygwin_conv_path(flags, path.c_str(), ptr, size)) + throw conversion_error( + "Error converting cygwin path to windows."); + + return fs::path(static_cast<wchar_t*>(ptr)); + } + + stream_string path_to_stream(fs::path const& path) + { + cygwin_conv_path_t flags = CCP_WIN_W_TO_POSIX | CCP_RELATIVE; + + ssize_t size = + cygwin_conv_path(flags, path.native().c_str(), NULL, 0); + + if (size < 0) + throw conversion_error( + "Error converting windows path to cygwin."); + + boost::scoped_array<char> result(new char[size]); + + if (cygwin_conv_path( + flags, path.native().c_str(), result.get(), size)) + throw conversion_error( + "Error converting windows path to cygwin."); + + return std::string(result.get()); + } +#else + fs::path command_line_to_path(command_line_string const& path) + { + return fs::path(path); + } + +#if QUICKBOOK_WIDE_PATHS && !QUICKBOOK_WIDE_STREAMS + stream_string path_to_stream(fs::path const& path) + { + return path.string(); + } +#else + stream_string path_to_stream(fs::path const& path) + { + return path.native(); + } +#endif + +#endif // QUICKBOOK_CYGWIN_PATHS + + enum path_or_url_type + { + path_or_url_empty = 0, + path_or_url_path, + path_or_url_url + }; + + path_or_url::path_or_url() : type_(path_or_url_empty) {} + + path_or_url::path_or_url(path_or_url const& x) + : type_(x.type_), path_(x.path_), url_(x.url_) + { + } + + path_or_url::path_or_url(command_line_string const& x) + { + auto rep = command_line_to_utf8(x); + auto it = rep.begin(), end = rep.end(); + std::size_t count = 0; + while (it != end && + ((*it >= 'a' && *it <= 'z') || (*it >= 'A' && *it <= 'Z') || + *it == '+' || *it == '-' || *it == '.')) { + ++it; + ++count; + } + + if (it != end && *it == ':' && count > 1) { + type_ = path_or_url_url; + } + else { + type_ = path_or_url_path; + } + + switch (type_) { + case path_or_url_empty: + break; + case path_or_url_path: + path_ = command_line_to_path(x); + break; + case path_or_url_url: + url_ = rep; + break; + default: + assert(false); + } + } + + path_or_url& path_or_url::operator=(path_or_url const& x) + { + type_ = x.type_; + path_ = x.path_; + url_ = x.url_; + return *this; + } + + path_or_url& path_or_url::operator=(command_line_string const& x) + { + path_or_url tmp(x); + swap(tmp); + return *this; + } + + void path_or_url::swap(path_or_url& x) + { + std::swap(type_, x.type_); + std::swap(path_, x.path_); + std::swap(url_, x.url_); + } + + path_or_url path_or_url::url(string_view x) + { + path_or_url r; + r.type_ = path_or_url_url; + r.url_.assign(x.begin(), x.end()); + return r; + } + + path_or_url path_or_url::path(boost::filesystem::path const& x) + { + path_or_url r; + r.type_ = path_or_url_path; + r.path_ = x; + return r; + } + + path_or_url::operator bool() const + { + return type_ != path_or_url_empty; + } + + bool path_or_url::is_path() const { return type_ == path_or_url_path; } + + bool path_or_url::is_url() const { return type_ == path_or_url_url; } + + boost::filesystem::path const& path_or_url::get_path() const + { + assert(is_path()); + return path_; + } + + std::string const& path_or_url::get_url() const + { + assert(is_url()); + return url_; + } + + path_or_url path_or_url::operator/(string_view x) const + { + path_or_url r; + r.type_ = type_; + + switch (type_) { + case path_or_url_empty: + assert(false); + break; + case path_or_url_path: + r.path_ = path_ / x.to_s(); + break; + case path_or_url_url: { + r.url_ = url_; + auto pos = r.url_.rfind('/'); + if (pos == std::string::npos) { + pos = r.url_.rfind(':'); + } + if (pos != std::string::npos) { + r.url_.resize(pos + 1); + } + else { + // Error? Empty string? + r.url_ = "/"; + } + r.url_ += x; + break; + } + default: + assert(false); + } + return r; + } + } +} |