DYT/Tool/matlab/include/MatlabCppSharedLib/detail/cppsharedlib_ctf_path.hpp
2024-11-22 23:19:31 +08:00

338 lines
11 KiB
C++

/* Copyright 2017 The MathWorks, Inc. */
#ifndef CPPSHAREDLIB_CTF_PATH_HPP
#define CPPSHAREDLIB_CTF_PATH_HPP
#include "cppsharedlib_path_init.hpp"
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#else
#include <stdio.h>
#include <unistd.h>
#ifdef __APPLE__
#include <mach-o/dyld.h>
#endif
#endif
#include <algorithm>
#include <iostream>
#include <string>
namespace matlab {
namespace cpplib {
namespace detail {
// An environment variable name must be ASCII, so it can be stored in an std::string.
const std::string BASE_CTF_PATH_ENV_VAR_NAME = "CPPSHARED_BASE_CTF_PATH";
const std::u16string BASE_CTF_PATH_ENV_VAR_NAME_U{
BASE_CTF_PATH_ENV_VAR_NAME.cbegin(), BASE_CTF_PATH_ENV_VAR_NAME.cend()};
const char WINDOWS_PATH_SEP_CH = '\\';
const char COLON_CH = ':';
const char UNIX_PATH_SEP_CH = '/';
const char TILDE_CH = '~';
template<typename CharT>
inline void appendPathSepIfMissing(std::basic_string<CharT> & strPath, CharT pathSep)
{
if (strPath.empty())
{
strPath.append(1, pathSep);
}
else
{
CharT lastChar = strPath[strPath.length() - 1];
if (lastChar != static_cast<CharT>(WINDOWS_PATH_SEP_CH) &&
lastChar != static_cast<CharT>(UNIX_PATH_SEP_CH))
{
strPath.append(1, pathSep);
}
}
}
template<typename CharT>
inline bool isAbsolute(const std::basic_string<CharT> & strPath)
{
// The path to a CTF should always be greater than 2 characters.
return (strPath.length() > 2 &&
(strPath[0] == static_cast<CharT>(WINDOWS_PATH_SEP_CH) ||
strPath[0] == static_cast<CharT>(UNIX_PATH_SEP_CH) ||
strPath[0] == static_cast<CharT>(TILDE_CH) ||
strPath[1] == static_cast<CharT>(COLON_CH)));
}
#ifdef _WIN32
const wchar_t WIN_PATH_SEP_W = L'\\';
const wchar_t * const PATH_SEPS_W = L"/\\";
const std::wstring BASE_CTF_PATH_ENV_VAR_NAME_W{BASE_CTF_PATH_ENV_VAR_NAME.cbegin(),
BASE_CTF_PATH_ENV_VAR_NAME.cend()};
// In the names of the following functions, the suffix "W" can be read as referring
// to either "Windows" or "wchar_t" (or "wstring").
inline std::wstring getPathToExecutableW()
{
// There's no guarantee that an ordinary path will fit within MAX_PATH. If it
// doesn't fit, we iteratively double the buffer size up to 7 times (which
// should be more than enough).
static const size_t INITIAL_BUFFER_SIZE = MAX_PATH;
static const size_t MAX_ITERATIONS = 7;
std::wstring ret;
DWORD bufferSize = INITIAL_BUFFER_SIZE;
for (size_t iterations = 0; iterations < MAX_ITERATIONS; ++iterations)
{
ret.resize(bufferSize);
DWORD charsReturned = GetModuleFileNameW(NULL, &ret[0], bufferSize);
if (charsReturned < ret.length())
{
ret.resize(charsReturned);
return ret;
}
else
{
bufferSize *= 2;
}
}
return L"";
}
// Path will end with a path separator (forward slash or backslash).
// This differs from UNIX, where only forward slashes are treated
// as path separators and backslashes are treated as literal characters.
inline std::wstring getBaseCtfPathFromExecutableW()
{
std::wstring ret = getPathToExecutableW();
size_t lastPathSep = ret.find_last_of(PATH_SEPS_W);
return ret.substr(0, lastPathSep+1);
}
inline std::wstring getBaseCtfPathFromEnvVarW()
{
return getValueFromEnvVarW(BASE_CTF_PATH_ENV_VAR_NAME_W.c_str());
}
inline std::wstring getWorkingDirW()
{
DWORD bufferSize = GetCurrentDirectoryW(0, NULL);
std::wstring strPath(bufferSize, wchar_t(0));
GetCurrentDirectoryW(bufferSize, &strPath[0]);
return strPath.substr(0, bufferSize-1);
}
inline std::wstring getPathToCtfW(const std::wstring & ctfAsSpecifiedToInitFunc)
{
if (isAbsolute(ctfAsSpecifiedToInitFunc))
{
if (fileExistsW(ctfAsSpecifiedToInitFunc))
{
return ctfAsSpecifiedToInitFunc;
}
else
{
return std::wstring();
}
}
else
{
std::wstring strPath = getBaseCtfPathFromEnvVarW();
if (fileExistsInDirW(ctfAsSpecifiedToInitFunc, strPath))
{
appendPathSepIfMissing(strPath, WIN_PATH_SEP_W);
strPath += ctfAsSpecifiedToInitFunc;
return strPath;
}
else
{
strPath = getWorkingDirW();
if (fileExistsInDirW(ctfAsSpecifiedToInitFunc, strPath))
{
appendPathSepIfMissing(strPath, WIN_PATH_SEP_W);
strPath += ctfAsSpecifiedToInitFunc;
return strPath;
}
else
{
strPath = getBaseCtfPathFromExecutableW();
if (fileExistsInDirW(ctfAsSpecifiedToInitFunc, strPath))
{
appendPathSepIfMissing(strPath, WIN_PATH_SEP_W);
strPath += ctfAsSpecifiedToInitFunc;
return strPath;
}
else
{
return std::wstring();
}
}
}
}
}
inline std::u16string getWorkingDir()
{
std::wstring wstr = getWorkingDirW();
return std::u16string(wstr.cbegin(), wstr.cend());
}
inline std::u16string getPathToCtf(const std::u16string & ctfAsSpecifiedToInitFunc)
{
std::wstring wstrCtfAsSpecifiedToInitFunc(ctfAsSpecifiedToInitFunc.cbegin(),
ctfAsSpecifiedToInitFunc.cend());
const std::wstring wstrRet = getPathToCtfW(wstrCtfAsSpecifiedToInitFunc);
return std::u16string(wstrRet.cbegin(), wstrRet.cend());
}
#else // non-Windows code follows
const char16_t UNIX_PATH_SEP_U = static_cast<char16_t>(UNIX_PATH_SEP_CH);
inline std::u16string getWorkingDir()
{
char buf[FILENAME_MAX];
const char * workingDir = getcwd(buf, FILENAME_MAX);
if (!workingDir)
{
return std::u16string();
}
else
{
return std::u16string(buf, buf + strlen(buf));
}
}
inline std::u16string getBaseCtfPathFromEnvVar()
{
return getValueFromEnvVar(BASE_CTF_PATH_ENV_VAR_NAME_U);
}
inline std::u16string getPathToExecutable()
{
char pBuf[FILENAME_MAX];
#ifdef __linux__
int bytes = std::min(readlink("/proc/self/exe", pBuf, FILENAME_MAX), static_cast<ssize_t>(FILENAME_MAX - 1));
if (bytes >= 0)
{
pBuf[bytes] = '\0';
}
return std::u16string(pBuf, pBuf+bytes);
#else
uint32_t size = sizeof(pBuf);
if (_NSGetExecutablePath(pBuf, &size) == 0)
{
// realpath allocates a buffer using malloc. Use a unique_ptr to ensure that it gets freed
// even if convertUTF8StringToUTF16String() throws an exception.
std::unique_ptr<char, std::function<void(char*)>> ptrCanonical(
realpath(pBuf, NULL), [](char * ptr){free(ptr);});
std::u16string ret = matlab::execution::convertUTF8StringToUTF16String(ptrCanonical.get());
return ret;
}
else
{
return std::u16string();
}
#endif
}
// Path will end with a path separator (forward slash only).
inline std::u16string getBaseCtfPathFromExecutable(const std::u16string & pathToExecutable)
{
size_t lastPathSep = pathToExecutable.find_last_of(UNIX_PATH_SEP_U);
return pathToExecutable.substr(0, lastPathSep+1);
}
#ifdef __APPLE__
const std::u16string CONTENTS_MAC_OS = u".app/Contents/MacOS";
// Go up three directories. For example, if your executable is
// generic_interface/foo_generic.app/Contents/MacOS/foo, look for the
// last directory separator prior to ".app/Contents/MacOS" and
// terminate the string there.
inline std::u16string getPathOutsideBundle(const std::u16string & pathToExecutable)
{
size_t lastPathSep = pathToExecutable.rfind(CONTENTS_MAC_OS);
if (lastPathSep != std::string::npos && lastPathSep != 0)
{
lastPathSep = pathToExecutable.rfind(UNIX_PATH_SEP_U, lastPathSep-1);
if (lastPathSep != std::string::npos)
{
return pathToExecutable.substr(0, lastPathSep+1);
}
}
return std::u16string();
}
#endif
// The code in this function is similar, but not identical, to the code
// in getPathToCtfW(), which is called by the Windows-defined version of
// getPathToCtf(). While it seems as though the code could be shared, in
// reality, this would involve having to either (a) write template versions of
// lower-level functions that are platform-specific anyway or (b) perform
// unnecessary conversions between strings of different types.
inline std::u16string getPathToCtf(const std::u16string & ctfAsSpecifiedToInitFunc)
{
if (isAbsolute(ctfAsSpecifiedToInitFunc))
{
if (fileExists(ctfAsSpecifiedToInitFunc))
{
return ctfAsSpecifiedToInitFunc;
}
else
{
return std::u16string();
}
}
else
{
std::u16string strPath = getBaseCtfPathFromEnvVar();
if (fileExistsInDir(ctfAsSpecifiedToInitFunc, strPath))
{
appendPathSepIfMissing(strPath, UNIX_PATH_SEP_U);
strPath += ctfAsSpecifiedToInitFunc;
return strPath;
}
else
{
strPath = getWorkingDir();
if (fileExistsInDir(ctfAsSpecifiedToInitFunc, strPath))
{
appendPathSepIfMissing(strPath, UNIX_PATH_SEP_U);
strPath += ctfAsSpecifiedToInitFunc;
return strPath;
}
else
{
std::u16string pathToExecutable = getPathToExecutable();
strPath = getBaseCtfPathFromExecutable(pathToExecutable);
if (fileExistsInDir(ctfAsSpecifiedToInitFunc, strPath))
{
appendPathSepIfMissing(strPath, UNIX_PATH_SEP_U);
strPath += ctfAsSpecifiedToInitFunc;
return strPath;
}
else
{
#ifdef __APPLE__
strPath = getPathOutsideBundle(pathToExecutable);
if (fileExistsInDir(ctfAsSpecifiedToInitFunc, strPath))
{
appendPathSepIfMissing(strPath, UNIX_PATH_SEP_U);
strPath += ctfAsSpecifiedToInitFunc;
return strPath;
}
#endif
return std::u16string();
}
}
}
}
}
#endif
}}} // end of nested namespace
#endif // CPPSHARED_CTF_PATH_HPP