You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

78 lines
2.5 KiB

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::<io::Result<ExitStatus>>();
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())),
},
}
}