Added link resolution to ProjectPath

This commit is contained in:
Jack Andersen 2015-10-05 15:49:23 -10:00
parent f78aa15980
commit f0325f7946
4 changed files with 128 additions and 14 deletions

View File

@ -467,6 +467,28 @@ public:
*/
const SystemString& getAbsolutePath() const {return m_projRoot;}
/**
* @brief Make absolute path project relative
* @param absPath
* @return
*/
const SystemString getProjectRelativeFromAbsolute(const SystemString& absPath) const
{
if (absPath.size() > m_projRoot.size())
{
if (!absPath.compare(0, m_projRoot.size(), m_projRoot))
{
auto beginIt = absPath.cbegin() + m_projRoot.size();
while (*beginIt == _S('/') || *beginIt == _S('\\'))
++beginIt;
return SystemString(beginIt, absPath.cend());
}
}
LogModule.report(LogVisor::FatalError, "unable to resolve '%s' as project relative '%s'",
absPath.c_str(), m_projRoot.c_str());
return SystemString();
}
/**
* @brief Create directory at path
*
@ -686,7 +708,8 @@ public:
PT_NONE, /**< If path doesn't reference a valid filesystem entity, this is returned */
PT_FILE, /**< Singular file path (confirmed with filesystem) */
PT_DIRECTORY, /**< Singular directory path (confirmed with filesystem) */
PT_GLOB /**< Glob-path (whenever one or more '*' occurs in syntax) */
PT_GLOB, /**< Glob-path (whenever one or more '*' occurs in syntax) */
PT_LINK /**< Link (symlink on POSIX, ShellLink on Windows) */
};
/**
@ -705,6 +728,12 @@ public:
*/
Time getModtime() const;
/**
* @brief For link paths, get the target path
* @return Target path
*/
ProjectPath resolveLink() const;
/**
* @brief Insert directory children into list
* @param outPaths list to append children to

View File

@ -9,5 +9,6 @@
void* memmem(const void *haystack, size_t hlen, const void *needle, size_t nlen);
HRESULT CreateShellLink(LPCWSTR lpszPathObj, LPCWSTR lpszPathLink, LPCWSTR lpszDesc);
HRESULT ResolveShellLink(LPCWSTR lpszLinkFile, LPWSTR lpszPath, int iPathBufferSize);
bool TestShellLink(LPCWSTR lpszLinkFile);
#endif // _HECL_WINSUPPORT_H_

View File

@ -8,15 +8,22 @@ static const SystemRegex regGLOB(_S("\\*"), SystemRegex::ECMAScript|SystemRegex:
static const SystemRegex regPATHCOMP(_S("[/\\\\]*([^/\\\\]+)"), SystemRegex::ECMAScript|SystemRegex::optimize);
static const SystemRegex regDRIVELETTER(_S("^([^/]*)/"), SystemRegex::ECMAScript|SystemRegex::optimize);
static SystemString canonRelPath(const SystemString& path)
static bool IsAbsolute(const SystemString& path)
{
/* Absolute paths not allowed */
if (path[0] == _S('/') || path[0] == _S('\\'))
{
LogModule.report(LogVisor::Error, "Absolute path provided; expected relative: %s", path.c_str());
return _S(".");
}
#if WIN32
if (path.size() && (path[0] == _S('\\') || path[0] == _S('/')))
return true;
if (path.size() >= 2 && iswalpha(path[0]) && path[1] == _S(':'))
return true;
#else
if (path[0] == _S('/'))
return true;
#endif
return false;
}
static SystemString CanonRelPath(const SystemString& path)
{
/* Tokenize Path */
std::vector<SystemString> comps;
HECL::SystemRegexMatch matches;
@ -48,18 +55,29 @@ static SystemString canonRelPath(const SystemString& path)
SystemString retval = *it;
for (++it ; it != comps.end() ; ++it)
{
retval += _S('/');
retval += *it;
if ((*it).size())
{
retval += _S('/');
retval += *it;
}
}
return retval;
}
return _S(".");
}
static SystemString CanonRelPath(const SystemString& path, const ProjectRootPath& projectRoot)
{
/* Absolute paths not allowed; attempt to make project-relative */
if (IsAbsolute(path))
return CanonRelPath(projectRoot.getProjectRelativeFromAbsolute(path));
return CanonRelPath(path);
}
void ProjectPath::assign(Database::Project& project, const SystemString& path)
{
m_proj = &project;
m_relPath = canonRelPath(path);
m_relPath = CanonRelPath(path);
m_absPath = project.getProjectRootPath().getAbsolutePath() + _S('/') + m_relPath;
SanitizePath(m_relPath);
SanitizePath(m_absPath);
@ -89,7 +107,7 @@ void ProjectPath::assign(Database::Project& project, const std::string& path)
void ProjectPath::assign(const ProjectPath& parentPath, const SystemString& path)
{
m_proj = parentPath.m_proj;
m_relPath = canonRelPath(parentPath.m_relPath + _S('/') + path);
m_relPath = CanonRelPath(parentPath.m_relPath + _S('/') + path);
m_absPath = m_proj->getProjectRootPath().getAbsolutePath() + _S('/') + m_relPath;
SanitizePath(m_relPath);
SanitizePath(m_absPath);
@ -124,6 +142,16 @@ ProjectPath ProjectPath::getCookedPath(const Database::DataSpecEntry& spec) cons
ProjectPath::PathType ProjectPath::getPathType() const
{
#if WIN32
if (TestShellLink(m_absPath.c_str()))
return PT_LINK;
#else
HECL::Sstat lnStat;
if (lstat(m_absPath.c_str(), &lnStat))
return PT_NONE;
if (S_ISLNK(lnStat.st_mode))
return PT_LINK;
#endif
if (std::regex_search(m_absPath, regGLOB))
return PT_GLOB;
Sstat theStat;
@ -184,6 +212,22 @@ Time ProjectPath::getModtime() const
return Time();
}
ProjectPath ProjectPath::resolveLink() const
{
#if WIN32
wchar_t target[2048];
if (FAILED(ResolveShellLink(m_absPath.c_str(), target, 2048)))
LogModule.report(LogVisor::FatalError, _S("unable to resolve link '%s'"), m_absPath.c_str());
#else
char target[2048];
ssize_t targetSz;
if ((targetSz = readlink(m_absPath.c_str(), target, 2048)) < 0)
LogModule.report(LogVisor::FatalError, _S("unable to resolve link '%s': %s"), m_absPath.c_str(), strerror(errno));
target[targetSz] = '\0';
#endif
return ProjectPath(*this, target);
}
static void _recursiveGlob(Database::Project& proj,
std::vector<ProjectPath>& outPaths,
size_t level,

View File

@ -106,8 +106,6 @@ HRESULT ResolveShellLink(LPCWSTR lpszLinkFile, LPWSTR lpszPath, int iPathBufferS
*lpszPath = 0; // Assume failure
// Get a pointer to the IShellLink interface. It is assumed that CoInitialize
// has already been called.
hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl);
if (SUCCEEDED(hres))
{
@ -157,4 +155,46 @@ HRESULT ResolveShellLink(LPCWSTR lpszLinkFile, LPWSTR lpszPath, int iPathBufferS
return hres;
}
bool TestShellLink(LPCWSTR lpszLinkFile)
{
HRESULT hres;
IShellLink* psl;
hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl);
if (SUCCEEDED(hres))
{
IPersistFile* ppf;
// Get a pointer to the IPersistFile interface.
hres = psl->QueryInterface(IID_IPersistFile, (void**)&ppf);
if (SUCCEEDED(hres))
{
// Load the shortcut.
hres = ppf->Load(lpszLinkFile, STGM_READ);
if (SUCCEEDED(hres))
{
// Resolve the link.
HWND hwnd = GetConsoleWindow();
if (!hwnd)
hwnd = GetTopWindow(nullptr);
hres = psl->Resolve(hwnd, 0);
if (SUCCEEDED(hres))
{
return true;
}
}
// Release the pointer to the IPersistFile interface.
ppf->Release();
}
// Release the pointer to the IShellLink interface.
psl->Release();
}
return false;
}