Initial commit: most of <string.h>

This commit is contained in:
2024-01-08 18:35:01 +02:00
commit ab268f1fb6
48 changed files with 2506 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
/target
+14
View File
@@ -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"
+54
View File
@@ -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);
});
}
+6
View File
@@ -0,0 +1,6 @@
#ifndef _YGGDRASIL_ERRNO_H
#define _YGGDRASIL_ERRNO_H 1
extern int errno;
#endif
+6
View File
@@ -0,0 +1,6 @@
#ifndef _YGGDRASIL_LOCALE_H
#define _YGGDRASIL_LOCALE_H 1
#define LC_GLOBAL_LOCALE ((locale_t) -1L)
#endif
+6
View File
@@ -0,0 +1,6 @@
#ifndef _YGGDRASIL_MATH_H
#define _YGGDRASIL_MATH_H 1
#define INFINITY __builtin_inff()
#endif
+7
View File
@@ -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
+11
View File
@@ -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
+8
View File
@@ -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
+16
View File
@@ -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>
+1
View File
@@ -0,0 +1 @@
pub mod rust;
+4
View File
@@ -0,0 +1,4 @@
use libyalloc::GlobalAllocator;
#[global_allocator]
static GLOBAL_ALLOCATOR: GlobalAllocator = GlobalAllocator;
+1
View File
@@ -0,0 +1 @@
#![allow(non_camel_case_types)]
+116
View File
@@ -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),
}
}
}
+3
View File
@@ -0,0 +1,3 @@
pub trait FileImpl {}
pub struct File {}
+10
View File
@@ -0,0 +1,10 @@
language = "C"
style = "Type"
sys_includes = []
no_includes = true
include_guard = "_ERRNO_H"
trailer = "#include <bits/errno.h>"
[export]
+174
View File
@@ -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,
}
}
}
+13
View File
@@ -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"]
+84
View File
@@ -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!()
}
+12
View File
@@ -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]
+1
View File
@@ -0,0 +1 @@
pub const NAN: f64 = f64::NAN;
+12
View File
@@ -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;
+14
View File
@@ -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"]
+45
View File
@@ -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
}
+47
View File
@@ -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()
}
+250
View File
@@ -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_;
}
+170
View File
@@ -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)
}
}
+303
View File
@@ -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)
}
}
+133
View File
@@ -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,
}
}
+12
View File
@@ -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 = []
+4
View File
@@ -0,0 +1,4 @@
use core::ffi::c_int;
pub const EXIT_SUCCESS: c_int = 0;
pub const EXIT_FAILURE: c_int = 1;
+11
View File
@@ -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]
+73
View File
@@ -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 _
}
+9
View File
@@ -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;
}
+315
View File
@@ -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!()
}
+10
View File
@@ -0,0 +1,10 @@
language = "C"
style = "Type"
sys_includes = ["stddef.h"]
no_includes = true
include_guard = "_SYS_TYPES_H"
[export]
include = ["pid_t"]
+3
View File
@@ -0,0 +1,3 @@
use core::ffi::c_int;
pub type pid_t = i32;
+12
View File
@@ -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 = []
+34
View File
@@ -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,
}
}
+13
View File
@@ -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 = []
+114
View File
@@ -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
View File
@@ -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
View File
@@ -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())
}
}
+27
View File
@@ -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
View File
@@ -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();
}
}
}
+16
View File
@@ -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>;
}
+5
View File
@@ -0,0 +1,5 @@
#![allow(non_camel_case_types)]
use core::ffi::c_int;
pub type wchar_t = i32;
+79
View File
@@ -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)
}