1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-28 19:47:45 +00:00

Merge branch 'master' into ls_selinux

This commit is contained in:
Jan Scheer 2021-09-13 13:42:51 +02:00
commit 4dc6536bb9
No known key found for this signature in database
GPG key ID: C62AD4C29E2B9828
114 changed files with 1238 additions and 944 deletions

View file

@ -9,6 +9,8 @@ bytewise
canonicalization canonicalization
canonicalize canonicalize
canonicalizing canonicalizing
codepoint
codepoints
colorizable colorizable
colorize colorize
coprime coprime

3
Cargo.lock generated
View file

@ -3198,7 +3198,6 @@ name = "uu_unlink"
version = "0.0.7" version = "0.0.7"
dependencies = [ dependencies = [
"clap", "clap",
"libc",
"uucore", "uucore",
"uucore_procs", "uucore_procs",
] ]
@ -3277,6 +3276,8 @@ dependencies = [
"getopts", "getopts",
"lazy_static", "lazy_static",
"libc", "libc",
"nix 0.20.0",
"once_cell",
"termion", "termion",
"thiserror", "thiserror",
"time", "time",

View file

@ -98,6 +98,7 @@ feat_common_core = [
"touch", "touch",
"unexpand", "unexpand",
"uniq", "uniq",
"unlink",
"wc", "wc",
"yes", "yes",
] ]
@ -182,7 +183,6 @@ feat_require_unix = [
"timeout", "timeout",
"tty", "tty",
"uname", "uname",
"unlink",
] ]
# "feat_require_unix_utmpx" == set of utilities requiring unix utmp/utmpx support # "feat_require_unix_utmpx" == set of utilities requiring unix utmp/utmpx support
# * ref: <https://wiki.musl-libc.org/faq.html#Q:-Why-is-the-utmp/wtmp-functionality-only-implemented-as-stubs?> # * ref: <https://wiki.musl-libc.org/faq.html#Q:-Why-is-the-utmp/wtmp-functionality-only-implemented-as-stubs?>

View file

@ -10,10 +10,12 @@ use clap::Arg;
use clap::Shell; use clap::Shell;
use std::cmp; use std::cmp;
use std::collections::hash_map::HashMap; use std::collections::hash_map::HashMap;
use std::ffi::OsStr;
use std::ffi::OsString; use std::ffi::OsString;
use std::io::{self, Write}; use std::io::{self, Write};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::process; use std::process;
use uucore::display::Quotable;
const VERSION: &str = env!("CARGO_PKG_VERSION"); const VERSION: &str = env!("CARGO_PKG_VERSION");
@ -76,13 +78,21 @@ fn main() {
// 0th argument equals util name? // 0th argument equals util name?
if let Some(util_os) = 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" { if util == "completion" {
gen_completions(args, utils); gen_completions(args, utils);
} }
match utils.get(&util[..]) { match utils.get(util) {
Some(&(uumain, _)) => { Some(&(uumain, _)) => {
process::exit(uumain((vec![util_os].into_iter()).chain(args))); process::exit(uumain((vec![util_os].into_iter()).chain(args)));
} }
@ -90,9 +100,12 @@ fn main() {
if util == "--help" || util == "-h" { if util == "--help" || util == "-h" {
// see if they want help on a specific util // see if they want help on a specific util
if let Some(util_os) = args.next() { 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, _)) => { Some(&(uumain, _)) => {
let code = uumain( let code = uumain(
(vec![util_os, OsString::from("--help")].into_iter()) (vec![util_os, OsString::from("--help")].into_iter())
@ -101,17 +114,13 @@ fn main() {
io::stdout().flush().expect("could not flush stdout"); io::stdout().flush().expect("could not flush stdout");
process::exit(code); process::exit(code);
} }
None => { None => not_found(&util_os),
println!("{}: function/utility not found", util);
process::exit(1);
}
} }
} }
usage(&utils, binary_as_util); usage(&utils, binary_as_util);
process::exit(0); process::exit(0);
} else { } else {
println!("{}: function/utility not found", util); not_found(&util_os);
process::exit(1);
} }
} }
} }

View file

@ -38,7 +38,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
let name = uucore::util_name(); let name = uucore::util_name();
let config_result: Result<base_common::Config, String> = let config_result: Result<base_common::Config, String> =
base_common::parse_base_cmd_args(args, &name, VERSION, ABOUT, &usage); base_common::parse_base_cmd_args(args, name, VERSION, ABOUT, &usage);
let config = config_result.unwrap_or_else(|s| crash!(BASE_CMD_PARSE_ERROR, "{}", s)); let config = config_result.unwrap_or_else(|s| crash!(BASE_CMD_PARSE_ERROR, "{}", s));
// Create a reference to stdin so we can return a locked stdin from // Create a reference to stdin so we can return a locked stdin from
@ -52,12 +52,12 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
config.wrap_cols, config.wrap_cols,
config.ignore_garbage, config.ignore_garbage,
config.decode, config.decode,
&name, name,
); );
0 0
} }
pub fn uu_app() -> App<'static, 'static> { pub fn uu_app() -> App<'static, 'static> {
base_common::base_app(&uucore::util_name(), VERSION, ABOUT) base_common::base_app(uucore::util_name(), VERSION, ABOUT)
} }

View file

@ -9,6 +9,7 @@
use std::io::{stdout, Read, Write}; use std::io::{stdout, Read, Write};
use uucore::display::Quotable;
use uucore::encoding::{wrap_print, Data, Format}; use uucore::encoding::{wrap_print, Data, Format};
use uucore::InvalidEncodingHandling; use uucore::InvalidEncodingHandling;
@ -40,8 +41,9 @@ impl Config {
let name = values.next().unwrap(); let name = values.next().unwrap();
if let Some(extra_op) = values.next() { if let Some(extra_op) = values.next() {
return Err(format!( return Err(format!(
"extra operand '{}'\nTry '{} --help' for more information.", "extra operand {}\nTry '{} --help' for more information.",
extra_op, app_name extra_op.quote(),
app_name
)); ));
} }
@ -49,7 +51,7 @@ impl Config {
None None
} else { } else {
if !Path::exists(Path::new(name)) { 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()) Some(name.to_owned())
} }
@ -61,7 +63,7 @@ impl Config {
.value_of(options::WRAP) .value_of(options::WRAP)
.map(|num| { .map(|num| {
num.parse::<usize>() num.parse::<usize>()
.map_err(|_| format!("invalid wrap size: '{}'", num)) .map_err(|_| format!("invalid wrap size: {}", num.quote()))
}) })
.transpose()?; .transpose()?;

View file

@ -38,7 +38,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
let usage = usage(); let usage = usage();
let name = uucore::util_name(); let name = uucore::util_name();
let config_result: Result<base_common::Config, String> = let config_result: Result<base_common::Config, String> =
base_common::parse_base_cmd_args(args, &name, VERSION, ABOUT, &usage); base_common::parse_base_cmd_args(args, name, VERSION, ABOUT, &usage);
let config = config_result.unwrap_or_else(|s| crash!(BASE_CMD_PARSE_ERROR, "{}", s)); let config = config_result.unwrap_or_else(|s| crash!(BASE_CMD_PARSE_ERROR, "{}", s));
// Create a reference to stdin so we can return a locked stdin from // Create a reference to stdin so we can return a locked stdin from
@ -52,7 +52,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
config.wrap_cols, config.wrap_cols,
config.ignore_garbage, config.ignore_garbage,
config.decode, config.decode,
&name, name,
); );
0 0

View file

@ -47,7 +47,7 @@ fn usage() -> String {
} }
pub fn uu_app() -> App<'static, 'static> { pub fn uu_app() -> App<'static, 'static> {
let mut app = base_common::base_app(&uucore::util_name(), crate_version!(), ABOUT); let mut app = base_common::base_app(uucore::util_name(), crate_version!(), ABOUT);
for encoding in ENCODINGS { for encoding in ENCODINGS {
app = app.arg(Arg::with_name(encoding.0).long(encoding.0)); app = app.arg(Arg::with_name(encoding.0).long(encoding.0));
} }
@ -88,7 +88,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
config.wrap_cols, config.wrap_cols,
config.ignore_garbage, config.ignore_garbage,
config.decode, config.decode,
&name, name,
); );
0 0

View file

@ -18,7 +18,7 @@ path = "src/cat.rs"
clap = { version = "2.33", features = ["wrap_help"] } clap = { version = "2.33", features = ["wrap_help"] }
thiserror = "1.0" thiserror = "1.0"
atty = "0.2" atty = "0.2"
uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["fs"] } uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["fs", "pipes"] }
uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" }
[target.'cfg(unix)'.dependencies] [target.'cfg(unix)'.dependencies]

View file

