1 use std::{ 2 cell::RefCell, 3 fs::{self, File, OpenOptions}, 4 io::{BufRead, BufReader, Read, Write}, 5 ops::Deref, 6 print, 7 process::Child, 8 rc::Rc, 9 }; 10 11 use crate::{ 12 keycode::{FunctionKeySuffix, SpecialKeycode}, 13 parser::{Parser, Pipeline}, 14 }; 15 16 use colored::Colorize; 17 use command::BuildInCmd; 18 use printer::Printer; 19 use thread_manager::ThreadManager; 20 21 mod printer; 22 23 mod thread_manager; 24 25 pub mod command; 26 27 const DEFAULT_HISTORY_COMMANDS_PATH: &str = "/history_commands.txt"; 28 29 #[allow(dead_code)] 30 pub struct Shell { 31 history_commands: Vec<Rc<RefCell<Vec<u8>>>>, 32 history_path: String, 33 printer: Printer, 34 backend_thread: ThreadManager<Pipeline, Child>, 35 } 36 37 impl Shell { 38 pub fn new() -> Shell { 39 if BuildInCmd::map().is_none() { 40 unsafe { BuildInCmd::init() }; 41 } 42 43 let mut shell = Shell { 44 history_commands: Vec::new(), 45 history_path: DEFAULT_HISTORY_COMMANDS_PATH.to_string(), 46 printer: Printer::new(&Rc::new(RefCell::new(Vec::new()))), 47 backend_thread: Self::create_backend_thread(), 48 }; 49 shell.read_commands(); 50 shell 51 } 52 53 fn create_backend_thread() -> ThreadManager<Pipeline, Child> { 54 ThreadManager::new(|| { 55 let (p_s, c_r) = std::sync::mpsc::channel::<Pipeline>(); 56 let (c_s, p_r) = std::sync::mpsc::channel::<Child>(); 57 let map = BuildInCmd::map(); 58 let func = move || loop { 59 if let Ok(pipeline) = c_r.recv() { 60 for child in pipeline.execute(map.clone()) { 61 let _ = c_s.send(child); 62 } 63 }; 64 }; 65 (p_s, p_r, func) 66 }) 67 } 68 69 pub fn exec(&mut self) { 70 // 设置前台进程组 71 unsafe { 72 libc::tcsetpgrp(libc::STDIN_FILENO, std::process::id() as i32); 73 }; 74 75 // 开启终端raw模式 76 crossterm::terminal::enable_raw_mode().expect("failed to enable raw mode"); 77 78 // 循环读取一行 79 loop { 80 self.printer.init_before_readline(); 81 // 读取一行 82 if self.readline() == 0 { 83 println!(); 84 break; 85 } 86 87 let command_bytes = self.printer.buf.borrow().clone(); 88 // 如果命令不以空格开头且不跟上一条命令相同,这条命令会被记录 89 if !command_bytes.is_empty() 90 && !command_bytes.starts_with(&[b' ']) 91 && command_bytes 92 != self 93 .history_commands 94 .last() 95 .unwrap_or(&Rc::new(RefCell::new(Vec::new()))) 96 .borrow() 97 .clone() 98 { 99 self.history_commands 100 .push(Rc::new(RefCell::new(command_bytes.clone()))); 101 self.write_commands(&command_bytes); 102 }; 103 // 命令不为空,执行命令 104 if !command_bytes.iter().all(|&byte| byte == b' ') { 105 self.exec_commands_in_line(&command_bytes); 106 } 107 } 108 } 109 110 fn exec_commands_in_line(&mut self, command_bytes: &Vec<u8>) { 111 // match Command::parse(String::from_utf8(command_bytes.clone()).unwrap()) { 112 // Ok(commands) => commands 113 // .into_iter() 114 // .for_each(|command| self.exec_command(command)), 115 // Err(e) => CommandError::handle(e), 116 // } 117 118 // 解析命令 119 let input_command = String::from_utf8(command_bytes.clone()).unwrap(); 120 let pipelines = Parser::parse(&input_command).unwrap(); 121 122 let mut foreground_pipelines = Vec::new(); 123 124 // 后台pipeline发送给子线程执行 125 for pipeline in pipelines { 126 if pipeline.backend() { 127 let _ = self.backend_thread.send(pipeline); 128 } else { 129 foreground_pipelines.push(pipeline); 130 } 131 } 132 133 crossterm::terminal::disable_raw_mode().expect("failed to disable raw mode"); 134 135 // 顺序执行所有前台pipeline 136 for pipeline in &foreground_pipelines { 137 for mut child in pipeline.execute(BuildInCmd::map().clone()) { 138 let _ = child.wait(); 139 } 140 } 141 142 crossterm::terminal::enable_raw_mode().expect("failed to enable raw mode"); 143 144 foreground_pipelines.clear(); 145 } 146 147 pub fn read_commands(&mut self) { 148 let mut history = Vec::new(); 149 for line in BufReader::new(match File::open(&self.history_path) { 150 Ok(file) => file, 151 Err(_) => File::create(&self.history_path).unwrap(), 152 }) 153 .lines() 154 { 155 match line { 156 Ok(s) => history.push(Rc::new(RefCell::new(s.into_bytes()))), 157 Err(_) => { 158 break; 159 } 160 } 161 } 162 self.history_commands = history; 163 } 164 165 fn write_commands(&self, command_bytes: &Vec<u8>) { 166 let mut file = OpenOptions::new() 167 .append(true) 168 .open(self.history_path.as_str()) 169 .unwrap(); 170 file.write_all(&command_bytes) 171 .expect("failed to write history command"); 172 file.write_all(&[SpecialKeycode::LF.into()]).unwrap(); 173 } 174 175 fn read_char() -> u8 { 176 let mut buf: [u8; 1] = [0]; 177 loop { 178 if std::io::stdin().read(&mut buf).is_ok() { 179 return buf[0]; 180 } 181 } 182 } 183 184 fn handle_funckey(&mut self, command_index: &mut usize) { 185 let mut keys = Vec::new(); 186 187 while FunctionKeySuffix::should_read_more(&keys) { 188 keys.push(Self::read_char()); 189 } 190 let function_key = FunctionKeySuffix::try_from(&keys); 191 if function_key.is_none() { 192 return; 193 } 194 195 let function_key = function_key.unwrap(); 196 197 match function_key { 198 FunctionKeySuffix::Up => { 199 if *command_index > 0 { 200 *command_index -= 1; 201 self.printer 202 .change_line(self.history_commands.get(*command_index).unwrap()); 203 } 204 } 205 206 FunctionKeySuffix::Down => { 207 if *command_index < self.history_commands.len() - 1 { 208 *command_index += 1; 209 self.printer 210 .change_line(self.history_commands.get(*command_index).unwrap()); 211 } 212 } 213 214 FunctionKeySuffix::Left => { 215 if self.printer.cursor > 0 { 216 self.printer.cursor_left(1); 217 } 218 } 219 220 FunctionKeySuffix::Right => { 221 if self.printer.cursor < self.printer.buf.borrow().len() { 222 self.printer.cursor_right(1); 223 } 224 } 225 226 FunctionKeySuffix::Home => { 227 self.printer.home(); 228 } 229 230 FunctionKeySuffix::End => { 231 self.printer.end(); 232 } 233 FunctionKeySuffix::Delete => self.printer.delete(1), 234 } 235 } 236 237 fn readline(&mut self) -> usize { 238 let mut stdout = std::io::stdout(); 239 self.history_commands.push(Rc::clone(&self.printer.buf)); 240 let mut command_index = self.history_commands.len() - 1; 241 loop { 242 let key = Self::read_char(); 243 if let Ok(special_key) = SpecialKeycode::try_from(key) { 244 match special_key { 245 SpecialKeycode::ESC => { 246 self.handle_funckey(&mut command_index); 247 } 248 249 SpecialKeycode::LF | SpecialKeycode::CR => { 250 println!(); 251 self.history_commands.pop(); 252 return 1; 253 } 254 255 SpecialKeycode::BackSpace => { 256 self.printer.backspace(); 257 } 258 259 SpecialKeycode::Tab => { 260 let mut buf = self.printer.buf.deref().borrow().clone(); 261 buf.truncate(self.printer.cursor); 262 let str = String::from_utf8(buf.clone()).unwrap(); 263 if buf.len() == 0 || buf.iter().all(|byte| *byte == b' ') { 264 continue; 265 } 266 267 let iter = str.chars(); 268 let mut fragments: Vec<String> = Vec::new(); 269 let mut stack: String = String::with_capacity(str.len()); 270 let mut left_quote: char = ' '; 271 for ch in iter { 272 //存在未闭合的左引号,此时包括空格的任何字符都加入栈中,直到匹配到右引号 273 if left_quote != ' ' { 274 if ch == left_quote { 275 left_quote = ' '; 276 } 277 stack.push(ch); 278 } else { 279 //不存在未闭合的左引号 280 if ch == '\'' || ch == '\"' { 281 //字符为引号,记录下来 282 left_quote = ch; 283 stack.push(ch); 284 } else if ch == ' ' { 285 if !stack.is_empty() { 286 //字符为空格且栈中不为空,该空格视作命令段之间的分割线 287 //将栈中字符作为一个命令段加入集合,之后重置栈 288 fragments.push(stack.to_string()); 289 stack.clear(); 290 } 291 } else { 292 //其他字符都作为普通字符加入栈中 293 stack.push(ch); 294 } 295 } 296 } 297 //结束时如果栈不为空 298 if !stack.is_empty() { 299 fragments.push(stack.to_string()); 300 } else { 301 //结束时如果栈为空,说明光标左边的字符不属于任何命令片段,无法进行补全 302 return 1; 303 } 304 305 let mut target_fragment = fragments.last().unwrap().clone(); 306 target_fragment = target_fragment.replace("\'", "").replace("\"", ""); 307 308 let (prefix, candidates) = if fragments.len() < 2 { 309 //补全命令 310 complete_command(&target_fragment) 311 } else { 312 //补全参数 313 complete_path(&target_fragment) 314 }; 315 316 match candidates.len() { 317 1 => { 318 let old_fragment = fragments.last().unwrap(); 319 let candidate = candidates.last().unwrap(); 320 self.printer.cursor_left(old_fragment.len()); 321 self.printer.delete(old_fragment.len()); 322 self.printer 323 .insert(format!("{}{}", prefix, candidate).as_bytes()); 324 } 325 2.. => { 326 self.printer.end(); 327 println!(); 328 for candidate in candidates { 329 print!( 330 "{}\t", 331 if candidate.ends_with('/') { 332 candidate.truecolor(0x00, 0x88, 0xff) 333 } else { 334 candidate.white() 335 } 336 ); 337 } 338 println!(); 339 self.printer.print_prompt(); 340 print!( 341 "{}", 342 String::from_utf8(self.printer.buf.borrow().to_vec()).unwrap() 343 ); 344 } 345 _ => {} 346 } 347 } 348 349 _ => {} 350 } 351 } else { 352 match key { 353 1..=31 => {} 354 c => { 355 self.printer.insert(&[c]); 356 // String::from_utf8("abdsdf".as_bytes().to_vec()).unwrap(); 357 } 358 } 359 } 360 stdout.flush().unwrap(); 361 } 362 } 363 } 364 365 #[allow(dead_code)] 366 struct WindowSize { 367 row: usize, 368 col: usize, 369 } 370 371 #[allow(dead_code)] 372 impl WindowSize { 373 pub fn new() -> Option<Self> { 374 let mut ws: libc::winsize = unsafe { std::mem::zeroed() }; 375 if unsafe { 376 libc::ioctl( 377 libc::STDOUT_FILENO, 378 libc::TIOCGWINSZ, 379 &mut ws as *mut libc::winsize, 380 ) 381 } == -1 382 { 383 None 384 } else { 385 Some(Self { 386 row: ws.ws_row.into(), 387 col: ws.ws_col.into(), 388 }) 389 } 390 } 391 } 392 393 pub fn complete_command(command: &str) -> (&str, Vec<String>) { 394 let mut candidates: Vec<String> = Vec::new(); 395 for (cmd, _) in BuildInCmd::map().as_ref().unwrap().lock().unwrap().iter() { 396 if cmd.starts_with(command) { 397 candidates.push(String::from(cmd)); 398 } 399 } 400 ("", candidates) 401 } 402 403 pub fn complete_path(incomplete_path: &str) -> (&str, Vec<String>) { 404 let mut candidates: Vec<String> = Vec::new(); 405 let mut dir = ""; 406 let incomplete_name: &str; 407 if let Some(index) = incomplete_path.rfind('/') { 408 dir = &incomplete_path[..=index]; 409 incomplete_name = &incomplete_path[index + 1..]; 410 } else { 411 incomplete_name = incomplete_path; 412 } 413 if let Ok(read_dir) = fs::read_dir(if dir.is_empty() { "." } else { dir }) { 414 for entry in read_dir { 415 let entry = entry.unwrap(); 416 let mut file_name = entry.file_name().into_string().unwrap(); 417 if file_name.starts_with(incomplete_name) { 418 if file_name.contains(' ') { 419 file_name = format!("\'{}\'", file_name); 420 } 421 if entry.file_type().unwrap().is_dir() { 422 file_name.push('/'); 423 } 424 candidates.push(file_name); 425 } 426 } 427 } 428 429 return (dir, candidates); 430 } 431