Commandes fonctionnent
This commit is contained in:
28
Cargo.lock
generated
28
Cargo.lock
generated
@ -70,6 +70,12 @@ dependencies = [
|
|||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "numtoa"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.47"
|
version = "1.0.47"
|
||||||
@ -97,6 +103,15 @@ dependencies = [
|
|||||||
"bitflags",
|
"bitflags",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redox_termios"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8440d8acb4fd3d277125b4bd01a6f38aee8d814b3b5fc09b3f2b825d37d3fe8f"
|
||||||
|
dependencies = [
|
||||||
|
"redox_syscall",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_users"
|
name = "redox_users"
|
||||||
version = "0.4.3"
|
version = "0.4.3"
|
||||||
@ -114,6 +129,7 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"dirs",
|
"dirs",
|
||||||
"gethostname",
|
"gethostname",
|
||||||
|
"termion",
|
||||||
"users",
|
"users",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -128,6 +144,18 @@ dependencies = [
|
|||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "termion"
|
||||||
|
version = "2.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "659c1f379f3408c7e5e84c7d0da6d93404e3800b6b9d063ba24436419302ec90"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"numtoa",
|
||||||
|
"redox_syscall",
|
||||||
|
"redox_termios",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.37"
|
version = "1.0.37"
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rshell"
|
name = "sqish"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
@ -11,3 +11,4 @@ codegen-units = 1
|
|||||||
dirs = "4.0.0"
|
dirs = "4.0.0"
|
||||||
users = "0.11.0"
|
users = "0.11.0"
|
||||||
gethostname = "0.4.1"
|
gethostname = "0.4.1"
|
||||||
|
termion = "2.0.1"
|
||||||
|
264
src/lib.rs
264
src/lib.rs
@ -6,6 +6,7 @@ pub mod shell {
|
|||||||
use std::io::ErrorKind;
|
use std::io::ErrorKind;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::env;
|
use std::env;
|
||||||
|
use std::str;
|
||||||
use std::process::Child;
|
use std::process::Child;
|
||||||
use std::process::Stdio;
|
use std::process::Stdio;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
@ -14,15 +15,20 @@ pub mod shell {
|
|||||||
use std::io::{self, prelude::*, BufReader};
|
use std::io::{self, prelude::*, BufReader};
|
||||||
use users::{get_user_by_uid, get_current_uid};
|
use users::{get_user_by_uid, get_current_uid};
|
||||||
use gethostname::gethostname;
|
use gethostname::gethostname;
|
||||||
|
use termion::event::Key;
|
||||||
|
use termion::input::TermRead;
|
||||||
|
use termion::raw::IntoRawMode;
|
||||||
|
|
||||||
|
|
||||||
//used for home directory
|
//used for home directory
|
||||||
extern crate dirs;
|
extern crate dirs;
|
||||||
|
|
||||||
|
|
||||||
fn process_line(input: String) -> bool {
|
|
||||||
|
|
||||||
|
fn process_line(input: &str) -> String {
|
||||||
let mut commands = input.trim().split(" | ").peekable();
|
let mut commands = input.trim().split(" | ").peekable();
|
||||||
let mut previous_command = None;
|
let mut previous_command = None;
|
||||||
|
let mut resultat = String::new();
|
||||||
|
|
||||||
while let Some(command) = commands.next() {
|
while let Some(command) = commands.next() {
|
||||||
let mut parts = command.trim().split_whitespace();
|
let mut parts = command.trim().split_whitespace();
|
||||||
@ -34,52 +40,61 @@ pub mod shell {
|
|||||||
let new_dir = args.peekable().peek().map_or("/", |x| *x);
|
let new_dir = args.peekable().peek().map_or("/", |x| *x);
|
||||||
let root = Path::new(new_dir);
|
let root = Path::new(new_dir);
|
||||||
if let Err(e) = env::set_current_dir(&root) {
|
if let Err(e) = env::set_current_dir(&root) {
|
||||||
eprintln!("{}", e);
|
eprintln!(" Err: {}", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
previous_command = None;
|
previous_command = None;
|
||||||
},
|
},
|
||||||
"exit" => return true,
|
|
||||||
"history" => {
|
"history" => {
|
||||||
let stdout = Stdio::inherit();
|
let stdout = Stdio::inherit();
|
||||||
print_history();
|
let res = get_history();
|
||||||
|
match res {
|
||||||
|
Ok(r) => return r,
|
||||||
|
Err(e) => eprintln!(" Err: {}", e),
|
||||||
|
}
|
||||||
},
|
},
|
||||||
command => {
|
command => {
|
||||||
let stdin = previous_command.map_or(
|
if commands.peek().is_some() {
|
||||||
Stdio::inherit(),
|
let stdin = match previous_command {
|
||||||
|output: Child| Stdio::from(output.stdout.unwrap())
|
None => Stdio::inherit(),
|
||||||
);
|
Some(o) => o,
|
||||||
|
};
|
||||||
let stdout = if commands.peek().is_some() {
|
let stdout = Stdio::piped();
|
||||||
Stdio::piped()
|
|
||||||
} else {
|
|
||||||
Stdio::inherit()
|
|
||||||
};
|
|
||||||
|
|
||||||
let output = Command::new(command)
|
let output = Command::new(command)
|
||||||
.args(args)
|
.args(args)
|
||||||
.stdin(stdin)
|
.stdout(stdout)
|
||||||
.stdout(stdout)
|
.stdin(stdin)
|
||||||
.spawn();
|
.spawn();
|
||||||
|
|
||||||
match output {
|
match output {
|
||||||
Ok(output) => { previous_command = Some(output); },
|
Ok(o) => previous_command = Some(Stdio::from(o.stdout.unwrap())),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
previous_command = None;
|
previous_command = None;
|
||||||
eprintln!("{}", e);
|
eprintln!(" Err: {}", e);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
} else {
|
||||||
}
|
let stdin = match previous_command {
|
||||||
|
None => Stdio::inherit(),
|
||||||
|
Some(o) => o,
|
||||||
|
};
|
||||||
|
let output = Command::new(command)
|
||||||
|
.args(args)
|
||||||
|
.stdin(stdin)
|
||||||
|
.output()
|
||||||
|
.expect("???");
|
||||||
|
&mut resultat.push_str(str::from_utf8(&output.stdout)
|
||||||
|
.expect("Could not convert command to str")
|
||||||
|
);
|
||||||
|
previous_command = None;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
return resultat;
|
||||||
if let Some(mut final_command) = previous_command {
|
|
||||||
// block until the final command has finished
|
|
||||||
final_command.wait();
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn write_to_history(command: &str) -> Result<(), std::io::Error> {
|
fn write_to_history(command: &str) -> Result<(), std::io::Error> {
|
||||||
//Write a command to history
|
//Write a command to history
|
||||||
let filepath = dirs::home_dir().unwrap().join("history.rshell");
|
let filepath = dirs::home_dir().unwrap().join("history.rshell");
|
||||||
@ -109,14 +124,16 @@ pub mod shell {
|
|||||||
return Ok(number);
|
return Ok(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_history() -> Result<(), std::io::Error> {
|
fn get_history() -> Result<String, std::io::Error> {
|
||||||
let filepath = dirs::home_dir().unwrap().join("history.rshell");
|
let filepath = dirs::home_dir().unwrap().join("history.rshell");
|
||||||
let file = File::open(filepath)?;
|
let file = File::open(filepath)?;
|
||||||
let contents = BufReader::new(file).lines();
|
let contents = BufReader::new(file).lines();
|
||||||
|
let mut res = String::new();
|
||||||
for line in contents {
|
for line in contents {
|
||||||
println!("{}", line.unwrap());
|
let myline = format!("\r\n{}", line.unwrap());
|
||||||
|
res.push_str(&myline);
|
||||||
}
|
}
|
||||||
return Ok(());
|
return Ok(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_hist_from_number(number: &i32) -> Option<String> {
|
fn get_hist_from_number(number: &i32) -> Option<String> {
|
||||||
@ -194,8 +211,8 @@ pub mod shell {
|
|||||||
return prompt;
|
return prompt;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn replace_signs(line: String) -> String {
|
fn replace_signs(line: &String) -> String {
|
||||||
let mut ayo = String::from(&line);
|
let mut ayo = String::from(line);
|
||||||
if ayo.contains('~') {
|
if ayo.contains('~') {
|
||||||
let homedir = dirs::home_dir().unwrap().into_os_string().into_string().unwrap();
|
let homedir = dirs::home_dir().unwrap().into_os_string().into_string().unwrap();
|
||||||
let result = str::replace(&ayo, "~", &homedir);
|
let result = str::replace(&ayo, "~", &homedir);
|
||||||
@ -211,38 +228,147 @@ pub mod shell {
|
|||||||
return ayo;
|
return ayo;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(){
|
fn handle_input(line: &str) -> String {
|
||||||
loop {
|
//history callback
|
||||||
print!("{}", build_prompt());
|
if (line.len() > 0) && (line.starts_with('!')) {
|
||||||
stdout().flush();
|
let command_found = treat_history_callback(line);
|
||||||
let mut input = String::new();
|
match command_found {
|
||||||
let bytes = stdin().read_line(&mut input).unwrap();
|
Some(c) => {
|
||||||
input = replace_signs(input);
|
write_to_history(&c);
|
||||||
|
let outp = process_line(&c);
|
||||||
//history callback
|
return outp;
|
||||||
if (bytes > 0) && (input != String::from("\n")) && (input.starts_with('!')) {
|
},
|
||||||
let command_found = treat_history_callback(&input);
|
None => return String::from(" "),
|
||||||
match command_found {
|
};
|
||||||
Some(c) => {
|
//Regular command
|
||||||
write_to_history(&c);
|
} else if line.len() > 0 {
|
||||||
process_line(c);
|
write_to_history(line);
|
||||||
},
|
return process_line(line);
|
||||||
None => ()
|
//Nothing
|
||||||
};
|
} else {
|
||||||
//Regular command
|
//Command was empty, new line
|
||||||
} else if (bytes > 0) && (input != String::from("\n")) {
|
return String::from(" ");
|
||||||
write_to_history(&input);
|
|
||||||
if process_line(input) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
//Nothing
|
|
||||||
} else {
|
|
||||||
//Command was empty, new line
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn fake_tab(input: &str) -> String {
|
||||||
|
let faketab = format!(" --This is a fake output for autocomplete from {input}-- ");
|
||||||
|
//In reality, we would keep the input and append to it
|
||||||
|
let aaa = String::from(faketab);
|
||||||
|
return aaa;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run_raw() {
|
||||||
|
let stdin = stdin();
|
||||||
|
let mut stdout = stdout().into_raw_mode().unwrap();
|
||||||
|
let prompt = build_prompt();
|
||||||
|
let mut mycommand = String::new();
|
||||||
|
|
||||||
|
//Initialize
|
||||||
|
write!(stdout, "\r\n SquiShell (sqish)--- \r\n{}", prompt);
|
||||||
|
stdout.flush();
|
||||||
|
|
||||||
|
for c in stdin.keys() {
|
||||||
|
|
||||||
|
match c.unwrap() {
|
||||||
|
Key::Char('\t') => {
|
||||||
|
let res = fake_tab(&mycommand);
|
||||||
|
mycommand.clear();
|
||||||
|
mycommand.push_str(&res);
|
||||||
|
write!(stdout, "\r\n{}{}", prompt, res);
|
||||||
|
stdout.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
Key::Char('\n') => {
|
||||||
|
if (mycommand != String::from("\n")) && (mycommand != String::from("exit")) {
|
||||||
|
let comm = replace_signs(&mycommand);
|
||||||
|
let res = handle_input(&comm);
|
||||||
|
mycommand.clear();
|
||||||
|
//Proper printing
|
||||||
|
for line in res.split('\n') {
|
||||||
|
write!(stdout, "\r\n{}", line);
|
||||||
|
}
|
||||||
|
write!(stdout, "\r\n{}", prompt).unwrap();
|
||||||
|
stdout.flush();
|
||||||
|
} else if mycommand == String::from("exit") {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
write!(stdout, "\r\n{}", prompt).unwrap();
|
||||||
|
stdout.flush();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
Key::Ctrl('q') => break,
|
||||||
|
|
||||||
|
Key::Char(C) => {
|
||||||
|
mycommand.push(C);
|
||||||
|
write!(stdout, "{}", C).unwrap();
|
||||||
|
},
|
||||||
|
|
||||||
|
Key::Backspace => {
|
||||||
|
match mycommand.pop() {
|
||||||
|
Some(_) => {},
|
||||||
|
None => continue,
|
||||||
|
}
|
||||||
|
//Move cursor left
|
||||||
|
write!(stdout, "\x1b[D").unwrap();
|
||||||
|
//Delete one character there
|
||||||
|
write!(stdout, "\x1b[K").unwrap();
|
||||||
|
stdout.flush();
|
||||||
|
},
|
||||||
|
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
|
||||||
|
stdout.flush().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// pub fn run(){
|
||||||
|
// loop {
|
||||||
|
// print!("{}", build_prompt());
|
||||||
|
// stdout().flush();
|
||||||
|
// let mut input = String::new();
|
||||||
|
// let bytes = stdin().read_line(&mut input).unwrap();
|
||||||
|
// input = replace_signs(input);
|
||||||
|
//
|
||||||
|
// //history callback
|
||||||
|
// if (bytes > 0) && (input != String::from("\n")) && (input.starts_with('!')) {
|
||||||
|
// let command_found = treat_history_callback(&input);
|
||||||
|
// match command_found {
|
||||||
|
// Some(c) => {
|
||||||
|
// write_to_history(&c);
|
||||||
|
// process_line(&c);
|
||||||
|
// },
|
||||||
|
// None => ()
|
||||||
|
// };
|
||||||
|
// //Regular command
|
||||||
|
// } else if (bytes > 0) && (input != String::from("\n")) {
|
||||||
|
// write_to_history(&input);
|
||||||
|
// if process_line(input) {
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// //Nothing
|
||||||
|
// } else {
|
||||||
|
// //Command was empty, new line
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
pub use ::rshell::shell::run;
|
pub use ::rshell::shell::run_raw;
|
||||||
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
run();
|
run_raw();
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user