From 9cc66d49903144b31621a944e2be29674e4789b8 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Thu, 5 Nov 2026 15:14:17 +0000 Subject: [PATCH 1/2] Fix refcount fgets for size 1 and a sticky error flag --- rules/stdio/tgt_refcount.rs | 8 ++++++-- tests/unit/stdio_nofd.c | 22 ++++++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/rules/stdio/tgt_refcount.rs b/rules/stdio/tgt_refcount.rs index eddb40b74b..fa44c4c716 100644 --- a/rules/stdio/tgt_refcount.rs +++ b/rules/stdio/tgt_refcount.rs @@ -99,6 +99,8 @@ fn f17(a0: Ptr, a1: i32, a2: Ptr) -> Ptr { let mut __dst = a0.clone(); let mut __count: usize = 0; let __failed = a2.with_mut(|__f| { + let __prev_err = __f.err; + __f.err = false; while __count < __max { let __c = __f.getc(); if __c < 0 { @@ -111,9 +113,11 @@ fn f17(a0: Ptr, a1: i32, a2: Ptr) -> Ptr { break; } } - __f.err + let __read_err = __f.err; + __f.err |= __prev_err; + __read_err }); - if __failed || __count == 0 { + if __failed || (__max > 0 && __count == 0) { Ptr::null() } else { __dst.write(0); diff --git a/tests/unit/stdio_nofd.c b/tests/unit/stdio_nofd.c index eea3a5f7e0..1b7bbfd4a4 100644 --- a/tests/unit/stdio_nofd.c +++ b/tests/unit/stdio_nofd.c @@ -48,6 +48,27 @@ static void test_fgets_getc(void) { assert(unlink(path) == 0); } +static void test_fgets_size_one_and_error_flag(void) { + const char *path = "cpp2rust_stdio_nofd_gets_flag.tmp"; + FILE *fp = fopen(path, "wb"); + assert(fp != NULL); + assert(fputs("line1\n", fp) >= 0); + assert(fclose(fp) == 0); + fp = fopen(path, "rb"); + assert(fp != NULL); + char buf[8]; + buf[0] = 'x'; + assert(fgets(buf, 1, fp) == buf); + assert(buf[0] == '\0'); + assert(fputs("x", fp) == EOF); + assert(ferror(fp) != 0); + assert(fgets(buf, 8, fp) == buf); + assert(memcmp(buf, "line1\n", 7) == 0); + assert(ferror(fp) != 0); + assert(fclose(fp) == 0); + assert(unlink(path) == 0); +} + static void test_freopen(void) { const char *path = "cpp2rust_stdio_nofd_reopen.tmp"; FILE *fp = fopen(path, "wb"); @@ -112,6 +133,7 @@ int main(void) { test_fputc_fputs(); test_puts(); test_fgets_getc(); + test_fgets_size_one_and_error_flag(); test_freopen(); test_fseeko(); test_rename(); From 484d4c14b708dfa446ee5b4b5cc3809130f6e40e Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Thu, 5 Nov 2026 15:36:28 +0000 Subject: [PATCH 2/2] Update tests --- tests/unit/out/refcount/stdio_nofd.rs | 209 ++++++++++++++++++++++++-- tests/unit/out/unsafe/stdio_nofd.rs | 62 +++++++- 2 files changed, 247 insertions(+), 24 deletions(-) diff --git a/tests/unit/out/refcount/stdio_nofd.rs b/tests/unit/out/refcount/stdio_nofd.rs index a3a1bea5dd..9b545b28af 100644 --- a/tests/unit/out/refcount/stdio_nofd.rs +++ b/tests/unit/out/refcount/stdio_nofd.rs @@ -173,6 +173,8 @@ pub fn test_fgets_getc_2() { let mut __dst = __tmp_0.clone(); let mut __count: usize = 0; let __failed = fp.with_mut(|__f| { + let __prev_err = __f.err; + __f.err = false; while __count < __max { let __c = __f.getc(); if __c < 0 { @@ -185,9 +187,11 @@ pub fn test_fgets_getc_2() { break; } } - __f.err + let __read_err = __f.err; + __f.err |= __prev_err; + __read_err }); - if __failed || __count == 0 { + if __failed || (__max > 0 && __count == 0) { Ptr::null() } else { __dst.write(0); @@ -220,6 +224,8 @@ pub fn test_fgets_getc_2() { let mut __dst = __tmp_1.clone(); let mut __count: usize = 0; let __failed = fp.with_mut(|__f| { + let __prev_err = __f.err; + __f.err = false; while __count < __max { let __c = __f.getc(); if __c < 0 { @@ -232,9 +238,11 @@ pub fn test_fgets_getc_2() { break; } } - __f.err + let __read_err = __f.err; + __f.err |= __prev_err; + __read_err }); - if __failed || __count == 0 { + if __failed || (__max > 0 && __count == 0) { Ptr::null() } else { __dst.write(0); @@ -263,6 +271,8 @@ pub fn test_fgets_getc_2() { let mut __dst = __tmp_2.clone(); let mut __count: usize = 0; let __failed = fp.with_mut(|__f| { + let __prev_err = __f.err; + __f.err = false; while __count < __max { let __c = __f.getc(); if __c < 0 { @@ -275,9 +285,11 @@ pub fn test_fgets_getc_2() { break; } } - __f.err + let __read_err = __f.err; + __f.err |= __prev_err; + __read_err }); - if __failed || __count == 0 { + if __failed || (__max > 0 && __count == 0) { Ptr::null() } else { __dst.write(0); @@ -306,6 +318,8 @@ pub fn test_fgets_getc_2() { let mut __dst = __tmp_3.clone(); let mut __count: usize = 0; let __failed = fp.with_mut(|__f| { + let __prev_err = __f.err; + __f.err = false; while __count < __max { let __c = __f.getc(); if __c < 0 { @@ -318,9 +332,11 @@ pub fn test_fgets_getc_2() { break; } } - __f.err + let __read_err = __f.err; + __f.err |= __prev_err; + __read_err }); - if __failed || __count == 0 { + if __failed || (__max > 0 && __count == 0) { Ptr::null() } else { __dst.write(0); @@ -352,7 +368,167 @@ pub fn test_fgets_getc_2() { != 0) ); } -pub fn test_freopen_3() { +pub fn test_fgets_size_one_and_error_flag_3() { + let mut path: Ptr = Ptr::::from_string_literal(b"cpp2rust_stdio_nofd_gets_flag.tmp"); + let mut fp: Ptr = match CFile::open( + &path.to_rust_string(), + &Ptr::::from_string_literal(b"wb").to_rust_string(), + ) { + Some(__f) => Ptr::alloc(__f), + None => Ptr::null(), + }; + assert!((((!((fp).is_null())) as i32) != 0)); + assert!( + (((match Ptr::::from_string_literal(b"line1\n") + .with_c_bytes(|__bytes| fp.with_mut(|__f| __f.write(__bytes)) == __bytes.len()) + { + true => 0, + false => -1, + } >= 0) as i32) + != 0) + ); + assert!( + ((({ + let __r = fp.with(|__f| __f.close()); + fp.delete(); + __r + } == 0) as i32) + != 0) + ); + fp = match CFile::open( + &path.to_rust_string(), + &Ptr::::from_string_literal(b"rb").to_rust_string(), + ) { + Some(__f) => Ptr::alloc(__f), + None => Ptr::null(), + }; + assert!((((!((fp).is_null())) as i32) != 0)); + let buf: Value> = Rc::new(RefCell::new((0..8).map(|_| 0_i8).collect::>())); + (*buf.borrow_mut())[(0) as usize] = (('x' as i32) as i8); + assert!( + ((({ + match ({ (buf.as_pointer() as Ptr) },) { + (mut __tmp_4,) => { + if 1 <= 0 { + Ptr::null() + } else { + let __max = (1 - 1) as usize; + let mut __dst = __tmp_4.clone(); + let mut __count: usize = 0; + let __failed = fp.with_mut(|__f| { + let __prev_err = __f.err; + __f.err = false; + while __count < __max { + let __c = __f.getc(); + if __c < 0 { + break; + } + __dst.write(__c as i8); + __dst += 1; + __count += 1; + if __c as u8 == b'\n' { + break; + } + } + let __read_err = __f.err; + __f.err |= __prev_err; + __read_err + }); + if __failed || (__max > 0 && __count == 0) { + Ptr::null() + } else { + __dst.write(0); + (__tmp_4).clone() + } + } + } + } + } == { (buf.as_pointer() as Ptr::) }) as i32) + != 0) + ); + assert!(({ (((((*buf.borrow())[(0) as usize] as i32) == ('\0' as i32)) as i32) != 0) })); + assert!( + (((match Ptr::::from_string_literal(b"x") + .with_c_bytes(|__bytes| fp.with_mut(|__f| __f.write(__bytes)) == __bytes.len()) + { + true => 0, + false => -1, + } == (-1_i32)) as i32) + != 0) + ); + assert!((((fp.with(|__f| __f.err) as i32 != 0) as i32) != 0)); + assert!( + ((({ + match ({ (buf.as_pointer() as Ptr) },) { + (mut __tmp_5,) => { + if 8 <= 0 { + Ptr::null() + } else { + let __max = (8 - 1) as usize; + let mut __dst = __tmp_5.clone(); + let mut __count: usize = 0; + let __failed = fp.with_mut(|__f| { + let __prev_err = __f.err; + __f.err = false; + while __count < __max { + let __c = __f.getc(); + if __c < 0 { + break; + } + __dst.write(__c as i8); + __dst += 1; + __count += 1; + if __c as u8 == b'\n' { + break; + } + } + let __read_err = __f.err; + __f.err |= __prev_err; + __read_err + }); + if __failed || (__max > 0 && __count == 0) { + Ptr::null() + } else { + __dst.write(0); + (__tmp_5).clone() + } + } + } + } + } == { (buf.as_pointer() as Ptr::) }) as i32) + != 0) + ); + assert!( + (((((buf.as_pointer() as Ptr::) as Ptr) + .to_any() + .memcmp( + &Ptr::::from_string_literal(b"line1\n").to_any(), + 7_usize + ) + == 0) as i32) + != 0) + ); + assert!((((fp.with(|__f| __f.err) as i32 != 0) as i32) != 0)); + assert!( + ((({ + let __r = fp.with(|__f| __f.close()); + fp.delete(); + __r + } == 0) as i32) + != 0) + ); + assert!( + (((match nix::unistd::unlink(path.to_rust_string().as_str()) { + Ok(()) => 0, + Err(__e) => { + libcc2rs::cpp2rust_errno().write(__e as i32); + -1 + } + } == 0) as i32) + != 0) + ); +} +pub fn test_freopen_4() { let mut path: Ptr = Ptr::::from_string_literal(b"cpp2rust_stdio_nofd_reopen.tmp"); let mut fp: Ptr = match CFile::open( &path.to_rust_string(), @@ -429,7 +605,7 @@ pub fn test_freopen_3() { != 0) ); } -pub fn test_fseeko_4() { +pub fn test_fseeko_5() { let mut path: Ptr = Ptr::::from_string_literal(b"cpp2rust_stdio_nofd_seek.tmp"); let mut fp: Ptr = match CFile::open( &path.to_rust_string(), @@ -525,7 +701,7 @@ pub fn test_fseeko_4() { != 0) ); } -pub fn test_rename_5() { +pub fn test_rename_6() { let mut from: Ptr = Ptr::::from_string_literal(b"cpp2rust_stdio_nofd_from.tmp"); let mut to: Ptr = Ptr::::from_string_literal(b"cpp2rust_stdio_nofd_to.tmp"); let mut fp: Ptr = match CFile::open( @@ -611,7 +787,7 @@ pub fn test_rename_5() { != 0) ); } -pub fn test_setvbuf_6() { +pub fn test_setvbuf_7() { let mut path: Ptr = Ptr::::from_string_literal(b"cpp2rust_stdio_nofd_vbuf.tmp"); let mut fp: Ptr = match CFile::open( &path.to_rust_string(), @@ -658,10 +834,11 @@ fn main_0() -> i32 { ({ test_fputc_fputs_0() }); ({ test_puts_1() }); ({ test_fgets_getc_2() }); - ({ test_freopen_3() }); - ({ test_fseeko_4() }); - ({ test_rename_5() }); - ({ test_setvbuf_6() }); + ({ test_fgets_size_one_and_error_flag_3() }); + ({ test_freopen_4() }); + ({ test_fseeko_5() }); + ({ test_rename_6() }); + ({ test_setvbuf_7() }); return 0; } pub fn __cpp2rust_init_globals() {} diff --git a/tests/unit/out/unsafe/stdio_nofd.rs b/tests/unit/out/unsafe/stdio_nofd.rs index 68f8e163f7..555da998c3 100644 --- a/tests/unit/out/unsafe/stdio_nofd.rs +++ b/tests/unit/out/unsafe/stdio_nofd.rs @@ -192,7 +192,52 @@ pub unsafe fn test_fgets_getc_2() { assert!(((((libc::fclose(fp)) == (0)) as i32) != 0)); assert!(((((libc::unlink(path)) == (0)) as i32) != 0)); } -pub unsafe fn test_freopen_3() { +pub unsafe fn test_fgets_size_one_and_error_flag_3() { + let mut path: *const libc::c_char = + (c"cpp2rust_stdio_nofd_gets_flag.tmp".as_ptr().cast_mut()).cast_const(); + let mut fp: *mut ::libc::FILE = libc::fopen(path, (c"wb".as_ptr().cast_mut()).cast_const()); + assert!((((!((fp).is_null())) as i32) != 0)); + assert!( + ((((libc::fputs((c"line1\n".as_ptr().cast_mut()).cast_const(), fp)) >= (0)) as i32) != 0) + ); + assert!(((((libc::fclose(fp)) == (0)) as i32) != 0)); + fp = libc::fopen(path, (c"rb".as_ptr().cast_mut()).cast_const()); + assert!((((!((fp).is_null())) as i32) != 0)); + let mut buf: [libc::c_char; 8] = [(0 as libc::c_char); 8]; + buf[(0) as usize] = (('x' as i32) as libc::c_char); + assert!(((((libc::fgets(buf.as_mut_ptr(), 1, fp)) == (buf.as_mut_ptr())) as i32) != 0)); + assert!(((((buf[(0) as usize] as i32) == ('\0' as i32)) as i32) != 0)); + assert!( + ((((libc::fputs((c"x".as_ptr().cast_mut()).cast_const(), fp)) == (-1_i32)) as i32) != 0) + ); + assert!(((((libc::ferror(fp)) != (0)) as i32) != 0)); + assert!(((((libc::fgets(buf.as_mut_ptr(), 8, fp)) == (buf.as_mut_ptr())) as i32) != 0)); + assert!( + (((({ + let sa = core::slice::from_raw_parts( + (buf.as_mut_ptr() as *const ::libc::c_void) as *const u8, + 7_usize as usize, + ); + let sb = core::slice::from_raw_parts( + (c"line1\n".as_ptr().cast_mut() as *const ::libc::c_void) as *const u8, + 7_usize as usize, + ); + let mut diff = 0_i32; + for (x, y) in sa.iter().zip(sb.iter()) { + if x != y { + diff = (*x as i32) - (*y as i32); + break; + } + } + diff + }) == (0)) as i32) + != 0) + ); + assert!(((((libc::ferror(fp)) != (0)) as i32) != 0)); + assert!(((((libc::fclose(fp)) == (0)) as i32) != 0)); + assert!(((((libc::unlink(path)) == (0)) as i32) != 0)); +} +pub unsafe fn test_freopen_4() { let mut path: *const libc::c_char = (c"cpp2rust_stdio_nofd_reopen.tmp".as_ptr().cast_mut()).cast_const(); let mut fp: *mut ::libc::FILE = libc::fopen(path, (c"wb".as_ptr().cast_mut()).cast_const()); @@ -246,7 +291,7 @@ pub unsafe fn test_freopen_3() { assert!(((((libc::fclose(fp2)) == (0)) as i32) != 0)); assert!(((((libc::unlink(path)) == (0)) as i32) != 0)); } -pub unsafe fn test_fseeko_4() { +pub unsafe fn test_fseeko_5() { let mut path: *const libc::c_char = (c"cpp2rust_stdio_nofd_seek.tmp".as_ptr().cast_mut()).cast_const(); let mut fp: *mut ::libc::FILE = libc::fopen(path, (c"wb".as_ptr().cast_mut()).cast_const()); @@ -321,7 +366,7 @@ pub unsafe fn test_fseeko_4() { assert!(((((libc::fclose(fp)) == (0)) as i32) != 0)); assert!(((((libc::unlink(path)) == (0)) as i32) != 0)); } -pub unsafe fn test_rename_5() { +pub unsafe fn test_rename_6() { let mut from: *const libc::c_char = (c"cpp2rust_stdio_nofd_from.tmp".as_ptr().cast_mut()).cast_const(); let mut to: *const libc::c_char = @@ -340,7 +385,7 @@ pub unsafe fn test_rename_5() { assert!(((((libc::rename(from, to)) == (-1_i32)) as i32) != 0)); assert!(((((libc::unlink(to)) == (0)) as i32) != 0)); } -pub unsafe fn test_setvbuf_6() { +pub unsafe fn test_setvbuf_7() { let mut path: *const libc::c_char = (c"cpp2rust_stdio_nofd_vbuf.tmp".as_ptr().cast_mut()).cast_const(); let mut fp: *mut ::libc::FILE = libc::fopen(path, (c"wb".as_ptr().cast_mut()).cast_const()); @@ -360,10 +405,11 @@ unsafe fn main_0() -> i32 { (unsafe { test_fputc_fputs_0() }); (unsafe { test_puts_1() }); (unsafe { test_fgets_getc_2() }); - (unsafe { test_freopen_3() }); - (unsafe { test_fseeko_4() }); - (unsafe { test_rename_5() }); - (unsafe { test_setvbuf_6() }); + (unsafe { test_fgets_size_one_and_error_flag_3() }); + (unsafe { test_freopen_4() }); + (unsafe { test_fseeko_5() }); + (unsafe { test_rename_6() }); + (unsafe { test_setvbuf_7() }); return 0; } pub unsafe fn __cpp2rust_init_globals() {}