From c24d1e2b361eae5227d2294d98e45d0658374785 Mon Sep 17 00:00:00 2001
From: Tom Almeida <tommoa256@gmail.com>
Date: Sun, 18 Mar 2018 00:20:21 +0800
Subject: [PATCH] Removed all function pointers in FILE, moved internal
 functions to be member functions of FILE, made relevant *mut FILEs into &mut
 FILE and made suitable functions safe again

---
 src/stdio/src/default.rs  |   9 -
 src/stdio/src/helpers.rs  | 104 ++++-----
 src/stdio/src/internal.rs | 156 +------------
 src/stdio/src/lib.rs      | 465 ++++++++++++++++++++++++--------------
 4 files changed, 358 insertions(+), 376 deletions(-)

diff --git a/src/stdio/src/default.rs b/src/stdio/src/default.rs
index efeb05c9..840a6fa0 100644
--- a/src/stdio/src/default.rs
+++ b/src/stdio/src/default.rs
@@ -19,9 +19,6 @@ static mut default_stdin: FILE = FILE {
     buf_char: -1,
     unget: UNGET,
     lock: AtomicBool::new(false),
-    write: None,
-    read: Some(internal::stdio_read),
-    seek: None,
 };
 
 #[allow(non_upper_case_globals)]
@@ -41,9 +38,6 @@ static mut default_stdout: FILE = FILE {
     buf_char: b'\n' as i8,
     unget: 0,
     lock: AtomicBool::new(false),
-    write: Some(internal::stdio_write),
-    read: None,
-    seek: None,
 };
 
 #[allow(non_upper_case_globals)]
@@ -63,9 +57,6 @@ static mut default_stderr: FILE = FILE {
     buf_char: -1,
     unget: 0,
     lock: AtomicBool::new(false),
-    write: Some(internal::stdio_write),
-    read: None,
-    seek: None,
 };
 
 // Don't ask me how the casting below works, I have no idea
diff --git a/src/stdio/src/helpers.rs b/src/stdio/src/helpers.rs
index 53424902..378d2af3 100644
--- a/src/stdio/src/helpers.rs
+++ b/src/stdio/src/helpers.rs
@@ -1,5 +1,4 @@
 use super::{internal, BUFSIZ, FILE, UNGET};
-use compiler_builtins::mem::memset;
 use stdlib::calloc;
 use core::{mem, ptr};
 use core::sync::atomic::AtomicBool;
@@ -78,82 +77,71 @@ pub unsafe fn _fdopen(fd: c_int, mode: *const c_char) -> *mut FILE {
         buf_char: -1,
         unget: UNGET,
         lock: AtomicBool::new(false),
-        write: Some(internal::stdio_write),
-        read: Some(internal::stdio_read),
-        seek: Some(internal::stdio_seek),
     };
     file
 }
 
 /// Write buffer `buf` of length `l` into `stream`
-pub unsafe fn fwritex(buf: *const u8, l: size_t, stream: *mut FILE) -> size_t {
-    use compiler_builtins::mem::memcpy;
-    let mut buf = buf;
+pub fn fwritex(buf: *const u8, l: size_t, stream: &mut FILE) -> size_t {
+    use core::ptr::copy_nonoverlapping;
+    use core::slice;
+
+    let buf: &'static [u8] = unsafe { slice::from_raw_parts(buf, l) };
     let mut l = l;
-    if let Some(stream_write) = (*stream).write {
-        if (*stream).wend.is_null() && !internal::to_write(stream) {
-            // We can't write to this stream
-            return 0;
-        }
-        if l > (*stream).wend as usize - (*stream).wpos as usize {
-            // We can't fit all of buf in the buffer
-            return stream_write(stream, buf, l);
-        }
+    let mut advance = 0;
 
-        let i = if (*stream).buf_char >= 0 {
-            let mut i = l;
-            while i > 0 && *buf.offset(i as isize - 1) != b'\n' {
-                i -= 1;
-            }
-            if i > 0 {
-                let n = stream_write(stream, buf, i);
-                if n < i {
-                    return n;
-                }
-                buf = buf.add(i);
-                l -= i;
-            }
-            i
-        } else {
-            0
-        };
+    if stream.wend.is_null() && !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);
+    }
 
-        memcpy((*stream).wpos, buf, l);
-        (*stream).wpos = (*stream).wpos.add(l);
-        l + i
+    let i = if stream.buf_char >= 0 {
+        let mut i = l;
+        while i > 0 && buf[i - 1] != b'\n' {
+            i -= 1;
+        }
+        if i > 0 {
+            let n = stream.write(buf);
+            if n < i {
+                return n;
+            }
+            advance += i;
+            l -= i;
+        }
+        i
     } else {
-        // We can't write to this stream
         0
+    };
+
+    unsafe {
+        // Copy and reposition
+        copy_nonoverlapping(&buf[advance..] as *const _ as *const u8, stream.wpos, l);
+        stream.wpos = stream.wpos.add(l);
     }
+    l + i
 }
 
 /// Flush `stream` without locking it.
