#ifndef HECL_HPP #define HECL_HPP #ifndef _WIN32 #include #include #include #include #include #include #include #else #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN 1 #endif #ifndef NOMINMAX #define NOMINMAX #endif #include #include #include "winsupport.hpp" #endif #include #include #include #include #include #include #include #include #include #include #include #include #include "../extern/xxhash/xxhash.h" namespace HECL { namespace Database { class Project; struct DataSpecEntry; } extern unsigned VerbosityLevel; extern LogVisor::LogModule LogModule; #if _WIN32 && UNICODE #define HECL_UCS2 1 #endif std::string WideToUTF8(const std::wstring& src); std::wstring UTF8ToWide(const std::string& src); /* humanize_number port from FreeBSD's libutil */ enum class HNFlags { None = 0, Decimal = 0x01, NoSpace = 0x02, B = 0x04, Divisor1000 = 0x08, IECPrefixes = 0x10 }; ENABLE_BITWISE_ENUM(HNFlags) enum class HNScale { None = 0, AutoScale = 0x20 }; ENABLE_BITWISE_ENUM(HNScale) std::string HumanizeNumber(int64_t quotient, size_t len, const char* suffix, int scale, HNFlags flags); #if HECL_UCS2 typedef wchar_t SystemChar; static inline size_t StrLen(const SystemChar* str) {return wcslen(str);} typedef std::wstring SystemString; static inline void ToLower(SystemString& str) {std::transform(str.begin(), str.end(), str.begin(), towlower);} static inline void ToUpper(SystemString& str) {std::transform(str.begin(), str.end(), str.begin(), towupper);} class SystemUTF8View { std::string m_utf8; public: SystemUTF8View(const SystemString& str) : m_utf8(WideToUTF8(str)) {} operator const std::string&() const {return m_utf8;} const std::string& str() const {return m_utf8;} const char* c_str() const {return m_utf8.c_str();} std::string operator+(const std::string& other) const {return m_utf8 + other;} std::string operator+(const char* other) const {return m_utf8 + other;} }; inline std::string operator+(const std::string& lhs, const SystemUTF8View& rhs) {return lhs + std::string(rhs);} inline std::string operator+(const char* lhs, const SystemUTF8View& rhs) {return lhs + std::string(rhs);} class SystemStringView { std::wstring m_sys; public: SystemStringView(const std::string& str) : m_sys(UTF8ToWide(str)) {} operator const std::wstring&() const {return m_sys;} const std::wstring& sys_str() const {return m_sys;} const SystemChar* c_str() const {return m_sys.c_str();} std::wstring operator+(const std::wstring& other) const {return m_sys + other;} std::wstring operator+(const wchar_t* other) const {return m_sys + other;} }; inline std::wstring operator+(const std::wstring& lhs, const SystemStringView& rhs) {return lhs + std::wstring(rhs);} inline std::wstring operator+(const wchar_t* lhs, const SystemStringView& rhs) {return lhs + std::wstring(rhs);} #ifndef _S #define _S(val) L ## val #endif typedef struct _stat Sstat; #else typedef char SystemChar; static inline size_t StrLen(const SystemChar* str) {return strlen(str);} typedef std::string SystemString; static inline void ToLower(SystemString& str) {std::transform(str.begin(), str.end(), str.begin(), tolower);} static inline void ToUpper(SystemString& str) {std::transform(str.begin(), str.end(), str.begin(), toupper);} class SystemUTF8View { const std::string& m_utf8; public: SystemUTF8View(const SystemString& str) : m_utf8(str) {} operator const std::string&() const {return m_utf8;} const std::string& str() const {return m_utf8;} const char* c_str() const {return m_utf8.c_str();} std::string operator+(const std::string& other) const {return std::string(m_utf8) + other;} std::string operator+(const char* other) const {return std::string(m_utf8) + other;} }; inline std::string operator+(const std::string& lhs, const SystemUTF8View& rhs) {return lhs + std::string(rhs);} inline std::string operator+(const char* lhs, const SystemUTF8View& rhs) {return lhs + std::string(rhs);} class SystemStringView { const std::string& m_sys; public: SystemStringView(const std::string& str) : m_sys(str) {} operator const std::string&() const {return m_sys;} const std::string& sys_str() const {return m_sys;} const SystemChar* c_str() const {return m_sys.c_str();} std::string operator+(const std::string& other) const {return m_sys + other;} std::string operator+(const char* other) const {return m_sys + other;} }; inline std::string operator+(const std::string& lhs, const SystemStringView& rhs) {return lhs + std::string(rhs);} inline std::string operator+(const char* lhs, const SystemStringView& rhs) {return lhs + std::string(rhs);} #ifndef _S #define _S(val) val #endif typedef struct stat Sstat; #endif void SanitizePath(std::string& path); void SanitizePath(std::wstring& path); static inline void Unlink(const SystemChar* file) { #if _WIN32 _wunlink(file); #else unlink(file); #endif } static inline void MakeDir(const char* dir) { #if _WIN32 HRESULT err; if (!CreateDirectoryA(dir, NULL)) if ((err = GetLastError()) != ERROR_ALREADY_EXISTS) LogModule.report(LogVisor::FatalError, _S("MakeDir(%s)"), dir); #else if (mkdir(dir, 0755)) if (errno != EEXIST) LogModule.report(LogVisor::FatalError, "MakeDir(%s): %s", dir, strerror(errno)); #endif } #if _WIN32 static inline void MakeDir(const wchar_t* dir) { HRESULT err; if (!CreateDirectoryW(dir, NULL)) if ((err = GetLastError()) != ERROR_ALREADY_EXISTS) LogModule.report(LogVisor::FatalError, _S("MakeDir(%s)"), dir); } #endif static inline void MakeLink(const SystemChar* target, const SystemChar* linkPath) { #if _WIN32 HRESULT res = CreateShellLink(target, linkPath, _S("HECL Link")); /* :(( */ if (!SUCCEEDED(res)) { LPWSTR messageBuffer = nullptr; size_t size = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, res, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&messageBuffer, 0, NULL); /* :((( */ LogModule.report(LogVisor::FatalError, _S("MakeLink(%s, %s): %s"), target, linkPath, messageBuffer); } #else if (symlink(target, linkPath)) /* :) */ if (errno != EEXIST) LogModule.report(LogVisor::FatalError, "MakeLink(%s, %s): %s", target, linkPath, strerror(errno)); #endif } static inline SystemChar* Getcwd(SystemChar* buf, int maxlen) { #if HECL_UCS2 return _wgetcwd(buf, maxlen); #else return getcwd(buf, maxlen); #endif } static SystemString GetcwdStr() { /* http://stackoverflow.com/a/2869667 */ //const size_t ChunkSize=255; //const int MaxChunks=10240; // 2550 KiBs of current path are more than enough SystemChar stackBuffer[255]; // Stack buffer for the "normal" case if (Getcwd(stackBuffer, 255) != nullptr) return SystemString(stackBuffer); if (errno != ERANGE) { // It's not ERANGE, so we don't know how to handle it LogModule.report(LogVisor::FatalError, "Cannot determine the current path."); // Of course you may choose a different error reporting method } // Ok, the stack buffer isn't long enough; fallback to heap allocation for (int chunks=2 ; chunks<10240 ; chunks++) { // With boost use scoped_ptr; in C++0x, use unique_ptr // If you want to be less C++ but more efficient you may want to use realloc std::unique_ptr cwd(new SystemChar[255*chunks]); if (Getcwd(cwd.get(), 255*chunks) != nullptr) return SystemString(cwd.get()); if (errno != ERANGE) { // It's not ERANGE, so we don't know how to handle it LogModule.report(LogVisor::FatalError, "Cannot determine the current path."); // Of course you may choose a different error reporting method } } LogModule.report(LogVisor::FatalError, "Cannot determine the current path; the path is apparently unreasonably long"); return SystemString(); } static inline bool IsAbsolute(const SystemString& path) { #if _WIN32 if (path.size() && (path[0] == _S('\\') || path[0] == _S('/'))) return true; if (path.size() >= 2 && iswalpha(path[0]) && path[1] == _S(':')) return true; #else if (path.size() && path[0] == _S('/')) return true; #endif return false; } enum class FileLockType { None = 0, Read, Write }; static inline FILE* Fopen(const SystemChar* path, const SystemChar* mode, FileLockType lock=FileLockType::None) { #if HECL_UCS2 FILE* fp = _wfopen(path, mode); if (!fp) LogModule.report(LogVisor::FatalError, L"fopen %s: %s", path, _wcserror(errno)); #else FILE* fp = fopen(path, mode); if (!fp) LogModule.report(LogVisor::FatalError, "fopen %s: %s", path, strerror(errno)); #endif if (lock != FileLockType::None) { #if _WIN32 OVERLAPPED ov = {}; LockFileEx((HANDLE)(uintptr_t)_fileno(fp), (lock == FileLockType::Write) ? LOCKFILE_EXCLUSIVE_LOCK : 0, 0, 0, 1, &ov); #else if (flock(fileno(fp), ((lock == FileLockType::Write) ? LOCK_EX : LOCK_SH) | LOCK_NB)) LogModule.report(LogVisor::FatalError, "flock %s: %s", path, strerror(errno)); #endif } return fp; } static inline int Stat(const SystemChar* path, Sstat* statOut) { #if HECL_UCS2 return _wstat(path, statOut); #else return stat(path, statOut); #endif } #if __GNUC__ __attribute__((__format__ (__printf__, 1, 2))) #endif static inline void Printf(const SystemChar* format, ...) { va_list va; va_start(va, format); #if HECL_UCS2 vwprintf(format, va); #else vprintf(format, va); #endif va_end(va); } #if __GNUC__ __attribute__((__format__ (__printf__, 2, 3))) #endif static inline void FPrintf(FILE* fp, const SystemChar* format, ...) { va_list va; va_start(va, format); #if HECL_UCS2 vfwprintf(fp, format, va); #else vfprintf(fp, format, va); #endif va_end(va); } #if __GNUC__ __attribute__((__format__ (__printf__, 3, 4))) #endif static inline void SNPrintf(SystemChar* str, size_t maxlen, const SystemChar* format, ...) { va_list va; va_start(va, format); #if HECL_UCS2 _vsnwprintf(str, maxlen, format, va); #else vsnprintf(str, maxlen, format, va); #endif va_end(va); } static inline int StrCmp(const SystemChar* str1, const SystemChar* str2) { #if HECL_UCS2 return wcscmp(str1, str2); #else return strcmp(str1, str2); #endif } #define FORMAT_BUF_SZ 1024 #if __GNUC__ __attribute__((__format__ (__printf__, 1, 2))) #endif static inline SystemString SysFormat(const SystemChar* format, ...) { SystemChar resultBuf[FORMAT_BUF_SZ]; va_list va; va_start(va, format); #if HECL_UCS2 int printSz = vswprintf(resultBuf, FORMAT_BUF_SZ, format, va); #else int printSz = vsnprintf(resultBuf, FORMAT_BUF_SZ, format, va); #endif va_end(va); return SystemString(resultBuf, printSz); } #if __GNUC__ __attribute__((__format__ (__printf__, 1, 2))) #endif static inline std::string Format(const char* format, ...) { char resultBuf[FORMAT_BUF_SZ]; va_list va; va_start(va, format); int printSz = vsnprintf(resultBuf, FORMAT_BUF_SZ, format, va); va_end(va); return std::string(resultBuf, printSz); } static inline int ConsoleWidth() { int retval = 80; #if _WIN32 CONSOLE_SCREEN_BUFFER_INFO info; GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &info); retval = info.dwSize.X; #else struct winsize w; if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) != -1) retval = w.ws_col; #endif if (retval < 10) return 10; return retval; } typedef std::basic_regex SystemRegex; typedef std::regex_token_iterator SystemRegexTokenIterator; typedef std::match_results SystemRegexMatch; class ProjectRootPath; /** * @brief FourCC representation used within HECL's database * * FourCCs are efficient, mnemonic four-char-sequences used to represent types * while fitting comfortably in a 32-bit word. HECL uses a four-char array * to remain endian-independent. */ class FourCC { protected: union { char fcc[4]; uint32_t num; }; public: FourCC() /* Sentinel FourCC */ : num(0) {} FourCC(const FourCC& other) {num = other.num;} FourCC(const char* name) : num(*(uint32_t*)name) {} FourCC(uint32_t n) : num(n) {} bool operator==(const FourCC& other) const {return num == other.num;} bool operator!=(const FourCC& other) const {return num != other.num;} bool operator==(const char* other) const {return num == *(uint32_t*)other;} bool operator!=(const char* other) const {return num != *(uint32_t*)other;} bool operator==(uint32_t other) const {return num == other;} bool operator!=(uint32_t other) const {return num != other;} std::string toString() const {return std::string(fcc, 4);} uint32_t toUint32() const {return num;} operator uint32_t() const {return num;} }; #define FOURCC(chars) FourCC(SBIG(chars)) /** * @brief Hash representation used for all storable and comparable objects * * Hashes are used within HECL to avoid redundant storage of objects; * providing a rapid mechanism to compare for equality. */ class Hash { protected: uint64_t hash = 0; public: Hash() = default; operator bool() const {return hash != 0;} Hash(const void* buf, size_t len) : hash(XXH64((uint8_t*)buf, len, 0)) {} Hash(const std::string& str) : hash(XXH64((uint8_t*)str.data(), str.size(), 0)) {} Hash(const std::wstring& str) : hash(XXH64((uint8_t*)str.data(), str.size()*2, 0)) {} Hash(uint64_t hashin) : hash(hashin) {} Hash(const Hash& other) {hash = other.hash;} uint32_t val32() const {return uint32_t(hash);} uint64_t val64() const {return uint64_t(hash);} size_t valSizeT() const {return size_t(hash);} Hash& operator=(const Hash& other) {hash = other.hash; return *this;} bool operator==(const Hash& other) const {return hash == other.hash;} bool operator!=(const Hash& other) const {return hash != other.hash;} bool operator<(const Hash& other) const {return hash < other.hash;} bool operator>(const Hash& other) const {return hash > other.hash;} bool operator<=(const Hash& other) const {return hash <= other.hash;} bool operator>=(const Hash& other) const {return hash >= other.hash;} }; /** * @brief Timestamp representation used for comparing modtimes of cooked resources */ class Time final { time_t ts; public: Time() : ts(time(NULL)) {} Time(time_t ti) : ts(ti) {} Time(const Time& other) {ts = other.ts;} time_t getTs() const {return ts;} Time& operator=(const Time& other) {ts = other.ts; return *this;} bool operator==(const Time& other) const {return ts == other.ts;} bool operator!=(const Time& other) const {return ts != other.ts;} bool operator<(const Time& other) const {return ts < other.ts;} bool operator>(const Time& other) const {return ts > other.ts;} bool operator<=(const Time& other) const {return ts <= other.ts;} bool operator>=(const Time& other) const {return ts >= other.ts;} }; /** * @brief Case-insensitive comparator for std::map sorting */ struct CaseInsensitiveCompare { bool operator()(const std::string& lhs, const std::string& rhs) const { #if _WIN32 if (_stricmp(lhs.c_str(), rhs.c_str()) < 0) #else if (strcasecmp(lhs.c_str(), rhs.c_str()) < 0) #endif return true; return false; } #if _WIN32 bool operator()(const std::wstring& lhs, const std::wstring& rhs) const { if (_wcsicmp(lhs.c_str(), rhs.c_str()) < 0) return true; return false; } #endif }; /** * @brief Directory traversal tool for accessing sorted directory entries */ class DirectoryEnumerator { public: enum class Mode { Native, DirsSorted, FilesSorted, DirsThenFilesSorted }; struct Entry { HECL::SystemString m_path; HECL::SystemString m_name; size_t m_fileSz; bool m_isDir; private: friend class DirectoryEnumerator; Entry(HECL::SystemString&& path, const HECL::SystemChar* name, size_t sz, bool isDir) : m_path(std::move(path)), m_name(name), m_fileSz(sz), m_isDir(isDir) {} }; private: std::vector m_entries; public: DirectoryEnumerator(const HECL::SystemString& path, Mode mode=Mode::DirsThenFilesSorted, bool sizeSort=false, bool reverse=false, bool noHidden=false) : DirectoryEnumerator(path.c_str(), mode, sizeSort, reverse, noHidden) {} DirectoryEnumerator(const HECL::SystemChar* path, Mode mode=Mode::DirsThenFilesSorted, bool sizeSort=false, bool reverse=false, bool noHidden=false); operator bool() const {return m_entries.size() != 0;} size_t size() const {return m_entries.size();} std::vector::const_iterator begin() const {return m_entries.cbegin();} std::vector::const_iterator end() const {return m_entries.cend();} }; /** * @brief Build list of common OS-specific directories */ std::vector GetSystemLocations(); /** * @brief Special ProjectRootPath class for opening HECLDatabase::IProject instances * * Constructing a ProjectPath requires supplying a ProjectRootPath to consistently * resolve canonicalized relative paths. */ class ProjectRootPath { SystemString m_projRoot; Hash m_hash = 0; public: /** * @brief Empty constructor * * Used to preallocate ProjectPath for later population using assign() */ ProjectRootPath() = default; /** * @brief Tests for non-empty project root path */ operator bool() const {return m_projRoot.size() != 0;} /** * @brief Construct a representation of a project root path * @param path valid filesystem-path (relative or absolute) to project root */ ProjectRootPath(const SystemString& path) : m_projRoot(path) { SanitizePath(m_projRoot); m_hash = Hash(m_projRoot); } /** * @brief Access fully-canonicalized absolute path * @return Absolute path reference */ const SystemString& getAbsolutePath() const {return m_projRoot;} /** * @brief Make absolute path project relative * @param absPath Absolute path * @return SystemString of path relative to project root */ SystemString getProjectRelativeFromAbsolute(const SystemString& absPath) const { if (absPath.size() > m_projRoot.size()) { if (!absPath.compare(0, m_projRoot.size(), m_projRoot)) { auto beginIt = absPath.cbegin() + m_projRoot.size(); while (*beginIt == _S('/') || *beginIt == _S('\\')) ++beginIt; return SystemString(beginIt, absPath.cend()); } } LogModule.report(LogVisor::FatalError, "unable to resolve '%s' as project relative '%s'", absPath.c_str(), m_projRoot.c_str()); return SystemString(); } /** * @brief Create directory at path * * Fatal log report is issued if directory is not able to be created or doesn't already exist. * If directory already exists, no action taken. */ void makeDir() const {MakeDir(m_projRoot.c_str());} /** * @brief HECL-specific xxhash * @return unique hash value */ Hash hash() const {return m_hash;} bool operator==(const ProjectRootPath& other) const {return m_hash == other.m_hash;} bool operator!=(const ProjectRootPath& other) const {return m_hash != other.m_hash;} }; /** * @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 { Database::Project* m_proj = nullptr; SystemString m_absPath; SystemString m_relPath; Hash m_hash = 0; #if HECL_UCS2 std::string m_utf8AbsPath; std::string m_utf8RelPath; #endif public: /** * @brief Empty constructor * * Used to preallocate ProjectPath for later population using assign() */ ProjectPath() = default; /** * @brief Tests for non-empty project path */ operator bool() const {return m_absPath.size() != 0;} /** * @brief Clears path */ void clear() { m_proj = nullptr; m_absPath.clear(); m_relPath.clear(); m_hash = 0; #if HECL_UCS2 m_utf8AbsPath.clear(); m_utf8RelPath.clear(); #endif } /** * @brief Construct a project subpath representation within a project's root path * @param project previously constructed Project to use root path of * @param path valid filesystem-path (relative or absolute) to subpath */ ProjectPath(Database::Project& project, const SystemString& path) {assign(project, path);} void assign(Database::Project& project, const SystemString& path); #if HECL_UCS2 ProjectPath(Database::Project& project, const std::string& path) {assign(project, path);} void assign(Database::Project& project, const std::string& path); #endif /** * @brief Construct a project subpath representation within another subpath * @param parentPath previously constructed ProjectPath which ultimately connects to a ProjectRootPath * @param path valid filesystem-path (relative or absolute) to subpath */ ProjectPath(const ProjectPath& parentPath, const SystemString& path) {assign(parentPath, path);} void assign(const ProjectPath& parentPath, const SystemString& path); #if HECL_UCS2 ProjectPath(const ProjectPath& parentPath, const std::string& path) {assign(parentPath, path);} void assign(const ProjectPath& parentPath, const std::string& path); #endif /** * @brief Determine if ProjectPath represents project root directory * @return true if project root directory */ bool isRoot() const {return m_relPath.empty();} /** * @brief Return new ProjectPath with extension added * @param ext file extension to add (nullptr may be passed to remove the extension) * @param replace remove existing extension (if any) before appending new extension * @return new path with extension */ ProjectPath getWithExtension(const SystemChar* ext, bool replace=false) const { ProjectPath pp(*this); if (replace) { auto relIt = pp.m_relPath.end(); auto absIt = pp.m_absPath.end(); while (relIt != pp.m_relPath.begin() && *relIt != _S('.') && *relIt != _S('/')) { --relIt; --absIt; } if (*relIt == _S('.') && relIt != pp.m_relPath.begin()) { pp.m_relPath.resize(relIt - pp.m_relPath.begin()); pp.m_absPath.resize(absIt - pp.m_absPath.begin()); } } if (ext) { pp.m_relPath += ext; pp.m_absPath += ext; } #if HECL_UCS2 pp.m_utf8AbsPath = WideToUTF8(pp.m_absPath); pp.m_utf8RelPath = WideToUTF8(pp.m_relPath); #endif return pp; } /** * @brief Access fully-canonicalized absolute path * @return Absolute path reference */ const SystemString& getAbsolutePath() const {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) */ const SystemString& getRelativePath() const { if (m_relPath.size()) return m_relPath; static const SystemString dot = _S("."); return dot; } /** * @brief Obtain cooked equivalent of this ProjectPath * @param spec DataSpec to get path against * @return Cooked representation path */ ProjectPath getCookedPath(const Database::DataSpecEntry& spec) const; /** * @brief Obtain path of parent entity (a directory for file paths) * @return Parent Path * * This will not resolve outside the project root (error in that case) */ ProjectPath getParentPath() const { if (m_relPath == _S(".")) LogModule.report(LogVisor::FatalError, "attempted to resolve parent of root project path"); size_t pos = m_relPath.rfind(_S('/')); if (pos == SystemString::npos) return ProjectPath(*m_proj, _S("")); return ProjectPath(*m_proj, SystemString(m_relPath.begin(), m_relPath.begin() + pos)); } /** * @brief Obtain c-string of final path component (stored within relative path) * @return Final component c-string (may be empty) */ const SystemChar* getLastComponent() const { size_t pos = m_relPath.rfind(_S('/')); if (pos == SystemString::npos) return m_relPath.c_str() + m_relPath.size(); return m_relPath.c_str() + pos + 1; } const char* getLastComponentUTF8() const { size_t pos = m_relPath.rfind(_S('/')); #if HECL_UCS2 if (pos == SystemString::npos) return m_utf8RelPath.c_str() + m_utf8RelPath.size(); return m_utf8RelPath.c_str() + pos + 1; #else if (pos == SystemString::npos) return m_relPath.c_str() + m_relPath.size(); return m_relPath.c_str() + pos + 1; #endif } /** * @brief Obtain c-string of extension of final path component (stored within relative path) * @return Final component extension c-string (may be nullptr) */ const SystemChar* getLastComponentExt() const { const SystemChar* lastCompOrig = getLastComponent(); const SystemChar* lastComp = lastCompOrig; while (*lastComp != _S('\0')) ++lastComp; while (lastComp != lastCompOrig) { if (*lastComp == _S('.')) return lastComp + 1; --lastComp; } return nullptr; } /** * @brief Access fully-canonicalized absolute path in UTF-8 * @return Absolute path reference */ const std::string& getAbsolutePathUTF8() const { #if HECL_UCS2 return m_utf8AbsPath; #else return m_absPath; #endif } const std::string& getRelativePathUTF8() const { #if HECL_UCS2 return m_utf8RelPath; #else return m_relPath; #endif } /** * @brief Type of path */ enum class Type { None, /**< If path doesn't reference a valid filesystem entity, this is returned */ File, /**< Singular file path (confirmed with filesystem) */ Directory, /**< Singular directory path (confirmed with filesystem) */ Glob, /**< Glob-path (whenever one or more '*' occurs in syntax) */ Link /**< Link (symlink on POSIX, ShellLink on Windows) */ }; /** * @brief Get type of path based on syntax and filesystem queries * @return Type of path */ Type getPathType() const; /** * @brief Get time of last modification with special behaviors for directories and glob-paths * @return Time object representing entity's time of last modification * * Regular files simply return their modtime as queried from the OS * Directories return the latest modtime of all first-level regular files * Glob-paths return the latest modtime of all matched regular files */ Time getModtime() const; /** * @brief For link paths, get the target path * @return Target path */ ProjectPath resolveLink() const; /** * @brief Insert directory children into list * @param outPaths list to append children to */ void getDirChildren(std::map& outPaths) const; /** * @brief Insert glob matches into existing vector * @param outPaths Vector to add matches to (will not erase existing contents) */ void getGlobResults(std::vector& outPaths) const; /** * @brief Count how many directory levels deep in project path is * @return Level Count */ size_t levelCount() const { size_t count = 0; for (SystemChar ch : m_relPath) if (ch == _S('/') || ch == _S('\\')) ++count; return count; } /** * @brief Create directory at path * * Fatal log report is issued if directory is not able to be created or doesn't already exist. * If directory already exists, no action taken. */ void makeDir() const {MakeDir(m_absPath.c_str());} /** * @brief Create relative symbolic link at calling path targeting another path * @param target Path to target */ void makeLinkTo(const ProjectPath& target) const { SystemString relTarget; for (SystemChar ch : m_relPath) if (ch == _S('/') || ch == _S('\\')) relTarget += _S("../"); relTarget += target.m_relPath; MakeLink(relTarget.c_str(), m_absPath.c_str()); } /** * @brief Fetch project that contains path * @return Project */ Database::Project& getProject() const { if (!m_proj) LogModule.report(LogVisor::FatalError, "ProjectPath::getProject() called on unqualified path"); return *m_proj; } /** * @brief HECL-specific xxhash * @return unique hash value */ Hash hash() const {return m_hash;} bool operator==(const ProjectPath& other) const {return m_hash == other.m_hash;} bool operator!=(const ProjectPath& other) const {return m_hash != other.m_hash;} }; /** * @brief Search from within provided directory for the project root * @param path absolute or relative file path to search from * @return Newly-constructed root path (bool-evaluating to false if not found) */ ProjectRootPath SearchForProject(const SystemString& path); /** * @brief Search from within provided directory for the project root * @param path absolute or relative file path to search from * @param subpathOut remainder of provided path assigned to this ProjectPath * @return Newly-constructed root path (bool-evaluating to false if not found) */ ProjectRootPath SearchForProject(const SystemString& path, SystemString& subpathOut); /** * @brief Test if given path is a PNG (based on file header) * @param path Path to test * @return true if PNG */ bool IsPathPNG(const HECL::ProjectPath& path); /** * @brief Test if given path is a blend (based on file header) * @param path Path to test * @return true if blend */ bool IsPathBlend(const HECL::ProjectPath& path); /** * @brief Test if given path is a yaml (based on file extension) * @param path Path to test * @return true if yaml */ bool IsPathYAML(const HECL::ProjectPath& path); #undef bswap16 #undef bswap32 #undef bswap64 /* Type-sensitive byte swappers */ template static inline T bswap16(T val) { #if __GNUC__ return __builtin_bswap16(val); #elif _WIN32 return _byteswap_ushort(val); #else return (val = (val << 8) | ((val >> 8) & 0xFF)); #endif } template static inline T bswap32(T val) { #if __GNUC__ return __builtin_bswap32(val); #elif _WIN32 return _byteswap_ulong(val); #else val = (val & 0x0000FFFF) << 16 | (val & 0xFFFF0000) >> 16; val = (val & 0x00FF00FF) << 8 | (val & 0xFF00FF00) >> 8; return val; #endif } template static inline T bswap64(T val) { #if __GNUC__ return __builtin_bswap64(val); #elif _WIN32 return _byteswap_uint64(val); #else return ((val & 0xFF00000000000000ULL) >> 56) | ((val & 0x00FF000000000000ULL) >> 40) | ((val & 0x0000FF0000000000ULL) >> 24) | ((val & 0x000000FF00000000ULL) >> 8) | ((val & 0x00000000FF000000ULL) << 8) | ((val & 0x0000000000FF0000ULL) << 24) | ((val & 0x000000000000FF00ULL) << 40) | ((val & 0x00000000000000FFULL) << 56); #endif } #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ static inline int16_t SBig(int16_t val) {return bswap16(val);} static inline uint16_t SBig(uint16_t val) {return bswap16(val);} static inline int32_t SBig(int32_t val) {return bswap32(val);} static inline uint32_t SBig(uint32_t val) {return bswap32(val);} static inline int64_t SBig(int64_t val) {return bswap64(val);} static inline uint64_t SBig(uint64_t val) {return bswap64(val);} #define SBIG(q) ( ( (q) & 0x000000FF ) << 24 | ( (q) & 0x0000FF00 ) << 8 \ | ( (q) & 0x00FF0000 ) >> 8 | ( (q) & 0xFF000000 ) >> 24 ) static inline int16_t SLittle(int16_t val) {return val;} static inline uint16_t SLittle(uint16_t val) {return val;} static inline int32_t SLittle(int32_t val) {return val;} static inline uint32_t SLittle(uint32_t val) {return val;} static inline int64_t SLittle(int64_t val) {return val;} static inline uint64_t SLittle(uint64_t val) {return val;} #define SLITTLE(q) (q) #else static inline int16_t SLittle(int16_t val) {return bswap16(val);} static inline uint16_t SLittle(uint16_t val) {return bswap16(val);} static inline int32_t SLittle(int32_t val) {return bswap32(val);} static inline uint32_t SLittle(uint32_t val) {return bswap32(val);} static inline int64_t SLittle(int64_t val) {return bswap64(val);} static inline uint64_t SLittle(uint64_t val) {return bswap64(val);} #define SLITTLE(q) ( ( (q) & 0x000000FF ) << 24 | ( (q) & 0x0000FF00 ) << 8 \ | ( (q) & 0x00FF0000 ) >> 8 | ( (q) & 0xFF000000 ) >> 24 ) static inline int16_t SBig(int16_t val) {return val;} static inline uint16_t SBig(uint16_t val) {return val;} static inline int32_t SBig(int32_t val) {return val;} static inline uint32_t SBig(uint32_t val) {return val;} static inline int64_t SBig(int64_t val) {return val;} static inline uint64_t SBig(uint64_t val) {return val;} #define SBIG(q) (q) #endif } #if _MSC_VER #define NOEXCEPT #else #define NOEXCEPT noexcept #endif namespace std { template <> struct hash { size_t operator()(const HECL::FourCC& val) const NOEXCEPT {return val.toUint32();} }; template <> struct hash { size_t operator()(const HECL::ProjectPath& val) const NOEXCEPT {return val.hash().valSizeT();} }; template <> struct hash { size_t operator()(const HECL::Hash& val) const NOEXCEPT {return val.valSizeT();} }; } #endif // HECL_HPP