diff --git a/src/uu/head/Cargo.toml b/src/uu/head/Cargo.toml index 5d05f1921..04a512492 100644 --- a/src/uu/head/Cargo.toml +++ b/src/uu/head/Cargo.toml @@ -17,7 +17,7 @@ path = "src/head.rs" [dependencies] clap = { version = "3.0", features = ["wrap_help", "cargo"] } memchr = "2" -uucore = { version=">=0.0.11", package="uucore", path="../../uucore", features=["ringbuffer"] } +uucore = { version=">=0.0.11", package="uucore", path="../../uucore", features=["ringbuffer", "lines"] } [[bin]] name = "head" diff --git a/src/uu/head/src/head.rs b/src/uu/head/src/head.rs index 9fcdb3faa..ac2e4561e 100644 --- a/src/uu/head/src/head.rs +++ b/src/uu/head/src/head.rs @@ -11,6 +11,7 @@ use std::ffi::OsString; use std::io::{self, BufWriter, ErrorKind, Read, Seek, SeekFrom, Write}; use uucore::display::Quotable; use uucore::error::{FromIo, UError, UResult, USimpleError}; +use uucore::lines::lines; use uucore::show; const BUF_SIZE: usize = 65536; @@ -35,10 +36,8 @@ mod options { pub const ZERO_NAME: &str = "ZERO"; pub const FILES_NAME: &str = "FILE"; } -mod lines; mod parse; mod take; -use lines::zlines; use take::take_all_but; use take::take_lines; @@ -286,12 +285,14 @@ fn read_but_last_n_lines( if zero { let stdout = std::io::stdout(); let mut stdout = stdout.lock(); - for bytes in take_all_but(zlines(input), n) { + for bytes in take_all_but(lines(input, b'\0'), n) { stdout.write_all(&bytes?)?; } } else { - for line in take_all_but(input.lines(), n) { - println!("{}", line?); + let stdout = std::io::stdout(); + let mut stdout = stdout.lock(); + for bytes in take_all_but(lines(input, b'\n'), n) { + stdout.write_all(&bytes?)?; } } Ok(()) diff --git a/src/uu/head/src/lines.rs b/src/uu/head/src/lines.rs deleted file mode 100644 index 474f5717d..000000000 --- a/src/uu/head/src/lines.rs +++ /dev/null @@ -1,75 +0,0 @@ -// spell-checker:ignore (vars) zline zlines - -//! Iterate over zero-terminated lines. -use std::io::BufRead; - -/// The zero byte, representing the null character. -const ZERO: u8 = 0; - -/// Returns an iterator over the lines of the given reader. -/// -/// The iterator returned from this function will yield instances of -/// [`std::io::Result`]<[`Vec`]<[`u8`]>>, representing the bytes of the line -/// *including* the null character (with the possible exception of the -/// last line, which may not have one). -/// -/// # Examples -/// -/// ```rust,ignore -/// use std::io::Cursor; -/// -/// let cursor = Cursor::new(b"x\0y\0z\0"); -/// let mut iter = zlines(cursor).map(|l| l.unwrap()); -/// assert_eq!(iter.next(), Some(b"x\0".to_vec())); -/// assert_eq!(iter.next(), Some(b"y\0".to_vec())); -/// assert_eq!(iter.next(), Some(b"z\0".to_vec())); -/// assert_eq!(iter.next(), None); -/// ``` -pub fn zlines(buf: B) -> ZLines { - ZLines { buf } -} - -/// An iterator over the zero-terminated lines of an instance of `BufRead`. -pub struct ZLines { - buf: B, -} - -impl Iterator for ZLines { - type Item = std::io::Result>; - - fn next(&mut self) -> Option>> { - let mut buf = Vec::new(); - match self.buf.read_until(ZERO, &mut buf) { - Ok(0) => None, - Ok(_) => Some(Ok(buf)), - Err(e) => Some(Err(e)), - } - } -} - -#[cfg(test)] -mod tests { - - use crate::lines::zlines; - use std::io::Cursor; - - #[test] - fn test_null_terminated() { - let cursor = Cursor::new(b"x\0y\0z\0"); - let mut iter = zlines(cursor).map(|l| l.unwrap()); - assert_eq!(iter.next(), Some(b"x\0".to_vec())); - assert_eq!(iter.next(), Some(b"y\0".to_vec())); - assert_eq!(iter.next(), Some(b"z\0".to_vec())); - assert_eq!(iter.next(), None); - } - - #[test] - fn test_not_null_terminated() { - let cursor = Cursor::new(b"x\0y\0z"); - let mut iter = zlines(cursor).map(|l| l.unwrap()); - assert_eq!(iter.next(), Some(b"x\0".to_vec())); - assert_eq!(iter.next(), Some(b"y\0".to_vec())); - assert_eq!(iter.next(), Some(b"z".to_vec())); - assert_eq!(iter.next(), None); - } -} diff --git a/src/uu/tail/Cargo.toml b/src/uu/tail/Cargo.toml index d70502dab..4f40431b1 100644 --- a/src/uu/tail/Cargo.toml +++ b/src/uu/tail/Cargo.toml @@ -17,7 +17,7 @@ path = "src/tail.rs" [dependencies] clap = { version = "3.0", features = ["wrap_help", "cargo"] } libc = "0.2.42" -uucore = { version=">=0.0.11", package="uucore", path="../../uucore", features=["ringbuffer"] } +uucore = { version=">=0.0.11", package="uucore", path="../../uucore", features=["ringbuffer", "lines"] } [target.'cfg(windows)'.dependencies] winapi = { version="0.3", features=["fileapi", "handleapi", "processthreadsapi", "synchapi", "winbase"] } diff --git a/src/uu/tail/src/tail.rs b/src/uu/tail/src/tail.rs index 951399866..2c9a248f0 100644 --- a/src/uu/tail/src/tail.rs +++ b/src/uu/tail/src/tail.rs @@ -16,11 +16,9 @@ extern crate clap; extern crate uucore; mod chunks; -mod lines; mod parse; mod platform; use chunks::ReverseChunks; -use lines::lines; use clap::{App, AppSettings, Arg}; use std::collections::VecDeque; @@ -33,6 +31,7 @@ use std::thread::sleep; use std::time::Duration; use uucore::display::Quotable; use uucore::error::{FromIo, UResult, USimpleError}; +use uucore::lines::lines; use uucore::parse_size::{parse_size, ParseSizeError}; use uucore::ringbuffer::RingBuffer; diff --git a/src/uucore/Cargo.toml b/src/uucore/Cargo.toml index 3a6bf25c1..5bd5994cc 100644 --- a/src/uucore/Cargo.toml +++ b/src/uucore/Cargo.toml @@ -55,6 +55,7 @@ encoding = ["data-encoding", "data-encoding-macro", "z85", "thiserror"] entries = ["libc"] fs = ["libc", "nix", "winapi-util"] fsext = ["libc", "time"] +lines = [] memo = ["itertools"] mode = ["libc"] perms = ["libc", "walkdir"] diff --git a/src/uucore/src/lib/features.rs b/src/uucore/src/lib/features.rs index 999d8af6c..b1b87a613 100644 --- a/src/uucore/src/lib/features.rs +++ b/src/uucore/src/lib/features.rs @@ -6,6 +6,8 @@ pub mod encoding; pub mod fs; #[cfg(feature = "fsext")] pub mod fsext; +#[cfg(feature = "lines")] +pub mod lines; #[cfg(feature = "memo")] pub mod memo; #[cfg(feature = "ringbuffer")] diff --git a/src/uu/tail/src/lines.rs b/src/uucore/src/lib/features/lines.rs similarity index 89% rename from src/uu/tail/src/lines.rs rename to src/uucore/src/lib/features/lines.rs index ee8b36662..a7f4df76d 100644 --- a/src/uu/tail/src/lines.rs +++ b/src/uucore/src/lib/features/lines.rs @@ -2,15 +2,17 @@ // * // * For the full copyright and license information, please view the LICENSE // * file that was distributed with this source code. +// spell-checker:ignore (vars) //! Iterate over lines, including the line ending character(s). //! //! This module provides the [`lines`] function, similar to the //! [`BufRead::lines`] method. While the [`BufRead::lines`] method //! yields [`String`] instances that do not include the line ending -//! characters (`"\n"` or `"\r\n"`), our function yields [`String`] -//! instances that include the line ending characters. This is useful -//! if the input data does not end with a newline character and you -//! want to preserve the exact form of the input data. +//! characters (`"\n"` or `"\r\n"`), our functions yield +//! [`Vec`]<['u8']> instances that include the line ending +//! characters. This is useful if the input data does not end with a +//! newline character and you want to preserve the exact form of the +//! input data. use std::io::BufRead; /// Returns an iterator over the lines, including line ending characters. @@ -51,7 +53,7 @@ use std::io::BufRead; /// assert_eq!(it.next(), Some(Vec::from("z"))); /// assert_eq!(it.next(), None); /// ``` -pub(crate) fn lines(reader: B, sep: u8) -> Lines +pub fn lines(reader: B, sep: u8) -> Lines where B: BufRead, { @@ -62,7 +64,7 @@ where /// /// This struct is generally created by calling [`lines`] on a `BufRead`. /// Please see the documentation of [`lines`] for more details. -pub(crate) struct Lines { +pub struct Lines { buf: B, sep: u8, } diff --git a/src/uucore/src/lib/lib.rs b/src/uucore/src/lib/lib.rs index ae7788e05..4dc5e6987 100644 --- a/src/uucore/src/lib/lib.rs +++ b/src/uucore/src/lib/lib.rs @@ -38,6 +38,8 @@ pub use crate::features::encoding; pub use crate::features::fs; #[cfg(feature = "fsext")] pub use crate::features::fsext; +#[cfg(feature = "lines")] +pub use crate::features::lines; #[cfg(feature = "memo")] pub use crate::features::memo; #[cfg(feature = "ringbuffer")] diff --git a/tests/by-util/test_head.rs b/tests/by-util/test_head.rs index e3f4e79aa..1c4a01557 100644 --- a/tests/by-util/test_head.rs +++ b/tests/by-util/test_head.rs @@ -157,11 +157,17 @@ fn test_negative_byte_syntax() { #[test] fn test_negative_zero_lines() { new_ucmd!() - .args(&["--lines=-0"]) + .arg("--lines=-0") .pipe_in("a\nb\n") .succeeds() .stdout_is("a\nb\n"); + new_ucmd!() + .arg("--lines=-0") + .pipe_in("a\nb") + .succeeds() + .stdout_is("a\nb"); } + #[test] fn test_negative_zero_bytes() { new_ucmd!()