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