1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
use super::VariableError;
use crate::ffi::variables as ffi;
use std::convert::TryFrom;
use std::ffi::{CStr, CString};
use std::os::raw::c_char;
use std::sync::Once;
pub fn array_set<T>(name: &str, index: usize, value: T) -> Result<(), VariableError>
where
T: AsRef<[u8]>,
{
let name = CString::new(name).map_err(|_| VariableError::InvalidName)?;
let value = CString::new(value.as_ref()).map_err(|_| VariableError::InvalidValue)?;
let res = unsafe {
if ffi::legal_identifier(name.as_ptr()) == 0 {
return Err(VariableError::InvalidName);
}
ffi::bind_array_variable(name.as_ptr(), index as _, value.as_ptr(), 0)
};
if res.is_null() {
Err(VariableError::InvalidValue)
} else {
Ok(())
}
}
pub fn array_get(name: &str, index: usize) -> Option<CString> {
let index = index as libc::intmax_t;
let var = super::find_raw(name)?;
unsafe {
if !var.is_array() {
return None;
}
let value = var
.array_items()
.find(|&(i, _)| i == index)
.map(|(_, s)| CStr::from_ptr(s).to_owned());
value
}
}
fn array_head(array: &ffi::Array) -> *const ffi::ArrayElement {
static mut IS_5_0: bool = false;
static INIT: Once = Once::new();
let is_5_0 = unsafe {
INIT.call_once(|| {
let shver = CStr::from_ptr(crate::ffi::shell_version_string());
IS_5_0 = shver.to_bytes().starts_with(b"5.0.".as_ref());
});
IS_5_0
};
if is_5_0 {
array.lastref
} else {
array.head
}
}
pub(super) struct ArrayItemsIterator<'a> {
array: &'a ffi::Array,
elem: *const ffi::ArrayElement,
}
impl ArrayItemsIterator<'_> {
pub(super) unsafe fn new(array: &ffi::Array) -> ArrayItemsIterator {
ArrayItemsIterator {
array,
elem: (*array_head(array)).next,
}
}
}
impl Iterator for ArrayItemsIterator<'_> {
type Item = (libc::intmax_t, *const c_char);
fn size_hint(&self) -> (usize, Option<usize>) {
match usize::try_from(self.array.num_elements) {
Ok(n) => (n, Some(n)),
Err(_) => (0, None),
}
}
fn next(&mut self) -> Option<Self::Item> {
if self.elem == array_head(self.array) {
return None;
}
let current = unsafe { &(*self.elem) };
let value = current.value;
self.elem = current.next;
Some((current.ind, value))
}
}