From 0d621f0e07d57eaa9be401547c49732755c647d6 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Sat, 20 Sep 2025 16:37:55 +0200 Subject: [PATCH 1/2] seq: support non-utf8 for --separator --- src/uu/seq/src/seq.rs | 20 ++++++++++---------- tests/by-util/test_seq.rs | 23 +++++++++++++++++++++++ 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/src/uu/seq/src/seq.rs b/src/uu/seq/src/seq.rs index a489e54b9..c90cdb262 100644 --- a/src/uu/seq/src/seq.rs +++ b/src/uu/seq/src/seq.rs @@ -3,7 +3,7 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. // spell-checker:ignore (ToDO) bigdecimal extendedbigdecimal numberparse hexadecimalfloat biguint -use std::ffi::OsString; +use std::ffi::{OsStr, OsString}; use std::io::{BufWriter, ErrorKind, Write, stdout}; use clap::{Arg, ArgAction, Command}; @@ -39,7 +39,7 @@ const ARG_NUMBERS: &str = "numbers"; #[derive(Clone)] struct SeqOptions<'a> { - separator: String, + separator: OsString, terminator: String, equal_width: bool, format: Option<&'a str>, @@ -105,9 +105,8 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { let options = SeqOptions { separator: matches - .get_one::(OPT_SEPARATOR) - .map_or("\n", |s| s.as_str()) - .to_string(), + .get_one::(OPT_SEPARATOR) + .map_or(OsString::from("\n"), |s| s.to_os_string()), terminator: matches .get_one::(OPT_TERMINATOR) .map(|s| s.as_str()) @@ -229,7 +228,8 @@ pub fn uu_app() -> Command { Arg::new(OPT_SEPARATOR) .short('s') .long("separator") - .help(translate!("seq-help-separator")), + .help(translate!("seq-help-separator")) + .value_parser(clap::value_parser!(OsString)), ) .arg( Arg::new(OPT_TERMINATOR) @@ -267,7 +267,7 @@ fn fast_print_seq( first: &BigUint, increment: u64, last: &BigUint, - separator: &str, + separator: &OsStr, terminator: &str, padding: usize, ) -> std::io::Result<()> { @@ -305,7 +305,7 @@ fn fast_print_seq( // Initialize buf with first and separator. buf[start..num_end].copy_from_slice(first_str.as_bytes()); - buf[num_end..].copy_from_slice(separator.as_bytes()); + buf[num_end..].copy_from_slice(separator.as_encoded_bytes()); // Normally, if padding is > 0, it should be equal to last_length, // so start would be == 0, but there are corner cases. @@ -337,7 +337,7 @@ fn done_printing(next: &T, increment: &T, last: &T) -> boo /// Arbitrary precision decimal number code path ("slow" path) fn print_seq( range: RangeFloat, - separator: &str, + separator: &OsStr, terminator: &str, format: &Format, fast_allowed: bool, @@ -375,7 +375,7 @@ fn print_seq( let mut is_first_iteration = true; while !done_printing(&value, &increment, &last) { if !is_first_iteration { - stdout.write_all(separator.as_bytes())?; + stdout.write_all(separator.as_encoded_bytes())?; } format.fmt(&mut stdout, &value)?; // TODO Implement augmenting addition. diff --git a/tests/by-util/test_seq.rs b/tests/by-util/test_seq.rs index a4f49ea41..3308b4cb5 100644 --- a/tests/by-util/test_seq.rs +++ b/tests/by-util/test_seq.rs @@ -228,6 +228,29 @@ fn test_separator_and_terminator() { .stdout_is("2\\n3\\n4\\n5\\n6\n"); } +#[test] +#[cfg(target_os = "linux")] +fn test_separator_non_utf8() { + use std::{ffi::OsString, os::unix::ffi::OsStringExt}; + + fn create_arg(prefix: &[u8]) -> OsString { + let separator = [0xFF, 0xFE]; + OsString::from_vec([prefix, &separator].concat()) + } + + let short = create_arg(b"-s"); + let long = create_arg(b"--separator="); + let expected = [b'1', 0xFF, 0xFE, b'2', b'\n']; + + for arg in [short, long] { + new_ucmd!() + .arg(&arg) + .arg("2") + .succeeds() + .stdout_is_bytes(expected); + } +} + #[test] fn test_equalize_widths() { let args = ["-w", "--equal-width"]; From 001ab890d55c4bcdb92a0f883d834e852412aeac Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Sat, 20 Sep 2025 17:38:42 +0200 Subject: [PATCH 2/2] seq: support non-utf8 for --terminator --- src/uu/seq/src/seq.rs | 19 +++++++++---------- tests/by-util/test_seq.rs | 23 +++++++++++++++++++++++ 2 files changed, 32 insertions(+), 10 deletions(-) diff --git a/src/uu/seq/src/seq.rs b/src/uu/seq/src/seq.rs index c90cdb262..4c050c2c7 100644 --- a/src/uu/seq/src/seq.rs +++ b/src/uu/seq/src/seq.rs @@ -40,7 +40,7 @@ const ARG_NUMBERS: &str = "numbers"; #[derive(Clone)] struct SeqOptions<'a> { separator: OsString, - terminator: String, + terminator: OsString, equal_width: bool, format: Option<&'a str>, } @@ -108,10 +108,8 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { .get_one::(OPT_SEPARATOR) .map_or(OsString::from("\n"), |s| s.to_os_string()), terminator: matches - .get_one::(OPT_TERMINATOR) - .map(|s| s.as_str()) - .unwrap_or("\n") - .to_string(), + .get_one::(OPT_TERMINATOR) + .map_or(OsString::from("\n"), |s| s.to_os_string()), equal_width: matches.get_flag(OPT_EQUAL_WIDTH), format: matches.get_one::(OPT_FORMAT).map(|s| s.as_str()), }; @@ -235,7 +233,8 @@ pub fn uu_app() -> Command { Arg::new(OPT_TERMINATOR) .short('t') .long("terminator") - .help(translate!("seq-help-terminator")), + .help(translate!("seq-help-terminator")) + .value_parser(clap::value_parser!(OsString)), ) .arg( Arg::new(OPT_EQUAL_WIDTH) @@ -268,7 +267,7 @@ fn fast_print_seq( increment: u64, last: &BigUint, separator: &OsStr, - terminator: &str, + terminator: &OsStr, padding: usize, ) -> std::io::Result<()> { // Nothing to do, just return. @@ -321,7 +320,7 @@ fn fast_print_seq( } // Write the last number without separator, but with terminator. stdout.write_all(&buf[start..num_end])?; - write!(stdout, "{terminator}")?; + stdout.write_all(terminator.as_encoded_bytes())?; stdout.flush()?; Ok(()) } @@ -338,7 +337,7 @@ fn done_printing(next: &T, increment: &T, last: &T) -> boo fn print_seq( range: RangeFloat, separator: &OsStr, - terminator: &str, + terminator: &OsStr, format: &Format, fast_allowed: bool, padding: usize, // Used by fast path only @@ -383,7 +382,7 @@ fn print_seq( is_first_iteration = false; } if !is_first_iteration { - stdout.write_all(terminator.as_bytes())?; + stdout.write_all(terminator.as_encoded_bytes())?; } stdout.flush()?; Ok(()) diff --git a/tests/by-util/test_seq.rs b/tests/by-util/test_seq.rs index 3308b4cb5..f82a6228f 100644 --- a/tests/by-util/test_seq.rs +++ b/tests/by-util/test_seq.rs @@ -251,6 +251,29 @@ fn test_separator_non_utf8() { } } +#[test] +#[cfg(target_os = "linux")] +fn test_terminator_non_utf8() { + use std::{ffi::OsString, os::unix::ffi::OsStringExt}; + + fn create_arg(prefix: &[u8]) -> OsString { + let terminator = [0xFF, 0xFE]; + OsString::from_vec([prefix, &terminator].concat()) + } + + let short = create_arg(b"-t"); + let long = create_arg(b"--terminator="); + let expected = [b'1', b'\n', b'2', 0xFF, 0xFE]; + + for arg in [short, long] { + new_ucmd!() + .arg(&arg) + .arg("2") + .succeeds() + .stdout_is_bytes(expected); + } +} + #[test] fn test_equalize_widths() { let args = ["-w", "--equal-width"];