flatten 20260225
This commit is contained in:
98
tjp/core/file/Contents.h
Normal file
98
tjp/core/file/Contents.h
Normal file
@@ -0,0 +1,98 @@
|
||||
// TJP COPYRIGHT HEADER
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <tjp/core/containers/Vector.h>
|
||||
#include <tjp/core/string/StringView.h>
|
||||
#include <tjp/core/string/String.h>
|
||||
#include <tjp/core/const_expr/Str.hpp>
|
||||
|
||||
namespace tjp::core::file {
|
||||
|
||||
using Contents = Vector<char>;
|
||||
|
||||
struct ContentsView
|
||||
{
|
||||
using value_type = char;
|
||||
using pointer_type = const value_type *;
|
||||
|
||||
const char *data_;
|
||||
const size_t size_;
|
||||
|
||||
constexpr
|
||||
pointer_type data() const
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
constexpr
|
||||
size_t size() const
|
||||
{
|
||||
return size_;
|
||||
}
|
||||
|
||||
constexpr ContentsView() :
|
||||
data_((char *)nullptr),
|
||||
size_(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
constexpr ContentsView(const T *data, const size_t size) :
|
||||
data_((pointer_type)data),
|
||||
size_(size * sizeof(T))
|
||||
{
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ContentsView(const Vector<T> &v) :
|
||||
ContentsView(v.data(), v.size())
|
||||
{
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr ContentsView(const T &v) :
|
||||
ContentsView(v.data(), v.size())
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
constexpr ContentsView(const char *data) :
|
||||
ContentsView(data, core::const_expr::length(data))
|
||||
{
|
||||
}
|
||||
|
||||
constexpr ContentsView(const ContentsView &v) :
|
||||
ContentsView(v.data(), v.size())
|
||||
{
|
||||
}
|
||||
|
||||
constexpr ContentsView(const StringView &v) :
|
||||
ContentsView(v.data(), v.size())
|
||||
{
|
||||
}
|
||||
|
||||
ContentsView(const String &v) :
|
||||
ContentsView(v.c_str(), v.size())
|
||||
{
|
||||
}
|
||||
|
||||
operator Contents() const
|
||||
{
|
||||
return Contents(data_, data_ + size_);
|
||||
}
|
||||
|
||||
operator StringView() const
|
||||
{
|
||||
return StringView(data_, size_);
|
||||
}
|
||||
|
||||
operator String() const
|
||||
{
|
||||
return String(data_, size_);
|
||||
}
|
||||
} ;
|
||||
|
||||
} // namespace
|
||||
24
tjp/core/file/Directories.h
Normal file
24
tjp/core/file/Directories.h
Normal file
@@ -0,0 +1,24 @@
|
||||
// TJP COPYRIGHT HEADER
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <tjp/core/system/System.h>
|
||||
|
||||
#if defined(SYS_APPLE)
|
||||
|
||||
#include "apple/Directories.h"
|
||||
|
||||
namespace tjp::core::file {
|
||||
using namespace file::apple;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#include "linux/Directories.h"
|
||||
|
||||
namespace tjp::core::file {
|
||||
using namespace file::linux;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
158
tjp/core/file/File.cpp
Normal file
158
tjp/core/file/File.cpp
Normal file
@@ -0,0 +1,158 @@
|
||||
// TJP COPYRIGHT HEADER
|
||||
|
||||
#include "File.h"
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <utime.h>
|
||||
|
||||
#include <tjp/core/exception/Exception.hpp>
|
||||
#include <tjp/core/string/to_string.hpp>
|
||||
#include <tjp/core/string/StringView+concat.hpp>
|
||||
|
||||
namespace tjp::core::file {
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
Optional<size_t> size(const StringView& path)
|
||||
{
|
||||
std::ifstream file(String(path), std::ios::ate | std::ios::binary);
|
||||
bool exists = (bool)file;
|
||||
|
||||
if (!exists || !file.is_open())
|
||||
return {};
|
||||
|
||||
size_t fileSize = (size_t)file.tellg();
|
||||
return fileSize;
|
||||
}
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
Optional<Contents> readRange(const StringView& path, s64 offset, size_t length)
|
||||
{
|
||||
std::ifstream file(String(path), std::ios::ate | std::ios::binary);
|
||||
bool exists = (bool)file;
|
||||
|
||||
if (!exists || !file.is_open())
|
||||
return {};
|
||||
|
||||
size_t fileSize = (size_t)file.tellg();
|
||||
if (offset < 0)
|
||||
offset += s64(fileSize);
|
||||
|
||||
if (offset < 0)
|
||||
return {};
|
||||
|
||||
if (fileSize < offset + length)
|
||||
return {};
|
||||
|
||||
Contents buffer(length);
|
||||
|
||||
file.seekg(offset);
|
||||
file.read(buffer.data(), buffer.size());
|
||||
file.close();
|
||||
|
||||
return buffer;
|
||||
};
|
||||
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
bool pathExists (const StringView &file)
|
||||
{
|
||||
struct stat status;
|
||||
return (stat (file.data(), &status)==0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
bool ensurePath (const StringView &base_, const StringView &path)
|
||||
{
|
||||
char delimiter = '/';
|
||||
|
||||
// find the end of the last portion of the path we need to create
|
||||
auto finalPos = path.rfind(delimiter);
|
||||
if (finalPos == path.npos)
|
||||
return true;
|
||||
|
||||
auto base = String(base_);
|
||||
if (!base.empty() && base.back() != delimiter)
|
||||
base += delimiter;
|
||||
|
||||
decltype(finalPos) pos = 0;
|
||||
|
||||
auto colonpos = path.find(':');
|
||||
auto firstdelimiterpos = path.find(delimiter);
|
||||
|
||||
if (colonpos > 0 && firstdelimiterpos > 0 && colonpos < firstdelimiterpos)
|
||||
{
|
||||
// Skip the first one if there's a colon ('c:/blah/whaterver')
|
||||
pos = path.find (delimiter, pos+1);
|
||||
}
|
||||
|
||||
while (pos<finalPos)
|
||||
{
|
||||
// find the next part of the path to create
|
||||
pos = path.find (delimiter, pos+1);
|
||||
|
||||
|
||||
// does it already exist
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
// pull it out as a separate string
|
||||
std::wstring directory_ = toLongPath(path.substr (0, pos));
|
||||
std::wstring directory = base + directory_;
|
||||
|
||||
WIN32_FILE_ATTRIBUTE_DATA data;
|
||||
bool exists = GetFileAttributesExW (base + directory.c_str(), GetFileExInfoStandard, &data) > 0;
|
||||
|
||||
if (exists)
|
||||
{
|
||||
// if it does exist, but is not a directory, ensurePath fails
|
||||
if (!(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// if it does not exist create it, if this fails ensurePath fails
|
||||
if (_wmkdir (directory.c_str())!=0)
|
||||
return false;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
auto directory_ = path.substr (0, pos);
|
||||
auto directory = base + directory_;
|
||||
|
||||
struct stat status;
|
||||
int exists = (stat (directory.c_str(), &status)==0);
|
||||
|
||||
if (exists)
|
||||
{
|
||||
// if it does exist, but is not a directory, ensurePath fails
|
||||
if (!(status.st_mode & S_IFDIR))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// if it does not exist create it, if this fails ensurePath fails
|
||||
auto result = mkdir (directory.c_str(), (S_IRWXU | S_IRWXG | S_IRWXO));
|
||||
if (result)
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
bool ensurePath (const StringView &path)
|
||||
{
|
||||
return ensurePath("", path);
|
||||
}
|
||||
|
||||
|
||||
} // namespace
|
||||
46
tjp/core/file/File.h
Normal file
46
tjp/core/file/File.h
Normal file
@@ -0,0 +1,46 @@
|
||||
// TJP COPYRIGHT HEADER
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <tjp/core/header_only/compile.h>
|
||||
|
||||
#include "Contents.h"
|
||||
#include <tjp/core/string/StringView.h>
|
||||
#include <tjp/core/string/String.h>
|
||||
#include <tjp/core/containers/Vector.h>
|
||||
#include <tjp/core/containers/Optional.h>
|
||||
#include <tjp/core/types/Types.h>
|
||||
|
||||
#include <time.h>
|
||||
#include "file_exists.hpp"
|
||||
#include "read.hpp"
|
||||
#include "write.hpp"
|
||||
#include "times.hpp"
|
||||
|
||||
namespace tjp::core::file {
|
||||
|
||||
Optional<size_t> size(const StringView &path);
|
||||
|
||||
|
||||
Optional<Contents> readRange(const StringView &path, s64 offset, size_t length);
|
||||
|
||||
|
||||
Contents readFile(const StringView &path);
|
||||
|
||||
|
||||
bool writeFile(const StringView &path, const ContentsView &contents, const WriteOptions &options = {});
|
||||
|
||||
bool pathExists (const StringView &path);
|
||||
bool ensurePath (const StringView &path);
|
||||
bool ensurePath (const StringView &base, const StringView &path);
|
||||
|
||||
|
||||
bool deleteFile(const StringView &path);
|
||||
|
||||
Vector<String> getFiles(const StringView &path, const Optional<String> &extension);
|
||||
|
||||
} // namespace
|
||||
|
||||
#ifdef TJP_CORE_HEADER_ONLY
|
||||
#include "File.cpp"
|
||||
#endif
|
||||
328
tjp/core/file/File_Messy.cpp
Normal file
328
tjp/core/file/File_Messy.cpp
Normal file
@@ -0,0 +1,328 @@
|
||||
// TJP COPYRIGHT HEADER
|
||||
|
||||
#include "File.h"
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <tjp/core/exception/Exception.hpp>
|
||||
#include <tjp/core/system/System.h>
|
||||
|
||||
#include <tjp/core/string/to_string.hpp>
|
||||
#include "../random/Random.h"
|
||||
|
||||
#include <tjp/core/log/Log.h>
|
||||
#include <tjp/core/log/LogOf.h>
|
||||
|
||||
#include <dirent.h>
|
||||
#include <sys/types.h>
|
||||
#include <string>
|
||||
#include <queue>
|
||||
#include <filesystem>
|
||||
|
||||
#include <utime.h>
|
||||
|
||||
namespace tjp::core::file {
|
||||
|
||||
std::vector<char> readFile(const StringView& path)
|
||||
{
|
||||
std::ifstream file(String(path), std::ios::ate | std::ios::binary);
|
||||
bool exists = (bool)file;
|
||||
|
||||
if (!exists || !file.is_open()) {
|
||||
throw Exception { "failed to open file " + String(path) };
|
||||
}
|
||||
|
||||
size_t fileSize = (size_t)file.tellg();
|
||||
std::vector<char> buffer(fileSize);
|
||||
|
||||
file.seekg(0);
|
||||
file.read(buffer.data(), fileSize);
|
||||
|
||||
file.close();
|
||||
|
||||
return buffer;
|
||||
};
|
||||
|
||||
bool writeFile(const StringView &path_, const ContentsView &contents, const WriteOptions &options)
|
||||
{
|
||||
auto path = String(path_);
|
||||
auto extension = "." + core::to_string(core::StandardRandom.next<u32>());
|
||||
auto temporary = path + extension;
|
||||
|
||||
std::ofstream file(temporary, std::ios::binary);
|
||||
if (!file)
|
||||
return false;
|
||||
|
||||
file.write(contents.data(), contents.size());
|
||||
file.close();
|
||||
|
||||
remove(path.c_str());
|
||||
if (rename(temporary.c_str(), path.c_str()))
|
||||
return false;
|
||||
|
||||
if (options.lastModifiedTime)
|
||||
{
|
||||
setFileLastModifiedTime(path, options.lastModifiedTime);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
bool ensurePath (const StringView &path)
|
||||
{
|
||||
return ensurePath("", path);
|
||||
}
|
||||
|
||||
bool ensurePath (const StringView &base_, const StringView &path_)
|
||||
{
|
||||
sLogDebug("core::file", logVar(base_) << logVar(path_));
|
||||
|
||||
char delimiter = '/';
|
||||
|
||||
auto path = String(path_);
|
||||
auto base = String(base_);
|
||||
if (!base.empty() && base.back() != delimiter)
|
||||
base += delimiter;
|
||||
|
||||
// find the end of the last portion of the path we need to create
|
||||
auto finalPos = path.rfind (delimiter);
|
||||
|
||||
decltype(finalPos) pos = 0;
|
||||
|
||||
auto colonpos = path.find(':');
|
||||
auto firstdelimiterpos = path.find(delimiter);
|
||||
|
||||
if (colonpos > 0 && firstdelimiterpos > 0 && colonpos < firstdelimiterpos)
|
||||
{
|
||||
// Skip the first one if there's a colon ('c:/blah/whaterver')
|
||||
pos = path.find (delimiter, pos+1);
|
||||
}
|
||||
|
||||
while (pos<finalPos)
|
||||
{
|
||||
// find the next part of the path to create
|
||||
pos = path.find (delimiter, pos+1);
|
||||
|
||||
|
||||
// does it already exist
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
// pull it out as a separate string
|
||||
std::wstring directory_ = toLongPath(path.substr (0, pos));
|
||||
std::wstring directory = base + directory_;
|
||||
|
||||
WIN32_FILE_ATTRIBUTE_DATA data;
|
||||
bool exists = GetFileAttributesExW (base + directory.c_str(), GetFileExInfoStandard, &data) > 0;
|
||||
|
||||
if (exists)
|
||||
{
|
||||
// if it does exist, but is not a directory, ensurePath fails
|
||||
if (!(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// if it does not exist create it, if this fails ensurePath fails
|
||||
if (_wmkdir (directory.c_str())!=0)
|
||||
return false;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
std::string directory_ = path.substr (0, pos);
|
||||
std::string directory = String(base) + directory_;
|
||||
|
||||
sLogDebug("core::file", logVar(path) << logVar(directory));
|
||||
|
||||
struct stat status;
|
||||
int exists = (stat (directory.c_str(), &status)==0);
|
||||
|
||||
if (exists)
|
||||
{
|
||||
// if it does exist, but is not a directory, ensurePath fails
|
||||
if (!(status.st_mode & S_IFDIR))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// if it does not exist create it, if this fails ensurePath fails
|
||||
auto result = mkdir (directory.c_str(), (S_IRWXU | S_IRWXG | S_IRWXO));
|
||||
if (result)
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool fileExists (const StringView &file)
|
||||
{
|
||||
#ifdef WIN32
|
||||
|
||||
WIN32_FILE_ATTRIBUTE_DATA data;
|
||||
bool exists = GetFileAttributesExW (toLongPath(file).c_str(), GetFileExInfoStandard, &data) > 0;
|
||||
|
||||
return exists && !(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
|
||||
|
||||
#else
|
||||
struct stat status;
|
||||
if (stat (file.data(), &status)==0)
|
||||
{
|
||||
// if it is not a directory return true
|
||||
if (!(status.st_mode & S_IFDIR))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
bool pathExists (const StringView &file)
|
||||
{
|
||||
#ifdef WIN32
|
||||
|
||||
WIN32_FILE_ATTRIBUTE_DATA data;
|
||||
bool exists = GetFileAttributesExW (toLongPath(file).c_str(), GetFileExInfoStandard, &data) > 0;
|
||||
|
||||
return exists;
|
||||
|
||||
#else
|
||||
struct stat status;
|
||||
return (stat (file.data(), &status)==0);
|
||||
|
||||
#endif
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool deleteFile(const StringView &file)
|
||||
{
|
||||
return remove(file.data()) == 0;
|
||||
}
|
||||
|
||||
#ifndef SYS_IOS
|
||||
|
||||
Vector<String> getFiles(const StringView &path, const Optional<String> &extension)
|
||||
{
|
||||
Vector<String> files;
|
||||
try
|
||||
{
|
||||
std::queue<std::filesystem::path> dirs;
|
||||
std::filesystem::path basePath = std::filesystem::canonical(path);
|
||||
dirs.push(basePath);
|
||||
|
||||
while (!dirs.empty())
|
||||
{
|
||||
auto current = dirs.front();
|
||||
dirs.pop();
|
||||
|
||||
for (const auto &entry : std::filesystem::directory_iterator(current))
|
||||
{
|
||||
if (entry.is_directory())
|
||||
{
|
||||
dirs.push(entry.path());
|
||||
}
|
||||
else
|
||||
if (entry.is_regular_file())
|
||||
{
|
||||
if (!extension || entry.path().extension() == *extension) {
|
||||
files.push_back(std::filesystem::relative(entry.path(), basePath).string());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (std::filesystem::filesystem_error &)
|
||||
{
|
||||
std::cerr << "caught std::filesystem::filesystem_error " << std::endl;
|
||||
}
|
||||
catch (std::exception &)
|
||||
{
|
||||
std::cerr << "caught std::exception " << std::endl;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
std::cerr << "caught unknown exception " << std::endl;
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#else
|
||||
|
||||
Vector<String> getFiles(const StringView &path_, const Optional<String> &extension)
|
||||
{
|
||||
auto path = String(path_);
|
||||
Vector<String> files;
|
||||
std::queue<std::string> dirs;
|
||||
dirs.push("");
|
||||
|
||||
while (!dirs.empty())
|
||||
{
|
||||
auto relativeDir = dirs.front();
|
||||
std::string absoluteDir = path + relativeDir;
|
||||
dirs.pop();
|
||||
|
||||
DIR *dir = opendir(absoluteDir.c_str());
|
||||
if (!dir) {
|
||||
continue;
|
||||
}
|
||||
|
||||
struct dirent *entry;
|
||||
while ((entry = readdir(dir)) != nullptr)
|
||||
{
|
||||
std::string name(entry->d_name);
|
||||
|
||||
// skip "." and ".."
|
||||
if (name == "." || name == "..") {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string relativePath =
|
||||
relativeDir.empty() ?
|
||||
name :
|
||||
relativeDir + "/" + name;
|
||||
|
||||
if (entry->d_type == DT_DIR)
|
||||
{
|
||||
// enqueue subdirectory
|
||||
dirs.push(relativePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (extension.has_value())
|
||||
{
|
||||
if (name.size() >= extension->size() &&
|
||||
name.compare(name.size() - extension->size(), extension->size(), *extension) == 0)
|
||||
{
|
||||
files.emplace_back(relativePath.c_str());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
files.emplace_back(relativePath.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
169
tjp/core/file/Path.cpp
Normal file
169
tjp/core/file/Path.cpp
Normal file
@@ -0,0 +1,169 @@
|
||||
// TJP COPYRIGHT HEADER
|
||||
|
||||
#ifdef TJP_CORE_HEADER_ONLY
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "Path.h"
|
||||
|
||||
namespace tjp::core::file {
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
std::string_view removeExtensions(const std::string_view &s)
|
||||
{
|
||||
auto lastSlash = s.rfind('/');
|
||||
auto firstDotAfterLastSlash = s.find('.', lastSlash+1);
|
||||
return s.substr(0, firstDotAfterLastSlash);
|
||||
}
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
std::string joinDirs(const std::vector<std::string_view> &dirs)
|
||||
{
|
||||
std::string joined;
|
||||
for (auto &dir : dirs)
|
||||
{
|
||||
if (dir.empty())
|
||||
continue;
|
||||
|
||||
bool first = joined.empty();
|
||||
|
||||
if (dir.front() == '/' && !first)
|
||||
joined += dir.substr(1);
|
||||
else
|
||||
joined += dir;
|
||||
|
||||
if (joined.back() != '/')
|
||||
joined.push_back('/');
|
||||
}
|
||||
|
||||
return joined;
|
||||
}
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
std::string joinPaths(const std::vector<std::string_view> &dirs)
|
||||
{
|
||||
std::string joined;
|
||||
for (auto &dir : dirs)
|
||||
{
|
||||
if (dir.empty())
|
||||
continue;
|
||||
|
||||
bool first = joined.empty();
|
||||
|
||||
if (!first)
|
||||
if (joined.back() != '/')
|
||||
joined.push_back('/');
|
||||
|
||||
if (dir.front() == '/' && !first)
|
||||
joined += dir.substr(1);
|
||||
else
|
||||
joined += dir;
|
||||
}
|
||||
return joined;
|
||||
}
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
std::string joinDirsAndFile(const std::vector<std::string_view> &dirs, const std::string &fileName)
|
||||
{
|
||||
return joinDirs(dirs) + fileName;
|
||||
}
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
std::string convertDelimiter (const std::string &str, char from, char to)
|
||||
{
|
||||
if (from==to) return str;
|
||||
if (str.empty()) return str;
|
||||
|
||||
std::string copy = str;
|
||||
for (auto &c : copy)
|
||||
if (c == from)
|
||||
c = to;
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
std::string convertDelimiter (const std::string &path)
|
||||
{
|
||||
return path;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T popFileName_(const T &path)
|
||||
{
|
||||
auto lastDelimiter = path.rfind('/');
|
||||
return path.substr(0, lastDelimiter+1);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T popLast_(const T &path)
|
||||
{
|
||||
if (path.empty())
|
||||
return {};
|
||||
|
||||
if (path.back() != '/')
|
||||
return popFileName(path);
|
||||
|
||||
if (path.size() < 2)
|
||||
return {};
|
||||
|
||||
auto lastDelimiter = path.rfind('/', path.size()-2);
|
||||
return path.substr(0, lastDelimiter+1);
|
||||
}
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
std::string_view popFileName(const std::string_view &path)
|
||||
{
|
||||
return popFileName_(path);
|
||||
}
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
std::string_view popLast(const std::string_view &path)
|
||||
{
|
||||
return popLast_(path);
|
||||
}
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
std::string popFileName(const std::string &path)
|
||||
{
|
||||
return popFileName_(path);
|
||||
}
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
std::string popLast(const std::string &path)
|
||||
{
|
||||
return popLast_(path);
|
||||
}
|
||||
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
std::string fileName(const std::string &path)
|
||||
{
|
||||
auto lastDelimiter = path.rfind('/');
|
||||
if (lastDelimiter == std::string::npos)
|
||||
return path;
|
||||
|
||||
return path.substr(lastDelimiter+1);
|
||||
}
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
std::string_view fileName(const std::string_view &path)
|
||||
{
|
||||
auto lastDelimiter = path.rfind('/');
|
||||
if (lastDelimiter == std::string::npos)
|
||||
return path;
|
||||
|
||||
return path.substr(lastDelimiter+1);
|
||||
}
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
std::string extension(const std::string &path)
|
||||
{
|
||||
auto lastDelimiter = path.rfind('.');
|
||||
if (lastDelimiter == std::string::npos)
|
||||
return "";
|
||||
|
||||
return path.substr(lastDelimiter);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
44
tjp/core/file/Path.h
Normal file
44
tjp/core/file/Path.h
Normal file
@@ -0,0 +1,44 @@
|
||||
// TJP COPYRIGHT HEADER
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include <tjp/core/header_only/compile.h>
|
||||
|
||||
namespace tjp::core::file {
|
||||
|
||||
std::string joinDirs(const std::vector<std::string_view> &dirs);
|
||||
std::string joinPaths(const std::vector<std::string_view> &paths);
|
||||
std::string joinDirsAndFile(const std::vector<std::string_view> &dirs, const std::string_view &fileName);
|
||||
|
||||
std::string fileName(const std::string &path);
|
||||
std::string_view fileName(const std::string_view &path);
|
||||
|
||||
std::string extension(const std::string &path);
|
||||
std::string_view removeExtensions(const std::string_view &path);
|
||||
|
||||
std::string_view popFileName(const std::string_view &path);
|
||||
std::string_view popLast(const std::string_view &path);
|
||||
|
||||
std::string popFileName(const std::string &path);
|
||||
std::string popLast(const std::string &path);
|
||||
|
||||
|
||||
#ifdef WIN32
|
||||
const char LocalDirectoryDelimiter = '\\';
|
||||
#else
|
||||
const char LocalDirectoryDelimiter = '/';
|
||||
#endif
|
||||
|
||||
const char ServerDirectoryDelimiter = '/';
|
||||
|
||||
std::string convertDelimiter (const std::string &path);
|
||||
std::string convertDelimiter (const std::string &path, char from, char to);
|
||||
|
||||
} // namespace
|
||||
|
||||
#ifdef TJP_CORE_HEADER_ONLY
|
||||
#include "Path.cpp"
|
||||
#endif
|
||||
31
tjp/core/file/VirtualFileSystem.cpp
Normal file
31
tjp/core/file/VirtualFileSystem.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
// TJP COPYRIGHT HEADER
|
||||
|
||||
#ifdef TJP_CORE_HEADER_ONLY
|
||||
#pragma once
|
||||
#endif
|
||||
#include <tjp/core/header_only/compile.h>
|
||||
|
||||
#include "VirtualFileSystem.hpp"
|
||||
|
||||
namespace tjp::core::file {
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
Contents readFile(const VirtualFileSystem &system, const StringView &path)
|
||||
{
|
||||
return system.get(path);
|
||||
}
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
void writeFile(VirtualFileSystem &system, const StringView &path, ContentsView contents)
|
||||
{
|
||||
return system.put(path, contents);
|
||||
}
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
bool fileExists(const VirtualFileSystem &system, const StringView &path)
|
||||
{
|
||||
return system.has(path);
|
||||
}
|
||||
|
||||
|
||||
} // namespace
|
||||
20
tjp/core/file/VirtualFileSystem.h
Normal file
20
tjp/core/file/VirtualFileSystem.h
Normal file
@@ -0,0 +1,20 @@
|
||||
// TJP COPYRIGHT HEADER
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Contents.h"
|
||||
#include <tjp/core/string/StringView.h>
|
||||
|
||||
namespace tjp::core::file {
|
||||
|
||||
struct VirtualFileSystem;
|
||||
|
||||
Contents readFile(const VirtualFileSystem &system, const StringView &path);
|
||||
void writeFile(VirtualFileSystem &system, const StringView &path, ContentsView);
|
||||
bool fileExists(const VirtualFileSystem &system, const StringView &path);
|
||||
|
||||
} // namespace
|
||||
|
||||
#ifdef TJP_CORE_HEADER_ONLY
|
||||
#include "VirtualFileSystem.cpp"
|
||||
#endif
|
||||
108
tjp/core/file/VirtualFileSystem.hpp
Normal file
108
tjp/core/file/VirtualFileSystem.hpp
Normal file
@@ -0,0 +1,108 @@
|
||||
// TJP COPYRIGHT HEADER
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "File.h"
|
||||
#include "Path.h"
|
||||
|
||||
namespace tjp::core::file {
|
||||
|
||||
struct VirtualFileSystem
|
||||
{
|
||||
virtual ~VirtualFileSystem () {};
|
||||
|
||||
virtual String pathFor(const StringView &path) const = 0;
|
||||
virtual Optional<Contents> get_if(const StringView &path) const = 0;
|
||||
virtual Contents get(const StringView &path) const = 0;
|
||||
virtual void put(const StringView &path, ContentsView) = 0;
|
||||
virtual bool has(const StringView &path) const = 0;
|
||||
} ;
|
||||
|
||||
struct VirtualFileSystemRoot : VirtualFileSystem
|
||||
{
|
||||
String base;
|
||||
|
||||
VirtualFileSystemRoot(const StringView &base_) :
|
||||
base(base_)
|
||||
{}
|
||||
|
||||
inline
|
||||
String pathFor(const StringView &path) const override
|
||||
{
|
||||
return joinPaths({ base, String(path) });
|
||||
}
|
||||
|
||||
inline
|
||||
Optional<Contents> get_if(const StringView &path) const override
|
||||
{
|
||||
return file::read(pathFor(path));
|
||||
}
|
||||
|
||||
inline
|
||||
Contents get(const StringView &path) const override
|
||||
{
|
||||
return file::require_read(pathFor(path));
|
||||
}
|
||||
|
||||
inline
|
||||
void put(const StringView &path, ContentsView contents) override
|
||||
{
|
||||
file::ensurePath(base, path);
|
||||
file::require_write(pathFor(path), contents);
|
||||
}
|
||||
|
||||
inline
|
||||
bool has(const StringView &path) const override
|
||||
{
|
||||
return file::fileExists(pathFor(path));
|
||||
}
|
||||
} ;
|
||||
|
||||
struct VirtualFileSystemRelative : VirtualFileSystem
|
||||
{
|
||||
VirtualFileSystem *to;
|
||||
String base;
|
||||
|
||||
VirtualFileSystemRelative(VirtualFileSystem *to_, const StringView &base_) :
|
||||
to(to_),
|
||||
base(base_)
|
||||
{}
|
||||
|
||||
String join(const StringView &path) const
|
||||
{
|
||||
return joinPaths({ base, String(path) });
|
||||
}
|
||||
|
||||
String pathFor(const StringView &path) const override
|
||||
{
|
||||
return to->pathFor(join(path));
|
||||
}
|
||||
|
||||
inline
|
||||
Optional<Contents> get_if(const StringView &path) const override
|
||||
{
|
||||
return to->get_if(join(path));
|
||||
}
|
||||
|
||||
inline
|
||||
Contents get(const StringView &path) const override
|
||||
{
|
||||
return to->get(join(path));
|
||||
}
|
||||
|
||||
inline
|
||||
void put(const StringView &path, ContentsView contents) override
|
||||
{
|
||||
to->put(join(path), contents);
|
||||
}
|
||||
|
||||
inline
|
||||
bool has(const StringView &path) const override
|
||||
{
|
||||
return to->has(join(path));
|
||||
}
|
||||
|
||||
} ;
|
||||
|
||||
} // namespace
|
||||
|
||||
32
tjp/core/file/_tests/Path.cpp
Normal file
32
tjp/core/file/_tests/Path.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
// TJP COPYRIGHT HEADER
|
||||
|
||||
#include <tjp/core/testing/catch.hpp>
|
||||
#include <tjp/core/containers/Vector.hpp>
|
||||
#include <tjp/core/containers/Tuple.hpp>
|
||||
#include <tjp/core/string/String.hpp>
|
||||
#include "../Path.h"
|
||||
|
||||
namespace tjp::core::file {
|
||||
namespace {
|
||||
|
||||
SCENARIO("core::file::Path")
|
||||
{
|
||||
GIVEN("fileNames")
|
||||
{
|
||||
Vector<Tuple<String, String>> tests {
|
||||
{ "hello.cpp", "hello" },
|
||||
{ "asdf.123.345.456", "asdf" },
|
||||
{ "/asdf/123.asdf.sdf", "/asdf/123" },
|
||||
{ "/asdf.zxc/123.asdf.sdf", "/asdf.zxc/123" }
|
||||
};
|
||||
|
||||
for (auto &[t, e] : tests)
|
||||
{
|
||||
auto v = removeExtensions(t);
|
||||
REQUIRE(v == e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
71
tjp/core/file/apple/Directories.cpp
Normal file
71
tjp/core/file/apple/Directories.cpp
Normal file
@@ -0,0 +1,71 @@
|
||||
// TJP COPYRIGHT HEADER
|
||||
|
||||
#include "Directories.h"
|
||||
#include "../Path.h"
|
||||
|
||||
#include <tjp/core/system/System.h>
|
||||
|
||||
#ifdef SYS_MAC
|
||||
#include <mach-o/dyld.h>
|
||||
#include "CoreFoundation/CFBundle.h"
|
||||
#endif
|
||||
|
||||
#ifdef SYS_IOS
|
||||
#include <mach-o/dyld.h>
|
||||
#include "CoreFoundation/CFBundle.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
namespace tjp {
|
||||
namespace core {
|
||||
namespace file {
|
||||
namespace apple {
|
||||
|
||||
std::string getHomeDirectory ()
|
||||
{
|
||||
const char *homeDir = getenv("HOME");
|
||||
if (homeDir)
|
||||
return homeDir;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string getExecutablePath ()
|
||||
{
|
||||
char path_[PATH_MAX+1];
|
||||
|
||||
uint32_t size=PATH_MAX;
|
||||
_NSGetExecutablePath(path_, &size);
|
||||
if (size > 0)
|
||||
{
|
||||
auto path = std::string(path_);
|
||||
return path;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string getExecutableDirectory ()
|
||||
{
|
||||
auto path = getExecutablePath();
|
||||
return popFileName(path);
|
||||
}
|
||||
|
||||
std::string getExecutableBundleDirectory ()
|
||||
{
|
||||
return popLast(getExecutableDirectory());
|
||||
}
|
||||
|
||||
std::string getExecutableResourceDirectory ()
|
||||
{
|
||||
auto path = getExecutableBundleDirectory();
|
||||
return joinDirs({ path, "Resources"});
|
||||
}
|
||||
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
} // namespace
|
||||
} // namespace
|
||||
22
tjp/core/file/apple/Directories.h
Normal file
22
tjp/core/file/apple/Directories.h
Normal file
@@ -0,0 +1,22 @@
|
||||
// TJP COPYRIGHT HEADER
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
namespace tjp {
|
||||
namespace core {
|
||||
namespace file {
|
||||
namespace apple {
|
||||
|
||||
std::string getExecutablePath();
|
||||
std::string getExecutableDirectory();
|
||||
std::string getExecutableBundleDirectory();
|
||||
std::string getExecutableResourceDirectory();
|
||||
std::string getHomeDirectory();
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
} // namespace
|
||||
} // namespace
|
||||
28
tjp/core/file/copy.cpp
Normal file
28
tjp/core/file/copy.cpp
Normal file
@@ -0,0 +1,28 @@
|
||||
// TJP COPYRIGHT HEADER
|
||||
|
||||
|
||||
#ifdef TJP_CORE_HEADER_ONLY
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include <tjp/core/header_only/compile.h>
|
||||
|
||||
#include "copy.hpp"
|
||||
#include "read.hpp"
|
||||
#include "write.hpp"
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
namespace tjp::core::file {
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
bool copy(const String &from, const String &to)
|
||||
{
|
||||
if (auto contents = read(from))
|
||||
if (write(to, *contents))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
15
tjp/core/file/copy.hpp
Normal file
15
tjp/core/file/copy.hpp
Normal file
@@ -0,0 +1,15 @@
|
||||
// TJP COPYRIGHT HEADER
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <tjp/core/string/String.hpp>
|
||||
|
||||
namespace tjp::core::file {
|
||||
|
||||
bool copy(const String &path, const String &to);
|
||||
|
||||
} // namespace
|
||||
|
||||
#ifdef TJP_CORE_HEADER_ONLY
|
||||
#include "copy.cpp"
|
||||
#endif
|
||||
28
tjp/core/file/directory_exists.cpp
Normal file
28
tjp/core/file/directory_exists.cpp
Normal file
@@ -0,0 +1,28 @@
|
||||
// TJP COPYRIGHT HEADER
|
||||
|
||||
|
||||
#ifdef TJP_CORE_HEADER_ONLY
|
||||
#pragma once
|
||||
#endif
|
||||
#include <tjp/core/header_only/compile.h>
|
||||
|
||||
#include "directory_exists.hpp"
|
||||
#include <sys/stat.h>
|
||||
|
||||
namespace tjp::core::file {
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
bool directory_exists (const String &file)
|
||||
{
|
||||
struct stat status;
|
||||
if (stat (file.c_str(), &status)==0)
|
||||
{
|
||||
// if it is not a directory return true
|
||||
if (status.st_mode & S_IFDIR)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
15
tjp/core/file/directory_exists.hpp
Normal file
15
tjp/core/file/directory_exists.hpp
Normal file
@@ -0,0 +1,15 @@
|
||||
// TJP COPYRIGHT HEADER
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <tjp/core/string/String.hpp>
|
||||
|
||||
namespace tjp::core::file {
|
||||
|
||||
bool directory_exists(const String &path);
|
||||
|
||||
} // namespace
|
||||
|
||||
#ifdef TJP_CORE_HEADER_ONLY
|
||||
#include "directory_exists.cpp"
|
||||
#endif
|
||||
20
tjp/core/file/directory_of.cpp
Normal file
20
tjp/core/file/directory_of.cpp
Normal file
@@ -0,0 +1,20 @@
|
||||
// TJP COPYRIGHT HEADER
|
||||
|
||||
#ifdef TJP_HEADER_ONLY
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include <tjp/core/header_only/compile.h>
|
||||
|
||||
#include "directory_of.hpp"
|
||||
|
||||
namespace tjp::core::file {
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
StringView directory_of(const StringView &path)
|
||||
{
|
||||
auto lastDelimiter = path.rfind('/');
|
||||
return path.substr(0, lastDelimiter+1);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
15
tjp/core/file/directory_of.hpp
Normal file
15
tjp/core/file/directory_of.hpp
Normal file
@@ -0,0 +1,15 @@
|
||||
// TJP COPYRIGHT HEADER
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <tjp/core/string/StringView.hpp>
|
||||
|
||||
namespace tjp::core::file {
|
||||
|
||||
StringView directory_of(const StringView &path);
|
||||
|
||||
} // namespace
|
||||
|
||||
#ifdef TJP_CORE_HEADER_ONLY
|
||||
#include "directory_of.cpp"
|
||||
#endif
|
||||
34
tjp/core/file/file_exists.cpp
Normal file
34
tjp/core/file/file_exists.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
// TJP COPYRIGHT HEADER
|
||||
|
||||
|
||||
#ifdef TJP_CORE_HEADER_ONLY
|
||||
#pragma once
|
||||
#endif
|
||||
#include <tjp/core/header_only/compile.h>
|
||||
|
||||
#include "file_exists.hpp"
|
||||
#include <sys/stat.h>
|
||||
|
||||
namespace tjp::core::file {
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
bool fileExists (const String &file)
|
||||
{
|
||||
struct stat status;
|
||||
if (stat (file.c_str(), &status)==0)
|
||||
{
|
||||
// if it is not a directory return true
|
||||
if (!(status.st_mode & S_IFDIR))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
bool fileExists (const StringView &file)
|
||||
{
|
||||
return fileExists(String(file));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
17
tjp/core/file/file_exists.hpp
Normal file
17
tjp/core/file/file_exists.hpp
Normal file
@@ -0,0 +1,17 @@
|
||||
// TJP COPYRIGHT HEADER
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <tjp/core/string/String.hpp>
|
||||
#include <tjp/core/string/StringView.hpp>
|
||||
|
||||
namespace tjp::core::file {
|
||||
|
||||
bool fileExists(const String &path);
|
||||
bool fileExists(const StringView &path);
|
||||
|
||||
} // namespace
|
||||
|
||||
#ifdef TJP_CORE_HEADER_ONLY
|
||||
#include "file_exists.cpp"
|
||||
#endif
|
||||
135
tjp/core/file/get_files.cpp
Normal file
135
tjp/core/file/get_files.cpp
Normal file
@@ -0,0 +1,135 @@
|
||||
// TJP COPYRIGHT HEADER
|
||||
|
||||
#ifdef TJP_HEADER_ONLY
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include <tjp/core/header_only/compile.h>
|
||||
|
||||
#include "get_files.hpp"
|
||||
#include <tjp/core/system/System.h>
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <dirent.h>
|
||||
#include <sys/types.h>
|
||||
#include <queue>
|
||||
#include <filesystem>
|
||||
|
||||
namespace tjp::core::file {
|
||||
|
||||
#ifndef SYS_IOS
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
Vector<String> get_files(const String &path, const Optional<String> &extension, bool recursive)
|
||||
{
|
||||
Vector<String> files;
|
||||
try
|
||||
{
|
||||
std::queue<std::filesystem::path> dirs;
|
||||
std::filesystem::path basePath = std::filesystem::canonical(path);
|
||||
dirs.push(basePath);
|
||||
|
||||
while (!dirs.empty())
|
||||
{
|
||||
auto current = dirs.front();
|
||||
dirs.pop();
|
||||
|
||||
for (const auto &entry : std::filesystem::directory_iterator(current))
|
||||
{
|
||||
if (entry.is_directory())
|
||||
{
|
||||
if (recursive)
|
||||
dirs.push(entry.path());
|
||||
}
|
||||
else
|
||||
if (entry.is_regular_file())
|
||||
{
|
||||
if (!extension || entry.path().extension() == *extension) {
|
||||
files.push_back(std::filesystem::relative(entry.path(), basePath).string());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (std::filesystem::filesystem_error &)
|
||||
{
|
||||
}
|
||||
catch (std::exception &)
|
||||
{
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
Vector<String> get_files(const String &path_, const Optional<String> &extension, bool recursive)
|
||||
{
|
||||
auto path = String(path_);
|
||||
Vector<String> files;
|
||||
std::queue<std::string> dirs;
|
||||
dirs.push("");
|
||||
|
||||
while (!dirs.empty())
|
||||
{
|
||||
auto relativeDir = dirs.front();
|
||||
std::string absoluteDir = path + relativeDir;
|
||||
dirs.pop();
|
||||
|
||||
DIR *dir = opendir(absoluteDir.c_str());
|
||||
if (!dir) {
|
||||
continue;
|
||||
}
|
||||
|
||||
struct dirent *entry;
|
||||
while ((entry = readdir(dir)) != nullptr)
|
||||
{
|
||||
std::string name(entry->d_name);
|
||||
|
||||
// skip "." and ".."
|
||||
if (name == "." || name == "..") {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string relativePath =
|
||||
relativeDir.empty() ?
|
||||
name :
|
||||
relativeDir + "/" + name;
|
||||
|
||||
if (entry->d_type == DT_DIR)
|
||||
{
|
||||
// enqueue subdirectory
|
||||
if (recursive)
|
||||
dirs.push(relativePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (extension.has_value())
|
||||
{
|
||||
if (name.size() >= extension->size() &&
|
||||
name.compare(name.size() - extension->size(), extension->size(), *extension) == 0)
|
||||
{
|
||||
files.emplace_back(relativePath.c_str());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
files.emplace_back(relativePath.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
17
tjp/core/file/get_files.hpp
Normal file
17
tjp/core/file/get_files.hpp
Normal file
@@ -0,0 +1,17 @@
|
||||
// TJP COPYRIGHT HEADER
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <tjp/core/string/String.hpp>
|
||||
#include <tjp/core/containers/Vector.hpp>
|
||||
#include <tjp/core/containers/Optional.hpp>
|
||||
|
||||
namespace tjp::core::file {
|
||||
|
||||
Vector<String> get_files(const String &path, const Optional<String> &extension, bool recursive);
|
||||
|
||||
} // namespace
|
||||
|
||||
#ifdef TJP_CORE_HEADER_ONLY
|
||||
#include "get_files.cpp"
|
||||
#endif
|
||||
15
tjp/core/file/is_relative_path.hpp
Normal file
15
tjp/core/file/is_relative_path.hpp
Normal file
@@ -0,0 +1,15 @@
|
||||
// TJP COPYRIGHT HEADER
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <tjp/core/string/StringView.hpp>
|
||||
|
||||
namespace tjp::core::file {
|
||||
|
||||
inline
|
||||
bool is_relative_path(const StringView &p)
|
||||
{
|
||||
return p.empty() || p[0] != '/';
|
||||
}
|
||||
|
||||
} // namespace
|
||||
47
tjp/core/file/linux/Directories.cpp
Normal file
47
tjp/core/file/linux/Directories.cpp
Normal file
@@ -0,0 +1,47 @@
|
||||
// TJP COPYRIGHT HEADER
|
||||
|
||||
#include "Directories.h"
|
||||
#include "../Path.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
namespace tjp {
|
||||
namespace core {
|
||||
namespace file {
|
||||
namespace linux {
|
||||
|
||||
std::string getHomeDirectory()
|
||||
{
|
||||
const char *homeDir = getenv("HOME");
|
||||
if (homeDir)
|
||||
return homeDir;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string getExecutablePath ()
|
||||
{
|
||||
return "./";
|
||||
}
|
||||
|
||||
std::string getExecutableDirectory ()
|
||||
{
|
||||
auto path = getExecutablePath();
|
||||
return popFileName(path);
|
||||
}
|
||||
|
||||
std::string getExecutableBundleDirectory ()
|
||||
{
|
||||
return getExecutableDirectory();
|
||||
}
|
||||
|
||||
std::string getExecutableResourceDirectory ()
|
||||
{
|
||||
return getExecutableDirectory();
|
||||
}
|
||||
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
} // namespace
|
||||
} // namespace
|
||||
22
tjp/core/file/linux/Directories.h
Normal file
22
tjp/core/file/linux/Directories.h
Normal file
@@ -0,0 +1,22 @@
|
||||
// TJP COPYRIGHT HEADER
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
namespace tjp {
|
||||
namespace core {
|
||||
namespace file {
|
||||
namespace linux {
|
||||
|
||||
std::string getExecutablePath();
|
||||
std::string getExecutableDirectory();
|
||||
std::string getExecutableBundleDirectory();
|
||||
std::string getExecutableResourceDirectory();
|
||||
std::string getHomeDirectory();
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
} // namespace
|
||||
} // namespace
|
||||
32
tjp/core/file/move.cpp
Normal file
32
tjp/core/file/move.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
// TJP COPYRIGHT HEADER
|
||||
|
||||
|
||||
#ifdef TJP_CORE_HEADER_ONLY
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include <tjp/core/header_only/compile.h>
|
||||
|
||||
#include "move.hpp"
|
||||
#include <tjp/core/exception/Exception.hpp>
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
namespace tjp::core::file {
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
bool move(const String &from, const String &to)
|
||||
{
|
||||
return std::rename(from.c_str(), to.c_str()) != 0;
|
||||
}
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
void require_move(const String &from, const String &to)
|
||||
{
|
||||
if (!move(from, to))
|
||||
{
|
||||
throw Exception { "Move failed" };
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
16
tjp/core/file/move.hpp
Normal file
16
tjp/core/file/move.hpp
Normal file
@@ -0,0 +1,16 @@
|
||||
// TJP COPYRIGHT HEADER
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <tjp/core/string/String.hpp>
|
||||
|
||||
namespace tjp::core::file {
|
||||
|
||||
void require_move(const String &path, const String &to);
|
||||
bool move(const String &path, const String &to);
|
||||
|
||||
} // namespace
|
||||
|
||||
#ifdef TJP_CORE_HEADER_ONLY
|
||||
#include "move.cpp"
|
||||
#endif
|
||||
21
tjp/core/file/pop_first.hpp
Normal file
21
tjp/core/file/pop_first.hpp
Normal file
@@ -0,0 +1,21 @@
|
||||
// TJP COPYRIGHT HEADER
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <tjp/core/string/String.hpp>
|
||||
#include <tjp/core/containers/Optional.hpp>
|
||||
|
||||
namespace tjp::core::file {
|
||||
|
||||
template<typename T>
|
||||
Optional<T> pop_first(const T &path)
|
||||
{
|
||||
auto s = path.find('/');
|
||||
if (s != path.npos)
|
||||
return path.substr(s+1);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
62
tjp/core/file/read.cpp
Normal file
62
tjp/core/file/read.cpp
Normal file
@@ -0,0 +1,62 @@
|
||||
// TJP COPYRIGHT HEADER
|
||||
|
||||
|
||||
#ifdef TJP_CORE_HEADER_ONLY
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include <tjp/core/header_only/compile.h>
|
||||
|
||||
#include "read.hpp"
|
||||
|
||||
#include <tjp/core/exception/Exception.hpp>
|
||||
#include <cstdio>
|
||||
#include <fstream>
|
||||
|
||||
|
||||
namespace tjp::core::file {
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
Optional<Contents> read(const String& path)
|
||||
{
|
||||
std::ifstream file(path, std::ios::ate | std::ios::binary);
|
||||
bool exists = (bool)file;
|
||||
|
||||
if (!exists || !file.is_open())
|
||||
return {};
|
||||
|
||||
size_t fileSize = (size_t)file.tellg();
|
||||
Contents buffer(fileSize);
|
||||
|
||||
file.seekg(0);
|
||||
file.read(buffer.data(), buffer.size());
|
||||
file.close();
|
||||
|
||||
return buffer;
|
||||
};
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
Contents require_read(const String& path)
|
||||
{
|
||||
if (auto result = read(path))
|
||||
return *result;
|
||||
|
||||
throw Exception { "failed to read file " + String(path) };
|
||||
}
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
Optional<Contents> read(const StringView& path)
|
||||
{
|
||||
return read(String(path));
|
||||
}
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
Contents require_read(const StringView& path)
|
||||
{
|
||||
return require_read(String(path));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
} // namespace
|
||||
21
tjp/core/file/read.hpp
Normal file
21
tjp/core/file/read.hpp
Normal file
@@ -0,0 +1,21 @@
|
||||
// TJP COPYRIGHT HEADER
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Contents.h"
|
||||
#include <tjp/core/string/String.hpp>
|
||||
#include <tjp/core/containers/Optional.hpp>
|
||||
|
||||
namespace tjp::core::file {
|
||||
|
||||
Optional<Contents> read(const String &path);
|
||||
Contents require_read(const String &path);
|
||||
|
||||
Optional<Contents> read(const StringView &path);
|
||||
Contents require_read(const StringView &path);
|
||||
|
||||
} // namespace
|
||||
|
||||
#ifdef TJP_CORE_HEADER_ONLY
|
||||
#include "read.cpp"
|
||||
#endif
|
||||
28
tjp/core/file/split_first_directory.cpp
Normal file
28
tjp/core/file/split_first_directory.cpp
Normal file
@@ -0,0 +1,28 @@
|
||||
// TJP COPYRIGHT HEADER
|
||||
|
||||
#ifdef TJP_HEADER_ONLY
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include <tjp/core/header_only/compile.h>
|
||||
|
||||
#include "split_first_directory.hpp"
|
||||
|
||||
namespace tjp::core::file {
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
Optional<Tuple<StringView, StringView>> split_first_directory(const StringView &path)
|
||||
{
|
||||
auto slash = path.find('/');
|
||||
if (slash != path.npos)
|
||||
{
|
||||
return Tuple<StringView,StringView> {
|
||||
path.substr(0, slash),
|
||||
path.substr(slash+1)
|
||||
} ;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
17
tjp/core/file/split_first_directory.hpp
Normal file
17
tjp/core/file/split_first_directory.hpp
Normal file
@@ -0,0 +1,17 @@
|
||||
// TJP COPYRIGHT HEADER
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <tjp/core/string/StringView.hpp>
|
||||
#include <tjp/core/containers/Tuple.hpp>
|
||||
#include <tjp/core/containers/Optional.hpp>
|
||||
|
||||
namespace tjp::core::file {
|
||||
|
||||
Optional<Tuple<StringView, StringView>> split_first_directory(const StringView &path);
|
||||
|
||||
} // namespace
|
||||
|
||||
#ifdef TJP_CORE_HEADER_ONLY
|
||||
#include "split_first_directory.cpp"
|
||||
#endif
|
||||
74
tjp/core/file/times.cpp
Normal file
74
tjp/core/file/times.cpp
Normal file
@@ -0,0 +1,74 @@
|
||||
// TJP COPYRIGHT HEADER
|
||||
|
||||
|
||||
#ifdef TJP_CORE_HEADER_ONLY
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include <tjp/core/header_only/compile.h>
|
||||
|
||||
#include "times.hpp"
|
||||
#include <tjp/core/exception/Exception.hpp>
|
||||
|
||||
#include <cstdio>
|
||||
#include <sys/stat.h>
|
||||
#include <utime.h>
|
||||
|
||||
|
||||
namespace tjp::core::file {
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
time_t getFileLastModifiedTime(const String & fullFileName)
|
||||
{
|
||||
struct stat status;
|
||||
|
||||
int result = stat(fullFileName.c_str(), &status);
|
||||
|
||||
if (result != 0)
|
||||
return 0;
|
||||
|
||||
return status.st_mtime;
|
||||
}
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
bool setFileLastModifiedTime(const String & fullFileName, time_t time)
|
||||
{
|
||||
struct utimbuf timbuf;
|
||||
timbuf.actime = getFileAccessTime(fullFileName);
|
||||
timbuf.modtime = time;
|
||||
|
||||
int result = utime(fullFileName.c_str(), &timbuf);
|
||||
|
||||
if (result != 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
time_t getFileCreationTime (const String & fullFileName)
|
||||
{
|
||||
struct stat status;
|
||||
|
||||
int result = stat(fullFileName.c_str(), &status);
|
||||
|
||||
if (result != 0)
|
||||
return 0;
|
||||
|
||||
return status.st_ctime;
|
||||
}
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
time_t getFileAccessTime (const String & fullFileName)
|
||||
{
|
||||
struct stat status;
|
||||
|
||||
int result = stat(fullFileName.c_str(), &status);
|
||||
|
||||
if (result != 0)
|
||||
return 0;
|
||||
|
||||
return status.st_atime;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
22
tjp/core/file/times.hpp
Normal file
22
tjp/core/file/times.hpp
Normal file
@@ -0,0 +1,22 @@
|
||||
// TJP COPYRIGHT HEADER
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include <tjp/core/string/String.hpp>
|
||||
#include <tjp/core/containers/Optional.hpp>
|
||||
|
||||
namespace tjp::core::file {
|
||||
|
||||
time_t getFileLastModifiedTime(const String & fullFileName);
|
||||
bool setFileLastModifiedTime(const String & fullFileName, time_t);
|
||||
|
||||
time_t getFileCreationTime (const String & fullFileName);
|
||||
time_t getFileAccessTime (const String & fullFileName);
|
||||
|
||||
} // namespace
|
||||
|
||||
#ifdef TJP_CORE_HEADER_ONLY
|
||||
#include "times.cpp"
|
||||
#endif
|
||||
24
tjp/core/file/unlink.cpp
Normal file
24
tjp/core/file/unlink.cpp
Normal file
@@ -0,0 +1,24 @@
|
||||
// TJP COPYRIGHT HEADER
|
||||
|
||||
|
||||
#ifdef TJP_CORE_HEADER_ONLY
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include <tjp/core/header_only/compile.h>
|
||||
|
||||
|
||||
#include "unlink.hpp"
|
||||
#include <tjp/core/exception/Exception.hpp>
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
namespace tjp::core::file {
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
bool unlink (const String &file)
|
||||
{
|
||||
return std::remove(file.data()) == 0;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
15
tjp/core/file/unlink.hpp
Normal file
15
tjp/core/file/unlink.hpp
Normal file
@@ -0,0 +1,15 @@
|
||||
// TJP COPYRIGHT HEADER
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <tjp/core/string/String.hpp>
|
||||
|
||||
namespace tjp::core::file {
|
||||
|
||||
bool unlink(const String &path);
|
||||
|
||||
} // namespace
|
||||
|
||||
#ifdef TJP_CORE_HEADER_ONLY
|
||||
#include "unlink.cpp"
|
||||
#endif
|
||||
42
tjp/core/file/validate_safe_relative_path.cpp
Normal file
42
tjp/core/file/validate_safe_relative_path.cpp
Normal file
@@ -0,0 +1,42 @@
|
||||
// TJP COPYRIGHT HEADER
|
||||
|
||||
#ifdef TJP_CORE_HEADER_ONLY
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include <tjp/core/header_only/compile.h>
|
||||
|
||||
#include "validate_safe_relative_path.hpp"
|
||||
|
||||
namespace tjp::core::file {
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
bool is_safe_relative_path(const StringView &s)
|
||||
{
|
||||
if (s.find("..") == -1)
|
||||
return false;
|
||||
|
||||
if (s.empty())
|
||||
return false;
|
||||
|
||||
if (s[0] == '/')
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
void validate_safe_relative_path(const StringView &s)
|
||||
{
|
||||
if (s.find("..") == -1)
|
||||
throw Exception { "Path has dots" };
|
||||
|
||||
if (s.empty())
|
||||
throw Exception { "Path is empty" };
|
||||
|
||||
if (s[0] == '/')
|
||||
throw Exception { "Path begins with slash" };
|
||||
}
|
||||
|
||||
|
||||
} // namespace
|
||||
17
tjp/core/file/validate_safe_relative_path.hpp
Normal file
17
tjp/core/file/validate_safe_relative_path.hpp
Normal file
@@ -0,0 +1,17 @@
|
||||
// TJP COPYRIGHT HEADER
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <tjp/core/exception/Exception.hpp>
|
||||
#include <tjp/core/string/StringView.hpp>
|
||||
|
||||
namespace tjp::core::file {
|
||||
|
||||
bool is_safe_relative_path(const StringView &);
|
||||
void validate_safe_relative_path(const StringView &);
|
||||
|
||||
} // namespace
|
||||
|
||||
#ifdef TJP_CORE_HEADER_ONLY
|
||||
#include "validate_safe_relative_path.cpp"
|
||||
#endif
|
||||
66
tjp/core/file/write.cpp
Normal file
66
tjp/core/file/write.cpp
Normal file
@@ -0,0 +1,66 @@
|
||||
// TJP COPYRIGHT HEADER
|
||||
|
||||
|
||||
#ifdef TJP_CORE_HEADER_ONLY
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include <tjp/core/header_only/compile.h>
|
||||
|
||||
#include "write.hpp"
|
||||
#include <tjp/core/exception/Exception.hpp>
|
||||
#include "../random/file_name.hpp"
|
||||
#include "times.hpp"
|
||||
|
||||
#include <cstdio>
|
||||
#include <fstream>
|
||||
|
||||
namespace tjp::core::file {
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
bool write(const String &path_, const ContentsView &contents, const WriteOptions &options)
|
||||
{
|
||||
auto path = String(path_);
|
||||
auto random_ = random::file_name();
|
||||
|
||||
auto extension = "." + random_;
|
||||
auto temporary = path + extension;
|
||||
|
||||
std::ofstream file(temporary, std::ios::binary);
|
||||
if (!file)
|
||||
return false;
|
||||
|
||||
file.write(contents.data(), contents.size());
|
||||
file.close();
|
||||
|
||||
remove(path.c_str());
|
||||
if (rename(temporary.c_str(), path.c_str()))
|
||||
return false;
|
||||
|
||||
if (options.lastModifiedTime)
|
||||
setFileLastModifiedTime(path, options.lastModifiedTime);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
void require_write(const String &path, const ContentsView &contents, const WriteOptions &options)
|
||||
{
|
||||
if (!write(path, contents, options))
|
||||
throw Exception { "failed to write file " + String(path) };
|
||||
}
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
bool write(const StringView &path, const ContentsView &contents, const WriteOptions &options)
|
||||
{
|
||||
return write(String(path), contents, options);
|
||||
}
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
void require_write(const StringView &path, const ContentsView &contents, const WriteOptions &options)
|
||||
{
|
||||
return require_write(String(path), contents, options);
|
||||
}
|
||||
|
||||
|
||||
} // namespace
|
||||
25
tjp/core/file/write.hpp
Normal file
25
tjp/core/file/write.hpp
Normal file
@@ -0,0 +1,25 @@
|
||||
// TJP COPYRIGHT HEADER
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Contents.h"
|
||||
#include <tjp/core/string/String.hpp>
|
||||
#include <tjp/core/containers/Optional.hpp>
|
||||
|
||||
namespace tjp::core::file {
|
||||
|
||||
struct WriteOptions {
|
||||
time_t lastModifiedTime = 0;
|
||||
} ;
|
||||
|
||||
bool write(const String &path, const ContentsView &, const WriteOptions &options = {});
|
||||
void require_write(const String &path, const ContentsView &, const WriteOptions &options = {});
|
||||
|
||||
bool write(const StringView &path, const ContentsView &, const WriteOptions &options = {});
|
||||
void require_write(const StringView &path, const ContentsView &, const WriteOptions &options = {});
|
||||
|
||||
} // namespace
|
||||
|
||||
#ifdef TJP_CORE_HEADER_ONLY
|
||||
#include "write.cpp"
|
||||
#endif
|
||||
53
tjp/core/file/writeSegmentsToFile.cpp
Normal file
53
tjp/core/file/writeSegmentsToFile.cpp
Normal file
@@ -0,0 +1,53 @@
|
||||
// TJP COPYRIGHT HEADER
|
||||
|
||||
#include "writeSegmentsToFile.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <tjp/core/exception/Exception.hpp>
|
||||
#include <tjp/core/system/System.h>
|
||||
|
||||
#include <tjp/core/string/to_string.hpp>
|
||||
#include "../random/Random.h"
|
||||
|
||||
#include <utime.h>
|
||||
|
||||
namespace tjp {
|
||||
namespace core {
|
||||
namespace file {
|
||||
|
||||
bool writeSegmentsToFile(const StringView &path_, const StackArray<containers::MemorySegment<u8>> &segments, const WriteOptions &options)
|
||||
{
|
||||
auto path = String(path_);
|
||||
auto extension = "." + core::to_string(core::StandardRandom.next<u32>());
|
||||
auto temporary = path + extension;
|
||||
|
||||
std::ofstream file(temporary, std::ios::binary);
|
||||
if (!file)
|
||||
return false;
|
||||
|
||||
for (auto &contents: segments)
|
||||
file.write((char *)contents.data(), contents.size());
|
||||
|
||||
file.close();
|
||||
|
||||
remove(path.c_str());
|
||||
if (rename(temporary.c_str(), path.c_str()))
|
||||
return false;
|
||||
|
||||
if (options.lastModifiedTime)
|
||||
{
|
||||
setFileLastModifiedTime(path, options.lastModifiedTime);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
} // namespace
|
||||
18
tjp/core/file/writeSegmentsToFile.h
Normal file
18
tjp/core/file/writeSegmentsToFile.h
Normal file
@@ -0,0 +1,18 @@
|
||||
// TJP COPYRIGHT HEADER
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "File.h"
|
||||
|
||||
#include <tjp/core/containers/MemorySegment.hpp>
|
||||
#include <tjp/core/containers/StackArray.hpp>
|
||||
|
||||
namespace tjp {
|
||||
namespace core {
|
||||
namespace file {
|
||||
|
||||
bool writeSegmentsToFile(const StringView &path, const StackArray<containers::MemorySegment<u8>> &contents, const WriteOptions &options = {});
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
} // namespace
|
||||
Reference in New Issue
Block a user