80 lines
1.8 KiB
Rust
80 lines
1.8 KiB
Rust
use core::marker::PhantomData;
|
|
|
|
use yggdrasil_rt::path::Path;
|
|
|
|
use crate::{
|
|
error::{EResult, OptionExt},
|
|
path::{PathBuf, PathExt},
|
|
ENVS,
|
|
};
|
|
|
|
pub trait Nullable {
|
|
fn is_null(&self) -> bool;
|
|
}
|
|
|
|
pub struct NullTerminated<'a, T: Nullable> {
|
|
data: *const T,
|
|
_pd: PhantomData<&'a ()>,
|
|
}
|
|
|
|
impl<T> Nullable for *const T {
|
|
fn is_null(&self) -> bool {
|
|
<*const T>::is_null(*self)
|
|
}
|
|
}
|
|
|
|
impl<T> Nullable for *mut T {
|
|
fn is_null(&self) -> bool {
|
|
<*mut T>::is_null(*self)
|
|
}
|
|
}
|
|
|
|
impl<'a, T: Nullable> NullTerminated<'a, T> {
|
|
pub unsafe fn try_from_ptr(ptr: *const T) -> Option<Self> {
|
|
(!ptr.is_null()).then(|| Self {
|
|
data: ptr,
|
|
_pd: PhantomData,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl<'a, T: Nullable + 'a> Iterator for NullTerminated<'a, T> {
|
|
type Item = &'a T;
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
let value = unsafe { &*self.data };
|
|
if value.is_null() {
|
|
return None;
|
|
}
|
|
self.data = unsafe { self.data.add(1) };
|
|
Some(value)
|
|
}
|
|
}
|
|
|
|
pub fn envs() -> impl Iterator<Item = (&'static str, &'static str)> {
|
|
// SAFETY C_ENVS is only mutable during init
|
|
let it = unsafe { ENVS.iter() };
|
|
it.filter_map(|v| v.to_str().ok().and_then(|s| s.split_once('=')))
|
|
}
|
|
|
|
pub fn getenv(name: &str) -> Option<&'static str> {
|
|
envs().find_map(|(k, v)| (k == name).then_some(v))
|
|
}
|
|
|
|
pub fn resolve_binary<P: AsRef<Path>>(name: P) -> EResult<PathBuf> {
|
|
let name = name.as_ref();
|
|
if name.is_absolute() {
|
|
return EResult::Ok(PathBuf::from(name));
|
|
}
|
|
let env_path = getenv("PATH").e_ok_or(yggdrasil_rt::Error::DoesNotExist)?;
|
|
|
|
for el in env_path.split(':') {
|
|
let path = PathBuf::from_str(el).join(name);
|
|
if path.exists() {
|
|
return EResult::Ok(path);
|
|
}
|
|
}
|
|
|
|
EResult::Err(yggdrasil_rt::Error::DoesNotExist)
|
|
}
|