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
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())),
|
|
},
|
|
}
|
|
}
|