From aa8b14e10797d732f2170b2704c7f5270d42f2ca Mon Sep 17 00:00:00 2001 From: Tom Almeida Date: Thu, 15 Mar 2018 15:21:02 +0800 Subject: [PATCH 01/16] Added some constants in linux for file modes --- src/fcntl/src/lib.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/fcntl/src/lib.rs b/src/fcntl/src/lib.rs index 0e5048277a..dc29bb99b4 100644 --- a/src/fcntl/src/lib.rs +++ b/src/fcntl/src/lib.rs @@ -18,6 +18,12 @@ pub const O_CREAT: c_int = 0x0040; pub const O_TRUNC: c_int = 0x0200; #[cfg(target_os = "linux")] pub const O_ACCMODE: c_int = O_RDONLY | O_WRONLY | O_RDWR; +#[cfg(target_os = "linux")] +pub const O_APPEND: c_int = 0o2000; +#[cfg(target_os = "linux")] +pub const O_CLOEXEC: c_int = 0o2_000_000; +#[cfg(target_os = "linux")] +pub const O_EXCL: c_int = 0o200; #[cfg(target_os = "redox")] pub const O_RDONLY: c_int = 0x0001_0000; From f20878c5922972f50e17811354b71603826f120b Mon Sep 17 00:00:00 2001 From: Tom Almeida Date: Thu, 15 Mar 2018 15:21:52 +0800 Subject: [PATCH 02/16] Added lseek to syscalls --- src/platform/src/linux/mod.rs | 4 ++++ src/platform/src/redox/mod.rs | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/platform/src/linux/mod.rs b/src/platform/src/linux/mod.rs index 8915e6fea7..6868ac3af2 100644 --- a/src/platform/src/linux/mod.rs +++ b/src/platform/src/linux/mod.rs @@ -119,6 +119,10 @@ pub fn link(path1: *const c_char, path2: *const c_char) -> c_int { e(unsafe { syscall!(LINKAT, AT_FDCWD, path1, AT_FDCWD, path2, 0) }) as c_int } +pub fn lseek(fildes: c_int, offset: off_t, whence: c_int) -> off_t { + e(unsafe { syscall!(LSEEK, fildes, offset, whence) }) as off_t +} + pub fn mkdir(path: *const c_char, mode: mode_t) -> c_int { e(unsafe { syscall!(MKDIRAT, AT_FDCWD, path, mode) }) as c_int } diff --git a/src/platform/src/redox/mod.rs b/src/platform/src/redox/mod.rs index e663a1b2da..869e52b42e 100644 --- a/src/platform/src/redox/mod.rs +++ b/src/platform/src/redox/mod.rs @@ -125,6 +125,10 @@ pub fn link(path1: *const c_char, path2: *const c_char) -> c_int { e(unsafe { syscall::link(path1.as_ptr(), path2.as_ptr()) }) as c_int } +pub fn lseek(fd: c_int, offset: isize, whence: usize) -> c_int { + e(syscall::lseek(fd as usize, offset, whence)) as c_int +} + pub fn mkdir(path: *const c_char, mode: mode_t) -> c_int { let flags = O_CREAT | O_EXCL | O_CLOEXEC | O_DIRECTORY | mode as usize & 0o777; let path = unsafe { c_str(path) }; From e73678d8adb134b93064ceb81ac887815d6d093d Mon Sep 17 00:00:00 2001 From: Tom Almeida Date: Thu, 15 Mar 2018 15:22:18 +0800 Subject: [PATCH 03/16] Added a FileReader struct --- src/platform/src/lib.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/platform/src/lib.rs b/src/platform/src/lib.rs index 5f57298db7..a9b6d25c46 100644 --- a/src/platform/src/lib.rs +++ b/src/platform/src/lib.rs @@ -57,8 +57,8 @@ pub unsafe fn c_str_n(s: *const c_char, n: usize) -> &'static [u8] { pub struct FileWriter(pub c_int); impl FileWriter { - pub fn write(&mut self, buf: &[u8]) { - write(self.0, buf); + pub fn write(&mut self, buf: &[u8]) -> isize { + write(self.0, buf) } } @@ -69,6 +69,14 @@ impl fmt::Write for FileWriter { } } +pub struct FileReader(pub c_int); + +impl FileReader { + pub fn read(&mut self, buf: &mut [u8]) -> isize { + read(self.0, buf) + } +} + pub struct StringWriter(pub *mut u8, pub usize); impl StringWriter { From 046ce1468ef803b0ccbb796431528da7281344ad Mon Sep 17 00:00:00 2001 From: Tom Almeida Date: Thu, 15 Mar 2018 15:23:38 +0800 Subject: [PATCH 04/16] Removed an unused import from printf --- src/stdio/src/printf.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stdio/src/printf.rs b/src/stdio/src/printf.rs index 093ee82c12..fee1c1156d 100644 --- a/src/stdio/src/printf.rs +++ b/src/stdio/src/printf.rs @@ -1,4 +1,4 @@ -use core::{fmt, mem, slice, str}; +use core::{fmt, slice, str}; use platform::types::*; use vl::VaList; From 7f2b720962f9fbabdfda651af4fa59c914021f20 Mon Sep 17 00:00:00 2001 From: Tom Almeida Date: Thu, 15 Mar 2018 15:27:07 +0800 Subject: [PATCH 05/16] Implemented stdio functions --- src/stdio/Cargo.toml | 4 + src/stdio/src/constants.rs | 27 ++ src/stdio/src/default.rs | 85 ++++++ src/stdio/src/helpers.rs | 159 ++++++++++ src/stdio/src/internal.rs | 143 +++++++++ src/stdio/src/lib.rs | 596 +++++++++++++++++++++++++++++-------- 6 files changed, 898 insertions(+), 116 deletions(-) create mode 100644 src/stdio/src/constants.rs create mode 100644 src/stdio/src/default.rs create mode 100644 src/stdio/src/helpers.rs create mode 100644 src/stdio/src/internal.rs diff --git a/src/stdio/Cargo.toml b/src/stdio/Cargo.toml index 2f53e58dc7..1a920812a6 100644 --- a/src/stdio/Cargo.toml +++ b/src/stdio/Cargo.toml @@ -8,6 +8,10 @@ build = "build.rs" cbindgen = { path = "../../cbindgen" } [dependencies] +compiler_builtins = { git = "https://github.com/rust-lang-nursery/compiler-builtins.git", default-features = false, features = ["mem"] } platform = { path = "../platform" } va_list = { path = "../../va_list", features = ["no_std"] } +fcntl = { path = "../fcntl" } +string = { path = "../string" } +stdlib = { path = "../stdlib" } errno = { path = "../errno"} diff --git a/src/stdio/src/constants.rs b/src/stdio/src/constants.rs new file mode 100644 index 0000000000..c601cbc85e --- /dev/null +++ b/src/stdio/src/constants.rs @@ -0,0 +1,27 @@ +use platform::types::*; + +pub const BUFSIZ: size_t = 1024; + +pub const UNGET: size_t = 8; + +pub const FILENAME_MAX: c_int = 4096; + +pub const F_PERM: c_int = 1; +pub const F_NORD: c_int = 4; +pub const F_NOWR: c_int = 8; +pub const F_EOF: c_int = 16; +pub const F_ERR: c_int = 32; +pub const F_SVB: c_int = 64; +pub const F_APP: c_int = 128; +pub const F_BADJ: c_int = 256; + +pub const SEEK_SET: c_int = 0; +pub const SEEK_CUR: c_int = 1; +pub const SEEK_END: c_int = 2; + +pub const _IOFBF: c_int = 0; +pub const _IOLBF: c_int = 1; +pub const _IONBF: c_int = 2; + +#[allow(non_camel_case_types)] +pub type fpos_t = off_t; diff --git a/src/stdio/src/default.rs b/src/stdio/src/default.rs new file mode 100644 index 0000000000..b3c1d71f67 --- /dev/null +++ b/src/stdio/src/default.rs @@ -0,0 +1,85 @@ +use core::sync::atomic::AtomicBool; +use core::ptr; +use super::{internal, BUFSIZ, FILE, UNGET, constants}; + +#[allow(non_upper_case_globals)] +static mut default_stdin_buf: [u8; BUFSIZ as usize + UNGET] = [0; BUFSIZ as usize + UNGET]; + +#[allow(non_upper_case_globals)] +static mut default_stdin: FILE = FILE { + flags: constants::F_PERM | constants::F_NOWR | constants::F_BADJ, + rpos: ptr::null_mut(), + rend: ptr::null_mut(), + wend: ptr::null_mut(), + wpos: ptr::null_mut(), + wbase: ptr::null_mut(), + fd: 0, + buf: unsafe { &mut default_stdin_buf as *mut [u8] as *mut u8 }, + buf_size: BUFSIZ as usize, + buf_char: -1, + unget: UNGET, + lock: AtomicBool::new(false), + write: None, + read: Some(&internal::stdio_read), + seek: None, +}; + +#[allow(non_upper_case_globals)] +static mut default_stdout_buf: [u8; BUFSIZ as usize] = [0; BUFSIZ as usize]; + +#[allow(non_upper_case_globals)] +static mut default_stdout: FILE = FILE { + flags: constants::F_PERM | constants::F_NORD | constants::F_BADJ, + rpos: ptr::null_mut(), + rend: ptr::null_mut(), + wend: ptr::null_mut(), + wpos: ptr::null_mut(), + wbase: ptr::null_mut(), + fd: 1, + buf: unsafe { &mut default_stdout_buf } as *mut [u8] as *mut u8, + buf_size: BUFSIZ as usize, + buf_char: b'\n' as i8, + unget: 0, + lock: AtomicBool::new(false), + write: Some(&internal::stdio_write), + read: None, + seek: None, +}; + +#[allow(non_upper_case_globals)] +static mut default_stderr_buf: [u8; BUFSIZ as usize] = [0; BUFSIZ as usize]; + +#[allow(non_upper_case_globals)] +static mut default_stderr: FILE = FILE { + flags: constants::F_PERM | constants::F_NORD | constants::F_BADJ, + rpos: ptr::null_mut(), + rend: ptr::null_mut(), + wend: ptr::null_mut(), + wpos: ptr::null_mut(), + wbase: ptr::null_mut(), + fd: 2, + buf: unsafe { &mut default_stderr_buf } as *mut [u8] as *mut u8, + buf_size: BUFSIZ as usize, + buf_char: -1, + unget: 0, + lock: AtomicBool::new(false), + write: Some(&internal::stdio_write), + read: None, + seek: None, +}; + +// Don't ask me how the casting below works, I have no idea +// " as *const FILE as *mut FILE" rust pls +// +// -- Tommoa +#[allow(non_upper_case_globals)] +#[no_mangle] +pub static mut stdin: *mut FILE = unsafe { &default_stdin } as *const FILE as *mut FILE; + +#[allow(non_upper_case_globals)] +#[no_mangle] +pub static mut stdout: *mut FILE = unsafe { &default_stdout } as *const FILE as *mut FILE; + +#[allow(non_upper_case_globals)] +#[no_mangle] +pub static mut stderr: *mut FILE = unsafe { &default_stderr } as *const FILE as *mut FILE; diff --git a/src/stdio/src/helpers.rs b/src/stdio/src/helpers.rs new file mode 100644 index 0000000000..4bc84ac375 --- /dev/null +++ b/src/stdio/src/helpers.rs @@ -0,0 +1,159 @@ +use super::{internal, BUFSIZ, FILE, UNGET}; +use compiler_builtins::mem::memset; +use stdlib::calloc; +use core::{mem, ptr}; +use core::sync::atomic::AtomicBool; +use platform::types::*; +use super::constants::*; +use fcntl::*; +use platform; +use errno; + +/// Parse mode flags as a string and output a mode flags integer +pub unsafe fn parse_mode_flags(mode_str: *const c_char) -> i32 { + use string::strchr; + let mut flags = if !strchr(mode_str, b'+' as i32).is_null() { + O_RDWR + } else if (*mode_str) == b'r' as i8 { + O_RDONLY + } else { + O_WRONLY + }; + if !strchr(mode_str, b'x' as i32).is_null() { + flags |= O_EXCL; + } + if !strchr(mode_str, b'e' as i32).is_null() { + flags |= O_CLOEXEC; + } + if (*mode_str) != b'r' as i8 { + flags |= O_CREAT; + } + if (*mode_str) == b'w' as i8 { + flags |= O_TRUNC; + } + if (*mode_str) != b'a' as i8 { + flags |= O_APPEND; + } + + flags +} + +/// Open a file with the file descriptor `fd` in the mode `mode` +pub unsafe fn _fdopen(fd: c_int, mode: *const c_char) -> *mut FILE { + use string::strchr; + if *mode != b'r' as i8 && *mode != b'w' as i8 && *mode != b'a' as i8 { + platform::errno = errno::EINVAL; + return ptr::null_mut(); + } + + let mut flags = 0; + if strchr(mode, b'+' as i32).is_null() { + flags |= if *mode == b'r' as i8 { F_NOWR } else { F_NORD }; + } + + if !strchr(mode, b'e' as i32).is_null() { + sys_fcntl(fd, F_SETFD, FD_CLOEXEC); + } + + if *mode == 'a' as i8 { + let f = sys_fcntl(fd, F_GETFL, 0); + if (f & O_APPEND) == 0 { + sys_fcntl(fd, F_SETFL, f | O_APPEND); + } + flags |= F_APP; + } + + let file = calloc(mem::size_of::() + BUFSIZ + UNGET, 1) as *mut FILE; + // Allocate the file + (*file) = FILE { + flags: flags, + rpos: ptr::null_mut(), + rend: ptr::null_mut(), + wend: ptr::null_mut(), + wpos: ptr::null_mut(), + wbase: ptr::null_mut(), + fd: fd, + buf: (file as *mut u8).add(mem::size_of::() + UNGET), + buf_size: BUFSIZ, + buf_char: -1, + unget: UNGET, + lock: AtomicBool::new(false), + write: Some(&internal::stdio_write), + read: Some(&internal::stdio_read), + seek: Some(&internal::stdio_seek), + }; + file +} + +/// Write buffer `buf` of length `l` into `stream` +pub unsafe fn fwritex(buf: *const u8, l: size_t, stream: *mut FILE) -> size_t { + use compiler_builtins::mem::memcpy; + let mut buf = buf; + let mut l = l; + if let Some(stream_write) = (*stream).write { + if (*stream).wend.is_null() && !internal::to_write(stream) { + // We can't write to this stream + return 0; + } + if l > (*stream).wend as usize - (*stream).wpos as usize { + // We can't fit all of buf in the buffer + return (*stream_write)(stream, buf, l); + } + + let i = if (*stream).buf_char >= 0 { + let mut i = l; + while i > 0 && *buf.offset(i as isize - 1) != b'\n' { + i -= 1; + } + if i > 0 { + let n = (*stream_write)(stream, buf, i); + if n < i { + return n; + } + buf = buf.add(i); + l -= i; + } + i + } else { + 0 + }; + + memcpy((*stream).wpos, buf, l); + (*stream).wpos = (*stream).wpos.add(l); + l + i + } else { + // We can't write to this stream + 0 + } +} + +/// Flush `stream` without locking it. +pub unsafe fn fflush_unlocked(stream: *mut FILE) -> c_int { + if (*stream).wpos > (*stream).wbase { + if let Some(f) = (*stream).write { + (*f)(stream, ptr::null(), 0); + if (*stream).wpos.is_null() { + return -1; + } + } else { + return -1; + } + } + + if (*stream).rpos < (*stream).rend { + if let Some(s) = (*stream).seek { + (*s)( + stream, + (*stream).rpos as i64 - (*stream).rend as i64, + SEEK_CUR, + ); + } + } + + (*stream).wpos = ptr::null_mut(); + (*stream).wend = ptr::null_mut(); + (*stream).wbase = ptr::null_mut(); + (*stream).rpos = ptr::null_mut(); + (*stream).rend = ptr::null_mut(); + 0 +} diff --git a/src/stdio/src/internal.rs b/src/stdio/src/internal.rs new file mode 100644 index 0000000000..5330eb269d --- /dev/null +++ b/src/stdio/src/internal.rs @@ -0,0 +1,143 @@ +use super::{FILE, constants}; +use platform; +use platform::types::*; +use core::{mem, ptr, slice}; + +pub fn stdio_read(stream: *mut FILE, buf: *mut u8, size: usize) -> usize { + unsafe { + let mut buff = slice::from_raw_parts_mut(buf, size - !((*stream).buf_size == 0) as usize); + let mut file_buf = slice::from_raw_parts_mut((*stream).buf, (*stream).buf_size); + let mut file = platform::FileReader((*stream).fd); + let count = if buff.len() == 0 { + file.read(&mut file_buf) + } else { + file.read(&mut buff) + file.read(&mut file_buf) + }; + mem::forget(buff); + mem::forget(file_buf); + if count <= 0 { + (*stream).flags |= if count == 0 { constants::F_EOF } else { constants::F_ERR }; + return 0; + } + if count as usize <= size { + return count as usize; + } + (*stream).rpos = (*stream).buf; + (*stream).rend = (*stream).buf.offset(count); + *buf.offset(size as isize - 1) = *(*stream).rpos; + (*stream).rpos = (*stream).rpos.add(1); + } + size +} + +pub fn stdio_write(stream: *mut FILE, buf: *const u8, size: usize) -> usize { + unsafe { + let len = (*stream).wpos as usize - (*stream).wbase as usize; + let mut advance = 0; + let mut f_buf = slice::from_raw_parts((*stream).wbase, len); + let mut buff = slice::from_raw_parts(buf, size); + let mut f_filled = false; + let mut rem = f_buf.len() + buff.len(); + let mut file = platform::FileWriter((*stream).fd); + loop { + let mut count = if f_filled { + file.write(&f_buf[advance..]) + } else { + file.write(&f_buf[advance..]) + file.write(buff) + }; + if count == rem as isize { + (*stream).wend = (*stream).buf.add((*stream).buf_size -1); + (*stream).wpos = (*stream).buf; + (*stream).wbase = (*stream).buf; + return size; + } + if count < 0 { + (*stream).wpos = ptr::null_mut(); + (*stream).wbase = ptr::null_mut(); + (*stream).wend = ptr::null_mut(); + (*stream).flags |= constants::F_ERR; + return 0; + } + rem -= count as usize; + if count as usize > len { + count -= len as isize; + f_buf = buff; + f_filled = true; + advance = 0; + } + advance += count as usize; + } + } +} + +pub unsafe fn to_read(stream: *mut FILE) -> bool { + if (*stream).flags & constants::F_BADJ > 0 { + // Static and needs unget region + (*stream).buf = (*stream).buf.add((*stream).unget); + (*stream).flags &= !constants::F_BADJ; + } + + if (*stream).wpos > (*stream).wbase { + if let Some(f) = (*stream).write { + (*f)(stream, ptr::null(), 0); + } + } + (*stream).wpos = ptr::null_mut(); + (*stream).wbase = ptr::null_mut(); + (*stream).wend = ptr::null_mut(); + if (*stream).flags & constants::F_NORD > 0 { + (*stream).flags |= constants::F_ERR; + return true; + } + (*stream).rpos = (*stream).buf.offset((*stream).buf_size as isize -1); + (*stream).rend = (*stream).buf.offset((*stream).buf_size as isize -1); + if (*stream).flags & constants::F_EOF > 0 { + true + } else { + false + } +} + +pub unsafe fn to_write(stream: *mut FILE) -> bool { + if (*stream).flags & constants::F_BADJ > 0 { + // Static and needs unget region + (*stream).buf = (*stream).buf.add((*stream).unget); + (*stream).flags &= !constants::F_BADJ; + } + + if (*stream).flags & constants::F_NOWR > 0 { + (*stream).flags &= constants::F_ERR; + return false; + } + (*stream).rpos = ptr::null_mut(); + (*stream).rend = ptr::null_mut(); + (*stream).wpos = (*stream).buf; + (*stream).wbase = (*stream).buf; + (*stream).wend = (*stream).buf.offset((*stream).buf_size as isize -1); + return true; +} + +pub unsafe fn ftello(stream: *mut FILE) -> off_t { + if let Some(s) = (*stream).seek { + let pos = (*s)( + stream, + 0, + if ((*stream).flags & constants::F_APP > 0) && (*stream).wpos > (*stream).wbase { + constants::SEEK_END + } else { + constants::SEEK_CUR + }, + ); + if pos < 0 { + return pos; + } + pos - ((*stream).rend as i64 - (*stream).rpos as i64) + + ((*stream).wpos as i64 - (*stream).wbase as i64) + } else { + -1 + } +} + +pub fn stdio_seek(stream: *mut FILE, off: off_t, whence: c_int) -> off_t { + unsafe { platform::lseek((*stream).fd, off, whence) } +} diff --git a/src/stdio/src/lib.rs b/src/stdio/src/lib.rs index e99351639b..e77b86eaa8 100644 --- a/src/stdio/src/lib.rs +++ b/src/stdio/src/lib.rs @@ -1,41 +1,64 @@ //! stdio implementation for Redox, following http://pubs.opengroup.org/onlinepubs/7908799/xsh/stdio.h.html +#![feature(compiler_builtins_lib)] #![no_std] +extern crate compiler_builtins; extern crate errno; +extern crate fcntl; extern crate platform; +extern crate stdlib; +extern crate string; extern crate va_list as vl; use core::str; +use core::ptr; use core::fmt::Write; +use core::sync::atomic::{AtomicBool, Ordering}; use platform::types::*; -use platform::c_str; -use platform::errno; +use platform::{c_str, errno}; use errno::STR_ERROR; use vl::VaList as va_list; mod printf; -pub const BUFSIZ: c_int = 4096; +mod default; +pub use default::*; -pub const FILENAME_MAX: c_int = 4096; +mod constants; +pub use constants::*; -pub type fpos_t = off_t; +mod helpers; -pub struct FILE; +mod internal; -#[allow(non_upper_case_globals)] +/// The structure of this struct is likely to change. +/// all of the _pos pointers are likely to change to isize offsets and its possible that the +/// function pointers will be removed +#[repr(C)] +pub struct FILE { + flags: c_int, + rpos: *mut u8, + rend: *mut u8, + wend: *mut u8, + wpos: *mut u8, + wbase: *mut u8, + fd: c_int, + buf: *mut u8, + buf_size: size_t, + buf_char: i8, + lock: AtomicBool, + unget: size_t, + write: Option<*const (Fn(*mut FILE, *const u8, usize) -> size_t)>, + read: Option<*const (Fn(*mut FILE, *mut u8, usize) -> size_t)>, + seek: Option<*const (Fn(*mut FILE, off_t, c_int) -> off_t)>, +} + +/// Clears EOF and ERR indicators on a stream #[no_mangle] -pub static mut stdout: *mut FILE = 1 as *mut FILE; - -#[allow(non_upper_case_globals)] -#[no_mangle] -pub static mut stderr: *mut FILE = 2 as *mut FILE; - -#[no_mangle] -pub extern "C" fn clearerr(stream: *mut FILE) { - unimplemented!(); +pub unsafe extern "C" fn clearerr(stream: *mut FILE) { + (*stream).flags &= !(F_EOF & F_ERR); } #[no_mangle] @@ -48,88 +71,244 @@ pub extern "C" fn cuserid(s: *mut c_char) -> *mut c_char { unimplemented!(); } +/// Close a file +/// This function does not guarentee that the file buffer will be flushed or that the file +/// descriptor will be closed, so if it is important that the file be written to, use `fflush()` +/// prior to using this function. #[no_mangle] -pub extern "C" fn fclose(stream: *mut FILE) -> c_int { - unimplemented!(); +pub unsafe extern "C" fn fclose(stream: *mut FILE) -> c_int { + use stdlib::free; + flockfile(stream); + let r = helpers::fflush_unlocked(stream) | platform::close((*stream).fd); + if (*stream).flags & constants::F_PERM == 0 { + // Not one of stdin, stdout or stderr + free(stream as *mut _); + } + r } +/// Open a file from a file descriptor #[no_mangle] -pub extern "C" fn fdopen(fildes: c_int, mode: *const c_char) -> *mut FILE { - unimplemented!(); +pub unsafe extern "C" fn fdopen(fildes: c_int, mode: *const c_char) -> *mut FILE { + helpers::_fdopen(fildes, mode) } +/// Check for EOF #[no_mangle] -pub extern "C" fn feof(stream: *mut FILE) -> c_int { - unimplemented!(); +pub unsafe extern "C" fn feof(stream: *mut FILE) -> c_int { + flockfile(stream); + let ret = (*stream).flags & F_EOF; + funlockfile(stream); + ret } +/// Check for ERR #[no_mangle] -pub extern "C" fn ferror(stream: *mut FILE) -> c_int { - unimplemented!(); +pub unsafe extern "C" fn ferror(stream: *mut FILE) -> c_int { + flockfile(stream); + let ret = (*stream).flags & F_ERR; + funlockfile(stream); + ret } +/// Flush output to stream, or sync read position +/// Ensure the file is unlocked before calling this function, as it will attempt to lock the file +/// itself. #[no_mangle] -pub extern "C" fn fflush(stream: *mut FILE) -> c_int { - unimplemented!(); +pub unsafe extern "C" fn fflush(stream: *mut FILE) -> c_int { + flockfile(stream); + + let ret = helpers::fflush_unlocked(stream); + + funlockfile(stream); + ret } +/// Get a single char from a stream #[no_mangle] -pub extern "C" fn fgetc(stream: *mut FILE) -> c_int { - unimplemented!(); -} - -#[no_mangle] -pub extern "C" fn fgetpos(stream: *mut FILE, pos: *mut fpos_t) -> c_int { - unimplemented!(); -} - -#[no_mangle] -pub extern "C" fn fgets(s: *mut c_char, n: c_int, stream: *mut FILE) -> *mut c_char { - unimplemented!(); -} - -#[no_mangle] -pub extern "C" fn fileno(stream: *mut FILE) -> c_int { - unimplemented!(); -} - -#[no_mangle] -pub extern "C" fn flockfile(file: *mut FILE) { - unimplemented!(); -} - -#[no_mangle] -pub extern "C" fn fopen(filename: *const c_char, mode: *const c_char) -> *mut FILE { - unimplemented!(); -} - -#[no_mangle] -pub extern "C" fn fputc(c: c_int, stream: *mut FILE) -> c_int { - platform::FileWriter(stream as c_int) - .write_char(c as u8 as char) - .map_err(|_| return -1); +pub unsafe extern "C" fn fgetc(stream: *mut FILE) -> c_int { + flockfile(stream); + let c = getc_unlocked(stream); + funlockfile(stream); c } +/// Get the position of the stream and store it in pos +#[no_mangle] +pub unsafe extern "C" fn fgetpos(stream: *mut FILE, pos: *mut fpos_t) -> c_int { + let off = internal::ftello(stream); + if off < 0 { + return -1; + } + (*pos) = off; + 0 +} + +/// Get a string from the stream +#[no_mangle] +pub unsafe extern "C" fn fgets(s: *mut c_char, n: c_int, stream: *mut FILE) -> *mut c_char { + use string::memchr; + use compiler_builtins::mem::memcpy; + flockfile(stream); + let mut ptr = s as *mut u8; + let mut n = n; + if n <= 1 { + funlockfile(stream); + if n == 0 { + return ptr::null_mut(); + } + (*s) = b'\0' as i8; + return s; + } + while n > 0 { + let z = memchr( + (*stream).rpos as *const c_void, + b'\n' as c_int, + (*stream).rend as usize - (*stream).rpos as usize, + ) as *mut u8; + let k = if z.is_null() { + (*stream).rend as usize - (*stream).rpos as usize + } else { + z as usize - (*stream).rpos as usize + 1 + }; + let k = if k as i32 > n { n as usize } else { k }; + memcpy(ptr, (*stream).rpos, k); + (*stream).rpos = (*stream).rpos.add(k); + ptr = ptr.add(k); + n -= k as i32; + if !z.is_null() || n < 1 { + break; + } + let c = getc_unlocked(stream); + if c < 0 { + break; + } + n -= 1; + *ptr = c as u8; + ptr = ptr.add(1); + if c as u8 == b'\n' { + break; + } + } + if !s.is_null() { + *ptr = 0; + } + funlockfile(stream); + s +} + +/// Get the underlying file descriptor +#[no_mangle] +pub unsafe extern "C" fn fileno(stream: *mut FILE) -> c_int { + flockfile(stream); + funlockfile(stream); + (*stream).fd +} + +/// Lock the file +/// Do not call any functions other than those with the `_unlocked` postfix while the file is +/// locked +#[no_mangle] +pub unsafe extern "C" fn flockfile(file: *mut FILE) { + while ftrylockfile(file) != 0 {} +} + +/// Open the file in mode `mode` +#[no_mangle] +pub unsafe extern "C" fn fopen(filename: *const c_char, mode: *const c_char) -> *mut FILE { + use core::ptr; + let initial_mode = *mode; + if initial_mode != b'r' as i8 && initial_mode != b'w' as i8 && initial_mode != b'a' as i8 { + platform::errno = errno::EINVAL; + return ptr::null_mut(); + } + + let flags = helpers::parse_mode_flags(mode); + + let fd = fcntl::sys_open(filename, flags, 0o666); + if fd < 0 { + return ptr::null_mut(); + } + + if flags & fcntl::O_CLOEXEC > 0 { + fcntl::sys_fcntl(fd, fcntl::F_SETFD, fcntl::FD_CLOEXEC); + } + + let f = helpers::_fdopen(fd, mode); + if f.is_null() { + platform::close(fd); + return ptr::null_mut(); + } + f +} + +/// Insert a character into the stream +#[no_mangle] +pub unsafe extern "C" fn fputc(c: c_int, stream: *mut FILE) -> c_int { + flockfile(stream); + let c = putc_unlocked(c, stream); + funlockfile(stream); + c +} + +/// Insert a string into a stream #[no_mangle] pub unsafe extern "C" fn fputs(s: *const c_char, stream: *mut FILE) -> c_int { extern "C" { fn strlen(s: *const c_char) -> size_t; } - use core::{slice, str}; let len = strlen(s); - platform::FileWriter(stream as c_int) - .write_str(str::from_utf8_unchecked(slice::from_raw_parts( - s as *const u8, - len, - ))) - .map_err(|_| return -1); - len as i32 + (fwrite(s as *const c_void, 1, len, stream) == len) as c_int - 1 } +/// Read `nitems` of size `size` into `ptr` from `stream` #[no_mangle] -pub extern "C" fn fread(ptr: *mut c_void, size: usize, nitems: usize, stream: *mut FILE) -> usize { - unimplemented!(); +pub unsafe extern "C" fn fread( + ptr: *mut c_void, + size: usize, + nitems: usize, + stream: *mut FILE, +) -> usize { + use compiler_builtins::mem::memcpy; + let mut dest = ptr as *mut u8; + let len = size * nitems; + let mut l = len as isize; + + flockfile(stream); + + if (*stream).rend > (*stream).rpos { + // We have some buffered data that can be read + let diff = (*stream).rend as usize - (*stream).rpos as usize; + let k = if diff < l as usize { diff } else { l as usize }; + memcpy(dest, (*stream).rpos, k); + (*stream).rpos = (*stream).rpos.add(k); + dest = dest.add(k); + l -= k as isize; + } + + while l > 0 { + let k = if internal::to_read(stream) { + 0 + } else { + if let Some(f) = (*stream).read { + (*f)(stream, dest, l as usize) + } + else { + 0 + } + }; + + if k == 0 { + funlockfile(stream); + return (len - l as usize) / 2; + } + + l -= k as isize; + dest = dest.add(k); + } + + funlockfile(stream); + nitems } #[no_mangle] @@ -141,79 +320,169 @@ pub extern "C" fn freopen( unimplemented!(); } +/// Seek to an offset `offset` from `whence` #[no_mangle] -pub extern "C" fn fseek(stream: *mut FILE, offset: c_long, whence: c_int) -> c_int { - unimplemented!(); +pub unsafe extern "C" fn fseek(stream: *mut FILE, offset: c_long, whence: c_int) -> c_int { + if fseeko(stream, offset as off_t, whence) != -1 { + return 0; + } + -1 } +/// Seek to an offset `offset` from `whence` #[no_mangle] -pub extern "C" fn fseeko(stream: *mut FILE, offset: off_t, whence: c_int) -> c_int { - unimplemented!(); +pub unsafe extern "C" fn fseeko(stream: *mut FILE, offset: off_t, whence: c_int) -> c_int { + let mut off = offset; + flockfile(stream); + // Adjust for what is currently in the buffer + if whence == SEEK_CUR { + off -= ((*stream).rend as usize - (*stream).rpos as usize) as i64; + } + if (*stream).wpos > (*stream).wbase { + if let Some(f) = (*stream).write { + (*f)(stream, ptr::null(), 0); + if (*stream).wpos.is_null() { + return -1; + } + } + } + (*stream).wpos = ptr::null_mut(); + (*stream).wend = ptr::null_mut(); + (*stream).wbase = ptr::null_mut(); + if let Some(s) = (*stream).seek { + if (*s)(stream, off, whence) < 0 { + return -1; + } + } else { + return -1; + } + (*stream).rpos = ptr::null_mut(); + (*stream).rend = ptr::null_mut(); + (*stream).flags &= !F_EOF; + funlockfile(stream); + 0 } +/// Seek to a position `pos` in the file from the beginning of the file #[no_mangle] -pub extern "C" fn fsetpos(stream: *mut FILE, pos: *const fpos_t) -> c_int { - unimplemented!(); +pub unsafe extern "C" fn fsetpos(stream: *mut FILE, pos: *const fpos_t) -> c_int { + fseek(stream, *pos as off_t, SEEK_SET) } +/// Get the current position of the cursor in the file #[no_mangle] -pub extern "C" fn ftell(stream: *mut FILE) -> c_long { - unimplemented!(); +pub unsafe extern "C" fn ftell(stream: *mut FILE) -> c_long { + ftello(stream) as c_long } +/// Get the current position of the cursor in the file #[no_mangle] -pub extern "C" fn ftello(stream: *mut FILE) -> off_t { - unimplemented!(); +pub unsafe extern "C" fn ftello(stream: *mut FILE) -> off_t { + flockfile(stream); + let pos = internal::ftello(stream); + funlockfile(stream); + pos } +/// Try to lock the file. Returns 0 for success, 1 for failure #[no_mangle] -pub extern "C" fn ftrylockfile(file: *mut FILE) -> c_int { - unimplemented!(); +pub unsafe extern "C" fn ftrylockfile(file: *mut FILE) -> c_int { + (*file) + .lock + .compare_and_swap(false, true, Ordering::Acquire) as c_int } +/// Unlock the file #[no_mangle] -pub extern "C" fn funlockfile(file: *mut FILE) { - unimplemented!(); +pub unsafe extern "C" fn funlockfile(file: *mut FILE) { + (*file).lock.store(false, Ordering::Release); } +/// Write `nitems` of size `size` from `ptr` to `stream` #[no_mangle] -pub extern "C" fn fwrite( +pub unsafe extern "C" fn fwrite( ptr: *const c_void, size: usize, nitems: usize, stream: *mut FILE, ) -> usize { - unimplemented!(); + let l = size * nitems; + let nitems = if size == 0 { 0 } else { nitems }; + flockfile(stream); + let k = helpers::fwritex(ptr as *const u8, l, stream); + funlockfile(stream); + if k == l { + nitems + } else { + k / size + } } +/// Get a single char from a stream #[no_mangle] -pub extern "C" fn getc(stream: *mut FILE) -> c_int { - unimplemented!(); +pub unsafe extern "C" fn getc(stream: *mut FILE) -> c_int { + flockfile(stream); + let c = getc_unlocked(stream); + funlockfile(stream); + c } +/// Get a single char from `stdin` #[no_mangle] -pub extern "C" fn getchar() -> c_int { - unimplemented!(); +pub unsafe extern "C" fn getchar() -> c_int { + fgetc(stdin) } +/// Get a char from a stream without locking the stream #[no_mangle] -pub extern "C" fn getc_unlocked(stream: *mut FILE) -> c_int { - unimplemented!(); +pub unsafe extern "C" fn getc_unlocked(stream: *mut FILE) -> c_int { + if (*stream).rpos < (*stream).rend { + let ret = *(*stream).rpos as c_int; + (*stream).rpos = (*stream).rpos.add(1); + ret + } else { + if let Some(read) = (*stream).read { + let mut c = 0u8; + if !internal::to_read(stream) && (*read)(stream, &mut c, 1) == 1 { + c as c_int + } else { + -1 + } + } else { + -1 + } + } } +/// Get a char from `stdin` without locking `stdin` #[no_mangle] -pub extern "C" fn getchar_unlocked() -> c_int { - unimplemented!(); +pub unsafe extern "C" fn getchar_unlocked() -> c_int { + getc_unlocked(stdin) } +/// Get a string from `stdin` #[no_mangle] -pub extern "C" fn gets(s: *mut c_char) -> *mut c_char { - unimplemented!(); +pub unsafe extern "C" fn gets(s: *mut c_char) -> *mut c_char { + use core::i32; + fgets(s, i32::MAX, stdin) } +/// Get an integer from `stream` #[no_mangle] -pub extern "C" fn getw(stream: *mut FILE) -> c_int { - unimplemented!(); +pub unsafe extern "C" fn getw(stream: *mut FILE) -> c_int { + use core::mem; + let mut ret: c_int = 0; + if fread( + &mut ret as *mut c_int as *mut c_void, + mem::size_of_val(&ret), + 1, + stream, + ) > 0 + { + ret + } else { + -1 + } } #[no_mangle] @@ -238,60 +507,134 @@ pub extern "C" fn popen(command: *const c_char, mode: *const c_char) -> *mut FIL unimplemented!(); } +/// Put a character `c` into `stream` #[no_mangle] -pub extern "C" fn putc(c: c_int, stream: *mut FILE) -> c_int { - fputc(c, stream) +pub unsafe extern "C" fn putc(c: c_int, stream: *mut FILE) -> c_int { + flockfile(stream); + let ret = putc_unlocked(c, stream); + funlockfile(stream); + ret } +/// Put a character `c` into `stdout` #[no_mangle] pub unsafe extern "C" fn putchar(c: c_int) -> c_int { - putc(c, stdout) + fputc(c, stdout) } +/// Put a character `c` into `stream` without locking `stream` #[no_mangle] -pub extern "C" fn putc_unlocked(c: c_int, stream: *mut FILE) -> c_int { - unimplemented!(); +pub unsafe extern "C" fn putc_unlocked(c: c_int, stream: *mut FILE) -> c_int { + if c as i8 != (*stream).buf_char && (*stream).wpos < (*stream).wend { + *(*stream).wpos = c as u8; + (*stream).wpos = (*stream).wpos.add(1); + c + } else { + if let Some(write) = (*stream).write { + if (*stream).wend.is_null() && internal::to_write(stream) { + -1 + } else if c as i8 != (*stream).buf_char && (*stream).wpos < (*stream).wend { + *(*stream).wpos = c as u8; + (*stream).wpos = (*stream).wpos.add(1); + c + } else if (*write)(stream, &c as *const i32 as *const _, 1) != 1 { + -1 + } else { + c + } + } else { + -1 + } + } } +/// Put a character `c` into `stdout` without locking `stdout` #[no_mangle] pub unsafe extern "C" fn putchar_unlocked(c: c_int) -> c_int { putc_unlocked(c, stdout) } +/// Put a string `s` into `stdout` #[no_mangle] pub unsafe extern "C" fn puts(s: *const c_char) -> c_int { - fputs(s, stdout); - putchar(b'\n' as c_int) + let ret = (fputs(s, stdout) > 0) || (putchar_unlocked(b'\n' as c_int) > 0); + if ret { + 0 + } else { + -1 + } } +/// Put an integer `w` into `stream` #[no_mangle] -pub extern "C" fn putw(w: c_int, stream: *mut FILE) -> c_int { - unimplemented!(); +pub unsafe extern "C" fn putw(w: c_int, stream: *mut FILE) -> c_int { + use core::mem; + fwrite(&w as *const i32 as _, mem::size_of_val(&w), 1, stream) as i32 - 1 } +/// Delete file or directory `path` #[no_mangle] pub extern "C" fn remove(path: *const c_char) -> c_int { - unimplemented!(); + let r = platform::unlink(path); + if r == -errno::EISDIR { + platform::rmdir(path) + } else { + r + } } #[no_mangle] pub extern "C" fn rename(old: *const c_char, new: *const c_char) -> c_int { + // This function requires a rename syscall, which currently is not in platform. unimplemented!(); } +/// Rewind `stream` back to the beginning of it #[no_mangle] -pub extern "C" fn rewind(stream: *mut FILE) { - unimplemented!(); +pub unsafe extern "C" fn rewind(stream: *mut FILE) { + fseeko(stream, 0, SEEK_SET); + flockfile(stream); + (*stream).flags &= !F_ERR; + funlockfile(stream); } +/// Reset `stream` to use buffer `buf`. Buffer must be `BUFSIZ` in length #[no_mangle] pub extern "C" fn setbuf(stream: *mut FILE, buf: *mut c_char) { - unimplemented!(); + unsafe { + setvbuf( + stream, + buf, + if buf.is_null() { _IONBF } else { _IOFBF }, + BUFSIZ as usize, + ) + }; } +/// Reset `stream` to use buffer `buf` of size `size` #[no_mangle] -pub extern "C" fn setvbuf(stream: *mut FILE, buf: *mut c_char, mode: c_int, size: usize) -> c_int { - unimplemented!(); +pub unsafe extern "C" fn setvbuf( + stream: *mut FILE, + buf: *mut c_char, + mode: c_int, + size: usize, +) -> c_int { + // TODO: Check correctness + use stdlib::calloc; + let mut buf = buf; + if buf.is_null() && mode != _IONBF { + buf = calloc(size, 1) as *mut c_char; + } + (*stream).buf_size = size; + (*stream).buf_char = -1; + if mode == _IONBF { + (*stream).buf_size = 0; + } else if mode == _IOLBF { + (*stream).buf_char = b'\n' as i8; + } + (*stream).flags |= F_SVB; + (*stream).buf = buf as *mut u8; + 0 } #[no_mangle] @@ -309,14 +652,35 @@ pub extern "C" fn tmpnam(s: *mut c_char) -> *mut c_char { unimplemented!(); } +/// Push character `c` back onto `stream` so it'll be read next #[no_mangle] -pub extern "C" fn ungetc(c: c_int, stream: *mut FILE) -> c_int { - unimplemented!(); +pub unsafe extern "C" fn ungetc(c: c_int, stream: *mut FILE) -> c_int { + if c < 0 { + c + } else { + flockfile(stream); + if (*stream).rpos.is_null() { + internal::to_read(stream); + } + if (*stream).rpos.is_null() + || (*stream).rpos <= (*stream).buf.sub((*stream).unget) + { + funlockfile(stream); + return -1; + } + + (*stream).rpos = (*stream).rpos.sub(1); + *(*stream).rpos = c as u8; + (*stream).flags &= !F_EOF; + + funlockfile(stream); + c + } } #[no_mangle] pub unsafe extern "C" fn vfprintf(file: *mut FILE, format: *const c_char, ap: va_list) -> c_int { - printf::printf(platform::FileWriter(file as c_int), format, ap) + printf::printf(platform::FileWriter((*file).fd), format, ap) } #[no_mangle] From b0492eba842bfb9ccadc5cefccf75cd675f01bcd Mon Sep 17 00:00:00 2001 From: Tom Almeida Date: Thu, 15 Mar 2018 15:28:14 +0800 Subject: [PATCH 06/16] Added some tests for stdio --- tests/.gitignore | 3 +++ tests/Makefile | 4 ++++ tests/getc_unget.c | 12 ++++++++++++ tests/stdio/all.c | 11 +++++++++++ tests/stdio/fwrite.c | 11 +++++++++++ tests/stdio/fwrite.out | 1 + tests/stdio/stdio.in | 3 +++ 7 files changed, 45 insertions(+) create mode 100644 tests/getc_unget.c create mode 100644 tests/stdio/all.c create mode 100644 tests/stdio/fwrite.c create mode 100644 tests/stdio/fwrite.out create mode 100644 tests/stdio/stdio.in diff --git a/tests/.gitignore b/tests/.gitignore index 6cfb8396be..10ba484ea8 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -15,6 +15,7 @@ /fsync /ftruncate /getid +/getc_unget /link /math /mem @@ -27,6 +28,8 @@ /sprintf /stdlib/strtol /stdlib/a64l +/stdio/fwrite +/stdio/all /string/strncmp /string/strcspn /string/strchr diff --git a/tests/Makefile b/tests/Makefile index a252889605..43b5512399 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -12,6 +12,8 @@ EXPECT_BINS=\ fcntl \ fsync \ ftruncate \ + getid \ + getc_unget \ link \ math \ mem \ @@ -20,6 +22,8 @@ EXPECT_BINS=\ rmdir \ sleep \ sprintf \ + stdio/fwrite \ + stdio/all \ stdlib/strtol \ stdlib/a64l \ string/strncmp \ diff --git a/tests/getc_unget.c b/tests/getc_unget.c new file mode 100644 index 0000000000..f8008534fb --- /dev/null +++ b/tests/getc_unget.c @@ -0,0 +1,12 @@ +#include + +int main(int argc, char ** argv) { + ungetc('h', stdin); + char c; + if ((c = getchar()) == 'h') { + printf("Worked!\n"); + return 0; + } + printf("failed :( %c\n", c); + return 0; +} diff --git a/tests/stdio/all.c b/tests/stdio/all.c new file mode 100644 index 0000000000..450829364e --- /dev/null +++ b/tests/stdio/all.c @@ -0,0 +1,11 @@ +#include +#include + +int main(int argc, char ** argv) { + FILE *f = fopen("stdio/stdio.in", "r"); + printf("%c\n", fgetc(f)); + ungetc('H', f); + char *in = malloc(30); + printf("%s\n", fgets(in, 30, f)); + return 0; +} diff --git a/tests/stdio/fwrite.c b/tests/stdio/fwrite.c new file mode 100644 index 0000000000..da172d5b58 --- /dev/null +++ b/tests/stdio/fwrite.c @@ -0,0 +1,11 @@ +#include +#include +#include + +int main(int argc, char ** argv) { + FILE *f = fopen("stdio/fwrite.out", "w"); + char *in = "Hello World!"; + fputs(in, f); // calls fwrite, helpers::fwritex, internal::to_write and internal::stdio_write + fclose(f); + return 0; +} diff --git a/tests/stdio/fwrite.out b/tests/stdio/fwrite.out new file mode 100644 index 0000000000..c57eff55eb --- /dev/null +++ b/tests/stdio/fwrite.out @@ -0,0 +1 @@ +Hello World! \ No newline at end of file diff --git a/tests/stdio/stdio.in b/tests/stdio/stdio.in new file mode 100644 index 0000000000..c50d87bb17 --- /dev/null +++ b/tests/stdio/stdio.in @@ -0,0 +1,3 @@ +Hello World! + +Line 2 From c4c8b739032e6e1274f1f0b3dd3ce97b727e3e2f Mon Sep 17 00:00:00 2001 From: Tom Almeida Date: Thu, 15 Mar 2018 15:34:39 +0800 Subject: [PATCH 07/16] Formatted stdio files --- src/stdio/src/default.rs | 2 +- src/stdio/src/helpers.rs | 2 +- src/stdio/src/internal.rs | 16 ++++++++++------ src/stdio/src/lib.rs | 11 ++++------- 4 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/stdio/src/default.rs b/src/stdio/src/default.rs index b3c1d71f67..8ba56533de 100644 --- a/src/stdio/src/default.rs +++ b/src/stdio/src/default.rs @@ -1,6 +1,6 @@ use core::sync::atomic::AtomicBool; use core::ptr; -use super::{internal, BUFSIZ, FILE, UNGET, constants}; +use super::{constants, internal, BUFSIZ, FILE, UNGET}; #[allow(non_upper_case_globals)] static mut default_stdin_buf: [u8; BUFSIZ as usize + UNGET] = [0; BUFSIZ as usize + UNGET]; diff --git a/src/stdio/src/helpers.rs b/src/stdio/src/helpers.rs index 4bc84ac375..267d49da39 100644 --- a/src/stdio/src/helpers.rs +++ b/src/stdio/src/helpers.rs @@ -32,7 +32,7 @@ pub unsafe fn parse_mode_flags(mode_str: *const c_char) -> i32 { flags |= O_TRUNC; } if (*mode_str) != b'a' as i8 { - flags |= O_APPEND; + flags |= O_APPEND; } flags diff --git a/src/stdio/src/internal.rs b/src/stdio/src/internal.rs index 5330eb269d..00b6892404 100644 --- a/src/stdio/src/internal.rs +++ b/src/stdio/src/internal.rs @@ -1,4 +1,4 @@ -use super::{FILE, constants}; +use super::{constants, FILE}; use platform; use platform::types::*; use core::{mem, ptr, slice}; @@ -16,7 +16,11 @@ pub fn stdio_read(stream: *mut FILE, buf: *mut u8, size: usize) -> usize { mem::forget(buff); mem::forget(file_buf); if count <= 0 { - (*stream).flags |= if count == 0 { constants::F_EOF } else { constants::F_ERR }; + (*stream).flags |= if count == 0 { + constants::F_EOF + } else { + constants::F_ERR + }; return 0; } if count as usize <= size { @@ -46,7 +50,7 @@ pub fn stdio_write(stream: *mut FILE, buf: *const u8, size: usize) -> usize { file.write(&f_buf[advance..]) + file.write(buff) }; if count == rem as isize { - (*stream).wend = (*stream).buf.add((*stream).buf_size -1); + (*stream).wend = (*stream).buf.add((*stream).buf_size - 1); (*stream).wpos = (*stream).buf; (*stream).wbase = (*stream).buf; return size; @@ -89,8 +93,8 @@ pub unsafe fn to_read(stream: *mut FILE) -> bool { (*stream).flags |= constants::F_ERR; return true; } - (*stream).rpos = (*stream).buf.offset((*stream).buf_size as isize -1); - (*stream).rend = (*stream).buf.offset((*stream).buf_size as isize -1); + (*stream).rpos = (*stream).buf.offset((*stream).buf_size as isize - 1); + (*stream).rend = (*stream).buf.offset((*stream).buf_size as isize - 1); if (*stream).flags & constants::F_EOF > 0 { true } else { @@ -113,7 +117,7 @@ pub unsafe fn to_write(stream: *mut FILE) -> bool { (*stream).rend = ptr::null_mut(); (*stream).wpos = (*stream).buf; (*stream).wbase = (*stream).buf; - (*stream).wend = (*stream).buf.offset((*stream).buf_size as isize -1); + (*stream).wend = (*stream).buf.offset((*stream).buf_size as isize - 1); return true; } diff --git a/src/stdio/src/lib.rs b/src/stdio/src/lib.rs index e77b86eaa8..0979a695d1 100644 --- a/src/stdio/src/lib.rs +++ b/src/stdio/src/lib.rs @@ -80,7 +80,7 @@ pub unsafe extern "C" fn fclose(stream: *mut FILE) -> c_int { use stdlib::free; flockfile(stream); let r = helpers::fflush_unlocked(stream) | platform::close((*stream).fd); - if (*stream).flags & constants::F_PERM == 0 { + if (*stream).flags & constants::F_PERM == 0 { // Not one of stdin, stdout or stderr free(stream as *mut _); } @@ -234,7 +234,7 @@ pub unsafe extern "C" fn fopen(filename: *const c_char, mode: *const c_char) -> fcntl::sys_fcntl(fd, fcntl::F_SETFD, fcntl::FD_CLOEXEC); } - let f = helpers::_fdopen(fd, mode); + let f = helpers::_fdopen(fd, mode); if f.is_null() { platform::close(fd); return ptr::null_mut(); @@ -292,8 +292,7 @@ pub unsafe extern "C" fn fread( } else { if let Some(f) = (*stream).read { (*f)(stream, dest, l as usize) - } - else { + } else { 0 } }; @@ -662,9 +661,7 @@ pub unsafe extern "C" fn ungetc(c: c_int, stream: *mut FILE) -> c_int { if (*stream).rpos.is_null() { internal::to_read(stream); } - if (*stream).rpos.is_null() - || (*stream).rpos <= (*stream).buf.sub((*stream).unget) - { + if (*stream).rpos.is_null() || (*stream).rpos <= (*stream).buf.sub((*stream).unget) { funlockfile(stream); return -1; } From 41b96fede3c869491412d6efbe748080b1017650 Mon Sep 17 00:00:00 2001 From: Tom Almeida Date: Thu, 15 Mar 2018 15:57:19 +0800 Subject: [PATCH 08/16] Added a different internal function for redox --- src/stdio/src/internal.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/stdio/src/internal.rs b/src/stdio/src/internal.rs index 00b6892404..f9ef756f64 100644 --- a/src/stdio/src/internal.rs +++ b/src/stdio/src/internal.rs @@ -142,6 +142,12 @@ pub unsafe fn ftello(stream: *mut FILE) -> off_t { } } +#[cfg(target_os = "linux")] pub fn stdio_seek(stream: *mut FILE, off: off_t, whence: c_int) -> off_t { unsafe { platform::lseek((*stream).fd, off, whence) } } + +#[cfg(target_os = "redox")] +pub fn stdio_seek(stream: *mut FILE, off: off_t, whence: c_int) -> off_t { + unsafe { platform::lseek((*stream).fd, off as isize, whence as usize) as off_t } +} From 8d4042402077687d5b5c60decc33897a29053322 Mon Sep 17 00:00:00 2001 From: Tom Almeida Date: Fri, 16 Mar 2018 20:24:40 +0800 Subject: [PATCH 09/16] Added freopen() and relevant tests --- src/stdio/src/lib.rs | 40 ++++++++++++++++++++++++++++++++++++++-- tests/.gitignore | 1 + tests/Makefile | 1 + tests/stdio/freopen.c | 9 +++++++++ 4 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 tests/stdio/freopen.c diff --git a/src/stdio/src/lib.rs b/src/stdio/src/lib.rs index 0979a695d1..4748fdfb38 100644 --- a/src/stdio/src/lib.rs +++ b/src/stdio/src/lib.rs @@ -311,12 +311,48 @@ pub unsafe extern "C" fn fread( } #[no_mangle] -pub extern "C" fn freopen( +pub unsafe extern "C" fn freopen( filename: *const c_char, mode: *const c_char, stream: *mut FILE, ) -> *mut FILE { - unimplemented!(); + let mut flags = helpers::parse_mode_flags(mode); + flockfile(stream); + + helpers::fflush_unlocked(stream); + if filename.is_null() { // Reopen stream in new mode + if flags & fcntl::O_CLOEXEC > 0 { + fcntl::sys_fcntl((*stream).fd, fcntl::F_SETFD, fcntl::FD_CLOEXEC); + } + flags &= !(fcntl::O_CREAT | fcntl::O_EXCL | fcntl::O_CLOEXEC); + if fcntl::sys_fcntl((*stream).fd, fcntl::F_SETFL, flags) < 0 { + funlockfile(stream); + fclose(stream); + return ptr::null_mut(); + } + } else { + let new = fopen(filename, mode); + if new.is_null() { + funlockfile(stream); + fclose(stream); + return ptr::null_mut(); + } + if (*new).fd == (*stream).fd { + (*new).fd = -1; + } else if platform::dup2((*new).fd, (*stream).fd) < 0 || fcntl::sys_fcntl((*stream).fd, fcntl::F_SETFL, flags&fcntl::O_CLOEXEC) < 0 { + fclose(new); + funlockfile(stream); + fclose(stream); + return ptr::null_mut(); + } + (*stream).flags = ((*stream).flags & constants::F_PERM) | (*new).flags; + (*stream).read = (*new).read; + (*stream).write = (*new).write; + (*stream).seek = (*new).seek; + fclose(new); + } + funlockfile(stream); + stream } /// Seek to an offset `offset` from `whence` diff --git a/tests/.gitignore b/tests/.gitignore index 10ba484ea8..64317b6cbe 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -30,6 +30,7 @@ /stdlib/a64l /stdio/fwrite /stdio/all +/stdio/freopen /string/strncmp /string/strcspn /string/strchr diff --git a/tests/Makefile b/tests/Makefile index f5e47bb4e4..92e3f70f46 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -24,6 +24,7 @@ EXPECT_BINS=\ sprintf \ stdio/fwrite \ stdio/all \ + stdio/freopen \ stdlib/strtol \ stdlib/a64l \ string/strncmp \ diff --git a/tests/stdio/freopen.c b/tests/stdio/freopen.c new file mode 100644 index 0000000000..7bf3b64f20 --- /dev/null +++ b/tests/stdio/freopen.c @@ -0,0 +1,9 @@ +#include + +int main(int argc, char ** argv) { + freopen("stdio/stdio.in", "r", stdin); + char in[6]; + fgets(in, 6, stdin); + printf("%s\n", in); // should print Hello + return 0; +} From 81d96c214a9cc9ce9af7f48558db635db53a6062 Mon Sep 17 00:00:00 2001 From: Tom Almeida Date: Fri, 16 Mar 2018 20:47:32 +0800 Subject: [PATCH 10/16] Ran formatting for freopen() --- src/stdio/src/lib.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/stdio/src/lib.rs b/src/stdio/src/lib.rs index 4748fdfb38..8c291ca281 100644 --- a/src/stdio/src/lib.rs +++ b/src/stdio/src/lib.rs @@ -320,7 +320,8 @@ pub unsafe extern "C" fn freopen( flockfile(stream); helpers::fflush_unlocked(stream); - if filename.is_null() { // Reopen stream in new mode + if filename.is_null() { + // Reopen stream in new mode if flags & fcntl::O_CLOEXEC > 0 { fcntl::sys_fcntl((*stream).fd, fcntl::F_SETFD, fcntl::FD_CLOEXEC); } @@ -339,7 +340,9 @@ pub unsafe extern "C" fn freopen( } if (*new).fd == (*stream).fd { (*new).fd = -1; - } else if platform::dup2((*new).fd, (*stream).fd) < 0 || fcntl::sys_fcntl((*stream).fd, fcntl::F_SETFL, flags&fcntl::O_CLOEXEC) < 0 { + } else if platform::dup2((*new).fd, (*stream).fd) < 0 + || fcntl::sys_fcntl((*stream).fd, fcntl::F_SETFL, flags & fcntl::O_CLOEXEC) < 0 + { fclose(new); funlockfile(stream); fclose(stream); From 659d3d10428c4bd6d5f6c4ffaa298a77e2f37ce1 Mon Sep 17 00:00:00 2001 From: Tom Almeida Date: Fri, 16 Mar 2018 23:14:43 +0800 Subject: [PATCH 11/16] Changed object type of function pointers from Option<*const (Fn(...))> to Option for readability --- src/stdio/src/default.rs | 6 +++--- src/stdio/src/helpers.rs | 14 +++++++------- src/stdio/src/internal.rs | 4 ++-- src/stdio/src/lib.rs | 16 ++++++++-------- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/stdio/src/default.rs b/src/stdio/src/default.rs index 8ba56533de..efeb05c9a4 100644 --- a/src/stdio/src/default.rs +++ b/src/stdio/src/default.rs @@ -20,7 +20,7 @@ static mut default_stdin: FILE = FILE { unget: UNGET, lock: AtomicBool::new(false), write: None, - read: Some(&internal::stdio_read), + read: Some(internal::stdio_read), seek: None, }; @@ -41,7 +41,7 @@ static mut default_stdout: FILE = FILE { buf_char: b'\n' as i8, unget: 0, lock: AtomicBool::new(false), - write: Some(&internal::stdio_write), + write: Some(internal::stdio_write), read: None, seek: None, }; @@ -63,7 +63,7 @@ static mut default_stderr: FILE = FILE { buf_char: -1, unget: 0, lock: AtomicBool::new(false), - write: Some(&internal::stdio_write), + write: Some(internal::stdio_write), read: None, seek: None, }; diff --git a/src/stdio/src/helpers.rs b/src/stdio/src/helpers.rs index 267d49da39..5342490269 100644 --- a/src/stdio/src/helpers.rs +++ b/src/stdio/src/helpers.rs @@ -78,9 +78,9 @@ pub unsafe fn _fdopen(fd: c_int, mode: *const c_char) -> *mut FILE { buf_char: -1, unget: UNGET, lock: AtomicBool::new(false), - write: Some(&internal::stdio_write), - read: Some(&internal::stdio_read), - seek: Some(&internal::stdio_seek), + write: Some(internal::stdio_write), + read: Some(internal::stdio_read), + seek: Some(internal::stdio_seek), }; file } @@ -97,7 +97,7 @@ pub unsafe fn fwritex(buf: *const u8, l: size_t, stream: *mut FILE) -> size_t { } if l > (*stream).wend as usize - (*stream).wpos as usize { // We can't fit all of buf in the buffer - return (*stream_write)(stream, buf, l); + return stream_write(stream, buf, l); } let i = if (*stream).buf_char >= 0 { @@ -106,7 +106,7 @@ pub unsafe fn fwritex(buf: *const u8, l: size_t, stream: *mut FILE) -> size_t { i -= 1; } if i > 0 { - let n = (*stream_write)(stream, buf, i); + let n = stream_write(stream, buf, i); if n < i { return n; } @@ -131,7 +131,7 @@ pub unsafe fn fwritex(buf: *const u8, l: size_t, stream: *mut FILE) -> size_t { pub unsafe fn fflush_unlocked(stream: *mut FILE) -> c_int { if (*stream).wpos > (*stream).wbase { if let Some(f) = (*stream).write { - (*f)(stream, ptr::null(), 0); + f(stream, ptr::null(), 0); if (*stream).wpos.is_null() { return -1; } @@ -142,7 +142,7 @@ pub unsafe fn fflush_unlocked(stream: *mut FILE) -> c_int { if (*stream).rpos < (*stream).rend { if let Some(s) = (*stream).seek { - (*s)( + s( stream, (*stream).rpos as i64 - (*stream).rend as i64, SEEK_CUR, diff --git a/src/stdio/src/internal.rs b/src/stdio/src/internal.rs index f9ef756f64..99005e87a6 100644 --- a/src/stdio/src/internal.rs +++ b/src/stdio/src/internal.rs @@ -83,7 +83,7 @@ pub unsafe fn to_read(stream: *mut FILE) -> bool { if (*stream).wpos > (*stream).wbase { if let Some(f) = (*stream).write { - (*f)(stream, ptr::null(), 0); + f(stream, ptr::null(), 0); } } (*stream).wpos = ptr::null_mut(); @@ -123,7 +123,7 @@ pub unsafe fn to_write(stream: *mut FILE) -> bool { pub unsafe fn ftello(stream: *mut FILE) -> off_t { if let Some(s) = (*stream).seek { - let pos = (*s)( + let pos = s( stream, 0, if ((*stream).flags & constants::F_APP > 0) && (*stream).wpos > (*stream).wbase { diff --git a/src/stdio/src/lib.rs b/src/stdio/src/lib.rs index 8c291ca281..df5e9b6db3 100644 --- a/src/stdio/src/lib.rs +++ b/src/stdio/src/lib.rs @@ -50,9 +50,9 @@ pub struct FILE { buf_char: i8, lock: AtomicBool, unget: size_t, - write: Option<*const (Fn(*mut FILE, *const u8, usize) -> size_t)>, - read: Option<*const (Fn(*mut FILE, *mut u8, usize) -> size_t)>, - seek: Option<*const (Fn(*mut FILE, off_t, c_int) -> off_t)>, + write: Option<(fn(*mut FILE, *const u8, usize) -> size_t)>, + read: Option<(fn(*mut FILE, *mut u8, usize) -> size_t)>, + seek: Option<(fn(*mut FILE, off_t, c_int) -> off_t)>, } /// Clears EOF and ERR indicators on a stream @@ -291,7 +291,7 @@ pub unsafe extern "C" fn fread( 0 } else { if let Some(f) = (*stream).read { - (*f)(stream, dest, l as usize) + f(stream, dest, l as usize) } else { 0 } @@ -378,7 +378,7 @@ pub unsafe extern "C" fn fseeko(stream: *mut FILE, offset: off_t, whence: c_int) } if (*stream).wpos > (*stream).wbase { if let Some(f) = (*stream).write { - (*f)(stream, ptr::null(), 0); + f(stream, ptr::null(), 0); if (*stream).wpos.is_null() { return -1; } @@ -388,7 +388,7 @@ pub unsafe extern "C" fn fseeko(stream: *mut FILE, offset: off_t, whence: c_int) (*stream).wend = ptr::null_mut(); (*stream).wbase = ptr::null_mut(); if let Some(s) = (*stream).seek { - if (*s)(stream, off, whence) < 0 { + if s(stream, off, whence) < 0 { return -1; } } else { @@ -481,7 +481,7 @@ pub unsafe extern "C" fn getc_unlocked(stream: *mut FILE) -> c_int { } else { if let Some(read) = (*stream).read { let mut c = 0u8; - if !internal::to_read(stream) && (*read)(stream, &mut c, 1) == 1 { + if !internal::to_read(stream) && read(stream, &mut c, 1) == 1 { c as c_int } else { -1 @@ -575,7 +575,7 @@ pub unsafe extern "C" fn putc_unlocked(c: c_int, stream: *mut FILE) -> c_int { *(*stream).wpos = c as u8; (*stream).wpos = (*stream).wpos.add(1); c - } else if (*write)(stream, &c as *const i32 as *const _, 1) != 1 { + } else if write(stream, &c as *const i32 as *const _, 1) != 1 { -1 } else { c From 25501b364006f833a122bd0491d2406031758924 Mon Sep 17 00:00:00 2001 From: Tom Almeida Date: Sun, 18 Mar 2018 00:18:14 +0800 Subject: [PATCH 12/16] Changed redox lseek to have the same function signature as the linux version --- src/platform/src/redox/mod.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/platform/src/redox/mod.rs b/src/platform/src/redox/mod.rs index 869e52b42e..0db7bfaac0 100644 --- a/src/platform/src/redox/mod.rs +++ b/src/platform/src/redox/mod.rs @@ -125,8 +125,12 @@ pub fn link(path1: *const c_char, path2: *const c_char) -> c_int { e(unsafe { syscall::link(path1.as_ptr(), path2.as_ptr()) }) as c_int } -pub fn lseek(fd: c_int, offset: isize, whence: usize) -> c_int { - e(syscall::lseek(fd as usize, offset, whence)) as c_int +pub fn lseek(fd: c_int, offset: off_t, whence: c_int) -> off_t { + e(syscall::lseek( + fd as usize, + offset as isize, + whence as usize, + )) as off_t } pub fn mkdir(path: *const c_char, mode: mode_t) -> c_int { From d7965f25983108e82ffb6ff5efe80da310f8fc13 Mon Sep 17 00:00:00 2001 From: Tom Almeida Date: Sun, 18 Mar 2018 00:19:08 +0800 Subject: [PATCH 13/16] Made it so that AtomicBool exports as volatile char --- src/stdio/cbindgen.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/stdio/cbindgen.toml b/src/stdio/cbindgen.toml index 58483d52ac..3f13ef55cb 100644 --- a/src/stdio/cbindgen.toml +++ b/src/stdio/cbindgen.toml @@ -5,3 +5,6 @@ language = "C" [enum] prefix_with_name = true + +[export.rename] +"AtomicBool" = "volatile char" From c24d1e2b361eae5227d2294d98e45d0658374785 Mon Sep 17 00:00:00 2001 From: Tom Almeida Date: Sun, 18 Mar 2018 00:20:21 +0800 Subject: [PATCH 14/16] Removed all function pointers in FILE, moved internal functions to be member functions of FILE, made relevant *mut FILEs into &mut FILE and made suitable functions safe again --- src/stdio/src/default.rs | 9 - src/stdio/src/helpers.rs | 108 ++++----- src/stdio/src/internal.rs | 156 +------------ src/stdio/src/lib.rs | 465 ++++++++++++++++++++++++-------------- 4 files changed, 360 insertions(+), 378 deletions(-) diff --git a/src/stdio/src/default.rs b/src/stdio/src/default.rs index efeb05c9a4..840a6fa07f 100644 --- a/src/stdio/src/default.rs +++ b/src/stdio/src/default.rs @@ -19,9 +19,6 @@ static mut default_stdin: FILE = FILE { buf_char: -1, unget: UNGET, lock: AtomicBool::new(false), - write: None, - read: Some(internal::stdio_read), - seek: None, }; #[allow(non_upper_case_globals)] @@ -41,9 +38,6 @@ static mut default_stdout: FILE = FILE { buf_char: b'\n' as i8, unget: 0, lock: AtomicBool::new(false), - write: Some(internal::stdio_write), - read: None, - seek: None, }; #[allow(non_upper_case_globals)] @@ -63,9 +57,6 @@ static mut default_stderr: FILE = FILE { buf_char: -1, unget: 0, lock: AtomicBool::new(false), - write: Some(internal::stdio_write), - read: None, - seek: None, }; // Don't ask me how the casting below works, I have no idea diff --git a/src/stdio/src/helpers.rs b/src/stdio/src/helpers.rs index 5342490269..378d2af3b6 100644 --- a/src/stdio/src/helpers.rs +++ b/src/stdio/src/helpers.rs @@ -1,5 +1,4 @@ use super::{internal, BUFSIZ, FILE, UNGET}; -use compiler_builtins::mem::memset; use stdlib::calloc; use core::{mem, ptr}; use core::sync::atomic::AtomicBool; @@ -78,82 +77,71 @@ pub unsafe fn _fdopen(fd: c_int, mode: *const c_char) -> *mut FILE { buf_char: -1, unget: UNGET, lock: AtomicBool::new(false), - write: Some(internal::stdio_write), - read: Some(internal::stdio_read), - seek: Some(internal::stdio_seek), }; file } /// Write buffer `buf` of length `l` into `stream` -pub unsafe fn fwritex(buf: *const u8, l: size_t, stream: *mut FILE) -> size_t { - use compiler_builtins::mem::memcpy; - let mut buf = buf; +pub fn fwritex(buf: *const u8, l: size_t, stream: &mut FILE) -> size_t { + use core::ptr::copy_nonoverlapping; + use core::slice; + + let buf: &'static [u8] = unsafe { slice::from_raw_parts(buf, l) }; let mut l = l; - if let Some(stream_write) = (*stream).write { - if (*stream).wend.is_null() && !internal::to_write(stream) { - // We can't write to this stream - return 0; - } - if l > (*stream).wend as usize - (*stream).wpos as usize { - // We can't fit all of buf in the buffer - return stream_write(stream, buf, l); - } + let mut advance = 0; - let i = if (*stream).buf_char >= 0 { - let mut i = l; - while i > 0 && *buf.offset(i as isize - 1) != b'\n' { - i -= 1; - } - if i > 0 { - let n = stream_write(stream, buf, i); - if n < i { - return n; - } - buf = buf.add(i); - l -= i; - } - i - } else { - 0 - }; - - memcpy((*stream).wpos, buf, l); - (*stream).wpos = (*stream).wpos.add(l); - l + i - } else { + if stream.wend.is_null() && !stream.can_write() { // We can't write to this stream - 0 + return 0; } + if l > stream.wend as usize - stream.wpos as usize { + // We can't fit all of buf in the buffer + return stream.write(buf); + } + + let i = if stream.buf_char >= 0 { + let mut i = l; + while i > 0 && buf[i - 1] != b'\n' { + i -= 1; + } + if i > 0 { + let n = stream.write(buf); + if n < i { + return n; + } + advance += i; + l -= i; + } + i + } else { + 0 + }; + + unsafe { + // Copy and reposition + copy_nonoverlapping(&buf[advance..] as *const _ as *const u8, stream.wpos, l); + stream.wpos = stream.wpos.add(l); + } + l + i } /// Flush `stream` without locking it. -pub unsafe fn fflush_unlocked(stream: *mut FILE) -> c_int { - if (*stream).wpos > (*stream).wbase { - if let Some(f) = (*stream).write { - f(stream, ptr::null(), 0); - if (*stream).wpos.is_null() { - return -1; - } - } else { +pub fn fflush_unlocked(stream: &mut FILE) -> c_int { + if stream.wpos > stream.wbase { + stream.write(&[]); + if stream.wpos.is_null() { return -1; } } - if (*stream).rpos < (*stream).rend { - if let Some(s) = (*stream).seek { - s( - stream, - (*stream).rpos as i64 - (*stream).rend as i64, - SEEK_CUR, - ); - } + if stream.rpos < stream.rend { + stream.seek(stream.rpos as i64 - stream.rend as i64, SEEK_CUR); } - (*stream).wpos = ptr::null_mut(); - (*stream).wend = ptr::null_mut(); - (*stream).wbase = ptr::null_mut(); - (*stream).rpos = ptr::null_mut(); - (*stream).rend = ptr::null_mut(); + stream.wpos = ptr::null_mut(); + stream.wend = ptr::null_mut(); + stream.wbase = ptr::null_mut(); + stream.rpos = ptr::null_mut(); + stream.rend = ptr::null_mut(); 0 } diff --git a/src/stdio/src/internal.rs b/src/stdio/src/internal.rs index 99005e87a6..35eb5f47e8 100644 --- a/src/stdio/src/internal.rs +++ b/src/stdio/src/internal.rs @@ -3,151 +3,17 @@ use platform; use platform::types::*; use core::{mem, ptr, slice}; -pub fn stdio_read(stream: *mut FILE, buf: *mut u8, size: usize) -> usize { - unsafe { - let mut buff = slice::from_raw_parts_mut(buf, size - !((*stream).buf_size == 0) as usize); - let mut file_buf = slice::from_raw_parts_mut((*stream).buf, (*stream).buf_size); - let mut file = platform::FileReader((*stream).fd); - let count = if buff.len() == 0 { - file.read(&mut file_buf) +pub fn ftello(stream: &mut FILE) -> off_t { + let pos = stream.seek( + 0, + if (stream.flags & constants::F_APP > 0) && stream.wpos > stream.wbase { + constants::SEEK_END } else { - file.read(&mut buff) + file.read(&mut file_buf) - }; - mem::forget(buff); - mem::forget(file_buf); - if count <= 0 { - (*stream).flags |= if count == 0 { - constants::F_EOF - } else { - constants::F_ERR - }; - return 0; - } - if count as usize <= size { - return count as usize; - } - (*stream).rpos = (*stream).buf; - (*stream).rend = (*stream).buf.offset(count); - *buf.offset(size as isize - 1) = *(*stream).rpos; - (*stream).rpos = (*stream).rpos.add(1); + constants::SEEK_CUR + }, + ); + if pos < 0 { + return pos; } - size -} - -pub fn stdio_write(stream: *mut FILE, buf: *const u8, size: usize) -> usize { - unsafe { - let len = (*stream).wpos as usize - (*stream).wbase as usize; - let mut advance = 0; - let mut f_buf = slice::from_raw_parts((*stream).wbase, len); - let mut buff = slice::from_raw_parts(buf, size); - let mut f_filled = false; - let mut rem = f_buf.len() + buff.len(); - let mut file = platform::FileWriter((*stream).fd); - loop { - let mut count = if f_filled { - file.write(&f_buf[advance..]) - } else { - file.write(&f_buf[advance..]) + file.write(buff) - }; - if count == rem as isize { - (*stream).wend = (*stream).buf.add((*stream).buf_size - 1); - (*stream).wpos = (*stream).buf; - (*stream).wbase = (*stream).buf; - return size; - } - if count < 0 { - (*stream).wpos = ptr::null_mut(); - (*stream).wbase = ptr::null_mut(); - (*stream).wend = ptr::null_mut(); - (*stream).flags |= constants::F_ERR; - return 0; - } - rem -= count as usize; - if count as usize > len { - count -= len as isize; - f_buf = buff; - f_filled = true; - advance = 0; - } - advance += count as usize; - } - } -} - -pub unsafe fn to_read(stream: *mut FILE) -> bool { - if (*stream).flags & constants::F_BADJ > 0 { - // Static and needs unget region - (*stream).buf = (*stream).buf.add((*stream).unget); - (*stream).flags &= !constants::F_BADJ; - } - - if (*stream).wpos > (*stream).wbase { - if let Some(f) = (*stream).write { - f(stream, ptr::null(), 0); - } - } - (*stream).wpos = ptr::null_mut(); - (*stream).wbase = ptr::null_mut(); - (*stream).wend = ptr::null_mut(); - if (*stream).flags & constants::F_NORD > 0 { - (*stream).flags |= constants::F_ERR; - return true; - } - (*stream).rpos = (*stream).buf.offset((*stream).buf_size as isize - 1); - (*stream).rend = (*stream).buf.offset((*stream).buf_size as isize - 1); - if (*stream).flags & constants::F_EOF > 0 { - true - } else { - false - } -} - -pub unsafe fn to_write(stream: *mut FILE) -> bool { - if (*stream).flags & constants::F_BADJ > 0 { - // Static and needs unget region - (*stream).buf = (*stream).buf.add((*stream).unget); - (*stream).flags &= !constants::F_BADJ; - } - - if (*stream).flags & constants::F_NOWR > 0 { - (*stream).flags &= constants::F_ERR; - return false; - } - (*stream).rpos = ptr::null_mut(); - (*stream).rend = ptr::null_mut(); - (*stream).wpos = (*stream).buf; - (*stream).wbase = (*stream).buf; - (*stream).wend = (*stream).buf.offset((*stream).buf_size as isize - 1); - return true; -} - -pub unsafe fn ftello(stream: *mut FILE) -> off_t { - if let Some(s) = (*stream).seek { - let pos = s( - stream, - 0, - if ((*stream).flags & constants::F_APP > 0) && (*stream).wpos > (*stream).wbase { - constants::SEEK_END - } else { - constants::SEEK_CUR - }, - ); - if pos < 0 { - return pos; - } - pos - ((*stream).rend as i64 - (*stream).rpos as i64) - + ((*stream).wpos as i64 - (*stream).wbase as i64) - } else { - -1 - } -} - -#[cfg(target_os = "linux")] -pub fn stdio_seek(stream: *mut FILE, off: off_t, whence: c_int) -> off_t { - unsafe { platform::lseek((*stream).fd, off, whence) } -} - -#[cfg(target_os = "redox")] -pub fn stdio_seek(stream: *mut FILE, off: off_t, whence: c_int) -> off_t { - unsafe { platform::lseek((*stream).fd, off as isize, whence as usize) as off_t } + pos - (stream.rend as i64 - stream.rpos as i64) + (stream.wpos as i64 - stream.wbase as i64) } diff --git a/src/stdio/src/lib.rs b/src/stdio/src/lib.rs index df5e9b6db3..fcae989865 100644 --- a/src/stdio/src/lib.rs +++ b/src/stdio/src/lib.rs @@ -13,7 +13,7 @@ extern crate va_list as vl; use core::str; use core::ptr; -use core::fmt::Write; +use core::fmt::{Error, Result, Write}; use core::sync::atomic::{AtomicBool, Ordering}; use platform::types::*; @@ -33,9 +33,6 @@ mod helpers; mod internal; -/// The structure of this struct is likely to change. -/// all of the _pos pointers are likely to change to isize offsets and its possible that the -/// function pointers will be removed #[repr(C)] pub struct FILE { flags: c_int, @@ -50,15 +47,140 @@ pub struct FILE { buf_char: i8, lock: AtomicBool, unget: size_t, - write: Option<(fn(*mut FILE, *const u8, usize) -> size_t)>, - read: Option<(fn(*mut FILE, *mut u8, usize) -> size_t)>, - seek: Option<(fn(*mut FILE, off_t, c_int) -> off_t)>, +} + +impl FILE { + pub fn can_read(&mut self) -> bool { + if self.flags & constants::F_BADJ > 0 { + // Static and needs unget region + self.buf = unsafe { self.buf.add(self.unget) }; + self.flags &= !constants::F_BADJ; + } + + if self.wpos > self.wbase { + self.write(&[]); + } + self.wpos = ptr::null_mut(); + self.wbase = ptr::null_mut(); + self.wend = ptr::null_mut(); + if self.flags & constants::F_NORD > 0 { + self.flags |= constants::F_ERR; + return false; + } + self.rpos = unsafe { self.buf.offset(self.buf_size as isize - 1) }; + self.rend = unsafe { self.buf.offset(self.buf_size as isize - 1) }; + if self.flags & constants::F_EOF > 0 { + false + } else { + true + } + } + pub fn can_write(&mut self) -> bool { + if self.flags & constants::F_BADJ > 0 { + // Static and needs unget region + self.buf = unsafe { self.buf.add(self.unget) }; + self.flags &= !constants::F_BADJ; + } + + if self.flags & constants::F_NOWR > 0 { + self.flags &= constants::F_ERR; + return false; + } + // Buffer repositioning + self.rpos = ptr::null_mut(); + self.rend = ptr::null_mut(); + self.wpos = self.buf; + self.wbase = self.buf; + self.wend = unsafe { self.buf.offset(self.buf_size as isize - 1) }; + return true; + } + pub fn write(&mut self, to_write: &[u8]) -> usize { + use core::slice; + use core::mem; + let len = self.wpos as usize - self.wbase as usize; + let mut advance = 0; + let mut f_buf: &'static _ = unsafe { slice::from_raw_parts(self.wbase, len) }; + let mut f_filled = false; + let mut rem = f_buf.len() + to_write.len(); + loop { + let mut count = if f_filled { + platform::write(self.fd, &f_buf[advance..]) + } else { + platform::write(self.fd, &f_buf[advance..]) + platform::write(self.fd, to_write) + }; + if count == rem as isize { + self.wend = unsafe { self.buf.add(self.buf_size - 1) }; + self.wpos = self.buf; + self.wbase = self.buf; + return to_write.len(); + } + if count < 0 { + self.wpos = ptr::null_mut(); + self.wbase = ptr::null_mut(); + self.wend = ptr::null_mut(); + self.flags |= constants::F_ERR; + return 0; + } + rem -= count as usize; + if count as usize > len { + count -= len as isize; + f_buf = unsafe { mem::transmute(to_write) }; + f_filled = true; + advance = 0; + } + advance += count as usize; + } + } + pub fn read(&mut self, buf: &mut [u8]) -> usize { + use core::slice; + // let buff = slice::from_raw_parts_mut(buf, size - !((*stream).buf_size == 0) as usize); + let adj = !(self.buf_size == 0) as usize; + let mut file_buf: &'static mut _ = + unsafe { slice::from_raw_parts_mut(self.buf, self.buf_size) }; + let count = if buf.len() <= 1 + adj { + platform::read(self.fd, file_buf) + } else { + platform::read(self.fd, buf) + platform::read(self.fd, file_buf) + }; + if count <= 0 { + self.flags |= if count == 0 { + constants::F_EOF + } else { + constants::F_ERR + }; + return 0; + } + if count as usize <= buf.len() - adj { + return count as usize; + } + unsafe { + // Adjust pointers + self.rpos = self.buf; + self.rend = self.buf.offset(count); + buf[buf.len() - 1] = *self.rpos; + self.rpos = self.rpos.add(1); + } + buf.len() + } + pub fn seek(&self, off: off_t, whence: c_int) -> off_t { + unsafe { platform::lseek(self.fd, off, whence) } + } +} +impl Write for FILE { + fn write_str(&mut self, s: &str) -> Result { + let s = s.as_bytes(); + if self.write(s) != s.len() { + Err(Error) + } else { + Ok(()) + } + } } /// Clears EOF and ERR indicators on a stream #[no_mangle] -pub unsafe extern "C" fn clearerr(stream: *mut FILE) { - (*stream).flags &= !(F_EOF & F_ERR); +pub extern "C" fn clearerr(stream: &mut FILE) { + stream.flags &= !(F_EOF & F_ERR); } #[no_mangle] @@ -76,37 +198,39 @@ pub extern "C" fn cuserid(s: *mut c_char) -> *mut c_char { /// descriptor will be closed, so if it is important that the file be written to, use `fflush()` /// prior to using this function. #[no_mangle] -pub unsafe extern "C" fn fclose(stream: *mut FILE) -> c_int { +pub extern "C" fn fclose(stream: &mut FILE) -> c_int { use stdlib::free; flockfile(stream); - let r = helpers::fflush_unlocked(stream) | platform::close((*stream).fd); - if (*stream).flags & constants::F_PERM == 0 { + let r = helpers::fflush_unlocked(stream) | platform::close(stream.fd); + if stream.flags & constants::F_PERM == 0 { // Not one of stdin, stdout or stderr - free(stream as *mut _); + unsafe { + free(stream as *mut _ as *mut _); + } } r } /// Open a file from a file descriptor #[no_mangle] -pub unsafe extern "C" fn fdopen(fildes: c_int, mode: *const c_char) -> *mut FILE { - helpers::_fdopen(fildes, mode) +pub extern "C" fn fdopen(fildes: c_int, mode: *const c_char) -> *mut FILE { + unsafe { helpers::_fdopen(fildes, mode) } } /// Check for EOF #[no_mangle] -pub unsafe extern "C" fn feof(stream: *mut FILE) -> c_int { +pub extern "C" fn feof(stream: &mut FILE) -> c_int { flockfile(stream); - let ret = (*stream).flags & F_EOF; + let ret = stream.flags & F_EOF; funlockfile(stream); ret } /// Check for ERR #[no_mangle] -pub unsafe extern "C" fn ferror(stream: *mut FILE) -> c_int { +pub extern "C" fn ferror(stream: &mut FILE) -> c_int { flockfile(stream); - let ret = (*stream).flags & F_ERR; + let ret = stream.flags & F_ERR; funlockfile(stream); ret } @@ -115,7 +239,7 @@ pub unsafe extern "C" fn ferror(stream: *mut FILE) -> c_int { /// Ensure the file is unlocked before calling this function, as it will attempt to lock the file /// itself. #[no_mangle] -pub unsafe extern "C" fn fflush(stream: *mut FILE) -> c_int { +pub unsafe extern "C" fn fflush(stream: &mut FILE) -> c_int { flockfile(stream); let ret = helpers::fflush_unlocked(stream); @@ -126,7 +250,7 @@ pub unsafe extern "C" fn fflush(stream: *mut FILE) -> c_int { /// Get a single char from a stream #[no_mangle] -pub unsafe extern "C" fn fgetc(stream: *mut FILE) -> c_int { +pub extern "C" fn fgetc(stream: &mut FILE) -> c_int { flockfile(stream); let c = getc_unlocked(stream); funlockfile(stream); @@ -135,46 +259,58 @@ pub unsafe extern "C" fn fgetc(stream: *mut FILE) -> c_int { /// Get the position of the stream and store it in pos #[no_mangle] -pub unsafe extern "C" fn fgetpos(stream: *mut FILE, pos: *mut fpos_t) -> c_int { +pub extern "C" fn fgetpos(stream: &mut FILE, pos: *mut fpos_t) -> c_int { let off = internal::ftello(stream); if off < 0 { return -1; } - (*pos) = off; + unsafe { + (*pos) = off; + } 0 } /// Get a string from the stream #[no_mangle] -pub unsafe extern "C" fn fgets(s: *mut c_char, n: c_int, stream: *mut FILE) -> *mut c_char { +pub extern "C" fn fgets(s: *mut c_char, n: c_int, stream: &mut FILE) -> *mut c_char { use string::memchr; - use compiler_builtins::mem::memcpy; + use core::ptr::copy_nonoverlapping; + flockfile(stream); let mut ptr = s as *mut u8; let mut n = n; + if n <= 1 { funlockfile(stream); if n == 0 { return ptr::null_mut(); } - (*s) = b'\0' as i8; + unsafe { + (*s) = b'\0' as i8; + } return s; } while n > 0 { - let z = memchr( - (*stream).rpos as *const c_void, - b'\n' as c_int, - (*stream).rend as usize - (*stream).rpos as usize, - ) as *mut u8; + let z = unsafe { + memchr( + stream.rpos as *const c_void, + b'\n' as c_int, + stream.rend as usize - stream.rpos as usize, + ) as *mut u8 + }; let k = if z.is_null() { - (*stream).rend as usize - (*stream).rpos as usize + stream.rend as usize - stream.rpos as usize } else { - z as usize - (*stream).rpos as usize + 1 + z as usize - stream.rpos as usize + 1 }; let k = if k as i32 > n { n as usize } else { k }; - memcpy(ptr, (*stream).rpos, k); - (*stream).rpos = (*stream).rpos.add(k); - ptr = ptr.add(k); + unsafe { + // Copy + copy_nonoverlapping(stream.rpos, ptr, k); + // Reposition pointers + stream.rpos = stream.rpos.add(k); + ptr = ptr.add(k); + } n -= k as i32; if !z.is_null() || n < 1 { break; @@ -184,14 +320,21 @@ pub unsafe extern "C" fn fgets(s: *mut c_char, n: c_int, stream: *mut FILE) -> * break; } n -= 1; - *ptr = c as u8; - ptr = ptr.add(1); + + unsafe { + // Pointer stuff + *ptr = c as u8; + ptr = ptr.add(1); + } + if c as u8 == b'\n' { break; } } if !s.is_null() { - *ptr = 0; + unsafe { + *ptr = 0; + } } funlockfile(stream); s @@ -199,17 +342,17 @@ pub unsafe extern "C" fn fgets(s: *mut c_char, n: c_int, stream: *mut FILE) -> * /// Get the underlying file descriptor #[no_mangle] -pub unsafe extern "C" fn fileno(stream: *mut FILE) -> c_int { +pub extern "C" fn fileno(stream: &mut FILE) -> c_int { flockfile(stream); funlockfile(stream); - (*stream).fd + stream.fd } /// Lock the file /// Do not call any functions other than those with the `_unlocked` postfix while the file is /// locked #[no_mangle] -pub unsafe extern "C" fn flockfile(file: *mut FILE) { +pub extern "C" fn flockfile(file: &mut FILE) { while ftrylockfile(file) != 0 {} } @@ -244,7 +387,7 @@ pub unsafe extern "C" fn fopen(filename: *const c_char, mode: *const c_char) -> /// Insert a character into the stream #[no_mangle] -pub unsafe extern "C" fn fputc(c: c_int, stream: *mut FILE) -> c_int { +pub extern "C" fn fputc(c: c_int, stream: &mut FILE) -> c_int { flockfile(stream); let c = putc_unlocked(c, stream); funlockfile(stream); @@ -253,48 +396,44 @@ pub unsafe extern "C" fn fputc(c: c_int, stream: *mut FILE) -> c_int { /// Insert a string into a stream #[no_mangle] -pub unsafe extern "C" fn fputs(s: *const c_char, stream: *mut FILE) -> c_int { +pub extern "C" fn fputs(s: *const c_char, stream: &mut FILE) -> c_int { extern "C" { fn strlen(s: *const c_char) -> size_t; } - let len = strlen(s); + let len = unsafe { strlen(s) }; (fwrite(s as *const c_void, 1, len, stream) == len) as c_int - 1 } /// Read `nitems` of size `size` into `ptr` from `stream` #[no_mangle] -pub unsafe extern "C" fn fread( - ptr: *mut c_void, - size: usize, - nitems: usize, - stream: *mut FILE, -) -> usize { - use compiler_builtins::mem::memcpy; +pub extern "C" fn fread(ptr: *mut c_void, size: usize, nitems: usize, stream: &mut FILE) -> usize { + use core::ptr::copy_nonoverlapping; + use core::slice; let mut dest = ptr as *mut u8; let len = size * nitems; let mut l = len as isize; flockfile(stream); - if (*stream).rend > (*stream).rpos { + if stream.rend > stream.rpos { // We have some buffered data that can be read - let diff = (*stream).rend as usize - (*stream).rpos as usize; + let diff = stream.rend as usize - stream.rpos as usize; let k = if diff < l as usize { diff } else { l as usize }; - memcpy(dest, (*stream).rpos, k); - (*stream).rpos = (*stream).rpos.add(k); - dest = dest.add(k); + unsafe { + // Copy data + copy_nonoverlapping(stream.rpos, dest, k); + // Reposition pointers + stream.rpos = stream.rpos.add(k); + dest = dest.add(k); + } l -= k as isize; } while l > 0 { - let k = if internal::to_read(stream) { + let k = if !stream.can_read() { 0 } else { - if let Some(f) = (*stream).read { - f(stream, dest, l as usize) - } else { - 0 - } + stream.read(unsafe { slice::from_raw_parts_mut(dest, l as usize) }) }; if k == 0 { @@ -303,7 +442,10 @@ pub unsafe extern "C" fn fread( } l -= k as isize; - dest = dest.add(k); + unsafe { + // Reposition + dest = dest.add(k); + } } funlockfile(stream); @@ -311,47 +453,45 @@ pub unsafe extern "C" fn fread( } #[no_mangle] -pub unsafe extern "C" fn freopen( +pub extern "C" fn freopen( filename: *const c_char, mode: *const c_char, - stream: *mut FILE, + stream: &mut FILE, ) -> *mut FILE { - let mut flags = helpers::parse_mode_flags(mode); + let mut flags = unsafe { helpers::parse_mode_flags(mode) }; flockfile(stream); helpers::fflush_unlocked(stream); if filename.is_null() { // Reopen stream in new mode if flags & fcntl::O_CLOEXEC > 0 { - fcntl::sys_fcntl((*stream).fd, fcntl::F_SETFD, fcntl::FD_CLOEXEC); + fcntl::sys_fcntl(stream.fd, fcntl::F_SETFD, fcntl::FD_CLOEXEC); } flags &= !(fcntl::O_CREAT | fcntl::O_EXCL | fcntl::O_CLOEXEC); - if fcntl::sys_fcntl((*stream).fd, fcntl::F_SETFL, flags) < 0 { + if fcntl::sys_fcntl(stream.fd, fcntl::F_SETFL, flags) < 0 { funlockfile(stream); fclose(stream); return ptr::null_mut(); } } else { - let new = fopen(filename, mode); + let new = unsafe { fopen(filename, mode) }; if new.is_null() { funlockfile(stream); fclose(stream); return ptr::null_mut(); } - if (*new).fd == (*stream).fd { - (*new).fd = -1; - } else if platform::dup2((*new).fd, (*stream).fd) < 0 - || fcntl::sys_fcntl((*stream).fd, fcntl::F_SETFL, flags & fcntl::O_CLOEXEC) < 0 + let new = unsafe { &mut *new }; // Should be safe, new is not null + if new.fd == stream.fd { + new.fd = -1; + } else if platform::dup2(new.fd, stream.fd) < 0 + || fcntl::sys_fcntl(stream.fd, fcntl::F_SETFL, flags & fcntl::O_CLOEXEC) < 0 { fclose(new); funlockfile(stream); fclose(stream); return ptr::null_mut(); } - (*stream).flags = ((*stream).flags & constants::F_PERM) | (*new).flags; - (*stream).read = (*new).read; - (*stream).write = (*new).write; - (*stream).seek = (*new).seek; + stream.flags = (stream.flags & constants::F_PERM) | new.flags; fclose(new); } funlockfile(stream); @@ -360,7 +500,7 @@ pub unsafe extern "C" fn freopen( /// Seek to an offset `offset` from `whence` #[no_mangle] -pub unsafe extern "C" fn fseek(stream: *mut FILE, offset: c_long, whence: c_int) -> c_int { +pub extern "C" fn fseek(stream: &mut FILE, offset: c_long, whence: c_int) -> c_int { if fseeko(stream, offset as off_t, whence) != -1 { return 0; } @@ -369,53 +509,47 @@ pub unsafe extern "C" fn fseek(stream: *mut FILE, offset: c_long, whence: c_int) /// Seek to an offset `offset` from `whence` #[no_mangle] -pub unsafe extern "C" fn fseeko(stream: *mut FILE, offset: off_t, whence: c_int) -> c_int { +pub extern "C" fn fseeko(stream: &mut FILE, offset: off_t, whence: c_int) -> c_int { let mut off = offset; flockfile(stream); // Adjust for what is currently in the buffer if whence == SEEK_CUR { - off -= ((*stream).rend as usize - (*stream).rpos as usize) as i64; + off -= (stream.rend as usize - stream.rpos as usize) as i64; } - if (*stream).wpos > (*stream).wbase { - if let Some(f) = (*stream).write { - f(stream, ptr::null(), 0); - if (*stream).wpos.is_null() { - return -1; - } - } - } - (*stream).wpos = ptr::null_mut(); - (*stream).wend = ptr::null_mut(); - (*stream).wbase = ptr::null_mut(); - if let Some(s) = (*stream).seek { - if s(stream, off, whence) < 0 { + if stream.wpos > stream.wbase { + stream.write(&[]); + if stream.wpos.is_null() { return -1; } - } else { + } + stream.wpos = ptr::null_mut(); + stream.wend = ptr::null_mut(); + stream.wbase = ptr::null_mut(); + if stream.seek(off, whence) < 0 { return -1; } - (*stream).rpos = ptr::null_mut(); - (*stream).rend = ptr::null_mut(); - (*stream).flags &= !F_EOF; + stream.rpos = ptr::null_mut(); + stream.rend = ptr::null_mut(); + stream.flags &= !F_EOF; funlockfile(stream); 0 } /// Seek to a position `pos` in the file from the beginning of the file #[no_mangle] -pub unsafe extern "C" fn fsetpos(stream: *mut FILE, pos: *const fpos_t) -> c_int { +pub unsafe extern "C" fn fsetpos(stream: &mut FILE, pos: *const fpos_t) -> c_int { fseek(stream, *pos as off_t, SEEK_SET) } /// Get the current position of the cursor in the file #[no_mangle] -pub unsafe extern "C" fn ftell(stream: *mut FILE) -> c_long { +pub unsafe extern "C" fn ftell(stream: &mut FILE) -> c_long { ftello(stream) as c_long } /// Get the current position of the cursor in the file #[no_mangle] -pub unsafe extern "C" fn ftello(stream: *mut FILE) -> off_t { +pub extern "C" fn ftello(stream: &mut FILE) -> off_t { flockfile(stream); let pos = internal::ftello(stream); funlockfile(stream); @@ -424,25 +558,23 @@ pub unsafe extern "C" fn ftello(stream: *mut FILE) -> off_t { /// Try to lock the file. Returns 0 for success, 1 for failure #[no_mangle] -pub unsafe extern "C" fn ftrylockfile(file: *mut FILE) -> c_int { - (*file) - .lock - .compare_and_swap(false, true, Ordering::Acquire) as c_int +pub extern "C" fn ftrylockfile(file: &mut FILE) -> c_int { + file.lock.compare_and_swap(false, true, Ordering::Acquire) as c_int } /// Unlock the file #[no_mangle] -pub unsafe extern "C" fn funlockfile(file: *mut FILE) { - (*file).lock.store(false, Ordering::Release); +pub extern "C" fn funlockfile(file: &mut FILE) { + file.lock.store(false, Ordering::Release); } /// Write `nitems` of size `size` from `ptr` to `stream` #[no_mangle] -pub unsafe extern "C" fn fwrite( +pub extern "C" fn fwrite( ptr: *const c_void, size: usize, nitems: usize, - stream: *mut FILE, + stream: &mut FILE, ) -> usize { let l = size * nitems; let nitems = if size == 0 { 0 } else { nitems }; @@ -458,7 +590,7 @@ pub unsafe extern "C" fn fwrite( /// Get a single char from a stream #[no_mangle] -pub unsafe extern "C" fn getc(stream: *mut FILE) -> c_int { +pub extern "C" fn getc(stream: &mut FILE) -> c_int { flockfile(stream); let c = getc_unlocked(stream); funlockfile(stream); @@ -468,24 +600,22 @@ pub unsafe extern "C" fn getc(stream: *mut FILE) -> c_int { /// Get a single char from `stdin` #[no_mangle] pub unsafe extern "C" fn getchar() -> c_int { - fgetc(stdin) + fgetc(&mut *stdin) } /// 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 (*stream).rpos < (*stream).rend { - let ret = *(*stream).rpos as c_int; - (*stream).rpos = (*stream).rpos.add(1); - ret +pub extern "C" fn getc_unlocked(stream: &mut FILE) -> c_int { + if stream.rpos < stream.rend { + unsafe { + let ret = *stream.rpos as c_int; + stream.rpos = stream.rpos.add(1); + ret + } } else { - if let Some(read) = (*stream).read { - let mut c = 0u8; - if !internal::to_read(stream) && read(stream, &mut c, 1) == 1 { - c as c_int - } else { - -1 - } + let mut c = [0u8; 1]; + if stream.can_read() && stream.read(&mut c) == 1 { + c[0] as c_int } else { -1 } @@ -495,19 +625,19 @@ pub unsafe extern "C" fn getc_unlocked(stream: *mut FILE) -> c_int { /// Get a char from `stdin` without locking `stdin` #[no_mangle] pub unsafe extern "C" fn getchar_unlocked() -> c_int { - getc_unlocked(stdin) + getc_unlocked(&mut *stdin) } /// Get a string from `stdin` #[no_mangle] pub unsafe extern "C" fn gets(s: *mut c_char) -> *mut c_char { use core::i32; - fgets(s, i32::MAX, stdin) + fgets(s, i32::MAX, &mut *stdin) } /// Get an integer from `stream` #[no_mangle] -pub unsafe extern "C" fn getw(stream: *mut FILE) -> c_int { +pub extern "C" fn getw(stream: &mut FILE) -> c_int { use core::mem; let mut ret: c_int = 0; if fread( @@ -524,7 +654,7 @@ pub unsafe extern "C" fn getw(stream: *mut FILE) -> c_int { } #[no_mangle] -pub extern "C" fn pclose(stream: *mut FILE) -> c_int { +pub extern "C" fn pclose(stream: &mut FILE) -> c_int { unimplemented!(); } @@ -547,7 +677,7 @@ pub extern "C" fn popen(command: *const c_char, mode: *const c_char) -> *mut FIL /// Put a character `c` into `stream` #[no_mangle] -pub unsafe extern "C" fn putc(c: c_int, stream: *mut FILE) -> c_int { +pub extern "C" fn putc(c: c_int, stream: &mut FILE) -> c_int { flockfile(stream); let ret = putc_unlocked(c, stream); funlockfile(stream); @@ -557,31 +687,31 @@ pub unsafe extern "C" fn putc(c: c_int, stream: *mut FILE) -> c_int { /// Put a character `c` into `stdout` #[no_mangle] pub unsafe extern "C" fn putchar(c: c_int) -> c_int { - fputc(c, stdout) + fputc(c, &mut *stdout) } /// 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 c as i8 != (*stream).buf_char && (*stream).wpos < (*stream).wend { - *(*stream).wpos = c as u8; - (*stream).wpos = (*stream).wpos.add(1); - c +pub extern "C" fn putc_unlocked(c: c_int, stream: &mut FILE) -> c_int { + if c as i8 != stream.buf_char && stream.wpos < stream.wend { + unsafe { + *stream.wpos = c as u8; + stream.wpos = stream.wpos.add(1); + c + } } else { - if let Some(write) = (*stream).write { - if (*stream).wend.is_null() && internal::to_write(stream) { - -1 - } else if c as i8 != (*stream).buf_char && (*stream).wpos < (*stream).wend { - *(*stream).wpos = c as u8; - (*stream).wpos = (*stream).wpos.add(1); - c - } else if write(stream, &c as *const i32 as *const _, 1) != 1 { - -1 - } else { + if stream.wend.is_null() && stream.can_write() { + -1 + } else if c as i8 != stream.buf_char && stream.wpos < stream.wend { + unsafe { + *stream.wpos = c as u8; + stream.wpos = stream.wpos.add(1); c } - } else { + } else if stream.write(&[c as u8]) != 1 { -1 + } else { + c } } } @@ -589,13 +719,13 @@ pub unsafe extern "C" fn putc_unlocked(c: c_int, stream: *mut FILE) -> c_int { /// Put a character `c` into `stdout` without locking `stdout` #[no_mangle] pub unsafe extern "C" fn putchar_unlocked(c: c_int) -> c_int { - putc_unlocked(c, stdout) + putc_unlocked(c, &mut *stdout) } /// Put a string `s` into `stdout` #[no_mangle] pub unsafe extern "C" fn puts(s: *const c_char) -> c_int { - let ret = (fputs(s, stdout) > 0) || (putchar_unlocked(b'\n' as c_int) > 0); + let ret = (fputs(s, &mut *stdout) > 0) || (putchar_unlocked(b'\n' as c_int) > 0); if ret { 0 } else { @@ -605,7 +735,7 @@ pub unsafe extern "C" fn puts(s: *const c_char) -> c_int { /// Put an integer `w` into `stream` #[no_mangle] -pub unsafe extern "C" fn putw(w: c_int, stream: *mut FILE) -> c_int { +pub extern "C" fn putw(w: c_int, stream: &mut FILE) -> c_int { use core::mem; fwrite(&w as *const i32 as _, mem::size_of_val(&w), 1, stream) as i32 - 1 } @@ -629,16 +759,16 @@ pub extern "C" fn rename(old: *const c_char, new: *const c_char) -> c_int { /// Rewind `stream` back to the beginning of it #[no_mangle] -pub unsafe extern "C" fn rewind(stream: *mut FILE) { +pub extern "C" fn rewind(stream: &mut FILE) { fseeko(stream, 0, SEEK_SET); flockfile(stream); - (*stream).flags &= !F_ERR; + stream.flags &= !F_ERR; funlockfile(stream); } /// Reset `stream` to use buffer `buf`. Buffer must be `BUFSIZ` in length #[no_mangle] -pub extern "C" fn setbuf(stream: *mut FILE, buf: *mut c_char) { +pub extern "C" fn setbuf(stream: &mut FILE, buf: *mut c_char) { unsafe { setvbuf( stream, @@ -650,9 +780,10 @@ pub extern "C" fn setbuf(stream: *mut FILE, buf: *mut c_char) { } /// Reset `stream` to use buffer `buf` of size `size` +/// If this isn't the meaning of unsafe, idk what is #[no_mangle] pub unsafe extern "C" fn setvbuf( - stream: *mut FILE, + stream: &mut FILE, buf: *mut c_char, mode: c_int, size: usize, @@ -692,22 +823,24 @@ pub extern "C" fn tmpnam(s: *mut c_char) -> *mut c_char { /// Push character `c` back onto `stream` so it'll be read next #[no_mangle] -pub unsafe extern "C" fn ungetc(c: c_int, stream: *mut FILE) -> c_int { +pub extern "C" fn ungetc(c: c_int, stream: &mut FILE) -> c_int { if c < 0 { c } else { flockfile(stream); - if (*stream).rpos.is_null() { - internal::to_read(stream); + if stream.rpos.is_null() { + stream.can_read(); } - if (*stream).rpos.is_null() || (*stream).rpos <= (*stream).buf.sub((*stream).unget) { + if stream.rpos.is_null() || stream.rpos <= unsafe { stream.buf.sub(stream.unget) } { funlockfile(stream); return -1; } - (*stream).rpos = (*stream).rpos.sub(1); - *(*stream).rpos = c as u8; - (*stream).flags &= !F_EOF; + unsafe { + stream.rpos = stream.rpos.sub(1); + *stream.rpos = c as u8; + } + stream.flags &= !F_EOF; funlockfile(stream); c @@ -715,13 +848,13 @@ 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 { - printf::printf(platform::FileWriter((*file).fd), format, ap) +pub unsafe extern "C" fn vfprintf(file: &mut FILE, format: *const c_char, ap: va_list) -> c_int { + printf::printf(file, format, ap) } #[no_mangle] pub unsafe extern "C" fn vprintf(format: *const c_char, ap: va_list) -> c_int { - vfprintf(stdout, format, ap) + vfprintf(&mut *stdout, format, ap) } #[no_mangle] @@ -731,12 +864,16 @@ pub unsafe extern "C" fn vsnprintf( format: *const c_char, ap: va_list, ) -> c_int { - printf::printf(platform::StringWriter(s as *mut u8, n as usize), format, ap) + printf::printf( + &mut platform::StringWriter(s as *mut u8, n as usize), + format, + ap, + ) } #[no_mangle] pub unsafe extern "C" fn vsprintf(s: *mut c_char, format: *const c_char, ap: va_list) -> c_int { - printf::printf(platform::UnsafeStringWriter(s as *mut u8), format, ap) + printf::printf(&mut platform::UnsafeStringWriter(s as *mut u8), format, ap) } /* From dbc3e413cc49f5a0a9c1364ed764eb6791847763 Mon Sep 17 00:00:00 2001 From: Tom Almeida Date: Tue, 20 Mar 2018 23:43:42 +0800 Subject: [PATCH 15/16] Fixed clearerr actually doing nothing --- src/stdio/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stdio/src/lib.rs b/src/stdio/src/lib.rs index fcae989865..98e3a3913f 100644 --- a/src/stdio/src/lib.rs +++ b/src/stdio/src/lib.rs @@ -180,7 +180,7 @@ impl Write for FILE { /// Clears EOF and ERR indicators on a stream #[no_mangle] pub extern "C" fn clearerr(stream: &mut FILE) { - stream.flags &= !(F_EOF & F_ERR); + stream.flags &= !(F_EOF | F_ERR); } #[no_mangle] From e1b20ed368cc74a27fe0a1c96a6babf69e721c8b Mon Sep 17 00:00:00 2001 From: Tom Almeida Date: Thu, 22 Mar 2018 12:43:05 +0800 Subject: [PATCH 16/16] Fixed getopt passing the wrong argument to stdio functions --- src/unistd/src/getopt.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/unistd/src/getopt.rs b/src/unistd/src/getopt.rs index 2790529a35..c93a5487b9 100644 --- a/src/unistd/src/getopt.rs +++ b/src/unistd/src/getopt.rs @@ -69,10 +69,10 @@ unsafe fn parse_arg( let print_error = |desc: &[u8]| { // NOTE: we don't use fprintf to get around the usage of va_list - stdio::fputs(*argv as _, stdio::stderr); - stdio::fputs(desc.as_ptr() as _, stdio::stderr); - stdio::fputc(*current_arg as _, stdio::stderr); - stdio::fputc(b'\n' as _, stdio::stderr); + stdio::fputs(*argv as _, &mut *stdio::stderr); + stdio::fputs(desc.as_ptr() as _, &mut *stdio::stderr); + stdio::fputc(*current_arg as _, &mut *stdio::stderr); + stdio::fputc(b'\n' as _, &mut *stdio::stderr); }; match find_option(*current_arg, optstring) {