diff --git a/doc/release_history.html b/doc/release_history.html
index 60adff12b..f1ffa34e3 100644
--- a/doc/release_history.html
+++ b/doc/release_history.html
@@ -46,6 +46,8 @@
1.86.0
On POSIX systems, is_empty
now indicates error if invoked on a file other than a regular file or a directory.
On Windows, fixed file_size
and is_empty
operating on symlinks rather than the files the symlinks refer to. (#313)
directory_entry::refresh
no longer throws an exception if the file referenced by the entry doesn't exist. This makes directory_entry::status
and directory_entry::symlink_status
, as well as methods based on them, behave similarly to the equivalent standalone operations. The fact that the file does not exist is still indicated via the error_code
returned by the corresponding directory_entry::refresh
overload, or can be seen by testing if the file type returned by directory_entry::status
or directory_entry::symlink_status
calls is file_type::file_not_found
. (#314)
+ Fixed weakly_canonical
testing path elements for existence relative to the current path instead of the base path specified in the call, if the input path was a relative path.
+ On Windows, fixed weakly_canonical
producing incorrect result path when the input path started with "..". (#311)
1.85.0
diff --git a/src/operations.cpp b/src/operations.cpp
index 25fb1ceea..e2fa23545 100644
--- a/src/operations.cpp
+++ b/src/operations.cpp
@@ -2568,18 +2568,11 @@ path absolute_v4(path const& p, path const& base, system::error_code* ec)
BOOST_FILESYSTEM_DECL
path canonical_v3(path const& p, path const& base, system::error_code* ec)
{
- if (ec)
- ec->clear();
-
- path source(p);
- if (!p.is_absolute())
+ path source(detail::absolute_v3(p, base, ec));
+ if (ec && *ec)
{
- source = detail::absolute_v3(p, base, ec);
- if (ec && *ec)
- {
- return_empty_path:
- return path();
- }
+ return_empty_path:
+ return path();
}
system::error_code local_ec;
@@ -2700,18 +2693,11 @@ path canonical_v3(path const& p, path const& base, system::error_code* ec)
BOOST_FILESYSTEM_DECL
path canonical_v4(path const& p, path const& base, system::error_code* ec)
{
- if (ec)
- ec->clear();
-
- path source(p);
- if (!p.is_absolute())
+ path source(detail::absolute_v4(p, base, ec));
+ if (ec && *ec)
{
- source = detail::absolute_v4(p, base, ec);
- if (ec && *ec)
- {
- return_empty_path:
- return path();
- }
+ return_empty_path:
+ return path();
}
system::error_code local_ec;
@@ -4882,13 +4868,20 @@ path system_complete(path const& p, system::error_code* ec)
BOOST_FILESYSTEM_DECL
path weakly_canonical_v3(path const& p, path const& base, system::error_code* ec)
{
+ path source(detail::absolute_v3(p, base, ec));
+ if (ec && *ec)
+ {
+ return_empty_path:
+ return path();
+ }
+
system::error_code local_ec;
- const path::iterator p_end(p.end());
+ const path::iterator source_end(source.end());
#if defined(BOOST_POSIX_API)
- path::iterator itr(p_end);
- path head(p);
+ path::iterator itr(source_end);
+ path head(source);
for (; !head.empty(); path_algorithms::decrement_v4(itr))
{
file_status head_status(detail::status_impl(head, &local_ec));
@@ -4898,7 +4891,7 @@ path weakly_canonical_v3(path const& p, path const& base, system::error_code* ec
BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::weakly_canonical", head, local_ec));
*ec = local_ec;
- return path();
+ goto return_empty_path;
}
if (head_status.type() != fs::file_not_found)
@@ -4925,18 +4918,18 @@ path weakly_canonical_v3(path const& p, path const& base, system::error_code* ec
// Also, avoid querying status of the root name such as \\?\c: as CreateFileW returns ERROR_INVALID_FUNCTION for
// such path. Querying the status of a root name such as c: is also not right as this path refers to the current
// directory on drive C:, which is not what we want to test for existence anyway.
- path::iterator itr(p.begin());
+ path::iterator itr(source.begin());
path head;
- if (p.has_root_name())
+ if (source.has_root_name())
{
- BOOST_ASSERT(itr != p_end);
+ BOOST_ASSERT(itr != source_end);
head = *itr;
path_algorithms::increment_v4(itr);
}
- if (p.has_root_directory())
+ if (source.has_root_directory())
{
- BOOST_ASSERT(itr != p_end);
+ BOOST_ASSERT(itr != source_end);
// Convert generic separator returned by the iterator for the root directory to
// the preferred separator.
head += path::preferred_separator;
@@ -4952,28 +4945,28 @@ path weakly_canonical_v3(path const& p, path const& base, system::error_code* ec
BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::weakly_canonical", head, local_ec));
*ec = local_ec;
- return path();
+ goto return_empty_path;
}
if (head_status.type() == fs::file_not_found)
{
// If the root path does not exist then no path element exists
- itr = p.begin();
+ itr = source.begin();
head.clear();
goto skip_head;
}
}
- for (; itr != p_end; path_algorithms::increment_v4(itr))
+ for (; itr != source_end; path_algorithms::increment_v4(itr))
{
- path const& p_elem = *itr;
+ path const& source_elem = *itr;
// Avoid querying status of paths containing dot and dot-dot elements, as this will break
// if the root name starts with "\\?\".
- if (path_algorithms::compare_v4(p_elem, dot_p) == 0)
+ if (path_algorithms::compare_v4(source_elem, dot_p) == 0)
continue;
- if (path_algorithms::compare_v4(p_elem, dot_dot_p) == 0)
+ if (path_algorithms::compare_v4(source_elem, dot_dot_p) == 0)
{
if (head.has_relative_path())
head.remove_filename_and_trailing_separators();
@@ -4981,7 +4974,7 @@ path weakly_canonical_v3(path const& p, path const& base, system::error_code* ec
continue;
}
- path_algorithms::append_v4(head, p_elem);
+ path_algorithms::append_v4(head, source_elem);
file_status head_status(detail::status_impl(head, &local_ec));
if (BOOST_UNLIKELY(head_status.type() == fs::status_error))
@@ -4990,7 +4983,7 @@ path weakly_canonical_v3(path const& p, path const& base, system::error_code* ec
BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::weakly_canonical", head, local_ec));
*ec = local_ec;
- return path();
+ goto return_empty_path;
}
if (head_status.type() == fs::file_not_found)
@@ -5006,7 +4999,7 @@ skip_head:;
path tail;
bool tail_has_dots = false;
- for (; itr != p_end; path_algorithms::increment_v4(itr))
+ for (; itr != source_end; path_algorithms::increment_v4(itr))
{
path const& tail_elem = *itr;
path_algorithms::append_v4(tail, tail_elem);
@@ -5022,7 +5015,7 @@ skip_head:;
BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::weakly_canonical", head, local_ec));
*ec = local_ec;
- return path();
+ goto return_empty_path;
}
if (BOOST_LIKELY(!tail.empty()))
@@ -5031,7 +5024,7 @@ skip_head:;
// optimization: only normalize if tail had dot or dot-dot element
if (tail_has_dots)
- return path_algorithms::lexically_normal_v4(head);
+ head = path_algorithms::lexically_normal_v4(head);
}
return head;
@@ -5040,13 +5033,20 @@ skip_head:;
BOOST_FILESYSTEM_DECL
path weakly_canonical_v4(path const& p, path const& base, system::error_code* ec)
{
+ path source(detail::absolute_v4(p, base, ec));
+ if (ec && *ec)
+ {
+ return_empty_path:
+ return path();
+ }
+
system::error_code local_ec;
- const path::iterator p_end(p.end());
+ const path::iterator source_end(source.end());
#if defined(BOOST_POSIX_API)
- path::iterator itr(p_end);
- path head(p);
+ path::iterator itr(source_end);
+ path head(source);
for (; !head.empty(); path_algorithms::decrement_v4(itr))
{
file_status head_status(detail::status_impl(head, &local_ec));
@@ -5056,7 +5056,7 @@ path weakly_canonical_v4(path const& p, path const& base, system::error_code* ec
BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::weakly_canonical", head, local_ec));
*ec = local_ec;
- return path();
+ goto return_empty_path;
}
if (head_status.type() != fs::file_not_found)
@@ -5083,18 +5083,18 @@ path weakly_canonical_v4(path const& p, path const& base, system::error_code* ec
// Also, avoid querying status of the root name such as \\?\c: as CreateFileW returns ERROR_INVALID_FUNCTION for
// such path. Querying the status of a root name such as c: is also not right as this path refers to the current
// directory on drive C:, which is not what we want to test for existence anyway.
- path::iterator itr(p.begin());
+ path::iterator itr(source.begin());
path head;
- if (p.has_root_name())
+ if (source.has_root_name())
{
- BOOST_ASSERT(itr != p_end);
+ BOOST_ASSERT(itr != source_end);
head = *itr;
path_algorithms::increment_v4(itr);
}
- if (p.has_root_directory())
+ if (source.has_root_directory())
{
- BOOST_ASSERT(itr != p_end);
+ BOOST_ASSERT(itr != source_end);
// Convert generic separator returned by the iterator for the root directory to
// the preferred separator.
head += path::preferred_separator;
@@ -5110,28 +5110,28 @@ path weakly_canonical_v4(path const& p, path const& base, system::error_code* ec
BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::weakly_canonical", head, local_ec));
*ec = local_ec;
- return path();
+ goto return_empty_path;
}
if (head_status.type() == fs::file_not_found)
{
// If the root path does not exist then no path element exists
- itr = p.begin();
+ itr = source.begin();
head.clear();
goto skip_head;
}
}
- for (; itr != p_end; path_algorithms::increment_v4(itr))
+ for (; itr != source_end; path_algorithms::increment_v4(itr))
{
- path const& p_elem = *itr;
+ path const& source_elem = *itr;
// Avoid querying status of paths containing dot and dot-dot elements, as this will break
// if the root name starts with "\\?\".
- if (path_algorithms::compare_v4(p_elem, dot_p) == 0)
+ if (path_algorithms::compare_v4(source_elem, dot_p) == 0)
continue;
- if (path_algorithms::compare_v4(p_elem, dot_dot_p) == 0)
+ if (path_algorithms::compare_v4(source_elem, dot_dot_p) == 0)
{
if (head.has_relative_path())
head.remove_filename_and_trailing_separators();
@@ -5139,7 +5139,7 @@ path weakly_canonical_v4(path const& p, path const& base, system::error_code* ec
continue;
}
- path_algorithms::append_v4(head, p_elem);
+ path_algorithms::append_v4(head, source_elem);
file_status head_status(detail::status_impl(head, &local_ec));
if (BOOST_UNLIKELY(head_status.type() == fs::status_error))
@@ -5148,7 +5148,7 @@ path weakly_canonical_v4(path const& p, path const& base, system::error_code* ec
BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::weakly_canonical", head, local_ec));
*ec = local_ec;
- return path();
+ goto return_empty_path;
}
if (head_status.type() == fs::file_not_found)
@@ -5164,7 +5164,7 @@ skip_head:;
path tail;
bool tail_has_dots = false;
- for (; itr != p_end; path_algorithms::increment_v4(itr))
+ for (; itr != source_end; path_algorithms::increment_v4(itr))
{
path const& tail_elem = *itr;
path_algorithms::append_v4(tail, tail_elem);
@@ -5180,7 +5180,7 @@ skip_head:;
BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::weakly_canonical", head, local_ec));
*ec = local_ec;
- return path();
+ goto return_empty_path;
}
if (BOOST_LIKELY(!tail.empty()))
@@ -5189,7 +5189,7 @@ skip_head:;
// optimization: only normalize if tail had dot or dot-dot element
if (tail_has_dots)
- return path_algorithms::lexically_normal_v4(head);
+ head = path_algorithms::lexically_normal_v4(head);
}
return head;
diff --git a/test/operations_test.cpp b/test/operations_test.cpp
index 7f379377d..d690acf92 100644
--- a/test/operations_test.cpp
+++ b/test/operations_test.cpp
@@ -2821,6 +2821,12 @@ void weakly_canonical_basic_tests()
BOOST_TEST_EQ(fs::weakly_canonical(dir / "no-such/foo/../bar"), dir / "no-such/bar");
BOOST_TEST_EQ(fs::weakly_canonical(dir / "../no-such/foo/../bar"), dir.parent_path() / "no-such/bar");
BOOST_TEST_EQ(fs::weakly_canonical(dir / "no-such/../f0"), dir / "f0"); // dir / "f0" exists, dir / "no-such" does not
+ BOOST_TEST_EQ(fs::weakly_canonical("f0", d1), d1 / "f0");
+ BOOST_TEST_EQ(fs::weakly_canonical("./f0", d1), d1 / "f0");
+ BOOST_TEST_EQ(fs::weakly_canonical("./foo", d1), d1 / "foo");
+ BOOST_TEST_EQ(fs::weakly_canonical("../f0", d1), dir / "f0");
+ BOOST_TEST_EQ(fs::weakly_canonical("../foo", d1), dir / "foo");
+ BOOST_TEST_EQ(fs::weakly_canonical("..//foo", d1), dir / "foo");
#ifdef BOOST_WINDOWS_API
BOOST_TEST_EQ(fs::weakly_canonical("c:/no-such/foo/bar"), fs::path("c:/no-such/foo/bar"));