diff --git a/src/uu/rev/src/rev.rs b/src/uu/rev/src/rev.rs index db1a907..b06d377 100644 --- a/src/uu/rev/src/rev.rs +++ b/src/uu/rev/src/rev.rs @@ -12,11 +12,18 @@ use uucore::{error::UResult, format_usage, help_about, help_usage}; const ABOUT: &str = help_about!("rev.md"); const USAGE: &str = help_usage!("rev.md"); +mod options { + pub const FILE: &str = "file"; + pub const ZERO: &str = "zero"; +} + #[uucore::main] pub fn uumain(args: impl uucore::Args) -> UResult<()> { let matches: clap::ArgMatches = uu_app().try_get_matches_from(args)?; - let files = matches.get_many::<String>("file"); + let files = matches.get_many::<String>(options::FILE); + let zero = matches.get_flag(options::ZERO); + let sep = if zero { b'\0' } else { b'\n' }; match files { Some(files) => { for path in files { @@ -25,7 +32,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { uucore::show_error!("cannot open {path}: No such file or directory"); continue; }; - if let Err(err) = rev_stream(file) { + if let Err(err) = rev_stream(file, sep) { uucore::error::set_exit_code(1); uucore::show_error!("cannot read {path}: {err}"); } @@ -33,28 +40,28 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { } None => { let stdin = std::io::stdin().lock(); - let _ = rev_stream(stdin); + let _ = rev_stream(stdin, sep); } } Ok(()) } -fn rev_stream(stream: impl Read) -> std::io::Result<()> { +fn rev_stream(stream: impl Read, sep: u8) -> std::io::Result<()> { let mut stdout = std::io::stdout().lock(); let mut stream = BufReader::new(stream); let mut buf = Vec::with_capacity(4096); loop { buf.clear(); - stream.read_until(b'\n', &mut buf)?; - if buf.last().copied() != Some(b'\n') { + stream.read_until(sep, &mut buf)?; + if buf.last().copied() != Some(sep) { buf.reverse(); stdout.write_all(&buf)?; break; } else { buf.pop(); buf.reverse(); - buf.push(b'\n'); + buf.push(sep); stdout.write_all(&buf)?; } } @@ -68,11 +75,18 @@ pub fn uu_app() -> Command { .override_usage(format_usage(USAGE)) .infer_long_args(true) .arg( - Arg::new("file") + Arg::new(options::FILE) .value_name("FILE") .help("Paths of files to reverse") .index(1) .action(ArgAction::Set) .num_args(1..), ) + .arg( + Arg::new(options::ZERO) + .short('0') + .long("zero") + .help("Zero termination. Use the byte '\\0' as line separator.") + .action(ArgAction::SetTrue), + ) } diff --git a/tests/by-util/test_rev.rs b/tests/by-util/test_rev.rs index b1ad67a..6fe7335 100644 --- a/tests/by-util/test_rev.rs +++ b/tests/by-util/test_rev.rs @@ -24,6 +24,18 @@ fn test_existing_file() { ucmd.arg("a.txt").succeeds().stdout_is("A enil\nB enil"); } +#[test] +fn test_zero() { + let (at, mut ucmd) = at_and_ucmd!(); + + at.write("a.txt", "line A\0line B"); + + ucmd.arg("a.txt") + .arg("--zero") + .succeeds() + .stdout_is("A enil\0B enil"); +} + #[test] fn test_multiple_files() { let (at, mut ucmd) = at_and_ucmd!();