From 428bd9db5c3625d3d1dc96b48388ac1ac6a7bc8c Mon Sep 17 00:00:00 2001 From: Nick Hackman Date: Mon, 27 May 2019 11:13:13 -0400 Subject: [PATCH 1/8] Feat globs can be relative for newglob and cli arg Globs are now normalized and can be relative --- src/cli.rs | 17 +++++++- src/lib.rs | 85 +++++++++++++++++++++++++++++++++---- src/program/command_mode.rs | 56 ++++++++++-------------- src/program/mod.rs | 5 +-- 4 files changed, 114 insertions(+), 49 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index 6f5e882..11d1a0a 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -5,6 +5,7 @@ use crate::sort::SortOrder; use clap::{App, Arg}; use glob::glob; +use std::env::current_dir; use std::path::PathBuf; /// Args contains the arguments that have been successfully parsed by the clap cli app @@ -21,6 +22,8 @@ pub struct Args { pub max_length: usize, /// Start in fullscreen mode pub fullscreen: bool, + /// New base directory defaults to std::env::current_dir + pub base_dir: PathBuf, } /// cli sets up the command line app and parses the arguments, using clap. @@ -83,8 +86,17 @@ pub fn cli() -> Result { Some(v) => v, None => panic!("No value for paths!"), }; - let path_glob = crate::convert_to_globable(path_glob)?; - let glob_matches = glob(&path_glob).map_err(|e| e.to_string())?; + // find current directory so glob provided can be relative + let mut base_dir = match current_dir() { + Ok(c) => c, + Err(_) => PathBuf::new(), + }; + let path_glob = crate::path_to_glob(&base_dir, path_glob)?; + // find new base directory + if let Ok(new_base_dir) = crate::new_base_dir(&path_glob) { + base_dir = new_base_dir; + } + let glob_matches = glob(&path_glob.to_string_lossy()).map_err(|e| e.to_string())?; for path in glob_matches { match path { Ok(p) => push_image_path(&mut files, p), @@ -117,6 +129,7 @@ pub fn cli() -> Result { reverse, max_length, fullscreen, + base_dir, }) } diff --git a/src/lib.rs b/src/lib.rs index cd5d184..597d852 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -24,18 +24,85 @@ pub mod sort; pub mod ui; use shellexpand::full; +use std::fs::canonicalize; use std::path::PathBuf; -/// Converts the provided path by user to a path that can be glob'd +/// Converts the provided path by user to a path that can be glob'd, note this function takes the +/// current_directory in order to handle relative paths +/// Paths are normalized removing ".." and "." +/// Environment variables, like ~ and $HOME, are expanded +/// On Unix escaped spaces are removed for example: folder\ path -> folder path /// Directories are changed from /home/etc to /home/etc/* -pub fn convert_to_globable(path: &str) -> Result { - let expanded_path = full(path).map_err(|e| format!("\"{}\": {}", e.var_name, e.cause))?; - // remove escaped spaces - let absolute_path = String::from(expanded_path).replace(r"\ ", " "); +pub fn path_to_glob(current_dir: &PathBuf, path: &str) -> Result { + const ESCAPED_SPACE: &str = r"\ "; + const SPACE: &str = " "; + + let mut expanded_path = match full(path) { + Ok(path) => { + let mut path_str = path.to_string(); + // remove escaped spaces for Unix + if cfg!(unix) { + path_str = path_str.replace(ESCAPED_SPACE, SPACE); + } + PathBuf::from(&path_str) + } + Err(e) => return Err(format!("\"{}\": {}", e.var_name, e.cause)), + }; + + if expanded_path.is_relative() { + expanded_path = current_dir.join(expanded_path); + } + // normalize path + let mut expanded_path = match normalize_path(expanded_path) { + Ok(path) => path, + Err(e) => return Err(e.to_string()), + }; // If path is a dir, add /* to glob - let mut pathbuf = PathBuf::from(&absolute_path); - if pathbuf.is_dir() { - pathbuf = pathbuf.join("*"); + if expanded_path.is_dir() { + expanded_path.push("*"); + } + Ok(expanded_path) +} + +/// Normalizes paths removing "." and ".." +/// This is a helper function to path_to_glob +fn normalize_path(path: PathBuf) -> Result { + if let Ok(path) = canonicalize(&path) { + Ok(path) + } else { + // if canonicalize failed it's most likely because the path contains '*'s + // remove those and see if it is successful, and add them back on if it is + use std::ffi::OsStr; + + let mut stack: Vec<&OsStr> = Vec::new(); + let mut invalid_expanded_path = path.clone(); + for parent in path.ancestors() { + if let Some(child) = parent.file_name() { + if invalid_expanded_path.exists() { + break; + } + stack.push(child); + } else { + // parent is '..' remove it + invalid_expanded_path.pop(); + } + invalid_expanded_path.pop(); + } + let mut new_path = canonicalize(&invalid_expanded_path).map_err(|e| e.to_string())?; + while let Some(sub_path) = stack.pop() { + new_path.push(sub_path); + } + Ok(new_path) + } +} + +/// Takes in the output of path_to_glob and finds the closest parent in that path +/// This is the new base directory +pub fn new_base_dir(path: &PathBuf) -> Result { + for parent in path.ancestors() { + if parent.is_dir() { + return Ok(parent.to_path_buf()); + } } - Ok(pathbuf.to_string_lossy().to_string()) + Err(format!("Failed to get new base directory")) } diff --git a/src/program/command_mode.rs b/src/program/command_mode.rs index cdaa42b..aa36b4e 100644 --- a/src/program/command_mode.rs +++ b/src/program/command_mode.rs @@ -69,12 +69,11 @@ impl FromStr for Commands { /// Globs the passed path, returning an error if no images are in that path, glob::glob fails, or /// path is unexpected -fn glob_path(path: &str) -> Result, String> { +fn glob_path(path: &PathBuf) -> Result, String> { use crate::cli::push_image_path; let mut new_images: Vec = Vec::new(); - let globable_path = crate::convert_to_globable(path)?; - let path_matches = glob::glob(&globable_path).map_err(|e| e.to_string())?; + let path_matches = glob::glob(&path.to_string_lossy()).map_err(|e| e.to_string())?; for path in path_matches { match path { Ok(p) => { @@ -86,10 +85,8 @@ fn glob_path(path: &str) -> Result, String> { } } } - if path.find(' ').is_some() && new_images.is_empty() { - return Err("Newglob accepts only one argument, but more were provided".to_string()); - } else if new_images.is_empty() { - let err_msg = format!("Path \"{}\" had no images", path); + if new_images.is_empty() { + let err_msg = format!("Path \"{}\" had no images", path.display()); return Err(err_msg); } Ok(new_images) @@ -117,28 +114,6 @@ fn parse_user_input(input: String) -> Result<(Commands, String), String> { Ok((command, arguments)) } -/// When provided a newglob set current_dir to the nearest directory -fn find_new_base_dir(new_path: &str) -> Option { - let expanded_path = match full(new_path) { - Ok(path) => path, - Err(_e) => { - return None; - } - }; - let pathbuf = PathBuf::from(&expanded_path.to_string()); - if pathbuf.is_dir() { - Some(pathbuf) - } else { - // Provided newglob is a path to an image or a glob - for parent in pathbuf.ancestors() { - if parent.is_dir() { - return Some(parent.to_path_buf()); - } - } - None - } -} - impl<'a> Program<'a> { /// User input is taken in and displayed on infobar, cmd is either '/' or ':' /// Returning empty string signifies switching modes back to normal mode @@ -177,7 +152,14 @@ impl<'a> Program<'a> { /// Takes a path to a directory or glob and adds these images to self.paths.images fn newglob(&mut self, path_to_newglob: &str) { - let new_images = match glob_path(path_to_newglob) { + let path = match crate::path_to_glob(&self.paths.base_dir, path_to_newglob) { + Ok(path) => path, + Err(e) => { + self.ui_state.mode = Mode::Error(e.to_string()); + return; + } + }; + let new_images = match glob_path(&path) { Ok(new_images) => new_images, Err(e) => { self.ui_state.mode = Mode::Error(e.to_string()); @@ -191,8 +173,8 @@ impl<'a> Program<'a> { }; self.paths.images = new_images; // Set current directory to new one - let new_base_dir = find_new_base_dir(&path_to_newglob.replace("\\ ", " ")); - if let Some(base_dir) = new_base_dir { + let new_base_dir = crate::new_base_dir(&path); + if let Ok(base_dir) = new_base_dir { self.paths.base_dir = base_dir } self.sorter.sort(&mut self.paths.images); @@ -340,8 +322,14 @@ impl<'a> Program<'a> { } match full(&arguments) { Ok(path) => { - self.paths.dest_folder = - PathBuf::from(path.to_string().replace("\\ ", " ")); + const ESCAPED_SPACE: &str = "\\ "; + const SPACE: &str = " "; + + let mut path = path.to_string(); + if cfg!(unix) { + path = path.replace(ESCAPED_SPACE, SPACE); + } + self.paths.dest_folder = PathBuf::from(path); } Err(e) => { self.ui_state.mode = diff --git a/src/program/mod.rs b/src/program/mod.rs index 1af0f92..be5066d 100644 --- a/src/program/mod.rs +++ b/src/program/mod.rs @@ -52,6 +52,7 @@ impl<'a> Program<'a> { let reverse = args.reverse; let sort_order = args.sort_order; let max_length = args.max_length; + let base_dir = args.base_dir; let max_viewable = if max_length > 0 && max_length <= images.len() { max_length @@ -62,10 +63,6 @@ impl<'a> Program<'a> { let sorter = Sorter::new(sort_order, reverse); sorter.sort(&mut images); - let base_dir = match std::env::current_dir() { - Ok(c) => c, - Err(_) => PathBuf::new(), - }; let font_bytes = include_bytes!("../../resources/Roboto-Medium.ttf"); let font_bytes = match RWops::from_bytes(font_bytes) { Ok(b) => b, From 46bc3a7fc3e3ee38b7b2ef87192ce5f742dee1fd Mon Sep 17 00:00:00 2001 From: Nick Hackman Date: Mon, 27 May 2019 15:29:06 -0400 Subject: [PATCH 2/8] Fix output in new_base_dir to be more descriptive --- src/lib.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 597d852..c17cc3e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -104,5 +104,8 @@ pub fn new_base_dir(path: &PathBuf) -> Result { return Ok(parent.to_path_buf()); } } - Err(format!("Failed to get new base directory")) + Err(format!( + "Failed to get new base directory for path: \"{}\"", + path.display() + )) } From ca9a121a6883c7ccb9fb37b838ae2d4aa6bd3969 Mon Sep 17 00:00:00 2001 From: Nick Hackman Date: Wed, 29 May 2019 13:22:31 -0400 Subject: [PATCH 3/8] Normalizing paths is now crossplatform * Stopped using std::fs::canonicalize in favor of a custom implementation, because std::fs::canonicalize requires paths to exist and therefore all globs fail --- src/lib.rs | 54 +++++++++++++++++++++++++----------------------------- 1 file changed, 25 insertions(+), 29 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index c17cc3e..6e2e621 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -24,7 +24,6 @@ pub mod sort; pub mod ui; use shellexpand::full; -use std::fs::canonicalize; use std::path::PathBuf; /// Converts the provided path by user to a path that can be glob'd, note this function takes the @@ -33,9 +32,11 @@ use std::path::PathBuf; /// Environment variables, like ~ and $HOME, are expanded /// On Unix escaped spaces are removed for example: folder\ path -> folder path /// Directories are changed from /home/etc to /home/etc/* +/// Symlinks are followed pub fn path_to_glob(current_dir: &PathBuf, path: &str) -> Result { const ESCAPED_SPACE: &str = r"\ "; const SPACE: &str = " "; + const GLOB: &str = "*"; let mut expanded_path = match full(path) { Ok(path) => { @@ -53,47 +54,42 @@ pub fn path_to_glob(current_dir: &PathBuf, path: &str) -> Result path, - Err(e) => return Err(e.to_string()), - }; + let mut expanded_path = normalize_path(expanded_path); // If path is a dir, add /* to glob if expanded_path.is_dir() { - expanded_path.push("*"); + expanded_path.push(GLOB); } Ok(expanded_path) } /// Normalizes paths removing "." and ".." +/// This follows symlinks like std::fs::canonicalize /// This is a helper function to path_to_glob -fn normalize_path(path: PathBuf) -> Result { - if let Ok(path) = canonicalize(&path) { - Ok(path) - } else { - // if canonicalize failed it's most likely because the path contains '*'s - // remove those and see if it is successful, and add them back on if it is - use std::ffi::OsStr; +/// +/// +/// This implementation is preferred to using std::fs::canonicalize due to canonicalize requiring +/// the path to exist, causing all globbing to fail and having to remove the glob, normalize then add it back on +/// which is quadratic time complexity versus linear in the size of the path +fn normalize_path(path: PathBuf) -> PathBuf { + use std::fs::read_link; + use std::path::Component; - let mut stack: Vec<&OsStr> = Vec::new(); - let mut invalid_expanded_path = path.clone(); - for parent in path.ancestors() { - if let Some(child) = parent.file_name() { - if invalid_expanded_path.exists() { - break; + let mut normalized = PathBuf::new(); + for component in path.components() { + match component { + Component::ParentDir => { + normalized.pop(); + } + Component::CurDir => continue, + _ => { + normalized.push(component); + if let Ok(actual_path) = read_link(&normalized) { + normalized.set_file_name(actual_path); } - stack.push(child); - } else { - // parent is '..' remove it - invalid_expanded_path.pop(); } - invalid_expanded_path.pop(); - } - let mut new_path = canonicalize(&invalid_expanded_path).map_err(|e| e.to_string())?; - while let Some(sub_path) = stack.pop() { - new_path.push(sub_path); } - Ok(new_path) } + normalized } /// Takes in the output of path_to_glob and finds the closest parent in that path From 205413fc64d1c630c11ad828e546a131390bdffe Mon Sep 17 00:00:00 2001 From: Nick Hackman Date: Wed, 29 May 2019 13:26:43 -0400 Subject: [PATCH 4/8] Fixed documentation --- src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 6e2e621..7a76a0f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -69,7 +69,6 @@ pub fn path_to_glob(current_dir: &PathBuf, path: &str) -> Result PathBuf { use std::fs::read_link; use std::path::Component; From 3a611d71e764a2aeff0303f86fd660493cc2ae1f Mon Sep 17 00:00:00 2001 From: Nick Hackman Date: Wed, 29 May 2019 15:16:00 -0400 Subject: [PATCH 5/8] For Unix now all escaped sequences are covered * Before only escaped spaces were covered, but now all escape sequences are properly evaluated. --- Cargo.lock | 60 +++++++++++++++++++++++++++++++++++++ Cargo.toml | 2 ++ src/lib.rs | 28 ++++++++++++++--- src/program/command_mode.rs | 15 +++++++--- 4 files changed, 97 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 77e780b..1bd6e87 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,13 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "aho-corasick" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "ansi_term" version = "0.11.0" @@ -80,6 +88,11 @@ name = "libc" version = "0.2.53" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "memchr" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "natord" version = "1.0.9" @@ -239,6 +252,26 @@ dependencies = [ "redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "regex" +version = "1.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "aho-corasick 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", + "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "regex-syntax" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "riv" version = "0.2.0" @@ -246,7 +279,9 @@ dependencies = [ "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", "fs_extra 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "natord 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "sdl2 0.32.2 (registry+https://github.com/rust-lang/crates.io-index)", "shellexpand 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -302,11 +337,29 @@ dependencies = [ "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "thread_local" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ucd-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "unicode-width" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "utf8-ranges" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "vec_map" version = "0.8.1" @@ -332,6 +385,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] +"checksum aho-corasick 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e6f484ae0c99fec2e858eb6134949117399f222608d84cadb3f58c1f97c2364c" "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" "checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" "checksum autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a6d640bee2da49f60a4068a7fae53acde8982514ab7bae8b8cea9e88cbcfd799" @@ -344,6 +398,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" "checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14" "checksum libc 0.2.53 (registry+https://github.com/rust-lang/crates.io-index)" = "ec350a9417dfd244dc9a6c4a71e13895a4db6b92f0b106f07ebbc3f3bc580cee" +"checksum memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2efc7bc57c883d4a4d6e3246905283d8dae951bb3bd32f49d6ef297f546e1c39" "checksum natord 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "308d96db8debc727c3fd9744aac51751243420e46edf401010908da7f8d5e57c" "checksum num 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "4703ad64153382334aa8db57c637364c322d3372e097840c72000dabdcf6156e" "checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea" @@ -363,13 +418,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" "checksum redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)" = "12229c14a0f65c4f1cb046a3b52047cdd9da1f4b30f8a39c5063c8bae515e252" "checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" +"checksum regex 1.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "8f0a0bcab2fd7d1d7c54fa9eae6f43eddeb9ce2e7352f8518a814a4f65d60c58" +"checksum regex-syntax 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "dcfd8681eebe297b81d98498869d4aae052137651ad7b96822f09ceb690d0a96" "checksum sdl2 0.32.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d051a07231e303f5f719da78cb6f7394f6d5b54f733aef5b0b447804a83edd7b" "checksum sdl2-sys 0.32.6 (registry+https://github.com/rust-lang/crates.io-index)" = "34e71125077d297d57e4c1acfe8981b5bdfbf5a20e7b589abfdcb33bf1127f86" "checksum shellexpand 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de7a5b5a9142fd278a10e0209b021a1b85849352e6951f4f914735c976737564" "checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" "checksum termion 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dde0593aeb8d47accea5392b39350015b5eccb12c0d98044d856983d89548dea" "checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" +"checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86" "checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526" +"checksum utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "796f7e48bef87609f7ade7e06495a87d5cd06c7866e6a5cbfceffc558a243737" "checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" "checksum winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f10e386af2b13e47c89e7236a7a14a086791a2b88ebad6df9bf42040195cf770" "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" diff --git a/Cargo.toml b/Cargo.toml index 16046d9..aee998c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,8 @@ glob = "0.3" fs_extra = "1.1" natord = "1.0.9" shellexpand = "1.0" +lazy_static = "1.3.0" +regex = "1" [dependencies.sdl2] version = "0.32" diff --git a/src/lib.rs b/src/lib.rs index 7a76a0f..622dd67 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,6 +14,9 @@ #[macro_use] extern crate clap; +extern crate regex; +#[macro_use] +extern crate lazy_static; pub mod cli; pub mod infobar; @@ -23,6 +26,7 @@ pub mod screen; pub mod sort; pub mod ui; +use regex::Regex; use shellexpand::full; use std::path::PathBuf; @@ -34,16 +38,22 @@ use std::path::PathBuf; /// Directories are changed from /home/etc to /home/etc/* /// Symlinks are followed pub fn path_to_glob(current_dir: &PathBuf, path: &str) -> Result { - const ESCAPED_SPACE: &str = r"\ "; - const SPACE: &str = " "; const GLOB: &str = "*"; let mut expanded_path = match full(path) { Ok(path) => { let mut path_str = path.to_string(); - // remove escaped spaces for Unix + // remove escaped characters for Unix if cfg!(unix) { - path_str = path_str.replace(ESCAPED_SPACE, SPACE); + lazy_static! { + static ref REGEX_REMOVE_ESCAPED_CHARS: Regex = match Regex::new(r"\\(.)") { + Ok(regex) => regex, + Err(e) => panic!("Logic Error: {}", e), + }; + } + path_str = REGEX_REMOVE_ESCAPED_CHARS + .replace_all(&path_str, "$1") + .to_string(); } PathBuf::from(&path_str) } @@ -91,6 +101,16 @@ fn normalize_path(path: PathBuf) -> PathBuf { normalized } +#[test] +fn escaped_mess() { + let current_dir = PathBuf::from("/home/nick"); + let new_path = r"Down\\\ loads"; + assert_eq!( + path_to_glob(¤t_dir, new_path), + Ok(PathBuf::from(r"/home/nick/Down\\\ loads/*")) + ); +} + /// Takes in the output of path_to_glob and finds the closest parent in that path /// This is the new base directory pub fn new_base_dir(path: &PathBuf) -> Result { diff --git a/src/program/command_mode.rs b/src/program/command_mode.rs index aa36b4e..5b876ec 100644 --- a/src/program/command_mode.rs +++ b/src/program/command_mode.rs @@ -3,6 +3,7 @@ use super::Program; use crate::sort::SortOrder; use crate::ui::{process_command_mode, Action, Mode}; +use regex::Regex; use shellexpand::full; use std::path::PathBuf; use std::str::FromStr; @@ -322,12 +323,18 @@ impl<'a> Program<'a> { } match full(&arguments) { Ok(path) => { - const ESCAPED_SPACE: &str = "\\ "; - const SPACE: &str = " "; - let mut path = path.to_string(); if cfg!(unix) { - path = path.replace(ESCAPED_SPACE, SPACE); + lazy_static! { + static ref REGEX_REMOVE_ESCAPED_CHARS: Regex = + match Regex::new(r"\\(.)") { + Ok(regex) => regex, + Err(e) => panic!("Logic Error: {}", e), + }; + } + path = REGEX_REMOVE_ESCAPED_CHARS + .replace_all(&path, "$1") + .to_string(); } self.paths.dest_folder = PathBuf::from(path); } From 580f406d6ff10782b3b328a2a7151c73fc69d119 Mon Sep 17 00:00:00 2001 From: Nick Hackman Date: Wed, 29 May 2019 15:29:56 -0400 Subject: [PATCH 6/8] I'm bad at merging --- src/program/command_mode.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/program/command_mode.rs b/src/program/command_mode.rs index 7252c8b..1efc95a 100644 --- a/src/program/command_mode.rs +++ b/src/program/command_mode.rs @@ -348,7 +348,7 @@ impl<'a> Program<'a> { .to_string(); } let success_msg = - format!("destination folder successfully set to {}", path.display()); + format!("destination folder successfully set to {}", path); self.paths.dest_folder = PathBuf::from(path); self.ui_state.mode = Mode::Success(success_msg); self.ui_state.rerender_time = Some(Instant::now()); From fea5b6cd49b4b839aa5a61905077cdd185f08061 Mon Sep 17 00:00:00 2001 From: Nick Hackman Date: Wed, 29 May 2019 15:33:39 -0400 Subject: [PATCH 7/8] Accidentally left a test case --- src/lib.rs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 622dd67..2898ee8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -101,16 +101,6 @@ fn normalize_path(path: PathBuf) -> PathBuf { normalized } -#[test] -fn escaped_mess() { - let current_dir = PathBuf::from("/home/nick"); - let new_path = r"Down\\\ loads"; - assert_eq!( - path_to_glob(¤t_dir, new_path), - Ok(PathBuf::from(r"/home/nick/Down\\\ loads/*")) - ); -} - /// Takes in the output of path_to_glob and finds the closest parent in that path /// This is the new base directory pub fn new_base_dir(path: &PathBuf) -> Result { From 0fcd681ccf38c4fda3091ce0ee68b296dbed65ef Mon Sep 17 00:00:00 2001 From: Nick Hackman Date: Wed, 29 May 2019 15:36:57 -0400 Subject: [PATCH 8/8] Removed test and extra documenation --- src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 2898ee8..486a70c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -63,7 +63,6 @@ pub fn path_to_glob(current_dir: &PathBuf, path: &str) -> Result