Skip to content

head, tail: reject multiplier suffixes that GNU rejects - #14228

Open
kennywillbe wants to merge 5 commits into
uutils:mainfrom
kennywillbe:tail-head-gnu-suffixes
Open

head, tail: reject multiplier suffixes that GNU rejects#14228
kennywillbe wants to merge 5 commits into
uutils:mainfrom
kennywillbe:tail-head-gnu-suffixes

Conversation

@kennywillbe

Copy link
Copy Markdown

Fixes #13136.

head and tail parse their count argument through parse_signed_num_max, which uses the generic size parser with no allow list. That parser accepts a lowercase form of every multiplier, so 2g, 2t, 2p, 2e, 2z, 2y, 2r and 2q were silently accepted:

$ tail -c2z a        # accepted, treated as 2 ZiB
hello

GNU reports invalid number of bytes: '2z' for all of these. It accepts a lowercase suffix only for k and m.

This restricts the shared parser to the set both utilities accept: k m K M G T P E Z Y R Q, each also valid followed by B, iB or D, plus a bare b (which has no B/iB/D form). head and tail take the same set, so one allow list covers both.

I derived the set by running GNU coreutils 9.11 and comparing, not from its source. After the change I diffed every letter of the alphabet in all four forms (X, XB, XiB, XD) for -c on both utilities, 208 combinations, and every -n combination with a +/- prefix: no remaining differences.

Tests added to test_head.rs and test_tail.rs cover both the newly rejected suffixes and the accepted ones, for -c and -n, so a regression in either direction fails.

One thing left out deliberately: with a leading zero the error names the trimmed string, so tail -c0g says invalid number of bytes: 'g' where GNU says '0g'. That is pre-existing (tail -c0fb misreports the same way today) and unrelated to the suffix set, so I will report it separately.

kennywillbe and others added 3 commits August 29, 2026 14:31
The count argument of head and tail is parsed with the generic size
parser, which accepts a lowercase form of every multiplier. GNU only
accepts a lowercase 'k' and 'm', so 2g, 2t, 2p, 2e, 2z, 2y, 2r and 2q
were silently accepted instead of being reported as invalid.

Restrict the shared parser to the suffixes these utilities accept:
k, m, K, M, G, T, P, E, Z, Y, R and Q, each also valid followed by B,
iB or D, plus a bare b.
/dev/null does not exist on Windows and is not visible inside the WASI
sandbox, so the accepted-suffix test failed on those targets. Feed the
input through stdin, as the matching tail tests already do.
@github-actions

github-actions Bot commented Aug 29, 2026

Copy link
Copy Markdown

GNU testsuite comparison:

Skipping an intermittent issue tests/cut/bounded-memory (passes in this run but fails in the 'main' branch)
Skipping an intermittent issue tests/date/date-locale-hour (passes in this run but fails in the 'main' branch)

kennywillbe and others added 2 commits August 29, 2026 19:23
The test only needs to show that these suffixes parse, and asserting the
copied bytes makes it fail on 32-bit targets, where uucore's splice path
truncates the byte count.
@codspeed-hq

codspeed-hq Bot commented Aug 29, 2026

Copy link
Copy Markdown

Merging this PR will improve performance by 3.04%

⚠️ 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
✅ 360 untouched benchmarks
⏩ 50 skipped benchmarks1

Performance Changes

Mode Benchmark BASE HEAD Efficiency
Simulation du_deep_tree[(100, 3)] 2.1 ms 2 ms +3.04%

Tip

Curious why performance improved? Comment @codspeedbot explain why performance improved on this PR, or directly use the CodSpeed MCP with your agent.


Comparing kennywillbe:tail-head-gnu-suffixes (dbe4ef1) with main (c4c2fd0)2

Open in CodSpeed

Footnotes

  1. 50 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 (594dc6a) during the generation of this report, so c4c2fd0 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

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.

gnucompability(tail): -c accepts some large units that gnu rejects

1 participant