/* * This source code is released into the public domain. */ #ifndef LFJAIL_READ_FILE_HH #define LFJAIL_READ_FILE_HH #include #include #include #include #include #include #include #include #include #include #include "context.hh" #include "io_error.hh" #include "guard.hh" /* * std::formatter for path was only added in C++26; LLVM 19 doesn't have it. */ #ifndef __cpp_lib_format_path template<> struct std::formatter { template constexpr auto parse(ParseContext &ctx) -> ParseContext::iterator { return ctx.begin(); } template auto format(std::filesystem::path const &path, FmtContext& ctx) const -> FmtContext::iterator { return std::ranges::copy(path.native(), ctx.out()).out; } }; #endif // !__cpp_lib_format_path namespace lfjail { void ensure_dir(context const &ctx, std::filesystem::path const &dir); /* * Load the contents of a file into an output iterator. Throws io_error * on failure. */ void read_file(std::filesystem::path const &filename, std::output_iterator auto &&iter) { constexpr std::size_t bufsize = 1024; std::array buffer; int fd, err; if ((fd = ::open(filename.c_str(), O_RDONLY)) == -1) throw io_error(filename, errno); auto fd_guard = guard([fd] { ::close(fd); }); while ((err = ::read(fd, &buffer[0], buffer.size())) > 0) { auto data = std::span(buffer).subspan(0, err); std::ranges::copy(data, iter); } if (err == -1) throw io_error(filename, errno); } /* * Write the contents of a range to a file. Throws io_error on error. */ void write_file(std::filesystem::path const &filename, std::ranges::range auto &&range) { // Convert the range into an array. std::vector chars; std::ranges::copy(range, std::back_inserter(chars)); // Open the output file. int fd; if ((fd = ::open(filename.c_str(), O_CREAT|O_WRONLY, 0777)) == -1) throw io_error(filename, errno); auto fd_guard = guard([fd] { ::close(fd); }); // Write the data. if (int err = ::write(fd, chars.data(), chars.size()); err < 0) throw io_error(filename, errno); } /* * Write the contents of a range to a file safely. The data will be written * to ".tmp", and if the write succeeds, the temporary file will be * renamed to the target filename. If an error occurs, the target file will * not be modified. */ void safe_write_file(std::filesystem::path const &filename, std::ranges::range auto &&range) { auto tmpfile(filename); tmpfile.remove_filename(); tmpfile /= (filename.filename().native() + ".tmp"); auto tmpfile_guard = guard([tmpfile] { ::unlink(tmpfile.c_str()); }); write_file(tmpfile, std::forward(range)); int err = ::rename(tmpfile.c_str(), filename.c_str()); if (err != 0) throw io_error(filename, errno); } } // namespace lfjail #endif // !LFJAIL_FILEUTILS_HH