mirror of
https://github.com/Noratrieb/libuwuc.git
synced 2026-01-14 11:45:05 +01:00
getenv works
This commit is contained in:
parent
0c69d7db10
commit
9464ea4829
7 changed files with 206 additions and 33 deletions
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
use core::ffi::c_char;
|
||||
|
||||
use libuwuc::println;
|
||||
use libuwuc::{println, utils::SharedThinCstr};
|
||||
|
||||
extern crate libuwuc;
|
||||
|
||||
|
|
@ -23,5 +23,15 @@ fn handler(arg: &core::panic::PanicInfo) -> ! {
|
|||
#[no_mangle]
|
||||
extern "C" fn main(_argc: i32, _argv: *const *const c_char) -> i32 {
|
||||
println!("Hello, world!");
|
||||
let pwd = libuwuc::env::getenv(SharedThinCstr::from_array(b"PWD\0"));
|
||||
println!("PWD={pwd:?}");
|
||||
0
|
||||
}
|
||||
|
||||
// libcore seems to require this symbol, even though it's unused.
|
||||
#[no_mangle]
|
||||
fn rust_eh_personality() {
|
||||
unsafe {
|
||||
libuwuc::trap!();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
132
src/env.rs
132
src/env.rs
|
|
@ -1,35 +1,38 @@
|
|||
use core::ffi::CStr;
|
||||
use core::{
|
||||
ffi::{c_char, CStr},
|
||||
iter,
|
||||
ptr::NonNull,
|
||||
};
|
||||
|
||||
use crate::{println, utils::SharedThinCstr};
|
||||
|
||||
mod global {
|
||||
use core::cell::UnsafeCell;
|
||||
use core::{cell::UnsafeCell, ptr::NonNull};
|
||||
|
||||
use crate::utils::{SharedThinCstr, SyncUnsafeCell};
|
||||
|
||||
use super::EnvP;
|
||||
|
||||
static ENVP: SyncUnsafeCell<EnvP> =
|
||||
SyncUnsafeCell(UnsafeCell::new(EnvP(core::ptr::null_mut())));
|
||||
static ENVP: SyncUnsafeCell<EnvP> = SyncUnsafeCell(UnsafeCell::new(EnvP(None)));
|
||||
|
||||
pub(super) unsafe fn init(envp: *mut SharedThinCstr) {
|
||||
assert!((*ENVP.0.get()).0.is_null());
|
||||
*ENVP.0.get() = EnvP(envp);
|
||||
pub(super) unsafe fn init(envp: *mut Option<SharedThinCstr>) {
|
||||
assert!((*ENVP.0.get()).0.is_none());
|
||||
*ENVP.0.get() = EnvP(Some(NonNull::new(envp).unwrap()));
|
||||
}
|
||||
|
||||
pub(super) fn get() -> EnvP {
|
||||
let ptr = unsafe { *ENVP.0.get() };
|
||||
assert!(!ptr.0.is_null());
|
||||
assert!(ptr.0.is_some());
|
||||
ptr
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn init(envp: *mut SharedThinCstr) {
|
||||
pub(crate) unsafe fn init(envp: *mut Option<SharedThinCstr>) {
|
||||
global::init(envp);
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct EnvP(*mut SharedThinCstr);
|
||||
struct EnvP(Option<NonNull<Option<SharedThinCstr>>>);
|
||||
|
||||
unsafe impl Sync for EnvP {}
|
||||
|
||||
|
|
@ -38,13 +41,12 @@ impl Iterator for EnvP {
|
|||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
unsafe {
|
||||
let value: SharedThinCstr = self.0.read();
|
||||
if value.0.is_null() {
|
||||
None
|
||||
} else {
|
||||
self.0 = self.0.add(1);
|
||||
Some(value)
|
||||
}
|
||||
let value: Option<SharedThinCstr> = self.0.unwrap().as_ptr().read();
|
||||
|
||||
value.map(|value| {
|
||||
self.0 = Some(NonNull::new_unchecked(self.0.unwrap().as_ptr().add(1)));
|
||||
value
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -57,3 +59,99 @@ pub(crate) fn debug_env() {
|
|||
});
|
||||
println!("end vars");
|
||||
}
|
||||
|
||||
pub fn getenv(name: SharedThinCstr) -> Option<SharedThinCstr> {
|
||||
getenv_inner(global::get(), name)
|
||||
}
|
||||
|
||||
fn getenv_inner(mut envp: EnvP, name: SharedThinCstr) -> Option<SharedThinCstr> {
|
||||
let mut eq_idx = 0;
|
||||
envp.find(|env| {
|
||||
// Find ENV
|
||||
// EN=x
|
||||
// ENV=x <- this one
|
||||
// ENVNO=x
|
||||
|
||||
let mut name_iter = name.into_iter();
|
||||
let mut env_iter = env.into_iter();
|
||||
eq_idx = 0;
|
||||
loop {
|
||||
let name = name_iter.next().map(|c| c as u8);
|
||||
let env = env_iter.next().map(|c| c as u8);
|
||||
if let (None, Some(b'=')) = (name, env) {
|
||||
return true;
|
||||
}
|
||||
if name.is_none() || env == Some(b'=') {
|
||||
return false;
|
||||
}
|
||||
eq_idx += 1;
|
||||
}
|
||||
})
|
||||
.map(|elem| {
|
||||
let value_idx = eq_idx + 1;
|
||||
unsafe { elem.add(value_idx) }
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use core::ptr::NonNull;
|
||||
use std::string::ToString;
|
||||
use std::{ffi::CString, vec::Vec};
|
||||
|
||||
use crate::utils::SharedThinCstr;
|
||||
|
||||
use super::EnvP;
|
||||
|
||||
fn cstr(s: &str) -> SharedThinCstr {
|
||||
assert_eq!(s.as_bytes()[s.len() - 1], 0);
|
||||
unsafe { SharedThinCstr::from_ptr(NonNull::new(s.as_ptr() as _).unwrap()) }
|
||||
}
|
||||
|
||||
fn with_envp(env: &[&str], f: impl FnOnce(EnvP)) {
|
||||
let cstrs = env
|
||||
.iter()
|
||||
.map(|s| CString::new(s.to_string()).unwrap())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut envs: Vec<Option<SharedThinCstr>> = cstrs
|
||||
.iter()
|
||||
.map(|cstr| unsafe {
|
||||
Some(SharedThinCstr::from_ptr(
|
||||
NonNull::new(cstr.as_ptr() as _).unwrap(),
|
||||
))
|
||||
})
|
||||
.collect();
|
||||
envs.push(None);
|
||||
|
||||
let envp = EnvP(Some(NonNull::new(envs.as_ptr() as _).unwrap()));
|
||||
f(envp)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn getenv_exact_first() {
|
||||
with_envp(&["UWU=a"], |envp| {
|
||||
assert_eq!(
|
||||
super::getenv_inner(envp, cstr("UWU\0")).unwrap(),
|
||||
cstr("a\0")
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn getenv_previous_mismatches() {
|
||||
with_envp(&["UW=a", "UWUU=b", "UWU=c"], |envp| {
|
||||
assert_eq!(
|
||||
super::getenv_inner(envp, cstr("UWU\0")).unwrap(),
|
||||
cstr("c\0")
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn getenv_name_long() {
|
||||
with_envp(&["U=w"], |envp| {
|
||||
assert_eq!(super::getenv_inner(envp, cstr("LONG_NAME\0")), None);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,16 @@
|
|||
#![no_std]
|
||||
#![warn(unreachable_pub)]
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate std;
|
||||
|
||||
mod basic_mem;
|
||||
mod env;
|
||||
pub mod env;
|
||||
pub mod io;
|
||||
pub mod start;
|
||||
mod stubs;
|
||||
mod sys;
|
||||
mod utils;
|
||||
pub mod utils;
|
||||
|
||||
pub mod syscall {
|
||||
pub use crate::sys::syscall::*;
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use crate::utils::SharedThinCstr;
|
|||
/// The entrypoint of the program.
|
||||
/// This is called by a bit of assembly handling architecture-specific _start.
|
||||
pub(crate) unsafe extern "C" fn start(argc: u64, argv: *const *const c_char, rsp: u64) -> ! {
|
||||
let envp = (8 + 8 * argc + rsp + 8) as *mut SharedThinCstr;
|
||||
let envp = (8 + 8 * argc + rsp + 8) as *mut Option<SharedThinCstr>;
|
||||
|
||||
crate::env::init(envp);
|
||||
|
||||
|
|
@ -13,7 +13,7 @@ pub(crate) unsafe extern "C" fn start(argc: u64, argv: *const *const c_char, rsp
|
|||
fn main(argc: c_int, argv: *const *const c_char) -> c_int;
|
||||
}
|
||||
|
||||
crate::env::debug_env();
|
||||
// crate::env::debug_env();
|
||||
|
||||
let result = main(argc as i32, argv);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +0,0 @@
|
|||
// libcore seems to require this symbol, even though it's unused.
|
||||
#[no_mangle]
|
||||
fn rust_eh_personality() {
|
||||
unsafe {
|
||||
crate::sys::helpers::trap!();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
#[macro_export]
|
||||
macro_rules! trap {
|
||||
() => {
|
||||
::core::arch::asm!("ud2");
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
use core::{
|
||||
cell::UnsafeCell,
|
||||
ffi::{c_char, CStr},
|
||||
fmt::Debug,
|
||||
ptr::NonNull,
|
||||
};
|
||||
|
||||
#[repr(transparent)]
|
||||
|
|
@ -9,18 +11,84 @@ pub(crate) struct SyncUnsafeCell<T>(pub(crate) UnsafeCell<T>);
|
|||
|
||||
unsafe impl<T: Sync> Sync for SyncUnsafeCell<T> {}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
#[repr(transparent)]
|
||||
pub(crate) struct SharedThinCstr(pub(crate) *const c_char);
|
||||
pub struct SharedThinCstr(pub NonNull<c_char>);
|
||||
|
||||
impl SharedThinCstr {
|
||||
pub unsafe fn from_ptr(ptr: NonNull<c_char>) -> Self {
|
||||
Self(ptr)
|
||||
}
|
||||
|
||||
pub fn from_array<const N: usize>(arr: &[u8; N]) -> Self {
|
||||
assert!(arr[N - 1] == 0);
|
||||
unsafe { Self(NonNull::new_unchecked(arr as *const u8 as *mut c_char)) }
|
||||
}
|
||||
|
||||
pub fn as_ptr(self) -> NonNull<c_char> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub unsafe fn add(self, amount: usize) -> Self {
|
||||
Self(NonNull::new_unchecked(self.0.as_ptr().add(amount)))
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoIterator for SharedThinCstr {
|
||||
type Item = c_char;
|
||||
|
||||
type IntoIter = CStrIter;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
CStrIter(self)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CStrIter(SharedThinCstr);
|
||||
|
||||
impl Iterator for CStrIter {
|
||||
type Item = c_char;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
unsafe {
|
||||
let c = self.0 .0.as_ptr().read();
|
||||
if c == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
self.0 = self.0.add(1);
|
||||
Some(c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for SharedThinCstr {}
|
||||
unsafe impl Sync for SharedThinCstr {}
|
||||
|
||||
impl From<SharedThinCstr> for &CStr {
|
||||
fn from(value: SharedThinCstr) -> Self {
|
||||
unsafe { CStr::from_ptr(value.0) }
|
||||
impl Debug for SharedThinCstr {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
let str = <&CStr>::from(*self).to_str();
|
||||
match str {
|
||||
Ok(str) => f.write_str(str),
|
||||
Err(_) => f.write_str("<invalid UTF-8>"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SharedThinCstr> for &CStr {
|
||||
fn from(value: SharedThinCstr) -> Self {
|
||||
unsafe { CStr::from_ptr(value.0.as_ptr()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for SharedThinCstr {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.into_iter().eq(other.into_iter())
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for SharedThinCstr {}
|
||||
|
||||
#[repr(transparent)]
|
||||
pub(crate) struct SyncPtr<T>(pub(crate) *mut T);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue