Most of <stdio.h>
This commit is contained in:
+2
-1
@@ -9,6 +9,7 @@ crate-type = ["staticlib"]
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
yggdrasil-rt = { git = "https://git.alnyan.me/yggdrasil/yggdrasil-rt.git" }
|
yggdrasil-rt = { git = "https://git.alnyan.me/yggdrasil/yggdrasil-rt.git" }
|
||||||
libyalloc = { git = "https://git.alnyan.me/yggdrasil/libyalloc.git" }
|
libyalloc = { git = "https://git.alnyan.me/yggdrasil/libyalloc.git" }
|
||||||
|
bitflags = { version = "2.4.1" }
|
||||||
|
|
||||||
[build-dependencies]
|
[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 printf(const char *format, ...);
|
||||||
int fprintf(FILE *stream, 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
|
#endif
|
||||||
|
|||||||
+13
-1
@@ -4,16 +4,21 @@ use core::{
|
|||||||
ops::{ControlFlow, FromResidual, Try},
|
ops::{ControlFlow, FromResidual, Try},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::header::errno::Errno;
|
use crate::header::{errno::Errno, stdio::EOF};
|
||||||
|
|
||||||
pub trait CZeroResult {
|
pub trait CZeroResult {
|
||||||
fn into_zero_status(self) -> c_int;
|
fn into_zero_status(self) -> c_int;
|
||||||
|
fn into_eof_status(self) -> c_int;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait CSizeResult {
|
pub trait CSizeResult {
|
||||||
fn into_size_status(self) -> usize;
|
fn into_size_status(self) -> usize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait TryFromExt<T>: Sized {
|
||||||
|
fn e_try_from(value: T) -> EResult<Self>;
|
||||||
|
}
|
||||||
|
|
||||||
pub trait OptionExt<T> {
|
pub trait OptionExt<T> {
|
||||||
fn e_ok_or(self, err: yggdrasil_rt::Error) -> EResult<T>;
|
fn e_ok_or(self, err: yggdrasil_rt::Error) -> EResult<T>;
|
||||||
}
|
}
|
||||||
@@ -60,6 +65,13 @@ impl CZeroResult for Result<(), Errno> {
|
|||||||
Self::Err(_) => -1,
|
Self::Err(_) => -1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn into_eof_status(self) -> c_int {
|
||||||
|
match self {
|
||||||
|
Self::Ok(_) => 0,
|
||||||
|
Self::Err(_) => EOF,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CSizeResult for Result<usize, Errno> {
|
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"
|
include_guard = "_LOCALE_H"
|
||||||
trailer = "#include <bits/locale.h>"
|
trailer = "#include <bits/locale.h>"
|
||||||
|
|
||||||
usize_is_size_t = true
|
usize_type = "size_t"
|
||||||
|
isize_type = "ssize_t"
|
||||||
|
|
||||||
[export]
|
[export]
|
||||||
include = ["lconv", "locale_t"]
|
include = ["lconv", "locale_t"]
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ no_includes = true
|
|||||||
include_guard = "_MATH_H"
|
include_guard = "_MATH_H"
|
||||||
trailer = "#include <bits/math.h>"
|
trailer = "#include <bits/math.h>"
|
||||||
|
|
||||||
usize_is_size_t = true
|
usize_type = "size_t"
|
||||||
|
isize_type = "ssize_t"
|
||||||
|
|
||||||
[export]
|
[export]
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
#![allow(nonstandard_style)]
|
#![allow(nonstandard_style, unused_variables)]
|
||||||
|
|
||||||
pub mod sys_types;
|
pub mod sys_types;
|
||||||
pub mod sys_wait;
|
pub mod sys_wait;
|
||||||
|
|||||||
@@ -1,14 +1,25 @@
|
|||||||
language = "C"
|
language = "C"
|
||||||
style = "Type"
|
style = "Type"
|
||||||
|
|
||||||
sys_includes = ["stdarg.h", "stddef.h", "stdint.h"]
|
sys_includes = ["stdarg.h", "stddef.h", "stdint.h", "sys/types.h"]
|
||||||
no_includes = true
|
no_includes = true
|
||||||
|
|
||||||
include_guard = "_STDIO_H"
|
include_guard = "_STDIO_H"
|
||||||
trailer = "#include <bits/stdio.h>"
|
trailer = "#include <bits/stdio.h>"
|
||||||
|
|
||||||
usize_is_size_t = true
|
usize_type = "size_t"
|
||||||
|
isize_type = "ssize_t"
|
||||||
|
|
||||||
[export]
|
[export]
|
||||||
|
include = ["fpos_t"]
|
||||||
# Varargs are broken for these
|
# 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 core::{
|
||||||
|
ffi::{c_char, c_int, c_long, c_void, CStr},
|
||||||
use crate::{
|
ptr::null_mut,
|
||||||
error::CSizeResult,
|
|
||||||
traits::{Read, Write},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
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]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn fopen(pathname: *const c_char, mode: *const c_char) -> *mut FILE {
|
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]
|
#[no_mangle]
|
||||||
@@ -43,3 +129,103 @@ pub unsafe extern "C" fn fread(
|
|||||||
|
|
||||||
stream.read(data).into_size_status() / size
|
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
|
// 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 stream = stream.as_mut().unwrap();
|
||||||
let c = c as u8;
|
let c = c as u8;
|
||||||
|
|
||||||
match stream.putc(c) {
|
match stream.write(&[c]) {
|
||||||
Ok(_) => c as c_int,
|
Ok(_) => c as c_int,
|
||||||
// TODO EOF
|
Err(_) => EOF,
|
||||||
Err(_) => -1,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,16 +34,115 @@ unsafe extern "C" fn putchar(c: c_int) -> c_int {
|
|||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
unsafe extern "C" fn puts(s: *const c_char) -> c_int {
|
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]
|
#[no_mangle]
|
||||||
unsafe extern "C" fn fputs(s: *const c_char, stream: *mut FILE) -> c_int {
|
unsafe extern "C" fn fputs(s: *const c_char, stream: *mut FILE) -> c_int {
|
||||||
let stream = stream.as_mut().unwrap();
|
let stream = stream.as_mut().unwrap();
|
||||||
if s.is_null() {
|
if s.is_null() {
|
||||||
return stream.puts(b"(nil)").into_zero_status();
|
panic!();
|
||||||
}
|
}
|
||||||
let s = CStr::from_ptr(s);
|
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 alloc::{boxed::Box, collections::BTreeSet, vec::Vec};
|
||||||
use yggdrasil_rt::{io::RawFd, sys as syscall};
|
use bitflags::bitflags;
|
||||||
|
use yggdrasil_rt::{
|
||||||
|
io::{FileMode, OpenOptions, RawFd, SeekFrom},
|
||||||
|
path::Path,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
error::EResult,
|
error::{EResult, TryFromExt},
|
||||||
|
file::{File, FileBacking},
|
||||||
|
io::{
|
||||||
|
buffered::{BufWriter, FileWriter, LineWriter, ReadBuffer, UnbufferedWriter},
|
||||||
|
BufRead, Read, Seek, Write,
|
||||||
|
},
|
||||||
sync::{Mutex, RawMutex},
|
sync::{Mutex, RawMutex},
|
||||||
traits::{Read, Write},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::errno::Errno;
|
use super::errno::Errno;
|
||||||
|
|
||||||
// TODO:
|
// TODO:
|
||||||
//
|
|
||||||
// fpos_t
|
|
||||||
// off_t -> sys/types.h
|
|
||||||
// ssize_t -> sys/types.h
|
|
||||||
//
|
|
||||||
// BUFSIZ
|
|
||||||
// L_ctermid
|
// L_ctermid
|
||||||
// L_tmpnam
|
// L_tmpnam
|
||||||
//
|
//
|
||||||
// _IOFBF
|
|
||||||
// _IOLBF
|
|
||||||
// _IONBF
|
|
||||||
//
|
|
||||||
// SEEK_CUR
|
// SEEK_CUR
|
||||||
// SEEK_END
|
// SEEK_END
|
||||||
// SEEK_SET
|
// SEEK_SET
|
||||||
@@ -38,196 +39,427 @@ use super::errno::Errno;
|
|||||||
// P_tmpdir
|
// P_tmpdir
|
||||||
|
|
||||||
/*
|
/*
|
||||||
void clearerr(FILE *);
|
|
||||||
char *ctermid(char *);
|
char *ctermid(char *);
|
||||||
int dprintf(int, const char *restrict, ...)
|
|
||||||
int fclose(FILE *);
|
|
||||||
FILE *fdopen(int, const char *);
|
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 *);
|
void flockfile(FILE *);
|
||||||
FILE *fmemopen(void *restrict, size_t, const char *restrict);
|
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 *freopen(const char *restrict, const char *restrict,
|
||||||
FILE *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 *);
|
int ftrylockfile(FILE *);
|
||||||
void funlockfile(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 *);
|
FILE *open_memstream(char **, size_t *);
|
||||||
int pclose(FILE *);
|
int pclose(FILE *);
|
||||||
void perror(const char *);
|
void perror(const char *);
|
||||||
FILE *popen(const char *, 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 remove(const char *);
|
||||||
int rename(const char *, const char *);
|
int rename(const char *, const char *);
|
||||||
int renameat(int, const char *, int, 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 *);
|
char *tempnam(const char *, const char *);
|
||||||
FILE *tmpfile(void);
|
FILE *tmpfile(void);
|
||||||
char *tmpnam(char *);
|
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 file;
|
||||||
mod get_put;
|
mod get_put;
|
||||||
mod printf;
|
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 {
|
pub struct FILE {
|
||||||
lock: RawMutex,
|
lock: RawMutex,
|
||||||
|
|
||||||
file: FileInner,
|
inner: FileBacking,
|
||||||
|
output: Box<dyn FileWriter>,
|
||||||
|
read_buffer: Option<ReadBuffer<'static>>,
|
||||||
|
|
||||||
error: bool,
|
flags: FileFlags,
|
||||||
eof: bool,
|
|
||||||
|
|
||||||
buffer: Vec<u8>,
|
ungetc: Vec<u8>,
|
||||||
unget_buffer: Vec<u8>,
|
|
||||||
read_pos: usize,
|
|
||||||
read_size: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FileInner {
|
// NOTE:
|
||||||
unsafe fn read(&mut self, data: &mut [u8]) -> Result<usize, Errno> {
|
// man setvbuf(3):
|
||||||
let count = EResult::from(syscall::read(self.0, data))?;
|
// The setvbuf() function may be used only after opening a stream and
|
||||||
Ok(count)
|
// before any other operations have been performed on it.
|
||||||
}
|
last_operation: Option<Direction>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FILE {
|
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 {
|
Self {
|
||||||
lock: RawMutex::new(),
|
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,
|
flags,
|
||||||
error: false,
|
|
||||||
|
|
||||||
buffer: vec![0; 1024],
|
ungetc: Vec::new(),
|
||||||
unget_buffer: vec![],
|
|
||||||
read_pos: 0,
|
last_operation: None,
|
||||||
read_size: 0,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn puts(&mut self, data: &[u8]) -> Result<(), Errno> {
|
pub unsafe fn new_builtin(fd: RawFd, flags: FileFlags) -> Self {
|
||||||
self.write(data)?;
|
let inner = FileBacking::File(File::new(fd));
|
||||||
self.write(b"\n")?;
|
Self {
|
||||||
Ok(())
|
lock: RawMutex::new(),
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn fill_buf(&mut self) -> Result<&[u8], Errno> {
|
output: Box::new(LineWriter::with_capacity(inner.make_ref(), BUFSIZ)),
|
||||||
if self.read_pos == self.read_size {
|
inner,
|
||||||
self.read_size = match self.file.read(&mut self.buffer) {
|
read_buffer: Some(ReadBuffer::owned(BUFSIZ)),
|
||||||
Ok(0) => {
|
|
||||||
self.eof = true;
|
flags: FileFlags::BUILTIN | flags,
|
||||||
0
|
|
||||||
}
|
ungetc: Vec::new(),
|
||||||
Ok(n) => n,
|
|
||||||
Err(err) => {
|
last_operation: None,
|
||||||
self.error = true;
|
|
||||||
return Err(err);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
self.read_pos = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(&self.buffer[self.read_pos..self.read_size])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn consume(&mut self, len: usize) {
|
pub unsafe fn close(self: *mut Self) -> Result<(), Errno> {
|
||||||
self.read_pos = (self.read_pos + len).min(self.read_size);
|
// 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> {
|
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());
|
self.set_direction(Direction::Read)?;
|
||||||
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());
|
|
||||||
|
|
||||||
|
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]);
|
data[..len].copy_from_slice(&buf[..len]);
|
||||||
len
|
|
||||||
};
|
|
||||||
unsafe {
|
|
||||||
self.consume(len);
|
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 {
|
unsafe fn ungetc_unlocked(&mut self, ch: u8) -> bool {
|
||||||
fn read(&mut self, data: &mut [u8]) -> Result<usize, Errno> {
|
// ungetc() for write doesn't make any sense
|
||||||
self.lock.lock();
|
if self.set_direction(Direction::Read).is_err() {
|
||||||
let result = unsafe { self.read_unlocked(data) };
|
return false;
|
||||||
unsafe {
|
|
||||||
self.lock.release();
|
|
||||||
}
|
}
|
||||||
result
|
if self.ungetc.len() == UNGETC_MAX {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
self.ungetc.push(ch);
|
||||||
|
true
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl Write for FILE {
|
pub fn ungetc(&mut self, ch: u8) -> bool {
|
||||||
fn write(&mut self, data: &[u8]) -> Result<usize, Errno> {
|
locked_op!(self, self.ungetc_unlocked(ch))
|
||||||
// TODO
|
}
|
||||||
self.lock.lock();
|
|
||||||
let count = EResult::from(unsafe { syscall::write(self.file.0, data) })?;
|
fn reset(&mut self) {
|
||||||
unsafe {
|
if let Some(read_buffer) = self.read_buffer.as_mut() {
|
||||||
self.lock.release();
|
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 {
|
impl fmt::Write for FILE {
|
||||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||||
match self.write(s.as_bytes()) {
|
self.write_all(s.as_bytes()).map_err(|_| fmt::Error)
|
||||||
Ok(_) => Ok(()),
|
}
|
||||||
Err(_) => 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]
|
#[no_mangle]
|
||||||
pub static mut stderr: *mut FILE = ptr::null_mut();
|
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() {
|
pub unsafe fn setup_default_files() {
|
||||||
let stdin_ = Box::into_raw(Box::new(FILE::new_builtin(RawFd::STDIN)));
|
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)));
|
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)));
|
let stderr_ = Box::into_raw(Box::new(FILE::new_builtin(RawFd::STDERR, FileFlags::WRITE)));
|
||||||
|
|
||||||
stdin = stdin_;
|
stdin = stdin_;
|
||||||
stdout = stdout_;
|
stdout = stdout_;
|
||||||
stderr = stderr_;
|
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 alloc::{format, string::String};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
header::errno::{Errno, EINVAL},
|
header::errno::{Errno, EINVAL},
|
||||||
traits::Write,
|
io::Write,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::format::FmtOpts;
|
use super::format::FmtOpts;
|
||||||
@@ -30,7 +30,7 @@ fn float_exp(mut val: c_double) -> (c_double, isize) {
|
|||||||
(val, exp)
|
(val, exp)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fmt_float_exp<W: Write>(
|
fn fmt_float_exp<W: Write + fmt::Write>(
|
||||||
val: c_double,
|
val: c_double,
|
||||||
exp: isize,
|
exp: isize,
|
||||||
exp_fmt: u8,
|
exp_fmt: u8,
|
||||||
@@ -38,9 +38,6 @@ fn fmt_float_exp<W: Write>(
|
|||||||
precision: usize,
|
precision: usize,
|
||||||
opts: &FmtOpts,
|
opts: &FmtOpts,
|
||||||
) -> Result<usize, Errno> {
|
) -> Result<usize, Errno> {
|
||||||
// TODO padding
|
|
||||||
use core::fmt::Write as FW;
|
|
||||||
|
|
||||||
let mut exp2 = exp;
|
let mut exp2 = exp;
|
||||||
let mut exp_len = 1;
|
let mut exp_len = 1;
|
||||||
while exp2 >= 10 {
|
while exp2 >= 10 {
|
||||||
@@ -65,7 +62,7 @@ fn fmt_float_string(val: c_double, precision: usize) -> String {
|
|||||||
string
|
string
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fmt_float_finite<W: Write>(
|
fn fmt_float_finite<W: Write + fmt::Write>(
|
||||||
val: c_double,
|
val: c_double,
|
||||||
output: &mut W,
|
output: &mut W,
|
||||||
precision: usize,
|
precision: usize,
|
||||||
@@ -78,7 +75,7 @@ fn fmt_float_finite<W: Write>(
|
|||||||
Ok(lpad + flen + rpad)
|
Ok(lpad + flen + rpad)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fmt_float_nonfinite<W: Write>(
|
fn fmt_float_nonfinite<W: Write + fmt::Write>(
|
||||||
val: c_double,
|
val: c_double,
|
||||||
output: &mut W,
|
output: &mut W,
|
||||||
upper: bool,
|
upper: bool,
|
||||||
@@ -108,7 +105,7 @@ fn fmt_float_nonfinite<W: Write>(
|
|||||||
Ok(len + lpad + flen + rpad)
|
Ok(len + lpad + flen + rpad)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fmt_float<W: Write>(
|
pub fn fmt_float<W: Write + fmt::Write>(
|
||||||
val: c_double,
|
val: c_double,
|
||||||
output: &mut W,
|
output: &mut W,
|
||||||
upper: bool,
|
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,
|
val: c_double,
|
||||||
output: &mut W,
|
output: &mut W,
|
||||||
upper: bool,
|
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,
|
val: c_double,
|
||||||
output: &mut W,
|
output: &mut W,
|
||||||
upper: bool,
|
upper: bool,
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
use core::ffi::{
|
use core::{
|
||||||
c_char, c_double, c_int, c_long, c_longlong, c_short, c_uchar, c_uint, c_ulong, c_ulonglong,
|
ffi::{
|
||||||
c_ushort, CStr, VaList,
|
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 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};
|
use super::float::{fmt_float, fmt_float_any, fmt_float_scientific};
|
||||||
|
|
||||||
@@ -140,7 +143,7 @@ impl FmtSize {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl FmtOpts {
|
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);
|
let pad = self.width - core::cmp::min(self.width, len);
|
||||||
for _ in 0..pad {
|
for _ in 0..pad {
|
||||||
output.write(&[self.pad_char])?;
|
output.write(&[self.pad_char])?;
|
||||||
@@ -148,7 +151,11 @@ impl FmtOpts {
|
|||||||
Ok(pad)
|
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 {
|
if !self.left_adjust {
|
||||||
self.pad(output, len)
|
self.pad(output, len)
|
||||||
} else {
|
} 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 {
|
if self.left_adjust {
|
||||||
self.pad(output, len)
|
self.pad(output, len)
|
||||||
} else {
|
} else {
|
||||||
@@ -164,7 +175,7 @@ impl FmtOpts {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fmt<W: Write>(
|
pub fn fmt<W: Write + fmt::Write>(
|
||||||
&self,
|
&self,
|
||||||
spec: FmtSpec,
|
spec: FmtSpec,
|
||||||
output: &mut W,
|
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};
|
use self::format::{FmtOpts, FmtRadix, FmtSign, FmtSize, FmtSpec};
|
||||||
|
|
||||||
@@ -9,14 +14,86 @@ use super::{stdout, FILE};
|
|||||||
mod float;
|
mod float;
|
||||||
mod format;
|
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 fmt = format.into_iter();
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
|
|
||||||
while let Some(&c) = fmt.next() {
|
while let Some(&c) = fmt.next() {
|
||||||
if c != b'%' {
|
if c != b'%' {
|
||||||
output.putc(c)?;
|
count += output.write(&[c])?;
|
||||||
count += 1;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,3 +208,74 @@ unsafe extern "C" fn vfprintf(stream: *mut FILE, format: *const c_char, ap: VaLi
|
|||||||
Err(_) => -1,
|
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"
|
include_guard = "_STDLIB_H"
|
||||||
|
|
||||||
usize_is_size_t = true
|
usize_type = "size_t"
|
||||||
|
isize_type = "ssize_t"
|
||||||
|
|
||||||
[export]
|
[export]
|
||||||
exclude = []
|
exclude = []
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ no_includes = true
|
|||||||
|
|
||||||
include_guard = "_STRING_H"
|
include_guard = "_STRING_H"
|
||||||
|
|
||||||
usize_is_size_t = true
|
usize_type = "size_t"
|
||||||
|
isize_type = "ssize_t"
|
||||||
|
|
||||||
[export]
|
[export]
|
||||||
|
|||||||
@@ -1,10 +1,30 @@
|
|||||||
language = "C"
|
language = "C"
|
||||||
style = "Type"
|
style = "Type"
|
||||||
|
|
||||||
sys_includes = ["stddef.h"]
|
sys_includes = ["stdint.h", "stddef.h"]
|
||||||
no_includes = true
|
no_includes = true
|
||||||
|
|
||||||
include_guard = "_SYS_TYPES_H"
|
include_guard = "_SYS_TYPES_H"
|
||||||
|
|
||||||
[export]
|
[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 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"
|
include_guard = "_SYS_WAIT_H"
|
||||||
|
|
||||||
usize_is_size_t = true
|
usize_type = "size_t"
|
||||||
|
isize_type = "ssize_t"
|
||||||
|
|
||||||
[export]
|
[export]
|
||||||
exclude = []
|
exclude = []
|
||||||
|
|||||||
@@ -7,7 +7,8 @@ no_includes = true
|
|||||||
include_guard = "_UNISTD_H"
|
include_guard = "_UNISTD_H"
|
||||||
trailer = "#include <bits/unistd.h>"
|
trailer = "#include <bits/unistd.h>"
|
||||||
|
|
||||||
usize_is_size_t = true
|
usize_type = "size_t"
|
||||||
|
isize_type = "ssize_t"
|
||||||
|
|
||||||
[export]
|
[export]
|
||||||
exclude = []
|
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]
|
#![no_std]
|
||||||
|
|
||||||
use core::ffi::{c_char, c_int};
|
use core::ffi::{c_char, c_int};
|
||||||
@@ -7,6 +15,8 @@ use alloc::{ffi::CString, vec::Vec};
|
|||||||
use header::unistd::environ;
|
use header::unistd::environ;
|
||||||
use yggdrasil_rt::process::{ExitCode, ProgramArgumentInner};
|
use yggdrasil_rt::process::{ExitCode, ProgramArgumentInner};
|
||||||
|
|
||||||
|
use crate::header::stdio;
|
||||||
|
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
extern crate yggdrasil_rt;
|
extern crate yggdrasil_rt;
|
||||||
|
|
||||||
@@ -14,10 +24,11 @@ pub mod header;
|
|||||||
|
|
||||||
pub mod allocator;
|
pub mod allocator;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
|
pub mod file;
|
||||||
|
pub mod io;
|
||||||
pub mod path;
|
pub mod path;
|
||||||
pub mod process;
|
pub mod process;
|
||||||
pub mod sync;
|
pub mod sync;
|
||||||
pub mod traits;
|
|
||||||
pub mod types;
|
pub mod types;
|
||||||
pub mod util;
|
pub mod util;
|
||||||
|
|
||||||
@@ -42,6 +53,10 @@ unsafe fn setup_env(arg: usize) {
|
|||||||
environ = C_ENVS.as_mut_ptr();
|
environ = C_ENVS.as_mut_ptr();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsafe fn cleanup() {
|
||||||
|
stdio::cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
unsafe extern "C" fn _start(arg: usize) -> ! {
|
unsafe extern "C" fn _start(arg: usize) -> ! {
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@@ -49,12 +64,14 @@ unsafe extern "C" fn _start(arg: usize) -> ! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setup_env(arg);
|
setup_env(arg);
|
||||||
header::stdio::setup_default_files();
|
stdio::setup_default_files();
|
||||||
|
|
||||||
// TODO setup signals, allocator, etc.
|
// TODO setup signals, allocator, etc.
|
||||||
|
|
||||||
let code = main(C_ARGS.len() as _, C_ARGS.as_ptr());
|
let code = main(C_ARGS.len() as _, C_ARGS.as_ptr());
|
||||||
|
|
||||||
|
cleanup();
|
||||||
|
|
||||||
process::exit(code)
|
process::exit(code)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -87,6 +87,8 @@ impl<T> Mutex<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsafe impl<T> Sync for Mutex<T> {}
|
||||||
|
|
||||||
impl<T> Deref for MutexGuard<'_, T> {
|
impl<T> Deref for MutexGuard<'_, T> {
|
||||||
type Target = 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)]
|
#![allow(non_camel_case_types)]
|
||||||
|
|
||||||
use core::ffi::c_int;
|
|
||||||
|
|
||||||
pub type wchar_t = i32;
|
pub type wchar_t = i32;
|
||||||
|
|||||||
Reference in New Issue
Block a user