bash_builtins/variables/
assoc.rs1use super::VariableError;
4use crate::ffi::variables as ffi;
5use std::ffi::{CStr, CString};
6use std::os::raw::c_char;
7
8fn cstrdup<T: AsRef<[u8]>>(bytes: T) -> Result<*const c_char, VariableError> {
14 let bytes = bytes.as_ref();
15
16 if bytes.contains(&b'\0') {
17 return Err(VariableError::InvalidValue);
18 }
19
20 unsafe { Ok(libc::strndup(bytes.as_ptr().cast(), bytes.len())) }
21}
22
23pub fn assoc_set<T0, T1>(name: &str, key: T0, value: T1) -> Result<(), VariableError>
29where
30 T0: AsRef<[u8]>,
31 T1: AsRef<[u8]>,
32{
33 let name = CString::new(name).map_err(|_| VariableError::InvalidName)?;
34 let key = cstrdup(key)?;
35 let value = cstrdup(value)?;
36
37 let res = unsafe {
38 if ffi::legal_identifier(name.as_ptr()) == 0 {
39 return Err(VariableError::InvalidName);
40 }
41
42 let mut shell_var = ffi::find_variable(name.as_ptr());
43
44 if shell_var.is_null() {
45 shell_var = ffi::make_new_assoc_variable(name.as_ptr());
46 } else if (*shell_var).attributes & ffi::ATT_ASSOC == 0 {
47 return Err(VariableError::NotAssocArray);
48 }
49
50 ffi::bind_assoc_variable(shell_var, name.as_ptr(), key, value, 0)
51 };
52
53 if res.is_null() {
54 Err(VariableError::InvalidValue)
55 } else {
56 Ok(())
57 }
58}
59
60pub fn assoc_get<T: AsRef<[u8]>>(name: &str, key: T) -> Option<CString> {
62 let key = key.as_ref();
63 let var = super::find_raw(name)?;
64
65 unsafe {
66 if !var.is_assoc() {
67 return None;
68 }
69
70 let value = var
71 .assoc_items()
72 .find(|&(k, _)| libc::strncmp(key.as_ptr().cast(), k, key.len()) == 0)
73 .map(|(_, s)| CStr::from_ptr(s).to_owned());
74
75 value
76 }
77}
78
79pub(super) struct AssocItemsIterator<'a> {
81 table: &'a ffi::HashTable,
82 num_bucket: isize,
83 current_bucket_item: Option<*const ffi::BucketContents>,
84}
85
86impl AssocItemsIterator<'_> {
87 pub(super) unsafe fn new(table: &ffi::HashTable) -> AssocItemsIterator {
88 AssocItemsIterator {
89 table,
90 num_bucket: 0,
91 current_bucket_item: None,
92 }
93 }
94}
95
96impl Iterator for AssocItemsIterator<'_> {
97 type Item = (*const c_char, *const c_char);
98
99 fn next(&mut self) -> Option<Self::Item> {
100 while self.num_bucket < self.table.nbuckets as isize {
101 let bucket = self
102 .current_bucket_item
103 .take()
104 .unwrap_or_else(|| unsafe { *self.table.bucket_array.offset(self.num_bucket) });
105
106 if !bucket.is_null() {
107 unsafe {
108 let bucket = &*bucket;
109 let item = (bucket.key, bucket.data);
110 self.current_bucket_item = Some(bucket.next);
111 return Some(item);
112 }
113 }
114
115 self.num_bucket += 1;
116 }
117
118 None
119 }
120}