From 4581d4f0f6370056f87296ba97768743bf57d552 Mon Sep 17 00:00:00 2001 From: Justine Date: Wed, 28 Dec 2022 12:39:43 +0100 Subject: [PATCH] =?UTF-8?q?fl=C3=A8ches,=20backspace,=20insertion=20foncti?= =?UTF-8?q?onnent?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 10 +---- src/lib.rs | 124 ++++++++++++++++++++++++++++++++++++++++------------- 2 files changed, 96 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index 18b5abf..f0cfc27 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,6 @@ # Sqish Rust Shell. This is an attempt to create a simple shell in Rust, because why not. +!!! LOOK into the pty crate to handle pseudoterminals such as in top or ssh + My starting point is [this article](https://www.joshmcguigan.com/blog/build-your-own-shell-rust/) which is excellent. I will start with the final form of a shell given in the article and try to reformat it: -* Make it more easy to extend -* Make it a lib - -I can also add basic features: -* customizable prompt -* Colours ? - -Later, I can even make something useful out of it. For example, I could make it so that it is configurable via a config file. I could make it the default terminal via ssh for some user and make it so the terminal opens directly in a docker container, or chrooted or smthing, as a basic form of honeypot. diff --git a/src/lib.rs b/src/lib.rs index 13238cd..76a323f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,7 +8,6 @@ pub mod shell { use std::env; use std::str; use std::process::Stdio; - use std::num::Wrapping; use users::{get_user_by_uid, get_current_uid}; use termion::event::Key; use termion::input::TermRead; @@ -105,10 +104,7 @@ pub mod shell { return resultat; } - fn build_prompt(curr_number: &i32) -> (String, usize) { - let mut length: usize = 9; - - length += curr_number.to_string().len(); + fn build_prompt(curr_number: &i32) -> String { let user = String::from( get_user_by_uid(get_current_uid()) @@ -118,16 +114,12 @@ pub mod shell { .unwrap() ); - length += user.chars().count(); - let host = String::from( gethostname::gethostname() .to_str() .unwrap() ); - length += host.chars().count(); - let current_dir = String::from( env::current_dir() .unwrap() @@ -141,15 +133,13 @@ pub mod shell { .unwrap() ); - length += dir_last.chars().count(); - let prompt = String::from(format!("{}{}{}[{}@{} in {}]{} ", color::Fg(color::LightBlack), curr_number, color::Fg(color::LightCyan), user, host, dir_last, color::Fg(color::Reset))); - return (prompt, length); + return prompt; } fn replace_signs(line: &String) -> String { @@ -206,6 +196,28 @@ pub mod shell { return String::new(); } + fn get_cmd_curs_pos(command: &String) -> (usize, usize) { + let mut max_pos: usize = 0; + let mut current_pos: usize = 0; + + for _c in command.graphemes(true) { + max_pos += 1; + current_pos += 1; + } + + return (max_pos, current_pos); + } + + fn clear_line(max_pos: &usize) { + //clears currently written command from the screen + //...NOT from the variable ! + let mut stdout = stdout().into_raw_mode().unwrap(); + for i in 0..*max_pos { + write!(stdout, "{}", cursor::Left(1)); + write!(stdout, "\x1b[K").unwrap(); + } + } + pub fn run_raw() { @@ -216,7 +228,11 @@ pub mod shell { //Used in conjunction with Up arrow to go back in history //Resetted by pressing Enter let mut current_number = get_curr_history_number(); - let (mut prompt, mut pr_len) = build_prompt(¤t_number); + let mut prompt = build_prompt(¤t_number); + + //Used to track the location of the cursor inside the command + let mut current_pos: usize = 0; + let mut max_pos: usize = 0; //Initialize write!(stdout, "\r\n SquiShell (sqish)--- \r\n{}", prompt); @@ -235,7 +251,9 @@ pub mod shell { Key::Char('\n') => { current_number = get_curr_history_number(); - (prompt, pr_len) = build_prompt(¤t_number); + prompt = build_prompt(¤t_number); + current_pos = 0; + max_pos = 0; if (mycommand != String::from("\n")) && (mycommand != String::from("exit")) { let comm = replace_signs(&mycommand); let res = handle_input(&comm); @@ -245,7 +263,7 @@ pub mod shell { write!(stdout, "\r\n{}", line); } current_number = get_curr_history_number(); - (prompt, pr_len) = build_prompt(¤t_number); + prompt = build_prompt(¤t_number); write!(stdout, "{}", prompt).unwrap(); stdout.flush(); } else if mycommand == String::from("exit") { @@ -262,21 +280,63 @@ pub mod shell { break; }, + Key::Ctrl('c') => { + continue; + }, + + Key::Ctrl('t') => { + clear_line(&max_pos); + max_pos = 0; + current_pos = 0; + mycommand.clear(); + }, + + Key::Ctrl('a') => { + write!(stdout, "{}", cursor::Left(current_pos as u16)); + current_pos = 0; + }, + + Key::Ctrl('e') => { + let chars_left = max_pos - current_pos; + write!(stdout, "{}", cursor::Right(chars_left as u16)); + current_pos = max_pos; + }, + Key::Char(c) => { - mycommand.push(c); - write!(stdout, "{}", c).unwrap(); + //if at the end of the command... + if current_pos == max_pos { + mycommand.push(c); + current_pos += 1; + max_pos += 1; + write!(stdout, "{}", c).unwrap(); + } else if current_pos < max_pos { + //Inserting a char inside the command... + write!(stdout, "{}", cursor::Save); + mycommand.insert(current_pos, c); + let command_piece = &mycommand[current_pos..]; + write!(stdout, "{}", command_piece); + stdout.flush(); + write!(stdout, "{}", cursor::Restore); + write!(stdout, "{}", cursor::Right(1)); + max_pos += 1; + } + }, Key::Backspace => { - match mycommand.pop() { - Some(_) => {}, - None => continue, + if current_pos > 0 { + max_pos -= 1; + current_pos -= 1; + mycommand.remove(current_pos); + write!(stdout, "{}", cursor::Left(1)); + //Delete the end + write!(stdout, "\x1b[K").unwrap(); + write!(stdout, "{}", cursor::Save); + clear_line(&max_pos); + write!(stdout, "{}", mycommand); + write!(stdout, "{}", cursor::Restore); + stdout.flush(); } - //Move cursor left - write!(stdout, "\x1b[D").unwrap(); - //Delete one character there - write!(stdout, "\x1b[K").unwrap(); - stdout.flush(); }, Key::Up => { @@ -287,6 +347,7 @@ pub mod shell { None => continue }; mycommand = lastcmd.trim().to_string(); + (max_pos, current_pos) = get_cmd_curs_pos(&mycommand); //let prompt = build_prompt(); write!(stdout, "{}", mycommand); }, @@ -299,21 +360,24 @@ pub mod shell { None => continue }; mycommand = lastcmd.trim().to_string(); + (max_pos, current_pos) = get_cmd_curs_pos(&mycommand); //let prompt = build_prompt(); write!(stdout, "{}", mycommand); }, Key::Left => { - //La taille du prompt est incorrecte à cause des couleurs ! - let curs_pos = stdout.cursor_pos().unwrap().0; - if usize::from(curs_pos) > pr_len { + if current_pos > 0 { + current_pos -= 1; write!(stdout, "{}", cursor::Left(1)); stdout.flush(); - } + } }, Key::Right => { - print!("{}", cursor::Right(1)); + if current_pos < max_pos { + print!("{}", cursor::Right(1)); + current_pos += 1; + } } _ => (),