xref: /NovaShell/src/shell/mod.rs (revision 732e87253615edb7d991be2f18a7c5518a49498e)
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(incomplete_frag)
220                                 }
221                                 _ => Vec::new(),
222                             };
223                             match candidates.len() {
224                                 1 => {
225                                     let complete_part = candidates[0][incomplete_len..].as_bytes();
226 
227                                     Printer::delete_from_index(cursor, buf.len());
228 
229                                     // stdout.write_all(complete_part).unwrap();
230                                     Printer::print(complete_part);
231 
232                                     Printer::print_cursor(buf[cursor]);
233                                     Printer::print(&buf[cursor + 1..]);
234 
235                                     buf.splice(cursor..cursor, complete_part.iter().cloned());
236                                     cursor += candidates[0].len() - incomplete_len;
237                                 }
238                                 2.. => {
239                                     Printer::delete_from_index(cursor, buf.len());
240                                     Printer::print(&buf[cursor..buf.len()]);
241                                     println!();
242                                     for candidate in candidates {
243                                         print!("{candidate}    ");
244                                     }
245                                     println!();
246                                     Printer::print_prompt(&prompt);
247                                     Printer::print(&buf[..buf.len() - 1]);
248                                     Printer::print_cursor(b' ');
249                                 }
250                                 _ => {}
251                             }
252                         }
253                     }
254 
255                     _ => todo!(),
256                 }
257             } else {
258                 match key[0] {
259                     1..=31 => {}
260                     c => {
261                         Printer::insert(cursor, &[c], buf);
262                         buf.insert(cursor, c);
263                         cursor += 1;
264                     }
265                 }
266             }
267             stdout.flush().unwrap();
268         }
269     }
270 }
271 
272 struct Printer;
273 
274 impl Printer {
275     fn print_prompt(current_dir: &String) {
276         io::stdout().flush().unwrap();
277         Self::print_color("[DragonOS]:".as_bytes(), 0x0000ff90, 0x00000000);
278         Self::print_color(current_dir.as_bytes(), 0x000088ff, 0x00000000);
279         print!("$ ");
280     }
281 
282     fn print_cursor(c: u8) {
283         Self::print_color(&[c], 0x00000000, 0x00ffffff);
284     }
285 
286     fn delete_from_index(index: usize, length: usize) {
287         for _i in 0..length - index {
288             Printer::print(&[
289                 SpecialKeycode::BackSpace.into(),
290                 b' ',
291                 SpecialKeycode::BackSpace.into(),
292             ]);
293         }
294     }
295 
296     fn insert(cursor: usize, bytes: &[u8], buf: &Vec<u8>) {
297         Printer::delete_from_index(cursor, buf.len());
298         Printer::print(bytes);
299         Printer::print_cursor(buf[cursor]);
300         Printer::print(&buf[cursor + 1..]);
301     }
302 
303     fn delete(cursor: usize, buf: &Vec<u8>) {
304         if cursor < buf.len() - 1 {
305             Printer::delete_from_index(cursor, buf.len());
306             Printer::print_cursor(buf[cursor + 1]);
307             Printer::print(&buf[cursor + 2..]);
308         }
309     }
310 
311     fn delete_to_cursor(cursor: usize, length: usize, buf: &Vec<u8>) {
312         if cursor > 0 {
313             Printer::delete_from_index(cursor - length, buf.len());
314             Printer::print_cursor(buf[cursor]);
315             Printer::print(&buf[cursor + 1..]);
316         }
317     }
318 
319     fn replace(bytes: &[u8], old_length: usize) {
320         Printer::delete_from_index(0, old_length);
321         Printer::print(&bytes[0..bytes.len() - 1]);
322         Printer::print_cursor(b' ');
323     }
324 
325     fn print(bytes: &[u8]) {
326         print!("{}", String::from_utf8(bytes.to_vec()).unwrap());
327     }
328 
329     fn print_color(bytes: &[u8], front_color: usize, background_color: usize) {
330         std::io::stdout().flush().unwrap();
331         let cstr = std::ffi::CString::new(bytes).unwrap();
332         unsafe {
333             dsc::syscall!(SYS_PUT_STRING, cstr.as_ptr(), front_color, background_color);
334         }
335     }
336 
337     fn set_cursor(buf: &mut Vec<u8>, old_index: usize, new_index: usize) {
338         if new_index < buf.len() {
339             let index = std::cmp::min(old_index, new_index);
340             Printer::delete_from_index(index, buf.len());
341             Printer::print(&buf[index..new_index]);
342             Printer::print_cursor(buf[new_index]);
343             Printer::print(&buf[new_index + 1..]);
344         } else {
345             Printer::delete_from_index(old_index, buf.len());
346             Printer::print(&buf[old_index..]);
347         }
348     }
349 
350     fn complete_command(command: &str) -> Vec<String> {
351         let mut candidates: Vec<String> = Vec::new();
352         for BuildInCmd(cmd) in BuildInCmd::BUILD_IN_CMD {
353             if cmd.starts_with(command) {
354                 candidates.push(String::from(*cmd));
355             }
356         }
357         candidates
358     }
359 
360     fn complete_path(path: &str) -> Vec<String> {
361         let mut candidates: Vec<String> = Vec::new();
362         let dir: &str;
363         let incomplete_name: &str;
364         if let Some(index) = path.rfind('/') {
365             dir = &path[..=index];
366             if index < path.len() {
367                 incomplete_name = &path[index + 1..];
368             } else {
369                 incomplete_name = "";
370             }
371         } else {
372             dir = ".";
373             incomplete_name = &path[..];
374         }
375         match fs::read_dir(dir) {
376             Ok(read_dir) => {
377                 if incomplete_name == "" {
378                     for entry in read_dir {
379                         let entry = entry.unwrap();
380                         let mut file_name = entry.file_name().into_string().unwrap();
381                         if entry.file_type().unwrap().is_dir() {
382                             file_name.push('/');
383                         }
384                         candidates.push(file_name);
385                     }
386                 } else {
387                     for entry in read_dir {
388                         let entry = entry.unwrap();
389                         let mut file_name = entry.file_name().into_string().unwrap();
390                         if file_name.starts_with(incomplete_name) {
391                             if entry.file_type().unwrap().is_dir() {
392                                 file_name.push('/');
393                             }
394                             candidates.push(file_name);
395                         }
396                     }
397                 }
398             }
399 
400             Err(_) => {}
401         }
402         return candidates;
403     }
404 }
405