dev #2
							
								
								
									
										10
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								README.md
									
									
									
									
									
								
							| @ -14,12 +14,18 @@ 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. | 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 redirecting >> to the end of a file ?  | ||||||
| * Allow && in commands ?  | * Allow && in commands ?  | ||||||
| * Improve word jumping | * Improve word jumping | ||||||
| * Add arguments : run a file (sqish myexec) which I believe would make it compatible with gdb ? | * 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) | ## sqishrc (Config) | ||||||
| See the included sqishrc file included for comments, and copy it as ~/.sqishrc for use (optionnal). | See the included sqishrc file included for comments, and copy it as ~/.sqishrc for use (optionnal). | ||||||
|  | |||||||
							
								
								
									
										5
									
								
								sqishrc
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								sqishrc
									
									
									
									
									
								
							| @ -44,3 +44,8 @@ hotkeys: | |||||||
|  |  | ||||||
| #Init : A command to run on startup. Can be used for many things... | #Init : A command to run on startup. Can be used for many things... | ||||||
| init: "echo ---Initialization over---" | init: "echo ---Initialization over---" | ||||||
|  |  | ||||||
|  | #Env : to set environment variables at launch | ||||||
|  | env: | ||||||
|  |   CAT: "meow" | ||||||
|  |   PATH: /home/cat/bin:$PATH | ||||||
|  | |||||||
							
								
								
									
										161
									
								
								src/lib.rs
									
									
									
									
									
								
							
							
						
						
									
										161
									
								
								src/lib.rs
									
									
									
									
									
								
							| @ -1,5 +1,6 @@ | |||||||
