[libc-commits] [libc] [libc] implement fgetwc (PR #196159)
via libc-commits
libc-commits at lists.llvm.org
Wed May 6 13:57:58 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-libc
Author: Michael Jones (michaelrj-google)
<details>
<summary>Changes</summary>
Add fgetwc function and tests. Part 3/11. All build file changes are in
part 11.
Assisted by Gemini
---
Full diff: https://github.com/llvm/llvm-project/pull/196159.diff
3 Files Affected:
- (added) libc/src/wchar/fgetwc.cpp (+34)
- (added) libc/src/wchar/fgetwc.h (+22)
- (added) libc/test/src/wchar/fgetwc_test.cpp (+145)
``````````diff
diff --git a/libc/src/wchar/fgetwc.cpp b/libc/src/wchar/fgetwc.cpp
new file mode 100644
index 0000000000000..a28596e51105f
--- /dev/null
+++ b/libc/src/wchar/fgetwc.cpp
@@ -0,0 +1,34 @@
+//===-- Implementation of fgetwc ------------------------------------------===//
+//
+// 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/wchar/fgetwc.h"
+#include "hdr/types/FILE.h"
+#include "hdr/types/wint_t.h"
+#include "hdr/wchar_macros.h" // For WEOF
+#include "src/__support/File/file.h"
+#include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/macros/null_check.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(wint_t, fgetwc, (::FILE *stream)) {
+ LIBC_CRASH_ON_NULLPTR(stream);
+ auto *f = reinterpret_cast<File *>(stream);
+ wchar_t wc;
+ FileIOResult result = f->read(&wc, 1);
+ if (result.has_error() || result.value < 1) {
+ if (result.has_error())
+ libc_errno = result.error;
+ return WEOF;
+ }
+ return static_cast<wint_t>(wc);
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/wchar/fgetwc.h b/libc/src/wchar/fgetwc.h
new file mode 100644
index 0000000000000..aa4c0048d6416
--- /dev/null
+++ b/libc/src/wchar/fgetwc.h
@@ -0,0 +1,22 @@
+//===-- Implementation header for fgetwc ----------------------------------===//
+//
+// 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_WCHAR_FGETWC_H
+#define LLVM_LIBC_SRC_WCHAR_FGETWC_H
+
+#include "hdr/types/FILE.h"
+#include "hdr/types/wint_t.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+wint_t fgetwc(::FILE *stream);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_WCHAR_FGETWC_H
diff --git a/libc/test/src/wchar/fgetwc_test.cpp b/libc/test/src/wchar/fgetwc_test.cpp
new file mode 100644
index 0000000000000..92f70b50bdacb
--- /dev/null
+++ b/libc/test/src/wchar/fgetwc_test.cpp
@@ -0,0 +1,145 @@
+//===-- Unittests for fgetwc ----------------------------------------------===//
+//
+// 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 "hdr/errno_macros.h"
+#include "hdr/wchar_macros.h" // For WEOF
+#include "src/stdio/fclose.h"
+#include "src/stdio/feof.h"
+#include "src/stdio/ferror.h"
+#include "src/stdio/fopen.h"
+#include "src/stdio/fwrite.h"
+#include "src/wchar/fgetwc.h"
+#include "src/wchar/fwide.h"
+#include "test/UnitTest/ErrnoCheckingTest.h"
+#include "test/UnitTest/Test.h"
+
+using LlvmLibcFgetwcTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
+
+TEST_F(LlvmLibcFgetwcTest, ReadASCII) {
+ auto FILENAME =
+ libc_make_test_file_path(APPEND_LIBC_TEST("fgetwc_ascii.test"));
+ ::FILE *file = LIBC_NAMESPACE::fopen(FILENAME, "w");
+ ASSERT_FALSE(file == nullptr);
+
+ // Write "abc"
+ constexpr char CONTENT[] = "abc";
+ ASSERT_EQ(LIBC_NAMESPACE::fwrite(CONTENT, 1, sizeof(CONTENT) - 1, file),
+ sizeof(CONTENT) - 1);
+ ASSERT_EQ(LIBC_NAMESPACE::fclose(file), 0);
+
+ // Open for reading
+ file = LIBC_NAMESPACE::fopen(FILENAME, "r");
+ ASSERT_FALSE(file == nullptr);
+
+ EXPECT_EQ(LIBC_NAMESPACE::fgetwc(file), static_cast<wint_t>(L'a'));
+ EXPECT_EQ(LIBC_NAMESPACE::fgetwc(file), static_cast<wint_t>(L'b'));
+ EXPECT_EQ(LIBC_NAMESPACE::fgetwc(file), static_cast<wint_t>(L'c'));
+
+ ASSERT_EQ(LIBC_NAMESPACE::fclose(file), 0);
+}
+
+TEST_F(LlvmLibcFgetwcTest, ReadUtf8) {
+ auto FILENAME =
+ libc_make_test_file_path(APPEND_LIBC_TEST("fgetwc_utf8.test"));
+ ::FILE *file = LIBC_NAMESPACE::fopen(FILENAME, "w");
+ ASSERT_FALSE(file == nullptr);
+
+ // Write "a¢€𐍈"
+ // a -> 0x61 (1-byte)
+ // ¢ -> 0xC2 0xA2 (2-byte)
+ // € -> 0xE2 0x82 0xAC (3-byte)
+ // 𐍈 -> 0xF0 0x90 0x8D 0x88 (4-byte)
+ constexpr unsigned char CONTENT[] = {0x61, 0xC2, 0xA2, 0xE2, 0x82,
+ 0xAC, 0xF0, 0x90, 0x8D, 0x88};
+ ASSERT_EQ(LIBC_NAMESPACE::fwrite(CONTENT, 1, sizeof(CONTENT), file),
+ sizeof(CONTENT));
+ ASSERT_EQ(LIBC_NAMESPACE::fclose(file), 0);
+
+ // Open for reading
+ file = LIBC_NAMESPACE::fopen(FILENAME, "r");
+ ASSERT_FALSE(file == nullptr);
+
+ EXPECT_EQ(LIBC_NAMESPACE::fgetwc(file), static_cast<wint_t>(L'a'));
+ EXPECT_EQ(LIBC_NAMESPACE::fgetwc(file), static_cast<wint_t>(L'¢'));
+ EXPECT_EQ(LIBC_NAMESPACE::fgetwc(file), static_cast<wint_t>(L'€'));
+ EXPECT_EQ(LIBC_NAMESPACE::fgetwc(file), static_cast<wint_t>(L'𐍈'));
+
+ ASSERT_EQ(LIBC_NAMESPACE::fclose(file), 0);
+}
+
+TEST_F(LlvmLibcFgetwcTest, EOFAndInvalidStream) {
+ auto FILENAME = libc_make_test_file_path(APPEND_LIBC_TEST("fgetwc_eof.test"));
+ ::FILE *file = LIBC_NAMESPACE::fopen(FILENAME, "w");
+ ASSERT_FALSE(file == nullptr);
+
+ // Write nothing, just close
+ ASSERT_EQ(LIBC_NAMESPACE::fclose(file), 0);
+
+ // Open for reading
+ file = LIBC_NAMESPACE::fopen(FILENAME, "r");
+ ASSERT_FALSE(file == nullptr);
+
+ // EOF Test
+ EXPECT_EQ(LIBC_NAMESPACE::fgetwc(file), static_cast<wint_t>(WEOF));
+ EXPECT_NE(LIBC_NAMESPACE::feof(file), 0);
+ EXPECT_EQ(LIBC_NAMESPACE::ferror(file), 0);
+
+ ASSERT_EQ(LIBC_NAMESPACE::fclose(file), 0);
+
+ // Open in write-only mode and try to read
+ file = LIBC_NAMESPACE::fopen(FILENAME, "w");
+ ASSERT_FALSE(file == nullptr);
+
+ ASSERT_ERRNO_SUCCESS();
+ EXPECT_EQ(LIBC_NAMESPACE::fgetwc(file), static_cast<wint_t>(WEOF));
+ ASSERT_ERRNO_EQ(EBADF);
+
+ ASSERT_EQ(LIBC_NAMESPACE::fclose(file), 0);
+}
+
+TEST_F(LlvmLibcFgetwcTest, EncodingErrorEILSEQ) {
+ auto FILENAME =
+ libc_make_test_file_path(APPEND_LIBC_TEST("fgetwc_eilseq.test"));
+ ::FILE *file = LIBC_NAMESPACE::fopen(FILENAME, "w");
+ ASSERT_FALSE(file == nullptr);
+
+ // Write an invalid UTF-8 sequence: 0x80 (stray continuation byte)
+ constexpr unsigned char CONTENT[] = {0x80};
+ ASSERT_EQ(LIBC_NAMESPACE::fwrite(CONTENT, 1, sizeof(CONTENT), file),
+ sizeof(CONTENT));
+ ASSERT_EQ(LIBC_NAMESPACE::fclose(file), 0);
+
+ // Open for reading
+ file = LIBC_NAMESPACE::fopen(FILENAME, "r");
+ ASSERT_FALSE(file == nullptr);
+
+ // Reading invalid sequence should fail with EILSEQ
+ ASSERT_ERRNO_SUCCESS();
+ EXPECT_EQ(LIBC_NAMESPACE::fgetwc(file), static_cast<wint_t>(WEOF));
+ ASSERT_ERRNO_EQ(EILSEQ);
+ EXPECT_NE(LIBC_NAMESPACE::ferror(file), 0);
+
+ ASSERT_EQ(LIBC_NAMESPACE::fclose(file), 0);
+}
+
+TEST_F(LlvmLibcFgetwcTest, ByteModeFailure) {
+ auto FILENAME =
+ libc_make_test_file_path(APPEND_LIBC_TEST("fgetwc_bytemode.test"));
+ ::FILE *file = LIBC_NAMESPACE::fopen(FILENAME, "w+");
+ ASSERT_FALSE(file == nullptr);
+
+ // Orient to byte mode
+ EXPECT_LT(LIBC_NAMESPACE::fwide(file, -1), 0);
+
+ // Read wide char should fail and set errno to EINVAL
+ EXPECT_EQ(LIBC_NAMESPACE::fgetwc(file), static_cast<wint_t>(WEOF));
+ ASSERT_ERRNO_EQ(EINVAL);
+ EXPECT_NE(LIBC_NAMESPACE::ferror(file), 0);
+
+ ASSERT_EQ(LIBC_NAMESPACE::fclose(file), 0);
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/196159
More information about the libc-commits
mailing list