From 851221f861613f83385a7ad46021b3abcb0a966c Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Tue, 26 May 2015 23:09:05 -1000 Subject: [PATCH] project path representation --- hecl/driver/CToolBase.hpp | 1 + hecl/driver/CToolInit.hpp | 16 ++- hecl/driver/main.cpp | 4 + hecl/include/HECL.hpp | 214 +++++++++++++++++++++++++++++++-- hecl/include/HECLDatabase.hpp | 21 ++-- hecl/lib/HECL.cpp | 9 ++ hecl/lib/database/CProject.cpp | 8 +- hecl/lib/lib.pro | 3 + 8 files changed, 244 insertions(+), 32 deletions(-) create mode 100644 hecl/lib/HECL.cpp diff --git a/hecl/driver/CToolBase.hpp b/hecl/driver/CToolBase.hpp index 087b66f05..51063ab15 100644 --- a/hecl/driver/CToolBase.hpp +++ b/hecl/driver/CToolBase.hpp @@ -12,6 +12,7 @@ struct SToolPassInfo { std::string pname; + std::string cwd; std::vector args; std::string output; unsigned verbosityLevel = 0; diff --git a/hecl/driver/CToolInit.hpp b/hecl/driver/CToolInit.hpp index 0f11ae85d..e59e4efb1 100644 --- a/hecl/driver/CToolInit.hpp +++ b/hecl/driver/CToolInit.hpp @@ -6,16 +6,27 @@ class CToolInit final : public CToolBase { + const std::string* m_dir = NULL; public: CToolInit(const SToolPassInfo& info) : CToolBase(info) { + if (info.args.size()) + { + + } + m_dir = &info.args[0]; } ~CToolInit() { } + int run() + { + return 0; + } + static void Help(CHelpOutput& help) { help.secHead("NAME"); @@ -44,11 +55,6 @@ public: } std::string toolName() const {return "init";} - - int run() - { - return 0; - } }; #endif // CTOOL_INIT diff --git a/hecl/driver/main.cpp b/hecl/driver/main.cpp index 5048dd031..507d09178 100644 --- a/hecl/driver/main.cpp +++ b/hecl/driver/main.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -69,6 +70,9 @@ int main(int argc, const char** argv) /* Assemble common tool pass info */ SToolPassInfo info; info.pname = argv[0]; + char cwdbuf[MAXPATHLEN]; + if (getcwd(cwdbuf, MAXPATHLEN)) + info.cwd = cwdbuf; /* Concatenate args */ std::list args; diff --git a/hecl/include/HECL.hpp b/hecl/include/HECL.hpp index 6cb53262c..572e3e253 100644 --- a/hecl/include/HECL.hpp +++ b/hecl/include/HECL.hpp @@ -1,12 +1,34 @@ #ifndef HECL_HPP #define HECL_HPP +#if _WIN32 +char* win_realpath(const char* name, char* restrict resolved); +#else +#include +#include +#include +#include +#endif + #include +#include +#include +#include +#include "../extern/blowfish/blowfish.h" namespace HECL { -#include "../extern/blowfish/blowfish.h" +#if _WIN32 +typedef std::basic_string TSystemPath; +#else +typedef std::string TSystemPath; +#endif + +class ProjectRootPath; +static const std::regex regGLOB("\\*", std::regex::ECMAScript|std::regex::optimize); +static const std::regex regPATHCOMP("/([^/]+)", std::regex::ECMAScript|std::regex::optimize); +static const std::regex regDRIVELETTER("^([^/]*)/", std::regex::ECMAScript|std::regex::optimize); /** * @brief Severity of a log event @@ -69,7 +91,177 @@ public: inline bool operator>=(ObjectHash& other) {return hash >= other.hash;} }; -inline int16_t bswap(int16_t val) +/** + * @brief Canonicalized project path representation using POSIX conventions + * + * HECL uses POSIX-style paths (with '/' separator) and directory tokens + * ('.','..') to resolve files within a project. The database internally + * uses this representation to track working files. + * + * This class provides a convenient way to resolve paths relative to the + * project root. Part of this representation involves resolving symbolic + * links to regular file/directory paths and determining its type. + * + * NOTE THAT PROJECT PATHS ARE TREATED AS CASE SENSITIVE!! + */ +class ProjectPath +{ +protected: + TSystemPath m_absPath; + const char* m_relPath = NULL; + ProjectPath() {} + bool _canonAbsPath(const TSystemPath& path) + { +#if _WIN32 +#else + char resolvedPath[PATH_MAX]; + if (!realpath(path.c_str(), resolvedPath)) + { + throw std::invalid_argument("Unable to resolve '" + path + "' as a canonicalized path"); + return false; + } + m_absPath = resolvedPath; +#endif + return true; + } +public: + /** + * @brief Construct a project subpath representation + * @param rootPath previously constructed ProjectRootPath held by HECLDatabase::IProject + * @param path valid filesystem-path (relative or absolute) to subpath + */ + ProjectPath(const ProjectRootPath& rootPath, const TSystemPath& path) + { + _canonAbsPath(path); + if (m_absPath.size() < ((ProjectPath&)rootPath).m_absPath.size() || + m_absPath.compare(0, ((ProjectPath&)rootPath).m_absPath.size(), + ((ProjectPath&)rootPath).m_absPath)) + { + throw std::invalid_argument("'" + m_absPath + "' is not a subpath of '" + + ((ProjectPath&)rootPath).m_absPath + "'"); + return; + } + if (m_absPath.size() == ((ProjectPath&)rootPath).m_absPath.size()) + { + /* Copies of the project root are permitted */ + return; + } + m_relPath = m_absPath.c_str() + ((ProjectPath&)rootPath).m_absPath.size(); + if (m_relPath[0] == '/') + ++m_relPath; + if (m_relPath[0] == '\0') + m_relPath = NULL; + } + /** + * @brief Determine if ProjectPath represents project root directory + * @return true if project root directory + */ + inline bool isRoot() {return (m_relPath == NULL);} + + /** + * @brief Access fully-canonicalized absolute path + * @return Absolute path reference + */ + inline const TSystemPath& getAbsolutePath() {return m_absPath;} + + /** + * @brief Access fully-canonicalized project-relative path + * @return Relative pointer to within absolute-path or "." for project root-directory (use isRoot to detect) + */ + inline const char* getRelativePath() + { + if (m_relPath) + return m_relPath; + return "."; + } + + /** + * @brief Type of path + */ + enum PathType + { + 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) */ + }; + + /** + * @brief Get type of path based on syntax and filesystem queries + * @return Type of path + */ + PathType getPathType() + { + if (std::regex_search(m_absPath, regGLOB)) + return PT_GLOB; +#if _WIN32 +#else + struct stat theStat; + if (stat(m_absPath.c_str(), &theStat)) + return PT_NONE; + if (S_ISDIR(theStat.st_mode)) + return PT_DIRECTORY; + if (S_ISREG(theStat.st_mode)) + return PT_FILE; + return PT_NONE; +#endif + } + + void getGlobResults(std::vector& outPaths) + { +#if _WIN32 + std::string itStr; + std::smatch letterMatch; + if (m_absPath.compare(0, 2, "//")) + itStr = "\\\\"; + else if (std::regex_search(m_absPath, letterMatch, regDRIVELETTER)) + if (letterMatch[1].str().size()) + itStr = letterMatch[1]; +#else + std::string itStr = "/"; +#endif + bool needSlash = false; + + std::sregex_token_iterator pathComps(m_absPath.begin(), m_absPath.end(), regPATHCOMP); + for (; pathComps != std::sregex_token_iterator() ; ++pathComps) + { + const std::string& comp = *pathComps; + if (!std::regex_search(comp, regGLOB)) + { + if (needSlash) + itStr += '/'; + else + needSlash = true; + itStr += comp; + continue; + } +#if _WIN32 +#else + DIR* dir = opendir(""); +#endif + } + } +}; + +/** + * @brief Special ProjectRootPath subclass for opening HECLDatabase::IProject instances + * + * Constructing a ProjectPath requires supplying a ProjectRootPath to consistently + * resolve canonicalized relative paths. + */ +class ProjectRootPath : public ProjectPath +{ +public: + ProjectRootPath(const TSystemPath& path) + { + _canonAbsPath(path); + } +}; + + + +/* Type-sensitive byte swappers */ +static inline int16_t bswap(int16_t val) { #if __GNUC__ return __builtin_bswap16(val); @@ -80,7 +272,7 @@ inline int16_t bswap(int16_t val) #endif } -inline uint16_t bswap(uint16_t val) +static inline uint16_t bswap(uint16_t val) { #if __GNUC__ return __builtin_bswap16(val); @@ -91,7 +283,7 @@ inline uint16_t bswap(uint16_t val) #endif } -inline int32_t bswap(int32_t val) +static inline int32_t bswap(int32_t val) { #if __GNUC__ return __builtin_bswap32(val); @@ -104,7 +296,7 @@ inline int32_t bswap(int32_t val) #endif } -inline uint32_t bswap(uint32_t val) +static inline uint32_t bswap(uint32_t val) { #if __GNUC__ return __builtin_bswap32(val); @@ -117,7 +309,7 @@ inline uint32_t bswap(uint32_t val) #endif } -inline int64_t bswap(int64_t val) +static inline int64_t bswap(int64_t val) { #if __GNUC__ return __builtin_bswap64(val); @@ -135,7 +327,7 @@ inline int64_t bswap(int64_t val) #endif } -inline uint64_t bswap(uint64_t val) +static inline uint64_t bswap(uint64_t val) { #if __GNUC__ return __builtin_bswap64(val); @@ -153,6 +345,14 @@ inline uint64_t bswap(uint64_t val) #endif } +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +#define HECLMakeBig(val) HECL::bswap(val) +#define HECLMakeLittle(val) (val) +#else +#define HECLMakeBig(val) (val) +#define HECLMakeLittle(val) HECL::bswap(val) +#endif + } #endif // HECL_HPP diff --git a/hecl/include/HECLDatabase.hpp b/hecl/include/HECLDatabase.hpp index baf6483b7..87384a323 100644 --- a/hecl/include/HECLDatabase.hpp +++ b/hecl/include/HECLDatabase.hpp @@ -213,23 +213,16 @@ public: * * Self explanatory */ - virtual std::string getProjectRootPath(bool absolute=false) const=0; + virtual const HECL::ProjectRootPath& getProjectRootPath(bool absolute=false) const=0; /** - * @brief Determine if an arbitrary absolute or relative path lies within project - * @param subpath directory or file to validate - * @return true if valid - */ - virtual bool validateSubPath(const std::string& subpath) const=0; - - /** - * @brief Add a given file or file-pattern to the database + * @brief Add given file(s) to the database * @param path file or pattern within project * @return true on success * * This method blocks while object hashing takes place */ - virtual bool addPath(const std::string& path)=0; + virtual bool addPaths(const std::vector& paths)=0; /** * @brief Remove a given file or file-pattern from the database @@ -240,7 +233,7 @@ public: * This method will not delete actual working files from the project * directory. It will delete associated cooked objects though. */ - virtual bool removePath(const std::string& path, bool recursive=false)=0; + virtual bool removePaths(const std::string& path, bool recursive=false)=0; /** * @brief Register a working sub-directory as a Dependency Group @@ -323,15 +316,15 @@ public: }; /** - * @brief Creates a new (empty) project using specified root directory - * @param rootPath Path to project root-directory (may be relative) + * @brief Opens an existing or creates a new project using specified root directory + * @param rootPath Path to project root-directory * @return New project object * * This is the preferred way to open an existing or create a new HECL project. * All necessary database index files and object directories will be established * within the specified directory path. */ -IProject* NewProject(const std::string& rootPath); +IProject* OpenProject(const HECL::ProjectRootPath& rootPath); /** diff --git a/hecl/lib/HECL.cpp b/hecl/lib/HECL.cpp new file mode 100644 index 000000000..b8f8dfeb9 --- /dev/null +++ b/hecl/lib/HECL.cpp @@ -0,0 +1,9 @@ +#include "HECL.hpp" + +#if _WIN32 +char* win_realpath(const char* name, char* restrict resolved) +{ +} +#endif + + diff --git a/hecl/lib/database/CProject.cpp b/hecl/lib/database/CProject.cpp index 338cd7e21..4936df459 100644 --- a/hecl/lib/database/CProject.cpp +++ b/hecl/lib/database/CProject.cpp @@ -45,11 +45,7 @@ public: { } - std::string getProjectRootPath(bool absolute) const - { - } - - bool validateSubPath(const std::string& subpath) const + const HECL::ProjectRootPath& getProjectRootPath(bool absolute) const { } @@ -89,7 +85,7 @@ public: }; -IProject* NewProject(const std::string& rootPath) +IProject* OpenProject(const std::string& rootPath) { return new CProject(rootPath); } diff --git a/hecl/lib/lib.pro b/hecl/lib/lib.pro index 314457326..ecb6e5aec 100644 --- a/hecl/lib/lib.pro +++ b/hecl/lib/lib.pro @@ -15,3 +15,6 @@ include (frontend/frontend.pri) include (backend/backend.pri) include (database/database.pri) include (runtime/runtime.pri) + +SOURCES += \ + HECL.cpp