use arceos_posix_api::{environ, environ_iter, RX_ENVIRON};
use core::ffi::{c_char, c_int, c_void};
use crate::malloc::{free, malloc};
use crate::string::strlen;
unsafe fn find_env(search: *const c_char) -> Option<(usize, *mut c_char)> {
for (i, mut item) in environ_iter().enumerate() {
let mut search = search;
loop {
let end_of_query = *search == 0 || *search == b'=' as c_char;
assert_ne!(*item, 0, "environ has an item without value");
if *item == b'=' as c_char || end_of_query {
if *item == b'=' as c_char && end_of_query {
return Some((i, item.add(1)));
} else {
break;
}
}
if *item != *search {
break;
}
item = item.add(1);
search = search.add(1);
}
}
None
}
unsafe fn put_new_env(insert: *mut c_char) {
if environ == RX_ENVIRON.as_mut_ptr() {
*RX_ENVIRON.last_mut().unwrap() = insert;
RX_ENVIRON.push(core::ptr::null_mut());
environ = RX_ENVIRON.as_mut_ptr();
} else {
RX_ENVIRON.clear();
RX_ENVIRON.extend(environ_iter());
RX_ENVIRON.push(insert);
RX_ENVIRON.push(core::ptr::null_mut());
environ = RX_ENVIRON.as_mut_ptr();
}
}
unsafe fn copy_kv(
existing: *mut c_char,
key: *const c_char,
value: *const c_char,
key_len: usize,
value_len: usize,
) {
core::ptr::copy_nonoverlapping(key, existing, key_len);
core::ptr::write(existing.add(key_len), b'=' as c_char);
core::ptr::copy_nonoverlapping(value, existing.add(key_len + 1), value_len);
core::ptr::write(existing.add(key_len + 1 + value_len), 0);
}
#[no_mangle]
pub unsafe extern "C" fn setenv(
key: *const c_char,
value: *const c_char,
overwrite: c_int,
) -> c_int {
let key_len = strlen(key);
let value_len = strlen(value);
if let Some((i, existing)) = find_env(key) {
if overwrite == 0 {
return 0;
}
let existing_len = strlen(existing);
if existing_len >= value_len {
core::ptr::copy_nonoverlapping(value, existing, value_len);
core::ptr::write(existing.add(value_len), 0);
} else {
let ptr = malloc(key_len + 1 + value_len + 1) as *mut c_char;
copy_kv(ptr, key, value, key_len, value_len);
environ.add(i).write(ptr);
}
} else {
let ptr = malloc(key_len + 1 + value_len + 1) as *mut c_char;
copy_kv(ptr, key, value, key_len, value_len);
put_new_env(ptr);
}
0
}
#[no_mangle]
pub unsafe extern "C" fn unsetenv(key: *const c_char) -> c_int {
if let Some((i, _)) = find_env(key) {
if environ == RX_ENVIRON.as_mut_ptr() {
let rm = RX_ENVIRON.remove(i);
free(rm as *mut c_void);
environ = RX_ENVIRON.as_mut_ptr();
} else {
let len = RX_ENVIRON.len();
for _ in 0..len {
let rm = RX_ENVIRON.pop().unwrap();
free(rm as *mut c_void);
}
RX_ENVIRON.extend(
environ_iter()
.enumerate()
.filter(|&(j, _)| j != i)
.map(|(_, v)| v),
);
RX_ENVIRON.push(core::ptr::null_mut());
environ = RX_ENVIRON.as_mut_ptr();
}
}
0
}
#[no_mangle]
pub unsafe extern "C" fn getenv(name: *const c_char) -> *mut c_char {
find_env(name)
.map(|val| val.1)
.unwrap_or(core::ptr::null_mut())
}