mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-29 20:17:45 +00:00
Merge branch 'master' into split-uresult
This commit is contained in:
commit
64effa5e78
23 changed files with 208 additions and 432 deletions
6
.github/workflows/CICD.yml
vendored
6
.github/workflows/CICD.yml
vendored
|
@ -844,13 +844,13 @@ jobs:
|
|||
## Generate coverage data
|
||||
COVERAGE_REPORT_DIR="target/debug"
|
||||
COVERAGE_REPORT_FILE="${COVERAGE_REPORT_DIR}/lcov.info"
|
||||
# GRCOV_IGNORE_OPTION='--ignore build.rs --ignore "/*" --ignore "[a-zA-Z]:/*"' ## `grcov` ignores these params when passed as an environment variable (why?)
|
||||
# GRCOV_IGNORE_OPTION='--ignore build.rs --ignore "vendor/*" --ignore "/*" --ignore "[a-zA-Z]:/*"' ## `grcov` ignores these params when passed as an environment variable (why?)
|
||||
# GRCOV_EXCLUDE_OPTION='--excl-br-line "^\s*((debug_)?assert(_eq|_ne)?!|#\[derive\()"' ## `grcov` ignores these params when passed as an environment variable (why?)
|
||||
mkdir -p "${COVERAGE_REPORT_DIR}"
|
||||
# display coverage files
|
||||
grcov . --output-type files --ignore build.rs --ignore "/*" --ignore "[a-zA-Z]:/*" --excl-br-line "^\s*((debug_)?assert(_eq|_ne)?!|#\[derive\()" | sort --unique
|
||||
grcov . --output-type files --ignore build.rs --ignore "vendor/*" --ignore "/*" --ignore "[a-zA-Z]:/*" --excl-br-line "^\s*((debug_)?assert(_eq|_ne)?!|#\[derive\()" | sort --unique
|
||||
# generate coverage report
|
||||
grcov . --output-type lcov --output-path "${COVERAGE_REPORT_FILE}" --branch --ignore build.rs --ignore "/*" --ignore "[a-zA-Z]:/*" --excl-br-line "^\s*((debug_)?assert(_eq|_ne)?!|#\[derive\()"
|
||||
grcov . --output-type lcov --output-path "${COVERAGE_REPORT_FILE}" --branch --ignore build.rs --ignore "vendor/*" --ignore "/*" --ignore "[a-zA-Z]:/*" --excl-br-line "^\s*((debug_)?assert(_eq|_ne)?!|#\[derive\()"
|
||||
echo ::set-output name=report::${COVERAGE_REPORT_FILE}
|
||||
- name: Upload coverage results (to Codecov.io)
|
||||
uses: codecov/codecov-action@v1
|
||||
|
|
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -3238,7 +3238,6 @@ dependencies = [
|
|||
"data-encoding-macro",
|
||||
"dns-lookup",
|
||||
"dunce",
|
||||
"getopts",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"nix 0.23.1",
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
|
||||
#![allow(clippy::upper_case_acronyms)]
|
||||
|
||||
use uucore::{display::Quotable, show_error, show_usage_error, show_warning};
|
||||
use uucore::error::{UResult, USimpleError, UUsageError};
|
||||
use uucore::{display::Quotable, show_error, show_warning};
|
||||
|
||||
use clap::{App, Arg};
|
||||
use selinux::{OpaqueSecurityContext, SecurityContext};
|
||||
|
@ -60,7 +61,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());
|
||||
|
@ -72,14 +74,13 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
match r.kind {
|
||||
clap::ErrorKind::HelpDisplayed | clap::ErrorKind::VersionDisplayed => {
|
||||
println!("{}", r);
|
||||
return libc::EXIT_SUCCESS;
|
||||
return Ok(());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
show_usage_error!("{}.\n", r);
|
||||
return libc::EXIT_FAILURE;
|
||||
return Err(UUsageError::new(libc::EXIT_FAILURE, format!("{}.\n", r)));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -98,8 +99,10 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
|
||||
match result {
|
||||
Err(r) => {
|
||||
show_error!("{}.", report_full_error(&r));
|
||||
return libc::EXIT_FAILURE;
|
||||
return Err(USimpleError::new(
|
||||
libc::EXIT_FAILURE,
|
||||
format!("{}.", report_full_error(&r)),
|
||||
));
|
||||
}
|
||||
|
||||
Ok(file_context) => SELinuxSecurityContext::File(file_context),
|
||||
|
@ -111,14 +114,18 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
Ok(context) => context,
|
||||
|
||||
Err(_r) => {
|
||||
show_error!("Invalid security context {}.", context.quote());
|
||||
return libc::EXIT_FAILURE;
|
||||
return Err(USimpleError::new(
|
||||
libc::EXIT_FAILURE,
|
||||
format!("Invalid security context {}.", context.quote()),
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
if SecurityContext::from_c_str(&c_context, false).check() == Some(false) {
|
||||
show_error!("Invalid security context {}.", context.quote());
|
||||
return libc::EXIT_FAILURE;
|
||||
return Err(USimpleError::new(
|
||||
libc::EXIT_FAILURE,
|
||||
format!("Invalid security context {}.", context.quote()),
|
||||
));
|
||||
}
|
||||
|
||||
SELinuxSecurityContext::String(Some(c_context))
|
||||
|
@ -132,8 +139,10 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
Ok(r) => Some(r),
|
||||
|
||||
Err(r) => {
|
||||
show_error!("{}.", report_full_error(&r));
|
||||
return libc::EXIT_FAILURE;
|
||||
return Err(USimpleError::new(
|
||||
libc::EXIT_FAILURE,
|
||||
format!("{}.", report_full_error(&r)),
|
||||
));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -142,13 +151,13 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
|
||||
let results = process_files(&options, &context, root_dev_ino);
|
||||
if results.is_empty() {
|
||||
return libc::EXIT_SUCCESS;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
for result in &results {
|
||||
show_error!("{}.", report_full_error(result));
|
||||
}
|
||||
libc::EXIT_FAILURE
|
||||
Err(libc::EXIT_FAILURE.into())
|
||||
}
|
||||
|
||||
pub fn uu_app() -> App<'static, 'static> {
|
||||
|
|
|
@ -466,12 +466,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
|||
delim = "=";
|
||||
}
|
||||
if delim.chars().count() > 1 {
|
||||
Err(msg_opt_invalid_should_be!(
|
||||
"empty or 1 character long",
|
||||
"a value 2 characters or longer",
|
||||
"--delimiter",
|
||||
"-d"
|
||||
))
|
||||
Err("invalid input: The '--delimiter' ('-d') option expects empty or 1 character long, but was provided a value 2 characters or longer".into())
|
||||
} else {
|
||||
let delim = if delim.is_empty() {
|
||||
"\0".to_owned()
|
||||
|
@ -503,13 +498,9 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
|||
})
|
||||
}
|
||||
(ref b, ref c, ref f) if b.is_some() || c.is_some() || f.is_some() => Err(
|
||||
msg_expects_no_more_than_one_of!("--fields (-f)", "--chars (-c)", "--bytes (-b)"),
|
||||
"invalid usage: expects no more than one of --fields (-f), --chars (-c) or --bytes (-b)".into()
|
||||
),
|
||||
_ => Err(msg_expects_one_of!(
|
||||
"--fields (-f)",
|
||||
"--chars (-c)",
|
||||
"--bytes (-b)"
|
||||
)),
|
||||
_ => Err("invalid usage: expects one of --fields (-f), --chars (-c) or --bytes (-b)".into()),
|
||||
};
|
||||
|
||||
let mode_parse = match mode_parse {
|
||||
|
@ -518,20 +509,12 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
|||
Mode::Bytes(_, _) | Mode::Characters(_, _)
|
||||
if matches.is_present(options::DELIMITER) =>
|
||||
{
|
||||
Err(msg_opt_only_usable_if!(
|
||||
"printing a sequence of fields",
|
||||
"--delimiter",
|
||||
"-d"
|
||||
))
|
||||
Err("invalid input: The '--delimiter' ('-d') option only usable if printing a sequence of fields".into())
|
||||
}
|
||||
Mode::Bytes(_, _) | Mode::Characters(_, _)
|
||||
if matches.is_present(options::ONLY_DELIMITED) =>
|
||||
{
|
||||
Err(msg_opt_only_usable_if!(
|
||||
"printing a sequence of fields",
|
||||
"--only-delimited",
|
||||
"-s"
|
||||
))
|
||||
Err("invalid input: The '--only-delimited' ('-s') option only usable if printing a sequence of fields".into())
|
||||
}
|
||||
_ => Ok(mode),
|
||||
},
|
||||
|
|
|
@ -18,7 +18,7 @@ use std::env;
|
|||
use std::fs;
|
||||
#[cfg(not(windows))]
|
||||
use std::fs::Metadata;
|
||||
use std::io::{stderr, ErrorKind, Result, Write};
|
||||
use std::io::{ErrorKind, Result};
|
||||
use std::iter;
|
||||
#[cfg(not(windows))]
|
||||
use std::os::unix::fs::MetadataExt;
|
||||
|
@ -292,8 +292,7 @@ fn du(
|
|||
let read = match fs::read_dir(&my_stat.path) {
|
||||
Ok(read) => read,
|
||||
Err(e) => {
|
||||
safe_writeln!(
|
||||
stderr(),
|
||||
eprintln!(
|
||||
"{}: cannot read directory {}: {}",
|
||||
options.util_name,
|
||||
my_stat.path.quote(),
|
||||
|
|
|
@ -413,7 +413,7 @@ impl<'a> State<'a> {
|
|||
|
||||
// This is fatal if the check is enabled.
|
||||
if input.check_order == CheckOrder::Enabled {
|
||||
exit!(1);
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
self.has_failed = true;
|
||||
|
|
|
@ -220,16 +220,32 @@ fn format_string(
|
|||
options: &NumfmtOptions,
|
||||
implicit_padding: Option<isize>,
|
||||
) -> Result<String> {
|
||||
// strip the (optional) suffix before applying any transformation
|
||||
let source_without_suffix = match &options.suffix {
|
||||
Some(suffix) => source.strip_suffix(suffix).unwrap_or(source),
|
||||
None => source,
|
||||
};
|
||||
|
||||
let number = transform_to(
|
||||
transform_from(source, &options.transform.from)?,
|
||||
transform_from(source_without_suffix, &options.transform.from)?,
|
||||
&options.transform.to,
|
||||
options.round,
|
||||
)?;
|
||||
|
||||
// bring back the suffix before applying padding
|
||||
let number_with_suffix = match &options.suffix {
|
||||
Some(suffix) => format!("{}{}", number, suffix),
|
||||
None => number,
|
||||
};
|
||||
|
||||
Ok(match implicit_padding.unwrap_or(options.padding) {
|
||||
0 => number,
|
||||
p if p > 0 => format!("{:>padding$}", number, padding = p as usize),
|
||||
p => format!("{:<padding$}", number, padding = p.abs() as usize),
|
||||
0 => number_with_suffix,
|
||||
p if p > 0 => format!("{:>padding$}", number_with_suffix, padding = p as usize),
|
||||
p => format!(
|
||||
"{:<padding$}",
|
||||
number_with_suffix,
|
||||
padding = p.abs() as usize
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -142,6 +142,8 @@ fn parse_options(args: &ArgMatches) -> Result<NumfmtOptions> {
|
|||
_ => unreachable!("Should be restricted by clap"),
|
||||
};
|
||||
|
||||
let suffix = args.value_of(options::SUFFIX).map(|s| s.to_owned());
|
||||
|
||||
Ok(NumfmtOptions {
|
||||
transform,
|
||||
padding,
|
||||
|
@ -149,6 +151,7 @@ fn parse_options(args: &ArgMatches) -> Result<NumfmtOptions> {
|
|||
fields,
|
||||
delimiter,
|
||||
round,
|
||||
suffix,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -242,5 +245,14 @@ pub fn uu_app() -> App<'static, 'static> {
|
|||
.default_value("from-zero")
|
||||
.possible_values(&["up", "down", "from-zero", "towards-zero", "nearest"]),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(options::SUFFIX)
|
||||
.long(options::SUFFIX)
|
||||
.help(
|
||||
"print SUFFIX after each formatted number, and accept \
|
||||
inputs optionally ending with SUFFIX",
|
||||
)
|
||||
.value_name("SUFFIX"),
|
||||
)
|
||||
.arg(Arg::with_name(options::NUMBER).hidden(true).multiple(true))
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ pub const HEADER_DEFAULT: &str = "1";
|
|||
pub const NUMBER: &str = "NUMBER";
|
||||
pub const PADDING: &str = "padding";
|
||||
pub const ROUND: &str = "round";
|
||||
pub const SUFFIX: &str = "suffix";
|
||||
pub const TO: &str = "to";
|
||||
pub const TO_DEFAULT: &str = "none";
|
||||
|
||||
|
@ -26,6 +27,7 @@ pub struct NumfmtOptions {
|
|||
pub fields: Vec<Range>,
|
||||
pub delimiter: Option<String>,
|
||||
pub round: RoundMethod,
|
||||
pub suffix: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
|
|
|
@ -410,7 +410,7 @@ fn get_size(size_str_opt: Option<String>) -> Option<u64> {
|
|||
util_name(),
|
||||
size_str_opt.unwrap().maybe_quote()
|
||||
);
|
||||
exit!(1);
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -20,17 +20,23 @@ mod platform {
|
|||
}
|
||||
|
||||
fn main() {
|
||||
let manifest_dir = env::var("CARGO_MANIFEST_DIR").expect("Could not find manifest dir");
|
||||
let profile = env::var("PROFILE").expect("Could not determine profile");
|
||||
|
||||
let out_dir = env::var("OUT_DIR").unwrap();
|
||||
let libstdbuf = format!(
|
||||
"{}/../../../{}/{}/deps/liblibstdbuf{}",
|
||||
manifest_dir,
|
||||
env::var("CARGO_TARGET_DIR").unwrap_or_else(|_| "target".to_string()),
|
||||
profile,
|
||||
platform::DYLIB_EXT
|
||||
);
|
||||
let mut target_dir = Path::new(&out_dir);
|
||||
|
||||
// Depending on how this is util is built, the directory structure. This seems to work for now.
|
||||
// Here are three cases to test when changing this:
|
||||
// - cargo run
|
||||
// - cross run
|
||||
// - cargo install --git
|
||||
let mut name = target_dir.file_name().unwrap().to_string_lossy();
|
||||
while name != "target" && !name.starts_with("cargo-install") {
|
||||
target_dir = target_dir.parent().unwrap();
|
||||
name = target_dir.file_name().unwrap().to_string_lossy();
|
||||
}
|
||||
let mut libstdbuf = target_dir.to_path_buf();
|
||||
libstdbuf.push(env::var("PROFILE").unwrap());
|
||||
libstdbuf.push("deps");
|
||||
libstdbuf.push(format!("liblibstdbuf{}", platform::DYLIB_EXT));
|
||||
|
||||
fs::copy(libstdbuf, Path::new(&out_dir).join("libstdbuf.so")).unwrap();
|
||||
}
|
||||
|
|
|
@ -12,9 +12,10 @@ extern crate uucore;
|
|||
|
||||
use clap::{crate_version, App, Arg};
|
||||
use std::fs::File;
|
||||
use std::io::{stdin, Read, Result};
|
||||
use std::io::{stdin, Read};
|
||||
use std::path::Path;
|
||||
use uucore::display::Quotable;
|
||||
use uucore::error::{FromIo, UResult, USimpleError};
|
||||
use uucore::InvalidEncodingHandling;
|
||||
|
||||
static NAME: &str = "sum";
|
||||
|
@ -65,26 +66,25 @@ fn sysv_sum(mut reader: Box<dyn Read>) -> (usize, u16) {
|
|||
(blocks_read, ret as u16)
|
||||
}
|
||||
|
||||
fn open(name: &str) -> Result<Box<dyn Read>> {
|
||||
fn open(name: &str) -> UResult<Box<dyn Read>> {
|
||||
match name {
|
||||
"-" => Ok(Box::new(stdin()) as Box<dyn Read>),
|
||||
_ => {
|
||||
let path = &Path::new(name);
|
||||
if path.is_dir() {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidInput,
|
||||
"Is a directory",
|
||||
return Err(USimpleError::new(
|
||||
2,
|
||||
format!("{}: Is a directory", name.maybe_quote()),
|
||||
));
|
||||
};
|
||||
// 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(
|
||||
std::io::ErrorKind::NotFound,
|
||||
"No such file or directory",
|
||||
return Err(USimpleError::new(
|
||||
2,
|
||||
format!("{}: No such file or directory", name.maybe_quote()),
|
||||
));
|
||||
};
|
||||
let f = File::open(path)?;
|
||||
let f = File::open(path).map_err_context(String::new)?;
|
||||
Ok(Box::new(f) as Box<dyn Read>)
|
||||
}
|
||||
}
|
||||
|
@ -96,7 +96,8 @@ mod options {
|
|||
pub static SYSTEM_V_COMPATIBLE: &str = "sysv";
|
||||
}
|
||||
|
||||
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();
|
||||
|
@ -116,13 +117,11 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
files.len() > 1
|
||||
};
|
||||
|
||||
let mut exit_code = 0;
|
||||
for file in &files {
|
||||
let reader = match open(file) {
|
||||
Ok(f) => f,
|
||||
Err(error) => {
|
||||
show_error!("{}: {}", file.maybe_quote(), error);
|
||||
exit_code = 2;
|
||||
show!(error);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
@ -138,8 +137,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
println!("{} {}", sum, blocks);
|
||||
}
|
||||
}
|
||||
|
||||
exit_code
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn uu_app() -> App<'static, 'static> {
|
||||
|
|
|
@ -9,14 +9,10 @@
|
|||
|
||||
extern crate libc;
|
||||
|
||||
#[macro_use]
|
||||
extern crate uucore;
|
||||
|
||||
use clap::{crate_version, App, Arg};
|
||||
use std::path::Path;
|
||||
use uucore::display::Quotable;
|
||||
|
||||
static EXIT_ERR: i32 = 1;
|
||||
use uucore::error::{UResult, USimpleError};
|
||||
|
||||
static ABOUT: &str = "Synchronize cached writes to persistent storage";
|
||||
pub mod options {
|
||||
|
@ -72,6 +68,7 @@ mod platform {
|
|||
use std::mem;
|
||||
use std::os::windows::prelude::*;
|
||||
use std::path::Path;
|
||||
use uucore::crash;
|
||||
use uucore::wide::{FromWide, ToWide};
|
||||
|
||||
unsafe fn flush_volume(name: &str) {
|
||||
|
@ -164,7 +161,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);
|
||||
|
@ -176,11 +174,10 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
|
||||
for f in &files {
|
||||
if !Path::new(&f).exists() {
|
||||
crash!(
|
||||
EXIT_ERR,
|
||||
"cannot stat {}: No such file or directory",
|
||||
f.quote()
|
||||
);
|
||||
return Err(USimpleError::new(
|
||||
1,
|
||||
format!("cannot stat {}: No such file or directory", f.quote()),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -194,7 +191,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
} else {
|
||||
sync();
|
||||
}
|
||||
0
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn uu_app() -> App<'static, 'static> {
|
||||
|
|
|
@ -14,6 +14,7 @@ use std::fs::OpenOptions;
|
|||
use std::io::{copy, sink, stdin, stdout, Error, ErrorKind, Read, Result, Write};
|
||||
use std::path::PathBuf;
|
||||
use uucore::display::Quotable;
|
||||
use uucore::error::UResult;
|
||||
|
||||
#[cfg(unix)]
|
||||
use uucore::libc;
|
||||
|
@ -37,7 +38,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);
|
||||
|
@ -52,8 +54,8 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
};
|
||||
|
||||
match tee(options) {
|
||||
Ok(_) => 0,
|
||||
Err(_) => 1,
|
||||
Ok(_) => Ok(()),
|
||||
Err(_) => Err(1.into()),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,8 @@ mod parser;
|
|||
use clap::{crate_version, App, AppSettings};
|
||||
use parser::{parse, Operator, Symbol, UnaryOperator};
|
||||
use std::ffi::{OsStr, OsString};
|
||||
use uucore::{display::Quotable, show_error};
|
||||
use uucore::display::Quotable;
|
||||
use uucore::error::{UResult, USimpleError};
|
||||
|
||||
const USAGE: &str = "test EXPRESSION
|
||||
or: test
|
||||
|
@ -91,7 +92,8 @@ pub fn uu_app() -> App<'static, 'static> {
|
|||
.setting(AppSettings::DisableVersion)
|
||||
}
|
||||
|
||||
pub fn uumain(mut args: impl uucore::Args) -> i32 {
|
||||
#[uucore_procs::gen_uumain]
|
||||
pub fn uumain(mut args: impl uucore::Args) -> UResult<()> {
|
||||
let program = args.next().unwrap_or_else(|| OsString::from("test"));
|
||||
let binary_name = uucore::util_name();
|
||||
let mut args: Vec<_> = args.collect();
|
||||
|
@ -109,13 +111,12 @@ pub fn uumain(mut args: impl uucore::Args) -> i32 {
|
|||
.setting(AppSettings::NeedsLongHelp)
|
||||
.setting(AppSettings::NeedsLongVersion)
|
||||
.get_matches_from(std::iter::once(program).chain(args.into_iter()));
|
||||
return 0;
|
||||
return Ok(());
|
||||
}
|
||||
// If invoked via name '[', matching ']' must be in the last arg
|
||||
let last = args.pop();
|
||||
if last.as_deref() != Some(OsStr::new("]")) {
|
||||
show_error!("missing ']'");
|
||||
return 2;
|
||||
return Err(USimpleError::new(2, "missing ']'"));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -124,15 +125,12 @@ pub fn uumain(mut args: impl uucore::Args) -> i32 {
|
|||
match result {
|
||||
Ok(result) => {
|
||||
if result {
|
||||
0
|
||||
Ok(())
|
||||
} else {
|
||||
1
|
||||
Err(1.into())
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
show_error!("{}", e);
|
||||
2
|
||||
}
|
||||
Err(e) => Err(USimpleError::new(2, e)),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
use clap::{crate_version, App, Arg};
|
||||
use std::ffi::CStr;
|
||||
use std::io::Write;
|
||||
use uucore::error::{UResult, UUsageError};
|
||||
use uucore::InvalidEncodingHandling;
|
||||
|
||||
static ABOUT: &str = "Print the file name of the terminal connected to standard input.";
|
||||
|
@ -24,21 +25,17 @@ fn usage() -> String {
|
|||
format!("{0} [OPTION]...", uucore::execution_phrase())
|
||||
}
|
||||
|
||||
pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||
#[uucore_procs::gen_uumain]
|
||||
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
let usage = usage();
|
||||
let args = args
|
||||
.collect_str(InvalidEncodingHandling::ConvertLossy)
|
||||
.accept_any();
|
||||
|
||||
let matches = uu_app().usage(&usage[..]).get_matches_from_safe(args);
|
||||
|
||||
let matches = match matches {
|
||||
Ok(m) => m,
|
||||
Err(e) => {
|
||||
eprint!("{}", e);
|
||||
return 2;
|
||||
}
|
||||
};
|
||||
let matches = uu_app()
|
||||
.usage(&usage[..])
|
||||
.get_matches_from_safe(args)
|
||||
.map_err(|e| UUsageError::new(2, format!("{}", e)))?;
|
||||
|
||||
let silent = matches.is_present(options::SILENT);
|
||||
|
||||
|
@ -68,9 +65,9 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
}
|
||||
|
||||
if atty::is(atty::Stream::Stdin) {
|
||||
libc::EXIT_SUCCESS
|
||||
Ok(())
|
||||
} else {
|
||||
libc::EXIT_FAILURE
|
||||
Err(libc::EXIT_FAILURE.into())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ use std::io::{stdin, stdout, BufRead, BufReader, BufWriter, Read, Stdout, Write}
|
|||
use std::str::from_utf8;
|
||||
use unicode_width::UnicodeWidthChar;
|
||||
use uucore::display::Quotable;
|
||||
use uucore::error::{FromIo, UResult};
|
||||
use uucore::InvalidEncodingHandling;
|
||||
|
||||
static NAME: &str = "unexpand";
|
||||
|
@ -90,16 +91,15 @@ impl Options {
|
|||
}
|
||||
}
|
||||
|
||||
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::Ignore)
|
||||
.accept_any();
|
||||
|
||||
let matches = uu_app().get_matches_from(args);
|
||||
|
||||
unexpand(Options::new(matches));
|
||||
|
||||
0
|
||||
unexpand(Options::new(matches)).map_err_context(String::new)
|
||||
}
|
||||
|
||||
pub fn uu_app() -> App<'static, 'static> {
|
||||
|
@ -242,7 +242,7 @@ fn next_char_info(uflag: bool, buf: &[u8], byte: usize) -> (CharType, usize, usi
|
|||
(ctype, cwidth, nbytes)
|
||||
}
|
||||
|
||||
fn unexpand(options: Options) {
|
||||
fn unexpand(options: Options) -> std::io::Result<()> {
|
||||
let mut output = BufWriter::new(stdout());
|
||||
let ts = &options.tabstops[..];
|
||||
let mut buf = Vec::new();
|
||||
|
@ -273,7 +273,7 @@ fn unexpand(options: Options) {
|
|||
init,
|
||||
true,
|
||||
);
|
||||
crash_if_err!(1, output.write_all(&buf[byte..]));
|
||||
output.write_all(&buf[byte..])?;
|
||||
scol = col;
|
||||
break;
|
||||
}
|
||||
|
@ -293,7 +293,7 @@ fn unexpand(options: Options) {
|
|||
};
|
||||
|
||||
if !tabs_buffered {
|
||||
crash_if_err!(1, output.write_all(&buf[byte..byte + nbytes]));
|
||||
output.write_all(&buf[byte..byte + nbytes])?;
|
||||
scol = col; // now printed up to this column
|
||||
}
|
||||
}
|
||||
|
@ -318,7 +318,7 @@ fn unexpand(options: Options) {
|
|||
} else {
|
||||
0
|
||||
};
|
||||
crash_if_err!(1, output.write_all(&buf[byte..byte + nbytes]));
|
||||
output.write_all(&buf[byte..byte + nbytes])?;
|
||||
scol = col; // we've now printed up to this column
|
||||
}
|
||||
}
|
||||
|
@ -337,9 +337,9 @@ fn unexpand(options: Options) {
|
|||
init,
|
||||
true,
|
||||
);
|
||||
crash_if_err!(1, output.flush());
|
||||
output.flush()?;
|
||||
buf.truncate(0); // clear out the buffer
|
||||
}
|
||||
}
|
||||
crash_if_err!(1, output.flush())
|
||||
output.flush()
|
||||
}
|
||||
|
|
|
@ -19,7 +19,6 @@ path="src/lib/lib.rs"
|
|||
clap = "2.33.3"
|
||||
dns-lookup = { version="1.0.5", optional=true }
|
||||
dunce = "1.0.0"
|
||||
getopts = "<= 0.2.21"
|
||||
wild = "2.0"
|
||||
# * optional
|
||||
thiserror = { version="1.0", optional=true }
|
||||
|
|
|
@ -18,7 +18,6 @@ mod parser; // string parsing modules
|
|||
|
||||
// * cross-platform modules
|
||||
pub use crate::mods::backup_control;
|
||||
pub use crate::mods::coreopts;
|
||||
pub use crate::mods::display;
|
||||
pub use crate::mods::error;
|
||||
pub use crate::mods::os;
|
||||
|
|
|
@ -26,9 +26,7 @@
|
|||
//! - From custom messages: [`show_error!`], [`show_usage_error!`]
|
||||
//! - Print warnings: [`show_warning!`]
|
||||
//! - Terminate util execution
|
||||
//! - Terminate regularly: [`exit!`], [`return_if_err!`]
|
||||
//! - Crash program: [`crash!`], [`crash_if_err!`], [`safe_unwrap!`]
|
||||
//! - Unwrapping result types: [`safe_unwrap!`]
|
||||
//! - Crash program: [`crash!`], [`crash_if_err!`]
|
||||
|
||||
// spell-checker:ignore sourcepath targetpath
|
||||
|
||||
|
@ -223,22 +221,10 @@ macro_rules! show_usage_error(
|
|||
})
|
||||
);
|
||||
|
||||
//====
|
||||
|
||||
/// Calls [`std::process::exit`] with the provided exit code.
|
||||
///
|
||||
/// Why not call exit directly?
|
||||
#[macro_export]
|
||||
macro_rules! exit(
|
||||
($exit_code:expr) => ({
|
||||
::std::process::exit($exit_code)
|
||||
})
|
||||
);
|
||||
|
||||
/// Display an error and [`exit!`]
|
||||
///
|
||||
/// Displays the provided error message using [`show_error!`], then invokes
|
||||
/// [`exit!`] with the provided exit code.
|
||||
/// [`std::process::exit`] with the provided exit code.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
@ -255,7 +241,7 @@ macro_rules! exit(
|
|||
macro_rules! crash(
|
||||
($exit_code:expr, $($args:tt)+) => ({
|
||||
$crate::show_error!($($args)+);
|
||||
$crate::exit!($exit_code)
|
||||
std::process::exit($exit_code);
|
||||
})
|
||||
);
|
||||
|
||||
|
@ -288,150 +274,3 @@ macro_rules! crash_if_err(
|
|||
}
|
||||
)
|
||||
);
|
||||
|
||||
/// Unwrap some Result, crashing instead of panicking.
|
||||
///
|
||||
/// Drop this in favor of `crash_if_err!`
|
||||
#[macro_export]
|
||||
macro_rules! safe_unwrap(
|
||||
($exp:expr) => (
|
||||
match $exp {
|
||||
Ok(m) => m,
|
||||
Err(f) => $crate::crash!(1, "{}", f.to_string())
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
//====
|
||||
|
||||
/// Unwraps the Result. Instead of panicking, it shows the error and then
|
||||
/// returns from the function with the provided exit code.
|
||||
/// Assumes the current function returns an i32 value.
|
||||
///
|
||||
/// Replace with `crash_if_err`?
|
||||
#[macro_export]
|
||||
macro_rules! return_if_err(
|
||||
($exit_code:expr, $exp:expr) => (
|
||||
match $exp {
|
||||
Ok(m) => m,
|
||||
Err(f) => {
|
||||
$crate::show_error!("{}", f);
|
||||
return $exit_code;
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
//====
|
||||
|
||||
/// This is used exclusively by du...
|
||||
#[macro_export]
|
||||
macro_rules! safe_writeln(
|
||||
($fd:expr, $($args:tt)+) => (
|
||||
match writeln!($fd, $($args)+) {
|
||||
Ok(_) => {}
|
||||
Err(f) => panic!("{}", f)
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
//-- message templates
|
||||
|
||||
//-- message templates : (join utility sub-macros)
|
||||
|
||||
// used only by "cut"
|
||||
#[macro_export]
|
||||
macro_rules! snippet_list_join_oxford_comma {
|
||||
($conjunction:expr, $valOne:expr, $valTwo:expr) => (
|
||||
format!("{}, {} {}", $valOne, $conjunction, $valTwo)
|
||||
);
|
||||
($conjunction:expr, $valOne:expr, $valTwo:expr $(, $remaining_values:expr)*) => (
|
||||
format!("{}, {}", $valOne, $crate::snippet_list_join_oxford_comma!($conjunction, $valTwo $(, $remaining_values)*))
|
||||
);
|
||||
}
|
||||
|
||||
// used only by "cut"
|
||||
#[macro_export]
|
||||
macro_rules! snippet_list_join {
|
||||
($conjunction:expr, $valOne:expr, $valTwo:expr) => (
|
||||
format!("{} {} {}", $valOne, $conjunction, $valTwo)
|
||||
);
|
||||
($conjunction:expr, $valOne:expr, $valTwo:expr $(, $remaining_values:expr)*) => (
|
||||
format!("{}, {}", $valOne, $crate::snippet_list_join_oxford_comma!($conjunction, $valTwo $(, $remaining_values)*))
|
||||
);
|
||||
}
|
||||
|
||||
//-- message templates : invalid input
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! msg_invalid_input {
|
||||
($reason: expr) => {
|
||||
format!("invalid input: {}", $reason)
|
||||
};
|
||||
}
|
||||
|
||||
// -- message templates : invalid input : flag
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! msg_invalid_opt_use {
|
||||
($about:expr, $flag:expr) => {
|
||||
$crate::msg_invalid_input!(format!("The '{}' option {}", $flag, $about))
|
||||
};
|
||||
($about:expr, $long_flag:expr, $short_flag:expr) => {
|
||||
$crate::msg_invalid_input!(format!(
|
||||
"The '{}' ('{}') option {}",
|
||||
$long_flag, $short_flag, $about
|
||||
))
|
||||
};
|
||||
}
|
||||
|
||||
// Only used by "cut"
|
||||
#[macro_export]
|
||||
macro_rules! msg_opt_only_usable_if {
|
||||
($clause:expr, $flag:expr) => {
|
||||
$crate::msg_invalid_opt_use!(format!("only usable if {}", $clause), $flag)
|
||||
};
|
||||
($clause:expr, $long_flag:expr, $short_flag:expr) => {
|
||||
$crate::msg_invalid_opt_use!(
|
||||
format!("only usable if {}", $clause),
|
||||
$long_flag,
|
||||
$short_flag
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
// Used only by "cut"
|
||||
#[macro_export]
|
||||
macro_rules! msg_opt_invalid_should_be {
|
||||
($expects:expr, $received:expr, $flag:expr) => {
|
||||
$crate::msg_invalid_opt_use!(
|
||||
format!("expects {}, but was provided {}", $expects, $received),
|
||||
$flag
|
||||
)
|
||||
};
|
||||
($expects:expr, $received:expr, $long_flag:expr, $short_flag:expr) => {
|
||||
$crate::msg_invalid_opt_use!(
|
||||
format!("expects {}, but was provided {}", $expects, $received),
|
||||
$long_flag,
|
||||
$short_flag
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
// -- message templates : invalid input : input combinations
|
||||
|
||||
// UNUSED!
|
||||
#[macro_export]
|
||||
macro_rules! msg_expects_one_of {
|
||||
($valOne:expr $(, $remaining_values:expr)*) => (
|
||||
$crate::msg_invalid_input!(format!("expects one of {}", $crate::snippet_list_join!("or", $valOne $(, $remaining_values)*)))
|
||||
);
|
||||
}
|
||||
|
||||
// Used only by "cut"
|
||||
#[macro_export]
|
||||
macro_rules! msg_expects_no_more_than_one_of {
|
||||
($valOne:expr $(, $remaining_values:expr)*) => (
|
||||
$crate::msg_invalid_input!(format!("expects no more than one of {}", $crate::snippet_list_join!("or", $valOne $(, $remaining_values)*))) ;
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
// mods ~ cross-platforms modules (core/bundler file)
|
||||
|
||||
pub mod backup_control;
|
||||
pub mod coreopts;
|
||||
pub mod display;
|
||||
pub mod error;
|
||||
pub mod os;
|
||||
|
|
|
@ -1,141 +0,0 @@
|
|||
pub struct HelpText<'a> {
|
||||
pub name: &'a str,
|
||||
pub version: &'a str,
|
||||
pub syntax: &'a str,
|
||||
pub summary: &'a str,
|
||||
pub long_help: &'a str,
|
||||
pub display_usage: bool,
|
||||
}
|
||||
|
||||
pub struct CoreOptions<'a> {
|
||||
options: getopts::Options,
|
||||
help_text: HelpText<'a>,
|
||||
}
|
||||
|
||||
impl<'a> CoreOptions<'a> {
|
||||
pub fn new(help_text: HelpText<'a>) -> Self {
|
||||
let mut ret = CoreOptions {
|
||||
options: getopts::Options::new(),
|
||||
help_text,
|
||||
};
|
||||
ret.options
|
||||
.optflag("", "help", "print usage information")
|
||||
.optflag("", "version", "print name and version number");
|
||||
ret
|
||||
}
|
||||
pub fn optflagopt(
|
||||
&mut self,
|
||||
short_name: &str,
|
||||
long_name: &str,
|
||||
desc: &str,
|
||||
hint: &str,
|
||||
) -> &mut CoreOptions<'a> {
|
||||
self.options.optflagopt(short_name, long_name, desc, hint);
|
||||
self
|
||||
}
|
||||
pub fn optflag(
|
||||
&mut self,
|
||||
short_name: &str,
|
||||
long_name: &str,
|
||||
desc: &str,
|
||||
) -> &mut CoreOptions<'a> {
|
||||
self.options.optflag(short_name, long_name, desc);
|
||||
self
|
||||
}
|
||||
pub fn optflagmulti(
|
||||
&mut self,
|
||||
short_name: &str,
|
||||
long_name: &str,
|
||||
desc: &str,
|
||||
) -> &mut CoreOptions<'a> {
|
||||
self.options.optflagmulti(short_name, long_name, desc);
|
||||
self
|
||||
}
|
||||
pub fn optopt(
|
||||
&mut self,
|
||||
short_name: &str,
|
||||
long_name: &str,
|
||||
desc: &str,
|
||||
hint: &str,
|
||||
) -> &mut CoreOptions<'a> {
|
||||
self.options.optopt(short_name, long_name, desc, hint);
|
||||
self
|
||||
}
|
||||
pub fn optmulti(
|
||||
&mut self,
|
||||
short_name: &str,
|
||||
long_name: &str,
|
||||
desc: &str,
|
||||
hint: &str,
|
||||
) -> &mut CoreOptions<'a> {
|
||||
self.options.optmulti(short_name, long_name, desc, hint);
|
||||
self
|
||||
}
|
||||
pub fn usage(&self, summary: &str) -> String {
|
||||
self.options.usage(summary)
|
||||
}
|
||||
pub fn parse(&mut self, args: Vec<String>) -> getopts::Matches {
|
||||
let matches = match self.options.parse(&args[1..]) {
|
||||
Ok(m) => Some(m),
|
||||
Err(f) => {
|
||||
eprint!("{}: error: ", self.help_text.name);
|
||||
eprintln!("{}", f);
|
||||
::std::process::exit(1);
|
||||
}
|
||||
}
|
||||
.unwrap();
|
||||
if matches.opt_present("help") {
|
||||
let usage_str = if self.help_text.display_usage {
|
||||
format!(
|
||||
"\n {}\n\n Reference\n",
|
||||
self.options.usage(self.help_text.summary)
|
||||
)
|
||||
.replace("Options:", " Options:")
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
print!(
|
||||
"
|
||||
{0} {1}
|
||||
|
||||
{0} {2}
|
||||
{3}{4}
|
||||
",
|
||||
self.help_text.name,
|
||||
self.help_text.version,
|
||||
self.help_text.syntax,
|
||||
usage_str,
|
||||
self.help_text.long_help
|
||||
);
|
||||
crate::exit!(0);
|
||||
} else if matches.opt_present("version") {
|
||||
println!("{} {}", self.help_text.name, self.help_text.version);
|
||||
crate::exit!(0);
|
||||
}
|
||||
matches
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! app {
|
||||
($syntax: expr, $summary: expr, $long_help: expr) => {
|
||||
uucore::coreopts::CoreOptions::new(uucore::coreopts::HelpText {
|
||||
name: uucore::util_name(),
|
||||
version: env!("CARGO_PKG_VERSION"),
|
||||
syntax: $syntax,
|
||||
summary: $summary,
|
||||
long_help: $long_help,
|
||||
display_usage: true,
|
||||
})
|
||||
};
|
||||
($syntax: expr, $summary: expr, $long_help: expr, $display_usage: expr) => {
|
||||
uucore::coreopts::CoreOptions::new(uucore::coreopts::HelpText {
|
||||
name: uucore::util_name(),
|
||||
version: env!("CARGO_PKG_VERSION"),
|
||||
syntax: $syntax,
|
||||
summary: $summary,
|
||||
long_help: $long_help,
|
||||
display_usage: $display_usage,
|
||||
})
|
||||
};
|
||||
}
|
|
@ -505,3 +505,66 @@ fn test_round() {
|
|||
.stdout_only(exp.join("\n") + "\n");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_suffix_is_added_if_not_supplied() {
|
||||
new_ucmd!()
|
||||
.args(&["--suffix=TEST"])
|
||||
.pipe_in("1000")
|
||||
.succeeds()
|
||||
.stdout_only("1000TEST\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_suffix_is_preserved() {
|
||||
new_ucmd!()
|
||||
.args(&["--suffix=TEST"])
|
||||
.pipe_in("1000TEST")
|
||||
.succeeds()
|
||||
.stdout_only("1000TEST\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_suffix_is_only_applied_to_selected_field() {
|
||||
new_ucmd!()
|
||||
.args(&["--suffix=TEST", "--field=2"])
|
||||
.pipe_in("1000 2000 3000")
|
||||
.succeeds()
|
||||
.stdout_only("1000 2000TEST 3000\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_transform_with_suffix_on_input() {
|
||||
new_ucmd!()
|
||||
.args(&["--suffix=b", "--to=si"])
|
||||
.pipe_in("2000b")
|
||||
.succeeds()
|
||||
.stdout_only("2.0Kb\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_transform_without_suffix_on_input() {
|
||||
new_ucmd!()
|
||||
.args(&["--suffix=b", "--to=si"])
|
||||
.pipe_in("2000")
|
||||
.succeeds()
|
||||
.stdout_only("2.0Kb\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_transform_with_suffix_and_delimiter() {
|
||||
new_ucmd!()
|
||||
.args(&["--suffix=b", "--to=si", "-d=|"])
|
||||
.pipe_in("1000b|2000|3000")
|
||||
.succeeds()
|
||||
.stdout_only("1.0Kb|2000|3000\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_suffix_with_padding() {
|
||||
new_ucmd!()
|
||||
.args(&["--suffix=pad", "--padding=12"])
|
||||
.pipe_in("1000 2000 3000")
|
||||
.succeeds()
|
||||
.stdout_only(" 1000pad 2000 3000\n");
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue