Skip to content

stat: restore GNU's separators in the default output record - #101

Open
kelp wants to merge 4 commits into
mainfrom
fix/stat-dev-minor-truncation
Open

stat: restore GNU's separators in the default output record#101
kelp wants to merge 4 commits into
mainfrom
fix/stat-dev-minor-truncation

Conversation

@kelp

@kelp kelp commented Jul 29, 2026

Copy link
Copy Markdown
Owner

GNU separates Size: from Blocks: with a tab, and follows the
Blocks, IO Block and Inode fields with literal spaces. We folded
each separator into the preceding column's pad width. That renders
identically for everyday values, which is why it went unnoticed, but the
separator disappears entirely once a field outgrows its pad:

  • a 10 GB file printed 10737418240Blocks: — no separator at all
  • an inode of 11+ digits left one space before Links:, not two

The issue reported only the missing tab. The other two divergences are
the same defect and turned up while checking whether the rest of that
line was genuinely equivalent to GNU or merely coincidentally aligned
for the values tested. The fix is three format literals in
printDefaultFormat_sizeLine and printDefaultFormat_deviceLine,
matched against coreutils 9.5 default_format.

Still divergent, out of scope here: GNU's %N shell-quoting of file
names (File: 'my file'). We never quote.

Tests

Red was verified on macOS and Linux before the fix, failing for the
right reason: expected '\x09' after the size field, found '\x42'
the B of Blocks.

  • Three ungated Size-line unit tests: one pins GNU's literal bytes for
    a 1383-byte file, two drive an 11-digit size and an 18-digit block
    count past the pads.
  • Two Linux-gated Device-line tests with an 11-digit inode, covering
    both the plain and the Device type: branch.
  • Three integration cases diffing the whole record against
    /usr/bin/stat for a regular file, a directory and /dev/null.

That whole-record diff replaces the grep '^Device:' scoping #92's
tests had to settle for; those narrower checks stay as defense in depth.

Check macOS Linux
zig build test 2668/2724 pass, 56 skip, 0 fail 2672/2724 pass, 52 skip, 0 fail
Integration 48/48 utilities 48/48 utilities

Byte-level check on Linux — diff of cat -A output against
/usr/bin/stat is identical for /etc/passwd, /dev/null and /tmp.

Also drops a stale embedded briefing from the tdd-bugfix workflow,
which made the green phase fall back to another bug's notes when none
was passed in.

Closes #98

🤖 Generated with Claude Code

https://claude.ai/code/session_01VapqXpB1wtGk5Cb9KeMSok

kelp and others added 4 commits July 29, 2026 19:50
Eight failing tests for issue #98. GNU's default_format puts a
literal tab after the Size field and literal spaces after Blocks,
IO Block, and Inode. Our format strings fold those separators into
the pad widths, so they render identically for everyday values and
vanish entirely once a field overflows its pad.

Three unit tests cover the Size line and are ungated: one pins
GNU's exact bytes for a 1383-byte file, two drive synthetic sizes
and block counts past the pad. Two more cover the Device line with
an 11-digit inode, Linux-gated like their issue-92 neighbours
because the dev_t layout differs. The integration helper diffs the
whole record against GNU for a regular file, a directory, and
/dev/null, replacing the grep '^Device:' scoping those earlier
tests needed.

Red on both platforms: expected '\x09' after the size field, found
'\x42' -- the B of Blocks.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VapqXpB1wtGk5Cb9KeMSok
The green phase fell back to a briefing embedded from an earlier dd
run when none was passed in, so a bug fix could be implemented
against another bug's notes. Re-scout instead.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VapqXpB1wtGk5Cb9KeMSok
GNU's default_format puts a literal tab after the Size field and
literal spaces after Blocks, IO Block, and Inode. We folded each
separator into the preceding pad width, which renders identically
for everyday values and drops the separator entirely once a field
overflows its pad: a 10GB file printed "10737418240Blocks:", and
an 11-digit inode left one space before Links where GNU leaves two.

Three format literals now match coreutils 9.5 byte for byte. On
Linux, stat FILE is identical to /usr/bin/stat FILE for a regular
file, a directory, and /dev/null, which lets the whole record be
diffed against GNU instead of just the Device line.

Fixes #98

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VapqXpB1wtGk5Cb9KeMSok
The EXAMPLES record rendered the separator as spaces, which is what
the code used to emit. Also notes the fix in the changelog.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VapqXpB1wtGk5Cb9KeMSok
@kelp
kelp force-pushed the fix/stat-dev-minor-truncation branch from 6e52029 to 8ef8485 Compare July 30, 2026 02:51
@kelp kelp changed the title stat: GNU parity for device numbers and the default record stat: restore GNU's separators in the default output record Jul 30, 2026
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.

stat: default output omits GNU's tab after the Size field

1 participant