2019-12-31 12:10:03 +00:00
|
|
|
use crate::{print, user, kernel};
|
2020-01-20 09:44:59 +00:00
|
|
|
use alloc::vec::Vec;
|
|
|
|
use alloc::string::String;
|
2019-12-28 17:08:11 +00:00
|
|
|
|
2020-01-01 08:07:09 +00:00
|
|
|
#[repr(u8)]
|
|
|
|
pub enum ExitCode {
|
|
|
|
CommandSuccessful = 0,
|
|
|
|
CommandUnknown = 1,
|
|
|
|
CommandError = 2,
|
|
|
|
ShellExit = 255,
|
|
|
|
}
|
|
|
|
|
2019-12-31 12:10:03 +00:00
|
|
|
pub struct Shell {
|
2020-01-20 09:44:59 +00:00
|
|
|
cmd: String,
|
|
|
|
prompt: String,
|
|
|
|
history: Vec<String>,
|
2019-12-31 12:10:03 +00:00
|
|
|
history_index: usize,
|
2019-12-28 17:08:11 +00:00
|
|
|
}
|
|
|
|
|
2019-12-31 12:10:03 +00:00
|
|
|
impl Shell {
|
|
|
|
pub fn new() -> Self {
|
|
|
|
Shell {
|
|
|
|
cmd: String::new(),
|
2020-01-07 09:48:09 +00:00
|
|
|
prompt: String::from("> "),
|
2020-01-20 09:44:59 +00:00
|
|
|
history: Vec::new(),
|
2019-12-31 12:10:03 +00:00
|
|
|
history_index: 0,
|
|
|
|
}
|
|
|
|
}
|
2019-12-29 13:43:36 +00:00
|
|
|
|
2020-01-01 08:07:09 +00:00
|
|
|
pub fn run(&mut self) -> user::shell::ExitCode {
|
2019-12-31 12:10:03 +00:00
|
|
|
self.print_prompt();
|
|
|
|
loop {
|
2020-01-14 22:54:46 +00:00
|
|
|
let (x, y) = kernel::vga::cursor_position();
|
2019-12-31 20:20:59 +00:00
|
|
|
let c = kernel::console::get_char();
|
|
|
|
match c {
|
2019-12-31 17:16:52 +00:00
|
|
|
'\0' => {
|
2019-12-31 12:10:03 +00:00
|
|
|
continue;
|
2019-12-29 09:34:08 +00:00
|
|
|
}
|
2020-01-07 09:48:09 +00:00
|
|
|
'\x03' => { // Ctrl C
|
2020-01-07 09:48:30 +00:00
|
|
|
if self.cmd.len() > 0 {
|
|
|
|
self.cmd.clear();
|
|
|
|
print!("\n");
|
|
|
|
self.print_prompt();
|
|
|
|
} else {
|
|
|
|
return ExitCode::CommandSuccessful;
|
|
|
|
}
|
2020-01-07 09:48:09 +00:00
|
|
|
},
|
|
|
|
'\n' => { // Newline
|
2019-12-31 12:10:03 +00:00
|
|
|
print!("\n");
|
2019-12-31 22:14:17 +00:00
|
|
|
if self.cmd.len() > 0 {
|
|
|
|
// Add or move command to history at the end
|
|
|
|
let cmd = self.cmd.clone();
|
2020-01-20 09:44:59 +00:00
|
|
|
if let Some(pos) = self.history.iter().position(|s| *s == *cmd) {
|
|
|
|
self.history.remove(pos);
|
2019-12-31 22:14:17 +00:00
|
|
|
}
|
2020-01-20 09:44:59 +00:00
|
|
|
self.history.push(cmd);
|
|
|
|
self.history_index = self.history.len();
|
2019-12-31 12:10:03 +00:00
|
|
|
|
|
|
|
let line = self.cmd.clone();
|
2020-01-01 08:07:09 +00:00
|
|
|
match self.exec(&line) {
|
|
|
|
ExitCode::CommandSuccessful => {},
|
|
|
|
ExitCode::ShellExit => { return ExitCode::CommandSuccessful },
|
|
|
|
_ => { print!("?\n") },
|
2019-12-31 12:10:03 +00:00
|
|
|
}
|
|
|
|
self.cmd.clear();
|
|
|
|
}
|
|
|
|
self.print_prompt();
|
|
|
|
},
|
2019-12-31 17:16:52 +00:00
|
|
|
'↑' => { // Arrow up
|
2019-12-31 12:10:03 +00:00
|
|
|
if self.history.len() > 0 {
|
|
|
|
if self.history_index > 0 {
|
|
|
|
self.history_index -= 1;
|
|
|
|
}
|
|
|
|
if let Some(cmd) = self.history.iter().nth(self.history_index) {
|
|
|
|
self.cmd = cmd.clone();
|
2020-01-07 09:48:09 +00:00
|
|
|
kernel::vga::clear_row();
|
|
|
|
print!("{}{}", self.prompt, self.cmd);
|
2019-12-31 12:10:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
2019-12-31 17:16:52 +00:00
|
|
|
'↓' => { // Arrow down
|
2019-12-31 12:10:03 +00:00
|
|
|
if self.history.len() > 0 {
|
|
|
|
if self.history_index < self.history.len() - 1 {
|
|
|
|
self.history_index += 1;
|
|
|
|
}
|
|
|
|
if let Some(cmd) = self.history.iter().nth(self.history_index) {
|
|
|
|
self.cmd = cmd.clone();
|
2020-01-07 09:48:09 +00:00
|
|
|
kernel::vga::clear_row();
|
|
|
|
print!("{}{}", self.prompt, self.cmd);
|
2019-12-31 12:10:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
2020-01-07 09:48:09 +00:00
|
|
|
'←' => { // Arrow left
|
|
|
|
if x > self.prompt.len() {
|
|
|
|
kernel::vga::set_cursor_position(x - 1, y);
|
|
|
|
kernel::vga::set_writer_position(x - 1, y);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
'→' => { // Arrow right
|
|
|
|
if x < self.prompt.len() + self.cmd.len() {
|
|
|
|
kernel::vga::set_cursor_position(x + 1, y);
|
|
|
|
kernel::vga::set_writer_position(x + 1, y);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
'\x08' => { // Backspace
|
|
|
|
let cmd = self.cmd.clone();
|
|
|
|
if cmd.len() > 0 && x > 0 {
|
|
|
|
let (before_cursor, mut after_cursor) = cmd.split_at(x - 1 - self.prompt.len());
|
|
|
|
if after_cursor.len() > 0 {
|
|
|
|
after_cursor = &after_cursor[1..];
|
|
|
|
}
|
|
|
|
self.cmd.clear();
|
2020-01-20 09:44:59 +00:00
|
|
|
self.cmd.push_str(before_cursor);
|
|
|
|
self.cmd.push_str(after_cursor);
|
2020-01-07 09:48:09 +00:00
|
|
|
kernel::vga::clear_row();
|
|
|
|
print!("{}{}", self.prompt, self.cmd);
|
|
|
|
kernel::vga::set_cursor_position(x - 1, y);
|
|
|
|
kernel::vga::set_writer_position(x - 1, y);
|
|
|
|
}
|
|
|
|
},
|
2019-12-31 17:16:52 +00:00
|
|
|
c => {
|
2020-01-23 20:30:53 +00:00
|
|
|
if c.is_ascii() && kernel::vga::is_printable(c as u8) {
|
2020-01-07 09:48:09 +00:00
|
|
|
let cmd = self.cmd.clone();
|
|
|
|
let (before_cursor, after_cursor) = cmd.split_at(x - self.prompt.len());
|
|
|
|
self.cmd.clear();
|
2020-01-20 09:44:59 +00:00
|
|
|
self.cmd.push_str(before_cursor);
|
|
|
|
self.cmd.push(c);
|
|
|
|
self.cmd.push_str(after_cursor);
|
2020-01-07 09:48:09 +00:00
|
|
|
kernel::vga::clear_row();
|
|
|
|
print!("{}{}", self.prompt, self.cmd);
|
|
|
|
kernel::vga::set_cursor_position(x + 1, y);
|
|
|
|
kernel::vga::set_writer_position(x + 1, y);
|
2019-12-31 12:10:03 +00:00
|
|
|
}
|
|
|
|
},
|
2019-12-29 09:34:08 +00:00
|
|
|
}
|
2019-12-31 12:10:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-20 09:44:59 +00:00
|
|
|
pub fn parse<'a>(&self, cmd: &'a str) -> Vec<&'a str> {
|
|
|
|
//let args: Vec<&str> = cmd.split_whitespace().collect();
|
|
|
|
let mut args: Vec<&str> = Vec::new();
|
2020-01-06 21:26:48 +00:00
|
|
|
let mut i = 0;
|
|
|
|
let mut n = cmd.len();
|
|
|
|
let mut is_quote = false;
|
|
|
|
|
|
|
|
for (j, c) in cmd.char_indices() {
|
2020-01-12 08:19:16 +00:00
|
|
|
if c == '#' && !is_quote {
|
|
|
|
n = j; // Discard comments
|
|
|
|
break;
|
|
|
|
} else if c == ' ' && !is_quote {
|
2020-01-06 21:26:48 +00:00
|
|
|
if i != j {
|
2020-01-20 09:44:59 +00:00
|
|
|
args.push(&cmd[i..j]);
|
2020-01-06 21:26:48 +00:00
|
|
|
}
|
|
|
|
i = j + 1;
|
|
|
|
} else if c == '"' {
|
|
|
|
is_quote = !is_quote;
|
|
|
|
if !is_quote {
|
2020-01-20 09:44:59 +00:00
|
|
|
args.push(&cmd[i..j]);
|
2020-01-06 21:26:48 +00:00
|
|
|
}
|
|
|
|
i = j + 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if i < n {
|
|
|
|
if is_quote {
|
|
|
|
n -= 1;
|
|
|
|
}
|
2020-01-20 09:44:59 +00:00
|
|
|
args.push(&cmd[i..n]);
|
2020-01-06 21:26:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
args
|
|
|
|
}
|
|
|
|
|
2020-01-01 08:07:09 +00:00
|
|
|
pub fn exec(&self, cmd: &str) -> ExitCode {
|
2020-01-06 21:26:48 +00:00
|
|
|
let args = self.parse(cmd);
|
|
|
|
|
2020-01-06 21:31:46 +00:00
|
|
|
if args.len() == 0 {
|
|
|
|
return ExitCode::CommandSuccessful;
|
|
|
|
}
|
|
|
|
|
2020-01-01 08:07:09 +00:00
|
|
|
match args[0] {
|
2020-01-17 18:55:09 +00:00
|
|
|
"a" | "alias" => ExitCode::CommandUnknown,
|
|
|
|
"b" => ExitCode::CommandUnknown,
|
|
|
|
"c" | "copy" => user::copy::main(&args),
|
|
|
|
"d" | "del" | "delete" => user::delete::main(&args),
|
|
|
|
"e" | "edit" => user::editor::main(&args),
|
|
|
|
"f" | "find" => ExitCode::CommandUnknown,
|
|
|
|
"g" | "go" => ExitCode::CommandUnknown,
|
2020-01-17 19:10:27 +00:00
|
|
|
"h" | "help" => user::help::main(&args),
|
2020-01-17 18:55:09 +00:00
|
|
|
"i" => ExitCode::CommandUnknown,
|
|
|
|
"j" | "jump" => ExitCode::CommandUnknown,
|
|
|
|
"k" | "kill" => ExitCode::CommandUnknown,
|
|
|
|
"l" | "list" => user::list::main(&args),
|
|
|
|
"m" | "move" => user::r#move::main(&args),
|
|
|
|
"n" => ExitCode::CommandUnknown,
|
|
|
|
"o" => ExitCode::CommandUnknown,
|
|
|
|
"p" | "print" => user::print::main(&args),
|
|
|
|
"q" | "quit" | "exit" => ExitCode::ShellExit,
|
|
|
|
"r" | "read" => user::read::main(&args),
|
|
|
|
"s" => ExitCode::CommandUnknown,
|
|
|
|
"t" => ExitCode::CommandUnknown,
|
|
|
|
"u" => ExitCode::CommandUnknown,
|
|
|
|
"v" => ExitCode::CommandUnknown,
|
|
|
|
"w" | "write" => user::write::main(&args),
|
|
|
|
"x" => ExitCode::CommandUnknown,
|
|
|
|
"y" => ExitCode::CommandUnknown,
|
|
|
|
"z" => ExitCode::CommandUnknown,
|
|
|
|
"shell" => user::shell::main(&args),
|
|
|
|
"sleep" => user::sleep::main(&args),
|
|
|
|
"clear" => user::clear::main(&args),
|
|
|
|
"login" => user::login::main(&args),
|
|
|
|
"base64" => user::base64::main(&args),
|
|
|
|
"halt" => user::halt::main(&args),
|
|
|
|
"hex" => user::hex::main(&args),
|
|
|
|
_ => ExitCode::CommandUnknown,
|
2020-01-01 08:07:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-31 12:10:03 +00:00
|
|
|
fn print_prompt(&self) {
|
2020-01-07 09:48:09 +00:00
|
|
|
print!("\n{}", self.prompt);
|
2019-12-28 17:08:11 +00:00
|
|
|
}
|
|
|
|
}
|
2020-01-01 08:07:24 +00:00
|
|
|
|
|
|
|
pub fn main(args: &[&str]) -> ExitCode {
|
|
|
|
let mut shell = Shell::new();
|
|
|
|
match args.len() {
|
|
|
|
1 => {
|
|
|
|
return shell.run();
|
|
|
|
},
|
|
|
|
2 => {
|
|
|
|
let pathname = args[1];
|
|
|
|
if let Some(file) = kernel::fs::File::open(pathname) {
|
2020-01-17 18:52:48 +00:00
|
|
|
for line in file.read_to_string().split("\n") {
|
2020-01-01 08:55:33 +00:00
|
|
|
if line.len() > 0 {
|
|
|
|
shell.exec(line);
|
|
|
|
}
|
2020-01-01 08:07:24 +00:00
|
|
|
}
|
|
|
|
ExitCode::CommandSuccessful
|
|
|
|
} else {
|
|
|
|
print!("File not found '{}'\n", pathname);
|
|
|
|
ExitCode::CommandError
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_ => {
|
|
|
|
ExitCode::CommandError
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|