Skip to content

Commit a27a124

Browse files
authored
Merge pull request #8681 from cakebaker/seq_separator_non_utf8
seq: support non-utf8 for `--separator` & `--terminator`
2 parents af105d4 + 001ab89 commit a27a124

File tree

2 files changed

+65
-20
lines changed

2 files changed

+65
-20
lines changed

src/uu/seq/src/seq.rs

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// For the full copyright and license information, please view the LICENSE
44
// file that was distributed with this source code.
55
// spell-checker:ignore (ToDO) bigdecimal extendedbigdecimal numberparse hexadecimalfloat biguint
6-
use std::ffi::OsString;
6+
use std::ffi::{OsStr, OsString};
77
use std::io::{BufWriter, ErrorKind, Write, stdout};
88

99
use clap::{Arg, ArgAction, Command};
@@ -39,8 +39,8 @@ const ARG_NUMBERS: &str = "numbers";
3939

4040
#[derive(Clone)]
4141
struct SeqOptions<'a> {
42-
separator: String,
43-
terminator: String,
42+
separator: OsString,
43+
terminator: OsString,
4444
equal_width: bool,
4545
format: Option<&'a str>,
4646
}
@@ -105,14 +105,11 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
105105

106106
let options = SeqOptions {
107107
separator: matches
108-
.get_one::<String>(OPT_SEPARATOR)
109-
.map_or("\n", |s| s.as_str())
110-
.to_string(),
108+
.get_one::<OsString>(OPT_SEPARATOR)
109+
.map_or(OsString::from("\n"), |s| s.to_os_string()),
111110
terminator: matches
112-
.get_one::<String>(OPT_TERMINATOR)
113-
.map(|s| s.as_str())
114-
.unwrap_or("\n")
115-
.to_string(),
111+
.get_one::<OsString>(OPT_TERMINATOR)
112+
.map_or(OsString::from("\n"), |s| s.to_os_string()),
116113
equal_width: matches.get_flag(OPT_EQUAL_WIDTH),
117114
format: matches.get_one::<String>(OPT_FORMAT).map(|s| s.as_str()),
118115
};
@@ -229,13 +226,15 @@ pub fn uu_app() -> Command {
229226
Arg::new(OPT_SEPARATOR)
230227
.short('s')
231228
.long("separator")
232-
.help(translate!("seq-help-separator")),
229+
.help(translate!("seq-help-separator"))
230+
.value_parser(clap::value_parser!(OsString)),
233231
)
234232
.arg(
235233
Arg::new(OPT_TERMINATOR)
236234
.short('t')
237235
.long("terminator")
238-
.help(translate!("seq-help-terminator")),
236+
.help(translate!("seq-help-terminator"))
237+
.value_parser(clap::value_parser!(OsString)),
239238
)
240239
.arg(
241240
Arg::new(OPT_EQUAL_WIDTH)
@@ -267,8 +266,8 @@ fn fast_print_seq(
267266
first: &BigUint,
268267
increment: u64,
269268
last: &BigUint,
270-
separator: &str,
271-
terminator: &str,
269+
separator: &OsStr,
270+
terminator: &OsStr,
272271
padding: usize,
273272
) -> std::io::Result<()> {
274273
// Nothing to do, just return.
@@ -305,7 +304,7 @@ fn fast_print_seq(
305304

306305
// Initialize buf with first and separator.
307306
buf[start..num_end].copy_from_slice(first_str.as_bytes());
308-
buf[num_end..].copy_from_slice(separator.as_bytes());
307+
buf[num_end..].copy_from_slice(separator.as_encoded_bytes());
309308

310309
// Normally, if padding is > 0, it should be equal to last_length,
311310
// so start would be == 0, but there are corner cases.
@@ -321,7 +320,7 @@ fn fast_print_seq(
321320
}
322321
// Write the last number without separator, but with terminator.
323322
stdout.write_all(&buf[start..num_end])?;
324-
write!(stdout, "{terminator}")?;
323+
stdout.write_all(terminator.as_encoded_bytes())?;
325324
stdout.flush()?;
326325
Ok(())
327326
}
@@ -337,8 +336,8 @@ fn done_printing<T: Zero + PartialOrd>(next: &T, increment: &T, last: &T) -> boo
337336
/// Arbitrary precision decimal number code path ("slow" path)
338337
fn print_seq(
339338
range: RangeFloat,
340-
separator: &str,
341-
terminator: &str,
339+
separator: &OsStr,
340+
terminator: &OsStr,
342341
format: &Format<num_format::Float, &ExtendedBigDecimal>,
343342
fast_allowed: bool,
344343
padding: usize, // Used by fast path only
@@ -375,15 +374,15 @@ fn print_seq(
375374
let mut is_first_iteration = true;
376375
while !done_printing(&value, &increment, &last) {
377376
if !is_first_iteration {
378-
stdout.write_all(separator.as_bytes())?;
377+
stdout.write_all(separator.as_encoded_bytes())?;
379378
}
380379
format.fmt(&mut stdout, &value)?;
381380
// TODO Implement augmenting addition.
382381
value = value + increment.clone();
383382
is_first_iteration = false;
384383
}
385384
if !is_first_iteration {
386-
stdout.write_all(terminator.as_bytes())?;
385+
stdout.write_all(terminator.as_encoded_bytes())?;
387386
}
388387
stdout.flush()?;
389388
Ok(())

tests/by-util/test_seq.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,52 @@ fn test_separator_and_terminator() {
228228
.stdout_is("2\\n3\\n4\\n5\\n6\n");
229229
}
230230

231+
#[test]
232+
#[cfg(target_os = "linux")]
233+
fn test_separator_non_utf8() {
234+
use std::{ffi::OsString, os::unix::ffi::OsStringExt};
235+
236+
fn create_arg(prefix: &[u8]) -> OsString {
237+
let separator = [0xFF, 0xFE];
238+
OsString::from_vec([prefix, &separator].concat())
239+
}
240+
241+
let short = create_arg(b"-s");
242+
let long = create_arg(b"--separator=");
243+
let expected = [b'1', 0xFF, 0xFE, b'2', b'\n'];
244+
245+
for arg in [short, long] {
246+
new_ucmd!()
247+
.arg(&arg)
248+
.arg("2")
249+
.succeeds()
250+
.stdout_is_bytes(expected);
251+
}
252+
}
253+
254+
#[test]
255+
#[cfg(target_os = "linux")]
256+
fn test_terminator_non_utf8() {
257+
use std::{ffi::OsString, os::unix::ffi::OsStringExt};
258+
259+
fn create_arg(prefix: &[u8]) -> OsString {
260+
let terminator = [0xFF, 0xFE];
261+
OsString::from_vec([prefix, &terminator].concat())
262+
}
263+
264+
let short = create_arg(b"-t");
265+
let long = create_arg(b"--terminator=");
266+
let expected = [b'1', b'\n', b'2', 0xFF, 0xFE];
267+
268+
for arg in [short, long] {
269+
new_ucmd!()
270+
.arg(&arg)
271+
.arg("2")
272+
.succeeds()
273+
.stdout_is_bytes(expected);
274+
}
275+
}
276+
231277
#[test]
232278
fn test_equalize_widths() {
233279
let args = ["-w", "--equal-width"];

0 commit comments

Comments
 (0)