diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -251,8 +251,14 @@ libc.src.stdlib.getenv # stdio.h entrypoints + libc.src.stdio.clearerr + libc.src.stdio.clearerr_unlocked libc.src.stdio.fclose libc.src.stdio.flockfile + libc.src.stdio.feof + libc.src.stdio.feof_unlocked + libc.src.stdio.ferror + libc.src.stdio.ferror_unlocked libc.src.stdio.fflush libc.src.stdio.fopen libc.src.stdio.fopencookie diff --git a/libc/spec/gnu_ext.td b/libc/spec/gnu_ext.td --- a/libc/spec/gnu_ext.td +++ b/libc/spec/gnu_ext.td @@ -72,6 +72,21 @@ [CookieIOFunctionsT], // Types [], // Enumerations [ + FunctionSpec< + "clearerr_unlocked", + RetValSpec, + [ArgSpec] + >, + FunctionSpec< + "feof_unlocked", + RetValSpec, + [ArgSpec] + >, + FunctionSpec< + "ferror_unlocked", + RetValSpec, + [ArgSpec] + >, FunctionSpec< "fopencookie", RetValSpec, diff --git a/libc/spec/stdc.td b/libc/spec/stdc.td --- a/libc/spec/stdc.td +++ b/libc/spec/stdc.td @@ -478,11 +478,26 @@ ], [], // Enumerations [ + FunctionSpec< + "clearerr", + RetValSpec, + [ArgSpec] + >, FunctionSpec< "fclose", RetValSpec, [ArgSpec] >, + FunctionSpec< + "feof", + RetValSpec, + [ArgSpec] + >, + FunctionSpec< + "ferror", + RetValSpec, + [ArgSpec] + >, FunctionSpec< "fflush", RetValSpec, diff --git a/libc/src/__support/File/file.h b/libc/src/__support/File/file.h --- a/libc/src/__support/File/file.h +++ b/libc/src/__support/File/file.h @@ -97,6 +97,19 @@ bool eof; bool err; + // This is a convenience RAII class to lock and unlock file objects. + class FileLock { + File *file; + + public: + explicit FileLock(File *f) : file(f) { file->lock(); } + + ~FileLock() { file->unlock(); } + + FileLock(const FileLock &) = delete; + FileLock(FileLock &&) = delete; + }; + protected: bool write_allowed() const { return mode & (static_cast(OpenMode::WRITE) | @@ -150,10 +163,8 @@ // Buffered write of |len| bytes from |data| under the file lock. size_t write(const void *data, size_t len) { - lock(); - size_t ret = write_unlocked(data, len); - unlock(); - return ret; + FileLock l(this); + return write_unlocked(data, len); } // Buffered read of |len| bytes into |data| without the file lock. @@ -161,10 +172,8 @@ // Buffered read of |len| bytes into |data| under the file lock. size_t read(void *data, size_t len) { - lock(); - size_t ret = read_unlocked(data, len); - unlock(); - return ret; + FileLock l(this); + return read_unlocked(data, len); } int seek(long offset, int whence); @@ -184,26 +193,30 @@ void lock() { mutex.lock(); } void unlock() { mutex.unlock(); } - bool error() const { return err; } - void clearerr() { err = false; } - bool iseof() const { return eof; } + bool error_unlocked() const { return err; } - // Returns an bit map of flags corresponding to enumerations of - // OpenMode, ContentType and CreateType. - static ModeFlags mode_flags(const char *mode); -}; + bool error() { + FileLock l(this); + return error_unlocked(); + } -// This is a convenience RAII class to lock and unlock file objects. -class FileLock { - File *file; + void clearerr_unlocked() { err = false; } -public: - explicit FileLock(File *f) : file(f) { file->lock(); } + void clearerr() { + FileLock l(this); + clearerr_unlocked(); + } + + bool iseof_unlocked() { return eof; } - ~FileLock() { file->unlock(); } + bool iseof() { + FileLock l(this); + return iseof_unlocked(); + } - FileLock(const FileLock &) = delete; - FileLock(FileLock &&) = delete; + // Returns an bit map of flags corresponding to enumerations of + // OpenMode, ContentType and CreateType. + static ModeFlags mode_flags(const char *mode); }; // The implementaiton of this function is provided by the platfrom_file diff --git a/libc/src/stdio/CMakeLists.txt b/libc/src/stdio/CMakeLists.txt --- a/libc/src/stdio/CMakeLists.txt +++ b/libc/src/stdio/CMakeLists.txt @@ -24,6 +24,78 @@ libc.src.__support.File.platform_file ) +add_entrypoint_object( + clearerr + SRCS + clearerr.cpp + HDRS + clearerr.h + DEPENDS + libc.include.stdio + libc.src.__support.File.file + libc.src.__support.File.platform_file +) + +add_entrypoint_object( + clearerr_unlocked + SRCS + clearerr_unlocked.cpp + HDRS + clearerr_unlocked.h + DEPENDS + libc.include.stdio + libc.src.__support.File.file + libc.src.__support.File.platform_file +) + +add_entrypoint_object( + feof + SRCS + feof.cpp + HDRS + feof.h + DEPENDS + libc.include.stdio + libc.src.__support.File.file + libc.src.__support.File.platform_file +) + +add_entrypoint_object( + feof_unlocked + SRCS + feof_unlocked.cpp + HDRS + feof_unlocked.h + DEPENDS + libc.include.stdio + libc.src.__support.File.file + libc.src.__support.File.platform_file +) + +add_entrypoint_object( + ferror + SRCS + ferror.cpp + HDRS + ferror.h + DEPENDS + libc.include.stdio + libc.src.__support.File.file + libc.src.__support.File.platform_file +) + +add_entrypoint_object( + ferror_unlocked + SRCS + ferror_unlocked.cpp + HDRS + ferror_unlocked.h + DEPENDS + libc.include.stdio + libc.src.__support.File.file + libc.src.__support.File.platform_file +) + add_entrypoint_object( fflush SRCS diff --git a/libc/src/stdio/clearerr.h b/libc/src/stdio/clearerr.h new file mode 100644 --- /dev/null +++ b/libc/src/stdio/clearerr.h @@ -0,0 +1,20 @@ +//===-- Implementation header of clearerr -----------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SRC_STDIO_CLEARERR_H +#define LLVM_LIBC_SRC_STDIO_CLEARERR_H + +#include + +namespace __llvm_libc { + +void clearerr(::FILE *stream); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_STDIO_CLEARERR_H diff --git a/libc/src/stdio/clearerr.cpp b/libc/src/stdio/clearerr.cpp new file mode 100644 --- /dev/null +++ b/libc/src/stdio/clearerr.cpp @@ -0,0 +1,20 @@ +//===-- Implementation of clearerr ----------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/stdio/clearerr.h" +#include "src/__support/File/file.h" + +#include + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(void, clearerr, (::FILE * stream)) { + reinterpret_cast<__llvm_libc::File *>(stream)->clearerr(); +} + +} // namespace __llvm_libc diff --git a/libc/src/stdio/clearerr_unlocked.h b/libc/src/stdio/clearerr_unlocked.h new file mode 100644 --- /dev/null +++ b/libc/src/stdio/clearerr_unlocked.h @@ -0,0 +1,20 @@ +//===-- Implementation header of clearerr_unlocked --------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SRC_STDIO_CLEARERR_UNLOCKED_H +#define LLVM_LIBC_SRC_STDIO_CLEARERR_UNLOCKED_H + +#include + +namespace __llvm_libc { + +void clearerr_unlocked(::FILE *stream); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_STDIO_CLEARERR_UNLOCKED_H diff --git a/libc/src/stdio/clearerr_unlocked.cpp b/libc/src/stdio/clearerr_unlocked.cpp new file mode 100644 --- /dev/null +++ b/libc/src/stdio/clearerr_unlocked.cpp @@ -0,0 +1,20 @@ +//===-- Implementation of clearerr_unlocked -------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/stdio/clearerr_unlocked.h" +#include "src/__support/File/file.h" + +#include + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(void, clearerr_unlocked, (::FILE * stream)) { + reinterpret_cast<__llvm_libc::File *>(stream)->clearerr_unlocked(); +} + +} // namespace __llvm_libc diff --git a/libc/src/stdio/feof.h b/libc/src/stdio/feof.h new file mode 100644 --- /dev/null +++ b/libc/src/stdio/feof.h @@ -0,0 +1,20 @@ +//===-- Implementation header of feof ---------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SRC_STDIO_FEOF_H +#define LLVM_LIBC_SRC_STDIO_FEOF_H + +#include + +namespace __llvm_libc { + +int feof(::FILE *stream); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_STDIO_FEOF_H diff --git a/libc/src/stdio/feof.cpp b/libc/src/stdio/feof.cpp new file mode 100644 --- /dev/null +++ b/libc/src/stdio/feof.cpp @@ -0,0 +1,20 @@ +//===-- Implementation of feof --------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/stdio/feof.h" +#include "src/__support/File/file.h" + +#include + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(int, feof, (::FILE * stream)) { + return reinterpret_cast<__llvm_libc::File *>(stream)->iseof(); +} + +} // namespace __llvm_libc diff --git a/libc/src/stdio/feof_unlocked.h b/libc/src/stdio/feof_unlocked.h new file mode 100644 --- /dev/null +++ b/libc/src/stdio/feof_unlocked.h @@ -0,0 +1,20 @@ +//===-- Implementation header of feof_unlocked ------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SRC_STDIO_FEOF_UNLOCKED_H +#define LLVM_LIBC_SRC_STDIO_FEOF_UNLOCKED_H + +#include + +namespace __llvm_libc { + +int feof_unlocked(::FILE *stream); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_STDIO_FEOF_UNLOCKED_H diff --git a/libc/src/stdio/feof_unlocked.cpp b/libc/src/stdio/feof_unlocked.cpp new file mode 100644 --- /dev/null +++ b/libc/src/stdio/feof_unlocked.cpp @@ -0,0 +1,20 @@ +//===-- Implementation of feof_unlocked -----------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/stdio/feof_unlocked.h" +#include "src/__support/File/file.h" + +#include + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(int, feof_unlocked, (::FILE * stream)) { + return reinterpret_cast<__llvm_libc::File *>(stream)->iseof_unlocked(); +} + +} // namespace __llvm_libc diff --git a/libc/src/stdio/ferror.h b/libc/src/stdio/ferror.h new file mode 100644 --- /dev/null +++ b/libc/src/stdio/ferror.h @@ -0,0 +1,20 @@ +//===-- Implementation header of ferror -------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SRC_STDIO_FERROR_H +#define LLVM_LIBC_SRC_STDIO_FERROR_H + +#include + +namespace __llvm_libc { + +int ferror(::FILE *stream); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_STDIO_FERROR_H diff --git a/libc/src/stdio/ferror.cpp b/libc/src/stdio/ferror.cpp new file mode 100644 --- /dev/null +++ b/libc/src/stdio/ferror.cpp @@ -0,0 +1,20 @@ +//===-- Implementation of ferror ------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/stdio/ferror.h" +#include "src/__support/File/file.h" + +#include + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(int, ferror, (::FILE * stream)) { + return reinterpret_cast<__llvm_libc::File *>(stream)->error(); +} + +} // namespace __llvm_libc diff --git a/libc/src/stdio/ferror_unlocked.h b/libc/src/stdio/ferror_unlocked.h new file mode 100644 --- /dev/null +++ b/libc/src/stdio/ferror_unlocked.h @@ -0,0 +1,20 @@ +//===-- Implementation header of ferror_unlocked ----------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SRC_STDIO_FERROR_UNLOCKED_H +#define LLVM_LIBC_SRC_STDIO_FERROR_UNLOCKED_H + +#include + +namespace __llvm_libc { + +int ferror_unlocked(::FILE *stream); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_STDIO_FERROR_UNLOCKED_H diff --git a/libc/src/stdio/ferror_unlocked.cpp b/libc/src/stdio/ferror_unlocked.cpp new file mode 100644 --- /dev/null +++ b/libc/src/stdio/ferror_unlocked.cpp @@ -0,0 +1,20 @@ +//===-- Implementation of ferror_unlocked ---------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/stdio/ferror_unlocked.h" +#include "src/__support/File/file.h" + +#include + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(int, ferror_unlocked, (::FILE * stream)) { + return reinterpret_cast<__llvm_libc::File *>(stream)->error_unlocked(); +} + +} // namespace __llvm_libc diff --git a/libc/test/src/stdio/CMakeLists.txt b/libc/test/src/stdio/CMakeLists.txt --- a/libc/test/src/stdio/CMakeLists.txt +++ b/libc/test/src/stdio/CMakeLists.txt @@ -7,7 +7,12 @@ SRCS fileop_test.cpp DEPENDS + libc.include.errno + libc.include.stdio + libc.src.stdio.clearerr libc.src.stdio.fclose + libc.src.stdio.feof + libc.src.stdio.ferror libc.src.stdio.fflush libc.src.stdio.fopen libc.src.stdio.fread @@ -22,7 +27,12 @@ SRCS unlocked_fileop_test.cpp DEPENDS + libc.include.errno + libc.include.stdio + libc.src.stdio.clearerr_unlocked libc.src.stdio.fclose + libc.src.stdio.feof_unlocked + libc.src.stdio.ferror_unlocked libc.src.stdio.flockfile libc.src.stdio.fopen libc.src.stdio.fread_unlocked @@ -40,7 +50,10 @@ libc.include.errno libc.include.stdio libc.include.stdlib + libc.src.stdio.clearerr libc.src.stdio.fclose + libc.src.stdio.feof + libc.src.stdio.ferror libc.src.stdio.fflush libc.src.stdio.fopencookie libc.src.stdio.fread diff --git a/libc/test/src/stdio/fileop_test.cpp b/libc/test/src/stdio/fileop_test.cpp --- a/libc/test/src/stdio/fileop_test.cpp +++ b/libc/test/src/stdio/fileop_test.cpp @@ -6,7 +6,10 @@ // //===----------------------------------------------------------------------===// +#include "src/stdio/clearerr.h" #include "src/stdio/fclose.h" +#include "src/stdio/feof.h" +#include "src/stdio/ferror.h" #include "src/stdio/fflush.h" #include "src/stdio/fopen.h" #include "src/stdio/fread.h" @@ -14,15 +17,27 @@ #include "src/stdio/fwrite.h" #include "utils/UnitTest/Test.h" +#include #include -TEST(LlvmLibcStdio, SimpleOperations) { +TEST(LlvmLibcFILETest, SimpleFileOperations) { constexpr char FILENAME[] = "testdata/simple_operations.test"; ::FILE *file = __llvm_libc::fopen(FILENAME, "w"); ASSERT_FALSE(file == nullptr); constexpr char CONTENT[] = "1234567890987654321"; ASSERT_EQ(sizeof(CONTENT) - 1, __llvm_libc::fwrite(CONTENT, 1, sizeof(CONTENT) - 1, file)); + + // This is not a readable file. + char read_data[sizeof(CONTENT)]; + ASSERT_EQ(__llvm_libc::fread(read_data, 1, sizeof(CONTENT), file), size_t(0)); + ASSERT_NE(__llvm_libc::ferror(file), 0); + EXPECT_NE(errno, 0); + errno = 0; + + __llvm_libc::clearerr(file); + ASSERT_EQ(__llvm_libc::ferror(file), 0); + ASSERT_EQ(0, __llvm_libc::fclose(file)); file = __llvm_libc::fopen(FILENAME, "r"); @@ -40,10 +55,24 @@ ASSERT_EQ(__llvm_libc::fread(data, 1, READ_SIZE - 1, file), READ_SIZE - 1); ASSERT_STREQ(data, "9098"); + // Reading another time should trigger eof. + ASSERT_NE(sizeof(CONTENT), + __llvm_libc::fread(read_data, 1, sizeof(CONTENT), file)); + ASSERT_NE(__llvm_libc::feof(file), 0); + + // Should be an error to write. + ASSERT_EQ(size_t(0), __llvm_libc::fwrite(CONTENT, 1, sizeof(CONTENT), file)); + ASSERT_NE(__llvm_libc::ferror(file), 0); + ASSERT_NE(errno, 0); + errno = 0; + + __llvm_libc::clearerr(file); + ASSERT_EQ(__llvm_libc::ferror(file), 0); + ASSERT_EQ(__llvm_libc::fclose(file), 0); } -TEST(LlvmLibcFILE, FFlushTest) { +TEST(LlvmLibcFILETest, FFlush) { constexpr char FILENAME[] = "testdata/fflush.test"; ::FILE *file = __llvm_libc::fopen(FILENAME, "w+"); ASSERT_FALSE(file == nullptr); diff --git a/libc/test/src/stdio/fopencookie_test.cpp b/libc/test/src/stdio/fopencookie_test.cpp --- a/libc/test/src/stdio/fopencookie_test.cpp +++ b/libc/test/src/stdio/fopencookie_test.cpp @@ -6,7 +6,10 @@ // //===----------------------------------------------------------------------===// +#include "src/stdio/clearerr.h" #include "src/stdio/fclose.h" +#include "src/stdio/feof.h" +#include "src/stdio/ferror.h" #include "src/stdio/fflush.h" #include "src/stdio/fopencookie.h" #include "src/stdio/fread.h" @@ -102,12 +105,21 @@ __llvm_libc::fread(read_data, 1, sizeof(CONTENT), f)); ASSERT_STREQ(read_data, CONTENT); + // Reading another time should trigger eof. + ASSERT_NE(sizeof(CONTENT), + __llvm_libc::fread(read_data, 1, sizeof(CONTENT), f)); + ASSERT_NE(__llvm_libc::feof(f), 0); + ASSERT_EQ(0, __llvm_libc::fseek(f, 0, SEEK_SET)); // Should be an error to write. ASSERT_EQ(size_t(0), __llvm_libc::fwrite(CONTENT, 1, sizeof(CONTENT), f)); - ASSERT_EQ(errno, EBADF); + ASSERT_NE(__llvm_libc::ferror(f), 0); + ASSERT_NE(errno, 0); errno = 0; + __llvm_libc::clearerr(f); + ASSERT_EQ(__llvm_libc::ferror(f), 0); + ASSERT_EQ(0, __llvm_libc::fclose(f)); free(ss); } @@ -134,9 +146,13 @@ char read_data[sizeof(WRITE_DATA)]; // Should be an error to read. ASSERT_EQ(size_t(0), __llvm_libc::fread(read_data, 1, sizeof(WRITE_DATA), f)); + ASSERT_NE(__llvm_libc::ferror(f), 0); ASSERT_EQ(errno, EBADF); errno = 0; + __llvm_libc::clearerr(f); + ASSERT_EQ(__llvm_libc::ferror(f), 0); + ASSERT_EQ(0, __llvm_libc::fclose(f)); free(ss); } @@ -159,9 +175,13 @@ char read_data[READ_SIZE]; // This is not a readable file. ASSERT_EQ(__llvm_libc::fread(read_data, 1, READ_SIZE, f), size_t(0)); + ASSERT_NE(__llvm_libc::ferror(f), 0); EXPECT_NE(errno, 0); errno = 0; + __llvm_libc::clearerr(f); + ASSERT_EQ(__llvm_libc::ferror(f), 0); + ASSERT_EQ(__llvm_libc::fwrite(WRITE_DATA, 1, sizeof(WRITE_DATA), f), sizeof(WRITE_DATA)); EXPECT_EQ(__llvm_libc::fflush(f), 0); diff --git a/libc/test/src/stdio/unlocked_fileop_test.cpp b/libc/test/src/stdio/unlocked_fileop_test.cpp --- a/libc/test/src/stdio/unlocked_fileop_test.cpp +++ b/libc/test/src/stdio/unlocked_fileop_test.cpp @@ -1,4 +1,4 @@ -//===-- Unittests for file operations like fopen, flcose etc --------------===// +//===-- Unittests for f operations like fopen, flcose etc --------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,7 +6,10 @@ // //===----------------------------------------------------------------------===// +#include "src/stdio/clearerr_unlocked.h" #include "src/stdio/fclose.h" +#include "src/stdio/feof_unlocked.h" +#include "src/stdio/ferror_unlocked.h" #include "src/stdio/flockfile.h" #include "src/stdio/fopen.h" #include "src/stdio/fread_unlocked.h" @@ -14,31 +17,60 @@ #include "src/stdio/fwrite_unlocked.h" #include "utils/UnitTest/Test.h" +#include #include TEST(LlvmLibcFILETest, UnlockedReadAndWrite) { - constexpr char FILENAME[] = "testdata/unlocked_read_and_write.test"; - ::FILE *file = __llvm_libc::fopen(FILENAME, "w"); - ASSERT_FALSE(file == nullptr); + constexpr char fNAME[] = "testdata/unlocked_read_and_write.test"; + ::FILE *f = __llvm_libc::fopen(fNAME, "w"); + ASSERT_FALSE(f == nullptr); constexpr char CONTENT[] = "1234567890987654321"; - __llvm_libc::flockfile(file); - ASSERT_EQ(sizeof(CONTENT) - 1, __llvm_libc::fwrite_unlocked( - CONTENT, 1, sizeof(CONTENT) - 1, file)); - __llvm_libc::funlockfile(file); - ASSERT_EQ(0, __llvm_libc::fclose(file)); - - file = __llvm_libc::fopen(FILENAME, "r"); - ASSERT_FALSE(file == nullptr); - + __llvm_libc::flockfile(f); + ASSERT_EQ(sizeof(CONTENT) - 1, + __llvm_libc::fwrite_unlocked(CONTENT, 1, sizeof(CONTENT) - 1, f)); + // Should be an error to read. constexpr size_t READ_SIZE = 5; char data[READ_SIZE * 2 + 1]; data[READ_SIZE * 2] = '\0'; - __llvm_libc::flockfile(file); - ASSERT_EQ(__llvm_libc::fread_unlocked(data, 1, READ_SIZE, file), READ_SIZE); - ASSERT_EQ(__llvm_libc::fread_unlocked(data + READ_SIZE, 1, READ_SIZE, file), + + ASSERT_EQ(size_t(0), + __llvm_libc::fread_unlocked(data, 1, sizeof(READ_SIZE), f)); + ASSERT_NE(__llvm_libc::ferror_unlocked(f), 0); + ASSERT_NE(errno, 0); + errno = 0; + + __llvm_libc::clearerr_unlocked(f); + ASSERT_EQ(__llvm_libc::ferror_unlocked(f), 0); + + __llvm_libc::funlockfile(f); + ASSERT_EQ(0, __llvm_libc::fclose(f)); + + f = __llvm_libc::fopen(fNAME, "r"); + ASSERT_FALSE(f == nullptr); + + __llvm_libc::flockfile(f); + ASSERT_EQ(__llvm_libc::fread_unlocked(data, 1, READ_SIZE, f), READ_SIZE); + ASSERT_EQ(__llvm_libc::fread_unlocked(data + READ_SIZE, 1, READ_SIZE, f), READ_SIZE); - __llvm_libc::funlockfile(file); + + // Should be an error to write. + ASSERT_EQ(size_t(0), + __llvm_libc::fwrite_unlocked(CONTENT, 1, sizeof(CONTENT), f)); + ASSERT_NE(__llvm_libc::ferror_unlocked(f), 0); + ASSERT_NE(errno, 0); + errno = 0; + + __llvm_libc::clearerr_unlocked(f); + ASSERT_EQ(__llvm_libc::ferror_unlocked(f), 0); + + // Reading more should trigger eof. + char large_data[sizeof(CONTENT)]; + ASSERT_NE(sizeof(CONTENT), + __llvm_libc::fread_unlocked(large_data, 1, sizeof(CONTENT), f)); + ASSERT_NE(__llvm_libc::feof_unlocked(f), 0); + + __llvm_libc::funlockfile(f); ASSERT_STREQ(data, "1234567890"); - ASSERT_EQ(__llvm_libc::fclose(file), 0); + ASSERT_EQ(__llvm_libc::fclose(f), 0); }