From 98ad7dd049a366e21d60a34548736a3c8ef72877 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Sat, 30 Dec 2017 04:32:49 +0100 Subject: Add support for windows (ugly fast solution) --- src/FileUtil.cpp | 246 ++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 209 insertions(+), 37 deletions(-) (limited to 'src/FileUtil.cpp') 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 #if OS_FAMILY == OS_FAMILY_POSIX @@ -7,27 +6,92 @@ #include #include #include +#else +#include +// 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 getFileContent(const char *filepath) + Result 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::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::Ok(StringView(result, fileSize)); } - Result fileOverwrite(const char *filepath, StringView data) + Result 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::Err(errMsg); @@ -130,8 +202,8 @@ namespace sibs fclose(file); return Result::Ok(true); } - - const char* getHomeDir() +#if OS_FAMILY == OS_FAMILY_POSIX + Result 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::Ok(homeDir); } - Result getCwd() + Result 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::Ok(cwd); + cwd.resize(_tinydir_strlen(cwd.c_str())); + return Result::Ok(cwd); } - - return Result::Err(strerror(errno)); + return Result::Err(strerror(errno)); } -#if OS_FAMILY == OS_FAMILY_POSIX - Result createDirectoryRecursive(const char *path) + Result 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::Ok(true); } - Result getRealPath(const char *path) + Result 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::Err(errMsg, error); + return Result::Err(errMsg, error); } string result = resolved; free(resolved); - return Result::Ok(result); + return Result::Ok(result); } #else -#error "TODO: Implement createDirectoryRecursive and getRealPath on windows" + +#pragma comment(lib, "Userenv.lib") + + Result getHomeDir() + { + BOOL ret; + HANDLE hToken; + FileString homeDir; + DWORD homeDirLen = _TINYDIR_PATH_MAX; + homeDir.resize(homeDirLen); + + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &hToken)) + return Result::Err("Failed to open process token"); + + if (!GetUserProfileDirectory(hToken, &homeDir[0], &homeDirLen)) + return Result::Err("Failed to get home directory"); + + CloseHandle(hToken); + homeDir.resize(_tinydir_strlen(homeDir.c_str())); + return Result::Ok(homeDir); + } + + Result getCwd() + { + FileString cwd; + cwd.resize(_TINYDIR_PATH_MAX); + if (GetCurrentDirectory(_TINYDIR_PATH_MAX, &cwd[0]) == 0) + { + FileString lastErrStr = getLastErrorAsString(); + return Result::Err(toUtf8(lastErrStr)); + } + cwd.resize(_tinydir_strlen(cwd.c_str())); + return Result::Ok(cwd); + } + + Result 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::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::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::Err(errMsg, error); + } + } + + return Result::Ok(true); + } + + Result 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::Err(errMsg, error); + } + fullPath.resize(_tinydir_strlen(fullPath.c_str())); + return Result::Ok(fullPath); + } #endif } \ No newline at end of file -- cgit v1.2.3