Skip to content

tail: accept obsolete -cf and allow only one trailing f - #15518

Open
emirmrkaljevic wants to merge 2 commits into
uutils:mainfrom
emirmrkaljevic:tail-obsolete-c
Open

emirmrkaljevic wants to merge 2 commits into
uutils:mainfrom
emirmrkaljevic:tail-obsolete-c

Conversation

@emirmrkaljevic

Copy link
Copy Markdown
Contributor

Summary

  • tail -cf FILE failed with invalid number of bytes: 'f', while GNU prints the last 10 bytes and follows. clap reads -cf as -c f, and the count error was returned with ? before the obsolete-syntax parser got a chance. Now an invalid count falls through to the obsolete parser when the value was attached to the option (-cf); a separate argument (tail -c f) is still an invalid count, as in GNU.
  • The obsolete parser accepted any number of trailing fs (-5ff, -cff, +cff). GNU accepts only one, so -5ff is now option used in invalid context -- 5 and +cff is a file name, like GNU.
$ printf 'abcdefghijklmnop\n' > t.txt
$ ./target/debug/tail -cf t.txt
hijklmnop
(keeps following)
$ ./target/debug/tail -5ff t.txt
tail: option used in invalid context -- 5

Fixes #15517

Test plan

  • New test_tail_obsolete_bytes_follow covers -cf FILE (follows, prints the last 10 bytes), -c f (still an error) and -5ff; it fails without the change
  • New parser unit cases for -cff, +ff and -5ff
  • cargo test --no-default-features --features tail test_tail::: 168 passed, 12 ignored; cargo test -p uu_tail --lib: 38 passed
  • cargo fmt --check, cargo clippy -p uu_tail --all-targets -- -D warnings
  • Output and exit codes match GNU coreutils 9.12 for -cf, -cf FILE, -c FILE, -cl, -cff, -cfv, -c3f, -nf, -vcf, --bytes=f, -5ff, +cff, -3cf, +5f, -5f, +f

Found with behave.

Comment thread src/uu/tail/src/args.rs Outdated
pub fn parse_args(args: impl uucore::Args) -> UResult<Settings> {
let args_vec: Vec<OsString> = args.collect();
let clap_args = uu_app().try_get_matches_from(args_vec.clone());
let mut invalid_value = false;

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.

invalid_value is set for any settings error, not only an invalid count, maybe a clearer name?

Comment thread src/uu/tail/src/args.rs Outdated
}
// `tail -cf` is obsolete syntax even though clap reads it as `-c f`,
// but in `tail -c f` the separate argument is always the count.
if invalid_value && possible_obsolete_args.len() <= 2 {

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.

len() <= 2 is a bit magic, could you please make it explicit that it means "no value attached to the option"?

Comment thread src/uu/tail/src/args.rs
if invalid_value && possible_obsolete_args.len() <= 2 {
return clap_result;
}
match parse_obsolete(possible_obsolete_args, args_vec.get(2))? {

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.

could you please run the GNU tail tests too? tail.pl has lots of obsolete syntax cases
setup: git clone https://github.com/coreutils/coreutils.git ../gnu
run: bash util/build-gnu.sh && bash util/run-gnu-test.sh tests/tail/tail.pl

.make_assertion()
.with_all_output()
.stdout_only("hijklmnop\n");

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.

could you please also add the stdin case (-cf without a file)? it is in the issue

@emirmrkaljevic

Copy link
Copy Markdown
Contributor Author

Thanks for the review! I renamed invalid_value to settings_failed, since it's set for any settings error. The length check is now a named no_value_attached condition with a comment explaining that a bare -c has no value attached. I also added a stdin test for -cf without a file. I ran the GNU tail tests (util/build-gnu.sh + util/run-gnu-test.sh tests/tail/tail.pl) and tail.pl passes (PASS: 1, FAIL: 0).

@codspeed

codspeed Bot commented Oct 20, 2026

Copy link
Copy Markdown

Merging this PR will degrade performance by 13.34%

⚠️ 121 benchmarks spent significant time in system calls

System calls cannot be consistently instrumented, so they are not included in the measure, which understates the real cost. Please switch to the Walltime instrument to accurately measure system calls.

Measurement and system calls

ℹ️ 1 benchmark has no flame graph

The flamegraph could not be generated because too much time was spent in runtime internals, making the visualization not useful for performance analysis.

⚠️ Different runtime environments detected

Some benchmarks with significant performance changes were compared across different runtime environments,
which may affect the accuracy of the results.

Open the report in CodSpeed to investigate

⚡ 1 improved benchmark
❌ 1 regressed benchmark
✅ 399 untouched benchmarks
⏩ 70 skipped benchmarks1

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Mode Benchmark BASE HEAD Efficiency
❌ Simulation complex_relative_date 277.7 µs 386.2 µs -28.1%
⚡ ⚠️ Simulation du_deep_tree[(100, 3)] 1.9 ms 1.9 ms +4.45%

Tip

Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.


Comparing emirmrkaljevic:tail-obsolete-c (41cb88b) with main (5fda18e)2

Open in CodSpeed

Footnotes

  1. 70 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩

  2. No successful run was found on main (b77b664) during the generation of this report, so 5fda18e was used instead as the comparison base. There might be some changes unrelated to this pull request in this report. ↩

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

tail: obsolete syntax rejects -cf and accepts more than one f

2 participants