1use crate::bindings;
15use crate::error::{Error, Result};
16
17pub type CmdFn = fn(args: &[&[u8]]);
21
22const MAX_CMDS: usize = 16;
23const MAX_ARGS: usize = 8;
24
25struct CmdEntry {
26 name: &'static [u8],
27 handler: CmdFn,
28}
29
30static mut CMD_TABLE: [Option<CmdEntry>; MAX_CMDS] = {
31 const NONE: Option<CmdEntry> = None;
32 [NONE; MAX_CMDS]
33};
34static mut CMD_COUNT: usize = 0;
35
36pub fn init() -> Result<()> {
38 let rc = unsafe { bindings::ove_shell_init() };
39 Error::from_code(rc)
40}
41
42pub fn process_char(c: i32) {
44 unsafe { bindings::ove_shell_process_char(c) }
45}
46
47pub fn register_cmd(name: &'static [u8], help: &'static [u8], handler: CmdFn) -> Result<()> {
52 let idx = unsafe { CMD_COUNT };
53 if idx >= MAX_CMDS {
54 return Err(Error::NoMemory);
55 }
56
57 unsafe {
58 CMD_TABLE[idx] = Some(CmdEntry { name, handler });
59 CMD_COUNT = idx + 1;
60 }
61
62 let cmd = bindings::ove_shell_cmd {
63 name: name.as_ptr() as *const _,
64 help: help.as_ptr() as *const _,
65 handler: Some(trampoline),
66 };
67 let rc = unsafe { bindings::ove_shell_register_cmd(&cmd) };
68 Error::from_code(rc)
69}
70
71unsafe extern "C" fn trampoline(argc: core::ffi::c_int, argv: *mut *const core::ffi::c_char) {
72 if argc <= 0 || argv.is_null() {
73 return;
74 }
75
76 let argc = (argc as usize).min(MAX_ARGS);
78 let mut args: [&[u8]; MAX_ARGS] = [&[]; MAX_ARGS];
79 for i in 0..argc {
80 let ptr = unsafe { *argv.add(i) } as *const u8;
81 if !ptr.is_null() {
82 args[i] = unsafe { cstr_ptr_to_slice(ptr) };
83 }
84 }
85 let args = &args[..argc];
86
87 if args.is_empty() {
89 return;
90 }
91 let cmd_name = args[0];
92
93 let count = unsafe { CMD_COUNT };
94 for i in 0..count {
95 if let Some(entry) = unsafe { &CMD_TABLE[i] } {
96 let entry_name = strip_nul(entry.name);
98 if cmd_name == entry_name {
99 (entry.handler)(args);
100 return;
101 }
102 }
103 }
104}
105
106fn strip_nul(s: &[u8]) -> &[u8] {
107 if s.last() == Some(&0) {
108 &s[..s.len() - 1]
109 } else {
110 s
111 }
112}
113
114unsafe fn cstr_ptr_to_slice<'a>(ptr: *const u8) -> &'a [u8] {
115 let mut len = 0;
116 while unsafe { *ptr.add(len) } != 0 {
117 len += 1;
118 }
119 unsafe { core::slice::from_raw_parts(ptr, len) }
120}
121
122pub fn process_line(line: &[u8]) {
126 unsafe { bindings::ove_shell_process_line(line.as_ptr() as *const _) }
127}
128
129pub fn set_output_hook(hook: bindings::ove_shell_output_hook_t) {
133 unsafe { bindings::ove_shell_set_output_hook(hook) }
134}