Added hist number to prompt, Arrows working
This commit is contained in:
9
Cargo.lock
generated
9
Cargo.lock
generated
@ -124,12 +124,13 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rshell"
|
name = "sqish"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"dirs",
|
"dirs",
|
||||||
"gethostname",
|
"gethostname",
|
||||||
"termion",
|
"termion",
|
||||||
|
"unicode-segmentation",
|
||||||
"users",
|
"users",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -182,6 +183,12 @@ version = "1.0.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
|
checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-segmentation"
|
||||||
|
version = "1.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "users"
|
name = "users"
|
||||||
version = "0.11.0"
|
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
|
* Make it a lib
|
||||||
|
|
||||||
I can also add basic features:
|
I can also add basic features:
|
||||||
* History
|
|
||||||
* customizable prompt
|
* customizable prompt
|
||||||
* Colours ?
|
* 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.
|
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;
|
return resultat;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_prompt() -> String {
|
fn build_prompt(curr_number: &i32) -> String {
|
||||||
let user = String::from(
|
let user = String::from(
|
||||||
get_user_by_uid(get_current_uid())
|
get_user_by_uid(get_current_uid())
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@ -132,7 +132,9 @@ pub mod shell {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
);
|
);
|
||||||
|
|
||||||
let prompt = String::from(format!("{}[{}@{} in {}]{} ",
|
let prompt = String::from(format!("{}{}{}[{}@{} in {}]{} ",
|
||||||
|
color::Fg(color::LightBlack),
|
||||||
|
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)));
|
||||||
@ -175,19 +177,21 @@ pub mod shell {
|
|||||||
//Nothing
|
//Nothing
|
||||||
} else {
|
} else {
|
||||||
//Command was empty, new line
|
//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
|
//Deletes currently written command
|
||||||
|
|
||||||
let mut stdout = stdout().into_raw_mode().unwrap();
|
let mut stdout = stdout().into_raw_mode().unwrap();
|
||||||
let nbr_of_chars = current_cmd.graphemes(true).count();
|
//write!(stdout, "{}", cursor::Save);
|
||||||
while nbr_of_chars > 0 {
|
for c in current_cmd.graphemes(true) {
|
||||||
write!(stdout, "\x1b[D").unwrap();
|
write!(stdout, "\x1b[D").unwrap();
|
||||||
write!(stdout, "\x1b[K").unwrap();
|
write!(stdout, "\x1b[K").unwrap();
|
||||||
}
|
}
|
||||||
|
stdout.flush();
|
||||||
|
//write!(stdout, "{}", cursor::Restore);
|
||||||
return String::new();
|
return String::new();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -196,9 +200,13 @@ pub mod shell {
|
|||||||
pub fn run_raw() {
|
pub fn run_raw() {
|
||||||
let stdin = stdin();
|
let stdin = stdin();
|
||||||
let mut stdout = stdout().into_raw_mode().unwrap();
|
let mut stdout = stdout().into_raw_mode().unwrap();
|
||||||
let mut prompt = build_prompt();
|
|
||||||
let mut mycommand = String::new();
|
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
|
//Initialize
|
||||||
write!(stdout, "\r\n SquiShell (sqish)--- \r\n{}", prompt);
|
write!(stdout, "\r\n SquiShell (sqish)--- \r\n{}", prompt);
|
||||||
stdout.flush();
|
stdout.flush();
|
||||||
@ -215,6 +223,8 @@ pub mod shell {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Key::Char('\n') => {
|
Key::Char('\n') => {
|
||||||
|
current_number = get_curr_history_number();
|
||||||
|
prompt = build_prompt(¤t_number);
|
||||||
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);
|
||||||
@ -223,7 +233,8 @@ pub mod shell {
|
|||||||
for line in res.split('\n') {
|
for line in res.split('\n') {
|
||||||
write!(stdout, "\r\n{}", line);
|
write!(stdout, "\r\n{}", line);
|
||||||
}
|
}
|
||||||
prompt = build_prompt();
|
current_number = get_curr_history_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") {
|
||||||
@ -258,19 +269,27 @@ pub mod shell {
|
|||||||
},
|
},
|
||||||
|
|
||||||
Key::Up => {
|
Key::Up => {
|
||||||
mycommand = rewrite(mycommand);
|
mycommand = delete_current_cmd(mycommand);
|
||||||
let lastnumber = match get_history_number() {
|
current_number -= 1;
|
||||||
Ok(e) => e,
|
let lastcmd = match get_hist_from_number(¤t_number) {
|
||||||
Err(_) => continue,
|
Some(c) => c,
|
||||||
};
|
None => continue
|
||||||
let lastcmd = match get_hist_from_number(&lastnumber) {
|
};
|
||||||
|
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,
|
Some(c) => c,
|
||||||
None => continue
|
None => continue
|
||||||
};
|
};
|
||||||
mycommand = lastcmd.trim().to_string();
|
mycommand = lastcmd.trim().to_string();
|
||||||
//let prompt = build_prompt();
|
//let prompt = build_prompt();
|
||||||
write!(stdout, "{}", mycommand);
|
write!(stdout, "{}", mycommand);
|
||||||
mycommand = lastcmd;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_ => (),
|
_ => (),
|
||||||
|
|||||||
@ -34,6 +34,16 @@ pub fn get_history_number() -> Result<i32, std::io::Error> {
|
|||||||
return Ok(number);
|
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> {
|
pub fn get_history() -> Result<String, std::io::Error> {
|
||||||
let filepath = dirs::home_dir().unwrap().join("history.sqish");
|
let filepath = dirs::home_dir().unwrap().join("history.sqish");
|
||||||
let file = File::open(filepath)?;
|
let file = File::open(filepath)?;
|
||||||
|
|||||||
Reference in New Issue
Block a user