use crate::{set_errno, Errno};
use alloc::vec;
use alloc::vec::Vec;
use core::ptr::null_mut;
use core::slice;
use core::sync::atomic::{AtomicPtr, Ordering};
use libc::{c_char, c_int};
use rustix::ffi::{CStr, CString};
#[no_mangle]
static environ: AtomicPtr<*mut c_char> = AtomicPtr::new(null_mut());
pub(crate) unsafe fn load_environ() -> *mut *mut c_char {
environ.load(Ordering::Relaxed)
}
#[cfg(not(target_os = "wasi"))]
pub(super) fn init_from_envp(envp: *mut *mut c_char) {
environ.store(envp, Ordering::Relaxed);
}
#[no_mangle]
unsafe extern "C" fn setenv(key: *const c_char, value: *const c_char, overwrite: c_int) -> c_int {
libc!(libc::setenv(key, value, overwrite));
if key.is_null() || value.is_null() {
set_errno(Errno(libc::EINVAL));
return -1;
}
let key = CStr::from_ptr(key);
let key_bytes = key.to_bytes();
let value = CStr::from_ptr(value);
let value_bytes = value.to_bytes();
if key_bytes.is_empty() || key_bytes.contains(&b'=') {
set_errno(Errno(libc::EINVAL));
return -1;
}
let mut owned = Vec::new();
owned.extend_from_slice(key_bytes);
owned.extend_from_slice(b"=");
owned.extend_from_slice(value_bytes);
let owned = CString::new(owned).unwrap();
let leaked = owned.into_raw();
let mut environ_vecs = EnvironVecs::read();
let start = environ_vecs.ptrs.as_mut_ptr();
let mut ptr = start;
loop {
let env = *ptr;
if env.is_null() {
break;
}
let mut c = env;
while *c != (b'=' as c_char) {
c = c.add(1);
}
if key_bytes
== slice::from_raw_parts(env.cast::<u8>(), c.offset_from(env).try_into().unwrap())
{
if overwrite != 0 {
let index = ptr.offset_from(start) as usize;
environ_vecs.ptrs[index] = leaked;
environ_vecs.install();
}
return 0;
}
ptr = ptr.add(1);
}
environ_vecs.ptrs.pop();
environ_vecs.ptrs.push(leaked);
environ_vecs.ptrs.push(null_mut());
environ_vecs.install();
0
}
#[no_mangle]
unsafe extern "C" fn unsetenv(key: *const c_char) -> c_int {
libc!(libc::unsetenv(key));
if key.is_null() {
set_errno(Errno(libc::EINVAL));
return -1;
}
let key = CStr::from_ptr(key);
let key_bytes = key.to_bytes();
if key_bytes.is_empty() || key_bytes.contains(&b'=') {
set_errno(Errno(libc::EINVAL));
return -1;
}
let mut environ_vecs = EnvironVecs::read();
let start = environ_vecs.ptrs.as_mut_ptr();
let mut ptr = start;
loop {
let env = *ptr;
if env.is_null() {
break;
}
let mut c = env;
while *c != (b'=' as c_char) {
c = c.add(1);
}
if key_bytes
== slice::from_raw_parts(env.cast::<u8>(), c.offset_from(env).try_into().unwrap())
{
let index = ptr.offset_from(start) as usize;
environ_vecs.ptrs.pop();
environ_vecs.ptrs.swap_remove(index);
environ_vecs.ptrs.push(null_mut());
} else {
ptr = ptr.add(1);
}
}
environ_vecs.install();
0
}
#[no_mangle]
unsafe extern "C" fn putenv(key_value: *mut c_char) -> c_int {
libc!(libc::putenv(key_value));
let key_value_cstr = CStr::from_ptr(key_value);
let key_value_bytes = key_value_cstr.to_bytes();
let eq = match key_value_bytes.iter().position(|x| *x == b'=') {
Some(pos) => pos,
None => return unsetenv(key_value),
};
let key_bytes = &key_value_bytes[..eq];
let mut environ_vecs = EnvironVecs::read();
let start = environ_vecs.ptrs.as_mut_ptr();
let mut ptr = start;
loop {
let env = *ptr;
if env.is_null() {
break;
}
let mut c = env;
while *c != (b'=' as c_char) {
c = c.add(1);
}
if key_bytes
== slice::from_raw_parts(env.cast::<u8>(), c.offset_from(env).try_into().unwrap())
{
let index = ptr.offset_from(start) as usize;
environ_vecs.ptrs[index] = key_value;
environ_vecs.install();
return 0;
}
ptr = ptr.add(1);
}
environ_vecs.ptrs.pop();
environ_vecs.ptrs.push(key_value);
environ_vecs.ptrs.push(null_mut());
environ_vecs.install();
0
}
#[no_mangle]
unsafe extern "C" fn clearenv() -> c_int {
libc!(libc::clearenv());
EnvironVecs {
ptrs: vec![null_mut()],
}
.install();
0
}
struct EnvironVecs {
ptrs: Vec<*mut c_char>,
}
impl EnvironVecs {
unsafe fn read() -> Self {
let mut ptrs = Vec::new();
let mut ptr = load_environ();
loop {
let env = *ptr;
ptrs.push(env);
if env.is_null() {
break;
}
ptr = ptr.add(1);
}
Self { ptrs }
}
fn install(mut self) {
self.ptrs.shrink_to_fit();
let leaked: &'static mut [*mut c_char] = self.ptrs.leak();
environ.store(leaked.as_mut_ptr(), Ordering::Relaxed);
}
}