From 0d621f0e07d57eaa9be401547c49732755c647d6 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Sat, 20 Sep 2025 16:37:55 +0200 Subject: [PATCH] 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"];