aboutsummaryrefslogtreecommitdiff
path: root/src/FileUtil.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/FileUtil.cpp')
-rw-r--r--src/FileUtil.cpp246
1 files changed, 209 insertions, 37 deletions
diff --git a/src/FileUtil.cpp b/src/FileUtil.cpp
index d075f2b..5cf8377 100644
--- a/src/FileUtil.cpp
+++ b/src/FileUtil.cpp
@@ -1,5 +1,4 @@
#include "../include/FileUtil.hpp"
-#include "../include/env.hpp"
#include <cstdio>
#if OS_FAMILY == OS_FAMILY_POSIX
@@ -7,27 +6,92 @@
#include <sys/types.h>
#include <pwd.h>
#include <fcntl.h>
+#else
+#include <UserEnv.h>
+// Copied from linux libc sys/stat.h:
+#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
+#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
#endif
using namespace std;
namespace sibs
{
- FileType getFileType(const char *path)
+#if OS_FAMILY == OS_FAMILY_POSIX
+#define toUtf8(input) input
+ FileString toFileString(const std::string &utf8Str)
{
- tinydir_file file;
- if(tinydir_file_open(&file, path) == 0)
- {
- return file.is_dir ? FileType::DIRECTORY : FileType::REGULAR;
- }
+ return utf8Str;
+ }
+#else
+ std::string toUtf8(const sibs::FileString &input)
+ {
+ std::string result;
+ utf8::utf16to8(input.data(), input.data() + input.size(), std::back_inserter(result));
+ return result;
+ }
+
+ std::string toUtf8(const TCHAR *input)
+ {
+ size_t inputSize = wcslen(input);
+ std::string result;
+ utf8::utf16to8(input, input + inputSize, std::back_inserter(result));
+ return result;
+ }
+
+ FileString utf8To16(const StringView &utf8Str)
+ {
+ FileString result;
+ utf8::utf8to16(utf8Str.data, utf8Str.data + utf8Str.size, std::back_inserter(result));
+ return result;
+ }
+
+ FileString utf8To16(const std::string &utf8Str)
+ {
+ FileString result;
+ utf8::utf8to16(utf8Str.data(), utf8Str.data() + utf8Str.size(), std::back_inserter(result));
+ return result;
+ }
+
+ FileString toFileString(const std::string &utf8Str)
+ {
+ return utf8To16(utf8Str);
+ }
+
+ FileString getLastErrorAsString()
+ {
+ DWORD errorMessageId = GetLastError();
+ if (errorMessageId == 0) return TINYDIR_STRING("");
+ LPWSTR messageBuffer = nullptr;
+ size_t size = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, errorMessageId, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&messageBuffer, 0, nullptr);
+ FileString message(messageBuffer, size);
+ LocalFree(messageBuffer);
+ return message;
+ }
+#endif
+
+#if OS_FAMILY == OS_FAMILY_POSIX
+ FileType getFileType(const _tinydir_char_t *path)
+ {
+ struct stat64 fileStat;
+ if (stat64(path, &fileStat) == 0)
+ return S_ISREG(fileStat.st_mode) ? FileType::REGULAR : FileType::DIRECTORY;
else
- {
return FileType::FILE_NOT_FOUND;
- }
}
+#else
+ FileType getFileType(const _tinydir_char_t *path)
+ {
+ struct _stat64i32 fileStat;
+ if (_wstat(path, &fileStat) == 0)
+ return S_ISREG(fileStat.st_mode) ? FileType::REGULAR : FileType::DIRECTORY;
+ else
+ return FileType::FILE_NOT_FOUND;
+ }
+#endif
// TODO: Handle failure (directory doesn't exist, no permission etc)
- void walkDir(const char *directory, FileWalkCallbackFunc callbackFunc)
+ void walkDir(const _tinydir_char_t *directory, FileWalkCallbackFunc callbackFunc)
{
tinydir_dir dir;
tinydir_open(&dir, directory);
@@ -36,7 +100,7 @@ namespace sibs
{
tinydir_file file;
tinydir_readfile(&dir, &file);
- if(_tinydir_strcmp(file.name, ".") != 0 && _tinydir_strcmp(file.name, "..") != 0)
+ if(_tinydir_strcmp(file.name, TINYDIR_STRING(".")) != 0 && _tinydir_strcmp(file.name, TINYDIR_STRING("..")) != 0)
callbackFunc(&file);
tinydir_next(&dir);
}
@@ -45,7 +109,7 @@ namespace sibs
}
// TODO: Handle failure (directory doesn't exist, no permission etc)
- void walkDirFiles(const char *directory, FileWalkCallbackFunc callbackFunc)
+ void walkDirFiles(const _tinydir_char_t *directory, FileWalkCallbackFunc callbackFunc)
{
tinydir_dir dir;
tinydir_open(&dir, directory);
@@ -63,7 +127,7 @@ namespace sibs
}
// TODO: Handle failure (directory doesn't exist, no permission etc)
- void walkDirFilesRecursive(const char *directory, FileWalkCallbackFunc callbackFunc)
+ void walkDirFilesRecursive(const _tinydir_char_t *directory, FileWalkCallbackFunc callbackFunc)
{
tinydir_dir dir;
tinydir_open(&dir, directory);
@@ -74,7 +138,7 @@ namespace sibs
tinydir_readfile(&dir, &file);
if(file.is_reg)
callbackFunc(&file);
- else if(_tinydir_strcmp(file.name, ".") != 0 && _tinydir_strcmp(file.name, "..") != 0)
+ else if(_tinydir_strcmp(file.name, TINYDIR_STRING(".")) != 0 && _tinydir_strcmp(file.name, TINYDIR_STRING("..")) != 0)
walkDirFilesRecursive(file.path, callbackFunc);
tinydir_next(&dir);
}
@@ -82,14 +146,18 @@ namespace sibs
tinydir_close(&dir);
}
- Result<StringView> getFileContent(const char *filepath)
+ Result<StringView> getFileContent(const _tinydir_char_t *filepath)
{
+#if OS_FAMILY == OS_FAMILY_POSIX
FILE *file = fopen(filepath, "rb");
+#else
+ FILE *file = _wfopen(filepath, TINYDIR_STRING("rb"));
+#endif
if(!file)
{
int error = errno;
string errMsg = "Failed to open file: ";
- errMsg += filepath;
+ errMsg += toUtf8(filepath);
errMsg += "; reason: ";
errMsg += strerror(error);
return Result<StringView>::Err(errMsg);
@@ -104,7 +172,7 @@ namespace sibs
if(!result)
{
std::string errMsg = "Failed to load file content from file: ";
- errMsg += filepath;
+ errMsg += toUtf8(filepath);
throw std::runtime_error(errMsg);
}
result[fileSize] = '\0';
@@ -113,14 +181,18 @@ namespace sibs
return Result<StringView>::Ok(StringView(result, fileSize));
}
- Result<bool> fileOverwrite(const char *filepath, StringView data)
+ Result<bool> fileOverwrite(const _tinydir_char_t *filepath, StringView data)
{
+#if OS_FAMILY == OS_FAMILY_POSIX
FILE *file = fopen(filepath, "wb");
+#else
+ FILE *file = _wfopen(filepath, TINYDIR_STRING("wb"));
+#endif
if(!file)
{
int error = errno;
string errMsg = "Failed to overwrite file: ";
- errMsg += filepath;
+ errMsg += toUtf8(filepath);
errMsg += "; reason: ";
errMsg += strerror(error);
return Result<bool>::Err(errMsg);
@@ -130,8 +202,8 @@ namespace sibs
fclose(file);
return Result<bool>::Ok(true);
}
-
- const char* getHomeDir()
+#if OS_FAMILY == OS_FAMILY_POSIX
+ Result<FileString> getHomeDir()
{
const char *homeDir = getenv("HOME");
if(!homeDir)
@@ -139,26 +211,25 @@ namespace sibs
passwd *pw = getpwuid(getuid());
homeDir = pw->pw_dir;
}
- return homeDir;
+ return Result<FileString>::Ok(homeDir);
}
- Result<string> getCwd()
+ Result<FileString> getCwd()
{
- string cwd;
- cwd.reserve(PATH_MAX);
- if(getcwd(&cwd[0], PATH_MAX) != 0)
+ FileString cwd;
+ cwd.resize(_TINYDIR_PATH_MAX);
+ if(getcwd(&cwd[0], _TINYDIR_PATH_MAX) != 0)
{
if(cwd.empty()) cwd = ".";
- return Result<string>::Ok(cwd);
+ cwd.resize(_tinydir_strlen(cwd.c_str()));
+ return Result<FileString>::Ok(cwd);
}
-
- return Result<string>::Err(strerror(errno));
+ return Result<FileString>::Err(strerror(errno));
}
-#if OS_FAMILY == OS_FAMILY_POSIX
- Result<bool> createDirectoryRecursive(const char *path)
+ Result<bool> createDirectoryRecursive(const _tinydir_char_t *path)
{
- char pathBuffer[PATH_MAX];
+ char pathBuffer[_TINYDIR_PATH_MAX];
size_t pathLength = strlen(path);
if(pathLength > sizeof(pathBuffer) - 1)
{
@@ -207,25 +278,126 @@ namespace sibs
return Result<bool>::Ok(true);
}
- Result<string> getRealPath(const char *path)
+ Result<FileString> getRealPath(const _tinydir_char_t *path)
{
// TODO: Verify NULL can be passed as 'resolved' argument with different compilers and operating systems (clang, freebsd etc)
char *resolved = realpath(path, nullptr);
if(!resolved)
{
int error = errno;
- string errMsg = "Failed to get real path for \"";
+ FileString errMsg = "Failed to get real path for \"";
errMsg += path;
errMsg += "\": ";
errMsg += strerror(error);
- return Result<string>::Err(errMsg, error);
+ return Result<FileString>::Err(errMsg, error);
}
string result = resolved;
free(resolved);
- return Result<string>::Ok(result);
+ return Result<FileString>::Ok(result);
}
#else
-#error "TODO: Implement createDirectoryRecursive and getRealPath on windows"
+
+#pragma comment(lib, "Userenv.lib")
+
+ Result<FileString> getHomeDir()
+ {
+ BOOL ret;
+ HANDLE hToken;
+ FileString homeDir;
+ DWORD homeDirLen = _TINYDIR_PATH_MAX;
+ homeDir.resize(homeDirLen);
+
+ if (!OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &hToken))
+ return Result<FileString>::Err("Failed to open process token");
+
+ if (!GetUserProfileDirectory(hToken, &homeDir[0], &homeDirLen))
+ return Result<FileString>::Err("Failed to get home directory");
+
+ CloseHandle(hToken);
+ homeDir.resize(_tinydir_strlen(homeDir.c_str()));
+ return Result<FileString>::Ok(homeDir);
+ }
+
+ Result<FileString> getCwd()
+ {
+ FileString cwd;
+ cwd.resize(_TINYDIR_PATH_MAX);
+ if (GetCurrentDirectory(_TINYDIR_PATH_MAX, &cwd[0]) == 0)
+ {
+ FileString lastErrStr = getLastErrorAsString();
+ return Result<FileString>::Err(toUtf8(lastErrStr));
+ }
+ cwd.resize(_tinydir_strlen(cwd.c_str()));
+ return Result<FileString>::Ok(cwd);
+ }
+
+ Result<bool> createDirectoryRecursive(const _tinydir_char_t *path)
+ {
+ _tinydir_char_t pathBuffer[_TINYDIR_PATH_MAX];
+ size_t pathLength = _tinydir_strlen(path);
+ if (pathLength > sizeof(pathBuffer) - 1)
+ {
+ string errMsg = "Directory path too long: ";
+ errMsg += toUtf8(FileString(path, pathLength));
+ return Result<bool>::Err(errMsg, ENAMETOOLONG);
+ }
+ _tinydir_strcpy(pathBuffer, path);
+
+ _tinydir_char_t *p = pathBuffer;
+ for (size_t i = 0; i < pathLength; ++i)
+ {
+ if (i > 0 && *p == '/')
+ {
+ *p = '\0';
+ if (_wmkdir(pathBuffer) != 0)
+ {
+ int error = errno;
+ if (error != EEXIST)
+ {
+ string errMsg = "Failed to create directory: ";
+ errMsg += toUtf8(pathBuffer);
+ errMsg += "; reason: ";
+ errMsg += strerror(error);
+ return Result<bool>::Err(errMsg, error);
+ }
+ }
+ *p = '/';
+ }
+ ++p;
+ }
+
+ if (_wmkdir(pathBuffer) != 0)
+ {
+ int error = errno;
+ if (error != EEXIST)
+ {
+ string errMsg = "Failed to create directory: ";
+ errMsg += toUtf8(pathBuffer);
+ errMsg += "; reason: ";
+ errMsg += strerror(error);
+ return Result<bool>::Err(errMsg, error);
+ }
+ }
+
+ return Result<bool>::Ok(true);
+ }
+
+ Result<FileString> getRealPath(const _tinydir_char_t *path)
+ {
+ FileString fullPath;
+ fullPath.resize(_TINYDIR_PATH_MAX);
+ if (GetFullPathName(path, _TINYDIR_PATH_MAX, &fullPath[0], nullptr) == 0)
+ {
+ int error = GetLastError();
+ string errMsg = "Failed to get real path for \"";
+ errMsg += toUtf8(path);
+ errMsg += "\": ";
+ errMsg += toUtf8(getLastErrorAsString());
+ return Result<FileString>::Err(errMsg, error);
+ }
+ fullPath.resize(_tinydir_strlen(fullPath.c_str()));
+ return Result<FileString>::Ok(fullPath);
+ }
#endif
} \ No newline at end of file