Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 27 additions & 4 deletions src/uu/sort/src/sort.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2118,15 +2118,38 @@
// https://github.com/uutils/coreutils/issues/2424#issuecomment-863825242,
// and the same rewrite in `cut`), so rewrite every attached `-t<chars>`
// argument to its long form, which preserves the separator verbatim.
let args = args.into_iter().map(|x| {
let args = args.into_iter().flat_map(|x| {
// Non-UTF-8 separators are rejected later anyway, so lossy conversion
// here only affects arguments that cannot become a valid separator.
let as_str = x.to_string_lossy();
// Attached form -t<chars>: route through long option to preserve verbatim.
if as_str.starts_with("-t") && as_str.chars().count() > 2 {
OsString::from(format!("--{}={}", options::SEPARATOR, &as_str[2..]))
} else {
x
return vec![OsString::from(format!(
"--{}={}",
options::SEPARATOR,
&as_str[2..]
))];
}
// Clustered form, e.g. -nt=5: split into -<flags>t + rest so clap
// passes the value through unstripped. Only for valueless flag clusters.
let valueless_shorts = "bCcdfghimMnRrsuz";

Check warning on line 2135 in src/uu/sort/src/sort.rs

View workflow job for this annotation

GitHub Actions / Style/spelling (ubuntu-latest, feat_os_unix)

WARNING: `cspell`: Unknown word 'Ccdfghim' (file:'src/uu/sort/src/sort.rs', line:2135)
let bytes = as_str.as_bytes();
if bytes.len() > 3
&& bytes[0] == b'-'
&& bytes[1] != b'-'
&& let Some(t_idx) = (1..bytes.len() - 1).find(|&i| bytes[i] == b't')
{
let flags_before = &as_str[1..t_idx];
if !flags_before.is_empty()
&& flags_before.chars().all(|c| valueless_shorts.contains(c))
{
return vec![
OsString::from(format!("-{flags_before}t")),
OsString::from(&as_str[t_idx + 1..]),
];
}
}
vec![x]
});
let args: Vec<OsString> = args.collect();

Expand Down
33 changes: 32 additions & 1 deletion tests/by-util/test_sort.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1819,12 +1819,43 @@ fn test_separator_attached_equals_double() {
.stderr_contains("separator must be exactly one character long: '=='");
}

#[test]
fn test_separator_clustered_attached() {
// `-nt=5`: -n is a flag, -t takes the rest of the argument (`=5`)
// verbatim, which GNU rejects as multi-character.
new_ucmd!()
.args(&["-nt=5"])
.pipe_in("a=b=c\n")
.fails()
.stderr_contains("'=5'");
}

#[test]
fn test_separator_clustered_attached_b() {
new_ucmd!()
.args(&["-bt=x"])
.pipe_in("a=b=c\n")
.fails()
.stderr_contains("'=x'");
}

#[test]
fn test_separator_clustered_still_sorts() {
// The rewrite must not disturb value-taking shorts inside clusters'
// siblings: -n plus a working attached separator. #14120
// Uses numeric field values so -n actually sorts.
new_ucmd!()
.args(&["-nt=", "-k", "2"])
.pipe_in("3=b\n1=a\n2=c\n")
.succeeds()
.stdout_only("1=a\n2=c\n3=b\n");
}

#[test]
fn test_separator_attached_equals_multi_char() {
// `-t=a` selects the two-character separator `=a`, which GNU rejects.
new_ucmd!()
.args(&["-t=a", "-k", "2"])
.pipe_in("a=b=c\n")
.fails()
.stderr_contains("separator must be exactly one character long: '=a'");
}
Expand Down
Loading