[libc-commits] [libc] [libc] implement fputwc (PR #196158)

via libc-commits libc-commits at lists.llvm.org
Wed May 6 12:54:24 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 fputwc function and tests. Part 2/11. All build file changes are in
part 11.


---
Full diff: https://github.com/llvm/llvm-project/pull/196158.diff


3 Files Affected:

- (added) libc/src/wchar/fputwc.cpp (+33) 
- (added) libc/src/wchar/fputwc.h (+22) 
- (added) libc/test/src/wchar/fputwc_test.cpp (+146) 


``````````diff
diff --git a/libc/src/wchar/fputwc.cpp b/libc/src/wchar/fputwc.cpp
new file mode 100644
index 0000000000000..490c8068aab52
--- /dev/null
+++ b/libc/src/wchar/fputwc.cpp
@@ -0,0 +1,33 @@
+//===-- Implementation of fputwc ------------------------------------------===//
+//
+// 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/fputwc.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, fputwc, (wchar_t wc, ::FILE *stream)) {
+  LIBC_CRASH_ON_NULLPTR(stream);
+  auto *f = reinterpret_cast<File *>(stream);
+  FileIOResult result = f->write(&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/fputwc.h b/libc/src/wchar/fputwc.h
new file mode 100644
index 0000000000000..cd20698a515bf
--- /dev/null
+++ b/libc/src/wchar/fputwc.h
@@ -0,0 +1,22 @@
+//===-- Implementation header for fputwc ----------------------------------===//
+//
+// 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_FPUTWC_H
+#define LLVM_LIBC_SRC_WCHAR_FPUTWC_H
+
+#include "hdr/types/FILE.h"
+#include "hdr/types/wint_t.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+wint_t fputwc(wchar_t wc, ::FILE *stream);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_WCHAR_FPUTWC_H
diff --git a/libc/test/src/wchar/fputwc_test.cpp b/libc/test/src/wchar/fputwc_test.cpp
new file mode 100644
index 0000000000000..740d84643c4e3
--- /dev/null
+++ b/libc/test/src/wchar/fputwc_test.cpp
@@ -0,0 +1,146 @@
+//===-- Unittests for fputwc ----------------------------------------------===//
+//
+// 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/ferror.h"
+#include "src/stdio/fopen.h"
+#include "src/stdio/fread.h"
+#include "src/stdio/fwrite.h"
+#include "src/wchar/fputwc.h"
+#include "src/wchar/fwide.h"
+#include "test/UnitTest/ErrnoCheckingTest.h"
+#include "test/UnitTest/Test.h"
+
+using LlvmLibcFputwcTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
+
+TEST_F(LlvmLibcFputwcTest, WriteASCII) {
+  auto FILENAME =
+      libc_make_test_file_path(APPEND_LIBC_TEST("fputwc_ascii.test"));
+  ::FILE *file = LIBC_NAMESPACE::fopen(FILENAME, "w");
+  ASSERT_FALSE(file == nullptr);
+
+  // Write 'a', 'b', 'c'
+  EXPECT_EQ(LIBC_NAMESPACE::fputwc(L'a', file), static_cast<wint_t>(L'a'));
+  EXPECT_EQ(LIBC_NAMESPACE::fputwc(L'b', file), static_cast<wint_t>(L'b'));
+  EXPECT_EQ(LIBC_NAMESPACE::fputwc(L'c', file), static_cast<wint_t>(L'c'));
+
+  ASSERT_EQ(LIBC_NAMESPACE::fclose(file), 0);
+
+  // Open again to read raw bytes
+  file = LIBC_NAMESPACE::fopen(FILENAME, "r");
+  ASSERT_FALSE(file == nullptr);
+
+  char buffer[10] = {0};
+  ASSERT_EQ(LIBC_NAMESPACE::fread(buffer, 1, 3, file), size_t(3));
+  EXPECT_STREQ(buffer, "abc");
+
+  ASSERT_EQ(LIBC_NAMESPACE::fclose(file), 0);
+}
+
+TEST_F(LlvmLibcFputwcTest, WriteUtf8) {
+  auto FILENAME =
+      libc_make_test_file_path(APPEND_LIBC_TEST("fputwc_utf8.test"));
+  ::FILE *file = LIBC_NAMESPACE::fopen(FILENAME, "w");
+  ASSERT_FALSE(file == nullptr);
+
+  // 1-byte character: 'a' (L'a', 0x61) -> UTF-8: 0x61
+  EXPECT_EQ(LIBC_NAMESPACE::fputwc(L'a', file), static_cast<wint_t>(L'a'));
+
+  // 2-byte character: '¢' (L'¢', 0xA2) -> UTF-8: 0xC2 0xA2
+  EXPECT_EQ(LIBC_NAMESPACE::fputwc(L'¢', file), static_cast<wint_t>(L'¢'));
+
+  // 3-byte character: '€' (L'€', 0x20AC) -> UTF-8: 0xE2 0x82 0xAC
+  EXPECT_EQ(LIBC_NAMESPACE::fputwc(L'€', file), static_cast<wint_t>(L'€'));
+
+  // 4-byte character: '𐍈' (L'𐍈', 0x10348) -> UTF-8: 0xF0 0x90 0x8D 0x88
+  EXPECT_EQ(LIBC_NAMESPACE::fputwc(L'𐍈', file), static_cast<wint_t>(L'𐍈'));
+
+  ASSERT_EQ(LIBC_NAMESPACE::fclose(file), 0);
+
+  // Open again to read raw bytes
+  file = LIBC_NAMESPACE::fopen(FILENAME, "r");
+  ASSERT_FALSE(file == nullptr);
+
+  unsigned char buffer[15] = {0};
+  ASSERT_EQ(LIBC_NAMESPACE::fread(buffer, 1, 10, file), size_t(10));
+
+  // Verify 1-byte
+  EXPECT_EQ(buffer[0], static_cast<unsigned char>(0x61));
+
+  // Verify 2-byte
+  EXPECT_EQ(buffer[1], static_cast<unsigned char>(0xC2));
+  EXPECT_EQ(buffer[2], static_cast<unsigned char>(0xA2));
+
+  // Verify 3-byte
+  EXPECT_EQ(buffer[3], static_cast<unsigned char>(0xE2));
+  EXPECT_EQ(buffer[4], static_cast<unsigned char>(0x82));
+  EXPECT_EQ(buffer[5], static_cast<unsigned char>(0xAC));
+
+  // Verify 4-byte
+  EXPECT_EQ(buffer[6], static_cast<unsigned char>(0xF0));
+  EXPECT_EQ(buffer[7], static_cast<unsigned char>(0x90));
+  EXPECT_EQ(buffer[8], static_cast<unsigned char>(0x8D));
+  EXPECT_EQ(buffer[9], static_cast<unsigned char>(0x88));
+
+  ASSERT_EQ(LIBC_NAMESPACE::fclose(file), 0);
+}
+
+TEST_F(LlvmLibcFputwcTest, EncodingErrorEILSEQ) {
+  auto FILENAME =
+      libc_make_test_file_path(APPEND_LIBC_TEST("fputwc_eilseq.test"));
+  ::FILE *file = LIBC_NAMESPACE::fopen(FILENAME, "w");
+  ASSERT_FALSE(file == nullptr);
+
+  // Try to write an invalid wide character point (outside Unicode range)
+  ASSERT_ERRNO_SUCCESS();
+  EXPECT_EQ(LIBC_NAMESPACE::fputwc(static_cast<wchar_t>(0x110000), 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(LlvmLibcFputwcTest, InvalidStream) {
+  auto FILENAME =
+      libc_make_test_file_path(APPEND_LIBC_TEST("fputwc_invalid.test"));
+
+  // Create the file first
+  ::FILE *file = LIBC_NAMESPACE::fopen(FILENAME, "w");
+  ASSERT_FALSE(file == nullptr);
+  ASSERT_EQ(LIBC_NAMESPACE::fclose(file), 0);
+
+  // Open in read-only mode
+  file = LIBC_NAMESPACE::fopen(FILENAME, "r");
+  ASSERT_FALSE(file == nullptr);
+
+  // Try to write to read-only file
+  ASSERT_ERRNO_SUCCESS();
+  EXPECT_EQ(LIBC_NAMESPACE::fputwc(L'x', file), static_cast<wint_t>(WEOF));
+  ASSERT_ERRNO_EQ(EBADF);
+
+  ASSERT_EQ(LIBC_NAMESPACE::fclose(file), 0);
+}
+
+TEST_F(LlvmLibcFputwcTest, ByteModeFailure) {
+  auto FILENAME =
+      libc_make_test_file_path(APPEND_LIBC_TEST("fputwc_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);
+
+  // Writing wide char should fail and set errno to EINVAL
+  EXPECT_EQ(LIBC_NAMESPACE::fputwc(L'a', file), static_cast<wint_t>(WEOF));
+  ASSERT_ERRNO_EQ(EINVAL);
+
+  ASSERT_EQ(LIBC_NAMESPACE::fclose(file), 0);
+}

``````````

</details>


https://github.com/llvm/llvm-project/pull/196158


More information about the libc-commits mailing list