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