1 use help::Help; 2 use regex::{Captures, Regex}; 3 use std::io::Read; 4 use std::os::unix::ffi::OsStrExt; 5 use std::{ 6 format, 7 fs::{self, File, OpenOptions}, 8 io::Write, 9 path::Path, 10 print, println, 11 string::String, 12 vec::Vec, 13 }; 14 15 use crate::shell::Shell; 16 use crate::Env; 17 use crate::{special_keycode::*, ROOT_PATH}; 18 19 mod help; 20 21 enum CommandType { 22 InternalCommand(BuildInCmd), 23 ExternalCommand(String), 24 } 25 26 pub struct Command { 27 args: Vec<String>, 28 cmd_type: CommandType, 29 } 30 31 #[derive(Debug, PartialEq, Eq, Clone)] 32 pub enum CommandError { 33 CommandNotFound(String), 34 InvalidArgument(String), 35 WrongArgumentCount(usize), 36 EnvironmentVariableNotFound(String), 37 PathNotFound(String), 38 FileNotFound(String), 39 DirectoryNotFound(String), 40 NotDirectory(String), 41 NotFile(String), 42 } 43 44 impl CommandError { 45 pub fn handle(e: CommandError) { 46 match e { 47 CommandError::CommandNotFound(command) => { 48 println!("cannot find command: {}", command) 49 } 50 CommandError::InvalidArgument(argument) => { 51 println!("invalid argument: {}", argument) 52 } 53 CommandError::WrongArgumentCount(count) => { 54 println!("argument count incorrect: {}", count) 55 } 56 CommandError::EnvironmentVariableNotFound(env) => { 57 println!("environment variable not found: {}", env); 58 } 59 CommandError::PathNotFound(path) => { 60 println!("cannot found file or dirctory: {}", path) 61 } 62 CommandError::FileNotFound(path) => { 63 println!("cannot found file: {}", path) 64 } 65 CommandError::DirectoryNotFound(path) => { 66 println!("cannot found dirctory: {}", path) 67 } 68 CommandError::NotDirectory(path) => { 69 println!("path is not a dirctory: {}", path) 70 } 71 CommandError::NotFile(path) => { 72 println!("path is not a file: {}", path) 73 } 74 }; 75 } 76 } 77 78 impl Command { 79 fn new(name: String, args: Vec<String>) -> Result<Command, CommandError> { 80 for BuildInCmd(cmd) in BuildInCmd::BUILD_IN_CMD { 81 if name == *cmd { 82 return Ok(Command { 83 args, 84 cmd_type: CommandType::InternalCommand(BuildInCmd(cmd)), 85 }); 86 } 87 } 88 89 return Ok(Command { 90 args, 91 cmd_type: CommandType::ExternalCommand(name), 92 }); 93 } 94 95 fn from_string(str: String) -> Result<Command, CommandError> { 96 let regex: Regex = Regex::new(r#"([^\s'"]|("[^"]*"|'[^']*'))+"#).unwrap(); 97 let hay = str.clone(); 98 let mut iter = regex 99 .captures_iter(hay.as_str()) 100 .map(|c| String::from(c.get(0).unwrap().as_str())); 101 // let mut iter = str.split_ascii_whitespace(); 102 let name = iter.next().unwrap(); 103 let re: Regex = Regex::new(r"\$[\w_]+").unwrap(); 104 let replacement = |caps: &Captures| -> String { 105 match Env::get(&String::from(&caps[0][1..])) { 106 Some(value) => value, 107 None => String::from(&caps[0]), 108 } 109 }; 110 let mut args: Vec<String> = Vec::new(); 111 for arg in iter.collect::<Vec<String>>().iter() { 112 let arg = re.replace_all(arg.as_str(), &replacement).to_string(); 113 match re.captures(arg.as_str()) { 114 Some(caps) => { 115 return Err(CommandError::EnvironmentVariableNotFound(String::from( 116 caps.get(0).unwrap().as_str(), 117 ))) 118 } 119 None => args.push(arg), 120 } 121 } 122 Command::new(name, args) 123 } 124 125 pub fn from_strings(str: String) -> Vec<Command> { 126 str.split(';') 127 .filter_map(|s| match Command::from_string(String::from(s)) { 128 Ok(s) => Some(s), 129 Err(e) => { 130 CommandError::handle(e); 131 None 132 } 133 }) 134 .collect::<Vec<Command>>() 135 } 136 } 137 138 pub struct BuildInCmd(pub &'static str); 139 140 impl BuildInCmd { 141 pub const BUILD_IN_CMD: &[BuildInCmd] = &[ 142 BuildInCmd("cd"), 143 BuildInCmd("ls"), 144 BuildInCmd("cat"), 145 BuildInCmd("touch"), 146 BuildInCmd("mkdir"), 147 BuildInCmd("rm"), 148 BuildInCmd("rmdir"), 149 BuildInCmd("pwd"), 150 BuildInCmd("cp"), 151 BuildInCmd("exec"), 152 BuildInCmd("echo"), 153 BuildInCmd("reboot"), 154 BuildInCmd("free"), 155 BuildInCmd("kill"), 156 BuildInCmd("help"), 157 BuildInCmd("export"), 158 BuildInCmd("env"), 159 BuildInCmd("compgen"), 160 BuildInCmd("complete"), 161 ]; 162 } 163 164 impl Shell { 165 pub fn exec_internal_command( 166 &mut self, 167 cmd: &str, 168 args: &Vec<String>, 169 ) -> Result<(), CommandError> { 170 match cmd { 171 "cd" => self.shell_cmd_cd(args), 172 "ls" => self.shell_cmd_ls(args), 173 "cat" => self.shell_cmd_cat(args), 174 "touch" => self.shell_cmd_touch(args), 175 "mkdir" => self.shell_cmd_mkdir(args), 176 "rm" => self.shell_cmd_rm(args), 177 "rmdir" => self.shell_cmd_rmdir(args), 178 "pwd" => self.shell_cmd_pwd(args), 179 "cp" => self.shell_cmd_cp(args), 180 "exec" => self.shell_cmd_exec(args), 181 "echo" => self.shell_cmd_echo(args), 182 "reboot" => self.shell_cmd_reboot(args), 183 "free" => self.shell_cmd_free(args), 184 "kill" => self.shell_cmd_kill(args), 185 "help" => self.shell_cmd_help(args), 186 "export" => self.shell_cmd_export(args), 187 "env" => self.shell_cmd_env(args), 188 "compgen" => self.shell_cmd_compgen(args), 189 "complete" => self.shell_cmd_complete(args), 190 191 _ => Err(CommandError::CommandNotFound(String::from(cmd))), 192 } 193 } 194 195 pub fn exec_external_command(&mut self, path: String, args: &Vec<String>) { 196 let mut full_args = args.clone(); 197 full_args.insert(0, path.clone()); 198 self.shell_cmd_exec(&full_args).unwrap_or_else(|e| { 199 let err = match e { 200 CommandError::FileNotFound(_) => CommandError::CommandNotFound(path.clone()), 201 _ => e, 202 }; 203 CommandError::handle(err); 204 }) 205 } 206 207 pub fn exec_command(&mut self, command: &Command) { 208 match &command.cmd_type { 209 CommandType::ExternalCommand(path) => { 210 self.exec_external_command(path.to_string(), &command.args); 211 } 212 213 CommandType::InternalCommand(BuildInCmd(cmd)) => { 214 match self.exec_internal_command(cmd, &command.args) { 215 Ok(_) => {} 216 Err(e) => CommandError::handle(e), 217 } 218 if command.args.contains(&String::from("--help")) { 219 Help::shell_help(cmd); 220 } 221 } 222 } 223 } 224 225 fn shell_cmd_cd(&mut self, args: &Vec<String>) -> Result<(), CommandError> { 226 if args.len() == 0 { 227 self.set_current_dir(String::from(ROOT_PATH)); 228 return Ok(()); 229 } 230 if args.len() == 1 { 231 let path = self.to_absolute_path(args.get(0).unwrap()); 232 if Path::new(&path).is_dir() { 233 self.set_current_dir(String::from(path)); 234 return Ok(()); 235 } else { 236 return Err(CommandError::NotDirectory(path)); 237 } 238 } else { 239 return Err(CommandError::WrongArgumentCount(args.len())); 240 } 241 } 242 243 fn shell_cmd_ls(&self, args: &Vec<String>) -> Result<(), CommandError> { 244 let mut path = String::new(); 245 if args.len() == 0 { 246 path = self.current_dir(); 247 } 248 if args.len() == 1 { 249 path = self.to_absolute_path(args.get(0).unwrap()); 250 } 251 252 if path.is_empty() { 253 return Err(CommandError::WrongArgumentCount(args.len())); 254 } 255 256 let dir: fs::ReadDir; 257 match fs::read_dir(Path::new(&path)) { 258 Ok(readdir) => dir = readdir, 259 Err(_) => return Err(CommandError::InvalidArgument(path)), 260 } 261 for entry in dir { 262 let entry = entry.unwrap(); 263 if entry.file_type().unwrap().is_dir() { 264 crate::shell::Printer::print_color( 265 entry.file_name().as_bytes(), 266 0x000088ff, 267 0x00000000, 268 ); 269 print!(" "); 270 } else { 271 print!("{} ", entry.file_name().into_string().unwrap()); 272 } 273 } 274 print!("{}", String::from_utf8(vec![CR, LF]).unwrap()); 275 return Ok(()); 276 } 277 278 fn shell_cmd_cat(&self, args: &Vec<String>) -> Result<(), CommandError> { 279 if args.len() > 0 { 280 let path = self.to_absolute_path(args.get(0).unwrap()); 281 let mut buf: Vec<u8> = Vec::new(); 282 if !Path::new(&path).exists() { 283 return Err(CommandError::PathNotFound(path)); 284 } else if !Path::new(&path).is_file() { 285 return Err(CommandError::NotFile(path)); 286 } 287 288 File::open(path).unwrap().read_to_end(&mut buf).unwrap(); 289 if args.len() == 1 { 290 println!("{}", String::from_utf8(buf.clone()).unwrap()); 291 } 292 293 if args.len() == 3 { 294 let target_path = self.to_absolute_path(args.get(2).unwrap()); 295 if !Path::new(&target_path).is_file() { 296 return Err(CommandError::NotFile(target_path)); 297 } 298 299 if args[1] == ">" { 300 match OpenOptions::new().write(true).open(target_path) { 301 Ok(mut file) => { 302 file.write_all(&buf).unwrap(); 303 } 304 Err(e) => print!("{e}"), 305 } 306 } else if args[1] == ">>" { 307 match OpenOptions::new().append(true).open(target_path) { 308 Ok(mut file) => { 309 file.write_all(&buf).unwrap(); 310 } 311 Err(e) => print!("{e}"), 312 } 313 } 314 } 315 return Ok(()); 316 } 317 return Err(CommandError::WrongArgumentCount(args.len())); 318 } 319 320 fn shell_cmd_touch(&self, args: &Vec<String>) -> Result<(), CommandError> { 321 if args.len() == 1 { 322 let path = self.to_absolute_path(args.get(0).unwrap()); 323 match File::create(path) { 324 Ok(_) => {} 325 Err(e) => { 326 print!("{e}") 327 } 328 }; 329 return Ok(()); 330 } 331 return Err(CommandError::WrongArgumentCount(args.len())); 332 } 333 334 fn shell_cmd_mkdir(&self, args: &Vec<String>) -> Result<(), CommandError> { 335 if args.len() == 1 { 336 let path = self.to_absolute_path(args.get(0).unwrap()); 337 let dir = &path[0..path.rfind('/').unwrap_or(0)]; 338 if !Path::new(dir).is_dir() { 339 return Err(CommandError::DirectoryNotFound(dir.to_string())); 340 } 341 match fs::create_dir_all(path) { 342 Ok(_) => {} 343 Err(e) => { 344 print!("{e}") 345 } 346 } 347 return Ok(()); 348 } else { 349 return Err(CommandError::WrongArgumentCount(args.len())); 350 } 351 } 352 353 fn shell_cmd_rm(&self, args: &Vec<String>) -> Result<(), CommandError> { 354 if args.len() == 1 { 355 let path = self.to_absolute_path(args.get(0).unwrap()); 356 // match fs::remove_file(path) { 357 // Ok(_) => {} 358 // Err(e) => { 359 // print!("{e}") 360 // } 361 // } 362 if Path::new(&path).is_file() { 363 let path_cstr = std::ffi::CString::new(path).unwrap(); 364 unsafe { 365 // libc::unlinkat(0, path_cstr.as_ptr(), 0); 366 libc::syscall(libc::SYS_unlinkat, 0, path_cstr.as_ptr(), 0, 0, 0, 0); 367 } 368 return Ok(()); 369 } else { 370 return Err(CommandError::FileNotFound(path)); 371 } 372 } 373 return Err(CommandError::WrongArgumentCount(args.len())); 374 } 375 376 fn shell_cmd_rmdir(&self, args: &Vec<String>) -> Result<(), CommandError> { 377 if args.len() == 1 { 378 let path = self.to_absolute_path(args.get(0).unwrap()); 379 if Path::new(&path).is_dir() { 380 let path_cstr = std::ffi::CString::new(path).unwrap(); 381 unsafe { libc::unlinkat(0, path_cstr.as_ptr(), libc::AT_REMOVEDIR) }; 382 return Ok(()); 383 } else { 384 return Err(CommandError::DirectoryNotFound(path)); 385 } 386 } else { 387 return Err(CommandError::WrongArgumentCount(args.len())); 388 } 389 } 390 391 fn shell_cmd_pwd(&self, args: &Vec<String>) -> Result<(), CommandError> { 392 if args.len() == 0 { 393 println!("{}", self.current_dir()); 394 return Ok(()); 395 } else { 396 return Err(CommandError::WrongArgumentCount(args.len())); 397 } 398 } 399 400 fn shell_cmd_cp(&self, args: &Vec<String>) -> Result<(), CommandError> { 401 if args.len() == 2 { 402 let src_path = self.to_absolute_path(args.get(0).unwrap()); 403 let mut target_path = self.to_absolute_path(args.get(1).unwrap()); 404 if !Path::new(&src_path).is_file() { 405 return Err(CommandError::NotFile(src_path)); 406 }; 407 let mut src_file = File::open(&src_path).unwrap(); 408 let name = &src_path[src_path.rfind('/').unwrap_or(0)..]; 409 410 if !Path::new(&target_path).exists() { 411 return Err(CommandError::PathNotFound(target_path)); 412 } else if Path::new(&target_path).is_dir() { 413 target_path = format!("{}/{}", target_path, name); 414 } 415 let mut target_file = File::create(target_path).unwrap(); 416 let mut buf: Vec<u8> = Vec::new(); 417 src_file.read_to_end(&mut buf).unwrap(); 418 target_file.write_all(&buf).unwrap(); 419 return Ok(()); 420 } else { 421 return Err(CommandError::WrongArgumentCount(args.len())); 422 } 423 } 424 425 pub fn shell_cmd_exec(&self, args: &Vec<String>) -> Result<(), CommandError> { 426 let path = args.get(0).unwrap(); 427 let mut real_path = String::new(); 428 if !path.starts_with('/') { 429 let mut prefix_collection = Env::path(); 430 prefix_collection.insert(0, self.current_dir()); 431 for prefix in prefix_collection { 432 real_path = Self::path_format(&format!("{}/{}", prefix, path)); 433 if Path::new(&real_path).is_file() { 434 break; 435 } 436 } 437 } 438 439 if !Path::new(&real_path).is_file() { 440 return Err(CommandError::FileNotFound(path.clone())); 441 } 442 443 let pid: libc::pid_t = unsafe { 444 libc::syscall(libc::SYS_fork, 0, 0, 0, 0, 0, 0) 445 .try_into() 446 .unwrap() 447 }; 448 let mut retval = 0; 449 if pid == 0 { 450 let path_cstr = std::ffi::CString::new(real_path).unwrap(); 451 let mut argv: *const *const i8 = std::ptr::null(); 452 if args.len() > 1 { 453 let args_cstr = args 454 .iter() 455 .skip(1) 456 .map(|str| std::ffi::CString::new(str.as_str()).unwrap()) 457 .collect::<Vec<std::ffi::CString>>(); 458 let mut args_ptr = args_cstr 459 .iter() 460 .map(|c_str| c_str.as_ptr()) 461 .collect::<Vec<*const i8>>(); 462 args_ptr.push(std::ptr::null()); 463 argv = args_ptr.as_ptr(); 464 } 465 unsafe { 466 libc::execv(path_cstr.as_ptr(), argv); 467 } 468 } else { 469 if args.last().unwrap() != &"&" { 470 unsafe { libc::waitpid(pid, &mut retval as *mut i32, 0) }; 471 } else { 472 println!("[1] {}", pid); 473 } 474 } 475 return Ok(()); 476 } 477 478 fn shell_cmd_echo(&self, args: &Vec<String>) -> Result<(), CommandError> { 479 if args.len() > 0 { 480 let str = args.get(0).unwrap(); 481 if args.len() == 1 { 482 println!("{str}"); 483 } 484 485 if args.len() == 3 { 486 let target_path = self.to_absolute_path(args.get(2).unwrap()); 487 if args[1] == ">" { 488 match OpenOptions::new().write(true).open(target_path) { 489 Ok(mut file) => { 490 file.write_all(str.as_bytes()).unwrap(); 491 } 492 Err(e) => print!("{e}"), 493 } 494 } else if args[1] == ">>" { 495 match OpenOptions::new().append(true).open(target_path) { 496 Ok(mut file) => { 497 file.write_all(str.as_bytes()).unwrap(); 498 } 499 Err(e) => print!("{e}"), 500 } 501 } 502 } 503 return Ok(()); 504 } else { 505 return Err(CommandError::WrongArgumentCount(args.len())); 506 } 507 } 508 509 fn shell_cmd_reboot(&self, args: &Vec<String>) -> Result<(), CommandError> { 510 if args.len() == 0 { 511 unsafe { libc::syscall(libc::SYS_reboot, 0, 0, 0, 0, 0, 0) }; 512 return Ok(()); 513 } else { 514 return Err(CommandError::WrongArgumentCount(args.len())); 515 } 516 } 517 518 fn shell_cmd_free(&self, args: &Vec<String>) -> Result<(), CommandError> { 519 if args.len() == 1 && args.get(0).unwrap() != "-m" { 520 return Err(CommandError::InvalidArgument( 521 args.get(0).unwrap().to_string(), 522 )); 523 } 524 525 struct mstat_t { 526 total: u64, // 计算机的总内存数量大小 527 used: u64, // 已使用的内存大小 528 free: u64, // 空闲物理页所占的内存大小 529 shared: u64, // 共享的内存大小 530 cache_used: u64, // 位于slab缓冲区中的已使用的内存大小 531 cache_free: u64, // 位于slab缓冲区中的空闲的内存大小 532 available: u64, // 系统总空闲内存大小(包括kmalloc缓冲区) 533 }; 534 535 let mut mst = mstat_t { 536 total: 0, 537 used: 0, 538 free: 0, 539 shared: 0, 540 cache_used: 0, 541 cache_free: 0, 542 available: 0, 543 }; 544 545 let mut info_file = File::open("/proc/meminfo").unwrap(); 546 let mut buf: Vec<u8> = Vec::new(); 547 info_file.read_to_end(&mut buf).unwrap(); 548 let str = String::from_utf8(buf).unwrap(); 549 let info = str 550 .split(&['\n', '\t', ' ']) 551 .filter_map(|str| str.parse::<u64>().ok()) 552 .collect::<Vec<u64>>(); 553 mst.total = *info.get(0).unwrap(); 554 mst.free = *info.get(1).unwrap(); 555 mst.used = mst.total - mst.free; 556 557 print!("\ttotal\tused\tfree\tshared\tcache\tavailable\n"); 558 print!("Mem:\t"); 559 560 if args.len() == 0 { 561 print!( 562 "{}\t{}\t{}\t{}\t{}\t{}\t\n", 563 mst.total, mst.used, mst.free, mst.shared, mst.cache_used, mst.available 564 ); 565 } else { 566 print!( 567 "{}\t{}\t{}\t{}\t{}\t{}\t\n", 568 mst.total >> 10, 569 mst.used >> 10, 570 mst.free >> 10, 571 mst.shared >> 10, 572 mst.cache_used >> 10, 573 mst.available >> 10 574 ); 575 } 576 Ok(()) 577 } 578 579 fn shell_cmd_kill(&self, args: &Vec<String>) -> Result<(), CommandError> { 580 if args.len() == 1 { 581 let pid: i32; 582 match args.get(0).unwrap().parse::<i32>() { 583 Ok(x) => pid = x, 584 Err(_) => { 585 return Err(CommandError::InvalidArgument( 586 args.get(0).unwrap().to_string(), 587 )) 588 } 589 } 590 unsafe { 591 libc::kill(pid, libc::SIGKILL); 592 } 593 return Ok(()); 594 } else { 595 return Err(CommandError::WrongArgumentCount(args.len())); 596 } 597 } 598 599 fn shell_cmd_help(&self, args: &Vec<String>) -> Result<(), CommandError> { 600 if args.len() == 0 { 601 for BuildInCmd(cmd) in BuildInCmd::BUILD_IN_CMD { 602 Help::shell_help(cmd) 603 } 604 return Ok(()); 605 } 606 return Err(CommandError::WrongArgumentCount(args.len())); 607 } 608 609 fn shell_cmd_export(&self, args: &Vec<String>) -> Result<(), CommandError> { 610 if args.len() == 1 { 611 let pair = args.get(0).unwrap().split('=').collect::<Vec<&str>>(); 612 613 if pair.len() == 2 && !pair.contains(&"") { 614 let name = pair.get(0).unwrap().to_string(); 615 let value = pair.get(1).unwrap().to_string(); 616 Env::insert(name, value); 617 return Ok(()); 618 } else { 619 return Err(CommandError::InvalidArgument(args.get(0).unwrap().clone())); 620 } 621 } 622 return Err(CommandError::WrongArgumentCount(args.len())); 623 } 624 625 fn shell_cmd_env(&self, args: &Vec<String>) -> Result<(), CommandError> { 626 if args.len() == 0 { 627 let mut file = File::open("/etc/profile").unwrap(); 628 let mut buf: Vec<u8> = Vec::new(); 629 file.read_to_end(&mut buf).unwrap(); 630 println!("{}", String::from_utf8(buf).unwrap()); 631 return Ok(()); 632 } else { 633 return Err(CommandError::InvalidArgument(args.get(0).unwrap().clone())); 634 } 635 } 636 637 fn shell_cmd_compgen(&self, args: &Vec<String>) -> Result<(), CommandError> { 638 Ok(()) 639 } 640 641 fn shell_cmd_complete(&self, args: &Vec<String>) -> Result<(), CommandError> { 642 Ok(()) 643 } 644 645 fn to_absolute_path(&self, path: &String) -> String { 646 let mut fmt_path = Self::path_format(path); 647 if !path.starts_with('/') { 648 if self.current_dir() == "/" { 649 fmt_path = format!("/{}", fmt_path); 650 } else { 651 fmt_path = format!("{}/{}", self.current_dir(), fmt_path); 652 } 653 } 654 if fmt_path.ends_with('/') { 655 fmt_path.pop().unwrap(); 656 } 657 658 let re = regex::Regex::new(r"\/\.\/").unwrap(); 659 let replacement = |_caps: ®ex::Captures| -> String { String::from("/") }; 660 fmt_path = re.replace_all(fmt_path.as_str(), replacement).to_string(); 661 662 let re = regex::Regex::new(r"\/[^\/]+\/\.\.").unwrap(); 663 let replacement = |_caps: ®ex::Captures| -> String { String::from("/") }; 664 fmt_path = re.replace_all(fmt_path.as_str(), replacement).to_string(); 665 fmt_path = Self::path_format(&fmt_path); 666 return fmt_path; 667 } 668 669 fn path_format(path: &String) -> String { 670 let re = regex::Regex::new(r"\/{2,}").unwrap(); 671 let replacement = |_caps: ®ex::Captures| -> String { String::from("/") }; 672 let fmt_path = re.replace_all(path, replacement).to_string(); 673 return fmt_path; 674 } 675 } 676