2015-07-04 06:03:59 +00:00
|
|
|
#include "HECL/HECL.hpp"
|
2015-06-09 22:19:59 +00:00
|
|
|
#include <regex>
|
|
|
|
|
|
|
|
namespace HECL
|
|
|
|
{
|
|
|
|
|
|
|
|
static const SystemRegex regGLOB(_S("\\*"), SystemRegex::ECMAScript|SystemRegex::optimize);
|
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);
|
|
|
|
|
2015-07-18 04:35:01 +00:00
|
|
|
static SystemString canonRelPath(const SystemString& path)
|
2015-07-16 02:03:38 +00:00
|
|
|
{
|
2015-07-18 04:35:01 +00:00
|
|
|
/* Absolute paths not allowed */
|
|
|
|
if (path[0] == _S('/') || path[0] == _S('\\'))
|
|
|
|
{
|
2015-07-26 02:52:02 +00:00
|
|
|
LogModule.report(LogVisor::Error, "Absolute path provided; expected relative: %s", path.c_str());
|
2015-07-18 04:35:01 +00:00
|
|
|
return _S(".");
|
|
|
|
}
|
2015-07-16 02:03:38 +00:00
|
|
|
|
2015-07-18 04:35:01 +00:00
|
|
|
/* Tokenize Path */
|
|
|
|
std::vector<SystemString> comps;
|
|
|
|
HECL::SystemRegexMatch matches;
|
|
|
|
SystemString in = path;
|
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 */
|
2015-07-26 02:52:02 +00:00
|
|
|
LogModule.report(LogVisor::Error, _S("Unable to resolve outside project root in %s"), path.c_str());
|
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)
|
|
|
|
{
|
|
|
|
retval += _S('/');
|
|
|
|
retval += *it;
|
|
|
|
}
|
|
|
|
return retval;
|
|
|
|
}
|
2015-07-22 19:14:50 +00:00
|
|
|
return _S(".");
|
2015-06-09 22:19:59 +00:00
|
|
|
}
|
|
|
|
|
2015-07-16 02:03:38 +00:00
|
|
|
ProjectPath::ProjectPath(const ProjectPath& parentPath, const SystemString& path)
|
2015-07-18 04:35:01 +00:00
|
|
|
: m_projRoot(parentPath.m_projRoot)
|
2015-06-09 22:19:59 +00:00
|
|
|
{
|
2015-07-22 19:14:50 +00:00
|
|
|
m_relPath = canonRelPath(parentPath.m_relPath + _S('/') + path);
|
|
|
|
m_absPath = parentPath.m_projRoot + _S('/') + m_relPath;
|
2015-06-12 09:08:49 +00:00
|
|
|
m_hash = Hash(m_relPath);
|
2015-06-10 02:40:03 +00:00
|
|
|
#if HECL_UCS2
|
|
|
|
m_utf8AbsPath = WideToUTF8(m_absPath);
|
2015-07-22 19:14:50 +00:00
|
|
|
m_utf8RelPath = WideToUTF8(m_relPath);
|
2015-06-10 02:40:03 +00:00
|
|
|
#endif
|
2015-06-09 22:19:59 +00:00
|
|
|
}
|
|
|
|
|
2015-07-22 19:14:50 +00:00
|
|
|
#if HECL_UCS2
|
|
|
|
ProjectPath::ProjectPath(const ProjectPath& parentPath, const std::string& path)
|
|
|
|
: m_projRoot(parentPath.m_projRoot)
|
|
|
|
{
|
|
|
|
std::wstring wpath = UTF8ToWide(path);
|
|
|
|
m_relPath = canonRelPath(parentPath.m_relPath + _S('/') + wpath);
|
|
|
|
m_absPath = parentPath.m_projRoot + _S('/') + m_relPath;
|
|
|
|
m_hash = Hash(m_relPath);
|
|
|
|
m_utf8AbsPath = WideToUTF8(m_absPath);
|
|
|
|
m_utf8RelPath = WideToUTF8(m_relPath);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2015-06-10 02:40:03 +00:00
|
|
|
ProjectPath::PathType ProjectPath::getPathType() const
|
2015-06-09 22:19:59 +00:00
|
|
|
{
|
|
|
|
if (std::regex_search(m_absPath, regGLOB))
|
|
|
|
return PT_GLOB;
|
2015-07-22 19:14:50 +00:00
|
|
|
Sstat theStat;
|
|
|
|
if (HECL::Stat(m_absPath.c_str(), &theStat))
|
2015-06-09 22:19:59 +00:00
|
|
|
return PT_NONE;
|
|
|
|
if (S_ISDIR(theStat.st_mode))
|
|
|
|
return PT_DIRECTORY;
|
|
|
|
if (S_ISREG(theStat.st_mode))
|
|
|
|
return PT_FILE;
|
|
|
|
return PT_NONE;
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
if (std::regex_search(m_absPath, regGLOB))
|
|
|
|
{
|
|
|
|
std::vector<SystemString> globReults;
|
|
|
|
getGlobResults(globReults);
|
|
|
|
for (SystemString& path : globReults)
|
|
|
|
{
|
|
|
|
if (!HECL::Stat(path.c_str(), &theStat))
|
|
|
|
{
|
|
|
|
if (S_ISREG(theStat.st_mode) && theStat.st_mtime > latestTime)
|
|
|
|
latestTime = theStat.st_mtime;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
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))
|
|
|
|
{
|
|
|
|
#if _WIN32
|
|
|
|
#else
|
|
|
|
DIR* dir = opendir(m_absPath.c_str());
|
|
|
|
dirent* de;
|
|
|
|
while ((de = readdir(dir)))
|
|
|
|
{
|
|
|
|
if (de->d_name[0] == '.')
|
|
|
|
continue;
|
|
|
|
if (!HECL::Stat(de->d_name, &theStat))
|
|
|
|
{
|
|
|
|
if (S_ISREG(theStat.st_mode) && theStat.st_mtime > latestTime)
|
|
|
|
latestTime = theStat.st_mtime;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
closedir(dir);
|
|
|
|
#endif
|
|
|
|
return Time(latestTime);
|
|
|
|
}
|
|
|
|
}
|
2015-07-06 01:35:08 +00:00
|
|
|
LogModule.report(LogVisor::Error, _S("invalid path type for computing modtime"));
|
2015-06-11 04:55:06 +00:00
|
|
|
return Time();
|
2015-06-10 23:34:14 +00:00
|
|
|
}
|
|
|
|
|
2015-06-09 22:19:59 +00:00
|
|
|
static void _recursiveGlob(std::vector<SystemString>& outPaths,
|
|
|
|
size_t level,
|
|
|
|
const SystemRegexMatch& pathCompMatches,
|
|
|
|
const SystemString& itStr,
|
|
|
|
bool needSlash)
|
|
|
|
{
|
|
|
|
if (level >= pathCompMatches.size())
|
|
|
|
return;
|
|
|
|
|
|
|
|
SystemString comp = pathCompMatches.str(level);
|
|
|
|
if (!std::regex_search(comp, regGLOB))
|
|
|
|
{
|
|
|
|
SystemString nextItStr = itStr;
|
|
|
|
if (needSlash)
|
|
|
|
nextItStr += _S('/');
|
|
|
|
nextItStr += comp;
|
|
|
|
_recursiveGlob(outPaths, level+1, pathCompMatches, nextItStr, true);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Compile component into regex */
|
|
|
|
SystemRegex regComp(comp, SystemRegex::ECMAScript);
|
|
|
|
|
|
|
|
#if _WIN32
|
|
|
|
#else
|
|
|
|
DIR* dir = opendir(itStr.c_str());
|
|
|
|
if (!dir)
|
2015-07-26 02:52:02 +00:00
|
|
|
{
|
|
|
|
LogModule.report(LogVisor::Error, "unable to open directory for traversal at '%s'", itStr.c_str());
|
|
|
|
return;
|
|
|
|
}
|
2015-06-09 22:19:59 +00:00
|
|
|
|
|
|
|
struct dirent* de;
|
|
|
|
while ((de = readdir(dir)))
|
|
|
|
{
|
|
|
|
if (std::regex_search(de->d_name, regComp))
|
|
|
|
{
|
|
|
|
SystemString nextItStr = itStr;
|
|
|
|
if (needSlash)
|
|
|
|
nextItStr += '/';
|
|
|
|
nextItStr += de->d_name;
|
|
|
|
|
|
|
|
struct stat theStat;
|
|
|
|
if (stat(nextItStr.c_str(), &theStat))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (S_ISDIR(theStat.st_mode))
|
|
|
|
_recursiveGlob(outPaths, level+1, pathCompMatches, nextItStr, true);
|
|
|
|
else if (S_ISREG(theStat.st_mode))
|
|
|
|
outPaths.push_back(nextItStr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2015-06-10 02:40:03 +00:00
|
|
|
void ProjectPath::getGlobResults(std::vector<SystemString>& outPaths) const
|
2015-06-09 22:19:59 +00:00
|
|
|
{
|
|
|
|
#if _WIN32
|
2015-07-22 19:14:50 +00:00
|
|
|
SystemString itStr;
|
2015-06-09 22:19:59 +00:00
|
|
|
SystemRegexMatch letterMatch;
|
|
|
|
if (m_absPath.compare(0, 2, _S("//")))
|
|
|
|
itStr = _S("\\\\");
|
|
|
|
else if (std::regex_search(m_absPath, letterMatch, regDRIVELETTER))
|
|
|
|
if (letterMatch[1].str().size())
|
|
|
|
itStr = letterMatch[1];
|
|
|
|
#else
|
|
|
|
SystemString itStr = _S("/");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
SystemRegexMatch pathCompMatches;
|
|
|
|
if (std::regex_search(m_absPath, pathCompMatches, regPATHCOMP))
|
|
|
|
_recursiveGlob(outPaths, 1, pathCompMatches, itStr, false);
|
|
|
|
}
|
|
|
|
|
2015-06-12 04:02:23 +00:00
|
|
|
std::unique_ptr<ProjectRootPath> SearchForProject(const SystemString& path)
|
2015-06-11 04:55:06 +00:00
|
|
|
{
|
|
|
|
ProjectRootPath testRoot(path);
|
|
|
|
SystemString::const_iterator begin = testRoot.getAbsolutePath().begin();
|
|
|
|
SystemString::const_iterator end = testRoot.getAbsolutePath().end();
|
|
|
|
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;
|
2015-06-11 04:55:06 +00:00
|
|
|
if (!HECL::Stat(testIndexPath.c_str(), &theStat))
|
|
|
|
{
|
|
|
|
if (S_ISREG(theStat.st_mode))
|
|
|
|
{
|
|
|
|
FILE* fp = HECL::Fopen(testIndexPath.c_str(), _S("rb"));
|
|
|
|
if (!fp)
|
|
|
|
continue;
|
|
|
|
char magic[4];
|
|
|
|
size_t readSize = fread(magic, 1, 4, fp);
|
|
|
|
fclose(fp);
|
|
|
|
if (readSize != 4)
|
|
|
|
continue;
|
|
|
|
static const HECL::FourCC hecl("HECL");
|
|
|
|
if (HECL::FourCC(magic) != hecl)
|
|
|
|
continue;
|
2015-06-12 04:02:23 +00:00
|
|
|
return std::unique_ptr<ProjectRootPath>(new ProjectRootPath(testPath));
|
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-06-12 04:02:23 +00:00
|
|
|
return std::unique_ptr<ProjectRootPath>();
|
2015-06-11 04:55:06 +00:00
|
|
|
}
|
|
|
|
|
2015-06-09 22:19:59 +00:00
|
|
|
}
|