mod command;
use std::io::prelude::*;
use std::println;
use std::string::ToString;
use crate::shell::command::{
CommandHistory, clear_line_and_redraw, handle_builtin_commands, print_prompt, run_cmd_bytes,
};
const LF: u8 = b'\n';
const CR: u8 = b'\r';
const DL: u8 = b'\x7f';
const BS: u8 = b'\x08';
const ESC: u8 = 0x1b;
const MAX_LINE_LEN: usize = 256;
pub fn console_init() {
let mut stdin = std::io::stdin();
let mut stdout = std::io::stdout();
let mut history = CommandHistory::new(100);
let mut buf = [0; MAX_LINE_LEN];
let mut cursor = 0; let mut line_len = 0;
enum InputState {
Normal,
Escape,
EscapeSeq,
}
let mut input_state = InputState::Normal;
println!("Welcome to AxVisor Shell!");
println!("Type 'help' to see available commands");
println!("Use UP/DOWN arrows to navigate command history");
#[cfg(not(feature = "fs"))]
println!("Note: Running with limited features (filesystem support disabled).");
println!();
print_prompt();
loop {
let mut temp_buf = [0u8; 1];
let ch = match stdin.read(&mut temp_buf) {
Ok(1) => temp_buf[0],
_ => {
continue;
}
};
match input_state {
InputState::Normal => {
match ch {
CR | LF => {
println!();
if line_len > 0 {
let cmd_str = std::str::from_utf8(&buf[..line_len]).unwrap_or("");
history.add_command(cmd_str.to_string());
if !handle_builtin_commands(cmd_str) {
run_cmd_bytes(&buf[..line_len]);
}
buf[..line_len].fill(0);
cursor = 0;
line_len = 0;
}
print_prompt();
}
BS | DL => {
if cursor > 0 {
for i in cursor..line_len {
buf[i - 1] = buf[i];
}
cursor -= 1;
line_len -= 1;
if line_len < buf.len() {
buf[line_len] = 0;
}
let current_content =
std::str::from_utf8(&buf[..line_len]).unwrap_or("");
#[cfg(feature = "fs")]
let prompt = format!("axvisor:{}$ ", &std::env::current_dir().unwrap());
#[cfg(not(feature = "fs"))]
let prompt = "axvisor:$ ".to_string();
clear_line_and_redraw(&mut stdout, &prompt, current_content, cursor);
}
}
ESC => {
input_state = InputState::Escape;
}
0..=31 => {
}
c => {
if line_len < MAX_LINE_LEN - 1 {
for i in (cursor..line_len).rev() {
buf[i + 1] = buf[i];
}
buf[cursor] = c;
cursor += 1;
line_len += 1;
let current_content =
std::str::from_utf8(&buf[..line_len]).unwrap_or("");
#[cfg(feature = "fs")]
let prompt = format!("axvisor:{}$ ", &std::env::current_dir().unwrap());
#[cfg(not(feature = "fs"))]
let prompt = "axvisor:$ ".to_string();
clear_line_and_redraw(&mut stdout, &prompt, current_content, cursor);
}
}
}
}
InputState::Escape => match ch {
b'[' => {
input_state = InputState::EscapeSeq;
}
_ => {
input_state = InputState::Normal;
}
},
InputState::EscapeSeq => {
match ch {
b'A' => {
if let Some(prev_cmd) = history.previous() {
buf[..line_len].fill(0);
let cmd_bytes = prev_cmd.as_bytes();
let copy_len = cmd_bytes.len().min(MAX_LINE_LEN - 1);
buf[..copy_len].copy_from_slice(&cmd_bytes[..copy_len]);
cursor = copy_len;
line_len = copy_len;
#[cfg(feature = "fs")]
let prompt = format!("axvisor:{}$ ", &std::env::current_dir().unwrap());
#[cfg(not(feature = "fs"))]
let prompt = "axvisor:$ ".to_string();
clear_line_and_redraw(&mut stdout, &prompt, prev_cmd, cursor);
}
input_state = InputState::Normal;
}
b'B' => {
match history.next() {
Some(next_cmd) => {
buf[..line_len].fill(0);
let cmd_bytes = next_cmd.as_bytes();
let copy_len = cmd_bytes.len().min(MAX_LINE_LEN - 1);
buf[..copy_len].copy_from_slice(&cmd_bytes[..copy_len]);
cursor = copy_len;
line_len = copy_len;
#[cfg(feature = "fs")]
let prompt =
format!("axvisor:{}$ ", &std::env::current_dir().unwrap());
#[cfg(not(feature = "fs"))]
let prompt = "axvisor:$ ".to_string();
clear_line_and_redraw(&mut stdout, &prompt, next_cmd, cursor);
}
None => {
buf[..line_len].fill(0);
cursor = 0;
line_len = 0;
#[cfg(feature = "fs")]
let prompt =
format!("axvisor:{}$ ", &std::env::current_dir().unwrap());
#[cfg(not(feature = "fs"))]
let prompt = "axvisor:$ ".to_string();
clear_line_and_redraw(&mut stdout, &prompt, "", cursor);
}
}
input_state = InputState::Normal;
}
b'C' => {
if cursor < line_len {
cursor += 1;
stdout.write_all(b"\x1b[C").ok();
stdout.flush().ok();
}
input_state = InputState::Normal;
}
b'D' => {
if cursor > 0 {
cursor -= 1;
stdout.write_all(b"\x1b[D").ok();
stdout.flush().ok();
}
input_state = InputState::Normal;
}
b'3' => {
input_state = InputState::Normal;
}
_ => {
input_state = InputState::Normal;
}
}
}
}
}
}