mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-27 19:17:43 +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::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(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>> {
|
||||||
|
|
|
@ -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> {
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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> {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -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())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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> {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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!());
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue