Simplify sys_get_executable_name by using realpath

This was SVN commit r7104.
This commit is contained in:
Ykkrosh 2009-08-09 20:28:46 +00:00
parent 2114bf6795
commit 43950c2fde
6 changed files with 110 additions and 128 deletions

View File

@ -22,94 +22,7 @@
#define GNU_SOURCE
#include "mocks/dlfcn.h"
#include "mocks/boost_filesystem.h"
// TODO: This normalization code is copied from boost::filesystem,
// where it is deprecated, presumably for good reasons (probably
// because symlinks mean "x/../y" != "y" in general), and this file
// is not an appropriate place for the code anyway, so it should be
// rewritten or removed or something. (Also, the const_cast is evil.)
namespace boost { namespace filesystem {
// Derived from boost/filesystem/path.hpp:
// "Copyright Beman Dawes 2002-2005
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)"
template<class String, class Traits>
basic_path<String, Traits> normalize_COPIED(const basic_path<String, Traits> &self)
{
typedef basic_path<String, Traits> path_type;
typedef typename path_type::string_type string_type;
typedef typename path_type::iterator iterator;
static const typename string_type::value_type dot_str[]
= { dot<path>::value, 0 };
if ( self.empty() ) return self;
path temp;
iterator start( self.begin() );
iterator last( self.end() );
iterator stop( last-- );
for ( iterator itr( start ); itr != stop; ++itr )
{
// ignore "." except at start and last
if ( itr->size() == 1
&& (*itr)[0] == dot<path_type>::value
&& itr != start
&& itr != last ) continue;
// ignore a name and following ".."
if ( !temp.empty()
&& itr->size() == 2
&& (*itr)[0] == dot<path_type>::value
&& (*itr)[1] == dot<path_type>::value ) // dot dot
{
string_type lf( temp.leaf() );
if ( lf.size() > 0
&& (lf.size() != 1
|| (lf[0] != dot<path_type>::value
&& lf[0] != slash<path_type>::value))
&& (lf.size() != 2
|| (lf[0] != dot<path_type>::value
&& lf[1] != dot<path_type>::value
# ifdef BOOST_WINDOWS_PATH
&& lf[1] != colon<path_type>::value
# endif
)
)
)
{
temp.remove_leaf();
// if not root directory, must also remove "/" if any
if ( temp.string().size() > 0
&& temp.string()[temp.string().size()-1]
== slash<path_type>::value )
{
typename string_type::size_type rds(
detail::root_directory_start<String,Traits>( temp.string(),
temp.string().size() ) );
if ( rds == string_type::npos
|| rds != temp.string().size()-1 )
{ const_cast<String&>( temp.string() ).erase( temp.string().size()-1 ); }
}
iterator next( itr );
if ( temp.empty() && ++next != stop
&& next == last && *last == dot_str ) temp /= dot_str;
continue;
}
}
temp /= *itr;
};
if ( temp.empty() ) temp /= dot_str;
return temp;
}
} }
#include "mocks/unistd.h"
LibError sys_get_executable_name(char* n_path, size_t max_chars)
{
@ -125,12 +38,33 @@ LibError sys_get_executable_name(char* n_path, size_t max_chars)
}
path = dl_info.dli_fname;
// If this looks like a relative path, resolve against cwd.
// If this looks like an absolute path, we still need to normalize it.
// If this looks like an absolute path, use realpath to get the normalized
// path (with no '.' or '..')
if (path[0] == '/') {
char* resolved = realpath(path, NULL);
if (!resolved)
return ERR::NO_SYS;
strncpy(n_path, resolved, max_chars);
free(resolved);
return INFO::OK;
}
// If this looks like a relative path, resolve against cwd and use realpath
if (strchr(path, '/')) {
fs::path p = fs::complete(fs::path(path), T::Boost_Filesystem_initial_path());
fs::path n = fs::normalize_COPIED(p);
strncpy(n_path, n.string().c_str(), max_chars);
char cwd[PATH_MAX];
if (!T::getcwd(cwd, PATH_MAX))
return ERR::NO_SYS;
char absolute[PATH_MAX];
int ret = snprintf(absolute, PATH_MAX, "%s/%s", cwd, path);
if (ret < 0 || ret >= PATH_MAX)
return ERR::NO_SYS; // path too long, or other error
char* resolved = realpath(absolute, NULL);
if (!resolved)
return ERR::NO_SYS;
strncpy(n_path, resolved, max_chars);
free(resolved);
return INFO::OK;
}

View File

