diff --git a/src/header/stdio/default.rs b/src/header/stdio/default.rs index b86115e2d6..90aa709b02 100644 --- a/src/header/stdio/default.rs +++ b/src/header/stdio/default.rs @@ -21,6 +21,8 @@ impl GlobalFile { writer, pid: None, + + orientation: 0, })) } pub fn get(&self) -> *mut FILE { diff --git a/src/header/stdio/helpers.rs b/src/header/stdio/helpers.rs index 6d5d9da841..be7aa9fa0b 100644 --- a/src/header/stdio/helpers.rs +++ b/src/header/stdio/helpers.rs @@ -76,5 +76,7 @@ pub unsafe fn _fdopen(fd: c_int, mode: *const c_char) -> Option<*mut FILE> { writer, pid: None, + + orientation: 0, }))) } diff --git a/src/header/stdio/mod.rs b/src/header/stdio/mod.rs index 7693cd2157..f6625766dd 100644 --- a/src/header/stdio/mod.rs +++ b/src/header/stdio/mod.rs @@ -9,7 +9,7 @@ use core::{ cmp, ffi::VaList as va_list, fmt::{self, Write as WriteFmt}, - mem, + i32, mem, ops::{Deref, DerefMut}, ptr, slice, str, }; @@ -85,6 +85,9 @@ pub struct FILE { // Optional pid for use with popen/pclose pid: Option, + + // wchar support + pub(crate) orientation: c_int, } impl Read for FILE { @@ -169,6 +172,29 @@ impl FILE { } LockGuard(self) } + + pub fn try_set_orientation(&mut self, mode: c_int) -> c_int { + let stream = self.lock(); + stream.0.try_set_orientation_unlocked(mode) + } + + pub fn try_set_orientation_unlocked(&mut self, mode: c_int) -> c_int { + if self.orientation == 0 { + self.orientation = match mode { + 1..=i32::MAX => 1, + i32::MIN..=-1 => -1, + 0 => self.orientation, + }; + } + self.orientation + } + + pub fn try_set_byte_orientation_unlocked(&mut self) -> core::result::Result<(), c_int> { + match self.try_set_orientation_unlocked(-1) { + i32::MIN..=-1 => Ok(()), + x => Err(x), + } + } } pub struct LockGuard<'a>(&'a mut FILE); @@ -287,6 +313,10 @@ pub unsafe extern "C" fn fflush(stream: *mut FILE) -> c_int { #[no_mangle] pub unsafe extern "C" fn fgetc(stream: *mut FILE) -> c_int { let mut stream = (*stream).lock(); + if let Err(_) = (*stream).try_set_byte_orientation_unlocked() { + return -1; + } + getc_unlocked(&mut *stream) } @@ -309,6 +339,10 @@ pub unsafe extern "C" fn fgets( stream: *mut FILE, ) -> *mut c_char { let mut stream = (*stream).lock(); + if let Err(_) = (*stream).try_set_byte_orientation_unlocked() { + return ptr::null_mut(); + } + let mut out = original; let max = max as usize; let mut left = max.saturating_sub(1); // Make space for the terminating NUL-byte @@ -422,6 +456,10 @@ pub unsafe extern "C" fn fopen(filename: *const c_char, mode: *const c_char) -> #[no_mangle] pub unsafe extern "C" fn fputc(c: c_int, stream: *mut FILE) -> c_int { let mut stream = (*stream).lock(); + if let Err(_) = (*stream).try_set_byte_orientation_unlocked() { + return -1; + } + putc_unlocked(c, &mut *stream) } @@ -429,6 +467,10 @@ pub unsafe extern "C" fn fputc(c: c_int, stream: *mut FILE) -> c_int { #[no_mangle] pub unsafe extern "C" fn fputs(s: *const c_char, stream: *mut FILE) -> c_int { let mut stream = (*stream).lock(); + if let Err(_) = (*stream).try_set_byte_orientation_unlocked() { + return -1; + } + let buf = slice::from_raw_parts(s as *mut u8, strlen(s)); if stream.write_all(&buf).is_ok() { @@ -451,6 +493,10 @@ pub unsafe extern "C" fn fread( } let mut stream = (*stream).lock(); + if let Err(_) = (*stream).try_set_byte_orientation_unlocked() { + return 0; + } + let buf = slice::from_raw_parts_mut(ptr as *mut u8, size as usize * nitems as usize); let mut read = 0; while read < buf.len() { @@ -504,6 +550,7 @@ pub unsafe extern "C" fn freopen( stream.flags = (stream.flags & constants::F_PERM) | new.flags; fclose(new); } + stream.orientation = 0; funlockfile(stream); stream } @@ -600,6 +647,10 @@ pub unsafe extern "C" fn fwrite( return 0; } let mut stream = (*stream).lock(); + if let Err(_) = (*stream).try_set_byte_orientation_unlocked() { + return 0; + } + let buf = slice::from_raw_parts(ptr as *const u8, size as usize * nitems as usize); let mut written = 0; while written < buf.len() { @@ -627,6 +678,10 @@ pub unsafe extern "C" fn getchar() -> c_int { /// Get a char from a stream without locking the stream #[no_mangle] pub unsafe extern "C" fn getc_unlocked(stream: *mut FILE) -> c_int { + if let Err(_) = (*stream).try_set_byte_orientation_unlocked() { + return -1; + } + let mut buf = [0]; match (*stream).read(&mut buf) { @@ -806,6 +861,10 @@ pub unsafe extern "C" fn putchar(c: c_int) -> c_int { /// Put a character `c` into `stream` without locking `stream` #[no_mangle] pub unsafe extern "C" fn putc_unlocked(c: c_int, stream: *mut FILE) -> c_int { + if let Err(_) = (*stream).try_set_byte_orientation_unlocked() { + return -1; + } + match (*stream).write(&[c as u8]) { Ok(0) | Err(_) => EOF, Ok(_) => c, @@ -822,6 +881,10 @@ pub unsafe extern "C" fn putchar_unlocked(c: c_int) -> c_int { #[no_mangle] pub unsafe extern "C" fn puts(s: *const c_char) -> c_int { let mut stream = (&mut *stdout).lock(); + if let Err(_) = (*stream).try_set_byte_orientation_unlocked() { + return -1; + } + let buf = slice::from_raw_parts(s as *mut u8, strlen(s)); if stream.write_all(&buf).is_err() { @@ -1001,6 +1064,10 @@ unsafe extern "C" fn tmpnam_inner(buf: *mut c_char, offset: usize) -> *mut c_cha #[no_mangle] pub unsafe extern "C" fn ungetc(c: c_int, stream: *mut FILE) -> c_int { let mut stream = (*stream).lock(); + if let Err(_) = (*stream).try_set_byte_orientation_unlocked() { + return -1; + } + stream.unget.push(c as u8); c } @@ -1008,6 +1075,10 @@ pub unsafe extern "C" fn ungetc(c: c_int, stream: *mut FILE) -> c_int { #[no_mangle] pub unsafe extern "C" fn vfprintf(file: *mut FILE, format: *const c_char, ap: va_list) -> c_int { let mut file = (*file).lock(); + if let Err(_) = file.try_set_byte_orientation_unlocked() { + return -1; + } + printf::printf(&mut *file, format, ap) } @@ -1053,6 +1124,10 @@ pub unsafe extern "C" fn vsprintf(s: *mut c_char, format: *const c_char, ap: va_ pub unsafe extern "C" fn vfscanf(file: *mut FILE, format: *const c_char, ap: va_list) -> c_int { let ret = { let mut file = (*file).lock(); + if let Err(_) = file.try_set_byte_orientation_unlocked() { + return -1; + } + let f: &mut FILE = &mut *file; let reader: LookAheadReader = f.into(); scanf::scanf(reader, format, ap) diff --git a/src/header/wchar/mod.rs b/src/header/wchar/mod.rs index 4c4a281bbd..cde6a5e85b 100644 --- a/src/header/wchar/mod.rs +++ b/src/header/wchar/mod.rs @@ -89,9 +89,9 @@ pub unsafe extern "C" fn fputws(ws: *const wchar_t, stream: *mut FILE) -> c_int } } -// #[no_mangle] -pub extern "C" fn fwide(stream: *mut FILE, mode: c_int) -> c_int { - unimplemented!(); +#[no_mangle] +pub unsafe extern "C" fn fwide(stream: *mut FILE, mode: c_int) -> c_int { + (*stream).try_set_orientation(mode) } #[no_mangle] diff --git a/src/platform/types.rs b/src/platform/types.rs index 85e3ba3aaf..5d604d383a 100644 --- a/src/platform/types.rs +++ b/src/platform/types.rs @@ -1,3 +1,5 @@ +use core::i32; + // Use repr(u8) as LLVM expects `void*` to be the same as `i8*` to help enable // more optimization opportunities around it recognizing things like // malloc/free. diff --git a/tests/Makefile b/tests/Makefile index fde2681030..a1f07bb9a2 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -100,6 +100,7 @@ EXPECT_NAMES=\ unistd/swab \ unistd/write \ waitpid \ + wchar/fwide \ wchar/mbrtowc \ wchar/mbsrtowcs \ wchar/printf-on-wchars \ diff --git a/tests/expected/stdio/freopen.stdout b/tests/expected/stdio/freopen.stdout index e965047ad7..1dd338fd1a 100644 --- a/tests/expected/stdio/freopen.stdout +++ b/tests/expected/stdio/freopen.stdout @@ -1 +1,3 @@ Hello +0 +0 diff --git a/tests/expected/wchar/fwide.stderr b/tests/expected/wchar/fwide.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/wchar/fwide.stdout b/tests/expected/wchar/fwide.stdout new file mode 100644 index 0000000000..44e0be8e35 --- /dev/null +++ b/tests/expected/wchar/fwide.stdout @@ -0,0 +1,4 @@ +0 +0 +0 +0 diff --git a/tests/stdio/freopen.c b/tests/stdio/freopen.c index 6c658435f6..3fb6069cbf 100644 --- a/tests/stdio/freopen.c +++ b/tests/stdio/freopen.c @@ -1,12 +1,38 @@ +#include #include +#include #include "test_helpers.h" -int main(void) { +int test_reopen_opens_file(void) { FILE *f = freopen("stdio/stdio.in", "r", stdin); ERROR_IF(freopen, f, == NULL); char in[6]; fgets(in, 6, stdin); printf("%s\n", in); // should print Hello + fclose(f); + return 0; +} + +int test_reopen_resets_orientation(void) { + FILE *f = freopen("stdio/stdio.in", "r", stdin); + assert(fwide(f, 0) == 0); + assert(fwide(f, -1) == -1); + + f = freopen("stdio/stdio.in", "r", stdin); + assert(fwide(f, 0) == 0); + + fclose(f); + return 0; +} + +int main(void) { + int(*tests[])(void) = { + &test_reopen_opens_file, + &test_reopen_resets_orientation, + }; + for(int i=0; i +#include +#include + +int test_initial_orientation(void) { + FILE *f = tmpfile(); + assert(fwide(f, 0) == 0); + return 0; +} + +int test_manual_byte_orientation(void) { + FILE *f = tmpfile(); + + // set manually to byte orientation + assert(fwide(f, -483) == -1); + + // Cannot change to wchar orientation + assert(fwide(f, 1) == -1); + + fclose(f); + return 0; +} + +int test_manual_wchar_orientation(void) { + FILE *f = tmpfile(); + + // set manually to wchar orientation + assert(fwide(f, 483) == 1); + + // Cannot change to byte orientation + assert(fwide(f, -1) == 1); + + fclose(f); + return 0; +} + +int test_orientation_after_fprintf(void) { + // open file and write bytes; implicitly setting the bytes orientation + FILE *f = tmpfile(); + assert(fprintf(f, "blah\n") == 5); + + // Check that bytes orientation is set + assert(fwide(f, 0) == -1); + + fclose(f); + return 0; +} + +int main() { + int(*tests[])(void) = { + &test_initial_orientation, + &test_manual_byte_orientation, + &test_manual_wchar_orientation, + &test_orientation_after_fprintf, + }; + for(int i=0; i