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