Files

378 lines
12 KiB
C++
Raw Permalink Normal View History

2016-03-04 13:02:44 -10:00
#include "hecl/hecl.hpp"
2019-09-04 17:22:05 -04:00
2015-06-09 12:19:59 -10:00
#include <regex>
2019-09-04 17:22:05 -04:00
#include "hecl/Database.hpp"
#include "hecl/FourCC.hpp"
2018-12-07 19:18:42 -10:00
namespace hecl {
static const SystemRegex regPATHCOMP(_SYS_STR("[/\\\\]*([^/\\\\]+)"), SystemRegex::ECMAScript | SystemRegex::optimize);
2015-06-09 12:19:59 -10:00
2018-12-07 19:18:42 -10:00
static SystemString CanonRelPath(SystemStringView path) {
/* Tokenize Path */
std::vector<SystemString> comps;
hecl::SystemRegexMatch matches;
SystemString in(path);
SanitizePath(in);
for (; std::regex_search(in, matches, regPATHCOMP); in = matches.suffix().str()) {
hecl::SystemRegexMatch::const_reference match = matches[1];
2019-09-30 21:23:35 -10:00
if (match == _SYS_STR("."))
2018-12-07 19:18:42 -10:00
continue;
2019-09-30 21:23:35 -10:00
else if (match == _SYS_STR("..")) {
2018-12-07 19:18:42 -10:00
if (comps.empty()) {
/* Unable to resolve outside project */
2020-04-11 12:48:11 -10:00
LogModule.report(logvisor::Fatal, FMT_STRING(_SYS_STR("Unable to resolve outside project root in {}")), path);
2018-12-07 19:18:42 -10:00
return _SYS_STR(".");
}
comps.pop_back();
continue;
2015-07-17 18:35:01 -10:00
}
2018-12-07 19:18:42 -10:00
comps.push_back(match.str());
}
2015-07-17 18:35:01 -10:00
2018-12-07 19:18:42 -10:00
/* Emit relative path */
if (comps.size()) {
auto it = comps.begin();
SystemString retval = *it;
for (++it; it != comps.end(); ++it) {
if ((*it).size()) {
retval += _SYS_STR('/');
retval += *it;
}
2015-07-17 18:35:01 -10:00
}
2018-12-07 19:18:42 -10:00
return retval;
}
return _SYS_STR(".");
2015-06-09 12:19:59 -10:00
}
2018-12-07 19:18:42 -10:00
static SystemString CanonRelPath(SystemStringView path, const ProjectRootPath& projectRoot) {
/* Absolute paths not allowed; attempt to make project-relative */
if (IsAbsolute(path))
return CanonRelPath(projectRoot.getProjectRelativeFromAbsolute(path));
return CanonRelPath(path);
2015-10-05 15:49:23 -10:00
}
2018-12-07 19:18:42 -10:00
void ProjectPath::assign(Database::Project& project, SystemStringView path) {
m_proj = &project;
2016-04-04 15:49:42 -10:00
2018-12-07 19:18:42 -10:00
SystemString usePath;
size_t pipeFind = path.rfind(_SYS_STR('|'));
if (pipeFind != SystemString::npos) {
m_auxInfo.assign(path.cbegin() + pipeFind + 1, path.cend());
usePath.assign(path.cbegin(), path.cbegin() + pipeFind);
} else
usePath = path;
2016-04-04 15:49:42 -10:00
2018-12-07 19:18:42 -10:00
m_relPath = CanonRelPath(usePath, project.getProjectRootPath());
m_absPath = SystemString(project.getProjectRootPath().getAbsolutePath()) + _SYS_STR('/') + m_relPath;
SanitizePath(m_relPath);
SanitizePath(m_absPath);
2018-10-11 10:48:13 -10:00
2018-12-07 19:18:42 -10:00
ComputeHash();
2015-09-29 11:50:07 -10:00
}
#if HECL_UCS2
2018-12-07 19:18:42 -10:00
void ProjectPath::assign(Database::Project& project, std::string_view path) {
std::wstring wpath = UTF8ToWide(path);
assign(project, wpath);
2015-09-29 11:50:07 -10:00
}
#endif
2018-12-07 19:18:42 -10:00
void ProjectPath::assign(const ProjectPath& parentPath, SystemStringView path) {
m_proj = parentPath.m_proj;
2016-04-04 15:49:42 -10:00
2018-12-07 19:18:42 -10:00
SystemString usePath;
size_t pipeFind = path.rfind(_SYS_STR('|'));
if (pipeFind != SystemString::npos) {
m_auxInfo.assign(path.cbegin() + pipeFind + 1, path.cend());
usePath.assign(path.cbegin(), path.cbegin() + pipeFind);
} else
usePath = path;
2016-04-04 15:49:42 -10:00
2018-12-07 19:18:42 -10:00
m_relPath = CanonRelPath(parentPath.m_relPath + _SYS_STR('/') + usePath);
m_absPath = SystemString(m_proj->getProjectRootPath().getAbsolutePath()) + _SYS_STR('/') + m_relPath;
SanitizePath(m_relPath);
SanitizePath(m_absPath);
2015-08-04 18:54:35 -07:00
2018-12-07 19:18:42 -10:00
ComputeHash();
2015-06-09 12:19:59 -10:00
}
2015-07-22 09:14:50 -10:00
#if HECL_UCS2
2018-12-07 19:18:42 -10:00
void ProjectPath::assign(const ProjectPath& parentPath, std::string_view path) {
std::wstring wpath = UTF8ToWide(path);
assign(parentPath, wpath);
2015-07-22 09:14:50 -10:00
}
#endif
2018-12-07 19:18:42 -10:00
ProjectPath ProjectPath::getWithExtension(const SystemChar* ext, bool replace) const {
ProjectPath pp(*this);
if (replace) {
auto relIt = pp.m_relPath.end();
if (relIt != pp.m_relPath.begin())
--relIt;
auto absIt = pp.m_absPath.end();
if (absIt != pp.m_absPath.begin())
--absIt;
while (relIt != pp.m_relPath.begin() && *relIt != _SYS_STR('.') && *relIt != _SYS_STR('/')) {
--relIt;
--absIt;
2018-10-11 10:48:13 -10:00
}
2018-12-07 19:18:42 -10:00
if (*relIt == _SYS_STR('.') && relIt != pp.m_relPath.begin()) {
pp.m_relPath.resize(relIt - pp.m_relPath.begin());
pp.m_absPath.resize(absIt - pp.m_absPath.begin());
2018-10-11 10:48:13 -10:00
}
2018-12-07 19:18:42 -10:00
}
if (ext) {
pp.m_relPath += ext;
pp.m_absPath += ext;
}
2018-10-11 10:48:13 -10:00
2018-12-07 19:18:42 -10:00
pp.ComputeHash();
return pp;
2018-10-11 10:48:13 -10:00
}
2018-12-07 19:18:42 -10:00
ProjectPath ProjectPath::getCookedPath(const Database::DataSpecEntry& spec) const {
ProjectPath woExt = getWithExtension(nullptr, true);
ProjectPath ret(m_proj->getProjectCookedPath(spec), woExt.getRelativePath());
2018-12-07 19:18:42 -10:00
if (getAuxInfo().size())
return ret.getWithExtension((SystemString(_SYS_STR(".")) + getAuxInfo().data()).c_str());
else
return ret;
2015-09-29 20:23:07 -10:00
}
2018-12-07 19:18:42 -10:00
ProjectPath::Type ProjectPath::getPathType() const {
2019-09-30 21:23:35 -10:00
if (m_absPath.empty())
return Type::None;
2018-12-07 19:18:42 -10:00
if (m_absPath.find(_SYS_STR('*')) != SystemString::npos)
return Type::Glob;
Sstat theStat;
if (hecl::Stat(m_absPath.c_str(), &theStat))
2015-11-20 15:13:06 -10:00
return Type::None;
2018-12-07 19:18:42 -10:00
if (S_ISDIR(theStat.st_mode))
return Type::Directory;
if (S_ISREG(theStat.st_mode))
return Type::File;
return Type::None;
2015-06-09 12:19:59 -10:00
}
2018-12-07 19:18:42 -10:00
Time ProjectPath::getModtime() const {
Sstat theStat;
time_t latestTime = 0;
if (m_absPath.find(_SYS_STR('*')) != SystemString::npos) {
std::vector<ProjectPath> globResults;
getGlobResults(globResults);
for (ProjectPath& path : globResults) {
if (!hecl::Stat(path.getAbsolutePath().data(), &theStat)) {
if (S_ISREG(theStat.st_mode) && theStat.st_mtime > latestTime)
latestTime = theStat.st_mtime;
}
}
return Time(latestTime);
}
if (!hecl::Stat(m_absPath.c_str(), &theStat)) {
if (S_ISREG(theStat.st_mode)) {
return Time(theStat.st_mtime);
} else if (S_ISDIR(theStat.st_mode)) {
hecl::DirectoryEnumerator de(m_absPath, hecl::DirectoryEnumerator::Mode::DirsThenFilesSorted, false, false, true);
for (const hecl::DirectoryEnumerator::Entry& ent : de) {
if (!hecl::Stat(ent.m_path.c_str(), &theStat)) {
if (S_ISREG(theStat.st_mode) && theStat.st_mtime > latestTime)
latestTime = theStat.st_mtime;
}
}
return Time(latestTime);
}
}
2020-04-11 12:48:11 -10:00
LogModule.report(logvisor::Fatal, FMT_STRING(_SYS_STR("invalid path type for computing modtime in '{}'")), m_absPath);
2018-12-07 19:18:42 -10:00
return Time();
}
static void _recursiveGlob(Database::Project& proj, std::vector<ProjectPath>& outPaths, const SystemString& remPath,
const SystemString& itStr, bool needSlash) {
SystemRegexMatch matches;
if (!std::regex_search(remPath, matches, regPATHCOMP))
return;
const SystemString& comp = matches[1];
if (comp.find(_SYS_STR('*')) == SystemString::npos) {
SystemString nextItStr = itStr;
if (needSlash)
nextItStr += _SYS_STR('/');
nextItStr += comp;
hecl::Sstat theStat;
if (Stat(nextItStr.c_str(), &theStat))
return;
if (S_ISDIR(theStat.st_mode))
_recursiveGlob(proj, outPaths, matches.suffix().str(), nextItStr, true);
else
outPaths.emplace_back(proj, nextItStr);
return;
}
/* Compile component into regex */
SystemRegex regComp(comp, SystemRegex::ECMAScript);
hecl::DirectoryEnumerator de(itStr, hecl::DirectoryEnumerator::Mode::DirsThenFilesSorted, false, false, true);
for (const hecl::DirectoryEnumerator::Entry& ent : de) {
if (std::regex_match(ent.m_name, regComp)) {
SystemString nextItStr = itStr;
if (needSlash)
nextItStr += '/';
nextItStr += ent.m_name;
hecl::Sstat theStat;
if (Stat(nextItStr.c_str(), &theStat))
continue;
if (ent.m_isDir)
_recursiveGlob(proj, outPaths, matches.suffix().str(), nextItStr, true);
else
outPaths.emplace_back(proj, nextItStr);
}
}
}
void ProjectPath::getDirChildren(std::map<SystemString, ProjectPath>& outPaths) const {
hecl::DirectoryEnumerator de(m_absPath, hecl::DirectoryEnumerator::Mode::DirsThenFilesSorted, false, false, true);
for (const hecl::DirectoryEnumerator::Entry& ent : de)
outPaths[ent.m_name] = ProjectPath(*this, ent.m_name);
}
hecl::DirectoryEnumerator ProjectPath::enumerateDir() const {
return hecl::DirectoryEnumerator(m_absPath, hecl::DirectoryEnumerator::Mode::DirsThenFilesSorted, false, false, true);
}
void ProjectPath::getGlobResults(std::vector<ProjectPath>& outPaths) const {
auto rootPath = m_proj->getProjectRootPath().getAbsolutePath();
_recursiveGlob(*m_proj, outPaths, m_relPath, rootPath.data(), rootPath.back() != _SYS_STR('/'));
}
2019-09-30 21:23:35 -10:00
template <typename T>
static bool RegexSearchLast(const T& str, std::match_results<typename T::const_iterator>& m,
const std::basic_regex<typename T::value_type>& reg) {
using Iterator = std::regex_iterator<typename T::const_iterator>;
Iterator begin = Iterator(str.begin(), str.end(), reg);
Iterator end = Iterator();
if (begin == end)
return false;
Iterator last_it;
for (auto it = begin; it != end; ++it)
last_it = it;
m = *last_it;
return true;
}
static const hecl::SystemRegex regParsedHash32(_SYS_STR(R"(_([0-9a-fA-F]{8}))"),
std::regex::ECMAScript | std::regex::optimize);
uint32_t ProjectPath::parsedHash32() const {
2019-10-12 18:54:07 -10:00
if (!m_auxInfo.empty()) {
2019-09-30 21:23:35 -10:00
hecl::SystemRegexMatch match;
if (RegexSearchLast(m_auxInfo, match, regParsedHash32)) {
auto hexStr = match[1].str();
if (auto val = hecl::StrToUl(hexStr.c_str(), nullptr, 16))
return val;
}
2019-10-12 18:54:07 -10:00
} else {
2019-09-30 21:23:35 -10:00
hecl::SystemViewRegexMatch match;
if (RegexSearchLast(getLastComponent(), match, regParsedHash32)) {
auto hexStr = match[1].str();
if (auto val = hecl::StrToUl(hexStr.c_str(), nullptr, 16))
return val;
}
}
return hash().val32();
}
2018-12-07 19:18:42 -10:00
ProjectRootPath SearchForProject(SystemStringView path) {
ProjectRootPath testRoot(path);
auto begin = testRoot.getAbsolutePath().begin();
auto end = testRoot.getAbsolutePath().end();
while (begin != end) {
SystemString testPath(begin, end);
SystemString testIndexPath = testPath + _SYS_STR("/.hecl/beacon");
2015-07-22 09:14:50 -10:00
Sstat theStat;
2018-12-07 19:18:42 -10:00
if (!hecl::Stat(testIndexPath.c_str(), &theStat)) {
if (S_ISREG(theStat.st_mode)) {
2019-08-21 19:33:25 -04:00
const auto fp = hecl::FopenUnique(testIndexPath.c_str(), _SYS_STR("rb"));
if (fp == nullptr) {
2018-12-07 19:18:42 -10:00
continue;
2019-08-21 19:33:25 -04:00
}
2018-12-07 19:18:42 -10:00
char magic[4];
2019-08-21 19:33:25 -04:00
const size_t readSize = std::fread(magic, 1, sizeof(magic), fp.get());
if (readSize != sizeof(magic)) {
2018-12-07 19:18:42 -10:00
continue;
2019-08-21 19:33:25 -04:00
}
static constexpr hecl::FourCC hecl("HECL");
2019-08-21 19:33:25 -04:00
if (hecl::FourCC(magic) != hecl) {
2018-12-07 19:18:42 -10:00
continue;
2019-08-21 19:33:25 -04:00
}
2018-12-07 19:18:42 -10:00
return ProjectRootPath(testPath);
}
2015-06-09 12:19:59 -10:00
}
2018-12-07 19:18:42 -10:00
while (begin != end && *(end - 1) != _SYS_STR('/') && *(end - 1) != _SYS_STR('\\'))
--end;
if (begin != end)
--end;
}
return ProjectRootPath();
}
2015-06-09 12:19:59 -10:00
2018-12-07 19:18:42 -10:00
ProjectRootPath SearchForProject(SystemStringView path, SystemString& subpathOut) {
2019-08-21 19:33:25 -04:00
const ProjectRootPath testRoot(path);
2018-12-07 19:18:42 -10:00
auto begin = testRoot.getAbsolutePath().begin();
auto end = testRoot.getAbsolutePath().end();
2019-08-21 19:33:25 -04:00
2018-12-07 19:18:42 -10:00
while (begin != end) {
SystemString testPath(begin, end);
SystemString testIndexPath = testPath + _SYS_STR("/.hecl/beacon");
Sstat theStat;
2019-08-21 19:33:25 -04:00
2018-12-07 19:18:42 -10:00
if (!hecl::Stat(testIndexPath.c_str(), &theStat)) {
if (S_ISREG(theStat.st_mode)) {
2019-08-21 19:33:25 -04:00
const auto fp = hecl::FopenUnique(testIndexPath.c_str(), _SYS_STR("rb"));
if (fp == nullptr) {
2018-12-07 19:18:42 -10:00
continue;
2019-08-21 19:33:25 -04:00
}
2018-12-07 19:18:42 -10:00
char magic[4];
2019-08-21 19:33:25 -04:00
const size_t readSize = std::fread(magic, 1, sizeof(magic), fp.get());
if (readSize != sizeof(magic)) {
2018-12-07 19:18:42 -10:00
continue;
2019-08-21 19:33:25 -04:00
}
if (hecl::FourCC(magic) != FOURCC('HECL')) {
2018-12-07 19:18:42 -10:00
continue;
2019-08-21 19:33:25 -04:00
}
const ProjectRootPath newRootPath = ProjectRootPath(testPath);
const auto origEnd = testRoot.getAbsolutePath().end();
while (end != origEnd && *end != _SYS_STR('/') && *end != _SYS_STR('\\')) {
2018-12-07 19:18:42 -10:00
++end;
2019-08-21 19:33:25 -04:00
}
if (end != origEnd && (*end == _SYS_STR('/') || *end == _SYS_STR('\\'))) {
2018-12-07 19:18:42 -10:00
++end;
2019-08-21 19:33:25 -04:00
}
2018-12-07 19:18:42 -10:00
subpathOut.assign(end, origEnd);
return newRootPath;
}
2015-06-09 12:19:59 -10:00
}
2018-12-07 19:18:42 -10:00
2019-08-21 19:33:25 -04:00
while (begin != end && *(end - 1) != _SYS_STR('/') && *(end - 1) != _SYS_STR('\\')) {
2018-12-07 19:18:42 -10:00
--end;
2019-08-21 19:33:25 -04:00
}
if (begin != end) {
2018-12-07 19:18:42 -10:00
--end;
2019-08-21 19:33:25 -04:00
}
2018-12-07 19:18:42 -10:00
}
return ProjectRootPath();
2015-06-09 12:19:59 -10:00
}
2018-12-07 19:18:42 -10:00
} // namespace hecl