Skip to content

cmp: honor the byte limit in quiet size checks - #296

Open
SichenLiang wants to merge 1 commit into
uutils:mainfrom
SichenLiang:cmp-quiet-byte-limit
Open

SichenLiang wants to merge 1 commit into
uutils:mainfrom
SichenLiang:cmp-quiet-byte-limit

Conversation

@SichenLiang

@SichenLiang SichenLiang commented Oct 3, 2026 •

Copy link
Copy Markdown

In quiet mode, when both operands are regular files and neither is -, subtract the skipped bytes from each size, stopping at zero, and then apply the -n limit. If the resulting lengths differ, report a difference without reading; otherwise compare the requested data.

Do not use the shortcut for -, because metadata for that path would describe a file literally named -, not standard input.

This fixes byte-limited comparisons and gives GNU-compatible status in the tested directory and /dev/zero cases. One difference remains: cmp -s a dir now returns 2, but quiet mode still omits GNU's Is a directory diagnostic.

New tests cover byte limits, skipped bytes, devices, directories, and -i 0.

Fixes #295

Comment thread src/cmp.rs Outdated
if params.quiet
&& a_meta.is_file()
&& b_meta.is_file()
&& params.skip_a.unwrap_or(0) == 0

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

why not subtract the skips from the sizes instead of disabling the shortcut?

Comment thread src/cmp.rs Outdated
#[cfg(unix)]
{
let stdin = io::stdin().lock();
// SAFETY: stdin's fd 0 must stay open and valid while locked.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

do we really need unsafe here?
io::stdin().as_fd().try_clone_to_owned() would be safe, and failing on an fd limit of 4 seems acceptable to me

Comment thread tests/integration.rs Outdated

#[test]
#[cfg(unix)]
fn cmp_zero_bytes_stdin_directory() -> Result<(), Box<dyn std::error::Error>> {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

this is mostly the same as cmp_zero_bytes_stdin_directory_fd_limit and cmp_zero_bytes_directory_operand.
could you please factor the cmd + assert part into a helper? 250 lines of tests is a lot for this

Comment thread tests/integration.rs Outdated
let mut cmd = std::process::Command::new("sh");
cmd.env("LC_ALL", "C")
.current_dir(tmp_dir.path())
.args(["-c", "ulimit -n 4 || exit; exec \"$@\"", "sh"])

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

the ulimit -n 4 test seems overkill, could we drop it?

Comment thread tests/integration.rs Outdated

#[test]
#[cfg(not(windows))]
fn cmp_quiet_size_shortcut_variants() -> Result<(), Box<dyn std::error::Error>> {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

almost the same as cmp_fast_paths, could this be merged into it?

In quiet mode, when both operands are regular files and neither is `-`,
subtract the skipped bytes from each size, stopping at zero, and then
apply the `-n` limit. If the resulting lengths differ, report a
difference without reading; otherwise compare the requested data.

Do not use the shortcut for `-`, because metadata for that path would
describe a file literally named `-`, not standard input.

This fixes byte-limited comparisons and gives GNU-compatible status in
the tested directory and /dev/zero cases. One difference remains:
`cmp -s a dir` now returns 2, but quiet mode still omits GNU's
"Is a directory" diagnostic.

New tests cover byte limits, skipped bytes, devices, directories, and
`-i 0`.
@SichenLiang
SichenLiang force-pushed the cmp-quiet-byte-limit branch from 3b39012 to 906e801 Compare October 4, 2026 02:03
@SichenLiang

Copy link
Copy Markdown
Author

Thanks for the review. I’ve addressed all five comments.

  • The size shortcut now subtracts the skips and applies the -n limit before comparing the remaining lengths. The added -i 1 sparse-file case in cmp_fast_paths exercises this path.
  • I removed the unsafe block and now use io::stdin().as_fd().try_clone_to_owned(). A failure is reported as an error with status 2.
  • The command setup and assertions now live in an assert_cmp helper, and the cases have been reduced to two table-driven tests.
  • The ulimit -n 4 test is gone.
  • The separate size-shortcut test has been folded into cmp_fast_paths, which now runs the four relevant argument sets.

I also excluded - operands from the metadata shortcut. Otherwise, fs::metadata("-") describes a file literally named -, not stdin. This fixes the case where ./- contains zz and cmp -s - abc < abc incorrectly exits 1 on main instead of 0. If cloning stdin’s descriptor fails, the error is now reported after both operands are opened, preserving the existing error order.
One pre-existing parsing difference is now visible in quiet mode: when -i is present, positional skip operands are ignored. As a result, cmp -s -i 1:0 xabc abc 0 1 exits 0 while GNU exits 1; main already returns 0 for the same case without -s. I left the option parsing unchanged and can address it separately.

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.

cmp: quiet mode compares full file sizes before applying -n

2 participants