diff --git a/Cargo.toml b/Cargo.toml index f8fce19..a781df4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,3 +20,4 @@ sha1 = "0.10.5" base64 = "0.20.0" rpassword = "7.2.0" shlex = "1.1.0" +shellexpand = "3.0.0" diff --git a/src/parser.rs b/src/parser.rs index e540846..8a9ea1c 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -17,6 +17,7 @@ peg::parser! { / mv_cmd() / rm_cmd() / pb_cmd() + / source_cmd() / enc_cmd() / pass_cmd() / noop_cmd() @@ -77,6 +78,7 @@ peg::parser! { rule help_cmd() -> Command<'input> = "help" { Command::Help } rule quit_cmd() -> Command<'input> = "quit" { Command::Quit } rule pb_cmd() -> Command<'input> = "pb" _ e:$(([' '..='~'])+) { Command::PasteBuffer(e.to_string()) } + rule source_cmd() -> Command<'input> = "source" _ s:$(([' '..='~'])+) { Command::Source(s.to_string()) } rule ls_cmd() -> Command<'input> = "ls" f:comment()? { Command::Ls(f.unwrap_or(".".to_string())) } rule add_cmd() -> Command<'input> = "add" _ name:name() { Command::Add(Rc::new(RefCell::new(name))) } rule error_cmd() -> Command<'input> = "error" _ e:$(([' '..='~'])+) { Command::Error(LKErr::Error(e)) } diff --git a/src/repl.rs b/src/repl.rs index 12eba52..242cfb5 100644 --- a/src/repl.rs +++ b/src/repl.rs @@ -3,12 +3,13 @@ use rpassword::prompt_password; use rustyline::error::ReadlineError; use rustyline::Editor; use std::{cell::RefCell, rc::Rc}; +use std::path::PathBuf; use crate::lk::LK; use crate::parser::command_parser; use crate::password::{fix_password_recursion, PasswordRef}; use crate::structs::{Command, LKErr, Radix, HISTORY_FILE}; -use crate::utils::{ call_cmd_with_input, get_copy_command_from_env }; +use crate::utils::{ call_cmd_with_input, get_copy_command_from_env, get_cmd_args_from_command }; #[derive(Debug)] pub struct LKRead { @@ -203,6 +204,33 @@ impl<'a> LKEval<'a> { }; } + fn cmd_source(&self, out: &mut Vec, source: &String) { + let script = if source.trim().ends_with("|") { + let (cmd, args) = match get_cmd_args_from_command(source.trim().trim_end_matches('|')) { + Ok(c) => c, + Err(e) => { out.push(format!("error: failed to parse command {:?}: {}", source, e.to_string())); return; }, + }; + match call_cmd_with_input(&cmd, &args, "") { + Ok(o) => o, + Err(e) => { out.push(format!("error: failed to execute command {}: {}", cmd, e.to_string())); return; }, + } + } else { + let script = PathBuf::from(source); + match std::fs::read_to_string(script) { + Ok(script) => script, + Err(err) => { out.push(format!("error: failed to read file {}: {}", source, err.to_string())); return; } + } + }; + match command_parser::script(&script) { + Ok(cmd_list) => { + for cmd in cmd_list { + LKEval::new(cmd, self.state.clone(), prompt_password).eval().print(); + } + } + Err(err) => { out.push(format!("error: {}", err.to_string())); return; } + }; + } + fn cmd_ls(&self, out: &mut Vec, filter: String) { let re = match Regex::new(&filter) { Ok(re) => re, @@ -268,6 +296,7 @@ impl<'a> LKEval<'a> { }, Command::Enc(name) => self.cmd_enc(&mut out, name), Command::PasteBuffer(command) => self.cmd_pb(&mut out, command), + Command::Source(script) => self.cmd_source(&mut out, script), Command::Pass(name) => match self.get_password(name) { Some(p) => { self.state.borrow_mut().secrets.insert( diff --git a/src/structs.rs b/src/structs.rs index e606242..ab686c0 100644 --- a/src/structs.rs +++ b/src/structs.rs @@ -27,6 +27,7 @@ pub enum Command<'a> { Enc(Name), Pass(Name), PasteBuffer(String), + Source(String), Comment(Name, Comment), Error(LKErr<'a>), Noop, diff --git a/src/utils.rs b/src/utils.rs index 4020c8c..b001ab1 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -23,6 +23,14 @@ pub fn call_cmd_with_input(cmd: &str, args: &Vec, input: &str) -> io::Re } } +pub fn get_cmd_args_from_command(command: &str) -> io::Result<(String, Vec)> { + let args = match split(command) { + Some(c) => c, + None => return Err(io::Error::new(io::ErrorKind::InvalidData, format!("Failed to parse the command: {:?}", command))), + }; + Ok((args[0].clone(), args[1..].to_vec())) +} + pub fn get_copy_command_from_env() -> (String, Vec) { let cmd_os_str = env::var_os("LESSKEY_PB").unwrap_or_else(|| match env::consts::OS { _ if env::var("TMUX").is_ok() => OsString::from("tmux load-buffer -"), @@ -30,8 +38,7 @@ pub fn get_copy_command_from_env() -> (String, Vec) { "linux" => OsString::from("xclip"), _ => OsString::from("cat"), }); - let args = split(&cmd_os_str.to_string_lossy()).unwrap_or_else(|| vec!["cat".to_string()]); - (args[0].clone(), args[1..].to_vec()) + get_cmd_args_from_command(&cmd_os_str.to_string_lossy()).unwrap_or_else(|_| ("cat".to_string(), vec![])) } #[cfg(test)]