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::{Env, SpecialKeycode}; 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 println!(); 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(&[SpecialKeycode::LF.into()]).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 let Ok(special_key) = SpecialKeycode::try_from(key[0]) { 128 match special_key { 129 SpecialKeycode::FunctionKey => { 130 Self::read_char(&mut key[0]); 131 let special_key = SpecialKeycode::try_from(key[0]).unwrap(); 132 match special_key { 133 SpecialKeycode::Up => { 134 if command_index > 0 { 135 command_index -= 1; 136 } 137 let old_length = buf.len(); 138 buf = history_commands.get_mut(command_index).unwrap(); 139 Printer::replace(&buf, old_length); 140 cursor = buf.len() - 1; 141 } 142 143 SpecialKeycode::Down => { 144 if command_index < len { 145 command_index += 1; 146 } 147 let old_length = buf.len(); 148 buf = history_commands.get_mut(command_index).unwrap(); 149 Printer::replace(&buf, old_length); 150 cursor = buf.len() - 1; 151 } 152 153 SpecialKeycode::Left => { 154 if cursor > 0 { 155 Printer::set_cursor(buf, cursor, cursor - 1); 156 cursor -= 1; 157 } 158 } 159 160 SpecialKeycode::Right => { 161 if cursor < buf.len() - 1 { 162 Printer::set_cursor(buf, cursor, cursor + 1); 163 cursor += 1; 164 } 165 } 166 167 SpecialKeycode::Home => { 168 Printer::set_cursor(buf, cursor, 0); 169 } 170 171 SpecialKeycode::End => { 172 Printer::set_cursor(buf, cursor, buf.len()); 173 } 174 175 _ => {} 176 } 177 } 178 179 SpecialKeycode::LF | SpecialKeycode::CR => { 180 if cursor > 0 { 181 Printer::set_cursor(buf, cursor, buf.len()); 182 println!(); 183 let mut command = buf.clone(); 184 buf = history_commands.get_mut(len).unwrap(); 185 buf.clear(); 186 buf.append(&mut command); 187 188 return 1; 189 } 190 } 191 192 SpecialKeycode::BackSpace => { 193 if cursor > 0 { 194 Printer::delete_to_cursor(cursor, 1, buf); 195 buf.remove(cursor - 1); 196 cursor -= 1; 197 } 198 } 199 200 SpecialKeycode::Delete => { 201 if cursor < buf.len() - 1 { 202 Printer::delete(cursor, buf); 203 buf.remove(cursor); 204 } 205 } 206 207 SpecialKeycode::Tab => { 208 if buf.len() > 1 && buf[cursor - 1] != b' ' { 209 let command: String = 210 String::from_utf8(buf[..cursor].to_vec()).unwrap(); 211 let mut command_frag = 212 command.split_ascii_whitespace().collect::<Vec<_>>(); 213 let incomplete_frag = command_frag.pop().unwrap(); 214 let mut incomplete_len: usize = incomplete_frag.len(); 215 let candidates = match command_frag.len() { 216 0 => Printer::complete_command(incomplete_frag), 217 1.. => { 218 if let Some(index) = incomplete_frag.rfind('/') { 219 incomplete_len = incomplete_frag.len() - index - 1; 220 } else { 221 incomplete_len = incomplete_frag.len(); 222 } 223 Printer::complete_path(incomplete_frag) 224 } 225 _ => Vec::new(), 226 }; 227 match candidates.len() { 228 1 => { 229 let complete_part = candidates[0][incomplete_len..].as_bytes(); 230 231 Printer::delete_from_index(cursor, buf.len()); 232 233 // stdout.write_all(complete_part).unwrap(); 234 Printer::print(complete_part); 235 236 Printer::print_cursor(buf[cursor]); 237 Printer::print(&buf[cursor + 1..]); 238 239 buf.splice(cursor..cursor, complete_part.iter().cloned()); 240 cursor += candidates[0].len() - incomplete_len; 241 } 242 2.. => { 243 Printer::delete_from_index(cursor, buf.len()); 244 Printer::print(&buf[cursor..buf.len()]); 245 println!(); 246 for candidate in candidates { 247 print!("{candidate} "); 248 } 249 println!(); 250 Printer::print_prompt(&prompt); 251 Printer::print(&buf[..buf.len() - 1]); 252 Printer::print_cursor(b' '); 253 } 254 _ => {} 255 } 256 } 257 } 258 259 _ => todo!(), 260 } 261 } else { 262 match key[0] { 263 1..=31 => {} 264 c => { 265 Printer::insert(cursor, &[c], buf); 266 buf.insert(cursor, c); 267 cursor += 1; 268 } 269 } 270 } 271 stdout.flush().unwrap(); 272 } 273 } 274 } 275 276 struct Printer; 277 278 impl Printer { 279 fn print_prompt(current_dir: &String) { 280 io::stdout().flush().unwrap(); 281 Self::print_color("[DragonOS]:".as_bytes(), 0x0000ff90, 0x00000000); 282 Self::print_color(current_dir.as_bytes(), 0x000088ff, 0x00000000); 283 print!("$ "); 284 } 285 286 fn print_cursor(c: u8) { 287 Self::print_color(&[c], 0x00000000, 0x00ffffff); 288 } 289 290 fn delete_from_index(index: usize, length: usize) { 291 for _i in 0..length - index { 292 Printer::print(&[ 293 SpecialKeycode::BackSpace.into(), 294 b' ', 295 SpecialKeycode::BackSpace.into(), 296 ]); 297 } 298 } 299 300 fn insert(cursor: usize, bytes: &[u8], buf: &Vec<u8>) { 301 Printer::delete_from_index(cursor, buf.len()); 302 Printer::print(bytes); 303 Printer::print_cursor(buf[cursor]); 304 Printer::print(&buf[cursor + 1..]); 305 } 306 307 fn delete(cursor: usize, buf: &Vec<u8>) { 308 if cursor < buf.len() - 1 { 309 Printer::delete_from_index(cursor, buf.len()); 310 Printer::print_cursor(buf[cursor + 1]); 311 Printer::print(&buf[cursor + 2..]); 312 } 313 } 314 315 fn delete_to_cursor(cursor: usize, length: usize, buf: &Vec<u8>) { 316 if cursor > 0 { 317 Printer::delete_from_index(cursor - length, buf.len()); 318 Printer::print_cursor(buf[cursor]); 319 Printer::print(&buf[cursor + 1..]); 320 } 321 } 322 323 fn replace(bytes: &[u8], old_length: usize) { 324 Printer::delete_from_index(0, old_length); 325 Printer::print(&bytes[0..bytes.len() - 1]); 326 Printer::print_cursor(b' '); 327 } 328 329 fn print(bytes: &[u8]) { 330 print!("{}", String::from_utf8(bytes.to_vec()).unwrap()); 331 } 332 333 fn print_color(bytes: &[u8], front_color: usize, background_color: usize) { 334 std::io::stdout().flush().unwrap(); 335 let cstr = std::ffi::CString::new(bytes).unwrap(); 336 unsafe { 337 dsc::syscall!(SYS_PUT_STRING, cstr.as_ptr(), front_color, background_color); 338 } 339 } 340 341 fn set_cursor(buf: &mut Vec<u8>, old_index: usize, new_index: usize) { 342 if new_index < buf.len() { 343 let index = std::cmp::min(old_index, new_index); 344 Printer::delete_from_index(index, buf.len()); 345 Printer::print(&buf[index..new_index]); 346 Printer::print_cursor(buf[new_index]); 347 Printer::print(&buf[new_index + 1..]); 348 } else { 349 Printer::delete_from_index(old_index, buf.len()); 350 Printer::print(&buf[old_index..]); 351 } 352 } 353 354 fn complete_command(command: &str) -> Vec<String> { 355 let mut candidates: Vec<String> = Vec::new(); 356 for BuildInCmd(cmd) in BuildInCmd::BUILD_IN_CMD { 357 if cmd.starts_with(command) { 358 candidates.push(String::from(*cmd)); 359 } 360 } 361 candidates 362 } 363 364 fn complete_path(path: &str) -> Vec<String> { 365 let mut candidates: Vec<String> = Vec::new(); 366 let dir: &str; 367 let incomplete_name: &str; 368 if let Some(index) = path.rfind('/') { 369 dir = &path[..=index]; 370 if index < path.len() { 371 incomplete_name = &path[index + 1..]; 372 } else { 373 incomplete_name = ""; 374 } 375 } else { 376 dir = "."; 377 incomplete_name = &path[..]; 378 } 379 match fs::read_dir(dir) { 380 Ok(read_dir) => { 381 if incomplete_name == "" { 382 for entry in read_dir { 383 let entry = entry.unwrap(); 384 let mut file_name = entry.file_name().into_string().unwrap(); 385 if entry.file_type().unwrap().is_dir() { 386 file_name.push('/'); 387 } 388 candidates.push(file_name); 389 } 390 } else { 391 for entry in read_dir { 392 let entry = entry.unwrap(); 393 let mut file_name = entry.file_name().into_string().unwrap(); 394 if file_name.starts_with(incomplete_name) { 395 if entry.file_type().unwrap().is_dir() { 396 file_name.push('/'); 397 } 398 candidates.push(file_name); 399 } 400 } 401 } 402 } 403 404 Err(_) => {} 405 } 406 return candidates; 407 } 408 } 409