Added hist number to prompt, Arrows working

This commit is contained in:
Justine 2022-12-23 17:35:20 +01:00
parent 8c92b82656
commit c65a6aa4d0
4 changed files with 53 additions and 119 deletions

9
Cargo.lock generated
View File

@ -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"

102
README.md
View File

@ -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 !<number> 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;
}
```

View File

@ -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(&current_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(&current_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(&current_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(&current_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(&current_number) {
Some(c) => c,
None => continue
};
mycommand = lastcmd.trim().to_string();
//let prompt = build_prompt();
write!(stdout, "{}", mycommand);
mycommand = lastcmd;
},
_ => (),

View File

@ -34,6 +34,16 @@ pub fn get_history_number() -> Result<i32, std::io::Error> {
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<String, std::io::Error> {
let filepath = dirs::home_dir().unwrap().join("history.sqish");
let file = File::open(filepath)?;