diff --git a/src/file.cpp b/src/file.cpp index 6a5368d..d3b71eb 100644 --- a/src/file.cpp +++ b/src/file.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #ifdef __GLIBCXX__ @@ -15,8 +16,6 @@ #endif #ifdef _WIN32 -#define NOMINMAX -#define UNICODE #include #include #endif @@ -37,129 +36,110 @@ file::native_handle_type get_native_handle(std::FILE* file) noexcept { #endif } -#ifdef _WIN32 -/// Makes a mode string for opening a temporary file using `_wfsopen` +/// Makes a mode string for opening a temporary file /// @param[in] mode The file opening mode /// @returns A suitable mode string -const wchar_t* make_mdstring(std::ios::openmode mode) noexcept { - switch (mode & ~std::ios::in & ~std::ios::out & ~std::ios::ate) { - case std::ios::openmode(): - case std::ios::trunc: - return L"w+xTD"; +const char* make_mdstring(std::ios::openmode mode) noexcept { + // Special case: the C++ standard forbids `app` and `trunc` at the same time + if ((mode & std::ios::app) != 0 && (mode & std::ios::trunc) != 0) { + return nullptr; + } + + // - `std::ios::in` and `std::ios::out` are always applied + // - `std::ios::trunc` has no effect on the empty file + // - `std::ios::noreplace` has no effect for temporary files + // - any other platform dependent flag is not supported + unsigned filtered = mode & (std::ios::app | std::ios::binary); + + switch (filtered) { + case 0: +#ifdef _WIN32 + return "w+TD"; +#else + return "w+"; +#endif case std::ios::app: - return L"a+TD"; +#ifdef _WIN32 + return "a+TD"; +#else + return "a+"; +#endif case std::ios::binary: - case std::ios::trunc | std::ios::binary: - return L"w+bxTD"; +#ifdef _WIN32 + return "w+bTD"; +#else + return "w+b"; +#endif case std::ios::app | std::ios::binary: - return L"a+bTD"; +#ifdef _WIN32 + return "a+bTD"; +#else + return "a+b"; +#endif default: return nullptr; } } -/// Creates and opens a temporary file in the current user's temporary directory -/// @param[in] mode The file opening mode -/// @param[out] ec Parameter for error reporting -/// @returns A handle to the created temporary file -/// @throws std::invalid_argument if the given openmode is invalid -std::FILE* create_file(std::ios::openmode mode, std::error_code& ec) { - const wchar_t* mdstr = make_mdstring(mode); - if (mdstr == nullptr) { - throw std::invalid_argument( - "Cannot create a temporary file: invalid openmode"); - } - - std::FILE* file = std::tmpfile(); - if (file == nullptr) { - ec.assign(errno, std::generic_category()); - return nullptr; - } +/// Reopens the given temporary file with the given open mode +/// @param[in] mdstring The temporary file opening mode +/// @param[out] file The file to reopen +/// @param[out] ec Parameter for error reporting +void reopen_file(const char* mdstring, std::FILE* file, + std::error_code& ec) noexcept { + ec.clear(); +#ifdef _WIN32 HANDLE handle = get_native_handle(file); - std::wstring path; + std::string path; path.resize(MAX_PATH); - DWORD ret = GetFinalPathNameByHandle(handle, path.data(), MAX_PATH, 0); + DWORD ret = GetFinalPathNameByHandleA(handle, path.data(), MAX_PATH, 0); if (ret == 0) { ec.assign(GetLastError(), std::system_category()); - return nullptr; + return; } path.resize(ret); - file = _wfreopen(path.c_str(), make_mdstring(mode), file); + file = freopen(path.c_str(), mdstring, file); if (file == nullptr) { ec.assign(errno, std::generic_category()); - return nullptr; } - - ec.clear(); - return file; -} #else -/// Makes a mode string for opening a file using `fopen` -/// @param[in] mode The file opening mode -/// @returns A suitable mode string -const char* make_mdstring(std::ios::openmode mode) noexcept { - unsigned filtered = mode & ~std::ios::in & ~std::ios::out & ~std::ios::ate; - switch (filtered) { - case 0: - return "r+"; - case std::ios::trunc: - return "w+"; - case std::ios::app: - return "a+"; - case std::ios::binary: - return "r+b"; - case std::ios::trunc | std::ios::binary: - return "w+b"; - case std::ios::app | std::ios::binary: - return "a+b"; - default: - return nullptr; + file = std::freopen(nullptr, mdstring, file); + if (file == nullptr) { + ec.assign(errno, std::generic_category()); } -} #endif +} /// Creates and opens a temporary file in the current user's temporary directory /// @param[in] mode The file opening mode /// @returns A handle to the created temporary file /// @throws fs::filesystem_error if cannot create a temporary file /// @throws std::invalid_argument if the given openmode is invalid -std::FILE* create_file(std::ios::openmode mode); - -#ifdef _WIN32 std::FILE* create_file(std::ios::openmode mode) { - std::error_code ec; - std::FILE* file = create_file(mode, ec); - if (file == nullptr) { - throw fs::filesystem_error("Cannot create a temporary file", ec); - } - - return file; -} -#else -std::FILE* create_file(std::ios::openmode mode) { - const char* mdstr = make_mdstring(mode); - if (mdstr == nullptr) { + const char* mdstring = make_mdstring(mode); + if (mdstring == nullptr) { throw std::invalid_argument( "Cannot create a temporary file: invalid openmode"); } + std::error_code ec; std::FILE* file = std::tmpfile(); - if (file != nullptr) { - file = freopen(nullptr, mdstr, file); + if (file == nullptr) { + ec.assign(errno, std::generic_category()); + } else { + reopen_file(mdstring, file, ec); } - if (file == nullptr) { - std::error_code ec = std::error_code(errno, std::system_category()); + if (ec) { throw fs::filesystem_error("Cannot create a temporary file", ec); } return file; } -#endif } // namespace file::file(std::ios::openmode mode)