Added hist number to prompt, Arrows working
This commit is contained in:
parent
8c92b82656
commit
c65a6aa4d0
9
Cargo.lock
generated
9
Cargo.lock
generated
@ -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
102
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 !<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;
|
||||
}
|
||||
```
|
||||
|
51
src/lib.rs
51
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;
|
||||
},
|
||||
|
||||
_ => (),
|
||||
|
@ -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)?;
|
||||
|
Loading…
x
Reference in New Issue
Block a user