xref: /NovaShell/src/shell/command/mod.rs (revision 6f50094a7e1f5a323ffdd8ef4be4c22bba777501)
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: &regex::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: &regex::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: &regex::Captures| -> String { String::from("/") };
672         let fmt_path = re.replace_all(path, replacement).to_string();
673         return fmt_path;
674     }
675 }
676