parent
5ab27330e9
commit
7d114bf4d5
@ -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!();
|
||||
|
Loading…
x
Reference in New Issue
Block a user