Initial commit: most of <string.h>
This commit is contained in:
@@ -0,0 +1 @@
|
|||||||
|
/target
|
||||||
+14
@@ -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"
|
||||||
@@ -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<P: AsRef<Path>>(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);
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
#ifndef _YGGDRASIL_ERRNO_H
|
||||||
|
#define _YGGDRASIL_ERRNO_H 1
|
||||||
|
|
||||||
|
extern int errno;
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
#ifndef _YGGDRASIL_LOCALE_H
|
||||||
|
#define _YGGDRASIL_LOCALE_H 1
|
||||||
|
|
||||||
|
#define LC_GLOBAL_LOCALE ((locale_t) -1L)
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
#ifndef _YGGDRASIL_MATH_H
|
||||||
|
#define _YGGDRASIL_MATH_H 1
|
||||||
|
|
||||||
|
#define INFINITY __builtin_inff()
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -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
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
#ifndef _YGGDRASIL_STRING_H
|
||||||
|
#define _YGGDRASIL_STRING_H 1
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
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
|
||||||
@@ -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
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
#ifndef _YGGDRASIL_WCHAR_H
|
||||||
|
#define _YGGDRASIL_WCHAR_H 1
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#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 <stddef.h>
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
pub mod rust;
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
use libyalloc::GlobalAllocator;
|
||||||
|
|
||||||
|
#[global_allocator]
|
||||||
|
static GLOBAL_ALLOCATOR: GlobalAllocator = GlobalAllocator;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
#![allow(non_camel_case_types)]
|
||||||
+116
@@ -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<T> {
|
||||||
|
fn e_ok_or(self, err: yggdrasil_rt::Error) -> EResult<T>;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO thread_local
|
||||||
|
#[no_mangle]
|
||||||
|
pub static mut errno: Errno = Errno(0);
|
||||||
|
|
||||||
|
pub enum EResult<T> {
|
||||||
|
Ok(T),
|
||||||
|
Err(yggdrasil_rt::Error),
|
||||||
|
Errno(Errno),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> From<Result<T, yggdrasil_rt::Error>> for EResult<T> {
|
||||||
|
fn from(value: Result<T, yggdrasil_rt::Error>) -> Self {
|
||||||
|
match value {
|
||||||
|
Ok(value) => Self::Ok(value),
|
||||||
|
Err(error) => Self::Err(error),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> EResult<T> {
|
||||||
|
pub fn into_set_errno(self) -> EResult<T> {
|
||||||
|
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<usize, Errno> {
|
||||||
|
fn into_size_status(self) -> usize {
|
||||||
|
match self {
|
||||||
|
Self::Ok(value) => value,
|
||||||
|
Self::Err(_) => 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> FromResidual<Errno> for EResult<T> {
|
||||||
|
fn from_residual(residual: Errno) -> Self {
|
||||||
|
Self::Errno(residual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> FromResidual<yggdrasil_rt::Error> for EResult<T> {
|
||||||
|
fn from_residual(residual: yggdrasil_rt::Error) -> Self {
|
||||||
|
Self::Err(residual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> FromResidual<Result<Infallible, Errno>> for EResult<T> {
|
||||||
|
fn from_residual(residual: Result<Infallible, Errno>) -> Self {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Try for EResult<T> {
|
||||||
|
type Output = T;
|
||||||
|
type Residual = Result<Infallible, Errno>;
|
||||||
|
|
||||||
|
fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
|
||||||
|
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<T> OptionExt<T> for Option<T> {
|
||||||
|
fn e_ok_or(self, err: yggdrasil_rt::Error) -> EResult<T> {
|
||||||
|
match self {
|
||||||
|
Some(value) => EResult::Ok(value),
|
||||||
|
None => EResult::Err(err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
pub trait FileImpl {}
|
||||||
|
|
||||||
|
pub struct File {}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
language = "C"
|
||||||
|
style = "Type"
|
||||||
|
|
||||||
|
sys_includes = []
|
||||||
|
no_includes = true
|
||||||
|
|
||||||
|
include_guard = "_ERRNO_H"
|
||||||
|
trailer = "#include <bits/errno.h>"
|
||||||
|
|
||||||
|
[export]
|
||||||
@@ -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<Self> {
|
||||||
|
if v < 0 || v > MAX_ERROR.0 {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(Self(v))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<yggdrasil_rt::Error> 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
language = "C"
|
||||||
|
style = "Tag"
|
||||||
|
|
||||||
|
sys_includes = ["stddef.h"]
|
||||||
|
no_includes = true
|
||||||
|
|
||||||
|
include_guard = "_LOCALE_H"
|
||||||
|
trailer = "#include <bits/locale.h>"
|
||||||
|
|
||||||
|
usize_is_size_t = true
|
||||||
|
|
||||||
|
[export]
|
||||||
|
include = ["lconv", "locale_t"]
|
||||||
@@ -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!()
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
language = "C"
|
||||||
|
style = "Type"
|
||||||
|
|
||||||
|
sys_includes = []
|
||||||
|
no_includes = true
|
||||||
|
|
||||||
|
include_guard = "_MATH_H"
|
||||||
|
trailer = "#include <bits/math.h>"
|
||||||
|
|
||||||
|
usize_is_size_t = true
|
||||||
|
|
||||||
|
[export]
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
pub const NAN: f64 = f64::NAN;
|
||||||
@@ -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;
|
||||||
@@ -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 <bits/stdio.h>"
|
||||||
|
|
||||||
|
usize_is_size_t = true
|
||||||
|
|
||||||
|
[export]
|
||||||
|
# Varargs are broken for these
|
||||||
|
exclude = ["printf", "fprintf"]
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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()
|
||||||
|
}
|
||||||
@@ -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<u8>,
|
||||||
|
unget_buffer: Vec<u8>,
|
||||||
|
read_pos: usize,
|
||||||
|
read_size: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FileInner {
|
||||||
|
unsafe fn read(&mut self, data: &mut [u8]) -> Result<usize, Errno> {
|
||||||
|
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<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());
|
||||||
|
|
||||||
|
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<usize, Errno> {
|
||||||
|
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<usize, Errno> {
|
||||||
|
// 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_;
|
||||||
|
}
|
||||||
@@ -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<W: Write>(
|
||||||
|
val: c_double,
|
||||||
|
exp: isize,
|
||||||
|
exp_fmt: u8,
|
||||||
|
output: &mut W,
|
||||||
|
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 {
|
||||||
|
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<W: Write>(
|
||||||
|
val: c_double,
|
||||||
|
output: &mut W,
|
||||||
|
precision: usize,
|
||||||
|
opts: &FmtOpts,
|
||||||
|
) -> Result<usize, Errno> {
|
||||||
|
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<W: Write>(
|
||||||
|
val: c_double,
|
||||||
|
output: &mut W,
|
||||||
|
upper: bool,
|
||||||
|
opts: &FmtOpts,
|
||||||
|
) -> Result<usize, Errno> {
|
||||||
|
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<W: Write>(
|
||||||
|
val: c_double,
|
||||||
|
output: &mut W,
|
||||||
|
upper: bool,
|
||||||
|
opts: &FmtOpts,
|
||||||
|
) -> Result<usize, Errno> {
|
||||||
|
if val.is_finite() {
|
||||||
|
fmt_float_finite(val, output, 6, opts)
|
||||||
|
} else {
|
||||||
|
fmt_float_nonfinite(val, output, upper, opts)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fmt_float_scientific<W: Write>(
|
||||||
|
val: c_double,
|
||||||
|
output: &mut W,
|
||||||
|
upper: bool,
|
||||||
|
opts: &FmtOpts,
|
||||||
|
) -> Result<usize, Errno> {
|
||||||
|
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<W: Write>(
|
||||||
|
val: c_double,
|
||||||
|
output: &mut W,
|
||||||
|
upper: bool,
|
||||||
|
opts: &FmtOpts,
|
||||||
|
) -> Result<usize, Errno> {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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::<c_char>() as _ },
|
||||||
|
Self::Short => unsafe { ap.arg::<c_short>() as _ },
|
||||||
|
Self::Normal => unsafe { ap.arg::<c_int>() as _ },
|
||||||
|
Self::Long => unsafe { ap.arg::<c_long>() as _ },
|
||||||
|
Self::LongLong => unsafe { ap.arg::<c_longlong>() as _ },
|
||||||
|
Self::Size => unsafe { ap.arg::<isize>() as _ },
|
||||||
|
Self::LongFloat => panic!("Incorrect L used with ints"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn arg_uint(&self, ap: &mut VaList) -> u64 {
|
||||||
|
match self {
|
||||||
|
Self::ShortShort => unsafe { ap.arg::<c_uchar>() as _ },
|
||||||
|
Self::Short => unsafe { ap.arg::<c_ushort>() as _ },
|
||||||
|
Self::Normal => unsafe { ap.arg::<c_uint>() as _ },
|
||||||
|
Self::Long => unsafe { ap.arg::<c_ulong>() as _ },
|
||||||
|
Self::LongLong => unsafe { ap.arg::<c_ulonglong>() as _ },
|
||||||
|
Self::Size => unsafe { ap.arg::<usize>() 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::<c_double>() },
|
||||||
|
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::<usize>() }
|
||||||
|
}
|
||||||
|
|
||||||
|
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<W: 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])?;
|
||||||
|
}
|
||||||
|
Ok(pad)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn left_pad<W: Write>(&self, output: &mut W, len: usize) -> Result<usize, Errno> {
|
||||||
|
if !self.left_adjust {
|
||||||
|
self.pad(output, len)
|
||||||
|
} else {
|
||||||
|
Ok(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn right_pad<W: Write>(&self, output: &mut W, len: usize) -> Result<usize, Errno> {
|
||||||
|
if self.left_adjust {
|
||||||
|
self.pad(output, len)
|
||||||
|
} else {
|
||||||
|
Ok(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fmt<W: Write>(
|
||||||
|
&self,
|
||||||
|
spec: FmtSpec,
|
||||||
|
output: &mut W,
|
||||||
|
ap: &mut VaList,
|
||||||
|
) -> Result<usize, Errno> {
|
||||||
|
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::<c_char>() } 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<W: 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;
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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 = []
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
use core::ffi::c_int;
|
||||||
|
|
||||||
|
pub const EXIT_SUCCESS: c_int = 0;
|
||||||
|
pub const EXIT_FAILURE: c_int = 1;
|
||||||
@@ -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]
|
||||||
@@ -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 _
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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!()
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
language = "C"
|
||||||
|
style = "Type"
|
||||||
|
|
||||||
|
sys_includes = ["stddef.h"]
|
||||||
|
no_includes = true
|
||||||
|
|
||||||
|
include_guard = "_SYS_TYPES_H"
|
||||||
|
|
||||||
|
[export]
|
||||||
|
include = ["pid_t"]
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
use core::ffi::c_int;
|
||||||
|
|
||||||
|
pub type pid_t = i32;
|
||||||
@@ -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 = []
|
||||||
@@ -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<ExitCode, Errno> {
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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 <bits/unistd.h>"
|
||||||
|
|
||||||
|
usize_is_size_t = true
|
||||||
|
|
||||||
|
[export]
|
||||||
|
exclude = []
|
||||||
@@ -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<pid_t, Errno> {
|
||||||
|
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<P: AsRef<Path>>(
|
||||||
|
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()
|
||||||
|
}
|
||||||
+67
@@ -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<CString> = Vec::new();
|
||||||
|
static mut C_ARGS: Vec<*const c_char> = Vec::new();
|
||||||
|
static mut ENVS: Vec<CString> = 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));
|
||||||
|
}
|
||||||
+70
@@ -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<FileAttr, Errno>;
|
||||||
|
|
||||||
|
fn exists(&self) -> bool {
|
||||||
|
self.metadata().is_ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct PathBuf(String);
|
||||||
|
|
||||||
|
impl PathExt for Path {
|
||||||
|
fn metadata(&self) -> Result<FileAttr, Errno> {
|
||||||
|
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<P: AsRef<Path>>(&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<P: AsRef<Path>>(&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<Path> for PathBuf {
|
||||||
|
fn as_ref(&self) -> &Path {
|
||||||
|
Path::from_str(self.0.as_str())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Borrow<Path> for PathBuf {
|
||||||
|
fn borrow(&self) -> &Path {
|
||||||
|
Path::from_str(self.0.as_str())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<T: ToExitCode>(code: T) -> ! {
|
||||||
|
// TODO handle closing files, cleanup, etc.
|
||||||
|
|
||||||
|
unsafe { raw_exit(code.to_exit_status()) }
|
||||||
|
}
|
||||||
+110
@@ -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<T> {
|
||||||
|
value: UnsafeCell<T>,
|
||||||
|
inner: RawMutex,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct MutexGuard<'a, T> {
|
||||||
|
lock: &'a Mutex<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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<T> Mutex<T> {
|
||||||
|
pub const fn new(value: T) -> Self {
|
||||||
|
Self {
|
||||||
|
value: UnsafeCell::new(value),
|
||||||
|
inner: RawMutex::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lock(&self) -> MutexGuard<T> {
|
||||||
|
self.inner.lock();
|
||||||
|
MutexGuard { lock: self }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Deref for MutexGuard<'_, T> {
|
||||||
|
type Target = T;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
unsafe { &*self.lock.value.get() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> DerefMut for MutexGuard<'_, T> {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
unsafe { &mut *self.lock.value.get() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Drop for MutexGuard<'_, T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
self.lock.inner.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
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>;
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
#![allow(non_camel_case_types)]
|
||||||
|
|
||||||
|
use core::ffi::c_int;
|
||||||
|
|
||||||
|
pub type wchar_t = i32;
|
||||||
+79
@@ -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<T> Nullable for *const T {
|
||||||
|
fn is_null(&self) -> bool {
|
||||||
|
<*const T>::is_null(*self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> 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<Self> {
|
||||||
|
(!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<Self::Item> {
|
||||||
|
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<Item = (&'static str, &'static str)> {
|
||||||
|
// 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<P: AsRef<Path>>(name: P) -> EResult<PathBuf> {
|
||||||
|
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)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user