2018-10-06 20:38:44 -07:00
|
|
|
#pragma once
|
2015-05-19 22:22:32 -07:00
|
|
|
|
2015-07-17 21:35:01 -07:00
|
|
|
#ifndef _WIN32
|
2017-12-28 23:56:31 -08:00
|
|
|
#include <cstdlib>
|
2015-05-27 02:09:05 -07:00
|
|
|
#include <sys/stat.h>
|
2015-06-11 02:41:10 -07:00
|
|
|
#include <sys/file.h>
|
2015-07-20 16:27:22 -07:00
|
|
|
#include <sys/ioctl.h>
|
2015-05-27 02:09:05 -07:00
|
|
|
#include <dirent.h>
|
2015-06-10 16:34:14 -07:00
|
|
|
#include <fcntl.h>
|
2015-07-01 16:53:05 -07:00
|
|
|
#include <unistd.h>
|
2016-01-24 18:16:40 -08:00
|
|
|
#include <sys/statvfs.h>
|
2019-02-26 21:13:19 -08:00
|
|
|
#if __linux__ || __APPLE__
|
|
|
|
extern "C" int rep_closefrom(int lower);
|
|
|
|
#define closefrom rep_closefrom
|
|
|
|
#endif
|
2015-07-22 12:14:50 -07:00
|
|
|
#else
|
2015-08-30 20:36:24 -07:00
|
|
|
#ifndef WIN32_LEAN_AND_MEAN
|
|
|
|
#define WIN32_LEAN_AND_MEAN 1
|
|
|
|
#endif
|
2015-09-26 21:35:36 -07:00
|
|
|
#ifndef NOMINMAX
|
|
|
|
#define NOMINMAX
|
|
|
|
#endif
|
2015-07-22 12:14:50 -07:00
|
|
|
#include <Windows.h>
|
2017-12-28 23:56:31 -08:00
|
|
|
#include <cwchar>
|
2019-08-14 20:20:25 -07:00
|
|
|
#include <cwctype>
|
2017-02-24 23:58:36 -08:00
|
|
|
#include <Shlwapi.h>
|
2015-08-30 20:36:24 -07:00
|
|
|
#include "winsupport.hpp"
|
2015-05-27 02:09:05 -07:00
|
|
|
#endif
|
|
|
|
|
2019-08-14 20:20:25 -07:00
|
|
|
#include <algorithm>
|
2017-12-28 23:56:31 -08:00
|
|
|
#include <cinttypes>
|
|
|
|
#include <cstdarg>
|
|
|
|
#include <cstdio>
|
2019-08-14 20:20:25 -07:00
|
|
|
#include <ctime>
|
2015-05-19 22:22:32 -07:00
|
|
|
#include <functional>
|
2015-09-29 23:23:07 -07:00
|
|
|
#include <list>
|
2015-10-14 16:06:47 -07:00
|
|
|
#include <map>
|
2019-08-14 20:20:25 -07:00
|
|
|
#include <regex>
|
|
|
|
#include <string>
|
|
|
|
|
2016-03-04 15:02:44 -08:00
|
|
|
#include "logvisor/logvisor.hpp"
|
2017-12-28 23:56:31 -08:00
|
|
|
#include "athena/Global.hpp"
|
2021-04-06 12:02:50 -07:00
|
|
|
#include "xxhash/xxhash.h"
|
2017-06-10 11:22:40 -07:00
|
|
|
#include "SystemChar.hpp"
|
2017-12-28 23:56:31 -08:00
|
|
|
#include "FourCC.hpp"
|
2015-05-19 22:22:32 -07:00
|
|
|
|
2018-06-01 17:02:20 -07:00
|
|
|
#if defined(__has_feature)
|
|
|
|
#if __has_feature(thread_sanitizer)
|
|
|
|
#define HECL_NO_SANITIZE_THREAD __attribute__((no_sanitize("thread")))
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
#ifndef HECL_NO_SANITIZE_THREAD
|
|
|
|
#define HECL_NO_SANITIZE_THREAD
|
|
|
|
#endif
|
|
|
|
|
2018-12-07 21:18:42 -08:00
|
|
|
namespace hecl {
|
|
|
|
namespace Database {
|
2015-09-29 23:23:07 -07:00
|
|
|
class Project;
|
2015-10-11 21:38:49 -07:00
|
|
|
struct DataSpecEntry;
|
2018-12-07 21:18:42 -08:00
|
|
|
} // namespace Database
|
2015-09-29 23:23:07 -07:00
|
|
|
|
2018-12-07 21:18:42 -08:00
|
|
|
namespace blender {
|
2019-10-01 00:23:35 -07:00
|
|
|
enum class BlendType { None, Mesh, ColMesh, Armature, Actor, Area,
|
|
|
|
World, MapArea, MapUniverse, Frame, PathMesh };
|
2017-12-28 23:56:31 -08:00
|
|
|
|
2019-09-04 14:22:05 -07:00
|
|
|
class ANIMOutStream;
|
2017-12-28 23:56:31 -08:00
|
|
|
class Connection;
|
|
|
|
class DataStream;
|
|
|
|
class PyOutStream;
|
2019-09-04 14:22:05 -07:00
|
|
|
class Token;
|
|
|
|
|
|
|
|
struct Action;
|
|
|
|
struct Actor;
|
|
|
|
struct Armature;
|
|
|
|
struct Bone;
|
2017-12-28 23:56:31 -08:00
|
|
|
struct ColMesh;
|
|
|
|
struct Light;
|
|
|
|
struct MapArea;
|
|
|
|
struct MapUniverse;
|
2019-09-04 14:22:05 -07:00
|
|
|
struct Material;
|
2017-12-28 23:56:31 -08:00
|
|
|
struct Matrix3f;
|
2018-04-07 13:52:35 -07:00
|
|
|
struct Matrix4f;
|
2019-09-04 14:22:05 -07:00
|
|
|
struct Mesh;
|
|
|
|
struct PathMesh;
|
2017-12-28 23:56:31 -08:00
|
|
|
struct PoolSkinIndex;
|
2019-09-04 14:22:05 -07:00
|
|
|
struct World;
|
2017-12-28 23:56:31 -08:00
|
|
|
|
|
|
|
extern class Token SharedBlenderToken;
|
2018-12-07 21:18:42 -08:00
|
|
|
} // namespace blender
|
2017-12-28 23:56:31 -08:00
|
|
|
|
2015-08-06 12:10:12 -07:00
|
|
|
extern unsigned VerbosityLevel;
|
2017-12-15 18:13:20 -08:00
|
|
|
extern bool GuiMode;
|
2016-03-04 15:02:44 -08:00
|
|
|
extern logvisor::Module LogModule;
|
2015-07-05 18:35:08 -07:00
|
|
|
|
2017-11-12 22:13:53 -08:00
|
|
|
std::string WideToUTF8(std::wstring_view src);
|
|
|
|
std::string Char16ToUTF8(std::u16string_view src);
|
|
|
|
std::wstring Char16ToWide(std::u16string_view src);
|
|
|
|
std::wstring UTF8ToWide(std::string_view src);
|
|
|
|
std::u16string UTF8ToChar16(std::string_view src);
|
2015-06-09 15:19:59 -07:00
|
|
|
|
2015-12-30 14:03:37 -08:00
|
|
|
/* humanize_number port from FreeBSD's libutil */
|
2018-12-07 21:18:42 -08:00
|
|
|
enum class HNFlags { None = 0, Decimal = 0x01, NoSpace = 0x02, B = 0x04, Divisor1000 = 0x08, IECPrefixes = 0x10 };
|
2015-12-30 14:03:37 -08:00
|
|
|
ENABLE_BITWISE_ENUM(HNFlags)
|
|
|
|
|
2018-12-07 21:18:42 -08:00
|
|
|
enum class HNScale { None = 0, AutoScale = 0x20 };
|
2015-12-30 14:03:37 -08:00
|
|
|
ENABLE_BITWISE_ENUM(HNScale)
|
|
|
|
|
|
|
|
std::string HumanizeNumber(int64_t quotient, size_t len, const char* suffix, int scale, HNFlags flags);
|
|
|
|
|
2017-06-10 11:40:27 -07:00
|
|
|
#if HECL_UCS2
|
2018-12-07 21:18:42 -08:00
|
|
|
class SystemUTF8Conv {
|
|
|
|
std::string m_utf8;
|
|
|
|
|
2017-06-10 11:40:27 -07:00
|
|
|
public:
|
2018-12-07 21:18:42 -08:00
|
|
|
explicit SystemUTF8Conv(SystemStringView str) : m_utf8(WideToUTF8(str)) {}
|
2019-08-14 22:51:23 -07:00
|
|
|
|
2018-12-07 21:18:42 -08:00
|
|
|
std::string_view str() const { return m_utf8; }
|
|
|
|
const char* c_str() const { return m_utf8.c_str(); }
|
2019-08-14 22:51:23 -07:00
|
|
|
|
|
|
|
friend std::string operator+(const SystemUTF8Conv& lhs, std::string_view rhs) { return lhs.m_utf8 + rhs.data(); }
|
|
|
|
friend std::string operator+(std::string_view lhs, const SystemUTF8Conv& rhs) {
|
|
|
|
return std::string(lhs).append(rhs.m_utf8);
|
|
|
|
}
|
2017-06-10 11:40:27 -07:00
|
|
|
};
|
2019-08-14 22:51:23 -07:00
|
|
|
|
2018-12-07 21:18:42 -08:00
|
|
|
class SystemStringConv {
|
|
|
|
std::wstring m_sys;
|
|
|
|
|
2017-06-10 11:40:27 -07:00
|
|
|
public:
|
2018-12-07 21:18:42 -08:00
|
|
|
explicit SystemStringConv(std::string_view str) : m_sys(UTF8ToWide(str)) {}
|
2019-08-14 22:51:23 -07:00
|
|
|
|
2018-12-07 21:18:42 -08:00
|
|
|
SystemStringView sys_str() const { return m_sys; }
|
|
|
|
const SystemChar* c_str() const { return m_sys.c_str(); }
|
2019-08-14 22:51:23 -07:00
|
|
|
|
|
|
|
friend std::wstring operator+(const SystemStringConv& lhs, const std::wstring_view rhs) {
|
|
|
|
return lhs.m_sys + rhs.data();
|
|
|
|
}
|
|
|
|
friend std::wstring operator+(std::wstring_view lhs, const SystemStringConv& rhs) {
|
|
|
|
return std::wstring(lhs).append(rhs.m_sys);
|
|
|
|
}
|
2017-06-10 11:40:27 -07:00
|
|
|
};
|
2019-10-01 00:23:35 -07:00
|
|
|
|
|
|
|
inline hecl::SystemString UTF8StringToSysString(std::string_view src) { return UTF8ToWide(src); }
|
2017-06-10 11:40:27 -07:00
|
|
|
#else
|
2018-12-07 21:18:42 -08:00
|
|
|
class SystemUTF8Conv {
|
|
|
|
std::string_view m_utf8;
|
|
|
|
|
2017-06-10 11:40:27 -07:00
|
|
|
public:
|
2018-12-07 21:18:42 -08:00
|
|
|
explicit SystemUTF8Conv(SystemStringView str) : m_utf8(str) {}
|
2019-08-14 22:51:23 -07:00
|
|
|
|
2018-12-07 21:18:42 -08:00
|
|
|
std::string_view str() const { return m_utf8; }
|
|
|
|
const char* c_str() const { return m_utf8.data(); }
|
2019-08-14 22:51:23 -07:00
|
|
|
|
|
|
|
friend std::string operator+(const SystemUTF8Conv& lhs, std::string_view rhs) {
|
|
|
|
return std::string(lhs.m_utf8).append(rhs);
|
|
|
|
}
|
|
|
|
friend std::string operator+(std::string_view lhs, const SystemUTF8Conv& rhs) {
|
|
|
|
return std::string(lhs).append(rhs.m_utf8);
|
|
|
|
}
|
2017-06-10 11:40:27 -07:00
|
|
|
};
|
2019-08-14 22:51:23 -07:00
|
|
|
|
2018-12-07 21:18:42 -08:00
|
|
|
class SystemStringConv {
|
|
|
|
std::string_view m_sys;
|
|
|
|
|
2017-06-10 11:40:27 -07:00
|
|
|
public:
|
2018-12-07 21:18:42 -08:00
|
|
|
explicit SystemStringConv(std::string_view str) : m_sys(str) {}
|
2019-08-14 22:51:23 -07:00
|
|
|
|
2018-12-07 21:18:42 -08:00
|
|
|
SystemStringView sys_str() const { return m_sys; }
|
|
|
|
const SystemChar* c_str() const { return m_sys.data(); }
|
2019-08-14 22:51:23 -07:00
|
|
|
|
|
|
|
friend std::string operator+(const SystemStringConv& lhs, std::string_view rhs) {
|
|
|
|
return std::string(lhs.m_sys).append(rhs);
|
|
|
|
}
|
|
|
|
friend std::string operator+(std::string_view lhs, const SystemStringConv& rhs) {
|
|
|
|
return std::string(lhs).append(rhs.m_sys);
|
|
|
|
}
|
2017-06-10 11:40:27 -07:00
|
|
|
};
|
2019-10-01 00:23:35 -07:00
|
|
|
|
|
|
|
inline hecl::SystemString UTF8StringToSysString(std::string src) { return src; }
|
2017-06-10 11:40:27 -07:00
|
|
|
#endif
|
|
|
|
|
2015-08-04 18:54:35 -07:00
|
|
|
void SanitizePath(std::string& path);
|
|
|
|
void SanitizePath(std::wstring& path);
|
|
|
|
|
2019-07-19 21:22:58 -07:00
|
|
|
inline void Unlink(const SystemChar* file) {
|
2015-08-05 15:59:59 -07:00
|
|
|
#if _WIN32
|
2018-12-07 21:18:42 -08:00
|
|
|
_wunlink(file);
|
2015-08-05 15:59:59 -07:00
|
|
|
#else
|
2018-12-07 21:18:42 -08:00
|
|
|
unlink(file);
|
2015-08-05 15:59:59 -07:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2019-07-19 21:22:58 -07:00
|
|
|
inline void MakeDir(const char* dir) {
|
2015-05-27 02:09:05 -07:00
|
|
|
#if _WIN32
|
2018-12-07 21:18:42 -08:00
|
|
|
HRESULT err;
|
|
|
|
if (!CreateDirectoryA(dir, NULL))
|
|
|
|
if ((err = GetLastError()) != ERROR_ALREADY_EXISTS)
|
2020-04-11 15:48:11 -07:00
|
|
|
LogModule.report(logvisor::Fatal, FMT_STRING("MakeDir({})"), dir);
|
2015-05-27 02:09:05 -07:00
|
|
|
#else
|
2018-12-07 21:18:42 -08:00
|
|
|
if (mkdir(dir, 0755))
|
|
|
|
if (errno != EEXIST)
|
2020-04-11 15:48:11 -07:00
|
|
|
LogModule.report(logvisor::Fatal, FMT_STRING("MakeDir({}): {}"), dir, strerror(errno));
|
2015-07-28 16:54:54 -07:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2015-08-30 20:36:24 -07:00
|
|
|
#if _WIN32
|
2019-07-19 21:22:58 -07:00
|
|
|
inline void MakeDir(const wchar_t* dir) {
|
2018-12-07 21:18:42 -08:00
|
|
|
HRESULT err;
|
|
|
|
if (!CreateDirectoryW(dir, NULL))
|
|
|
|
if ((err = GetLastError()) != ERROR_ALREADY_EXISTS)
|
2020-04-11 15:48:11 -07:00
|
|
|
LogModule.report(logvisor::Fatal, FMT_STRING(_SYS_STR("MakeDir({})")), dir);
|
2015-08-30 20:36:24 -07:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2017-02-03 19:45:39 -08:00
|
|
|
int RecursiveMakeDir(const SystemChar* dir);
|
|
|
|
|
2019-07-19 21:22:58 -07:00
|
|
|
inline const SystemChar* GetEnv(const SystemChar* name) {
|
2017-12-05 19:22:31 -08:00
|
|
|
#if WINDOWS_STORE
|
2018-12-07 21:18:42 -08:00
|
|
|
return nullptr;
|
2017-12-05 19:22:31 -08:00
|
|
|
#else
|
2016-03-28 14:38:31 -07:00
|
|
|
#if HECL_UCS2
|
2018-12-07 21:18:42 -08:00
|
|
|
return _wgetenv(name);
|
2016-03-28 14:38:31 -07:00
|
|
|
#else
|
2018-12-07 21:18:42 -08:00
|
|
|
return getenv(name);
|
2016-03-28 14:38:31 -07:00
|
|
|
#endif
|
2017-12-05 19:22:31 -08:00
|
|
|
#endif
|
2016-03-28 14:38:31 -07:00
|
|
|
}
|
|
|
|
|
2019-07-19 21:22:58 -07:00
|
|
|
inline SystemChar* Getcwd(SystemChar* buf, int maxlen) {
|
2015-06-09 19:40:03 -07:00
|
|
|
#if HECL_UCS2
|
2018-12-07 21:18:42 -08:00
|
|
|
return _wgetcwd(buf, maxlen);
|
2015-06-09 19:40:03 -07:00
|
|
|
#else
|
2018-12-07 21:18:42 -08:00
|
|
|
return getcwd(buf, maxlen);
|
2015-06-09 19:40:03 -07:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2018-05-24 23:34:58 -07:00
|
|
|
SystemString GetcwdStr();
|
2015-12-15 13:55:50 -08:00
|
|
|
|
2019-07-19 21:22:58 -07:00
|
|
|
inline bool IsAbsolute(SystemStringView path) {
|
2015-12-17 20:55:46 -08:00
|
|
|
#if _WIN32
|
2018-12-07 21:18:42 -08:00
|
|
|
if (path.size() && (path[0] == _SYS_STR('\\') || path[0] == _SYS_STR('/')))
|
|
|
|
return true;
|
|
|
|
if (path.size() >= 2 && iswalpha(path[0]) && path[1] == _SYS_STR(':'))
|
|
|
|
return true;
|
2015-12-15 13:55:50 -08:00
|
|
|
#else
|
2018-12-07 21:18:42 -08:00
|
|
|
if (path.size() && path[0] == _SYS_STR('/'))
|
|
|
|
return true;
|
2015-12-15 13:55:50 -08:00
|
|
|
#endif
|
2018-12-07 21:18:42 -08:00
|
|
|
return false;
|
2015-12-15 13:55:50 -08:00
|
|
|
}
|
|
|
|
|
2017-02-24 00:27:07 -08:00
|
|
|
const SystemChar* GetTmpDir();
|
|
|
|
|
2017-12-05 19:22:31 -08:00
|
|
|
#if !WINDOWS_STORE
|
2017-02-24 00:27:07 -08:00
|
|
|
int RunProcess(const SystemChar* path, const SystemChar* const args[]);
|
2017-12-05 19:22:31 -08:00
|
|
|
#endif
|
2017-02-24 00:27:07 -08:00
|
|
|
|
2018-12-07 21:18:42 -08:00
|
|
|
enum class FileLockType { None = 0, Read, Write };
|
2019-07-19 21:22:58 -07:00
|
|
|
inline FILE* Fopen(const SystemChar* path, const SystemChar* mode, FileLockType lock = FileLockType::None) {
|
2015-06-09 19:40:03 -07:00
|
|
|
#if HECL_UCS2
|
2018-12-07 21:18:42 -08:00
|
|
|
FILE* fp = _wfopen(path, mode);
|
|
|
|
if (!fp)
|
|
|
|
return nullptr;
|
2015-06-09 15:19:59 -07:00
|
|
|
#else
|
2018-12-07 21:18:42 -08:00
|
|
|
FILE* fp = fopen(path, mode);
|
|
|
|
if (!fp)
|
|
|
|
return nullptr;
|
2015-08-31 17:26:35 -07:00
|
|
|
#endif
|
2015-06-09 15:19:59 -07:00
|
|
|
|
2018-12-07 21:18:42 -08:00
|
|
|
if (lock != FileLockType::None) {
|
2015-06-10 16:34:14 -07:00
|
|
|
#if _WIN32
|
2018-12-07 21:18:42 -08:00
|
|
|
OVERLAPPED ov = {};
|
|
|
|
LockFileEx((HANDLE)(uintptr_t)_fileno(fp), (lock == FileLockType::Write) ? LOCKFILE_EXCLUSIVE_LOCK : 0, 0, 0, 1,
|
|
|
|
&ov);
|
2015-06-10 16:34:14 -07:00
|
|
|
#else
|
2018-12-07 21:18:42 -08:00
|
|
|
if (flock(fileno(fp), ((lock == FileLockType::Write) ? LOCK_EX : LOCK_SH) | LOCK_NB))
|
2020-04-11 15:48:11 -07:00
|
|
|
LogModule.report(logvisor::Error, FMT_STRING("flock {}: {}"), path, strerror(errno));
|
2015-06-10 16:34:14 -07:00
|
|
|
#endif
|
2018-12-07 21:18:42 -08:00
|
|
|
}
|
2015-06-10 16:34:14 -07:00
|
|
|
|
2018-12-07 21:18:42 -08:00
|
|
|
return fp;
|
2015-06-09 15:19:59 -07:00
|
|
|
}
|
|
|
|
|
2019-08-21 16:28:10 -07:00
|
|
|
struct UniqueFileDeleter {
|
|
|
|
void operator()(FILE* file) const noexcept { std::fclose(file); }
|
|
|
|
};
|
|
|
|
using UniqueFilePtr = std::unique_ptr<FILE, UniqueFileDeleter>;
|
|
|
|
|
|
|
|
inline UniqueFilePtr FopenUnique(const SystemChar* path, const SystemChar* mode,
|
|
|
|
FileLockType lock = FileLockType::None) {
|
|
|
|
return UniqueFilePtr{Fopen(path, mode, lock)};
|
|
|
|
}
|
|
|
|
|
2019-07-19 21:22:58 -07:00
|
|
|
inline int FSeek(FILE* fp, int64_t offset, int whence) {
|
2016-02-03 16:55:13 -08:00
|
|
|
#if _WIN32
|
2018-12-07 21:18:42 -08:00
|
|
|
return _fseeki64(fp, offset, whence);
|
2016-02-03 16:55:13 -08:00
|
|
|
#elif __APPLE__ || __FreeBSD__
|
2018-12-07 21:18:42 -08:00
|
|
|
return fseeko(fp, offset, whence);
|
2016-02-03 16:55:13 -08:00
|
|
|
#else
|
2018-12-07 21:18:42 -08:00
|
|
|
return fseeko64(fp, offset, whence);
|
2016-02-03 16:55:13 -08:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2019-07-19 21:22:58 -07:00
|
|
|
inline int64_t FTell(FILE* fp) {
|
2016-02-03 16:55:13 -08:00
|
|
|
#if _WIN32
|
2018-12-07 21:18:42 -08:00
|
|
|
return _ftelli64(fp);
|
2016-02-03 16:55:13 -08:00
|
|
|
#elif __APPLE__ || __FreeBSD__
|
2018-12-07 21:18:42 -08:00
|
|
|
return ftello(fp);
|
2016-02-03 16:55:13 -08:00
|
|
|
#else
|
2018-12-07 21:18:42 -08:00
|
|
|
return ftello64(fp);
|
2016-02-03 16:55:13 -08:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2019-07-19 21:22:58 -07:00
|
|
|
inline int Rename(const SystemChar* oldpath, const SystemChar* newpath) {
|
2016-01-04 16:00:34 -08:00
|
|
|
#if HECL_UCS2
|
2018-12-07 21:18:42 -08:00
|
|
|
// return _wrename(oldpath, newpath);
|
|
|
|
return MoveFileExW(oldpath, newpath, MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH) == 0;
|
2016-01-04 16:00:34 -08:00
|
|
|
#else
|
2018-12-07 21:18:42 -08:00
|
|
|
return rename(oldpath, newpath);
|
2016-01-04 16:00:34 -08:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2019-07-19 21:22:58 -07:00
|
|
|
inline int Stat(const SystemChar* path, Sstat* statOut) {
|
2015-06-09 19:40:03 -07:00
|
|
|
#if HECL_UCS2
|
2018-12-07 21:18:42 -08:00
|
|
|
size_t pos;
|
|
|
|
for (pos = 0; pos < 3 && path[pos] != L'\0'; ++pos) {}
|
|
|
|
if (pos == 2 && path[1] == L':') {
|
|
|
|
SystemChar fixPath[4] = {path[0], L':', L'/', L'\0'};
|
|
|
|
return _wstat(fixPath, statOut);
|
|
|
|
}
|
|
|
|
return _wstat(path, statOut);
|
2015-06-09 19:40:03 -07:00
|
|
|
#else
|
2018-12-07 21:18:42 -08:00
|
|
|
return stat(path, statOut);
|
2015-06-09 19:40:03 -07:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2019-07-19 21:22:58 -07:00
|
|
|
inline int StrCmp(const SystemChar* str1, const SystemChar* str2) {
|
2018-12-07 21:18:42 -08:00
|
|
|
if (!str1 || !str2)
|
|
|
|
return str1 != str2;
|
2015-09-30 17:40:06 -07:00
|
|
|
#if HECL_UCS2
|
2018-12-07 21:18:42 -08:00
|
|
|
return wcscmp(str1, str2);
|
2015-09-30 17:40:06 -07:00
|
|
|
#else
|
2018-12-07 21:18:42 -08:00
|
|
|
return strcmp(str1, str2);
|
2015-09-30 17:40:06 -07:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2019-07-19 21:22:58 -07:00
|
|
|
inline int StrNCmp(const SystemChar* str1, const SystemChar* str2, size_t count) {
|
2018-12-07 21:18:42 -08:00
|
|
|
if (!str1 || !str2)
|
|
|
|
return str1 != str2;
|
2019-08-14 22:11:39 -07:00
|
|
|
|
|
|
|
return std::char_traits<SystemChar>::compare(str1, str2, count);
|
2017-10-27 03:10:08 -07:00
|
|
|
}
|
|
|
|
|
2019-07-19 21:22:58 -07:00
|
|
|
inline int StrCaseCmp(const SystemChar* str1, const SystemChar* str2) {
|
2018-12-07 21:18:42 -08:00
|
|
|
if (!str1 || !str2)
|
|
|
|
return str1 != str2;
|
2016-01-22 19:38:51 -08:00
|
|
|
#if HECL_UCS2
|
2018-12-07 21:18:42 -08:00
|
|
|
return _wcsicmp(str1, str2);
|
2016-01-22 19:38:51 -08:00
|
|
|
#else
|
2018-12-07 21:18:42 -08:00
|
|
|
return strcasecmp(str1, str2);
|
2016-01-22 19:38:51 -08:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2019-07-19 21:22:58 -07:00
|
|
|
inline unsigned long StrToUl(const SystemChar* str, SystemChar** endPtr, int base) {
|
2016-10-01 16:18:52 -07:00
|
|
|
#if HECL_UCS2
|
2018-12-07 21:18:42 -08:00
|
|
|
return wcstoul(str, endPtr, base);
|
2016-10-01 16:18:52 -07:00
|
|
|
#else
|
2018-12-07 21:18:42 -08:00
|
|
|
return strtoul(str, endPtr, base);
|
2016-10-01 16:18:52 -07:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2019-07-19 21:22:58 -07:00
|
|
|
inline bool CheckFreeSpace(const SystemChar* path, size_t reqSz) {
|
2016-01-24 18:16:40 -08:00
|
|
|
#if _WIN32
|
2018-12-07 21:18:42 -08:00
|
|
|
ULARGE_INTEGER freeBytes;
|
|
|
|
wchar_t buf[1024];
|
|
|
|
wchar_t* end;
|
|
|
|
DWORD ret = GetFullPathNameW(path, 1024, buf, &end);
|
|
|
|
if (!ret || ret > 1024)
|
2020-04-11 15:48:11 -07:00
|
|
|
LogModule.report(logvisor::Fatal, FMT_STRING(_SYS_STR("GetFullPathNameW {}")), path);
|
2018-12-07 21:18:42 -08:00
|
|
|
if (end)
|
|
|
|
end[0] = L'\0';
|
|
|
|
if (!GetDiskFreeSpaceExW(buf, &freeBytes, nullptr, nullptr))
|
2020-04-11 15:48:11 -07:00
|
|
|
LogModule.report(logvisor::Fatal, FMT_STRING(_SYS_STR("GetDiskFreeSpaceExW {}: {}")), path, GetLastError());
|
2018-12-07 21:18:42 -08:00
|
|
|
return reqSz < freeBytes.QuadPart;
|
2016-01-24 18:16:40 -08:00
|
|
|
#else
|
2018-12-07 21:18:42 -08:00
|
|
|
struct statvfs svfs;
|
|
|
|
if (statvfs(path, &svfs))
|
2020-04-11 15:48:11 -07:00
|
|
|
LogModule.report(logvisor::Fatal, FMT_STRING("statvfs {}: {}"), path, strerror(errno));
|
2018-12-07 21:18:42 -08:00
|
|
|
return reqSz < svfs.f_frsize * svfs.f_bavail;
|
2016-01-24 18:16:40 -08:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2019-07-19 21:22:58 -07:00
|
|
|
inline bool PathRelative(const SystemChar* path) {
|
2018-12-07 21:18:42 -08:00
|
|
|
if (!path || !path[0])
|
|
|
|
return false;
|
2017-12-05 19:22:31 -08:00
|
|
|
#if _WIN32 && !WINDOWS_STORE
|
2018-12-07 21:18:42 -08:00
|
|
|
return PathIsRelative(path);
|
2017-02-24 23:58:36 -08:00
|
|
|
#else
|
2018-12-07 21:18:42 -08:00
|
|
|
return path[0] != '/';
|
2017-02-24 23:58:36 -08:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2019-07-19 21:22:58 -07:00
|
|
|
inline int ConsoleWidth(bool* ok = nullptr) {
|
2018-12-07 21:18:42 -08:00
|
|
|
int retval = 80;
|
2015-07-20 16:27:22 -07:00
|
|
|
#if _WIN32
|
2017-12-05 19:22:31 -08:00
|
|
|
#if !WINDOWS_STORE
|
2018-12-07 21:18:42 -08:00
|
|
|
CONSOLE_SCREEN_BUFFER_INFO info;
|
|
|
|
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &info);
|
|
|
|
retval = info.dwSize.X;
|
|
|
|
if (ok)
|
|
|
|
*ok = true;
|
2017-12-05 19:22:31 -08:00
|
|
|
#endif
|
2015-07-20 16:27:22 -07:00
|
|
|
#else
|
2018-12-07 21:18:42 -08:00
|
|
|
if (ok)
|
|
|
|
*ok = false;
|
|
|
|
struct winsize w;
|
|
|
|
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) != -1) {
|
|
|
|
retval = w.ws_col;
|
2018-03-28 01:06:34 -07:00
|
|
|
if (ok)
|
2018-12-07 21:18:42 -08:00
|
|
|
*ok = true;
|
|
|
|
}
|
2015-07-20 16:27:22 -07:00
|
|
|
#endif
|
2018-12-07 21:18:42 -08:00
|
|
|
if (retval < 10)
|
|
|
|
return 10;
|
|
|
|
return retval;
|
2015-07-20 16:27:22 -07:00
|
|
|
}
|
|
|
|
|
2018-03-23 14:40:12 -07:00
|
|
|
class MultiProgressPrinter;
|
2015-05-27 02:09:05 -07:00
|
|
|
class ProjectRootPath;
|
2015-05-19 22:22:32 -07:00
|
|
|
|
2019-08-14 23:16:21 -07:00
|
|
|
using SystemRegex = std::basic_regex<SystemChar>;
|
2019-10-01 00:23:35 -07:00
|
|
|
using SystemRegexIterator = std::regex_iterator<SystemString::const_iterator>;
|
2019-08-14 23:16:21 -07:00
|
|
|
using SystemRegexMatch = std::match_results<SystemString::const_iterator>;
|
2019-10-01 00:23:35 -07:00
|
|
|
using SystemViewRegexMatch = std::match_results<SystemStringView::const_iterator>;
|
2019-08-14 23:16:21 -07:00
|
|
|
using SystemRegexTokenIterator = std::regex_token_iterator<SystemString::const_iterator>;
|
|
|
|
|
2015-05-19 22:22:32 -07:00
|
|
|
/**
|
|
|
|
* @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.
|
|
|
|
*/
|
2018-12-07 21:18:42 -08:00
|
|
|
class Hash {
|
2015-11-15 20:30:06 -08:00
|
|
|
protected:
|
2018-12-07 21:18:42 -08:00
|
|
|
uint64_t hash = 0;
|
|
|
|
|
2015-05-19 22:22:32 -07:00
|
|
|
public:
|
2019-08-23 23:52:24 -07:00
|
|
|
constexpr Hash() noexcept = default;
|
|
|
|
constexpr Hash(const Hash&) noexcept = default;
|
2019-08-14 23:50:16 -07:00
|
|
|
constexpr Hash(Hash&&) noexcept = default;
|
2019-08-23 23:52:24 -07:00
|
|
|
constexpr Hash(uint64_t hashin) noexcept : hash(hashin) {}
|
2019-08-23 23:55:06 -07:00
|
|
|
explicit Hash(const void* buf, size_t len) noexcept : hash(XXH64(buf, len, 0)) {}
|
|
|
|
explicit Hash(std::string_view str) noexcept : hash(XXH64(str.data(), str.size(), 0)) {}
|
|
|
|
explicit Hash(std::wstring_view str) noexcept : hash(XXH64(str.data(), str.size() * 2, 0)) {}
|
2019-08-23 23:52:24 -07:00
|
|
|
|
|
|
|
constexpr uint32_t val32() const noexcept { return uint32_t(hash) ^ uint32_t(hash >> 32); }
|
|
|
|
constexpr uint64_t val64() const noexcept { return uint64_t(hash); }
|
|
|
|
constexpr size_t valSizeT() const noexcept { return size_t(hash); }
|
2018-12-07 21:18:42 -08:00
|
|
|
template <typename T>
|
2019-08-23 23:52:24 -07:00
|
|
|
constexpr T valT() const noexcept;
|
2019-08-14 23:50:16 -07:00
|
|
|
|
2019-08-23 23:52:24 -07:00
|
|
|
constexpr Hash& operator=(const Hash& other) noexcept = default;
|
2019-08-14 23:50:16 -07:00
|
|
|
constexpr Hash& operator=(Hash&& other) noexcept = default;
|
2019-08-23 23:52:24 -07:00
|
|
|
constexpr bool operator==(const Hash& other) const noexcept { return hash == other.hash; }
|
|
|
|
constexpr bool operator!=(const Hash& other) const noexcept { return !operator==(other); }
|
|
|
|
constexpr bool operator<(const Hash& other) const noexcept { return hash < other.hash; }
|
|
|
|
constexpr bool operator>(const Hash& other) const noexcept { return hash > other.hash; }
|
|
|
|
constexpr bool operator<=(const Hash& other) const noexcept { return hash <= other.hash; }
|
|
|
|
constexpr bool operator>=(const Hash& other) const noexcept { return hash >= other.hash; }
|
|
|
|
constexpr explicit operator bool() const noexcept { return hash != 0; }
|
2015-06-10 16:34:14 -07:00
|
|
|
};
|
2018-12-07 21:18:42 -08:00
|
|
|
template <>
|
2019-08-23 23:52:24 -07:00
|
|
|
constexpr uint32_t Hash::valT<uint32_t>() const noexcept {
|
2018-12-07 21:18:42 -08:00
|
|
|
return val32();
|
|
|
|
}
|
|
|
|
template <>
|
2019-08-23 23:52:24 -07:00
|
|
|
constexpr uint64_t Hash::valT<uint64_t>() const noexcept {
|
2018-12-07 21:18:42 -08:00
|
|
|
return val64();
|
|
|
|
}
|
2015-06-10 16:34:14 -07:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Timestamp representation used for comparing modtimes of cooked resources
|
|
|
|
*/
|
2018-12-07 21:18:42 -08:00
|
|
|
class Time final {
|
|
|
|
time_t ts;
|
|
|
|
|
2015-06-10 16:34:14 -07:00
|
|
|
public:
|
2020-04-05 06:34:22 -07:00
|
|
|
Time() : ts(std::time(nullptr)) {}
|
|
|
|
constexpr Time(time_t ti) noexcept : ts{ti} {}
|
|
|
|
constexpr Time(const Time& other) noexcept : ts{other.ts} {}
|
|
|
|
[[nodiscard]] constexpr time_t getTs() const { return ts; }
|
|
|
|
constexpr Time& operator=(const Time& other) noexcept {
|
2018-12-07 21:18:42 -08:00
|
|
|
ts = other.ts;
|
|
|
|
return *this;
|
|
|
|
}
|
2020-04-05 06:34:22 -07:00
|
|
|
[[nodiscard]] constexpr bool operator==(const Time& other) const noexcept { return ts == other.ts; }
|
|
|
|
[[nodiscard]] constexpr bool operator!=(const Time& other) const noexcept { return ts != other.ts; }
|
|
|
|
[[nodiscard]] constexpr bool operator<(const Time& other) const noexcept { return ts < other.ts; }
|
|
|
|
[[nodiscard]] constexpr bool operator>(const Time& other) const noexcept { return ts > other.ts; }
|
|
|
|
[[nodiscard]] constexpr bool operator<=(const Time& other) const noexcept { return ts <= other.ts; }
|
|
|
|
[[nodiscard]] constexpr bool operator>=(const Time& other) const noexcept { return ts >= other.ts; }
|
2015-05-19 22:22:32 -07:00
|
|
|
};
|
|
|
|
|
2015-12-30 14:03:37 -08:00
|
|
|
/**
|
|
|
|
* @brief Case-insensitive comparator for std::map sorting
|
|
|
|
*/
|
2018-12-07 21:18:42 -08:00
|
|
|
struct CaseInsensitiveCompare {
|
hecl/hecl: Allow CaseInsensitiveCompare to be used with heterogenous lookup
It's quite common to see maps or sets that make use of a string->object
association, usually as <std::string, T>. However, this can result in
inefficient code when performing lookups or indexing, as something like:
std::map<std::string, T> some_map;
...
auto iter = some_map.find("Name");
...
will result in a std::string instance being constructed around "Name",
which is less than ideal.
With a heterogenous comparator, however (such as std::less<>), like:
std::map<std::string, T, std::less<>>
we allow the container to do automatic type deduction and comparisons.
This allows types comparable to the key type to be used in find calls,
etc, without actually needing to construct the key type around the other
type.
The main way to enable containers to perform such behavior is by
defining a type named is_transparent within the comparator type.
2020-02-24 01:31:52 -08:00
|
|
|
// Allow heterogenous lookup with maps that use this comparator.
|
|
|
|
using is_transparent = void;
|
|
|
|
|
2018-12-07 21:18:42 -08:00
|
|
|
bool operator()(std::string_view lhs, std::string_view rhs) const {
|
2019-08-14 20:20:25 -07:00
|
|
|
return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end(), [](char lhs, char rhs) {
|
|
|
|
return std::tolower(static_cast<unsigned char>(lhs)) < std::tolower(static_cast<unsigned char>(rhs));
|
|
|
|
});
|
2018-12-07 21:18:42 -08:00
|
|
|
}
|
2015-12-30 14:03:37 -08:00
|
|
|
|
2018-12-07 21:18:42 -08:00
|
|
|
bool operator()(std::wstring_view lhs, std::wstring_view rhs) const {
|
2019-08-14 20:20:25 -07:00
|
|
|
return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end(), [](wchar_t lhs, wchar_t rhs) {
|
|
|
|
return std::towlower(lhs) < std::towlower(rhs);
|
|
|
|
});
|
2018-12-07 21:18:42 -08:00
|
|
|
}
|
2015-12-30 14:03:37 -08:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Directory traversal tool for accessing sorted directory entries
|
|
|
|
*/
|
2018-12-07 21:18:42 -08:00
|
|
|
class DirectoryEnumerator {
|
2015-12-30 14:03:37 -08:00
|
|
|
public:
|
2018-12-07 21:18:42 -08:00
|
|
|
enum class Mode { Native, DirsSorted, FilesSorted, DirsThenFilesSorted };
|
|
|
|
struct Entry {
|
|
|
|
hecl::SystemString m_path;
|
|
|
|
hecl::SystemString m_name;
|
|
|
|
size_t m_fileSz;
|
|
|
|
bool m_isDir;
|
|
|
|
|
2019-08-14 23:02:42 -07:00
|
|
|
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) {}
|
2018-12-07 21:18:42 -08:00
|
|
|
};
|
2015-12-30 14:03:37 -08:00
|
|
|
|
|
|
|
private:
|
2018-12-07 21:18:42 -08:00
|
|
|
std::vector<Entry> m_entries;
|
2015-12-30 14:03:37 -08:00
|
|
|
|
|
|
|
public:
|
2018-12-07 21:18:42 -08:00
|
|
|
DirectoryEnumerator(SystemStringView path, Mode mode = Mode::DirsThenFilesSorted, bool sizeSort = false,
|
|
|
|
bool reverse = false, bool noHidden = false);
|
2015-12-30 14:03:37 -08:00
|
|
|
|
2019-08-21 14:31:20 -07:00
|
|
|
explicit operator bool() const { return m_entries.size() != 0; }
|
2018-12-07 21:18:42 -08:00
|
|
|
size_t size() const { return m_entries.size(); }
|
|
|
|
std::vector<Entry>::const_iterator begin() const { return m_entries.cbegin(); }
|
|
|
|
std::vector<Entry>::const_iterator end() const { return m_entries.cend(); }
|
2015-12-30 14:03:37 -08:00
|
|
|
};
|
|
|
|
|
2016-01-01 18:27:17 -08:00
|
|
|
/**
|
|
|
|
* @brief Build list of common OS-specific directories
|
|
|
|
*/
|
2016-03-04 15:02:44 -08:00
|
|
|
std::vector<std::pair<hecl::SystemString, std::string>> GetSystemLocations();
|
2016-01-01 18:27:17 -08:00
|
|
|
|
2015-09-29 14:50:07 -07:00
|
|
|
/**
|
2016-09-24 18:57:43 -07:00
|
|
|
* @brief Special ProjectRootPath class for opening Database::Project instances
|
2015-09-29 14:50:07 -07:00
|
|
|
*
|
|
|
|
* Constructing a ProjectPath requires supplying a ProjectRootPath to consistently
|
|
|
|
* resolve canonicalized relative paths.
|
|
|
|
*/
|
2018-12-07 21:18:42 -08:00
|
|
|
class ProjectRootPath {
|
|
|
|
SystemString m_projRoot;
|
|
|
|
Hash m_hash = 0;
|
2015-09-29 23:23:07 -07:00
|
|
|
|
2018-12-07 21:18:42 -08:00
|
|
|
public:
|
|
|
|
/**
|
|
|
|
* @brief Empty constructor
|
|
|
|
*
|
|
|
|
* Used to preallocate ProjectPath for later population using assign()
|
|
|
|
*/
|
|
|
|
ProjectRootPath() = default;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Tests for non-empty project root path
|
|
|
|
*/
|
2019-08-21 14:31:20 -07:00
|
|
|
explicit operator bool() const { return m_projRoot.size() != 0; }
|
2018-12-07 21:18:42 -08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Construct a representation of a project root path
|
|
|
|
* @param path valid filesystem-path (relative or absolute) to project root
|
|
|
|
*/
|
|
|
|
ProjectRootPath(SystemStringView path) : m_projRoot(path) {
|
|
|
|
SanitizePath(m_projRoot);
|
|
|
|
m_hash = Hash(m_projRoot);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Access fully-canonicalized absolute path
|
|
|
|
* @return Absolute path reference
|
|
|
|
*/
|
|
|
|
SystemStringView 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(SystemStringView absPath) const {
|
|
|
|
if (absPath.size() > m_projRoot.size()) {
|
|
|
|
SystemString absPathForward(absPath);
|
|
|
|
for (SystemChar& ch : absPathForward)
|
|
|
|
if (ch == _SYS_STR('\\'))
|
|
|
|
ch = _SYS_STR('/');
|
|
|
|
if (!absPathForward.compare(0, m_projRoot.size(), m_projRoot)) {
|
|
|
|
auto beginIt = absPathForward.cbegin() + m_projRoot.size();
|
|
|
|
while (*beginIt == _SYS_STR('/'))
|
|
|
|
++beginIt;
|
|
|
|
return SystemString(beginIt, absPathForward.cend());
|
|
|
|
}
|
2016-01-04 16:00:34 -08:00
|
|
|
}
|
2020-04-11 15:48:11 -07:00
|
|
|
LogModule.report(logvisor::Fatal, FMT_STRING(_SYS_STR("unable to resolve '{}' as project relative '{}'")), absPath,
|
2019-08-14 23:27:36 -07:00
|
|
|
m_projRoot);
|
2018-12-07 21:18:42 -08:00
|
|
|
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
|
|
|
|
*/
|
2019-08-23 23:52:24 -07:00
|
|
|
Hash hash() const noexcept { return m_hash; }
|
|
|
|
bool operator==(const ProjectRootPath& other) const noexcept { return m_hash == other.m_hash; }
|
|
|
|
bool operator!=(const ProjectRootPath& other) const noexcept { return !operator==(other); }
|
2018-12-07 21:18:42 -08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Obtain c-string of final path component
|
|
|
|
* @return Final component c-string (may be empty)
|
|
|
|
*/
|
|
|
|
SystemStringView getLastComponent() const {
|
|
|
|
size_t pos = m_projRoot.rfind(_SYS_STR('/'));
|
|
|
|
if (pos == SystemString::npos)
|
|
|
|
return {};
|
|
|
|
return {m_projRoot.c_str() + pos + 1, size_t(m_projRoot.size() - pos - 1)};
|
|
|
|
}
|
2015-09-29 14:50:07 -07:00
|
|
|
};
|
|
|
|
|
2015-05-27 02:09:05 -07:00
|
|
|
/**
|
|
|
|
* @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!!
|
|
|
|
*/
|
2018-12-07 21:18:42 -08:00
|
|
|
class ProjectPath {
|
|
|
|
Database::Project* m_proj = nullptr;
|
|
|
|
SystemString m_absPath;
|
|
|
|
SystemString m_relPath;
|
|
|
|
SystemString m_auxInfo;
|
|
|
|
Hash m_hash = 0;
|
2015-06-09 19:40:03 -07:00
|
|
|
#if HECL_UCS2
|
2018-12-07 21:18:42 -08:00
|
|
|
std::string m_utf8AbsPath;
|
|
|
|
std::string m_utf8RelPath;
|
|
|
|
std::string m_utf8AuxInfo;
|
2015-06-09 19:40:03 -07:00
|
|
|
#endif
|
2018-12-07 21:18:42 -08:00
|
|
|
void ComputeHash() {
|
2016-08-28 17:28:24 -07:00
|
|
|
#if HECL_UCS2
|
2018-12-07 21:18:42 -08:00
|
|
|
m_utf8AbsPath = WideToUTF8(m_absPath);
|
|
|
|
m_utf8RelPath = WideToUTF8(m_relPath);
|
|
|
|
m_utf8AuxInfo = WideToUTF8(m_auxInfo);
|
|
|
|
if (m_utf8AuxInfo.size())
|
|
|
|
m_hash = Hash(m_utf8RelPath + '|' + m_utf8AuxInfo);
|
|
|
|
else
|
|
|
|
m_hash = Hash(m_utf8RelPath);
|
2016-08-28 17:28:24 -07:00
|
|
|
#else
|
2018-12-07 21:18:42 -08:00
|
|
|
if (m_auxInfo.size())
|
|
|
|
m_hash = Hash(m_relPath + '|' + m_auxInfo);
|
|
|
|
else
|
|
|
|
m_hash = Hash(m_relPath);
|
2016-08-28 17:28:24 -07:00
|
|
|
#endif
|
2018-12-07 21:18:42 -08:00
|
|
|
}
|
|
|
|
|
2015-05-27 02:09:05 -07:00
|
|
|
public:
|
2018-12-07 21:18:42 -08:00
|
|
|
/**
|
|
|
|
* @brief Empty constructor
|
|
|
|
*
|
|
|
|
* Used to preallocate ProjectPath for later population using assign()
|
|
|
|
*/
|
|
|
|
ProjectPath() = default;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Tests for non-empty project path
|
|
|
|
*/
|
2019-08-21 14:31:20 -07:00
|
|
|
explicit operator bool() const { return m_absPath.size() != 0; }
|
2018-12-07 21:18:42 -08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Clears path
|
|
|
|
*/
|
|
|
|
void clear() {
|
|
|
|
m_proj = nullptr;
|
|
|
|
m_absPath.clear();
|
|
|
|
m_relPath.clear();
|
|
|
|
m_hash = 0;
|
2015-11-09 11:44:47 -08:00
|
|
|
#if HECL_UCS2
|
2018-12-07 21:18:42 -08:00
|
|
|
m_utf8AbsPath.clear();
|
|
|
|
m_utf8RelPath.clear();
|
2015-11-09 11:44:47 -08:00
|
|
|
#endif
|
2018-12-07 21:18:42 -08:00
|
|
|
}
|
2015-11-09 11:44:47 -08:00
|
|
|
|
2018-12-07 21:18:42 -08:00
|
|
|
/**
|
|
|
|
* @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, SystemStringView path) { assign(project, path); }
|
|
|
|
void assign(Database::Project& project, SystemStringView path);
|
2015-09-29 14:50:07 -07:00
|
|
|
|
|
|
|
#if HECL_UCS2
|
2018-12-07 21:18:42 -08:00
|
|
|
ProjectPath(Database::Project& project, std::string_view path) { assign(project, path); }
|
|
|
|
void assign(Database::Project& project, std::string_view path);
|
2015-09-29 14:50:07 -07:00
|
|
|
#endif
|
|
|
|
|
2018-12-07 21:18:42 -08:00
|
|
|
/**
|
|
|
|
* @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, SystemStringView path) { assign(parentPath, path); }
|
|
|
|
void assign(const ProjectPath& parentPath, SystemStringView path);
|
2015-06-09 15:19:59 -07:00
|
|
|
|
2015-08-05 16:19:28 -07:00
|
|
|
#if HECL_UCS2
|
2018-12-07 21:18:42 -08:00
|
|
|
ProjectPath(const ProjectPath& parentPath, std::string_view path) { assign(parentPath, path); }
|
|
|
|
void assign(const ProjectPath& parentPath, std::string_view path);
|
2015-07-22 12:14:50 -07:00
|
|
|
#endif
|
|
|
|
|
2018-12-07 21:18:42 -08:00
|
|
|
/**
|
|
|
|
* @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;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Access fully-canonicalized absolute path
|
|
|
|
* @return Absolute path reference
|
|
|
|
*/
|
|
|
|
SystemStringView 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)
|
|
|
|
*/
|
|
|
|
SystemStringView getRelativePath() const {
|
|
|
|
if (m_relPath.size())
|
|
|
|
return m_relPath;
|
|
|
|
static const SystemString dot = _SYS_STR(".");
|
|
|
|
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 == _SYS_STR("."))
|
2020-04-11 15:48:11 -07:00
|
|
|
LogModule.report(logvisor::Fatal, FMT_STRING("attempted to resolve parent of root project path"));
|
2018-12-07 21:18:42 -08:00
|
|
|
size_t pos = m_relPath.rfind(_SYS_STR('/'));
|
|
|
|
if (pos == SystemString::npos)
|
|
|
|
return ProjectPath(*m_proj, _SYS_STR(""));
|
|
|
|
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)
|
|
|
|
*/
|
|
|
|
SystemStringView getLastComponent() const {
|
|
|
|
size_t pos = m_relPath.rfind(_SYS_STR('/'));
|
|
|
|
if (pos == SystemString::npos)
|
|
|
|
return m_relPath;
|
|
|
|
return {m_relPath.c_str() + pos + 1, m_relPath.size() - pos - 1};
|
|
|
|
}
|
|
|
|
std::string_view getLastComponentUTF8() const {
|
|
|
|
size_t pos = m_relPath.rfind(_SYS_STR('/'));
|
2015-10-14 16:06:47 -07:00
|
|
|
#if HECL_UCS2
|
2018-12-07 21:18:42 -08:00
|
|
|
if (pos == SystemString::npos)
|
|
|
|
return m_utf8RelPath;
|
|
|
|
return {m_utf8RelPath.c_str() + pos + 1, size_t(m_utf8RelPath.size() - pos - 1)};
|
2015-10-14 16:06:47 -07:00
|
|
|
#else
|
2018-12-07 21:18:42 -08:00
|
|
|
if (pos == SystemString::npos)
|
|
|
|
return m_relPath;
|
|
|
|
return {m_relPath.c_str() + pos + 1, size_t(m_relPath.size() - pos - 1)};
|
2015-10-14 16:06:47 -07:00
|
|
|
#endif
|
2018-12-07 21:18:42 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Obtain c-string of extension of final path component (stored within relative path)
|
|
|
|
* @return Final component extension c-string (may be empty)
|
|
|
|
*/
|
|
|
|
SystemStringView getLastComponentExt() const {
|
|
|
|
SystemStringView lastCompOrig = getLastComponent().data();
|
|
|
|
const SystemChar* end = lastCompOrig.data() + lastCompOrig.size();
|
|
|
|
const SystemChar* lastComp = end;
|
|
|
|
while (lastComp != lastCompOrig.data()) {
|
|
|
|
if (*lastComp == _SYS_STR('.'))
|
|
|
|
return {lastComp + 1, size_t(end - lastComp - 1)};
|
|
|
|
--lastComp;
|
2015-10-14 16:06:47 -07:00
|
|
|
}
|
2018-12-07 21:18:42 -08:00
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Build vector of project-relative directory/file components
|
|
|
|
* @return Vector of path components
|
|
|
|
*/
|
|
|
|
std::vector<hecl::SystemString> getPathComponents() const {
|
|
|
|
std::vector<hecl::SystemString> ret;
|
|
|
|
if (m_relPath.empty())
|
|
|
|
return ret;
|
|
|
|
auto it = m_relPath.cbegin();
|
|
|
|
if (*it == _SYS_STR('/')) {
|
|
|
|
ret.push_back(_SYS_STR("/"));
|
|
|
|
++it;
|
2015-09-30 17:40:06 -07:00
|
|
|
}
|
2018-12-07 21:18:42 -08:00
|
|
|
hecl::SystemString comp;
|
|
|
|
for (; it != m_relPath.cend(); ++it) {
|
|
|
|
if (*it == _SYS_STR('/')) {
|
|
|
|
if (comp.empty())
|
|
|
|
continue;
|
|
|
|
ret.push_back(std::move(comp));
|
|
|
|
comp.clear();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
comp += *it;
|
2016-01-06 13:03:41 -08:00
|
|
|
}
|
2018-12-07 21:18:42 -08:00
|
|
|
if (comp.size())
|
|
|
|
ret.push_back(std::move(comp));
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Build vector of project-relative directory/file components
|
|
|
|
* @return Vector of path components encoded as UTF8
|
|
|
|
*/
|
|
|
|
std::vector<std::string> getPathComponentsUTF8() const {
|
2016-01-06 13:03:41 -08:00
|
|
|
#if HECL_UCS2
|
2018-12-07 21:18:42 -08:00
|
|
|
const std::string& relPath = m_utf8RelPath;
|
2016-01-06 13:03:41 -08:00
|
|
|
#else
|
2018-12-07 21:18:42 -08:00
|
|
|
const std::string& relPath = m_relPath;
|
2016-01-06 13:03:41 -08:00
|
|
|
#endif
|
2018-12-07 21:18:42 -08:00
|
|
|
std::vector<std::string> ret;
|
|
|
|
if (relPath.empty())
|
|
|
|
return ret;
|
|
|
|
auto it = relPath.cbegin();
|
|
|
|
if (*it == '/') {
|
|
|
|
ret.push_back("/");
|
|
|
|
++it;
|
2016-01-06 13:03:41 -08:00
|
|
|
}
|
2018-12-07 21:18:42 -08:00
|
|
|
std::string comp;
|
|
|
|
for (; it != relPath.cend(); ++it) {
|
|
|
|
if (*it == '/') {
|
|
|
|
if (comp.empty())
|
|
|
|
continue;
|
|
|
|
ret.push_back(std::move(comp));
|
|
|
|
comp.clear();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
comp += *it;
|
|
|
|
}
|
|
|
|
if (comp.size())
|
|
|
|
ret.push_back(std::move(comp));
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Access fully-canonicalized absolute path in UTF-8
|
|
|
|
* @return Absolute path reference
|
|
|
|
*/
|
|
|
|
std::string_view getAbsolutePathUTF8() const {
|
2015-06-09 19:40:03 -07:00
|
|
|
#if HECL_UCS2
|
2018-12-07 21:18:42 -08:00
|
|
|
return m_utf8AbsPath;
|
2015-06-09 19:40:03 -07:00
|
|
|
#else
|
2018-12-07 21:18:42 -08:00
|
|
|
return m_absPath;
|
2015-06-09 19:40:03 -07:00
|
|
|
#endif
|
2018-12-07 21:18:42 -08:00
|
|
|
}
|
2015-06-09 19:40:03 -07:00
|
|
|
|
2018-12-07 21:18:42 -08:00
|
|
|
std::string_view getRelativePathUTF8() const {
|
2015-06-09 19:40:03 -07:00
|
|
|
#if HECL_UCS2
|
2018-12-07 21:18:42 -08:00
|
|
|
return m_utf8RelPath;
|
2015-06-09 19:40:03 -07:00
|
|
|
#else
|
2018-12-07 21:18:42 -08:00
|
|
|
return m_relPath;
|
2015-06-09 19:40:03 -07:00
|
|
|
#endif
|
2018-12-07 21:18:42 -08:00
|
|
|
}
|
2015-06-09 19:40:03 -07:00
|
|
|
|
2018-12-07 21:18:42 -08:00
|
|
|
SystemStringView getAuxInfo() const { return m_auxInfo; }
|
2016-04-04 18:49:42 -07:00
|
|
|
|
2018-12-07 21:18:42 -08:00
|
|
|
std::string_view getAuxInfoUTF8() const {
|
2016-04-04 18:49:42 -07:00
|
|
|
#if HECL_UCS2
|
2018-12-07 21:18:42 -08:00
|
|
|
return m_utf8AuxInfo;
|
2016-04-04 18:49:42 -07:00
|
|
|
#else
|
2018-12-07 21:18:42 -08:00
|
|
|
return m_auxInfo;
|
2016-04-04 18:49:42 -07:00
|
|
|
#endif
|
2018-12-07 21:18:42 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Construct a path with the aux info overwritten with specified string
|
|
|
|
* @param auxStr string to replace existing auxInfo with
|
|
|
|
*/
|
|
|
|
ProjectPath ensureAuxInfo(SystemStringView auxStr) const {
|
|
|
|
if (auxStr.empty())
|
|
|
|
return ProjectPath(getProject(), getRelativePath());
|
|
|
|
else
|
|
|
|
return ProjectPath(getProject(), SystemString(getRelativePath()) + _SYS_STR('|') + auxStr.data());
|
|
|
|
}
|
2016-08-30 18:13:00 -07:00
|
|
|
|
2016-09-18 18:02:57 -07:00
|
|
|
#if HECL_UCS2
|
2018-12-07 21:18:42 -08:00
|
|
|
ProjectPath ensureAuxInfo(std::string_view auxStr) const {
|
|
|
|
return ProjectPath(getProject(), SystemString(getRelativePath()) + _SYS_STR('|') + UTF8ToWide(auxStr));
|
|
|
|
}
|
2016-09-18 18:02:57 -07:00
|
|
|
#endif
|
|
|
|
|
2019-10-01 00:23:35 -07:00
|
|
|
template<typename StringT>
|
|
|
|
class EncodableString {
|
|
|
|
friend class ProjectPath;
|
|
|
|
using EncStringView = std::basic_string_view<typename StringT::value_type>;
|
|
|
|
StringT m_ownedString;
|
|
|
|
EncStringView m_stringView;
|
|
|
|
EncodableString(StringT s) : m_ownedString(std::move(s)), m_stringView(m_ownedString) {}
|
|
|
|
EncodableString(EncStringView sv) : m_stringView(sv) {}
|
|
|
|
EncodableString(const EncodableString&) = delete;
|
|
|
|
EncodableString& operator=(const EncodableString&) = delete;
|
|
|
|
EncodableString(EncodableString&&) = delete;
|
|
|
|
EncodableString& operator=(EncodableString&&) = delete;
|
|
|
|
public:
|
|
|
|
operator EncStringView() const { return m_stringView; }
|
|
|
|
};
|
|
|
|
|
|
|
|
EncodableString<SystemString> getEncodableString() const {
|
|
|
|
if (!getAuxInfo().empty())
|
|
|
|
return {SystemString(getRelativePath()) + _SYS_STR('|') + getAuxInfo().data()};
|
|
|
|
else
|
|
|
|
return {getRelativePath()};
|
|
|
|
}
|
|
|
|
|
|
|
|
EncodableString<std::string> getEncodableStringUTF8() const {
|
|
|
|
if (!getAuxInfo().empty())
|
|
|
|
return {std::string(getRelativePathUTF8()) + '|' + getAuxInfoUTF8().data()};
|
|
|
|
else
|
|
|
|
return {getRelativePathUTF8()};
|
|
|
|
}
|
|
|
|
|
2018-12-07 21:18:42 -08:00
|
|
|
/**
|
|
|
|
* @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) */
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Get type of path based on syntax and filesystem queries
|
|
|
|
* @return Type of path
|
|
|
|
*/
|
|
|
|
Type getPathType() const;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Test if nothing exists at path
|
|
|
|
* @return True if nothing exists at path
|
|
|
|
*/
|
|
|
|
bool isNone() const { return getPathType() == Type::None; }
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Test if regular file exists at path
|
|
|
|
* @return True if regular file exists at path
|
|
|
|
*/
|
|
|
|
bool isFile() const { return getPathType() == Type::File; }
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Test if directory exists at path
|
|
|
|
* @return True if directory exists at path
|
|
|
|
*/
|
|
|
|
bool isDirectory() const { return getPathType() == Type::Directory; }
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Certain singular resource targets are cooked based on this test
|
|
|
|
* @return True if file or glob
|
|
|
|
*/
|
|
|
|
bool isFileOrGlob() const {
|
|
|
|
Type type = getPathType();
|
|
|
|
return (type == Type::File || type == Type::Glob);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @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 Insert directory children into list
|
|
|
|
* @param outPaths list to append children to
|
|
|
|
*/
|
|
|
|
void getDirChildren(std::map<SystemString, ProjectPath>& outPaths) const;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Construct DirectoryEnumerator set to project path
|
|
|
|
*/
|
|
|
|
hecl::DirectoryEnumerator enumerateDir() const;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Insert glob matches into existing vector
|
|
|
|
* @param outPaths Vector to add matches to (will not erase existing contents)
|
|
|
|
*/
|
|
|
|
void getGlobResults(std::vector<ProjectPath>& 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 == _SYS_STR('/') || ch == _SYS_STR('\\'))
|
|
|
|
++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 directory chain leading up to path
|
|
|
|
* @param includeLastComp if set, the ProjectPath is assumed to be a
|
|
|
|
* directory, creating the final component
|
|
|
|
*/
|
|
|
|
void makeDirChain(bool includeLastComp) const {
|
|
|
|
std::vector<hecl::SystemString> comps = getPathComponents();
|
|
|
|
auto end = comps.cend();
|
|
|
|
if (end != comps.cbegin() && !includeLastComp)
|
|
|
|
--end;
|
|
|
|
ProjectPath compPath(*m_proj, _SYS_STR("."));
|
|
|
|
for (auto it = comps.cbegin(); it != end; ++it) {
|
|
|
|
compPath = ProjectPath(compPath, *it);
|
|
|
|
compPath.makeDir();
|
2015-08-04 14:37:12 -07:00
|
|
|
}
|
2018-12-07 21:18:42 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Fetch project that contains path
|
|
|
|
* @return Project
|
|
|
|
*/
|
|
|
|
Database::Project& getProject() const {
|
|
|
|
if (!m_proj)
|
2020-04-11 15:48:11 -07:00
|
|
|
LogModule.report(logvisor::Fatal, FMT_STRING("ProjectPath::getProject() called on unqualified path"));
|
2018-12-07 21:18:42 -08:00
|
|
|
return *m_proj;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief HECL-specific xxhash
|
|
|
|
* @return unique hash value
|
|
|
|
*/
|
2019-08-23 23:52:24 -07:00
|
|
|
Hash hash() const noexcept { return m_hash; }
|
|
|
|
bool operator==(const ProjectPath& other) const noexcept { return m_hash == other.m_hash; }
|
|
|
|
bool operator!=(const ProjectPath& other) const noexcept { return !operator==(other); }
|
2019-10-01 00:23:35 -07:00
|
|
|
|
|
|
|
uint32_t parsedHash32() const;
|
2015-05-27 02:09:05 -07:00
|
|
|
};
|
|
|
|
|
2016-08-30 18:13:00 -07:00
|
|
|
/**
|
2016-08-31 16:07:40 -07:00
|
|
|
* @brief Handy functions not directly provided via STL strings
|
2016-08-30 18:13:00 -07:00
|
|
|
*/
|
2018-12-07 21:18:42 -08:00
|
|
|
class StringUtils {
|
2016-08-30 18:13:00 -07:00
|
|
|
public:
|
2018-12-07 21:18:42 -08:00
|
|
|
static bool BeginsWith(SystemStringView str, SystemStringView test) {
|
|
|
|
if (test.size() > str.size())
|
|
|
|
return false;
|
2019-08-14 20:47:56 -07:00
|
|
|
return str.compare(0, test.size(), test) == 0;
|
2018-12-07 21:18:42 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
static bool EndsWith(SystemStringView str, SystemStringView test) {
|
|
|
|
if (test.size() > str.size())
|
|
|
|
return false;
|
2019-08-14 20:47:56 -07:00
|
|
|
return str.compare(str.size() - test.size(), SystemStringView::npos, test) == 0;
|
2018-12-07 21:18:42 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
static std::string TrimWhitespace(std::string_view str) {
|
|
|
|
auto bit = str.begin();
|
2019-08-14 20:35:11 -07:00
|
|
|
while (bit != str.cend() && std::isspace(static_cast<unsigned char>(*bit)))
|
2018-12-07 21:18:42 -08:00
|
|
|
++bit;
|
|
|
|
auto eit = str.end();
|
2019-08-14 20:35:11 -07:00
|
|
|
while (eit != str.cbegin() && std::isspace(static_cast<unsigned char>(*(eit - 1))))
|
2018-12-07 21:18:42 -08:00
|
|
|
--eit;
|
|
|
|
return {bit, eit};
|
|
|
|
}
|
2016-10-01 16:18:52 -07:00
|
|
|
|
2016-09-18 16:46:49 -07:00
|
|
|
#if HECL_UCS2
|
2018-12-07 21:18:42 -08:00
|
|
|
static bool BeginsWith(std::string_view str, std::string_view test) {
|
|
|
|
if (test.size() > str.size())
|
|
|
|
return false;
|
2019-08-14 20:47:56 -07:00
|
|
|
return str.compare(0, test.size(), test) == 0;
|
2018-12-07 21:18:42 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
static bool EndsWith(std::string_view str, std::string_view test) {
|
|
|
|
if (test.size() > str.size())
|
|
|
|
return false;
|
2019-08-14 20:47:56 -07:00
|
|
|
return str.compare(str.size() - test.size(), std::string_view::npos, test) == 0;
|
2018-12-07 21:18:42 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
static SystemString TrimWhitespace(SystemStringView str) {
|
|
|
|
auto bit = str.begin();
|
2019-08-14 20:35:11 -07:00
|
|
|
while (bit != str.cend() && std::iswspace(*bit))
|
2018-12-07 21:18:42 -08:00
|
|
|
++bit;
|
|
|
|
auto eit = str.end();
|
2019-08-14 20:35:11 -07:00
|
|
|
while (eit != str.cbegin() && std::iswspace(*(eit - 1)))
|
2018-12-07 21:18:42 -08:00
|
|
|
--eit;
|
|
|
|
return {bit, eit};
|
|
|
|
}
|
2016-09-18 16:46:49 -07:00
|
|
|
#endif
|
2016-08-30 18:13:00 -07:00
|
|
|
};
|
|
|
|
|
2016-08-11 19:33:03 -07:00
|
|
|
/**
|
|
|
|
* @brief Mutex-style centralized resource-path tracking
|
|
|
|
*
|
|
|
|
* Provides a means to safely parallelize resource processing; detecting when another
|
|
|
|
* thread is working on the same resource.
|
|
|
|
*/
|
2018-12-07 21:18:42 -08:00
|
|
|
class ResourceLock {
|
|
|
|
static bool SetThreadRes(const ProjectPath& path);
|
|
|
|
static void ClearThreadRes();
|
|
|
|
bool good;
|
|
|
|
|
2016-08-11 19:33:03 -07:00
|
|
|
public:
|
2019-08-14 23:10:49 -07:00
|
|
|
explicit operator bool() const { return good; }
|
2018-12-07 21:18:42 -08:00
|
|
|
static bool InProgress(const ProjectPath& path);
|
2019-08-14 23:10:49 -07:00
|
|
|
explicit ResourceLock(const ProjectPath& path) : good{SetThreadRes(path)} {}
|
2018-12-07 21:18:42 -08:00
|
|
|
~ResourceLock() {
|
|
|
|
if (good)
|
|
|
|
ClearThreadRes();
|
|
|
|
}
|
|
|
|
ResourceLock(const ResourceLock&) = delete;
|
|
|
|
ResourceLock& operator=(const ResourceLock&) = delete;
|
|
|
|
ResourceLock(ResourceLock&&) = delete;
|
|
|
|
ResourceLock& operator=(ResourceLock&&) = delete;
|
2016-08-11 19:33:03 -07:00
|
|
|
};
|
|
|
|
|
2015-06-10 21:55:06 -07:00
|
|
|
/**
|
|
|
|
* @brief Search from within provided directory for the project root
|
|
|
|
* @param path absolute or relative file path to search from
|
2015-09-29 23:23:07 -07:00
|
|
|
* @return Newly-constructed root path (bool-evaluating to false if not found)
|
|
|
|
*/
|
2017-11-12 22:13:53 -08:00
|
|
|
ProjectRootPath SearchForProject(SystemStringView path);
|
2015-09-29 23:23:07 -07:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @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)
|
2015-06-10 21:55:06 -07:00
|
|
|
*/
|
2017-11-12 22:13:53 -08:00
|
|
|
ProjectRootPath SearchForProject(SystemStringView path, SystemString& subpathOut);
|
2015-05-27 02:09:05 -07:00
|
|
|
|
2015-09-30 17:40:06 -07:00
|
|
|
/**
|
|
|
|
* @brief Test if given path is a PNG (based on file header)
|
|
|
|
* @param path Path to test
|
|
|
|
* @return true if PNG
|
|
|
|
*/
|
2016-03-04 15:02:44 -08:00
|
|
|
bool IsPathPNG(const hecl::ProjectPath& path);
|
2015-09-30 17:40:06 -07:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Test if given path is a blend (based on file header)
|
|
|
|
* @param path Path to test
|
|
|
|
* @return true if blend
|
|
|
|
*/
|
2016-03-04 15:02:44 -08:00
|
|
|
bool IsPathBlend(const hecl::ProjectPath& path);
|
2015-09-30 17:40:06 -07:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Test if given path is a yaml (based on file extension)
|
|
|
|
* @param path Path to test
|
|
|
|
* @return true if yaml
|
|
|
|
*/
|
2016-03-04 15:02:44 -08:00
|
|
|
bool IsPathYAML(const hecl::ProjectPath& path);
|
2015-09-30 17:40:06 -07:00
|
|
|
|
2015-09-12 20:40:22 -07:00
|
|
|
#undef bswap16
|
|
|
|
#undef bswap32
|
|
|
|
#undef bswap64
|
2015-05-27 02:09:05 -07:00
|
|
|
|
|
|
|
/* Type-sensitive byte swappers */
|
2015-06-09 15:19:59 -07:00
|
|
|
template <typename T>
|
2019-08-23 23:44:35 -07:00
|
|
|
constexpr T bswap16(T val) noexcept {
|
2015-05-22 01:21:44 -07:00
|
|
|
#if __GNUC__
|
2018-12-07 21:18:42 -08:00
|
|
|
return __builtin_bswap16(val);
|
2015-05-22 01:21:44 -07:00
|
|
|
#elif _WIN32
|
2018-12-07 21:18:42 -08:00
|
|
|
return _byteswap_ushort(val);
|
2015-05-22 01:21:44 -07:00
|
|
|
#else
|
2018-12-07 21:18:42 -08:00
|
|
|
return (val = (val << 8) | ((val >> 8) & 0xFF));
|
2015-05-22 01:21:44 -07:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2015-06-09 15:19:59 -07:00
|
|
|
template <typename T>
|
2019-08-23 23:44:35 -07:00
|
|
|
constexpr T bswap32(T val) noexcept {
|
2015-05-22 01:21:44 -07:00
|
|
|
#if __GNUC__
|
2018-12-07 21:18:42 -08:00
|
|
|
return __builtin_bswap32(val);
|
2015-05-22 01:21:44 -07:00
|
|
|
#elif _WIN32
|
2018-12-07 21:18:42 -08:00
|
|
|
return _byteswap_ulong(val);
|
2015-05-22 01:21:44 -07:00
|
|
|
#else
|
2018-12-07 21:18:42 -08:00
|
|
|
val = (val & 0x0000FFFF) << 16 | (val & 0xFFFF0000) >> 16;
|
|
|
|
val = (val & 0x00FF00FF) << 8 | (val & 0xFF00FF00) >> 8;
|
|
|
|
return val;
|
2015-05-22 01:21:44 -07:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2015-06-09 15:19:59 -07:00
|
|
|
template <typename T>
|
2019-08-23 23:44:35 -07:00
|
|
|
constexpr T bswap64(T val) noexcept {
|
2015-05-22 01:21:44 -07:00
|
|
|
#if __GNUC__
|
2018-12-07 21:18:42 -08:00
|
|
|
return __builtin_bswap64(val);
|
2015-05-22 01:21:44 -07:00
|
|
|
#elif _WIN32
|
2018-12-07 21:18:42 -08:00
|
|
|
return _byteswap_uint64(val);
|
2015-05-22 01:21:44 -07:00
|
|
|
#else
|
2018-12-07 21:18:42 -08:00
|
|
|
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);
|
2015-05-22 01:21:44 -07:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2015-05-27 02:09:05 -07:00
|
|
|
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
2019-08-23 23:44:35 -07:00
|
|
|
constexpr int16_t SBig(int16_t val) noexcept { return bswap16(val); }
|
|
|
|
constexpr uint16_t SBig(uint16_t val) noexcept { return bswap16(val); }
|
|
|
|
constexpr int32_t SBig(int32_t val) noexcept { return bswap32(val); }
|
|
|
|
constexpr uint32_t SBig(uint32_t val) noexcept { return bswap32(val); }
|
|
|
|
constexpr int64_t SBig(int64_t val) noexcept { return bswap64(val); }
|
|
|
|
constexpr uint64_t SBig(uint64_t val) noexcept { return bswap64(val); }
|
|
|
|
constexpr float SBig(float val) noexcept {
|
2019-08-14 23:27:36 -07:00
|
|
|
union {
|
|
|
|
float f;
|
|
|
|
atInt32 i;
|
|
|
|
} uval1 = {val};
|
|
|
|
union {
|
|
|
|
atInt32 i;
|
|
|
|
float f;
|
|
|
|
} uval2 = {bswap32(uval1.i)};
|
2019-06-11 19:01:19 -07:00
|
|
|
return uval2.f;
|
2016-03-05 17:19:32 -08:00
|
|
|
}
|
2019-08-23 23:44:35 -07:00
|
|
|
constexpr double SBig(double val) noexcept {
|
2019-08-14 23:27:36 -07:00
|
|
|
union {
|
|
|
|
double f;
|
|
|
|
atInt64 i;
|
|
|
|
} uval1 = {val};
|
|
|
|
union {
|
|
|
|
atInt64 i;
|
|
|
|
double f;
|
|
|
|
} uval2 = {bswap64(uval1.i)};
|
2019-06-11 19:01:19 -07:00
|
|
|
return uval2.f;
|
2016-03-05 17:19:32 -08:00
|
|
|
}
|
2016-12-16 15:05:11 -08:00
|
|
|
#ifndef SBIG
|
2018-12-07 21:18:42 -08:00
|
|
|
#define SBIG(q) (((q)&0x000000FF) << 24 | ((q)&0x0000FF00) << 8 | ((q)&0x00FF0000) >> 8 | ((q)&0xFF000000) >> 24)
|
2016-12-16 15:05:11 -08:00
|
|
|
#endif
|
2015-06-11 21:02:23 -07:00
|
|
|
|
2019-08-23 23:44:35 -07:00
|
|
|
constexpr int16_t SLittle(int16_t val) noexcept { return val; }
|
|
|
|
constexpr uint16_t SLittle(uint16_t val) noexcept { return val; }
|
|
|
|
constexpr int32_t SLittle(int32_t val) noexcept { return val; }
|
|
|
|
constexpr uint32_t SLittle(uint32_t val) noexcept { return val; }
|
|
|
|
constexpr int64_t SLittle(int64_t val) noexcept { return val; }
|
|
|
|
constexpr uint64_t SLittle(uint64_t val) noexcept { return val; }
|
|
|
|
constexpr float SLittle(float val) noexcept { return val; }
|
|
|
|
constexpr double SLittle(double val) noexcept { return val; }
|
2016-12-16 15:05:11 -08:00
|
|
|
#ifndef SLITTLE
|
2015-07-19 13:52:22 -07:00
|
|
|
#define SLITTLE(q) (q)
|
2016-12-16 15:05:11 -08:00
|
|
|
#endif
|
2015-05-27 02:09:05 -07:00
|
|
|
#else
|
2019-08-23 23:44:35 -07:00
|
|
|
constexpr int16_t SLittle(int16_t val) noexcept { return bswap16(val); }
|
|
|
|
constexpr uint16_t SLittle(uint16_t val) noexcept { return bswap16(val); }
|
|
|
|
constexpr int32_t SLittle(int32_t val) noexcept { return bswap32(val); }
|
|
|
|
constexpr uint32_t SLittle(uint32_t val) noexcept { return bswap32(val); }
|
|
|
|
constexpr int64_t SLittle(int64_t val) noexcept { return bswap64(val); }
|
|
|
|
constexpr uint64_t SLittle(uint64_t val) noexcept { return bswap64(val); }
|
|
|
|
constexpr float SLittle(float val) noexcept {
|
2018-12-07 21:18:42 -08:00
|
|
|
int32_t ival = bswap32(*((int32_t*)(&val)));
|
|
|
|
return *((float*)(&ival));
|
2016-03-05 17:19:32 -08:00
|
|
|
}
|
2019-08-23 23:44:35 -07:00
|
|
|
constexpr double SLittle(double val) noexcept {
|
2018-12-07 21:18:42 -08:00
|
|
|
int64_t ival = bswap64(*((int64_t*)(&val)));
|
|
|
|
return *((double*)(&ival));
|
2016-03-05 17:19:32 -08:00
|
|
|
}
|
2016-12-16 15:05:11 -08:00
|
|
|
#ifndef SLITTLE
|
2018-12-07 21:18:42 -08:00
|
|
|
#define SLITTLE(q) (((q)&0x000000FF) << 24 | ((q)&0x0000FF00) << 8 | ((q)&0x00FF0000) >> 8 | ((q)&0xFF000000) >> 24)
|
2016-12-16 15:05:11 -08:00
|
|
|
#endif
|
2015-06-11 21:02:23 -07:00
|
|
|
|
2019-08-23 23:44:35 -07:00
|
|
|
constexpr int16_t SBig(int16_t val) noexcept { return val; }
|
|
|
|
constexpr uint16_t SBig(uint16_t val) noexcept { return val; }
|
|
|
|
constexpr int32_t SBig(int32_t val) noexcept { return val; }
|
|
|
|
constexpr uint32_t SBig(uint32_t val) noexcept { return val; }
|
|
|
|
constexpr int64_t SBig(int64_t val) noexcept { return val; }
|
|
|
|
constexpr uint64_t SBig(uint64_t val) noexcept { return val; }
|
|
|
|
constexpr float SBig(float val) noexcept { return val; }
|
|
|
|
constexpr double SBig(double val) noexcept { return val; }
|
2016-12-16 15:05:11 -08:00
|
|
|
#ifndef SBIG
|
2015-07-19 13:52:22 -07:00
|
|
|
#define SBIG(q) (q)
|
2015-05-27 02:09:05 -07:00
|
|
|
#endif
|
2016-12-16 15:05:11 -08:00
|
|
|
#endif
|
2015-05-27 02:09:05 -07:00
|
|
|
|
2019-05-07 20:47:34 -07:00
|
|
|
template <typename SizeT>
|
2019-08-23 23:45:40 -07:00
|
|
|
constexpr void hash_combine_impl(SizeT& seed, SizeT value) noexcept {
|
2019-08-14 23:27:36 -07:00
|
|
|
seed ^= value + 0x9e3779b9 + (seed << 6) + (seed >> 2);
|
2019-05-07 20:47:34 -07:00
|
|
|
}
|
|
|
|
|
2018-12-07 21:18:42 -08:00
|
|
|
} // namespace hecl
|
2015-05-19 22:22:32 -07:00
|
|
|
|
2019-10-01 00:23:35 -07:00
|
|
|
#define CHAINED_SIGNAL_HANDLER(name, signal) \
|
|
|
|
class ChainedSignalHandler_##name { \
|
|
|
|
typedef void(*sighandler_t)(int); \
|
|
|
|
typedef void(*sigaction_t)(int, siginfo_t*, void*); \
|
|
|
|
static std::atomic_bool m_isSetup; \
|
|
|
|
static sighandler_t m_nextsh; \
|
|
|
|
static sigaction_t m_nextsa; \
|
|
|
|
static void my_sig_action(int sig, siginfo_t* si, void* ctx); \
|
|
|
|
static void sig_action(int sig, siginfo_t* si, void* ctx) { \
|
|
|
|
my_sig_action(sig, si, ctx); \
|
|
|
|
if (m_nextsa) \
|
|
|
|
m_nextsa(sig, si, ctx); \
|
|
|
|
else if (m_nextsh) \
|
|
|
|
m_nextsh(sig); \
|
|
|
|
} \
|
|
|
|
public: \
|
|
|
|
static void setup() { \
|
|
|
|
if (ChainedSignalHandler_##name::m_isSetup.exchange(true) == true) \
|
|
|
|
return; \
|
|
|
|
{ \
|
|
|
|
struct sigaction sold; \
|
|
|
|
if (sigaction(signal, nullptr, &sold) == 0) { \
|
|
|
|
if (sold.sa_flags & SA_SIGINFO) \
|
|
|
|
m_nextsa = sold.sa_sigaction; \
|
|
|
|
else \
|
|
|
|
m_nextsh = sold.sa_handler; \
|
|
|
|
} \
|
|
|
|
} \
|
|
|
|
{ \
|
|
|
|
struct sigaction snew = {}; \
|
|
|
|
snew.sa_sigaction = sig_action; \
|
|
|
|
snew.sa_flags = SA_RESTART | SA_NOCLDSTOP | SA_SIGINFO; \
|
|
|
|
sigaction(signal, &snew, nullptr); \
|
|
|
|
} \
|
|
|
|
} \
|
|
|
|
}; \
|
|
|
|
std::atomic_bool ChainedSignalHandler_##name::m_isSetup = {false}; \
|
|
|
|
ChainedSignalHandler_##name::sighandler_t ChainedSignalHandler_##name::m_nextsh = {}; \
|
|
|
|
ChainedSignalHandler_##name::sigaction_t ChainedSignalHandler_##name::m_nextsa = {}; \
|
|
|
|
inline void ChainedSignalHandler_##name::my_sig_action
|
|
|
|
|
|
|
|
#define SETUP_CHAINED_SIGNAL_HANDLER(name) \
|
|
|
|
ChainedSignalHandler_##name::setup()
|
|
|
|
|
2018-12-07 21:18:42 -08:00
|
|
|
namespace std {
|
|
|
|
template <>
|
|
|
|
struct hash<hecl::ProjectPath> {
|
|
|
|
size_t operator()(const hecl::ProjectPath& val) const noexcept { return val.hash().valSizeT(); }
|
2015-06-10 16:34:14 -07:00
|
|
|
};
|
2018-12-07 21:18:42 -08:00
|
|
|
template <>
|
|
|
|
struct hash<hecl::Hash> {
|
|
|
|
size_t operator()(const hecl::Hash& val) const noexcept { return val.valSizeT(); }
|
2015-11-12 18:12:09 -08:00
|
|
|
};
|
2018-12-07 21:18:42 -08:00
|
|
|
} // namespace std
|
2019-07-19 21:22:58 -07:00
|
|
|
|
2019-07-27 18:19:48 -07:00
|
|
|
FMT_CUSTOM_FORMATTER(hecl::SystemUTF8Conv, "{}", obj.str())
|
|
|
|
FMT_CUSTOM_FORMATTER(hecl::SystemStringConv, "{}", obj.sys_str())
|