From d7026b8fe2dfbd9110cec73d543f0cfffe1cb977 Mon Sep 17 00:00:00 2001 From: Jonas Maier <> Date: Thu, 5 Mar 2026 13:34:03 +0100 Subject: better handling of builtins --- src/run/mod.rs | 223 ++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 134 insertions(+), 89 deletions(-) (limited to 'src/run') diff --git a/src/run/mod.rs b/src/run/mod.rs index 0a51da2..10df604 100644 --- a/src/run/mod.rs +++ b/src/run/mod.rs @@ -2,14 +2,144 @@ use std::collections::HashMap; use std::fs; use std::os::unix::fs::PermissionsExt; use std::path::PathBuf; +use std::thread::Thread; use crate::parse::Ast; use crate::*; mod builtin; -pub fn run(se: &Session, cmd: Vec) { - run_command(&se.raw, cmd); +pub fn run(se: &mut Session, cmd: Vec) { + let parsed = parse::do_parse(&cmd); + + let parsed = match parsed { + Ok(p) => p, + Err(err) => { + println!("{cmd:?}"); + print!("{err:?}\r\n{PROMPT}"); + return; + } + }; + + let Ast::Pipes(pipes) = parsed else { + todo!("can only handle pipes"); + }; + + let mut children = Vec::new(); + let mut threads = Vec::new(); + let mut prev_reader = None; + let mut spawn_error = false; + + se.raw.disable(); + + for (i, cmd) in pipes.cmds.iter().enumerate() { + let last = i == pipes.cmds.len() - 1; + + let (reader, writer) = if !last { + let (r, w) = io::pipe().unwrap(); + (Some(r), Some(w)) + } else { + (None, None) + }; + + let Some(dc) = se.dispatch.get(&cmd.cmd[..]) else { + println!( + "unknown command {}", + String::from_utf8_lossy(cmd.cmd.as_slice()) + ); + spawn_error = true; + break; + }; + + match dc { + CommandKind::Path(path) => { + let mut command = Command::new(&path); + for arg in cmd.args.iter() { + command.arg(OsStr::from_bytes(arg)); + } + + if let Some(r) = prev_reader.take() { + command.stdin(Stdio::from(r)); + } + + if let Some(w) = writer { + command.stdout(Stdio::from(w)); + } + + let Ok(child) = command.spawn() else { + println!("failed to spawn {path:?}"); + spawn_error = true; + break; + }; + + children.push(child); + } + + CommandKind::Builtin(builtin) => { + builtin.mod_session(se, &cmd.args); + + let mut input: Box = match prev_reader.take() { + Some(r) => Box::new(r), + None => Box::new(io::stdin()), + }; + + let mut output: Box = match writer { + Some(w) => Box::new(w), + None => Box::new(io::stdout()), + }; + + // SAFETY: safe as long as we join all threads below again. + // panics were not considered so probably needs to be fixed + let args = &cmd.args; + let args: &'static Vec = unsafe { std::mem::transmute(args) }; + + let handle = std::thread::spawn(move || builtin.io(args, &mut input, &mut output)); + + threads.push(handle); + } + } + + prev_reader = reader; + } + + let status_string; + + if spawn_error { + for child in children.iter_mut() { + if let Err(e) = child.kill() { + println!("failed to kill child - {e:?}"); + } + } + status_string = "ERR".into(); + } else { + let mut code = 0; + for jh in threads { + // TODO do not ignore panics + let _ = jh.join(); + } + for mut child in children { + match child.wait() { + Ok(ec) => { + if let Some(c) = ec.code() { + code = c; + } + } + Err(e) => { + println!("failed to wait for child - {e:?}") + } + } + } + if code == 0 { + status_string = String::new(); + } else { + status_string = format!("{code}"); + } + } + + se.raw.enable(); + + print!("\r{status_string}{PROMPT}"); + let _ = std::io::stdout().lock().flush(); } pub trait Builtin: Send + Sync { @@ -41,7 +171,7 @@ pub struct CommandDispatch { } impl CommandDispatch { - fn new() -> Self { + pub fn new() -> Self { let mut map = HashMap::new(); // all the commands from PATH @@ -83,7 +213,7 @@ impl CommandDispatch { map.insert(b.name().as_bytes().to_vec(), CommandKind::Builtin(b)); } - todo!() + Self { map } } fn get(&self, cmd: &bstr) -> Option { @@ -100,88 +230,3 @@ pub enum CommandKind { Builtin(&'static dyn Builtin), Path(PathBuf), } - -fn run_command(raw: &ScopedRawMode, line: Vec) { - let parsed = parse::do_parse(&line); - - let parsed = match parsed { - Ok(p) => p, - Err(err) => { - println!("{line:?}"); - print!("{err:?}\r\n{PROMPT}"); - return; - } - }; - - let Ast::Pipes(pipes) = parsed else { - todo!("can only handle pipes"); - }; - - let mut children: Vec = Vec::new(); - let mut prev_stdout = None; - let mut spawn_error = false; - - raw.disable(); - - for (i, cmd) in pipes.cmds.iter().enumerate() { - let mut command = Command::new(OsStr::from_bytes(&cmd.cmd)); - for arg in cmd.args.iter() { - command.arg(OsStr::from_bytes(arg)); - } - - if let Some(stdout) = prev_stdout.take() { - command.stdin(Stdio::from(stdout)); - } - - if i < pipes.cmds.len() - 1 { - command.stdout(Stdio::piped()); - } - - let mut child = match command.spawn() { - Ok(c) => c, - Err(e) => { - println!("failed to spawn {:?} - {e:?}", OsStr::from_bytes(&cmd.cmd)); - spawn_error = true; - break; - } - }; - - prev_stdout = child.stdout.take(); - - children.push(child); - } - - let status_string; - - if spawn_error { - for child in children.iter_mut() { - if let Err(e) = child.kill() { - println!("failed to kill child - {e:?}"); - } - } - status_string = "ERR".into(); - } else { - let mut code = 0; - for mut child in children { - match child.wait() { - Ok(ec) => { - if let Some(c) = ec.code() { - code = c; - } - } - Err(e) => { - println!("failed to wait for child - {e:?}") - } - } - } - if code == 0 { - status_string = String::new(); - } else { - status_string = format!("{code}"); - } - } - - raw.enable(); - - print!("\r{status_string}{PROMPT}"); -} -- cgit v1.2.3