| #[allow(unused_must_use)] | #[allow(unused_must_use)] | ||||||
| pub mod shell { | pub mod shell { | ||||||
|  |     use regex::Regex; | ||||||
|     use std::io::Stdout; |     use std::io::Stdout; | ||||||
|     use std::process::Command; |     use std::process::Command; | ||||||
|     use std::io::stdin; |     use std::io::stdin; | ||||||
| @ -14,7 +15,7 @@ pub mod shell { | |||||||
|     use termion::input::TermRead; |     use termion::input::TermRead; | ||||||
|     use termion::raw::IntoRawMode; |     use termion::raw::IntoRawMode; | ||||||
|     use termion::raw::RawTerminal; |     use termion::raw::RawTerminal; | ||||||
|     use termion::cursor; |     use termion::{color, cursor}; | ||||||
|     use termion; |     use termion; | ||||||
|     use unicode_segmentation::UnicodeSegmentation; |     use unicode_segmentation::UnicodeSegmentation; | ||||||
|     use std::collections::HashMap; |     use std::collections::HashMap; | ||||||
| @ -33,7 +34,46 @@ pub mod shell { | |||||||
|     extern crate shell_words; |     extern crate shell_words; | ||||||
|     extern crate ctrlc; |     extern crate ctrlc; | ||||||
|  |  | ||||||
|     fn change_dir(args: &Vec<String>, previous_command: &mut Option<Stdio>) { |     ///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<Stdout>, | ||||||
|  |         //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<String>, previous_command: &mut Option<Stdio>) -> Result<CmdOutput, CmdOutput> { | ||||||
|         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 new_dir = args.peekable().peek().map_or(homedir.as_str(), |x| *x); |         //let new_dir = args.peekable().peek().map_or(homedir.as_str(), |x| *x); | ||||||
|         let mut new_dir = String::new(); |         let mut new_dir = String::new(); | ||||||
| @ -45,14 +85,15 @@ pub mod shell { | |||||||
|  |  | ||||||
|         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!(" Err: {}", e); |             return Err(CmdOutput::from_values( | ||||||
|         } else { |                     format!("Error setting directory to {}, got {:?}", | ||||||
|             println!(""); |                                  new_dir, e), -1)); | ||||||
|         } |         }; | ||||||
|         *previous_command = None; |         *previous_command = None; | ||||||
|  |         return Ok(CmdOutput::new_empty()); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn print_hist(previous_command: &mut Option<Stdio>, command_next: bool) { |     fn print_hist(previous_command: &mut Option<Stdio>, command_next: bool) -> Result<CmdOutput, CmdOutput> { | ||||||
|         //let stdout = Stdio::inherit(); |         //let stdout = Stdio::inherit(); | ||||||
|         let res = get_history(); |         let res = get_history(); | ||||||
|         match res { |         match res { | ||||||
| @ -65,30 +106,37 @@ pub mod shell { | |||||||
|                     print!("{}", r); |                     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()); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn process_line(input: &str) -> String { |  | ||||||
|  |     fn process_line(input: &str) -> Result<CmdOutput, CmdOutput> { | ||||||
|         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(); |         let mut resultat = String::new(); | ||||||
|  |         let mut rc: i16 = 0; | ||||||
|  |  | ||||||
|         while let Some(command) = commands.next()  { |         while let Some(command) = commands.next()  { | ||||||
|             let parts = match shell_words::split(&command.trim()) { |             let parts = match shell_words::split(&command.trim()) { | ||||||
|                 Ok(w) => w, |                 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 command = parts[0].as_str(); | ||||||
|             let args = Vec::from(&parts[1..]); |             let args = Vec::from(&parts[1..]); | ||||||
|  |  | ||||||
|             match command { |             match command { | ||||||
|                 "cd" => { |                 "cd" => { | ||||||
|                     change_dir(&args, &mut previous_command); |                     change_dir(&args, &mut previous_command)?; | ||||||
|                 }, |                 }, | ||||||
|                 "history" => { |                 "history" => { | ||||||
|                     let next = commands.peek().is_some(); |                     let next = commands.peek().is_some(); | ||||||
|                     print_hist(&mut previous_command, next); |                     print_hist(&mut previous_command, next)?; | ||||||
|                 }, |                 }, | ||||||
|                 command => { |                 command => { | ||||||
|                     if commands.peek().is_some() { |                     if commands.peek().is_some() { | ||||||
| @ -126,7 +174,7 @@ pub mod shell { | |||||||
|                                 Ok(h) => h, |                                 Ok(h) => h, | ||||||
|                                 Err(_) => { |                                 Err(_) => { | ||||||
|                                     let err_string = format!("Not found : {}", command); |                                     let err_string = format!("Not found : {}", command); | ||||||
|                                     return String::from(err_string); |                                     return Err(CmdOutput::from_values(err_string, -1)); | ||||||
|                                 }, |                                 }, | ||||||
|                             }; |                             }; | ||||||
|  |  | ||||||
| @ -140,12 +188,15 @@ pub mod shell { | |||||||
|                         previous_command = None; |                         previous_command = None; | ||||||
|                         let format_res = format!("{}",  |                         let format_res = format!("{}",  | ||||||
|                                                  status); |                                                  status); | ||||||
|                         let _ = &mut resultat.push_str(&format_res); |                         *&mut rc = format_res.parse::<i16>().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 { |     fn replace_signs(line: &String) -> String { | ||||||
| @ -165,17 +216,18 @@ pub mod shell { | |||||||
|         return ayo; |         return ayo; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn handle_input(line: &str) -> String { |     fn handle_input(line: &str) -> Result<CmdOutput, CmdOutput> { | ||||||
|         //history callback |         //history callback | ||||||
|         if (line.len() > 0) && (line.starts_with('!'))  { |         if (line.len() > 0) && (line.starts_with('!'))  { | ||||||
|             let command_found = treat_history_callback(line); |             let command_found = treat_history_callback(line); | ||||||
|             match command_found { |             match command_found { | ||||||
|                 Some(c) => { |                 Some(c) => { | ||||||
|                     write_to_history(&c); |                     write_to_history(&c); | ||||||
|                     let outp = process_line(&c); |                     return process_line(&c); | ||||||
|                     return outp; |  | ||||||
|                     }, |                     }, | ||||||
|                 None => return String::from(" "), |                 None => return Err(CmdOutput::from_values( | ||||||
|  |                         String::from("No such value"), | ||||||
|  |                         255)), | ||||||
|             }; |             }; | ||||||
|         //Regular command |         //Regular command | ||||||
|         } else if line.len() > 0 { |         } else if line.len() > 0 { | ||||||
| @ -184,7 +236,7 @@ pub mod shell { | |||||||
|         //Nothing |         //Nothing | ||||||
|         } else { |         } else { | ||||||
|             //Command was empty, new line |             //Command was empty, new line | ||||||
|             return String::from(""); |             return Ok(CmdOutput::new_empty()); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @ -262,25 +314,36 @@ pub mod shell { | |||||||
|                current_number: &mut i32, |                current_number: &mut i32, | ||||||
|                conf: &mut SqishConf, |                conf: &mut SqishConf, | ||||||
|                stdout: &mut RawTerminal<Stdout>) { |                stdout: &mut RawTerminal<Stdout>) { | ||||||
|  |         //NORMAL CMD | ||||||
|         if (mycommand != &String::from("")) && (mycommand != &String::from("exit")) { |         if (mycommand != &String::from("")) && (mycommand != &String::from("exit")) { | ||||||
|  |             //Run the command | ||||||
|             let comm = replace_signs(&mycommand); |             let comm = replace_signs(&mycommand); | ||||||
|             RawTerminal::suspend_raw_mode(&stdout); |             RawTerminal::suspend_raw_mode(&stdout); | ||||||
|             let res = handle_input(&comm); |             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); |             RawTerminal::activate_raw_mode(&stdout); | ||||||
|             mycommand.clear(); |             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()); |             conf.update_prompt(get_curr_history_number()); | ||||||
|             write!(stdout, "{}", conf.promptline).unwrap(); |             write!(stdout, "{}", conf.promptline).unwrap(); | ||||||
|             stdout.flush(); |             stdout.flush(); | ||||||
|             *current_number += 1; |             *current_number += 1; | ||||||
|  |         //EXITTING | ||||||
|         } else if mycommand == &String::from("exit") { |         } else if mycommand == &String::from("exit") { | ||||||
|             write!(stdout, "\r\nThanks for using Sqish!\r\nSayonara \r\n"); |             write!(stdout, "\r\nThanks for using Sqish!\r\nSayonara \r\n"); | ||||||
|             RawTerminal::suspend_raw_mode(&stdout); |             RawTerminal::suspend_raw_mode(&stdout); | ||||||
|             std::process::exit(0); |             std::process::exit(0); | ||||||
|  |         //EMPTY LINE | ||||||
|         } else { |         } else { | ||||||
|             conf.update_prompt(get_curr_history_number()); |             conf.update_prompt(get_curr_history_number()); | ||||||
|             write!(stdout, "\r\n{}", conf.promptline).unwrap(); |             write!(stdout, "\r\n{}", conf.promptline).unwrap(); | ||||||
| @ -350,6 +413,7 @@ pub mod shell { | |||||||
|                     aliases: HashMap::new(), |                     aliases: HashMap::new(), | ||||||
|                     hotkeys: HashMap::new(), |                     hotkeys: HashMap::new(), | ||||||
|                     init: String::new(), |                     init: String::new(), | ||||||
|  |                     env: HashMap::new(), | ||||||
|                 }; |                 }; | ||||||
|                 let ret_line = format!("Could not build conf, got {}\ |                 let ret_line = format!("Could not build conf, got {}\ | ||||||
|                                        \r\nUsing default promptline",  e); |                                        \r\nUsing default promptline",  e); | ||||||
| @ -516,20 +580,6 @@ pub mod shell { | |||||||
|         write!(elems.stdout, "{}{}", elems.conf.promptline, elems.command); |         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<Stdout>, |  | ||||||
|         //Current history number |  | ||||||
|         current_number: i32, |  | ||||||
|         //Configuration |  | ||||||
|         conf: SqishConf, |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fn jmp_nxt_word(elems: &mut TermElements) { |     fn jmp_nxt_word(elems: &mut TermElements) { | ||||||
|         let steps = find_next_space(&elems.cur_pos, &elems.max_pos, &elems.command); |         let steps = find_next_space(&elems.cur_pos, &elems.max_pos, &elems.command); | ||||||
|         //println!("steps: {:?} cur {:?} max {:?}", steps, elems.cur_pos, elems.max_pos); |         //println!("steps: {:?} cur {:?} max {:?}", steps, elems.cur_pos, elems.max_pos); | ||||||
| @ -588,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 |     //THE ENTRYPOINT FUNCTION | ||||||
|     pub fn run_raw() { |     pub fn run_raw() { | ||||||
|  |  | ||||||
| @ -610,6 +692,7 @@ pub mod shell { | |||||||
|         //Initializing |         //Initializing | ||||||
|         write!(elems.stdout, "\r\n ---Sqish initializing--- \r\n{}", elems.conf.promptline); |         write!(elems.stdout, "\r\n ---Sqish initializing--- \r\n{}", elems.conf.promptline); | ||||||
|         elems.stdout.flush(); |         elems.stdout.flush(); | ||||||
|  |         set_envvars(&elems.conf); | ||||||
|  |  | ||||||
|         if elems.conf.init != String::from("") { |         if elems.conf.init != String::from("") { | ||||||
|             run_cmd(&mut String::from(&elems.conf.init), &mut elems.current_number, &mut elems.conf, &mut elems.stdout); |             run_cmd(&mut String::from(&elems.conf.init), &mut elems.current_number, &mut elems.conf, &mut elems.stdout); | ||||||
|  | |||||||
| @ -16,6 +16,7 @@ pub struct SqishConf { | |||||||
|     pub aliases: HashMap<String, String>, |     pub aliases: HashMap<String, String>, | ||||||
|     pub hotkeys: HashMap<String, String>, |     pub hotkeys: HashMap<String, String>, | ||||||
|     pub init: String, |     pub init: String, | ||||||
|  |     pub env: HashMap<String, String>, | ||||||
| } | } | ||||||
|  |  | ||||||
| impl SqishConf { | impl SqishConf { | ||||||
| @ -54,12 +55,16 @@ impl SqishConf { | |||||||
|         let hotkeys_yaml = &sqishrc["hotkeys"]; |         let hotkeys_yaml = &sqishrc["hotkeys"]; | ||||||
|         let hotkeys = Self::yaml_dict2hashmap(hotkeys_yaml); |         let hotkeys = Self::yaml_dict2hashmap(hotkeys_yaml); | ||||||
|  |  | ||||||
|  |         let env_yaml = &sqishrc["env"]; | ||||||
|  |         let env = Self::yaml_dict2hashmap(env_yaml); | ||||||
|  |  | ||||||
|         let mut out_conf = SqishConf { |         let mut out_conf = SqishConf { | ||||||
|             promptline: out_str.clone(), |             promptline: out_str.clone(), | ||||||
|             promptline_base: out_str, |             promptline_base: out_str, | ||||||
|             aliases: aliases, |             aliases: aliases, | ||||||
|             hotkeys: hotkeys, |             hotkeys: hotkeys, | ||||||
|             init: startup, |             init: startup, | ||||||
|  |             env: env, | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         out_conf.handle_rgb(); |         out_conf.handle_rgb(); | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user