zsh_module/
export_module.rs1use std::{
2 ffi::{c_char, c_int, CStr},
3 sync::atomic::AtomicBool,
4};
5
6use crate::{log, options::Opts, to_cstr, AnyError, MaybeError, Module};
7
8use parking_lot::Mutex;
9use zsh_sys as zsys;
10
11struct ModuleHolder {
12 module: Mutex<Option<Module>>,
13 panicked: AtomicBool,
14}
15
16impl ModuleHolder {
17 const fn empty() -> Self {
18 Self {
19 module: parking_lot::const_mutex(None),
20 panicked: AtomicBool::new(false),
21 }
22 }
23}
24
25unsafe impl Sync for ModuleHolder {}
28unsafe impl Send for ModuleHolder {}
29
30static MODULE: ModuleHolder = ModuleHolder::empty();
31
32unsafe fn strings_from_ptr<'a>(mut ptr: *const *const c_char) -> Vec<&'a str> {
33 let mut vec = Vec::with_capacity(2);
34 loop {
35 if (*ptr).is_null() {
36 break vec;
37 }
38 vec.push(CStr::from_ptr(*ptr).to_str().expect("Failed to parse arg"));
39 ptr = ptr.add(1);
40 }
41}
42
43extern "C" fn builtin_callback(
44 name: *mut c_char,
45 args: *mut *mut c_char,
46 opts: *mut zsys::options,
47 _: i32,
48) -> i32 {
49 handle_panic(|| {
50 let args = unsafe { strings_from_ptr(std::mem::transmute(args)) };
51 let name = unsafe { CStr::from_ptr(name) };
52 let opts = unsafe { Opts::from_raw(opts) };
53
54 let mut module = get_mod();
55 let Module {
56 bintable,
57 user_data,
58 ..
59 } = &mut *module;
60 let bin = bintable.get_mut(name).expect("Failed to find binary name");
61 match bin(
62 &mut **user_data,
63 name.to_str().expect("Failed to parse binary name"),
64 &args,
65 opts,
66 ) {
67 Ok(()) => 0,
68 Err(e) => {
69 let msg = to_cstr(e.to_string());
70 log::error_named(name, msg);
71 1
72 }
73 }
74 })
75 .unwrap_or(65)
76}
77
78pub fn set_mod(mut module: Module, name: &'static str) {
79 for x in module.features.get_binaries() {
80 x.handlerfunc = Some(builtin_callback)
81 }
82 module.name = Some(name);
83 *MODULE.module.lock() = Some(module);
84}
85
86fn drop_mod() {
87 if !panicked() {
88 MODULE.module.lock().take();
89 }
90}
91
92fn get_mod() -> parking_lot::MappedMutexGuard<'static, Module> {
93 parking_lot::MutexGuard::map(MODULE.module.lock(), |opt| {
94 opt.as_mut().expect("No module set")
95 })
96}
97
98fn panicked() -> bool {
99 MODULE.panicked.load(std::sync::atomic::Ordering::Acquire)
100}
101
102pub fn handle_maybe_error<E>(error: MaybeError<E>) -> i32
103where
104 E: std::fmt::Display,
105{
106 match error {
107 Ok(()) => 0,
108 Err(e) => {
109 let name = get_mod().name.unwrap();
110 crate::error!("{:?}: {}", name, e);
111 1
112 }
113 }
114}
115
116pub fn handle_panic<F, R>(cb: F) -> Option<R>
117where
118 F: FnOnce() -> R + std::panic::UnwindSafe,
119{
120 let res = std::panic::catch_unwind(|| cb());
121 match res {
122 Ok(ret) => Some(ret),
123 Err(err) => {
124 let name = get_mod().name.unwrap();
125 MODULE
126 .panicked
127 .store(true, std::sync::atomic::Ordering::Release);
128 if let Some(msg) = err.downcast_ref::<&str>() {
129 crate::error!("{:?} Panic: {}", name, msg);
130 } else if let Some(msg) = err.downcast_ref::<String>() {
131 crate::error!("{:?} Panic: {}", name, msg);
132 } else {
133 crate::error!("{:?} Panic: No additional information", name);
134 }
135 None
136 }
137 }
138}
139
140pub use paste;
141
142pub mod ffi {
143 pub use super::zsys::Module;
144}
145
146#[macro_export]
147macro_rules! export_module {
150 ($module_name:ident, $setupfn:ident) => {
151 #[doc(hidden)]
152 static MOD_NAME: &'static str = stringify!($module_name);
153
154 #[no_mangle]
155 #[doc(hidden)]
156 extern "C" fn setup_(_: $crate::export_module::ffi::Module) -> i32 {
157 $crate::export_module::handle_panic(|| {
158 let res = $setupfn().map(|module|
159 $crate::export_module::set_mod(module, MOD_NAME)
160 );
161 $crate::export_module::handle_maybe_error(res)
162 })
163 .unwrap_or(65)
164 }
165
166 mod _zsh_private_glue {
167 use ::std::ffi::{ c_char, c_int };
168 $crate::export_module!(@fn boot_(module: $crate::export_module::ffi::Module));
169 $crate::export_module!(@fn features_(module: $crate::export_module::ffi::Module, features_ptr: *mut *mut *mut c_char));
170 $crate::export_module!(@fn enables_(module: $crate::export_module::ffi::Module, enables_ptr: *mut *mut c_int));
171 $crate::export_module!(@fn cleanup_(module: $crate::export_module::ffi::Module));
172 $crate::export_module!(@fn finish_(module: $crate::export_module::ffi::Module) );
173 }
174 };
175 (@fn $name:ident ($($arg:ident : $type:ty),*)) => {
176 #[no_mangle]
177 #[doc(hidden)]
178 extern "C" fn $name($($arg: $type),*) -> i32 {
179 $crate::export_module::$name($($arg),*)
180 }
181 }
182}
183
184macro_rules! mod_fn {
185 (fn $name:ident($mod:ident $(,$arg:ident : $type:ty)*) try $block:expr) => {
186 mod_fn!(
187 fn $name($mod $(,$arg : $type)*) {
188 handle_maybe_error($block)
189 }
190 );
191 };
192 (fn $name:ident($mod:ident $(,$arg:ident : $type:ty)*) $block:expr) => {
193 pub fn $name($mod: $crate::zsys::Module $(,$arg: $type)*) -> i32 {
194 handle_panic(|| {
195 $block
196 }).unwrap_or(65)
197 }
198 };
199}
200
201mod_fn!(
202 fn boot_(_mod) try {
203 Ok::<_, std::convert::Infallible>(())
205 }
206);
207
208mod_fn!(
209 fn features_(mod_, features_ptr: *mut *mut *mut c_char) {
210 let mut module = get_mod();
211 unsafe { *features_ptr = zsys::featuresarray(mod_, &mut *module.features) };
212 0
213 }
214);
215
216mod_fn!(
217 fn enables_(mod_, enables_ptr: *mut *mut c_int) {
218 let mut module = get_mod();
219 unsafe {
220 zsys::handlefeatures(mod_, &mut *module.features, enables_ptr)
221 }
222 }
223);
224
225mod_fn!(
227 fn cleanup_(_mod) {
228 let mut module = get_mod();
229 unsafe {
230 zsys::setfeatureenables(_mod, &mut *module.features, std::ptr::null_mut())
231 }
232 }
233);
234
235mod_fn!(
237 fn finish_(_mod) try {
238 drop_mod();
239 Ok::<(), std::convert::Infallible>(())
240 }
241);