From 1b4b449d49b98a4e2c3d0da85592b39dc327d70c Mon Sep 17 00:00:00 2001
From: Sylvestre Ledru <sylvestre@debian.org>
Date: Fri, 26 Jan 2024 19:09:54 +0100
Subject: [PATCH] match the coreutils style

---
 build.rs                        | 101 +++++++++
 src/bin/util-linux.rs           | 224 ++++++++++++++++++++
 src/bin/uudoc.rs                | 361 ++++++++++++++++++++++++++++++++
 src/uu/lscpu/lscpu.md           |   7 +
 src/uu/lscpu/src/main.rs        |   1 +
 src/uu/mountpoint/mountpoint.md |   9 +
 src/uu/mountpoint/src/main.rs   |   1 +
 src/uu/pwdx/pwdx.md             |   7 +
 src/uu/pwdx/src/main.rs         |   1 +
 src/uu/renice/renice.md         |   7 +
 src/uu/renice/src/main.rs       |   1 +
 11 files changed, 720 insertions(+)
 create mode 100644 build.rs
 create mode 100644 src/bin/util-linux.rs
 create mode 100644 src/bin/uudoc.rs
 create mode 100644 src/uu/lscpu/lscpu.md
 create mode 100644 src/uu/lscpu/src/main.rs
 create mode 100644 src/uu/mountpoint/mountpoint.md
 create mode 100644 src/uu/mountpoint/src/main.rs
 create mode 100644 src/uu/pwdx/pwdx.md
 create mode 100644 src/uu/pwdx/src/main.rs
 create mode 100644 src/uu/renice/renice.md
 create mode 100644 src/uu/renice/src/main.rs

diff --git a/build.rs b/build.rs
new file mode 100644
index 0000000..bb4e2b5
--- /dev/null
+++ b/build.rs
@@ -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();
+}
diff --git a/src/bin/util-linux.rs b/src/bin/util-linux.rs
new file mode 100644
index 0000000..fc2cd16
--- /dev/null
+++ b/src/bin/util-linux.rs
@@ -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
+}
diff --git a/src/bin/uudoc.rs b/src/bin/uudoc.rs
new file mode 100644
index 0000000..77c7a2f
--- /dev/null
+++ b/src/bin/uudoc.rs
@@ -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!("&lt;{}&gt;", 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!("&lt;{}&gt;", 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)
+}
diff --git a/src/uu/lscpu/lscpu.md b/src/uu/lscpu/lscpu.md
new file mode 100644
index 0000000..ada8e8d
--- /dev/null
+++ b/src/uu/lscpu/lscpu.md
@@ -0,0 +1,7 @@
+# lscpu
+
+```
+lscpu [OPTION]...
+```
+
+display information about the CPU architecture
\ No newline at end of file
diff --git a/src/uu/lscpu/src/main.rs b/src/uu/lscpu/src/main.rs
new file mode 100644
index 0000000..d0e1be6
--- /dev/null
+++ b/src/uu/lscpu/src/main.rs
@@ -0,0 +1 @@
+uucore::bin!(uu_lscpu);
diff --git a/src/uu/mountpoint/mountpoint.md b/src/uu/mountpoint/mountpoint.md
new file mode 100644
index 0000000..fc84920
--- /dev/null
+++ b/src/uu/mountpoint/mountpoint.md
@@ -0,0 +1,9 @@
+# mountpoint
+
+```
+mountpoint [-d|-q] directory|file
+
+mountpoint -x device
+```
+
+See if a directory or file is a mountpoint
\ No newline at end of file
diff --git a/src/uu/mountpoint/src/main.rs b/src/uu/mountpoint/src/main.rs
new file mode 100644
index 0000000..8661979
--- /dev/null
+++ b/src/uu/mountpoint/src/main.rs
@@ -0,0 +1 @@
+uucore::bin!(uu_mountpoint);
diff --git a/src/uu/pwdx/pwdx.md b/src/uu/pwdx/pwdx.md
new file mode 100644
index 0000000..0efb6d6
--- /dev/null
+++ b/src/uu/pwdx/pwdx.md
@@ -0,0 +1,7 @@
+# pwdx
+
+```
+pwdx [options] pid [...]
+```
+
+Report current working directory of a process
\ No newline at end of file
diff --git a/src/uu/pwdx/src/main.rs b/src/uu/pwdx/src/main.rs
new file mode 100644
index 0000000..ee1a336
--- /dev/null
+++ b/src/uu/pwdx/src/main.rs
@@ -0,0 +1 @@
+uucore::bin!(uu_pwdx);
diff --git a/src/uu/renice/renice.md b/src/uu/renice/renice.md
new file mode 100644
index 0000000..43fd7e5
--- /dev/null
+++ b/src/uu/renice/renice.md
@@ -0,0 +1,7 @@
+# renice
+
+```
+renice [--priority|--relative] priority [-g|-p|-u] identifier...
+```
+
+Alter priority of running processes
\ No newline at end of file
diff --git a/src/uu/renice/src/main.rs b/src/uu/renice/src/main.rs
new file mode 100644
index 0000000..899bccb
--- /dev/null
+++ b/src/uu/renice/src/main.rs
@@ -0,0 +1 @@
+uucore::bin!(uu_renice);