From 488c9c6e0ab460e7a43655b6b80125186b9cfa23 Mon Sep 17 00:00:00 2001 From: Justine Pelletreau Date: Thu, 2 Mar 2023 11:46:21 +0100 Subject: [PATCH 1/5] Started to implement CmdOutput - keep going... --- src/lib.rs | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index babd18c..92c4fb5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -69,15 +69,34 @@ pub mod shell { } } - fn process_line(input: &str) -> String { + struct CmdOutput { + outp: String, + rc: i16, + } + + impl CmdOutput { + fn from_values(output: String, rc: i16) -> CmdOutput { + let myoutp = CmdOutput { + outp: output, + rc: rc, + } + return myoutp; + } + } + + fn process_line(input: &str) -> Result { let mut commands = input.trim().split("|").peekable(); let mut previous_command = None; let mut resultat = String::new(); + let mut outp = CmdOutput::new_empty(); + while let Some(command) = commands.next() { let parts = match shell_words::split(&command.trim()) { Ok(w) => w, - Err(e) => { return format!("Error parsing the command : {:?}", e); }, + Err(e) => { + return Err(CmdOutput::from_values(format!("Could not parse command {:?}", e), -1)); + }, }; let command = parts[0].as_str(); let args = Vec::from(&parts[1..]); -- 2.47.2 From 1eb1b239c2a6a49a1c103562a0167cb0ca61da01 Mon Sep 17 00:00:00 2001 From: Justine Pelletreau Date: Thu, 2 Mar 2023 14:49:34 +0100 Subject: [PATCH 2/5] Reformatted the command execution part --- README.md | 8 +++- src/lib.rs | 135 ++++++++++++++++++++++++++++++++--------------------- 2 files changed, 89 insertions(+), 54 deletions(-) diff --git a/README.md b/README.md index e91b0d8..0c62d1e 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,13 @@ Sqish is now my default shell on my home computer, and I'll keep adding features Also keep in mind I'm a beginner in Rust, so the code is pretty dirty; I will probably come around to reformating it once I have all the features I want and ironed out all the bugs. -TODO: +# TODO + +TODO (Reformat): +* Creating a struct "shell" that contains builder method taking an input and an output as parameters as well as a conf could be cool +* Maybe subdivide lib.src to have more modularity 'cause it getting kinda long... 500 lines it a bit much ? + +TODO(Features): * Allow redirecting >> to the end of a file ? * Allow && in commands ? * Improve word jumping diff --git a/src/lib.rs b/src/lib.rs index 92c4fb5..82c579e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,7 +14,7 @@ pub mod shell { use termion::input::TermRead; use termion::raw::IntoRawMode; use termion::raw::RawTerminal; - use termion::cursor; + use termion::{color, cursor}; use termion; use unicode_segmentation::UnicodeSegmentation; use std::collections::HashMap; @@ -33,7 +33,46 @@ pub mod shell { extern crate shell_words; extern crate ctrlc; - fn change_dir(args: &Vec, previous_command: &mut Option) { + ///Stores essential terminal variables + struct TermElements { + //Stores currently typed command + command: String, + //Current cursor position in the line + cur_pos: usize, + //Max position of cursor in the line + max_pos: usize, + stdout: RawTerminal, + //Current history number + current_number: i32, + //Configuration + conf: SqishConf, + } + + ///Used to unify command output and error handling + ///across functions + struct CmdOutput { + outp: String, + rc: i16, + } + + impl CmdOutput { + fn from_values(output: String, rc: i16) -> CmdOutput { + let myoutp = CmdOutput { + outp: output, + rc: rc, + }; + return myoutp; + } + fn new_empty() -> CmdOutput { + let outp = CmdOutput { + outp: String::new(), + rc: 0, + }; + return outp; + } + } + + fn change_dir(args: &Vec, previous_command: &mut Option) -> Result { let homedir = dirs::home_dir().unwrap().into_os_string().into_string().unwrap(); //let new_dir = args.peekable().peek().map_or(homedir.as_str(), |x| *x); let mut new_dir = String::new(); @@ -45,14 +84,15 @@ pub mod shell { let root = Path::new(&new_dir); if let Err(e) = env::set_current_dir(&root) { - eprintln!(" Err: {}", e); - } else { - println!(""); - } + return Err(CmdOutput::from_values( + format!("Error setting directory to {}, got {:?}", + new_dir, e), -1)); + }; *previous_command = None; + return Ok(CmdOutput::new_empty()); } - fn print_hist(previous_command: &mut Option, command_next: bool) { + fn print_hist(previous_command: &mut Option, command_next: bool) -> Result { //let stdout = Stdio::inherit(); let res = get_history(); match res { @@ -65,31 +105,19 @@ pub mod shell { print!("{}", r); } }, - Err(e) => eprintln!(" Err reading history: {}", e), + Err(e) => { + return Err(CmdOutput::from_values(format!("Could not print history, got {:?}", e), 255)); + }, } + return Ok(CmdOutput::new_empty()); } - struct CmdOutput { - outp: String, - rc: i16, - } - - impl CmdOutput { - fn from_values(output: String, rc: i16) -> CmdOutput { - let myoutp = CmdOutput { - outp: output, - rc: rc, - } - return myoutp; - } - } fn process_line(input: &str) -> Result { let mut commands = input.trim().split("|").peekable(); let mut previous_command = None; let mut resultat = String::new(); - - let mut outp = CmdOutput::new_empty(); + let mut rc: i16 = 0; while let Some(command) = commands.next() { let parts = match shell_words::split(&command.trim()) { @@ -103,11 +131,11 @@ pub mod shell { match command { "cd" => { - change_dir(&args, &mut previous_command); + change_dir(&args, &mut previous_command)?; }, "history" => { let next = commands.peek().is_some(); - print_hist(&mut previous_command, next); + print_hist(&mut previous_command, next)?; }, command => { if commands.peek().is_some() { @@ -145,7 +173,7 @@ pub mod shell { Ok(h) => h, Err(_) => { let err_string = format!("Not found : {}", command); - return String::from(err_string); + return Err(CmdOutput::from_values(err_string, -1)); }, }; @@ -159,12 +187,15 @@ pub mod shell { previous_command = None; let format_res = format!("{}", status); - let _ = &mut resultat.push_str(&format_res); + *&mut rc = format_res.parse::().unwrap(); + *&mut resultat = String::from_utf8(output.stdout).unwrap(); + //let _ = &mut resultat.push_str(&format_res); } }, }; } - return resultat; + //return resultat; + return Ok(CmdOutput::from_values(resultat, rc)); } fn replace_signs(line: &String) -> String { @@ -184,17 +215,18 @@ pub mod shell { return ayo; } - fn handle_input(line: &str) -> String { + fn handle_input(line: &str) -> Result { //history callback if (line.len() > 0) && (line.starts_with('!')) { let command_found = treat_history_callback(line); match command_found { Some(c) => { write_to_history(&c); - let outp = process_line(&c); - return outp; + return process_line(&c); }, - None => return String::from(" "), + None => return Err(CmdOutput::from_values( + String::from("No such value"), + 255)), }; //Regular command } else if line.len() > 0 { @@ -203,7 +235,7 @@ pub mod shell { //Nothing } else { //Command was empty, new line - return String::from(""); + return Ok(CmdOutput::new_empty()); } } @@ -281,25 +313,36 @@ pub mod shell { current_number: &mut i32, conf: &mut SqishConf, stdout: &mut RawTerminal) { + //NORMAL CMD if (mycommand != &String::from("")) && (mycommand != &String::from("exit")) { + //Run the command let comm = replace_signs(&mycommand); RawTerminal::suspend_raw_mode(&stdout); let res = handle_input(&comm); + //Display the results + match res { + Ok(_) => (), + Err(e) => { + println!("\r\n{}Got error {}, RC : {:?}{}", + color::Fg(color::Red), + e.outp, + e.rc, + color::Fg(color::Reset)); + }, + }; + //Prepare for a new command RawTerminal::activate_raw_mode(&stdout); mycommand.clear(); - for line in res.split("\r\n") { - if line != "" && line.starts_with('N'){ - write!(stdout, "{}\r\n", line); - } - } conf.update_prompt(get_curr_history_number()); write!(stdout, "{}", conf.promptline).unwrap(); stdout.flush(); *current_number += 1; + //EXITTING } else if mycommand == &String::from("exit") { write!(stdout, "\r\nThanks for using Sqish!\r\nSayonara \r\n"); RawTerminal::suspend_raw_mode(&stdout); std::process::exit(0); + //EMPTY LINE } else { conf.update_prompt(get_curr_history_number()); write!(stdout, "\r\n{}", conf.promptline).unwrap(); @@ -535,20 +578,6 @@ pub mod shell { write!(elems.stdout, "{}{}", elems.conf.promptline, elems.command); } - struct TermElements { - //Stores currently typed command - command: String, - //Current cursor position in the line - cur_pos: usize, - //Max position of cursor in the line - max_pos: usize, - stdout: RawTerminal, - //Current history number - current_number: i32, - //Configuration - conf: SqishConf, - } - fn jmp_nxt_word(elems: &mut TermElements) { let steps = find_next_space(&elems.cur_pos, &elems.max_pos, &elems.command); //println!("steps: {:?} cur {:?} max {:?}", steps, elems.cur_pos, elems.max_pos); -- 2.47.2 From 68dc23a15ec3ce95d161e6e7dd9748cd5f53b090 Mon Sep 17 00:00:00 2001 From: Justine Date: Sat, 4 Mar 2023 14:19:01 +0100 Subject: [PATCH 3/5] Added a dirty implementation of env var setting --- src/lib.rs | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 82c579e..57150ba 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,6 @@ #[allow(unused_must_use)] pub mod shell { + use regex::Regex; use std::io::Stdout; use std::process::Command; use std::io::stdin; @@ -412,6 +413,7 @@ pub mod shell { aliases: HashMap::new(), hotkeys: HashMap::new(), init: String::new(), + env: HashMap::new(), }; let ret_line = format!("Could not build conf, got {}\ \r\nUsing default promptline", e); @@ -636,6 +638,38 @@ pub mod shell { }; } + //I smell horrible code in here + fn set_envvars(conf: &SqishConf) { + let vars = conf.env.clone(); + let re = Regex::new(r"\$[A-Za-z_]+").unwrap(); + + for (key, value) in vars { + let mut after = value.clone(); + let mut nbr_of_vars = *&value.matches('$').count(); + while nbr_of_vars >= 1 { + let temp = after.clone(); + let caps = re.captures(&temp).unwrap(); + for cap in caps.iter() { + match cap { + Some(c) => { + let myvar = String::from(c.as_str()).replace('$', ""); + match env::var(myvar) { + Ok(r) => { + *&mut after = after.replace(c.as_str(), &r); + }, + Err(_) => continue, + }; + }, + None => continue, + }; + } + nbr_of_vars -= 1; + } + env::set_var(&key, &after); + } + } + + //THE ENTRYPOINT FUNCTION pub fn run_raw() { @@ -658,6 +692,7 @@ pub mod shell { //Initializing write!(elems.stdout, "\r\n ---Sqish initializing--- \r\n{}", elems.conf.promptline); elems.stdout.flush(); + set_envvars(&elems.conf); if elems.conf.init != String::from("") { run_cmd(&mut String::from(&elems.conf.init), &mut elems.current_number, &mut elems.conf, &mut elems.stdout); -- 2.47.2 From 643c69623c6f91875e64535c570b3369b69b6e1b Mon Sep 17 00:00:00 2001 From: Justine Date: Sat, 4 Mar 2023 14:28:20 +0100 Subject: [PATCH 4/5] Forget to add the config part --- src/shell/config.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/shell/config.rs b/src/shell/config.rs index 526adb9..89ab380 100644 --- a/src/shell/config.rs +++ b/src/shell/config.rs @@ -16,6 +16,7 @@ pub struct SqishConf { pub aliases: HashMap, pub hotkeys: HashMap, pub init: String, + pub env: HashMap, } impl SqishConf { @@ -54,12 +55,16 @@ impl SqishConf { let hotkeys_yaml = &sqishrc["hotkeys"]; let hotkeys = Self::yaml_dict2hashmap(hotkeys_yaml); + let env_yaml = &sqishrc["env"]; + let env = Self::yaml_dict2hashmap(env_yaml); + let mut out_conf = SqishConf { promptline: out_str.clone(), promptline_base: out_str, aliases: aliases, hotkeys: hotkeys, init: startup, + env: env, }; out_conf.handle_rgb(); -- 2.47.2 From 522b8e7037bdea03c16d41c188a455921c3bc96e Mon Sep 17 00:00:00 2001 From: Justine Date: Sat, 4 Mar 2023 14:30:13 +0100 Subject: [PATCH 5/5] Add doc --- README.md | 2 +- sqishrc | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0c62d1e..8101784 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ TODO(Features): * Allow && in commands ? * Improve word jumping * Add arguments : run a file (sqish myexec) which I believe would make it compatible with gdb ? -* Add export command +* Add export command => Reuse the function set_envvars ## sqishrc (Config) See the included sqishrc file included for comments, and copy it as ~/.sqishrc for use (optionnal). diff --git a/sqishrc b/sqishrc index dfe0cc9..65fd264 100644 --- a/sqishrc +++ b/sqishrc @@ -44,3 +44,8 @@ hotkeys: #Init : A command to run on startup. Can be used for many things... init: "echo ---Initialization over---" + +#Env : to set environment variables at launch +env: + CAT: "meow" + PATH: /home/cat/bin:$PATH -- 2.47.2