@ -23,7 +23,7 @@
#if OS_LINUX
# include "mocks/dlfcn.h"
# include "mocks/boost_filesystem.h"
# include "mocks/unistd.h"
#endif
#include <cxxtest/PsTestWrapper.h>
@ -94,36 +94,73 @@ public:
// implementations of sys_get_executable_name:
#if OS_LINUX
// Since the implementation uses realpath, the tested files need to
// really exist. So set up a directory tree for testing:
const char* tmpdir = getenv("TMPDIR");
if (! tmpdir) tmpdir = P_tmpdir;
char root[PATH_MAX];
sprintf_s(root, PATH_MAX, "%s/pyrogenesis-test-sysdep-XXXXXX", tmpdir);
TS_ASSERT(mkdtemp(root));
std::string rootstr(root);
const char* dirs[] = {
"/example",
"/example/a",
"/example/a/b",
"/example/a/b/c",
"/example/a/b/d",
"/example/a/e",
"/example/a/f"
};
const char* files[] = {
"/example/executable",
"/example/a/f/executable",
};
for (size_t i = 0; i < ARRAY_SIZE(dirs); ++i)
{
std::string name = rootstr + dirs[i];
TS_ASSERT_EQUALS(mkdir(name.c_str(), 0700), 0);
}
for (size_t i = 0; i < ARRAY_SIZE(files); ++i)
{
std::string name = rootstr + files[i];
FILE* f = fopen(name.c_str(), "w");
TS_ASSERT(f);
fclose(f);
}
// Try with absolute paths
{
Mock_dladdr d("/example/executable");
Mock_dladdr d(rootstr+"/example/executable");
TS_ASSERT_EQUALS(sys_get_executable_name(path, PATH_MAX), INFO::OK);
TS_ASSERT_STR_EQUALS(path, "/example/executable");
TS_ASSERT_STR_EQUALS(path, rootstr+"/example/executable");
}
{
Mock_dladdr d("/example/./a/b/../c/../../executable");
Mock_dladdr d(rootstr+"/example/./a/b/../e/../../executable");
TS_ASSERT_EQUALS(sys_get_executable_name(path, PATH_MAX), INFO::OK);
TS_ASSERT_STR_EQUALS(path, "/example/executable");
TS_ASSERT_STR_EQUALS(path, rootstr+"/example/executable");
}
// Try with relative paths
{
Mock_dladdr d("./executable");
Mock_initial_path m("/example");
Mock_getcwd m(rootstr+"/example");
TS_ASSERT_EQUALS(sys_get_executable_name(path, PATH_MAX), INFO::OK);
TS_ASSERT_STR_EQUALS(path, "/example/executable");
TS_ASSERT_STR_EQUALS(path, rootstr+"/example/executable");
}
{
Mock_dladdr d("./executable");
Mock_initial_path m("/example/");
Mock_getcwd m(rootstr+"/example/");
TS_ASSERT_EQUALS(sys_get_executable_name(path, PATH_MAX), INFO::OK);
TS_ASSERT_STR_EQUALS(path, "/example/executable");
TS_ASSERT_STR_EQUALS(path, rootstr+"/example/executable");
}
{
Mock_dladdr d("../d/../../e/executable");
Mock_initial_path m("/example/a/b/c");
Mock_dladdr d("../d/../../f/executable");
Mock_getcwd m(rootstr+"/example/a/b/c");
TS_ASSERT_EQUALS(sys_get_executable_name(path, PATH_MAX), INFO::OK);
TS_ASSERT_STR_EQUALS(path, "/example/a/e/executable");
TS_ASSERT_STR_EQUALS(path, rootstr+"/example/a/f/executable");
}
// Try with pathless names
@ -131,6 +168,20 @@ public:
Mock_dladdr d("executable");
TS_ASSERT_EQUALS(sys_get_executable_name(path, PATH_MAX), ERR::NO_SYS);
}
// Clean up the temporary files
for (size_t i = 0; i < ARRAY_SIZE(files); ++i)
{
std::string name = rootstr + files[i];
TS_ASSERT_EQUALS(unlink(name.c_str()), 0);
}
for (ssize_t i = ARRAY_SIZE(dirs)-1; i >= 0; --i) // reverse order
{
std::string name(root);
name += dirs[i];
TS_ASSERT_EQUALS(rmdir(name.c_str()), 0);
}
TS_ASSERT_EQUALS(rmdir(root), 0);
#endif // OS_LINUX
}
@ -139,24 +190,25 @@ public:
class Mock_dladdr : public T::Base_dladdr
{
public:
Mock_dladdr(const char* fname) : fname_(fname) { }
Mock_dladdr(const std::string& fname) : fname_(fname) { }
int dladdr(void *UNUSED(addr), Dl_info *info) {
info->dli_fname = fname_;
info->dli_fname = fname_.c_str();
return 1;
}
private:
const char* fname_;
std::string fname_;
};
class Mock_initial_path : public T::Base_Boost_Filesystem_initial_path
class Mock_getcwd : public T::Base_getcwd
{
public:
Mock_initial_path(const char* buf) : buf_(buf) { }
fs::path Boost_Filesystem_initial_path() {
return fs::path(buf_);
Mock_getcwd(const std::string& buf) : buf_(buf) { }
char* getcwd(char* buf, size_t size) {
strncpy_s(buf, size, buf_.c_str(), buf_.length());
return buf;
}
private:
const char* buf_;
std::string buf_;
};
#endif

View File

@ -1,10 +0,0 @@
#include "lib/external_libraries/boost_filesystem.h"
#include <cxxtest/Mock.h>
CXXTEST_MOCK(
Boost_Filesystem_initial_path,
boost::filesystem::path,
Boost_Filesystem_initial_path,
(),
boost::filesystem::initial_path,
()
);

View File

@ -8,8 +8,7 @@
#endif
#include "lib/precompiled.h"
#include "mocks/boost_filesystem.h"
#if OS_LINUX
#include "mocks/dlfcn.h"
#include "mocks/unistd.h"
#endif // OS_LINUX

View File

@ -11,12 +11,12 @@
// Cause calls to be redirected to the real function by default
#define DEFAULT(name) static T::Real_##name real_##name
#include "mocks/boost_filesystem.h"
DEFAULT(Boost_Filesystem_initial_path);
#if OS_LINUX
#include "mocks/dlfcn.h"
DEFAULT(dladdr);
#include "mocks/unistd.h"
DEFAULT(getcwd);
#endif // OS_LINUX

7
source/mocks/unistd.h Normal file
View File

@ -0,0 +1,7 @@
#include <unistd.h>
#include <cxxtest/Mock.h>
CXXTEST_MOCK_GLOBAL(
char *, getcwd,
(char *buf, size_t size),
(buf, size)
);