mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-27 11:07:44 +00:00
Merge branch 'uutils:master' into ls_fix_errno_1
This commit is contained in:
commit
4ea61545c6
13 changed files with 291 additions and 244 deletions
|
@ -11,8 +11,7 @@ use std::fs::File;
|
|||
use std::io::{self, stdin, BufReader, Read};
|
||||
use std::path::Path;
|
||||
use uucore::display::Quotable;
|
||||
use uucore::error::UResult;
|
||||
use uucore::error::USimpleError;
|
||||
use uucore::error::{FromIo, UResult};
|
||||
use uucore::show;
|
||||
use uucore::InvalidEncodingHandling;
|
||||
|
||||
|
@ -81,27 +80,18 @@ fn cksum(fname: &str) -> io::Result<(u32, usize)> {
|
|||
let mut crc = 0u32;
|
||||
let mut size = 0usize;
|
||||
|
||||
let file;
|
||||
let mut rd: Box<dyn Read> = match fname {
|
||||
"-" => Box::new(stdin()),
|
||||
_ => {
|
||||
let path = &Path::new(fname);
|
||||
if path.is_dir() {
|
||||
return Err(std::io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
"Is a directory",
|
||||
));
|
||||
};
|
||||
// Silent the warning as we want to the error message
|
||||
#[allow(clippy::question_mark)]
|
||||
if path.metadata().is_err() {
|
||||
return Err(std::io::Error::new(
|
||||
io::ErrorKind::NotFound,
|
||||
"No such file or directory",
|
||||
));
|
||||
};
|
||||
file = File::open(&path)?;
|
||||
Box::new(BufReader::new(file))
|
||||
let p = Path::new(fname);
|
||||
|
||||
// Directories should not give an error, but should be interpreted
|
||||
// as empty files to match GNU semantics.
|
||||
if p.is_dir() {
|
||||
Box::new(BufReader::new(io::empty())) as Box<dyn Read>
|
||||
} else {
|
||||
Box::new(BufReader::new(File::open(p)?)) as Box<dyn Read>
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -136,23 +126,16 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
|||
};
|
||||
|
||||
if files.is_empty() {
|
||||
match cksum("-") {
|
||||
Ok((crc, size)) => println!("{} {}", crc, size),
|
||||
Err(err) => {
|
||||
return Err(USimpleError::new(2, format!("{}", err)));
|
||||
}
|
||||
}
|
||||
let (crc, size) = cksum("-")?;
|
||||
println!("{} {}", crc, size);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
for fname in &files {
|
||||
match cksum(fname.as_ref()) {
|
||||
match cksum(fname.as_ref()).map_err_context(|| format!("{}", fname.maybe_quote())) {
|
||||
Ok((crc, size)) => println!("{} {} {}", crc, size, fname),
|
||||
Err(err) => show!(USimpleError::new(
|
||||
2,
|
||||
format!("{}: {}", fname.maybe_quote(), err)
|
||||
)),
|
||||
}
|
||||
Err(err) => show!(err),
|
||||
};
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1393,8 +1393,16 @@ fn list(locs: Vec<&Path>, config: Config) -> UResult<()> {
|
|||
|
||||
display_items(&files, &config, &mut out);
|
||||
|
||||
for dir in &dirs {
|
||||
enter_directory(dir, &config, initial_locs_len, &mut out);
|
||||
for (pos, dir) in dirs.iter().enumerate() {
|
||||
// Print dir heading - name... 'total' comes after error display
|
||||
if initial_locs_len > 1 || config.recursive {
|
||||
if pos.eq(&0usize) && files.is_empty() {
|
||||
let _ = writeln!(out, "{}:", dir.p_buf.display());
|
||||
} else {
|
||||
let _ = writeln!(out, "\n{}:", dir.p_buf.display());
|
||||
}
|
||||
}
|
||||
enter_directory(dir, &config, &mut out);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -1464,12 +1472,7 @@ fn should_display(entry: &DirEntry, config: &Config) -> bool {
|
|||
true
|
||||
}
|
||||
|
||||
fn enter_directory(
|
||||
dir: &PathData,
|
||||
config: &Config,
|
||||
initial_locs_len: usize,
|
||||
out: &mut BufWriter<Stdout>,
|
||||
) {
|
||||
fn enter_directory(dir: &PathData, config: &Config, out: &mut BufWriter<Stdout>) {
|
||||
// Create vec of entries with initial dot files
|
||||
let mut entries: Vec<PathData> = if config.files == Files::All {
|
||||
vec![
|
||||
|
@ -1524,10 +1527,6 @@ fn enter_directory(
|
|||
sort_entries(&mut vec_path_data, config, out);
|
||||
entries.append(&mut vec_path_data);
|
||||
|
||||
// Print dir heading - name...
|
||||
if initial_locs_len > 1 || config.recursive {
|
||||
let _ = writeln!(out, "\n{}:", dir.p_buf.display());
|
||||
}
|
||||
// ...and total
|
||||
if config.format == Format::Long {
|
||||
display_total(&entries, config, out);
|
||||
|
@ -1544,7 +1543,8 @@ fn enter_directory(
|
|||
.filter(|p| p.ft.get().unwrap().is_some())
|
||||
.filter(|p| p.ft.get().unwrap().unwrap().is_dir())
|
||||
{
|
||||
enter_directory(e, config, 0, out);
|
||||
let _ = writeln!(out, "\n{}:", e.p_buf.display());
|
||||
enter_directory(e, config, out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ use std::io::{stdin, stdout, BufRead, BufReader, Lines, Read, Write};
|
|||
use std::os::unix::fs::FileTypeExt;
|
||||
|
||||
use uucore::display::Quotable;
|
||||
use uucore::error::UResult;
|
||||
|
||||
type IOError = std::io::Error;
|
||||
|
||||
|
@ -174,7 +175,8 @@ pub fn uu_app() -> clap::App<'static, 'static> {
|
|||
clap::App::new(uucore::util_name())
|
||||
}
|
||||
|
||||
pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||
#[uucore_procs::gen_uumain]
|
||||
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
let args = args
|
||||
.collect_str(uucore::InvalidEncodingHandling::Ignore)
|
||||
.accept_any();
|
||||
|
@ -388,7 +390,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
|
||||
if matches.opt_present("version") {
|
||||
println!("{} {}", NAME, VERSION);
|
||||
return 0;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut files = matches.free.clone();
|
||||
|
@ -412,7 +414,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
Ok(options) => options,
|
||||
Err(err) => {
|
||||
print_error(&matches, err);
|
||||
return 1;
|
||||
return Err(1.into());
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -430,11 +432,10 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
_ => 0,
|
||||
};
|
||||
if status != 0 {
|
||||
return status;
|
||||
return Err(status.into());
|
||||
}
|
||||
}
|
||||
|
||||
0
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns re-written arguments which are passed to the program.
|
||||
|
@ -470,7 +471,7 @@ fn print_error(matches: &Matches, err: PrError) {
|
|||
}
|
||||
}
|
||||
|
||||
fn print_usage(opts: &mut getopts::Options, matches: &Matches) -> i32 {
|
||||
fn print_usage(opts: &mut getopts::Options, matches: &Matches) -> UResult<()> {
|
||||
println!("{} {} -- print files", NAME, VERSION);
|
||||
println!();
|
||||
println!(
|
||||
|
@ -508,10 +509,9 @@ fn print_usage(opts: &mut getopts::Options, matches: &Matches) -> i32 {
|
|||
options::COLUMN_OPTION
|
||||
);
|
||||
if matches.free.is_empty() {
|
||||
return 1;
|
||||
return Err(1.into());
|
||||
}
|
||||
|
||||
0
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_usize(matches: &Matches, opt: &str) -> Option<Result<usize, PrError>> {
|
||||
|
|
|
@ -17,6 +17,7 @@ use std::{
|
|||
};
|
||||
use uucore::{
|
||||
display::{print_verbatim, Quotable},
|
||||
error::{FromIo, UResult},
|
||||
fs::{canonicalize, MissingHandling, ResolveMode},
|
||||
};
|
||||
|
||||
|
@ -36,7 +37,8 @@ fn usage() -> String {
|
|||
format!("{0} [OPTION]... FILE...", 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 matches = uu_app().usage(&usage[..]).get_matches_from(args);
|
||||
|
@ -60,16 +62,16 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
} else {
|
||||
MissingHandling::Normal
|
||||
};
|
||||
let mut retcode = 0;
|
||||
for path in &paths {
|
||||
if let Err(e) = resolve_path(path, strip, zero, logical, can_mode) {
|
||||
if !quiet {
|
||||
show_error!("{}: {}", path.maybe_quote(), e);
|
||||
}
|
||||
retcode = 1
|
||||
};
|
||||
let result = resolve_path(path, strip, zero, logical, can_mode);
|
||||
if !quiet {
|
||||
show_if_err!(result.map_err_context(|| path.maybe_quote().to_string()));
|
||||
}
|
||||
}
|
||||
retcode
|
||||
// Although we return `Ok`, it is possible that a call to
|
||||
// `show!()` above has set the exit code for the program to a
|
||||
// non-zero integer.
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn uu_app() -> App<'static, 'static> {
|
||||
|
|
|
@ -1,12 +1,22 @@
|
|||
use std::ffi::OsString;
|
||||
use std::fmt::Write;
|
||||
use std::fmt::{Display, Formatter, Write};
|
||||
use std::io;
|
||||
use std::str::Utf8Error;
|
||||
|
||||
use uucore::display::Quotable;
|
||||
use uucore::error::UError;
|
||||
|
||||
pub(crate) type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
// This list is NOT exhaustive. This command might perform an `execvp()` to run
|
||||
// a different program. When that happens successfully, the exit status of this
|
||||
// process will be the exit status of that program.
|
||||
pub(crate) mod error_exit_status {
|
||||
pub const NOT_FOUND: i32 = 127;
|
||||
pub const COULD_NOT_EXECUTE: i32 = 126;
|
||||
pub const ANOTHER_ERROR: i32 = libc::EXIT_FAILURE;
|
||||
}
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub(crate) enum Error {
|
||||
#[error("No command is specified")]
|
||||
|
@ -63,13 +73,44 @@ impl Error {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn report_full_error(mut err: &dyn std::error::Error) -> String {
|
||||
let mut desc = String::with_capacity(256);
|
||||
write!(&mut desc, "{}", err).unwrap();
|
||||
pub(crate) fn write_full_error<W>(writer: &mut W, err: &dyn std::error::Error) -> std::fmt::Result
|
||||
where
|
||||
W: Write,
|
||||
{
|
||||
write!(writer, "{}", err)?;
|
||||
let mut err = err;
|
||||
while let Some(source) = err.source() {
|
||||
err = source;
|
||||
write!(&mut desc, ": {}", err).unwrap();
|
||||
write!(writer, ": {}", err)?;
|
||||
}
|
||||
write!(writer, ".")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct RunconError {
|
||||
inner: Error,
|
||||
code: i32,
|
||||
}
|
||||
|
||||
impl RunconError {
|
||||
pub(crate) fn new(e: Error) -> RunconError {
|
||||
RunconError::with_code(error_exit_status::ANOTHER_ERROR, e)
|
||||
}
|
||||
|
||||
pub(crate) fn with_code(code: i32, e: Error) -> RunconError {
|
||||
RunconError { inner: e, code }
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for RunconError {}
|
||||
impl UError for RunconError {
|
||||
fn code(&self) -> i32 {
|
||||
self.code
|
||||
}
|
||||
}
|
||||
impl Display for RunconError {
|
||||
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
|
||||
write_full_error(f, &self.inner)
|
||||
}
|
||||
desc.push('.');
|
||||
desc
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// spell-checker:ignore (vars) RFILE
|
||||
|
||||
use uucore::{show_error, show_usage_error};
|
||||
use uucore::error::{UResult, UUsageError};
|
||||
|
||||
use clap::{App, Arg};
|
||||
use selinux::{OpaqueSecurityContext, SecurityClass, SecurityContext};
|
||||
|
@ -13,7 +13,8 @@ use std::{io, ptr};
|
|||
|
||||
mod errors;
|
||||
|
||||
use errors::{report_full_error, Error, Result};
|
||||
use errors::error_exit_status;
|
||||
use errors::{Error, Result, RunconError};
|
||||
|
||||
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
const ABOUT: &str = "Run command with specified security context.";
|
||||
|
@ -35,16 +36,6 @@ pub mod options {
|
|||
pub const RANGE: &str = "range";
|
||||
}
|
||||
|
||||
// This list is NOT exhaustive. This command might perform an `execvp()` to run
|
||||
// a different program. When that happens successfully, the exit status of this
|
||||
// process will be the exit status of that program.
|
||||
mod error_exit_status {
|
||||
pub const SUCCESS: i32 = libc::EXIT_SUCCESS;
|
||||
pub const NOT_FOUND: i32 = 127;
|
||||
pub const COULD_NOT_EXECUTE: i32 = 126;
|
||||
pub const ANOTHER_ERROR: i32 = libc::EXIT_FAILURE;
|
||||
}
|
||||
|
||||
fn get_usage() -> String {
|
||||
format!(
|
||||
"{0} [CONTEXT COMMAND [ARG...]]\n \
|
||||
|
@ -53,7 +44,8 @@ fn get_usage() -> String {
|
|||
)
|
||||
}
|
||||
|
||||
pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||
#[uucore_procs::gen_uumain]
|
||||
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
let usage = get_usage();
|
||||
|
||||
let config = uu_app().usage(usage.as_ref());
|
||||
|
@ -65,39 +57,28 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
match r.kind {
|
||||
clap::ErrorKind::HelpDisplayed | clap::ErrorKind::VersionDisplayed => {
|
||||
println!("{}", r);
|
||||
return error_exit_status::SUCCESS;
|
||||
return Ok(());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
show_usage_error!("{}.\n", r);
|
||||
return error_exit_status::ANOTHER_ERROR;
|
||||
return Err(UUsageError::new(
|
||||
error_exit_status::ANOTHER_ERROR,
|
||||
format!("{}", r),
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
match &options.mode {
|
||||
CommandLineMode::Print => {
|
||||
if let Err(r) = print_current_context() {
|
||||
show_error!("{}", report_full_error(&r));
|
||||
return error_exit_status::ANOTHER_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
CommandLineMode::Print => print_current_context().map_err(|e| RunconError::new(e).into()),
|
||||
CommandLineMode::PlainContext { context, command } => {
|
||||
let (exit_status, err) =
|
||||
if let Err(err) = get_plain_context(context).and_then(set_next_exec_context) {
|
||||
(error_exit_status::ANOTHER_ERROR, err)
|
||||
} else {
|
||||
// On successful execution, the following call never returns,
|
||||
// and this process image is replaced.
|
||||
execute_command(command, &options.arguments)
|
||||
};
|
||||
|
||||
show_error!("{}", report_full_error(&err));
|
||||
return exit_status;
|
||||
get_plain_context(context)
|
||||
.and_then(set_next_exec_context)
|
||||
.map_err(RunconError::new)?;
|
||||
// On successful execution, the following call never returns,
|
||||
// and this process image is replaced.
|
||||
execute_command(command, &options.arguments)
|
||||
}
|
||||
|
||||
CommandLineMode::CustomContext {
|
||||
compute_transition_context,
|
||||
user,
|
||||
|
@ -106,34 +87,26 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
range,
|
||||
command,
|
||||
} => {
|
||||
if let Some(command) = command {
|
||||
let (exit_status, err) = if let Err(err) = get_custom_context(
|
||||
*compute_transition_context,
|
||||
user.as_deref(),
|
||||
role.as_deref(),
|
||||
the_type.as_deref(),
|
||||
range.as_deref(),
|
||||
command,
|
||||
)
|
||||
.and_then(set_next_exec_context)
|
||||
{
|
||||
(error_exit_status::ANOTHER_ERROR, err)
|
||||
} else {
|
||||
match command {
|
||||
Some(command) => {
|
||||
get_custom_context(
|
||||
*compute_transition_context,
|
||||
user.as_deref(),
|
||||
role.as_deref(),
|
||||
the_type.as_deref(),
|
||||
range.as_deref(),
|
||||
command,
|
||||
)
|
||||
.and_then(set_next_exec_context)
|
||||
.map_err(RunconError::new)?;
|
||||
// On successful execution, the following call never returns,
|
||||
// and this process image is replaced.
|
||||
execute_command(command, &options.arguments)
|
||||
};
|
||||
|
||||
show_error!("{}", report_full_error(&err));
|
||||
return exit_status;
|
||||
} else if let Err(r) = print_current_context() {
|
||||
show_error!("{}", report_full_error(&r));
|
||||
return error_exit_status::ANOTHER_ERROR;
|
||||
}
|
||||
None => print_current_context().map_err(|e| RunconError::new(e).into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
error_exit_status::SUCCESS
|
||||
}
|
||||
|
||||
pub fn uu_app() -> App<'static, 'static> {
|
||||
|
@ -406,25 +379,19 @@ fn get_custom_context(
|
|||
Ok(osc)
|
||||
}
|
||||
|
||||
/// The actual return type of this function should be `Result<!, (i32, Error)>`
|
||||
/// The actual return type of this function should be `UResult<!>`.
|
||||
/// However, until the *never* type is stabilized, one way to indicate to the
|
||||
/// compiler the only valid return type is to say "if this returns, it will
|
||||
/// always return an error".
|
||||
fn execute_command(command: &OsStr, arguments: &[OsString]) -> (i32, Error) {
|
||||
let c_command = match os_str_to_c_string(command) {
|
||||
Ok(v) => v,
|
||||
Err(r) => return (error_exit_status::ANOTHER_ERROR, r),
|
||||
};
|
||||
fn execute_command(command: &OsStr, arguments: &[OsString]) -> UResult<()> {
|
||||
let c_command = os_str_to_c_string(command).map_err(RunconError::new)?;
|
||||
|
||||
let argv_storage: Vec<CString> = match arguments
|
||||
let argv_storage: Vec<CString> = arguments
|
||||
.iter()
|
||||
.map(AsRef::as_ref)
|
||||
.map(os_str_to_c_string)
|
||||
.collect::<Result<_>>()
|
||||
{
|
||||
Ok(v) => v,
|
||||
Err(r) => return (error_exit_status::ANOTHER_ERROR, r),
|
||||
};
|
||||
.map_err(RunconError::new)?;
|
||||
|
||||
let mut argv: Vec<*const c_char> = Vec::with_capacity(arguments.len().saturating_add(2));
|
||||
argv.push(c_command.as_ptr());
|
||||
|
@ -441,7 +408,7 @@ fn execute_command(command: &OsStr, arguments: &[OsString]) -> (i32, Error) {
|
|||
};
|
||||
|
||||
let err = Error::from_io1("Executing command", command, err);
|
||||
(exit_status, err)
|
||||
Err(RunconError::with_code(exit_status, err).into())
|
||||
}
|
||||
|
||||
fn os_str_to_c_string(s: &OsStr) -> Result<CString> {
|
||||
|
|
|
@ -2,6 +2,8 @@ use std::env;
|
|||
use std::io::Write;
|
||||
use std::io::{BufWriter, Result};
|
||||
use std::process::{Child, Command, Stdio};
|
||||
use uucore::crash;
|
||||
|
||||
/// A writer that writes to a shell_process' stdin
|
||||
///
|
||||
/// We use a shell process (not directly calling a sub-process) so we can forward the name of the
|
||||
|
|
|
@ -7,9 +7,6 @@
|
|||
|
||||
// spell-checker:ignore (ToDO) PREFIXaa
|
||||
|
||||
#[macro_use]
|
||||
extern crate uucore;
|
||||
|
||||
mod platform;
|
||||
|
||||
use clap::{crate_version, App, Arg, ArgMatches};
|
||||
|
@ -20,6 +17,7 @@ use std::io::{stdin, BufRead, BufReader, BufWriter, Read, Write};
|
|||
use std::path::Path;
|
||||
use std::{char, fs::remove_file};
|
||||
use uucore::display::Quotable;
|
||||
use uucore::error::{FromIo, UResult, USimpleError, UUsageError};
|
||||
use uucore::parse_size::parse_size;
|
||||
|
||||
static OPT_BYTES: &str = "bytes";
|
||||
|
@ -53,7 +51,8 @@ size is 1000, and default PREFIX is 'x'. With no INPUT, or when INPUT is
|
|||
)
|
||||
}
|
||||
|
||||
pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||
#[uucore_procs::gen_uumain]
|
||||
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
let usage = usage();
|
||||
let long_usage = get_long_usage();
|
||||
|
||||
|
@ -83,15 +82,17 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
settings.additional_suffix = matches.value_of(OPT_ADDITIONAL_SUFFIX).unwrap().to_owned();
|
||||
|
||||
settings.verbose = matches.occurrences_of("verbose") > 0;
|
||||
settings.strategy = Strategy::from(&matches);
|
||||
settings.strategy = Strategy::from(&matches)?;
|
||||
settings.input = matches.value_of(ARG_INPUT).unwrap().to_owned();
|
||||
settings.prefix = matches.value_of(ARG_PREFIX).unwrap().to_owned();
|
||||
|
||||
if matches.occurrences_of(OPT_FILTER) > 0 {
|
||||
if cfg!(windows) {
|
||||
// see https://github.com/rust-lang/rust/issues/29494
|
||||
show_error!("{} is currently not supported in this platform", OPT_FILTER);
|
||||
std::process::exit(-1);
|
||||
return Err(USimpleError::new(
|
||||
-1,
|
||||
format!("{} is currently not supported in this platform", OPT_FILTER),
|
||||
));
|
||||
} else {
|
||||
settings.filter = Some(matches.value_of(OPT_FILTER).unwrap().to_owned());
|
||||
}
|
||||
|
@ -193,7 +194,7 @@ enum Strategy {
|
|||
|
||||
impl Strategy {
|
||||
/// Parse a strategy from the command-line arguments.
|
||||
fn from(matches: &ArgMatches) -> Self {
|
||||
fn from(matches: &ArgMatches) -> UResult<Self> {
|
||||
// Check that the user is not specifying more than one strategy.
|
||||
//
|
||||
// Note: right now, this exact behavior cannot be handled by
|
||||
|
@ -204,26 +205,26 @@ impl Strategy {
|
|||
matches.occurrences_of(OPT_BYTES),
|
||||
matches.occurrences_of(OPT_LINE_BYTES),
|
||||
) {
|
||||
(0, 0, 0) => Strategy::Lines(1000),
|
||||
(0, 0, 0) => Ok(Strategy::Lines(1000)),
|
||||
(1, 0, 0) => {
|
||||
let s = matches.value_of(OPT_LINES).unwrap();
|
||||
let n =
|
||||
parse_size(s).unwrap_or_else(|e| crash!(1, "invalid number of lines: {}", e));
|
||||
Strategy::Lines(n)
|
||||
let n = parse_size(s)
|
||||
.map_err(|e| USimpleError::new(1, format!("invalid number of lines: {}", e)))?;
|
||||
Ok(Strategy::Lines(n))
|
||||
}
|
||||
(0, 1, 0) => {
|
||||
let s = matches.value_of(OPT_BYTES).unwrap();
|
||||
let n =
|
||||
parse_size(s).unwrap_or_else(|e| crash!(1, "invalid number of bytes: {}", e));
|
||||
Strategy::Bytes(n)
|
||||
let n = parse_size(s)
|
||||
.map_err(|e| USimpleError::new(1, format!("invalid number of bytes: {}", e)))?;
|
||||
Ok(Strategy::Bytes(n))
|
||||
}
|
||||
(0, 0, 1) => {
|
||||
let s = matches.value_of(OPT_LINE_BYTES).unwrap();
|
||||
let n =
|
||||
parse_size(s).unwrap_or_else(|e| crash!(1, "invalid number of bytes: {}", e));
|
||||
Strategy::LineBytes(n)
|
||||
let n = parse_size(s)
|
||||
.map_err(|e| USimpleError::new(1, format!("invalid number of bytes: {}", e)))?;
|
||||
Ok(Strategy::LineBytes(n))
|
||||
}
|
||||
_ => crash!(1, "cannot split in more than one way"),
|
||||
_ => Err(UUsageError::new(1, "cannot split in more than one way")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -249,7 +250,7 @@ trait Splitter {
|
|||
&mut self,
|
||||
reader: &mut BufReader<Box<dyn Read>>,
|
||||
writer: &mut BufWriter<Box<dyn Write>>,
|
||||
) -> u128;
|
||||
) -> std::io::Result<u128>;
|
||||
}
|
||||
|
||||
struct LineSplitter {
|
||||
|
@ -269,21 +270,17 @@ impl Splitter for LineSplitter {
|
|||
&mut self,
|
||||
reader: &mut BufReader<Box<dyn Read>>,
|
||||
writer: &mut BufWriter<Box<dyn Write>>,
|
||||
) -> u128 {
|
||||
) -> std::io::Result<u128> {
|
||||
let mut bytes_consumed = 0u128;
|
||||
let mut buffer = String::with_capacity(1024);
|
||||
for _ in 0..self.lines_per_split {
|
||||
let bytes_read = reader
|
||||
.read_line(&mut buffer)
|
||||
.unwrap_or_else(|_| crash!(1, "error reading bytes from input file"));
|
||||
let bytes_read = reader.read_line(&mut buffer)?;
|
||||
// If we ever read 0 bytes then we know we've hit EOF.
|
||||
if bytes_read == 0 {
|
||||
return bytes_consumed;
|
||||
return Ok(bytes_consumed);
|
||||
}
|
||||
|
||||
writer
|
||||
.write_all(buffer.as_bytes())
|
||||
.unwrap_or_else(|_| crash!(1, "error writing bytes to output file"));
|
||||
writer.write_all(buffer.as_bytes())?;
|
||||
// Empty out the String buffer since `read_line` appends instead of
|
||||
// replaces.
|
||||
buffer.clear();
|
||||
|
@ -291,7 +288,7 @@ impl Splitter for LineSplitter {
|
|||
bytes_consumed += bytes_read as u128;
|
||||
}
|
||||
|
||||
bytes_consumed
|
||||
Ok(bytes_consumed)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -312,7 +309,7 @@ impl Splitter for ByteSplitter {
|
|||
&mut self,
|
||||
reader: &mut BufReader<Box<dyn Read>>,
|
||||
writer: &mut BufWriter<Box<dyn Write>>,
|
||||
) -> u128 {
|
||||
) -> std::io::Result<u128> {
|
||||
// We buffer reads and writes. We proceed until `bytes_consumed` is
|
||||
// equal to `self.bytes_per_split` or we reach EOF.
|
||||
let mut bytes_consumed = 0u128;
|
||||
|
@ -329,22 +326,18 @@ impl Splitter for ByteSplitter {
|
|||
// than BUFFER_SIZE in this branch.
|
||||
(self.bytes_per_split - bytes_consumed) as usize
|
||||
};
|
||||
let bytes_read = reader
|
||||
.read(&mut buffer[0..bytes_desired])
|
||||
.unwrap_or_else(|_| crash!(1, "error reading bytes from input file"));
|
||||
let bytes_read = reader.read(&mut buffer[0..bytes_desired])?;
|
||||
// If we ever read 0 bytes then we know we've hit EOF.
|
||||
if bytes_read == 0 {
|
||||
return bytes_consumed;
|
||||
return Ok(bytes_consumed);
|
||||
}
|
||||
|
||||
writer
|
||||
.write_all(&buffer[0..bytes_read])
|
||||
.unwrap_or_else(|_| crash!(1, "error writing bytes to output file"));
|
||||
writer.write_all(&buffer[0..bytes_read])?;
|
||||
|
||||
bytes_consumed += bytes_read as u128;
|
||||
}
|
||||
|
||||
bytes_consumed
|
||||
Ok(bytes_consumed)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -380,17 +373,16 @@ fn num_prefix(i: usize, width: usize) -> String {
|
|||
c
|
||||
}
|
||||
|
||||
fn split(settings: &Settings) -> i32 {
|
||||
fn split(settings: &Settings) -> UResult<()> {
|
||||
let mut reader = BufReader::new(if settings.input == "-" {
|
||||
Box::new(stdin()) as Box<dyn Read>
|
||||
} else {
|
||||
let r = File::open(Path::new(&settings.input)).unwrap_or_else(|_| {
|
||||
crash!(
|
||||
1,
|
||||
let r = File::open(Path::new(&settings.input)).map_err_context(|| {
|
||||
format!(
|
||||
"cannot open {} for reading: No such file or directory",
|
||||
settings.input.quote()
|
||||
)
|
||||
});
|
||||
})?;
|
||||
Box::new(r) as Box<dyn Read>
|
||||
});
|
||||
|
||||
|
@ -416,10 +408,12 @@ fn split(settings: &Settings) -> i32 {
|
|||
filename.push_str(settings.additional_suffix.as_ref());
|
||||
let mut writer = platform::instantiate_current_writer(&settings.filter, filename.as_str());
|
||||
|
||||
let bytes_consumed = splitter.consume(&mut reader, &mut writer);
|
||||
let bytes_consumed = splitter
|
||||
.consume(&mut reader, &mut writer)
|
||||
.map_err_context(|| "input/output error".to_string())?;
|
||||
writer
|
||||
.flush()
|
||||
.unwrap_or_else(|e| crash!(1, "error flushing to output file: {}", e));
|
||||
.map_err_context(|| "error flushing to output file".to_string())?;
|
||||
|
||||
// If we didn't write anything we should clean up the empty file, and
|
||||
// break from the loop.
|
||||
|
@ -428,12 +422,12 @@ fn split(settings: &Settings) -> i32 {
|
|||
// Complicated, I know...
|
||||
if settings.filter.is_none() {
|
||||
remove_file(filename)
|
||||
.unwrap_or_else(|e| crash!(1, "error removing empty file: {}", e));
|
||||
.map_err_context(|| "error removing empty file".to_string())?;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
fileno += 1;
|
||||
}
|
||||
0
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ use std::io::ErrorKind;
|
|||
use std::process::{Command, Stdio};
|
||||
use std::time::Duration;
|
||||
use uucore::display::Quotable;
|
||||
use uucore::error::{UResult, USimpleError};
|
||||
use uucore::process::ChildExt;
|
||||
use uucore::signals::{signal_by_name_or_value, signal_name_by_value};
|
||||
use uucore::InvalidEncodingHandling;
|
||||
|
@ -99,7 +100,8 @@ impl Config {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||
#[uucore_procs::gen_uumain]
|
||||
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
let args = args
|
||||
.collect_str(InvalidEncodingHandling::ConvertLossy)
|
||||
.accept_any();
|
||||
|
@ -188,32 +190,36 @@ fn timeout(
|
|||
foreground: bool,
|
||||
preserve_status: bool,
|
||||
verbose: bool,
|
||||
) -> i32 {
|
||||
) -> UResult<()> {
|
||||
if !foreground {
|
||||
unsafe { libc::setpgid(0, 0) };
|
||||
}
|
||||
let mut process = match Command::new(&cmd[0])
|
||||
let mut process = Command::new(&cmd[0])
|
||||
.args(&cmd[1..])
|
||||
.stdin(Stdio::inherit())
|
||||
.stdout(Stdio::inherit())
|
||||
.stderr(Stdio::inherit())
|
||||
.spawn()
|
||||
{
|
||||
Ok(p) => p,
|
||||
Err(err) => {
|
||||
show_error!("failed to execute process: {}", err);
|
||||
if err.kind() == ErrorKind::NotFound {
|
||||
.map_err(|err| {
|
||||
let status_code = if err.kind() == ErrorKind::NotFound {
|
||||
// FIXME: not sure which to use
|
||||
return 127;
|
||||
127
|
||||
} else {
|
||||
// FIXME: this may not be 100% correct...
|
||||
return 126;
|
||||
}
|
||||
}
|
||||
};
|
||||
126
|
||||
};
|
||||
USimpleError::new(status_code, format!("failed to execute process: {}", err))
|
||||
})?;
|
||||
unblock_sigchld();
|
||||
match process.wait_or_timeout(duration) {
|
||||
Ok(Some(status)) => status.code().unwrap_or_else(|| status.signal().unwrap()),
|
||||
Ok(Some(status)) => {
|
||||
let status_code = status.code().unwrap_or_else(|| status.signal().unwrap());
|
||||
if status_code == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(status_code.into())
|
||||
}
|
||||
}
|
||||
Ok(None) => {
|
||||
if verbose {
|
||||
show_error!(
|
||||
|
@ -222,38 +228,50 @@ fn timeout(
|
|||
cmd[0].quote()
|
||||
);
|
||||
}
|
||||
crash_if_err!(ERR_EXIT_STATUS, process.send_signal(signal));
|
||||
process
|
||||
.send_signal(signal)
|
||||
.map_err(|e| USimpleError::new(ERR_EXIT_STATUS, format!("{}", e)))?;
|
||||
if let Some(kill_after) = kill_after {
|
||||
match process.wait_or_timeout(kill_after) {
|
||||
Ok(Some(status)) => {
|
||||
if preserve_status {
|
||||
status.code().unwrap_or_else(|| status.signal().unwrap())
|
||||
let status_code =
|
||||
status.code().unwrap_or_else(|| status.signal().unwrap());
|
||||
if status_code == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(status_code.into())
|
||||
}
|
||||
} else {
|
||||
124
|
||||
Err(124.into())
|
||||
}
|
||||
}
|
||||
Ok(None) => {
|
||||
if verbose {
|
||||
show_error!("sending signal KILL to command {}", cmd[0].quote());
|
||||
}
|
||||
crash_if_err!(
|
||||
ERR_EXIT_STATUS,
|
||||
process.send_signal(
|
||||
uucore::signals::signal_by_name_or_value("KILL").unwrap()
|
||||
)
|
||||
);
|
||||
crash_if_err!(ERR_EXIT_STATUS, process.wait());
|
||||
137
|
||||
process
|
||||
.send_signal(uucore::signals::signal_by_name_or_value("KILL").unwrap())
|
||||
.map_err(|e| USimpleError::new(ERR_EXIT_STATUS, format!("{}", e)))?;
|
||||
process
|
||||
.wait()
|
||||
.map_err(|e| USimpleError::new(ERR_EXIT_STATUS, format!("{}", e)))?;
|
||||
Err(137.into())
|
||||
}
|
||||
Err(_) => 124,
|
||||
Err(_) => Err(124.into()),
|
||||
}
|
||||
} else {
|
||||
124
|
||||
Err(124.into())
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
crash_if_err!(ERR_EXIT_STATUS, process.send_signal(signal));
|
||||
ERR_EXIT_STATUS
|
||||
// We're going to return ERR_EXIT_STATUS regardless of
|
||||
// whether `send_signal()` succeeds or fails, so just
|
||||
// ignore the return value.
|
||||
process
|
||||
.send_signal(signal)
|
||||
.map_err(|e| USimpleError::new(ERR_EXIT_STATUS, format!("{}", e)))?;
|
||||
Err(ERR_EXIT_STATUS.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,16 +5,13 @@
|
|||
// *
|
||||
// * For the full copyright and license information, please view the LICENSE
|
||||
// * file that was distributed with this source code.
|
||||
|
||||
#[macro_use]
|
||||
extern crate uucore;
|
||||
|
||||
use clap::{crate_version, App, Arg};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::fs::File;
|
||||
use std::io::{stdin, BufRead, BufReader, Read};
|
||||
use std::path::Path;
|
||||
use uucore::display::Quotable;
|
||||
use uucore::error::{FromIo, UResult, USimpleError};
|
||||
use uucore::InvalidEncodingHandling;
|
||||
|
||||
static SUMMARY: &str = "Topological sort the strings in FILE.
|
||||
|
@ -26,7 +23,8 @@ mod options {
|
|||
pub const FILE: &str = "file";
|
||||
}
|
||||
|
||||
pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||
#[uucore_procs::gen_uumain]
|
||||
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
let args = args
|
||||
.collect_str(InvalidEncodingHandling::ConvertLossy)
|
||||
.accept_any();
|
||||
|
@ -43,13 +41,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
stdin_buf = stdin();
|
||||
&mut stdin_buf as &mut dyn Read
|
||||
} else {
|
||||
file_buf = match File::open(Path::new(&input)) {
|
||||
Ok(a) => a,
|
||||
_ => {
|
||||
show_error!("{}: No such file or directory", input.maybe_quote());
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
file_buf = File::open(Path::new(&input)).map_err_context(|| input.to_string())?;
|
||||
&mut file_buf as &mut dyn Read
|
||||
});
|
||||
|
||||
|
@ -69,11 +61,15 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
for ab in tokens.chunks(2) {
|
||||
match ab.len() {
|
||||
2 => g.add_edge(&ab[0], &ab[1]),
|
||||
_ => crash!(
|
||||
1,
|
||||
"{}: input contains an odd number of tokens",
|
||||
input.maybe_quote()
|
||||
),
|
||||
_ => {
|
||||
return Err(USimpleError::new(
|
||||
1,
|
||||
format!(
|
||||
"{}: input contains an odd number of tokens",
|
||||
input.maybe_quote()
|
||||
),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -84,14 +80,17 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
g.run_tsort();
|
||||
|
||||
if !g.is_acyclic() {
|
||||
crash!(1, "{}, input contains a loop:", input);
|
||||
return Err(USimpleError::new(
|
||||
1,
|
||||
format!("{}, input contains a loop:", input),
|
||||
));
|
||||
}
|
||||
|
||||
for x in &g.result {
|
||||
println!("{}", x);
|
||||
}
|
||||
|
||||
0
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn uu_app() -> App<'static, 'static> {
|
||||
|
|
|
@ -371,7 +371,7 @@ impl UError for UUsageError {
|
|||
/// ```
|
||||
#[derive(Debug)]
|
||||
pub struct UIoError {
|
||||
context: String,
|
||||
context: Option<String>,
|
||||
inner: std::io::Error,
|
||||
}
|
||||
|
||||
|
@ -379,7 +379,7 @@ impl UIoError {
|
|||
#[allow(clippy::new_ret_no_self)]
|
||||
pub fn new<S: Into<String>>(kind: std::io::ErrorKind, context: S) -> Box<dyn UError> {
|
||||
Box::new(Self {
|
||||
context: context.into(),
|
||||
context: Some(context.into()),
|
||||
inner: kind.into(),
|
||||
})
|
||||
}
|
||||
|
@ -435,7 +435,11 @@ impl Display for UIoError {
|
|||
capitalize(&mut message);
|
||||
&message
|
||||
};
|
||||
write!(f, "{}: {}", self.context, message)
|
||||
if let Some(ctx) = &self.context {
|
||||
write!(f, "{}: {}", ctx, message)
|
||||
} else {
|
||||
write!(f, "{}", message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -464,7 +468,7 @@ pub trait FromIo<T> {
|
|||
impl FromIo<Box<UIoError>> for std::io::Error {
|
||||
fn map_err_context(self, context: impl FnOnce() -> String) -> Box<UIoError> {
|
||||
Box::new(UIoError {
|
||||
context: (context)(),
|
||||
context: Some((context)()),
|
||||
inner: self,
|
||||
})
|
||||
}
|
||||
|
@ -479,12 +483,28 @@ impl<T> FromIo<UResult<T>> for std::io::Result<T> {
|
|||
impl FromIo<Box<UIoError>> for std::io::ErrorKind {
|
||||
fn map_err_context(self, context: impl FnOnce() -> String) -> Box<UIoError> {
|
||||
Box::new(UIoError {
|
||||
context: (context)(),
|
||||
context: Some((context)()),
|
||||
inner: std::io::Error::new(self, ""),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::io::Error> for UIoError {
|
||||
fn from(f: std::io::Error) -> UIoError {
|
||||
UIoError {
|
||||
context: None,
|
||||
inner: f,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::io::Error> for Box<dyn UError> {
|
||||
fn from(f: std::io::Error) -> Box<dyn UError> {
|
||||
let u_error: UIoError = f.into();
|
||||
Box::new(u_error) as Box<dyn UError>
|
||||
}
|
||||
}
|
||||
|
||||
/// Shorthand to construct [`UIoError`]-instances.
|
||||
///
|
||||
/// This macro serves as a convenience call to quickly construct instances of
|
||||
|
|
|
@ -74,9 +74,8 @@ fn test_invalid_file() {
|
|||
at.mkdir(folder_name);
|
||||
ts.ucmd()
|
||||
.arg(folder_name)
|
||||
.fails()
|
||||
.no_stdout()
|
||||
.stderr_contains("cksum: asdf: Is a directory");
|
||||
.succeeds()
|
||||
.stdout_only("4294967295 0 asdf\n");
|
||||
}
|
||||
|
||||
// Make sure crc is correct for files larger than 32 bytes
|
||||
|
|
|
@ -107,6 +107,28 @@ fn test_ls_io_errors() {
|
|||
.stdout_contains("some-dir4");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ls_only_dirs_formatting() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let at = &scene.fixtures;
|
||||
at.mkdir("some-dir1");
|
||||
at.mkdir("some-dir2");
|
||||
at.mkdir("some-dir3");
|
||||
|
||||
#[cfg(unix)]
|
||||
{
|
||||
scene.ucmd().arg("-1").arg("-R").succeeds().stdout_only(
|
||||
".:\nsome-dir1\nsome-dir2\nsome-dir3\n\n./some-dir1:\n\n./some-dir2:\n\n./some-dir3:\n",
|
||||
);
|
||||
}
|
||||
#[cfg(windows)]
|
||||
{
|
||||
scene.ucmd().arg("-1").arg("-R").succeeds().stdout_only(
|
||||
".:\nsome-dir1\nsome-dir2\nsome-dir3\n\n.\\some-dir1:\n\n.\\some-dir2:\n\n.\\some-dir3:\n",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ls_walk_glob() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue