use crate::{set_errno, Errno};
use alloc::borrow::ToOwned;
use alloc::vec::Vec;
use core::cell::SyncUnsafeCell;
use core::ptr::null_mut;
use core::slice;
use libc::{c_char, c_int};
use rustix::ffi::{CStr, CString};
#[no_mangle]
static mut environ: *mut *mut c_char = null_mut();
pub(crate) unsafe fn load_environ() -> *mut *mut c_char {
environ
}
#[cfg(not(target_os = "wasi"))]
pub(super) unsafe fn init_from_envp(envp: *mut *mut c_char) {
environ = envp;
}
unsafe fn sync_environ(environ_vecs: &mut EnvironVecs) {
let mut ptr = load_environ();
if environ_vecs.ptrs.is_empty() || environ_vecs.ptrs.as_ptr() != ptr {
let mut vecs = EnvironVecs::new();
loop {
let env = *ptr;
if env.is_null() {
break;
}
let owned = CStr::from_ptr(env).to_owned();
vecs.ptrs.push(owned.as_ptr().cast_mut());
vecs.allocs.push(owned);
ptr = ptr.add(1);
}
vecs.ptrs.push(null_mut());
*environ_vecs = vecs;
environ = environ_vecs.ptrs.as_mut_ptr();
}
}
#[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 environ_vecs = &mut *ENVIRON_VECS.get();
sync_environ(environ_vecs);
let start = load_environ();
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] = owned.as_ptr().cast_mut();
environ_vecs.allocs[index] = owned;
}
return 0;
}
ptr = ptr.add(1);
}
environ_vecs.ptrs.pop();
environ_vecs.ptrs.push(owned.as_ptr().cast_mut());
environ_vecs.ptrs.push(null_mut());
environ_vecs.allocs.push(owned);
environ = environ_vecs.ptrs.as_mut_ptr();
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 environ_vecs = &mut *ENVIRON_VECS.get();
sync_environ(environ_vecs);
let start = load_environ();
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());
environ_vecs.allocs.swap_remove(index);
environ = environ_vecs.ptrs.as_mut_ptr();
} else {
ptr = ptr.add(1);
}
}
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 environ_vecs = &mut *ENVIRON_VECS.get();
sync_environ(environ_vecs);
let start = load_environ();
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;
return 0;
}
ptr = ptr.add(1);
}
environ_vecs.ptrs.pop();
environ_vecs.ptrs.push(key_value);
environ_vecs.ptrs.push(null_mut());
environ = environ_vecs.ptrs.as_mut_ptr();
0
}
#[no_mangle]
unsafe extern "C" fn clearenv() -> c_int {
libc!(libc::clearenv());
let environ_vecs = &mut *ENVIRON_VECS.get();
environ_vecs.ptrs.clear();
environ_vecs.ptrs.push(null_mut());
environ_vecs.allocs.clear();
0
}
struct EnvironVecs {
ptrs: Vec<*mut c_char>,
allocs: Vec<CString>,
}
impl EnvironVecs {
const fn new() -> Self {
Self {
ptrs: Vec::new(),
allocs: Vec::new(),
}
}
}
unsafe impl Sync for EnvironVecs {}
static ENVIRON_VECS: SyncUnsafeCell<EnvironVecs> = SyncUnsafeCell::new(EnvironVecs::new());