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