Skip to content

wc: derive column width from the size of stdin - #14187

Open
Socialpranker wants to merge 2 commits into
uutils:mainfrom
Socialpranker:wc-stdin-width
Open

wc: derive column width from the size of stdin#14187
Socialpranker wants to merge 2 commits into
uutils:mainfrom
Socialpranker:wc-stdin-width

Conversation

@Socialpranker

Copy link
Copy Markdown

wc pads its counts to a width derived from the total size of its inputs, but
stdin was always treated as a stream of unknown length, even when it is a
regular file whose size fstat reports up front. The same 839-byte file
therefore printed differently depending on how it was handed to wc:

$ wc < 839-byte-file
     18      20     839
$ wc 839-byte-file
 18  20 839 839-byte-file

GNU wc prints 18 20 839 in both cases; it only falls back to the minimum
width when the size genuinely is not known in advance.

This PR takes the size of stdin from its metadata when stdin is a regular file
and feeds it into the existing width computation, so:

  • wc < file matches wc file;
  • a - argument contributes the size of stdin to the total, the way a named
    file does (wc - other-file < file widens to fit size(file) + size(other));
  • pipes, terminals and character devices are unchanged — cat file | wc and
    wc < /dev/null still use the minimum width of 7;
  • a regular file of size zero still yields width 1, matching how a zero-byte
    named file behaves.

The digit-width calculation that was inline in compute_number_width is pulled
out into a small width_of helper so both the stdin-only and the mixed-input
paths share it.

Testing: new test_stdin_size_dictates_width in tests/by-util/test_wc.rs
covers the three cases (regular file on stdin, the same content piped in, and
- mixed with a named file). It fails on the current main and passes with
this change. The full wc suite is green (58 passed), cargo fmt --check and
cargo clippy -p uu_wc --all-targets are clean.

Behaviour was established by observing GNU wc's output on a Debian system —
no GNU source was consulted.

When stdin is a regular file its size is known before reading, but the
counts were still padded to the fixed minimum width of 7:

    $ wc < 839-byte-file
         18      20     839
    $ wc 839-byte-file
     18  20 839 839-byte-file

The width is now derived from the size of stdin whenever fstat reports a
regular file, which also makes "-" contribute its size to the total for
mixed inputs. Streams whose length is not known in advance (pipes,
terminals, character devices) keep the minimum width.

    $ wc < 839-byte-file
     18  20 839
    $ cat 839-byte-file | wc
         18      20     839
@Socialpranker

Copy link
Copy Markdown
Author

Side-by-side check against GNU wc (Debian, coreutils 9.x) with the patched binary built from this branch — all eight cases that differed before now match byte for byte:

OK   wc < p839            → [ 18  20 839]
OK   wc - < p839          → [ 18  20 839 -]
OK   wc - p839 < p839     → [  18   20  839 -|  18   20  839 p839|  36   40 1678 total]
OK   wc < /dev/null       → [      0       0       0]
OK   wc < big             → [ 156 1129 8011]
OK   wc -l < p839         → [18]
OK   wc < /proc/cpuinfo   → [45 310 1715]
OK   cat p839 | wc        → [     18      20     839]

/proc/cpuinfo is the interesting one: it is a regular file of size 0, so the width stays 1 even though 1715 bytes are read — same as GNU.

@github-actions

github-actions Bot commented Aug 28, 2026

Copy link
Copy Markdown

GNU testsuite comparison:

Skip an intermittent issue tests/date/date-locale-hour (fails in this run but passes in the 'main' branch)
Skip an intermittent issue tests/tail/retry (fails in this run but passes in the 'main' branch)
Skipping an intermittent issue tests/tail/follow-name (passes in this run but fails in the 'main' branch)
Congrats! The gnu test tests/tail/flush-initial is no longer failing!

@sylvestre

Copy link
Copy Markdown
Contributor

A bunch of jobs are failing

`at_and_ucmd!` is imported under `#[cfg(unix)]` in this file, so the test
failed to compile on the windows and wasm targets.
@sylvestre

Copy link
Copy Markdown
Contributor

Is there a bug somewhere about this issue?

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.

2 participants