1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-27 11:07:44 +00:00

sort: improve the error mgmt with --batch-size

should make the gnu test tests/sort/sort-merge.pl pass
This commit is contained in:
Sylvestre Ledru 2024-07-04 01:19:48 +02:00
parent 69b603bfe7
commit e2c66ea092
7 changed files with 124 additions and 11 deletions

View file

@ -74,6 +74,7 @@ microbenchmarks
microbenchmarking microbenchmarking
multibyte multibyte
multicall multicall
nmerge
noatime noatime
nocache nocache
nocreat nocreat

1
Cargo.lock generated
View file

@ -3158,6 +3158,7 @@ dependencies = [
"fnv", "fnv",
"itertools 0.13.0", "itertools 0.13.0",
"memchr", "memchr",
"nix",
"rand", "rand",
"rayon", "rayon",
"self_cell", "self_cell",

View file

@ -29,6 +29,9 @@ tempfile = { workspace = true }
unicode-width = { workspace = true } unicode-width = { workspace = true }
uucore = { workspace = true, features = ["fs", "version-cmp"] } uucore = { workspace = true, features = ["fs", "version-cmp"] }
[target.'cfg(target_os = "linux")'.dependencies]
nix = { workspace = true }
[[bin]] [[bin]]
name = "sort" name = "sort"
path = "src/main.rs" path = "src/main.rs"

View file

@ -7,7 +7,7 @@
// https://pubs.opengroup.org/onlinepubs/9699919799/utilities/sort.html // https://pubs.opengroup.org/onlinepubs/9699919799/utilities/sort.html
// https://www.gnu.org/software/coreutils/manual/html_node/sort-invocation.html // https://www.gnu.org/software/coreutils/manual/html_node/sort-invocation.html
// spell-checker:ignore (misc) HFKJFK Mbdfhn // spell-checker:ignore (misc) HFKJFK Mbdfhn getrlimit RLIMIT_NOFILE rlim
mod check; mod check;
mod chunks; mod chunks;
@ -23,6 +23,8 @@ use clap::{crate_version, Arg, ArgAction, Command};
use custom_str_cmp::custom_str_cmp; use custom_str_cmp::custom_str_cmp;
use ext_sort::ext_sort; use ext_sort::ext_sort;
use fnv::FnvHasher; use fnv::FnvHasher;
#[cfg(target_os = "linux")]
use nix::libc::{getrlimit, rlimit, RLIMIT_NOFILE};
use numeric_str_cmp::{human_numeric_str_cmp, numeric_str_cmp, NumInfo, NumInfoParseSettings}; use numeric_str_cmp::{human_numeric_str_cmp, numeric_str_cmp, NumInfo, NumInfoParseSettings};
use rand::{thread_rng, Rng}; use rand::{thread_rng, Rng};
use rayon::prelude::*; use rayon::prelude::*;
@ -45,7 +47,7 @@ use uucore::line_ending::LineEnding;
use uucore::parse_size::{ParseSizeError, Parser}; use uucore::parse_size::{ParseSizeError, Parser};
use uucore::shortcut_value_parser::ShortcutValueParser; use uucore::shortcut_value_parser::ShortcutValueParser;
use uucore::version_cmp::version_cmp; use uucore::version_cmp::version_cmp;
use uucore::{format_usage, help_about, help_section, help_usage}; use uucore::{format_usage, help_about, help_section, help_usage, show_error};
use crate::tmp_dir::TmpDirWrapper; use crate::tmp_dir::TmpDirWrapper;
@ -146,7 +148,9 @@ enum SortError {
CompressProgTerminatedAbnormally { CompressProgTerminatedAbnormally {
prog: String, prog: String,
}, },
TmpDirCreationFailed, TmpFileCreationFailed {
path: PathBuf,
},
Uft8Error { Uft8Error {
error: Utf8Error, error: Utf8Error,
}, },
@ -212,7 +216,9 @@ impl Display for SortError {
Self::CompressProgTerminatedAbnormally { prog } => { Self::CompressProgTerminatedAbnormally { prog } => {
write!(f, "{} terminated abnormally", prog.quote()) write!(f, "{} terminated abnormally", prog.quote())
} }
Self::TmpDirCreationFailed => write!(f, "could not create temporary directory"), Self::TmpFileCreationFailed { path } => {
write!(f, "cannot create temporary file in '{}':", path.display())
}
Self::Uft8Error { error } => write!(f, "{error}"), Self::Uft8Error { error } => write!(f, "{error}"),
} }
} }
@ -1030,6 +1036,18 @@ fn make_sort_mode_arg(mode: &'static str, short: char, help: &'static str) -> Ar
arg arg
} }
#[cfg(target_os = "linux")]
fn get_rlimit() -> UResult<usize> {
let mut limit = rlimit {
rlim_cur: 0,
rlim_max: 0,
};
match unsafe { getrlimit(RLIMIT_NOFILE, &mut limit) } {
0 => Ok(limit.rlim_cur as usize),
_ => Err(UUsageError::new(2, "Failed to fetch rlimit")),
}
}
#[uucore::main] #[uucore::main]
#[allow(clippy::cognitive_complexity)] #[allow(clippy::cognitive_complexity)]
pub fn uumain(args: impl uucore::Args) -> UResult<()> { pub fn uumain(args: impl uucore::Args) -> UResult<()> {
@ -1158,12 +1176,36 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
.map(String::from); .map(String::from);
if let Some(n_merge) = matches.get_one::<String>(options::BATCH_SIZE) { if let Some(n_merge) = matches.get_one::<String>(options::BATCH_SIZE) {
settings.merge_batch_size = n_merge.parse().map_err(|_| { match n_merge.parse::<usize>() {
UUsageError::new( Ok(parsed_value) => {
2, if parsed_value < 2 {
format!("invalid --batch-size argument {}", n_merge.quote()), show_error!("invalid --batch-size argument '{}'", n_merge);
) return Err(UUsageError::new(2, "minimum --batch-size argument is '2'"));
})?; }
settings.merge_batch_size = parsed_value;
}
Err(e) => {
let error_message = if *e.kind() == std::num::IntErrorKind::PosOverflow {
#[cfg(target_os = "linux")]
{
show_error!("--batch-size argument {} too large", n_merge.quote());
format!(
"maximum --batch-size argument with current rlimit is {}",
get_rlimit()?
)
}
#[cfg(not(target_os = "linux"))]
{
format!("--batch-size argument {} too large", n_merge.quote())
}
} else {
format!("invalid --batch-size argument {}", n_merge.quote())
};
return Err(UUsageError::new(2, error_message));
}
}
} }
settings.line_ending = LineEnding::from_zero_flag(matches.get_flag(options::ZERO_TERMINATED)); settings.line_ending = LineEnding::from_zero_flag(matches.get_flag(options::ZERO_TERMINATED));

View file

@ -46,7 +46,9 @@ impl TmpDirWrapper {
tempfile::Builder::new() tempfile::Builder::new()
.prefix("uutils_sort") .prefix("uutils_sort")
.tempdir_in(&self.parent_path) .tempdir_in(&self.parent_path)
.map_err(|_| SortError::TmpDirCreationFailed)?, .map_err(|_| SortError::TmpFileCreationFailed {
path: self.parent_path.clone(),
})?,
); );
let path = self.temp_dir.as_ref().unwrap().path().to_owned(); let path = self.temp_dir.as_ref().unwrap().path().to_owned();

View file

@ -1035,6 +1035,38 @@ fn test_merge_batches() {
.stdout_only_fixture("ext_sort.expected"); .stdout_only_fixture("ext_sort.expected");
} }
#[test]
fn test_batch_size_invalid() {
TestScenario::new(util_name!())
.ucmd()
.arg("--batch-size=0")
.fails()
.code_is(2)
.stderr_contains("sort: invalid --batch-size argument '0'")
.stderr_contains("sort: minimum --batch-size argument is '2'");
}
#[test]
fn test_batch_size_too_large() {
let large_batch_size = "18446744073709551616";
TestScenario::new(util_name!())
.ucmd()
.arg(format!("--batch-size={}", large_batch_size))
.fails()
.code_is(2)
.stderr_contains(format!(
"--batch-size argument '{}' too large",
large_batch_size
));
#[cfg(target_os = "linux")]
TestScenario::new(util_name!())
.ucmd()
.arg(format!("--batch-size={}", large_batch_size))
.fails()
.code_is(2)
.stderr_contains("maximum --batch-size argument with current rlimit is");
}
#[test] #[test]
fn test_merge_batch_size() { fn test_merge_batch_size() {
TestScenario::new(util_name!()) TestScenario::new(util_name!())

View file

@ -0,0 +1,32 @@
diff --git a/tests/sort/sort-merge.pl b/tests/sort/sort-merge.pl
index 89eed0c64..c2f5aa7e5 100755
--- a/tests/sort/sort-merge.pl
+++ b/tests/sort/sort-merge.pl
@@ -43,22 +43,22 @@ my @Tests =
# check validation of --batch-size option
['nmerge-0', "-m --batch-size=0", @inputs,
{ERR=>"$prog: invalid --batch-size argument '0'\n".
- "$prog: minimum --batch-size argument is '2'\n"}, {EXIT=>2}],
+ "$prog: minimum --batch-size argument is '2'\nTry 'sort --help' for more information.\n"}, {EXIT=>2}],
['nmerge-1', "-m --batch-size=1", @inputs,
{ERR=>"$prog: invalid --batch-size argument '1'\n".
- "$prog: minimum --batch-size argument is '2'\n"}, {EXIT=>2}],
+ "$prog: minimum --batch-size argument is '2'\nTry 'sort --help' for more information.\n"}, {EXIT=>2}],
['nmerge-neg', "-m --batch-size=-1", @inputs,
- {ERR=>"$prog: invalid --batch-size argument '-1'\n"}, {EXIT=>2}],
+ {ERR=>"$prog: invalid --batch-size argument '-1'\nTry 'sort --help' for more information.\n"}, {EXIT=>2}],
['nmerge-nan', "-m --batch-size=a", @inputs,
- {ERR=>"$prog: invalid --batch-size argument 'a'\n"}, {EXIT=>2}],
+ {ERR=>"$prog: invalid --batch-size argument 'a'\nTry 'sort --help' for more information.\n"}, {EXIT=>2}],
['nmerge-big', "-m --batch-size=$bigint", @inputs,
{ERR_SUBST=>'s/(current rlimit is) \d+/$1/'},
{ERR=>"$prog: --batch-size argument '$bigint' too large\n".
- "$prog: maximum --batch-size argument with current rlimit is\n"},
+ "$prog: maximum --batch-size argument with current rlimit is\nTry 'sort --help' for more information.\n"},
{EXIT=>2}],
# This should work since nmerge >= the number of input files