132 lines
4.0 KiB
Rust
132 lines
4.0 KiB
Rust
use core::str::Split;
|
|
|
|
use crate::console::Console;
|
|
use alloc::{
|
|
format,
|
|
string::{String, ToString},
|
|
};
|
|
use victoriafalls::dir;
|
|
use voyageurs::listener::KeyboardHandler;
|
|
|
|
pub struct Terminal {
|
|
console: Console,
|
|
curr_cmd: String,
|
|
cwd: String,
|
|
row: u32,
|
|
}
|
|
|
|
impl KeyboardHandler for Terminal {
|
|
fn handle_char(&mut self, c: char) {
|
|
let mut should_execute = false;
|
|
match c {
|
|
'\x08' => {
|
|
let mut chars = self.curr_cmd.chars();
|
|
chars.next_back(); // Pop last char.
|
|
self.curr_cmd = chars.collect();
|
|
}
|
|
'\n' => should_execute = true,
|
|
_ => self.curr_cmd.push(c),
|
|
}
|
|
self.rewrite_command();
|
|
if should_execute {
|
|
self.execute_command();
|
|
self.rewrite_command();
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Terminal {
|
|
pub fn new(console: Console) -> Self {
|
|
let mut term = Self {
|
|
console,
|
|
curr_cmd: String::new(),
|
|
cwd: "/".to_string(),
|
|
row: 0,
|
|
};
|
|
term.rewrite_command();
|
|
term
|
|
}
|
|
|
|
fn rewrite_command(&mut self) {
|
|
self.console.write_char('>', self.row, 0);
|
|
|
|
let mut col = 1;
|
|
for c in self.curr_cmd.chars() {
|
|
self.console.write_char(c, self.row, col);
|
|
col += 1;
|
|
}
|
|
|
|
// Hacky way to properly backspace.
|
|
// FIXME: This won't work once we have line wrapping.
|
|
self.console.write_char(' ', self.row, col)
|
|
}
|
|
|
|
fn write_line(&mut self, line: &str) {
|
|
let mut col = 0;
|
|
for c in line.chars() {
|
|
self.console.write_char(c, self.row, col);
|
|
col += 1;
|
|
}
|
|
|
|
self.row += 1
|
|
}
|
|
|
|
fn execute_command(&mut self) {
|
|
self.row += 1;
|
|
|
|
let curr_cmd = self.curr_cmd.clone();
|
|
let mut tokens = curr_cmd.split(' ');
|
|
|
|
match tokens.next() {
|
|
None => {}
|
|
Some(command) => self.execute_command_parsed(command, tokens),
|
|
}
|
|
|
|
self.curr_cmd.clear()
|
|
}
|
|
|
|
fn execute_command_parsed(&mut self, cmd: &str, mut args: Split<'_, char>) {
|
|
// TODO: Check that no extraneous params are provided.
|
|
match cmd {
|
|
"help" => self.write_line("Available commands are 'pwd', 'ls', 'cd', and 'exec'"),
|
|
"pwd" => self.write_line(&self.cwd.clone()),
|
|
"cd" => match args.next() {
|
|
None => self.write_line("Specify a directory"),
|
|
Some(directory) => match dir::exists(directory) {
|
|
Ok(true) => self.cwd = directory.to_string(),
|
|
Ok(false) => self.write_line(&format!("Directory not found: {}", directory)),
|
|
Err(e) => self.write_line(&format!("Error stating directory {:?}", e)),
|
|
},
|
|
},
|
|
"ls" => {
|
|
let use_dir = match args.next() {
|
|
None => self.cwd.clone(),
|
|
Some(d) => d.to_string(),
|
|
};
|
|
match dir::ls(&use_dir) {
|
|
Err(e) => self.write_line(&format!("Error reading directory {:?}", e)),
|
|
Ok(files) => {
|
|
for file in files {
|
|
self.write_line(&file);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
"exec" => match args.next() {
|
|
None => self.write_line("Specify a program"),
|
|
Some(prog) => {
|
|
let file = victoriafalls::file::File::open(prog).expect("Failed to open file");
|
|
let proc_cap = mammoth::elf::spawn_process_from_elf(file.slice())
|
|
.expect("Failed to spawn process");
|
|
|
|
let exit_code = mammoth::syscall::process_wait(&proc_cap)
|
|
.expect("Failed to wait on process.");
|
|
|
|
self.write_line(&format!("Process exit code: {}", exit_code));
|
|
}
|
|
},
|
|
_ => self.write_line(&format!("Unrecognized command: {}", cmd)),
|
|
}
|
|
}
|
|
}
|