mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 19:47:45 +00:00
Merge pull request #2957 from jfinkels/head-preserve-no-final-newline
head: don't add trailing newline to end of file that didn't originally have one
This commit is contained in:
commit
0ed5e2c884
10 changed files with 29 additions and 91 deletions
|
@ -17,7 +17,7 @@ path = "src/head.rs"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = { version = "3.0", features = ["wrap_help", "cargo"] }
|
clap = { version = "3.0", features = ["wrap_help", "cargo"] }
|
||||||
memchr = "2"
|
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]]
|
[[bin]]
|
||||||
name = "head"
|
name = "head"
|
||||||
|
|
|
@ -11,6 +11,7 @@ use std::ffi::OsString;
|
||||||
use std::io::{self, BufWriter, ErrorKind, Read, Seek, SeekFrom, Write};
|
use std::io::{self, BufWriter, ErrorKind, Read, Seek, SeekFrom, Write};
|
||||||
use uucore::display::Quotable;
|
use uucore::display::Quotable;
|
||||||
use uucore::error::{FromIo, UError, UResult, USimpleError};
|
use uucore::error::{FromIo, UError, UResult, USimpleError};
|
||||||
|
use uucore::lines::lines;
|
||||||
use uucore::show;
|
use uucore::show;
|
||||||
|
|
||||||
const BUF_SIZE: usize = 65536;
|
const BUF_SIZE: usize = 65536;
|
||||||
|
@ -35,10 +36,8 @@ mod options {
|
||||||
pub const ZERO_NAME: &str = "ZERO";
|
pub const ZERO_NAME: &str = "ZERO";
|
||||||
pub const FILES_NAME: &str = "FILE";
|
pub const FILES_NAME: &str = "FILE";
|
||||||
}
|
}
|
||||||
mod lines;
|
|
||||||
mod parse;
|
mod parse;
|
||||||
mod take;
|
mod take;
|
||||||
use lines::zlines;
|
|
||||||
use take::take_all_but;
|
use take::take_all_but;
|
||||||
use take::take_lines;
|
use take::take_lines;
|
||||||
|
|
||||||
|
@ -286,12 +285,14 @@ fn read_but_last_n_lines(
|
||||||
if zero {
|
if zero {
|
||||||
let stdout = std::io::stdout();
|
let stdout = std::io::stdout();
|
||||||
let mut stdout = stdout.lock();
|
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?)?;
|
stdout.write_all(&bytes?)?;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for line in take_all_but(input.lines(), n) {
|
let stdout = std::io::stdout();
|
||||||
println!("{}", line?);
|
let mut stdout = stdout.lock();
|
||||||
|
for bytes in take_all_but(lines(input, b'\n'), n) {
|
||||||
|
stdout.write_all(&bytes?)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -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<B>(buf: B) -> ZLines<B> {
|
|
||||||
ZLines { buf }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An iterator over the zero-terminated lines of an instance of `BufRead`.
|
|
||||||
pub struct ZLines<B> {
|
|
||||||
buf: B,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<B: BufRead> Iterator for ZLines<B> {
|
|
||||||
type Item = std::io::Result<Vec<u8>>;
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<std::io::Result<Vec<u8>>> {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -17,7 +17,7 @@ path = "src/tail.rs"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = { version = "3.0", features = ["wrap_help", "cargo"] }
|
clap = { version = "3.0", features = ["wrap_help", "cargo"] }
|
||||||
libc = "0.2.42"
|
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]
|
[target.'cfg(windows)'.dependencies]
|
||||||
winapi = { version="0.3", features=["fileapi", "handleapi", "processthreadsapi", "synchapi", "winbase"] }
|
winapi = { version="0.3", features=["fileapi", "handleapi", "processthreadsapi", "synchapi", "winbase"] }
|
||||||
|
|
|
@ -16,11 +16,9 @@ extern crate clap;
|
||||||
extern crate uucore;
|
extern crate uucore;
|
||||||
|
|
||||||
mod chunks;
|
mod chunks;
|
||||||
mod lines;
|
|
||||||
mod parse;
|
mod parse;
|
||||||
mod platform;
|
mod platform;
|
||||||
use chunks::ReverseChunks;
|
use chunks::ReverseChunks;
|
||||||
use lines::lines;
|
|
||||||
|
|
||||||
use clap::{App, AppSettings, Arg};
|
use clap::{App, AppSettings, Arg};
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
@ -33,6 +31,7 @@ use std::thread::sleep;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use uucore::display::Quotable;
|
use uucore::display::Quotable;
|
||||||
use uucore::error::{FromIo, UResult, USimpleError};
|
use uucore::error::{FromIo, UResult, USimpleError};
|
||||||
|
use uucore::lines::lines;
|
||||||
use uucore::parse_size::{parse_size, ParseSizeError};
|
use uucore::parse_size::{parse_size, ParseSizeError};
|
||||||
use uucore::ringbuffer::RingBuffer;
|
use uucore::ringbuffer::RingBuffer;
|
||||||
|
|
||||||
|
|
|
@ -55,6 +55,7 @@ encoding = ["data-encoding", "data-encoding-macro", "z85", "thiserror"]
|
||||||
entries = ["libc"]
|
entries = ["libc"]
|
||||||
fs = ["libc", "nix", "winapi-util"]
|
fs = ["libc", "nix", "winapi-util"]
|
||||||
fsext = ["libc", "time"]
|
fsext = ["libc", "time"]
|
||||||
|
lines = []
|
||||||
memo = ["itertools"]
|
memo = ["itertools"]
|
||||||
mode = ["libc"]
|
mode = ["libc"]
|
||||||
perms = ["libc", "walkdir"]
|
perms = ["libc", "walkdir"]
|
||||||
|
|
|
@ -6,6 +6,8 @@ pub mod encoding;
|
||||||
pub mod fs;
|
pub mod fs;
|
||||||
#[cfg(feature = "fsext")]
|
#[cfg(feature = "fsext")]
|
||||||
pub mod fsext;
|
pub mod fsext;
|
||||||
|
#[cfg(feature = "lines")]
|
||||||
|
pub mod lines;
|
||||||
#[cfg(feature = "memo")]
|
#[cfg(feature = "memo")]
|
||||||
pub mod memo;
|
pub mod memo;
|
||||||
#[cfg(feature = "ringbuffer")]
|
#[cfg(feature = "ringbuffer")]
|
||||||
|
|
|
@ -2,15 +2,17 @@
|
||||||
// *
|
// *
|
||||||
// * For the full copyright and license information, please view the LICENSE
|
// * For the full copyright and license information, please view the LICENSE
|
||||||
// * file that was distributed with this source code.
|
// * file that was distributed with this source code.
|
||||||
|
// spell-checker:ignore (vars)
|
||||||
//! Iterate over lines, including the line ending character(s).
|
//! Iterate over lines, including the line ending character(s).
|
||||||
//!
|
//!
|
||||||
//! This module provides the [`lines`] function, similar to the
|
//! This module provides the [`lines`] function, similar to the
|
||||||
//! [`BufRead::lines`] method. While the [`BufRead::lines`] method
|
//! [`BufRead::lines`] method. While the [`BufRead::lines`] method
|
||||||
//! yields [`String`] instances that do not include the line ending
|
//! yields [`String`] instances that do not include the line ending
|
||||||
//! characters (`"\n"` or `"\r\n"`), our function yields [`String`]
|
//! characters (`"\n"` or `"\r\n"`), our functions yield
|
||||||
//! instances that include the line ending characters. This is useful
|
//! [`Vec`]<['u8']> instances that include the line ending
|
||||||
//! if the input data does not end with a newline character and you
|
//! characters. This is useful if the input data does not end with a
|
||||||
//! want to preserve the exact form of the input data.
|
//! newline character and you want to preserve the exact form of the
|
||||||
|
//! input data.
|
||||||
use std::io::BufRead;
|
use std::io::BufRead;
|
||||||
|
|
||||||
/// Returns an iterator over the lines, including line ending characters.
|
/// 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(), Some(Vec::from("z")));
|
||||||
/// assert_eq!(it.next(), None);
|
/// assert_eq!(it.next(), None);
|
||||||
/// ```
|
/// ```
|
||||||
pub(crate) fn lines<B>(reader: B, sep: u8) -> Lines<B>
|
pub fn lines<B>(reader: B, sep: u8) -> Lines<B>
|
||||||
where
|
where
|
||||||
B: BufRead,
|
B: BufRead,
|
||||||
{
|
{
|
||||||
|
@ -62,7 +64,7 @@ where
|
||||||
///
|
///
|
||||||
/// This struct is generally created by calling [`lines`] on a `BufRead`.
|
/// This struct is generally created by calling [`lines`] on a `BufRead`.
|
||||||
/// Please see the documentation of [`lines`] for more details.
|
/// Please see the documentation of [`lines`] for more details.
|
||||||
pub(crate) struct Lines<B> {
|
pub struct Lines<B> {
|
||||||
buf: B,
|
buf: B,
|
||||||
sep: u8,
|
sep: u8,
|
||||||
}
|
}
|
|
@ -38,6 +38,8 @@ pub use crate::features::encoding;
|
||||||
pub use crate::features::fs;
|
pub use crate::features::fs;
|
||||||
#[cfg(feature = "fsext")]
|
#[cfg(feature = "fsext")]
|
||||||
pub use crate::features::fsext;
|
pub use crate::features::fsext;
|
||||||
|
#[cfg(feature = "lines")]
|
||||||
|
pub use crate::features::lines;
|
||||||
#[cfg(feature = "memo")]
|
#[cfg(feature = "memo")]
|
||||||
pub use crate::features::memo;
|
pub use crate::features::memo;
|
||||||
#[cfg(feature = "ringbuffer")]
|
#[cfg(feature = "ringbuffer")]
|
||||||
|
|
|
@ -157,11 +157,17 @@ fn test_negative_byte_syntax() {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_negative_zero_lines() {
|
fn test_negative_zero_lines() {
|
||||||
new_ucmd!()
|
new_ucmd!()
|
||||||
.args(&["--lines=-0"])
|
.arg("--lines=-0")
|
||||||
.pipe_in("a\nb\n")
|
.pipe_in("a\nb\n")
|
||||||
.succeeds()
|
.succeeds()
|
||||||
.stdout_is("a\nb\n");
|
.stdout_is("a\nb\n");
|
||||||
|
new_ucmd!()
|
||||||
|
.arg("--lines=-0")
|
||||||
|
.pipe_in("a\nb")
|
||||||
|
.succeeds()
|
||||||
|
.stdout_is("a\nb");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_negative_zero_bytes() {
|
fn test_negative_zero_bytes() {
|
||||||
new_ucmd!()
|
new_ucmd!()
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue