Most of <stdio.h>

This commit is contained in:
2024-01-09 19:30:43 +02:00
parent ab268f1fb6
commit 34c360d3e8
28 changed files with 1589 additions and 222 deletions
+2 -1
View File
@@ -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" }
+7
View File
@@ -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
View File
@@ -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
View File
@@ -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),
}
}
}
+2 -1
View File
@@ -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"]
+2 -1
View File
@@ -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
View File
@@ -1,4 +1,4 @@
#![allow(nonstandard_style)]
#![allow(nonstandard_style, unused_variables)]
pub mod sys_types;
pub mod sys_wait;
+14 -3
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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();
}
}
+8 -11
View File
@@ -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,
+19 -8
View File
@@ -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,
+153 -5
View File
@@ -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,
}
}
+42
View File
@@ -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!()
}
+90
View File
@@ -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!()
}
+2 -1
View File
@@ -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 = []
+2 -1
View File
@@ -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]
+22 -2
View File
@@ -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"
]
+29 -1
View File
@@ -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;
+2 -1
View File
@@ -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 = []
+2 -1
View File
@@ -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 = []
+231
View File
@@ -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();
}
}
+51
View File
@@ -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
View File
@@ -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)
}
+2
View File
@@ -87,6 +87,8 @@ impl<T> Mutex<T> {
}
}
unsafe impl<T> Sync for Mutex<T> {}
impl<T> Deref for MutexGuard<'_, T> {
type Target = T;
-16
View File
@@ -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>;
}
-2
View File
@@ -1,5 +1,3 @@
#![allow(non_camel_case_types)]
use core::ffi::c_int;
pub type wchar_t = i32;