libc: basic ygglibc with stubs
This commit is contained in:
parent
cdf9243962
commit
e0600c2bf6
@ -6,6 +6,7 @@ exclude = [
|
||||
"tool/abi-generator",
|
||||
"toolchain",
|
||||
"userspace/dynload-program",
|
||||
"userspace/lib/ygglibc"
|
||||
]
|
||||
members = [
|
||||
"xtask",
|
||||
|
@ -535,7 +535,7 @@ fn write_rela(rela: &Rela, space: &ProcessAddressSpace, b: usize) -> Result<(),
|
||||
|
||||
match width {
|
||||
8 => {
|
||||
unsafe { (dst.as_mut_ptr() as *mut u64).write_volatile(value as u64) };
|
||||
unsafe { (dst.as_mut_ptr() as *mut u64).write_unaligned(value as u64) };
|
||||
Ok(())
|
||||
}
|
||||
_ => todo!("Unhandled rela width: {}", width),
|
||||
|
9
test.c
Normal file
9
test.c
Normal file
@ -0,0 +1,9 @@
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
char buf[512];
|
||||
int v = 1234 + 3;
|
||||
snprintf(buf, sizeof(buf), "Hello %d\n", v);
|
||||
return 0;
|
||||
}
|
@ -17,9 +17,8 @@ members = [
|
||||
"lib/yasync",
|
||||
"rsh",
|
||||
"lib/cross",
|
||||
"crypt"
|
||||
]
|
||||
exclude = ["dynload-program", "test-kernel-module"]
|
||||
"crypt"]
|
||||
exclude = ["dynload-program", "test-kernel-module", "lib/ygglibc"]
|
||||
|
||||
[workspace.dependencies]
|
||||
log = "0.4.22"
|
||||
|
1
userspace/lib/ygglibc/.gitignore
vendored
Normal file
1
userspace/lib/ygglibc/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/target
|
576
userspace/lib/ygglibc/Cargo.lock
generated
Normal file
576
userspace/lib/ygglibc/Cargo.lock
generated
Normal file
@ -0,0 +1,576 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "abi-generator"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "abi-lib"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
"anstyle-query",
|
||||
"anstyle-wincon",
|
||||
"colorchoice",
|
||||
"is_terminal_polyfill",
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
|
||||
dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
version = "1.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8334215b81e418a0a7bdb8ef0849474f40bb10c8b71f1c4ed315cff49f32494d"
|
||||
dependencies = [
|
||||
"bytemuck_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck_derive"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bcfcc3cd946cb52f0bbfdbbcfa2f4e24f75ebb6c0e1002f7c25904fada18b9ec"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cbindgen"
|
||||
version = "0.26.0"
|
||||
source = "git+https://git.alnyan.me/yggdrasil/cbindgen.git?branch=master#97f9e1961ab890c7d0f760793abd27b048982b74"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"heck",
|
||||
"indexmap",
|
||||
"log",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"syn 1.0.109",
|
||||
"tempfile",
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.1.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baee610e9452a8f6f0a1b6194ec09ff9e2d85dea54432acdae41aa0761c95d70"
|
||||
dependencies = [
|
||||
"shlex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"clap_lex",
|
||||
"strsim",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is_terminal_polyfill"
|
||||
version = "1.70.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.161"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1"
|
||||
|
||||
[[package]]
|
||||
name = "libyalloc"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"yggdrasil-rt",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
|
||||
|
||||
[[package]]
|
||||
name = "prettyplease"
|
||||
version = "0.2.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.39"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "375116bee2be9ed569afe2154ea6a99dfdffd257f533f187498c2a8f5feaf4ee"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.214"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.214"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.132"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "0.6.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.109"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.87"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"fastrand",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.68"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02dd99dc800bbb97186339685293e1cc5d9df1f8fae2d0aecd9ff1c77efea892"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.68"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7c61ec9a6f64d2793d8a45faba21efbe3ced62a886d44c36a009b2b519b4c7e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.8.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"toml_edit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.6.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.22.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.6.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yggdrasil-abi"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"abi-generator",
|
||||
"abi-lib",
|
||||
"bytemuck",
|
||||
"prettyplease",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yggdrasil-rt"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"abi-generator",
|
||||
"abi-lib",
|
||||
"cc",
|
||||
"prettyplease",
|
||||
"yggdrasil-abi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ygglibc"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cbindgen",
|
||||
"libyalloc",
|
||||
"yggdrasil-abi",
|
||||
"yggdrasil-rt",
|
||||
]
|
16
userspace/lib/ygglibc/Cargo.toml
Normal file
16
userspace/lib/ygglibc/Cargo.toml
Normal file
@ -0,0 +1,16 @@
|
||||
[package]
|
||||
name = "ygglibc"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
crate-type = ["staticlib"]
|
||||
|
||||
[dependencies]
|
||||
yggdrasil-rt = { path = "../../../lib/runtime" }
|
||||
yggdrasil-abi = { path = "../../../lib/abi", features = ["alloc", "bytemuck"] }
|
||||
libyalloc = { path = "../../../lib/libyalloc" }
|
||||
bitflags = "2.6.0"
|
||||
|
||||
[build-dependencies]
|
||||
cbindgen = { git = "https://git.alnyan.me/yggdrasil/cbindgen.git", branch = "master" }
|
75
userspace/lib/ygglibc/build.rs
Normal file
75
userspace/lib/ygglibc/build.rs
Normal file
@ -0,0 +1,75 @@
|
||||
use std::{
|
||||
env, fs::{self, DirEntry}, path::Path
|
||||
};
|
||||
|
||||
const RENAMES: &[(&str, &str)] = &[
|
||||
("CUsizeResult", "size_t"),
|
||||
("CIsizeResult", "ssize_t"),
|
||||
("CEofResult", "int"),
|
||||
("CIntCountResult", "int"),
|
||||
("CIntZeroResult", "int"),
|
||||
("CFdResult", "int"),
|
||||
("COffsetResult", "off_t"),
|
||||
("CPidResult", "pid_t"),
|
||||
];
|
||||
|
||||
fn include_dir(d: &DirEntry) -> bool {
|
||||
d.metadata().map(|m| m.is_dir()).unwrap_or(false)
|
||||
&& d.path()
|
||||
.iter()
|
||||
.nth(2)
|
||||
.map_or(false, |c| c.to_str().map_or(false, |x| !x.starts_with("_")))
|
||||
}
|
||||
|
||||
fn generate_header(config_path: impl AsRef<Path>, header_output: impl AsRef<Path>) {
|
||||
let config_path = config_path.as_ref();
|
||||
let header_output = header_output.as_ref();
|
||||
let relative_path = config_path
|
||||
.strip_prefix("src/headers")
|
||||
.ok()
|
||||
.and_then(|p| p.parent())
|
||||
.and_then(|p| p.to_str())
|
||||
.unwrap()
|
||||
.replace("_", "/");
|
||||
// TODO use outer workspace's target directory?
|
||||
let header_path = header_output
|
||||
.join(&relative_path)
|
||||
.with_extension("h");
|
||||
let mod_path = config_path.with_file_name("mod.rs");
|
||||
|
||||
let mut config = cbindgen::Config::from_file(config_path).unwrap();
|
||||
|
||||
config
|
||||
.export
|
||||
.rename
|
||||
.extend(RENAMES.into_iter().map(|&(x, y)| (x.into(), y.into())));
|
||||
|
||||
cbindgen::Builder::new()
|
||||
.with_config(config)
|
||||
.with_src(mod_path)
|
||||
.generate()
|
||||
.unwrap()
|
||||
.write_to_file(header_path);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let target = env::var("TARGET").expect("$TARGET is not set");
|
||||
let profile = env::var("PROFILE").expect("$PROFILE is not set");
|
||||
|
||||
let header_output = format!("target/{target}/{profile}/include");
|
||||
|
||||
fs::read_dir("src/headers")
|
||||
.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, &header_output);
|
||||
});
|
||||
}
|
||||
|
23
userspace/lib/ygglibc/etc/x86_64-unknown-none.json
Normal file
23
userspace/lib/ygglibc/etc/x86_64-unknown-none.json
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"is-builtin": false,
|
||||
"arch": "x86_64",
|
||||
"cpu": "x86-64",
|
||||
"os": "none",
|
||||
"llvm-target": "x86_64-unknown-linux-gnu",
|
||||
"data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128",
|
||||
"max-atomic-width": 64,
|
||||
"target-pointer-width": "64",
|
||||
"features": "-avx,-sse,+soft-float",
|
||||
|
||||
"disable-redzone": true,
|
||||
"executables": true,
|
||||
"panic-strategy": "abort",
|
||||
"dynamic-linking": true,
|
||||
"relocation-model": "static",
|
||||
"position-independent-executables": false,
|
||||
|
||||
"has-thread-local": false,
|
||||
|
||||
"linker": "rust-lld",
|
||||
"linker-flavor": "ld.lld"
|
||||
}
|
6
userspace/lib/ygglibc/include/bits/errno.h
Normal file
6
userspace/lib/ygglibc/include/bits/errno.h
Normal file
@ -0,0 +1,6 @@
|
||||
#ifndef _YGGDRASIL_ERRNO_H
|
||||
#define _YGGDRASIL_ERRNO_H 1
|
||||
|
||||
extern int errno;
|
||||
|
||||
#endif
|
17
userspace/lib/ygglibc/include/bits/stdio.h
Normal file
17
userspace/lib/ygglibc/include/bits/stdio.h
Normal file
@ -0,0 +1,17 @@
|
||||
#ifndef _YGGDRASIL_STDIO_H
|
||||
#define _YGGDRASIL_STDIO_H 1
|
||||
|
||||
struct __FILE;
|
||||
typedef struct __FILE FILE;
|
||||
|
||||
extern FILE *stdin;
|
||||
extern FILE *stdout;
|
||||
extern FILE *stderr;
|
||||
|
||||
int dprintf(int fd, const char *fmt, ...);
|
||||
int fprintf(FILE *fp, const char *fmt, ...);
|
||||
int printf(const char *fmt, ...);
|
||||
int snprintf(char *buf, size_t len, const char *fmt, ...);
|
||||
int sprintf(char *buf, const char *fmt, ...);
|
||||
|
||||
#endif
|
8
userspace/lib/ygglibc/include/bits/unistd.h
Normal file
8
userspace/lib/ygglibc/include/bits/unistd.h
Normal file
@ -0,0 +1,8 @@
|
||||
#ifndef _YGGDRASIL_UNISTD_H
|
||||
#define _YGGDRASIL_UNISTD_H 1
|
||||
|
||||
int execl(const char *path, const char *arg, ...);
|
||||
int execlp(const char *file, const char *arg, ...);
|
||||
int execle(const char *path, const char *arg, ...);
|
||||
|
||||
#endif
|
89
userspace/lib/ygglibc/src/allocator.rs
Normal file
89
userspace/lib/ygglibc/src/allocator.rs
Normal file
@ -0,0 +1,89 @@
|
||||
use core::{
|
||||
alloc::{GlobalAlloc, Layout}, ffi::c_void, ptr::{self, null, null_mut, NonNull}
|
||||
};
|
||||
|
||||
use libyalloc::{allocator::BucketAllocator, sys::PageProvider};
|
||||
use yggdrasil_rt::{mem::MappingSource, sys as syscall};
|
||||
|
||||
use crate::error::EResult;
|
||||
|
||||
struct Allocator;
|
||||
struct PageProviderImpl;
|
||||
|
||||
unsafe impl GlobalAlloc for Allocator {
|
||||
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
||||
let pointer = YALLOC.allocate(layout);
|
||||
match pointer {
|
||||
Some(ptr) => ptr.as_ptr(),
|
||||
None => null_mut(),
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
|
||||
let pointer = NonNull::new(ptr).expect("NULL pointer passed to dealloc()");
|
||||
YALLOC.free(pointer, layout);
|
||||
}
|
||||
}
|
||||
|
||||
impl PageProvider for PageProviderImpl {
|
||||
fn map_pages(count: usize) -> Option<NonNull<u8>> {
|
||||
match unsafe { syscall::map_memory(None, count * 0x1000, &MappingSource::Anonymous) } {
|
||||
Ok(address) => {
|
||||
let pointer = ptr::with_exposed_provenance_mut(address);
|
||||
NonNull::new(pointer)
|
||||
}
|
||||
Err(_error) => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn unmap_pages(address: NonNull<u8>, count: usize) {
|
||||
let address = address.as_ptr().addr();
|
||||
unsafe { syscall::unmap_memory(address, count * 0x1000) }
|
||||
.expect("Could not unmap memory pages");
|
||||
}
|
||||
}
|
||||
|
||||
const MALLOC_HEADER_SIZE: usize = size_of::<usize>();
|
||||
|
||||
fn c_alloc(size: usize, mut align: usize, zero: bool) -> EResult<NonNull<c_void>> {
|
||||
// assert!(align.is_power_of_two());
|
||||
// if align < align_of::<usize>() {
|
||||
// align = align_of::<usize>();
|
||||
// }
|
||||
// let offset = (MALLOC_HEADER_SIZE + align - 1) & !(align - 1);
|
||||
// let size = offset + size;
|
||||
// let layout = Layout::from_size_align(size, align).ok()?;
|
||||
|
||||
// let ptr = unsafe { YALLOC.allocate(layout) }?;
|
||||
|
||||
// if zero {
|
||||
// // TODO
|
||||
// }
|
||||
|
||||
// unsafe {
|
||||
// // Write the size right below the pointer
|
||||
// ptr.add(offset - size_of::<usize>()).cast::<usize>().write(size);
|
||||
// }
|
||||
|
||||
todo!()
|
||||
// unsafe { Some(ptr.cast::<c_void>().add(offset)) }
|
||||
}
|
||||
|
||||
pub unsafe fn malloc(size: usize) -> EResult<NonNull<c_void>> {
|
||||
todo!()
|
||||
// // TODO errno setting
|
||||
// match c_alloc(size, 16, false) {
|
||||
// Some(ptr) => ptr.as_ptr(),
|
||||
// None => null_mut()
|
||||
// }
|
||||
}
|
||||
|
||||
pub unsafe fn free(ptr: NonNull<c_void>) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[global_allocator]
|
||||
static ALLOCATOR: Allocator = Allocator;
|
||||
|
||||
// TODO locking for multithreaded binaries
|
||||
static mut YALLOC: BucketAllocator<PageProviderImpl> = BucketAllocator::new();
|
9
userspace/lib/ygglibc/src/args.rs
Normal file
9
userspace/lib/ygglibc/src/args.rs
Normal file
@ -0,0 +1,9 @@
|
||||
use alloc::{string::String, vec::Vec};
|
||||
use yggdrasil_rt::process::ProgramArgumentInner;
|
||||
|
||||
pub fn handle_kernel_argument(arg: usize) -> (Vec<String>, Vec<String>) {
|
||||
let arg_ptr: *const ProgramArgumentInner = core::ptr::with_exposed_provenance(arg);
|
||||
let arg = unsafe { arg_ptr.as_ref() }.expect("TODO");
|
||||
|
||||
todo!()
|
||||
}
|
260
userspace/lib/ygglibc/src/error.rs
Normal file
260
userspace/lib/ygglibc/src/error.rs
Normal file
@ -0,0 +1,260 @@
|
||||
use core::{
|
||||
convert::Infallible,
|
||||
ffi::c_int,
|
||||
ops::{ControlFlow, FromResidual, Try},
|
||||
ptr::{null_mut, NonNull},
|
||||
};
|
||||
|
||||
use yggdrasil_rt::io::RawFd;
|
||||
|
||||
use crate::headers::{errno::Errno, sys_types::off_t};
|
||||
|
||||
macro impl_from_residual($($ty:ty),+) {
|
||||
$(
|
||||
impl FromResidual<EResult<Infallible>> for $ty {
|
||||
fn from_residual(residual: EResult<Infallible>) -> Self {
|
||||
let err = residual.unwrap_err();
|
||||
unsafe {
|
||||
errno = err;
|
||||
}
|
||||
Self::ERROR
|
||||
}
|
||||
}
|
||||
)+
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[allow(non_upper_case_globals)]
|
||||
pub static mut errno: Errno = Errno(0);
|
||||
|
||||
pub trait CResult {
|
||||
const ERROR: Self;
|
||||
}
|
||||
|
||||
#[must_use = "EResult must be converted into its output type with proper errno set"]
|
||||
pub enum EResult<T> {
|
||||
Ok(T),
|
||||
Err(Errno),
|
||||
}
|
||||
|
||||
/// On success: non-null ptr
|
||||
/// On failure: NULL
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[repr(transparent)]
|
||||
pub struct CPtrResult<T>(*mut T);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[repr(transparent)]
|
||||
pub struct CIsizeResult(isize);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[repr(transparent)]
|
||||
pub struct CIntZeroResult(c_int);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[repr(transparent)]
|
||||
pub struct CEofResult(c_int);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[repr(transparent)]
|
||||
pub struct CIntCountResult(c_int);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[repr(transparent)]
|
||||
pub struct CFdResult(c_int);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[repr(transparent)]
|
||||
pub struct COffsetResult(off_t);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[repr(transparent)]
|
||||
pub struct CUsizeResult(usize);
|
||||
|
||||
pub trait TryFromExt<T>: Sized {
|
||||
fn e_try_from(value: T) -> EResult<Self>;
|
||||
}
|
||||
|
||||
impl<T> EResult<T> {
|
||||
// pub fn is_ok(&self) -> bool {
|
||||
// matches!(self, Self::Ok(_))
|
||||
// }
|
||||
|
||||
pub fn is_err(&self) -> bool {
|
||||
matches!(self, Self::Err(_))
|
||||
}
|
||||
|
||||
pub fn ok(self) -> Option<T> {
|
||||
match self {
|
||||
Self::Ok(res) => Some(res),
|
||||
Self::Err(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expect(self, message: &str) -> T {
|
||||
match self {
|
||||
Self::Ok(value) => value,
|
||||
Self::Err(err) => panic!("expect() failed: {}, errno={:?}", message, err)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unwrap_err(self) -> Errno {
|
||||
match self {
|
||||
Self::Ok(_) => panic!("Expected an error"),
|
||||
Self::Err(err) => err,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_result<E, F: FnOnce(Errno) -> E>(
|
||||
self,
|
||||
map_err: F,
|
||||
set_errno: bool,
|
||||
) -> Result<T, E> {
|
||||
match self {
|
||||
Self::Ok(value) => Ok(value),
|
||||
Self::Err(err) => {
|
||||
if set_errno {
|
||||
unsafe {
|
||||
errno = err;
|
||||
}
|
||||
}
|
||||
Err(map_err(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Try for EResult<T> {
|
||||
type Output = T;
|
||||
type Residual = EResult<Infallible>;
|
||||
|
||||
fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
|
||||
match self {
|
||||
Self::Ok(value) => ControlFlow::Continue(value),
|
||||
Self::Err(err) => ControlFlow::Break(EResult::Err(err)),
|
||||
}
|
||||
}
|
||||
|
||||
fn from_output(output: Self::Output) -> Self {
|
||||
Self::Ok(output)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> FromResidual<EResult<Infallible>> for EResult<T> {
|
||||
fn from_residual(residual: EResult<Infallible>) -> Self {
|
||||
let err = residual.unwrap_err();
|
||||
Self::Err(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, E: Into<Errno>> FromResidual<Result<Infallible, E>> for EResult<T> {
|
||||
fn from_residual(residual: Result<Infallible, E>) -> Self {
|
||||
let err = residual.unwrap_err();
|
||||
Self::Err(err.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, E: From<Errno>> FromResidual<EResult<Infallible>> for Result<T, E> {
|
||||
fn from_residual(residual: EResult<Infallible>) -> Self {
|
||||
let err = residual.unwrap_err();
|
||||
Self::Err(err.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> CPtrResult<T> {
|
||||
pub const fn success(ptr: NonNull<T>) -> Self {
|
||||
Self(ptr.as_ptr())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> CResult for CPtrResult<T> {
|
||||
const ERROR: Self = Self(null_mut());
|
||||
}
|
||||
|
||||
impl<T> FromResidual<EResult<Infallible>> for CPtrResult<T> {
|
||||
fn from_residual(residual: EResult<Infallible>) -> Self {
|
||||
let err = residual.unwrap_err();
|
||||
unsafe {
|
||||
errno = err;
|
||||
}
|
||||
Self::ERROR
|
||||
}
|
||||
}
|
||||
|
||||
impl CIsizeResult {
|
||||
pub const fn success(value: usize) -> Self {
|
||||
Self(value as _)
|
||||
}
|
||||
}
|
||||
|
||||
impl CResult for CIsizeResult {
|
||||
const ERROR: Self = Self(-1);
|
||||
}
|
||||
|
||||
impl CIntZeroResult {
|
||||
pub const SUCCESS: Self = Self(0);
|
||||
}
|
||||
|
||||
impl CResult for CIntZeroResult {
|
||||
const ERROR: Self = Self(-1);
|
||||
}
|
||||
|
||||
impl CEofResult {
|
||||
pub fn success(value: c_int) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl CResult for CEofResult {
|
||||
const ERROR: Self = Self(-1);
|
||||
}
|
||||
|
||||
impl CIntCountResult {
|
||||
pub fn success(value: c_int) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl CResult for CIntCountResult {
|
||||
const ERROR: Self = Self(-1);
|
||||
}
|
||||
|
||||
impl CFdResult {
|
||||
pub fn success(value: RawFd) -> Self {
|
||||
Self(value.into_raw().try_into().unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
impl CResult for CFdResult {
|
||||
const ERROR: Self = Self(-1);
|
||||
}
|
||||
|
||||
impl COffsetResult {
|
||||
pub fn success(value: u64) -> Self {
|
||||
Self(value.try_into().unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
impl CResult for COffsetResult {
|
||||
const ERROR: Self = Self(-1);
|
||||
}
|
||||
|
||||
impl CUsizeResult {
|
||||
pub fn success(value: usize) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl CResult for CUsizeResult {
|
||||
const ERROR: Self = Self(0);
|
||||
}
|
||||
|
||||
impl_from_residual!(
|
||||
CIsizeResult,
|
||||
CIntZeroResult,
|
||||
CEofResult,
|
||||
CIntCountResult,
|
||||
CFdResult,
|
||||
COffsetResult,
|
||||
CUsizeResult
|
||||
);
|
10
userspace/lib/ygglibc/src/headers/errno/cbindgen.toml
Normal file
10
userspace/lib/ygglibc/src/headers/errno/cbindgen.toml
Normal file
@ -0,0 +1,10 @@
|
||||
language = "C"
|
||||
style = "Type"
|
||||
|
||||
sys_includes = []
|
||||
no_includes = true
|
||||
|
||||
include_guard = "_ERRNO_H"
|
||||
trailer = "#include <bits/errno.h>"
|
||||
|
||||
[export]
|
188
userspace/lib/ygglibc/src/headers/errno/mod.rs
Normal file
188
userspace/lib/ygglibc/src/headers/errno/mod.rs
Normal file
@ -0,0 +1,188 @@
|
||||
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 ESUCCESS: Errno = Errno(0);
|
||||
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 EDEADLK: Errno = Errno(35);
|
||||
pub const ENAMETOOLONG: Errno = Errno(36);
|
||||
|
||||
pub const ENOLCK: Errno = Errno(37);
|
||||
pub const ENOSYS: Errno = Errno(38);
|
||||
pub const ENOTEMPTY: Errno = Errno(39);
|
||||
pub const ELOOP: Errno = Errno(40);
|
||||
pub const EWOULDBLOCK: Errno = EAGAIN;
|
||||
pub const ENOMSG: Errno = Errno(42);
|
||||
|
||||
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,
|
||||
|
||||
_ => todo!()
|
||||
}
|
||||
}
|
||||
}
|
8
userspace/lib/ygglibc/src/headers/mod.rs
Normal file
8
userspace/lib/ygglibc/src/headers/mod.rs
Normal file
@ -0,0 +1,8 @@
|
||||
#![allow(non_camel_case_types)]
|
||||
|
||||
pub mod errno;
|
||||
pub mod stdio;
|
||||
pub mod stdlib;
|
||||
pub mod unistd;
|
||||
|
||||
pub mod sys_types;
|
31
userspace/lib/ygglibc/src/headers/stdio/cbindgen.toml
Normal file
31
userspace/lib/ygglibc/src/headers/stdio/cbindgen.toml
Normal file
@ -0,0 +1,31 @@
|
||||
language = "C"
|
||||
style = "Type"
|
||||
|
||||
sys_includes = [
|
||||
"stdarg.h",
|
||||
"stddef.h",
|
||||
"stdint.h",
|
||||
"sys/types.h",
|
||||
"bits/stdio.h"
|
||||
# "limits.h"
|
||||
]
|
||||
no_includes = true
|
||||
|
||||
include_guard = "_STDIO_H"
|
||||
# trailer = "#include <bits/stdio.h>"
|
||||
|
||||
usize_type = "size_t"
|
||||
isize_type = "ssize_t"
|
||||
|
||||
[export]
|
||||
include = ["fpos_t"]
|
||||
exclude = [
|
||||
"printf",
|
||||
"fprintf",
|
||||
"dprintf",
|
||||
"sprintf",
|
||||
"snprintf",
|
||||
"scanf",
|
||||
"fscanf",
|
||||
"sscanf"
|
||||
]
|
192
userspace/lib/ygglibc/src/headers/stdio/file.rs
Normal file
192
userspace/lib/ygglibc/src/headers/stdio/file.rs
Normal file
@ -0,0 +1,192 @@
|
||||
use core::{
|
||||
ffi::{c_char, c_int, c_long, c_void},
|
||||
ptr::NonNull,
|
||||
};
|
||||
|
||||
use alloc::boxed::Box;
|
||||
use yggdrasil_rt::{debug_trace, io::OpenOptions, path::Path};
|
||||
|
||||
use crate::{
|
||||
error::{CEofResult, CFdResult, CIntZeroResult, CPtrResult, EResult},
|
||||
headers::sys_types::off_t,
|
||||
io::{
|
||||
self,
|
||||
managed::{FileOpenSource, FILE},
|
||||
},
|
||||
util::{PointerExt, PointerStrExt},
|
||||
};
|
||||
|
||||
use super::fpos_t;
|
||||
|
||||
fn open_inner<O: FileOpenSource>(source: O, mode_str: &[u8]) -> EResult<NonNull<FILE>> {
|
||||
let opts = match mode_str {
|
||||
b"r" | b"rb" => OpenOptions::READ,
|
||||
b"r+" | b"rb+" => OpenOptions::WRITE | OpenOptions::READ,
|
||||
b"w" | b"wb" => OpenOptions::WRITE | OpenOptions::TRUNCATE | OpenOptions::CREATE,
|
||||
b"w+" | b"wb+" => {
|
||||
OpenOptions::WRITE | OpenOptions::TRUNCATE | OpenOptions::CREATE | OpenOptions::READ
|
||||
}
|
||||
b"a" | b"ab" => {
|
||||
OpenOptions::APPEND | OpenOptions::CREATE | OpenOptions::READ | OpenOptions::WRITE
|
||||
}
|
||||
b"a+" | b"ab+" => {
|
||||
OpenOptions::APPEND | OpenOptions::CREATE | OpenOptions::READ | OpenOptions::WRITE
|
||||
}
|
||||
// TODO
|
||||
_ => todo!(),
|
||||
};
|
||||
|
||||
let file = source.open_with(opts)?;
|
||||
let file = NonNull::from(Box::leak(Box::new(file)));
|
||||
io::managed::register_file(file);
|
||||
EResult::Ok(file)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn clearerr(fp: *mut FILE) {}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn fclose(fp: *mut FILE) -> CEofResult {
|
||||
let fp = NonNull::new(fp).expect("fclose(): fp == NULL");
|
||||
|
||||
if !io::managed::deregister_file(fp) {
|
||||
debug_trace!("fclose() on non-registered file: {fp:p}");
|
||||
}
|
||||
|
||||
FILE::close(fp)?;
|
||||
CEofResult::success(0)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn fdopen(fd: c_int, mode: *const c_char) -> CPtrResult<FILE> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn feof(fp: *mut FILE) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn ferror(fp: *mut FILE) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn fflush(fp: *mut FILE) -> CIntZeroResult {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn fileno(fp: *mut FILE) -> CFdResult {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn flockfile(fp: *mut FILE) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn fgetpos(fp: *mut FILE, pos: *mut fpos_t) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn fmemopen(
|
||||
buffer: *mut c_void,
|
||||
size: usize,
|
||||
mode: *const c_char,
|
||||
) -> CPtrResult<FILE> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn freopen(
|
||||
path: *const c_char,
|
||||
mode: *const c_char,
|
||||
fp: *mut FILE,
|
||||
) -> CPtrResult<FILE> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn fopen(path: *const c_char, mode: *const c_char) -> CPtrResult<FILE> {
|
||||
let path = path.ensure_str();
|
||||
let mode = mode.ensure_cstr();
|
||||
let file = open_inner(Path::from_str(path), mode.to_bytes())?;
|
||||
CPtrResult::success(file)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn fseek(fp: *mut FILE, offset: c_long, whence: c_int) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn fseeko(fp: *mut FILE, offset: off_t, whence: c_int) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn fsetpos(fp: *mut FILE, pos: *const fpos_t) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn ftell(fp: *mut FILE) -> c_long {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn ftello(fp: *mut FILE) -> off_t {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn ftrylockfile(fp: *mut FILE) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn funlockfile(fp: *mut FILE) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn open_memstream(
|
||||
ptr: *mut *mut c_char,
|
||||
sizeloc: *mut usize,
|
||||
) -> CPtrResult<FILE> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn pclose(fp: *mut FILE) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn popen(command: *const c_char, ty: *const c_char) -> CPtrResult<FILE> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn rewind(fp: *mut FILE) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn setbuf(fp: *mut FILE, buf: *mut c_char) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn setvbuf(
|
||||
fp: *mut FILE,
|
||||
buffer: *mut c_void,
|
||||
mode: c_int,
|
||||
size: usize,
|
||||
) -> c_int {
|
||||
todo!()
|
||||
}
|
125
userspace/lib/ygglibc/src/headers/stdio/io.rs
Normal file
125
userspace/lib/ygglibc/src/headers/stdio/io.rs
Normal file
@ -0,0 +1,125 @@
|
||||
use core::ffi::{c_char, c_int, c_void};
|
||||
|
||||
use crate::{
|
||||
error::CUsizeResult,
|
||||
io::{managed::FILE, Read, Write},
|
||||
util::PointerExt,
|
||||
};
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn fgetc(fp: *mut FILE) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn fgets(buf: *mut c_char, size: c_int, fp: *mut FILE) -> *mut c_char {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn fputc(ch: c_int, fp: *mut FILE) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn fputs(str: *const c_char, fp: *mut FILE) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn fread(
|
||||
buf: *mut c_void,
|
||||
size: usize,
|
||||
nmemb: usize,
|
||||
fp: *mut FILE,
|
||||
) -> CUsizeResult {
|
||||
let size = size.checked_mul(nmemb).expect("size * nmemb too large");
|
||||
let buf = buf.cast::<u8>().ensure_slice_mut(size);
|
||||
let fp = fp.ensure_mut();
|
||||
let len = fp.read(buf)?;
|
||||
CUsizeResult::success(len)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn fwrite(
|
||||
buf: *const c_void,
|
||||
size: usize,
|
||||
nmemb: usize,
|
||||
fp: *mut FILE,
|
||||
) -> CUsizeResult {
|
||||
let size = size.checked_mul(nmemb).expect("size * nmemb too large");
|
||||
let buf = buf.cast::<u8>().ensure_slice(size);
|
||||
let fp = fp.ensure_mut();
|
||||
let len = fp.write(buf)?;
|
||||
CUsizeResult::success(len)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn getc(fp: *mut FILE) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn getchar() -> c_int {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn getc_unlocked(fp: *mut FILE) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn getchar_unlocked() -> c_int {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn getdelim(
|
||||
lineptr: *mut *mut c_char,
|
||||
size: *mut usize,
|
||||
delim: c_int,
|
||||
fp: *mut FILE,
|
||||
) -> isize {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn getline(lineptr: *mut *mut c_char, size: *mut usize, fp: *mut FILE) -> isize {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn gets(buf: *mut c_char) -> *mut c_char {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn putc(ch: c_int, fp: *mut FILE) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn putchar(ch: c_int) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn putc_unlocked(ch: c_int, fp: *mut FILE) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn putchar_unlocked(ch: c_int) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn puts(str: *const c_char) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn ungetc(ch: c_int, fp: *mut FILE) -> c_int {
|
||||
todo!()
|
||||
}
|
26
userspace/lib/ygglibc/src/headers/stdio/mod.rs
Normal file
26
userspace/lib/ygglibc/src/headers/stdio/mod.rs
Normal file
@ -0,0 +1,26 @@
|
||||
use core::ffi::c_int;
|
||||
|
||||
mod printf;
|
||||
mod scanf;
|
||||
mod file;
|
||||
mod io;
|
||||
mod util;
|
||||
|
||||
pub const _IOFBF: c_int = 0;
|
||||
pub const _IOLBF: c_int = 1;
|
||||
pub const _IONBF: c_int = 2;
|
||||
|
||||
pub const SEEK_SET: c_int = 0;
|
||||
pub const SEEK_CUR: c_int = 1;
|
||||
pub const SEEK_END: c_int = 2;
|
||||
|
||||
pub const TMP_MAX: usize = 216000;
|
||||
|
||||
pub const EOF: c_int = -1;
|
||||
|
||||
pub type fpos_t = u64;
|
||||
|
||||
pub const BUFSIZ: usize = 8192;
|
||||
|
||||
const UNGETC_MAX: usize = 128;
|
||||
|
166
userspace/lib/ygglibc/src/headers/stdio/printf/float.rs
Normal file
166
userspace/lib/ygglibc/src/headers/stdio/printf/float.rs
Normal file
@ -0,0 +1,166 @@
|
||||
use core::{ffi::c_double, fmt, num::FpCategory};
|
||||
|
||||
use alloc::{format, string::String};
|
||||
|
||||
use crate::{error::EResult, headers::errno, io::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 + fmt::Write>(
|
||||
val: c_double,
|
||||
exp: isize,
|
||||
exp_fmt: u8,
|
||||
output: &mut W,
|
||||
precision: usize,
|
||||
_opts: &FmtOpts,
|
||||
) -> EResult<usize> {
|
||||
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,
|
||||
// TODO proper error code
|
||||
Err(_) => return EResult::Err(errno::EINVAL),
|
||||
};
|
||||
|
||||
EResult::Ok(f_len + 2 + 2.max(exp_len))
|
||||
}
|
||||
|
||||
fn fmt_float_string(val: c_double, precision: usize) -> String {
|
||||
let string = format!("{:.p$}", val, p = precision);
|
||||
// TODO trim
|
||||
string
|
||||
}
|
||||
|
||||
fn fmt_float_finite<W: Write + fmt::Write>(
|
||||
val: c_double,
|
||||
output: &mut W,
|
||||
precision: usize,
|
||||
opts: &FmtOpts,
|
||||
) -> EResult<usize> {
|
||||
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())?;
|
||||
EResult::Ok(lpad + flen + rpad)
|
||||
}
|
||||
|
||||
fn fmt_float_nonfinite<W: Write + fmt::Write>(
|
||||
val: c_double,
|
||||
output: &mut W,
|
||||
upper: bool,
|
||||
opts: &FmtOpts,
|
||||
) -> EResult<usize> {
|
||||
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())?;
|
||||
|
||||
EResult::Ok(len + lpad + flen + rpad)
|
||||
}
|
||||
|
||||
pub fn fmt_float<W: Write + fmt::Write>(
|
||||
val: c_double,
|
||||
output: &mut W,
|
||||
upper: bool,
|
||||
opts: &FmtOpts,
|
||||
) -> EResult<usize> {
|
||||
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 + fmt::Write>(
|
||||
val: c_double,
|
||||
output: &mut W,
|
||||
upper: bool,
|
||||
opts: &FmtOpts,
|
||||
) -> EResult<usize> {
|
||||
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 + fmt::Write>(
|
||||
val: c_double,
|
||||
output: &mut W,
|
||||
upper: bool,
|
||||
opts: &FmtOpts,
|
||||
) -> EResult<usize> {
|
||||
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)
|
||||
}
|
||||
}
|
307
userspace/lib/ygglibc/src/headers/stdio/printf/format.rs
Normal file
307
userspace/lib/ygglibc/src/headers/stdio/printf/format.rs
Normal file
@ -0,0 +1,307 @@
|
||||
use core::{
|
||||
ffi::{
|
||||
c_char, c_double, c_int, c_long, c_longlong, c_short, c_uchar, c_uint, c_ulong,
|
||||
c_ulonglong, c_ushort, CStr, VaList,
|
||||
},
|
||||
fmt,
|
||||
};
|
||||
|
||||
use alloc::string::String;
|
||||
|
||||
use crate::{error::EResult, io::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,
|
||||
#[allow(unused)]
|
||||
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 + fmt::Write>(&self, output: &mut W, len: usize) -> EResult<usize> {
|
||||
let pad = self.width - core::cmp::min(self.width, len);
|
||||
for _ in 0..pad {
|
||||
output.write(&[self.pad_char])?;
|
||||
}
|
||||
EResult::Ok(pad)
|
||||
}
|
||||
|
||||
pub fn left_pad<W: Write + fmt::Write>(&self, output: &mut W, len: usize) -> EResult<usize> {
|
||||
if !self.left_adjust {
|
||||
self.pad(output, len)
|
||||
} else {
|
||||
EResult::Ok(0)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn right_pad<W: Write + fmt::Write>(&self, output: &mut W, len: usize) -> EResult<usize> {
|
||||
if self.left_adjust {
|
||||
self.pad(output, len)
|
||||
} else {
|
||||
EResult::Ok(0)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fmt<W: Write + fmt::Write>(
|
||||
&self,
|
||||
spec: FmtSpec,
|
||||
output: &mut W,
|
||||
ap: &mut VaList,
|
||||
) -> EResult<usize> {
|
||||
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 EResult::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 EResult::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)?;
|
||||
|
||||
EResult::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)
|
||||
}
|
||||
}
|
266
userspace/lib/ygglibc/src/headers/stdio/printf/mod.rs
Normal file
266
userspace/lib/ygglibc/src/headers/stdio/printf/mod.rs
Normal file
@ -0,0 +1,266 @@
|
||||
use core::{
|
||||
ffi::{c_char, c_int, VaList},
|
||||
fmt,
|
||||
ptr::NonNull,
|
||||
};
|
||||
|
||||
use format::{FmtOpts, FmtRadix, FmtSign, FmtSize, FmtSpec};
|
||||
|
||||
use crate::{
|
||||
error::{CIntCountResult, EResult, TryFromExt},
|
||||
io::{
|
||||
managed::{stdout, FILE},
|
||||
raw::RawFile,
|
||||
Write,
|
||||
},
|
||||
util::{PointerExt, PointerStrExt},
|
||||
};
|
||||
|
||||
mod float;
|
||||
mod format;
|
||||
|
||||
struct FileWriter<'w, W: Write>(&'w mut W);
|
||||
struct StringWriter {
|
||||
buffer: NonNull<c_char>,
|
||||
position: usize,
|
||||
capacity: usize,
|
||||
}
|
||||
|
||||
trait FmtWriter: Write + fmt::Write {}
|
||||
|
||||
impl<'w, W: Write> fmt::Write for FileWriter<'w, W> {
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
self.0
|
||||
.write_all(s.as_bytes())
|
||||
.into_result(|_| fmt::Error, true)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'w, W: Write> Write for FileWriter<'w, W> {
|
||||
fn write(&mut self, data: &[u8]) -> EResult<usize> {
|
||||
self.0.write(data)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> EResult<()> {
|
||||
self.0.flush()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'w, W: Write> FmtWriter for FileWriter<'w, W> {}
|
||||
|
||||
impl StringWriter {
|
||||
pub fn new(buffer: NonNull<c_char>, capacity: usize) -> Self {
|
||||
Self {
|
||||
buffer,
|
||||
capacity,
|
||||
position: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Write for StringWriter {
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
self.write(s.as_bytes()).into_result(|_| fmt::Error, true)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for StringWriter {
|
||||
fn write(&mut self, data: &[u8]) -> EResult<usize> {
|
||||
let count = core::cmp::min(self.capacity - self.position, data.len());
|
||||
if count > 0 {
|
||||
let dst = unsafe {
|
||||
core::slice::from_raw_parts_mut(
|
||||
self.buffer.add(self.position).as_ptr().cast(),
|
||||
count,
|
||||
)
|
||||
};
|
||||
dst.copy_from_slice(&data[..count]);
|
||||
self.position += count;
|
||||
}
|
||||
EResult::Ok(count)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> EResult<()> {
|
||||
EResult::Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl FmtWriter for StringWriter {}
|
||||
|
||||
fn printf_inner<W: FmtWriter>(output: &mut W, format: &[u8], mut ap: VaList) -> EResult<usize> {
|
||||
let mut fmt = format.into_iter();
|
||||
let mut count = 0;
|
||||
|
||||
while let Some(&c) = fmt.next() {
|
||||
if c != b'%' {
|
||||
count += output.write(&[c])?;
|
||||
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)?;
|
||||
}
|
||||
}
|
||||
|
||||
EResult::Ok(count)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn dprintf(fd: c_int, fmt: *const c_char, mut args: ...) -> CIntCountResult {
|
||||
vdprintf(fd, fmt, args.as_va_list())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn vdprintf(fd: c_int, fmt: *const c_char, args: VaList) -> CIntCountResult {
|
||||
let mut file = RawFile::e_try_from(fd)?;
|
||||
let fmt = fmt.ensure_cstr();
|
||||
let mut writer = FileWriter(&mut file);
|
||||
let count = printf_inner(&mut writer, fmt.to_bytes(), args)?;
|
||||
CIntCountResult::success(count.try_into().unwrap())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn printf(fmt: *const c_char, mut args: ...) -> CIntCountResult {
|
||||
vfprintf(stdout, fmt, args.as_va_list())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn vprintf(fmt: *const c_char, args: VaList) -> CIntCountResult {
|
||||
vfprintf(stdout, fmt, args)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn fprintf(fp: *mut FILE, fmt: *const c_char, mut args: ...) -> CIntCountResult {
|
||||
vfprintf(fp, fmt, args.as_va_list())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn vfprintf(fp: *mut FILE, fmt: *const c_char, args: VaList) -> CIntCountResult {
|
||||
let fp = fp.ensure_mut();
|
||||
let fmt = fmt.ensure_cstr();
|
||||
let mut writer = FileWriter(fp);
|
||||
let count = printf_inner(&mut writer, fmt.to_bytes(), args)?;
|
||||
CIntCountResult::success(count.try_into().unwrap())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn sprintf(
|
||||
buffer: *mut c_char,
|
||||
fmt: *const c_char,
|
||||
mut args: ...
|
||||
) -> CIntCountResult {
|
||||
vsnprintf(buffer, usize::MAX, fmt, args.as_va_list())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn snprintf(
|
||||
buffer: *mut c_char,
|
||||
capacity: usize,
|
||||
fmt: *const c_char,
|
||||
mut args: ...
|
||||
) -> CIntCountResult {
|
||||
vsnprintf(buffer, capacity, fmt, args.as_va_list())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn vsprintf(
|
||||
buffer: *mut c_char,
|
||||
fmt: *const c_char,
|
||||
args: VaList,
|
||||
) -> CIntCountResult {
|
||||
vsnprintf(buffer, usize::MAX, fmt, args)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn vsnprintf(
|
||||
buffer: *mut c_char,
|
||||
capacity: usize,
|
||||
fmt: *const c_char,
|
||||
args: VaList,
|
||||
) -> CIntCountResult {
|
||||
let buffer = NonNull::new(buffer).expect("vsnprintf(): buffer == NULL");
|
||||
let mut writer = StringWriter::new(buffer, capacity);
|
||||
let fmt = fmt.ensure_cstr();
|
||||
let count = printf_inner(&mut writer, fmt.to_bytes(), args)?;
|
||||
CIntCountResult::success(count.try_into().unwrap())
|
||||
}
|
115
userspace/lib/ygglibc/src/headers/stdio/scanf/char_set.rs
Normal file
115
userspace/lib/ygglibc/src/headers/stdio/scanf/char_set.rs
Normal file
@ -0,0 +1,115 @@
|
||||
use core::ops::RangeInclusive;
|
||||
|
||||
use super::FormatError;
|
||||
|
||||
pub struct ScanCharSet {
|
||||
mask: [u64; 4],
|
||||
invert: bool,
|
||||
}
|
||||
|
||||
impl ScanCharSet {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
mask: [0; 4],
|
||||
invert: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_format_iter<'a, I: Iterator<Item = &'a u8>>(
|
||||
mut it: I,
|
||||
) -> Result<(I, Self), FormatError> {
|
||||
let mut maybe_close_bracket = true;
|
||||
let mut hyphen = false;
|
||||
let mut start: Option<u8> = None;
|
||||
let mut set = ScanCharSet::new();
|
||||
|
||||
while let Some(&ch) = it.next() {
|
||||
if ch == b'^' && maybe_close_bracket {
|
||||
// "[^...]"
|
||||
set.set_invert();
|
||||
continue;
|
||||
}
|
||||
|
||||
if ch == b']' {
|
||||
if !maybe_close_bracket {
|
||||
if hyphen {
|
||||
// "[...-]"
|
||||
set.insert(b'-');
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
debug_assert!(!hyphen);
|
||||
// "[]...]" or "[^]...]"
|
||||
maybe_close_bracket = false;
|
||||
set.insert(b']');
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
maybe_close_bracket = false;
|
||||
|
||||
if ch == b'-' {
|
||||
if hyphen {
|
||||
// "--"?
|
||||
return Err(FormatError);
|
||||
}
|
||||
|
||||
hyphen = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Any other character
|
||||
if hyphen {
|
||||
let Some(start) = start.take() else {
|
||||
// "-x" without start?
|
||||
return Err(FormatError);
|
||||
};
|
||||
|
||||
let same_category = (start.is_ascii_digit() && ch.is_ascii_digit())
|
||||
|| (start.is_ascii_uppercase() && ch.is_ascii_uppercase())
|
||||
|| (start.is_ascii_lowercase() && ch.is_ascii_lowercase());
|
||||
|
||||
if same_category && ch > start {
|
||||
hyphen = false;
|
||||
set.insert_range(start..=ch);
|
||||
} else {
|
||||
return Err(FormatError);
|
||||
}
|
||||
} else {
|
||||
if let Some(start) = start.replace(ch) {
|
||||
// "c" without range
|
||||
set.insert(ch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok((it, set))
|
||||
}
|
||||
|
||||
pub fn set_invert(&mut self) {
|
||||
self.invert = true;
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, ch: u8) {
|
||||
let index = (ch / 64) as usize;
|
||||
let bit = 1 << (ch % 64);
|
||||
self.mask[index] |= bit;
|
||||
}
|
||||
|
||||
pub fn insert_range(&mut self, range: RangeInclusive<u8>) {
|
||||
for ch in range {
|
||||
self.insert(ch);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn contains(&self, ch: u8) -> bool {
|
||||
let index = (ch / 64) as usize;
|
||||
let bit = 1 << (ch % 64);
|
||||
self.mask[index] & bit == bit
|
||||
}
|
||||
|
||||
pub fn check(&self, ch: u8) -> bool {
|
||||
self.invert ^ self.contains(ch)
|
||||
}
|
||||
}
|
||||
|
434
userspace/lib/ygglibc/src/headers/stdio/scanf/format.rs
Normal file
434
userspace/lib/ygglibc/src/headers/stdio/scanf/format.rs
Normal file
@ -0,0 +1,434 @@
|
||||
use core::ffi::{
|
||||
c_char, c_int, c_long, c_longlong, c_short, c_uchar, c_uint, c_ulong, c_ulonglong, c_ushort,
|
||||
c_void, VaList,
|
||||
};
|
||||
|
||||
use super::{
|
||||
char_set::ScanCharSet,
|
||||
reader::{GetChar, ScanReader},
|
||||
ConversionError,
|
||||
};
|
||||
|
||||
pub enum ScanRadix {
|
||||
Decimal,
|
||||
Oct,
|
||||
Hex,
|
||||
Auto,
|
||||
}
|
||||
|
||||
pub enum ScanSpec {
|
||||
// %s, a sequence of non-whitespace characters
|
||||
Word,
|
||||
// %c, a sequence of characters whose length is specified by field width (default 1)
|
||||
Chars(ScanCharSet),
|
||||
// %[...], nonempty sequence of characters from the specified set
|
||||
AnyChar,
|
||||
// %d, %u, %x/%X, %o, %i, deprecated, optionally-signed/unsigned decimals
|
||||
Signed(ScanRadix),
|
||||
Unsigned(ScanRadix),
|
||||
// %n, reports consumed count
|
||||
ConsumedCount,
|
||||
// %p
|
||||
Pointer,
|
||||
// %%
|
||||
Percent,
|
||||
}
|
||||
|
||||
pub enum ScanSize {
|
||||
ShortShort,
|
||||
Short,
|
||||
Normal,
|
||||
Long,
|
||||
LongLong,
|
||||
Size,
|
||||
}
|
||||
|
||||
pub struct ScanOpt {
|
||||
pub width: Option<usize>,
|
||||
pub size: ScanSize,
|
||||
pub discard: bool,
|
||||
}
|
||||
|
||||
impl ScanSize {
|
||||
pub fn shorter(self) -> ScanSize {
|
||||
match self {
|
||||
Self::Normal => Self::Short,
|
||||
Self::Short => Self::ShortShort,
|
||||
_ => self,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn longer(self) -> ScanSize {
|
||||
match self {
|
||||
Self::Normal => Self::Long,
|
||||
Self::Long => Self::LongLong,
|
||||
_ => self,
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn write_int(&self, value: i64, ap: &mut VaList) {
|
||||
let raw = ap.arg::<*mut c_void>();
|
||||
|
||||
if raw.is_null() {
|
||||
panic!();
|
||||
}
|
||||
|
||||
match self {
|
||||
Self::ShortShort => *(raw as *mut c_char) = value as _,
|
||||
Self::Short => *(raw as *mut c_short) = value as _,
|
||||
Self::Normal => *(raw as *mut c_int) = value as _,
|
||||
Self::Long => *(raw as *mut c_long) = value as _,
|
||||
Self::LongLong => *(raw as *mut c_longlong) = value as _,
|
||||
Self::Size => *(raw as *mut isize) = value as _,
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn write_uint(&self, value: u64, ap: &mut VaList) {
|
||||
let raw = ap.arg::<*mut c_void>();
|
||||
|
||||
if raw.is_null() {
|
||||
panic!();
|
||||
}
|
||||
|
||||
match self {
|
||||
Self::ShortShort => *(raw as *mut c_uchar) = value as _,
|
||||
Self::Short => *(raw as *mut c_ushort) = value as _,
|
||||
Self::Normal => *(raw as *mut c_uint) = value as _,
|
||||
Self::Long => *(raw as *mut c_ulong) = value as _,
|
||||
Self::LongLong => *(raw as *mut c_ulonglong) = value as _,
|
||||
Self::Size => *(raw as *mut usize) = value as _,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ScanOpt {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
width: None,
|
||||
size: ScanSize::Normal,
|
||||
discard: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ScanOpt {
|
||||
pub fn scan<G: GetChar>(
|
||||
self,
|
||||
stream: &mut ScanReader<G>,
|
||||
spec: ScanSpec,
|
||||
ap: &mut VaList,
|
||||
) -> Result<usize, ConversionError> {
|
||||
let n = if self.discard { 0 } else { 1 };
|
||||
|
||||
match (spec, self.size) {
|
||||
(ScanSpec::Percent, _) => todo!(),
|
||||
(ScanSpec::Word, ScanSize::Normal) => {
|
||||
let width = self.width.unwrap_or(usize::MAX);
|
||||
let dst = if self.discard {
|
||||
None
|
||||
} else {
|
||||
let dst = unsafe { ap.arg::<*mut c_char>() };
|
||||
if dst.is_null() {
|
||||
panic!();
|
||||
}
|
||||
Some(dst)
|
||||
};
|
||||
|
||||
scan_word(stream, dst, width)?;
|
||||
|
||||
Ok(n)
|
||||
}
|
||||
(ScanSpec::Chars(set), ScanSize::Normal) => {
|
||||
let width = self.width.unwrap_or(usize::MAX);
|
||||
let dst = if self.discard {
|
||||
None
|
||||
} else {
|
||||
let dst = unsafe { ap.arg::<*mut c_char>() };
|
||||
if dst.is_null() {
|
||||
panic!();
|
||||
}
|
||||
Some(dst)
|
||||
};
|
||||
|
||||
scan_char_set(stream, dst, set, width)?;
|
||||
|
||||
Ok(n)
|
||||
}
|
||||
(ScanSpec::AnyChar, ScanSize::Normal) => {
|
||||
let width = self.width.unwrap_or(1);
|
||||
let dst = if self.discard {
|
||||
None
|
||||
} else {
|
||||
let dst = unsafe { ap.arg::<*mut c_char>() };
|
||||
if dst.is_null() {
|
||||
panic!();
|
||||
}
|
||||
Some(dst)
|
||||
};
|
||||
|
||||
scan_chars(stream, dst, width)?;
|
||||
|
||||
Ok(n)
|
||||
}
|
||||
(ScanSpec::Word, _) => todo!("%ls"),
|
||||
(ScanSpec::Chars(_), _) => todo!("%l[...]"),
|
||||
(ScanSpec::AnyChar, _) => todo!("%lc"),
|
||||
(ScanSpec::Signed(radix), size) => {
|
||||
let value = scan_signed(stream, radix)?;
|
||||
|
||||
if !self.discard {
|
||||
unsafe {
|
||||
size.write_int(value, ap);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(n)
|
||||
}
|
||||
(ScanSpec::Unsigned(radix), size) => {
|
||||
let value = scan_unsigned(stream, radix)?;
|
||||
|
||||
if !self.discard {
|
||||
unsafe {
|
||||
size.write_uint(value, ap);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(n)
|
||||
}
|
||||
(ScanSpec::Pointer, _) => todo!(),
|
||||
(ScanSpec::ConsumedCount, size) => {
|
||||
if !self.discard {
|
||||
unsafe {
|
||||
size.write_int(stream.consumed_count().try_into().unwrap(), ap);
|
||||
}
|
||||
}
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn scan_word<G: GetChar>(
|
||||
stream: &mut ScanReader<G>,
|
||||
mut dst: Option<*mut c_char>,
|
||||
limit: usize,
|
||||
) -> Result<(), ConversionError> {
|
||||
// Read until limit reached or whitespace/EOF
|
||||
let mut matched = 0;
|
||||
while matched < limit {
|
||||
let Some(ch) = stream.peek()? else {
|
||||
break;
|
||||
};
|
||||
|
||||
if ch.is_ascii_whitespace() {
|
||||
break;
|
||||
}
|
||||
|
||||
if let Some(dst) = dst.as_mut() {
|
||||
unsafe {
|
||||
**dst = ch as _;
|
||||
*dst = dst.add(1);
|
||||
}
|
||||
}
|
||||
matched += 1;
|
||||
|
||||
stream.next()?;
|
||||
}
|
||||
|
||||
if matched == 0 {
|
||||
return Err(ConversionError::ConversionFailed);
|
||||
}
|
||||
|
||||
if let Some(dst) = dst.as_mut() {
|
||||
unsafe {
|
||||
**dst = 0;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn scan_chars<G: GetChar>(
|
||||
stream: &mut ScanReader<G>,
|
||||
mut dst: Option<*mut c_char>,
|
||||
width: usize,
|
||||
) -> Result<(), ConversionError> {
|
||||
let mut matched = 0;
|
||||
|
||||
while matched < width {
|
||||
let Some(ch) = stream.next()? else {
|
||||
break;
|
||||
};
|
||||
|
||||
if let Some(dst) = dst.as_mut() {
|
||||
unsafe {
|
||||
**dst = ch as _;
|
||||
*dst = dst.add(1);
|
||||
}
|
||||
}
|
||||
|
||||
matched += 1;
|
||||
stream.next()?;
|
||||
}
|
||||
|
||||
if matched == width {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ConversionError::ConversionFailed)
|
||||
}
|
||||
}
|
||||
|
||||
fn scan_char_set<G: GetChar>(
|
||||
stream: &mut ScanReader<G>,
|
||||
mut dst: Option<*mut c_char>,
|
||||
set: ScanCharSet,
|
||||
width: usize,
|
||||
) -> Result<(), ConversionError> {
|
||||
let mut matched = 0;
|
||||
|
||||
while matched < width {
|
||||
let Some(ch) = stream.peek()? else {
|
||||
break;
|
||||
};
|
||||
|
||||
if !set.check(ch) {
|
||||
break;
|
||||
}
|
||||
|
||||
if let Some(dst) = dst.as_mut() {
|
||||
unsafe {
|
||||
**dst = ch as _;
|
||||
*dst = dst.add(1);
|
||||
}
|
||||
}
|
||||
|
||||
stream.next()?;
|
||||
matched += 1;
|
||||
}
|
||||
|
||||
if matched == 0 {
|
||||
return Err(ConversionError::ConversionFailed);
|
||||
}
|
||||
|
||||
if let Some(dst) = dst.as_mut() {
|
||||
unsafe {
|
||||
**dst = 0;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn scan_unsigned<G: GetChar>(
|
||||
stream: &mut ScanReader<G>,
|
||||
radix: ScanRadix,
|
||||
) -> Result<u64, ConversionError> {
|
||||
let mut value = 0;
|
||||
let mut matched = 0;
|
||||
|
||||
match radix {
|
||||
ScanRadix::Auto => {
|
||||
let Some(maybe_0) = stream.peek()? else {
|
||||
return Err(ConversionError::ConversionFailed);
|
||||
};
|
||||
|
||||
if maybe_0 == b'0' {
|
||||
stream.next()?;
|
||||
|
||||
// Maybe x/X or octal
|
||||
let Some(maybe_next) = stream.peek()? else {
|
||||
// At least matched a zero
|
||||
return Ok(0);
|
||||
};
|
||||
|
||||
return match maybe_next {
|
||||
b'x' | b'X' => {
|
||||
stream.next()?;
|
||||
scan_unsigned(stream, ScanRadix::Hex)
|
||||
}
|
||||
b'0'..=b'9' => scan_unsigned(stream, ScanRadix::Oct),
|
||||
_ => Err(ConversionError::ConversionFailed),
|
||||
};
|
||||
} else {
|
||||
// Anything else is a decimal
|
||||
return scan_unsigned(stream, ScanRadix::Decimal);
|
||||
}
|
||||
}
|
||||
ScanRadix::Oct => {
|
||||
while let Some(ch) = stream.peek()? {
|
||||
let digit = match ch {
|
||||
b'0'..=b'7' => (ch - b'0') as u64,
|
||||
_ => break,
|
||||
};
|
||||
|
||||
stream.next()?;
|
||||
|
||||
value <<= 3;
|
||||
value |= digit;
|
||||
|
||||
matched += 1;
|
||||
}
|
||||
}
|
||||
ScanRadix::Decimal => {
|
||||
while let Some(ch) = stream.peek()? {
|
||||
let digit = match ch {
|
||||
b'0'..=b'9' => (ch - b'0') as u64,
|
||||
_ => break,
|
||||
};
|
||||
|
||||
stream.next()?;
|
||||
|
||||
value *= 10;
|
||||
value += digit;
|
||||
|
||||
matched += 1;
|
||||
}
|
||||
}
|
||||
ScanRadix::Hex => {
|
||||
while let Some(ch) = stream.peek()? {
|
||||
let digit = match ch {
|
||||
b'0'..=b'9' => (ch - b'0') as u64,
|
||||
b'a'..=b'f' => (ch - b'a') as u64 + 10,
|
||||
b'A'..=b'F' => (ch - b'A') as u64 + 10,
|
||||
_ => break,
|
||||
};
|
||||
|
||||
stream.next()?;
|
||||
|
||||
value <<= 4;
|
||||
value |= digit;
|
||||
|
||||
matched += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if matched != 0 {
|
||||
Ok(value)
|
||||
} else {
|
||||
// No characters matched
|
||||
Err(ConversionError::ConversionFailed)
|
||||
}
|
||||
}
|
||||
|
||||
fn scan_signed<G: GetChar>(
|
||||
stream: &mut ScanReader<G>,
|
||||
radix: ScanRadix,
|
||||
) -> Result<i64, ConversionError> {
|
||||
let sign = if let Some(ch) = stream.peek()? {
|
||||
if ch == b'-' {
|
||||
stream.next()?;
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
} else {
|
||||
// EOF
|
||||
return Err(ConversionError::ConversionFailed);
|
||||
};
|
||||
|
||||
let value: i64 = scan_unsigned(stream, radix)?
|
||||
.try_into()
|
||||
.map_err(|_| ConversionError::ConversionFailed)?;
|
||||
|
||||
Ok(if sign { value } else { -value })
|
||||
}
|
207
userspace/lib/ygglibc/src/headers/stdio/scanf/mod.rs
Normal file
207
userspace/lib/ygglibc/src/headers/stdio/scanf/mod.rs
Normal file
@ -0,0 +1,207 @@
|
||||
use core::ffi::{c_char, VaList};
|
||||
|
||||
use char_set::ScanCharSet;
|
||||
use format::{ScanOpt, ScanRadix, ScanSize, ScanSpec};
|
||||
use reader::{GetChar, ScanReader};
|
||||
|
||||
use crate::{
|
||||
error::{CEofResult, EResult},
|
||||
headers::errno::{self, Errno},
|
||||
io::managed::{stdin, FILE}, util::{PointerExt, PointerStrExt},
|
||||
};
|
||||
|
||||
mod char_set;
|
||||
mod format;
|
||||
mod reader;
|
||||
|
||||
pub enum ConversionError {
|
||||
Error(Errno),
|
||||
ConversionFailed,
|
||||
}
|
||||
|
||||
pub struct FormatError;
|
||||
|
||||
impl From<Errno> for ConversionError {
|
||||
fn from(value: Errno) -> Self {
|
||||
Self::Error(value)
|
||||
}
|
||||
}
|
||||
|
||||
fn scanf_inner<G: GetChar>(
|
||||
stream: &mut ScanReader<G>,
|
||||
format: &[u8],
|
||||
mut ap: VaList,
|
||||
) -> EResult<usize> {
|
||||
let mut it = format.into_iter();
|
||||
let mut matched = 0;
|
||||
let mut skip_space = false;
|
||||
|
||||
while let Some(&ch) = it.next() {
|
||||
if ch == b' ' {
|
||||
// Any amount of whitespace
|
||||
if skip_space {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip following whitespace in format
|
||||
skip_space = true;
|
||||
|
||||
// Skip whitespace in stream
|
||||
while let Some(ic) = stream.peek()? {
|
||||
if !ic.is_ascii_whitespace() {
|
||||
break;
|
||||
}
|
||||
|
||||
stream.next()?;
|
||||
}
|
||||
|
||||
continue;
|
||||
} else if ch != b'%' {
|
||||
// Regular character
|
||||
skip_space = false;
|
||||
|
||||
let ic = stream.peek()?;
|
||||
if !ic.map(|ic| ic == ch).unwrap_or(false) {
|
||||
break;
|
||||
}
|
||||
|
||||
stream.next()?;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
skip_space = false;
|
||||
|
||||
// %*123lld
|
||||
let mut opts = ScanOpt::default();
|
||||
let mut cur = it.next();
|
||||
|
||||
if let Some(&ch) = cur {
|
||||
if ch == b'*' {
|
||||
opts.discard = true;
|
||||
cur = it.next();
|
||||
}
|
||||
}
|
||||
|
||||
while let Some(&ch) = cur {
|
||||
if ch.is_ascii_digit() {
|
||||
opts.width
|
||||
.replace(opts.width.unwrap_or(0) * 10 + (ch - b'0') as usize);
|
||||
cur = it.next();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
while let Some(&ch) = cur {
|
||||
match ch {
|
||||
b'h' => opts.size = opts.size.shorter(),
|
||||
b'l' => opts.size = opts.size.longer(),
|
||||
b'z' => opts.size = ScanSize::Size,
|
||||
// TODO ptrdiff_t
|
||||
b't' => todo!(),
|
||||
// TODO intmax_t/uintmax_t
|
||||
b'j' => todo!(),
|
||||
// TODO floats
|
||||
b'L' | b'q' => todo!(),
|
||||
_ => break,
|
||||
}
|
||||
|
||||
cur = it.next();
|
||||
}
|
||||
|
||||
let spec = if let Some(&ch) = cur {
|
||||
match ch {
|
||||
b'i' => ScanSpec::Signed(ScanRadix::Auto),
|
||||
b'd' => ScanSpec::Signed(ScanRadix::Decimal),
|
||||
b'x' | b'X' => ScanSpec::Unsigned(ScanRadix::Hex),
|
||||
b'u' => ScanSpec::Unsigned(ScanRadix::Decimal),
|
||||
b'o' => ScanSpec::Unsigned(ScanRadix::Oct),
|
||||
b's' => ScanSpec::Word,
|
||||
b'[' => {
|
||||
let (new_it, set) = match ScanCharSet::from_format_iter(it) {
|
||||
Ok(v) => v,
|
||||
Err(FormatError) => return EResult::Err(errno::ESUCCESS),
|
||||
};
|
||||
it = new_it;
|
||||
ScanSpec::Chars(set)
|
||||
}
|
||||
b'c' => ScanSpec::AnyChar,
|
||||
b'n' => ScanSpec::ConsumedCount,
|
||||
b'p' => ScanSpec::Pointer,
|
||||
b'%' => ScanSpec::Percent,
|
||||
b'f' | b'e' | b'g' | b'E' | b'a' => todo!(),
|
||||
// Unrecognized specifier
|
||||
_ => return EResult::Err(errno::ESUCCESS),
|
||||
}
|
||||
} else {
|
||||
// No specifier after %
|
||||
return EResult::Err(errno::ESUCCESS);
|
||||
};
|
||||
|
||||
matched += match opts.scan(stream, spec, &mut ap) {
|
||||
Ok(matched) => matched,
|
||||
Err(ConversionError::ConversionFailed) => break,
|
||||
Err(ConversionError::Error(err)) => return EResult::Err(err),
|
||||
};
|
||||
}
|
||||
|
||||
// TODO report end of stream before anything is matched
|
||||
|
||||
EResult::Ok(matched)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn scanf(format: *const c_char, mut args: ...) -> CEofResult {
|
||||
vfscanf(stdin, format, args.as_va_list())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn fscanf(
|
||||
fp: *mut FILE,
|
||||
format: *const c_char,
|
||||
mut args: ...
|
||||
) -> CEofResult {
|
||||
vfscanf(fp, format, args.as_va_list())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn vscanf(format: *const c_char, args: VaList) -> CEofResult {
|
||||
vfscanf(stdin, format, args)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn vfscanf(
|
||||
fp: *mut FILE,
|
||||
format: *const c_char,
|
||||
args: VaList,
|
||||
) -> CEofResult {
|
||||
let fp = fp.ensure_mut();
|
||||
let format = format.ensure_cstr();
|
||||
let mut reader = ScanReader::new(fp);
|
||||
let count = scanf_inner(&mut reader, format.to_bytes(), args)?;
|
||||
CEofResult::success(count.try_into().unwrap())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn sscanf(
|
||||
input: *const c_char,
|
||||
format: *const c_char,
|
||||
mut args: ...
|
||||
) -> CEofResult {
|
||||
vsscanf(input, format, args.as_va_list())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn vsscanf(
|
||||
input: *const c_char,
|
||||
format: *const c_char,
|
||||
args: VaList,
|
||||
) -> CEofResult {
|
||||
let input = input.ensure_cstr();
|
||||
let format = format.ensure_cstr();
|
||||
let mut str_it = input.to_bytes().into_iter();
|
||||
let mut reader = ScanReader::new(&mut str_it);
|
||||
let count = scanf_inner(&mut reader, format.to_bytes(), args)?;
|
||||
CEofResult::success(count.try_into().unwrap())
|
||||
}
|
66
userspace/lib/ygglibc/src/headers/stdio/scanf/reader.rs
Normal file
66
userspace/lib/ygglibc/src/headers/stdio/scanf/reader.rs
Normal file
@ -0,0 +1,66 @@
|
||||
use crate::{error::EResult, io::{managed::FILE, Read}};
|
||||
|
||||
pub trait GetChar {
|
||||
fn getc(&mut self) -> EResult<Option<u8>>;
|
||||
}
|
||||
|
||||
pub struct ScanReader<'a, G: GetChar> {
|
||||
getter: &'a mut G,
|
||||
buffer: Option<u8>,
|
||||
consumed_count: usize,
|
||||
}
|
||||
|
||||
impl GetChar for FILE {
|
||||
fn getc(&mut self) -> EResult<Option<u8>> {
|
||||
let mut buf = [0];
|
||||
match self.read(&mut buf) {
|
||||
EResult::Ok(1) => EResult::Ok(Some(buf[0])),
|
||||
EResult::Ok(_) => EResult::Ok(None),
|
||||
EResult::Err(err) => EResult::Err(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, I: Iterator<Item = &'a u8>> GetChar for I {
|
||||
fn getc(&mut self) -> EResult<Option<u8>> {
|
||||
EResult::Ok(self.next().copied())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, G: GetChar> ScanReader<'a, G> {
|
||||
pub fn new(getter: &'a mut G) -> Self {
|
||||
Self {
|
||||
getter,
|
||||
buffer: None,
|
||||
consumed_count: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn getc(&mut self) -> EResult<Option<u8>> {
|
||||
match self.getter.getc() {
|
||||
EResult::Ok(Some(n)) => {
|
||||
self.consumed_count += 1;
|
||||
EResult::Ok(Some(n))
|
||||
}
|
||||
e => e,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn consumed_count(&self) -> usize {
|
||||
self.consumed_count
|
||||
}
|
||||
|
||||
pub fn peek(&mut self) -> EResult<Option<u8>> {
|
||||
if self.buffer.is_none() {
|
||||
self.buffer = self.getc()?;
|
||||
}
|
||||
EResult::Ok(self.buffer)
|
||||
}
|
||||
|
||||
pub fn next(&mut self) -> EResult<Option<u8>> {
|
||||
match self.buffer.take() {
|
||||
Some(v) => EResult::Ok(Some(v)),
|
||||
None => self.getc(),
|
||||
}
|
||||
}
|
||||
}
|
44
userspace/lib/ygglibc/src/headers/stdio/util.rs
Normal file
44
userspace/lib/ygglibc/src/headers/stdio/util.rs
Normal file
@ -0,0 +1,44 @@
|
||||
use core::ffi::{c_char, c_int};
|
||||
|
||||
use crate::{error::CPtrResult, io::managed::FILE};
|
||||
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn perror(message: *const c_char) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn ctermid(buf: *mut c_char) -> *mut c_char {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn remove(path: *const c_char) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn rename(src: *const c_char, dst: *const c_char) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn renameat(atfd: c_int, src: *const c_char, dst: *const c_char) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn tempnam(dir: *const c_char, prefix: *const c_char) -> *mut c_char {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn tmpfile() -> CPtrResult<FILE> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn tmpnam(template: *mut c_char) -> *mut c_char {
|
||||
todo!()
|
||||
}
|
12
userspace/lib/ygglibc/src/headers/stdlib/cbindgen.toml
Normal file
12
userspace/lib/ygglibc/src/headers/stdlib/cbindgen.toml
Normal file
@ -0,0 +1,12 @@
|
||||
language = "C"
|
||||
style = "Type"
|
||||
|
||||
sys_includes = [
|
||||
"stddef.h"
|
||||
]
|
||||
no_includes = true
|
||||
|
||||
include_guard = "_STDLIB_H"
|
||||
|
||||
usize_type = "size_t"
|
||||
isize_type = "ssize_t"
|
8
userspace/lib/ygglibc/src/headers/stdlib/mod.rs
Normal file
8
userspace/lib/ygglibc/src/headers/stdlib/mod.rs
Normal file
@ -0,0 +1,8 @@
|
||||
use core::ffi::c_void;
|
||||
|
||||
use crate::error::CPtrResult;
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn malloc(size: usize) -> CPtrResult<c_void> {
|
||||
todo!()
|
||||
}
|
30
userspace/lib/ygglibc/src/headers/sys_types/cbindgen.toml
Normal file
30
userspace/lib/ygglibc/src/headers/sys_types/cbindgen.toml
Normal file
@ -0,0 +1,30 @@
|
||||
language = "C"
|
||||
style = "Type"
|
||||
|
||||
sys_includes = ["stdint.h", "stddef.h"]
|
||||
no_includes = true
|
||||
|
||||
include_guard = "_SYS_TYPES_H"
|
||||
|
||||
[export]
|
||||
include = [
|
||||
"blkcnt_t",
|
||||
"blksize_t",
|
||||
"clock_t",
|
||||
"clockid_t",
|
||||
"dev_t",
|
||||
"fsblkcnt_t",
|
||||
"fsfilcnt_t",
|
||||
"gid_t",
|
||||
"id_t",
|
||||
"ino_t",
|
||||
"mode_t",
|
||||
"nlink_t",
|
||||
"off_t",
|
||||
"pid_t",
|
||||
"ssize_t",
|
||||
"suseconds_t",
|
||||
"time_t",
|
||||
"timer_t",
|
||||
"uid_t"
|
||||
]
|
34
userspace/lib/ygglibc/src/headers/sys_types/mod.rs
Normal file
34
userspace/lib/ygglibc/src/headers/sys_types/mod.rs
Normal file
@ -0,0 +1,34 @@
|
||||
use core::ffi::{c_int, c_ulong};
|
||||
|
||||
pub type pid_t = i32;
|
||||
pub type uid_t = i32;
|
||||
pub type gid_t = i32;
|
||||
pub type id_t = i32;
|
||||
|
||||
pub type dev_t = u32;
|
||||
|
||||
pub type clockid_t = i32;
|
||||
pub type timer_t = i32;
|
||||
|
||||
pub type mode_t = c_int;
|
||||
|
||||
#[cfg(any(target_pointer_width = "64", rust_analyzer))]
|
||||
pub type ssize_t = i64;
|
||||
|
||||
pub type blkcnt_t = i64;
|
||||
pub type blksize_t = c_ulong;
|
||||
pub type nlink_t = u64;
|
||||
|
||||
pub type fsblkcnt_t = u64;
|
||||
pub type fsfilcnt_t = u64;
|
||||
pub type ino_t = u64;
|
||||
|
||||
pub type clock_t = u64;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
|
||||
#[repr(transparent)]
|
||||
pub struct time_t(pub i64);
|
||||
|
||||
pub type off_t = i64;
|
||||
|
||||
pub type suseconds_t = c_ulong;
|
21
userspace/lib/ygglibc/src/headers/unistd/cbindgen.toml
Normal file
21
userspace/lib/ygglibc/src/headers/unistd/cbindgen.toml
Normal file
@ -0,0 +1,21 @@
|
||||
language = "C"
|
||||
style = "Type"
|
||||
|
||||
sys_includes = [
|
||||
"stddef.h",
|
||||
"sys/types.h"
|
||||
]
|
||||
no_includes = true
|
||||
|
||||
include_guard = "_UNISTD_H"
|
||||
trailer = "#include <bits/unistd.h>"
|
||||
|
||||
usize_type = "size_t"
|
||||
isize_type = "ssize_t"
|
||||
|
||||
[export]
|
||||
exclude = [
|
||||
"execl",
|
||||
"execlp",
|
||||
"execle"
|
||||
]
|
46
userspace/lib/ygglibc/src/headers/unistd/exec.rs
Normal file
46
userspace/lib/ygglibc/src/headers/unistd/exec.rs
Normal file
@ -0,0 +1,46 @@
|
||||
use core::ffi::{c_char, c_int, va_list, VaList};
|
||||
|
||||
use crate::error::CIntZeroResult;
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn execl(path: *const c_char, arg: *const c_char, args: ...) -> CIntZeroResult {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn execlp(file: *const c_char, arg: *const c_char, args: ...) -> CIntZeroResult {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn execle(path: *const c_char, arg: *const c_char, args: ...) -> CIntZeroResult {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn execv(path: *const c_char, argv: *const *mut c_char) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn execp(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 {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn fexecve(
|
||||
fd: c_int,
|
||||
argv: *const *mut c_char,
|
||||
envp: *const *mut c_char,
|
||||
) -> c_int {
|
||||
todo!()
|
||||
}
|
122
userspace/lib/ygglibc/src/headers/unistd/fs.rs
Normal file
122
userspace/lib/ygglibc/src/headers/unistd/fs.rs
Normal file
@ -0,0 +1,122 @@
|
||||
use core::ffi::{c_char, c_int, c_long};
|
||||
|
||||
use crate::headers::sys_types::{gid_t, off_t, uid_t};
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn access(path: *const c_char, mode: c_int) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn chdir(path: *const c_char) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn chown(path: *const c_char, uid: uid_t, gid: gid_t) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn faccessat(
|
||||
atfd: c_int,
|
||||
path: *const c_char,
|
||||
mode: c_int,
|
||||
flags: c_int,
|
||||
) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn fchdir(fd: c_int) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn fchown(fd: c_int, uid: uid_t, gid: gid_t) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn fchownat(fd: c_int, path: *const c_char, uid: uid_t, gid: gid_t) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn fpathconf(fd: c_int, name: c_int) -> c_long {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn ftruncate(fd: c_int, size: off_t) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn getcwd(buf: *mut c_char, len: usize) -> *mut c_char {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn lchown(path: *const c_char, uid: uid_t, gid: gid_t) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn link(path1: *const c_char, path2: *const c_char) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn linkat(
|
||||
path1fd: c_int,
|
||||
path1: *const c_char,
|
||||
path2fd: c_int,
|
||||
path2: *const c_char,
|
||||
flags: c_int,
|
||||
) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn lockf(fd: c_int, op: c_int, len: off_t) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn pathconf(path: *const c_char, name: c_int) -> c_long {
|
||||
todo!()
|
||||
}
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn readlink(path: *const c_char, buf: *mut c_char, len: usize) -> isize {
|
||||
todo!()
|
||||
}
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn readlinkat(
|
||||
atfd: c_int,
|
||||
path: *const c_char,
|
||||
buf: *mut c_char,
|
||||
len: usize,
|
||||
) -> isize {
|
||||
todo!()
|
||||
}
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn rmdir(path: *const c_char) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn symlink(path1: *const c_char, path2: *const c_char) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn symlinkat(path1: *const c_char, atfd: c_int, path2: *const c_char) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn truncate(path: *const c_char, size: off_t) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn unlink(path: *const c_char) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn unlinkat(atfd: c_int, path: *const c_char, flags: c_int) -> c_int {
|
||||
todo!()
|
||||
}
|
10
userspace/lib/ygglibc/src/headers/unistd/getopt.rs
Normal file
10
userspace/lib/ygglibc/src/headers/unistd/getopt.rs
Normal file
@ -0,0 +1,10 @@
|
||||
use core::ffi::{c_char, c_int};
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn getopt(
|
||||
argc: c_int,
|
||||
argv: *const *mut c_char,
|
||||
optstring: *const c_char,
|
||||
) -> c_int {
|
||||
todo!()
|
||||
}
|
108
userspace/lib/ygglibc/src/headers/unistd/io.rs
Normal file
108
userspace/lib/ygglibc/src/headers/unistd/io.rs
Normal file
@ -0,0 +1,108 @@
|
||||
use core::{ffi::{c_char, c_int, c_void}, mem::MaybeUninit};
|
||||
|
||||
use yggdrasil_rt::{debug_trace, io::{PipeOptions, RawFd, SeekFrom}};
|
||||
|
||||
use crate::{
|
||||
error::{CFdResult, CIntZeroResult, CIsizeResult, COffsetResult, TryFromExt},
|
||||
headers::sys_types::{off_t, pid_t},
|
||||
io::{self, raw::RawFile, IntoRawFd, Read, Seek, Write},
|
||||
};
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn close(fd: c_int) -> CIntZeroResult {
|
||||
let mut file = RawFile::e_try_from(fd)?;
|
||||
file.close()?;
|
||||
CIntZeroResult::SUCCESS
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn dup(oldfd: c_int) -> CFdResult {
|
||||
let oldfile = RawFile::e_try_from(oldfd)?;
|
||||
let newfd = oldfile.duplicate(None)?.into_raw_fd();
|
||||
CFdResult::success(newfd)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn dup2(oldfd: c_int, newfd: c_int) -> CFdResult {
|
||||
let oldfile = RawFile::e_try_from(oldfd)?;
|
||||
let newfd = RawFd::e_try_from(newfd)?;
|
||||
let newfd = oldfile.duplicate(Some(newfd))?.into_raw_fd();
|
||||
CFdResult::success(newfd)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn fdatasync(fd: c_int) -> CIntZeroResult {
|
||||
debug_trace!("TODO: fdatasync()");
|
||||
CIntZeroResult::SUCCESS
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn fsync(fd: c_int) -> CIntZeroResult {
|
||||
debug_trace!("TODO: fsync()");
|
||||
CIntZeroResult::SUCCESS
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn isatty(fd: c_int) -> c_int {
|
||||
debug_trace!("TODO: isatty()");
|
||||
0
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn lseek(fd: c_int, offset: off_t, whence: c_int) -> COffsetResult {
|
||||
let mut file = RawFile::e_try_from(fd)?;
|
||||
let seek = SeekFrom::e_try_from((offset, whence))?;
|
||||
let pos = file.seek(seek)?;
|
||||
COffsetResult::success(pos)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn pipe(fds: *mut c_int) -> CIntZeroResult {
|
||||
let fds = core::slice::from_raw_parts_mut(fds, 2);
|
||||
let (read, write) = io::create_pipe(PipeOptions::empty())?;
|
||||
fds[0] = read.into_raw().try_into().unwrap();
|
||||
fds[1] = write.into_raw().try_into().unwrap();
|
||||
CIntZeroResult::SUCCESS
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn pread(fd: c_int, buf: *mut c_void, len: usize, pos: off_t) -> CIsizeResult {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn pwrite(fd: c_int, buf: *const c_void, len: usize, pos: off_t) -> CIsizeResult {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn sync() {
|
||||
debug_trace!("TODO: sync()");
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn tcgetpgrp(fd: c_int) -> pid_t {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn tcsetpgrp(fd: c_int, pgrp: pid_t) -> CIntZeroResult {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn write(fd: c_int, buf: *const c_void, count: usize) -> CIsizeResult {
|
||||
let mut file = RawFile::e_try_from(fd)?;
|
||||
let data = core::slice::from_raw_parts(buf.cast::<u8>(), count);
|
||||
let count = file.write(data)?;
|
||||
|
||||
CIsizeResult::success(count)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn read(fd: c_int, buf: *mut c_void, count: usize) -> CIsizeResult {
|
||||
let mut file = RawFile::e_try_from(fd)?;
|
||||
let data = core::slice::from_raw_parts_mut(buf.cast::<u8>(), count);
|
||||
let count = file.read(data)?;
|
||||
CIsizeResult::success(count)
|
||||
}
|
8
userspace/lib/ygglibc/src/headers/unistd/mod.rs
Normal file
8
userspace/lib/ygglibc/src/headers/unistd/mod.rs
Normal file
@ -0,0 +1,8 @@
|
||||
#![allow(unused)]
|
||||
|
||||
mod exec;
|
||||
mod fs;
|
||||
mod getopt;
|
||||
mod io;
|
||||
mod process;
|
||||
mod util;
|
126
userspace/lib/ygglibc/src/headers/unistd/process.rs
Normal file
126
userspace/lib/ygglibc/src/headers/unistd/process.rs
Normal file
@ -0,0 +1,126 @@
|
||||
use core::{
|
||||
ffi::{c_char, c_int, c_uint},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use yggdrasil_rt::debug_trace;
|
||||
|
||||
use crate::{
|
||||
error::{CIntCountResult, CIntZeroResult},
|
||||
headers::sys_types::{gid_t, pid_t, uid_t},
|
||||
process,
|
||||
};
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn _exit(status: c_int) -> ! {
|
||||
process::c_exit_immediately(status)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn fork() -> pid_t {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn getegid() -> gid_t {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn geteuid() -> uid_t {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn getgid() -> gid_t {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn getgroups(gidsetsize: c_int, grouplist: *mut gid_t) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn getpgid(pid: pid_t) -> pid_t {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn getpgrp() -> pid_t {
|
||||
process::getpgrp()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn getpid() -> pid_t {
|
||||
process::getpid()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn getppid() -> pid_t {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn getsid(pid: pid_t) -> pid_t {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn getuid() -> uid_t {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn nice(incr: c_int) -> CIntCountResult {
|
||||
debug_trace!("TODO: nice()");
|
||||
CIntCountResult::success(0)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn pause() -> CIntZeroResult {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn setegid(gid: gid_t) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn seteuid(uid: uid_t) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn setgid(gid: gid_t) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn setpgid(pid: pid_t, pgid: pid_t) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn setpgrp() -> pid_t {
|
||||
todo!()
|
||||
}
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn setregid(rgid: gid_t, egid: gid_t) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn setreuid(ruid: uid_t, euid: uid_t) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn setsid() -> pid_t {
|
||||
todo!()
|
||||
}
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn setuid(uid: uid_t) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn sleep(seconds: c_uint) -> c_uint {
|
||||
let duration = Duration::from_secs(seconds.try_into().unwrap());
|
||||
process::sleep(duration).expect("TODO: sleep failed");
|
||||
0
|
||||
}
|
45
userspace/lib/ygglibc/src/headers/unistd/util.rs
Normal file
45
userspace/lib/ygglibc/src/headers/unistd/util.rs
Normal file
@ -0,0 +1,45 @@
|
||||
use core::ffi::{c_char, c_int, c_long, c_uint, c_void};
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn alarm(value: c_uint) -> c_uint {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn confstr(name: c_int, buf: *mut c_char, size: usize) -> usize {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn gethostid() -> c_long {
|
||||
todo!()
|
||||
}
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn gethostname(name: *mut c_char, namelen: usize) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn getlogin() -> *mut c_char {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn getlogin_r(name: *mut c_char, namelen: usize) -> c_int {
|
||||
todo!()
|
||||
}
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn swab(from: *const c_void, to: *mut c_void, n: isize) {
|
||||
todo!()
|
||||
}
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn sysconf(name: c_int) -> c_long {
|
||||
todo!()
|
||||
}
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn ttyname(fd: c_int) -> *mut c_char {
|
||||
todo!()
|
||||
}
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn ttyname_r(fd: c_int, buf: *mut c_char, len: usize) -> c_int {
|
||||
todo!()
|
||||
}
|
230
userspace/lib/ygglibc/src/io/buffer.rs
Normal file
230
userspace/lib/ygglibc/src/io/buffer.rs
Normal file
@ -0,0 +1,230 @@
|
||||
use core::{mem::MaybeUninit, ops::{Deref, DerefMut}, slice::memchr};
|
||||
|
||||
use alloc::{boxed::Box, vec::Vec};
|
||||
|
||||
use crate::error::EResult;
|
||||
|
||||
use super::{Read, Write};
|
||||
|
||||
// Read
|
||||
|
||||
enum ReadBufferBacking<'a> {
|
||||
Owned(Box<[MaybeUninit<u8>]>),
|
||||
Borrowed(&'a mut [MaybeUninit<u8>])
|
||||
}
|
||||
|
||||
pub struct ReadBuffer<'a> {
|
||||
data: ReadBufferBacking<'a>,
|
||||
position: usize,
|
||||
len: usize
|
||||
}
|
||||
|
||||
// Write
|
||||
|
||||
pub trait FileWriter: Write {
|
||||
fn reset(&mut self);
|
||||
}
|
||||
|
||||
pub struct UnbufferedWriter<W: Write> {
|
||||
inner: W
|
||||
}
|
||||
|
||||
pub struct FullBufferedWriter<W: Write> {
|
||||
inner: W,
|
||||
buffer: Vec<u8>
|
||||
}
|
||||
|
||||
pub struct LineBufferedWriter<W: Write> {
|
||||
inner: FullBufferedWriter<W>,
|
||||
need_flush: bool
|
||||
}
|
||||
|
||||
// Read impl
|
||||
|
||||
impl<'a> ReadBuffer<'a> {
|
||||
pub fn owned(capacity: usize) -> Self {
|
||||
Self {
|
||||
data: ReadBufferBacking::Owned(Box::new_uninit_slice(capacity)),
|
||||
position: 0,
|
||||
len: 0
|
||||
}
|
||||
}
|
||||
|
||||
pub fn borrowed(data: &'a mut [MaybeUninit<u8>]) -> Self {
|
||||
Self {
|
||||
data: ReadBufferBacking::Borrowed(data),
|
||||
position: 0,
|
||||
len: 0
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) {
|
||||
self.position = 0;
|
||||
self.len = 0;
|
||||
}
|
||||
|
||||
pub fn fill_from<R: Read + ?Sized>(&mut self, source: &mut R) -> EResult<&[u8]> {
|
||||
let buffer = unsafe { self.data.as_slice_mut() };
|
||||
if self.position == self.len {
|
||||
let amount = match source.read(buffer) {
|
||||
EResult::Ok(0) => return EResult::Ok(&buffer[..0]),
|
||||
EResult::Ok(n) => n,
|
||||
EResult::Err(err) => return EResult::Err(err),
|
||||
};
|
||||
|
||||
self.position = 0;
|
||||
self.len = amount;
|
||||
}
|
||||
|
||||
EResult::Ok(&buffer[self.position..self.len])
|
||||
}
|
||||
|
||||
pub fn consume(&mut self, amount: usize) {
|
||||
self.position = (self.position + amount).min(self.len);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ReadBufferBacking<'a> {
|
||||
unsafe fn as_slice_mut(&mut self) -> &mut [u8] {
|
||||
MaybeUninit::slice_assume_init_mut(self.deref_mut())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Deref for ReadBufferBacking<'a> {
|
||||
type Target = [MaybeUninit<u8>];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
match self {
|
||||
Self::Owned(owned) => owned.deref(),
|
||||
Self::Borrowed(borrowed) => borrowed.deref()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> DerefMut for ReadBufferBacking<'a> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
match self {
|
||||
Self::Owned(owned) => owned.deref_mut(),
|
||||
Self::Borrowed(borrowed) => borrowed.deref_mut(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Write impl
|
||||
|
||||
impl<W: Write> UnbufferedWriter<W> {
|
||||
pub fn new(inner: W) -> Self {
|
||||
Self { inner }
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> Write for UnbufferedWriter<W> {
|
||||
fn write(&mut self, data: &[u8]) -> EResult<usize> {
|
||||
self.inner.write(data)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> EResult<()> {
|
||||
EResult::Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> FileWriter for UnbufferedWriter<W> {
|
||||
fn reset(&mut self) {}
|
||||
}
|
||||
|
||||
impl<W: Write> FullBufferedWriter<W> {
|
||||
pub fn with_capacity(inner: W, capacity: usize) -> Self {
|
||||
Self {
|
||||
inner,
|
||||
buffer: Vec::with_capacity(capacity)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> Write for FullBufferedWriter<W> {
|
||||
fn write(&mut self, mut data: &[u8]) -> EResult<usize> {
|
||||
if data.len() + self.buffer.len() >= self.buffer.capacity() {
|
||||
self.flush()?;
|
||||
assert_eq!(self.buffer.len(), 0);
|
||||
}
|
||||
|
||||
let len = data.len();
|
||||
|
||||
if len >= self.buffer.capacity() {
|
||||
// Write directly from `data` until it can be fit into the buffer
|
||||
let (amount, result) = self
|
||||
.inner
|
||||
.write_chunked(&data[..len - self.buffer.capacity()]);
|
||||
|
||||
// Fail if write fails
|
||||
result?;
|
||||
|
||||
data = &data[amount..];
|
||||
}
|
||||
|
||||
// Store the data in the buffer
|
||||
assert!(data.len() <= self.buffer.capacity());
|
||||
self.buffer.extend_from_slice(data);
|
||||
EResult::Ok(len)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> EResult<()> {
|
||||
let (amount, result) = self.inner.write_chunked(&self.buffer);
|
||||
if amount != 0 {
|
||||
self.buffer.drain(..amount);
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> FileWriter for FullBufferedWriter<W> {
|
||||
fn reset(&mut self) {
|
||||
self.buffer.clear();
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> LineBufferedWriter<W> {
|
||||
pub fn with_capacity(inner: W, capacity: usize) -> Self {
|
||||
Self {
|
||||
inner: FullBufferedWriter::with_capacity(inner, capacity),
|
||||
need_flush: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> Write for LineBufferedWriter<W> {
|
||||
fn write(&mut self, data: &[u8]) -> EResult<usize> {
|
||||
if self.need_flush {
|
||||
self.flush()?;
|
||||
}
|
||||
|
||||
let Some(nl_pos) = memchr::memchr(b'\n', data) else {
|
||||
return self.inner.write(data);
|
||||
};
|
||||
// Write up to the last newline and flush
|
||||
let amount = self.inner.write(&data[..nl_pos + 1])?;
|
||||
self.need_flush = true;
|
||||
if self.flush().is_err() || amount != nl_pos + 1 {
|
||||
return EResult::Ok(amount);
|
||||
}
|
||||
|
||||
// Write the rest to the buffer
|
||||
match self.inner.write(&data[nl_pos + 1..]) {
|
||||
EResult::Ok(amount_to_buffer) => EResult::Ok(amount_to_buffer + amount),
|
||||
EResult::Err(_) => EResult::Ok(amount),
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> EResult<()> {
|
||||
self.inner.flush()?;
|
||||
self.need_flush = false;
|
||||
EResult::Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> FileWriter for LineBufferedWriter<W> {
|
||||
fn reset(&mut self) {
|
||||
self.inner.reset();
|
||||
self.need_flush = false;
|
||||
}
|
||||
}
|
383
userspace/lib/ygglibc/src/io/managed.rs
Normal file
383
userspace/lib/ygglibc/src/io/managed.rs
Normal file
@ -0,0 +1,383 @@
|
||||
#![allow(non_upper_case_globals)]
|
||||
|
||||
use core::{
|
||||
ops::{Deref, DerefMut},
|
||||
ptr::{null_mut, NonNull},
|
||||
};
|
||||
|
||||
use alloc::{boxed::Box, collections::btree_set::BTreeSet};
|
||||
use bitflags::bitflags;
|
||||
use yggdrasil_rt::{
|
||||
io::{FileMode, OpenOptions, RawFd, SeekFrom},
|
||||
path::Path,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
error::EResult,
|
||||
headers::errno,
|
||||
sync::{Mutex, RawMutex},
|
||||
};
|
||||
|
||||
use super::{
|
||||
buffer::{FileWriter, FullBufferedWriter, LineBufferedWriter, ReadBuffer, UnbufferedWriter},
|
||||
raw::RawFile,
|
||||
AsRawFd, FromRawFd, Read, Seek, Write,
|
||||
};
|
||||
|
||||
macro locked_op($self:expr, $op:expr) {{
|
||||
$self.lock.lock();
|
||||
let result = unsafe { $op };
|
||||
unsafe { $self.lock.release() };
|
||||
result
|
||||
}}
|
||||
|
||||
bitflags! {
|
||||
pub struct FileFlags: u32 {
|
||||
const READ = 1 << 0;
|
||||
const WRITE = 1 << 1;
|
||||
const EOF = 1 << 2;
|
||||
const ERROR = 1 << 3;
|
||||
const BUILTIN = 1 << 16;
|
||||
}
|
||||
}
|
||||
|
||||
pub trait FileOpenSource {
|
||||
fn open_with(self, open: OpenOptions) -> EResult<FILE>;
|
||||
}
|
||||
|
||||
pub enum BufferingMode {
|
||||
Full,
|
||||
Line,
|
||||
None,
|
||||
}
|
||||
|
||||
pub enum FileBacking {
|
||||
File(ManagedFile),
|
||||
}
|
||||
|
||||
pub struct ManagedFile {
|
||||
inner: RawFile,
|
||||
reference: bool,
|
||||
}
|
||||
|
||||
pub struct FILE {
|
||||
lock: RawMutex,
|
||||
|
||||
inner: FileBacking,
|
||||
read_buffer: Option<ReadBuffer<'static>>,
|
||||
write_buffer: Box<dyn FileWriter>,
|
||||
|
||||
flags: FileFlags,
|
||||
}
|
||||
|
||||
// ManagedFile
|
||||
|
||||
impl ManagedFile {
|
||||
pub fn open_at<P: AsRef<Path>>(
|
||||
at: Option<RawFd>,
|
||||
pathname: P,
|
||||
opts: OpenOptions,
|
||||
mode: FileMode,
|
||||
) -> EResult<Self> {
|
||||
let inner = RawFile::open_at(at, pathname, opts, mode)?;
|
||||
EResult::Ok(Self {
|
||||
inner,
|
||||
reference: false,
|
||||
})
|
||||
}
|
||||
|
||||
pub unsafe fn close(&mut self) -> EResult<()> {
|
||||
if self.reference {
|
||||
unreachable!("Cannot close a reference ManagedFile")
|
||||
}
|
||||
self.reference = true;
|
||||
RawFile::close(&mut self.inner)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for ManagedFile {
|
||||
type Target = RawFile;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for ManagedFile {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl FromRawFd for ManagedFile {
|
||||
unsafe fn from_raw_fd(fd: RawFd) -> Self {
|
||||
Self::from(RawFile::from_raw_fd(fd))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RawFile> for ManagedFile {
|
||||
fn from(inner: RawFile) -> Self {
|
||||
Self {
|
||||
inner,
|
||||
reference: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for ManagedFile {
|
||||
fn drop(&mut self) {
|
||||
if !self.reference {
|
||||
unsafe { self.close().ok() };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FileBacking
|
||||
|
||||
impl FileBacking {
|
||||
pub unsafe fn make_ref(&self) -> Self {
|
||||
match self {
|
||||
Self::File(file) => Self::File(ManagedFile {
|
||||
inner: RawFile::from_raw_fd(file.as_raw_fd()),
|
||||
reference: true,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn close(&mut self) -> EResult<()> {
|
||||
match self {
|
||||
Self::File(file) => file.close(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for FileBacking {
|
||||
fn write(&mut self, data: &[u8]) -> EResult<usize> {
|
||||
match self {
|
||||
Self::File(file) => file.write(data),
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> EResult<()> {
|
||||
match self {
|
||||
Self::File(file) => file.flush(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for FileBacking {
|
||||
fn read(&mut self, data: &mut [u8]) -> EResult<usize> {
|
||||
match self {
|
||||
Self::File(file) => file.read(data),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Seek for FileBacking {
|
||||
fn seek(&mut self, pos: SeekFrom) -> EResult<u64> {
|
||||
match self {
|
||||
Self::File(file) => file.seek(pos),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FILE
|
||||
|
||||
impl FILE {
|
||||
unsafe fn from_backing(
|
||||
backing: FileBacking,
|
||||
flags: FileFlags,
|
||||
buffering: BufferingMode,
|
||||
) -> Self {
|
||||
let write_buffer = backing.make_ref();
|
||||
let write_buffer: Box<dyn FileWriter> = match buffering {
|
||||
BufferingMode::None => Box::new(UnbufferedWriter::new(write_buffer)),
|
||||
BufferingMode::Line => Box::new(LineBufferedWriter::with_capacity(write_buffer, 1024)),
|
||||
BufferingMode::Full => Box::new(FullBufferedWriter::with_capacity(write_buffer, 1024)),
|
||||
};
|
||||
|
||||
Self {
|
||||
lock: RawMutex::new(),
|
||||
|
||||
inner: backing,
|
||||
write_buffer,
|
||||
read_buffer: None,
|
||||
|
||||
flags,
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn from_raw_file(file: RawFile, flags: FileFlags) -> Self {
|
||||
Self::from_managed_file(file.into(), flags)
|
||||
}
|
||||
|
||||
unsafe fn from_managed_file(file: ManagedFile, flags: FileFlags) -> Self {
|
||||
assert!(!flags.contains(FileFlags::BUILTIN));
|
||||
let backing = FileBacking::File(file);
|
||||
Self::from_backing(backing, flags, BufferingMode::Full)
|
||||
}
|
||||
|
||||
unsafe fn new_builtin(fd: RawFd, flags: FileFlags, buffering: BufferingMode) -> Self {
|
||||
let inner = FileBacking::File(ManagedFile::from_raw_fd(fd));
|
||||
Self::from_backing(inner, flags, buffering)
|
||||
}
|
||||
|
||||
pub unsafe fn close(mut ptr: NonNull<Self>) -> EResult<()> {
|
||||
let file = ptr.as_mut();
|
||||
|
||||
if file.flags.contains(FileFlags::WRITE) {
|
||||
file.flush()?;
|
||||
}
|
||||
|
||||
if !file.flags.contains(FileFlags::BUILTIN) {
|
||||
let mut this = Box::from_raw(file);
|
||||
let result = FileBacking::close(&mut this.inner);
|
||||
drop(this);
|
||||
result
|
||||
} else {
|
||||
EResult::Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) {
|
||||
if let Some(read_buffer) = self.read_buffer.as_mut() {
|
||||
read_buffer.reset();
|
||||
}
|
||||
self.write_buffer.reset();
|
||||
self.flags &= !(FileFlags::ERROR | FileFlags::EOF);
|
||||
}
|
||||
|
||||
unsafe fn read_unlocked(&mut self, data: &mut [u8]) -> EResult<usize> {
|
||||
if !self.flags.contains(FileFlags::READ) {
|
||||
self.flags |= FileFlags::ERROR;
|
||||
return EResult::Err(errno::EBADF);
|
||||
}
|
||||
|
||||
if self.read_buffer.is_some() {
|
||||
todo!()
|
||||
} else {
|
||||
match self.inner.read(data) {
|
||||
EResult::Ok(0) => {
|
||||
self.flags |= FileFlags::EOF;
|
||||
EResult::Ok(0)
|
||||
}
|
||||
EResult::Ok(len) => EResult::Ok(len),
|
||||
EResult::Err(err) => {
|
||||
self.flags |= FileFlags::ERROR;
|
||||
EResult::Err(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn write_unlocked(&mut self, data: &[u8]) -> EResult<usize> {
|
||||
if !self.flags.contains(FileFlags::WRITE) {
|
||||
self.flags |= FileFlags::ERROR;
|
||||
return EResult::Err(errno::EBADF);
|
||||
}
|
||||
|
||||
match self.write_buffer.write(data) {
|
||||
EResult::Ok(len) => EResult::Ok(len),
|
||||
EResult::Err(err) => {
|
||||
self.flags |= FileFlags::ERROR;
|
||||
EResult::Err(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn flush_unlocked(&mut self) -> EResult<()> {
|
||||
if !self.flags.contains(FileFlags::WRITE) {
|
||||
self.flags |= FileFlags::ERROR;
|
||||
return EResult::Err(errno::EBADF);
|
||||
}
|
||||
|
||||
match self.write_buffer.flush() {
|
||||
EResult::Ok(()) => EResult::Ok(()),
|
||||
EResult::Err(err) => {
|
||||
self.flags |= FileFlags::ERROR;
|
||||
EResult::Err(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for FILE {
|
||||
fn read(&mut self, data: &mut [u8]) -> EResult<usize> {
|
||||
locked_op!(self, self.read_unlocked(data))
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for FILE {
|
||||
fn write(&mut self, data: &[u8]) -> EResult<usize> {
|
||||
locked_op!(self, self.write_unlocked(data))
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> EResult<()> {
|
||||
locked_op!(self, self.flush_unlocked())
|
||||
}
|
||||
}
|
||||
|
||||
impl FileOpenSource for &Path {
|
||||
fn open_with(self, opts: OpenOptions) -> EResult<FILE> {
|
||||
let f = ManagedFile::open_at(None, self, opts, FileMode::default_file())?;
|
||||
let mut flags = FileFlags::empty();
|
||||
if opts.contains(OpenOptions::READ) {
|
||||
flags |= FileFlags::READ;
|
||||
}
|
||||
if opts.contains(OpenOptions::WRITE) {
|
||||
flags |= FileFlags::WRITE;
|
||||
}
|
||||
EResult::Ok(unsafe { FILE::from_managed_file(f, flags) })
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub static mut stdout: *mut FILE = null_mut();
|
||||
#[no_mangle]
|
||||
pub static mut stderr: *mut FILE = null_mut();
|
||||
#[no_mangle]
|
||||
pub static mut stdin: *mut FILE = null_mut();
|
||||
|
||||
static OPEN_FILES: Mutex<BTreeSet<NonNull<FILE>>> = Mutex::new(BTreeSet::new());
|
||||
|
||||
pub fn deregister_file(file: NonNull<FILE>) -> bool {
|
||||
OPEN_FILES.lock().remove(&file)
|
||||
}
|
||||
|
||||
pub fn register_file(file: NonNull<FILE>) {
|
||||
OPEN_FILES.lock().insert(file);
|
||||
}
|
||||
|
||||
pub unsafe fn init() {
|
||||
let stdin_ = Box::leak(Box::new(FILE::new_builtin(
|
||||
RawFd::STDIN,
|
||||
FileFlags::READ,
|
||||
BufferingMode::Line,
|
||||
)));
|
||||
let stdout_ = Box::leak(Box::new(FILE::new_builtin(
|
||||
RawFd::STDOUT,
|
||||
FileFlags::WRITE,
|
||||
BufferingMode::Line,
|
||||
)));
|
||||
let stderr_ = Box::leak(Box::new(FILE::new_builtin(
|
||||
RawFd::STDERR,
|
||||
FileFlags::WRITE,
|
||||
BufferingMode::None,
|
||||
)));
|
||||
|
||||
stdin = stdin_;
|
||||
stdout = stdout_;
|
||||
stderr = stderr_;
|
||||
|
||||
let mut open_files = OPEN_FILES.lock();
|
||||
open_files.insert(NonNull::from(stdin_));
|
||||
open_files.insert(NonNull::from(stdout_));
|
||||
open_files.insert(NonNull::from(stderr_));
|
||||
}
|
||||
|
||||
pub unsafe fn cleanup() {
|
||||
let mut open_files = OPEN_FILES.lock();
|
||||
while let Some(file) = open_files.pop_first() {
|
||||
FILE::close(file).ok();
|
||||
}
|
||||
}
|
105
userspace/lib/ygglibc/src/io/mod.rs
Normal file
105
userspace/lib/ygglibc/src/io/mod.rs
Normal file
@ -0,0 +1,105 @@
|
||||
use core::{ffi::c_int, mem::MaybeUninit};
|
||||
|
||||
use yggdrasil_rt::{io::{PipeOptions, RawFd, SeekFrom}, sys as syscall};
|
||||
|
||||
use crate::{
|
||||
error::{EResult, TryFromExt},
|
||||
headers::{
|
||||
errno,
|
||||
stdio::{SEEK_CUR, SEEK_END, SEEK_SET},
|
||||
sys_types::off_t,
|
||||
},
|
||||
};
|
||||
|
||||
pub mod raw;
|
||||
pub mod managed;
|
||||
pub mod buffer;
|
||||
|
||||
pub trait Write {
|
||||
fn write(&mut self, data: &[u8]) -> EResult<usize>;
|
||||
fn flush(&mut self) -> EResult<()>;
|
||||
|
||||
fn write_chunked(&mut self, data: &[u8]) -> (usize, EResult<()>) {
|
||||
let mut pos = 0;
|
||||
let len = data.len();
|
||||
|
||||
while pos < len {
|
||||
match self.write(data) {
|
||||
EResult::Ok(0) => todo!(),
|
||||
EResult::Ok(n) => pos += n,
|
||||
EResult::Err(err) if err == errno::EINTR => todo!(),
|
||||
EResult::Err(err) => return (pos, EResult::Err(err)),
|
||||
}
|
||||
}
|
||||
|
||||
(pos, EResult::Ok(()))
|
||||
}
|
||||
|
||||
fn write_all(&mut self, data: &[u8]) -> EResult<()> {
|
||||
match self.write(data) {
|
||||
EResult::Ok(n) if n == data.len() => EResult::Ok(()),
|
||||
EResult::Ok(_) => todo!(),
|
||||
EResult::Err(err) => EResult::Err(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Read {
|
||||
fn read(&mut self, data: &mut [u8]) -> EResult<usize>;
|
||||
}
|
||||
|
||||
pub trait Seek {
|
||||
fn seek(&mut self, pos: SeekFrom) -> EResult<u64>;
|
||||
}
|
||||
|
||||
pub trait FromRawFd {
|
||||
unsafe fn from_raw_fd(fd: RawFd) -> Self;
|
||||
}
|
||||
|
||||
pub trait IntoRawFd {
|
||||
fn into_raw_fd(self) -> RawFd;
|
||||
}
|
||||
|
||||
pub trait AsRawFd {
|
||||
fn as_raw_fd(&self) -> RawFd;
|
||||
}
|
||||
|
||||
impl TryFromExt<c_int> for RawFd {
|
||||
fn e_try_from(value: c_int) -> EResult<Self> {
|
||||
match value {
|
||||
0.. => EResult::Ok(unsafe { Self::from_raw(value as _) }),
|
||||
_ => EResult::Err(errno::EBADF),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFromExt<(off_t, c_int)> for SeekFrom {
|
||||
fn e_try_from((offset, whence): (off_t, c_int)) -> EResult<Self> {
|
||||
match whence {
|
||||
SEEK_SET => {
|
||||
if offset < 0 {
|
||||
return EResult::Err(errno::EINVAL);
|
||||
}
|
||||
EResult::Ok(Self::Start(offset as _))
|
||||
}
|
||||
SEEK_CUR => EResult::Ok(Self::Current(offset)),
|
||||
SEEK_END => EResult::Ok(Self::End(offset)),
|
||||
_ => EResult::Err(errno::EINVAL),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_pipe(options: PipeOptions) -> EResult<(RawFd, RawFd)> {
|
||||
let mut fds = MaybeUninit::uninit_array();
|
||||
unsafe { syscall::create_pipe(&mut fds, options) }?;
|
||||
let fds = unsafe { MaybeUninit::array_assume_init(fds) };
|
||||
EResult::Ok((fds[0], fds[1]))
|
||||
}
|
||||
|
||||
pub unsafe fn init() {
|
||||
managed::init();
|
||||
}
|
||||
|
||||
pub unsafe fn cleanup() {
|
||||
managed::cleanup();
|
||||
}
|
87
userspace/lib/ygglibc/src/io/raw.rs
Normal file
87
userspace/lib/ygglibc/src/io/raw.rs
Normal file
@ -0,0 +1,87 @@
|
||||
use core::ffi::c_int;
|
||||
|
||||
use yggdrasil_rt::{
|
||||
io::{FileMode, OpenOptions, RawFd, SeekFrom},
|
||||
path::Path,
|
||||
sys as syscall,
|
||||
};
|
||||
|
||||
use crate::error::{EResult, TryFromExt};
|
||||
|
||||
use super::{AsRawFd, FromRawFd, IntoRawFd, Read, Seek, Write};
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct RawFile(RawFd);
|
||||
|
||||
impl RawFile {
|
||||
pub fn open_at<P: AsRef<Path>>(
|
||||
at: Option<RawFd>,
|
||||
pathname: P,
|
||||
opts: OpenOptions,
|
||||
mode: FileMode,
|
||||
) -> EResult<Self> {
|
||||
let fd = unsafe { syscall::open(at, pathname.as_ref().as_str(), opts, mode) }?;
|
||||
EResult::Ok(Self(fd))
|
||||
}
|
||||
|
||||
pub unsafe fn close(&mut self) -> EResult<()> {
|
||||
unsafe { syscall::close(self.0) }?;
|
||||
EResult::Ok(())
|
||||
}
|
||||
|
||||
pub fn duplicate(&self, new: Option<RawFd>) -> EResult<Self> {
|
||||
let fd = unsafe { syscall::clone_fd(self.0, new) }?;
|
||||
EResult::Ok(Self(fd))
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for RawFile {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl FromRawFd for RawFile {
|
||||
unsafe fn from_raw_fd(fd: RawFd) -> Self {
|
||||
Self(fd)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoRawFd for RawFile {
|
||||
fn into_raw_fd(self) -> RawFd {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for RawFile {
|
||||
fn write(&mut self, data: &[u8]) -> EResult<usize> {
|
||||
let count = unsafe { syscall::write(self.0, data) }?;
|
||||
EResult::Ok(count)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> EResult<()> {
|
||||
EResult::Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for RawFile {
|
||||
fn read(&mut self, data: &mut [u8]) -> EResult<usize> {
|
||||
let count = unsafe { syscall::read(self.0, data) }?;
|
||||
EResult::Ok(count)
|
||||
}
|
||||
}
|
||||
|
||||
impl Seek for RawFile {
|
||||
fn seek(&mut self, seek: SeekFrom) -> EResult<u64> {
|
||||
let mut pos = 0;
|
||||
unsafe { syscall::seek(self.0, seek, &mut pos) }?;
|
||||
EResult::Ok(pos)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFromExt<c_int> for RawFile {
|
||||
fn e_try_from(value: c_int) -> EResult<Self> {
|
||||
let fd = RawFd::e_try_from(value)?;
|
||||
EResult::Ok(unsafe { Self::from_raw_fd(fd) })
|
||||
}
|
||||
}
|
62
userspace/lib/ygglibc/src/lib.rs
Normal file
62
userspace/lib/ygglibc/src/lib.rs
Normal file
@ -0,0 +1,62 @@
|
||||
#![feature(
|
||||
try_trait_v2,
|
||||
decl_macro,
|
||||
maybe_uninit_uninit_array,
|
||||
maybe_uninit_array_assume_init,
|
||||
c_variadic,
|
||||
arbitrary_self_types_pointers,
|
||||
maybe_uninit_slice,
|
||||
slice_internals
|
||||
)]
|
||||
#![allow(internal_features)]
|
||||
#![no_std]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
use core::{
|
||||
ffi::{c_char, c_int, c_void},
|
||||
panic::PanicInfo,
|
||||
ptr::null,
|
||||
};
|
||||
|
||||
use alloc::boxed::Box;
|
||||
use yggdrasil_rt::{
|
||||
debug_trace,
|
||||
process::{ExitCode, ProcessId, Signal},
|
||||
};
|
||||
|
||||
mod allocator;
|
||||
mod args;
|
||||
mod error;
|
||||
mod io;
|
||||
mod process;
|
||||
mod util;
|
||||
mod sync;
|
||||
mod types;
|
||||
|
||||
pub mod headers;
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn _start(arg: usize) {
|
||||
extern "C" {
|
||||
fn main(argc: c_int, argv: *const *const c_char, envp: *const *const c_char) -> c_int;
|
||||
}
|
||||
|
||||
io::init();
|
||||
|
||||
let status = main(0, null(), null());
|
||||
|
||||
process::c_exit(status)
|
||||
}
|
||||
|
||||
#[panic_handler]
|
||||
fn panic_handler(pi: &PanicInfo) -> ! {
|
||||
debug_trace!("PANIC {:?}", &pi);
|
||||
|
||||
// Kill myself
|
||||
unsafe {
|
||||
let pid = yggdrasil_rt::sys::get_pid();
|
||||
yggdrasil_rt::sys::send_signal(pid, Signal::Aborted).ok();
|
||||
unreachable!()
|
||||
}
|
||||
}
|
36
userspace/lib/ygglibc/src/process.rs
Normal file
36
userspace/lib/ygglibc/src/process.rs
Normal file
@ -0,0 +1,36 @@
|
||||
use core::{ffi::c_int, time::Duration};
|
||||
|
||||
use yggdrasil_rt::{process::ExitCode, sys as syscall};
|
||||
|
||||
use crate::{error::EResult, headers::sys_types::pid_t, io};
|
||||
|
||||
pub fn getpid() -> pid_t {
|
||||
let pid = unsafe { syscall::get_pid() };
|
||||
pid.into_raw().try_into().unwrap()
|
||||
}
|
||||
|
||||
pub fn getpgrp() -> pid_t {
|
||||
let pgid = unsafe { syscall::get_process_group_id() };
|
||||
pgid.into_raw().try_into().unwrap()
|
||||
}
|
||||
|
||||
pub fn sleep(duration: Duration) -> EResult<()> {
|
||||
unsafe { syscall::nanosleep(&duration) }?;
|
||||
EResult::Ok(())
|
||||
}
|
||||
|
||||
pub fn c_exit_immediately(status: c_int) -> ! {
|
||||
let code = ExitCode::Exited(status);
|
||||
unsafe { syscall::exit_process(code) };
|
||||
}
|
||||
|
||||
pub fn c_exit(status: c_int) -> ! {
|
||||
unsafe {
|
||||
pre_exit();
|
||||
c_exit_immediately(status)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn pre_exit() {
|
||||
io::cleanup();
|
||||
}
|
103
userspace/lib/ygglibc/src/sync.rs
Normal file
103
userspace/lib/ygglibc/src/sync.rs
Normal file
@ -0,0 +1,103 @@
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
||||
pub 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() };
|
||||
}
|
||||
}
|
||||
|
||||
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 }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T> Sync for Mutex<T> {}
|
||||
|
||||
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() };
|
||||
}
|
||||
}
|
5
userspace/lib/ygglibc/src/types.rs
Normal file
5
userspace/lib/ygglibc/src/types.rs
Normal file
@ -0,0 +1,5 @@
|
||||
#![allow(non_camel_case_types)]
|
||||
|
||||
use core::ffi::c_int;
|
||||
|
||||
pub type wchar_t = c_int;
|
50
userspace/lib/ygglibc/src/util.rs
Normal file
50
userspace/lib/ygglibc/src/util.rs
Normal file
@ -0,0 +1,50 @@
|
||||
use core::ffi::{c_char, CStr};
|
||||
|
||||
pub trait PointerExt {
|
||||
unsafe fn ensure(self: *const Self) -> &'static Self;
|
||||
unsafe fn ensure_mut(self: *mut Self) -> &'static mut Self;
|
||||
unsafe fn ensure_slice(self: *const Self, len: usize) -> &'static [Self] where Self: Sized;
|
||||
unsafe fn ensure_slice_mut(self: *mut Self, len: usize) -> &'static mut [Self] where Self: Sized;
|
||||
}
|
||||
|
||||
pub trait PointerStrExt {
|
||||
unsafe fn ensure_str(self: *const Self) -> &'static str;
|
||||
unsafe fn ensure_cstr(self: *const Self) -> &'static CStr;
|
||||
}
|
||||
|
||||
impl<T> PointerExt for T {
|
||||
unsafe fn ensure(self: *const Self) -> &'static Self {
|
||||
unsafe { self.as_ref().expect("ensure() failed: NULL pointer") }
|
||||
}
|
||||
|
||||
unsafe fn ensure_mut(self: *mut Self) -> &'static mut Self {
|
||||
unsafe { self.as_mut().expect("ensure_mut() failed: NULL pointer") }
|
||||
}
|
||||
|
||||
unsafe fn ensure_slice(self: *const Self, len: usize) -> &'static [Self] where Self: Sized {
|
||||
if self.is_null() {
|
||||
panic!("ensure_slice() failed: NULL pointer");
|
||||
}
|
||||
unsafe { core::slice::from_raw_parts(self, len) }
|
||||
}
|
||||
|
||||
unsafe fn ensure_slice_mut(self: *mut Self, len: usize) -> &'static mut [Self] where Self: Sized {
|
||||
if self.is_null() {
|
||||
panic!("ensure_slice_mut() failed: NULL pointer");
|
||||
}
|
||||
unsafe { core::slice::from_raw_parts_mut(self, len) }
|
||||
}
|
||||
}
|
||||
|
||||
impl PointerStrExt for c_char {
|
||||
unsafe fn ensure_str(self: *const Self) -> &'static str {
|
||||
self.ensure_cstr().to_str().expect("ensure_str() failed: not a valid string")
|
||||
}
|
||||
|
||||
unsafe fn ensure_cstr(self: *const Self) -> &'static CStr {
|
||||
if self.is_null() {
|
||||
panic!("ensure_str() failed: NULL pointer");
|
||||
}
|
||||
CStr::from_ptr(self)
|
||||
}
|
||||
}
|
@ -37,6 +37,7 @@ pub struct ModuleInfo {
|
||||
pub enum CargoBuilder<'e> {
|
||||
Host(bool),
|
||||
Userspace(&'e BuildEnv),
|
||||
Ygglibc(&'e BuildEnv),
|
||||
Kernel(&'e BuildEnv),
|
||||
Module(&'e BuildEnv, &'e ModuleInfo),
|
||||
}
|
||||
@ -78,6 +79,22 @@ impl<'e> CargoBuilder<'e> {
|
||||
command.arg("--release");
|
||||
}
|
||||
}
|
||||
Self::Ygglibc(env) => {
|
||||
let target = format!("{:?}-unknown-none", env.arch);
|
||||
|
||||
command
|
||||
.arg(arg)
|
||||
.arg("-Zbuild-std=core,alloc,compiler_builtins")
|
||||
.arg(&format!("--target={target}"));
|
||||
|
||||
if env.verbose {
|
||||
command.arg("-v");
|
||||
}
|
||||
|
||||
if env.profile == Profile::Release {
|
||||
command.arg("--release");
|
||||
}
|
||||
}
|
||||
Self::Host(verbose) => {
|
||||
command.arg("+nightly").arg(arg);
|
||||
|
||||
|
@ -2,11 +2,18 @@ use std::{
|
||||
fs::{self, File},
|
||||
io::BufWriter,
|
||||
path::{Path, PathBuf},
|
||||
process::Command,
|
||||
};
|
||||
|
||||
use walkdir::WalkDir;
|
||||
|
||||
use crate::{build::cargo::CargoBuilder, check::AllOk, env::BuildEnv, error::Error, util};
|
||||
use crate::{
|
||||
build::cargo::CargoBuilder,
|
||||
check::AllOk,
|
||||
env::{Arch, BuildEnv},
|
||||
error::Error,
|
||||
util,
|
||||
};
|
||||
|
||||
use super::InitrdGenerated;
|
||||
|
||||
@ -55,12 +62,54 @@ const PROGRAMS: &[(&str, &str)] = &[
|
||||
// crypt
|
||||
("crypt", "bin/crypt"),
|
||||
("dyn-loader", "libexec/dyn-loader"),
|
||||
// TODO: proper process for C program builds
|
||||
("c-test", "c-test"),
|
||||
];
|
||||
|
||||
fn build_test_c_program(env: &BuildEnv) -> Result<(), Error> {
|
||||
log::info!("Building a test C program");
|
||||
let mut command = Command::new("clang");
|
||||
match env.arch {
|
||||
Arch::aarch64 => todo!(),
|
||||
Arch::i686 => todo!(),
|
||||
Arch::x86_64 => {
|
||||
command.args(["-target", "x86_64-unknown-none"]);
|
||||
}
|
||||
}
|
||||
|
||||
let ygglibc_target = format!("{:?}-unknown-none", env.arch);
|
||||
let ygglibc_target_dir = env
|
||||
.workspace_root
|
||||
.join("userspace/lib/ygglibc/target")
|
||||
.join(ygglibc_target)
|
||||
.join(env.profile.dir());
|
||||
let target_dir = &env.userspace_output_dir;
|
||||
|
||||
command
|
||||
.args(["-nostdlib", "-ffreestanding", "-static", "-Og", "-ggdb"])
|
||||
.arg("-I")
|
||||
.arg("userspace/lib/ygglibc/include")
|
||||
.arg("-I")
|
||||
.arg(ygglibc_target_dir.join("include"))
|
||||
.arg("-o")
|
||||
.arg(target_dir.join("c-test"))
|
||||
.arg(ygglibc_target_dir.join("libygglibc.a"))
|
||||
.arg(env.workspace_root.join("test.c"))
|
||||
.arg(ygglibc_target_dir.join("libygglibc.a"));
|
||||
|
||||
if !command.status()?.success() {
|
||||
return Err(Error::ExternalCommandFailed);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn build_userspace(env: &BuildEnv, _: AllOk) -> Result<(), Error> {
|
||||
log::info!("Building userspace");
|
||||
CargoBuilder::Userspace(env).build(env.workspace_root.join("userspace"))?;
|
||||
CargoBuilder::Userspace(env).build(env.workspace_root.join("userspace/dynload-program"))?;
|
||||
CargoBuilder::Ygglibc(env).build(env.workspace_root.join("userspace/lib/ygglibc"))?;
|
||||
build_test_c_program(env)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user