3 Commits

Author SHA1 Message Date
LyAhn a4d08e9998 fix(base64-rs): print usage instead of blocking on console stdin
Running base64 with no FILE fell into the stdin branch and sat in a read
loop. That is exactly what GNU does, but on Windows it presents as a dead
terminal: the console echoes keystrokes so it looks like a prompt, output
buffers into 32K chunks so nothing comes back as you type, and there is no
hint that it is waiting for Ctrl+Z.

Guard the implicit case only. With no FILE operand and stdin attached to a
console, print the help text to stderr and exit 1. Pipes and '<' redirects
are untouched, so pipelines and the parity harness see no change -- the
harness always hands the child a stdin pipe via input=. An explicit '-'
still reads the console for anyone who does want to type input by hand.

Uses std::io::IsTerminal, so the crate stays dependency-free.
2026-07-27 22:33:13 +01:00
LyAhn 665f846dc9 test(base64-rs): add GNU differential parity harness
Runs both binaries over 154 cases and compares stdout, stderr and exit
code: encoding at every wrap width, decode round-trips, canonical-tail
rules, padding placement, concatenated streams, every prefix of a known
encoding, usage errors and file operands.

Locates a genuine GNU base64 rather than trusting /usr/bin/base64, since
distributions increasingly ship uutils there and it disagrees with GNU on
most malformed input — running against it fails 51 of the 154 cases, which
doubles as a negative control for the harness itself.

The three known differences are asserted as expected rather than ignored.
2026-07-27 21:53:26 +01:00
LyAhn 5e6b4428bb feat(base64-rs): add GNU-compatible base64 command for Windows
Windows has no base64 command; certutil is file-only, adds PEM markers
and cannot be piped. This is a real base64.exe matching GNU coreutils:
same flags (-d, -i, -w), stdin/stdout streaming, exit codes and error
messages, so existing muscle memory and scripts carry over.

Verified against coreutils 9.7 with a 153-case differential test. The
subtle behaviours are reproduced: bytes are emitted incrementally so a
stream that goes bad still writes its valid prefix, unpadded tails are
accepted only when canonically encoded, concatenated streams decode as
one, and -w0 suppresses the trailing newline.

Deliberate deviation: '\r' is skipped when decoding alongside '\n', as
CRLF base64 files are routine on Windows and GNU rejects them.

Zero dependencies, so the build works offline.
2026-07-27 21:36:01 +01:00