Improved file_path_to_url for windows edge cases

Really not sure about this, but filesystem doesn't have great support
anyway[1], and this handles some unusual cases better.

[1]: https://svn.boost.org/trac10/ticket/5448
This commit is contained in:
Daniel James
2017-10-04 18:05:00 +01:00
parent e0ec41683d
commit 476aa61984
2 changed files with 125 additions and 34 deletions

View File

@@ -121,44 +121,85 @@ namespace quickbook
//
// Some info on file URLs at:
// https://en.wikipedia.org/wiki/File_URI_scheme
std::string file_path_to_url(fs::path const& x)
std::string file_path_to_url_impl(fs::path const& x, bool is_dir)
{
// TODO: Maybe some kind of error if this doesn't understand the path.
// TODO: Might need a special cygwin implementation.
// TODO: What if x.has_root_name() && !x.has_root_directory()?
// TODO: What does Boost.Filesystem do for '//localhost/c:/path'?
// Is that event allowed by windows?
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()) {
std::string root_name = detail::path_to_generic(x.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:///";
}
if (root_name.size() > 2 && root_name[0] == '/' && root_name[1] == '/') {
// root_name is a network location.
return "file:" + detail::escape_uri(detail::path_to_generic(x));
// 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 (root_name.size() >= 2 && root_name[root_name.size() - 1] == ':') {
// root_name is a drive.
return "file:///"
+ detail::escape_uri(root_name.substr(0, root_name.size() - 1))
+ ":/" // Somtimes "|/" is used, to avoid misinterpreting the ':'.
+ detail::escape_uri(detail::path_to_generic(x.relative_path()));
}
else {
// Not sure what root_name is.
return detail::escape_uri(detail::path_to_generic(x));
} 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;
}
}
else if (x.has_root_directory()) {
return "file://" + detail::escape_uri(detail::path_to_generic(x));
}
else {
return detail::escape_uri(detail::path_to_generic(x));
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(x) + "/";
return file_path_to_url_impl(x, true);
}
namespace detail {