diff --git a/posix.cc b/posix.cc index 03e02d4e..9aaeedc2 100644 --- a/posix.cc +++ b/posix.cc @@ -70,6 +70,16 @@ fmt::BufferedFile::~BufferedFile() FMT_NOEXCEPT(true) { fmt::report_system_error(errno, "cannot close file"); } +fmt::BufferedFile::BufferedFile(fmt::StringRef filename, fmt::StringRef mode) { + for (;;) { + file_ = FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())); + if (file_) + return; + if (errno != EINTR) + throw SystemError(errno, "cannot open file"); + } +} + void fmt::BufferedFile::close() { if (!file_) return; diff --git a/posix.h b/posix.h index 99a660c2..810ceaca 100644 --- a/posix.h +++ b/posix.h @@ -162,6 +162,9 @@ public: } #endif + // Opens a file. + BufferedFile(fmt::StringRef filename, fmt::StringRef mode); + // Closes the file. void close(); @@ -169,6 +172,11 @@ public: FILE *get() const { return file_; } int fileno() const; + + void print(fmt::StringRef format_str, const ArgList &args) { + fmt::print(file_, format_str, args); + } + FMT_VARIADIC(void, print, fmt::StringRef) }; // A file. Closed file is represented by a File object with descriptor -1. diff --git a/test/posix-test.cc b/test/posix-test.cc index 95391dce..b2e78e1a 100644 --- a/test/posix-test.cc +++ b/test/posix-test.cc @@ -50,6 +50,7 @@ int fdopen_count; int read_count; int write_count; int pipe_count; +int fopen_count; int fclose_count; int fileno_count; std::size_t read_nbyte; @@ -123,6 +124,11 @@ int test::pipe(int *pfds, unsigned psize, int textmode) { } #endif +FILE *test::fopen(const char *filename, const char *mode) { + EMULATE_EINTR(fopen, 0); + return ::fopen(filename, mode); +} + int test::fclose(FILE *stream) { EMULATE_EINTR(fclose, EOF); return ::fclose(stream); @@ -148,10 +154,16 @@ int test::fileno(FILE *stream) { # define EXPECT_EQ_POSIX(expected, actual) #endif +void write_file(fmt::StringRef filename, fmt::StringRef content) { + fmt::BufferedFile f(filename, "w"); + fmt::print(f.get(), "{}", content); +} + TEST(FileTest, OpenRetry) { + write_file("test", "there must be something here"); File *f = 0; - EXPECT_RETRY(f = new File("CMakeLists.txt", File::RDONLY), - open, "cannot open file CMakeLists.txt"); + EXPECT_RETRY(f = new File("test", File::RDONLY), + open, "cannot open file test"); #ifndef _WIN32 char c = 0; f->read(&c, 1); @@ -289,6 +301,18 @@ TEST(FileTest, FdopenNoRetry) { fdopen_count = 0; } +TEST(BufferedFileTest, OpenRetry) { + write_file("test", "there must be something here"); + BufferedFile *f = 0; + EXPECT_RETRY(f = new BufferedFile("test", "r"), + fopen, "cannot open file test"); +#ifndef _WIN32 + char c = 0; + fread(&c, 1, 1, f->get()); +#endif + delete f; +} + TEST(BufferedFileTest, CloseNoRetryInDtor) { File read_end, write_end; File::pipe(read_end, write_end); diff --git a/test/posix-test.h b/test/posix-test.h index c42a20e4..355409fd 100644 --- a/test/posix-test.h +++ b/test/posix-test.h @@ -61,6 +61,7 @@ int pipe(int fildes[2]); int pipe(int *pfds, unsigned psize, int textmode); #endif +FILE *fopen(const char *filename, const char *mode); int fclose(FILE *stream); int fileno(FILE *stream);