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