1 use help::Help; 2 use path_clean::PathClean; 3 use regex::{Captures, Regex}; 4 use std::{format, fs::File, io::Read, path::Path, print, println}; 5 6 use crate::env::{Env, ROOT_PATH}; 7 use crate::shell::Shell; 8 9 mod help; 10 11 #[derive(Debug, PartialEq, Eq, Clone)] 12 enum CommandType { 13 InternalCommand(BuildInCmd), 14 ExternalCommand(String), 15 } 16 17 #[derive(Debug, PartialEq, Eq, Clone)] 18 pub struct Command { 19 args: Vec<String>, 20 cmd_type: CommandType, 21 run_backend: bool, 22 } 23 24 #[allow(dead_code)] 25 #[derive(Debug, PartialEq, Eq, Clone)] 26 pub enum CommandError { 27 CommandNotFound(String), 28 InvalidArgument(String), 29 WrongArgumentCount(usize), 30 EnvironmentVariableNotFound(String), 31 PathNotFound(String), 32 FileNotFound(String), 33 DirectoryNotFound(String), 34 NotDirectory(String), 35 NotFile(String), 36 UnclosedQuotation(usize), 37 UnableGetArg, 38 EmptyCommand, 39 } 40 41 impl CommandError { 42 pub fn handle(e: CommandError) { 43 match e { 44 CommandError::CommandNotFound(command) => { 45 println!("cannot find command: {}", command) 46 } 47 CommandError::InvalidArgument(argument) => { 48 println!("invalid argument: {}", argument) 49 } 50 CommandError::WrongArgumentCount(count) => { 51 println!("argument count incorrect: {}", count) 52 } 53 CommandError::EnvironmentVariableNotFound(env) => { 54 println!("environment variable not found: {}", env); 55 } 56 CommandError::PathNotFound(path) => { 57 println!("cannot found file or dirctory: {}", path) 58 } 59 CommandError::FileNotFound(path) => { 60 println!("cannot found file: {}", path) 61 } 62 CommandError::DirectoryNotFound(path) => { 63 println!("cannot found dirctory: {}", path) 64 } 65 CommandError::NotDirectory(path) => { 66 println!("path is not a dirctory: {}", path) 67 } 68 CommandError::NotFile(path) => { 69 println!("path is not a file: {}", path) 70 } 71 CommandError::UnclosedQuotation(index) => { 72 println!("command exists unclosed quotation at index: {}", index) 73 } 74 CommandError::UnableGetArg => { 75 println!("unable to get argument") 76 } 77 CommandError::EmptyCommand => println!("try to construct an empty command"), 78 } 79 } 80 } 81 82 impl Command { 83 fn new(name: String, args: Vec<String>, run_backend: bool) -> Result<Command, CommandError> { 84 for BuildInCmd(cmd) in BuildInCmd::BUILD_IN_CMD { 85 if name == *cmd { 86 return Ok(Command { 87 args, 88 cmd_type: CommandType::InternalCommand(BuildInCmd(cmd)), 89 run_backend, 90 }); 91 } 92 } 93 94 return Ok(Command { 95 args, 96 cmd_type: CommandType::ExternalCommand(name), 97 run_backend, 98 }); 99 } 100 101 pub fn parse(str: String) -> Result<Vec<Command>, CommandError> { 102 let iter = str.chars(); 103 let mut fragments: Vec<String> = Vec::new(); 104 let mut stack: String = String::with_capacity(str.len()); 105 let mut left_quote: char = ' '; 106 let mut left_quote_index: usize = 0; 107 let mut commands: Vec<Command> = Vec::new(); 108 for (index, ch) in iter.enumerate() { 109 //存在未闭合的左引号,此时除能够配对的引号外,任何字符都加入栈中 110 if left_quote != ' ' { 111 if ch == left_quote { 112 left_quote = ' '; 113 } else { 114 stack.push(ch); 115 } 116 } else { 117 //不存在未闭合的左引号 118 if ch == '\'' || ch == '\"' { 119 //字符为引号,记录下来 120 left_quote = ch; 121 left_quote_index = index; 122 } else if ch == ' ' && !stack.is_empty() { 123 //字符为空格且栈中不为空,该空格视作命令段之间的分割线 124 //将栈中字符作为一个命令段加入集合,之后重置栈 125 fragments.push(stack.to_string()); 126 stack.clear(); 127 } else if ch == ';' || ch == '&' { 128 // ;和&视作命令之间的分隔,且&标志命令后台运行 129 // 使用命令段构造一条命令 130 if !stack.is_empty() { 131 fragments.push(stack.to_string()); 132 stack.clear(); 133 } 134 if !fragments.is_empty() { 135 match Self::build_command_from_fragments(&fragments, ch == '&') { 136 Ok(command) => commands.push(command), 137 Err(e) => return Err(e), 138 } 139 } 140 141 fragments.clear(); 142 } else { 143 //其他字符都作为普通字符加入栈中 144 stack.push(ch); 145 } 146 } 147 } 148 //结束时如果栈不为空 149 if !stack.is_empty() { 150 if left_quote == ' ' { 151 //不存在未闭合的引号,将栈中剩余内容作为命令段加入集合,并构造命令 152 fragments.push(stack.to_string()); 153 match Self::build_command_from_fragments(&fragments, false) { 154 Ok(command) => commands.push(command), 155 Err(e) => return Err(e), 156 } 157 } else { 158 //存在未闭合的引号,返回此引号的下标 159 return Err(CommandError::UnclosedQuotation(left_quote_index)); 160 } 161 } 162 Ok(commands) 163 } 164 165 fn build_command_from_fragments( 166 fragments: &Vec<String>, 167 run_backend: bool, 168 ) -> Result<Command, CommandError> { 169 if fragments.len() == 0 { 170 return Err(CommandError::EmptyCommand); 171 } 172 173 let mut iter = fragments.into_iter(); 174 175 let name = iter.next().unwrap(); 176 let re: Regex = Regex::new(r"\$[\w_]+").unwrap(); 177 let replacement = |caps: &Captures| -> String { 178 match Env::get(&String::from(&caps[0][1..])) { 179 Some(entry) => entry.origin().clone(), 180 None => String::from(&caps[0]), 181 } 182 }; 183 let mut args: Vec<String> = Vec::new(); 184 for arg in iter.collect::<Vec<&String>>().iter() { 185 let arg = re.replace_all(arg.as_str(), &replacement).to_string(); 186 match re.captures(arg.as_str()) { 187 Some(caps) => { 188 return Err(CommandError::EnvironmentVariableNotFound(String::from( 189 caps.get(0).unwrap().as_str(), 190 ))) 191 } 192 None => args.push(arg), 193 } 194 } 195 Command::new(name.clone(), args, run_backend) 196 } 197 } 198 199 #[derive(Debug, PartialEq, Eq, Clone)] 200 pub struct BuildInCmd(pub &'static str); 201 202 impl BuildInCmd { 203 pub const BUILD_IN_CMD: &'static [BuildInCmd] = &[ 204 BuildInCmd("cd"), 205 BuildInCmd("exec"), 206 BuildInCmd("reboot"), 207 BuildInCmd("free"), 208 BuildInCmd("help"), 209 BuildInCmd("export"), 210 BuildInCmd("compgen"), 211 BuildInCmd("complete"), 212 ]; 213 } 214 215 impl Shell { 216 pub fn exec_internal_command( 217 &mut self, 218 cmd: &str, 219 args: &Vec<String>, 220 ) -> Result<(), CommandError> { 221 match cmd { 222 "cd" => self.shell_cmd_cd(args), 223 "exec" => self.shell_cmd_exec(args), 224 "reboot" => self.shell_cmd_reboot(args), 225 "free" => self.shell_cmd_free(args), 226 "help" => self.shell_cmd_help(args), 227 "export" => self.shell_cmd_export(args), 228 "compgen" => self.shell_cmd_compgen(args), 229 "complete" => self.shell_cmd_complete(args), 230 231 _ => Err(CommandError::CommandNotFound(String::from(cmd))), 232 } 233 } 234 235 pub fn exec_external_command(&mut self, path: String, args: &Vec<String>) { 236 let mut full_args = args.clone(); 237 full_args.insert(0, path.clone()); 238 self.shell_cmd_exec(&full_args).unwrap_or_else(|e| { 239 let err = match e { 240 CommandError::FileNotFound(rp) => CommandError::CommandNotFound(rp), 241 _ => e, 242 }; 243 CommandError::handle(err); 244 }) 245 } 246 247 pub fn exec_command(&mut self, mut command: Command) { 248 if command.run_backend { 249 command.args.push("&".to_string()); 250 } 251 252 match &command.cmd_type { 253 CommandType::ExternalCommand(path) => { 254 self.exec_external_command(path.to_string(), &command.args); 255 } 256 257 CommandType::InternalCommand(BuildInCmd(cmd)) => { 258 match self.exec_internal_command(cmd, &command.args) { 259 Ok(_) => {} 260 Err(e) => CommandError::handle(e), 261 } 262 if command.args.contains(&String::from("--help")) { 263 Help::shell_help(cmd); 264 } 265 } 266 } 267 } 268 269 fn shell_cmd_cd(&mut self, args: &Vec<String>) -> Result<(), CommandError> { 270 let path = match args.len() { 271 0 => String::from(ROOT_PATH), 272 1 => self.is_dir(args.get(0).unwrap())?, 273 _ => return Err(CommandError::WrongArgumentCount(args.len())), 274 }; 275 self.chdir(&path); 276 Ok(()) 277 } 278 279 pub fn shell_cmd_exec(&mut self, args: &Vec<String>) -> Result<(), CommandError> { 280 if args.len() <= 0 { 281 return Err(CommandError::WrongArgumentCount(args.len())); 282 } 283 284 let path = args.get(0).unwrap(); 285 let real_path = match Env::search_path_from_env(path) { 286 Some(str) => str, 287 None => return Err(CommandError::FileNotFound(path.clone())), 288 }; 289 290 // 如果文件不存在,返回错误 291 if !Path::new(&real_path).is_file() { 292 // println!("{}: command not found", real_path); 293 return Err(CommandError::NotFile(real_path.clone())); 294 } 295 296 let mut args = args.split_first().unwrap().1; 297 let run_backend = if let Some(last) = args.last() { 298 if last == "&" { 299 args = &args[..args.len() - 1]; 300 true 301 } else { 302 false 303 } 304 } else { 305 false 306 }; 307 308 crossterm::terminal::disable_raw_mode().expect("failed to disable raw mode"); 309 310 let mut child = std::process::Command::new(real_path) 311 .args(args) 312 .current_dir(Env::current_dir()) 313 .envs(Env::get_all()) 314 .spawn() 315 .expect("Failed to execute command"); 316 317 if !run_backend { 318 let _ = child.wait(); 319 } else { 320 self.add_backend_task(child); 321 } 322 323 crossterm::terminal::enable_raw_mode().expect("failed to enable raw mode"); 324 return Ok(()); 325 } 326 327 fn shell_cmd_reboot(&self, args: &Vec<String>) -> Result<(), CommandError> { 328 if args.len() == 0 { 329 unsafe { libc::syscall(libc::SYS_reboot, 0, 0, 0, 0, 0, 0) }; 330 return Ok(()); 331 } else { 332 return Err(CommandError::WrongArgumentCount(args.len())); 333 } 334 } 335 336 fn shell_cmd_free(&self, args: &Vec<String>) -> Result<(), CommandError> { 337 if args.len() == 1 && args.get(0).unwrap() != "-m" { 338 return Err(CommandError::InvalidArgument( 339 args.get(0).unwrap().to_string(), 340 )); 341 } 342 343 struct Mstat { 344 total: u64, // 计算机的总内存数量大小 345 used: u64, // 已使用的内存大小 346 free: u64, // 空闲物理页所占的内存大小 347 shared: u64, // 共享的内存大小 348 cache_used: u64, // 位于slab缓冲区中的已使用的内存大小 349 cache_free: u64, // 位于slab缓冲区中的空闲的内存大小 350 available: u64, // 系统总空闲内存大小(包括kmalloc缓冲区) 351 } 352 353 let mut mst = Mstat { 354 total: 0, 355 used: 0, 356 free: 0, 357 shared: 0, 358 cache_used: 0, 359 cache_free: 0, 360 available: 0, 361 }; 362 363 let mut info_file = File::open("/proc/meminfo").unwrap(); 364 let mut buf: Vec<u8> = Vec::new(); 365 info_file.read_to_end(&mut buf).unwrap(); 366 let str = String::from_utf8(buf).unwrap(); 367 let info = str 368 .split(&['\n', '\t', ' ']) 369 .filter_map(|str| str.parse::<u64>().ok()) 370 .collect::<Vec<u64>>(); 371 mst.total = *info.get(0).unwrap(); 372 mst.free = *info.get(1).unwrap(); 373 mst.used = mst.total - mst.free; 374 375 print!("\ttotal\t\tused\t\tfree\t\tshared\t\tcache_used\tcache_free\tavailable\n"); 376 print!("Mem:\t"); 377 378 if args.len() == 0 { 379 print!( 380 "{}\t\t{}\t\t{}\t\t{}\t\t{}\t\t{}\t\t{}\n", 381 mst.total, 382 mst.used, 383 mst.free, 384 mst.shared, 385 mst.cache_used, 386 mst.cache_free, 387 mst.available 388 ); 389 } else { 390 print!( 391 "{}\t\t{}\t\t{}\t\t{}\t\t{}\t\t{}\n", 392 mst.total >> 10, 393 mst.used >> 10, 394 mst.free >> 10, 395 mst.shared >> 10, 396 mst.cache_used >> 10, 397 mst.available >> 10 398 ); 399 } 400 Ok(()) 401 } 402 403 fn shell_cmd_help(&self, args: &Vec<String>) -> Result<(), CommandError> { 404 if args.len() == 0 { 405 for BuildInCmd(cmd) in BuildInCmd::BUILD_IN_CMD { 406 Help::shell_help(cmd) 407 } 408 return Ok(()); 409 } 410 return Err(CommandError::WrongArgumentCount(args.len())); 411 } 412 413 fn shell_cmd_export(&self, args: &Vec<String>) -> Result<(), CommandError> { 414 if args.len() == 1 { 415 let pair = args.get(0).unwrap().split('=').collect::<Vec<&str>>(); 416 417 if pair.len() == 2 && !pair.contains(&"") { 418 let name = pair.get(0).unwrap().to_string(); 419 let value = pair.get(1).unwrap().to_string(); 420 Env::insert(name, value); 421 return Ok(()); 422 } else { 423 return Err(CommandError::InvalidArgument(args.get(0).unwrap().clone())); 424 } 425 } 426 return Err(CommandError::WrongArgumentCount(args.len())); 427 } 428 429 fn shell_cmd_compgen(&self, _args: &Vec<String>) -> Result<(), CommandError> { 430 //TODO 431 Ok(()) 432 } 433 434 fn shell_cmd_complete(&self, _args: &Vec<String>) -> Result<(), CommandError> { 435 //TODO 436 Ok(()) 437 } 438 439 fn path_format(&self, path: &String) -> Result<String, CommandError> { 440 let mut abs_path = path.clone(); 441 if !path.starts_with('/') { 442 abs_path = format!("{}/{}", Env::current_dir(), path); 443 } 444 let path = Path::new(&abs_path).clean(); 445 let mut fmt_path = path.to_str().unwrap().to_string(); 446 let replacement = |_caps: ®ex::Captures| -> String { String::from("/") }; 447 let re = regex::Regex::new(r"\/{2,}").unwrap(); 448 fmt_path = re.replace_all(fmt_path.as_str(), replacement).to_string(); 449 return Ok(fmt_path); 450 } 451 452 #[allow(dead_code)] 453 fn is_file(&self, path_str: &String) -> Result<String, CommandError> { 454 match self.path_format(path_str) { 455 Ok(path_str) => { 456 let path = Path::new(&path_str); 457 if !path.is_file() { 458 return Err(CommandError::NotFile(path_str.clone())); 459 }; 460 Ok(path_str) 461 } 462 Err(_) => Err(CommandError::FileNotFound(path_str.clone())), 463 } 464 } 465 466 #[allow(dead_code)] 467 fn is_dir(&self, path_str: &String) -> Result<String, CommandError> { 468 match self.path_format(path_str) { 469 Ok(path_str) => { 470 let path = Path::new(&path_str); 471 if !path.is_dir() { 472 return Err(CommandError::NotDirectory(path_str.clone())); 473 }; 474 Ok(path_str) 475 } 476 Err(_) => Err(CommandError::DirectoryNotFound(path_str.clone())), 477 } 478 } 479 480 #[allow(dead_code)] 481 fn is_file_or_dir(&self, path_str: &String) -> Result<String, CommandError> { 482 match self.path_format(path_str) { 483 Ok(path_str) => Ok(path_str), 484 Err(_) => Err(CommandError::PathNotFound(path_str.clone())), 485 } 486 } 487 } 488