@ -20,6 +20,7 @@ use clap::{crate_version, App, Arg};
use std::fs::{metadata, File}; use std::fs::{metadata, File};
use std::io::{self, Read, Write}; use std::io::{self, Read, Write};
use thiserror::Error; use thiserror::Error;
use uucore::display::Quotable;
use uucore::error::UResult; use uucore::error::UResult;
#[cfg(unix)] #[cfg(unix)]
@ -28,8 +29,6 @@ use std::os::unix::io::AsRawFd;
/// Linux splice support /// Linux splice support
#[cfg(any(target_os = "linux", target_os = "android"))] #[cfg(any(target_os = "linux", target_os = "android"))]
mod splice; mod splice;
#[cfg(any(target_os = "linux", target_os = "android"))]
use std::os::unix::io::RawFd;
/// Unix domain socket support /// Unix domain socket support
#[cfg(unix)] #[cfg(unix)]
@ -136,10 +135,18 @@ struct OutputState {
one_blank_kept: bool, one_blank_kept: bool,
} }
#[cfg(unix)]
trait FdReadable: Read + AsRawFd {}
#[cfg(not(unix))]
trait FdReadable: Read {}
#[cfg(unix)]
impl<T> FdReadable for T where T: Read + AsRawFd {}
#[cfg(not(unix))]
impl<T> FdReadable for T where T: Read {}
/// Represents an open file handle, stream, or other device /// Represents an open file handle, stream, or other device
struct InputHandle<R: Read> { struct InputHandle<R: FdReadable> {
#[cfg(any(target_os = "linux", target_os = "android"))]
file_descriptor: RawFd,
reader: R, reader: R,
is_interactive: bool, is_interactive: bool,
} }
@ -296,7 +303,7 @@ pub fn uu_app() -> App<'static, 'static> {
) )
} }
fn cat_handle<R: Read>( fn cat_handle<R: FdReadable>(
handle: &mut InputHandle<R>, handle: &mut InputHandle<R>,
options: &OutputOptions, options: &OutputOptions,
state: &mut OutputState, state: &mut OutputState,
@ -318,8 +325,6 @@ fn cat_path(
if path == "-" { if path == "-" {
let stdin = io::stdin(); let stdin = io::stdin();
let mut handle = InputHandle { let mut handle = InputHandle {
#[cfg(any(target_os = "linux", target_os = "android"))]
file_descriptor: stdin.as_raw_fd(),
reader: stdin, reader: stdin,
is_interactive: atty::is(atty::Stream::Stdin), is_interactive: atty::is(atty::Stream::Stdin),
}; };
@ -332,8 +337,6 @@ fn cat_path(
let socket = UnixStream::connect(path)?; let socket = UnixStream::connect(path)?;
socket.shutdown(Shutdown::Write)?; socket.shutdown(Shutdown::Write)?;
let mut handle = InputHandle { let mut handle = InputHandle {
#[cfg(any(target_os = "linux", target_os = "android"))]
file_descriptor: socket.as_raw_fd(),
reader: socket, reader: socket,
is_interactive: false, is_interactive: false,
}; };
@ -346,8 +349,6 @@ fn cat_path(
return Err(CatError::OutputIsInput); return Err(CatError::OutputIsInput);
} }
let mut handle = InputHandle { let mut handle = InputHandle {
#[cfg(any(target_os = "linux", target_os = "android"))]
file_descriptor: file.as_raw_fd(),
reader: file, reader: file,
is_interactive: false, is_interactive: false,
}; };
@ -386,7 +387,7 @@ fn cat_files(files: Vec<String>, options: &OutputOptions) -> UResult<()> {
for path in &files { for path in &files {
if let Err(err) = cat_path(path, options, &mut state, &out_info) { 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 { if state.skipped_carriage_return {
@ -436,14 +437,14 @@ fn get_input_type(path: &str) -> CatResult<InputType> {
/// Writes handle to stdout with no configuration. This allows a /// Writes handle to stdout with no configuration. This allows a
/// simple memory copy. /// simple memory copy.
fn write_fast<R: Read>(handle: &mut InputHandle<R>) -> CatResult<()> { fn write_fast<R: FdReadable>(handle: &mut InputHandle<R>) -> CatResult<()> {
let stdout = io::stdout(); let stdout = io::stdout();
let mut stdout_lock = stdout.lock(); let mut stdout_lock = stdout.lock();
#[cfg(any(target_os = "linux", target_os = "android"))] #[cfg(any(target_os = "linux", target_os = "android"))]
{ {
// If we're on Linux or Android, try to use the splice() system call // If we're on Linux or Android, try to use the splice() system call
// for faster writing. If it works, we're done. // for faster writing. If it works, we're done.
if !splice::write_fast_using_splice(handle, stdout_lock.as_raw_fd())? { if !splice::write_fast_using_splice(handle, &stdout_lock)? {
return Ok(()); return Ok(());
} }
} }
@ -461,7 +462,7 @@ fn write_fast<R: Read>(handle: &mut InputHandle<R>) -> CatResult<()> {
/// Outputs file contents to stdout in a line-by-line fashion, /// Outputs file contents to stdout in a line-by-line fashion,
/// propagating any errors that might occur. /// propagating any errors that might occur.
fn write_lines<R: Read>( fn write_lines<R: FdReadable>(
handle: &mut InputHandle<R>, handle: &mut InputHandle<R>,
options: &OutputOptions, options: &OutputOptions,
state: &mut OutputState, state: &mut OutputState,

View file

@ -1,11 +1,11 @@
use super::{CatResult, InputHandle}; use super::{CatResult, FdReadable, InputHandle};
use nix::fcntl::{splice, SpliceFFlags}; use nix::unistd;
use nix::unistd::{self, pipe}; use std::os::unix::io::{AsRawFd, RawFd};
use std::fs::File;
use std::io::Read;
use std::os::unix::io::{FromRawFd, RawFd};
use uucore::pipes::{pipe, splice, splice_exact};
const SPLICE_SIZE: usize = 1024 * 128;
const BUF_SIZE: usize = 1024 * 16; const BUF_SIZE: usize = 1024 * 16;
/// This function is called from `write_fast()` on Linux and Android. The /// This function is called from `write_fast()` on Linux and Android. The
@ -16,36 +16,25 @@ const BUF_SIZE: usize = 1024 * 16;
/// The `bool` in the result value indicates if we need to fall back to normal /// The `bool` in the result value indicates if we need to fall back to normal
/// copying or not. False means we don't have to. /// copying or not. False means we don't have to.
#[inline] #[inline]
pub(super) fn write_fast_using_splice<R: Read>( pub(super) fn write_fast_using_splice<R: FdReadable>(
handle: &mut InputHandle<R>, handle: &mut InputHandle<R>,
write_fd: RawFd, write_fd: &impl AsRawFd,
) -> CatResult<bool> { ) -> CatResult<bool> {
let (pipe_rd, pipe_wr) = pipe()?; let (pipe_rd, pipe_wr) = pipe()?;
// Ensure the pipe is closed when the function returns.
// SAFETY: The file descriptors do not have other owners.
let _handles = unsafe { (File::from_raw_fd(pipe_rd), File::from_raw_fd(pipe_wr)) };
loop { loop {
match splice( match splice(&handle.reader, &pipe_wr, SPLICE_SIZE) {
handle.file_descriptor,
None,
pipe_wr,
None,
BUF_SIZE,
SpliceFFlags::empty(),
) {
Ok(n) => { Ok(n) => {
if n == 0 { if n == 0 {
return Ok(false); return Ok(false);
} }
if splice_exact(pipe_rd, write_fd, n).is_err() { if splice_exact(&pipe_rd, write_fd, n).is_err() {
// If the first splice manages to copy to the intermediate // If the first splice manages to copy to the intermediate
// pipe, but the second splice to stdout fails for some reason // pipe, but the second splice to stdout fails for some reason
// we can recover by copying the data that we have from the // we can recover by copying the data that we have from the
// intermediate pipe to stdout using normal read/write. Then // intermediate pipe to stdout using normal read/write. Then
// we tell the caller to fall back. // we tell the caller to fall back.
copy_exact(pipe_rd, write_fd, n)?; copy_exact(pipe_rd.as_raw_fd(), write_fd.as_raw_fd(), n)?;
return Ok(true); return Ok(true);
} }
} }
@ -56,35 +45,23 @@ pub(super) fn write_fast_using_splice<R: Read>(
} }
} }
/// Splice wrapper which handles short writes. /// Move exactly `num_bytes` bytes from `read_fd` to `write_fd`.
#[inline] ///
fn splice_exact(read_fd: RawFd, write_fd: RawFd, num_bytes: usize) -> nix::Result<()> { /// Panics if not enough bytes can be read.
let mut left = num_bytes;
loop {
let written = splice(read_fd, None, write_fd, None, left, SpliceFFlags::empty())?;
left -= written;
if left == 0 {
break;
}
}
Ok(())
}
/// Caller must ensure that `num_bytes <= BUF_SIZE`, otherwise this function
/// will panic. The way we use this function in `write_fast_using_splice`
/// above is safe because `splice` is set to write at most `BUF_SIZE` to the
/// pipe.
#[inline]
fn copy_exact(read_fd: RawFd, write_fd: RawFd, num_bytes: usize) -> nix::Result<()> { fn copy_exact(read_fd: RawFd, write_fd: RawFd, num_bytes: usize) -> nix::Result<()> {
let mut left = num_bytes; let mut left = num_bytes;
let mut buf = [0; BUF_SIZE]; let mut buf = [0; BUF_SIZE];
loop { while left > 0 {
let read = unistd::read(read_fd, &mut buf[..left])?; let read = unistd::read(read_fd, &mut buf)?;
let written = unistd::write(write_fd, &buf[..read])?; assert_ne!(read, 0, "unexpected end of pipe");
left -= written; let mut written = 0;
if left == 0 { while written < read {
break; match unistd::write(write_fd, &buf[written..read])? {
0 => panic!(),
n => written += n,
} }
} }
left -= read;
}
Ok(()) Ok(())
} }

View file

@ -2,7 +2,7 @@
#![allow(clippy::upper_case_acronyms)] #![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 clap::{App, Arg};
use selinux::{OpaqueSecurityContext, SecurityContext}; use selinux::{OpaqueSecurityContext, SecurityContext};
@ -111,13 +111,13 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
Ok(context) => context, Ok(context) => context,
Err(_r) => { Err(_r) => {
show_error!("Invalid security context '{}'.", context.to_string_lossy()); show_error!("Invalid security context {}.", context.quote());
return libc::EXIT_FAILURE; return libc::EXIT_FAILURE;
} }
}; };
if SecurityContext::from_c_str(&c_context, false).check() == Some(false) { 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; return libc::EXIT_FAILURE;
} }
@ -281,7 +281,6 @@ pub fn uu_app() -> App<'static, 'static> {
#[derive(Debug)] #[derive(Debug)]
struct Options { struct Options {
verbose: bool, verbose: bool,
dereference: bool,
preserve_root: bool, preserve_root: bool,
recursive_mode: RecursiveMode, recursive_mode: RecursiveMode,
affect_symlink_referent: bool, affect_symlink_referent: bool,
@ -331,9 +330,6 @@ fn parse_command_line(config: clap::App, args: impl uucore::Args) -> Result<Opti
(RecursiveMode::NotRecursive, !no_dereference) (RecursiveMode::NotRecursive, !no_dereference)
}; };
// By default, dereference.
let dereference = !matches.is_present(options::dereference::NO_DEREFERENCE);
// By default, do not preserve root. // By default, do not preserve root.
let preserve_root = matches.is_present(options::preserve_root::PRESERVE_ROOT); let preserve_root = matches.is_present(options::preserve_root::PRESERVE_ROOT);
@ -369,7 +365,6 @@ fn parse_command_line(config: clap::App, args: impl uucore::Args) -> Result<Opti
Ok(Options { Ok(Options {
verbose, verbose,
dereference,
preserve_root, preserve_root,
recursive_mode, recursive_mode,
affect_symlink_referent, affect_symlink_referent,
@ -564,7 +559,7 @@ fn process_file(
println!( println!(
"{}: Changing security context of: {}", "{}: Changing security context of: {}",
uucore::util_name(), uucore::util_name(),
file_full_name.to_string_lossy() file_full_name.quote()
); );
} }
@ -699,9 +694,9 @@ fn root_dev_ino_warn(dir_name: &Path) {
); );
} else { } else {
show_warning!( 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.", Use --{} to override this failsafe.",
dir_name.to_string_lossy(), dir_name.quote(),
options::preserve_root::NO_PRESERVE_ROOT, options::preserve_root::NO_PRESERVE_ROOT,
); );
} }
@ -726,8 +721,8 @@ fn emit_cycle_warning(file_name: &Path) {
"Circular directory structure.\n\ "Circular directory structure.\n\
This almost certainly means that you have a corrupted file system.\n\ This almost certainly means that you have a corrupted file system.\n\
NOTIFY YOUR SYSTEM MANAGER.\n\ NOTIFY YOUR SYSTEM MANAGER.\n\
The following directory is part of the cycle '{}'.", The following directory is part of the cycle {}.",
file_name.display() file_name.quote()
) )
} }

View file

@ -2,6 +2,8 @@ use std::ffi::OsString;
use std::fmt::Write; use std::fmt::Write;
use std::io; use std::io;
use uucore::display::Quotable;
pub(crate) type Result<T> = std::result::Result<T, Error>; pub(crate) type Result<T> = std::result::Result<T, Error>;
#[derive(thiserror::Error, Debug)] #[derive(thiserror::Error, Debug)]
@ -30,7 +32,7 @@ pub(crate) enum Error {
source: io::Error, source: io::Error,
}, },
#[error("{operation} failed on '{}'", .operand1.to_string_lossy())] #[error("{operation} failed on {}", .operand1.quote())]
Io1 { Io1 {
operation: &'static str, operation: &'static str,
operand1: OsString, operand1: OsString,

View file

@ -9,6 +9,7 @@
#[macro_use] #[macro_use]
extern crate uucore; extern crate uucore;
use uucore::display::Quotable;
pub use uucore::entries; pub use uucore::entries;
use uucore::error::{FromIo, UResult, USimpleError}; use uucore::error::{FromIo, UResult, USimpleError};
use uucore::perms::{chown_base, options, IfFrom}; use uucore::perms::{chown_base, options, IfFrom};
@ -32,7 +33,7 @@ fn parse_gid_and_uid(matches: &ArgMatches) -> UResult<(Option<u32>, Option<u32>,
let dest_gid = if let Some(file) = matches.value_of(options::REFERENCE) { let dest_gid = if let Some(file) = matches.value_of(options::REFERENCE) {
fs::metadata(&file) fs::metadata(&file)
.map(|meta| Some(meta.gid())) .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 { } else {
let group = matches.value_of(options::ARG_GROUP).unwrap_or_default(); let group = matches.value_of(options::ARG_GROUP).unwrap_or_default();
if group.is_empty() { if group.is_empty() {
@ -40,7 +41,12 @@ fn parse_gid_and_uid(matches: &ArgMatches) -> UResult<(Option<u32>, Option<u32>,
} else { } else {
match entries::grp2gid(group) { match entries::grp2gid(group) {
Ok(g) => Some(g), Ok(g) => Some(g),
_ => return Err(USimpleError::new(1, format!("invalid group: '{}'", group))), _ => {
return Err(USimpleError::new(
1,
format!("invalid group: {}", group.quote()),
))
}
} }
} }
}; };

View file

@ -14,6 +14,7 @@ use clap::{crate_version, App, Arg};
use std::fs; use std::fs;
use std::os::unix::fs::{MetadataExt, PermissionsExt}; use std::os::unix::fs::{MetadataExt, PermissionsExt};
use std::path::Path; use std::path::Path;
use uucore::display::Quotable;
use uucore::fs::display_permissions_unix; use uucore::fs::display_permissions_unix;
use uucore::libc::mode_t; use uucore::libc::mode_t;
#[cfg(not(windows))] #[cfg(not(windows))]
@ -75,7 +76,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
.value_of(options::REFERENCE) .value_of(options::REFERENCE)
.and_then(|fref| match fs::metadata(fref) { .and_then(|fref| match fs::metadata(fref) {
Ok(meta) => Some(meta.mode()), 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 modes = matches.value_of(options::MODE).unwrap(); // should always be Some because required
let cmode = if mode_had_minus_prefix { let cmode = if mode_had_minus_prefix {
@ -223,21 +224,24 @@ impl Chmoder {
if !file.exists() { if !file.exists() {
if is_symlink(file) { if is_symlink(file) {
println!( println!(
"failed to change mode of '{}' from 0000 (---------) to 0000 (---------)", "failed to change mode of {} from 0000 (---------) to 0000 (---------)",
filename filename.quote()
); );
if !self.quiet { if !self.quiet {
show_error!("cannot operate on dangling symlink '{}'", filename); show_error!("cannot operate on dangling symlink {}", filename.quote());
} }
} else if !self.quiet { } 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); return Err(1);
} }
if self.recursive && self.preserve_root && filename == "/" { if self.recursive && self.preserve_root && filename == "/" {
show_error!( show_error!(
"it is dangerous to operate recursively on '{}'\nuse --no-preserve-root to override this failsafe", "it is dangerous to operate recursively on {}\nuse --no-preserve-root to override this failsafe",
filename filename.quote()
); );
return Err(1); return Err(1);
} }
@ -270,15 +274,17 @@ impl Chmoder {
if is_symlink(file) { if is_symlink(file) {
if self.verbose { if self.verbose {
println!( println!(
"neither symbolic link '{}' nor referent has been changed", "neither symbolic link {} nor referent has been changed",
file.display() file.quote()
); );
} }
return Ok(()); return Ok(());
} else if err.kind() == std::io::ErrorKind::PermissionDenied { } 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 { } else {
show_error!("'{}': {}", file.display(), err); show_error!("{}: {}", file.quote(), err);
} }
return Err(1); return Err(1);
} }
@ -325,7 +331,7 @@ impl Chmoder {
if (new_mode & !naively_expected_new_mode) != 0 { if (new_mode & !naively_expected_new_mode) != 0 {
show_error!( show_error!(
"{}: new permissions are {}, not {}", "{}: new permissions are {}, not {}",
file.display(), file.maybe_quote(),
display_permissions_unix(new_mode as mode_t, false), display_permissions_unix(new_mode as mode_t, false),
display_permissions_unix(naively_expected_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 fperm == mode {
if self.verbose && !self.changes { if self.verbose && !self.changes {
println!( println!(
"mode of '{}' retained as {:04o} ({})", "mode of {} retained as {:04o} ({})",
file.display(), file.quote(),
fperm, fperm,
display_permissions_unix(fperm as mode_t, false), display_permissions_unix(fperm as mode_t, false),
); );
@ -355,8 +361,8 @@ impl Chmoder {
} }
if self.verbose { if self.verbose {
println!( println!(
"failed to change mode of file '{}' from {:04o} ({}) to {:04o} ({})", "failed to change mode of file {} from {:04o} ({}) to {:04o} ({})",
file.display(), file.quote(),
fperm, fperm,
display_permissions_unix(fperm as mode_t, false), display_permissions_unix(fperm as mode_t, false),
mode, mode,
@ -367,8 +373,8 @@ impl Chmoder {
} else { } else {
if self.verbose || self.changes { if self.verbose || self.changes {
println!( println!(
"mode of '{}' changed from {:04o} ({}) to {:04o} ({})", "mode of {} changed from {:04o} ({}) to {:04o} ({})",
file.display(), file.quote(),
fperm, fperm,
display_permissions_unix(fperm as mode_t, false), display_permissions_unix(fperm as mode_t, false),
mode, mode,

View file

@ -5,10 +5,11 @@
// For the full copyright and license information, please view the LICENSE // For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code. // file that was distributed with this source code.
// spell-checker:ignore (ToDO) COMFOLLOW Passwd RFILE RFILE's derefer dgid duid // spell-checker:ignore (ToDO) COMFOLLOW Passwd RFILE RFILE's derefer dgid duid groupname
#[macro_use] #[macro_use]
extern crate uucore; extern crate uucore;
use uucore::display::Quotable;
pub use uucore::entries::{self, Group, Locate, Passwd}; pub use uucore::entries::{self, Group, Locate, Passwd};
use uucore::perms::{chown_base, options, IfFrom}; use uucore::perms::{chown_base, options, IfFrom};
@ -30,7 +31,7 @@ fn get_usage() -> String {
fn parse_gid_uid_and_filter(matches: &ArgMatches) -> UResult<(Option<u32>, Option<u32>, IfFrom)> { fn parse_gid_uid_and_filter(matches: &ArgMatches) -> UResult<(Option<u32>, Option<u32>, IfFrom)> {
let filter = if let Some(spec) = matches.value_of(options::FROM) { let filter = if let Some(spec) = matches.value_of(options::FROM) {
match parse_spec(spec)? { match parse_spec(spec, ':')? {
(Some(uid), None) => IfFrom::User(uid), (Some(uid), None) => IfFrom::User(uid),
(None, Some(gid)) => IfFrom::Group(gid), (None, Some(gid)) => IfFrom::Group(gid),
(Some(uid), Some(gid)) => IfFrom::UserGroup(uid, gid), (Some(uid), Some(gid)) => IfFrom::UserGroup(uid, gid),
@ -44,11 +45,11 @@ fn parse_gid_uid_and_filter(matches: &ArgMatches) -> UResult<(Option<u32>, Optio
let dest_gid: Option<u32>; let dest_gid: Option<u32>;
if let Some(file) = matches.value_of(options::REFERENCE) { if let Some(file) = matches.value_of(options::REFERENCE) {
let meta = fs::metadata(&file) 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_gid = Some(meta.gid());
dest_uid = Some(meta.uid()); dest_uid = Some(meta.uid());
} else { } else {
let (u, g) = parse_spec(matches.value_of(options::ARG_OWNER).unwrap())?; let (u, g) = parse_spec(matches.value_of(options::ARG_OWNER).unwrap(), ':')?;
dest_uid = u; dest_uid = u;
dest_gid = g; dest_gid = g;
} }
@ -165,24 +166,50 @@ pub fn uu_app() -> App<'static, 'static> {
) )
} }
fn parse_spec(spec: &str) -> UResult<(Option<u32>, Option<u32>)> { /// Parse the username and groupname
let args = spec.split_terminator(':').collect::<Vec<_>>(); ///
let usr_only = args.len() == 1 && !args[0].is_empty(); /// In theory, it should be username:groupname
let grp_only = args.len() == 2 && args[0].is_empty(); /// but ...
let usr_grp = args.len() == 2 && !args[0].is_empty() && !args[1].is_empty(); /// it can user.name:groupname
let uid = if usr_only || usr_grp { /// or username.groupname
Some( ///
Passwd::locate(args[0]) /// # Arguments
.map_err(|_| USimpleError::new(1, format!("invalid user: '{}'", spec)))? ///
.uid(), /// * `spec` - The input from the user
) /// * `sep` - Should be ':' or '.'
fn parse_spec(spec: &str, sep: char) -> UResult<(Option<u32>, Option<u32>)> {
assert!(['.', ':'].contains(&sep));
let mut args = spec.splitn(2, sep);
let user = args.next().unwrap_or("");
let group = args.next().unwrap_or("");
let uid = if !user.is_empty() {
Some(match Passwd::locate(user) {
Ok(u) => u.uid(), // We have been able to get the uid
Err(_) =>
// we have NOT been able to find the uid
// but we could be in the case where we have user.group
{
if spec.contains('.') && !spec.contains(':') && sep == ':' {
// but the input contains a '.' but not a ':'
// we might have something like username.groupname
// So, try to parse it this way
return parse_spec(spec, '.');
} else {
return Err(USimpleError::new(
1,
format!("invalid user: {}", spec.quote()),
));
}
}
})
} else { } else {
None None
}; };
let gid = if grp_only || usr_grp { let gid = if !group.is_empty() {
Some( Some(
Group::locate(args[1]) Group::locate(group)
.map_err(|_| USimpleError::new(1, format!("invalid group: '{}'", spec)))? .map_err(|_| USimpleError::new(1, format!("invalid group: {}", spec.quote())))?
.gid(), .gid(),
) )
} else { } else {
@ -197,7 +224,10 @@ mod test {
#[test] #[test]
fn test_parse_spec() { fn test_parse_spec() {
assert!(matches!(parse_spec(":"), Ok((None, None)))); assert!(matches!(parse_spec(":", ':'), Ok((None, None))));
assert!(format!("{}", parse_spec("::").err().unwrap()).starts_with("invalid group: ")); assert!(matches!(parse_spec(".", ':'), Ok((None, None))));
assert!(matches!(parse_spec(".", '.'), Ok((None, None))));
assert!(format!("{}", parse_spec("::", ':').err().unwrap()).starts_with("invalid group: "));
assert!(format!("{}", parse_spec("..", ':').err().unwrap()).starts_with("invalid group: "));
} }
} }

View file

@ -15,6 +15,7 @@ use std::ffi::CString;
use std::io::Error; use std::io::Error;
use std::path::Path; use std::path::Path;
use std::process::Command; use std::process::Command;
use uucore::display::Quotable;
use uucore::libc::{self, chroot, setgid, setgroups, setuid}; use uucore::libc::{self, chroot, setgid, setgroups, setuid};
use uucore::{entries, InvalidEncodingHandling}; use uucore::{entries, InvalidEncodingHandling};
@ -53,8 +54,8 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
if !newroot.is_dir() { if !newroot.is_dir() {
crash!( crash!(
1, 1,
"cannot change root directory to `{}`: no such directory", "cannot change root directory to {}: no such directory",
newroot.display() newroot.quote()
); );
} }
@ -149,7 +150,7 @@ fn set_context(root: &Path, options: &clap::ArgMatches) {
Some(u) => { Some(u) => {
let s: Vec<&str> = u.split(':').collect(); let s: Vec<&str> = u.split(':').collect();
if s.len() != 2 || s.iter().any(|&spec| spec.is_empty()) { if s.len() != 2 || s.iter().any(|&spec| spec.is_empty()) {
crash!(1, "invalid userspec: `{}`", u) crash!(1, "invalid userspec: {}", u.quote())
}; };
s s
} }
@ -170,7 +171,6 @@ fn set_context(root: &Path, options: &clap::ArgMatches) {
} }
fn enter_chroot(root: &Path) { fn enter_chroot(root: &Path) {
let root_str = root.display();
std::env::set_current_dir(root).unwrap(); std::env::set_current_dir(root).unwrap();
let err = unsafe { let err = unsafe {
chroot(CString::new(".").unwrap().as_bytes_with_nul().as_ptr() as *const libc::c_char) 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!( crash!(
1, 1,
"cannot chroot to {}: {}", "cannot chroot to {}: {}",
root_str, root.quote(),
Error::last_os_error() Error::last_os_error()
) )
}; };
@ -189,7 +189,7 @@ fn set_main_group(group: &str) {
if !group.is_empty() { if !group.is_empty() {
let group_id = match entries::grp2gid(group) { let group_id = match entries::grp2gid(group) {
Ok(g) => g, Ok(g) => g,
_ => crash!(1, "no such group: {}", group), _ => crash!(1, "no such group: {}", group.maybe_quote()),
}; };
let err = unsafe { setgid(group_id) }; let err = unsafe { setgid(group_id) };
if err != 0 { if err != 0 {
@ -234,7 +234,12 @@ fn set_user(user: &str) {
let user_id = entries::usr2uid(user).unwrap(); let user_id = entries::usr2uid(user).unwrap();
let err = unsafe { setuid(user_id as libc::uid_t) }; let err = unsafe { setuid(user_id as libc::uid_t) };
if err != 0 { 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()
)
} }
} }
} }

View file

@ -14,6 +14,7 @@ use clap::{crate_version, App, Arg};
use std::fs::File; use std::fs::File;
use std::io::{self, stdin, BufReader, Read}; use std::io::{self, stdin, BufReader, Read};
use std::path::Path; use std::path::Path;
use uucore::display::Quotable;
use uucore::InvalidEncodingHandling; use uucore::InvalidEncodingHandling;
// NOTE: CRC_TABLE_LEN *must* be <= 256 as we cast 0..CRC_TABLE_LEN to u8 // 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("-") { match cksum("-") {
Ok((crc, size)) => println!("{} {}", crc, size), Ok((crc, size)) => println!("{} {}", crc, size),
Err(err) => { Err(err) => {
show_error!("{}", err); show_error!("-: {}", err);
return 2; return 2;
} }
} }
@ -203,7 +204,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
match cksum(fname.as_ref()) { match cksum(fname.as_ref()) {
Ok((crc, size)) => println!("{} {} {}", crc, size, fname), Ok((crc, size)) => println!("{} {} {}", crc, size, fname),
Err(err) => { Err(err) => {
show_error!("'{}' {}", fname, err); show_error!("{}: {}", fname.maybe_quote(), err);
exit_code = 2; exit_code = 2;
} }
} }

View file

@ -18,6 +18,7 @@ extern crate quick_error;
#[macro_use] #[macro_use]
extern crate uucore; extern crate uucore;
use uucore::display::Quotable;
#[cfg(windows)] #[cfg(windows)]
use winapi::um::fileapi::CreateFileW; use winapi::um::fileapi::CreateFileW;
#[cfg(windows)] #[cfg(windows)]
@ -541,8 +542,8 @@ impl FromStr for Attribute {
"xattr" => Attribute::Xattr, "xattr" => Attribute::Xattr,
_ => { _ => {
return Err(Error::InvalidArgument(format!( return Err(Error::InvalidArgument(format!(
"invalid attribute '{}'", "invalid attribute {}",
value value.quote()
))); )));
} }
}) })
@ -659,8 +660,8 @@ impl Options {
"never" => ReflinkMode::Never, "never" => ReflinkMode::Never,
value => { value => {
return Err(Error::InvalidArgument(format!( return Err(Error::InvalidArgument(format!(
"invalid argument '{}' for \'reflink\'", "invalid argument {} for \'reflink\'",
value value.quote()
))); )));
} }
} }
@ -832,7 +833,7 @@ fn copy(sources: &[Source], target: &TargetSlice, options: &Options) -> CopyResu
let mut seen_sources = HashSet::with_capacity(sources.len()); let mut seen_sources = HashSet::with_capacity(sources.len());
for source in sources { for source in sources {
if seen_sources.contains(source) { if seen_sources.contains(source) {
show_warning!("source '{}' specified more than once", source.display()); show_warning!("source {} specified more than once", source.quote());
} else { } else {
let mut found_hard_link = false; let mut found_hard_link = false;
if preserve_hard_links { if preserve_hard_links {
@ -873,8 +874,8 @@ fn construct_dest_path(
) -> CopyResult<PathBuf> { ) -> CopyResult<PathBuf> {
if options.no_target_dir && target.is_dir() { if options.no_target_dir && target.is_dir() {
return Err(format!( return Err(format!(
"cannot overwrite directory '{}' with non-directory", "cannot overwrite directory {} with non-directory",
target.display() target.quote()
) )
.into()); .into());
} }
@ -941,7 +942,7 @@ fn adjust_canonicalization(p: &Path) -> Cow<Path> {
/// will not cause a short-circuit. /// will not cause a short-circuit.
fn copy_directory(root: &Path, target: &TargetSlice, options: &Options) -> CopyResult<()> { fn copy_directory(root: &Path, target: &TargetSlice, options: &Options) -> CopyResult<()> {
if !options.recursive { 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 // if no-dereference is enabled and this is a symlink, copy it as a file
@ -1041,12 +1042,12 @@ impl OverwriteMode {
match *self { match *self {
OverwriteMode::NoClobber => Err(Error::NotAllFilesCopied), OverwriteMode::NoClobber => Err(Error::NotAllFilesCopied),
OverwriteMode::Interactive(_) => { OverwriteMode::Interactive(_) => {
if prompt_yes!("{}: overwrite {}? ", uucore::util_name(), path.display()) { if prompt_yes!("{}: overwrite {}? ", uucore::util_name(), path.quote()) {
Ok(()) Ok(())
} else { } else {
Err(Error::Skipped(format!( Err(Error::Skipped(format!(
"Not overwriting {} at user request", "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<()> { 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)?; let source_metadata = fs::symlink_metadata(source).context(context)?;
match *attribute { match *attribute {
Attribute::Mode => { Attribute::Mode => {
@ -1152,7 +1153,7 @@ fn symlink_file(source: &Path, dest: &Path, context: &str) -> CopyResult<()> {
} }
fn context_for(src: &Path, dest: &Path) -> String { 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. /// 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(), Some(name) => dest.join(name).into(),
None => crash!( None => crash!(
EXIT_ERR, EXIT_ERR,
"cannot stat '{}': No such file or directory", "cannot stat {}: No such file or directory",
source.display() source.quote()
), ),
} }
} else { } else {
@ -1454,11 +1455,11 @@ fn copy_on_write_macos(
pub fn verify_target_type(target: &Path, target_type: &TargetType) -> CopyResult<()> { pub fn verify_target_type(target: &Path, target_type: &TargetType) -> CopyResult<()> {
match (target_type, target.is_dir()) { match (target_type, target.is_dir()) {
(&TargetType::Directory, false) => { (&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!( (&TargetType::File, true) => Err(format!(
"cannot overwrite directory '{}' with non-directory", "cannot overwrite directory {} with non-directory",
target.display() target.quote()
) )
.into()), .into()),
_ => Ok(()), _ => Ok(()),

View file

@ -10,6 +10,7 @@ use std::{
fs::{remove_file, File}, fs::{remove_file, File},
io::{BufRead, BufWriter, Write}, io::{BufRead, BufWriter, Write},
}; };
use uucore::display::Quotable;
mod csplit_error; mod csplit_error;
mod patterns; 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 = crash_if_err!(1, File::open(file_name));
let file_metadata = crash_if_err!(1, file.metadata()); let file_metadata = crash_if_err!(1, file.metadata());
if !file_metadata.is_file() { 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))); crash_if_err!(1, csplit(&options, patterns, BufReader::new(file)));
}; };

View file

@ -1,26 +1,28 @@
use std::io; use std::io;
use thiserror::Error; use thiserror::Error;
use uucore::display::Quotable;
/// Errors thrown by the csplit command /// Errors thrown by the csplit command
#[derive(Debug, Error)] #[derive(Debug, Error)]
pub enum CsplitError { pub enum CsplitError {
#[error("IO error: {}", _0)] #[error("IO error: {}", _0)]
IoError(io::Error), IoError(io::Error),
#[error("'{}': line number out of range", _0)] #[error("{}: line number out of range", ._0.quote())]
LineOutOfRange(String), 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), LineOutOfRangeOnRepetition(String, usize),
#[error("'{}': match not found", _0)] #[error("{}: match not found", ._0.quote())]
MatchNotFound(String), MatchNotFound(String),
#[error("'{}': match not found on repetition {}", _0, _1)] #[error("{}: match not found on repetition {}", ._0.quote(), _1)]
MatchNotFoundOnRepetition(String, usize), MatchNotFoundOnRepetition(String, usize),
#[error("line number must be greater than zero")] #[error("line number must be greater than zero")]
LineNumberIsZero, LineNumberIsZero,
#[error("line number '{}' is smaller than preceding line number, {}", _0, _1)] #[error("line number '{}' is smaller than preceding line number, {}", _0, _1)]
LineNumberSmallerThanPrevious(usize, usize), LineNumberSmallerThanPrevious(usize, usize),
#[error("invalid pattern: {}", _0)] #[error("{}: invalid pattern", ._0.quote())]
InvalidPattern(String), InvalidPattern(String),
#[error("invalid number: '{}'", _0)] #[error("invalid number: {}", ._0.quote())]
InvalidNumber(String), InvalidNumber(String),
#[error("incorrect conversion specification in suffix")] #[error("incorrect conversion specification in suffix")]
SuffixFormatIncorrect, SuffixFormatIncorrect,

View file

@ -15,6 +15,7 @@ use clap::{crate_version, App, Arg};
use std::fs::File; use std::fs::File;
use std::io::{stdin, stdout, BufReader, BufWriter, Read, Write}; use std::io::{stdin, stdout, BufReader, BufWriter, Read, Write};
use std::path::Path; use std::path::Path;
use uucore::display::Quotable;
use self::searcher::Searcher; use self::searcher::Searcher;
use uucore::ranges::Range; use uucore::ranges::Range;
@ -351,19 +352,19 @@ fn cut_files(mut filenames: Vec<String>, mode: Mode) -> i32 {
let path = Path::new(&filename[..]); let path = Path::new(&filename[..]);
if path.is_dir() { if path.is_dir() {
show_error!("{}: Is a directory", filename); show_error!("{}: Is a directory", filename.maybe_quote());
continue; continue;
} }
if path.metadata().is_err() { if path.metadata().is_err() {
show_error!("{}: No such file or directory", filename); show_error!("{}: No such file or directory", filename.maybe_quote());
continue; continue;
} }
let file = match File::open(&path) { let file = match File::open(&path) {
Ok(f) => f, Ok(f) => f,
Err(e) => { Err(e) => {
show_error!("opening '{}': {}", &filename[..], e); show_error!("opening {}: {}", filename.quote(), e);
continue; continue;
} }
}; };

View file

@ -17,6 +17,8 @@ use libc::{clock_settime, timespec, CLOCK_REALTIME};
use std::fs::File; use std::fs::File;
use std::io::{BufRead, BufReader}; use std::io::{BufRead, BufReader};
use std::path::PathBuf; use std::path::PathBuf;
use uucore::display::Quotable;
use uucore::show_error;
#[cfg(windows)] #[cfg(windows)]
use winapi::{ use winapi::{
shared::minwindef::WORD, shared::minwindef::WORD,
@ -145,7 +147,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
let format = if let Some(form) = matches.value_of(OPT_FORMAT) { let format = if let Some(form) = matches.value_of(OPT_FORMAT) {
if !form.starts_with('+') { if !form.starts_with('+') {
eprintln!("date: invalid date '{}'", form); show_error!("invalid date {}", form.quote());
return 1; return 1;
} }
let form = form[1..].to_string(); let form = form[1..].to_string();
@ -174,7 +176,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
let set_to = match matches.value_of(OPT_SET).map(parse_date) { let set_to = match matches.value_of(OPT_SET).map(parse_date) {
None => None, None => None,
Some(Err((input, _err))) => { Some(Err((input, _err))) => {
eprintln!("date: invalid date '{}'", input); show_error!("invalid date {}", input.quote());
return 1; return 1;
} }
Some(Ok(date)) => Some(date), Some(Ok(date)) => Some(date),
@ -240,7 +242,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
println!("{}", formatted); println!("{}", formatted);
} }
Err((input, _err)) => { Err((input, _err)) => {
println!("date: invalid date '{}'", input); show_error!("invalid date {}", input.quote());
} }
} }
} }
@ -352,13 +354,13 @@ fn set_system_datetime(_date: DateTime<Utc>) -> i32 {
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
fn set_system_datetime(_date: DateTime<Utc>) -> i32 { fn set_system_datetime(_date: DateTime<Utc>) -> i32 {
eprintln!("date: setting the date is not supported by macOS"); show_error!("setting the date is not supported by macOS");
1 1
} }
#[cfg(target_os = "redox")] #[cfg(target_os = "redox")]
fn set_system_datetime(_date: DateTime<Utc>) -> i32 { fn set_system_datetime(_date: DateTime<Utc>) -> i32 {
eprintln!("date: setting the date is not supported by Redox"); show_error!("setting the date is not supported by Redox");
1 1
} }
@ -378,7 +380,7 @@ fn set_system_datetime(date: DateTime<Utc>) -> i32 {
if result != 0 { if result != 0 {
let error = std::io::Error::last_os_error(); let error = std::io::Error::last_os_error();
eprintln!("date: cannot set date: {}", error); show_error!("cannot set date: {}", error);
error.raw_os_error().unwrap() error.raw_os_error().unwrap()
} else { } else {
0 0
@ -408,7 +410,7 @@ fn set_system_datetime(date: DateTime<Utc>) -> i32 {
if result == 0 { if result == 0 {
let error = std::io::Error::last_os_error(); let error = std::io::Error::last_os_error();
eprintln!("date: cannot set date: {}", error); show_error!("cannot set date: {}", error);
error.raw_os_error().unwrap() error.raw_os_error().unwrap()
} else { } else {
0 0

View file

@ -17,6 +17,7 @@ use std::fs::File;
use std::io::{BufRead, BufReader}; use std::io::{BufRead, BufReader};
use clap::{crate_version, App, Arg}; use clap::{crate_version, App, Arg};
use uucore::display::Quotable;
mod options { mod options {
pub const BOURNE_SHELL: &str = "bourne-shell"; 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 matches.is_present(options::PRINT_DATABASE) {
if !files.is_empty() { if !files.is_empty() {
show_usage_error!( show_usage_error!(
"extra operand '{}'\nfile operands cannot be combined with \ "extra operand {}\nfile operands cannot be combined with \
--print-database (-p)", --print-database (-p)",
files[0] files[0].quote()
); );
return 1; return 1;
} }
@ -126,7 +127,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
result = parse(INTERNAL_DB.lines(), out_format, "") result = parse(INTERNAL_DB.lines(), out_format, "")
} else { } else {
if files.len() > 1 { if files.len() > 1 {
show_usage_error!("extra operand '{}'", files[1]); show_usage_error!("extra operand {}", files[1].quote());
return 1; return 1;
} }
match File::open(files[0]) { 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]) result = parse(fin.lines().filter_map(Result::ok), out_format, files[0])
} }
Err(e) => { Err(e) => {
show_error!("{}: {}", files[0], e); show_error!("{}: {}", files[0].maybe_quote(), e);
return 1; return 1;
} }
} }
@ -314,7 +315,8 @@ where
if val.is_empty() { if val.is_empty() {
return Err(format!( return Err(format!(
"{}:{}: invalid line; missing second token", "{}:{}: invalid line; missing second token",
fp, num fp.maybe_quote(),
num
)); ));
} }
let lower = key.to_lowercase(); let lower = key.to_lowercase();
@ -341,7 +343,12 @@ where
} else if let Some(s) = table.get(lower.as_str()) { } else if let Some(s) = table.get(lower.as_str()) {
result.push_str(format!("{}={}:", s, val).as_str()); result.push_str(format!("{}={}:", s, val).as_str());
} else { } else {
return Err(format!("{}:{}: unrecognized keyword {}", fp, num, key)); return Err(format!(
"{}:{}: unrecognized keyword {}",
fp.maybe_quote(),
num,
key
));
} }
} }
} }

View file

@ -10,6 +10,7 @@ extern crate uucore;
use clap::{crate_version, App, Arg}; use clap::{crate_version, App, Arg};
use std::path::Path; use std::path::Path;
use uucore::display::print_verbatim;
use uucore::error::{UResult, UUsageError}; use uucore::error::{UResult, UUsageError};
use uucore::InvalidEncodingHandling; use uucore::InvalidEncodingHandling;
@ -65,7 +66,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
if d.components().next() == None { if d.components().next() == None {
print!(".") print!(".")
} else { } else {
print!("{}", d.to_string_lossy()); print_verbatim(d).unwrap();
} }
} }
None => { None => {

View file

@ -32,6 +32,7 @@ use std::path::PathBuf;
use std::str::FromStr; use std::str::FromStr;
use std::time::{Duration, UNIX_EPOCH}; use std::time::{Duration, UNIX_EPOCH};
use std::{error::Error, fmt::Display}; use std::{error::Error, fmt::Display};
use uucore::display::{print_verbatim, Quotable};
use uucore::error::{UError, UResult}; use uucore::error::{UError, UResult};
use uucore::parse_size::{parse_size, ParseSizeError}; use uucore::parse_size::{parse_size, ParseSizeError};
use uucore::InvalidEncodingHandling; use uucore::InvalidEncodingHandling;
@ -293,9 +294,9 @@ fn du(
Err(e) => { Err(e) => {
safe_writeln!( safe_writeln!(
stderr(), stderr(),
"{}: cannot read directory '{}': {}", "{}: cannot read directory {}: {}",
options.util_name, options.util_name,
my_stat.path.display(), my_stat.path.quote(),
e e
); );
return Box::new(iter::once(my_stat)); return Box::new(iter::once(my_stat));
@ -334,11 +335,11 @@ fn du(
} }
Err(error) => match error.kind() { Err(error) => match error.kind() {
ErrorKind::PermissionDenied => { ErrorKind::PermissionDenied => {
let description = format!("cannot access '{}'", entry.path().display()); let description = format!("cannot access {}", entry.path().quote());
let error_message = "Permission denied"; let error_message = "Permission denied";
show_error_custom_description!(description, "{}", error_message) 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), Err(error) => show_error!("{}", error),
@ -411,26 +412,30 @@ enum DuError {
impl Display for DuError { impl Display for DuError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
DuError::InvalidMaxDepthArg(s) => write!(f, "invalid maximum depth '{}'", s), DuError::InvalidMaxDepthArg(s) => write!(f, "invalid maximum depth {}", s.quote()),
DuError::SummarizeDepthConflict(s) => { 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!( DuError::InvalidTimeStyleArg(s) => write!(
f, f,
"invalid argument '{}' for 'time style' "invalid argument {} for 'time style'
Valid arguments are: Valid arguments are:
- 'full-iso' - 'full-iso'
- 'long-iso' - 'long-iso'
- 'iso' - 'iso'
Try '{} --help' for more information.", Try '{} --help' for more information.",
s, s.quote(),
uucore::execution_phrase() uucore::execution_phrase()
), ),
DuError::InvalidTimeArg(s) => write!( DuError::InvalidTimeArg(s) => write!(
f, f,
"Invalid argument '{}' for --time. "Invalid argument {} for --time.
'birth' and 'creation' arguments are not supported on this platform.", 'birth' and 'creation' arguments are not supported on this platform.",
s s.quote()
), ),
} }
} }
@ -466,7 +471,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let options = Options { let options = Options {
all: matches.is_present(options::ALL), all: matches.is_present(options::ALL),
util_name: uucore::util_name(), util_name: uucore::util_name().to_owned(),
max_depth, max_depth,
total: matches.is_present(options::TOTAL), total: matches.is_present(options::TOTAL),
separate_dirs: matches.is_present(options::SEPARATE_DIRS), separate_dirs: matches.is_present(options::SEPARATE_DIRS),
@ -566,21 +571,14 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
}; };
if !summarize || index == len - 1 { if !summarize || index == len - 1 {
let time_str = tm.format(time_format_str).to_string(); let time_str = tm.format(time_format_str).to_string();
print!( print!("{}\t{}\t", convert_size(size), time_str);
"{}\t{}\t{}{}", print_verbatim(stat.path).unwrap();
convert_size(size), print!("{}", line_separator);
time_str,
stat.path.display(),
line_separator
);
} }
} else if !summarize || index == len - 1 { } else if !summarize || index == len - 1 {
print!( print!("{}\t", convert_size(size));
"{}\t{}{}", print_verbatim(stat.path).unwrap();
convert_size(size), print!("{}", line_separator);
stat.path.display(),
line_separator
);
} }
if options.total && index == (len - 1) { if options.total && index == (len - 1) {
// The last element will be the total size of the the path under // 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(_) => { 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 echos affected flag, -B or --block-size (-t or --threshold), depending user's selection
// GNU's du does distinguish between "invalid (suffix in) argument" // GNU's du does distinguish between "invalid (suffix in) argument"
match error { match error {
ParseSizeError::ParseFailure(_) => format!("invalid --{} argument '{}'", option, s), ParseSizeError::ParseFailure(_) => format!("invalid --{} argument {}", option, s.quote()),
ParseSizeError::SizeTooBig(_) => format!("--{} argument '{}' too large", option, s), ParseSizeError::SizeTooBig(_) => format!("--{} argument {} too large", option, s.quote()),
} }
} }

13
src/uu/env/src/env.rs vendored
View file

@ -22,6 +22,7 @@ use std::env;
use std::io::{self, Write}; use std::io::{self, Write};
use std::iter::Iterator; use std::iter::Iterator;
use std::process::Command; use std::process::Command;
use uucore::display::Quotable;
use uucore::error::{UResult, USimpleError}; use uucore::error::{UResult, USimpleError};
const USAGE: &str = "env [OPTION]... [-] [NAME=VALUE]... [COMMAND [ARG]...]"; const USAGE: &str = "env [OPTION]... [-] [NAME=VALUE]... [COMMAND [ARG]...]";
@ -65,8 +66,14 @@ fn parse_name_value_opt<'a>(opts: &mut Options<'a>, opt: &'a str) -> Result<bool
fn parse_program_opt<'a>(opts: &mut Options<'a>, opt: &'a str) -> Result<(), i32> { fn parse_program_opt<'a>(opts: &mut Options<'a>, opt: &'a str) -> Result<(), i32> {
if opts.null { if opts.null {
eprintln!("{}: cannot specify --null (-0) with command", crate_name!()); eprintln!(
eprintln!("Type \"{} --help\" for detailed information", crate_name!()); "{}: cannot specify --null (-0) with command",
uucore::util_name()
);
eprintln!(
"Type \"{} --help\" for detailed information",
uucore::execution_phrase()
);
Err(1) Err(1)
} else { } else {
opts.program.push(opt); opts.program.push(opt);
@ -87,7 +94,7 @@ fn load_config_file(opts: &mut Options) -> UResult<()> {
}; };
let conf = conf.map_err(|error| { let conf = conf.map_err(|error| {
eprintln!("env: error: \"{}\": {}", file, error); show_error!("{}: {}", file.maybe_quote(), error);
1 1
})?; })?;

View file

@ -17,6 +17,7 @@ use std::fs::File;
use std::io::{stdin, stdout, BufRead, BufReader, BufWriter, Read, Write}; use std::io::{stdin, stdout, BufRead, BufReader, BufWriter, Read, Write};
use std::str::from_utf8; use std::str::from_utf8;
use unicode_width::UnicodeWidthChar; use unicode_width::UnicodeWidthChar;
use uucore::display::Quotable;
static ABOUT: &str = "Convert tabs in each FILE to spaces, writing to standard output. static ABOUT: &str = "Convert tabs in each FILE to spaces, writing to standard output.
With no FILE, or when FILE is -, read standard input."; With no FILE, or when FILE is -, read standard input.";
@ -216,7 +217,7 @@ fn open(path: String) -> BufReader<Box<dyn Read + 'static>> {
} else { } else {
file_buf = match File::open(&path[..]) { file_buf = match File::open(&path[..]) {
Ok(a) => a, 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<dyn Read>) BufReader::new(Box::new(file_buf) as Box<dyn Read>)
} }

View file

@ -16,6 +16,7 @@ use std::io::{self, stdin, stdout, BufRead, Write};
mod factor; mod factor;
use clap::{crate_version, App, Arg}; use clap::{crate_version, App, Arg};
pub use factor::*; pub use factor::*;
use uucore::display::Quotable;
mod miller_rabin; mod miller_rabin;
pub mod numeric; pub mod numeric;
@ -52,7 +53,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
if let Some(values) = matches.values_of(options::NUMBER) { if let Some(values) = matches.values_of(options::NUMBER) {
for number in values { for number in values {
if let Err(e) = print_factors_str(number, &mut w, &mut factors_buffer) { if let Err(e) = print_factors_str(number, &mut w, &mut factors_buffer) {
show_warning!("{}: {}", number, e); show_warning!("{}: {}", number.maybe_quote(), e);
} }
} }
} else { } else {
@ -61,7 +62,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
for line in stdin.lock().lines() { for line in stdin.lock().lines() {
for number in line.unwrap().split_whitespace() { for number in line.unwrap().split_whitespace() {
if let Err(e) = print_factors_str(number, &mut w, &mut factors_buffer) { if let Err(e) = print_factors_str(number, &mut w, &mut factors_buffer) {
show_warning!("{}: {}", number, e); show_warning!("{}: {}", number.maybe_quote(), e);
} }
} }
} }

View file

@ -15,6 +15,7 @@ use std::cmp;
use std::fs::File; use std::fs::File;
use std::io::{stdin, stdout, Write}; use std::io::{stdin, stdout, Write};
use std::io::{BufReader, BufWriter, Read}; use std::io::{BufReader, BufWriter, Read};
use uucore::display::Quotable;
use self::linebreak::break_lines; use self::linebreak::break_lines;
use self::parasplit::ParagraphStream; use self::parasplit::ParagraphStream;
@ -132,7 +133,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
fmt_opts.width = match s.parse::<usize>() { fmt_opts.width = match s.parse::<usize>() {
Ok(t) => t, Ok(t) => t,
Err(e) => { Err(e) => {
crash!(1, "Invalid WIDTH specification: `{}': {}", s, e); crash!(1, "Invalid WIDTH specification: {}: {}", s.quote(), e);
} }
}; };
if fmt_opts.width > MAX_WIDTH { if fmt_opts.width > MAX_WIDTH {
@ -149,7 +150,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
fmt_opts.goal = match s.parse::<usize>() { fmt_opts.goal = match s.parse::<usize>() {
Ok(t) => t, Ok(t) => t,
Err(e) => { Err(e) => {
crash!(1, "Invalid GOAL specification: `{}': {}", s, e); crash!(1, "Invalid GOAL specification: {}: {}", s.quote(), e);
} }
}; };
if !matches.is_present(OPT_WIDTH) { if !matches.is_present(OPT_WIDTH) {
@ -163,7 +164,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
fmt_opts.tabwidth = match s.parse::<usize>() { fmt_opts.tabwidth = match s.parse::<usize>() {
Ok(t) => t, Ok(t) => t,
Err(e) => { Err(e) => {
crash!(1, "Invalid TABWIDTH specification: `{}': {}", s, e); crash!(1, "Invalid TABWIDTH specification: {}: {}", s.quote(), e);
} }
}; };
}; };
@ -187,7 +188,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
_ => match File::open(i) { _ => match File::open(i) {
Ok(f) => BufReader::new(Box::new(f) as Box<dyn Read + 'static>), Ok(f) => BufReader::new(Box::new(f) as Box<dyn Read + 'static>),
Err(e) => { Err(e) => {
show_warning!("{}: {}", i, e); show_warning!("{}: {}", i.maybe_quote(), e);
continue; continue;
} }
}, },

View file

@ -17,7 +17,10 @@
#[macro_use] #[macro_use]
extern crate uucore; 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}; use clap::{crate_version, App, Arg};
@ -77,7 +80,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
.join(" ") .join(" ")
); );
} else { } else {
show_error!("'{}': no such user", user); show_error!("{}: no such user", user.quote());
exit_code = 1; exit_code = 1;
} }
} }

View file

@ -34,6 +34,7 @@ use std::io::{self, stdin, BufRead, BufReader, Read};
use std::iter; use std::iter;
use std::num::ParseIntError; use std::num::ParseIntError;
use std::path::Path; use std::path::Path;
use uucore::display::Quotable;
const NAME: &str = "hashsum"; const NAME: &str = "hashsum";
@ -525,7 +526,7 @@ where
if options.warn { if options.warn {
show_warning!( show_warning!(
"{}: {}: improperly formatted {} checksum line", "{}: {}: improperly formatted {} checksum line",
filename.display(), filename.maybe_quote(),
i + 1, i + 1,
options.algoname options.algoname
); );
@ -546,6 +547,15 @@ where
) )
) )
.to_ascii_lowercase(); .to_ascii_lowercase();
// FIXME: Filenames with newlines should be treated specially.
// GNU appears to replace newlines by \n and backslashes by
// \\ and prepend a backslash (to the hash or filename) if it did
// this escaping.
// Different sorts of output (checking vs outputting hashes) may
// handle this differently. Compare carefully to GNU.
// If you can, try to preserve invalid unicode using OsStr(ing)Ext
// and display it using uucore::display::print_verbatim(). This is
// easier (and more important) on Unix than on Windows.
if sum == real_sum { if sum == real_sum {
if !options.quiet { if !options.quiet {
println!("{}: OK", ck_filename); println!("{}: OK", ck_filename);

View file

@ -9,6 +9,7 @@ use clap::{crate_version, App, Arg};
use std::convert::TryFrom; use std::convert::TryFrom;
use std::ffi::OsString; use std::ffi::OsString;
use std::io::{self, ErrorKind, Read, Seek, SeekFrom, Write}; use std::io::{self, ErrorKind, Read, Seek, SeekFrom, Write};
use uucore::display::Quotable;
use uucore::{crash, show_error_custom_description}; use uucore::{crash, show_error_custom_description};
const EXIT_FAILURE: i32 = 1; const EXIT_FAILURE: i32 = 1;
@ -127,10 +128,10 @@ fn arg_iterate<'a>(
match parse::parse_obsolete(s) { match parse::parse_obsolete(s) {
Some(Ok(iter)) => Ok(Box::new(vec![first].into_iter().chain(iter).chain(args))), Some(Ok(iter)) => Ok(Box::new(vec![first].into_iter().chain(iter).chain(args))),
Some(Err(e)) => match e { 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!( parse::ParseError::Overflow => Err(format!(
"invalid argument: '{}' Value too large for defined datatype", "invalid argument: {} Value too large for defined datatype",
s s.quote()
)), )),
}, },
None => Ok(Box::new(vec![first, second].into_iter().chain(args))), 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) { let mut file = match std::fs::File::open(name) {
Ok(f) => f, Ok(f) => f,
Err(err) => { Err(err) => {
let prefix = format!("cannot open '{}' for reading", name); let prefix = format!("cannot open {} for reading", name.quote());
match err.kind() { match err.kind() {
ErrorKind::NotFound => { ErrorKind::NotFound => {
show_error_custom_description!(prefix, "No such file or directory"); show_error_custom_description!(prefix, "No such file or directory");

View file

@ -41,6 +41,7 @@ extern crate uucore;
use clap::{crate_version, App, Arg}; use clap::{crate_version, App, Arg};
use std::ffi::CStr; use std::ffi::CStr;
use uucore::display::Quotable;
use uucore::entries::{self, Group, Locate, Passwd}; use uucore::entries::{self, Group, Locate, Passwd};
use uucore::error::UResult; use uucore::error::UResult;
use uucore::error::{set_exit_code, USimpleError}; 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()) { match Passwd::locate(users[i].as_str()) {
Ok(p) => Some(p), Ok(p) => Some(p),
Err(_) => { Err(_) => {
show_error!("'{}': no such user", users[i]); show_error!("{}: no such user", users[i].quote());
set_exit_code(1); set_exit_code(1);
if i + 1 >= users.len() { if i + 1 >= users.len() {
break; break;

View file

@ -16,6 +16,7 @@ use clap::{crate_version, App, Arg, ArgMatches};
use file_diff::diff; use file_diff::diff;
use filetime::{set_file_times, FileTime}; use filetime::{set_file_times, FileTime};
use uucore::backup_control::{self, BackupMode}; use uucore::backup_control::{self, BackupMode};
use uucore::display::Quotable;
use uucore::entries::{grp2gid, usr2uid}; use uucore::entries::{grp2gid, usr2uid};
use uucore::error::{FromIo, UError, UIoError, UResult}; use uucore::error::{FromIo, UError, UIoError, UResult};
use uucore::mode::get_umask; use uucore::mode::get_umask;
@ -95,40 +96,30 @@ impl Display for InstallError {
) )
} }
IE::CreateDirFailed(dir, e) => { 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!( IE::InvalidTarget(target) => write!(
f, f,
"invalid target {}: No such file or directory", "invalid target {}: No such file or directory",
target.display() target.quote()
), ),
IE::TargetDirIsntDir(target) => { 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( IE::BackupFailed(from, to, e) => Display::fmt(
&uio_error!( &uio_error!(e, "cannot backup {} to {}", from.quote(), to.quote()),
e,
"cannot backup '{}' to '{}'",
from.display(),
to.display()
),
f, f,
), ),
IE::InstallFailed(from, to, e) => Display::fmt( IE::InstallFailed(from, to, e) => Display::fmt(
&uio_error!( &uio_error!(e, "cannot install {} to {}", from.quote(), to.quote()),
e,
"cannot install '{}' to '{}'",
from.display(),
to.display()
),
f, f,
), ),
IE::StripProgramFailed(msg) => write!(f, "strip program failed: {}", msg), IE::StripProgramFailed(msg) => write!(f, "strip program failed: {}", msg),
IE::MetadataFailed(e) => Display::fmt(&uio_error!(e, ""), f), IE::MetadataFailed(e) => Display::fmt(&uio_error!(e, ""), f),
IE::NoSuchUser(user) => write!(f, "no such user: {}", user), IE::NoSuchUser(user) => write!(f, "no such user: {}", user.maybe_quote()),
IE::NoSuchGroup(group) => write!(f, "no such group: {}", group), IE::NoSuchGroup(group) => write!(f, "no such group: {}", group.maybe_quote()),
IE::OmittingDirectory(dir) => write!(f, "omitting directory '{}'", dir.display()), IE::OmittingDirectory(dir) => write!(f, "omitting directory {}", dir.quote()),
} }
} }
} }
@ -416,14 +407,14 @@ fn directory(paths: Vec<String>, b: Behavior) -> UResult<()> {
// the default mode. Hence it is safe to use fs::create_dir_all // the default mode. Hence it is safe to use fs::create_dir_all
// and then only modify the target's dir mode. // and then only modify the target's dir mode.
if let Err(e) = 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); show!(e);
continue; continue;
} }
if b.verbose { if b.verbose {
println!("creating directory '{}'", path.display()); println!("creating directory {}", path.quote());
} }
} }
@ -445,7 +436,7 @@ fn directory(paths: Vec<String>, b: Behavior) -> UResult<()> {
fn is_new_file_path(path: &Path) -> bool { fn is_new_file_path(path: &Path) -> bool {
!path.exists() !path.exists()
&& (path.parent().map(Path::is_dir).unwrap_or(true) && (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. /// 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() { if !sourcepath.exists() {
let err = UIoError::new( let err = UIoError::new(
std::io::ErrorKind::NotFound, std::io::ErrorKind::NotFound,
format!("cannot stat '{}'", sourcepath.display()), format!("cannot stat {}", sourcepath.quote()),
); );
show!(err); show!(err);
continue; 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 /* workaround a limitation of fs::copy
* https://github.com/rust-lang/rust/issues/79390 * https://github.com/rust-lang/rust/issues/79390
*/ */
@ -674,9 +665,9 @@ fn copy(from: &Path, to: &Path, b: &Behavior) -> UResult<()> {
} }
if b.verbose { if b.verbose {
print!("'{}' -> '{}'", from.display(), to.display()); print!("{} -> {}", from.quote(), to.quote());
match backup_path { match backup_path {
Some(path) => println!(" (backup: '{}')", path.display()), Some(path) => println!(" (backup: {})", path.quote()),
None => println!(), None => println!(),
} }
} }

View file

@ -22,8 +22,9 @@ pub fn parse(mode_string: &str, considering_dir: bool, umask: u32) -> Result<u32
#[cfg(any(unix, target_os = "redox"))] #[cfg(any(unix, target_os = "redox"))]
pub fn chmod(path: &Path, mode: u32) -> Result<(), ()> { pub fn chmod(path: &Path, mode: u32) -> Result<(), ()> {
use std::os::unix::fs::PermissionsExt; use std::os::unix::fs::PermissionsExt;
use uucore::display::Quotable;
fs::set_permissions(path, fs::Permissions::from_mode(mode)).map_err(|err| { 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);
}) })
} }

View file

@ -14,6 +14,7 @@ use clap::{crate_version, App, Arg};
use std::cmp::Ordering; use std::cmp::Ordering;
use std::fs::File; use std::fs::File;
use std::io::{stdin, BufRead, BufReader, Lines, Stdin}; use std::io::{stdin, BufRead, BufReader, Lines, Stdin};
use uucore::display::Quotable;
static NAME: &str = "join"; static NAME: &str = "join";
@ -181,18 +182,18 @@ impl Spec {
return Spec::Key; return Spec::Key;
} }
crash!(1, "invalid field specifier: '{}'", format); crash!(1, "invalid field specifier: {}", format.quote());
} }
Some('1') => FileNum::File1, Some('1') => FileNum::File1,
Some('2') => FileNum::File2, 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() { if let Some('.') = chars.next() {
return Spec::Field(file_num, parse_field_number(chars.as_str())); 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 { } else {
match File::open(name) { match File::open(name) {
Ok(file) => Box::new(BufReader::new(file)) as Box<dyn BufRead>, Ok(file) => Box::new(BufReader::new(file)) as Box<dyn BufRead>,
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)); let diff = input.compare(self.get_current_key(), line.get_field(self.key));
if diff == Ordering::Greater { 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. // This is fatal if the check is enabled.
if input.check_order == CheckOrder::Enabled { if input.check_order == CheckOrder::Enabled {
@ -727,7 +732,7 @@ fn get_field_number(keys: Option<usize>, key: Option<usize>) -> usize {
fn parse_field_number(value: &str) -> usize { fn parse_field_number(value: &str) -> usize {
match value.parse::<usize>() { match value.parse::<usize>() {
Ok(result) if result > 0 => result - 1, 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 { match value {
"1" => FileNum::File1, "1" => FileNum::File1,
"2" => FileNum::File2, "2" => FileNum::File2,
value => crash!(1, "invalid file number: '{}'", value), value => crash!(1, "invalid file number: {}", value.quote()),
} }
} }

View file

@ -13,6 +13,7 @@ extern crate uucore;
use clap::{crate_version, App, Arg}; use clap::{crate_version, App, Arg};
use libc::{c_int, pid_t}; use libc::{c_int, pid_t};
use std::io::Error; use std::io::Error;
use uucore::display::Quotable;
use uucore::error::{UResult, USimpleError}; use uucore::error::{UResult, USimpleError};
use uucore::signals::ALL_SIGNALS; use uucore::signals::ALL_SIGNALS;
use uucore::InvalidEncodingHandling; use uucore::InvalidEncodingHandling;
@ -154,7 +155,7 @@ fn print_signal(signal_name_or_value: &str) -> UResult<()> {
} }
Err(USimpleError::new( Err(USimpleError::new(
1, 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 => { None => {
return Err(USimpleError::new( return Err(USimpleError::new(
1, 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) => { Err(e) => {
return Err(USimpleError::new( return Err(USimpleError::new(
1, 1,
format!("failed to parse argument {}: {}", pid, e), format!("failed to parse argument {}: {}", pid.quote(), e),
)); ));
} }
}; };

View file

@ -11,11 +11,12 @@
extern crate uucore; extern crate uucore;
use clap::{crate_version, App, Arg}; use clap::{crate_version, App, Arg};
use uucore::display::Quotable;
use uucore::error::{UError, UResult}; use uucore::error::{UError, UResult};
use std::borrow::Cow; use std::borrow::Cow;
use std::error::Error; use std::error::Error;
use std::ffi::OsStr; use std::ffi::{OsStr, OsString};
use std::fmt::Display; use std::fmt::Display;
use std::fs; use std::fs;
@ -49,26 +50,26 @@ pub enum OverwriteMode {
#[derive(Debug)] #[derive(Debug)]
enum LnError { enum LnError {
TargetIsDirectory(String), TargetIsDirectory(PathBuf),
SomeLinksFailed, SomeLinksFailed,
FailedToLink(String), FailedToLink(String),
MissingDestination(String), MissingDestination(PathBuf),
ExtraOperand(String), ExtraOperand(OsString),
} }
impl Display for LnError { impl Display for LnError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
Self::TargetIsDirectory(s) => write!(f, "target '{}' is not a directory", s), Self::TargetIsDirectory(s) => write!(f, "target {} is not a directory", s.quote()),
Self::FailedToLink(s) => write!(f, "failed to link '{}'", s), Self::FailedToLink(e) => write!(f, "failed to link: {}", e),
Self::SomeLinksFailed => write!(f, "some links failed to create"), Self::SomeLinksFailed => write!(f, "some links failed to create"),
Self::MissingDestination(s) => { Self::MissingDestination(s) => {
write!(f, "missing destination file operand after '{}'", s) write!(f, "missing destination file operand after {}", s.quote())
} }
Self::ExtraOperand(s) => write!( Self::ExtraOperand(s) => write!(
f, f,
"extra operand '{}'\nTry '{} --help' for more information.", "extra operand {}\nTry '{} --help' for more information.",
s, s.quote(),
uucore::execution_phrase() 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 // 1st form. Now there should be only two operands, but if -T is
// specified we may have a wrong number of operands. // specified we may have a wrong number of operands.
if files.len() == 1 { 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 { 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()); 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<()> { fn link_files_in_dir(files: &[PathBuf], target_dir: &Path, settings: &Settings) -> UResult<()> {
if !target_dir.is_dir() { 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; 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 is_symlink(target_dir) {
if target_dir.is_file() { if target_dir.is_file() {
if let Err(e) = fs::remove_file(target_dir) { 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() { 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 // considered as a dir
// See test_ln::test_symlink_no_deref_dir // See test_ln::test_symlink_no_deref_dir
if let Err(e) = fs::remove_dir(target_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 => { None => {
show_error!( show_error!("cannot stat {}: No such file or directory", srcpath.quote());
"cannot stat '{}': No such file or directory",
srcpath.display()
);
all_successful = false; all_successful = false;
continue; 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) { if let Err(e) = link(srcpath, &targetpath, settings) {
show_error!( show_error!(
"cannot link '{}' to '{}': {}", "cannot link {} to {}: {}",
targetpath.display(), targetpath.quote(),
srcpath.display(), srcpath.quote(),
e e
); );
all_successful = false; all_successful = false;
@ -399,7 +397,7 @@ fn link(src: &Path, dst: &Path, settings: &Settings) -> Result<()> {
match settings.overwrite { match settings.overwrite {
OverwriteMode::NoClobber => {} OverwriteMode::NoClobber => {}
OverwriteMode::Interactive => { OverwriteMode::Interactive => {
print!("{}: overwrite '{}'? ", uucore::util_name(), dst.display()); print!("{}: overwrite {}? ", uucore::util_name(), dst.quote());
if !read_yes() { if !read_yes() {
return Ok(()); return Ok(());
} }
@ -426,9 +424,9 @@ fn link(src: &Path, dst: &Path, settings: &Settings) -> Result<()> {
} }
if settings.verbose { if settings.verbose {
print!("'{}' -> '{}'", dst.display(), &source.display()); print!("{} -> {}", dst.quote(), source.quote());
match backup_path { match backup_path {
Some(path) => println!(" (backup: '{}')", path.display()), Some(path) => println!(" (backup: {})", path.quote()),
None => println!(), None => println!(),
} }
} }

View file

@ -35,7 +35,7 @@ fn get_userlogin() -> Option<String> {
static SUMMARY: &str = "Print user's login name"; static SUMMARY: &str = "Print user's login name";
fn usage() -> String { fn usage() -> &'static str {
uucore::execution_phrase() uucore::execution_phrase()
} }
@ -44,8 +44,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
.collect_str(InvalidEncodingHandling::Ignore) .collect_str(InvalidEncodingHandling::Ignore)
.accept_any(); .accept_any();
let usage = usage(); let _ = uu_app().usage(usage()).get_matches_from(args);
let _ = uu_app().usage(&usage[..]).get_matches_from(args);
match get_userlogin() { match get_userlogin() {
Some(userlogin) => println!("{}", userlogin), Some(userlogin) => println!("{}", userlogin),

View file

@ -40,8 +40,10 @@ use std::{
time::Duration, time::Duration,
}; };
use term_grid::{Cell, Direction, Filling, Grid, GridOptions}; use term_grid::{Cell, Direction, Filling, Grid, GridOptions};
use uucore::display::Quotable; use uucore::{
use uucore::error::{set_exit_code, FromIo, UError, UResult}; display::Quotable,
error::{set_exit_code, FromIo, UError, UResult},
};
use unicode_width::UnicodeWidthStr; use unicode_width::UnicodeWidthStr;
#[cfg(unix)] #[cfg(unix)]
@ -157,8 +159,8 @@ impl Error for LsError {}
impl Display for LsError { impl Display for LsError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
LsError::InvalidLineWidth(s) => write!(f, "invalid line width: '{}'", s), LsError::InvalidLineWidth(s) => write!(f, "invalid line width: {}", s.quote()),
LsError::NoMetadata(p) => write!(f, "could not open file: '{}'", p.display()), LsError::NoMetadata(p) => write!(f, "could not open file: {}", p.quote()),
} }
} }
} }
@ -428,18 +430,18 @@ impl Config {
}, },
None => match termsize::get() { None => match termsize::get() {
Some(size) => size.cols, Some(size) => size.cols,
None => match std::env::var("COLUMNS") { None => match std::env::var_os("COLUMNS") {
Ok(columns) => match columns.parse() { Some(columns) => match columns.to_str().and_then(|s| s.parse().ok()) {
Ok(columns) => columns, Some(columns) => columns,
Err(_) => { None => {
show_error!( show_error!(
"ignoring invalid width in environment variable COLUMNS: '{}'", "ignoring invalid width in environment variable COLUMNS: {}",
columns columns.quote()
); );
DEFAULT_TERM_WIDTH DEFAULT_TERM_WIDTH
} }
}, },
Err(_) => DEFAULT_TERM_WIDTH, None => DEFAULT_TERM_WIDTH,
}, },
}, },
}; };
@ -556,7 +558,7 @@ impl Config {
Ok(p) => { Ok(p) => {
ignore_patterns.add(p); ignore_patterns.add(p);
} }
Err(_) => show_warning!("Invalid pattern for ignore: '{}'", pattern), Err(_) => show_warning!("Invalid pattern for ignore: {}", pattern.quote()),
} }
} }
@ -566,7 +568,7 @@ impl Config {
Ok(p) => { Ok(p) => {
ignore_patterns.add(p); ignore_patterns.add(p);
} }
Err(_) => show_warning!("Invalid pattern for hide: '{}'", pattern), Err(_) => show_warning!("Invalid pattern for hide: {}", pattern.quote()),
} }
} }
} }
@ -1332,7 +1334,7 @@ fn list(locs: Vec<&Path>, config: Config) -> UResult<()> {
if path_data.md().is_none() { if path_data.md().is_none() {
show!(std::io::ErrorKind::NotFound 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 // We found an error, no need to continue the execution
continue; continue;
} }

View file

@ -12,6 +12,7 @@ use clap::OsValues;
use clap::{crate_version, App, Arg}; use clap::{crate_version, App, Arg};
use std::fs; use std::fs;
use std::path::Path; use std::path::Path;
use uucore::display::Quotable;
use uucore::error::{FromIo, UResult, USimpleError}; use uucore::error::{FromIo, UResult, USimpleError};
static ABOUT: &str = "Create the given DIRECTORY(ies) if they do not exist"; 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 // Not tested on Windows
let mode: u16 = match matches.value_of(options::MODE) { let mode: u16 = match matches.value_of(options::MODE) {
Some(m) => u16::from_str_radix(m, 8) 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, None => 0o755_u16,
}; };
@ -100,13 +101,13 @@ fn mkdir(path: &Path, recursive: bool, mode: u16, verbose: bool) -> UResult<()>
fs::create_dir 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 { if verbose {
println!( println!(
"{}: created directory '{}'", "{}: created directory {}",
uucore::util_name(), 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)); let mode = Permissions::from_mode(u32::from(mode));
set_permissions(path, 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)] #[cfg(windows)]

View file

@ -11,7 +11,7 @@ extern crate uucore;
use clap::{crate_version, App, Arg}; use clap::{crate_version, App, Arg};
use libc::mkfifo; use libc::mkfifo;
use std::ffi::CString; use std::ffi::CString;
use uucore::InvalidEncodingHandling; use uucore::{display::Quotable, InvalidEncodingHandling};
static NAME: &str = "mkfifo"; static NAME: &str = "mkfifo";
static USAGE: &str = "mkfifo [OPTION]... NAME..."; 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) mkfifo(name.as_ptr(), mode as libc::mode_t)
}; };
if err == -1 { if err == -1 {
show_error!("cannot create fifo '{}': File exists", f); show_error!("cannot create fifo {}: File exists", f.quote());
exit_code = 1; exit_code = 1;
} }
} }

View file

@ -16,6 +16,7 @@ use clap::{crate_version, App, Arg, ArgMatches};
use libc::{dev_t, mode_t}; 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 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; use uucore::InvalidEncodingHandling;
static ABOUT: &str = "Create the special file NAME of the given TYPE."; 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) { if vec!['b', 'c', 'u', 'p'].contains(&first_char) {
Ok(()) Ok(())
} else { } else {
Err(format!("invalid device type '{}'", tpe)) Err(format!("invalid device type {}", tpe.quote()))
} }
}) })
} }

View file

@ -12,6 +12,7 @@
extern crate uucore; extern crate uucore;
use clap::{crate_version, App, Arg}; use clap::{crate_version, App, Arg};
use uucore::display::{println_verbatim, Quotable};
use uucore::error::{FromIo, UError, UResult}; use uucore::error::{FromIo, UError, UResult};
use std::env; use std::env;
@ -57,16 +58,20 @@ impl Display for MkTempError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use MkTempError::*; use MkTempError::*;
match self { match self {
PersistError(p) => write!(f, "could not persist file '{}'", p.display()), PersistError(p) => write!(f, "could not persist file {}", p.quote()),
MustEndInX(s) => write!(f, "with --suffix, template '{}' must end in X", s), MustEndInX(s) => write!(f, "with --suffix, template {} must end in X", s.quote()),
TooFewXs(s) => write!(f, "too few X's in template '{}'", s), TooFewXs(s) => write!(f, "too few X's in template {}", s.quote()),
ContainsDirSeparator(s) => { ContainsDirSeparator(s) => {
write!(f, "invalid suffix '{}', contains directory separator", s) write!(
f,
"invalid suffix {}, contains directory separator",
s.quote()
)
} }
InvalidTemplate(s) => write!( InvalidTemplate(s) => write!(
f, f,
"invalid template, '{}'; with --tmpdir, it may not be absolute", "invalid template, {}; with --tmpdir, it may not be absolute",
s s.quote()
), ),
} }
} }
@ -224,15 +229,13 @@ fn parse_template<'a>(
pub fn dry_exec(mut tmpdir: PathBuf, prefix: &str, rand: usize, suffix: &str) -> UResult<()> { pub fn dry_exec(mut tmpdir: PathBuf, prefix: &str, rand: usize, suffix: &str) -> UResult<()> {
let len = prefix.len() + suffix.len() + rand; let len = prefix.len() + suffix.len() + rand;
let mut buf = String::with_capacity(len); let mut buf = Vec::with_capacity(len);
buf.push_str(prefix); buf.extend(prefix.as_bytes());
buf.extend(iter::repeat('X').take(rand)); buf.extend(iter::repeat(b'X').take(rand));
buf.push_str(suffix); buf.extend(suffix.as_bytes());
// Randomize. // Randomize.
unsafe { let bytes = &mut buf[prefix.len()..prefix.len() + rand];
// We guarantee utf8.
let bytes = &mut buf.as_mut_vec()[prefix.len()..prefix.len() + rand];
rand::thread_rng().fill(bytes); rand::thread_rng().fill(bytes);
for byte in bytes.iter_mut() { for byte in bytes.iter_mut() {
*byte = match *byte % 62 { *byte = match *byte % 62 {
@ -242,10 +245,10 @@ pub fn dry_exec(mut tmpdir: PathBuf, prefix: &str, rand: usize, suffix: &str) ->
_ => unreachable!(), _ => unreachable!(),
} }
} }
} // We guarantee utf8.
let buf = String::from_utf8(buf).unwrap();
tmpdir.push(buf); tmpdir.push(buf);
println!("{}", tmpdir.display()); println_verbatim(tmpdir).map_err_context(|| "failed to print directory name".to_owned())
Ok(())
} }
fn exec(dir: PathBuf, prefix: &str, rand: usize, suffix: &str, make_dir: bool) -> UResult<()> { fn exec(dir: PathBuf, prefix: &str, rand: usize, suffix: &str, make_dir: bool) -> UResult<()> {
@ -274,6 +277,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()))? .map_err(|e| MkTempError::PersistError(e.file.path().to_path_buf()))?
.1 .1
}; };
println!("{}", path.display()); println_verbatim(path).map_err_context(|| "failed to print directory name".to_owned())
Ok(())
} }

View file

@ -30,6 +30,7 @@ use crossterm::{
use unicode_segmentation::UnicodeSegmentation; use unicode_segmentation::UnicodeSegmentation;
use unicode_width::UnicodeWidthStr; use unicode_width::UnicodeWidthStr;
use uucore::display::Quotable;
const BELL: &str = "\x07"; const BELL: &str = "\x07";
@ -64,12 +65,12 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
let file = Path::new(file); let file = Path::new(file);
if file.is_dir() { if file.is_dir() {
terminal::disable_raw_mode().unwrap(); terminal::disable_raw_mode().unwrap();
show_usage_error!("'{}' is a directory.", file.display()); show_usage_error!("{} is a directory.", file.quote());
return 1; return 1;
} }
if !file.exists() { if !file.exists() {
terminal::disable_raw_mode().unwrap(); 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; return 1;
} }
if length > 1 { if length > 1 {

View file

@ -21,6 +21,7 @@ use std::os::unix;
use std::os::windows; use std::os::windows;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use uucore::backup_control::{self, BackupMode}; use uucore::backup_control::{self, BackupMode};
use uucore::display::Quotable;
use fs_extra::dir::{move_dir, CopyOptions as DirCopyOptions}; 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 // `Ok()` results unless the source does not exist, or the user
// lacks permission to access metadata. // lacks permission to access metadata.
if source.symlink_metadata().is_err() { if source.symlink_metadata().is_err() {
show_error!( show_error!("cannot stat {}: No such file or directory", source.quote());
"cannot stat '{}': No such file or directory",
source.display()
);
return 1; return 1;
} }
@ -234,8 +232,8 @@ fn exec(files: &[PathBuf], b: Behavior) -> i32 {
if b.no_target_dir { if b.no_target_dir {
if !source.is_dir() { if !source.is_dir() {
show_error!( show_error!(
"cannot overwrite directory '{}' with non-directory", "cannot overwrite directory {} with non-directory",
target.display() target.quote()
); );
return 1; return 1;
} }
@ -243,9 +241,9 @@ fn exec(files: &[PathBuf], b: Behavior) -> i32 {
return match rename(source, target, &b) { return match rename(source, target, &b) {
Err(e) => { Err(e) => {
show_error!( show_error!(
"cannot move '{}' to '{}': {}", "cannot move {} to {}: {}",
source.display(), source.quote(),
target.display(), target.quote(),
e.to_string() e.to_string()
); );
1 1
@ -257,9 +255,9 @@ fn exec(files: &[PathBuf], b: Behavior) -> i32 {
return move_files_into_dir(&[source.clone()], target, &b); return move_files_into_dir(&[source.clone()], target, &b);
} else if target.exists() && source.is_dir() { } else if target.exists() && source.is_dir() {
show_error!( show_error!(
"cannot overwrite non-directory '{}' with directory '{}'", "cannot overwrite non-directory {} with directory {}",
target.display(), target.quote(),
source.display() source.quote()
); );
return 1; return 1;
} }
@ -272,9 +270,9 @@ fn exec(files: &[PathBuf], b: Behavior) -> i32 {
_ => { _ => {
if b.no_target_dir { if b.no_target_dir {
show_error!( show_error!(
"mv: extra operand '{}'\n\ "mv: extra operand {}\n\
Try '{} --help' for more information.", Try '{} --help' for more information.",
files[2].display(), files[2].quote(),
uucore::execution_phrase() uucore::execution_phrase()
); );
return 1; 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 { fn move_files_into_dir(files: &[PathBuf], target_dir: &Path, b: &Behavior) -> i32 {
if !target_dir.is_dir() { 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; 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), Some(name) => target_dir.join(name),
None => { None => {
show_error!( show_error!(
"cannot stat '{}': No such file or directory", "cannot stat {}: No such file or directory",
sourcepath.display() sourcepath.quote()
); );
all_successful = false; 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) { if let Err(e) = rename(sourcepath, &targetpath, b) {
show_error!( show_error!(
"cannot move '{}' to '{}': {}", "cannot move {} to {}: {}",
sourcepath.display(), sourcepath.quote(),
targetpath.display(), targetpath.quote(),
e.to_string() e.to_string()
); );
all_successful = false; all_successful = false;
@ -332,7 +330,7 @@ fn rename(from: &Path, to: &Path, b: &Behavior) -> io::Result<()> {
match b.overwrite { match b.overwrite {
OverwriteMode::NoClobber => return Ok(()), OverwriteMode::NoClobber => return Ok(()),
OverwriteMode::Interactive => { OverwriteMode::Interactive => {
println!("{}: overwrite '{}'? ", uucore::util_name(), to.display()); println!("{}: overwrite {}? ", uucore::util_name(), to.quote());
if !read_yes() { if !read_yes() {
return Ok(()); return Ok(());
} }
@ -365,9 +363,9 @@ fn rename(from: &Path, to: &Path, b: &Behavior) -> io::Result<()> {
rename_with_fallback(from, to)?; rename_with_fallback(from, to)?;
if b.verbose { if b.verbose {
print!("'{}' -> '{}'", from.display(), to.display()); print!("{} -> {}", from.quote(), to.quote());
match backup_path { match backup_path {
Some(path) => println!(" (backup: '{}')", path.display()), Some(path) => println!(" (backup: {})", path.quote()),
None => println!(), None => println!(),
} }
} }

View file

@ -19,6 +19,7 @@ use std::fs::{File, OpenOptions};
use std::io::Error; use std::io::Error;
use std::os::unix::prelude::*; use std::os::unix::prelude::*;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use uucore::display::Quotable;
use uucore::InvalidEncodingHandling; use uucore::InvalidEncodingHandling;
static ABOUT: &str = "Run COMMAND ignoring hangup signals."; static ABOUT: &str = "Run COMMAND ignoring hangup signals.";
@ -122,13 +123,16 @@ fn find_stdout() -> File {
.open(Path::new(NOHUP_OUT)) .open(Path::new(NOHUP_OUT))
{ {
Ok(t) => { Ok(t) => {
show_error!("ignoring input and appending output to '{}'", NOHUP_OUT); show_error!(
"ignoring input and appending output to {}",
NOHUP_OUT.quote()
);
t t
} }
Err(e1) => { Err(e1) => {
let home = match env::var("HOME") { let home = match env::var("HOME") {
Err(_) => { Err(_) => {
show_error!("failed to open '{}': {}", NOHUP_OUT, e1); show_error!("failed to open {}: {}", NOHUP_OUT.quote(), e1);
exit!(internal_failure_code) exit!(internal_failure_code)
} }
Ok(h) => h, Ok(h) => h,
@ -143,12 +147,15 @@ fn find_stdout() -> File {
.open(&homeout) .open(&homeout)
{ {
Ok(t) => { Ok(t) => {
show_error!("ignoring input and appending output to '{}'", homeout_str); show_error!(
"ignoring input and appending output to {}",
homeout_str.quote()
);
t t
} }
Err(e2) => { Err(e2) => {
show_error!("failed to open '{}': {}", NOHUP_OUT, e1); show_error!("failed to open {}: {}", NOHUP_OUT.quote(), e1);
show_error!("failed to open '{}': {}", homeout_str, e2); show_error!("failed to open {}: {}", homeout_str.quote(), e2);
exit!(internal_failure_code) exit!(internal_failure_code)
} }
} }

View file

@ -1,3 +1,5 @@
use uucore::display::Quotable;
use crate::options::{NumfmtOptions, RoundMethod}; use crate::options::{NumfmtOptions, RoundMethod};
use crate::units::{DisplayableSuffix, RawSuffix, Result, Suffix, Unit, IEC_BASES, SI_BASES}; use crate::units::{DisplayableSuffix, RawSuffix, Result, Suffix, Unit, IEC_BASES, SI_BASES};
@ -78,7 +80,7 @@ fn parse_suffix(s: &str) -> Result<(f64, Option<Suffix>)> {
Some('Z') => Some((RawSuffix::Z, with_i)), Some('Z') => Some((RawSuffix::Z, with_i)),
Some('Y') => Some((RawSuffix::Y, with_i)), Some('Y') => Some((RawSuffix::Y, with_i)),
Some('0'..='9') => None, 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 { let suffix_len = match suffix {
@ -89,7 +91,7 @@ fn parse_suffix(s: &str) -> Result<(f64, Option<Suffix>)> {
let number = s[..s.len() - suffix_len] let number = s[..s.len() - suffix_len]
.parse::<f64>() .parse::<f64>()
.map_err(|_| format!("invalid number: '{}'", s))?; .map_err(|_| format!("invalid number: {}", s.quote()))?;
Ok((number, suffix)) Ok((number, suffix))
} }

View file

@ -15,6 +15,7 @@ use crate::options::*;
use crate::units::{Result, Unit}; use crate::units::{Result, Unit};
use clap::{crate_version, App, AppSettings, Arg, ArgMatches}; use clap::{crate_version, App, AppSettings, Arg, ArgMatches};
use std::io::{BufRead, Write}; use std::io::{BufRead, Write};
use uucore::display::Quotable;
use uucore::ranges::Range; use uucore::ranges::Range;
pub mod format; pub mod format;
@ -113,7 +114,7 @@ fn parse_options(args: &ArgMatches) -> Result<NumfmtOptions> {
0 => Err(value), 0 => Err(value),
_ => Ok(n), _ => Ok(n),
}) })
.map_err(|value| format!("invalid header value '{}'", value)) .map_err(|value| format!("invalid header value {}", value.quote()))
} }
}?; }?;

View file

@ -5,6 +5,8 @@ use std::io;
use std::io::BufReader; use std::io::BufReader;
use std::vec::Vec; use std::vec::Vec;
use uucore::display::Quotable;
pub enum InputSource<'a> { pub enum InputSource<'a> {
FileName(&'a str), FileName(&'a str),
Stdin, Stdin,
@ -57,7 +59,7 @@ impl<'b> MultifileReader<'b> {
// print an error at the time that the file is needed, // print an error at the time that the file is needed,
// then move on the the next file. // then move on the the next file.
// This matches the behavior of the original `od` // This matches the behavior of the original `od`
eprintln!("{}: '{}': {}", uucore::util_name(), fname, e); show_error!("{}: {}", fname.maybe_quote(), e);
self.any_err = true self.any_err = true
} }
} }
@ -90,7 +92,7 @@ impl<'b> io::Read for MultifileReader<'b> {
Ok(0) => break, Ok(0) => break,
Ok(n) => n, Ok(n) => n,
Err(e) => { Err(e) => {
eprintln!("{}: I/O: {}", uucore::util_name(), e); show_error!("I/O: {}", e);
self.any_err = true; self.any_err = true;
break; break;
} }

View file

@ -43,6 +43,7 @@ use crate::partialreader::*;
use crate::peekreader::*; use crate::peekreader::*;
use crate::prn_char::format_ascii_dump; use crate::prn_char::format_ascii_dump;
use clap::{self, crate_version, AppSettings, Arg, ArgMatches}; use clap::{self, crate_version, AppSettings, Arg, ArgMatches};
use uucore::display::Quotable;
use uucore::parse_size::ParseSizeError; use uucore::parse_size::ParseSizeError;
use uucore::InvalidEncodingHandling; 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 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" // GNU's od does distinguish between "invalid (suffix in) argument"
match error { match error {
ParseSizeError::ParseFailure(_) => format!("invalid --{} argument '{}'", option, s), ParseSizeError::ParseFailure(_) => format!("invalid --{} argument {}", option, s.quote()),
ParseSizeError::SizeTooBig(_) => format!("--{} argument '{}' too large", option, s), ParseSizeError::SizeTooBig(_) => format!("--{} argument {} too large", option, s.quote()),
} }
} }

View file

@ -1,5 +1,7 @@
// spell-checker:ignore formatteriteminfo docopt fvox fvoxw vals acdx // spell-checker:ignore formatteriteminfo docopt fvox fvoxw vals acdx
use uucore::display::Quotable;
use crate::formatteriteminfo::FormatterItemInfo; use crate::formatteriteminfo::FormatterItemInfo;
use crate::prn_char::*; use crate::prn_char::*;
use crate::prn_float::*; use crate::prn_float::*;
@ -272,8 +274,9 @@ fn parse_type_string(params: &str) -> Result<Vec<ParsedFormatterItemInfo>, Strin
while let Some(type_char) = ch { while let Some(type_char) = ch {
let type_char = format_type(type_char).ok_or_else(|| { let type_char = format_type(type_char).ok_or_else(|| {
format!( format!(
"unexpected char '{}' in format specification '{}'", "unexpected char '{}' in format specification {}",
type_char, params type_char,
params.quote()
) )
})?; })?;
@ -293,8 +296,9 @@ fn parse_type_string(params: &str) -> Result<Vec<ParsedFormatterItemInfo>, Strin
if !decimal_size.is_empty() { if !decimal_size.is_empty() {
byte_size = decimal_size.parse().map_err(|_| { byte_size = decimal_size.parse().map_err(|_| {
format!( format!(
"invalid number '{}' in format specification '{}'", "invalid number {} in format specification {}",
decimal_size, params decimal_size.quote(),
params.quote()
) )
})?; })?;
} }
@ -305,8 +309,9 @@ fn parse_type_string(params: &str) -> Result<Vec<ParsedFormatterItemInfo>, Strin
let ft = od_format_type(type_char, byte_size).ok_or_else(|| { let ft = od_format_type(type_char, byte_size).ok_or_else(|| {
format!( format!(
"invalid size '{}' in format specification '{}'", "invalid size '{}' in format specification {}",
byte_size, params byte_size,
params.quote()
) )
})?; })?;
formats.push(ParsedFormatterItemInfo::new(ft, show_ascii_dump)); formats.push(ParsedFormatterItemInfo::new(ft, show_ascii_dump));

View file

@ -15,6 +15,7 @@ extern crate uucore;
use clap::{crate_version, App, Arg}; use clap::{crate_version, App, Arg};
use std::fs; use std::fs;
use std::io::{ErrorKind, Write}; use std::io::{ErrorKind, Write};
use uucore::display::Quotable;
use uucore::InvalidEncodingHandling; use uucore::InvalidEncodingHandling;
// operating mode // operating mode
@ -153,10 +154,10 @@ fn check_basic(path: &[String]) -> bool {
if component_len > POSIX_NAME_MAX { if component_len > POSIX_NAME_MAX {
writeln!( writeln!(
&mut std::io::stderr(), &mut std::io::stderr(),
"limit {} exceeded by length {} of file name component '{}'", "limit {} exceeded by length {} of file name component {}",
POSIX_NAME_MAX, POSIX_NAME_MAX,
component_len, component_len,
p p.quote()
); );
return false; return false;
} }
@ -175,8 +176,8 @@ fn check_extra(path: &[String]) -> bool {
if p.starts_with('-') { if p.starts_with('-') {
writeln!( writeln!(
&mut std::io::stderr(), &mut std::io::stderr(),
"leading hyphen in file name component '{}'", "leading hyphen in file name component {}",
p p.quote()
); );
return false; return false;
} }
@ -197,10 +198,10 @@ fn check_default(path: &[String]) -> bool {
if total_len > libc::PATH_MAX as usize { if total_len > libc::PATH_MAX as usize {
writeln!( writeln!(
&mut std::io::stderr(), &mut std::io::stderr(),
"limit {} exceeded by length {} of file name '{}'", "limit {} exceeded by length {} of file name {}",
libc::PATH_MAX, libc::PATH_MAX,
total_len, total_len,
joined_path joined_path.quote()
); );
return false; return false;
} }
@ -210,10 +211,10 @@ fn check_default(path: &[String]) -> bool {
if component_len > libc::FILENAME_MAX as usize { if component_len > libc::FILENAME_MAX as usize {
writeln!( writeln!(
&mut std::io::stderr(), &mut std::io::stderr(),
"limit {} exceeded by length {} of file name component '{}'", "limit {} exceeded by length {} of file name component {}",
libc::FILENAME_MAX, libc::FILENAME_MAX,
component_len, component_len,
p p.quote()
); );
return false; return false;
} }
@ -246,9 +247,9 @@ fn check_portable_chars(path_segment: &str) -> bool {
let invalid = path_segment[i..].chars().next().unwrap(); let invalid = path_segment[i..].chars().next().unwrap();
writeln!( writeln!(
&mut std::io::stderr(), &mut std::io::stderr(),
"nonportable character '{}' in file name component '{}'", "nonportable character '{}' in file name component {}",
invalid, invalid,
path_segment path_segment.quote()
); );
return false; return false;
} }

View file

@ -24,6 +24,8 @@ use std::io::{stdin, stdout, BufRead, BufReader, Lines, Read, Stdout, Write};
#[cfg(unix)] #[cfg(unix)]
use std::os::unix::fs::FileTypeExt; use std::os::unix::fs::FileTypeExt;
use uucore::display::Quotable;
type IOError = std::io::Error; type IOError = std::io::Error;
const NAME: &str = "pr"; const NAME: &str = "pr";
@ -517,7 +519,7 @@ fn parse_usize(matches: &Matches, opt: &str) -> Option<Result<usize, PrError>> {
let i = value_to_parse.0; let i = value_to_parse.0;
let option = value_to_parse.1; let option = value_to_parse.1;
i.parse().map_err(|_e| { i.parse().map_err(|_e| {
PrError::EncounteredErrors(format!("invalid {} argument '{}'", option, i)) PrError::EncounteredErrors(format!("invalid {} argument {}", option, i.quote()))
}) })
}; };
matches matches
@ -619,7 +621,7 @@ fn build_options(
let unparsed_num = i.get(1).unwrap().as_str().trim(); let unparsed_num = i.get(1).unwrap().as_str().trim();
let x: Vec<_> = unparsed_num.split(':').collect(); let x: Vec<_> = unparsed_num.split(':').collect();
x[0].to_string().parse::<usize>().map_err(|_e| { x[0].to_string().parse::<usize>().map_err(|_e| {
PrError::EncounteredErrors(format!("invalid {} argument '{}'", "+", unparsed_num)) PrError::EncounteredErrors(format!("invalid {} argument {}", "+", unparsed_num.quote()))
}) })
}) { }) {
Some(res) => res?, Some(res) => res?,
@ -633,7 +635,11 @@ fn build_options(
.map(|unparsed_num| { .map(|unparsed_num| {
let x: Vec<_> = unparsed_num.split(':').collect(); let x: Vec<_> = unparsed_num.split(':').collect();
x[1].to_string().parse::<usize>().map_err(|_e| { x[1].to_string().parse::<usize>().map_err(|_e| {
PrError::EncounteredErrors(format!("invalid {} argument '{}'", "+", unparsed_num)) PrError::EncounteredErrors(format!(
"invalid {} argument {}",
"+",
unparsed_num.quote()
))
}) })
}) { }) {
Some(res) => Some(res?), Some(res) => Some(res?),
@ -643,7 +649,10 @@ fn build_options(
let invalid_pages_map = |i: String| { let invalid_pages_map = |i: String| {
let unparsed_value = matches.opt_str(options::PAGE_RANGE_OPTION).unwrap(); let unparsed_value = matches.opt_str(options::PAGE_RANGE_OPTION).unwrap();
i.parse::<usize>().map_err(|_e| { i.parse::<usize>().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 start_column_option = match re_col.captures(&free_args).map(|i| {
let unparsed_num = i.get(1).unwrap().as_str().trim(); let unparsed_num = i.get(1).unwrap().as_str().trim();
unparsed_num.parse::<usize>().map_err(|_e| { unparsed_num.parse::<usize>().map_err(|_e| {
PrError::EncounteredErrors(format!("invalid {} argument '{}'", "-", unparsed_num)) PrError::EncounteredErrors(format!("invalid {} argument {}", "-", unparsed_num.quote()))
}) })
}) { }) {
Some(res) => Some(res?), Some(res) => Some(res?),

View file

@ -2,20 +2,11 @@
// spell-checker:ignore (ToDO) bslice // spell-checker:ignore (ToDO) bslice
use std::env; use std::io::{stdout, Write};
use std::io::{stderr, stdout, Write};
pub const EXIT_OK: i32 = 0; pub const EXIT_OK: i32 = 0;
pub const EXIT_ERR: i32 = 1; 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 // by default stdout only flushes
// to console when a newline is passed. // to console when a newline is passed.
pub fn flush_char(c: char) { pub fn flush_char(c: char) {

View file

@ -8,8 +8,9 @@
use itertools::put_back_n; use itertools::put_back_n;
use std::iter::Peekable; use std::iter::Peekable;
use std::slice::Iter; use std::slice::Iter;
use uucore::display::Quotable;
use uucore::show_warning;
use crate::cli;
use crate::tokenize::sub::Sub; use crate::tokenize::sub::Sub;
use crate::tokenize::token::{Token, Tokenizer}; use crate::tokenize::token::{Token, Tokenizer};
use crate::tokenize::unescaped_text::UnescapedText; use crate::tokenize::unescaped_text::UnescapedText;
@ -19,10 +20,10 @@ pub struct Memo {
} }
fn warn_excess_args(first_arg: &str) { fn warn_excess_args(first_arg: &str) {
cli::err_msg(&format!( show_warning!(
"warning: ignoring excess arguments, starting with '{}'", "ignoring excess arguments, starting with {}",
first_arg first_arg.quote()
)); );
} }
impl Memo { impl Memo {

View file

@ -3,15 +3,15 @@
use itertools::{put_back_n, PutBackN}; use itertools::{put_back_n, PutBackN};
use std::str::Chars; use std::str::Chars;
use uucore::{display::Quotable, show_error};
use super::format_field::FormatField; use super::format_field::FormatField;
use crate::cli;
// contains the rough ingredients to final // contains the rough ingredients to final
// output for a number, organized together // output for a number, organized together
// to allow for easy generalization of output manipulation // to allow for easy generalization of output manipulation
// (e.g. max number of digits after decimal) // (e.g. max number of digits after decimal)
#[derive(Default)]
pub struct FormatPrimitive { pub struct FormatPrimitive {
pub prefix: Option<String>, pub prefix: Option<String>,
pub pre_decimal: Option<String>, pub pre_decimal: Option<String>,
@ -19,17 +19,6 @@ pub struct FormatPrimitive {
pub suffix: Option<String>, pub suffix: Option<String>,
} }
impl Default for FormatPrimitive {
fn default() -> FormatPrimitive {
FormatPrimitive {
prefix: None,
pre_decimal: None,
post_decimal: None,
suffix: None,
}
}
}
#[derive(Clone, PartialEq)] #[derive(Clone, PartialEq)]
pub enum Base { pub enum Base {
Ten = 10, Ten = 10,
@ -66,5 +55,5 @@ pub fn get_it_at(offset: usize, str_in: &str) -> PutBackN<Chars> {
// TODO: put this somewhere better // TODO: put this somewhere better
pub fn warn_incomplete_conv(pf_arg: &str) { pub fn warn_incomplete_conv(pf_arg: &str) {
// important: keep println here not print // 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());
} }

View file

@ -7,6 +7,9 @@
use std::env; use std::env;
use std::vec::Vec; use std::vec::Vec;
use uucore::display::Quotable;
use uucore::{show_error, show_warning};
use super::format_field::{FieldType, FormatField}; use super::format_field::{FieldType, FormatField};
use super::formatter::{Base, FormatPrimitive, Formatter, InitialPrefix}; use super::formatter::{Base, FormatPrimitive, Formatter, InitialPrefix};
use super::formatters::cninetyninehexfloatf::CninetyNineHexFloatf; use super::formatters::cninetyninehexfloatf::CninetyNineHexFloatf;
@ -15,11 +18,9 @@ use super::formatters::floatf::Floatf;
use super::formatters::intf::Intf; use super::formatters::intf::Intf;
use super::formatters::scif::Scif; use super::formatters::scif::Scif;
use crate::cli;
pub fn warn_expected_numeric(pf_arg: &str) { pub fn warn_expected_numeric(pf_arg: &str) {
// important: keep println here not print // 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 // when character constant arguments have excess characters
@ -29,11 +30,11 @@ fn warn_char_constant_ign(remaining_bytes: Vec<u8>) {
Ok(_) => {} Ok(_) => {}
Err(e) => { Err(e) => {
if let env::VarError::NotPresent = e { if let env::VarError::NotPresent = e {
cli::err_msg(&format!( show_warning!(
"warning: {:?}: character(s) following character \ "{:?}: character(s) following character \
constant have been ignored", constant have been ignored",
&*remaining_bytes &*remaining_bytes
)); );
} }
} }
} }

View file

@ -10,6 +10,7 @@ use std::iter::Peekable;
use std::process::exit; use std::process::exit;
use std::slice::Iter; use std::slice::Iter;
use std::str::Chars; use std::str::Chars;
use uucore::show_error;
// use std::collections::HashSet; // use std::collections::HashSet;
use super::num_format::format_field::{FieldType, FormatField}; use super::num_format::format_field::{FieldType, FormatField};
@ -19,7 +20,7 @@ use super::unescaped_text::UnescapedText;
use crate::cli; use crate::cli;
fn err_conv(sofar: &str) { fn err_conv(sofar: &str) {
cli::err_msg(&format!("%{}: invalid conversion specification", sofar)); show_error!("%{}: invalid conversion specification", sofar);
exit(cli::EXIT_ERR); exit(cli::EXIT_ERR);
} }

View file

@ -17,6 +17,7 @@ use std::collections::{BTreeSet, HashMap, HashSet};
use std::default::Default; use std::default::Default;
use std::fs::File; use std::fs::File;
use std::io::{stdin, stdout, BufRead, BufReader, BufWriter, Read, Write}; use std::io::{stdin, stdout, BufRead, BufReader, BufWriter, Read, Write};
use uucore::display::Quotable;
use uucore::InvalidEncodingHandling; use uucore::InvalidEncodingHandling;
static NAME: &str = "ptx"; 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 { fn get_reference(config: &Config, word_ref: &WordRef, line: &str, context_reg: &Regex) -> String {
if config.auto_ref { 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 { } else if config.input_ref {
let (beg, end) = match context_reg.find(line) { let (beg, end) = match context_reg.find(line) {
Some(x) => (x.start(), x.end()), Some(x) => (x.start(), x.end()),

View file

@ -14,6 +14,7 @@ use clap::{crate_version, App, Arg};
use std::fs; use std::fs;
use std::io::{stdout, Write}; use std::io::{stdout, Write};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use uucore::display::Quotable;
use uucore::fs::{canonicalize, MissingHandling, ResolveMode}; use uucore::fs::{canonicalize, MissingHandling, ResolveMode};
const ABOUT: &str = "Print value of a symbolic link or canonical file name."; 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 { if no_newline && files.len() > 1 && !silent {
eprintln!( show_error!("ignoring --no-newline with multiple arguments");
"{}: ignoring --no-newline with multiple arguments",
uucore::util_name()
);
no_newline = false; no_newline = false;
} }
@ -85,12 +83,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
Ok(path) => show(&path, no_newline, use_zero), Ok(path) => show(&path, no_newline, use_zero),
Err(err) => { Err(err) => {
if verbose { if verbose {
eprintln!( show_error!("{}: errno {}", f.maybe_quote(), err.raw_os_error().unwrap());
"{}: {}: errno {}",
uucore::util_name(),
f,
err.raw_os_error().unwrap()
);
} }
return 1; return 1;
} }
@ -100,12 +93,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
Ok(path) => show(&path, no_newline, use_zero), Ok(path) => show(&path, no_newline, use_zero),
Err(err) => { Err(err) => {
if verbose { if verbose {
eprintln!( show_error!("{}: errno {}", f.maybe_quote(), err.raw_os_error().unwrap());
"{}: {}: errno {:?}",
uucore::util_name(),
f,
err.raw_os_error().unwrap()
);
} }
return 1; return 1;
} }

View file

@ -11,8 +11,14 @@
extern crate uucore; extern crate uucore;
use clap::{crate_version, App, Arg}; use clap::{crate_version, App, Arg};
use std::path::{Path, PathBuf}; use std::{
use uucore::fs::{canonicalize, MissingHandling, ResolveMode}; io::{stdout, Write},
path::{Path, PathBuf},
};
use uucore::{
display::{print_verbatim, Quotable},
fs::{canonicalize, MissingHandling, ResolveMode},
};
static ABOUT: &str = "print the resolved path"; static ABOUT: &str = "print the resolved path";
@ -58,7 +64,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
for path in &paths { for path in &paths {
if let Err(e) = resolve_path(path, strip, zero, logical, can_mode) { if let Err(e) = resolve_path(path, strip, zero, logical, can_mode) {
if !quiet { if !quiet {
show_error!("{}: {}", e, path.display()); show_error!("{}: {}", path.maybe_quote(), e);
} }
retcode = 1 retcode = 1
}; };
@ -154,8 +160,9 @@ fn resolve_path(
ResolveMode::Physical ResolveMode::Physical
}; };
let abs = canonicalize(p, can_mode, resolve)?; 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(()) Ok(())
} }

View file

@ -10,6 +10,7 @@
use clap::{crate_version, App, Arg}; use clap::{crate_version, App, Arg};
use std::env; use std::env;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use uucore::display::println_verbatim;
use uucore::fs::{canonicalize, MissingHandling, ResolveMode}; use uucore::fs::{canonicalize, MissingHandling, ResolveMode};
use uucore::InvalidEncodingHandling; use uucore::InvalidEncodingHandling;
@ -48,7 +49,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
if !absto.as_path().starts_with(absbase.as_path()) if !absto.as_path().starts_with(absbase.as_path())
|| !absfrom.as_path().starts_with(absbase.as_path()) || !absfrom.as_path().starts_with(absbase.as_path())
{ {
println!("{}", absto.display()); println_verbatim(absto).unwrap();
return 0; return 0;
} }
} }
@ -74,7 +75,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
.map(|x| result.push(x.as_os_str())) .map(|x| result.push(x.as_os_str()))
.last(); .last();
println!("{}", result.display()); println_verbatim(result).unwrap();
0 0
} }

View file

@ -17,6 +17,7 @@ use std::fs;
use std::io::{stderr, stdin, BufRead, Write}; use std::io::{stderr, stdin, BufRead, Write};
use std::ops::BitOr; use std::ops::BitOr;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use uucore::display::Quotable;
use walkdir::{DirEntry, WalkDir}; use walkdir::{DirEntry, WalkDir};
#[derive(Eq, PartialEq, Clone, Copy)] #[derive(Eq, PartialEq, Clone, Copy)]
@ -236,7 +237,10 @@ fn remove(files: Vec<String>, options: Options) -> bool {
// (e.g., permission), even rm -f should fail with // (e.g., permission), even rm -f should fail with
// outputting the error, but there's no easy eay. // outputting the error, but there's no easy eay.
if !options.force { 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 true
} else { } else {
false false
@ -263,13 +267,9 @@ fn handle_dir(path: &Path, options: &Options) -> bool {
// GNU compatibility (rm/fail-eacces.sh) // GNU compatibility (rm/fail-eacces.sh)
// here, GNU doesn't use some kind of remove_dir_all // here, GNU doesn't use some kind of remove_dir_all
// It will show directory+file // It will show directory+file
show_error!( show_error!("cannot remove {}: {}", path.quote(), "Permission denied");
"cannot remove '{}': {}",
path.display(),
"Permission denied"
);
} else { } else {
show_error!("cannot remove '{}': {}", path.display(), e); show_error!("cannot remove {}: {}", path.quote(), e);
} }
} }
} else { } else {
@ -287,7 +287,7 @@ fn handle_dir(path: &Path, options: &Options) -> bool {
} }
Err(e) => { Err(e) => {
had_err = true; 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) { } else if options.dir && (!is_root || !options.preserve_root) {
had_err = remove_dir(path, options).bitor(had_err); had_err = remove_dir(path, options).bitor(had_err);
} else if options.recursive { } else if options.recursive {
show_error!("could not remove directory '{}'", path.display()); show_error!("could not remove directory {}", path.quote());
had_err = true; had_err = true;
} else { } else {
show_error!( show_error!(
"cannot remove '{}': Is a directory", // GNU's rm error message does not include help "cannot remove {}: Is a directory", // GNU's rm error message does not include help
path.display() path.quote()
); );
had_err = true; had_err = true;
} }
@ -325,36 +325,36 @@ fn remove_dir(path: &Path, options: &Options) -> bool {
match fs::remove_dir(path) { match fs::remove_dir(path) {
Ok(_) => { Ok(_) => {
if options.verbose { if options.verbose {
println!("removed directory '{}'", normalize(path).display()); println!("removed directory {}", normalize(path).quote());
} }
} }
Err(e) => { Err(e) => {
if e.kind() == std::io::ErrorKind::PermissionDenied { if e.kind() == std::io::ErrorKind::PermissionDenied {
// GNU compatibility (rm/fail-eacces.sh) // GNU compatibility (rm/fail-eacces.sh)
show_error!( show_error!(
"cannot remove '{}': {}", "cannot remove {}: {}",
path.display(), path.quote(),
"Permission denied" "Permission denied"
); );
} else { } else {
show_error!("cannot remove '{}': {}", path.display(), e); show_error!("cannot remove {}: {}", path.quote(), e);
} }
return true; return true;
} }
} }
} else { } else {
// directory can be read but is not empty // 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; return true;
} }
} else { } else {
// called to remove a symlink_dir (windows) without "-r"/"-R" or "-d" // 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; return true;
} }
} else { } else {
// GNU's rm shows this message if directory is empty but not readable // 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; return true;
} }
} }
@ -372,19 +372,15 @@ fn remove_file(path: &Path, options: &Options) -> bool {
match fs::remove_file(path) { match fs::remove_file(path) {
Ok(_) => { Ok(_) => {
if options.verbose { if options.verbose {
println!("removed '{}'", normalize(path).display()); println!("removed {}", normalize(path).quote());
} }
} }
Err(e) => { Err(e) => {
if e.kind() == std::io::ErrorKind::PermissionDenied { if e.kind() == std::io::ErrorKind::PermissionDenied {
// GNU compatibility (rm/fail-eacces.sh) // GNU compatibility (rm/fail-eacces.sh)
show_error!( show_error!("cannot remove {}: {}", path.quote(), "Permission denied");
"cannot remove '{}': {}",
path.display(),
"Permission denied"
);
} else { } else {
show_error!("cannot remove '{}': {}", path.display(), e); show_error!("cannot remove {}: {}", path.quote(), e);
} }
return true; return true;
} }
@ -396,9 +392,9 @@ fn remove_file(path: &Path, options: &Options) -> bool {
fn prompt_file(path: &Path, is_dir: bool) -> bool { fn prompt_file(path: &Path, is_dir: bool) -> bool {
if is_dir { if is_dir {
prompt(&(format!("rm: remove directory '{}'? ", path.display()))) prompt(&(format!("rm: remove directory {}? ", path.quote())))
} else { } else {
prompt(&(format!("rm: remove file '{}'? ", path.display()))) prompt(&(format!("rm: remove file {}? ", path.quote())))
} }
} }

View file

@ -3,6 +3,8 @@ use std::fmt::Write;
use std::io; use std::io;
use std::str::Utf8Error; use std::str::Utf8Error;
use uucore::display::Quotable;
pub(crate) type Result<T> = std::result::Result<T, Error>; pub(crate) type Result<T> = std::result::Result<T, Error>;
#[derive(thiserror::Error, Debug)] #[derive(thiserror::Error, Debug)]
@ -31,7 +33,7 @@ pub(crate) enum Error {
source: io::Error, source: io::Error,
}, },
#[error("{operation} failed on '{}'", .operand1.to_string_lossy())] #[error("{operation} failed on {}", .operand1.quote())]
Io1 { Io1 {
operation: &'static str, operation: &'static str,
operand1: OsString, operand1: OsString,

View file

@ -14,6 +14,7 @@ use num_traits::{Num, ToPrimitive};
use std::cmp; use std::cmp;
use std::io::{stdout, Write}; use std::io::{stdout, Write};
use std::str::FromStr; use std::str::FromStr;
use uucore::display::Quotable;
static ABOUT: &str = "Display numbers from FIRST to LAST, in steps of INCREMENT."; static ABOUT: &str = "Display numbers from FIRST to LAST, in steps of INCREMENT.";
static OPT_SEPARATOR: &str = "separator"; static OPT_SEPARATOR: &str = "separator";
@ -115,14 +116,14 @@ impl FromStr for Number {
} }
Err(_) => match s.parse::<f64>() { Err(_) => match s.parse::<f64>() {
Ok(value) if value.is_nan() => Err(format!( Ok(value) if value.is_nan() => Err(format!(
"invalid 'not-a-number' argument: '{}'\nTry '{} --help' for more information.", "invalid 'not-a-number' argument: {}\nTry '{} --help' for more information.",
s, s.quote(),
uucore::execution_phrase(), uucore::execution_phrase(),
)), )),
Ok(value) => Ok(Number::F64(value)), Ok(value) => Ok(Number::F64(value)),
Err(_) => Err(format!( Err(_) => Err(format!(
"invalid floating point argument: '{}'\nTry '{} --help' for more information.", "invalid floating point argument: {}\nTry '{} --help' for more information.",
s, s.quote(),
uucore::execution_phrase(), uucore::execution_phrase(),
)), )),
}, },

View file

@ -18,12 +18,12 @@ use std::io;
use std::io::prelude::*; use std::io::prelude::*;
use std::io::SeekFrom; use std::io::SeekFrom;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use uucore::InvalidEncodingHandling; use uucore::display::Quotable;
use uucore::{util_name, InvalidEncodingHandling};
#[macro_use] #[macro_use]
extern crate uucore; extern crate uucore;
static NAME: &str = "shred";
const BLOCK_SIZE: usize = 512; const BLOCK_SIZE: usize = 512;
const NAME_CHARSET: &[u8] = b"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_."; const NAME_CHARSET: &[u8] = b"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_.";
@ -281,7 +281,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
if !matches.is_present(options::FILE) { if !matches.is_present(options::FILE) {
show_error!("Missing an argument"); show_error!("Missing an argument");
show_error!("For help, try '{} --help'", NAME); show_error!("For help, try '{} --help'", uucore::execution_phrase());
return 0; return 0;
} }
@ -289,7 +289,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
Some(s) => match s.parse::<usize>() { Some(s) => match s.parse::<usize>() {
Ok(u) => u, Ok(u) => u,
Err(_) => { Err(_) => {
errs.push(format!("invalid number of passes: '{}'", s)); errs.push(format!("invalid number of passes: {}", s.quote()));
0 0
} }
}, },
@ -414,7 +414,11 @@ fn get_size(size_str_opt: Option<String>) -> Option<u64> {
let coefficient = match size_str.parse::<u64>() { let coefficient = match size_str.parse::<u64>() {
Ok(u) => u, Ok(u) => u,
Err(_) => { Err(_) => {
println!("{}: {}: Invalid file size", NAME, size_str_opt.unwrap()); println!(
"{}: {}: Invalid file size",
util_name(),
size_str_opt.unwrap().maybe_quote()
);
exit!(1); exit!(1);
} }
}; };
@ -452,11 +456,11 @@ fn wipe_file(
// Get these potential errors out of the way first // Get these potential errors out of the way first
let path: &Path = Path::new(path_str); let path: &Path = Path::new(path_str);
if !path.exists() { if !path.exists() {
show_error!("{}: No such file or directory", path.display()); show_error!("{}: No such file or directory", path.maybe_quote());
return; return;
} }
if !path.is_file() { if !path.is_file() {
show_error!("{}: Not a file", path.display()); show_error!("{}: Not a file", path.maybe_quote());
return; return;
} }
@ -520,7 +524,7 @@ fn wipe_file(
let mut file: File = match OpenOptions::new().write(true).truncate(false).open(path) { let mut file: File = match OpenOptions::new().write(true).truncate(false).open(path) {
Ok(f) => f, Ok(f) => f,
Err(e) => { Err(e) => {
show_error!("{}: failed to open for writing: {}", path.display(), e); show_error!("{}: failed to open for writing: {}", path.maybe_quote(), e);
return; return;
} }
}; };
@ -535,8 +539,8 @@ fn wipe_file(
if total_passes.to_string().len() == 1 { if total_passes.to_string().len() == 1 {
println!( println!(
"{}: {}: pass {}/{} ({})... ", "{}: {}: pass {}/{} ({})... ",
NAME, util_name(),
path.display(), path.maybe_quote(),
i + 1, i + 1,
total_passes, total_passes,
pass_name pass_name
@ -544,8 +548,8 @@ fn wipe_file(
} else { } else {
println!( println!(
"{}: {}: pass {:2.0}/{:2.0} ({})... ", "{}: {}: pass {:2.0}/{:2.0} ({})... ",
NAME, util_name(),
path.display(), path.maybe_quote(),
i + 1, i + 1,
total_passes, total_passes,
pass_name pass_name
@ -556,7 +560,7 @@ fn wipe_file(
match do_pass(&mut file, path, &mut generator, *pass_type, size) { match do_pass(&mut file, path, &mut generator, *pass_type, size) {
Ok(_) => {} Ok(_) => {}
Err(e) => { 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 // Ignore failed writes; just keep trying
@ -567,7 +571,7 @@ fn wipe_file(
match do_remove(path, path_str, verbose) { match do_remove(path, path_str, verbose) {
Ok(_) => {} Ok(_) => {}
Err(e) => { 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<PathBuf> {
if verbose { if verbose {
println!( println!(
"{}: {}: renamed to {}", "{}: {}: renamed to {}",
NAME, util_name(),
last_path.display(), last_path.maybe_quote(),
new_path.display() new_path.quote()
); );
} }
@ -641,9 +645,9 @@ fn wipe_name(orig_path: &Path, verbose: bool) -> Option<PathBuf> {
Err(e) => { Err(e) => {
println!( println!(
"{}: {}: Couldn't rename to {}: {}", "{}: {}: Couldn't rename to {}: {}",
NAME, util_name(),
last_path.display(), last_path.maybe_quote(),
new_path.display(), new_path.quote(),
e e
); );
return None; return None;
@ -657,7 +661,7 @@ fn wipe_name(orig_path: &Path, verbose: bool) -> Option<PathBuf> {
fn do_remove(path: &Path, orig_filename: &str, verbose: bool) -> Result<(), io::Error> { fn do_remove(path: &Path, orig_filename: &str, verbose: bool) -> Result<(), io::Error> {
if verbose { if verbose {
println!("{}: {}: removing", NAME, orig_filename); println!("{}: {}: removing", util_name(), orig_filename.maybe_quote());
} }
let renamed_path: Option<PathBuf> = wipe_name(path, verbose); let renamed_path: Option<PathBuf> = wipe_name(path, verbose);
@ -666,7 +670,7 @@ fn do_remove(path: &Path, orig_filename: &str, verbose: bool) -> Result<(), io::
} }
if verbose { if verbose {
println!("{}: {}: removed", NAME, orig_filename); println!("{}: {}: removed", util_name(), orig_filename.maybe_quote());
} }
Ok(()) Ok(())

View file

@ -14,6 +14,7 @@ use clap::{crate_version, App, Arg};
use rand::Rng; use rand::Rng;
use std::fs::File; use std::fs::File;
use std::io::{stdin, stdout, BufReader, BufWriter, Read, Write}; use std::io::{stdin, stdout, BufReader, BufWriter, Read, Write};
use uucore::display::Quotable;
use uucore::InvalidEncodingHandling; use uucore::InvalidEncodingHandling;
enum Mode { enum Mode {
@ -76,7 +77,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
Some(count) => match count.parse::<usize>() { Some(count) => match count.parse::<usize>() {
Ok(val) => val, Ok(val) => val,
Err(_) => { Err(_) => {
show_error!("invalid line count: '{}'", count); show_error!("invalid line count: {}", count.quote());
return 1; return 1;
} }
}, },
@ -185,13 +186,13 @@ fn read_input_file(filename: &str) -> Vec<u8> {
} else { } else {
match File::open(filename) { match File::open(filename) {
Ok(f) => Box::new(f) as Box<dyn Read>, Ok(f) => Box::new(f) as Box<dyn Read>,
Err(e) => crash!(1, "failed to open '{}': {}", filename, e), Err(e) => crash!(1, "failed to open {}: {}", filename.quote(), e),
} }
}); });
let mut data = Vec::new(); let mut data = Vec::new();
if let Err(e) = file.read_to_end(&mut data) { if let Err(e) = file.read_to_end(&mut data) {
crash!(1, "failed reading '{}': {}", filename, e) crash!(1, "failed reading {}: {}", filename.quote(), e)
}; };
data data
@ -235,7 +236,7 @@ fn shuf_bytes(input: &mut Vec<&[u8]>, opts: Options) {
None => Box::new(stdout()) as Box<dyn Write>, None => Box::new(stdout()) as Box<dyn Write>,
Some(s) => match File::create(&s[..]) { Some(s) => match File::create(&s[..]) {
Ok(f) => Box::new(f) as Box<dyn Write>, Ok(f) => Box::new(f) as Box<dyn Write>,
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( Some(r) => WrappedRng::RngFile(rand::rngs::adapter::ReadRng::new(
match File::open(&r[..]) { match File::open(&r[..]) {
Ok(f) => f, 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()), 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> { fn parse_range(input_range: &str) -> Result<(usize, usize), String> {
let split: Vec<&str> = input_range.split('-').collect(); let split: Vec<&str> = input_range.split('-').collect();
if split.len() != 2 { if split.len() != 2 {
Err(format!("invalid input range: '{}'", input_range)) Err(format!("invalid input range: {}", input_range.quote()))
} else { } else {
let begin = split[0] let begin = split[0]
.parse::<usize>() .parse::<usize>()
.map_err(|_| format!("invalid input range: '{}'", split[0]))?; .map_err(|_| format!("invalid input range: {}", split[0].quote()))?;
let end = split[1] let end = split[1]
.parse::<usize>() .parse::<usize>()
.map_err(|_| format!("invalid input range: '{}'", split[1]))?; .map_err(|_| format!("invalid input range: {}", split[1].quote()))?;
Ok((begin, end + 1)) Ok((begin, end + 1))
} }
} }

View file

@ -45,6 +45,7 @@ use std::path::Path;
use std::path::PathBuf; use std::path::PathBuf;
use std::str::Utf8Error; use std::str::Utf8Error;
use unicode_width::UnicodeWidthStr; use unicode_width::UnicodeWidthStr;
use uucore::display::Quotable;
use uucore::error::{set_exit_code, strip_errno, UError, UResult, USimpleError, UUsageError}; use uucore::error::{set_exit_code, strip_errno, UError, UResult, USimpleError, UUsageError};
use uucore::parse_size::{parse_size, ParseSizeError}; use uucore::parse_size::{parse_size, ParseSizeError};
use uucore::version_cmp::version_cmp; use uucore::version_cmp::version_cmp;
@ -139,7 +140,7 @@ enum SortError {
error: std::io::Error, error: std::io::Error,
}, },
ReadFailed { ReadFailed {
path: String, path: PathBuf,
error: std::io::Error, error: std::io::Error,
}, },
ParseKeyError { ParseKeyError {
@ -189,7 +190,7 @@ impl Display for SortError {
write!( write!(
f, f,
"{}:{}: disorder: {}", "{}:{}: disorder: {}",
file.to_string_lossy(), file.maybe_quote(),
line_number, line_number,
line line
) )
@ -198,13 +199,23 @@ impl Display for SortError {
} }
} }
SortError::OpenFailed { path, error } => { 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 } => { 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 } => { 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 } => { SortError::OpenTmpFileFailed { error } => {
write!(f, "failed to open temporary file: {}", strip_errno(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) write!(f, "couldn't execute compress program: errno {}", code)
} }
SortError::CompressProgTerminatedAbnormally { prog } => { SortError::CompressProgTerminatedAbnormally { prog } => {
write!(f, "'{}' terminated abnormally", prog) write!(f, "{} terminated abnormally", prog.quote())
} }
SortError::TmpDirCreationFailed => write!(f, "could not create temporary directory"), SortError::TmpDirCreationFailed => write!(f, "could not create temporary directory"),
SortError::Uft8Error { error } => write!(f, "{}", error), SortError::Uft8Error { error } => write!(f, "{}", error),
@ -756,19 +767,19 @@ impl KeyPosition {
let field = field_and_char let field = field_and_char
.next() .next()
.ok_or_else(|| format!("invalid key `{}`", key))?; .ok_or_else(|| format!("invalid key {}", key.quote()))?;
let char = field_and_char.next(); let char = field_and_char.next();
let field = field let field = field
.parse() .parse()
.map_err(|e| format!("failed to parse field index `{}`: {}", field, e))?; .map_err(|e| format!("failed to parse field index {}: {}", field.quote(), e))?;
if field == 0 { if field == 0 {
return Err("field index can not be 0".to_string()); return Err("field index can not be 0".to_string());
} }
let char = char.map_or(Ok(default_char_index), |char| { let char = char.map_or(Ok(default_char_index), |char| {
char.parse() char.parse()
.map_err(|e| format!("failed to parse character index `{}`: {}", char, e)) .map_err(|e| format!("failed to parse character index {}: {}", char.quote(), e))
})?; })?;
Ok(Self { Ok(Self {
@ -789,7 +800,7 @@ impl Default for KeyPosition {
} }
} }
#[derive(Clone, PartialEq, Debug)] #[derive(Clone, PartialEq, Debug, Default)]
struct FieldSelector { struct FieldSelector {
from: KeyPosition, from: KeyPosition,
to: Option<KeyPosition>, to: Option<KeyPosition>,
@ -801,18 +812,6 @@ struct FieldSelector {
needs_selection: bool, needs_selection: bool,
} }
impl Default for FieldSelector {
fn default() -> Self {
Self {
from: Default::default(),
to: None,
settings: Default::default(),
needs_tokens: false,
needs_selection: false,
}
}
}
impl FieldSelector { impl FieldSelector {
/// Splits this position into the actual position and the attached options. /// Splits this position into the actual position and the attached options.
fn split_key_options(position: &str) -> (&str, &str) { fn split_key_options(position: &str) -> (&str, &str) {
@ -879,7 +878,7 @@ impl FieldSelector {
'R' => key_settings.set_sort_mode(SortMode::Random)?, 'R' => key_settings.set_sort_mode(SortMode::Random)?,
'r' => key_settings.reverse = true, 'r' => key_settings.reverse = true,
'V' => key_settings.set_sort_mode(SortMode::Version)?, 'V' => key_settings.set_sort_mode(SortMode::Version)?,
c => return Err(format!("invalid option: `{}`", c)), c => return Err(format!("invalid option: '{}'", c)),
} }
} }
Ok(ignore_blanks) Ok(ignore_blanks)
@ -1179,7 +1178,10 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
if let Some(n_merge) = matches.value_of(options::BATCH_SIZE) { if let Some(n_merge) = matches.value_of(options::BATCH_SIZE) {
settings.merge_batch_size = n_merge.parse().map_err(|_| { 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 +1213,30 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
} else if settings.check && files.len() != 1 { } else if settings.check && files.len() != 1 {
return Err(UUsageError::new( return Err(UUsageError::new(
2, 2,
format!( format!("extra operand {} not allowed with -c", files[1].quote()),
"extra operand `{}' not allowed with -c",
files[1].to_string_lossy()
),
)); ));
} }
if let Some(arg) = matches.args.get(options::SEPARATOR) { if let Some(arg) = matches.args.get(options::SEPARATOR) {
let separator = arg.vals[0].to_string_lossy(); let mut separator = arg.vals[0].to_str().ok_or_else(|| {
let mut separator = separator.as_ref(); UUsageError::new(
2,
format!("separator is not valid unicode: {}", arg.vals[0].quote()),
)
})?;
if separator == "\\0" { if separator == "\\0" {
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 { if separator.len() != 1 {
return Err(UUsageError::new( return Err(UUsageError::new(
2, 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()) settings.separator = Some(separator.chars().next().unwrap())
@ -1816,7 +1825,7 @@ fn open(path: impl AsRef<OsStr>) -> UResult<Box<dyn Read + Send>> {
match File::open(path) { match File::open(path) {
Ok(f) => Ok(Box::new(f) as Box<dyn Read + Send>), Ok(f) => Ok(Box::new(f) as Box<dyn Read + Send>),
Err(error) => Err(SortError::ReadFailed { Err(error) => Err(SortError::ReadFailed {
path: path.to_string_lossy().to_string(), path: path.to_owned(),
error, error,
} }
.into()), .into()),
@ -1828,8 +1837,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 echos affected flag, -S or --buffer-size, depending user's selection
// GNU's sort does distinguish between "invalid (suffix in) argument" // GNU's sort does distinguish between "invalid (suffix in) argument"
match error { match error {
ParseSizeError::ParseFailure(_) => format!("invalid --{} argument '{}'", option, s), ParseSizeError::ParseFailure(_) => format!("invalid --{} argument {}", option, s.quote()),
ParseSizeError::SizeTooBig(_) => format!("--{} argument '{}' too large", option, s), ParseSizeError::SizeTooBig(_) => format!("--{} argument {} too large", option, s.quote()),
} }
} }

View file

@ -19,6 +19,7 @@ use std::fs::File;
use std::io::{stdin, BufRead, BufReader, BufWriter, Read, Write}; use std::io::{stdin, BufRead, BufReader, BufWriter, Read, Write};
use std::path::Path; use std::path::Path;
use std::{char, fs::remove_file}; use std::{char, fs::remove_file};
use uucore::display::Quotable;
use uucore::parse_size::parse_size; use uucore::parse_size::parse_size;
static OPT_BYTES: &str = "bytes"; static OPT_BYTES: &str = "bytes";
@ -238,7 +239,11 @@ impl LineSplitter {
fn new(settings: &Settings) -> LineSplitter { fn new(settings: &Settings) -> LineSplitter {
LineSplitter { LineSplitter {
lines_per_split: settings.strategy_param.parse().unwrap_or_else(|_| { 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(|_| { let r = File::open(Path::new(&settings.input)).unwrap_or_else(|_| {
crash!( crash!(
1, 1,
"cannot open '{}' for reading: No such file or directory", "cannot open {} for reading: No such file or directory",
settings.input settings.input.quote()
) )
}); });
Box::new(r) as Box<dyn Read> Box::new(r) as Box<dyn Read>
@ -383,7 +388,7 @@ fn split(settings: &Settings) -> i32 {
let mut splitter: Box<dyn Splitter> = match settings.strategy.as_str() { let mut splitter: Box<dyn Splitter> = match settings.strategy.as_str() {
s if s == OPT_LINES => Box::new(LineSplitter::new(settings)), s if s == OPT_LINES => Box::new(LineSplitter::new(settings)),
s if (s == OPT_BYTES || s == OPT_LINE_BYTES) => Box::new(ByteSplitter::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; let mut fileno = 0;

View file

@ -7,6 +7,7 @@
#[macro_use] #[macro_use]
extern crate uucore; extern crate uucore;
use uucore::display::Quotable;
use uucore::entries; use uucore::entries;
use uucore::fs::display_permissions; use uucore::fs::display_permissions;
use uucore::fsext::{ use uucore::fsext::{
@ -24,7 +25,7 @@ use std::{cmp, fs, iter};
macro_rules! check_bound { macro_rules! check_bound {
($str: ident, $bound:expr, $beg: expr, $end: expr) => { ($str: ident, $bound:expr, $beg: expr, $end: expr) => {
if $end >= $bound { 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; return 1;
} }
}; };
arg = format!( arg = format!("{} -> {}", file.quote(), dst.quote());
"`{}' -> `{}'",
file,
dst.to_string_lossy()
);
} else { } else {
arg = file.to_string(); arg = file.to_string();
} }
@ -750,7 +747,7 @@ impl Stater {
} }
} }
Err(e) => { Err(e) => {
show_error!("cannot stat '{}': {}", file, e); show_error!("cannot stat {}: {}", file.quote(), e);
return 1; return 1;
} }
} }
@ -843,7 +840,11 @@ impl Stater {
} }
} }
Err(e) => { 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; return 1;
} }
} }

View file

@ -14,6 +14,7 @@ use clap::{crate_version, App, Arg};
use std::fs::File; use std::fs::File;
use std::io::{stdin, Read, Result}; use std::io::{stdin, Read, Result};
use std::path::Path; use std::path::Path;
use uucore::display::Quotable;
use uucore::InvalidEncodingHandling; use uucore::InvalidEncodingHandling;
static NAME: &str = "sum"; static NAME: &str = "sum";
@ -118,7 +119,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
let reader = match open(file) { let reader = match open(file) {
Ok(f) => f, Ok(f) => f,
Err(error) => { Err(error) => {
show_error!("'{}' {}", file, error); show_error!("{}: {}", file.maybe_quote(), error);
exit_code = 2; exit_code = 2;
continue; continue;
} }

View file

@ -14,6 +14,7 @@ extern crate uucore;
use clap::{crate_version, App, Arg}; use clap::{crate_version, App, Arg};
use std::path::Path; use std::path::Path;
use uucore::display::Quotable;
static EXIT_ERR: i32 = 1; static EXIT_ERR: i32 = 1;
@ -175,7 +176,11 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
for f in &files { for f in &files {
if !Path::new(&f).exists() { 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()
);
} }
} }

View file

@ -14,6 +14,7 @@ use clap::{crate_version, App, Arg};
use memchr::memmem; use memchr::memmem;
use std::io::{stdin, stdout, BufReader, Read, Write}; use std::io::{stdin, stdout, BufReader, Read, Write};
use std::{fs::File, path::Path}; use std::{fs::File, path::Path};
use uucore::display::Quotable;
use uucore::InvalidEncodingHandling; use uucore::InvalidEncodingHandling;
static NAME: &str = "tac"; static NAME: &str = "tac";
@ -141,11 +142,11 @@ fn tac(filenames: Vec<String>, before: bool, _: bool, separator: &str) -> i32 {
let path = Path::new(filename); let path = Path::new(filename);
if path.is_dir() || path.metadata().is_err() { if path.is_dir() || path.metadata().is_err() {
if path.is_dir() { if path.is_dir() {
show_error!("{}: read error: Invalid argument", filename); show_error!("{}: read error: Invalid argument", filename.maybe_quote());
} else { } else {
show_error!( show_error!(
"failed to open '{}' for reading: No such file or directory", "failed to open {} for reading: No such file or directory",
filename filename.quote()
); );
} }
exit_code = 1; exit_code = 1;
@ -154,7 +155,7 @@ fn tac(filenames: Vec<String>, before: bool, _: bool, separator: &str) -> i32 {
match File::open(path) { match File::open(path) {
Ok(f) => Box::new(f) as Box<dyn Read>, Ok(f) => Box::new(f) as Box<dyn Read>,
Err(e) => { Err(e) => {
show_error!("failed to open '{}' for reading: {}", filename, e); show_error!("failed to open {} for reading: {}", filename.quote(), e);
exit_code = 1; exit_code = 1;
continue; continue;
} }
@ -163,7 +164,7 @@ fn tac(filenames: Vec<String>, before: bool, _: bool, separator: &str) -> i32 {
let mut data = Vec::new(); let mut data = Vec::new();
if let Err(e) = file.read_to_end(&mut data) { 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; exit_code = 1;
continue; continue;
}; };

View file

@ -12,7 +12,8 @@ use clap::{crate_version, App, Arg};
use retain_mut::RetainMut; use retain_mut::RetainMut;
use std::fs::OpenOptions; use std::fs::OpenOptions;
use std::io::{copy, sink, stdin, stdout, Error, ErrorKind, Read, Result, Write}; 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)] #[cfg(unix)]
use uucore::libc; use uucore::libc;
@ -167,7 +168,7 @@ impl Write for MultiWriter {
let result = writer.write_all(buf); let result = writer.write_all(buf);
match result { match result {
Err(f) => { Err(f) => {
show_error!("{}: {}", writer.name, f.to_string()); show_error!("{}: {}", writer.name.maybe_quote(), f);
false false
} }
_ => true, _ => true,
@ -181,7 +182,7 @@ impl Write for MultiWriter {
let result = writer.flush(); let result = writer.flush();
match result { match result {
Err(f) => { Err(f) => {
show_error!("{}: {}", writer.name, f.to_string()); show_error!("{}: {}", writer.name.maybe_quote(), f);
false false
} }
_ => true, _ => true,
@ -214,7 +215,7 @@ impl Read for NamedReader {
fn read(&mut self, buf: &mut [u8]) -> Result<usize> { fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
match self.inner.read(buf) { match self.inner.read(buf) {
Err(f) => { Err(f) => {
show_error!("{}: {}", Path::new("stdin").display(), f.to_string()); show_error!("stdin: {}", f);
Err(f) Err(f)
} }
okay => okay, okay => okay,

View file

@ -10,6 +10,9 @@
use std::ffi::OsString; use std::ffi::OsString;
use std::iter::Peekable; use std::iter::Peekable;
use uucore::display::Quotable;
use uucore::show_error;
/// Represents one of the binary comparison operators for strings, integers, or files /// Represents one of the binary comparison operators for strings, integers, or files
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum Operator { pub enum Operator {
@ -43,7 +46,8 @@ impl Symbol {
/// Returns Symbol::None in place of None /// Returns Symbol::None in place of None
fn new(token: Option<OsString>) -> Symbol { fn new(token: Option<OsString>) -> Symbol {
match token { match token {
Some(s) => match s.to_string_lossy().as_ref() { Some(s) => match s.to_str() {
Some(t) => match t {
"(" => Symbol::LParen, "(" => Symbol::LParen,
"!" => Symbol::Bang, "!" => Symbol::Bang,
"-a" | "-o" => Symbol::BoolOp(s), "-a" | "-o" => Symbol::BoolOp(s),
@ -57,6 +61,8 @@ impl Symbol {
} }
_ => Symbol::Literal(s), _ => Symbol::Literal(s),
}, },
None => Symbol::Literal(s),
},
None => Symbol::None, None => Symbol::None,
} }
} }
@ -202,7 +208,7 @@ impl Parser {
// case 2: error if end of stream is `( <any_token>` // case 2: error if end of stream is `( <any_token>`
[symbol] => { [symbol] => {
eprintln!("test: missing argument after {:?}", symbol); show_error!("missing argument after {:?}", symbol);
std::process::exit(2); std::process::exit(2);
} }
@ -391,7 +397,7 @@ impl Parser {
self.expr(); self.expr();
match self.tokens.next() { match self.tokens.next() {
Some(token) => Err(format!("extra argument '{}'", token.to_string_lossy())), Some(token) => Err(format!("extra argument {}", token.quote())),
None => Ok(()), None => Ok(()),
} }
} }

View file

@ -13,7 +13,7 @@ mod parser;
use clap::{crate_version, App, AppSettings}; use clap::{crate_version, App, AppSettings};
use parser::{parse, Operator, Symbol, UnaryOperator}; use parser::{parse, Operator, Symbol, UnaryOperator};
use std::ffi::{OsStr, OsString}; use std::ffi::{OsStr, OsString};
use std::path::Path; use uucore::{display::Quotable, show_error};
const USAGE: &str = "test EXPRESSION const USAGE: &str = "test EXPRESSION
or: test or: test
@ -93,10 +93,7 @@ pub fn uu_app() -> App<'static, 'static> {
pub fn uumain(mut args: impl uucore::Args) -> i32 { pub fn uumain(mut args: impl uucore::Args) -> i32 {
let program = args.next().unwrap_or_else(|| OsString::from("test")); let program = args.next().unwrap_or_else(|| OsString::from("test"));
let binary_name = Path::new(&program) let binary_name = uucore::util_name();
.file_name()
.unwrap_or_else(|| OsStr::new("test"))
.to_string_lossy();
let mut args: Vec<_> = args.collect(); let mut args: Vec<_> = args.collect();
if binary_name.ends_with('[') { 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 // If invoked via name '[', matching ']' must be in the last arg
let last = args.pop(); let last = args.pop();
if last != Some(OsString::from("]")) { if last.as_deref() != Some(OsStr::new("]")) {
eprintln!("[: missing ']'"); show_error!("missing ']'");
return 2; return 2;
} }
} }
@ -133,7 +130,7 @@ pub fn uumain(mut args: impl uucore::Args) -> i32 {
} }
} }
Err(e) => { Err(e) => {
eprintln!("test: {}", e); show_error!("{}", e);
2 2
} }
} }
@ -190,11 +187,11 @@ fn eval(stack: &mut Vec<Symbol>) -> Result<bool, String> {
}) })
} }
Some(Symbol::UnaryOp(UnaryOperator::FiletestOp(op))) => { Some(Symbol::UnaryOp(UnaryOperator::FiletestOp(op))) => {
let op = op.to_string_lossy(); let op = op.to_str().unwrap();
let f = pop_literal!(); let f = pop_literal!();
Ok(match op.as_ref() { Ok(match op {
"-b" => path(&f, PathCondition::BlockSpecial), "-b" => path(&f, PathCondition::BlockSpecial),
"-c" => path(&f, PathCondition::CharacterSpecial), "-c" => path(&f, PathCondition::CharacterSpecial),
"-d" => path(&f, PathCondition::Directory), "-d" => path(&f, PathCondition::Directory),
@ -231,31 +228,33 @@ fn eval(stack: &mut Vec<Symbol>) -> Result<bool, String> {
} }
fn integers(a: &OsStr, b: &OsStr, op: &OsStr) -> Result<bool, String> { fn integers(a: &OsStr, b: &OsStr, op: &OsStr) -> Result<bool, String> {
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
let a: i64 = a.parse().map_err(|_| format_err(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
let b: i64 = b.parse().map_err(|_| format_err(b))?; .to_str()
.and_then(|s| s.parse().ok())
.ok_or_else(|| format_err(b))?;
let operator = op.to_string_lossy(); Ok(match op.to_str() {
Ok(match operator.as_ref() { Some("-eq") => a == b,
"-eq" => a == b, Some("-ne") => a != b,
"-ne" => a != b, Some("-gt") => a > b,
"-gt" => a > b, Some("-ge") => a >= b,
"-ge" => a >= b, Some("-lt") => a < b,
"-lt" => a < b, Some("-le") => a <= b,
"-le" => a <= b, _ => return Err(format!("unknown operator {}", op.quote())),
_ => return Err(format!("unknown operator '{}'", operator)),
}) })
} }
fn isatty(fd: &OsStr) -> Result<bool, String> { fn isatty(fd: &OsStr) -> Result<bool, String> {
let fd = fd.to_string_lossy(); fd.to_str()
.and_then(|s| s.parse().ok())
fd.parse() .ok_or_else(|| format!("invalid integer {}", fd.quote()))
.map_err(|_| format!("invalid integer '{}'", fd))
.map(|i| { .map(|i| {
#[cfg(not(target_os = "redox"))] #[cfg(not(target_os = "redox"))]
unsafe { unsafe {

View file

@ -16,6 +16,7 @@ use clap::{crate_version, App, AppSettings, Arg};
use std::io::ErrorKind; use std::io::ErrorKind;
use std::process::{Command, Stdio}; use std::process::{Command, Stdio};
use std::time::Duration; use std::time::Duration;
use uucore::display::Quotable;
use uucore::process::ChildExt; use uucore::process::ChildExt;
use uucore::signals::{signal_by_name_or_value, signal_name_by_value}; use uucore::signals::{signal_by_name_or_value, signal_name_by_value};
use uucore::InvalidEncodingHandling; use uucore::InvalidEncodingHandling;
@ -61,7 +62,7 @@ impl Config {
let signal_result = signal_by_name_or_value(signal_); let signal_result = signal_by_name_or_value(signal_);
match signal_result { match signal_result {
None => { None => {
unreachable!("invalid signal '{}'", signal_); unreachable!("invalid signal {}", signal_.quote());
} }
Some(signal_value) => signal_value, Some(signal_value) => signal_value,
} }
@ -216,9 +217,9 @@ fn timeout(
Ok(None) => { Ok(None) => {
if verbose { if verbose {
show_error!( show_error!(
"sending signal {} to command '{}'", "sending signal {} to command {}",
signal_name_by_value(signal).unwrap(), signal_name_by_value(signal).unwrap(),
cmd[0] cmd[0].quote()
); );
} }
crash_if_err!(ERR_EXIT_STATUS, process.send_signal(signal)); crash_if_err!(ERR_EXIT_STATUS, process.send_signal(signal));
@ -233,7 +234,7 @@ fn timeout(
} }
Ok(None) => { Ok(None) => {
if verbose { if verbose {
show_error!("sending signal KILL to command '{}'", cmd[0]); show_error!("sending signal KILL to command {}", cmd[0].quote());
} }
crash_if_err!( crash_if_err!(
ERR_EXIT_STATUS, ERR_EXIT_STATUS,

View file

@ -6,7 +6,7 @@
// For the full copyright and license information, please view the LICENSE file // For the full copyright and license information, please view the LICENSE file
// that was distributed with this source code. // that was distributed with this source code.
// spell-checker:ignore (ToDO) filetime strptime utcoff strs datetime MMDDhhmm // spell-checker:ignore (ToDO) filetime strptime utcoff strs datetime MMDDhhmm clapv
pub extern crate filetime; pub extern crate filetime;
@ -17,6 +17,7 @@ use clap::{crate_version, App, Arg, ArgGroup};
use filetime::*; use filetime::*;
use std::fs::{self, File}; use std::fs::{self, File};
use std::path::Path; use std::path::Path;
use uucore::display::Quotable;
use uucore::error::{FromIo, UError, UResult, USimpleError}; use uucore::error::{FromIo, UError, UResult, USimpleError};
static ABOUT: &str = "Update the access and modification times of each FILE to the current time."; 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) { 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; continue;
}; };
@ -122,7 +123,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
} else { } else {
filetime::set_file_times(path, atime, mtime) 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(()) Ok(())
@ -175,6 +176,7 @@ pub fn uu_app() -> App<'static, 'static> {
Arg::with_name(options::sources::REFERENCE) Arg::with_name(options::sources::REFERENCE)
.short("r") .short("r")
.long(options::sources::REFERENCE) .long(options::sources::REFERENCE)
.alias("ref") // clapv3
.help("use this file's times instead of the current time") .help("use this file's times instead of the current time")
.value_name("FILE"), .value_name("FILE"),
) )
@ -209,7 +211,7 @@ fn stat(path: &Path, follow: bool) -> UResult<(FileTime, FileTime)> {
} else { } else {
fs::metadata(path) 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(( Ok((
FileTime::from_last_access_time(&metadata), FileTime::from_last_access_time(&metadata),
@ -249,11 +251,16 @@ fn parse_timestamp(s: &str) -> UResult<FileTime> {
10 => ("%y%m%d%H%M", s.to_owned()), 10 => ("%y%m%d%H%M", s.to_owned()),
11 => ("%Y%m%d%H%M.%S", format!("{}{}", now.tm_year + 1900, s)), 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)), 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) 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); let mut local = to_local(tm);
local.tm_isdst = -1; local.tm_isdst = -1;
@ -269,7 +276,10 @@ fn parse_timestamp(s: &str) -> UResult<FileTime> {
}; };
let tm2 = time::at(ts); let tm2 = time::at(ts);
if tm.tm_hour != tm2.tm_hour { 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) Ok(ft)

View file

@ -21,7 +21,7 @@ use fnv::FnvHashMap;
use std::io::{stdin, stdout, BufRead, BufWriter, Write}; use std::io::{stdin, stdout, BufRead, BufWriter, Write};
use crate::expand::ExpandSet; use crate::expand::ExpandSet;
use uucore::InvalidEncodingHandling; use uucore::{display::Quotable, InvalidEncodingHandling};
static ABOUT: &str = "translate or delete characters"; 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 { if !(delete_flag || squeeze_flag) && sets.len() < 2 {
show_error!( show_error!(
"missing operand after '{}'\nTry '{} --help' for more information.", "missing operand after {}\nTry '{} --help' for more information.",
sets[0], sets[0].quote(),
uucore::execution_phrase() uucore::execution_phrase()
); );
return 1; return 1;

View file

@ -15,6 +15,7 @@ use std::convert::TryFrom;
use std::fs::{metadata, OpenOptions}; use std::fs::{metadata, OpenOptions};
use std::io::ErrorKind; use std::io::ErrorKind;
use std::path::Path; use std::path::Path;
use uucore::display::Quotable;
use uucore::parse_size::{parse_size, ParseSizeError}; use uucore::parse_size::{parse_size, ParseSizeError};
#[derive(Debug, Eq, PartialEq)] #[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); let reference = matches.value_of(options::REFERENCE).map(String::from);
crash!( crash!(
1, 1,
"cannot stat '{}': No such file or directory", "cannot stat {}: No such file or directory",
reference.unwrap_or_else(|| "".to_string()) reference.as_deref().unwrap_or("").quote()
); // TODO: fix '--no-create' see test_reference and test_truncate_bytes_size ); // TODO: fix '--no-create' see test_reference and test_truncate_bytes_size
} }
_ => crash!(1, "{}", e.to_string()), _ => crash!(1, "{}", e.to_string()),

View file

@ -14,6 +14,7 @@ use std::collections::{HashMap, HashSet};
use std::fs::File; use std::fs::File;
use std::io::{stdin, BufRead, BufReader, Read}; use std::io::{stdin, BufRead, BufReader, Read};
use std::path::Path; use std::path::Path;
use uucore::display::Quotable;
use uucore::InvalidEncodingHandling; use uucore::InvalidEncodingHandling;
static SUMMARY: &str = "Topological sort the strings in FILE. 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)) { file_buf = match File::open(Path::new(&input)) {
Ok(a) => a, Ok(a) => a,
_ => { _ => {
show_error!("{}: No such file or directory", input); show_error!("{}: No such file or directory", input.maybe_quote());
return 1; return 1;
} }
}; };
@ -68,7 +69,11 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
for ab in tokens.chunks(2) { for ab in tokens.chunks(2) {
match ab.len() { match ab.len() {
2 => g.add_edge(&ab[0], &ab[1]), 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()
),
} }
} }
} }

View file

@ -16,6 +16,7 @@ use std::fs::File;
use std::io::{stdin, stdout, BufRead, BufReader, BufWriter, Read, Stdout, Write}; use std::io::{stdin, stdout, BufRead, BufReader, BufWriter, Read, Stdout, Write};
use std::str::from_utf8; use std::str::from_utf8;
use unicode_width::UnicodeWidthChar; use unicode_width::UnicodeWidthChar;
use uucore::display::Quotable;
use uucore::InvalidEncodingHandling; use uucore::InvalidEncodingHandling;
static NAME: &str = "unexpand"; static NAME: &str = "unexpand";
@ -141,9 +142,9 @@ fn open(path: String) -> BufReader<Box<dyn Read + 'static>> {
if path == "-" { if path == "-" {
BufReader::new(Box::new(stdin()) as Box<dyn Read>) BufReader::new(Box::new(stdin()) as Box<dyn Read>)
} else { } else {
file_buf = match File::open(&path[..]) { file_buf = match File::open(&path) {
Ok(a) => a, 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<dyn Read>) BufReader::new(Box::new(file_buf) as Box<dyn Read>)
} }

View file

@ -14,6 +14,7 @@ use std::io::{stdin, stdout, BufRead, BufReader, BufWriter, Read, Result, Write}
use std::path::Path; use std::path::Path;
use std::str::FromStr; use std::str::FromStr;
use strum_macros::{AsRefStr, EnumString}; use strum_macros::{AsRefStr, EnumString};
use uucore::display::Quotable;
static ABOUT: &str = "Report or omit repeated lines."; static ABOUT: &str = "Report or omit repeated lines.";
pub mod options { pub mod options {
@ -217,7 +218,14 @@ fn get_line_string(io_line: Result<Vec<u8>>) -> String {
fn opt_parsed<T: FromStr>(opt_name: &str, matches: &ArgMatches) -> Option<T> { fn opt_parsed<T: FromStr>(opt_name: &str, matches: &ArgMatches) -> Option<T> {
matches.value_of(opt_name).map(|arg_str| { matches.value_of(opt_name).map(|arg_str| {
let opt_val: Option<T> = arg_str.parse().ok(); let opt_val: Option<T> = 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()
)
})
}) })
} }

View file

@ -16,7 +16,6 @@ path = "src/unlink.rs"
[dependencies] [dependencies]
clap = { version = "2.33", features = ["wrap_help"] } clap = { version = "2.33", features = ["wrap_help"] }
libc = "0.2.42"
uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } uucore = { version=">=0.0.9", package="uucore", path="../../uucore" }
uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" }

View file

@ -7,96 +7,32 @@
/* last synced with: unlink (GNU coreutils) 8.21 */ /* last synced with: unlink (GNU coreutils) 8.21 */
// spell-checker:ignore (ToDO) lstat IFLNK IFMT IFREG
#[macro_use] #[macro_use]
extern crate uucore; extern crate uucore;
use clap::{crate_version, App, Arg}; use std::fs::remove_file;
use libc::{lstat, stat, unlink}; use std::path::Path;
use libc::{S_IFLNK, S_IFMT, S_IFREG};
use std::ffi::CString;
use std::io::{Error, ErrorKind};
use uucore::InvalidEncodingHandling;
static ABOUT: &str = "Unlink the file at [FILE]."; use clap::{crate_version, App, Arg};
use uucore::display::Quotable;
use uucore::error::{FromIo, UResult};
static ABOUT: &str = "Unlink the file at FILE.";
static OPT_PATH: &str = "FILE"; static OPT_PATH: &str = "FILE";
fn usage() -> String { #[uucore_procs::gen_uumain]
format!("{} [OPTION]... FILE", uucore::execution_phrase()) pub fn uumain(args: impl uucore::Args) -> UResult<()> {
} let matches = uu_app().get_matches_from(args);
pub fn uumain(args: impl uucore::Args) -> i32 { let path: &Path = matches.value_of_os(OPT_PATH).unwrap().as_ref();
let args = args
.collect_str(InvalidEncodingHandling::ConvertLossy)
.accept_any();
let usage = usage(); remove_file(path).map_err_context(|| format!("cannot unlink {}", path.quote()))
let matches = uu_app().usage(&usage[..]).get_matches_from(args);
let paths: Vec<String> = matches
.values_of(OPT_PATH)
.map(|v| v.map(ToString::to_string).collect())
.unwrap_or_default();
if paths.is_empty() {
crash!(
1,
"missing operand\nTry '{0} --help' for more information.",
uucore::execution_phrase()
);
} else if paths.len() > 1 {
crash!(
1,
"extra operand: '{1}'\nTry '{0} --help' for more information.",
uucore::execution_phrase(),
paths[1]
);
}
let c_string = CString::new(paths[0].clone()).unwrap(); // unwrap() cannot fail, the string comes from argv so it cannot contain a \0.
let st_mode = {
#[allow(deprecated)]
let mut buf: stat = unsafe { std::mem::uninitialized() };
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());
}
buf.st_mode & S_IFMT
};
let result = if st_mode != S_IFREG && st_mode != S_IFLNK {
Err(Error::new(
ErrorKind::Other,
"Not a regular file or symlink",
))
} else {
let result = unsafe { unlink(c_string.as_ptr()) };
if result < 0 {
Err(Error::last_os_error())
} else {
Ok(())
}
};
match result {
Ok(_) => (),
Err(e) => {
crash!(1, "cannot unlink '{0}': {1}", paths[0], e);
}
}
0
} }
pub fn uu_app() -> App<'static, 'static> { pub fn uu_app() -> App<'static, 'static> {
App::new(uucore::util_name()) App::new(uucore::util_name())
.version(crate_version!()) .version(crate_version!())
.about(ABOUT) .about(ABOUT)
.arg(Arg::with_name(OPT_PATH).hidden(true).multiple(true)) .arg(Arg::with_name(OPT_PATH).required(true).hidden(true))
} }

View file

@ -17,6 +17,8 @@ extern crate uucore;
pub use uucore::libc; pub use uucore::libc;
use uucore::libc::time_t; use uucore::libc::time_t;
use uucore::error::{UResult, USimpleError};
static ABOUT: &str = "Display the current time, the length of time the system has been up,\n\ static ABOUT: &str = "Display the current time, the length of time the system has been up,\n\
the number of users on the system, and the average number of jobs\n\ the number of users on the system, and the average number of jobs\n\
in the run queue over the last 1, 5 and 15 minutes."; in the run queue over the last 1, 5 and 15 minutes.";
@ -36,21 +38,20 @@ fn usage() -> String {
format!("{0} [OPTION]...", uucore::execution_phrase()) format!("{0} [OPTION]...", uucore::execution_phrase())
} }
pub fn uumain(args: impl uucore::Args) -> i32 { #[uucore_procs::gen_uumain]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let usage = usage(); let usage = usage();
let matches = uu_app().usage(&usage[..]).get_matches_from(args); let matches = uu_app().usage(&usage[..]).get_matches_from(args);
let (boot_time, user_count) = process_utmpx(); let (boot_time, user_count) = process_utmpx();
let uptime = get_uptime(boot_time); let uptime = get_uptime(boot_time);
if uptime < 0 { if uptime < 0 {
show_error!("could not retrieve system uptime"); Err(USimpleError::new(1, "could not retrieve system uptime"))
1
} else { } else {
if matches.is_present(options::SINCE) { if matches.is_present(options::SINCE) {
let initial_date = Local.timestamp(Utc::now().timestamp() - uptime, 0); let initial_date = Local.timestamp(Utc::now().timestamp() - uptime, 0);
println!("{}", initial_date.format("%Y-%m-%d %H:%M:%S")); println!("{}", initial_date.format("%Y-%m-%d %H:%M:%S"));
return 0; return Ok(());
} }
print_time(); print_time();
@ -59,7 +60,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
print_nusers(user_count); print_nusers(user_count);
print_loadavg(); print_loadavg();
0 Ok(())
} }
} }

View file

@ -16,7 +16,7 @@ path = "src/wc.rs"
[dependencies] [dependencies]
clap = { version = "2.33", features = ["wrap_help"] } clap = { version = "2.33", features = ["wrap_help"] }
uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["pipes"] }
uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" }
bytecount = "0.6.2" bytecount = "0.6.2"
utf-8 = "0.7.6" utf-8 = "0.7.6"

View file

@ -3,7 +3,7 @@ use crate::word_count::WordCount;
use super::WordCountable; use super::WordCountable;
#[cfg(any(target_os = "linux", target_os = "android"))] #[cfg(any(target_os = "linux", target_os = "android"))]
use std::fs::{File, OpenOptions}; use std::fs::OpenOptions;
use std::io::{self, ErrorKind, Read}; use std::io::{self, ErrorKind, Read};
#[cfg(unix)] #[cfg(unix)]
@ -11,34 +11,17 @@ use libc::S_IFREG;
#[cfg(unix)] #[cfg(unix)]
use nix::sys::stat; use nix::sys::stat;
#[cfg(any(target_os = "linux", target_os = "android"))] #[cfg(any(target_os = "linux", target_os = "android"))]
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use std::os::unix::io::AsRawFd;
#[cfg(any(target_os = "linux", target_os = "android"))] #[cfg(any(target_os = "linux", target_os = "android"))]
use libc::S_IFIFO; use libc::S_IFIFO;
#[cfg(any(target_os = "linux", target_os = "android"))] #[cfg(any(target_os = "linux", target_os = "android"))]
use nix::fcntl::{splice, SpliceFFlags}; use uucore::pipes::{pipe, splice, splice_exact};
#[cfg(any(target_os = "linux", target_os = "android"))]
use nix::unistd::pipe;
const BUF_SIZE: usize = 16 * 1024; const BUF_SIZE: usize = 16 * 1024;
#[cfg(any(target_os = "linux", target_os = "android"))] #[cfg(any(target_os = "linux", target_os = "android"))]
const SPLICE_SIZE: usize = 128 * 1024; const SPLICE_SIZE: usize = 128 * 1024;
/// Splice wrapper which handles short writes
#[cfg(any(target_os = "linux", target_os = "android"))]
#[inline]
fn splice_exact(read_fd: RawFd, write_fd: RawFd, num_bytes: usize) -> nix::Result<()> {
let mut left = num_bytes;
loop {
let written = splice(read_fd, None, write_fd, None, left, SpliceFFlags::empty())?;
left -= written;
if left == 0 {
break;
}
}
Ok(())
}
/// This is a Linux-specific function to count the number of bytes using the /// This is a Linux-specific function to count the number of bytes using the
/// `splice` system call, which is faster than using `read`. /// `splice` system call, which is faster than using `read`.
/// ///
@ -46,13 +29,14 @@ fn splice_exact(read_fd: RawFd, write_fd: RawFd, num_bytes: usize) -> nix::Resul
/// caller will fall back to a simpler method. /// caller will fall back to a simpler method.
#[inline] #[inline]
#[cfg(any(target_os = "linux", target_os = "android"))] #[cfg(any(target_os = "linux", target_os = "android"))]
fn count_bytes_using_splice(fd: RawFd) -> Result<usize, usize> { fn count_bytes_using_splice(fd: &impl AsRawFd) -> Result<usize, usize> {
let null_file = OpenOptions::new() let null_file = OpenOptions::new()
.write(true) .write(true)
.open("/dev/null") .open("/dev/null")
.map_err(|_| 0_usize)?; .map_err(|_| 0_usize)?;
let null = null_file.as_raw_fd(); let null_rdev = stat::fstat(null_file.as_raw_fd())
let null_rdev = stat::fstat(null).map_err(|_| 0_usize)?.st_rdev; .map_err(|_| 0_usize)?
.st_rdev;
if (stat::major(null_rdev), stat::minor(null_rdev)) != (1, 3) { if (stat::major(null_rdev), stat::minor(null_rdev)) != (1, 3) {
// This is not a proper /dev/null, writing to it is probably bad // This is not a proper /dev/null, writing to it is probably bad
// Bit of an edge case, but it has been known to happen // Bit of an edge case, but it has been known to happen
@ -60,17 +44,13 @@ fn count_bytes_using_splice(fd: RawFd) -> Result<usize, usize> {
} }
let (pipe_rd, pipe_wr) = pipe().map_err(|_| 0_usize)?; let (pipe_rd, pipe_wr) = pipe().map_err(|_| 0_usize)?;
// Ensure the pipe is closed when the function returns.
// SAFETY: The file descriptors do not have other owners.
let _handles = unsafe { (File::from_raw_fd(pipe_rd), File::from_raw_fd(pipe_wr)) };
let mut byte_count = 0; let mut byte_count = 0;
loop { loop {
match splice(fd, None, pipe_wr, None, SPLICE_SIZE, SpliceFFlags::empty()) { match splice(fd, &pipe_wr, SPLICE_SIZE) {
Ok(0) => break, Ok(0) => break,
Ok(res) => { Ok(res) => {
byte_count += res; byte_count += res;
if splice_exact(pipe_rd, null, res).is_err() { if splice_exact(&pipe_rd, &null_file, res).is_err() {
return Err(byte_count); return Err(byte_count);
} }
} }
@ -106,7 +86,7 @@ pub(crate) fn count_bytes_fast<T: WordCountable>(handle: &mut T) -> (usize, Opti
// Else, if we're on Linux and our file is a FIFO pipe // Else, if we're on Linux and our file is a FIFO pipe
// (or stdin), we use splice to count the number of bytes. // (or stdin), we use splice to count the number of bytes.
if (stat.st_mode & S_IFIFO) != 0 { if (stat.st_mode & S_IFIFO) != 0 {
match count_bytes_using_splice(fd) { match count_bytes_using_splice(handle) {
Ok(n) => return (n, None), Ok(n) => return (n, None),
Err(n) => byte_count = n, Err(n) => byte_count = n,
} }

View file

@ -424,8 +424,15 @@ fn wc(inputs: Vec<Input>, settings: &Settings) -> Result<(), u32> {
// The width is the number of digits needed to print the number of // The width is the number of digits needed to print the number of
// bytes in the largest file. This is true regardless of whether // bytes in the largest file. This is true regardless of whether
// the `settings` indicate that the bytes will be displayed. // the `settings` indicate that the bytes will be displayed.
//
// If we only need to display a single number, set this to 0 to
// prevent leading spaces.
let mut failure = false; let mut failure = false;
let max_width = max_width(&inputs); let max_width = if settings.number_enabled() <= 1 {
0
} else {
max_width(&inputs)
};
let mut total_word_count = WordCount::default(); let mut total_word_count = WordCount::default();
@ -475,20 +482,10 @@ fn wc(inputs: Vec<Input>, settings: &Settings) -> Result<(), u32> {
} }
} }
fn print_stats( fn print_stats(settings: &Settings, result: &TitledWordCount, min_width: usize) -> io::Result<()> {
settings: &Settings,
result: &TitledWordCount,
mut min_width: usize,
) -> io::Result<()> {
let stdout = io::stdout(); let stdout = io::stdout();
let mut stdout_lock = stdout.lock(); let mut stdout_lock = stdout.lock();
if settings.number_enabled() <= 1 {
// Prevent a leading space in case we only need to display a single
// number.
min_width = 0;
}
let mut is_first: bool = true; let mut is_first: bool = true;
if settings.show_lines { if settings.show_lines {

View file

@ -16,7 +16,7 @@ path = "src/yes.rs"
[dependencies] [dependencies]
clap = { version = "2.33", features = ["wrap_help"] } clap = { version = "2.33", features = ["wrap_help"] }
uucore = { version=">=0.0.9", package="uucore", path="../../uucore" } uucore = { version=">=0.0.9", package="uucore", path="../../uucore", features=["pipes"] }
uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" } uucore_procs = { version=">=0.0.6", package="uucore_procs", path="../../uucore_procs" }
[target.'cfg(any(target_os = "linux", target_os = "android"))'.dependencies] [target.'cfg(any(target_os = "linux", target_os = "android"))'.dependencies]

View file

@ -16,18 +16,11 @@
//! make any effort to rescue data from the pipe if splice() fails, we can //! make any effort to rescue data from the pipe if splice() fails, we can
//! just fall back and start over from the beginning. //! just fall back and start over from the beginning.
use std::{ use std::{io, os::unix::io::AsRawFd};
fs::File,
io,
os::unix::io::{AsRawFd, FromRawFd},
};
use nix::{ use nix::{errno::Errno, libc::S_IFIFO, sys::stat::fstat};
errno::Errno,
fcntl::SpliceFFlags, use uucore::pipes::{pipe, splice_exact, vmsplice};
libc::S_IFIFO,
sys::{stat::fstat, uio::IoVec},
};
pub(crate) fn splice_data(bytes: &[u8], out: &impl AsRawFd) -> Result<()> { pub(crate) fn splice_data(bytes: &[u8], out: &impl AsRawFd) -> Result<()> {
let is_pipe = fstat(out.as_raw_fd())?.st_mode & S_IFIFO != 0; let is_pipe = fstat(out.as_raw_fd())?.st_mode & S_IFIFO != 0;
@ -36,7 +29,7 @@ pub(crate) fn splice_data(bytes: &[u8], out: &impl AsRawFd) -> Result<()> {
loop { loop {
let mut bytes = bytes; let mut bytes = bytes;
while !bytes.is_empty() { while !bytes.is_empty() {
let len = vmsplice(out, bytes)?; let len = vmsplice(out, bytes).map_err(maybe_unsupported)?;
bytes = &bytes[len..]; bytes = &bytes[len..];
} }
} }
@ -45,14 +38,8 @@ pub(crate) fn splice_data(bytes: &[u8], out: &impl AsRawFd) -> Result<()> {
loop { loop {
let mut bytes = bytes; let mut bytes = bytes;
while !bytes.is_empty() { while !bytes.is_empty() {
let len = vmsplice(&write, bytes)?; let len = vmsplice(&write, bytes).map_err(maybe_unsupported)?;
let mut remaining = len; splice_exact(&read, out, len).map_err(maybe_unsupported)?;
while remaining > 0 {
match splice(&read, out, remaining)? {
0 => panic!("Unexpected end of pipe"),
n => remaining -= n,
};
}
bytes = &bytes[len..]; bytes = &bytes[len..];
} }
} }
@ -81,30 +68,3 @@ fn maybe_unsupported(error: nix::Error) -> Error {
_ => error.into(), _ => error.into(),
} }
} }
fn splice(source: &impl AsRawFd, target: &impl AsRawFd, len: usize) -> Result<usize> {
nix::fcntl::splice(
source.as_raw_fd(),
None,
target.as_raw_fd(),
None,
len,
SpliceFFlags::empty(),
)
.map_err(maybe_unsupported)
}
fn vmsplice(target: &impl AsRawFd, bytes: &[u8]) -> Result<usize> {
nix::fcntl::vmsplice(
target.as_raw_fd(),
&[IoVec::from_slice(bytes)],
SpliceFFlags::empty(),
)
.map_err(maybe_unsupported)
}
fn pipe() -> nix::Result<(File, File)> {
let (read, write) = nix::unistd::pipe()?;
// SAFETY: The file descriptors do not have other owners.
unsafe { Ok((File::from_raw_fd(read), File::from_raw_fd(write))) }
}

View file

@ -30,6 +30,8 @@ data-encoding = { version="2.1", optional=true }
data-encoding-macro = { version="0.1.12", optional=true } data-encoding-macro = { version="0.1.12", optional=true }
z85 = { version="3.0.3", optional=true } z85 = { version="3.0.3", optional=true }
libc = { version="0.2.15", optional=true } libc = { version="0.2.15", optional=true }
once_cell = "1.8.0"
nix = { version="0.20", optional=true }
[dev-dependencies] [dev-dependencies]
clap = "2.33.3" clap = "2.33.3"
@ -56,3 +58,4 @@ signals = []
utf8 = [] utf8 = []
utmpx = ["time", "libc", "dns-lookup"] utmpx = ["time", "libc", "dns-lookup"]
wide = [] wide = []
pipes = ["nix"]

View file

@ -19,6 +19,8 @@ pub mod mode;
pub mod entries; pub mod entries;
#[cfg(all(unix, feature = "perms"))] #[cfg(all(unix, feature = "perms"))]
pub mod perms; pub mod perms;
#[cfg(all(unix, feature = "pipes"))]
pub mod pipes;
#[cfg(all(unix, feature = "process"))] #[cfg(all(unix, feature = "process"))]
pub mod process; pub mod process;

View file

@ -5,6 +5,7 @@
//! Common functions to manage permissions //! Common functions to manage permissions
use crate::display::Quotable;
use crate::error::strip_errno; use crate::error::strip_errno;
use crate::error::UResult; use crate::error::UResult;
use crate::error::USimpleError; use crate::error::USimpleError;
@ -80,29 +81,29 @@ pub fn wrap_chown<P: AsRef<Path>>(
VerbosityLevel::Silent => (), VerbosityLevel::Silent => (),
level => { level => {
out = format!( out = format!(
"changing {} of '{}': {}", "changing {} of {}: {}",
if verbosity.groups_only { if verbosity.groups_only {
"group" "group"
} else { } else {
"ownership" "ownership"
}, },
path.display(), path.quote(),
e e
); );
if level == VerbosityLevel::Verbose { if level == VerbosityLevel::Verbose {
out = if verbosity.groups_only { out = if verbosity.groups_only {
format!( format!(
"{}\nfailed to change group of '{}' from {} to {}", "{}\nfailed to change group of {} from {} to {}",
out, out,
path.display(), path.quote(),
entries::gid2grp(meta.gid()).unwrap(), entries::gid2grp(meta.gid()).unwrap(),
entries::gid2grp(dest_gid).unwrap() entries::gid2grp(dest_gid).unwrap()
) )
} else { } else {
format!( format!(
"{}\nfailed to change ownership of '{}' from {}:{} to {}:{}", "{}\nfailed to change ownership of {} from {}:{} to {}:{}",
out, out,
path.display(), path.quote(),
entries::uid2usr(meta.uid()).unwrap(), entries::uid2usr(meta.uid()).unwrap(),
entries::gid2grp(meta.gid()).unwrap(), entries::gid2grp(meta.gid()).unwrap(),
entries::uid2usr(dest_uid).unwrap(), entries::uid2usr(dest_uid).unwrap(),
@ -120,15 +121,15 @@ pub fn wrap_chown<P: AsRef<Path>>(
VerbosityLevel::Changes | VerbosityLevel::Verbose => { VerbosityLevel::Changes | VerbosityLevel::Verbose => {
out = if verbosity.groups_only { out = if verbosity.groups_only {
format!( format!(
"changed group of '{}' from {} to {}", "changed group of {} from {} to {}",
path.display(), path.quote(),
entries::gid2grp(meta.gid()).unwrap(), entries::gid2grp(meta.gid()).unwrap(),
entries::gid2grp(dest_gid).unwrap() entries::gid2grp(dest_gid).unwrap()
) )
} else { } else {
format!( format!(
"changed ownership of '{}' from {}:{} to {}:{}", "changed ownership of {} from {}:{} to {}:{}",
path.display(), path.quote(),
entries::uid2usr(meta.uid()).unwrap(), entries::uid2usr(meta.uid()).unwrap(),
entries::gid2grp(meta.gid()).unwrap(), entries::gid2grp(meta.gid()).unwrap(),
entries::uid2usr(dest_uid).unwrap(), entries::uid2usr(dest_uid).unwrap(),
@ -141,14 +142,14 @@ pub fn wrap_chown<P: AsRef<Path>>(
} else if verbosity.level == VerbosityLevel::Verbose { } else if verbosity.level == VerbosityLevel::Verbose {
out = if verbosity.groups_only { out = if verbosity.groups_only {
format!( format!(
"group of '{}' retained as {}", "group of {} retained as {}",
path.display(), path.quote(),
entries::gid2grp(dest_gid).unwrap_or_default() entries::gid2grp(dest_gid).unwrap_or_default()
) )
} else { } else {
format!( format!(
"ownership of '{}' retained as {}:{}", "ownership of {} retained as {}:{}",
path.display(), path.quote(),
entries::uid2usr(dest_uid).unwrap(), entries::uid2usr(dest_uid).unwrap(),
entries::gid2grp(dest_gid).unwrap() entries::gid2grp(dest_gid).unwrap()
) )
@ -358,9 +359,9 @@ impl ChownExecutor {
match self.verbosity.level { match self.verbosity.level {
VerbosityLevel::Silent => (), VerbosityLevel::Silent => (),
_ => show_error!( _ => show_error!(
"cannot {} '{}': {}", "cannot {} {}: {}",
if follow { "dereference" } else { "access" }, if follow { "dereference" } else { "access" },
path.display(), path.quote(),
strip_errno(&e) strip_errno(&e)
), ),
} }

View file

@ -0,0 +1,69 @@
/// Thin pipe-related wrappers around functions from the `nix` crate.
use std::fs::File;
#[cfg(any(target_os = "linux", target_os = "android"))]
use std::os::unix::io::AsRawFd;
use std::os::unix::io::FromRawFd;
#[cfg(any(target_os = "linux", target_os = "android"))]
use nix::{fcntl::SpliceFFlags, sys::uio::IoVec};
pub use nix::{Error, Result};
/// A wrapper around [`nix::unistd::Pipe`] that ensures the pipe is cleaned up.
///
/// Returns two `File` objects: everything written to the second can be read
/// from the first.
pub fn pipe() -> Result<(File, File)> {
let (read, write) = nix::unistd::pipe()?;
// SAFETY: The file descriptors do not have other owners.
unsafe { Ok((File::from_raw_fd(read), File::from_raw_fd(write))) }
}
/// Less noisy wrapper around [`nix::fcntl::splice`].
///
/// Up to `len` bytes are moved from `source` to `target`. Returns the number
/// of successfully moved bytes.
///
/// At least one of `source` and `target` must be some sort of pipe.
/// To get around this requirement, consider splicing from your source into
/// a [`pipe`] and then from the pipe into your target (with `splice_exact`):
/// this is still very efficient.
#[cfg(any(target_os = "linux", target_os = "android"))]
pub fn splice(source: &impl AsRawFd, target: &impl AsRawFd, len: usize) -> Result<usize> {
nix::fcntl::splice(
source.as_raw_fd(),
None,
target.as_raw_fd(),
None,
len,
SpliceFFlags::empty(),
)
}
/// Splice wrapper which fully finishes the write.
///
/// Exactly `len` bytes are moved from `source` into `target`.
///
/// Panics if `source` runs out of data before `len` bytes have been moved.
#[cfg(any(target_os = "linux", target_os = "android"))]
pub fn splice_exact(source: &impl AsRawFd, target: &impl AsRawFd, len: usize) -> Result<()> {
let mut left = len;
while left != 0 {
let written = splice(source, target, left)?;
assert_ne!(written, 0, "unexpected end of data");
left -= written;
}
Ok(())
}
/// Copy data from `bytes` into `target`, which must be a pipe.
///
/// Returns the number of successfully copied bytes.
#[cfg(any(target_os = "linux", target_os = "android"))]
pub fn vmsplice(target: &impl AsRawFd, bytes: &[u8]) -> Result<usize> {
nix::fcntl::vmsplice(
target.as_raw_fd(),
&[IoVec::from_slice(bytes)],
SpliceFFlags::empty(),
)
}

View file

@ -49,6 +49,8 @@ pub use crate::features::mode;
pub use crate::features::entries; pub use crate::features::entries;
#[cfg(all(unix, feature = "perms"))] #[cfg(all(unix, feature = "perms"))]
pub use crate::features::perms; pub use crate::features::perms;
#[cfg(all(unix, feature = "pipes"))]
pub use crate::features::pipes;
#[cfg(all(unix, feature = "process"))] #[cfg(all(unix, feature = "process"))]
pub use crate::features::process; pub use crate::features::process;
#[cfg(all(unix, not(target_os = "fuchsia"), feature = "signals"))] #[cfg(all(unix, not(target_os = "fuchsia"), feature = "signals"))]
@ -70,6 +72,10 @@ pub use crate::features::wide;
use std::ffi::OsString; use std::ffi::OsString;
use std::sync::atomic::Ordering; use std::sync::atomic::Ordering;
use once_cell::sync::Lazy;
use crate::display::Quotable;
pub fn get_utility_is_second_arg() -> bool { pub fn get_utility_is_second_arg() -> bool {
crate::macros::UTILITY_IS_SECOND_ARG.load(Ordering::SeqCst) crate::macros::UTILITY_IS_SECOND_ARG.load(Ordering::SeqCst)
} }
@ -78,36 +84,40 @@ pub fn set_utility_is_second_arg() {
crate::macros::UTILITY_IS_SECOND_ARG.store(true, Ordering::SeqCst) crate::macros::UTILITY_IS_SECOND_ARG.store(true, Ordering::SeqCst)
} }
/// Get the executable path (as `OsString`). // args_os() can be expensive to call, it copies all of argv before iterating.
fn executable_os() -> OsString { // So if we want only the first arg or so it's overkill. We cache it.
args_os().next().unwrap() static ARGV: Lazy<Vec<OsString>> = Lazy::new(|| wild::args_os().collect());
}
/// Get the executable path (as `String`). static UTIL_NAME: Lazy<String> = Lazy::new(|| {
fn executable() -> String { if get_utility_is_second_arg() {
executable_os().to_string_lossy().into_owned() &ARGV[1]
} } else {
&ARGV[0]
}
.to_string_lossy()
.into_owned()
});
/// Derive the utility name. /// Derive the utility name.
pub fn util_name() -> String { pub fn util_name() -> &'static str {
if get_utility_is_second_arg() { &UTIL_NAME
args_os().nth(1).unwrap().to_string_lossy().into_owned()
} else {
executable()
}
} }
/// Derive the complete execution phrase for "usage". static EXECUTION_PHRASE: Lazy<String> = Lazy::new(|| {
pub fn execution_phrase() -> String {
if get_utility_is_second_arg() { if get_utility_is_second_arg() {
args_os() ARGV.iter()
.take(2) .take(2)
.map(|os_str| os_str.to_string_lossy().into_owned()) .map(|os_str| os_str.to_string_lossy().into_owned())
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join(" ") .join(" ")
} else { } else {
executable() ARGV[0].to_string_lossy().into_owned()
} }
});
/// Derive the complete execution phrase for "usage".
pub fn execution_phrase() -> &'static str {
&EXECUTION_PHRASE
} }
pub enum InvalidEncodingHandling { pub enum InvalidEncodingHandling {
@ -165,14 +175,15 @@ pub trait Args: Iterator<Item = OsString> + Sized {
Ok(string) => Ok(string), Ok(string) => Ok(string),
Err(s_ret) => { Err(s_ret) => {
full_conversion = false; full_conversion = false;
let lossy_conversion = s_ret.to_string_lossy();
eprintln!( eprintln!(
"Input with broken encoding occurred! (s = '{}') ", "Input with broken encoding occurred! (s = {}) ",
&lossy_conversion s_ret.quote()
); );
match handling { match handling {
InvalidEncodingHandling::Ignore => Err(String::new()), InvalidEncodingHandling::Ignore => Err(String::new()),
InvalidEncodingHandling::ConvertLossy => Err(lossy_conversion.to_string()), InvalidEncodingHandling::ConvertLossy => {
Err(s_ret.to_string_lossy().into_owned())
}
InvalidEncodingHandling::Panic => { InvalidEncodingHandling::Panic => {
panic!("Broken encoding found but caller cannot handle it") panic!("Broken encoding found but caller cannot handle it")
} }
@ -204,13 +215,8 @@ pub trait Args: Iterator<Item = OsString> + Sized {
impl<T: Iterator<Item = OsString> + Sized> Args for T {} impl<T: Iterator<Item = OsString> + Sized> Args for T {}
// args() ...
pub fn args() -> impl Iterator<Item = String> {
wild::args()
}
pub fn args_os() -> impl Iterator<Item = OsString> { pub fn args_os() -> impl Iterator<Item = OsString> {
wild::args_os() ARGV.iter().cloned()
} }
#[cfg(test)] #[cfg(test)]

View file

@ -78,7 +78,10 @@
// spell-checker:ignore backupopt // spell-checker:ignore backupopt
use crate::error::{UError, UResult}; use crate::{
display::Quotable,
error::{UError, UResult},
};
use clap::ArgMatches; use clap::ArgMatches;
use std::{ use std::{
env, env,
@ -167,18 +170,22 @@ impl Display for BackupError {
match self { match self {
BE::InvalidArgument(arg, origin) => write!( BE::InvalidArgument(arg, origin) => write!(
f, f,
"invalid argument '{}' for '{}'\n{}", "invalid argument {} for '{}'\n{}",
arg, origin, VALID_ARGS_HELP arg.quote(),
origin,
VALID_ARGS_HELP
), ),
BE::AmbiguousArgument(arg, origin) => write!( BE::AmbiguousArgument(arg, origin) => write!(
f, f,
"ambiguous argument '{}' for '{}'\n{}", "ambiguous argument {} for '{}'\n{}",
arg, origin, VALID_ARGS_HELP arg.quote(),
origin,
VALID_ARGS_HELP
), ),
BE::BackupImpossible() => write!(f, "cannot create backup"), BE::BackupImpossible() => write!(f, "cannot create backup"),
// Placeholder for later // Placeholder for later
// BE::BackupFailed(from, to, e) => Display::fmt( // 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 // f
// ), // ),
} }

View file

@ -87,13 +87,16 @@ macro_rules! impl_as_ref {
}; };
} }
impl_as_ref!(str);
impl_as_ref!(&'_ str); impl_as_ref!(&'_ str);
impl_as_ref!(String); impl_as_ref!(String);
impl_as_ref!(std::path::Path);
impl_as_ref!(&'_ std::path::Path); impl_as_ref!(&'_ std::path::Path);
impl_as_ref!(std::path::PathBuf); impl_as_ref!(std::path::PathBuf);
impl_as_ref!(std::path::Component<'_>); impl_as_ref!(std::path::Component<'_>);
impl_as_ref!(std::path::Components<'_>); impl_as_ref!(std::path::Components<'_>);
impl_as_ref!(std::path::Iter<'_>); impl_as_ref!(std::path::Iter<'_>);
impl_as_ref!(std::ffi::OsStr);
impl_as_ref!(&'_ std::ffi::OsStr); impl_as_ref!(&'_ std::ffi::OsStr);
impl_as_ref!(std::ffi::OsString); 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. /// A wrapper around [`OsStr`] for printing paths with quoting and escaping applied.
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub struct Quoted<'a> { pub struct Quoted<'a> {
@ -407,6 +417,19 @@ pub fn println_verbatim<S: AsRef<OsStr>>(text: S) -> io::Result<()> {
Ok(()) Ok(())
} }
/// Like `println_verbatim`, without the trailing newline.
pub fn print_verbatim<S: AsRef<OsStr>>(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)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

Some files were not shown because too many files have changed in this diff Show more