1
Fork 0
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:
kimono-koans 2022-01-07 21:48:53 -06:00 committed by GitHub
commit 4ea61545c6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 291 additions and 244 deletions

View file

@ -11,8 +11,7 @@ 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::display::Quotable;
use uucore::error::UResult; use uucore::error::{FromIo, UResult};
use uucore::error::USimpleError;
use uucore::show; use uucore::show;
use uucore::InvalidEncodingHandling; use uucore::InvalidEncodingHandling;
@ -81,27 +80,18 @@ fn cksum(fname: &str) -> io::Result<(u32, usize)> {
let mut crc = 0u32; let mut crc = 0u32;
let mut size = 0usize; let mut size = 0usize;
let file;
let mut rd: Box<dyn Read> = match fname { let mut rd: Box<dyn Read> = match fname {
"-" => Box::new(stdin()), "-" => Box::new(stdin()),
_ => { _ => {
let path = &Path::new(fname); let p = Path::new(fname);
if path.is_dir() {
return Err(std::io::Error::new( // Directories should not give an error, but should be interpreted
io::ErrorKind::InvalidInput, // as empty files to match GNU semantics.
"Is a directory", if p.is_dir() {
)); Box::new(BufReader::new(io::empty())) as Box<dyn Read>
}; } else {
// Silent the warning as we want to the error message Box::new(BufReader::new(File::open(p)?)) as Box<dyn Read>
#[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))
} }
}; };
@ -136,23 +126,16 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
}; };
if files.is_empty() { if files.is_empty() {
match cksum("-") { let (crc, size) = cksum("-")?;
Ok((crc, size)) => println!("{} {}", crc, size), println!("{} {}", crc, size);
Err(err) => {
return Err(USimpleError::new(2, format!("{}", err)));
}
}
return Ok(()); return Ok(());
} }
for fname in &files { 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), Ok((crc, size)) => println!("{} {} {}", crc, size, fname),
Err(err) => show!(USimpleError::new( Err(err) => show!(err),
2, };
format!("{}: {}", fname.maybe_quote(), err)
)),
}
} }
Ok(()) Ok(())
} }

View file

