From e721d3d7c8363f141735550add77404efeb65458 Mon Sep 17 00:00:00 2001 From: Connor-GH Date: Fri, 27 Mar 2026 16:15:48 -0500 Subject: [PATCH] fgetws: don't always return NULL on EOF This was a bug that prevented a lot of tests from passing. We only need to return NULL if the file had an error. `fgetws` "stops when end of stream is reached". Also, it only returns an error if "end of stream was *already* reached or if an error occurred" (emphasis mine). Compare this to the musl implementation: https://git.musl-libc.org/cgit/musl/tree/src/stdio/fgetws.c --- src/header/wchar/mod.rs | 56 +++++++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 25 deletions(-) diff --git a/src/header/wchar/mod.rs b/src/header/wchar/mod.rs index 304c9940f8..4ae63172f4 100644 --- a/src/header/wchar/mod.rs +++ b/src/header/wchar/mod.rs @@ -57,9 +57,8 @@ pub unsafe extern "C" fn btowc(c: c_int) -> wint_t { wc as wint_t } -/// See . -#[unsafe(no_mangle)] -pub unsafe extern "C" fn fgetwc(stream: *mut FILE) -> wint_t { +// not in POSIX. +pub unsafe fn fgetwc_unlocked(stream: *mut FILE) -> wint_t { // TODO: Process locale let mut buf: [c_uchar; MB_CUR_MAX as usize] = [0; MB_CUR_MAX as usize]; let mut encoded_length = 0; @@ -67,20 +66,12 @@ pub unsafe extern "C" fn fgetwc(stream: *mut FILE) -> wint_t { let mut wc: wchar_t = 0; loop { - let nread = unsafe { - fread( - buf[bytes_read..bytes_read + 1] - .as_mut_ptr() - .cast::(), - 1, - 1, - stream, - ) - }; - - if nread != 1 { - ERRNO.set(EILSEQ); - return WEOF; + unsafe { + let ret = getc_unlocked(stream); + if ret == EOF { + return WEOF; + } + *buf.as_mut_ptr().add(bytes_read) = ret as c_uchar; } bytes_read += 1; @@ -89,6 +80,9 @@ pub unsafe extern "C" fn fgetwc(stream: *mut FILE) -> wint_t { encoded_length = if let Some(el) = get_char_encoded_length(buf[0]) { el } else { + unsafe { + (*stream).flags |= F_ERR; + } ERRNO.set(EILSEQ); return WEOF; }; @@ -111,24 +105,36 @@ pub unsafe extern "C" fn fgetwc(stream: *mut FILE) -> wint_t { wc as wint_t } +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn fgetwc(stream: *mut FILE) -> wint_t { + let mut stream = unsafe { (*stream).lock() }; + unsafe { fgetwc_unlocked(&raw mut *stream) } +} + /// See . #[unsafe(no_mangle)] pub unsafe extern "C" fn fgetws(ws: *mut wchar_t, n: c_int, stream: *mut FILE) -> *mut wchar_t { - //TODO: lock let mut i = 0; + let mut stream = unsafe { (*stream).lock() }; while ((i + 1) as c_int) < n { - let wc = unsafe { fgetwc(stream) }; + let wc = unsafe { fgetwc_unlocked(&raw mut *stream) }; if wc == WEOF { - return ptr::null_mut(); + break; } unsafe { *ws.add(i) = wc as wchar_t }; i += 1; + if wc as wchar_t == '\n' as wchar_t { + break; + } } - while (i as c_int) < n { - unsafe { *ws.add(i) = 0 }; - i += 1; - } - ws + // NUL-terminate result + unsafe { *ws.add(i) = 0 }; + return if i == 0 || unsafe { ferror(&raw mut *stream) != 0 } { + core::ptr::null_mut() + } else { + ws + }; } /// See .