diff --git a/src/uu/od/src/input_offset.rs b/src/uu/od/src/input_offset.rs index 6e5e36b5579..bb91b5439c5 100644 --- a/src/uu/od/src/input_offset.rs +++ b/src/uu/od/src/input_offset.rs @@ -35,6 +35,11 @@ impl InputOffset { } } + /// Whether no input has been consumed yet, including skipped bytes. + pub fn is_at_start(&self) -> bool { + self.byte_pos == 0 + } + /// Increase `byte_pos` and `label` if a label is used. pub fn increase_position(&mut self, n: u64) { self.byte_pos += n; diff --git a/src/uu/od/src/od.rs b/src/uu/od/src/od.rs index 2b5d1c2b7f0..94c8fb1eca5 100644 --- a/src/uu/od/src/od.rs +++ b/src/uu/od/src/od.rs @@ -546,7 +546,10 @@ where let length = memory_decoder.length(); if length == 0 { - if !input_decoder.has_error() { + // After a read error, print the final offset only when + // some input was already consumed; otherwise stay silent + // (e.g. `od directory` or `od missing`). + if !input_decoder.has_error() || !input_offset.is_at_start() { input_offset.write_final_offset(writer)?; } break; diff --git a/tests/by-util/test_od.rs b/tests/by-util/test_od.rs index 5ebd456ce3d..e4182e0ca52 100644 --- a/tests/by-util/test_od.rs +++ b/tests/by-util/test_od.rs @@ -1748,3 +1748,56 @@ fn test_od_zero_byte_type_size() { .succeeds() .stdout_is("0000000 64636261\n0000004\n"); } + +#[test] +fn test_final_offset_printed_when_an_input_fails() { + let (at, mut ucmd) = at_and_ucmd!(); + at.write("a.txt", "a\n"); + at.mkdir("d"); + + // The message for an unreadable input differs between platforms (WASI says + // "Bad file descriptor"), so only the prefix is checked here. + ucmd.args(&["a.txt", "d"]) + .fails_with_code(1) + .stdout_is("0000000 005141\n0000002\n") + .stderr_contains("od: d:"); +} + +#[test] +#[cfg(not(windows))] +#[cfg_attr(wasi_runner, ignore)] +fn test_no_final_offset_for_directory() { + let (at, mut ucmd) = at_and_ucmd!(); + at.mkdir("d"); + + // A directory can be opened but not read; with no input consumed, there is + // no final offset to report. + ucmd.arg("d") + .fails_with_code(1) + .no_stdout() + .stderr_contains("od: d:"); +} + +#[test] +#[cfg(not(windows))] +#[cfg_attr(wasi_runner, ignore)] +fn test_no_final_offset_for_directory_with_limited_read() { + let (at, mut ucmd) = at_and_ucmd!(); + at.mkdir("d"); + + // Same as above, with -N limiting the read to one byte. + ucmd.args(&["-N1", "d"]) + .fails_with_code(1) + .no_stdout() + .stderr_contains("od: d:"); +} + +#[test] +fn test_no_final_offset_when_no_input_can_be_opened() { + let (_at, mut ucmd) = at_and_ucmd!(); + + ucmd.arg("missing") + .fails_with_code(1) + .no_stdout() + .stderr_contains("od: missing:"); +}