From 18709b1ede5ce083eae19798bc51fdc28c61d73c Mon Sep 17 00:00:00 2001 From: Matthias Reitinger Date: Fri, 3 Nov 2017 01:39:03 +0100 Subject: [PATCH] Remove shell with --exec --- Cargo.lock | 7 -- Cargo.toml | 1 - src/app.rs | 4 +- src/exec/input.rs | 209 +++++++++++++-------------------------- src/exec/job.rs | 9 +- src/exec/mod.rs | 242 +++++++++++++++++++++++---------------------- src/exec/ticket.rs | 55 ++++------- src/main.rs | 3 +- 8 files changed, 219 insertions(+), 311 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e80467d..b5a1da3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13,7 +13,6 @@ dependencies = [ "num_cpus 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "regex-syntax 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "shell-escape 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "version_check 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -228,11 +227,6 @@ dependencies = [ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "shell-escape" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "strsim" version = "0.6.0" @@ -365,7 +359,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1731164734096285ec2a5ec7fea5248ae2f5485b3feeb0115af4fda2183b2d1b" "checksum regex-syntax 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ad890a5eef7953f55427c50575c680c42841653abd2b028b68cd223d157f62db" "checksum same-file 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d931a44fdaa43b8637009e7632a02adc4f2b2e0733c08caa4cf00e8da4a117a7" -"checksum shell-escape 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "dd5cc96481d54583947bfe88bf30c23d53f883c6cd0145368b69989d97b84ef8" "checksum strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d15c810519a91cf877e7e36e63fe068815c678181439f2f29e2562147c3694" "checksum tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "87974a6f5c1dfb344d733055601650059a3363de2a6104819293baff662132d6" "checksum term_size 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2b6b55df3198cc93372e85dd2ed817f0e38ce8cc0f22eb32391bfad9c4bf209" diff --git a/Cargo.toml b/Cargo.toml index 378f3e0..65b33ec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,7 +40,6 @@ lazy_static = "0.2.9" num_cpus = "1.6.2" regex = "0.2" regex-syntax = "0.4" -shell-escape = "0.1.3" [target.'cfg(all(unix, not(target_os = "redox")))'.dependencies] libc = "0.2" diff --git a/src/app.rs b/src/app.rs index 9365e75..a8da659 100644 --- a/src/app.rs +++ b/src/app.rs @@ -85,7 +85,9 @@ pub fn build_app() -> App<'static, 'static> { arg("exec") .long("exec") .short("x") - .takes_value(true) + .multiple(true) + .allow_hyphen_values(true) + .value_terminator(";") .value_name("cmd"), ) .arg( diff --git a/src/exec/input.rs b/src/exec/input.rs index 1fec91d..0edd1a9 100644 --- a/src/exec/input.rs +++ b/src/exec/input.rs @@ -7,228 +7,159 @@ // according to those terms. use std::path::MAIN_SEPARATOR; -use std::borrow::Cow; -use shell_escape::escape; +/// Removes the parent component of the path +pub fn basename(path: &str) -> &str { + let mut index = 0; + for (id, character) in path.char_indices() { + if character == MAIN_SEPARATOR { + index = id; + } + } -/// A builder for efficiently generating input strings. -/// -/// After choosing your required specs, the `get()` method will escape special characters found -/// in the input. Allocations will only occur if special characters are found that need to be -/// escaped. -pub struct Input<'a> { - data: &'a str, + // FIXME: On Windows, should return what for C:file.txt D:file.txt and \\server\share ? + if index != 0 { + return &path[index + 1..]; + } + + path } -impl<'a> Input<'a> { - /// Creates a new `Input` structure, which provides access to command-building - /// primitives, such as `basename()` and `dirname()`. - pub fn new(data: &'a str) -> Input<'a> { - Input { data } +/// Removes the extension from the path +pub fn remove_extension(path: &str) -> &str { + let mut has_dir = false; + let mut dir_index = 0; + let mut ext_index = 0; + + for (id, character) in path.char_indices() { + if character == MAIN_SEPARATOR { + has_dir = true; + dir_index = id; + } + if character == '.' { + ext_index = id; + } } - /// Removes the parent component of the path - pub fn basename(&'a mut self) -> &'a mut Self { - let mut index = 0; - for (id, character) in self.data.char_indices() { - if character == MAIN_SEPARATOR { - index = id; - } - } - - // FIXME: On Windows, should return what for C:file.txt D:file.txt and \\server\share ? - if index != 0 { - self.data = &self.data[index + 1..] - } - - self + // Account for hidden files and directories + if ext_index != 0 && (!has_dir || dir_index + 2 <= ext_index) { + return &path[0..ext_index]; } - /// Removes the extension from the path - pub fn remove_extension(&'a mut self) -> &'a mut Self { - let mut has_dir = false; - let mut dir_index = 0; - let mut ext_index = 0; + path +} - for (id, character) in self.data.char_indices() { - if character == MAIN_SEPARATOR { - has_dir = true; - dir_index = id; - } - if character == '.' { - ext_index = id; - } +/// Removes the basename from the path. +pub fn dirname(path: &str) -> &str { + let mut has_dir = false; + let mut index = 0; + for (id, character) in path.char_indices() { + if character == MAIN_SEPARATOR { + has_dir = true; + index = id; } - - // Account for hidden files and directories - if ext_index != 0 && (!has_dir || dir_index + 2 <= ext_index) { - self.data = &self.data[0..ext_index]; - } - - self } - /// Removes the basename from the path. - pub fn dirname(&'a mut self) -> &'a mut Self { - let mut has_dir = false; - let mut index = 0; - for (id, character) in self.data.char_indices() { - if character == MAIN_SEPARATOR { - has_dir = true; - index = id; - } - } - - // FIXME: On Windows, return what for C:file.txt D:file.txt and \\server\share ? - self.data = if !has_dir { - "." - } else if index == 0 { - &self.data[..1] - } else { - &self.data[0..index] - }; - - self - } - - pub fn get(&'a self) -> Cow<'a, str> { - escape(Cow::Borrowed(self.data)) - } - - #[cfg(test)] - fn get_private(&'a self) -> Cow<'a, str> { - Cow::Borrowed(self.data) + // FIXME: On Windows, return what for C:file.txt D:file.txt and \\server\share ? + if !has_dir { + "." + } else if index == 0 { + &path[..1] + } else { + &path[0..index] } } #[cfg(test)] mod tests { - use super::{MAIN_SEPARATOR, Input}; + use super::{MAIN_SEPARATOR, basename, dirname, remove_extension}; fn correct(input: &str) -> String { - let mut sep = String::new(); - sep.push(MAIN_SEPARATOR); - input.replace('/', &sep) + input.replace('/', &MAIN_SEPARATOR.to_string()) } #[test] fn path_remove_ext_simple() { - assert_eq!( - &Input::new("foo.txt").remove_extension().get_private(), - "foo" - ); + assert_eq!(remove_extension("foo.txt"), "foo"); } #[test] fn path_remove_ext_dir() { assert_eq!( - &Input::new(&correct("dir/foo.txt")) - .remove_extension() - .get_private(), - &correct("dir/foo") + remove_extension(&correct("dir/foo.txt")), + correct("dir/foo") ); } #[test] fn path_hidden() { - assert_eq!(&Input::new(".foo").remove_extension().get_private(), ".foo") + assert_eq!(remove_extension(".foo"), ".foo") } #[test] fn path_remove_ext_utf8() { - assert_eq!( - &Input::new("💖.txt").remove_extension().get_private(), - "💖" - ); + assert_eq!(remove_extension("💖.txt"), "💖"); } #[test] fn path_remove_ext_empty() { - assert_eq!(&Input::new("").remove_extension().get_private(), ""); + assert_eq!(remove_extension(""), ""); } #[test] fn path_basename_simple() { - assert_eq!(&Input::new("foo.txt").basename().get_private(), "foo.txt"); + assert_eq!(basename("foo.txt"), "foo.txt"); } #[test] fn path_basename_no_ext() { - assert_eq!( - &Input::new("foo.txt") - .basename() - .remove_extension() - .get_private(), - "foo" - ); + assert_eq!(remove_extension(basename("foo.txt")), "foo"); } #[test] fn path_basename_dir() { - assert_eq!( - &Input::new(&correct("dir/foo.txt")).basename().get_private(), - "foo.txt" - ); + assert_eq!(basename(&correct("dir/foo.txt")), "foo.txt"); } #[test] fn path_basename_empty() { - assert_eq!(&Input::new("").basename().get_private(), ""); + assert_eq!(basename(""), ""); } #[test] fn path_basename_utf8() { - assert_eq!( - &Input::new(&correct("💖/foo.txt")) - .basename() - .get_private(), - "foo.txt" - ); - assert_eq!( - &Input::new(&correct("dir/💖.txt")) - .basename() - .get_private(), - "💖.txt" - ); + assert_eq!(basename(&correct("💖/foo.txt")), "foo.txt"); + assert_eq!(basename(&correct("dir/💖.txt")), "💖.txt"); } #[test] fn path_dirname_simple() { - assert_eq!(&Input::new("foo.txt").dirname().get_private(), "."); + assert_eq!(dirname("foo.txt"), "."); } #[test] fn path_dirname_dir() { - assert_eq!( - &Input::new(&correct("dir/foo.txt")).dirname().get_private(), - "dir" - ); + assert_eq!(dirname(&correct("dir/foo.txt")), "dir"); } #[test] fn path_dirname_utf8() { - assert_eq!( - &Input::new(&correct("💖/foo.txt")).dirname().get_private(), - "💖" - ); - assert_eq!( - &Input::new(&correct("dir/💖.txt")).dirname().get_private(), - "dir" - ); + assert_eq!(dirname(&correct("💖/foo.txt")), "💖"); + assert_eq!(dirname(&correct("dir/💖.txt")), "dir"); } #[test] fn path_dirname_empty() { - assert_eq!(&Input::new("").dirname().get_private(), "."); + assert_eq!(dirname(""), "."); } #[test] fn path_dirname_root() { #[cfg(windows)] - assert_eq!(&Input::new("C:\\").dirname().get_private(), "C:"); + assert_eq!(dirname("C:\\"), "C:"); #[cfg(windows)] - assert_eq!(&Input::new("\\").dirname().get_private(), "\\"); + assert_eq!(dirname("\\"), "\\"); #[cfg(not(windows))] - assert_eq!(&Input::new("/").dirname().get_private(), "/"); + assert_eq!(dirname("/"), "/"); } } diff --git a/src/exec/job.rs b/src/exec/job.rs index 4ca518e..42e3882 100644 --- a/src/exec/job.rs +++ b/src/exec/job.rs @@ -20,9 +20,6 @@ pub fn job( cmd: Arc, out_perm: Arc>, ) { - // A string buffer that will be re-used in each iteration. - let buffer = &mut String::with_capacity(256); - loop { // Create a lock on the shared receiver for this thread. let lock = rx.lock().unwrap(); @@ -36,9 +33,7 @@ pub fn job( // Drop the lock so that other threads can read from the the receiver. drop(lock); - // Generate a command to store within the buffer, and execute the command. - // Note that the `then_execute()` method will clear the buffer for us. - cmd.generate(buffer, &value, Arc::clone(&out_perm)) - .then_execute(); + // Generate a command and execute it. + cmd.generate(&value, Arc::clone(&out_perm)).then_execute(); } } diff --git a/src/exec/mod.rs b/src/exec/mod.rs index 0fff487..a29be4c 100644 --- a/src/exec/mod.rs +++ b/src/exec/mod.rs @@ -12,163 +12,167 @@ mod token; mod job; mod input; +use std::borrow::Cow; use std::path::Path; use std::sync::{Arc, Mutex}; -// use self::paths::{basename, dirname, remove_extension}; -use self::input::Input; +use regex::Regex; + +use self::input::{basename, dirname, remove_extension}; use self::ticket::CommandTicket; use self::token::Token; pub use self::job::job; -/// Signifies that a placeholder token was found -const PLACE: u8 = 1; - -/// Signifies that the '{' character was found. -const OPEN: u8 = 2; - -/// Contains a collection of `Token`'s that are utilized to generate command strings. +/// Contains a collection of `TokenizedArgument`s that are utilized to generate command strings. /// -/// The tokens are a represntation of the supplied command template, and are meant to be coupled -/// with an input in order to generate a command. The `generate()` method will be used to -/// generate a command and obtain a ticket for executing that command. +/// The arguments are a representation of the supplied command template, and are meant to be coupled +/// with an input in order to generate a command. The `generate()` method will be used to generate +/// a command and obtain a ticket for executing that command. #[derive(Debug, Clone, PartialEq)] pub struct TokenizedCommand { - pub tokens: Vec, + args: Vec, +} + +/// Represents a single command argument. +/// +/// The argument is either a collection of `Token`s including at least one placeholder variant, +/// or a fixed text. +#[derive(Clone, Debug, PartialEq)] +enum TokenizedArgument { + Tokens(Vec), + Text(String), +} + +impl TokenizedArgument { + pub fn generate<'a>(&'a self, path: &str) -> Cow<'a, str> { + use self::Token::*; + + match *self { + TokenizedArgument::Tokens(ref tokens) => { + let mut s = String::new(); + for token in tokens { + match *token { + Basename => s += basename(path), + BasenameNoExt => s += remove_extension(basename(path)), + NoExt => s += remove_extension(path), + Parent => s += dirname(path), + Placeholder => s += path, + Text(ref string) => s += string, + } + } + Cow::Owned(s) + } + TokenizedArgument::Text(ref text) => Cow::Borrowed(text), + } + } } impl TokenizedCommand { - pub fn new(input: &str) -> TokenizedCommand { - let mut tokens = Vec::new(); - let mut start = 0; - let mut flags = 0; - let mut chars = input.char_indices(); - let mut text = String::new(); - - while let Some((id, character)) = chars.next() { - match character { - // Backslashes are useful in cases where we want to use the '{' character - // without having all occurrences of it to collect placeholder tokens. - '\\' => { - if let Some((_, nchar)) = chars.next() { - if nchar != '{' { - text.push(character); - } - text.push(nchar); - } - } - // When a raw '{' is discovered, we will note it's position, and use that for a - // later comparison against valid placeholder tokens. - '{' if flags & OPEN == 0 => { - flags |= OPEN; - start = id; - if !text.is_empty() { - append(&mut tokens, &text); - text.clear(); - } - } - // If the `OPEN` bit is set, we will compare the contents between the discovered - // '{' and '}' characters against a list of valid tokens, then pushing the - // corresponding token onto the `tokens` vector. - '}' if flags & OPEN != 0 => { - flags ^= OPEN; - match &input[start + 1..id] { - "" => tokens.push(Token::Placeholder), - "." => tokens.push(Token::NoExt), - "/" => tokens.push(Token::Basename), - "//" => tokens.push(Token::Parent), - "/." => tokens.push(Token::BasenameNoExt), - _ => { - append(&mut tokens, &input[start..id + 1]); - continue; - } - } - flags |= PLACE; - } - // We aren't collecting characters for a text string if the `OPEN` bit is set. - _ if flags & OPEN != 0 => (), - // Push the character onto the text buffer - _ => text.push(character), - } + pub fn new(input: I) -> TokenizedCommand + where + I: IntoIterator, + S: AsRef, + { + lazy_static! { + static ref PLACEHOLDER: Regex = Regex::new(r"\{(/?\.?|//)\}").unwrap(); } - // Take care of any stragglers left behind. - if !text.is_empty() { - append(&mut tokens, &text); + let mut args = Vec::new(); + let mut has_placeholder = false; + + for arg in input { + let arg = arg.as_ref(); + + let mut tokens = Vec::new(); + let mut start = 0; + + for placeholder in PLACEHOLDER.find_iter(arg) { + // Leading text before the placeholder. + if placeholder.start() > start { + tokens.push(Token::Text(arg[start..placeholder.start()].to_owned())); + } + + start = placeholder.end(); + + match placeholder.as_str() { + "{}" => tokens.push(Token::Placeholder), + "{.}" => tokens.push(Token::NoExt), + "{/}" => tokens.push(Token::Basename), + "{//}" => tokens.push(Token::Parent), + "{/.}" => tokens.push(Token::BasenameNoExt), + _ => panic!("Unhandled placeholder"), + } + + has_placeholder = true; + } + + // Without a placeholder, the argument is just fixed text. + if tokens.is_empty() { + args.push(TokenizedArgument::Text(arg.to_owned())); + continue; + } + + if start < arg.len() { + // Trailing text after last placeholder. + tokens.push(Token::Text(arg[start..].to_owned())); + } + + args.push(TokenizedArgument::Tokens(tokens)); } // If a placeholder token was not supplied, append one at the end of the command. - if flags & PLACE == 0 { - append(&mut tokens, " "); - tokens.push(Token::Placeholder) + if !has_placeholder { + args.push(TokenizedArgument::Tokens(vec![Token::Placeholder])); } - TokenizedCommand { tokens: tokens } + TokenizedCommand { args: args } } /// Generates a ticket that is required to execute the generated command. /// - /// Using the internal `tokens` field, and a supplied `input` variable, commands will be - /// written into the `command` buffer. Once all tokens have been processed, the mutable - /// reference of the `command` will be wrapped within a `CommandTicket`, which will be - /// responsible for executing the command and clearing the buffer. - pub fn generate<'a>( - &self, - command: &'a mut String, - input: &Path, - out_perm: Arc>, - ) -> CommandTicket<'a> { - use self::Token::*; - let input = input.strip_prefix(".").unwrap_or(input).to_string_lossy(); - for token in &self.tokens { - match *token { - Basename => *command += &Input::new(&input).basename().get(), - BasenameNoExt => { - *command += &Input::new(&input).basename().remove_extension().get() - } - NoExt => *command += &Input::new(&input).remove_extension().get(), - Parent => *command += &Input::new(&input).dirname().get(), - Placeholder => *command += &Input::new(&input).get(), - Text(ref string) => *command += string, - } + /// Using the internal `args` field, and a supplied `input` variable, arguments will be + /// collected in a Vec. Once all arguments have been processed, the Vec will be wrapped + /// within a `CommandTicket`, which will be responsible for executing the command. + pub fn generate(&self, input: &Path, out_perm: Arc>) -> CommandTicket { + let input = input + .strip_prefix(".") + .unwrap_or(input) + .to_string_lossy() + .into_owned(); + + let mut args = Vec::with_capacity(self.args.len()); + for arg in &self.args { + args.push(arg.generate(&input)); } - CommandTicket::new(command, out_perm) - } -} - -/// If the last token is a text token, append to that token. Otherwise, create a new token. -fn append(tokens: &mut Vec, elem: &str) { - // Useful to avoid a borrowing issue with the tokens vector. - let mut append_text = false; - - // If the last token is a `Text` token, simply the `elem` at the end. - match tokens.last_mut() { - Some(&mut Token::Text(ref mut string)) => *string += elem, - _ => append_text = true, - }; - - // Otherwise, we will need to add a new `Text` token that contains the `elem` - if append_text { - tokens.push(Token::Text(String::from(elem))); + CommandTicket::new(args, out_perm) } } #[cfg(test)] mod tests { - use super::{TokenizedCommand, Token}; + use super::{TokenizedCommand, TokenizedArgument, Token}; #[test] fn tokens() { let expected = TokenizedCommand { - tokens: vec![Token::Text("echo ${SHELL}: ".into()), Token::Placeholder], + args: vec![ + TokenizedArgument::Text("echo".into()), + TokenizedArgument::Text("${SHELL}:".into()), + TokenizedArgument::Tokens(vec![Token::Placeholder]), + ], }; - assert_eq!(TokenizedCommand::new("echo $\\{SHELL}: {}"), expected); - assert_eq!(TokenizedCommand::new("echo ${SHELL}:"), expected); + assert_eq!(TokenizedCommand::new(&[&"echo", &"${SHELL}:"]), expected); + assert_eq!( - TokenizedCommand::new("echo {.}"), - TokenizedCommand { tokens: vec![Token::Text("echo ".into()), Token::NoExt] } + TokenizedCommand::new(&["echo", "{.}"]), + TokenizedCommand { + args: vec![ + TokenizedArgument::Text("echo".into()), + TokenizedArgument::Tokens(vec![Token::NoExt]), + ], + } ); } } diff --git a/src/exec/ticket.rs b/src/exec/ticket.rs index 7e3dcaf..e9982c7 100644 --- a/src/exec/ticket.rs +++ b/src/exec/ticket.rs @@ -6,47 +6,39 @@ // notice may not be copied, modified, or distributed except // according to those terms. -use std::env; use std::process::Command; use std::sync::{Arc, Mutex}; use std::io; -lazy_static! { - /// On non-Windows systems, the `SHELL` environment variable will be used to determine the - /// preferred shell of choice for execution. Windows will simply use `cmd`. - static ref COMMAND: (String, &'static str) = if cfg!(target_os = "windows") { - ("cmd".into(), "/C") - } else { - (env::var("SHELL").unwrap_or_else(|_| "/bin/sh".into()), "-c") - }; -} - /// A state that offers access to executing a generated command. /// -/// The ticket holds a mutable reference to a string that contains the command to be executed. -/// After execution of the the command via the `then_execute()` method, the string will be -/// cleared so that a new command can be written to the string in the future. -pub struct CommandTicket<'a> { - command: &'a mut String, +/// The ticket holds the collection of arguments of a command to be executed. +pub struct CommandTicket { + args: Vec, out_perm: Arc>, } -impl<'a> CommandTicket<'a> { - pub fn new(command: &'a mut String, out_perm: Arc>) -> CommandTicket<'a> { - CommandTicket { command, out_perm } +impl CommandTicket { + pub fn new(args: I, out_perm: Arc>) -> CommandTicket + where + I: IntoIterator, + S: AsRef, + { + CommandTicket { + args: args.into_iter().map(|x| x.as_ref().to_owned()).collect(), + out_perm: out_perm, + } } - /// Executes the command stored within the ticket, and - /// clearing the command's buffer when finished.' + /// Executes the command stored within the ticket. #[cfg(not(unix))] pub fn then_execute(self) { use std::process::Stdio; use std::io::Write; - // Spawn a shell with the supplied command. - let cmd = Command::new(COMMAND.0.as_str()) - .arg(COMMAND.1) - .arg(&self.command) + // Spawn the supplied command. + let cmd = Command::new(&self.args[0]) + .args(&self.args[1..]) .stdout(Stdio::piped()) .stderr(Stdio::piped()) .output(); @@ -66,9 +58,6 @@ impl<'a> CommandTicket<'a> { } Err(why) => eprintln!("fd: exec error: {}", why), } - - // Clear the buffer for later re-use. - self.command.clear(); } #[cfg(all(unix))] @@ -88,10 +77,9 @@ impl<'a> CommandTicket<'a> { pipe(stderr_fds.as_mut_ptr()); } - // Spawn a shell with the supplied command. - let cmd = Command::new(COMMAND.0.as_str()) - .arg(COMMAND.1) - .arg(&self.command) + // Spawn the supplied command. + let cmd = Command::new(&self.args[0]) + .args(&self.args[1..]) // Configure the pipes accordingly in the child. .before_exec(move || unsafe { // Redirect the child's std{out,err} to the write ends of our pipe. @@ -134,8 +122,5 @@ impl<'a> CommandTicket<'a> { } Err(why) => eprintln!("fd: exec error: {}", why), } - - // Clear the command string's buffer for later re-use. - self.command.clear(); } } diff --git a/src/main.rs b/src/main.rs index e5d9ff7..c1d56fe 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,7 +18,6 @@ extern crate libc; extern crate num_cpus; extern crate regex; extern crate regex_syntax; -extern crate shell_escape; pub mod fshelper; pub mod lscolors; @@ -106,7 +105,7 @@ fn main() { None }; - let command = matches.value_of("exec").map(|x| TokenizedCommand::new(x)); + let command = matches.values_of("exec").map(TokenizedCommand::new); let config = FdOptions { case_sensitive,