From cf290ea3255625513b6ad5a5c4e189c833a67a92 Mon Sep 17 00:00:00 2001 From: Jonathan Wakely Date: Wed, 23 May 2018 17:11:06 +0100 Subject: [PATCH] Fix handling of an empty filename at end of a path The C++17 std::filesystem::path grammar allows an empty filename as the last component (to signify a trailing slash). The existing code does not handle this consistently, sometimes an empty filename has type _Multi and sometimes it has type _Filename. This can result in a non-empty iterator range for an empty filename component. This change ensures that empty paths always have type _Filename and will yield an empty iterator range. * include/bits/fs_path.h (path::_M_type): Change default member initializer to _Filename. (path::begin): Create past-the-end iterator for empty path. * src/filesystem/std-path.cc (path::remove_filename()): Remove debugging check. (path::has_relative_path()): Return false for empty filenames. (path::_M_split_cmpts): Set _M_type to _Filename for empty paths. Fix offset of empty final component. * testsuite/27_io/filesystem/path/itr/components.cc: New. * testsuite/27_io/filesystem/path/itr/traversal.cc: Add new inputs. From-SVN: r260616 --- libstdc++-v3/ChangeLog | 13 +++++ libstdc++-v3/include/bits/fs_path.h | 4 +- libstdc++-v3/src/filesystem/std-path.cc | 17 +++---- .../27_io/filesystem/path/itr/components.cc | 51 +++++++++++++++++++ .../27_io/filesystem/path/itr/traversal.cc | 2 +- 5 files changed, 75 insertions(+), 12 deletions(-) create mode 100644 libstdc++-v3/testsuite/27_io/filesystem/path/itr/components.cc diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index 9ca6ec6c1c0..f5d6b10af1a 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,3 +1,16 @@ +2018-05-23 Jonathan Wakely + + * include/bits/fs_path.h (path::_M_type): Change default member + initializer to _Filename. + (path::begin): Create past-the-end iterator for empty path. + * src/filesystem/std-path.cc (path::remove_filename()): Remove + debugging check. + (path::has_relative_path()): Return false for empty filenames. + (path::_M_split_cmpts): Set _M_type to _Filename for empty paths. + Fix offset of empty final component. + * testsuite/27_io/filesystem/path/itr/components.cc: New. + * testsuite/27_io/filesystem/path/itr/traversal.cc: Add new inputs. + 2018-05-21 Jonathan Wakely Add support for opening file streams from wide character strings. diff --git a/libstdc++-v3/include/bits/fs_path.h b/libstdc++-v3/include/bits/fs_path.h index 51af2891647..79a341830db 100644 --- a/libstdc++-v3/include/bits/fs_path.h +++ b/libstdc++-v3/include/bits/fs_path.h @@ -497,7 +497,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 struct _Cmpt; using _List = _GLIBCXX_STD_C::vector<_Cmpt>; _List _M_cmpts; // empty unless _M_type == _Type::_Multi - _Type _M_type = _Type::_Multi; + _Type _M_type = _Type::_Filename; }; template<> @@ -1076,7 +1076,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 { if (_M_type == _Type::_Multi) return iterator(this, _M_cmpts.begin()); - return iterator(this, false); + return iterator(this, empty()); } inline path::iterator diff --git a/libstdc++-v3/src/filesystem/std-path.cc b/libstdc++-v3/src/filesystem/std-path.cc index 6f594cec1d5..755cb7c883a 100644 --- a/libstdc++-v3/src/filesystem/std-path.cc +++ b/libstdc++-v3/src/filesystem/std-path.cc @@ -63,8 +63,6 @@ path::remove_filename() } else if (_M_type == _Type::_Filename) clear(); - if (!empty() && _M_pathname.back() != '/') - throw 1; return *this; } @@ -292,7 +290,7 @@ path::has_root_path() const bool path::has_relative_path() const { - if (_M_type == _Type::_Filename) + if (_M_type == _Type::_Filename && !_M_pathname.empty()) return true; if (!_M_cmpts.empty()) { @@ -301,7 +299,7 @@ path::has_relative_path() const ++__it; if (__it != _M_cmpts.end() && __it->_M_type == _Type::_Root_dir) ++__it; - if (__it != _M_cmpts.end()) + if (__it != _M_cmpts.end() && !__it->_M_pathname.empty()) return true; } return false; @@ -514,11 +512,13 @@ path::_M_find_extension() const void path::_M_split_cmpts() { - _M_type = _Type::_Multi; _M_cmpts.clear(); - if (_M_pathname.empty()) - return; + { + _M_type = _Type::_Filename; + return; + } + _M_type = _Type::_Multi; size_t pos = 0; const size_t len = _M_pathname.size(); @@ -593,8 +593,7 @@ path::_M_split_cmpts() // An empty element, if trailing non-root directory-separator present. if (_M_cmpts.back()._M_type == _Type::_Filename) { - const auto& last = _M_cmpts.back(); - pos = last._M_pos + last._M_pathname.size(); + pos = _M_pathname.size(); _M_cmpts.emplace_back(string_type(), _Type::_Filename, pos); } } diff --git a/libstdc++-v3/testsuite/27_io/filesystem/path/itr/components.cc b/libstdc++-v3/testsuite/27_io/filesystem/path/itr/components.cc new file mode 100644 index 00000000000..15736b22fd5 --- /dev/null +++ b/libstdc++-v3/testsuite/27_io/filesystem/path/itr/components.cc @@ -0,0 +1,51 @@ +// Copyright (C) 2018 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library 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 General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// . + +// { dg-options "-std=gnu++17 -lstdc++fs" } +// { dg-do run { target c++17 } } +// { dg-require-filesystem-ts "" } + +#include +#include +#include +#include + +void +test01() +{ + for (std::filesystem::path p : __gnu_test::test_paths) + { + if (p.empty()) + VERIFY(std::distance(p.begin(), p.end()) == 0); + else + VERIFY(std::distance(p.begin(), p.end()) != 0); + + for (const std::filesystem::path& cmpt : p) + { + if (cmpt.empty()) + VERIFY(std::distance(cmpt.begin(), cmpt.end()) == 0); + else + VERIFY(std::distance(cmpt.begin(), cmpt.end()) == 1); + } + } +} + +int +main() +{ + test01(); +} diff --git a/libstdc++-v3/testsuite/27_io/filesystem/path/itr/traversal.cc b/libstdc++-v3/testsuite/27_io/filesystem/path/itr/traversal.cc index 3fc60d48d13..c23cb14fd73 100644 --- a/libstdc++-v3/testsuite/27_io/filesystem/path/itr/traversal.cc +++ b/libstdc++-v3/testsuite/27_io/filesystem/path/itr/traversal.cc @@ -104,7 +104,7 @@ test02() void test03() { - path paths[] = { "single", "multiple/elements" }; + path paths[] = { "single", "multiple/elements", "trailing/slash/", "/." }; for (const path& p : paths) for (auto iter = p.begin(); iter != p.end(); ++iter) { -- 2.30.2