From ab268f1fb6f3aac1e3354b3175c7d86ca7489fa9 Mon Sep 17 00:00:00 2001 From: Mark Poliakov Date: Mon, 8 Jan 2024 18:35:01 +0200 Subject: [PATCH] Initial commit: most of --- .gitignore | 1 + Cargo.toml | 14 ++ build.rs | 54 +++++ include/bits/errno.h | 6 + include/bits/locale.h | 6 + include/bits/math.h | 6 + include/bits/stdio.h | 7 + include/bits/string.h | 11 + include/bits/unistd.h | 8 + include/bits/wchar.h | 16 ++ src/allocator/mod.rs | 1 + src/allocator/rust.rs | 4 + src/c_api.rs | 1 + src/error.rs | 116 +++++++++++ src/file.rs | 3 + src/header/errno/cbindgen.toml | 10 + src/header/errno/mod.rs | 174 ++++++++++++++++ src/header/locale/cbindgen.toml | 13 ++ src/header/locale/mod.rs | 84 ++++++++ src/header/math/cbindgen.toml | 12 ++ src/header/math/mod.rs | 1 + src/header/mod.rs | 12 ++ src/header/stdio/cbindgen.toml | 14 ++ src/header/stdio/file.rs | 45 +++++ src/header/stdio/get_put.rs | 47 +++++ src/header/stdio/mod.rs | 250 +++++++++++++++++++++++ src/header/stdio/printf/float.rs | 170 ++++++++++++++++ src/header/stdio/printf/format.rs | 303 +++++++++++++++++++++++++++ src/header/stdio/printf/mod.rs | 133 ++++++++++++ src/header/stdlib/cbindgen.toml | 12 ++ src/header/stdlib/mod.rs | 4 + src/header/string/cbindgen.toml | 11 + src/header/string/mem.rs | 73 +++++++ src/header/string/mod.rs | 9 + src/header/string/str.rs | 315 +++++++++++++++++++++++++++++ src/header/sys_types/cbindgen.toml | 10 + src/header/sys_types/mod.rs | 3 + src/header/sys_wait/cbindgen.toml | 12 ++ src/header/sys_wait/mod.rs | 34 ++++ src/header/unistd/cbindgen.toml | 13 ++ src/header/unistd/mod.rs | 114 +++++++++++ src/lib.rs | 67 ++++++ src/path.rs | 70 +++++++ src/process.rs | 27 +++ src/sync.rs | 110 ++++++++++ src/traits.rs | 16 ++ src/types.rs | 5 + src/util.rs | 79 ++++++++ 48 files changed, 2506 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 build.rs create mode 100644 include/bits/errno.h create mode 100644 include/bits/locale.h create mode 100644 include/bits/math.h create mode 100644 include/bits/stdio.h create mode 100644 include/bits/string.h create mode 100644 include/bits/unistd.h create mode 100644 include/bits/wchar.h create mode 100644 src/allocator/mod.rs create mode 100644 src/allocator/rust.rs create mode 100644 src/c_api.rs create mode 100644 src/error.rs create mode 100644 src/file.rs create mode 100644 src/header/errno/cbindgen.toml create mode 100644 src/header/errno/mod.rs create mode 100644 src/header/locale/cbindgen.toml create mode 100644 src/header/locale/mod.rs create mode 100644 src/header/math/cbindgen.toml create mode 100644 src/header/math/mod.rs create mode 100644 src/header/mod.rs create mode 100644 src/header/stdio/cbindgen.toml create mode 100644 src/header/stdio/file.rs create mode 100644 src/header/stdio/get_put.rs create mode 100644 src/header/stdio/mod.rs create mode 100644 src/header/stdio/printf/float.rs create mode 100644 src/header/stdio/printf/format.rs create mode 100644 src/header/stdio/printf/mod.rs create mode 100644 src/header/stdlib/cbindgen.toml create mode 100644 src/header/stdlib/mod.rs create mode 100644 src/header/string/cbindgen.toml create mode 100644 src/header/string/mem.rs create mode 100644 src/header/string/mod.rs create mode 100644 src/header/string/str.rs create mode 100644 src/header/sys_types/cbindgen.toml create mode 100644 src/header/sys_types/mod.rs create mode 100644 src/header/sys_wait/cbindgen.toml create mode 100644 src/header/sys_wait/mod.rs create mode 100644 src/header/unistd/cbindgen.toml create mode 100644 src/header/unistd/mod.rs create mode 100644 src/lib.rs create mode 100644 src/path.rs create mode 100644 src/process.rs create mode 100644 src/sync.rs create mode 100644 src/traits.rs create mode 100644 src/types.rs create mode 100644 src/util.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..f3c801e --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "ygglibc" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["staticlib"] + +[dependencies] +yggdrasil-rt = { git = "https://git.alnyan.me/yggdrasil/yggdrasil-rt.git" } +libyalloc = { git = "https://git.alnyan.me/yggdrasil/libyalloc.git" } + +[build-dependencies] +cbindgen = "0.26.0" diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..70ca870 --- /dev/null +++ b/build.rs @@ -0,0 +1,54 @@ +use std::{ + env, + fs::{self, DirEntry}, + path::Path, +}; + +fn include_dir(d: &DirEntry) -> bool { + d.metadata().map(|m| m.is_dir()).unwrap_or(false) + && d.path() + .iter() + .nth(2) + .map_or(false, |c| c.to_str().map_or(false, |x| !x.starts_with("_"))) +} + +fn generate_header>(config_path: P) { + let config_path = config_path.as_ref(); + let relative_path = config_path + .strip_prefix("src/header") + .ok() + .and_then(|p| p.parent()) + .and_then(|p| p.to_str()) + .unwrap() + .replace("_", "/"); + // TODO use outer workspace's target directory? + let header_path = Path::new("target/include") + .join(&relative_path) + .with_extension("h"); + let mod_path = config_path.with_file_name("mod.rs"); + + let config = cbindgen::Config::from_file(config_path).unwrap(); + + cbindgen::Builder::new() + .with_config(config) + .with_src(mod_path) + .generate() + .unwrap() + .write_to_file(header_path); +} + +fn main() { + fs::read_dir("src/header") + .unwrap() + .into_iter() + .filter_map(Result::ok) + .filter(|d| include_dir(d)) + .map(|d| d.path().as_path().join("cbindgen.toml")) + .filter(|p| p.exists()) + .for_each(|p| { + println!("cargo:rerun-if-changed={:?}", p.parent().unwrap()); + println!("cargo:rerun-if-changed={:?}", p); + println!("cargo:rerun-if-changed={:?}", p.with_file_name("mod.rs")); + generate_header(&p); + }); +} diff --git a/include/bits/errno.h b/include/bits/errno.h new file mode 100644 index 0000000..e00a91c --- /dev/null +++ b/include/bits/errno.h @@ -0,0 +1,6 @@ +#ifndef _YGGDRASIL_ERRNO_H +#define _YGGDRASIL_ERRNO_H 1 + +extern int errno; + +#endif diff --git a/include/bits/locale.h b/include/bits/locale.h new file mode 100644 index 0000000..e8d45be --- /dev/null +++ b/include/bits/locale.h @@ -0,0 +1,6 @@ +#ifndef _YGGDRASIL_LOCALE_H +#define _YGGDRASIL_LOCALE_H 1 + +#define LC_GLOBAL_LOCALE ((locale_t) -1L) + +#endif diff --git a/include/bits/math.h b/include/bits/math.h new file mode 100644 index 0000000..cf38b7b --- /dev/null +++ b/include/bits/math.h @@ -0,0 +1,6 @@ +#ifndef _YGGDRASIL_MATH_H +#define _YGGDRASIL_MATH_H 1 + +#define INFINITY __builtin_inff() + +#endif diff --git a/include/bits/stdio.h b/include/bits/stdio.h new file mode 100644 index 0000000..6a939aa --- /dev/null +++ b/include/bits/stdio.h @@ -0,0 +1,7 @@ +#ifndef _YGGDRASIL_STDIO_H +#define _YGGDRASIL_STDIO_H 1 + +int printf(const char *format, ...); +int fprintf(FILE *stream, const char *format, ...); + +#endif diff --git a/include/bits/string.h b/include/bits/string.h new file mode 100644 index 0000000..4683e31 --- /dev/null +++ b/include/bits/string.h @@ -0,0 +1,11 @@ +#ifndef _YGGDRASIL_STRING_H +#define _YGGDRASIL_STRING_H 1 +#include + +void *memcpy(void *dst, const void *src, size_t n); +void *memmove(void *dst, const void *src, size_t n); +int memcmp(const void *a, const void *b, size_t n); + +size_t strlen(const char *a); + +#endif diff --git a/include/bits/unistd.h b/include/bits/unistd.h new file mode 100644 index 0000000..1d8cf6a --- /dev/null +++ b/include/bits/unistd.h @@ -0,0 +1,8 @@ +#ifndef _YGGDRASIL_UNISTD_H +#define _YGGDRASIL_UNISTD_H 1 + +int execl(const char *pathname, const char *arg, ...); +int execlp(const char *file, const char *arg, ...); +int execle(const char *pathname, const char *arg, ...); + +#endif diff --git a/include/bits/wchar.h b/include/bits/wchar.h new file mode 100644 index 0000000..9cfa960 --- /dev/null +++ b/include/bits/wchar.h @@ -0,0 +1,16 @@ +#ifndef _YGGDRASIL_WCHAR_H +#define _YGGDRASIL_WCHAR_H 1 + +#include + +#ifndef _WCHAR_T +#define _WCHAR_T + #ifndef __WCHAR_TYPE__ + #define __WCHAR_TYPE__ int32_t + #endif + typedef __WCHAR_TYPE__ wchar_t; +#endif + +#define __need_size_t +#define __need_NULL +#include diff --git a/src/allocator/mod.rs b/src/allocator/mod.rs new file mode 100644 index 0000000..0ad9e7d --- /dev/null +++ b/src/allocator/mod.rs @@ -0,0 +1 @@ +pub mod rust; diff --git a/src/allocator/rust.rs b/src/allocator/rust.rs new file mode 100644 index 0000000..79cb3ab --- /dev/null +++ b/src/allocator/rust.rs @@ -0,0 +1,4 @@ +use libyalloc::GlobalAllocator; + +#[global_allocator] +static GLOBAL_ALLOCATOR: GlobalAllocator = GlobalAllocator; diff --git a/src/c_api.rs b/src/c_api.rs new file mode 100644 index 0000000..918c3c7 --- /dev/null +++ b/src/c_api.rs @@ -0,0 +1 @@ +#![allow(non_camel_case_types)] diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..b6d90a0 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,116 @@ +use core::{ + convert::Infallible, + ffi::c_int, + ops::{ControlFlow, FromResidual, Try}, +}; + +use crate::header::errno::Errno; + +pub trait CZeroResult { + fn into_zero_status(self) -> c_int; +} + +pub trait CSizeResult { + fn into_size_status(self) -> usize; +} + +pub trait OptionExt { + fn e_ok_or(self, err: yggdrasil_rt::Error) -> EResult; +} + +// TODO thread_local +#[no_mangle] +pub static mut errno: Errno = Errno(0); + +pub enum EResult { + Ok(T), + Err(yggdrasil_rt::Error), + Errno(Errno), +} + +impl From> for EResult { + fn from(value: Result) -> Self { + match value { + Ok(value) => Self::Ok(value), + Err(error) => Self::Err(error), + } + } +} + +impl EResult { + pub fn into_set_errno(self) -> EResult { + match self { + Self::Ok(value) => Self::Ok(value), + Self::Errno(e) => Self::Errno(e), + Self::Err(err) => { + let e = Errno::from(err); + unsafe { + errno = e; + } + Self::Errno(e) + } + } + } +} + +impl CZeroResult for Result<(), Errno> { + fn into_zero_status(self) -> c_int { + match self { + Self::Ok(_) => 0, + Self::Err(_) => -1, + } + } +} + +impl CSizeResult for Result { + fn into_size_status(self) -> usize { + match self { + Self::Ok(value) => value, + Self::Err(_) => 0, + } + } +} + +impl FromResidual for EResult { + fn from_residual(residual: Errno) -> Self { + Self::Errno(residual) + } +} + +impl FromResidual for EResult { + fn from_residual(residual: yggdrasil_rt::Error) -> Self { + Self::Err(residual) + } +} + +impl FromResidual> for EResult { + fn from_residual(residual: Result) -> Self { + todo!() + } +} + +impl Try for EResult { + type Output = T; + type Residual = Result; + + fn branch(self) -> ControlFlow { + match self.into_set_errno() { + Self::Ok(value) => ControlFlow::Continue(value), + Self::Errno(e) => ControlFlow::Break(Err(e)), + Self::Err(error) => unreachable!(), + } + } + + fn from_output(output: Self::Output) -> Self { + todo!() + } +} + +impl OptionExt for Option { + fn e_ok_or(self, err: yggdrasil_rt::Error) -> EResult { + match self { + Some(value) => EResult::Ok(value), + None => EResult::Err(err), + } + } +} diff --git a/src/file.rs b/src/file.rs new file mode 100644 index 0000000..bda0b46 --- /dev/null +++ b/src/file.rs @@ -0,0 +1,3 @@ +pub trait FileImpl {} + +pub struct File {} diff --git a/src/header/errno/cbindgen.toml b/src/header/errno/cbindgen.toml new file mode 100644 index 0000000..08ef633 --- /dev/null +++ b/src/header/errno/cbindgen.toml @@ -0,0 +1,10 @@ +language = "C" +style = "Type" + +sys_includes = [] +no_includes = true + +include_guard = "_ERRNO_H" +trailer = "#include " + +[export] diff --git a/src/header/errno/mod.rs b/src/header/errno/mod.rs new file mode 100644 index 0000000..f0c6c05 --- /dev/null +++ b/src/header/errno/mod.rs @@ -0,0 +1,174 @@ +use core::ffi::{c_char, c_int, CStr}; + +macro_rules! static_cstr { + ($string:expr) => { + unsafe { + ::core::ffi::CStr::from_bytes_with_nul_unchecked(concat!($string, "\0").as_bytes()) + } + }; +} + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] +#[repr(transparent)] +pub struct Errno(pub c_int); + +pub const EPERM: Errno = Errno(1); +pub const ENOENT: Errno = Errno(2); +pub const ESRCH: Errno = Errno(3); +pub const EINTR: Errno = Errno(4); +pub const EIO: Errno = Errno(5); +pub const ENXIO: Errno = Errno(6); +pub const E2BIG: Errno = Errno(7); +pub const ENOEXEC: Errno = Errno(8); +pub const EBADF: Errno = Errno(9); +pub const ECHILD: Errno = Errno(10); +pub const EAGAIN: Errno = Errno(11); +pub const ENOMEM: Errno = Errno(12); +pub const EACCES: Errno = Errno(13); +pub const EFAULT: Errno = Errno(14); +pub const ENOTBLK: Errno = Errno(15); +pub const EBUSY: Errno = Errno(16); +pub const EEXIST: Errno = Errno(17); +pub const EXDEV: Errno = Errno(18); +pub const ENODEV: Errno = Errno(19); +pub const ENOTDIR: Errno = Errno(20); +pub const EISDIR: Errno = Errno(21); +pub const EINVAL: Errno = Errno(22); +pub const ENFILE: Errno = Errno(23); +pub const EMFILE: Errno = Errno(24); +pub const ENOTTY: Errno = Errno(25); +pub const ETXTBSY: Errno = Errno(26); +pub const EFBIG: Errno = Errno(27); +pub const ENOSPC: Errno = Errno(28); +pub const ESPIPE: Errno = Errno(29); +pub const EROFS: Errno = Errno(30); +pub const EMLINK: Errno = Errno(31); +pub const EPIPE: Errno = Errno(32); +pub const EDOM: Errno = Errno(33); +pub const ERANGE: Errno = Errno(34); +pub const MAX_ERROR: Errno = ERANGE; + +static SUCCESS: &CStr = static_cstr!("Success"); +pub static UNKNOWN_ERROR: &CStr = static_cstr!("Unknown error"); +static ERRNO_STRINGS: &[&CStr] = &[ + // 0 + SUCCESS, + // EPERM 1 + static_cstr!("Operation not permitted"), + // ENOENT 2 + static_cstr!("No such file or directory"), + // ESRCH 3 + static_cstr!("No such process"), + // EINTR 4 + static_cstr!("Interrupted system call"), + // EIO 5 + static_cstr!("Input/output error"), + // ENXIO 6 + static_cstr!("No such device or address"), + // E2BIG 7 + static_cstr!("Argument list too long"), + // ENOEXEC 8 + static_cstr!("Exec format error"), + // EBADF 9 + static_cstr!("Bad file descriptor"), + // ECHILD 10 + static_cstr!("No child processses"), + // EAGAIN 11 + static_cstr!("Resource temporarily unavailable"), + // ENOMEM 12 + static_cstr!("Cannot allocate memory"), + // EACCES 13 + static_cstr!("Permission denied"), + // EFAULT 14 + static_cstr!("Bad address"), + // ENOTBLK 15 + static_cstr!("Block device required"), + // EBUSY 16 + static_cstr!("Device or resource busy"), + // EEXIST 17 + static_cstr!("File exists"), + // EXDEV 18 + static_cstr!("Invalid cross-device link"), + // ENODEV 19 + static_cstr!("No such device"), + // ENOTDIR 20 + static_cstr!("Not a directory"), + // EISDIR 21 + static_cstr!("Is a directory"), + // EINVAL 22 + static_cstr!("Invalid argument"), + // ENFILE 23 + static_cstr!("Too many open files in system"), + // EMFILE 24 + static_cstr!("Too many open files"), + // ENOTTY 25 + static_cstr!("Inappropriate ioctl for device"), + // ETXTBSY 26 + static_cstr!("Text file busy"), + // EFBIG 27 + static_cstr!("File too large"), + // ENOSPC 28 + static_cstr!("No space left on device"), + // ESPIPE 29 + static_cstr!("Illegal seek"), + // EROFS 30 + static_cstr!("Read-only file system"), + // EMLINK 31 + static_cstr!("Too many links"), + // EPIPE 32 + static_cstr!("Broken pipe"), + // EDOM 33 + static_cstr!("Numerical argument out of domain"), + // ERANGE 34 + static_cstr!("Numerical result out of range"), +]; + +impl Errno { + pub fn to_c_str(&self) -> *const c_char { + ERRNO_STRINGS + .get(self.0 as usize) + .copied() + .unwrap_or(SUCCESS) + .as_ptr() as *const _ + } + + pub fn from_c_int(v: c_int) -> Option { + if v < 0 || v > MAX_ERROR.0 { + None + } else { + Some(Self(v)) + } + } +} + +impl From for Errno { + fn from(value: yggdrasil_rt::Error) -> Self { + use yggdrasil_rt::Error as E; + + match value { + E::TimedOut => todo!(), + E::MissingData => todo!(), + // TODO ??? + E::InvalidMemoryOperation => EPERM, + // TODO ??? + E::NotImplemented => EPERM, + // TODO ??? + E::DirectoryNotEmpty => EISDIR, + // TODO ??? + E::WouldBlock => EAGAIN, + // TODO ??? + E::InvalidOperation => EPERM, + E::DoesNotExist => ENOENT, + E::AlreadyExists => EEXIST, + E::InvalidArgument => EINVAL, + E::NotADirectory => ENOTDIR, + E::PermissionDenied => EACCES, + E::UnrecognizedExecutable => ENOEXEC, + E::ReadOnly => EROFS, + E::OutOfMemory => ENOMEM, + E::InvalidFile => EBADF, + E::Interrupted => EINTR, + E::IsADirectory => EISDIR, + } + } +} diff --git a/src/header/locale/cbindgen.toml b/src/header/locale/cbindgen.toml new file mode 100644 index 0000000..2b3b191 --- /dev/null +++ b/src/header/locale/cbindgen.toml @@ -0,0 +1,13 @@ +language = "C" +style = "Tag" + +sys_includes = ["stddef.h"] +no_includes = true + +include_guard = "_LOCALE_H" +trailer = "#include " + +usize_is_size_t = true + +[export] +include = ["lconv", "locale_t"] diff --git a/src/header/locale/mod.rs b/src/header/locale/mod.rs new file mode 100644 index 0000000..255c7b4 --- /dev/null +++ b/src/header/locale/mod.rs @@ -0,0 +1,84 @@ +use core::ffi::{c_char, c_int}; + +#[derive(Clone, Copy)] +#[repr(C)] +pub struct lconv { + pub currency_symbol: *mut c_char, + pub decimal_point: *mut c_char, + pub frac_digits: c_char, + pub grouping: *mut c_char, + pub int_curr_symbol: *mut c_char, + pub int_frac_digits: c_char, + pub int_n_cs_precedes: c_char, + pub int_n_sep_by_space: c_char, + pub int_n_sign_posn: c_char, + pub int_p_cs_precedes: c_char, + pub int_p_sep_by_space: c_char, + pub int_p_sign_posn: c_char, + pub mon_decimal_point: *mut c_char, + pub mon_grouping: *mut c_char, + pub mon_thousands_sep: *mut c_char, + pub negative_sign: *mut c_char, + pub n_cs_precedes: c_char, + pub n_sep_by_space: c_char, + pub n_sign_posn: c_char, + pub positive_sign: *mut c_char, + pub p_cs_precedes: c_char, + pub p_sep_by_space: c_char, + pub p_sign_posn: c_char, + pub thousands_sep: *mut c_char, +} + +pub struct __locale_data {} + +#[derive(Clone, Copy)] +#[repr(C)] +pub struct __locale_struct { + pub __locales: [*mut __locale_data; __LC_LAST], +} + +pub type locale_t = *mut __locale_struct; + +pub const LC_COLLATE: c_int = 0; +pub const LC_CTYPE: c_int = 1; +pub const LC_MESSAGES: c_int = 2; +pub const LC_MONETARY: c_int = 3; +pub const LC_NUMERIC: c_int = 4; +pub const LC_TIME: c_int = 5; + +pub const LC_ALL: c_int = 6; +pub const __LC_LAST: usize = 6; + +#[no_mangle] +unsafe extern "C" fn duplocale(locobj: locale_t) -> locale_t { + todo!() +} + +#[no_mangle] +unsafe extern "C" fn freelocale(locobj: locale_t) { + todo!() +} + +#[no_mangle] +unsafe extern "C" fn localeconv() -> *mut lconv { + todo!() +} + +#[no_mangle] +unsafe extern "C" fn newlocale( + category_mask: c_int, + locale: *const c_char, + base: locale_t, +) -> locale_t { + todo!() +} + +#[no_mangle] +unsafe extern "C" fn setlocale(category: c_int, locale: *const c_char) -> *mut c_char { + todo!() +} + +#[no_mangle] +unsafe extern "C" fn uselocale(locobj: locale_t) -> locale_t { + todo!() +} diff --git a/src/header/math/cbindgen.toml b/src/header/math/cbindgen.toml new file mode 100644 index 0000000..4e5c28a --- /dev/null +++ b/src/header/math/cbindgen.toml @@ -0,0 +1,12 @@ +language = "C" +style = "Type" + +sys_includes = [] +no_includes = true + +include_guard = "_MATH_H" +trailer = "#include " + +usize_is_size_t = true + +[export] diff --git a/src/header/math/mod.rs b/src/header/math/mod.rs new file mode 100644 index 0000000..d499edf --- /dev/null +++ b/src/header/math/mod.rs @@ -0,0 +1 @@ +pub const NAN: f64 = f64::NAN; diff --git a/src/header/mod.rs b/src/header/mod.rs new file mode 100644 index 0000000..583dcf8 --- /dev/null +++ b/src/header/mod.rs @@ -0,0 +1,12 @@ +#![allow(nonstandard_style)] + +pub mod sys_types; +pub mod sys_wait; + +pub mod errno; +pub mod locale; +pub mod math; +pub mod stdio; +pub mod stdlib; +pub mod string; +pub mod unistd; diff --git a/src/header/stdio/cbindgen.toml b/src/header/stdio/cbindgen.toml new file mode 100644 index 0000000..4e51ea4 --- /dev/null +++ b/src/header/stdio/cbindgen.toml @@ -0,0 +1,14 @@ +language = "C" +style = "Type" + +sys_includes = ["stdarg.h", "stddef.h", "stdint.h"] +no_includes = true + +include_guard = "_STDIO_H" +trailer = "#include " + +usize_is_size_t = true + +[export] +# Varargs are broken for these +exclude = ["printf", "fprintf"] diff --git a/src/header/stdio/file.rs b/src/header/stdio/file.rs new file mode 100644 index 0000000..78599e6 --- /dev/null +++ b/src/header/stdio/file.rs @@ -0,0 +1,45 @@ +use core::ffi::{c_char, c_void}; + +use crate::{ + error::CSizeResult, + traits::{Read, Write}, +}; + +use super::FILE; + +#[no_mangle] +pub unsafe extern "C" fn fopen(pathname: *const c_char, mode: *const c_char) -> *mut FILE { + todo!() +} + +#[no_mangle] +pub unsafe extern "C" fn fwrite( + ptr: *const 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(data).into_size_status() / size +} + +#[no_mangle] +pub unsafe extern "C" fn fread( + 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(data).into_size_status() / size +} diff --git a/src/header/stdio/get_put.rs b/src/header/stdio/get_put.rs new file mode 100644 index 0000000..d6ed374 --- /dev/null +++ b/src/header/stdio/get_put.rs @@ -0,0 +1,47 @@ +use core::ffi::{c_char, c_int, CStr}; + +use crate::{error::CZeroResult, traits::Write}; + +use super::{stdout, FILE}; + +// Chars + +#[no_mangle] +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) { + Ok(_) => c as c_int, + // TODO EOF + Err(_) => -1, + } +} + +#[no_mangle] +unsafe extern "C" fn putc(c: c_int, stream: *mut FILE) -> c_int { + fputc(c, stream) +} + +#[no_mangle] +unsafe extern "C" fn putchar(c: c_int) -> c_int { + fputc(c, stdout) +} + +// Strings + +#[no_mangle] +unsafe extern "C" fn puts(s: *const c_char) -> c_int { + fputs(s, 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(); + } + let s = CStr::from_ptr(s); + + stream.puts(s.to_bytes()).into_zero_status() +} diff --git a/src/header/stdio/mod.rs b/src/header/stdio/mod.rs new file mode 100644 index 0000000..3eaace1 --- /dev/null +++ b/src/header/stdio/mod.rs @@ -0,0 +1,250 @@ +use core::{fmt, mem::MaybeUninit, ptr}; + +use alloc::{boxed::Box, vec, vec::Vec}; +use yggdrasil_rt::{io::RawFd, sys as syscall}; + +use crate::{ + error::EResult, + 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 +// +// FILENAME_MAX +// FOPEN_MAX +// TMP_MAX +// +// EOF +// +// 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); +*/ + +mod file; +mod get_put; +mod printf; + +struct FileInner(RawFd); + +pub struct FILE { + lock: RawMutex, + + file: FileInner, + + error: bool, + eof: bool, + + buffer: Vec, + unget_buffer: Vec, + read_pos: usize, + read_size: usize, +} + +impl FileInner { + unsafe fn read(&mut self, data: &mut [u8]) -> Result { + let count = EResult::from(syscall::read(self.0, data))?; + Ok(count) + } +} + +impl FILE { + unsafe fn new_builtin(fd: RawFd) -> Self { + Self { + lock: RawMutex::new(), + + file: FileInner(fd), + + eof: false, + error: false, + + buffer: vec![0; 1024], + unget_buffer: vec![], + read_pos: 0, + read_size: 0, + } + } + + pub fn puts(&mut self, data: &[u8]) -> Result<(), Errno> { + self.write(data)?; + self.write(b"\n")?; + Ok(()) + } + + 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; + } + + 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 read_unlocked(&mut self, data: &mut [u8]) -> Result { + 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()); + + data[..len].copy_from_slice(&buf[..len]); + len + }; + unsafe { + self.consume(len); + } + Ok(len) + } +} + +impl Read for FILE { + fn read(&mut self, data: &mut [u8]) -> Result { + self.lock.lock(); + let result = unsafe { self.read_unlocked(data) }; + unsafe { + self.lock.release(); + } + result + } +} + +impl Write for FILE { + fn write(&mut self, data: &[u8]) -> Result { + // TODO + self.lock.lock(); + let count = EResult::from(unsafe { syscall::write(self.file.0, data) })?; + unsafe { + self.lock.release(); + } + 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), + } + } +} + +#[no_mangle] +pub static mut stdin: *mut FILE = ptr::null_mut(); +#[no_mangle] +pub static mut stdout: *mut FILE = ptr::null_mut(); +#[no_mangle] +pub static mut stderr: *mut FILE = ptr::null_mut(); + +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))); + + stdin = stdin_; + stdout = stdout_; + stderr = stderr_; +} diff --git a/src/header/stdio/printf/float.rs b/src/header/stdio/printf/float.rs new file mode 100644 index 0000000..daeb0df --- /dev/null +++ b/src/header/stdio/printf/float.rs @@ -0,0 +1,170 @@ +use core::{ffi::c_double, num::FpCategory}; + +use alloc::{format, string::String}; + +use crate::{ + header::errno::{Errno, EINVAL}, + traits::Write, +}; + +use super::format::FmtOpts; + +fn abs(f: c_double) -> c_double { + if f.is_sign_negative() { + -f + } else { + f + } +} + +fn float_exp(mut val: c_double) -> (c_double, isize) { + let mut exp: isize = 0; + while abs(val) >= 10.0 { + val /= 10.0; + exp += 1; + } + while c_double::EPSILON < abs(val) && abs(val) < 1.0 { + val *= 10.0; + exp -= 1; + } + (val, exp) +} + +fn fmt_float_exp( + val: c_double, + exp: isize, + exp_fmt: u8, + output: &mut W, + precision: usize, + opts: &FmtOpts, +) -> Result { + // TODO padding + use core::fmt::Write as FW; + + let mut exp2 = exp; + let mut exp_len = 1; + while exp2 >= 10 { + exp2 /= 10; + exp_len += 1; + } + + let string = fmt_float_string(val, precision); + + let f_len = output.write(string.as_bytes())?; + let e_len = match write!(output, "{}{:+03}", exp_fmt as char, exp) { + Ok(count) => count, + Err(_) => return Err(EINVAL), + }; + + Ok(f_len + 2 + 2.max(exp_len)) +} + +fn fmt_float_string(val: c_double, precision: usize) -> String { + let mut string = format!("{:.p$}", val, p = precision); + // TODO trim + string +} + +fn fmt_float_finite( + val: c_double, + output: &mut W, + precision: usize, + opts: &FmtOpts, +) -> Result { + let s = fmt_float_string(val, precision); + let lpad = opts.left_pad(output, s.len())?; + let flen = output.write(s.as_bytes())?; + let rpad = opts.right_pad(output, s.len())?; + Ok(lpad + flen + rpad) +} + +fn fmt_float_nonfinite( + val: c_double, + output: &mut W, + upper: bool, + opts: &FmtOpts, +) -> Result { + let mut len = 0; + if val.is_sign_negative() { + len += output.write(b"-")?; + } + + let nonfinite_str = match val.classify() { + FpCategory::Infinite => match upper { + false => b"inf", + true => b"INF", + }, + FpCategory::Nan => match upper { + false => b"nan", + true => b"NaN", + }, + _ => unreachable!(), + }; + + let lpad = opts.left_pad(output, len + nonfinite_str.len())?; + let flen = output.write(nonfinite_str)?; + let rpad = opts.right_pad(output, len + nonfinite_str.len())?; + + Ok(len + lpad + flen + rpad) +} + +pub fn fmt_float( + val: c_double, + output: &mut W, + upper: bool, + opts: &FmtOpts, +) -> Result { + if val.is_finite() { + fmt_float_finite(val, output, 6, opts) + } else { + fmt_float_nonfinite(val, output, upper, opts) + } +} + +pub fn fmt_float_scientific( + val: c_double, + output: &mut W, + upper: bool, + opts: &FmtOpts, +) -> Result { + if val.is_finite() { + let (val, exp) = float_exp(val); + let exp_fmt = match upper { + false => b'e', + true => b'E', + }; + let precision = 6; + + fmt_float_exp(val, exp, exp_fmt, output, precision, opts) + } else { + fmt_float_nonfinite(val, output, upper, opts) + } +} + +pub fn fmt_float_any( + val: c_double, + output: &mut W, + upper: bool, + opts: &FmtOpts, +) -> Result { + if val.is_finite() { + let (log, exp) = float_exp(val); + let exp_fmt = match upper { + false => b'e', + true => b'E', + }; + let precision: usize = 6; + let use_exp_format = exp < -4 || exp >= precision as isize; + + if use_exp_format { + let precision = precision.saturating_sub(1); + fmt_float_exp(log, exp, exp_fmt, output, precision, opts) + } else { + let len = 1 + core::cmp::max(0, exp) as usize; + let precision = precision.saturating_sub(len); + fmt_float_finite(val, output, precision, opts) + } + } else { + fmt_float_nonfinite(val, output, upper, opts) + } +} diff --git a/src/header/stdio/printf/format.rs b/src/header/stdio/printf/format.rs new file mode 100644 index 0000000..7192901 --- /dev/null +++ b/src/header/stdio/printf/format.rs @@ -0,0 +1,303 @@ +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 alloc::string::String; + +use crate::{header::errno::Errno, traits::Write, types::wchar_t}; + +use super::float::{fmt_float, fmt_float_any, fmt_float_scientific}; + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub enum FmtSize { + ShortShort, + Short, + Normal, + Long, + LongLong, + Size, + LongFloat, +} + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub enum FmtSign { + Default, + Space, + Always, +} + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub enum FmtSpec { + Integer, + Unsigned(FmtRadix), + AnyFloat(bool), + Float(bool), + ScientificFloat(bool), + String, + Char, + Pointer, +} + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub enum FmtRadix { + Decimal, + Octal, + Hex(bool), +} + +pub struct FmtOpts { + pub size: FmtSize, + pub alternate: bool, + pub pad_char: u8, + pub width: usize, + pub sign: FmtSign, + pub left_adjust: bool, +} + +impl Default for FmtOpts { + fn default() -> Self { + Self { + size: FmtSize::Normal, + alternate: false, + pad_char: b' ', + width: 0, + sign: FmtSign::Default, + left_adjust: false, + } + } +} + +impl FmtRadix { + const CHARSET_LOWER: &'static [u8] = b"0123456789abcdef"; + const CHARSET_UPPER: &'static [u8] = b"0123456789ABCDEF"; + + pub fn charset_and_divisor(self) -> (&'static [u8], u64) { + match self { + Self::Octal => (Self::CHARSET_LOWER, 8), + Self::Decimal => (Self::CHARSET_LOWER, 10), + Self::Hex(true) => (Self::CHARSET_UPPER, 16), + Self::Hex(false) => (Self::CHARSET_LOWER, 16), + } + } +} + +impl FmtSize { + fn arg_int(&self, ap: &mut VaList) -> i64 { + match self { + Self::ShortShort => unsafe { ap.arg::() as _ }, + Self::Short => unsafe { ap.arg::() as _ }, + Self::Normal => unsafe { ap.arg::() as _ }, + Self::Long => unsafe { ap.arg::() as _ }, + Self::LongLong => unsafe { ap.arg::() as _ }, + Self::Size => unsafe { ap.arg::() as _ }, + Self::LongFloat => panic!("Incorrect L used with ints"), + } + } + + fn arg_uint(&self, ap: &mut VaList) -> u64 { + match self { + Self::ShortShort => unsafe { ap.arg::() as _ }, + Self::Short => unsafe { ap.arg::() as _ }, + Self::Normal => unsafe { ap.arg::() as _ }, + Self::Long => unsafe { ap.arg::() as _ }, + Self::LongLong => unsafe { ap.arg::() as _ }, + Self::Size => unsafe { ap.arg::() as _ }, + Self::LongFloat => panic!("Incorrect L used with uints"), + } + } + + fn arg_float(&self, ap: &mut VaList) -> c_double { + match self { + Self::Normal | Self::Long => unsafe { ap.arg::() }, + Self::LongFloat => unimplemented!("Long floats are not implemented yet"), + _ => panic!("Incorrect size specifier used with floats"), + } + } + + fn arg_pointer(&self, ap: &mut VaList) -> usize { + if *self != Self::Normal { + panic!("Incorrect size specifier used with a pointer"); + } + unsafe { ap.arg::() } + } + + pub fn shorter(self) -> Self { + match self { + Self::Normal => Self::Short, + Self::Short => Self::ShortShort, + _ => self, + } + } + + pub fn longer(self) -> Self { + match self { + Self::Normal => Self::Long, + Self::Long => Self::LongLong, + _ => self, + } + } +} + +impl FmtOpts { + fn pad(&self, output: &mut W, len: usize) -> Result { + let pad = self.width - core::cmp::min(self.width, len); + for _ in 0..pad { + output.write(&[self.pad_char])?; + } + Ok(pad) + } + + pub fn left_pad(&self, output: &mut W, len: usize) -> Result { + if !self.left_adjust { + self.pad(output, len) + } else { + Ok(0) + } + } + + pub fn right_pad(&self, output: &mut W, len: usize) -> Result { + if self.left_adjust { + self.pad(output, len) + } else { + Ok(0) + } + } + + pub fn fmt( + &self, + spec: FmtSpec, + output: &mut W, + ap: &mut VaList, + ) -> Result { + let mut buffer = [0; 64]; + + let len = match spec { + FmtSpec::Integer => { + let val = self.size.arg_int(ap); + fmt_signed_int(val, &mut buffer) + } + FmtSpec::Unsigned(radix) => { + let val = self.size.arg_uint(ap); + fmt_unsigned_int(val, &mut buffer, radix) + } + FmtSpec::Pointer => { + let val = self.size.arg_pointer(ap); + if val == 0 { + buffer[..5].copy_from_slice(b"(nil)"); + 5 + } else { + fmt_unsigned_int(val as u64, &mut buffer, FmtRadix::Hex(false)) + } + } + // TODO string precision + FmtSpec::String if self.size == FmtSize::Normal => { + let val = unsafe { ap.arg::<*const c_char>() }; + if val.is_null() { + buffer[..5].copy_from_slice(b"(nil)"); + 5 + } else { + let val = unsafe { CStr::from_ptr(val) }; + let bytes = val.to_bytes(); + let lpad = self.left_pad(output, bytes.len())?; + let len = output.write(bytes)?; + let rpad = self.right_pad(output, bytes.len())?; + return Ok(lpad + len + rpad); + } + } + FmtSpec::String if self.size == FmtSize::Long => { + let mut val = unsafe { ap.arg::<*const wchar_t>() }; + if val.is_null() { + buffer[..5].copy_from_slice(b"(nil)"); + 5 + } else { + let mut string = String::new(); + unsafe { + while *val != 0 { + let c = match char::from_u32(*val as _) { + Some(c) => c, + None => { + // TODO EILSEQ + todo!(); + } + }; + string.push(c); + val = val.add(1); + } + } + + let bytes = string.as_bytes(); + let lpad = self.left_pad(output, bytes.len())?; + let len = output.write(bytes)?; + let rpad = self.right_pad(output, bytes.len())?; + return Ok(lpad + len + rpad); + } + } + FmtSpec::Char if self.size == FmtSize::Normal => { + let ch = unsafe { ap.arg::() } as u8; + buffer[0] = ch; + 1 + } + FmtSpec::Char if self.size == FmtSize::Long => { + todo!(); + } + FmtSpec::Float(upper) => { + let val = self.size.arg_float(ap); + return fmt_float(val, output, upper, self); + } + FmtSpec::AnyFloat(upper) => { + let val = self.size.arg_float(ap); + return fmt_float_any(val, output, upper, self); + } + FmtSpec::ScientificFloat(upper) => { + let val = self.size.arg_float(ap); + return fmt_float_scientific(val, output, upper, self); + } + // Incorrect cases + FmtSpec::Char => { + panic!("Incorrect size specifier used with a C char"); + } + FmtSpec::String => { + panic!("Incorrect size specifier used with a C string"); + } + }; + + let lpad = self.left_pad(output, len)?; + let count = output.write(&buffer[..len])?; + let rpad = self.right_pad(output, len)?; + + Ok(lpad + count + rpad) + } +} + +fn fmt_unsigned_int(mut value: u64, output: &mut [u8], radix: FmtRadix) -> usize { + if value == 0 { + output[0] = b'0'; + return 1; + } + + let (charset, divisor) = radix.charset_and_divisor(); + let mut pos = 0; + + while value != 0 { + output[pos] = charset[(value % divisor) as usize]; + value /= divisor; + pos += 1; + } + + output[..pos].reverse(); + pos +} + +fn fmt_signed_int(value: i64, output: &mut [u8]) -> usize { + if value < 0 { + output[0] = b'-'; + 1 + fmt_unsigned_int( + value.wrapping_neg() as u64, + &mut output[1..], + FmtRadix::Decimal, + ) + } else { + fmt_unsigned_int(value as u64, output, FmtRadix::Decimal) + } +} diff --git a/src/header/stdio/printf/mod.rs b/src/header/stdio/printf/mod.rs new file mode 100644 index 0000000..36f89ad --- /dev/null +++ b/src/header/stdio/printf/mod.rs @@ -0,0 +1,133 @@ +use core::ffi::{c_char, c_int, CStr, VaList}; + +use crate::{header::errno::Errno, traits::Write}; + +use self::format::{FmtOpts, FmtRadix, FmtSign, FmtSize, FmtSpec}; + +use super::{stdout, FILE}; + +mod float; +mod format; + +fn printf_inner(output: &mut W, format: &[u8], mut ap: VaList) -> Result { + let mut fmt = format.into_iter(); + let mut count = 0; + + while let Some(&c) = fmt.next() { + if c != b'%' { + output.putc(c)?; + count += 1; + continue; + } + + let mut opts = FmtOpts::default(); + let mut cur = fmt.next(); + + // Parse flag characters + while let Some(&c) = cur { + match c { + b'#' => opts.alternate = true, + b'0' => opts.pad_char = b'0', + b'-' => opts.left_adjust = true, + b' ' => opts.sign = FmtSign::Space, + b'+' => opts.sign = FmtSign::Always, + b'\'' => unimplemented!("The ' flag is not implemented"), + _ => break, + } + cur = fmt.next(); + } + + // TODO Field width + while let Some(&c) = cur { + if c.is_ascii_digit() { + opts.width *= 10; + opts.width += (c - b'0') as usize; + cur = fmt.next(); + } else { + break; + } + } + // TODO Precision + + // Length modifier + while let Some(&c) = cur { + match c { + b'h' => opts.size = opts.size.shorter(), + b'l' => opts.size = opts.size.longer(), + b'q' => opts.size = FmtSize::LongLong, + b'L' => todo!(), + b'j' => todo!(), + b'z' | b'Z' => opts.size = FmtSize::Size, + b't' => todo!(), + _ => break, + } + cur = fmt.next(); + } + + // Conversion specifier + let mut spec = None; + if let Some(&c) = cur { + match c { + b'd' | b'i' => spec = Some(FmtSpec::Integer), + b'o' => spec = Some(FmtSpec::Unsigned(FmtRadix::Octal)), + b'u' => spec = Some(FmtSpec::Unsigned(FmtRadix::Decimal)), + b'x' | b'X' => { + spec = Some(FmtSpec::Unsigned(FmtRadix::Hex(c.is_ascii_uppercase()))) + } + b'e' | b'E' => spec = Some(FmtSpec::ScientificFloat(c.is_ascii_uppercase())), + b'f' | b'F' => spec = Some(FmtSpec::Float(c.is_ascii_uppercase())), + b'g' | b'G' => spec = Some(FmtSpec::AnyFloat(c.is_ascii_uppercase())), + b'a' | b'A' => unimplemented!("%a/%A are not implemented"), + b'c' => spec = Some(FmtSpec::Char), + b's' => spec = Some(FmtSpec::String), + b'C' => { + opts.size = FmtSize::Long; + spec = Some(FmtSpec::Char); + } + b'S' => { + opts.size = FmtSize::Long; + spec = Some(FmtSpec::String); + } + b'p' => spec = Some(FmtSpec::Pointer), + b'n' => todo!(), + b'm' => todo!(), + b'%' => { + count += output.write(b"%")?; + continue; + } + _ => (), + } + } + + if let Some(spec) = spec { + count += opts.fmt(spec, output, &mut ap)?; + } + } + + Ok(count) +} + +#[no_mangle] +unsafe extern "C" fn printf(format: *const c_char, mut args: ...) -> c_int { + vfprintf(stdout, format, args.as_va_list()) +} + +#[no_mangle] +unsafe extern "C" fn fprintf(stream: *mut FILE, format: *const c_char, mut args: ...) -> c_int { + vfprintf(stream, format, args.as_va_list()) +} + +#[no_mangle] +unsafe extern "C" fn vfprintf(stream: *mut FILE, format: *const c_char, ap: VaList) -> c_int { + if format.is_null() { + panic!(); + } + let stream = stream.as_mut().unwrap(); + let format = CStr::from_ptr(format); + + match printf_inner(stream, format.to_bytes(), ap) { + // TODO handle this + Ok(count) => count as c_int, + Err(_) => -1, + } +} diff --git a/src/header/stdlib/cbindgen.toml b/src/header/stdlib/cbindgen.toml new file mode 100644 index 0000000..4758ec7 --- /dev/null +++ b/src/header/stdlib/cbindgen.toml @@ -0,0 +1,12 @@ +language = "C" +style = "Type" + +sys_includes = ["stdarg.h", "stddef.h", "stdint.h"] +no_includes = true + +include_guard = "_STDLIB_H" + +usize_is_size_t = true + +[export] +exclude = [] diff --git a/src/header/stdlib/mod.rs b/src/header/stdlib/mod.rs new file mode 100644 index 0000000..39546e3 --- /dev/null +++ b/src/header/stdlib/mod.rs @@ -0,0 +1,4 @@ +use core::ffi::c_int; + +pub const EXIT_SUCCESS: c_int = 0; +pub const EXIT_FAILURE: c_int = 1; diff --git a/src/header/string/cbindgen.toml b/src/header/string/cbindgen.toml new file mode 100644 index 0000000..f78711c --- /dev/null +++ b/src/header/string/cbindgen.toml @@ -0,0 +1,11 @@ +language = "C" +style = "Type" + +sys_includes = ["stddef.h", "locale.h"] +no_includes = true + +include_guard = "_STRING_H" + +usize_is_size_t = true + +[export] diff --git a/src/header/string/mem.rs b/src/header/string/mem.rs new file mode 100644 index 0000000..4a7e42b --- /dev/null +++ b/src/header/string/mem.rs @@ -0,0 +1,73 @@ +use core::{ + ffi::{c_int, c_void}, + ptr::null_mut, +}; + +#[no_mangle] +unsafe extern "C" fn memccpy( + dst: *mut c_void, + src: *const c_void, + c: c_int, + mut n: usize, +) -> *mut c_void { + let c = c as u8; + let mut dst = dst as *mut u8; + let mut src = src as *mut u8; + + if dst.is_null() || src.is_null() { + panic!(); + } + + while n != 0 { + let ch = *src; + *dst = ch; + dst = dst.add(1); + + if ch == c { + return dst as _; + } + + src = src.add(1); + n -= 1; + } + + null_mut() +} + +#[no_mangle] +unsafe extern "C" fn memchr(s: *const c_void, c: c_int, mut n: usize) -> *mut c_void { + if s.is_null() { + panic!(); + } + + let c = c as u8; + let mut s = s as *const u8; + + while n != 0 { + if *s == c { + return s as _; + } + s = s.add(1); + n -= 1; + } + + null_mut() +} + +#[no_mangle] +pub(super) unsafe extern "C" fn mempcpy( + dst: *mut c_void, + src: *const c_void, + n: usize, +) -> *mut c_void { + if dst.is_null() || src.is_null() { + panic!(); + } + let dst = dst as *mut u8; + let src = src as *const u8; + + for i in 0..n { + *dst.add(i) = *src.add(i); + } + dst.add(n) as _ +} diff --git a/src/header/string/mod.rs b/src/header/string/mod.rs new file mode 100644 index 0000000..b9264b7 --- /dev/null +++ b/src/header/string/mod.rs @@ -0,0 +1,9 @@ +use core::ffi::{c_char, c_int, c_void}; + +pub mod mem; +pub mod str; + +extern "C" { + fn strlen(s: *const c_char) -> usize; + fn memset(a: *mut c_void, c: c_int, n: usize) -> *mut c_void; +} diff --git a/src/header/string/str.rs b/src/header/string/str.rs new file mode 100644 index 0000000..45a3383 --- /dev/null +++ b/src/header/string/str.rs @@ -0,0 +1,315 @@ +use core::{ + cmp::Ordering, + ffi::{c_char, c_int}, + ptr::null_mut, +}; + +use crate::header::{ + errno::{self, Errno}, + locale::locale_t, + string::strlen, +}; + +use super::{mem::mempcpy, memset}; + +#[no_mangle] +unsafe extern "C" fn stpcpy(dst: *mut c_char, src: *const c_char) -> *mut c_char { + stpncpy(dst, src, usize::MAX) +} + +#[no_mangle] +unsafe extern "C" fn stpncpy(dst: *mut c_char, src: *const c_char, n: usize) -> *mut c_char { + if dst.is_null() || src.is_null() { + panic!(); + } + memset(dst as _, 0, n); + mempcpy(dst as _, src as _, strnlen(src, n)) as _ +} + +#[no_mangle] +unsafe extern "C" fn strcat(dst: *mut c_char, src: *const c_char) -> *mut c_char { + strncat(dst, src, usize::MAX) +} + +#[no_mangle] +unsafe extern "C" fn strchr(mut s: *const c_char, c: c_int) -> *mut c_char { + if s.is_null() { + panic!(); + } + + loop { + if *s == c as _ { + return s as _; + } + + if *s == 0 { + break; + } + + s = s.add(1); + } + + null_mut() +} + +#[no_mangle] +unsafe extern "C" fn strcmp(a: *const c_char, b: *const c_char) -> c_int { + strncmp(a, b, usize::MAX) +} + +#[no_mangle] +unsafe extern "C" fn strcpy(dst: *mut c_char, src: *const c_char) -> *mut c_char { + strncpy(dst, src, usize::MAX) +} + +#[no_mangle] +unsafe extern "C" fn strcspn(mut s: *const c_char, reject: *const c_char) -> usize { + if s.is_null() || reject.is_null() { + panic!(); + } + let mut n = 0; + while *s != 0 { + if !strchr(reject, *s as _).is_null() { + return n; + } + n += 1; + s = s.add(1); + } + n +} + +#[no_mangle] +unsafe extern "C" fn strdup(s: *const c_char) -> *mut c_char { + strndup(s, usize::MAX) +} + +unsafe fn strerror_inner(e: c_int) -> *const c_char { + if let Some(errno) = Errno::from_c_int(e) { + errno.to_c_str() + } else { + errno::UNKNOWN_ERROR.as_ptr() + } +} + +#[no_mangle] +unsafe extern "C" fn strerror(e: c_int) -> *mut c_char { + static mut BUF: [c_char; 128] = [0; 128]; + strerror_r(e, BUF.as_mut_ptr(), BUF.len()) +} + +#[no_mangle] +unsafe extern "C" fn strerror_r(e: c_int, buf: *mut c_char, n: usize) -> *mut c_char { + let source = strerror_inner(e); + strncpy(buf, source, n) +} + +#[no_mangle] +unsafe extern "C" fn strncat(dst: *mut c_char, src: *const c_char, n: usize) -> *mut c_char { + if dst.is_null() { + panic!(); + } + let len = strnlen(src, n); + let p = dst.add(strlen(dst)); + let p = mempcpy(p as _, src as _, len) as *mut c_char; + *p = 0; + + dst +} + +#[no_mangle] +unsafe extern "C" fn strncmp(mut a: *const c_char, mut b: *const c_char, mut n: usize) -> c_int { + if a.is_null() || b.is_null() { + panic!(); + } + if a == b { + return 0; + } + while n != 0 { + match Ord::cmp(&*a, &*b) { + Ordering::Less => return -1, + Ordering::Greater => return 1, + Ordering::Equal => (), + } + + if *a == 0 { + break; + } + + a = a.add(1); + b = b.add(1); + n -= 1; + } + + 0 +} + +#[no_mangle] +unsafe extern "C" fn strncpy(dst: *mut c_char, src: *const c_char, n: usize) -> *mut c_char { + stpncpy(dst, src, n); + dst +} + +#[no_mangle] +unsafe extern "C" fn strndup(s: *const c_char, n: usize) -> *mut c_char { + todo!() +} + +#[no_mangle] +unsafe extern "C" fn strnlen(s: *const c_char, n: usize) -> usize { + if s.is_null() { + panic!(); + } + for i in 0..n { + if *s.add(i) == 0 { + return i; + } + } + + n +} + +#[no_mangle] +unsafe extern "C" fn strpbrk(mut a: *const c_char, b: *const c_char) -> *mut c_char { + if a.is_null() || b.is_null() { + return null_mut(); + } + + loop { + let c = *a; + + if c == 0 { + break; + } + + if !strchr(b, c as _).is_null() { + return a as _; + } + + a = a.add(1); + } + + null_mut() +} + +#[no_mangle] +unsafe extern "C" fn strrchr(a: *const c_char, c: c_int) -> *mut c_char { + if a.is_null() { + return null_mut(); + } + + let n = strnlen(a, usize::MAX); + for i in (0..n).rev() { + if *a.add(i) == c as _ { + return a.add(i) as _; + } + } + + null_mut() +} + +#[no_mangle] +unsafe extern "C" fn strsignal(signum: c_int) -> *mut c_char { + todo!() +} + +#[no_mangle] +unsafe extern "C" fn strspn(mut s: *const c_char, accept: *const c_char) -> usize { + if s.is_null() || accept.is_null() { + panic!(); + } + let mut n = 0; + while *s != 0 { + if strchr(accept, *s as _).is_null() { + return n; + } + n += 1; + s = s.add(1); + } + n +} + +#[no_mangle] +unsafe extern "C" fn strstr(mut a: *const c_char, b: *const c_char) -> *mut c_char { + if a.is_null() || b.is_null() { + panic!(); + } + let n = strnlen(b, usize::MAX); + if *a == 0 && *b == 0 { + return a as _; + } + + while *a != 0 { + if strncmp(a, b, n) == 0 { + return a as _; + } + a = a.add(1); + } + + null_mut() +} + +#[no_mangle] +unsafe extern "C" fn strtok(str: *mut c_char, delim: *const c_char) -> *mut c_char { + static mut STRTOK_BUF: *mut c_char = null_mut(); + if !str.is_null() { + STRTOK_BUF = null_mut(); + } + strtok_r(str, delim, &mut STRTOK_BUF) +} + +#[no_mangle] +unsafe extern "C" fn strtok_r( + mut str: *mut c_char, + delim: *const c_char, + saveptr: *mut *mut c_char, +) -> *mut c_char { + if saveptr.is_null() || delim.is_null() { + panic!(); + } + + if str.is_null() { + str = *saveptr; + } + + str = str.add(strspn(str, delim)); + + if *str == 0 { + *saveptr = null_mut(); + return null_mut(); + } + + let len = strcspn(str, delim); + if *str.add(len) != 0 { + *saveptr = str.add(len + 1); + } else { + *saveptr = null_mut(); + } + *str.add(len) = 0; + str as _ +} + +// TODO locales +#[no_mangle] +unsafe extern "C" fn strcoll(a: *const c_char, b: *const c_char) -> c_int { + todo!() +} + +#[no_mangle] +unsafe extern "C" fn strcoll_l(a: *const c_char, b: *const c_char, l: locale_t) -> c_int { + todo!() +} + +#[no_mangle] +unsafe extern "C" fn strerror_l(e: c_int, l: locale_t) -> *mut c_char { + todo!() +} + +#[no_mangle] +unsafe extern "C" fn strxfrm(a: *mut c_char, b: *const c_char, n: usize) -> usize { + todo!() +} + +#[no_mangle] +unsafe extern "C" fn strxfrm_l(a: *mut c_char, b: *const c_char, n: usize, l: locale_t) -> usize { + todo!() +} diff --git a/src/header/sys_types/cbindgen.toml b/src/header/sys_types/cbindgen.toml new file mode 100644 index 0000000..4d45124 --- /dev/null +++ b/src/header/sys_types/cbindgen.toml @@ -0,0 +1,10 @@ +language = "C" +style = "Type" + +sys_includes = ["stddef.h"] +no_includes = true + +include_guard = "_SYS_TYPES_H" + +[export] +include = ["pid_t"] diff --git a/src/header/sys_types/mod.rs b/src/header/sys_types/mod.rs new file mode 100644 index 0000000..bd17fce --- /dev/null +++ b/src/header/sys_types/mod.rs @@ -0,0 +1,3 @@ +use core::ffi::c_int; + +pub type pid_t = i32; diff --git a/src/header/sys_wait/cbindgen.toml b/src/header/sys_wait/cbindgen.toml new file mode 100644 index 0000000..aa6e2e6 --- /dev/null +++ b/src/header/sys_wait/cbindgen.toml @@ -0,0 +1,12 @@ +language = "C" +style = "Type" + +sys_includes = ["stdarg.h", "stddef.h", "stdint.h", "sys/types.h"] +no_includes = true + +include_guard = "_SYS_WAIT_H" + +usize_is_size_t = true + +[export] +exclude = [] diff --git a/src/header/sys_wait/mod.rs b/src/header/sys_wait/mod.rs new file mode 100644 index 0000000..d5e478f --- /dev/null +++ b/src/header/sys_wait/mod.rs @@ -0,0 +1,34 @@ +use core::ffi::c_int; + +use yggdrasil_rt::{process::ExitCode, sys as syscall}; + +use super::{errno::Errno, sys_types::pid_t}; + +fn waitpid_inner(pid: u32) -> Result { + let mut exit_code = ExitCode::SUCCESS; + unsafe { syscall::wait_process(pid, &mut exit_code) }?; + Ok(exit_code) +} + +#[no_mangle] +unsafe extern "C" fn waitpid(pid: pid_t, wstatus: *mut c_int, options: c_int) -> pid_t { + let _ = options; + if pid < 0 { + todo!(); + } + let pid = pid as u32; + + match waitpid_inner(pid) { + Ok(code) => { + if let Some(wstatus) = wstatus.as_mut() { + match code { + ExitCode::Exited(code) => *wstatus = code, + ExitCode::BySignal(_) => todo!(), + } + } + + pid as pid_t + } + Err(_) => -1, + } +} diff --git a/src/header/unistd/cbindgen.toml b/src/header/unistd/cbindgen.toml new file mode 100644 index 0000000..97dcd36 --- /dev/null +++ b/src/header/unistd/cbindgen.toml @@ -0,0 +1,13 @@ +language = "C" +style = "Type" + +sys_includes = ["stdarg.h", "stddef.h", "stdint.h", "sys/types.h"] +no_includes = true + +include_guard = "_UNISTD_H" +trailer = "#include " + +usize_is_size_t = true + +[export] +exclude = [] diff --git a/src/header/unistd/mod.rs b/src/header/unistd/mod.rs new file mode 100644 index 0000000..6fb7159 --- /dev/null +++ b/src/header/unistd/mod.rs @@ -0,0 +1,114 @@ +use core::ffi::{c_char, c_int, CStr}; + +use crate::{ + error::{CZeroResult, EResult}, + util::{self, NullTerminated}, +}; + +use super::{errno::Errno, sys_types::pid_t}; + +use alloc::{vec, vec::Vec}; +use yggdrasil_rt::{path::Path, process::ExecveOptions, sys as syscall}; + +unsafe fn fork_inner() -> Result { + let result = EResult::from(syscall::fork())?; + Ok(result as _) +} + +// TODO error reporting +unsafe fn collect_execve_args<'a, 'e>( + argv: *const *mut c_char, + envp: *const *mut c_char, +) -> Result<(Vec<&'a str>, Vec<&'e str>), Errno> { + let mut arg_list = vec![]; + let mut env_list = vec![]; + + if let Some(argv) = NullTerminated::try_from_ptr(argv) { + for &arg in argv { + let arg = CStr::from_ptr(arg); + arg_list.push(arg.to_str().unwrap()); + } + } + if let Some(envp) = NullTerminated::try_from_ptr(envp) { + for &env in envp { + let env = CStr::from_ptr(env); + env_list.push(env.to_str().unwrap()); + } + } + + Ok((arg_list, env_list)) +} + +unsafe fn execve_inner>( + pathname: P, + argv: &[&str], + envp: &[&str], +) -> Result<(), Errno> { + EResult::Err(yggdrasil_rt::Error::InvalidFile)?; + let opts = ExecveOptions { + program: pathname.as_ref().as_str(), + arguments: argv, + environment: envp, + }; + let result = EResult::from(syscall::execve(&opts))?; + Ok(result as _) +} + +unsafe fn execvpe_inner(file: &str, argv: &[&str], envp: &[&str]) -> Result<(), Errno> { + let pathname = util::resolve_binary(file)?; + execve_inner(&pathname, argv, envp) +} + +#[no_mangle] +pub static mut environ: *mut *const c_char = core::ptr::null_mut(); + +#[no_mangle] +unsafe extern "C" fn fork() -> pid_t { + match fork_inner() { + Ok(pid) => pid, + Err(_) => -1, + } +} + +#[no_mangle] +unsafe extern "C" fn execve( + pathname: *const c_char, + argv: *const *mut c_char, + envp: *const *mut c_char, +) -> c_int { + if pathname.is_null() { + panic!(); + } + let pathname = CStr::from_ptr(pathname); + let pathname = pathname.to_str().unwrap(); + let (argv, envp) = match collect_execve_args(argv, envp) { + Ok(r) => r, + Err(_) => return -1, + }; + + execve_inner(pathname, &argv, &envp).into_zero_status() +} + +#[no_mangle] +unsafe extern "C" fn execvp(file: *const c_char, argv: *const *mut c_char) -> c_int { + todo!() +} + +#[no_mangle] +unsafe extern "C" fn execvpe( + file: *const c_char, + argv: *const *mut c_char, + envp: *const *mut c_char, +) -> c_int { + if file.is_null() { + panic!(); + } + let file = CStr::from_ptr(file); + let file = file.to_str().unwrap(); + let (argv, envp) = match collect_execve_args(argv, envp) { + Ok(r) => r, + Err(_) => return -1, + }; + + execvpe_inner(file, &argv, &envp).into_zero_status() +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..2e00e84 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,67 @@ +#![feature(type_alias_impl_trait, c_variadic, try_trait_v2)] +#![no_std] + +use core::ffi::{c_char, c_int}; + +use alloc::{ffi::CString, vec::Vec}; +use header::unistd::environ; +use yggdrasil_rt::process::{ExitCode, ProgramArgumentInner}; + +extern crate alloc; +extern crate yggdrasil_rt; + +pub mod header; + +pub mod allocator; +pub mod error; +pub mod path; +pub mod process; +pub mod sync; +pub mod traits; +pub mod types; +pub mod util; + +static mut ARGS: Vec = Vec::new(); +static mut C_ARGS: Vec<*const c_char> = Vec::new(); +static mut ENVS: Vec = Vec::new(); +static mut C_ENVS: Vec<*const c_char> = Vec::new(); + +unsafe fn setup_env(arg: usize) { + let arg = &*(arg as *const ProgramArgumentInner<'static>); + + for (i, &arg) in arg.args.into_iter().enumerate() { + ARGS.push(CString::new(arg).unwrap()); + C_ARGS.push(ARGS[i].as_ptr()); + } + + for (i, &env) in arg.env.into_iter().enumerate() { + ENVS.push(CString::new(env).unwrap()); + C_ENVS.push(ENVS[i].as_ptr()); + } + + environ = C_ENVS.as_mut_ptr(); +} + +#[no_mangle] +unsafe extern "C" fn _start(arg: usize) -> ! { + extern "C" { + fn main(argc: c_int, argv: *const *const c_char) -> c_int; + } + + setup_env(arg); + header::stdio::setup_default_files(); + + // TODO setup signals, allocator, etc. + + let code = main(C_ARGS.len() as _, C_ARGS.as_ptr()); + + process::exit(code) +} + +#[panic_handler] +fn panic_handler(pi: &core::panic::PanicInfo) -> ! { + yggdrasil_rt::debug_trace!("--- C PROGRAM PANICKED ---"); + yggdrasil_rt::debug_trace!("{:?}", pi); + yggdrasil_rt::debug_trace!("--- END PANIC ---"); + process::exit(ExitCode::Exited(1)); +} diff --git a/src/path.rs b/src/path.rs new file mode 100644 index 0000000..4b77be6 --- /dev/null +++ b/src/path.rs @@ -0,0 +1,70 @@ +use core::{borrow::Borrow, mem::MaybeUninit, ops::Deref}; + +use alloc::{borrow::ToOwned, string::String}; +use yggdrasil_rt::{io::FileAttr, path::Path, sys as syscall}; + +use crate::{error::EResult, header::errno::Errno}; + +pub trait PathExt { + fn metadata(&self) -> Result; + + fn exists(&self) -> bool { + self.metadata().is_ok() + } +} + +#[derive(Clone, Debug)] +pub struct PathBuf(String); + +impl PathExt for Path { + fn metadata(&self) -> Result { + let mut metadata = MaybeUninit::uninit(); + EResult::from(unsafe { syscall::get_metadata(None, self.as_str(), &mut metadata, true) })?; + let metadata = unsafe { metadata.assume_init() }; + Ok(metadata) + } +} + +impl PathBuf { + pub fn from_str(s: &str) -> Self { + Self(String::from(s)) + } + + pub fn push>(&mut self, elem: P) { + let elem = elem.as_ref().trim_start_separators().as_str(); + self.0.push('/'); + self.0.push_str(elem); + } + + pub fn join>(&self, elem: P) -> Self { + let mut new = self.clone(); + new.push(elem); + new + } +} + +impl From<&Path> for PathBuf { + fn from(value: &Path) -> Self { + Self(value.as_str().to_owned()) + } +} + +impl Deref for PathBuf { + type Target = Path; + + fn deref(&self) -> &Self::Target { + Path::from_str(self.0.as_str()) + } +} + +impl AsRef for PathBuf { + fn as_ref(&self) -> &Path { + Path::from_str(self.0.as_str()) + } +} + +impl Borrow for PathBuf { + fn borrow(&self) -> &Path { + Path::from_str(self.0.as_str()) + } +} diff --git a/src/process.rs b/src/process.rs new file mode 100644 index 0000000..690b71a --- /dev/null +++ b/src/process.rs @@ -0,0 +1,27 @@ +use yggdrasil_rt::{process::ExitCode, sys as syscall}; + +pub trait ToExitCode { + fn to_exit_status(self) -> ExitCode; +} + +impl ToExitCode for i32 { + fn to_exit_status(self) -> ExitCode { + ExitCode::Exited(self) + } +} + +impl ToExitCode for ExitCode { + fn to_exit_status(self) -> ExitCode { + self + } +} + +pub unsafe fn raw_exit(code: ExitCode) -> ! { + syscall::exit_process(code) +} + +pub fn exit(code: T) -> ! { + // TODO handle closing files, cleanup, etc. + + unsafe { raw_exit(code.to_exit_status()) } +} diff --git a/src/sync.rs b/src/sync.rs new file mode 100644 index 0000000..4072fec --- /dev/null +++ b/src/sync.rs @@ -0,0 +1,110 @@ +use core::{ + cell::UnsafeCell, + ops::{Deref, DerefMut}, + sync::atomic::{AtomicU32, Ordering}, +}; + +use yggdrasil_rt::{process::MutexOperation, sys as syscall}; + +pub struct RawMutex { + value: AtomicU32, +} + +pub struct Mutex { + value: UnsafeCell, + inner: RawMutex, +} + +pub struct MutexGuard<'a, T> { + lock: &'a Mutex, +} + +impl RawMutex { + const UNLOCKED: u32 = 0; + const LOCKED: u32 = 1; + + pub const fn new() -> Self { + Self { + value: AtomicU32::new(Self::UNLOCKED), + } + } + + fn try_lock(&self) -> bool { + self.value + .compare_exchange( + Self::UNLOCKED, + Self::LOCKED, + Ordering::Acquire, + Ordering::Relaxed, + ) + .is_ok() + } + + pub fn lock(&self) { + loop { + if self.try_lock() { + // Got a lock + return; + } + + self.wait(Self::LOCKED); + } + } + + pub unsafe fn release(&self) { + if self.value.swap(Self::UNLOCKED, Ordering::Release) == Self::LOCKED { + self.wake(); + } + } + + fn wait(&self, value: u32) { + unsafe { + syscall::mutex(&self.value, &MutexOperation::Wait(value, None)).unwrap(); + } + } + + fn wake(&self) { + unsafe { + syscall::mutex(&self.value, &MutexOperation::Wake).unwrap(); + } + } +} + +unsafe impl Send for RawMutex {} +unsafe impl Sync for RawMutex {} + +impl Mutex { + pub const fn new(value: T) -> Self { + Self { + value: UnsafeCell::new(value), + inner: RawMutex::new(), + } + } + + pub fn lock(&self) -> MutexGuard { + self.inner.lock(); + MutexGuard { lock: self } + } +} + +impl Deref for MutexGuard<'_, T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + unsafe { &*self.lock.value.get() } + } +} + +impl DerefMut for MutexGuard<'_, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + unsafe { &mut *self.lock.value.get() } + } +} + +impl Drop for MutexGuard<'_, T> { + fn drop(&mut self) { + unsafe { + self.lock.inner.release(); + } + } +} diff --git a/src/traits.rs b/src/traits.rs new file mode 100644 index 0000000..62d9013 --- /dev/null +++ b/src/traits.rs @@ -0,0 +1,16 @@ +use core::fmt; + +use crate::header::errno::Errno; + +pub trait Write: fmt::Write { + fn write(&mut self, data: &[u8]) -> Result; + + fn putc(&mut self, ch: u8) -> Result<(), Errno> { + self.write(&[ch])?; + Ok(()) + } +} + +pub trait Read { + fn read(&mut self, data: &mut [u8]) -> Result; +} diff --git a/src/types.rs b/src/types.rs new file mode 100644 index 0000000..e4cf0b2 --- /dev/null +++ b/src/types.rs @@ -0,0 +1,5 @@ +#![allow(non_camel_case_types)] + +use core::ffi::c_int; + +pub type wchar_t = i32; diff --git a/src/util.rs b/src/util.rs new file mode 100644 index 0000000..98db6c0 --- /dev/null +++ b/src/util.rs @@ -0,0 +1,79 @@ +use core::marker::PhantomData; + +use yggdrasil_rt::path::Path; + +use crate::{ + error::{EResult, OptionExt}, + path::{PathBuf, PathExt}, + ENVS, +}; + +pub trait Nullable { + fn is_null(&self) -> bool; +} + +pub struct NullTerminated<'a, T: Nullable> { + data: *const T, + _pd: PhantomData<&'a ()>, +} + +impl Nullable for *const T { + fn is_null(&self) -> bool { + <*const T>::is_null(*self) + } +} + +impl Nullable for *mut T { + fn is_null(&self) -> bool { + <*mut T>::is_null(*self) + } +} + +impl<'a, T: Nullable> NullTerminated<'a, T> { + pub unsafe fn try_from_ptr(ptr: *const T) -> Option { + (!ptr.is_null()).then(|| Self { + data: ptr, + _pd: PhantomData, + }) + } +} + +impl<'a, T: Nullable + 'a> Iterator for NullTerminated<'a, T> { + type Item = &'a T; + + fn next(&mut self) -> Option { + let value = unsafe { &*self.data }; + if value.is_null() { + return None; + } + self.data = unsafe { self.data.add(1) }; + Some(value) + } +} + +pub fn envs() -> impl Iterator { + // SAFETY C_ENVS is only mutable during init + let it = unsafe { ENVS.iter() }; + it.filter_map(|v| v.to_str().ok().and_then(|s| s.split_once('='))) +} + +pub fn getenv(name: &str) -> Option<&'static str> { + envs().find_map(|(k, v)| (k == name).then_some(v)) +} + +pub fn resolve_binary>(name: P) -> EResult { + let name = name.as_ref(); + if name.is_absolute() { + return EResult::Ok(PathBuf::from(name)); + } + let env_path = getenv("PATH").e_ok_or(yggdrasil_rt::Error::DoesNotExist)?; + + for el in env_path.split(':') { + let path = PathBuf::from_str(el).join(name); + if path.exists() { + return EResult::Ok(path); + } + } + + EResult::Err(yggdrasil_rt::Error::DoesNotExist) +}