2019-01-04 Jonathan Wakely <jwakely@redhat.com>
+ * include/bits/fs_path.h (path::_List::erase): Replace both overloads
+ with ...
+ (path::pop_back(), path::_M_erase_from(const_iterator)): New member
+ functions that will only erase elements at the end.
+ * src/filesystem/std-path.cc (path::_List::_Impl::pop_back()): Define.
+ (path::_List::_Impl::_M_erase_from(const_iterator)): Define.
+ (path::_List::operator=(const _List&)): Use _M_erase_from(p) instead
+ of erase(p, end()).
+ (path::_List::pop_back()): Define.
+ (path::_List::_M_erase_from(const_iterator)): Define.
+ (path::operator/=(const path&)): Use pop_back to remove last component
+ and _M_erase_from to remove multiple components.
+ (path::_M_append(basic_string_view<value_type>)): Likewise.
+ (path::operator+=(const path&)): Likewise.
+ (path::_M_concat(basic_string_view<value_type>)): Likewise.
+ (path::remove_filename()): Likewise.
+ (path::lexically_normal()): Use _List::_Impl iterators instead of
+ path::iterator. Use pop_back to remove components from the end. Clear
+ trailing filename, instead of using erase(const_iterator) to remove
+ a non-final component.
+ * testsuite/27_io/filesystem/path/generation/normal.cc: Test
+ additional cases.
+ * testsuite/27_io/filesystem/path/generation/normal2.cc: New test.
+
* src/filesystem/std-path.cc (path::operator+=(const path&)): Fix
incorrect treatment of empty filename after trailing slash.
* testsuite/27_io/filesystem/path/concat/path.cc: Test problem case.
void clear() { std::destroy_n(begin(), _M_size); _M_size = 0; }
- void erase(const_iterator cpos)
+ void pop_back()
{
- iterator pos = begin() + (cpos - begin());
- if (pos + 1 != end())
- std::move(pos + 1, end(), pos);
- pos->~_Cmpt();
+ back().~_Cmpt();
--_M_size;
}
- void erase(const_iterator cfirst, const_iterator clast)
+ void _M_erase_from(const_iterator pos)
{
- iterator first = begin() + (cfirst - begin());
- iterator last = begin() + (clast - begin());
- if (last != end())
- std::move(last, end(), first);
- std::destroy(first + (end() - last), end());
+ iterator first = begin() + (pos - begin());
+ iterator last = end();
+ std::destroy(first, last);
_M_size -= last - first;
}
impl->_M_size = newsize;
}
else if (newsize < oldsize)
- impl->erase(impl->begin() + newsize, impl->end());
+ impl->_M_erase_from(impl->begin() + newsize);
std::copy_n(from, minsize, to);
type(_Type::_Multi);
}
}
inline void
-path::_List::erase(const_iterator pos)
+path::_List::pop_back()
{
- _M_impl->erase(pos);
+ __glibcxx_assert(size() > 0);
+ _M_impl->pop_back();
}
inline void
-path::_List::erase(const_iterator first, const_iterator last)
+path::_List::_M_erase_from(const_iterator pos)
{
- _M_impl->erase(first, last);
+ _M_impl->_M_erase_from(pos);
}
inline void
{
// Remove empty final component
if (_M_cmpts._M_impl->back().empty())
- _M_cmpts._M_impl->erase(--output);
+ {
+ _M_cmpts.pop_back();
+ --output;
+ }
}
else if (orig_pathlen != 0)
{
{
_M_pathname.resize(orig_pathlen);
if (orig_type == _Type::_Multi)
- _M_cmpts.erase(_M_cmpts.begin() + orig_size, _M_cmpts.end());
+ _M_cmpts._M_erase_from(_M_cmpts.begin() + orig_size);
else
_M_cmpts.clear();
_M_cmpts.type(orig_type);
{
// Remove empty final component
if (_M_cmpts._M_impl->back().empty())
- _M_cmpts._M_impl->erase(--output);
+ {
+ _M_cmpts.pop_back();
+ --output;
+ }
}
else if (orig_pathlen != 0)
{
{
_M_pathname.resize(orig_pathlen);
if (orig_type == _Type::_Multi)
- _M_cmpts.erase(_M_cmpts.begin() + orig_size, _M_cmpts.end());
+ _M_cmpts._M_erase_from(_M_cmpts.begin() + orig_size);
else
_M_cmpts.clear();
_M_cmpts.type(orig_type);
else if (orig_filenamelen == 0 && it != last)
{
// Remove empty filename at end of original path.
- _M_cmpts.erase(--output);
+ _M_cmpts.pop_back();
+ --output;
}
if (it != last && it->_M_type() == _Type::_Root_name)
if (orig_type == _Type::_Multi)
{
if (_M_cmpts.size() > orig_size)
- _M_cmpts.erase(_M_cmpts.begin() + orig_size, _M_cmpts.end());
+ _M_cmpts._M_erase_from(_M_cmpts.begin() + orig_size);
if (orig_filenamelen != -1)
{
if (_M_cmpts.size() == orig_size)
_M_pathname.resize(orig_pathlen);
if (orig_type == _Type::_Multi)
{
- _M_cmpts.erase(_M_cmpts.begin() + orig_size, _M_cmpts.end());
+ _M_cmpts._M_erase_from(_M_cmpts.begin() + orig_size);
if (orig_filenamelen != -1)
{
auto& back = _M_cmpts.back();
if (prev->_M_type() == _Type::_Root_dir
|| prev->_M_type() == _Type::_Root_name)
{
- _M_cmpts.erase(cmpt);
+ _M_cmpts.pop_back();
if (_M_cmpts.size() == 1)
{
_M_cmpts.type(_M_cmpts.front()._M_type());
// Got a path with a relative path (i.e. at least one non-root
// element) and no filename at the end (i.e. empty last element),
// so must have a trailing slash. See what is before it.
- auto elem = std::prev(ret.end(), 2);
+ auto elem = ret._M_cmpts.end() - 2;
if (elem->has_filename() && !is_dotdot(*elem))
{
// Remove the filename before the trailing slash
// (equiv. to ret = ret.parent_path().remove_filename())
- if (elem == ret.begin())
+ if (elem == ret._M_cmpts.begin())
ret.clear();
else
{
- ret._M_pathname.erase(elem._M_cur->_M_pos);
- // Do we still have a trailing slash?
+ ret._M_pathname.erase(elem->_M_pos);
+ // Remove empty filename at the end:
+ ret._M_cmpts.pop_back();
+ // If we still have a trailing non-root dir separator
+ // then leave an empty filename at the end:
if (std::prev(elem)->_M_type() == _Type::_Filename)
- ret._M_cmpts.erase(elem._M_cur);
- else
- ret._M_cmpts.erase(elem._M_cur, ret._M_cmpts.end());
+ elem->clear();
+ else // remove the component completely:
+ ret._M_cmpts.pop_back();
}
}
- else // ???
+ else
+ // Append the ".." to something ending in "../" which happens
+ // when normalising paths like ".././.." and "../a/../.."
ret /= p;
}
}
--- /dev/null
+// Copyright (C) 2019 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
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++17 -lstdc++fs" }
+// { dg-do run { target c++17 } }
+// { dg-require-filesystem-ts "" }
+
+#undef _GLIBCXX_USE_CXX11_ABI
+#define _GLIBCXX_USE_CXX11_ABI 0
+#include <filesystem>
+#include <testsuite_fs.h>
+
+using std::filesystem::path;
+
+void
+compare_paths(path p, std::string expected)
+{
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ for (auto& c : expected)
+ if (c == '/')
+ c = '\\';
+#endif
+ __gnu_test::compare_paths(p, expected);
+}
+
+void
+test02()
+{
+ path p = "./a/b/c/../.././b/c";
+ // For the COW string this used to produce incorrect results:
+ auto norm = p.lexically_normal();
+ compare_paths( norm, "a/b/c" );
+}
+
+int
+main()
+{
+ test02();
+}