Files
oh-my-pi/crates/pi-builtins
can1357 edc0caeb0f feat(builtins): sweep of GNU/BSD compat fixes + str performance opts
Addresses a broad audit of built-in shell utilities against their real counterparts: timeout gains signal delivery, -s/-k/--preserve-status/--foreground/-v, and GNU exit codes; diff defaults to normal format and gains -w/-b/-B/-i/-c/-x/-L/-s/--strip-trailing-cr and proper -r gating; find fixes -newerXY timestamp comparison direction, anchors -regex to whole paths, and gains BSD -perm +mode, -type lists, -size T/P suffixes, -E/-x/-s flags; date gains BSD -r epoch, -v adjustments, -j -f strptime, and non-greedy -I; tail/head accept obsolete -N/+N at any position with any file count; rg resolves case flags by last occurrence and gains --path-separator and clean -0 output; stat prints integer epochs for %X/%Y/%Z and gains BSD -s/-x/-t; cksum is registered as a builtin; truncate implements -o/--io-blocks and b/= size suffixes; sleep/timeout accept infinity; yes/errno/kill accept hyphen-prefixed operands; nohup -- cmd no longer runs --; which gains BSD -s.
2026-08-20 02:33:10 +02:00
..
2026-08-08 10:19:25 +02:00

pi-builtins

Every builtin the embedded shell installs. Two layers:

  1. Shell builtins — cd, echo, test, printf, read, export, trap, wait, … A locally-patched fork of brush-builtins (MIT), tracking upstream except where noted in LICENSE.
  2. In-process command-line utilities — cat, grep/rg, sed, ls, find, sort, jq, fd, diff, xargs, ps, top, kill, the moreutils set, and ~50 more. One module per command, in src/<command>.rs.

The second layer exists so the shell never has to fork: a long-lived embedded shell resolves these names itself, on every platform, whether or not the host has the real binaries. They were previously ~50 separate vendored crates driven through a thread-local I/O shim; consolidating them here removed the shim.

The Host contract

src/host.rs is the whole story for a utility builtin. A utility is a clap argument model plus a synchronous body:

pub(crate) trait Utility: clap::Parser + Send + Sync + 'static {
    const NAME: &'static str;
    const USAGE_ERROR: u8 = 1;
    fn run(self, host: &mut Host) -> i32;
}

Host is the shell as the utility sees it, threaded explicitly rather than through process globals or thread locals: the command's own stdio (host.stdout, host.stderr, host.stdin), the shell's working directory (host.resolve(path) — mandatory for every path argument, since the host process's current directory is unrelated), the exported environment (host.var, host.env), cancellation (host.is_cancelled), a child-process launcher that inherits all of the above (host.child_env()), and the accumulated exit status (host.fail, host.exit_code).

host::util::<U, SE>() wraps a Utility into a registration that handles, once for all of them: process-substitution arguments (diff <(a) <(b)), --help/--version on stdout with status 0, usage errors on stderr, execution on a blocking thread, the shell's cancellation token, and panic containment.

Utilities that are genuinely async — sleep, timeout, ps, top, pgrep, kill, nohup — implement brush_core::builtins::Command directly instead.

Registration

Three entry points, so the embedding shell decides what to install:

Function Contents
default_builtins(set) The POSIX/bash builtins, per BuiltinSet.
utility_builtins() The coreutils-style commands, which shadow system binaries.
process_builtins() ps, top, pgrep, pkill, pidwait, sleep, timeout, nohup.

Every command is behind a cargo feature (builtin.<name>, util.<name>), so a single one can be built and tested in isolation:

$ cargo test -p pi-builtins --no-default-features --features base,util.sed

Licensing

MIT throughout, but the ported utilities carry upstream notices — uutils coreutils, uutils findutils, uutils sed, jaq, and rust-utf8. See LICENSE, which reproduces each in full and lists which commands it covers.