-pub unsafe fn fflush_unlocked(stream: *mut FILE) -> c_int {
-    if (*stream).wpos > (*stream).wbase {
-        if let Some(f) = (*stream).write {
-            f(stream, ptr::null(), 0);
-            if (*stream).wpos.is_null() {
-                return -1;
-            }
-        } else {
+pub fn fflush_unlocked(stream: &mut FILE) -> c_int {
+    if stream.wpos > stream.wbase {
+        stream.write(&[]);
+        if stream.wpos.is_null() {
             return -1;
         }
     }
 
-    if (*stream).rpos < (*stream).rend {
-        if let Some(s) = (*stream).seek {
-            s(
-                stream,
-                (*stream).rpos as i64 - (*stream).rend as i64,
-                SEEK_CUR,
-            );
-        }
+    if stream.rpos < stream.rend {
+        stream.seek(stream.rpos as i64 - stream.rend as i64, SEEK_CUR);
     }
 
-    (*stream).wpos = ptr::null_mut();
-    (*stream).wend = ptr::null_mut();
-    (*stream).wbase = ptr::null_mut();
-    (*stream).rpos = ptr::null_mut();
-    (*stream).rend = ptr::null_mut();
+    stream.wpos = ptr::null_mut();
+    stream.wend = ptr::null_mut();
+    stream.wbase = ptr::null_mut();
+    stream.rpos = ptr::null_mut();
+    stream.rend = ptr::null_mut();
     0
 }
diff --git a/src/stdio/src/internal.rs b/src/stdio/src/internal.rs
index 99005e87..35eb5f47 100644
--- a/src/stdio/src/internal.rs
+++ b/src/stdio/src/internal.rs
@@ -3,151 +3,17 @@ use platform;
 use platform::types::*;
 use core::{mem, ptr, slice};
 
-pub fn stdio_read(stream: *mut FILE, buf: *mut u8, size: usize) -> usize {
-    unsafe {
-        let mut buff = slice::from_raw_parts_mut(buf, size - !((*stream).buf_size == 0) as usize);
-        let mut file_buf = slice::from_raw_parts_mut((*stream).buf, (*stream).buf_size);
-        let mut file = platform::FileReader((*stream).fd);
-        let count = if buff.len() == 0 {
-            file.read(&mut file_buf)
+pub fn ftello(stream: &mut FILE) -> off_t {
+    let pos = stream.seek(
+        0,
+        if (stream.flags & constants::F_APP > 0) && stream.wpos > stream.wbase {
+            constants::SEEK_END
         } else {
-            file.read(&mut buff) + file.read(&mut file_buf)
-        };
-        mem::forget(buff);
-        mem::forget(file_buf);
-        if count <= 0 {
-            (*stream).flags |= if count == 0 {
-                constants::F_EOF
-            } else {
-                constants::F_ERR
-            };
-            return 0;
-        }
-        if count as usize <= size {
-            return count as usize;
-        }
-        (*stream).rpos = (*stream).buf;
-        (*stream).rend = (*stream).buf.offset(count);
-        *buf.offset(size as isize - 1) = *(*stream).rpos;
-        (*stream).rpos = (*stream).rpos.add(1);
+            constants::SEEK_CUR
+        },
+    );
+    if pos < 0 {
+        return pos;
     }
-    size
-}
-
-pub fn stdio_write(stream: *mut FILE, buf: *const u8, size: usize) -> usize {
-    unsafe {
-        let len = (*stream).wpos as usize - (*stream).wbase as usize;
-        let mut advance = 0;
-        let mut f_buf = slice::from_raw_parts((*stream).wbase, len);
-        let mut buff = slice::from_raw_parts(buf, size);
-        let mut f_filled = false;
-        let mut rem = f_buf.len() + buff.len();
-        let mut file = platform::FileWriter((*stream).fd);
-        loop {
-            let mut count = if f_filled {
-                file.write(&f_buf[advance..])
-            } else {
-                file.write(&f_buf[advance..]) + file.write(buff)
-            };
-            if count == rem as isize {
-                (*stream).wend = (*stream).buf.add((*stream).buf_size - 1);
-                (*stream).wpos = (*stream).buf;
-                (*stream).wbase = (*stream).buf;
-                return size;
-            }
-            if count < 0 {
-                (*stream).wpos = ptr::null_mut();
-                (*stream).wbase = ptr::null_mut();
-                (*stream).wend = ptr::null_mut();
-                (*stream).flags |= constants::F_ERR;
-                return 0;
-            }
-            rem -= count as usize;
-            if count as usize > len {
-                count -= len as isize;
-                f_buf = buff;
-                f_filled = true;
-                advance = 0;
-            }
-            advance += count as usize;
-        }
-    }
-}
-
-pub unsafe fn to_read(stream: *mut FILE) -> bool {
-    if (*stream).flags & constants::F_BADJ > 0 {
-        // Static and needs unget region
-        (*stream).buf = (*stream).buf.add((*stream).unget);
-        (*stream).flags &= !constants::F_BADJ;
-    }
-
-    if (*stream).wpos > (*stream).wbase {
-        if let Some(f) = (*stream).write {
-            f(stream, ptr::null(), 0);
-        }
-    }
-    (*stream).wpos = ptr::null_mut();
-    (*stream).wbase = ptr::null_mut();
-    (*stream).wend = ptr::null_mut();
-    if (*stream).flags & constants::F_NORD > 0 {
-        (*stream).flags |= constants::F_ERR;
-        return true;
-    }
-    (*stream).rpos = (*stream).buf.offset((*stream).buf_size as isize - 1);
-    (*stream).rend = (*stream).buf.offset((*stream).buf_size as isize - 1);
-    if (*stream).flags & constants::F_EOF > 0 {
-        true
-    } else {
-        false
-    }
-}
-
-pub unsafe fn to_write(stream: *mut FILE) -> bool {
-    if (*stream).flags & constants::F_BADJ > 0 {
-        // Static and needs unget region
-        (*stream).buf = (*stream).buf.add((*stream).unget);
-        (*stream).flags &= !constants::F_BADJ;
-    }
-
-    if (*stream).flags & constants::F_NOWR > 0 {
-        (*stream).flags &= constants::F_ERR;
-        return false;
-    }
-    (*stream).rpos = ptr::null_mut();
-    (*stream).rend = ptr::null_mut();
-    (*stream).wpos = (*stream).buf;
-    (*stream).wbase = (*stream).buf;
-    (*stream).wend = (*stream).buf.offset((*stream).buf_size as isize - 1);
-    return true;
-}
-
-pub unsafe fn ftello(stream: *mut FILE) -> off_t {
-    if let Some(s) = (*stream).seek {
-        let pos = s(
-            stream,
-            0,
-            if ((*stream).flags & constants::F_APP > 0) && (*stream).wpos > (*stream).wbase {
-                constants::SEEK_END
-            } else {
-                constants::SEEK_CUR
-            },
-        );
-        if pos < 0 {
-            return pos;
-        }
-        pos - ((*stream).rend as i64 - (*stream).rpos as i64)
-            + ((*stream).wpos as i64 - (*stream).wbase as i64)
-    } else {
-        -1
-    }
-}
-
-#[cfg(target_os = "linux")]
-pub fn stdio_seek(stream: *mut FILE, off: off_t, whence: c_int) -> off_t {
-    unsafe { platform::lseek((*stream).fd, off, whence) }
-}
-
-#[cfg(target_os = "redox")]
-pub fn stdio_seek(stream: *mut FILE, off: off_t, whence: c_int) -> off_t {
-    unsafe { platform::lseek((*stream).fd, off as isize, whence as usize) as off_t }
+    pos - (stream.rend as i64 - stream.rpos as i64) + (stream.wpos as i64 - stream.wbase as i64)
 }
diff --git a/src/stdio/src/lib.rs b/src/stdio/src/lib.rs
index df5e9b6d..fcae9898 100644
--- a/src/stdio/src/lib.rs
+++ b/src/stdio/src/lib.rs
@@ -13,7 +13,7 @@ extern crate va_list as vl;
 
 use core::str;
 use core::ptr;
-use core::fmt::Write;
+use core::fmt::{Error, Result, Write};
 use core::sync::atomic::{AtomicBool, Ordering};
 
 use platform::types::*;
@@ -33,9 +33,6 @@ mod helpers;
 
 mod internal;
 
-/// The structure of this struct is likely to change.
-/// all of the _pos pointers are likely to change to isize offsets and its possible that the
-/// function pointers will be removed
 #[repr(C)]
 pub struct FILE {
     flags: c_int,
@@ -50,15 +47,140 @@ pub struct FILE {
     buf_char: i8,
     lock: AtomicBool,
     unget: size_t,
-    write: Option<(fn(*mut FILE, *const u8, usize) -> size_t)>,
-    read: Option<(fn(*mut FILE, *mut u8, usize) -> size_t)>,
-    seek: Option<(fn(*mut FILE, off_t, c_int) -> off_t)>,
+}
+
+impl FILE {
+    pub fn can_read(&mut self) -> bool {
+        if self.flags & constants::F_BADJ > 0 {
+            // Static and needs unget region
+            self.buf = unsafe { self.buf.add(self.unget) };
+            self.flags &= !constants::F_BADJ;
+        }
+
+        if self.wpos > self.wbase {
+            self.write(&[]);
+        }
+        self.wpos = ptr::null_mut();
+        self.wbase = ptr::null_mut();
+        self.wend = ptr::null_mut();
+        if self.flags & constants::F_NORD > 0 {
+            self.flags |= constants::F_ERR;
+            return false;
+        }
+        self.rpos = unsafe { self.buf.offset(self.buf_size as isize - 1) };
+        self.rend = unsafe { self.buf.offset(self.buf_size as isize - 1) };
+        if self.flags & constants::F_EOF > 0 {
+            false
+        } else {
+            true
+        }
+    }
+    pub fn can_write(&mut self) -> bool {
+        if self.flags & constants::F_BADJ > 0 {
+            // Static and needs unget region
+            self.buf = unsafe { self.buf.add(self.unget) };
+            self.flags &= !constants::F_BADJ;
+        }
+
+        if self.flags & constants::F_NOWR > 0 {
+            self.flags &= constants::F_ERR;
+            return false;
+        }
+        // Buffer repositioning
+        self.rpos = ptr::null_mut();
+        self.rend = ptr::null_mut();
+        self.wpos = self.buf;
+        self.wbase = self.buf;
+        self.wend = unsafe { self.buf.offset(self.buf_size as isize - 1) };
+        return true;
+    }
+    pub fn write(&mut self, to_write: &[u8]) -> usize {
+        use core::slice;
+        use core::mem;
+        let len = self.wpos as usize - self.wbase as usize;
+        let mut advance = 0;
+        let mut f_buf: &'static _ = unsafe { slice::from_raw_parts(self.wbase, len) };
+        let mut f_filled = false;
+        let mut rem = f_buf.len() + to_write.len();
+        loop {
+            let mut count = if f_filled {
+                platform::write(self.fd, &f_buf[advance..])
+            } else {
+                platform::write(self.fd, &f_buf[advance..]) + platform::write(self.fd, to_write)
+            };
+            if count == rem as isize {
+                self.wend = unsafe { self.buf.add(self.buf_size - 1) };
+                self.wpos = self.buf;
+                self.wbase = self.buf;
+                return to_write.len();
+            }
+            if count < 0 {
+                self.wpos = ptr::null_mut();
+                self.wbase = ptr::null_mut();
+                self.wend = ptr::null_mut();
+                self.flags |= constants::F_ERR;
+                return 0;
+            }
+            rem -= count as usize;
+            if count as usize > len {
+                count -= len as isize;
+                f_buf = unsafe { mem::transmute(to_write) };
+                f_filled = true;
+                advance = 0;
+            }
+            advance += count as usize;
+        }
+    }
+    pub fn read(&mut self, buf: &mut [u8]) -> usize {
+        use core::slice;
+        // let buff = slice::from_raw_parts_mut(buf, size - !((*stream).buf_size == 0) as usize);
+        let adj = !(self.buf_size == 0) as usize;
+        let mut file_buf: &'static mut _ =
+            unsafe { slice::from_raw_parts_mut(self.buf, self.buf_size) };
+        let count = if buf.len() <= 1 + adj {
+            platform::read(self.fd, file_buf)
+        } else {
+            platform::read(self.fd, buf) + platform::read(self.fd, file_buf)
+        };
+        if count <= 0 {
+            self.flags |= if count == 0 {
+                constants::F_EOF
+            } else {
+                constants::F_ERR
+            };
+            return 0;
+        }
+        if count as usize <= buf.len() - adj {
+            return count as usize;
+        }
+        unsafe {
+            // Adjust pointers
+            self.rpos = self.buf;
+            self.rend = self.buf.offset(count);
+            buf[buf.len() - 1] = *self.rpos;
+            self.rpos = self.rpos.add(1);
+        }
+        buf.len()
+    }
+    pub fn seek(&self, off: off_t, whence: c_int) -> off_t {
+        unsafe { platform::lseek(self.fd, off, whence) }
+    }
+}
+impl Write for FILE {
+    fn write_str(&mut self, s: &str) -> Result {
+        let s = s.as_bytes();
+        if self.write(s) != s.len() {
+            Err(Error)
+        } else {
+            Ok(())
+        }
+    }
 }
 
 /// Clears EOF and ERR indicators on a stream
 #[no_mangle]
-pub unsafe extern "C" fn clearerr(stream: *mut FILE) {
-    (*stream).flags &= !(F_EOF & F_ERR);
+pub extern "C" fn clearerr(stream: &mut FILE) {
+    stream.flags &= !(F_EOF & F_ERR);
 }
 
 #[no_mangle]
@@ -76,37 +198,39 @@ pub extern "C" fn cuserid(s: *mut c_char) -> *mut c_char {
 /// descriptor will be closed, so if it is important that the file be written to, use `fflush()`
 /// prior to using this function.
 #[no_mangle]
-pub unsafe extern "C" fn fclose(stream: *mut FILE) -> c_int {
+pub extern "C" fn fclose(stream: &mut FILE) -> c_int {
     use stdlib::free;
     flockfile(stream);
-    let r = helpers::fflush_unlocked(stream) | platform::close((*stream).fd);
-    if (*stream).flags & constants::F_PERM == 0 {
+    let r = helpers::fflush_unlocked(stream) | platform::close(stream.fd);
+    if stream.flags & constants::F_PERM == 0 {
         // Not one of stdin, stdout or stderr
-        free(stream as *mut _);
+        unsafe {
+            free(stream as *mut _ as *mut _);
+        }
     }
     r
 }
 
 /// Open a file from a file descriptor
 #[no_mangle]
-pub unsafe extern "C" fn fdopen(fildes: c_int, mode: *const c_char) -> *mut FILE {
-    helpers::_fdopen(fildes, mode)
+pub extern "C" fn fdopen(fildes: c_int, mode: *const c_char) -> *mut FILE {
+    unsafe { helpers::_fdopen(fildes, mode) }
 }
 
 /// Check for EOF
 #[no_mangle]
-pub unsafe extern "C" fn feof(stream: *mut FILE) -> c_int {
+pub extern "C" fn feof(stream: &mut FILE) -> c_int {
     flockfile(stream);
-    let ret = (*stream).flags & F_EOF;
+    let ret = stream.flags & F_EOF;
     funlockfile(stream);
     ret
 }
 
 /// Check for ERR
 #[no_mangle]
-pub unsafe extern "C" fn ferror(stream: *mut FILE) -> c_int {
+pub extern "C" fn ferror(stream: &mut FILE) -> c_int {
     flockfile(stream);
-    let ret = (*stream).flags & F_ERR;
+    let ret = stream.flags & F_ERR;
     funlockfile(stream);
     ret
 }
@@ -115,7 +239,7 @@ pub unsafe extern "C" fn ferror(stream: *mut FILE) -> c_int {
 /// Ensure the file is unlocked before calling this function, as it will attempt to lock the file
 /// itself.
 #[no_mangle]
-pub unsafe extern "C" fn fflush(stream: *mut FILE) -> c_int {
+pub unsafe extern "C" fn fflush(stream: &mut FILE) -> c_int {
     flockfile(stream);
 
     let ret = helpers::fflush_unlocked(stream);
@@ -126,7 +250,7 @@ pub unsafe extern "C" fn fflush(stream: *mut FILE) -> c_int {
 
 /// Get a single char from a stream
 #[no_mangle]
-pub unsafe extern "C" fn fgetc(stream: *mut FILE) -> c_int {
+pub extern "C" fn fgetc(stream: &mut FILE) -> c_int {
     flockfile(stream);
     let c = getc_unlocked(stream);
     funlockfile(stream);
@@ -135,46 +259,58 @@ pub unsafe extern "C" fn fgetc(stream: *mut FILE) -> c_int {
 
 /// Get the position of the stream and store it in pos
 #[no_mangle]
-pub unsafe extern "C" fn fgetpos(stream: *mut FILE, pos: *mut fpos_t) -> c_int {
+pub extern "C" fn fgetpos(stream: &mut FILE, pos: *mut fpos_t) -> c_int {
     let off = internal::ftello(stream);
     if off < 0 {
         return -1;
     }
-    (*pos) = off;
+    unsafe {
+        (*pos) = off;
+    }
     0
 }
 
 /// Get a string from the stream
 #[no_mangle]
-pub unsafe extern "C" fn fgets(s: *mut c_char, n: c_int, stream: *mut FILE) -> *mut c_char {
+pub extern "C" fn fgets(s: *mut c_char, n: c_int, stream: &mut FILE) -> *mut c_char {
     use string::memchr;
-    use compiler_builtins::mem::memcpy;
+    use core::ptr::copy_nonoverlapping;
+
     flockfile(stream);
     let mut ptr = s as *mut u8;
     let mut n = n;
+
     if n <= 1 {
         funlockfile(stream);
         if n == 0 {
             return ptr::null_mut();
         }
-        (*s) = b'\0' as i8;
+        unsafe {
+            (*s) = b'\0' as i8;
+        }
         return s;
     }
     while n > 0 {
-        let z = memchr(
-            (*stream).rpos as *const c_void,
-            b'\n' as c_int,
-            (*stream).rend as usize - (*stream).rpos as usize,
-        ) as *mut u8;
+        let z = unsafe {
+            memchr(
+                stream.rpos as *const c_void,
+                b'\n' as c_int,
+                stream.rend as usize - stream.rpos as usize,
+            ) as *mut u8
+        };
         let k = if z.is_null() {
-            (*stream).rend as usize - (*stream).rpos as usize
+            stream.rend as usize - stream.rpos as usize
         } else {
-            z as usize - (*stream).rpos as usize + 1
+            z as usize - stream.rpos as usize + 1
         };
         let k = if k as i32 > n { n as usize } else { k };
-        memcpy(ptr, (*stream).rpos, k);
-        (*stream).rpos = (*stream).rpos.add(k);
-        ptr = ptr.add(k);
+        unsafe {
+            // Copy
+            copy_nonoverlapping(stream.rpos, ptr, k);
+            // Reposition pointers
+            stream.rpos = stream.rpos.add(k);
+            ptr = ptr.add(k);
+        }
         n -= k as i32;
         if !z.is_null() || n < 1 {
             break;
@@ -184,14 +320,21 @@ pub unsafe extern "C" fn fgets(s: *mut c_char, n: c_int, stream: *mut FILE) -> *
             break;
         }
         n -= 1;
-        *ptr = c as u8;
-        ptr = ptr.add(1);
+
+        unsafe {
+            // Pointer stuff
+            *ptr = c as u8;
+            ptr = ptr.add(1);
+        }
+
         if c as u8 == b'\n' {
             break;
         }
     }
     if !s.is_null() {
-        *ptr = 0;
+        unsafe {
+            *ptr = 0;
+        }
     }
     funlockfile(stream);
     s
@@ -199,17 +342,17 @@ pub unsafe extern "C" fn fgets(s: *mut c_char, n: c_int, stream: *mut FILE) -> *
 
 /// Get the underlying file descriptor
 #[no_mangle]
-pub unsafe extern "C" fn fileno(stream: *mut FILE) -> c_int {
+pub extern "C" fn fileno(stream: &mut FILE) -> c_int {
     flockfile(stream);
     funlockfile(stream);
-    (*stream).fd
+    stream.fd
 }
 
 /// Lock the file
 /// Do not call any functions other than those with the `_unlocked` postfix while the file is
 /// locked
 #[no_mangle]
-pub unsafe extern "C" fn flockfile(file: *mut FILE) {
+pub extern "C" fn flockfile(file: &mut FILE) {
     while ftrylockfile(file) != 0 {}
 }
 
@@ -244,7 +387,7 @@ pub unsafe extern "C" fn fopen(filename: *const c_char, mode: *const c_char) ->
 
 /// Insert a character into the stream
 #[no_mangle]
-pub unsafe extern "C" fn fputc(c: c_int, stream: *mut FILE) -> c_int {
+pub extern "C" fn fputc(c: c_int, stream: &mut FILE) -> c_int {
     flockfile(stream);
     let c = putc_unlocked(c, stream);
     funlockfile(stream);
@@ -253,48 +396,44 @@ pub unsafe extern "C" fn fputc(c: c_int, stream: *mut FILE) -> c_int {
 
 /// Insert a string into a stream
 #[no_mangle]
-pub unsafe extern "C" fn fputs(s: *const c_char, stream: *mut FILE) -> c_int {
+pub extern "C" fn fputs(s: *const c_char, stream: &mut FILE) -> c_int {
     extern "C" {
         fn strlen(s: *const c_char) -> size_t;
     }
-    let len = strlen(s);
+    let len = unsafe { strlen(s) };
     (fwrite(s as *const c_void, 1, len, stream) == len) as c_int - 1
 }
 
 /// Read `nitems` of size `size` into `ptr` from `stream`
 #[no_mangle]
-pub unsafe extern "C" fn fread(
-    ptr: *mut c_void,
-    size: usize,
-    nitems: usize,
-    stream: *mut FILE,
-) -> usize {
-    use compiler_builtins::mem::memcpy;
+pub extern "C" fn fread(ptr: *mut c_void, size: usize, nitems: usize, stream: &mut FILE) -> usize {
+    use core::ptr::copy_nonoverlapping;
+    use core::slice;
     let mut dest = ptr as *mut u8;
     let len = size * nitems;
     let mut l = len as isize;
 
     flockfile(stream);
 
-    if (*stream).rend > (*stream).rpos {
+    if stream.rend > stream.rpos {
         // We have some buffered data that can be read
-        let diff = (*stream).rend as usize - (*stream).rpos as usize;
+        let diff = stream.rend as usize - stream.rpos as usize;
         let k = if diff < l as usize { diff } else { l as usize };
-        memcpy(dest, (*stream).rpos, k);
-        (*stream).rpos = (*stream).rpos.add(k);
-        dest = dest.add(k);
+        unsafe {
+            // Copy data
+            copy_nonoverlapping(stream.rpos, dest, k);
+            // Reposition pointers
+            stream.rpos = stream.rpos.add(k);
+            dest = dest.add(k);
+        }
         l -= k as isize;
     }
 
     while l > 0 {
-        let k = if internal::to_read(stream) {
+        let k = if !stream.can_read() {
             0
         } else {
-            if let Some(f) = (*stream).read {
-                f(stream, dest, l as usize)
-            } else {
-                0
-            }
+            stream.read(unsafe { slice::from_raw_parts_mut(dest, l as usize) })
         };
 
         if k == 0 {
@@ -303,7 +442,10 @@ pub unsafe extern "C" fn fread(
         }
 
         l -= k as isize;
-        dest = dest.add(k);
+        unsafe {
+            // Reposition
+            dest = dest.add(k);
+        }
     }
 
     funlockfile(stream);
@@ -311,47 +453,45 @@ pub unsafe extern "C" fn fread(
 }
 
 #[no_mangle]
-pub unsafe extern "C" fn freopen(
+pub extern "C" fn freopen(
     filename: *const c_char,
     mode: *const c_char,
-    stream: *mut FILE,
+    stream: &mut FILE,
 ) -> *mut FILE {
-    let mut flags = helpers::parse_mode_flags(mode);
+    let mut flags = unsafe { helpers::parse_mode_flags(mode) };
     flockfile(stream);
 
     helpers::fflush_unlocked(stream);
     if filename.is_null() {
         // Reopen stream in new mode
         if flags & fcntl::O_CLOEXEC > 0 {
-            fcntl::sys_fcntl((*stream).fd, fcntl::F_SETFD, fcntl::FD_CLOEXEC);
+            fcntl::sys_fcntl(stream.fd, fcntl::F_SETFD, fcntl::FD_CLOEXEC);
         }
         flags &= !(fcntl::O_CREAT | fcntl::O_EXCL | fcntl::O_CLOEXEC);
-        if fcntl::sys_fcntl((*stream).fd, fcntl::F_SETFL, flags) < 0 {
+        if fcntl::sys_fcntl(stream.fd, fcntl::F_SETFL, flags) < 0 {
             funlockfile(stream);
             fclose(stream);
             return ptr::null_mut();
         }
     } else {
-        let new = fopen(filename, mode);
+        let new = unsafe { fopen(filename, mode) };
         if new.is_null() {
             funlockfile(stream);
             fclose(stream);
             return ptr::null_mut();
         }
-        if (*new).fd == (*stream).fd {
-            (*new).fd = -1;
-        } else if platform::dup2((*new).fd, (*stream).fd) < 0
-            || fcntl::sys_fcntl((*stream).fd, fcntl::F_SETFL, flags & fcntl::O_CLOEXEC) < 0
+        let new = unsafe { &mut *new }; // Should be safe, new is not null
+        if new.fd == stream.fd {
+            new.fd = -1;
+        } else if platform::dup2(new.fd, stream.fd) < 0
+            || fcntl::sys_fcntl(stream.fd, fcntl::F_SETFL, flags & fcntl::O_CLOEXEC) < 0
         {
             fclose(new);
             funlockfile(stream);
             fclose(stream);
             return ptr::null_mut();
         }
-        (*stream).flags = ((*stream).flags & constants::F_PERM) | (*new).flags;
-        (*stream).read = (*new).read;
-        (*stream).write = (*new).write;
-        (*stream).seek = (*new).seek;
+        stream.flags = (stream.flags & constants::F_PERM) | new.flags;
         fclose(new);
     }
     funlockfile(stream);
@@ -360,7 +500,7 @@ pub unsafe extern "C" fn freopen(
 
 /// Seek to an offset `offset` from `whence`
 #[no_mangle]
-pub unsafe extern "C" fn fseek(stream: *mut FILE, offset: c_long, whence: c_int) -> c_int {
+pub extern "C" fn fseek(stream: &mut FILE, offset: c_long, whence: c_int) -> c_int {
     if fseeko(stream, offset as off_t, whence) != -1 {
         return 0;
     }
@@ -369,53 +509,47 @@ pub unsafe extern "C" fn fseek(stream: *mut FILE, offset: c_long, whence: c_int)
 
 /// Seek to an offset `offset` from `whence`
 #[no_mangle]
-pub unsafe extern "C" fn fseeko(stream: *mut FILE, offset: off_t, whence: c_int) -> c_int {
+pub extern "C" fn fseeko(stream: &mut FILE, offset: off_t, whence: c_int) -> c_int {
     let mut off = offset;
     flockfile(stream);
     // Adjust for what is currently in the buffer
     if whence == SEEK_CUR {
-        off -= ((*stream).rend as usize - (*stream).rpos as usize) as i64;
+        off -= (stream.rend as usize - stream.rpos as usize) as i64;
     }
-    if (*stream).wpos > (*stream).wbase {
-        if let Some(f) = (*stream).write {
-            f(stream, ptr::null(), 0);
-            if (*stream).wpos.is_null() {
-                return -1;
-            }
-        }
-    }
-    (*stream).wpos = ptr::null_mut();
-    (*stream).wend = ptr::null_mut();
-    (*stream).wbase = ptr::null_mut();
-    if let Some(s) = (*stream).seek {
-        if s(stream, off, whence) < 0 {
+    if stream.wpos > stream.wbase {
+        stream.write(&[]);
+        if stream.wpos.is_null() {
             return -1;
         }
-    } else {
+    }
+    stream.wpos = ptr::null_mut();
+    stream.wend = ptr::null_mut();
+    stream.wbase = ptr::null_mut();
+    if stream.seek(off, whence) < 0 {
         return -1;
     }
-    (*stream).rpos = ptr::null_mut();
-    (*stream).rend = ptr::null_mut();
-    (*stream).flags &= !F_EOF;
+    stream.rpos = ptr::null_mut();
+    stream.rend = ptr::null_mut();
+    stream.flags &= !F_EOF;
     funlockfile(stream);
     0
 }
 
 /// Seek to a position `pos` in the file from the beginning of the file
 #[no_mangle]
-pub unsafe extern "C" fn fsetpos(stream: *mut FILE, pos: *const fpos_t) -> c_int {
+pub unsafe extern "C" fn fsetpos(stream: &mut FILE, pos: *const fpos_t) -> c_int {
     fseek(stream, *pos as off_t, SEEK_SET)
 }
 
 /// Get the current position of the cursor in the file
 #[no_mangle]
-pub unsafe extern "C" fn ftell(stream: *mut FILE) -> c_long {
+pub unsafe extern "C" fn ftell(stream: &mut FILE) -> c_long {
     ftello(stream) as c_long
 }
 
 /// Get the current position of the cursor in the file
 #[no_mangle]
-pub unsafe extern "C" fn ftello(stream: *mut FILE) -> off_t {
+pub extern "C" fn ftello(stream: &mut FILE) -> off_t {
     flockfile(stream);
     let pos = internal::ftello(stream);
     funlockfile(stream);
@@ -424,25 +558,23 @@ pub unsafe extern "C" fn ftello(stream: *mut FILE) -> off_t {
 
 /// Try to lock the file. Returns 0 for success, 1 for failure
 #[no_mangle]
-pub unsafe extern "C" fn ftrylockfile(file: *mut FILE) -> c_int {
-    (*file)
-        .lock
-        .compare_and_swap(false, true, Ordering::Acquire) as c_int
+pub extern "C" fn ftrylockfile(file: &mut FILE) -> c_int {
+    file.lock.compare_and_swap(false, true, Ordering::Acquire) as c_int
 }
 
 /// Unlock the file
 #[no_mangle]
-pub unsafe extern "C" fn funlockfile(file: *mut FILE) {
-    (*file).lock.store(false, Ordering::Release);
+pub extern "C" fn funlockfile(file: &mut FILE) {
+    file.lock.store(false, Ordering::Release);
 }
 
 /// Write `nitems` of size `size` from `ptr` to `stream`
 #[no_mangle]
-pub unsafe extern "C" fn fwrite(
+pub extern "C" fn fwrite(
     ptr: *const c_void,
     size: usize,
     nitems: usize,
-    stream: *mut FILE,
+    stream: &mut FILE,
 ) -> usize {
     let l = size * nitems;
     let nitems = if size == 0 { 0 } else { nitems };
@@ -458,7 +590,7 @@ pub unsafe extern "C" fn fwrite(
 
 /// Get a single char from a stream
 #[no_mangle]
-pub unsafe extern "C" fn getc(stream: *mut FILE) -> c_int {
+pub extern "C" fn getc(stream: &mut FILE) -> c_int {
     flockfile(stream);
     let c = getc_unlocked(stream);
     funlockfile(stream);
@@ -468,24 +600,22 @@ pub unsafe extern "C" fn getc(stream: *mut FILE) -> c_int {
 /// Get a single char from `stdin`
 #[no_mangle]
 pub unsafe extern "C" fn getchar() -> c_int {
-    fgetc(stdin)
+    fgetc(&mut *stdin)
 }
 
 /// Get a char from a stream without locking the stream
 #[no_mangle]
-pub unsafe extern "C" fn getc_unlocked(stream: *mut FILE) -> c_int {
-    if (*stream).rpos < (*stream).rend {
-        let ret = *(*stream).rpos as c_int;
-        (*stream).rpos = (*stream).rpos.add(1);
-        ret
+pub extern "C" fn getc_unlocked(stream: &mut FILE) -> c_int {
+    if stream.rpos < stream.rend {
+        unsafe {
+            let ret = *stream.rpos as c_int;
+            stream.rpos = stream.rpos.add(1);
+            ret
+        }
     } else {
-        if let Some(read) = (*stream).read {
-            let mut c = 0u8;
-            if !internal::to_read(stream) && read(stream, &mut c, 1) == 1 {
-                c as c_int
-            } else {
-                -1
-            }
+        let mut c = [0u8; 1];
+        if stream.can_read() && stream.read(&mut c) == 1 {
+            c[0] as c_int
         } else {
             -1
         }
@@ -495,19 +625,19 @@ pub unsafe extern "C" fn getc_unlocked(stream: *mut FILE) -> c_int {
 /// Get a char from `stdin` without locking `stdin`
 #[no_mangle]
 pub unsafe extern "C" fn getchar_unlocked() -> c_int {
-    getc_unlocked(stdin)
+    getc_unlocked(&mut *stdin)
 }
 
 /// Get a string from `stdin`
 #[no_mangle]
 pub unsafe extern "C" fn gets(s: *mut c_char) -> *mut c_char {
     use core::i32;
-    fgets(s, i32::MAX, stdin)
+    fgets(s, i32::MAX, &mut *stdin)
 }
 
 /// Get an integer from `stream`
 #[no_mangle]
-pub unsafe extern "C" fn getw(stream: *mut FILE) -> c_int {
+pub extern "C" fn getw(stream: &mut FILE) -> c_int {
     use core::mem;
     let mut ret: c_int = 0;
     if fread(
@@ -524,7 +654,7 @@ pub unsafe extern "C" fn getw(stream: *mut FILE) -> c_int {
 }
 
 #[no_mangle]
-pub extern "C" fn pclose(stream: *mut FILE) -> c_int {
+pub extern "C" fn pclose(stream: &mut FILE) -> c_int {
     unimplemented!();
 }
 
@@ -547,7 +677,7 @@ pub extern "C" fn popen(command: *const c_char, mode: *const c_char) -> *mut FIL
 
 /// Put a character `c` into `stream`
 #[no_mangle]
-pub unsafe extern "C" fn putc(c: c_int, stream: *mut FILE) -> c_int {
+pub extern "C" fn putc(c: c_int, stream: &mut FILE) -> c_int {
     flockfile(stream);
     let ret = putc_unlocked(c, stream);
     funlockfile(stream);
@@ -557,31 +687,31 @@ pub unsafe extern "C" fn putc(c: c_int, stream: *mut FILE) -> c_int {
 /// Put a character `c` into `stdout`
 #[no_mangle]
 pub unsafe extern "C" fn putchar(c: c_int) -> c_int {
-    fputc(c, stdout)
+    fputc(c, &mut *stdout)
 }
 
 /// Put a character `c` into `stream` without locking `stream`
 #[no_mangle]
-pub unsafe extern "C" fn putc_unlocked(c: c_int, stream: *mut FILE) -> c_int {
-    if c as i8 != (*stream).buf_char && (*stream).wpos < (*stream).wend {
-        *(*stream).wpos = c as u8;
-        (*stream).wpos = (*stream).wpos.add(1);
-        c
+pub extern "C" fn putc_unlocked(c: c_int, stream: &mut FILE) -> c_int {
+    if c as i8 != stream.buf_char && stream.wpos < stream.wend {
+        unsafe {
+            *stream.wpos = c as u8;
+            stream.wpos = stream.wpos.add(1);
+            c
+        }
     } else {
-        if let Some(write) = (*stream).write {
-            if (*stream).wend.is_null() && internal::to_write(stream) {
-                -1
-            } else if c as i8 != (*stream).buf_char && (*stream).wpos < (*stream).wend {
-                *(*stream).wpos = c as u8;
-                (*stream).wpos = (*stream).wpos.add(1);
-                c
-            } else if write(stream, &c as *const i32 as *const _, 1) != 1 {
-                -1
-            } else {
+        if stream.wend.is_null() && stream.can_write() {
+            -1
+        } else if c as i8 != stream.buf_char && stream.wpos < stream.wend {
+            unsafe {
+                *stream.wpos = c as u8;
+                stream.wpos = stream.wpos.add(1);
                 c
             }
-        } else {
+        } else if stream.write(&[c as u8]) != 1 {
             -1
+        } else {
+            c
         }
     }
 }
@@ -589,13 +719,13 @@ pub unsafe extern "C" fn putc_unlocked(c: c_int, stream: *mut FILE) -> c_int {
 /// Put a character `c` into `stdout` without locking `stdout`
 #[no_mangle]
 pub unsafe extern "C" fn putchar_unlocked(c: c_int) -> c_int {
-    putc_unlocked(c, stdout)
+    putc_unlocked(c, &mut *stdout)
 }
 
 /// Put a string `s` into `stdout`
 #[no_mangle]
 pub unsafe extern "C" fn puts(s: *const c_char) -> c_int {
-    let ret = (fputs(s, stdout) > 0) || (putchar_unlocked(b'\n' as c_int) > 0);
+    let ret = (fputs(s, &mut *stdout) > 0) || (putchar_unlocked(b'\n' as c_int) > 0);
     if ret {
         0
     } else {
@@ -605,7 +735,7 @@ pub unsafe extern "C" fn puts(s: *const c_char) -> c_int {
 
 /// Put an integer `w` into `stream`
 #[no_mangle]
-pub unsafe extern "C" fn putw(w: c_int, stream: *mut FILE) -> c_int {
+pub extern "C" fn putw(w: c_int, stream: &mut FILE) -> c_int {
     use core::mem;
     fwrite(&w as *const i32 as _, mem::size_of_val(&w), 1, stream) as i32 - 1
 }
@@ -629,16 +759,16 @@ pub extern "C" fn rename(old: *const c_char, new: *const c_char) -> c_int {
 
 /// Rewind `stream` back to the beginning of it
 #[no_mangle]
-pub unsafe extern "C" fn rewind(stream: *mut FILE) {
+pub extern "C" fn rewind(stream: &mut FILE) {
     fseeko(stream, 0, SEEK_SET);
     flockfile(stream);
-    (*stream).flags &= !F_ERR;
+    stream.flags &= !F_ERR;
     funlockfile(stream);
 }
 
 /// Reset `stream` to use buffer `buf`. Buffer must be `BUFSIZ` in length
 #[no_mangle]
-pub extern "C" fn setbuf(stream: *mut FILE, buf: *mut c_char) {
+pub extern "C" fn setbuf(stream: &mut FILE, buf: *mut c_char) {
     unsafe {
         setvbuf(
             stream,
@@ -650,9 +780,10 @@ pub extern "C" fn setbuf(stream: *mut FILE, buf: *mut c_char) {
 }
 
 /// Reset `stream` to use buffer `buf` of size `size`
+/// If this isn't the meaning of unsafe, idk what is
 #[no_mangle]
 pub unsafe extern "C" fn setvbuf(
-    stream: *mut FILE,
+    stream: &mut FILE,
     buf: *mut c_char,
     mode: c_int,
     size: usize,
@@ -692,22 +823,24 @@ pub extern "C" fn tmpnam(s: *mut c_char) -> *mut c_char {
 
 /// Push character `c` back onto `stream` so it'll be read next
 #[no_mangle]
-pub unsafe extern "C" fn ungetc(c: c_int, stream: *mut FILE) -> c_int {
+pub extern "C" fn ungetc(c: c_int, stream: &mut FILE) -> c_int {
     if c < 0 {
         c
     } else {
         flockfile(stream);
-        if (*stream).rpos.is_null() {
-            internal::to_read(stream);
+        if stream.rpos.is_null() {
+            stream.can_read();
         }
-        if (*stream).rpos.is_null() || (*stream).rpos <= (*stream).buf.sub((*stream).unget) {
+        if stream.rpos.is_null() || stream.rpos <= unsafe { stream.buf.sub(stream.unget) } {
             funlockfile(stream);
             return -1;
         }
 
-        (*stream).rpos = (*stream).rpos.sub(1);
-        *(*stream).rpos = c as u8;
-        (*stream).flags &= !F_EOF;
+        unsafe {
+            stream.rpos = stream.rpos.sub(1);
+            *stream.rpos = c as u8;
+        }
+        stream.flags &= !F_EOF;
 
         funlockfile(stream);
         c
@@ -715,13 +848,13 @@ pub unsafe extern "C" fn ungetc(c: c_int, stream: *mut FILE) -> c_int {
 }
 
 #[no_mangle]
-pub unsafe extern "C" fn vfprintf(file: *mut FILE, format: *const c_char, ap: va_list) -> c_int {
-    printf::printf(platform::FileWriter((*file).fd), format, ap)
+pub unsafe extern "C" fn vfprintf(file: &mut FILE, format: *const c_char, ap: va_list) -> c_int {
+    printf::printf(file, format, ap)
 }
 
 #[no_mangle]
 pub unsafe extern "C" fn vprintf(format: *const c_char, ap: va_list) -> c_int {
-    vfprintf(stdout, format, ap)
+    vfprintf(&mut *stdout, format, ap)
 }
 
 #[no_mangle]
@@ -731,12 +864,16 @@ pub unsafe extern "C" fn vsnprintf(
     format: *const c_char,
     ap: va_list,
 ) -> c_int {
-    printf::printf(platform::StringWriter(s as *mut u8, n as usize), format, ap)
+    printf::printf(
+        &mut platform::StringWriter(s as *mut u8, n as usize),
+        format,
+        ap,
+    )
 }
 
 #[no_mangle]
 pub unsafe extern "C" fn vsprintf(s: *mut c_char, format: *const c_char, ap: va_list) -> c_int {
-    printf::printf(platform::UnsafeStringWriter(s as *mut u8), format, ap)
+    printf::printf(&mut platform::UnsafeStringWriter(s as *mut u8), format, ap)
 }
 
 /*
-- 
GitLab