Most of <stdio.h>
This commit is contained in:
+2
-1
@@ -9,6 +9,7 @@ crate-type = ["staticlib"]
|
||||
[dependencies]
|
||||
yggdrasil-rt = { git = "https://git.alnyan.me/yggdrasil/yggdrasil-rt.git" }
|
||||
libyalloc = { git = "https://git.alnyan.me/yggdrasil/libyalloc.git" }
|
||||
bitflags = { version = "2.4.1" }
|
||||
|
||||
[build-dependencies]
|
||||
cbindgen = "0.26.0"
|
||||
cbindgen = { git = "https://git.alnyan.me/yggdrasil/cbindgen.git", branch = "master" }
|
||||
|
||||
@@ -3,5 +3,12 @@
|
||||
|
||||
int printf(const char *format, ...);
|
||||
int fprintf(FILE *stream, const char *format, ...);
|
||||
int sprintf(FILE *stream, const char *format, ...);
|
||||
int snprintf(FILE *stream, size_t len, const char *format, ...);
|
||||
int dprintf(int fd, const char *format, ...);
|
||||
|
||||
int scanf(const char *format, ...);
|
||||
int sscanf(const char *str, const char *format, ...);
|
||||
int fscanf(FILE *stream, const char *format, ...);
|
||||
|
||||
#endif
|
||||
|
||||
+13
-1
@@ -4,16 +4,21 @@ use core::{
|
||||
ops::{ControlFlow, FromResidual, Try},
|
||||
};
|
||||
|
||||
use crate::header::errno::Errno;
|
||||
use crate::header::{errno::Errno, stdio::EOF};
|
||||
|
||||
pub trait CZeroResult {
|
||||
fn into_zero_status(self) -> c_int;
|
||||
fn into_eof_status(self) -> c_int;
|
||||
}
|
||||
|
||||
pub trait CSizeResult {
|
||||
fn into_size_status(self) -> usize;
|
||||
}
|
||||
|
||||
pub trait TryFromExt<T>: Sized {
|
||||
fn e_try_from(value: T) -> EResult<Self>;
|
||||
}
|
||||
|
||||
pub trait OptionExt<T> {
|
||||
fn e_ok_or(self, err: yggdrasil_rt::Error) -> EResult<T>;
|
||||
}
|
||||
@@ -60,6 +65,13 @@ impl CZeroResult for Result<(), Errno> {
|
||||
Self::Err(_) => -1,
|
||||
}
|
||||
}
|
||||
|
||||
fn into_eof_status(self) -> c_int {
|
||||
match self {
|
||||
Self::Ok(_) => 0,
|
||||
Self::Err(_) => EOF,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CSizeResult for Result<usize, Errno> {
|
||||
|
||||
+138
-2
@@ -1,3 +1,139 @@
|
||||
pub trait FileImpl {}
|
||||
use yggdrasil_rt::{
|
||||
io::{FileMode, OpenOptions, RawFd, SeekFrom},
|
||||
path::Path,
|
||||
sys as syscall,
|
||||
};
|
||||
|
||||
pub struct File {}
|
||||
use crate::{
|
||||
error::EResult,
|
||||
header::errno::Errno,
|
||||
io::{Read, Seek, Write},
|
||||
};
|
||||
|
||||
pub enum FileBacking {
|
||||
File(File),
|
||||
}
|
||||
|
||||
pub struct File {
|
||||
fd: RawFd,
|
||||
reference: bool,
|
||||
}
|
||||
|
||||
impl File {
|
||||
pub unsafe fn new(fd: RawFd) -> Self {
|
||||
Self {
|
||||
fd,
|
||||
reference: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_raw(fd: RawFd) -> Self {
|
||||
Self {
|
||||
fd,
|
||||
reference: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn open_at<P: AsRef<Path>>(
|
||||
at: Option<RawFd>,
|
||||
pathname: P,
|
||||
opts: OpenOptions,
|
||||
mode: FileMode,
|
||||
) -> Result<Self, Errno> {
|
||||
let fd =
|
||||
EResult::from(unsafe { syscall::open(at, pathname.as_ref().as_str(), opts, mode) })?;
|
||||
Ok(Self {
|
||||
fd,
|
||||
reference: false,
|
||||
})
|
||||
}
|
||||
|
||||
pub unsafe fn close(&self) -> Result<(), Errno> {
|
||||
yggdrasil_rt::debug_trace!("Close fd {:?}", self.fd);
|
||||
EResult::from(unsafe { syscall::close(self.fd) })?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for File {
|
||||
fn write(&mut self, data: &[u8]) -> Result<usize, Errno> {
|
||||
let count = EResult::from(unsafe { syscall::write(self.fd, data) })?;
|
||||
Ok(count)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> Result<(), Errno> {
|
||||
// TODO fsync
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for File {
|
||||
fn read(&mut self, data: &mut [u8]) -> Result<usize, Errno> {
|
||||
let count = EResult::from(unsafe { syscall::read(self.fd, data) })?;
|
||||
Ok(count)
|
||||
}
|
||||
}
|
||||
|
||||
impl Seek for File {
|
||||
fn seek(&mut self, off: SeekFrom) -> Result<u64, Errno> {
|
||||
let pos = EResult::from(unsafe { syscall::seek(self.fd, off) })?;
|
||||
Ok(pos)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for File {
|
||||
fn drop(&mut self) {
|
||||
if !self.reference {
|
||||
unsafe {
|
||||
self.close().ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FileBacking {
|
||||
pub unsafe fn make_ref(&self) -> Self {
|
||||
match self {
|
||||
Self::File(file) => Self::File(File {
|
||||
fd: file.fd,
|
||||
reference: true,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_raw_fd(&self) -> EResult<RawFd> {
|
||||
match self {
|
||||
Self::File(file) => EResult::Ok(file.fd),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for FileBacking {
|
||||
fn write(&mut self, data: &[u8]) -> Result<usize, Errno> {
|
||||
match self {
|
||||
Self::File(file) => file.write(data),
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> Result<(), Errno> {
|
||||
match self {
|
||||
Self::File(file) => file.flush(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for FileBacking {
|
||||
fn read(&mut self, data: &mut [u8]) -> Result<usize, Errno> {
|
||||
match self {
|
||||
Self::File(file) => file.read(data),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Seek for FileBacking {
|
||||
fn seek(&mut self, off: SeekFrom) -> Result<u64, Errno> {
|
||||
match self {
|
||||
Self::File(file) => file.seek(off),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,8 @@ no_includes = true
|
||||
include_guard = "_LOCALE_H"
|
||||
trailer = "#include <bits/locale.h>"
|
||||
|
||||
usize_is_size_t = true
|
||||
usize_type = "size_t"
|
||||
isize_type = "ssize_t"
|
||||
|
||||
[export]
|
||||
include = ["lconv", "locale_t"]
|
||||
|
||||
@@ -7,6 +7,7 @@ no_includes = true
|
||||
include_guard = "_MATH_H"
|
||||
trailer = "#include <bits/math.h>"
|
||||
|
||||
usize_is_size_t = true
|
||||
usize_type = "size_t"
|
||||
isize_type = "ssize_t"
|
||||
|
||||
[export]
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
#![allow(nonstandard_style)]
|
||||
#![allow(nonstandard_style, unused_variables)]
|
||||
|
||||
pub mod sys_types;
|
||||
pub mod sys_wait;
|
||||
|
||||
@@ -1,14 +1,25 @@
|
||||
language = "C"
|
||||
style = "Type"
|
||||
|
||||
sys_includes = ["stdarg.h", "stddef.h", "stdint.h"]
|
||||
sys_includes = ["stdarg.h", "stddef.h", "stdint.h", "sys/types.h"]
|
||||
no_includes = true
|
||||
|
||||
include_guard = "_STDIO_H"
|
||||
trailer = "#include <bits/stdio.h>"
|
||||
|
||||
usize_is_size_t = true
|
||||
usize_type = "size_t"
|
||||
isize_type = "ssize_t"
|
||||
|
||||
[export]
|
||||
include = ["fpos_t"]
|
||||
# Varargs are broken for these
|
||||
exclude = ["printf", "fprintf"]
|
||||
exclude = [
|
||||
"printf",
|
||||
"fprintf",
|
||||
"sprintf",
|
||||
"snprintf",
|
||||
"dprintf",
|
||||
"scanf",
|
||||
"fscanf",
|
||||
"sscanf"
|
||||
]
|
||||
|
||||
+193
-7
@@ -1,15 +1,101 @@
|
||||
use core::ffi::{c_char, c_void};
|
||||
|
||||
use crate::{
|
||||
error::CSizeResult,
|
||||
traits::{Read, Write},
|
||||
use core::{
|
||||
ffi::{c_char, c_int, c_long, c_void, CStr},
|
||||
ptr::null_mut,
|
||||
};
|
||||
|
||||
use super::FILE;
|
||||
use alloc::boxed::Box;
|
||||
use yggdrasil_rt::{
|
||||
io::{OpenOptions, RawFd, SeekFrom},
|
||||
path::Path,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
error::{CSizeResult, CZeroResult},
|
||||
header::{errno::Errno, sys_types::off_t},
|
||||
io::{Read, Seek, Write},
|
||||
};
|
||||
|
||||
use super::{
|
||||
fpos_t, FileFlags, FileOpenSource, FILE, SEEK_CUR, SEEK_END, SEEK_SET, _IOFBF, _IOLBF, _IONBF,
|
||||
};
|
||||
|
||||
fn open_inner<O: FileOpenSource>(source: O, mode_str: &[u8]) -> Result<*mut FILE, Errno> {
|
||||
let opts = match mode_str {
|
||||
b"r" | b"rb" => OpenOptions::READ,
|
||||
b"r+" | b"rb+" => OpenOptions::READ | OpenOptions::WRITE,
|
||||
b"w" | b"wb" => OpenOptions::TRUNCATE | OpenOptions::WRITE,
|
||||
b"w+" | b"wb+" => OpenOptions::TRUNCATE | OpenOptions::READ | OpenOptions::WRITE,
|
||||
b"a" | b"ab" => OpenOptions::APPEND | OpenOptions::READ | OpenOptions::WRITE,
|
||||
b"a+" | b"ab+" => OpenOptions::APPEND | OpenOptions::READ | OpenOptions::WRITE,
|
||||
// TODO: errno and fail
|
||||
_ => todo!(),
|
||||
};
|
||||
|
||||
match source.open_with(opts) {
|
||||
Ok(file) => {
|
||||
let file = Box::into_raw(Box::new(file));
|
||||
unsafe {
|
||||
super::register_file(file);
|
||||
}
|
||||
Ok(file)
|
||||
}
|
||||
Err(err) => Err(err),
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn fopen(pathname: *const c_char, mode: *const c_char) -> *mut FILE {
|
||||
todo!()
|
||||
if pathname.is_null() || mode.is_null() {
|
||||
panic!();
|
||||
}
|
||||
let pathname = CStr::from_ptr(pathname);
|
||||
let pathname = pathname.to_str().unwrap();
|
||||
let mode = CStr::from_ptr(mode);
|
||||
|
||||
match open_inner(Path::from_str(pathname), mode.to_bytes()) {
|
||||
Ok(file) => file,
|
||||
Err(_) => null_mut(),
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn feof(stream: *mut FILE) -> c_int {
|
||||
let stream = stream.as_mut().unwrap();
|
||||
stream.has_flag(FileFlags::EOF) as _
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn ferror(stream: *mut FILE) -> c_int {
|
||||
let stream = stream.as_mut().unwrap();
|
||||
stream.has_flag(FileFlags::ERROR) as _
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn clearerr(stream: *mut FILE) {
|
||||
let stream = stream.as_mut().unwrap();
|
||||
stream.clear_error();
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn fclose(stream: *mut FILE) -> c_int {
|
||||
if stream.is_null() {
|
||||
panic!();
|
||||
}
|
||||
|
||||
if !super::deregister_file(stream) {
|
||||
yggdrasil_rt::debug_trace!("fclose() non-registered file: {:p}", stream);
|
||||
}
|
||||
|
||||
stream.close().into_eof_status()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn fileno(stream: *mut FILE) -> c_int {
|
||||
let stream = stream.as_mut().unwrap();
|
||||
match stream.as_raw_fd() {
|
||||
Ok(RawFd(fd)) => fd as _,
|
||||
Err(_) => -1,
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -43,3 +129,103 @@ pub unsafe extern "C" fn fread(
|
||||
|
||||
stream.read(data).into_size_status() / size
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn fflush(stream: *mut FILE) -> c_int {
|
||||
if let Some(stream) = stream.as_mut() {
|
||||
stream.flush().into_eof_status()
|
||||
} else {
|
||||
super::fflush_all().into_eof_status()
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn fseek(stream: *mut FILE, offset: c_long, whence: c_int) -> c_int {
|
||||
fseeko(stream, offset.try_into().unwrap(), whence)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn fseeko(stream: *mut FILE, offset: off_t, whence: c_int) -> c_int {
|
||||
let stream = stream.as_mut().unwrap();
|
||||
let off = match whence {
|
||||
SEEK_SET => SeekFrom::Start(offset.try_into().unwrap()),
|
||||
SEEK_CUR => SeekFrom::Current(offset),
|
||||
SEEK_END => SeekFrom::End(offset),
|
||||
// TODO set errno and fail
|
||||
_ => todo!(),
|
||||
};
|
||||
|
||||
match stream.seek(off) {
|
||||
Ok(_) => 0,
|
||||
Err(_) => -1,
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn ftell(stream: *mut FILE) -> c_long {
|
||||
ftello(stream).try_into().unwrap()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn ftello(stream: *mut FILE) -> off_t {
|
||||
let stream = stream.as_mut().unwrap();
|
||||
match stream.stream_position() {
|
||||
Ok(p) => p.try_into().unwrap(),
|
||||
Err(_) => -1,
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn rewind(stream: *mut FILE) {
|
||||
fseek(stream, 0, SEEK_SET);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn fgetpos(stream: *mut FILE, pos: *mut fpos_t) -> c_int {
|
||||
let stream = stream.as_mut().unwrap();
|
||||
let pos = pos.as_mut().unwrap();
|
||||
match stream.stream_position() {
|
||||
Ok(p) => {
|
||||
*pos = p;
|
||||
0
|
||||
}
|
||||
Err(_) => -1,
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn fsetpos(stream: *mut FILE, pos: *const fpos_t) -> c_int {
|
||||
let stream = stream.as_mut().unwrap();
|
||||
let pos = pos.as_ref().unwrap();
|
||||
match stream.seek(SeekFrom::Start(*pos as _)) {
|
||||
Ok(_) => 0,
|
||||
Err(_) => -1,
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn setvbuf(
|
||||
stream: *mut FILE,
|
||||
buf: *mut c_char,
|
||||
mode: c_int,
|
||||
size: usize,
|
||||
) -> c_int {
|
||||
let stream = stream.as_mut().unwrap();
|
||||
stream.setvbuf(mode, buf, size).into_eof_status()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn setbuf(stream: *mut FILE, buf: *mut c_char) {
|
||||
setbuffer(stream, buf, 0);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn setbuffer(stream: *mut FILE, buf: *mut c_char, size: usize) {
|
||||
let mode = if buf.is_null() { _IONBF } else { _IOFBF };
|
||||
setvbuf(stream, buf, mode, size);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn setlinebuf(stream: *mut FILE) {
|
||||
setvbuf(stream, null_mut(), _IOLBF, 0);
|
||||
}
|
||||
|
||||
+110
-9
@@ -1,8 +1,11 @@
|
||||
use core::ffi::{c_char, c_int, CStr};
|
||||
use core::{
|
||||
ffi::{c_char, c_int, CStr},
|
||||
ptr::null_mut,
|
||||
};
|
||||
|
||||
use crate::{error::CZeroResult, traits::Write};
|
||||
use crate::io::{Read, Write};
|
||||
|
||||
use super::{stdout, FILE};
|
||||
use super::{stdin, stdout, EOF, FILE};
|
||||
|
||||
// Chars
|
||||
|
||||
@@ -11,10 +14,9 @@ unsafe extern "C" fn fputc(c: c_int, stream: *mut FILE) -> c_int {
|
||||
let stream = stream.as_mut().unwrap();
|
||||
let c = c as u8;
|
||||
|
||||
match stream.putc(c) {
|
||||
match stream.write(&[c]) {
|
||||
Ok(_) => c as c_int,
|
||||
// TODO EOF
|
||||
Err(_) => -1,
|
||||
Err(_) => EOF,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,16 +34,115 @@ unsafe extern "C" fn putchar(c: c_int) -> c_int {
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn puts(s: *const c_char) -> c_int {
|
||||
fputs(s, stdout)
|
||||
let r = fputs(s, stdout);
|
||||
if r < 0 {
|
||||
return r;
|
||||
}
|
||||
fputc(b'\n' as _, stdout)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn fputs(s: *const c_char, stream: *mut FILE) -> c_int {
|
||||
let stream = stream.as_mut().unwrap();
|
||||
if s.is_null() {
|
||||
return stream.puts(b"(nil)").into_zero_status();
|
||||
panic!();
|
||||
}
|
||||
let s = CStr::from_ptr(s);
|
||||
|
||||
stream.puts(s.to_bytes()).into_zero_status()
|
||||
match stream.write(s.to_bytes()) {
|
||||
Ok(_) => 0,
|
||||
Err(_) => EOF,
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn fgetc(stream: *mut FILE) -> c_int {
|
||||
let stream = stream.as_mut().unwrap();
|
||||
let mut buf = [0];
|
||||
match stream.read(&mut buf) {
|
||||
Ok(1) => buf[0] as c_int,
|
||||
Ok(_) => EOF,
|
||||
Err(_) => EOF,
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn getc(stream: *mut FILE) -> c_int {
|
||||
fgetc(stream)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn getchar() -> c_int {
|
||||
fgetc(stdin)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn fgets(s: *mut c_char, size: c_int, stream: *mut FILE) -> *mut c_char {
|
||||
let stream = stream.as_mut().unwrap();
|
||||
|
||||
if size <= 0 {
|
||||
return null_mut();
|
||||
}
|
||||
// Nothing to read
|
||||
if size == 1 {
|
||||
*s = 0;
|
||||
return s;
|
||||
}
|
||||
|
||||
let size = size as usize;
|
||||
let mut pos = 0;
|
||||
let mut buf = [0];
|
||||
|
||||
while pos < size - 1 {
|
||||
let ch = match stream.read(&mut buf) {
|
||||
Ok(1) => buf[0],
|
||||
Ok(_) => {
|
||||
break;
|
||||
}
|
||||
Err(err) => {
|
||||
return null_mut();
|
||||
}
|
||||
};
|
||||
|
||||
*s.add(pos) = ch as _;
|
||||
pos += 1;
|
||||
|
||||
if ch == b'\n' {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if pos == 0 {
|
||||
null_mut()
|
||||
} else {
|
||||
*s.add(pos) = 0;
|
||||
s
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn ungetc(c: c_int, stream: *mut FILE) -> c_int {
|
||||
let stream = stream.as_mut().unwrap();
|
||||
if stream.ungetc(c as _) {
|
||||
c
|
||||
} else {
|
||||
EOF
|
||||
}
|
||||
}
|
||||
|
||||
// Line
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn getdelim(
|
||||
lineptr: *mut *mut c_char,
|
||||
n: *mut usize,
|
||||
delim: c_int,
|
||||
stream: *mut FILE,
|
||||
) -> isize {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn getline(lineptr: *mut *mut c_char, n: *mut usize, stream: *mut FILE) -> isize {
|
||||
todo!()
|
||||
}
|
||||
|
||||
+432
-144
@@ -1,30 +1,31 @@
|
||||
use core::{fmt, mem::MaybeUninit, ptr};
|
||||
use core::{
|
||||
ffi::{c_char, c_int},
|
||||
fmt, ptr,
|
||||
};
|
||||
|
||||
use alloc::{boxed::Box, vec, vec::Vec};
|
||||
use yggdrasil_rt::{io::RawFd, sys as syscall};
|
||||
use alloc::{boxed::Box, collections::BTreeSet, vec::Vec};
|
||||
use bitflags::bitflags;
|
||||
use yggdrasil_rt::{
|
||||
io::{FileMode, OpenOptions, RawFd, SeekFrom},
|
||||
path::Path,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
error::EResult,
|
||||
error::{EResult, TryFromExt},
|
||||
file::{File, FileBacking},
|
||||
io::{
|
||||
buffered::{BufWriter, FileWriter, LineWriter, ReadBuffer, UnbufferedWriter},
|
||||
BufRead, Read, Seek, Write,
|
||||
},
|
||||
sync::{Mutex, RawMutex},
|
||||
traits::{Read, Write},
|
||||
};
|
||||
|
||||
use super::errno::Errno;
|
||||
|
||||
// TODO:
|
||||
//
|
||||
// fpos_t
|
||||
// off_t -> sys/types.h
|
||||
// ssize_t -> sys/types.h
|
||||
//
|
||||
// BUFSIZ
|
||||
// L_ctermid
|
||||
// L_tmpnam
|
||||
//
|
||||
// _IOFBF
|
||||
// _IOLBF
|
||||
// _IONBF
|
||||
//
|
||||
// SEEK_CUR
|
||||
// SEEK_END
|
||||
// SEEK_SET
|
||||
@@ -38,196 +39,427 @@ use super::errno::Errno;
|
||||
// P_tmpdir
|
||||
|
||||
/*
|
||||
void clearerr(FILE *);
|
||||
char *ctermid(char *);
|
||||
int dprintf(int, const char *restrict, ...)
|
||||
int fclose(FILE *);
|
||||
FILE *fdopen(int, const char *);
|
||||
int feof(FILE *);
|
||||
int ferror(FILE *);
|
||||
int fflush(FILE *);
|
||||
int fgetc(FILE *);
|
||||
int fgetpos(FILE *restrict, fpos_t *restrict);
|
||||
char *fgets(char *restrict, int, FILE *restrict);
|
||||
int fileno(FILE *);
|
||||
void flockfile(FILE *);
|
||||
FILE *fmemopen(void *restrict, size_t, const char *restrict);
|
||||
FILE *fopen(const char *restrict, const char *restrict);
|
||||
int fprintf(FILE *restrict, const char *restrict, ...);
|
||||
int fputc(int, FILE *);
|
||||
size_t fread(void *restrict, size_t, size_t, FILE *restrict);
|
||||
FILE *freopen(const char *restrict, const char *restrict,
|
||||
FILE *restrict);
|
||||
int fscanf(FILE *restrict, const char *restrict, ...);
|
||||
int fseek(FILE *, long, int);
|
||||
int fseeko(FILE *, off_t, int);
|
||||
int fsetpos(FILE *, const fpos_t *);
|
||||
long ftell(FILE *);
|
||||
off_t ftello(FILE *);
|
||||
int ftrylockfile(FILE *);
|
||||
void funlockfile(FILE *);
|
||||
int getc(FILE *);
|
||||
int getchar(void);
|
||||
int getc_unlocked(FILE *);
|
||||
int getchar_unlocked(void);
|
||||
ssize_t getdelim(char **restrict, size_t *restrict, int,
|
||||
FILE *restrict);
|
||||
ssize_t getline(char **restrict, size_t *restrict, FILE *restrict);
|
||||
char *gets(char *);
|
||||
FILE *open_memstream(char **, size_t *);
|
||||
int pclose(FILE *);
|
||||
void perror(const char *);
|
||||
FILE *popen(const char *, const char *);
|
||||
int putc(int, FILE *);
|
||||
int putchar(int);
|
||||
int putc_unlocked(int, FILE *);
|
||||
int putchar_unlocked(int);
|
||||
int remove(const char *);
|
||||
int rename(const char *, const char *);
|
||||
int renameat(int, const char *, int, const char *);
|
||||
void rewind(FILE *);
|
||||
int scanf(const char *restrict, ...);
|
||||
void setbuf(FILE *restrict, char *restrict);
|
||||
int setvbuf(FILE *restrict, char *restrict, int, size_t);
|
||||
int snprintf(char *restrict, size_t, const char *restrict, ...);
|
||||
int sprintf(char *restrict, const char *restrict, ...);
|
||||
int sscanf(const char *restrict, const char *restrict, ...);
|
||||
char *tempnam(const char *, const char *);
|
||||
FILE *tmpfile(void);
|
||||
char *tmpnam(char *);
|
||||
int ungetc(int, FILE *);
|
||||
int vdprintf(int, const char *restrict, va_list);
|
||||
int vfscanf(FILE *restrict, const char *restrict, va_list);
|
||||
int vscanf(const char *restrict, va_list);
|
||||
int vsnprintf(char *restrict, size_t, const char *restrict,
|
||||
va_list);
|
||||
int vsprintf(char *restrict, const char *restrict, va_list);
|
||||
int vsscanf(const char *restrict, const char *restrict, va_list);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
*/
|
||||
|
||||
macro_rules! locked_op {
|
||||
($self:expr, $op:expr) => {{
|
||||
$self.lock.lock();
|
||||
let result = unsafe { $op };
|
||||
unsafe { $self.lock.release() }
|
||||
result
|
||||
}};
|
||||
}
|
||||
|
||||
mod file;
|
||||
mod get_put;
|
||||
mod printf;
|
||||
mod scanf;
|
||||
mod unlocked;
|
||||
|
||||
struct FileInner(RawFd);
|
||||
pub trait FileOpenSource {
|
||||
fn open_with(self, opts: OpenOptions) -> Result<FILE, Errno>;
|
||||
}
|
||||
|
||||
pub enum BufferingMode {
|
||||
Full,
|
||||
Line,
|
||||
None,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
pub enum Direction {
|
||||
Read,
|
||||
Write,
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
pub struct FileFlags: u32 {
|
||||
const ERROR = 1 << 0;
|
||||
const EOF = 1 << 1;
|
||||
const READ = 1 << 2;
|
||||
const WRITE = 1 << 3;
|
||||
const BUILTIN = 1 << 4;
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FILE {
|
||||
lock: RawMutex,
|
||||
|
||||
file: FileInner,
|
||||
inner: FileBacking,
|
||||
output: Box<dyn FileWriter>,
|
||||
read_buffer: Option<ReadBuffer<'static>>,
|
||||
|
||||
error: bool,
|
||||
eof: bool,
|
||||
flags: FileFlags,
|
||||
|
||||
buffer: Vec<u8>,
|
||||
unget_buffer: Vec<u8>,
|
||||
read_pos: usize,
|
||||
read_size: usize,
|
||||
}
|
||||
ungetc: Vec<u8>,
|
||||
|
||||
impl FileInner {
|
||||
unsafe fn read(&mut self, data: &mut [u8]) -> Result<usize, Errno> {
|
||||
let count = EResult::from(syscall::read(self.0, data))?;
|
||||
Ok(count)
|
||||
}
|
||||
// NOTE:
|
||||
// man setvbuf(3):
|
||||
// The setvbuf() function may be used only after opening a stream and
|
||||
// before any other operations have been performed on it.
|
||||
last_operation: Option<Direction>,
|
||||
}
|
||||
|
||||
impl FILE {
|
||||
unsafe fn new_builtin(fd: RawFd) -> Self {
|
||||
pub unsafe fn from_c_file(file: File, flags: FileFlags) -> Self {
|
||||
assert!(!flags.contains(FileFlags::BUILTIN));
|
||||
let inner = FileBacking::File(file);
|
||||
// TODO run is_terminal on file and check for desired buffering mode
|
||||
Self {
|
||||
lock: RawMutex::new(),
|
||||
|
||||
file: FileInner(fd),
|
||||
output: Box::new(BufWriter::with_capacity(inner.make_ref(), BUFSIZ)),
|
||||
inner,
|
||||
read_buffer: Some(ReadBuffer::owned(BUFSIZ)),
|
||||
|
||||
eof: false,
|
||||
error: false,
|
||||
flags,
|
||||
|
||||
buffer: vec![0; 1024],
|
||||
unget_buffer: vec![],
|
||||
read_pos: 0,
|
||||
read_size: 0,
|
||||
ungetc: Vec::new(),
|
||||
|
||||
last_operation: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn puts(&mut self, data: &[u8]) -> Result<(), Errno> {
|
||||
self.write(data)?;
|
||||
self.write(b"\n")?;
|
||||
Ok(())
|
||||
}
|
||||
pub unsafe fn new_builtin(fd: RawFd, flags: FileFlags) -> Self {
|
||||
let inner = FileBacking::File(File::new(fd));
|
||||
Self {
|
||||
lock: RawMutex::new(),
|
||||
|
||||
unsafe fn fill_buf(&mut self) -> Result<&[u8], Errno> {
|
||||
if self.read_pos == self.read_size {
|
||||
self.read_size = match self.file.read(&mut self.buffer) {
|
||||
Ok(0) => {
|
||||
self.eof = true;
|
||||
0
|
||||
}
|
||||
Ok(n) => n,
|
||||
Err(err) => {
|
||||
self.error = true;
|
||||
return Err(err);
|
||||
}
|
||||
};
|
||||
self.read_pos = 0;
|
||||
output: Box::new(LineWriter::with_capacity(inner.make_ref(), BUFSIZ)),
|
||||
inner,
|
||||
read_buffer: Some(ReadBuffer::owned(BUFSIZ)),
|
||||
|
||||
flags: FileFlags::BUILTIN | flags,
|
||||
|
||||
ungetc: Vec::new(),
|
||||
|
||||
last_operation: None,
|
||||
}
|
||||
|
||||
Ok(&self.buffer[self.read_pos..self.read_size])
|
||||
}
|
||||
|
||||
unsafe fn consume(&mut self, len: usize) {
|
||||
self.read_pos = (self.read_pos + len).min(self.read_size);
|
||||
pub unsafe fn close(self: *mut Self) -> Result<(), Errno> {
|
||||
// TODO lock needed?
|
||||
let r = self.as_mut().unwrap();
|
||||
r.flush()?;
|
||||
if r.flags.contains(FileFlags::BUILTIN) {
|
||||
// NOTE The OS will close the file for us
|
||||
Ok(())
|
||||
} else {
|
||||
// Drop the file
|
||||
drop(Box::from_raw(self));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn as_raw_fd_unlocked(&mut self) -> Result<RawFd, Errno> {
|
||||
let fd = self.inner.as_raw_fd()?;
|
||||
Ok(fd)
|
||||
}
|
||||
|
||||
pub fn as_raw_fd(&mut self) -> Result<RawFd, Errno> {
|
||||
locked_op!(self, self.as_raw_fd_unlocked())
|
||||
}
|
||||
|
||||
pub unsafe fn clear_error_unlocked(&mut self) {
|
||||
self.flags &= !FileFlags::ERROR;
|
||||
}
|
||||
|
||||
pub fn clear_error(&mut self) {
|
||||
locked_op!(self, self.clear_error_unlocked());
|
||||
}
|
||||
|
||||
pub fn has_flag(&mut self, flag: FileFlags) -> bool {
|
||||
locked_op!(self, self.has_flag_unlocked(flag))
|
||||
}
|
||||
|
||||
pub unsafe fn has_flag_unlocked(&self, flag: FileFlags) -> bool {
|
||||
self.flags.contains(flag)
|
||||
}
|
||||
|
||||
pub fn setvbuf(
|
||||
&mut self,
|
||||
mode: c_int,
|
||||
buffer: *mut c_char,
|
||||
capacity: usize,
|
||||
) -> Result<(), Errno> {
|
||||
locked_op!(self, self.setvbuf_unlocked(mode, buffer, capacity))
|
||||
}
|
||||
|
||||
pub unsafe fn write_unlocked(&mut self, data: &[u8]) -> Result<usize, Errno> {
|
||||
self.set_direction(Direction::Write)?;
|
||||
|
||||
match self.output.write(data) {
|
||||
Ok(amount) => Ok(amount),
|
||||
Err(err) => {
|
||||
self.flags |= FileFlags::ERROR;
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn flush_unlocked(&mut self) -> Result<(), Errno> {
|
||||
match self.output.flush() {
|
||||
Ok(()) => {
|
||||
self.last_operation = None;
|
||||
Ok(())
|
||||
}
|
||||
Err(err) => {
|
||||
self.flags |= FileFlags::ERROR;
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn read_unlocked(&mut self, data: &mut [u8]) -> Result<usize, Errno> {
|
||||
let unget_len = core::cmp::min(self.unget_buffer.len(), data.len());
|
||||
for i in 0..unget_len {
|
||||
data[i] = self.unget_buffer.pop().unwrap();
|
||||
}
|
||||
if unget_len != 0 {
|
||||
return Ok(unget_len);
|
||||
}
|
||||
let len = {
|
||||
let buf = unsafe { self.fill_buf()? };
|
||||
let len = buf.len().min(data.len());
|
||||
self.set_direction(Direction::Read)?;
|
||||
|
||||
if !self.ungetc.is_empty() {
|
||||
// Read from ungetc
|
||||
let amount = core::cmp::min(self.ungetc.len(), data.len());
|
||||
data[..amount].copy_from_slice(&self.ungetc[..amount]);
|
||||
self.ungetc.drain(..amount);
|
||||
return Ok(amount);
|
||||
}
|
||||
|
||||
if self.read_buffer.is_some() {
|
||||
let buf = self.fill_buf()?;
|
||||
let len = core::cmp::min(data.len(), buf.len());
|
||||
data[..len].copy_from_slice(&buf[..len]);
|
||||
len
|
||||
};
|
||||
unsafe {
|
||||
self.consume(len);
|
||||
Ok(len)
|
||||
} else {
|
||||
match self.inner.read(data) {
|
||||
Ok(0) => {
|
||||
self.flags |= FileFlags::EOF;
|
||||
Ok(0)
|
||||
}
|
||||
Ok(n) => Ok(n),
|
||||
Err(err) => {
|
||||
self.flags |= FileFlags::ERROR;
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(len)
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for FILE {
|
||||
fn read(&mut self, data: &mut [u8]) -> Result<usize, Errno> {
|
||||
self.lock.lock();
|
||||
let result = unsafe { self.read_unlocked(data) };
|
||||
unsafe {
|
||||
self.lock.release();
|
||||
unsafe fn ungetc_unlocked(&mut self, ch: u8) -> bool {
|
||||
// ungetc() for write doesn't make any sense
|
||||
if self.set_direction(Direction::Read).is_err() {
|
||||
return false;
|
||||
}
|
||||
result
|
||||
if self.ungetc.len() == UNGETC_MAX {
|
||||
return false;
|
||||
}
|
||||
self.ungetc.push(ch);
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for FILE {
|
||||
fn write(&mut self, data: &[u8]) -> Result<usize, Errno> {
|
||||
// TODO
|
||||
self.lock.lock();
|
||||
let count = EResult::from(unsafe { syscall::write(self.file.0, data) })?;
|
||||
unsafe {
|
||||
self.lock.release();
|
||||
pub fn ungetc(&mut self, ch: u8) -> bool {
|
||||
locked_op!(self, self.ungetc_unlocked(ch))
|
||||
}
|
||||
|
||||
fn reset(&mut self) {
|
||||
if let Some(read_buffer) = self.read_buffer.as_mut() {
|
||||
read_buffer.reset();
|
||||
}
|
||||
self.output.reset();
|
||||
self.ungetc.clear();
|
||||
self.last_operation = None;
|
||||
self.flags &= !(FileFlags::ERROR | FileFlags::EOF);
|
||||
}
|
||||
|
||||
unsafe fn setvbuf_unlocked(
|
||||
&mut self,
|
||||
mode: c_int,
|
||||
buffer: *mut c_char,
|
||||
capacity: usize,
|
||||
) -> Result<(), Errno> {
|
||||
let mode = BufferingMode::e_try_from(mode)?;
|
||||
|
||||
if self.last_operation.is_some() {
|
||||
// TODO EINVAL?
|
||||
todo!();
|
||||
}
|
||||
|
||||
let mut read_capacity = capacity;
|
||||
let mut write_capacity = capacity;
|
||||
|
||||
// Set read buffer
|
||||
match mode {
|
||||
BufferingMode::None => self.read_buffer = None,
|
||||
BufferingMode::Full | BufferingMode::Line => {
|
||||
if buffer.is_null() || read_capacity == 0 {
|
||||
if read_capacity == 0 {
|
||||
read_capacity = BUFSIZ;
|
||||
}
|
||||
|
||||
self.read_buffer = Some(ReadBuffer::owned(read_capacity));
|
||||
} else {
|
||||
let slice =
|
||||
unsafe { core::slice::from_raw_parts_mut(buffer as *mut _, capacity) };
|
||||
self.read_buffer = Some(ReadBuffer::borrowed(slice));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set write buffer
|
||||
let file_ref = unsafe { self.inner.make_ref() };
|
||||
match mode {
|
||||
BufferingMode::None => self.output = Box::new(UnbufferedWriter::new(file_ref)),
|
||||
BufferingMode::Line => {
|
||||
if write_capacity == 0 {
|
||||
write_capacity = BUFSIZ;
|
||||
}
|
||||
self.output = Box::new(LineWriter::with_capacity(file_ref, write_capacity))
|
||||
}
|
||||
BufferingMode::Full => {
|
||||
if write_capacity == 0 {
|
||||
write_capacity = BUFSIZ;
|
||||
}
|
||||
self.output = Box::new(LineWriter::with_capacity(file_ref, write_capacity))
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn seek_unlocked(&mut self, off: SeekFrom) -> Result<u64, Errno> {
|
||||
self.flush_unlocked()?;
|
||||
self.ungetc.clear();
|
||||
|
||||
match self.inner.seek(off) {
|
||||
Ok(pos) => {
|
||||
self.flags &= !FileFlags::EOF;
|
||||
Ok(pos)
|
||||
}
|
||||
Err(err) => {
|
||||
self.flags |= FileFlags::ERROR;
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn set_direction(&mut self, direction: Direction) -> Result<(), Errno> {
|
||||
match self.last_operation.replace(direction) {
|
||||
Some(dir) if dir != direction => {
|
||||
self.flags |= FileFlags::ERROR;
|
||||
todo!()
|
||||
}
|
||||
_ => Ok(()),
|
||||
}
|
||||
Ok(count)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Write for FILE {
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
match self.write(s.as_bytes()) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(_) => Err(fmt::Error),
|
||||
self.write_all(s.as_bytes()).map_err(|_| fmt::Error)
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for FILE {
|
||||
fn write(&mut self, data: &[u8]) -> Result<usize, Errno> {
|
||||
locked_op!(self, self.write_unlocked(data))
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> Result<(), Errno> {
|
||||
locked_op!(self, self.flush_unlocked())
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for FILE {
|
||||
fn read(&mut self, data: &mut [u8]) -> Result<usize, Errno> {
|
||||
locked_op!(self, self.read_unlocked(data))
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE assumes file is locked
|
||||
impl BufRead for FILE {
|
||||
fn fill_buf(&mut self) -> Result<&[u8], Errno> {
|
||||
let buffer = self.read_buffer.as_mut().unwrap();
|
||||
match buffer.fill_from(&mut self.inner) {
|
||||
Ok(slice) => {
|
||||
if slice.len() == 0 {
|
||||
self.flags |= FileFlags::EOF;
|
||||
}
|
||||
Ok(slice)
|
||||
}
|
||||
Err(err) => {
|
||||
self.flags |= FileFlags::ERROR;
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn consume(&mut self, amount: usize) {
|
||||
let buffer = self.read_buffer.as_mut().unwrap();
|
||||
buffer.consume(amount)
|
||||
}
|
||||
}
|
||||
|
||||
impl Seek for FILE {
|
||||
fn seek(&mut self, off: SeekFrom) -> Result<u64, Errno> {
|
||||
locked_op!(self, self.seek_unlocked(off))
|
||||
}
|
||||
}
|
||||
|
||||
impl FileOpenSource for &Path {
|
||||
fn open_with(self, opts: OpenOptions) -> Result<FILE, Errno> {
|
||||
let f = File::open_at(None, self, opts, FileMode::default_file())?;
|
||||
let mut flags = FileFlags::empty();
|
||||
if opts.contains(OpenOptions::READ) {
|
||||
flags |= FileFlags::READ;
|
||||
}
|
||||
if opts.contains(OpenOptions::WRITE) {
|
||||
flags |= FileFlags::WRITE;
|
||||
}
|
||||
Ok(unsafe { FILE::from_c_file(f, flags) })
|
||||
}
|
||||
}
|
||||
|
||||
impl FileOpenSource for RawFd {
|
||||
fn open_with(self, opts: OpenOptions) -> Result<FILE, Errno> {
|
||||
let f = File::from_raw(self);
|
||||
let mut flags = FileFlags::empty();
|
||||
if opts.contains(OpenOptions::READ) {
|
||||
flags |= FileFlags::READ;
|
||||
}
|
||||
if opts.contains(OpenOptions::WRITE) {
|
||||
flags |= FileFlags::WRITE;
|
||||
}
|
||||
Ok(unsafe { FILE::from_c_file(f, flags) })
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFromExt<c_int> for BufferingMode {
|
||||
fn e_try_from(value: c_int) -> EResult<Self> {
|
||||
match value {
|
||||
_IOFBF => EResult::Ok(Self::Full),
|
||||
_IOLBF => EResult::Ok(Self::Line),
|
||||
_IONBF => EResult::Ok(Self::None),
|
||||
_ => EResult::Err(yggdrasil_rt::Error::InvalidArgument),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -239,12 +471,68 @@ pub static mut stdout: *mut FILE = ptr::null_mut();
|
||||
#[no_mangle]
|
||||
pub static mut stderr: *mut FILE = ptr::null_mut();
|
||||
|
||||
pub const _IOFBF: c_int = 0;
|
||||
pub const _IOLBF: c_int = 1;
|
||||
pub const _IONBF: c_int = 2;
|
||||
|
||||
pub const SEEK_SET: c_int = 0;
|
||||
pub const SEEK_CUR: c_int = 1;
|
||||
pub const SEEK_END: c_int = 2;
|
||||
|
||||
pub const EOF: c_int = -1;
|
||||
|
||||
pub type fpos_t = u64;
|
||||
|
||||
pub const BUFSIZ: usize = 8192;
|
||||
|
||||
const UNGETC_MAX: usize = 128;
|
||||
|
||||
pub static OPEN_FILES: Mutex<BTreeSet<*mut FILE>> = Mutex::new(BTreeSet::new());
|
||||
|
||||
pub unsafe fn register_file(file: *mut FILE) {
|
||||
OPEN_FILES.lock().insert(file);
|
||||
}
|
||||
|
||||
pub unsafe fn deregister_file(file: *mut FILE) -> bool {
|
||||
OPEN_FILES.lock().remove(&file)
|
||||
}
|
||||
|
||||
pub unsafe fn setup_default_files() {
|
||||
let stdin_ = Box::into_raw(Box::new(FILE::new_builtin(RawFd::STDIN)));
|
||||
let stdout_ = Box::into_raw(Box::new(FILE::new_builtin(RawFd::STDOUT)));
|
||||
let stderr_ = Box::into_raw(Box::new(FILE::new_builtin(RawFd::STDERR)));
|
||||
let stdin_ = Box::into_raw(Box::new(FILE::new_builtin(RawFd::STDIN, FileFlags::READ)));
|
||||
let stdout_ = Box::into_raw(Box::new(FILE::new_builtin(RawFd::STDOUT, FileFlags::WRITE)));
|
||||
let stderr_ = Box::into_raw(Box::new(FILE::new_builtin(RawFd::STDERR, FileFlags::WRITE)));
|
||||
|
||||
stdin = stdin_;
|
||||
stdout = stdout_;
|
||||
stderr = stderr_;
|
||||
|
||||
let mut open_files = OPEN_FILES.lock();
|
||||
open_files.insert(stdin);
|
||||
open_files.insert(stdout);
|
||||
open_files.insert(stderr);
|
||||
}
|
||||
|
||||
pub fn fflush_all() -> Result<(), Errno> {
|
||||
let open_files = OPEN_FILES.lock();
|
||||
for &file in open_files.iter() {
|
||||
unsafe {
|
||||
file.as_mut().unwrap().flush()?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub unsafe fn fflush_all_unlocked() -> Result<(), Errno> {
|
||||
let open_files = OPEN_FILES.lock();
|
||||
for &file in open_files.iter() {
|
||||
file.as_mut().unwrap().flush_unlocked()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub unsafe fn cleanup() {
|
||||
let mut open_files = OPEN_FILES.lock();
|
||||
while let Some(file) = open_files.pop_first() {
|
||||
file.close().ok();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
use core::{ffi::c_double, num::FpCategory};
|
||||
use core::{ffi::c_double, fmt, num::FpCategory};
|
||||
|
||||
use alloc::{format, string::String};
|
||||
|
||||
use crate::{
|
||||
header::errno::{Errno, EINVAL},
|
||||
traits::Write,
|
||||
io::Write,
|
||||
};
|
||||
|
||||
use super::format::FmtOpts;
|
||||
@@ -30,7 +30,7 @@ fn float_exp(mut val: c_double) -> (c_double, isize) {
|
||||
(val, exp)
|
||||
}
|
||||
|
||||
fn fmt_float_exp<W: Write>(
|
||||
fn fmt_float_exp<W: Write + fmt::Write>(
|
||||
val: c_double,
|
||||
exp: isize,
|
||||
exp_fmt: u8,
|
||||
@@ -38,9 +38,6 @@ fn fmt_float_exp<W: Write>(
|
||||
precision: usize,
|
||||
opts: &FmtOpts,
|
||||
) -> Result<usize, Errno> {
|
||||
// TODO padding
|
||||
use core::fmt::Write as FW;
|
||||
|
||||
let mut exp2 = exp;
|
||||
let mut exp_len = 1;
|
||||
while exp2 >= 10 {
|
||||
@@ -65,7 +62,7 @@ fn fmt_float_string(val: c_double, precision: usize) -> String {
|
||||
string
|
||||
}
|
||||
|
||||
fn fmt_float_finite<W: Write>(
|
||||
fn fmt_float_finite<W: Write + fmt::Write>(
|
||||
val: c_double,
|
||||
output: &mut W,
|
||||
precision: usize,
|
||||
@@ -78,7 +75,7 @@ fn fmt_float_finite<W: Write>(
|
||||
Ok(lpad + flen + rpad)
|
||||
}
|
||||
|
||||
fn fmt_float_nonfinite<W: Write>(
|
||||
fn fmt_float_nonfinite<W: Write + fmt::Write>(
|
||||
val: c_double,
|
||||
output: &mut W,
|
||||
upper: bool,
|
||||
@@ -108,7 +105,7 @@ fn fmt_float_nonfinite<W: Write>(
|
||||
Ok(len + lpad + flen + rpad)
|
||||
}
|
||||
|
||||
pub fn fmt_float<W: Write>(
|
||||
pub fn fmt_float<W: Write + fmt::Write>(
|
||||
val: c_double,
|
||||
output: &mut W,
|
||||
upper: bool,
|
||||
@@ -121,7 +118,7 @@ pub fn fmt_float<W: Write>(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fmt_float_scientific<W: Write>(
|
||||
pub fn fmt_float_scientific<W: Write + fmt::Write>(
|
||||
val: c_double,
|
||||
output: &mut W,
|
||||
upper: bool,
|
||||
@@ -141,7 +138,7 @@ pub fn fmt_float_scientific<W: Write>(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fmt_float_any<W: Write>(
|
||||
pub fn fmt_float_any<W: Write + fmt::Write>(
|
||||
val: c_double,
|
||||
output: &mut W,
|
||||
upper: bool,
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
use core::ffi::{
|
||||
c_char, c_double, c_int, c_long, c_longlong, c_short, c_uchar, c_uint, c_ulong, c_ulonglong,
|
||||
c_ushort, CStr, VaList,
|
||||
use core::{
|
||||
ffi::{
|
||||
c_char, c_double, c_int, c_long, c_longlong, c_short, c_uchar, c_uint, c_ulong,
|
||||
c_ulonglong, c_ushort, CStr, VaList,
|
||||
},
|
||||
fmt,
|
||||
};
|
||||
|
||||
use alloc::string::String;
|
||||
|
||||
use crate::{header::errno::Errno, traits::Write, types::wchar_t};
|
||||
use crate::{header::errno::Errno, io::Write, types::wchar_t};
|
||||
|
||||
use super::float::{fmt_float, fmt_float_any, fmt_float_scientific};
|
||||
|
||||
@@ -140,7 +143,7 @@ impl FmtSize {
|
||||
}
|
||||
|
||||
impl FmtOpts {
|
||||
fn pad<W: Write>(&self, output: &mut W, len: usize) -> Result<usize, Errno> {
|
||||
fn pad<W: Write + fmt::Write>(&self, output: &mut W, len: usize) -> Result<usize, Errno> {
|
||||
let pad = self.width - core::cmp::min(self.width, len);
|
||||
for _ in 0..pad {
|
||||
output.write(&[self.pad_char])?;
|
||||
@@ -148,7 +151,11 @@ impl FmtOpts {
|
||||
Ok(pad)
|
||||
}
|
||||
|
||||
pub fn left_pad<W: Write>(&self, output: &mut W, len: usize) -> Result<usize, Errno> {
|
||||
pub fn left_pad<W: Write + fmt::Write>(
|
||||
&self,
|
||||
output: &mut W,
|
||||
len: usize,
|
||||
) -> Result<usize, Errno> {
|
||||
if !self.left_adjust {
|
||||
self.pad(output, len)
|
||||
} else {
|
||||
@@ -156,7 +163,11 @@ impl FmtOpts {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn right_pad<W: Write>(&self, output: &mut W, len: usize) -> Result<usize, Errno> {
|
||||
pub fn right_pad<W: Write + fmt::Write>(
|
||||
&self,
|
||||
output: &mut W,
|
||||
len: usize,
|
||||
) -> Result<usize, Errno> {
|
||||
if self.left_adjust {
|
||||
self.pad(output, len)
|
||||
} else {
|
||||
@@ -164,7 +175,7 @@ impl FmtOpts {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fmt<W: Write>(
|
||||
pub fn fmt<W: Write + fmt::Write>(
|
||||
&self,
|
||||
spec: FmtSpec,
|
||||
output: &mut W,
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
use core::ffi::{c_char, c_int, CStr, VaList};
|
||||
use core::{
|
||||
ffi::{c_char, c_int, CStr, VaList},
|
||||
fmt,
|
||||
};
|
||||
|
||||
use crate::{header::errno::Errno, traits::Write};
|
||||
use yggdrasil_rt::{io::RawFd, sys as syscall};
|
||||
|
||||
use crate::{error::EResult, header::errno::Errno, io::Write};
|
||||
|
||||
use self::format::{FmtOpts, FmtRadix, FmtSign, FmtSize, FmtSpec};
|
||||
|
||||
@@ -9,14 +14,86 @@ use super::{stdout, FILE};
|
||||
mod float;
|
||||
mod format;
|
||||
|
||||
fn printf_inner<W: Write>(output: &mut W, format: &[u8], mut ap: VaList) -> Result<usize, Errno> {
|
||||
struct StringWriter {
|
||||
buffer: *mut c_char,
|
||||
position: usize,
|
||||
capacity: usize,
|
||||
}
|
||||
|
||||
struct FdWriter {
|
||||
fd: RawFd,
|
||||
}
|
||||
|
||||
impl StringWriter {
|
||||
pub fn new(buffer: *mut c_char, capacity: usize) -> Self {
|
||||
Self {
|
||||
buffer,
|
||||
capacity,
|
||||
position: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for StringWriter {
|
||||
fn write(&mut self, data: &[u8]) -> Result<usize, Errno> {
|
||||
let count = core::cmp::min(self.capacity - self.position, data.len());
|
||||
if count > 0 {
|
||||
let dst = unsafe {
|
||||
core::slice::from_raw_parts_mut(self.buffer.add(self.position) as *mut u8, count)
|
||||
};
|
||||
dst.copy_from_slice(&data[..count]);
|
||||
self.position += count;
|
||||
}
|
||||
Ok(count)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> Result<(), Errno> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Write for StringWriter {
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
self.write(s.as_bytes()).map_err(|_| fmt::Error)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl FdWriter {
|
||||
pub fn new(fd: RawFd) -> Self {
|
||||
Self { fd }
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for FdWriter {
|
||||
fn write(&mut self, data: &[u8]) -> Result<usize, Errno> {
|
||||
let count = EResult::from(unsafe { syscall::write(self.fd, data) })?;
|
||||
Ok(count)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> Result<(), Errno> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Write for FdWriter {
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
self.write(s.as_bytes()).map_err(|_| fmt::Error)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn printf_inner<W: Write + fmt::Write>(
|
||||
output: &mut W,
|
||||
format: &[u8],
|
||||
mut ap: VaList,
|
||||
) -> Result<usize, Errno> {
|
||||
let mut fmt = format.into_iter();
|
||||
let mut count = 0;
|
||||
|
||||
while let Some(&c) = fmt.next() {
|
||||
if c != b'%' {
|
||||
output.putc(c)?;
|
||||
count += 1;
|
||||
count += output.write(&[c])?;
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -131,3 +208,74 @@ unsafe extern "C" fn vfprintf(stream: *mut FILE, format: *const c_char, ap: VaLi
|
||||
Err(_) => -1,
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn sprintf(str: *mut c_char, format: *const c_char, mut args: ...) -> c_int {
|
||||
vsnprintf(str, usize::MAX, format, args.as_va_list())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn snprintf(
|
||||
str: *mut c_char,
|
||||
len: usize,
|
||||
format: *const c_char,
|
||||
mut args: ...
|
||||
) -> c_int {
|
||||
vsnprintf(str, len, format, args.as_va_list())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn vsprintf(str: *mut c_char, format: *const c_char, ap: VaList) -> c_int {
|
||||
vsnprintf(str, usize::MAX, format, ap)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn vsnprintf(
|
||||
str: *mut c_char,
|
||||
len: usize,
|
||||
format: *const c_char,
|
||||
ap: VaList,
|
||||
) -> c_int {
|
||||
if format.is_null() {
|
||||
panic!();
|
||||
}
|
||||
if len == 0 {
|
||||
return 0;
|
||||
}
|
||||
if str.is_null() {
|
||||
// TODO output the length
|
||||
todo!()
|
||||
}
|
||||
let mut writer = StringWriter::new(str, len);
|
||||
let format = CStr::from_ptr(format);
|
||||
|
||||
match printf_inner(&mut writer, format.to_bytes(), ap) {
|
||||
// TODO handle this
|
||||
Ok(count) => count as c_int,
|
||||
Err(_) => -1,
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn dprintf(fd: c_int, format: *const c_char, mut args: ...) -> c_int {
|
||||
vdprintf(fd, format, args.as_va_list())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn vdprintf(fd: c_int, format: *const c_char, ap: VaList) -> c_int {
|
||||
if format.is_null() {
|
||||
panic!();
|
||||
}
|
||||
if fd < 0 {
|
||||
// TODO set errno and return
|
||||
todo!();
|
||||
}
|
||||
let mut writer = FdWriter::new(RawFd(fd as u32));
|
||||
let format = CStr::from_ptr(format);
|
||||
|
||||
match printf_inner(&mut writer, format.to_bytes(), ap) {
|
||||
// TODO handle this
|
||||
Ok(count) => count as c_int,
|
||||
Err(_) => -1,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
int fscanf(FILE *restrict, const char *restrict, ...);
|
||||
int scanf(const char *restrict, ...);
|
||||
int sscanf(const char *restrict, const char *restrict, ...);
|
||||
int vfscanf(FILE *restrict, const char *restrict, va_list);
|
||||
int vscanf(const char *restrict, va_list);
|
||||
int vsscanf(const char *restrict, const char *restrict, va_list);
|
||||
*/
|
||||
|
||||
use core::ffi::{c_char, c_int, VaList};
|
||||
|
||||
use super::{stdin, FILE};
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn fscanf(stream: *mut FILE, format: *const c_char, mut args: ...) -> c_int {
|
||||
vfscanf(stream, format, args.as_va_list())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn scanf(format: *const c_char, mut args: ...) -> c_int {
|
||||
vfscanf(stdin, format, args.as_va_list())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn sscanf(str: *const c_char, format: *const c_char, mut args: ...) -> c_int {
|
||||
vsscanf(str, format, args.as_va_list())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn vfscanf(stream: *mut FILE, format: *const c_char, ap: VaList) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn vscanf(format: *const c_char, ap: VaList) -> c_int {
|
||||
vfscanf(stdin, format, ap)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn vsscanf(str: *const c_char, format: *const c_char, ap: VaList) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
use core::ffi::{c_int, c_void};
|
||||
|
||||
use yggdrasil_rt::io::RawFd;
|
||||
|
||||
use crate::error::{CSizeResult, CZeroResult};
|
||||
|
||||
use super::{FileFlags, FILE};
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn feof_unlocked(stream: *mut FILE) -> c_int {
|
||||
let stream = stream.as_mut().unwrap();
|
||||
stream.has_flag_unlocked(FileFlags::EOF) as _
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn ferror_unlocked(stream: *mut FILE) -> c_int {
|
||||
let stream = stream.as_mut().unwrap();
|
||||
stream.has_flag_unlocked(FileFlags::ERROR) as _
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn clearerr_unlocked(stream: *mut FILE) {
|
||||
let stream = stream.as_mut().unwrap();
|
||||
stream.clear_error_unlocked();
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn fileno_unlocked(stream: *mut FILE) -> c_int {
|
||||
let stream = stream.as_mut().unwrap();
|
||||
match stream.as_raw_fd_unlocked() {
|
||||
Ok(RawFd(fd)) => fd as _,
|
||||
Err(_) => -1,
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn fwrite_unlocked(
|
||||
ptr: *mut c_void,
|
||||
size: usize,
|
||||
nmemb: usize,
|
||||
stream: *mut FILE,
|
||||
) -> usize {
|
||||
if ptr.is_null() {
|
||||
panic!();
|
||||
}
|
||||
let stream = stream.as_mut().unwrap();
|
||||
let data = core::slice::from_raw_parts(ptr as *const u8, size * nmemb);
|
||||
|
||||
stream.write_unlocked(data).into_size_status() / size
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn fread_unlocked(
|
||||
ptr: *mut c_void,
|
||||
size: usize,
|
||||
nmemb: usize,
|
||||
stream: *mut FILE,
|
||||
) -> usize {
|
||||
if ptr.is_null() {
|
||||
panic!();
|
||||
}
|
||||
let stream = stream.as_mut().unwrap();
|
||||
let data = core::slice::from_raw_parts_mut(ptr as *mut u8, size * nmemb);
|
||||
|
||||
stream.read_unlocked(data).into_size_status() / size
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn fflush_unlocked(stream: *mut FILE) -> c_int {
|
||||
if let Some(stream) = stream.as_mut() {
|
||||
stream.flush_unlocked().into_eof_status()
|
||||
} else {
|
||||
super::fflush_all_unlocked().into_eof_status()
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
int getc_unlocked(FILE *stream);
|
||||
int getchar_unlocked(void);
|
||||
int putc_unlocked(int c, FILE *stream);
|
||||
int putchar_unlocked(int c);
|
||||
|
||||
int fgetc_unlocked(FILE *stream);
|
||||
int fputc_unlocked(int c, FILE *stream);
|
||||
*/
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn getc_unlocked(stream: *mut FILE) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
@@ -6,7 +6,8 @@ no_includes = true
|
||||
|
||||
include_guard = "_STDLIB_H"
|
||||
|
||||
usize_is_size_t = true
|
||||
usize_type = "size_t"
|
||||
isize_type = "ssize_t"
|
||||
|
||||
[export]
|
||||
exclude = []
|
||||
|
||||
@@ -6,6 +6,7 @@ no_includes = true
|
||||
|
||||
include_guard = "_STRING_H"
|
||||
|
||||
usize_is_size_t = true
|
||||
usize_type = "size_t"
|
||||
isize_type = "ssize_t"
|
||||
|
||||
[export]
|
||||
|
||||
@@ -1,10 +1,30 @@
|
||||
language = "C"
|
||||
style = "Type"
|
||||
|
||||
sys_includes = ["stddef.h"]
|
||||
sys_includes = ["stdint.h", "stddef.h"]
|
||||
no_includes = true
|
||||
|
||||
include_guard = "_SYS_TYPES_H"
|
||||
|
||||
[export]
|
||||
include = ["pid_t"]
|
||||
include = [
|
||||
"blkcnt_t",
|
||||
"blksize_t",
|
||||
"clock_t",
|
||||
"clockid_t",
|
||||
"dev_t",
|
||||
"fsblkcnt_t",
|
||||
"fsfilcnt_t",
|
||||
"gid_t",
|
||||
"id_t",
|
||||
"ino_t",
|
||||
"mode_t",
|
||||
"nlink_t",
|
||||
"off_t",
|
||||
"pid_t",
|
||||
"ssize_t",
|
||||
"suseconds_t",
|
||||
"time_t",
|
||||
"timer_t",
|
||||
"uid_t"
|
||||
]
|
||||
|
||||
@@ -1,3 +1,31 @@
|
||||
use core::ffi::c_int;
|
||||
use core::ffi::c_ulong;
|
||||
|
||||
pub type pid_t = i32;
|
||||
pub type uid_t = i32;
|
||||
pub type gid_t = i32;
|
||||
pub type id_t = i32;
|
||||
|
||||
pub type dev_t = u32;
|
||||
|
||||
pub type clockid_t = i32;
|
||||
pub type timer_t = i32;
|
||||
|
||||
pub type mode_t = u32;
|
||||
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
pub type ssize_t = i64;
|
||||
|
||||
pub type blkcnt_t = i64;
|
||||
pub type blksize_t = c_ulong;
|
||||
pub type nlink_t = u64;
|
||||
|
||||
pub type fsblkcnt_t = u64;
|
||||
pub type fsfilcnt_t = u64;
|
||||
pub type ino_t = u64;
|
||||
|
||||
pub type clock_t = u64;
|
||||
pub type time_t = u64;
|
||||
|
||||
pub type off_t = i64;
|
||||
|
||||
pub type suseconds_t = c_ulong;
|
||||
|
||||
@@ -6,7 +6,8 @@ no_includes = true
|
||||
|
||||
include_guard = "_SYS_WAIT_H"
|
||||
|
||||
usize_is_size_t = true
|
||||
usize_type = "size_t"
|
||||
isize_type = "ssize_t"
|
||||
|
||||
[export]
|
||||
exclude = []
|
||||
|
||||
@@ -7,7 +7,8 @@ no_includes = true
|
||||
include_guard = "_UNISTD_H"
|
||||
trailer = "#include <bits/unistd.h>"
|
||||
|
||||
usize_is_size_t = true
|
||||
usize_type = "size_t"
|
||||
isize_type = "ssize_t"
|
||||
|
||||
[export]
|
||||
exclude = []
|
||||
|
||||
@@ -0,0 +1,231 @@
|
||||
use core::{
|
||||
mem::MaybeUninit,
|
||||
ops::{Deref, DerefMut},
|
||||
slice::memchr,
|
||||
};
|
||||
|
||||
use alloc::{boxed::Box, vec::Vec};
|
||||
|
||||
use crate::header::errno::Errno;
|
||||
|
||||
use super::{Read, Write};
|
||||
|
||||
// Read
|
||||
|
||||
enum ReadBufferBacking<'a> {
|
||||
Owned(Box<[MaybeUninit<u8>]>),
|
||||
Borrowed(&'a mut [MaybeUninit<u8>]),
|
||||
}
|
||||
|
||||
pub struct ReadBuffer<'a> {
|
||||
data: ReadBufferBacking<'a>,
|
||||
position: usize,
|
||||
len: usize,
|
||||
}
|
||||
|
||||
// Write
|
||||
|
||||
pub trait FileWriter: Write {
|
||||
fn reset(&mut self);
|
||||
}
|
||||
|
||||
pub struct UnbufferedWriter<W: Write> {
|
||||
inner: W,
|
||||
}
|
||||
|
||||
pub struct BufWriter<W: Write> {
|
||||
inner: W,
|
||||
buffer: Vec<u8>,
|
||||
}
|
||||
|
||||
pub struct LineWriter<W: Write> {
|
||||
inner: BufWriter<W>,
|
||||
need_flush: bool,
|
||||
}
|
||||
|
||||
impl<'a> ReadBuffer<'a> {
|
||||
pub fn owned(capacity: usize) -> Self {
|
||||
Self {
|
||||
data: ReadBufferBacking::Owned(Box::new_uninit_slice(capacity)),
|
||||
position: 0,
|
||||
len: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn borrowed(data: &'a mut [MaybeUninit<u8>]) -> Self {
|
||||
Self {
|
||||
data: ReadBufferBacking::Borrowed(data),
|
||||
position: 0,
|
||||
len: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) {
|
||||
self.position = 0;
|
||||
self.len = 0;
|
||||
}
|
||||
|
||||
pub fn fill_from<R: Read + ?Sized>(&mut self, source: &mut R) -> Result<&[u8], Errno> {
|
||||
let buffer = unsafe { self.data.as_slice_mut() };
|
||||
if self.position == self.len {
|
||||
let amount = match source.read(buffer) {
|
||||
Ok(0) => return Ok(&buffer[..0]),
|
||||
Ok(n) => n,
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
|
||||
self.position = 0;
|
||||
self.len = amount;
|
||||
}
|
||||
|
||||
Ok(&mut buffer[self.position..self.len])
|
||||
}
|
||||
|
||||
pub fn consume(&mut self, amount: usize) {
|
||||
self.position = (self.position + amount).min(self.len);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ReadBufferBacking<'a> {
|
||||
unsafe fn as_slice_mut(&mut self) -> &mut [u8] {
|
||||
MaybeUninit::slice_assume_init_mut(self.deref_mut())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Deref for ReadBufferBacking<'a> {
|
||||
type Target = [MaybeUninit<u8>];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
match self {
|
||||
Self::Owned(owned) => owned.deref(),
|
||||
Self::Borrowed(borrowed) => borrowed.deref(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> DerefMut for ReadBufferBacking<'a> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
match self {
|
||||
Self::Owned(owned) => owned.deref_mut(),
|
||||
Self::Borrowed(borrowed) => borrowed.deref_mut(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> UnbufferedWriter<W> {
|
||||
pub fn new(inner: W) -> Self {
|
||||
Self { inner }
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> BufWriter<W> {
|
||||
pub fn with_capacity(inner: W, capacity: usize) -> Self {
|
||||
Self {
|
||||
inner,
|
||||
buffer: Vec::with_capacity(capacity),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> LineWriter<W> {
|
||||
pub fn with_capacity(inner: W, capacity: usize) -> Self {
|
||||
Self {
|
||||
inner: BufWriter::with_capacity(inner, capacity),
|
||||
need_flush: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> Write for UnbufferedWriter<W> {
|
||||
fn write(&mut self, data: &[u8]) -> Result<usize, Errno> {
|
||||
self.inner.write(data)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> Result<(), Errno> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> Write for BufWriter<W> {
|
||||
fn write(&mut self, mut data: &[u8]) -> Result<usize, Errno> {
|
||||
if data.len() + self.buffer.len() > self.buffer.capacity() {
|
||||
self.flush()?;
|
||||
}
|
||||
|
||||
let len = data.len();
|
||||
|
||||
if len >= self.buffer.capacity() {
|
||||
// Write directly from `data` until it can be fit into the buffer
|
||||
let (amount, result) = self
|
||||
.inner
|
||||
.write_chunked(&data[..len - self.buffer.capacity()]);
|
||||
|
||||
// Fail if write fails
|
||||
result?;
|
||||
|
||||
data = &data[amount..];
|
||||
}
|
||||
|
||||
// Store the data in the buffer
|
||||
assert!(data.len() < self.buffer.capacity());
|
||||
self.buffer.extend_from_slice(data);
|
||||
Ok(len)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> Result<(), Errno> {
|
||||
let (amount, result) = self.inner.write_chunked(&self.buffer);
|
||||
if amount != 0 {
|
||||
self.buffer.drain(..amount);
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> Write for LineWriter<W> {
|
||||
fn write(&mut self, data: &[u8]) -> Result<usize, Errno> {
|
||||
if self.need_flush {
|
||||
self.flush()?;
|
||||
}
|
||||
|
||||
let Some(nl_pos) = memchr::memrchr(b'\n', data) else {
|
||||
return self.inner.write(data);
|
||||
};
|
||||
|
||||
// Write up to the last newline and flush
|
||||
let amount = self.inner.write(&data[..nl_pos + 1])?;
|
||||
self.need_flush = true;
|
||||
if self.flush().is_err() || amount != nl_pos + 1 {
|
||||
return Ok(amount);
|
||||
}
|
||||
|
||||
// Write the rest to the buffer
|
||||
match self.inner.write(&data[nl_pos + 1..]) {
|
||||
Ok(amount_to_buffer) => Ok(amount_to_buffer + amount),
|
||||
Err(_) => Ok(amount),
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> Result<(), Errno> {
|
||||
self.inner.flush()?;
|
||||
self.need_flush = false;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> FileWriter for UnbufferedWriter<W> {
|
||||
fn reset(&mut self) {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> FileWriter for BufWriter<W> {
|
||||
fn reset(&mut self) {
|
||||
self.buffer.clear();
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> FileWriter for LineWriter<W> {
|
||||
fn reset(&mut self) {
|
||||
self.inner.reset();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
use yggdrasil_rt::io::SeekFrom;
|
||||
|
||||
use crate::header::errno::{Errno, EINTR};
|
||||
|
||||
pub mod buffered;
|
||||
|
||||
pub trait Write {
|
||||
fn write(&mut self, data: &[u8]) -> Result<usize, Errno>;
|
||||
fn flush(&mut self) -> Result<(), Errno>;
|
||||
|
||||
fn write_chunked(&mut self, data: &[u8]) -> (usize, Result<(), Errno>) {
|
||||
let mut pos = 0;
|
||||
let len = data.len();
|
||||
|
||||
while pos < len {
|
||||
match self.write(data) {
|
||||
Ok(0) => todo!(),
|
||||
Ok(n) => pos += n,
|
||||
Err(err) if err == EINTR => todo!(),
|
||||
Err(err) => return (pos, Err(err)),
|
||||
}
|
||||
}
|
||||
|
||||
(pos, Ok(()))
|
||||
}
|
||||
|
||||
fn write_all(&mut self, data: &[u8]) -> Result<(), Errno> {
|
||||
match self.write(data) {
|
||||
Ok(n) if n == data.len() => Ok(()),
|
||||
Ok(_) => todo!(), // TODO handle partial writes in write_all
|
||||
Err(err) => Err(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Read {
|
||||
fn read(&mut self, data: &mut [u8]) -> Result<usize, Errno>;
|
||||
}
|
||||
|
||||
pub trait BufRead {
|
||||
fn fill_buf(&mut self) -> Result<&[u8], Errno>;
|
||||
fn consume(&mut self, amount: usize);
|
||||
}
|
||||
|
||||
pub trait Seek {
|
||||
fn seek(&mut self, off: SeekFrom) -> Result<u64, Errno>;
|
||||
|
||||
fn stream_position(&mut self) -> Result<u64, Errno> {
|
||||
self.seek(SeekFrom::Current(0))
|
||||
}
|
||||
}
|
||||
+20
-3
@@ -1,4 +1,12 @@
|
||||
#![feature(type_alias_impl_trait, c_variadic, try_trait_v2)]
|
||||
#![feature(
|
||||
type_alias_impl_trait,
|
||||
c_variadic,
|
||||
try_trait_v2,
|
||||
slice_internals,
|
||||
arbitrary_self_types,
|
||||
new_uninit,
|
||||
maybe_uninit_slice
|
||||
)]
|
||||
#![no_std]
|
||||
|
||||
use core::ffi::{c_char, c_int};
|
||||
@@ -7,6 +15,8 @@ use alloc::{ffi::CString, vec::Vec};
|
||||
use header::unistd::environ;
|
||||
use yggdrasil_rt::process::{ExitCode, ProgramArgumentInner};
|
||||
|
||||
use crate::header::stdio;
|
||||
|
||||
extern crate alloc;
|
||||
extern crate yggdrasil_rt;
|
||||
|
||||
@@ -14,10 +24,11 @@ pub mod header;
|
||||
|
||||
pub mod allocator;
|
||||
pub mod error;
|
||||
pub mod file;
|
||||
pub mod io;
|
||||
pub mod path;
|
||||
pub mod process;
|
||||
pub mod sync;
|
||||
pub mod traits;
|
||||
pub mod types;
|
||||
pub mod util;
|
||||
|
||||
@@ -42,6 +53,10 @@ unsafe fn setup_env(arg: usize) {
|
||||
environ = C_ENVS.as_mut_ptr();
|
||||
}
|
||||
|
||||
unsafe fn cleanup() {
|
||||
stdio::cleanup();
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn _start(arg: usize) -> ! {
|
||||
extern "C" {
|
||||
@@ -49,12 +64,14 @@ unsafe extern "C" fn _start(arg: usize) -> ! {
|
||||
}
|
||||
|
||||
setup_env(arg);
|
||||
header::stdio::setup_default_files();
|
||||
stdio::setup_default_files();
|
||||
|
||||
// TODO setup signals, allocator, etc.
|
||||
|
||||
let code = main(C_ARGS.len() as _, C_ARGS.as_ptr());
|
||||
|
||||
cleanup();
|
||||
|
||||
process::exit(code)
|
||||
}
|
||||
|
||||
|
||||
@@ -87,6 +87,8 @@ impl<T> Mutex<T> {
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T> Sync for Mutex<T> {}
|
||||
|
||||
impl<T> Deref for MutexGuard<'_, T> {
|
||||
type Target = T;
|
||||
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
use core::fmt;
|
||||
|
||||
use crate::header::errno::Errno;
|
||||
|
||||
pub trait Write: fmt::Write {
|
||||
fn write(&mut self, data: &[u8]) -> Result<usize, Errno>;
|
||||
|
||||
fn putc(&mut self, ch: u8) -> Result<(), Errno> {
|
||||
self.write(&[ch])?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Read {
|
||||
fn read(&mut self, data: &mut [u8]) -> Result<usize, Errno>;
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
#![allow(non_camel_case_types)]
|
||||
|
||||
use core::ffi::c_int;
|
||||
|
||||
pub type wchar_t = i32;
|
||||
|
||||
Reference in New Issue
Block a user