Implement filesystem::canonical() without realpath

PR libstdc++/67173
	* acinclude.m4 (GLIBCXX_CHECK_FILESYSTEM_DEPS): Check _XOPEN_VERSION
	and PATH_MAX for _GLIBCXX_USE_REALPATH.
	* config.h.in: Regenerate.
	* configure: Regenerate.
	* src/filesystem/ops.cc: (canonical) [!_GLIBCXX_USE_REALPATH]: Add
	alternative implementation.
	* testsuite/experimental/filesystem/operations/canonical.cc: New.
	* testsuite/experimental/filesystem/operations/exists.cc: Add more
	tests.
	* testsuite/experimental/filesystem/operations/absolute.cc: Add test
	variables.
	* testsuite/experimental/filesystem/operations/copy.cc: Likewise.
	* testsuite/experimental/filesystem/operations/current_path.cc:
	Likewise.
	* testsuite/experimental/filesystem/operations/file_size.cc: Likewise.
	* testsuite/experimental/filesystem/operations/status.cc: Likewise.
	* testsuite/experimental/filesystem/operations/temp_directory_path.cc:
	Likewise.

From-SVN: r227836
This commit is contained in:
Jonathan Wakely 2015-09-16 23:50:28 +01:00 committed by Jonathan Wakely
parent 4ec39494ac
commit 3036299861
13 changed files with 262 additions and 26 deletions

View File

@ -1,3 +1,25 @@
2015-09-16 Jonathan Wakely <jwakely@redhat.com>
PR libstdc++/67173
* acinclude.m4 (GLIBCXX_CHECK_FILESYSTEM_DEPS): Check _XOPEN_VERSION
and PATH_MAX for _GLIBCXX_USE_REALPATH.
* config.h.in: Regenerate.
* configure: Regenerate.
* src/filesystem/ops.cc: (canonical) [!_GLIBCXX_USE_REALPATH]: Add
alternative implementation.
* testsuite/experimental/filesystem/operations/canonical.cc: New.
* testsuite/experimental/filesystem/operations/exists.cc: Add more
tests.
* testsuite/experimental/filesystem/operations/absolute.cc: Add test
variables.
* testsuite/experimental/filesystem/operations/copy.cc: Likewise.
* testsuite/experimental/filesystem/operations/current_path.cc:
Likewise.
* testsuite/experimental/filesystem/operations/file_size.cc: Likewise.
* testsuite/experimental/filesystem/operations/status.cc: Likewise.
* testsuite/experimental/filesystem/operations/temp_directory_path.cc:
Likewise.
2015-09-11 Jonathan Wakely <jwakely@redhat.com>
PR libstdc++/67173

View File

