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:
parent
92c3f60440
commit
942928b0ea
3 changed files with 60 additions and 11 deletions
|
@ -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 {
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue