bash_builtins/variables/
arrays.rs

1//! Access to array variables.
2
3use std::convert::TryFrom;
4use std::ffi::{c_int, c_void, CStr, CString};
5
6use super::VariableError;
7use crate::ffi::variables as ffi;
8
9/// Change an element of the array contained in the shell variable referenced by
10/// `name`.
11///
12/// `value` is not required to be valid UTF-8, but it can't contain any nul
13/// byte.
14pub fn array_set<T>(name: &str, index: usize, value: T) -> Result<(), VariableError>
15where
16    T: AsRef<[u8]>,
17{
18    let name = CString::new(name).map_err(|_| VariableError::InvalidName)?;
19    let value = CString::new(value.as_ref()).map_err(|_| VariableError::InvalidValue)?;
20
21    let res = unsafe {
22        if ffi::legal_identifier(name.as_ptr()) == 0 {
23            return Err(VariableError::InvalidName);
24        }
25
26        ffi::bind_array_variable(name.as_ptr(), index as _, value.as_ptr(), 0)
27    };
28
29    if res.is_null() {
30        Err(VariableError::InvalidValue)
31    } else {
32        Ok(())
33    }
34}
35
36/// Returns a copy of the value corresponding to an element in the array.
37pub fn array_get(name: &str, index: usize) -> Option<CString> {
38    let var = super::find_raw(name)?;
39
40    let mut result = None;
41
42    unsafe {
43        if !var.is_array() {
44            return None;
45        }
46
47        #[repr(C)]
48        struct Data {
49            result: *mut Option<CString>,
50            index: usize,
51        }
52
53        unsafe extern "C" fn collect(elem: *mut ffi::ArrayElement, data: *mut c_void) -> c_int {
54            let data = &mut *data.cast::<Data>();
55
56            if usize::try_from((*elem).ind) == Ok(data.index) {
57                data.result
58                    .write(Some(CStr::from_ptr((*elem).value).to_owned()));
59                -1
60            } else {
61                1
62            }
63        }
64
65        let data = Data {
66            result: &mut result,
67            index,
68        };
69
70        ffi::array_walk(
71            (*var.0.as_ptr()).value,
72            collect,
73            &data as *const Data as *const c_void,
74        );
75    }
76
77    result
78}
79
80pub(crate) unsafe fn array_items(shell_var: *const ffi::ShellVar) -> Vec<(i64, CString)> {
81    let array: ffi::ArrayPtr = unsafe { (*shell_var).value.cast() };
82    let mut vec = Vec::new();
83
84    #[repr(C)]
85    struct Data(*mut Vec<(i64, CString)>);
86
87    unsafe extern "C" fn collect(elem: *mut ffi::ArrayElement, data: *mut c_void) -> c_int {
88        let vec = &mut *(*data.cast::<Data>()).0;
89
90        let index = (*elem).ind;
91        let value = CStr::from_ptr((*elem).value).to_owned();
92
93        vec.push((index, value));
94
95        1
96    }
97
98    ffi::array_walk(
99        array,
100        collect,
101        &Data(&mut vec) as *const Data as *const c_void,
102    );
103
104    vec
105}