[libc-commits] [libc] [libc] implement fgetws (PR #196161)
Michael Jones via libc-commits
libc-commits at lists.llvm.org
Mon May 18 15:49:50 PDT 2026
https://github.com/michaelrj-google updated https://github.com/llvm/llvm-project/pull/196161
>From b89fc8aaee918730e6da9be93e6d69826621010f Mon Sep 17 00:00:00 2001
From: Michael Jones <michaelrj at google.com>
Date: Wed, 6 May 2026 18:27:43 +0000
Subject: [PATCH 1/5] [libc] implement fgetws
Add fgetws function and tests. Part 5/11. All build file changes are in
part 11.
Assisted by Gemini
---
libc/src/stdio/generic/fgets.cpp | 2 +
libc/src/wchar/fgetws.cpp | 63 ++++++++++
libc/src/wchar/fgetws.h | 21 ++++
libc/test/src/wchar/fgetws_test.cpp | 181 ++++++++++++++++++++++++++++
4 files changed, 267 insertions(+)
create mode 100644 libc/src/wchar/fgetws.cpp
create mode 100644 libc/src/wchar/fgetws.h
create mode 100644 libc/test/src/wchar/fgetws_test.cpp
diff --git a/libc/src/stdio/generic/fgets.cpp b/libc/src/stdio/generic/fgets.cpp
index 33d469c620ca2..e3de77b5dc281 100644
--- a/libc/src/stdio/generic/fgets.cpp
+++ b/libc/src/stdio/generic/fgets.cpp
@@ -29,6 +29,8 @@ LLVM_LIBC_FUNCTION(char *, fgets,
// i is an int because it's frequently compared to count, which is also int.
int i = 0;
+ // TODO: rewrite this logic. If there's an error it should return nullptr. See
+ // fgetws for example.
for (; i < (count - 1) && c != '\n'; ++i) {
auto result = stream->read_unlocked(&c, 1);
size_t r = result.value;
diff --git a/libc/src/wchar/fgetws.cpp b/libc/src/wchar/fgetws.cpp
new file mode 100644
index 0000000000000..97031f9af144f
--- /dev/null
+++ b/libc/src/wchar/fgetws.cpp
@@ -0,0 +1,63 @@
+//===-- Implementation of fgetws ------------------------------------------===//
+//
+// 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/fgetws.h"
+#include "hdr/types/FILE.h"
+#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(wchar_t *, fgetws,
+ (wchar_t *__restrict ws, int count,
+ ::FILE *__restrict stream)) {
+ if (count <= 0)
+ return nullptr;
+
+ LIBC_CRASH_ON_NULLPTR(ws);
+ LIBC_CRASH_ON_NULLPTR(stream);
+
+ auto *f = reinterpret_cast<File *>(stream);
+
+ if (count == 1) {
+ ws[0] = L'\0';
+ return ws;
+ }
+
+ f->lock();
+
+ wchar_t *result = ws;
+ int chars_read = 0;
+ wchar_t c = L'\0';
+
+ for (; chars_read < count - 1 && c != '\n'; ++chars_read) {
+ auto read_res = f->read_unlocked(&c, 1);
+ if (read_res.has_error()) {
+ libc_errno = read_res.error;
+ result = nullptr;
+ break;
+ }
+ if (read_res.value < 1) {
+ if (chars_read == 0)
+ result = nullptr;
+ break;
+ }
+ ws[chars_read] = c;
+ }
+
+ if (result != nullptr)
+ ws[chars_read] = L'\0';
+
+ f->unlock();
+ return result;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/wchar/fgetws.h b/libc/src/wchar/fgetws.h
new file mode 100644
index 0000000000000..5fe9572ae25f7
--- /dev/null
+++ b/libc/src/wchar/fgetws.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for fgetws ----------------------------------===//
+//
+// 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_FGETWS_H
+#define LLVM_LIBC_SRC_WCHAR_FGETWS_H
+
+#include "hdr/types/FILE.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+wchar_t *fgetws(wchar_t *__restrict str, int count, ::FILE *__restrict stream);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_WCHAR_FGETWS_H
diff --git a/libc/test/src/wchar/fgetws_test.cpp b/libc/test/src/wchar/fgetws_test.cpp
new file mode 100644
index 0000000000000..5bd6cd7dc9d98
--- /dev/null
+++ b/libc/test/src/wchar/fgetws_test.cpp
@@ -0,0 +1,181 @@
+//===-- Unittests for fgetws ----------------------------------------------===//
+//
+// 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/types/wint_t.h"
+#include "src/stdio/fclose.h"
+#include "src/stdio/ferror.h"
+#include "src/stdio/fopen.h"
+#include "src/stdio/fwrite.h"
+#include "src/wchar/fgetws.h"
+#include "src/wchar/fwide.h"
+#include "src/wchar/wcscmp.h"
+#include "test/UnitTest/ErrnoCheckingTest.h"
+#include "test/UnitTest/Test.h"
+
+using LlvmLibcFgetwsTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
+
+// TODO: Refactor these tests to use standard wide-character string comparison
+// assert macros (e.g., EXPECT_STREQ for wchar_t) once they are supported
+// natively by the LLVM-libc test framework, instead of calling wcscmp.
+TEST_F(LlvmLibcFgetwsTest, ReadWideString) {
+ auto FILENAME =
+ libc_make_test_file_path(APPEND_LIBC_TEST("fgetws_string.test"));
+ ::FILE *file = LIBC_NAMESPACE::fopen(FILENAME, "w");
+ ASSERT_FALSE(file == nullptr);
+
+ // Write UTF-8 bytes for: "Hello, ¢€𐍈 world!\n"
+ constexpr unsigned char CONTENT[] = {
+ 'H', 'e', 'l', 'l', 'o', ',', ' ', 0xC2, 0xA2, 0xE2, 0x82, 0xAC,
+ 0xF0, 0x90, 0x8D, 0x88, ' ', 'w', 'o', 'r', 'l', 'd', '!', '\n'};
+ 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);
+
+ wchar_t buffer[50] = {0};
+ wchar_t *result = LIBC_NAMESPACE::fgetws(buffer, 50, file);
+ ASSERT_FALSE(result == nullptr);
+ EXPECT_EQ(result, buffer);
+ EXPECT_EQ(LIBC_NAMESPACE::wcscmp(buffer, L"Hello, ¢€𐍈 world!\n"), 0);
+
+ ASSERT_EQ(LIBC_NAMESPACE::fclose(file), 0);
+}
+
+TEST_F(LlvmLibcFgetwsTest, ReadBounded) {
+ auto FILENAME =
+ libc_make_test_file_path(APPEND_LIBC_TEST("fgetws_bounded.test"));
+ ::FILE *file = LIBC_NAMESPACE::fopen(FILENAME, "w");
+ ASSERT_FALSE(file == nullptr);
+
+ // Write "1234567890"
+ constexpr char CONTENT[] = "1234567890";
+ 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);
+
+ wchar_t buffer[10] = {0};
+ // Read bounded by count = 5 (4 chars + null terminator)
+ wchar_t *result = LIBC_NAMESPACE::fgetws(buffer, 5, file);
+ ASSERT_FALSE(result == nullptr);
+ EXPECT_EQ(result, buffer);
+ EXPECT_EQ(LIBC_NAMESPACE::wcscmp(buffer, L"1234"), 0);
+
+ // Read bounded by count = 1 (writes only null terminator)
+ wchar_t buffer_one[5] = {L'x', L'y', L'z', L'\0'};
+ wchar_t *result_one = LIBC_NAMESPACE::fgetws(buffer_one, 1, file);
+ ASSERT_FALSE(result_one == nullptr);
+ EXPECT_EQ(result_one, buffer_one);
+ EXPECT_EQ(static_cast<wint_t>(buffer_one[0]), static_cast<wint_t>(L'\0'));
+ EXPECT_EQ(static_cast<wint_t>(buffer_one[1]),
+ static_cast<wint_t>(L'y')); // untouched
+
+ ASSERT_EQ(LIBC_NAMESPACE::fclose(file), 0);
+}
+
+TEST_F(LlvmLibcFgetwsTest, NewlineStops) {
+ auto FILENAME =
+ libc_make_test_file_path(APPEND_LIBC_TEST("fgetws_newline.test"));
+ ::FILE *file = LIBC_NAMESPACE::fopen(FILENAME, "w");
+ ASSERT_FALSE(file == nullptr);
+
+ // Write "abc\ndef"
+ constexpr char CONTENT[] = "abc\ndef";
+ 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);
+
+ wchar_t buffer[20] = {0};
+ wchar_t *result = LIBC_NAMESPACE::fgetws(buffer, 10, file);
+ ASSERT_FALSE(result == nullptr);
+ EXPECT_EQ(result, buffer);
+ EXPECT_EQ(LIBC_NAMESPACE::wcscmp(buffer, L"abc\n"), 0);
+
+ ASSERT_EQ(LIBC_NAMESPACE::fclose(file), 0);
+}
+
+TEST_F(LlvmLibcFgetwsTest, InvalidStream) {
+ auto FILENAME =
+ libc_make_test_file_path(APPEND_LIBC_TEST("fgetws_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 write-only mode
+ file = LIBC_NAMESPACE::fopen(FILENAME, "w");
+ ASSERT_FALSE(file == nullptr);
+
+ // Try to read from write-only stream
+ wchar_t buffer[10] = {0};
+ ASSERT_ERRNO_SUCCESS();
+ EXPECT_EQ(LIBC_NAMESPACE::fgetws(buffer, 5, file),
+ static_cast<wchar_t *>(nullptr));
+ ASSERT_ERRNO_EQ(EBADF);
+ EXPECT_NE(LIBC_NAMESPACE::ferror(file), 0);
+
+ ASSERT_EQ(LIBC_NAMESPACE::fclose(file), 0);
+}
+
+TEST_F(LlvmLibcFgetwsTest, EncodingErrorEILSEQ) {
+ auto FILENAME =
+ libc_make_test_file_path(APPEND_LIBC_TEST("fgetws_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
+ wchar_t buffer[10] = {0};
+ ASSERT_ERRNO_SUCCESS();
+ EXPECT_EQ(LIBC_NAMESPACE::fgetws(buffer, 5, file),
+ static_cast<wchar_t *>(nullptr));
+ ASSERT_ERRNO_EQ(EILSEQ);
+ EXPECT_NE(LIBC_NAMESPACE::ferror(file), 0);
+
+ ASSERT_EQ(LIBC_NAMESPACE::fclose(file), 0);
+}
+
+TEST_F(LlvmLibcFgetwsTest, ByteModeFailure) {
+ auto FILENAME =
+ libc_make_test_file_path(APPEND_LIBC_TEST("fgetws_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 string should fail and set errno to EINVAL
+ wchar_t buffer[10] = {0};
+ EXPECT_EQ(LIBC_NAMESPACE::fgetws(buffer, 5, file),
+ static_cast<wchar_t *>(nullptr));
+ ASSERT_ERRNO_EQ(EINVAL);
+ EXPECT_NE(LIBC_NAMESPACE::ferror(file), 0);
+
+ ASSERT_EQ(LIBC_NAMESPACE::fclose(file), 0);
+}
>From 68e2f0ca701dd01935b5a013b3739b98cf9074e3 Mon Sep 17 00:00:00 2001
From: Michael Jones <michaelrj at google.com>
Date: Wed, 13 May 2026 17:07:54 +0000
Subject: [PATCH 2/5] address comments and update entrypoints
---
libc/config/linux/aarch64/entrypoints.txt | 2 +-
libc/config/linux/riscv/entrypoints.txt | 2 +-
libc/config/linux/x86_64/entrypoints.txt | 2 +-
libc/src/wchar/fgetws.cpp | 8 +++++++-
libc/src/wchar/fgetws.h | 8 +++++++-
5 files changed, 17 insertions(+), 5 deletions(-)
diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index c7458ef36c941..5faf0447173a8 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -1256,7 +1256,7 @@ if(LLVM_LIBC_FULL_BUILD)
# wchar.h entrypoints
libc.src.wchar.fgetwc
- # libc.src.wchar.fgetws
+ libc.src.wchar.fgetws
# libc.src.wchar.fputwc
# libc.src.wchar.fputws
libc.src.wchar.fwide
diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt
index e3336420cbbcc..0c387c2465116 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -1390,7 +1390,7 @@ if(LLVM_LIBC_FULL_BUILD)
# wchar.h entrypoints
libc.src.wchar.fgetwc
- # libc.src.wchar.fgetws
+ libc.src.wchar.fgetws
# libc.src.wchar.fputwc
# libc.src.wchar.fputws
libc.src.wchar.fwide
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 3c3b0e835429f..f1202d27facf1 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -1472,7 +1472,7 @@ if(LLVM_LIBC_FULL_BUILD)
libc.src.wchar.wcsrtombs
libc.src.wchar.wcsnrtombs
libc.src.wchar.fgetwc
- # libc.src.wchar.fgetws
+ libc.src.wchar.fgetws
# libc.src.wchar.fputwc
# libc.src.wchar.fputws
libc.src.wchar.fwide
diff --git a/libc/src/wchar/fgetws.cpp b/libc/src/wchar/fgetws.cpp
index 97031f9af144f..cdabcc981d202 100644
--- a/libc/src/wchar/fgetws.cpp
+++ b/libc/src/wchar/fgetws.cpp
@@ -1,10 +1,16 @@
-//===-- Implementation of fgetws ------------------------------------------===//
+//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file contains the implementation of the fgetws function, which reads a
+/// wide-character string from a file.
+///
+//===----------------------------------------------------------------------===//
#include "src/wchar/fgetws.h"
#include "hdr/types/FILE.h"
diff --git a/libc/src/wchar/fgetws.h b/libc/src/wchar/fgetws.h
index 5fe9572ae25f7..9c0cb37321786 100644
--- a/libc/src/wchar/fgetws.h
+++ b/libc/src/wchar/fgetws.h
@@ -1,10 +1,16 @@
-//===-- Implementation header for fgetws ----------------------------------===//
+//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file contains the implementation header for the fgetws function, which
+/// reads a wide-character string from a file.
+///
+//===----------------------------------------------------------------------===//
#ifndef LLVM_LIBC_SRC_WCHAR_FGETWS_H
#define LLVM_LIBC_SRC_WCHAR_FGETWS_H
>From 1bcfa944d36f8c7ff811a51a2fb3f9311dffa302 Mon Sep 17 00:00:00 2001
From: Michael Jones <michaelrj at google.com>
Date: Wed, 13 May 2026 21:13:03 +0000
Subject: [PATCH 3/5] address comments, fixup includes, add guard to test file
---
libc/src/wchar/fgetws.cpp | 3 +--
libc/src/wchar/fgetws.h | 4 ++--
libc/test/src/wchar/CMakeLists.txt | 4 +++-
libc/test/src/wchar/fgetws_test.cpp | 12 +++++++++++-
4 files changed, 17 insertions(+), 6 deletions(-)
diff --git a/libc/src/wchar/fgetws.cpp b/libc/src/wchar/fgetws.cpp
index cdabcc981d202..8ec4a2023e64c 100644
--- a/libc/src/wchar/fgetws.cpp
+++ b/libc/src/wchar/fgetws.cpp
@@ -42,9 +42,8 @@ LLVM_LIBC_FUNCTION(wchar_t *, fgetws,
wchar_t *result = ws;
int chars_read = 0;
- wchar_t c = L'\0';
- for (; chars_read < count - 1 && c != '\n'; ++chars_read) {
+ for (wchar_t c = L'\0'; chars_read < count - 1 && c != '\n'; ++chars_read) {
auto read_res = f->read_unlocked(&c, 1);
if (read_res.has_error()) {
libc_errno = read_res.error;
diff --git a/libc/src/wchar/fgetws.h b/libc/src/wchar/fgetws.h
index 9c0cb37321786..86faea0395441 100644
--- a/libc/src/wchar/fgetws.h
+++ b/libc/src/wchar/fgetws.h
@@ -7,8 +7,8 @@
//===----------------------------------------------------------------------===//
///
/// \file
-/// This file contains the implementation header for the fgetws function, which
-/// reads a wide-character string from a file.
+/// This file contains the prototype for the fgetws function, which reads a
+/// wide-character string from a file.
///
//===----------------------------------------------------------------------===//
diff --git a/libc/test/src/wchar/CMakeLists.txt b/libc/test/src/wchar/CMakeLists.txt
index 55b9f32e8226b..6c647bf867000 100644
--- a/libc/test/src/wchar/CMakeLists.txt
+++ b/libc/test/src/wchar/CMakeLists.txt
@@ -613,7 +613,9 @@ add_libc_test(
SRCS
fgetws_test.cpp
DEPENDS
- libc.include.stdio
+ libc.hdr.errno_macros
+ libc.hdr.stdint_proxy
+ libc.hdr.types.wint_t
libc.src.stdio.fopen
libc.src.stdio.fclose
libc.src.stdio.fwrite
diff --git a/libc/test/src/wchar/fgetws_test.cpp b/libc/test/src/wchar/fgetws_test.cpp
index 5bd6cd7dc9d98..8b01fb566c171 100644
--- a/libc/test/src/wchar/fgetws_test.cpp
+++ b/libc/test/src/wchar/fgetws_test.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "hdr/errno_macros.h"
+#include "hdr/stdint_proxy.h"
#include "hdr/types/wint_t.h"
#include "src/stdio/fclose.h"
#include "src/stdio/ferror.h"
@@ -29,10 +30,19 @@ TEST_F(LlvmLibcFgetwsTest, ReadWideString) {
::FILE *file = LIBC_NAMESPACE::fopen(FILENAME, "w");
ASSERT_FALSE(file == nullptr);
+#if WCHAR_MAX > 0xFFFF
// Write UTF-8 bytes for: "Hello, ¢€𐍈 world!\n"
constexpr unsigned char CONTENT[] = {
'H', 'e', 'l', 'l', 'o', ',', ' ', 0xC2, 0xA2, 0xE2, 0x82, 0xAC,
0xF0, 0x90, 0x8D, 0x88, ' ', 'w', 'o', 'r', 'l', 'd', '!', '\n'};
+ constexpr const wchar_t *EXPECTED_STR = L"Hello, ¢€𐍈 world!\n";
+#else
+ // Write UTF-8 bytes for: "Hello, ¢ world!\n"
+ constexpr unsigned char CONTENT[] = {'H', 'e', 'l', 'l', 'o', ',',
+ ' ', 0xC2, 0xA2, ' ', 'w', 'o',
+ 'r', 'l', 'd', '!', '\n'};
+ constexpr const wchar_t *EXPECTED_STR = L"Hello, ¢ world!\n";
+#endif
ASSERT_EQ(LIBC_NAMESPACE::fwrite(CONTENT, 1, sizeof(CONTENT), file),
sizeof(CONTENT));
ASSERT_EQ(LIBC_NAMESPACE::fclose(file), 0);
@@ -45,7 +55,7 @@ TEST_F(LlvmLibcFgetwsTest, ReadWideString) {
wchar_t *result = LIBC_NAMESPACE::fgetws(buffer, 50, file);
ASSERT_FALSE(result == nullptr);
EXPECT_EQ(result, buffer);
- EXPECT_EQ(LIBC_NAMESPACE::wcscmp(buffer, L"Hello, ¢€𐍈 world!\n"), 0);
+ EXPECT_EQ(LIBC_NAMESPACE::wcscmp(buffer, EXPECTED_STR), 0);
ASSERT_EQ(LIBC_NAMESPACE::fclose(file), 0);
}
>From 36fa93cb09d79a10956c31abf78ba7118cf315cb Mon Sep 17 00:00:00 2001
From: Michael Jones <michaelrj at google.com>
Date: Fri, 15 May 2026 21:31:38 +0000
Subject: [PATCH 4/5] fix header and const FILENAME
---
libc/test/src/wchar/fgetws_test.cpp | 19 ++++++++++++-------
1 file changed, 12 insertions(+), 7 deletions(-)
diff --git a/libc/test/src/wchar/fgetws_test.cpp b/libc/test/src/wchar/fgetws_test.cpp
index 8b01fb566c171..4a940c7281b6e 100644
--- a/libc/test/src/wchar/fgetws_test.cpp
+++ b/libc/test/src/wchar/fgetws_test.cpp
@@ -1,10 +1,15 @@
-//===-- Unittests for fgetws ----------------------------------------------===//
+//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Unittests for fgetws
+///
+//===----------------------------------------------------------------------===//
#include "hdr/errno_macros.h"
#include "hdr/stdint_proxy.h"
@@ -25,7 +30,7 @@ using LlvmLibcFgetwsTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
// assert macros (e.g., EXPECT_STREQ for wchar_t) once they are supported
// natively by the LLVM-libc test framework, instead of calling wcscmp.
TEST_F(LlvmLibcFgetwsTest, ReadWideString) {
- auto FILENAME =
+ const auto FILENAME =
libc_make_test_file_path(APPEND_LIBC_TEST("fgetws_string.test"));
::FILE *file = LIBC_NAMESPACE::fopen(FILENAME, "w");
ASSERT_FALSE(file == nullptr);
@@ -61,7 +66,7 @@ TEST_F(LlvmLibcFgetwsTest, ReadWideString) {
}
TEST_F(LlvmLibcFgetwsTest, ReadBounded) {
- auto FILENAME =
+ const auto FILENAME =
libc_make_test_file_path(APPEND_LIBC_TEST("fgetws_bounded.test"));
::FILE *file = LIBC_NAMESPACE::fopen(FILENAME, "w");
ASSERT_FALSE(file == nullptr);
@@ -96,7 +101,7 @@ TEST_F(LlvmLibcFgetwsTest, ReadBounded) {
}
TEST_F(LlvmLibcFgetwsTest, NewlineStops) {
- auto FILENAME =
+ const auto FILENAME =
libc_make_test_file_path(APPEND_LIBC_TEST("fgetws_newline.test"));
::FILE *file = LIBC_NAMESPACE::fopen(FILENAME, "w");
ASSERT_FALSE(file == nullptr);
@@ -121,7 +126,7 @@ TEST_F(LlvmLibcFgetwsTest, NewlineStops) {
}
TEST_F(LlvmLibcFgetwsTest, InvalidStream) {
- auto FILENAME =
+ const auto FILENAME =
libc_make_test_file_path(APPEND_LIBC_TEST("fgetws_invalid.test"));
// Create the file first
@@ -145,7 +150,7 @@ TEST_F(LlvmLibcFgetwsTest, InvalidStream) {
}
TEST_F(LlvmLibcFgetwsTest, EncodingErrorEILSEQ) {
- auto FILENAME =
+ const auto FILENAME =
libc_make_test_file_path(APPEND_LIBC_TEST("fgetws_eilseq.test"));
::FILE *file = LIBC_NAMESPACE::fopen(FILENAME, "w");
ASSERT_FALSE(file == nullptr);
@@ -172,7 +177,7 @@ TEST_F(LlvmLibcFgetwsTest, EncodingErrorEILSEQ) {
}
TEST_F(LlvmLibcFgetwsTest, ByteModeFailure) {
- auto FILENAME =
+ const auto FILENAME =
libc_make_test_file_path(APPEND_LIBC_TEST("fgetws_bytemode.test"));
::FILE *file = LIBC_NAMESPACE::fopen(FILENAME, "w+");
ASSERT_FALSE(file == nullptr);
>From a9ff0cb4a1c2a945d5d32f69d2e77cd3ad131a7e Mon Sep 17 00:00:00 2001
From: Michael Jones <michaelrj at google.com>
Date: Mon, 18 May 2026 22:48:14 +0000
Subject: [PATCH 5/5] fix '\n' as wchar
---
libc/src/wchar/fgetws.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libc/src/wchar/fgetws.cpp b/libc/src/wchar/fgetws.cpp
index 8ec4a2023e64c..b84196bdad63f 100644
--- a/libc/src/wchar/fgetws.cpp
+++ b/libc/src/wchar/fgetws.cpp
@@ -43,7 +43,7 @@ LLVM_LIBC_FUNCTION(wchar_t *, fgetws,
wchar_t *result = ws;
int chars_read = 0;
- for (wchar_t c = L'\0'; chars_read < count - 1 && c != '\n'; ++chars_read) {
+ for (wchar_t c = L'\0'; chars_read < count - 1 && c != L'\n'; ++chars_read) {
auto read_res = f->read_unlocked(&c, 1);
if (read_res.has_error()) {
libc_errno = read_res.error;
More information about the libc-commits
mailing list