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