diff --git a/source/lib/sysdep/os/linux/linux.cpp b/source/lib/sysdep/os/linux/linux.cpp index da44a65681..d9f88ed246 100644 --- a/source/lib/sysdep/os/linux/linux.cpp +++ b/source/lib/sysdep/os/linux/linux.cpp @@ -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 - basic_path normalize_COPIED(const basic_path &self) - { - typedef basic_path 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::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::value - && itr != start - && itr != last ) continue; - - // ignore a name and following ".." - if ( !temp.empty() - && itr->size() == 2 - && (*itr)[0] == dot::value - && (*itr)[1] == dot::value ) // dot dot - { - string_type lf( temp.leaf() ); - if ( lf.size() > 0 - && (lf.size() != 1 - || (lf[0] != dot::value - && lf[0] != slash::value)) - && (lf.size() != 2 - || (lf[0] != dot::value - && lf[1] != dot::value -# ifdef BOOST_WINDOWS_PATH - && lf[1] != colon::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::value ) - { - typename string_type::size_type rds( - detail::root_directory_start( temp.string(), - temp.string().size() ) ); - if ( rds == string_type::npos - || rds != temp.string().size()-1 ) - { const_cast( 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; } diff --git a/source/lib/sysdep/tests/test_sysdep.h b/source/lib/sysdep/tests/test_sysdep.h index ea3f473296..897a9ba214 100644 --- a/source/lib/sysdep/tests/test_sysdep.h +++ b/source/lib/sysdep/tests/test_sysdep.h @@ -23,7 +23,7 @@ #if OS_LINUX # include "mocks/dlfcn.h" -# include "mocks/boost_filesystem.h" +# include "mocks/unistd.h" #endif #include @@ -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 diff --git a/source/mocks/boost_filesystem.h b/source/mocks/boost_filesystem.h deleted file mode 100644 index 770e26be0e..0000000000 --- a/source/mocks/boost_filesystem.h +++ /dev/null @@ -1,10 +0,0 @@ -#include "lib/external_libraries/boost_filesystem.h" -#include -CXXTEST_MOCK( - Boost_Filesystem_initial_path, - boost::filesystem::path, - Boost_Filesystem_initial_path, - (), - boost::filesystem::initial_path, - () -); diff --git a/source/mocks/mocks_real.cpp b/source/mocks/mocks_real.cpp index ed18bf52c1..1d181afe0a 100644 --- a/source/mocks/mocks_real.cpp +++ b/source/mocks/mocks_real.cpp @@ -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 diff --git a/source/mocks/mocks_test.cpp b/source/mocks/mocks_test.cpp index 7e038fcf9f..c72c4b579f 100644 --- a/source/mocks/mocks_test.cpp +++ b/source/mocks/mocks_test.cpp @@ -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 diff --git a/source/mocks/unistd.h b/source/mocks/unistd.h new file mode 100644 index 0000000000..b252952644 --- /dev/null +++ b/source/mocks/unistd.h @@ -0,0 +1,7 @@ +#include +#include +CXXTEST_MOCK_GLOBAL( + char *, getcwd, + (char *buf, size_t size), + (buf, size) +);