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