Merge nucleic/sleek-thistle-egret-fyej into dev

This commit is contained in:
2026-07-18 05:19:31 -07:00
commit b6be87b72d
677 changed files with 102939 additions and 0 deletions
+277
View File
@@ -0,0 +1,277 @@
[package]
name = "brush-coreutils-builtins"
description = "Optional coreutils builtins for brush-shell (powered by uutils/coreutils)"
version = "0.1.0"
categories.workspace = true
edition.workspace = true
keywords.workspace = true
license.workspace = true
readme.workspace = true
repository.workspace = true
rust-version.workspace = true
[lib]
bench = false
[features]
# No utilities are enabled by default, so plain `cargo build --workspace`
# pays only for the scaffolding. Consumers opt into the full set by enabling
# `coreutils.all` (or individual `coreutils.<name>` features).
default = []
'coreutils.all' = [
'coreutils.arch',
'coreutils.base32',
'coreutils.base64',
'coreutils.basename',
'coreutils.basenc',
'coreutils.cat',
'coreutils.cksum',
'coreutils.b2sum',
'coreutils.md5sum',
'coreutils.sha1sum',
'coreutils.sha224sum',
'coreutils.sha256sum',
'coreutils.sha384sum',
'coreutils.sha512sum',
'coreutils.comm',
'coreutils.cp',
'coreutils.csplit',
'coreutils.cut',
'coreutils.date',
'coreutils.dd',
'coreutils.df',
'coreutils.dir',
'coreutils.dircolors',
'coreutils.dirname',
'coreutils.du',
'coreutils.echo',
'coreutils.env',
'coreutils.expand',
'coreutils.expr',
'coreutils.factor',
'coreutils.false',
'coreutils.fmt',
'coreutils.fold',
'coreutils.head',
'coreutils.hostname',
'coreutils.join',
'coreutils.link',
'coreutils.ln',
'coreutils.ls',
'coreutils.mkdir',
'coreutils.mktemp',
'coreutils.more',
'coreutils.mv',
'coreutils.nl',
'coreutils.nproc',
'coreutils.numfmt',
'coreutils.od',
'coreutils.paste',
'coreutils.pr',
'coreutils.printenv',
'coreutils.printf',
'coreutils.ptx',
'coreutils.pwd',
'coreutils.readlink',
'coreutils.realpath',
'coreutils.rm',
'coreutils.rmdir',
'coreutils.seq',
'coreutils.shred',
'coreutils.shuf',
'coreutils.sleep',
'coreutils.sort',
'coreutils.split',
'coreutils.sum',
'coreutils.sync',
'coreutils.tac',
'coreutils.tail',
'coreutils.tee',
'coreutils.test',
'coreutils.touch',
'coreutils.tr',
'coreutils.true',
'coreutils.truncate',
'coreutils.tsort',
'coreutils.uname',
'coreutils.unexpand',
'coreutils.uniq',
'coreutils.unlink',
'coreutils.vdir',
'coreutils.wc',
'coreutils.whoami',
'coreutils.yes',
]
'coreutils.arch' = ["dep:uu_arch"]
'coreutils.base32' = ["dep:uu_base32"]
'coreutils.base64' = ["dep:uu_base64"]
'coreutils.basename' = ["dep:uu_basename"]
'coreutils.basenc' = ["dep:uu_basenc"]
'coreutils.cat' = ["dep:uu_cat"]
'coreutils.cksum' = ["dep:uu_cksum"]
'coreutils.b2sum' = ["dep:uu_b2sum"]
'coreutils.md5sum' = ["dep:uu_md5sum"]
'coreutils.sha1sum' = ["dep:uu_sha1sum"]
'coreutils.sha224sum' = ["dep:uu_sha224sum"]
'coreutils.sha256sum' = ["dep:uu_sha256sum"]
'coreutils.sha384sum' = ["dep:uu_sha384sum"]
'coreutils.sha512sum' = ["dep:uu_sha512sum"]
'coreutils.comm' = ["dep:uu_comm"]
'coreutils.cp' = ["dep:uu_cp"]
'coreutils.csplit' = ["dep:uu_csplit"]
'coreutils.cut' = ["dep:uu_cut"]
'coreutils.date' = ["dep:uu_date"]
'coreutils.dd' = ["dep:uu_dd"]
'coreutils.df' = ["dep:uu_df"]
'coreutils.dir' = ["dep:uu_dir"]
'coreutils.dircolors' = ["dep:uu_dircolors"]
'coreutils.dirname' = ["dep:uu_dirname"]
'coreutils.du' = ["dep:uu_du"]
'coreutils.echo' = ["dep:uu_echo"]
'coreutils.env' = ["dep:uu_env"]
'coreutils.expand' = ["dep:uu_expand"]
'coreutils.expr' = ["dep:uu_expr"]
'coreutils.factor' = ["dep:uu_factor"]
'coreutils.false' = ["dep:uu_false"]
'coreutils.fmt' = ["dep:uu_fmt"]
'coreutils.fold' = ["dep:uu_fold"]
'coreutils.head' = ["dep:uu_head"]
'coreutils.hostname' = ["dep:uu_hostname"]
'coreutils.join' = ["dep:uu_join"]
'coreutils.link' = ["dep:uu_link"]
'coreutils.ln' = ["dep:uu_ln"]
'coreutils.ls' = ["dep:uu_ls"]
'coreutils.mkdir' = ["dep:uu_mkdir"]
'coreutils.mktemp' = ["dep:uu_mktemp"]
'coreutils.more' = ["dep:uu_more"]
'coreutils.mv' = ["dep:uu_mv"]
'coreutils.nl' = ["dep:uu_nl"]
'coreutils.nproc' = ["dep:uu_nproc"]
'coreutils.numfmt' = ["dep:uu_numfmt"]
'coreutils.od' = ["dep:uu_od"]
'coreutils.paste' = ["dep:uu_paste"]
'coreutils.pr' = ["dep:uu_pr"]
'coreutils.printenv' = ["dep:uu_printenv"]
'coreutils.printf' = ["dep:uu_printf"]
'coreutils.ptx' = ["dep:uu_ptx"]
'coreutils.pwd' = ["dep:uu_pwd"]
'coreutils.readlink' = ["dep:uu_readlink"]
'coreutils.realpath' = ["dep:uu_realpath"]
'coreutils.rm' = ["dep:uu_rm"]
'coreutils.rmdir' = ["dep:uu_rmdir"]
'coreutils.seq' = ["dep:uu_seq"]
'coreutils.shred' = ["dep:uu_shred"]
'coreutils.shuf' = ["dep:uu_shuf"]
'coreutils.sleep' = ["dep:uu_sleep"]
'coreutils.sort' = ["dep:uu_sort"]
'coreutils.split' = ["dep:uu_split"]
'coreutils.sum' = ["dep:uu_sum"]
'coreutils.sync' = ["dep:uu_sync"]
'coreutils.tac' = ["dep:uu_tac"]
'coreutils.tail' = ["dep:uu_tail"]
'coreutils.tee' = ["dep:uu_tee"]
'coreutils.test' = ["dep:uu_test"]
'coreutils.touch' = ["dep:uu_touch"]
'coreutils.tr' = ["dep:uu_tr"]
'coreutils.true' = ["dep:uu_true"]
'coreutils.truncate' = ["dep:uu_truncate"]
'coreutils.tsort' = ["dep:uu_tsort"]
'coreutils.uname' = ["dep:uu_uname"]
'coreutils.unexpand' = ["dep:uu_unexpand"]
'coreutils.uniq' = ["dep:uu_uniq"]
'coreutils.unlink' = ["dep:uu_unlink"]
'coreutils.vdir' = ["dep:uu_vdir"]
'coreutils.wc' = ["dep:uu_wc"]
'coreutils.whoami' = ["dep:uu_whoami"]
'coreutils.yes' = ["dep:uu_yes"]
[lints]
workspace = true
[dependencies]
uucore = { version = "0.8.0", default-features = false }
uu_arch = { version = "0.8.0", optional = true }
uu_base32 = { version = "0.8.0", optional = true }
uu_base64 = { version = "0.8.0", optional = true }
uu_basename = { version = "0.8.0", optional = true }
uu_basenc = { version = "0.8.0", optional = true }
uu_cat = { version = "0.8.0", optional = true }
uu_cksum = { version = "0.8.0", optional = true }
uu_b2sum = { version = "0.8.0", optional = true }
uu_md5sum = { version = "0.8.0", optional = true }
uu_sha1sum = { version = "0.8.0", optional = true }
uu_sha224sum = { version = "0.8.0", optional = true }
uu_sha256sum = { version = "0.8.0", optional = true }
uu_sha384sum = { version = "0.8.0", optional = true }
uu_sha512sum = { version = "0.8.0", optional = true }
uu_comm = { version = "0.8.0", optional = true }
uu_cp = { version = "0.8.0", optional = true }
uu_csplit = { version = "0.8.0", optional = true }
uu_cut = { version = "0.8.0", optional = true }
uu_date = { version = "0.8.0", optional = true }
uu_dd = { version = "0.8.0", optional = true }
uu_df = { version = "0.8.0", optional = true }
uu_dir = { version = "0.8.0", optional = true }
uu_dircolors = { version = "0.8.0", optional = true }
uu_dirname = { version = "0.8.0", optional = true }
uu_du = { version = "0.8.0", optional = true }
uu_echo = { version = "0.8.0", optional = true }
uu_env = { version = "0.8.0", optional = true }
uu_expand = { version = "0.8.0", optional = true }
uu_expr = { version = "0.8.0", optional = true }
uu_factor = { version = "0.8.0", optional = true }
uu_false = { version = "0.8.0", optional = true }
uu_fmt = { version = "0.8.0", optional = true }
uu_fold = { version = "0.8.0", optional = true }
uu_head = { version = "0.8.0", optional = true }
uu_hostname = { version = "0.8.0", optional = true }
uu_join = { version = "0.8.0", optional = true }
uu_link = { version = "0.8.0", optional = true }
uu_ln = { version = "0.8.0", optional = true }
uu_ls = { version = "0.8.0", optional = true }
uu_mkdir = { version = "0.8.0", optional = true }
uu_mktemp = { version = "0.8.0", optional = true }
uu_more = { version = "0.8.0", optional = true }
uu_mv = { version = "0.8.0", optional = true }
uu_nl = { version = "0.8.0", optional = true }
uu_nproc = { version = "0.8.0", optional = true }
uu_numfmt = { version = "0.8.0", optional = true }
uu_od = { version = "0.8.0", optional = true }
uu_paste = { version = "0.8.0", optional = true }
uu_pr = { version = "0.8.0", optional = true }
uu_printenv = { version = "0.8.0", optional = true }
uu_printf = { version = "0.8.0", optional = true }
uu_ptx = { version = "0.8.0", optional = true }
uu_pwd = { version = "0.8.0", optional = true }
uu_readlink = { version = "0.8.0", optional = true }
uu_realpath = { version = "0.8.0", optional = true }
uu_rm = { version = "0.8.0", optional = true }
uu_rmdir = { version = "0.8.0", optional = true }
uu_seq = { version = "0.8.0", optional = true }
uu_shred = { version = "0.8.0", optional = true }
uu_shuf = { version = "0.8.0", optional = true }
uu_sleep = { version = "0.8.0", optional = true }
uu_sort = { version = "0.8.0", optional = true }
uu_split = { version = "0.8.0", optional = true }
uu_sum = { version = "0.8.0", optional = true }
uu_sync = { version = "0.8.0", optional = true }
uu_tac = { version = "0.8.0", optional = true }
uu_tail = { version = "0.8.0", optional = true }
uu_tee = { version = "0.8.0", optional = true }
uu_test = { version = "0.8.0", optional = true }
uu_touch = { version = "0.8.0", optional = true }
uu_tr = { version = "0.8.0", optional = true }
uu_true = { version = "0.8.0", optional = true }
uu_truncate = { version = "0.8.0", optional = true }
uu_tsort = { version = "0.8.0", optional = true }
uu_uname = { version = "0.8.0", optional = true }
uu_unexpand = { version = "0.8.0", optional = true }
uu_uniq = { version = "0.8.0", optional = true }
uu_unlink = { version = "0.8.0", optional = true }
uu_vdir = { version = "0.8.0", optional = true }
uu_wc = { version = "0.8.0", optional = true }
uu_whoami = { version = "0.8.0", optional = true }
uu_yes = { version = "0.8.0", optional = true }
+1
View File
@@ -0,0 +1 @@
../LICENSE
+204
View File
@@ -0,0 +1,204 @@
//! Optional bundled coreutils for brush, powered by uutils/coreutils.
//!
//! Each utility is feature-gated (e.g., `coreutils.cat`, `coreutils.ls`) and
//! can be individually enabled or disabled. The `coreutils.all` feature enables
//! all cross-platform utilities.
//!
//! The crate exposes [`bundled_commands`], a registry of utility name to
//! `uumain`-style function pointer. The binary consumer (typically
//! brush-shell) installs this registry at startup; when its bundled-dispatch
//! protocol fires, the registered function is called directly. The consumer
//! is also responsible for registering any per-name builtin shims that
//! re-enter the binary as an external process, so shell redirections, pipes,
//! and process-group state are honored by uutils transparently.
//!
//! ## Relationship to `uucore::bin!`
//!
//! Each adapter generated by [`register!`] is a hand-rolled port of the body
//! of upstream's `uucore::bin!($util)` macro (SIGPIPE setup, Fluent
//! localization, `uumain` call, stdout flush). We can't invoke the macro
//! directly because it expands to a `pub fn main()` that reads argv via
//! `uucore::args_os()` and terminates the process with `std::process::exit`.
//! Our call shape is different: adapters receive argv as a parameter (the
//! `--invoke-bundled` dispatcher owns argv slicing) and must *return* an exit
//! code so the dispatcher can run a single `exit` site.
//!
//! Intentional divergences from `bin!`, all of which are benign in our usage:
//!
//! * **No `exit`:** adapters return `i32` instead of calling
//! `std::process::exit`.
//! * **Caller-supplied argv:** `args` is passed in rather than pulled from
//! `uucore::args_os()`.
//! * **Simpler `LocalizationError` formatting:** we render with `Display`
//! rather than matching on `ParseResource` for snippet-aware output.
use std::collections::HashMap;
use std::ffi::OsString;
/// First half of the `uucore::bin!` body: install SIGPIPE handling and
/// initialize the thread-local Fluent localizer. Without this, `translate!`
/// calls inside the utility fall back to emitting raw Fluent keys (e.g.,
/// `ls-error-cannot-access-no-such-file`) instead of rendered messages.
///
/// `util_crate_name` is the upstream crate name (e.g., `"uu_dir"`), matching
/// what `bin!` passes via `stringify!($util)`. We route it through
/// [`uucore::get_canonical_util_name`] to apply upstream's alias map (`dir` →
/// `ls`, `vdir` → `ls`, `[` → `test`); utilities implemented as thin wrappers
/// over another crate load their translation bundle under the wrapped name,
/// so skipping this step silently breaks localization for the aliases.
///
/// Matches `uucore::bin!` behavior on localization-init failure: exit 99.
/// (This is uutils' contract, not brush's — the fact that brush-core uses 99
/// for `ExecutionExitCode::Unimplemented` is coincidental.)
/// `setup_localization` is idempotent per thread, so repeated invocations
/// (e.g., successive bundled-dispatch calls in a long-lived host, if that
/// ever happens) are cheap.
#[allow(
dead_code,
reason = "only called by adapters generated by the `register!` macro; with no coreutils.* features enabled, no adapter exists"
)]
fn prepare_uutil_runtime(util_crate_name: &str) {
uucore::panic::preserve_inherited_sigpipe();
uucore::panic::mute_sigpipe_panic();
let localization_name = uucore::get_canonical_util_name(util_crate_name);
if let Err(err) = uucore::locale::setup_localization(localization_name) {
eprintln!("brush: could not initialize localization for '{localization_name}': {err}");
std::process::exit(99);
}
}
/// Second half of the `uucore::bin!` body: flush stdout after `uumain`
/// returns. `bin!` does this before calling `std::process::exit` to work
/// around <https://github.com/rust-lang/rust/issues/23818>. We do it here so
/// callers that don't immediately `exit` (e.g., a test harness) still observe
/// complete output.
#[allow(
dead_code,
reason = "only called by adapters generated by the `register!` macro; with no coreutils.* features enabled, no adapter exists"
)]
fn finalize_uutil_runtime() {
use std::io::Write;
if let Err(e) = std::io::stdout().flush() {
eprintln!("brush: error flushing stdout: {e}");
}
}
/// Adapts a uutils `uumain` (`impl Iterator<Item = OsString> -> i32`) to a
/// plain `fn(Vec<OsString>) -> i32` and inserts it into the registry under
/// the given name when its feature is enabled. The generated adapter mirrors
/// the body of `uucore::bin!`; see the module-level docs for why we port it
/// by hand rather than invoke the macro.
macro_rules! register {
($map:expr, $feature:literal, $name:literal, $util_crate:ident) => {
#[cfg(feature = $feature)]
{
fn adapter(args: Vec<OsString>) -> i32 {
$crate::prepare_uutil_runtime(stringify!($util_crate));
let code = $util_crate::uumain(args.into_iter());
$crate::finalize_uutil_runtime();
code
}
$map.insert($name.to_string(), adapter as fn(Vec<OsString>) -> i32);
}
};
}
/// Returns the set of bundled coreutils commands enabled by feature flags.
///
/// Keyed by utility name (e.g., `"cat"`); values are `uumain`-shaped entry
/// points that consume a full argv (with `argv[0]` as the command name) and
/// return an exit code.
#[allow(clippy::too_many_lines)]
#[must_use]
pub fn bundled_commands() -> HashMap<String, fn(Vec<OsString>) -> i32> {
#[allow(
unused_mut,
reason = "every register! is cfg'd on a coreutils.* feature; with none enabled, nothing mutates the map"
)]
let mut m = HashMap::<String, fn(Vec<OsString>) -> i32>::new();
register!(m, "coreutils.arch", "arch", uu_arch);
register!(m, "coreutils.base32", "base32", uu_base32);
register!(m, "coreutils.base64", "base64", uu_base64);
register!(m, "coreutils.basename", "basename", uu_basename);
register!(m, "coreutils.basenc", "basenc", uu_basenc);
register!(m, "coreutils.cat", "cat", uu_cat);
register!(m, "coreutils.cksum", "cksum", uu_cksum);
register!(m, "coreutils.b2sum", "b2sum", uu_b2sum);
register!(m, "coreutils.md5sum", "md5sum", uu_md5sum);
register!(m, "coreutils.sha1sum", "sha1sum", uu_sha1sum);
register!(m, "coreutils.sha224sum", "sha224sum", uu_sha224sum);
register!(m, "coreutils.sha256sum", "sha256sum", uu_sha256sum);
register!(m, "coreutils.sha384sum", "sha384sum", uu_sha384sum);
register!(m, "coreutils.sha512sum", "sha512sum", uu_sha512sum);
register!(m, "coreutils.comm", "comm", uu_comm);
register!(m, "coreutils.cp", "cp", uu_cp);
register!(m, "coreutils.csplit", "csplit", uu_csplit);
register!(m, "coreutils.cut", "cut", uu_cut);
register!(m, "coreutils.date", "date", uu_date);
register!(m, "coreutils.dd", "dd", uu_dd);
register!(m, "coreutils.df", "df", uu_df);
register!(m, "coreutils.dir", "dir", uu_dir);
register!(m, "coreutils.dircolors", "dircolors", uu_dircolors);
register!(m, "coreutils.dirname", "dirname", uu_dirname);
register!(m, "coreutils.du", "du", uu_du);
register!(m, "coreutils.echo", "echo", uu_echo);
register!(m, "coreutils.env", "env", uu_env);
register!(m, "coreutils.expand", "expand", uu_expand);
register!(m, "coreutils.expr", "expr", uu_expr);
register!(m, "coreutils.factor", "factor", uu_factor);
register!(m, "coreutils.false", "false", uu_false);
register!(m, "coreutils.fmt", "fmt", uu_fmt);
register!(m, "coreutils.fold", "fold", uu_fold);
register!(m, "coreutils.head", "head", uu_head);
register!(m, "coreutils.hostname", "hostname", uu_hostname);
register!(m, "coreutils.join", "join", uu_join);
register!(m, "coreutils.link", "link", uu_link);
register!(m, "coreutils.ln", "ln", uu_ln);
register!(m, "coreutils.ls", "ls", uu_ls);
register!(m, "coreutils.mkdir", "mkdir", uu_mkdir);
register!(m, "coreutils.mktemp", "mktemp", uu_mktemp);
register!(m, "coreutils.more", "more", uu_more);
register!(m, "coreutils.mv", "mv", uu_mv);
register!(m, "coreutils.nl", "nl", uu_nl);
register!(m, "coreutils.nproc", "nproc", uu_nproc);
register!(m, "coreutils.numfmt", "numfmt", uu_numfmt);
register!(m, "coreutils.od", "od", uu_od);
register!(m, "coreutils.paste", "paste", uu_paste);
register!(m, "coreutils.pr", "pr", uu_pr);
register!(m, "coreutils.printenv", "printenv", uu_printenv);
register!(m, "coreutils.printf", "printf", uu_printf);
register!(m, "coreutils.ptx", "ptx", uu_ptx);
register!(m, "coreutils.pwd", "pwd", uu_pwd);
register!(m, "coreutils.readlink", "readlink", uu_readlink);
register!(m, "coreutils.realpath", "realpath", uu_realpath);
register!(m, "coreutils.rm", "rm", uu_rm);
register!(m, "coreutils.rmdir", "rmdir", uu_rmdir);
register!(m, "coreutils.seq", "seq", uu_seq);
register!(m, "coreutils.shred", "shred", uu_shred);
register!(m, "coreutils.shuf", "shuf", uu_shuf);
register!(m, "coreutils.sleep", "sleep", uu_sleep);
register!(m, "coreutils.sort", "sort", uu_sort);
register!(m, "coreutils.split", "split", uu_split);
register!(m, "coreutils.sum", "sum", uu_sum);
register!(m, "coreutils.sync", "sync", uu_sync);
register!(m, "coreutils.tac", "tac", uu_tac);
register!(m, "coreutils.tail", "tail", uu_tail);
register!(m, "coreutils.tee", "tee", uu_tee);
register!(m, "coreutils.test", "test", uu_test);
register!(m, "coreutils.touch", "touch", uu_touch);
register!(m, "coreutils.tr", "tr", uu_tr);
register!(m, "coreutils.true", "true", uu_true);
register!(m, "coreutils.truncate", "truncate", uu_truncate);
register!(m, "coreutils.tsort", "tsort", uu_tsort);
register!(m, "coreutils.uname", "uname", uu_uname);
register!(m, "coreutils.unexpand", "unexpand", uu_unexpand);
register!(m, "coreutils.uniq", "uniq", uu_uniq);
register!(m, "coreutils.unlink", "unlink", uu_unlink);
register!(m, "coreutils.vdir", "vdir", uu_vdir);
register!(m, "coreutils.wc", "wc", uu_wc);
register!(m, "coreutils.whoami", "whoami", uu_whoami);
register!(m, "coreutils.yes", "yes", uu_yes);
m
}