diff options
Diffstat (limited to 'matrix-stdlib/src/sys.rs')
-rw-r--r-- | matrix-stdlib/src/sys.rs | 195 |
1 files changed, 174 insertions, 21 deletions
diff --git a/matrix-stdlib/src/sys.rs b/matrix-stdlib/src/sys.rs index e91e635..d30226f 100644 --- a/matrix-stdlib/src/sys.rs +++ b/matrix-stdlib/src/sys.rs @@ -1,7 +1,8 @@ -use std::{process::{exit, Command, Stdio}, env, rc::Rc, io::Read}; +use std::{process::{exit, Command, Stdio, Child}, env, rc::Rc, io::{Read, self}, cell::RefCell, fs::{File, self}, os::fd::FromRawFd, sync::OnceLock, path::PathBuf}; use matrix::{vm::Vm, value::{Value, ValueMap}, unpack_args, Result, gc::Gc}; use matrix_macros::native_func; +use os_info::Info; use crate::{VmArgs, error}; #[native_func(1)] @@ -35,26 +36,7 @@ fn env(_: VmArgs, args: Vec<Value>) -> Result<Value> { } } -#[native_func(2)] -fn exec(_: VmArgs, args: Vec<Value>) -> Result<Value> { - let [cmd, args] = unpack_args!(args); - let (cmd, args) = match (cmd, args) { - (Value::String(s), Value::List(l)) => (s, l.into_inner()), - _ => return error!("exec requires a string cmd and string argument list") - }; - let mut sargs = Vec::new(); - for arg in args { - let Value::String(arg) = arg else { - return error!("exec requires a string cmd and string argument list") - }; - sargs.push(arg.to_string()); - }; - let cmd = Command::new(cmd.to_string()) - .args(sargs) - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .spawn(); +fn exec_impl(cmd: io::Result<Child>) -> Result<Value> { let mut child = match cmd { Ok(c) => c, Err(e) => return error!("error executing command: {e}") @@ -91,9 +73,180 @@ fn exec(_: VmArgs, args: Vec<Value>) -> Result<Value> { Ok(Value::Table(res.into())) } +#[native_func(2)] +fn exec(_: VmArgs, args: Vec<Value>) -> Result<Value> { + let [cmd, args] = unpack_args!(args); + let (cmd, args) = match (cmd, args) { + (Value::String(s), Value::List(l)) => (s, l.into_inner()), + _ => return error!("exec requires a string cmd and string argument list") + }; + let mut sargs = Vec::new(); + for arg in args { + let Value::String(arg) = arg else { + return error!("exec requires a string cmd and string argument list") + }; + sargs.push(arg.to_string()); + }; + let cmd = Command::new(cmd.to_string()) + .args(sargs) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn(); + + exec_impl(cmd) +} + +#[native_func(1)] +fn system(_: VmArgs, args: Vec<Value>) -> Result<Value> { + let [cmd] = unpack_args!(args); + let Value::String(cmd) = cmd else { + return error!("system requires a full command argument string") + }; + let sh = String::from("/bin/sh"); + let args = vec!["-c".to_string(), cmd.to_string()]; + + let cmd = Command::new(sh) + .args(args) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn(); + + exec_impl(cmd) +} + +#[native_func(1)] +fn systemi(_: VmArgs, args: Vec<Value>) -> Result<Value> { + let [cmd] = unpack_args!(args); + let Value::String(cmd) = cmd else { + return error!("systemi requires a full command argument string") + }; + let sh = String::from("/bin/sh"); + let args = vec!["-c".to_string(), cmd.to_string()]; + + let cmd = Command::new(sh) + .args(args) + .stdin(Stdio::inherit()) + .stdout(Stdio::inherit()) + .stderr(Stdio::inherit()) + .spawn(); + + exec_impl(cmd) +} + +fn stdin() -> Value { + let f = unsafe { File::from_raw_fd(0) }; + Value::File(Rc::new(RefCell::new(f))) +} + +fn stdout() -> Value { + let f = unsafe { File::from_raw_fd(1) }; + Value::File(Rc::new(RefCell::new(f))) +} + +fn stderr() -> Value { + let f = unsafe { File::from_raw_fd(2) }; + Value::File(Rc::new(RefCell::new(f))) +} + +const OS_INFO: OnceLock<Info> = OnceLock::new(); + +#[native_func(0)] +fn os_type(_: VmArgs, _: Vec<Value>) -> Result<Value> { + Ok(Value::String(OS_INFO.get_or_init(|| os_info::get()).os_type().to_string().into())) +} + +#[native_func(0)] +fn os_version(_: VmArgs, _: Vec<Value>) -> Result<Value> { + Ok(Value::String(OS_INFO.get_or_init(|| os_info::get()).version().to_string().into())) +} + +#[native_func(0)] +fn os_edition(_: VmArgs, _: Vec<Value>) -> Result<Value> { + Ok(Value::String(OS_INFO.get_or_init(|| os_info::get()).edition().unwrap_or("Unknown").into())) +} + +#[native_func(0)] +fn os_bitness(_: VmArgs, _: Vec<Value>) -> Result<Value> { + Ok(Value::String(OS_INFO.get_or_init(|| os_info::get()).bitness().to_string().into())) +} + +#[native_func(0)] +fn os_arch(_: VmArgs, _: Vec<Value>) -> Result<Value> { + Ok(Value::String(OS_INFO.get_or_init(|| os_info::get()).architecture().unwrap_or("Unknown").into())) +} + +#[native_func(0)] +fn cwd(_: VmArgs, _: Vec<Value>) -> Result<Value> { + match env::current_dir() { + Ok(v) => Ok(Value::String(v.into_os_string().into_string().unwrap_or(String::new()).into())), + Err(e) => error!("cant get cwd: {e}") + } +} + +#[native_func(1)] +fn basename(_: VmArgs, args: Vec<Value>) -> Result<Value> { + let [value] = unpack_args!(args); + let Value::String(value) = value else { + return error!("basename requires a string path") + }; + let path = PathBuf::from(value.to_string()); + match path.file_name() { + Some(p) => Ok(Value::String(p.to_str().unwrap().into())), + None => Ok(Value::String(value.into())) + } +} + +#[native_func(1)] +fn dirname(_: VmArgs, args: Vec<Value>) -> Result<Value> { + let [value] = unpack_args!(args); + let Value::String(value) = value else { + return error!("basename requires a string path") + }; + let path = PathBuf::from(value.to_string()); + let parent = match path.parent() { + Some(p) => p, + None => path.as_path() + }; + let str = parent.as_os_str().to_str().unwrap(); + match str { + "" => Ok(Value::String(".".into())), + s => Ok(Value::String(s.into())) + } +} + +#[native_func(1)] +fn realpath(_: VmArgs, args: Vec<Value>) -> Result<Value> { + let [value] = unpack_args!(args); + let Value::String(value) = value else { + return error!("basename requires a string path") + }; + let path = match fs::canonicalize(value.as_ref()) { + Ok(p) => p, + Err(e) => return error!("could not get realpath: {e}") + }; + Ok(Value::String(path.to_str().unwrap().into())) +} + + pub fn load(vm: &mut Vm) { vm.load_global_fn(sys_exit(), "exit"); vm.load_global_fn(argv(), "argv"); vm.load_global_fn(exec(), "exec"); + vm.load_global_fn(system(), "system"); + vm.load_global_fn(systemi(), "systemi"); vm.load_global_fn(env(), "env"); + vm.load_global(stdin(), "stdin"); + vm.load_global(stdout(), "stdout"); + vm.load_global(stderr(), "stderr"); + vm.load_global_fn(os_type(), "os_type"); + vm.load_global_fn(os_version(), "os_version"); + vm.load_global_fn(os_edition(), "os_edition"); + vm.load_global_fn(os_bitness(), "os_bitness"); + vm.load_global_fn(os_arch(), "os_arch"); + vm.load_global_fn(cwd(), "cwd"); + vm.load_global_fn(basename(), "basename"); + vm.load_global_fn(dirname(), "dirname"); + vm.load_global_fn(realpath(), "realpath"); } |