From e36ddf34b4ede92e88c31207c114bea9c0a67f6d Mon Sep 17 00:00:00 2001 From: rabite Date: Wed, 20 Mar 2019 00:29:20 +0100 Subject: use osstring for filenames/paths --- src/file_browser.rs | 75 +++++++++++++++++++----------- src/files.rs | 131 ++++++++++++++++++++++++++++++++++++++++++++++++++-- src/listview.rs | 1 + src/preview.rs | 2 +- src/proclist.rs | 13 ++++-- 5 files changed, 185 insertions(+), 37 deletions(-) diff --git a/src/file_browser.rs b/src/file_browser.rs index 51bf72f..ce4a843 100644 --- a/src/file_browser.rs +++ b/src/file_browser.rs @@ -7,8 +7,9 @@ use std::sync::mpsc::{channel, Receiver, Sender}; use std::time::Duration; use std::path::PathBuf; use std::collections::HashMap; +use std::ffi::{OsString, OsStr}; -use crate::files::{File, Files, ShortPaths}; +use crate::files::{File, Files, PathBufExt, OsStrTools}; use crate::listview::ListView; use crate::miller_columns::MillerColumns; use crate::widget::Widget; @@ -131,13 +132,15 @@ impl Tabbable for TabView { Key::Char('!') => { let tab_dirs = self.widgets.iter().map(|w| w.cwd.clone()) .collect::>(); - let selected_files = self.widgets.iter().fold(HashMap::new(), - |mut f, w| { - let dir = w.cwd().unwrap().clone(); - let selected_files = w.selected_files().unwrap(); - f.insert(dir, selected_files); - f - }); + let selected_files = self + .widgets + .iter() + .map(|w| { + w.selected_files() + .map_err(|_| Vec::::new()) + .unwrap() + }).collect(); + self.widgets[self.active].exec_cmd(tab_dirs, selected_files) } _ => { self.active_tab_mut().on_key(key) } @@ -633,39 +636,55 @@ impl FileBrowser { fn exec_cmd(&mut self, tab_dirs: Vec, - tab_files: HashMap>) -> HResult<()> { + tab_files: Vec>) -> HResult<()> { let cwd = self.cwd()?; - let filename = self.selected_file()?.name.clone(); + let filename = self.selected_file()?.path.quoted_file_name()?; let selected_files = self.selected_files()?; - let file_names - = selected_files.iter().map(|f| f.name.clone()).collect::>(); + let files = selected_files.iter() + .map(|f| f.path()) + .collect::>(); - let cmd = self.minibuffer("exec")?; + let cmd = self.minibuffer("exec")?.trim_start().to_string(); - self.show_status(&format!("Running: \"{}\"", &cmd)).log(); + let cmd = OsString::from(cmd); + let space = OsString::from(" "); - let mut cmd = if file_names.len() == 0 { - cmd.replace("$s", &format!("{}", &filename)) + let mut cmd = if files.len() == 0 { + cmd.replace(&OsString::from("$s"), &filename) } else { - let args = file_names.iter().map(|f| { - format!(" \"{}\" ", f) - }).collect::(); - cmd.replace("$s", &args) + let args = files.iter() + .fold(OsString::new(), |mut args, file| { + if let Some(name) = file.quoted_file_name() { + args.push(name); + args.push(space.clone()); + } + args + }); + let args = args.trim_last_space(); + + cmd.replace(&OsString::from("$s"), &args) }; for (i, tab_dir) in tab_dirs.iter().enumerate() { - if let Some(tab_files) = tab_files.get(tab_dir) { - let tab_file_identifier = format!("${}s", i); - let args = tab_files.iter().map(|f| { - let file_path = f.strip_prefix(&cwd); - format!(" \"{}\" ", file_path.to_string_lossy()) - }).collect::(); + if let Some(tab_files) = tab_files.get(i) { + let tab_file_identifier = OsString::from(format!("${}s", i)); + + let args = tab_files.iter() + .fold(OsString::new(), |mut args, file| { + let file_path = file.strip_prefix(&cwd); + let name = file_path.quoted_path(); + args.push(name); + args.push(space.clone()); + + args + }); + cmd = cmd.replace(&tab_file_identifier, &args); } - let tab_identifier = format!("${}", i); - let tab_path = tab_dir.path.to_string_lossy(); + let tab_identifier = OsString::from(format!("${}", i)); + let tab_path = tab_dir.path().into_os_string(); cmd = cmd.replace(&tab_identifier, &tab_path); } diff --git a/src/files.rs b/src/files.rs index 84dbc93..53f167a 100644 --- a/src/files.rs +++ b/src/files.rs @@ -4,6 +4,8 @@ use std::os::unix::fs::MetadataExt; use std::path::{Path, PathBuf}; use std::sync::{Arc, Mutex}; use std::hash::{Hash, Hasher}; +use std::os::unix::ffi::{OsStringExt, OsStrExt}; +use std::ffi::{OsStr, OsString}; use lscolors::LsColors; use mime_detective; @@ -609,25 +611,144 @@ impl File { Some(time.format("%F %R").to_string()) } + pub fn short_path(&self) -> PathBuf { + self.path.short_path() + } + pub fn short_string(&self) -> String { self.path.short_string() } } -pub trait ShortPaths { +pub trait PathBufExt { + fn short_path(&self) -> PathBuf; fn short_string(&self) -> String; + fn name_starts_with(&self, pat: &str) -> bool; + fn quoted_file_name(&self) -> Option; + fn quoted_path(&self) -> OsString; } -impl ShortPaths for PathBuf { - fn short_string(&self) -> String { +impl PathBufExt for PathBuf { + fn short_path(&self) -> PathBuf { if let Ok(home) = crate::paths::home_path() { if let Ok(short) = self.strip_prefix(home) { let mut path = PathBuf::from("~"); path.push(short); - return path.to_string_lossy().to_string(); + return path + } + } + return self.clone(); + } + + fn short_string(&self) -> String { + self.short_path().to_string_lossy().to_string() + } + + fn name_starts_with(&self, pat: &str) -> bool { + if let Some(name) = self.file_name() { + let nbytes = name.as_bytes(); + let pbytes = pat.as_bytes(); + + if nbytes.starts_with(pbytes) { + return true; + } else { + return false; } } - return self.to_string_lossy().to_string(); + false + } + + fn quoted_file_name(&self) -> Option { + if let Some(name) = self.file_name() { + let mut name = name.as_bytes().to_vec(); + let mut quote = "\"".as_bytes().to_vec(); + //let mut quote_after = "\"".as_bytes().to_vec(); + let mut quoted = vec![]; + quoted.append(&mut quote.clone()); + quoted.append(&mut name); + quoted.append(&mut quote); + + let quoted_name = OsStr::from_bytes("ed).to_os_string(); + return Some(quoted_name); + } + None + } + + fn quoted_path(&self) -> OsString { + let mut path = self.clone().into_os_string().into_vec(); + let mut quote = "\"".as_bytes().to_vec(); + + let mut quoted = vec![]; + quoted.append(&mut quote.clone()); + quoted.append(&mut path); + quoted.append(&mut quote); + + OsString::from_vec(quoted) + } +} + +pub trait OsStrTools { + fn replace(&self, from: &OsStr, to: &OsStr) -> OsString; + fn trim_last_space(&self) -> OsString; + fn contains_osstr(&self, pat: &OsStr) -> bool; + fn position(&self, pat: &OsStr) -> Option; +} + +impl OsStrTools for OsStr { + fn replace(&self, from: &OsStr, to: &OsStr) -> OsString { + let orig_string = self.as_bytes().to_vec(); + let from = from.as_bytes(); + let to = to.as_bytes().to_vec(); + let from_len = from.len(); + + let new_string = orig_string + .windows(from_len) + .enumerate() + .fold(Vec::new(), |mut pos, (i, chars)| { + if chars == from { + pos.push(i); + } + pos + }).iter().rev().fold(orig_string.to_vec(), |mut string, pos| { + let pos = *pos; + string.splice(pos..pos+from_len, to.clone()); + string + }); + + OsString::from_vec(new_string) + } + + fn trim_last_space(&self) -> OsString { + let string = self.as_bytes(); + let len = string.len(); + + if len > 0 { + OsString::from_vec(string[..len-1].to_vec()) + } else { + self.to_os_string() + } + } + + fn contains_osstr(&self, pat: &OsStr) -> bool { + let string = self.as_bytes(); + let pat = pat.as_bytes(); + let pat_len = pat.len(); + + string.windows(pat_len) + .find(|chars| + chars == &pat + ).is_some() + } + + fn position(&self, pat: &OsStr) -> Option { + let string = self.as_bytes(); + let pat = pat.as_bytes(); + let pat_len = pat.len(); + + string.windows(pat_len) + .position(|chars| + chars == pat + ) } } diff --git a/src/listview.rs b/src/listview.rs index 1c6f620..d655050 100644 --- a/src/listview.rs +++ b/src/listview.rs @@ -300,6 +300,7 @@ impl ListView let file = self.selected_file_mut(); file.toggle_selection(); self.move_down(); + self.core.set_dirty(); self.refresh().log(); } diff --git a/src/preview.rs b/src/preview.rs index 9965229..03e2805 100644 --- a/src/preview.rs +++ b/src/preview.rs @@ -388,7 +388,7 @@ impl Previewer { -> Result, HError> { let process = std::process::Command::new("scope.sh") - .arg(&file.name) + .arg(&file.path) .arg("10".to_string()) .arg("10".to_string()) .arg("".to_string()) diff --git a/src/proclist.rs b/src/proclist.rs index f36ede4..bab9394 100644 --- a/src/proclist.rs +++ b/src/proclist.rs @@ -3,6 +3,7 @@ use std::sync::mpsc::Sender; use std::process::Child; use std::os::unix::process::{CommandExt, ExitStatusExt}; use std::io::{BufRead, BufReader}; +use std::ffi::OsString; use termion::event::Key; use unicode_width::UnicodeWidthStr; @@ -15,6 +16,7 @@ use crate::hbox::HBox; use crate::preview::WillBeWidget; use crate::fail::{HResult, HError, ErrorLog}; use crate::term; +use crate::files::OsStrTools; #[derive(Debug)] struct Process { @@ -114,8 +116,13 @@ impl Listable for ListView> { } impl ListView> { - fn run_proc(&mut self, cmd: &str) -> HResult<()> { + fn run_proc(&mut self, cmd: &OsString) -> HResult<()> { let shell = std::env::var("SHELL").unwrap_or("sh".into()); + let home = crate::paths::home_path()?.into_os_string(); + let short = OsString::from("~"); + let short_cmd = cmd.replace(&home, &short).to_string_lossy().to_string(); + + self.show_status(&format!("Running: {}", &short_cmd)).log(); let handle = std::process::Command::new(shell) .arg("-c") @@ -125,7 +132,7 @@ impl ListView> { .before_exec(|| unsafe { libc::dup2(1, 2); Ok(()) }) .spawn()?; let mut proc = Process { - cmd: cmd.to_string(), + cmd: short_cmd, handle: Arc::new(Mutex::new(handle)), output: Arc::new(Mutex::new(String::new())), status: Arc::new(Mutex::new(None)), @@ -278,7 +285,7 @@ impl ProcView { self.hbox.get_textview() } - pub fn run_proc(&mut self, cmd: &str) -> HResult<()> { + pub fn run_proc(&mut self, cmd: &OsString) -> HResult<()> { self.get_listview_mut().run_proc(cmd)?; Ok(()) } -- cgit v1.2.3