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 mut path = args.get(0).unwrap().clone(); 232 match self.is_dir(&path) { 233 Ok(str) => path = str, 234 Err(e) => return Err(e), 235 } 236 self.set_current_dir(&path); 237 return Ok(()); 238 } 239 return Err(CommandError::WrongArgumentCount(args.len())); 240 } 241 242 fn shell_cmd_ls(&self, args: &Vec<String>) -> Result<(), CommandError> { 243 let mut path = String::new(); 244 if args.len() == 0 { 245 path = self.current_dir(); 246 } 247 if args.len() == 1 { 248 path = args.get(0).unwrap().clone(); 249 match self.is_dir(&path) { 250 Ok(str) => path = str, 251 Err(e) => return Err(e), 252 } 253 } 254 255 if path.is_empty() { 256 return Err(CommandError::WrongArgumentCount(args.len())); 257 } 258 259 let dir: fs::ReadDir; 260 match fs::read_dir(Path::new(&path)) { 261 Ok(readdir) => dir = readdir, 262 Err(_) => return Err(CommandError::InvalidArgument(path)), 263 } 264 for entry in dir { 265 let entry = entry.unwrap(); 266 if entry.file_type().unwrap().is_dir() { 267 crate::shell::Printer::print_color( 268 entry.file_name().as_bytes(), 269 0x000088ff, 270 0x00000000, 271 ); 272 print!(" "); 273 } else { 274 print!("{} ", entry.file_name().into_string().unwrap()); 275 } 276 } 277 print!("{}", String::from_utf8(vec![CR, LF]).unwrap()); 278 return Ok(()); 279 } 280 281 fn shell_cmd_cat(&self, args: &Vec<String>) -> Result<(), CommandError> { 282 if args.len() > 0 { 283 let mut path = args.get(0).unwrap().clone(); 284 let mut buf: Vec<u8> = Vec::new(); 285 286 match self.is_file(&path) { 287 Ok(str) => path = str, 288 Err(e) => return Err(e), 289 } 290 291 File::open(path).unwrap().read_to_end(&mut buf).unwrap(); 292 if args.len() == 1 { 293 println!("{}", String::from_utf8(buf.clone()).unwrap()); 294 } 295 296 if args.len() == 3 { 297 let target_path = args.get(2).unwrap().clone(); 298 match self.is_file(&target_path) { 299 Ok(str) => path = str, 300 Err(e) => return Err(e), 301 } 302 303 if args[1] == ">" { 304 match OpenOptions::new().write(true).open(target_path) { 305 Ok(mut file) => { 306 file.write_all(&buf).unwrap(); 307 } 308 Err(e) => print!("{e}"), 309 } 310 } else if args[1] == ">>" { 311 match OpenOptions::new().append(true).open(target_path) { 312 Ok(mut file) => { 313 file.write_all(&buf).unwrap(); 314 } 315 Err(e) => print!("{e}"), 316 } 317 } 318 } 319 return Ok(()); 320 } 321 return Err(CommandError::WrongArgumentCount(args.len())); 322 } 323 324 fn shell_cmd_touch(&self, args: &Vec<String>) -> Result<(), CommandError> { 325 if args.len() == 1 { 326 let mut path = args.get(0).unwrap().clone(); 327 match self.is_file(&path) { 328 Ok(str) => path = str, 329 Err(e) => return Err(e), 330 } 331 File::open(path).unwrap(); 332 return Ok(()); 333 } 334 return Err(CommandError::WrongArgumentCount(args.len())); 335 } 336 337 fn shell_cmd_mkdir(&self, args: &Vec<String>) -> Result<(), CommandError> { 338 if args.len() == 1 { 339 let path = args.get(0).unwrap(); 340 match fs::create_dir_all(path) { 341 Ok(_) => {} 342 Err(e) => { 343 print!("{e}") 344 } 345 } 346 return Ok(()); 347 } else { 348 return Err(CommandError::WrongArgumentCount(args.len())); 349 } 350 } 351 352 fn shell_cmd_rm(&self, args: &Vec<String>) -> Result<(), CommandError> { 353 if args.len() == 1 { 354 let mut path = args.get(0).unwrap().clone(); 355 // match fs::remove_file(path) { 356 // Ok(_) => {} 357 // Err(e) => { 358 // print!("{e}") 359 // } 360 // } 361 362 match self.is_file(&path) { 363 Ok(str) => path = str, 364 Err(e) => return Err(e), 365 } 366 367 let path_cstr = std::ffi::CString::new(path.clone()).unwrap(); 368 unsafe { 369 libc::syscall(libc::SYS_unlinkat, 0, path_cstr.as_ptr(), 0, 0, 0, 0); 370 } 371 return Ok(()); 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 mut path = args.get(0).unwrap().clone(); 379 match self.is_dir(&path) { 380 Ok(str) => path = str, 381 Err(e) => return Err(e), 382 } 383 384 let path_cstr = std::ffi::CString::new(path).unwrap(); 385 unsafe { libc::unlinkat(0, path_cstr.as_ptr(), libc::AT_REMOVEDIR) }; 386 return Ok(()); 387 } 388 return Err(CommandError::WrongArgumentCount(args.len())); 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 } 396 return Err(CommandError::WrongArgumentCount(args.len())); 397 } 398 399 fn shell_cmd_cp(&self, args: &Vec<String>) -> Result<(), CommandError> { 400 if args.len() == 2 { 401 let mut src_path = args.get(0).unwrap().clone(); 402 let mut target_path = args.get(1).unwrap().clone(); 403 404 match self.is_file(&src_path) { 405 Ok(str) => src_path = str, 406 Err(e) => return Err(e), 407 } 408 409 match self.is_file_or_dir(&target_path) { 410 Ok(str) => target_path = str, 411 Err(e) => { 412 let prefix = &target_path[..target_path.rfind('/').unwrap_or(0)]; 413 if !Path::new(prefix).is_dir() { 414 return Err(e); 415 } 416 } 417 } 418 419 if Path::new(&src_path).is_dir() { 420 let mut name = &src_path[src_path.rfind('/').unwrap_or(0)..]; 421 target_path = format!("{}/{}", target_path, name); 422 } 423 424 let mut src_file = File::open(&src_path).unwrap(); 425 let mut target_file = File::create(target_path).unwrap(); 426 let mut buf: Vec<u8> = Vec::new(); 427 src_file.read_to_end(&mut buf).unwrap(); 428 target_file.write_all(&buf).unwrap(); 429 return Ok(()); 430 } 431 return Err(CommandError::WrongArgumentCount(args.len())); 432 } 433 434 pub fn shell_cmd_exec(&self, args: &Vec<String>) -> Result<(), CommandError> { 435 if args.len() <= 0 { 436 return Err(CommandError::WrongArgumentCount(args.len())); 437 } 438 let path = args.get(0).unwrap(); 439 let mut real_path = String::new(); 440 if !path.starts_with('/') && !path.starts_with('.') { 441 let mut prefix_collection = Env::path(); 442 prefix_collection.insert(0, self.current_dir()); 443 for prefix in prefix_collection { 444 real_path = format!("{}/{}", prefix, path); 445 if Path::new(&real_path).is_file() { 446 break; 447 } 448 } 449 } 450 451 match self.is_file(&real_path) { 452 Ok(str) => real_path = str, 453 Err(e) => return Err(e), 454 } 455 456 let pid: libc::pid_t = unsafe { 457 libc::syscall(libc::SYS_fork, 0, 0, 0, 0, 0, 0) 458 .try_into() 459 .unwrap() 460 }; 461 462 let mut retval = 0; 463 if pid == 0 { 464 let path_cstr = std::ffi::CString::new(real_path).unwrap(); 465 let mut argv: *const *const i8 = std::ptr::null(); 466 if args.len() > 1 { 467 let args_cstr = args 468 .iter() 469 .skip(1) 470 .map(|str| std::ffi::CString::new(str.as_str()).unwrap()) 471 .collect::<Vec<std::ffi::CString>>(); 472 let mut args_ptr = args_cstr 473 .iter() 474 .map(|c_str| c_str.as_ptr()) 475 .collect::<Vec<*const i8>>(); 476 args_ptr.push(std::ptr::null()); 477 argv = args_ptr.as_ptr(); 478 } 479 unsafe { 480 libc::execv(path_cstr.as_ptr(), argv); 481 } 482 } else { 483 if args.last().unwrap() != &"&" { 484 unsafe { libc::waitpid(pid, &mut retval as *mut i32, 0) }; 485 } else { 486 println!("[1] {}", pid); 487 } 488 } 489 return Ok(()); 490 } 491 492 fn shell_cmd_echo(&self, args: &Vec<String>) -> Result<(), CommandError> { 493 if args.len() > 0 { 494 let str = args.get(0).unwrap(); 495 if args.len() == 1 { 496 println!("{str}"); 497 } 498 499 if args.len() == 3 { 500 let mut target_path = args.get(2).unwrap().clone(); 501 match self.is_file(&target_path) { 502 Ok(str) => target_path = str, 503 Err(e) => return Err(e), 504 } 505 506 if args[1] == ">" { 507 match OpenOptions::new().write(true).open(target_path) { 508 Ok(mut file) => { 509 file.write_all(str.as_bytes()).unwrap(); 510 } 511 Err(e) => print!("{e}"), 512 } 513 } else if args[1] == ">>" { 514 match OpenOptions::new().append(true).open(target_path) { 515 Ok(mut file) => { 516 file.write_all(str.as_bytes()).unwrap(); 517 } 518 Err(e) => print!("{e}"), 519 } 520 } 521 } 522 return Ok(()); 523 } 524 return Err(CommandError::WrongArgumentCount(args.len())); 525 } 526 527 fn shell_cmd_reboot(&self, args: &Vec<String>) -> Result<(), CommandError> { 528 if args.len() == 0 { 529 unsafe { libc::syscall(libc::SYS_reboot, 0, 0, 0, 0, 0, 0) }; 530 return Ok(()); 531 } else { 532 return Err(CommandError::WrongArgumentCount(args.len())); 533 } 534 } 535 536 fn shell_cmd_free(&self, args: &Vec<String>) -> Result<(), CommandError> { 537 if args.len() == 1 && args.get(0).unwrap() != "-m" { 538 return Err(CommandError::InvalidArgument( 539 args.get(0).unwrap().to_string(), 540 )); 541 } 542 543 struct mstat_t { 544 total: u64, // 计算机的总内存数量大小 545 used: u64, // 已使用的内存大小 546 free: u64, // 空闲物理页所占的内存大小 547 shared: u64, // 共享的内存大小 548 cache_used: u64, // 位于slab缓冲区中的已使用的内存大小 549 cache_free: u64, // 位于slab缓冲区中的空闲的内存大小 550 available: u64, // 系统总空闲内存大小(包括kmalloc缓冲区) 551 }; 552 553 let mut mst = mstat_t { 554 total: 0, 555 used: 0, 556 free: 0, 557 shared: 0, 558 cache_used: 0, 559 cache_free: 0, 560 available: 0, 561 }; 562 563 let mut info_file = File::open("/proc/meminfo").unwrap(); 564 let mut buf: Vec<u8> = Vec::new(); 565 info_file.read_to_end(&mut buf).unwrap(); 566 let str = String::from_utf8(buf).unwrap(); 567 let info = str 568 .split(&['\n', '\t', ' ']) 569 .filter_map(|str| str.parse::<u64>().ok()) 570 .collect::<Vec<u64>>(); 571 mst.total = *info.get(0).unwrap(); 572 mst.free = *info.get(1).unwrap(); 573 mst.used = mst.total - mst.free; 574 575 print!("\ttotal\tused\tfree\tshared\tcache\tavailable\n"); 576 print!("Mem:\t"); 577 578 if args.len() == 0 { 579 print!( 580 "{}\t{}\t{}\t{}\t{}\t{}\t\n", 581 mst.total, mst.used, mst.free, mst.shared, mst.cache_used, mst.available 582 ); 583 } else { 584 print!( 585 "{}\t{}\t{}\t{}\t{}\t{}\t\n", 586 mst.total >> 10, 587 mst.used >> 10, 588 mst.free >> 10, 589 mst.shared >> 10, 590 mst.cache_used >> 10, 591 mst.available >> 10 592 ); 593 } 594 Ok(()) 595 } 596 597 fn shell_cmd_kill(&self, args: &Vec<String>) -> Result<(), CommandError> { 598 if args.len() == 1 { 599 let pid: i32; 600 match args.get(0).unwrap().parse::<i32>() { 601 Ok(x) => pid = x, 602 Err(_) => { 603 return Err(CommandError::InvalidArgument( 604 args.get(0).unwrap().to_string(), 605 )) 606 } 607 } 608 unsafe { 609 libc::kill(pid, libc::SIGKILL); 610 } 611 return Ok(()); 612 } else { 613 return Err(CommandError::WrongArgumentCount(args.len())); 614 } 615 } 616 617 fn shell_cmd_help(&self, args: &Vec<String>) -> Result<(), CommandError> { 618 if args.len() == 0 { 619 for BuildInCmd(cmd) in BuildInCmd::BUILD_IN_CMD { 620 Help::shell_help(cmd) 621 } 622 return Ok(()); 623 } 624 return Err(CommandError::WrongArgumentCount(args.len())); 625 } 626 627 fn shell_cmd_export(&self, args: &Vec<String>) -> Result<(), CommandError> { 628 if args.len() == 1 { 629 let pair = args.get(0).unwrap().split('=').collect::<Vec<&str>>(); 630 631 if pair.len() == 2 && !pair.contains(&"") { 632 let name = pair.get(0).unwrap().to_string(); 633 let value = pair.get(1).unwrap().to_string(); 634 Env::insert(name, value); 635 return Ok(()); 636 } else { 637 return Err(CommandError::InvalidArgument(args.get(0).unwrap().clone())); 638 } 639 } 640 return Err(CommandError::WrongArgumentCount(args.len())); 641 } 642 643 fn shell_cmd_env(&self, args: &Vec<String>) -> Result<(), CommandError> { 644 if args.len() == 0 { 645 let mut file = File::open("/etc/profile").unwrap(); 646 let mut buf: Vec<u8> = Vec::new(); 647 file.read_to_end(&mut buf).unwrap(); 648 println!("{}", String::from_utf8(buf).unwrap()); 649 return Ok(()); 650 } else { 651 return Err(CommandError::InvalidArgument(args.get(0).unwrap().clone())); 652 } 653 } 654 655 fn shell_cmd_compgen(&self, args: &Vec<String>) -> Result<(), CommandError> { 656 Ok(()) 657 } 658 659 fn shell_cmd_complete(&self, args: &Vec<String>) -> Result<(), CommandError> { 660 Ok(()) 661 } 662 663 fn path_format(&self, path: &String) -> Result<String, CommandError> { 664 let mut abs_path = path.clone(); 665 if !path.starts_with('/') { 666 abs_path = format!("{}/{}", self.current_dir(), abs_path); 667 } 668 if let Ok(path) = Path::new(path).canonicalize() { 669 let mut fmt_path = path.to_str().unwrap().to_string(); 670 let replacement = |_caps: ®ex::Captures| -> String { String::from("/") }; 671 let re = regex::Regex::new(r"\/{2,}").unwrap(); 672 fmt_path = re.replace_all(fmt_path.as_str(), replacement).to_string(); 673 return Ok(fmt_path); 674 } else { 675 return Err(CommandError::PathNotFound(path.clone())); 676 } 677 } 678 679 fn is_file(&self, path_str: &String) -> Result<String, CommandError> { 680 match self.path_format(path_str) { 681 Ok(path_str) => { 682 let path = Path::new(&path_str); 683 if !path.is_file() { 684 return Err(CommandError::NotFile(path_str.clone())); 685 }; 686 Ok(path_str) 687 } 688 Err(_) => Err(CommandError::FileNotFound(path_str.clone())), 689 } 690 } 691 692 fn is_dir(&self, path_str: &String) -> Result<String, CommandError> { 693 match self.path_format(path_str) { 694 Ok(path_str) => { 695 let path = Path::new(&path_str); 696 if !path.is_dir() { 697 return Err(CommandError::NotDirectory(path_str.clone())); 698 }; 699 Ok(path_str) 700 } 701 Err(_) => Err(CommandError::DirectoryNotFound(path_str.clone())), 702 } 703 } 704 705 fn is_file_or_dir(&self, path_str: &String) -> Result<String, CommandError> { 706 match self.path_format(path_str) { 707 Ok(path_str) => Ok(path_str), 708 Err(_) => Err(CommandError::PathNotFound(path_str.clone())), 709 } 710 } 711 } 712