From 454ce67d45e9f24437fda43a55308012b33e8e2a Mon Sep 17 00:00:00 2001 From: Tom Almeida Date: Thu, 21 Jun 2018 00:16:20 +0800 Subject: [PATCH 01/25] Changed FILE to use a vector as a buffer instead of raw pointers. This allows us to remove the large majority of unsafe blocks from the code --- src/platform/src/lib.rs | 20 ++ src/stdio/Cargo.toml | 8 +- src/stdio/src/default.rs | 101 +++----- src/stdio/src/helpers.rs | 102 ++++---- src/stdio/src/internal.rs | 22 +- src/stdio/src/lib.rs | 498 +++++++++++++++++++------------------- src/stdio/src/printf.rs | 59 ++--- 7 files changed, 408 insertions(+), 402 deletions(-) diff --git a/src/platform/src/lib.rs b/src/platform/src/lib.rs index 1d44e93bb3..bf5f8dc255 100644 --- a/src/platform/src/lib.rs +++ b/src/platform/src/lib.rs @@ -33,6 +33,26 @@ use types::*; #[no_mangle] pub static mut errno: c_int = 0; +pub unsafe fn c_str_mut<'a>(s: *mut c_char) -> &'a mut [u8] { + use core::usize; + + c_str_n_mut(s, usize::MAX) +} + +pub unsafe fn c_str_n_mut<'a>(s: *mut c_char, n: usize) -> &'a mut [u8] { + use core::slice; + + let mut size = 0; + + for _ in 0..n { + if *s.offset(size) == 0 { + break; + } + size += 1; + } + + slice::from_raw_parts_mut(s as *mut u8, size as usize) +} pub unsafe fn c_str<'a>(s: *const c_char) -> &'a [u8] { use core::usize; diff --git a/src/stdio/Cargo.toml b/src/stdio/Cargo.toml index aa82586feb..dd1e6126da 100644 --- a/src/stdio/Cargo.toml +++ b/src/stdio/Cargo.toml @@ -8,9 +8,11 @@ build = "build.rs" cbindgen = { path = "../../cbindgen" } [dependencies] -platform = { path = "../platform" } -va_list = { path = "../../va_list", features = ["no_std"] } +errno = { path = "../errno"} fcntl = { path = "../fcntl" } +lazy_static = "*" +platform = { path = "../platform" } +ralloc = { path = "../../ralloc" } string = { path = "../string" } stdlib = { path = "../stdlib" } -errno = { path = "../errno"} +va_list = { path = "../../va_list", features = ["no_std"] } diff --git a/src/stdio/src/default.rs b/src/stdio/src/default.rs index 840a6fa07f..090a8b4d44 100644 --- a/src/stdio/src/default.rs +++ b/src/stdio/src/default.rs @@ -1,76 +1,53 @@ use core::sync::atomic::AtomicBool; -use core::ptr; -use super::{constants, internal, BUFSIZ, FILE, UNGET}; +use super::{constants, BUFSIZ, FILE, UNGET}; -#[allow(non_upper_case_globals)] -static mut default_stdin_buf: [u8; BUFSIZ as usize + UNGET] = [0; BUFSIZ as usize + UNGET]; +lazy_static! { + #[allow(non_upper_case_globals)] + static ref default_stdin: FILE = FILE { + flags: constants::F_PERM | constants::F_NOWR | constants::F_BADJ, + read: None, + write: None, + fd: 0, + buf: vec![0u8;(BUFSIZ + UNGET) as usize], + buf_char: -1, + unget: UNGET, + lock: AtomicBool::new(false), + }; -#[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), -}; + #[allow(non_upper_case_globals)] + static ref default_stdout: FILE = FILE { + flags: constants::F_PERM | constants::F_NORD | constants::F_BADJ, + read: None, + write: None, + fd: 1, + buf: vec![0u8;(BUFSIZ + UNGET) as usize], + buf_char: b'\n' as i8, + unget: 0, + lock: AtomicBool::new(false), + }; -#[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), -}; - -#[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), -}; + #[allow(non_upper_case_globals)] + static ref default_stderr: FILE = FILE { + flags: constants::F_PERM | constants::F_NORD | constants::F_BADJ, + read: None, + write: None, + fd: 2, + buf: vec![0u8;(BUFSIZ + UNGET) as usize], + buf_char: -1, + unget: 0, + lock: AtomicBool::new(false), + }; +} // 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; +pub static mut stdin: *mut FILE = &default_stdin as *const _ 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; +pub static mut stdout: *mut FILE = &default_stdout as *const _ 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; +pub static mut stderr: *mut FILE = &default_stderr as *const _ as *const FILE as *mut FILE; diff --git a/src/stdio/src/helpers.rs b/src/stdio/src/helpers.rs index 378d2af3b6..d670509201 100644 --- a/src/stdio/src/helpers.rs +++ b/src/stdio/src/helpers.rs @@ -1,6 +1,4 @@ -use super::{internal, BUFSIZ, FILE, UNGET}; -use stdlib::calloc; -use core::{mem, ptr}; +use super::{BUFSIZ, FILE, UNGET}; use core::sync::atomic::AtomicBool; use platform::types::*; use super::constants::*; @@ -38,11 +36,11 @@ pub unsafe fn parse_mode_flags(mode_str: *const c_char) -> i32 { } /// 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 { +pub unsafe fn _fdopen(fd: c_int, mode: *const c_char) -> Option { 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(); + return None; } let mut flags = 0; @@ -62,23 +60,17 @@ pub unsafe fn _fdopen(fd: c_int, mode: *const c_char) -> *mut FILE { flags |= F_APP; } - let file = calloc(mem::size_of::() + BUFSIZ + UNGET, 1) as *mut FILE; // Allocate the file - (*file) = FILE { + Some(FILE { flags: flags, - rpos: ptr::null_mut(), - rend: ptr::null_mut(), - wend: ptr::null_mut(), - wpos: ptr::null_mut(), - wbase: ptr::null_mut(), + read: None, + write: None, fd: fd, - buf: (file as *mut u8).add(mem::size_of::() + UNGET), - buf_size: BUFSIZ, + buf: vec![0u8; BUFSIZ + UNGET], buf_char: -1, unget: UNGET, lock: AtomicBool::new(false), - }; - file + }) } /// Write buffer `buf` of length `l` into `stream` @@ -90,58 +82,68 @@ pub fn fwritex(buf: *const u8, l: size_t, stream: &mut FILE) -> size_t { let mut l = l; let mut advance = 0; - if stream.wend.is_null() && !stream.can_write() { + if stream.write.is_none() && !stream.can_write() { // 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(buf); - } + if let Some((wbase, wpos, wend)) = stream.write { + if l > wend - wpos { + // 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; + let i = if stream.buf_char >= 0 { + let mut i = l; + while i > 0 && buf[i - 1] != b'\n' { + i -= 1; } - advance += i; - l -= i; + if i > 0 { + let n = stream.write(buf); + if n < i { + return n; + } + advance += i; + l -= i; + } + i + } else { + 0 + }; + + unsafe { + copy_nonoverlapping( + &buf[advance..] as *const _ as *const u8, + &mut stream.buf[wpos..] as *mut _ as *mut u8, + l, + ); } - i + stream.write = Some((wbase, wpos + l, wend)); + l + 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 fn fflush_unlocked(stream: &mut FILE) -> c_int { - if stream.wpos > stream.wbase { - stream.write(&[]); - if stream.wpos.is_null() { + if let Some((wbase, wpos, _)) = stream.write { + if wpos > wbase { + stream.write(&[]); + /* + if stream.wpos.is_null() { return -1; + } + */ } } - if stream.rpos < stream.rend { - stream.seek(stream.rpos as i64 - stream.rend as i64, SEEK_CUR); + if let Some((rpos, rend)) = stream.read { + if rpos < rend { + stream.seek(rpos as i64 - 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.write = None; + stream.read = None; 0 } diff --git a/src/stdio/src/internal.rs b/src/stdio/src/internal.rs index 35eb5f47e8..60ad4f3ebf 100644 --- a/src/stdio/src/internal.rs +++ b/src/stdio/src/internal.rs @@ -1,13 +1,15 @@ use super::{constants, FILE}; -use platform; use platform::types::*; -use core::{mem, ptr, slice}; 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 + if let Some((wbase, wpos, _)) = stream.write { + if (stream.flags & constants::F_APP > 0) && wpos > wbase { + constants::SEEK_END + } else { + constants::SEEK_CUR + } } else { constants::SEEK_CUR }, @@ -15,5 +17,15 @@ pub fn ftello(stream: &mut FILE) -> off_t { if pos < 0 { return pos; } - pos - (stream.rend as i64 - stream.rpos as i64) + (stream.wpos as i64 - stream.wbase as i64) + let rdiff = if let Some((rpos, rend)) = stream.read { + rend - rpos + } else { + 0 + }; + let wdiff = if let Some((wbase, wpos, _)) = stream.write { + wpos - wbase + } else { + 0 + }; + pos - rdiff as i64 + wdiff as i64 } diff --git a/src/stdio/src/lib.rs b/src/stdio/src/lib.rs index e9d562df44..bc113fefca 100644 --- a/src/stdio/src/lib.rs +++ b/src/stdio/src/lib.rs @@ -1,9 +1,18 @@ //! stdio implementation for Redox, following http://pubs.opengroup.org/onlinepubs/7908799/xsh/stdio.h.html #![no_std] +// For Vec +#![feature(alloc)] +// For stdin, stdout and stderr +#![allow(non_upper_case_globals)] +#![deny(warnings)] +#[macro_use] +extern crate alloc; extern crate errno; extern crate fcntl; +#[macro_use] +extern crate lazy_static; extern crate platform; extern crate stdlib; extern crate string; @@ -15,9 +24,10 @@ use core::fmt::{self, Error, Result}; use core::fmt::Write as WriteFmt; use core::sync::atomic::{AtomicBool, Ordering}; +use errno::STR_ERROR; use platform::types::*; use platform::{c_str, errno, Write}; -use errno::STR_ERROR; +use alloc::vec::Vec; use vl::VaList as va_list; mod printf; @@ -32,42 +42,39 @@ mod helpers; mod internal; -#[repr(C)] +/// +/// This struct gets exposed to the C API. +/// pub struct FILE { - flags: c_int, - rpos: *mut u8, - rend: *mut u8, - wend: *mut u8, - wpos: *mut u8, - wbase: *mut u8, + flags: i32, + read: Option<(usize, usize)>, + write: Option<(usize, usize, usize)>, fd: c_int, - buf: *mut u8, - buf_size: size_t, + buf: Vec, buf_char: i8, lock: AtomicBool, - unget: size_t, + unget: usize, } 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 { + if let Some(_) = self.write { self.write(&[]); } - self.wpos = ptr::null_mut(); - self.wbase = ptr::null_mut(); - self.wend = ptr::null_mut(); + self.write = None; 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) }; + self.read = Some((self.buf.len() - 1, self.buf.len() - 1)); if self.flags & constants::F_EOF > 0 { false } else { @@ -75,71 +82,68 @@ impl FILE { } } 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) }; + self.read = None; + self.write = Some((self.unget, self.unget, self.buf.len() - 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 let Some((wbase, wpos, _)) = self.write { + let len = wpos - wbase; + let mut advance = 0; + let mut f_buf = &self.buf[wbase..wpos]; + 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.write = Some((self.unget, self.unget, self.buf.len() - 1)); + return to_write.len(); + } + if count < 0 { + self.write = None; + self.flags |= constants::F_ERR; + return 0; + } + rem -= count as usize; + if count as usize > len { + count -= len as isize; + f_buf = to_write; + f_filled = true; + advance = 0; + } + advance += count as usize; } - 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; } + // self.can_write() should always be called before self.write() + // and should automatically fill self.write if it returns true. + // Thus, we should never reach this + // -- Tommoa (20/6/2018) + unreachable!() } 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 adj = !(self.buf.len() == 0) as usize; + let mut file_buf = &mut self.buf[self.unget..]; let count = if buf.len() <= 1 + adj { - platform::read(self.fd, file_buf) + platform::read(self.fd, &mut file_buf) } else { - platform::read(self.fd, buf) + platform::read(self.fd, file_buf) + platform::read(self.fd, buf) + platform::read(self.fd, &mut file_buf) }; if count <= 0 { self.flags |= if count == 0 { @@ -152,17 +156,13 @@ impl FILE { 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); - } + // Adjust pointers + self.read = Some((self.unget + 1, self.unget + (count as usize))); + buf[buf.len() - 1] = file_buf[0]; buf.len() } pub fn seek(&self, off: off_t, whence: c_int) -> off_t { - unsafe { platform::lseek(self.fd, off, whence) } + platform::lseek(self.fd, off, whence) } } impl fmt::Write for FILE { @@ -192,12 +192,12 @@ pub extern "C" fn clearerr(stream: &mut FILE) { } #[no_mangle] -pub extern "C" fn ctermid(s: *mut c_char) -> *mut c_char { +pub extern "C" fn ctermid(_s: *mut c_char) -> *mut c_char { unimplemented!(); } #[no_mangle] -pub extern "C" fn cuserid(s: *mut c_char) -> *mut c_char { +pub extern "C" fn cuserid(_s: *mut c_char) -> *mut c_char { unimplemented!(); } @@ -222,7 +222,12 @@ pub extern "C" fn fclose(stream: &mut FILE) -> c_int { /// Open a file from a file descriptor #[no_mangle] pub extern "C" fn fdopen(fildes: c_int, mode: *const c_char) -> *mut FILE { - unsafe { helpers::_fdopen(fildes, mode) } + use core::ptr; + if let Some(mut f) = unsafe { helpers::_fdopen(fildes, mode) } { + &mut f + } else { + ptr::null_mut() + } } /// Check for EOF @@ -281,13 +286,12 @@ pub extern "C" fn fgetpos(stream: &mut FILE, pos: *mut fpos_t) -> c_int { /// Get a string from the stream #[no_mangle] pub extern "C" fn fgets(s: *mut c_char, n: c_int, stream: &mut FILE) -> *mut c_char { - use string::memchr; - use core::ptr::copy_nonoverlapping; + use platform::c_str_n_mut; flockfile(stream); - let mut ptr = s as *mut u8; - let mut n = n; + let st = unsafe { c_str_n_mut(s, n as usize) }; + // We can only fit one or less chars in if n <= 1 { funlockfile(stream); if n == 0 { @@ -298,52 +302,29 @@ pub extern "C" fn fgets(s: *mut c_char, n: c_int, stream: &mut FILE) -> *mut c_c } return s; } - while n > 0 { - 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 - } else { - z as usize - stream.rpos as usize + 1 - }; - let k = if k as i32 > n { n as usize } else { 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; - } - let c = getc_unlocked(stream); - if c < 0 { - break; - } - n -= 1; - - unsafe { - // Pointer stuff - *ptr = c as u8; - ptr = ptr.add(1); - } - - if c as u8 == b'\n' { - break; + // Scope this so we can reuse stream mutably + { + // We can't read from this stream + if !stream.can_read() { + return ptr::null_mut(); } } - if !s.is_null() { - unsafe { - *ptr = 0; + + if let Some((rpos, rend)) = stream.read { + let mut diff = 0; + for (_, mut c) in stream.buf[rpos..rend] + .iter() + .enumerate() + .take_while(|&(i, c)| *c != b'\n' && i < n as usize) + { + st[diff] = *c; + diff += 1; } + stream.read = Some((rpos + diff, rend)); + } else { + return ptr::null_mut(); } + funlockfile(stream); s } @@ -366,15 +347,15 @@ pub extern "C" fn flockfile(file: &mut FILE) { /// Open the file in mode `mode` #[no_mangle] -pub unsafe extern "C" fn fopen(filename: *const c_char, mode: *const c_char) -> *mut FILE { +pub extern "C" fn fopen(filename: *const c_char, mode: *const c_char) -> *mut FILE { use core::ptr; - let initial_mode = *mode; + let initial_mode = unsafe { *mode }; if initial_mode != b'r' as i8 && initial_mode != b'w' as i8 && initial_mode != b'a' as i8 { - platform::errno = errno::EINVAL; + unsafe { platform::errno = errno::EINVAL }; return ptr::null_mut(); } - let flags = helpers::parse_mode_flags(mode); + let flags = unsafe { helpers::parse_mode_flags(mode) }; let fd = fcntl::sys_open(filename, flags, 0o666); if fd < 0 { @@ -385,12 +366,13 @@ 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); - if f.is_null() { + let f = unsafe { helpers::_fdopen(fd, mode) }; + if let Some(mut fi) = f { + &mut fi + } else { platform::close(fd); return ptr::null_mut(); } - f } /// Insert a character into the stream @@ -423,41 +405,49 @@ pub extern "C" fn fread(ptr: *mut c_void, size: usize, nitems: usize, stream: &m 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 }; - 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; + if !stream.can_read() { + return 0; } - while l > 0 { - let k = if !stream.can_read() { - 0 - } else { - stream.read(unsafe { slice::from_raw_parts_mut(dest, l as usize) }) - }; - - if k == 0 { - funlockfile(stream); - return (len - l as usize) / 2; + if let Some((rpos, rend)) = stream.read { + if rend > rpos { + // We have some buffered data that can be read + let diff = rend - rpos; + let k = if diff < l as usize { diff } else { l as usize }; + unsafe { + // Copy data + copy_nonoverlapping(&stream.buf[rpos..] as *const _ as *const u8, dest, k); + // Reposition pointers + dest = dest.add(k); + } + stream.read = Some((rpos + k, rend)); + l -= k as isize; } - l -= k as isize; - unsafe { - // Reposition - dest = dest.add(k); + while l > 0 { + let k = if !stream.can_read() { + 0 + } else { + stream.read(unsafe { slice::from_raw_parts_mut(dest, l as usize) }) + }; + + if k == 0 { + funlockfile(stream); + return (len - l as usize) / 2; + } + + l -= k as isize; + unsafe { + // Reposition + dest = dest.add(k); + } } + + funlockfile(stream); + nitems + } else { + unreachable!() } - - funlockfile(stream); - nitems } #[no_mangle] @@ -482,7 +472,7 @@ pub extern "C" fn freopen( return ptr::null_mut(); } } else { - let new = unsafe { fopen(filename, mode) }; + let new = fopen(filename, mode); if new.is_null() { funlockfile(stream); fclose(stream); @@ -521,23 +511,22 @@ pub extern "C" fn fseeko(stream: &mut FILE, offset: off_t, whence: c_int) -> c_i let mut off = offset; flockfile(stream); // Adjust for what is currently in the buffer + let rdiff = if let Some((rpos, rend)) = stream.read { + rend - rpos + } else { + 0 + }; if whence == SEEK_CUR { - off -= (stream.rend as usize - stream.rpos as usize) as i64; + off -= (rdiff) as i64; } - if stream.wpos > stream.wbase { + if let Some(_) = stream.write { stream.write(&[]); - if stream.wpos.is_null() { - return -1; - } } - stream.wpos = ptr::null_mut(); - stream.wend = ptr::null_mut(); - stream.wbase = ptr::null_mut(); + stream.write = None; if stream.seek(off, whence) < 0 { return -1; } - stream.rpos = ptr::null_mut(); - stream.rend = ptr::null_mut(); + stream.read = None; stream.flags &= !F_EOF; funlockfile(stream); 0 @@ -551,7 +540,7 @@ pub unsafe extern "C" fn fsetpos(stream: &mut FILE, pos: *const fpos_t) -> c_int /// Get the current position of the cursor in the file #[no_mangle] -pub unsafe extern "C" fn ftell(stream: &mut FILE) -> c_long { +pub extern "C" fn ftell(stream: &mut FILE) -> c_long { ftello(stream) as c_long } @@ -607,40 +596,48 @@ pub 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(&mut *stdin) +pub extern "C" fn getchar() -> c_int { + fgetc(unsafe { &mut *stdin }) } /// Get a char from a stream without locking the stream #[no_mangle] 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); + if !stream.can_read() { + return -1; + } + if let Some((rpos, rend)) = stream.read { + if rpos < rend { + let ret = stream.buf[rpos] as c_int; + stream.read = Some((rpos + 1, rend)); ret + } else { + let mut c = [0u8; 1]; + if stream.read(&mut c) == 1 { + c[0] as c_int + } else { + -1 + } } } else { - let mut c = [0u8; 1]; - if stream.can_read() && stream.read(&mut c) == 1 { - c[0] as c_int - } else { - -1 - } + // We made a prior call to can_read() and are checking it, therefore we + // should never be in a case where stream.read is None + // -- Tommoa (20/6/2018) + unreachable!() } } /// Get a char from `stdin` without locking `stdin` #[no_mangle] -pub unsafe extern "C" fn getchar_unlocked() -> c_int { - getc_unlocked(&mut *stdin) +pub extern "C" fn getchar_unlocked() -> c_int { + getc_unlocked(unsafe { &mut *stdin }) } /// Get a string from `stdin` #[no_mangle] -pub unsafe extern "C" fn gets(s: *mut c_char) -> *mut c_char { +pub extern "C" fn gets(s: *mut c_char) -> *mut c_char { use core::i32; - fgets(s, i32::MAX, &mut *stdin) + fgets(s, i32::MAX, unsafe { &mut *stdin }) } /// Get an integer from `stream` @@ -662,7 +659,7 @@ pub 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!(); } @@ -672,14 +669,16 @@ pub unsafe extern "C" fn perror(s: *const c_char) { let mut w = platform::FileWriter(2); if errno >= 0 && errno < STR_ERROR.len() as c_int { - w.write_fmt(format_args!("{}: {}\n", s_str, STR_ERROR[errno as usize])); + w.write_fmt(format_args!("{}: {}\n", s_str, STR_ERROR[errno as usize])) + .unwrap(); } else { - w.write_fmt(format_args!("{}: Unknown error {}\n", s_str, errno)); + w.write_fmt(format_args!("{}: Unknown error {}\n", s_str, errno)) + .unwrap(); } } #[no_mangle] -pub extern "C" fn popen(command: *const c_char, mode: *const c_char) -> *mut FILE { +pub extern "C" fn popen(_command: *const c_char, _mode: *const c_char) -> *mut FILE { unimplemented!(); } @@ -694,46 +693,42 @@ pub 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, &mut *stdout) +pub extern "C" fn putchar(c: c_int) -> c_int { + fputc(c, unsafe { &mut *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 { - if c as i8 != stream.buf_char && stream.wpos < stream.wend { - unsafe { - *stream.wpos = c as u8; - stream.wpos = stream.wpos.add(1); - c + if stream.can_write() { + if let Some((wbase, wpos, wend)) = stream.write { + if c as i8 != stream.buf_char { + stream.buf[wpos] = c as u8; + stream.write = Some((wbase, wpos + 1, wend)); + c + } else if stream.write(&[c as u8]) == 1 { + c + } else { + -1 + } + } else { + -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 if stream.write(&[c as u8]) != 1 { - -1 - } else { - c - } + -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, &mut *stdout) +pub extern "C" fn putchar_unlocked(c: c_int) -> c_int { + putc_unlocked(c, unsafe { &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, &mut *stdout) > 0) || (putchar_unlocked(b'\n' as c_int) > 0); +pub extern "C" fn puts(s: *const c_char) -> c_int { + let ret = (fputs(s, unsafe { &mut *stdout }) > 0) || (putchar_unlocked(b'\n' as c_int) > 0); if ret { 0 } else { @@ -776,45 +771,41 @@ pub extern "C" fn rewind(stream: &mut FILE) { /// 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) { - unsafe { - setvbuf( - stream, - buf, - if buf.is_null() { _IONBF } else { _IOFBF }, - BUFSIZ as usize, - ) - }; + setvbuf( + stream, + buf, + if buf.is_null() { _IONBF } else { _IOFBF }, + BUFSIZ as usize, + ); } /// 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, - 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; +pub extern "C" fn setvbuf(stream: &mut FILE, buf: *mut c_char, mode: c_int, size: usize) -> c_int { + // Set a buffer of size `size` if no buffer is given + let buf = if buf.is_null() { + if mode != _IONBF { + vec![0u8; 1] + } else { + Vec::new() + } + } else { + // We trust the user on this one + // -- Tommoa (20/6/2018) + unsafe { Vec::from_raw_parts(buf as *mut u8, size, size) } + }; + stream.buf_char = -1; + if mode == _IOLBF { + stream.buf_char = b'\n' as i8; } - (*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; + stream.flags |= F_SVB; + stream.buf = buf; 0 } #[no_mangle] -pub extern "C" fn tempnam(dir: *const c_char, pfx: *const c_char) -> *mut c_char { +pub extern "C" fn tempnam(_dir: *const c_char, _pfx: *const c_char) -> *mut c_char { unimplemented!(); } @@ -824,7 +815,7 @@ pub extern "C" fn tmpfile() -> *mut FILE { } #[no_mangle] -pub extern "C" fn tmpnam(s: *mut c_char) -> *mut c_char { +pub extern "C" fn tmpnam(_s: *mut c_char) -> *mut c_char { unimplemented!(); } @@ -835,22 +826,23 @@ pub extern "C" fn ungetc(c: c_int, stream: &mut FILE) -> c_int { c } else { flockfile(stream); - if stream.rpos.is_null() { + if stream.read.is_none() { stream.can_read(); } - if stream.rpos.is_null() || stream.rpos <= unsafe { stream.buf.sub(stream.unget) } { + if let Some((rpos, rend)) = stream.read { + if rpos == 0 { + funlockfile(stream); + return -1; + } + stream.read = Some((rpos - 1, rend)); + stream.buf[rpos - 1] = c as u8; + stream.flags &= !F_EOF; funlockfile(stream); - return -1; + c + } else { + funlockfile(stream); + -1 } - - unsafe { - stream.rpos = stream.rpos.sub(1); - *stream.rpos = c as u8; - } - stream.flags &= !F_EOF; - - funlockfile(stream); - c } } diff --git a/src/stdio/src/printf.rs b/src/stdio/src/printf.rs index 75b619b258..da12b07c75 100644 --- a/src/stdio/src/printf.rs +++ b/src/stdio/src/printf.rs @@ -1,4 +1,4 @@ -use core::{fmt, slice, str}; +use core::{slice, str}; use platform::{self, Write}; use platform::types::*; @@ -17,90 +17,91 @@ pub unsafe fn printf(mut w: W, format: *const c_char, mut ap: VaList) if found_percent { match b as char { '%' => { - w.write_char('%'); found_percent = false; + w.write_char('%') } 'c' => { let a = ap.get::(); - w.write_u8(a as u8); - found_percent = false; + + w.write_u8(a as u8) } 'd' | 'i' => { let a = ap.get::(); - w.write_fmt(format_args!("{}", a)); - found_percent = false; + + w.write_fmt(format_args!("{}", a)) } 'f' | 'F' => { let a = ap.get::(); - w.write_fmt(format_args!("{}", a)); - found_percent = false; + + w.write_fmt(format_args!("{}", a)) } 'n' => { let _a = ap.get::(); found_percent = false; + Ok(()) } 'p' => { let a = ap.get::(); - w.write_fmt(format_args!("0x{:x}", a)); - found_percent = false; + + w.write_fmt(format_args!("0x{:x}", a)) } 's' => { let a = ap.get::(); + found_percent = false; + w.write_str(str::from_utf8_unchecked(platform::c_str( a as *const c_char, - ))); - - found_percent = false; + ))) } 'u' => { let a = ap.get::(); - w.write_fmt(format_args!("{}", a)); - found_percent = false; + + w.write_fmt(format_args!("{}", a)) } 'x' => { let a = ap.get::(); - w.write_fmt(format_args!("{:x}", a)); - found_percent = false; + + w.write_fmt(format_args!("{:x}", a)) } 'X' => { let a = ap.get::(); - w.write_fmt(format_args!("{:X}", a)); - found_percent = false; + + w.write_fmt(format_args!("{:X}", a)) } 'o' => { let a = ap.get::(); - w.write_fmt(format_args!("{:o}", a)); - found_percent = false; + + w.write_fmt(format_args!("{:o}", a)) } - '-' => {} - '+' => {} - ' ' => {} - '#' => {} - '0'...'9' => {} - _ => {} - } + '-' => Ok(()), + '+' => Ok(()), + ' ' => Ok(()), + '#' => Ok(()), + '0'...'9' => Ok(()), + _ => Ok(()), + }.expect("Error writing!") } else if b == b'%' { found_percent = true; } else { - w.write_u8(b); + w.write_u8(b).expect("Error writing char!"); } } From 77b160c70d5c03ad9ba66107a4ecd0dc3db0403f Mon Sep 17 00:00:00 2001 From: Tom Almeida Date: Thu, 21 Jun 2018 09:33:21 +0800 Subject: [PATCH 02/25] Fixed some issues with temporary files and moved some raw pointers to Option<&T>s --- src/stdio/src/helpers.rs | 28 ++++++++++++++++----------- src/stdio/src/lib.rs | 41 +++++++++++++++++++++------------------- 2 files changed, 39 insertions(+), 30 deletions(-) diff --git a/src/stdio/src/helpers.rs b/src/stdio/src/helpers.rs index d670509201..242e83f1ec 100644 --- a/src/stdio/src/helpers.rs +++ b/src/stdio/src/helpers.rs @@ -36,8 +36,10 @@ pub unsafe fn parse_mode_flags(mode_str: *const c_char) -> i32 { } /// Open a file with the file descriptor `fd` in the mode `mode` -pub unsafe fn _fdopen(fd: c_int, mode: *const c_char) -> Option { +pub unsafe fn _fdopen(fd: c_int, mode: *const c_char) -> Option<*mut FILE> { use string::strchr; + use stdlib::malloc; + use core::mem::size_of; if *mode != b'r' as i8 && *mode != b'w' as i8 && *mode != b'a' as i8 { platform::errno = errno::EINVAL; return None; @@ -61,16 +63,20 @@ pub unsafe fn _fdopen(fd: c_int, mode: *const c_char) -> Option { } // Allocate the file - Some(FILE { - flags: flags, - read: None, - write: None, - fd: fd, - buf: vec![0u8; BUFSIZ + UNGET], - buf_char: -1, - unget: UNGET, - lock: AtomicBool::new(false), - }) + let f = malloc(size_of::()) as *mut FILE; + if f.is_null() { + None + } else { + (*f).flags = flags; + (*f).read = None; + (*f).write = None; + (*f).fd = fd; + (*f).buf = vec![0u8; BUFSIZ + UNGET]; + (*f).buf_char = -1; + (*f).unget = UNGET; + (*f).lock = AtomicBool::new(false); + Some(f) + } } /// Write buffer `buf` of length `l` into `stream` diff --git a/src/stdio/src/lib.rs b/src/stdio/src/lib.rs index bc113fefca..8ddb27d4d5 100644 --- a/src/stdio/src/lib.rs +++ b/src/stdio/src/lib.rs @@ -46,14 +46,14 @@ mod internal; /// This struct gets exposed to the C API. /// pub struct FILE { - flags: i32, - read: Option<(usize, usize)>, - write: Option<(usize, usize, usize)>, - fd: c_int, - buf: Vec, + flags: i32, + read: Option<(usize, usize)>, + write: Option<(usize, usize, usize)>, + fd: c_int, + buf: Vec, buf_char: i8, - lock: AtomicBool, - unget: usize, + lock: AtomicBool, + unget: usize, } impl FILE { @@ -215,6 +215,8 @@ pub extern "C" fn fclose(stream: &mut FILE) -> c_int { unsafe { free(stream as *mut _ as *mut _); } + } else { + funlockfile(stream); } r } @@ -223,8 +225,8 @@ pub extern "C" fn fclose(stream: &mut FILE) -> c_int { #[no_mangle] pub extern "C" fn fdopen(fildes: c_int, mode: *const c_char) -> *mut FILE { use core::ptr; - if let Some(mut f) = unsafe { helpers::_fdopen(fildes, mode) } { - &mut f + if let Some(f) = unsafe { helpers::_fdopen(fildes, mode) } { + f } else { ptr::null_mut() } @@ -272,15 +274,17 @@ pub extern "C" fn fgetc(stream: &mut FILE) -> c_int { /// Get the position of the stream and store it in pos #[no_mangle] -pub extern "C" fn fgetpos(stream: &mut FILE, pos: *mut fpos_t) -> c_int { +pub extern "C" fn fgetpos(stream: &mut FILE, pos: Option<&mut fpos_t>) -> c_int { let off = internal::ftello(stream); if off < 0 { return -1; } - unsafe { - (*pos) = off; + if let Some(pos) = pos { + *pos = off; + 0 + } else { + -1 } - 0 } /// Get a string from the stream @@ -366,12 +370,11 @@ pub extern "C" fn fopen(filename: *const c_char, mode: *const c_char) -> *mut FI fcntl::sys_fcntl(fd, fcntl::F_SETFD, fcntl::FD_CLOEXEC); } - let f = unsafe { helpers::_fdopen(fd, mode) }; - if let Some(mut fi) = f { - &mut fi + if let Some(f) = unsafe { helpers::_fdopen(fd, mode) } { + f } else { platform::close(fd); - return ptr::null_mut(); + ptr::null_mut() } } @@ -534,8 +537,8 @@ pub extern "C" fn fseeko(stream: &mut FILE, offset: off_t, whence: c_int) -> c_i /// 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 { - fseek(stream, *pos as off_t, SEEK_SET) +pub unsafe extern "C" fn fsetpos(stream: &mut FILE, pos: Option<&fpos_t>) -> c_int { + fseek(stream, *pos.expect("You must specify a valid position"), SEEK_SET) } /// Get the current position of the cursor in the file From 90c6937f173e8eedb1824e262b1edb9fe76a3346 Mon Sep 17 00:00:00 2001 From: Tom Almeida Date: Thu, 21 Jun 2018 00:16:20 +0800 Subject: [PATCH 03/25] Changed FILE to use a vector as a buffer instead of raw pointers. This allows us to remove the large majority of unsafe blocks from the code --- src/platform/src/lib.rs | 20 ++ src/stdio/Cargo.toml | 8 +- src/stdio/src/default.rs | 101 +++----- src/stdio/src/helpers.rs | 102 ++++---- src/stdio/src/internal.rs | 22 +- src/stdio/src/lib.rs | 495 +++++++++++++++++++------------------- src/stdio/src/printf.rs | 59 ++--- 7 files changed, 406 insertions(+), 401 deletions(-) diff --git a/src/platform/src/lib.rs b/src/platform/src/lib.rs index 3784ea30d7..433c6977cc 100644 --- a/src/platform/src/lib.rs +++ b/src/platform/src/lib.rs @@ -33,6 +33,26 @@ use types::*; #[no_mangle] pub static mut errno: c_int = 0; +pub unsafe fn c_str_mut<'a>(s: *mut c_char) -> &'a mut [u8] { + use core::usize; + + c_str_n_mut(s, usize::MAX) +} + +pub unsafe fn c_str_n_mut<'a>(s: *mut c_char, n: usize) -> &'a mut [u8] { + use core::slice; + + let mut size = 0; + + for _ in 0..n { + if *s.offset(size) == 0 { + break; + } + size += 1; + } + + slice::from_raw_parts_mut(s as *mut u8, size as usize) +} pub unsafe fn c_str<'a>(s: *const c_char) -> &'a [u8] { use core::usize; diff --git a/src/stdio/Cargo.toml b/src/stdio/Cargo.toml index aa82586feb..dd1e6126da 100644 --- a/src/stdio/Cargo.toml +++ b/src/stdio/Cargo.toml @@ -8,9 +8,11 @@ build = "build.rs" cbindgen = { path = "../../cbindgen" } [dependencies] -platform = { path = "../platform" } -va_list = { path = "../../va_list", features = ["no_std"] } +errno = { path = "../errno"} fcntl = { path = "../fcntl" } +lazy_static = "*" +platform = { path = "../platform" } +ralloc = { path = "../../ralloc" } string = { path = "../string" } stdlib = { path = "../stdlib" } -errno = { path = "../errno"} +va_list = { path = "../../va_list", features = ["no_std"] } diff --git a/src/stdio/src/default.rs b/src/stdio/src/default.rs index 840a6fa07f..090a8b4d44 100644 --- a/src/stdio/src/default.rs +++ b/src/stdio/src/default.rs @@ -1,76 +1,53 @@ use core::sync::atomic::AtomicBool; -use core::ptr; -use super::{constants, internal, BUFSIZ, FILE, UNGET}; +use super::{constants, BUFSIZ, FILE, UNGET}; -#[allow(non_upper_case_globals)] -static mut default_stdin_buf: [u8; BUFSIZ as usize + UNGET] = [0; BUFSIZ as usize + UNGET]; +lazy_static! { + #[allow(non_upper_case_globals)] + static ref default_stdin: FILE = FILE { + flags: constants::F_PERM | constants::F_NOWR | constants::F_BADJ, + read: None, + write: None, + fd: 0, + buf: vec![0u8;(BUFSIZ + UNGET) as usize], + buf_char: -1, + unget: UNGET, + lock: AtomicBool::new(false), + }; -#[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), -}; + #[allow(non_upper_case_globals)] + static ref default_stdout: FILE = FILE { + flags: constants::F_PERM | constants::F_NORD | constants::F_BADJ, + read: None, + write: None, + fd: 1, + buf: vec![0u8;(BUFSIZ + UNGET) as usize], + buf_char: b'\n' as i8, + unget: 0, + lock: AtomicBool::new(false), + }; -#[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), -}; - -#[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), -}; + #[allow(non_upper_case_globals)] + static ref default_stderr: FILE = FILE { + flags: constants::F_PERM | constants::F_NORD | constants::F_BADJ, + read: None, + write: None, + fd: 2, + buf: vec![0u8;(BUFSIZ + UNGET) as usize], + buf_char: -1, + unget: 0, + lock: AtomicBool::new(false), + }; +} // 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; +pub static mut stdin: *mut FILE = &default_stdin as *const _ 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; +pub static mut stdout: *mut FILE = &default_stdout as *const _ 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; +pub static mut stderr: *mut FILE = &default_stderr as *const _ as *const FILE as *mut FILE; diff --git a/src/stdio/src/helpers.rs b/src/stdio/src/helpers.rs index 378d2af3b6..d670509201 100644 --- a/src/stdio/src/helpers.rs +++ b/src/stdio/src/helpers.rs @@ -1,6 +1,4 @@ -use super::{internal, BUFSIZ, FILE, UNGET}; -use stdlib::calloc; -use core::{mem, ptr}; +use super::{BUFSIZ, FILE, UNGET}; use core::sync::atomic::AtomicBool; use platform::types::*; use super::constants::*; @@ -38,11 +36,11 @@ pub unsafe fn parse_mode_flags(mode_str: *const c_char) -> i32 { } /// 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 { +pub unsafe fn _fdopen(fd: c_int, mode: *const c_char) -> Option { 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(); + return None; } let mut flags = 0; @@ -62,23 +60,17 @@ pub unsafe fn _fdopen(fd: c_int, mode: *const c_char) -> *mut FILE { flags |= F_APP; } - let file = calloc(mem::size_of::() + BUFSIZ + UNGET, 1) as *mut FILE; // Allocate the file - (*file) = FILE { + Some(FILE { flags: flags, - rpos: ptr::null_mut(), - rend: ptr::null_mut(), - wend: ptr::null_mut(), - wpos: ptr::null_mut(), - wbase: ptr::null_mut(), + read: None, + write: None, fd: fd, - buf: (file as *mut u8).add(mem::size_of::() + UNGET), - buf_size: BUFSIZ, + buf: vec![0u8; BUFSIZ + UNGET], buf_char: -1, unget: UNGET, lock: AtomicBool::new(false), - }; - file + }) } /// Write buffer `buf` of length `l` into `stream` @@ -90,58 +82,68 @@ pub fn fwritex(buf: *const u8, l: size_t, stream: &mut FILE) -> size_t { let mut l = l; let mut advance = 0; - if stream.wend.is_null() && !stream.can_write() { + if stream.write.is_none() && !stream.can_write() { // 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(buf); - } + if let Some((wbase, wpos, wend)) = stream.write { + if l > wend - wpos { + // 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; + let i = if stream.buf_char >= 0 { + let mut i = l; + while i > 0 && buf[i - 1] != b'\n' { + i -= 1; } - advance += i; - l -= i; + if i > 0 { + let n = stream.write(buf); + if n < i { + return n; + } + advance += i; + l -= i; + } + i + } else { + 0 + }; + + unsafe { + copy_nonoverlapping( + &buf[advance..] as *const _ as *const u8, + &mut stream.buf[wpos..] as *mut _ as *mut u8, + l, + ); } - i + stream.write = Some((wbase, wpos + l, wend)); + l + 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 fn fflush_unlocked(stream: &mut FILE) -> c_int { - if stream.wpos > stream.wbase { - stream.write(&[]); - if stream.wpos.is_null() { + if let Some((wbase, wpos, _)) = stream.write { + if wpos > wbase { + stream.write(&[]); + /* + if stream.wpos.is_null() { return -1; + } + */ } } - if stream.rpos < stream.rend { - stream.seek(stream.rpos as i64 - stream.rend as i64, SEEK_CUR); + if let Some((rpos, rend)) = stream.read { + if rpos < rend { + stream.seek(rpos as i64 - 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.write = None; + stream.read = None; 0 } diff --git a/src/stdio/src/internal.rs b/src/stdio/src/internal.rs index 35eb5f47e8..60ad4f3ebf 100644 --- a/src/stdio/src/internal.rs +++ b/src/stdio/src/internal.rs @@ -1,13 +1,15 @@ use super::{constants, FILE}; -use platform; use platform::types::*; -use core::{mem, ptr, slice}; 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 + if let Some((wbase, wpos, _)) = stream.write { + if (stream.flags & constants::F_APP > 0) && wpos > wbase { + constants::SEEK_END + } else { + constants::SEEK_CUR + } } else { constants::SEEK_CUR }, @@ -15,5 +17,15 @@ pub fn ftello(stream: &mut FILE) -> off_t { if pos < 0 { return pos; } - pos - (stream.rend as i64 - stream.rpos as i64) + (stream.wpos as i64 - stream.wbase as i64) + let rdiff = if let Some((rpos, rend)) = stream.read { + rend - rpos + } else { + 0 + }; + let wdiff = if let Some((wbase, wpos, _)) = stream.write { + wpos - wbase + } else { + 0 + }; + pos - rdiff as i64 + wdiff as i64 } diff --git a/src/stdio/src/lib.rs b/src/stdio/src/lib.rs index cec2032da6..014555f7c9 100644 --- a/src/stdio/src/lib.rs +++ b/src/stdio/src/lib.rs @@ -1,11 +1,18 @@ //! stdio implementation for Redox, following http://pubs.opengroup.org/onlinepubs/7908799/xsh/stdio.h.html #![no_std] +// For Vec #![feature(alloc)] +// For stdin, stdout and stderr +#![allow(non_upper_case_globals)] +#![deny(warnings)] +#[macro_use] extern crate alloc; extern crate errno; extern crate fcntl; +#[macro_use] +extern crate lazy_static; extern crate platform; extern crate stdlib; extern crate string; @@ -17,9 +24,11 @@ use core::fmt::{self, Error, Result}; use core::fmt::Write as WriteFmt; use core::sync::atomic::{AtomicBool, Ordering}; +use errno::STR_ERROR; use platform::types::*; use platform::{c_str, errno, Read, Write}; use errno::STR_ERROR; +use alloc::vec::Vec; use vl::VaList as va_list; mod scanf; @@ -35,42 +44,39 @@ mod helpers; mod internal; -#[repr(C)] +/// +/// This struct gets exposed to the C API. +/// pub struct FILE { - flags: c_int, - rpos: *mut u8, - rend: *mut u8, - wend: *mut u8, - wpos: *mut u8, - wbase: *mut u8, + flags: i32, + read: Option<(usize, usize)>, + write: Option<(usize, usize, usize)>, fd: c_int, - buf: *mut u8, - buf_size: size_t, + buf: Vec, buf_char: i8, lock: AtomicBool, - unget: size_t, + unget: usize, } 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 { + if let Some(_) = self.write { self.write(&[]); } - self.wpos = ptr::null_mut(); - self.wbase = ptr::null_mut(); - self.wend = ptr::null_mut(); + self.write = None; 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) }; + self.read = Some((self.buf.len() - 1, self.buf.len() - 1)); if self.flags & constants::F_EOF > 0 { false } else { @@ -78,71 +84,68 @@ impl FILE { } } 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) }; + self.read = None; + self.write = Some((self.unget, self.unget, self.buf.len() - 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 let Some((wbase, wpos, _)) = self.write { + let len = wpos - wbase; + let mut advance = 0; + let mut f_buf = &self.buf[wbase..wpos]; + 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.write = Some((self.unget, self.unget, self.buf.len() - 1)); + return to_write.len(); + } + if count < 0 { + self.write = None; + self.flags |= constants::F_ERR; + return 0; + } + rem -= count as usize; + if count as usize > len { + count -= len as isize; + f_buf = to_write; + f_filled = true; + advance = 0; + } + advance += count as usize; } - 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; } + // self.can_write() should always be called before self.write() + // and should automatically fill self.write if it returns true. + // Thus, we should never reach this + // -- Tommoa (20/6/2018) + unreachable!() } 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 adj = !(self.buf.len() == 0) as usize; + let mut file_buf = &mut self.buf[self.unget..]; let count = if buf.len() <= 1 + adj { - platform::read(self.fd, file_buf) + platform::read(self.fd, &mut file_buf) } else { - platform::read(self.fd, buf) + platform::read(self.fd, file_buf) + platform::read(self.fd, buf) + platform::read(self.fd, &mut file_buf) }; if count <= 0 { self.flags |= if count == 0 { @@ -155,17 +158,13 @@ impl FILE { 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); - } + // Adjust pointers + self.read = Some((self.unget + 1, self.unget + (count as usize))); + buf[buf.len() - 1] = file_buf[0]; buf.len() } pub fn seek(&self, off: off_t, whence: c_int) -> off_t { - unsafe { platform::lseek(self.fd, off, whence) } + platform::lseek(self.fd, off, whence) } } impl fmt::Write for FILE { @@ -203,12 +202,12 @@ pub extern "C" fn clearerr(stream: &mut FILE) { } #[no_mangle] -pub extern "C" fn ctermid(s: *mut c_char) -> *mut c_char { +pub extern "C" fn ctermid(_s: *mut c_char) -> *mut c_char { unimplemented!(); } #[no_mangle] -pub extern "C" fn cuserid(s: *mut c_char) -> *mut c_char { +pub extern "C" fn cuserid(_s: *mut c_char) -> *mut c_char { unimplemented!(); } @@ -233,7 +232,12 @@ pub extern "C" fn fclose(stream: &mut FILE) -> c_int { /// Open a file from a file descriptor #[no_mangle] pub extern "C" fn fdopen(fildes: c_int, mode: *const c_char) -> *mut FILE { - unsafe { helpers::_fdopen(fildes, mode) } + use core::ptr; + if let Some(mut f) = unsafe { helpers::_fdopen(fildes, mode) } { + &mut f + } else { + ptr::null_mut() + } } /// Check for EOF @@ -292,13 +296,12 @@ pub extern "C" fn fgetpos(stream: &mut FILE, pos: *mut fpos_t) -> c_int { /// Get a string from the stream #[no_mangle] pub extern "C" fn fgets(s: *mut c_char, n: c_int, stream: &mut FILE) -> *mut c_char { - use string::memchr; - use core::ptr::copy_nonoverlapping; + use platform::c_str_n_mut; flockfile(stream); - let mut ptr = s as *mut u8; - let mut n = n; + let st = unsafe { c_str_n_mut(s, n as usize) }; + // We can only fit one or less chars in if n <= 1 { funlockfile(stream); if n == 0 { @@ -309,52 +312,29 @@ pub extern "C" fn fgets(s: *mut c_char, n: c_int, stream: &mut FILE) -> *mut c_c } return s; } - while n > 0 { - 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 - } else { - z as usize - stream.rpos as usize + 1 - }; - let k = if k as i32 > n { n as usize } else { 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; - } - let c = getc_unlocked(stream); - if c < 0 { - break; - } - n -= 1; - - unsafe { - // Pointer stuff - *ptr = c as u8; - ptr = ptr.add(1); - } - - if c as u8 == b'\n' { - break; + // Scope this so we can reuse stream mutably + { + // We can't read from this stream + if !stream.can_read() { + return ptr::null_mut(); } } - if !s.is_null() { - unsafe { - *ptr = 0; + + if let Some((rpos, rend)) = stream.read { + let mut diff = 0; + for (_, mut c) in stream.buf[rpos..rend] + .iter() + .enumerate() + .take_while(|&(i, c)| *c != b'\n' && i < n as usize) + { + st[diff] = *c; + diff += 1; } + stream.read = Some((rpos + diff, rend)); + } else { + return ptr::null_mut(); } + funlockfile(stream); s } @@ -377,15 +357,15 @@ pub extern "C" fn flockfile(file: &mut FILE) { /// Open the file in mode `mode` #[no_mangle] -pub unsafe extern "C" fn fopen(filename: *const c_char, mode: *const c_char) -> *mut FILE { +pub extern "C" fn fopen(filename: *const c_char, mode: *const c_char) -> *mut FILE { use core::ptr; - let initial_mode = *mode; + let initial_mode = unsafe { *mode }; if initial_mode != b'r' as i8 && initial_mode != b'w' as i8 && initial_mode != b'a' as i8 { - platform::errno = errno::EINVAL; + unsafe { platform::errno = errno::EINVAL }; return ptr::null_mut(); } - let flags = helpers::parse_mode_flags(mode); + let flags = unsafe { helpers::parse_mode_flags(mode) }; let fd = fcntl::sys_open(filename, flags, 0o666); if fd < 0 { @@ -396,12 +376,13 @@ 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); - if f.is_null() { + let f = unsafe { helpers::_fdopen(fd, mode) }; + if let Some(mut fi) = f { + &mut fi + } else { platform::close(fd); return ptr::null_mut(); } - f } /// Insert a character into the stream @@ -434,41 +415,49 @@ pub extern "C" fn fread(ptr: *mut c_void, size: usize, nitems: usize, stream: &m 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 }; - 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; + if !stream.can_read() { + return 0; } - while l > 0 { - let k = if !stream.can_read() { - 0 - } else { - stream.read(unsafe { slice::from_raw_parts_mut(dest, l as usize) }) - }; - - if k == 0 { - funlockfile(stream); - return (len - l as usize) / 2; + if let Some((rpos, rend)) = stream.read { + if rend > rpos { + // We have some buffered data that can be read + let diff = rend - rpos; + let k = if diff < l as usize { diff } else { l as usize }; + unsafe { + // Copy data + copy_nonoverlapping(&stream.buf[rpos..] as *const _ as *const u8, dest, k); + // Reposition pointers + dest = dest.add(k); + } + stream.read = Some((rpos + k, rend)); + l -= k as isize; } - l -= k as isize; - unsafe { - // Reposition - dest = dest.add(k); + while l > 0 { + let k = if !stream.can_read() { + 0 + } else { + stream.read(unsafe { slice::from_raw_parts_mut(dest, l as usize) }) + }; + + if k == 0 { + funlockfile(stream); + return (len - l as usize) / 2; + } + + l -= k as isize; + unsafe { + // Reposition + dest = dest.add(k); + } } + + funlockfile(stream); + nitems + } else { + unreachable!() } - - funlockfile(stream); - nitems } #[no_mangle] @@ -493,7 +482,7 @@ pub extern "C" fn freopen( return ptr::null_mut(); } } else { - let new = unsafe { fopen(filename, mode) }; + let new = fopen(filename, mode); if new.is_null() { funlockfile(stream); fclose(stream); @@ -532,23 +521,22 @@ pub extern "C" fn fseeko(stream: &mut FILE, offset: off_t, whence: c_int) -> c_i let mut off = offset; flockfile(stream); // Adjust for what is currently in the buffer + let rdiff = if let Some((rpos, rend)) = stream.read { + rend - rpos + } else { + 0 + }; if whence == SEEK_CUR { - off -= (stream.rend as usize - stream.rpos as usize) as i64; + off -= (rdiff) as i64; } - if stream.wpos > stream.wbase { + if let Some(_) = stream.write { stream.write(&[]); - if stream.wpos.is_null() { - return -1; - } } - stream.wpos = ptr::null_mut(); - stream.wend = ptr::null_mut(); - stream.wbase = ptr::null_mut(); + stream.write = None; if stream.seek(off, whence) < 0 { return -1; } - stream.rpos = ptr::null_mut(); - stream.rend = ptr::null_mut(); + stream.read = None; stream.flags &= !F_EOF; funlockfile(stream); 0 @@ -562,7 +550,7 @@ pub unsafe extern "C" fn fsetpos(stream: &mut FILE, pos: *const fpos_t) -> c_int /// Get the current position of the cursor in the file #[no_mangle] -pub unsafe extern "C" fn ftell(stream: &mut FILE) -> c_long { +pub extern "C" fn ftell(stream: &mut FILE) -> c_long { ftello(stream) as c_long } @@ -618,40 +606,48 @@ pub 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(&mut *stdin) +pub extern "C" fn getchar() -> c_int { + fgetc(unsafe { &mut *stdin }) } /// Get a char from a stream without locking the stream #[no_mangle] 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); + if !stream.can_read() { + return -1; + } + if let Some((rpos, rend)) = stream.read { + if rpos < rend { + let ret = stream.buf[rpos] as c_int; + stream.read = Some((rpos + 1, rend)); ret + } else { + let mut c = [0u8; 1]; + if stream.read(&mut c) == 1 { + c[0] as c_int + } else { + -1 + } } } else { - let mut c = [0u8; 1]; - if stream.can_read() && stream.read(&mut c) == 1 { - c[0] as c_int - } else { - -1 - } + // We made a prior call to can_read() and are checking it, therefore we + // should never be in a case where stream.read is None + // -- Tommoa (20/6/2018) + unreachable!() } } /// Get a char from `stdin` without locking `stdin` #[no_mangle] -pub unsafe extern "C" fn getchar_unlocked() -> c_int { - getc_unlocked(&mut *stdin) +pub extern "C" fn getchar_unlocked() -> c_int { + getc_unlocked(unsafe { &mut *stdin }) } /// Get a string from `stdin` #[no_mangle] -pub unsafe extern "C" fn gets(s: *mut c_char) -> *mut c_char { +pub extern "C" fn gets(s: *mut c_char) -> *mut c_char { use core::i32; - fgets(s, i32::MAX, &mut *stdin) + fgets(s, i32::MAX, unsafe { &mut *stdin }) } /// Get an integer from `stream` @@ -673,7 +669,7 @@ pub 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!(); } @@ -683,14 +679,16 @@ pub unsafe extern "C" fn perror(s: *const c_char) { let mut w = platform::FileWriter(2); if errno >= 0 && errno < STR_ERROR.len() as c_int { - w.write_fmt(format_args!("{}: {}\n", s_str, STR_ERROR[errno as usize])); + w.write_fmt(format_args!("{}: {}\n", s_str, STR_ERROR[errno as usize])) + .unwrap(); } else { - w.write_fmt(format_args!("{}: Unknown error {}\n", s_str, errno)); + w.write_fmt(format_args!("{}: Unknown error {}\n", s_str, errno)) + .unwrap(); } } #[no_mangle] -pub extern "C" fn popen(command: *const c_char, mode: *const c_char) -> *mut FILE { +pub extern "C" fn popen(_command: *const c_char, _mode: *const c_char) -> *mut FILE { unimplemented!(); } @@ -705,46 +703,42 @@ pub 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, &mut *stdout) +pub extern "C" fn putchar(c: c_int) -> c_int { + fputc(c, unsafe { &mut *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 { - if c as i8 != stream.buf_char && stream.wpos < stream.wend { - unsafe { - *stream.wpos = c as u8; - stream.wpos = stream.wpos.add(1); - c + if stream.can_write() { + if let Some((wbase, wpos, wend)) = stream.write { + if c as i8 != stream.buf_char { + stream.buf[wpos] = c as u8; + stream.write = Some((wbase, wpos + 1, wend)); + c + } else if stream.write(&[c as u8]) == 1 { + c + } else { + -1 + } + } else { + -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 if stream.write(&[c as u8]) != 1 { - -1 - } else { - c - } + -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, &mut *stdout) +pub extern "C" fn putchar_unlocked(c: c_int) -> c_int { + putc_unlocked(c, unsafe { &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, &mut *stdout) > 0) || (putchar_unlocked(b'\n' as c_int) > 0); +pub extern "C" fn puts(s: *const c_char) -> c_int { + let ret = (fputs(s, unsafe { &mut *stdout }) > 0) || (putchar_unlocked(b'\n' as c_int) > 0); if ret { 0 } else { @@ -787,45 +781,41 @@ pub extern "C" fn rewind(stream: &mut FILE) { /// 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) { - unsafe { - setvbuf( - stream, - buf, - if buf.is_null() { _IONBF } else { _IOFBF }, - BUFSIZ as usize, - ) - }; + setvbuf( + stream, + buf, + if buf.is_null() { _IONBF } else { _IOFBF }, + BUFSIZ as usize, + ); } /// 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, - 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; +pub extern "C" fn setvbuf(stream: &mut FILE, buf: *mut c_char, mode: c_int, size: usize) -> c_int { + // Set a buffer of size `size` if no buffer is given + let buf = if buf.is_null() { + if mode != _IONBF { + vec![0u8; 1] + } else { + Vec::new() + } + } else { + // We trust the user on this one + // -- Tommoa (20/6/2018) + unsafe { Vec::from_raw_parts(buf as *mut u8, size, size) } + }; + stream.buf_char = -1; + if mode == _IOLBF { + stream.buf_char = b'\n' as i8; } - (*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; + stream.flags |= F_SVB; + stream.buf = buf; 0 } #[no_mangle] -pub extern "C" fn tempnam(dir: *const c_char, pfx: *const c_char) -> *mut c_char { +pub extern "C" fn tempnam(_dir: *const c_char, _pfx: *const c_char) -> *mut c_char { unimplemented!(); } @@ -835,7 +825,7 @@ pub extern "C" fn tmpfile() -> *mut FILE { } #[no_mangle] -pub extern "C" fn tmpnam(s: *mut c_char) -> *mut c_char { +pub extern "C" fn tmpnam(_s: *mut c_char) -> *mut c_char { unimplemented!(); } @@ -846,22 +836,23 @@ pub extern "C" fn ungetc(c: c_int, stream: &mut FILE) -> c_int { c } else { flockfile(stream); - if stream.rpos.is_null() { + if stream.read.is_none() { stream.can_read(); } - if stream.rpos.is_null() || stream.rpos <= unsafe { stream.buf.sub(stream.unget) } { + if let Some((rpos, rend)) = stream.read { + if rpos == 0 { + funlockfile(stream); + return -1; + } + stream.read = Some((rpos - 1, rend)); + stream.buf[rpos - 1] = c as u8; + stream.flags &= !F_EOF; funlockfile(stream); - return -1; + c + } else { + funlockfile(stream); + -1 } - - unsafe { - stream.rpos = stream.rpos.sub(1); - *stream.rpos = c as u8; - } - stream.flags &= !F_EOF; - - funlockfile(stream); - c } } diff --git a/src/stdio/src/printf.rs b/src/stdio/src/printf.rs index 75b619b258..da12b07c75 100644 --- a/src/stdio/src/printf.rs +++ b/src/stdio/src/printf.rs @@ -1,4 +1,4 @@ -use core::{fmt, slice, str}; +use core::{slice, str}; use platform::{self, Write}; use platform::types::*; @@ -17,90 +17,91 @@ pub unsafe fn printf(mut w: W, format: *const c_char, mut ap: VaList) if found_percent { match b as char { '%' => { - w.write_char('%'); found_percent = false; + w.write_char('%') } 'c' => { let a = ap.get::(); - w.write_u8(a as u8); - found_percent = false; + + w.write_u8(a as u8) } 'd' | 'i' => { let a = ap.get::(); - w.write_fmt(format_args!("{}", a)); - found_percent = false; + + w.write_fmt(format_args!("{}", a)) } 'f' | 'F' => { let a = ap.get::(); - w.write_fmt(format_args!("{}", a)); - found_percent = false; + + w.write_fmt(format_args!("{}", a)) } 'n' => { let _a = ap.get::(); found_percent = false; + Ok(()) } 'p' => { let a = ap.get::(); - w.write_fmt(format_args!("0x{:x}", a)); - found_percent = false; + + w.write_fmt(format_args!("0x{:x}", a)) } 's' => { let a = ap.get::(); + found_percent = false; + w.write_str(str::from_utf8_unchecked(platform::c_str( a as *const c_char, - ))); - - found_percent = false; + ))) } 'u' => { let a = ap.get::(); - w.write_fmt(format_args!("{}", a)); - found_percent = false; + + w.write_fmt(format_args!("{}", a)) } 'x' => { let a = ap.get::(); - w.write_fmt(format_args!("{:x}", a)); - found_percent = false; + + w.write_fmt(format_args!("{:x}", a)) } 'X' => { let a = ap.get::(); - w.write_fmt(format_args!("{:X}", a)); - found_percent = false; + + w.write_fmt(format_args!("{:X}", a)) } 'o' => { let a = ap.get::(); - w.write_fmt(format_args!("{:o}", a)); - found_percent = false; + + w.write_fmt(format_args!("{:o}", a)) } - '-' => {} - '+' => {} - ' ' => {} - '#' => {} - '0'...'9' => {} - _ => {} - } + '-' => Ok(()), + '+' => Ok(()), + ' ' => Ok(()), + '#' => Ok(()), + '0'...'9' => Ok(()), + _ => Ok(()), + }.expect("Error writing!") } else if b == b'%' { found_percent = true; } else { - w.write_u8(b); + w.write_u8(b).expect("Error writing char!"); } } From 50bfebfe3e3f7546f268e0edd55ffe1dc925d395 Mon Sep 17 00:00:00 2001 From: Tom Almeida Date: Thu, 21 Jun 2018 09:33:21 +0800 Subject: [PATCH 04/25] Fixed some issues with temporary files and moved some raw pointers to Option<&T>s --- src/stdio/src/helpers.rs | 28 ++++++++++++++++----------- src/stdio/src/lib.rs | 41 +++++++++++++++++++++------------------- 2 files changed, 39 insertions(+), 30 deletions(-) diff --git a/src/stdio/src/helpers.rs b/src/stdio/src/helpers.rs index d670509201..242e83f1ec 100644 --- a/src/stdio/src/helpers.rs +++ b/src/stdio/src/helpers.rs @@ -36,8 +36,10 @@ pub unsafe fn parse_mode_flags(mode_str: *const c_char) -> i32 { } /// Open a file with the file descriptor `fd` in the mode `mode` -pub unsafe fn _fdopen(fd: c_int, mode: *const c_char) -> Option { +pub unsafe fn _fdopen(fd: c_int, mode: *const c_char) -> Option<*mut FILE> { use string::strchr; + use stdlib::malloc; + use core::mem::size_of; if *mode != b'r' as i8 && *mode != b'w' as i8 && *mode != b'a' as i8 { platform::errno = errno::EINVAL; return None; @@ -61,16 +63,20 @@ pub unsafe fn _fdopen(fd: c_int, mode: *const c_char) -> Option { } // Allocate the file - Some(FILE { - flags: flags, - read: None, - write: None, - fd: fd, - buf: vec![0u8; BUFSIZ + UNGET], - buf_char: -1, - unget: UNGET, - lock: AtomicBool::new(false), - }) + let f = malloc(size_of::()) as *mut FILE; + if f.is_null() { + None + } else { + (*f).flags = flags; + (*f).read = None; + (*f).write = None; + (*f).fd = fd; + (*f).buf = vec![0u8; BUFSIZ + UNGET]; + (*f).buf_char = -1; + (*f).unget = UNGET; + (*f).lock = AtomicBool::new(false); + Some(f) + } } /// Write buffer `buf` of length `l` into `stream` diff --git a/src/stdio/src/lib.rs b/src/stdio/src/lib.rs index 014555f7c9..ee7080b900 100644 --- a/src/stdio/src/lib.rs +++ b/src/stdio/src/lib.rs @@ -48,14 +48,14 @@ mod internal; /// This struct gets exposed to the C API. /// pub struct FILE { - flags: i32, - read: Option<(usize, usize)>, - write: Option<(usize, usize, usize)>, - fd: c_int, - buf: Vec, + flags: i32, + read: Option<(usize, usize)>, + write: Option<(usize, usize, usize)>, + fd: c_int, + buf: Vec, buf_char: i8, - lock: AtomicBool, - unget: usize, + lock: AtomicBool, + unget: usize, } impl FILE { @@ -225,6 +225,8 @@ pub extern "C" fn fclose(stream: &mut FILE) -> c_int { unsafe { free(stream as *mut _ as *mut _); } + } else { + funlockfile(stream); } r } @@ -233,8 +235,8 @@ pub extern "C" fn fclose(stream: &mut FILE) -> c_int { #[no_mangle] pub extern "C" fn fdopen(fildes: c_int, mode: *const c_char) -> *mut FILE { use core::ptr; - if let Some(mut f) = unsafe { helpers::_fdopen(fildes, mode) } { - &mut f + if let Some(f) = unsafe { helpers::_fdopen(fildes, mode) } { + f } else { ptr::null_mut() } @@ -282,15 +284,17 @@ pub extern "C" fn fgetc(stream: &mut FILE) -> c_int { /// Get the position of the stream and store it in pos #[no_mangle] -pub extern "C" fn fgetpos(stream: &mut FILE, pos: *mut fpos_t) -> c_int { +pub extern "C" fn fgetpos(stream: &mut FILE, pos: Option<&mut fpos_t>) -> c_int { let off = internal::ftello(stream); if off < 0 { return -1; } - unsafe { - (*pos) = off; + if let Some(pos) = pos { + *pos = off; + 0 + } else { + -1 } - 0 } /// Get a string from the stream @@ -376,12 +380,11 @@ pub extern "C" fn fopen(filename: *const c_char, mode: *const c_char) -> *mut FI fcntl::sys_fcntl(fd, fcntl::F_SETFD, fcntl::FD_CLOEXEC); } - let f = unsafe { helpers::_fdopen(fd, mode) }; - if let Some(mut fi) = f { - &mut fi + if let Some(f) = unsafe { helpers::_fdopen(fd, mode) } { + f } else { platform::close(fd); - return ptr::null_mut(); + ptr::null_mut() } } @@ -544,8 +547,8 @@ pub extern "C" fn fseeko(stream: &mut FILE, offset: off_t, whence: c_int) -> c_i /// 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 { - fseek(stream, *pos as off_t, SEEK_SET) +pub unsafe extern "C" fn fsetpos(stream: &mut FILE, pos: Option<&fpos_t>) -> c_int { + fseek(stream, *pos.expect("You must specify a valid position"), SEEK_SET) } /// Get the current position of the cursor in the file From 5921f00e90a1657f4cafb6f887f04887c75073d9 Mon Sep 17 00:00:00 2001 From: Tom Almeida Date: Thu, 21 Jun 2018 00:16:20 +0800 Subject: [PATCH 05/25] Changed FILE to use a vector as a buffer instead of raw pointers. This allows us to remove the large majority of unsafe blocks from the code --- src/platform/src/lib.rs | 20 ++ src/stdio/Cargo.toml | 8 +- src/stdio/src/default.rs | 98 ++++---- src/stdio/src/helpers.rs | 102 ++++---- src/stdio/src/internal.rs | 22 +- src/stdio/src/lib.rs | 480 ++++++++++++++++++-------------------- src/stdio/src/printf.rs | 59 ++--- 7 files changed, 396 insertions(+), 393 deletions(-) diff --git a/src/platform/src/lib.rs b/src/platform/src/lib.rs index 3784ea30d7..433c6977cc 100644 --- a/src/platform/src/lib.rs +++ b/src/platform/src/lib.rs @@ -33,6 +33,26 @@ use types::*; #[no_mangle] pub static mut errno: c_int = 0; +pub unsafe fn c_str_mut<'a>(s: *mut c_char) -> &'a mut [u8] { + use core::usize; + + c_str_n_mut(s, usize::MAX) +} + +pub unsafe fn c_str_n_mut<'a>(s: *mut c_char, n: usize) -> &'a mut [u8] { + use core::slice; + + let mut size = 0; + + for _ in 0..n { + if *s.offset(size) == 0 { + break; + } + size += 1; + } + + slice::from_raw_parts_mut(s as *mut u8, size as usize) +} pub unsafe fn c_str<'a>(s: *const c_char) -> &'a [u8] { use core::usize; diff --git a/src/stdio/Cargo.toml b/src/stdio/Cargo.toml index aa82586feb..dd1e6126da 100644 --- a/src/stdio/Cargo.toml +++ b/src/stdio/Cargo.toml @@ -8,9 +8,11 @@ build = "build.rs" cbindgen = { path = "../../cbindgen" } [dependencies] -platform = { path = "../platform" } -va_list = { path = "../../va_list", features = ["no_std"] } +errno = { path = "../errno"} fcntl = { path = "../fcntl" } +lazy_static = "*" +platform = { path = "../platform" } +ralloc = { path = "../../ralloc" } string = { path = "../string" } stdlib = { path = "../stdlib" } -errno = { path = "../errno"} +va_list = { path = "../../va_list", features = ["no_std"] } diff --git a/src/stdio/src/default.rs b/src/stdio/src/default.rs index 25b5844719..2d96384a3b 100644 --- a/src/stdio/src/default.rs +++ b/src/stdio/src/default.rs @@ -1,7 +1,6 @@ use core::cell::UnsafeCell; use core::sync::atomic::AtomicBool; -use core::ptr; -use super::{constants, internal, BUFSIZ, FILE, UNGET}; +use super::{constants, BUFSIZ, FILE, UNGET}; struct GlobalFile(UnsafeCell); impl GlobalFile { @@ -15,63 +14,6 @@ impl GlobalFile { // statics need to be Sync unsafe impl Sync for GlobalFile {} -#[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: GlobalFile = GlobalFile::new(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), -}); - -#[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: GlobalFile = GlobalFile::new(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), -}); - -#[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: GlobalFile = GlobalFile::new(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), -}); - #[no_mangle] pub extern "C" fn __stdin() -> *mut FILE { unsafe { default_stdin.get() } @@ -86,3 +28,41 @@ pub extern "C" fn __stdout() -> *mut FILE { pub extern "C" fn __stderr() -> *mut FILE { unsafe { default_stderr.get() } } + +lazy_static! { + #[allow(non_upper_case_globals)] + static ref default_stdin: GlobalFile = GlobalFile::new(FILE { + flags: constants::F_PERM | constants::F_NOWR, + read: None, + write: None, + fd: 0, + buf: vec![0u8;(BUFSIZ + UNGET) as usize], + buf_char: -1, + unget: UNGET, + lock: AtomicBool::new(false), + }); + + #[allow(non_upper_case_globals)] + static ref default_stdout: GlobalFile = GlobalFile::new(FILE { + flags: constants::F_PERM | constants::F_NORD, + read: None, + write: None, + fd: 1, + buf: vec![0u8;(BUFSIZ + UNGET) as usize], + buf_char: b'\n' as i8, + unget: 0, + lock: AtomicBool::new(false), + }); + + #[allow(non_upper_case_globals)] + static ref default_stderr: GlobalFile = GlobalFile::new(FILE { + flags: constants::F_PERM | constants::F_NORD, + read: None, + write: None, + fd: 2, + buf: vec![0u8;(BUFSIZ + UNGET) as usize], + buf_char: -1, + unget: 0, + lock: AtomicBool::new(false), + }); +} diff --git a/src/stdio/src/helpers.rs b/src/stdio/src/helpers.rs index 378d2af3b6..d670509201 100644 --- a/src/stdio/src/helpers.rs +++ b/src/stdio/src/helpers.rs @@ -1,6 +1,4 @@ -use super::{internal, BUFSIZ, FILE, UNGET}; -use stdlib::calloc; -use core::{mem, ptr}; +use super::{BUFSIZ, FILE, UNGET}; use core::sync::atomic::AtomicBool; use platform::types::*; use super::constants::*; @@ -38,11 +36,11 @@ pub unsafe fn parse_mode_flags(mode_str: *const c_char) -> i32 { } /// 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 { +pub unsafe fn _fdopen(fd: c_int, mode: *const c_char) -> Option { 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(); + return None; } let mut flags = 0; @@ -62,23 +60,17 @@ pub unsafe fn _fdopen(fd: c_int, mode: *const c_char) -> *mut FILE { flags |= F_APP; } - let file = calloc(mem::size_of::() + BUFSIZ + UNGET, 1) as *mut FILE; // Allocate the file - (*file) = FILE { + Some(FILE { flags: flags, - rpos: ptr::null_mut(), - rend: ptr::null_mut(), - wend: ptr::null_mut(), - wpos: ptr::null_mut(), - wbase: ptr::null_mut(), + read: None, + write: None, fd: fd, - buf: (file as *mut u8).add(mem::size_of::() + UNGET), - buf_size: BUFSIZ, + buf: vec![0u8; BUFSIZ + UNGET], buf_char: -1, unget: UNGET, lock: AtomicBool::new(false), - }; - file + }) } /// Write buffer `buf` of length `l` into `stream` @@ -90,58 +82,68 @@ pub fn fwritex(buf: *const u8, l: size_t, stream: &mut FILE) -> size_t { let mut l = l; let mut advance = 0; - if stream.wend.is_null() && !stream.can_write() { + if stream.write.is_none() && !stream.can_write() { // 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(buf); - } + if let Some((wbase, wpos, wend)) = stream.write { + if l > wend - wpos { + // 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; + let i = if stream.buf_char >= 0 { + let mut i = l; + while i > 0 && buf[i - 1] != b'\n' { + i -= 1; } - advance += i; - l -= i; + if i > 0 { + let n = stream.write(buf); + if n < i { + return n; + } + advance += i; + l -= i; + } + i + } else { + 0 + }; + + unsafe { + copy_nonoverlapping( + &buf[advance..] as *const _ as *const u8, + &mut stream.buf[wpos..] as *mut _ as *mut u8, + l, + ); } - i + stream.write = Some((wbase, wpos + l, wend)); + l + 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 fn fflush_unlocked(stream: &mut FILE) -> c_int { - if stream.wpos > stream.wbase { - stream.write(&[]); - if stream.wpos.is_null() { + if let Some((wbase, wpos, _)) = stream.write { + if wpos > wbase { + stream.write(&[]); + /* + if stream.wpos.is_null() { return -1; + } + */ } } - if stream.rpos < stream.rend { - stream.seek(stream.rpos as i64 - stream.rend as i64, SEEK_CUR); + if let Some((rpos, rend)) = stream.read { + if rpos < rend { + stream.seek(rpos as i64 - 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.write = None; + stream.read = None; 0 } diff --git a/src/stdio/src/internal.rs b/src/stdio/src/internal.rs index 35eb5f47e8..60ad4f3ebf 100644 --- a/src/stdio/src/internal.rs +++ b/src/stdio/src/internal.rs @@ -1,13 +1,15 @@ use super::{constants, FILE}; -use platform; use platform::types::*; -use core::{mem, ptr, slice}; 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 + if let Some((wbase, wpos, _)) = stream.write { + if (stream.flags & constants::F_APP > 0) && wpos > wbase { + constants::SEEK_END + } else { + constants::SEEK_CUR + } } else { constants::SEEK_CUR }, @@ -15,5 +17,15 @@ pub fn ftello(stream: &mut FILE) -> off_t { if pos < 0 { return pos; } - pos - (stream.rend as i64 - stream.rpos as i64) + (stream.wpos as i64 - stream.wbase as i64) + let rdiff = if let Some((rpos, rend)) = stream.read { + rend - rpos + } else { + 0 + }; + let wdiff = if let Some((wbase, wpos, _)) = stream.write { + wpos - wbase + } else { + 0 + }; + pos - rdiff as i64 + wdiff as i64 } diff --git a/src/stdio/src/lib.rs b/src/stdio/src/lib.rs index ff4bd1a860..6ca0544476 100644 --- a/src/stdio/src/lib.rs +++ b/src/stdio/src/lib.rs @@ -4,9 +4,12 @@ #![feature(alloc)] #![feature(const_fn)] +#[macro_use] extern crate alloc; extern crate errno; extern crate fcntl; +#[macro_use] +extern crate lazy_static; extern crate platform; extern crate stdlib; extern crate string; @@ -18,9 +21,10 @@ use core::fmt::{self, Error, Result}; use core::fmt::Write as WriteFmt; use core::sync::atomic::{AtomicBool, Ordering}; +use errno::STR_ERROR; use platform::types::*; use platform::{c_str, errno, Read, Write}; -use errno::STR_ERROR; +use alloc::vec::Vec; use vl::VaList as va_list; mod scanf; @@ -36,42 +40,39 @@ mod helpers; mod internal; -#[repr(C)] +/// +/// This struct gets exposed to the C API. +/// pub struct FILE { - flags: c_int, - rpos: *mut u8, - rend: *mut u8, - wend: *mut u8, - wpos: *mut u8, - wbase: *mut u8, + flags: i32, + read: Option<(usize, usize)>, + write: Option<(usize, usize, usize)>, fd: c_int, - buf: *mut u8, - buf_size: size_t, + buf: Vec, buf_char: i8, lock: AtomicBool, - unget: size_t, + unget: usize, } 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 { + if let Some(_) = self.write { self.write(&[]); } - self.wpos = ptr::null_mut(); - self.wbase = ptr::null_mut(); - self.wend = ptr::null_mut(); + self.write = None; 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) }; + self.read = Some((self.buf.len() - 1, self.buf.len() - 1)); if self.flags & constants::F_EOF > 0 { false } else { @@ -79,71 +80,68 @@ impl FILE { } } 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) }; + self.read = None; + self.write = Some((self.unget, self.unget, self.buf.len() - 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 let Some((wbase, wpos, _)) = self.write { + let len = wpos - wbase; + let mut advance = 0; + let mut f_buf = &self.buf[wbase..wpos]; + 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.write = Some((self.unget, self.unget, self.buf.len() - 1)); + return to_write.len(); + } + if count < 0 { + self.write = None; + self.flags |= constants::F_ERR; + return 0; + } + rem -= count as usize; + if count as usize > len { + count -= len as isize; + f_buf = to_write; + f_filled = true; + advance = 0; + } + advance += count as usize; } - 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; } + // self.can_write() should always be called before self.write() + // and should automatically fill self.write if it returns true. + // Thus, we should never reach this + // -- Tommoa (20/6/2018) + unreachable!() } 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 adj = !(self.buf.len() == 0) as usize; + let mut file_buf = &mut self.buf[self.unget..]; let count = if buf.len() <= 1 + adj { - platform::read(self.fd, file_buf) + platform::read(self.fd, &mut file_buf) } else { - platform::read(self.fd, buf) + platform::read(self.fd, file_buf) + platform::read(self.fd, buf) + platform::read(self.fd, &mut file_buf) }; if count <= 0 { self.flags |= if count == 0 { @@ -156,17 +154,13 @@ impl FILE { 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); - } + // Adjust pointers + self.read = Some((self.unget + 1, self.unget + (count as usize))); + buf[buf.len() - 1] = file_buf[0]; buf.len() } pub fn seek(&self, off: off_t, whence: c_int) -> off_t { - unsafe { platform::lseek(self.fd, off, whence) } + platform::lseek(self.fd, off, whence) } } impl fmt::Write for FILE { @@ -204,12 +198,12 @@ pub extern "C" fn clearerr(stream: &mut FILE) { } #[no_mangle] -pub extern "C" fn ctermid(s: *mut c_char) -> *mut c_char { +pub extern "C" fn ctermid(_s: *mut c_char) -> *mut c_char { unimplemented!(); } #[no_mangle] -pub extern "C" fn cuserid(s: *mut c_char) -> *mut c_char { +pub extern "C" fn cuserid(_s: *mut c_char) -> *mut c_char { unimplemented!(); } @@ -234,7 +228,12 @@ pub extern "C" fn fclose(stream: &mut FILE) -> c_int { /// Open a file from a file descriptor #[no_mangle] pub extern "C" fn fdopen(fildes: c_int, mode: *const c_char) -> *mut FILE { - unsafe { helpers::_fdopen(fildes, mode) } + use core::ptr; + if let Some(mut f) = unsafe { helpers::_fdopen(fildes, mode) } { + &mut f + } else { + ptr::null_mut() + } } /// Check for EOF @@ -293,13 +292,12 @@ pub extern "C" fn fgetpos(stream: &mut FILE, pos: *mut fpos_t) -> c_int { /// Get a string from the stream #[no_mangle] pub extern "C" fn fgets(s: *mut c_char, n: c_int, stream: &mut FILE) -> *mut c_char { - use string::memchr; - use core::ptr::copy_nonoverlapping; + use platform::c_str_n_mut; flockfile(stream); - let mut ptr = s as *mut u8; - let mut n = n; + let st = unsafe { c_str_n_mut(s, n as usize) }; + // We can only fit one or less chars in if n <= 1 { funlockfile(stream); if n == 0 { @@ -310,52 +308,29 @@ pub extern "C" fn fgets(s: *mut c_char, n: c_int, stream: &mut FILE) -> *mut c_c } return s; } - while n > 0 { - 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 - } else { - z as usize - stream.rpos as usize + 1 - }; - let k = if k as i32 > n { n as usize } else { 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; - } - let c = getc_unlocked(stream); - if c < 0 { - break; - } - n -= 1; - - unsafe { - // Pointer stuff - *ptr = c as u8; - ptr = ptr.add(1); - } - - if c as u8 == b'\n' { - break; + // Scope this so we can reuse stream mutably + { + // We can't read from this stream + if !stream.can_read() { + return ptr::null_mut(); } } - if !s.is_null() { - unsafe { - *ptr = 0; + + if let Some((rpos, rend)) = stream.read { + let mut diff = 0; + for (_, mut c) in stream.buf[rpos..rend] + .iter() + .enumerate() + .take_while(|&(i, c)| *c != b'\n' && i < n as usize) + { + st[diff] = *c; + diff += 1; } + stream.read = Some((rpos + diff, rend)); + } else { + return ptr::null_mut(); } + funlockfile(stream); s } @@ -378,15 +353,15 @@ pub extern "C" fn flockfile(file: &mut FILE) { /// Open the file in mode `mode` #[no_mangle] -pub unsafe extern "C" fn fopen(filename: *const c_char, mode: *const c_char) -> *mut FILE { +pub extern "C" fn fopen(filename: *const c_char, mode: *const c_char) -> *mut FILE { use core::ptr; - let initial_mode = *mode; + let initial_mode = unsafe { *mode }; if initial_mode != b'r' as i8 && initial_mode != b'w' as i8 && initial_mode != b'a' as i8 { - platform::errno = errno::EINVAL; + unsafe { platform::errno = errno::EINVAL }; return ptr::null_mut(); } - let flags = helpers::parse_mode_flags(mode); + let flags = unsafe { helpers::parse_mode_flags(mode) }; let fd = fcntl::sys_open(filename, flags, 0o666); if fd < 0 { @@ -397,12 +372,13 @@ 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); - if f.is_null() { + let f = unsafe { helpers::_fdopen(fd, mode) }; + if let Some(mut fi) = f { + &mut fi + } else { platform::close(fd); return ptr::null_mut(); } - f } /// Insert a character into the stream @@ -435,41 +411,49 @@ pub extern "C" fn fread(ptr: *mut c_void, size: usize, nitems: usize, stream: &m 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 }; - 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; + if !stream.can_read() { + return 0; } - while l > 0 { - let k = if !stream.can_read() { - 0 - } else { - stream.read(unsafe { slice::from_raw_parts_mut(dest, l as usize) }) - }; - - if k == 0 { - funlockfile(stream); - return (len - l as usize) / 2; + if let Some((rpos, rend)) = stream.read { + if rend > rpos { + // We have some buffered data that can be read + let diff = rend - rpos; + let k = if diff < l as usize { diff } else { l as usize }; + unsafe { + // Copy data + copy_nonoverlapping(&stream.buf[rpos..] as *const _ as *const u8, dest, k); + // Reposition pointers + dest = dest.add(k); + } + stream.read = Some((rpos + k, rend)); + l -= k as isize; } - l -= k as isize; - unsafe { - // Reposition - dest = dest.add(k); + while l > 0 { + let k = if !stream.can_read() { + 0 + } else { + stream.read(unsafe { slice::from_raw_parts_mut(dest, l as usize) }) + }; + + if k == 0 { + funlockfile(stream); + return (len - l as usize) / 2; + } + + l -= k as isize; + unsafe { + // Reposition + dest = dest.add(k); + } } + + funlockfile(stream); + nitems + } else { + unreachable!() } - - funlockfile(stream); - nitems } #[no_mangle] @@ -494,7 +478,7 @@ pub extern "C" fn freopen( return ptr::null_mut(); } } else { - let new = unsafe { fopen(filename, mode) }; + let new = fopen(filename, mode); if new.is_null() { funlockfile(stream); fclose(stream); @@ -533,23 +517,22 @@ pub extern "C" fn fseeko(stream: &mut FILE, offset: off_t, whence: c_int) -> c_i let mut off = offset; flockfile(stream); // Adjust for what is currently in the buffer + let rdiff = if let Some((rpos, rend)) = stream.read { + rend - rpos + } else { + 0 + }; if whence == SEEK_CUR { - off -= (stream.rend as usize - stream.rpos as usize) as i64; + off -= (rdiff) as i64; } - if stream.wpos > stream.wbase { + if let Some(_) = stream.write { stream.write(&[]); - if stream.wpos.is_null() { - return -1; - } } - stream.wpos = ptr::null_mut(); - stream.wend = ptr::null_mut(); - stream.wbase = ptr::null_mut(); + stream.write = None; if stream.seek(off, whence) < 0 { return -1; } - stream.rpos = ptr::null_mut(); - stream.rend = ptr::null_mut(); + stream.read = None; stream.flags &= !F_EOF; funlockfile(stream); 0 @@ -563,7 +546,7 @@ pub unsafe extern "C" fn fsetpos(stream: &mut FILE, pos: *const fpos_t) -> c_int /// Get the current position of the cursor in the file #[no_mangle] -pub unsafe extern "C" fn ftell(stream: &mut FILE) -> c_long { +pub extern "C" fn ftell(stream: &mut FILE) -> c_long { ftello(stream) as c_long } @@ -619,38 +602,46 @@ pub 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 { +pub extern "C" fn getchar() -> c_int { fgetc(&mut *__stdin()) } /// Get a char from a stream without locking the stream #[no_mangle] 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); + if !stream.can_read() { + return -1; + } + if let Some((rpos, rend)) = stream.read { + if rpos < rend { + let ret = stream.buf[rpos] as c_int; + stream.read = Some((rpos + 1, rend)); ret + } else { + let mut c = [0u8; 1]; + if stream.read(&mut c) == 1 { + c[0] as c_int + } else { + -1 + } } } else { - let mut c = [0u8; 1]; - if stream.can_read() && stream.read(&mut c) == 1 { - c[0] as c_int - } else { - -1 - } + // We made a prior call to can_read() and are checking it, therefore we + // should never be in a case where stream.read is None + // -- Tommoa (20/6/2018) + unreachable!() } } /// Get a char from `stdin` without locking `stdin` #[no_mangle] -pub unsafe extern "C" fn getchar_unlocked() -> c_int { +pub extern "C" fn getchar_unlocked() -> c_int { getc_unlocked(&mut *__stdin()) } /// Get a string from `stdin` #[no_mangle] -pub unsafe extern "C" fn gets(s: *mut c_char) -> *mut c_char { +pub extern "C" fn gets(s: *mut c_char) -> *mut c_char { use core::i32; fgets(s, i32::MAX, &mut *__stdin()) } @@ -674,7 +665,7 @@ pub 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!(); } @@ -684,14 +675,16 @@ pub unsafe extern "C" fn perror(s: *const c_char) { let mut w = platform::FileWriter(2); if errno >= 0 && errno < STR_ERROR.len() as c_int { - w.write_fmt(format_args!("{}: {}\n", s_str, STR_ERROR[errno as usize])); + w.write_fmt(format_args!("{}: {}\n", s_str, STR_ERROR[errno as usize])) + .unwrap(); } else { - w.write_fmt(format_args!("{}: Unknown error {}\n", s_str, errno)); + w.write_fmt(format_args!("{}: Unknown error {}\n", s_str, errno)) + .unwrap(); } } #[no_mangle] -pub extern "C" fn popen(command: *const c_char, mode: *const c_char) -> *mut FILE { +pub extern "C" fn popen(_command: *const c_char, _mode: *const c_char) -> *mut FILE { unimplemented!(); } @@ -706,45 +699,41 @@ pub 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 { +pub extern "C" fn putchar(c: c_int) -> c_int { fputc(c, &mut *__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 { - if c as i8 != stream.buf_char && stream.wpos < stream.wend { - unsafe { - *stream.wpos = c as u8; - stream.wpos = stream.wpos.add(1); - c + if stream.can_write() { + if let Some((wbase, wpos, wend)) = stream.write { + if c as i8 != stream.buf_char { + stream.buf[wpos] = c as u8; + stream.write = Some((wbase, wpos + 1, wend)); + c + } else if stream.write(&[c as u8]) == 1 { + c + } else { + -1 + } + } else { + -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 if stream.write(&[c as u8]) != 1 { - -1 - } else { - c - } + -1 } } /// Put a character `c` into `stdout` without locking `stdout` #[no_mangle] -pub unsafe extern "C" fn putchar_unlocked(c: c_int) -> c_int { +pub extern "C" fn putchar_unlocked(c: c_int) -> c_int { 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 { +pub extern "C" fn puts(s: *const c_char) -> c_int { let ret = (fputs(s, &mut *__stdout()) > 0) || (putchar_unlocked(b'\n' as c_int) > 0); if ret { 0 @@ -788,45 +777,41 @@ pub extern "C" fn rewind(stream: &mut FILE) { /// 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) { - unsafe { - setvbuf( - stream, - buf, - if buf.is_null() { _IONBF } else { _IOFBF }, - BUFSIZ as usize, - ) - }; + setvbuf( + stream, + buf, + if buf.is_null() { _IONBF } else { _IOFBF }, + BUFSIZ as usize, + ); } /// 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, - 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; +pub extern "C" fn setvbuf(stream: &mut FILE, buf: *mut c_char, mode: c_int, size: usize) -> c_int { + // Set a buffer of size `size` if no buffer is given + let buf = if buf.is_null() { + if mode != _IONBF { + vec![0u8; 1] + } else { + Vec::new() + } + } else { + // We trust the user on this one + // -- Tommoa (20/6/2018) + unsafe { Vec::from_raw_parts(buf as *mut u8, size, size) } + }; + stream.buf_char = -1; + if mode == _IOLBF { + stream.buf_char = b'\n' as i8; } - (*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; + stream.flags |= F_SVB; + stream.buf = buf; 0 } #[no_mangle] -pub extern "C" fn tempnam(dir: *const c_char, pfx: *const c_char) -> *mut c_char { +pub extern "C" fn tempnam(_dir: *const c_char, _pfx: *const c_char) -> *mut c_char { unimplemented!(); } @@ -836,7 +821,7 @@ pub extern "C" fn tmpfile() -> *mut FILE { } #[no_mangle] -pub extern "C" fn tmpnam(s: *mut c_char) -> *mut c_char { +pub extern "C" fn tmpnam(_s: *mut c_char) -> *mut c_char { unimplemented!(); } @@ -847,22 +832,23 @@ pub extern "C" fn ungetc(c: c_int, stream: &mut FILE) -> c_int { c } else { flockfile(stream); - if stream.rpos.is_null() { + if stream.read.is_none() { stream.can_read(); } - if stream.rpos.is_null() || stream.rpos <= unsafe { stream.buf.sub(stream.unget) } { + if let Some((rpos, rend)) = stream.read { + if rpos == 0 { + funlockfile(stream); + return -1; + } + stream.read = Some((rpos - 1, rend)); + stream.buf[rpos - 1] = c as u8; + stream.flags &= !F_EOF; funlockfile(stream); - return -1; + c + } else { + funlockfile(stream); + -1 } - - unsafe { - stream.rpos = stream.rpos.sub(1); - *stream.rpos = c as u8; - } - stream.flags &= !F_EOF; - - funlockfile(stream); - c } } diff --git a/src/stdio/src/printf.rs b/src/stdio/src/printf.rs index 75b619b258..da12b07c75 100644 --- a/src/stdio/src/printf.rs +++ b/src/stdio/src/printf.rs @@ -1,4 +1,4 @@ -use core::{fmt, slice, str}; +use core::{slice, str}; use platform::{self, Write}; use platform::types::*; @@ -17,90 +17,91 @@ pub unsafe fn printf(mut w: W, format: *const c_char, mut ap: VaList) if found_percent { match b as char { '%' => { - w.write_char('%'); found_percent = false; + w.write_char('%') } 'c' => { let a = ap.get::(); - w.write_u8(a as u8); - found_percent = false; + + w.write_u8(a as u8) } 'd' | 'i' => { let a = ap.get::(); - w.write_fmt(format_args!("{}", a)); - found_percent = false; + + w.write_fmt(format_args!("{}", a)) } 'f' | 'F' => { let a = ap.get::(); - w.write_fmt(format_args!("{}", a)); - found_percent = false; + + w.write_fmt(format_args!("{}", a)) } 'n' => { let _a = ap.get::(); found_percent = false; + Ok(()) } 'p' => { let a = ap.get::(); - w.write_fmt(format_args!("0x{:x}", a)); - found_percent = false; + + w.write_fmt(format_args!("0x{:x}", a)) } 's' => { let a = ap.get::(); + found_percent = false; + w.write_str(str::from_utf8_unchecked(platform::c_str( a as *const c_char, - ))); - - found_percent = false; + ))) } 'u' => { let a = ap.get::(); - w.write_fmt(format_args!("{}", a)); - found_percent = false; + + w.write_fmt(format_args!("{}", a)) } 'x' => { let a = ap.get::(); - w.write_fmt(format_args!("{:x}", a)); - found_percent = false; + + w.write_fmt(format_args!("{:x}", a)) } 'X' => { let a = ap.get::(); - w.write_fmt(format_args!("{:X}", a)); - found_percent = false; + + w.write_fmt(format_args!("{:X}", a)) } 'o' => { let a = ap.get::(); - w.write_fmt(format_args!("{:o}", a)); - found_percent = false; + + w.write_fmt(format_args!("{:o}", a)) } - '-' => {} - '+' => {} - ' ' => {} - '#' => {} - '0'...'9' => {} - _ => {} - } + '-' => Ok(()), + '+' => Ok(()), + ' ' => Ok(()), + '#' => Ok(()), + '0'...'9' => Ok(()), + _ => Ok(()), + }.expect("Error writing!") } else if b == b'%' { found_percent = true; } else { - w.write_u8(b); + w.write_u8(b).expect("Error writing char!"); } } From b5529c9b718bc88b284686b577495e95243460c7 Mon Sep 17 00:00:00 2001 From: Tom Almeida Date: Thu, 21 Jun 2018 09:33:21 +0800 Subject: [PATCH 06/25] Fixed some issues with temporary files and moved some raw pointers to Option<&T>s --- src/stdio/src/helpers.rs | 28 ++++++++++++++++----------- src/stdio/src/lib.rs | 41 +++++++++++++++++++++------------------- 2 files changed, 39 insertions(+), 30 deletions(-) diff --git a/src/stdio/src/helpers.rs b/src/stdio/src/helpers.rs index d670509201..242e83f1ec 100644 --- a/src/stdio/src/helpers.rs +++ b/src/stdio/src/helpers.rs @@ -36,8 +36,10 @@ pub unsafe fn parse_mode_flags(mode_str: *const c_char) -> i32 { } /// Open a file with the file descriptor `fd` in the mode `mode` -pub unsafe fn _fdopen(fd: c_int, mode: *const c_char) -> Option { +pub unsafe fn _fdopen(fd: c_int, mode: *const c_char) -> Option<*mut FILE> { use string::strchr; + use stdlib::malloc; + use core::mem::size_of; if *mode != b'r' as i8 && *mode != b'w' as i8 && *mode != b'a' as i8 { platform::errno = errno::EINVAL; return None; @@ -61,16 +63,20 @@ pub unsafe fn _fdopen(fd: c_int, mode: *const c_char) -> Option { } // Allocate the file - Some(FILE { - flags: flags, - read: None, - write: None, - fd: fd, - buf: vec![0u8; BUFSIZ + UNGET], - buf_char: -1, - unget: UNGET, - lock: AtomicBool::new(false), - }) + let f = malloc(size_of::()) as *mut FILE; + if f.is_null() { + None + } else { + (*f).flags = flags; + (*f).read = None; + (*f).write = None; + (*f).fd = fd; + (*f).buf = vec![0u8; BUFSIZ + UNGET]; + (*f).buf_char = -1; + (*f).unget = UNGET; + (*f).lock = AtomicBool::new(false); + Some(f) + } } /// Write buffer `buf` of length `l` into `stream` diff --git a/src/stdio/src/lib.rs b/src/stdio/src/lib.rs index 6ca0544476..8912eccdaf 100644 --- a/src/stdio/src/lib.rs +++ b/src/stdio/src/lib.rs @@ -44,14 +44,14 @@ mod internal; /// This struct gets exposed to the C API. /// pub struct FILE { - flags: i32, - read: Option<(usize, usize)>, - write: Option<(usize, usize, usize)>, - fd: c_int, - buf: Vec, + flags: i32, + read: Option<(usize, usize)>, + write: Option<(usize, usize, usize)>, + fd: c_int, + buf: Vec, buf_char: i8, - lock: AtomicBool, - unget: usize, + lock: AtomicBool, + unget: usize, } impl FILE { @@ -221,6 +221,8 @@ pub extern "C" fn fclose(stream: &mut FILE) -> c_int { unsafe { free(stream as *mut _ as *mut _); } + } else { + funlockfile(stream); } r } @@ -229,8 +231,8 @@ pub extern "C" fn fclose(stream: &mut FILE) -> c_int { #[no_mangle] pub extern "C" fn fdopen(fildes: c_int, mode: *const c_char) -> *mut FILE { use core::ptr; - if let Some(mut f) = unsafe { helpers::_fdopen(fildes, mode) } { - &mut f + if let Some(f) = unsafe { helpers::_fdopen(fildes, mode) } { + f } else { ptr::null_mut() } @@ -278,15 +280,17 @@ pub extern "C" fn fgetc(stream: &mut FILE) -> c_int { /// Get the position of the stream and store it in pos #[no_mangle] -pub extern "C" fn fgetpos(stream: &mut FILE, pos: *mut fpos_t) -> c_int { +pub extern "C" fn fgetpos(stream: &mut FILE, pos: Option<&mut fpos_t>) -> c_int { let off = internal::ftello(stream); if off < 0 { return -1; } - unsafe { - (*pos) = off; + if let Some(pos) = pos { + *pos = off; + 0 + } else { + -1 } - 0 } /// Get a string from the stream @@ -372,12 +376,11 @@ pub extern "C" fn fopen(filename: *const c_char, mode: *const c_char) -> *mut FI fcntl::sys_fcntl(fd, fcntl::F_SETFD, fcntl::FD_CLOEXEC); } - let f = unsafe { helpers::_fdopen(fd, mode) }; - if let Some(mut fi) = f { - &mut fi + if let Some(f) = unsafe { helpers::_fdopen(fd, mode) } { + f } else { platform::close(fd); - return ptr::null_mut(); + ptr::null_mut() } } @@ -540,8 +543,8 @@ pub extern "C" fn fseeko(stream: &mut FILE, offset: off_t, whence: c_int) -> c_i /// 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 { - fseek(stream, *pos as off_t, SEEK_SET) +pub unsafe extern "C" fn fsetpos(stream: &mut FILE, pos: Option<&fpos_t>) -> c_int { + fseek(stream, *pos.expect("You must specify a valid position"), SEEK_SET) } /// Get the current position of the cursor in the file From 6bc28203ca0e945bc2b0a5400ffa0ddb444cdfac Mon Sep 17 00:00:00 2001 From: Tom Almeida Date: Thu, 21 Jun 2018 00:16:20 +0800 Subject: [PATCH 07/25] Changed FILE to use a vector as a buffer instead of raw pointers. This allows us to remove the large majority of unsafe blocks from the code --- src/stdio/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/stdio/src/lib.rs b/src/stdio/src/lib.rs index 8912eccdaf..8f07338b73 100644 --- a/src/stdio/src/lib.rs +++ b/src/stdio/src/lib.rs @@ -1,6 +1,7 @@ //! stdio implementation for Redox, following http://pubs.opengroup.org/onlinepubs/7908799/xsh/stdio.h.html #![no_std] +// For Vec #![feature(alloc)] #![feature(const_fn)] From 7e731e0b01924f46204346bc604b5827845805a6 Mon Sep 17 00:00:00 2001 From: Tom Almeida Date: Sat, 23 Jun 2018 13:08:28 +0800 Subject: [PATCH 08/25] Made sure lazy_static works with no_std --- src/stdio/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stdio/Cargo.toml b/src/stdio/Cargo.toml index dd1e6126da..1f2f2794e9 100644 --- a/src/stdio/Cargo.toml +++ b/src/stdio/Cargo.toml @@ -10,7 +10,7 @@ cbindgen = { path = "../../cbindgen" } [dependencies] errno = { path = "../errno"} fcntl = { path = "../fcntl" } -lazy_static = "*" +lazy_static = { version = "*", features = ["nightly", "spin_no_std"] } platform = { path = "../platform" } ralloc = { path = "../../ralloc" } string = { path = "../string" } From 71fa4026f56043b9c222c1106bb7e474d3a53107 Mon Sep 17 00:00:00 2001 From: Tom Almeida Date: Thu, 21 Jun 2018 00:16:20 +0800 Subject: [PATCH 09/25] Changed FILE to use a vector as a buffer instead of raw pointers. This allows us to remove the large majority of unsafe blocks from the code --- src/platform/src/lib.rs | 20 ++ src/stdio/Cargo.toml | 8 +- src/stdio/src/default.rs | 92 +++---- src/stdio/src/helpers.rs | 102 ++++---- src/stdio/src/internal.rs | 22 +- src/stdio/src/lib.rs | 488 ++++++++++++++++++-------------------- src/stdio/src/printf.rs | 59 ++--- 7 files changed, 397 insertions(+), 394 deletions(-) diff --git a/src/platform/src/lib.rs b/src/platform/src/lib.rs index 78bc4c39ca..185354ff85 100644 --- a/src/platform/src/lib.rs +++ b/src/platform/src/lib.rs @@ -40,6 +40,26 @@ static ALLOCATOR: ralloc::Allocator = ralloc::Allocator; #[no_mangle] pub static mut errno: c_int = 0; +pub unsafe fn c_str_mut<'a>(s: *mut c_char) -> &'a mut [u8] { + use core::usize; + + c_str_n_mut(s, usize::MAX) +} + +pub unsafe fn c_str_n_mut<'a>(s: *mut c_char, n: usize) -> &'a mut [u8] { + use core::slice; + + let mut size = 0; + + for _ in 0..n { + if *s.offset(size) == 0 { + break; + } + size += 1; + } + + slice::from_raw_parts_mut(s as *mut u8, size as usize) +} pub unsafe fn c_str<'a>(s: *const c_char) -> &'a [u8] { use core::usize; diff --git a/src/stdio/Cargo.toml b/src/stdio/Cargo.toml index aa82586feb..dd1e6126da 100644 --- a/src/stdio/Cargo.toml +++ b/src/stdio/Cargo.toml @@ -8,9 +8,11 @@ build = "build.rs" cbindgen = { path = "../../cbindgen" } [dependencies] -platform = { path = "../platform" } -va_list = { path = "../../va_list", features = ["no_std"] } +errno = { path = "../errno"} fcntl = { path = "../fcntl" } +lazy_static = "*" +platform = { path = "../platform" } +ralloc = { path = "../../ralloc" } string = { path = "../string" } stdlib = { path = "../stdlib" } -errno = { path = "../errno"} +va_list = { path = "../../va_list", features = ["no_std"] } diff --git a/src/stdio/src/default.rs b/src/stdio/src/default.rs index 9337856c5d..4adbe01543 100644 --- a/src/stdio/src/default.rs +++ b/src/stdio/src/default.rs @@ -1,63 +1,43 @@ use core::sync::atomic::AtomicBool; -use core::ptr; -use super::{constants, internal, BUFSIZ, FILE, UNGET}; +use super::{constants, BUFSIZ, FILE, UNGET}; -#[allow(non_upper_case_globals)] -static mut default_stdin_buf: [u8; BUFSIZ as usize + UNGET] = [0; BUFSIZ as usize + UNGET]; +lazy_static! { + #[allow(non_upper_case_globals)] + static ref default_stdin: FILE = FILE { + flags: constants::F_PERM | constants::F_NOWR | constants::F_BADJ, + read: None, + write: None, + fd: 0, + buf: vec![0u8;(BUFSIZ + UNGET) as usize], + buf_char: -1, + unget: UNGET, + lock: AtomicBool::new(false), + }; -#[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), -}; + #[allow(non_upper_case_globals)] + static ref default_stdout: FILE = FILE { + flags: constants::F_PERM | constants::F_NORD | constants::F_BADJ, + read: None, + write: None, + fd: 1, + buf: vec![0u8;(BUFSIZ + UNGET) as usize], + buf_char: b'\n' as i8, + unget: 0, + lock: AtomicBool::new(false), + }; -#[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), -}; - -#[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), -}; + #[allow(non_upper_case_globals)] + static ref default_stderr: FILE = FILE { + flags: constants::F_PERM | constants::F_NORD | constants::F_BADJ, + read: None, + write: None, + fd: 2, + buf: vec![0u8;(BUFSIZ + UNGET) as usize], + buf_char: -1, + unget: 0, + lock: AtomicBool::new(false), + }; +} #[no_mangle] pub extern "C" fn __stdin() -> *mut FILE { diff --git a/src/stdio/src/helpers.rs b/src/stdio/src/helpers.rs index 378d2af3b6..d670509201 100644 --- a/src/stdio/src/helpers.rs +++ b/src/stdio/src/helpers.rs @@ -1,6 +1,4 @@ -use super::{internal, BUFSIZ, FILE, UNGET}; -use stdlib::calloc; -use core::{mem, ptr}; +use super::{BUFSIZ, FILE, UNGET}; use core::sync::atomic::AtomicBool; use platform::types::*; use super::constants::*; @@ -38,11 +36,11 @@ pub unsafe fn parse_mode_flags(mode_str: *const c_char) -> i32 { } /// 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 { +pub unsafe fn _fdopen(fd: c_int, mode: *const c_char) -> Option { 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(); + return None; } let mut flags = 0; @@ -62,23 +60,17 @@ pub unsafe fn _fdopen(fd: c_int, mode: *const c_char) -> *mut FILE { flags |= F_APP; } - let file = calloc(mem::size_of::() + BUFSIZ + UNGET, 1) as *mut FILE; // Allocate the file - (*file) = FILE { + Some(FILE { flags: flags, - rpos: ptr::null_mut(), - rend: ptr::null_mut(), - wend: ptr::null_mut(), - wpos: ptr::null_mut(), - wbase: ptr::null_mut(), + read: None, + write: None, fd: fd, - buf: (file as *mut u8).add(mem::size_of::() + UNGET), - buf_size: BUFSIZ, + buf: vec![0u8; BUFSIZ + UNGET], buf_char: -1, unget: UNGET, lock: AtomicBool::new(false), - }; - file + }) } /// Write buffer `buf` of length `l` into `stream` @@ -90,58 +82,68 @@ pub fn fwritex(buf: *const u8, l: size_t, stream: &mut FILE) -> size_t { let mut l = l; let mut advance = 0; - if stream.wend.is_null() && !stream.can_write() { + if stream.write.is_none() && !stream.can_write() { // 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(buf); - } + if let Some((wbase, wpos, wend)) = stream.write { + if l > wend - wpos { + // 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; + let i = if stream.buf_char >= 0 { + let mut i = l; + while i > 0 && buf[i - 1] != b'\n' { + i -= 1; } - advance += i; - l -= i; + if i > 0 { + let n = stream.write(buf); + if n < i { + return n; + } + advance += i; + l -= i; + } + i + } else { + 0 + }; + + unsafe { + copy_nonoverlapping( + &buf[advance..] as *const _ as *const u8, + &mut stream.buf[wpos..] as *mut _ as *mut u8, + l, + ); } - i + stream.write = Some((wbase, wpos + l, wend)); + l + 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 fn fflush_unlocked(stream: &mut FILE) -> c_int { - if stream.wpos > stream.wbase { - stream.write(&[]); - if stream.wpos.is_null() { + if let Some((wbase, wpos, _)) = stream.write { + if wpos > wbase { + stream.write(&[]); + /* + if stream.wpos.is_null() { return -1; + } + */ } } - if stream.rpos < stream.rend { - stream.seek(stream.rpos as i64 - stream.rend as i64, SEEK_CUR); + if let Some((rpos, rend)) = stream.read { + if rpos < rend { + stream.seek(rpos as i64 - 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.write = None; + stream.read = None; 0 } diff --git a/src/stdio/src/internal.rs b/src/stdio/src/internal.rs index 35eb5f47e8..60ad4f3ebf 100644 --- a/src/stdio/src/internal.rs +++ b/src/stdio/src/internal.rs @@ -1,13 +1,15 @@ use super::{constants, FILE}; -use platform; use platform::types::*; -use core::{mem, ptr, slice}; 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 + if let Some((wbase, wpos, _)) = stream.write { + if (stream.flags & constants::F_APP > 0) && wpos > wbase { + constants::SEEK_END + } else { + constants::SEEK_CUR + } } else { constants::SEEK_CUR }, @@ -15,5 +17,15 @@ pub fn ftello(stream: &mut FILE) -> off_t { if pos < 0 { return pos; } - pos - (stream.rend as i64 - stream.rpos as i64) + (stream.wpos as i64 - stream.wbase as i64) + let rdiff = if let Some((rpos, rend)) = stream.read { + rend - rpos + } else { + 0 + }; + let wdiff = if let Some((wbase, wpos, _)) = stream.write { + wpos - wbase + } else { + 0 + }; + pos - rdiff as i64 + wdiff as i64 } diff --git a/src/stdio/src/lib.rs b/src/stdio/src/lib.rs index ff4bd1a860..5343da4b50 100644 --- a/src/stdio/src/lib.rs +++ b/src/stdio/src/lib.rs @@ -4,9 +4,12 @@ #![feature(alloc)] #![feature(const_fn)] +#[macro_use] extern crate alloc; extern crate errno; extern crate fcntl; +#[macro_use] +extern crate lazy_static; extern crate platform; extern crate stdlib; extern crate string; @@ -18,9 +21,10 @@ use core::fmt::{self, Error, Result}; use core::fmt::Write as WriteFmt; use core::sync::atomic::{AtomicBool, Ordering}; +use errno::STR_ERROR; use platform::types::*; use platform::{c_str, errno, Read, Write}; -use errno::STR_ERROR; +use alloc::vec::Vec; use vl::VaList as va_list; mod scanf; @@ -36,42 +40,39 @@ mod helpers; mod internal; -#[repr(C)] +/// +/// This struct gets exposed to the C API. +/// pub struct FILE { - flags: c_int, - rpos: *mut u8, - rend: *mut u8, - wend: *mut u8, - wpos: *mut u8, - wbase: *mut u8, + flags: i32, + read: Option<(usize, usize)>, + write: Option<(usize, usize, usize)>, fd: c_int, - buf: *mut u8, - buf_size: size_t, + buf: Vec, buf_char: i8, lock: AtomicBool, - unget: size_t, + unget: usize, } 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 { + if let Some(_) = self.write { self.write(&[]); } - self.wpos = ptr::null_mut(); - self.wbase = ptr::null_mut(); - self.wend = ptr::null_mut(); + self.write = None; 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) }; + self.read = Some((self.buf.len() - 1, self.buf.len() - 1)); if self.flags & constants::F_EOF > 0 { false } else { @@ -79,71 +80,68 @@ impl FILE { } } 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) }; + self.read = None; + self.write = Some((self.unget, self.unget, self.buf.len() - 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 let Some((wbase, wpos, _)) = self.write { + let len = wpos - wbase; + let mut advance = 0; + let mut f_buf = &self.buf[wbase..wpos]; + 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.write = Some((self.unget, self.unget, self.buf.len() - 1)); + return to_write.len(); + } + if count < 0 { + self.write = None; + self.flags |= constants::F_ERR; + return 0; + } + rem -= count as usize; + if count as usize > len { + count -= len as isize; + f_buf = to_write; + f_filled = true; + advance = 0; + } + advance += count as usize; } - 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; } + // self.can_write() should always be called before self.write() + // and should automatically fill self.write if it returns true. + // Thus, we should never reach this + // -- Tommoa (20/6/2018) + unreachable!() } 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 adj = !(self.buf.len() == 0) as usize; + let mut file_buf = &mut self.buf[self.unget..]; let count = if buf.len() <= 1 + adj { - platform::read(self.fd, file_buf) + platform::read(self.fd, &mut file_buf) } else { - platform::read(self.fd, buf) + platform::read(self.fd, file_buf) + platform::read(self.fd, buf) + platform::read(self.fd, &mut file_buf) }; if count <= 0 { self.flags |= if count == 0 { @@ -156,17 +154,13 @@ impl FILE { 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); - } + // Adjust pointers + self.read = Some((self.unget + 1, self.unget + (count as usize))); + buf[buf.len() - 1] = file_buf[0]; buf.len() } pub fn seek(&self, off: off_t, whence: c_int) -> off_t { - unsafe { platform::lseek(self.fd, off, whence) } + platform::lseek(self.fd, off, whence) } } impl fmt::Write for FILE { @@ -204,12 +198,12 @@ pub extern "C" fn clearerr(stream: &mut FILE) { } #[no_mangle] -pub extern "C" fn ctermid(s: *mut c_char) -> *mut c_char { +pub extern "C" fn ctermid(_s: *mut c_char) -> *mut c_char { unimplemented!(); } #[no_mangle] -pub extern "C" fn cuserid(s: *mut c_char) -> *mut c_char { +pub extern "C" fn cuserid(_s: *mut c_char) -> *mut c_char { unimplemented!(); } @@ -234,7 +228,12 @@ pub extern "C" fn fclose(stream: &mut FILE) -> c_int { /// Open a file from a file descriptor #[no_mangle] pub extern "C" fn fdopen(fildes: c_int, mode: *const c_char) -> *mut FILE { - unsafe { helpers::_fdopen(fildes, mode) } + use core::ptr; + if let Some(mut f) = unsafe { helpers::_fdopen(fildes, mode) } { + &mut f + } else { + ptr::null_mut() + } } /// Check for EOF @@ -293,13 +292,12 @@ pub extern "C" fn fgetpos(stream: &mut FILE, pos: *mut fpos_t) -> c_int { /// Get a string from the stream #[no_mangle] pub extern "C" fn fgets(s: *mut c_char, n: c_int, stream: &mut FILE) -> *mut c_char { - use string::memchr; - use core::ptr::copy_nonoverlapping; + use platform::c_str_n_mut; flockfile(stream); - let mut ptr = s as *mut u8; - let mut n = n; + let st = unsafe { c_str_n_mut(s, n as usize) }; + // We can only fit one or less chars in if n <= 1 { funlockfile(stream); if n == 0 { @@ -310,52 +308,29 @@ pub extern "C" fn fgets(s: *mut c_char, n: c_int, stream: &mut FILE) -> *mut c_c } return s; } - while n > 0 { - 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 - } else { - z as usize - stream.rpos as usize + 1 - }; - let k = if k as i32 > n { n as usize } else { 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; - } - let c = getc_unlocked(stream); - if c < 0 { - break; - } - n -= 1; - - unsafe { - // Pointer stuff - *ptr = c as u8; - ptr = ptr.add(1); - } - - if c as u8 == b'\n' { - break; + // Scope this so we can reuse stream mutably + { + // We can't read from this stream + if !stream.can_read() { + return ptr::null_mut(); } } - if !s.is_null() { - unsafe { - *ptr = 0; + + if let Some((rpos, rend)) = stream.read { + let mut diff = 0; + for (_, mut c) in stream.buf[rpos..rend] + .iter() + .enumerate() + .take_while(|&(i, c)| *c != b'\n' && i < n as usize) + { + st[diff] = *c; + diff += 1; } + stream.read = Some((rpos + diff, rend)); + } else { + return ptr::null_mut(); } + funlockfile(stream); s } @@ -378,15 +353,15 @@ pub extern "C" fn flockfile(file: &mut FILE) { /// Open the file in mode `mode` #[no_mangle] -pub unsafe extern "C" fn fopen(filename: *const c_char, mode: *const c_char) -> *mut FILE { +pub extern "C" fn fopen(filename: *const c_char, mode: *const c_char) -> *mut FILE { use core::ptr; - let initial_mode = *mode; + let initial_mode = unsafe { *mode }; if initial_mode != b'r' as i8 && initial_mode != b'w' as i8 && initial_mode != b'a' as i8 { - platform::errno = errno::EINVAL; + unsafe { platform::errno = errno::EINVAL }; return ptr::null_mut(); } - let flags = helpers::parse_mode_flags(mode); + let flags = unsafe { helpers::parse_mode_flags(mode) }; let fd = fcntl::sys_open(filename, flags, 0o666); if fd < 0 { @@ -397,12 +372,13 @@ 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); - if f.is_null() { + let f = unsafe { helpers::_fdopen(fd, mode) }; + if let Some(mut fi) = f { + &mut fi + } else { platform::close(fd); return ptr::null_mut(); } - f } /// Insert a character into the stream @@ -435,41 +411,49 @@ pub extern "C" fn fread(ptr: *mut c_void, size: usize, nitems: usize, stream: &m 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 }; - 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; + if !stream.can_read() { + return 0; } - while l > 0 { - let k = if !stream.can_read() { - 0 - } else { - stream.read(unsafe { slice::from_raw_parts_mut(dest, l as usize) }) - }; - - if k == 0 { - funlockfile(stream); - return (len - l as usize) / 2; + if let Some((rpos, rend)) = stream.read { + if rend > rpos { + // We have some buffered data that can be read + let diff = rend - rpos; + let k = if diff < l as usize { diff } else { l as usize }; + unsafe { + // Copy data + copy_nonoverlapping(&stream.buf[rpos..] as *const _ as *const u8, dest, k); + // Reposition pointers + dest = dest.add(k); + } + stream.read = Some((rpos + k, rend)); + l -= k as isize; } - l -= k as isize; - unsafe { - // Reposition - dest = dest.add(k); + while l > 0 { + let k = if !stream.can_read() { + 0 + } else { + stream.read(unsafe { slice::from_raw_parts_mut(dest, l as usize) }) + }; + + if k == 0 { + funlockfile(stream); + return (len - l as usize) / 2; + } + + l -= k as isize; + unsafe { + // Reposition + dest = dest.add(k); + } } + + funlockfile(stream); + nitems + } else { + unreachable!() } - - funlockfile(stream); - nitems } #[no_mangle] @@ -494,7 +478,7 @@ pub extern "C" fn freopen( return ptr::null_mut(); } } else { - let new = unsafe { fopen(filename, mode) }; + let new = fopen(filename, mode); if new.is_null() { funlockfile(stream); fclose(stream); @@ -533,23 +517,22 @@ pub extern "C" fn fseeko(stream: &mut FILE, offset: off_t, whence: c_int) -> c_i let mut off = offset; flockfile(stream); // Adjust for what is currently in the buffer + let rdiff = if let Some((rpos, rend)) = stream.read { + rend - rpos + } else { + 0 + }; if whence == SEEK_CUR { - off -= (stream.rend as usize - stream.rpos as usize) as i64; + off -= (rdiff) as i64; } - if stream.wpos > stream.wbase { + if let Some(_) = stream.write { stream.write(&[]); - if stream.wpos.is_null() { - return -1; - } } - stream.wpos = ptr::null_mut(); - stream.wend = ptr::null_mut(); - stream.wbase = ptr::null_mut(); + stream.write = None; if stream.seek(off, whence) < 0 { return -1; } - stream.rpos = ptr::null_mut(); - stream.rend = ptr::null_mut(); + stream.read = None; stream.flags &= !F_EOF; funlockfile(stream); 0 @@ -563,7 +546,7 @@ pub unsafe extern "C" fn fsetpos(stream: &mut FILE, pos: *const fpos_t) -> c_int /// Get the current position of the cursor in the file #[no_mangle] -pub unsafe extern "C" fn ftell(stream: &mut FILE) -> c_long { +pub extern "C" fn ftell(stream: &mut FILE) -> c_long { ftello(stream) as c_long } @@ -619,38 +602,46 @@ pub 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(&mut *__stdin()) +pub extern "C" fn getchar() -> c_int { + fgetc(unsafe { &mut *__stdin() }) } /// Get a char from a stream without locking the stream #[no_mangle] 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); + if !stream.can_read() { + return -1; + } + if let Some((rpos, rend)) = stream.read { + if rpos < rend { + let ret = stream.buf[rpos] as c_int; + stream.read = Some((rpos + 1, rend)); ret + } else { + let mut c = [0u8; 1]; + if stream.read(&mut c) == 1 { + c[0] as c_int + } else { + -1 + } } } else { - let mut c = [0u8; 1]; - if stream.can_read() && stream.read(&mut c) == 1 { - c[0] as c_int - } else { - -1 - } + // We made a prior call to can_read() and are checking it, therefore we + // should never be in a case where stream.read is None + // -- Tommoa (20/6/2018) + unreachable!() } } /// Get a char from `stdin` without locking `stdin` #[no_mangle] -pub unsafe extern "C" fn getchar_unlocked() -> c_int { +pub extern "C" fn getchar_unlocked() -> c_int { getc_unlocked(&mut *__stdin()) } /// Get a string from `stdin` #[no_mangle] -pub unsafe extern "C" fn gets(s: *mut c_char) -> *mut c_char { +pub extern "C" fn gets(s: *mut c_char) -> *mut c_char { use core::i32; fgets(s, i32::MAX, &mut *__stdin()) } @@ -674,7 +665,7 @@ pub 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!(); } @@ -684,14 +675,16 @@ pub unsafe extern "C" fn perror(s: *const c_char) { let mut w = platform::FileWriter(2); if errno >= 0 && errno < STR_ERROR.len() as c_int { - w.write_fmt(format_args!("{}: {}\n", s_str, STR_ERROR[errno as usize])); + w.write_fmt(format_args!("{}: {}\n", s_str, STR_ERROR[errno as usize])) + .unwrap(); } else { - w.write_fmt(format_args!("{}: Unknown error {}\n", s_str, errno)); + w.write_fmt(format_args!("{}: Unknown error {}\n", s_str, errno)) + .unwrap(); } } #[no_mangle] -pub extern "C" fn popen(command: *const c_char, mode: *const c_char) -> *mut FILE { +pub extern "C" fn popen(_command: *const c_char, _mode: *const c_char) -> *mut FILE { unimplemented!(); } @@ -706,46 +699,42 @@ pub 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, &mut *__stdout()) +pub extern "C" fn putchar(c: c_int) -> c_int { + fputc(c, unsafe { &mut *__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 { - if c as i8 != stream.buf_char && stream.wpos < stream.wend { - unsafe { - *stream.wpos = c as u8; - stream.wpos = stream.wpos.add(1); - c + if stream.can_write() { + if let Some((wbase, wpos, wend)) = stream.write { + if c as i8 != stream.buf_char { + stream.buf[wpos] = c as u8; + stream.write = Some((wbase, wpos + 1, wend)); + c + } else if stream.write(&[c as u8]) == 1 { + c + } else { + -1 + } + } else { + -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 if stream.write(&[c as u8]) != 1 { - -1 - } else { - c - } + -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, &mut *__stdout()) +pub extern "C" fn putchar_unlocked(c: c_int) -> c_int { + putc_unlocked(c, unsafe { &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, &mut *__stdout()) > 0) || (putchar_unlocked(b'\n' as c_int) > 0); +pub extern "C" fn puts(s: *const c_char) -> c_int { + let ret = (fputs(s, unsafe { &mut *__stdout() }) > 0) || (putchar_unlocked(b'\n' as c_int) > 0); if ret { 0 } else { @@ -788,45 +777,41 @@ pub extern "C" fn rewind(stream: &mut FILE) { /// 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) { - unsafe { - setvbuf( - stream, - buf, - if buf.is_null() { _IONBF } else { _IOFBF }, - BUFSIZ as usize, - ) - }; + setvbuf( + stream, + buf, + if buf.is_null() { _IONBF } else { _IOFBF }, + BUFSIZ as usize, + ); } /// 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, - 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; +pub extern "C" fn setvbuf(stream: &mut FILE, buf: *mut c_char, mode: c_int, size: usize) -> c_int { + // Set a buffer of size `size` if no buffer is given + let buf = if buf.is_null() { + if mode != _IONBF { + vec![0u8; 1] + } else { + Vec::new() + } + } else { + // We trust the user on this one + // -- Tommoa (20/6/2018) + unsafe { Vec::from_raw_parts(buf as *mut u8, size, size) } + }; + stream.buf_char = -1; + if mode == _IOLBF { + stream.buf_char = b'\n' as i8; } - (*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; + stream.flags |= F_SVB; + stream.buf = buf; 0 } #[no_mangle] -pub extern "C" fn tempnam(dir: *const c_char, pfx: *const c_char) -> *mut c_char { +pub extern "C" fn tempnam(_dir: *const c_char, _pfx: *const c_char) -> *mut c_char { unimplemented!(); } @@ -836,7 +821,7 @@ pub extern "C" fn tmpfile() -> *mut FILE { } #[no_mangle] -pub extern "C" fn tmpnam(s: *mut c_char) -> *mut c_char { +pub extern "C" fn tmpnam(_s: *mut c_char) -> *mut c_char { unimplemented!(); } @@ -847,22 +832,23 @@ pub extern "C" fn ungetc(c: c_int, stream: &mut FILE) -> c_int { c } else { flockfile(stream); - if stream.rpos.is_null() { + if stream.read.is_none() { stream.can_read(); } - if stream.rpos.is_null() || stream.rpos <= unsafe { stream.buf.sub(stream.unget) } { + if let Some((rpos, rend)) = stream.read { + if rpos == 0 { + funlockfile(stream); + return -1; + } + stream.read = Some((rpos - 1, rend)); + stream.buf[rpos - 1] = c as u8; + stream.flags &= !F_EOF; funlockfile(stream); - return -1; + c + } else { + funlockfile(stream); + -1 } - - unsafe { - stream.rpos = stream.rpos.sub(1); - *stream.rpos = c as u8; - } - stream.flags &= !F_EOF; - - funlockfile(stream); - c } } diff --git a/src/stdio/src/printf.rs b/src/stdio/src/printf.rs index 75b619b258..da12b07c75 100644 --- a/src/stdio/src/printf.rs +++ b/src/stdio/src/printf.rs @@ -1,4 +1,4 @@ -use core::{fmt, slice, str}; +use core::{slice, str}; use platform::{self, Write}; use platform::types::*; @@ -17,90 +17,91 @@ pub unsafe fn printf(mut w: W, format: *const c_char, mut ap: VaList) if found_percent { match b as char { '%' => { - w.write_char('%'); found_percent = false; + w.write_char('%') } 'c' => { let a = ap.get::(); - w.write_u8(a as u8); - found_percent = false; + + w.write_u8(a as u8) } 'd' | 'i' => { let a = ap.get::(); - w.write_fmt(format_args!("{}", a)); - found_percent = false; + + w.write_fmt(format_args!("{}", a)) } 'f' | 'F' => { let a = ap.get::(); - w.write_fmt(format_args!("{}", a)); - found_percent = false; + + w.write_fmt(format_args!("{}", a)) } 'n' => { let _a = ap.get::(); found_percent = false; + Ok(()) } 'p' => { let a = ap.get::(); - w.write_fmt(format_args!("0x{:x}", a)); - found_percent = false; + + w.write_fmt(format_args!("0x{:x}", a)) } 's' => { let a = ap.get::(); + found_percent = false; + w.write_str(str::from_utf8_unchecked(platform::c_str( a as *const c_char, - ))); - - found_percent = false; + ))) } 'u' => { let a = ap.get::(); - w.write_fmt(format_args!("{}", a)); - found_percent = false; + + w.write_fmt(format_args!("{}", a)) } 'x' => { let a = ap.get::(); - w.write_fmt(format_args!("{:x}", a)); - found_percent = false; + + w.write_fmt(format_args!("{:x}", a)) } 'X' => { let a = ap.get::(); - w.write_fmt(format_args!("{:X}", a)); - found_percent = false; + + w.write_fmt(format_args!("{:X}", a)) } 'o' => { let a = ap.get::(); - w.write_fmt(format_args!("{:o}", a)); - found_percent = false; + + w.write_fmt(format_args!("{:o}", a)) } - '-' => {} - '+' => {} - ' ' => {} - '#' => {} - '0'...'9' => {} - _ => {} - } + '-' => Ok(()), + '+' => Ok(()), + ' ' => Ok(()), + '#' => Ok(()), + '0'...'9' => Ok(()), + _ => Ok(()), + }.expect("Error writing!") } else if b == b'%' { found_percent = true; } else { - w.write_u8(b); + w.write_u8(b).expect("Error writing char!"); } } From 4c65f14f9a025bc37660cc33fa4d095edc10032d Mon Sep 17 00:00:00 2001 From: Tom Almeida Date: Thu, 21 Jun 2018 09:33:21 +0800 Subject: [PATCH 10/25] Fixed some issues with temporary files and moved some raw pointers to Option<&T>s --- src/stdio/src/helpers.rs | 28 ++++++++++++++++----------- src/stdio/src/lib.rs | 41 +++++++++++++++++++++------------------- 2 files changed, 39 insertions(+), 30 deletions(-) diff --git a/src/stdio/src/helpers.rs b/src/stdio/src/helpers.rs index d670509201..242e83f1ec 100644 --- a/src/stdio/src/helpers.rs +++ b/src/stdio/src/helpers.rs @@ -36,8 +36,10 @@ pub unsafe fn parse_mode_flags(mode_str: *const c_char) -> i32 { } /// Open a file with the file descriptor `fd` in the mode `mode` -pub unsafe fn _fdopen(fd: c_int, mode: *const c_char) -> Option { +pub unsafe fn _fdopen(fd: c_int, mode: *const c_char) -> Option<*mut FILE> { use string::strchr; + use stdlib::malloc; + use core::mem::size_of; if *mode != b'r' as i8 && *mode != b'w' as i8 && *mode != b'a' as i8 { platform::errno = errno::EINVAL; return None; @@ -61,16 +63,20 @@ pub unsafe fn _fdopen(fd: c_int, mode: *const c_char) -> Option { } // Allocate the file - Some(FILE { - flags: flags, - read: None, - write: None, - fd: fd, - buf: vec![0u8; BUFSIZ + UNGET], - buf_char: -1, - unget: UNGET, - lock: AtomicBool::new(false), - }) + let f = malloc(size_of::()) as *mut FILE; + if f.is_null() { + None + } else { + (*f).flags = flags; + (*f).read = None; + (*f).write = None; + (*f).fd = fd; + (*f).buf = vec![0u8; BUFSIZ + UNGET]; + (*f).buf_char = -1; + (*f).unget = UNGET; + (*f).lock = AtomicBool::new(false); + Some(f) + } } /// Write buffer `buf` of length `l` into `stream` diff --git a/src/stdio/src/lib.rs b/src/stdio/src/lib.rs index 5343da4b50..1c67857bd1 100644 --- a/src/stdio/src/lib.rs +++ b/src/stdio/src/lib.rs @@ -44,14 +44,14 @@ mod internal; /// This struct gets exposed to the C API. /// pub struct FILE { - flags: i32, - read: Option<(usize, usize)>, - write: Option<(usize, usize, usize)>, - fd: c_int, - buf: Vec, + flags: i32, + read: Option<(usize, usize)>, + write: Option<(usize, usize, usize)>, + fd: c_int, + buf: Vec, buf_char: i8, - lock: AtomicBool, - unget: usize, + lock: AtomicBool, + unget: usize, } impl FILE { @@ -221,6 +221,8 @@ pub extern "C" fn fclose(stream: &mut FILE) -> c_int { unsafe { free(stream as *mut _ as *mut _); } + } else { + funlockfile(stream); } r } @@ -229,8 +231,8 @@ pub extern "C" fn fclose(stream: &mut FILE) -> c_int { #[no_mangle] pub extern "C" fn fdopen(fildes: c_int, mode: *const c_char) -> *mut FILE { use core::ptr; - if let Some(mut f) = unsafe { helpers::_fdopen(fildes, mode) } { - &mut f + if let Some(f) = unsafe { helpers::_fdopen(fildes, mode) } { + f } else { ptr::null_mut() } @@ -278,15 +280,17 @@ pub extern "C" fn fgetc(stream: &mut FILE) -> c_int { /// Get the position of the stream and store it in pos #[no_mangle] -pub extern "C" fn fgetpos(stream: &mut FILE, pos: *mut fpos_t) -> c_int { +pub extern "C" fn fgetpos(stream: &mut FILE, pos: Option<&mut fpos_t>) -> c_int { let off = internal::ftello(stream); if off < 0 { return -1; } - unsafe { - (*pos) = off; + if let Some(pos) = pos { + *pos = off; + 0 + } else { + -1 } - 0 } /// Get a string from the stream @@ -372,12 +376,11 @@ pub extern "C" fn fopen(filename: *const c_char, mode: *const c_char) -> *mut FI fcntl::sys_fcntl(fd, fcntl::F_SETFD, fcntl::FD_CLOEXEC); } - let f = unsafe { helpers::_fdopen(fd, mode) }; - if let Some(mut fi) = f { - &mut fi + if let Some(f) = unsafe { helpers::_fdopen(fd, mode) } { + f } else { platform::close(fd); - return ptr::null_mut(); + ptr::null_mut() } } @@ -540,8 +543,8 @@ pub extern "C" fn fseeko(stream: &mut FILE, offset: off_t, whence: c_int) -> c_i /// 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 { - fseek(stream, *pos as off_t, SEEK_SET) +pub unsafe extern "C" fn fsetpos(stream: &mut FILE, pos: Option<&fpos_t>) -> c_int { + fseek(stream, *pos.expect("You must specify a valid position"), SEEK_SET) } /// Get the current position of the cursor in the file From 57f7de1e6dd23ee2662330e7c44444dc35a95ec3 Mon Sep 17 00:00:00 2001 From: Tom Almeida Date: Thu, 21 Jun 2018 00:16:20 +0800 Subject: [PATCH 11/25] Changed FILE to use a vector as a buffer instead of raw pointers. This allows us to remove the large majority of unsafe blocks from the code --- src/stdio/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/stdio/src/lib.rs b/src/stdio/src/lib.rs index 1c67857bd1..9cdf30a700 100644 --- a/src/stdio/src/lib.rs +++ b/src/stdio/src/lib.rs @@ -1,6 +1,7 @@ //! stdio implementation for Redox, following http://pubs.opengroup.org/onlinepubs/7908799/xsh/stdio.h.html #![no_std] +// For Vec #![feature(alloc)] #![feature(const_fn)] From 8075447fad46671743bff8c66c09e6083a50c62e Mon Sep 17 00:00:00 2001 From: Tom Almeida Date: Thu, 21 Jun 2018 00:16:20 +0800 Subject: [PATCH 12/25] Changed FILE to use a vector as a buffer instead of raw pointers. This allows us to remove the large majority of unsafe blocks from the code --- src/stdio/src/default.rs | 50 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/src/stdio/src/default.rs b/src/stdio/src/default.rs index 4adbe01543..45adea80f1 100644 --- a/src/stdio/src/default.rs +++ b/src/stdio/src/default.rs @@ -39,6 +39,18 @@ lazy_static! { }; } +struct GlobalFile(UnsafeCell); +impl GlobalFile { + const fn new(file: FILE) -> Self { + GlobalFile(UnsafeCell::new(file)) + } + fn get(&self) -> *mut FILE { + self.0.get() + } +} +// statics need to be Sync +unsafe impl Sync for GlobalFile {} + #[no_mangle] pub extern "C" fn __stdin() -> *mut FILE { unsafe { &mut default_stdin } @@ -53,3 +65,41 @@ pub extern "C" fn __stdout() -> *mut FILE { pub extern "C" fn __stderr() -> *mut FILE { unsafe { &mut default_stderr } } + +lazy_static! { + #[allow(non_upper_case_globals)] + static ref default_stdin: GlobalFile = GlobalFile::new(FILE { + flags: constants::F_PERM | constants::F_NOWR, + read: None, + write: None, + fd: 0, + buf: vec![0u8;(BUFSIZ + UNGET) as usize], + buf_char: -1, + unget: UNGET, + lock: AtomicBool::new(false), + }); + + #[allow(non_upper_case_globals)] + static ref default_stdout: GlobalFile = GlobalFile::new(FILE { + flags: constants::F_PERM | constants::F_NORD, + read: None, + write: None, + fd: 1, + buf: vec![0u8;(BUFSIZ + UNGET) as usize], + buf_char: b'\n' as i8, + unget: 0, + lock: AtomicBool::new(false), + }); + + #[allow(non_upper_case_globals)] + static ref default_stderr: GlobalFile = GlobalFile::new(FILE { + flags: constants::F_PERM | constants::F_NORD, + read: None, + write: None, + fd: 2, + buf: vec![0u8;(BUFSIZ + UNGET) as usize], + buf_char: -1, + unget: 0, + lock: AtomicBool::new(false), + }); +} From 18418254b925a498643b988bef323b5efc200f23 Mon Sep 17 00:00:00 2001 From: Tom Almeida Date: Sat, 23 Jun 2018 13:08:28 +0800 Subject: [PATCH 13/25] Made sure lazy_static works with no_std --- src/stdio/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stdio/Cargo.toml b/src/stdio/Cargo.toml index dd1e6126da..1f2f2794e9 100644 --- a/src/stdio/Cargo.toml +++ b/src/stdio/Cargo.toml @@ -10,7 +10,7 @@ cbindgen = { path = "../../cbindgen" } [dependencies] errno = { path = "../errno"} fcntl = { path = "../fcntl" } -lazy_static = "*" +lazy_static = { version = "*", features = ["nightly", "spin_no_std"] } platform = { path = "../platform" } ralloc = { path = "../../ralloc" } string = { path = "../string" } From 05b4b7642688b300b6ec69625597ef4837097823 Mon Sep 17 00:00:00 2001 From: Tom Almeida Date: Sat, 30 Jun 2018 17:49:34 +0800 Subject: [PATCH 14/25] Fix some issues --- src/stdio/src/default.rs | 45 ++++------------------------------------ 1 file changed, 4 insertions(+), 41 deletions(-) diff --git a/src/stdio/src/default.rs b/src/stdio/src/default.rs index 45adea80f1..1ff697d8e1 100644 --- a/src/stdio/src/default.rs +++ b/src/stdio/src/default.rs @@ -1,44 +1,7 @@ use core::sync::atomic::AtomicBool; +use core::cell::UnsafeCell; use super::{constants, BUFSIZ, FILE, UNGET}; -lazy_static! { - #[allow(non_upper_case_globals)] - static ref default_stdin: FILE = FILE { - flags: constants::F_PERM | constants::F_NOWR | constants::F_BADJ, - read: None, - write: None, - fd: 0, - buf: vec![0u8;(BUFSIZ + UNGET) as usize], - buf_char: -1, - unget: UNGET, - lock: AtomicBool::new(false), - }; - - #[allow(non_upper_case_globals)] - static ref default_stdout: FILE = FILE { - flags: constants::F_PERM | constants::F_NORD | constants::F_BADJ, - read: None, - write: None, - fd: 1, - buf: vec![0u8;(BUFSIZ + UNGET) as usize], - buf_char: b'\n' as i8, - unget: 0, - lock: AtomicBool::new(false), - }; - - #[allow(non_upper_case_globals)] - static ref default_stderr: FILE = FILE { - flags: constants::F_PERM | constants::F_NORD | constants::F_BADJ, - read: None, - write: None, - fd: 2, - buf: vec![0u8;(BUFSIZ + UNGET) as usize], - buf_char: -1, - unget: 0, - lock: AtomicBool::new(false), - }; -} - struct GlobalFile(UnsafeCell); impl GlobalFile { const fn new(file: FILE) -> Self { @@ -53,17 +16,17 @@ unsafe impl Sync for GlobalFile {} #[no_mangle] pub extern "C" fn __stdin() -> *mut FILE { - unsafe { &mut default_stdin } + unsafe { default_stdin.get() } } #[no_mangle] pub extern "C" fn __stdout() -> *mut FILE { - unsafe { &mut default_stdout } + unsafe { default_stdout.get() } } #[no_mangle] pub extern "C" fn __stderr() -> *mut FILE { - unsafe { &mut default_stderr } + unsafe { default_stderr.get() } } lazy_static! { From 0d61f9f4fdc9795596125536175f194c9deabdab Mon Sep 17 00:00:00 2001 From: Tom Almeida Date: Tue, 3 Jul 2018 09:13:48 +0800 Subject: [PATCH 15/25] Make sure we can actually write before writing anything when using printf --- src/stdio/src/lib.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/stdio/src/lib.rs b/src/stdio/src/lib.rs index 317d5c9a98..b0af3f6243 100644 --- a/src/stdio/src/lib.rs +++ b/src/stdio/src/lib.rs @@ -165,6 +165,9 @@ impl FILE { } impl fmt::Write for FILE { fn write_str(&mut self, s: &str) -> Result { + if !self.can_write() { + return Err(Error); + } let s = s.as_bytes(); if self.write(s) != s.len() { Err(Error) @@ -175,6 +178,9 @@ impl fmt::Write for FILE { } impl Write for FILE { fn write_u8(&mut self, byte: u8) -> Result { + if !self.can_write() { + return Err(Error); + } if self.write(&[byte]) != 1 { Err(Error) } else { From e9cecfead3b4cf5787d80c009a31dd4833cf33dd Mon Sep 17 00:00:00 2001 From: Tom Almeida Date: Tue, 3 Jul 2018 09:48:21 +0800 Subject: [PATCH 16/25] Return -1 for error in printf --- src/stdio/src/printf.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/stdio/src/printf.rs b/src/stdio/src/printf.rs index da12b07c75..813213f4dc 100644 --- a/src/stdio/src/printf.rs +++ b/src/stdio/src/printf.rs @@ -97,11 +97,11 @@ pub unsafe fn printf(mut w: W, format: *const c_char, mut ap: VaList) '#' => Ok(()), '0'...'9' => Ok(()), _ => Ok(()), - }.expect("Error writing!") + }.map_err(|_| return -1).unwrap() } else if b == b'%' { found_percent = true; } else { - w.write_u8(b).expect("Error writing char!"); + w.write_u8(b).map_err(|_| return -1).unwrap() } } From 81107f8cd1bb69bbd7934583e5e8f19d46c53091 Mon Sep 17 00:00:00 2001 From: Tom Almeida Date: Tue, 3 Jul 2018 10:01:48 +0800 Subject: [PATCH 17/25] Don't reset read/write every time we check if we can read or write --- src/stdio/src/lib.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/stdio/src/lib.rs b/src/stdio/src/lib.rs index b0af3f6243..cb53560895 100644 --- a/src/stdio/src/lib.rs +++ b/src/stdio/src/lib.rs @@ -64,6 +64,9 @@ impl FILE { } */ + if let Some(_) = self.read { + return true; + } if let Some(_) = self.write { self.write(&[]); } @@ -93,6 +96,9 @@ impl FILE { return false; } // Buffer repositioning + if let Some(_) = self.write { + return true; + } self.read = None; self.write = Some((self.unget, self.unget, self.buf.len() - 1)); return true; From 7277286efd4d28c9444b03edaecf130b3fd63da8 Mon Sep 17 00:00:00 2001 From: Tom Almeida Date: Tue, 3 Jul 2018 10:05:12 +0800 Subject: [PATCH 18/25] Implement Drop for FILE, so we flush when the process exits --- src/stdio/src/lib.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/stdio/src/lib.rs b/src/stdio/src/lib.rs index cb53560895..1d720ed7e1 100644 --- a/src/stdio/src/lib.rs +++ b/src/stdio/src/lib.rs @@ -203,6 +203,15 @@ impl Read for FILE { } } +impl Drop for FILE { + fn drop(&mut self) { + // Flush + if let Some(_) = self.write { + self.write(&[]); + } + } +} + /// Clears EOF and ERR indicators on a stream #[no_mangle] pub extern "C" fn clearerr(stream: &mut FILE) { From 72177be0fad9ca27b31dd4ef00f5cd4cc489660f Mon Sep 17 00:00:00 2001 From: Tom Almeida Date: Tue, 3 Jul 2018 12:14:30 +0800 Subject: [PATCH 19/25] Add a working implementation of gets --- src/stdio/src/lib.rs | 47 ++++++++++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 15 deletions(-) diff --git a/src/stdio/src/lib.rs b/src/stdio/src/lib.rs index 1d720ed7e1..4c00df7c2c 100644 --- a/src/stdio/src/lib.rs +++ b/src/stdio/src/lib.rs @@ -142,7 +142,11 @@ impl FILE { unreachable!() } pub fn read(&mut self, buf: &mut [u8]) -> usize { - let adj = !(self.buf.len() == 0) as usize; + let adj = if self.buf.len() > 0 { + 0 + } else { + 1 + }; let mut file_buf = &mut self.buf[self.unget..]; let count = if buf.len() <= 1 + adj { platform::read(self.fd, &mut file_buf) @@ -320,15 +324,14 @@ pub extern "C" fn fgetpos(stream: &mut FILE, pos: Option<&mut fpos_t>) -> c_int /// Get a string from the stream #[no_mangle] pub extern "C" fn fgets(s: *mut c_char, n: c_int, stream: &mut FILE) -> *mut c_char { - use platform::c_str_n_mut; - + use core::slice; flockfile(stream); - let st = unsafe { c_str_n_mut(s, n as usize) }; + let st = unsafe { slice::from_raw_parts_mut(s, n as usize) }; // We can only fit one or less chars in if n <= 1 { funlockfile(stream); - if n == 0 { + if n <= 0 { return ptr::null_mut(); } unsafe { @@ -344,21 +347,35 @@ pub extern "C" fn fgets(s: *mut c_char, n: c_int, stream: &mut FILE) -> *mut c_c } } + let mut diff = 0; if let Some((rpos, rend)) = stream.read { - let mut diff = 0; - for (_, mut c) in stream.buf[rpos..rend] - .iter() - .enumerate() - .take_while(|&(i, c)| *c != b'\n' && i < n as usize) - { - st[diff] = *c; + for _ in (0..(n-1) as usize).take_while(|x| rpos + x < rend) { + st[diff] = stream.buf[rpos + diff] as i8; diff += 1; } - stream.read = Some((rpos + diff, rend)); - } else { - return ptr::null_mut(); + stream.read = Some((rpos+diff, rend)); + for i in diff..(n-1) as usize { + let mut c = [0u8]; + let d = stream.read(&mut c); + if d != 1 { + if diff == 0 { + return ptr::null_mut(); + } else { + break; + } + } + if c[0] as i8 == -1 { + break; + } + st[i] = c[0] as i8; + diff += 1; + if c[0] == b'\n' || c[0] as i8 == stream.buf_char { + break; + } + } } + st[diff] = 0; funlockfile(stream); s } From bf6db9199307bc87bb1de78bb4cbc8768799973e Mon Sep 17 00:00:00 2001 From: Tom Almeida Date: Tue, 3 Jul 2018 12:24:51 +0800 Subject: [PATCH 20/25] Actually remove stdlib from stdio. This should have been done with wchar being put in, but I messed something up --- src/stdio/Cargo.toml | 1 - src/stdio/src/helpers.rs | 1 - 2 files changed, 2 deletions(-) diff --git a/src/stdio/Cargo.toml b/src/stdio/Cargo.toml index 27fbe42bc7..8261bc765a 100644 --- a/src/stdio/Cargo.toml +++ b/src/stdio/Cargo.toml @@ -14,5 +14,4 @@ lazy_static = { version = "*", features = ["nightly", "spin_no_std"] } platform = { path = "../platform" } ralloc = { path = "../../ralloc", default-features = false } string = { path = "../string" } -stdlib = { path = "../stdlib" } va_list = { path = "../../va_list", features = ["no_std"] } diff --git a/src/stdio/src/helpers.rs b/src/stdio/src/helpers.rs index 3e898a24b5..261a968090 100644 --- a/src/stdio/src/helpers.rs +++ b/src/stdio/src/helpers.rs @@ -40,7 +40,6 @@ pub unsafe fn parse_mode_flags(mode_str: *const c_char) -> i32 { /// Open a file with the file descriptor `fd` in the mode `mode` pub unsafe fn _fdopen(fd: c_int, mode: *const c_char) -> Option<*mut FILE> { use string::strchr; - use stdlib::malloc; use core::mem::size_of; if *mode != b'r' as i8 && *mode != b'w' as i8 && *mode != b'a' as i8 { platform::errno = errno::EINVAL; From d7a0f3d526e7f213c3de54816feefb1a60b5a474 Mon Sep 17 00:00:00 2001 From: Tom Almeida Date: Tue, 3 Jul 2018 12:36:41 +0800 Subject: [PATCH 21/25] Ensure gets stops on newline or bufchar --- src/stdio/src/lib.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/stdio/src/lib.rs b/src/stdio/src/lib.rs index 4c00df7c2c..d0b38f9405 100644 --- a/src/stdio/src/lib.rs +++ b/src/stdio/src/lib.rs @@ -351,6 +351,9 @@ pub extern "C" fn fgets(s: *mut c_char, n: c_int, stream: &mut FILE) -> *mut c_c if let Some((rpos, rend)) = stream.read { for _ in (0..(n-1) as usize).take_while(|x| rpos + x < rend) { st[diff] = stream.buf[rpos + diff] as i8; + if st[diff] == b'\n' as i8 || st[diff] == stream.buf_char { + break; + } diff += 1; } stream.read = Some((rpos+diff, rend)); From bf2973e857b3836b0c3e3c8e81f37f998dff95a5 Mon Sep 17 00:00:00 2001 From: Tom Almeida Date: Tue, 3 Jul 2018 12:50:04 +0800 Subject: [PATCH 22/25] Ensure we correctly insert null character in gets --- src/stdio/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/stdio/src/lib.rs b/src/stdio/src/lib.rs index d0b38f9405..0ce1eb1c96 100644 --- a/src/stdio/src/lib.rs +++ b/src/stdio/src/lib.rs @@ -351,10 +351,10 @@ pub extern "C" fn fgets(s: *mut c_char, n: c_int, stream: &mut FILE) -> *mut c_c if let Some((rpos, rend)) = stream.read { for _ in (0..(n-1) as usize).take_while(|x| rpos + x < rend) { st[diff] = stream.buf[rpos + diff] as i8; - if st[diff] == b'\n' as i8 || st[diff] == stream.buf_char { + diff += 1; + if st[diff-1] == b'\n' as i8 || st[diff-1] == stream.buf_char { break; } - diff += 1; } stream.read = Some((rpos+diff, rend)); for i in diff..(n-1) as usize { From ebe1ed15f815ec948997dd117ea87cdce3478fde Mon Sep 17 00:00:00 2001 From: Tom Almeida Date: Tue, 3 Jul 2018 14:34:40 +0800 Subject: [PATCH 23/25] Fix fgets --- src/stdio/src/lib.rs | 52 +++++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 27 deletions(-) diff --git a/src/stdio/src/lib.rs b/src/stdio/src/lib.rs index 0ce1eb1c96..759d7142f7 100644 --- a/src/stdio/src/lib.rs +++ b/src/stdio/src/lib.rs @@ -165,8 +165,12 @@ impl FILE { return count as usize; } // Adjust pointers - self.read = Some((self.unget + 1, self.unget + (count as usize))); - buf[buf.len() - 1] = file_buf[0]; + if buf.len() > 0 { + self.read = Some((self.unget + 1, self.unget + (count as usize))); + buf[buf.len() - 1] = file_buf[0]; + } else { + self.read = Some((self.unget, self.unget + (count as usize))); + } buf.len() } pub fn seek(&self, off: off_t, whence: c_int) -> off_t { @@ -328,6 +332,8 @@ pub extern "C" fn fgets(s: *mut c_char, n: c_int, stream: &mut FILE) -> *mut c_c flockfile(stream); let st = unsafe { slice::from_raw_parts_mut(s, n as usize) }; + let mut len = n; + // We can only fit one or less chars in if n <= 1 { funlockfile(stream); @@ -347,38 +353,30 @@ pub extern "C" fn fgets(s: *mut c_char, n: c_int, stream: &mut FILE) -> *mut c_c } } - let mut diff = 0; - if let Some((rpos, rend)) = stream.read { - for _ in (0..(n-1) as usize).take_while(|x| rpos + x < rend) { - st[diff] = stream.buf[rpos + diff] as i8; - diff += 1; - if st[diff-1] == b'\n' as i8 || st[diff-1] == stream.buf_char { - break; - } - } - stream.read = Some((rpos+diff, rend)); - for i in diff..(n-1) as usize { - let mut c = [0u8]; - let d = stream.read(&mut c); - if d != 1 { - if diff == 0 { - return ptr::null_mut(); - } else { - break; + // TODO: Look at this later to determine correctness and efficiency + 'outer: while stream.read(&mut []) == 0 && stream.flags & F_ERR == 0 { + if let Some((rpos, rend)) = stream.read { + let mut idiff = 0usize; + for _ in (0..(len-1) as usize).take_while(|x| rpos + x < rend) { + let pos = (n - len) as usize; + st[pos] = stream.buf[rpos + idiff] as i8; + idiff += 1; + len -= 1; + if st[pos] == b'\n' as i8 || st[pos] == stream.buf_char { + break 'outer; } } - if c[0] as i8 == -1 { - break; - } - st[i] = c[0] as i8; - diff += 1; - if c[0] == b'\n' || c[0] as i8 == stream.buf_char { + stream.read = Some((rpos+idiff, rend)); + if len <= 1 { break; } } + // We can read, there's been no errors. We should have stream.read setbuf + // -- Tommoa (3/7/2018) + unreachable!() } - st[diff] = 0; + st[(n - len) as usize] = 0; funlockfile(stream); s } From 8bc07abd05c6619037b15ccfb8d26a061d7e22ee Mon Sep 17 00:00:00 2001 From: Tom Almeida Date: Tue, 3 Jul 2018 14:42:31 +0800 Subject: [PATCH 24/25] Fix freopen.stdout. There was a trailing space --- tests/expected/stdio/freopen.stdout | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/expected/stdio/freopen.stdout b/tests/expected/stdio/freopen.stdout index a8f9fd84ee..e965047ad7 100644 --- a/tests/expected/stdio/freopen.stdout +++ b/tests/expected/stdio/freopen.stdout @@ -1 +1 @@ -Hello +Hello From 53a03cb0ba01590211f99aec84ae58977414e532 Mon Sep 17 00:00:00 2001 From: Tom Almeida Date: Tue, 3 Jul 2018 14:50:38 +0800 Subject: [PATCH 25/25] Made sure errors were properly handled by printf --- src/stdio/src/printf.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/stdio/src/printf.rs b/src/stdio/src/printf.rs index 813213f4dc..aa9af15fca 100644 --- a/src/stdio/src/printf.rs +++ b/src/stdio/src/printf.rs @@ -15,7 +15,7 @@ pub unsafe fn printf(mut w: W, format: *const c_char, mut ap: VaList) } if found_percent { - match b as char { + if match b as char { '%' => { found_percent = false; w.write_char('%') @@ -97,11 +97,15 @@ pub unsafe fn printf(mut w: W, format: *const c_char, mut ap: VaList) '#' => Ok(()), '0'...'9' => Ok(()), _ => Ok(()), - }.map_err(|_| return -1).unwrap() + }.is_err() { + return -1; + } } else if b == b'%' { found_percent = true; } else { - w.write_u8(b).map_err(|_| return -1).unwrap() + if w.write_u8(b).is_err() { + return -1; + } } }