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.

55 lines
1.7 KiB

  1. use crossbeam_channel::{select, Receiver};
  2. use std::io::ErrorKind;
  3. use std::process::{Command, ExitStatus};
  4. use std::{io, thread};
  5. use crate::builtins::{execute_builtin, is_builtin, BuiltinConfig};
  6. use crate::error::ShellError;
  7. use crate::parse::parse_line;
  8. pub fn interpret(
  9. line: String,
  10. config: &mut BuiltinConfig,
  11. ctrlc_recv: Receiver<()>,
  12. ) -> Result<(), ShellError> {
  13. if line.is_empty() {
  14. return Err(ShellError::EmptyLine);
  15. }
  16. let (keyword, args) = parse_line(&line)?;
  17. if is_builtin(keyword) {
  18. execute_builtin(keyword, config, args)?;
  19. } else {
  20. let mut command = Command::new(keyword);
  21. command.args(args);
  22. execute(command, ctrlc_recv)?;
  23. }
  24. Ok(())
  25. }
  26. fn execute(mut command: Command, ctrlc_recv: Receiver<()>) -> Result<(), ShellError> {
  27. match command.spawn() {
  28. Ok(mut child) => {
  29. let (cmd_send, cmd_recv) = crossbeam_channel::unbounded::<io::Result<ExitStatus>>();
  30. thread::spawn(move || cmd_send.send(child.wait()));
  31. select! {
  32. recv(ctrlc_recv) -> _ => Err(ShellError::Interrupt),
  33. recv(cmd_recv) -> result => {
  34. if let Err(err) = result.unwrap() {
  35. Err(ShellError::ExecuteFailure(err.to_string()))
  36. } else {
  37. Ok(())
  38. }
  39. }
  40. }
  41. }
  42. Err(err) => match err.kind() {
  43. ErrorKind::NotFound => Err(ShellError::NotFound(command)),
  44. ErrorKind::Interrupted => Err(ShellError::Interrupt),
  45. _ => Err(ShellError::ExecuteFailure(err.to_string())),
  46. },
  47. }
  48. }