xref: /NovaShell/src/shell/command/mod.rs (revision 5b85994156defaf7a9d43e4b747af603d8d1ffc4)
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: &regex::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