rev: add option --zero

closes 
This commit is contained in:
Yang Hau 2024-07-28 01:55:07 +08:00
parent 5ab27330e9
commit 7d114bf4d5
2 changed files with 34 additions and 8 deletions
src/uu/rev/src
tests/by-util

@ -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),
)
}

@ -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!();