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