use crate::wchar_ffi::WCharToFFI; #[rustfmt::skip] use ::std::pin::Pin; #[rustfmt::skip] use ::std::slice; use crate::env::{EnvMode, EnvStackRef, EnvStackRefFFI}; use crate::job_group::JobGroup; pub use crate::wait_handle::{ WaitHandleRef, WaitHandleRefFFI, WaitHandleStore, WaitHandleStoreFFI, }; use crate::wchar::prelude::*; use crate::wchar_ffi::WCharFromFFI; use autocxx::prelude::*; use cxx::SharedPtr; use libc::pid_t; // autocxx has been hacked up to know about this. pub type wchar_t = u32; include_cpp! { #include "autoload.h" #include "builtin.h" #include "color.h" #include "common.h" #include "complete.h" #include "env.h" #include "env_universal_common.h" #include "event.h" #include "exec.h" #include "fallback.h" #include "fds.h" #include "fish_indent_common.h" #include "flog.h" #include "function.h" #include "highlight.h" #include "history.h" #include "io.h" #include "input_common.h" #include "input.h" #include "parse_constants.h" #include "parser.h" #include "parse_util.h" #include "path.h" #include "proc.h" #include "reader.h" #include "screen.h" #include "tokenizer.h" #include "wildcard.h" #include "wutil.h" // We need to block these types so when exposing C++ to Rust. block!("WaitHandleStoreFFI") block!("WaitHandleRefFFI") safety!(unsafe_ffi) generate_pod!("wcharz_t") generate!("wcstring_list_ffi_t") generate!("wperror") generate!("set_inheriteds_ffi") generate!("init_input") generate_pod!("pipes_ffi_t") generate!("environment_t") generate!("env_stack_t") generate!("env_var_t") generate!("env_universal_t") generate!("env_universal_sync_result_t") generate!("callback_data_t") generate!("universal_notifier_t") generate!("var_table_ffi_t") generate!("event_list_ffi_t") generate!("make_pipes_ffi") generate!("log_extra_to_flog_file") generate!("fish_wcwidth") generate!("fish_wcswidth") generate!("wildcard_match") generate!("wgettext_ptr") generate!("block_t") generate!("parser_t") generate!("job_t") generate!("job_control_t") generate!("get_job_control_mode") generate!("set_job_control_mode") generate!("get_login") generate!("process_t") generate!("library_data_t") generate_pod!("library_data_pod_t") generate!("highlighter_t") generate!("proc_wait_any") generate!("output_stream_t") generate!("io_streams_t") generate!("make_null_io_streams_ffi") generate!("make_test_io_streams_ffi") generate!("get_test_output_ffi") generate_pod!("RustFFIJobList") generate_pod!("RustFFIProcList") generate_pod!("RustBuiltin") generate!("builtin_exists") generate!("builtin_missing_argument") generate!("builtin_unknown_option") generate!("builtin_print_help") generate!("builtin_print_error_trailer") generate!("builtin_get_names_ffi") generate!("pretty_printer_t") generate!("escape_string") generate!("fd_event_signaller_t") generate!("block_t") generate!("block_type_t") generate!("statuses_t") generate!("io_chain_t") generate!("env_var_t") generate!("exec_subshell_ffi") generate!("rgb_color_t") generate_pod!("color24_t") generate!("colorize_shell") generate!("reader_status_count") generate!("get_history_variable_text_ffi") generate!("is_interactive_session") generate!("set_interactive_session") generate!("screen_set_midnight_commander_hack") generate!("screen_clear_layout_cache_ffi") generate!("escape_code_length_ffi") generate!("reader_schedule_prompt_repaint") generate!("reader_change_history") generate!("history_session_id") generate!("reader_change_cursor_selection_mode") generate!("reader_set_autosuggestion_enabled_ffi") generate!("complete_invalidate_path") generate!("complete_add_wrapper") generate!("update_wait_on_escape_ms_ffi") generate!("update_wait_on_sequence_key_ms_ffi") generate!("autoload_t") generate!("make_autoload_ffi") generate!("perform_autoload_ffi") generate!("complete_get_wrap_targets_ffi") generate!("is_thompson_shell_script") } impl parser_t { pub fn get_wait_handles_mut(&mut self) -> &mut WaitHandleStore { let ptr = self.get_wait_handles_void() as *mut Box; assert!(!ptr.is_null()); unsafe { (*ptr).from_ffi_mut() } } pub fn get_wait_handles(&self) -> &WaitHandleStore { let ptr = self.get_wait_handles_void() as *const Box; assert!(!ptr.is_null()); unsafe { (*ptr).from_ffi() } } pub fn get_block_at_index(&self, i: usize) -> Option<&block_t> { let b = self.block_at_index(i); unsafe { b.as_ref() } } pub fn get_jobs(&self) -> &[SharedPtr] { let ffi_jobs = self.ffi_jobs(); unsafe { slice::from_raw_parts(ffi_jobs.jobs, ffi_jobs.count) } } pub fn libdata_pod(&mut self) -> &mut library_data_pod_t { let libdata = self.pin().ffi_libdata_pod(); unsafe { &mut *libdata } } pub fn remove_var(&mut self, var: &wstr, flags: c_int) -> c_int { self.pin().remove_var_ffi(&var.to_ffi(), flags) } pub fn job_get_from_pid(&self, pid: pid_t) -> Option<&job_t> { let job = self.ffi_job_get_from_pid(pid.into()); unsafe { job.as_ref() } } pub fn get_vars(&mut self) -> EnvStackRef { self.pin().vars().from_ffi() } pub fn get_func_name(&mut self, level: i32) -> Option { let name = self.pin().get_function_name_ffi(c_int(level)); name.as_ref() .map(|s| s.from_ffi()) .filter(|s| !s.is_empty()) } } unsafe impl Send for env_universal_t {} impl env_stack_t { /// Access the underlying Rust environment stack. #[allow(clippy::borrowed_box)] pub fn from_ffi(&self) -> EnvStackRef { // Safety: get_impl_ffi returns a pointer to a Box. let envref = self.get_impl_ffi(); assert!(!envref.is_null()); let env: &Box = unsafe { &*(envref.cast()) }; env.0.clone() } } impl environment_t { /// Helper to get a variable as a string, using the default flags. pub fn get_as_string(&self, name: &wstr) -> Option { self.get_as_string_flags(name, EnvMode::default()) } /// Helper to get a variable as a string, using the given flags. pub fn get_as_string_flags(&self, name: &wstr, flags: EnvMode) -> Option { self.get_or_null(&name.to_ffi(), flags.bits()) .as_ref() .map(|s| s.as_string().from_ffi()) } } impl env_stack_t { /// Helper to get a variable as a string, using the default flags. pub fn get_as_string(&self, name: &wstr) -> Option { self.get_as_string_flags(name, EnvMode::default()) } /// Helper to get a variable as a string, using the given flags. pub fn get_as_string_flags(&self, name: &wstr, flags: EnvMode) -> Option { self.get_or_null(&name.to_ffi(), flags.bits()) .as_ref() .map(|s| s.as_string().from_ffi()) } /// Helper to set a value. pub fn set_var, U: AsRef>( &mut self, name: T, value: &[U], flags: EnvMode, ) -> libc::c_int { use crate::wchar_ffi::{wstr_to_u32string, W0String}; let strings: Vec = value.iter().map(wstr_to_u32string).collect(); let ptrs: Vec<*const u32> = strings.iter().map(|s| s.as_ptr()).collect(); self.pin() .set_ffi( &name.as_ref().to_ffi(), flags.bits(), ptrs.as_ptr() as *const c_void, ptrs.len(), ) .into() } } impl job_t { #[allow(clippy::mut_from_ref)] pub fn get_procs(&self) -> &mut [UniquePtr] { let ffi_procs = self.ffi_processes(); unsafe { slice::from_raw_parts_mut(ffi_procs.procs, ffi_procs.count) } } pub fn get_job_group(&self) -> &JobGroup { unsafe { ::std::mem::transmute::<&job_group_t, &JobGroup>(self.ffi_group()) } } } impl process_t { /// \return the wait handle for the process, if it exists. pub fn get_wait_handle(&self) -> Option { let handle_ptr = self.get_wait_handle_void() as *const Box; if handle_ptr.is_null() { None } else { let handle: &WaitHandleRefFFI = unsafe { &*handle_ptr }; Some(handle.from_ffi().clone()) } } /// \return the wait handle for the process, creating it if necessary. pub fn make_wait_handle(&mut self, jid: u64) -> Option { let handle_ref = self.pin().make_wait_handle_void(jid) as *const Box; if handle_ref.is_null() { None } else { let handle: &WaitHandleRefFFI = unsafe { &*handle_ref }; Some(handle.from_ffi().clone()) } } } /// Allow wcharz_t to be "into" wstr. impl From for &wstr { fn from(w: wcharz_t) -> Self { let len = w.length(); #[allow(clippy::unnecessary_cast)] let v = unsafe { slice::from_raw_parts(w.str_ as *const u32, len) }; wstr::from_slice(v).expect("Invalid UTF-32") } } /// Allow wcharz_t to be "into" WString. impl From for WString { fn from(w: wcharz_t) -> Self { let w: &wstr = w.into(); w.to_owned() } } /// A bogus trait for turning &mut Foo into Pin<&mut Foo>. /// autocxx enforces that non-const methods must be called through Pin, /// but this means we can't pass around mutable references to types like parser_t. /// We also don't want to assert that parser_t is Unpin. /// So we just allow constructing a pin from a mutable reference; none of the C++ code. /// It's worth considering disabling this in cxx; for now we use this trait. /// Eventually parser_t and io_streams_t will not require Pin so we just unsafe-it away. pub trait Repin { fn pin(&mut self) -> Pin<&mut Self> { unsafe { Pin::new_unchecked(self) } } fn unpin(self: Pin<&mut Self>) -> &mut Self { unsafe { self.get_unchecked_mut() } } } // Implement Repin for our types. impl Repin for autoload_t {} impl Repin for block_t {} impl Repin for env_stack_t {} impl Repin for env_universal_t {} impl Repin for io_streams_t {} impl Repin for job_t {} impl Repin for output_stream_t {} impl Repin for parser_t {} impl Repin for process_t {} impl Repin for wcstring_list_ffi_t {} pub use autocxx::c_int; pub use ffi::*; pub use libc::c_char; /// A version of [`* const core::ffi::c_void`] (or [`* const libc::c_void`], if you prefer) that /// implements `Copy` and `Clone`, because those two don't. Used to represent a `void *` ptr for ffi /// purposes. #[repr(transparent)] #[derive(Copy, Clone)] pub struct void_ptr(pub *const core::ffi::c_void); impl core::fmt::Debug for void_ptr { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!(f, "{:p}", &self.0) } } unsafe impl Send for void_ptr {} unsafe impl Sync for void_ptr {} impl core::convert::From<*const core::ffi::c_void> for void_ptr { fn from(value: *const core::ffi::c_void) -> Self { Self(value as *const _) } } impl core::convert::From<*const u8> for void_ptr { fn from(value: *const u8) -> Self { Self(value as *const _) } } impl core::convert::From<*const autocxx::c_void> for void_ptr { fn from(value: *const autocxx::c_void) -> Self { Self(value as *const _) } } impl core::convert::From for *const u8 { fn from(value: void_ptr) -> Self { value.0 as *const _ } } impl core::convert::From for *const core::ffi::c_void { fn from(value: void_ptr) -> Self { value.0 as *const _ } } impl core::convert::From for *const autocxx::c_void { fn from(value: void_ptr) -> Self { value.0 as *const _ } } impl TryFrom<&wstr> for job_control_t { type Error = (); fn try_from(value: &wstr) -> Result { if value == "full" { Ok(job_control_t::all) } else if value == "interactive" { Ok(job_control_t::interactive) } else if value == "none" { Ok(job_control_t::none) } else { Err(()) } } }