mitsuba/src/libcore/fresolver.cpp

182 lines
4.8 KiB
C++

#include <mitsuba/core/fresolver.h>
#include <boost/algorithm/string.hpp>
#if defined(__LINUX__)
# if !defined(_GNU_SOURCE)
# define _GNU_SOURCE
# endif
# include <dlfcn.h>
#elif defined(__OSX__)
# include <mach-o/dyld.h>
#elif defined(__WINDOWS__)
# include <windows.h>
# include <vector>
#endif
MTS_NAMESPACE_BEGIN
#if defined(__WINDOWS__) || defined(__LINUX__)
namespace {
void dummySymbol() { }
}
#endif
FileResolver::FileResolver() {
/* Try to detect the base path of the Mitsuba installation */
fs::path basePath;
#if defined(__LINUX__)
Dl_info info;
dladdr((const void *) &dummySymbol, &info);
if (info.dli_fname) {
/* Try to detect a few default setups */
if (boost::starts_with(info.dli_fname, "/usr/lib") ||
boost::starts_with(info.dli_fname, "/lib")) {
basePath = fs::path("/usr/share/mitsuba");
} else if (boost::starts_with(info.dli_fname, "/usr/local/lib")) {
basePath = fs::path("/usr/local/share/mitsuba");
} else {
/* This is a locally-compiled repository */
basePath = fs::path(info.dli_fname).parent_path();
}
}
#elif defined(__OSX__)
MTS_AUTORELEASE_BEGIN()
uint32_t imageCount = _dyld_image_count();
for (uint32_t i=0; i<imageCount; ++i) {
const char *imageName = _dyld_get_image_name(i);
if (boost::ends_with(imageName, "libmitsuba-core.dylib")) {
basePath = fs::canonical(imageName).parent_path().parent_path().parent_path();
break;
}
}
MTS_AUTORELEASE_END()
if (basePath.empty())
Log(EError, "Could not detect the executable path!");
#elif defined(__WINDOWS__)
std::vector<WCHAR> lpFilename(MAX_PATH);
// Module handle to this DLL. If the function fails it sets handle to NULL.
// In that case GetModuleFileName will get the name of the executable which
// is acceptable soft-failure behavior.
HMODULE handle;
GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
| GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
reinterpret_cast<LPCWSTR>(&dummySymbol), &handle);
// Try to get the path with the default MAX_PATH length (260 chars)
DWORD nSize = GetModuleFileNameW(handle, &lpFilename[0], MAX_PATH);
// Adjust the buffer size in case if was too short
while (nSize != 0 && nSize == lpFilename.size()) {
lpFilename.resize(nSize * 2);
nSize = GetModuleFileNameW(handle, &lpFilename[0],
static_cast<DWORD>(lpFilename.size()));
}
// There is an error if and only if the function returns 0
if (nSize != 0)
basePath = fs::path(lpFilename).parent_path();
else
Log(EError, "Could not detect the executable path! (%s)", lastErrorText().c_str());
#endif
#if BOOST_VERSION >= 104800
m_paths.push_back(fs::canonical(basePath));
#else
m_paths.push_back(fs::absolute(basePath));
#endif
m_paths.push_back(fs::current_path());
}
FileResolver *FileResolver::clone() const {
FileResolver *cloned = new FileResolver();
cloned->m_paths = m_paths;
return cloned;
}
void FileResolver::clear() {
m_paths.clear();
}
void FileResolver::prependPath(const fs::path &path) {
for (size_t i=0; i<m_paths.size(); ++i) {
if (m_paths[i] == path)
return;
}
m_paths.push_front(path);
}
void FileResolver::appendPath(const fs::path &path) {
for (size_t i=0; i<m_paths.size(); ++i) {
if (m_paths[i] == path)
return;
}
m_paths.push_back(path);
}
fs::path FileResolver::resolve(const fs::path &path) const {
/* First, try to resolve in case-sensitive mode */
for (size_t i=0; i<m_paths.size(); i++) {
fs::path newPath = m_paths[i] / path;
if (fs::exists(newPath))
return newPath;
}
#if defined(__LINUX__)
/* On Linux, also try case-insensitive mode if the above failed */
fs::path parentPath = path.parent_path();
std::string filename = boost::to_lower_copy(path.filename().string());
for (size_t i=0; i<m_paths.size(); i++) {
fs::path path = m_paths[i] / parentPath;
if (!fs::is_directory(path))
continue;
fs::directory_iterator end, it(path);
for (; it != end; ++it) {
if (boost::algorithm::to_lower_copy(it->path().filename().string()) == filename)
return it->path();
}
}
#endif
return path;
}
std::vector<fs::path> FileResolver::resolveAll(const fs::path &path) const {
std::vector<fs::path> results;
for (size_t i=0; i<m_paths.size(); i++) {
fs::path newPath = m_paths[i] / path;
if (fs::exists(newPath))
results.push_back(newPath);
}
return results;
}
fs::path FileResolver::resolveAbsolute(const fs::path &path) const {
return fs::absolute(resolve(path));
}
std::string FileResolver::toString() const {
std::ostringstream oss;
oss << "FileResolver[" << endl
<< " paths = {" << endl;
for (size_t i=0; i<m_paths.size(); ++i) {
oss << " \"" << m_paths[i].string() << "\"";
if (i+1 < m_paths.size())
oss << ",";
oss << endl;
}
oss << " }" << endl
<< "]";
return oss.str();
}
MTS_IMPLEMENT_CLASS(FileResolver, false, Object)
MTS_NAMESPACE_END