1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-29 12:07:46 +00:00

Merge pull request #3845 from jhscheer/fix_3842

tail: fix stdin redirect (#3842)
This commit is contained in:
Terts Diepraam 2022-09-07 19:10:45 +02:00 committed by GitHub
commit 987479d601
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 101 additions and 15 deletions

1
Cargo.lock generated
View file

@ -2816,6 +2816,7 @@ dependencies = [
"libc", "libc",
"nix", "nix",
"notify", "notify",
"same-file",
"uucore", "uucore",
"winapi", "winapi",
"winapi-util", "winapi-util",

View file

@ -20,6 +20,7 @@ clap = { version = "3.2", features = ["wrap_help", "cargo"] }
libc = "0.2.132" libc = "0.2.132"
notify = { version = "=5.0.0-pre.16", features=["macos_kqueue"]} notify = { version = "=5.0.0-pre.16", features=["macos_kqueue"]}
uucore = { version=">=0.0.15", package="uucore", path="../../uucore", features=["ringbuffer", "lines"] } uucore = { version=">=0.0.15", package="uucore", path="../../uucore", features=["ringbuffer", "lines"] }
same-file = "1.0.6"
[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"] }

View file

@ -30,7 +30,12 @@ pub struct ReverseChunks<'a> {
impl<'a> ReverseChunks<'a> { impl<'a> ReverseChunks<'a> {
pub fn new(file: &'a mut File) -> ReverseChunks<'a> { pub fn new(file: &'a mut File) -> ReverseChunks<'a> {
let size = file.seek(SeekFrom::End(0)).unwrap(); let current = if cfg!(unix) {
file.seek(SeekFrom::Current(0)).unwrap()
} else {
0
};
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 max_blocks_to_read = (size as f64 / BLOCK_SIZE as f64).ceil() as usize;
let block_idx = 0; let block_idx = 0;
ReverseChunks { ReverseChunks {

View file

@ -128,6 +128,8 @@ pub struct Settings {
use_polling: bool, use_polling: bool,
verbose: bool, verbose: bool,
stdin_is_pipe_or_fifo: bool, stdin_is_pipe_or_fifo: bool,
stdin_offset: u64,
stdin_redirect: PathBuf,
} }
impl Settings { impl Settings {
@ -316,6 +318,18 @@ fn uu_tail(mut settings: Settings) -> UResult<()> {
)); ));
} }
settings.stdin_redirect = dash.handle_redirect();
if cfg!(unix) && settings.stdin_is_pipe_or_fifo {
// Save the current seek position/offset of a stdin redirected file.
// This is needed to pass "gnu/tests/tail-2/start-middle.sh"
use same_file::Handle;
if let Ok(mut stdin_handle) = Handle::stdin() {
if let Ok(offset) = stdin_handle.as_file_mut().seek(SeekFrom::Current(0)) {
settings.stdin_offset = offset;
}
}
}
// add '-' to paths // add '-' to paths
if !settings.paths.contains(&dash) && settings.stdin_is_pipe_or_fifo if !settings.paths.contains(&dash) && settings.stdin_is_pipe_or_fifo
|| settings.paths.is_empty() && !settings.stdin_is_pipe_or_fifo || settings.paths.is_empty() && !settings.stdin_is_pipe_or_fifo
@ -341,7 +355,11 @@ fn uu_tail(mut settings: Settings) -> UResult<()> {
// Iterate user provided `paths` and add them to Watcher. // Iterate user provided `paths` and add them to Watcher.
if let Some((ref mut watcher, _)) = watcher_rx { if let Some((ref mut watcher, _)) = watcher_rx {
for path in &settings.paths { 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() { if path.is_stdin() {
continue; continue;
} }
@ -376,7 +394,11 @@ fn uu_tail(mut settings: Settings) -> UResult<()> {
} else { } else {
path.to_owned() 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(); let path_is_tailable = path.is_tailable();
if !path.is_stdin() && !path_is_tailable { if !path.is_stdin() && !path_is_tailable {
@ -436,9 +458,9 @@ fn uu_tail(mut settings: Settings) -> UResult<()> {
let metadata = path.metadata().ok(); let metadata = path.metadata().ok();
if display_name.is_stdin() && path_is_tailable { if display_name.is_stdin() && !path.is_file() {
if settings.verbose { if settings.verbose {
files.print_header(Path::new(text::STDIN_HEADER), !first_header); files.print_header(&display_name, !first_header);
first_header = false; first_header = false;
} }
@ -452,7 +474,7 @@ fn uu_tail(mut settings: Settings) -> UResult<()> {
PathData { PathData {
reader: Some(Box::new(reader)), reader: Some(Box::new(reader)),
metadata: None, metadata: None,
display_name: PathBuf::from(text::STDIN_HEADER), display_name,
}, },
true, true,
); );
@ -476,12 +498,17 @@ fn uu_tail(mut settings: Settings) -> UResult<()> {
match File::open(&path) { match File::open(&path) {
Ok(mut file) => { Ok(mut file) => {
if settings.verbose { if settings.verbose {
files.print_header(&path, !first_header); files.print_header(&display_name, !first_header);
first_header = false; 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); bounded_tail(&mut file, &settings);
reader = BufReader::new(file); reader = BufReader::new(file);
} else { } else {
@ -513,9 +540,11 @@ fn uu_tail(mut settings: Settings) -> UResult<()> {
} }
} }
} else if settings.retry && settings.follow.is_some() { } else if settings.retry && settings.follow.is_some() {
if path.is_relative() { let path = if path.is_relative() {
path = std::env::current_dir()?.join(&path); std::env::current_dir()?.join(&path)
} } else {
path.to_owned()
};
// Insert non-is_tailable() paths into `files.map` // Insert non-is_tailable() paths into `files.map`
files.insert( files.insert(
&path, &path,
@ -1544,14 +1573,16 @@ pub fn stdin_is_bad_fd() -> bool {
trait FileExtTail { trait FileExtTail {
#[allow(clippy::wrong_self_convention)] #[allow(clippy::wrong_self_convention)]
fn is_seekable(&mut self) -> bool; fn is_seekable(&mut self, current_offset: u64) -> bool;
} }
impl FileExtTail for File { 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::Current(0)).is_ok()
&& self.seek(SeekFrom::End(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

@ -76,6 +76,7 @@ fn test_stdin_redirect_file() {
.set_stdin(std::fs::File::open(at.plus("f")).unwrap()) .set_stdin(std::fs::File::open(at.plus("f")).unwrap())
.arg("-v") .arg("-v")
.run() .run()
.no_stderr()
.stdout_is("==> standard input <==\nfoo") .stdout_is("==> standard input <==\nfoo")
.succeeded(); .succeeded();
@ -93,6 +94,53 @@ fn test_stdin_redirect_file() {
assert!(buf_stderr.is_empty()); 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()
.no_stderr()
.stdout_is("2\n")
.succeeded();
}
#[test]
#[cfg(all(unix, not(any(target_os = "android", target_vendor = "apple"))))] // FIXME: make this work not just on Linux
fn test_stdin_redirect_offset2() {
// like test_stdin_redirect_offset but with multiple files
use std::io::{Seek, SeekFrom};
let ts = TestScenario::new(util_name!());
let at = &ts.fixtures;
at.write("k", "1\n2\n");
at.write("l", "3\n4\n");
at.write("m", "5\n6\n");
let mut fh = std::fs::File::open(at.plus("k")).unwrap();
fh.seek(SeekFrom::Start(2)).unwrap();
ts.ucmd()
.set_stdin(fh)
.args(&["k", "-", "l", "m"])
.run()
.no_stderr()
.stdout_is(
"==> k <==\n1\n2\n\n==> standard input <==\n2\n\n==> l <==\n3\n4\n\n==> m <==\n5\n6\n",
)
.succeeded();
}
#[test] #[test]
fn test_nc_0_wo_follow() { fn test_nc_0_wo_follow() {
// verify that -[nc]0 without -f, exit without reading // verify that -[nc]0 without -f, exit without reading