From 90c6937f173e8eedb1824e262b1edb9fe76a3346 Mon Sep 17 00:00:00 2001 From: Tom Almeida <tommoa256@gmail.com> Date: Thu, 21 Jun 2018 00:16:20 +0800 Subject: [PATCH] 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 | 107 ++++----- src/stdio/src/helpers.rs | 102 ++++---- src/stdio/src/internal.rs | 22 +- src/stdio/src/lib.rs | 493 +++++++++++++++++++------------------- src/stdio/src/printf.rs | 59 ++--- 7 files changed, 408 insertions(+), 403 deletions(-) diff --git a/src/platform/src/lib.rs b/src/platform/src/lib.rs index 3784ea30d..433c6977c 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 aa82586fe..dd1e6126d 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 840a6fa07..090a8b4d4 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}; - -#[allow(non_upper_case_globals)] -static mut default_stdin_buf: [u8; BUFSIZ as usize + UNGET] = [0; BUFSIZ as usize + UNGET]; - -#[allow(non_upper_case_globals)] -static mut default_stdin: FILE = FILE { - flags: constants::F_PERM | constants::F_NOWR | constants::F_BADJ, - rpos: ptr::null_mut(), - rend: ptr::null_mut(), - wend: ptr::null_mut(), - wpos: ptr::null_mut(), - wbase: ptr::null_mut(), - fd: 0, - buf: unsafe { &mut default_stdin_buf as *mut [u8] as *mut u8 }, - buf_size: BUFSIZ as usize, - buf_char: -1, - unget: UNGET, - lock: AtomicBool::new(false), -}; - -#[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), -}; +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), + }; +} // 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 378d2af3b..d67050920 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<FILE> { use string::strchr; if *mode != b'r' as i8 && *mode != b'w' as i8 && *mode != b'a' as i8 { platform::errno = errno::EINVAL; - return ptr::null_mut(); + 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::<FILE>() + 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::<FILE>() + 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); - } - - let i = if stream.buf_char >= 0 { - let mut i = l; - while i > 0 && buf[i - 1] != b'\n' { - i -= 1; + 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); } - 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; + } + if i > 0 { + let n = stream.write(buf); + if n < i { + return n; + } + advance += i; + l -= i; } - 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 35eb5f47e..60ad4f3eb 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 cec2032da..014555f7c 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<u8>, 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 count < 0 { - self.wpos = ptr::null_mut(); - self.wbase = ptr::null_mut(); - self.wend = ptr::null_mut(); - self.flags |= constants::F_ERR; - return 0; + 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; } - 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 + funlockfile(stream); + nitems + } else { + unreachable!() + } } #[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.can_read() && stream.read(&mut c) == 1 { - c[0] as c_int } else { - -1 + let mut c = [0u8; 1]; + if stream.read(&mut c) == 1 { + c[0] as c_int + } else { + -1 + } } + } else { + // 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 - } - } 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); + 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 if stream.write(&[c as u8]) != 1 { - -1 } else { - c + -1 } + } else { + -1 } } /// Put a character `c` into `stdout` without locking `stdout` #[no_mangle] -pub unsafe extern "C" fn putchar_unlocked(c: c_int) -> c_int { - putc_unlocked(c, &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; - } - (*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; +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.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; - } - - unsafe { - stream.rpos = stream.rpos.sub(1); - *stream.rpos = c as u8; + c + } else { + funlockfile(stream); + -1 } - stream.flags &= !F_EOF; - - funlockfile(stream); - c } } diff --git a/src/stdio/src/printf.rs b/src/stdio/src/printf.rs index 75b619b25..da12b07c7 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<W: Write>(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::<u32>(); - w.write_u8(a as u8); - found_percent = false; + + w.write_u8(a as u8) } 'd' | 'i' => { let a = ap.get::<c_int>(); - w.write_fmt(format_args!("{}", a)); - found_percent = false; + + w.write_fmt(format_args!("{}", a)) } 'f' | 'F' => { let a = ap.get::<f64>(); - w.write_fmt(format_args!("{}", a)); - found_percent = false; + + w.write_fmt(format_args!("{}", a)) } 'n' => { let _a = ap.get::<c_int>(); found_percent = false; + Ok(()) } 'p' => { let a = ap.get::<usize>(); - w.write_fmt(format_args!("0x{:x}", a)); - found_percent = false; + + w.write_fmt(format_args!("0x{:x}", a)) } 's' => { let a = ap.get::<usize>(); + 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::<c_uint>(); - w.write_fmt(format_args!("{}", a)); - found_percent = false; + + w.write_fmt(format_args!("{}", a)) } 'x' => { let a = ap.get::<c_uint>(); - w.write_fmt(format_args!("{:x}", a)); - found_percent = false; + + w.write_fmt(format_args!("{:x}", a)) } 'X' => { let a = ap.get::<c_uint>(); - w.write_fmt(format_args!("{:X}", a)); - found_percent = false; + + w.write_fmt(format_args!("{:X}", a)) } 'o' => { let a = ap.get::<c_uint>(); - 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!"); } } -- GitLab