@ -1393,8 +1393,16 @@ fn list(locs: Vec<&Path>, config: Config) -> UResult<()> {
display_items(&files, &config, &mut out); display_items(&files, &config, &mut out);
for dir in &dirs { for (pos, dir) in dirs.iter().enumerate() {
enter_directory(dir, &config, initial_locs_len, &mut out); // 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(()) Ok(())
@ -1464,12 +1472,7 @@ fn should_display(entry: &DirEntry, config: &Config) -> bool {
true true
} }
fn enter_directory( fn enter_directory(dir: &PathData, config: &Config, out: &mut BufWriter<Stdout>) {
dir: &PathData,
config: &Config,
initial_locs_len: usize,
out: &mut BufWriter<Stdout>,
) {
// Create vec of entries with initial dot files // Create vec of entries with initial dot files
let mut entries: Vec<PathData> = if config.files == Files::All { let mut entries: Vec<PathData> = if config.files == Files::All {
vec![ vec![
@ -1524,10 +1527,6 @@ fn enter_directory(
sort_entries(&mut vec_path_data, config, out); sort_entries(&mut vec_path_data, config, out);
entries.append(&mut vec_path_data); 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 // ...and total
if config.format == Format::Long { if config.format == Format::Long {
display_total(&entries, config, out); 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().is_some())
.filter(|p| p.ft.get().unwrap().unwrap().is_dir()) .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);
} }
} }
} }

View file

@ -25,6 +25,7 @@ use std::io::{stdin, stdout, BufRead, BufReader, Lines, Read, Write};
use std::os::unix::fs::FileTypeExt; use std::os::unix::fs::FileTypeExt;
use uucore::display::Quotable; use uucore::display::Quotable;
use uucore::error::UResult;
type IOError = std::io::Error; type IOError = std::io::Error;
@ -174,7 +175,8 @@ pub fn uu_app() -> clap::App<'static, 'static> {
clap::App::new(uucore::util_name()) 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 let args = args
.collect_str(uucore::InvalidEncodingHandling::Ignore) .collect_str(uucore::InvalidEncodingHandling::Ignore)
.accept_any(); .accept_any();
@ -388,7 +390,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
if matches.opt_present("version") { if matches.opt_present("version") {
println!("{} {}", NAME, VERSION); println!("{} {}", NAME, VERSION);
return 0; return Ok(());
} }
let mut files = matches.free.clone(); let mut files = matches.free.clone();
@ -412,7 +414,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
Ok(options) => options, Ok(options) => options,
Err(err) => { Err(err) => {
print_error(&matches, err); print_error(&matches, err);
return 1; return Err(1.into());
} }
}; };
@ -430,11 +432,10 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
_ => 0, _ => 0,
}; };
if status != 0 { if status != 0 {
return status; return Err(status.into());
} }
} }
Ok(())
0
} }
/// Returns re-written arguments which are passed to the program. /// 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!("{} {} -- print files", NAME, VERSION);
println!(); println!();
println!( println!(
@ -508,10 +509,9 @@ fn print_usage(opts: &mut getopts::Options, matches: &Matches) -> i32 {
options::COLUMN_OPTION options::COLUMN_OPTION
); );
if matches.free.is_empty() { if matches.free.is_empty() {
return 1; return Err(1.into());
} }
Ok(())
0
} }
fn parse_usize(matches: &Matches, opt: &str) -> Option<Result<usize, PrError>> { fn parse_usize(matches: &Matches, opt: &str) -> Option<Result<usize, PrError>> {

View file

@ -17,6 +17,7 @@ use std::{
}; };
use uucore::{ use uucore::{
display::{print_verbatim, Quotable}, display::{print_verbatim, Quotable},
error::{FromIo, UResult},
fs::{canonicalize, MissingHandling, ResolveMode}, fs::{canonicalize, MissingHandling, ResolveMode},
}; };
@ -36,7 +37,8 @@ fn usage() -> String {
format!("{0} [OPTION]... FILE...", uucore::execution_phrase()) 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 usage = usage();
let matches = uu_app().usage(&usage[..]).get_matches_from(args); let matches = uu_app().usage(&usage[..]).get_matches_from(args);
@ -60,16 +62,16 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
} else { } else {
MissingHandling::Normal MissingHandling::Normal
}; };
let mut retcode = 0;
for path in &paths { for path in &paths {
if let Err(e) = resolve_path(path, strip, zero, logical, can_mode) { let result = resolve_path(path, strip, zero, logical, can_mode);
if !quiet { if !quiet {
show_error!("{}: {}", path.maybe_quote(), e); show_if_err!(result.map_err_context(|| path.maybe_quote().to_string()));
} }
retcode = 1
};
} }
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> { pub fn uu_app() -> App<'static, 'static> {

View file

@ -1,12 +1,22 @@
use std::ffi::OsString; use std::ffi::OsString;
use std::fmt::Write; use std::fmt::{Display, Formatter, Write};
use std::io; use std::io;
use std::str::Utf8Error; use std::str::Utf8Error;
use uucore::display::Quotable; use uucore::display::Quotable;
use uucore::error::UError;
pub(crate) type Result<T> = std::result::Result<T, Error>; 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)] #[derive(thiserror::Error, Debug)]
pub(crate) enum Error { pub(crate) enum Error {
#[error("No command is specified")] #[error("No command is specified")]
@ -63,13 +73,44 @@ impl Error {
} }
} }
pub(crate) fn report_full_error(mut err: &dyn std::error::Error) -> String { pub(crate) fn write_full_error<W>(writer: &mut W, err: &dyn std::error::Error) -> std::fmt::Result
let mut desc = String::with_capacity(256); where
write!(&mut desc, "{}", err).unwrap(); W: Write,
{
write!(writer, "{}", err)?;
let mut err = err;
while let Some(source) = err.source() { while let Some(source) = err.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
} }

View file

@ -1,6 +1,6 @@
// spell-checker:ignore (vars) RFILE // spell-checker:ignore (vars) RFILE
use uucore::{show_error, show_usage_error}; use uucore::error::{UResult, UUsageError};
use clap::{App, Arg}; use clap::{App, Arg};
use selinux::{OpaqueSecurityContext, SecurityClass, SecurityContext}; use selinux::{OpaqueSecurityContext, SecurityClass, SecurityContext};
@ -13,7 +13,8 @@ use std::{io, ptr};
mod errors; 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 VERSION: &str = env!("CARGO_PKG_VERSION");
const ABOUT: &str = "Run command with specified security context."; const ABOUT: &str = "Run command with specified security context.";
@ -35,16 +36,6 @@ pub mod options {
pub const RANGE: &str = "range"; 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 { fn get_usage() -> String {
format!( format!(
"{0} [CONTEXT COMMAND [ARG...]]\n \ "{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 usage = get_usage();
let config = uu_app().usage(usage.as_ref()); let config = uu_app().usage(usage.as_ref());
@ -65,39 +57,28 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
match r.kind { match r.kind {
clap::ErrorKind::HelpDisplayed | clap::ErrorKind::VersionDisplayed => { clap::ErrorKind::HelpDisplayed | clap::ErrorKind::VersionDisplayed => {
println!("{}", r); println!("{}", r);
return error_exit_status::SUCCESS; return Ok(());
} }
_ => {} _ => {}
} }
} }
return Err(UUsageError::new(
show_usage_error!("{}.\n", r); error_exit_status::ANOTHER_ERROR,
return error_exit_status::ANOTHER_ERROR; format!("{}", r),
));
} }
}; };
match &options.mode { match &options.mode {
CommandLineMode::Print => { CommandLineMode::Print => print_current_context().map_err(|e| RunconError::new(e).into()),
if let Err(r) = print_current_context() {
show_error!("{}", report_full_error(&r));
return error_exit_status::ANOTHER_ERROR;
}
}
CommandLineMode::PlainContext { context, command } => { CommandLineMode::PlainContext { context, command } => {
let (exit_status, err) = get_plain_context(context)
if let Err(err) = get_plain_context(context).and_then(set_next_exec_context) { .and_then(set_next_exec_context)
(error_exit_status::ANOTHER_ERROR, err) .map_err(RunconError::new)?;
} else { // On successful execution, the following call never returns,
// On successful execution, the following call never returns, // and this process image is replaced.
// and this process image is replaced. execute_command(command, &options.arguments)
execute_command(command, &options.arguments)
};
show_error!("{}", report_full_error(&err));
return exit_status;
} }
CommandLineMode::CustomContext { CommandLineMode::CustomContext {
compute_transition_context, compute_transition_context,
user, user,
@ -106,34 +87,26 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
range, range,
command, command,
} => { } => {
if let Some(command) = command { match command {
let (exit_status, err) = if let Err(err) = get_custom_context( Some(command) => {
*compute_transition_context, get_custom_context(
user.as_deref(), *compute_transition_context,
role.as_deref(), user.as_deref(),
the_type.as_deref(), role.as_deref(),
range.as_deref(), the_type.as_deref(),
command, range.as_deref(),
) command,
.and_then(set_next_exec_context) )
{ .and_then(set_next_exec_context)
(error_exit_status::ANOTHER_ERROR, err) .map_err(RunconError::new)?;
} else {
// On successful execution, the following call never returns, // On successful execution, the following call never returns,
// and this process image is replaced. // and this process image is replaced.
execute_command(command, &options.arguments) execute_command(command, &options.arguments)
}; }
None => print_current_context().map_err(|e| RunconError::new(e).into()),
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;
} }
} }
} }
error_exit_status::SUCCESS
} }
pub fn uu_app() -> App<'static, 'static> { pub fn uu_app() -> App<'static, 'static> {
@ -406,25 +379,19 @@ fn get_custom_context(
Ok(osc) 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 /// 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 /// compiler the only valid return type is to say "if this returns, it will
/// always return an error". /// always return an error".
fn execute_command(command: &OsStr, arguments: &[OsString]) -> (i32, Error) { fn execute_command(command: &OsStr, arguments: &[OsString]) -> UResult<()> {
let c_command = match os_str_to_c_string(command) { let c_command = os_str_to_c_string(command).map_err(RunconError::new)?;
Ok(v) => v,
Err(r) => return (error_exit_status::ANOTHER_ERROR, r),
};
let argv_storage: Vec<CString> = match arguments let argv_storage: Vec<CString> = arguments
.iter() .iter()
.map(AsRef::as_ref) .map(AsRef::as_ref)
.map(os_str_to_c_string) .map(os_str_to_c_string)
.collect::<Result<_>>() .collect::<Result<_>>()
{ .map_err(RunconError::new)?;
Ok(v) => v,
Err(r) => return (error_exit_status::ANOTHER_ERROR, r),
};
let mut argv: Vec<*const c_char> = Vec::with_capacity(arguments.len().saturating_add(2)); let mut argv: Vec<*const c_char> = Vec::with_capacity(arguments.len().saturating_add(2));
argv.push(c_command.as_ptr()); 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); 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> { fn os_str_to_c_string(s: &OsStr) -> Result<CString> {

View file

@ -2,6 +2,8 @@ use std::env;
use std::io::Write; use std::io::Write;
use std::io::{BufWriter, Result}; use std::io::{BufWriter, Result};
use std::process::{Child, Command, Stdio}; use std::process::{Child, Command, Stdio};
use uucore::crash;
/// A writer that writes to a shell_process' stdin /// 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 /// We use a shell process (not directly calling a sub-process) so we can forward the name of the

View file

@ -7,9 +7,6 @@
// spell-checker:ignore (ToDO) PREFIXaa // spell-checker:ignore (ToDO) PREFIXaa
#[macro_use]
extern crate uucore;
mod platform; mod platform;
use clap::{crate_version, App, Arg, ArgMatches}; 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::path::Path;
use std::{char, fs::remove_file}; use std::{char, fs::remove_file};
use uucore::display::Quotable; use uucore::display::Quotable;
use uucore::error::{FromIo, UResult, USimpleError, UUsageError};
use uucore::parse_size::parse_size; use uucore::parse_size::parse_size;
static OPT_BYTES: &str = "bytes"; 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 usage = usage();
let long_usage = get_long_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.additional_suffix = matches.value_of(OPT_ADDITIONAL_SUFFIX).unwrap().to_owned();
settings.verbose = matches.occurrences_of("verbose") > 0; 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.input = matches.value_of(ARG_INPUT).unwrap().to_owned();
settings.prefix = matches.value_of(ARG_PREFIX).unwrap().to_owned(); settings.prefix = matches.value_of(ARG_PREFIX).unwrap().to_owned();
if matches.occurrences_of(OPT_FILTER) > 0 { if matches.occurrences_of(OPT_FILTER) > 0 {
if cfg!(windows) { if cfg!(windows) {
// see https://github.com/rust-lang/rust/issues/29494 // see https://github.com/rust-lang/rust/issues/29494
show_error!("{} is currently not supported in this platform", OPT_FILTER); return Err(USimpleError::new(
std::process::exit(-1); -1,
format!("{} is currently not supported in this platform", OPT_FILTER),
));
} else { } else {
settings.filter = Some(matches.value_of(OPT_FILTER).unwrap().to_owned()); settings.filter = Some(matches.value_of(OPT_FILTER).unwrap().to_owned());
} }
@ -193,7 +194,7 @@ enum Strategy {
impl Strategy { impl Strategy {
/// Parse a strategy from the command-line arguments. /// 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. // Check that the user is not specifying more than one strategy.
// //
// Note: right now, this exact behavior cannot be handled by // 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_BYTES),
matches.occurrences_of(OPT_LINE_BYTES), matches.occurrences_of(OPT_LINE_BYTES),
) { ) {
(0, 0, 0) => Strategy::Lines(1000), (0, 0, 0) => Ok(Strategy::Lines(1000)),
(1, 0, 0) => { (1, 0, 0) => {
let s = matches.value_of(OPT_LINES).unwrap(); let s = matches.value_of(OPT_LINES).unwrap();
let n = let n = parse_size(s)
parse_size(s).unwrap_or_else(|e| crash!(1, "invalid number of lines: {}", e)); .map_err(|e| USimpleError::new(1, format!("invalid number of lines: {}", e)))?;
Strategy::Lines(n) Ok(Strategy::Lines(n))
} }
(0, 1, 0) => { (0, 1, 0) => {
let s = matches.value_of(OPT_BYTES).unwrap(); let s = matches.value_of(OPT_BYTES).unwrap();
let n = let n = parse_size(s)
parse_size(s).unwrap_or_else(|e| crash!(1, "invalid number of bytes: {}", e)); .map_err(|e| USimpleError::new(1, format!("invalid number of bytes: {}", e)))?;
Strategy::Bytes(n) Ok(Strategy::Bytes(n))
} }
(0, 0, 1) => { (0, 0, 1) => {
let s = matches.value_of(OPT_LINE_BYTES).unwrap(); let s = matches.value_of(OPT_LINE_BYTES).unwrap();
let n = let n = parse_size(s)
parse_size(s).unwrap_or_else(|e| crash!(1, "invalid number of bytes: {}", e)); .map_err(|e| USimpleError::new(1, format!("invalid number of bytes: {}", e)))?;
Strategy::LineBytes(n) 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, &mut self,
reader: &mut BufReader<Box<dyn Read>>, reader: &mut BufReader<Box<dyn Read>>,
writer: &mut BufWriter<Box<dyn Write>>, writer: &mut BufWriter<Box<dyn Write>>,
) -> u128; ) -> std::io::Result<u128>;
} }
struct LineSplitter { struct LineSplitter {
@ -269,21 +270,17 @@ impl Splitter for LineSplitter {
&mut self, &mut self,
reader: &mut BufReader<Box<dyn Read>>, reader: &mut BufReader<Box<dyn Read>>,
writer: &mut BufWriter<Box<dyn Write>>, writer: &mut BufWriter<Box<dyn Write>>,
) -> u128 { ) -> std::io::Result<u128> {
let mut bytes_consumed = 0u128; let mut bytes_consumed = 0u128;
let mut buffer = String::with_capacity(1024); let mut buffer = String::with_capacity(1024);
for _ in 0..self.lines_per_split { for _ in 0..self.lines_per_split {
let bytes_read = reader let bytes_read = reader.read_line(&mut buffer)?;
.read_line(&mut buffer)
.unwrap_or_else(|_| crash!(1, "error reading bytes from input file"));
// If we ever read 0 bytes then we know we've hit EOF. // If we ever read 0 bytes then we know we've hit EOF.
if bytes_read == 0 { if bytes_read == 0 {
return bytes_consumed; return Ok(bytes_consumed);
} }
writer writer.write_all(buffer.as_bytes())?;
.write_all(buffer.as_bytes())
.unwrap_or_else(|_| crash!(1, "error writing bytes to output file"));
// Empty out the String buffer since `read_line` appends instead of // Empty out the String buffer since `read_line` appends instead of
// replaces. // replaces.
buffer.clear(); buffer.clear();
@ -291,7 +288,7 @@ impl Splitter for LineSplitter {
bytes_consumed += bytes_read as u128; bytes_consumed += bytes_read as u128;
} }
bytes_consumed Ok(bytes_consumed)
} }
} }
@ -312,7 +309,7 @@ impl Splitter for ByteSplitter {
&mut self, &mut self,
reader: &mut BufReader<Box<dyn Read>>, reader: &mut BufReader<Box<dyn Read>>,
writer: &mut BufWriter<Box<dyn Write>>, writer: &mut BufWriter<Box<dyn Write>>,
) -> u128 { ) -> std::io::Result<u128> {
// We buffer reads and writes. We proceed until `bytes_consumed` is // We buffer reads and writes. We proceed until `bytes_consumed` is
// equal to `self.bytes_per_split` or we reach EOF. // equal to `self.bytes_per_split` or we reach EOF.
let mut bytes_consumed = 0u128; let mut bytes_consumed = 0u128;
@ -329,22 +326,18 @@ impl Splitter for ByteSplitter {
// than BUFFER_SIZE in this branch. // than BUFFER_SIZE in this branch.
(self.bytes_per_split - bytes_consumed) as usize (self.bytes_per_split - bytes_consumed) as usize
}; };
let bytes_read = reader let bytes_read = reader.read(&mut buffer[0..bytes_desired])?;
.read(&mut buffer[0..bytes_desired])
.unwrap_or_else(|_| crash!(1, "error reading bytes from input file"));
// If we ever read 0 bytes then we know we've hit EOF. // If we ever read 0 bytes then we know we've hit EOF.
if bytes_read == 0 { if bytes_read == 0 {
return bytes_consumed; return Ok(bytes_consumed);
} }
writer writer.write_all(&buffer[0..bytes_read])?;
.write_all(&buffer[0..bytes_read])
.unwrap_or_else(|_| crash!(1, "error writing bytes to output file"));
bytes_consumed += bytes_read as u128; bytes_consumed += bytes_read as u128;
} }
bytes_consumed Ok(bytes_consumed)
} }
} }
@ -380,17 +373,16 @@ fn num_prefix(i: usize, width: usize) -> String {
c c
} }
fn split(settings: &Settings) -> i32 { fn split(settings: &Settings) -> UResult<()> {
let mut reader = BufReader::new(if settings.input == "-" { let mut reader = BufReader::new(if settings.input == "-" {
Box::new(stdin()) as Box<dyn Read> Box::new(stdin()) as Box<dyn Read>
} else { } else {
let r = File::open(Path::new(&settings.input)).unwrap_or_else(|_| { let r = File::open(Path::new(&settings.input)).map_err_context(|| {
crash!( format!(
1,
"cannot open {} for reading: No such file or directory", "cannot open {} for reading: No such file or directory",
settings.input.quote() settings.input.quote()
) )
}); })?;
Box::new(r) as Box<dyn Read> Box::new(r) as Box<dyn Read>
}); });
@ -416,10 +408,12 @@ fn split(settings: &Settings) -> i32 {
filename.push_str(settings.additional_suffix.as_ref()); filename.push_str(settings.additional_suffix.as_ref());
let mut writer = platform::instantiate_current_writer(&settings.filter, filename.as_str()); 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 writer
.flush() .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 // If we didn't write anything we should clean up the empty file, and
// break from the loop. // break from the loop.
@ -428,12 +422,12 @@ fn split(settings: &Settings) -> i32 {
// Complicated, I know... // Complicated, I know...
if settings.filter.is_none() { if settings.filter.is_none() {
remove_file(filename) remove_file(filename)
.unwrap_or_else(|e| crash!(1, "error removing empty file: {}", e)); .map_err_context(|| "error removing empty file".to_string())?;
} }
break; break;
} }
fileno += 1; fileno += 1;
} }
0 Ok(())
} }

