Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/uu/tail/src/tail.rs
Original file line number Diff line number Diff line change
Expand Up @@ -469,7 +469,11 @@
file.seek(SeekFrom::End(0)).unwrap();
}
FilterMode::Bytes(Signum::Negative(count)) => {
if file.seek(SeekFrom::End(-(*count as i64))).is_err() {
// A count above `i64::MAX` has no negative counterpart to seek by,
// and reaches further back than any file can be long, so treat it
// like any other offset landing before the start of the file.
Comment on lines +472 to +474

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you write this as one line?

let offset = i64::try_from(*count).ok();
if offset.is_none_or(|offset| file.seek(SeekFrom::End(-offset)).is_err()) {
file.seek(SeekFrom::Start(0)).unwrap();
}
limit = Some(*count);
Expand All @@ -478,7 +482,7 @@
// GNU `tail` seems to index bytes and lines starting at 1, not
// at 0. It seems to treat `+0` and `+1` as the same thing.
// A start offset past the largest seekable position makes the
// underlying `lseek` fail with `EINVAL`; treat that like a start

Check warning on line 485 in src/uu/tail/src/tail.rs

View workflow job for this annotation

GitHub Actions / Style/spelling (ubuntu-latest, feat_os_unix)

WARNING: `cspell`: Unknown word 'lseek' (file:'src/uu/tail/src/tail.rs', line:485)
// beyond the end of the file and produce no output.
file.seek(SeekFrom::Start(*count - 1))
.or_else(|_| file.seek(SeekFrom::End(0)))
Expand Down
14 changes: 14 additions & 0 deletions tests/by-util/test_tail.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1377,6 +1377,20 @@ fn test_positive_bytes_file_offset_past_seek_limit() {
.no_stdout();
}

// `tail -c -N` on a seekable file larger than the block size seeks back `N`
// bytes from the end. An `N` that does not fit in an `i64` must not abort the
// process; it should print the whole file, like any other too-large count.
#[test]
fn test_negative_bytes_file_count_past_seek_limit() {
let (at, mut ucmd) = at_and_ucmd!();
// Larger than tail's `sane_blksize` (~4 KiB) so the seek path is taken.
let data = "a".repeat(8192);
at.write("big", &data);
ucmd.args(&["-c", "-9223372036854775808", "big"])
.succeeds()
.stdout_is(&data);
}

#[test]
fn test_num_with_undocumented_sign_bytes() {
// tail: '-' is not documented (8.32 man pages)
Expand Down
Loading