@ -3947,13 +3947,24 @@ dnl
AC_MSG_CHECKING([for realpath])
AC_CACHE_VAL(glibcxx_cv_realpath, [dnl
GCC_TRY_COMPILE_OR_LINK(
[#include <stdlib.h>],
[char *tmp = realpath((const char*)NULL, (char*)NULL);],
[
#include <stdlib.h>
#include <unistd.h>
],
[
#if _XOPEN_VERSION < 500
#error
#elif _XOPEN_VERSION >= 700 || defined(PATH_MAX)
char *tmp = realpath((const char*)NULL, (char*)NULL);
#else
#error
#endif
],
[glibcxx_cv_realpath=yes],
[glibcxx_cv_realpath=no])
])
if test $glibcxx_cv_realpath = yes; then
AC_DEFINE(_GLIBCXX_USE_REALPATH, 1, [Define if realpath is available in <stdlib.h>.])
AC_DEFINE(_GLIBCXX_USE_REALPATH, 1, [Define if usable realpath is available in <stdlib.h>.])
fi
AC_MSG_RESULT($glibcxx_cv_realpath)
dnl

View File

@ -883,7 +883,7 @@
of TR1 (Chapter 5.1). */
#undef _GLIBCXX_USE_RANDOM_TR1
/* Define if realpath is available in <stdlib.h>. */
/* Define if usable realpath is available in <stdlib.h>. */
#undef _GLIBCXX_USE_REALPATH
/* Defined if sched_yield is available. */

View File

@ -79178,11 +79178,22 @@ else
if test x$gcc_no_link = xyes; then
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <stdlib.h>
#include <stdlib.h>
#include <unistd.h>
int
main ()
{
char *tmp = realpath((const char*)NULL, (char*)NULL);
#if _XOPEN_VERSION < 500
#error
#elif _XOPEN_VERSION >= 700 || defined(PATH_MAX)
char *tmp = realpath((const char*)NULL, (char*)NULL);
#else
#error
#endif
;
return 0;
}
@ -79199,11 +79210,22 @@ else
fi
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <stdlib.h>
#include <stdlib.h>
#include <unistd.h>
int
main ()
{
char *tmp = realpath((const char*)NULL, (char*)NULL);
#if _XOPEN_VERSION < 500
#error
#elif _XOPEN_VERSION >= 700 || defined(PATH_MAX)
char *tmp = realpath((const char*)NULL, (char*)NULL);
#else
#error
#endif
;
return 0;
}

View File

@ -96,23 +96,98 @@ namespace
fs::path
fs::canonical(const path& p, const path& base, error_code& ec)
{
path can;
const path pa = absolute(p, base);
path result;
#ifdef _GLIBCXX_USE_REALPATH
char* buffer = nullptr;
#if defined(__SunOS_5_10) && defined(PATH_MAX)
buffer = (char*)::malloc(PATH_MAX);
#endif
if (char_ptr rp = char_ptr{::realpath(absolute(p, base).c_str(), buffer)})
char_ptr buf{ nullptr };
# if _XOPEN_VERSION < 700
// Not safe to call realpath(path, NULL)
buf.reset( (char*)::malloc(PATH_MAX) );
# endif
if (char* rp = ::realpath(pa.c_str(), buf.get()))
{
can.assign(rp.get());
if (buf == nullptr)
buf.reset(rp);
result.assign(rp);
ec.clear();
return result;
}
if (errno != ENAMETOOLONG)
{
ec.assign(errno, std::generic_category());
return result;
}
else
ec.assign(errno, std::generic_category());
#else
ec = std::make_error_code(std::errc::not_supported);
#endif
return can;
auto fail = [&ec, &result](int e) mutable {
if (!ec.value())
ec.assign(e, std::generic_category());
result.clear();
};
if (!exists(pa, ec))
{
fail(ENOENT);
return result;
}
// else we can assume no unresolvable symlink loops
result = pa.root_path();
deque<path> cmpts;
for (auto& f : pa.relative_path())
cmpts.push_back(f);
while (!cmpts.empty())
{
path f = std::move(cmpts.front());
cmpts.pop_front();
if (f.compare(".") == 0)
{
if (!is_directory(result, ec))
{
fail(ENOTDIR);
break;
}
}
else if (f.compare("..") == 0)
{
auto parent = result.parent_path();
if (parent.empty())
result = pa.root_path();
else
result.swap(parent);
}
else
{
result /= f;
if (is_symlink(result, ec))
{
path link = read_symlink(result, ec);
if (!ec.value())
{
if (link.is_absolute())
{
result = link.root_path();
link = link.relative_path();
}
else
result.remove_filename();
cmpts.insert(cmpts.begin(), link.begin(), link.end());
}
}
if (ec.value() || !exists(result, ec))
{
fail(ENOENT);
break;
}
}
}
return result;
}
fs::path

View File

@ -29,6 +29,8 @@ using std::experimental::filesystem::path;
void
test01()
{
bool test __attribute__((unused)) = false;
for (const path& p : __gnu_test::test_paths)
VERIFY( absolute(p).is_absolute() );
}
@ -36,6 +38,8 @@ test01()
void
test02()
{
bool test __attribute__((unused)) = false;
path p1("/");
VERIFY( absolute(p1) == p1 );
VERIFY( absolute(p1, "/bar") == p1 );

View File

@ -0,0 +1,77 @@
// Copyright (C) 2015 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++11 -lstdc++fs" }
// { dg-require-filesystem-ts "" }
#include <experimental/filesystem>
#include <testsuite_hooks.h>
#include <testsuite_fs.h>
namespace fs = std::experimental::filesystem;
void
test01()
{
bool test __attribute__((unused)) = false;
std::error_code ec;
auto p = __gnu_test::nonexistent_path();
canonical( p, ec );
VERIFY( ec );
p = fs::current_path();
canonical( p, ec );
VERIFY( !ec );
p = "/";
p = canonical( p, ec );
VERIFY( p == "/" );
VERIFY( !ec );
p = "/.";
p = canonical( p, ec );
VERIFY( p == "/" );
VERIFY( !ec );
p = "/..";
p = canonical( p, ec );
VERIFY( p == "/" );
VERIFY( !ec );
p = "/../.././.";
p = canonical( p, ec );
VERIFY( p == "/" );
VERIFY( !ec );
p = "/dev/stdin";
if (exists(p))
{
auto p2 = canonical(p);
if (is_symlink(p))
VERIFY( p != p2 );
else
VERIFY( p == p2 );
VERIFY( canonical(p2) == p2 );
}
}
int
main()
{
test01();
}

View File

@ -29,6 +29,8 @@ using std::experimental::filesystem::path;
void
test01()
{
bool test __attribute__((unused)) = false;
for (const path& p : __gnu_test::test_paths)
VERIFY( absolute(p).is_absolute() );
}
@ -36,6 +38,8 @@ test01()
void
test02()
{
bool test __attribute__((unused)) = false;
path p1("/");
VERIFY( absolute(p1) == p1 );
VERIFY( absolute(p1, "/bar") == p1 );

View File

@ -29,6 +29,8 @@ namespace fs = std::experimental::filesystem;
void
test01()
{
bool test __attribute__((unused)) = false;
fs::path dot(".");
fs::path cwd = fs::current_path();
std::error_code ec;
@ -39,6 +41,8 @@ test01()
void
test02()
{
bool test __attribute__((unused)) = false;
auto oldwd = fs::current_path();
auto tmpdir = fs::temp_directory_path();
current_path(tmpdir);

View File

@ -20,32 +20,37 @@
#include <experimental/filesystem>
#include <testsuite_hooks.h>
#include <testsuite_fs.h>
using std::experimental::filesystem::path;
void
test01()
{
bool test __attribute__((unused)) = false;
VERIFY( exists(path{"/"}) );
VERIFY( exists(path{"/."}) );
VERIFY( exists(path{"."}) );
VERIFY( exists(path{".."}) );
VERIFY( exists(std::experimental::filesystem::current_path()) );
}
void
test02()
{
path rel{"xXxXx"};
while (exists(rel))
rel /= "x";
bool test __attribute__((unused)) = false;
path rel = __gnu_test::nonexistent_path();
VERIFY( !exists(rel) );
}
void
test03()
{
path abs{"/xXxXx"};
while (exists(abs))
abs /= "x";
bool test __attribute__((unused)) = false;
path abs = absolute(__gnu_test::nonexistent_path());
VERIFY( !exists(abs) );
}

View File

@ -27,6 +27,8 @@ namespace fs = std::experimental::filesystem;
void
test01()
{
bool test __attribute__((unused)) = false;
std::error_code ec;
size_t size = fs::file_size(".", ec);
VERIFY( ec == std::errc::is_a_directory );
@ -45,6 +47,8 @@ test01()
void
test02()
{
bool test __attribute__((unused)) = false;
fs::path p = __gnu_test::nonexistent_path();
std::error_code ec;

View File

@ -27,6 +27,8 @@ namespace fs = std::experimental::filesystem;
void
test01()
{
bool test __attribute__((unused)) = false;
std::error_code ec;
fs::file_status st1 = fs::status(".", ec);
VERIFY( !ec );
@ -39,6 +41,8 @@ test01()
void
test02()
{
bool test __attribute__((unused)) = false;
fs::path p = __gnu_test::nonexistent_path();
std::error_code ec;

View File

@ -37,6 +37,8 @@ namespace fs = std::experimental::filesystem;
void
test01()
{
bool test __attribute__((unused)) = false;
clean_env();
if (!fs::exists("/tmp"))
@ -53,6 +55,8 @@ test01()
void
test02()
{
bool test __attribute__((unused)) = false;
clean_env();
if (::setenv("TMPDIR", __gnu_test::nonexistent_path().string().c_str(), 1))