1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-28 03:27:44 +00:00

tail: fix stdin redirect when file is not at its beginning

Previously, if stdin redirect pointed to a regular file,
tailing started at the beginning of the file. However,
tailing needs to start at the current position because this
is expected by tests/tail-2/start-middle.sh.

This fixes the issue by taking the current offset into account
while going backwards through the stdin redirected file.
This commit is contained in:
Jan Scheer 2022-08-29 01:41:06 +02:00 committed by Sylvestre Ledru
parent 92c3f60440
commit 942928b0ea
3 changed files with 60 additions and 11 deletions

View file

@ -30,7 +30,8 @@ pub struct ReverseChunks<'a> {
impl<'a> ReverseChunks<'a> {
pub fn new(file: &'a mut File) -> ReverseChunks<'a> {
let size = file.seek(SeekFrom::End(0)).unwrap();
let current = file.seek(SeekFrom::Current(0)).unwrap();
let size = file.seek(SeekFrom::End(0)).unwrap() - current;
let max_blocks_to_read = (size as f64 / BLOCK_SIZE as f64).ceil() as usize;
let block_idx = 0;
ReverseChunks {

View file

@ -128,6 +128,8 @@ pub struct Settings {
use_polling: bool,
verbose: bool,
stdin_is_pipe_or_fifo: bool,
stdin_offset: u64,
stdin_redirect: PathBuf,
}
impl Settings {
@ -323,6 +325,15 @@ fn uu_tail(mut settings: Settings) -> UResult<()> {
settings.paths.push_front(dash);
}
if cfg!(unix) && settings.stdin_is_pipe_or_fifo {
settings.stdin_redirect = PathBuf::from(text::STDIN_HEADER).handle_redirect();
use std::os::unix::io::FromRawFd;
let mut stdin_handle = unsafe { std::fs::File::from_raw_fd(0) };
if let Ok(offset) = stdin_handle.seek(SeekFrom::Current(0)) {
settings.stdin_offset = offset;
}
}
// TODO: is there a better way to check for a readable stdin?
let mut buf = [0; 0]; // empty buffer to check if stdin().read().is_err()
let stdin_read_possible = settings.stdin_is_pipe_or_fifo && stdin().read(&mut buf).is_ok();
@ -341,7 +352,11 @@ fn uu_tail(mut settings: Settings) -> UResult<()> {
// Iterate user provided `paths` and add them to Watcher.
if let Some((ref mut watcher, _)) = watcher_rx {
for path in &settings.paths {
let mut path = path.handle_redirect();
let mut path = if path.is_stdin() {
settings.stdin_redirect.to_owned()
} else {
path.to_owned()
};
if path.is_stdin() {
continue;
}
@ -376,7 +391,11 @@ fn uu_tail(mut settings: Settings) -> UResult<()> {
} else {
path.to_owned()
};
let mut path = path.handle_redirect();
let path = if path.is_stdin() {
settings.stdin_redirect.to_owned()
} else {
path.to_owned()
};
let path_is_tailable = path.is_tailable();
if !path.is_stdin() && !path_is_tailable {
@ -479,9 +498,14 @@ fn uu_tail(mut settings: Settings) -> UResult<()> {
files.print_header(&display_name, !first_header);
first_header = false;
}
let mut reader;
if file.is_seekable() && metadata.as_ref().unwrap().get_block_size() > 0 {
let mut reader;
if file.is_seekable(if display_name.is_stdin() {
settings.stdin_offset
} else {
0
}) && metadata.as_ref().unwrap().get_block_size() > 0
{
bounded_tail(&mut file, &settings);
reader = BufReader::new(file);
} else {
@ -513,9 +537,11 @@ fn uu_tail(mut settings: Settings) -> UResult<()> {
}
}
} else if settings.retry && settings.follow.is_some() {
if path.is_relative() {
path = std::env::current_dir()?.join(&path);
}
let path = if path.is_relative() {
std::env::current_dir()?.join(&path)
} else {
path.to_owned()
};
// Insert non-is_tailable() paths into `files.map`
files.insert(
&path,
@ -1544,14 +1570,16 @@ pub fn stdin_is_bad_fd() -> bool {
trait FileExtTail {
#[allow(clippy::wrong_self_convention)]
fn is_seekable(&mut self) -> bool;
fn is_seekable(&mut self, current_offset: u64) -> bool;
}
impl FileExtTail for File {
fn is_seekable(&mut self) -> bool {
/// Test if File is seekable.
/// Set the current position offset to `current_offset`.
fn is_seekable(&mut self, current_offset: u64) -> bool {
self.seek(SeekFrom::Current(0)).is_ok()
&& self.seek(SeekFrom::End(0)).is_ok()
&& self.seek(SeekFrom::Start(0)).is_ok()
&& self.seek(SeekFrom::Start(current_offset)).is_ok()
}
}

View file

@ -94,6 +94,26 @@ fn test_stdin_redirect_file() {
assert!(buf_stderr.is_empty());
}
#[test]
#[cfg(all(unix, not(any(target_os = "android", target_vendor = "apple"))))] // FIXME: make this work not just on Linux
fn test_stdin_redirect_offset() {
// inspired by: "gnu/tests/tail-2/start-middle.sh"
use std::io::{Seek, SeekFrom};
let ts = TestScenario::new(util_name!());
let at = &ts.fixtures;
at.write("k", "1\n2\n");
let mut fh = std::fs::File::open(at.plus("k")).unwrap();
fh.seek(SeekFrom::Start(2)).unwrap();
ts.ucmd()
.set_stdin(fh)
.run()
.stdout_is("2\n")
.succeeded();
}
#[test]
fn test_nc_0_wo_follow() {
// verify that -[nc]0 without -f, exit without reading