View file

@ -17,6 +17,7 @@ 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::display::Quotable;
use uucore::error::{UResult, USimpleError};
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;
@ -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 let args = args
.collect_str(InvalidEncodingHandling::ConvertLossy) .collect_str(InvalidEncodingHandling::ConvertLossy)
.accept_any(); .accept_any();
@ -188,32 +190,36 @@ fn timeout(
foreground: bool, foreground: bool,
preserve_status: bool, preserve_status: bool,
verbose: bool, verbose: bool,
) -> i32 { ) -> UResult<()> {
if !foreground { if !foreground {
unsafe { libc::setpgid(0, 0) }; unsafe { libc::setpgid(0, 0) };
} }
let mut process = match Command::new(&cmd[0]) let mut process = Command::new(&cmd[0])
.args(&cmd[1..]) .args(&cmd[1..])
.stdin(Stdio::inherit()) .stdin(Stdio::inherit())
.stdout(Stdio::inherit()) .stdout(Stdio::inherit())
.stderr(Stdio::inherit()) .stderr(Stdio::inherit())
.spawn() .spawn()
{ .map_err(|err| {
Ok(p) => p, let status_code = if err.kind() == ErrorKind::NotFound {
Err(err) => {
show_error!("failed to execute process: {}", err);
if err.kind() == ErrorKind::NotFound {
// FIXME: not sure which to use // FIXME: not sure which to use
return 127; 127
} else { } else {
// FIXME: this may not be 100% correct... // FIXME: this may not be 100% correct...
return 126; 126
} };
} USimpleError::new(status_code, format!("failed to execute process: {}", err))
}; })?;
unblock_sigchld(); unblock_sigchld();
match process.wait_or_timeout(duration) { 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) => { Ok(None) => {
if verbose { if verbose {
show_error!( show_error!(
@ -222,38 +228,50 @@ fn timeout(
cmd[0].quote() 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 { if let Some(kill_after) = kill_after {
match process.wait_or_timeout(kill_after) { match process.wait_or_timeout(kill_after) {
Ok(Some(status)) => { Ok(Some(status)) => {
if preserve_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 { } else {
124 Err(124.into())
} }
} }
Ok(None) => { Ok(None) => {
if verbose { if verbose {
show_error!("sending signal KILL to command {}", cmd[0].quote()); show_error!("sending signal KILL to command {}", cmd[0].quote());
} }
crash_if_err!( process
ERR_EXIT_STATUS, .send_signal(uucore::signals::signal_by_name_or_value("KILL").unwrap())
process.send_signal( .map_err(|e| USimpleError::new(ERR_EXIT_STATUS, format!("{}", e)))?;
uucore::signals::signal_by_name_or_value("KILL").unwrap() process
) .wait()
); .map_err(|e| USimpleError::new(ERR_EXIT_STATUS, format!("{}", e)))?;
crash_if_err!(ERR_EXIT_STATUS, process.wait()); Err(137.into())
137
} }
Err(_) => 124, Err(_) => Err(124.into()),
} }
} else { } else {
124 Err(124.into())
} }
} }
Err(_) => { Err(_) => {
crash_if_err!(ERR_EXIT_STATUS, process.send_signal(signal)); // We're going to return ERR_EXIT_STATUS regardless of
ERR_EXIT_STATUS // 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())
} }
} }
} }

View file

@ -5,16 +5,13 @@
// * // *
// * 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.
#[macro_use]
extern crate uucore;
use clap::{crate_version, App, Arg}; use clap::{crate_version, App, Arg};
use std::collections::{HashMap, HashSet}; 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::display::Quotable;
use uucore::error::{FromIo, UResult, USimpleError};
use uucore::InvalidEncodingHandling; use uucore::InvalidEncodingHandling;
static SUMMARY: &str = "Topological sort the strings in FILE. static SUMMARY: &str = "Topological sort the strings in FILE.
@ -26,7 +23,8 @@ mod options {
pub const FILE: &str = "file"; 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 let args = args
.collect_str(InvalidEncodingHandling::ConvertLossy) .collect_str(InvalidEncodingHandling::ConvertLossy)
.accept_any(); .accept_any();
@ -43,13 +41,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
stdin_buf = stdin(); stdin_buf = stdin();
&mut stdin_buf as &mut dyn Read &mut stdin_buf as &mut dyn Read
} else { } else {
file_buf = match File::open(Path::new(&input)) { file_buf = File::open(Path::new(&input)).map_err_context(|| input.to_string())?;
Ok(a) => a,
_ => {
show_error!("{}: No such file or directory", input.maybe_quote());
return 1;
}
};
&mut file_buf as &mut dyn Read &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) { 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, return Err(USimpleError::new(
"{}: input contains an odd number of tokens", 1,
input.maybe_quote() 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(); g.run_tsort();
if !g.is_acyclic() { 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 { for x in &g.result {
println!("{}", x); println!("{}", x);
} }
0 Ok(())
} }
pub fn uu_app() -> App<'static, 'static> { pub fn uu_app() -> App<'static, 'static> {

View file

@ -371,7 +371,7 @@ impl UError for UUsageError {
/// ``` /// ```
#[derive(Debug)] #[derive(Debug)]
pub struct UIoError { pub struct UIoError {
context: String, context: Option<String>,
inner: std::io::Error, inner: std::io::Error,
} }
@ -379,7 +379,7 @@ impl UIoError {
#[allow(clippy::new_ret_no_self)] #[allow(clippy::new_ret_no_self)]
pub fn new<S: Into<String>>(kind: std::io::ErrorKind, context: S) -> Box<dyn UError> { pub fn new<S: Into<String>>(kind: std::io::ErrorKind, context: S) -> Box<dyn UError> {
Box::new(Self { Box::new(Self {
context: context.into(), context: Some(context.into()),
inner: kind.into(), inner: kind.into(),
}) })
} }
@ -435,7 +435,11 @@ impl Display for UIoError {
capitalize(&mut message); capitalize(&mut message);
&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 { impl FromIo<Box<UIoError>> for std::io::Error {
fn map_err_context(self, context: impl FnOnce() -> String) -> Box<UIoError> { fn map_err_context(self, context: impl FnOnce() -> String) -> Box<UIoError> {
Box::new(UIoError { Box::new(UIoError {
context: (context)(), context: Some((context)()),
inner: self, inner: self,
}) })
} }
@ -479,12 +483,28 @@ impl<T> FromIo<UResult<T>> for std::io::Result<T> {
impl FromIo<Box<UIoError>> for std::io::ErrorKind { impl FromIo<Box<UIoError>> for std::io::ErrorKind {
fn map_err_context(self, context: impl FnOnce() -> String) -> Box<UIoError> { fn map_err_context(self, context: impl FnOnce() -> String) -> Box<UIoError> {
Box::new(UIoError { Box::new(UIoError {
context: (context)(), context: Some((context)()),
inner: std::io::Error::new(self, ""), 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. /// Shorthand to construct [`UIoError`]-instances.
/// ///
/// This macro serves as a convenience call to quickly construct instances of /// This macro serves as a convenience call to quickly construct instances of

View file

@ -74,9 +74,8 @@ fn test_invalid_file() {
at.mkdir(folder_name); at.mkdir(folder_name);
ts.ucmd() ts.ucmd()
.arg(folder_name) .arg(folder_name)
.fails() .succeeds()
.no_stdout() .stdout_only("4294967295 0 asdf\n");
.stderr_contains("cksum: asdf: Is a directory");
} }
// Make sure crc is correct for files larger than 32 bytes // Make sure crc is correct for files larger than 32 bytes

View file

@ -107,6 +107,28 @@ fn test_ls_io_errors() {
.stdout_contains("some-dir4"); .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] #[test]
fn test_ls_walk_glob() { fn test_ls_walk_glob() {
let scene = TestScenario::new(util_name!()); let scene = TestScenario::new(util_name!());