aboutsummaryrefslogtreecommitdiffstats
path: root/src/run
diff options
context:
space:
mode:
Diffstat (limited to 'src/run')
-rw-r--r--src/run/mod.rs223
1 files changed, 134 insertions, 89 deletions
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<u8>) {
- run_command(&se.raw, cmd);
+pub fn run(se: &mut Session, cmd: Vec<u8>) {
+ 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<dyn io::Read + Send> = match prev_reader.take() {
+ Some(r) => Box::new(r),
+ None => Box::new(io::stdin()),
+ };
+
+ let mut output: Box<dyn io::Write + Send> = 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<BString> = 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<CommandKind> {
@@ -100,88 +230,3 @@ pub enum CommandKind {
Builtin(&'static dyn Builtin),
Path(PathBuf),
}
-
-fn run_command(raw: &ScopedRawMode, line: Vec<u8>) {
- 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<Child> = 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}");
-}