flèches, backspace, insertion fonctionnent
This commit is contained in:
10
README.md
10
README.md
@ -1,12 +1,6 @@
|
|||||||
# Sqish
|
# Sqish
|
||||||
Rust Shell. This is an attempt to create a simple shell in Rust, because why not.
|
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:
|
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.
|
|
||||||
|
124
src/lib.rs
124
src/lib.rs
@ -8,7 +8,6 @@ pub mod shell {
|
|||||||
use std::env;
|
use std::env;
|
||||||
use std::str;
|
use std::str;
|
||||||
use std::process::Stdio;
|
use std::process::Stdio;
|
||||||
use std::num::Wrapping;
|
|
||||||
use users::{get_user_by_uid, get_current_uid};
|
use users::{get_user_by_uid, get_current_uid};
|
||||||
use termion::event::Key;
|
use termion::event::Key;
|
||||||
use termion::input::TermRead;
|
use termion::input::TermRead;
|
||||||
@ -105,10 +104,7 @@ pub mod shell {
|
|||||||
return resultat;
|
return resultat;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_prompt(curr_number: &i32) -> (String, usize) {
|
fn build_prompt(curr_number: &i32) -> String {
|
||||||
let mut length: usize = 9;
|
|
||||||
|
|
||||||
length += curr_number.to_string().len();
|
|
||||||
|
|
||||||
let user = String::from(
|
let user = String::from(
|
||||||
get_user_by_uid(get_current_uid())
|
get_user_by_uid(get_current_uid())
|
||||||
@ -118,16 +114,12 @@ pub mod shell {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
);
|
);
|
||||||
|
|
||||||
length += user.chars().count();
|
|
||||||
|
|
||||||
let host = String::from(
|
let host = String::from(
|
||||||
gethostname::gethostname()
|
gethostname::gethostname()
|
||||||
.to_str()
|
.to_str()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
);
|
);
|
||||||
|
|
||||||
length += host.chars().count();
|
|
||||||
|
|
||||||
let current_dir = String::from(
|
let current_dir = String::from(
|
||||||
env::current_dir()
|
env::current_dir()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@ -141,15 +133,13 @@ pub mod shell {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
);
|
);
|
||||||
|
|
||||||
length += dir_last.chars().count();
|
|
||||||
|
|
||||||
let prompt = String::from(format!("{}{}{}[{}@{} in {}]{} ",
|
let prompt = String::from(format!("{}{}{}[{}@{} in {}]{} ",
|
||||||
color::Fg(color::LightBlack),
|
color::Fg(color::LightBlack),
|
||||||
curr_number,
|
curr_number,
|
||||||
color::Fg(color::LightCyan),
|
color::Fg(color::LightCyan),
|
||||||
user, host, dir_last,
|
user, host, dir_last,
|
||||||
color::Fg(color::Reset)));
|
color::Fg(color::Reset)));
|
||||||
return (prompt, length);
|
return prompt;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn replace_signs(line: &String) -> String {
|
fn replace_signs(line: &String) -> String {
|
||||||
@ -206,6 +196,28 @@ pub mod shell {
|
|||||||
return String::new();
|
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() {
|
pub fn run_raw() {
|
||||||
@ -216,7 +228,11 @@ pub mod shell {
|
|||||||
//Used in conjunction with Up arrow to go back in history
|
//Used in conjunction with Up arrow to go back in history
|
||||||
//Resetted by pressing Enter
|
//Resetted by pressing Enter
|
||||||
let mut current_number = get_curr_history_number();
|
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
|
//Initialize
|
||||||
write!(stdout, "\r\n SquiShell (sqish)--- \r\n{}", prompt);
|
write!(stdout, "\r\n SquiShell (sqish)--- \r\n{}", prompt);
|
||||||
@ -235,7 +251,9 @@ pub mod shell {
|
|||||||
|
|
||||||
Key::Char('\n') => {
|
Key::Char('\n') => {
|
||||||
current_number = get_curr_history_number();
|
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")) {
|
if (mycommand != String::from("\n")) && (mycommand != String::from("exit")) {
|
||||||
let comm = replace_signs(&mycommand);
|
let comm = replace_signs(&mycommand);
|
||||||
let res = handle_input(&comm);
|
let res = handle_input(&comm);
|
||||||
@ -245,7 +263,7 @@ pub mod shell {
|
|||||||
write!(stdout, "\r\n{}", line);
|
write!(stdout, "\r\n{}", line);
|
||||||
}
|
}
|
||||||
current_number = get_curr_history_number();
|
current_number = get_curr_history_number();
|
||||||
(prompt, pr_len) = build_prompt(¤t_number);
|
prompt = build_prompt(¤t_number);
|
||||||
write!(stdout, "{}", prompt).unwrap();
|
write!(stdout, "{}", prompt).unwrap();
|
||||||
stdout.flush();
|
stdout.flush();
|
||||||
} else if mycommand == String::from("exit") {
|
} else if mycommand == String::from("exit") {
|
||||||
@ -262,21 +280,63 @@ pub mod shell {
|
|||||||
break;
|
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) => {
|
Key::Char(c) => {
|
||||||
mycommand.push(c);
|
//if at the end of the command...
|
||||||
write!(stdout, "{}", c).unwrap();
|
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 => {
|
Key::Backspace => {
|
||||||
match mycommand.pop() {
|
if current_pos > 0 {
|
||||||
Some(_) => {},
|
max_pos -= 1;
|
||||||
None => continue,
|
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 => {
|
Key::Up => {
|
||||||
@ -287,6 +347,7 @@ pub mod shell {
|
|||||||
None => continue
|
None => continue
|
||||||
};
|
};
|
||||||
mycommand = lastcmd.trim().to_string();
|
mycommand = lastcmd.trim().to_string();
|
||||||
|
(max_pos, current_pos) = get_cmd_curs_pos(&mycommand);
|
||||||
//let prompt = build_prompt();
|
//let prompt = build_prompt();
|
||||||
write!(stdout, "{}", mycommand);
|
write!(stdout, "{}", mycommand);
|
||||||
},
|
},
|
||||||
@ -299,21 +360,24 @@ pub mod shell {
|
|||||||
None => continue
|
None => continue
|
||||||
};
|
};
|
||||||
mycommand = lastcmd.trim().to_string();
|
mycommand = lastcmd.trim().to_string();
|
||||||
|
(max_pos, current_pos) = get_cmd_curs_pos(&mycommand);
|
||||||
//let prompt = build_prompt();
|
//let prompt = build_prompt();
|
||||||
write!(stdout, "{}", mycommand);
|
write!(stdout, "{}", mycommand);
|
||||||
},
|
},
|
||||||
|
|
||||||
Key::Left => {
|
Key::Left => {
|
||||||
//La taille du prompt est incorrecte à cause des couleurs !
|
if current_pos > 0 {
|
||||||
let curs_pos = stdout.cursor_pos().unwrap().0;
|
current_pos -= 1;
|
||||||
if usize::from(curs_pos) > pr_len {
|
|
||||||
write!(stdout, "{}", cursor::Left(1));
|
write!(stdout, "{}", cursor::Left(1));
|
||||||
stdout.flush();
|
stdout.flush();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
Key::Right => {
|
Key::Right => {
|
||||||
print!("{}", cursor::Right(1));
|
if current_pos < max_pos {
|
||||||
|
print!("{}", cursor::Right(1));
|
||||||
|
current_pos += 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => (),
|
_ => (),
|
||||||
|
Reference in New Issue
Block a user