match the coreutils style
This commit is contained in:
parent
b16e835b9b
commit
1b4b449d49
101
build.rs
Normal file
101
build.rs
Normal file
@ -0,0 +1,101 @@
|
||||
// This file is part of the uutils coreutils package.
|
||||
//
|
||||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
|
||||
// spell-checker:ignore (vars) krate
|
||||
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
|
||||
pub fn main() {
|
||||
if let Ok(profile) = env::var("PROFILE") {
|
||||
println!("cargo:rustc-cfg=build={profile:?}");
|
||||
}
|
||||
|
||||
const ENV_FEATURE_PREFIX: &str = "CARGO_FEATURE_";
|
||||
const FEATURE_PREFIX: &str = "feat_";
|
||||
const OVERRIDE_PREFIX: &str = "uu_";
|
||||
|
||||
let out_dir = env::var("OUT_DIR").unwrap();
|
||||
|
||||
let mut crates = Vec::new();
|
||||
for (key, val) in env::vars() {
|
||||
if val == "1" && key.starts_with(ENV_FEATURE_PREFIX) {
|
||||
let krate = key[ENV_FEATURE_PREFIX.len()..].to_lowercase();
|
||||
// Allow this as we have a bunch of info in the comments
|
||||
#[allow(clippy::match_same_arms)]
|
||||
match krate.as_ref() {
|
||||
"default" | "macos" | "unix" | "windows" | "selinux" | "zip" => continue, // common/standard feature names
|
||||
"nightly" | "test_unimplemented" => continue, // crate-local custom features
|
||||
"uudoc" => continue, // is not a utility
|
||||
"test" => continue, // over-ridden with 'uu_test' to avoid collision with rust core crate 'test'
|
||||
s if s.starts_with(FEATURE_PREFIX) => continue, // crate feature sets
|
||||
_ => {} // util feature name
|
||||
}
|
||||
crates.push(krate);
|
||||
}
|
||||
}
|
||||
crates.sort();
|
||||
|
||||
let mut mf = File::create(Path::new(&out_dir).join("uutils_map.rs")).unwrap();
|
||||
|
||||
mf.write_all(
|
||||
"type UtilityMap<T> = phf::OrderedMap<&'static str, (fn(T) -> i32, fn() -> Command)>;\n\
|
||||
\n\
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn util_map<T: uucore::Args>() -> UtilityMap<T> {\n"
|
||||
.as_bytes(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let mut phf_map = phf_codegen::OrderedMap::<&str>::new();
|
||||
for krate in &crates {
|
||||
let map_value = format!("({krate}::uumain, {krate}::uu_app)");
|
||||
match krate.as_ref() {
|
||||
// 'test' is named uu_test to avoid collision with rust core crate 'test'.
|
||||
// It can also be invoked by name '[' for the '[ expr ] syntax'.
|
||||
"uu_test" => {
|
||||
phf_map.entry("test", &map_value);
|
||||
phf_map.entry("[", &map_value);
|
||||
}
|
||||
k if k.starts_with(OVERRIDE_PREFIX) => {
|
||||
phf_map.entry(&k[OVERRIDE_PREFIX.len()..], &map_value);
|
||||
}
|
||||
"false" | "true" => {
|
||||
phf_map.entry(krate, &format!("(r#{krate}::uumain, r#{krate}::uu_app)"));
|
||||
}
|
||||
"hashsum" => {
|
||||
phf_map.entry(krate, &format!("({krate}::uumain, {krate}::uu_app_custom)"));
|
||||
|
||||
let map_value = format!("({krate}::uumain, {krate}::uu_app_common)");
|
||||
let map_value_bits = format!("({krate}::uumain, {krate}::uu_app_bits)");
|
||||
let map_value_b3sum = format!("({krate}::uumain, {krate}::uu_app_b3sum)");
|
||||
phf_map.entry("md5sum", &map_value);
|
||||
phf_map.entry("sha1sum", &map_value);
|
||||
phf_map.entry("sha224sum", &map_value);
|
||||
phf_map.entry("sha256sum", &map_value);
|
||||
phf_map.entry("sha384sum", &map_value);
|
||||
phf_map.entry("sha512sum", &map_value);
|
||||
phf_map.entry("sha3sum", &map_value_bits);
|
||||
phf_map.entry("sha3-224sum", &map_value);
|
||||
phf_map.entry("sha3-256sum", &map_value);
|
||||
phf_map.entry("sha3-384sum", &map_value);
|
||||
phf_map.entry("sha3-512sum", &map_value);
|
||||
phf_map.entry("shake128sum", &map_value_bits);
|
||||
phf_map.entry("shake256sum", &map_value_bits);
|
||||
phf_map.entry("b2sum", &map_value);
|
||||
phf_map.entry("b3sum", &map_value_b3sum);
|
||||
}
|
||||
_ => {
|
||||
phf_map.entry(krate, &map_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
write!(mf, "{}", phf_map.build()).unwrap();
|
||||
mf.write_all(b"\n}\n").unwrap();
|
||||
|
||||
mf.flush().unwrap();
|
||||
}
|
224
src/bin/util-linux.rs
Normal file
224
src/bin/util-linux.rs
Normal file
@ -0,0 +1,224 @@
|
||||
// This file is part of the uutils coreutils package.
|
||||
//
|
||||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
|
||||
// spell-checker:ignore manpages mangen
|
||||
|
||||
use clap::{Arg, Command};
|
||||
use clap_complete::Shell;
|
||||
use std::cmp;
|
||||
use std::ffi::OsStr;
|
||||
use std::ffi::OsString;
|
||||
use std::io::{self, Write};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process;
|
||||
use uucore::display::Quotable;
|
||||
|
||||
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/uutils_map.rs"));
|
||||
|
||||
fn usage<T>(utils: &UtilityMap<T>, name: &str) {
|
||||
println!("{name} {VERSION} (multi-call binary)\n");
|
||||
println!("Usage: {name} [function [arguments...]]\n");
|
||||
println!("Currently defined functions:\n");
|
||||
#[allow(clippy::map_clone)]
|
||||
let mut utils: Vec<&str> = utils.keys().map(|&s| s).collect();
|
||||
utils.sort_unstable();
|
||||
let display_list = utils.join(", ");
|
||||
let width = cmp::min(textwrap::termwidth(), 100) - 4 * 2; // (opinion/heuristic) max 100 chars wide with 4 character side indentions
|
||||
println!(
|
||||
"{}",
|
||||
textwrap::indent(&textwrap::fill(&display_list, width), " ")
|
||||
);
|
||||
}
|
||||
|
||||
fn binary_path(args: &mut impl Iterator<Item = OsString>) -> PathBuf {
|
||||
match args.next() {
|
||||
Some(ref s) if !s.is_empty() => PathBuf::from(s),
|
||||
_ => std::env::current_exe().unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
fn name(binary_path: &Path) -> Option<&str> {
|
||||
binary_path.file_stem()?.to_str()
|
||||
}
|
||||
|
||||
#[allow(clippy::cognitive_complexity)]
|
||||
fn main() {
|
||||
uucore::panic::mute_sigpipe_panic();
|
||||
|
||||
let utils = util_map();
|
||||
let mut args = uucore::args_os();
|
||||
|
||||
let binary = binary_path(&mut args);
|
||||
let binary_as_util = name(&binary).unwrap_or_else(|| {
|
||||
usage(&utils, "<unknown binary name>");
|
||||
process::exit(0);
|
||||
});
|
||||
|
||||
// binary name equals util name?
|
||||
if let Some(&(uumain, _)) = utils.get(binary_as_util) {
|
||||
process::exit(uumain((vec![binary.into()].into_iter()).chain(args)));
|
||||
}
|
||||
|
||||
// binary name equals prefixed util name?
|
||||
// * prefix/stem may be any string ending in a non-alphanumeric character
|
||||
let util_name = if let Some(util) = utils.keys().find(|util| {
|
||||
binary_as_util.ends_with(*util)
|
||||
&& !binary_as_util[..binary_as_util.len() - (*util).len()]
|
||||
.ends_with(char::is_alphanumeric)
|
||||
}) {
|
||||
// prefixed util => replace 0th (aka, executable name) argument
|
||||
Some(OsString::from(*util))
|
||||
} else {
|
||||
// unmatched binary name => regard as multi-binary container and advance argument list
|
||||
uucore::set_utility_is_second_arg();
|
||||
args.next()
|
||||
};
|
||||
|
||||
// 0th argument equals util name?
|
||||
if let Some(util_os) = util_name {
|
||||
fn not_found(util: &OsStr) -> ! {
|
||||
println!("{}: function/utility not found", util.maybe_quote());
|
||||
process::exit(1);
|
||||
}
|
||||
|
||||
let util = match util_os.to_str() {
|
||||
Some(util) => util,
|
||||
None => not_found(&util_os),
|
||||
};
|
||||
|
||||
if util == "completion" {
|
||||
gen_completions(args, &utils);
|
||||
}
|
||||
|
||||
if util == "manpage" {
|
||||
gen_manpage(args, &utils);
|
||||
}
|
||||
|
||||
match utils.get(util) {
|
||||
Some(&(uumain, _)) => {
|
||||
process::exit(uumain((vec![util_os].into_iter()).chain(args)));
|
||||
}
|
||||
None => {
|
||||
if util == "--help" || util == "-h" {
|
||||
// see if they want help on a specific util
|
||||
if let Some(util_os) = args.next() {
|
||||
let util = match util_os.to_str() {
|
||||
Some(util) => util,
|
||||
None => not_found(&util_os),
|
||||
};
|
||||
|
||||
match utils.get(util) {
|
||||
Some(&(uumain, _)) => {
|
||||
let code = uumain(
|
||||
(vec![util_os, OsString::from("--help")].into_iter())
|
||||
.chain(args),
|
||||
);
|
||||
io::stdout().flush().expect("could not flush stdout");
|
||||
process::exit(code);
|
||||
}
|
||||
None => not_found(&util_os),
|
||||
}
|
||||
}
|
||||
usage(&utils, binary_as_util);
|
||||
process::exit(0);
|
||||
} else {
|
||||
not_found(&util_os);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// no arguments provided
|
||||
usage(&utils, binary_as_util);
|
||||
process::exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
/// Prints completions for the utility in the first parameter for the shell in the second parameter to stdout
|
||||
fn gen_completions<T: uucore::Args>(
|
||||
args: impl Iterator<Item = OsString>,
|
||||
util_map: &UtilityMap<T>,
|
||||
) -> ! {
|
||||
let all_utilities: Vec<_> = std::iter::once("coreutils")
|
||||
.chain(util_map.keys().copied())
|
||||
.collect();
|
||||
|
||||
let matches = Command::new("completion")
|
||||
.about("Prints completions to stdout")
|
||||
.arg(
|
||||
Arg::new("utility")
|
||||
.value_parser(clap::builder::PossibleValuesParser::new(all_utilities))
|
||||
.required(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("shell")
|
||||
.value_parser(clap::builder::EnumValueParser::<Shell>::new())
|
||||
.required(true),
|
||||
)
|
||||
.get_matches_from(std::iter::once(OsString::from("completion")).chain(args));
|
||||
|
||||
let utility = matches.get_one::<String>("utility").unwrap();
|
||||
let shell = *matches.get_one::<Shell>("shell").unwrap();
|
||||
|
||||
let mut command = if utility == "coreutils" {
|
||||
gen_coreutils_app(util_map)
|
||||
} else {
|
||||
util_map.get(utility).unwrap().1()
|
||||
};
|
||||
let bin_name = std::env::var("PROG_PREFIX").unwrap_or_default() + utility;
|
||||
|
||||
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
|
||||
fn gen_manpage<T: uucore::Args>(
|
||||
args: impl Iterator<Item = OsString>,
|
||||
util_map: &UtilityMap<T>,
|
||||
) -> ! {
|
||||
let all_utilities: Vec<_> = std::iter::once("coreutils")
|
||||
.chain(util_map.keys().copied())
|
||||
.collect();
|
||||
|
||||
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));
|
||||
|
||||
let utility = matches.get_one::<String>("utility").unwrap();
|
||||
|
||||
let command = if utility == "coreutils" {
|
||||
gen_coreutils_app(util_map)
|
||||
} else {
|
||||
util_map.get(utility).unwrap().1()
|
||||
};
|
||||
|
||||
let man = clap_mangen::Man::new(command);
|
||||
man.render(&mut io::stdout())
|
||||
.expect("Man page generation failed");
|
||||
io::stdout().flush().unwrap();
|
||||
process::exit(0);
|
||||
}
|
||||
|
||||
fn gen_coreutils_app<T: uucore::Args>(util_map: &UtilityMap<T>) -> Command {
|
||||
let mut command = Command::new("coreutils");
|
||||
for (name, (_, sub_app)) in util_map {
|
||||
// Recreate a small subcommand with only the relevant info
|
||||
// (name & short description)
|
||||
let about = sub_app()
|
||||
.get_about()
|
||||
.expect("Could not get the 'about'")
|
||||
.to_string();
|
||||
let sub_app = Command::new(name).about(about);
|
||||
command = command.subcommand(sub_app);
|
||||
}
|
||||
command
|
||||
}
|
361
src/bin/uudoc.rs
Normal file
361
src/bin/uudoc.rs
Normal file
@ -0,0 +1,361 @@
|
||||
// This file is part of the uutils coreutils package.
|
||||
//
|
||||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
// spell-checker:ignore tldr uuhelp
|
||||
|
||||
use clap::Command;
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::OsString;
|
||||
use std::fs::File;
|
||||
use std::io::{self, Read, Seek, Write};
|
||||
use zip::ZipArchive;
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/uutils_map.rs"));
|
||||
|
||||
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::<Box<dyn Iterator<Item = OsString>>>();
|
||||
match std::fs::create_dir("docs/src/utils/") {
|
||||
Err(e) if e.kind() == std::io::ErrorKind::AlreadyExists => Ok(()),
|
||||
x => x,
|
||||
}?;
|
||||
|
||||
println!("Writing initial info to SUMMARY.md");
|
||||
let mut summary = File::create("docs/src/SUMMARY.md")?;
|
||||
|
||||
let _ = write!(
|
||||
summary,
|
||||
"# Summary\n\
|
||||
\n\
|
||||
[Introduction](index.md)\n\
|
||||
* [Installation](installation.md)\n\
|
||||
* [Build from source](build.md)\n\
|
||||
* [Platform support](platforms.md)\n\
|
||||
* [Contributing](contributing.md)\n\
|
||||
* [GNU test coverage](test_coverage.md)\n\
|
||||
* [Extensions](extensions.md)\n\
|
||||
\n\
|
||||
# Reference\n\
|
||||
* [Multi-call binary](multicall.md)\n",
|
||||
);
|
||||
|
||||
println!("Gathering utils per platform");
|
||||
let utils_per_platform = {
|
||||
let mut map = HashMap::new();
|
||||
for platform in ["unix", "macos", "windows", "unix_android"] {
|
||||
let platform_utils: Vec<String> = String::from_utf8(
|
||||
std::process::Command::new("./util/show-utils.sh")
|
||||
.arg(format!("--features=feat_os_{}", platform))
|
||||
.output()?
|
||||
.stdout,
|
||||
)
|
||||
.unwrap()
|
||||
.trim()
|
||||
.split(' ')
|
||||
.map(ToString::to_string)
|
||||
.collect();
|
||||
map.insert(platform, platform_utils);
|
||||
}
|
||||
|
||||
// Linux is a special case because it can support selinux
|
||||
let platform_utils: Vec<String> = String::from_utf8(
|
||||
std::process::Command::new("./util/show-utils.sh")
|
||||
.arg("--features=feat_os_unix feat_selinux")
|
||||
.output()?
|
||||
.stdout,
|
||||
)
|
||||
.unwrap()
|
||||
.trim()
|
||||
.split(' ')
|
||||
.map(ToString::to_string)
|
||||
.collect();
|
||||
map.insert("linux", platform_utils);
|
||||
|
||||
map
|
||||
};
|
||||
|
||||
let mut utils = utils.entries().collect::<Vec<_>>();
|
||||
utils.sort();
|
||||
|
||||
println!("Writing util per platform table");
|
||||
{
|
||||
let mut platform_table_file = File::create("docs/src/platform_table.md").unwrap();
|
||||
|
||||
// sum, cksum, b2sum, etc. are all available on all platforms, but not in the data structure
|
||||
// otherwise, we check the map for the util name.
|
||||
let check_supported = |name: &str, platform: &str| {
|
||||
if name.ends_with("sum") || utils_per_platform[platform].iter().any(|u| u == name) {
|
||||
"✓"
|
||||
} else {
|
||||
" "
|
||||
}
|
||||
};
|
||||
writeln!(
|
||||
platform_table_file,
|
||||
"| util | Linux | macOS | Windows | FreeBSD | Android |\n\
|
||||
| ---------------- | ----- | ----- | ------- | ------- | ------- |"
|
||||
)?;
|
||||
for (&name, _) in &utils {
|
||||
if name == "[" {
|
||||
continue;
|
||||
}
|
||||
// The alignment is not necessary, but makes the output a bit more
|
||||
// pretty when viewed as plain markdown.
|
||||
writeln!(
|
||||
platform_table_file,
|
||||
"| {:<16} | {:<5} | {:<5} | {:<7} | {:<7} | {:<7} |",
|
||||
format!("**{name}**"),
|
||||
check_supported(name, "linux"),
|
||||
check_supported(name, "macos"),
|
||||
check_supported(name, "windows"),
|
||||
check_supported(name, "unix"),
|
||||
check_supported(name, "unix_android"),
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
println!("Writing to utils");
|
||||
for (&name, (_, command)) in utils {
|
||||
if name == "[" {
|
||||
continue;
|
||||
}
|
||||
let p = format!("docs/src/utils/{}.md", name);
|
||||
|
||||
let markdown = File::open(format!("src/uu/{name}/{name}.md"))
|
||||
.and_then(|mut f: File| {
|
||||
let mut s = String::new();
|
||||
f.read_to_string(&mut s)?;
|
||||
Ok(s)
|
||||
})
|
||||
.ok();
|
||||
|
||||
if let Ok(f) = File::create(&p) {
|
||||
MDWriter {
|
||||
w: Box::new(f),
|
||||
command: command(),
|
||||
name,
|
||||
tldr_zip: &mut tldr_zip,
|
||||
utils_per_platform: &utils_per_platform,
|
||||
markdown,
|
||||
}
|
||||
.markdown()?;
|
||||
println!("Wrote to '{}'", p);
|
||||
} else {
|
||||
println!("Error writing to {}", p);
|
||||
}
|
||||
writeln!(summary, "* [{0}](utils/{0}.md)", name)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
struct MDWriter<'a, 'b> {
|
||||
w: Box<dyn Write>,
|
||||
command: Command,
|
||||
name: &'a str,
|
||||
tldr_zip: &'b mut Option<ZipArchive<File>>,
|
||||
utils_per_platform: &'b HashMap<&'b str, Vec<String>>,
|
||||
markdown: Option<String>,
|
||||
}
|
||||
|
||||
impl<'a, 'b> MDWriter<'a, 'b> {
|
||||
fn markdown(&mut self) -> io::Result<()> {
|
||||
write!(self.w, "# {}\n\n", self.name)?;
|
||||
self.additional()?;
|
||||
self.usage()?;
|
||||
self.about()?;
|
||||
self.options()?;
|
||||
self.after_help()?;
|
||||
self.examples()
|
||||
}
|
||||
|
||||
fn additional(&mut self) -> io::Result<()> {
|
||||
writeln!(self.w, "<div class=\"additional\">")?;
|
||||
self.platforms()?;
|
||||
self.version()?;
|
||||
writeln!(self.w, "</div>")
|
||||
}
|
||||
|
||||
fn platforms(&mut self) -> io::Result<()> {
|
||||
writeln!(self.w, "<div class=\"platforms\">")?;
|
||||
for (feature, icon) in [
|
||||
("linux", "linux"),
|
||||
// freebsd is disabled for now because mdbook does not use font-awesome 5 yet.
|
||||
// ("unix", "freebsd"),
|
||||
("macos", "apple"),
|
||||
("windows", "windows"),
|
||||
] {
|
||||
if self.name.contains("sum")
|
||||
|| self.utils_per_platform[feature]
|
||||
.iter()
|
||||
.any(|u| u == self.name)
|
||||
{
|
||||
writeln!(self.w, "<i class=\"fa fa-brands fa-{}\"></i>", icon)?;
|
||||
}
|
||||
}
|
||||
writeln!(self.w, "</div>")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn version(&mut self) -> io::Result<()> {
|
||||
writeln!(
|
||||
self.w,
|
||||
"<div class=\"version\">v{}</div>",
|
||||
self.command.render_version().split_once(' ').unwrap().1
|
||||
)
|
||||
}
|
||||
|
||||
fn usage(&mut self) -> io::Result<()> {
|
||||
if let Some(markdown) = &self.markdown {
|
||||
let usage = uuhelp_parser::parse_usage(markdown);
|
||||
let usage = usage.replace("{}", self.name);
|
||||
|
||||
writeln!(self.w, "\n```")?;
|
||||
writeln!(self.w, "{}", usage)?;
|
||||
writeln!(self.w, "```")
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn about(&mut self) -> io::Result<()> {
|
||||
if let Some(markdown) = &self.markdown {
|
||||
writeln!(self.w, "{}", uuhelp_parser::parse_about(markdown))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn after_help(&mut self) -> io::Result<()> {
|
||||
if let Some(markdown) = &self.markdown {
|
||||
if let Some(after_help) = uuhelp_parser::parse_section("after help", markdown) {
|
||||
return writeln!(self.w, "\n\n{after_help}");
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
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(())
|
||||
}
|
||||
|
||||
fn options(&mut self) -> io::Result<()> {
|
||||
writeln!(self.w, "<h2>Options</h2>")?;
|
||||
write!(self.w, "<dl>")?;
|
||||
for arg in self.command.get_arguments() {
|
||||
write!(self.w, "<dt>")?;
|
||||
let mut first = true;
|
||||
for l in arg.get_long_and_visible_aliases().unwrap_or_default() {
|
||||
if first {
|
||||
first = false;
|
||||
} else {
|
||||
write!(self.w, ", ")?;
|
||||
}
|
||||
write!(self.w, "<code>")?;
|
||||
write!(self.w, "--{}", l)?;
|
||||
if let Some(names) = arg.get_value_names() {
|
||||
write!(
|
||||
self.w,
|
||||
"={}",
|
||||
names
|
||||
.iter()
|
||||
.map(|x| format!("<{}>", x))
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ")
|
||||
)?;
|
||||
}
|
||||
write!(self.w, "</code>")?;
|
||||
}
|
||||
for s in arg.get_short_and_visible_aliases().unwrap_or_default() {
|
||||
if first {
|
||||
first = false;
|
||||
} else {
|
||||
write!(self.w, ", ")?;
|
||||
}
|
||||
write!(self.w, "<code>")?;
|
||||
write!(self.w, "-{}", s)?;
|
||||
if let Some(names) = arg.get_value_names() {
|
||||
write!(
|
||||
self.w,
|
||||
" {}",
|
||||
names
|
||||
.iter()
|
||||
.map(|x| format!("<{}>", x))
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ")
|
||||
)?;
|
||||
}
|
||||
write!(self.w, "</code>")?;
|
||||
}
|
||||
writeln!(self.w, "</dt>")?;
|
||||
writeln!(
|
||||
self.w,
|
||||
"<dd>\n\n{}\n\n</dd>",
|
||||
arg.get_help()
|
||||
.unwrap_or_default()
|
||||
.to_string()
|
||||
.replace('\n', "<br />")
|
||||
)?;
|
||||
}
|
||||
writeln!(self.w, "</dl>\n")
|
||||
}
|
||||
}
|
||||
|
||||
fn get_zip_content(archive: &mut ZipArchive<impl Read + Seek>, name: &str) -> Option<String> {
|
||||
let mut s = String::new();
|
||||
archive.by_name(name).ok()?.read_to_string(&mut s).unwrap();
|
||||
Some(s)
|
||||
}
|
7
src/uu/lscpu/lscpu.md
Normal file
7
src/uu/lscpu/lscpu.md
Normal file
@ -0,0 +1,7 @@
|
||||
# lscpu
|
||||
|
||||
```
|
||||
lscpu [OPTION]...
|
||||
```
|
||||
|
||||
display information about the CPU architecture
|
1
src/uu/lscpu/src/main.rs
Normal file
1
src/uu/lscpu/src/main.rs
Normal file
@ -0,0 +1 @@
|
||||
uucore::bin!(uu_lscpu);
|
9
src/uu/mountpoint/mountpoint.md
Normal file
9
src/uu/mountpoint/mountpoint.md
Normal file
@ -0,0 +1,9 @@
|
||||
# mountpoint
|
||||
|
||||
```
|
||||
mountpoint [-d|-q] directory|file
|
||||
|
||||
mountpoint -x device
|
||||
```
|
||||
|
||||
See if a directory or file is a mountpoint
|
1
src/uu/mountpoint/src/main.rs
Normal file
1
src/uu/mountpoint/src/main.rs
Normal file
@ -0,0 +1 @@
|
||||
uucore::bin!(uu_mountpoint);
|
7
src/uu/pwdx/pwdx.md
Normal file
7
src/uu/pwdx/pwdx.md
Normal file
@ -0,0 +1,7 @@
|
||||
# pwdx
|
||||
|
||||
```
|
||||
pwdx [options] pid [...]
|
||||
```
|
||||
|
||||
Report current working directory of a process
|
1
src/uu/pwdx/src/main.rs
Normal file
1
src/uu/pwdx/src/main.rs
Normal file
@ -0,0 +1 @@
|
||||
uucore::bin!(uu_pwdx);
|
7
src/uu/renice/renice.md
Normal file
7
src/uu/renice/renice.md
Normal file
@ -0,0 +1,7 @@
|
||||
# renice
|
||||
|
||||
```
|
||||
renice [--priority|--relative] priority [-g|-p|-u] identifier...
|
||||
```
|
||||
|
||||
Alter priority of running processes
|
1
src/uu/renice/src/main.rs
Normal file
1
src/uu/renice/src/main.rs
Normal file
@ -0,0 +1 @@
|
||||
uucore::bin!(uu_renice);
|
Loading…
x
Reference in New Issue
Block a user