use crate::builtins::{execute_builtin, is_builtin, BuiltinConfig}; use crate::error::ShellError; use crate::parse::parse_line; use crate::preprocess::preprocess; use crossbeam_channel::{select, Receiver}; use std::io::ErrorKind; use std::process::{Command, ExitStatus}; use std::{io, thread}; /// This function is not directly in main.rs because it might be called by other function too (eg. /// when piping commands). pub fn interpret(line: String, config: &mut BuiltinConfig, ctrlc_recv: Receiver<()>) { match try_interpret(line, config, ctrlc_recv.clone()) { Ok(_) | Err(ShellError::Interrupt) | Err(ShellError::EmptyLine) => (), Err(ShellError::NotFound(cmd)) => { eprintln!("{}: command not found", cmd.get_program().to_string_lossy()) } Err(ShellError::ExecuteFailure(msg)) => { eprintln!("{}", msg) } Err(ShellError::MalformedArgs(args)) => { eprintln!("Malformed arguments: {}", args) } } } fn try_interpret( mut line: String, config: &mut BuiltinConfig, ctrlc_recv: Receiver<()>, ) -> Result<(), ShellError> { if let Some(l) = preprocess(line)? { line = l } else { return Ok(()); } if line.is_empty() { return Err(ShellError::EmptyLine); } let (keyword, args) = parse_line(&line)?; if is_builtin(keyword) { execute_builtin(keyword, config, args)?; } else { let mut command = Command::new(keyword); command.args(args); execute(command, ctrlc_recv)?; } Ok(()) } fn execute(mut command: Command, ctrlc_recv: Receiver<()>) -> Result<(), ShellError> { match command.spawn() { Ok(mut child) => { let (cmd_send, cmd_recv) = crossbeam_channel::unbounded::>(); thread::spawn(move || cmd_send.send(child.wait())); select! { recv(ctrlc_recv) -> _ => Err(ShellError::Interrupt), recv(cmd_recv) -> result => { if let Err(err) = result.unwrap() { Err(ShellError::ExecuteFailure(err.to_string())) } else { Ok(()) } } } } Err(err) => match err.kind() { ErrorKind::NotFound => Err(ShellError::NotFound(command)), ErrorKind::Interrupted => Err(ShellError::Interrupt), _ => Err(ShellError::ExecuteFailure(err.to_string())), }, } }