From 259f18fcabf2405067f9444fc7bff520cda2dc0c Mon Sep 17 00:00:00 2001 From: Jan Verbeek Date: Fri, 3 Sep 2021 16:10:39 +0200 Subject: [PATCH] Update message quoting and filename printing --- .../cspell.dictionaries/jargon.wordlist.txt | 2 + src/bin/coreutils.rs | 29 ++++++---- src/uu/base32/src/base_common.rs | 10 ++-- src/uu/cat/src/cat.rs | 3 +- src/uu/chcon/src/chcon.rs | 16 +++--- src/uu/chcon/src/errors.rs | 4 +- src/uu/chgrp/src/chgrp.rs | 10 +++- src/uu/chmod/src/chmod.rs | 42 ++++++++------ src/uu/chown/src/chown.rs | 7 ++- src/uu/chroot/src/chroot.rs | 17 ++++-- src/uu/cksum/src/cksum.rs | 5 +- src/uu/cp/src/cp.rs | 35 ++++++------ src/uu/csplit/src/csplit.rs | 3 +- src/uu/csplit/src/csplit_error.rs | 14 +++-- src/uu/cut/src/cut.rs | 7 ++- src/uu/date/src/date.rs | 7 ++- src/uu/dircolors/src/dircolors.rs | 19 +++++-- src/uu/dirname/src/dirname.rs | 3 +- src/uu/du/src/du.rs | 54 +++++++++--------- src/uu/env/src/env.rs | 10 +++- src/uu/expand/src/expand.rs | 3 +- src/uu/factor/src/cli.rs | 5 +- src/uu/fmt/src/fmt.rs | 3 +- src/uu/groups/src/groups.rs | 7 ++- src/uu/hashsum/src/hashsum.rs | 7 ++- src/uu/head/src/head.rs | 9 +-- src/uu/id/src/id.rs | 3 +- src/uu/install/src/install.rs | 43 ++++++--------- src/uu/install/src/mode.rs | 3 +- src/uu/join/src/join.rs | 19 ++++--- src/uu/kill/src/kill.rs | 7 ++- src/uu/ln/src/ln.rs | 46 ++++++++-------- src/uu/ls/src/ls.rs | 29 +++++----- src/uu/mkdir/src/mkdir.rs | 11 ++-- src/uu/mkfifo/src/mkfifo.rs | 4 +- src/uu/mknod/src/mknod.rs | 3 +- src/uu/mktemp/src/mktemp.rs | 23 ++++---- src/uu/more/src/more.rs | 5 +- src/uu/mv/src/mv.rs | 44 +++++++-------- src/uu/nohup/src/nohup.rs | 17 ++++-- src/uu/numfmt/src/format.rs | 6 +- src/uu/numfmt/src/numfmt.rs | 3 +- src/uu/od/src/multifilereader.rs | 4 +- src/uu/od/src/od.rs | 5 +- src/uu/od/src/parse_formats.rs | 17 ++++-- src/uu/pathchk/src/pathchk.rs | 21 +++---- src/uu/pr/src/pr.rs | 19 +++++-- src/uu/printf/src/cli.rs | 11 +--- src/uu/printf/src/memo.rs | 11 ++-- .../src/tokenize/num_format/formatter.rs | 5 +- .../src/tokenize/num_format/num_format.rs | 11 ++-- src/uu/printf/src/tokenize/sub.rs | 3 +- src/uu/ptx/src/ptx.rs | 7 ++- src/uu/readlink/src/readlink.rs | 20 ++----- src/uu/realpath/src/realpath.rs | 17 ++++-- src/uu/relpath/src/relpath.rs | 5 +- src/uu/rm/src/rm.rs | 50 ++++++++--------- src/uu/runcon/src/errors.rs | 4 +- src/uu/seq/src/seq.rs | 9 +-- src/uu/shred/src/shred.rs | 48 ++++++++-------- src/uu/shuf/src/shuf.rs | 17 +++--- src/uu/sort/src/sort.rs | 55 +++++++++++++------ src/uu/split/src/split.rs | 13 +++-- src/uu/stat/src/stat.rs | 17 +++--- src/uu/sum/src/sum.rs | 3 +- src/uu/sync/src/sync.rs | 7 ++- src/uu/tac/src/tac.rs | 11 ++-- src/uu/tee/src/tee.rs | 9 +-- src/uu/test/src/parser.rs | 33 ++++++----- src/uu/test/src/test.rs | 55 +++++++++---------- src/uu/timeout/src/timeout.rs | 9 +-- src/uu/touch/src/touch.rs | 21 +++++-- src/uu/tr/src/tr.rs | 6 +- src/uu/truncate/src/truncate.rs | 5 +- src/uu/tsort/src/tsort.rs | 9 ++- src/uu/unexpand/src/unexpand.rs | 5 +- src/uu/uniq/src/uniq.rs | 10 +++- src/uu/unlink/src/unlink.rs | 8 ++- src/uucore/src/lib/features/perms.rs | 33 +++++------ src/uucore/src/lib/lib.rs | 11 ++-- src/uucore/src/lib/mods/backup_control.rs | 19 +++++-- src/uucore/src/lib/mods/display.rs | 23 ++++++++ src/uucore/src/lib/mods/error.rs | 36 ++++++++---- src/uucore/src/lib/mods/ranges.rs | 4 +- src/uucore/src/lib/parser/parse_size.rs | 14 ++++- src/uucore/src/lib/parser/parse_time.rs | 6 +- tests/by-util/test_chroot.rs | 4 +- tests/by-util/test_cksum.rs | 4 +- tests/by-util/test_sort.rs | 8 +-- tests/by-util/test_sum.rs | 4 +- tests/by-util/test_test.rs | 4 +- 91 files changed, 777 insertions(+), 550 deletions(-) diff --git a/.vscode/cspell.dictionaries/jargon.wordlist.txt b/.vscode/cspell.dictionaries/jargon.wordlist.txt index 6561dc63b..cd1cc18b3 100644 --- a/.vscode/cspell.dictionaries/jargon.wordlist.txt +++ b/.vscode/cspell.dictionaries/jargon.wordlist.txt @@ -9,6 +9,8 @@ bytewise canonicalization canonicalize canonicalizing +codepoint +codepoints colorizable colorize coprime diff --git a/src/bin/coreutils.rs b/src/bin/coreutils.rs index 477931dbd..1de1b6354 100644 --- a/src/bin/coreutils.rs +++ b/src/bin/coreutils.rs @@ -10,10 +10,12 @@ use clap::Arg; use clap::Shell; use std::cmp; use std::collections::hash_map::HashMap; +use std::ffi::OsStr; use std::ffi::OsString; use std::io::{self, Write}; use std::path::{Path, PathBuf}; use std::process; +use uucore::display::Quotable; const VERSION: &str = env!("CARGO_PKG_VERSION"); @@ -76,13 +78,21 @@ fn main() { // 0th argument equals util name? if let Some(util_os) = util_name { - let util = util_os.as_os_str().to_string_lossy(); + fn not_found(util: &OsStr) -> ! { + println!("{}: function/utility not found", util.maybe_quote()); + process::exit(1); + } + + let util = match util_os.to_str() { + Some(util) => util, + None => not_found(&util_os), + }; if util == "completion" { gen_completions(args, utils); } - match utils.get(&util[..]) { + match utils.get(util) { Some(&(uumain, _)) => { process::exit(uumain((vec![util_os].into_iter()).chain(args))); } @@ -90,9 +100,12 @@ fn main() { if util == "--help" || util == "-h" { // see if they want help on a specific util if let Some(util_os) = args.next() { - let util = util_os.as_os_str().to_string_lossy(); + let util = match util_os.to_str() { + Some(util) => util, + None => not_found(&util_os), + }; - match utils.get(&util[..]) { + match utils.get(util) { Some(&(uumain, _)) => { let code = uumain( (vec![util_os, OsString::from("--help")].into_iter()) @@ -101,17 +114,13 @@ fn main() { io::stdout().flush().expect("could not flush stdout"); process::exit(code); } - None => { - println!("{}: function/utility not found", util); - process::exit(1); - } + None => not_found(&util_os), } } usage(&utils, binary_as_util); process::exit(0); } else { - println!("{}: function/utility not found", util); - process::exit(1); + not_found(&util_os); } } } diff --git a/src/uu/base32/src/base_common.rs b/src/uu/base32/src/base_common.rs index 78962d9db..0fd0fa5c4 100644 --- a/src/uu/base32/src/base_common.rs +++ b/src/uu/base32/src/base_common.rs @@ -9,6 +9,7 @@ use std::io::{stdout, Read, Write}; +use uucore::display::Quotable; use uucore::encoding::{wrap_print, Data, Format}; use uucore::InvalidEncodingHandling; @@ -40,8 +41,9 @@ impl Config { let name = values.next().unwrap(); if let Some(extra_op) = values.next() { return Err(format!( - "extra operand '{}'\nTry '{} --help' for more information.", - extra_op, app_name + "extra operand {}\nTry '{} --help' for more information.", + extra_op.quote(), + app_name )); } @@ -49,7 +51,7 @@ impl Config { None } else { if !Path::exists(Path::new(name)) { - return Err(format!("{}: No such file or directory", name)); + return Err(format!("{}: No such file or directory", name.maybe_quote())); } Some(name.to_owned()) } @@ -61,7 +63,7 @@ impl Config { .value_of(options::WRAP) .map(|num| { num.parse::() - .map_err(|_| format!("invalid wrap size: '{}'", num)) + .map_err(|_| format!("invalid wrap size: {}", num.quote())) }) .transpose()?; diff --git a/src/uu/cat/src/cat.rs b/src/uu/cat/src/cat.rs index a176b8c5d..9459ee86a 100644 --- a/src/uu/cat/src/cat.rs +++ b/src/uu/cat/src/cat.rs @@ -20,6 +20,7 @@ use clap::{crate_version, App, Arg}; use std::fs::{metadata, File}; use std::io::{self, Read, Write}; use thiserror::Error; +use uucore::display::Quotable; use uucore::error::UResult; #[cfg(unix)] @@ -386,7 +387,7 @@ fn cat_files(files: Vec, options: &OutputOptions) -> UResult<()> { for path in &files { if let Err(err) = cat_path(path, options, &mut state, &out_info) { - error_messages.push(format!("{}: {}", path, err)); + error_messages.push(format!("{}: {}", path.maybe_quote(), err)); } } if state.skipped_carriage_return { diff --git a/src/uu/chcon/src/chcon.rs b/src/uu/chcon/src/chcon.rs index 5f6a80bde..98ebebf34 100644 --- a/src/uu/chcon/src/chcon.rs +++ b/src/uu/chcon/src/chcon.rs @@ -2,7 +2,7 @@ #![allow(clippy::upper_case_acronyms)] -use uucore::{show_error, show_usage_error, show_warning}; +use uucore::{display::Quotable, show_error, show_usage_error, show_warning}; use clap::{App, Arg}; use selinux::{OpaqueSecurityContext, SecurityContext}; @@ -111,13 +111,13 @@ pub fn uumain(args: impl uucore::Args) -> i32 { Ok(context) => context, Err(_r) => { - show_error!("Invalid security context '{}'.", context.to_string_lossy()); + show_error!("Invalid security context {}.", context.quote()); return libc::EXIT_FAILURE; } }; if SecurityContext::from_c_str(&c_context, false).check() == Some(false) { - show_error!("Invalid security context '{}'.", context.to_string_lossy()); + show_error!("Invalid security context {}.", context.quote()); return libc::EXIT_FAILURE; } @@ -564,7 +564,7 @@ fn process_file( println!( "{}: Changing security context of: {}", uucore::util_name(), - file_full_name.to_string_lossy() + file_full_name.quote() ); } @@ -699,9 +699,9 @@ fn root_dev_ino_warn(dir_name: &Path) { ); } else { show_warning!( - "It is dangerous to operate recursively on '{}' (same as '/'). \ + "It is dangerous to operate recursively on {} (same as '/'). \ Use --{} to override this failsafe.", - dir_name.to_string_lossy(), + dir_name.quote(), options::preserve_root::NO_PRESERVE_ROOT, ); } @@ -726,8 +726,8 @@ fn emit_cycle_warning(file_name: &Path) { "Circular directory structure.\n\ This almost certainly means that you have a corrupted file system.\n\ NOTIFY YOUR SYSTEM MANAGER.\n\ -The following directory is part of the cycle '{}'.", - file_name.display() +The following directory is part of the cycle {}.", + file_name.quote() ) } diff --git a/src/uu/chcon/src/errors.rs b/src/uu/chcon/src/errors.rs index ecbd7d409..2d8f72e67 100644 --- a/src/uu/chcon/src/errors.rs +++ b/src/uu/chcon/src/errors.rs @@ -2,6 +2,8 @@ use std::ffi::OsString; use std::fmt::Write; use std::io; +use uucore::display::Quotable; + pub(crate) type Result = std::result::Result; #[derive(thiserror::Error, Debug)] @@ -30,7 +32,7 @@ pub(crate) enum Error { source: io::Error, }, - #[error("{operation} failed on '{}'", .operand1.to_string_lossy())] + #[error("{operation} failed on {}", .operand1.quote())] Io1 { operation: &'static str, operand1: OsString, diff --git a/src/uu/chgrp/src/chgrp.rs b/src/uu/chgrp/src/chgrp.rs index a077ab7a4..1795ad0d5 100644 --- a/src/uu/chgrp/src/chgrp.rs +++ b/src/uu/chgrp/src/chgrp.rs @@ -9,6 +9,7 @@ #[macro_use] extern crate uucore; +use uucore::display::Quotable; pub use uucore::entries; use uucore::error::{FromIo, UResult, USimpleError}; use uucore::perms::{chown_base, options, IfFrom}; @@ -32,7 +33,7 @@ fn parse_gid_and_uid(matches: &ArgMatches) -> UResult<(Option, Option, let dest_gid = if let Some(file) = matches.value_of(options::REFERENCE) { fs::metadata(&file) .map(|meta| Some(meta.gid())) - .map_err_context(|| format!("failed to get attributes of '{}'", file))? + .map_err_context(|| format!("failed to get attributes of {}", file.quote()))? } else { let group = matches.value_of(options::ARG_GROUP).unwrap_or_default(); if group.is_empty() { @@ -40,7 +41,12 @@ fn parse_gid_and_uid(matches: &ArgMatches) -> UResult<(Option, Option, } else { match entries::grp2gid(group) { Ok(g) => Some(g), - _ => return Err(USimpleError::new(1, format!("invalid group: '{}'", group))), + _ => { + return Err(USimpleError::new( + 1, + format!("invalid group: {}", group.quote()), + )) + } } } }; diff --git a/src/uu/chmod/src/chmod.rs b/src/uu/chmod/src/chmod.rs index 26b3fd85b..e25202fbe 100644 --- a/src/uu/chmod/src/chmod.rs +++ b/src/uu/chmod/src/chmod.rs @@ -14,6 +14,7 @@ use clap::{crate_version, App, Arg}; use std::fs; use std::os::unix::fs::{MetadataExt, PermissionsExt}; use std::path::Path; +use uucore::display::Quotable; use uucore::fs::display_permissions_unix; use uucore::libc::mode_t; #[cfg(not(windows))] @@ -75,7 +76,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .value_of(options::REFERENCE) .and_then(|fref| match fs::metadata(fref) { Ok(meta) => Some(meta.mode()), - Err(err) => crash!(1, "cannot stat attributes of '{}': {}", fref, err), + Err(err) => crash!(1, "cannot stat attributes of {}: {}", fref.quote(), err), }); let modes = matches.value_of(options::MODE).unwrap(); // should always be Some because required let cmode = if mode_had_minus_prefix { @@ -223,21 +224,24 @@ impl Chmoder { if !file.exists() { if is_symlink(file) { println!( - "failed to change mode of '{}' from 0000 (---------) to 0000 (---------)", - filename + "failed to change mode of {} from 0000 (---------) to 0000 (---------)", + filename.quote() ); if !self.quiet { - show_error!("cannot operate on dangling symlink '{}'", filename); + show_error!("cannot operate on dangling symlink {}", filename.quote()); } } else if !self.quiet { - show_error!("cannot access '{}': No such file or directory", filename); + show_error!( + "cannot access {}: No such file or directory", + filename.quote() + ); } return Err(1); } if self.recursive && self.preserve_root && filename == "/" { show_error!( - "it is dangerous to operate recursively on '{}'\nuse --no-preserve-root to override this failsafe", - filename + "it is dangerous to operate recursively on {}\nuse --no-preserve-root to override this failsafe", + filename.quote() ); return Err(1); } @@ -270,15 +274,17 @@ impl Chmoder { if is_symlink(file) { if self.verbose { println!( - "neither symbolic link '{}' nor referent has been changed", - file.display() + "neither symbolic link {} nor referent has been changed", + file.quote() ); } return Ok(()); } else if err.kind() == std::io::ErrorKind::PermissionDenied { - show_error!("'{}': Permission denied", file.display()); + // These two filenames would normally be conditionally + // quoted, but GNU's tests expect them to always be quoted + show_error!("{}: Permission denied", file.quote()); } else { - show_error!("'{}': {}", file.display(), err); + show_error!("{}: {}", file.quote(), err); } return Err(1); } @@ -325,7 +331,7 @@ impl Chmoder { if (new_mode & !naively_expected_new_mode) != 0 { show_error!( "{}: new permissions are {}, not {}", - file.display(), + file.maybe_quote(), display_permissions_unix(new_mode as mode_t, false), display_permissions_unix(naively_expected_new_mode as mode_t, false) ); @@ -342,8 +348,8 @@ impl Chmoder { if fperm == mode { if self.verbose && !self.changes { println!( - "mode of '{}' retained as {:04o} ({})", - file.display(), + "mode of {} retained as {:04o} ({})", + file.quote(), fperm, display_permissions_unix(fperm as mode_t, false), ); @@ -355,8 +361,8 @@ impl Chmoder { } if self.verbose { println!( - "failed to change mode of file '{}' from {:04o} ({}) to {:04o} ({})", - file.display(), + "failed to change mode of file {} from {:04o} ({}) to {:04o} ({})", + file.quote(), fperm, display_permissions_unix(fperm as mode_t, false), mode, @@ -367,8 +373,8 @@ impl Chmoder { } else { if self.verbose || self.changes { println!( - "mode of '{}' changed from {:04o} ({}) to {:04o} ({})", - file.display(), + "mode of {} changed from {:04o} ({}) to {:04o} ({})", + file.quote(), fperm, display_permissions_unix(fperm as mode_t, false), mode, diff --git a/src/uu/chown/src/chown.rs b/src/uu/chown/src/chown.rs index 4abb9ac61..5525f9325 100644 --- a/src/uu/chown/src/chown.rs +++ b/src/uu/chown/src/chown.rs @@ -9,6 +9,7 @@ #[macro_use] extern crate uucore; +use uucore::display::Quotable; pub use uucore::entries::{self, Group, Locate, Passwd}; use uucore::perms::{chown_base, options, IfFrom}; @@ -44,7 +45,7 @@ fn parse_gid_uid_and_filter(matches: &ArgMatches) -> UResult<(Option, Optio let dest_gid: Option; if let Some(file) = matches.value_of(options::REFERENCE) { let meta = fs::metadata(&file) - .map_err_context(|| format!("failed to get attributes of '{}'", file))?; + .map_err_context(|| format!("failed to get attributes of {}", file.quote()))?; dest_gid = Some(meta.gid()); dest_uid = Some(meta.uid()); } else { @@ -173,7 +174,7 @@ fn parse_spec(spec: &str) -> UResult<(Option, Option)> { let uid = if usr_only || usr_grp { Some( Passwd::locate(args[0]) - .map_err(|_| USimpleError::new(1, format!("invalid user: '{}'", spec)))? + .map_err(|_| USimpleError::new(1, format!("invalid user: {}", spec.quote())))? .uid(), ) } else { @@ -182,7 +183,7 @@ fn parse_spec(spec: &str) -> UResult<(Option, Option)> { let gid = if grp_only || usr_grp { Some( Group::locate(args[1]) - .map_err(|_| USimpleError::new(1, format!("invalid group: '{}'", spec)))? + .map_err(|_| USimpleError::new(1, format!("invalid group: {}", spec.quote())))? .gid(), ) } else { diff --git a/src/uu/chroot/src/chroot.rs b/src/uu/chroot/src/chroot.rs index c0f392913..240b5eafe 100644 --- a/src/uu/chroot/src/chroot.rs +++ b/src/uu/chroot/src/chroot.rs @@ -15,6 +15,7 @@ use std::ffi::CString; use std::io::Error; use std::path::Path; use std::process::Command; +use uucore::display::Quotable; use uucore::libc::{self, chroot, setgid, setgroups, setuid}; use uucore::{entries, InvalidEncodingHandling}; @@ -53,8 +54,8 @@ pub fn uumain(args: impl uucore::Args) -> i32 { if !newroot.is_dir() { crash!( 1, - "cannot change root directory to `{}`: no such directory", - newroot.display() + "cannot change root directory to {}: no such directory", + newroot.quote() ); } @@ -170,7 +171,6 @@ fn set_context(root: &Path, options: &clap::ArgMatches) { } fn enter_chroot(root: &Path) { - let root_str = root.display(); std::env::set_current_dir(root).unwrap(); let err = unsafe { chroot(CString::new(".").unwrap().as_bytes_with_nul().as_ptr() as *const libc::c_char) @@ -179,7 +179,7 @@ fn enter_chroot(root: &Path) { crash!( 1, "cannot chroot to {}: {}", - root_str, + root.quote(), Error::last_os_error() ) }; @@ -189,7 +189,7 @@ fn set_main_group(group: &str) { if !group.is_empty() { let group_id = match entries::grp2gid(group) { Ok(g) => g, - _ => crash!(1, "no such group: {}", group), + _ => crash!(1, "no such group: {}", group.maybe_quote()), }; let err = unsafe { setgid(group_id) }; if err != 0 { @@ -234,7 +234,12 @@ fn set_user(user: &str) { let user_id = entries::usr2uid(user).unwrap(); let err = unsafe { setuid(user_id as libc::uid_t) }; if err != 0 { - crash!(1, "cannot set user to {}: {}", user, Error::last_os_error()) + crash!( + 1, + "cannot set user to {}: {}", + user.maybe_quote(), + Error::last_os_error() + ) } } } diff --git a/src/uu/cksum/src/cksum.rs b/src/uu/cksum/src/cksum.rs index a5beec368..e682aa70c 100644 --- a/src/uu/cksum/src/cksum.rs +++ b/src/uu/cksum/src/cksum.rs @@ -14,6 +14,7 @@ use clap::{crate_version, App, Arg}; use std::fs::File; use std::io::{self, stdin, BufReader, Read}; use std::path::Path; +use uucore::display::Quotable; use uucore::InvalidEncodingHandling; // NOTE: CRC_TABLE_LEN *must* be <= 256 as we cast 0..CRC_TABLE_LEN to u8 @@ -191,7 +192,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { match cksum("-") { Ok((crc, size)) => println!("{} {}", crc, size), Err(err) => { - show_error!("{}", err); + show_error!("-: {}", err); return 2; } } @@ -203,7 +204,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { match cksum(fname.as_ref()) { Ok((crc, size)) => println!("{} {} {}", crc, size, fname), Err(err) => { - show_error!("'{}' {}", fname, err); + show_error!("{}: {}", fname.maybe_quote(), err); exit_code = 2; } } diff --git a/src/uu/cp/src/cp.rs b/src/uu/cp/src/cp.rs index e9e76237b..cd33f9fa6 100644 --- a/src/uu/cp/src/cp.rs +++ b/src/uu/cp/src/cp.rs @@ -18,6 +18,7 @@ extern crate quick_error; #[macro_use] extern crate uucore; +use uucore::display::Quotable; #[cfg(windows)] use winapi::um::fileapi::CreateFileW; #[cfg(windows)] @@ -541,8 +542,8 @@ impl FromStr for Attribute { "xattr" => Attribute::Xattr, _ => { return Err(Error::InvalidArgument(format!( - "invalid attribute '{}'", - value + "invalid attribute {}", + value.quote() ))); } }) @@ -659,8 +660,8 @@ impl Options { "never" => ReflinkMode::Never, value => { return Err(Error::InvalidArgument(format!( - "invalid argument '{}' for \'reflink\'", - value + "invalid argument {} for \'reflink\'", + value.quote() ))); } } @@ -832,7 +833,7 @@ fn copy(sources: &[Source], target: &TargetSlice, options: &Options) -> CopyResu let mut seen_sources = HashSet::with_capacity(sources.len()); for source in sources { if seen_sources.contains(source) { - show_warning!("source '{}' specified more than once", source.display()); + show_warning!("source {} specified more than once", source.quote()); } else { let mut found_hard_link = false; if preserve_hard_links { @@ -873,8 +874,8 @@ fn construct_dest_path( ) -> CopyResult { if options.no_target_dir && target.is_dir() { return Err(format!( - "cannot overwrite directory '{}' with non-directory", - target.display() + "cannot overwrite directory {} with non-directory", + target.quote() ) .into()); } @@ -941,7 +942,7 @@ fn adjust_canonicalization(p: &Path) -> Cow { /// will not cause a short-circuit. fn copy_directory(root: &Path, target: &TargetSlice, options: &Options) -> CopyResult<()> { if !options.recursive { - return Err(format!("omitting directory '{}'", root.display()).into()); + return Err(format!("omitting directory {}", root.quote()).into()); } // if no-dereference is enabled and this is a symlink, copy it as a file @@ -1041,12 +1042,12 @@ impl OverwriteMode { match *self { OverwriteMode::NoClobber => Err(Error::NotAllFilesCopied), OverwriteMode::Interactive(_) => { - if prompt_yes!("{}: overwrite {}? ", uucore::util_name(), path.display()) { + if prompt_yes!("{}: overwrite {}? ", uucore::util_name(), path.quote()) { Ok(()) } else { Err(Error::Skipped(format!( "Not overwriting {} at user request", - path.display() + path.quote() ))) } } @@ -1056,7 +1057,7 @@ impl OverwriteMode { } fn copy_attribute(source: &Path, dest: &Path, attribute: &Attribute) -> CopyResult<()> { - let context = &*format!("'{}' -> '{}'", source.display().to_string(), dest.display()); + let context = &*format!("{} -> {}", source.quote(), dest.quote()); let source_metadata = fs::symlink_metadata(source).context(context)?; match *attribute { Attribute::Mode => { @@ -1152,7 +1153,7 @@ fn symlink_file(source: &Path, dest: &Path, context: &str) -> CopyResult<()> { } fn context_for(src: &Path, dest: &Path) -> String { - format!("'{}' -> '{}'", src.display(), dest.display()) + format!("{} -> {}", src.quote(), dest.quote()) } /// Implements a simple backup copy for the destination file. @@ -1332,8 +1333,8 @@ fn copy_link(source: &Path, dest: &Path) -> CopyResult<()> { Some(name) => dest.join(name).into(), None => crash!( EXIT_ERR, - "cannot stat '{}': No such file or directory", - source.display() + "cannot stat {}: No such file or directory", + source.quote() ), } } else { @@ -1454,11 +1455,11 @@ fn copy_on_write_macos( pub fn verify_target_type(target: &Path, target_type: &TargetType) -> CopyResult<()> { match (target_type, target.is_dir()) { (&TargetType::Directory, false) => { - Err(format!("target: '{}' is not a directory", target.display()).into()) + Err(format!("target: {} is not a directory", target.quote()).into()) } (&TargetType::File, true) => Err(format!( - "cannot overwrite directory '{}' with non-directory", - target.display() + "cannot overwrite directory {} with non-directory", + target.quote() ) .into()), _ => Ok(()), diff --git a/src/uu/csplit/src/csplit.rs b/src/uu/csplit/src/csplit.rs index 977583a2f..dbf65b71d 100644 --- a/src/uu/csplit/src/csplit.rs +++ b/src/uu/csplit/src/csplit.rs @@ -10,6 +10,7 @@ use std::{ fs::{remove_file, File}, io::{BufRead, BufWriter, Write}, }; +use uucore::display::Quotable; mod csplit_error; mod patterns; @@ -734,7 +735,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { let file = crash_if_err!(1, File::open(file_name)); let file_metadata = crash_if_err!(1, file.metadata()); if !file_metadata.is_file() { - crash!(1, "'{}' is not a regular file", file_name); + crash!(1, "{} is not a regular file", file_name.quote()); } crash_if_err!(1, csplit(&options, patterns, BufReader::new(file))); }; diff --git a/src/uu/csplit/src/csplit_error.rs b/src/uu/csplit/src/csplit_error.rs index 637cf8890..1d4823ee2 100644 --- a/src/uu/csplit/src/csplit_error.rs +++ b/src/uu/csplit/src/csplit_error.rs @@ -1,26 +1,28 @@ use std::io; use thiserror::Error; +use uucore::display::Quotable; + /// Errors thrown by the csplit command #[derive(Debug, Error)] pub enum CsplitError { #[error("IO error: {}", _0)] IoError(io::Error), - #[error("'{}': line number out of range", _0)] + #[error("{}: line number out of range", ._0.quote())] LineOutOfRange(String), - #[error("'{}': line number out of range on repetition {}", _0, _1)] + #[error("{}: line number out of range on repetition {}", ._0.quote(), _1)] LineOutOfRangeOnRepetition(String, usize), - #[error("'{}': match not found", _0)] + #[error("{}: match not found", ._0.quote())] MatchNotFound(String), - #[error("'{}': match not found on repetition {}", _0, _1)] + #[error("{}: match not found on repetition {}", ._0.quote(), _1)] MatchNotFoundOnRepetition(String, usize), #[error("line number must be greater than zero")] LineNumberIsZero, #[error("line number '{}' is smaller than preceding line number, {}", _0, _1)] LineNumberSmallerThanPrevious(usize, usize), - #[error("invalid pattern: {}", _0)] + #[error("{}: invalid pattern", ._0.quote())] InvalidPattern(String), - #[error("invalid number: '{}'", _0)] + #[error("invalid number: {}", ._0.quote())] InvalidNumber(String), #[error("incorrect conversion specification in suffix")] SuffixFormatIncorrect, diff --git a/src/uu/cut/src/cut.rs b/src/uu/cut/src/cut.rs index 783502e3d..35d92b83f 100644 --- a/src/uu/cut/src/cut.rs +++ b/src/uu/cut/src/cut.rs @@ -15,6 +15,7 @@ use clap::{crate_version, App, Arg}; use std::fs::File; use std::io::{stdin, stdout, BufReader, BufWriter, Read, Write}; use std::path::Path; +use uucore::display::Quotable; use self::searcher::Searcher; use uucore::ranges::Range; @@ -351,19 +352,19 @@ fn cut_files(mut filenames: Vec, mode: Mode) -> i32 { let path = Path::new(&filename[..]); if path.is_dir() { - show_error!("{}: Is a directory", filename); + show_error!("{}: Is a directory", filename.maybe_quote()); continue; } if path.metadata().is_err() { - show_error!("{}: No such file or directory", filename); + show_error!("{}: No such file or directory", filename.maybe_quote()); continue; } let file = match File::open(&path) { Ok(f) => f, Err(e) => { - show_error!("opening '{}': {}", &filename[..], e); + show_error!("opening {}: {}", filename.quote(), e); continue; } }; diff --git a/src/uu/date/src/date.rs b/src/uu/date/src/date.rs index 7bf6298c0..b2ccf2e9f 100644 --- a/src/uu/date/src/date.rs +++ b/src/uu/date/src/date.rs @@ -17,6 +17,7 @@ use libc::{clock_settime, timespec, CLOCK_REALTIME}; use std::fs::File; use std::io::{BufRead, BufReader}; use std::path::PathBuf; +use uucore::display::Quotable; #[cfg(windows)] use winapi::{ shared::minwindef::WORD, @@ -145,7 +146,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { let format = if let Some(form) = matches.value_of(OPT_FORMAT) { if !form.starts_with('+') { - eprintln!("date: invalid date '{}'", form); + eprintln!("date: invalid date {}", form.quote()); return 1; } let form = form[1..].to_string(); @@ -174,7 +175,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { let set_to = match matches.value_of(OPT_SET).map(parse_date) { None => None, Some(Err((input, _err))) => { - eprintln!("date: invalid date '{}'", input); + eprintln!("date: invalid date {}", input.quote()); return 1; } Some(Ok(date)) => Some(date), @@ -240,7 +241,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { println!("{}", formatted); } Err((input, _err)) => { - println!("date: invalid date '{}'", input); + println!("date: invalid date {}", input.quote()); } } } diff --git a/src/uu/dircolors/src/dircolors.rs b/src/uu/dircolors/src/dircolors.rs index 2b9c25ba3..5d0b3ac3e 100644 --- a/src/uu/dircolors/src/dircolors.rs +++ b/src/uu/dircolors/src/dircolors.rs @@ -17,6 +17,7 @@ use std::fs::File; use std::io::{BufRead, BufReader}; use clap::{crate_version, App, Arg}; +use uucore::display::Quotable; mod options { pub const BOURNE_SHELL: &str = "bourne-shell"; @@ -94,9 +95,9 @@ pub fn uumain(args: impl uucore::Args) -> i32 { if matches.is_present(options::PRINT_DATABASE) { if !files.is_empty() { show_usage_error!( - "extra operand '{}'\nfile operands cannot be combined with \ + "extra operand {}\nfile operands cannot be combined with \ --print-database (-p)", - files[0] + files[0].quote() ); return 1; } @@ -126,7 +127,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { result = parse(INTERNAL_DB.lines(), out_format, "") } else { if files.len() > 1 { - show_usage_error!("extra operand '{}'", files[1]); + show_usage_error!("extra operand {}", files[1].quote()); return 1; } match File::open(files[0]) { @@ -135,7 +136,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { result = parse(fin.lines().filter_map(Result::ok), out_format, files[0]) } Err(e) => { - show_error!("{}: {}", files[0], e); + show_error!("{}: {}", files[0].maybe_quote(), e); return 1; } } @@ -314,7 +315,8 @@ where if val.is_empty() { return Err(format!( "{}:{}: invalid line; missing second token", - fp, num + fp.maybe_quote(), + num )); } let lower = key.to_lowercase(); @@ -341,7 +343,12 @@ where } else if let Some(s) = table.get(lower.as_str()) { result.push_str(format!("{}={}:", s, val).as_str()); } else { - return Err(format!("{}:{}: unrecognized keyword {}", fp, num, key)); + return Err(format!( + "{}:{}: unrecognized keyword {}", + fp.maybe_quote(), + num, + key + )); } } } diff --git a/src/uu/dirname/src/dirname.rs b/src/uu/dirname/src/dirname.rs index 3e91d598a..601d93ac0 100644 --- a/src/uu/dirname/src/dirname.rs +++ b/src/uu/dirname/src/dirname.rs @@ -10,6 +10,7 @@ extern crate uucore; use clap::{crate_version, App, Arg}; use std::path::Path; +use uucore::display::print_verbatim; use uucore::error::{UResult, UUsageError}; use uucore::InvalidEncodingHandling; @@ -65,7 +66,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { if d.components().next() == None { print!(".") } else { - print!("{}", d.to_string_lossy()); + print_verbatim(d).unwrap(); } } None => { diff --git a/src/uu/du/src/du.rs b/src/uu/du/src/du.rs index 685064dfc..9fd44b001 100644 --- a/src/uu/du/src/du.rs +++ b/src/uu/du/src/du.rs @@ -32,6 +32,7 @@ use std::path::PathBuf; use std::str::FromStr; use std::time::{Duration, UNIX_EPOCH}; use std::{error::Error, fmt::Display}; +use uucore::display::{print_verbatim, Quotable}; use uucore::error::{UError, UResult}; use uucore::parse_size::{parse_size, ParseSizeError}; use uucore::InvalidEncodingHandling; @@ -293,9 +294,9 @@ fn du( Err(e) => { safe_writeln!( stderr(), - "{}: cannot read directory '{}': {}", + "{}: cannot read directory {}: {}", options.util_name, - my_stat.path.display(), + my_stat.path.quote(), e ); return Box::new(iter::once(my_stat)); @@ -334,11 +335,11 @@ fn du( } Err(error) => match error.kind() { ErrorKind::PermissionDenied => { - let description = format!("cannot access '{}'", entry.path().display()); + let description = format!("cannot access {}", entry.path().quote()); let error_message = "Permission denied"; show_error_custom_description!(description, "{}", error_message) } - _ => show_error!("cannot access '{}': {}", entry.path().display(), error), + _ => show_error!("cannot access {}: {}", entry.path().quote(), error), }, }, Err(error) => show_error!("{}", error), @@ -411,26 +412,30 @@ enum DuError { impl Display for DuError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - DuError::InvalidMaxDepthArg(s) => write!(f, "invalid maximum depth '{}'", s), + DuError::InvalidMaxDepthArg(s) => write!(f, "invalid maximum depth {}", s.quote()), DuError::SummarizeDepthConflict(s) => { - write!(f, "summarizing conflicts with --max-depth={}", s) + write!( + f, + "summarizing conflicts with --max-depth={}", + s.maybe_quote() + ) } DuError::InvalidTimeStyleArg(s) => write!( f, - "invalid argument '{}' for 'time style' + "invalid argument {} for 'time style' Valid arguments are: - 'full-iso' - 'long-iso' - 'iso' Try '{} --help' for more information.", - s, + s.quote(), uucore::execution_phrase() ), DuError::InvalidTimeArg(s) => write!( f, - "Invalid argument '{}' for --time. + "Invalid argument {} for --time. 'birth' and 'creation' arguments are not supported on this platform.", - s + s.quote() ), } } @@ -566,21 +571,14 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { }; if !summarize || index == len - 1 { let time_str = tm.format(time_format_str).to_string(); - print!( - "{}\t{}\t{}{}", - convert_size(size), - time_str, - stat.path.display(), - line_separator - ); + print!("{}\t{}\t", convert_size(size), time_str); + print_verbatim(stat.path).unwrap(); + print!("{}", line_separator); } } else if !summarize || index == len - 1 { - print!( - "{}\t{}{}", - convert_size(size), - stat.path.display(), - line_separator - ); + print!("{}\t", convert_size(size)); + print_verbatim(stat.path).unwrap(); + print!("{}", line_separator); } if options.total && index == (len - 1) { // The last element will be the total size of the the path under @@ -590,7 +588,11 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { } } Err(_) => { - show_error!("{}: {}", path_string, "No such file or directory"); + show_error!( + "{}: {}", + path_string.maybe_quote(), + "No such file or directory" + ); } } } @@ -837,8 +839,8 @@ fn format_error_message(error: ParseSizeError, s: &str, option: &str) -> String // GNU's du echos affected flag, -B or --block-size (-t or --threshold), depending user's selection // GNU's du does distinguish between "invalid (suffix in) argument" match error { - ParseSizeError::ParseFailure(_) => format!("invalid --{} argument '{}'", option, s), - ParseSizeError::SizeTooBig(_) => format!("--{} argument '{}' too large", option, s), + ParseSizeError::ParseFailure(_) => format!("invalid --{} argument {}", option, s.quote()), + ParseSizeError::SizeTooBig(_) => format!("--{} argument {} too large", option, s.quote()), } } diff --git a/src/uu/env/src/env.rs b/src/uu/env/src/env.rs index baa3ca86c..af889d093 100644 --- a/src/uu/env/src/env.rs +++ b/src/uu/env/src/env.rs @@ -65,8 +65,14 @@ fn parse_name_value_opt<'a>(opts: &mut Options<'a>, opt: &'a str) -> Result(opts: &mut Options<'a>, opt: &'a str) -> Result<(), i32> { if opts.null { - eprintln!("{}: cannot specify --null (-0) with command", crate_name!()); - eprintln!("Type \"{} --help\" for detailed information", crate_name!()); + eprintln!( + "{}: cannot specify --null (-0) with command", + uucore::util_name() + ); + eprintln!( + "Type \"{} --help\" for detailed information", + uucore::execution_phrase() + ); Err(1) } else { opts.program.push(opt); diff --git a/src/uu/expand/src/expand.rs b/src/uu/expand/src/expand.rs index f5a5686f6..b09b8aaab 100644 --- a/src/uu/expand/src/expand.rs +++ b/src/uu/expand/src/expand.rs @@ -17,6 +17,7 @@ use std::fs::File; use std::io::{stdin, stdout, BufRead, BufReader, BufWriter, Read, Write}; use std::str::from_utf8; use unicode_width::UnicodeWidthChar; +use uucore::display::Quotable; static ABOUT: &str = "Convert tabs in each FILE to spaces, writing to standard output. With no FILE, or when FILE is -, read standard input."; @@ -216,7 +217,7 @@ fn open(path: String) -> BufReader> { } else { file_buf = match File::open(&path[..]) { Ok(a) => a, - Err(e) => crash!(1, "{}: {}\n", &path[..], e), + Err(e) => crash!(1, "{}: {}\n", path.maybe_quote(), e), }; BufReader::new(Box::new(file_buf) as Box) } diff --git a/src/uu/factor/src/cli.rs b/src/uu/factor/src/cli.rs index 95b8bb866..30541c244 100644 --- a/src/uu/factor/src/cli.rs +++ b/src/uu/factor/src/cli.rs @@ -16,6 +16,7 @@ use std::io::{self, stdin, stdout, BufRead, Write}; mod factor; use clap::{crate_version, App, Arg}; pub use factor::*; +use uucore::display::Quotable; mod miller_rabin; pub mod numeric; @@ -52,7 +53,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { if let Some(values) = matches.values_of(options::NUMBER) { for number in values { if let Err(e) = print_factors_str(number, &mut w, &mut factors_buffer) { - show_warning!("{}: {}", number, e); + show_warning!("{}: {}", number.maybe_quote(), e); } } } else { @@ -61,7 +62,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { for line in stdin.lock().lines() { for number in line.unwrap().split_whitespace() { if let Err(e) = print_factors_str(number, &mut w, &mut factors_buffer) { - show_warning!("{}: {}", number, e); + show_warning!("{}: {}", number.maybe_quote(), e); } } } diff --git a/src/uu/fmt/src/fmt.rs b/src/uu/fmt/src/fmt.rs index 5203745a1..df5d971e5 100644 --- a/src/uu/fmt/src/fmt.rs +++ b/src/uu/fmt/src/fmt.rs @@ -15,6 +15,7 @@ use std::cmp; use std::fs::File; use std::io::{stdin, stdout, Write}; use std::io::{BufReader, BufWriter, Read}; +use uucore::display::Quotable; use self::linebreak::break_lines; use self::parasplit::ParagraphStream; @@ -187,7 +188,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { _ => match File::open(i) { Ok(f) => BufReader::new(Box::new(f) as Box), Err(e) => { - show_warning!("{}: {}", i, e); + show_warning!("{}: {}", i.maybe_quote(), e); continue; } }, diff --git a/src/uu/groups/src/groups.rs b/src/uu/groups/src/groups.rs index c2af5c4b0..43e2a2239 100644 --- a/src/uu/groups/src/groups.rs +++ b/src/uu/groups/src/groups.rs @@ -17,7 +17,10 @@ #[macro_use] extern crate uucore; -use uucore::entries::{get_groups_gnu, gid2grp, Locate, Passwd}; +use uucore::{ + display::Quotable, + entries::{get_groups_gnu, gid2grp, Locate, Passwd}, +}; use clap::{crate_version, App, Arg}; @@ -77,7 +80,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .join(" ") ); } else { - show_error!("'{}': no such user", user); + show_error!("{}: no such user", user.quote()); exit_code = 1; } } diff --git a/src/uu/hashsum/src/hashsum.rs b/src/uu/hashsum/src/hashsum.rs index f308da300..2081d7612 100644 --- a/src/uu/hashsum/src/hashsum.rs +++ b/src/uu/hashsum/src/hashsum.rs @@ -34,6 +34,7 @@ use std::io::{self, stdin, BufRead, BufReader, Read}; use std::iter; use std::num::ParseIntError; use std::path::Path; +use uucore::display::Quotable; const NAME: &str = "hashsum"; @@ -525,7 +526,7 @@ where if options.warn { show_warning!( "{}: {}: improperly formatted {} checksum line", - filename.display(), + filename.maybe_quote(), i + 1, options.algoname ); @@ -546,6 +547,10 @@ where ) ) .to_ascii_lowercase(); + // FIXME: (How) should these be quoted? + // They seem like they would be processed programmatically, and + // our ordinary quoting might interfere, but newlines should be + // sanitized probably if sum == real_sum { if !options.quiet { println!("{}: OK", ck_filename); diff --git a/src/uu/head/src/head.rs b/src/uu/head/src/head.rs index 760cb44d5..ead734088 100644 --- a/src/uu/head/src/head.rs +++ b/src/uu/head/src/head.rs @@ -9,6 +9,7 @@ use clap::{crate_version, App, Arg}; use std::convert::TryFrom; use std::ffi::OsString; use std::io::{self, ErrorKind, Read, Seek, SeekFrom, Write}; +use uucore::display::Quotable; use uucore::{crash, show_error_custom_description}; const EXIT_FAILURE: i32 = 1; @@ -127,10 +128,10 @@ fn arg_iterate<'a>( match parse::parse_obsolete(s) { Some(Ok(iter)) => Ok(Box::new(vec![first].into_iter().chain(iter).chain(args))), Some(Err(e)) => match e { - parse::ParseError::Syntax => Err(format!("bad argument format: '{}'", s)), + parse::ParseError::Syntax => Err(format!("bad argument format: {}", s.quote())), parse::ParseError::Overflow => Err(format!( - "invalid argument: '{}' Value too large for defined datatype", - s + "invalid argument: {} Value too large for defined datatype", + s.quote() )), }, None => Ok(Box::new(vec![first, second].into_iter().chain(args))), @@ -418,7 +419,7 @@ fn uu_head(options: &HeadOptions) -> Result<(), u32> { let mut file = match std::fs::File::open(name) { Ok(f) => f, Err(err) => { - let prefix = format!("cannot open '{}' for reading", name); + let prefix = format!("cannot open {} for reading", name.quote()); match err.kind() { ErrorKind::NotFound => { show_error_custom_description!(prefix, "No such file or directory"); diff --git a/src/uu/id/src/id.rs b/src/uu/id/src/id.rs index 4e8e30e52..1229b577e 100644 --- a/src/uu/id/src/id.rs +++ b/src/uu/id/src/id.rs @@ -41,6 +41,7 @@ extern crate uucore; use clap::{crate_version, App, Arg}; use std::ffi::CStr; +use uucore::display::Quotable; use uucore::entries::{self, Group, Locate, Passwd}; use uucore::error::UResult; use uucore::error::{set_exit_code, USimpleError}; @@ -230,7 +231,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { match Passwd::locate(users[i].as_str()) { Ok(p) => Some(p), Err(_) => { - show_error!("'{}': no such user", users[i]); + show_error!("{}: no such user", users[i].quote()); set_exit_code(1); if i + 1 >= users.len() { break; diff --git a/src/uu/install/src/install.rs b/src/uu/install/src/install.rs index b72721d20..a9f91f658 100644 --- a/src/uu/install/src/install.rs +++ b/src/uu/install/src/install.rs @@ -16,6 +16,7 @@ use clap::{crate_version, App, Arg, ArgMatches}; use file_diff::diff; use filetime::{set_file_times, FileTime}; use uucore::backup_control::{self, BackupMode}; +use uucore::display::Quotable; use uucore::entries::{grp2gid, usr2uid}; use uucore::error::{FromIo, UError, UIoError, UResult}; use uucore::mode::get_umask; @@ -95,40 +96,30 @@ impl Display for InstallError { ) } IE::CreateDirFailed(dir, e) => { - Display::fmt(&uio_error!(e, "failed to create {}", dir.display()), f) + Display::fmt(&uio_error!(e, "failed to create {}", dir.quote()), f) } - IE::ChmodFailed(file) => write!(f, "failed to chmod {}", file.display()), + IE::ChmodFailed(file) => write!(f, "failed to chmod {}", file.quote()), IE::InvalidTarget(target) => write!( f, "invalid target {}: No such file or directory", - target.display() + target.quote() ), IE::TargetDirIsntDir(target) => { - write!(f, "target '{}' is not a directory", target.display()) + write!(f, "target {} is not a directory", target.quote()) } IE::BackupFailed(from, to, e) => Display::fmt( - &uio_error!( - e, - "cannot backup '{}' to '{}'", - from.display(), - to.display() - ), + &uio_error!(e, "cannot backup {} to {}", from.quote(), to.quote()), f, ), IE::InstallFailed(from, to, e) => Display::fmt( - &uio_error!( - e, - "cannot install '{}' to '{}'", - from.display(), - to.display() - ), + &uio_error!(e, "cannot install {} to {}", from.quote(), to.quote()), f, ), IE::StripProgramFailed(msg) => write!(f, "strip program failed: {}", msg), IE::MetadataFailed(e) => Display::fmt(&uio_error!(e, ""), f), - IE::NoSuchUser(user) => write!(f, "no such user: {}", user), - IE::NoSuchGroup(group) => write!(f, "no such group: {}", group), - IE::OmittingDirectory(dir) => write!(f, "omitting directory '{}'", dir.display()), + IE::NoSuchUser(user) => write!(f, "no such user: {}", user.maybe_quote()), + IE::NoSuchGroup(group) => write!(f, "no such group: {}", group.maybe_quote()), + IE::OmittingDirectory(dir) => write!(f, "omitting directory {}", dir.quote()), } } } @@ -416,14 +407,14 @@ fn directory(paths: Vec, b: Behavior) -> UResult<()> { // the default mode. Hence it is safe to use fs::create_dir_all // and then only modify the target's dir mode. if let Err(e) = - fs::create_dir_all(path).map_err_context(|| format!("{}", path.display())) + fs::create_dir_all(path).map_err_context(|| path.maybe_quote().to_string()) { show!(e); continue; } if b.verbose { - println!("creating directory '{}'", path.display()); + println!("creating directory {}", path.quote()); } } @@ -445,7 +436,7 @@ fn directory(paths: Vec, b: Behavior) -> UResult<()> { fn is_new_file_path(path: &Path) -> bool { !path.exists() && (path.parent().map(Path::is_dir).unwrap_or(true) - || path.parent().unwrap().to_string_lossy().is_empty()) // In case of a simple file + || path.parent().unwrap().as_os_str().is_empty()) // In case of a simple file } /// Perform an install, given a list of paths and behavior. @@ -502,7 +493,7 @@ fn copy_files_into_dir(files: &[PathBuf], target_dir: &Path, b: &Behavior) -> UR if !sourcepath.exists() { let err = UIoError::new( std::io::ErrorKind::NotFound, - format!("cannot stat '{}'", sourcepath.display()), + format!("cannot stat {}", sourcepath.quote()), ); show!(err); continue; @@ -566,7 +557,7 @@ fn copy(from: &Path, to: &Path, b: &Behavior) -> UResult<()> { } } - if from.to_string_lossy() == "/dev/null" { + if from.as_os_str() == "/dev/null" { /* workaround a limitation of fs::copy * https://github.com/rust-lang/rust/issues/79390 */ @@ -674,9 +665,9 @@ fn copy(from: &Path, to: &Path, b: &Behavior) -> UResult<()> { } if b.verbose { - print!("'{}' -> '{}'", from.display(), to.display()); + print!("{} -> {}", from.quote(), to.quote()); match backup_path { - Some(path) => println!(" (backup: '{}')", path.display()), + Some(path) => println!(" (backup: {})", path.quote()), None => println!(), } } diff --git a/src/uu/install/src/mode.rs b/src/uu/install/src/mode.rs index fd4cee50e..310e1fb43 100644 --- a/src/uu/install/src/mode.rs +++ b/src/uu/install/src/mode.rs @@ -22,8 +22,9 @@ pub fn parse(mode_string: &str, considering_dir: bool, umask: u32) -> Result Result<(), ()> { use std::os::unix::fs::PermissionsExt; + use uucore::display::Quotable; fs::set_permissions(path, fs::Permissions::from_mode(mode)).map_err(|err| { - show_error!("{}: chmod failed with error {}", path.display(), err); + show_error!("{}: chmod failed with error {}", path.maybe_quote(), err); }) } diff --git a/src/uu/join/src/join.rs b/src/uu/join/src/join.rs index ae991489f..51dfeec6f 100644 --- a/src/uu/join/src/join.rs +++ b/src/uu/join/src/join.rs @@ -14,6 +14,7 @@ use clap::{crate_version, App, Arg}; use std::cmp::Ordering; use std::fs::File; use std::io::{stdin, BufRead, BufReader, Lines, Stdin}; +use uucore::display::Quotable; static NAME: &str = "join"; @@ -181,18 +182,18 @@ impl Spec { return Spec::Key; } - crash!(1, "invalid field specifier: '{}'", format); + crash!(1, "invalid field specifier: {}", format.quote()); } Some('1') => FileNum::File1, Some('2') => FileNum::File2, - _ => crash!(1, "invalid file number in field spec: '{}'", format), + _ => crash!(1, "invalid file number in field spec: {}", format.quote()), }; if let Some('.') = chars.next() { return Spec::Field(file_num, parse_field_number(chars.as_str())); } - crash!(1, "invalid field specifier: '{}'", format); + crash!(1, "invalid field specifier: {}", format.quote()); } } @@ -245,7 +246,7 @@ impl<'a> State<'a> { } else { match File::open(name) { Ok(file) => Box::new(BufReader::new(file)) as Box, - Err(err) => crash!(1, "{}: {}", name, err), + Err(err) => crash!(1, "{}: {}", name.maybe_quote(), err), } }; @@ -393,7 +394,11 @@ impl<'a> State<'a> { let diff = input.compare(self.get_current_key(), line.get_field(self.key)); if diff == Ordering::Greater { - eprintln!("{}:{}: is not sorted", self.file_name, self.line_num); + eprintln!( + "{}:{}: is not sorted", + self.file_name.maybe_quote(), + self.line_num + ); // This is fatal if the check is enabled. if input.check_order == CheckOrder::Enabled { @@ -727,7 +732,7 @@ fn get_field_number(keys: Option, key: Option) -> usize { fn parse_field_number(value: &str) -> usize { match value.parse::() { Ok(result) if result > 0 => result - 1, - _ => crash!(1, "invalid field number: '{}'", value), + _ => crash!(1, "invalid field number: {}", value.quote()), } } @@ -735,7 +740,7 @@ fn parse_file_number(value: &str) -> FileNum { match value { "1" => FileNum::File1, "2" => FileNum::File2, - value => crash!(1, "invalid file number: '{}'", value), + value => crash!(1, "invalid file number: {}", value.quote()), } } diff --git a/src/uu/kill/src/kill.rs b/src/uu/kill/src/kill.rs index 47bd97dbc..494dc0602 100644 --- a/src/uu/kill/src/kill.rs +++ b/src/uu/kill/src/kill.rs @@ -13,6 +13,7 @@ extern crate uucore; use clap::{crate_version, App, Arg}; use libc::{c_int, pid_t}; use std::io::Error; +use uucore::display::Quotable; use uucore::error::{UResult, USimpleError}; use uucore::signals::ALL_SIGNALS; use uucore::InvalidEncodingHandling; @@ -154,7 +155,7 @@ fn print_signal(signal_name_or_value: &str) -> UResult<()> { } Err(USimpleError::new( 1, - format!("unknown signal name {}", signal_name_or_value), + format!("unknown signal name {}", signal_name_or_value.quote()), )) } @@ -190,7 +191,7 @@ fn kill(signalname: &str, pids: &[String]) -> UResult<()> { None => { return Err(USimpleError::new( 1, - format!("unknown signal name {}", signalname), + format!("unknown signal name {}", signalname.quote()), )); } }; @@ -204,7 +205,7 @@ fn kill(signalname: &str, pids: &[String]) -> UResult<()> { Err(e) => { return Err(USimpleError::new( 1, - format!("failed to parse argument {}: {}", pid, e), + format!("failed to parse argument {}: {}", pid.quote(), e), )); } }; diff --git a/src/uu/ln/src/ln.rs b/src/uu/ln/src/ln.rs index 4eeb637e7..e480d8f13 100644 --- a/src/uu/ln/src/ln.rs +++ b/src/uu/ln/src/ln.rs @@ -11,11 +11,12 @@ extern crate uucore; use clap::{crate_version, App, Arg}; +use uucore::display::Quotable; use uucore::error::{UError, UResult}; use std::borrow::Cow; use std::error::Error; -use std::ffi::OsStr; +use std::ffi::{OsStr, OsString}; use std::fmt::Display; use std::fs; @@ -49,26 +50,26 @@ pub enum OverwriteMode { #[derive(Debug)] enum LnError { - TargetIsDirectory(String), + TargetIsDirectory(PathBuf), SomeLinksFailed, FailedToLink(String), - MissingDestination(String), - ExtraOperand(String), + MissingDestination(PathBuf), + ExtraOperand(OsString), } impl Display for LnError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Self::TargetIsDirectory(s) => write!(f, "target '{}' is not a directory", s), - Self::FailedToLink(s) => write!(f, "failed to link '{}'", s), + Self::TargetIsDirectory(s) => write!(f, "target {} is not a directory", s.quote()), + Self::FailedToLink(e) => write!(f, "failed to link: {}", e), Self::SomeLinksFailed => write!(f, "some links failed to create"), Self::MissingDestination(s) => { - write!(f, "missing destination file operand after '{}'", s) + write!(f, "missing destination file operand after {}", s.quote()) } Self::ExtraOperand(s) => write!( f, - "extra operand '{}'\nTry '{} --help' for more information.", - s, + "extra operand {}\nTry '{} --help' for more information.", + s.quote(), uucore::execution_phrase() ), } @@ -279,10 +280,10 @@ fn exec(files: &[PathBuf], settings: &Settings) -> UResult<()> { // 1st form. Now there should be only two operands, but if -T is // specified we may have a wrong number of operands. if files.len() == 1 { - return Err(LnError::MissingDestination(files[0].to_string_lossy().into()).into()); + return Err(LnError::MissingDestination(files[0].clone()).into()); } if files.len() > 2 { - return Err(LnError::ExtraOperand(files[2].display().to_string()).into()); + return Err(LnError::ExtraOperand(files[2].clone().into()).into()); } assert!(!files.is_empty()); @@ -294,7 +295,7 @@ fn exec(files: &[PathBuf], settings: &Settings) -> UResult<()> { fn link_files_in_dir(files: &[PathBuf], target_dir: &Path, settings: &Settings) -> UResult<()> { if !target_dir.is_dir() { - return Err(LnError::TargetIsDirectory(target_dir.display().to_string()).into()); + return Err(LnError::TargetIsDirectory(target_dir.to_owned()).into()); } let mut all_successful = true; @@ -306,7 +307,7 @@ fn link_files_in_dir(files: &[PathBuf], target_dir: &Path, settings: &Settings) if is_symlink(target_dir) { if target_dir.is_file() { if let Err(e) = fs::remove_file(target_dir) { - show_error!("Could not update {}: {}", target_dir.display(), e) + show_error!("Could not update {}: {}", target_dir.quote(), e) }; } if target_dir.is_dir() { @@ -314,7 +315,7 @@ fn link_files_in_dir(files: &[PathBuf], target_dir: &Path, settings: &Settings) // considered as a dir // See test_ln::test_symlink_no_deref_dir if let Err(e) = fs::remove_dir(target_dir) { - show_error!("Could not update {}: {}", target_dir.display(), e) + show_error!("Could not update {}: {}", target_dir.quote(), e) }; } } @@ -332,10 +333,7 @@ fn link_files_in_dir(files: &[PathBuf], target_dir: &Path, settings: &Settings) } } None => { - show_error!( - "cannot stat '{}': No such file or directory", - srcpath.display() - ); + show_error!("cannot stat {}: No such file or directory", srcpath.quote()); all_successful = false; continue; } @@ -344,9 +342,9 @@ fn link_files_in_dir(files: &[PathBuf], target_dir: &Path, settings: &Settings) if let Err(e) = link(srcpath, &targetpath, settings) { show_error!( - "cannot link '{}' to '{}': {}", - targetpath.display(), - srcpath.display(), + "cannot link {} to {}: {}", + targetpath.quote(), + srcpath.quote(), e ); all_successful = false; @@ -399,7 +397,7 @@ fn link(src: &Path, dst: &Path, settings: &Settings) -> Result<()> { match settings.overwrite { OverwriteMode::NoClobber => {} OverwriteMode::Interactive => { - print!("{}: overwrite '{}'? ", uucore::util_name(), dst.display()); + print!("{}: overwrite {}? ", uucore::util_name(), dst.quote()); if !read_yes() { return Ok(()); } @@ -426,9 +424,9 @@ fn link(src: &Path, dst: &Path, settings: &Settings) -> Result<()> { } if settings.verbose { - print!("'{}' -> '{}'", dst.display(), &source.display()); + print!("{} -> {}", dst.quote(), source.quote()); match backup_path { - Some(path) => println!(" (backup: '{}')", path.display()), + Some(path) => println!(" (backup: {})", path.quote()), None => println!(), } } diff --git a/src/uu/ls/src/ls.rs b/src/uu/ls/src/ls.rs index 6f63c2a4a..fad30157c 100644 --- a/src/uu/ls/src/ls.rs +++ b/src/uu/ls/src/ls.rs @@ -40,7 +40,10 @@ use std::{ time::Duration, }; use term_grid::{Cell, Direction, Filling, Grid, GridOptions}; -use uucore::error::{set_exit_code, FromIo, UError, UResult}; +use uucore::{ + display::Quotable, + error::{set_exit_code, FromIo, UError, UResult}, +}; use unicode_width::UnicodeWidthStr; #[cfg(unix)] @@ -150,8 +153,8 @@ impl Error for LsError {} impl Display for LsError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - LsError::InvalidLineWidth(s) => write!(f, "invalid line width: '{}'", s), - LsError::NoMetadata(p) => write!(f, "could not open file: '{}'", p.display()), + LsError::InvalidLineWidth(s) => write!(f, "invalid line width: {}", s.quote()), + LsError::NoMetadata(p) => write!(f, "could not open file: {}", p.quote()), } } } @@ -410,18 +413,18 @@ impl Config { }, None => match termsize::get() { Some(size) => size.cols, - None => match std::env::var("COLUMNS") { - Ok(columns) => match columns.parse() { - Ok(columns) => columns, - Err(_) => { + None => match std::env::var_os("COLUMNS") { + Some(columns) => match columns.to_str().and_then(|s| s.parse().ok()) { + Some(columns) => columns, + None => { show_error!( - "ignoring invalid width in environment variable COLUMNS: '{}'", - columns + "ignoring invalid width in environment variable COLUMNS: {}", + columns.quote() ); DEFAULT_TERM_WIDTH } }, - Err(_) => DEFAULT_TERM_WIDTH, + None => DEFAULT_TERM_WIDTH, }, }, }; @@ -538,7 +541,7 @@ impl Config { Ok(p) => { ignore_patterns.add(p); } - Err(_) => show_warning!("Invalid pattern for ignore: '{}'", pattern), + Err(_) => show_warning!("Invalid pattern for ignore: {}", pattern.quote()), } } @@ -548,7 +551,7 @@ impl Config { Ok(p) => { ignore_patterns.add(p); } - Err(_) => show_warning!("Invalid pattern for hide: '{}'", pattern), + Err(_) => show_warning!("Invalid pattern for hide: {}", pattern.quote()), } } } @@ -1255,7 +1258,7 @@ fn list(locs: Vec<&Path>, config: Config) -> UResult<()> { if path_data.md().is_none() { show!(std::io::ErrorKind::NotFound - .map_err_context(|| format!("cannot access '{}'", path_data.p_buf.display()))); + .map_err_context(|| format!("cannot access {}", path_data.p_buf.quote()))); // We found an error, no need to continue the execution continue; } diff --git a/src/uu/mkdir/src/mkdir.rs b/src/uu/mkdir/src/mkdir.rs index 848f79988..92c068408 100644 --- a/src/uu/mkdir/src/mkdir.rs +++ b/src/uu/mkdir/src/mkdir.rs @@ -12,6 +12,7 @@ use clap::OsValues; use clap::{crate_version, App, Arg}; use std::fs; use std::path::Path; +use uucore::display::Quotable; use uucore::error::{FromIo, UResult, USimpleError}; static ABOUT: &str = "Create the given DIRECTORY(ies) if they do not exist"; @@ -43,7 +44,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { // Not tested on Windows let mode: u16 = match matches.value_of(options::MODE) { Some(m) => u16::from_str_radix(m, 8) - .map_err(|_| USimpleError::new(1, format!("invalid mode '{}'", m)))?, + .map_err(|_| USimpleError::new(1, format!("invalid mode {}", m.quote())))?, None => 0o755_u16, }; @@ -100,13 +101,13 @@ fn mkdir(path: &Path, recursive: bool, mode: u16, verbose: bool) -> UResult<()> fs::create_dir }; - create_dir(path).map_err_context(|| format!("cannot create directory '{}'", path.display()))?; + create_dir(path).map_err_context(|| format!("cannot create directory {}", path.quote()))?; if verbose { println!( - "{}: created directory '{}'", + "{}: created directory {}", uucore::util_name(), - path.display() + path.quote() ); } @@ -121,7 +122,7 @@ fn chmod(path: &Path, mode: u16) -> UResult<()> { let mode = Permissions::from_mode(u32::from(mode)); set_permissions(path, mode) - .map_err_context(|| format!("cannot set permissions '{}'", path.display())) + .map_err_context(|| format!("cannot set permissions {}", path.quote())) } #[cfg(windows)] diff --git a/src/uu/mkfifo/src/mkfifo.rs b/src/uu/mkfifo/src/mkfifo.rs index 009675811..66c3f7bb6 100644 --- a/src/uu/mkfifo/src/mkfifo.rs +++ b/src/uu/mkfifo/src/mkfifo.rs @@ -11,7 +11,7 @@ extern crate uucore; use clap::{crate_version, App, Arg}; use libc::mkfifo; use std::ffi::CString; -use uucore::InvalidEncodingHandling; +use uucore::{display::Quotable, InvalidEncodingHandling}; static NAME: &str = "mkfifo"; static USAGE: &str = "mkfifo [OPTION]... NAME..."; @@ -61,7 +61,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { mkfifo(name.as_ptr(), mode as libc::mode_t) }; if err == -1 { - show_error!("cannot create fifo '{}': File exists", f); + show_error!("cannot create fifo {}: File exists", f.quote()); exit_code = 1; } } diff --git a/src/uu/mknod/src/mknod.rs b/src/uu/mknod/src/mknod.rs index f8fb9c469..dd529c3ba 100644 --- a/src/uu/mknod/src/mknod.rs +++ b/src/uu/mknod/src/mknod.rs @@ -16,6 +16,7 @@ use clap::{crate_version, App, Arg, ArgMatches}; use libc::{dev_t, mode_t}; use libc::{S_IFBLK, S_IFCHR, S_IFIFO, S_IRGRP, S_IROTH, S_IRUSR, S_IWGRP, S_IWOTH, S_IWUSR}; +use uucore::display::Quotable; use uucore::InvalidEncodingHandling; static ABOUT: &str = "Create the special file NAME of the given TYPE."; @@ -219,7 +220,7 @@ fn valid_type(tpe: String) -> Result<(), String> { if vec!['b', 'c', 'u', 'p'].contains(&first_char) { Ok(()) } else { - Err(format!("invalid device type '{}'", tpe)) + Err(format!("invalid device type {}", tpe.quote())) } }) } diff --git a/src/uu/mktemp/src/mktemp.rs b/src/uu/mktemp/src/mktemp.rs index 0b30f0087..59e82569a 100644 --- a/src/uu/mktemp/src/mktemp.rs +++ b/src/uu/mktemp/src/mktemp.rs @@ -12,6 +12,7 @@ extern crate uucore; use clap::{crate_version, App, Arg}; +use uucore::display::{println_verbatim, Quotable}; use uucore::error::{FromIo, UError, UResult}; use std::env; @@ -57,16 +58,20 @@ impl Display for MkTempError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { use MkTempError::*; match self { - PersistError(p) => write!(f, "could not persist file '{}'", p.display()), - MustEndInX(s) => write!(f, "with --suffix, template '{}' must end in X", s), - TooFewXs(s) => write!(f, "too few X's in template '{}'", s), + PersistError(p) => write!(f, "could not persist file {}", p.quote()), + MustEndInX(s) => write!(f, "with --suffix, template {} must end in X", s.quote()), + TooFewXs(s) => write!(f, "too few X's in template {}", s.quote()), ContainsDirSeparator(s) => { - write!(f, "invalid suffix '{}', contains directory separator", s) + write!( + f, + "invalid suffix {}, contains directory separator", + s.quote() + ) } InvalidTemplate(s) => write!( f, - "invalid template, '{}'; with --tmpdir, it may not be absolute", - s + "invalid template, {}; with --tmpdir, it may not be absolute", + s.quote() ), } } @@ -244,8 +249,7 @@ pub fn dry_exec(mut tmpdir: PathBuf, prefix: &str, rand: usize, suffix: &str) -> } } tmpdir.push(buf); - println!("{}", tmpdir.display()); - Ok(()) + println_verbatim(tmpdir).map_err_context(|| "failed to print directory name".to_owned()) } fn exec(dir: PathBuf, prefix: &str, rand: usize, suffix: &str, make_dir: bool) -> UResult<()> { @@ -274,6 +278,5 @@ fn exec(dir: PathBuf, prefix: &str, rand: usize, suffix: &str, make_dir: bool) - .map_err(|e| MkTempError::PersistError(e.file.path().to_path_buf()))? .1 }; - println!("{}", path.display()); - Ok(()) + println_verbatim(path).map_err_context(|| "failed to print directory name".to_owned()) } diff --git a/src/uu/more/src/more.rs b/src/uu/more/src/more.rs index 8097d1402..3a601c1e8 100644 --- a/src/uu/more/src/more.rs +++ b/src/uu/more/src/more.rs @@ -30,6 +30,7 @@ use crossterm::{ use unicode_segmentation::UnicodeSegmentation; use unicode_width::UnicodeWidthStr; +use uucore::display::Quotable; const BELL: &str = "\x07"; @@ -64,12 +65,12 @@ pub fn uumain(args: impl uucore::Args) -> i32 { let file = Path::new(file); if file.is_dir() { terminal::disable_raw_mode().unwrap(); - show_usage_error!("'{}' is a directory.", file.display()); + show_usage_error!("{} is a directory.", file.quote()); return 1; } if !file.exists() { terminal::disable_raw_mode().unwrap(); - show_error!("cannot open {}: No such file or directory", file.display()); + show_error!("cannot open {}: No such file or directory", file.quote()); return 1; } if length > 1 { diff --git a/src/uu/mv/src/mv.rs b/src/uu/mv/src/mv.rs index e2e2352a0..9d23f86de 100644 --- a/src/uu/mv/src/mv.rs +++ b/src/uu/mv/src/mv.rs @@ -21,6 +21,7 @@ use std::os::unix; use std::os::windows; use std::path::{Path, PathBuf}; use uucore::backup_control::{self, BackupMode}; +use uucore::display::Quotable; use fs_extra::dir::{move_dir, CopyOptions as DirCopyOptions}; @@ -223,10 +224,7 @@ fn exec(files: &[PathBuf], b: Behavior) -> i32 { // `Ok()` results unless the source does not exist, or the user // lacks permission to access metadata. if source.symlink_metadata().is_err() { - show_error!( - "cannot stat '{}': No such file or directory", - source.display() - ); + show_error!("cannot stat {}: No such file or directory", source.quote()); return 1; } @@ -234,8 +232,8 @@ fn exec(files: &[PathBuf], b: Behavior) -> i32 { if b.no_target_dir { if !source.is_dir() { show_error!( - "cannot overwrite directory '{}' with non-directory", - target.display() + "cannot overwrite directory {} with non-directory", + target.quote() ); return 1; } @@ -243,9 +241,9 @@ fn exec(files: &[PathBuf], b: Behavior) -> i32 { return match rename(source, target, &b) { Err(e) => { show_error!( - "cannot move '{}' to '{}': {}", - source.display(), - target.display(), + "cannot move {} to {}: {}", + source.quote(), + target.quote(), e.to_string() ); 1 @@ -257,9 +255,9 @@ fn exec(files: &[PathBuf], b: Behavior) -> i32 { return move_files_into_dir(&[source.clone()], target, &b); } else if target.exists() && source.is_dir() { show_error!( - "cannot overwrite non-directory '{}' with directory '{}'", - target.display(), - source.display() + "cannot overwrite non-directory {} with directory {}", + target.quote(), + source.quote() ); return 1; } @@ -272,9 +270,9 @@ fn exec(files: &[PathBuf], b: Behavior) -> i32 { _ => { if b.no_target_dir { show_error!( - "mv: extra operand '{}'\n\ + "mv: extra operand {}\n\ Try '{} --help' for more information.", - files[2].display(), + files[2].quote(), uucore::execution_phrase() ); return 1; @@ -288,7 +286,7 @@ fn exec(files: &[PathBuf], b: Behavior) -> i32 { fn move_files_into_dir(files: &[PathBuf], target_dir: &Path, b: &Behavior) -> i32 { if !target_dir.is_dir() { - show_error!("target '{}' is not a directory", target_dir.display()); + show_error!("target {} is not a directory", target_dir.quote()); return 1; } @@ -298,8 +296,8 @@ fn move_files_into_dir(files: &[PathBuf], target_dir: &Path, b: &Behavior) -> i3 Some(name) => target_dir.join(name), None => { show_error!( - "cannot stat '{}': No such file or directory", - sourcepath.display() + "cannot stat {}: No such file or directory", + sourcepath.quote() ); all_successful = false; @@ -309,9 +307,9 @@ fn move_files_into_dir(files: &[PathBuf], target_dir: &Path, b: &Behavior) -> i3 if let Err(e) = rename(sourcepath, &targetpath, b) { show_error!( - "cannot move '{}' to '{}': {}", - sourcepath.display(), - targetpath.display(), + "cannot move {} to {}: {}", + sourcepath.quote(), + targetpath.quote(), e.to_string() ); all_successful = false; @@ -332,7 +330,7 @@ fn rename(from: &Path, to: &Path, b: &Behavior) -> io::Result<()> { match b.overwrite { OverwriteMode::NoClobber => return Ok(()), OverwriteMode::Interactive => { - println!("{}: overwrite '{}'? ", uucore::util_name(), to.display()); + println!("{}: overwrite {}? ", uucore::util_name(), to.quote()); if !read_yes() { return Ok(()); } @@ -365,9 +363,9 @@ fn rename(from: &Path, to: &Path, b: &Behavior) -> io::Result<()> { rename_with_fallback(from, to)?; if b.verbose { - print!("'{}' -> '{}'", from.display(), to.display()); + print!("{} -> {}", from.quote(), to.quote()); match backup_path { - Some(path) => println!(" (backup: '{}')", path.display()), + Some(path) => println!(" (backup: {})", path.quote()), None => println!(), } } diff --git a/src/uu/nohup/src/nohup.rs b/src/uu/nohup/src/nohup.rs index 1ecb9914f..d83170ae8 100644 --- a/src/uu/nohup/src/nohup.rs +++ b/src/uu/nohup/src/nohup.rs @@ -19,6 +19,7 @@ use std::fs::{File, OpenOptions}; use std::io::Error; use std::os::unix::prelude::*; use std::path::{Path, PathBuf}; +use uucore::display::Quotable; use uucore::InvalidEncodingHandling; static ABOUT: &str = "Run COMMAND ignoring hangup signals."; @@ -122,13 +123,16 @@ fn find_stdout() -> File { .open(Path::new(NOHUP_OUT)) { Ok(t) => { - show_error!("ignoring input and appending output to '{}'", NOHUP_OUT); + show_error!( + "ignoring input and appending output to {}", + NOHUP_OUT.quote() + ); t } Err(e1) => { let home = match env::var("HOME") { Err(_) => { - show_error!("failed to open '{}': {}", NOHUP_OUT, e1); + show_error!("failed to open {}: {}", NOHUP_OUT.quote(), e1); exit!(internal_failure_code) } Ok(h) => h, @@ -143,12 +147,15 @@ fn find_stdout() -> File { .open(&homeout) { Ok(t) => { - show_error!("ignoring input and appending output to '{}'", homeout_str); + show_error!( + "ignoring input and appending output to {}", + homeout_str.quote() + ); t } Err(e2) => { - show_error!("failed to open '{}': {}", NOHUP_OUT, e1); - show_error!("failed to open '{}': {}", homeout_str, e2); + show_error!("failed to open {}: {}", NOHUP_OUT.quote(), e1); + show_error!("failed to open {}: {}", homeout_str.quote(), e2); exit!(internal_failure_code) } } diff --git a/src/uu/numfmt/src/format.rs b/src/uu/numfmt/src/format.rs index e44446818..bdee83e12 100644 --- a/src/uu/numfmt/src/format.rs +++ b/src/uu/numfmt/src/format.rs @@ -1,3 +1,5 @@ +use uucore::display::Quotable; + use crate::options::{NumfmtOptions, RoundMethod}; use crate::units::{DisplayableSuffix, RawSuffix, Result, Suffix, Unit, IEC_BASES, SI_BASES}; @@ -78,7 +80,7 @@ fn parse_suffix(s: &str) -> Result<(f64, Option)> { Some('Z') => Some((RawSuffix::Z, with_i)), Some('Y') => Some((RawSuffix::Y, with_i)), Some('0'..='9') => None, - _ => return Err(format!("invalid suffix in input: '{}'", s)), + _ => return Err(format!("invalid suffix in input: {}", s.quote())), }; let suffix_len = match suffix { @@ -89,7 +91,7 @@ fn parse_suffix(s: &str) -> Result<(f64, Option)> { let number = s[..s.len() - suffix_len] .parse::() - .map_err(|_| format!("invalid number: '{}'", s))?; + .map_err(|_| format!("invalid number: {}", s.quote()))?; Ok((number, suffix)) } diff --git a/src/uu/numfmt/src/numfmt.rs b/src/uu/numfmt/src/numfmt.rs index 1798975dc..da2fa8130 100644 --- a/src/uu/numfmt/src/numfmt.rs +++ b/src/uu/numfmt/src/numfmt.rs @@ -15,6 +15,7 @@ use crate::options::*; use crate::units::{Result, Unit}; use clap::{crate_version, App, AppSettings, Arg, ArgMatches}; use std::io::{BufRead, Write}; +use uucore::display::Quotable; use uucore::ranges::Range; pub mod format; @@ -113,7 +114,7 @@ fn parse_options(args: &ArgMatches) -> Result { 0 => Err(value), _ => Ok(n), }) - .map_err(|value| format!("invalid header value '{}'", value)) + .map_err(|value| format!("invalid header value {}", value.quote())) } }?; diff --git a/src/uu/od/src/multifilereader.rs b/src/uu/od/src/multifilereader.rs index 0f8616467..90796b2eb 100644 --- a/src/uu/od/src/multifilereader.rs +++ b/src/uu/od/src/multifilereader.rs @@ -5,6 +5,8 @@ use std::io; use std::io::BufReader; use std::vec::Vec; +use uucore::display::Quotable; + pub enum InputSource<'a> { FileName(&'a str), Stdin, @@ -57,7 +59,7 @@ impl<'b> MultifileReader<'b> { // print an error at the time that the file is needed, // then move on the the next file. // This matches the behavior of the original `od` - eprintln!("{}: '{}': {}", uucore::util_name(), fname, e); + eprintln!("{}: {}: {}", uucore::util_name(), fname.maybe_quote(), e); self.any_err = true } } diff --git a/src/uu/od/src/od.rs b/src/uu/od/src/od.rs index 6c1110362..e9983f991 100644 --- a/src/uu/od/src/od.rs +++ b/src/uu/od/src/od.rs @@ -43,6 +43,7 @@ use crate::partialreader::*; use crate::peekreader::*; use crate::prn_char::format_ascii_dump; use clap::{self, crate_version, AppSettings, Arg, ArgMatches}; +use uucore::display::Quotable; use uucore::parse_size::ParseSizeError; use uucore::InvalidEncodingHandling; @@ -635,7 +636,7 @@ fn format_error_message(error: ParseSizeError, s: &str, option: &str) -> String // GNU's od echos affected flag, -N or --read-bytes (-j or --skip-bytes, etc.), depending user's selection // GNU's od does distinguish between "invalid (suffix in) argument" match error { - ParseSizeError::ParseFailure(_) => format!("invalid --{} argument '{}'", option, s), - ParseSizeError::SizeTooBig(_) => format!("--{} argument '{}' too large", option, s), + ParseSizeError::ParseFailure(_) => format!("invalid --{} argument {}", option, s.quote()), + ParseSizeError::SizeTooBig(_) => format!("--{} argument {} too large", option, s.quote()), } } diff --git a/src/uu/od/src/parse_formats.rs b/src/uu/od/src/parse_formats.rs index 80d477b27..301bb5154 100644 --- a/src/uu/od/src/parse_formats.rs +++ b/src/uu/od/src/parse_formats.rs @@ -1,5 +1,7 @@ // spell-checker:ignore formatteriteminfo docopt fvox fvoxw vals acdx +use uucore::display::Quotable; + use crate::formatteriteminfo::FormatterItemInfo; use crate::prn_char::*; use crate::prn_float::*; @@ -272,8 +274,9 @@ fn parse_type_string(params: &str) -> Result, Strin while let Some(type_char) = ch { let type_char = format_type(type_char).ok_or_else(|| { format!( - "unexpected char '{}' in format specification '{}'", - type_char, params + "unexpected char '{}' in format specification {}", + type_char, + params.quote() ) })?; @@ -293,8 +296,9 @@ fn parse_type_string(params: &str) -> Result, Strin if !decimal_size.is_empty() { byte_size = decimal_size.parse().map_err(|_| { format!( - "invalid number '{}' in format specification '{}'", - decimal_size, params + "invalid number {} in format specification {}", + decimal_size.quote(), + params.quote() ) })?; } @@ -305,8 +309,9 @@ fn parse_type_string(params: &str) -> Result, Strin let ft = od_format_type(type_char, byte_size).ok_or_else(|| { format!( - "invalid size '{}' in format specification '{}'", - byte_size, params + "invalid size '{}' in format specification {}", + byte_size, + params.quote() ) })?; formats.push(ParsedFormatterItemInfo::new(ft, show_ascii_dump)); diff --git a/src/uu/pathchk/src/pathchk.rs b/src/uu/pathchk/src/pathchk.rs index 863bb6bf2..8afeaff18 100644 --- a/src/uu/pathchk/src/pathchk.rs +++ b/src/uu/pathchk/src/pathchk.rs @@ -15,6 +15,7 @@ extern crate uucore; use clap::{crate_version, App, Arg}; use std::fs; use std::io::{ErrorKind, Write}; +use uucore::display::Quotable; use uucore::InvalidEncodingHandling; // operating mode @@ -153,10 +154,10 @@ fn check_basic(path: &[String]) -> bool { if component_len > POSIX_NAME_MAX { writeln!( &mut std::io::stderr(), - "limit {} exceeded by length {} of file name component '{}'", + "limit {} exceeded by length {} of file name component {}", POSIX_NAME_MAX, component_len, - p + p.quote() ); return false; } @@ -175,8 +176,8 @@ fn check_extra(path: &[String]) -> bool { if p.starts_with('-') { writeln!( &mut std::io::stderr(), - "leading hyphen in file name component '{}'", - p + "leading hyphen in file name component {}", + p.quote() ); return false; } @@ -197,10 +198,10 @@ fn check_default(path: &[String]) -> bool { if total_len > libc::PATH_MAX as usize { writeln!( &mut std::io::stderr(), - "limit {} exceeded by length {} of file name '{}'", + "limit {} exceeded by length {} of file name {}", libc::PATH_MAX, total_len, - joined_path + joined_path.quote() ); return false; } @@ -210,10 +211,10 @@ fn check_default(path: &[String]) -> bool { if component_len > libc::FILENAME_MAX as usize { writeln!( &mut std::io::stderr(), - "limit {} exceeded by length {} of file name component '{}'", + "limit {} exceeded by length {} of file name component {}", libc::FILENAME_MAX, component_len, - p + p.quote() ); return false; } @@ -246,9 +247,9 @@ fn check_portable_chars(path_segment: &str) -> bool { let invalid = path_segment[i..].chars().next().unwrap(); writeln!( &mut std::io::stderr(), - "nonportable character '{}' in file name component '{}'", + "nonportable character '{}' in file name component {}", invalid, - path_segment + path_segment.quote() ); return false; } diff --git a/src/uu/pr/src/pr.rs b/src/uu/pr/src/pr.rs index 1358cef6c..45d9480a7 100644 --- a/src/uu/pr/src/pr.rs +++ b/src/uu/pr/src/pr.rs @@ -24,6 +24,8 @@ use std::io::{stdin, stdout, BufRead, BufReader, Lines, Read, Stdout, Write}; #[cfg(unix)] use std::os::unix::fs::FileTypeExt; +use uucore::display::Quotable; + type IOError = std::io::Error; const NAME: &str = "pr"; @@ -517,7 +519,7 @@ fn parse_usize(matches: &Matches, opt: &str) -> Option> { let i = value_to_parse.0; let option = value_to_parse.1; i.parse().map_err(|_e| { - PrError::EncounteredErrors(format!("invalid {} argument '{}'", option, i)) + PrError::EncounteredErrors(format!("invalid {} argument {}", option, i.quote())) }) }; matches @@ -619,7 +621,7 @@ fn build_options( let unparsed_num = i.get(1).unwrap().as_str().trim(); let x: Vec<_> = unparsed_num.split(':').collect(); x[0].to_string().parse::().map_err(|_e| { - PrError::EncounteredErrors(format!("invalid {} argument '{}'", "+", unparsed_num)) + PrError::EncounteredErrors(format!("invalid {} argument {}", "+", unparsed_num.quote())) }) }) { Some(res) => res?, @@ -633,7 +635,11 @@ fn build_options( .map(|unparsed_num| { let x: Vec<_> = unparsed_num.split(':').collect(); x[1].to_string().parse::().map_err(|_e| { - PrError::EncounteredErrors(format!("invalid {} argument '{}'", "+", unparsed_num)) + PrError::EncounteredErrors(format!( + "invalid {} argument {}", + "+", + unparsed_num.quote() + )) }) }) { Some(res) => Some(res?), @@ -643,7 +649,10 @@ fn build_options( let invalid_pages_map = |i: String| { let unparsed_value = matches.opt_str(options::PAGE_RANGE_OPTION).unwrap(); i.parse::().map_err(|_e| { - PrError::EncounteredErrors(format!("invalid --pages argument '{}'", unparsed_value)) + PrError::EncounteredErrors(format!( + "invalid --pages argument {}", + unparsed_value.quote() + )) }) }; @@ -741,7 +750,7 @@ fn build_options( let start_column_option = match re_col.captures(&free_args).map(|i| { let unparsed_num = i.get(1).unwrap().as_str().trim(); unparsed_num.parse::().map_err(|_e| { - PrError::EncounteredErrors(format!("invalid {} argument '{}'", "-", unparsed_num)) + PrError::EncounteredErrors(format!("invalid {} argument {}", "-", unparsed_num.quote())) }) }) { Some(res) => Some(res?), diff --git a/src/uu/printf/src/cli.rs b/src/uu/printf/src/cli.rs index a5e9c9775..fa2fda1d2 100644 --- a/src/uu/printf/src/cli.rs +++ b/src/uu/printf/src/cli.rs @@ -2,20 +2,11 @@ // spell-checker:ignore (ToDO) bslice -use std::env; -use std::io::{stderr, stdout, Write}; +use std::io::{stdout, Write}; pub const EXIT_OK: i32 = 0; pub const EXIT_ERR: i32 = 1; -pub fn err_msg(msg: &str) { - let exe_path = match env::current_exe() { - Ok(p) => p.to_string_lossy().into_owned(), - _ => String::from(""), - }; - writeln!(&mut stderr(), "{}: {}", exe_path, msg).unwrap(); -} - // by default stdout only flushes // to console when a newline is passed. pub fn flush_char(c: char) { diff --git a/src/uu/printf/src/memo.rs b/src/uu/printf/src/memo.rs index 2f12f9192..f5d41aeb6 100644 --- a/src/uu/printf/src/memo.rs +++ b/src/uu/printf/src/memo.rs @@ -8,8 +8,9 @@ use itertools::put_back_n; use std::iter::Peekable; use std::slice::Iter; +use uucore::display::Quotable; +use uucore::show_error; -use crate::cli; use crate::tokenize::sub::Sub; use crate::tokenize::token::{Token, Tokenizer}; use crate::tokenize::unescaped_text::UnescapedText; @@ -19,10 +20,10 @@ pub struct Memo { } fn warn_excess_args(first_arg: &str) { - cli::err_msg(&format!( - "warning: ignoring excess arguments, starting with '{}'", - first_arg - )); + show_error!( + "warning: ignoring excess arguments, starting with {}", + first_arg.quote() + ); } impl Memo { diff --git a/src/uu/printf/src/tokenize/num_format/formatter.rs b/src/uu/printf/src/tokenize/num_format/formatter.rs index f5f5d71b1..0438f78bf 100644 --- a/src/uu/printf/src/tokenize/num_format/formatter.rs +++ b/src/uu/printf/src/tokenize/num_format/formatter.rs @@ -3,11 +3,10 @@ use itertools::{put_back_n, PutBackN}; use std::str::Chars; +use uucore::{display::Quotable, show_error}; use super::format_field::FormatField; -use crate::cli; - // contains the rough ingredients to final // output for a number, organized together // to allow for easy generalization of output manipulation @@ -66,5 +65,5 @@ pub fn get_it_at(offset: usize, str_in: &str) -> PutBackN { // TODO: put this somewhere better pub fn warn_incomplete_conv(pf_arg: &str) { // important: keep println here not print - cli::err_msg(&format!("{}: value not completely converted", pf_arg)) + show_error!("{}: value not completely converted", pf_arg.maybe_quote()); } diff --git a/src/uu/printf/src/tokenize/num_format/num_format.rs b/src/uu/printf/src/tokenize/num_format/num_format.rs index b32731f2d..74666ad8e 100644 --- a/src/uu/printf/src/tokenize/num_format/num_format.rs +++ b/src/uu/printf/src/tokenize/num_format/num_format.rs @@ -7,6 +7,9 @@ use std::env; use std::vec::Vec; +use uucore::display::Quotable; +use uucore::show_error; + use super::format_field::{FieldType, FormatField}; use super::formatter::{Base, FormatPrimitive, Formatter, InitialPrefix}; use super::formatters::cninetyninehexfloatf::CninetyNineHexFloatf; @@ -15,11 +18,9 @@ use super::formatters::floatf::Floatf; use super::formatters::intf::Intf; use super::formatters::scif::Scif; -use crate::cli; - pub fn warn_expected_numeric(pf_arg: &str) { // important: keep println here not print - cli::err_msg(&format!("{}: expected a numeric value", pf_arg)); + show_error!("{}: expected a numeric value", pf_arg.maybe_quote()); } // when character constant arguments have excess characters @@ -29,11 +30,11 @@ fn warn_char_constant_ign(remaining_bytes: Vec) { Ok(_) => {} Err(e) => { if let env::VarError::NotPresent = e { - cli::err_msg(&format!( + show_error!( "warning: {:?}: character(s) following character \ constant have been ignored", &*remaining_bytes - )); + ); } } } diff --git a/src/uu/printf/src/tokenize/sub.rs b/src/uu/printf/src/tokenize/sub.rs index d01e00699..48d854fab 100644 --- a/src/uu/printf/src/tokenize/sub.rs +++ b/src/uu/printf/src/tokenize/sub.rs @@ -10,6 +10,7 @@ use std::iter::Peekable; use std::process::exit; use std::slice::Iter; use std::str::Chars; +use uucore::show_error; // use std::collections::HashSet; use super::num_format::format_field::{FieldType, FormatField}; @@ -19,7 +20,7 @@ use super::unescaped_text::UnescapedText; use crate::cli; fn err_conv(sofar: &str) { - cli::err_msg(&format!("%{}: invalid conversion specification", sofar)); + show_error!("%{}: invalid conversion specification", sofar); exit(cli::EXIT_ERR); } diff --git a/src/uu/ptx/src/ptx.rs b/src/uu/ptx/src/ptx.rs index 264a37d72..3619b8d4d 100644 --- a/src/uu/ptx/src/ptx.rs +++ b/src/uu/ptx/src/ptx.rs @@ -17,6 +17,7 @@ use std::collections::{BTreeSet, HashMap, HashSet}; use std::default::Default; use std::fs::File; use std::io::{stdin, stdout, BufRead, BufReader, BufWriter, Read, Write}; +use uucore::display::Quotable; use uucore::InvalidEncodingHandling; static NAME: &str = "ptx"; @@ -292,7 +293,11 @@ fn create_word_set(config: &Config, filter: &WordFilter, file_map: &FileMap) -> fn get_reference(config: &Config, word_ref: &WordRef, line: &str, context_reg: &Regex) -> String { if config.auto_ref { - format!("{}:{}", word_ref.filename, word_ref.local_line_nr + 1) + format!( + "{}:{}", + word_ref.filename.maybe_quote(), + word_ref.local_line_nr + 1 + ) } else if config.input_ref { let (beg, end) = match context_reg.find(line) { Some(x) => (x.start(), x.end()), diff --git a/src/uu/readlink/src/readlink.rs b/src/uu/readlink/src/readlink.rs index 1c62d8278..d6dd1634a 100644 --- a/src/uu/readlink/src/readlink.rs +++ b/src/uu/readlink/src/readlink.rs @@ -14,6 +14,7 @@ use clap::{crate_version, App, Arg}; use std::fs; use std::io::{stdout, Write}; use std::path::{Path, PathBuf}; +use uucore::display::Quotable; use uucore::fs::{canonicalize, MissingHandling, ResolveMode}; const ABOUT: &str = "Print value of a symbolic link or canonical file name."; @@ -71,10 +72,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { } if no_newline && files.len() > 1 && !silent { - eprintln!( - "{}: ignoring --no-newline with multiple arguments", - uucore::util_name() - ); + show_error!("ignoring --no-newline with multiple arguments"); no_newline = false; } @@ -85,12 +83,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { Ok(path) => show(&path, no_newline, use_zero), Err(err) => { if verbose { - eprintln!( - "{}: {}: errno {}", - uucore::util_name(), - f, - err.raw_os_error().unwrap() - ); + show_error!("{}: errno {}", f.maybe_quote(), err.raw_os_error().unwrap()); } return 1; } @@ -100,12 +93,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { Ok(path) => show(&path, no_newline, use_zero), Err(err) => { if verbose { - eprintln!( - "{}: {}: errno {:?}", - uucore::util_name(), - f, - err.raw_os_error().unwrap() - ); + show_error!("{}: errno {}", f.maybe_quote(), err.raw_os_error().unwrap()); } return 1; } diff --git a/src/uu/realpath/src/realpath.rs b/src/uu/realpath/src/realpath.rs index 451253d50..d13aed6c7 100644 --- a/src/uu/realpath/src/realpath.rs +++ b/src/uu/realpath/src/realpath.rs @@ -11,8 +11,14 @@ extern crate uucore; use clap::{crate_version, App, Arg}; -use std::path::{Path, PathBuf}; -use uucore::fs::{canonicalize, MissingHandling, ResolveMode}; +use std::{ + io::{stdout, Write}, + path::{Path, PathBuf}, +}; +use uucore::{ + display::{print_verbatim, Quotable}, + fs::{canonicalize, MissingHandling, ResolveMode}, +}; static ABOUT: &str = "print the resolved path"; @@ -58,7 +64,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { for path in &paths { if let Err(e) = resolve_path(path, strip, zero, logical, can_mode) { if !quiet { - show_error!("{}: {}", e, path.display()); + show_error!("{}: {}", path.maybe_quote(), e); } retcode = 1 }; @@ -154,8 +160,9 @@ fn resolve_path( ResolveMode::Physical }; let abs = canonicalize(p, can_mode, resolve)?; - let line_ending = if zero { '\0' } else { '\n' }; + let line_ending = if zero { b'\0' } else { b'\n' }; - print!("{}{}", abs.display(), line_ending); + print_verbatim(&abs)?; + stdout().write_all(&[line_ending])?; Ok(()) } diff --git a/src/uu/relpath/src/relpath.rs b/src/uu/relpath/src/relpath.rs index 95f03e423..16b920861 100644 --- a/src/uu/relpath/src/relpath.rs +++ b/src/uu/relpath/src/relpath.rs @@ -10,6 +10,7 @@ use clap::{crate_version, App, Arg}; use std::env; use std::path::{Path, PathBuf}; +use uucore::display::println_verbatim; use uucore::fs::{canonicalize, MissingHandling, ResolveMode}; use uucore::InvalidEncodingHandling; @@ -48,7 +49,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { if !absto.as_path().starts_with(absbase.as_path()) || !absfrom.as_path().starts_with(absbase.as_path()) { - println!("{}", absto.display()); + println_verbatim(absto).unwrap(); return 0; } } @@ -74,7 +75,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .map(|x| result.push(x.as_os_str())) .last(); - println!("{}", result.display()); + println_verbatim(result).unwrap(); 0 } diff --git a/src/uu/rm/src/rm.rs b/src/uu/rm/src/rm.rs index 0613ff857..54fce52ff 100644 --- a/src/uu/rm/src/rm.rs +++ b/src/uu/rm/src/rm.rs @@ -17,6 +17,7 @@ use std::fs; use std::io::{stderr, stdin, BufRead, Write}; use std::ops::BitOr; use std::path::{Path, PathBuf}; +use uucore::display::Quotable; use walkdir::{DirEntry, WalkDir}; #[derive(Eq, PartialEq, Clone, Copy)] @@ -236,7 +237,10 @@ fn remove(files: Vec, options: Options) -> bool { // (e.g., permission), even rm -f should fail with // outputting the error, but there's no easy eay. if !options.force { - show_error!("cannot remove '{}': No such file or directory", filename); + show_error!( + "cannot remove {}: No such file or directory", + filename.quote() + ); true } else { false @@ -263,13 +267,9 @@ fn handle_dir(path: &Path, options: &Options) -> bool { // GNU compatibility (rm/fail-eacces.sh) // here, GNU doesn't use some kind of remove_dir_all // It will show directory+file - show_error!( - "cannot remove '{}': {}", - path.display(), - "Permission denied" - ); + show_error!("cannot remove {}: {}", path.quote(), "Permission denied"); } else { - show_error!("cannot remove '{}': {}", path.display(), e); + show_error!("cannot remove {}: {}", path.quote(), e); } } } else { @@ -287,7 +287,7 @@ fn handle_dir(path: &Path, options: &Options) -> bool { } Err(e) => { had_err = true; - show_error!("recursing in '{}': {}", path.display(), e); + show_error!("recursing in {}: {}", path.quote(), e); } } } @@ -299,12 +299,12 @@ fn handle_dir(path: &Path, options: &Options) -> bool { } else if options.dir && (!is_root || !options.preserve_root) { had_err = remove_dir(path, options).bitor(had_err); } else if options.recursive { - show_error!("could not remove directory '{}'", path.display()); + show_error!("could not remove directory {}", path.quote()); had_err = true; } else { show_error!( - "cannot remove '{}': Is a directory", // GNU's rm error message does not include help - path.display() + "cannot remove {}: Is a directory", // GNU's rm error message does not include help + path.quote() ); had_err = true; } @@ -325,36 +325,36 @@ fn remove_dir(path: &Path, options: &Options) -> bool { match fs::remove_dir(path) { Ok(_) => { if options.verbose { - println!("removed directory '{}'", normalize(path).display()); + println!("removed directory {}", normalize(path).quote()); } } Err(e) => { if e.kind() == std::io::ErrorKind::PermissionDenied { // GNU compatibility (rm/fail-eacces.sh) show_error!( - "cannot remove '{}': {}", - path.display(), + "cannot remove {}: {}", + path.quote(), "Permission denied" ); } else { - show_error!("cannot remove '{}': {}", path.display(), e); + show_error!("cannot remove {}: {}", path.quote(), e); } return true; } } } else { // directory can be read but is not empty - show_error!("cannot remove '{}': Directory not empty", path.display()); + show_error!("cannot remove {}: Directory not empty", path.quote()); return true; } } else { // called to remove a symlink_dir (windows) without "-r"/"-R" or "-d" - show_error!("cannot remove '{}': Is a directory", path.display()); + show_error!("cannot remove {}: Is a directory", path.quote()); return true; } } else { // GNU's rm shows this message if directory is empty but not readable - show_error!("cannot remove '{}': Directory not empty", path.display()); + show_error!("cannot remove {}: Directory not empty", path.quote()); return true; } } @@ -372,19 +372,15 @@ fn remove_file(path: &Path, options: &Options) -> bool { match fs::remove_file(path) { Ok(_) => { if options.verbose { - println!("removed '{}'", normalize(path).display()); + println!("removed {}", normalize(path).quote()); } } Err(e) => { if e.kind() == std::io::ErrorKind::PermissionDenied { // GNU compatibility (rm/fail-eacces.sh) - show_error!( - "cannot remove '{}': {}", - path.display(), - "Permission denied" - ); + show_error!("cannot remove {}: {}", path.quote(), "Permission denied"); } else { - show_error!("cannot remove '{}': {}", path.display(), e); + show_error!("cannot remove {}: {}", path.quote(), e); } return true; } @@ -396,9 +392,9 @@ fn remove_file(path: &Path, options: &Options) -> bool { fn prompt_file(path: &Path, is_dir: bool) -> bool { if is_dir { - prompt(&(format!("rm: remove directory '{}'? ", path.display()))) + prompt(&(format!("rm: remove directory {}? ", path.quote()))) } else { - prompt(&(format!("rm: remove file '{}'? ", path.display()))) + prompt(&(format!("rm: remove file {}? ", path.quote()))) } } diff --git a/src/uu/runcon/src/errors.rs b/src/uu/runcon/src/errors.rs index bc10a2f3e..5f8258de0 100644 --- a/src/uu/runcon/src/errors.rs +++ b/src/uu/runcon/src/errors.rs @@ -3,6 +3,8 @@ use std::fmt::Write; use std::io; use std::str::Utf8Error; +use uucore::display::Quotable; + pub(crate) type Result = std::result::Result; #[derive(thiserror::Error, Debug)] @@ -31,7 +33,7 @@ pub(crate) enum Error { source: io::Error, }, - #[error("{operation} failed on '{}'", .operand1.to_string_lossy())] + #[error("{operation} failed on {}", .operand1.quote())] Io1 { operation: &'static str, operand1: OsString, diff --git a/src/uu/seq/src/seq.rs b/src/uu/seq/src/seq.rs index 730c5efc7..501b12ac0 100644 --- a/src/uu/seq/src/seq.rs +++ b/src/uu/seq/src/seq.rs @@ -14,6 +14,7 @@ use num_traits::{Num, ToPrimitive}; use std::cmp; use std::io::{stdout, Write}; use std::str::FromStr; +use uucore::display::Quotable; static ABOUT: &str = "Display numbers from FIRST to LAST, in steps of INCREMENT."; static OPT_SEPARATOR: &str = "separator"; @@ -115,14 +116,14 @@ impl FromStr for Number { } Err(_) => match s.parse::() { Ok(value) if value.is_nan() => Err(format!( - "invalid 'not-a-number' argument: '{}'\nTry '{} --help' for more information.", - s, + "invalid 'not-a-number' argument: {}\nTry '{} --help' for more information.", + s.quote(), uucore::execution_phrase(), )), Ok(value) => Ok(Number::F64(value)), Err(_) => Err(format!( - "invalid floating point argument: '{}'\nTry '{} --help' for more information.", - s, + "invalid floating point argument: {}\nTry '{} --help' for more information.", + s.quote(), uucore::execution_phrase(), )), }, diff --git a/src/uu/shred/src/shred.rs b/src/uu/shred/src/shred.rs index 0464c5d50..fa455f027 100644 --- a/src/uu/shred/src/shred.rs +++ b/src/uu/shred/src/shred.rs @@ -18,12 +18,12 @@ use std::io; use std::io::prelude::*; use std::io::SeekFrom; use std::path::{Path, PathBuf}; -use uucore::InvalidEncodingHandling; +use uucore::display::Quotable; +use uucore::{util_name, InvalidEncodingHandling}; #[macro_use] extern crate uucore; -static NAME: &str = "shred"; const BLOCK_SIZE: usize = 512; const NAME_CHARSET: &[u8] = b"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_."; @@ -281,7 +281,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { if !matches.is_present(options::FILE) { show_error!("Missing an argument"); - show_error!("For help, try '{} --help'", NAME); + show_error!("For help, try '{} --help'", uucore::execution_phrase()); return 0; } @@ -289,7 +289,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { Some(s) => match s.parse::() { Ok(u) => u, Err(_) => { - errs.push(format!("invalid number of passes: '{}'", s)); + errs.push(format!("invalid number of passes: {}", s.quote())); 0 } }, @@ -414,7 +414,11 @@ fn get_size(size_str_opt: Option) -> Option { let coefficient = match size_str.parse::() { Ok(u) => u, Err(_) => { - println!("{}: {}: Invalid file size", NAME, size_str_opt.unwrap()); + println!( + "{}: {}: Invalid file size", + util_name(), + size_str_opt.unwrap().maybe_quote() + ); exit!(1); } }; @@ -452,11 +456,11 @@ fn wipe_file( // Get these potential errors out of the way first let path: &Path = Path::new(path_str); if !path.exists() { - show_error!("{}: No such file or directory", path.display()); + show_error!("{}: No such file or directory", path.maybe_quote()); return; } if !path.is_file() { - show_error!("{}: Not a file", path.display()); + show_error!("{}: Not a file", path.maybe_quote()); return; } @@ -520,7 +524,7 @@ fn wipe_file( let mut file: File = match OpenOptions::new().write(true).truncate(false).open(path) { Ok(f) => f, Err(e) => { - show_error!("{}: failed to open for writing: {}", path.display(), e); + show_error!("{}: failed to open for writing: {}", path.maybe_quote(), e); return; } }; @@ -535,8 +539,8 @@ fn wipe_file( if total_passes.to_string().len() == 1 { println!( "{}: {}: pass {}/{} ({})... ", - NAME, - path.display(), + util_name(), + path.maybe_quote(), i + 1, total_passes, pass_name @@ -544,8 +548,8 @@ fn wipe_file( } else { println!( "{}: {}: pass {:2.0}/{:2.0} ({})... ", - NAME, - path.display(), + util_name(), + path.maybe_quote(), i + 1, total_passes, pass_name @@ -556,7 +560,7 @@ fn wipe_file( match do_pass(&mut file, path, &mut generator, *pass_type, size) { Ok(_) => {} Err(e) => { - show_error!("{}: File write pass failed: {}", path.display(), e); + show_error!("{}: File write pass failed: {}", path.maybe_quote(), e); } } // Ignore failed writes; just keep trying @@ -567,7 +571,7 @@ fn wipe_file( match do_remove(path, path_str, verbose) { Ok(_) => {} Err(e) => { - show_error!("{}: failed to remove file: {}", path.display(), e); + show_error!("{}: failed to remove file: {}", path.maybe_quote(), e); } } } @@ -622,9 +626,9 @@ fn wipe_name(orig_path: &Path, verbose: bool) -> Option { if verbose { println!( "{}: {}: renamed to {}", - NAME, - last_path.display(), - new_path.display() + util_name(), + last_path.maybe_quote(), + new_path.quote() ); } @@ -641,9 +645,9 @@ fn wipe_name(orig_path: &Path, verbose: bool) -> Option { Err(e) => { println!( "{}: {}: Couldn't rename to {}: {}", - NAME, - last_path.display(), - new_path.display(), + util_name(), + last_path.maybe_quote(), + new_path.quote(), e ); return None; @@ -657,7 +661,7 @@ fn wipe_name(orig_path: &Path, verbose: bool) -> Option { fn do_remove(path: &Path, orig_filename: &str, verbose: bool) -> Result<(), io::Error> { if verbose { - println!("{}: {}: removing", NAME, orig_filename); + println!("{}: {}: removing", util_name(), orig_filename.maybe_quote()); } let renamed_path: Option = wipe_name(path, verbose); @@ -666,7 +670,7 @@ fn do_remove(path: &Path, orig_filename: &str, verbose: bool) -> Result<(), io:: } if verbose { - println!("{}: {}: removed", NAME, orig_filename); + println!("{}: {}: removed", util_name(), orig_filename.maybe_quote()); } Ok(()) diff --git a/src/uu/shuf/src/shuf.rs b/src/uu/shuf/src/shuf.rs index 7125014b1..9a899d746 100644 --- a/src/uu/shuf/src/shuf.rs +++ b/src/uu/shuf/src/shuf.rs @@ -14,6 +14,7 @@ use clap::{crate_version, App, Arg}; use rand::Rng; use std::fs::File; use std::io::{stdin, stdout, BufReader, BufWriter, Read, Write}; +use uucore::display::Quotable; use uucore::InvalidEncodingHandling; enum Mode { @@ -76,7 +77,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { Some(count) => match count.parse::() { Ok(val) => val, Err(_) => { - show_error!("invalid line count: '{}'", count); + show_error!("invalid line count: {}", count.quote()); return 1; } }, @@ -185,13 +186,13 @@ fn read_input_file(filename: &str) -> Vec { } else { match File::open(filename) { Ok(f) => Box::new(f) as Box, - Err(e) => crash!(1, "failed to open '{}': {}", filename, e), + Err(e) => crash!(1, "failed to open {}: {}", filename.quote(), e), } }); let mut data = Vec::new(); if let Err(e) = file.read_to_end(&mut data) { - crash!(1, "failed reading '{}': {}", filename, e) + crash!(1, "failed reading {}: {}", filename.quote(), e) }; data @@ -235,7 +236,7 @@ fn shuf_bytes(input: &mut Vec<&[u8]>, opts: Options) { None => Box::new(stdout()) as Box, Some(s) => match File::create(&s[..]) { Ok(f) => Box::new(f) as Box, - Err(e) => crash!(1, "failed to open '{}' for writing: {}", &s[..], e), + Err(e) => crash!(1, "failed to open {} for writing: {}", s.quote(), e), }, }); @@ -243,7 +244,7 @@ fn shuf_bytes(input: &mut Vec<&[u8]>, opts: Options) { Some(r) => WrappedRng::RngFile(rand::rngs::adapter::ReadRng::new( match File::open(&r[..]) { Ok(f) => f, - Err(e) => crash!(1, "failed to open random source '{}': {}", &r[..], e), + Err(e) => crash!(1, "failed to open random source {}: {}", r.quote(), e), }, )), None => WrappedRng::RngDefault(rand::thread_rng()), @@ -288,14 +289,14 @@ fn shuf_bytes(input: &mut Vec<&[u8]>, opts: Options) { fn parse_range(input_range: &str) -> Result<(usize, usize), String> { let split: Vec<&str> = input_range.split('-').collect(); if split.len() != 2 { - Err(format!("invalid input range: '{}'", input_range)) + Err(format!("invalid input range: {}", input_range.quote())) } else { let begin = split[0] .parse::() - .map_err(|_| format!("invalid input range: '{}'", split[0]))?; + .map_err(|_| format!("invalid input range: {}", split[0].quote()))?; let end = split[1] .parse::() - .map_err(|_| format!("invalid input range: '{}'", split[1]))?; + .map_err(|_| format!("invalid input range: {}", split[1].quote()))?; Ok((begin, end + 1)) } } diff --git a/src/uu/sort/src/sort.rs b/src/uu/sort/src/sort.rs index abba1e63b..bab6dd770 100644 --- a/src/uu/sort/src/sort.rs +++ b/src/uu/sort/src/sort.rs @@ -45,6 +45,7 @@ use std::path::Path; use std::path::PathBuf; use std::str::Utf8Error; use unicode_width::UnicodeWidthStr; +use uucore::display::Quotable; use uucore::error::{set_exit_code, strip_errno, UError, UResult, USimpleError, UUsageError}; use uucore::parse_size::{parse_size, ParseSizeError}; use uucore::version_cmp::version_cmp; @@ -139,7 +140,7 @@ enum SortError { error: std::io::Error, }, ReadFailed { - path: String, + path: PathBuf, error: std::io::Error, }, ParseKeyError { @@ -189,7 +190,7 @@ impl Display for SortError { write!( f, "{}:{}: disorder: {}", - file.to_string_lossy(), + file.maybe_quote(), line_number, line ) @@ -198,13 +199,23 @@ impl Display for SortError { } } SortError::OpenFailed { path, error } => { - write!(f, "open failed: {}: {}", path, strip_errno(error)) + write!( + f, + "open failed: {}: {}", + path.maybe_quote(), + strip_errno(error) + ) } SortError::ParseKeyError { key, msg } => { - write!(f, "failed to parse key `{}`: {}", key, msg) + write!(f, "failed to parse key {}: {}", key.quote(), msg) } SortError::ReadFailed { path, error } => { - write!(f, "cannot read: {}: {}", path, strip_errno(error)) + write!( + f, + "cannot read: {}: {}", + path.maybe_quote(), + strip_errno(error) + ) } SortError::OpenTmpFileFailed { error } => { write!(f, "failed to open temporary file: {}", strip_errno(error)) @@ -213,7 +224,7 @@ impl Display for SortError { write!(f, "couldn't execute compress program: errno {}", code) } SortError::CompressProgTerminatedAbnormally { prog } => { - write!(f, "'{}' terminated abnormally", prog) + write!(f, "{} terminated abnormally", prog.quote()) } SortError::TmpDirCreationFailed => write!(f, "could not create temporary directory"), SortError::Uft8Error { error } => write!(f, "{}", error), @@ -1179,7 +1190,10 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { if let Some(n_merge) = matches.value_of(options::BATCH_SIZE) { settings.merge_batch_size = n_merge.parse().map_err(|_| { - UUsageError::new(2, format!("invalid --batch-size argument '{}'", n_merge)) + UUsageError::new( + 2, + format!("invalid --batch-size argument {}", n_merge.quote()), + ) })?; } @@ -1211,23 +1225,30 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { } else if settings.check && files.len() != 1 { return Err(UUsageError::new( 2, - format!( - "extra operand `{}' not allowed with -c", - files[1].to_string_lossy() - ), + format!("extra operand {} not allowed with -c", files[1].quote()), )); } if let Some(arg) = matches.args.get(options::SEPARATOR) { - let separator = arg.vals[0].to_string_lossy(); - let mut separator = separator.as_ref(); + let mut separator = arg.vals[0].to_str().ok_or_else(|| { + UUsageError::new( + 2, + format!("separator is not valid unicode: {}", arg.vals[0].quote()), + ) + })?; if separator == "\\0" { separator = "\0"; } + // This rejects non-ASCII codepoints, but perhaps we don't have to. + // On the other hand GNU accepts any single byte, valid unicode or not. + // (Supporting multi-byte chars would require changes in tokenize_with_separator().) if separator.len() != 1 { return Err(UUsageError::new( 2, - "separator must be exactly one character long", + format!( + "separator must be exactly one character long: {}", + separator.quote() + ), )); } settings.separator = Some(separator.chars().next().unwrap()) @@ -1816,7 +1837,7 @@ fn open(path: impl AsRef) -> UResult> { match File::open(path) { Ok(f) => Ok(Box::new(f) as Box), Err(error) => Err(SortError::ReadFailed { - path: path.to_string_lossy().to_string(), + path: path.to_owned(), error, } .into()), @@ -1828,8 +1849,8 @@ fn format_error_message(error: ParseSizeError, s: &str, option: &str) -> String // GNU's sort echos affected flag, -S or --buffer-size, depending user's selection // GNU's sort does distinguish between "invalid (suffix in) argument" match error { - ParseSizeError::ParseFailure(_) => format!("invalid --{} argument '{}'", option, s), - ParseSizeError::SizeTooBig(_) => format!("--{} argument '{}' too large", option, s), + ParseSizeError::ParseFailure(_) => format!("invalid --{} argument {}", option, s.quote()), + ParseSizeError::SizeTooBig(_) => format!("--{} argument {} too large", option, s.quote()), } } diff --git a/src/uu/split/src/split.rs b/src/uu/split/src/split.rs index e405c757a..581b632d2 100644 --- a/src/uu/split/src/split.rs +++ b/src/uu/split/src/split.rs @@ -19,6 +19,7 @@ use std::fs::File; use std::io::{stdin, BufRead, BufReader, BufWriter, Read, Write}; use std::path::Path; use std::{char, fs::remove_file}; +use uucore::display::Quotable; use uucore::parse_size::parse_size; static OPT_BYTES: &str = "bytes"; @@ -238,7 +239,11 @@ impl LineSplitter { fn new(settings: &Settings) -> LineSplitter { LineSplitter { lines_per_split: settings.strategy_param.parse().unwrap_or_else(|_| { - crash!(1, "invalid number of lines: '{}'", settings.strategy_param) + crash!( + 1, + "invalid number of lines: {}", + settings.strategy_param.quote() + ) }), } } @@ -373,8 +378,8 @@ fn split(settings: &Settings) -> i32 { let r = File::open(Path::new(&settings.input)).unwrap_or_else(|_| { crash!( 1, - "cannot open '{}' for reading: No such file or directory", - settings.input + "cannot open {} for reading: No such file or directory", + settings.input.quote() ) }); Box::new(r) as Box @@ -383,7 +388,7 @@ fn split(settings: &Settings) -> i32 { let mut splitter: Box = match settings.strategy.as_str() { s if s == OPT_LINES => Box::new(LineSplitter::new(settings)), s if (s == OPT_BYTES || s == OPT_LINE_BYTES) => Box::new(ByteSplitter::new(settings)), - a => crash!(1, "strategy {} not supported", a), + a => crash!(1, "strategy {} not supported", a.quote()), }; let mut fileno = 0; diff --git a/src/uu/stat/src/stat.rs b/src/uu/stat/src/stat.rs index d017999b4..fd4a6443d 100644 --- a/src/uu/stat/src/stat.rs +++ b/src/uu/stat/src/stat.rs @@ -7,6 +7,7 @@ #[macro_use] extern crate uucore; +use uucore::display::Quotable; use uucore::entries; use uucore::fs::display_permissions; use uucore::fsext::{ @@ -24,7 +25,7 @@ use std::{cmp, fs, iter}; macro_rules! check_bound { ($str: ident, $bound:expr, $beg: expr, $end: expr) => { if $end >= $bound { - return Err(format!("'{}': invalid directive", &$str[$beg..$end])); + return Err(format!("{}: invalid directive", $str[$beg..$end].quote())); } }; } @@ -652,11 +653,7 @@ impl Stater { return 1; } }; - arg = format!( - "`{}' -> `{}'", - file, - dst.to_string_lossy() - ); + arg = format!("{} -> {}", file.quote(), dst.quote()); } else { arg = file.to_string(); } @@ -750,7 +747,7 @@ impl Stater { } } Err(e) => { - show_error!("cannot stat '{}': {}", file, e); + show_error!("cannot stat {}: {}", file.quote(), e); return 1; } } @@ -843,7 +840,11 @@ impl Stater { } } Err(e) => { - show_error!("cannot read file system information for '{}': {}", file, e); + show_error!( + "cannot read file system information for {}: {}", + file.quote(), + e + ); return 1; } } diff --git a/src/uu/sum/src/sum.rs b/src/uu/sum/src/sum.rs index f1147ca2e..15c20f09d 100644 --- a/src/uu/sum/src/sum.rs +++ b/src/uu/sum/src/sum.rs @@ -14,6 +14,7 @@ use clap::{crate_version, App, Arg}; use std::fs::File; use std::io::{stdin, Read, Result}; use std::path::Path; +use uucore::display::Quotable; use uucore::InvalidEncodingHandling; static NAME: &str = "sum"; @@ -118,7 +119,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { let reader = match open(file) { Ok(f) => f, Err(error) => { - show_error!("'{}' {}", file, error); + show_error!("{}: {}", file.maybe_quote(), error); exit_code = 2; continue; } diff --git a/src/uu/sync/src/sync.rs b/src/uu/sync/src/sync.rs index f49f51728..d6a21f280 100644 --- a/src/uu/sync/src/sync.rs +++ b/src/uu/sync/src/sync.rs @@ -14,6 +14,7 @@ extern crate uucore; use clap::{crate_version, App, Arg}; use std::path::Path; +use uucore::display::Quotable; static EXIT_ERR: i32 = 1; @@ -175,7 +176,11 @@ pub fn uumain(args: impl uucore::Args) -> i32 { for f in &files { if !Path::new(&f).exists() { - crash!(EXIT_ERR, "cannot stat '{}': No such file or directory", f); + crash!( + EXIT_ERR, + "cannot stat {}: No such file or directory", + f.quote() + ); } } diff --git a/src/uu/tac/src/tac.rs b/src/uu/tac/src/tac.rs index 0568f1601..e54697f2b 100644 --- a/src/uu/tac/src/tac.rs +++ b/src/uu/tac/src/tac.rs @@ -14,6 +14,7 @@ use clap::{crate_version, App, Arg}; use memchr::memmem; use std::io::{stdin, stdout, BufReader, Read, Write}; use std::{fs::File, path::Path}; +use uucore::display::Quotable; use uucore::InvalidEncodingHandling; static NAME: &str = "tac"; @@ -141,11 +142,11 @@ fn tac(filenames: Vec, before: bool, _: bool, separator: &str) -> i32 { let path = Path::new(filename); if path.is_dir() || path.metadata().is_err() { if path.is_dir() { - show_error!("{}: read error: Invalid argument", filename); + show_error!("{}: read error: Invalid argument", filename.maybe_quote()); } else { show_error!( - "failed to open '{}' for reading: No such file or directory", - filename + "failed to open {} for reading: No such file or directory", + filename.quote() ); } exit_code = 1; @@ -154,7 +155,7 @@ fn tac(filenames: Vec, before: bool, _: bool, separator: &str) -> i32 { match File::open(path) { Ok(f) => Box::new(f) as Box, Err(e) => { - show_error!("failed to open '{}' for reading: {}", filename, e); + show_error!("failed to open {} for reading: {}", filename.quote(), e); exit_code = 1; continue; } @@ -163,7 +164,7 @@ fn tac(filenames: Vec, before: bool, _: bool, separator: &str) -> i32 { let mut data = Vec::new(); if let Err(e) = file.read_to_end(&mut data) { - show_error!("failed to read '{}': {}", filename, e); + show_error!("failed to read {}: {}", filename.quote(), e); exit_code = 1; continue; }; diff --git a/src/uu/tee/src/tee.rs b/src/uu/tee/src/tee.rs index d98835265..d2fb015bf 100644 --- a/src/uu/tee/src/tee.rs +++ b/src/uu/tee/src/tee.rs @@ -12,7 +12,8 @@ use clap::{crate_version, App, Arg}; use retain_mut::RetainMut; use std::fs::OpenOptions; use std::io::{copy, sink, stdin, stdout, Error, ErrorKind, Read, Result, Write}; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; +use uucore::display::Quotable; #[cfg(unix)] use uucore::libc; @@ -167,7 +168,7 @@ impl Write for MultiWriter { let result = writer.write_all(buf); match result { Err(f) => { - show_error!("{}: {}", writer.name, f.to_string()); + show_error!("{}: {}", writer.name.maybe_quote(), f); false } _ => true, @@ -181,7 +182,7 @@ impl Write for MultiWriter { let result = writer.flush(); match result { Err(f) => { - show_error!("{}: {}", writer.name, f.to_string()); + show_error!("{}: {}", writer.name.maybe_quote(), f); false } _ => true, @@ -214,7 +215,7 @@ impl Read for NamedReader { fn read(&mut self, buf: &mut [u8]) -> Result { match self.inner.read(buf) { Err(f) => { - show_error!("{}: {}", Path::new("stdin").display(), f.to_string()); + show_error!("stdin: {}", f); Err(f) } okay => okay, diff --git a/src/uu/test/src/parser.rs b/src/uu/test/src/parser.rs index 7b7d77469..0d68cea39 100644 --- a/src/uu/test/src/parser.rs +++ b/src/uu/test/src/parser.rs @@ -10,6 +10,8 @@ use std::ffi::OsString; use std::iter::Peekable; +use uucore::display::Quotable; + /// Represents one of the binary comparison operators for strings, integers, or files #[derive(Debug, PartialEq)] pub enum Operator { @@ -43,19 +45,22 @@ impl Symbol { /// Returns Symbol::None in place of None fn new(token: Option) -> Symbol { match token { - Some(s) => match s.to_string_lossy().as_ref() { - "(" => Symbol::LParen, - "!" => Symbol::Bang, - "-a" | "-o" => Symbol::BoolOp(s), - "=" | "==" | "!=" => Symbol::Op(Operator::String(s)), - "-eq" | "-ge" | "-gt" | "-le" | "-lt" | "-ne" => Symbol::Op(Operator::Int(s)), - "-ef" | "-nt" | "-ot" => Symbol::Op(Operator::File(s)), - "-n" | "-z" => Symbol::UnaryOp(UnaryOperator::StrlenOp(s)), - "-b" | "-c" | "-d" | "-e" | "-f" | "-g" | "-G" | "-h" | "-k" | "-L" | "-O" - | "-p" | "-r" | "-s" | "-S" | "-t" | "-u" | "-w" | "-x" => { - Symbol::UnaryOp(UnaryOperator::FiletestOp(s)) - } - _ => Symbol::Literal(s), + Some(s) => match s.to_str() { + Some(t) => match t { + "(" => Symbol::LParen, + "!" => Symbol::Bang, + "-a" | "-o" => Symbol::BoolOp(s), + "=" | "==" | "!=" => Symbol::Op(Operator::String(s)), + "-eq" | "-ge" | "-gt" | "-le" | "-lt" | "-ne" => Symbol::Op(Operator::Int(s)), + "-ef" | "-nt" | "-ot" => Symbol::Op(Operator::File(s)), + "-n" | "-z" => Symbol::UnaryOp(UnaryOperator::StrlenOp(s)), + "-b" | "-c" | "-d" | "-e" | "-f" | "-g" | "-G" | "-h" | "-k" | "-L" | "-O" + | "-p" | "-r" | "-s" | "-S" | "-t" | "-u" | "-w" | "-x" => { + Symbol::UnaryOp(UnaryOperator::FiletestOp(s)) + } + _ => Symbol::Literal(s), + }, + None => Symbol::Literal(s), }, None => Symbol::None, } @@ -391,7 +396,7 @@ impl Parser { self.expr(); match self.tokens.next() { - Some(token) => Err(format!("extra argument '{}'", token.to_string_lossy())), + Some(token) => Err(format!("extra argument {}", token.quote())), None => Ok(()), } } diff --git a/src/uu/test/src/test.rs b/src/uu/test/src/test.rs index 50563ba49..5ce798bfa 100644 --- a/src/uu/test/src/test.rs +++ b/src/uu/test/src/test.rs @@ -13,7 +13,7 @@ mod parser; use clap::{crate_version, App, AppSettings}; use parser::{parse, Operator, Symbol, UnaryOperator}; use std::ffi::{OsStr, OsString}; -use std::path::Path; +use uucore::{display::Quotable, show_error}; const USAGE: &str = "test EXPRESSION or: test @@ -93,10 +93,7 @@ pub fn uu_app() -> App<'static, 'static> { pub fn uumain(mut args: impl uucore::Args) -> i32 { let program = args.next().unwrap_or_else(|| OsString::from("test")); - let binary_name = Path::new(&program) - .file_name() - .unwrap_or_else(|| OsStr::new("test")) - .to_string_lossy(); + let binary_name = uucore::util_name(); let mut args: Vec<_> = args.collect(); if binary_name.ends_with('[') { @@ -116,8 +113,8 @@ pub fn uumain(mut args: impl uucore::Args) -> i32 { } // If invoked via name '[', matching ']' must be in the last arg let last = args.pop(); - if last != Some(OsString::from("]")) { - eprintln!("[: missing ']'"); + if last.as_deref() != Some(OsStr::new("]")) { + show_error!("missing ']'"); return 2; } } @@ -133,7 +130,7 @@ pub fn uumain(mut args: impl uucore::Args) -> i32 { } } Err(e) => { - eprintln!("test: {}", e); + show_error!("{}", e); 2 } } @@ -190,11 +187,11 @@ fn eval(stack: &mut Vec) -> Result { }) } Some(Symbol::UnaryOp(UnaryOperator::FiletestOp(op))) => { - let op = op.to_string_lossy(); + let op = op.to_str().unwrap(); let f = pop_literal!(); - Ok(match op.as_ref() { + Ok(match op { "-b" => path(&f, PathCondition::BlockSpecial), "-c" => path(&f, PathCondition::CharacterSpecial), "-d" => path(&f, PathCondition::Directory), @@ -231,31 +228,33 @@ fn eval(stack: &mut Vec) -> Result { } fn integers(a: &OsStr, b: &OsStr, op: &OsStr) -> Result { - let format_err = |value| format!("invalid integer '{}'", value); + let format_err = |value: &OsStr| format!("invalid integer {}", value.quote()); - let a = a.to_string_lossy(); - let a: i64 = a.parse().map_err(|_| format_err(a))?; + let a: i64 = a + .to_str() + .and_then(|s| s.parse().ok()) + .ok_or_else(|| format_err(a))?; - let b = b.to_string_lossy(); - let b: i64 = b.parse().map_err(|_| format_err(b))?; + let b: i64 = b + .to_str() + .and_then(|s| s.parse().ok()) + .ok_or_else(|| format_err(b))?; - let operator = op.to_string_lossy(); - Ok(match operator.as_ref() { - "-eq" => a == b, - "-ne" => a != b, - "-gt" => a > b, - "-ge" => a >= b, - "-lt" => a < b, - "-le" => a <= b, - _ => return Err(format!("unknown operator '{}'", operator)), + Ok(match op.to_str() { + Some("-eq") => a == b, + Some("-ne") => a != b, + Some("-gt") => a > b, + Some("-ge") => a >= b, + Some("-lt") => a < b, + Some("-le") => a <= b, + _ => return Err(format!("unknown operator {}", op.quote())), }) } fn isatty(fd: &OsStr) -> Result { - let fd = fd.to_string_lossy(); - - fd.parse() - .map_err(|_| format!("invalid integer '{}'", fd)) + fd.to_str() + .and_then(|s| s.parse().ok()) + .ok_or_else(|| format!("invalid integer {}", fd.quote())) .map(|i| { #[cfg(not(target_os = "redox"))] unsafe { diff --git a/src/uu/timeout/src/timeout.rs b/src/uu/timeout/src/timeout.rs index 21b0a0c37..f686dde3b 100644 --- a/src/uu/timeout/src/timeout.rs +++ b/src/uu/timeout/src/timeout.rs @@ -16,6 +16,7 @@ use clap::{crate_version, App, AppSettings, Arg}; use std::io::ErrorKind; use std::process::{Command, Stdio}; use std::time::Duration; +use uucore::display::Quotable; use uucore::process::ChildExt; use uucore::signals::{signal_by_name_or_value, signal_name_by_value}; use uucore::InvalidEncodingHandling; @@ -61,7 +62,7 @@ impl Config { let signal_result = signal_by_name_or_value(signal_); match signal_result { None => { - unreachable!("invalid signal '{}'", signal_); + unreachable!("invalid signal {}", signal_.quote()); } Some(signal_value) => signal_value, } @@ -216,9 +217,9 @@ fn timeout( Ok(None) => { if verbose { show_error!( - "sending signal {} to command '{}'", + "sending signal {} to command {}", signal_name_by_value(signal).unwrap(), - cmd[0] + cmd[0].quote() ); } crash_if_err!(ERR_EXIT_STATUS, process.send_signal(signal)); @@ -233,7 +234,7 @@ fn timeout( } Ok(None) => { if verbose { - show_error!("sending signal KILL to command '{}'", cmd[0]); + show_error!("sending signal KILL to command {}", cmd[0].quote()); } crash_if_err!( ERR_EXIT_STATUS, diff --git a/src/uu/touch/src/touch.rs b/src/uu/touch/src/touch.rs index 7c0903665..8dd7bf7d2 100644 --- a/src/uu/touch/src/touch.rs +++ b/src/uu/touch/src/touch.rs @@ -17,6 +17,7 @@ use clap::{crate_version, App, Arg, ArgGroup}; use filetime::*; use std::fs::{self, File}; use std::path::Path; +use uucore::display::Quotable; use uucore::error::{FromIo, UError, UResult, USimpleError}; static ABOUT: &str = "Update the access and modification times of each FILE to the current time."; @@ -82,7 +83,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { } if let Err(e) = File::create(path) { - show!(e.map_err_context(|| format!("cannot touch '{}'", path.display()))); + show!(e.map_err_context(|| format!("cannot touch {}", path.quote()))); continue; }; @@ -122,7 +123,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { } else { filetime::set_file_times(path, atime, mtime) } - .map_err_context(|| format!("setting times of '{}'", path.display()))?; + .map_err_context(|| format!("setting times of {}", path.quote()))?; } Ok(()) @@ -209,7 +210,7 @@ fn stat(path: &Path, follow: bool) -> UResult<(FileTime, FileTime)> { } else { fs::metadata(path) } - .map_err_context(|| format!("failed to get attributes of '{}'", path.display()))?; + .map_err_context(|| format!("failed to get attributes of {}", path.quote()))?; Ok(( FileTime::from_last_access_time(&metadata), @@ -249,11 +250,16 @@ fn parse_timestamp(s: &str) -> UResult { 10 => ("%y%m%d%H%M", s.to_owned()), 11 => ("%Y%m%d%H%M.%S", format!("{}{}", now.tm_year + 1900, s)), 8 => ("%Y%m%d%H%M", format!("{}{}", now.tm_year + 1900, s)), - _ => return Err(USimpleError::new(1, format!("invalid date format '{}'", s))), + _ => { + return Err(USimpleError::new( + 1, + format!("invalid date format {}", s.quote()), + )) + } }; let tm = time::strptime(&ts, format) - .map_err(|_| USimpleError::new(1, format!("invalid date format '{}'", s)))?; + .map_err(|_| USimpleError::new(1, format!("invalid date format {}", s.quote())))?; let mut local = to_local(tm); local.tm_isdst = -1; @@ -269,7 +275,10 @@ fn parse_timestamp(s: &str) -> UResult { }; let tm2 = time::at(ts); if tm.tm_hour != tm2.tm_hour { - return Err(USimpleError::new(1, format!("invalid date format '{}'", s))); + return Err(USimpleError::new( + 1, + format!("invalid date format {}", s.quote()), + )); } Ok(ft) diff --git a/src/uu/tr/src/tr.rs b/src/uu/tr/src/tr.rs index d46318e38..fbc4bab9b 100644 --- a/src/uu/tr/src/tr.rs +++ b/src/uu/tr/src/tr.rs @@ -21,7 +21,7 @@ use fnv::FnvHashMap; use std::io::{stdin, stdout, BufRead, BufWriter, Write}; use crate::expand::ExpandSet; -use uucore::InvalidEncodingHandling; +use uucore::{display::Quotable, InvalidEncodingHandling}; static ABOUT: &str = "translate or delete characters"; @@ -271,8 +271,8 @@ pub fn uumain(args: impl uucore::Args) -> i32 { if !(delete_flag || squeeze_flag) && sets.len() < 2 { show_error!( - "missing operand after '{}'\nTry '{} --help' for more information.", - sets[0], + "missing operand after {}\nTry '{} --help' for more information.", + sets[0].quote(), uucore::execution_phrase() ); return 1; diff --git a/src/uu/truncate/src/truncate.rs b/src/uu/truncate/src/truncate.rs index 062ef3811..6fb1f06f6 100644 --- a/src/uu/truncate/src/truncate.rs +++ b/src/uu/truncate/src/truncate.rs @@ -15,6 +15,7 @@ use std::convert::TryFrom; use std::fs::{metadata, OpenOptions}; use std::io::ErrorKind; use std::path::Path; +use uucore::display::Quotable; use uucore::parse_size::{parse_size, ParseSizeError}; #[derive(Debug, Eq, PartialEq)] @@ -120,8 +121,8 @@ pub fn uumain(args: impl uucore::Args) -> i32 { let reference = matches.value_of(options::REFERENCE).map(String::from); crash!( 1, - "cannot stat '{}': No such file or directory", - reference.unwrap_or_else(|| "".to_string()) + "cannot stat {}: No such file or directory", + reference.as_deref().unwrap_or("").quote() ); // TODO: fix '--no-create' see test_reference and test_truncate_bytes_size } _ => crash!(1, "{}", e.to_string()), diff --git a/src/uu/tsort/src/tsort.rs b/src/uu/tsort/src/tsort.rs index c0ef66598..11798db13 100644 --- a/src/uu/tsort/src/tsort.rs +++ b/src/uu/tsort/src/tsort.rs @@ -14,6 +14,7 @@ use std::collections::{HashMap, HashSet}; use std::fs::File; use std::io::{stdin, BufRead, BufReader, Read}; use std::path::Path; +use uucore::display::Quotable; use uucore::InvalidEncodingHandling; static SUMMARY: &str = "Topological sort the strings in FILE. @@ -45,7 +46,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { file_buf = match File::open(Path::new(&input)) { Ok(a) => a, _ => { - show_error!("{}: No such file or directory", input); + show_error!("{}: No such file or directory", input.maybe_quote()); return 1; } }; @@ -68,7 +69,11 @@ pub fn uumain(args: impl uucore::Args) -> i32 { for ab in tokens.chunks(2) { match ab.len() { 2 => g.add_edge(&ab[0], &ab[1]), - _ => crash!(1, "{}: input contains an odd number of tokens", input), + _ => crash!( + 1, + "{}: input contains an odd number of tokens", + input.maybe_quote() + ), } } } diff --git a/src/uu/unexpand/src/unexpand.rs b/src/uu/unexpand/src/unexpand.rs index 7fb9b2590..95383b89d 100644 --- a/src/uu/unexpand/src/unexpand.rs +++ b/src/uu/unexpand/src/unexpand.rs @@ -16,6 +16,7 @@ use std::fs::File; use std::io::{stdin, stdout, BufRead, BufReader, BufWriter, Read, Stdout, Write}; use std::str::from_utf8; use unicode_width::UnicodeWidthChar; +use uucore::display::Quotable; use uucore::InvalidEncodingHandling; static NAME: &str = "unexpand"; @@ -141,9 +142,9 @@ fn open(path: String) -> BufReader> { if path == "-" { BufReader::new(Box::new(stdin()) as Box) } else { - file_buf = match File::open(&path[..]) { + file_buf = match File::open(&path) { Ok(a) => a, - Err(e) => crash!(1, "{}: {}", &path[..], e), + Err(e) => crash!(1, "{}: {}", path.maybe_quote(), e), }; BufReader::new(Box::new(file_buf) as Box) } diff --git a/src/uu/uniq/src/uniq.rs b/src/uu/uniq/src/uniq.rs index 5c3fa3b1e..f84bfc26d 100644 --- a/src/uu/uniq/src/uniq.rs +++ b/src/uu/uniq/src/uniq.rs @@ -14,6 +14,7 @@ use std::io::{stdin, stdout, BufRead, BufReader, BufWriter, Read, Result, Write} use std::path::Path; use std::str::FromStr; use strum_macros::{AsRefStr, EnumString}; +use uucore::display::Quotable; static ABOUT: &str = "Report or omit repeated lines."; pub mod options { @@ -217,7 +218,14 @@ fn get_line_string(io_line: Result>) -> String { fn opt_parsed(opt_name: &str, matches: &ArgMatches) -> Option { matches.value_of(opt_name).map(|arg_str| { let opt_val: Option = arg_str.parse().ok(); - opt_val.unwrap_or_else(|| crash!(1, "Invalid argument for {}: {}", opt_name, arg_str)) + opt_val.unwrap_or_else(|| { + crash!( + 1, + "Invalid argument for {}: {}", + opt_name, + arg_str.maybe_quote() + ) + }) }) } diff --git a/src/uu/unlink/src/unlink.rs b/src/uu/unlink/src/unlink.rs index c7fc00639..7c66708e0 100644 --- a/src/uu/unlink/src/unlink.rs +++ b/src/uu/unlink/src/unlink.rs @@ -17,6 +17,7 @@ use libc::{lstat, stat, unlink}; use libc::{S_IFLNK, S_IFMT, S_IFREG}; use std::ffi::CString; use std::io::{Error, ErrorKind}; +use uucore::display::Quotable; use uucore::InvalidEncodingHandling; static ABOUT: &str = "Unlink the file at [FILE]."; @@ -63,7 +64,12 @@ pub fn uumain(args: impl uucore::Args) -> i32 { let result = unsafe { lstat(c_string.as_ptr(), &mut buf as *mut stat) }; if result < 0 { - crash!(1, "Cannot stat '{}': {}", paths[0], Error::last_os_error()); + crash!( + 1, + "Cannot stat {}: {}", + paths[0].quote(), + Error::last_os_error() + ); } buf.st_mode & S_IFMT diff --git a/src/uucore/src/lib/features/perms.rs b/src/uucore/src/lib/features/perms.rs index 8a2f20417..16ee01b88 100644 --- a/src/uucore/src/lib/features/perms.rs +++ b/src/uucore/src/lib/features/perms.rs @@ -5,6 +5,7 @@ //! Common functions to manage permissions +use crate::display::Quotable; use crate::error::strip_errno; use crate::error::UResult; use crate::error::USimpleError; @@ -80,29 +81,29 @@ pub fn wrap_chown>( VerbosityLevel::Silent => (), level => { out = format!( - "changing {} of '{}': {}", + "changing {} of {}: {}", if verbosity.groups_only { "group" } else { "ownership" }, - path.display(), + path.quote(), e ); if level == VerbosityLevel::Verbose { out = if verbosity.groups_only { format!( - "{}\nfailed to change group of '{}' from {} to {}", + "{}\nfailed to change group of {} from {} to {}", out, - path.display(), + path.quote(), entries::gid2grp(meta.gid()).unwrap(), entries::gid2grp(dest_gid).unwrap() ) } else { format!( - "{}\nfailed to change ownership of '{}' from {}:{} to {}:{}", + "{}\nfailed to change ownership of {} from {}:{} to {}:{}", out, - path.display(), + path.quote(), entries::uid2usr(meta.uid()).unwrap(), entries::gid2grp(meta.gid()).unwrap(), entries::uid2usr(dest_uid).unwrap(), @@ -120,15 +121,15 @@ pub fn wrap_chown>( VerbosityLevel::Changes | VerbosityLevel::Verbose => { out = if verbosity.groups_only { format!( - "changed group of '{}' from {} to {}", - path.display(), + "changed group of {} from {} to {}", + path.quote(), entries::gid2grp(meta.gid()).unwrap(), entries::gid2grp(dest_gid).unwrap() ) } else { format!( - "changed ownership of '{}' from {}:{} to {}:{}", - path.display(), + "changed ownership of {} from {}:{} to {}:{}", + path.quote(), entries::uid2usr(meta.uid()).unwrap(), entries::gid2grp(meta.gid()).unwrap(), entries::uid2usr(dest_uid).unwrap(), @@ -141,14 +142,14 @@ pub fn wrap_chown>( } else if verbosity.level == VerbosityLevel::Verbose { out = if verbosity.groups_only { format!( - "group of '{}' retained as {}", - path.display(), + "group of {} retained as {}", + path.quote(), entries::gid2grp(dest_gid).unwrap_or_default() ) } else { format!( - "ownership of '{}' retained as {}:{}", - path.display(), + "ownership of {} retained as {}:{}", + path.quote(), entries::uid2usr(dest_uid).unwrap(), entries::gid2grp(dest_gid).unwrap() ) @@ -358,9 +359,9 @@ impl ChownExecutor { match self.verbosity.level { VerbosityLevel::Silent => (), _ => show_error!( - "cannot {} '{}': {}", + "cannot {} {}: {}", if follow { "dereference" } else { "access" }, - path.display(), + path.quote(), strip_errno(&e) ), } diff --git a/src/uucore/src/lib/lib.rs b/src/uucore/src/lib/lib.rs index 129ff9106..925246c24 100644 --- a/src/uucore/src/lib/lib.rs +++ b/src/uucore/src/lib/lib.rs @@ -72,6 +72,8 @@ use std::sync::atomic::Ordering; use once_cell::sync::Lazy; +use crate::display::Quotable; + pub fn get_utility_is_second_arg() -> bool { crate::macros::UTILITY_IS_SECOND_ARG.load(Ordering::SeqCst) } @@ -171,14 +173,15 @@ pub trait Args: Iterator + Sized { Ok(string) => Ok(string), Err(s_ret) => { full_conversion = false; - let lossy_conversion = s_ret.to_string_lossy(); eprintln!( - "Input with broken encoding occurred! (s = '{}') ", - &lossy_conversion + "Input with broken encoding occurred! (s = {}) ", + s_ret.quote() ); match handling { InvalidEncodingHandling::Ignore => Err(String::new()), - InvalidEncodingHandling::ConvertLossy => Err(lossy_conversion.to_string()), + InvalidEncodingHandling::ConvertLossy => { + Err(s_ret.to_string_lossy().into_owned()) + } InvalidEncodingHandling::Panic => { panic!("Broken encoding found but caller cannot handle it") } diff --git a/src/uucore/src/lib/mods/backup_control.rs b/src/uucore/src/lib/mods/backup_control.rs index 83617f8ed..acb7342b7 100644 --- a/src/uucore/src/lib/mods/backup_control.rs +++ b/src/uucore/src/lib/mods/backup_control.rs @@ -78,7 +78,10 @@ // spell-checker:ignore backupopt -use crate::error::{UError, UResult}; +use crate::{ + display::Quotable, + error::{UError, UResult}, +}; use clap::ArgMatches; use std::{ env, @@ -167,18 +170,22 @@ impl Display for BackupError { match self { BE::InvalidArgument(arg, origin) => write!( f, - "invalid argument '{}' for '{}'\n{}", - arg, origin, VALID_ARGS_HELP + "invalid argument {} for '{}'\n{}", + arg.quote(), + origin, + VALID_ARGS_HELP ), BE::AmbiguousArgument(arg, origin) => write!( f, - "ambiguous argument '{}' for '{}'\n{}", - arg, origin, VALID_ARGS_HELP + "ambiguous argument {} for '{}'\n{}", + arg.quote(), + origin, + VALID_ARGS_HELP ), BE::BackupImpossible() => write!(f, "cannot create backup"), // Placeholder for later // BE::BackupFailed(from, to, e) => Display::fmt( - // &uio_error!(e, "failed to backup '{}' to '{}'", from.display(), to.display()), + // &uio_error!(e, "failed to backup {} to {}", from.quote(), to.quote()), // f // ), } diff --git a/src/uucore/src/lib/mods/display.rs b/src/uucore/src/lib/mods/display.rs index e647a8c71..dfe64184f 100644 --- a/src/uucore/src/lib/mods/display.rs +++ b/src/uucore/src/lib/mods/display.rs @@ -87,13 +87,16 @@ macro_rules! impl_as_ref { }; } +impl_as_ref!(str); impl_as_ref!(&'_ str); impl_as_ref!(String); +impl_as_ref!(std::path::Path); impl_as_ref!(&'_ std::path::Path); impl_as_ref!(std::path::PathBuf); impl_as_ref!(std::path::Component<'_>); impl_as_ref!(std::path::Components<'_>); impl_as_ref!(std::path::Iter<'_>); +impl_as_ref!(std::ffi::OsStr); impl_as_ref!(&'_ std::ffi::OsStr); impl_as_ref!(std::ffi::OsString); @@ -106,6 +109,13 @@ impl Quotable for Cow<'_, str> { } } +impl Quotable for Cow<'_, std::path::Path> { + fn quote(&self) -> Quoted<'_> { + let text: &std::path::Path = self.as_ref(); + Quoted::new(text.as_ref()) + } +} + /// A wrapper around [`OsStr`] for printing paths with quoting and escaping applied. #[derive(Debug, Copy, Clone)] pub struct Quoted<'a> { @@ -407,6 +417,19 @@ pub fn println_verbatim>(text: S) -> io::Result<()> { Ok(()) } +/// Like `println_verbatim`, without the trailing newline. +pub fn print_verbatim>(text: S) -> io::Result<()> { + let mut stdout = io::stdout(); + #[cfg(any(unix, target_os = "wasi"))] + { + stdout.write_all(text.as_ref().as_bytes()) + } + #[cfg(not(any(unix, target_os = "wasi")))] + { + write!(stdout, "{}", std::path::Path::new(text.as_ref()).display()) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/uucore/src/lib/mods/error.rs b/src/uucore/src/lib/mods/error.rs index 04019e234..11ec91bdf 100644 --- a/src/uucore/src/lib/mods/error.rs +++ b/src/uucore/src/lib/mods/error.rs @@ -99,7 +99,10 @@ pub type UResult = Result>; /// An example of a custom error from `ls`: /// /// ``` -/// use uucore::error::{UError, UResult}; +/// use uucore::{ +/// display::Quotable, +/// error::{UError, UResult} +/// }; /// use std::{ /// error::Error, /// fmt::{Display, Debug}, @@ -126,8 +129,8 @@ pub type UResult = Result>; /// impl Display for LsError { /// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { /// match self { -/// LsError::InvalidLineWidth(s) => write!(f, "invalid line width: '{}'", s), -/// LsError::NoMetadata(p) => write!(f, "could not open file: '{}'", p.display()), +/// LsError::InvalidLineWidth(s) => write!(f, "invalid line width: {}", s.quote()), +/// LsError::NoMetadata(p) => write!(f, "could not open file: {}", p.quote()), /// } /// } /// } @@ -158,7 +161,10 @@ pub trait UError: Error + Send { /// # Example /// /// ``` - /// use uucore::error::{UError}; + /// use uucore::{ + /// display::Quotable, + /// error::UError + /// }; /// use std::{ /// error::Error, /// fmt::{Display, Debug}, @@ -189,8 +195,8 @@ pub trait UError: Error + Send { /// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { /// use MyError as ME; /// match self { - /// ME::Foo(s) => write!(f, "Unknown Foo: '{}'", s), - /// ME::Bar(p) => write!(f, "Couldn't find Bar: '{}'", p.display()), + /// ME::Foo(s) => write!(f, "Unknown Foo: {}", s.quote()), + /// ME::Bar(p) => write!(f, "Couldn't find Bar: {}", p.quote()), /// ME::Bing() => write!(f, "Exterminate!"), /// } /// } @@ -209,7 +215,10 @@ pub trait UError: Error + Send { /// # Example /// /// ``` - /// use uucore::error::{UError}; + /// use uucore::{ + /// display::Quotable, + /// error::UError + /// }; /// use std::{ /// error::Error, /// fmt::{Display, Debug}, @@ -240,8 +249,8 @@ pub trait UError: Error + Send { /// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { /// use MyError as ME; /// match self { - /// ME::Foo(s) => write!(f, "Unknown Foo: '{}'", s), - /// ME::Bar(p) => write!(f, "Couldn't find Bar: '{}'", p.display()), + /// ME::Foo(s) => write!(f, "Unknown Foo: {}", s.quote()), + /// ME::Bar(p) => write!(f, "Couldn't find Bar: {}", p.quote()), /// ME::Bing() => write!(f, "Exterminate!"), /// } /// } @@ -342,7 +351,10 @@ impl UError for UUsageError { /// There are two ways to construct this type: with [`UIoError::new`] or by calling the /// [`FromIo::map_err_context`] method on a [`std::io::Result`] or [`std::io::Error`]. /// ``` -/// use uucore::error::{FromIo, UResult, UIoError, UError}; +/// use uucore::{ +/// display::Quotable, +/// error::{FromIo, UResult, UIoError, UError} +/// }; /// use std::fs::File; /// use std::path::Path; /// let path = Path::new("test.txt"); @@ -350,12 +362,12 @@ impl UError for UUsageError { /// // Manual construction /// let e: Box = UIoError::new( /// std::io::ErrorKind::NotFound, -/// format!("cannot access '{}'", path.display()) +/// format!("cannot access {}", path.quote()) /// ); /// let res: UResult<()> = Err(e.into()); /// /// // Converting from an `std::io::Error`. -/// let res: UResult = File::open(path).map_err_context(|| format!("cannot access '{}'", path.display())); +/// let res: UResult = File::open(path).map_err_context(|| format!("cannot access {}", path.quote())); /// ``` #[derive(Debug)] pub struct UIoError { diff --git a/src/uucore/src/lib/mods/ranges.rs b/src/uucore/src/lib/mods/ranges.rs index 9e1e67d5a..f142e14fb 100644 --- a/src/uucore/src/lib/mods/ranges.rs +++ b/src/uucore/src/lib/mods/ranges.rs @@ -9,6 +9,8 @@ use std::str::FromStr; +use crate::display::Quotable; + #[derive(PartialEq, Eq, PartialOrd, Ord, Debug)] pub struct Range { pub low: usize, @@ -86,7 +88,7 @@ impl Range { for item in list.split(',') { let range_item = FromStr::from_str(item) - .map_err(|e| format!("range '{}' was invalid: {}", item, e))?; + .map_err(|e| format!("range {} was invalid: {}", item.quote(), e))?; ranges.push(range_item); } diff --git a/src/uucore/src/lib/parser/parse_size.rs b/src/uucore/src/lib/parser/parse_size.rs index ec0b08c9e..c05c0d3f1 100644 --- a/src/uucore/src/lib/parser/parse_size.rs +++ b/src/uucore/src/lib/parser/parse_size.rs @@ -9,6 +9,8 @@ use std::convert::TryFrom; use std::error::Error; use std::fmt; +use crate::display::Quotable; + /// Parse a size string into a number of bytes. /// /// A size string comprises an integer and an optional unit. The unit @@ -107,6 +109,9 @@ impl fmt::Display for ParseSizeError { } } +// FIXME: It's more idiomatic to move the formatting into the Display impl, +// but there's a lot of downstream code that constructs these errors manually +// that would be affected impl ParseSizeError { fn parse_failure(s: &str) -> ParseSizeError { // stderr on linux (GNU coreutils 8.32) (LC_ALL=C) @@ -140,7 +145,7 @@ impl ParseSizeError { // --width // --strings // etc. - ParseSizeError::ParseFailure(format!("'{}'", s)) + ParseSizeError::ParseFailure(format!("{}", s.quote())) } fn size_too_big(s: &str) -> ParseSizeError { @@ -160,7 +165,10 @@ impl ParseSizeError { // stderr on macos (brew - GNU coreutils 8.32) also differs for the same version, e.g.: // ghead: invalid number of bytes: '1Y': Value too large to be stored in data type // gtail: invalid number of bytes: '1Y': Value too large to be stored in data type - ParseSizeError::SizeTooBig(format!("'{}': Value too large for defined data type", s)) + ParseSizeError::SizeTooBig(format!( + "{}: Value too large for defined data type", + s.quote() + )) } } @@ -262,7 +270,7 @@ mod tests { for &test_string in &test_strings { assert_eq!( parse_size(test_string).unwrap_err(), - ParseSizeError::ParseFailure(format!("'{}'", test_string)) + ParseSizeError::ParseFailure(format!("{}", test_string.quote())) ); } } diff --git a/src/uucore/src/lib/parser/parse_time.rs b/src/uucore/src/lib/parser/parse_time.rs index fdf43b727..68f0ca8d0 100644 --- a/src/uucore/src/lib/parser/parse_time.rs +++ b/src/uucore/src/lib/parser/parse_time.rs @@ -9,6 +9,8 @@ use std::time::Duration; +use crate::display::Quotable; + pub fn from_str(string: &str) -> Result { let len = string.len(); if len == 0 { @@ -25,13 +27,13 @@ pub fn from_str(string: &str) -> Result { if string == "inf" || string == "infinity" { ("inf", 1) } else { - return Err(format!("invalid time interval '{}'", string)); + return Err(format!("invalid time interval {}", string.quote())); } } }; let num = numstr .parse::() - .map_err(|e| format!("invalid time interval '{}': {}", string, e))?; + .map_err(|e| format!("invalid time interval {}: {}", string.quote(), e))?; const NANOS_PER_SEC: u32 = 1_000_000_000; let whole_secs = num.trunc(); diff --git a/tests/by-util/test_chroot.rs b/tests/by-util/test_chroot.rs index 3bac07d44..65d821d01 100644 --- a/tests/by-util/test_chroot.rs +++ b/tests/by-util/test_chroot.rs @@ -23,7 +23,7 @@ fn test_enter_chroot_fails() { assert!(result .stderr_str() - .starts_with("chroot: cannot chroot to jail: Operation not permitted (os error 1)")); + .starts_with("chroot: cannot chroot to 'jail': Operation not permitted (os error 1)")); } #[test] @@ -34,7 +34,7 @@ fn test_no_such_directory() { ucmd.arg("a") .fails() - .stderr_is("chroot: cannot change root directory to `a`: no such directory"); + .stderr_is("chroot: cannot change root directory to 'a': no such directory"); } #[test] diff --git a/tests/by-util/test_cksum.rs b/tests/by-util/test_cksum.rs index 9590c1ac5..bf31ceb18 100644 --- a/tests/by-util/test_cksum.rs +++ b/tests/by-util/test_cksum.rs @@ -68,7 +68,7 @@ fn test_invalid_file() { .arg(folder_name) .fails() .no_stdout() - .stderr_contains("cksum: 'asdf' No such file or directory"); + .stderr_contains("cksum: asdf: No such file or directory"); // Then check when the file is of an invalid type at.mkdir(folder_name); @@ -76,7 +76,7 @@ fn test_invalid_file() { .arg(folder_name) .fails() .no_stdout() - .stderr_contains("cksum: 'asdf' Is a directory"); + .stderr_contains("cksum: asdf: Is a directory"); } // Make sure crc is correct for files larger than 32 bytes diff --git a/tests/by-util/test_sort.rs b/tests/by-util/test_sort.rs index cfe96d3c5..b389c9011 100644 --- a/tests/by-util/test_sort.rs +++ b/tests/by-util/test_sort.rs @@ -531,7 +531,7 @@ fn test_keys_invalid_field() { new_ucmd!() .args(&["-k", "1."]) .fails() - .stderr_only("sort: failed to parse key `1.`: failed to parse character index ``: cannot parse integer from empty string"); + .stderr_only("sort: failed to parse key '1.': failed to parse character index ``: cannot parse integer from empty string"); } #[test] @@ -539,7 +539,7 @@ fn test_keys_invalid_field_option() { new_ucmd!() .args(&["-k", "1.1x"]) .fails() - .stderr_only("sort: failed to parse key `1.1x`: invalid option: `x`"); + .stderr_only("sort: failed to parse key '1.1x': invalid option: `x`"); } #[test] @@ -547,7 +547,7 @@ fn test_keys_invalid_field_zero() { new_ucmd!() .args(&["-k", "0.1"]) .fails() - .stderr_only("sort: failed to parse key `0.1`: field index can not be 0"); + .stderr_only("sort: failed to parse key '0.1': field index can not be 0"); } #[test] @@ -555,7 +555,7 @@ fn test_keys_invalid_char_zero() { new_ucmd!() .args(&["-k", "1.0"]) .fails() - .stderr_only("sort: failed to parse key `1.0`: invalid character index 0 for the start position of a field"); + .stderr_only("sort: failed to parse key '1.0': invalid character index 0 for the start position of a field"); } #[test] diff --git a/tests/by-util/test_sum.rs b/tests/by-util/test_sum.rs index f09ba9d00..0248c05cf 100644 --- a/tests/by-util/test_sum.rs +++ b/tests/by-util/test_sum.rs @@ -59,7 +59,7 @@ fn test_invalid_file() { at.mkdir("a"); - ucmd.arg("a").fails().stderr_is("sum: 'a' Is a directory"); + ucmd.arg("a").fails().stderr_is("sum: a: Is a directory"); } #[test] @@ -68,5 +68,5 @@ fn test_invalid_metadata() { ucmd.arg("b") .fails() - .stderr_is("sum: 'b' No such file or directory"); + .stderr_is("sum: b: No such file or directory"); } diff --git a/tests/by-util/test_test.rs b/tests/by-util/test_test.rs index 23facd610..db74265a4 100644 --- a/tests/by-util/test_test.rs +++ b/tests/by-util/test_test.rs @@ -320,7 +320,7 @@ fn test_invalid_utf8_integer_compare() { cmd.run() .status_code(2) - .stderr_is("test: invalid integer 'fo�o'"); + .stderr_is("test: invalid integer $'fo\\x80o'"); let mut cmd = new_ucmd!(); cmd.raw.arg(arg); @@ -328,7 +328,7 @@ fn test_invalid_utf8_integer_compare() { cmd.run() .status_code(2) - .stderr_is("test: invalid integer 'fo�o'"); + .stderr_is("test: invalid integer $'fo\\x80o'"); } #[test]