diff --git a/include/bits/stdio.h b/include/bits/stdio.h index 10d1314d8b5c20709eeb86a7952f713e9455e99e..23d913d779806803bc35c21af2ba88249039dab3 100644 --- a/include/bits/stdio.h +++ b/include/bits/stdio.h @@ -1,9 +1,6 @@ #ifndef _BITS_STDIO_H #define _BITS_STDIO_H -#define EOF (-1) -#define BUFSIZ 1024 - typedef struct FILE FILE; int fprintf(FILE * stream, const char * fmt, ...); diff --git a/src/fs.rs b/src/fs.rs index 097c40ca2d1f2a96c1e4e274967127a7a2c86735..672081a4efa3b317de28ea29dcd1df683320e8fb 100644 --- a/src/fs.rs +++ b/src/fs.rs @@ -1,4 +1,5 @@ use c_str::CStr; +use core::ops::Deref; use header::fcntl::O_CREAT; use header::unistd::{SEEK_SET, SEEK_CUR, SEEK_END}; use io; @@ -11,48 +12,70 @@ fn last_os_error() -> io::Error { io::Error::from_raw_os_error(errno) } -pub struct File(c_int); +pub struct File { + pub fd: c_int, + /// To avoid self referential FILE struct that needs both a reader and a writer, + /// make "reference" files that share fd but don't close on drop. + pub reference: bool +} impl File { + pub fn new(fd: c_int) -> Self { + Self { + fd, + reference: false + } + } + pub fn open(path: &CStr, oflag: c_int) -> io::Result<Self> { match Sys::open(path, oflag, 0) { -1 => Err(last_os_error()), - ok => Ok(File(ok)), + ok => Ok(Self::new(ok)), } } pub fn create(path: &CStr, oflag: c_int, mode: mode_t) -> io::Result<Self> { match Sys::open(path, oflag | O_CREAT, mode) { -1 => Err(last_os_error()), - ok => Ok(File(ok)), + ok => Ok(Self::new(ok)), } } pub fn sync_all(&self) -> io::Result<()> { - match Sys::fsync(self.0) { + match Sys::fsync(self.fd) { -1 => Err(last_os_error()), _ok => Ok(()), } } pub fn set_len(&self, size: u64) -> io::Result<()> { - match Sys::ftruncate(self.0, size as off_t) { + match Sys::ftruncate(self.fd, size as off_t) { -1 => Err(last_os_error()), _ok => Ok(()), } } pub fn try_clone(&self) -> io::Result<Self> { - match Sys::dup(self.0) { + match Sys::dup(self.fd) { -1 => Err(last_os_error()), - ok => Ok(File(ok)), + ok => Ok(Self::new(ok)), + } + } + + /// Create a new file pointing to the same underlying descriptor. This file + /// will know it's a "reference" and won't close the fd. It will, however, + /// not prevent the original file from closing the fd. + pub unsafe fn get_ref(&self) -> Self { + Self { + fd: self.fd, + reference: false } } } impl io::Read for File { fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { - match Sys::read(self.0, buf) { + match Sys::read(self.fd, buf) { -1 => Err(last_os_error()), ok => Ok(ok as usize), } @@ -61,7 +84,7 @@ impl io::Read for File { impl io::Write for File { fn write(&mut self, buf: &[u8]) -> io::Result<usize> { - match Sys::write(self.0, buf) { + match Sys::write(self.fd, buf) { -1 => Err(last_os_error()), ok => Ok(ok as usize), } @@ -80,9 +103,25 @@ impl io::Seek for File { io::SeekFrom::End(end) => (end as off_t, SEEK_END), }; - match Sys::lseek(self.0, offset, whence) { + match Sys::lseek(self.fd, offset, whence) { -1 => Err(last_os_error()), ok => Ok(ok as u64), } } } + +impl Deref for File { + type Target = c_int; + + fn deref(&self) -> &Self::Target { + &self.fd + } +} + +impl Drop for File { + fn drop(&mut self) { + if !self.reference { + let _ = Sys::close(self.fd); + } + } +} diff --git a/src/header/stdio/constants.rs b/src/header/stdio/constants.rs index c601cbc85e3d07206b6dfdaa0314dfe870b58771..2adea19182e650b87265dbf2b69de99808d44d1f 100644 --- a/src/header/stdio/constants.rs +++ b/src/header/stdio/constants.rs @@ -1,8 +1,9 @@ use platform::types::*; -pub const BUFSIZ: size_t = 1024; +pub const EOF: c_int = -1; +pub const BUFSIZ: c_int = 1024; -pub const UNGET: size_t = 8; +pub const UNGET: c_int = 8; pub const FILENAME_MAX: c_int = 4096; diff --git a/src/header/stdio/default.rs b/src/header/stdio/default.rs index 5928254178e5e64c90978b81c2c28c0e59486578..315d5f7505a4ef2eedc57139b10f5f06ec9105ea 100644 --- a/src/header/stdio/default.rs +++ b/src/header/stdio/default.rs @@ -1,12 +1,28 @@ -use super::{constants, BUFSIZ, FILE, UNGET}; +use super::{constants, Buffer, BUFSIZ, FILE, UNGET}; use core::cell::UnsafeCell; use core::ptr; use core::sync::atomic::AtomicBool; +use fs::File; +use io::LineWriter; +use platform::types::*; + pub struct GlobalFile(UnsafeCell<FILE>); impl GlobalFile { - const fn new(file: FILE) -> Self { - GlobalFile(UnsafeCell::new(file)) + fn new(file: c_int, flags: c_int) -> Self { + let file = File::new(file); + let writer = LineWriter::new(unsafe { file.get_ref() }); + GlobalFile(UnsafeCell::new(FILE { + lock: AtomicBool::new(false), + + file, + flags: constants::F_PERM | flags, + read_buf: Buffer::Owned(vec![0; BUFSIZ as usize]), + read_pos: 0, + read_size: 0, + unget: None, + writer + })) } pub fn get(&self) -> *mut FILE { self.0.get() @@ -17,40 +33,13 @@ unsafe impl Sync for GlobalFile {} lazy_static! { #[allow(non_upper_case_globals)] - pub static ref default_stdin: GlobalFile = GlobalFile::new(FILE { - flags: constants::F_PERM | constants::F_NOWR, - read: None, - write: None, - fd: 0, - buf: vec![0u8;(BUFSIZ + UNGET) as usize], - buf_char: -1, - unget: UNGET, - lock: AtomicBool::new(false), - }); + pub static ref default_stdin: GlobalFile = GlobalFile::new(0, constants::F_NOWR); #[allow(non_upper_case_globals)] - pub static ref default_stdout: GlobalFile = GlobalFile::new(FILE { - flags: constants::F_PERM | constants::F_NORD, - read: None, - write: None, - fd: 1, - buf: vec![0u8;(BUFSIZ + UNGET) as usize], - buf_char: b'\n' as i8, - unget: 0, - lock: AtomicBool::new(false), - }); + pub static ref default_stdout: GlobalFile = GlobalFile::new(1, constants::F_NORD); #[allow(non_upper_case_globals)] - pub static ref default_stderr: GlobalFile = GlobalFile::new(FILE { - flags: constants::F_PERM | constants::F_NORD, - read: None, - write: None, - fd: 2, - buf: vec![0u8;(BUFSIZ + UNGET) as usize], - buf_char: -1, - unget: 0, - lock: AtomicBool::new(false), - }); + pub static ref default_stderr: GlobalFile = GlobalFile::new(2, constants::F_NORD); } #[no_mangle] diff --git a/src/header/stdio/helpers.rs b/src/header/stdio/helpers.rs index 3d1629fdd4f65e12e17fc892b71c14208c3f5c7d..1544462f06f2772a468ec2f6b69bac18c1219ff2 100644 --- a/src/header/stdio/helpers.rs +++ b/src/header/stdio/helpers.rs @@ -1,14 +1,17 @@ +use alloc::boxed::Box; use core::sync::atomic::AtomicBool; use core::{mem, ptr}; +use fs::File; use header::errno; use header::fcntl::*; use header::string::strchr; -use platform; +use io::LineWriter; use platform::types::*; +use platform; use super::constants::*; -use super::{BUFSIZ, FILE, UNGET}; +use super::{Buffer, FILE}; /// Parse mode flags as a string and output a mode flags integer pub unsafe fn parse_mode_flags(mode_str: *const c_char) -> i32 { @@ -61,104 +64,18 @@ pub unsafe fn _fdopen(fd: c_int, mode: *const c_char) -> Option<*mut FILE> { flags |= F_APP; } - let f = platform::alloc(mem::size_of::<FILE>()) as *mut FILE; - // Allocate the file - if f.is_null() { - None - } else { - ptr::write( - f, - FILE { - flags: flags, - read: None, - write: None, - fd: fd, - buf: vec![0u8; BUFSIZ + UNGET], - buf_char: -1, - unget: UNGET, - lock: AtomicBool::new(false), - }, - ); - Some(f) - } -} - -/// Write buffer `buf` of length `l` into `stream` -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; - let mut advance = 0; - - if stream.write.is_none() && !stream.can_write() { - // We can't write to this stream - return 0; - } - if let Some((wbase, mut wpos, wend)) = stream.write { - if l > wend - wpos { - // We can't fit all of buf in the buffer - return stream.write(buf); - } - - let i = if stream.buf_char >= 0 { - let mut i = l; - while i > 0 && buf[i - 1] != b'\n' { - i -= 1; - } - if i > 0 { - let n = stream.write(buf); - match stream.write { - Some((_, new_wpos, _)) => wpos = new_wpos, - None => unreachable!("stream.write should never be None after a write call") - } - - if n < i { - return n; - } - advance += i; - l -= i; - } - i - } else { - 0 - }; + let file = File::new(fd); + let writer = LineWriter::new(file.get_ref()); - unsafe { - copy_nonoverlapping( - &buf[advance..] as *const _ as *const u8, - &mut stream.buf[wpos..] as *mut _ as *mut u8, - l, - ); - } - stream.write = Some((wbase, wpos + l, wend)); - l + i - } else { - 0 - } -} - -/// Flush `stream` without locking it. -pub fn fflush_unlocked(stream: &mut FILE) -> c_int { - if let Some((wbase, wpos, _)) = stream.write { - if wpos > wbase { - stream.write(&[]); - /* - if stream.wpos.is_null() { - return -1; - } - */ - } - } - - if let Some((rpos, rend)) = stream.read { - if rpos < rend { - stream.seek(rpos as i64 - rend as i64, SEEK_CUR); - } - } + Some(Box::into_raw(Box::new(FILE { + lock: AtomicBool::new(false), - stream.write = None; - stream.read = None; - 0 + file, + flags, + read_buf: Buffer::Owned(vec![0; BUFSIZ as usize]), + read_pos: 0, + read_size: 0, + unget: None, + writer + }))) } diff --git a/src/header/stdio/internal.rs b/src/header/stdio/internal.rs deleted file mode 100644 index 60ad4f3ebfd9b02a6220a45c4ddb82067ab9425e..0000000000000000000000000000000000000000 --- a/src/header/stdio/internal.rs +++ /dev/null @@ -1,31 +0,0 @@ -use super::{constants, FILE}; -use platform::types::*; - -pub fn ftello(stream: &mut FILE) -> off_t { - let pos = stream.seek( - 0, - 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 - }, - ); - if pos < 0 { - return pos; - } - 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/header/stdio/mod.rs b/src/header/stdio/mod.rs index 6a8867d727db16ffb138b1ddb58b27de34721442..8c20ba6e0d83403b42b358bace3a10832d6b1b9b 100644 --- a/src/header/stdio/mod.rs +++ b/src/header/stdio/mod.rs @@ -1,22 +1,26 @@ //! stdio implementation for Redox, following http://pubs.opengroup.org/onlinepubs/7908799/xsh/stdio.h.html +use alloc::borrow::{Borrow, BorrowMut}; +use alloc::boxed::Box; use alloc::vec::Vec; use core::fmt::Write as WriteFmt; use core::fmt::{self, Error}; use core::ops::{Deref, DerefMut}; use core::sync::atomic::{self, AtomicBool, Ordering}; -use core::{ptr, str}; +use core::{ptr, str, slice}; use va_list::VaList as va_list; use c_str::CStr; +use fs::File; use header::errno::{self, STR_ERROR}; use header::fcntl; use header::stdlib::mkstemp; use header::string::strlen; -use platform; +use io::{self, BufRead, LineWriter, SeekFrom, Read, Write}; use platform::types::*; -use platform::{errno, ReadByte, WriteByte}; use platform::{Pal, Sys}; +use platform::{errno, WriteByte}; +use platform; mod printf; mod scanf; @@ -29,158 +33,97 @@ mod constants; mod helpers; -mod internal; - -/// -/// This struct gets exposed to the C API. -/// -pub struct FILE { - flags: i32, - read: Option<(usize, usize)>, - write: Option<(usize, usize, usize)>, - fd: c_int, - buf: Vec<u8>, - buf_char: i8, - lock: AtomicBool, - unget: usize, +enum Buffer<'a> { + Borrowed(&'a mut [u8]), + Owned(Vec<u8>) } +impl<'a> Deref for Buffer<'a> { + type Target = [u8]; -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 let Some(_) = self.read { - return true; - } - if let Some(_) = self.write { - self.write(&[]); - } - self.write = None; - if self.flags & constants::F_NORD > 0 { - self.flags |= constants::F_ERR; - return false; - } - self.read = if self.buf.len() == 0 { - Some((0, 0)) - } else { - Some((self.buf.len() - 1, self.buf.len() - 1)) - }; - if self.flags & constants::F_EOF > 0 { - false - } else { - true + fn deref(&self) -> &Self::Target { + match self { + Buffer::Borrowed(inner) => inner, + Buffer::Owned(inner) => inner.borrow() } } - 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; +} +impl<'a> DerefMut for Buffer<'a> { + fn deref_mut(&mut self) -> &mut Self::Target { + match self { + Buffer::Borrowed(inner) => inner, + Buffer::Owned(inner) => inner.borrow_mut() } - */ + } +} - if self.flags & constants::F_NOWR > 0 { - self.flags &= constants::F_ERR; - return false; - } - // Buffer repositioning - if let Some(_) = self.write { - return true; - } - self.read = None; - self.write = if self.buf.len() == 0 { - Some((0, 0, 0)) - } else { - Some((self.unget, self.unget, self.buf.len() - 1)) - }; - return true; - } - - pub fn write(&mut self, to_write: &[u8]) -> usize { - 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 { - Sys::write(self.fd, &f_buf[advance..]) - } else { - Sys::write(self.fd, &f_buf[advance..]) + Sys::write(self.fd, to_write) - }; - if count == rem as isize { - self.write = if self.buf.len() == 0 { - Some((0, 0, 0)) - } else { - 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; +/// This struct gets exposed to the C API. +pub struct FILE { + // Can't use spin crate because *_unlocked functions are things in C :( + lock: AtomicBool, + + file: File, + flags: c_int, + read_buf: Buffer<'static>, + read_pos: usize, + read_size: usize, + unget: Option<u8>, + writer: LineWriter<File> +} + +impl Read for FILE { + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { + if !buf.is_empty() { + if let Some(c) = self.unget.take() { + buf[0] = c; + return Ok(1); } } - // 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 { - let adj = if self.buf.len() > 0 { 0 } else { 1 }; - let mut file_buf = &mut self.buf[self.unget..]; - let count = if buf.len() <= 1 - adj { - Sys::read(self.fd, &mut file_buf) - } else { - Sys::read(self.fd, buf) + Sys::read(self.fd, &mut 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; + + if self.read_pos == self.read_size { + self.fill_buf()?; } - // Adjust pointers - if file_buf.len() == 0 { - self.read = Some((0, 0)); - } else if buf.len() > 0 { - self.read = Some((self.unget + 1, self.unget + (count as usize))); - buf[buf.len() - 1] = file_buf[0]; - } else { - self.read = Some((self.unget, self.unget + (count as usize))); + + let len = buf.len().min(self.read_size); + buf[..len].copy_from_slice(&mut self.read_buf[..len]); + self.consume(len); + Ok(len) + } +} +impl BufRead for FILE { + fn fill_buf(&mut self) -> io::Result<&[u8]> { + if self.read_pos == self.read_size { + self.read_size = self.file.read(&mut self.read_buf)?; + self.read_pos = 0; } - buf.len() + Ok(&self.read_buf[self.read_pos..self.read_size]) } - - pub fn seek(&self, off: off_t, whence: c_int) -> off_t { - Sys::lseek(self.fd, off, whence) + fn consume(&mut self, i: usize) { + self.read_pos = (self.read_pos + i).min(self.read_size); } - +} +impl Write for FILE { + fn write(&mut self, buf: &[u8]) -> io::Result<usize> { + self.writer.write(buf) + } + fn flush(&mut self) -> io::Result<()> { + self.writer.flush() + } +} +impl WriteFmt for FILE { + fn write_str(&mut self, s: &str) -> fmt::Result { + self.write_all(s.as_bytes()) + .map(|_| ()) + .map_err(|_| fmt::Error) + } +} +impl WriteByte for FILE { + fn write_u8(&mut self, c: u8) -> fmt::Result { + self.write_all(&[c]) + .map(|_| ()) + .map_err(|_| fmt::Error) + } +} +impl FILE { pub fn lock(&mut self) -> LockGuard { flockfile(self); LockGuard(self) @@ -189,7 +132,7 @@ impl FILE { pub struct LockGuard<'a>(&'a mut FILE); impl<'a> Deref for LockGuard<'a> { - type Target = &'a mut FILE; + type Target = FILE; fn deref(&self) -> &Self::Target { &self.0 @@ -197,7 +140,7 @@ impl<'a> Deref for LockGuard<'a> { } impl<'a> DerefMut for LockGuard<'a> { fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 + self.0 } } impl<'a> Drop for LockGuard<'a> { @@ -206,50 +149,6 @@ impl<'a> Drop for LockGuard<'a> { } } -impl<'a> fmt::Write for LockGuard<'a> { - fn write_str(&mut self, s: &str) -> fmt::Result { - if !self.0.can_write() { - return Err(Error); - } - let s = s.as_bytes(); - if self.0.write(s) != s.len() { - Err(Error) - } else { - Ok(()) - } - } -} -impl<'a> WriteByte for LockGuard<'a> { - fn write_u8(&mut self, byte: u8) -> fmt::Result { - if !self.0.can_write() { - return Err(Error); - } - if self.0.write(&[byte]) != 1 { - Err(Error) - } else { - Ok(()) - } - } -} -impl<'a> ReadByte for LockGuard<'a> { - fn read_u8(&mut self) -> Result<Option<u8>, ()> { - let mut buf = [0]; - match self.0.read(&mut buf) { - 0 => Ok(None), - _ => Ok(Some(buf[0])), - } - } -} - -impl Drop for FILE { - fn drop(&mut self) { - // Flush - if let Some(_) = self.write { - self.write(&[]); - } - } -} - /// Clears EOF and ERR indicators on a stream #[no_mangle] pub extern "C" fn clearerr(stream: &mut FILE) { @@ -271,18 +170,24 @@ 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 extern "C" fn fclose(stream: &mut FILE) -> c_int { +pub extern "C" fn fclose(stream: *mut FILE) -> c_int { + let stream = unsafe { &mut *stream }; flockfile(stream); - let r = helpers::fflush_unlocked(stream) | Sys::close(stream.fd); + + let mut r = stream.flush().is_err(); + let close = Sys::close(*stream.file) < 0; + r = r || close; + if stream.flags & constants::F_PERM == 0 { // Not one of stdin, stdout or stderr - unsafe { - platform::free(stream as *mut FILE as *mut c_void); - } + let mut stream = unsafe { Box::from_raw(stream) }; + // Reference files aren't closed on drop, so pretend to be a reference + stream.file.reference = true; } else { funlockfile(stream); } - r + + r as c_int } /// Open a file from a file descriptor @@ -298,15 +203,15 @@ pub extern "C" fn fdopen(fildes: c_int, mode: *const c_char) -> *mut FILE { /// Check for EOF #[no_mangle] -pub extern "C" fn feof(stream: &mut FILE) -> c_int { - let stream = stream.lock(); +pub extern "C" fn feof(stream: *mut FILE) -> c_int { + let stream = unsafe { &mut *stream }.lock(); stream.flags & F_EOF } /// Check for ERR #[no_mangle] -pub extern "C" fn ferror(stream: &mut FILE) -> c_int { - let stream = stream.lock(); +pub extern "C" fn ferror(stream: *mut FILE) -> c_int { + let stream = unsafe { &mut *stream }.lock(); stream.flags & F_ERR } @@ -314,109 +219,116 @@ pub 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 { - let mut stream = stream.lock(); - helpers::fflush_unlocked(&mut stream) +pub unsafe extern "C" fn fflush(stream: *mut FILE) -> c_int { + unsafe { &mut *stream }.lock().flush().is_err() as c_int } /// Get a single char from a stream #[no_mangle] -pub extern "C" fn fgetc(stream: &mut FILE) -> c_int { - let mut stream = stream.lock(); - getc_unlocked(&mut stream) +pub extern "C" fn fgetc(stream: *mut FILE) -> c_int { + let mut stream = unsafe { &mut *stream }.lock(); + getc_unlocked(&mut *stream) } /// Get the position of the stream and store it in pos #[no_mangle] -pub extern "C" fn fgetpos(stream: &mut FILE, pos: Option<&mut fpos_t>) -> c_int { - let off = internal::ftello(stream); +pub extern "C" fn fgetpos(stream: *mut FILE, pos: *mut fpos_t) -> c_int { + let off = ftello(stream); if off < 0 { return -1; } - if let Some(pos) = pos { + unsafe { *pos = off; - 0 - } else { - -1 } + 0 } /// 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 core::slice; - let mut stream = stream.lock(); - let st = unsafe { slice::from_raw_parts_mut(s as *mut u8, n as usize) }; - - let mut len = n; +pub extern "C" fn fgets(original: *mut c_char, max: c_int, stream: *mut FILE) -> *mut c_char { + let mut stream = unsafe { &mut *stream }.lock(); + let mut out = original; + let max = max as usize; + let mut left = max.saturating_sub(1); // Make space for the terminating NUL-byte + let mut wrote = false; - // We can only fit one or less chars in - if n <= 1 { - if n <= 0 { - return ptr::null_mut(); - } - unsafe { - (*s) = b'\0' as i8; + if left >= 1 { + if let Some(c) = stream.unget.take() { + unsafe { + *out = c as c_char; + out = out.offset(1); + } + left -= 1; } - return s; } - // Scope this so we can reuse stream mutably - { - // We can't read from this stream - if !stream.can_read() { - return ptr::null_mut(); + + loop { + if left == 0 { + break; } - } - // TODO: Look at this later to determine correctness and efficiency - 'outer: while stream.read(&mut []) == 0 && stream.flags & F_ERR == 0 { - if let Some((rpos, rend)) = stream.read { - let mut idiff = 0usize; - for _ in (0..(len - 1) as usize).take_while(|x| rpos + x < rend) { - let pos = (n - len) as usize; - st[pos] = stream.buf[rpos + idiff]; - idiff += 1; - len -= 1; - if st[pos] == b'\n' || st[pos] as i8 == stream.buf_char { - stream.read = Some((rpos + idiff, rend)); - break 'outer; - } - } - stream.read = Some((rpos + idiff, rend)); - if rend - rpos == 0 { - match stream.read(&mut st[((n - len) as usize)..]) as i32 { - 0 if idiff == 0 => return ptr::null_mut(), - n => { - len -= n.max(0); - break; + // TODO: When NLL is a thing, this block can be flattened out + let (read, exit) = { + let mut buf = match stream.fill_buf() { + Ok(buf) => buf, + Err(err) => { + unsafe { + platform::errno = errno::EIO; } + return ptr::null_mut(); } - } - if len <= 1 { + }; + if buf.is_empty() { break; } + wrote = true; + let len = buf.len().min(left); + + let newline = buf[..len].iter().position(|&c| c == b'\n'); + let len = newline.map(|i| i + 1).unwrap_or(len); + + unsafe { + ptr::copy_nonoverlapping(buf.as_ptr(), out as *mut u8, len); + } + + (len, newline.is_some()) + }; + + stream.consume(read); + + out = unsafe { out.offset(read as isize) }; + left -= read; + + if exit { + break; } - // We can read, there's been no errors. We should have stream.read setbuf - // -- Tommoa (3/7/2018) - unreachable!() } - st[(n - len) as usize] = 0; - s + if max >= 1 { + // Write the NUL byte + unsafe { + *out = 0; + } + } + if wrote { + original + } else { + ptr::null_mut() + } } /// Get the underlying file descriptor #[no_mangle] -pub extern "C" fn fileno(stream: &mut FILE) -> c_int { - let stream = stream.lock(); - stream.fd +pub extern "C" fn fileno(stream: *mut FILE) -> c_int { + let stream = unsafe { &mut *stream }.lock(); + *stream.file } /// Lock the file /// Do not call any functions other than those with the `_unlocked` postfix while the file is /// locked #[no_mangle] -pub extern "C" fn flockfile(file: &mut FILE) { +pub extern "C" fn flockfile(file: *mut FILE) { while ftrylockfile(file) != 0 { atomic::spin_loop_hint(); } @@ -459,69 +371,34 @@ pub extern "C" fn fopen(filename: *const c_char, mode: *const c_char) -> *mut FI /// Insert a character into the stream #[no_mangle] -pub extern "C" fn fputc(c: c_int, stream: &mut FILE) -> c_int { - let mut stream = stream.lock(); - putc_unlocked(c, &mut stream) +pub extern "C" fn fputc(c: c_int, stream: *mut FILE) -> c_int { + let mut stream = unsafe { &mut *stream }.lock(); + putc_unlocked(c, &mut *stream) } /// Insert a string into a stream #[no_mangle] -pub 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 { 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 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; - - let mut stream = stream.lock(); - - if !stream.can_read() { - return 0; - } - - 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 }; +pub extern "C" fn fread(ptr: *mut c_void, size: size_t, count: size_t, stream: *mut FILE) -> size_t { + let mut stream = unsafe { &mut *stream }.lock(); + let buf = unsafe { slice::from_raw_parts_mut( + ptr as *mut u8, + size as usize * count as usize + ) }; + match stream.read(buf) { + Ok(bytes) => (bytes as usize / size as usize) as size_t, + Err(err) => { unsafe { - // Copy data - copy_nonoverlapping(&stream.buf[rpos..] as *const _ as *const u8, dest, k); - // Reposition pointers - dest = dest.add(k); + platform::errno = errno::EIO; } - stream.read = Some((rpos + k, rend)); - l -= k as isize; + 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 { - return (len - l as usize) / 2; - } - - l -= k as isize; - unsafe { - // Reposition - dest = dest.add(k); - } - } - - nitems - } else { - unreachable!() } } @@ -534,14 +411,14 @@ pub extern "C" fn freopen( let mut flags = unsafe { helpers::parse_mode_flags(mode) }; flockfile(stream); - helpers::fflush_unlocked(stream); + let _ = stream.flush(); 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.file, 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.file, fcntl::F_SETFL, flags) < 0 { funlockfile(stream); fclose(stream); return ptr::null_mut(); @@ -554,10 +431,10 @@ pub extern "C" fn freopen( return ptr::null_mut(); } let new = unsafe { &mut *new }; // Should be safe, new is not null - if new.fd == stream.fd { - new.fd = -1; - } else if Sys::dup2(new.fd, stream.fd) < 0 - || fcntl::sys_fcntl(stream.fd, fcntl::F_SETFL, flags & fcntl::O_CLOEXEC) < 0 + if *new.file == *stream.file { + new.file.fd = -1; + } else if Sys::dup2(*new.file, *stream.file) < 0 + || fcntl::sys_fcntl(*stream.file, fcntl::F_SETFL, flags & fcntl::O_CLOEXEC) < 0 { funlockfile(stream); fclose(new); @@ -573,99 +450,93 @@ pub extern "C" fn freopen( /// Seek to an offset `offset` from `whence` #[no_mangle] -pub extern "C" fn fseek(stream: &mut FILE, offset: c_long, whence: c_int) -> c_int { - if fseeko(stream, offset as off_t, whence) != -1 { - return 0; - } - -1 +pub extern "C" fn fseek(stream: *mut FILE, offset: c_long, whence: c_int) -> c_int { + fseeko(stream, offset as off_t, whence) } /// Seek to an offset `offset` from `whence` #[no_mangle] -pub extern "C" fn fseeko(stream: &mut FILE, offset: off_t, whence: c_int) -> c_int { - let mut off = offset; - let mut stream = stream.lock(); +pub extern "C" fn fseeko(stream: *mut FILE, mut off: off_t, whence: c_int) -> c_int { + let mut stream = unsafe { &mut *stream }.lock(); - // 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 -= (rdiff) as i64; + // Since it's a buffered writer, our actual cursor isn't where the user + // thinks + off -= (stream.read_size - stream.read_pos) as off_t; } - if let Some(_) = stream.write { - stream.write(&[]); - } - stream.write = None; - if stream.seek(off, whence) < 0 { - return -1; + + let err = Sys::lseek(*stream.file, off, whence); + if err < 0 { + return err as c_int; } - stream.read = None; + stream.flags &= !F_EOF; + stream.read_pos = 0; + stream.read_size = 0; + stream.unget = None; 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: Option<&fpos_t>) -> c_int { - fseek( - stream, - *pos.expect("You must specify a valid position"), - SEEK_SET, - ) +pub unsafe extern "C" fn fsetpos(stream: *mut FILE, pos: *const fpos_t) -> c_int { + fseek(stream, *pos, SEEK_SET) } /// Get the current position of the cursor in the file #[no_mangle] -pub extern "C" fn ftell(stream: &mut FILE) -> c_long { +pub extern "C" fn ftell(stream: *mut FILE) -> c_long { ftello(stream) as c_long } /// Get the current position of the cursor in the file #[no_mangle] -pub extern "C" fn ftello(stream: &mut FILE) -> off_t { - let mut stream = stream.lock(); - internal::ftello(&mut stream) +pub extern "C" fn ftello(stream: *mut FILE) -> off_t { + let mut stream = unsafe { &mut *stream }.lock(); + let pos = Sys::lseek(*stream.file, 0, SEEK_CUR); + if pos < 0 { + return -1; + } + + pos - (stream.read_size - stream.read_pos) as off_t } /// Try to lock the file. Returns 0 for success, 1 for failure #[no_mangle] -pub 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 { + unsafe { &mut *file }.lock.compare_and_swap(false, true, Ordering::Acquire) as c_int } /// Unlock the file #[no_mangle] -pub extern "C" fn funlockfile(file: &mut FILE) { - file.lock.store(false, Ordering::Release); +pub extern "C" fn funlockfile(file: *mut FILE) { + unsafe { &mut *file }.lock.store(false, Ordering::Release); } /// Write `nitems` of size `size` from `ptr` to `stream` #[no_mangle] -pub extern "C" fn fwrite( - ptr: *const c_void, - size: usize, - nitems: usize, - stream: &mut FILE, -) -> usize { - let l = size * nitems; - let nitems = if size == 0 { 0 } else { nitems }; - let mut stream = stream.lock(); - let k = helpers::fwritex(ptr as *const u8, l, &mut stream); - if k == l { - nitems - } else { - k / size +pub extern "C" fn fwrite(ptr: *const c_void, size: usize, count: usize, stream: *mut FILE) -> usize { + let mut stream = unsafe { &mut *stream }.lock(); + let buf = unsafe { slice::from_raw_parts_mut( + ptr as *mut u8, + size as usize * count as usize + ) }; + match stream.write(buf) { + Ok(bytes) => (bytes as usize / size as usize) as size_t, + Err(err) => { + unsafe { + platform::errno = errno::EIO; + } + 0 + } } } /// Get a single char from a stream #[no_mangle] -pub extern "C" fn getc(stream: &mut FILE) -> c_int { - let mut stream = stream.lock(); - getc_unlocked(&mut stream) +pub extern "C" fn getc(stream: *mut FILE) -> c_int { + let mut stream = unsafe { &mut *stream }.lock(); + getc_unlocked(&mut *stream) } /// Get a single char from `stdin` @@ -676,28 +547,16 @@ pub extern "C" fn getchar() -> c_int { /// 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.can_read() { - return -1; - } - if let Some((rpos, rend)) = stream.read { - if rpos < rend { - let ret = stream.buf[rpos] as c_int; - stream.read = Some((rpos + 1, rend)); - ret - } else { - let mut c = [0u8; 1]; - if stream.read(&mut c) == 1 { - c[0] as c_int - } else { - -1 - } +pub extern "C" fn getc_unlocked(stream: *mut FILE) -> c_int { + let mut buf = [0]; + + match unsafe { &mut *stream }.read(&mut buf) { + Ok(0) => EOF, + Ok(_) => buf[0] as c_int, + Err(err) => unsafe { + platform::errno = errno::EIO; + EOF } - } 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!() } } @@ -710,20 +569,19 @@ pub extern "C" fn getchar_unlocked() -> c_int { /// Get a string from `stdin` #[no_mangle] pub extern "C" fn gets(s: *mut c_char) -> *mut c_char { - use core::i32; - fgets(s, i32::MAX, unsafe { &mut *stdin }) + fgets(s, c_int::max_value(), unsafe { &mut *stdin }) } /// Get an integer from `stream` #[no_mangle] -pub 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( - &mut ret as *mut c_int as *mut c_void, + &mut ret as *mut _ as *mut c_void, mem::size_of_val(&ret), 1, - stream, + stream ) > 0 { ret @@ -733,7 +591,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!(); } @@ -759,9 +617,9 @@ pub extern "C" fn popen(_command: *const c_char, _mode: *const c_char) -> *mut F /// Put a character `c` into `stream` #[no_mangle] -pub extern "C" fn putc(c: c_int, stream: &mut FILE) -> c_int { - let mut stream = stream.lock(); - putc_unlocked(c, &mut stream) +pub extern "C" fn putc(c: c_int, stream: *mut FILE) -> c_int { + let mut stream = unsafe { &mut *stream }.lock(); + putc_unlocked(c, &mut *stream) } /// Put a character `c` into `stdout` @@ -772,36 +630,27 @@ pub extern "C" fn putchar(c: c_int) -> c_int { /// 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 stream.can_write() { - if let Some((wbase, wpos, wend)) = stream.write { - if c as i8 != stream.buf_char { - stream.buf[wpos] = c as u8; - stream.write = Some((wbase, wpos + 1, wend)); - c - } else if stream.write(&[c as u8]) == 1 { - c - } else { - -1 - } - } else { - -1 +pub extern "C" fn putc_unlocked(c: c_int, stream: *mut FILE) -> c_int { + match unsafe { &mut *stream }.write(&[c as u8]) { + Ok(0) => EOF, + Ok(_) => c, + Err(_) => unsafe { + platform::errno = errno::EIO; + EOF } - } else { - -1 } } /// Put a character `c` into `stdout` without locking `stdout` #[no_mangle] pub extern "C" fn putchar_unlocked(c: c_int) -> c_int { - putc_unlocked(c, unsafe { &mut *stdout }) + putc_unlocked(c, unsafe { stdout }) } /// Put a string `s` into `stdout` #[no_mangle] 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); + let ret = (fputs(s, unsafe { stdout }) > 0) || (putchar_unlocked(b'\n' as c_int) > 0); if ret { 0 } else { @@ -811,9 +660,9 @@ pub extern "C" fn puts(s: *const c_char) -> c_int { /// Put an integer `w` into `stream` #[no_mangle] -pub 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 + fwrite(&w as *const c_int as _, mem::size_of_val(&w), 1, stream) as i32 - 1 } /// Delete file or directory `path` @@ -837,15 +686,13 @@ pub extern "C" fn rename(oldpath: *const c_char, newpath: *const c_char) -> c_in /// Rewind `stream` back to the beginning of it #[no_mangle] -pub extern "C" fn rewind(stream: &mut FILE) { +pub extern "C" fn rewind(stream: *mut FILE) { fseeko(stream, 0, SEEK_SET); - let mut stream = stream.lock(); - stream.flags &= !F_ERR; } /// 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) { setvbuf( stream, buf, @@ -857,31 +704,27 @@ 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 extern "C" fn setvbuf(stream: &mut FILE, buf: *mut c_char, mode: c_int, size: usize) -> c_int { +pub extern "C" fn setvbuf(stream: *mut FILE, buf: *mut c_char, mode: c_int, mut size: size_t) -> c_int { + let mut stream = unsafe { &mut *stream }.lock(); // Set a buffer of size `size` if no buffer is given - let buf = if buf.is_null() { - if mode != _IONBF { - vec![0u8; 1] - } else { - stream.unget = 0; - if let Some(_) = stream.write { - stream.write = Some((0, 0, 0)); - } else if let Some(_) = stream.read { - stream.read = Some((0, 0)); - } - Vec::new() - } + stream.read_buf = if buf.is_null() || size == 0 { + if size == 0 { + size = BUFSIZ as usize; + } + // TODO: Make it unbuffered if _IONBF + // if mode == _IONBF { + // } else { + Buffer::Owned(vec![0; size as usize]) + // } } else { - // We trust the user on this one - // -- Tommoa (20/6/2018) - unsafe { Vec::from_raw_parts(buf as *mut u8, size, size) } + unsafe { + Buffer::Borrowed(slice::from_raw_parts_mut( + buf as *mut u8, + size + )) + } }; - stream.buf_char = -1; - if mode == _IOLBF { - stream.buf_char = b'\n' as i8; - } stream.flags |= F_SVB; - stream.buf = buf; 0 } @@ -920,32 +763,22 @@ 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 extern "C" fn ungetc(c: c_int, stream: &mut FILE) -> c_int { - if c < 0 { - c - } else { - let mut stream = stream.lock(); - - if stream.read.is_none() { - stream.can_read(); - } - if let Some((rpos, rend)) = stream.read { - if rpos == 0 { - return -1; - } - stream.read = Some((rpos - 1, rend)); - stream.buf[rpos - 1] = c as u8; - stream.flags &= !F_EOF; - c - } else { - -1 +pub extern "C" fn ungetc(c: c_int, stream: *mut FILE) -> c_int { + let mut stream = unsafe { &mut *stream }.lock(); + if stream.unget.is_some() { + unsafe { + platform::errno = errno::EIO; + return EOF; } } + stream.unget = Some(c as u8); + c } #[no_mangle] -pub unsafe extern "C" fn vfprintf(file: &mut FILE, format: *const c_char, ap: va_list) -> c_int { - printf::printf(file.lock(), format, ap) +pub unsafe extern "C" fn vfprintf(file: *mut FILE, format: *const c_char, ap: va_list) -> c_int { + let mut file = (*file).lock(); + printf::printf(&mut *file, format, ap) } #[no_mangle] @@ -973,8 +806,9 @@ pub unsafe extern "C" fn vsprintf(s: *mut c_char, format: *const c_char, ap: va_ } #[no_mangle] -pub unsafe extern "C" fn vfscanf(file: &mut FILE, format: *const c_char, ap: va_list) -> c_int { - scanf::scanf(file.lock(), format, ap) +pub unsafe extern "C" fn vfscanf(file: *mut FILE, format: *const c_char, ap: va_list) -> c_int { + let mut file = (*file).lock(); + scanf::scanf(&mut *file, format, ap) } #[no_mangle] diff --git a/src/header/stdio/scanf.rs b/src/header/stdio/scanf.rs index 97864af620e7cf3c9816cbb5ef708fe81f1f85ea..c92ff7721efc56a97e4b89a5d89d4ba785151e10 100644 --- a/src/header/stdio/scanf.rs +++ b/src/header/stdio/scanf.rs @@ -1,7 +1,7 @@ use alloc::String; use alloc::Vec; +use io::Read; use platform::types::*; -use platform::ReadByte; use va_list::VaList; #[derive(PartialEq, Eq)] @@ -27,7 +27,7 @@ unsafe fn next_byte(string: &mut *const c_char) -> Result<u8, c_int> { } } -unsafe fn inner_scanf<R: ReadByte>( +unsafe fn inner_scanf<R: Read>( mut r: R, mut format: *const c_char, mut ap: VaList, @@ -39,14 +39,15 @@ unsafe fn inner_scanf<R: ReadByte>( macro_rules! read { () => {{ - match r.read_u8() { - Ok(Some(b)) => { - byte = b; + let mut buf = &mut [byte]; + match r.read(buf) { + Ok(0) => false, + Ok(_) => { + byte = buf[0]; count += 1; true } - Ok(None) => false, - Err(()) => return Err(-1), + Err(_) => return Err(-1), } }}; } @@ -420,7 +421,7 @@ unsafe fn inner_scanf<R: ReadByte>( } Ok(matched) } -pub unsafe fn scanf<R: ReadByte>(r: R, format: *const c_char, ap: VaList) -> c_int { +pub unsafe fn scanf<R: Read>(r: R, format: *const c_char, ap: VaList) -> c_int { match inner_scanf(r, format, ap) { Ok(n) => n, Err(n) => n, diff --git a/src/platform/linux/mod.rs b/src/platform/linux/mod.rs index 08a6f4d1dc50097d2ec99fb0c0fb911e18fae335..5b3d8957335a5d1199dd1b72342ff0db1183fdde 100644 --- a/src/platform/linux/mod.rs +++ b/src/platform/linux/mod.rs @@ -194,32 +194,31 @@ impl Pal for Sys { e(unsafe { syscall!(GETGID) }) as gid_t } - unsafe fn gethostname(mut name: *mut c_char, len: size_t) -> c_int { - // len only needs to be mutable on linux - let mut len = len; - - let mut uts = mem::uninitialized(); - let err = Sys::uname(&mut uts); - if err < 0 { - mem::forget(uts); - return err; - } - for c in uts.nodename.iter() { - if len == 0 { - break; + fn gethostname(mut name: *mut c_char, mut len: size_t) -> c_int { + unsafe { + let mut uts = mem::uninitialized(); + let err = Sys::uname(&mut uts); + if err < 0 { + mem::forget(uts); + return err; } - len -= 1; + for c in uts.nodename.iter() { + if len == 0 { + break; + } + len -= 1; - *name = *c; + *name = *c; - if *name == 0 { - // We do want to copy the zero also, so we check this after the copying. - break; - } + if *name == 0 { + // We do want to copy the zero also, so we check this after the copying. + break; + } - name = name.offset(1); + name = name.offset(1); + } + 0 } - 0 } fn getpgid(pid: pid_t) -> pid_t { diff --git a/src/platform/mod.rs b/src/platform/mod.rs index e492bc6338a900586da913e823e3ae4f501de243..ca964759ab7038c05a8f43d5e968b5db80625264 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -1,5 +1,6 @@ use alloc::vec::Vec; use core::{fmt, ptr}; +use io::{self, Read}; pub use self::allocator::*; @@ -70,16 +71,6 @@ impl<'a, W: WriteByte> WriteByte for &'a mut W { } } -pub trait ReadByte { - fn read_u8(&mut self) -> Result<Option<u8>, ()>; -} - -impl<'a, R: ReadByte> ReadByte for &'a mut R { - fn read_u8(&mut self) -> Result<Option<u8>, ()> { - (**self).read_u8() - } -} - pub struct FileWriter(pub c_int); impl FileWriter { @@ -110,13 +101,13 @@ impl FileReader { } } -impl ReadByte for FileReader { - fn read_u8(&mut self) -> Result<Option<u8>, ()> { - let mut buf = [0]; - match self.read(&mut buf) { - 0 => Ok(None), - n if n < 0 => Err(()), - _ => Ok(Some(buf[0])), +impl Read for FileReader { + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { + let i = Sys::read(self.0, buf); + if i >= 0 { + Ok(i as usize) + } else { + Err(io::Error::from_raw_os_error(-i as i32)) } } } @@ -182,32 +173,20 @@ impl WriteByte for UnsafeStringWriter { } } -pub struct StringReader<'a>(pub &'a [u8]); - -impl<'a> ReadByte for StringReader<'a> { - fn read_u8(&mut self) -> Result<Option<u8>, ()> { - if self.0.is_empty() { - Ok(None) - } else { - let byte = self.0[0]; - self.0 = &self.0[1..]; - Ok(Some(byte)) - } - } -} - pub struct UnsafeStringReader(pub *const u8); -impl ReadByte for UnsafeStringReader { - fn read_u8(&mut self) -> Result<Option<u8>, ()> { +impl Read for UnsafeStringReader { + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { unsafe { - if *self.0 == 0 { - Ok(None) - } else { - let byte = *self.0; + for i in 0..buf.len() { + if *self.0 == 0 { + return Ok(i); + } + + buf[i] = *self.0; self.0 = self.0.offset(1); - Ok(Some(byte)) } + Ok(buf.len()) } } } diff --git a/src/platform/pal/mod.rs b/src/platform/pal/mod.rs index 9a3a2030dc193ef37d48060b63cbd379b9f816ce..bd18cbabe1f8af8c066de3488eab114e632ba29c 100644 --- a/src/platform/pal/mod.rs +++ b/src/platform/pal/mod.rs @@ -71,7 +71,7 @@ pub trait Pal { fn getgid() -> gid_t; - unsafe fn gethostname(name: *mut c_char, len: size_t) -> c_int; + fn gethostname(name: *mut c_char, len: size_t) -> c_int; fn getpgid(pid: pid_t) -> pid_t; diff --git a/src/platform/redox/mod.rs b/src/platform/redox/mod.rs index 59a5edcecf414835fead726ecb5be0a321f5cce7..42cb72b974d2a1c49b20a4c54aa376bf48a19824 100644 --- a/src/platform/redox/mod.rs +++ b/src/platform/redox/mod.rs @@ -11,8 +11,10 @@ use syscall::flag::*; use syscall::{self, Result}; use c_str::{CStr, CString}; +use fs::File; +use io; use header::dirent::dirent; -use header::errno::{EINVAL, ENOSYS}; +use header::errno::{EIO, EINVAL, ENOSYS}; use header::fcntl; const MAP_ANON: c_int = 1; //use header::sys_mman::MAP_ANON; @@ -423,31 +425,30 @@ impl Pal for Sys { e(syscall::getgid()) as gid_t } - unsafe fn gethostname(mut name: *mut c_char, len: size_t) -> c_int { - let fd = match RawFile::open( - &CString::new("/etc/hostname").unwrap(), - fcntl::O_RDONLY | fcntl::O_CLOEXEC, - 0, - ) { - Ok(fd) => fd, - Err(_) => return -1, - }; + fn gethostname(name: *mut c_char, len: size_t) -> c_int { + fn inner(name: &mut [u8]) -> io::Result<()> { + let mut file = File::open( + &CString::new("/etc/hostname").unwrap(), + fcntl::O_RDONLY | fcntl::O_CLOEXEC + )?; - let mut reader = FileReader(*fd); - for _ in 0..len { - match reader.read_u8() { - Ok(Some(b)) => { - *name = b as c_char; - name = name.offset(1); - } - Ok(None) => { - *name = 0; - break; + let mut read = 0; + loop { + match file.read(&mut name[read..])? { + 0 => break, + n => read += n } - Err(()) => return -1, + } + Ok(()) + } + + match inner(unsafe { slice::from_raw_parts_mut(name as *mut u8, len as usize) }) { + Ok(()) => 0, + Err(_) => unsafe { + errno = EIO; + -1 } } - 0 } fn getpgid(pid: pid_t) -> pid_t { diff --git a/tests/Makefile b/tests/Makefile index ab914b7798a27b164e823cc06924171fd5d50977..34bc799f3d5b1a3bd804bc3246aa7d78589c151d 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -16,7 +16,9 @@ EXPECT_BINS=\ signal \ stdio/all \ stdio/buffer \ + stdio/fgets \ stdio/freopen \ + stdio/fseek \ stdio/fwrite \ stdio/getc_unget \ stdio/printf \ diff --git a/tests/expected/stdio/buffer.stderr b/tests/expected/stdio/buffer.stderr new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/expected/stdio/buffer.stdout b/tests/expected/stdio/buffer.stdout new file mode 100644 index 0000000000000000000000000000000000000000..4b124bc7c01363ec010f1d727b8fb106838b6883 --- /dev/null +++ b/tests/expected/stdio/buffer.stdout @@ -0,0 +1,5 @@ +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaTest +Hello +World +It works +No buffering issues here diff --git a/tests/expected/stdio/fgets.stderr b/tests/expected/stdio/fgets.stderr new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/expected/stdio/fgets.stdout b/tests/expected/stdio/fgets.stdout new file mode 100644 index 0000000000000000000000000000000000000000..a856e2de524d031649f2f5c84fdc43cbd6decbc8 --- /dev/null +++ b/tests/expected/stdio/fgets.stdout @@ -0,0 +1,31 @@ +Hello World! + +Line 2 + +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb +ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc +ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggg +hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh +iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii +jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj +kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk +lllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllll +mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm +nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn +ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo +ppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp +qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq +rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr +sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss +ttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt +uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu +vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy +zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz +EOF diff --git a/tests/expected/stdio/fseek.stderr b/tests/expected/stdio/fseek.stderr new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/expected/stdio/fseek.stdout b/tests/expected/stdio/fseek.stdout new file mode 100644 index 0000000000000000000000000000000000000000..574d89ae9e30f294a172cf0192722281134d4c12 --- /dev/null +++ b/tests/expected/stdio/fseek.stdout @@ -0,0 +1,2 @@ +Line 2 +ftell: 21 diff --git a/tests/expected/stdio/setvbuf.stdout b/tests/expected/stdio/setvbuf.stdout index 7f04b787f7dc9dab3d29ca26572878533525395d..ebda98d01a2738e3b1f5ea75d3163815195255e3 100644 --- a/tests/expected/stdio/setvbuf.stdout +++ b/tests/expected/stdio/setvbuf.stdout @@ -1,6 +1,4 @@ H -ello World! - -Line 2 +Hello World! Hello diff --git a/tests/stdio/fgets.c b/tests/stdio/fgets.c new file mode 100644 index 0000000000000000000000000000000000000000..34877e9089821a5e3f987f2dcde113fc203c5634 --- /dev/null +++ b/tests/stdio/fgets.c @@ -0,0 +1,16 @@ +#include <stdio.h> + +int main() { + //FILE *f = fopen("/etc/ssl/certs/ca-certificates.crt", "r"); + FILE *f = fopen("stdio/stdio.in", "r"); + char line[256]; + + while (1) { + if (fgets(line, 256, f)) { + fputs(line, stdout); + } else { + puts("EOF"); + break; + } + } +} diff --git a/tests/stdio/fseek.c b/tests/stdio/fseek.c new file mode 100644 index 0000000000000000000000000000000000000000..4bed8733c5efa84db9916a163304a6e09356d377 --- /dev/null +++ b/tests/stdio/fseek.c @@ -0,0 +1,12 @@ +#include <stdio.h> + +int main() { + FILE *f = fopen("stdio/stdio.in", "r"); + if (fseek(f, 14, SEEK_CUR) < 0) { + puts("fseek error"); + return 1; + } + char buffer[256]; + printf("%s", fgets(buffer, 256, f)); + printf("ftell: %d\n", ftello(f)); +} diff --git a/tests/stdio/stdio.in b/tests/stdio/stdio.in index c50d87bb17331bbf91d4b38a48453dc1aee539f1..e4d49013909e51e9cdf5b076bd7c170d11510d38 100644 --- a/tests/stdio/stdio.in +++ b/tests/stdio/stdio.in @@ -1,3 +1,30 @@ Hello World! Line 2 + +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb +ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc +ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd +eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee +fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggg +hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh +iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii +jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj +kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk +lllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllll +mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm +nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn +ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo +ppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp +qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq +rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr +sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss +ttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt +uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu +vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy +zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz