diff --git a/Cargo.lock b/Cargo.lock index f5c90b8..c958fdc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,15 +2,6 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "libc" -version = "0.2.175" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" - [[package]] name = "rush" version = "0.1.0" -dependencies = [ - "libc", -] diff --git a/Cargo.toml b/Cargo.toml index 849701d..39e279a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,3 @@ name = "rush" version = "0.1.0" edition = "2024" -default-run = "rush" - -[dependencies] -libc = "0.2.175" diff --git a/src/bin/keycode.rs b/src/bin/keycode.rs deleted file mode 100644 index b488c82..0000000 --- a/src/bin/keycode.rs +++ /dev/null @@ -1,47 +0,0 @@ -use std::io::{stdin, stdout, Read, Write}; - -fn enable_raw_mode() -> libc::termios { - unsafe { - let fd = libc::STDIN_FILENO; - let mut termios = std::mem::zeroed(); - libc::tcgetattr(fd, &mut termios); - - let mut raw = termios; - // Input modes: no break, no CR to NL, no parity check, no strip char, - // no start/stop output control. - raw.c_iflag &= !(libc::BRKINT | libc::ICRNL | libc::INPCK | libc::ISTRIP | libc::IXON); - // Output modes: disable post processing - raw.c_oflag &= !(libc::OPOST); - // Control modes: set 8 bit chars - raw.c_cflag |= libc::CS8; - // Local modes: disable echo, canonical, extended functions, and signals - raw.c_lflag &= !(libc::ECHO | libc::ICANON | libc::IEXTEN | libc::ISIG); - // Control chars: set read timeout - raw.c_cc[libc::VMIN] = 1; // minimum number of bytes before read returns - raw.c_cc[libc::VTIME] = 0; // no timeout - - libc::tcsetattr(fd, libc::TCSAFLUSH, &raw); - - termios // return original so we can restore later - } -} - -fn disable_raw_mode(orig: &libc::termios) { - unsafe { - libc::tcsetattr(libc::STDIN_FILENO, libc::TCSAFLUSH, orig); - } -} - -fn main() { - let orig = enable_raw_mode(); - let mut buffer = [0u8; 1]; - - loop { - stdin().read_exact(&mut buffer).unwrap(); - if buffer[0] == 3 {break;} - - print!("{:?}\r\n", buffer); - stdout().flush().unwrap(); - } - disable_raw_mode(&orig); -} \ No newline at end of file diff --git a/src/data.rs b/src/data.rs deleted file mode 100644 index 0cf8566..0000000 --- a/src/data.rs +++ /dev/null @@ -1,117 +0,0 @@ -use std::{env, fs::{File, OpenOptions}, io::{BufRead, ErrorKind, Write}, path::PathBuf}; - -#[derive(Debug)] -pub struct Data { - prev_path: PathBuf, - curr_path: PathBuf, - - history: Vec, - hist_file: String, - hist_pos: usize, - hist_partial: String -} - -impl Default for Data { - fn default() -> Self { - let prev_path = PathBuf::new(); - let curr_path = env::current_dir().unwrap(); - let history: Vec = Vec::new(); - let hist_file = String::new(); - let hist_pos = 0; - let hist_partial = String::new(); - - Self { - prev_path, - curr_path, - - history, - hist_file, - hist_pos, - hist_partial - } - } -} - -impl Data { - pub fn set_path(&mut self, path: PathBuf) { - self.prev_path = self.curr_path.clone(); - self.curr_path = path; - } - - pub fn get_current_path(&self) -> PathBuf { - self.curr_path.clone() - } - - pub fn get_previous_path(&self) -> PathBuf { - self.prev_path.clone() - } - - pub fn add_to_hist(&mut self, command: String) { - self.history.push(command); - self.hist_pos += 1; - self.save_history(); - } - - pub fn save_command(&mut self, command: String) { - self.hist_partial = command; - } - - pub fn get_prev_hist_item(&mut self) -> Option { - if self.history.is_empty() { return None; } - if self.hist_pos <= self.history.len() && self.hist_pos > 0 { self.hist_pos -= 1; Some(self.history[self.hist_pos].clone()) } - else { None } - } - - pub fn get_next_hist_item(&mut self) -> Option { - if self.hist_pos < self.history.len() { self.hist_pos += 1; } - if self.hist_pos < self.history.len() { Some(self.history[self.hist_pos].clone()) } - else if self.hist_pos == self.history.len() { Some(self.hist_partial.clone()) } - else { None } - } - - pub fn at_hist_end(&self) -> bool { - self.hist_pos+1 == self.history.len() - } - - pub fn set_hist_file(&mut self, hist_file: &str) { - self.hist_file = hist_file.to_string(); - } - - pub fn save_history(&self) { - let mut hist_file = match OpenOptions::new().create(true).write(true).open(&self.hist_file) { - Ok(file) => file, - Err(e) => { - eprint!("\r\n{e}"); - return - } - }; - for line in &self.history { - write!(hist_file, "{line}\n").unwrap(); - } - } - - pub fn load_history(&mut self) { - let hist_file = match File::open(&self.hist_file) { - Ok(file) => file, - Err(e) => { - if e.kind() == ErrorKind::NotFound {eprint!("\r\nHistory file not found, creating")} - else { eprint!("\r\n{e}"); } - return - } - }; - let reader = std::io::BufReader::new(hist_file); - - for line in reader.lines() { - match line { - Ok(line) => { - self.history.push(line.trim().to_string()); - self.hist_pos += 1; - }, - Err(e) => { - eprint!("\r\n{e}"); - return - } - } - } - } -} \ No newline at end of file diff --git a/src/key.rs b/src/key.rs deleted file mode 100644 index e953fae..0000000 --- a/src/key.rs +++ /dev/null @@ -1,40 +0,0 @@ -use std::io::{stdin, Read}; - -#[derive(PartialEq)] -pub enum Key { - Char(char), - Enter, - Backspace, - Escape, - ArrowUp, - ArrowDown, - ArrowRight, - ArrowLeft, - #[allow(dead_code)] - Unknown(Vec) -} - -impl From for Key { - fn from(byte: u8) -> Self { - match byte { - b'\n' | b'\r' => Key::Enter, - 8 | 127 => Key::Backspace, - 27 => { - let mut buf = [0u8; 2]; - if stdin().read_exact(&mut buf).is_err() { - return Key::Escape; - } - - match buf { - [b'[', b'A'] => Key::ArrowUp, - [b'[', b'B'] => Key::ArrowDown, - [b'[', b'C'] => Key::ArrowRight, - [b'[', b'D'] => Key::ArrowLeft, - _ => Key::Unknown(vec![27, buf[0], buf[1]]) - } - }, - char if char.is_ascii() => Key::Char(byte as char), - byte => Key::Unknown(vec![byte]) - } - } -} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index c45dc2e..8a55e80 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,176 +1,3 @@ -use std::{ - env, - io::{self, stdin, stdout, Read, Stdin, Stdout, Write}, - process::Command, - str::SplitWhitespace, -}; - -mod raw; -use raw::{enable_raw_mode, disable_raw_mode}; - -mod key; -use key::Key; - -mod data; -use data::Data; - -fn print_prompt(pre_input: &str, data: &mut Data, newline: bool) -> io::Result<()> { - if newline { - print!("\r\n{}\r\n> {}", data.get_current_path().display(), pre_input); - } else { - print!("\r> {}", pre_input); - } - stdout().flush()?; - - Ok(()) -} - -fn flush_prompt(flush_count: usize) -> io::Result<()> { - print!("\r> {}", " ".repeat(flush_count)); - print!("\r> {}", "\x08".repeat(flush_count)); - Ok(()) -} - -fn handle_input(stdin: &mut Stdin, stdout: &mut Stdout, data: &mut Data) -> io::Result { - let mut input = String::new(); - let mut buffer = [0u8; 1]; - - loop { - stdin.read_exact(&mut buffer)?; - let key = Key::from(buffer[0]); - - match key { - Key::Enter => { - data.add_to_hist(input.clone()); - write!(stdout, "\r\n")?; - break; - } - Key::Backspace => { - if !input.is_empty() { - input.pop(); - write!(stdout, "\x08 \x08")?; - stdout.flush()?; - } - } - Key::Escape => continue, - Key::ArrowUp => { - if let Some(hist_command) = data.get_prev_hist_item() { - if data.at_hist_end() { data.save_command(input.clone()); } - flush_prompt(input.len())?; - input = hist_command.clone(); - print_prompt(&input, data, false)?; - } - }, - Key::ArrowDown => { - if let Some(hist_command) = data.get_next_hist_item() { - flush_prompt(input.len())?; - input = hist_command.clone(); - print_prompt(&input, data, false)?; - } - }, - Key::ArrowLeft => {}, - Key::ArrowRight => {}, - Key::Char(c) => { - input.push(c); - write!(stdout, "{c}")?; - stdout.flush()?; - } - Key::Unknown(_) => {} - } - } - - Ok(input) -} - -fn parse_input(mut input: I) -> (::Item, I) -where - I: Iterator, -{ - let command = input.next().unwrap(); - let args = input; - - (command, args) -} - -fn run_command(command: &str, args: SplitWhitespace, data: &mut Data) -> Result { - match command { - "exit" => return Ok(1), - "cd" => { - let path = args.peekable().peek().map_or("~", |dir| *dir); - - let mut new_path = data.get_current_path(); - - if path.chars().nth(0).unwrap() == '/' { new_path.push("/"); } - for subpath in path.split("/") { - match subpath { - "-" => { - new_path = data.get_previous_path(); - break; - } - "~" => { - new_path.push(env::home_dir().unwrap()); - } - ".." => { - new_path.pop(); - } - "." => {} - path => new_path.push(path), - } - } - - if let Err(e) = env::set_current_dir(&new_path) { - Err(e.to_string()) - } else { - data.set_path(new_path); - Ok(0) - } - } - command => { - let child = Command::new(command).args(args).spawn(); - - match child { - Ok(mut child) => { child.wait().unwrap(); Ok(0) } - Err(e) => { return Err(e.to_string()); } - } - } - } -} - -fn rush_loop(data: &mut Data, orig: libc::termios) -> io::Result<()> { - let mut stdin = stdin(); - let mut stdout = stdout(); - - loop { - print_prompt("", data, true)?; - - let input = handle_input(&mut stdin, &mut stdout, data)?; - let input = input.trim(); - if input.is_empty() { continue; } - let input = input.split_whitespace(); - let (command, args) = parse_input(input); - - disable_raw_mode(&orig); - match run_command(command, args, data) { - Ok(status) => match status { - 1 => return Ok(()), - _ => {} - }, - Err(e) => eprint!("{e}\r\n") - } - enable_raw_mode(); - } -} - fn main() { - let mut data = Data::default(); - let home = env::home_dir().unwrap(); - let home_str = home.into_os_string().into_string().unwrap(); - data.set_hist_file(&format!("{}/.rush_history", home_str)); - data.load_history(); - - let orig = enable_raw_mode(); - - rush_loop(&mut data, orig).unwrap(); - - disable_raw_mode(&orig); -} + +} \ No newline at end of file diff --git a/src/raw.rs b/src/raw.rs deleted file mode 100644 index 7e23eaf..0000000 --- a/src/raw.rs +++ /dev/null @@ -1,24 +0,0 @@ -pub fn enable_raw_mode() -> libc::termios { - unsafe { - let fd = libc::STDIN_FILENO; - let mut termios = std::mem::zeroed(); - libc::tcgetattr(fd, &mut termios); - - let mut raw = termios; - raw.c_iflag &= !(libc::BRKINT | libc::ICRNL | libc::INPCK | libc::ISTRIP | libc::IXON); - raw.c_oflag &= !(libc::OPOST); - raw.c_cflag |= libc::CS8; - raw.c_lflag &= !(libc::ECHO | libc::ICANON | libc::IEXTEN | libc::ISIG); - raw.c_cc[libc::VMIN] = 1; - raw.c_cc[libc::VTIME] = 0; - libc::tcsetattr(fd, libc::TCSAFLUSH, &raw); - - termios - } -} - -pub fn disable_raw_mode(orig: &libc::termios) { - unsafe { - libc::tcsetattr(libc::STDIN_FILENO, libc::TCSAFLUSH, orig); - } -} \ No newline at end of file