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

  1. use crate::builtins::{execute_builtin, is_builtin, BuiltinConfig};
  2. use crate::error::ShellError;
  3. use crate::parse::parse_line;
  4. use crate::preprocess::preprocess;
  5. use crossbeam_channel::{select, Receiver};
  6. use std::io::ErrorKind;
  7. use std::process::{Command, ExitStatus};
  8. use std::{io, thread};
  9. /// This function is not directly in main.rs because it might be called by other function too (eg.
  10. /// when piping commands).
  11. pub fn interpret(line: String, config: &mut BuiltinConfig, ctrlc_recv: Receiver<()>) {
  12. match try_interpret(line, config, ctrlc_recv.clone()) {
  13. Ok(_) | Err(ShellError::Interrupt) | Err(ShellError::EmptyLine) => (),
  14. Err(ShellError::NotFound(cmd)) => {
  15. eprintln!("{}: command not found", cmd.get_program().to_string_lossy())
  16. }
  17. Err(ShellError::ExecuteFailure(msg)) => {
  18. eprintln!("{}", msg)
  19. }
  20. Err(ShellError::MalformedArgs(args)) => {
  21. eprintln!("Malformed arguments: {}", args)
  22. }
  23. }
  24. }
  25. fn try_interpret(
  26. mut line: String,
  27. config: &mut BuiltinConfig,
  28. ctrlc_recv: Receiver<()>,
  29. ) -> Result<(), ShellError> {
  30. if let Some(l) = preprocess(line)? {
  31. line = l
  32. } else {
  33. return Ok(());
  34. }
  35. if line.is_empty() {
  36. return Err(ShellError::EmptyLine);
  37. }
  38. let (keyword, args) = parse_line(&line)?;
  39. if is_builtin(keyword) {
  40. execute_builtin(keyword, config, args)?;
  41. } else {
  42. let mut command = Command::new(keyword);
  43. command.args(args);
  44. execute(command, ctrlc_recv)?;
  45. }
  46. Ok(())
  47. }
  48. fn execute(mut command: Command, ctrlc_recv: Receiver<()>) -> Result<(), ShellError> {
  49. match command.spawn() {
  50. Ok(mut child) => {
  51. let (cmd_send, cmd_recv) = crossbeam_channel::unbounded::<io::Result<ExitStatus>>();
  52. thread::spawn(move || cmd_send.send(child.wait()));
  53. select! {
  54. recv(ctrlc_recv) -> _ => Err(ShellError::Interrupt),
  55. recv(cmd_recv) -> result => {
  56. if let Err(err) = result.unwrap() {
  57. Err(ShellError::ExecuteFailure(err.to_string()))
  58. } else {
  59. Ok(())
  60. }
  61. }
  62. }
  63. }
  64. Err(err) => match err.kind() {
  65. ErrorKind::NotFound => Err(ShellError::NotFound(command)),
  66. ErrorKind::Interrupted => Err(ShellError::Interrupt),
  67. _ => Err(ShellError::ExecuteFailure(err.to_string())),
  68. },
  69. }
  70. }