metaforce/hecl/lib/ProjectPath.cpp

346 lines
11 KiB
C++
Raw Normal View History

2016-03-04 23:02:44 +00:00
#include "hecl/hecl.hpp"
#include "hecl/Database.hpp"
2015-06-09 22:19:59 +00:00
#include <regex>
2016-03-04 23:02:44 +00:00
namespace hecl
2015-06-09 22:19:59 +00:00
{
2015-07-18 04:35:01 +00:00
static const SystemRegex regPATHCOMP(_S("[/\\\\]*([^/\\\\]+)"), SystemRegex::ECMAScript|SystemRegex::optimize);
2015-06-09 22:19:59 +00:00
static const SystemRegex regDRIVELETTER(_S("^([^/]*)/"), SystemRegex::ECMAScript|SystemRegex::optimize);
2017-11-13 06:13:53 +00:00
static SystemString CanonRelPath(SystemStringView path)
2015-10-06 01:49:23 +00:00
{
2015-07-18 04:35:01 +00:00
/* Tokenize Path */
std::vector<SystemString> comps;
2016-03-04 23:02:44 +00:00
hecl::SystemRegexMatch matches;
2017-11-13 06:13:53 +00:00
SystemString in(path);
2015-08-05 01:54:35 +00:00
SanitizePath(in);
2015-07-19 20:52:22 +00:00
for (; std::regex_search(in, matches, regPATHCOMP) ; in = matches.suffix())
2015-06-09 22:19:59 +00:00
{
2015-07-18 04:35:01 +00:00
const SystemString& match = matches[1];
if (!match.compare(_S(".")))
continue;
else if (!match.compare(_S("..")))
2015-07-16 02:03:38 +00:00
{
2015-07-18 04:35:01 +00:00
if (comps.empty())
{
/* Unable to resolve outside project */
2017-11-13 06:13:53 +00:00
LogModule.report(logvisor::Fatal, _S("Unable to resolve outside project root in %s"), path.data());
2015-07-18 04:35:01 +00:00
return _S(".");
}
comps.pop_back();
continue;
2015-07-16 02:03:38 +00:00
}
2015-07-18 04:35:01 +00:00
comps.push_back(match);
2015-06-09 22:19:59 +00:00
}
2015-07-18 04:35:01 +00:00
/* Emit relative path */
if (comps.size())
{
auto it = comps.begin();
SystemString retval = *it;
for (++it ; it != comps.end() ; ++it)
{
2015-10-06 01:49:23 +00:00
if ((*it).size())
{
retval += _S('/');
retval += *it;
}
2015-07-18 04:35:01 +00:00
}
return retval;
}
2015-07-22 19:14:50 +00:00
return _S(".");
2015-06-09 22:19:59 +00:00
}
2017-11-13 06:13:53 +00:00
static SystemString CanonRelPath(SystemStringView path, const ProjectRootPath& projectRoot)
2015-10-06 01:49:23 +00:00
{
/* Absolute paths not allowed; attempt to make project-relative */
if (IsAbsolute(path))
return CanonRelPath(projectRoot.getProjectRelativeFromAbsolute(path));
return CanonRelPath(path);
}
2017-11-13 06:13:53 +00:00
void ProjectPath::assign(Database::Project& project, SystemStringView path)
2015-09-29 21:50:07 +00:00
{
2015-09-30 06:23:07 +00:00
m_proj = &project;
2016-04-05 01:49:42 +00:00
SystemString usePath;
size_t pipeFind = path.rfind(_S('|'));
if (pipeFind != SystemString::npos)
{
m_auxInfo.assign(path.cbegin() + pipeFind + 1, path.cend());
usePath.assign(path.cbegin(), path.cbegin() + pipeFind);
}
else
usePath = path;
2016-09-21 05:41:06 +00:00
m_relPath = CanonRelPath(usePath, project.getProjectRootPath());
2017-11-13 06:13:53 +00:00
m_absPath = SystemString(project.getProjectRootPath().getAbsolutePath()) + _S('/') + m_relPath;
2015-09-29 21:50:07 +00:00
SanitizePath(m_relPath);
SanitizePath(m_absPath);
2016-08-29 00:28:24 +00:00
ComputeHash();
2015-09-29 21:50:07 +00:00
}
#if HECL_UCS2
2017-11-13 06:13:53 +00:00
void ProjectPath::assign(Database::Project& project, std::string_view path)
2015-09-29 21:50:07 +00:00
{
std::wstring wpath = UTF8ToWide(path);
2016-04-05 01:49:42 +00:00
assign(project, wpath);
2015-09-29 21:50:07 +00:00
}
#endif
2017-11-13 06:13:53 +00:00
void ProjectPath::assign(const ProjectPath& parentPath, SystemStringView path)
2015-06-09 22:19:59 +00:00
{
2015-09-30 06:23:07 +00:00
m_proj = parentPath.m_proj;
2016-04-05 01:49:42 +00:00
SystemString usePath;
size_t pipeFind = path.rfind(_S('|'));
if (pipeFind != SystemString::npos)
{
m_auxInfo.assign(path.cbegin() + pipeFind + 1, path.cend());
usePath.assign(path.cbegin(), path.cbegin() + pipeFind);
}
else
usePath = path;
m_relPath = CanonRelPath(parentPath.m_relPath + _S('/') + usePath);
2017-11-13 06:13:53 +00:00
m_absPath = SystemString(m_proj->getProjectRootPath().getAbsolutePath()) + _S('/') + m_relPath;
2015-08-05 01:54:35 +00:00
SanitizePath(m_relPath);
SanitizePath(m_absPath);
2016-08-29 00:28:24 +00:00
ComputeHash();
2015-06-09 22:19:59 +00:00
}
2015-07-22 19:14:50 +00:00
#if HECL_UCS2
2017-11-13 06:13:53 +00:00
void ProjectPath::assign(const ProjectPath& parentPath, std::string_view path)
2015-07-22 19:14:50 +00:00
{
2015-09-29 21:50:07 +00:00
std::wstring wpath = UTF8ToWide(path);
2016-04-05 01:49:42 +00:00
assign(parentPath, wpath);
2015-07-22 19:14:50 +00:00
}
#endif
2015-09-30 06:23:07 +00:00
ProjectPath ProjectPath::getCookedPath(const Database::DataSpecEntry& spec) const
{
2015-10-04 04:35:18 +00:00
ProjectPath woExt = getWithExtension(nullptr, true);
ProjectPath ret(m_proj->getProjectCookedPath(spec), woExt.getRelativePath());
if (getAuxInfo().size())
2017-11-13 06:13:53 +00:00
return ret.getWithExtension((SystemString(_S(".")) + getAuxInfo().data()).c_str());
else
return ret;
2015-09-30 06:23:07 +00:00
}
2015-11-21 01:13:06 +00:00
ProjectPath::Type ProjectPath::getPathType() const
2015-06-09 22:19:59 +00:00
{
2016-09-25 01:57:43 +00:00
if (m_absPath.find(_S('*')) != SystemString::npos)
2016-09-21 05:41:06 +00:00
return Type::Glob;
2015-07-22 19:14:50 +00:00
Sstat theStat;
2016-03-04 23:02:44 +00:00
if (hecl::Stat(m_absPath.c_str(), &theStat))
2015-11-21 01:13:06 +00:00
return Type::None;
2015-06-09 22:19:59 +00:00
if (S_ISDIR(theStat.st_mode))
2015-11-21 01:13:06 +00:00
return Type::Directory;
2015-06-09 22:19:59 +00:00
if (S_ISREG(theStat.st_mode))
2015-11-21 01:13:06 +00:00
return Type::File;
return Type::None;
2015-06-09 22:19:59 +00:00
}
2015-06-10 23:34:14 +00:00
Time ProjectPath::getModtime() const
{
2015-07-22 19:14:50 +00:00
Sstat theStat;
2015-06-11 04:55:06 +00:00
time_t latestTime = 0;
2016-09-25 01:57:43 +00:00
if (m_absPath.find(_S('*')) != SystemString::npos)
2015-06-11 04:55:06 +00:00
{
2015-10-04 04:35:18 +00:00
std::vector<ProjectPath> globResults;
2017-10-25 07:46:32 +00:00
getGlobResults(globResults);
2015-09-30 06:23:07 +00:00
for (ProjectPath& path : globResults)
2015-06-11 04:55:06 +00:00
{
2017-11-13 06:13:53 +00:00
if (!hecl::Stat(path.getAbsolutePath().data(), &theStat))
2015-06-11 04:55:06 +00:00
{
if (S_ISREG(theStat.st_mode) && theStat.st_mtime > latestTime)
latestTime = theStat.st_mtime;
}
}
2016-09-21 05:41:06 +00:00
return Time(latestTime);
2015-06-11 04:55:06 +00:00
}
2016-03-04 23:02:44 +00:00
if (!hecl::Stat(m_absPath.c_str(), &theStat))
2015-06-11 04:55:06 +00:00
{
if (S_ISREG(theStat.st_mode))
{
return Time(theStat.st_mtime);
}
else if (S_ISDIR(theStat.st_mode))
{
2017-12-02 05:49:45 +00:00
hecl::DirectoryEnumerator de(m_absPath, hecl::DirectoryEnumerator::Mode::DirsThenFilesSorted,
false, false, true);
2016-03-04 23:02:44 +00:00
for (const hecl::DirectoryEnumerator::Entry& ent : de)
2015-06-11 04:55:06 +00:00
{
2016-03-04 23:02:44 +00:00
if (!hecl::Stat(ent.m_path.c_str(), &theStat))
2015-06-11 04:55:06 +00:00
{
if (S_ISREG(theStat.st_mode) && theStat.st_mtime > latestTime)
latestTime = theStat.st_mtime;
}
}
return Time(latestTime);
}
}
2016-03-04 23:02:44 +00:00
LogModule.report(logvisor::Fatal, _S("invalid path type for computing modtime in '%s'"), m_absPath.c_str());
2015-06-11 04:55:06 +00:00
return Time();
2015-06-10 23:34:14 +00:00
}
2015-09-30 06:23:07 +00:00
static void _recursiveGlob(Database::Project& proj,
2015-10-04 04:35:18 +00:00
std::vector<ProjectPath>& outPaths,
2016-09-21 05:41:06 +00:00
const SystemString& remPath,
2015-06-09 22:19:59 +00:00
const SystemString& itStr,
bool needSlash)
{
2016-09-21 05:41:06 +00:00
SystemRegexMatch matches;
if (!std::regex_search(remPath, matches, regPATHCOMP))
2015-06-09 22:19:59 +00:00
return;
2016-09-21 05:41:06 +00:00
const SystemString& comp = matches[1];
2017-10-25 07:46:32 +00:00
if (comp.find(_S('*')) == SystemString::npos)
2015-06-09 22:19:59 +00:00
{
SystemString nextItStr = itStr;
if (needSlash)
nextItStr += _S('/');
nextItStr += comp;
2017-10-25 07:46:32 +00:00
hecl::Sstat theStat;
if (Stat(nextItStr.c_str(), &theStat))
return;
if (S_ISDIR(theStat.st_mode))
_recursiveGlob(proj, outPaths, matches.suffix(), nextItStr, true);
else
outPaths.emplace_back(proj, nextItStr);
2015-06-09 22:19:59 +00:00
return;
}
/* Compile component into regex */
SystemRegex regComp(comp, SystemRegex::ECMAScript);
2017-12-02 05:49:45 +00:00
hecl::DirectoryEnumerator de(itStr, hecl::DirectoryEnumerator::Mode::DirsThenFilesSorted,
false, false, true);
2016-03-04 23:02:44 +00:00
for (const hecl::DirectoryEnumerator::Entry& ent : de)
2015-06-09 22:19:59 +00:00
{
2017-10-25 07:46:32 +00:00
if (std::regex_match(ent.m_name, regComp))
2015-06-09 22:19:59 +00:00
{
SystemString nextItStr = itStr;
if (needSlash)
nextItStr += '/';
nextItStr += ent.m_name;
2015-06-09 22:19:59 +00:00
2016-03-04 23:02:44 +00:00
hecl::Sstat theStat;
2016-02-17 03:36:06 +00:00
if (Stat(nextItStr.c_str(), &theStat))
2015-06-09 22:19:59 +00:00
continue;
if (ent.m_isDir)
2016-09-21 05:41:06 +00:00
_recursiveGlob(proj, outPaths, matches.suffix(), nextItStr, true);
else
2015-09-30 06:23:07 +00:00
outPaths.emplace_back(proj, nextItStr);
2015-06-09 22:19:59 +00:00
}
}
}
2015-10-14 23:06:47 +00:00
void ProjectPath::getDirChildren(std::map<SystemString, ProjectPath>& outPaths) const
2015-09-30 06:23:07 +00:00
{
2017-12-02 05:49:45 +00:00
hecl::DirectoryEnumerator de(m_absPath, hecl::DirectoryEnumerator::Mode::DirsThenFilesSorted,
false, false, true);
2016-03-04 23:02:44 +00:00
for (const hecl::DirectoryEnumerator::Entry& ent : de)
outPaths[ent.m_name] = ProjectPath(*this, ent.m_name);
}
2015-09-30 06:23:07 +00:00
2016-03-04 23:02:44 +00:00
hecl::DirectoryEnumerator ProjectPath::enumerateDir() const
{
2017-12-02 05:49:45 +00:00
return hecl::DirectoryEnumerator(m_absPath, hecl::DirectoryEnumerator::Mode::DirsThenFilesSorted,
false, false, true);
2015-09-30 06:23:07 +00:00
}
2017-10-25 07:46:32 +00:00
void ProjectPath::getGlobResults(std::vector<ProjectPath>& outPaths) const
2015-06-09 22:19:59 +00:00
{
2017-11-13 06:13:53 +00:00
auto rootPath = m_proj->getProjectRootPath().getAbsolutePath();
_recursiveGlob(*m_proj, outPaths, m_relPath, rootPath.data(), rootPath.back() != _S('/'));
2015-09-30 06:23:07 +00:00
}
2017-11-13 06:13:53 +00:00
ProjectRootPath SearchForProject(SystemStringView path)
2015-09-30 06:23:07 +00:00
{
ProjectRootPath testRoot(path);
2017-11-13 06:13:53 +00:00
auto begin = testRoot.getAbsolutePath().begin();
auto end = testRoot.getAbsolutePath().end();
2015-09-30 06:23:07 +00:00
while (begin != end)
{
SystemString testPath(begin, end);
SystemString testIndexPath = testPath + _S("/.hecl/beacon");
Sstat theStat;
2016-03-04 23:02:44 +00:00
if (!hecl::Stat(testIndexPath.c_str(), &theStat))
2015-09-30 06:23:07 +00:00
{
if (S_ISREG(theStat.st_mode))
{
2016-03-04 23:02:44 +00:00
FILE* fp = hecl::Fopen(testIndexPath.c_str(), _S("rb"));
2015-09-30 06:23:07 +00:00
if (!fp)
continue;
char magic[4];
size_t readSize = fread(magic, 1, 4, fp);
fclose(fp);
if (readSize != 4)
continue;
2016-03-04 23:02:44 +00:00
static const hecl::FourCC hecl("HECL");
if (hecl::FourCC(magic) != hecl)
2015-09-30 06:23:07 +00:00
continue;
return ProjectRootPath(testPath);
}
}
while (begin != end && *(end-1) != _S('/') && *(end-1) != _S('\\'))
--end;
if (begin != end)
--end;
}
return ProjectRootPath();
2015-06-09 22:19:59 +00:00
}
2017-11-13 06:13:53 +00:00
ProjectRootPath SearchForProject(SystemStringView path, SystemString& subpathOut)
2015-06-11 04:55:06 +00:00
{
ProjectRootPath testRoot(path);
2017-11-13 06:13:53 +00:00
auto begin = testRoot.getAbsolutePath().begin();
auto end = testRoot.getAbsolutePath().end();
2015-06-11 04:55:06 +00:00
while (begin != end)
{
SystemString testPath(begin, end);
2015-06-12 09:08:49 +00:00
SystemString testIndexPath = testPath + _S("/.hecl/beacon");
2015-07-22 19:14:50 +00:00
Sstat theStat;
2016-03-04 23:02:44 +00:00
if (!hecl::Stat(testIndexPath.c_str(), &theStat))
2015-06-11 04:55:06 +00:00
{
if (S_ISREG(theStat.st_mode))
{
2016-03-04 23:02:44 +00:00
FILE* fp = hecl::Fopen(testIndexPath.c_str(), _S("rb"));
2015-06-11 04:55:06 +00:00
if (!fp)
continue;
char magic[4];
size_t readSize = fread(magic, 1, 4, fp);
fclose(fp);
if (readSize != 4)
continue;
2016-03-04 23:02:44 +00:00
if (hecl::FourCC(magic) != FOURCC('HECL'))
2015-06-11 04:55:06 +00:00
continue;
2015-09-30 06:23:07 +00:00
ProjectRootPath newRootPath = ProjectRootPath(testPath);
2017-11-13 06:13:53 +00:00
auto origEnd = testRoot.getAbsolutePath().end();
2015-09-30 06:23:07 +00:00
while (end != origEnd && *end != _S('/') && *end != _S('\\'))
++end;
2017-11-07 08:05:40 +00:00
if (end != origEnd && (*end == _S('/') || *end == _S('\\')))
2017-10-26 05:37:15 +00:00
++end;
2015-09-30 06:23:07 +00:00
subpathOut.assign(end, origEnd);
return newRootPath;
2015-06-11 04:55:06 +00:00
}
}
2015-06-12 04:02:23 +00:00
while (begin != end && *(end-1) != _S('/') && *(end-1) != _S('\\'))
--end;
2015-07-22 19:14:50 +00:00
if (begin != end)
--end;
2015-06-11 04:55:06 +00:00
}
2015-09-30 06:23:07 +00:00
return ProjectRootPath();
2015-06-11 04:55:06 +00:00
}
2015-06-09 22:19:59 +00:00
}