#include "foundation/fs_utf8.h" #include #include #include #if defined(_WIN32) #define WIN32_LEAN_AND_MEAN #define NOMINMAX #include #include #include #include #else #include #include #endif namespace joc::fs_utf8 { namespace fs = std::filesystem; fs::path to_path(const std::string& utf8) { return fs::path(std::u8string(reinterpret_cast(utf8.data()), utf8.size())); } std::string from_path(const fs::path& path) { const std::u8string text = path.u8string(); return std::string(reinterpret_cast(text.data()), text.size()); } std::FILE* fopen(const std::string& utf8_path, const char* mode) { #if defined(_WIN32) const std::wstring wide_mode(mode, mode + std::strlen(mode)); return ::_wfopen(to_path(utf8_path).c_str(), wide_mode.c_str()); #else return std::fopen(utf8_path.c_str(), mode); #endif } std::FILE* fopen_spool(const std::string& utf8_path) { #if defined(_WIN32) // Delete-on-close handed to the CRT: if the process is killed the file goes with // it, which is what stops an aborted run from leaving hundreds of gigabytes. HANDLE handle = ::CreateFileW( to_path(utf8_path).c_str(), GENERIC_READ | GENERIC_WRITE | DELETE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, nullptr); if (handle == INVALID_HANDLE_VALUE) { return nullptr; } const int descriptor = ::_open_osfhandle(reinterpret_cast(handle), 0); if (descriptor == -1) { ::CloseHandle(handle); return nullptr; } return ::_fdopen(descriptor, "wb+"); #else return std::fopen(utf8_path.c_str(), "wb+"); #endif } int remove(const std::string& utf8_path) { #if defined(_WIN32) return ::_wremove(to_path(utf8_path).c_str()); #else return std::remove(utf8_path.c_str()); #endif } bool exists(const std::string& utf8_path) { std::error_code error; return fs::exists(to_path(utf8_path), error); } bool is_directory(const std::string& utf8_path) { std::error_code error; return fs::is_directory(to_path(utf8_path), error); } std::uintmax_t file_size(const std::string& utf8_path, std::error_code& error) { return fs::file_size(to_path(utf8_path), error); } std::string temp_directory() { std::error_code error; const fs::path directory = fs::temp_directory_path(error); return error ? std::string(".") : from_path(directory); } std::string executable_path() { #if defined(_WIN32) std::vector buffer(MAX_PATH); while (true) { const DWORD written = ::GetModuleFileNameW(nullptr, buffer.data(), static_cast(buffer.size())); if (written == 0) { return std::string(); } if (written < buffer.size()) { return from_path(fs::path(std::wstring(buffer.data(), written))); } buffer.resize(buffer.size() * 2u); } #elif defined(__linux__) std::vector buffer(4096u, '\0'); const ssize_t written = ::readlink("/proc/self/exe", buffer.data(), buffer.size() - 1u); return written > 0 ? std::string(buffer.data(), static_cast(written)) : std::string(); #else return std::string(); #endif } std::ifstream open_input(const std::string& utf8_path) { return std::ifstream(to_path(utf8_path), std::ios::binary); } std::ofstream open_output(const std::string& utf8_path) { return std::ofstream(to_path(utf8_path), std::ios::binary); } std::vector command_line_arguments(int argc, char** argv) { #if defined(_WIN32) (void)argc; (void)argv; int count = 0; LPWSTR* wide = ::CommandLineToArgvW(::GetCommandLineW(), &count); std::vector arguments; if (wide == nullptr) { return arguments; } arguments.reserve(static_cast(count)); for (int index = 0; index < count; ++index) { const std::wstring_view text(wide[index]); const int size = ::WideCharToMultiByte(CP_UTF8, 0, text.data(), static_cast(text.size()), nullptr, 0, nullptr, nullptr); std::string utf8(static_cast(size), '\0'); if (size > 0) { ::WideCharToMultiByte(CP_UTF8, 0, text.data(), static_cast(text.size()), utf8.data(), size, nullptr, nullptr); } arguments.push_back(std::move(utf8)); } ::LocalFree(wide); return arguments; #else std::vector arguments; arguments.reserve(static_cast(argc)); for (int index = 0; index < argc; ++index) { arguments.emplace_back(argv[index]); } return arguments; #endif } void configure_console() { #if defined(_WIN32) ::SetConsoleOutputCP(CP_UTF8); #endif } } // namespace joc::fs_utf8