Brak opisu
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.

lib.rs 6.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. extern crate nix;
  2. use std::io::{BufRead, Write};
  3. use std::str::FromStr;
  4. use std::env;
  5. use std::ffi::{OsString, CString};
  6. use nix::unistd::ForkResult::{Child, Parent};
  7. use nix::sys::wait::wait;
  8. use nix::unistd::{dup2, close, execvp, fork, pipe};
  9. mod errortype;
  10. use errortype::ErrorType;
  11. #[derive(PartialEq, Debug)]
  12. pub enum Command {
  13. Empty,
  14. Exit,
  15. Program(String),
  16. Cd(Option<OsString>),
  17. }
  18. impl FromStr for Command {
  19. type Err = ErrorType;
  20. fn from_str(s: &str) -> Result<Self, Self::Err> {
  21. let mut parts = s.split_whitespace();
  22. match parts.next() {
  23. Some("exit") => Ok(Command::Exit),
  24. Some("cd") => Ok(Command::parse_cd(parts.next())),
  25. Some(_) => Ok(Command::Program(s.to_string())),
  26. None => Ok(Command::Empty),
  27. }
  28. }
  29. }
  30. impl Command {
  31. /// If the passed String exists, set the path of the Cd in the enum (as OsString)
  32. /// Returns **Command**
  33. pub fn parse_cd(cmd: Option<&str>) -> Self {
  34. match cmd {
  35. None => Command::Cd(None),
  36. Some(dest) => Command::Cd(Some(OsString::from(dest))),
  37. }
  38. }
  39. /// If this instance is of Kind **Command::Cd** either go to HOME or navigate to the given path
  40. pub fn exec_cd(&self) {
  41. if let Command::Cd(None) = *self {
  42. let possible_home = env::home_dir();
  43. if let Some(home) = possible_home {
  44. let home_path = home.as_path();
  45. let _ = env::set_current_dir(home_path);
  46. }
  47. }
  48. if let Command::Cd(Some(ref path)) = *self {
  49. match path.to_str() {
  50. Some(_) => {
  51. let _ = env::set_current_dir(path);
  52. }
  53. None => {}
  54. }
  55. }
  56. }
  57. }
  58. pub struct Shell<R: BufRead, W: Write> {
  59. pub reader: R,
  60. pub writer: W,
  61. pub should_exit: bool,
  62. pub name: String,
  63. }
  64. impl<R: BufRead, W: Write> Shell<R, W> {
  65. /// Creates a new Shell with a name, input and output.
  66. pub fn new(input: R, output: W, name: String) -> Self {
  67. Shell {
  68. reader: input,
  69. writer: output,
  70. should_exit: false,
  71. name: name,
  72. }
  73. }
  74. /// Initializes the Shell Loop.
  75. /// Starts the shell.
  76. pub fn start(&mut self) -> Result<(), ErrorType> {
  77. self.shell_loop()
  78. }
  79. /// Loops while exit hasn't been called.
  80. /// When *prompt()* returns (the user hit enter) a Command is created through the FromStr-Trait.
  81. /// If the Command-creation succeeds, run the command.
  82. fn shell_loop(&mut self) -> Result<(), ErrorType> {
  83. while !self.should_exit {
  84. if let Ok(Some(line)) = self.prompt() {
  85. let _ = Command::from_str(&line).and_then(|cmd| self.run(cmd));
  86. }
  87. }
  88. Ok(())
  89. }
  90. /// Prints the shell prompt.
  91. /// Waits for user input and returns the read line.
  92. pub fn prompt(&mut self) -> Result<Option<String>, ErrorType> {
  93. match env::current_dir() {
  94. Ok(pwd) => {
  95. let _ = self.writer.write(
  96. format!("{} {} > ", self.name, pwd.display())
  97. .as_bytes(),
  98. );
  99. let _ = self.writer.flush();
  100. let mut line: String = String::new();
  101. let read_result = self.reader.read_line(&mut line);
  102. match read_result {
  103. Ok(_) => Ok(Some(line)),
  104. Err(_) => Ok(None),
  105. }
  106. }
  107. Err(_) => return Err(ErrorType::PathError),
  108. }
  109. }
  110. /// Runs a command.
  111. /// Currently only `cd` and `exit` are working.
  112. fn run(&mut self, command: Command) -> Result<(), ErrorType> {
  113. match command {
  114. Command::Empty => {}
  115. Command::Program(cmd) => {
  116. let commands = cmd.split('|');
  117. let commands_vec: Vec<&str> = commands.collect();
  118. let needs_pipe = commands_vec.len() == 2;
  119. let fi = fork();
  120. match fi {
  121. Ok(Parent { child: _child }) => {
  122. let _ = wait();
  123. }
  124. Ok(Child) => {
  125. if let Ok((reader, writer)) = pipe() {
  126. let sec = fork();
  127. match sec {
  128. Ok(Parent { child: _child }) => {
  129. let _ = wait();
  130. if let Some(second) = commands_vec.get(1) {
  131. match close(writer) {
  132. Ok(_) => {}
  133. Err(_) => return Err(ErrorType::BrokenPipeError),
  134. }
  135. dup2(reader, 0).unwrap();
  136. self.execute(second);
  137. }
  138. }
  139. Ok(Child) => {
  140. if let Some(first) = commands_vec.get(0) {
  141. if needs_pipe {
  142. close(reader).unwrap();
  143. dup2(writer, 1).unwrap();
  144. }
  145. self.execute(first);
  146. }
  147. }
  148. Err(_) => return Err(ErrorType::ForkError),
  149. }
  150. }
  151. }
  152. Err(_) => return Err(ErrorType::ForkError),
  153. }
  154. }
  155. Command::Exit => self.should_exit = true,
  156. Command::Cd(_) => {
  157. command.exec_cd();
  158. }
  159. }
  160. Ok(())
  161. }
  162. fn execute(&self, cmd: &str) {
  163. let parts: Vec<&str> = cmd.split_whitespace().collect();
  164. let args: Vec<CString> = parts.iter().map(|f| CString::new(*f).unwrap()).collect();
  165. let t = args.into_boxed_slice();
  166. execvp(&t[0], &t).unwrap();
  167. }
  168. }