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.

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