xref: /NovaShell/src/shell/mod.rs (revision 6537b36977cef705c78c1800d561b0d464bc0dc0)
1 use std::{
2     fs::{self, File, OpenOptions},
3     io::{self, BufRead, BufReader, Write},
4     print,
5     string::String,
6     vec::Vec,
7 };
8 
9 use crate::{Env, SpecialKeycode};
10 
11 use command::{BuildInCmd, Command};
12 
13 pub mod command;
14 
15 pub struct Shell {
16     history_commands: Vec<Vec<u8>>,
17     executed_commands: Vec<Vec<u8>>,
18     current_dir: String,
19 }
20 
21 impl Shell {
22     pub fn new() -> Shell {
23         let mut shell = Shell {
24             history_commands: Vec::new(),
25             executed_commands: Vec::new(),
26             current_dir: String::from("/"),
27         };
28         shell.read_commands();
29         shell
30     }
31 
32     pub fn current_dir(&self) -> String {
33         self.current_dir.clone()
34     }
35 
36     pub fn set_current_dir(&mut self, new_dir: &String) {
37         self.current_dir = new_dir.clone();
38         Env::insert(String::from("PWD"), self.current_dir());
39     }
40 
41     pub fn exec(&mut self) {
42         let mut buf: Vec<u8>;
43         loop {
44             buf = Vec::new();
45             buf.push(b' ');
46             self.history_commands.push(buf);
47             Printer::print_prompt(&self.current_dir);
48             if self.readline(0) == 0 {
49                 println!();
50                 break;
51             }
52             let command_bytes = self.history_commands.last().unwrap().clone();
53             if command_bytes.starts_with(&[b' ']) {
54                 self.history_commands.pop().unwrap();
55             } else {
56                 self.executed_commands.push(command_bytes.clone());
57             }
58             if !command_bytes.iter().all(|&byte| byte == b' ') {
59                 self.exec_command_in_bytes(&command_bytes);
60             }
61         }
62         self.write_commands();
63     }
64 
65     fn exec_command_in_bytes(&mut self, command_bytes: &Vec<u8>) {
66         let commands = Command::from_strings(String::from_utf8(command_bytes.clone()).unwrap());
67         commands
68             .iter()
69             .for_each(|command| self.exec_command(command));
70     }
71 
72     fn read_commands(&mut self) {
73         for line in BufReader::new(match File::open("history_commands.txt") {
74             Ok(file) => file,
75             Err(_) => File::create("history_commands.txt").unwrap(),
76         })
77         .lines()
78         {
79             match line {
80                 Ok(s) => self.history_commands.push(s.into_bytes()),
81                 Err(_) => {
82                     break;
83                 }
84             }
85         }
86     }
87 
88     fn write_commands(&self) {
89         let mut file = OpenOptions::new()
90             .append(true)
91             .open("history_commands.txt")
92             .unwrap();
93         for command_line in &self.executed_commands {
94             file.write_all(&command_line[..]).unwrap();
95             file.write_all(&[SpecialKeycode::LF.into()]).unwrap();
96         }
97     }
98 
99     fn read_char(byte: &mut u8) {
100         let mut c: libc::c_uchar = 0;
101         unsafe {
102             let p = &mut c as *mut libc::c_uchar as *mut libc::c_void;
103             libc::read(0, p, 1);
104         }
105         *byte = c;
106     }
107 
108     fn readline(&mut self, _fd: usize) -> usize {
109         let mut stdout = std::io::stdout();
110         let prompt: String = self.current_dir.clone();
111         let history_commands = &mut self.history_commands;
112         let len = history_commands.len() - 1;
113         let mut key: [u8; 1] = [0];
114         let mut command_index = len;
115         let mut buf = history_commands.get_mut(command_index).unwrap();
116         let mut cursor = 0;
117 
118         Printer::print_cursor(b' ');
119         stdout.flush().unwrap();
120         loop {
121             Self::read_char(&mut key[0]);
122             // if stdin.read(&mut key).ok() != Some(1) {
123             //     continue;
124             // }
125             if let Ok(special_key) = SpecialKeycode::try_from(key[0]) {
126                 match special_key {
127                     SpecialKeycode::FunctionKey => {
128                         Self::read_char(&mut key[0]);
129                         let special_key = SpecialKeycode::try_from(key[0]).unwrap();
130                         match special_key {
131                             SpecialKeycode::Up => {
132                                 if command_index > 0 {
133                                     command_index -= 1;
134                                 }
135                                 let old_length = buf.len();
136                                 buf = history_commands.get_mut(command_index).unwrap();
137                                 Printer::replace(&buf, old_length);
138                                 cursor = buf.len() - 1;
139                             }
140 
141                             SpecialKeycode::Down => {
142                                 if command_index < len {
143                                     command_index += 1;
144                                 }
145                                 let old_length = buf.len();
146                                 buf = history_commands.get_mut(command_index).unwrap();
147                                 Printer::replace(&buf, old_length);
148                                 cursor = buf.len() - 1;
149                             }
150 
151                             SpecialKeycode::Left => {
152                                 if cursor > 0 {
153                                     Printer::set_cursor(buf, cursor, cursor - 1);
154                                     cursor -= 1;
155                                 }
156                             }
157 
158                             SpecialKeycode::Right => {
159                                 if cursor < buf.len() - 1 {
160                                     Printer::set_cursor(buf, cursor, cursor + 1);
161                                     cursor += 1;
162                                 }
163                             }
164 
165                             SpecialKeycode::Home => {
166                                 Printer::set_cursor(buf, cursor, 0);
167                             }
168 
169                             SpecialKeycode::End => {
170                                 Printer::set_cursor(buf, cursor, buf.len());
171                             }
172 
173                             _ => {}
174                         }
175                     }
176 
177                     SpecialKeycode::LF | SpecialKeycode::CR => {
178                         Printer::set_cursor(buf, cursor, buf.len());
179                         println!();
180                         let mut command = buf.clone();
181                         buf = history_commands.get_mut(len).unwrap();
182                         buf.clear();
183                         buf.append(&mut command);
184 
185                         return 1;
186                     }
187 
188                     SpecialKeycode::BackSpace => {
189                         if cursor > 0 {
190                             Printer::delete_to_cursor(cursor, 1, buf);
191                             buf.remove(cursor - 1);
192                             cursor -= 1;
193                         }
194                     }
195 
196                     SpecialKeycode::Delete => {
197                         if cursor < buf.len() - 1 {
198                             Printer::delete(cursor, buf);
199                             buf.remove(cursor);
200                         }
201                     }
202 
203                     SpecialKeycode::Tab => {
204                         if buf.len() > 1 && buf[cursor - 1] != b' ' {
205                             let command: String =
206                                 String::from_utf8(buf[..cursor].to_vec()).unwrap();
207                             let mut command_frag =
208                                 command.split_ascii_whitespace().collect::<Vec<_>>();
209                             let incomplete_frag = command_frag.pop().unwrap();
210                             let mut incomplete_len: usize = incomplete_frag.len();
211                             let candidates = match command_frag.len() {
212                                 0 => Printer::complete_command(incomplete_frag),
213                                 1.. => {
214                                     if let Some(index) = incomplete_frag.rfind('/') {
215                                         incomplete_len = incomplete_frag.len() - index - 1;
216                                     } else {
217                                         incomplete_len = incomplete_frag.len();
218                                     }
219                                     Printer::complete_path(
220                                         format!("{}/{}", self.current_dir, incomplete_frag)
221                                             .as_str(),
222                                     )
223                                 }
224                                 _ => Vec::new(),
225                             };
226                             match candidates.len() {
227                                 1 => {
228                                     let complete_part = candidates[0][incomplete_len..].as_bytes();
229 
230                                     Printer::delete_from_index(cursor, buf.len());
231 
232                                     // stdout.write_all(complete_part).unwrap();
233                                     Printer::print(complete_part);
234 
235                                     Printer::print_cursor(buf[cursor]);
236                                     Printer::print(&buf[cursor + 1..]);
237 
238                                     buf.splice(cursor..cursor, complete_part.iter().cloned());
239                                     cursor += candidates[0].len() - incomplete_len;
240                                 }
241                                 2.. => {
242                                     Printer::delete_from_index(cursor, buf.len());
243                                     Printer::print(&buf[cursor..buf.len()]);
244                                     println!();
245                                     for candidate in candidates {
246                                         if candidate.ends_with('/') {
247                                             crate::shell::Printer::print_color(
248                                                 candidate.as_bytes(),
249                                                 0x000088ff,
250                                                 0x00000000,
251                                             );
252                                             print!("    ");
253                                         } else {
254                                             print!("{candidate}    ");
255                                         }
256                                     }
257                                     println!();
258                                     Printer::print_prompt(&prompt);
259                                     Printer::print(&buf[..buf.len() - 1]);
260                                     Printer::print_cursor(b' ');
261                                 }
262                                 _ => {}
263                             }
264                         }
265                     }
266 
267                     _ => todo!(),
268                 }
269             } else {
270                 match key[0] {
271                     1..=31 => {}
272                     c => {
273                         Printer::insert(cursor, &[c], buf);
274                         buf.insert(cursor, c);
275                         cursor += 1;
276                     }
277                 }
278             }
279             stdout.flush().unwrap();
280         }
281     }
282 }
283 
284 struct Printer;
285 
286 impl Printer {
287     fn print_prompt(current_dir: &String) {
288         io::stdout().flush().unwrap();
289         Self::print_color("[DragonOS]:".as_bytes(), 0x0000ff90, 0x00000000);
290         Self::print_color(current_dir.as_bytes(), 0x000088ff, 0x00000000);
291         print!("$ ");
292     }
293 
294     fn print_cursor(c: u8) {
295         Self::print_color(&[c], 0x00000000, 0x00ffffff);
296     }
297 
298     fn delete_from_index(index: usize, length: usize) {
299         for _i in 0..length - index {
300             Printer::print(&[
301                 SpecialKeycode::BackSpace.into(),
302                 b' ',
303                 SpecialKeycode::BackSpace.into(),
304             ]);
305         }
306     }
307 
308     fn insert(cursor: usize, bytes: &[u8], buf: &Vec<u8>) {
309         Printer::delete_from_index(cursor, buf.len());
310         Printer::print(bytes);
311         Printer::print_cursor(buf[cursor]);
312         Printer::print(&buf[cursor + 1..]);
313     }
314 
315     fn delete(cursor: usize, buf: &Vec<u8>) {
316         if cursor < buf.len() - 1 {
317             Printer::delete_from_index(cursor, buf.len());
318             Printer::print_cursor(buf[cursor + 1]);
319             Printer::print(&buf[cursor + 2..]);
320         }
321     }
322 
323     fn delete_to_cursor(cursor: usize, length: usize, buf: &Vec<u8>) {
324         if cursor > 0 {
325             Printer::delete_from_index(cursor - length, buf.len());
326             Printer::print_cursor(buf[cursor]);
327             Printer::print(&buf[cursor + 1..]);
328         }
329     }
330 
331     fn replace(bytes: &[u8], old_length: usize) {
332         Printer::delete_from_index(0, old_length);
333         Printer::print(&bytes[0..bytes.len() - 1]);
334         Printer::print_cursor(b' ');
335     }
336 
337     fn print(bytes: &[u8]) {
338         print!("{}", String::from_utf8(bytes.to_vec()).unwrap());
339     }
340 
341     fn print_color(bytes: &[u8], front_color: usize, background_color: usize) {
342         std::io::stdout().flush().unwrap();
343         let cstr = std::ffi::CString::new(bytes).unwrap();
344         unsafe {
345             dsc::syscall!(SYS_PUT_STRING, cstr.as_ptr(), front_color, background_color);
346         }
347     }
348 
349     fn set_cursor(buf: &mut Vec<u8>, old_index: usize, new_index: usize) {
350         if new_index < buf.len() {
351             let index = std::cmp::min(old_index, new_index);
352             Printer::delete_from_index(index, buf.len());
353             Printer::print(&buf[index..new_index]);
354             Printer::print_cursor(buf[new_index]);
355             Printer::print(&buf[new_index + 1..]);
356         } else {
357             Printer::delete_from_index(old_index, buf.len());
358             Printer::print(&buf[old_index..]);
359         }
360     }
361 
362     fn complete_command(command: &str) -> Vec<String> {
363         let mut candidates: Vec<String> = Vec::new();
364         for BuildInCmd(cmd) in BuildInCmd::BUILD_IN_CMD {
365             if cmd.starts_with(command) {
366                 candidates.push(String::from(*cmd));
367             }
368         }
369         candidates
370     }
371 
372     fn complete_path(path: &str) -> Vec<String> {
373         // println!("{}", path);
374         let mut candidates: Vec<String> = Vec::new();
375         let dir: &str;
376         let incomplete_name: &str;
377         if let Some(index) = path.rfind('/') {
378             dir = &path[..=index];
379             if index < path.len() {
380                 incomplete_name = &path[index + 1..];
381             } else {
382                 incomplete_name = "";
383             }
384         } else {
385             dir = ".";
386             incomplete_name = &path[..];
387         }
388         match fs::read_dir(dir) {
389             Ok(read_dir) => {
390                 if incomplete_name == "" {
391                     for entry in read_dir {
392                         let entry = entry.unwrap();
393                         let mut file_name = entry.file_name().into_string().unwrap();
394                         if entry.file_type().unwrap().is_dir() {
395                             file_name.push('/');
396                         }
397                         candidates.push(file_name);
398                     }
399                 } else {
400                     for entry in read_dir {
401                         let entry = entry.unwrap();
402                         let mut file_name = entry.file_name().into_string().unwrap();
403                         if file_name.starts_with(incomplete_name) {
404                             if entry.file_type().unwrap().is_dir() {
405                                 file_name.push('/');
406                             }
407                             candidates.push(file_name);
408                         }
409                     }
410                 }
411             }
412 
413             Err(_) => {}
414         }
415         return candidates;
416     }
417 }
418