use std::io::Stdout; use std::process::Command; use std::io::stdin; use std::io::{stdout}; use std::io::Write; use std::path::Path; use std::env; use std::str; use std::fs::File; use std::process::Stdio; use termion::event::Key; use termion::input::TermRead; use termion::raw::IntoRawMode; use termion::raw::RawTerminal; use termion::{color, cursor}; use termion; use unicode_segmentation::UnicodeSegmentation; use std::collections::HashMap; mod history; use crate::shell::history::*; ///Used to unify command output and error handling ///across functions pub struct CmdOutput { pub outp: String, pub rc: i16, } impl CmdOutput { pub fn from_values(output: String, rc: i16) -> CmdOutput { let myoutp = CmdOutput { outp: output, rc: rc, }; return myoutp; } pub 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(); if args.len() > 0 { *&mut new_dir = args.join(" "); } else { *&mut new_dir = String::from(homedir.as_str()); } let root = Path::new(&new_dir); if let Err(e) = env::set_current_dir(&root) { 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) -> Result { //let stdout = Stdio::inherit(); let res = get_history(); match res { Ok(r) => { let filepath = dirs::home_dir().unwrap().join(".history.sqish"); let file = File::open(filepath).unwrap(); *previous_command = Some(Stdio::from(file)); //if !commands.peek().is_some() { if !command_next { print!("{}", r); } }, 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) -> Result { let mut commands = input.trim().split("|").peekable(); let mut previous_command = None; let mut resultat = String::new(); let mut rc: i16 = 0; while let Some(command) = commands.next() { let parts = match shell_words::split(&command.trim()) { Ok(w) => w, 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..]); match command { "cd" => { change_dir(&args, &mut previous_command)?; }, "history" => { let next = commands.peek().is_some(); print_hist(&mut previous_command, next)?; }, command => { if commands.peek().is_some() { let stdin = match previous_command { None => Stdio::inherit(), Some(o) => o, }; let stdout = Stdio::piped(); let output = Command::new(command) .args(args) .stdout(stdout) .stdin(stdin) .spawn(); match output { Ok(o) => previous_command = Some(Stdio::from(o.stdout.unwrap())), Err(e) => { previous_command = None; eprintln!(" Err: {}", e); }, }; } else { let stdin = match previous_command { None => Stdio::inherit(), Some(o) => o, }; print!("\r\n"); let child = match Command::new(command) .args(args) .stdin(stdin) .spawn() { Ok(h) => h, Err(_) => { let err_string = format!("Not found : {}", command); return Err(CmdOutput::from_values(err_string, -1)); }, }; let output = child .wait_with_output() .expect("Failed to wait on child"); let status = output.status .code() .unwrap_or(255) .to_string(); previous_command = None; let format_res = format!("{}", status); *&mut rc = format_res.parse::().unwrap(); *&mut resultat = String::from_utf8(output.stdout).unwrap(); //let _ = &mut resultat.push_str(&format_res); } }, }; } //return resultat; return Ok(CmdOutput::from_values(resultat, rc)); } pub fn run_cmd(mycommand: &mut String, 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(); 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(); } stdout.flush(); } pub 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); return process_line(&c); }, None => return Err(CmdOutput::from_values( String::from("No such value"), 255)), }; //Regular command } else if line.len() > 0 { write_to_history(line); return process_line(line); //Nothing } else { //Command was empty, new line return Ok(CmdOutput::new_empty()); } }