From c65a6aa4d0f54ef069cee810e5f846fbd9ce31f7 Mon Sep 17 00:00:00 2001 From: Justine Date: Fri, 23 Dec 2022 17:35:20 +0100 Subject: [PATCH] Added hist number to prompt, Arrows working --- Cargo.lock | 9 +++- README.md | 102 ------------------------------------------- src/lib.rs | 51 +++++++++++++++------- src/shell/history.rs | 10 +++++ 4 files changed, 53 insertions(+), 119 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9ede201..5c56024 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -124,12 +124,13 @@ dependencies = [ ] [[package]] -name = "rshell" +name = "sqish" version = "0.1.0" dependencies = [ "dirs", "gethostname", "termion", + "unicode-segmentation", "users", ] @@ -182,6 +183,12 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" +[[package]] +name = "unicode-segmentation" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a" + [[package]] name = "users" version = "0.11.0" diff --git a/README.md b/README.md index a4f89b1..18b5abf 100644 --- a/README.md +++ b/README.md @@ -6,109 +6,7 @@ My starting point is [this article](https://www.joshmcguigan.com/blog/build-your * Make it a lib I can also add basic features: -* History * 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. - -# Done - -* A basic history system. Writes to ~/history.rshell -* Tests for this history system -* Added the ! function -* Order the file (modules...) => Ok, we doing a lib. - -# Todo -* Use termion to capture the TAB and autocomplete - -# Annotated process_line -```rust -fn process_line(input: String) -> bool { - - // must be peekable so we know when we are on the last command - let mut commands = input.trim().split(" | ").peekable(); - //Will be used later, in the case of pipes - let mut previous_command = None; - - //While have commands to process... - while let Some(command) = commands.next() { - //First command is divided... - let mut parts = command.trim().split_whitespace(); - //into a command... - let mut command = parts.next().unwrap(); - //and args - let args = parts; - - //builtins : exit and cd are special - match command { - "cd" => { - //If a dir is given, we use it by dereferencing - //otherwise it is / - let new_dir = args.peekable().peek().map_or("/", |x| *x); - let root = Path::new(new_dir); - if let Err(e) = env::set_current_dir(&root) { - eprintln!("{}", e); - } - - previous_command = None; - }, - "exit" => return true, - "history" => { - let stdout = Stdio::inherit(); - println!("Insert history here"); - }, - //not builtin: we process it - command => { - //If we are at the first command (aka previous_command is None)... - let stdin = previous_command.map_or( - //We inherit, meaning stdin gets the stream from its parent - //what I guess that means is that the child (our command) - //inherits the file descriptors (stdin, stdout, stderr) - //from the process of this very own rust program ! - Stdio::inherit(), - //otherwise, our input is the output from the last command - //(from converts a ChildStdOut to a Stdio) - |output: Child| Stdio::from(output.stdout.unwrap()) - ); - - let stdout = if commands.peek().is_some() { - // there is another command piped behind this one - // prepare to send output to the next command - Stdio::piped() - } else { - // there are no more commands piped behind this one - // send output to shell stdout - // the child inherits from the parent's file descriptor - Stdio::inherit() - }; - - //Run the command - let output = Command::new(command) - .args(args) - .stdin(stdin) - .stdout(stdout) - //spawn : execute the command as a child process, returning a handle to it - .spawn(); - - //Checking for errors - //If it matched, previous_command becomes the output of this to be used - //next loop - match output { - Ok(output) => { previous_command = Some(output); }, - Err(e) => { - previous_command = None; - eprintln!("{}", e); - }, - }; - } - } - } - - if let Some(mut final_command) = previous_command { - // block until the final command has finished - final_command.wait(); - } - return false; -} -``` diff --git a/src/lib.rs b/src/lib.rs index f4228d4..67a5f0d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -104,7 +104,7 @@ pub mod shell { return resultat; } - fn build_prompt() -> String { + fn build_prompt(curr_number: &i32) -> String { let user = String::from( get_user_by_uid(get_current_uid()) .unwrap() @@ -132,7 +132,9 @@ pub mod shell { .unwrap() ); - let prompt = String::from(format!("{}[{}@{} in {}]{} ", + let prompt = String::from(format!("{}{}{}[{}@{} in {}]{} ", + color::Fg(color::LightBlack), + curr_number, color::Fg(color::LightCyan), user, host, dir_last, color::Fg(color::Reset))); @@ -175,19 +177,21 @@ pub mod shell { //Nothing } else { //Command was empty, new line - return String::from(" "); + return String::from(""); } } - fn rewrite(current_cmd: String) -> String { + fn delete_current_cmd(current_cmd: String) -> String { //Deletes currently written command - + let mut stdout = stdout().into_raw_mode().unwrap(); - let nbr_of_chars = current_cmd.graphemes(true).count(); - while nbr_of_chars > 0 { + //write!(stdout, "{}", cursor::Save); + for c in current_cmd.graphemes(true) { write!(stdout, "\x1b[D").unwrap(); write!(stdout, "\x1b[K").unwrap(); } + stdout.flush(); + //write!(stdout, "{}", cursor::Restore); return String::new(); } @@ -196,9 +200,13 @@ pub mod shell { pub fn run_raw() { let stdin = stdin(); let mut stdout = stdout().into_raw_mode().unwrap(); - let mut prompt = build_prompt(); let mut mycommand = String::new(); + //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 = build_prompt(¤t_number); + //Initialize write!(stdout, "\r\n SquiShell (sqish)--- \r\n{}", prompt); stdout.flush(); @@ -215,6 +223,8 @@ pub mod shell { } Key::Char('\n') => { + current_number = get_curr_history_number(); + prompt = build_prompt(¤t_number); if (mycommand != String::from("\n")) && (mycommand != String::from("exit")) { let comm = replace_signs(&mycommand); let res = handle_input(&comm); @@ -223,7 +233,8 @@ pub mod shell { for line in res.split('\n') { write!(stdout, "\r\n{}", line); } - prompt = build_prompt(); + current_number = get_curr_history_number(); + prompt = build_prompt(¤t_number); write!(stdout, "{}", prompt).unwrap(); stdout.flush(); } else if mycommand == String::from("exit") { @@ -258,19 +269,27 @@ pub mod shell { }, Key::Up => { - mycommand = rewrite(mycommand); - let lastnumber = match get_history_number() { - Ok(e) => e, - Err(_) => continue, - }; - let lastcmd = match get_hist_from_number(&lastnumber) { + mycommand = delete_current_cmd(mycommand); + current_number -= 1; + let lastcmd = match get_hist_from_number(¤t_number) { + Some(c) => c, + None => continue + }; + mycommand = lastcmd.trim().to_string(); + //let prompt = build_prompt(); + write!(stdout, "{}", mycommand); + }, + + Key::Down => { + mycommand = delete_current_cmd(mycommand); + current_number += 1; + let lastcmd = match get_hist_from_number(¤t_number) { Some(c) => c, None => continue }; mycommand = lastcmd.trim().to_string(); //let prompt = build_prompt(); write!(stdout, "{}", mycommand); - mycommand = lastcmd; }, _ => (), diff --git a/src/shell/history.rs b/src/shell/history.rs index 0f26e45..8bd2fe3 100644 --- a/src/shell/history.rs +++ b/src/shell/history.rs @@ -34,6 +34,16 @@ pub fn get_history_number() -> Result { return Ok(number); } +pub fn get_curr_history_number() -> i32 { + //Returns CURRENT history number, AKA the one for the command we are typing + let mynumber = match get_history_number() { + Ok(n) => n + 1, + Err(e) => 1, + }; + + return mynumber; +} + pub fn get_history() -> Result { let filepath = dirs::home_dir().unwrap().join("history.sqish"); let file = File::open(filepath)?;