From 4a7626f27a19b2bac9774e6b10ea59c5355c61c5 Mon Sep 17 00:00:00 2001 From: n4n5 Date: Fri, 25 Apr 2025 18:09:45 +0200 Subject: [PATCH 01/35] add examples --- Cargo.toml | 330 +++++++++++++++++++++---------------------- src/bin/coreutils.rs | 154 ++++++++++++++++---- 2 files changed, 288 insertions(+), 196 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index cde946b68f4..b7ca5a03308 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ edition.workspace = true all-features = true [features] -default = ["feat_common_core"] +default = ["feat_common_core", "zip"] ## OS feature shortcodes macos = ["feat_os_macos"] unix = ["feat_os_unix"] @@ -47,138 +47,138 @@ feat_acl = ["cp/feat_acl"] # * The selinux(-sys) crate requires `libselinux` headers and shared library to be accessible in the C toolchain at compile time. # * Running a uutils compiled with `feat_selinux` requires an SELinux enabled Kernel at run time. feat_selinux = [ - "cp/selinux", - "id/selinux", - "ls/selinux", - "mkdir/selinux", - "mkfifo/selinux", - "mknod/selinux", - "stat/selinux", - "selinux", - "feat_require_selinux", + "cp/selinux", + "id/selinux", + "ls/selinux", + "mkdir/selinux", + "mkfifo/selinux", + "mknod/selinux", + "stat/selinux", + "selinux", + "feat_require_selinux", ] ## ## feature sets ## (common/core and Tier1) feature sets # "feat_common_core" == baseline core set of utilities which can be built/run on most targets feat_common_core = [ - "base32", - "base64", - "basename", - "basenc", - "cat", - "cksum", - "comm", - "cp", - "csplit", - "cut", - "date", - "df", - "dir", - "dircolors", - "dirname", - "dd", - "du", - "echo", - "env", - "expand", - "expr", - "factor", - "false", - "fmt", - "fold", - "hashsum", - "head", - "join", - "link", - "ln", - "ls", - "mkdir", - "mktemp", - "more", - "mv", - "nl", - "numfmt", - "od", - "paste", - "pr", - "printenv", - "printf", - "ptx", - "pwd", - "readlink", - "realpath", - "rm", - "rmdir", - "seq", - "shred", - "shuf", - "sleep", - "sort", - "split", - "sum", - "tac", - "tail", - "tee", - "test", - "tr", - "true", - "truncate", - "tsort", - "touch", - "unexpand", - "uniq", - "unlink", - "vdir", - "wc", - "yes", + "base32", + "base64", + "basename", + "basenc", + "cat", + "cksum", + "comm", + "cp", + "csplit", + "cut", + "date", + "df", + "dir", + "dircolors", + "dirname", + "dd", + "du", + "echo", + "env", + "expand", + "expr", + "factor", + "false", + "fmt", + "fold", + "hashsum", + "head", + "join", + "link", + "ln", + "ls", + "mkdir", + "mktemp", + "more", + "mv", + "nl", + "numfmt", + "od", + "paste", + "pr", + "printenv", + "printf", + "ptx", + "pwd", + "readlink", + "realpath", + "rm", + "rmdir", + "seq", + "shred", + "shuf", + "sleep", + "sort", + "split", + "sum", + "tac", + "tail", + "tee", + "test", + "tr", + "true", + "truncate", + "tsort", + "touch", + "unexpand", + "uniq", + "unlink", + "vdir", + "wc", + "yes", ] # "feat_Tier1" == expanded set of utilities which can be built/run on the usual rust "Tier 1" target platforms (ref: ) feat_Tier1 = [ - "feat_common_core", - # - "arch", - "hostname", - "nproc", - "sync", - "touch", - "uname", - "whoami", + "feat_common_core", + # + "arch", + "hostname", + "nproc", + "sync", + "touch", + "uname", + "whoami", ] ## (primary platforms) feature sets # "feat_os_macos" == set of utilities which can be built/run on the MacOS platform feat_os_macos = [ - "feat_os_unix", ## == a modern/usual *nix platform - # - "feat_require_unix_hostid", + "feat_os_unix", ## == a modern/usual *nix platform + # + "feat_require_unix_hostid", ] # "feat_os_unix" == set of utilities which can be built/run on modern/usual *nix platforms. # Also used for targets binding to the "musl" library (ref: ) feat_os_unix = [ - "feat_Tier1", - # - "feat_require_crate_cpp", - "feat_require_unix", - "feat_require_unix_utmpx", - "feat_require_unix_hostid", + "feat_Tier1", + # + "feat_require_crate_cpp", + "feat_require_unix", + "feat_require_unix_utmpx", + "feat_require_unix_hostid", ] # "feat_os_windows" == set of utilities which can be built/run on modern/usual windows platforms feat_os_windows = [ - "feat_Tier1", ## == "feat_os_windows_legacy" + "hostname" + "feat_Tier1", ## == "feat_os_windows_legacy" + "hostname" ] ## (secondary platforms) feature sets # "feat_os_unix_gnueabihf" == set of utilities which can be built/run on the "arm-unknown-linux-gnueabihf" target (ARMv6 Linux [hardfloat]) feat_os_unix_gnueabihf = [ - "feat_Tier1", - # - "feat_require_unix", - "feat_require_unix_hostid", - "feat_require_unix_utmpx", + "feat_Tier1", + # + "feat_require_unix", + "feat_require_unix_hostid", + "feat_require_unix_utmpx", ] feat_os_unix_android = [ - "feat_Tier1", - # - "feat_require_unix", + "feat_Tier1", + # + "feat_require_unix", ] ## feature sets with requirements (restricting cross-platform availability) # @@ -188,24 +188,24 @@ feat_os_unix_android = [ feat_require_crate_cpp = ["stdbuf"] # "feat_require_unix" == set of utilities requiring support which is only available on unix platforms (as of 2020-04-23) feat_require_unix = [ - "chgrp", - "chmod", - "chown", - "chroot", - "groups", - "id", - "install", - "kill", - "logname", - "mkfifo", - "mknod", - "nice", - "nohup", - "pathchk", - "stat", - "stty", - "timeout", - "tty", + "chgrp", + "chmod", + "chown", + "chroot", + "groups", + "id", + "install", + "kill", + "logname", + "mkfifo", + "mknod", + "nice", + "nohup", + "pathchk", + "stat", + "stty", + "timeout", + "tty", ] # "feat_require_unix_utmpx" == set of utilities requiring unix utmp/utmpx support # * ref: @@ -217,43 +217,43 @@ feat_require_selinux = ["chcon", "runcon"] ## (alternate/newer/smaller platforms) feature sets # "feat_os_unix_fuchsia" == set of utilities which can be built/run on the "Fuchsia" OS (refs: ; ) feat_os_unix_fuchsia = [ - "feat_common_core", - # - "feat_require_crate_cpp", - # - "chgrp", - "chmod", - "chown", - "du", - "groups", - "hostid", - "install", - "logname", - "mkfifo", - "mknod", - "nice", - "pathchk", - "tty", - "uname", - "unlink", + "feat_common_core", + # + "feat_require_crate_cpp", + # + "chgrp", + "chmod", + "chown", + "du", + "groups", + "hostid", + "install", + "logname", + "mkfifo", + "mknod", + "nice", + "pathchk", + "tty", + "uname", + "unlink", ] # "feat_os_unix_redox" == set of utilities which can be built/run on "Redox OS" (refs: ; ) feat_os_unix_redox = [ - "feat_common_core", - # - "chmod", - "stat", - "uname", + "feat_common_core", + # + "chmod", + "stat", + "uname", ] # "feat_os_windows_legacy" == slightly restricted set of utilities which can be built/run on early windows platforms (eg, "WinXP") feat_os_windows_legacy = [ - "feat_common_core", - # - "arch", - "nproc", - "sync", - "touch", - "whoami", + "feat_common_core", + # + "arch", + "nproc", + "sync", + "touch", + "whoami", ] ## # * bypass/override ~ translate 'test' feature name to avoid dependency collision with rust core 'test' crate (o/w surfaces as compiler errors during testing) @@ -277,9 +277,9 @@ bstr = "1.9.1" bytecount = "0.6.8" byteorder = "1.5.0" chrono = { version = "0.4.38", default-features = false, features = [ - "std", - "alloc", - "clock", + "std", + "alloc", + "clock", ] } chrono-tz = "0.10.0" clap = { version = "4.5", features = ["wrap_help", "cargo"] } @@ -306,7 +306,7 @@ itertools = "0.14.0" libc = "0.2.172" linux-raw-sys = "0.9" lscolors = { version = "0.20.0", default-features = false, features = [ - "gnu_legacy", + "gnu_legacy", ] } memchr = "2.7.2" memmap2 = "0.9.4" @@ -509,11 +509,11 @@ time = { workspace = true, features = ["local-offset"] } unindent = "0.2.3" uutests = { workspace = true } uucore = { workspace = true, features = [ - "mode", - "entries", - "process", - "signals", - "utmpx", + "mode", + "entries", + "process", + "signals", + "utmpx", ] } walkdir = { workspace = true } hex-literal = "1.0.0" diff --git a/src/bin/coreutils.rs b/src/bin/coreutils.rs index b29e7ea2337..c9064189772 100644 --- a/src/bin/coreutils.rs +++ b/src/bin/coreutils.rs @@ -10,19 +10,34 @@ use clap_complete::Shell; use std::cmp; use std::ffi::OsStr; use std::ffi::OsString; +use std::fs::File; +use std::io::Read; +use std::io::Seek; use std::io::{self, Write}; use std::path::{Path, PathBuf}; use std::process; use uucore::display::Quotable; +use zip::ZipArchive; const VERSION: &str = env!("CARGO_PKG_VERSION"); +const COMPLETION: &str = "completion"; +const MANPAGE: &str = "manpage"; + include!(concat!(env!("OUT_DIR"), "/uutils_map.rs")); fn usage(utils: &UtilityMap, name: &str) { println!("{name} {VERSION} (multi-call binary)\n"); println!("Usage: {name} [function [arguments...]]"); - println!(" {name} --list\n"); + println!(" {name} --list"); + println!(); + println!("Functions:"); + println!(" {COMPLETION}",); + println!(" {}", get_completion_args(utils).render_usage()); + println!(" '{MANPAGE}'",); + println!(" {}", get_manpage_args(utils).render_usage()); + println!(" '' [arguments...]"); + println!(); println!("Options:"); println!(" --list lists all defined functions, one per row\n"); println!("Currently defined functions:\n"); @@ -95,8 +110,14 @@ fn main() { }; match util { - "completion" => gen_completions(args, &utils), - "manpage" => gen_manpage(args, &utils), + COMPLETION => { + gen_completions(args, &utils); + process::exit(0); + } + MANPAGE => { + gen_manpage(args, &utils); + process::exit(0); + } "--list" => { let mut utils: Vec<_> = utils.keys().collect(); utils.sort(); @@ -148,18 +169,11 @@ fn main() { } } -/// Prints completions for the utility in the first parameter for the shell in the second parameter to stdout -/// # Panics -/// Panics if the utility map is empty -fn gen_completions( - args: impl Iterator, - util_map: &UtilityMap, -) -> ! { +fn get_completion_args(util_map: &UtilityMap) -> clap::Command { let all_utilities: Vec<_> = std::iter::once("coreutils") .chain(util_map.keys().copied()) .collect(); - - let matches = Command::new("completion") + Command::new(COMPLETION) .about("Prints completions to stdout") .arg( Arg::new("utility") @@ -171,7 +185,17 @@ fn gen_completions( .value_parser(clap::builder::EnumValueParser::::new()) .required(true), ) - .get_matches_from(std::iter::once(OsString::from("completion")).chain(args)); +} + +/// Prints completions for the utility in the first parameter for the shell in the second parameter to stdout +/// # Panics +/// Panics if the utility map is empty +fn gen_completions( + args: impl Iterator, + util_map: &UtilityMap, +) { + let matches = get_completion_args(util_map) + .get_matches_from(std::iter::once(OsString::from(COMPLETION)).chain(args)); let utility = matches.get_one::("utility").unwrap(); let shell = *matches.get_one::("shell").unwrap(); @@ -185,42 +209,49 @@ fn gen_completions( clap_complete::generate(shell, &mut command, bin_name, &mut io::stdout()); io::stdout().flush().unwrap(); - process::exit(0); } -/// Generate the manpage for the utility in the first parameter -/// # Panics -/// Panics if the utility map is empty -fn gen_manpage( - args: impl Iterator, - util_map: &UtilityMap, -) -> ! { +fn get_manpage_args(util_map: &UtilityMap) -> clap::Command { let all_utilities: Vec<_> = std::iter::once("coreutils") .chain(util_map.keys().copied()) .collect(); + Command::new(MANPAGE).about("Prints manpage to stdout").arg( + Arg::new("utility") + .value_parser(clap::builder::PossibleValuesParser::new(all_utilities)) + .required(true), + ) +} - let matches = Command::new("manpage") - .about("Prints manpage to stdout") - .arg( - Arg::new("utility") - .value_parser(clap::builder::PossibleValuesParser::new(all_utilities)) - .required(true), - ) - .get_matches_from(std::iter::once(OsString::from("manpage")).chain(args)); +/// Generate the manpage for the utility in the first parameter +/// # Panics +/// Panics if the utility map is empty +fn gen_manpage(args: impl Iterator, util_map: &UtilityMap) { + let tldr_zip = File::open("docs/tldr.zip") + .ok() + .and_then(|f| ZipArchive::new(f).ok()); + + if tldr_zip.is_none() { + // Could not open tldr.zip + } + let matches = get_manpage_args(util_map) + .get_matches_from(std::iter::once(OsString::from(MANPAGE)).chain(args)); let utility = matches.get_one::("utility").unwrap(); let command = if utility == "coreutils" { gen_coreutils_app(util_map) } else { - util_map.get(utility).unwrap().1() + let mut cmd = util_map.get(utility).unwrap().1(); + if let Ok(examples) = get_zip_examples(utility) { + cmd = cmd.after_help(examples); + } + cmd }; let man = clap_mangen::Man::new(command); man.render(&mut io::stdout()) .expect("Man page generation failed"); io::stdout().flush().unwrap(); - process::exit(0); } /// # Panics @@ -239,3 +270,64 @@ fn gen_coreutils_app(util_map: &UtilityMap) -> Command { } command } + +/// # Errors +/// Returns an error if the tldr.zip file cannot be opened or read +fn get_zip_examples(name: &str) -> io::Result { + fn get_zip_content(archive: &mut ZipArchive, name: &str) -> Option { + let mut s = String::new(); + archive.by_name(name).ok()?.read_to_string(&mut s).unwrap(); + Some(s) + } + + let mut w = io::BufWriter::new(Vec::new()); + let file = File::open("docs/tldr.zip")?; + let mut tldr_zip = match ZipArchive::new(file) { + Ok(zip) => zip, + Err(e) => { + return Err(io::Error::new( + io::ErrorKind::NotFound, + format!("Error reading tldr.zip: {}", e), + )); + } + }; + + let content = if let Some(f) = + get_zip_content(&mut tldr_zip, &format!("pages/common/{}.md", name)) + { + f + } else if let Some(f) = get_zip_content(&mut tldr_zip, &format!("pages/linux/{}.md", name)) { + f + } else { + return Err(io::Error::new( + io::ErrorKind::NotFound, + "Could not find tldr examples", + )); + }; + + writeln!(w, "Examples")?; + writeln!(w)?; + for line in content.lines().skip_while(|l| !l.starts_with('-')) { + if let Some(l) = line.strip_prefix("- ") { + writeln!(w, "{l}")?; + } else if line.starts_with('`') { + writeln!(w, "{}", line.trim_matches('`'))?; + } else if line.is_empty() { + writeln!(w)?; + } else { + // println!("Not sure what to do with this line:"); + // println!("{line}"); + } + } + writeln!(w)?; + writeln!( + w, + "> The examples are provided by the [tldr-pages project](https://tldr.sh) under the [CC BY 4.0 License](https://github.com/tldr-pages/tldr/blob/main/LICENSE.md)." + )?; + writeln!(w, "\n\n\n")?; + writeln!( + w, + "> Please note that, as uutils is a work in progress, some examples might fail." + )?; + Ok(String::from_utf8(w.into_inner().unwrap()).unwrap()) +} From 78d0b99a0ea201af538b843b85f4c8ac03e5457c Mon Sep 17 00:00:00 2001 From: n4n5 Date: Fri, 25 Apr 2025 18:11:58 +0200 Subject: [PATCH 02/35] fix: space --- Cargo.toml | 328 ++++++++++++++++++++++++++--------------------------- 1 file changed, 164 insertions(+), 164 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b7ca5a03308..d8582c989fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,138 +47,138 @@ feat_acl = ["cp/feat_acl"] # * The selinux(-sys) crate requires `libselinux` headers and shared library to be accessible in the C toolchain at compile time. # * Running a uutils compiled with `feat_selinux` requires an SELinux enabled Kernel at run time. feat_selinux = [ - "cp/selinux", - "id/selinux", - "ls/selinux", - "mkdir/selinux", - "mkfifo/selinux", - "mknod/selinux", - "stat/selinux", - "selinux", - "feat_require_selinux", + "cp/selinux", + "id/selinux", + "ls/selinux", + "mkdir/selinux", + "mkfifo/selinux", + "mknod/selinux", + "stat/selinux", + "selinux", + "feat_require_selinux", ] ## ## feature sets ## (common/core and Tier1) feature sets # "feat_common_core" == baseline core set of utilities which can be built/run on most targets feat_common_core = [ - "base32", - "base64", - "basename", - "basenc", - "cat", - "cksum", - "comm", - "cp", - "csplit", - "cut", - "date", - "df", - "dir", - "dircolors", - "dirname", - "dd", - "du", - "echo", - "env", - "expand", - "expr", - "factor", - "false", - "fmt", - "fold", - "hashsum", - "head", - "join", - "link", - "ln", - "ls", - "mkdir", - "mktemp", - "more", - "mv", - "nl", - "numfmt", - "od", - "paste", - "pr", - "printenv", - "printf", - "ptx", - "pwd", - "readlink", - "realpath", - "rm", - "rmdir", - "seq", - "shred", - "shuf", - "sleep", - "sort", - "split", - "sum", - "tac", - "tail", - "tee", - "test", - "tr", - "true", - "truncate", - "tsort", - "touch", - "unexpand", - "uniq", - "unlink", - "vdir", - "wc", - "yes", + "base32", + "base64", + "basename", + "basenc", + "cat", + "cksum", + "comm", + "cp", + "csplit", + "cut", + "date", + "df", + "dir", + "dircolors", + "dirname", + "dd", + "du", + "echo", + "env", + "expand", + "expr", + "factor", + "false", + "fmt", + "fold", + "hashsum", + "head", + "join", + "link", + "ln", + "ls", + "mkdir", + "mktemp", + "more", + "mv", + "nl", + "numfmt", + "od", + "paste", + "pr", + "printenv", + "printf", + "ptx", + "pwd", + "readlink", + "realpath", + "rm", + "rmdir", + "seq", + "shred", + "shuf", + "sleep", + "sort", + "split", + "sum", + "tac", + "tail", + "tee", + "test", + "tr", + "true", + "truncate", + "tsort", + "touch", + "unexpand", + "uniq", + "unlink", + "vdir", + "wc", + "yes", ] # "feat_Tier1" == expanded set of utilities which can be built/run on the usual rust "Tier 1" target platforms (ref: ) feat_Tier1 = [ - "feat_common_core", - # - "arch", - "hostname", - "nproc", - "sync", - "touch", - "uname", - "whoami", + "feat_common_core", + # + "arch", + "hostname", + "nproc", + "sync", + "touch", + "uname", + "whoami", ] ## (primary platforms) feature sets # "feat_os_macos" == set of utilities which can be built/run on the MacOS platform feat_os_macos = [ - "feat_os_unix", ## == a modern/usual *nix platform - # - "feat_require_unix_hostid", + "feat_os_unix", ## == a modern/usual *nix platform + # + "feat_require_unix_hostid", ] # "feat_os_unix" == set of utilities which can be built/run on modern/usual *nix platforms. # Also used for targets binding to the "musl" library (ref: ) feat_os_unix = [ - "feat_Tier1", - # - "feat_require_crate_cpp", - "feat_require_unix", - "feat_require_unix_utmpx", - "feat_require_unix_hostid", + "feat_Tier1", + # + "feat_require_crate_cpp", + "feat_require_unix", + "feat_require_unix_utmpx", + "feat_require_unix_hostid", ] # "feat_os_windows" == set of utilities which can be built/run on modern/usual windows platforms feat_os_windows = [ - "feat_Tier1", ## == "feat_os_windows_legacy" + "hostname" + "feat_Tier1", ## == "feat_os_windows_legacy" + "hostname" ] ## (secondary platforms) feature sets # "feat_os_unix_gnueabihf" == set of utilities which can be built/run on the "arm-unknown-linux-gnueabihf" target (ARMv6 Linux [hardfloat]) feat_os_unix_gnueabihf = [ - "feat_Tier1", - # - "feat_require_unix", - "feat_require_unix_hostid", - "feat_require_unix_utmpx", + "feat_Tier1", + # + "feat_require_unix", + "feat_require_unix_hostid", + "feat_require_unix_utmpx", ] feat_os_unix_android = [ - "feat_Tier1", - # - "feat_require_unix", + "feat_Tier1", + # + "feat_require_unix", ] ## feature sets with requirements (restricting cross-platform availability) # @@ -188,24 +188,24 @@ feat_os_unix_android = [ feat_require_crate_cpp = ["stdbuf"] # "feat_require_unix" == set of utilities requiring support which is only available on unix platforms (as of 2020-04-23) feat_require_unix = [ - "chgrp", - "chmod", - "chown", - "chroot", - "groups", - "id", - "install", - "kill", - "logname", - "mkfifo", - "mknod", - "nice", - "nohup", - "pathchk", - "stat", - "stty", - "timeout", - "tty", + "chgrp", + "chmod", + "chown", + "chroot", + "groups", + "id", + "install", + "kill", + "logname", + "mkfifo", + "mknod", + "nice", + "nohup", + "pathchk", + "stat", + "stty", + "timeout", + "tty", ] # "feat_require_unix_utmpx" == set of utilities requiring unix utmp/utmpx support # * ref: @@ -217,43 +217,43 @@ feat_require_selinux = ["chcon", "runcon"] ## (alternate/newer/smaller platforms) feature sets # "feat_os_unix_fuchsia" == set of utilities which can be built/run on the "Fuchsia" OS (refs: ; ) feat_os_unix_fuchsia = [ - "feat_common_core", - # - "feat_require_crate_cpp", - # - "chgrp", - "chmod", - "chown", - "du", - "groups", - "hostid", - "install", - "logname", - "mkfifo", - "mknod", - "nice", - "pathchk", - "tty", - "uname", - "unlink", + "feat_common_core", + # + "feat_require_crate_cpp", + # + "chgrp", + "chmod", + "chown", + "du", + "groups", + "hostid", + "install", + "logname", + "mkfifo", + "mknod", + "nice", + "pathchk", + "tty", + "uname", + "unlink", ] # "feat_os_unix_redox" == set of utilities which can be built/run on "Redox OS" (refs: ; ) feat_os_unix_redox = [ - "feat_common_core", - # - "chmod", - "stat", - "uname", + "feat_common_core", + # + "chmod", + "stat", + "uname", ] # "feat_os_windows_legacy" == slightly restricted set of utilities which can be built/run on early windows platforms (eg, "WinXP") feat_os_windows_legacy = [ - "feat_common_core", - # - "arch", - "nproc", - "sync", - "touch", - "whoami", + "feat_common_core", + # + "arch", + "nproc", + "sync", + "touch", + "whoami", ] ## # * bypass/override ~ translate 'test' feature name to avoid dependency collision with rust core 'test' crate (o/w surfaces as compiler errors during testing) @@ -277,9 +277,9 @@ bstr = "1.9.1" bytecount = "0.6.8" byteorder = "1.5.0" chrono = { version = "0.4.38", default-features = false, features = [ - "std", - "alloc", - "clock", + "std", + "alloc", + "clock", ] } chrono-tz = "0.10.0" clap = { version = "4.5", features = ["wrap_help", "cargo"] } @@ -306,7 +306,7 @@ itertools = "0.14.0" libc = "0.2.172" linux-raw-sys = "0.9" lscolors = { version = "0.20.0", default-features = false, features = [ - "gnu_legacy", + "gnu_legacy", ] } memchr = "2.7.2" memmap2 = "0.9.4" @@ -509,11 +509,11 @@ time = { workspace = true, features = ["local-offset"] } unindent = "0.2.3" uutests = { workspace = true } uucore = { workspace = true, features = [ - "mode", - "entries", - "process", - "signals", - "utmpx", + "mode", + "entries", + "process", + "signals", + "utmpx", ] } walkdir = { workspace = true } hex-literal = "1.0.0" From 8d2a9c1a12d56184e67f374beb98f89b7c118552 Mon Sep 17 00:00:00 2001 From: n4n5 Date: Fri, 25 Apr 2025 22:51:28 +0200 Subject: [PATCH 03/35] fix features flag --- Cargo.toml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d8582c989fb..427b8995093 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ edition.workspace = true all-features = true [features] -default = ["feat_common_core", "zip"] +default = ["feat_common_core", "dep:zip"] ## OS feature shortcodes macos = ["feat_os_macos"] unix = ["feat_os_unix"] @@ -35,7 +35,7 @@ expensive_tests = [] # "test_risky_names" == enable tests that create problematic file names (would make a network share inaccessible to Windows, breaks SVN on Mac OS, etc.) test_risky_names = [] # * only build `uudoc` when `--feature uudoc` is activated -uudoc = ["zip", "dep:uuhelp_parser"] +uudoc = ["dep:zip", "dep:uuhelp_parser"] ## features # "feat_acl" == enable support for ACLs (access control lists; by using`--features feat_acl`) # NOTE: @@ -542,6 +542,7 @@ phf_codegen = { workspace = true } [[bin]] name = "coreutils" path = "src/bin/coreutils.rs" +required-features = ["default"] [[bin]] name = "uudoc" From f01e40b17a3666cb94a9becd7512267bedeead26 Mon Sep 17 00:00:00 2001 From: n4n5 Date: Fri, 25 Apr 2025 23:00:47 +0200 Subject: [PATCH 04/35] change required feature --- Cargo.toml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 427b8995093..6125ea8423b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ edition.workspace = true all-features = true [features] -default = ["feat_common_core", "dep:zip"] +default = ["feat_common_core"] ## OS feature shortcodes macos = ["feat_os_macos"] unix = ["feat_os_unix"] @@ -36,6 +36,7 @@ expensive_tests = [] test_risky_names = [] # * only build `uudoc` when `--feature uudoc` is activated uudoc = ["dep:zip", "dep:uuhelp_parser"] +coreutils = ["dep:zip"] ## features # "feat_acl" == enable support for ACLs (access control lists; by using`--features feat_acl`) # NOTE: @@ -542,7 +543,7 @@ phf_codegen = { workspace = true } [[bin]] name = "coreutils" path = "src/bin/coreutils.rs" -required-features = ["default"] +required-features = ["coreutils"] [[bin]] name = "uudoc" From 5bdafc62810607008a35fb4680a48a6eb6ead337 Mon Sep 17 00:00:00 2001 From: n4n5 Date: Fri, 25 Apr 2025 23:07:19 +0200 Subject: [PATCH 05/35] depencies --- Cargo.toml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6125ea8423b..427b8995093 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ edition.workspace = true all-features = true [features] -default = ["feat_common_core"] +default = ["feat_common_core", "dep:zip"] ## OS feature shortcodes macos = ["feat_os_macos"] unix = ["feat_os_unix"] @@ -36,7 +36,6 @@ expensive_tests = [] test_risky_names = [] # * only build `uudoc` when `--feature uudoc` is activated uudoc = ["dep:zip", "dep:uuhelp_parser"] -coreutils = ["dep:zip"] ## features # "feat_acl" == enable support for ACLs (access control lists; by using`--features feat_acl`) # NOTE: @@ -543,7 +542,7 @@ phf_codegen = { workspace = true } [[bin]] name = "coreutils" path = "src/bin/coreutils.rs" -required-features = ["coreutils"] +required-features = ["default"] [[bin]] name = "uudoc" From 1063992b225fdece929c6178848d4a087290bb73 Mon Sep 17 00:00:00 2001 From: n4n5 Date: Fri, 25 Apr 2025 18:09:45 +0200 Subject: [PATCH 06/35] add examples --- Cargo.toml | 330 +++++++++++++++++++++---------------------- src/bin/coreutils.rs | 154 ++++++++++++++++---- 2 files changed, 288 insertions(+), 196 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index cde946b68f4..b7ca5a03308 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ edition.workspace = true all-features = true [features] -default = ["feat_common_core"] +default = ["feat_common_core", "zip"] ## OS feature shortcodes macos = ["feat_os_macos"] unix = ["feat_os_unix"] @@ -47,138 +47,138 @@ feat_acl = ["cp/feat_acl"] # * The selinux(-sys) crate requires `libselinux` headers and shared library to be accessible in the C toolchain at compile time. # * Running a uutils compiled with `feat_selinux` requires an SELinux enabled Kernel at run time. feat_selinux = [ - "cp/selinux", - "id/selinux", - "ls/selinux", - "mkdir/selinux", - "mkfifo/selinux", - "mknod/selinux", - "stat/selinux", - "selinux", - "feat_require_selinux", + "cp/selinux", + "id/selinux", + "ls/selinux", + "mkdir/selinux", + "mkfifo/selinux", + "mknod/selinux", + "stat/selinux", + "selinux", + "feat_require_selinux", ] ## ## feature sets ## (common/core and Tier1) feature sets # "feat_common_core" == baseline core set of utilities which can be built/run on most targets feat_common_core = [ - "base32", - "base64", - "basename", - "basenc", - "cat", - "cksum", - "comm", - "cp", - "csplit", - "cut", - "date", - "df", - "dir", - "dircolors", - "dirname", - "dd", - "du", - "echo", - "env", - "expand", - "expr", - "factor", - "false", - "fmt", - "fold", - "hashsum", - "head", - "join", - "link", - "ln", - "ls", - "mkdir", - "mktemp", - "more", - "mv", - "nl", - "numfmt", - "od", - "paste", - "pr", - "printenv", - "printf", - "ptx", - "pwd", - "readlink", - "realpath", - "rm", - "rmdir", - "seq", - "shred", - "shuf", - "sleep", - "sort", - "split", - "sum", - "tac", - "tail", - "tee", - "test", - "tr", - "true", - "truncate", - "tsort", - "touch", - "unexpand", - "uniq", - "unlink", - "vdir", - "wc", - "yes", + "base32", + "base64", + "basename", + "basenc", + "cat", + "cksum", + "comm", + "cp", + "csplit", + "cut", + "date", + "df", + "dir", + "dircolors", + "dirname", + "dd", + "du", + "echo", + "env", + "expand", + "expr", + "factor", + "false", + "fmt", + "fold", + "hashsum", + "head", + "join", + "link", + "ln", + "ls", + "mkdir", + "mktemp", + "more", + "mv", + "nl", + "numfmt", + "od", + "paste", + "pr", + "printenv", + "printf", + "ptx", + "pwd", + "readlink", + "realpath", + "rm", + "rmdir", + "seq", + "shred", + "shuf", + "sleep", + "sort", + "split", + "sum", + "tac", + "tail", + "tee", + "test", + "tr", + "true", + "truncate", + "tsort", + "touch", + "unexpand", + "uniq", + "unlink", + "vdir", + "wc", + "yes", ] # "feat_Tier1" == expanded set of utilities which can be built/run on the usual rust "Tier 1" target platforms (ref: ) feat_Tier1 = [ - "feat_common_core", - # - "arch", - "hostname", - "nproc", - "sync", - "touch", - "uname", - "whoami", + "feat_common_core", + # + "arch", + "hostname", + "nproc", + "sync", + "touch", + "uname", + "whoami", ] ## (primary platforms) feature sets # "feat_os_macos" == set of utilities which can be built/run on the MacOS platform feat_os_macos = [ - "feat_os_unix", ## == a modern/usual *nix platform - # - "feat_require_unix_hostid", + "feat_os_unix", ## == a modern/usual *nix platform + # + "feat_require_unix_hostid", ] # "feat_os_unix" == set of utilities which can be built/run on modern/usual *nix platforms. # Also used for targets binding to the "musl" library (ref: ) feat_os_unix = [ - "feat_Tier1", - # - "feat_require_crate_cpp", - "feat_require_unix", - "feat_require_unix_utmpx", - "feat_require_unix_hostid", + "feat_Tier1", + # + "feat_require_crate_cpp", + "feat_require_unix", + "feat_require_unix_utmpx", + "feat_require_unix_hostid", ] # "feat_os_windows" == set of utilities which can be built/run on modern/usual windows platforms feat_os_windows = [ - "feat_Tier1", ## == "feat_os_windows_legacy" + "hostname" + "feat_Tier1", ## == "feat_os_windows_legacy" + "hostname" ] ## (secondary platforms) feature sets # "feat_os_unix_gnueabihf" == set of utilities which can be built/run on the "arm-unknown-linux-gnueabihf" target (ARMv6 Linux [hardfloat]) feat_os_unix_gnueabihf = [ - "feat_Tier1", - # - "feat_require_unix", - "feat_require_unix_hostid", - "feat_require_unix_utmpx", + "feat_Tier1", + # + "feat_require_unix", + "feat_require_unix_hostid", + "feat_require_unix_utmpx", ] feat_os_unix_android = [ - "feat_Tier1", - # - "feat_require_unix", + "feat_Tier1", + # + "feat_require_unix", ] ## feature sets with requirements (restricting cross-platform availability) # @@ -188,24 +188,24 @@ feat_os_unix_android = [ feat_require_crate_cpp = ["stdbuf"] # "feat_require_unix" == set of utilities requiring support which is only available on unix platforms (as of 2020-04-23) feat_require_unix = [ - "chgrp", - "chmod", - "chown", - "chroot", - "groups", - "id", - "install", - "kill", - "logname", - "mkfifo", - "mknod", - "nice", - "nohup", - "pathchk", - "stat", - "stty", - "timeout", - "tty", + "chgrp", + "chmod", + "chown", + "chroot", + "groups", + "id", + "install", + "kill", + "logname", + "mkfifo", + "mknod", + "nice", + "nohup", + "pathchk", + "stat", + "stty", + "timeout", + "tty", ] # "feat_require_unix_utmpx" == set of utilities requiring unix utmp/utmpx support # * ref: @@ -217,43 +217,43 @@ feat_require_selinux = ["chcon", "runcon"] ## (alternate/newer/smaller platforms) feature sets # "feat_os_unix_fuchsia" == set of utilities which can be built/run on the "Fuchsia" OS (refs: ; ) feat_os_unix_fuchsia = [ - "feat_common_core", - # - "feat_require_crate_cpp", - # - "chgrp", - "chmod", - "chown", - "du", - "groups", - "hostid", - "install", - "logname", - "mkfifo", - "mknod", - "nice", - "pathchk", - "tty", - "uname", - "unlink", + "feat_common_core", + # + "feat_require_crate_cpp", + # + "chgrp", + "chmod", + "chown", + "du", + "groups", + "hostid", + "install", + "logname", + "mkfifo", + "mknod", + "nice", + "pathchk", + "tty", + "uname", + "unlink", ] # "feat_os_unix_redox" == set of utilities which can be built/run on "Redox OS" (refs: ; ) feat_os_unix_redox = [ - "feat_common_core", - # - "chmod", - "stat", - "uname", + "feat_common_core", + # + "chmod", + "stat", + "uname", ] # "feat_os_windows_legacy" == slightly restricted set of utilities which can be built/run on early windows platforms (eg, "WinXP") feat_os_windows_legacy = [ - "feat_common_core", - # - "arch", - "nproc", - "sync", - "touch", - "whoami", + "feat_common_core", + # + "arch", + "nproc", + "sync", + "touch", + "whoami", ] ## # * bypass/override ~ translate 'test' feature name to avoid dependency collision with rust core 'test' crate (o/w surfaces as compiler errors during testing) @@ -277,9 +277,9 @@ bstr = "1.9.1" bytecount = "0.6.8" byteorder = "1.5.0" chrono = { version = "0.4.38", default-features = false, features = [ - "std", - "alloc", - "clock", + "std", + "alloc", + "clock", ] } chrono-tz = "0.10.0" clap = { version = "4.5", features = ["wrap_help", "cargo"] } @@ -306,7 +306,7 @@ itertools = "0.14.0" libc = "0.2.172" linux-raw-sys = "0.9" lscolors = { version = "0.20.0", default-features = false, features = [ - "gnu_legacy", + "gnu_legacy", ] } memchr = "2.7.2" memmap2 = "0.9.4" @@ -509,11 +509,11 @@ time = { workspace = true, features = ["local-offset"] } unindent = "0.2.3" uutests = { workspace = true } uucore = { workspace = true, features = [ - "mode", - "entries", - "process", - "signals", - "utmpx", + "mode", + "entries", + "process", + "signals", + "utmpx", ] } walkdir = { workspace = true } hex-literal = "1.0.0" diff --git a/src/bin/coreutils.rs b/src/bin/coreutils.rs index b29e7ea2337..c9064189772 100644 --- a/src/bin/coreutils.rs +++ b/src/bin/coreutils.rs @@ -10,19 +10,34 @@ use clap_complete::Shell; use std::cmp; use std::ffi::OsStr; use std::ffi::OsString; +use std::fs::File; +use std::io::Read; +use std::io::Seek; use std::io::{self, Write}; use std::path::{Path, PathBuf}; use std::process; use uucore::display::Quotable; +use zip::ZipArchive; const VERSION: &str = env!("CARGO_PKG_VERSION"); +const COMPLETION: &str = "completion"; +const MANPAGE: &str = "manpage"; + include!(concat!(env!("OUT_DIR"), "/uutils_map.rs")); fn usage(utils: &UtilityMap, name: &str) { println!("{name} {VERSION} (multi-call binary)\n"); println!("Usage: {name} [function [arguments...]]"); - println!(" {name} --list\n"); + println!(" {name} --list"); + println!(); + println!("Functions:"); + println!(" {COMPLETION}",); + println!(" {}", get_completion_args(utils).render_usage()); + println!(" '{MANPAGE}'",); + println!(" {}", get_manpage_args(utils).render_usage()); + println!(" '' [arguments...]"); + println!(); println!("Options:"); println!(" --list lists all defined functions, one per row\n"); println!("Currently defined functions:\n"); @@ -95,8 +110,14 @@ fn main() { }; match util { - "completion" => gen_completions(args, &utils), - "manpage" => gen_manpage(args, &utils), + COMPLETION => { + gen_completions(args, &utils); + process::exit(0); + } + MANPAGE => { + gen_manpage(args, &utils); + process::exit(0); + } "--list" => { let mut utils: Vec<_> = utils.keys().collect(); utils.sort(); @@ -148,18 +169,11 @@ fn main() { } } -/// Prints completions for the utility in the first parameter for the shell in the second parameter to stdout -/// # Panics -/// Panics if the utility map is empty -fn gen_completions( - args: impl Iterator, - util_map: &UtilityMap, -) -> ! { +fn get_completion_args(util_map: &UtilityMap) -> clap::Command { let all_utilities: Vec<_> = std::iter::once("coreutils") .chain(util_map.keys().copied()) .collect(); - - let matches = Command::new("completion") + Command::new(COMPLETION) .about("Prints completions to stdout") .arg( Arg::new("utility") @@ -171,7 +185,17 @@ fn gen_completions( .value_parser(clap::builder::EnumValueParser::::new()) .required(true), ) - .get_matches_from(std::iter::once(OsString::from("completion")).chain(args)); +} + +/// Prints completions for the utility in the first parameter for the shell in the second parameter to stdout +/// # Panics +/// Panics if the utility map is empty +fn gen_completions( + args: impl Iterator, + util_map: &UtilityMap, +) { + let matches = get_completion_args(util_map) + .get_matches_from(std::iter::once(OsString::from(COMPLETION)).chain(args)); let utility = matches.get_one::("utility").unwrap(); let shell = *matches.get_one::("shell").unwrap(); @@ -185,42 +209,49 @@ fn gen_completions( clap_complete::generate(shell, &mut command, bin_name, &mut io::stdout()); io::stdout().flush().unwrap(); - process::exit(0); } -/// Generate the manpage for the utility in the first parameter -/// # Panics -/// Panics if the utility map is empty -fn gen_manpage( - args: impl Iterator, - util_map: &UtilityMap, -) -> ! { +fn get_manpage_args(util_map: &UtilityMap) -> clap::Command { let all_utilities: Vec<_> = std::iter::once("coreutils") .chain(util_map.keys().copied()) .collect(); + Command::new(MANPAGE).about("Prints manpage to stdout").arg( + Arg::new("utility") + .value_parser(clap::builder::PossibleValuesParser::new(all_utilities)) + .required(true), + ) +} - let matches = Command::new("manpage") - .about("Prints manpage to stdout") - .arg( - Arg::new("utility") - .value_parser(clap::builder::PossibleValuesParser::new(all_utilities)) - .required(true), - ) - .get_matches_from(std::iter::once(OsString::from("manpage")).chain(args)); +/// Generate the manpage for the utility in the first parameter +/// # Panics +/// Panics if the utility map is empty +fn gen_manpage(args: impl Iterator, util_map: &UtilityMap) { + let tldr_zip = File::open("docs/tldr.zip") + .ok() + .and_then(|f| ZipArchive::new(f).ok()); + + if tldr_zip.is_none() { + // Could not open tldr.zip + } + let matches = get_manpage_args(util_map) + .get_matches_from(std::iter::once(OsString::from(MANPAGE)).chain(args)); let utility = matches.get_one::("utility").unwrap(); let command = if utility == "coreutils" { gen_coreutils_app(util_map) } else { - util_map.get(utility).unwrap().1() + let mut cmd = util_map.get(utility).unwrap().1(); + if let Ok(examples) = get_zip_examples(utility) { + cmd = cmd.after_help(examples); + } + cmd }; let man = clap_mangen::Man::new(command); man.render(&mut io::stdout()) .expect("Man page generation failed"); io::stdout().flush().unwrap(); - process::exit(0); } /// # Panics @@ -239,3 +270,64 @@ fn gen_coreutils_app(util_map: &UtilityMap) -> Command { } command } + +/// # Errors +/// Returns an error if the tldr.zip file cannot be opened or read +fn get_zip_examples(name: &str) -> io::Result { + fn get_zip_content(archive: &mut ZipArchive, name: &str) -> Option { + let mut s = String::new(); + archive.by_name(name).ok()?.read_to_string(&mut s).unwrap(); + Some(s) + } + + let mut w = io::BufWriter::new(Vec::new()); + let file = File::open("docs/tldr.zip")?; + let mut tldr_zip = match ZipArchive::new(file) { + Ok(zip) => zip, + Err(e) => { + return Err(io::Error::new( + io::ErrorKind::NotFound, + format!("Error reading tldr.zip: {}", e), + )); + } + }; + + let content = if let Some(f) = + get_zip_content(&mut tldr_zip, &format!("pages/common/{}.md", name)) + { + f + } else if let Some(f) = get_zip_content(&mut tldr_zip, &format!("pages/linux/{}.md", name)) { + f + } else { + return Err(io::Error::new( + io::ErrorKind::NotFound, + "Could not find tldr examples", + )); + }; + + writeln!(w, "Examples")?; + writeln!(w)?; + for line in content.lines().skip_while(|l| !l.starts_with('-')) { + if let Some(l) = line.strip_prefix("- ") { + writeln!(w, "{l}")?; + } else if line.starts_with('`') { + writeln!(w, "{}", line.trim_matches('`'))?; + } else if line.is_empty() { + writeln!(w)?; + } else { + // println!("Not sure what to do with this line:"); + // println!("{line}"); + } + } + writeln!(w)?; + writeln!( + w, + "> The examples are provided by the [tldr-pages project](https://tldr.sh) under the [CC BY 4.0 License](https://github.com/tldr-pages/tldr/blob/main/LICENSE.md)." + )?; + writeln!(w, "\n\n\n")?; + writeln!( + w, + "> Please note that, as uutils is a work in progress, some examples might fail." + )?; + Ok(String::from_utf8(w.into_inner().unwrap()).unwrap()) +} From a46a7742088ee41ad2c531db0694a5fec5ec2d4b Mon Sep 17 00:00:00 2001 From: n4n5 Date: Fri, 25 Apr 2025 18:11:58 +0200 Subject: [PATCH 07/35] fix: space --- Cargo.toml | 328 ++++++++++++++++++++++++++--------------------------- 1 file changed, 164 insertions(+), 164 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b7ca5a03308..d8582c989fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,138 +47,138 @@ feat_acl = ["cp/feat_acl"] # * The selinux(-sys) crate requires `libselinux` headers and shared library to be accessible in the C toolchain at compile time. # * Running a uutils compiled with `feat_selinux` requires an SELinux enabled Kernel at run time. feat_selinux = [ - "cp/selinux", - "id/selinux", - "ls/selinux", - "mkdir/selinux", - "mkfifo/selinux", - "mknod/selinux", - "stat/selinux", - "selinux", - "feat_require_selinux", + "cp/selinux", + "id/selinux", + "ls/selinux", + "mkdir/selinux", + "mkfifo/selinux", + "mknod/selinux", + "stat/selinux", + "selinux", + "feat_require_selinux", ] ## ## feature sets ## (common/core and Tier1) feature sets # "feat_common_core" == baseline core set of utilities which can be built/run on most targets feat_common_core = [ - "base32", - "base64", - "basename", - "basenc", - "cat", - "cksum", - "comm", - "cp", - "csplit", - "cut", - "date", - "df", - "dir", - "dircolors", - "dirname", - "dd", - "du", - "echo", - "env", - "expand", - "expr", - "factor", - "false", - "fmt", - "fold", - "hashsum", - "head", - "join", - "link", - "ln", - "ls", - "mkdir", - "mktemp", - "more", - "mv", - "nl", - "numfmt", - "od", - "paste", - "pr", - "printenv", - "printf", - "ptx", - "pwd", - "readlink", - "realpath", - "rm", - "rmdir", - "seq", - "shred", - "shuf", - "sleep", - "sort", - "split", - "sum", - "tac", - "tail", - "tee", - "test", - "tr", - "true", - "truncate", - "tsort", - "touch", - "unexpand", - "uniq", - "unlink", - "vdir", - "wc", - "yes", + "base32", + "base64", + "basename", + "basenc", + "cat", + "cksum", + "comm", + "cp", + "csplit", + "cut", + "date", + "df", + "dir", + "dircolors", + "dirname", + "dd", + "du", + "echo", + "env", + "expand", + "expr", + "factor", + "false", + "fmt", + "fold", + "hashsum", + "head", + "join", + "link", + "ln", + "ls", + "mkdir", + "mktemp", + "more", + "mv", + "nl", + "numfmt", + "od", + "paste", + "pr", + "printenv", + "printf", + "ptx", + "pwd", + "readlink", + "realpath", + "rm", + "rmdir", + "seq", + "shred", + "shuf", + "sleep", + "sort", + "split", + "sum", + "tac", + "tail", + "tee", + "test", + "tr", + "true", + "truncate", + "tsort", + "touch", + "unexpand", + "uniq", + "unlink", + "vdir", + "wc", + "yes", ] # "feat_Tier1" == expanded set of utilities which can be built/run on the usual rust "Tier 1" target platforms (ref: ) feat_Tier1 = [ - "feat_common_core", - # - "arch", - "hostname", - "nproc", - "sync", - "touch", - "uname", - "whoami", + "feat_common_core", + # + "arch", + "hostname", + "nproc", + "sync", + "touch", + "uname", + "whoami", ] ## (primary platforms) feature sets # "feat_os_macos" == set of utilities which can be built/run on the MacOS platform feat_os_macos = [ - "feat_os_unix", ## == a modern/usual *nix platform - # - "feat_require_unix_hostid", + "feat_os_unix", ## == a modern/usual *nix platform + # + "feat_require_unix_hostid", ] # "feat_os_unix" == set of utilities which can be built/run on modern/usual *nix platforms. # Also used for targets binding to the "musl" library (ref: ) feat_os_unix = [ - "feat_Tier1", - # - "feat_require_crate_cpp", - "feat_require_unix", - "feat_require_unix_utmpx", - "feat_require_unix_hostid", + "feat_Tier1", + # + "feat_require_crate_cpp", + "feat_require_unix", + "feat_require_unix_utmpx", + "feat_require_unix_hostid", ] # "feat_os_windows" == set of utilities which can be built/run on modern/usual windows platforms feat_os_windows = [ - "feat_Tier1", ## == "feat_os_windows_legacy" + "hostname" + "feat_Tier1", ## == "feat_os_windows_legacy" + "hostname" ] ## (secondary platforms) feature sets # "feat_os_unix_gnueabihf" == set of utilities which can be built/run on the "arm-unknown-linux-gnueabihf" target (ARMv6 Linux [hardfloat]) feat_os_unix_gnueabihf = [ - "feat_Tier1", - # - "feat_require_unix", - "feat_require_unix_hostid", - "feat_require_unix_utmpx", + "feat_Tier1", + # + "feat_require_unix", + "feat_require_unix_hostid", + "feat_require_unix_utmpx", ] feat_os_unix_android = [ - "feat_Tier1", - # - "feat_require_unix", + "feat_Tier1", + # + "feat_require_unix", ] ## feature sets with requirements (restricting cross-platform availability) # @@ -188,24 +188,24 @@ feat_os_unix_android = [ feat_require_crate_cpp = ["stdbuf"] # "feat_require_unix" == set of utilities requiring support which is only available on unix platforms (as of 2020-04-23) feat_require_unix = [ - "chgrp", - "chmod", - "chown", - "chroot", - "groups", - "id", - "install", - "kill", - "logname", - "mkfifo", - "mknod", - "nice", - "nohup", - "pathchk", - "stat", - "stty", - "timeout", - "tty", + "chgrp", + "chmod", + "chown", + "chroot", + "groups", + "id", + "install", + "kill", + "logname", + "mkfifo", + "mknod", + "nice", + "nohup", + "pathchk", + "stat", + "stty", + "timeout", + "tty", ] # "feat_require_unix_utmpx" == set of utilities requiring unix utmp/utmpx support # * ref: @@ -217,43 +217,43 @@ feat_require_selinux = ["chcon", "runcon"] ## (alternate/newer/smaller platforms) feature sets # "feat_os_unix_fuchsia" == set of utilities which can be built/run on the "Fuchsia" OS (refs: ; ) feat_os_unix_fuchsia = [ - "feat_common_core", - # - "feat_require_crate_cpp", - # - "chgrp", - "chmod", - "chown", - "du", - "groups", - "hostid", - "install", - "logname", - "mkfifo", - "mknod", - "nice", - "pathchk", - "tty", - "uname", - "unlink", + "feat_common_core", + # + "feat_require_crate_cpp", + # + "chgrp", + "chmod", + "chown", + "du", + "groups", + "hostid", + "install", + "logname", + "mkfifo", + "mknod", + "nice", + "pathchk", + "tty", + "uname", + "unlink", ] # "feat_os_unix_redox" == set of utilities which can be built/run on "Redox OS" (refs: ; ) feat_os_unix_redox = [ - "feat_common_core", - # - "chmod", - "stat", - "uname", + "feat_common_core", + # + "chmod", + "stat", + "uname", ] # "feat_os_windows_legacy" == slightly restricted set of utilities which can be built/run on early windows platforms (eg, "WinXP") feat_os_windows_legacy = [ - "feat_common_core", - # - "arch", - "nproc", - "sync", - "touch", - "whoami", + "feat_common_core", + # + "arch", + "nproc", + "sync", + "touch", + "whoami", ] ## # * bypass/override ~ translate 'test' feature name to avoid dependency collision with rust core 'test' crate (o/w surfaces as compiler errors during testing) @@ -277,9 +277,9 @@ bstr = "1.9.1" bytecount = "0.6.8" byteorder = "1.5.0" chrono = { version = "0.4.38", default-features = false, features = [ - "std", - "alloc", - "clock", + "std", + "alloc", + "clock", ] } chrono-tz = "0.10.0" clap = { version = "4.5", features = ["wrap_help", "cargo"] } @@ -306,7 +306,7 @@ itertools = "0.14.0" libc = "0.2.172" linux-raw-sys = "0.9" lscolors = { version = "0.20.0", default-features = false, features = [ - "gnu_legacy", + "gnu_legacy", ] } memchr = "2.7.2" memmap2 = "0.9.4" @@ -509,11 +509,11 @@ time = { workspace = true, features = ["local-offset"] } unindent = "0.2.3" uutests = { workspace = true } uucore = { workspace = true, features = [ - "mode", - "entries", - "process", - "signals", - "utmpx", + "mode", + "entries", + "process", + "signals", + "utmpx", ] } walkdir = { workspace = true } hex-literal = "1.0.0" From c65845e9fe2ecf138ccaebd81a3657caf5d47dce Mon Sep 17 00:00:00 2001 From: n4n5 Date: Fri, 25 Apr 2025 22:51:28 +0200 Subject: [PATCH 08/35] fix features flag --- Cargo.toml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d8582c989fb..427b8995093 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ edition.workspace = true all-features = true [features] -default = ["feat_common_core", "zip"] +default = ["feat_common_core", "dep:zip"] ## OS feature shortcodes macos = ["feat_os_macos"] unix = ["feat_os_unix"] @@ -35,7 +35,7 @@ expensive_tests = [] # "test_risky_names" == enable tests that create problematic file names (would make a network share inaccessible to Windows, breaks SVN on Mac OS, etc.) test_risky_names = [] # * only build `uudoc` when `--feature uudoc` is activated -uudoc = ["zip", "dep:uuhelp_parser"] +uudoc = ["dep:zip", "dep:uuhelp_parser"] ## features # "feat_acl" == enable support for ACLs (access control lists; by using`--features feat_acl`) # NOTE: @@ -542,6 +542,7 @@ phf_codegen = { workspace = true } [[bin]] name = "coreutils" path = "src/bin/coreutils.rs" +required-features = ["default"] [[bin]] name = "uudoc" From 7b75db17d9e26d4976d6640a50bf4bb4ded6af5e Mon Sep 17 00:00:00 2001 From: n4n5 Date: Fri, 25 Apr 2025 23:00:47 +0200 Subject: [PATCH 09/35] change required feature --- Cargo.toml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 427b8995093..6125ea8423b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ edition.workspace = true all-features = true [features] -default = ["feat_common_core", "dep:zip"] +default = ["feat_common_core"] ## OS feature shortcodes macos = ["feat_os_macos"] unix = ["feat_os_unix"] @@ -36,6 +36,7 @@ expensive_tests = [] test_risky_names = [] # * only build `uudoc` when `--feature uudoc` is activated uudoc = ["dep:zip", "dep:uuhelp_parser"] +coreutils = ["dep:zip"] ## features # "feat_acl" == enable support for ACLs (access control lists; by using`--features feat_acl`) # NOTE: @@ -542,7 +543,7 @@ phf_codegen = { workspace = true } [[bin]] name = "coreutils" path = "src/bin/coreutils.rs" -required-features = ["default"] +required-features = ["coreutils"] [[bin]] name = "uudoc" From 5e18f676faff86f82b3c42536fe4284af58c9249 Mon Sep 17 00:00:00 2001 From: n4n5 Date: Fri, 25 Apr 2025 23:07:19 +0200 Subject: [PATCH 10/35] depencies --- Cargo.toml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6125ea8423b..427b8995093 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ edition.workspace = true all-features = true [features] -default = ["feat_common_core"] +default = ["feat_common_core", "dep:zip"] ## OS feature shortcodes macos = ["feat_os_macos"] unix = ["feat_os_unix"] @@ -36,7 +36,6 @@ expensive_tests = [] test_risky_names = [] # * only build `uudoc` when `--feature uudoc` is activated uudoc = ["dep:zip", "dep:uuhelp_parser"] -coreutils = ["dep:zip"] ## features # "feat_acl" == enable support for ACLs (access control lists; by using`--features feat_acl`) # NOTE: @@ -543,7 +542,7 @@ phf_codegen = { workspace = true } [[bin]] name = "coreutils" path = "src/bin/coreutils.rs" -required-features = ["coreutils"] +required-features = ["default"] [[bin]] name = "uudoc" From 0b2a48bf9699b59992d445fbe6a8f34a9ce4e077 Mon Sep 17 00:00:00 2001 From: n4n5 Date: Tue, 6 May 2025 21:04:11 -0500 Subject: [PATCH 11/35] update --- Cargo.toml | 5 +++-- GNUmakefile | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 427b8995093..ee10e41b38d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ edition.workspace = true all-features = true [features] -default = ["feat_common_core", "dep:zip"] +default = ["feat_common_core", "coreutils"] ## OS feature shortcodes macos = ["feat_os_macos"] unix = ["feat_os_unix"] @@ -36,6 +36,7 @@ expensive_tests = [] test_risky_names = [] # * only build `uudoc` when `--feature uudoc` is activated uudoc = ["dep:zip", "dep:uuhelp_parser"] +coreutils = ["dep:zip"] ## features # "feat_acl" == enable support for ACLs (access control lists; by using`--features feat_acl`) # NOTE: @@ -542,7 +543,7 @@ phf_codegen = { workspace = true } [[bin]] name = "coreutils" path = "src/bin/coreutils.rs" -required-features = ["default"] +required-features = ["coreutils"] [[bin]] name = "uudoc" diff --git a/GNUmakefile b/GNUmakefile index 8d7179b5eb8..899bc07e24d 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -300,7 +300,7 @@ endif endif build-coreutils: - ${CARGO} build ${CARGOFLAGS} --features "${EXES} $(BUILD_SPEC_FEATURE)" ${PROFILE_CMD} --no-default-features + ${CARGO} build ${CARGOFLAGS} --features "${EXES} $(BUILD_SPEC_FEATURE) coreutils" ${PROFILE_CMD} --no-default-features build: build-coreutils build-pkgs From 5a1f64beb69dcdaaa4e9bea3ad9f8f3e2d109376 Mon Sep 17 00:00:00 2001 From: n4n5 Date: Tue, 6 May 2025 21:07:56 -0500 Subject: [PATCH 12/35] fix build.rs --- build.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build.rs b/build.rs index 3b6aa3878d1..7467b428feb 100644 --- a/build.rs +++ b/build.rs @@ -36,8 +36,9 @@ pub fn main() { "nightly" | "test_unimplemented" | "expensive_tests" | "test_risky_names" => { continue; } // crate-local custom features - "uudoc" => continue, // is not a utility + "uudoc" => continue, // is not a utility "test" => continue, // over-ridden with 'uu_test' to avoid collision with rust core crate 'test' + "coreutils" => continue, // coreutils s if s.starts_with(FEATURE_PREFIX) => continue, // crate feature sets _ => {} // util feature name } From 085f00a9526cb582082efd0868cd12e81d57a255 Mon Sep 17 00:00:00 2001 From: n4n5 Date: Tue, 6 May 2025 21:21:08 -0500 Subject: [PATCH 13/35] fix ci --- .github/workflows/CICD.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index df1fba73c54..6bb5c088da4 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -1160,8 +1160,8 @@ jobs: run: | for f in $(util/show-utils.sh) do - echo "Running tests with --features=$f and --no-default-features" - cargo test --features=$f --no-default-features + echo "Running tests with --features="$f coreutils" --no-default-features" + cargo test --features="$f coreutils" --no-default-features done test_selinux: From c9bc1c7776db9f05a97bff411a73eb9daba49ed7 Mon Sep 17 00:00:00 2001 From: n4n5 Date: Tue, 6 May 2025 21:22:55 -0500 Subject: [PATCH 14/35] fix not default features --- GNUmakefile | 4 ++-- src/uu/rm/benchmark.sh | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/GNUmakefile b/GNUmakefile index 899bc07e24d..907211e17b4 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -307,10 +307,10 @@ build: build-coreutils build-pkgs $(foreach test,$(filter-out $(SKIP_UTILS),$(PROGS)),$(eval $(call TEST_BUSYBOX,$(test)))) test: - ${CARGO} test ${CARGOFLAGS} --features "$(TESTS) $(TEST_SPEC_FEATURE)" --no-default-features $(TEST_NO_FAIL_FAST) + ${CARGO} test ${CARGOFLAGS} --features "$(TESTS) $(TEST_SPEC_FEATURE) coreutils" --no-default-features $(TEST_NO_FAIL_FAST) nextest: - ${CARGO} nextest run ${CARGOFLAGS} --features "$(TESTS) $(TEST_SPEC_FEATURE)" --no-default-features $(TEST_NO_FAIL_FAST) + ${CARGO} nextest run ${CARGOFLAGS} --features "$(TESTS) $(TEST_SPEC_FEATURE) coreutils" --no-default-features $(TEST_NO_FAIL_FAST) test_toybox: -(cd $(TOYBOX_SRC)/ && make tests) diff --git a/src/uu/rm/benchmark.sh b/src/uu/rm/benchmark.sh index 5feaea4e8f1..93d4d0b5466 100755 --- a/src/uu/rm/benchmark.sh +++ b/src/uu/rm/benchmark.sh @@ -3,6 +3,6 @@ # Exit on any failures set +x -cargo build --no-default-features --features rm --release +cargo build --no-default-features --features rm coreutils --release test_dir="$1" -hyperfine --prepare "cp -r $test_dir tmp_d" "rm -rf tmp_d" "target/release/coreutils rm -rf tmp_d" +hyperfine --prepare "cp -r $test_dir tmp_d" "rm -rf tmp_d" "target/release/coreutils rm -rf tmp_d" From afe66ff7903ee8836dcad1bf7c82512b4c08f1cf Mon Sep 17 00:00:00 2001 From: n4n5 Date: Tue, 6 May 2025 21:24:11 -0500 Subject: [PATCH 15/35] fix spaces --- build.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.rs b/build.rs index 7467b428feb..b9be1d1e85b 100644 --- a/build.rs +++ b/build.rs @@ -36,7 +36,7 @@ pub fn main() { "nightly" | "test_unimplemented" | "expensive_tests" | "test_risky_names" => { continue; } // crate-local custom features - "uudoc" => continue, // is not a utility + "uudoc" => continue, // is not a utility "test" => continue, // over-ridden with 'uu_test' to avoid collision with rust core crate 'test' "coreutils" => continue, // coreutils s if s.starts_with(FEATURE_PREFIX) => continue, // crate feature sets From c163fc47e6a5a863877db92addd1dde3dc64a0c2 Mon Sep 17 00:00:00 2001 From: n4n5 Date: Tue, 6 May 2025 21:32:54 -0500 Subject: [PATCH 16/35] cargo fmt --- build.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.rs b/build.rs index b9be1d1e85b..7467b428feb 100644 --- a/build.rs +++ b/build.rs @@ -36,7 +36,7 @@ pub fn main() { "nightly" | "test_unimplemented" | "expensive_tests" | "test_risky_names" => { continue; } // crate-local custom features - "uudoc" => continue, // is not a utility + "uudoc" => continue, // is not a utility "test" => continue, // over-ridden with 'uu_test' to avoid collision with rust core crate 'test' "coreutils" => continue, // coreutils s if s.starts_with(FEATURE_PREFIX) => continue, // crate feature sets From 0d53cdfce907f0d2c30d735dc6c2ae545decce87 Mon Sep 17 00:00:00 2001 From: n4n5 Date: Wed, 22 Oct 2025 17:31:14 -0600 Subject: [PATCH 17/35] cleaner dep --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index d54a073cd9a..4c51242820f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,7 @@ expensive_tests = [] # "test_risky_names" == enable tests that create problematic file names (would make a network share inaccessible to Windows, breaks SVN on Mac OS, etc.) test_risky_names = [] # * only build `uudoc` when `--feature uudoc` is activated -uudoc = ["clap_complete", "clap_mangen", "fluent-syntax", "zip", "feat_Tier1"] +uudoc = ["dep:clap_complete", "dep:clap_mangen", "dep:fluent-syntax", "dep:zip", "feat_Tier1"] ## features ## Optional feature for stdbuf # "feat_external_libstdbuf" == use an external libstdbuf.so for stdbuf instead of embedding it From 9579f32d4f381fa2439bb616dddb5bb1404d99c0 Mon Sep 17 00:00:00 2001 From: n4n5 Date: Wed, 22 Oct 2025 17:32:58 -0600 Subject: [PATCH 18/35] fix move to uudoc --- src/bin/uudoc.rs | 67 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/src/bin/uudoc.rs b/src/bin/uudoc.rs index 49d6f16bce7..4d73d28d6e0 100644 --- a/src/bin/uudoc.rs +++ b/src/bin/uudoc.rs @@ -78,7 +78,11 @@ fn gen_manpage(args: impl Iterator, util_map: &Utility gen_coreutils_app(util_map) } else { validation::setup_localization_or_exit(utility); - util_map.get(utility).unwrap().1() + let mut cmd = util_map.get(utility).unwrap().1(); + if let Ok(examples) = get_zip_examples(utility) { + cmd = cmd.after_help(examples); + } + cmd }; let man = Man::new(command); @@ -548,3 +552,64 @@ fn get_zip_content(archive: &mut ZipArchive, name: &str) -> Op archive.by_name(name).ok()?.read_to_string(&mut s).unwrap(); Some(s) } + +/// # Errors +/// Returns an error if the tldr.zip file cannot be opened or read +fn get_zip_examples(name: &str) -> io::Result { + fn get_zip_content(archive: &mut ZipArchive, name: &str) -> Option { + let mut s = String::new(); + archive.by_name(name).ok()?.read_to_string(&mut s).unwrap(); + Some(s) + } + + let mut w = io::BufWriter::new(Vec::new()); + let file = File::open("docs/tldr.zip")?; + let mut tldr_zip = match ZipArchive::new(file) { + Ok(zip) => zip, + Err(e) => { + return Err(io::Error::new( + io::ErrorKind::NotFound, + format!("Error reading tldr.zip: {}", e), + )); + } + }; + + let content = if let Some(f) = + get_zip_content(&mut tldr_zip, &format!("pages/common/{}.md", name)) + { + f + } else if let Some(f) = get_zip_content(&mut tldr_zip, &format!("pages/linux/{}.md", name)) { + f + } else { + return Err(io::Error::new( + io::ErrorKind::NotFound, + "Could not find tldr examples", + )); + }; + + writeln!(w, "Examples")?; + writeln!(w)?; + for line in content.lines().skip_while(|l| !l.starts_with('-')) { + if let Some(l) = line.strip_prefix("- ") { + writeln!(w, "{l}")?; + } else if line.starts_with('`') { + writeln!(w, "{}", line.trim_matches('`'))?; + } else if line.is_empty() { + writeln!(w)?; + } else { + // println!("Not sure what to do with this line:"); + // println!("{line}"); + } + } + writeln!(w)?; + writeln!( + w, + "> The examples are provided by the [tldr-pages project](https://tldr.sh) under the [CC BY 4.0 License](https://github.com/tldr-pages/tldr/blob/main/LICENSE.md)." + )?; + writeln!(w, "\n\n\n")?; + writeln!( + w, + "> Please note that, as uutils is a work in progress, some examples might fail." + )?; + Ok(String::from_utf8(w.into_inner().unwrap()).unwrap()) +} From 968e1bdb6cfa5c04b9bb00abb00753471b397a66 Mon Sep 17 00:00:00 2001 From: n4n5 Date: Wed, 22 Oct 2025 17:35:54 -0600 Subject: [PATCH 19/35] revert --- Cargo.toml | 3 +-- GNUmakefile | 6 +++--- build.rs | 3 +-- src/bin/coreutils.rs | 14 -------------- src/uu/rm/benchmark.sh | 2 +- 5 files changed, 6 insertions(+), 22 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4c51242820f..28ea3a29dab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,7 @@ edition.workspace = true all-features = true [features] -default = ["feat_common_core", "coreutils"] +default = ["feat_common_core"] ## OS feature shortcodes macos = ["feat_os_macos"] unix = ["feat_os_unix"] @@ -582,7 +582,6 @@ phf_codegen.workspace = true [[bin]] name = "coreutils" path = "src/bin/coreutils.rs" -required-features = ["coreutils"] [[bin]] name = "uudoc" diff --git a/GNUmakefile b/GNUmakefile index 08cd5dc37d7..e4bc5f4355f 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -349,17 +349,17 @@ endif endif build-coreutils: - ${CARGO} build ${CARGOFLAGS} --features "${EXES} $(BUILD_SPEC_FEATURE) coreutils" ${PROFILE_CMD} --no-default-features + ${CARGO} build ${CARGOFLAGS} --features "${EXES} $(BUILD_SPEC_FEATURE)" ${PROFILE_CMD} --no-default-features build: build-coreutils build-pkgs locales $(foreach test,$(UTILS),$(eval $(call TEST_BUSYBOX,$(test)))) test: - ${CARGO} test ${CARGOFLAGS} --features "$(TESTS) $(TEST_SPEC_FEATURE) coreutils" --no-default-features $(TEST_NO_FAIL_FAST) + ${CARGO} test ${CARGOFLAGS} --features "$(TESTS) $(TEST_SPEC_FEATURE)" --no-default-features $(TEST_NO_FAIL_FAST) nextest: - ${CARGO} nextest run ${CARGOFLAGS} --features "$(TESTS) $(TEST_SPEC_FEATURE) coreutils" --no-default-features $(TEST_NO_FAIL_FAST) + ${CARGO} nextest run ${CARGOFLAGS} --features "$(TESTS) $(TEST_SPEC_FEATURE)" --no-default-features $(TEST_NO_FAIL_FAST) test_toybox: -(cd $(TOYBOX_SRC)/ && make tests) diff --git a/build.rs b/build.rs index 79144bd5747..aba8a95ee3d 100644 --- a/build.rs +++ b/build.rs @@ -37,9 +37,8 @@ pub fn main() { "nightly" | "test_unimplemented" | "expensive_tests" | "test_risky_names" => { continue; } // crate-local custom features - "uudoc" => continue, // is not a utility + "uudoc" => continue, // is not a utility "test" => continue, // over-ridden with 'uu_test' to avoid collision with rust core crate 'test' - "coreutils" => continue, // coreutils s if s.starts_with(FEATURE_PREFIX) => continue, // crate feature sets _ => {} // util feature name } diff --git a/src/bin/coreutils.rs b/src/bin/coreutils.rs index 5f5a6ecab27..d113cb8160a 100644 --- a/src/bin/coreutils.rs +++ b/src/bin/coreutils.rs @@ -5,23 +5,13 @@ use std::cmp; use std::ffi::OsString; -use std::fs::File; -use std::io::Read; -use std::io::Seek; use std::io::{self, Write}; use std::process; -use uucore::display::Quotable; -use zip::ZipArchive; - use clap::Command; - use coreutils::validation; const VERSION: &str = env!("CARGO_PKG_VERSION"); -const COMPLETION: &str = "completion"; -const MANPAGE: &str = "manpage"; - include!(concat!(env!("OUT_DIR"), "/uutils_map.rs")); fn usage(utils: &UtilityMap, name: &str) { @@ -30,10 +20,6 @@ fn usage(utils: &UtilityMap, name: &str) { println!(" {name} --list"); println!(); println!("Functions:"); - println!(" {COMPLETION}",); - println!(" {}", get_completion_args(utils).render_usage()); - println!(" '{MANPAGE}'",); - println!(" {}", get_manpage_args(utils).render_usage()); println!(" '' [arguments...]"); println!(); println!("Options:"); diff --git a/src/uu/rm/benchmark.sh b/src/uu/rm/benchmark.sh index 93d4d0b5466..60d5ff1fcd3 100755 --- a/src/uu/rm/benchmark.sh +++ b/src/uu/rm/benchmark.sh @@ -3,6 +3,6 @@ # Exit on any failures set +x -cargo build --no-default-features --features rm coreutils --release +cargo build --no-default-features --features rm --release test_dir="$1" hyperfine --prepare "cp -r $test_dir tmp_d" "rm -rf tmp_d" "target/release/coreutils rm -rf tmp_d" From 86d2bead23a1af75040557d34b139cd353a9aa6e Mon Sep 17 00:00:00 2001 From: n4n5 Date: Wed, 22 Oct 2025 17:36:12 -0600 Subject: [PATCH 20/35] fix order --- src/bin/coreutils.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bin/coreutils.rs b/src/bin/coreutils.rs index d113cb8160a..9ffb88b496c 100644 --- a/src/bin/coreutils.rs +++ b/src/bin/coreutils.rs @@ -3,12 +3,12 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. +use clap::Command; +use coreutils::validation; use std::cmp; use std::ffi::OsString; use std::io::{self, Write}; use std::process; -use clap::Command; -use coreutils::validation; const VERSION: &str = env!("CARGO_PKG_VERSION"); From f290ab3ea3803352abbd5737aee672ee8851ed2d Mon Sep 17 00:00:00 2001 From: n4n5 Date: Wed, 22 Oct 2025 17:36:47 -0600 Subject: [PATCH 21/35] typo --- src/uu/rm/BENCHMARKING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uu/rm/BENCHMARKING.md b/src/uu/rm/BENCHMARKING.md index 507906d497e..0118b093c45 100644 --- a/src/uu/rm/BENCHMARKING.md +++ b/src/uu/rm/BENCHMARKING.md @@ -26,7 +26,7 @@ Here is a `bash` script for doing this comparison: #!/bin/bash cargo build --no-default-features --features rm --release test_dir="$1" -hyperfine --prepare "cp -r $test_dir tmp_d" "rm -rf tmp_d" "target/release/coreutils rm -rf tmp_d" +hyperfine --prepare "cp -r $test_dir tmp_d" "rm -rf tmp_d" "target/release/coreutils rm -rf tmp_d" ``` ## Checking system call count From aa31b8a85ba7ad000184742caec5ef9aa84e1673 Mon Sep 17 00:00:00 2001 From: n4n5 Date: Wed, 22 Oct 2025 17:37:16 -0600 Subject: [PATCH 22/35] revert --- .github/workflows/CICD.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index 03bc0ca4cb0..a19084b132b 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -1241,8 +1241,8 @@ jobs: CARGO_FEATURES_OPTION='--features=${{ matrix.job.features }}' ; for f in $(util/show-utils.sh ${CARGO_FEATURES_OPTION}) do - echo "Running tests with --features="$f coreutils" --no-default-features" - cargo test --features="$f coreutils" --no-default-features + echo "Running tests with --features="$f" --no-default-features" + cargo test --features="$f" --no-default-features done test_selinux: From 58ba88623547fa85ed805940b1005967401fb599 Mon Sep 17 00:00:00 2001 From: n4n5 Date: Wed, 22 Oct 2025 17:37:45 -0600 Subject: [PATCH 23/35] revert --- .github/workflows/CICD.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index a19084b132b..7c62cc0cbdb 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -1241,8 +1241,8 @@ jobs: CARGO_FEATURES_OPTION='--features=${{ matrix.job.features }}' ; for f in $(util/show-utils.sh ${CARGO_FEATURES_OPTION}) do - echo "Running tests with --features="$f" --no-default-features" - cargo test --features="$f" --no-default-features + echo "Running tests with --features=$f and --no-default-features" + cargo test --features=$f --no-default-features done test_selinux: From 0c830b1a0fe229b24107a70c5d132537065f3307 Mon Sep 17 00:00:00 2001 From: n4n5 Date: Wed, 22 Oct 2025 17:38:24 -0600 Subject: [PATCH 24/35] revert --- src/uu/rm/BENCHMARKING.md | 2 +- src/uu/rm/benchmark.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/uu/rm/BENCHMARKING.md b/src/uu/rm/BENCHMARKING.md index 0118b093c45..48949b6889f 100644 --- a/src/uu/rm/BENCHMARKING.md +++ b/src/uu/rm/BENCHMARKING.md @@ -26,7 +26,7 @@ Here is a `bash` script for doing this comparison: #!/bin/bash cargo build --no-default-features --features rm --release test_dir="$1" -hyperfine --prepare "cp -r $test_dir tmp_d" "rm -rf tmp_d" "target/release/coreutils rm -rf tmp_d" +hyperfine --prepare "cp -r $test_dir tmp_d" "rm -rf tmp_d" "target/release/coreutils rm -rf tmp_d" ``` ## Checking system call count diff --git a/src/uu/rm/benchmark.sh b/src/uu/rm/benchmark.sh index 60d5ff1fcd3..ce532a2295c 100755 --- a/src/uu/rm/benchmark.sh +++ b/src/uu/rm/benchmark.sh @@ -5,4 +5,4 @@ set +x cargo build --no-default-features --features rm --release test_dir="$1" -hyperfine --prepare "cp -r $test_dir tmp_d" "rm -rf tmp_d" "target/release/coreutils rm -rf tmp_d" +hyperfine --prepare "cp -r $test_dir tmp_d" "rm -rf tmp_d" "target/release/coreutils rm -rf tmp_d" From ae311582ae0a1e3dcdb6d329d79d07c31cf23224 Mon Sep 17 00:00:00 2001 From: n4n5 Date: Wed, 22 Oct 2025 17:46:09 -0600 Subject: [PATCH 25/35] fix --- src/bin/coreutils.rs | 9 ++++++--- src/bin/uudoc.rs | 7 +++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/bin/coreutils.rs b/src/bin/coreutils.rs index 9ffb88b496c..6a9141936f3 100644 --- a/src/bin/coreutils.rs +++ b/src/bin/coreutils.rs @@ -19,9 +19,12 @@ fn usage(utils: &UtilityMap, name: &str) { println!("Usage: {name} [function [arguments...]]"); println!(" {name} --list"); println!(); - println!("Functions:"); - println!(" '' [arguments...]"); - println!(); + #[cfg(feature = "feat_common_core")] + { + println!("Functions:"); + println!(" '' [arguments...]"); + println!(); + } println!("Options:"); println!(" --list lists all defined functions, one per row\n"); println!("Currently defined functions:\n"); diff --git a/src/bin/uudoc.rs b/src/bin/uudoc.rs index 4d73d28d6e0..567536478f7 100644 --- a/src/bin/uudoc.rs +++ b/src/bin/uudoc.rs @@ -553,7 +553,14 @@ fn get_zip_content(archive: &mut ZipArchive, name: &str) -> Op Some(s) } +/// Extract examples for tldr.zip. The file docs/tldr.zip must exists +/// +/// ```sh +/// curl https://tldr.sh/assets/tldr.zip -o docs/tldr.zip +/// ``` +/// /// # Errors +/// /// Returns an error if the tldr.zip file cannot be opened or read fn get_zip_examples(name: &str) -> io::Result { fn get_zip_content(archive: &mut ZipArchive, name: &str) -> Option { From 14e7635c22f604b290bce5589426e84ec4c04ad6 Mon Sep 17 00:00:00 2001 From: n4n5 Date: Wed, 22 Oct 2025 17:48:31 -0600 Subject: [PATCH 26/35] fix typo --- src/uu/rm/BENCHMARKING.md | 2 +- src/uu/rm/benchmark.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/uu/rm/BENCHMARKING.md b/src/uu/rm/BENCHMARKING.md index 48949b6889f..507906d497e 100644 --- a/src/uu/rm/BENCHMARKING.md +++ b/src/uu/rm/BENCHMARKING.md @@ -26,7 +26,7 @@ Here is a `bash` script for doing this comparison: #!/bin/bash cargo build --no-default-features --features rm --release test_dir="$1" -hyperfine --prepare "cp -r $test_dir tmp_d" "rm -rf tmp_d" "target/release/coreutils rm -rf tmp_d" +hyperfine --prepare "cp -r $test_dir tmp_d" "rm -rf tmp_d" "target/release/coreutils rm -rf tmp_d" ``` ## Checking system call count diff --git a/src/uu/rm/benchmark.sh b/src/uu/rm/benchmark.sh index ce532a2295c..5feaea4e8f1 100755 --- a/src/uu/rm/benchmark.sh +++ b/src/uu/rm/benchmark.sh @@ -5,4 +5,4 @@ set +x cargo build --no-default-features --features rm --release test_dir="$1" -hyperfine --prepare "cp -r $test_dir tmp_d" "rm -rf tmp_d" "target/release/coreutils rm -rf tmp_d" +hyperfine --prepare "cp -r $test_dir tmp_d" "rm -rf tmp_d" "target/release/coreutils rm -rf tmp_d" From 7eebb4fa9ac448d364a0315ac1cdf84324c19cc9 Mon Sep 17 00:00:00 2001 From: n4n5 Date: Wed, 22 Oct 2025 17:57:36 -0600 Subject: [PATCH 27/35] cargo clippy --- src/bin/uudoc.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/bin/uudoc.rs b/src/bin/uudoc.rs index 567536478f7..ca9669c2df9 100644 --- a/src/bin/uudoc.rs +++ b/src/bin/uudoc.rs @@ -576,16 +576,16 @@ fn get_zip_examples(name: &str) -> io::Result { Err(e) => { return Err(io::Error::new( io::ErrorKind::NotFound, - format!("Error reading tldr.zip: {}", e), + format!("Error reading tldr.zip: {e}"), )); } }; let content = if let Some(f) = - get_zip_content(&mut tldr_zip, &format!("pages/common/{}.md", name)) + get_zip_content(&mut tldr_zip, &format!("pages/common/{name}.md")) { f - } else if let Some(f) = get_zip_content(&mut tldr_zip, &format!("pages/linux/{}.md", name)) { + } else if let Some(f) = get_zip_content(&mut tldr_zip, &format!("pages/linux/{name}.md")) { f } else { return Err(io::Error::new( @@ -613,7 +613,7 @@ fn get_zip_examples(name: &str) -> io::Result { w, "> The examples are provided by the [tldr-pages project](https://tldr.sh) under the [CC BY 4.0 License](https://github.com/tldr-pages/tldr/blob/main/LICENSE.md)." )?; - writeln!(w, "\n\n\n")?; + write!(w, "\n")?; writeln!( w, "> Please note that, as uutils is a work in progress, some examples might fail." From f72a66f5c847f59480068f523b0f21a4f01e5b97 Mon Sep 17 00:00:00 2001 From: n4n5 Date: Wed, 22 Oct 2025 17:58:22 -0600 Subject: [PATCH 28/35] fix fmt deps --- Cargo.toml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 28ea3a29dab..a55460a284d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,13 @@ expensive_tests = [] # "test_risky_names" == enable tests that create problematic file names (would make a network share inaccessible to Windows, breaks SVN on Mac OS, etc.) test_risky_names = [] # * only build `uudoc` when `--feature uudoc` is activated -uudoc = ["dep:clap_complete", "dep:clap_mangen", "dep:fluent-syntax", "dep:zip", "feat_Tier1"] +uudoc = [ + "dep:clap_complete", + "dep:clap_mangen", + "dep:fluent-syntax", + "dep:zip", + "feat_Tier1", +] ## features ## Optional feature for stdbuf # "feat_external_libstdbuf" == use an external libstdbuf.so for stdbuf instead of embedding it From 0dec9073ce4dd95b07e37672411f271a136c0e20 Mon Sep 17 00:00:00 2001 From: n4n5 Date: Wed, 22 Oct 2025 18:00:31 -0600 Subject: [PATCH 29/35] fmt --- src/bin/uudoc.rs | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/bin/uudoc.rs b/src/bin/uudoc.rs index ca9669c2df9..368a000568d 100644 --- a/src/bin/uudoc.rs +++ b/src/bin/uudoc.rs @@ -581,18 +581,17 @@ fn get_zip_examples(name: &str) -> io::Result { } }; - let content = if let Some(f) = - get_zip_content(&mut tldr_zip, &format!("pages/common/{name}.md")) - { - f - } else if let Some(f) = get_zip_content(&mut tldr_zip, &format!("pages/linux/{name}.md")) { - f - } else { - return Err(io::Error::new( - io::ErrorKind::NotFound, - "Could not find tldr examples", - )); - }; + let content = + if let Some(f) = get_zip_content(&mut tldr_zip, &format!("pages/common/{name}.md")) { + f + } else if let Some(f) = get_zip_content(&mut tldr_zip, &format!("pages/linux/{name}.md")) { + f + } else { + return Err(io::Error::new( + io::ErrorKind::NotFound, + "Could not find tldr examples", + )); + }; writeln!(w, "Examples")?; writeln!(w)?; From bfd283f822e3ba55d51e6f8a8b89168912093629 Mon Sep 17 00:00:00 2001 From: n4n5 Date: Wed, 22 Oct 2025 18:05:42 -0600 Subject: [PATCH 30/35] clippy again --- src/bin/uudoc.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/uudoc.rs b/src/bin/uudoc.rs index 368a000568d..9ad0bcb1533 100644 --- a/src/bin/uudoc.rs +++ b/src/bin/uudoc.rs @@ -612,7 +612,7 @@ fn get_zip_examples(name: &str) -> io::Result { w, "> The examples are provided by the [tldr-pages project](https://tldr.sh) under the [CC BY 4.0 License](https://github.com/tldr-pages/tldr/blob/main/LICENSE.md)." )?; - write!(w, "\n")?; + writeln!(w)?; writeln!( w, "> Please note that, as uutils is a work in progress, some examples might fail." From 3df5a7cde78c3ef19fdfb2f00694fd40734a0038 Mon Sep 17 00:00:00 2001 From: n4n5 Date: Thu, 23 Oct 2025 07:41:25 -0600 Subject: [PATCH 31/35] fix: use one function --- src/bin/uudoc.rs | 136 ++++++++++++++++++----------------------------- 1 file changed, 52 insertions(+), 84 deletions(-) diff --git a/src/bin/uudoc.rs b/src/bin/uudoc.rs index 9ad0bcb1533..8e466be073e 100644 --- a/src/bin/uudoc.rs +++ b/src/bin/uudoc.rs @@ -61,7 +61,11 @@ fn gen_coreutils_app(util_map: &UtilityMap) -> clap::Command { } /// Generate the manpage for the utility in the first parameter -fn gen_manpage(args: impl Iterator, util_map: &UtilityMap) -> ! { +fn gen_manpage( + tldr: &mut Option>, + args: impl Iterator, + util_map: &UtilityMap, +) -> ! { let all_utilities = validation::get_all_utilities(util_map); let matches = Command::new("manpage") @@ -79,8 +83,10 @@ fn gen_manpage(args: impl Iterator, util_map: &Utility } else { validation::setup_localization_or_exit(utility); let mut cmd = util_map.get(utility).unwrap().1(); - if let Ok(examples) = get_zip_examples(utility) { - cmd = cmd.after_help(examples); + if let Some(zip) = tldr { + if let Ok(examples) = write_zip_examples(zip, utility, false) { + cmd = cmd.after_help(examples); + } } cmd }; @@ -132,13 +138,32 @@ fn gen_completions(args: impl Iterator, util_map: &Uti fn main() -> io::Result<()> { let args: Vec = uucore::args_os_filtered().collect(); + let mut tldr_zip = File::open("docs/tldr.zip") + .ok() + .and_then(|f| ZipArchive::new(f).ok()); + + if tldr_zip.is_none() { + eprintln!( + "Warning: No tldr archive found, so the documentation will not include examples." + ); + eprintln!( + "To include examples in the documentation, download the tldr archive and put it in the docs/ folder." + ); + eprintln!(); + eprintln!(" curl https://tldr.sh/assets/tldr.zip -o docs/tldr.zip"); + eprintln!(); + } // Check for manpage/completion commands first if args.len() > 1 { let command = args.get(1).and_then(|s| s.to_str()).unwrap_or_default(); match command { "manpage" => { let args_iter = args.into_iter().skip(2); - gen_manpage(args_iter, &util_map::>>()); + gen_manpage( + &mut tldr_zip, + args_iter, + &util_map::>>(), + ); } "completion" => { let args_iter = args.into_iter().skip(2); @@ -155,19 +180,6 @@ fn main() -> io::Result<()> { } } } - let mut tldr_zip = File::open("docs/tldr.zip") - .ok() - .and_then(|f| ZipArchive::new(f).ok()); - - if tldr_zip.is_none() { - println!("Warning: No tldr archive found, so the documentation will not include examples."); - println!( - "To include examples in the documentation, download the tldr archive and put it in the docs/ folder." - ); - println!(); - println!(" curl https://tldr.sh/assets/tldr.zip -o docs/tldr.zip"); - println!(); - } let utils = util_map::>>(); match std::fs::create_dir("docs/src/utils/") { @@ -435,44 +447,9 @@ impl MDWriter<'_, '_> { /// Returns an error if the writer fails. fn examples(&mut self) -> io::Result<()> { if let Some(zip) = self.tldr_zip { - let content = if let Some(f) = - get_zip_content(zip, &format!("pages/common/{}.md", self.name)) - { - f - } else if let Some(f) = get_zip_content(zip, &format!("pages/linux/{}.md", self.name)) { - f - } else { - println!( - "Warning: Could not find tldr examples for page '{}'", - self.name - ); - return Ok(()); - }; - - writeln!(self.w, "## Examples")?; - writeln!(self.w)?; - for line in content.lines().skip_while(|l| !l.starts_with('-')) { - if let Some(l) = line.strip_prefix("- ") { - writeln!(self.w, "{l}")?; - } else if line.starts_with('`') { - writeln!(self.w, "```shell\n{}\n```", line.trim_matches('`'))?; - } else if line.is_empty() { - writeln!(self.w)?; - } else { - println!("Not sure what to do with this line:"); - println!("{line}"); - } + if let Ok(examples) = write_zip_examples(zip, self.name, true) { + writeln!(self.w, "{examples}")?; } - writeln!(self.w)?; - writeln!( - self.w, - "> The examples are provided by the [tldr-pages project](https://tldr.sh) under the [CC BY 4.0 License](https://github.com/tldr-pages/tldr/blob/main/LICENSE.md)." - )?; - writeln!(self.w, ">")?; - writeln!( - self.w, - "> Please note that, as uutils is a work in progress, some examples might fail." - )?; } Ok(()) } @@ -562,44 +539,35 @@ fn get_zip_content(archive: &mut ZipArchive, name: &str) -> Op /// # Errors /// /// Returns an error if the tldr.zip file cannot be opened or read -fn get_zip_examples(name: &str) -> io::Result { - fn get_zip_content(archive: &mut ZipArchive, name: &str) -> Option { - let mut s = String::new(); - archive.by_name(name).ok()?.read_to_string(&mut s).unwrap(); - Some(s) - } - +fn write_zip_examples( + archive: &mut ZipArchive, + name: &str, + output_markdown: bool, +) -> io::Result { let mut w = io::BufWriter::new(Vec::new()); - let file = File::open("docs/tldr.zip")?; - let mut tldr_zip = match ZipArchive::new(file) { - Ok(zip) => zip, - Err(e) => { - return Err(io::Error::new( - io::ErrorKind::NotFound, - format!("Error reading tldr.zip: {e}"), - )); - } + let content = if let Some(f) = get_zip_content(archive, &format!("pages/common/{name}.md")) { + f + } else if let Some(f) = get_zip_content(archive, &format!("pages/linux/{name}.md")) { + f + } else { + return Err(io::Error::new( + io::ErrorKind::NotFound, + "Could not find tldr examples", + )); }; - let content = - if let Some(f) = get_zip_content(&mut tldr_zip, &format!("pages/common/{name}.md")) { - f - } else if let Some(f) = get_zip_content(&mut tldr_zip, &format!("pages/linux/{name}.md")) { - f - } else { - return Err(io::Error::new( - io::ErrorKind::NotFound, - "Could not find tldr examples", - )); - }; - + writeln!(w)?; writeln!(w, "Examples")?; writeln!(w)?; for line in content.lines().skip_while(|l| !l.starts_with('-')) { if let Some(l) = line.strip_prefix("- ") { writeln!(w, "{l}")?; } else if line.starts_with('`') { - writeln!(w, "{}", line.trim_matches('`'))?; + if output_markdown { + writeln!(w, "```shell\n{}\n```", line.trim_matches('`'))?; + } else { + writeln!(w, "{}", line.trim_matches('`'))?; + } } else if line.is_empty() { writeln!(w)?; } else { @@ -612,7 +580,7 @@ fn get_zip_examples(name: &str) -> io::Result { w, "> The examples are provided by the [tldr-pages project](https://tldr.sh) under the [CC BY 4.0 License](https://github.com/tldr-pages/tldr/blob/main/LICENSE.md)." )?; - writeln!(w)?; + writeln!(w, ">")?; writeln!( w, "> Please note that, as uutils is a work in progress, some examples might fail." From bcf2685b287efe77c71ae907fcad1b8ecf008328 Mon Sep 17 00:00:00 2001 From: n4n5 Date: Thu, 6 Nov 2025 20:26:01 -0700 Subject: [PATCH 32/35] cleaning by removing unwrap --- src/bin/uudoc.rs | 42 ++++++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/src/bin/uudoc.rs b/src/bin/uudoc.rs index 8e466be073e..fa01782af82 100644 --- a/src/bin/uudoc.rs +++ b/src/bin/uudoc.rs @@ -544,7 +544,6 @@ fn write_zip_examples( name: &str, output_markdown: bool, ) -> io::Result { - let mut w = io::BufWriter::new(Vec::new()); let content = if let Some(f) = get_zip_content(archive, &format!("pages/common/{name}.md")) { f } else if let Some(f) = get_zip_content(archive, &format!("pages/linux/{name}.md")) { @@ -552,38 +551,53 @@ fn write_zip_examples( } else { return Err(io::Error::new( io::ErrorKind::NotFound, - "Could not find tldr examples", + format!("Could not find tldr examples for {name}"), )); }; - writeln!(w)?; - writeln!(w, "Examples")?; - writeln!(w)?; + match format_examples(content, output_markdown) { + Err(e) => Err(std::io::Error::new( + std::io::ErrorKind::Other, + format!( + "Failed to format the tldr examples of {name}: {}", + e.to_string() + ), + )), + Ok(s) => Ok(s), + } +} + +fn format_examples(content: String, output_markdown: bool) -> Result { + use std::fmt::Write; + let mut s = String::new(); + writeln!(s)?; + writeln!(s, "Examples")?; + writeln!(s)?; for line in content.lines().skip_while(|l| !l.starts_with('-')) { if let Some(l) = line.strip_prefix("- ") { - writeln!(w, "{l}")?; + writeln!(s, "{l}")?; } else if line.starts_with('`') { if output_markdown { - writeln!(w, "```shell\n{}\n```", line.trim_matches('`'))?; + writeln!(s, "```shell\n{}\n```", line.trim_matches('`'))?; } else { - writeln!(w, "{}", line.trim_matches('`'))?; + writeln!(s, "{}", line.trim_matches('`'))?; } } else if line.is_empty() { - writeln!(w)?; + writeln!(s)?; } else { // println!("Not sure what to do with this line:"); // println!("{line}"); } } - writeln!(w)?; + writeln!(s)?; writeln!( - w, + s, "> The examples are provided by the [tldr-pages project](https://tldr.sh) under the [CC BY 4.0 License](https://github.com/tldr-pages/tldr/blob/main/LICENSE.md)." )?; - writeln!(w, ">")?; + writeln!(s, ">")?; writeln!( - w, + s, "> Please note that, as uutils is a work in progress, some examples might fail." )?; - Ok(String::from_utf8(w.into_inner().unwrap()).unwrap()) + Ok(s) } From 0c77c1eb9616cb488b713b72a60b3d094b84c73c Mon Sep 17 00:00:00 2001 From: n4n5 Date: Thu, 6 Nov 2025 20:26:39 -0700 Subject: [PATCH 33/35] fix cargo.toml --- Cargo.toml | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e940fded013..dbcdf98f942 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,13 +31,7 @@ expensive_tests = [] # "test_risky_names" == enable tests that create problematic file names (would make a network share inaccessible to Windows, breaks SVN on Mac OS, etc.) test_risky_names = [] # * only build `uudoc` when `--feature uudoc` is activated -uudoc = [ - "dep:clap_complete", - "dep:clap_mangen", - "dep:fluent-syntax", - "dep:zip", - "feat_Tier1", -] +uudoc = ["dep:clap_complete", "dep:clap_mangen", "dep:fluent-syntax", "dep:zip"] ## features ## Optional feature for stdbuf # "feat_external_libstdbuf" == use an external libstdbuf.so for stdbuf instead of embedding it From 9ee347691b22f37ac9d010e7c6d08be287c2d4c6 Mon Sep 17 00:00:00 2001 From: n4n5 Date: Thu, 6 Nov 2025 21:09:18 -0700 Subject: [PATCH 34/35] fix tests --- src/bin/uudoc.rs | 30 ++++++++++++++++++------------ tests/uudoc/mod.rs | 15 ++++++--------- 2 files changed, 24 insertions(+), 21 deletions(-) diff --git a/src/bin/uudoc.rs b/src/bin/uudoc.rs index fa01782af82..15e0bc32def 100644 --- a/src/bin/uudoc.rs +++ b/src/bin/uudoc.rs @@ -132,6 +132,17 @@ fn gen_completions(args: impl Iterator, util_map: &Uti process::exit(0); } +/// print tldr error +fn print_tldr_error() { + eprintln!("Warning: No tldr archive found, so the documentation will not include examples."); + eprintln!( + "To include examples in the documentation, download the tldr archive and put it in the docs/ folder." + ); + eprintln!(); + eprintln!(" curl https://tldr.sh/assets/tldr.zip -o docs/tldr.zip"); + eprintln!(); +} + /// # Errors /// Returns an error if the writer fails. #[allow(clippy::too_many_lines)] @@ -142,23 +153,15 @@ fn main() -> io::Result<()> { .ok() .and_then(|f| ZipArchive::new(f).ok()); - if tldr_zip.is_none() { - eprintln!( - "Warning: No tldr archive found, so the documentation will not include examples." - ); - eprintln!( - "To include examples in the documentation, download the tldr archive and put it in the docs/ folder." - ); - eprintln!(); - eprintln!(" curl https://tldr.sh/assets/tldr.zip -o docs/tldr.zip"); - eprintln!(); - } // Check for manpage/completion commands first if args.len() > 1 { let command = args.get(1).and_then(|s| s.to_str()).unwrap_or_default(); match command { "manpage" => { let args_iter = args.into_iter().skip(2); + if tldr_zip.is_none() { + print_tldr_error(); + } gen_manpage( &mut tldr_zip, args_iter, @@ -180,7 +183,9 @@ fn main() -> io::Result<()> { } } } - + if tldr_zip.is_none() { + print_tldr_error(); + } let utils = util_map::>>(); match std::fs::create_dir("docs/src/utils/") { Err(e) if e.kind() == std::io::ErrorKind::AlreadyExists => Ok(()), @@ -567,6 +572,7 @@ fn write_zip_examples( } } +/// Format examples using std::fmt::Write fn format_examples(content: String, output_markdown: bool) -> Result { use std::fmt::Write; let mut s = String::new(); diff --git a/tests/uudoc/mod.rs b/tests/uudoc/mod.rs index 33cb27a46b7..fc64417e47a 100644 --- a/tests/uudoc/mod.rs +++ b/tests/uudoc/mod.rs @@ -29,9 +29,8 @@ fn test_manpage_generation() { output.status ); assert!( - output.stderr.is_empty(), - "stderr should be empty but got: {}", - String::from_utf8_lossy(&output.stderr) + String::from_utf8_lossy(&output.stderr).contains("Warning: No tldr archive found"), + "stderr should contains tldr alert", ); let output_str = String::from_utf8_lossy(&output.stdout); @@ -53,9 +52,8 @@ fn test_manpage_coreutils() { output.status ); assert!( - output.stderr.is_empty(), - "stderr should be empty but got: {}", - String::from_utf8_lossy(&output.stderr) + String::from_utf8_lossy(&output.stderr).contains("Warning: No tldr archive found"), + "stderr should contains tldr alert", ); let output_str = String::from_utf8_lossy(&output.stdout); @@ -124,9 +122,8 @@ fn test_manpage_base64() { output.status ); assert!( - output.stderr.is_empty(), - "stderr should be empty but got: {}", - String::from_utf8_lossy(&output.stderr) + String::from_utf8_lossy(&output.stderr).contains("Warning: No tldr archive found"), + "stderr should contains tldr alert", ); let output_str = String::from_utf8_lossy(&output.stdout); From 453e6a355634cea13b3793604ec71dd39359c313 Mon Sep 17 00:00:00 2001 From: n4n5 Date: Thu, 6 Nov 2025 21:11:08 -0700 Subject: [PATCH 35/35] clippy --- src/bin/uudoc.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/bin/uudoc.rs b/src/bin/uudoc.rs index 15e0bc32def..a454555b3fe 100644 --- a/src/bin/uudoc.rs +++ b/src/bin/uudoc.rs @@ -561,13 +561,9 @@ fn write_zip_examples( }; match format_examples(content, output_markdown) { - Err(e) => Err(std::io::Error::new( - std::io::ErrorKind::Other, - format!( - "Failed to format the tldr examples of {name}: {}", - e.to_string() - ), - )), + Err(e) => Err(std::io::Error::other(format!( + "Failed to format the tldr examples of {name}: {e}" + ))), Ok(s) => Ok(s), } }